fancyopts: prevent mutation of the default value in customopts
Differential Revision: https://phab.mercurial-scm.org/D2937
--- a/mercurial/fancyopts.py Thu Mar 22 17:08:25 2018 -0700
+++ b/mercurial/fancyopts.py Fri Mar 23 11:20:49 2018 -0700
@@ -208,11 +208,18 @@
__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.
@@ -221,7 +228,7 @@
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():
--- a/mercurial/help.py Thu Mar 22 17:08:25 2018 -0700
+++ b/mercurial/help.py Fri Mar 23 11:20:49 2018 -0700
@@ -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