# HG changeset patch # Parent 8b79603699e4968c08fb83432faa11a6b8f86296 diff -r 8b79603699e4 Include/descrobject.h --- a/Include/descrobject.h Wed Jul 18 00:02:56 2012 +0300 +++ b/Include/descrobject.h Thu Jul 19 19:39:57 2012 +0300 @@ -69,6 +69,7 @@ PyDescr_COMMON; struct wrapperbase *d_base; void *d_wrapped; /* This can be any function pointer */ + PyObject *d_module; } PyWrapperDescrObject; #endif /* Py_LIMITED_API */ diff -r 8b79603699e4 Include/methodobject.h --- a/Include/methodobject.h Wed Jul 18 00:02:56 2012 +0300 +++ b/Include/methodobject.h Thu Jul 19 19:39:57 2012 +0300 @@ -46,9 +46,12 @@ }; typedef struct PyMethodDef PyMethodDef; -#define PyCFunction_New(ML, SELF) PyCFunction_NewEx((ML), (SELF), NULL) -PyAPI_FUNC(PyObject *) PyCFunction_NewEx(PyMethodDef *, PyObject *, - PyObject *); +#define PyCFunction_New(ML, SELF) \ + PyCFunction_NewExEx((ML), (SELF), NULL, NULL) +#define PyCFunction_NewEx(ML, SELF, MODULE) \ + PyCFunction_NewExEx((ML), (SELF), (MODULE), NULL) +PyAPI_FUNC(PyObject *) PyCFunction_NewExEx(PyMethodDef *, PyObject *, + PyObject *, PyObject *); /* Flag passed to newmethodobject */ /* #define METH_OLDARGS 0x0000 -- unsupported now */ @@ -77,6 +80,7 @@ PyMethodDef *m_ml; /* Description of the C function to call */ PyObject *m_self; /* Passed as 'self' arg to the C func, can be NULL */ PyObject *m_module; /* The __module__ attribute, can be anything */ + PyObject *m_unbound; } PyCFunctionObject; #endif diff -r 8b79603699e4 Lib/functools.py --- a/Lib/functools.py Wed Jul 18 00:02:56 2012 +0300 +++ b/Lib/functools.py Thu Jul 19 19:39:57 2012 +0300 @@ -9,10 +9,12 @@ # See C source code for _functools credits/copyright __all__ = ['update_wrapper', 'wraps', 'WRAPPER_ASSIGNMENTS', 'WRAPPER_UPDATES', - 'total_ordering', 'cmp_to_key', 'lru_cache', 'reduce', 'partial'] + 'total_ordering', 'cmp_to_key', 'lru_cache', 'reduce', 'partial', + 'unbind'] from _functools import partial, reduce from collections import namedtuple +from types import AllFunctionTypes try: from _thread import allocate_lock as Lock except: @@ -301,3 +303,13 @@ return update_wrapper(wrapper, user_function) return decorating_function + +def unbind(f): + if type(f) not in AllFunctionTypes: + raise TypeError('unbind only works on functions') + if not hasattr(f, '__self__'): + return f + # classmethod, don't remove __self__ + if type(f.__self__) is type: + return f + return f.__func__ diff -r 8b79603699e4 Lib/inspect.py --- a/Lib/inspect.py Wed Jul 18 00:02:56 2012 +0300 +++ b/Lib/inspect.py Thu Jul 19 19:39:57 2012 +0300 @@ -172,6 +172,27 @@ __kwdefaults__ dict of keyword only parameters with defaults""" return isinstance(object, types.FunctionType) +def isanyfunction(object): + """ + Return true if the object is any kind of function (method, builtin etc.). + """ + return type(object) in types.AllFunctionTypes + +def isanyboundfunction(object): + """ + Return true if the object is any kind of bound function. + + Class methods are considered bound functions. Static methods are not. + """ + return isanyfunction(object) and hasattr(object, '__self__') and \ + self is not None and type(self) is not types.ModuleType + +def isanyunboundfunction(object): + """ + Return true if the object is any kind of unbound function. + """ + return isanyfunction(object) and not isanyboundfunction(object) + def isgeneratorfunction(object): """Return true if the object is a user-defined generator function. diff -r 8b79603699e4 Lib/types.py --- a/Lib/types.py Wed Jul 18 00:02:56 2012 +0300 +++ b/Lib/types.py Thu Jul 19 19:39:57 2012 +0300 @@ -28,6 +28,18 @@ ModuleType = type(sys) +# XXX Note that the __func__ trick won't work anymore if we change the +# semantics of classmethods' __func__ to not unbind when type(__self__)=type +PyClassMethodDescriptorType = type(dict.fromkeys.__func__) +MethodDescriptorType = type(list.append) +WrapperDescriptorType = type(list.__add__) +MethodWrapperType = type([].__add__) + +AllFunctionTypes = (PyClassMethodDescriptorType, + BuiltinFunctionType, FunctionType, MethodType, + MethodDescriptorType, WrapperDescriptorType, + MethodWrapperType) + try: raise TypeError except TypeError: diff -r 8b79603699e4 Objects/classobject.c --- a/Objects/classobject.c Wed Jul 18 00:02:56 2012 +0300 +++ b/Objects/classobject.c Thu Jul 19 19:39:57 2012 +0300 @@ -244,8 +244,10 @@ else { klassname = _PyObject_GetAttrId(klass, &PyId___name__); if (klassname == NULL) { - if (!PyErr_ExceptionMatches(PyExc_AttributeError)) + if (!PyErr_ExceptionMatches(PyExc_AttributeError)) { + Py_XDECREF(funcname); return NULL; + } PyErr_Clear(); } else if (!PyUnicode_Check(klassname)) { diff -r 8b79603699e4 Objects/descrobject.c --- a/Objects/descrobject.c Wed Jul 18 00:02:56 2012 +0300 +++ b/Objects/descrobject.c Thu Jul 19 19:39:57 2012 +0300 @@ -115,7 +115,7 @@ ((PyTypeObject *)type)->tp_name); return NULL; } - return PyCFunction_New(descr->d_method, type); + return PyCFunction_NewExEx(descr->d_method, type, NULL, (PyObject*)descr); } static PyObject * @@ -125,7 +125,7 @@ if (descr_check((PyDescrObject *)descr, obj, &res)) return res; - return PyCFunction_New(descr->d_method, obj); + return PyCFunction_NewExEx(descr->d_method, obj, NULL, (PyObject*)descr); } static PyObject * @@ -239,7 +239,7 @@ return NULL; } - func = PyCFunction_New(descr->d_method, self); + func = PyCFunction_NewExEx(descr->d_method, self, NULL, (PyObject*)descr); if (func == NULL) return NULL; args = PyTuple_GetSlice(args, 1, argc); @@ -292,7 +292,7 @@ return NULL; } - func = PyCFunction_New(descr->d_method, self); + func = PyCFunction_NewExEx(descr->d_method, self, NULL, (PyObject*)descr); if (func == NULL) return NULL; args = PyTuple_GetSlice(args, 1, argc); @@ -398,6 +398,13 @@ return descr->d_qualname; } +static PyObject * +method_get_func(PyMethodDescrObject *descr, void *closure) +{ + Py_INCREF(descr); + return (PyObject*)descr; +} + static PyMemberDef descr_members[] = { {"__objclass__", T_OBJECT, offsetof(PyDescrObject, d_type), READONLY}, {"__name__", T_OBJECT, offsetof(PyDescrObject, d_name), READONLY}, @@ -407,6 +414,7 @@ static PyGetSetDef method_getset[] = { {"__doc__", (getter)method_get_doc}, {"__qualname__", (getter)descr_get_qualname}, + {"__func__", (getter)method_get_func}, {0} }; @@ -452,9 +460,17 @@ return PyUnicode_FromString(descr->d_base->doc); } +static PyObject * +wrapperdescr_get_func (PyWrapperDescrObject *descr, void *closure) +{ + Py_INCREF(descr); + return (PyObject*)descr; +} + static PyGetSetDef wrapperdescr_getset[] = { {"__doc__", (getter)wrapperdescr_get_doc}, {"__qualname__", (getter)descr_get_qualname}, + {"__func__", (getter)wrapperdescr_get_func}, {0} }; @@ -1091,7 +1107,7 @@ }; static PyObject * -wrapper_objclass(wrapperobject *wp) +wrapper_objclass(wrapperobject *wp, void *closure) { PyObject *c = (PyObject *)PyDescr_TYPE(wp->descr); @@ -1100,7 +1116,7 @@ } static PyObject * -wrapper_name(wrapperobject *wp) +wrapper_name(wrapperobject *wp, void *closure) { const char *s = wp->descr->d_base->name; @@ -1108,7 +1124,7 @@ } static PyObject * -wrapper_doc(wrapperobject *wp) +wrapper_doc(wrapperobject *wp, void *closure) { const char *s = wp->descr->d_base->doc; @@ -1122,16 +1138,24 @@ } static PyObject * -wrapper_qualname(wrapperobject *wp) +wrapper_qualname(wrapperobject *wp, void *closure) { return descr_get_qualname((PyDescrObject *)wp->descr); } +static PyObject * +wrapper_func(wrapperobject *wp, void *closure) +{ + Py_INCREF(wp->descr); + return (PyObject*)wp->descr; +} + static PyGetSetDef wrapper_getsets[] = { {"__objclass__", (getter)wrapper_objclass}, {"__name__", (getter)wrapper_name}, {"__qualname__", (getter)wrapper_qualname}, {"__doc__", (getter)wrapper_doc}, + {"__func__", (getter)wrapper_func}, {0} }; diff -r 8b79603699e4 Objects/methodobject.c --- a/Objects/methodobject.c Wed Jul 18 00:02:56 2012 +0300 +++ b/Objects/methodobject.c Thu Jul 19 19:39:57 2012 +0300 @@ -14,7 +14,8 @@ #endif PyObject * -PyCFunction_NewEx(PyMethodDef *ml, PyObject *self, PyObject *module) +PyCFunction_NewExEx(PyMethodDef *ml, PyObject *self, PyObject *module, + PyObject *unbound) { PyCFunctionObject *op; op = free_list; @@ -33,6 +34,8 @@ op->m_self = self; Py_XINCREF(module); op->m_module = module; + Py_XINCREF(unbound); + op->m_unbound = unbound; _PyObject_GC_TRACK(op); return (PyObject *)op; } @@ -206,11 +209,30 @@ return self; } +static PyObject * +meth_get__func__(PyCFunctionObject *m, void *closure) +{ + if (m->m_self == NULL || PyModule_Check(m->m_self) ) { + //XXX for classmethods add: || PyType_CheckExact(m->m_self)) + //XXX also change in classobject.c's __func__ + Py_INCREF(m); + return (PyObject*)m; + } + if (m->m_unbound != NULL) { + Py_XINCREF(m->m_unbound); + return m->m_unbound; + } + // this PyCFunctionObject was not provided with the unbounded version of + // the method, so we'll create one ourselves by replacing self with NULL + return PyCFunction_NewEx(m->m_ml, NULL, m->m_module); +} + static PyGetSetDef meth_getsets [] = { {"__doc__", (getter)meth_get__doc__, NULL, NULL}, {"__name__", (getter)meth_get__name__, NULL, NULL}, {"__qualname__", (getter)meth_get__qualname__, NULL, NULL}, {"__self__", (getter)meth_get__self__, NULL, NULL}, + {"__func__", (getter)meth_get__func__, NULL, NULL}, {0} }; @@ -347,16 +369,25 @@ numfree, sizeof(PyCFunction)); } -/* PyCFunction_New() is now just a macro that calls PyCFunction_NewEx(), - but it's part of the API so we need to keep a function around that - existing C extensions can call. +/* PyCFunction_New() and PyCFunction_NewEx() are now just macros that call + PyCFunction_NewExEx(), but they're part of the API so we need to keep + functions around that existing C extensions can call. */ #undef PyCFunction_New +#undef PyCFunction_NewEx PyAPI_FUNC(PyObject *) PyCFunction_New(PyMethodDef *, PyObject *); +PyAPI_FUNC(PyObject *) PyCFunction_NewEx( + PyMethodDef *, PyObject *, PyObject *); PyObject * PyCFunction_New(PyMethodDef *ml, PyObject *self) { - return PyCFunction_NewEx(ml, self, NULL); + return PyCFunction_NewExEx(ml, self, NULL, NULL); } + +PyObject * +PyCFunction_NewEx(PyMethodDef *ml, PyObject *self, PyObject *module) +{ + return PyCFunction_NewExEx(ml, self, module, NULL); +}