D2937: fancyopts: prevent mutation of the default value in customopts
dploch (Daniel Ploch)
phabricator at mercurial-scm.org
Fri Mar 23 18:24:04 UTC 2018
dploch created this revision.
Herald added a subscriber: mercurial-devel.
Herald added a reviewer: hg-reviewers.
REPOSITORY
rHG Mercurial
REVISION DETAIL
https://phab.mercurial-scm.org/D2937
AFFECTED FILES
mercurial/fancyopts.py
mercurial/help.py
tests/test-help.t
CHANGE DETAILS
diff --git a/tests/test-help.t b/tests/test-help.t
--- a/tests/test-help.t
+++ b/tests/test-help.t
@@ -716,15 +716,23 @@
$ cat > helpext.py <<EOF
> import os
- > from mercurial import commands, registrar
+ > from mercurial import commands, fancyopts, registrar
>
+ > def func(arg):
+ > return '%sfoo' % arg
+ > class customopt(fancyopts.customopt):
+ > def newstate(self, oldstate, newparam, abort):
+ > return '%sbar' % oldstate
> cmdtable = {}
> command = registrar.command(cmdtable)
>
> @command(b'nohelp',
- > [(b'', b'longdesc', 3, b'x'*90),
+ > [(b'', b'longdesc', 3, b'x'*67),
> (b'n', b'', None, b'normal desc'),
- > (b'', b'newline', b'', b'line1\nline2')],
+ > (b'', b'newline', b'', b'line1\nline2'),
+ > (b'', b'callableopt', func, b'adds foo'),
+ > (b'', b'customopt', customopt(''), b'adds bar'),
+ > (b'', b'customopt-withdefault', customopt('foo'), b'adds bar')],
> b'hg nohelp',
> norepo=True)
> @command(b'debugoptADV', [(b'', b'aopt', None, b'option is (ADVANCED)')])
@@ -786,10 +794,14 @@
options:
- --longdesc VALUE xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx (default: 3)
- -n -- normal desc
- --newline VALUE line1 line2
+ --longdesc VALUE
+ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+ xxxxxxxxxxxxxxxxxxxxxxx (default: 3)
+ -n -- normal desc
+ --newline VALUE line1 line2
+ --callableopt VALUE adds foo
+ --customopt VALUE adds bar
+ --customopt-withdefault VALUE adds bar (default: foo)
(some details hidden, use --verbose to show complete help)
diff --git a/mercurial/help.py b/mercurial/help.py
--- a/mercurial/help.py
+++ b/mercurial/help.py
@@ -87,7 +87,7 @@
lo = '--' + longopt
if isinstance(default, fancyopts.customopt):
- default = default.defaultvalue
+ default = default.getdefaultvalue()
if default and not callable(default):
# default is of unknown type, and in Python 2 we abused
# the %s-shows-repr property to handle integers etc. To
diff --git a/mercurial/fancyopts.py b/mercurial/fancyopts.py
--- a/mercurial/fancyopts.py
+++ b/mercurial/fancyopts.py
@@ -208,20 +208,27 @@
__metaclass__ = abc.ABCMeta
def __init__(self, defaultvalue):
- self.defaultvalue = defaultvalue
+ self._defaultvalue = defaultvalue
def _isboolopt(self):
return False
+ def getdefaultvalue(self):
+ """Returns the default value for this opt.
+
+ Subclasses should override this to return a new value if the value type
+ is mutable."""
+ return self._defaultvalue
+
@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, type(None)))
+ return isinstance(self._defaultvalue, (bool, type(None)))
def newstate(self, oldstate, newparam, abort):
return newparam
@@ -235,6 +242,9 @@
return self.callablefn(newparam)
class _listopt(customopt):
+ def getdefaultvalue(self):
+ return self._defaultvalue[:]
+
def newstate(self, oldstate, newparam, abort):
oldstate.append(newparam)
return oldstate
@@ -313,7 +323,7 @@
defmap[name] = _defaultopt(default)
# copy defaults to state
- state[name] = defmap[name].defaultvalue
+ state[name] = defmap[name].getdefaultvalue()
# does it take a parameter?
if not defmap[name]._isboolopt():
To: dploch, #hg-reviewers
Cc: mercurial-devel
More information about the Mercurial-devel
mailing list