summaryrefslogtreecommitdiffstats
path: root/contrib/tools/python3/Modules/_functoolsmodule.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/tools/python3/Modules/_functoolsmodule.c')
-rw-r--r--contrib/tools/python3/Modules/_functoolsmodule.c210
1 files changed, 136 insertions, 74 deletions
diff --git a/contrib/tools/python3/Modules/_functoolsmodule.c b/contrib/tools/python3/Modules/_functoolsmodule.c
index a6d1b83984b..a1682ec4c0e 100644
--- a/contrib/tools/python3/Modules/_functoolsmodule.c
+++ b/contrib/tools/python3/Modules/_functoolsmodule.c
@@ -6,7 +6,8 @@
#include "pycore_object.h" // _PyObject_GC_TRACK
#include "pycore_pystate.h" // _PyThreadState_GET()
#include "pycore_tuple.h" // _PyTuple_ITEMS()
-#include "structmember.h" // PyMemberDef
+#include "pycore_weakref.h" // FT_CLEAR_WEAKREFS()
+
#include "clinic/_functoolsmodule.c.h"
/*[clinic input]
@@ -79,12 +80,19 @@ partial_new(PyTypeObject *type, PyObject *args, PyObject *kw)
return NULL;
}
+ _functools_state *state = get_functools_state_by_type(type);
+ if (state == NULL) {
+ return NULL;
+ }
+
pargs = pkw = NULL;
func = PyTuple_GET_ITEM(args, 0);
- 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).
+
+ int res = PyObject_TypeCheck(func, state->partial_type);
+ if (res == -1) {
+ return NULL;
+ }
+ if (res == 1) {
// We can use its underlying function directly and merge the arguments.
partialobject *part = (partialobject *)func;
if (part->dict == NULL) {
@@ -182,14 +190,27 @@ 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) {
- PyObject_ClearWeakRefs((PyObject *) pto);
- }
+ FT_CLEAR_WEAKREFS((PyObject*)pto, pto->weakreflist);
(void)partial_clear(pto);
tp->tp_free(pto);
Py_DECREF(tp);
}
+static PyObject *
+partial_descr_get(PyObject *self, PyObject *obj, PyObject *type)
+{
+ if (obj == Py_None || obj == NULL) {
+ return Py_NewRef(self);
+ }
+ if (PyErr_WarnEx(PyExc_FutureWarning,
+ "functools.partial will be a method descriptor in "
+ "future Python versions; wrap it in staticmethod() "
+ "if you want to preserve the old behavior", 1) < 0)
+ {
+ return NULL;
+ }
+ return Py_NewRef(self);
+}
/* Merging keyword arguments using the vectorcall convention is messy, so
* if we would need to do that, we stop using vectorcall and fall back
@@ -199,7 +220,9 @@ partial_vectorcall_fallback(PyThreadState *tstate, partialobject *pto,
PyObject *const *args, size_t nargsf,
PyObject *kwnames)
{
+#ifndef Py_GIL_DISABLED
pto->vectorcall = NULL;
+#endif
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
return _PyObject_MakeTpCall(tstate, (PyObject *)pto,
args, nargs, kwnames);
@@ -276,7 +299,7 @@ partial_vectorcall(partialobject *pto, PyObject *const *args,
static void
partial_setvectorcall(partialobject *pto)
{
- if (_PyVectorcall_Function(pto->fn) == NULL) {
+ if (PyVectorcall_Function(pto->fn) == NULL) {
/* Don't use vectorcall if the underlying function doesn't support it */
pto->vectorcall = NULL;
}
@@ -335,23 +358,24 @@ partial_call(partialobject *pto, PyObject *args, PyObject *kwargs)
}
PyDoc_STRVAR(partial_doc,
-"partial(func, *args, **keywords) - new function with partial application\n\
- of the given arguments and keywords.\n");
+"partial(func, /, *args, **keywords)\n--\n\n\
+Create a new function with partial application of the given arguments\n\
+and keywords.");
#define OFF(x) offsetof(partialobject, x)
static PyMemberDef partial_memberlist[] = {
- {"func", T_OBJECT, OFF(fn), READONLY,
+ {"func", _Py_T_OBJECT, OFF(fn), Py_READONLY,
"function object to use in future partial calls"},
- {"args", T_OBJECT, OFF(args), READONLY,
+ {"args", _Py_T_OBJECT, OFF(args), Py_READONLY,
"tuple of arguments to future partial calls"},
- {"keywords", T_OBJECT, OFF(kw), READONLY,
+ {"keywords", _Py_T_OBJECT, OFF(kw), Py_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},
+ {"__weaklistoffset__", Py_T_PYSSIZET,
+ offsetof(partialobject, weakreflist), Py_READONLY},
+ {"__dictoffset__", Py_T_PYSSIZET,
+ offsetof(partialobject, dict), Py_READONLY},
+ {"__vectorcalloffset__", Py_T_PYSSIZET,
+ offsetof(partialobject, vectorcall), Py_READONLY},
{NULL} /* Sentinel */
};
@@ -364,46 +388,70 @@ static PyObject *
partial_repr(partialobject *pto)
{
PyObject *result = NULL;
- PyObject *arglist;
+ PyObject *arglist = NULL;
+ PyObject *mod = NULL;
+ PyObject *name = NULL;
Py_ssize_t i, n;
PyObject *key, *value;
int status;
status = Py_ReprEnter((PyObject *)pto);
if (status != 0) {
- if (status < 0)
+ if (status < 0) {
return NULL;
+ }
return PyUnicode_FromString("...");
}
+ /* Reference arguments in case they change */
+ PyObject *fn = Py_NewRef(pto->fn);
+ PyObject *args = Py_NewRef(pto->args);
+ PyObject *kw = Py_NewRef(pto->kw);
+ assert(PyTuple_Check(args));
+ assert(PyDict_Check(kw));
arglist = PyUnicode_FromString("");
- if (arglist == NULL)
+ if (arglist == NULL) {
goto done;
+ }
/* Pack positional arguments */
- assert (PyTuple_Check(pto->args));
- n = PyTuple_GET_SIZE(pto->args);
+ n = PyTuple_GET_SIZE(args);
for (i = 0; i < n; i++) {
Py_SETREF(arglist, PyUnicode_FromFormat("%U, %R", arglist,
- PyTuple_GET_ITEM(pto->args, i)));
- if (arglist == NULL)
+ PyTuple_GET_ITEM(args, i)));
+ if (arglist == NULL) {
goto done;
+ }
}
/* Pack keyword arguments */
- assert (PyDict_Check(pto->kw));
- for (i = 0; PyDict_Next(pto->kw, &i, &key, &value);) {
+ for (i = 0; PyDict_Next(kw, &i, &key, &value);) {
/* Prevent key.__str__ from deleting the value. */
Py_INCREF(value);
Py_SETREF(arglist, PyUnicode_FromFormat("%U, %S=%R", arglist,
key, value));
Py_DECREF(value);
- if (arglist == NULL)
+ if (arglist == NULL) {
goto done;
+ }
}
- result = PyUnicode_FromFormat("%s(%R%U)", Py_TYPE(pto)->tp_name,
- pto->fn, arglist);
- Py_DECREF(arglist);
- done:
+ mod = PyType_GetModuleName(Py_TYPE(pto));
+ if (mod == NULL) {
+ goto done;
+ }
+
+ name = PyType_GetQualName(Py_TYPE(pto));
+ if (name == NULL) {
+ goto done;
+ }
+
+ result = PyUnicode_FromFormat("%S.%S(%R%U)", mod, name, fn, arglist);
+done:
+ Py_XDECREF(name);
+ Py_XDECREF(mod);
+ Py_XDECREF(arglist);
+ Py_DECREF(fn);
+ Py_DECREF(args);
+ Py_DECREF(kw);
Py_ReprLeave((PyObject *)pto);
return result;
}
@@ -432,7 +480,8 @@ partial_setstate(partialobject *pto, PyObject *state)
!PyArg_ParseTuple(state, "OOOO", &fn, &fnargs, &kw, &dict) ||
!PyCallable_Check(fn) ||
!PyTuple_Check(fnargs) ||
- (kw != Py_None && !PyDict_Check(kw)))
+ (kw != Py_None && !PyDict_Check(kw)) ||
+ (dict != Py_None && !PyDict_Check(dict)))
{
PyErr_SetString(PyExc_TypeError, "invalid partial state");
return NULL;
@@ -489,6 +538,7 @@ static PyType_Slot partial_type_slots[] = {
{Py_tp_methods, partial_methods},
{Py_tp_members, partial_memberlist},
{Py_tp_getset, partial_getsetlist},
+ {Py_tp_descr_get, (descrgetfunc)partial_descr_get},
{Py_tp_new, partial_new},
{Py_tp_free, PyObject_GC_Del},
{0, 0}
@@ -540,13 +590,24 @@ keyobject_traverse(keyobject *ko, visitproc visit, void *arg)
}
static PyMemberDef keyobject_members[] = {
- {"obj", T_OBJECT,
+ {"obj", _Py_T_OBJECT,
offsetof(keyobject, object), 0,
PyDoc_STR("Value wrapped by a key function.")},
{NULL}
};
static PyObject *
+keyobject_text_signature(PyObject *self, void *Py_UNUSED(ignored))
+{
+ return PyUnicode_FromString("(obj)");
+}
+
+static PyGetSetDef keyobject_getset[] = {
+ {"__text_signature__", keyobject_text_signature, (setter)NULL},
+ {NULL}
+};
+
+static PyObject *
keyobject_call(keyobject *ko, PyObject *args, PyObject *kwds);
static PyObject *
@@ -560,6 +621,7 @@ static PyType_Slot keyobject_type_slots[] = {
{Py_tp_clear, keyobject_clear},
{Py_tp_richcompare, keyobject_richcompare},
{Py_tp_members, keyobject_members},
+ {Py_tp_getset, keyobject_getset},
{0, 0}
};
@@ -594,21 +656,15 @@ keyobject_call(keyobject *ko, PyObject *args, PyObject *kwds)
static PyObject *
keyobject_richcompare(PyObject *ko, PyObject *other, int op)
{
- PyObject *res;
- PyObject *x;
- PyObject *y;
- PyObject *compare;
- PyObject *answer;
- PyObject* stack[2];
-
if (!Py_IS_TYPE(other, Py_TYPE(ko))) {
PyErr_Format(PyExc_TypeError, "other argument must be K instance");
return NULL;
}
- compare = ((keyobject *) ko)->cmp;
+
+ PyObject *compare = ((keyobject *) ko)->cmp;
assert(compare != NULL);
- x = ((keyobject *) ko)->object;
- y = ((keyobject *) other)->object;
+ PyObject *x = ((keyobject *) ko)->object;
+ PyObject *y = ((keyobject *) other)->object;
if (!x || !y){
PyErr_Format(PyExc_AttributeError, "object");
return NULL;
@@ -617,14 +673,13 @@ keyobject_richcompare(PyObject *ko, PyObject *other, int op)
/* Call the user's comparison function and translate the 3-way
* result into true or false (or error).
*/
- stack[0] = x;
- stack[1] = y;
- res = _PyObject_FastCall(compare, stack, 2);
+ PyObject* args[2] = {x, y};
+ PyObject *res = PyObject_Vectorcall(compare, args, 2, NULL);
if (res == NULL) {
return NULL;
}
- answer = PyObject_RichCompare(res, _PyLong_GetZero(), op);
+ PyObject *answer = PyObject_RichCompare(res, _PyLong_GetZero(), op);
Py_DECREF(res);
return answer;
}
@@ -732,7 +787,7 @@ Fail:
}
PyDoc_STRVAR(functools_reduce_doc,
-"reduce(function, iterable[, initial]) -> value\n\
+"reduce(function, iterable[, initial], /) -> value\n\
\n\
Apply a function of two arguments cumulatively to the items of an iterable, from left to right.\n\
\n\
@@ -1096,19 +1151,9 @@ bounded_lru_cache_wrapper(lru_cache_object *self, PyObject *args, PyObject *kwds
The cache dict holds one reference to the link.
We created one other reference when the link was created.
The linked list only has borrowed references. */
- popresult = _PyDict_Pop_KnownHash(self->cache, link->key,
- link->hash, Py_None);
- if (popresult == Py_None) {
- /* Getting here means that the user function call or another
- thread has already removed the old key from the dictionary.
- This link is now an orphan. Since we don't want to leave the
- cache in an inconsistent state, we don't restore the link. */
- Py_DECREF(popresult);
- Py_DECREF(link);
- Py_DECREF(key);
- return result;
- }
- if (popresult == NULL) {
+ int res = _PyDict_Pop_KnownHash((PyDictObject*)self->cache, link->key,
+ link->hash, &popresult);
+ if (res < 0) {
/* An error arose while trying to remove the oldest key (the one
being evicted) from the cache. We restore the link to its
original position as the oldest link. Then we allow the
@@ -1119,10 +1164,22 @@ bounded_lru_cache_wrapper(lru_cache_object *self, PyObject *args, PyObject *kwds
Py_DECREF(result);
return NULL;
}
+ if (res == 0) {
+ /* Getting here means that the user function call or another
+ thread has already removed the old key from the dictionary.
+ This link is now an orphan. Since we don't want to leave the
+ cache in an inconsistent state, we don't restore the link. */
+ assert(popresult == NULL);
+ Py_DECREF(link);
+ Py_DECREF(key);
+ return result;
+ }
+
/* Keep a reference to the old key and old result to prevent their
ref counts from going to zero during the update. That will
prevent potentially arbitrary object clean-up code (i.e. __del__)
from running while we're still adjusting the links. */
+ assert(popresult != NULL);
oldkey = link->key;
oldresult = link->result;
@@ -1269,9 +1326,7 @@ lru_cache_dealloc(lru_cache_object *obj)
PyTypeObject *tp = Py_TYPE(obj);
/* bpo-31095: UnTrack is needed before calling any callbacks */
PyObject_GC_UnTrack(obj);
- if (obj->weakreflist != NULL) {
- PyObject_ClearWeakRefs((PyObject*)obj);
- }
+ FT_CLEAR_WEAKREFS((PyObject*)obj, obj->weakreflist);
(void)lru_cache_tp_clear(obj);
tp->tp_free(obj);
@@ -1281,7 +1336,11 @@ lru_cache_dealloc(lru_cache_object *obj)
static PyObject *
lru_cache_call(lru_cache_object *self, PyObject *args, PyObject *kwds)
{
- return self->wrapper(self, args, kwds);
+ PyObject *result;
+ Py_BEGIN_CRITICAL_SECTION(self);
+ result = self->wrapper(self, args, kwds);
+ Py_END_CRITICAL_SECTION();
+ return result;
}
static PyObject *
@@ -1294,6 +1353,7 @@ lru_cache_descr_get(PyObject *self, PyObject *obj, PyObject *type)
}
/*[clinic input]
+@critical_section
_functools._lru_cache_wrapper.cache_info
Report cache statistics
@@ -1301,7 +1361,7 @@ Report cache statistics
static PyObject *
_functools__lru_cache_wrapper_cache_info_impl(PyObject *self)
-/*[clinic end generated code: output=cc796a0b06dbd717 input=f05e5b6ebfe38645]*/
+/*[clinic end generated code: output=cc796a0b06dbd717 input=00e1acb31aa21ecc]*/
{
lru_cache_object *_self = (lru_cache_object *) self;
if (_self->maxsize == -1) {
@@ -1315,6 +1375,7 @@ _functools__lru_cache_wrapper_cache_info_impl(PyObject *self)
}
/*[clinic input]
+@critical_section
_functools._lru_cache_wrapper.cache_clear
Clear the cache and cache statistics
@@ -1322,7 +1383,7 @@ Clear the cache and cache statistics
static PyObject *
_functools__lru_cache_wrapper_cache_clear_impl(PyObject *self)
-/*[clinic end generated code: output=58423b35efc3e381 input=6ca59dba09b12584]*/
+/*[clinic end generated code: output=58423b35efc3e381 input=dfa33acbecf8b4b2]*/
{
lru_cache_object *_self = (lru_cache_object *) self;
lru_list_elem *list = lru_cache_unlink_list(_self);
@@ -1403,10 +1464,10 @@ static PyGetSetDef lru_cache_getsetlist[] = {
};
static PyMemberDef lru_cache_memberlist[] = {
- {"__dictoffset__", T_PYSSIZET,
- offsetof(lru_cache_object, dict), READONLY},
- {"__weaklistoffset__", T_PYSSIZET,
- offsetof(lru_cache_object, weakreflist), READONLY},
+ {"__dictoffset__", Py_T_PYSSIZET,
+ offsetof(lru_cache_object, dict), Py_READONLY},
+ {"__weaklistoffset__", Py_T_PYSSIZET,
+ offsetof(lru_cache_object, weakreflist), Py_READONLY},
{NULL} /* Sentinel */
};
@@ -1523,6 +1584,7 @@ _functools_free(void *module)
static struct PyModuleDef_Slot _functools_slots[] = {
{Py_mod_exec, _functools_exec},
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
+ {Py_mod_gil, Py_MOD_GIL_NOT_USED},
{0, NULL}
};