8000 [3.13] gh-131434: Improve error reporting for incorrect format in str… · python/cpython@e2d13b4 · GitHub
[go: up one dir, main page]

Skip to content

Commit e2d13b4

Browse files
[3.13] gh-131434: Improve error reporting for incorrect format in strptime() (GH-131568) (GH-132309)
In particularly, fix regression in detecting stray % at the end of the format string. (cherry picked from commit 3feac7a) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
1 parent 8e97bd7 commit e2d13b4

File tree

4 files changed

+23
-23
lines changed

4 files changed

+23
-23
lines changed

Lib/_strptime.py

Lines changed: 5 additions & 6 deletions
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

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

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

297297
def test_unconverteddata(self):
298298
# Check ValueError is raised when there is unconverted data

Lib/test/test_time.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -339,11 +339,11 @@ def test_strptime_exception_context(self):
339339
# check that this doesn't chain exceptions needlessly (see #17572)
340340
with self.assertRaises(ValueError) as e:
341341
time.strptime('', '%D')
342-
self.assertIs(e.exception.__suppress_context__, True)
343-
# additional check for IndexError branch (issue #19545)
342+
self.assertTrue(e.exception.__suppress_context__)
343+
# additional check for stray % branch
344344
with self.assertRaises(ValueError) as e:
345-
time.strptime('19', '%Y %')
346-
self.assertIsNone(e.exception.__context__)
345+
time.strptime('%', '%')
346+
self.assertTrue(e.exception.__suppress_context__)
347347

348348
def test_strptime_leap_year(self):
349349
# GH-70647: warns if parsing a format with a day and no year.
Lines changed: 1 addition & 0 deletions
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)
0