From 764eb1b81131fa99d6ac2ec31c6fa5cba19d53b3 Mon Sep 17 00:00:00 2001 From: donBarbos Date: Wed, 27 Nov 2024 23:05:25 +0400 Subject: [PATCH 01/30] Update error messages to be the same in datetime --- Lib/_pydatetime.py | 32 ++++++++++++++++---------------- Modules/_datetimemodule.c | 38 ++++++++++++++++++++++++-------------- 2 files changed, 40 insertions(+), 30 deletions(-) diff --git a/Lib/_pydatetime.py b/Lib/_pydatetime.py index ed01670cfece43..3d3cba6fbc4fdd 100644 --- a/Lib/_pydatetime.py +++ b/Lib/_pydatetime.py @@ -60,14 +60,14 @@ def _days_in_month(year, month): def _days_before_month(year, month): "year, month -> number of days in year preceding first day of month." - assert 1 <= month <= 12, 'month must be in 1..12' + assert 1 <= month <= 12, f"month must be in 1..12, but got {month}" return _DAYS_BEFORE_MONTH[month] + (month > 2 and _is_leap(year)) def _ymd2ord(year, month, day): "year, month, day -> ordinal, considering 01-Jan-0001 as day 1." - assert 1 <= month <= 12, 'month must be in 1..12' + assert 1 <= month <= 12, f"month must be in 1..12, but got {month}" dim = _days_in_month(year, month) - assert 1 <= day <= dim, ('day must be in 1..%d' % dim) + assert 1 <= day <= dim, f"day must be in 1..{dim}, but got {day}" return (_days_before_year(year) + _days_before_month(year, month) + day) @@ -512,7 +512,7 @@ def _parse_isoformat_time(tstr): def _isoweek_to_gregorian(year, week, day): # Year is bounded this way because 9999-12-31 is (9999, 52, 5) if not MINYEAR <= year <= MAXYEAR: - raise ValueError(f"Year is out of range: {year}") + raise ValueError(f"year must be in {MINYEAR}..{MAXYEAR}, but got {year}") if not 0 < week < 53: out_of_range = True @@ -561,21 +561,21 @@ def _check_utc_offset(name, offset): raise TypeError("tzinfo.%s() must return None " "or timedelta, not '%s'" % (name, type(offset))) if not -timedelta(1) < offset < timedelta(1): - raise ValueError("%s()=%s, must be strictly between " - "-timedelta(hours=24) and timedelta(hours=24)" % - (name, offset)) + raise ValueError("offset must be a timedelta " + "strictly between -timedelta(hours=24) and " + f"timedelta(hours=24), not {offset.__repr__()}") def _check_date_fields(year, month, day): year = _index(year) month = _index(month) day = _index(day) if not MINYEAR <= year <= MAXYEAR: - raise ValueError('year must be in %d..%d' % (MINYEAR, MAXYEAR), year) + raise ValueError(f"year must be in {MINYEAR}..{MAXYEAR}, but got {year}") if not 1 <= month <= 12: - raise ValueError('month must be in 1..12', month) + raise ValueError(f"month must be in 1..12, but got {month}") dim = _days_in_month(year, month) if not 1 <= day <= dim: - raise ValueError('day must be in 1..%d' % dim, day) + raise ValueError(f"day must be in 1..{dim}, but got {day}") return year, month, day def _check_time_fields(hour, minute, second, microsecond, fold): @@ -584,15 +584,15 @@ def _check_time_fields(hour, minute, second, microsecond, fold): second = _index(second) microsecond = _index(microsecond) if not 0 <= hour <= 23: - raise ValueError('hour must be in 0..23', hour) + raise ValueError(f"hour must be in 0..23, but got {hour}") if not 0 <= minute <= 59: - raise ValueError('minute must be in 0..59', minute) + raise ValueError(f"minute must be in 0..59, but got {minute}") if not 0 <= second <= 59: - raise ValueError('second must be in 0..59', second) + raise ValueError(f"second must be in 0..59, but got {second}") if not 0 <= microsecond <= 999999: - raise ValueError('microsecond must be in 0..999999', microsecond) + raise ValueError(f"microsecond must be in 0..999999, but got {microsecond}") if fold not in (0, 1): - raise ValueError('fold must be either 0 or 1', fold) + raise ValueError(f"fold must be either 0 or 1, but got {fold}") return hour, minute, second, microsecond, fold def _check_tzinfo_arg(tz): @@ -2419,7 +2419,7 @@ def __new__(cls, offset, name=_Omitted): if not cls._minoffset <= offset <= cls._maxoffset: raise ValueError("offset must be a timedelta " "strictly between -timedelta(hours=24) and " - "timedelta(hours=24).") + f"timedelta(hours=24), not {offset.__repr__()}") return cls._create(offset, name) def __init_subclass__(cls): diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index b1102984cb5e9e..2d01b1f29fd07a 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -637,17 +637,22 @@ check_date_args(int year, int month, int day) { if (year < MINYEAR || year > MAXYEAR) { - PyErr_Format(PyExc_ValueError, "year %i is out of range", year); + PyErr_Format(PyExc_ValueError, + "year must be in %d..%d, but got %d", + MINYEAR, MAXYEAR, year); return -1; } if (month < 1 || month > 12) { PyErr_SetString(PyExc_ValueError, - "month must be in 1..12"); + "month must be in 1..12, but got %d", + month); return -1; } - if (day < 1 || day > days_in_month(year, month)) { + int dim = days_in_month(year, month) + if (day < 1 || day > dim) { PyErr_SetString(PyExc_ValueError, - "day is out of range for month"); + "day must be in 1..%d, but got %d", + dim, day); return -1; } return 0; @@ -661,27 +666,27 @@ check_time_args(int h, int m, int s, int us, int fold) { if (h < 0 || h > 23) { PyErr_SetString(PyExc_ValueError, - "hour must be in 0..23"); + "hour must be in 0..23, but got %i", h); return -1; } if (m < 0 || m > 59) { PyErr_SetString(PyExc_ValueError, - "minute must be in 0..59"); + "minute must be in 0..59, but got %i", m); return -1; } if (s < 0 || s > 59) { PyErr_SetString(PyExc_ValueError, - "second must be in 0..59"); + "second must be in 0..59, but got %i", s); return -1; } if (us < 0 || us > 999999) { PyErr_SetString(PyExc_ValueError, - "microsecond must be in 0..999999"); + "microsecond must be in 0..999999, but got %i", us); return -1; } if (fold != 0 && fold != 1) { PyErr_SetString(PyExc_ValueError, - "fold must be either 0 or 1"); + "fold must be either 0 or 1, but got %i", fold); return -1; } return 0; @@ -1436,7 +1441,7 @@ new_timezone(PyObject *offset, PyObject *name) PyErr_Format(PyExc_ValueError, "offset must be a timedelta" " strictly between -timedelta(hours=24) and" " timedelta(hours=24)," - " not %R.", offset); + " not %R", offset); return NULL; } @@ -1508,7 +1513,8 @@ call_tzinfo_method(PyObject *tzinfo, const char *name, PyObject *tzinfoarg) Py_DECREF(offset); PyErr_Format(PyExc_ValueError, "offset must be a timedelta" " strictly between -timedelta(hours=24) and" - " timedelta(hours=24)."); + " timedelta(hours=24)," + " not %R", offset); return NULL; } } @@ -3387,7 +3393,9 @@ date_fromisocalendar(PyObject *cls, PyObject *args, PyObject *kw) int rv = iso_to_ymd(year, week, day, &year, &month, &day); if (rv == -4) { - PyErr_Format(PyExc_ValueError, "Year is out of range: %d", year); + PyErr_Format(PyExc_ValueError, + "year must be in %d..%d, but got %d", + MINYEAR, MAXYEAR, year); return NULL; } @@ -3397,7 +3405,7 @@ date_fromisocalendar(PyObject *cls, PyObject *args, PyObject *kw) } if (rv == -3) { - PyErr_Format(PyExc_ValueError, "Invalid day: %d (range is [1, 7])", + PyErr_Format(PyExc_ValueError, "Invalid weekday: %d (range is [1, 7])", day); return NULL; } @@ -5357,7 +5365,9 @@ utc_to_seconds(int year, int month, int day, /* ymd_to_ord() doesn't support year <= 0 */ if (year < MINYEAR || year > MAXYEAR) { - PyErr_Format(PyExc_ValueError, "year %i is out of range", year); + PyErr_Format(PyExc_ValueError, + "year must be in %d..%d, but got %d", + MINYEAR, MAXYEAR, year); return -1; } From 2bf419ffecdfdc23ed8986be2329fa3ae3435bb9 Mon Sep 17 00:00:00 2001 From: donBarbos Date: Wed, 27 Nov 2024 23:29:36 +0400 Subject: [PATCH 02/30] Add NEWS.d/next --- .../next/Library/2024-11-27-23-29-05.gh-issue-109798.OPj1CT.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2024-11-27-23-29-05.gh-issue-109798.OPj1CT.rst diff --git a/Misc/NEWS.d/next/Library/2024-11-27-23-29-05.gh-issue-109798.OPj1CT.rst b/Misc/NEWS.d/next/Library/2024-11-27-23-29-05.gh-issue-109798.OPj1CT.rst new file mode 100644 index 00000000000000..c67a9f721067a4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-11-27-23-29-05.gh-issue-109798.OPj1CT.rst @@ -0,0 +1,2 @@ +Update error messages to be the same in :mod:`datetime`. Patch by Semyon +Moroz. From 4363e9e389d6297eb98a90857ca674c8e5726ec5 Mon Sep 17 00:00:00 2001 From: donBarbos Date: Wed, 27 Nov 2024 23:45:11 +0400 Subject: [PATCH 03/30] fixed syntax errors --- Modules/_datetimemodule.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 2d01b1f29fd07a..babf8d4f521494 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -643,16 +643,16 @@ check_date_args(int year, int month, int day) return -1; } if (month < 1 || month > 12) { - PyErr_SetString(PyExc_ValueError, - "month must be in 1..12, but got %d", - month); + PyErr_Format(PyExc_ValueError, + "month must be in 1..12, but got %d", + month); return -1; } - int dim = days_in_month(year, month) + int dim = days_in_month(year, month); if (day < 1 || day > dim) { - PyErr_SetString(PyExc_ValueError, - "day must be in 1..%d, but got %d", - dim, day); + PyErr_Format(PyExc_ValueError, + "day must be in 1..%d, but got %d", + dim, day); return -1; } return 0; @@ -665,28 +665,28 @@ static int check_time_args(int h, int m, int s, int us, int fold) { if (h < 0 || h > 23) { - PyErr_SetString(PyExc_ValueError, - "hour must be in 0..23, but got %i", h); + PyErr_Format(PyExc_ValueError, + "hour must be in 0..23, but got %i", h); return -1; } if (m < 0 || m > 59) { - PyErr_SetString(PyExc_ValueError, - "minute must be in 0..59, but got %i", m); + PyErr_Format(PyExc_ValueError, + "minute must be in 0..59, but got %i", m); return -1; } if (s < 0 || s > 59) { - PyErr_SetString(PyExc_ValueError, - "second must be in 0..59, but got %i", s); + PyErr_Format(PyExc_ValueError, + "second must be in 0..59, but got %i", s); return -1; } if (us < 0 || us > 999999) { - PyErr_SetString(PyExc_ValueError, - "microsecond must be in 0..999999, but got %i", us); + PyErr_Format(PyExc_ValueError, + "microsecond must be in 0..999999, but got %i", us); return -1; } if (fold != 0 && fold != 1) { - PyErr_SetString(PyExc_ValueError, - "fold must be either 0 or 1, but got %i", fold); + PyErr_Format(PyExc_ValueError, + "fold must be either 0 or 1, but got %i", fold); return -1; } return 0; From 9da0dfca6b634b479af0398265037a2203f07ca5 Mon Sep 17 00:00:00 2001 From: donBarbos Date: Fri, 29 Nov 2024 16:31:41 +0400 Subject: [PATCH 04/30] Move Py_DECREF after PyErr_Format --- Modules/_datetimemodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index babf8d4f521494..8ba0cf5e3b4ad4 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -1510,11 +1510,11 @@ call_tzinfo_method(PyObject *tzinfo, const char *name, PyObject *tzinfoarg) GET_TD_SECONDS(offset) == 0 && GET_TD_MICROSECONDS(offset) < 1) || GET_TD_DAYS(offset) < -1 || GET_TD_DAYS(offset) >= 1) { - Py_DECREF(offset); PyErr_Format(PyExc_ValueError, "offset must be a timedelta" " strictly between -timedelta(hours=24) and" " timedelta(hours=24)," " not %R", offset); + Py_DECREF(offset); return NULL; } } From 179423d8964e9be4694807afd0cfa4fc04a06de8 Mon Sep 17 00:00:00 2001 From: donBarbos Date: Fri, 29 Nov 2024 16:58:59 +0400 Subject: [PATCH 05/30] Add more info in message error in _pydatetime impl --- Lib/_pydatetime.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Lib/_pydatetime.py b/Lib/_pydatetime.py index 3d3cba6fbc4fdd..34240fc32af3db 100644 --- a/Lib/_pydatetime.py +++ b/Lib/_pydatetime.py @@ -545,7 +545,7 @@ def _isoweek_to_gregorian(year, week, day): def _check_tzname(name): if name is not None and not isinstance(name, str): raise TypeError("tzinfo.tzname() must return None or string, " - "not '%s'" % type(name)) + "not '%s'" % type(name).__name__) # name is the offset-producing method, "utcoffset" or "dst". # offset is what it returned. @@ -559,7 +559,8 @@ def _check_utc_offset(name, offset): return if not isinstance(offset, timedelta): raise TypeError("tzinfo.%s() must return None " - "or timedelta, not '%s'" % (name, type(offset))) + "or timedelta, not '%s'" + % (name, type(offset).__name__)) if not -timedelta(1) < offset < timedelta(1): raise ValueError("offset must be a timedelta " "strictly between -timedelta(hours=24) and " @@ -597,7 +598,10 @@ def _check_time_fields(hour, minute, second, microsecond, fold): def _check_tzinfo_arg(tz): if tz is not None and not isinstance(tz, tzinfo): - raise TypeError("tzinfo argument must be None or of a tzinfo subclass") + raise TypeError( + "tzinfo argument must be None or of a tzinfo subclass, " + f"not type '{type(tz).__name__}'" + ) def _divide_and_round(a, b): """divide a by b and round result to the nearest integer From f691251761bdb68247b99ee9f72db5c95c173caa Mon Sep 17 00:00:00 2001 From: donBarbos Date: Fri, 29 Nov 2024 13:30:57 +0000 Subject: [PATCH 06/30] Update Modules/_datetimemodule.c Co-authored-by: Erlend E. Aasland --- Modules/_datetimemodule.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 8ba0cf5e3b4ad4..2dc632da0ad606 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -644,8 +644,7 @@ check_date_args(int year, int month, int day) } if (month < 1 || month > 12) { PyErr_Format(PyExc_ValueError, - "month must be in 1..12, but got %d", - month); + "month must be in 1..12, but got %d", month); return -1; } int dim = days_in_month(year, month); From d8973cf33bbf17812aa39982083f96110499f9ab Mon Sep 17 00:00:00 2001 From: donBarbos Date: Fri, 29 Nov 2024 13:31:32 +0000 Subject: [PATCH 07/30] Update Modules/_datetimemodule.c Co-authored-by: Erlend E. Aasland --- Modules/_datetimemodule.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 2dc632da0ad606..42a47de9664766 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -650,8 +650,7 @@ check_date_args(int year, int month, int day) int dim = days_in_month(year, month); if (day < 1 || day > dim) { PyErr_Format(PyExc_ValueError, - "day must be in 1..%d, but got %d", - dim, day); + "day must be in 1..%d, but got %d", dim, day); return -1; } return 0; From 5eea62fa036b9f3e9976a08f5297d66ebd28bae0 Mon Sep 17 00:00:00 2001 From: donBarbos Date: Fri, 29 Nov 2024 13:31:49 +0000 Subject: [PATCH 08/30] Update Modules/_datetimemodule.c Co-authored-by: Erlend E. Aasland --- Modules/_datetimemodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 42a47de9664766..3b69dab1b63c3f 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -664,7 +664,7 @@ check_time_args(int h, int m, int s, int us, int fold) { if (h < 0 || h > 23) { PyErr_Format(PyExc_ValueError, - "hour must be in 0..23, but got %i", h); + "hour must be in 0..23, but got %i", h); return -1; } if (m < 0 || m > 59) { From 79543cc11883a02a2687c6173f9ab38746aeb000 Mon Sep 17 00:00:00 2001 From: donBarbos Date: Fri, 29 Nov 2024 17:38:21 +0400 Subject: [PATCH 09/30] Update Modules/_datetimemodule.c for optimisation --- Modules/_datetimemodule.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 3b69dab1b63c3f..d639abf4b47da4 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -647,10 +647,10 @@ check_date_args(int year, int month, int day) "month must be in 1..12, but got %d", month); return -1; } - int dim = days_in_month(year, month); - if (day < 1 || day > dim) { + if (day < 1 || day > days_in_month(year, month)) { PyErr_Format(PyExc_ValueError, - "day must be in 1..%d, but got %d", dim, day); + "day must be in 1..%d, but got %d", + days_in_month(year, month), day); return -1; } return 0; From d174497cf223bb6e0c95bbf5e584c5d4e1ce9d07 Mon Sep 17 00:00:00 2001 From: donBarbos Date: Fri, 29 Nov 2024 17:42:17 +0400 Subject: [PATCH 10/30] Revert last update Modules/_datetimemodule.c --- Modules/_datetimemodule.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index d639abf4b47da4..3b69dab1b63c3f 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -647,10 +647,10 @@ check_date_args(int year, int month, int day) "month must be in 1..12, but got %d", month); return -1; } - if (day < 1 || day > days_in_month(year, month)) { + int dim = days_in_month(year, month); + if (day < 1 || day > dim) { PyErr_Format(PyExc_ValueError, - "day must be in 1..%d, but got %d", - days_in_month(year, month), day); + "day must be in 1..%d, but got %d", dim, day); return -1; } return 0; From 498c4ba9bd33f6870d5267daa36119753526a359 Mon Sep 17 00:00:00 2001 From: donBarbos Date: Fri, 29 Nov 2024 18:08:17 +0400 Subject: [PATCH 11/30] Update Modules/_datetimemodule.c --- Modules/_datetimemodule.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 3b69dab1b63c3f..5d020bb093109f 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -669,22 +669,22 @@ check_time_args(int h, int m, int s, int us, int fold) } if (m < 0 || m > 59) { PyErr_Format(PyExc_ValueError, - "minute must be in 0..59, but got %i", m); + "minute must be in 0..59, but got %i", m); return -1; } if (s < 0 || s > 59) { PyErr_Format(PyExc_ValueError, - "second must be in 0..59, but got %i", s); + "second must be in 0..59, but got %i", s); return -1; } if (us < 0 || us > 999999) { PyErr_Format(PyExc_ValueError, - "microsecond must be in 0..999999, but got %i", us); + "microsecond must be in 0..999999, but got %i", us); return -1; } if (fold != 0 && fold != 1) { PyErr_Format(PyExc_ValueError, - "fold must be either 0 or 1, but got %i", fold); + "fold must be either 0 or 1, but got %i", fold); return -1; } return 0; From 216d0fe75732954e7aa7f8bf77c8233854e47f5d Mon Sep 17 00:00:00 2001 From: donBarbos Date: Fri, 29 Nov 2024 18:36:53 +0400 Subject: [PATCH 12/30] Update Misc/NEWS.d message --- .../Library/2024-11-27-23-29-05.gh-issue-109798.OPj1CT.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Misc/NEWS.d/next/Library/2024-11-27-23-29-05.gh-issue-109798.OPj1CT.rst b/Misc/NEWS.d/next/Library/2024-11-27-23-29-05.gh-issue-109798.OPj1CT.rst index c67a9f721067a4..bbb2a33dc5db85 100644 --- a/Misc/NEWS.d/next/Library/2024-11-27-23-29-05.gh-issue-109798.OPj1CT.rst +++ b/Misc/NEWS.d/next/Library/2024-11-27-23-29-05.gh-issue-109798.OPj1CT.rst @@ -1,2 +1,3 @@ -Update error messages to be the same in :mod:`datetime`. Patch by Semyon -Moroz. +Normalized error messages for both implementations :mod:`_datetime` and +:mod:`_pydatetime`. Made more detailed error output showing input values. +Patch by Semyon Moroz. From c409feceb32d8e3c77ac26127c0fa834a10c29af Mon Sep 17 00:00:00 2001 From: donBarbos Date: Fri, 29 Nov 2024 18:43:39 +0400 Subject: [PATCH 13/30] Update Misc/NEWS.d message --- .../Library/2024-11-27-23-29-05.gh-issue-109798.OPj1CT.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Misc/NEWS.d/next/Library/2024-11-27-23-29-05.gh-issue-109798.OPj1CT.rst b/Misc/NEWS.d/next/Library/2024-11-27-23-29-05.gh-issue-109798.OPj1CT.rst index bbb2a33dc5db85..5aee89c381b7fc 100644 --- a/Misc/NEWS.d/next/Library/2024-11-27-23-29-05.gh-issue-109798.OPj1CT.rst +++ b/Misc/NEWS.d/next/Library/2024-11-27-23-29-05.gh-issue-109798.OPj1CT.rst @@ -1,3 +1,2 @@ -Normalized error messages for both implementations :mod:`_datetime` and -:mod:`_pydatetime`. Made more detailed error output showing input values. -Patch by Semyon Moroz. +Normalized error messages for both implementations :mod:`datetime`. Made more +detailed error messages showing input values. Patch by Semyon Moroz. From 3f454f639d41927c8bf2023d4d9c6a969779ab41 Mon Sep 17 00:00:00 2001 From: donBarbos Date: Fri, 29 Nov 2024 18:44:44 +0400 Subject: [PATCH 14/30] Update Misc/NEWS.d message --- .../next/Library/2024-11-27-23-29-05.gh-issue-109798.OPj1CT.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2024-11-27-23-29-05.gh-issue-109798.OPj1CT.rst b/Misc/NEWS.d/next/Library/2024-11-27-23-29-05.gh-issue-109798.OPj1CT.rst index 5aee89c381b7fc..990d341e180bf6 100644 --- a/Misc/NEWS.d/next/Library/2024-11-27-23-29-05.gh-issue-109798.OPj1CT.rst +++ b/Misc/NEWS.d/next/Library/2024-11-27-23-29-05.gh-issue-109798.OPj1CT.rst @@ -1,2 +1,2 @@ -Normalized error messages for both implementations :mod:`datetime`. Made more +Normalized error messages for both implementations of :mod:`datetime`. Made more detailed error messages showing input values. Patch by Semyon Moroz. From a2b8f7afc87f3cb9fe972f6554e3699c4df45dfd Mon Sep 17 00:00:00 2001 From: donBarbos Date: Fri, 29 Nov 2024 18:45:10 +0400 Subject: [PATCH 15/30] Update Misc/NEWS.d message --- .../Library/2024-11-27-23-29-05.gh-issue-109798.OPj1CT.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Misc/NEWS.d/next/Library/2024-11-27-23-29-05.gh-issue-109798.OPj1CT.rst b/Misc/NEWS.d/next/Library/2024-11-27-23-29-05.gh-issue-109798.OPj1CT.rst index 990d341e180bf6..e3d49467fe3880 100644 --- a/Misc/NEWS.d/next/Library/2024-11-27-23-29-05.gh-issue-109798.OPj1CT.rst +++ b/Misc/NEWS.d/next/Library/2024-11-27-23-29-05.gh-issue-109798.OPj1CT.rst @@ -1,2 +1,2 @@ -Normalized error messages for both implementations of :mod:`datetime`. Made more -detailed error messages showing input values. Patch by Semyon Moroz. +Normalized error messages for both implementations of :mod:`datetime`. Made +more detailed error messages showing input values. Patch by Semyon Moroz. From 209c3384200ea44b7a5bb3c7d97f6c9757028544 Mon Sep 17 00:00:00 2001 From: donBarbos Date: Sat, 30 Nov 2024 04:15:40 +0000 Subject: [PATCH 16/30] Update Lib/_pydatetime.py Co-authored-by: Peter Bierma --- Lib/_pydatetime.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/_pydatetime.py b/Lib/_pydatetime.py index 34240fc32af3db..4a38d24168fa56 100644 --- a/Lib/_pydatetime.py +++ b/Lib/_pydatetime.py @@ -545,7 +545,7 @@ def _isoweek_to_gregorian(year, week, day): def _check_tzname(name): if name is not None and not isinstance(name, str): raise TypeError("tzinfo.tzname() must return None or string, " - "not '%s'" % type(name).__name__) + f"not {type(name).__name__!r}") # name is the offset-producing method, "utcoffset" or "dst". # offset is what it returned. From 0777aa5a9e2ab68f6e039e95d0e654b4ff35aae7 Mon Sep 17 00:00:00 2001 From: donBarbos Date: Sat, 30 Nov 2024 04:16:22 +0000 Subject: [PATCH 17/30] Update Misc/NEWS.d/next/Library/2024-11-27-23-29-05.gh-issue-109798.OPj1CT.rst Co-authored-by: Peter Bierma --- .../Library/2024-11-27-23-29-05.gh-issue-109798.OPj1CT.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Misc/NEWS.d/next/Library/2024-11-27-23-29-05.gh-issue-109798.OPj1CT.rst b/Misc/NEWS.d/next/Library/2024-11-27-23-29-05.gh-issue-109798.OPj1CT.rst index e3d49467fe3880..fb6b0657b8c464 100644 --- a/Misc/NEWS.d/next/Library/2024-11-27-23-29-05.gh-issue-109798.OPj1CT.rst +++ b/Misc/NEWS.d/next/Library/2024-11-27-23-29-05.gh-issue-109798.OPj1CT.rst @@ -1,2 +1,2 @@ -Normalized error messages for both implementations of :mod:`datetime`. Made -more detailed error messages showing input values. Patch by Semyon Moroz. +Normalized error messages for :mod:`datetime`, and made +them more useful when debugging. Patch by Semyon Moroz. From 4d31d33ab6b896640559c27b33d04abc8c6f4419 Mon Sep 17 00:00:00 2001 From: donBarbos Date: Sat, 30 Nov 2024 04:17:22 +0000 Subject: [PATCH 18/30] Update Lib/_pydatetime.py Co-authored-by: Peter Bierma --- Lib/_pydatetime.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/_pydatetime.py b/Lib/_pydatetime.py index 4a38d24168fa56..8d6024e0088153 100644 --- a/Lib/_pydatetime.py +++ b/Lib/_pydatetime.py @@ -558,7 +558,7 @@ def _check_utc_offset(name, offset): if offset is None: return if not isinstance(offset, timedelta): - raise TypeError("tzinfo.%s() must return None " + raise TypeError(f"tzinfo.{name}() must return None " "or timedelta, not '%s'" % (name, type(offset).__name__)) if not -timedelta(1) < offset < timedelta(1): From f05ebba026778417457edbda2f910417ca1bc49d Mon Sep 17 00:00:00 2001 From: donBarbos Date: Sat, 30 Nov 2024 04:27:50 +0000 Subject: [PATCH 19/30] Update Lib/_pydatetime.py Co-authored-by: Peter Bierma --- Lib/_pydatetime.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/_pydatetime.py b/Lib/_pydatetime.py index 8d6024e0088153..db4574aedcfafe 100644 --- a/Lib/_pydatetime.py +++ b/Lib/_pydatetime.py @@ -564,7 +564,7 @@ def _check_utc_offset(name, offset): if not -timedelta(1) < offset < timedelta(1): raise ValueError("offset must be a timedelta " "strictly between -timedelta(hours=24) and " - f"timedelta(hours=24), not {offset.__repr__()}") + f"timedelta(hours=24), not {offset!r}") def _check_date_fields(year, month, day): year = _index(year) From f84010575eb911e6561f5aacab9a5fceff3418a2 Mon Sep 17 00:00:00 2001 From: donBarbos Date: Sat, 30 Nov 2024 04:28:00 +0000 Subject: [PATCH 20/30] Update Lib/_pydatetime.py Co-authored-by: Peter Bierma --- Lib/_pydatetime.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/_pydatetime.py b/Lib/_pydatetime.py index db4574aedcfafe..f783d28aa29d35 100644 --- a/Lib/_pydatetime.py +++ b/Lib/_pydatetime.py @@ -2423,7 +2423,7 @@ def __new__(cls, offset, name=_Omitted): if not cls._minoffset <= offset <= cls._maxoffset: raise ValueError("offset must be a timedelta " "strictly between -timedelta(hours=24) and " - f"timedelta(hours=24), not {offset.__repr__()}") + f"timedelta(hours=24), not {offset!r}") return cls._create(offset, name) def __init_subclass__(cls): From 61c95a59f4fbaf773b1495d97e5a5bb43e7cd305 Mon Sep 17 00:00:00 2001 From: donBarbos Date: Sat, 30 Nov 2024 04:28:11 +0000 Subject: [PATCH 21/30] Update Lib/_pydatetime.py Co-authored-by: Peter Bierma --- Lib/_pydatetime.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/_pydatetime.py b/Lib/_pydatetime.py index f783d28aa29d35..c8310b7c2dfe59 100644 --- a/Lib/_pydatetime.py +++ b/Lib/_pydatetime.py @@ -600,7 +600,7 @@ def _check_tzinfo_arg(tz): if tz is not None and not isinstance(tz, tzinfo): raise TypeError( "tzinfo argument must be None or of a tzinfo subclass, " - f"not type '{type(tz).__name__}'" + f"not type {type(tz).__name__!r}" ) def _divide_and_round(a, b): From 2ab77b3978693a5d25a1eaa83555654a37f2fafb Mon Sep 17 00:00:00 2001 From: donBarbos Date: Sat, 30 Nov 2024 04:28:21 +0000 Subject: [PATCH 22/30] Update Lib/_pydatetime.py Co-authored-by: Peter Bierma --- Lib/_pydatetime.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Lib/_pydatetime.py b/Lib/_pydatetime.py index c8310b7c2dfe59..7a4009c9de9450 100644 --- a/Lib/_pydatetime.py +++ b/Lib/_pydatetime.py @@ -559,8 +559,7 @@ def _check_utc_offset(name, offset): return if not isinstance(offset, timedelta): raise TypeError(f"tzinfo.{name}() must return None " - "or timedelta, not '%s'" - % (name, type(offset).__name__)) + f"or timedelta, not {type(offset).__name__!r}" if not -timedelta(1) < offset < timedelta(1): raise ValueError("offset must be a timedelta " "strictly between -timedelta(hours=24) and " From b1e272a57cfb4b3502e208da806148c11e84cb99 Mon Sep 17 00:00:00 2001 From: donBarbos Date: Sat, 30 Nov 2024 08:33:03 +0400 Subject: [PATCH 23/30] Update Lib/_pydatetime.py --- Lib/_pydatetime.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/_pydatetime.py b/Lib/_pydatetime.py index 7a4009c9de9450..2373e4ea736f85 100644 --- a/Lib/_pydatetime.py +++ b/Lib/_pydatetime.py @@ -545,7 +545,7 @@ def _isoweek_to_gregorian(year, week, day): def _check_tzname(name): if name is not None and not isinstance(name, str): raise TypeError("tzinfo.tzname() must return None or string, " - f"not {type(name).__name__!r}") + f"not '{type(name).__name__!r}'") # name is the offset-producing method, "utcoffset" or "dst". # offset is what it returned. @@ -559,7 +559,7 @@ def _check_utc_offset(name, offset): return if not isinstance(offset, timedelta): raise TypeError(f"tzinfo.{name}() must return None " - f"or timedelta, not {type(offset).__name__!r}" + f"or timedelta, not {type(offset).__name__!r}") if not -timedelta(1) < offset < timedelta(1): raise ValueError("offset must be a timedelta " "strictly between -timedelta(hours=24) and " From 7a35bd4e338bb65ad903b9d4fa4491d00f695bf8 Mon Sep 17 00:00:00 2001 From: donBarbos Date: Sat, 30 Nov 2024 08:36:25 +0400 Subject: [PATCH 24/30] Update Lib/_pydatetime.py --- Lib/_pydatetime.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/_pydatetime.py b/Lib/_pydatetime.py index 2373e4ea736f85..dfe27213131970 100644 --- a/Lib/_pydatetime.py +++ b/Lib/_pydatetime.py @@ -545,7 +545,7 @@ def _isoweek_to_gregorian(year, week, day): def _check_tzname(name): if name is not None and not isinstance(name, str): raise TypeError("tzinfo.tzname() must return None or string, " - f"not '{type(name).__name__!r}'") + f"not {type(name).__name__!r}") # name is the offset-producing method, "utcoffset" or "dst". # offset is what it returned. From cfd18cb4685be2ae1b1a31d5a95beff41fb0b4b8 Mon Sep 17 00:00:00 2001 From: donBarbos Date: Sat, 30 Nov 2024 10:32:32 +0400 Subject: [PATCH 25/30] Add tests --- Lib/test/datetimetester.py | 70 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 25a3015c4e19ce..2ad649b9c9e4db 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -1962,6 +1962,23 @@ def test_backdoor_resistance(self): # blow up because other fields are insane. self.theclass(base[:2] + bytes([ord_byte]) + base[3:]) + def test_valuerror_messages(self): + pattern = re.compile( + r"(year|month|day) must be in \d+\.\.\d+, but got \d+" + ) + test_cases = [ + (2009, 1, 32), # Day out of range + (2009, 2, 31), # Day out of range + (2009, 13, 1), # Month out of range + (2009, 0, 1), # Month out of range + (10000, 12, 31), # Year out of range + (0, 12, 31), # Year out of range + ] + for case in test_cases: + with self.subTest(case): + with self.assertRaisesRegex(ValueError, pattern): + self.theclass(*case) + def test_fromisoformat(self): # Test that isoformat() is reversible base_dates = [ @@ -3212,6 +3229,24 @@ class DateTimeSubclass(self.theclass): self.assertEqual(res.year, 2013) self.assertEqual(res.fold, fold) + def test_valuerror_messages(self): + pattern = re.compile( + r"(year|month|day|hour|minute|second) must " + r"be in \d+\.\.\d+, but got \d+" + ) + test_cases = [ + (2009, 4, 1, 12, 30, 90), # Second out of range + (2009, 4, 1, 12, 90, 45), # Minute out of range + (2009, 4, 1, 25, 30, 45), # Hour out of range + (2009, 4, 32, 24, 0, 0), # Day out of range + (2009, 13, 1, 24, 0, 0), # Month out of range + (9999, 12, 31, 24, 0, 0), # Year out of range + ] + for case in test_cases: + with self.subTest(case): + with self.assertRaisesRegex(ValueError, pattern): + self.theclass(*case) + def test_fromisoformat_datetime(self): # Test that isoformat() is reversible base_dates = [ @@ -3505,6 +3540,25 @@ def test_fromisoformat_fails_datetime(self): with self.assertRaises(ValueError): self.theclass.fromisoformat(bad_str) + def test_fromisoformat_fails_datetime_valueerror(self): + pattern = re.compile( + r"(year|month|day|hour|minute|second) must " + r"be in \d+\.\.\d+, but got \d+" + ) + bad_strs = [ + "2009-04-01T12:30:90", # Second out of range + "2009-04-01T12:90:45", # Minute out of range + "2009-04-01T25:30:45", # Hour out of range + "2009-04-32T24:00:00", # Day out of range + "2009-13-01T24:00:00", # Month out of range + "9999-12-31T24:00:00", # Year out of range + ] + + for bad_str in bad_strs: + with self.subTest(bad_str=bad_str): + with self.assertRaisesRegex(ValueError, pattern): + self.theclass.fromisoformat(bad_str) + def test_fromisoformat_fails_surrogate(self): # Test that when fromisoformat() fails with a surrogate character as # the separator, the error message contains the original string @@ -4481,6 +4535,22 @@ def utcoffset(self, t): t2 = t2.replace(tzinfo=Varies()) self.assertTrue(t1 < t2) # t1's offset counter still going up + def test_valuerror_messages(self): + pattern = re.compile( + r"(hour|minute|second|microsecond) must " + r"be in \d+\.\.\d+, but got \d+" + ) + test_cases = [ + (12, 30, 90, 9999991), # Microsecond out of range + (12, 30, 90, 000000), # Second out of range + (25, 30, 45, 000000), # Hour out of range + (12, 90, 45, 000000), # Minute out of range + ] + for case in test_cases: + with self.subTest(case): + with self.assertRaisesRegex(ValueError, pattern): + self.theclass(*case) + def test_fromisoformat(self): time_examples = [ (0, 0, 0, 0), From 282751480520653268ce2e5fc0f48dc1d3b6358d Mon Sep 17 00:00:00 2001 From: donBarbos Date: Sun, 1 Dec 2024 07:43:57 +0400 Subject: [PATCH 26/30] Update _pydatetime.py Co-authored-by: Peter Bierma --- Lib/_pydatetime.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/_pydatetime.py b/Lib/_pydatetime.py index dfe27213131970..82a492075627a9 100644 --- a/Lib/_pydatetime.py +++ b/Lib/_pydatetime.py @@ -599,7 +599,7 @@ def _check_tzinfo_arg(tz): if tz is not None and not isinstance(tz, tzinfo): raise TypeError( "tzinfo argument must be None or of a tzinfo subclass, " - f"not type {type(tz).__name__!r}" + f"but got {type(tz).__name__!r}" ) def _divide_and_round(a, b): From cd3bdc1cd7dbb22d981b15b741aab912e0e0d072 Mon Sep 17 00:00:00 2001 From: donBarbos Date: Sun, 1 Dec 2024 07:44:08 +0400 Subject: [PATCH 27/30] Update _pydatetime.py Co-authored-by: Peter Bierma --- Lib/_pydatetime.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/_pydatetime.py b/Lib/_pydatetime.py index 82a492075627a9..f92d74e44863a8 100644 --- a/Lib/_pydatetime.py +++ b/Lib/_pydatetime.py @@ -2422,7 +2422,7 @@ def __new__(cls, offset, name=_Omitted): if not cls._minoffset <= offset <= cls._maxoffset: raise ValueError("offset must be a timedelta " "strictly between -timedelta(hours=24) and " - f"timedelta(hours=24), not {offset!r}") + f"timedelta(hours=24), but got {offset!r}") return cls._create(offset, name) def __init_subclass__(cls): From 9915dfeaa47b5315d5312f919cfdcc3d3040e48b Mon Sep 17 00:00:00 2001 From: donBarbos Date: Sun, 1 Dec 2024 08:30:15 +0400 Subject: [PATCH 28/30] Change but got to not --- Lib/_pydatetime.py | 28 ++++++++++++++-------------- Lib/test/datetimetester.py | 8 ++++---- Modules/_datetimemodule.c | 22 +++++++++++----------- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/Lib/_pydatetime.py b/Lib/_pydatetime.py index f92d74e44863a8..fd65161bdd62ea 100644 --- a/Lib/_pydatetime.py +++ b/Lib/_pydatetime.py @@ -60,14 +60,14 @@ def _days_in_month(year, month): def _days_before_month(year, month): "year, month -> number of days in year preceding first day of month." - assert 1 <= month <= 12, f"month must be in 1..12, but got {month}" + assert 1 <= month <= 12, f"month must be in 1..12, not {month}" return _DAYS_BEFORE_MONTH[month] + (month > 2 and _is_leap(year)) def _ymd2ord(year, month, day): "year, month, day -> ordinal, considering 01-Jan-0001 as day 1." - assert 1 <= month <= 12, f"month must be in 1..12, but got {month}" + assert 1 <= month <= 12, f"month must be in 1..12, not {month}" dim = _days_in_month(year, month) - assert 1 <= day <= dim, f"day must be in 1..{dim}, but got {day}" + assert 1 <= day <= dim, f"day must be in 1..{dim}, not {day}" return (_days_before_year(year) + _days_before_month(year, month) + day) @@ -512,7 +512,7 @@ def _parse_isoformat_time(tstr): def _isoweek_to_gregorian(year, week, day): # Year is bounded this way because 9999-12-31 is (9999, 52, 5) if not MINYEAR <= year <= MAXYEAR: - raise ValueError(f"year must be in {MINYEAR}..{MAXYEAR}, but got {year}") + raise ValueError(f"year must be in {MINYEAR}..{MAXYEAR}, not {year}") if not 0 < week < 53: out_of_range = True @@ -570,12 +570,12 @@ def _check_date_fields(year, month, day): month = _index(month) day = _index(day) if not MINYEAR <= year <= MAXYEAR: - raise ValueError(f"year must be in {MINYEAR}..{MAXYEAR}, but got {year}") + raise ValueError(f"year must be in {MINYEAR}..{MAXYEAR}, not {year}") if not 1 <= month <= 12: - raise ValueError(f"month must be in 1..12, but got {month}") + raise ValueError(f"month must be in 1..12, not {month}") dim = _days_in_month(year, month) if not 1 <= day <= dim: - raise ValueError(f"day must be in 1..{dim}, but got {day}") + raise ValueError(f"day must be in 1..{dim}, not {day}") return year, month, day def _check_time_fields(hour, minute, second, microsecond, fold): @@ -584,22 +584,22 @@ def _check_time_fields(hour, minute, second, microsecond, fold): second = _index(second) microsecond = _index(microsecond) if not 0 <= hour <= 23: - raise ValueError(f"hour must be in 0..23, but got {hour}") + raise ValueError(f"hour must be in 0..23, not {hour}") if not 0 <= minute <= 59: - raise ValueError(f"minute must be in 0..59, but got {minute}") + raise ValueError(f"minute must be in 0..59, not {minute}") if not 0 <= second <= 59: - raise ValueError(f"second must be in 0..59, but got {second}") + raise ValueError(f"second must be in 0..59, not {second}") if not 0 <= microsecond <= 999999: - raise ValueError(f"microsecond must be in 0..999999, but got {microsecond}") + raise ValueError(f"microsecond must be in 0..999999, not {microsecond}") if fold not in (0, 1): - raise ValueError(f"fold must be either 0 or 1, but got {fold}") + raise ValueError(f"fold must be either 0 or 1, not {fold}") return hour, minute, second, microsecond, fold def _check_tzinfo_arg(tz): if tz is not None and not isinstance(tz, tzinfo): raise TypeError( "tzinfo argument must be None or of a tzinfo subclass, " - f"but got {type(tz).__name__!r}" + f"not {type(tz).__name__!r}" ) def _divide_and_round(a, b): @@ -2422,7 +2422,7 @@ def __new__(cls, offset, name=_Omitted): if not cls._minoffset <= offset <= cls._maxoffset: raise ValueError("offset must be a timedelta " "strictly between -timedelta(hours=24) and " - f"timedelta(hours=24), but got {offset!r}") + f"timedelta(hours=24), not {offset!r}") return cls._create(offset, name) def __init_subclass__(cls): diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 2ad649b9c9e4db..8c2a54c6965a07 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -1964,7 +1964,7 @@ def test_backdoor_resistance(self): def test_valuerror_messages(self): pattern = re.compile( - r"(year|month|day) must be in \d+\.\.\d+, but got \d+" + r"(year|month|day) must be in \d+\.\.\d+, not \d+" ) test_cases = [ (2009, 1, 32), # Day out of range @@ -3232,7 +3232,7 @@ class DateTimeSubclass(self.theclass): def test_valuerror_messages(self): pattern = re.compile( r"(year|month|day|hour|minute|second) must " - r"be in \d+\.\.\d+, but got \d+" + r"be in \d+\.\.\d+, not \d+" ) test_cases = [ (2009, 4, 1, 12, 30, 90), # Second out of range @@ -3543,7 +3543,7 @@ def test_fromisoformat_fails_datetime(self): def test_fromisoformat_fails_datetime_valueerror(self): pattern = re.compile( r"(year|month|day|hour|minute|second) must " - r"be in \d+\.\.\d+, but got \d+" + r"be in \d+\.\.\d+, not \d+" ) bad_strs = [ "2009-04-01T12:30:90", # Second out of range @@ -4538,7 +4538,7 @@ def utcoffset(self, t): def test_valuerror_messages(self): pattern = re.compile( r"(hour|minute|second|microsecond) must " - r"be in \d+\.\.\d+, but got \d+" + r"be in \d+\.\.\d+, not \d+" ) test_cases = [ (12, 30, 90, 9999991), # Microsecond out of range diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 5d020bb093109f..64d3631649b3c7 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -638,19 +638,19 @@ check_date_args(int year, int month, int day) if (year < MINYEAR || year > MAXYEAR) { PyErr_Format(PyExc_ValueError, - "year must be in %d..%d, but got %d", + "year must be in %d..%d, not %d", MINYEAR, MAXYEAR, year); return -1; } if (month < 1 || month > 12) { PyErr_Format(PyExc_ValueError, - "month must be in 1..12, but got %d", month); + "month must be in 1..12, not %d", month); return -1; } int dim = days_in_month(year, month); if (day < 1 || day > dim) { PyErr_Format(PyExc_ValueError, - "day must be in 1..%d, but got %d", dim, day); + "day must be in 1..%d, not %d", dim, day); return -1; } return 0; @@ -664,27 +664,27 @@ check_time_args(int h, int m, int s, int us, int fold) { if (h < 0 || h > 23) { PyErr_Format(PyExc_ValueError, - "hour must be in 0..23, but got %i", h); + "hour must be in 0..23, not %i", h); return -1; } if (m < 0 || m > 59) { PyErr_Format(PyExc_ValueError, - "minute must be in 0..59, but got %i", m); + "minute must be in 0..59, not %i", m); return -1; } if (s < 0 || s > 59) { PyErr_Format(PyExc_ValueError, - "second must be in 0..59, but got %i", s); + "second must be in 0..59, not %i", s); return -1; } if (us < 0 || us > 999999) { PyErr_Format(PyExc_ValueError, - "microsecond must be in 0..999999, but got %i", us); + "microsecond must be in 0..999999, not %i", us); return -1; } if (fold != 0 && fold != 1) { PyErr_Format(PyExc_ValueError, - "fold must be either 0 or 1, but got %i", fold); + "fold must be either 0 or 1, not %i", fold); return -1; } return 0; @@ -2270,7 +2270,7 @@ get_float_as_integer_ratio(PyObject *floatobj) if (!PyTuple_Check(ratio)) { PyErr_Format(PyExc_TypeError, "unexpected return type from as_integer_ratio(): " - "expected tuple, got '%.200s'", + "expected tuple, not '%.200s'", Py_TYPE(ratio)->tp_name); Py_DECREF(ratio); return NULL; @@ -3392,7 +3392,7 @@ date_fromisocalendar(PyObject *cls, PyObject *args, PyObject *kw) if (rv == -4) { PyErr_Format(PyExc_ValueError, - "year must be in %d..%d, but got %d", + "year must be in %d..%d, not %d", MINYEAR, MAXYEAR, year); return NULL; } @@ -5364,7 +5364,7 @@ utc_to_seconds(int year, int month, int day, /* ymd_to_ord() doesn't support year <= 0 */ if (year < MINYEAR || year > MAXYEAR) { PyErr_Format(PyExc_ValueError, - "year must be in %d..%d, but got %d", + "year must be in %d..%d, not %d", MINYEAR, MAXYEAR, year); return -1; } From 1da5a3aa3e7a090e4d33774f4d52b9b28ed7d1b9 Mon Sep 17 00:00:00 2001 From: donBarbos Date: Sun, 1 Dec 2024 09:49:25 +0400 Subject: [PATCH 29/30] Correct line break --- Lib/test/datetimetester.py | 3 +-- Modules/_datetimemodule.c | 27 +++++++++------------------ 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 8c2a54c6965a07..d1ef6339789d20 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -4537,8 +4537,7 @@ def utcoffset(self, t): def test_valuerror_messages(self): pattern = re.compile( - r"(hour|minute|second|microsecond) must " - r"be in \d+\.\.\d+, not \d+" + r"(hour|minute|second|microsecond) must be in \d+\.\.\d+, not \d+" ) test_cases = [ (12, 30, 90, 9999991), # Microsecond out of range diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 64d3631649b3c7..1ed691a4e4737c 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -638,8 +638,7 @@ check_date_args(int year, int month, int day) if (year < MINYEAR || year > MAXYEAR) { PyErr_Format(PyExc_ValueError, - "year must be in %d..%d, not %d", - MINYEAR, MAXYEAR, year); + "year must be in %d..%d, not %d", MINYEAR, MAXYEAR, year); return -1; } if (month < 1 || month > 12) { @@ -663,18 +662,15 @@ static int check_time_args(int h, int m, int s, int us, int fold) { if (h < 0 || h > 23) { - PyErr_Format(PyExc_ValueError, - "hour must be in 0..23, not %i", h); + PyErr_Format(PyExc_ValueError, "hour must be in 0..23, not %i", h); return -1; } if (m < 0 || m > 59) { - PyErr_Format(PyExc_ValueError, - "minute must be in 0..59, not %i", m); + PyErr_Format(PyExc_ValueError, "minute must be in 0..59, not %i", m); return -1; } if (s < 0 || s > 59) { - PyErr_Format(PyExc_ValueError, - "second must be in 0..59, not %i", s); + PyErr_Format(PyExc_ValueError, "second must be in 0..59, not %i", s); return -1; } if (us < 0 || us > 999999) { @@ -1438,8 +1434,7 @@ new_timezone(PyObject *offset, PyObject *name) GET_TD_DAYS(offset) < -1 || GET_TD_DAYS(offset) >= 1) { PyErr_Format(PyExc_ValueError, "offset must be a timedelta" " strictly between -timedelta(hours=24) and" - " timedelta(hours=24)," - " not %R", offset); + " timedelta(hours=24), not %R", offset); return NULL; } @@ -1510,8 +1505,7 @@ call_tzinfo_method(PyObject *tzinfo, const char *name, PyObject *tzinfoarg) GET_TD_DAYS(offset) < -1 || GET_TD_DAYS(offset) >= 1) { PyErr_Format(PyExc_ValueError, "offset must be a timedelta" " strictly between -timedelta(hours=24) and" - " timedelta(hours=24)," - " not %R", offset); + " timedelta(hours=24), not %R", offset); Py_DECREF(offset); return NULL; } @@ -3392,8 +3386,7 @@ date_fromisocalendar(PyObject *cls, PyObject *args, PyObject *kw) if (rv == -4) { PyErr_Format(PyExc_ValueError, - "year must be in %d..%d, not %d", - MINYEAR, MAXYEAR, year); + "year must be in %d..%d, not %d", MINYEAR, MAXYEAR, year); return NULL; } @@ -4389,8 +4382,7 @@ timezone_fromutc(PyDateTime_TimeZone *self, PyDateTime_DateTime *dt) return NULL; } if (!HASTZINFO(dt) || dt->tzinfo != (PyObject *)self) { - PyErr_SetString(PyExc_ValueError, "fromutc: dt.tzinfo " - "is not self"); + PyErr_SetString(PyExc_ValueError, "fromutc: dt.tzinfo is not self"); return NULL; } @@ -5364,8 +5356,7 @@ utc_to_seconds(int year, int month, int day, /* ymd_to_ord() doesn't support year <= 0 */ if (year < MINYEAR || year > MAXYEAR) { PyErr_Format(PyExc_ValueError, - "year must be in %d..%d, not %d", - MINYEAR, MAXYEAR, year); + "year must be in %d..%d, not %d", MINYEAR, MAXYEAR, year); return -1; } From 610f067d6b7dccc24ee023d9975e6817f933cc70 Mon Sep 17 00:00:00 2001 From: Paul Ganssle <1377457+pganssle@users.noreply.github.com> Date: Wed, 12 Feb 2025 09:26:17 -0500 Subject: [PATCH 30/30] Update 2024-11-27-23-29-05.gh-issue-109798.OPj1CT.rst --- .../Library/2024-11-27-23-29-05.gh-issue-109798.OPj1CT.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Misc/NEWS.d/next/Library/2024-11-27-23-29-05.gh-issue-109798.OPj1CT.rst b/Misc/NEWS.d/next/Library/2024-11-27-23-29-05.gh-issue-109798.OPj1CT.rst index fb6b0657b8c464..89b66d13d38df3 100644 --- a/Misc/NEWS.d/next/Library/2024-11-27-23-29-05.gh-issue-109798.OPj1CT.rst +++ b/Misc/NEWS.d/next/Library/2024-11-27-23-29-05.gh-issue-109798.OPj1CT.rst @@ -1,2 +1 @@ -Normalized error messages for :mod:`datetime`, and made -them more useful when debugging. Patch by Semyon Moroz. +Added additional information into error messages in :mod:`datetime`, and made the messages more consistent between the C and Python implementations. Patch by Semyon Moroz.