From ae3cbfbfa3ecc944fc4c07dfc834e80c53bdabaf Mon Sep 17 00:00:00 2001 From: lipengyu Date: Sun, 9 Aug 2026 22:51:39 +0800 Subject: [PATCH 1/3] gh-155436: Fix configparser.getboolean() for value-less options --- Lib/configparser.py | 2 ++ Lib/test/test_configparser.py | 8 ++++++++ .../2026-08-09-22-50-19.gh-issue-155436.gyB8_I.rst | 3 +++ 3 files changed, 13 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2026-08-09-22-50-19.gh-issue-155436.gyB8_I.rst diff --git a/Lib/configparser.py b/Lib/configparser.py index 3c452afe8ade485..72cc290150aa083 100644 --- a/Lib/configparser.py +++ b/Lib/configparser.py @@ -1222,6 +1222,8 @@ def _unify_values(self, section, vars): def _convert_to_boolean(self, value): """Return a boolean value translating from other types if necessary. """ + if value is None: + raise ValueError('Not a boolean: None') if value.lower() not in self.BOOLEAN_STATES: raise ValueError('Not a boolean: %s' % value) return self.BOOLEAN_STATES[value.lower()] diff --git a/Lib/test/test_configparser.py b/Lib/test/test_configparser.py index 4783943f71a1092..e4034c0ed3b93d8 100644 --- a/Lib/test/test_configparser.py +++ b/Lib/test/test_configparser.py @@ -1338,6 +1338,14 @@ def test_other_errors(self): class ConfigParserTestCaseNoValue(ConfigParserTestCase): allow_no_value = True + def test_getboolean_with_no_value(self): + cf = self.fromstring("[section]\noption\n") + + with self.assertRaisesRegex(ValueError, "Not a boolean: None"): + cf.getboolean("section", "option") + with self.assertRaisesRegex(ValueError, "Not a boolean: None"): + cf["section"].getboolean("option") + class NoValueAndExtendedInterpolation(CfgParserTestCaseClass): interpolation = configparser.ExtendedInterpolation() diff --git a/Misc/NEWS.d/next/Library/2026-08-09-22-50-19.gh-issue-155436.gyB8_I.rst b/Misc/NEWS.d/next/Library/2026-08-09-22-50-19.gh-issue-155436.gyB8_I.rst new file mode 100644 index 000000000000000..9fffec26eb8950a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-08-09-22-50-19.gh-issue-155436.gyB8_I.rst @@ -0,0 +1,3 @@ +:class:`configparser.ConfigParser` now raises :exc:`ValueError` instead of +:exc:`AttributeError` when :meth:`~configparser.ConfigParser.getboolean` is +called on an option without a value while ``allow_no_value=True``. From 94666c67614b34d1f2fdee378fdd1a9d59194ca7 Mon Sep 17 00:00:00 2001 From: stevens Date: Mon, 10 Aug 2026 09:13:51 +0800 Subject: [PATCH 2/3] Update Lib/configparser.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Lib/configparser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/configparser.py b/Lib/configparser.py index 72cc290150aa083..4bcbb0c696dbf08 100644 --- a/Lib/configparser.py +++ b/Lib/configparser.py @@ -1224,7 +1224,7 @@ def _convert_to_boolean(self, value): """ if value is None: raise ValueError('Not a boolean: None') - if value.lower() not in self.BOOLEAN_STATES: + if value is None or value.lower() not in self.BOOLEAN_STATES: raise ValueError('Not a boolean: %s' % value) return self.BOOLEAN_STATES[value.lower()] From 5062d6c632159fc5a5cd0ed78bd51f23f08c627f Mon Sep 17 00:00:00 2001 From: stevens Date: Mon, 10 Aug 2026 09:14:01 +0800 Subject: [PATCH 3/3] Update Lib/configparser.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com> --- Lib/configparser.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/Lib/configparser.py b/Lib/configparser.py index 4bcbb0c696dbf08..2d6b1f121d5aeea 100644 --- a/Lib/configparser.py +++ b/Lib/configparser.py @@ -1222,8 +1222,6 @@ def _unify_values(self, section, vars): def _convert_to_boolean(self, value): """Return a boolean value translating from other types if necessary. """ - if value is None: - raise ValueError('Not a boolean: None') if value is None or value.lower() not in self.BOOLEAN_STATES: raise ValueError('Not a boolean: %s' % value) return self.BOOLEAN_STATES[value.lower()]