From d2b66b9c04403937f6362dde3ddca974c2755d88 Mon Sep 17 00:00:00 2001 From: tonghuaroot Date: Wed, 24 Jun 2026 18:47:02 +0800 Subject: [PATCH 1/2] gh-152060: Fix datetime.fromisoformat() raising AssertionError in pure Python _pydatetime._parse_isoformat_date() asserted the date portion length, leaking a bare AssertionError out of datetime.fromisoformat() for some malformed strings (e.g. '2020-2020'), and behaving differently under -O. The C accelerator already raises ValueError. Replace the assert with an explicit ValueError so both implementations agree on all builds. --- Lib/_pydatetime.py | 3 ++- Lib/test/datetimetester.py | 2 ++ .../Library/2026-06-24-10-46-35.gh-issue-152060.f2asrt.rst | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2026-06-24-10-46-35.gh-issue-152060.f2asrt.rst diff --git a/Lib/_pydatetime.py b/Lib/_pydatetime.py index c1448374402de4a..e58a140b60d8440 100644 --- a/Lib/_pydatetime.py +++ b/Lib/_pydatetime.py @@ -358,7 +358,8 @@ def _find_isoformat_datetime_separator(dtstr): def _parse_isoformat_date(dtstr): # It is assumed that this is an ASCII-only string of lengths 7, 8 or 10, # see the comment on Modules/_datetimemodule.c:_find_isoformat_datetime_separator - assert len(dtstr) in (7, 8, 10) + if len(dtstr) not in (7, 8, 10): + raise ValueError(f"Invalid isoformat string: {dtstr!r}") year = int(dtstr[0:4]) has_sep = dtstr[4] == '-' diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 1cbe78c1ecbfdc6..f6a88849ed69207 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -3757,6 +3757,8 @@ def test_fromisoformat_fails_datetime(self): '2009-04-19T12:30:45+00:00:90', # Time zone field out from range '2009-04-19T12:30:45-00:90:00', # Time zone field out from range '2009-04-19T12:30:45-00:00:90', # Time zone field out from range + '2020-2020', # Ambiguous 9-char date portion + '2020-1234', # Ambiguous 9-char date portion ] for bad_str in bad_strs: diff --git a/Misc/NEWS.d/next/Library/2026-06-24-10-46-35.gh-issue-152060.f2asrt.rst b/Misc/NEWS.d/next/Library/2026-06-24-10-46-35.gh-issue-152060.f2asrt.rst new file mode 100644 index 000000000000000..6088a6b80829bb4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-06-24-10-46-35.gh-issue-152060.f2asrt.rst @@ -0,0 +1,3 @@ +Fix :meth:`datetime.datetime.fromisoformat` raising :exc:`AssertionError` +instead of :exc:`ValueError` for some malformed strings in the pure-Python +implementation, matching the C implementation. From e203639e9045b1d7ff2e58e3744995e3a5372675 Mon Sep 17 00:00:00 2001 From: tonghuaroot Date: Wed, 24 Jun 2026 20:37:06 +0800 Subject: [PATCH 2/2] =?UTF-8?q?gh-152060:=20Address=20review=20=E2=80=94?= =?UTF-8?q?=20simplify=20error=20message,=20drop=20duplicate=20test=20case?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Lib/_pydatetime.py | 2 +- Lib/test/datetimetester.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Lib/_pydatetime.py b/Lib/_pydatetime.py index e58a140b60d8440..db4ea8d30c7064f 100644 --- a/Lib/_pydatetime.py +++ b/Lib/_pydatetime.py @@ -359,7 +359,7 @@ def _parse_isoformat_date(dtstr): # It is assumed that this is an ASCII-only string of lengths 7, 8 or 10, # see the comment on Modules/_datetimemodule.c:_find_isoformat_datetime_separator if len(dtstr) not in (7, 8, 10): - raise ValueError(f"Invalid isoformat string: {dtstr!r}") + raise ValueError("Invalid isoformat string") year = int(dtstr[0:4]) has_sep = dtstr[4] == '-' diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index f6a88849ed69207..e29f5e3ecb5fd4f 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -3758,7 +3758,6 @@ def test_fromisoformat_fails_datetime(self): '2009-04-19T12:30:45-00:90:00', # Time zone field out from range '2009-04-19T12:30:45-00:00:90', # Time zone field out from range '2020-2020', # Ambiguous 9-char date portion - '2020-1234', # Ambiguous 9-char date portion ] for bad_str in bad_strs: