From 09c3a43a71a0f0fda0e3cb2ae05e94aa5c3a8f67 Mon Sep 17 00:00:00 2001 From: Vasiliy Kiryanov Date: Sat, 19 Sep 2026 20:36:59 -0400 Subject: [PATCH 1/5] gh-141540: Improve strptime error message for unconverted data --- Lib/_strptime.py | 12 ++++++------ Lib/test/test_strptime.py | 8 +++++++- .../2026-09-19-20-31-58.gh-issue-141540.phc605.rst | 3 +++ 3 files changed, 16 insertions(+), 7 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2026-09-19-20-31-58.gh-issue-141540.phc605.rst diff --git a/Lib/_strptime.py b/Lib/_strptime.py index 59ac96745aa15e2..f0906583e8ff651 100644 --- a/Lib/_strptime.py +++ b/Lib/_strptime.py @@ -564,15 +564,15 @@ def _strptime(data_string, format="%a %b %d %H:%M:%S %Y"): del err bad_directive = bad_directive.replace('\\s', '') if not bad_directive: - raise ValueError("stray %% in format '%s'" % format) from None + raise ValueError(f"stray % in format {format!r}") from None bad_directive = bad_directive.replace('\\', '', 1) - raise ValueError("'%s' is a bad directive in format '%s'" % - (bad_directive, format)) from None + raise ValueError(f"{bad_directive!r} is a bad directive " + f"in format {format!r}") from None _regex_cache[format] = format_regex found = format_regex.match(data_string) if not found: - raise ValueError("time data %r does not match format %r" % - (data_string, format)) + raise ValueError(f"time data {data_string!r} does not match " + f"format {format!r}") if len(data_string) != found.end(): rest = data_string[found.end():] # Specific check for '%:z' directive @@ -584,7 +584,7 @@ def _strptime(data_string, format="%a %b %d %H:%M:%S %Y"): raise ValueError( f"Missing colon in %:z before '{rest}', got '{data_string}'" ) - raise ValueError("unconverted data remains: %s" % rest) + raise ValueError(f"unconverted data remains: {rest!r}") iso_year = year = None month = day = 1 diff --git a/Lib/test/test_strptime.py b/Lib/test/test_strptime.py index e95cc6db170e243..e8a4d6740246412 100644 --- a/Lib/test/test_strptime.py +++ b/Lib/test/test_strptime.py @@ -236,7 +236,8 @@ def test_ValueError(self): directive = bad_format[1:].rstrip() with (self.subTest(format=bad_format), self.assertRaisesRegex(ValueError, - f"'{re.escape(directive)}' is a bad directive in format ")): + f"{re.escape(repr(directive))} is a bad directive " + f"in format ")): _strptime._strptime_time("2005", bad_format) msg_week_no_year_or_weekday = r"ISO week directive '%V' must be used with " \ @@ -303,6 +304,11 @@ def test_unconverteddata(self): # Check ValueError is raised when there is unconverted data self.assertRaises(ValueError, _strptime._strptime_time, "10 12", "%m") + # gh-141540: a trailing newline must be visible in the message + with self.assertRaisesRegex(ValueError, + r"unconverted data remains: '\\n'"): + _strptime._strptime_time("2001-02-03\n", "%Y-%m-%d") + def roundtrip(self, fmt, position, time_tuple=None): """Helper fxn in testing.""" if time_tuple is None: diff --git a/Misc/NEWS.d/next/Library/2026-09-19-20-31-58.gh-issue-141540.phc605.rst b/Misc/NEWS.d/next/Library/2026-09-19-20-31-58.gh-issue-141540.phc605.rst new file mode 100644 index 000000000000000..75421623f01b992 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-09-19-20-31-58.gh-issue-141540.phc605.rst @@ -0,0 +1,3 @@ +Improve the error messages of :func:`time.strptime` and +:meth:`datetime.datetime.strptime`: unconverted data is now quoted, making +whitespace such as a trailing newline visible. From 85df9c896923fcd0bb30a11083f811f610da52e8 Mon Sep 17 00:00:00 2001 From: Vasiliy Kiryanov Date: Sun, 20 Sep 2026 15:09:14 -0400 Subject: [PATCH 2/5] gh-141540: Quote the %:z error message with !r as well --- Lib/_strptime.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/_strptime.py b/Lib/_strptime.py index f0906583e8ff651..4bfd8eac06d1467 100644 --- a/Lib/_strptime.py +++ b/Lib/_strptime.py @@ -582,7 +582,7 @@ def _strptime(data_string, format="%a %b %d %H:%M:%S %Y"): and rest[0] != ":" ): raise ValueError( - f"Missing colon in %:z before '{rest}', got '{data_string}'" + f"Missing colon in %:z before {rest!r}, got {data_string!r}" ) raise ValueError(f"unconverted data remains: {rest!r}") From 3a8eeafdb753037ac7caca162886ae79208f2158 Mon Sep 17 00:00:00 2001 From: Vasiliy Kiryanov Date: Mon, 21 Sep 2026 13:11:06 -0400 Subject: [PATCH 3/5] gh-141540: Quote the colon error message with !r as well --- Lib/_strptime.py | 2 +- Lib/test/test_strptime.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Lib/_strptime.py b/Lib/_strptime.py index 4bfd8eac06d1467..3311ec888714696 100644 --- a/Lib/_strptime.py +++ b/Lib/_strptime.py @@ -700,7 +700,7 @@ def parse_int(s): z = z[:3] + z[4:] if len(z) > 5: if z[5] != ':': - msg = f"Inconsistent use of : in {found_dict[group_key]}" + msg = f"Inconsistent use of : in {found_dict[group_key]!r}" raise ValueError(msg) z = z[:5] + z[6:] hours = int(z[1:3]) diff --git a/Lib/test/test_strptime.py b/Lib/test/test_strptime.py index e8a4d6740246412..d70837ea63f5474 100644 --- a/Lib/test/test_strptime.py +++ b/Lib/test/test_strptime.py @@ -457,7 +457,8 @@ def test_bad_offset(self): with self.assertRaises(ValueError) as err: _strptime._strptime("-01:3030", "%z") - self.assertEqual("Inconsistent use of : in -01:3030", str(err.exception)) + self.assertEqual("Inconsistent use of : in '-01:3030'", + str(err.exception)) with self.assertRaises(ValueError) as err: _strptime._strptime("-01:3030", "%:z") self.assertEqual("Missing colon in %:z before '30', got '-01:3030'", From 405e08551c4ba296f23833ca4a4326f94840fbbc Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Mon, 21 Sep 2026 18:14:31 +0100 Subject: [PATCH 4/5] Update Misc/NEWS.d/next/Library/2026-09-19-20-31-58.gh-issue-141540.phc605.rst --- .../Library/2026-09-19-20-31-58.gh-issue-141540.phc605.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2026-09-19-20-31-58.gh-issue-141540.phc605.rst b/Misc/NEWS.d/next/Library/2026-09-19-20-31-58.gh-issue-141540.phc605.rst index 75421623f01b992..4bd7d8f8fdfe1f5 100644 --- a/Misc/NEWS.d/next/Library/2026-09-19-20-31-58.gh-issue-141540.phc605.rst +++ b/Misc/NEWS.d/next/Library/2026-09-19-20-31-58.gh-issue-141540.phc605.rst @@ -1,3 +1,5 @@ Improve the error messages of :func:`time.strptime` and :meth:`datetime.datetime.strptime`: unconverted data is now quoted, making -whitespace such as a trailing newline visible. +Quote the unconverted data and the format in the error messages of +:func:`time.strptime` and :meth:`datetime.datetime.strptime` so that +whitespace such as a trailing newline is visible. From 8b2040e38e32fe9dcbc5997ba8ec57cf25109f88 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Mon, 21 Sep 2026 18:17:33 +0100 Subject: [PATCH 5/5] !fixup news entry --- .../next/Library/2026-09-19-20-31-58.gh-issue-141540.phc605.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/Misc/NEWS.d/next/Library/2026-09-19-20-31-58.gh-issue-141540.phc605.rst b/Misc/NEWS.d/next/Library/2026-09-19-20-31-58.gh-issue-141540.phc605.rst index 4bd7d8f8fdfe1f5..1b38ffd26ea20c3 100644 --- a/Misc/NEWS.d/next/Library/2026-09-19-20-31-58.gh-issue-141540.phc605.rst +++ b/Misc/NEWS.d/next/Library/2026-09-19-20-31-58.gh-issue-141540.phc605.rst @@ -1,5 +1,3 @@ -Improve the error messages of :func:`time.strptime` and -:meth:`datetime.datetime.strptime`: unconverted data is now quoted, making Quote the unconverted data and the format in the error messages of :func:`time.strptime` and :meth:`datetime.datetime.strptime` so that whitespace such as a trailing newline is visible.