Skip to content

Commit 18450be

Browse files
bpo-34454: Clean up datetime.fromisoformat surrogate handling (GH-8959)
* Use _PyUnicode_Copy in sanitize_isoformat_str * Use repr in fromisoformat error message This reverses commit 67b74a98b2 per Serhiy Storchaka's suggestion: I suggested to use %R in the error message because including the raw string can be confusing in the case of empty string, or string containing trailing whitespaces, invisible or unprintable characters. We agree that it is better to change both the C and pure Python versions to use repr. * Retain non-sanitized dtstr for error printing This does not create an extra string, it just holds on to a reference to the original input string for purposes of creating the error message. * PEP 7 fixes to from_isoformat * Separate handling of Unicode and other errors In the initial implementation, errors other than encoding errors would both raise an error indicating an invalid format, which would not be true for errors like MemoryError. * Drop needs_decref from _sanitize_isoformat_str Instead _sanitize_isoformat_str returns a new reference, even to the original string. (cherry picked from commit 3df8540) Co-authored-by: Paul Ganssle <[email protected]>
1 parent 7f34d55 commit 18450be

File tree

3 files changed

+106
-76
lines changed

3 files changed

+106
-76
lines changed

Lib/datetime.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -857,7 +857,7 @@ def fromisoformat(cls, date_string):
857857
assert len(date_string) == 10
858858
return cls(*_parse_isoformat_date(date_string))
859859
except Exception:
860-
raise ValueError('Invalid isoformat string: %s' % date_string)
860+
raise ValueError(f'Invalid isoformat string: {date_string!r}')
861861

862862

863863
# Conversions to string
@@ -1369,7 +1369,7 @@ def fromisoformat(cls, time_string):
13691369
try:
13701370
return cls(*_parse_isoformat_time(time_string))
13711371
except Exception:
1372-
raise ValueError('Invalid isoformat string: %s' % time_string)
1372+
raise ValueError(f'Invalid isoformat string: {time_string!r}')
13731373

13741374

13751375
def strftime(self, fmt):
@@ -1646,13 +1646,13 @@ def fromisoformat(cls, date_string):
16461646
try:
16471647
date_components = _parse_isoformat_date(dstr)
16481648
except ValueError:
1649-
raise ValueError('Invalid isoformat string: %s' % date_string)
1649+
raise ValueError(f'Invalid isoformat string: {date_string!r}')
16501650

16511651
if tstr:
16521652
try:
16531653
time_components = _parse_isoformat_time(tstr)
16541654
except ValueError:
1655-
raise ValueError('Invalid isoformat string: %s' % date_string)
1655+
raise ValueError(f'Invalid isoformat string: {date_string!r}')
16561656
else:
16571657
time_components = [0, 0, 0, 0, None]
16581658

Lib/test/datetimetester.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import os
1414
import pickle
1515
import random
16+
import re
1617
import struct
1718
import unittest
1819

@@ -2676,6 +2677,14 @@ def test_fromisoformat_fails_datetime(self):
26762677
with self.assertRaises(ValueError):
26772678
self.theclass.fromisoformat(bad_str)
26782679

2680+
def test_fromisoformat_fails_surrogate(self):
2681+
# Test that when fromisoformat() fails with a surrogate character as
2682+
# the separator, the error message contains the original string
2683+
dtstr = "2018-01-03\ud80001:0113"
2684+
2685+
with self.assertRaisesRegex(ValueError, re.escape(repr(dtstr))):
2686+
self.theclass.fromisoformat(dtstr)
2687+
26792688
def test_fromisoformat_utc(self):
26802689
dt_str = '2014-04-19T13:21:13+00:00'
26812690
dt = self.theclass.fromisoformat(dt_str)

0 commit comments

Comments
 (0)