gh-136571: Convert more code in datetime to Argument Clinic (GH-136573)

This adds signatures for some classes and methods.

date.fromisocalendar() can now raise OverflowError for arguments that
don't fit in the C int.
This commit is contained in:
Serhiy Storchaka 2025-08-09 21:25:49 +03:00 committed by GitHub
parent af15e1d13e
commit 046a4e39b3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 2242 additions and 495 deletions

View file

@ -2383,7 +2383,7 @@ locations where different offsets are used in different days of the year or
where historical changes have been made to civil time.
.. class:: timezone(offset, name=None)
.. class:: timezone(offset[, name])
The *offset* argument must be specified as a :class:`timedelta`
object representing the difference between the local time and UTC. It must

View file

@ -891,7 +891,9 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(d_parameter_type));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(data));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(database));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(date));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(day));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(days));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(debug));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(decode));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(decoder));
@ -998,6 +1000,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(hi));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(hook));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(hour));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(hours));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(id));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(ident));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(identity_hint));
@ -1092,8 +1095,10 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(metadata));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(method));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(microsecond));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(microseconds));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(milliseconds));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(minute));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(minutes));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(mod));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(mode));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(module));
@ -1204,6 +1209,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(scheduler));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(script));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(second));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(seconds));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(security_attributes));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(seek));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(seekable));
@ -1267,9 +1273,12 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(text));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(threading));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(throw));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(time));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(timeout));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(timer));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(times));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(timespec));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(timestamp));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(timetuple));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(timeunit));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(top));
@ -1301,6 +1310,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(wbits));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(week));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(weekday));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(weeks));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(which));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(who));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(withdata));

View file

@ -382,7 +382,9 @@ struct _Py_global_strings {
STRUCT_FOR_ID(d_parameter_type)
STRUCT_FOR_ID(data)
STRUCT_FOR_ID(database)
STRUCT_FOR_ID(date)
STRUCT_FOR_ID(day)
STRUCT_FOR_ID(days)
STRUCT_FOR_ID(debug)
STRUCT_FOR_ID(decode)
STRUCT_FOR_ID(decoder)
@ -489,6 +491,7 @@ struct _Py_global_strings {
STRUCT_FOR_ID(hi)
STRUCT_FOR_ID(hook)
STRUCT_FOR_ID(hour)
STRUCT_FOR_ID(hours)
STRUCT_FOR_ID(id)
STRUCT_FOR_ID(ident)
STRUCT_FOR_ID(identity_hint)
@ -583,8 +586,10 @@ struct _Py_global_strings {
STRUCT_FOR_ID(metadata)
STRUCT_FOR_ID(method)
STRUCT_FOR_ID(microsecond)
STRUCT_FOR_ID(microseconds)
STRUCT_FOR_ID(milliseconds)
STRUCT_FOR_ID(minute)
STRUCT_FOR_ID(minutes)
STRUCT_FOR_ID(mod)
STRUCT_FOR_ID(mode)
STRUCT_FOR_ID(module)
@ -695,6 +700,7 @@ struct _Py_global_strings {
STRUCT_FOR_ID(scheduler)
STRUCT_FOR_ID(script)
STRUCT_FOR_ID(second)
STRUCT_FOR_ID(seconds)
STRUCT_FOR_ID(security_attributes)
STRUCT_FOR_ID(seek)
STRUCT_FOR_ID(seekable)
@ -758,9 +764,12 @@ struct _Py_global_strings {
STRUCT_FOR_ID(text)
STRUCT_FOR_ID(threading)
STRUCT_FOR_ID(throw)
STRUCT_FOR_ID(time)
STRUCT_FOR_ID(timeout)
STRUCT_FOR_ID(timer)
STRUCT_FOR_ID(times)
STRUCT_FOR_ID(timespec)
STRUCT_FOR_ID(timestamp)
STRUCT_FOR_ID(timetuple)
STRUCT_FOR_ID(timeunit)
STRUCT_FOR_ID(top)
@ -792,6 +801,7 @@ struct _Py_global_strings {
STRUCT_FOR_ID(wbits)
STRUCT_FOR_ID(week)
STRUCT_FOR_ID(weekday)
STRUCT_FOR_ID(weeks)
STRUCT_FOR_ID(which)
STRUCT_FOR_ID(who)
STRUCT_FOR_ID(withdata)

View file

@ -889,7 +889,9 @@ extern "C" {
INIT_ID(d_parameter_type), \
INIT_ID(data), \
INIT_ID(database), \
INIT_ID(date), \
INIT_ID(day), \
INIT_ID(days), \
INIT_ID(debug), \
INIT_ID(decode), \
INIT_ID(decoder), \
@ -996,6 +998,7 @@ extern "C" {
INIT_ID(hi), \
INIT_ID(hook), \
INIT_ID(hour), \
INIT_ID(hours), \
INIT_ID(id), \
INIT_ID(ident), \
INIT_ID(identity_hint), \
@ -1090,8 +1093,10 @@ extern "C" {
INIT_ID(metadata), \
INIT_ID(method), \
INIT_ID(microsecond), \
INIT_ID(microseconds), \
INIT_ID(milliseconds), \
INIT_ID(minute), \
INIT_ID(minutes), \
INIT_ID(mod), \
INIT_ID(mode), \
INIT_ID(module), \
@ -1202,6 +1207,7 @@ extern "C" {
INIT_ID(scheduler), \
INIT_ID(script), \
INIT_ID(second), \
INIT_ID(seconds), \
INIT_ID(security_attributes), \
INIT_ID(seek), \
INIT_ID(seekable), \
@ -1265,9 +1271,12 @@ extern "C" {
INIT_ID(text), \
INIT_ID(threading), \
INIT_ID(throw), \
INIT_ID(time), \
INIT_ID(timeout), \
INIT_ID(timer), \
INIT_ID(times), \
INIT_ID(timespec), \
INIT_ID(timestamp), \
INIT_ID(timetuple), \
INIT_ID(timeunit), \
INIT_ID(top), \
@ -1299,6 +1308,7 @@ extern "C" {
INIT_ID(wbits), \
INIT_ID(week), \
INIT_ID(weekday), \
INIT_ID(weeks), \
INIT_ID(which), \
INIT_ID(who), \
INIT_ID(withdata), \

View file

@ -1316,10 +1316,18 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
_PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1));
assert(PyUnicode_GET_LENGTH(string) != 1);
string = &_Py_ID(date);
_PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1));
assert(PyUnicode_GET_LENGTH(string) != 1);
string = &_Py_ID(day);
_PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1));
assert(PyUnicode_GET_LENGTH(string) != 1);
string = &_Py_ID(days);
_PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1));
assert(PyUnicode_GET_LENGTH(string) != 1);
string = &_Py_ID(debug);
_PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1));
@ -1744,6 +1752,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
_PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1));
assert(PyUnicode_GET_LENGTH(string) != 1);
string = &_Py_ID(hours);
_PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1));
assert(PyUnicode_GET_LENGTH(string) != 1);
string = &_Py_ID(id);
_PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1));
@ -2120,6 +2132,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
_PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1));
assert(PyUnicode_GET_LENGTH(string) != 1);
string = &_Py_ID(microseconds);
_PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1));
assert(PyUnicode_GET_LENGTH(string) != 1);
string = &_Py_ID(milliseconds);
_PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1));
@ -2128,6 +2144,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
_PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1));
assert(PyUnicode_GET_LENGTH(string) != 1);
string = &_Py_ID(minutes);
_PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1));
assert(PyUnicode_GET_LENGTH(string) != 1);
string = &_Py_ID(mod);
_PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1));
@ -2568,6 +2588,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
_PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1));
assert(PyUnicode_GET_LENGTH(string) != 1);
string = &_Py_ID(seconds);
_PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1));
assert(PyUnicode_GET_LENGTH(string) != 1);
string = &_Py_ID(security_attributes);
_PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1));
@ -2820,6 +2844,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
_PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1));
assert(PyUnicode_GET_LENGTH(string) != 1);
string = &_Py_ID(time);
_PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1));
assert(PyUnicode_GET_LENGTH(string) != 1);
string = &_Py_ID(timeout);
_PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1));
@ -2832,6 +2860,14 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
_PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1));
assert(PyUnicode_GET_LENGTH(string) != 1);
string = &_Py_ID(timespec);
_PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1));
assert(PyUnicode_GET_LENGTH(string) != 1);
string = &_Py_ID(timestamp);
_PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1));
assert(PyUnicode_GET_LENGTH(string) != 1);
string = &_Py_ID(timetuple);
_PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1));
@ -2956,6 +2992,10 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
_PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1));
assert(PyUnicode_GET_LENGTH(string) != 1);
string = &_Py_ID(weeks);
_PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1));
assert(PyUnicode_GET_LENGTH(string) != 1);
string = &_Py_ID(which);
_PyUnicode_InternStatic(interp, &string);
assert(_PyUnicode_CheckConsistency(string, 1));

View file

@ -1083,7 +1083,7 @@ def fromisocalendar(cls, year, week, day):
@classmethod
def strptime(cls, date_string, format):
"""Parse a date string according to the given format (like time.strptime())."""
"""Parse string according to the given date format (like time.strptime())."""
import _strptime
return _strptime._strptime_datetime_date(cls, date_string, format)
@ -1310,7 +1310,7 @@ def __reduce__(self):
class tzinfo:
"""Abstract base class for time zone info classes.
"""Abstract base class for time zone info objects.
Subclasses must override the tzname(), utcoffset() and dst() methods.
"""
@ -1468,7 +1468,7 @@ def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None, *, fold
@classmethod
def strptime(cls, date_string, format):
"""string, format -> new time parsed from a string (like time.strptime())."""
"""Parse string according to the given time format (like time.strptime())."""
import _strptime
return _strptime._strptime_datetime_time(cls, date_string, format)
@ -1776,7 +1776,7 @@ def __reduce__(self):
class datetime(date):
"""datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]])
"""A combination of a date and a time.
The year, month and day arguments are required. tzinfo may be None, or an
instance of a tzinfo subclass. The remaining arguments may be ints.
@ -2209,7 +2209,7 @@ def __str__(self):
@classmethod
def strptime(cls, date_string, format):
'string, format -> new datetime parsed from a string (like time.strptime()).'
"""Parse string according to the given date and time format (like time.strptime())."""
import _strptime
return _strptime._strptime_datetime_datetime(cls, date_string, format)
@ -2435,6 +2435,8 @@ def _isoweek1monday(year):
class timezone(tzinfo):
"""Fixed offset from UTC implementation of tzinfo."""
__slots__ = '_offset', '_name'
# Sentinel value to disallow None

View file

@ -2147,14 +2147,20 @@ def test_fromisocalendar_value_errors(self):
(10000, 1, 1),
(0, 1, 1),
(9999999, 1, 1),
]
for isocal in isocals:
with self.subTest(isocal=isocal):
with self.assertRaises(ValueError):
self.theclass.fromisocalendar(*isocal)
isocals = [
(2<<32, 1, 1),
(2019, 2<<32, 1),
(2019, 1, 2<<32),
]
for isocal in isocals:
with self.subTest(isocal=isocal):
with self.assertRaises(ValueError):
with self.assertRaises((ValueError, OverflowError)):
self.theclass.fromisocalendar(*isocal)
def test_fromisocalendar_type_errors(self):
@ -2301,7 +2307,7 @@ def test_isoformat_timezone(self):
dt = dt_base.replace(tzinfo=tzi)
exp = exp_base + exp_tz
with self.subTest(tzi=tzi):
assert dt.isoformat() == exp
self.assertEqual(dt.isoformat(), exp)
def test_format(self):
dt = self.theclass(2007, 9, 10, 4, 5, 1, 123)
@ -3349,7 +3355,7 @@ def test_fromisoformat_timezone(self):
with self.subTest(tstr=dtstr):
dt_rt = self.theclass.fromisoformat(dtstr)
assert dt == dt_rt, dt_rt
self.assertEqual(dt_rt, dt)
def test_fromisoformat_separators(self):
separators = [
@ -3865,7 +3871,7 @@ def test_isoformat_timezone(self):
t = t_base.replace(tzinfo=tzi)
exp = exp_base + exp_tz
with self.subTest(tzi=tzi):
assert t.isoformat() == exp
self.assertEqual(t.isoformat(), exp)
def test_1653736(self):
# verify it doesn't accept extra keyword arguments
@ -4350,7 +4356,7 @@ def utcoffset(self, t):
elif x is d2:
expected = -1
else:
assert y is d2
self.assertIs(y, d2)
expected = 1
self.assertEqual(got, expected)
@ -4678,7 +4684,7 @@ def test_fromisoformat_timezone(self):
with self.subTest(tstr=tstr):
t_rt = self.theclass.fromisoformat(tstr)
assert t == t_rt
self.assertEqual(t_rt, t)
def test_fromisoformat_timespecs(self):
time_bases = [
@ -5515,7 +5521,7 @@ def utcoffset(self, t):
elif x is d2:
expected = timedelta(minutes=(11-59)-0)
else:
assert y is d2
self.assertIs(y, d2)
expected = timedelta(minutes=0-(11-59))
self.assertEqual(got, expected)

View file

@ -5831,6 +5831,21 @@ def test_collections_abc_module_has_signatures(self):
import collections.abc
self._test_module_has_signatures(collections.abc)
def test_datetime_module_has_signatures(self):
# Only test if the C implementation is available.
import_helper.import_module('_datetime')
import datetime
no_signature = {'tzinfo'}
unsupported_signature = {'timezone'}
methods_unsupported_signature = {
'date': {'replace'},
'time': {'replace'},
'datetime': {'replace', 'combine'},
}
self._test_module_has_signatures(datetime,
no_signature, unsupported_signature,
methods_unsupported_signature=methods_unsupported_signature)
def test_errno_module_has_signatures(self):
import errno
self._test_module_has_signatures(errno)

View file

@ -0,0 +1,2 @@
:meth:`datetime.date.fromisocalendar` can now raise OverflowError for out of
range arguments.

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff