Skip to content

Commit 548a11d

Browse files
gh-117398: Convert datetime.IsoCalendarDate To A Heap Type (gh-119637)
This is the only static type in the module that we will not keep static.
1 parent 606be66 commit 548a11d

File tree

1 file changed

+60
-22
lines changed

1 file changed

+60
-22
lines changed

Modules/_datetimemodule.c

Lines changed: 60 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,18 @@
2626
#endif
2727

2828
typedef struct {
29+
/* Static types exposed by the datetime C-API. */
2930
PyTypeObject *date_type;
3031
PyTypeObject *datetime_type;
3132
PyTypeObject *delta_type;
32-
PyTypeObject *isocalendar_date_type;
3333
PyTypeObject *time_type;
3434
PyTypeObject *tzinfo_type;
35+
/* Exposed indirectly via TimeZone_UTC. */
3536
PyTypeObject *timezone_type;
3637

38+
/* Other module classes. */
39+
PyTypeObject *isocalendar_date_type;
40+
3741
/* Conversion factors. */
3842
PyObject *us_per_ms; // 1_000
3943
PyObject *us_per_second; // 1_000_000
@@ -3460,17 +3464,40 @@ static PyMethodDef iso_calendar_date_methods[] = {
34603464
{NULL, NULL},
34613465
};
34623466

3463-
static PyTypeObject PyDateTime_IsoCalendarDateType = {
3464-
PyVarObject_HEAD_INIT(NULL, 0)
3465-
.tp_name = "datetime.IsoCalendarDate",
3466-
.tp_basicsize = sizeof(PyDateTime_IsoCalendarDate),
3467-
.tp_repr = (reprfunc) iso_calendar_date_repr,
3468-
.tp_flags = Py_TPFLAGS_DEFAULT,
3469-
.tp_doc = iso_calendar_date__doc__,
3470-
.tp_methods = iso_calendar_date_methods,
3471-
.tp_getset = iso_calendar_date_getset,
3472-
// .tp_base = &PyTuple_Type, // filled in PyInit__datetime
3473-
.tp_new = iso_calendar_date_new,
3467+
static int
3468+
iso_calendar_date_traverse(PyDateTime_IsoCalendarDate *self, visitproc visit,
3469+
void *arg)
3470+
{
3471+
Py_VISIT(Py_TYPE(self));
3472+
return PyTuple_Type.tp_traverse((PyObject *)self, visit, arg);
3473+
}
3474+
3475+
static void
3476+
iso_calendar_date_dealloc(PyDateTime_IsoCalendarDate *self)
3477+
{
3478+
PyTypeObject *tp = Py_TYPE(self);
3479+
PyTuple_Type.tp_dealloc((PyObject *)self); // delegate GC-untrack as well
3480+
Py_DECREF(tp);
3481+
}
3482+
3483+
static PyType_Slot isocal_slots[] = {
3484+
{Py_tp_repr, iso_calendar_date_repr},
3485+
{Py_tp_doc, (void *)iso_calendar_date__doc__},
3486+
{Py_tp_methods, iso_calendar_date_methods},
3487+
{Py_tp_getset, iso_calendar_date_getset},
3488+
{Py_tp_new, iso_calendar_date_new},
3489+
{Py_tp_dealloc, iso_calendar_date_dealloc},
3490+
{Py_tp_traverse, iso_calendar_date_traverse},
3491+
{0, NULL},
3492+
};
3493+
3494+
static PyType_Spec isocal_spec = {
3495+
.name = "datetime.IsoCalendarDate",
3496+
.basicsize = sizeof(PyDateTime_IsoCalendarDate),
3497+
.flags = (Py_TPFLAGS_DEFAULT |
3498+
Py_TPFLAGS_HAVE_GC |
3499+
Py_TPFLAGS_IMMUTABLETYPE),
3500+
.slots = isocal_slots,
34743501
};
34753502

34763503
/*[clinic input]
@@ -6842,22 +6869,25 @@ create_timezone_from_delta(int days, int sec, int ms, int normalize)
68426869
}
68436870

68446871
static int
6845-
init_state(datetime_state *st)
6872+
init_state(datetime_state *st, PyTypeObject *PyDateTime_IsoCalendarDateType)
68466873
{
68476874
// While datetime uses global module "state", we unly initialize it once.
68486875
// The PyLong objects created here (once per process) are not decref'd.
68496876
if (st->initialized) {
68506877
return 0;
68516878
}
68526879

6880+
/* Static types exposed by the C-API. */
68536881
st->date_type = &PyDateTime_DateType;
68546882
st->datetime_type = &PyDateTime_DateTimeType;
68556883
st->delta_type = &PyDateTime_DeltaType;
6856-
st->isocalendar_date_type = &PyDateTime_IsoCalendarDateType;
68576884
st->time_type = &PyDateTime_TimeType;
68586885
st->tzinfo_type = &PyDateTime_TZInfoType;
68596886
st->timezone_type = &PyDateTime_TimeZoneType;
68606887

6888+
/* Per-module heap types. */
6889+
st->isocalendar_date_type = PyDateTime_IsoCalendarDateType;
6890+
68616891
st->us_per_ms = PyLong_FromLong(1000);
68626892
if (st->us_per_ms == NULL) {
68636893
return -1;
@@ -6914,11 +6944,10 @@ _datetime_exec(PyObject *module)
69146944
// `&...` is not a constant expression according to a strict reading
69156945
// of C standards. Fill tp_base at run-time rather than statically.
69166946
// See https://bugs.python.org/issue40777
6917-
PyDateTime_IsoCalendarDateType.tp_base = &PyTuple_Type;
69186947
PyDateTime_TimeZoneType.tp_base = &PyDateTime_TZInfoType;
69196948
PyDateTime_DateTimeType.tp_base = &PyDateTime_DateType;
69206949

6921-
PyTypeObject *types[] = {
6950+
PyTypeObject *capi_types[] = {
69226951
&PyDateTime_DateType,
69236952
&PyDateTime_DateTimeType,
69246953
&PyDateTime_TimeType,
@@ -6927,18 +6956,27 @@ _datetime_exec(PyObject *module)
69276956
&PyDateTime_TimeZoneType,
69286957
};
69296958

6930-
for (size_t i = 0; i < Py_ARRAY_LENGTH(types); i++) {
6931-
if (PyModule_AddType(module, types[i]) < 0) {
6959+
for (size_t i = 0; i < Py_ARRAY_LENGTH(capi_types); i++) {
6960+
if (PyModule_AddType(module, capi_types[i]) < 0) {
69326961
goto error;
69336962
}
69346963
}
69356964

6936-
if (PyType_Ready(&PyDateTime_IsoCalendarDateType) < 0) {
6937-
goto error;
6938-
}
6965+
#define CREATE_TYPE(VAR, SPEC, BASE) \
6966+
do { \
6967+
VAR = (PyTypeObject *)PyType_FromModuleAndSpec( \
6968+
module, SPEC, (PyObject *)BASE); \
6969+
if (VAR == NULL) { \
6970+
goto error; \
6971+
} \
6972+
} while (0)
6973+
6974+
PyTypeObject *PyDateTime_IsoCalendarDateType = NULL;
6975+
CREATE_TYPE(PyDateTime_IsoCalendarDateType, &isocal_spec, &PyTuple_Type);
6976+
#undef CREATE_TYPE
69396977

69406978
datetime_state *st = get_datetime_state();
6941-
if (init_state(st) < 0) {
6979+
if (init_state(st, PyDateTime_IsoCalendarDateType) < 0) {
69426980
goto error;
69436981
}
69446982

0 commit comments

Comments
 (0)