diff --git a/mercurial/fancyopts.py b/mercurial/fancyopts.py --- a/mercurial/fancyopts.py +++ b/mercurial/fancyopts.py @@ -7,7 +7,9 @@ from __future__ import absolute_import +import abc import functools +import types from .i18n import _ from . import ( @@ -201,6 +203,65 @@ parsedargs.extend(args[pos:]) return parsedopts, parsedargs +class customopt(object): + """Manage defaults and mutations for any type of opt.""" + + __metaclass__ = abc.ABCMeta + + def __init__(self, defaultvalue): + self.defaultvalue = defaultvalue + + def _isboolopt(self): + return False + + @abc.abstractmethod + def newstate(self, oldstate, newparam, abort): + """Adds newparam to oldstate and returns the new state. + + On failure, abort can be called with a string error message.""" + +class _simpleopt(customopt): + def _isboolopt(self): + return isinstance(self.defaultvalue, (bool, types.NoneType)) + + def newstate(self, oldstate, newparam, abort): + return newparam + +class _callableopt(customopt): + def __init__(self, callablefn): + self.callablefn = callablefn + super(_callableopt, self).__init__(None) + + def newstate(self, oldstate, newparam, abort): + return self.callablefn(newparam) + +class _listopt(customopt): + def newstate(self, oldstate, newparam, abort): + oldstate.append(newparam) + return oldstate + +class _intopt(customopt): + def newstate(self, oldstate, newparam, abort): + try: + return int(newparam) + except ValueError: + abort(_('expected int')) + +def _defaultopt(default): + """Returns a default opt implementation, given a default value.""" + + if isinstance(default, customopt): + return default + elif callable(default): + return _callableopt(default) + elif isinstance(default, list): + return _listopt(default[:]) + # isinstance(True, int) returns True for some reason. + elif isinstance(default, (int, long)) and not isinstance(default, bool): + return _intopt(default) + else: + return _simpleopt(default) + def fancyopts(args, options, state, gnu=False, early=False, optaliases=None): """ read args, parse options, and store options in state @@ -220,6 +281,7 @@ list - parameter string is added to a list integer - parameter strings is stored as int function - call function with parameter + customopt - subclass of 'customopt' optaliases is a mapping from a canonical option name to a list of additional long options. This exists for preserving backward compatibility @@ -250,18 +312,13 @@ argmap['-' + short] = name for n in onames: argmap['--' + n] = name - defmap[name] = default + defmap[name] = _defaultopt(default) # copy defaults to state - if isinstance(default, list): - state[name] = default[:] - elif callable(default): - state[name] = None - else: - state[name] = default + state[name] = defmap[name].defaultvalue # does it take a parameter? - if not (default is None or default is True or default is False): + if not defmap[name]._isboolopt(): if short: short += ':' onames = [n + '=' for n in onames] @@ -301,21 +358,13 @@ boolval = False name = argmap[opt] obj = defmap[name] - t = type(obj) - if callable(obj): - state[name] = defmap[name](val) - elif t is type(1): - try: - state[name] = int(val) - except ValueError: - raise error.Abort(_('invalid value %r for option %s, ' - 'expected int') % (val, opt)) - elif t is type(''): - state[name] = val - elif t is type([]): - state[name].append(val) - elif t is type(None) or t is type(False): + if obj._isboolopt(): state[name] = boolval + else: + def abort(s): + raise error.Abort( + _('invalid value %r for option %s, %s') % (val, opt, s)) + state[name] = defmap[name].newstate(state[name], val, abort) # return unparsed args return args