mirror of
https://github.com/python/cpython.git
synced 2026-01-06 23:42:34 +00:00
[3.13] gh-117398: Statically Allocate the Datetime C-API (GH-119472) (gh-119641)
This is a backport of 3 commits that go together. (cherry picked from commita895756) (cherry picked from commitb30d30c) (cherry picked from commita89fc26)
This commit is contained in:
parent
0d0be6b3ef
commit
12a580b82d
3 changed files with 194 additions and 110 deletions
|
|
@ -0,0 +1,3 @@
|
|||
Objects in the datetime C-API are now all statically allocated, which means
|
||||
better memory safety, especially when the module is reloaded. This should be
|
||||
transparent to users.
|
||||
|
|
@ -25,24 +25,15 @@
|
|||
# include <winsock2.h> /* struct timeval */
|
||||
#endif
|
||||
|
||||
#define PyDate_Check(op) PyObject_TypeCheck(op, &PyDateTime_DateType)
|
||||
#define PyDate_CheckExact(op) Py_IS_TYPE(op, &PyDateTime_DateType)
|
||||
|
||||
#define PyDateTime_Check(op) PyObject_TypeCheck(op, &PyDateTime_DateTimeType)
|
||||
#define PyDateTime_CheckExact(op) Py_IS_TYPE(op, &PyDateTime_DateTimeType)
|
||||
|
||||
#define PyTime_Check(op) PyObject_TypeCheck(op, &PyDateTime_TimeType)
|
||||
#define PyTime_CheckExact(op) Py_IS_TYPE(op, &PyDateTime_TimeType)
|
||||
|
||||
#define PyDelta_Check(op) PyObject_TypeCheck(op, &PyDateTime_DeltaType)
|
||||
#define PyDelta_CheckExact(op) Py_IS_TYPE(op, &PyDateTime_DeltaType)
|
||||
|
||||
#define PyTZInfo_Check(op) PyObject_TypeCheck(op, &PyDateTime_TZInfoType)
|
||||
#define PyTZInfo_CheckExact(op) Py_IS_TYPE(op, &PyDateTime_TZInfoType)
|
||||
|
||||
#define PyTimezone_Check(op) PyObject_TypeCheck(op, &PyDateTime_TimeZoneType)
|
||||
|
||||
typedef struct {
|
||||
PyTypeObject *date_type;
|
||||
PyTypeObject *datetime_type;
|
||||
PyTypeObject *delta_type;
|
||||
PyTypeObject *isocalendar_date_type;
|
||||
PyTypeObject *time_type;
|
||||
PyTypeObject *tzinfo_type;
|
||||
PyTypeObject *timezone_type;
|
||||
|
||||
/* Conversion factors. */
|
||||
PyObject *us_per_ms; // 1_000
|
||||
PyObject *us_per_second; // 1_000_000
|
||||
|
|
@ -57,11 +48,34 @@ typedef struct {
|
|||
|
||||
/* 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;
|
||||
|
||||
#define STATIC_STATE() (&_datetime_global_state)
|
||||
static inline datetime_state* get_datetime_state(void)
|
||||
{
|
||||
return &_datetime_global_state;
|
||||
}
|
||||
|
||||
#define PyDate_Check(op) PyObject_TypeCheck(op, get_datetime_state()->date_type)
|
||||
#define PyDate_CheckExact(op) Py_IS_TYPE(op, get_datetime_state()->date_type)
|
||||
|
||||
#define PyDateTime_Check(op) PyObject_TypeCheck(op, get_datetime_state()->datetime_type)
|
||||
#define PyDateTime_CheckExact(op) Py_IS_TYPE(op, get_datetime_state()->datetime_type)
|
||||
|
||||
#define PyTime_Check(op) PyObject_TypeCheck(op, get_datetime_state()->time_type)
|
||||
#define PyTime_CheckExact(op) Py_IS_TYPE(op, get_datetime_state()->time_type)
|
||||
|
||||
#define PyDelta_Check(op) PyObject_TypeCheck(op, get_datetime_state()->delta_type)
|
||||
#define PyDelta_CheckExact(op) Py_IS_TYPE(op, get_datetime_state()->delta_type)
|
||||
|
||||
#define PyTZInfo_Check(op) PyObject_TypeCheck(op, get_datetime_state()->tzinfo_type)
|
||||
#define PyTZInfo_CheckExact(op) Py_IS_TYPE(op, get_datetime_state()->tzinfo_type)
|
||||
|
||||
#define 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
|
||||
|
|
@ -142,25 +156,16 @@ static datetime_state _datetime_global_state;
|
|||
*/
|
||||
#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);
|
||||
|
||||
/*[clinic input]
|
||||
module datetime
|
||||
class datetime.datetime "PyDateTime_DateTime *" "&PyDateTime_DateTimeType"
|
||||
class datetime.date "PyDateTime_Date *" "&PyDateTime_DateType"
|
||||
class datetime.time "PyDateTime_Time *" "&PyDateTime_TimeType"
|
||||
class datetime.IsoCalendarDate "PyDateTime_IsoCalendarDate *" "&PyDateTime_IsoCalendarDateType"
|
||||
class datetime.datetime "PyDateTime_DateTime *" "get_datetime_state()->datetime_type"
|
||||
class datetime.date "PyDateTime_Date *" "get_datetime_state()->date_type"
|
||||
class datetime.time "PyDateTime_Time *" "get_datetime_state()->time_type"
|
||||
class datetime.IsoCalendarDate "PyDateTime_IsoCalendarDate *" "get_datetime_state()->isocalendar_date_type"
|
||||
[clinic start generated code]*/
|
||||
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=6f65a48dd22fa40f]*/
|
||||
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=c8f3d834a860d50a]*/
|
||||
|
||||
#include "clinic/_datetimemodule.c.h"
|
||||
|
||||
|
|
@ -979,7 +984,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, get_datetime_state()->date_type)
|
||||
|
||||
// Forward declaration
|
||||
static PyObject *
|
||||
|
|
@ -989,12 +994,13 @@ 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 == &PyDateTime_DateType) {
|
||||
if ((PyTypeObject *)cls == st->date_type) {
|
||||
result = new_date_ex(year, month, day, (PyTypeObject *)cls);
|
||||
}
|
||||
else if ((PyTypeObject *)cls == &PyDateTime_DateTimeType) {
|
||||
else if ((PyTypeObject *)cls == st->datetime_type) {
|
||||
result = new_datetime_ex(year, month, day, 0, 0, 0, 0, Py_None,
|
||||
(PyTypeObject *)cls);
|
||||
}
|
||||
|
|
@ -1049,7 +1055,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, \
|
||||
&PyDateTime_DateTimeType)
|
||||
get_datetime_state()->datetime_type)
|
||||
|
||||
static PyObject *
|
||||
call_subclass_fold(PyObject *cls, int fold, const char *format, ...)
|
||||
|
|
@ -1088,9 +1094,11 @@ call_subclass_fold(PyObject *cls, int fold, const char *format, ...)
|
|||
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) {
|
||||
int fold, PyObject *cls)
|
||||
{
|
||||
datetime_state *st = get_datetime_state();
|
||||
PyObject* dt;
|
||||
if ((PyTypeObject*)cls == &PyDateTime_DateTimeType) {
|
||||
if ((PyTypeObject*)cls == st->datetime_type) {
|
||||
// Use the fast path constructor
|
||||
dt = new_datetime(year, month, day, hour, minute, second, usecond,
|
||||
tzinfo, fold);
|
||||
|
|
@ -1152,14 +1160,15 @@ 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, get_datetime_state()->time_type)
|
||||
|
||||
static PyObject *
|
||||
new_time_subclass_fold_ex(int hour, int minute, int second, int usecond,
|
||||
PyObject *tzinfo, int fold, PyObject *cls)
|
||||
{
|
||||
PyObject *t;
|
||||
if ((PyTypeObject*)cls == &PyDateTime_TimeType) {
|
||||
datetime_state *st = get_datetime_state();
|
||||
if ((PyTypeObject*)cls == st->time_type) {
|
||||
// Use the fast path constructor
|
||||
t = new_time(hour, minute, second, usecond, tzinfo, fold);
|
||||
}
|
||||
|
|
@ -1172,6 +1181,8 @@ new_time_subclass_fold_ex(int hour, int minute, int second, int usecond,
|
|||
return t;
|
||||
}
|
||||
|
||||
static PyDateTime_Delta * look_up_delta(int, int, int, PyTypeObject *);
|
||||
|
||||
/* Create a timedelta instance. Normalize the members iff normalize is
|
||||
* true. Passing false is a speed optimization, if you know for sure
|
||||
* that seconds and microseconds are already in their proper ranges. In any
|
||||
|
|
@ -1192,6 +1203,12 @@ new_delta_ex(int days, int seconds, int microseconds, int normalize,
|
|||
if (check_delta_day_range(days) < 0)
|
||||
return NULL;
|
||||
|
||||
self = look_up_delta(days, seconds, microseconds, type);
|
||||
if (self != NULL) {
|
||||
return (PyObject *)self;
|
||||
}
|
||||
assert(!PyErr_Occurred());
|
||||
|
||||
self = (PyDateTime_Delta *) (type->tp_alloc(type, 0));
|
||||
if (self != NULL) {
|
||||
self->hashcode = -1;
|
||||
|
|
@ -1203,7 +1220,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, get_datetime_state()->delta_type)
|
||||
|
||||
|
||||
typedef struct
|
||||
|
|
@ -1213,6 +1230,8 @@ typedef struct
|
|||
PyObject *name;
|
||||
} PyDateTime_TimeZone;
|
||||
|
||||
static PyDateTime_TimeZone * look_up_timezone(PyObject *offset, PyObject *name);
|
||||
|
||||
/* Create new timezone instance checking offset range. This
|
||||
function does not check the name argument. Caller must assure
|
||||
that offset is a timedelta instance and name is either NULL
|
||||
|
|
@ -1221,12 +1240,19 @@ static PyObject *
|
|||
create_timezone(PyObject *offset, PyObject *name)
|
||||
{
|
||||
PyDateTime_TimeZone *self;
|
||||
PyTypeObject *type = &PyDateTime_TimeZoneType;
|
||||
datetime_state *st = get_datetime_state();
|
||||
PyTypeObject *type = st->timezone_type;
|
||||
|
||||
assert(offset != NULL);
|
||||
assert(PyDelta_Check(offset));
|
||||
assert(name == NULL || PyUnicode_Check(name));
|
||||
|
||||
self = look_up_timezone(offset, name);
|
||||
if (self != NULL) {
|
||||
return (PyObject *)self;
|
||||
}
|
||||
assert(!PyErr_Occurred());
|
||||
|
||||
self = (PyDateTime_TimeZone *)(type->tp_alloc(type, 0));
|
||||
if (self == NULL) {
|
||||
return NULL;
|
||||
|
|
@ -1246,7 +1272,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 = STATIC_STATE();
|
||||
datetime_state *st = get_datetime_state();
|
||||
return Py_NewRef(st->utc);
|
||||
}
|
||||
if ((GET_TD_DAYS(offset) == -1 &&
|
||||
|
|
@ -1460,7 +1486,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 = STATIC_STATE();
|
||||
datetime_state *st = get_datetime_state();
|
||||
return Py_NewRef(st->utc);
|
||||
}
|
||||
|
||||
|
|
@ -1893,7 +1919,7 @@ delta_to_microseconds(PyDateTime_Delta *self)
|
|||
x1 = PyLong_FromLong(GET_TD_DAYS(self));
|
||||
if (x1 == NULL)
|
||||
goto Done;
|
||||
datetime_state *st = STATIC_STATE();
|
||||
datetime_state *st = get_datetime_state();
|
||||
x2 = PyNumber_Multiply(x1, st->seconds_per_day); /* days in seconds */
|
||||
if (x2 == NULL)
|
||||
goto Done;
|
||||
|
|
@ -1966,7 +1992,7 @@ microseconds_to_delta_ex(PyObject *pyus, PyTypeObject *type)
|
|||
PyObject *num = NULL;
|
||||
PyObject *result = NULL;
|
||||
|
||||
datetime_state *st = STATIC_STATE();
|
||||
datetime_state *st = get_datetime_state();
|
||||
tuple = checked_divmod(pyus, st->us_per_second);
|
||||
if (tuple == NULL) {
|
||||
goto Done;
|
||||
|
|
@ -2019,7 +2045,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, get_datetime_state()->delta_type)
|
||||
|
||||
static PyObject *
|
||||
multiply_int_timedelta(PyObject *intobj, PyDateTime_Delta *delta)
|
||||
|
|
@ -2585,7 +2611,7 @@ delta_new(PyTypeObject *type, PyObject *args, PyObject *kw)
|
|||
y = accum("microseconds", x, us, _PyLong_GetOne(), &leftover_us);
|
||||
CLEANUP;
|
||||
}
|
||||
datetime_state *st = STATIC_STATE();
|
||||
datetime_state *st = get_datetime_state();
|
||||
if (ms) {
|
||||
y = accum("milliseconds", x, ms, st->us_per_ms, &leftover_us);
|
||||
CLEANUP;
|
||||
|
|
@ -2762,7 +2788,7 @@ delta_total_seconds(PyObject *self, PyObject *Py_UNUSED(ignored))
|
|||
if (total_microseconds == NULL)
|
||||
return NULL;
|
||||
|
||||
datetime_state *st = STATIC_STATE();
|
||||
datetime_state *st = get_datetime_state();
|
||||
total_seconds = PyNumber_TrueDivide(total_microseconds, st->us_per_second);
|
||||
|
||||
Py_DECREF(total_microseconds);
|
||||
|
|
@ -2885,6 +2911,25 @@ static PyTypeObject PyDateTime_DeltaType = {
|
|||
0, /* tp_free */
|
||||
};
|
||||
|
||||
// XXX Can we make this const?
|
||||
static PyDateTime_Delta zero_delta = {
|
||||
PyObject_HEAD_INIT(&PyDateTime_DeltaType)
|
||||
/* Letting this be set lazily is a benign race. */
|
||||
.hashcode = -1,
|
||||
};
|
||||
|
||||
static PyDateTime_Delta *
|
||||
look_up_delta(int days, int seconds, int microseconds, PyTypeObject *type)
|
||||
{
|
||||
if (days == 0 && seconds == 0 && microseconds == 0
|
||||
&& type == zero_delta.ob_base.ob_type)
|
||||
{
|
||||
return &zero_delta;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* PyDateTime_Date implementation.
|
||||
*/
|
||||
|
|
@ -3475,8 +3520,9 @@ 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);
|
||||
datetime_state *st = get_datetime_state();
|
||||
PyObject *v = iso_calendar_date_new_impl(st->isocalendar_date_type,
|
||||
year, week + 1, day + 1);
|
||||
if (v == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -3945,8 +3991,9 @@ 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,
|
||||
&PyDateTime_DeltaType, &offset, &name))
|
||||
st->delta_type, &offset, &name))
|
||||
return new_timezone(offset, name);
|
||||
|
||||
return NULL;
|
||||
|
|
@ -3999,7 +4046,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 = STATIC_STATE();
|
||||
datetime_state *st = get_datetime_state();
|
||||
if (((PyObject *)self) == st->utc) {
|
||||
return PyUnicode_FromFormat("%s.utc", type_name);
|
||||
}
|
||||
|
|
@ -4022,7 +4069,7 @@ timezone_str(PyDateTime_TimeZone *self)
|
|||
if (self->name != NULL) {
|
||||
return Py_NewRef(self->name);
|
||||
}
|
||||
datetime_state *st = STATIC_STATE();
|
||||
datetime_state *st = get_datetime_state();
|
||||
if ((PyObject *)self == st->utc ||
|
||||
(GET_TD_DAYS(self->offset) == 0 &&
|
||||
GET_TD_SECONDS(self->offset) == 0 &&
|
||||
|
|
@ -4175,6 +4222,23 @@ static PyTypeObject PyDateTime_TimeZoneType = {
|
|||
timezone_new, /* tp_new */
|
||||
};
|
||||
|
||||
// XXX Can we make this const?
|
||||
static PyDateTime_TimeZone utc_timezone = {
|
||||
PyObject_HEAD_INIT(&PyDateTime_TimeZoneType)
|
||||
.offset = (PyObject *)&zero_delta,
|
||||
.name = NULL,
|
||||
};
|
||||
|
||||
static PyDateTime_TimeZone *
|
||||
look_up_timezone(PyObject *offset, PyObject *name)
|
||||
{
|
||||
if (offset == utc_timezone.offset && name == NULL) {
|
||||
return &utc_timezone;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* PyDateTime_Time implementation.
|
||||
*/
|
||||
|
|
@ -4686,7 +4750,8 @@ time_fromisoformat(PyObject *cls, PyObject *tstr) {
|
|||
}
|
||||
|
||||
PyObject *t;
|
||||
if ( (PyTypeObject *)cls == &PyDateTime_TimeType ) {
|
||||
datetime_state *st = get_datetime_state();
|
||||
if ( (PyTypeObject *)cls == st->time_type) {
|
||||
t = new_time(hour, minute, second, microsecond, tzinfo, 0);
|
||||
} else {
|
||||
t = PyObject_CallFunction(cls, "iiiiO",
|
||||
|
|
@ -5284,9 +5349,10 @@ 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,
|
||||
&PyDateTime_DateType, &date,
|
||||
&PyDateTime_TimeType, &time, &tzinfo)) {
|
||||
st->date_type, &date,
|
||||
st->time_type, &time, &tzinfo)) {
|
||||
if (tzinfo == NULL) {
|
||||
if (HASTZINFO(time))
|
||||
tzinfo = ((PyDateTime_Time *)time)->tzinfo;
|
||||
|
|
@ -6116,6 +6182,7 @@ 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];
|
||||
|
|
@ -6170,10 +6237,11 @@ local_timezone(PyDateTime_DateTime *utc_time)
|
|||
PyObject *one_second;
|
||||
PyObject *seconds;
|
||||
|
||||
datetime_state *st = STATIC_STATE();
|
||||
datetime_state *st = get_datetime_state();
|
||||
delta = datetime_subtract((PyObject *)utc_time, st->epoch);
|
||||
if (delta == NULL)
|
||||
return NULL;
|
||||
|
||||
one_second = new_delta(0, 1, 0, 0);
|
||||
if (one_second == NULL) {
|
||||
Py_DECREF(delta);
|
||||
|
|
@ -6283,7 +6351,7 @@ datetime_astimezone(PyDateTime_DateTime *self, PyObject *args, PyObject *kw)
|
|||
if (result == NULL)
|
||||
return NULL;
|
||||
|
||||
datetime_state *st = STATIC_STATE();
|
||||
datetime_state *st = get_datetime_state();
|
||||
/* Make sure result is aware and UTC. */
|
||||
if (!HASTZINFO(result)) {
|
||||
temp = (PyObject *)result;
|
||||
|
|
@ -6408,7 +6476,7 @@ datetime_timestamp(PyDateTime_DateTime *self, PyObject *Py_UNUSED(ignored))
|
|||
PyObject *result;
|
||||
|
||||
if (HASTZINFO(self) && self->tzinfo != Py_None) {
|
||||
datetime_state *st = STATIC_STATE();
|
||||
datetime_state *st = get_datetime_state();
|
||||
PyObject *delta;
|
||||
delta = datetime_subtract((PyObject *)self, st->epoch);
|
||||
if (delta == NULL)
|
||||
|
|
@ -6706,50 +6774,48 @@ static PyMethodDef module_methods[] = {
|
|||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
||||
/* The C-API is process-global. This violates interpreter isolation
|
||||
* due to the objects stored here. Thus each of those objects must
|
||||
* be managed carefully. */
|
||||
// XXX Can we make this const?
|
||||
static PyDateTime_CAPI capi = {
|
||||
/* The classes must be readied before used here.
|
||||
* That will happen the first time the module is loaded.
|
||||
* They aren't safe to be shared between interpreters,
|
||||
* but that's okay as long as the module is single-phase init. */
|
||||
.DateType = &PyDateTime_DateType,
|
||||
.DateTimeType = &PyDateTime_DateTimeType,
|
||||
.TimeType = &PyDateTime_TimeType,
|
||||
.DeltaType = &PyDateTime_DeltaType,
|
||||
.TZInfoType = &PyDateTime_TZInfoType,
|
||||
|
||||
.TimeZone_UTC = (PyObject *)&utc_timezone,
|
||||
|
||||
.Date_FromDate = new_date_ex,
|
||||
.DateTime_FromDateAndTime = new_datetime_ex,
|
||||
.Time_FromTime = new_time_ex,
|
||||
.Delta_FromDelta = new_delta_ex,
|
||||
.TimeZone_FromTimeZone = new_timezone,
|
||||
.DateTime_FromTimestamp = datetime_fromtimestamp,
|
||||
.Date_FromTimestamp = datetime_date_fromtimestamp_capi,
|
||||
.DateTime_FromDateAndTimeAndFold = new_datetime_ex2,
|
||||
.Time_FromTimeAndFold = new_time_ex2,
|
||||
};
|
||||
|
||||
/* Get a new C API by calling this function.
|
||||
* Clients get at C API via PyDateTime_IMPORT, defined in datetime.h.
|
||||
*/
|
||||
static inline PyDateTime_CAPI *
|
||||
get_datetime_capi(void)
|
||||
{
|
||||
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;
|
||||
capi->Date_FromDate = new_date_ex;
|
||||
capi->DateTime_FromDateAndTime = new_datetime_ex;
|
||||
capi->Time_FromTime = new_time_ex;
|
||||
capi->Delta_FromDelta = new_delta_ex;
|
||||
capi->TimeZone_FromTimeZone = new_timezone;
|
||||
capi->DateTime_FromTimestamp = datetime_fromtimestamp;
|
||||
capi->Date_FromTimestamp = datetime_date_fromtimestamp_capi;
|
||||
capi->DateTime_FromDateAndTimeAndFold = new_datetime_ex2;
|
||||
capi->Time_FromTimeAndFold = new_time_ex2;
|
||||
// Make sure this function is called after utc has
|
||||
// been initialized.
|
||||
datetime_state *st = STATIC_STATE();
|
||||
assert(st->utc != NULL);
|
||||
capi->TimeZone_UTC = st->utc; // borrowed ref
|
||||
return capi;
|
||||
}
|
||||
|
||||
static void
|
||||
datetime_destructor(PyObject *op)
|
||||
{
|
||||
void *ptr = PyCapsule_GetPointer(op, PyDateTime_CAPSULE_NAME);
|
||||
PyMem_Free(ptr);
|
||||
return &capi;
|
||||
}
|
||||
|
||||
static int
|
||||
datetime_clear(PyObject *module)
|
||||
{
|
||||
datetime_state *st = STATIC_STATE();
|
||||
datetime_state *st = get_datetime_state();
|
||||
|
||||
Py_CLEAR(st->us_per_ms);
|
||||
Py_CLEAR(st->us_per_second);
|
||||
|
|
@ -6778,6 +6844,20 @@ create_timezone_from_delta(int days, int sec, int ms, int normalize)
|
|||
static int
|
||||
init_state(datetime_state *st)
|
||||
{
|
||||
// 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) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
st->date_type = &PyDateTime_DateType;
|
||||
st->datetime_type = &PyDateTime_DateTimeType;
|
||||
st->delta_type = &PyDateTime_DeltaType;
|
||||
st->isocalendar_date_type = &PyDateTime_IsoCalendarDateType;
|
||||
st->time_type = &PyDateTime_TimeType;
|
||||
st->tzinfo_type = &PyDateTime_TZInfoType;
|
||||
st->timezone_type = &PyDateTime_TimeZoneType;
|
||||
|
||||
st->us_per_ms = PyLong_FromLong(1000);
|
||||
if (st->us_per_ms == NULL) {
|
||||
return -1;
|
||||
|
|
@ -6822,6 +6902,9 @@ init_state(datetime_state *st)
|
|||
if (st->epoch == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
st->initialized = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -6854,6 +6937,11 @@ _datetime_exec(PyObject *module)
|
|||
goto error;
|
||||
}
|
||||
|
||||
datetime_state *st = get_datetime_state();
|
||||
if (init_state(st) < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
#define DATETIME_ADD_MACRO(dict, c, value_expr) \
|
||||
do { \
|
||||
PyObject *value = (value_expr); \
|
||||
|
|
@ -6868,39 +6956,34 @@ _datetime_exec(PyObject *module)
|
|||
} while(0)
|
||||
|
||||
/* timedelta values */
|
||||
PyObject *d = PyDateTime_DeltaType.tp_dict;
|
||||
PyObject *d = st->delta_type->tp_dict;
|
||||
DATETIME_ADD_MACRO(d, "resolution", new_delta(0, 0, 1, 0));
|
||||
DATETIME_ADD_MACRO(d, "min", new_delta(-MAX_DELTA_DAYS, 0, 0, 0));
|
||||
DATETIME_ADD_MACRO(d, "max",
|
||||
new_delta(MAX_DELTA_DAYS, 24*3600-1, 1000000-1, 0));
|
||||
|
||||
/* date values */
|
||||
d = PyDateTime_DateType.tp_dict;
|
||||
d = st->date_type->tp_dict;
|
||||
DATETIME_ADD_MACRO(d, "min", new_date(1, 1, 1));
|
||||
DATETIME_ADD_MACRO(d, "max", new_date(MAXYEAR, 12, 31));
|
||||
DATETIME_ADD_MACRO(d, "resolution", new_delta(1, 0, 0, 0));
|
||||
|
||||
/* time values */
|
||||
d = PyDateTime_TimeType.tp_dict;
|
||||
d = st->time_type->tp_dict;
|
||||
DATETIME_ADD_MACRO(d, "min", new_time(0, 0, 0, 0, Py_None, 0));
|
||||
DATETIME_ADD_MACRO(d, "max", new_time(23, 59, 59, 999999, Py_None, 0));
|
||||
DATETIME_ADD_MACRO(d, "resolution", new_delta(0, 0, 1, 0));
|
||||
|
||||
/* datetime values */
|
||||
d = PyDateTime_DateTimeType.tp_dict;
|
||||
d = st->datetime_type->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,
|
||||
999999, Py_None, 0));
|
||||
DATETIME_ADD_MACRO(d, "resolution", new_delta(0, 0, 1, 0));
|
||||
|
||||
datetime_state *st = STATIC_STATE();
|
||||
if (init_state(st) < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* timezone values */
|
||||
d = PyDateTime_TimeZoneType.tp_dict;
|
||||
d = st->timezone_type->tp_dict;
|
||||
if (PyDict_SetItemString(d, "utc", st->utc) < 0) {
|
||||
goto error;
|
||||
}
|
||||
|
|
@ -6933,14 +7016,9 @@ _datetime_exec(PyObject *module)
|
|||
if (capi == NULL) {
|
||||
goto error;
|
||||
}
|
||||
PyObject *capsule = PyCapsule_New(capi, PyDateTime_CAPSULE_NAME,
|
||||
datetime_destructor);
|
||||
if (capsule == NULL) {
|
||||
PyMem_Free(capi);
|
||||
goto error;
|
||||
}
|
||||
PyObject *capsule = PyCapsule_New(capi, PyDateTime_CAPSULE_NAME, NULL);
|
||||
// (capsule == NULL) is handled by PyModule_Add
|
||||
if (PyModule_Add(module, "datetime_CAPI", capsule) < 0) {
|
||||
PyMem_Free(capi);
|
||||
goto error;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -304,6 +304,9 @@ Python/crossinterp_exceptions.h - PyExc_InterpreterNotFoundError -
|
|||
##-----------------------
|
||||
## singletons
|
||||
|
||||
Modules/_datetimemodule.c - zero_delta -
|
||||
Modules/_datetimemodule.c - utc_timezone -
|
||||
Modules/_datetimemodule.c - capi -
|
||||
Objects/boolobject.c - _Py_FalseStruct -
|
||||
Objects/boolobject.c - _Py_TrueStruct -
|
||||
Objects/dictobject.c - empty_keys_struct -
|
||||
|
|
|
|||
|
Can't render this file because it has a wrong number of fields in line 4.
|
Loading…
Add table
Add a link
Reference in a new issue