Files
caotek_zidol_theme/PasteScript-1.7.4.2-py2.6.egg/paste/script/copydir.py

439 lines
15 KiB
Python
Executable File

# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
import os
import pkg_resources
import sys
if sys.version_info < (2, 4):
from paste.script.util import string24 as string
else:
import string
import cgi
import urllib
import re
Cheetah = None
try:
import subprocess
except ImportError:
try:
from paste.script.util import subprocess24 as subprocess
except ImportError:
subprocess = None # jython
import inspect
class SkipTemplate(Exception):
"""
Raised to indicate that the template should not be copied over.
Raise this exception during the substitution of your template
"""
def copy_dir(source, dest, vars, verbosity, simulate, indent=0,
use_cheetah=False, sub_vars=True, interactive=False,
svn_add=True, overwrite=True, template_renderer=None):
"""
Copies the ``source`` directory to the ``dest`` directory.
``vars``: A dictionary of variables to use in any substitutions.
``verbosity``: Higher numbers will show more about what is happening.
``simulate``: If true, then don't actually *do* anything.
``indent``: Indent any messages by this amount.
``sub_vars``: If true, variables in ``_tmpl`` files and ``+var+``
in filenames will be substituted.
``use_cheetah``: If true, then any templates encountered will be
substituted with Cheetah. Otherwise ``template_renderer`` or
``string.Template`` will be used for templates.
``svn_add``: If true, any files written out in directories with
``.svn/`` directories will be added (via ``svn add``).
``overwrite``: If false, then don't every overwrite anything.
``interactive``: If you are overwriting a file and interactive is
true, then ask before overwriting.
``template_renderer``: This is a function for rendering templates
(if you don't want to use Cheetah or string.Template). It should
have the signature ``template_renderer(content_as_string,
vars_as_dict, filename=filename)``.
"""
# This allows you to use a leading +dot+ in filenames which would
# otherwise be skipped because leading dots make the file hidden:
vars.setdefault('dot', '.')
vars.setdefault('plus', '+')
use_pkg_resources = isinstance(source, tuple)
if use_pkg_resources:
names = pkg_resources.resource_listdir(source[0], source[1])
else:
names = os.listdir(source)
names.sort()
pad = ' '*(indent*2)
if not os.path.exists(dest):
if verbosity >= 1:
print '%sCreating %s/' % (pad, dest)
if not simulate:
svn_makedirs(dest, svn_add=svn_add, verbosity=verbosity,
pad=pad)
elif verbosity >= 2:
print '%sDirectory %s exists' % (pad, dest)
for name in names:
if use_pkg_resources:
full = '/'.join([source[1], name])
else:
full = os.path.join(source, name)
reason = should_skip_file(name)
if reason:
if verbosity >= 2:
reason = pad + reason % {'filename': full}
print reason
continue
if sub_vars:
dest_full = os.path.join(dest, substitute_filename(name, vars))
sub_file = False
if dest_full.endswith('_tmpl'):
dest_full = dest_full[:-5]
sub_file = sub_vars
if use_pkg_resources and pkg_resources.resource_isdir(source[0], full):
if verbosity:
print '%sRecursing into %s' % (pad, os.path.basename(full))
copy_dir((source[0], full), dest_full, vars, verbosity, simulate,
indent=indent+1, use_cheetah=use_cheetah,
sub_vars=sub_vars, interactive=interactive,
svn_add=svn_add, template_renderer=template_renderer)
continue
elif not use_pkg_resources and os.path.isdir(full):
if verbosity:
print '%sRecursing into %s' % (pad, os.path.basename(full))
copy_dir(full, dest_full, vars, verbosity, simulate,
indent=indent+1, use_cheetah=use_cheetah,
sub_vars=sub_vars, interactive=interactive,
svn_add=svn_add, template_renderer=template_renderer)
continue
elif use_pkg_resources:
content = pkg_resources.resource_string(source[0], full)
else:
f = open(full, 'rb')
content = f.read()
f.close()
if sub_file:
try:
content = substitute_content(content, vars, filename=full,
use_cheetah=use_cheetah,
template_renderer=template_renderer)
except SkipTemplate:
continue
if content is None:
continue
already_exists = os.path.exists(dest_full)
if already_exists:
f = open(dest_full, 'rb')
old_content = f.read()
f.close()
if old_content == content:
if verbosity:
print '%s%s already exists (same content)' % (pad, dest_full)
continue
if interactive:
if not query_interactive(
full, dest_full, content, old_content,
simulate=simulate):
continue
elif not overwrite:
continue
if verbosity and use_pkg_resources:
print '%sCopying %s to %s' % (pad, full, dest_full)
elif verbosity:
print '%sCopying %s to %s' % (pad, os.path.basename(full), dest_full)
if not simulate:
f = open(dest_full, 'wb')
f.write(content)
f.close()
if svn_add and not already_exists:
if not os.path.exists(os.path.join(os.path.dirname(os.path.abspath(dest_full)), '.svn')):
if verbosity > 1:
print '%s.svn/ does not exist; cannot add file' % pad
else:
cmd = ['svn', 'add', dest_full]
if verbosity > 1:
print '%sRunning: %s' % (pad, ' '.join(cmd))
if not simulate:
# @@: Should
if subprocess is None:
raise RuntimeError('copydir failed, environment '
'does not support subprocess '
'module')
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
stdout, stderr = proc.communicate()
if verbosity > 1 and stdout:
print 'Script output:'
print stdout
elif svn_add and already_exists and verbosity > 1:
print '%sFile already exists (not doing svn add)' % pad
def should_skip_file(name):
"""
Checks if a file should be skipped based on its name.
If it should be skipped, returns the reason, otherwise returns
None.
"""
if name.startswith('.'):
return 'Skipping hidden file %(filename)s'
if name.endswith('~') or name.endswith('.bak'):
return 'Skipping backup file %(filename)s'
if name.endswith('.pyc') or name.endswith('.pyo'):
return 'Skipping %s file %%(filename)s' % os.path.splitext(name)[1]
if name.endswith('$py.class'):
return 'Skipping $py.class file %(filename)s'
if name in ('CVS', '_darcs'):
return 'Skipping version control directory %(filename)s'
return None
# Overridden on user's request:
all_answer = None
def query_interactive(src_fn, dest_fn, src_content, dest_content,
simulate):
global all_answer
from difflib import unified_diff, context_diff
u_diff = list(unified_diff(
dest_content.splitlines(),
src_content.splitlines(),
dest_fn, src_fn))
c_diff = list(context_diff(
dest_content.splitlines(),
src_content.splitlines(),
dest_fn, src_fn))
added = len([l for l in u_diff if l.startswith('+')
and not l.startswith('+++')])
removed = len([l for l in u_diff if l.startswith('-')
and not l.startswith('---')])
if added > removed:
msg = '; %i lines added' % (added-removed)
elif removed > added:
msg = '; %i lines removed' % (removed-added)
else:
msg = ''
print 'Replace %i bytes with %i bytes (%i/%i lines changed%s)' % (
len(dest_content), len(src_content),
removed, len(dest_content.splitlines()), msg)
prompt = 'Overwrite %s [y/n/d/B/?] ' % dest_fn
while 1:
if all_answer is None:
response = raw_input(prompt).strip().lower()
else:
response = all_answer
if not response or response[0] == 'b':
import shutil
new_dest_fn = dest_fn + '.bak'
n = 0
while os.path.exists(new_dest_fn):
n += 1
new_dest_fn = dest_fn + '.bak' + str(n)
print 'Backing up %s to %s' % (dest_fn, new_dest_fn)
if not simulate:
shutil.copyfile(dest_fn, new_dest_fn)
return True
elif response.startswith('all '):
rest = response[4:].strip()
if not rest or rest[0] not in ('y', 'n', 'b'):
print query_usage
continue
response = all_answer = rest[0]
if response[0] == 'y':
return True
elif response[0] == 'n':
return False
elif response == 'dc':
print '\n'.join(c_diff)
elif response[0] == 'd':
print '\n'.join(u_diff)
else:
print query_usage
query_usage = """\
Responses:
Y(es): Overwrite the file with the new content.
N(o): Do not overwrite the file.
D(iff): Show a unified diff of the proposed changes (dc=context diff)
B(ackup): Save the current file contents to a .bak file
(and overwrite)
Type "all Y/N/B" to use Y/N/B for answer to all future questions
"""
def svn_makedirs(dir, svn_add, verbosity, pad):
parent = os.path.dirname(os.path.abspath(dir))
if not os.path.exists(parent):
svn_makedirs(parent, svn_add, verbosity, pad)
os.mkdir(dir)
if not svn_add:
return
if not os.path.exists(os.path.join(parent, '.svn')):
if verbosity > 1:
print '%s.svn/ does not exist; cannot add directory' % pad
return
cmd = ['svn', 'add', dir]
if verbosity > 1:
print '%sRunning: %s' % (pad, ' '.join(cmd))
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
stdout, stderr = proc.communicate()
if verbosity > 1 and stdout:
print 'Script output:'
print stdout
def substitute_filename(fn, vars):
for var, value in vars.items():
fn = fn.replace('+%s+' % var, str(value))
return fn
def substitute_content(content, vars, filename='<string>',
use_cheetah=False, template_renderer=None):
global Cheetah
v = standard_vars.copy()
v.update(vars)
vars = v
if template_renderer is not None:
return template_renderer(content, vars, filename=filename)
if not use_cheetah:
tmpl = LaxTemplate(content)
try:
return tmpl.substitute(TypeMapper(v))
except Exception, e:
_add_except(e, ' in file %s' % filename)
raise
if Cheetah is None:
import Cheetah.Template
tmpl = Cheetah.Template.Template(source=content,
searchList=[vars])
return careful_sub(tmpl, vars, filename)
def careful_sub(cheetah_template, vars, filename):
"""
Substitutes the template with the variables, using the
.body() method if it exists. It assumes that the variables
were also passed in via the searchList.
"""
if not hasattr(cheetah_template, 'body'):
return sub_catcher(filename, vars, str, cheetah_template)
body = cheetah_template.body
args, varargs, varkw, defaults = inspect.getargspec(body)
call_vars = {}
for arg in args:
if arg in vars:
call_vars[arg] = vars[arg]
return sub_catcher(filename, vars, body, **call_vars)
def sub_catcher(filename, vars, func, *args, **kw):
"""
Run a substitution, returning the value. If an error occurs, show
the filename. If the error is a NameError, show the variables.
"""
try:
return func(*args, **kw)
except SkipTemplate, e:
print 'Skipping file %s' % filename
if str(e):
print str(e)
raise
except Exception, e:
print 'Error in file %s:' % filename
if isinstance(e, NameError):
items = vars.items()
items.sort()
for name, value in items:
print '%s = %r' % (name, value)
raise
def html_quote(s):
if s is None:
return ''
return cgi.escape(str(s), 1)
def url_quote(s):
if s is None:
return ''
return urllib.quote(str(s))
def test(conf, true_cond, false_cond=None):
if conf:
return true_cond
else:
return false_cond
def skip_template(condition=True, *args):
"""
Raise SkipTemplate, which causes copydir to skip the template
being processed. If you pass in a condition, only raise if that
condition is true (allows you to use this with string.Template)
If you pass any additional arguments, they will be used to
instantiate SkipTemplate (generally use like
``skip_template(license=='GPL', 'Skipping file; not using GPL')``)
"""
if condition:
raise SkipTemplate(*args)
def _add_except(exc, info):
if not hasattr(exc, 'args') or exc.args is None:
return
args = list(exc.args)
if args:
args[0] += ' ' + info
else:
args = [info]
exc.args = tuple(args)
return
standard_vars = {
'nothing': None,
'html_quote': html_quote,
'url_quote': url_quote,
'empty': '""',
'test': test,
'repr': repr,
'str': str,
'bool': bool,
'SkipTemplate': SkipTemplate,
'skip_template': skip_template,
}
class TypeMapper(dict):
def __getitem__(self, item):
options = item.split('|')
for op in options[:-1]:
try:
value = eval_with_catch(op, dict(self.items()))
break
except (NameError, KeyError):
pass
else:
value = eval(options[-1], dict(self.items()))
if value is None:
return ''
else:
return str(value)
def eval_with_catch(expr, vars):
try:
return eval(expr, vars)
except Exception, e:
_add_except(e, 'in expression %r' % expr)
raise
class LaxTemplate(string.Template):
# This change of pattern allows for anything in braces, but
# only identifiers outside of braces:
pattern = r"""
\$(?:
(?P<escaped>\$) | # Escape sequence of two delimiters
(?P<named>[_a-z][_a-z0-9]*) | # delimiter and a Python identifier
{(?P<braced>.*?)} | # delimiter and a braced identifier
(?P<invalid>) # Other ill-formed delimiter exprs
)
"""