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

Avoid unnecessary binding of fused functions on class lookup (GH-4370)

Among other things this makes it pickleable by ensuring that it's the same object each time.
parent e2a23fe1
......@@ -1227,6 +1227,15 @@ __pyx_FusedFunction_descr_get(PyObject *self, PyObject *obj, PyObject *type)
if (obj == Py_None)
obj = NULL;
if (func->func.flags & __Pyx_CYFUNCTION_CLASSMETHOD)
obj = type;
if (obj == NULL) {
// We aren't actually binding to anything, save the effort of rebinding
Py_INCREF(self);
return self;
}
meth = (__pyx_FusedFunctionObject *) __pyx_FusedFunction_New(
((PyCFunctionObject *) func)->m_ml,
((__pyx_CyFunctionObject *) func)->flags,
......@@ -1267,9 +1276,6 @@ __pyx_FusedFunction_descr_get(PyObject *self, PyObject *obj, PyObject *type)
Py_XINCREF(func->func.defaults_tuple);
meth->func.defaults_tuple = func->func.defaults_tuple;
if (func->func.flags & __Pyx_CYFUNCTION_CLASSMETHOD)
obj = type;
Py_XINCREF(obj);
meth->self = obj;
......
......@@ -36,6 +36,10 @@ def regular_func(x):
def regular_func_0():
return
@classmethod
def fused_classmethod_free(cls, x: IntOrFloat):
return (cls.__name__, type(x).__name__)
@cython.cclass
class Cdef:
__doc__ = """
......@@ -62,6 +66,20 @@ class Cdef:
>>> c.regular_func_0() # doctest: +ELLIPSIS
Traceback (most recent call last):
TypeError: regular_func_0() takes ... arguments ...1... given...
# Looking up a class attribute doesn't go through all of __get__
>>> Cdef.fused_in_class is Cdef.fused_in_class
True
# looking up a classmethod does go through __get__ though
>>> Cdef.fused_classmethod is Cdef.fused_classmethod
False
>>> Cdef.fused_classmethod_free is Cdef.fused_classmethod_free
False
>>> Cdef.fused_classmethod(1)
('Cdef', 'int')
>>> Cdef.fused_classmethod_free(1)
('Cdef', 'int')
""".format(typeofCdef = 'Python object' if cython.compiled else 'Cdef')
if cython.compiled:
......@@ -78,18 +96,29 @@ class Cdef:
>>> c.fused_func_0['float']() # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
TypeError: (Exception looks quite different in Python2 and 3 so no way to match both)
>>> Cdef.fused_classmethod['float'] is Cdef.fused_classmethod['float']
False
>>> Cdef.fused_classmethod_free['float'] is Cdef.fused_classmethod_free['float']
False
"""
fused_func = fused_func
fused_func_0 = fused_func_0
regular_func = regular_func
regular_func_0 = regular_func_0
fused_classmethod_free = fused_classmethod_free
def fused_in_class(self, x: MyFusedClass):
return (type(x).__name__, cython.typeof(x))
def regular_in_class(self):
return type(self).__name__
@classmethod
def fused_classmethod(cls, x: IntOrFloat):
return (cls.__name__, type(x).__name__)
class Regular(object):
__doc__ = """
>>> c = Regular()
......@@ -111,6 +140,20 @@ class Regular(object):
>>> c.regular_func_0() # doctest: +ELLIPSIS
Traceback (most recent call last):
TypeError: regular_func_0() takes ... arguments ...1... given...
# Looking up a class attribute doesn't go through all of __get__
>>> Regular.fused_func is Regular.fused_func
True
# looking up a classmethod does go __get__ though
>>> Regular.fused_classmethod is Regular.fused_classmethod
False
>>> Regular.fused_classmethod_free is Regular.fused_classmethod_free
False
>>> Regular.fused_classmethod(1)
('Regular', 'int')
>>> Regular.fused_classmethod_free(1)
('Regular', 'int')
""".format(typeofRegular = "Python object" if cython.compiled else 'Regular')
if cython.compiled:
__doc__ += """
......@@ -125,6 +168,11 @@ class Regular(object):
TypeError: (Exception looks quite different in Python2 and 3 so no way to match both)
>>> Regular.fused_func_0['float']()
('float', 'float')
>>> Regular.fused_classmethod['float'] is Regular.fused_classmethod['float']
False
>>> Regular.fused_classmethod_free['float'] is Regular.fused_classmethod_free['float']
False
"""
fused_func = fused_func
......@@ -132,6 +180,12 @@ class Regular(object):
regular_func = regular_func
regular_func_0 = regular_func_0
fused_classmethod_free = fused_classmethod_free
@classmethod
def fused_classmethod(cls, x: IntOrFloat):
return (cls.__name__, type(x).__name__)
import sys
if sys.version_info[0] > 2:
# extra Py3 only tests - shows that functions added to a class can be called
......
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