From c82d32aacf8665ecafb9c78aa998de38b0fd0397 Mon Sep 17 00:00:00 2001 From: Costas Date: Tue, 3 Oct 2023 11:19:12 +0100 Subject: [PATCH 1/8] fix: make datetime mostly subinterpreter friendly --- Modules/_datetimemodule.c | 879 ++++++++++++++++++++------------------ 1 file changed, 452 insertions(+), 427 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index d8183c63f44976..44078ac59bddb8 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -23,22 +23,22 @@ # include /* struct timeval */ #endif -#define PyDate_Check(op) PyObject_TypeCheck(op, &PyDateTime_DateType) -#define PyDate_CheckExact(op) Py_IS_TYPE(op, &PyDateTime_DateType) +#define PyDate_Check(op) PyObject_TypeCheck(op, mstate->DateType) +#define PyDate_CheckExact(op) Py_IS_TYPE(op, mstate->DateType) -#define PyDateTime_Check(op) PyObject_TypeCheck(op, &PyDateTime_DateTimeType) -#define PyDateTime_CheckExact(op) Py_IS_TYPE(op, &PyDateTime_DateTimeType) +#define PyDateTime_Check(op) PyObject_TypeCheck(op, mstate->DateTimeType) +#define PyDateTime_CheckExact(op) Py_IS_TYPE(op, mstate->DateTimeType) -#define PyTime_Check(op) PyObject_TypeCheck(op, &PyDateTime_TimeType) -#define PyTime_CheckExact(op) Py_IS_TYPE(op, &PyDateTime_TimeType) +#define PyTime_Check(op) PyObject_TypeCheck(op, mstate->TimeType) +#define PyTime_CheckExact(op) Py_IS_TYPE(op, mstate->TimeType) -#define PyDelta_Check(op) PyObject_TypeCheck(op, &PyDateTime_DeltaType) -#define PyDelta_CheckExact(op) Py_IS_TYPE(op, &PyDateTime_DeltaType) +#define PyDelta_Check(op) PyObject_TypeCheck(op, mstate->DeltaType) +#define PyDelta_CheckExact(op) Py_IS_TYPE(op, mstate->DeltaType) -#define PyTZInfo_Check(op) PyObject_TypeCheck(op, &PyDateTime_TZInfoType) -#define PyTZInfo_CheckExact(op) Py_IS_TYPE(op, &PyDateTime_TZInfoType) +#define PyTZInfo_Check(op) PyObject_TypeCheck(op, mstate->TZInfoType) +#define PyTZInfo_CheckExact(op) Py_IS_TYPE(op, mstate->TZInfoType) -#define PyTimezone_Check(op) PyObject_TypeCheck(op, &PyDateTime_TimeZoneType) +#define PyTimezone_Check(op) PyObject_TypeCheck(op, mstate->TimeZoneType) /*[clinic input] module datetime @@ -129,17 +129,58 @@ class datetime.IsoCalendarDate "PyDateTime_IsoCalendarDate *" "&PyDateTime_IsoCa */ #define MONTH_IS_SANE(M) ((unsigned int)(M) - 1 < 12) -/* Forward declarations. */ -static PyTypeObject PyDateTime_DateType; -static PyTypeObject PyDateTime_DateTimeType; -static PyTypeObject PyDateTime_DeltaType; -static PyTypeObject PyDateTime_IsoCalendarDateType; -static PyTypeObject PyDateTime_TimeType; -static PyTypeObject PyDateTime_TZInfoType; -static PyTypeObject PyDateTime_TimeZoneType; -static int check_tzinfo_subclass(PyObject *p); +typedef struct { + PyTypeObject *DateType; + PyTypeObject *DateTimeType; + PyTypeObject *DeltaType; + PyTypeObject *IsoCalendarDateType; + PyTypeObject *TimeType; + PyTypeObject *TZInfoType; + PyTypeObject *TimeZoneType; +} DateTimeState; +static inline DateTimeState * +get_module_state(PyObject *mod) +{ + void *state = _PyModule_GetState(mod); + assert(state != NULL); + return (DateTimeState *)state; +} + +static inline DateTimeState * +get_module_state_by_cls(PyTypeObject *cls) +{ + void *state = _PyType_GetModuleState(cls); + assert(state != NULL); + return (DateTimeState *)state; +} + +static struct PyModuleDef datetimemodule; + +static inline DateTimeState * +find_module_state_by_def(PyTypeObject *type) +{ + PyObject *mod = PyType_GetModuleByDef(type, &datetimemodule); + assert(mod != NULL); + return get_module_state(mod); +} + +static inline DateTimeState * +find_module_state_by_objs(PyObject *left, PyObject *right) +{ + PyTypeObject *tp = Py_TYPE(left); + PyObject *mod = PyType_GetModuleByDef(tp, &datetimemodule); + if (mod == NULL) { + PyErr_Clear(); + tp = Py_TYPE(right); + mod = PyType_GetModuleByDef(tp, &datetimemodule); + } + assert(mod != NULL); + return get_module_state(mod); +} + +static int check_tzinfo_subclass(PyObject *p, DateTimeState *state); /* --------------------------------------------------------------------------- * Math utilities. @@ -951,7 +992,7 @@ new_date_ex(int year, int month, int day, PyTypeObject *type) } #define new_date(year, month, day) \ - new_date_ex(year, month, day, &PyDateTime_DateType) + new_date_ex(year, month, day, mstate->DateType) // Forward declaration static PyObject * @@ -961,12 +1002,14 @@ new_datetime_ex(int, int, int, int, int, int, int, PyObject *, PyTypeObject *); static PyObject * new_date_subclass_ex(int year, int month, int day, PyObject *cls) { + DateTimeState *mstate = find_module_state_by_def((PyTypeObject *)cls); + PyObject *result; // We have "fast path" constructors for two subclasses: date and datetime - if ((PyTypeObject *)cls == &PyDateTime_DateType) { + if ((PyTypeObject *)cls == mstate->DateType) { result = new_date_ex(year, month, day, (PyTypeObject *)cls); } - else if ((PyTypeObject *)cls == &PyDateTime_DateTimeType) { + else if ((PyTypeObject *)cls == mstate->DateTimeType) { result = new_datetime_ex(year, month, day, 0, 0, 0, 0, Py_None, (PyTypeObject *)cls); } @@ -985,13 +1028,15 @@ new_datetime_ex2(int year, int month, int day, int hour, int minute, PyDateTime_DateTime *self; char aware = tzinfo != Py_None; + DateTimeState *mstate = find_module_state_by_def(type); + if (check_date_args(year, month, day) < 0) { return NULL; } if (check_time_args(hour, minute, second, usecond, fold) < 0) { return NULL; } - if (check_tzinfo_subclass(tzinfo) < 0) { + if (check_tzinfo_subclass(tzinfo, mstate) < 0) { return NULL; } @@ -1021,14 +1066,17 @@ new_datetime_ex(int year, int month, int day, int hour, int minute, #define new_datetime(y, m, d, hh, mm, ss, us, tzinfo, fold) \ new_datetime_ex2(y, m, d, hh, mm, ss, us, tzinfo, fold, \ - &PyDateTime_DateTimeType) + mstate->DateTimeType) static PyObject * new_datetime_subclass_fold_ex(int year, int month, int day, int hour, int minute, int second, int usecond, PyObject *tzinfo, int fold, PyObject *cls) { + + DateTimeState *mstate = find_module_state_by_def((PyTypeObject *)cls); + PyObject* dt; - if ((PyTypeObject*)cls == &PyDateTime_DateTimeType) { + if ((PyTypeObject*)cls == mstate->DateTimeType) { // Use the fast path constructor dt = new_datetime(year, month, day, hour, minute, second, usecond, tzinfo, fold); @@ -1068,7 +1116,10 @@ new_time_ex2(int hour, int minute, int second, int usecond, if (check_time_args(hour, minute, second, usecond, fold) < 0) { return NULL; } - if (check_tzinfo_subclass(tzinfo) < 0) { + + DateTimeState *mstate = find_module_state_by_def(type); + + if (check_tzinfo_subclass(tzinfo, mstate) < 0) { return NULL; } @@ -1096,7 +1147,7 @@ new_time_ex(int hour, int minute, int second, int usecond, } #define new_time(hh, mm, ss, us, tzinfo, fold) \ - new_time_ex2(hh, mm, ss, us, tzinfo, fold, &PyDateTime_TimeType) + new_time_ex2(hh, mm, ss, us, tzinfo, fold, mstate->TimeType) /* Create a timedelta instance. Normalize the members iff normalize is * true. Passing false is a speed optimization, if you know for sure @@ -1129,7 +1180,7 @@ new_delta_ex(int days, int seconds, int microseconds, int normalize, } #define new_delta(d, s, us, normalize) \ - new_delta_ex(d, s, us, normalize, &PyDateTime_DeltaType) + new_delta_ex(d, s, us, normalize, mstate->DeltaType) typedef struct @@ -1151,8 +1202,9 @@ static PyObject *PyDateTime_Epoch; static PyObject * create_timezone(PyObject *offset, PyObject *name) { + DateTimeState *mstate = find_module_state_by_def(Py_TYPE(offset)); PyDateTime_TimeZone *self; - PyTypeObject *type = &PyDateTime_TimeZoneType; + PyTypeObject *type = mstate->TimeZoneType; assert(offset != NULL); assert(PyDelta_Check(offset)); @@ -1201,7 +1253,7 @@ new_timezone(PyObject *offset, PyObject *name) * raise TypeError and return -1. */ static int -check_tzinfo_subclass(PyObject *p) +check_tzinfo_subclass(PyObject *p, DateTimeState *mstate) { if (p == Py_None || PyTZInfo_Check(p)) return 0; @@ -1219,6 +1271,8 @@ check_tzinfo_subclass(PyObject *p) static PyObject * get_tzinfo_member(PyObject *self) { + DateTimeState *mstate = find_module_state_by_def(Py_TYPE(self)); + PyObject *tzinfo = NULL; if (PyDateTime_Check(self) && HASTZINFO(self)) @@ -1241,12 +1295,15 @@ call_tzinfo_method(PyObject *tzinfo, const char *name, PyObject *tzinfoarg) { PyObject *offset; + if (tzinfo == Py_None) + Py_RETURN_NONE; + assert(tzinfo != NULL); - assert(PyTZInfo_Check(tzinfo) || tzinfo == Py_None); assert(tzinfoarg != NULL); - if (tzinfo == Py_None) - Py_RETURN_NONE; + DateTimeState *mstate = find_module_state_by_def(Py_TYPE(tzinfo)); + assert(PyTZInfo_Check(tzinfo)); + offset = PyObject_CallMethod(tzinfo, name, "O", tzinfoarg); if (offset == Py_None || offset == NULL) return offset; @@ -1311,13 +1368,17 @@ call_dst(PyObject *tzinfo, PyObject *tzinfoarg) static PyObject * call_tzname(PyObject *tzinfo, PyObject *tzinfoarg) { + if (tzinfo == Py_None) + Py_RETURN_NONE; + PyObject *result; assert(tzinfo != NULL); - assert(check_tzinfo_subclass(tzinfo) >= 0); - assert(tzinfoarg != NULL); - if (tzinfo == Py_None) - Py_RETURN_NONE; + DateTimeState *mstate = find_module_state_by_def(Py_TYPE(tzinfo)); + (void)mstate; // silence unused variable warning + + assert(check_tzinfo_subclass(tzinfo, mstate) >= 0); + assert(tzinfoarg != NULL); result = PyObject_CallMethodOneArg(tzinfo, &_Py_ID(tzname), tzinfoarg); @@ -1384,7 +1445,7 @@ append_keyword_fold(PyObject *repr, int fold) } static inline PyObject * -tzinfo_from_isoformat_results(int rv, int tzoffset, int tz_useconds) +tzinfo_from_isoformat_results(int rv, int tzoffset, int tz_useconds, DateTimeState *mstate) { PyObject *tzinfo; if (rv == 1) { @@ -1557,6 +1618,8 @@ make_Zreplacement(PyObject *object, PyObject *tzinfoarg) static PyObject * make_freplacement(PyObject *object) { + DateTimeState *mstate = find_module_state_by_def(Py_TYPE(object)); + char freplacement[64]; if (PyTime_Check(object)) sprintf(freplacement, "%06d", TIME_GET_MICROSECOND(object)); @@ -1969,7 +2032,7 @@ microseconds_to_delta_ex(PyObject *pyus, PyTypeObject *type) } #define microseconds_to_delta(pymicros) \ - microseconds_to_delta_ex(pymicros, &PyDateTime_DeltaType) + microseconds_to_delta_ex(pymicros, mstate->DeltaType) static PyObject * multiply_int_timedelta(PyObject *intobj, PyDateTime_Delta *delta) @@ -1987,6 +2050,8 @@ multiply_int_timedelta(PyObject *intobj, PyDateTime_Delta *delta) if (pyus_out == NULL) return NULL; + DateTimeState *mstate = find_module_state_by_def(Py_TYPE(delta)); + result = microseconds_to_delta(pyus_out); Py_DECREF(pyus_out); return result; @@ -2042,6 +2107,9 @@ multiply_truedivide_timedelta_float(PyDateTime_Delta *delta, PyObject *floatobj, Py_DECREF(temp); if (pyus_out == NULL) goto error; + + DateTimeState *mstate = find_module_state_by_def(Py_TYPE(delta)); + result = microseconds_to_delta(pyus_out); Py_DECREF(pyus_out); error: @@ -2067,6 +2135,8 @@ divide_timedelta_int(PyDateTime_Delta *delta, PyObject *intobj) if (pyus_out == NULL) return NULL; + DateTimeState *mstate = find_module_state_by_def(Py_TYPE(delta)); + result = microseconds_to_delta(pyus_out); Py_DECREF(pyus_out); return result; @@ -2130,6 +2200,9 @@ truedivide_timedelta_int(PyDateTime_Delta *delta, PyObject *i) Py_DECREF(pyus_in); if (pyus_out == NULL) return NULL; + + DateTimeState *mstate = find_module_state_by_def(Py_TYPE(delta)); + result = microseconds_to_delta(pyus_out); Py_DECREF(pyus_out); @@ -2141,6 +2214,8 @@ delta_add(PyObject *left, PyObject *right) { PyObject *result = Py_NotImplemented; + DateTimeState *mstate = find_module_state_by_objs(left, right); + if (PyDelta_Check(left) && PyDelta_Check(right)) { /* delta + delta */ /* The C-level additions can't overflow because of the @@ -2161,6 +2236,8 @@ delta_add(PyObject *left, PyObject *right) static PyObject * delta_negative(PyDateTime_Delta *self) { + DateTimeState *mstate = find_module_state_by_def(Py_TYPE(self)); + return new_delta(-GET_TD_DAYS(self), -GET_TD_SECONDS(self), -GET_TD_MICROSECONDS(self), @@ -2170,6 +2247,8 @@ delta_negative(PyDateTime_Delta *self) static PyObject * delta_positive(PyDateTime_Delta *self) { + DateTimeState *mstate = find_module_state_by_def(Py_TYPE(self)); + /* Could optimize this (by returning self) if this isn't a * subclass -- but who uses unary + ? Approximately nobody. */ @@ -2200,6 +2279,8 @@ delta_subtract(PyObject *left, PyObject *right) { PyObject *result = Py_NotImplemented; + DateTimeState *mstate = find_module_state_by_objs(left, right); + if (PyDelta_Check(left) && PyDelta_Check(right)) { /* delta - delta */ /* The C-level additions can't overflow because of the @@ -2225,7 +2306,7 @@ delta_cmp(PyObject *self, PyObject *other) diff = GET_TD_SECONDS(self) - GET_TD_SECONDS(other); if (diff == 0) diff = GET_TD_MICROSECONDS(self) - - GET_TD_MICROSECONDS(other); + GET_TD_MICROSECONDS(other); } return diff; } @@ -2233,6 +2314,8 @@ delta_cmp(PyObject *self, PyObject *other) static PyObject * delta_richcompare(PyObject *self, PyObject *other, int op) { + DateTimeState *mstate = find_module_state_by_def(Py_TYPE(self)); + if (PyDelta_Check(other)) { int diff = delta_cmp(self, other); return diff_to_bool(diff, op); @@ -2262,6 +2345,8 @@ delta_multiply(PyObject *left, PyObject *right) { PyObject *result = Py_NotImplemented; + DateTimeState *mstate = find_module_state_by_objs(left, right); + if (PyDelta_Check(left)) { /* delta * ??? */ if (PyLong_Check(right)) @@ -2288,6 +2373,8 @@ delta_divide(PyObject *left, PyObject *right) { PyObject *result = Py_NotImplemented; + DateTimeState *mstate = find_module_state_by_objs(left, right); + if (PyDelta_Check(left)) { /* delta * ??? */ if (PyLong_Check(right)) @@ -2310,6 +2397,8 @@ delta_truedivide(PyObject *left, PyObject *right) { PyObject *result = Py_NotImplemented; + DateTimeState *mstate = find_module_state_by_objs(left, right); + if (PyDelta_Check(left)) { if (PyDelta_Check(right)) result = truedivide_timedelta_timedelta( @@ -2336,6 +2425,8 @@ delta_remainder(PyObject *left, PyObject *right) PyObject *pyus_remainder; PyObject *remainder; + DateTimeState *mstate = find_module_state_by_objs(left, right); + if (!PyDelta_Check(left) || !PyDelta_Check(right)) Py_RETURN_NOTIMPLEMENTED; @@ -2372,6 +2463,8 @@ delta_divmod(PyObject *left, PyObject *right) PyObject *delta; PyObject *result; + DateTimeState *mstate = find_module_state_by_objs(left, right); + if (!PyDelta_Check(left) || !PyDelta_Check(right)) Py_RETURN_NOTIMPLEMENTED; @@ -2749,90 +2842,45 @@ static PyMethodDef delta_methods[] = { }; static const char delta_doc[] = -PyDoc_STR("Difference between two datetime values.\n\n" - "timedelta(days=0, seconds=0, microseconds=0, milliseconds=0, " - "minutes=0, hours=0, weeks=0)\n\n" - "All arguments are optional and default to 0.\n" - "Arguments may be integers or floats, and may be positive or negative."); - -static PyNumberMethods delta_as_number = { - delta_add, /* nb_add */ - delta_subtract, /* nb_subtract */ - delta_multiply, /* nb_multiply */ - delta_remainder, /* nb_remainder */ - delta_divmod, /* nb_divmod */ - 0, /* nb_power */ - (unaryfunc)delta_negative, /* nb_negative */ - (unaryfunc)delta_positive, /* nb_positive */ - (unaryfunc)delta_abs, /* nb_absolute */ - (inquiry)delta_bool, /* nb_bool */ - 0, /*nb_invert*/ - 0, /*nb_lshift*/ - 0, /*nb_rshift*/ - 0, /*nb_and*/ - 0, /*nb_xor*/ - 0, /*nb_or*/ - 0, /*nb_int*/ - 0, /*nb_reserved*/ - 0, /*nb_float*/ - 0, /*nb_inplace_add*/ - 0, /*nb_inplace_subtract*/ - 0, /*nb_inplace_multiply*/ - 0, /*nb_inplace_remainder*/ - 0, /*nb_inplace_power*/ - 0, /*nb_inplace_lshift*/ - 0, /*nb_inplace_rshift*/ - 0, /*nb_inplace_and*/ - 0, /*nb_inplace_xor*/ - 0, /*nb_inplace_or*/ - delta_divide, /* nb_floor_divide */ - delta_truedivide, /* nb_true_divide */ - 0, /* nb_inplace_floor_divide */ - 0, /* nb_inplace_true_divide */ + PyDoc_STR("Difference between two datetime values.\n\n" + "timedelta(days=0, seconds=0, microseconds=0, milliseconds=0, " + "minutes=0, hours=0, weeks=0)\n\n" + "All arguments are optional and default to 0.\n" + "Arguments may be integers or floats, and may be positive or negative."); + +static PyType_Slot delta_slots[] = { + {Py_tp_repr, (void *)delta_repr}, + {Py_nb_add, (void *)delta_add}, + {Py_nb_subtract, (void *)delta_subtract}, + {Py_nb_multiply, (void *)delta_multiply}, + {Py_nb_remainder, (void *)delta_remainder}, + {Py_nb_divmod, (void *)delta_divmod}, + {Py_nb_negative, (void *)delta_negative}, + {Py_nb_positive, (void *)delta_positive}, + {Py_nb_absolute, (void *)delta_abs}, + {Py_nb_bool, (void *)delta_bool}, + {Py_nb_floor_divide, (void *)delta_divide}, + {Py_nb_true_divide, (void *)delta_truedivide}, + {Py_tp_hash, (void *)delta_hash}, + {Py_tp_str, (void *)delta_str}, + {Py_tp_getattro, (void *)PyObject_GenericGetAttr}, + {Py_tp_richcompare, (void *)delta_richcompare}, + {Py_tp_methods, delta_methods}, + {Py_tp_members, delta_members}, + {Py_tp_new, (void *)delta_new}, + {Py_tp_doc, (void *)delta_doc}, + {0, NULL} // Sentinel }; -static PyTypeObject PyDateTime_DeltaType = { - PyVarObject_HEAD_INIT(NULL, 0) - "datetime.timedelta", /* tp_name */ - sizeof(PyDateTime_Delta), /* tp_basicsize */ - 0, /* tp_itemsize */ - 0, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - (reprfunc)delta_repr, /* tp_repr */ - &delta_as_number, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - (hashfunc)delta_hash, /* tp_hash */ - 0, /* tp_call */ - (reprfunc)delta_str, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - delta_doc, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - delta_richcompare, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - delta_methods, /* tp_methods */ - delta_members, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - delta_new, /* tp_new */ - 0, /* tp_free */ +static PyType_Spec PyDateTime_TimeDeltaType_Spec = { + "datetime.timedelta", + sizeof(PyDateTime_Delta), + 0, + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + delta_slots }; + /* * PyDateTime_Date implementation. */ @@ -3148,6 +3196,8 @@ add_date_timedelta(PyDateTime_Date *date, PyDateTime_Delta *delta, int negate) static PyObject * date_add(PyObject *left, PyObject *right) { + DateTimeState *mstate = find_module_state_by_objs(left, right); + if (PyDateTime_Check(left) || PyDateTime_Check(right)) Py_RETURN_NOTIMPLEMENTED; @@ -3175,6 +3225,8 @@ date_add(PyObject *left, PyObject *right) static PyObject * date_subtract(PyObject *left, PyObject *right) { + DateTimeState *mstate = find_module_state_by_objs(left, right); + if (PyDateTime_Check(left) || PyDateTime_Check(right)) Py_RETURN_NOTIMPLEMENTED; @@ -3281,8 +3333,9 @@ date_isoweekday(PyDateTime_Date *self, PyObject *Py_UNUSED(ignored)) return PyLong_FromLong(dow + 1); } -PyDoc_STRVAR(iso_calendar_date__doc__, -"The result of date.isocalendar() or datetime.isocalendar()\n\n\ + +static const char isocalendar_doc[] = + PyDoc_STR("The result of date.isocalendar() or datetime.isocalendar()\n\n\ This object may be accessed either as a tuple of\n\ ((year, week, weekday)\n\ or via the object attributes as named in the above tuple."); @@ -3364,23 +3417,28 @@ static PyGetSetDef iso_calendar_date_getset[] = { static PyMethodDef iso_calendar_date_methods[] = { {"__reduce__", (PyCFunction)iso_calendar_date_reduce, METH_NOARGS, - PyDoc_STR("__reduce__() -> (cls, state)")}, + PyDoc_STR("__reduce__() -> (cls, state)")}, {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 PyType_Slot isocalendar_slots[] = { + {Py_tp_repr, (void *)iso_calendar_date_repr}, + {Py_tp_methods, iso_calendar_date_methods}, + {Py_tp_getset, iso_calendar_date_getset}, + {Py_tp_new, iso_calendar_date_new}, + {Py_tp_doc, (void *)isocalendar_doc}, + {0, NULL} +}; + +static PyType_Spec PyDateTime_IsoCalendarDateType_Spec = { + "datetime.IsoCalendarDate", + sizeof(PyDateTime_IsoCalendarDate), + 0, + Py_TPFLAGS_DEFAULT, + isocalendar_slots }; + /*[clinic input] @classmethod datetime.IsoCalendarDate.__new__ as iso_calendar_date_new @@ -3411,6 +3469,8 @@ iso_calendar_date_new_impl(PyTypeObject *type, int year, int week, static PyObject * date_isocalendar(PyDateTime_Date *self, PyObject *Py_UNUSED(ignored)) { + DateTimeState *mstate = find_module_state_by_def(Py_TYPE(self)); + int year = GET_YEAR(self); int week1_monday = iso_week1_monday(year); int today = ymd_to_ord(year, GET_MONTH(self), GET_DAY(self)); @@ -3428,8 +3488,8 @@ date_isocalendar(PyDateTime_Date *self, PyObject *Py_UNUSED(ignored)) week = 0; } - PyObject* v = iso_calendar_date_new_impl(&PyDateTime_IsoCalendarDateType, - year, week + 1, day + 1); + PyObject* v = iso_calendar_date_new_impl(mstate->IsoCalendarDateType, + year, week + 1, day + 1); if (v == NULL) { return NULL; } @@ -3441,6 +3501,8 @@ date_isocalendar(PyDateTime_Date *self, PyObject *Py_UNUSED(ignored)) static PyObject * date_richcompare(PyObject *self, PyObject *other, int op) { + DateTimeState *mstate = find_module_state_by_def(Py_TYPE(self)); + if (PyDate_Check(other)) { int diff = memcmp(((PyDateTime_Date *)self)->data, ((PyDateTime_Date *)other)->data, @@ -3600,61 +3662,29 @@ static PyMethodDef date_methods[] = { }; static const char date_doc[] = -PyDoc_STR("date(year, month, day) --> date object"); - -static PyNumberMethods date_as_number = { - date_add, /* nb_add */ - date_subtract, /* nb_subtract */ - 0, /* nb_multiply */ - 0, /* nb_remainder */ - 0, /* nb_divmod */ - 0, /* nb_power */ - 0, /* nb_negative */ - 0, /* nb_positive */ - 0, /* nb_absolute */ - 0, /* nb_bool */ + PyDoc_STR("date(year, month, day) --> date object"); + +static PyType_Slot date_slots[] = { + {Py_tp_repr, (void*)date_repr}, + {Py_tp_hash, (void*)date_hash}, + {Py_tp_str, (void*)date_str}, + {Py_tp_getattro, (void*)PyObject_GenericGetAttr}, + {Py_tp_richcompare, (void*)date_richcompare}, + {Py_tp_methods, (void*)date_methods}, + {Py_tp_getset, (void*)date_getset}, + {Py_tp_new, (void*)date_new}, + {Py_nb_add, (void*)date_add}, + {Py_nb_subtract, (void*)date_subtract}, + {Py_tp_doc, (void *)date_doc}, + {0, 0} }; -static PyTypeObject PyDateTime_DateType = { - PyVarObject_HEAD_INIT(NULL, 0) - "datetime.date", /* tp_name */ - sizeof(PyDateTime_Date), /* tp_basicsize */ - 0, /* tp_itemsize */ - 0, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - (reprfunc)date_repr, /* tp_repr */ - &date_as_number, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - (hashfunc)date_hash, /* tp_hash */ - 0, /* tp_call */ - (reprfunc)date_str, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - date_doc, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - date_richcompare, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - date_methods, /* tp_methods */ - 0, /* tp_members */ - date_getset, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - date_new, /* tp_new */ - 0, /* tp_free */ +static PyType_Spec PyDateTime_DateType_Spec = { + "datetime.date", /* name */ + sizeof(PyDateTime_Date), /* basicsize */ + 0, /* itemsize */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* flags */ + date_slots /* slots */ }; /* @@ -3717,6 +3747,8 @@ tzinfo_fromutc(PyDateTime_TZInfo *self, PyObject *dt) PyObject *off = NULL, *dst = NULL; PyDateTime_Delta *delta = NULL; + DateTimeState *mstate = find_module_state_by_def(Py_TYPE(self)); + if (!PyDateTime_Check(dt)) { PyErr_SetString(PyExc_TypeError, "fromutc: argument must be a datetime"); @@ -3841,57 +3873,34 @@ static PyMethodDef tzinfo_methods[] = { static const char tzinfo_doc[] = PyDoc_STR("Abstract base class for time zone info objects."); -static PyTypeObject PyDateTime_TZInfoType = { - PyVarObject_HEAD_INIT(NULL, 0) - "datetime.tzinfo", /* tp_name */ - sizeof(PyDateTime_TZInfo), /* tp_basicsize */ - 0, /* tp_itemsize */ - 0, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 0, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - tzinfo_doc, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - tzinfo_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - PyType_GenericNew, /* tp_new */ - 0, /* tp_free */ +static PyType_Slot tzinfo_slots[] = { + {Py_tp_getattro, (void *)PyObject_GenericGetAttr}, + {Py_tp_methods, tzinfo_methods}, + {Py_tp_new, (void *)PyType_GenericNew}, + {Py_tp_doc, (void *)tzinfo_doc}, + {0, NULL} }; +static PyType_Spec PyDateTime_TZInfoType_Spec = { + "datetime.tzinfo", + sizeof(PyDateTime_TZInfo), + 0, + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + tzinfo_slots +}; + + static char *timezone_kws[] = {"offset", "name", NULL}; static PyObject * timezone_new(PyTypeObject *type, PyObject *args, PyObject *kw) { + DateTimeState *mstate = find_module_state_by_def(type); + PyObject *offset; PyObject *name = NULL; if (PyArg_ParseTupleAndKeywords(args, kw, "O!|U:timezone", timezone_kws, - &PyDateTime_DeltaType, &offset, &name)) + mstate->DeltaType, &offset, &name)) return new_timezone(offset, name); return NULL; @@ -3911,6 +3920,7 @@ timezone_richcompare(PyDateTime_TimeZone *self, { if (op != Py_EQ && op != Py_NE) Py_RETURN_NOTIMPLEMENTED; + DateTimeState *mstate = find_module_state_by_def(Py_TYPE(self)); if (!PyTimezone_Check(other)) { Py_RETURN_NOTIMPLEMENTED; } @@ -3928,7 +3938,7 @@ timezone_hash(PyDateTime_TimeZone *self) otherwise. */ static int -_timezone_check_argument(PyObject *dt, const char *meth) +_timezone_check_argument(PyObject *dt, const char *meth, DateTimeState *mstate) { if (dt == Py_None || PyDateTime_Check(dt)) return 0; @@ -4002,7 +4012,9 @@ timezone_str(PyDateTime_TimeZone *self) static PyObject * timezone_tzname(PyDateTime_TimeZone *self, PyObject *dt) { - if (_timezone_check_argument(dt, "tzname") == -1) + DateTimeState *mstate = find_module_state_by_def(Py_TYPE(self)); + + if (_timezone_check_argument(dt, "tzname", mstate) == -1) return NULL; return timezone_str(self); @@ -4011,7 +4023,9 @@ timezone_tzname(PyDateTime_TimeZone *self, PyObject *dt) static PyObject * timezone_utcoffset(PyDateTime_TimeZone *self, PyObject *dt) { - if (_timezone_check_argument(dt, "utcoffset") == -1) + DateTimeState *mstate = find_module_state_by_def(Py_TYPE(self)); + + if (_timezone_check_argument(dt, "utcoffset", mstate) == -1) return NULL; return Py_NewRef(self->offset); @@ -4020,7 +4034,9 @@ timezone_utcoffset(PyDateTime_TimeZone *self, PyObject *dt) static PyObject * timezone_dst(PyObject *self, PyObject *dt) { - if (_timezone_check_argument(dt, "dst") == -1) + DateTimeState *mstate = find_module_state_by_def(Py_TYPE(self)); + + if (_timezone_check_argument(dt, "dst", mstate) == -1) return NULL; Py_RETURN_NONE; @@ -4029,6 +4045,8 @@ timezone_dst(PyObject *self, PyObject *dt) static PyObject * timezone_fromutc(PyDateTime_TimeZone *self, PyDateTime_DateTime *dt) { + DateTimeState *mstate = find_module_state_by_def(Py_TYPE(self)); + if (!PyDateTime_Check(dt)) { PyErr_SetString(PyExc_TypeError, "fromutc: argument must be a datetime"); @@ -4074,47 +4092,27 @@ static PyMethodDef timezone_methods[] = { static const char timezone_doc[] = PyDoc_STR("Fixed offset from UTC implementation of tzinfo."); -static PyTypeObject PyDateTime_TimeZoneType = { - PyVarObject_HEAD_INIT(NULL, 0) - "datetime.timezone", /* tp_name */ - sizeof(PyDateTime_TimeZone), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)timezone_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - (reprfunc)timezone_repr, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - (hashfunc)timezone_hash, /* tp_hash */ - 0, /* tp_call */ - (reprfunc)timezone_str, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT, /* tp_flags */ - timezone_doc, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - (richcmpfunc)timezone_richcompare,/* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - timezone_methods, /* tp_methods */ - 0, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base; filled in PyInit__datetime */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - 0, /* tp_alloc */ - timezone_new, /* tp_new */ +static PyType_Slot timezone_slots[] = { + {Py_tp_dealloc, (void *)timezone_dealloc}, + {Py_tp_repr, (void *)timezone_repr}, + {Py_tp_hash, (void *)timezone_hash}, + {Py_tp_str, (void *)timezone_str}, + {Py_tp_richcompare, (void *)timezone_richcompare}, + {Py_tp_methods, timezone_methods}, + {Py_tp_new, (void *)timezone_new}, + {Py_tp_doc, (void *)timezone_doc}, + {0, NULL} +}; + +static PyType_Spec PyDateTime_TimeZoneType_Spec = { + "datetime.timezone", + sizeof(PyDateTime_TimeZone), + 0, + Py_TPFLAGS_DEFAULT, + timezone_slots }; + /* * PyDateTime_Time implementation. */ @@ -4183,7 +4181,9 @@ time_from_pickle(PyTypeObject *type, PyObject *state, PyObject *tzinfo) PyDateTime_Time *me; char aware = (char)(tzinfo != Py_None); - if (aware && check_tzinfo_subclass(tzinfo) < 0) { + DateTimeState *mstate = find_module_state_by_def(type); + + if (aware && check_tzinfo_subclass(tzinfo, mstate) < 0) { PyErr_SetString(PyExc_TypeError, "bad tzinfo state arg"); return NULL; } @@ -4444,6 +4444,8 @@ time_richcompare(PyObject *self, PyObject *other, int op) PyObject *offset1, *offset2; int diff; + DateTimeState *mstate = find_module_state_by_def(Py_TYPE(self)); + if (! PyTime_Check(other)) Py_RETURN_NOTIMPLEMENTED; @@ -4511,6 +4513,8 @@ time_richcompare(PyObject *self, PyObject *other, int op) static Py_hash_t time_hash(PyDateTime_Time *self) { + DateTimeState *mstate = find_module_state_by_def(Py_TYPE(self)); + if (self->hashcode == -1) { PyObject *offset, *self0; if (TIME_GET_FOLD(self)) { @@ -4599,6 +4603,8 @@ static PyObject * time_fromisoformat(PyObject *cls, PyObject *tstr) { assert(tstr != NULL); + DateTimeState *mstate = find_module_state_by_def((PyTypeObject *)cls); + if (!PyUnicode_Check(tstr)) { PyErr_SetString(PyExc_TypeError, "fromisoformat: argument must be str"); return NULL; @@ -4630,14 +4636,14 @@ time_fromisoformat(PyObject *cls, PyObject *tstr) { } PyObject *tzinfo = tzinfo_from_isoformat_results(rv, tzoffset, - tzimicrosecond); + tzimicrosecond, mstate); if (tzinfo == NULL) { return NULL; } PyObject *t; - if ( (PyTypeObject *)cls == &PyDateTime_TimeType ) { + if ( (PyTypeObject *)cls == mstate->TimeType ) { t = new_time(hour, minute, second, microsecond, tzinfo, 0); } else { t = PyObject_CallFunction(cls, "iiiiO", @@ -4743,46 +4749,27 @@ PyDoc_STR("time([hour[, minute[, second[, microsecond[, tzinfo]]]]]) --> a time All arguments are optional. tzinfo may be None, or an instance of\n\ a tzinfo subclass. The remaining arguments may be ints.\n"); -static PyTypeObject PyDateTime_TimeType = { - PyVarObject_HEAD_INIT(NULL, 0) - "datetime.time", /* tp_name */ - sizeof(PyDateTime_Time), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)time_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - (reprfunc)time_repr, /* tp_repr */ - 0, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - (hashfunc)time_hash, /* tp_hash */ - 0, /* tp_call */ - (reprfunc)time_str, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - time_doc, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - time_richcompare, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - time_methods, /* tp_methods */ - 0, /* tp_members */ - time_getset, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - time_alloc, /* tp_alloc */ - time_new, /* tp_new */ - 0, /* tp_free */ +static PyType_Slot time_slots[] = { + {Py_tp_dealloc, (void *)time_dealloc}, + {Py_tp_repr, (void *)time_repr}, + {Py_tp_hash, (void *)time_hash}, + {Py_tp_str, (void *)time_str}, + {Py_tp_getattro, (void *)PyObject_GenericGetAttr}, + {Py_tp_richcompare, (void *)time_richcompare}, + {Py_tp_methods, time_methods}, + {Py_tp_getset, time_getset}, + {Py_tp_alloc, (void *)time_alloc}, + {Py_tp_new, (void *)time_new}, + {Py_tp_doc, (void *)time_doc}, + {0, NULL} +}; + +static PyType_Spec PyDateTime_TimeType_Spec = { + "datetime.time", + sizeof(PyDateTime_Time), + 0, + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + time_slots }; /* @@ -4855,7 +4842,9 @@ datetime_from_pickle(PyTypeObject *type, PyObject *state, PyObject *tzinfo) PyDateTime_DateTime *me; char aware = (char)(tzinfo != Py_None); - if (aware && check_tzinfo_subclass(tzinfo) < 0) { + DateTimeState *mstate = find_module_state_by_def(type); + + if (aware && check_tzinfo_subclass(tzinfo, mstate) < 0) { PyErr_SetString(PyExc_TypeError, "bad tzinfo state arg"); return NULL; } @@ -5119,10 +5108,12 @@ datetime_datetime_now_impl(PyTypeObject *type, PyObject *tz) { PyObject *self; + DateTimeState *mstate = find_module_state_by_def(type); + /* 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(tz, mstate) < 0) return NULL; self = datetime_best_possible((PyObject *)type, @@ -5166,7 +5157,10 @@ 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) + + DateTimeState *mstate = find_module_state_by_def((PyTypeObject *)cls); + + if (check_tzinfo_subclass(tzinfo, mstate) < 0) return NULL; self = datetime_from_timestamp(cls, @@ -5232,9 +5226,11 @@ datetime_combine(PyObject *cls, PyObject *args, PyObject *kw) PyObject *tzinfo = NULL; PyObject *result = NULL; + DateTimeState *mstate = find_module_state_by_def((PyTypeObject *)cls); + if (PyArg_ParseTupleAndKeywords(args, kw, "O!O!|O:combine", keywords, - &PyDateTime_DateType, &date, - &PyDateTime_TimeType, &time, &tzinfo)) { + mstate->DateType, &date, + mstate->TimeType, &time, &tzinfo)) { if (tzinfo == NULL) { if (HASTZINFO(time)) tzinfo = ((PyDateTime_Time *)time)->tzinfo; @@ -5421,6 +5417,8 @@ datetime_fromisoformat(PyObject *cls, PyObject *dtstr) return NULL; } + DateTimeState *mstate = find_module_state_by_def((PyTypeObject *)cls); + // We only need to sanitize this string if the separator is a surrogate // character. In the situation where the separator location is ambiguous, // we don't have to sanitize it anything because that can only happen when @@ -5485,7 +5483,7 @@ datetime_fromisoformat(PyObject *cls, PyObject *dtstr) goto invalid_string_error; } - PyObject *tzinfo = tzinfo_from_isoformat_results(rv, tzoffset, tzusec); + PyObject *tzinfo = tzinfo_from_isoformat_results(rv, tzoffset, tzusec, mstate); if (tzinfo == NULL) { goto error; } @@ -5577,6 +5575,8 @@ add_datetime_timedelta(PyDateTime_DateTime *date, PyDateTime_Delta *delta, static PyObject * datetime_add(PyObject *left, PyObject *right) { + DateTimeState *mstate = find_module_state_by_objs(left, right); + if (PyDateTime_Check(left)) { /* datetime + ??? */ if (PyDelta_Check(right)) @@ -5600,6 +5600,8 @@ datetime_subtract(PyObject *left, PyObject *right) { PyObject *result = Py_NotImplemented; + DateTimeState *mstate = find_module_state_by_objs(left, right); + if (PyDateTime_Check(left)) { /* datetime - ??? */ if (PyDateTime_Check(right)) { @@ -5882,6 +5884,8 @@ datetime_richcompare(PyObject *self, PyObject *other, int op) PyObject *offset1, *offset2; int diff; + DateTimeState *mstate = find_module_state_by_def(Py_TYPE(self)); + if (! PyDateTime_Check(other)) { if (PyDate_Check(other)) { /* Prevent invocation of date_richcompare. We want to @@ -5973,6 +5977,8 @@ datetime_richcompare(PyObject *self, PyObject *other, int op) static Py_hash_t datetime_hash(PyDateTime_DateTime *self) { + DateTimeState *mstate = find_module_state_by_def(Py_TYPE(self)); + if (self->hashcode == -1) { PyObject *offset, *self0; if (DATE_GET_FOLD(self)) { @@ -6070,7 +6076,7 @@ datetime_replace(PyDateTime_DateTime *self, PyObject *args, PyObject *kw) } static PyObject * -local_timezone_from_timestamp(time_t timestamp) +local_timezone_from_timestamp(time_t timestamp, DateTimeState *mstate) { PyObject *result = NULL; PyObject *delta; @@ -6139,6 +6145,8 @@ local_timezone(PyDateTime_DateTime *utc_time) PyObject *one_second; PyObject *seconds; + DateTimeState *mstate = find_module_state_by_def(Py_TYPE(utc_time)); + delta = datetime_subtract((PyObject *)utc_time, PyDateTime_Epoch); if (delta == NULL) return NULL; @@ -6157,7 +6165,7 @@ local_timezone(PyDateTime_DateTime *utc_time) Py_DECREF(seconds); if (timestamp == -1 && PyErr_Occurred()) return NULL; - return local_timezone_from_timestamp(timestamp); + return local_timezone_from_timestamp(timestamp, mstate); } static long long @@ -6167,6 +6175,8 @@ local_to_seconds(int year, int month, int day, static PyObject * local_timezone_from_local(PyDateTime_DateTime *local_dt) { + DateTimeState *mstate = find_module_state_by_def(Py_TYPE(local_dt)); + long long seconds, seconds2; time_t timestamp; int fold = DATE_GET_FOLD(local_dt); @@ -6194,7 +6204,7 @@ local_timezone_from_local(PyDateTime_DateTime *local_dt) /* XXX: add bounds check */ timestamp = seconds - epoch; - return local_timezone_from_timestamp(timestamp); + return local_timezone_from_timestamp(timestamp, mstate); } static PyDateTime_DateTime * @@ -6207,11 +6217,13 @@ datetime_astimezone(PyDateTime_DateTime *self, PyObject *args, PyObject *kw) PyObject *tzinfo = Py_None; static char *keywords[] = {"tz", NULL}; + DateTimeState *mstate = find_module_state_by_def(Py_TYPE(self)); + if (! PyArg_ParseTupleAndKeywords(args, kw, "|O:astimezone", keywords, &tzinfo)) return NULL; - if (check_tzinfo_subclass(tzinfo) == -1) + if (check_tzinfo_subclass(tzinfo, mstate) == -1) return NULL; if (!HASTZINFO(self) || self->tzinfo == Py_None) { @@ -6402,6 +6414,7 @@ datetime_timestamp(PyDateTime_DateTime *self, PyObject *Py_UNUSED(ignored)) static PyObject * datetime_getdate(PyDateTime_DateTime *self, PyObject *Py_UNUSED(ignored)) { + DateTimeState *mstate = find_module_state_by_def(Py_TYPE(self)); return new_date(GET_YEAR(self), GET_MONTH(self), GET_DAY(self)); @@ -6410,6 +6423,7 @@ datetime_getdate(PyDateTime_DateTime *self, PyObject *Py_UNUSED(ignored)) static PyObject * datetime_gettime(PyDateTime_DateTime *self, PyObject *Py_UNUSED(ignored)) { + DateTimeState *mstate = find_module_state_by_def(Py_TYPE(self)); return new_time(DATE_GET_HOUR(self), DATE_GET_MINUTE(self), DATE_GET_SECOND(self), @@ -6421,6 +6435,7 @@ datetime_gettime(PyDateTime_DateTime *self, PyObject *Py_UNUSED(ignored)) static PyObject * datetime_gettimetz(PyDateTime_DateTime *self, PyObject *Py_UNUSED(ignored)) { + DateTimeState *mstate = find_module_state_by_def(Py_TYPE(self)); return new_time(DATE_GET_HOUR(self), DATE_GET_MINUTE(self), DATE_GET_SECOND(self), @@ -6606,62 +6621,33 @@ PyDoc_STR("datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzi The year, month and day arguments are required. tzinfo may be None, or an\n\ instance of a tzinfo subclass. The remaining arguments may be ints.\n"); -static PyNumberMethods datetime_as_number = { - datetime_add, /* nb_add */ - datetime_subtract, /* nb_subtract */ - 0, /* nb_multiply */ - 0, /* nb_remainder */ - 0, /* nb_divmod */ - 0, /* nb_power */ - 0, /* nb_negative */ - 0, /* nb_positive */ - 0, /* nb_absolute */ - 0, /* nb_bool */ + +static PyType_Slot datetime_slots[] = { + {Py_tp_dealloc, (void *)datetime_dealloc}, + {Py_tp_repr, (void *)datetime_repr}, + {Py_nb_add, (void *)datetime_add}, + {Py_nb_subtract, (void *)datetime_subtract}, + {Py_tp_hash, (void *)datetime_hash}, + {Py_tp_str, (void *)datetime_str}, + {Py_tp_getattro, (void *)PyObject_GenericGetAttr}, + {Py_tp_richcompare, (void *)datetime_richcompare}, + {Py_tp_methods, datetime_methods}, + {Py_tp_getset, datetime_getset}, + {Py_tp_alloc, (void *)datetime_alloc}, + {Py_tp_new, (void *)datetime_new}, + {Py_tp_doc, (void *)datetime_doc}, + {0, NULL} }; -static PyTypeObject PyDateTime_DateTimeType = { - PyVarObject_HEAD_INIT(NULL, 0) - "datetime.datetime", /* tp_name */ - sizeof(PyDateTime_DateTime), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)datetime_dealloc, /* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - (reprfunc)datetime_repr, /* tp_repr */ - &datetime_as_number, /* tp_as_number */ - 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ - (hashfunc)datetime_hash, /* tp_hash */ - 0, /* tp_call */ - (reprfunc)datetime_str, /* tp_str */ - PyObject_GenericGetAttr, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - datetime_doc, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - datetime_richcompare, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - datetime_methods, /* tp_methods */ - 0, /* tp_members */ - datetime_getset, /* tp_getset */ - 0, /* tp_base; filled in - PyInit__datetime */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - 0, /* tp_init */ - datetime_alloc, /* tp_alloc */ - datetime_new, /* tp_new */ - 0, /* tp_free */ +static PyType_Spec PyDateTime_DateTimeType_Spec = { + "datetime.datetime", + sizeof(PyDateTime_DateTime), + 0, + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + datetime_slots }; + /* --------------------------------------------------------------------------- * Module methods and initialization. */ @@ -6674,18 +6660,22 @@ static PyMethodDef module_methods[] = { * Clients get at C API via PyDateTime_IMPORT, defined in datetime.h. */ static inline PyDateTime_CAPI * -get_datetime_capi(void) +get_datetime_capi(PyObject *mod) { PyDateTime_CAPI *capi = PyMem_Malloc(sizeof(PyDateTime_CAPI)); if (capi == NULL) { PyErr_NoMemory(); return NULL; } - capi->DateType = &PyDateTime_DateType; - capi->DateTimeType = &PyDateTime_DateTimeType; - capi->TimeType = &PyDateTime_TimeType; - capi->DeltaType = &PyDateTime_DeltaType; - capi->TZInfoType = &PyDateTime_TZInfoType; + + DateTimeState *mstate = get_module_state(mod); + + capi->DateType = mstate->DateType; + capi->DateTimeType = mstate->DateTimeType; + capi->TimeType = mstate->TimeType; + capi->DeltaType = mstate->DeltaType; + capi->TZInfoType = mstate->TZInfoType; + capi->Date_FromDate = new_date_ex; capi->DateTime_FromDateAndTime = new_datetime_ex; capi->Time_FromTime = new_time_ex; @@ -6709,34 +6699,32 @@ datetime_destructor(PyObject *op) PyMem_Free(ptr); } + +#define ADD_TYPE(MOD, SPEC, TYPE, BASE) do { \ + TYPE = (PyTypeObject *)PyType_FromMetaclass(NULL, MOD, SPEC, \ + (PyObject *)BASE); \ + if (TYPE == NULL) { \ + return -1; \ + } \ + if (PyModule_AddType(MOD, TYPE) < 0) { \ + return -1; \ + } \ +} while (0) + + static int _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[] = { - &PyDateTime_DateType, - &PyDateTime_DateTimeType, - &PyDateTime_TimeType, - &PyDateTime_DeltaType, - &PyDateTime_TZInfoType, - &PyDateTime_TimeZoneType, - }; + DateTimeState *mstate = get_module_state(module); - for (size_t i = 0; i < Py_ARRAY_LENGTH(types); i++) { - if (PyModule_AddType(module, types[i]) < 0) { - return -1; - } - } + ADD_TYPE(module, &PyDateTime_DateType_Spec, mstate->DateType, NULL); + ADD_TYPE(module, &PyDateTime_DateTimeType_Spec, mstate->DateTimeType, mstate->DateType); + ADD_TYPE(module, &PyDateTime_TimeDeltaType_Spec, mstate->DeltaType, NULL); + ADD_TYPE(module, &PyDateTime_TimeType_Spec, mstate->TimeType, NULL); + ADD_TYPE(module, &PyDateTime_TZInfoType_Spec, mstate->TZInfoType, NULL); + ADD_TYPE(module, &PyDateTime_TimeZoneType_Spec, mstate->TimeZoneType, mstate->TZInfoType); + ADD_TYPE(module, &PyDateTime_IsoCalendarDateType_Spec, mstate->IsoCalendarDateType, &PyTuple_Type); - if (PyType_Ready(&PyDateTime_IsoCalendarDateType) < 0) { - return -1; - } #define DATETIME_ADD_MACRO(dict, c, value_expr) \ do { \ @@ -6752,26 +6740,26 @@ _datetime_exec(PyObject *module) } while(0) /* timedelta values */ - PyObject *d = PyDateTime_DeltaType.tp_dict; + PyObject *d = mstate->DeltaType->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, "max", - new_delta(MAX_DELTA_DAYS, 24*3600-1, 1000000-1, 0)); + new_delta(MAX_DELTA_DAYS, 24*3600-1, 1000000-1, 0)); /* date values */ - d = PyDateTime_DateType.tp_dict; + d = mstate->DateType->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)); /* time values */ - d = PyDateTime_TimeType.tp_dict; + d = mstate->TimeType->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 values */ - d = PyDateTime_DateTimeType.tp_dict; + d = mstate->DateTimeType->tp_dict; DATETIME_ADD_MACRO(d, "min", 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, @@ -6779,7 +6767,7 @@ _datetime_exec(PyObject *module) DATETIME_ADD_MACRO(d, "resolution", new_delta(0, 0, 1, 0)); /* timezone values */ - d = PyDateTime_TimeZoneType.tp_dict; + d = mstate->TimeZoneType->tp_dict; PyObject *delta = new_delta(0, 0, 0, 0); if (delta == NULL) { return -1; @@ -6833,7 +6821,7 @@ _datetime_exec(PyObject *module) return -1; } - PyDateTime_CAPI *capi = get_datetime_capi(); + PyDateTime_CAPI *capi = get_datetime_capi(module); if (capi == NULL) { return -1; } @@ -6916,27 +6904,64 @@ _datetime_exec(PyObject *module) return -1; } + +static int +datetime_traverse(PyObject *mod, visitproc visit, void *arg) +{ + DateTimeState *state = get_module_state(mod); + Py_VISIT(state->DateType); + Py_VISIT(state->DateTimeType); + Py_VISIT(state->DeltaType); + Py_VISIT(state->IsoCalendarDateType); + Py_VISIT(state->TimeType); + Py_VISIT(state->TZInfoType); + Py_VISIT(state->TimeZoneType); + return 0; +} + +static int +datetime_clear(PyObject *mod) +{ + DateTimeState *state = get_module_state(mod); + Py_CLEAR(state->DateType); + Py_CLEAR(state->DateTimeType); + Py_CLEAR(state->DeltaType); + Py_CLEAR(state->IsoCalendarDateType); + Py_CLEAR(state->TimeType); + Py_CLEAR(state->TZInfoType); + Py_CLEAR(state->TimeZoneType); + return 0; +} + +static void +datetime_free(void *module) +{ + datetime_clear((PyObject *)module); +} + + +static struct PyModuleDef_Slot datetime_mod_slots[] = { + {Py_mod_exec, _datetime_exec}, + {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED}, + {0, NULL} +}; + static struct PyModuleDef datetimemodule = { - PyModuleDef_HEAD_INIT, + .m_base = PyModuleDef_HEAD_INIT, .m_name = "_datetime", .m_doc = "Fast implementation of the datetime type.", - .m_size = -1, + .m_size = sizeof(DateTimeState), .m_methods = module_methods, + .m_slots = datetime_mod_slots, + .m_traverse = datetime_traverse, + .m_clear = datetime_clear, + .m_free = datetime_free, }; PyMODINIT_FUNC PyInit__datetime(void) { - PyObject *mod = PyModule_Create(&datetimemodule); - if (mod == NULL) - return NULL; - - if (_datetime_exec(mod) < 0) { - Py_DECREF(mod); - return NULL; - } - - return mod; + return PyModuleDef_Init(&datetimemodule); } /* --------------------------------------------------------------------------- From 89cbd783dd8e4e25a141e8d0ea399202714515d3 Mon Sep 17 00:00:00 2001 From: Costas Date: Tue, 3 Oct 2023 11:51:15 +0100 Subject: [PATCH 2/8] fix: test is wrong? --- Lib/test/datetimetester.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/datetimetester.py b/Lib/test/datetimetester.py index 55e061950ff280..96a6048b23a3eb 100644 --- a/Lib/test/datetimetester.py +++ b/Lib/test/datetimetester.py @@ -91,7 +91,7 @@ def test_name_cleanup(self): if not name.startswith('__') and not name.endswith('__')) allowed = set(['MAXYEAR', 'MINYEAR', 'date', 'datetime', 'datetime_CAPI', 'time', 'timedelta', 'timezone', - 'tzinfo', 'UTC', 'sys']) + 'tzinfo', 'UTC', 'sys', 'IsoCalendarDate']) self.assertEqual(names - allowed, set([])) def test_divide_and_round(self): From e1603ff2ef0bd002a0017bb4ab06f83373ac9622 Mon Sep 17 00:00:00 2001 From: Costas Date: Tue, 3 Oct 2023 20:37:45 +0100 Subject: [PATCH 3/8] fix: port the rest of datetime to use module state --- Modules/_datetimemodule.c | 181 ++++++++++++++++++++++++-------------- 1 file changed, 114 insertions(+), 67 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 44078ac59bddb8..3f86e3cb2eb46f 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -131,6 +131,7 @@ class datetime.IsoCalendarDate "PyDateTime_IsoCalendarDate *" "&PyDateTime_IsoCa typedef struct { + // Type definitions PyTypeObject *DateType; PyTypeObject *DateTimeType; PyTypeObject *DeltaType; @@ -138,6 +139,20 @@ typedef struct { PyTypeObject *TimeType; PyTypeObject *TZInfoType; PyTypeObject *TimeZoneType; + + // Conversion factors + PyObject *us_per_ms; /* 1000 */ + PyObject *us_per_second; /* 1000000 */ + PyObject *us_per_minute; /* 1e6 * 60 as Python int */ + PyObject *us_per_hour; /* 1e6 * 3600 as Python int */ + PyObject *us_per_day; /* 1e6 * 3600 * 24 as Python int */ + PyObject *us_per_week; /* 1e6*3600*24*7 as Python int */ + PyObject *seconds_per_day; /* 3600*24 as Python int */ + + // The interned UTC timezone instance + PyObject *PyDateTime_TimeZone_UTC; + // The interned Epoch datetime instance + PyObject *PyDateTime_Epoch; } DateTimeState; static inline DateTimeState * @@ -1191,9 +1206,9 @@ typedef struct } PyDateTime_TimeZone; /* The interned UTC timezone instance */ -static PyObject *PyDateTime_TimeZone_UTC; +// static PyObject *PyDateTime_TimeZone_UTC; /* The interned Epoch datetime instance */ -static PyObject *PyDateTime_Epoch; +// static PyObject *PyDateTime_Epoch; /* Create new timezone instance checking offset range. This function does not check the name argument. Caller must assure @@ -1228,8 +1243,10 @@ new_timezone(PyObject *offset, PyObject *name) assert(PyDelta_Check(offset)); assert(name == NULL || PyUnicode_Check(name)); + DateTimeState *mstate = find_module_state_by_def(Py_TYPE(offset)); + if (name == NULL && delta_bool((PyDateTime_Delta *)offset) == 0) { - return Py_NewRef(PyDateTime_TimeZone_UTC); + return Py_NewRef(mstate->PyDateTime_TimeZone_UTC); } if ((GET_TD_DAYS(offset) == -1 && GET_TD_SECONDS(offset) == 0 && @@ -1451,7 +1468,7 @@ tzinfo_from_isoformat_results(int rv, int tzoffset, int tz_useconds, DateTimeSta if (rv == 1) { // Create a timezone from offset in seconds (0 returns UTC) if (tzoffset == 0) { - return Py_NewRef(PyDateTime_TimeZone_UTC); + return Py_NewRef(mstate->PyDateTime_TimeZone_UTC); } PyObject *delta = new_delta(0, tzoffset, tz_useconds, 1); @@ -1875,13 +1892,13 @@ cmperror(PyObject *a, PyObject *b) */ /* Conversion factors. */ -static PyObject *us_per_ms = NULL; /* 1000 */ -static PyObject *us_per_second = NULL; /* 1000000 */ -static PyObject *us_per_minute = NULL; /* 1e6 * 60 as Python int */ -static PyObject *us_per_hour = NULL; /* 1e6 * 3600 as Python int */ -static PyObject *us_per_day = NULL; /* 1e6 * 3600 * 24 as Python int */ -static PyObject *us_per_week = NULL; /* 1e6*3600*24*7 as Python int */ -static PyObject *seconds_per_day = NULL; /* 3600*24 as Python int */ +// static PyObject *us_per_ms = NULL; /* 1000 */ +// static PyObject *us_per_second = NULL; /* 1000000 */ +// static PyObject *us_per_minute = NULL; /* 1e6 * 60 as Python int */ +// static PyObject *us_per_hour = NULL; /* 1e6 * 3600 as Python int */ +// static PyObject *us_per_day = NULL; /* 1e6 * 3600 * 24 as Python int */ +// static PyObject *us_per_week = NULL; /* 1e6*3600*24*7 as Python int */ +// static PyObject *seconds_per_day = NULL; /* 3600*24 as Python int */ /* --------------------------------------------------------------------------- * Class implementations. @@ -1905,10 +1922,12 @@ delta_to_microseconds(PyDateTime_Delta *self) PyObject *x3 = NULL; PyObject *result = NULL; + DateTimeState *mstate = find_module_state_by_def(Py_TYPE(self)); + x1 = PyLong_FromLong(GET_TD_DAYS(self)); if (x1 == NULL) goto Done; - x2 = PyNumber_Multiply(x1, seconds_per_day); /* days in seconds */ + x2 = PyNumber_Multiply(x1, mstate->seconds_per_day); /* days in seconds */ if (x2 == NULL) goto Done; Py_SETREF(x1, NULL); @@ -1925,7 +1944,7 @@ delta_to_microseconds(PyDateTime_Delta *self) /* x1 = */ x2 = NULL; /* x3 has days+seconds in seconds */ - x1 = PyNumber_Multiply(x3, us_per_second); /* us */ + x1 = PyNumber_Multiply(x3, mstate->us_per_second); /* us */ if (x1 == NULL) goto Done; Py_SETREF(x3, NULL); @@ -1980,7 +1999,9 @@ microseconds_to_delta_ex(PyObject *pyus, PyTypeObject *type) PyObject *num = NULL; PyObject *result = NULL; - tuple = checked_divmod(pyus, us_per_second); + DateTimeState *mstate = find_module_state_by_def(type); + + tuple = checked_divmod(pyus, mstate->us_per_second); if (tuple == NULL) { goto Done; } @@ -1998,7 +2019,7 @@ microseconds_to_delta_ex(PyObject *pyus, PyTypeObject *type) num = Py_NewRef(PyTuple_GET_ITEM(tuple, 0)); /* leftover seconds */ Py_DECREF(tuple); - tuple = checked_divmod(num, seconds_per_day); + tuple = checked_divmod(num, mstate->seconds_per_day); if (tuple == NULL) goto Done; Py_DECREF(num); @@ -2618,6 +2639,8 @@ delta_new(PyTypeObject *type, PyObject *args, PyObject *kw) if (x == NULL) goto Done; + DateTimeState *mstate = find_module_state_by_def(type); + #define CLEANUP \ Py_DECREF(x); \ x = y; \ @@ -2629,27 +2652,27 @@ delta_new(PyTypeObject *type, PyObject *args, PyObject *kw) CLEANUP; } if (ms) { - y = accum("milliseconds", x, ms, us_per_ms, &leftover_us); + y = accum("milliseconds", x, ms, mstate->us_per_ms, &leftover_us); CLEANUP; } if (second) { - y = accum("seconds", x, second, us_per_second, &leftover_us); + y = accum("seconds", x, second, mstate->us_per_second, &leftover_us); CLEANUP; } if (minute) { - y = accum("minutes", x, minute, us_per_minute, &leftover_us); + y = accum("minutes", x, minute, mstate->us_per_minute, &leftover_us); CLEANUP; } if (hour) { - y = accum("hours", x, hour, us_per_hour, &leftover_us); + y = accum("hours", x, hour, mstate->us_per_hour, &leftover_us); CLEANUP; } if (day) { - y = accum("days", x, day, us_per_day, &leftover_us); + y = accum("days", x, day, mstate->us_per_day, &leftover_us); CLEANUP; } if (week) { - y = accum("weeks", x, week, us_per_week, &leftover_us); + y = accum("weeks", x, week, mstate->us_per_week, &leftover_us); CLEANUP; } if (leftover_us) { @@ -2800,11 +2823,13 @@ delta_total_seconds(PyObject *self, PyObject *Py_UNUSED(ignored)) PyObject *total_seconds; PyObject *total_microseconds; + DateTimeState *mstate = find_module_state_by_def(Py_TYPE(self)); + total_microseconds = delta_to_microseconds((PyDateTime_Delta *)self); if (total_microseconds == NULL) return NULL; - total_seconds = PyNumber_TrueDivide(total_microseconds, us_per_second); + total_seconds = PyNumber_TrueDivide(total_microseconds, mstate->us_per_second); Py_DECREF(total_microseconds); return total_seconds; @@ -3954,7 +3979,9 @@ timezone_repr(PyDateTime_TimeZone *self) to use Py_TYPE(self)->tp_name here. */ const char *type_name = Py_TYPE(self)->tp_name; - if (((PyObject *)self) == PyDateTime_TimeZone_UTC) + DateTimeState *mstate = find_module_state_by_def(Py_TYPE(self)); + + if (((PyObject *)self) == mstate->PyDateTime_TimeZone_UTC) return PyUnicode_FromFormat("%s.utc", type_name); if (self->name == NULL) @@ -3975,7 +4002,10 @@ timezone_str(PyDateTime_TimeZone *self) if (self->name != NULL) { return Py_NewRef(self->name); } - if ((PyObject *)self == PyDateTime_TimeZone_UTC || + + DateTimeState *mstate = find_module_state_by_def(Py_TYPE(self)); + + if ((PyObject *)self == mstate->PyDateTime_TimeZone_UTC || (GET_TD_DAYS(self->offset) == 0 && GET_TD_SECONDS(self->offset) == 0 && GET_TD_MICROSECONDS(self->offset) == 0)) @@ -6147,7 +6177,7 @@ local_timezone(PyDateTime_DateTime *utc_time) DateTimeState *mstate = find_module_state_by_def(Py_TYPE(utc_time)); - delta = datetime_subtract((PyObject *)utc_time, PyDateTime_Epoch); + delta = datetime_subtract((PyObject *)utc_time, mstate->PyDateTime_Epoch); if (delta == NULL) return NULL; one_second = new_delta(0, 1, 0, 0); @@ -6274,7 +6304,7 @@ datetime_astimezone(PyDateTime_DateTime *self, PyObject *args, PyObject *kw) DATE_GET_MINUTE(result), DATE_GET_SECOND(result), DATE_GET_MICROSECOND(result), - PyDateTime_TimeZone_UTC, + mstate->PyDateTime_TimeZone_UTC, DATE_GET_FOLD(result), Py_TYPE(result)); Py_DECREF(temp); @@ -6283,7 +6313,7 @@ datetime_astimezone(PyDateTime_DateTime *self, PyObject *args, PyObject *kw) } else { /* Result is already aware - just replace tzinfo. */ - Py_SETREF(result->tzinfo, Py_NewRef(PyDateTime_TimeZone_UTC)); + Py_SETREF(result->tzinfo, Py_NewRef(mstate->PyDateTime_TimeZone_UTC)); } /* Attach new tzinfo and let fromutc() do the rest. */ @@ -6386,9 +6416,11 @@ datetime_timestamp(PyDateTime_DateTime *self, PyObject *Py_UNUSED(ignored)) { PyObject *result; + DateTimeState *mstate = find_module_state_by_def(Py_TYPE(self)); + if (HASTZINFO(self) && self->tzinfo != Py_None) { PyObject *delta; - delta = datetime_subtract((PyObject *)self, PyDateTime_Epoch); + delta = datetime_subtract((PyObject *)self, mstate->PyDateTime_Epoch); if (delta == NULL) return NULL; result = delta_total_seconds(delta, NULL); @@ -6687,8 +6719,8 @@ get_datetime_capi(PyObject *mod) capi->Time_FromTimeAndFold = new_time_ex2; // Make sure this function is called after PyDateTime_TimeZone_UTC has // been initialized. - assert(PyDateTime_TimeZone_UTC != NULL); - capi->TimeZone_UTC = PyDateTime_TimeZone_UTC; // borrowed ref + assert(mstate->PyDateTime_TimeZone_UTC != NULL); + capi->TimeZone_UTC = mstate->PyDateTime_TimeZone_UTC; // borrowed ref return capi; } @@ -6725,7 +6757,6 @@ _datetime_exec(PyObject *module) ADD_TYPE(module, &PyDateTime_TimeZoneType_Spec, mstate->TimeZoneType, mstate->TZInfoType); ADD_TYPE(module, &PyDateTime_IsoCalendarDateType_Spec, mstate->IsoCalendarDateType, &PyTuple_Type); - #define DATETIME_ADD_MACRO(dict, c, value_expr) \ do { \ PyObject *value = (value_expr); \ @@ -6783,7 +6814,7 @@ _datetime_exec(PyObject *module) return -1; } - PyDateTime_TimeZone_UTC = x; + mstate->PyDateTime_TimeZone_UTC = x; /* bpo-37642: These attributes are rounded to the nearest minute for backwards * compatibility, even though the constructor will accept a wider range of @@ -6807,9 +6838,9 @@ _datetime_exec(PyObject *module) DATETIME_ADD_MACRO(d, "max", x); /* Epoch */ - PyDateTime_Epoch = new_datetime(1970, 1, 1, 0, 0, 0, 0, - PyDateTime_TimeZone_UTC, 0); - if (PyDateTime_Epoch == NULL) { + mstate->PyDateTime_Epoch = new_datetime(1970, 1, 1, 0, 0, 0, 0, + mstate->PyDateTime_TimeZone_UTC, 0); + if (mstate->PyDateTime_Epoch == NULL) { return -1; } @@ -6836,7 +6867,7 @@ _datetime_exec(PyObject *module) return -1; } - if (PyModule_AddObjectRef(module, "UTC", PyDateTime_TimeZone_UTC) < 0) { + if (PyModule_AddObjectRef(module, "UTC", mstate->PyDateTime_TimeZone_UTC) < 0) { return -1; } @@ -6858,50 +6889,40 @@ _datetime_exec(PyObject *module) static_assert(DI100Y == 25 * DI4Y - 1, "DI100Y"); assert(DI100Y == days_before_year(100+1)); - us_per_ms = PyLong_FromLong(1000); - if (us_per_ms == NULL) { - goto error; + mstate->us_per_ms = PyLong_FromLong(1000); + if (mstate->us_per_ms == NULL) { + return -1; } - us_per_second = PyLong_FromLong(1000000); - if (us_per_second == NULL) { - goto error; + mstate->us_per_second = PyLong_FromLong(1000000); + if (mstate->us_per_second == NULL) { + return -1; } - us_per_minute = PyLong_FromLong(60000000); - if (us_per_minute == NULL) { - goto error; + mstate->us_per_minute = PyLong_FromLong(60000000); + if (mstate->us_per_minute == NULL) { + return -1; } - seconds_per_day = PyLong_FromLong(24 * 3600); - if (seconds_per_day == NULL) { - goto error; + mstate->seconds_per_day = PyLong_FromLong(24 * 3600); + if (mstate->seconds_per_day == NULL) { + return -1; } /* The rest are too big for 32-bit ints, but even * us_per_week fits in 40 bits, so doubles should be exact. */ - us_per_hour = PyLong_FromDouble(3600000000.0); - if (us_per_hour == NULL) { - goto error; + mstate->us_per_hour = PyLong_FromDouble(3600000000.0); + if (mstate->us_per_hour == NULL) { + return -1; } - us_per_day = PyLong_FromDouble(86400000000.0); - if (us_per_day == NULL) { - goto error; + mstate->us_per_day = PyLong_FromDouble(86400000000.0); + if (mstate->us_per_day == NULL) { + return -1; } - us_per_week = PyLong_FromDouble(604800000000.0); - if (us_per_week == NULL) { - goto error; + mstate->us_per_week = PyLong_FromDouble(604800000000.0); + if (mstate->us_per_week == NULL) { + return -1; } return 0; - -error: - Py_XDECREF(us_per_ms); - Py_XDECREF(us_per_second); - Py_XDECREF(us_per_minute); - Py_XDECREF(us_per_hour); - Py_XDECREF(us_per_day); - Py_XDECREF(us_per_week); - Py_XDECREF(seconds_per_day); - return -1; } @@ -6909,6 +6930,7 @@ static int datetime_traverse(PyObject *mod, visitproc visit, void *arg) { DateTimeState *state = get_module_state(mod); + Py_VISIT(state->DateType); Py_VISIT(state->DateTimeType); Py_VISIT(state->DeltaType); @@ -6916,6 +6938,18 @@ datetime_traverse(PyObject *mod, visitproc visit, void *arg) Py_VISIT(state->TimeType); Py_VISIT(state->TZInfoType); Py_VISIT(state->TimeZoneType); + + Py_VISIT(state->us_per_ms); + Py_VISIT(state->us_per_second); + Py_VISIT(state->us_per_minute); + Py_VISIT(state->us_per_hour); + Py_VISIT(state->us_per_day); + Py_VISIT(state->us_per_week); + Py_VISIT(state->seconds_per_day); + + Py_VISIT(state->PyDateTime_TimeZone_UTC); + Py_VISIT(state->PyDateTime_Epoch); + return 0; } @@ -6923,6 +6957,7 @@ static int datetime_clear(PyObject *mod) { DateTimeState *state = get_module_state(mod); + Py_CLEAR(state->DateType); Py_CLEAR(state->DateTimeType); Py_CLEAR(state->DeltaType); @@ -6930,6 +6965,18 @@ datetime_clear(PyObject *mod) Py_CLEAR(state->TimeType); Py_CLEAR(state->TZInfoType); Py_CLEAR(state->TimeZoneType); + + Py_CLEAR(state->us_per_ms); + Py_CLEAR(state->us_per_second); + Py_CLEAR(state->us_per_minute); + Py_CLEAR(state->us_per_hour); + Py_CLEAR(state->us_per_day); + Py_CLEAR(state->us_per_week); + Py_CLEAR(state->seconds_per_day); + + Py_CLEAR(state->PyDateTime_TimeZone_UTC); + Py_CLEAR(state->PyDateTime_Epoch); + return 0; } From 1b4c96a907eccd0e62a8edf1b87dc0dbe5e81cd9 Mon Sep 17 00:00:00 2001 From: Costas Date: Fri, 6 Oct 2023 01:23:20 +0100 Subject: [PATCH 4/8] fix: clean up the macros --- Modules/_datetimemodule.c | 126 +++++++++++++++++--------------------- 1 file changed, 55 insertions(+), 71 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 3f86e3cb2eb46f..586b9fb6bc0db9 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -23,22 +23,22 @@ # include /* struct timeval */ #endif -#define PyDate_Check(op) PyObject_TypeCheck(op, mstate->DateType) -#define PyDate_CheckExact(op) Py_IS_TYPE(op, mstate->DateType) +#define PyDate_Check(op, mstate) PyObject_TypeCheck(op, mstate->DateType) +#define PyDate_CheckExact(op, mstate) Py_IS_TYPE(op, mstate->DateType) -#define PyDateTime_Check(op) PyObject_TypeCheck(op, mstate->DateTimeType) -#define PyDateTime_CheckExact(op) Py_IS_TYPE(op, mstate->DateTimeType) +#define PyDateTime_Check(op, mstate) PyObject_TypeCheck(op, mstate->DateTimeType) +#define PyDateTime_CheckExact(op, mstate) Py_IS_TYPE(op, mstate->DateTimeType) -#define PyTime_Check(op) PyObject_TypeCheck(op, mstate->TimeType) -#define PyTime_CheckExact(op) Py_IS_TYPE(op, mstate->TimeType) +#define PyTime_Check(op, mstate) PyObject_TypeCheck(op, mstate->TimeType) +#define PyTime_CheckExact(op, mstate) Py_IS_TYPE(op, mstate->TimeType) -#define PyDelta_Check(op) PyObject_TypeCheck(op, mstate->DeltaType) -#define PyDelta_CheckExact(op) Py_IS_TYPE(op, mstate->DeltaType) +#define PyDelta_Check(op, mstate) PyObject_TypeCheck(op, mstate->DeltaType) +#define PyDelta_CheckExact(op, mstate) Py_IS_TYPE(op, mstate->DeltaType) -#define PyTZInfo_Check(op) PyObject_TypeCheck(op, mstate->TZInfoType) -#define PyTZInfo_CheckExact(op) Py_IS_TYPE(op, mstate->TZInfoType) +#define PyTZInfo_Check(op, mstate) PyObject_TypeCheck(op, mstate->TZInfoType) +#define PyTZInfo_CheckExact(op, mstate) Py_IS_TYPE(op, mstate->TZInfoType) -#define PyTimezone_Check(op) PyObject_TypeCheck(op, mstate->TimeZoneType) +#define PyTimezone_Check(op, mstate) PyObject_TypeCheck(op, mstate->TimeZoneType) /*[clinic input] module datetime @@ -1205,10 +1205,6 @@ typedef struct PyObject *name; } PyDateTime_TimeZone; -/* The interned UTC timezone instance */ -// static PyObject *PyDateTime_TimeZone_UTC; -/* The interned Epoch datetime instance */ -// static PyObject *PyDateTime_Epoch; /* Create new timezone instance checking offset range. This function does not check the name argument. Caller must assure @@ -1222,7 +1218,7 @@ create_timezone(PyObject *offset, PyObject *name) PyTypeObject *type = mstate->TimeZoneType; assert(offset != NULL); - assert(PyDelta_Check(offset)); + assert(PyDelta_Check(offset, mstate)); assert(name == NULL || PyUnicode_Check(name)); self = (PyDateTime_TimeZone *)(type->tp_alloc(type, 0)); @@ -1240,7 +1236,7 @@ static PyObject * new_timezone(PyObject *offset, PyObject *name) { assert(offset != NULL); - assert(PyDelta_Check(offset)); + assert(PyDelta_Check(offset, mstate)); assert(name == NULL || PyUnicode_Check(name)); DateTimeState *mstate = find_module_state_by_def(Py_TYPE(offset)); @@ -1272,7 +1268,7 @@ new_timezone(PyObject *offset, PyObject *name) static int check_tzinfo_subclass(PyObject *p, DateTimeState *mstate) { - if (p == Py_None || PyTZInfo_Check(p)) + if (p == Py_None || PyTZInfo_Check(p, mstate)) return 0; PyErr_Format(PyExc_TypeError, "tzinfo argument must be None or of a tzinfo subclass, " @@ -1292,9 +1288,9 @@ get_tzinfo_member(PyObject *self) PyObject *tzinfo = NULL; - if (PyDateTime_Check(self) && HASTZINFO(self)) + if (PyDateTime_Check(self, mstate) && HASTZINFO(self)) tzinfo = ((PyDateTime_DateTime *)self)->tzinfo; - else if (PyTime_Check(self) && HASTZINFO(self)) + else if (PyTime_Check(self, mstate) && HASTZINFO(self)) tzinfo = ((PyDateTime_Time *)self)->tzinfo; return tzinfo; @@ -1319,12 +1315,12 @@ call_tzinfo_method(PyObject *tzinfo, const char *name, PyObject *tzinfoarg) assert(tzinfoarg != NULL); DateTimeState *mstate = find_module_state_by_def(Py_TYPE(tzinfo)); - assert(PyTZInfo_Check(tzinfo)); + assert(PyTZInfo_Check(tzinfo, mstate)); offset = PyObject_CallMethod(tzinfo, name, "O", tzinfoarg); if (offset == Py_None || offset == NULL) return offset; - if (PyDelta_Check(offset)) { + if (PyDelta_Check(offset, mstate)) { if ((GET_TD_DAYS(offset) == -1 && GET_TD_SECONDS(offset) == 0 && GET_TD_MICROSECONDS(offset) < 1) || @@ -1638,9 +1634,9 @@ make_freplacement(PyObject *object) DateTimeState *mstate = find_module_state_by_def(Py_TYPE(object)); char freplacement[64]; - if (PyTime_Check(object)) + if (PyTime_Check(object, mstate)) sprintf(freplacement, "%06d", TIME_GET_MICROSECOND(object)); - else if (PyDateTime_Check(object)) + else if (PyDateTime_Check(object, mstate)) sprintf(freplacement, "%06d", DATE_GET_MICROSECOND(object)); else sprintf(freplacement, "%06d", 0); @@ -1887,18 +1883,6 @@ cmperror(PyObject *a, PyObject *b) return NULL; } -/* --------------------------------------------------------------------------- - * Cached Python objects; these are set by the module init function. - */ - -/* Conversion factors. */ -// static PyObject *us_per_ms = NULL; /* 1000 */ -// static PyObject *us_per_second = NULL; /* 1000000 */ -// static PyObject *us_per_minute = NULL; /* 1e6 * 60 as Python int */ -// static PyObject *us_per_hour = NULL; /* 1e6 * 3600 as Python int */ -// static PyObject *us_per_day = NULL; /* 1e6 * 3600 * 24 as Python int */ -// static PyObject *us_per_week = NULL; /* 1e6*3600*24*7 as Python int */ -// static PyObject *seconds_per_day = NULL; /* 3600*24 as Python int */ /* --------------------------------------------------------------------------- * Class implementations. @@ -2237,7 +2221,7 @@ delta_add(PyObject *left, PyObject *right) DateTimeState *mstate = find_module_state_by_objs(left, right); - if (PyDelta_Check(left) && PyDelta_Check(right)) { + if (PyDelta_Check(left, mstate) && PyDelta_Check(right, mstate)) { /* delta + delta */ /* The C-level additions can't overflow because of the * invariant bounds. @@ -2302,7 +2286,7 @@ delta_subtract(PyObject *left, PyObject *right) DateTimeState *mstate = find_module_state_by_objs(left, right); - if (PyDelta_Check(left) && PyDelta_Check(right)) { + if (PyDelta_Check(left, mstate) && PyDelta_Check(right, mstate)) { /* delta - delta */ /* The C-level additions can't overflow because of the * invariant bounds. @@ -2337,7 +2321,7 @@ delta_richcompare(PyObject *self, PyObject *other, int op) { DateTimeState *mstate = find_module_state_by_def(Py_TYPE(self)); - if (PyDelta_Check(other)) { + if (PyDelta_Check(other, mstate)) { int diff = delta_cmp(self, other); return diff_to_bool(diff, op); } @@ -2368,7 +2352,7 @@ delta_multiply(PyObject *left, PyObject *right) DateTimeState *mstate = find_module_state_by_objs(left, right); - if (PyDelta_Check(left)) { + if (PyDelta_Check(left, mstate)) { /* delta * ??? */ if (PyLong_Check(right)) result = multiply_int_timedelta(right, @@ -2396,13 +2380,13 @@ delta_divide(PyObject *left, PyObject *right) DateTimeState *mstate = find_module_state_by_objs(left, right); - if (PyDelta_Check(left)) { + if (PyDelta_Check(left, mstate)) { /* delta * ??? */ if (PyLong_Check(right)) result = divide_timedelta_int( (PyDateTime_Delta *)left, right); - else if (PyDelta_Check(right)) + else if (PyDelta_Check(right, mstate)) result = divide_timedelta_timedelta( (PyDateTime_Delta *)left, (PyDateTime_Delta *)right); @@ -2420,8 +2404,8 @@ delta_truedivide(PyObject *left, PyObject *right) DateTimeState *mstate = find_module_state_by_objs(left, right); - if (PyDelta_Check(left)) { - if (PyDelta_Check(right)) + if (PyDelta_Check(left, mstate)) { + if (PyDelta_Check(right, mstate)) result = truedivide_timedelta_timedelta( (PyDateTime_Delta *)left, (PyDateTime_Delta *)right); @@ -2448,7 +2432,7 @@ delta_remainder(PyObject *left, PyObject *right) DateTimeState *mstate = find_module_state_by_objs(left, right); - if (!PyDelta_Check(left) || !PyDelta_Check(right)) + if (!PyDelta_Check(left, mstate) || !PyDelta_Check(right, mstate)) Py_RETURN_NOTIMPLEMENTED; pyus_left = delta_to_microseconds((PyDateTime_Delta *)left); @@ -2486,7 +2470,7 @@ delta_divmod(PyObject *left, PyObject *right) DateTimeState *mstate = find_module_state_by_objs(left, right); - if (!PyDelta_Check(left) || !PyDelta_Check(right)) + if (!PyDelta_Check(left, mstate) || !PyDelta_Check(right, mstate)) Py_RETURN_NOTIMPLEMENTED; pyus_left = delta_to_microseconds((PyDateTime_Delta *)left); @@ -3223,12 +3207,12 @@ date_add(PyObject *left, PyObject *right) { DateTimeState *mstate = find_module_state_by_objs(left, right); - if (PyDateTime_Check(left) || PyDateTime_Check(right)) + if (PyDateTime_Check(left, mstate) || PyDateTime_Check(right, mstate)) Py_RETURN_NOTIMPLEMENTED; - if (PyDate_Check(left)) { + if (PyDate_Check(left, mstate)) { /* date + ??? */ - if (PyDelta_Check(right)) + if (PyDelta_Check(right, mstate)) /* date + delta */ return add_date_timedelta((PyDateTime_Date *) left, (PyDateTime_Delta *) right, @@ -3238,7 +3222,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(left, mstate)) /* delta + date */ return add_date_timedelta((PyDateTime_Date *) right, (PyDateTime_Delta *) left, @@ -3252,11 +3236,11 @@ date_subtract(PyObject *left, PyObject *right) { DateTimeState *mstate = find_module_state_by_objs(left, right); - if (PyDateTime_Check(left) || PyDateTime_Check(right)) + if (PyDateTime_Check(left, mstate) || PyDateTime_Check(right, mstate)) Py_RETURN_NOTIMPLEMENTED; - if (PyDate_Check(left)) { - if (PyDate_Check(right)) { + if (PyDate_Check(left, mstate)) { + if (PyDate_Check(right, mstate)) { /* date - date */ int left_ord = ymd_to_ord(GET_YEAR(left), GET_MONTH(left), @@ -3266,7 +3250,7 @@ date_subtract(PyObject *left, PyObject *right) GET_DAY(right)); return new_delta(left_ord - right_ord, 0, 0, 0); } - if (PyDelta_Check(right)) { + if (PyDelta_Check(right, mstate)) { /* date - delta */ return add_date_timedelta((PyDateTime_Date *) left, (PyDateTime_Delta *) right, @@ -3528,7 +3512,7 @@ date_richcompare(PyObject *self, PyObject *other, int op) { DateTimeState *mstate = find_module_state_by_def(Py_TYPE(self)); - if (PyDate_Check(other)) { + if (PyDate_Check(other, mstate)) { int diff = memcmp(((PyDateTime_Date *)self)->data, ((PyDateTime_Date *)other)->data, _PyDateTime_DATE_DATASIZE); @@ -3774,7 +3758,7 @@ tzinfo_fromutc(PyDateTime_TZInfo *self, PyObject *dt) DateTimeState *mstate = find_module_state_by_def(Py_TYPE(self)); - if (!PyDateTime_Check(dt)) { + if (!PyDateTime_Check(dt, mstate)) { PyErr_SetString(PyExc_TypeError, "fromutc: argument must be a datetime"); return NULL; @@ -3946,7 +3930,7 @@ timezone_richcompare(PyDateTime_TimeZone *self, if (op != Py_EQ && op != Py_NE) Py_RETURN_NOTIMPLEMENTED; DateTimeState *mstate = find_module_state_by_def(Py_TYPE(self)); - if (!PyTimezone_Check(other)) { + if (!PyTimezone_Check(other, mstate)) { Py_RETURN_NOTIMPLEMENTED; } return delta_richcompare(self->offset, other->offset, op); @@ -3965,7 +3949,7 @@ timezone_hash(PyDateTime_TimeZone *self) static int _timezone_check_argument(PyObject *dt, const char *meth, DateTimeState *mstate) { - if (dt == Py_None || PyDateTime_Check(dt)) + if (dt == Py_None || PyDateTime_Check(dt, mstate)) return 0; PyErr_Format(PyExc_TypeError, "%s(dt) argument must be a datetime instance" " or None, not %.200s", meth, Py_TYPE(dt)->tp_name); @@ -4077,7 +4061,7 @@ timezone_fromutc(PyDateTime_TimeZone *self, PyDateTime_DateTime *dt) { DateTimeState *mstate = find_module_state_by_def(Py_TYPE(self)); - if (!PyDateTime_Check(dt)) { + if (!PyDateTime_Check(dt, mstate)) { PyErr_SetString(PyExc_TypeError, "fromutc: argument must be a datetime"); return NULL; @@ -4476,7 +4460,7 @@ time_richcompare(PyObject *self, PyObject *other, int op) DateTimeState *mstate = find_module_state_by_def(Py_TYPE(self)); - if (! PyTime_Check(other)) + if (! PyTime_Check(other, mstate)) Py_RETURN_NOTIMPLEMENTED; if (GET_TIME_TZINFO(self) == GET_TIME_TZINFO(other)) { @@ -4496,7 +4480,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(offset1, mstate) && PyDelta_Check(offset2, mstate) && delta_cmp(offset1, offset2) == 0)) { diff = memcmp(((PyDateTime_Time *)self)->data, ((PyDateTime_Time *)other)->data, @@ -5607,16 +5591,16 @@ datetime_add(PyObject *left, PyObject *right) { DateTimeState *mstate = find_module_state_by_objs(left, right); - if (PyDateTime_Check(left)) { + if (PyDateTime_Check(left, mstate)) { /* datetime + ??? */ - if (PyDelta_Check(right)) + if (PyDelta_Check(right, mstate)) /* datetime + delta */ return add_datetime_timedelta( (PyDateTime_DateTime *)left, (PyDateTime_Delta *)right, 1); } - else if (PyDelta_Check(left)) { + else if (PyDelta_Check(left, mstate)) { /* delta + datetime */ return add_datetime_timedelta((PyDateTime_DateTime *) right, (PyDateTime_Delta *) left, @@ -5632,9 +5616,9 @@ datetime_subtract(PyObject *left, PyObject *right) DateTimeState *mstate = find_module_state_by_objs(left, right); - if (PyDateTime_Check(left)) { + if (PyDateTime_Check(left, mstate)) { /* datetime - ??? */ - if (PyDateTime_Check(right)) { + if (PyDateTime_Check(right, mstate)) { /* datetime - datetime */ PyObject *offset1, *offset2, *offdiff = NULL; int delta_d, delta_s, delta_us; @@ -5699,7 +5683,7 @@ datetime_subtract(PyObject *left, PyObject *right) Py_DECREF(offdiff); } } - else if (PyDelta_Check(right)) { + else if (PyDelta_Check(right, mstate)) { /* datetime - delta */ result = add_datetime_timedelta( (PyDateTime_DateTime *)left, @@ -5916,8 +5900,8 @@ datetime_richcompare(PyObject *self, PyObject *other, int op) DateTimeState *mstate = find_module_state_by_def(Py_TYPE(self)); - if (! PyDateTime_Check(other)) { - if (PyDate_Check(other)) { + if (! PyDateTime_Check(other, mstate)) { + if (PyDate_Check(other, mstate)) { /* Prevent invocation of date_richcompare. We want to return NotImplemented here to give the other object a chance. But since DateTime is a subclass of @@ -5951,7 +5935,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(offset1, mstate) && PyDelta_Check(offset2, mstate) && delta_cmp(offset1, offset2) == 0)) { diff = memcmp(((PyDateTime_DateTime *)self)->data, ((PyDateTime_DateTime *)other)->data, @@ -6280,7 +6264,7 @@ datetime_astimezone(PyDateTime_DateTime *self, PyObject *args, PyObject *kw) Py_DECREF(offset); goto naive; } - else if (!PyDelta_Check(offset)) { + else if (!PyDelta_Check(offset, mstate)) { Py_DECREF(offset); PyErr_Format(PyExc_TypeError, "utcoffset() returned %.200s," " expected timedelta or None", Py_TYPE(offset)->tp_name); From 94b3788693153b10106860e221ed1146f1e1849f Mon Sep 17 00:00:00 2001 From: Costas Date: Fri, 6 Oct 2023 01:29:49 +0100 Subject: [PATCH 5/8] fix: typo --- Modules/_datetimemodule.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 586b9fb6bc0db9..e0584f1ba7862e 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -1236,11 +1236,12 @@ static PyObject * new_timezone(PyObject *offset, PyObject *name) { assert(offset != NULL); - assert(PyDelta_Check(offset, mstate)); - assert(name == NULL || PyUnicode_Check(name)); DateTimeState *mstate = find_module_state_by_def(Py_TYPE(offset)); + assert(PyDelta_Check(offset, mstate)); + assert(name == NULL || PyUnicode_Check(name)); + if (name == NULL && delta_bool((PyDateTime_Delta *)offset) == 0) { return Py_NewRef(mstate->PyDateTime_TimeZone_UTC); } From 681d7c86c8f5786b420db749d05fcb0107b8580f Mon Sep 17 00:00:00 2001 From: Costas Date: Fri, 6 Oct 2023 01:54:10 +0100 Subject: [PATCH 6/8] fix: brackets in macros just in case --- Modules/_datetimemodule.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index e0584f1ba7862e..2d354b7b373426 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -23,22 +23,22 @@ # include /* struct timeval */ #endif -#define PyDate_Check(op, mstate) PyObject_TypeCheck(op, mstate->DateType) -#define PyDate_CheckExact(op, mstate) Py_IS_TYPE(op, mstate->DateType) +#define PyDate_Check(op, mstate) PyObject_TypeCheck(op, (mstate)->DateType) +#define PyDate_CheckExact(op, mstate) Py_IS_TYPE(op, (mstate)->DateType) -#define PyDateTime_Check(op, mstate) PyObject_TypeCheck(op, mstate->DateTimeType) -#define PyDateTime_CheckExact(op, mstate) Py_IS_TYPE(op, mstate->DateTimeType) +#define PyDateTime_Check(op, mstate) PyObject_TypeCheck(op, (mstate)->DateTimeType) +#define PyDateTime_CheckExact(op, mstate) Py_IS_TYPE(op, (mstate)->DateTimeType) -#define PyTime_Check(op, mstate) PyObject_TypeCheck(op, mstate->TimeType) -#define PyTime_CheckExact(op, mstate) Py_IS_TYPE(op, mstate->TimeType) +#define PyTime_Check(op, mstate) PyObject_TypeCheck(op, (mstate)->TimeType) +#define PyTime_CheckExact(op, mstate) Py_IS_TYPE(op, (mstate)->TimeType) -#define PyDelta_Check(op, mstate) PyObject_TypeCheck(op, mstate->DeltaType) -#define PyDelta_CheckExact(op, mstate) Py_IS_TYPE(op, mstate->DeltaType) +#define PyDelta_Check(op, mstate) PyObject_TypeCheck(op, (mstate)->DeltaType) +#define PyDelta_CheckExact(op, mstate) Py_IS_TYPE(op, (mstate)->DeltaType) -#define PyTZInfo_Check(op, mstate) PyObject_TypeCheck(op, mstate->TZInfoType) -#define PyTZInfo_CheckExact(op, mstate) Py_IS_TYPE(op, mstate->TZInfoType) +#define PyTZInfo_Check(op, mstate) PyObject_TypeCheck(op, (mstate)->TZInfoType) +#define PyTZInfo_CheckExact(op, mstate) Py_IS_TYPE(op, (mstate)->TZInfoType) -#define PyTimezone_Check(op, mstate) PyObject_TypeCheck(op, mstate->TimeZoneType) +#define PyTimezone_Check(op, mstate) PyObject_TypeCheck(op, (mstate)->TimeZoneType) /*[clinic input] module datetime From 68c7ec1b0988e8207f281eedf902d93940176091 Mon Sep 17 00:00:00 2001 From: Costas Date: Fri, 6 Oct 2023 02:15:46 +0100 Subject: [PATCH 7/8] fix: ZoneInfo warning about base needing to be immutable --- Modules/_datetimemodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c index 2d354b7b373426..ec84dacd6cde3a 100644 --- a/Modules/_datetimemodule.c +++ b/Modules/_datetimemodule.c @@ -3895,7 +3895,7 @@ static PyType_Spec PyDateTime_TZInfoType_Spec = { "datetime.tzinfo", sizeof(PyDateTime_TZInfo), 0, - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_IMMUTABLETYPE, tzinfo_slots }; From 2093f8a70e22df650a7f2d5ce3a4bb723ca898b8 Mon Sep 17 00:00:00 2001 From: Costas Date: Fri, 6 Oct 2023 02:43:27 +0100 Subject: [PATCH 8/8] chore: add news blurb --- .../2023-10-06-02-41-31.gh-issue-110415.rALDjU.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-10-06-02-41-31.gh-issue-110415.rALDjU.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-10-06-02-41-31.gh-issue-110415.rALDjU.rst b/Misc/NEWS.d/next/Core and Builtins/2023-10-06-02-41-31.gh-issue-110415.rALDjU.rst new file mode 100644 index 00000000000000..639476ab0203e7 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-10-06-02-41-31.gh-issue-110415.rALDjU.rst @@ -0,0 +1 @@ +Fixed crash when importing datetime from multiple sub-interpreters.