From 4f0dcc9a9a4629607d4fff79912e8d0c86cd3914 Mon Sep 17 00:00:00 2001 From: Jeremy Hylton Date: Fri, 31 Jan 2003 18:33:18 +0000 Subject: 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. --- Include/funcobject.h | 4 ++++ Include/methodobject.h | 5 ++++- Lib/pickle.py | 9 ++++++--- Lib/test/test_funcattrs.py | 4 ++++ Objects/funcobject.c | 28 ++++++++++++++++++++++++++++ Objects/methodobject.c | 46 ++++++++++++++++++++++++++++++++++++++++------ Python/exceptions.c | 20 ++++++++++++++------ Python/modsupport.c | 16 ++++++++++++++-- 8 files changed, 114 insertions(+), 18 deletions(-) diff --git a/Include/funcobject.h b/Include/funcobject.h index 7fee800..758c76d 100644 --- a/Include/funcobject.h +++ b/Include/funcobject.h @@ -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) \ diff --git a/Include/methodobject.h b/Include/methodobject.h index 993bdae..887385d 100644 --- a/Include/methodobject.h +++ b/Include/methodobject.h @@ -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 diff --git a/Lib/pickle.py b/Lib/pickle.py index ec19e24..e365bd1 100644 --- a/Lib/pickle.py +++ b/Lib/pickle.py @@ -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] diff --git a/Lib/test/test_funcattrs.py b/Lib/test/test_funcattrs.py index 3dc888c..2c31d36 100644 --- a/Lib/test/test_funcattrs.py +++ b/Lib/test/test_funcattrs.py @@ -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 diff --git a/Objects/funcobject.c b/Objects/funcobject.c index 6154d99..b162fdf 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -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; @@ -62,6 +73,16 @@ PyFunction_GetGlobals(PyObject *op) } PyObject * +PyFunction_GetModule(PyObject *op) +{ + if (!PyFunction_Check(op)) { + PyErr_BadInternalCall(); + return NULL; + } + return ((PyFunctionObject *) op) -> func_module; +} + +PyObject * PyFunction_GetDefaults(PyObject *op) { if (!PyFunction_Check(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) diff --git a/Objects/methodobject.c b/Objects/methodobject.c index a1f325d..aa64ff1 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -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); +} diff --git a/Python/exceptions.c b/Python/exceptions.c index 16b6738..0080694 100644 --- a/Python/exceptions.c +++ b/Python/exceptions.c @@ -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; } diff --git a/Python/modsupport.c b/Python/modsupport.c index bca3d6f..41e0c82 100644 --- a/Python/modsupport.c +++ b/Python/modsupport.c @@ -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) { -- cgit v0.12