mirror of
https://github.com/python/cpython.git
synced 2025-08-31 05:58:33 +00:00
Implement a safer and more predictable interpolation approach.
Closes SF bug #511737.
This commit is contained in:
parent
98e3b29b59
commit
0eebd5cef9
3 changed files with 74 additions and 1 deletions
|
@ -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}
|
||||
|
|
|
@ -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 '('")
|
||||
|
|
|
@ -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__":
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue