diff mercurial/fancyopts.py @ 37095:ef6215df2402

fancyopts: prevent mutation of the default value in customopts Differential Revision: https://phab.mercurial-scm.org/D2937
author Daniel Ploch <dploch@google.com>
date Fri, 23 Mar 2018 11:20:49 -0700
parents 24b481668293
children 39e5e346eba7
line wrap: on
line diff
--- 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():