Created
July 19, 2012 16:45
-
-
Save mstefanro/3145210 to your computer and use it in GitHub Desktop.
__func__ in cpython
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
+} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment