Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 0eebd5c

Browse files
committed
Implement a safer and more predictable interpolation approach.
Closes SF bug #511737.
1 parent 98e3b29 commit 0eebd5c

3 files changed

Lines changed: 74 additions & 1 deletion

File tree

Doc/lib/libcfgparser.tex

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,16 @@ \section{\module{ConfigParser} ---
5959
and will override any value provided in \var{defaults}.
6060
\end{classdesc}
6161

62+
\begin{classdesc}{SafeConfigParser}{\optional{defaults}}
63+
Derived class of \class{ConfigParser} that implements a more-sane
64+
variant of the magical interpolation feature. This implementation is
65+
more predictable as well.
66+
% XXX Need to explain what's safer/more predictable about it.
67+
New applications should prefer this version if they don't need to be
68+
compatible with older versions of Python.
69+
\versionadded{2.3}
70+
\end{classdesc}
71+
6272
\begin{excdesc}{NoSectionError}
6373
Exception raised when a specified section is not found.
6474
\end{excdesc}

Lib/ConfigParser.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,3 +538,51 @@ def _interpolate(self, section, option, rawval, vars):
538538
if value.find("%(") != -1:
539539
raise InterpolationDepthError(option, section, rawval)
540540
return value
541+
542+
543+
class SafeConfigParser(ConfigParser):
544+
545+
def _interpolate(self, section, option, rawval, vars):
546+
# do the string interpolation
547+
L = []
548+
self._interpolate_some(option, L, rawval, section, vars, 1)
549+
return ''.join(L)
550+
551+
_interpvar_match = re.compile(r"%\(([^)]+)\)s").match
552+
553+
def _interpolate_some(self, option, accum, rest, section, map, depth):
554+
if depth > MAX_INTERPOLATION_DEPTH:
555+
raise InterpolationDepthError(option, section, rest)
556+
while rest:
557+
p = rest.find("%")
558+
if p < 0:
559+
accum.append(rest)
560+
return
561+
if p > 0:
562+
accum.append(rest[:p])
563+
rest = rest[p:]
564+
# p is no longer used
565+
c = rest[1:2]
566+
if c == "%":
567+
accum.append("%")
568+
rest = rest[2:]
569+
elif c == "(":
570+
m = self._interpvar_match(rest)
571+
if m is None:
572+
raise InterpolationSyntaxError(
573+
"bad interpolation variable syntax at: %r" % rest)
574+
var = m.group(1)
575+
rest = rest[m.end():]
576+
try:
577+
v = map[var]
578+
except KeyError:
579+
raise InterpolationError(
580+
"no value found for %r" % var)
581+
if "%" in v:
582+
self._interpolate_some(option, accum, v,
583+
section, map, depth + 1)
584+
else:
585+
accum.append(v)
586+
else:
587+
raise InterpolationSyntaxError(
588+
"'%' must be followed by '%' or '('")

Lib/test/test_cfgparser.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,10 +289,25 @@ def test_items(self):
289289
('name', 'value')])
290290

291291

292+
class SafeConfigParserTestCase(ConfigParserTestCase):
293+
config_class = ConfigParser.SafeConfigParser
294+
295+
def test_safe_interpolation(self):
296+
# See http://www.python.org/sf/511737
297+
cf = self.fromstring("[section]\n"
298+
"option1=xxx\n"
299+
"option2=%(option1)s/xxx\n"
300+
"ok=%(option1)s/%%s\n"
301+
"not_ok=%(option2)s/%%s")
302+
self.assertEqual(cf.get("section", "ok"), "xxx/%s")
303+
self.assertEqual(cf.get("section", "not_ok"), "xxx/xxx/%s")
304+
305+
292306
def test_main():
293307
suite = unittest.TestSuite()
294308
suite.addTests([unittest.makeSuite(ConfigParserTestCase),
295-
unittest.makeSuite(RawConfigParserTestCase)])
309+
unittest.makeSuite(RawConfigParserTestCase),
310+
unittest.makeSuite(SafeConfigParserTestCase)])
296311
test_support.run_suite(suite)
297312

298313
if __name__ == "__main__":

0 commit comments

Comments
 (0)