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

819 lines
29 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 pkg_resources
import sys
import optparse
import bool_optparse
import os
import re
import textwrap
import pluginlib
import ConfigParser
import getpass
try:
import subprocess
except ImportError:
try:
from paste.script.util import subprocess24 as subprocess
except ImportError:
subprocess = None # jython
difflib = None
if sys.version_info >= (2, 6):
from logging.config import fileConfig
else:
# Use our custom fileConfig -- 2.5.1's with a custom Formatter class
# and less strict whitespace (which were incorporated into 2.6's)
from paste.script.util.logging_config import fileConfig
class BadCommand(Exception):
def __init__(self, message, exit_code=2):
self.message = message
self.exit_code = exit_code
Exception.__init__(self, message)
def _get_message(self):
"""Getter for 'message'; needed only to override deprecation
in BaseException."""
return self.__message
def _set_message(self, value):
"""Setter for 'message'; needed only to override deprecation
in BaseException."""
self.__message = value
# BaseException.message has been deprecated since Python 2.6.
# To prevent DeprecationWarning from popping up over this
# pre-existing attribute, use a new property that takes lookup
# precedence.
message = property(_get_message, _set_message)
class NoDefault(object):
pass
dist = pkg_resources.get_distribution('PasteScript')
python_version = sys.version.splitlines()[0].strip()
parser = optparse.OptionParser(add_help_option=False,
version='%s from %s (python %s)'
% (dist, dist.location, python_version),
usage='%prog [paster_options] COMMAND [command_options]')
parser.add_option(
'--plugin',
action='append',
dest='plugins',
help="Add a plugin to the list of commands (plugins are Egg specs; will also require() the Egg)")
parser.add_option(
'-h', '--help',
action='store_true',
dest='do_help',
help="Show this help message")
parser.disable_interspersed_args()
# @@: Add an option to run this in another Python interpreter
system_plugins = []
def run(args=None):
if (not args and
len(sys.argv) >= 2
and os.environ.get('_') and sys.argv[0] != os.environ['_']
and os.environ['_'] == sys.argv[1]):
# probably it's an exe execution
args = ['exe', os.environ['_']] + sys.argv[2:]
if args is None:
args = sys.argv[1:]
options, args = parser.parse_args(args)
options.base_parser = parser
system_plugins.extend(options.plugins or [])
commands = get_commands()
if options.do_help:
args = ['help'] + args
if not args:
print 'Usage: %s COMMAND' % sys.argv[0]
args = ['help']
command_name = args[0]
if command_name not in commands:
command = NotFoundCommand
else:
command = commands[command_name].load()
invoke(command, command_name, options, args[1:])
def parse_exe_file(config):
import shlex
p = ConfigParser.RawConfigParser()
p.read([config])
command_name = 'exe'
options = []
if p.has_option('exe', 'command'):
command_name = p.get('exe', 'command')
if p.has_option('exe', 'options'):
options = shlex.split(p.get('exe', 'options'))
if p.has_option('exe', 'sys.path'):
paths = shlex.split(p.get('exe', 'sys.path'))
paths = [os.path.abspath(os.path.join(os.path.dirname(config), p))
for p in paths]
for path in paths:
pkg_resources.working_set.add_entry(path)
sys.path.insert(0, path)
args = [command_name, config] + options
return args
def get_commands():
plugins = system_plugins[:]
egg_info_dir = pluginlib.find_egg_info_dir(os.getcwd())
if egg_info_dir:
plugins.append(os.path.splitext(os.path.basename(egg_info_dir))[0])
base_dir = os.path.dirname(egg_info_dir)
if base_dir not in sys.path:
sys.path.insert(0, base_dir)
pkg_resources.working_set.add_entry(base_dir)
plugins = pluginlib.resolve_plugins(plugins)
commands = pluginlib.load_commands_from_plugins(plugins)
commands.update(pluginlib.load_global_commands())
return commands
def invoke(command, command_name, options, args):
try:
runner = command(command_name)
exit_code = runner.run(args)
except BadCommand, e:
print e.message
exit_code = e.exit_code
sys.exit(exit_code)
class Command(object):
def __init__(self, name):
self.command_name = name
max_args = None
max_args_error = 'You must provide no more than %(max_args)s arguments'
min_args = None
min_args_error = 'You must provide at least %(min_args)s arguments'
required_args = None
# If this command takes a configuration file, set this to 1 or -1
# Then if invoked through #! the config file will be put into the positional
# arguments -- at the beginning with 1, at the end with -1
takes_config_file = None
# Grouped in help messages by this:
group_name = ''
required_args = ()
description = None
usage = ''
hidden = False
# This is the default verbosity level; --quiet subtracts,
# --verbose adds:
default_verbosity = 0
# This is the default interactive state:
default_interactive = 0
return_code = 0
BadCommand = BadCommand
# Must define:
# parser
# summary
# command()
def run(self, args):
self.parse_args(args)
# Setup defaults:
for name, default in [('verbose', 0),
('quiet', 0),
('interactive', False),
('overwrite', False)]:
if not hasattr(self.options, name):
setattr(self.options, name, default)
if getattr(self.options, 'simulate', False):
self.options.verbose = max(self.options.verbose, 1)
self.interactive = self.default_interactive
if getattr(self.options, 'interactive', False):
self.interactive += self.options.interactive
if getattr(self.options, 'no_interactive', False):
self.interactive = False
self.verbose = self.default_verbosity
self.verbose += self.options.verbose
self.verbose -= self.options.quiet
self.simulate = getattr(self.options, 'simulate', False)
# For #! situations:
if (os.environ.get('PASTE_CONFIG_FILE')
and self.takes_config_file is not None):
take = self.takes_config_file
filename = os.environ.get('PASTE_CONFIG_FILE')
if take == 1:
self.args.insert(0, filename)
elif take == -1:
self.args.append(filename)
else:
assert 0, (
"Value takes_config_file must be None, 1, or -1 (not %r)"
% take)
if (os.environ.get('PASTE_DEFAULT_QUIET')):
self.verbose = 0
# Validate:
if self.min_args is not None and len(self.args) < self.min_args:
raise BadCommand(
self.min_args_error % {'min_args': self.min_args,
'actual_args': len(self.args)})
if self.max_args is not None and len(self.args) > self.max_args:
raise BadCommand(
self.max_args_error % {'max_args': self.max_args,
'actual_args': len(self.args)})
for var_name, option_name in self.required_args:
if not getattr(self.options, var_name, None):
raise BadCommand(
'You must provide the option %s' % option_name)
result = self.command()
if result is None:
return self.return_code
else:
return result
def parse_args(self, args):
if self.usage:
usage = ' '+self.usage
else:
usage = ''
self.parser.usage = "%%prog [options]%s\n%s" % (
usage, self.summary)
self.parser.prog = self._prog_name()
if self.description:
desc = self.description
desc = textwrap.dedent(desc)
self.parser.description = desc
self.options, self.args = self.parser.parse_args(args)
def _prog_name(self):
return '%s %s' % (os.path.basename(sys.argv[0]), self.command_name)
########################################
## Utility methods
########################################
def here(cls):
mod = sys.modules[cls.__module__]
return os.path.dirname(mod.__file__)
here = classmethod(here)
def ask(self, prompt, safe=False, default=True):
"""
Prompt the user. Default can be true, false, ``'careful'`` or
``'none'``. If ``'none'`` then the user must enter y/n. If
``'careful'`` then the user must enter yes/no (long form).
If the interactive option is over two (``-ii``) then ``safe``
will be used as a default. This option should be the
do-nothing option.
"""
# @@: Should careful be a separate argument?
if self.options.interactive >= 2:
default = safe
if default == 'careful':
prompt += ' [yes/no]?'
elif default == 'none':
prompt += ' [y/n]?'
elif default:
prompt += ' [Y/n]? '
else:
prompt += ' [y/N]? '
while 1:
response = raw_input(prompt).strip().lower()
if not response:
if default in ('careful', 'none'):
print 'Please enter yes or no'
continue
return default
if default == 'careful':
if response in ('yes', 'no'):
return response == 'yes'
print 'Please enter "yes" or "no"'
continue
if response[0].lower() in ('y', 'n'):
return response[0].lower() == 'y'
print 'Y or N please'
def challenge(self, prompt, default=NoDefault, should_echo=True):
"""
Prompt the user for a variable.
"""
if default is not NoDefault:
prompt += ' [%r]' % default
prompt += ': '
while 1:
if should_echo:
prompt_method = raw_input
else:
prompt_method = getpass.getpass
response = prompt_method(prompt).strip()
if not response:
if default is not NoDefault:
return default
else:
continue
else:
return response
def pad(self, s, length, dir='left'):
if len(s) >= length:
return s
if dir == 'left':
return s + ' '*(length-len(s))
else:
return ' '*(length-len(s)) + s
def standard_parser(cls, verbose=True,
interactive=False,
no_interactive=False,
simulate=False,
quiet=False,
overwrite=False):
"""
Create a standard ``OptionParser`` instance.
Typically used like::
class MyCommand(Command):
parser = Command.standard_parser()
Subclasses may redefine ``standard_parser``, so use the
nearest superclass's class method.
"""
parser = bool_optparse.BoolOptionParser()
if verbose:
parser.add_option('-v', '--verbose',
action='count',
dest='verbose',
default=0)
if quiet:
parser.add_option('-q', '--quiet',
action='count',
dest='quiet',
default=0)
if no_interactive:
parser.add_option('--no-interactive',
action="count",
dest="no_interactive",
default=0)
if interactive:
parser.add_option('-i', '--interactive',
action='count',
dest='interactive',
default=0)
if simulate:
parser.add_option('-n', '--simulate',
action='store_true',
dest='simulate',
default=False)
if overwrite:
parser.add_option('-f', '--overwrite',
dest="overwrite",
action="store_true",
help="Overwrite files (warnings will be emitted for non-matching files otherwise)")
return parser
standard_parser = classmethod(standard_parser)
def shorten(self, fn, *paths):
"""
Return a shorted form of the filename (relative to the current
directory), typically for displaying in messages. If
``*paths`` are present, then use os.path.join to create the
full filename before shortening.
"""
if paths:
fn = os.path.join(fn, *paths)
if fn.startswith(os.getcwd()):
return fn[len(os.getcwd()):].lstrip(os.path.sep)
else:
return fn
def ensure_dir(self, dir, svn_add=True):
"""
Ensure that the directory exists, creating it if necessary.
Respects verbosity and simulation.
Adds directory to subversion if ``.svn/`` directory exists in
parent, and directory was created.
"""
dir = dir.rstrip(os.sep)
if not dir:
# we either reached the parent-most directory, or we got
# a relative directory
# @@: Should we make sure we resolve relative directories
# first? Though presumably the current directory always
# exists.
return
if not os.path.exists(dir):
self.ensure_dir(os.path.dirname(dir))
if self.verbose:
print 'Creating %s' % self.shorten(dir)
if not self.simulate:
os.mkdir(dir)
if (svn_add and
os.path.exists(os.path.join(os.path.dirname(dir), '.svn'))):
self.svn_command('add', dir)
else:
if self.verbose > 1:
print "Directory already exists: %s" % self.shorten(dir)
def ensure_file(self, filename, content, svn_add=True):
"""
Ensure a file named ``filename`` exists with the given
content. If ``--interactive`` has been enabled, this will ask
the user what to do if a file exists with different content.
"""
global difflib
assert content is not None, (
"You cannot pass a content of None")
self.ensure_dir(os.path.dirname(filename), svn_add=svn_add)
if not os.path.exists(filename):
if self.verbose:
print 'Creating %s' % filename
if not self.simulate:
f = open(filename, 'wb')
f.write(content)
f.close()
if svn_add and os.path.exists(os.path.join(os.path.dirname(filename), '.svn')):
self.svn_command('add', filename,
warn_returncode=True)
return
f = open(filename, 'rb')
old_content = f.read()
f.close()
if content == old_content:
if self.verbose > 1:
print 'File %s matches expected content' % filename
return
if not self.options.overwrite:
print 'Warning: file %s does not match expected content' % filename
if difflib is None:
import difflib
diff = difflib.context_diff(
content.splitlines(),
old_content.splitlines(),
'expected ' + filename,
filename)
print '\n'.join(diff)
if self.interactive:
while 1:
s = raw_input(
'Overwrite file with new content? [y/N] ').strip().lower()
if not s:
s = 'n'
if s.startswith('y'):
break
if s.startswith('n'):
return
print 'Unknown response; Y or N please'
else:
return
if self.verbose:
print 'Overwriting %s with new content' % filename
if not self.simulate:
f = open(filename, 'wb')
f.write(content)
f.close()
def insert_into_file(self, filename, marker_name, text,
indent=False):
"""
Inserts ``text`` into the file, right after the given marker.
Markers look like: ``-*- <marker_name>[:]? -*-``, and the text
will go on the immediately following line.
Raises ``ValueError`` if the marker is not found.
If ``indent`` is true, then the text will be indented at the
same level as the marker.
"""
if not text.endswith('\n'):
raise ValueError(
"The text must end with a newline: %r" % text)
if not os.path.exists(filename) and self.simulate:
# If we are doing a simulation, it's expected that some
# files won't exist...
if self.verbose:
print 'Would (if not simulating) insert text into %s' % (
self.shorten(filename))
return
f = open(filename)
lines = f.readlines()
f.close()
regex = re.compile(r'-\*-\s+%s:?\s+-\*-' % re.escape(marker_name),
re.I)
for i in range(len(lines)):
if regex.search(lines[i]):
# Found it!
if (lines[i:] and len(lines[i:]) > 1 and
''.join(lines[i+1:]).strip().startswith(text.strip())):
# Already have it!
print 'Warning: line already found in %s (not inserting' % filename
print ' %s' % lines[i]
return
if indent:
text = text.lstrip()
match = re.search(r'^[ \t]*', lines[i])
text = match.group(0) + text
lines[i+1:i+1] = [text]
break
else:
errstr = (
"Marker '-*- %s -*-' not found in %s"
% (marker_name, filename))
if 1 or self.simulate: # @@: being permissive right now
print 'Warning: %s' % errstr
else:
raise ValueError(errstr)
if self.verbose:
print 'Updating %s' % self.shorten(filename)
if not self.simulate:
f = open(filename, 'w')
f.write(''.join(lines))
f.close()
def run_command(self, cmd, *args, **kw):
"""
Runs the command, respecting verbosity and simulation.
Returns stdout, or None if simulating.
Keyword arguments:
cwd:
the current working directory to run the command in
capture_stderr:
if true, then both stdout and stderr will be returned
expect_returncode:
if true, then don't fail if the return code is not 0
force_no_simulate:
if true, run the command even if --simulate
"""
if subprocess is None:
raise RuntimeError('Environment does not support subprocess '
'module, cannot run command.')
cmd = self.quote_first_command_arg(cmd)
cwd = popdefault(kw, 'cwd', os.getcwd())
capture_stderr = popdefault(kw, 'capture_stderr', False)
expect_returncode = popdefault(kw, 'expect_returncode', False)
force = popdefault(kw, 'force_no_simulate', False)
warn_returncode = popdefault(kw, 'warn_returncode', False)
if warn_returncode:
expect_returncode = True
simulate = self.simulate
if force:
simulate = False
assert not kw, ("Arguments not expected: %s" % kw)
if capture_stderr:
stderr_pipe = subprocess.STDOUT
else:
stderr_pipe = subprocess.PIPE
try:
proc = subprocess.Popen([cmd] + list(args),
cwd=cwd,
stderr=stderr_pipe,
stdout=subprocess.PIPE)
except OSError, e:
if e.errno != 2:
# File not found
raise
raise OSError(
"The expected executable %s was not found (%s)"
% (cmd, e))
if self.verbose:
print 'Running %s %s' % (cmd, ' '.join(args))
if simulate:
return None
stdout, stderr = proc.communicate()
if proc.returncode and not expect_returncode:
if not self.verbose:
print 'Running %s %s' % (cmd, ' '.join(args))
print 'Error (exit code: %s)' % proc.returncode
if stderr:
print stderr
raise OSError("Error executing command %s" % cmd)
if self.verbose > 2:
if stderr:
print 'Command error output:'
print stderr
if stdout:
print 'Command output:'
print stdout
elif proc.returncode and warn_returncode:
print 'Warning: command failed (%s %s)' % (cmd, ' '.join(args))
print 'Exited with code %s' % proc.returncode
return stdout
def quote_first_command_arg(self, arg):
"""
There's a bug in Windows when running an executable that's
located inside a path with a space in it. This method handles
that case, or on non-Windows systems or an executable with no
spaces, it just leaves well enough alone.
"""
if (sys.platform != 'win32'
or ' ' not in arg):
# Problem does not apply:
return arg
try:
import win32api
except ImportError:
raise ValueError(
"The executable %r contains a space, and in order to "
"handle this issue you must have the win32api module "
"installed" % arg)
arg = win32api.GetShortPathName(arg)
return arg
_svn_failed = False
def svn_command(self, *args, **kw):
"""
Run an svn command, but don't raise an exception if it fails.
"""
try:
return self.run_command('svn', *args, **kw)
except OSError, e:
if not self._svn_failed:
print 'Unable to run svn command (%s); proceeding anyway' % e
self._svn_failed = True
def write_file(self, filename, content, source=None,
binary=True, svn_add=True):
"""
Like ``ensure_file``, but without the interactivity. Mostly
deprecated. (I think I forgot it existed)
"""
import warnings
warnings.warn(
"command.write_file has been replaced with "
"command.ensure_file",
DeprecationWarning, 2)
if os.path.exists(filename):
if binary:
f = open(filename, 'rb')
else:
f = open(filename, 'r')
old_content = f.read()
f.close()
if content == old_content:
if self.verbose:
print 'File %s exists with same content' % (
self.shorten(filename))
return
if (not self.simulate and self.options.interactive):
if not self.ask('Overwrite file %s?' % filename):
return
if self.verbose > 1 and source:
print 'Writing %s from %s' % (self.shorten(filename),
self.shorten(source))
elif self.verbose:
print 'Writing %s' % self.shorten(filename)
if not self.simulate:
already_existed = os.path.exists(filename)
if binary:
f = open(filename, 'wb')
else:
f = open(filename, 'w')
f.write(content)
f.close()
if (not already_existed
and svn_add
and os.path.exists(os.path.join(os.path.dirname(filename), '.svn'))):
self.svn_command('add', filename)
def parse_vars(self, args):
"""
Given variables like ``['a=b', 'c=d']`` turns it into ``{'a':
'b', 'c': 'd'}``
"""
result = {}
for arg in args:
if '=' not in arg:
raise BadCommand(
'Variable assignment %r invalid (no "=")'
% arg)
name, value = arg.split('=', 1)
result[name] = value
return result
def read_vars(self, config, section='pastescript'):
"""
Given a configuration filename, this will return a map of values.
"""
result = {}
p = ConfigParser.RawConfigParser()
p.read([config])
if p.has_section(section):
for key, value in p.items(section):
if key.endswith('__eval__'):
result[key[:-len('__eval__')]] = eval(value)
else:
result[key] = value
return result
def write_vars(self, config, vars, section='pastescript'):
"""
Given a configuration filename, this will add items in the
vars mapping to the configuration file. Will create the
configuration file if it doesn't exist.
"""
modified = False
p = ConfigParser.RawConfigParser()
if not os.path.exists(config):
f = open(config, 'w')
f.write('')
f.close()
modified = True
p.read([config])
if not p.has_section(section):
p.add_section(section)
modified = True
existing_options = p.options(section)
for key, value in vars.items():
if (key not in existing_options and
'%s__eval__' % key not in existing_options):
if not isinstance(value, str):
p.set(section, '%s__eval__' % key, repr(value))
else:
p.set(section, key, value)
modified = True
if modified:
p.write(open(config, 'w'))
def indent_block(self, text, indent=2, initial=None):
"""
Indent the block of text (each line is indented). If you give
``initial``, then that is used in lieue of ``indent`` for the
first line.
"""
if initial is None:
initial = indent
lines = text.splitlines()
first = (' '*initial) + lines[0]
rest = [(' '*indent)+l for l in lines[1:]]
return '\n'.join([first]+rest)
def logging_file_config(self, config_file):
"""
Setup logging via the logging module's fileConfig function with the
specified ``config_file``, if applicable.
ConfigParser defaults are specified for the special ``__file__``
and ``here`` variables, similar to PasteDeploy config loading.
"""
parser = ConfigParser.ConfigParser()
parser.read([config_file])
if parser.has_section('loggers'):
config_file = os.path.abspath(config_file)
fileConfig(config_file, dict(__file__=config_file,
here=os.path.dirname(config_file)))
class NotFoundCommand(Command):
def run(self, args):
#for name, value in os.environ.items():
# print '%s: %s' % (name, value)
#print sys.argv
print ('Command %r not known (you may need to run setup.py egg_info)'
% self.command_name)
commands = get_commands().items()
commands.sort()
if not commands:
print 'No commands registered.'
print 'Have you installed Paste Script?'
print '(try running python setup.py develop)'
return 2
print 'Known commands:'
longest = max([len(n) for n, c in commands])
for name, command in commands:
print ' %s %s' % (self.pad(name, length=longest),
command.load().summary)
return 2
def popdefault(dict, name, default=None):
if name not in dict:
return default
else:
v = dict[name]
del dict[name]
return v