From d5f7bef2cfe4d1e439b3f83674127ee1a808d6e8 Mon Sep 17 00:00:00 2001 From: Johnston Jiaa Date: Fri, 10 Nov 2023 07:04:28 -0800 Subject: [PATCH] Extract cached properties Reviewed By: carljm Differential Revision: D51032460 fbshipit-source-id: 522424b3d515f3c1b6f1f16a53bd882e91011640 --- CachedProperties/cached_properties.c | 988 +++++++++++++++++++++ CachedProperties/cached_properties.h | 37 + CinderX/CachedProperties | 1 + CinderX/clinic/cached_properties.c.h | 90 ++ CinderX/known-core-python-exported-symbols | 6 - CinderX/setup.py | 7 +- Include/descrobject.h | 31 - Makefile.pre.in | 12 + Modules/_static.c | 2 + Modules/cinder.c | 2 + Objects/clinic/descrobject.c.h | 96 +- Objects/descrobject.c | 932 +------------------ Shadowcode/shadowcode.c | 1 + Shadowcode/shadowcode.h | 2 + StaticPython/classloader.c | 2 + configure | 1 + configure.ac | 1 + 17 files changed, 1147 insertions(+), 1064 deletions(-) create mode 100644 CachedProperties/cached_properties.c create mode 100644 CachedProperties/cached_properties.h create mode 120000 CinderX/CachedProperties create mode 100644 CinderX/clinic/cached_properties.c.h diff --git a/CachedProperties/cached_properties.c b/CachedProperties/cached_properties.c new file mode 100644 index 00000000000..eb4d0f9f06a --- /dev/null +++ b/CachedProperties/cached_properties.c @@ -0,0 +1,988 @@ +#include "Python.h" +#include "structmember.h" // PyMemberDef + +#include "cached_properties.h" + +/* fb t46346203 */ + +typedef struct { + PyObject_HEAD + PyObject *func; /* function object */ + PyObject *name; /* str or member descriptor object */ + PyObject *value; /* value or NULL when uninitialized */ +} PyCachedClassPropertyDescrObject; + +static int +cached_classproperty_traverse(PyCachedClassPropertyDescrObject *prop, visitproc visit, void *arg) { + Py_VISIT(prop->func); + Py_VISIT(prop->value); + return 0; +} + +static PyObject * +cached_classproperty_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + PyObject *func; + PyCachedClassPropertyDescrObject *descr; + + if (PyTuple_GET_SIZE(args) != 1) { + PyErr_SetString(PyExc_TypeError, "cached_classproperty: 1 argument expected"); + return NULL; + } + + func = PyTuple_GET_ITEM(args, 0); + + descr = (PyCachedClassPropertyDescrObject *)PyType_GenericAlloc(type, 0); + if (descr != NULL) { + PyObject *name; + if (PyFunction_Check(func)) { + name = ((PyFunctionObject*)func)->func_name; + } else { + name = PyObject_GetAttrString(func, "__name__"); + if (name == NULL) { + Py_DECREF(descr); + return NULL; + } + } + descr->func = func; + descr->name = name; + Py_INCREF(func); + Py_INCREF(name); + } + return (PyObject *)descr; +} + +static PyObject * +cached_classproperty_get(PyObject *self, PyObject *obj, PyObject *cls) +{ + PyCachedClassPropertyDescrObject *cp = (PyCachedClassPropertyDescrObject *)self; + PyObject *res; + + res = cp->value; + if (res == NULL) { + res = _PyObject_Vectorcall(cp->func, &cls, 1, NULL); + if (res == NULL) { + return NULL; + } + if (cp->value == NULL) { + /* we steal the ref count */ + cp->value = res; + } else { + /* first value to return wins */ + Py_DECREF(res); + res = cp->value; + } + } + + Py_INCREF(res); + return res; +} + +static void +cached_classproperty_dealloc(PyCachedClassPropertyDescrObject *cp) +{ + PyObject_GC_UnTrack(cp); + Py_XDECREF(cp->func); + Py_XDECREF(cp->name); + Py_XDECREF(cp->value); + PyTypeObject *type = Py_TYPE(cp); + Py_TYPE(cp)->tp_free(cp); + Py_DECREF(type); +} + +static PyObject * +cached_classproperty_get___doc__(PyCachedClassPropertyDescrObject *cp, void *closure) +{ + PyObject *res = ((PyFunctionObject*)cp->func)->func_doc; + Py_INCREF(res); + return res; +} + +static PyObject * +cached_classproperty_get_name(PyCachedClassPropertyDescrObject *cp, void *closure) +{ + PyObject *res = cp->name; + Py_INCREF(res); + return res; +} + +static PyGetSetDef cached_classproperty_getsetlist[] = { + {"__doc__", (getter)cached_classproperty_get___doc__, NULL, NULL, NULL}, + {"name", (getter)cached_classproperty_get_name, NULL, NULL, NULL}, + {"__name__", (getter)cached_classproperty_get_name, NULL, NULL, NULL}, + {NULL} /* Sentinel */ +}; + +static PyMemberDef cached_classproperty_members[] = { + {"func", T_OBJECT, offsetof(PyCachedClassPropertyDescrObject, func), READONLY}, + {0} +}; + +static PyType_Slot PyCachedClassProperty_slots[] = { + {Py_tp_dealloc, cached_classproperty_dealloc}, + {Py_tp_traverse, cached_classproperty_traverse}, + {Py_tp_descr_get, cached_classproperty_get}, + {Py_tp_members, cached_classproperty_members}, + {Py_tp_getset, cached_classproperty_getsetlist}, + {Py_tp_new, cached_classproperty_new}, + {Py_tp_alloc, PyType_GenericAlloc}, + {Py_tp_free, PyObject_GC_Del}, + {0, 0}, +}; + +PyType_Spec _PyCachedClassProperty_TypeSpec = { + "builtins.cached_classproperty", + sizeof(PyCachedClassPropertyDescrObject), + 0, + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, + PyCachedClassProperty_slots +}; + +/* end fb t46346203 */ + +/* fb t46346203 */ + +static int +cached_property_traverse(PyCachedPropertyDescrObject *prop, visitproc visit, void *arg) +{ + Py_VISIT(prop->func); + Py_VISIT(prop->name_or_descr); + return 0; +} + + +PyTypeObject PyCachedProperty_Type; +PyTypeObject PyCachedPropertyWithDescr_Type; + +static int +cached_property_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + PyCachedPropertyDescrObject *cp = (PyCachedPropertyDescrObject *)self; + PyObject *name_or_descr, *func; + + if (PyTuple_GET_SIZE(args) != 1 && PyTuple_GET_SIZE(args) != 2) { + PyErr_SetString(PyExc_TypeError, "cached_property: 1 or 2 arguments expected"); + return -1; + } + + func = PyTuple_GET_ITEM(args, 0); + + if (PyTuple_GET_SIZE(args) == 2) { + PyMemberDescrObject *descr; + name_or_descr = PyTuple_GET_ITEM(args, 1); + + if (Py_TYPE(name_or_descr) != &PyMemberDescr_Type) { + PyErr_SetString(PyExc_TypeError, "cached_property: member descriptor expected for 2nd argument"); + return -1; + } + + descr = (PyMemberDescrObject *)name_or_descr; + if(descr->d_member->type != T_OBJECT_EX || + descr->d_member->flags) { + PyErr_SetString(PyExc_TypeError, "cached_property: incompatible descriptor"); + return -1; + } + + /* change our type to enable setting the cached property, we don't allow + * subtypes because we can't change their type, and the descriptor would + * need to account for doing the lookup, and we'd need to dynamically + * create a subtype of them too, not to mention dealing with extra ref + * counting on the types */ + if (Py_TYPE(self) != &PyCachedProperty_Type && + Py_TYPE(self) != &PyCachedPropertyWithDescr_Type) { + PyErr_SetString( + PyExc_TypeError, + "cached_property: descr cannot be used with subtypes of cached_property"); + return -1; + } + + Py_TYPE(self) = &PyCachedPropertyWithDescr_Type; + } else { + name_or_descr = Py_None; + } + + cp->func = func; + cp->name_or_descr = name_or_descr; + Py_INCREF(func); + Py_INCREF(name_or_descr); + + return 0; +} + +PyDoc_STRVAR(cached_property_doc, +"cached_property(function, [slot]) --> cached_property object\n\ +\n\ +Creates a new cached property where function will be called to produce\n\ +the value on the first access.\n\ +\n\ +If slot descriptor is provided it will be used for storing the value."); + + +static PyObject * +cached_property_get(PyObject *self, PyObject *obj, PyObject *cls) +{ + PyObject *res, *dict; + PyCachedPropertyDescrObject *cp = (PyCachedPropertyDescrObject *)self; + PyObject *stack[1] = {obj}; + + if (obj == NULL) { + Py_INCREF(self); + return self; + } + + if (Py_TYPE(cp->name_or_descr) == &PyMemberDescr_Type) { + PyObject **addr; + PyMemberDescrObject *descr = (PyMemberDescrObject *)cp->name_or_descr; + + if (Py_TYPE(obj) != PyDescr_TYPE(descr) && + !PyObject_TypeCheck(obj, PyDescr_TYPE(descr))) { + PyErr_Format(PyExc_TypeError, + "descriptor '%V' for '%s' objects " + "doesn't apply to '%s' object", + ((PyDescrObject*)descr)->d_name, "?", + PyDescr_TYPE(descr)->tp_name, + Py_TYPE(obj)->tp_name); + return NULL; + } + + addr = (PyObject **)(((const char *)obj) + descr->d_member->offset); + res = *addr; + if (res != NULL) { + Py_INCREF(res); + return res; + } + + res = _PyObject_Vectorcall(cp->func, stack, 1, NULL); + if (res == NULL) { + return NULL; + } + + *addr = res; + Py_INCREF(res); + } else { + dict = PyObject_GenericGetDict(obj, NULL); + if (dict == NULL) { + if (PyErr_ExceptionMatches(PyExc_AttributeError)) { + PyErr_Clear(); + PyErr_Format( + PyExc_TypeError, + "No '__dict__' attribute on '%s' instance to cache '%U' property.", + Py_TYPE(obj)->tp_name, cp->name_or_descr + ); + } + return NULL; + } + + res = PyDict_GetItem(dict, cp->name_or_descr); + Py_DECREF(dict); + if (res != NULL) { + Py_INCREF(res); /* we got a borrowed ref */ + return res; + } + + res = _PyObject_Vectorcall(cp->func, stack, 1, NULL); + if (res == NULL) { + return NULL; + } + + if (_PyObjectDict_SetItem(Py_TYPE(obj), _PyObject_GetDictPtr(obj), cp->name_or_descr, res)) { + Py_DECREF(res); + return NULL; + } + } + + return res; +} + +static int +cached_property_set(PyObject *self, PyObject *obj, PyObject *value) +{ + PyCachedPropertyDescrObject *cp = (PyCachedPropertyDescrObject *)self; + PyObject **dictptr; + + if (Py_TYPE(cp->name_or_descr) == &PyMemberDescr_Type) { + return Py_TYPE(cp->name_or_descr)->tp_descr_set(cp->name_or_descr, obj, value); + } + + dictptr = _PyObject_GetDictPtr(obj); + + if (dictptr == NULL) { + PyErr_SetString(PyExc_AttributeError, + "This object has no __dict__"); + return -1; + } + + return _PyObjectDict_SetItem(Py_TYPE(obj), dictptr, cp->name_or_descr, value); +} + +static void +cached_property_dealloc(PyCachedPropertyDescrObject *cp) +{ + PyObject_GC_UnTrack(cp); + Py_XDECREF(cp->func); + Py_XDECREF(cp->name_or_descr); + Py_TYPE(cp)->tp_free(cp); +} + +static PyObject * +cached_property_get___doc__(PyCachedPropertyDescrObject *cp, void *closure) +{ + PyObject *res = ((PyFunctionObject*)cp->func)->func_doc; + Py_INCREF(res); + return res; +} + +static PyObject * +cached_property_get_name(PyCachedPropertyDescrObject *cp, void *closure) +{ + PyObject *res; + + if (Py_TYPE(cp->name_or_descr) != &PyMemberDescr_Type) { + res = cp->name_or_descr; + } else { + res = PyDescr_NAME(cp->name_or_descr); + } + Py_INCREF(res); + return res; +} + +static PyObject * +cached_property_get_slot(PyCachedPropertyDescrObject *cp, void *closure) +{ + if (Py_TYPE(cp->name_or_descr) == &PyMemberDescr_Type) { + PyObject *res = cp->name_or_descr; + + Py_INCREF(res); + return res; + } + Py_RETURN_NONE; +} + +static PyObject * +cached_property_clear(PyCachedPropertyDescrObject *self, PyObject *obj) +{ + PyCachedPropertyDescrObject *cp = (PyCachedPropertyDescrObject *)self; + PyObject **dictptr; + + if (Py_TYPE(cp->name_or_descr) == &PyMemberDescr_Type) { + if (Py_TYPE(cp->name_or_descr)->tp_descr_set(cp->name_or_descr, obj, NULL) < 0) { + if (PyErr_ExceptionMatches(PyExc_AttributeError)) { + PyErr_Clear(); + Py_RETURN_NONE; + } + return NULL; + } + Py_RETURN_NONE; + } + + dictptr = _PyObject_GetDictPtr(obj); + + if (dictptr == NULL) { + PyErr_SetString(PyExc_AttributeError, + "This object has no __dict__"); + return NULL; + } + + if (_PyObjectDict_SetItem(Py_TYPE(obj), dictptr, cp->name_or_descr, NULL) < 0) { + if (PyErr_ExceptionMatches(PyExc_KeyError)) { + PyErr_Clear(); + Py_RETURN_NONE; + } + return NULL; + } + Py_RETURN_NONE; +} + +static PyObject * +cached_property_has_value(PyCachedPropertyDescrObject *self, PyObject *obj) +{ + PyCachedPropertyDescrObject *cp = (PyCachedPropertyDescrObject *)self; + PyObject **dictptr; + + if (Py_TYPE(cp->name_or_descr) == &PyMemberDescr_Type) { + PyObject *value = Py_TYPE(cp->name_or_descr)->tp_descr_get( + cp->name_or_descr, obj, (PyObject *)Py_TYPE(obj)); + + if (value == NULL) { + if (PyErr_ExceptionMatches(PyExc_AttributeError)) { + PyErr_Clear(); + Py_RETURN_FALSE; + } + return NULL; + } + Py_DECREF(value); + Py_RETURN_TRUE; + } + + dictptr = _PyObject_GetDictPtr(obj); + + if (dictptr == NULL) { + PyErr_SetString(PyExc_AttributeError, + "This object has no __dict__"); + return NULL; + } + + if (*dictptr == NULL) { + Py_RETURN_FALSE; + } + + PyObject *value = PyDict_GetItem(*dictptr, cp->name_or_descr); + if (value == NULL) { + Py_RETURN_FALSE; + } + Py_RETURN_TRUE; +} + +static PyObject * +cached_property___set_name__(PyObject* self, PyObject* const* args, Py_ssize_t nargs) +{ + if (nargs != 2) { + PyErr_SetString(PyExc_TypeError, "cached_property.__set_name__: 2 arguments expected"); + return NULL; + } + PyCachedPropertyDescrObject *cp = (PyCachedPropertyDescrObject *)self; + PyObject *Py_UNUSED(owner) = args[0]; + PyObject *name = args[1]; + + // Perform error checks if the name wasn't initialized (i.e, not None) + if (cp->name_or_descr != Py_None) { + if (PyUnicode_CheckExact(cp->name_or_descr)) { + // Check for naming conflicts + if (PyUnicode_Compare(cp->name_or_descr, name) != 0) { + if (!PyErr_Occurred()) { + // Avoid masking existing errors + PyErr_Format( + PyExc_TypeError, + "Cannot assign the same cached_property to two different names (%R and %R).", + cp->name_or_descr, name + ); + } + return NULL; + } + } else { + // This cannot normally happen in managed code, unless someone manually calls `__set_name__` + // after a slot-backed property was defined (see test_cached_property_set_name_on_slot_backed_property) + PyErr_Format( + PyExc_RuntimeError, + "Cannot set name (%R) for a cached property backed by a slot (%R)", + name, cp + ); + return NULL; + } + } + + Py_SETREF(cp->name_or_descr, name); + Py_INCREF(name); + Py_RETURN_NONE; +} + + +static PyGetSetDef cached_property_getsetlist[] = { + {"__doc__", (getter)cached_property_get___doc__, NULL, NULL, NULL}, + {"__name__", (getter)cached_property_get_name, NULL, NULL, NULL}, + {"name", (getter)cached_property_get_name, NULL, NULL, NULL}, + {"slot", (getter)cached_property_get_slot, NULL, NULL, NULL}, + {NULL} /* Sentinel */ +}; + +static PyMemberDef cached_property_members[] = { + {"func", T_OBJECT, offsetof(PyCachedPropertyDescrObject, func), READONLY}, + /* currently duplicated until all consumers are updated in favor of fget */ + {"fget", T_OBJECT, offsetof(PyCachedPropertyDescrObject, func), READONLY}, + {0} +}; + +static PyMethodDef cached_property_methods[] = { + {"clear", (PyCFunction)cached_property_clear, METH_O, NULL}, + {"has_value", (PyCFunction)cached_property_has_value, METH_O, NULL}, + {"__set_name__", (PyCFunction)cached_property___set_name__, METH_FASTCALL, NULL}, + {NULL, NULL} +}; + +PyTypeObject PyCachedProperty_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + .tp_name = "cached_property", + .tp_basicsize = sizeof(PyCachedPropertyDescrObject), + .tp_dealloc = (destructor)cached_property_dealloc, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_BASETYPE, + .tp_doc = cached_property_doc, + .tp_traverse = (traverseproc)cached_property_traverse, + .tp_descr_get = cached_property_get, + .tp_members = cached_property_members, + .tp_getset = cached_property_getsetlist, + .tp_new = PyType_GenericNew, + .tp_init = cached_property_init, + .tp_alloc = PyType_GenericAlloc, + .tp_free = PyObject_GC_Del, + .tp_methods = cached_property_methods, +}; + +PyTypeObject PyCachedPropertyWithDescr_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + .tp_name = "cached_property_with_descr", + .tp_base = &PyCachedProperty_Type, + .tp_basicsize = sizeof(PyCachedPropertyDescrObject), + .tp_dealloc = (destructor)cached_property_dealloc, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_BASETYPE, + .tp_doc = cached_property_doc, + .tp_traverse = (traverseproc)cached_property_traverse, + .tp_descr_get = cached_property_get, + .tp_descr_set = cached_property_set, + .tp_members = cached_property_members, + .tp_getset = cached_property_getsetlist, + .tp_new = PyType_GenericNew, + .tp_init = cached_property_init, + .tp_alloc = PyType_GenericAlloc, + .tp_free = PyObject_GC_Del, +}; + +/* end fb t46346203 */ + +/* fb T82701047 */ +/*********************** AsyncCachedProperty*********************/ +static PyObject *_AsyncLazyValue_Type; + +static int +async_cached_property_traverse(PyAsyncCachedPropertyDescrObject *prop, visitproc visit, void *arg) +{ + Py_VISIT(prop->func); + Py_VISIT(prop->name_or_descr); + return 0; +} + +static int +async_cached_property_init_impl(PyAsyncCachedPropertyDescrObject *self, + PyObject *func, PyObject *name_or_descr) +/*[clinic end generated code: output=d8f17f423e7ad7f2 input=5bfa6f9a771d138d]*/ +{ + if (name_or_descr != NULL) { + PyMemberDescrObject *descr = (PyMemberDescrObject *)name_or_descr; + if(descr->d_member->type != T_OBJECT_EX || + descr->d_member->flags) { + PyErr_SetString(PyExc_TypeError, "async_cached_property: incompatible descriptor"); + return -1; + } + + /* change our type to enable setting the cached property, we don't allow + * subtypes because we can't change their type, and the descriptor would + * need to account for doing the lookup, and we'd need to dynamically + * create a subtype of them too, not to mention dealing with extra ref + * counting on the types */ + if (Py_TYPE(self) != &PyAsyncCachedProperty_Type && + Py_TYPE(self) != &PyAsyncCachedPropertyWithDescr_Type) { + PyErr_SetString( + PyExc_TypeError, + "async_cached_property: descr cannot be used with subtypes of async_cached_property"); + return -1; + } + + Py_TYPE(self) = &PyAsyncCachedPropertyWithDescr_Type; + self->name_or_descr = name_or_descr; + } else if (PyFunction_Check(func)) { + self->name_or_descr = ((PyFunctionObject *)func)->func_name; + } else { + self->name_or_descr = PyObject_GetAttrString(func, "__name__"); + if (self->name_or_descr == NULL) { + return -1; + } + } + + self->func = func; + Py_INCREF(self->func); + Py_INCREF(self->name_or_descr); + + return 0; +} + +static inline int import_async_lazy_value() { + if (_AsyncLazyValue_Type == NULL) { + _Py_IDENTIFIER(AsyncLazyValue); + PyObject *asyncio = PyImport_ImportModule("_asyncio"); + if (asyncio == NULL) { + return -1; + } + _AsyncLazyValue_Type = _PyObject_GetAttrId(asyncio, &PyId_AsyncLazyValue); + Py_DECREF(asyncio); + if (_AsyncLazyValue_Type == NULL) { + return -1; + } + } + return 0; +} + +static PyObject * +async_cached_property_get(PyObject *self, PyObject *obj, PyObject *cls) +{ + PyObject *res; + PyAsyncCachedPropertyDescrObject *cp = (PyAsyncCachedPropertyDescrObject *)self; + + if (obj == NULL) { + Py_INCREF(self); + return self; + } + + if (Py_TYPE(cp->name_or_descr) == &PyMemberDescr_Type) { + PyObject **addr; + PyMemberDescrObject *descr = (PyMemberDescrObject *)cp->name_or_descr; + + if (Py_TYPE(obj) != PyDescr_TYPE(descr) && + !PyObject_TypeCheck(obj, PyDescr_TYPE(descr))) { + PyErr_Format(PyExc_TypeError, + "descriptor '%V' for '%s' objects " + "doesn't apply to '%s' object", + ((PyDescrObject*)descr)->d_name, "?", + PyDescr_TYPE(descr)->tp_name, + Py_TYPE(obj)->tp_name); + return NULL; + } + + addr = (PyObject **)(((const char *)obj) + descr->d_member->offset); + res = *addr; + if (res != NULL) { + Py_INCREF(res); + return res; + } + if (import_async_lazy_value() < 0) { + return NULL; + } + res = PyObject_CallFunctionObjArgs(_AsyncLazyValue_Type, cp->func, obj, NULL); + if (res == NULL) { + return NULL; + } + + *addr = res; + Py_INCREF(res); + } else { + if (import_async_lazy_value() < 0) { + return NULL; + } + + res = PyObject_CallFunctionObjArgs(_AsyncLazyValue_Type, cp->func, obj, NULL); + if (res == NULL) { + return NULL; + } + + if (PyObject_SetAttr(obj, cp->name_or_descr, res) < 0) { + Py_DECREF(res); + return NULL; + } + } + + return res; +} + +static void +async_cached_property_dealloc(PyAsyncCachedPropertyDescrObject *cp) +{ + PyObject_GC_UnTrack(cp); + Py_XDECREF(cp->func); + Py_XDECREF(cp->name_or_descr); + Py_TYPE(cp)->tp_free(cp); +} + +static PyObject * +async_cached_property_get___doc__(PyAsyncCachedPropertyDescrObject *cp, void *closure) +{ + PyObject *res = ((PyFunctionObject*)cp->func)->func_doc; + Py_INCREF(res); + return res; +} + +static PyObject * +async_cached_property_get_name(PyAsyncCachedPropertyDescrObject *cp, void *closure) +{ + PyObject *res; + + if (Py_TYPE(cp->name_or_descr) != &PyMemberDescr_Type) { + res = cp->name_or_descr; + } else { + res = PyDescr_NAME(cp->name_or_descr); + } + Py_INCREF(res); + return res; +} + +static PyObject * +async_cached_property_get_slot(PyAsyncCachedPropertyDescrObject *cp, void *closure) +{ + if (Py_TYPE(cp->name_or_descr) == &PyMemberDescr_Type) { + PyObject *res = cp->name_or_descr; + + Py_INCREF(res); + return res; + } + Py_RETURN_NONE; +} + +static int +async_cached_property_set(PyObject *self, PyObject *obj, PyObject *value) +{ + PyAsyncCachedPropertyDescrObject *cp = (PyAsyncCachedPropertyDescrObject *)self; + PyObject **dictptr; + + if (Py_TYPE(cp->name_or_descr) == &PyMemberDescr_Type) { + return Py_TYPE(cp->name_or_descr)->tp_descr_set(cp->name_or_descr, obj, value); + } + + dictptr = _PyObject_GetDictPtr(obj); + + if (dictptr == NULL) { + PyErr_SetString(PyExc_AttributeError, + "This object has no __dict__"); + return -1; + } + + return _PyObjectDict_SetItem(Py_TYPE(obj), dictptr, cp->name_or_descr, value); +} + + +static PyGetSetDef async_cached_property_getsetlist[] = { + {"__doc__", (getter)async_cached_property_get___doc__, NULL, NULL, NULL}, + {"name", (getter)async_cached_property_get_name, NULL, NULL, NULL}, + {"slot", (getter)async_cached_property_get_slot, NULL, NULL, NULL}, + {NULL} /* Sentinel */ +}; + +static PyMemberDef async_cached_property_members[] = { + {"func", T_OBJECT, offsetof(PyAsyncCachedPropertyDescrObject, func), READONLY}, + {"fget", T_OBJECT, offsetof(PyAsyncCachedPropertyDescrObject, func), READONLY}, + {0} +}; + +// This was originally clinic-generated from Objects/descrobject.c +// +// Copied over wholesale for speed of implementation to unblock CinderX, but a +// handwritten implementation would be cleaner. +/* start clinic-generated code */ +PyDoc_STRVAR(async_cached_property_init__doc__, +"async_cached_property(func, name_or_descr=None)\n" +"--\n" +"\n" +"init a async_cached_property.\n" +"\n" +"Creates a new async cached property where function will be called to produce\n" +"the async lazy value on the first access.\n" +"\n" +"If slot descriptor is provided it will be used for storing the value.\""); + +static int +async_cached_property_init(PyObject *self, PyObject *args, PyObject *kwargs) +{ + int return_value = -1; + static const char * const _keywords[] = {"func", "name_or_descr", NULL}; + static _PyArg_Parser _parser = {NULL, _keywords, "async_cached_property", 0}; + PyObject *argsbuf[2]; + PyObject * const *fastargs; + Py_ssize_t nargs = PyTuple_GET_SIZE(args); + Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 1; + PyObject *func; + PyObject *name_or_descr = NULL; + + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 2, 0, argsbuf); + if (!fastargs) { + goto exit; + } + func = fastargs[0]; + if (!noptargs) { + goto skip_optional_pos; + } + if (!PyObject_TypeCheck(fastargs[1], &PyMemberDescr_Type)) { + _PyArg_BadArgument("async_cached_property", "argument 'name_or_descr'", (&PyMemberDescr_Type)->tp_name, fastargs[1]); + goto exit; + } + name_or_descr = fastargs[1]; +skip_optional_pos: + return_value = async_cached_property_init_impl((PyAsyncCachedPropertyDescrObject *)self, func, name_or_descr); + +exit: + return return_value; +} + +PyDoc_STRVAR(async_cached_classproperty_new__doc__, +"async_cached_classproperty(func)\n" +"--\n" +"\n" +"Provides an async cached class property.\n" +"\n" +"Works with normal types and frozen types to create values on demand\n" +"and cache them in the class."); + +static PyObject * +async_cached_classproperty_new_impl(PyTypeObject *type, PyObject *func); + +static PyObject * +async_cached_classproperty_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + PyObject *return_value = NULL; + static const char * const _keywords[] = {"func", NULL}; + static _PyArg_Parser _parser = {NULL, _keywords, "async_cached_classproperty", 0}; + PyObject *argsbuf[1]; + PyObject * const *fastargs; + Py_ssize_t nargs = PyTuple_GET_SIZE(args); + PyObject *func; + + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 1, 0, argsbuf); + if (!fastargs) { + goto exit; + } + if (!PyObject_TypeCheck(fastargs[0], &PyFunction_Type)) { + _PyArg_BadArgument("async_cached_classproperty", "argument 'func'", (&PyFunction_Type)->tp_name, fastargs[0]); + goto exit; + } + func = fastargs[0]; + return_value = async_cached_classproperty_new_impl(type, func); + +exit: + return return_value; +} +/* end clinic-generated code */ + +PyTypeObject PyAsyncCachedProperty_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + .tp_name = "async_cached_property", + .tp_basicsize = sizeof(PyAsyncCachedPropertyDescrObject), + .tp_dealloc = (destructor)async_cached_property_dealloc, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_BASETYPE, + .tp_doc = async_cached_property_init__doc__, + .tp_traverse = (traverseproc)async_cached_property_traverse, + .tp_descr_get = async_cached_property_get, + .tp_members = async_cached_property_members, + .tp_getset = async_cached_property_getsetlist, + .tp_new = PyType_GenericNew, + .tp_init = async_cached_property_init, + .tp_alloc = PyType_GenericAlloc, + .tp_free = PyObject_GC_Del, +}; + +PyTypeObject PyAsyncCachedPropertyWithDescr_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + .tp_name = "async_cached_property_with_descr", + .tp_basicsize = sizeof(PyAsyncCachedPropertyDescrObject), + .tp_dealloc = (destructor)async_cached_property_dealloc, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_BASETYPE, + .tp_doc = async_cached_property_init__doc__, + .tp_traverse = (traverseproc)async_cached_property_traverse, + .tp_descr_get = async_cached_property_get, + .tp_descr_set = async_cached_property_set, + .tp_members = async_cached_property_members, + .tp_getset = async_cached_property_getsetlist, + .tp_new = PyType_GenericNew, + .tp_init = async_cached_property_init, + .tp_alloc = PyType_GenericAlloc, + .tp_free = PyObject_GC_Del, +}; + + +/* end fb T82701047 */ +/* fb T82701047 */ +/*********************** AsyncCachedClassProperty*********************/ +static int +async_cached_classproperty_traverse(PyAsyncCachedClassPropertyDescrObject *prop, visitproc visit, void *arg) { + Py_VISIT(prop->func); + Py_VISIT(prop->value); + return 0; +} + +static PyObject * +async_cached_classproperty_new_impl(PyTypeObject *type, PyObject *func) +/*[clinic end generated code: output=b7972e5345764116 input=056050fde0415935]*/ +{ + PyAsyncCachedClassPropertyDescrObject *descr; + descr = (PyAsyncCachedClassPropertyDescrObject *)PyType_GenericAlloc(type, 0); + if (descr != NULL) { + PyObject *name = ((PyFunctionObject*)func)->func_name; + descr->func = func; + descr->name = name; + Py_INCREF(func); + Py_INCREF(name); + } + return (PyObject *)descr; +} + +static PyObject * +async_cached_classproperty_get(PyObject *self, PyObject *obj, PyObject *cls) +{ + PyAsyncCachedClassPropertyDescrObject *cp = (PyAsyncCachedClassPropertyDescrObject *)self; + PyObject *res; + + res = cp->value; + if (res == NULL) { + if (import_async_lazy_value() < 0) { + return NULL; + } + res = PyObject_CallFunctionObjArgs(_AsyncLazyValue_Type, cp->func, cls, NULL); + if (res == NULL) { + return NULL; + } + if (cp->value == NULL) { + /* we steal the ref count */ + cp->value = res; + } else { + /* first value to return wins */ + Py_DECREF(res); + res = cp->value; + } + } + + Py_INCREF(res); + return res; +} + +static void +async_cached_classproperty_dealloc(PyAsyncCachedClassPropertyDescrObject *cp) +{ + PyObject_GC_UnTrack(cp); + Py_XDECREF(cp->func); + Py_XDECREF(cp->name); + Py_XDECREF(cp->value); + Py_TYPE(cp)->tp_free(cp); +} + +static PyObject * +async_cached_classproperty_get___doc__(PyAsyncCachedClassPropertyDescrObject *cp, void *closure) +{ + PyObject *res = ((PyFunctionObject*)cp->func)->func_doc; + Py_INCREF(res); + return res; +} + +static PyObject * +async_cached_classproperty_get_name(PyAsyncCachedClassPropertyDescrObject *cp, void *closure) +{ + PyObject *res = cp->name; + Py_INCREF(res); + return res; +} + +static PyGetSetDef async_cached_classproperty_getsetlist[] = { + {"__doc__", (getter)async_cached_classproperty_get___doc__, NULL, NULL, NULL}, + {"name", (getter)async_cached_classproperty_get_name, NULL, NULL, NULL}, + {NULL} /* Sentinel */ +}; + +static PyMemberDef async_cached_classproperty_members[] = { + {"func", T_OBJECT, offsetof(PyAsyncCachedClassPropertyDescrObject, func), READONLY}, + {0} +}; + +PyTypeObject PyAsyncCachedClassProperty_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + .tp_name = "async_cached_classproperty", + .tp_basicsize = sizeof(PyAsyncCachedClassPropertyDescrObject), + .tp_dealloc = (destructor)async_cached_classproperty_dealloc, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, + .tp_doc = async_cached_classproperty_new__doc__, + .tp_traverse = (traverseproc)async_cached_classproperty_traverse, + .tp_descr_get = async_cached_classproperty_get, + .tp_members = async_cached_classproperty_members, + .tp_getset = async_cached_classproperty_getsetlist, + .tp_new = async_cached_classproperty_new, + .tp_alloc = PyType_GenericAlloc, + .tp_free = PyObject_GC_Del, +}; + +/* end fb T82701047 */ diff --git a/CachedProperties/cached_properties.h b/CachedProperties/cached_properties.h new file mode 100644 index 00000000000..954022f943a --- /dev/null +++ b/CachedProperties/cached_properties.h @@ -0,0 +1,37 @@ +#ifndef Py_CACHED_PROPERTIES_H +#define Py_CACHED_PROPERTIES_H + +#include "Python.h" + +/* fb t46346203 */ +typedef struct { + PyObject_HEAD + PyObject *func; /* function object */ + PyObject *name_or_descr; /* str or member descriptor object */ +} PyCachedPropertyDescrObject; +/* end fb t46346203 */ + + /* fb T82701047 */ +typedef struct { + PyObject_HEAD + PyObject *func; /* function object */ + PyObject *name_or_descr; /* str or member descriptor object */ +} PyAsyncCachedPropertyDescrObject; + /* end fb T82701047 */ + +/* fb T82701047 */ +typedef struct { + PyObject_HEAD + PyObject *func; /* function object */ + PyObject *name; /* str or member descriptor object */ + PyObject *value; /* value or NULL when uninitialized */ +} PyAsyncCachedClassPropertyDescrObject; +/* end fb T82701047 */ + +CiAPI_DATA(PyTypeObject) PyAsyncCachedPropertyWithDescr_Type; +CiAPI_DATA(PyType_Spec) _PyCachedClassProperty_TypeSpec; /* fb t46346203 */ +CiAPI_DATA(PyTypeObject) PyCachedProperty_Type; /* fb T46346203 */ +CiAPI_DATA(PyTypeObject) PyCachedPropertyWithDescr_Type; /* fb T46346203 */ +CiAPI_DATA(PyTypeObject) PyAsyncCachedProperty_Type; /* fb T82701047 */ +CiAPI_DATA(PyTypeObject) PyAsyncCachedClassProperty_Type; /* fb T82701047 */ +#endif /* !Py_CACHED_PROPERTIES_H */ diff --git a/CinderX/CachedProperties b/CinderX/CachedProperties new file mode 120000 index 00000000000..98a7d9bba55 --- /dev/null +++ b/CinderX/CachedProperties @@ -0,0 +1 @@ +../CachedProperties \ No newline at end of file diff --git a/CinderX/clinic/cached_properties.c.h b/CinderX/clinic/cached_properties.c.h new file mode 100644 index 00000000000..59ccc19934d --- /dev/null +++ b/CinderX/clinic/cached_properties.c.h @@ -0,0 +1,90 @@ +/*[clinic input] +preserve +[clinic start generated code]*/ + +PyDoc_STRVAR(async_cached_property_init__doc__, +"async_cached_property(func, name_or_descr=None)\n" +"--\n" +"\n" +"init a async_cached_property.\n" +"\n" +"Creates a new async cached property where function will be called to produce\n" +"the async lazy value on the first access.\n" +"\n" +"If slot descriptor is provided it will be used for storing the value.\""); + +static int +async_cached_property_init_impl(PyAsyncCachedPropertyDescrObject *self, + PyObject *func, PyObject *name_or_descr); + +static int +async_cached_property_init(PyObject *self, PyObject *args, PyObject *kwargs) +{ + int return_value = -1; + static const char * const _keywords[] = {"func", "name_or_descr", NULL}; + static _PyArg_Parser _parser = {NULL, _keywords, "async_cached_property", 0}; + PyObject *argsbuf[2]; + PyObject * const *fastargs; + Py_ssize_t nargs = PyTuple_GET_SIZE(args); + Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 1; + PyObject *func; + PyObject *name_or_descr = NULL; + + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 2, 0, argsbuf); + if (!fastargs) { + goto exit; + } + func = fastargs[0]; + if (!noptargs) { + goto skip_optional_pos; + } + if (!PyObject_TypeCheck(fastargs[1], &PyMemberDescr_Type)) { + _PyArg_BadArgument("async_cached_property", "argument 'name_or_descr'", (&PyMemberDescr_Type)->tp_name, fastargs[1]); + goto exit; + } + name_or_descr = fastargs[1]; +skip_optional_pos: + return_value = async_cached_property_init_impl((PyAsyncCachedPropertyDescrObject *)self, func, name_or_descr); + +exit: + return return_value; +} + +PyDoc_STRVAR(async_cached_classproperty_new__doc__, +"async_cached_classproperty(func)\n" +"--\n" +"\n" +"Provides an async cached class property.\n" +"\n" +"Works with normal types and frozen types to create values on demand\n" +"and cache them in the class."); + +static PyObject * +async_cached_classproperty_new_impl(PyTypeObject *type, PyObject *func); + +static PyObject * +async_cached_classproperty_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) +{ + PyObject *return_value = NULL; + static const char * const _keywords[] = {"func", NULL}; + static _PyArg_Parser _parser = {NULL, _keywords, "async_cached_classproperty", 0}; + PyObject *argsbuf[1]; + PyObject * const *fastargs; + Py_ssize_t nargs = PyTuple_GET_SIZE(args); + PyObject *func; + + fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 1, 0, argsbuf); + if (!fastargs) { + goto exit; + } + if (!PyObject_TypeCheck(fastargs[0], &PyFunction_Type)) { + _PyArg_BadArgument("async_cached_classproperty", "argument 'func'", (&PyFunction_Type)->tp_name, fastargs[0]); + goto exit; + } + func = fastargs[0]; + return_value = async_cached_classproperty_new_impl(type, func); + +exit: + return return_value; +} +/*[clinic end generated code: output=aaf271c9aed7e9ec input=a9049054013a1b77]*/ diff --git a/CinderX/known-core-python-exported-symbols b/CinderX/known-core-python-exported-symbols index 9fc41b2c9f1..986c1dcd8b9 100644 --- a/CinderX/known-core-python-exported-symbols +++ b/CinderX/known-core-python-exported-symbols @@ -289,9 +289,6 @@ _PyAST_With _PyAST_withitem _PyAST_Yield _PyAST_YieldFrom -PyAsyncCachedClassProperty_Type -PyAsyncCachedProperty_Type -PyAsyncCachedPropertyWithDescr_Type _PyAsyncGenASend_Type _PyAsyncGenAThrow_Type PyAsyncGen_New @@ -350,9 +347,6 @@ _PyBytesWriter_Prepare _PyBytesWriter_Resize _PyBytesWriter_WriteBytes _Py_c_abs -_PyCachedClassProperty_TypeSpec -PyCachedProperty_Type -PyCachedPropertyWithDescr_Type PyCallable_Check PyCallIter_New PyCallIter_Type diff --git a/CinderX/setup.py b/CinderX/setup.py index 41ad39b35a1..e1ff1d8f4d7 100644 --- a/CinderX/setup.py +++ b/CinderX/setup.py @@ -235,6 +235,10 @@ "StaticPython/methodobject_vectorcall.c", ] +CACHEDPROPS_SRCS = [ + "CachedProperties/cached_properties.c", +] + ALL_SRCS = ( CINDER_SRCS + CINDERX_SRCS + @@ -243,7 +247,8 @@ ASMJIT_SRCS + SHADOWCODE_SRCS + STRICTM_SRCS + - STATICPYTHON_SRCS + STATICPYTHON_SRCS + + CACHEDPROPS_SRCS ) # Monkey-patch the ability to compile C++ files (but not C files) with diff --git a/Include/descrobject.h b/Include/descrobject.h index 4c98fd754fb..8649c81b425 100644 --- a/Include/descrobject.h +++ b/Include/descrobject.h @@ -73,46 +73,15 @@ typedef struct { } PyWrapperDescrObject; #endif /* Py_LIMITED_API */ -/* fb t46346203 */ -typedef struct { - PyObject_HEAD - PyObject *func; /* function object */ - PyObject *name_or_descr; /* str or member descriptor object */ -} PyCachedPropertyDescrObject; -/* end fb t46346203 */ - - /* fb T82701047 */ -typedef struct { - PyObject_HEAD - PyObject *func; /* function object */ - PyObject *name_or_descr; /* str or member descriptor object */ -} PyAsyncCachedPropertyDescrObject; - /* end fb T82701047 */ - -/* fb T82701047 */ -typedef struct { - PyObject_HEAD - PyObject *func; /* function object */ - PyObject *name; /* str or member descriptor object */ - PyObject *value; /* value or NULL when uninitialized */ -} PyAsyncCachedClassPropertyDescrObject; -/* end fb T82701047 */ - PyAPI_DATA(PyTypeObject) PyClassMethodDescr_Type; PyAPI_DATA(PyTypeObject) PyGetSetDescr_Type; PyAPI_DATA(PyTypeObject) PyMemberDescr_Type; -CiAPI_DATA(PyTypeObject) PyAsyncCachedPropertyWithDescr_Type; PyAPI_DATA(PyTypeObject) PyMethodDescr_Type; PyAPI_DATA(PyTypeObject) PyWrapperDescr_Type; PyAPI_DATA(PyTypeObject) PyDictProxy_Type; #ifndef Py_LIMITED_API PyAPI_DATA(PyTypeObject) _PyMethodWrapper_Type; #endif /* Py_LIMITED_API */ -CiAPI_DATA(PyType_Spec) _PyCachedClassProperty_TypeSpec; /* fb t46346203 */ -CiAPI_DATA(PyTypeObject) PyCachedProperty_Type; /* fb T46346203 */ -CiAPI_DATA(PyTypeObject) PyCachedPropertyWithDescr_Type; /* fb T46346203 */ -CiAPI_DATA(PyTypeObject) PyAsyncCachedProperty_Type; /* fb T82701047 */ -CiAPI_DATA(PyTypeObject) PyAsyncCachedClassProperty_Type; /* fb T82701047 */ PyAPI_FUNC(PyObject *) PyDescr_NewMethod(PyTypeObject *, PyMethodDef *); PyAPI_FUNC(PyObject *) PyDescr_NewClassMethod(PyTypeObject *, PyMethodDef *); diff --git a/Makefile.pre.in b/Makefile.pre.in index 927c8cb2e9b..f95fa87fd88 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -595,6 +595,11 @@ STRICTM_OBJS= \ PARALLELGC_OBJS = \ CinderX/ParallelGC/parallel_gc.o +########################################################################## +# Cached Properties +CACHEDPROPS_OBJS = \ + CachedProperties/cached_properties.o + ########################################################################## # Objects OBJECT_OBJS= \ @@ -711,6 +716,7 @@ BUILD_CINDERX_SRCS=@BUILD_CINDERX_SRCS@ ifeq ($(BUILD_CINDERX_SRCS), yes) CINDERX_OBJS = \ $(ASMJIT_OBJS) \ + $(CACHEDPROPS_OBJS) \ $(CINDER_OBJS) \ $(JIT_OBJS) \ $(PARALLELGC_OBJS) \ @@ -1505,6 +1511,9 @@ STRICTM_HEADERS = \ $(STRICTM_PRIVATE_HEADERS) \ $(STRICTM_PUBLIC_HEADERS) +CACHEDPROPS_HEADERS = \ + $(srcdir)/CachedProperties/cached_properties.h + CINDER_HEADERS= \ $(srcdir)/Cinder/Include/cinder/cinder.h \ $(srcdir)/Cinder/Include/cinder/exports.h \ @@ -1686,6 +1695,7 @@ PYTHON_HEADERS= \ $(SHADOWCODE_HEADERS) \ $(STATICPYTHON_HEADERS) \ $(STRICTM_HEADERS) \ + $(CACHEDPROPS_HEADERS) \ $(LIBRARY_OBJS) $(MODOBJS) Programs/python.o: $(PYTHON_HEADERS) $(I386_DASM_HEADERS) $(JSON_HEADERS) $(CINDER_HEADERS) @@ -1693,6 +1703,8 @@ $(CINDER_OBJS): $(JIT_PUBLIC_HEADERS) $(CINDER_HEADERS) $(PARALLELGC_OBJS): $(CINDER_HEADERS) $(srcdir)/CinderX/ParallelGC/condvar.h $(srcdir)/CinderX/ParallelGC/ws_deque.h +$(CACHEDPROPS_OBJS): $(CACHEDPROPS_HEADERS) $(PYTHON_HEADERS) + $(JIT_OBJS): $(JIT_HEADERS) $(CINDER_HEADERS) $(SHADOWCODE_OBJS): $(SHADOWCODE_HEADERS) $(PYTHON_HEADERS) diff --git a/Modules/_static.c b/Modules/_static.c index b43019d45d9..d8a2254093a 100644 --- a/Modules/_static.c +++ b/Modules/_static.c @@ -15,6 +15,8 @@ #include "structmember.h" #include "pycore_object.h" +#include "CachedProperties/cached_properties.h" + #include "StaticPython/classloader.h" PyDoc_STRVAR(_static__doc__, diff --git a/Modules/cinder.c b/Modules/cinder.c index f48d6fba2b8..738e5338bf1 100644 --- a/Modules/cinder.c +++ b/Modules/cinder.c @@ -7,6 +7,8 @@ #include "internal/pycore_shadow_frame.h" #include "frameobject.h" +#include "CachedProperties/cached_properties.h" + #include "Jit/pyjit.h" PyAPI_FUNC(void) _PyShadow_ClearCache(PyObject *co); diff --git a/Objects/clinic/descrobject.c.h b/Objects/clinic/descrobject.c.h index 5ee1e974e4a..d248b91bf48 100644 --- a/Objects/clinic/descrobject.c.h +++ b/Objects/clinic/descrobject.c.h @@ -115,98 +115,4 @@ property_init(PyObject *self, PyObject *args, PyObject *kwargs) exit: return return_value; } - -#if defined(ENABLE_CINDERX) - -PyDoc_STRVAR(async_cached_property_init__doc__, -"async_cached_property(func, name_or_descr=None)\n" -"--\n" -"\n" -"init a async_cached_property.\n" -"\n" -"Creates a new async cached property where function will be called to produce\n" -"the async lazy value on the first access.\n" -"\n" -"If slot descriptor is provided it will be used for storing the value.\""); - -static int -async_cached_property_init_impl(PyAsyncCachedPropertyDescrObject *self, - PyObject *func, PyObject *name_or_descr); - -static int -async_cached_property_init(PyObject *self, PyObject *args, PyObject *kwargs) -{ - int return_value = -1; - static const char * const _keywords[] = {"func", "name_or_descr", NULL}; - static _PyArg_Parser _parser = {NULL, _keywords, "async_cached_property", 0}; - PyObject *argsbuf[2]; - PyObject * const *fastargs; - Py_ssize_t nargs = PyTuple_GET_SIZE(args); - Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 1; - PyObject *func; - PyObject *name_or_descr = NULL; - - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 2, 0, argsbuf); - if (!fastargs) { - goto exit; - } - func = fastargs[0]; - if (!noptargs) { - goto skip_optional_pos; - } - if (!PyObject_TypeCheck(fastargs[1], &PyMemberDescr_Type)) { - _PyArg_BadArgument("async_cached_property", "argument 'name_or_descr'", (&PyMemberDescr_Type)->tp_name, fastargs[1]); - goto exit; - } - name_or_descr = fastargs[1]; -skip_optional_pos: - return_value = async_cached_property_init_impl((PyAsyncCachedPropertyDescrObject *)self, func, name_or_descr); - -exit: - return return_value; -} - -#endif /* defined(ENABLE_CINDERX) */ - -#if defined(ENABLE_CINDERX) - -PyDoc_STRVAR(async_cached_classproperty_new__doc__, -"async_cached_classproperty(func)\n" -"--\n" -"\n" -"Provides an async cached class property.\n" -"\n" -"Works with normal types and frozen types to create values on demand\n" -"and cache them in the class."); - -static PyObject * -async_cached_classproperty_new_impl(PyTypeObject *type, PyObject *func); - -static PyObject * -async_cached_classproperty_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) -{ - PyObject *return_value = NULL; - static const char * const _keywords[] = {"func", NULL}; - static _PyArg_Parser _parser = {NULL, _keywords, "async_cached_classproperty", 0}; - PyObject *argsbuf[1]; - PyObject * const *fastargs; - Py_ssize_t nargs = PyTuple_GET_SIZE(args); - PyObject *func; - - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 1, 0, argsbuf); - if (!fastargs) { - goto exit; - } - if (!PyObject_TypeCheck(fastargs[0], &PyFunction_Type)) { - _PyArg_BadArgument("async_cached_classproperty", "argument 'func'", (&PyFunction_Type)->tp_name, fastargs[0]); - goto exit; - } - func = fastargs[0]; - return_value = async_cached_classproperty_new_impl(type, func); - -exit: - return return_value; -} - -#endif /* defined(ENABLE_CINDERX) */ -/*[clinic end generated code: output=1e3401d10f761978 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=916624e717862abc input=a9049054013a1b77]*/ diff --git a/Objects/descrobject.c b/Objects/descrobject.c index 2a089c5d3f3..814cc2e6ab1 100644 --- a/Objects/descrobject.c +++ b/Objects/descrobject.c @@ -14,10 +14,8 @@ _Py_IDENTIFIER(getattr); /*[clinic input] class mappingproxy "mappingproxyobject *" "&PyDictProxy_Type" class property "propertyobject *" "&PyProperty_Type" -class async_cached_property "PyAsyncCachedPropertyDescrObject *" "&PyAsyncCachedProperty_Type" -class async_cached_classproperty "PyAsyncCachedClassPropertyDescrObject *" "PyAsyncCachedClassProperty_Type" [clinic start generated code]*/ -/*[clinic end generated code: output=da39a3ee5e6b4b0d input=6b7166b85bfc13b6]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=556352653fd4c02e]*/ static void descr_dealloc(PyDescrObject *descr) @@ -1937,934 +1935,6 @@ PyTypeObject PyProperty_Type = { PyObject_GC_Del, /* tp_free */ }; -#ifdef ENABLE_CINDERX - -/* fb t46346203 */ - -typedef struct { - PyObject_HEAD - PyObject *func; /* function object */ - PyObject *name; /* str or member descriptor object */ - PyObject *value; /* value or NULL when uninitialized */ -} PyCachedClassPropertyDescrObject; - -static int -cached_classproperty_traverse(PyCachedClassPropertyDescrObject *prop, visitproc visit, void *arg) { - Py_VISIT(prop->func); - Py_VISIT(prop->value); - return 0; -} - -static PyObject * -cached_classproperty_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - PyObject *func; - PyCachedClassPropertyDescrObject *descr; - - if (PyTuple_GET_SIZE(args) != 1) { - PyErr_SetString(PyExc_TypeError, "cached_classproperty: 1 argument expected"); - return NULL; - } - - func = PyTuple_GET_ITEM(args, 0); - - descr = (PyCachedClassPropertyDescrObject *)PyType_GenericAlloc(type, 0); - if (descr != NULL) { - PyObject *name; - if (PyFunction_Check(func)) { - name = ((PyFunctionObject*)func)->func_name; - } else { - name = PyObject_GetAttrString(func, "__name__"); - if (name == NULL) { - Py_DECREF(descr); - return NULL; - } - } - descr->func = func; - descr->name = name; - Py_INCREF(func); - Py_INCREF(name); - } - return (PyObject *)descr; -} - -static PyObject * -cached_classproperty_get(PyObject *self, PyObject *obj, PyObject *cls) -{ - PyCachedClassPropertyDescrObject *cp = (PyCachedClassPropertyDescrObject *)self; - PyObject *res; - - res = cp->value; - if (res == NULL) { - res = _PyObject_Vectorcall(cp->func, &cls, 1, NULL); - if (res == NULL) { - return NULL; - } - if (cp->value == NULL) { - /* we steal the ref count */ - cp->value = res; - } else { - /* first value to return wins */ - Py_DECREF(res); - res = cp->value; - } - } - - Py_INCREF(res); - return res; -} - -static void -cached_classproperty_dealloc(PyCachedClassPropertyDescrObject *cp) -{ - PyObject_GC_UnTrack(cp); - Py_XDECREF(cp->func); - Py_XDECREF(cp->name); - Py_XDECREF(cp->value); - PyTypeObject *type = Py_TYPE(cp); - Py_TYPE(cp)->tp_free(cp); - Py_DECREF(type); -} - -static PyObject * -cached_classproperty_get___doc__(PyCachedClassPropertyDescrObject *cp, void *closure) -{ - PyObject *res = ((PyFunctionObject*)cp->func)->func_doc; - Py_INCREF(res); - return res; -} - -static PyObject * -cached_classproperty_get_name(PyCachedClassPropertyDescrObject *cp, void *closure) -{ - PyObject *res = cp->name; - Py_INCREF(res); - return res; -} - -static PyGetSetDef cached_classproperty_getsetlist[] = { - {"__doc__", (getter)cached_classproperty_get___doc__, NULL, NULL, NULL}, - {"name", (getter)cached_classproperty_get_name, NULL, NULL, NULL}, - {"__name__", (getter)cached_classproperty_get_name, NULL, NULL, NULL}, - {NULL} /* Sentinel */ -}; - -static PyMemberDef cached_classproperty_members[] = { - {"func", T_OBJECT, offsetof(PyCachedClassPropertyDescrObject, func), READONLY}, - {0} -}; - -static PyType_Slot PyCachedClassProperty_slots[] = { - {Py_tp_dealloc, cached_classproperty_dealloc}, - {Py_tp_traverse, cached_classproperty_traverse}, - {Py_tp_descr_get, cached_classproperty_get}, - {Py_tp_members, cached_classproperty_members}, - {Py_tp_getset, cached_classproperty_getsetlist}, - {Py_tp_new, cached_classproperty_new}, - {Py_tp_alloc, PyType_GenericAlloc}, - {Py_tp_free, PyObject_GC_Del}, - {0, 0}, -}; - -PyType_Spec _PyCachedClassProperty_TypeSpec = { - "builtins.cached_classproperty", - sizeof(PyCachedClassPropertyDescrObject), - 0, - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, - PyCachedClassProperty_slots -}; - -/* end fb t46346203 */ - -/* fb t46346203 */ - -static int -cached_property_traverse(PyCachedPropertyDescrObject *prop, visitproc visit, void *arg) -{ - Py_VISIT(prop->func); - Py_VISIT(prop->name_or_descr); - return 0; -} - - -PyTypeObject PyCachedProperty_Type; -PyTypeObject PyCachedPropertyWithDescr_Type; - -static int -cached_property_init(PyObject *self, PyObject *args, PyObject *kwds) -{ - PyCachedPropertyDescrObject *cp = (PyCachedPropertyDescrObject *)self; - PyObject *name_or_descr, *func; - - if (PyTuple_GET_SIZE(args) != 1 && PyTuple_GET_SIZE(args) != 2) { - PyErr_SetString(PyExc_TypeError, "cached_property: 1 or 2 arguments expected"); - return -1; - } - - func = PyTuple_GET_ITEM(args, 0); - - if (PyTuple_GET_SIZE(args) == 2) { - PyMemberDescrObject *descr; - name_or_descr = PyTuple_GET_ITEM(args, 1); - - if (Py_TYPE(name_or_descr) != &PyMemberDescr_Type) { - PyErr_SetString(PyExc_TypeError, "cached_property: member descriptor expected for 2nd argument"); - return -1; - } - - descr = (PyMemberDescrObject *)name_or_descr; - if(descr->d_member->type != T_OBJECT_EX || - descr->d_member->flags) { - PyErr_SetString(PyExc_TypeError, "cached_property: incompatible descriptor"); - return -1; - } - - /* change our type to enable setting the cached property, we don't allow - * subtypes because we can't change their type, and the descriptor would - * need to account for doing the lookup, and we'd need to dynamically - * create a subtype of them too, not to mention dealing with extra ref - * counting on the types */ - if (Py_TYPE(self) != &PyCachedProperty_Type && - Py_TYPE(self) != &PyCachedPropertyWithDescr_Type) { - PyErr_SetString( - PyExc_TypeError, - "cached_property: descr cannot be used with subtypes of cached_property"); - return -1; - } - - Py_TYPE(self) = &PyCachedPropertyWithDescr_Type; - } else { - name_or_descr = Py_None; - } - - cp->func = func; - cp->name_or_descr = name_or_descr; - Py_INCREF(func); - Py_INCREF(name_or_descr); - - return 0; -} - -PyDoc_STRVAR(cached_property_doc, -"cached_property(function, [slot]) --> cached_property object\n\ -\n\ -Creates a new cached property where function will be called to produce\n\ -the value on the first access.\n\ -\n\ -If slot descriptor is provided it will be used for storing the value."); - - -static PyObject * -cached_property_get(PyObject *self, PyObject *obj, PyObject *cls) -{ - PyObject *res, *dict; - PyCachedPropertyDescrObject *cp = (PyCachedPropertyDescrObject *)self; - PyObject *stack[1] = {obj}; - - if (obj == NULL) { - Py_INCREF(self); - return self; - } - - if (Py_TYPE(cp->name_or_descr) == &PyMemberDescr_Type) { - PyObject **addr; - PyMemberDescrObject *descr = (PyMemberDescrObject *)cp->name_or_descr; - - if (Py_TYPE(obj) != PyDescr_TYPE(descr) && - !PyObject_TypeCheck(obj, PyDescr_TYPE(descr))) { - PyErr_Format(PyExc_TypeError, - "descriptor '%V' for '%s' objects " - "doesn't apply to '%s' object", - ((PyDescrObject*)descr)->d_name, "?", - PyDescr_TYPE(descr)->tp_name, - Py_TYPE(obj)->tp_name); - return NULL; - } - - addr = (PyObject **)(((const char *)obj) + descr->d_member->offset); - res = *addr; - if (res != NULL) { - Py_INCREF(res); - return res; - } - - res = _PyObject_Vectorcall(cp->func, stack, 1, NULL); - if (res == NULL) { - return NULL; - } - - *addr = res; - Py_INCREF(res); - } else { - dict = PyObject_GenericGetDict(obj, NULL); - if (dict == NULL) { - if (PyErr_ExceptionMatches(PyExc_AttributeError)) { - PyErr_Clear(); - PyErr_Format( - PyExc_TypeError, - "No '__dict__' attribute on '%s' instance to cache '%U' property.", - Py_TYPE(obj)->tp_name, cp->name_or_descr - ); - } - return NULL; - } - - res = PyDict_GetItem(dict, cp->name_or_descr); - Py_DECREF(dict); - if (res != NULL) { - Py_INCREF(res); /* we got a borrowed ref */ - return res; - } - - res = _PyObject_Vectorcall(cp->func, stack, 1, NULL); - if (res == NULL) { - return NULL; - } - - if (_PyObjectDict_SetItem(Py_TYPE(obj), _PyObject_GetDictPtr(obj), cp->name_or_descr, res)) { - Py_DECREF(res); - return NULL; - } - } - - return res; -} - -static int -cached_property_set(PyObject *self, PyObject *obj, PyObject *value) -{ - PyCachedPropertyDescrObject *cp = (PyCachedPropertyDescrObject *)self; - PyObject **dictptr; - - if (Py_TYPE(cp->name_or_descr) == &PyMemberDescr_Type) { - return Py_TYPE(cp->name_or_descr)->tp_descr_set(cp->name_or_descr, obj, value); - } - - dictptr = _PyObject_GetDictPtr(obj); - - if (dictptr == NULL) { - PyErr_SetString(PyExc_AttributeError, - "This object has no __dict__"); - return -1; - } - - return _PyObjectDict_SetItem(Py_TYPE(obj), dictptr, cp->name_or_descr, value); -} - -static void -cached_property_dealloc(PyCachedPropertyDescrObject *cp) -{ - PyObject_GC_UnTrack(cp); - Py_XDECREF(cp->func); - Py_XDECREF(cp->name_or_descr); - Py_TYPE(cp)->tp_free(cp); -} - -static PyObject * -cached_property_get___doc__(PyCachedPropertyDescrObject *cp, void *closure) -{ - PyObject *res = ((PyFunctionObject*)cp->func)->func_doc; - Py_INCREF(res); - return res; -} - -static PyObject * -cached_property_get_name(PyCachedPropertyDescrObject *cp, void *closure) -{ - PyObject *res; - - if (Py_TYPE(cp->name_or_descr) != &PyMemberDescr_Type) { - res = cp->name_or_descr; - } else { - res = PyDescr_NAME(cp->name_or_descr); - } - Py_INCREF(res); - return res; -} - -static PyObject * -cached_property_get_slot(PyCachedPropertyDescrObject *cp, void *closure) -{ - if (Py_TYPE(cp->name_or_descr) == &PyMemberDescr_Type) { - PyObject *res = cp->name_or_descr; - - Py_INCREF(res); - return res; - } - Py_RETURN_NONE; -} - -static PyObject * -cached_property_clear(PyCachedPropertyDescrObject *self, PyObject *obj) -{ - PyCachedPropertyDescrObject *cp = (PyCachedPropertyDescrObject *)self; - PyObject **dictptr; - - if (Py_TYPE(cp->name_or_descr) == &PyMemberDescr_Type) { - if (Py_TYPE(cp->name_or_descr)->tp_descr_set(cp->name_or_descr, obj, NULL) < 0) { - if (PyErr_ExceptionMatches(PyExc_AttributeError)) { - PyErr_Clear(); - Py_RETURN_NONE; - } - return NULL; - } - Py_RETURN_NONE; - } - - dictptr = _PyObject_GetDictPtr(obj); - - if (dictptr == NULL) { - PyErr_SetString(PyExc_AttributeError, - "This object has no __dict__"); - return NULL; - } - - if (_PyObjectDict_SetItem(Py_TYPE(obj), dictptr, cp->name_or_descr, NULL) < 0) { - if (PyErr_ExceptionMatches(PyExc_KeyError)) { - PyErr_Clear(); - Py_RETURN_NONE; - } - return NULL; - } - Py_RETURN_NONE; -} - -static PyObject * -cached_property_has_value(PyCachedPropertyDescrObject *self, PyObject *obj) -{ - PyCachedPropertyDescrObject *cp = (PyCachedPropertyDescrObject *)self; - PyObject **dictptr; - - if (Py_TYPE(cp->name_or_descr) == &PyMemberDescr_Type) { - PyObject *value = Py_TYPE(cp->name_or_descr)->tp_descr_get( - cp->name_or_descr, obj, (PyObject *)Py_TYPE(obj)); - - if (value == NULL) { - if (PyErr_ExceptionMatches(PyExc_AttributeError)) { - PyErr_Clear(); - Py_RETURN_FALSE; - } - return NULL; - } - Py_DECREF(value); - Py_RETURN_TRUE; - } - - dictptr = _PyObject_GetDictPtr(obj); - - if (dictptr == NULL) { - PyErr_SetString(PyExc_AttributeError, - "This object has no __dict__"); - return NULL; - } - - if (*dictptr == NULL) { - Py_RETURN_FALSE; - } - - PyObject *value = PyDict_GetItem(*dictptr, cp->name_or_descr); - if (value == NULL) { - Py_RETURN_FALSE; - } - Py_RETURN_TRUE; -} - -static PyObject * -cached_property___set_name__(PyObject* self, PyObject* const* args, Py_ssize_t nargs) -{ - if (nargs != 2) { - PyErr_SetString(PyExc_TypeError, "cached_property.__set_name__: 2 arguments expected"); - return NULL; - } - PyCachedPropertyDescrObject *cp = (PyCachedPropertyDescrObject *)self; - PyObject *Py_UNUSED(owner) = args[0]; - PyObject *name = args[1]; - - // Perform error checks if the name wasn't initialized (i.e, not None) - if (cp->name_or_descr != Py_None) { - if (PyUnicode_CheckExact(cp->name_or_descr)) { - // Check for naming conflicts - if (PyUnicode_Compare(cp->name_or_descr, name) != 0) { - if (!PyErr_Occurred()) { - // Avoid masking existing errors - PyErr_Format( - PyExc_TypeError, - "Cannot assign the same cached_property to two different names (%R and %R).", - cp->name_or_descr, name - ); - } - return NULL; - } - } else { - // This cannot normally happen in managed code, unless someone manually calls `__set_name__` - // after a slot-backed property was defined (see test_cached_property_set_name_on_slot_backed_property) - PyErr_Format( - PyExc_RuntimeError, - "Cannot set name (%R) for a cached property backed by a slot (%R)", - name, cp - ); - return NULL; - } - } - - Py_SETREF(cp->name_or_descr, name); - Py_INCREF(name); - Py_RETURN_NONE; -} - - -static PyGetSetDef cached_property_getsetlist[] = { - {"__doc__", (getter)cached_property_get___doc__, NULL, NULL, NULL}, - {"__name__", (getter)cached_property_get_name, NULL, NULL, NULL}, - {"name", (getter)cached_property_get_name, NULL, NULL, NULL}, - {"slot", (getter)cached_property_get_slot, NULL, NULL, NULL}, - {NULL} /* Sentinel */ -}; - -static PyMemberDef cached_property_members[] = { - {"func", T_OBJECT, offsetof(PyCachedPropertyDescrObject, func), READONLY}, - /* currently duplicated until all consumers are updated in favor of fget */ - {"fget", T_OBJECT, offsetof(PyCachedPropertyDescrObject, func), READONLY}, - {0} -}; - -static PyMethodDef cached_property_methods[] = { - {"clear", (PyCFunction)cached_property_clear, METH_O, NULL}, - {"has_value", (PyCFunction)cached_property_has_value, METH_O, NULL}, - {"__set_name__", (PyCFunction)cached_property___set_name__, METH_FASTCALL, NULL}, - {NULL, NULL} -}; - -PyTypeObject PyCachedProperty_Type = { - PyVarObject_HEAD_INIT(&PyType_Type, 0) - .tp_name = "cached_property", - .tp_basicsize = sizeof(PyCachedPropertyDescrObject), - .tp_dealloc = (destructor)cached_property_dealloc, - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | - Py_TPFLAGS_BASETYPE, - .tp_doc = cached_property_doc, - .tp_traverse = (traverseproc)cached_property_traverse, - .tp_descr_get = cached_property_get, - .tp_members = cached_property_members, - .tp_getset = cached_property_getsetlist, - .tp_new = PyType_GenericNew, - .tp_init = cached_property_init, - .tp_alloc = PyType_GenericAlloc, - .tp_free = PyObject_GC_Del, - .tp_methods = cached_property_methods, -}; - -PyTypeObject PyCachedPropertyWithDescr_Type = { - PyVarObject_HEAD_INIT(&PyType_Type, 0) - .tp_name = "cached_property_with_descr", - .tp_base = &PyCachedProperty_Type, - .tp_basicsize = sizeof(PyCachedPropertyDescrObject), - .tp_dealloc = (destructor)cached_property_dealloc, - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | - Py_TPFLAGS_BASETYPE, - .tp_doc = cached_property_doc, - .tp_traverse = (traverseproc)cached_property_traverse, - .tp_descr_get = cached_property_get, - .tp_descr_set = cached_property_set, - .tp_members = cached_property_members, - .tp_getset = cached_property_getsetlist, - .tp_new = PyType_GenericNew, - .tp_init = cached_property_init, - .tp_alloc = PyType_GenericAlloc, - .tp_free = PyObject_GC_Del, -}; - -/* end fb t46346203 */ - -/* fb T82701047 */ -/*********************** AsyncCachedProperty*********************/ -static PyObject *_AsyncLazyValue_Type; - -static int -async_cached_property_traverse(PyAsyncCachedPropertyDescrObject *prop, visitproc visit, void *arg) -{ - Py_VISIT(prop->func); - Py_VISIT(prop->name_or_descr); - return 0; -} - -/*[clinic input] - -async_cached_property.__init__ as async_cached_property_init - - self: self(type="PyAsyncCachedPropertyDescrObject *") - func: object - name_or_descr: object(c_default="NULL", subclass_of="&PyMemberDescr_Type") = None - -init a async_cached_property. - -Creates a new async cached property where function will be called to produce -the async lazy value on the first access. - -If slot descriptor is provided it will be used for storing the value." -[clinic start generated code]*/ - -static int -async_cached_property_init_impl(PyAsyncCachedPropertyDescrObject *self, - PyObject *func, PyObject *name_or_descr) -/*[clinic end generated code: output=d8f17f423e7ad7f2 input=5bfa6f9a771d138d]*/ -{ - if (name_or_descr != NULL) { - PyMemberDescrObject *descr = (PyMemberDescrObject *)name_or_descr; - if(descr->d_member->type != T_OBJECT_EX || - descr->d_member->flags) { - PyErr_SetString(PyExc_TypeError, "async_cached_property: incompatible descriptor"); - return -1; - } - - /* change our type to enable setting the cached property, we don't allow - * subtypes because we can't change their type, and the descriptor would - * need to account for doing the lookup, and we'd need to dynamically - * create a subtype of them too, not to mention dealing with extra ref - * counting on the types */ - if (Py_TYPE(self) != &PyAsyncCachedProperty_Type && - Py_TYPE(self) != &PyAsyncCachedPropertyWithDescr_Type) { - PyErr_SetString( - PyExc_TypeError, - "async_cached_property: descr cannot be used with subtypes of async_cached_property"); - return -1; - } - - Py_TYPE(self) = &PyAsyncCachedPropertyWithDescr_Type; - self->name_or_descr = name_or_descr; - } else if (PyFunction_Check(func)) { - self->name_or_descr = ((PyFunctionObject *)func)->func_name; - } else { - self->name_or_descr = PyObject_GetAttrString(func, "__name__"); - if (self->name_or_descr == NULL) { - return -1; - } - } - - self->func = func; - Py_INCREF(self->func); - Py_INCREF(self->name_or_descr); - - return 0; -} - -static inline int import_async_lazy_value() { - if (_AsyncLazyValue_Type == NULL) { - _Py_IDENTIFIER(AsyncLazyValue); - PyObject *asyncio = PyImport_ImportModule("_asyncio"); - if (asyncio == NULL) { - return -1; - } - _AsyncLazyValue_Type = _PyObject_GetAttrId(asyncio, &PyId_AsyncLazyValue); - Py_DECREF(asyncio); - if (_AsyncLazyValue_Type == NULL) { - return -1; - } - } - return 0; -} - -static PyObject * -async_cached_property_get(PyObject *self, PyObject *obj, PyObject *cls) -{ - PyObject *res; - PyAsyncCachedPropertyDescrObject *cp = (PyAsyncCachedPropertyDescrObject *)self; - - if (obj == NULL) { - Py_INCREF(self); - return self; - } - - if (Py_TYPE(cp->name_or_descr) == &PyMemberDescr_Type) { - PyObject **addr; - PyMemberDescrObject *descr = (PyMemberDescrObject *)cp->name_or_descr; - - if (Py_TYPE(obj) != PyDescr_TYPE(descr) && - !PyObject_TypeCheck(obj, PyDescr_TYPE(descr))) { - PyErr_Format(PyExc_TypeError, - "descriptor '%V' for '%s' objects " - "doesn't apply to '%s' object", - ((PyDescrObject*)descr)->d_name, "?", - PyDescr_TYPE(descr)->tp_name, - Py_TYPE(obj)->tp_name); - return NULL; - } - - addr = (PyObject **)(((const char *)obj) + descr->d_member->offset); - res = *addr; - if (res != NULL) { - Py_INCREF(res); - return res; - } - if (import_async_lazy_value() < 0) { - return NULL; - } - res = PyObject_CallFunctionObjArgs(_AsyncLazyValue_Type, cp->func, obj, NULL); - if (res == NULL) { - return NULL; - } - - *addr = res; - Py_INCREF(res); - } else { - if (import_async_lazy_value() < 0) { - return NULL; - } - - res = PyObject_CallFunctionObjArgs(_AsyncLazyValue_Type, cp->func, obj, NULL); - if (res == NULL) { - return NULL; - } - - if (PyObject_SetAttr(obj, cp->name_or_descr, res) < 0) { - Py_DECREF(res); - return NULL; - } - } - - return res; -} - -static void -async_cached_property_dealloc(PyAsyncCachedPropertyDescrObject *cp) -{ - PyObject_GC_UnTrack(cp); - Py_XDECREF(cp->func); - Py_XDECREF(cp->name_or_descr); - Py_TYPE(cp)->tp_free(cp); -} - -static PyObject * -async_cached_property_get___doc__(PyAsyncCachedPropertyDescrObject *cp, void *closure) -{ - PyObject *res = ((PyFunctionObject*)cp->func)->func_doc; - Py_INCREF(res); - return res; -} - -static PyObject * -async_cached_property_get_name(PyAsyncCachedPropertyDescrObject *cp, void *closure) -{ - PyObject *res; - - if (Py_TYPE(cp->name_or_descr) != &PyMemberDescr_Type) { - res = cp->name_or_descr; - } else { - res = PyDescr_NAME(cp->name_or_descr); - } - Py_INCREF(res); - return res; -} - -static PyObject * -async_cached_property_get_slot(PyAsyncCachedPropertyDescrObject *cp, void *closure) -{ - if (Py_TYPE(cp->name_or_descr) == &PyMemberDescr_Type) { - PyObject *res = cp->name_or_descr; - - Py_INCREF(res); - return res; - } - Py_RETURN_NONE; -} - -static int -async_cached_property_set(PyObject *self, PyObject *obj, PyObject *value) -{ - PyAsyncCachedPropertyDescrObject *cp = (PyAsyncCachedPropertyDescrObject *)self; - PyObject **dictptr; - - if (Py_TYPE(cp->name_or_descr) == &PyMemberDescr_Type) { - return Py_TYPE(cp->name_or_descr)->tp_descr_set(cp->name_or_descr, obj, value); - } - - dictptr = _PyObject_GetDictPtr(obj); - - if (dictptr == NULL) { - PyErr_SetString(PyExc_AttributeError, - "This object has no __dict__"); - return -1; - } - - return _PyObjectDict_SetItem(Py_TYPE(obj), dictptr, cp->name_or_descr, value); -} - - -static PyGetSetDef async_cached_property_getsetlist[] = { - {"__doc__", (getter)async_cached_property_get___doc__, NULL, NULL, NULL}, - {"name", (getter)async_cached_property_get_name, NULL, NULL, NULL}, - {"slot", (getter)async_cached_property_get_slot, NULL, NULL, NULL}, - {NULL} /* Sentinel */ -}; - -static PyMemberDef async_cached_property_members[] = { - {"func", T_OBJECT, offsetof(PyAsyncCachedPropertyDescrObject, func), READONLY}, - {"fget", T_OBJECT, offsetof(PyAsyncCachedPropertyDescrObject, func), READONLY}, - {0} -}; - - -PyTypeObject PyAsyncCachedProperty_Type = { - PyVarObject_HEAD_INIT(&PyType_Type, 0) - .tp_name = "async_cached_property", - .tp_basicsize = sizeof(PyAsyncCachedPropertyDescrObject), - .tp_dealloc = (destructor)async_cached_property_dealloc, - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | - Py_TPFLAGS_BASETYPE, - .tp_doc = async_cached_property_init__doc__, - .tp_traverse = (traverseproc)async_cached_property_traverse, - .tp_descr_get = async_cached_property_get, - .tp_members = async_cached_property_members, - .tp_getset = async_cached_property_getsetlist, - .tp_new = PyType_GenericNew, - .tp_init = async_cached_property_init, - .tp_alloc = PyType_GenericAlloc, - .tp_free = PyObject_GC_Del, -}; - -PyTypeObject PyAsyncCachedPropertyWithDescr_Type = { - PyVarObject_HEAD_INIT(&PyType_Type, 0) - .tp_name = "async_cached_property_with_descr", - .tp_basicsize = sizeof(PyAsyncCachedPropertyDescrObject), - .tp_dealloc = (destructor)async_cached_property_dealloc, - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | - Py_TPFLAGS_BASETYPE, - .tp_doc = async_cached_property_init__doc__, - .tp_traverse = (traverseproc)async_cached_property_traverse, - .tp_descr_get = async_cached_property_get, - .tp_descr_set = async_cached_property_set, - .tp_members = async_cached_property_members, - .tp_getset = async_cached_property_getsetlist, - .tp_new = PyType_GenericNew, - .tp_init = async_cached_property_init, - .tp_alloc = PyType_GenericAlloc, - .tp_free = PyObject_GC_Del, -}; - - -/* end fb T82701047 */ -/* fb T82701047 */ -/*********************** AsyncCachedClassProperty*********************/ -static int -async_cached_classproperty_traverse(PyAsyncCachedClassPropertyDescrObject *prop, visitproc visit, void *arg) { - Py_VISIT(prop->func); - Py_VISIT(prop->value); - return 0; -} -/*[clinic input] -@classmethod -async_cached_classproperty.__new__ as async_cached_classproperty_new - - func: object(subclass_of="&PyFunction_Type") - -Provides an async cached class property. - -Works with normal types and frozen types to create values on demand -and cache them in the class. - -[clinic start generated code]*/ - -static PyObject * -async_cached_classproperty_new_impl(PyTypeObject *type, PyObject *func) -/*[clinic end generated code: output=b7972e5345764116 input=056050fde0415935]*/ -{ - PyAsyncCachedClassPropertyDescrObject *descr; - descr = (PyAsyncCachedClassPropertyDescrObject *)PyType_GenericAlloc(type, 0); - if (descr != NULL) { - PyObject *name = ((PyFunctionObject*)func)->func_name; - descr->func = func; - descr->name = name; - Py_INCREF(func); - Py_INCREF(name); - } - return (PyObject *)descr; -} - -static PyObject * -async_cached_classproperty_get(PyObject *self, PyObject *obj, PyObject *cls) -{ - PyAsyncCachedClassPropertyDescrObject *cp = (PyAsyncCachedClassPropertyDescrObject *)self; - PyObject *res; - - res = cp->value; - if (res == NULL) { - if (import_async_lazy_value() < 0) { - return NULL; - } - res = PyObject_CallFunctionObjArgs(_AsyncLazyValue_Type, cp->func, cls, NULL); - if (res == NULL) { - return NULL; - } - if (cp->value == NULL) { - /* we steal the ref count */ - cp->value = res; - } else { - /* first value to return wins */ - Py_DECREF(res); - res = cp->value; - } - } - - Py_INCREF(res); - return res; -} - -static void -async_cached_classproperty_dealloc(PyAsyncCachedClassPropertyDescrObject *cp) -{ - PyObject_GC_UnTrack(cp); - Py_XDECREF(cp->func); - Py_XDECREF(cp->name); - Py_XDECREF(cp->value); - Py_TYPE(cp)->tp_free(cp); -} - -static PyObject * -async_cached_classproperty_get___doc__(PyAsyncCachedClassPropertyDescrObject *cp, void *closure) -{ - PyObject *res = ((PyFunctionObject*)cp->func)->func_doc; - Py_INCREF(res); - return res; -} - -static PyObject * -async_cached_classproperty_get_name(PyAsyncCachedClassPropertyDescrObject *cp, void *closure) -{ - PyObject *res = cp->name; - Py_INCREF(res); - return res; -} - -static PyGetSetDef async_cached_classproperty_getsetlist[] = { - {"__doc__", (getter)async_cached_classproperty_get___doc__, NULL, NULL, NULL}, - {"name", (getter)async_cached_classproperty_get_name, NULL, NULL, NULL}, - {NULL} /* Sentinel */ -}; - -static PyMemberDef async_cached_classproperty_members[] = { - {"func", T_OBJECT, offsetof(PyAsyncCachedClassPropertyDescrObject, func), READONLY}, - {0} -}; - -PyTypeObject PyAsyncCachedClassProperty_Type = { - PyVarObject_HEAD_INIT(&PyType_Type, 0) - .tp_name = "async_cached_classproperty", - .tp_basicsize = sizeof(PyAsyncCachedClassPropertyDescrObject), - .tp_dealloc = (destructor)async_cached_classproperty_dealloc, - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, - .tp_doc = async_cached_classproperty_new__doc__, - .tp_traverse = (traverseproc)async_cached_classproperty_traverse, - .tp_descr_get = async_cached_classproperty_get, - .tp_members = async_cached_classproperty_members, - .tp_getset = async_cached_classproperty_getsetlist, - .tp_new = async_cached_classproperty_new, - .tp_alloc = PyType_GenericAlloc, - .tp_free = PyObject_GC_Del, -}; - -/* end fb T82701047 */ -#endif - funcptr Cix_method_enter_call(PyThreadState *tstate, PyObject *func) { diff --git a/Shadowcode/shadowcode.c b/Shadowcode/shadowcode.c index 2d8b0cfdcef..b5c04b7a029 100644 --- a/Shadowcode/shadowcode.c +++ b/Shadowcode/shadowcode.c @@ -11,6 +11,7 @@ * which are used to store information to execute the optimized bytecodes. */ +#include "CachedProperties/cached_properties.h" #include "Jit/pyjit.h" #include "Python.h" #include "Shadowcode/shadowcode.h" diff --git a/Shadowcode/shadowcode.h b/Shadowcode/shadowcode.h index fc7c5e17c28..9db90168573 100644 --- a/Shadowcode/shadowcode.h +++ b/Shadowcode/shadowcode.h @@ -15,6 +15,8 @@ #include "cinder/ci_api.h" +#include "CachedProperties/cached_properties.h" + #include #ifndef Py_SHADOWCODE_H diff --git a/StaticPython/classloader.c b/StaticPython/classloader.c index acb99d808e7..304198202c1 100644 --- a/StaticPython/classloader.c +++ b/StaticPython/classloader.c @@ -13,6 +13,8 @@ #include "cinder/exports.h" +#include "CachedProperties/cached_properties.h" + #include "Jit/pyjit.h" #include "StaticPython/classloader.h" diff --git a/configure b/configure index b52fb829608..f21b51bdd62 100755 --- a/configure +++ b/configure @@ -17276,6 +17276,7 @@ done SRCDIRS="Parser Objects Python Modules Programs \ + CachedProperties \ Cinder \ CinderX/ParallelGC \ CinderX/RuntimeTests \ diff --git a/configure.ac b/configure.ac index ddb4250ff7e..f2453bc42d5 100644 --- a/configure.ac +++ b/configure.ac @@ -5681,6 +5681,7 @@ done AC_SUBST(SRCDIRS) SRCDIRS="Parser Objects Python Modules Programs \ + CachedProperties \ Cinder \ CinderX/ParallelGC \ CinderX/RuntimeTests \