From a2f4e4ecb871e3054e0d5832b73b3c10c23e25ac Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Thu, 20 Mar 2025 18:01:35 +0000 Subject: [PATCH 1/9] Add deprecations- --- Lib/_pydatetime.py | 22 ++++++++++++++++++++-- Modules/_datetimemodule.c | 13 +++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/Lib/_pydatetime.py b/Lib/_pydatetime.py index 26bcd1e491d78c..591a835d28dfe0 100644 --- a/Lib/_pydatetime.py +++ b/Lib/_pydatetime.py @@ -9,6 +9,8 @@ import time as _time import math as _math import sys + +import warnings from operator import index as _index def _cmp(x, y): @@ -452,7 +454,7 @@ def _parse_hh_mm_ss_ff(tstr): return time_comps -def _parse_isoformat_time(tstr): +def _parse_isoformat_time(tstr, is_expanded=False): # Format supported is HH[:MM[:SS[.fff[fff]]]][+HH:MM[:SS[.ffffff]]] len_str = len(tstr) if len_str < 2: @@ -493,6 +495,14 @@ def _parse_isoformat_time(tstr): if len(tzstr) in (0, 1, 3) or tstr[tz_pos-1] == 'Z': raise ValueError("Malformed time zone string") + if is_expanded and ":" not in tzstr and len(tzstr) > 2: + import warnings + warnings.warn( + "Support for partially expanded formats are deprecated in " + "accordance with ISO 8601:2 and will be removed in 3.15", + DeprecationWarning + ) + tz_comps = _parse_hh_mm_ss_ff(tzstr) if all(x == 0 for x in tz_comps): @@ -1933,6 +1943,11 @@ def fromisoformat(cls, date_string): # Split this at the separator try: separator_location = _find_isoformat_datetime_separator(date_string) + if separator_location != len(date_string) and date_string[separator_location] != 'T': + import warnings + warnings.warn("Support of date/time separators other than " + "[\"T\"] is deprecated in accordance with ISO " + "8601:2 and will be removed in 3.15", DeprecationWarning) dstr = date_string[0:separator_location] tstr = date_string[(separator_location+1):] @@ -1943,7 +1958,10 @@ def fromisoformat(cls, date_string): if tstr: try: - time_components, became_next_day, error_from_components = _parse_isoformat_time(tstr) + is_expanded = date_string[4] == "-" + time_components, became_next_day, error_from_components = ( + _parse_isoformat_time(tstr, is_expanded) + ) except ValueError: raise ValueError( f'Invalid isoformat string: {date_string!r}') from None diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 70b59d67f56bda..ee86ef83ef8bcf 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -1130,6 +1130,14 @@ parse_isoformat_time(const char *dtstr, size_t dtlen, int *hour, int *minute, int tzsign = (*tzinfo_pos == '-') ? -1 : 1; tzinfo_pos++; int tzhour = 0, tzminute = 0, tzsecond = 0; + + if (tzinfo_pos + 2 < p_end && tzinfo_pos[2] != ':' && strlen(tzinfo_pos) > 2) { + PyErr_WarnEx(PyExc_DeprecationWarning, + "Support for partially expanded formats is deprecated in " + "accordance with ISO 8601:2 and will be removed in 3.15", + 1); // Level 1 warning + } + rv = parse_hh_mm_ss_ff(tzinfo_pos, p_end, &tzhour, &tzminute, &tzsecond, tzmicrosecond); @@ -5893,6 +5901,11 @@ datetime_fromisoformat(PyObject *cls, PyObject *dtstr) const Py_ssize_t separator_location = _find_isoformat_datetime_separator( dt_ptr, len); + if (separator_location != -1 && dt_ptr[separator_location] != 'T') { + PyErr_WarnEx(PyExc_DeprecationWarning, + "Support of date/time separators other than [\"T\"] is deprecated in " + "accordance with ISO 8601:2 and will be removed in 3.15", 1); + } const char *p = dt_ptr; From 7b52e94f9e160102d4134287800b6e3f29ae65fe Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Thu, 20 Mar 2025 18:06:59 +0000 Subject: [PATCH 2/9] Docs --- Doc/library/datetime.rst | 11 ++++++----- Lib/_pydatetime.py | 2 -- .../2025-03-20-18-00-00.gh-issue-115783.befw32.rst | 1 + 3 files changed, 7 insertions(+), 7 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-03-20-18-00-00.gh-issue-115783.befw32.rst diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst index 1af7d6be750102..080407ac55e1b6 100644 --- a/Doc/library/datetime.rst +++ b/Doc/library/datetime.rst @@ -1076,13 +1076,12 @@ Other constructors, all class methods: ISO 8601 format, with the following exceptions: 1. Time zone offsets may have fractional seconds. - 2. The ``T`` separator may be replaced by any single unicode character. - 3. Fractional hours and minutes are not supported. - 4. Reduced precision dates are not currently supported (``YYYY-MM``, + 2. Fractional hours and minutes are not supported. + 3. Reduced precision dates are not currently supported (``YYYY-MM``, ``YYYY``). - 5. Extended date representations are not currently supported + 4. Extended date representations are not currently supported (``±YYYYYY-MM-DD``). - 6. Ordinal dates are not currently supported (``YYYY-OOO``). + 5. Ordinal dates are not currently supported (``YYYY-OOO``). Examples:: @@ -1111,6 +1110,8 @@ Other constructors, all class methods: .. versionchanged:: 3.11 Previously, this method only supported formats that could be emitted by :meth:`date.isoformat` or :meth:`datetime.isoformat`. + .. versionchanged:: next + Separators other than ``T`` are deprecated. .. classmethod:: datetime.fromisocalendar(year, week, day) diff --git a/Lib/_pydatetime.py b/Lib/_pydatetime.py index 591a835d28dfe0..6e2ca76ae449b0 100644 --- a/Lib/_pydatetime.py +++ b/Lib/_pydatetime.py @@ -9,8 +9,6 @@ import time as _time import math as _math import sys - -import warnings from operator import index as _index def _cmp(x, y): diff --git a/Misc/NEWS.d/next/Library/2025-03-20-18-00-00.gh-issue-115783.befw32.rst b/Misc/NEWS.d/next/Library/2025-03-20-18-00-00.gh-issue-115783.befw32.rst new file mode 100644 index 00000000000000..2832a9fdeffe14 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-03-20-18-00-00.gh-issue-115783.befw32.rst @@ -0,0 +1 @@ +Deprecate support for invalid ISO formats in :func:`datetime.datetime.fromisoformat` From 27711ab95050a7359fc90f644797e9b5b5694308 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Thu, 20 Mar 2025 18:07:51 +0000 Subject: [PATCH 3/9] clean up --- Modules/_datetimemodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index ee86ef83ef8bcf..831403003ccebc 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -1135,7 +1135,7 @@ parse_isoformat_time(const char *dtstr, size_t dtlen, int *hour, int *minute, PyErr_WarnEx(PyExc_DeprecationWarning, "Support for partially expanded formats is deprecated in " "accordance with ISO 8601:2 and will be removed in 3.15", - 1); // Level 1 warning + 1); } rv = parse_hh_mm_ss_ff(tzinfo_pos, p_end, &tzhour, &tzminute, &tzsecond, From b205bd231376b9e1659d68e56c35232f0c0e6874 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Thu, 20 Mar 2025 21:07:06 +0000 Subject: [PATCH 4/9] changes --- Doc/library/datetime.rst | 11 +++++------ Lib/_pydatetime.py | 5 ----- Modules/_datetimemodule.c | 9 +-------- 3 files changed, 6 insertions(+), 19 deletions(-) diff --git a/Doc/library/datetime.rst b/Doc/library/datetime.rst index 080407ac55e1b6..1af7d6be750102 100644 --- a/Doc/library/datetime.rst +++ b/Doc/library/datetime.rst @@ -1076,12 +1076,13 @@ Other constructors, all class methods: ISO 8601 format, with the following exceptions: 1. Time zone offsets may have fractional seconds. - 2. Fractional hours and minutes are not supported. - 3. Reduced precision dates are not currently supported (``YYYY-MM``, + 2. The ``T`` separator may be replaced by any single unicode character. + 3. Fractional hours and minutes are not supported. + 4. Reduced precision dates are not currently supported (``YYYY-MM``, ``YYYY``). - 4. Extended date representations are not currently supported + 5. Extended date representations are not currently supported (``±YYYYYY-MM-DD``). - 5. Ordinal dates are not currently supported (``YYYY-OOO``). + 6. Ordinal dates are not currently supported (``YYYY-OOO``). Examples:: @@ -1110,8 +1111,6 @@ Other constructors, all class methods: .. versionchanged:: 3.11 Previously, this method only supported formats that could be emitted by :meth:`date.isoformat` or :meth:`datetime.isoformat`. - .. versionchanged:: next - Separators other than ``T`` are deprecated. .. classmethod:: datetime.fromisocalendar(year, week, day) diff --git a/Lib/_pydatetime.py b/Lib/_pydatetime.py index 6e2ca76ae449b0..396804d7513fde 100644 --- a/Lib/_pydatetime.py +++ b/Lib/_pydatetime.py @@ -1941,11 +1941,6 @@ def fromisoformat(cls, date_string): # Split this at the separator try: separator_location = _find_isoformat_datetime_separator(date_string) - if separator_location != len(date_string) and date_string[separator_location] != 'T': - import warnings - warnings.warn("Support of date/time separators other than " - "[\"T\"] is deprecated in accordance with ISO " - "8601:2 and will be removed in 3.15", DeprecationWarning) dstr = date_string[0:separator_location] tstr = date_string[(separator_location+1):] diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 831403003ccebc..a8219fa0ddc5e5 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -1134,8 +1134,7 @@ parse_isoformat_time(const char *dtstr, size_t dtlen, int *hour, int *minute, if (tzinfo_pos + 2 < p_end && tzinfo_pos[2] != ':' && strlen(tzinfo_pos) > 2) { PyErr_WarnEx(PyExc_DeprecationWarning, "Support for partially expanded formats is deprecated in " - "accordance with ISO 8601:2 and will be removed in 3.15", - 1); + "accordance with ISO 8601:2 and will be removed in 3.15", 1); } rv = parse_hh_mm_ss_ff(tzinfo_pos, p_end, &tzhour, &tzminute, &tzsecond, @@ -5901,12 +5900,6 @@ datetime_fromisoformat(PyObject *cls, PyObject *dtstr) const Py_ssize_t separator_location = _find_isoformat_datetime_separator( dt_ptr, len); - if (separator_location != -1 && dt_ptr[separator_location] != 'T') { - PyErr_WarnEx(PyExc_DeprecationWarning, - "Support of date/time separators other than [\"T\"] is deprecated in " - "accordance with ISO 8601:2 and will be removed in 3.15", 1); - } - const char *p = dt_ptr; int year = 0, month = 0, day = 0; From 5f858c0ba0bccd583a14ed462a6faade7632b64e Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Sat, 29 Mar 2025 15:43:45 +0000 Subject: [PATCH 5/9] Update stack levels --- Lib/_pydatetime.py | 3 ++- Modules/_datetimemodule.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Lib/_pydatetime.py b/Lib/_pydatetime.py index a397eb4f4ede23..9eef8bb57e5b99 100644 --- a/Lib/_pydatetime.py +++ b/Lib/_pydatetime.py @@ -498,7 +498,8 @@ def _parse_isoformat_time(tstr, is_expanded=False): warnings.warn( "Support for partially expanded formats are deprecated in " "accordance with ISO 8601:2 and will be removed in 3.15", - DeprecationWarning + DeprecationWarning, + stacklevel=2, ) tz_comps = _parse_hh_mm_ss_ff(tzstr) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 5660e00057d310..8053236137ac28 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -1135,7 +1135,7 @@ parse_isoformat_time(const char *dtstr, size_t dtlen, int *hour, int *minute, if (tzinfo_pos + 2 < p_end && tzinfo_pos[2] != ':' && strlen(tzinfo_pos) > 2) { PyErr_WarnEx(PyExc_DeprecationWarning, "Support for partially expanded formats is deprecated in " - "accordance with ISO 8601:2 and will be removed in 3.15", 1); + "accordance with ISO 8601:2 and will be removed in 3.15", 2); } rv = parse_hh_mm_ss_ff(tzinfo_pos, p_end, &tzhour, &tzminute, &tzsecond, From dfe425203910f114918156bc0e7f8b394c341bea Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Sat, 29 Mar 2025 15:46:58 +0000 Subject: [PATCH 6/9] Update Modules/_datetimemodule.c --- Modules/_datetimemodule.c | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 8053236137ac28..0a72b8c06449da 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -5901,6 +5901,7 @@ datetime_fromisoformat(PyObject *cls, PyObject *dtstr) const Py_ssize_t separator_location = _find_isoformat_datetime_separator( dt_ptr, len); + const char *p = dt_ptr; int year = 0, month = 0, day = 0; From 4e3effc9f04166a28f7d4e422eb17eb31b110117 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Sat, 29 Mar 2025 16:48:22 +0000 Subject: [PATCH 7/9] Ignore warnings --- Lib/test/datetimetester.py | 1 + Modules/_datetimemodule.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index ecb37250ceb6c4..37f5784f9ed39b 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -3390,6 +3390,7 @@ def test_fromisoformat_timespecs(self): dt_rt = self.theclass.fromisoformat(dtstr) self.assertEqual(dt, dt_rt) + @warnings_helper.ignore_warnings(category=DeprecationWarning) def test_fromisoformat_datetime_examples(self): UTC = timezone.utc BST = timezone(timedelta(hours=1), 'BST') diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 0a72b8c06449da..492c56af4c8c69 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -1135,7 +1135,7 @@ parse_isoformat_time(const char *dtstr, size_t dtlen, int *hour, int *minute, if (tzinfo_pos + 2 < p_end && tzinfo_pos[2] != ':' && strlen(tzinfo_pos) > 2) { PyErr_WarnEx(PyExc_DeprecationWarning, "Support for partially expanded formats is deprecated in " - "accordance with ISO 8601:2 and will be removed in 3.15", 2); + "accordance with ISO 8601:2 and will be removed in 3.15", 1); } rv = parse_hh_mm_ss_ff(tzinfo_pos, p_end, &tzhour, &tzminute, &tzsecond, From 74d9b7d325b5ef6d51131ed11538ee098fdedbe6 Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Sat, 29 Mar 2025 17:48:05 +0000 Subject: [PATCH 8/9] Ignore warnings again --- Lib/test/datetimetester.py | 1 + Modules/_datetimemodule.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 37f5784f9ed39b..20973e98916783 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -4707,6 +4707,7 @@ def test_fromisoformat_fractions(self): self.assertEqual(actual, expected) + @warnings_helper.ignore_warnings(category=DeprecationWarning) def test_fromisoformat_time_examples(self): examples = [ ('0000', self.theclass(0, 0)), diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 492c56af4c8c69..0a72b8c06449da 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -1135,7 +1135,7 @@ parse_isoformat_time(const char *dtstr, size_t dtlen, int *hour, int *minute, if (tzinfo_pos + 2 < p_end && tzinfo_pos[2] != ':' && strlen(tzinfo_pos) > 2) { PyErr_WarnEx(PyExc_DeprecationWarning, "Support for partially expanded formats is deprecated in " - "accordance with ISO 8601:2 and will be removed in 3.15", 1); + "accordance with ISO 8601:2 and will be removed in 3.15", 2); } rv = parse_hh_mm_ss_ff(tzinfo_pos, p_end, &tzhour, &tzminute, &tzsecond, From 42e5adb5f9f363796411ef3837eea01b9310fd7e Mon Sep 17 00:00:00 2001 From: Stan Ulbrych Date: Sat, 29 Mar 2025 17:53:28 +0000 Subject: [PATCH 9/9] bump stacklevel --- Lib/_pydatetime.py | 2 +- Modules/_datetimemodule.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/_pydatetime.py b/Lib/_pydatetime.py index 9eef8bb57e5b99..101f4ffdfefdcd 100644 --- a/Lib/_pydatetime.py +++ b/Lib/_pydatetime.py @@ -499,7 +499,7 @@ def _parse_isoformat_time(tstr, is_expanded=False): "Support for partially expanded formats are deprecated in " "accordance with ISO 8601:2 and will be removed in 3.15", DeprecationWarning, - stacklevel=2, + stacklevel=3, ) tz_comps = _parse_hh_mm_ss_ff(tzstr) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 0a72b8c06449da..e9e6a941fb08c2 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -1135,7 +1135,7 @@ parse_isoformat_time(const char *dtstr, size_t dtlen, int *hour, int *minute, if (tzinfo_pos + 2 < p_end && tzinfo_pos[2] != ':' && strlen(tzinfo_pos) > 2) { PyErr_WarnEx(PyExc_DeprecationWarning, "Support for partially expanded formats is deprecated in " - "accordance with ISO 8601:2 and will be removed in 3.15", 2); + "accordance with ISO 8601:2 and will be removed in 3.15", 3); } rv = parse_hh_mm_ss_ff(tzinfo_pos, p_end, &tzhour, &tzminute, &tzsecond,