Skip to content

Instantly share code, notes, and snippets.

@mstefanro
Created July 19, 2012 16:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mstefanro/3145210 to your computer and use it in GitHub Desktop.
Save mstefanro/3145210 to your computer and use it in GitHub Desktop.
__func__ in cpython
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