Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Clean up special handling of PyCFunction and CyFunction #5739

Merged
merged 6 commits into from Oct 4, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 2 additions & 7 deletions Cython/Compiler/Nodes.py
Expand Up @@ -4969,14 +4969,9 @@ def generate_execution_code(self, code):
func_node_temp, self_arg, interned_attr_cname, err))
code.put_gotref(func_node_temp, py_object_type)

is_overridden = "(PyCFunction_GET_FUNCTION(%s) != (PyCFunction)(void*)%s)" % (
is_overridden = "(__Pyx_PyCFunction_GET_FUNCTION(%s) != (PyCFunction)(void*)%s)" % (
func_node_temp, method_entry.func_cname)
code.putln("#ifdef __Pyx_CyFunction_USED")
code.putln("if (!__Pyx_IsCyOrPyCFunction(%s)" % func_node_temp)
code.putln("#else")
code.putln("if (!PyCFunction_Check(%s)" % func_node_temp)
code.putln("#endif")
code.putln(" || %s) {" % is_overridden)
code.putln("if (!__Pyx_PyCFunction_Check(%s) || %s) {" % (func_node_temp, is_overridden))
self.body.generate_execution_code(code)
code.putln("}")

Expand Down
16 changes: 16 additions & 0 deletions Cython/Utility/CythonFunction.c
Expand Up @@ -75,6 +75,22 @@ typedef struct {
#define __Pyx_IsCyOrPyCFunction(obj) __Pyx_TypeCheck2(obj, __pyx_CyFunctionType, &PyCFunction_Type)
#define __Pyx_CyFunction_CheckExact(obj) __Pyx_IS_TYPE(obj, __pyx_CyFunctionType)

// Redefine PyCFunction access functions to also support CyFunction.
// We need our own copies because the inline functions in CPython have a type-check assert
// that breaks with a CyFunction in debug mode.
#undef __Pyx_PyCFunction_Check
#define __Pyx_PyCFunction_Check(func) __Pyx_IsCyOrPyCFunction(func)
scoder marked this conversation as resolved.
Show resolved Hide resolved
#if CYTHON_COMPILING_IN_CPYTHON
#undef __Pyx_PyCFunction_GET_FLAGS
#undef __Pyx_PyCFunction_GET_FUNCTION
#undef __Pyx_PyCFunction_GET_SELF
#define __Pyx_PyCFunction_GET_FLAGS(func) (((PyCFunctionObject*)(func))->m_ml->ml_flags)
#define __Pyx_PyCFunction_GET_FUNCTION(func) (((PyCFunctionObject*)(func))->m_ml->ml_meth)
static CYTHON_INLINE PyObject* __Pyx_PyCFunction_GET_SELF(PyObject *func) {
return (unlikely(__Pyx_PyCFunction_GET_FLAGS(func) & METH_STATIC)) ? NULL : ((PyCFunctionObject*)func)->m_self;
}
#endif

static PyObject *__Pyx_CyFunction_Init(__pyx_CyFunctionObject* op, PyMethodDef *ml,
int flags, PyObject* qualname,
PyObject *closure,
Expand Down
19 changes: 19 additions & 0 deletions Cython/Utility/ModuleSetupCode.c
Expand Up @@ -898,6 +898,25 @@ class __Pyx_FakeReference {
#define __Pyx_PyVectorcall_NARGS(n) ((Py_ssize_t)(n))
#endif

// These PyCFunction related macros get redefined in CythonFunction.c.
// We need our own copies because the inline functions in CPython have a type-check assert
// that breaks with a CyFunction in debug mode.
#if PY_MAJOR_VERSION >= 0x030900B1
#define __Pyx_PyCFunction_CheckExact(func) PyCFunction_CheckExact(func)
#else
#define __Pyx_PyCFunction_CheckExact(func) PyCFunction_Check(func)
#endif
#define __Pyx_PyCFunction_Check(func) PyCFunction_Check(func)
#if CYTHON_COMPILING_IN_LIMITED_API
#define __Pyx_PyCFunction_GET_FLAGS(func) PyCFunction_GetFlags(func)
#define __Pyx_PyCFunction_GET_FUNCTION(func) PyCFunction_GetFunction(func)
#define __Pyx_PyCFunction_GET_SELF(func) PyCFunction_GetSelf(func)
#else
#define __Pyx_PyCFunction_GET_FLAGS(func) PyCFunction_GET_FLAGS(func)
#define __Pyx_PyCFunction_GET_FUNCTION(func) PyCFunction_GET_FUNCTION(func)
#define __Pyx_PyCFunction_GET_SELF(func) PyCFunction_GET_SELF(func)
#endif

// PEP-573: PyCFunction holds reference to defining class (PyCMethodObject)
#if __PYX_LIMITED_VERSION_HEX < 0x030900B1
#define __Pyx_PyType_FromModuleAndSpec(m, s, b) ((void)m, PyType_FromSpecWithBases(s, b))
Expand Down
38 changes: 10 additions & 28 deletions Cython/Utility/ObjectHandling.c
Expand Up @@ -2017,7 +2017,7 @@ static int __Pyx_TryUnpackUnboundCMethod(__Pyx_CachedCFunction* target) {
// method descriptor type isn't exported in Py2.x, cannot easily check the type there.
// Therefore, reverse the check to the most likely alternative
// (which is returned for class methods)
if (likely(!PyCFunction_Check(method)))
if (likely(!__Pyx_PyCFunction_Check(method)))
#endif
{
PyMethodDescrObject *descr = (PyMethodDescrObject*) method;
Expand All @@ -2026,13 +2026,10 @@ static int __Pyx_TryUnpackUnboundCMethod(__Pyx_CachedCFunction* target) {
} else
#endif
// bound classmethods need special treatment
#if defined(CYTHON_COMPILING_IN_PYPY)
// In PyPy functions are regular methods, so just do
// the self check
#elif PY_VERSION_HEX >= 0x03090000
if (PyCFunction_CheckExact(method))
#if CYTHON_COMPILING_IN_PYPY
// In PyPy, functions are regular methods, so just do the self check.
#else
if (PyCFunction_Check(method))
if (__Pyx_PyCFunction_CheckExact(method))
#endif
{
PyObject *self;
Expand Down Expand Up @@ -2284,27 +2281,12 @@ static CYTHON_INLINE PyObject* __Pyx_PyObject_FastCallDict(PyObject *func, PyObj
Py_ssize_t nargs = __Pyx_PyVectorcall_NARGS(_nargs);
#if CYTHON_COMPILING_IN_CPYTHON
if (nargs == 0 && kwargs == NULL) {
#if defined(__Pyx_CyFunction_USED) && defined(NDEBUG)
// TODO PyCFunction_GET_FLAGS has a type-check assert that breaks with a CyFunction
// in debug mode. There is likely to be a better way of avoiding tripping this
// check that doesn't involve disabling the optimized path.
if (__Pyx_IsCyOrPyCFunction(func))
#else
if (PyCFunction_Check(func))
#endif
{
if (likely(PyCFunction_GET_FLAGS(func) & METH_NOARGS)) {
return __Pyx_PyObject_CallMethO(func, NULL);
}
}
if (__Pyx_PyCFunction_Check(func) && likely(__Pyx_PyCFunction_GET_FLAGS(func) & METH_NOARGS))
return __Pyx_PyObject_CallMethO(func, NULL);
}
else if (nargs == 1 && kwargs == NULL) {
if (PyCFunction_Check(func))
{
if (likely(PyCFunction_GET_FLAGS(func) & METH_O)) {
return __Pyx_PyObject_CallMethO(func, args[0]);
}
}
if (__Pyx_PyCFunction_Check(func) && likely(__Pyx_PyCFunction_GET_FLAGS(func) & METH_O))
return __Pyx_PyObject_CallMethO(func, args[0]);
}
#endif

Expand Down Expand Up @@ -2465,8 +2447,8 @@ static CYTHON_INLINE PyObject* __Pyx_PyObject_CallMethO(PyObject *func, PyObject
static CYTHON_INLINE PyObject* __Pyx_PyObject_CallMethO(PyObject *func, PyObject *arg) {
PyObject *self, *result;
PyCFunction cfunc;
cfunc = PyCFunction_GET_FUNCTION(func);
self = PyCFunction_GET_SELF(func);
cfunc = __Pyx_PyCFunction_GET_FUNCTION(func);
self = __Pyx_PyCFunction_GET_SELF(func);

if (unlikely(Py_EnterRecursiveCall((char*)" while calling a Python object")))
return NULL;
Expand Down