summaryrefslogtreecommitdiffstats
path: root/contrib/tools/python3/src/Modules/_functoolsmodule.c
diff options
context:
space:
mode:
authorshadchin <[email protected]>2022-04-18 12:39:32 +0300
committershadchin <[email protected]>2022-04-18 12:39:32 +0300
commitd4be68e361f4258cf0848fc70018dfe37a2acc24 (patch)
tree153e294cd97ac8b5d7a989612704a0c1f58e8ad4 /contrib/tools/python3/src/Modules/_functoolsmodule.c
parent260c02f5ccf242d9d9b8a873afaf6588c00237d6 (diff)
IGNIETFERRO-1816 Update Python 3 from 3.9.12 to 3.10.4
ref:9f96be6d02ee8044fdd6f124b799b270c20ce641
Diffstat (limited to 'contrib/tools/python3/src/Modules/_functoolsmodule.c')
-rw-r--r--contrib/tools/python3/src/Modules/_functoolsmodule.c556
1 files changed, 307 insertions, 249 deletions
diff --git a/contrib/tools/python3/src/Modules/_functoolsmodule.c b/contrib/tools/python3/src/Modules/_functoolsmodule.c
index 42764a181d2..fa145216809 100644
--- a/contrib/tools/python3/src/Modules/_functoolsmodule.c
+++ b/contrib/tools/python3/src/Modules/_functoolsmodule.c
@@ -1,7 +1,9 @@
#include "Python.h"
+#include "pycore_long.h" // _PyLong_GetZero()
+#include "pycore_moduleobject.h" // _PyModule_GetState()
#include "pycore_object.h" // _PyObject_GC_TRACK
#include "pycore_pystate.h" // _PyThreadState_GET()
-#include "pycore_tupleobject.h"
+#include "pycore_tuple.h" // _PyTuple_ITEMS()
#include "structmember.h" // PyMemberDef
/* _functools module written and maintained
@@ -11,6 +13,23 @@
All rights reserved.
*/
+typedef struct _functools_state {
+ /* this object is used delimit args and keywords in the cache keys */
+ PyObject *kwd_mark;
+ PyTypeObject *partial_type;
+ PyTypeObject *keyobject_type;
+ PyTypeObject *lru_list_elem_type;
+} _functools_state;
+
+static inline _functools_state *
+get_functools_state(PyObject *module)
+{
+ void *state = _PyModule_GetState(module);
+ assert(state != NULL);
+ return (_functools_state *)state;
+}
+
+
/* partial object **********************************************************/
typedef struct {
@@ -23,9 +42,20 @@ typedef struct {
vectorcallfunc vectorcall;
} partialobject;
-static PyTypeObject partial_type;
-
static void partial_setvectorcall(partialobject *pto);
+static struct PyModuleDef _functools_module;
+static PyObject *
+partial_call(partialobject *pto, PyObject *args, PyObject *kwargs);
+
+static inline _functools_state *
+get_functools_state_by_type(PyTypeObject *type)
+{
+ PyObject *module = _PyType_GetModuleByDef(type, &_functools_module);
+ if (module == NULL) {
+ return NULL;
+ }
+ return get_functools_state(module);
+}
static PyObject *
partial_new(PyTypeObject *type, PyObject *args, PyObject *kw)
@@ -41,7 +71,11 @@ partial_new(PyTypeObject *type, PyObject *args, PyObject *kw)
pargs = pkw = NULL;
func = PyTuple_GET_ITEM(args, 0);
- if (Py_IS_TYPE(func, &partial_type) && type == &partial_type) {
+ if (Py_TYPE(func)->tp_call == (ternaryfunc)partial_call) {
+ // The type of "func" might not be exactly the same type object
+ // as "type", but if it is called using partial_call, it must have the
+ // same memory layout (fn, args and kw members).
+ // We can use its underlying function directly and merge the arguments.
partialobject *part = (partialobject *)func;
if (part->dict == NULL) {
pargs = part->args;
@@ -113,18 +147,39 @@ partial_new(PyTypeObject *type, PyObject *args, PyObject *kw)
return (PyObject *)pto;
}
+static int
+partial_clear(partialobject *pto)
+{
+ Py_CLEAR(pto->fn);
+ Py_CLEAR(pto->args);
+ Py_CLEAR(pto->kw);
+ Py_CLEAR(pto->dict);
+ return 0;
+}
+
+static int
+partial_traverse(partialobject *pto, visitproc visit, void *arg)
+{
+ Py_VISIT(Py_TYPE(pto));
+ Py_VISIT(pto->fn);
+ Py_VISIT(pto->args);
+ Py_VISIT(pto->kw);
+ Py_VISIT(pto->dict);
+ return 0;
+}
+
static void
partial_dealloc(partialobject *pto)
{
+ PyTypeObject *tp = Py_TYPE(pto);
/* bpo-31095: UnTrack is needed before calling any callbacks */
PyObject_GC_UnTrack(pto);
- if (pto->weakreflist != NULL)
+ if (pto->weakreflist != NULL) {
PyObject_ClearWeakRefs((PyObject *) pto);
- Py_XDECREF(pto->fn);
- Py_XDECREF(pto->args);
- Py_XDECREF(pto->kw);
- Py_XDECREF(pto->dict);
- Py_TYPE(pto)->tp_free(pto);
+ }
+ (void)partial_clear(pto);
+ tp->tp_free(pto);
+ Py_DECREF(tp);
}
@@ -271,16 +326,6 @@ partial_call(partialobject *pto, PyObject *args, PyObject *kwargs)
return res;
}
-static int
-partial_traverse(partialobject *pto, visitproc visit, void *arg)
-{
- Py_VISIT(pto->fn);
- Py_VISIT(pto->args);
- Py_VISIT(pto->kw);
- Py_VISIT(pto->dict);
- return 0;
-}
-
PyDoc_STRVAR(partial_doc,
"partial(func, *args, **keywords) - new function with partial application\n\
of the given arguments and keywords.\n");
@@ -293,6 +338,12 @@ static PyMemberDef partial_memberlist[] = {
"tuple of arguments to future partial calls"},
{"keywords", T_OBJECT, OFF(kw), READONLY,
"dictionary of keyword arguments to future partial calls"},
+ {"__weaklistoffset__", T_PYSSIZET,
+ offsetof(partialobject, weakreflist), READONLY},
+ {"__dictoffset__", T_PYSSIZET,
+ offsetof(partialobject, dict), READONLY},
+ {"__vectorcalloffset__", T_PYSSIZET,
+ offsetof(partialobject, vectorcall), READONLY},
{NULL} /* Sentinel */
};
@@ -419,49 +470,30 @@ static PyMethodDef partial_methods[] = {
{NULL, NULL} /* sentinel */
};
-static PyTypeObject partial_type = {
- PyVarObject_HEAD_INIT(NULL, 0)
- "functools.partial", /* tp_name */
- sizeof(partialobject), /* tp_basicsize */
- 0, /* tp_itemsize */
- /* methods */
- (destructor)partial_dealloc, /* tp_dealloc */
- offsetof(partialobject, vectorcall),/* tp_vectorcall_offset */
- 0, /* tp_getattr */
- 0, /* tp_setattr */
- 0, /* tp_as_async */
- (reprfunc)partial_repr, /* tp_repr */
- 0, /* tp_as_number */
- 0, /* tp_as_sequence */
- 0, /* tp_as_mapping */
- 0, /* tp_hash */
- (ternaryfunc)partial_call, /* tp_call */
- 0, /* tp_str */
- PyObject_GenericGetAttr, /* tp_getattro */
- PyObject_GenericSetAttr, /* tp_setattro */
- 0, /* tp_as_buffer */
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
- Py_TPFLAGS_BASETYPE |
- Py_TPFLAGS_HAVE_VECTORCALL, /* tp_flags */
- partial_doc, /* tp_doc */
- (traverseproc)partial_traverse, /* tp_traverse */
- 0, /* tp_clear */
- 0, /* tp_richcompare */
- offsetof(partialobject, weakreflist), /* tp_weaklistoffset */
- 0, /* tp_iter */
- 0, /* tp_iternext */
- partial_methods, /* tp_methods */
- partial_memberlist, /* tp_members */
- partial_getsetlist, /* tp_getset */
- 0, /* tp_base */
- 0, /* tp_dict */
- 0, /* tp_descr_get */
- 0, /* tp_descr_set */
- offsetof(partialobject, dict), /* tp_dictoffset */
- 0, /* tp_init */
- 0, /* tp_alloc */
- partial_new, /* tp_new */
- PyObject_GC_Del, /* tp_free */
+static PyType_Slot partial_type_slots[] = {
+ {Py_tp_dealloc, partial_dealloc},
+ {Py_tp_repr, partial_repr},
+ {Py_tp_call, partial_call},
+ {Py_tp_getattro, PyObject_GenericGetAttr},
+ {Py_tp_setattro, PyObject_GenericSetAttr},
+ {Py_tp_doc, (void *)partial_doc},
+ {Py_tp_traverse, partial_traverse},
+ {Py_tp_clear, partial_clear},
+ {Py_tp_methods, partial_methods},
+ {Py_tp_members, partial_memberlist},
+ {Py_tp_getset, partial_getsetlist},
+ {Py_tp_new, partial_new},
+ {Py_tp_free, PyObject_GC_Del},
+ {0, 0}
+};
+
+static PyType_Spec partial_type_spec = {
+ .name = "functools.partial",
+ .basicsize = sizeof(partialobject),
+ .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
+ Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_VECTORCALL |
+ Py_TPFLAGS_IMMUTABLETYPE,
+ .slots = partial_type_slots
};
@@ -473,29 +505,30 @@ typedef struct {
PyObject *object;
} keyobject;
+static int
+keyobject_clear(keyobject *ko)
+{
+ Py_CLEAR(ko->cmp);
+ Py_CLEAR(ko->object);
+ return 0;
+}
+
static void
keyobject_dealloc(keyobject *ko)
{
- Py_DECREF(ko->cmp);
- Py_XDECREF(ko->object);
- PyObject_FREE(ko);
+ PyTypeObject *tp = Py_TYPE(ko);
+ PyObject_GC_UnTrack(ko);
+ (void)keyobject_clear(ko);
+ tp->tp_free(ko);
+ Py_DECREF(tp);
}
static int
keyobject_traverse(keyobject *ko, visitproc visit, void *arg)
{
+ Py_VISIT(Py_TYPE(ko));
Py_VISIT(ko->cmp);
- if (ko->object)
- Py_VISIT(ko->object);
- return 0;
-}
-
-static int
-keyobject_clear(keyobject *ko)
-{
- Py_CLEAR(ko->cmp);
- if (ko->object)
- Py_CLEAR(ko->object);
+ Py_VISIT(ko->object);
return 0;
}
@@ -512,38 +545,23 @@ keyobject_call(keyobject *ko, PyObject *args, PyObject *kwds);
static PyObject *
keyobject_richcompare(PyObject *ko, PyObject *other, int op);
-static PyTypeObject keyobject_type = {
- PyVarObject_HEAD_INIT(&PyType_Type, 0)
- "functools.KeyWrapper", /* tp_name */
- sizeof(keyobject), /* tp_basicsize */
- 0, /* tp_itemsize */
- /* methods */
- (destructor)keyobject_dealloc, /* tp_dealloc */
- 0, /* tp_vectorcall_offset */
- 0, /* tp_getattr */
- 0, /* tp_setattr */
- 0, /* tp_as_async */
- 0, /* tp_repr */
- 0, /* tp_as_number */
- 0, /* tp_as_sequence */
- 0, /* tp_as_mapping */
- 0, /* tp_hash */
- (ternaryfunc)keyobject_call, /* tp_call */
- 0, /* tp_str */
- PyObject_GenericGetAttr, /* tp_getattro */
- 0, /* tp_setattro */
- 0, /* tp_as_buffer */
- Py_TPFLAGS_DEFAULT, /* tp_flags */
- 0, /* tp_doc */
- (traverseproc)keyobject_traverse, /* tp_traverse */
- (inquiry)keyobject_clear, /* tp_clear */
- keyobject_richcompare, /* tp_richcompare */
- 0, /* tp_weaklistoffset */
- 0, /* tp_iter */
- 0, /* tp_iternext */
- 0, /* tp_methods */
- keyobject_members, /* tp_members */
- 0, /* tp_getset */
+static PyType_Slot keyobject_type_slots[] = {
+ {Py_tp_dealloc, keyobject_dealloc},
+ {Py_tp_call, keyobject_call},
+ {Py_tp_getattro, PyObject_GenericGetAttr},
+ {Py_tp_traverse, keyobject_traverse},
+ {Py_tp_clear, keyobject_clear},
+ {Py_tp_richcompare, keyobject_richcompare},
+ {Py_tp_members, keyobject_members},
+ {0, 0}
+};
+
+static PyType_Spec keyobject_type_spec = {
+ .name = "functools.KeyWrapper",
+ .basicsize = sizeof(keyobject),
+ .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION |
+ Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_IMMUTABLETYPE),
+ .slots = keyobject_type_slots
};
static PyObject *
@@ -555,13 +573,16 @@ keyobject_call(keyobject *ko, PyObject *args, PyObject *kwds)
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:K", kwargs, &object))
return NULL;
- result = PyObject_New(keyobject, &keyobject_type);
- if (!result)
+
+ result = PyObject_GC_New(keyobject, Py_TYPE(ko));
+ if (result == NULL) {
return NULL;
+ }
Py_INCREF(ko->cmp);
result->cmp = ko->cmp;
Py_INCREF(object);
result->object = object;
+ PyObject_GC_Track(result);
return (PyObject *)result;
}
@@ -575,7 +596,7 @@ keyobject_richcompare(PyObject *ko, PyObject *other, int op)
PyObject *answer;
PyObject* stack[2];
- if (!Py_IS_TYPE(other, &keyobject_type)) {
+ if (!Py_IS_TYPE(other, Py_TYPE(ko))) {
PyErr_Format(PyExc_TypeError, "other argument must be K instance");
return NULL;
}
@@ -598,7 +619,7 @@ keyobject_richcompare(PyObject *ko, PyObject *other, int op)
return NULL;
}
- answer = PyObject_RichCompare(res, _PyLong_Zero, op);
+ answer = PyObject_RichCompare(res, _PyLong_GetZero(), op);
Py_DECREF(res);
return answer;
}
@@ -609,15 +630,19 @@ functools_cmp_to_key(PyObject *self, PyObject *args, PyObject *kwds)
PyObject *cmp;
static char *kwargs[] = {"mycmp", NULL};
keyobject *object;
+ _functools_state *state;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:cmp_to_key", kwargs, &cmp))
return NULL;
- object = PyObject_New(keyobject, &keyobject_type);
+
+ state = get_functools_state(self);
+ object = PyObject_GC_New(keyobject, state->keyobject_type);
if (!object)
return NULL;
Py_INCREF(cmp);
object->cmp = cmp;
object->object = NULL;
+ PyObject_GC_Track(object);
return (PyObject *)object;
}
@@ -686,7 +711,7 @@ functools_reduce(PyObject *self, PyObject *args)
if (result == NULL)
PyErr_SetString(PyExc_TypeError,
- "reduce() of empty sequence with no initial value");
+ "reduce() of empty iterable with no initial value");
Py_DECREF(it);
return result;
@@ -699,14 +724,14 @@ Fail:
}
PyDoc_STRVAR(functools_reduce_doc,
-"reduce(function, sequence[, initial]) -> value\n\
+"reduce(function, iterable[, initial]) -> value\n\
\n\
-Apply a function of two arguments cumulatively to the items of a sequence,\n\
-from left to right, so as to reduce the sequence to a single value.\n\
-For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) calculates\n\
+Apply a function of two arguments cumulatively to the items of a sequence\n\
+or iterable, from left to right, so as to reduce the iterable to a single\n\
+value. For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) calculates\n\
((((1+2)+3)+4)+5). If initial is present, it is placed before the items\n\
-of the sequence in the calculation, and serves as a default when the\n\
-sequence is empty.");
+of the iterable in the calculation, and serves as a default when the\n\
+iterable is empty.");
/* lru_cache object **********************************************************/
@@ -729,10 +754,6 @@ sequence is empty.");
*/
-
-/* this object is used delimit args and keywords in the cache keys */
-static PyObject *kwd_mark = NULL;
-
struct lru_list_elem;
struct lru_cache_object;
@@ -746,33 +767,24 @@ typedef struct lru_list_elem {
static void
lru_list_elem_dealloc(lru_list_elem *link)
{
+ PyTypeObject *tp = Py_TYPE(link);
Py_XDECREF(link->key);
Py_XDECREF(link->result);
- PyObject_Del(link);
+ tp->tp_free(link);
+ Py_DECREF(tp);
}
-static PyTypeObject lru_list_elem_type = {
- PyVarObject_HEAD_INIT(&PyType_Type, 0)
- "functools._lru_list_elem", /* tp_name */
- sizeof(lru_list_elem), /* tp_basicsize */
- 0, /* tp_itemsize */
- /* methods */
- (destructor)lru_list_elem_dealloc, /* tp_dealloc */
- 0, /* tp_vectorcall_offset */
- 0, /* tp_getattr */
- 0, /* tp_setattr */
- 0, /* tp_as_async */
- 0, /* tp_repr */
- 0, /* tp_as_number */
- 0, /* tp_as_sequence */
- 0, /* tp_as_mapping */
- 0, /* tp_hash */
- 0, /* tp_call */
- 0, /* tp_str */
- 0, /* tp_getattro */
- 0, /* tp_setattro */
- 0, /* tp_as_buffer */
- Py_TPFLAGS_DEFAULT, /* tp_flags */
+static PyType_Slot lru_list_elem_type_slots[] = {
+ {Py_tp_dealloc, lru_list_elem_dealloc},
+ {0, 0}
+};
+
+static PyType_Spec lru_list_elem_type_spec = {
+ .name = "functools._lru_list_elem",
+ .basicsize = sizeof(lru_list_elem),
+ .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION |
+ Py_TPFLAGS_IMMUTABLETYPE,
+ .slots = lru_list_elem_type_slots
};
@@ -787,15 +799,17 @@ typedef struct lru_cache_object {
PyObject *func;
Py_ssize_t maxsize;
Py_ssize_t misses;
+ /* the kwd_mark is used delimit args and keywords in the cache keys */
+ PyObject *kwd_mark;
+ PyTypeObject *lru_list_elem_type;
PyObject *cache_info_type;
PyObject *dict;
PyObject *weakreflist;
} lru_cache_object;
-static PyTypeObject lru_cache_type;
-
static PyObject *
-lru_cache_make_key(PyObject *args, PyObject *kwds, int typed)
+lru_cache_make_key(PyObject *kwd_mark, PyObject *args,
+ PyObject *kwds, int typed)
{
PyObject *key, *keyword, *value;
Py_ssize_t key_size, pos, key_pos, kwds_size;
@@ -879,7 +893,7 @@ infinite_lru_cache_wrapper(lru_cache_object *self, PyObject *args, PyObject *kwd
{
PyObject *result;
Py_hash_t hash;
- PyObject *key = lru_cache_make_key(args, kwds, self->typed);
+ PyObject *key = lru_cache_make_key(self->kwd_mark, args, kwds, self->typed);
if (!key)
return NULL;
hash = PyObject_Hash(key);
@@ -980,7 +994,7 @@ bounded_lru_cache_wrapper(lru_cache_object *self, PyObject *args, PyObject *kwds
PyObject *key, *result, *testresult;
Py_hash_t hash;
- key = lru_cache_make_key(args, kwds, self->typed);
+ key = lru_cache_make_key(self->kwd_mark, args, kwds, self->typed);
if (!key)
return NULL;
hash = PyObject_Hash(key);
@@ -1035,7 +1049,7 @@ bounded_lru_cache_wrapper(lru_cache_object *self, PyObject *args, PyObject *kwds
{
/* Cache is not full, so put the result in a new link */
link = (lru_list_elem *)PyObject_New(lru_list_elem,
- &lru_list_elem_type);
+ self->lru_list_elem_type);
if (link == NULL) {
Py_DECREF(key);
Py_DECREF(result);
@@ -1146,6 +1160,7 @@ lru_cache_new(PyTypeObject *type, PyObject *args, PyObject *kw)
lru_cache_object *obj;
Py_ssize_t maxsize;
PyObject *(*wrapper)(lru_cache_object *, PyObject *, PyObject *);
+ _functools_state *state;
static char *keywords[] = {"user_function", "maxsize", "typed",
"cache_info_type", NULL};
@@ -1161,6 +1176,11 @@ lru_cache_new(PyTypeObject *type, PyObject *args, PyObject *kw)
return NULL;
}
+ state = get_functools_state_by_type(type);
+ if (state == NULL) {
+ return NULL;
+ }
+
/* select the caching function, and make/inc maxsize_O */
if (maxsize_O == Py_None) {
wrapper = infinite_lru_cache_wrapper;
@@ -1200,6 +1220,10 @@ lru_cache_new(PyTypeObject *type, PyObject *args, PyObject *kw)
obj->func = func;
obj->misses = obj->hits = 0;
obj->maxsize = maxsize;
+ Py_INCREF(state->kwd_mark);
+ obj->kwd_mark = state->kwd_mark;
+ Py_INCREF(state->lru_list_elem_type);
+ obj->lru_list_elem_type = state->lru_list_elem_type;
Py_INCREF(cache_info_type);
obj->cache_info_type = cache_info_type;
obj->dict = NULL;
@@ -1229,22 +1253,33 @@ lru_cache_clear_list(lru_list_elem *link)
}
}
+static int
+lru_cache_tp_clear(lru_cache_object *self)
+{
+ lru_list_elem *list = lru_cache_unlink_list(self);
+ Py_CLEAR(self->cache);
+ Py_CLEAR(self->func);
+ Py_CLEAR(self->kwd_mark);
+ Py_CLEAR(self->lru_list_elem_type);
+ Py_CLEAR(self->cache_info_type);
+ Py_CLEAR(self->dict);
+ lru_cache_clear_list(list);
+ return 0;
+}
+
static void
lru_cache_dealloc(lru_cache_object *obj)
{
- lru_list_elem *list;
+ PyTypeObject *tp = Py_TYPE(obj);
/* bpo-31095: UnTrack is needed before calling any callbacks */
PyObject_GC_UnTrack(obj);
- if (obj->weakreflist != NULL)
+ if (obj->weakreflist != NULL) {
PyObject_ClearWeakRefs((PyObject*)obj);
+ }
- list = lru_cache_unlink_list(obj);
- Py_XDECREF(obj->cache);
- Py_XDECREF(obj->func);
- Py_XDECREF(obj->cache_info_type);
- Py_XDECREF(obj->dict);
- lru_cache_clear_list(list);
- Py_TYPE(obj)->tp_free(obj);
+ (void)lru_cache_tp_clear(obj);
+ tp->tp_free(obj);
+ Py_DECREF(tp);
}
static PyObject *
@@ -1309,32 +1344,24 @@ lru_cache_deepcopy(PyObject *self, PyObject *unused)
static int
lru_cache_tp_traverse(lru_cache_object *self, visitproc visit, void *arg)
{
+ Py_VISIT(Py_TYPE(self));
lru_list_elem *link = self->root.next;
while (link != &self->root) {
lru_list_elem *next = link->next;
Py_VISIT(link->key);
Py_VISIT(link->result);
+ Py_VISIT(Py_TYPE(link));
link = next;
}
- Py_VISIT(self->func);
Py_VISIT(self->cache);
+ Py_VISIT(self->func);
+ Py_VISIT(self->kwd_mark);
+ Py_VISIT(self->lru_list_elem_type);
Py_VISIT(self->cache_info_type);
Py_VISIT(self->dict);
return 0;
}
-static int
-lru_cache_tp_clear(lru_cache_object *self)
-{
- lru_list_elem *list = lru_cache_unlink_list(self);
- Py_CLEAR(self->func);
- Py_CLEAR(self->cache);
- Py_CLEAR(self->cache_info_type);
- Py_CLEAR(self->dict);
- lru_cache_clear_list(list);
- return 0;
-}
-
PyDoc_STRVAR(lru_cache_doc,
"Create a cached callable that wraps another function.\n\
@@ -1366,51 +1393,37 @@ static PyGetSetDef lru_cache_getsetlist[] = {
{NULL}
};
-static PyTypeObject lru_cache_type = {
- PyVarObject_HEAD_INIT(NULL, 0)
- "functools._lru_cache_wrapper", /* tp_name */
- sizeof(lru_cache_object), /* tp_basicsize */
- 0, /* tp_itemsize */
- /* methods */
- (destructor)lru_cache_dealloc, /* tp_dealloc */
- 0, /* tp_vectorcall_offset */
- 0, /* tp_getattr */
- 0, /* tp_setattr */
- 0, /* tp_as_async */
- 0, /* tp_repr */
- 0, /* tp_as_number */
- 0, /* tp_as_sequence */
- 0, /* tp_as_mapping */
- 0, /* tp_hash */
- (ternaryfunc)lru_cache_call, /* tp_call */
- 0, /* tp_str */
- 0, /* tp_getattro */
- 0, /* tp_setattro */
- 0, /* tp_as_buffer */
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
- Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_METHOD_DESCRIPTOR,
- /* tp_flags */
- lru_cache_doc, /* tp_doc */
- (traverseproc)lru_cache_tp_traverse,/* tp_traverse */
- (inquiry)lru_cache_tp_clear, /* tp_clear */
- 0, /* tp_richcompare */
- offsetof(lru_cache_object, weakreflist),
- /* tp_weaklistoffset */
- 0, /* tp_iter */
- 0, /* tp_iternext */
- lru_cache_methods, /* tp_methods */
- 0, /* tp_members */
- lru_cache_getsetlist, /* tp_getset */
- 0, /* tp_base */
- 0, /* tp_dict */
- lru_cache_descr_get, /* tp_descr_get */
- 0, /* tp_descr_set */
- offsetof(lru_cache_object, dict), /* tp_dictoffset */
- 0, /* tp_init */
- 0, /* tp_alloc */
- lru_cache_new, /* tp_new */
+static PyMemberDef lru_cache_memberlist[] = {
+ {"__dictoffset__", T_PYSSIZET,
+ offsetof(lru_cache_object, dict), READONLY},
+ {"__weaklistoffset__", T_PYSSIZET,
+ offsetof(lru_cache_object, weakreflist), READONLY},
+ {NULL} /* Sentinel */
+};
+
+static PyType_Slot lru_cache_type_slots[] = {
+ {Py_tp_dealloc, lru_cache_dealloc},
+ {Py_tp_call, lru_cache_call},
+ {Py_tp_doc, (void *)lru_cache_doc},
+ {Py_tp_traverse, lru_cache_tp_traverse},
+ {Py_tp_clear, lru_cache_tp_clear},
+ {Py_tp_methods, lru_cache_methods},
+ {Py_tp_members, lru_cache_memberlist},
+ {Py_tp_getset, lru_cache_getsetlist},
+ {Py_tp_descr_get, lru_cache_descr_get},
+ {Py_tp_new, lru_cache_new},
+ {0, 0}
};
+static PyType_Spec lru_cache_type_spec = {
+ .name = "functools._lru_cache_wrapper",
+ .basicsize = sizeof(lru_cache_object),
+ .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
+ Py_TPFLAGS_METHOD_DESCRIPTOR | Py_TPFLAGS_IMMUTABLETYPE,
+ .slots = lru_cache_type_slots
+};
+
+
/* module level code ********************************************************/
PyDoc_STRVAR(_functools_doc,
@@ -1423,38 +1436,83 @@ static PyMethodDef _functools_methods[] = {
{NULL, NULL} /* sentinel */
};
-static void
-_functools_free(void *m)
-{
- // FIXME: Do not clear kwd_mark to avoid NULL pointer dereferencing if we have
- // other modules instances that could use it. Will fix when PEP-573 land
- // and we could move kwd_mark to a per-module state.
- // Py_CLEAR(kwd_mark);
-}
-
static int
_functools_exec(PyObject *module)
{
- PyTypeObject *typelist[] = {
- &partial_type,
- &lru_cache_type
- };
+ _functools_state *state = get_functools_state(module);
+ state->kwd_mark = _PyObject_CallNoArg((PyObject *)&PyBaseObject_Type);
+ if (state->kwd_mark == NULL) {
+ return -1;
+ }
- if (!kwd_mark) {
- kwd_mark = _PyObject_CallNoArg((PyObject *)&PyBaseObject_Type);
- if (!kwd_mark) {
- return -1;
- }
+ state->partial_type = (PyTypeObject *)PyType_FromModuleAndSpec(module,
+ &partial_type_spec, NULL);
+ if (state->partial_type == NULL) {
+ return -1;
+ }
+ if (PyModule_AddType(module, state->partial_type) < 0) {
+ return -1;
}
- for (size_t i = 0; i < Py_ARRAY_LENGTH(typelist); i++) {
- if (PyModule_AddType(module, typelist[i]) < 0) {
- return -1;
- }
+ PyObject *lru_cache_type = PyType_FromModuleAndSpec(module,
+ &lru_cache_type_spec, NULL);
+ if (lru_cache_type == NULL) {
+ return -1;
+ }
+ if (PyModule_AddType(module, (PyTypeObject *)lru_cache_type) < 0) {
+ Py_DECREF(lru_cache_type);
+ return -1;
+ }
+ Py_DECREF(lru_cache_type);
+
+ state->keyobject_type = (PyTypeObject *)PyType_FromModuleAndSpec(module,
+ &keyobject_type_spec, NULL);
+ if (state->keyobject_type == NULL) {
+ return -1;
+ }
+ if (PyModule_AddType(module, state->keyobject_type) < 0) {
+ return -1;
+ }
+
+ state->lru_list_elem_type = (PyTypeObject *)PyType_FromModuleAndSpec(
+ module, &lru_list_elem_type_spec, NULL);
+ if (state->lru_list_elem_type == NULL) {
+ return -1;
}
+ // lru_list_elem is used only in _lru_cache_wrapper.
+ // So we don't expose it in module namespace.
+
+ return 0;
+}
+
+static int
+_functools_traverse(PyObject *module, visitproc visit, void *arg)
+{
+ _functools_state *state = get_functools_state(module);
+ Py_VISIT(state->kwd_mark);
+ Py_VISIT(state->partial_type);
+ Py_VISIT(state->keyobject_type);
+ Py_VISIT(state->lru_list_elem_type);
+ return 0;
+}
+
+static int
+_functools_clear(PyObject *module)
+{
+ _functools_state *state = get_functools_state(module);
+ Py_CLEAR(state->kwd_mark);
+ Py_CLEAR(state->partial_type);
+ Py_CLEAR(state->keyobject_type);
+ Py_CLEAR(state->lru_list_elem_type);
return 0;
}
+static void
+_functools_free(void *module)
+{
+ _functools_clear((PyObject *)module);
+}
+
static struct PyModuleDef_Slot _functools_slots[] = {
{Py_mod_exec, _functools_exec},
{0, NULL}
@@ -1462,14 +1520,14 @@ static struct PyModuleDef_Slot _functools_slots[] = {
static struct PyModuleDef _functools_module = {
PyModuleDef_HEAD_INIT,
- "_functools",
- _functools_doc,
- 0,
- _functools_methods,
- _functools_slots,
- NULL,
- NULL,
- _functools_free,
+ .m_name = "_functools",
+ .m_doc = _functools_doc,
+ .m_size = sizeof(_functools_state),
+ .m_methods = _functools_methods,
+ .m_slots = _functools_slots,
+ .m_traverse = _functools_traverse,
+ .m_clear = _functools_clear,
+ .m_free = _functools_free,
};
PyMODINIT_FUNC