From 516d5a76075ac6450371c834051ad2e07c6680ef Mon Sep 17 00:00:00 2001 From: da-woods Date: Sat, 22 Jul 2023 21:11:48 +0100 Subject: [PATCH 1/6] Changes CyFunction to compile with Py_LIMITED_API Mostly that we store the PyCFunctionObject as an attribute of it rather than "inheriting" from PyCFunctionObject. --- Cython/Compiler/ModuleNode.py | 10 +- Cython/Utility/CythonFunction.c | 157 ++++++++++++++++++++++++++++--- Cython/Utility/ModuleSetupCode.c | 8 +- 3 files changed, 160 insertions(+), 15 deletions(-) diff --git a/Cython/Compiler/ModuleNode.py b/Cython/Compiler/ModuleNode.py index bf2cc63e1e1..f49d8cdeb8e 100644 --- a/Cython/Compiler/ModuleNode.py +++ b/Cython/Compiler/ModuleNode.py @@ -792,7 +792,15 @@ def generate_module_preamble(self, env, options, cimported_modules, metadata, co code.globalstate["end"].putln("#endif /* Py_PYTHON_H */") from .. import __version__ - code.putln('#define CYTHON_ABI "%s"' % __version__.replace('.', '_')) + code.putln('#if CYTHON_LIMITED_API') # CYTHON_COMPILING_IN_LIMITED_API not yet defined + # The limited API makes some significant changes to data structures, so we don't + # want to shared implementation compiled with and without the limited API. + code.putln('#define __PYX_EXTRA_ABI_MODULE_NAME "limited"') + code.putln('#else') + code.putln('#define __PYX_EXTRA_ABI_MODULE_NAME ""') + code.putln('#endif') + code.putln('#define CYTHON_ABI "%s" __PYX_EXTRA_ABI_MODULE_NAME' % + __version__.replace('.', '_')) code.putln('#define __PYX_ABI_MODULE_NAME "_cython_" CYTHON_ABI') code.putln('#define __PYX_TYPE_MODULE_PREFIX __PYX_ABI_MODULE_NAME "."') code.putln('#define CYTHON_HEX_VERSION %s' % build_hex_version(__version__)) diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index c2f85581a10..958bb824900 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -11,7 +11,7 @@ #define __Pyx_CyFunction_GetClosure(f) \ (((__pyx_CyFunctionObject *) (f))->func_closure) -#if PY_VERSION_HEX < 0x030900B1 +#if PY_VERSION_HEX < 0x030900B1 || CYTHON_COMPILING_IN_LIMITED_API #define __Pyx_CyFunction_GetClassObj(f) \ (((__pyx_CyFunctionObject *) (f))->func_classobj) #else @@ -28,16 +28,22 @@ typedef struct { +#if !CYTHON_COMPILING_IN_LIMITED_API #if PY_VERSION_HEX < 0x030900B1 PyCFunctionObject func; #else // PEP-573: PyCFunctionObject + mm_class PyCMethodObject func; #endif +#else + PyObject_HEAD; + // We can't "inherit" from func, but we can use it as a data store + PyObject *func; +#endif #if CYTHON_BACKPORT_VECTORCALL __pyx_vectorcallfunc func_vectorcall; #endif -#if PY_VERSION_HEX < 0x030500A0 +#if PY_VERSION_HEX < 0x030500A0 || CYTHON_COMPILING_IN_LIMITED_API PyObject *func_weakreflist; #endif PyObject *func_dict; @@ -47,7 +53,7 @@ typedef struct { PyObject *func_globals; PyObject *func_code; PyObject *func_closure; -#if PY_VERSION_HEX < 0x030900B1 +#if PY_VERSION_HEX < 0x030900B1 || CYTHON_COMPILING_IN_LIMITED_API // No-args super() class cell PyObject *func_classobj; #endif @@ -112,7 +118,7 @@ static PyObject * __Pyx_CyFunction_Vectorcall_FASTCALL_KEYWORDS_METHOD(PyObject //@requires: ObjectHandling.c::PyObjectGetAttrStr static CYTHON_INLINE void __Pyx__CyFunction_SetClassObj(__pyx_CyFunctionObject* f, PyObject* classobj) { -#if PY_VERSION_HEX < 0x030900B1 +#if PY_VERSION_HEX < 0x030900B1 || CYTHON_COMPILING_IN_LIMITED_API __Pyx_Py_XDECREF_SET( __Pyx_CyFunction_GetClassObj(f), ((classobj) ? __Pyx_NewRef(classobj) : NULL)); @@ -129,6 +135,7 @@ __Pyx_CyFunction_get_doc(__pyx_CyFunctionObject *op, void *closure) { CYTHON_UNUSED_VAR(closure); if (unlikely(op->func_doc == NULL)) { +#if !CYTHON_COMPILING_IN_LIMITED_API if (((PyCFunctionObject*)op)->m_ml->ml_doc) { #if PY_MAJOR_VERSION >= 3 op->func_doc = PyUnicode_FromString(((PyCFunctionObject*)op)->m_ml->ml_doc); @@ -141,6 +148,10 @@ __Pyx_CyFunction_get_doc(__pyx_CyFunctionObject *op, void *closure) Py_INCREF(Py_None); return Py_None; } +#else // CYTHON_COMPILING_IN_LIMITED_API + op->func_doc = PyObject_GetAttrString(op->func, "__doc__"); + if (unlikely(!op->func_doc)) return NULL; +#endif } Py_INCREF(op->func_doc); return op->func_doc; @@ -164,10 +175,14 @@ __Pyx_CyFunction_get_name(__pyx_CyFunctionObject *op, void *context) { CYTHON_UNUSED_VAR(context); if (unlikely(op->func_name == NULL)) { +#if !CYTHON_COMPILING_IN_LIMITED_API #if PY_MAJOR_VERSION >= 3 op->func_name = PyUnicode_InternFromString(((PyCFunctionObject*)op)->m_ml->ml_name); #else op->func_name = PyString_InternFromString(((PyCFunctionObject*)op)->m_ml->ml_name); +#endif +#else // CYTHON_COMPILING_IN_LIMITED_API + op->func_name = PyObject_GetAttrString(op->func, "__name__"); #endif if (unlikely(op->func_name == NULL)) return NULL; @@ -294,10 +309,10 @@ __Pyx_CyFunction_init_defaults(__pyx_CyFunctionObject *op) { op->defaults_kwdict = PyTuple_GET_ITEM(res, 1); Py_INCREF(op->defaults_kwdict); #else - op->defaults_tuple = PySequence_ITEM(res, 0); + op->defaults_tuple = __Pyx_PySequence_ITEM(res, 0); if (unlikely(!op->defaults_tuple)) result = -1; else { - op->defaults_kwdict = PySequence_ITEM(res, 1); + op->defaults_tuple = __Pyx_PySequence_ITEM(res, 0); if (unlikely(!op->defaults_kwdict)) result = -1; } #endif @@ -416,7 +431,15 @@ __Pyx_CyFunction_get_is_coroutine(__pyx_CyFunctionObject *op, void *context) { fromlist = PyList_New(1); if (unlikely(!fromlist)) return NULL; Py_INCREF(marker); +#if CYTHON_ASSUME_SAFE_MACROS PyList_SET_ITEM(fromlist, 0, marker); +#else + if (unlikely(PyList_SetItem(fromlist, 0, marker) < 0)) { + Py_DECREF(marker); + Py_DECREF(fromlist); + return NULL; + } +#endif module = PyImport_ImportModuleLevelObject(PYIDENT("asyncio.coroutines"), NULL, NULL, fromlist, 0); Py_DECREF(fromlist); if (unlikely(!module)) goto ignore; @@ -460,6 +483,20 @@ __Pyx_CyFunction_get_is_coroutine(__pyx_CyFunctionObject *op, void *context) { //} //#endif +#if CYTHON_COMPILING_IN_LIMITED_API +static PyObject * +__Pyx_CyFunction_get_module(__pyx_CyFunctionObject *op, void *context) { + CYTHON_UNUSED_VAR(context); + return PyObject_GetAttrString(op->func, "__module__"); +} + +static int +__Pyx_CyFunction_set_module(__pyx_CyFunctionObject *op, PyObject* value, void *context) { + CYTHON_UNUSED_VAR(context); + return PyObject_SetAttrString(op->func, "__module__", value); +} +#endif + static PyGetSetDef __pyx_CyFunction_getsets[] = { {(char *) "func_doc", (getter)__Pyx_CyFunction_get_doc, (setter)__Pyx_CyFunction_set_doc, 0, 0}, {(char *) "__doc__", (getter)__Pyx_CyFunction_get_doc, (setter)__Pyx_CyFunction_set_doc, 0, 0}, @@ -482,21 +519,28 @@ static PyGetSetDef __pyx_CyFunction_getsets[] = { //#if PY_VERSION_HEX >= 0x030400C1 // {(char *) "__signature__", (getter)__Pyx_CyFunction_get_signature, 0, 0, 0}, //#endif +#if CYTHON_COMPILING_IN_LIMITED_API + {"__module__", (getter)__Pyx_CyFunction_get_module, (setter)__Pyx_CyFunction_set_module, 0, 0}, +#endif {0, 0, 0, 0, 0} }; static PyMemberDef __pyx_CyFunction_members[] = { +#if !CYTHON_COMPILING_IN_LIMITED_API {(char *) "__module__", T_OBJECT, offsetof(PyCFunctionObject, m_module), 0, 0}, +#endif #if CYTHON_USE_TYPE_SPECS {(char *) "__dictoffset__", T_PYSSIZET, offsetof(__pyx_CyFunctionObject, func_dict), READONLY, 0}, #if CYTHON_METH_FASTCALL #if CYTHON_BACKPORT_VECTORCALL {(char *) "__vectorcalloffset__", T_PYSSIZET, offsetof(__pyx_CyFunctionObject, func_vectorcall), READONLY, 0}, #else +#if !CYTHON_COMPILING_IN_LIMITED_API {(char *) "__vectorcalloffset__", T_PYSSIZET, offsetof(PyCFunctionObject, vectorcall), READONLY, 0}, #endif #endif -#if PY_VERSION_HEX < 0x030500A0 +#endif +#if PY_VERSION_HEX < 0x030500A0 || CYTHON_COMPILING_IN_LIMITED_API {(char *) "__weaklistoffset__", T_PYSSIZET, offsetof(__pyx_CyFunctionObject, func_weakreflist), READONLY, 0}, #else {(char *) "__weaklistoffset__", T_PYSSIZET, offsetof(PyCFunctionObject, m_weakreflist), READONLY, 0}, @@ -523,7 +567,7 @@ static PyMethodDef __pyx_CyFunction_methods[] = { }; -#if PY_VERSION_HEX < 0x030500A0 +#if PY_VERSION_HEX < 0x030500A0 || CYTHON_COMPILING_IN_LIMITED_API #define __Pyx_CyFunction_weakreflist(cyfunc) ((cyfunc)->func_weakreflist) #else #define __Pyx_CyFunction_weakreflist(cyfunc) (((PyCFunctionObject*)cyfunc)->m_weakreflist) @@ -531,17 +575,27 @@ static PyMethodDef __pyx_CyFunction_methods[] = { static PyObject *__Pyx_CyFunction_Init(__pyx_CyFunctionObject *op, PyMethodDef *ml, int flags, PyObject* qualname, PyObject *closure, PyObject *module, PyObject* globals, PyObject* code) { +#if !CYTHON_COMPILING_IN_LIMITED_API PyCFunctionObject *cf = (PyCFunctionObject*) op; +#endif if (unlikely(op == NULL)) return NULL; +#if CYTHON_COMPILING_IN_LIMITED_API + op->func = PyCFunction_NewEx(ml, (PyObject*)op, module); + if (unlikely(!op->func)) return NULL; +#endif op->flags = flags; +#if !CYTHON_COMPILING_IN_LIMITED_API __Pyx_CyFunction_weakreflist(op) = NULL; cf->m_ml = ml; cf->m_self = (PyObject *) op; +#endif Py_XINCREF(closure); op->func_closure = closure; +#if !CYTHON_COMPILING_IN_LIMITED_API Py_XINCREF(module); cf->m_module = module; +#endif op->func_dict = NULL; op->func_name = NULL; Py_INCREF(qualname); @@ -549,7 +603,7 @@ static PyObject *__Pyx_CyFunction_Init(__pyx_CyFunctionObject *op, PyMethodDef * op->func_doc = NULL; #if PY_VERSION_HEX < 0x030900B1 op->func_classobj = NULL; -#else +#elif !CYTHON_COMPILING_IN_LIMITED_API ((PyCMethodObject*)op)->mm_class = NULL; #endif op->func_globals = globals; @@ -597,13 +651,18 @@ static int __Pyx_CyFunction_clear(__pyx_CyFunctionObject *m) { Py_CLEAR(m->func_closure); +#if !CYTHON_COMPILING_IN_LIMITED_API Py_CLEAR(((PyCFunctionObject*)m)->m_module); +#else + Py_CLEAR(m->func); +#endif Py_CLEAR(m->func_dict); Py_CLEAR(m->func_name); Py_CLEAR(m->func_qualname); Py_CLEAR(m->func_doc); Py_CLEAR(m->func_globals); Py_CLEAR(m->func_code); +#if !CYTHON_COMPILING_IN_LIMITED_API #if PY_VERSION_HEX < 0x030900B1 Py_CLEAR(__Pyx_CyFunction_GetClassObj(m)); #else @@ -612,6 +671,7 @@ __Pyx_CyFunction_clear(__pyx_CyFunctionObject *m) ((PyCMethodObject *) (m))->mm_class = NULL; Py_XDECREF(cls); } +#endif #endif Py_CLEAR(m->defaults_tuple); Py_CLEAR(m->defaults_kwdict); @@ -649,14 +709,20 @@ static void __Pyx_CyFunction_dealloc(__pyx_CyFunctionObject *m) static int __Pyx_CyFunction_traverse(__pyx_CyFunctionObject *m, visitproc visit, void *arg) { Py_VISIT(m->func_closure); +#if !CYTHON_COMPILING_IN_LIMITED_API Py_VISIT(((PyCFunctionObject*)m)->m_module); +#else + Py_VISIT(m->func); +#endif Py_VISIT(m->func_dict); Py_VISIT(m->func_name); Py_VISIT(m->func_qualname); Py_VISIT(m->func_doc); Py_VISIT(m->func_globals); Py_VISIT(m->func_code); +#if !CYTHON_COMPILING_IN_LIMITED_API Py_VISIT(__Pyx_CyFunction_GetClassObj(m)); +#endif Py_VISIT(m->defaults_tuple); Py_VISIT(m->defaults_kwdict); Py_VISIT(m->func_is_coroutine); @@ -686,11 +752,24 @@ __Pyx_CyFunction_repr(__pyx_CyFunctionObject *op) static PyObject * __Pyx_CyFunction_CallMethod(PyObject *func, PyObject *self, PyObject *arg, PyObject *kw) { // originally copied from PyCFunction_Call() in CPython's Objects/methodobject.c +#if !CYTHON_COMPILING_IN_LIMITED_API PyCFunctionObject* f = (PyCFunctionObject*)func; PyCFunction meth = f->m_ml->ml_meth; + int flags = f->m_ml->ml_flags; +#else + PyObject *f = ((__pyx_CyFunctionObject*)func)->func; + PyObject *py_name = NULL; + PyCFunction meth; + int flags; + meth = PyCFunction_GetFunction(f); + if (unlikely(!meth)) return NULL; + flags = PyCFunction_GetFlags(f); + if (unlikely(flags < 0)) return NULL; +#endif + Py_ssize_t size; - switch (f->m_ml->ml_flags & (METH_VARARGS | METH_KEYWORDS | METH_NOARGS | METH_O)) { + switch (flags & (METH_VARARGS | METH_KEYWORDS | METH_NOARGS | METH_O)) { case METH_VARARGS: if (likely(kw == NULL || PyDict_Size(kw) == 0)) return (*meth)(self, arg); @@ -699,24 +778,43 @@ static PyObject * __Pyx_CyFunction_CallMethod(PyObject *func, PyObject *self, Py return (*(PyCFunctionWithKeywords)(void*)meth)(self, arg, kw); case METH_NOARGS: if (likely(kw == NULL || PyDict_Size(kw) == 0)) { +#if CYTHON_ASSUME_SAFE_MACROS size = PyTuple_GET_SIZE(arg); +#else + size = PyTuple_Size(arg); + if (unlikely(size < 0)) return NULL; +#endif if (likely(size == 0)) return (*meth)(self, NULL); +#if !CYTHON_COMPILING_IN_LIMITED_API PyErr_Format(PyExc_TypeError, "%.200s() takes no arguments (%" CYTHON_FORMAT_SSIZE_T "d given)", f->m_ml->ml_name, size); +#else + py_name = __Pyx_CyFunction_get_name((__pyx_CyFunctionObject*)func, NULL); + if (!py_name) return NULL; + PyErr_Format(PyExc_TypeError, + "%.200S() takes no arguments (%" CYTHON_FORMAT_SSIZE_T "d given)", + py_name, size); + Py_DECREF(py_name); +#endif return NULL; } break; case METH_O: if (likely(kw == NULL || PyDict_Size(kw) == 0)) { +#if CYTHON_ASSUME_SAFE_MACROS size = PyTuple_GET_SIZE(arg); +#else + size = PyTuple_Size(arg); + if (unlikely(size < 0)) return NULL; +#endif if (likely(size == 1)) { PyObject *result, *arg0; #if CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS arg0 = PyTuple_GET_ITEM(arg, 0); #else - arg0 = PySequence_ITEM(arg, 0); if (unlikely(!arg0)) return NULL; + arg0 = __Pyx_PySequence_ITEM(arg, 0); if (unlikely(!arg0)) return NULL; #endif result = (*meth)(self, arg0); #if !(CYTHON_ASSUME_SAFE_MACROS && !CYTHON_AVOID_BORROWED_REFS) @@ -724,9 +822,19 @@ static PyObject * __Pyx_CyFunction_CallMethod(PyObject *func, PyObject *self, Py #endif return result; } +#if !CYTHON_COMPILING_IN_LIMITED_API PyErr_Format(PyExc_TypeError, "%.200s() takes exactly one argument (%" CYTHON_FORMAT_SSIZE_T "d given)", f->m_ml->ml_name, size); +#else + py_name = __Pyx_CyFunction_get_name((__pyx_CyFunctionObject*)func, NULL); + if (!py_name) return NULL; + PyErr_Format(PyExc_TypeError, + "%.200S() takes exactly one argument (%" CYTHON_FORMAT_SSIZE_T "d given)", + py_name, size); + Py_DECREF(py_name); +#endif + return NULL; } break; @@ -734,13 +842,32 @@ static PyObject * __Pyx_CyFunction_CallMethod(PyObject *func, PyObject *self, Py PyErr_SetString(PyExc_SystemError, "Bad call flags for CyFunction"); return NULL; } +#if !CYTHON_COMPILING_IN_LIMITED_API PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments", f->m_ml->ml_name); +#else + py_name = __Pyx_CyFunction_get_name((__pyx_CyFunctionObject*)func, NULL); + if (!py_name) return NULL; + PyErr_Format(PyExc_TypeError, "%.200S() takes no keyword arguments", + py_name); + Py_DECREF(py_name); +#endif return NULL; } static CYTHON_INLINE PyObject *__Pyx_CyFunction_Call(PyObject *func, PyObject *arg, PyObject *kw) { - return __Pyx_CyFunction_CallMethod(func, ((PyCFunctionObject*)func)->m_self, arg, kw); + PyObject *self, *result; +#if !CYTHON_COMPILING_IN_LIMITED_API + self = ((PyCFunctionObject*)func)->m_self; +#else + self = PyCFunction_GetSelf(((__pyx_CyFunctionObject*)func)->func); + if (unlikely(!self) && PyErr_Occurred()) return NULL; +#endif + result = __Pyx_CyFunction_CallMethod(func, self, arg, kw); +#if CYTHON_COMPILING_IN_LIMITED_API + Py_DECREF(self); +#endif + return result; } static PyObject *__Pyx_CyFunction_CallAsMethod(PyObject *func, PyObject *args, PyObject *kw) { @@ -767,7 +894,12 @@ static PyObject *__Pyx_CyFunction_CallAsMethod(PyObject *func, PyObject *args, P PyObject *new_args; PyObject *self; +#if CYTHON_ASSUME_SAFE_MACROS argc = PyTuple_GET_SIZE(args); +#else + argc = PyTuple_Size(args); + if (unlikely(!argc) < 0) return NULL; +#endif new_args = PyTuple_GetSlice(args, 1, argc); if (unlikely(!new_args)) @@ -1125,7 +1257,6 @@ static PyObject *__Pyx_CyFunction_New(PyMethodDef *ml, int flags, PyObject* qual return op; } - //////////////////// CyFunctionClassCell.proto //////////////////// static int __Pyx_CyFunction_InitClassCell(PyObject *cyfunctions, PyObject *classobj);/*proto*/ diff --git a/Cython/Utility/ModuleSetupCode.c b/Cython/Utility/ModuleSetupCode.c index f6e6b9b0251..82008a1e252 100644 --- a/Cython/Utility/ModuleSetupCode.c +++ b/Cython/Utility/ModuleSetupCode.c @@ -822,7 +822,7 @@ class __Pyx_FakeReference { #endif // PEP-573: PyCFunction holds reference to defining class (PyCMethodObject) -#if PY_VERSION_HEX < 0x030900B1 +#if __PYX_LIMITED_VERSION_HEX < 0x030900B1 #define __Pyx_PyType_FromModuleAndSpec(m, s, b) ((void)m, PyType_FromSpecWithBases(s, b)) typedef PyObject *(*__Pyx_PyCMethod)(PyObject *, PyTypeObject *, PyObject *const *, size_t, PyObject *); #else @@ -1167,6 +1167,12 @@ static CYTHON_INLINE PyObject * __Pyx_PyDict_GetItemStrWithError(PyObject *dict, #define __Pyx_PySequence_SIZE(seq) PySequence_Size(seq) #endif +#if CYTHON_COMPILING_IN_LIMITED_API + #define __Pyx_PySequence_ITEM(o, i) PySequence_GetItem(o, i) +#else + #define __Pyx_PySequence_ITEM(o, i) PySequence_ITEM(o, i) +#endif + #if PY_MAJOR_VERSION >= 3 #define PyIntObject PyLongObject #define PyInt_Type PyLong_Type From 5b743a1e784f90d31d6c8a81bbdef908d8652442 Mon Sep 17 00:00:00 2001 From: da-woods Date: Sun, 23 Jul 2023 08:33:30 +0100 Subject: [PATCH 2/6] Switch orders --- Cython/Utility/CythonFunction.c | 86 ++++++++++++++++----------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index 958bb824900..2b42bc526db 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -28,17 +28,17 @@ typedef struct { -#if !CYTHON_COMPILING_IN_LIMITED_API +#if CYTHON_COMPILING_IN_LIMITED_API + PyObject_HEAD; + // We can't "inherit" from func, but we can use it as a data store + PyObject *func; +#else #if PY_VERSION_HEX < 0x030900B1 PyCFunctionObject func; #else // PEP-573: PyCFunctionObject + mm_class PyCMethodObject func; #endif -#else - PyObject_HEAD; - // We can't "inherit" from func, but we can use it as a data store - PyObject *func; #endif #if CYTHON_BACKPORT_VECTORCALL __pyx_vectorcallfunc func_vectorcall; @@ -135,7 +135,10 @@ __Pyx_CyFunction_get_doc(__pyx_CyFunctionObject *op, void *closure) { CYTHON_UNUSED_VAR(closure); if (unlikely(op->func_doc == NULL)) { -#if !CYTHON_COMPILING_IN_LIMITED_API +#if CYTHON_COMPILING_IN_LIMITED_API + op->func_doc = PyObject_GetAttrString(op->func, "__doc__"); + if (unlikely(!op->func_doc)) return NULL; +#else if (((PyCFunctionObject*)op)->m_ml->ml_doc) { #if PY_MAJOR_VERSION >= 3 op->func_doc = PyUnicode_FromString(((PyCFunctionObject*)op)->m_ml->ml_doc); @@ -148,10 +151,7 @@ __Pyx_CyFunction_get_doc(__pyx_CyFunctionObject *op, void *closure) Py_INCREF(Py_None); return Py_None; } -#else // CYTHON_COMPILING_IN_LIMITED_API - op->func_doc = PyObject_GetAttrString(op->func, "__doc__"); - if (unlikely(!op->func_doc)) return NULL; -#endif +#endif /* CYTHON_COMPILING_IN_LIMITED_API */ } Py_INCREF(op->func_doc); return op->func_doc; @@ -175,15 +175,15 @@ __Pyx_CyFunction_get_name(__pyx_CyFunctionObject *op, void *context) { CYTHON_UNUSED_VAR(context); if (unlikely(op->func_name == NULL)) { -#if !CYTHON_COMPILING_IN_LIMITED_API +#if CYTHON_COMPILING_IN_LIMITED_API + op->func_name = PyObject_GetAttrString(op->func, "__name__"); +#else #if PY_MAJOR_VERSION >= 3 op->func_name = PyUnicode_InternFromString(((PyCFunctionObject*)op)->m_ml->ml_name); #else op->func_name = PyString_InternFromString(((PyCFunctionObject*)op)->m_ml->ml_name); #endif -#else // CYTHON_COMPILING_IN_LIMITED_API - op->func_name = PyObject_GetAttrString(op->func, "__name__"); -#endif +#endif /* CYTHON_COMPILING_IN_LIMITED_API */ if (unlikely(op->func_name == NULL)) return NULL; } @@ -601,9 +601,9 @@ static PyObject *__Pyx_CyFunction_Init(__pyx_CyFunctionObject *op, PyMethodDef * Py_INCREF(qualname); op->func_qualname = qualname; op->func_doc = NULL; -#if PY_VERSION_HEX < 0x030900B1 +#if PY_VERSION_HEX < 0x030900B1 || CYTHON_COMPILING_IN_LIMITED_API op->func_classobj = NULL; -#elif !CYTHON_COMPILING_IN_LIMITED_API +#else ((PyCMethodObject*)op)->mm_class = NULL; #endif op->func_globals = globals; @@ -651,10 +651,10 @@ static int __Pyx_CyFunction_clear(__pyx_CyFunctionObject *m) { Py_CLEAR(m->func_closure); -#if !CYTHON_COMPILING_IN_LIMITED_API - Py_CLEAR(((PyCFunctionObject*)m)->m_module); -#else +#if CYTHON_COMPILING_IN_LIMITED_API Py_CLEAR(m->func); +#else + Py_CLEAR(((PyCFunctionObject*)m)->m_module); #endif Py_CLEAR(m->func_dict); Py_CLEAR(m->func_name); @@ -709,10 +709,10 @@ static void __Pyx_CyFunction_dealloc(__pyx_CyFunctionObject *m) static int __Pyx_CyFunction_traverse(__pyx_CyFunctionObject *m, visitproc visit, void *arg) { Py_VISIT(m->func_closure); -#if !CYTHON_COMPILING_IN_LIMITED_API - Py_VISIT(((PyCFunctionObject*)m)->m_module); -#else +#if CYTHON_COMPILING_IN_LIMITED_API Py_VISIT(m->func); +#else + Py_VISIT(((PyCFunctionObject*)m)->m_module); #endif Py_VISIT(m->func_dict); Py_VISIT(m->func_name); @@ -753,10 +753,6 @@ __Pyx_CyFunction_repr(__pyx_CyFunctionObject *op) static PyObject * __Pyx_CyFunction_CallMethod(PyObject *func, PyObject *self, PyObject *arg, PyObject *kw) { // originally copied from PyCFunction_Call() in CPython's Objects/methodobject.c #if !CYTHON_COMPILING_IN_LIMITED_API - PyCFunctionObject* f = (PyCFunctionObject*)func; - PyCFunction meth = f->m_ml->ml_meth; - int flags = f->m_ml->ml_flags; -#else PyObject *f = ((__pyx_CyFunctionObject*)func)->func; PyObject *py_name = NULL; PyCFunction meth; @@ -765,6 +761,10 @@ static PyObject * __Pyx_CyFunction_CallMethod(PyObject *func, PyObject *self, Py if (unlikely(!meth)) return NULL; flags = PyCFunction_GetFlags(f); if (unlikely(flags < 0)) return NULL; +#else + PyCFunctionObject* f = (PyCFunctionObject*)func; + PyCFunction meth = f->m_ml->ml_meth; + int flags = f->m_ml->ml_flags; #endif Py_ssize_t size; @@ -786,17 +786,17 @@ static PyObject * __Pyx_CyFunction_CallMethod(PyObject *func, PyObject *self, Py #endif if (likely(size == 0)) return (*meth)(self, NULL); -#if !CYTHON_COMPILING_IN_LIMITED_API - PyErr_Format(PyExc_TypeError, - "%.200s() takes no arguments (%" CYTHON_FORMAT_SSIZE_T "d given)", - f->m_ml->ml_name, size); -#else +#if CYTHON_COMPILING_IN_LIMITED_API py_name = __Pyx_CyFunction_get_name((__pyx_CyFunctionObject*)func, NULL); if (!py_name) return NULL; PyErr_Format(PyExc_TypeError, "%.200S() takes no arguments (%" CYTHON_FORMAT_SSIZE_T "d given)", py_name, size); Py_DECREF(py_name); +#else + PyErr_Format(PyExc_TypeError, + "%.200s() takes no arguments (%" CYTHON_FORMAT_SSIZE_T "d given)", + f->m_ml->ml_name, size); #endif return NULL; } @@ -822,17 +822,17 @@ static PyObject * __Pyx_CyFunction_CallMethod(PyObject *func, PyObject *self, Py #endif return result; } -#if !CYTHON_COMPILING_IN_LIMITED_API - PyErr_Format(PyExc_TypeError, - "%.200s() takes exactly one argument (%" CYTHON_FORMAT_SSIZE_T "d given)", - f->m_ml->ml_name, size); -#else +#if CYTHON_COMPILING_IN_LIMITED_API py_name = __Pyx_CyFunction_get_name((__pyx_CyFunctionObject*)func, NULL); if (!py_name) return NULL; PyErr_Format(PyExc_TypeError, "%.200S() takes exactly one argument (%" CYTHON_FORMAT_SSIZE_T "d given)", py_name, size); Py_DECREF(py_name); +#else + PyErr_Format(PyExc_TypeError, + "%.200s() takes exactly one argument (%" CYTHON_FORMAT_SSIZE_T "d given)", + f->m_ml->ml_name, size); #endif return NULL; @@ -842,26 +842,26 @@ static PyObject * __Pyx_CyFunction_CallMethod(PyObject *func, PyObject *self, Py PyErr_SetString(PyExc_SystemError, "Bad call flags for CyFunction"); return NULL; } -#if !CYTHON_COMPILING_IN_LIMITED_API - PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments", - f->m_ml->ml_name); -#else +#if CYTHON_COMPILING_IN_LIMITED_API py_name = __Pyx_CyFunction_get_name((__pyx_CyFunctionObject*)func, NULL); if (!py_name) return NULL; PyErr_Format(PyExc_TypeError, "%.200S() takes no keyword arguments", py_name); Py_DECREF(py_name); +#else + PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments", + f->m_ml->ml_name); #endif return NULL; } static CYTHON_INLINE PyObject *__Pyx_CyFunction_Call(PyObject *func, PyObject *arg, PyObject *kw) { PyObject *self, *result; -#if !CYTHON_COMPILING_IN_LIMITED_API - self = ((PyCFunctionObject*)func)->m_self; -#else +#if CYTHON_COMPILING_IN_LIMITED_API self = PyCFunction_GetSelf(((__pyx_CyFunctionObject*)func)->func); if (unlikely(!self) && PyErr_Occurred()) return NULL; +#else + self = ((PyCFunctionObject*)func)->m_self; #endif result = __Pyx_CyFunction_CallMethod(func, self, arg, kw); #if CYTHON_COMPILING_IN_LIMITED_API From 0a3163c22081ef511b8df6d9ba6a1771067d2cb6 Mon Sep 17 00:00:00 2001 From: da-woods Date: Sun, 23 Jul 2023 08:49:16 +0100 Subject: [PATCH 3/6] Fixup --- Cython/Utility/CythonFunction.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index 2b42bc526db..24b6ebe012c 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -752,7 +752,7 @@ __Pyx_CyFunction_repr(__pyx_CyFunctionObject *op) static PyObject * __Pyx_CyFunction_CallMethod(PyObject *func, PyObject *self, PyObject *arg, PyObject *kw) { // originally copied from PyCFunction_Call() in CPython's Objects/methodobject.c -#if !CYTHON_COMPILING_IN_LIMITED_API +#if CYTHON_COMPILING_IN_LIMITED_API PyObject *f = ((__pyx_CyFunctionObject*)func)->func; PyObject *py_name = NULL; PyCFunction meth; From 7fdefa2294ef0b2ccdddd72b10c99c68d6ed2ca4 Mon Sep 17 00:00:00 2001 From: scoder Date: Sun, 23 Jul 2023 11:04:35 +0200 Subject: [PATCH 4/6] Merge conditions --- Cython/Utility/CythonFunction.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index 24b6ebe012c..5f1a03090ef 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -32,14 +32,12 @@ typedef struct { PyObject_HEAD; // We can't "inherit" from func, but we can use it as a data store PyObject *func; -#else -#if PY_VERSION_HEX < 0x030900B1 +#elif PY_VERSION_HEX < 0x030900B1 PyCFunctionObject func; #else // PEP-573: PyCFunctionObject + mm_class PyCMethodObject func; #endif -#endif #if CYTHON_BACKPORT_VECTORCALL __pyx_vectorcallfunc func_vectorcall; #endif @@ -177,12 +175,10 @@ __Pyx_CyFunction_get_name(__pyx_CyFunctionObject *op, void *context) if (unlikely(op->func_name == NULL)) { #if CYTHON_COMPILING_IN_LIMITED_API op->func_name = PyObject_GetAttrString(op->func, "__name__"); -#else -#if PY_MAJOR_VERSION >= 3 +#elif PY_MAJOR_VERSION >= 3 op->func_name = PyUnicode_InternFromString(((PyCFunctionObject*)op)->m_ml->ml_name); #else op->func_name = PyString_InternFromString(((PyCFunctionObject*)op)->m_ml->ml_name); -#endif #endif /* CYTHON_COMPILING_IN_LIMITED_API */ if (unlikely(op->func_name == NULL)) return NULL; From a7262dcdbf7c0978275e9d0de26e3c9d7022142c Mon Sep 17 00:00:00 2001 From: da-woods Date: Sun, 23 Jul 2023 14:16:21 +0100 Subject: [PATCH 5/6] Initialize weakref --- Cython/Utility/CythonFunction.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index 5f1a03090ef..1bcf311ffc7 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -581,8 +581,8 @@ static PyObject *__Pyx_CyFunction_Init(__pyx_CyFunctionObject *op, PyMethodDef * if (unlikely(!op->func)) return NULL; #endif op->flags = flags; -#if !CYTHON_COMPILING_IN_LIMITED_API __Pyx_CyFunction_weakreflist(op) = NULL; +#if !CYTHON_COMPILING_IN_LIMITED_API cf->m_ml = ml; cf->m_self = (PyObject *) op; #endif From ef209f8baf9d8df2f6b4667b192c6631c8ca686c Mon Sep 17 00:00:00 2001 From: da-woods Date: Sun, 23 Jul 2023 16:42:58 +0100 Subject: [PATCH 6/6] Fix reference counting mistake --- Cython/Utility/CythonFunction.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cython/Utility/CythonFunction.c b/Cython/Utility/CythonFunction.c index 1bcf311ffc7..cf36608f505 100644 --- a/Cython/Utility/CythonFunction.c +++ b/Cython/Utility/CythonFunction.c @@ -577,6 +577,8 @@ static PyObject *__Pyx_CyFunction_Init(__pyx_CyFunctionObject *op, PyMethodDef * if (unlikely(op == NULL)) return NULL; #if CYTHON_COMPILING_IN_LIMITED_API + // Note that we end up with a circular reference to op. This isn't + // a disaster, but in an ideal world it'd be nice to avoid it. op->func = PyCFunction_NewEx(ml, (PyObject*)op, module); if (unlikely(!op->func)) return NULL; #endif @@ -854,15 +856,13 @@ static PyObject * __Pyx_CyFunction_CallMethod(PyObject *func, PyObject *self, Py static CYTHON_INLINE PyObject *__Pyx_CyFunction_Call(PyObject *func, PyObject *arg, PyObject *kw) { PyObject *self, *result; #if CYTHON_COMPILING_IN_LIMITED_API + // PyCFunction_GetSelf returns a borrowed reference self = PyCFunction_GetSelf(((__pyx_CyFunctionObject*)func)->func); if (unlikely(!self) && PyErr_Occurred()) return NULL; #else self = ((PyCFunctionObject*)func)->m_self; #endif result = __Pyx_CyFunction_CallMethod(func, self, arg, kw); -#if CYTHON_COMPILING_IN_LIMITED_API - Py_DECREF(self); -#endif return result; }