--- /usr/lib/python3.2/argparse.py 2011-02-20 19:43:31.000000000 +0100 +++ argparse.py 2011-02-28 17:39:56.000000000 +0100 @@ -586,6 +586,12 @@ result = '...' elif action.nargs == PARSER: result = '%s ...' % get_metavar(1) + elif type(action.nargs) is tuple: + lo, hi = action.nargs + if hi is None: + result = '%d...' % lo + else: + result = '%d..%d' % action.nargs else: formats = ['%s' for _ in range(action.nargs)] result = ' '.join(formats) % get_metavar(action.nargs) @@ -1424,8 +1430,65 @@ raise ValueError(msg % option_string) dest = dest.replace('-', '_') + # preprocess nargs + nargs = kwargs.pop('nargs', None) + if nargs is not None: + if type(nargs) in [tuple, list]: + # allowed inputs: + # - (lo, hi, ...) + # - (None, hi, ...) + # - (lo, None, ...) + # - (None, None, ...) + # + # ouput: + # - (lo, hi) + # - (lo, None) + # - ?, *, + or int + try: + lo, hi, *tail = nargs + + if lo is not None: + lo = int(lo) + assert lo >= 0 + else: + lo = 0 + + if hi is not None: + hi = int(hi) + assert hi >= 0 + + except (ValueError, TypeError, AssertionError): + msg = _('if nargs= is sequence, then [number, number] or [number, None] is required') + raise ValueError(msg) + + if hi is None: + # cases (lo, None) and (0, None) + if lo == 0: + nargs = ZERO_OR_MORE + elif lo == 1: + nargs = ONE_OR_MORE + else: + nargs = (lo, None) + else: + # cases (lo, hi) and (0, hi) + if lo == hi: + nargs = lo + elif lo < hi: + if lo == 0 and hi == 1: + nargs = OPTIONAL + else: + nargs = (lo, hi) + else: + msg = _("nargs= lower bound %d must not be greater then upper bound %d" % (lo, hi)) + raise ValueError(msg) + else: + pass # do not touch other values + # return the updated keyword arguments - return dict(kwargs, dest=dest, option_strings=option_strings) + if nargs is not None: + return dict(kwargs, dest=dest, nargs=nargs, option_strings=option_strings) + else: + return dict(kwargs, dest=dest, option_strings=option_strings) def _pop_action_class(self, kwargs, default=None): action = kwargs.pop('action', default) @@ -2015,10 +2078,18 @@ OPTIONAL: _('expected at most one argument'), ONE_OR_MORE: _('expected at least one argument'), } - default = ngettext('expected %s argument', - 'expected %s arguments', - action.nargs) % action.nargs - msg = nargs_errors.get(action.nargs, default) + + if type(action.nargs) is tuple: + lo, hi = action.nargs + if hi is None: + msg = _('expected at least %d argument(s)' % lo) + else: + msg = _('expected from %d to %d argument(s)' % action.nargs) + else: + default = ngettext('expected %s argument', + 'expected %s arguments', + action.nargs) % action.nargs + msg = nargs_errors.get(action.nargs, default) raise ArgumentError(action, msg) # return the number of arguments matched @@ -2171,6 +2242,14 @@ elif nargs == PARSER: nargs_pattern = '(-*A[-AO]*)' + # allow lo .. hi options + elif type(nargs) is tuple: + lo, hi = nargs + if hi is None: + nargs_pattern = '(-*A{%d,})' % (lo) + else: + nargs_pattern = '(-*A{%d,%d})' % nargs + # all others should be integers else: nargs_pattern = '(-*%s-*)' % '-*'.join('A' * nargs)