aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Include/internal/pycore_global_objects_fini_generated.h1
-rw-r--r--Include/internal/pycore_global_strings.h1
-rw-r--r--Include/internal/pycore_runtime_init_generated.h1
-rw-r--r--Include/internal/pycore_unicodeobject_generated.h3
-rw-r--r--Modules/_datetimemodule.c532
5 files changed, 376 insertions, 162 deletions
diff --git a/Include/internal/pycore_global_objects_fini_generated.h b/Include/internal/pycore_global_objects_fini_generated.h
index 45a1429ad49..a40f007bdac 100644
--- a/Include/internal/pycore_global_objects_fini_generated.h
+++ b/Include/internal/pycore_global_objects_fini_generated.h
@@ -828,6 +828,7 @@ _PyStaticObjects_CheckRefcnt(PyInterpreterState *interp) {
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(c_call));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(c_exception));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(c_return));
+ _PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(cached_datetime_module));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(cached_statements));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(cadata));
_PyStaticObject_CheckRefcnt((PyObject *)&_Py_ID(cafile));
diff --git a/Include/internal/pycore_global_strings.h b/Include/internal/pycore_global_strings.h
index 86d9ffdd72f..eaf81e4f115 100644
--- a/Include/internal/pycore_global_strings.h
+++ b/Include/internal/pycore_global_strings.h
@@ -317,6 +317,7 @@ struct _Py_global_strings {
STRUCT_FOR_ID(c_call)
STRUCT_FOR_ID(c_exception)
STRUCT_FOR_ID(c_return)
+ STRUCT_FOR_ID(cached_datetime_module)
STRUCT_FOR_ID(cached_statements)
STRUCT_FOR_ID(cadata)
STRUCT_FOR_ID(cafile)
diff --git a/Include/internal/pycore_runtime_init_generated.h b/Include/internal/pycore_runtime_init_generated.h
index d52d2938bc0..b18c2684691 100644
--- a/Include/internal/pycore_runtime_init_generated.h
+++ b/Include/internal/pycore_runtime_init_generated.h
@@ -826,6 +826,7 @@ extern "C" {
INIT_ID(c_call), \
INIT_ID(c_exception), \
INIT_ID(c_return), \
+ INIT_ID(cached_datetime_module), \
INIT_ID(cached_statements), \
INIT_ID(cadata), \
INIT_ID(cafile), \
diff --git a/Include/internal/pycore_unicodeobject_generated.h b/Include/internal/pycore_unicodeobject_generated.h
index c4f79bf91e5..87e22ac39c1 100644
--- a/Include/internal/pycore_unicodeobject_generated.h
+++ b/Include/internal/pycore_unicodeobject_generated.h
@@ -792,6 +792,9 @@ _PyUnicode_InitStaticStrings(PyInterpreterState *interp) {
string = &_Py_ID(c_return);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
+ string = &_Py_ID(cached_datetime_module);
+ assert(_PyUnicode_CheckConsistency(string, 1));
+ _PyUnicode_InternInPlace(interp, &string);
string = &_Py_ID(cached_statements);
assert(_PyUnicode_CheckConsistency(string, 1));
_PyUnicode_InternInPlace(interp, &string);
diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c
index 466382b5148..16bb4c6980a 100644
--- a/Modules/_datetimemodule.c
+++ b/Modules/_datetimemodule.c
@@ -25,17 +25,18 @@
# include <winsock2.h> /* struct timeval */
#endif
+
+/* forward declarations */
+static PyTypeObject PyDateTime_DateType;
+static PyTypeObject PyDateTime_DateTimeType;
+static PyTypeObject PyDateTime_TimeType;
+static PyTypeObject PyDateTime_DeltaType;
+static PyTypeObject PyDateTime_TZInfoType;
+static PyTypeObject PyDateTime_TimeZoneType;
+
+
typedef struct {
- /* Static types exposed by the datetime C-API. */
- PyTypeObject *date_type;
- PyTypeObject *datetime_type;
- PyTypeObject *delta_type;
- PyTypeObject *time_type;
- PyTypeObject *tzinfo_type;
- /* Exposed indirectly via TimeZone_UTC. */
- PyTypeObject *timezone_type;
-
- /* Other module classes. */
+ /* Module heap types. */
PyTypeObject *isocalendar_date_type;
/* Conversion factors. */
@@ -47,39 +48,182 @@ typedef struct {
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 *utc;
-
/* The interned Unix epoch datetime instance */
PyObject *epoch;
-
- /* While we use a global state, we ensure it's only initialized once */
- int initialized;
} datetime_state;
-static datetime_state _datetime_global_state;
+/* The module has a fixed number of static objects, due to being exposed
+ * through the datetime C-API. There are five types exposed directly,
+ * one type exposed indirectly, and one singleton constant (UTC).
+ *
+ * Each of these objects is hidden behind a macro in the same way as
+ * the per-module objects stored in module state. The macros for the
+ * static objects don't need to be passed a state, but the consistency
+ * of doing so is more clear. We use a dedicated noop macro, NO_STATE,
+ * to make the special case obvious. */
+
+#define NO_STATE NULL
+
+#define DATE_TYPE(st) &PyDateTime_DateType
+#define DATETIME_TYPE(st) &PyDateTime_DateTimeType
+#define TIME_TYPE(st) &PyDateTime_TimeType
+#define DELTA_TYPE(st) &PyDateTime_DeltaType
+#define TZINFO_TYPE(st) &PyDateTime_TZInfoType
+#define TIMEZONE_TYPE(st) &PyDateTime_TimeZoneType
+#define ISOCALENDAR_DATE_TYPE(st) st->isocalendar_date_type
+
+#define PyDate_Check(op) PyObject_TypeCheck(op, DATE_TYPE(NO_STATE))
+#define PyDate_CheckExact(op) Py_IS_TYPE(op, DATE_TYPE(NO_STATE))
+
+#define PyDateTime_Check(op) PyObject_TypeCheck(op, DATETIME_TYPE(NO_STATE))
+#define PyDateTime_CheckExact(op) Py_IS_TYPE(op, DATETIME_TYPE(NO_STATE))
+
+#define PyTime_Check(op) PyObject_TypeCheck(op, TIME_TYPE(NO_STATE))
+#define PyTime_CheckExact(op) Py_IS_TYPE(op, TIME_TYPE(NO_STATE))
+
+#define PyDelta_Check(op) PyObject_TypeCheck(op, DELTA_TYPE(NO_STATE))
+#define PyDelta_CheckExact(op) Py_IS_TYPE(op, DELTA_TYPE(NO_STATE))
+
+#define PyTZInfo_Check(op) PyObject_TypeCheck(op, TZINFO_TYPE(NO_STATE))
+#define PyTZInfo_CheckExact(op) Py_IS_TYPE(op, TZINFO_TYPE(NO_STATE))
+
+#define PyTimezone_Check(op) PyObject_TypeCheck(op, TIMEZONE_TYPE(NO_STATE))
+
+#define CONST_US_PER_MS(st) st->us_per_ms
+#define CONST_US_PER_SECOND(st) st->us_per_second
+#define CONST_US_PER_MINUTE(st) st->us_per_minute
+#define CONST_US_PER_HOUR(st) st->us_per_hour
+#define CONST_US_PER_DAY(st) st->us_per_day
+#define CONST_US_PER_WEEK(st) st->us_per_week
+#define CONST_SEC_PER_DAY(st) st->seconds_per_day
+#define CONST_EPOCH(st) st->epoch
+#define CONST_UTC(st) ((PyObject *)&utc_timezone)
+
+static datetime_state *
+get_module_state(PyObject *module)
+{
+ void *state = _PyModule_GetState(module);
+ assert(state != NULL);
+ return (datetime_state *)state;
+}
+
+
+#define INTERP_KEY ((PyObject *)&_Py_ID(cached_datetime_module))
+
+static PyObject *
+get_current_module(PyInterpreterState *interp)
+{
+ PyObject *dict = PyInterpreterState_GetDict(interp);
+ if (dict == NULL) {
+ return NULL;
+ }
+ PyObject *ref = NULL;
+ if (PyDict_GetItemRef(dict, INTERP_KEY, &ref) < 0) {
+ return NULL;
+ }
+ if (ref == NULL) {
+ return NULL;
+ }
+ PyObject *mod = NULL;
+ (void)PyWeakref_GetRef(ref, &mod);
+ if (mod == Py_None) {
+ Py_CLEAR(mod);
+ }
+ Py_DECREF(ref);
+ return mod;
+}
+
+static PyModuleDef datetimemodule;
+
+static datetime_state *
+_get_current_state(PyObject **p_mod)
+{
+ PyInterpreterState *interp = PyInterpreterState_Get();
+ PyObject *mod = get_current_module(interp);
+ if (mod == NULL) {
+ assert(!PyErr_Occurred());
+ if (PyErr_Occurred()) {
+ return NULL;
+ }
+ /* The static types can outlive the module,
+ * so we must re-import the module. */
+ mod = PyImport_ImportModule("_datetime");
+ if (mod == NULL) {
+ return NULL;
+ }
+ }
+ datetime_state *st = get_module_state(mod);
+ *p_mod = mod;
+ return st;
+}
+
+#define GET_CURRENT_STATE(MOD_VAR) \
+ _get_current_state(&MOD_VAR)
+#define RELEASE_CURRENT_STATE(ST_VAR, MOD_VAR) \
+ Py_DECREF(MOD_VAR)
-static inline datetime_state* get_datetime_state(void)
+static int
+set_current_module(PyInterpreterState *interp, PyObject *mod)
{
- return &_datetime_global_state;
+ assert(mod != NULL);
+ PyObject *dict = PyInterpreterState_GetDict(interp);
+ if (dict == NULL) {
+ return -1;
+ }
+ PyObject *ref = PyWeakref_NewRef(mod, NULL);
+ if (ref == NULL) {
+ return -1;
+ }
+ int rc = PyDict_SetItem(dict, INTERP_KEY, ref);
+ Py_DECREF(ref);
+ return rc;
}
-#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 void
+clear_current_module(PyInterpreterState *interp, PyObject *expected)
+{
+ PyObject *exc = PyErr_GetRaisedException();
+
+ PyObject *current = NULL;
+
+ PyObject *dict = PyInterpreterState_GetDict(interp);
+ if (dict == NULL) {
+ goto error;
+ }
+
+ if (expected != NULL) {
+ PyObject *ref = NULL;
+ if (PyDict_GetItemRef(dict, INTERP_KEY, &ref) < 0) {
+ goto error;
+ }
+ if (ref != NULL) {
+ int rc = PyWeakref_GetRef(ref, &current);
+ Py_DECREF(ref);
+ if (rc < 0) {
+ goto error;
+ }
+ if (current != expected) {
+ goto finally;
+ }
+ }
+ }
-#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)
+ if (PyDict_DelItem(dict, INTERP_KEY) < 0) {
+ if (!PyErr_ExceptionMatches(PyExc_KeyError)) {
+ goto error;
+ }
+ }
-#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)
+ goto finally;
-#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)
+error:
+ PyErr_Print();
-#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)
+finally:
+ Py_XDECREF(current);
+ PyErr_SetRaisedException(exc);
+}
-#define PyTimezone_Check(op) PyObject_TypeCheck(op, get_datetime_state()->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
@@ -988,7 +1132,7 @@ new_date_ex(int year, int month, int day, PyTypeObject *type)
}
#define new_date(year, month, day) \
- new_date_ex(year, month, day, get_datetime_state()->date_type)
+ new_date_ex(year, month, day, DATE_TYPE(NO_STATE))
// Forward declaration
static PyObject *
@@ -998,13 +1142,12 @@ 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)
{
- datetime_state *st = get_datetime_state();
PyObject *result;
// We have "fast path" constructors for two subclasses: date and datetime
- if ((PyTypeObject *)cls == st->date_type) {
+ if ((PyTypeObject *)cls == DATE_TYPE(NO_STATE)) {
result = new_date_ex(year, month, day, (PyTypeObject *)cls);
}
- else if ((PyTypeObject *)cls == st->datetime_type) {
+ else if ((PyTypeObject *)cls == DATETIME_TYPE(NO_STATE)) {
result = new_datetime_ex(year, month, day, 0, 0, 0, 0, Py_None,
(PyTypeObject *)cls);
}
@@ -1058,8 +1201,7 @@ 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, \
- get_datetime_state()->datetime_type)
+ new_datetime_ex2(y, m, d, hh, mm, ss, us, tzinfo, fold, DATETIME_TYPE(NO_STATE))
static PyObject *
call_subclass_fold(PyObject *cls, int fold, const char *format, ...)
@@ -1100,9 +1242,8 @@ 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)
{
- datetime_state *st = get_datetime_state();
PyObject* dt;
- if ((PyTypeObject*)cls == st->datetime_type) {
+ if ((PyTypeObject*)cls == DATETIME_TYPE(NO_STATE)) {
// Use the fast path constructor
dt = new_datetime(year, month, day, hour, minute, second, usecond,
tzinfo, fold);
@@ -1163,16 +1304,15 @@ new_time_ex(int hour, int minute, int second, int usecond,
return new_time_ex2(hour, minute, second, usecond, tzinfo, 0, type);
}
-#define new_time(hh, mm, ss, us, tzinfo, fold) \
- new_time_ex2(hh, mm, ss, us, tzinfo, fold, get_datetime_state()->time_type)
+#define new_time(hh, mm, ss, us, tzinfo, fold) \
+ new_time_ex2(hh, mm, ss, us, tzinfo, fold, TIME_TYPE(NO_STATE))
static PyObject *
new_time_subclass_fold_ex(int hour, int minute, int second, int usecond,
PyObject *tzinfo, int fold, PyObject *cls)
{
PyObject *t;
- datetime_state *st = get_datetime_state();
- if ((PyTypeObject*)cls == st->time_type) {
+ if ((PyTypeObject*)cls == TIME_TYPE(NO_STATE)) {
// Use the fast path constructor
t = new_time(hour, minute, second, usecond, tzinfo, fold);
}
@@ -1224,7 +1364,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, get_datetime_state()->delta_type)
+ new_delta_ex(d, s, us, normalize, DELTA_TYPE(NO_STATE))
typedef struct
@@ -1244,8 +1384,7 @@ static PyObject *
create_timezone(PyObject *offset, PyObject *name)
{
PyDateTime_TimeZone *self;
- datetime_state *st = get_datetime_state();
- PyTypeObject *type = st->timezone_type;
+ PyTypeObject *type = TIMEZONE_TYPE(NO_STATE);
assert(offset != NULL);
assert(PyDelta_Check(offset));
@@ -1267,6 +1406,7 @@ create_timezone(PyObject *offset, PyObject *name)
}
static int delta_bool(PyDateTime_Delta *self);
+static PyDateTime_TimeZone utc_timezone;
static PyObject *
new_timezone(PyObject *offset, PyObject *name)
@@ -1276,8 +1416,7 @@ new_timezone(PyObject *offset, PyObject *name)
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);
+ return Py_NewRef(CONST_UTC(NO_STATE));
}
if ((GET_TD_DAYS(offset) == -1 &&
GET_TD_SECONDS(offset) == 0 &&
@@ -1490,8 +1629,7 @@ tzinfo_from_isoformat_results(int rv, int tzoffset, int tz_useconds)
if (rv == 1) {
// Create a timezone from offset in seconds (0 returns UTC)
if (tzoffset == 0) {
- datetime_state *st = get_datetime_state();
- return Py_NewRef(st->utc);
+ return Py_NewRef(CONST_UTC(NO_STATE));
}
PyObject *delta = new_delta(0, tzoffset, tz_useconds, 1);
@@ -1920,11 +2058,13 @@ delta_to_microseconds(PyDateTime_Delta *self)
PyObject *x3 = NULL;
PyObject *result = NULL;
+ PyObject *current_mod = NULL;
+ datetime_state *st = GET_CURRENT_STATE(current_mod);
+
x1 = PyLong_FromLong(GET_TD_DAYS(self));
if (x1 == NULL)
goto Done;
- datetime_state *st = get_datetime_state();
- x2 = PyNumber_Multiply(x1, st->seconds_per_day); /* days in seconds */
+ x2 = PyNumber_Multiply(x1, CONST_SEC_PER_DAY(st)); /* days in seconds */
if (x2 == NULL)
goto Done;
Py_SETREF(x1, NULL);
@@ -1941,7 +2081,7 @@ delta_to_microseconds(PyDateTime_Delta *self)
/* x1 = */ x2 = NULL;
/* x3 has days+seconds in seconds */
- x1 = PyNumber_Multiply(x3, st->us_per_second); /* us */
+ x1 = PyNumber_Multiply(x3, CONST_US_PER_SECOND(st)); /* us */
if (x1 == NULL)
goto Done;
Py_SETREF(x3, NULL);
@@ -1957,6 +2097,7 @@ Done:
Py_XDECREF(x1);
Py_XDECREF(x2);
Py_XDECREF(x3);
+ RELEASE_CURRENT_STATE(st, current_mod);
return result;
}
@@ -1996,8 +2137,10 @@ microseconds_to_delta_ex(PyObject *pyus, PyTypeObject *type)
PyObject *num = NULL;
PyObject *result = NULL;
- datetime_state *st = get_datetime_state();
- tuple = checked_divmod(pyus, st->us_per_second);
+ PyObject *current_mod = NULL;
+ datetime_state *st = GET_CURRENT_STATE(current_mod);
+
+ tuple = checked_divmod(pyus, CONST_US_PER_SECOND(st));
if (tuple == NULL) {
goto Done;
}
@@ -2015,7 +2158,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, st->seconds_per_day);
+ tuple = checked_divmod(num, CONST_SEC_PER_DAY(st));
if (tuple == NULL)
goto Done;
Py_DECREF(num);
@@ -2040,6 +2183,7 @@ microseconds_to_delta_ex(PyObject *pyus, PyTypeObject *type)
Done:
Py_XDECREF(tuple);
Py_XDECREF(num);
+ RELEASE_CURRENT_STATE(st, current_mod);
return result;
BadDivmod:
@@ -2049,7 +2193,7 @@ BadDivmod:
}
#define microseconds_to_delta(pymicros) \
- microseconds_to_delta_ex(pymicros, get_datetime_state()->delta_type)
+ microseconds_to_delta_ex(pymicros, DELTA_TYPE(NO_STATE))
static PyObject *
multiply_int_timedelta(PyObject *intobj, PyDateTime_Delta *delta)
@@ -2577,6 +2721,9 @@ delta_new(PyTypeObject *type, PyObject *args, PyObject *kw)
{
PyObject *self = NULL;
+ PyObject *current_mod = NULL;
+ datetime_state *st = GET_CURRENT_STATE(current_mod);
+
/* Argument objects. */
PyObject *day = NULL;
PyObject *second = NULL;
@@ -2615,29 +2762,28 @@ delta_new(PyTypeObject *type, PyObject *args, PyObject *kw)
y = accum("microseconds", x, us, _PyLong_GetOne(), &leftover_us);
CLEANUP;
}
- datetime_state *st = get_datetime_state();
if (ms) {
- y = accum("milliseconds", x, ms, st->us_per_ms, &leftover_us);
+ y = accum("milliseconds", x, ms, CONST_US_PER_MS(st), &leftover_us);
CLEANUP;
}
if (second) {
- y = accum("seconds", x, second, st->us_per_second, &leftover_us);
+ y = accum("seconds", x, second, CONST_US_PER_SECOND(st), &leftover_us);
CLEANUP;
}
if (minute) {
- y = accum("minutes", x, minute, st->us_per_minute, &leftover_us);
+ y = accum("minutes", x, minute, CONST_US_PER_MINUTE(st), &leftover_us);
CLEANUP;
}
if (hour) {
- y = accum("hours", x, hour, st->us_per_hour, &leftover_us);
+ y = accum("hours", x, hour, CONST_US_PER_HOUR(st), &leftover_us);
CLEANUP;
}
if (day) {
- y = accum("days", x, day, st->us_per_day, &leftover_us);
+ y = accum("days", x, day, CONST_US_PER_DAY(st), &leftover_us);
CLEANUP;
}
if (week) {
- y = accum("weeks", x, week, st->us_per_week, &leftover_us);
+ y = accum("weeks", x, week, CONST_US_PER_WEEK(st), &leftover_us);
CLEANUP;
}
if (leftover_us) {
@@ -2679,7 +2825,9 @@ delta_new(PyTypeObject *type, PyObject *args, PyObject *kw)
self = microseconds_to_delta_ex(x, type);
Py_DECREF(x);
+
Done:
+ RELEASE_CURRENT_STATE(st, current_mod);
return self;
#undef CLEANUP
@@ -2792,9 +2940,12 @@ delta_total_seconds(PyObject *self, PyObject *Py_UNUSED(ignored))
if (total_microseconds == NULL)
return NULL;
- datetime_state *st = get_datetime_state();
- total_seconds = PyNumber_TrueDivide(total_microseconds, st->us_per_second);
+ PyObject *current_mod = NULL;
+ datetime_state *st = GET_CURRENT_STATE(current_mod);
+
+ total_seconds = PyNumber_TrueDivide(total_microseconds, CONST_US_PER_SECOND(st));
+ RELEASE_CURRENT_STATE(st, current_mod);
Py_DECREF(total_microseconds);
return total_seconds;
}
@@ -3547,9 +3698,12 @@ date_isocalendar(PyDateTime_Date *self, PyObject *Py_UNUSED(ignored))
week = 0;
}
- datetime_state *st = get_datetime_state();
- PyObject *v = iso_calendar_date_new_impl(st->isocalendar_date_type,
+ PyObject *current_mod = NULL;
+ datetime_state *st = GET_CURRENT_STATE(current_mod);
+
+ PyObject *v = iso_calendar_date_new_impl(ISOCALENDAR_DATE_TYPE(st),
year, week + 1, day + 1);
+ RELEASE_CURRENT_STATE(st, current_mod);
if (v == NULL) {
return NULL;
}
@@ -4018,9 +4172,8 @@ timezone_new(PyTypeObject *type, PyObject *args, PyObject *kw)
{
PyObject *offset;
PyObject *name = NULL;
- datetime_state *st = get_datetime_state();
if (PyArg_ParseTupleAndKeywords(args, kw, "O!|U:timezone", timezone_kws,
- st->delta_type, &offset, &name))
+ DELTA_TYPE(NO_STATE), &offset, &name))
return new_timezone(offset, name);
return NULL;
@@ -4073,8 +4226,7 @@ timezone_repr(PyDateTime_TimeZone *self)
to use Py_TYPE(self)->tp_name here. */
const char *type_name = Py_TYPE(self)->tp_name;
- datetime_state *st = get_datetime_state();
- if (((PyObject *)self) == st->utc) {
+ if ((PyObject *)self == CONST_UTC(NO_STATE)) {
return PyUnicode_FromFormat("%s.utc", type_name);
}
@@ -4096,8 +4248,7 @@ timezone_str(PyDateTime_TimeZone *self)
if (self->name != NULL) {
return Py_NewRef(self->name);
}
- datetime_state *st = get_datetime_state();
- if ((PyObject *)self == st->utc ||
+ if ((PyObject *)self == CONST_UTC(NO_STATE) ||
(GET_TD_DAYS(self->offset) == 0 &&
GET_TD_SECONDS(self->offset) == 0 &&
GET_TD_MICROSECONDS(self->offset) == 0))
@@ -4260,7 +4411,7 @@ static PyDateTime_TimeZone *
look_up_timezone(PyObject *offset, PyObject *name)
{
if (offset == utc_timezone.offset && name == NULL) {
- return &utc_timezone;
+ return (PyDateTime_TimeZone *)CONST_UTC(NO_STATE);
}
return NULL;
}
@@ -4777,8 +4928,7 @@ time_fromisoformat(PyObject *cls, PyObject *tstr) {
}
PyObject *t;
- datetime_state *st = get_datetime_state();
- if ( (PyTypeObject *)cls == st->time_type) {
+ if ( (PyTypeObject *)cls == TIME_TYPE(NO_STATE)) {
t = new_time(hour, minute, second, microsecond, tzinfo, 0);
} else {
t = PyObject_CallFunction(cls, "iiiiO",
@@ -5376,10 +5526,9 @@ datetime_combine(PyObject *cls, PyObject *args, PyObject *kw)
PyObject *tzinfo = NULL;
PyObject *result = NULL;
- datetime_state *st = get_datetime_state();
if (PyArg_ParseTupleAndKeywords(args, kw, "O!O!|O:combine", keywords,
- st->date_type, &date,
- st->time_type, &time, &tzinfo)) {
+ DATE_TYPE(NO_STATE), &date,
+ TIME_TYPE(NO_STATE), &time, &tzinfo)) {
if (tzinfo == NULL) {
if (HASTZINFO(time))
tzinfo = ((PyDateTime_Time *)time)->tzinfo;
@@ -6209,7 +6358,6 @@ local_timezone_from_timestamp(time_t timestamp)
delta = new_delta(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];
@@ -6264,8 +6412,11 @@ local_timezone(PyDateTime_DateTime *utc_time)
PyObject *one_second;
PyObject *seconds;
- datetime_state *st = get_datetime_state();
- delta = datetime_subtract((PyObject *)utc_time, st->epoch);
+ PyObject *current_mod = NULL;
+ datetime_state *st = GET_CURRENT_STATE(current_mod);
+
+ delta = datetime_subtract((PyObject *)utc_time, CONST_EPOCH(st));
+ RELEASE_CURRENT_STATE(st, current_mod);
if (delta == NULL)
return NULL;
@@ -6378,7 +6529,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;
@@ -6390,7 +6540,7 @@ datetime_astimezone(PyDateTime_DateTime *self, PyObject *args, PyObject *kw)
DATE_GET_MINUTE(result),
DATE_GET_SECOND(result),
DATE_GET_MICROSECOND(result),
- st->utc,
+ CONST_UTC(NO_STATE),
DATE_GET_FOLD(result),
Py_TYPE(result));
Py_DECREF(temp);
@@ -6399,7 +6549,7 @@ datetime_astimezone(PyDateTime_DateTime *self, PyObject *args, PyObject *kw)
}
else {
/* Result is already aware - just replace tzinfo. */
- Py_SETREF(result->tzinfo, Py_NewRef(st->utc));
+ Py_SETREF(result->tzinfo, Py_NewRef(CONST_UTC(NO_STATE)));
}
/* Attach new tzinfo and let fromutc() do the rest. */
@@ -6503,9 +6653,12 @@ datetime_timestamp(PyDateTime_DateTime *self, PyObject *Py_UNUSED(ignored))
PyObject *result;
if (HASTZINFO(self) && self->tzinfo != Py_None) {
- datetime_state *st = get_datetime_state();
+ PyObject *current_mod = NULL;
+ datetime_state *st = GET_CURRENT_STATE(current_mod);
+
PyObject *delta;
- delta = datetime_subtract((PyObject *)self, st->epoch);
+ delta = datetime_subtract((PyObject *)self, CONST_EPOCH(st));
+ RELEASE_CURRENT_STATE(st, current_mod);
if (delta == NULL)
return NULL;
result = delta_total_seconds(delta, NULL);
@@ -6839,23 +6992,6 @@ get_datetime_capi(void)
return &capi;
}
-static int
-datetime_clear(PyObject *module)
-{
- datetime_state *st = get_datetime_state();
-
- Py_CLEAR(st->us_per_ms);
- Py_CLEAR(st->us_per_second);
- Py_CLEAR(st->us_per_minute);
- Py_CLEAR(st->us_per_hour);
- Py_CLEAR(st->us_per_day);
- Py_CLEAR(st->us_per_week);
- Py_CLEAR(st->seconds_per_day);
- Py_CLEAR(st->utc);
- Py_CLEAR(st->epoch);
- return 0;
-}
-
static PyObject *
create_timezone_from_delta(int days, int sec, int ms, int normalize)
{
@@ -6869,25 +7005,39 @@ create_timezone_from_delta(int days, int sec, int ms, int normalize)
}
static int
-init_state(datetime_state *st, PyTypeObject *PyDateTime_IsoCalendarDateType)
-{
- // While datetime uses global module "state", we unly initialize it once.
- // The PyLong objects created here (once per process) are not decref'd.
- if (st->initialized) {
+init_state(datetime_state *st, PyObject *module, PyObject *old_module)
+{
+ /* Each module gets its own heap types. */
+#define ADD_TYPE(FIELD, SPEC, BASE) \
+ do { \
+ PyObject *cls = PyType_FromModuleAndSpec( \
+ module, SPEC, (PyObject *)BASE); \
+ if (cls == NULL) { \
+ return -1; \
+ } \
+ st->FIELD = (PyTypeObject *)cls; \
+ } while (0)
+
+ ADD_TYPE(isocalendar_date_type, &isocal_spec, &PyTuple_Type);
+#undef ADD_TYPE
+
+ if (old_module != NULL) {
+ assert(old_module != module);
+ datetime_state *st_old = get_module_state(old_module);
+ *st = (datetime_state){
+ .isocalendar_date_type = st->isocalendar_date_type,
+ .us_per_ms = Py_NewRef(st_old->us_per_ms),
+ .us_per_second = Py_NewRef(st_old->us_per_second),
+ .us_per_minute = Py_NewRef(st_old->us_per_minute),
+ .us_per_hour = Py_NewRef(st_old->us_per_hour),
+ .us_per_day = Py_NewRef(st_old->us_per_day),
+ .us_per_week = Py_NewRef(st_old->us_per_week),
+ .seconds_per_day = Py_NewRef(st_old->seconds_per_day),
+ .epoch = Py_NewRef(st_old->epoch),
+ };
return 0;
}
- /* Static types exposed by the C-API. */
- st->date_type = &PyDateTime_DateType;
- st->datetime_type = &PyDateTime_DateTimeType;
- st->delta_type = &PyDateTime_DeltaType;
- 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;
@@ -6921,26 +7071,54 @@ init_state(datetime_state *st, PyTypeObject *PyDateTime_IsoCalendarDateType)
return -1;
}
- /* Init UTC timezone */
- st->utc = create_timezone_from_delta(0, 0, 0, 0);
- if (st->utc == NULL) {
- return -1;
- }
-
/* Init Unix epoch */
- st->epoch = new_datetime(1970, 1, 1, 0, 0, 0, 0, st->utc, 0);
+ st->epoch = new_datetime(
+ 1970, 1, 1, 0, 0, 0, 0, (PyObject *)&utc_timezone, 0);
if (st->epoch == NULL) {
return -1;
}
- st->initialized = 1;
+ return 0;
+}
+
+static int
+traverse_state(datetime_state *st, visitproc visit, void *arg)
+{
+ /* heap types */
+ Py_VISIT(st->isocalendar_date_type);
return 0;
}
static int
+clear_state(datetime_state *st)
+{
+ Py_CLEAR(st->isocalendar_date_type);
+ Py_CLEAR(st->us_per_ms);
+ Py_CLEAR(st->us_per_second);
+ Py_CLEAR(st->us_per_minute);
+ Py_CLEAR(st->us_per_hour);
+ Py_CLEAR(st->us_per_day);
+ Py_CLEAR(st->us_per_week);
+ Py_CLEAR(st->seconds_per_day);
+ Py_CLEAR(st->epoch);
+ return 0;
+}
+
+static int
_datetime_exec(PyObject *module)
{
+ int rc = -1;
+ datetime_state *st = get_module_state(module);
+
+ PyInterpreterState *interp = PyInterpreterState_Get();
+ PyObject *old_module = get_current_module(interp);
+ if (PyErr_Occurred()) {
+ assert(old_module == NULL);
+ goto error;
+ }
+ /* We actually set the "current" module right before a successful return. */
+
// `&...` 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
@@ -6953,6 +7131,7 @@ _datetime_exec(PyObject *module)
&PyDateTime_TimeType,
&PyDateTime_DeltaType,
&PyDateTime_TZInfoType,
+ /* Indirectly, via the utc object. */
&PyDateTime_TimeZoneType,
};
@@ -6962,29 +7141,16 @@ _datetime_exec(PyObject *module)
}
}
-#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;
- datetime_state *st = get_datetime_state();
-
- if (!st->initialized) {
- CREATE_TYPE(PyDateTime_IsoCalendarDateType, &isocal_spec, &PyTuple_Type);
- }
-#undef CREATE_TYPE
-
- if (init_state(st, PyDateTime_IsoCalendarDateType) < 0) {
+ if (init_state(st, module, old_module) < 0) {
goto error;
}
+ /* For now we only set the objects on the static types once.
+ * We will relax that once each types __dict__ is per-interpreter. */
#define DATETIME_ADD_MACRO(dict, c, value_expr) \
do { \
+ if (PyDict_GetItemString(dict, c) == NULL) { \
+ assert(!PyErr_Occurred()); \
PyObject *value = (value_expr); \
if (value == NULL) { \
goto error; \
@@ -6994,29 +7160,30 @@ _datetime_exec(PyObject *module)
goto error; \
} \
Py_DECREF(value); \
+ } \
} while(0)
/* timedelta values */
- PyObject *d = st->delta_type->tp_dict;
+ PyObject *d = PyDateTime_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));
/* date values */
- d = st->date_type->tp_dict;
+ d = PyDateTime_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 = st->time_type->tp_dict;
+ d = PyDateTime_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 = st->datetime_type->tp_dict;
+ d = PyDateTime_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,
@@ -7024,8 +7191,8 @@ _datetime_exec(PyObject *module)
DATETIME_ADD_MACRO(d, "resolution", new_delta(0, 0, 1, 0));
/* timezone values */
- d = st->timezone_type->tp_dict;
- if (PyDict_SetItemString(d, "utc", st->utc) < 0) {
+ d = PyDateTime_TimeZoneType.tp_dict;
+ if (PyDict_SetItemString(d, "utc", (PyObject *)&utc_timezone) < 0) {
goto error;
}
@@ -7034,12 +7201,13 @@ _datetime_exec(PyObject *module)
* values. This may change in the future.*/
/* -23:59 */
- PyObject *min = create_timezone_from_delta(-1, 60, 0, 1);
- DATETIME_ADD_MACRO(d, "min", min);
+ DATETIME_ADD_MACRO(d, "min", create_timezone_from_delta(-1, 60, 0, 1));
/* +23:59 */
- PyObject *max = create_timezone_from_delta(0, (23 * 60 + 59) * 60, 0, 0);
- DATETIME_ADD_MACRO(d, "max", max);
+ DATETIME_ADD_MACRO(
+ d, "max", create_timezone_from_delta(0, (23 * 60 + 59) * 60, 0, 0));
+
+#undef DATETIME_ADD_MACRO
/* Add module level attributes */
if (PyModule_AddIntMacro(module, MINYEAR) < 0) {
@@ -7048,7 +7216,7 @@ _datetime_exec(PyObject *module)
if (PyModule_AddIntMacro(module, MAXYEAR) < 0) {
goto error;
}
- if (PyModule_AddObjectRef(module, "UTC", st->utc) < 0) {
+ if (PyModule_AddObjectRef(module, "UTC", (PyObject *)&utc_timezone) < 0) {
goto error;
}
@@ -7081,13 +7249,20 @@ _datetime_exec(PyObject *module)
static_assert(DI100Y == 25 * DI4Y - 1, "DI100Y");
assert(DI100Y == days_before_year(100+1));
- return 0;
+ if (set_current_module(interp, module) < 0) {
+ goto error;
+ }
+
+ rc = 0;
+ goto finally;
error:
- datetime_clear(module);
- return -1;
+ clear_state(st);
+
+finally:
+ Py_XDECREF(old_module);
+ return rc;
}
-#undef DATETIME_ADD_MACRO
static PyModuleDef_Slot module_slots[] = {
{Py_mod_exec, _datetime_exec},
@@ -7096,13 +7271,46 @@ static PyModuleDef_Slot module_slots[] = {
{0, NULL},
};
+static int
+module_traverse(PyObject *mod, visitproc visit, void *arg)
+{
+ datetime_state *st = get_module_state(mod);
+ traverse_state(st, visit, arg);
+ return 0;
+}
+
+static int
+module_clear(PyObject *mod)
+{
+ datetime_state *st = get_module_state(mod);
+ clear_state(st);
+
+ PyInterpreterState *interp = PyInterpreterState_Get();
+ clear_current_module(interp, mod);
+
+ return 0;
+}
+
+static void
+module_free(void *mod)
+{
+ datetime_state *st = get_module_state((PyObject *)mod);
+ clear_state(st);
+
+ PyInterpreterState *interp = PyInterpreterState_Get();
+ clear_current_module(interp, (PyObject *)mod);
+}
+
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,
+ .m_traverse = module_traverse,
+ .m_clear = module_clear,
+ .m_free = module_free,
};
PyMODINIT_FUNC