summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/lib/libcfgparser.tex10
-rw-r--r--Lib/ConfigParser.py48
-rw-r--r--Lib/test/test_cfgparser.py17
3 files changed, 74 insertions, 1 deletions
diff --git a/Doc/lib/libcfgparser.tex b/Doc/lib/libcfgparser.tex
index 946590d..0ad9d61 100644
--- a/Doc/lib/libcfgparser.tex
+++ b/Doc/lib/libcfgparser.tex
@@ -59,6 +59,16 @@ appropriate for the \samp{\%()s} string interpolation. Note that
and will override any value provided in \var{defaults}.
\end{classdesc}
+\begin{classdesc}{SafeConfigParser}{\optional{defaults}}
+Derived class of \class{ConfigParser} that implements a more-sane
+variant of the magical interpolation feature. This implementation is
+more predictable as well.
+% XXX Need to explain what's safer/more predictable about it.
+New applications should prefer this version if they don't need to be
+compatible with older versions of Python.
+\versionadded{2.3}
+\end{classdesc}
+
\begin{excdesc}{NoSectionError}
Exception raised when a specified section is not found.
\end{excdesc}
diff --git a/Lib/ConfigParser.py b/Lib/ConfigParser.py
index c623ec9..6077022 100644
--- a/Lib/ConfigParser.py
+++ b/Lib/ConfigParser.py
@@ -538,3 +538,51 @@ class ConfigParser(RawConfigParser):
if value.find("%(") != -1:
raise InterpolationDepthError(option, section, rawval)
return value
+
+
+class SafeConfigParser(ConfigParser):
+
+ def _interpolate(self, section, option, rawval, vars):
+ # do the string interpolation
+ L = []
+ self._interpolate_some(option, L, rawval, section, vars, 1)
+ return ''.join(L)
+
+ _interpvar_match = re.compile(r"%\(([^)]+)\)s").match
+
+ def _interpolate_some(self, option, accum, rest, section, map, depth):
+ if depth > MAX_INTERPOLATION_DEPTH:
+ raise InterpolationDepthError(option, section, rest)
+ while rest:
+ p = rest.find("%")
+ if p < 0:
+ accum.append(rest)
+ return
+ if p > 0:
+ accum.append(rest[:p])
+ rest = rest[p:]
+ # p is no longer used
+ c = rest[1:2]
+ if c == "%":
+ accum.append("%")
+ rest = rest[2:]
+ elif c == "(":
+ m = self._interpvar_match(rest)
+ if m is None:
+ raise InterpolationSyntaxError(
+ "bad interpolation variable syntax at: %r" % rest)
+ var = m.group(1)
+ rest = rest[m.end():]
+ try:
+ v = map[var]
+ except KeyError:
+ raise InterpolationError(
+ "no value found for %r" % var)
+ if "%" in v:
+ self._interpolate_some(option, accum, v,
+ section, map, depth + 1)
+ else:
+ accum.append(v)
+ else:
+ raise InterpolationSyntaxError(
+ "'%' must be followed by '%' or '('")
diff --git a/Lib/test/test_cfgparser.py b/Lib/test/test_cfgparser.py
index 6d93c61..b221598 100644
--- a/Lib/test/test_cfgparser.py
+++ b/Lib/test/test_cfgparser.py
@@ -289,10 +289,25 @@ class RawConfigParserTestCase(TestCaseBase):
('name', 'value')])
+class SafeConfigParserTestCase(ConfigParserTestCase):
+ config_class = ConfigParser.SafeConfigParser
+
+ def test_safe_interpolation(self):
+ # See http://www.python.org/sf/511737
+ cf = self.fromstring("[section]\n"
+ "option1=xxx\n"
+ "option2=%(option1)s/xxx\n"
+ "ok=%(option1)s/%%s\n"
+ "not_ok=%(option2)s/%%s")
+ self.assertEqual(cf.get("section", "ok"), "xxx/%s")
+ self.assertEqual(cf.get("section", "not_ok"), "xxx/xxx/%s")
+
+
def test_main():
suite = unittest.TestSuite()
suite.addTests([unittest.makeSuite(ConfigParserTestCase),
- unittest.makeSuite(RawConfigParserTestCase)])
+ unittest.makeSuite(RawConfigParserTestCase),
+ unittest.makeSuite(SafeConfigParserTestCase)])
test_support.run_suite(suite)
if __name__ == "__main__":