0001# (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org)
0002# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
0003import pkg_resources
0004import sys
0005import optparse
0006import bool_optparse
0007import os
0008import re
0009import textwrap
0010import pluginlib
0011import ConfigParser
0012import getpass
0013try:
0014    import subprocess
0015except ImportError:
0016    from paste.script.util import subprocess24 as subprocess
0017difflib = None
0018
0019from paste.script.util.logging_config import fileConfig
0020
0021class BadCommand(Exception):
0022
0023    def __init__(self, message, exit_code=2):
0024        self.message = message
0025        self.exit_code = exit_code
0026        Exception.__init__(self, message)
0027
0028class NoDefault(object):
0029    pass
0030
0031dist = pkg_resources.get_distribution('PasteScript')
0032
0033python_version = sys.version.splitlines()[0].strip()
0034
0035parser = optparse.OptionParser(add_help_option=False,
0036                               version='%s from %s (python %s)'
0037                               % (dist, dist.location, python_version),
0038                               usage='%prog [paster_options] COMMAND [command_options]')
0039
0040parser.add_option(
0041    '--plugin',
0042    action='append',
0043    dest='plugins',
0044    help="Add a plugin to the list of commands (plugins are Egg specs; will also require() the Egg)")
0045parser.add_option(
0046    '-h', '--help',
0047    action='store_true',
0048    dest='do_help',
0049    help="Show this help message")
0050parser.disable_interspersed_args()
0051
0052# @@: Add an option to run this in another Python interpreter
0053
0054system_plugins = []
0055
0056def run(args=None):
0057    if (not args and
0058        len(sys.argv) >= 2
0059        and os.environ.get('_') and sys.argv[0] != os.environ['_']
0060        and os.environ['_'] == sys.argv[1]):
0061        # probably it's an exe execution
0062        args = ['exe', os.environ['_']] + sys.argv[2:]
0063    if args is None:
0064        args = sys.argv[1:]
0065    options, args = parser.parse_args(args)
0066    options.base_parser = parser
0067    system_plugins.extend(options.plugins or [])
0068    commands = get_commands()
0069    if options.do_help:
0070        args = ['help'] + args
0071    if not args:
0072        print 'Usage: %s COMMAND' % sys.argv[0]
0073        args = ['help']
0074    command_name = args[0]
0075    if command_name not in commands:
0076        command = NotFoundCommand
0077    else:
0078        command = commands[command_name].load()
0079    invoke(command, command_name, options, args[1:])
0080
0081def parse_exe_file(config):
0082    import shlex
0083    p = ConfigParser.RawConfigParser()
0084    p.read([config])
0085    command_name = 'exe'
0086    options = []
0087    if p.has_option('exe', 'command'):
0088        command_name = p.get('exe', 'command')
0089    if p.has_option('exe', 'options'):
0090        options = shlex.split(p.get('exe', 'options'))
0091    if p.has_option('exe', 'sys.path'):
0092        paths = shlex.split(p.get('exe', 'sys.path'))
0093        paths = [os.path.abspath(os.path.join(os.path.dirname(config), p))
0094                 for p in paths]
0095        for path in paths:
0096            pkg_resources.working_set.add_entry(path)
0097            sys.path.insert(0, path)
0098    args = [command_name, config] + options
0099    return args
0100
0101def get_commands():
0102    plugins = system_plugins[:]
0103    egg_info_dir = pluginlib.find_egg_info_dir(os.getcwd())
0104    if egg_info_dir:
0105        plugins.append(os.path.splitext(os.path.basename(egg_info_dir))[0])
0106        base_dir = os.path.dirname(egg_info_dir)
0107        if base_dir not in sys.path:
0108            sys.path.insert(0, base_dir)
0109            pkg_resources.working_set.add_entry(base_dir)
0110    plugins = pluginlib.resolve_plugins(plugins)
0111    commands = pluginlib.load_commands_from_plugins(plugins)
0112    commands.update(pluginlib.load_global_commands())
0113    return commands
0114
0115def invoke(command, command_name, options, args):
0116    try:
0117        runner = command(command_name)
0118        exit_code = runner.run(args)
0119    except BadCommand, e:
0120        print e.message
0121        exit_code = e.exit_code
0122    sys.exit(exit_code)
0123
0124
0125class Command(object):
0126
0127    def __init__(self, name):
0128        self.command_name = name
0129
0130    max_args = None
0131    max_args_error = 'You must provide no more than %(max_args)s arguments'
0132    min_args = None
0133    min_args_error = 'You must provide at least %(min_args)s arguments'
0134    required_args = None
0135    # If this command takes a configuration file, set this to 1 or -1
0136    # Then if invoked through #! the config file will be put into the positional
0137    # arguments -- at the beginning with 1, at the end with -1
0138    takes_config_file = None
0139
0140    # Grouped in help messages by this:
0141    group_name = ''
0142
0143    required_args = ()
0144    description = None
0145    usage = ''
0146    hidden = False
0147    # This is the default verbosity level; --quiet subtracts,
0148    # --verbose adds:
0149    default_verbosity = 0
0150    # This is the default interactive state:
0151    default_interactive = 0
0152    return_code = 0
0153
0154    BadCommand = BadCommand
0155
0156    # Must define:
0157    #   parser
0158    #   summary
0159    #   command()
0160
0161    def run(self, args):
0162        self.parse_args(args)
0163
0164        # Setup defaults:
0165        for name, default in [('verbose', 0),
0166                              ('quiet', 0),
0167                              ('interactive', False),
0168                              ('overwrite', False)]:
0169            if not hasattr(self.options, name):
0170                setattr(self.options, name, default)
0171        if getattr(self.options, 'simulate', False):
0172            self.options.verbose = max(self.options.verbose, 1)
0173        self.interactive = self.default_interactive
0174        if getattr(self.options, 'interactive', False):
0175            self.interactive += self.options.interactive
0176        if getattr(self.options, 'no_interactive', False):
0177            self.interactive = False
0178        self.verbose = self.default_verbosity
0179        self.verbose += self.options.verbose
0180        self.verbose -= self.options.quiet
0181        self.simulate = getattr(self.options, 'simulate', False)
0182
0183        # For #! situations:
0184        if (os.environ.get('PASTE_CONFIG_FILE')
0185            and self.takes_config_file is not None):
0186            take = self.takes_config_file
0187            filename = os.environ.get('PASTE_CONFIG_FILE')
0188            if take == 1:
0189                self.args.insert(0, filename)
0190            elif take == -1:
0191                self.args.append(filename)
0192            else:
0193                assert 0, (
0194                    "Value takes_config_file must be None, 1, or -1 (not %r)"
0195                    % take)
0196
0197        if (os.environ.get('PASTE_DEFAULT_QUIET')):
0198            self.verbose = 0
0199
0200        # Validate:
0201        if self.min_args is not None and len(self.args) < self.min_args:
0202            raise BadCommand(
0203                self.min_args_error % {'min_args': self.min_args,
0204                                       'actual_args': len(self.args)})
0205        if self.max_args is not None and len(self.args) > self.max_args:
0206            raise BadCommand(
0207                self.max_args_error % {'max_args': self.max_args,
0208                                       'actual_args': len(self.args)})
0209        for var_name, option_name in self.required_args:
0210            if not getattr(self.options, var_name, None):
0211                raise BadCommand(
0212                    'You must provide the option %s' % option_name)
0213        result = self.command()
0214        if result is None:
0215            return self.return_code
0216        else:
0217            return result
0218
0219    def parse_args(self, args):
0220        if self.usage:
0221            usage = ' '+self.usage
0222        else:
0223            usage = ''
0224        self.parser.usage = "%%prog [options]%s\n%s" % (
0225            usage, self.summary)
0226        self.parser.prog = '%s %s' % (sys.argv[0], self.command_name)
0227        if self.description:
0228            desc = self.description
0229            desc = textwrap.dedent(desc)
0230            self.parser.description = desc
0231        self.options, self.args = self.parser.parse_args(args)
0232
0233    ########################################
0234    ## Utility methods
0235    ########################################
0236
0237    def here(cls):
0238        mod = sys.modules[cls.__module__]
0239        return os.path.dirname(mod.__file__)
0240
0241    here = classmethod(here)
0242
0243    def ask(self, prompt, safe=False, default=True):
0244        """
0245        Prompt the user.  Default can be true, false, ``'careful'`` or
0246        ``'none'``.  If ``'none'`` then the user must enter y/n.  If
0247        ``'careful'`` then the user must enter yes/no (long form).
0248
0249        If the interactive option is over two (``-ii``) then ``safe``
0250        will be used as a default.  This option should be the
0251        do-nothing option.
0252        """
0253        # @@: Should careful be a separate argument?
0254
0255        if self.options.interactive >= 2:
0256            default = safe
0257        if default == 'careful':
0258            prompt += ' [yes/no]?'
0259        elif default == 'none':
0260            prompt += ' [y/n]?'
0261        elif default:
0262            prompt += ' [Y/n]? '
0263        else:
0264            prompt += ' [y/N]? '
0265        while 1:
0266            response = raw_input(prompt).strip().lower()
0267            if not response:
0268                if default in ('careful', 'none'):
0269                    print 'Please enter yes or no'
0270                    continue
0271                return default
0272            if default == 'careful':
0273                if response in ('yes', 'no'):
0274                    return response == 'yes'
0275                print 'Please enter "yes" or "no"'
0276                continue
0277            if response[0].lower() in ('y', 'n'):
0278                return response[0].lower() == 'y'
0279            print 'Y or N please'
0280
0281    def challenge(self, prompt, default=NoDefault, should_echo=True):
0282        """
0283        Prompt the user for a variable.
0284        """
0285        if default is not NoDefault:
0286            prompt += ' [%r]' % default
0287        prompt += ': '
0288        while 1:
0289            if should_echo:
0290                prompt_method = raw_input
0291            else:
0292                prompt_method = getpass.getpass
0293            response = prompt_method(prompt).strip()
0294            if not response:
0295                if default is not NoDefault:
0296                    return default
0297                else:
0298                    continue
0299            else:
0300                return response
0301
0302    def pad(self, s, length, dir='left'):
0303        if len(s) >= length:
0304            return s
0305        if dir == 'left':
0306            return s + ' '*(length-len(s))
0307        else:
0308            return ' '*(length-len(s)) + s
0309
0310    def standard_parser(cls, verbose=True,
0311                        interactive=False,
0312                        no_interactive=False,
0313                        simulate=False,
0314                        quiet=False,
0315                        overwrite=False):
0316        """
0317        Create a standard ``OptionParser`` instance.
0318        
0319        Typically used like::
0320
0321            class MyCommand(Command):
0322                parser = Command.standard_parser()
0323
0324        Subclasses may redefine ``standard_parser``, so use the
0325        nearest superclass's class method.
0326        """
0327        parser = bool_optparse.BoolOptionParser()
0328        if verbose:
0329            parser.add_option('-v', '--verbose',
0330                              action='count',
0331                              dest='verbose',
0332                              default=0)
0333        if quiet:
0334            parser.add_option('-q', '--quiet',
0335                              action='count',
0336                              dest='quiet',
0337                              default=0)
0338        if no_interactive:
0339            parser.add_option('--no-interactive',
0340                              action="count",
0341                              dest="no_interactive",
0342                              default=0)
0343        if interactive:
0344            parser.add_option('-i', '--interactive',
0345                              action='count',
0346                              dest='interactive',
0347                              default=0)
0348        if simulate:
0349            parser.add_option('-n', '--simulate',
0350                              action='store_true',
0351                              dest='simulate',
0352                              default=False)
0353        if overwrite:
0354            parser.add_option('-f', '--overwrite',
0355                              dest="overwrite",
0356                              action="store_true",
0357                              help="Overwrite files (warnings will be emitted for non-matching files otherwise)")
0358        return parser
0359
0360    standard_parser = classmethod(standard_parser)
0361
0362    def shorten(self, fn, *paths):
0363        """
0364        Return a shorted form of the filename (relative to the current
0365        directory), typically for displaying in messages.  If
0366        ``*paths`` are present, then use os.path.join to create the
0367        full filename before shortening.
0368        """
0369        if paths:
0370            fn = os.path.join(fn, *paths)
0371        if fn.startswith(os.getcwd()):
0372            return fn[len(os.getcwd()):].lstrip(os.path.sep)
0373        else:
0374            return fn
0375
0376    def ensure_dir(self, dir, svn_add=True):
0377        """
0378        Ensure that the directory exists, creating it if necessary.
0379        Respects verbosity and simulation.
0380
0381        Adds directory to subversion if ``.svn/`` directory exists in
0382        parent, and directory was created.
0383        """
0384        dir = dir.rstrip(os.sep)
0385        if not dir:
0386            # we either reached the parent-most directory, or we got
0387            # a relative directory
0388            # @@: Should we make sure we resolve relative directories
0389            # first?  Though presumably the current directory always
0390            # exists.
0391            return
0392        if not os.path.exists(dir):
0393            self.ensure_dir(os.path.dirname(dir))
0394            if self.verbose:
0395                print 'Creating %s' % self.shorten(dir)
0396            if not self.simulate:
0397                os.mkdir(dir)
0398            if (svn_add and
0399                os.path.exists(os.path.join(os.path.dirname(dir), '.svn'))):
0400                self.svn_command('add', dir)
0401        else:
0402            if self.verbose > 1:
0403                print "Directory already exists: %s" % self.shorten(dir)
0404
0405    def ensure_file(self, filename, content, svn_add=True):
0406        """
0407        Ensure a file named ``filename`` exists with the given
0408        content.  If ``--interactive`` has been enabled, this will ask
0409        the user what to do if a file exists with different content.
0410        """
0411        global difflib
0412        assert content is not None, (
0413            "You cannot pass a content of None")
0414        self.ensure_dir(os.path.dirname(filename), svn_add=svn_add)
0415        if not os.path.exists(filename):
0416            if self.verbose:
0417                print 'Creating %s' % filename
0418            if not self.simulate:
0419                f = open(filename, 'wb')
0420                f.write(content)
0421                f.close()
0422            if svn_add and os.path.exists(os.path.join(os.path.dirname(filename), '.svn')):
0423                self.svn_command('add', filename,
0424                                 warn_returncode=True)
0425            return
0426        f = open(filename, 'rb')
0427        old_content = f.read()
0428        f.close()
0429        if content == old_content:
0430            if self.verbose > 1:
0431                print 'File %s matches expected content' % filename
0432            return
0433        if not self.options.overwrite:
0434            print 'Warning: file %s does not match expected content' % filename
0435            if difflib is None:
0436                import difflib
0437            diff = difflib.context_diff(
0438                content.splitlines(),
0439                old_content.splitlines(),
0440                'expected ' + filename,
0441                filename)
0442            print '\n'.join(diff)
0443            if self.interactive:
0444                while 1:
0445                    s = raw_input(
0446                        'Overwrite file with new content? [y/N] ').strip().lower()
0447                    if not s:
0448                        s = 'n'
0449                    if s.startswith('y'):
0450                        break
0451                    if s.startswith('n'):
0452                        return
0453                    print 'Unknown response; Y or N please'
0454            else:
0455                return
0456
0457        if self.verbose:
0458            print 'Overwriting %s with new content' % filename
0459        if not self.simulate:
0460            f = open(filename, 'wb')
0461            f.write(content)
0462            f.close()
0463
0464    def insert_into_file(self, filename, marker_name, text,
0465                         indent=False):
0466        """
0467        Inserts ``text`` into the file, right after the given marker.
0468        Markers look like: ``-*- <marker_name>[:]? -*-``, and the text
0469        will go on the immediately following line.
0470
0471        Raises ``ValueError`` if the marker is not found.
0472
0473        If ``indent`` is true, then the text will be indented at the
0474        same level as the marker.
0475        """
0476        if not text.endswith('\n'):
0477            raise ValueError(
0478                "The text must end with a newline: %r" % text)
0479        if not os.path.exists(filename) and self.simulate:
0480            # If we are doing a simulation, it's expected that some
0481            # files won't exist...
0482            if self.verbose:
0483                print 'Would (if not simulating) insert text into %s' % (
0484                    self.shorten(filename))
0485            return
0486
0487        f = open(filename)
0488        lines = f.readlines()
0489        f.close()
0490        regex = re.compile(r'-\*-\s+%s:?\s+-\*-' % re.escape(marker_name),
0491                           re.I)
0492        for i in range(len(lines)):
0493            if regex.search(lines[i]):
0494                # Found it!
0495                if (lines[i:] and len(lines[i:]) > 1 and
0496                    ''.join(lines[i+1:]).strip().startswith(text.strip())):
0497                    # Already have it!
0498                    print 'Warning: line already found in %s (not inserting' % filename
0499                    print '  %s' % lines[i]
0500                    return
0501
0502                if indent:
0503                    text = text.lstrip()
0504                    match = re.search(r'^[ \t]*', lines[i])
0505                    text = match.group(0) + text
0506                lines[i+1:i+1] = [text]
0507                break
0508        else:
0509            errstr = (
0510                "Marker '-*- %s -*-' not found in %s"
0511                % (marker_name, filename))
0512            if 1 or self.simulate: # @@: being permissive right now
0513                print 'Warning: %s' % errstr
0514            else:
0515                raise ValueError(errstr)
0516        if self.verbose:
0517            print 'Updating %s' % self.shorten(filename)
0518        if not self.simulate:
0519            f = open(filename, 'w')
0520            f.write(''.join(lines))
0521            f.close()
0522
0523    def run_command(self, cmd, *args, **kw):
0524        """
0525        Runs the command, respecting verbosity and simulation.
0526        Returns stdout, or None if simulating.
0527        
0528        Keyword arguments:
0529        
0530        cwd: 
0531            the current working directory to run the command in
0532        capture_stderr: 
0533            if true, then both stdout and stderr will be returned
0534        expect_returncode: 
0535            if true, then don't fail if the return code is not 0
0536        force_no_simulate:
0537            if true, run the command even if --simulate
0538        """
0539        cmd = self.quote_first_command_arg(cmd)
0540        cwd = popdefault(kw, 'cwd', os.getcwd())
0541        capture_stderr = popdefault(kw, 'capture_stderr', False)
0542        expect_returncode = popdefault(kw, 'expect_returncode', False)
0543        force = popdefault(kw, 'force_no_simulate', False)
0544        warn_returncode = popdefault(kw, 'warn_returncode', False)
0545        if warn_returncode:
0546            expect_returncode = True
0547        simulate = self.simulate
0548        if force:
0549            simulate = False
0550        assert not kw, ("Arguments not expected: %s" % kw)
0551        if capture_stderr:
0552            stderr_pipe = subprocess.STDOUT
0553        else:
0554            stderr_pipe = subprocess.PIPE
0555        try:
0556            proc = subprocess.Popen([cmd] + list(args),
0557                                    cwd=cwd,
0558                                    stderr=stderr_pipe,
0559                                    stdout=subprocess.PIPE)
0560        except OSError, e:
0561            if e.errno != 2:
0562                # File not found
0563                raise
0564            raise OSError(
0565                "The expected executable %s was not found (%s)"
0566                % (cmd, e))
0567        if self.verbose:
0568            print 'Running %s %s' % (cmd, ' '.join(args))
0569        if simulate:
0570            return None
0571        stdout, stderr = proc.communicate()
0572        if proc.returncode and not expect_returncode:
0573            if not self.verbose:
0574                print 'Running %s %s' % (cmd, ' '.join(args))
0575            print 'Error (exit code: %s)' % proc.returncode
0576            if stderr:
0577                print stderr
0578            raise OSError("Error executing command %s" % cmd)
0579        if self.verbose > 2:
0580            if stderr:
0581                print 'Command error output:'
0582                print stderr
0583            if stdout:
0584                print 'Command output:'
0585                print stdout
0586        elif proc.returncode and warn_returncode:
0587            print 'Warning: command failed (%s %s)' % (cmd, ' '.join(args))
0588            print 'Exited with code %s' % proc.returncode
0589        return stdout
0590
0591    def quote_first_command_arg(self, arg):
0592        """
0593        There's a bug in Windows when running an executable that's
0594        located inside a path with a space in it.  This method handles
0595        that case, or on non-Windows systems or an executable with no
0596        spaces, it just leaves well enough alone.
0597        """
0598        if (sys.platform != 'win32'
0599            or ' ' not in arg):
0600            # Problem does not apply:
0601            return arg
0602        try:
0603            import win32api
0604        except ImportError:
0605            raise ValueError(
0606                "The executable %r contains a space, and in order to "
0607                "handle this issue you must have the win32api module "
0608                "installed" % arg)
0609        arg = win32api.GetShortPathName(arg)
0610        return arg
0611
0612    _svn_failed = False
0613
0614    def svn_command(self, *args, **kw):
0615        """
0616        Run an svn command, but don't raise an exception if it fails.
0617        """
0618        try:
0619            return self.run_command('svn', *args, **kw)
0620        except OSError, e:
0621            if not self._svn_failed:
0622                print 'Unable to run svn command (%s); proceeding anyway' % e
0623                self._svn_failed = True
0624
0625    def write_file(self, filename, content, source=None,
0626                   binary=True, svn_add=True):
0627        """
0628        Like ``ensure_file``, but without the interactivity.  Mostly
0629        deprecated.  (I think I forgot it existed)
0630        """
0631        import warnings
0632        warnings.warn(
0633            "command.write_file has been replaced with "
0634            "command.ensure_file",
0635            DeprecationWarning, 2)
0636        if os.path.exists(filename):
0637            if binary:
0638                f = open(filename, 'rb')
0639            else:
0640                f = open(filename, 'r')
0641            old_content = f.read()
0642            f.close()
0643            if content == old_content:
0644                if self.verbose:
0645                    print 'File %s exists with same content' % (
0646                        self.shorten(filename))
0647                return
0648            if (not self.simulate and self.options.interactive):
0649                if not self.ask('Overwrite file %s?' % filename):
0650                    return
0651        if self.verbose > 1 and source:
0652            print 'Writing %s from %s' % (self.shorten(filename),
0653                                          self.shorten(source))
0654        elif self.verbose:
0655            print 'Writing %s' % self.shorten(filename)
0656        if not self.simulate:
0657            already_existed = os.path.exists(filename)
0658            if binary:
0659                f = open(filename, 'wb')
0660            else:
0661                f = open(filename, 'w')
0662            f.write(content)
0663            f.close()
0664            if (not already_existed
0665                and svn_add
0666                and os.path.exists(os.path.join(os.path.dirname(filename), '.svn'))):
0667                self.svn_command('add', filename)
0668
0669    def parse_vars(self, args):
0670        """
0671        Given variables like ``['a=b', 'c=d']`` turns it into ``{'a':
0672        'b', 'c': 'd'}``
0673        """
0674        result = {}
0675        for arg in args:
0676            if '=' not in arg:
0677                raise BadCommand(
0678                    'Variable assignment %r invalid (no "=")'
0679                    % arg)
0680            name, value = arg.split('=', 1)
0681            result[name] = value
0682        return result
0683
0684    def read_vars(self, config, section='pastescript'):
0685        """
0686        Given a configuration filename, this will return a map of values.
0687        """
0688        result = {}
0689        p = ConfigParser.RawConfigParser()
0690        p.read([config])
0691        if p.has_section(section):
0692            for key, value in p.items(section):
0693                if key.endswith('__eval__'):
0694                    result[key[:-len('__eval__')]] = eval(value)
0695                else:
0696                    result[key] = value
0697        return result
0698
0699    def write_vars(self, config, vars, section='pastescript'):
0700        """
0701        Given a configuration filename, this will add items in the
0702        vars mapping to the configuration file.  Will create the
0703        configuration file if it doesn't exist.
0704        """
0705        modified = False
0706
0707        p = ConfigParser.RawConfigParser()
0708        if not os.path.exists(config):
0709            f = open(config, 'w')
0710            f.write('')
0711            f.close()
0712            modified = True
0713        p.read([config])
0714        if not p.has_section(section):
0715            p.add_section(section)
0716            modified = True
0717
0718        existing_options = p.options(section)
0719        for key, value in vars.items():
0720            if (key not in existing_options and
0721                '%s__eval__' % key not in existing_options):
0722                if not isinstance(value, str):
0723                    p.set(section, '%s__eval__' % key, repr(value))
0724                else:
0725                    p.set(section, key, value)
0726                modified = True
0727
0728        if modified:
0729            p.write(open(config, 'w'))
0730
0731    def indent_block(self, text, indent=2, initial=None):
0732        """
0733        Indent the block of text (each line is indented).  If you give
0734        ``initial``, then that is used in lieue of ``indent`` for the
0735        first line.
0736        """
0737        if initial is None:
0738            initial = indent
0739        lines = text.splitlines()
0740        first = (' '*initial) + lines[0]
0741        rest = [(' '*indent)+l for l in lines[1:]]
0742        return '\n'.join([first]+rest)
0743
0744    def logging_file_config(self, config_file):
0745        """
0746        Setup logging via the logging module's fileConfig function with the
0747        specified ``config_file``, if applicable.
0748        """
0749        parser = ConfigParser.ConfigParser()
0750        parser.read([config_file])
0751        if parser.has_section('loggers'):
0752            fileConfig(config_file)
0753
0754class NotFoundCommand(Command):
0755
0756    def run(self, args):
0757        #for name, value in os.environ.items():
0758        #    print '%s: %s' % (name, value)
0759        #print sys.argv
0760        print 'Command %s not known' % self.command_name
0761        commands = get_commands().items()
0762        commands.sort()
0763        if not commands:
0764            print 'No commands registered.'
0765            print 'Have you installed Paste Script?'
0766            print '(try running python setup.py develop)'
0767            return 2
0768        print 'Known commands:'
0769        longest = max([len(n) for n, c in commands])
0770        for name, command in commands:
0771            print '  %s  %s' % (self.pad(name, length=longest),
0772                                command.load().summary)
0773        return 2
0774
0775def popdefault(dict, name, default=None):
0776    if name not in dict:
0777        return default
0778    else:
0779        v = dict[name]
0780        del dict[name]
0781        return v