bpo-46417: time module uses PyStructSequence_NewType() (GH-30734)

The time module now creates its struct_time type as a heap
type using PyStructSequence_NewType(), rather than using a static
type.

* Add a module state to the time module: add traverse, clear and free
  functions.
* Use PyModule_AddType().
* Remove the 'initialized' variable.
This commit is contained in:
Victor Stinner 2022-01-21 02:52:43 +01:00 committed by GitHub
parent f389b37fb1
commit 17f268a4ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 81 additions and 37 deletions

View File

@ -2,6 +2,7 @@
#include "Python.h"
#include "pycore_fileutils.h" // _Py_BEGIN_SUPPRESS_IPH
#include "pycore_moduleobject.h" // _PyModule_GetState()
#include "pycore_namespace.h" // _PyNamespace_New()
#include <ctype.h>
@ -64,6 +65,19 @@
static int pysleep(_PyTime_t timeout);
typedef struct {
PyTypeObject *struct_time_type;
} time_module_state;
static inline time_module_state*
get_time_state(PyObject *module)
{
void *state = _PyModule_GetState(module);
assert(state != NULL);
return (time_module_state *)state;
}
static PyObject*
_PyFloat_FromPyTime(_PyTime_t t)
{
@ -405,9 +419,6 @@ static PyStructSequence_Desc struct_time_type_desc = {
9,
};
static int initialized;
static PyTypeObject StructTimeType;
#if defined(MS_WINDOWS)
#ifndef CREATE_WAITABLE_TIMER_HIGH_RESOLUTION
#define CREATE_WAITABLE_TIMER_HIGH_RESOLUTION 0x00000002
@ -417,13 +428,13 @@ static DWORD timer_flags = (DWORD)-1;
#endif
static PyObject *
tmtotuple(struct tm *p
tmtotuple(time_module_state *state, struct tm *p
#ifndef HAVE_STRUCT_TM_TM_ZONE
, const char *zone, time_t gmtoff
#endif
)
{
PyObject *v = PyStructSequence_New(&StructTimeType);
PyObject *v = PyStructSequence_New(state->struct_time_type);
if (v == NULL)
return NULL;
@ -480,7 +491,7 @@ parse_time_t_args(PyObject *args, const char *format, time_t *pwhen)
}
static PyObject *
time_gmtime(PyObject *self, PyObject *args)
time_gmtime(PyObject *module, PyObject *args)
{
time_t when;
struct tm buf;
@ -491,10 +502,12 @@ time_gmtime(PyObject *self, PyObject *args)
errno = 0;
if (_PyTime_gmtime(when, &buf) != 0)
return NULL;
time_module_state *state = get_time_state(module);
#ifdef HAVE_STRUCT_TM_TM_ZONE
return tmtotuple(&buf);
return tmtotuple(state, &buf);
#else
return tmtotuple(&buf, "UTC", 0);
return tmtotuple(state, &buf, "UTC", 0);
#endif
}
@ -522,7 +535,7 @@ If the platform supports the tm_gmtoff and tm_zone, they are available as\n\
attributes only.");
static PyObject *
time_localtime(PyObject *self, PyObject *args)
time_localtime(PyObject *module, PyObject *args)
{
time_t when;
struct tm buf;
@ -531,8 +544,10 @@ time_localtime(PyObject *self, PyObject *args)
return NULL;
if (_PyTime_localtime(when, &buf) != 0)
return NULL;
time_module_state *state = get_time_state(module);
#ifdef HAVE_STRUCT_TM_TM_ZONE
return tmtotuple(&buf);
return tmtotuple(state, &buf);
#else
{
struct tm local = buf;
@ -540,7 +555,7 @@ time_localtime(PyObject *self, PyObject *args)
time_t gmtoff;
strftime(zone, sizeof(zone), "%Z", &buf);
gmtoff = timegm(&buf) - when;
return tmtotuple(&local, zone, gmtoff);
return tmtotuple(state, &local, zone, gmtoff);
}
#endif
}
@ -560,7 +575,8 @@ When 'seconds' is not passed in, convert the current time instead.");
* an exception and return 0 on error.
*/
static int
gettmarg(PyObject *args, struct tm *p, const char *format)
gettmarg(time_module_state *state, PyObject *args,
struct tm *p, const char *format)
{
int y;
@ -588,7 +604,7 @@ gettmarg(PyObject *args, struct tm *p, const char *format)
p->tm_wday = (p->tm_wday + 1) % 7;
p->tm_yday--;
#ifdef HAVE_STRUCT_TM_TM_ZONE
if (Py_IS_TYPE(args, &StructTimeType)) {
if (Py_IS_TYPE(args, state->struct_time_type)) {
PyObject *item;
item = PyStructSequence_GET_ITEM(args, 9);
if (item != Py_None) {
@ -729,7 +745,7 @@ the C library strftime function.\n"
#endif
static PyObject *
time_strftime(PyObject *self, PyObject *args)
time_strftime(PyObject *module, PyObject *args)
{
PyObject *tup = NULL;
struct tm buf;
@ -753,12 +769,13 @@ time_strftime(PyObject *self, PyObject *args)
if (!PyArg_ParseTuple(args, "U|O:strftime", &format_arg, &tup))
return NULL;
time_module_state *state = get_time_state(module);
if (tup == NULL) {
time_t tt = time(NULL);
if (_PyTime_localtime(tt, &buf) != 0)
return NULL;
}
else if (!gettmarg(tup, &buf,
else if (!gettmarg(state, tup, &buf,
"iiiiiiiii;strftime(): illegal time tuple argument") ||
!checktm(&buf))
{
@ -941,19 +958,21 @@ _asctime(struct tm *timeptr)
}
static PyObject *
time_asctime(PyObject *self, PyObject *args)
time_asctime(PyObject *module, PyObject *args)
{
PyObject *tup = NULL;
struct tm buf;
if (!PyArg_UnpackTuple(args, "asctime", 0, 1, &tup))
return NULL;
time_module_state *state = get_time_state(module);
if (tup == NULL) {
time_t tt = time(NULL);
if (_PyTime_localtime(tt, &buf) != 0)
return NULL;
}
else if (!gettmarg(tup, &buf,
else if (!gettmarg(state, tup, &buf,
"iiiiiiiii;asctime(): illegal time tuple argument") ||
!checktm(&buf))
{
@ -990,12 +1009,13 @@ not present, current time as returned by localtime() is used.");
#ifdef HAVE_MKTIME
static PyObject *
time_mktime(PyObject *self, PyObject *tm_tuple)
time_mktime(PyObject *module, PyObject *tm_tuple)
{
struct tm tm;
time_t tt;
if (!gettmarg(tm_tuple, &tm,
time_module_state *state = get_time_state(module);
if (!gettmarg(state, tm_tuple, &tm,
"iiiiiiiii;mktime(): illegal time tuple argument"))
{
return NULL;
@ -1888,6 +1908,7 @@ if it is -1, mktime() should guess based on the date and time.\n");
static int
time_exec(PyObject *module)
{
time_module_state *state = get_time_state(module);
#if defined(__APPLE__) && defined(HAVE_CLOCK_GETTIME)
if (HAVE_CLOCK_GETTIME_RUNTIME) {
/* pass: ^^^ cannot use '!' here */
@ -2001,21 +2022,18 @@ time_exec(PyObject *module)
#endif /* defined(HAVE_CLOCK_GETTIME) || defined(HAVE_CLOCK_SETTIME) || defined(HAVE_CLOCK_GETRES) */
if (!initialized) {
if (PyStructSequence_InitType2(&StructTimeType,
&struct_time_type_desc) < 0) {
return -1;
}
}
if (PyModule_AddIntConstant(module, "_STRUCT_TM_ITEMS", 11)) {
return -1;
}
Py_INCREF(&StructTimeType);
if (PyModule_AddObject(module, "struct_time", (PyObject*) &StructTimeType)) {
Py_DECREF(&StructTimeType);
// struct_time type
state->struct_time_type = PyStructSequence_NewType(&struct_time_type_desc);
if (state->struct_time_type == NULL) {
return -1;
}
if (PyModule_AddType(module, state->struct_time_type)) {
return -1;
}
initialized = 1;
#if defined(__linux__) && !defined(__GLIBC__)
struct tm tm;
@ -2044,6 +2062,32 @@ time_exec(PyObject *module)
return 0;
}
static int
time_module_traverse(PyObject *module, visitproc visit, void *arg)
{
time_module_state *state = get_time_state(module);
Py_VISIT(state->struct_time_type);
return 0;
}
static int
time_module_clear(PyObject *module)
{
time_module_state *state = get_time_state(module);
Py_CLEAR(state->struct_time_type);
return 0;
}
static void
time_module_free(void *module)
{
time_module_clear((PyObject *)module);
}
static struct PyModuleDef_Slot time_slots[] = {
{Py_mod_exec, time_exec},
{0, NULL}
@ -2051,14 +2095,14 @@ static struct PyModuleDef_Slot time_slots[] = {
static struct PyModuleDef timemodule = {
PyModuleDef_HEAD_INIT,
"time",
module_doc,
0,
time_methods,
time_slots,
NULL,
NULL,
NULL
.m_name = "time",
.m_doc = module_doc,
.m_size = sizeof(time_module_state),
.m_methods = time_methods,
.m_slots = time_slots,
.m_traverse = time_module_traverse,
.m_clear = time_module_clear,
.m_free = time_module_free,
};
PyMODINIT_FUNC