Provide __module__ attributes for functions defined in C and Python.
__module__ is the string name of the module the function was defined in, just like __module__ of classes. In some cases, particularly for C functions, the __module__ may be None. Change PyCFunction_New() from a function to a macro, but keep an unused copy of the function around so that we don't change the binary API. Change pickle's save_global() to use whichmodule() if __module__ is None, but add the __module__ logic to whichmodule() since it might be used outside of pickle.
This commit is contained in:
parent
8f24cdc0d5
commit
4f0dcc9a9a
|
@ -17,6 +17,7 @@ typedef struct {
|
|||
PyObject *func_name;
|
||||
PyObject *func_dict;
|
||||
PyObject *func_weakreflist;
|
||||
PyObject *func_module;
|
||||
} PyFunctionObject;
|
||||
|
||||
PyAPI_DATA(PyTypeObject) PyFunction_Type;
|
||||
|
@ -26,6 +27,7 @@ PyAPI_DATA(PyTypeObject) PyFunction_Type;
|
|||
PyAPI_FUNC(PyObject *) PyFunction_New(PyObject *, PyObject *);
|
||||
PyAPI_FUNC(PyObject *) PyFunction_GetCode(PyObject *);
|
||||
PyAPI_FUNC(PyObject *) PyFunction_GetGlobals(PyObject *);
|
||||
PyAPI_FUNC(PyObject *) PyFunction_GetModule(PyObject *);
|
||||
PyAPI_FUNC(PyObject *) PyFunction_GetDefaults(PyObject *);
|
||||
PyAPI_FUNC(int) PyFunction_SetDefaults(PyObject *, PyObject *);
|
||||
PyAPI_FUNC(PyObject *) PyFunction_GetClosure(PyObject *);
|
||||
|
@ -37,6 +39,8 @@ PyAPI_FUNC(int) PyFunction_SetClosure(PyObject *, PyObject *);
|
|||
(((PyFunctionObject *)func) -> func_code)
|
||||
#define PyFunction_GET_GLOBALS(func) \
|
||||
(((PyFunctionObject *)func) -> func_globals)
|
||||
#define PyFunction_GET_MODULE(func) \
|
||||
(((PyFunctionObject *)func) -> func_module)
|
||||
#define PyFunction_GET_DEFAULTS(func) \
|
||||
(((PyFunctionObject *)func) -> func_defaults)
|
||||
#define PyFunction_GET_CLOSURE(func) \
|
||||
|
|
|
@ -40,7 +40,9 @@ typedef struct PyMethodDef PyMethodDef;
|
|||
|
||||
PyAPI_FUNC(PyObject *) Py_FindMethod(PyMethodDef[], PyObject *, char *);
|
||||
|
||||
PyAPI_FUNC(PyObject *) PyCFunction_New(PyMethodDef *, PyObject *);
|
||||
#define PyCFunction_New(ML, SELF) PyCFunction_NewEx((ML), (SELF), NULL)
|
||||
PyAPI_FUNC(PyObject *) PyCFunction_NewEx(PyMethodDef *, PyObject *,
|
||||
PyObject *);
|
||||
|
||||
/* Flag passed to newmethodobject */
|
||||
#define METH_OLDARGS 0x0000
|
||||
|
@ -68,6 +70,7 @@ typedef struct {
|
|||
PyObject_HEAD
|
||||
PyMethodDef *m_ml;
|
||||
PyObject *m_self;
|
||||
PyObject *m_module;
|
||||
} PyCFunctionObject;
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
@ -787,9 +787,8 @@ class Pickler:
|
|||
if name is None:
|
||||
name = obj.__name__
|
||||
|
||||
try:
|
||||
module = obj.__module__
|
||||
except AttributeError:
|
||||
module = getattr(obj, "__module__", None)
|
||||
if module is None:
|
||||
module = whichmodule(obj, name)
|
||||
|
||||
try:
|
||||
|
@ -876,6 +875,10 @@ def whichmodule(func, funcname):
|
|||
Return a module name.
|
||||
If the function cannot be found, return "__main__".
|
||||
"""
|
||||
# Python functions should always get an __module__ from their globals.
|
||||
mod = getattr(func, "__module__", None)
|
||||
if mod is not None:
|
||||
return mod
|
||||
if func in classmap:
|
||||
return classmap[func]
|
||||
|
||||
|
|
|
@ -9,6 +9,10 @@ def b():
|
|||
'my docstring'
|
||||
pass
|
||||
|
||||
# __module__ is a special attribute
|
||||
verify(b.__module__ == __name__)
|
||||
verify(verify.__module__ == "test.test_support")
|
||||
|
||||
# setting attributes on functions
|
||||
try:
|
||||
b.publish
|
||||
|
|
|
@ -14,6 +14,7 @@ PyFunction_New(PyObject *code, PyObject *globals)
|
|||
if (op != NULL) {
|
||||
PyObject *doc;
|
||||
PyObject *consts;
|
||||
PyObject *module;
|
||||
op->func_weakreflist = NULL;
|
||||
Py_INCREF(code);
|
||||
op->func_code = code;
|
||||
|
@ -34,6 +35,16 @@ PyFunction_New(PyObject *code, PyObject *globals)
|
|||
Py_INCREF(doc);
|
||||
op->func_doc = doc;
|
||||
op->func_dict = NULL;
|
||||
op->func_module = NULL;
|
||||
|
||||
/* __module__: If module name is in globals, use it.
|
||||
Otherwise, use None.
|
||||
*/
|
||||
module = PyDict_GetItemString(globals, "__name__");
|
||||
if (module) {
|
||||
Py_INCREF(module);
|
||||
op->func_module = module;
|
||||
}
|
||||
}
|
||||
else
|
||||
return NULL;
|
||||
|
@ -61,6 +72,16 @@ PyFunction_GetGlobals(PyObject *op)
|
|||
return ((PyFunctionObject *) op) -> func_globals;
|
||||
}
|
||||
|
||||
PyObject *
|
||||
PyFunction_GetModule(PyObject *op)
|
||||
{
|
||||
if (!PyFunction_Check(op)) {
|
||||
PyErr_BadInternalCall();
|
||||
return NULL;
|
||||
}
|
||||
return ((PyFunctionObject *) op) -> func_module;
|
||||
}
|
||||
|
||||
PyObject *
|
||||
PyFunction_GetDefaults(PyObject *op)
|
||||
{
|
||||
|
@ -138,6 +159,7 @@ static PyMemberDef func_memberlist[] = {
|
|||
RESTRICTED|READONLY},
|
||||
{"func_name", T_OBJECT, OFF(func_name), READONLY},
|
||||
{"__name__", T_OBJECT, OFF(func_name), READONLY},
|
||||
{"__module__", T_OBJECT, OFF(func_module), READONLY},
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
|
@ -373,6 +395,7 @@ func_dealloc(PyFunctionObject *op)
|
|||
PyObject_ClearWeakRefs((PyObject *) op);
|
||||
Py_DECREF(op->func_code);
|
||||
Py_DECREF(op->func_globals);
|
||||
Py_XDECREF(op->func_module);
|
||||
Py_DECREF(op->func_name);
|
||||
Py_XDECREF(op->func_defaults);
|
||||
Py_XDECREF(op->func_doc);
|
||||
|
@ -405,6 +428,11 @@ func_traverse(PyFunctionObject *f, visitproc visit, void *arg)
|
|||
if (err)
|
||||
return err;
|
||||
}
|
||||
if (f->func_module) {
|
||||
err = visit(f->func_module, arg);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
if (f->func_defaults) {
|
||||
err = visit(f->func_defaults, arg);
|
||||
if (err)
|
||||
|
|
|
@ -2,11 +2,12 @@
|
|||
/* Method object implementation */
|
||||
|
||||
#include "Python.h"
|
||||
#include "structmember.h"
|
||||
|
||||
static PyCFunctionObject *free_list = NULL;
|
||||
|
||||
PyObject *
|
||||
PyCFunction_New(PyMethodDef *ml, PyObject *self)
|
||||
PyCFunction_NewEx(PyMethodDef *ml, PyObject *self, PyObject *module)
|
||||
{
|
||||
PyCFunctionObject *op;
|
||||
op = free_list;
|
||||
|
@ -22,6 +23,8 @@ PyCFunction_New(PyMethodDef *ml, PyObject *self)
|
|||
op->m_ml = ml;
|
||||
Py_XINCREF(self);
|
||||
op->m_self = self;
|
||||
Py_XINCREF(module);
|
||||
op->m_module = module;
|
||||
_PyObject_GC_TRACK(op);
|
||||
return (PyObject *)op;
|
||||
}
|
||||
|
@ -121,6 +124,7 @@ meth_dealloc(PyCFunctionObject *m)
|
|||
{
|
||||
_PyObject_GC_UNTRACK(m);
|
||||
Py_XDECREF(m->m_self);
|
||||
Py_XDECREF(m->m_module);
|
||||
m->m_self = (PyObject *)free_list;
|
||||
free_list = m;
|
||||
}
|
||||
|
@ -145,10 +149,18 @@ meth_get__name__(PyCFunctionObject *m, void *closure)
|
|||
static int
|
||||
meth_traverse(PyCFunctionObject *m, visitproc visit, void *arg)
|
||||
{
|
||||
if (m->m_self != NULL)
|
||||
return visit(m->m_self, arg);
|
||||
else
|
||||
return 0;
|
||||
int err;
|
||||
if (m->m_self != NULL) {
|
||||
err = visit(m->m_self, arg);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
if (m->m_module != NULL) {
|
||||
err = visit(m->m_module, arg);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
|
@ -174,6 +186,13 @@ static PyGetSetDef meth_getsets [] = {
|
|||
{0}
|
||||
};
|
||||
|
||||
#define OFF(x) offsetof(PyCFunctionObject, x)
|
||||
|
||||
static PyMemberDef meth_members[] = {
|
||||
{"__module__", T_OBJECT, OFF(m_module), READONLY},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
static PyObject *
|
||||
meth_repr(PyCFunctionObject *m)
|
||||
{
|
||||
|
@ -250,7 +269,7 @@ PyTypeObject PyCFunction_Type = {
|
|||
0, /* tp_iter */
|
||||
0, /* tp_iternext */
|
||||
0, /* tp_methods */
|
||||
0, /* tp_members */
|
||||
meth_members, /* tp_members */
|
||||
meth_getsets, /* tp_getset */
|
||||
0, /* tp_base */
|
||||
0, /* tp_dict */
|
||||
|
@ -308,6 +327,7 @@ Py_FindMethodInChain(PyMethodChain *chain, PyObject *self, char *name)
|
|||
for (; ml->ml_name != NULL; ml++) {
|
||||
if (name[0] == ml->ml_name[0] &&
|
||||
strcmp(name+1, ml->ml_name+1) == 0)
|
||||
/* XXX */
|
||||
return PyCFunction_New(ml, self);
|
||||
}
|
||||
chain = chain->link;
|
||||
|
@ -338,3 +358,17 @@ PyCFunction_Fini(void)
|
|||
PyObject_GC_Del(v);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
#undef PyCFunction_New
|
||||
PyAPI_FUNC(PyObject *) PyCFunction_New(PyMethodDef *, PyObject *);
|
||||
|
||||
PyObject *
|
||||
PyCFunction_New(PyMethodDef *ml, PyObject *self)
|
||||
{
|
||||
return PyCFunction_NewEx(ml, self, NULL);
|
||||
}
|
||||
|
|
|
@ -126,22 +126,27 @@ Exception\n\
|
|||
static int
|
||||
populate_methods(PyObject *klass, PyObject *dict, PyMethodDef *methods)
|
||||
{
|
||||
PyObject *module;
|
||||
int status = -1;
|
||||
|
||||
if (!methods)
|
||||
return 0;
|
||||
|
||||
module = PyString_FromString("exceptions");
|
||||
if (!module)
|
||||
return 0;
|
||||
while (methods->ml_name) {
|
||||
/* get a wrapper for the built-in function */
|
||||
PyObject *func = PyCFunction_New(methods, NULL);
|
||||
PyObject *func = PyCFunction_NewEx(methods, NULL, module);
|
||||
PyObject *meth;
|
||||
int status;
|
||||
|
||||
if (!func)
|
||||
return -1;
|
||||
goto status;
|
||||
|
||||
/* turn the function into an unbound method */
|
||||
if (!(meth = PyMethod_New(func, NULL, klass))) {
|
||||
Py_DECREF(func);
|
||||
return -1;
|
||||
goto status;
|
||||
}
|
||||
|
||||
/* add method to dictionary */
|
||||
|
@ -151,11 +156,14 @@ populate_methods(PyObject *klass, PyObject *dict, PyMethodDef *methods)
|
|||
|
||||
/* stop now if an error occurred, otherwise do the next method */
|
||||
if (status)
|
||||
return status;
|
||||
goto status;
|
||||
|
||||
methods++;
|
||||
}
|
||||
return 0;
|
||||
status = 0;
|
||||
status:
|
||||
Py_DECREF(module);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ PyObject *
|
|||
Py_InitModule4(char *name, PyMethodDef *methods, char *doc,
|
||||
PyObject *passthrough, int module_api_version)
|
||||
{
|
||||
PyObject *m, *d, *v;
|
||||
PyObject *m, *d, *v, *n;
|
||||
PyMethodDef *ml;
|
||||
if (!Py_IsInitialized())
|
||||
Py_FatalError("Interpreter not initialized (version mismatch?)");
|
||||
|
@ -46,6 +46,15 @@ Py_InitModule4(char *name, PyMethodDef *methods, char *doc,
|
|||
if (PyErr_Warn(PyExc_RuntimeWarning, message))
|
||||
return NULL;
|
||||
}
|
||||
/* Make sure name is fully qualified.
|
||||
|
||||
This is a bit of a hack: when the shared library is loaded,
|
||||
the module name is "package.module", but the module calls
|
||||
Py_InitModule*() with just "module" for the name. The shared
|
||||
library loader squirrels away the true name of the module in
|
||||
_Py_PackageContext, and Py_InitModule*() will substitute this
|
||||
(if the name actually matches).
|
||||
*/
|
||||
if (_Py_PackageContext != NULL) {
|
||||
char *p = strrchr(_Py_PackageContext, '.');
|
||||
if (p != NULL && strcmp(name, p+1) == 0) {
|
||||
|
@ -57,6 +66,9 @@ Py_InitModule4(char *name, PyMethodDef *methods, char *doc,
|
|||
return NULL;
|
||||
d = PyModule_GetDict(m);
|
||||
if (methods != NULL) {
|
||||
n = PyString_FromString(name);
|
||||
if (n == NULL)
|
||||
return NULL;
|
||||
for (ml = methods; ml->ml_name != NULL; ml++) {
|
||||
if ((ml->ml_flags & METH_CLASS) ||
|
||||
(ml->ml_flags & METH_STATIC)) {
|
||||
|
@ -65,7 +77,7 @@ Py_InitModule4(char *name, PyMethodDef *methods, char *doc,
|
|||
" METH_CLASS or METH_STATIC");
|
||||
return NULL;
|
||||
}
|
||||
v = PyCFunction_New(ml, passthrough);
|
||||
v = PyCFunction_NewEx(ml, passthrough, n);
|
||||
if (v == NULL)
|
||||
return NULL;
|
||||
if (PyDict_SetItemString(d, ml->ml_name, v) != 0) {
|
||||
|
|
Loading…
Reference in New Issue