Commit 03278313 authored by da-woods's avatar da-woods Committed by GitHub

Remove incorrect CyFunction __self__ attribute (GH-4051)

The __self__ argument should be present only for bound functions.
Fixes https://github.com/cython/cython/issues/4036

Currently it isn't easily possible to get this working absolutely correctly for fused functions. I raise an attribute error but hasattr still returns True.

Additionally, Python 2 methods always have a __self__ attribute but set it to None. I follow Python 3 behaviour and don't have the attribute.
parent 3c34c40f
......@@ -192,18 +192,6 @@ __Pyx_CyFunction_set_qualname(__pyx_CyFunctionObject *op, PyObject *value, CYTHO
return 0;
}
static PyObject *
__Pyx_CyFunction_get_self(__pyx_CyFunctionObject *m, CYTHON_UNUSED void *closure)
{
PyObject *self;
self = m->func_closure;
if (self == NULL)
self = Py_None;
Py_INCREF(self);
return self;
}
static PyObject *
__Pyx_CyFunction_get_dict(__pyx_CyFunctionObject *op, CYTHON_UNUSED void *context)
{
......@@ -429,7 +417,6 @@ static PyGetSetDef __pyx_CyFunction_getsets[] = {
{(char *) "func_name", (getter)__Pyx_CyFunction_get_name, (setter)__Pyx_CyFunction_set_name, 0, 0},
{(char *) "__name__", (getter)__Pyx_CyFunction_get_name, (setter)__Pyx_CyFunction_set_name, 0, 0},
{(char *) "__qualname__", (getter)__Pyx_CyFunction_get_qualname, (setter)__Pyx_CyFunction_set_qualname, 0, 0},
{(char *) "__self__", (getter)__Pyx_CyFunction_get_self, 0, 0, 0},
{(char *) "func_dict", (getter)__Pyx_CyFunction_get_dict, (setter)__Pyx_CyFunction_set_dict, 0, 0},
{(char *) "__dict__", (getter)__Pyx_CyFunction_get_dict, (setter)__Pyx_CyFunction_set_dict, 0, 0},
{(char *) "func_globals", (getter)__Pyx_CyFunction_get_globals, 0, 0, 0},
......@@ -1360,6 +1347,18 @@ bad:
return result;
}
static PyObject *
__Pyx_FusedFunction_get_self(__pyx_FusedFunctionObject *m, CYTHON_UNUSED void *closure)
{
PyObject *self = m->self;
if (unlikely(!self)) {
PyErr_SetString(PyExc_AttributeError, "'function' object has no attribute '__self__'");
} else {
Py_INCREF(self);
}
return self;
}
static PyMemberDef __pyx_FusedFunction_members[] = {
{(char *) "__signatures__",
T_OBJECT,
......@@ -1369,6 +1368,15 @@ static PyMemberDef __pyx_FusedFunction_members[] = {
{0, 0, 0, 0, 0},
};
static PyGetSetDef __pyx_FusedFunction_getsets[] = {
{(char *) "__self__", (getter)__Pyx_FusedFunction_get_self, 0, 0, 0},
// __doc__ is None for the fused function type, but we need it to be
// a descriptor for the instance's __doc__, so rebuild the descriptor in our subclass
// (all other descriptors are inherited)
{(char *) "__doc__", (getter)__Pyx_CyFunction_get_doc, (setter)__Pyx_CyFunction_set_doc, 0, 0},
{0, 0, 0, 0, 0}
};
#if CYTHON_COMPILING_IN_LIMITED_API
static PyType_Slot __pyx_FusedFunctionType_slots[] = {
{Py_tp_dealloc, (void *)__pyx_FusedFunction_dealloc},
......@@ -1376,7 +1384,7 @@ static PyType_Slot __pyx_FusedFunctionType_slots[] = {
{Py_tp_traverse, (void *)__pyx_FusedFunction_traverse},
{Py_tp_clear, (void *)__pyx_FusedFunction_clear},
{Py_tp_members, (void *)__pyx_FusedFunction_members},
{Py_tp_getset, (void *)__pyx_CyFunction_getsets},
{Py_tp_getset, (void *)__pyx_FusedFunction_getsets},
{Py_tp_descr_get, (void *)__pyx_FusedFunction_descr_get},
{Py_mp_subscript, (void *)__pyx_FusedFunction_getitem},
{0, 0},
......@@ -1432,9 +1440,7 @@ static PyTypeObject __pyx_FusedFunctionType_type = {
0, /*tp_iternext*/
0, /*tp_methods*/
__pyx_FusedFunction_members, /*tp_members*/
// __doc__ is None for the fused function type, but we need it to be
// a descriptor for the instance's __doc__, so rebuild descriptors in our subclass
__pyx_CyFunction_getsets, /*tp_getset*/
__pyx_FusedFunction_getsets, /*tp_getset*/
// NOTE: tp_base may be changed later during module initialisation when importing CyFunction across modules.
&__pyx_CyFunctionType_type, /*tp_base*/
0, /*tp_dict*/
......
# mode: run
# tag: pure2.7
# cython: binding=True
import cython
import sys
def regular(x):
"""
>>> hasattr(regular, "__self__")
False
>>> nested = regular(10)
>>> hasattr(nested, "__self__")
False
"""
def nested(y):
return x+y
return nested
@cython.locals(x=cython.floating)
def fused(x):
"""
>>> nested = fused(10.)
>>> hasattr(nested, "__self__")
False
#>>> hasattr(fused, "__self__") # FIXME this fails for fused functions
#False
>>> fused.__self__ # but this is OK
Traceback (most recent call last):
...
AttributeError: 'function' object has no attribute '__self__'
"""
def nested_in_fused(y):
return x+y
return nested_in_fused
# FIXME - doesn't currently work at all
#def get_nested_fused(x):
# @cython.locals(x=cython.floating)
# def nested_fused(y):
# return x+y
# return nested_fused
class C:
"""
>>> c = C()
>>> c.regular.__self__ is c
True
>>> c.fused.__self__ is c
True
"""
def regular(self):
pass
@cython.locals(x=cython.floating)
def fused(self, x):
return x
__doc__ = ""
if sys.version_info[0] > 2 or cython.compiled:
__doc__ += """
>>> hasattr(C.regular, "__self__") # __self__==None on pure-python 2
False
>>> C.fused.__self__ # returns None on pure-python 2
Traceback (most recent call last):
...
AttributeError: 'function' object has no attribute '__self__'
"""
if cython.compiled:
__doc__ = """
>>> fused['double'].__self__
Traceback (most recent call last):
...
AttributeError: 'function' object has no attribute '__self__'
>>> C.fused['double'].__self__
Traceback (most recent call last):
...
AttributeError: 'function' object has no attribute '__self__'
>>> c = C()
>>> c.fused['double'].__self__ is c
True
# The PR that changed __self__ also changed how __doc__ is set up slightly
>>> fused['double'].__doc__ == fused.__doc__ and isinstance(fused.__doc__, str)
True
"""
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment