diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 271b37dfcded6c..a9395f22740288 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -26,14 +26,18 @@ #endif typedef struct { + /* Static types exposed by the datetime C-API. */ PyTypeObject *date_type; PyTypeObject *datetime_type; PyTypeObject *delta_type; - PyTypeObject *isocalendar_date_type; PyTypeObject *time_type; PyTypeObject *tzinfo_type; + /* Exposed indirectly via TimeZone_UTC. */ PyTypeObject *timezone_type; + /* Other module classes. */ + PyTypeObject *isocalendar_date_type; + /* Conversion factors. */ PyObject *us_per_ms; // 1_000 PyObject *us_per_second; // 1_000_000 @@ -57,22 +61,30 @@ static inline datetime_state* get_datetime_state(void) return &_datetime_global_state; } -#define PyDate_Check(op) PyObject_TypeCheck(op, get_datetime_state()->date_type) -#define PyDate_CheckExact(op) Py_IS_TYPE(op, get_datetime_state()->date_type) +static datetime_state * +get_module_state(PyObject *module) +{ + void *state = _PyModule_GetState(module); + assert(state != NULL); + return (datetime_state *)state; +} + +#define PyDate_Check(st, op) PyObject_TypeCheck(op, st->date_type) +#define PyDate_CheckExact(st, op) Py_IS_TYPE(op, st->date_type) -#define PyDateTime_Check(op) PyObject_TypeCheck(op, get_datetime_state()->datetime_type) -#define PyDateTime_CheckExact(op) Py_IS_TYPE(op, get_datetime_state()->datetime_type) +#define PyDateTime_Check(st, op) PyObject_TypeCheck(op, st->datetime_type) +#define PyDateTime_CheckExact(st, op) Py_IS_TYPE(op, st->datetime_type) -#define PyTime_Check(op) PyObject_TypeCheck(op, get_datetime_state()->time_type) -#define PyTime_CheckExact(op) Py_IS_TYPE(op, get_datetime_state()->time_type) +#define PyTime_Check(st, op) PyObject_TypeCheck(op, st->time_type) +#define PyTime_CheckExact(st, op) Py_IS_TYPE(op, st->time_type) -#define PyDelta_Check(op) PyObject_TypeCheck(op, get_datetime_state()->delta_type) -#define PyDelta_CheckExact(op) Py_IS_TYPE(op, get_datetime_state()->delta_type) +#define PyDelta_Check(st, op) PyObject_TypeCheck(op, st->delta_type) +#define PyDelta_CheckExact(st, op) Py_IS_TYPE(op, st->delta_type) -#define PyTZInfo_Check(op) PyObject_TypeCheck(op, get_datetime_state()->tzinfo_type) -#define PyTZInfo_CheckExact(op) Py_IS_TYPE(op, get_datetime_state()->tzinfo_type) +#define PyTZInfo_Check(st, op) PyObject_TypeCheck(op, st->tzinfo_type) +#define PyTZInfo_CheckExact(st, op) Py_IS_TYPE(op, st->tzinfo_type) -#define PyTimezone_Check(op) PyObject_TypeCheck(op, get_datetime_state()->timezone_type) +#define PyTimezone_Check(st, op) PyObject_TypeCheck(op, st->timezone_type) /* We require that C int be at least 32 bits, and use int virtually * everywhere. In just a few cases we use a temp long, where a Python @@ -153,7 +165,7 @@ static inline datetime_state* get_datetime_state(void) */ #define MONTH_IS_SANE(M) ((unsigned int)(M) - 1 < 12) -static int check_tzinfo_subclass(PyObject *p); +static int check_tzinfo_subclass(datetime_state *st, PyObject *p); /*[clinic input] module datetime @@ -1015,6 +1027,7 @@ new_datetime_ex2(int year, int month, int day, int hour, int minute, { PyDateTime_DateTime *self; char aware = tzinfo != Py_None; + datetime_state *st = get_datetime_state(); if (check_date_args(year, month, day) < 0) { return NULL; @@ -1022,7 +1035,7 @@ new_datetime_ex2(int year, int month, int day, int hour, int minute, if (check_time_args(hour, minute, second, usecond, fold) < 0) { return NULL; } - if (check_tzinfo_subclass(tzinfo) < 0) { + if (check_tzinfo_subclass(st, tzinfo) < 0) { return NULL; } @@ -1125,11 +1138,12 @@ new_time_ex2(int hour, int minute, int second, int usecond, { PyDateTime_Time *self; char aware = tzinfo != Py_None; + datetime_state *st = get_datetime_state(); if (check_time_args(hour, minute, second, usecond, fold) < 0) { return NULL; } - if (check_tzinfo_subclass(tzinfo) < 0) { + if (check_tzinfo_subclass(st, tzinfo) < 0) { return NULL; } @@ -1216,8 +1230,8 @@ new_delta_ex(int days, int seconds, int microseconds, int normalize, return (PyObject *) self; } -#define new_delta(d, s, us, normalize) \ - new_delta_ex(d, s, us, normalize, get_datetime_state()->delta_type) +#define new_delta(st, d, s, us, normalize) \ + new_delta_ex(d, s, us, normalize, st->delta_type) typedef struct @@ -1234,14 +1248,13 @@ static PyDateTime_TimeZone * look_up_timezone(PyObject *offset, PyObject *name); that offset is a timedelta instance and name is either NULL or a unicode object. */ static PyObject * -create_timezone(PyObject *offset, PyObject *name) +create_timezone(datetime_state *st, PyObject *offset, PyObject *name) { PyDateTime_TimeZone *self; - datetime_state *st = get_datetime_state(); PyTypeObject *type = st->timezone_type; assert(offset != NULL); - assert(PyDelta_Check(offset)); + assert(PyDelta_Check(st, offset)); assert(name == NULL || PyUnicode_Check(name)); self = look_up_timezone(offset, name); @@ -1262,14 +1275,13 @@ create_timezone(PyObject *offset, PyObject *name) static int delta_bool(PyDateTime_Delta *self); static PyObject * -new_timezone(PyObject *offset, PyObject *name) +new_timezone(datetime_state *st, PyObject *offset, PyObject *name) { assert(offset != NULL); - assert(PyDelta_Check(offset)); + assert(PyDelta_Check(st, offset)); assert(name == NULL || PyUnicode_Check(name)); if (name == NULL && delta_bool((PyDateTime_Delta *)offset) == 0) { - datetime_state *st = get_datetime_state(); return Py_NewRef(st->utc); } if ((GET_TD_DAYS(offset) == -1 && @@ -1283,7 +1295,7 @@ new_timezone(PyObject *offset, PyObject *name) return NULL; } - return create_timezone(offset, name); + return create_timezone(st, offset, name); } /* --------------------------------------------------------------------------- @@ -1294,9 +1306,9 @@ new_timezone(PyObject *offset, PyObject *name) * raise TypeError and return -1. */ static int -check_tzinfo_subclass(PyObject *p) +check_tzinfo_subclass(datetime_state *st, PyObject *p) { - if (p == Py_None || PyTZInfo_Check(p)) + if (p == Py_None || PyTZInfo_Check(st, p)) return 0; PyErr_Format(PyExc_TypeError, "tzinfo argument must be None or of a tzinfo subclass, " @@ -1313,10 +1325,11 @@ static PyObject * get_tzinfo_member(PyObject *self) { PyObject *tzinfo = NULL; + datetime_state *st = get_datetime_state(); - if (PyDateTime_Check(self) && HASTZINFO(self)) + if (PyDateTime_Check(st, self) && HASTZINFO(self)) tzinfo = ((PyDateTime_DateTime *)self)->tzinfo; - else if (PyTime_Check(self) && HASTZINFO(self)) + else if (PyTime_Check(st, self) && HASTZINFO(self)) tzinfo = ((PyDateTime_Time *)self)->tzinfo; return tzinfo; @@ -1333,9 +1346,10 @@ static PyObject * call_tzinfo_method(PyObject *tzinfo, const char *name, PyObject *tzinfoarg) { PyObject *offset; + datetime_state *st = get_datetime_state(); assert(tzinfo != NULL); - assert(PyTZInfo_Check(tzinfo) || tzinfo == Py_None); + assert(PyTZInfo_Check(st, tzinfo) || tzinfo == Py_None); assert(tzinfoarg != NULL); if (tzinfo == Py_None) @@ -1343,7 +1357,7 @@ call_tzinfo_method(PyObject *tzinfo, const char *name, PyObject *tzinfoarg) offset = PyObject_CallMethod(tzinfo, name, "O", tzinfoarg); if (offset == Py_None || offset == NULL) return offset; - if (PyDelta_Check(offset)) { + if (PyDelta_Check(st, offset)) { if ((GET_TD_DAYS(offset) == -1 && GET_TD_SECONDS(offset) == 0 && GET_TD_MICROSECONDS(offset) < 1) || @@ -1402,11 +1416,11 @@ call_dst(PyObject *tzinfo, PyObject *tzinfoarg) * string. */ static PyObject * -call_tzname(PyObject *tzinfo, PyObject *tzinfoarg) +call_tzname(datetime_state *st, PyObject *tzinfo, PyObject *tzinfoarg) { PyObject *result; assert(tzinfo != NULL); - assert(check_tzinfo_subclass(tzinfo) >= 0); + assert(check_tzinfo_subclass(st, tzinfo) >= 0); assert(tzinfoarg != NULL); if (tzinfo == Py_None) @@ -1481,17 +1495,18 @@ tzinfo_from_isoformat_results(int rv, int tzoffset, int tz_useconds) { PyObject *tzinfo; if (rv == 1) { + datetime_state *st = get_datetime_state(); + // Create a timezone from offset in seconds (0 returns UTC) if (tzoffset == 0) { - datetime_state *st = get_datetime_state(); return Py_NewRef(st->utc); } - PyObject *delta = new_delta(0, tzoffset, tz_useconds, 1); + PyObject *delta = new_delta(st, 0, tzoffset, tz_useconds, 1); if (delta == NULL) { return NULL; } - tzinfo = new_timezone(delta, NULL); + tzinfo = new_timezone(st, delta, NULL); Py_DECREF(delta); } else { @@ -1606,7 +1621,7 @@ make_somezreplacement(PyObject *object, char *sep, PyObject *tzinfoarg) } static PyObject * -make_Zreplacement(PyObject *object, PyObject *tzinfoarg) +make_Zreplacement(datetime_state *st, PyObject *object, PyObject *tzinfoarg) { PyObject *temp; PyObject *tzinfo = get_tzinfo_member(object); @@ -1618,7 +1633,7 @@ make_Zreplacement(PyObject *object, PyObject *tzinfoarg) return Zreplacement; assert(tzinfoarg != NULL); - temp = call_tzname(tzinfo, tzinfoarg); + temp = call_tzname(st, tzinfo, tzinfoarg); if (temp == NULL) goto Error; if (temp == Py_None) { @@ -1651,10 +1666,11 @@ make_Zreplacement(PyObject *object, PyObject *tzinfoarg) static PyObject * make_freplacement(PyObject *object) { + datetime_state *st = get_datetime_state(); char freplacement[64]; - if (PyTime_Check(object)) + if (PyTime_Check(st, object)) sprintf(freplacement, "%06d", TIME_GET_MICROSECOND(object)); - else if (PyDateTime_Check(object)) + else if (PyDateTime_Check(st, object)) sprintf(freplacement, "%06d", DATE_GET_MICROSECOND(object)); else sprintf(freplacement, "%06d", 0); @@ -1693,6 +1709,8 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple, const char *ptoappend; /* ptr to string to append to output buffer */ Py_ssize_t ntoappend; /* # of bytes to append to output buffer */ + datetime_state *st = get_datetime_state(); + assert(object && format && timetuple); assert(PyUnicode_Check(format)); /* Convert the input format to a C string and size */ @@ -1757,7 +1775,7 @@ wrap_strftime(PyObject *object, PyObject *format, PyObject *timetuple, else if (ch == 'Z') { /* format tzname */ if (Zreplacement == NULL) { - Zreplacement = make_Zreplacement(object, + Zreplacement = make_Zreplacement(st, object, tzinfoarg); if (Zreplacement == NULL) goto Done; @@ -2214,7 +2232,8 @@ delta_add(PyObject *left, PyObject *right) { PyObject *result = Py_NotImplemented; - if (PyDelta_Check(left) && PyDelta_Check(right)) { + datetime_state *st = get_datetime_state(); + if (PyDelta_Check(st, left) && PyDelta_Check(st, right)) { /* delta + delta */ /* The C-level additions can't overflow because of the * invariant bounds. @@ -2223,7 +2242,7 @@ delta_add(PyObject *left, PyObject *right) int seconds = GET_TD_SECONDS(left) + GET_TD_SECONDS(right); int microseconds = GET_TD_MICROSECONDS(left) + GET_TD_MICROSECONDS(right); - result = new_delta(days, seconds, microseconds, 1); + result = new_delta(st, days, seconds, microseconds, 1); } if (result == Py_NotImplemented) @@ -2234,7 +2253,9 @@ delta_add(PyObject *left, PyObject *right) static PyObject * delta_negative(PyDateTime_Delta *self) { - return new_delta(-GET_TD_DAYS(self), + datetime_state *st = get_datetime_state(); + return new_delta(st, + -GET_TD_DAYS(self), -GET_TD_SECONDS(self), -GET_TD_MICROSECONDS(self), 1); @@ -2243,10 +2264,12 @@ delta_negative(PyDateTime_Delta *self) static PyObject * delta_positive(PyDateTime_Delta *self) { + datetime_state *st = get_datetime_state(); /* Could optimize this (by returning self) if this isn't a * subclass -- but who uses unary + ? Approximately nobody. */ - return new_delta(GET_TD_DAYS(self), + return new_delta(st, + GET_TD_DAYS(self), GET_TD_SECONDS(self), GET_TD_MICROSECONDS(self), 0); @@ -2273,7 +2296,8 @@ delta_subtract(PyObject *left, PyObject *right) { PyObject *result = Py_NotImplemented; - if (PyDelta_Check(left) && PyDelta_Check(right)) { + datetime_state *st = get_datetime_state(); + if (PyDelta_Check(st, left) && PyDelta_Check(st, right)) { /* delta - delta */ /* The C-level additions can't overflow because of the * invariant bounds. @@ -2282,7 +2306,7 @@ delta_subtract(PyObject *left, PyObject *right) int seconds = GET_TD_SECONDS(left) - GET_TD_SECONDS(right); int microseconds = GET_TD_MICROSECONDS(left) - GET_TD_MICROSECONDS(right); - result = new_delta(days, seconds, microseconds, 1); + result = new_delta(st, days, seconds, microseconds, 1); } if (result == Py_NotImplemented) @@ -2306,7 +2330,8 @@ delta_cmp(PyObject *self, PyObject *other) static PyObject * delta_richcompare(PyObject *self, PyObject *other, int op) { - if (PyDelta_Check(other)) { + datetime_state *st = get_datetime_state(); + if (PyDelta_Check(st, other)) { int diff = delta_cmp(self, other); return diff_to_bool(diff, op); } @@ -2335,7 +2360,8 @@ delta_multiply(PyObject *left, PyObject *right) { PyObject *result = Py_NotImplemented; - if (PyDelta_Check(left)) { + datetime_state *st = get_datetime_state(); + if (PyDelta_Check(st, left)) { /* delta * ??? */ if (PyLong_Check(right)) result = multiply_int_timedelta(right, @@ -2361,13 +2387,14 @@ delta_divide(PyObject *left, PyObject *right) { PyObject *result = Py_NotImplemented; - if (PyDelta_Check(left)) { + datetime_state *st = get_datetime_state(); + if (PyDelta_Check(st, left)) { /* delta * ??? */ if (PyLong_Check(right)) result = divide_timedelta_int( (PyDateTime_Delta *)left, right); - else if (PyDelta_Check(right)) + else if (PyDelta_Check(st, right)) result = divide_timedelta_timedelta( (PyDateTime_Delta *)left, (PyDateTime_Delta *)right); @@ -2383,8 +2410,9 @@ delta_truedivide(PyObject *left, PyObject *right) { PyObject *result = Py_NotImplemented; - if (PyDelta_Check(left)) { - if (PyDelta_Check(right)) + datetime_state *st = get_datetime_state(); + if (PyDelta_Check(st, left)) { + if (PyDelta_Check(st, right)) result = truedivide_timedelta_timedelta( (PyDateTime_Delta *)left, (PyDateTime_Delta *)right); @@ -2409,7 +2437,8 @@ delta_remainder(PyObject *left, PyObject *right) PyObject *pyus_remainder; PyObject *remainder; - if (!PyDelta_Check(left) || !PyDelta_Check(right)) + datetime_state *st = get_datetime_state(); + if (!PyDelta_Check(st, left) || !PyDelta_Check(st, right)) Py_RETURN_NOTIMPLEMENTED; pyus_left = delta_to_microseconds((PyDateTime_Delta *)left); @@ -2445,7 +2474,8 @@ delta_divmod(PyObject *left, PyObject *right) PyObject *delta; PyObject *result; - if (!PyDelta_Check(left) || !PyDelta_Check(right)) + datetime_state *st = get_datetime_state(); + if (!PyDelta_Check(st, left) || !PyDelta_Check(st, right)) Py_RETURN_NOTIMPLEMENTED; pyus_left = delta_to_microseconds((PyDateTime_Delta *)left); @@ -3086,23 +3116,6 @@ datetime_date_fromtimestamp(PyTypeObject *type, PyObject *timestamp) return date_fromtimestamp((PyObject *) type, timestamp); } -/* bpo-36025: This is a wrapper for API compatibility with the public C API, - * which expects a function that takes an *args tuple, whereas the argument - * clinic generates code that takes METH_O. - */ -static PyObject * -datetime_date_fromtimestamp_capi(PyObject *cls, PyObject *args) -{ - PyObject *timestamp; - PyObject *result = NULL; - - if (PyArg_UnpackTuple(args, "fromtimestamp", 1, 1, ×tamp)) { - result = date_fromtimestamp(cls, timestamp); - } - - return result; -} - /* Return new date from proleptic Gregorian ordinal. Raises ValueError if * the ordinal is out of range. */ @@ -3237,12 +3250,13 @@ add_date_timedelta(PyDateTime_Date *date, PyDateTime_Delta *delta, int negate) static PyObject * date_add(PyObject *left, PyObject *right) { - if (PyDateTime_Check(left) || PyDateTime_Check(right)) + datetime_state *st = get_datetime_state(); + if (PyDateTime_Check(st, left) || PyDateTime_Check(st, right)) Py_RETURN_NOTIMPLEMENTED; - if (PyDate_Check(left)) { + if (PyDate_Check(st, left)) { /* date + ??? */ - if (PyDelta_Check(right)) + if (PyDelta_Check(st, right)) /* date + delta */ return add_date_timedelta((PyDateTime_Date *) left, (PyDateTime_Delta *) right, @@ -3252,7 +3266,7 @@ date_add(PyObject *left, PyObject *right) /* ??? + date * 'right' must be one of us, or we wouldn't have been called */ - if (PyDelta_Check(left)) + if (PyDelta_Check(st, left)) /* delta + date */ return add_date_timedelta((PyDateTime_Date *) right, (PyDateTime_Delta *) left, @@ -3264,11 +3278,12 @@ date_add(PyObject *left, PyObject *right) static PyObject * date_subtract(PyObject *left, PyObject *right) { - if (PyDateTime_Check(left) || PyDateTime_Check(right)) + datetime_state *st = get_datetime_state(); + if (PyDateTime_Check(st, left) || PyDateTime_Check(st, right)) Py_RETURN_NOTIMPLEMENTED; - if (PyDate_Check(left)) { - if (PyDate_Check(right)) { + if (PyDate_Check(st, left)) { + if (PyDate_Check(st, right)) { /* date - date */ int left_ord = ymd_to_ord(GET_YEAR(left), GET_MONTH(left), @@ -3276,9 +3291,9 @@ date_subtract(PyObject *left, PyObject *right) int right_ord = ymd_to_ord(GET_YEAR(right), GET_MONTH(right), GET_DAY(right)); - return new_delta(left_ord - right_ord, 0, 0, 0); + return new_delta(st, left_ord - right_ord, 0, 0, 0); } - if (PyDelta_Check(right)) { + if (PyDelta_Check(st, right)) { /* date - delta */ return add_date_timedelta((PyDateTime_Date *) left, (PyDateTime_Delta *) right, @@ -3457,17 +3472,40 @@ static PyMethodDef iso_calendar_date_methods[] = { {NULL, NULL}, }; -static PyTypeObject PyDateTime_IsoCalendarDateType = { - PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "datetime.IsoCalendarDate", - .tp_basicsize = sizeof(PyDateTime_IsoCalendarDate), - .tp_repr = (reprfunc) iso_calendar_date_repr, - .tp_flags = Py_TPFLAGS_DEFAULT, - .tp_doc = iso_calendar_date__doc__, - .tp_methods = iso_calendar_date_methods, - .tp_getset = iso_calendar_date_getset, - // .tp_base = &PyTuple_Type, // filled in PyInit__datetime - .tp_new = iso_calendar_date_new, +static int +iso_calendar_date_traverse(PyDateTime_IsoCalendarDate *self, visitproc visit, + void *arg) +{ + Py_VISIT(Py_TYPE(self)); + return PyTuple_Type.tp_traverse((PyObject *)self, visit, arg); +} + +static void +iso_calendar_date_dealloc(PyDateTime_IsoCalendarDate *self) +{ + PyTypeObject *tp = Py_TYPE(self); + PyTuple_Type.tp_dealloc((PyObject *)self); // delegate GC-untrack as well + Py_DECREF(tp); +} + +static PyType_Slot isocal_slots[] = { + {Py_tp_repr, iso_calendar_date_repr}, + {Py_tp_doc, (void *)iso_calendar_date__doc__}, + {Py_tp_methods, iso_calendar_date_methods}, + {Py_tp_getset, iso_calendar_date_getset}, + {Py_tp_new, iso_calendar_date_new}, + {Py_tp_dealloc, iso_calendar_date_dealloc}, + {Py_tp_traverse, iso_calendar_date_traverse}, + {0, NULL}, +}; + +static PyType_Spec isocal_spec = { + .name = "datetime.IsoCalendarDate", + .basicsize = sizeof(PyDateTime_IsoCalendarDate), + .flags = (Py_TPFLAGS_DEFAULT | + Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_IMMUTABLETYPE), + .slots = isocal_slots, }; /*[clinic input] @@ -3539,7 +3577,8 @@ date_richcompare(PyObject *self, PyObject *other, int op) * The behavior is the same as if Date and DateTime were independent * classes. */ - if (PyDate_Check(other) && !PyDateTime_Check(other)) { + datetime_state *st = get_datetime_state(); + if (PyDate_Check(st, other) && !PyDateTime_Check(st, other)) { int diff = memcmp(((PyDateTime_Date *)self)->data, ((PyDateTime_Date *)other)->data, _PyDateTime_DATE_DATASIZE); @@ -3815,7 +3854,8 @@ tzinfo_fromutc(PyDateTime_TZInfo *self, PyObject *dt) PyObject *off = NULL, *dst = NULL; PyDateTime_Delta *delta = NULL; - if (!PyDateTime_Check(dt)) { + datetime_state *st = get_datetime_state(); + if (!PyDateTime_Check(st, dt)) { PyErr_SetString(PyExc_TypeError, "fromutc: argument must be a datetime"); return NULL; @@ -3991,7 +4031,7 @@ timezone_new(PyTypeObject *type, PyObject *args, PyObject *kw) datetime_state *st = get_datetime_state(); if (PyArg_ParseTupleAndKeywords(args, kw, "O!|U:timezone", timezone_kws, st->delta_type, &offset, &name)) - return new_timezone(offset, name); + return new_timezone(st, offset, name); return NULL; } @@ -4010,7 +4050,8 @@ timezone_richcompare(PyDateTime_TimeZone *self, { if (op != Py_EQ && op != Py_NE) Py_RETURN_NOTIMPLEMENTED; - if (!PyTimezone_Check(other)) { + datetime_state *st = get_datetime_state(); + if (!PyTimezone_Check(st, other)) { Py_RETURN_NOTIMPLEMENTED; } return delta_richcompare(self->offset, other->offset, op); @@ -4029,7 +4070,8 @@ timezone_hash(PyDateTime_TimeZone *self) static int _timezone_check_argument(PyObject *dt, const char *meth) { - if (dt == Py_None || PyDateTime_Check(dt)) + datetime_state *st = get_datetime_state(); + if (dt == Py_None || PyDateTime_Check(st, dt)) return 0; PyErr_Format(PyExc_TypeError, "%s(dt) argument must be a datetime instance" " or None, not %.200s", meth, Py_TYPE(dt)->tp_name); @@ -4133,7 +4175,8 @@ timezone_dst(PyObject *self, PyObject *dt) static PyObject * timezone_fromutc(PyDateTime_TimeZone *self, PyDateTime_DateTime *dt) { - if (!PyDateTime_Check(dt)) { + datetime_state *st = get_datetime_state(); + if (!PyDateTime_Check(st, dt)) { PyErr_SetString(PyExc_TypeError, "fromutc: argument must be a datetime"); return NULL; @@ -4303,8 +4346,9 @@ time_from_pickle(PyTypeObject *type, PyObject *state, PyObject *tzinfo) { PyDateTime_Time *me; char aware = (char)(tzinfo != Py_None); + datetime_state *st = get_datetime_state(); - if (aware && check_tzinfo_subclass(tzinfo) < 0) { + if (aware && check_tzinfo_subclass(st, tzinfo) < 0) { PyErr_SetString(PyExc_TypeError, "bad tzinfo state arg"); return NULL; } @@ -4416,7 +4460,8 @@ time_dst(PyObject *self, PyObject *unused) { static PyObject * time_tzname(PyDateTime_Time *self, PyObject *unused) { - return call_tzname(GET_TIME_TZINFO(self), Py_None); + datetime_state *st = get_datetime_state(); + return call_tzname(st, GET_TIME_TZINFO(self), Py_None); } /* @@ -4561,8 +4606,9 @@ time_richcompare(PyObject *self, PyObject *other, int op) PyObject *result = NULL; PyObject *offset1, *offset2; int diff; + datetime_state *st = get_datetime_state(); - if (! PyTime_Check(other)) + if (! PyTime_Check(st, other)) Py_RETURN_NOTIMPLEMENTED; if (GET_TIME_TZINFO(self) == GET_TIME_TZINFO(other)) { @@ -4582,7 +4628,7 @@ time_richcompare(PyObject *self, PyObject *other, int op) * offset2 == Py_None at this point. */ if ((offset1 == offset2) || - (PyDelta_Check(offset1) && PyDelta_Check(offset2) && + (PyDelta_Check(st, offset1) && PyDelta_Check(st, offset2) && delta_cmp(offset1, offset2) == 0)) { diff = memcmp(((PyDateTime_Time *)self)->data, ((PyDateTime_Time *)other)->data, @@ -4662,7 +4708,8 @@ time_hash(PyDateTime_Time *self) TIME_GET_MINUTE(self) * 60 + TIME_GET_SECOND(self); microseconds = TIME_GET_MICROSECOND(self); - temp1 = new_delta(0, seconds, microseconds, 1); + datetime_state *st = get_datetime_state(); + temp1 = new_delta(st, 0, seconds, microseconds, 1); if (temp1 == NULL) { Py_DECREF(offset); return -1; @@ -4967,8 +5014,9 @@ datetime_from_pickle(PyTypeObject *type, PyObject *state, PyObject *tzinfo) { PyDateTime_DateTime *me; char aware = (char)(tzinfo != Py_None); + datetime_state *st = get_datetime_state(); - if (aware && check_tzinfo_subclass(tzinfo) < 0) { + if (aware && check_tzinfo_subclass(st, tzinfo) < 0) { PyErr_SetString(PyExc_TypeError, "bad tzinfo state arg"); return NULL; } @@ -5232,11 +5280,12 @@ datetime_datetime_now_impl(PyTypeObject *type, PyObject *tz) /*[clinic end generated code: output=b3386e5345e2b47a input=80d09869c5267d00]*/ { PyObject *self; + datetime_state *st = get_datetime_state(); /* Return best possible local time -- this isn't constrained by the * precision of a timestamp. */ - if (check_tzinfo_subclass(tz) < 0) + if (check_tzinfo_subclass(st, tz) < 0) return NULL; self = datetime_best_possible((PyObject *)type, @@ -5280,7 +5329,9 @@ datetime_fromtimestamp(PyObject *cls, PyObject *args, PyObject *kw) if (! PyArg_ParseTupleAndKeywords(args, kw, "O|O:fromtimestamp", keywords, ×tamp, &tzinfo)) return NULL; - if (check_tzinfo_subclass(tzinfo) < 0) + + datetime_state *st = get_datetime_state(); + if (check_tzinfo_subclass(st, tzinfo) < 0) return NULL; self = datetime_from_timestamp(cls, @@ -5651,7 +5702,8 @@ datetime_dst(PyObject *self, PyObject *unused) { static PyObject * datetime_tzname(PyObject *self, PyObject *unused) { - return call_tzname(GET_DT_TZINFO(self), self); + datetime_state *st = get_datetime_state(); + return call_tzname(st, GET_DT_TZINFO(self), self); } /* @@ -5692,16 +5744,17 @@ add_datetime_timedelta(PyDateTime_DateTime *date, PyDateTime_Delta *delta, static PyObject * datetime_add(PyObject *left, PyObject *right) { - if (PyDateTime_Check(left)) { + datetime_state *st = get_datetime_state(); + if (PyDateTime_Check(st, left)) { /* datetime + ??? */ - if (PyDelta_Check(right)) + if (PyDelta_Check(st, right)) /* datetime + delta */ return add_datetime_timedelta( (PyDateTime_DateTime *)left, (PyDateTime_Delta *)right, 1); } - else if (PyDelta_Check(left)) { + else if (PyDelta_Check(st, left)) { /* delta + datetime */ return add_datetime_timedelta((PyDateTime_DateTime *) right, (PyDateTime_Delta *) left, @@ -5715,9 +5768,10 @@ datetime_subtract(PyObject *left, PyObject *right) { PyObject *result = Py_NotImplemented; - if (PyDateTime_Check(left)) { + datetime_state *st = get_datetime_state(); + if (PyDateTime_Check(st, left)) { /* datetime - ??? */ - if (PyDateTime_Check(right)) { + if (PyDateTime_Check(st, right)) { /* datetime - datetime */ PyObject *offset1, *offset2, *offdiff = NULL; int delta_d, delta_s, delta_us; @@ -5773,7 +5827,7 @@ datetime_subtract(PyObject *left, PyObject *right) DATE_GET_SECOND(right)); delta_us = DATE_GET_MICROSECOND(left) - DATE_GET_MICROSECOND(right); - result = new_delta(delta_d, delta_s, delta_us, 1); + result = new_delta(st, delta_d, delta_s, delta_us, 1); if (result == NULL) return NULL; @@ -5782,7 +5836,7 @@ datetime_subtract(PyObject *left, PyObject *right) Py_DECREF(offdiff); } } - else if (PyDelta_Check(right)) { + else if (PyDelta_Check(st, right)) { /* datetime - delta */ result = add_datetime_timedelta( (PyDateTime_DateTime *)left, @@ -5996,8 +6050,9 @@ datetime_richcompare(PyObject *self, PyObject *other, int op) PyObject *result = NULL; PyObject *offset1, *offset2; int diff; + datetime_state *st = get_datetime_state(); - if (!PyDateTime_Check(other)) { + if (!PyDateTime_Check(st, other)) { Py_RETURN_NOTIMPLEMENTED; } @@ -6018,7 +6073,7 @@ datetime_richcompare(PyObject *self, PyObject *other, int op) * offset2 == Py_None at this point. */ if ((offset1 == offset2) || - (PyDelta_Check(offset1) && PyDelta_Check(offset2) && + (PyDelta_Check(st, offset1) && PyDelta_Check(st, offset2) && delta_cmp(offset1, offset2) == 0)) { diff = memcmp(((PyDateTime_DateTime *)self)->data, ((PyDateTime_DateTime *)other)->data, @@ -6105,6 +6160,7 @@ datetime_hash(PyDateTime_DateTime *self) else { PyObject *temp1, *temp2; int days, seconds; + datetime_state *st = get_datetime_state(); assert(HASTZINFO(self)); days = ymd_to_ord(GET_YEAR(self), @@ -6113,7 +6169,7 @@ datetime_hash(PyDateTime_DateTime *self) seconds = DATE_GET_HOUR(self) * 3600 + DATE_GET_MINUTE(self) * 60 + DATE_GET_SECOND(self); - temp1 = new_delta(days, seconds, + temp1 = new_delta(st, days, seconds, DATE_GET_MICROSECOND(self), 1); if (temp1 == NULL) { @@ -6171,15 +6227,15 @@ local_timezone_from_timestamp(time_t timestamp) struct tm local_time_tm; PyObject *nameo = NULL; const char *zone = NULL; + datetime_state *st = get_datetime_state(); if (_PyTime_localtime(timestamp, &local_time_tm) != 0) return NULL; #ifdef HAVE_STRUCT_TM_TM_ZONE zone = local_time_tm.tm_zone; - delta = new_delta(0, local_time_tm.tm_gmtoff, 0, 1); + delta = new_delta(st, 0, local_time_tm.tm_gmtoff, 0, 1); #else /* HAVE_STRUCT_TM_TM_ZONE */ { - datetime_state *st = get_datetime_state(); PyObject *local_time, *utc_time; struct tm utc_time_tm; char buf[100]; @@ -6219,7 +6275,7 @@ local_timezone_from_timestamp(time_t timestamp) if (nameo == NULL) goto error; } - result = new_timezone(delta, nameo); + result = new_timezone(st, delta, nameo); Py_XDECREF(nameo); error: Py_DECREF(delta); @@ -6239,7 +6295,7 @@ local_timezone(PyDateTime_DateTime *utc_time) if (delta == NULL) return NULL; - one_second = new_delta(0, 1, 0, 0); + one_second = new_delta(st, 0, 1, 0, 0); if (one_second == NULL) { Py_DECREF(delta); return NULL; @@ -6308,7 +6364,8 @@ datetime_astimezone(PyDateTime_DateTime *self, PyObject *args, PyObject *kw) &tzinfo)) return NULL; - if (check_tzinfo_subclass(tzinfo) == -1) + datetime_state *st = get_datetime_state(); + if (check_tzinfo_subclass(st, tzinfo) == -1) return NULL; if (!HASTZINFO(self) || self->tzinfo == Py_None) { @@ -6335,7 +6392,7 @@ datetime_astimezone(PyDateTime_DateTime *self, PyObject *args, PyObject *kw) Py_DECREF(offset); goto naive; } - else if (!PyDelta_Check(offset)) { + else if (!PyDelta_Check(st, offset)) { Py_DECREF(offset); PyErr_Format(PyExc_TypeError, "utcoffset() returned %.200s," " expected timedelta or None", Py_TYPE(offset)->tp_name); @@ -6348,7 +6405,6 @@ datetime_astimezone(PyDateTime_DateTime *self, PyObject *args, PyObject *kw) if (result == NULL) return NULL; - datetime_state *st = get_datetime_state(); /* Make sure result is aware and UTC. */ if (!HASTZINFO(result)) { temp = (PyObject *)result; @@ -6764,13 +6820,83 @@ static PyTypeObject PyDateTime_DateTimeType = { }; /* --------------------------------------------------------------------------- - * Module methods and initialization. + * datetime C-API implementation. */ -static PyMethodDef module_methods[] = { - {NULL, NULL} -}; +static PyObject * +capi_date_from_date(int year, int month, int day, PyTypeObject *type) +{ + return new_date_ex(year, month, day, type); +} + +static PyObject * +capi_datetime_from_date_and_time( + int year, int month, int day, int hour, int minute, int second, + int usecond, PyObject *tzinfo, PyTypeObject *type) +{ + return new_datetime_ex( + year, month, day, hour, minute, second, usecond, tzinfo, type); +} + +static PyObject * +capi_datetime_from_date_and_time_and_fold( + int year, int month, int day, int hour, int minute, int second, + int usecond, PyObject *tzinfo, int fold, PyTypeObject *type) +{ + return new_datetime_ex2( + year, month, day, hour, minute, second, usecond, tzinfo, fold, type); +} + +static PyObject * +capi_time_from_time(int hour, int minute, int second, int usecond, + PyObject *tzinfo, PyTypeObject *type) +{ + return new_time_ex(hour, minute, second, usecond, tzinfo, type); +} + +static PyObject * +capi_time_from_time_and_fold(int hour, int minute, int second, int usecond, + PyObject *tzinfo, int fold, PyTypeObject *type) +{ + return new_time_ex2(hour, minute, second, usecond, tzinfo, fold, type); +} + +static PyObject * +capi_delta_from_delta(int days, int seconds, int microseconds, + int normalize, PyTypeObject *type) +{ + return new_delta_ex(days, seconds, microseconds, normalize, type); +} + +static PyObject * +capi_timezone_from_timezone(PyObject *offset, PyObject *name) +{ + datetime_state *st = get_datetime_state(); + return new_timezone(st, offset, name); +} +static PyObject * +capi_datetime_from_timestamp(PyObject *cls, PyObject *args, PyObject *kw) +{ + return datetime_fromtimestamp(cls, args, kw); +} + +/* bpo-36025: This is a wrapper for API compatibility with the public C API, + * which expects a function that takes an *args tuple, whereas the argument + * clinic generates code that takes METH_O. + */ +static PyObject * +capi_date_from_timestamp(PyObject *cls, PyObject *args) +{ + PyObject *timestamp; + PyObject *result = NULL; + + if (PyArg_UnpackTuple(args, "fromtimestamp", 1, 1, ×tamp)) { + result = date_fromtimestamp(cls, timestamp); + } + + return result; +} /* The C-API is process-global. This violates interpreter isolation * due to the objects stored here. Thus each of those objects must @@ -6789,15 +6915,15 @@ static PyDateTime_CAPI capi = { .TimeZone_UTC = (PyObject *)&utc_timezone, - .Date_FromDate = new_date_ex, - .DateTime_FromDateAndTime = new_datetime_ex, - .Time_FromTime = new_time_ex, - .Delta_FromDelta = new_delta_ex, - .TimeZone_FromTimeZone = new_timezone, - .DateTime_FromTimestamp = datetime_fromtimestamp, - .Date_FromTimestamp = datetime_date_fromtimestamp_capi, - .DateTime_FromDateAndTimeAndFold = new_datetime_ex2, - .Time_FromTimeAndFold = new_time_ex2, + .Date_FromDate = capi_date_from_date, + .DateTime_FromDateAndTime = capi_datetime_from_date_and_time, + .Time_FromTime = capi_time_from_time, + .Delta_FromDelta = capi_delta_from_delta, + .TimeZone_FromTimeZone = capi_timezone_from_timezone, + .DateTime_FromTimestamp = capi_datetime_from_timestamp, + .Date_FromTimestamp = capi_date_from_timestamp, + .DateTime_FromDateAndTimeAndFold = capi_datetime_from_date_and_time_and_fold, + .Time_FromTimeAndFold = capi_time_from_time_and_fold, }; /* Get a new C API by calling this function. @@ -6809,6 +6935,16 @@ get_datetime_capi(void) return &capi; } + +/* --------------------------------------------------------------------------- + * Module methods and initialization. + */ + +static PyMethodDef module_methods[] = { + {NULL, NULL} +}; + + static int datetime_clear(PyObject *module) { @@ -6827,28 +6963,32 @@ datetime_clear(PyObject *module) } static PyObject * -create_timezone_from_delta(int days, int sec, int ms, int normalize) +create_timezone_from_delta(datetime_state *st, int days, int sec, int ms, + int normalize) { - PyObject *delta = new_delta(days, sec, ms, normalize); + PyObject *delta = new_delta(st, days, sec, ms, normalize); if (delta == NULL) { return NULL; } - PyObject *tz = create_timezone(delta, NULL); + PyObject *tz = create_timezone(st, delta, NULL); Py_DECREF(delta); return tz; } static int -init_state(datetime_state *st) +init_state(datetime_state *st, PyTypeObject *PyDateTime_IsoCalendarDateType) { + /* Static types exposed by the C-API. */ st->date_type = &PyDateTime_DateType; st->datetime_type = &PyDateTime_DateTimeType; st->delta_type = &PyDateTime_DeltaType; - st->isocalendar_date_type = &PyDateTime_IsoCalendarDateType; st->time_type = &PyDateTime_TimeType; st->tzinfo_type = &PyDateTime_TZInfoType; st->timezone_type = &PyDateTime_TimeZoneType; + /* Per-module heap types. */ + st->isocalendar_date_type = PyDateTime_IsoCalendarDateType; + st->us_per_ms = PyLong_FromLong(1000); if (st->us_per_ms == NULL) { return -1; @@ -6883,7 +7023,7 @@ init_state(datetime_state *st) } /* Init UTC timezone */ - st->utc = create_timezone_from_delta(0, 0, 0, 0); + st->utc = create_timezone_from_delta(st, 0, 0, 0, 0); if (st->utc == NULL) { return -1; } @@ -6902,11 +7042,10 @@ _datetime_exec(PyObject *module) // `&...` is not a constant expression according to a strict reading // of C standards. Fill tp_base at run-time rather than statically. // See https://bugs.python.org/issue40777 - PyDateTime_IsoCalendarDateType.tp_base = &PyTuple_Type; PyDateTime_TimeZoneType.tp_base = &PyDateTime_TZInfoType; PyDateTime_DateTimeType.tp_base = &PyDateTime_DateType; - PyTypeObject *types[] = { + PyTypeObject *capi_types[] = { &PyDateTime_DateType, &PyDateTime_DateTimeType, &PyDateTime_TimeType, @@ -6915,18 +7054,32 @@ _datetime_exec(PyObject *module) &PyDateTime_TimeZoneType, }; - for (size_t i = 0; i < Py_ARRAY_LENGTH(types); i++) { - if (PyModule_AddType(module, types[i]) < 0) { + for (size_t i = 0; i < Py_ARRAY_LENGTH(capi_types); i++) { + if (PyModule_AddType(module, capi_types[i]) < 0) { goto error; } } - if (PyType_Ready(&PyDateTime_IsoCalendarDateType) < 0) { +#define CREATE_TYPE(VAR, SPEC, BASE) \ + do { \ + VAR = (PyTypeObject *)PyType_FromModuleAndSpec( \ + module, SPEC, (PyObject *)BASE); \ + if (VAR == NULL) { \ + goto error; \ + } \ + } while (0) + + PyTypeObject *PyDateTime_IsoCalendarDateType = NULL; + CREATE_TYPE(PyDateTime_IsoCalendarDateType, &isocal_spec, &PyTuple_Type); +#undef CREATE_TYPE + + datetime_state *st = get_datetime_state(); + if (init_state(st, PyDateTime_IsoCalendarDateType) < 0) { goto error; } - datetime_state *st = get_datetime_state(); - if (init_state(st) < 0) { + st = get_module_state(module); + if (init_state(st, PyDateTime_IsoCalendarDateType) < 0) { goto error; } @@ -6945,22 +7098,22 @@ _datetime_exec(PyObject *module) /* timedelta values */ PyObject *d = st->delta_type->tp_dict; - DATETIME_ADD_MACRO(d, "resolution", new_delta(0, 0, 1, 0)); - DATETIME_ADD_MACRO(d, "min", new_delta(-MAX_DELTA_DAYS, 0, 0, 0)); + DATETIME_ADD_MACRO(d, "resolution", new_delta(st, 0, 0, 1, 0)); + DATETIME_ADD_MACRO(d, "min", new_delta(st, -MAX_DELTA_DAYS, 0, 0, 0)); DATETIME_ADD_MACRO(d, "max", - new_delta(MAX_DELTA_DAYS, 24*3600-1, 1000000-1, 0)); + new_delta(st, MAX_DELTA_DAYS, 24*3600-1, 1000000-1, 0)); /* date values */ d = st->date_type->tp_dict; DATETIME_ADD_MACRO(d, "min", new_date(1, 1, 1)); DATETIME_ADD_MACRO(d, "max", new_date(MAXYEAR, 12, 31)); - DATETIME_ADD_MACRO(d, "resolution", new_delta(1, 0, 0, 0)); + DATETIME_ADD_MACRO(d, "resolution", new_delta(st, 1, 0, 0, 0)); /* time values */ d = st->time_type->tp_dict; DATETIME_ADD_MACRO(d, "min", new_time(0, 0, 0, 0, Py_None, 0)); DATETIME_ADD_MACRO(d, "max", new_time(23, 59, 59, 999999, Py_None, 0)); - DATETIME_ADD_MACRO(d, "resolution", new_delta(0, 0, 1, 0)); + DATETIME_ADD_MACRO(d, "resolution", new_delta(st, 0, 0, 1, 0)); /* datetime values */ d = st->datetime_type->tp_dict; @@ -6968,7 +7121,7 @@ _datetime_exec(PyObject *module) new_datetime(1, 1, 1, 0, 0, 0, 0, Py_None, 0)); DATETIME_ADD_MACRO(d, "max", new_datetime(MAXYEAR, 12, 31, 23, 59, 59, 999999, Py_None, 0)); - DATETIME_ADD_MACRO(d, "resolution", new_delta(0, 0, 1, 0)); + DATETIME_ADD_MACRO(d, "resolution", new_delta(st, 0, 0, 1, 0)); /* timezone values */ d = st->timezone_type->tp_dict; @@ -6981,11 +7134,11 @@ _datetime_exec(PyObject *module) * values. This may change in the future.*/ /* -23:59 */ - PyObject *min = create_timezone_from_delta(-1, 60, 0, 1); + PyObject *min = create_timezone_from_delta(st, -1, 60, 0, 1); DATETIME_ADD_MACRO(d, "min", min); /* +23:59 */ - PyObject *max = create_timezone_from_delta(0, (23 * 60 + 59) * 60, 0, 0); + PyObject *max = create_timezone_from_delta(st, 0, (23 * 60 + 59) * 60, 0, 0); DATETIME_ADD_MACRO(d, "max", max); /* Add module level attributes */ @@ -7051,7 +7204,7 @@ static PyModuleDef datetimemodule = { .m_base = PyModuleDef_HEAD_INIT, .m_name = "_datetime", .m_doc = "Fast implementation of the datetime type.", - .m_size = 0, + .m_size = sizeof(datetime_state), .m_methods = module_methods, .m_slots = module_slots, };