From 7f85aeab6419cff32d2eed5ae488354ec747d3c2 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Fri, 7 Mar 2025 17:48:11 +0300 Subject: [PATCH 1/2] gh-130941: Fix `configparser` parsing values with `allow_no_value` and `interpolation` --- Lib/configparser.py | 2 ++ Lib/test/test_configparser.py | 16 ++++++++++++++++ ...025-03-07-17-47-32.gh-issue-130941.7_GvhW.rst | 2 ++ 3 files changed, 20 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2025-03-07-17-47-32.gh-issue-130941.7_GvhW.rst diff --git a/Lib/configparser.py b/Lib/configparser.py index 70cc651edabd86..6bc920661a2203 100644 --- a/Lib/configparser.py +++ b/Lib/configparser.py @@ -541,6 +541,8 @@ def _interpolate_some(self, parser, option, accum, rest, section, map, except (KeyError, NoSectionError, NoOptionError): raise InterpolationMissingOptionError( option, section, rawval, ":".join(path)) from None + if not v: + continue if "$" in v: self._interpolate_some(parser, opt, accum, v, sect, dict(parser.items(sect, raw=True)), diff --git a/Lib/test/test_configparser.py b/Lib/test/test_configparser.py index 1313ec2b9e884e..468da9bd749434 100644 --- a/Lib/test/test_configparser.py +++ b/Lib/test/test_configparser.py @@ -1328,6 +1328,22 @@ class ConfigParserTestCaseNoValue(ConfigParserTestCase): allow_no_value = True +class ConfigParserTestCaseNoValueAndInterpolation(ConfigParserTestCase): + allow_no_value = True + interpolation = configparser.ExtendedInterpolation() + + def test_interpolation_with_allow_no_value(self): + config = textwrap.dedent(""" + [dummy] + a + b = ${a} + """) + cf = self.fromstring(config) + + self.assertIs(cf["dummy"]["a"], None) + self.assertEqual(cf["dummy"]["b"], "") + + class ConfigParserTestCaseTrickyFile(CfgParserTestCaseClass, unittest.TestCase): config_class = configparser.ConfigParser delimiters = {'='} diff --git a/Misc/NEWS.d/next/Library/2025-03-07-17-47-32.gh-issue-130941.7_GvhW.rst b/Misc/NEWS.d/next/Library/2025-03-07-17-47-32.gh-issue-130941.7_GvhW.rst new file mode 100644 index 00000000000000..4f0cda8d03e902 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-03-07-17-47-32.gh-issue-130941.7_GvhW.rst @@ -0,0 +1,2 @@ +Fix :class:`configparser.ConfigParser` parsing empty interpolation with +``allow_no_value`` set to ``True``. From 1621a0f0e8bbe4b38229ee502b4e863266a5adf2 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Fri, 7 Mar 2025 18:08:40 +0300 Subject: [PATCH 2/2] Fix CI --- Lib/configparser.py | 2 +- Lib/test/test_configparser.py | 29 +++++++++++++++++++++++++++-- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/Lib/configparser.py b/Lib/configparser.py index 6bc920661a2203..239fda60a02ca0 100644 --- a/Lib/configparser.py +++ b/Lib/configparser.py @@ -541,7 +541,7 @@ def _interpolate_some(self, parser, option, accum, rest, section, map, except (KeyError, NoSectionError, NoOptionError): raise InterpolationMissingOptionError( option, section, rawval, ":".join(path)) from None - if not v: + if v is None: continue if "$" in v: self._interpolate_some(parser, opt, accum, v, sect, diff --git a/Lib/test/test_configparser.py b/Lib/test/test_configparser.py index 468da9bd749434..23904d17d326d8 100644 --- a/Lib/test/test_configparser.py +++ b/Lib/test/test_configparser.py @@ -1328,9 +1328,9 @@ class ConfigParserTestCaseNoValue(ConfigParserTestCase): allow_no_value = True -class ConfigParserTestCaseNoValueAndInterpolation(ConfigParserTestCase): - allow_no_value = True +class NoValueAndExtendedInterpolation(CfgParserTestCaseClass): interpolation = configparser.ExtendedInterpolation() + allow_no_value = True def test_interpolation_with_allow_no_value(self): config = textwrap.dedent(""" @@ -1343,6 +1343,31 @@ def test_interpolation_with_allow_no_value(self): self.assertIs(cf["dummy"]["a"], None) self.assertEqual(cf["dummy"]["b"], "") + def test_explicit_none(self): + config = textwrap.dedent(""" + [dummy] + a = None + b = ${a} + """) + cf = self.fromstring(config) + + self.assertEqual(cf["dummy"]["a"], "None") + self.assertEqual(cf["dummy"]["b"], "None") + + +class ConfigParserNoValueAndExtendedInterpolationTest( + NoValueAndExtendedInterpolation, + unittest.TestCase, +): + config_class = configparser.ConfigParser + + +class RawConfigParserNoValueAndExtendedInterpolationTest( + NoValueAndExtendedInterpolation, + unittest.TestCase, +): + config_class = configparser.RawConfigParser + class ConfigParserTestCaseTrickyFile(CfgParserTestCaseClass, unittest.TestCase): config_class = configparser.ConfigParser