You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							426 lines
						
					
					
						
							15 KiB
						
					
					
				
			
		
		
	
	
							426 lines
						
					
					
						
							15 KiB
						
					
					
				# -*- coding: utf-8 -*-
 | 
						|
"""
 | 
						|
    click.parser
 | 
						|
    ~~~~~~~~~~~~
 | 
						|
 | 
						|
    This module started out as largely a copy paste from the stdlib's
 | 
						|
    optparse module with the features removed that we do not need from
 | 
						|
    optparse because we implement them in Click on a higher level (for
 | 
						|
    instance type handling, help formatting and a lot more).
 | 
						|
 | 
						|
    The plan is to remove more and more from here over time.
 | 
						|
 | 
						|
    The reason this is a different module and not optparse from the stdlib
 | 
						|
    is that there are differences in 2.x and 3.x about the error messages
 | 
						|
    generated and optparse in the stdlib uses gettext for no good reason
 | 
						|
    and might cause us issues.
 | 
						|
"""
 | 
						|
import re
 | 
						|
from collections import deque
 | 
						|
from .exceptions import UsageError, NoSuchOption, BadOptionUsage, \
 | 
						|
     BadArgumentUsage
 | 
						|
 | 
						|
 | 
						|
def _unpack_args(args, nargs_spec):
 | 
						|
    """Given an iterable of arguments and an iterable of nargs specifications,
 | 
						|
    it returns a tuple with all the unpacked arguments at the first index
 | 
						|
    and all remaining arguments as the second.
 | 
						|
 | 
						|
    The nargs specification is the number of arguments that should be consumed
 | 
						|
    or `-1` to indicate that this position should eat up all the remainders.
 | 
						|
 | 
						|
    Missing items are filled with `None`.
 | 
						|
    """
 | 
						|
    args = deque(args)
 | 
						|
    nargs_spec = deque(nargs_spec)
 | 
						|
    rv = []
 | 
						|
    spos = None
 | 
						|
 | 
						|
    def _fetch(c):
 | 
						|
        try:
 | 
						|
            if spos is None:
 | 
						|
                return c.popleft()
 | 
						|
            else:
 | 
						|
                return c.pop()
 | 
						|
        except IndexError:
 | 
						|
            return None
 | 
						|
 | 
						|
    while nargs_spec:
 | 
						|
        nargs = _fetch(nargs_spec)
 | 
						|
        if nargs == 1:
 | 
						|
            rv.append(_fetch(args))
 | 
						|
        elif nargs > 1:
 | 
						|
            x = [_fetch(args) for _ in range(nargs)]
 | 
						|
            # If we're reversed, we're pulling in the arguments in reverse,
 | 
						|
            # so we need to turn them around.
 | 
						|
            if spos is not None:
 | 
						|
                x.reverse()
 | 
						|
            rv.append(tuple(x))
 | 
						|
        elif nargs < 0:
 | 
						|
            if spos is not None:
 | 
						|
                raise TypeError('Cannot have two nargs < 0')
 | 
						|
            spos = len(rv)
 | 
						|
            rv.append(None)
 | 
						|
 | 
						|
    # spos is the position of the wildcard (star).  If it's not `None`,
 | 
						|
    # we fill it with the remainder.
 | 
						|
    if spos is not None:
 | 
						|
        rv[spos] = tuple(args)
 | 
						|
        args = []
 | 
						|
        rv[spos + 1:] = reversed(rv[spos + 1:])
 | 
						|
 | 
						|
    return tuple(rv), list(args)
 | 
						|
 | 
						|
 | 
						|
def _error_opt_args(nargs, opt):
 | 
						|
    if nargs == 1:
 | 
						|
        raise BadOptionUsage('%s option requires an argument' % opt)
 | 
						|
    raise BadOptionUsage('%s option requires %d arguments' % (opt, nargs))
 | 
						|
 | 
						|
 | 
						|
def split_opt(opt):
 | 
						|
    first = opt[:1]
 | 
						|
    if first.isalnum():
 | 
						|
        return '', opt
 | 
						|
    if opt[1:2] == first:
 | 
						|
        return opt[:2], opt[2:]
 | 
						|
    return first, opt[1:]
 | 
						|
 | 
						|
 | 
						|
def normalize_opt(opt, ctx):
 | 
						|
    if ctx is None or ctx.token_normalize_func is None:
 | 
						|
        return opt
 | 
						|
    prefix, opt = split_opt(opt)
 | 
						|
    return prefix + ctx.token_normalize_func(opt)
 | 
						|
 | 
						|
 | 
						|
def split_arg_string(string):
 | 
						|
    """Given an argument string this attempts to split it into small parts."""
 | 
						|
    rv = []
 | 
						|
    for match in re.finditer(r"('([^'\\]*(?:\\.[^'\\]*)*)'"
 | 
						|
                             r'|"([^"\\]*(?:\\.[^"\\]*)*)"'
 | 
						|
                             r'|\S+)\s*', string, re.S):
 | 
						|
        arg = match.group().strip()
 | 
						|
        if arg[:1] == arg[-1:] and arg[:1] in '"\'':
 | 
						|
            arg = arg[1:-1].encode('ascii', 'backslashreplace') \
 | 
						|
                .decode('unicode-escape')
 | 
						|
        try:
 | 
						|
            arg = type(string)(arg)
 | 
						|
        except UnicodeError:
 | 
						|
            pass
 | 
						|
        rv.append(arg)
 | 
						|
    return rv
 | 
						|
 | 
						|
 | 
						|
class Option(object):
 | 
						|
 | 
						|
    def __init__(self, opts, dest, action=None, nargs=1, const=None, obj=None):
 | 
						|
        self._short_opts = []
 | 
						|
        self._long_opts = []
 | 
						|
        self.prefixes = set()
 | 
						|
 | 
						|
        for opt in opts:
 | 
						|
            prefix, value = split_opt(opt)
 | 
						|
            if not prefix:
 | 
						|
                raise ValueError('Invalid start character for option (%s)'
 | 
						|
                                 % opt)
 | 
						|
            self.prefixes.add(prefix[0])
 | 
						|
            if len(prefix) == 1 and len(value) == 1:
 | 
						|
                self._short_opts.append(opt)
 | 
						|
            else:
 | 
						|
                self._long_opts.append(opt)
 | 
						|
                self.prefixes.add(prefix)
 | 
						|
 | 
						|
        if action is None:
 | 
						|
            action = 'store'
 | 
						|
 | 
						|
        self.dest = dest
 | 
						|
        self.action = action
 | 
						|
        self.nargs = nargs
 | 
						|
        self.const = const
 | 
						|
        self.obj = obj
 | 
						|
 | 
						|
    @property
 | 
						|
    def takes_value(self):
 | 
						|
        return self.action in ('store', 'append')
 | 
						|
 | 
						|
    def process(self, value, state):
 | 
						|
        if self.action == 'store':
 | 
						|
            state.opts[self.dest] = value
 | 
						|
        elif self.action == 'store_const':
 | 
						|
            state.opts[self.dest] = self.const
 | 
						|
        elif self.action == 'append':
 | 
						|
            state.opts.setdefault(self.dest, []).append(value)
 | 
						|
        elif self.action == 'append_const':
 | 
						|
            state.opts.setdefault(self.dest, []).append(self.const)
 | 
						|
        elif self.action == 'count':
 | 
						|
            state.opts[self.dest] = state.opts.get(self.dest, 0) + 1
 | 
						|
        else:
 | 
						|
            raise ValueError('unknown action %r' % self.action)
 | 
						|
        state.order.append(self.obj)
 | 
						|
 | 
						|
 | 
						|
class Argument(object):
 | 
						|
 | 
						|
    def __init__(self, dest, nargs=1, obj=None):
 | 
						|
        self.dest = dest
 | 
						|
        self.nargs = nargs
 | 
						|
        self.obj = obj
 | 
						|
 | 
						|
    def process(self, value, state):
 | 
						|
        if self.nargs > 1:
 | 
						|
            holes = sum(1 for x in value if x is None)
 | 
						|
            if holes == len(value):
 | 
						|
                value = None
 | 
						|
            elif holes != 0:
 | 
						|
                raise BadArgumentUsage('argument %s takes %d values'
 | 
						|
                                       % (self.dest, self.nargs))
 | 
						|
        state.opts[self.dest] = value
 | 
						|
        state.order.append(self.obj)
 | 
						|
 | 
						|
 | 
						|
class ParsingState(object):
 | 
						|
 | 
						|
    def __init__(self, rargs):
 | 
						|
        self.opts = {}
 | 
						|
        self.largs = []
 | 
						|
        self.rargs = rargs
 | 
						|
        self.order = []
 | 
						|
 | 
						|
 | 
						|
class OptionParser(object):
 | 
						|
    """The option parser is an internal class that is ultimately used to
 | 
						|
    parse options and arguments.  It's modelled after optparse and brings
 | 
						|
    a similar but vastly simplified API.  It should generally not be used
 | 
						|
    directly as the high level Click classes wrap it for you.
 | 
						|
 | 
						|
    It's not nearly as extensible as optparse or argparse as it does not
 | 
						|
    implement features that are implemented on a higher level (such as
 | 
						|
    types or defaults).
 | 
						|
 | 
						|
    :param ctx: optionally the :class:`~click.Context` where this parser
 | 
						|
                should go with.
 | 
						|
    """
 | 
						|
 | 
						|
    def __init__(self, ctx=None):
 | 
						|
        #: The :class:`~click.Context` for this parser.  This might be
 | 
						|
        #: `None` for some advanced use cases.
 | 
						|
        self.ctx = ctx
 | 
						|
        #: This controls how the parser deals with interspersed arguments.
 | 
						|
        #: If this is set to `False`, the parser will stop on the first
 | 
						|
        #: non-option.  Click uses this to implement nested subcommands
 | 
						|
        #: safely.
 | 
						|
        self.allow_interspersed_args = True
 | 
						|
        #: This tells the parser how to deal with unknown options.  By
 | 
						|
        #: default it will error out (which is sensible), but there is a
 | 
						|
        #: second mode where it will ignore it and continue processing
 | 
						|
        #: after shifting all the unknown options into the resulting args.
 | 
						|
        self.ignore_unknown_options = False
 | 
						|
        if ctx is not None:
 | 
						|
            self.allow_interspersed_args = ctx.allow_interspersed_args
 | 
						|
            self.ignore_unknown_options = ctx.ignore_unknown_options
 | 
						|
        self._short_opt = {}
 | 
						|
        self._long_opt = {}
 | 
						|
        self._opt_prefixes = set(['-', '--'])
 | 
						|
        self._args = []
 | 
						|
 | 
						|
    def add_option(self, opts, dest, action=None, nargs=1, const=None,
 | 
						|
                   obj=None):
 | 
						|
        """Adds a new option named `dest` to the parser.  The destination
 | 
						|
        is not inferred (unlike with optparse) and needs to be explicitly
 | 
						|
        provided.  Action can be any of ``store``, ``store_const``,
 | 
						|
        ``append``, ``appnd_const`` or ``count``.
 | 
						|
 | 
						|
        The `obj` can be used to identify the option in the order list
 | 
						|
        that is returned from the parser.
 | 
						|
        """
 | 
						|
        if obj is None:
 | 
						|
            obj = dest
 | 
						|
        opts = [normalize_opt(opt, self.ctx) for opt in opts]
 | 
						|
        option = Option(opts, dest, action=action, nargs=nargs,
 | 
						|
                        const=const, obj=obj)
 | 
						|
        self._opt_prefixes.update(option.prefixes)
 | 
						|
        for opt in option._short_opts:
 | 
						|
            self._short_opt[opt] = option
 | 
						|
        for opt in option._long_opts:
 | 
						|
            self._long_opt[opt] = option
 | 
						|
 | 
						|
    def add_argument(self, dest, nargs=1, obj=None):
 | 
						|
        """Adds a positional argument named `dest` to the parser.
 | 
						|
 | 
						|
        The `obj` can be used to identify the option in the order list
 | 
						|
        that is returned from the parser.
 | 
						|
        """
 | 
						|
        if obj is None:
 | 
						|
            obj = dest
 | 
						|
        self._args.append(Argument(dest=dest, nargs=nargs, obj=obj))
 | 
						|
 | 
						|
    def parse_args(self, args):
 | 
						|
        """Parses positional arguments and returns ``(values, args, order)``
 | 
						|
        for the parsed options and arguments as well as the leftover
 | 
						|
        arguments if there are any.  The order is a list of objects as they
 | 
						|
        appear on the command line.  If arguments appear multiple times they
 | 
						|
        will be memorized multiple times as well.
 | 
						|
        """
 | 
						|
        state = ParsingState(args)
 | 
						|
        try:
 | 
						|
            self._process_args_for_options(state)
 | 
						|
            self._process_args_for_args(state)
 | 
						|
        except UsageError:
 | 
						|
            if self.ctx is None or not self.ctx.resilient_parsing:
 | 
						|
                raise
 | 
						|
        return state.opts, state.largs, state.order
 | 
						|
 | 
						|
    def _process_args_for_args(self, state):
 | 
						|
        pargs, args = _unpack_args(state.largs + state.rargs,
 | 
						|
                                   [x.nargs for x in self._args])
 | 
						|
 | 
						|
        for idx, arg in enumerate(self._args):
 | 
						|
            arg.process(pargs[idx], state)
 | 
						|
 | 
						|
        state.largs = args
 | 
						|
        state.rargs = []
 | 
						|
 | 
						|
    def _process_args_for_options(self, state):
 | 
						|
        while state.rargs:
 | 
						|
            arg = state.rargs.pop(0)
 | 
						|
            arglen = len(arg)
 | 
						|
            # Double dashes always handled explicitly regardless of what
 | 
						|
            # prefixes are valid.
 | 
						|
            if arg == '--':
 | 
						|
                return
 | 
						|
            elif arg[:1] in self._opt_prefixes and arglen > 1:
 | 
						|
                self._process_opts(arg, state)
 | 
						|
            elif self.allow_interspersed_args:
 | 
						|
                state.largs.append(arg)
 | 
						|
            else:
 | 
						|
                state.rargs.insert(0, arg)
 | 
						|
                return
 | 
						|
 | 
						|
        # Say this is the original argument list:
 | 
						|
        # [arg0, arg1, ..., arg(i-1), arg(i), arg(i+1), ..., arg(N-1)]
 | 
						|
        #                            ^
 | 
						|
        # (we are about to process arg(i)).
 | 
						|
        #
 | 
						|
        # Then rargs is [arg(i), ..., arg(N-1)] and largs is a *subset* of
 | 
						|
        # [arg0, ..., arg(i-1)] (any options and their arguments will have
 | 
						|
        # been removed from largs).
 | 
						|
        #
 | 
						|
        # The while loop will usually consume 1 or more arguments per pass.
 | 
						|
        # If it consumes 1 (eg. arg is an option that takes no arguments),
 | 
						|
        # then after _process_arg() is done the situation is:
 | 
						|
        #
 | 
						|
        #   largs = subset of [arg0, ..., arg(i)]
 | 
						|
        #   rargs = [arg(i+1), ..., arg(N-1)]
 | 
						|
        #
 | 
						|
        # If allow_interspersed_args is false, largs will always be
 | 
						|
        # *empty* -- still a subset of [arg0, ..., arg(i-1)], but
 | 
						|
        # not a very interesting subset!
 | 
						|
 | 
						|
    def _match_long_opt(self, opt, explicit_value, state):
 | 
						|
        if opt not in self._long_opt:
 | 
						|
            possibilities = [word for word in self._long_opt
 | 
						|
                             if word.startswith(opt)]
 | 
						|
            raise NoSuchOption(opt, possibilities=possibilities)
 | 
						|
 | 
						|
        option = self._long_opt[opt]
 | 
						|
        if option.takes_value:
 | 
						|
            # At this point it's safe to modify rargs by injecting the
 | 
						|
            # explicit value, because no exception is raised in this
 | 
						|
            # branch.  This means that the inserted value will be fully
 | 
						|
            # consumed.
 | 
						|
            if explicit_value is not None:
 | 
						|
                state.rargs.insert(0, explicit_value)
 | 
						|
 | 
						|
            nargs = option.nargs
 | 
						|
            if len(state.rargs) < nargs:
 | 
						|
                _error_opt_args(nargs, opt)
 | 
						|
            elif nargs == 1:
 | 
						|
                value = state.rargs.pop(0)
 | 
						|
            else:
 | 
						|
                value = tuple(state.rargs[:nargs])
 | 
						|
                del state.rargs[:nargs]
 | 
						|
 | 
						|
        elif explicit_value is not None:
 | 
						|
            raise BadOptionUsage('%s option does not take a value' % opt)
 | 
						|
 | 
						|
        else:
 | 
						|
            value = None
 | 
						|
 | 
						|
        option.process(value, state)
 | 
						|
 | 
						|
    def _match_short_opt(self, arg, state):
 | 
						|
        stop = False
 | 
						|
        i = 1
 | 
						|
        prefix = arg[0]
 | 
						|
        unknown_options = []
 | 
						|
 | 
						|
        for ch in arg[1:]:
 | 
						|
            opt = normalize_opt(prefix + ch, self.ctx)
 | 
						|
            option = self._short_opt.get(opt)
 | 
						|
            i += 1
 | 
						|
 | 
						|
            if not option:
 | 
						|
                if self.ignore_unknown_options:
 | 
						|
                    unknown_options.append(ch)
 | 
						|
                    continue
 | 
						|
                raise NoSuchOption(opt)
 | 
						|
            if option.takes_value:
 | 
						|
                # Any characters left in arg?  Pretend they're the
 | 
						|
                # next arg, and stop consuming characters of arg.
 | 
						|
                if i < len(arg):
 | 
						|
                    state.rargs.insert(0, arg[i:])
 | 
						|
                    stop = True
 | 
						|
 | 
						|
                nargs = option.nargs
 | 
						|
                if len(state.rargs) < nargs:
 | 
						|
                    _error_opt_args(nargs, opt)
 | 
						|
                elif nargs == 1:
 | 
						|
                    value = state.rargs.pop(0)
 | 
						|
                else:
 | 
						|
                    value = tuple(state.rargs[:nargs])
 | 
						|
                    del state.rargs[:nargs]
 | 
						|
 | 
						|
            else:
 | 
						|
                value = None
 | 
						|
 | 
						|
            option.process(value, state)
 | 
						|
 | 
						|
            if stop:
 | 
						|
                break
 | 
						|
 | 
						|
        # If we got any unknown options we re-combinate the string of the
 | 
						|
        # remaining options and re-attach the prefix, then report that
 | 
						|
        # to the state as new larg.  This way there is basic combinatorics
 | 
						|
        # that can be achieved while still ignoring unknown arguments.
 | 
						|
        if self.ignore_unknown_options and unknown_options:
 | 
						|
            state.largs.append(prefix + ''.join(unknown_options))
 | 
						|
 | 
						|
    def _process_opts(self, arg, state):
 | 
						|
        explicit_value = None
 | 
						|
        # Long option handling happens in two parts.  The first part is
 | 
						|
        # supporting explicitly attached values.  In any case, we will try
 | 
						|
        # to long match the option first.
 | 
						|
        if '=' in arg:
 | 
						|
            long_opt, explicit_value = arg.split('=', 1)
 | 
						|
        else:
 | 
						|
            long_opt = arg
 | 
						|
        norm_long_opt = normalize_opt(long_opt, self.ctx)
 | 
						|
 | 
						|
        # At this point we will match the (assumed) long option through
 | 
						|
        # the long option matching code.  Note that this allows options
 | 
						|
        # like "-foo" to be matched as long options.
 | 
						|
        try:
 | 
						|
            self._match_long_opt(norm_long_opt, explicit_value, state)
 | 
						|
        except NoSuchOption:
 | 
						|
            # At this point the long option matching failed, and we need
 | 
						|
            # to try with short options.  However there is a special rule
 | 
						|
            # which says, that if we have a two character options prefix
 | 
						|
            # (applies to "--foo" for instance), we do not dispatch to the
 | 
						|
            # short option code and will instead raise the no option
 | 
						|
            # error.
 | 
						|
            if arg[:2] not in self._opt_prefixes:
 | 
						|
                return self._match_short_opt(arg, state)
 | 
						|
            if not self.ignore_unknown_options:
 | 
						|
                raise
 | 
						|
            state.largs.append(arg)
 | 
						|
 |