Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 802f440

Browse files
committedMar 21, 2025
gh-131434: Improve error reporting for incorrect format in strptime()
In particularly, fix regression in detecting stray % at the end of the format string.
1 parent 684a759 commit 802f440

File tree

3 files changed

+19
-19
lines changed

3 files changed

+19
-19
lines changed
 

‎Lib/_strptime.py

+5-6
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ def repl(m):
365365
nonlocal day_of_month_in_format
366366
day_of_month_in_format = True
367367
return self[format_char]
368-
format = re_sub(r'%(O?.)', repl, format)
368+
format = re_sub(r'%([OE]?\\?.?)', repl, format)
369369
if day_of_month_in_format and not year_in_format:
370370
import warnings
371371
warnings.warn("""\
@@ -439,14 +439,13 @@ def _strptime(data_string, format="%a %b %d %H:%M:%S %Y"):
439439
# \\, in which case it was a stray % but with a space after it
440440
except KeyError as err:
441441
bad_directive = err.args[0]
442-
if bad_directive == "\\":
443-
bad_directive = "%"
444442
del err
443+
bad_directive = bad_directive.replace('\\s', '')
444+
if not bad_directive:
445+
raise ValueError("stray %% in format '%s'" % format) from None
446+
bad_directive = bad_directive.replace('\\', '', 1)
445447
raise ValueError("'%s' is a bad directive in format '%s'" %
446448
(bad_directive, format)) from None
447-
# IndexError only occurs when the format string is "%"
448-
except IndexError:
449-
raise ValueError("stray %% in format '%s'" % format) from None
450449
_regex_cache[format] = format_regex
451450
found = format_regex.match(data_string)
452451
if not found:

‎Lib/test/test_strptime.py

+13-13
Original file line numberDiff line numberDiff line change
@@ -220,16 +220,16 @@ def test_ValueError(self):
220220
# Make sure ValueError is raised when match fails or format is bad
221221
self.assertRaises(ValueError, _strptime._strptime_time, data_string="%d",
222222
format="%A")
223-
for bad_format in ("%", "% ", "%e"):
224-
try:
223+
for bad_format in ("%", "% ", "%\n"):
224+
with self.assertRaisesRegex(ValueError, "stray % in format "):
225+
_strptime._strptime_time("2005", bad_format)
226+
for bad_format in ("%e", "%Oe", "%O", "%O ", "%Ee", "%E", "%E ",
227+
"%.", "%+", "%_", "%~", "%\\",
228+
"%O.", "%O+", "%O_", "%O~", "%O\\"):
229+
directive = bad_format[1:].rstrip()
230+
with self.assertRaisesRegex(ValueError,
231+
f"'{re.escape(directive)}' is a bad directive in format "):
225232
_strptime._strptime_time("2005", bad_format)
226-
except ValueError:
227-
continue
228-
except Exception as err:
229-
self.fail("'%s' raised %s, not ValueError" %
230-
(bad_format, err.__class__.__name__))
231-
else:
232-
self.fail("'%s' did not raise ValueError" % bad_format)
233233

234234
msg_week_no_year_or_weekday = r"ISO week directive '%V' must be used with " \
235235
r"the ISO year directive '%G' and a weekday directive " \
@@ -285,11 +285,11 @@ def test_strptime_exception_context(self):
285285
# check that this doesn't chain exceptions needlessly (see #17572)
286286
with self.assertRaises(ValueError) as e:
287287
_strptime._strptime_time('', '%D')
288-
self.assertIs(e.exception.__suppress_context__, True)
289-
# additional check for IndexError branch (issue #19545)
288+
self.assertTrue(e.exception.__suppress_context__)
289+
# additional check for stray % branch
290290
with self.assertRaises(ValueError) as e:
291-
_strptime._strptime_time('19', '%Y %')
292-
self.assertIsNone(e.exception.__context__)
291+
_strptime._strptime_time('%', '%')
292+
self.assertTrue(e.exception.__suppress_context__)
293293

294294
def test_unconverteddata(self):
295295
# Check ValueError is raised when there is unconverted data
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Improve error reporting for incorrect format in :func:`time.strptime`.

0 commit comments

Comments
 (0)
Failed to load comments.