Skip to content

bpo-40077: Convert zoneinfo type to heap type #24481

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions Lib/test/test_zoneinfo/test_zoneinfo.py
Original file line number Diff line number Diff line change
Expand Up @@ -1775,12 +1775,9 @@ def test_cache_location(self):
self.assertTrue(hasattr(py_zoneinfo.ZoneInfo, "_weak_cache"))

def test_gc_tracked(self):
# The pure Python version is tracked by the GC but (for now) the C
# version is not.
import gc

self.assertTrue(gc.is_tracked(py_zoneinfo.ZoneInfo))
self.assertFalse(gc.is_tracked(c_zoneinfo.ZoneInfo))
self.assertTrue(gc.is_tracked(c_zoneinfo.ZoneInfo))


@dataclasses.dataclass(frozen=True)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Convert ``zoneinfo.ZoneInfo`` type to heap type. Patch by Erlend E. Aasland.
75 changes: 49 additions & 26 deletions Modules/_zoneinfo.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ struct StrongCacheNode {
PyObject *zone;
};

static PyTypeObject PyZoneInfo_ZoneInfoType;
static PyTypeObject *PyZoneInfo_ZoneInfoType;

// Globals
static PyObject *TIMEDELTA_CACHE = NULL;
Expand Down Expand Up @@ -242,7 +242,7 @@ zoneinfo_new_instance(PyTypeObject *type, PyObject *key)
static PyObject *
get_weak_cache(PyTypeObject *type)
{
if (type == &PyZoneInfo_ZoneInfoType) {
if (type == PyZoneInfo_ZoneInfoType) {
return ZONEINFO_WEAK_CACHE;
}
else {
Expand Down Expand Up @@ -296,10 +296,19 @@ zoneinfo_new(PyTypeObject *type, PyObject *args, PyObject *kw)
return instance;
}

static int
zoneinfo_traverse(PyObject *self, visitproc visit, void *arg)
{
Py_VISIT(Py_TYPE(self));
return 0;
}

static void
zoneinfo_dealloc(PyObject *obj_self)
{
PyObject_GC_UnTrack(obj_self);
PyZoneInfo_ZoneInfo *self = (PyZoneInfo_ZoneInfo *)obj_self;
PyTypeObject *tp = Py_TYPE(obj_self);

if (self->weakreflist != NULL) {
PyObject_ClearWeakRefs(obj_self);
Expand Down Expand Up @@ -331,7 +340,8 @@ zoneinfo_dealloc(PyObject *obj_self)
Py_XDECREF(self->key);
Py_XDECREF(self->file_repr);

Py_TYPE(self)->tp_free((PyObject *)self);
tp->tp_free(self);
Py_DECREF(tp);
}

static PyObject *
Expand Down Expand Up @@ -2366,7 +2376,7 @@ find_in_strong_cache(const StrongCacheNode *const root, PyObject *const key)
static int
eject_from_strong_cache(const PyTypeObject *const type, PyObject *key)
{
if (type != &PyZoneInfo_ZoneInfoType) {
if (type != PyZoneInfo_ZoneInfoType) {
return 0;
}

Expand Down Expand Up @@ -2419,7 +2429,7 @@ move_strong_cache_node_to_front(StrongCacheNode **root, StrongCacheNode *node)
static PyObject *
zone_from_strong_cache(const PyTypeObject *const type, PyObject *const key)
{
if (type != &PyZoneInfo_ZoneInfoType) {
if (type != PyZoneInfo_ZoneInfoType) {
return NULL; // Strong cache currently only implemented for base class
}

Expand All @@ -2444,7 +2454,7 @@ static void
update_strong_cache(const PyTypeObject *const type, PyObject *key,
PyObject *zone)
{
if (type != &PyZoneInfo_ZoneInfoType) {
if (type != PyZoneInfo_ZoneInfoType) {
return;
}

Expand Down Expand Up @@ -2477,7 +2487,7 @@ update_strong_cache(const PyTypeObject *const type, PyObject *key,
void
clear_strong_cache(const PyTypeObject *const type)
{
if (type != &PyZoneInfo_ZoneInfoType) {
if (type != PyZoneInfo_ZoneInfoType) {
return;
}

Expand Down Expand Up @@ -2585,23 +2595,30 @@ static PyMemberDef zoneinfo_members[] = {
.type = T_OBJECT_EX,
.flags = READONLY,
.doc = NULL},
{.name = "__weaklistoffset__",
.offset = offsetof(PyZoneInfo_ZoneInfo, weakreflist),
.type = T_PYSSIZET,
.flags = READONLY},
{NULL}, /* Sentinel */
};

static PyTypeObject PyZoneInfo_ZoneInfoType = {
PyVarObject_HEAD_INIT(NULL, 0) //
.tp_name = "zoneinfo.ZoneInfo",
.tp_basicsize = sizeof(PyZoneInfo_ZoneInfo),
.tp_weaklistoffset = offsetof(PyZoneInfo_ZoneInfo, weakreflist),
.tp_repr = (reprfunc)zoneinfo_repr,
.tp_str = (reprfunc)zoneinfo_str,
.tp_getattro = PyObject_GenericGetAttr,
.tp_flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE),
/* .tp_doc = zoneinfo_doc, */
.tp_methods = zoneinfo_methods,
.tp_members = zoneinfo_members,
.tp_new = zoneinfo_new,
.tp_dealloc = zoneinfo_dealloc,
static PyType_Slot zoneinfo_slots[] = {
{Py_tp_repr, zoneinfo_repr},
{Py_tp_str, zoneinfo_str},
{Py_tp_getattro, PyObject_GenericGetAttr},
{Py_tp_methods, zoneinfo_methods},
{Py_tp_members, zoneinfo_members},
{Py_tp_new, zoneinfo_new},
{Py_tp_dealloc, zoneinfo_dealloc},
{Py_tp_traverse, zoneinfo_traverse},
{0, NULL},
};

static PyType_Spec zoneinfo_spec = {
.name = "zoneinfo.ZoneInfo",
.basicsize = sizeof(PyZoneInfo_ZoneInfo),
.flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC),
.slots = zoneinfo_slots,
};

/////
Expand Down Expand Up @@ -2633,7 +2650,7 @@ module_free()
Py_CLEAR(ZONEINFO_WEAK_CACHE);
}

clear_strong_cache(&PyZoneInfo_ZoneInfoType);
clear_strong_cache(PyZoneInfo_ZoneInfoType);
}

static int
Expand All @@ -2643,13 +2660,19 @@ zoneinfomodule_exec(PyObject *m)
if (PyDateTimeAPI == NULL) {
goto error;
}
PyZoneInfo_ZoneInfoType.tp_base = PyDateTimeAPI->TZInfoType;
if (PyType_Ready(&PyZoneInfo_ZoneInfoType) < 0) {
PyObject *base = (PyObject *)PyDateTimeAPI->TZInfoType;
PyZoneInfo_ZoneInfoType = (PyTypeObject *)PyType_FromModuleAndSpec(m,
&zoneinfo_spec,
base);
if (PyZoneInfo_ZoneInfoType == NULL) {
goto error;
}

Py_INCREF(&PyZoneInfo_ZoneInfoType);
PyModule_AddObject(m, "ZoneInfo", (PyObject *)&PyZoneInfo_ZoneInfoType);
int rc = PyModule_AddObjectRef(m, "ZoneInfo", (PyObject *)PyZoneInfo_ZoneInfoType);
Py_DECREF(PyZoneInfo_ZoneInfoType);
if (rc < 0) {
goto error;
}

/* Populate imports */
PyObject *_tzpath_module = PyImport_ImportModule("zoneinfo._tzpath");
Expand Down