summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeremy Hylton <jeremy@alum.mit.edu>2003-01-31 18:33:18 (GMT)
committerJeremy Hylton <jeremy@alum.mit.edu>2003-01-31 18:33:18 (GMT)
commit4f0dcc9a9a4629607d4fff79912e8d0c86cd3914 (patch)
treebba33e033f6a51fec1c01a79a5f6e931f65e44f2
parent8f24cdc0d5e30e2f924ed2e8a71400fa87a70983 (diff)
downloadcpython-4f0dcc9a9a4629607d4fff79912e8d0c86cd3914.zip
cpython-4f0dcc9a9a4629607d4fff79912e8d0c86cd3914.tar.gz
cpython-4f0dcc9a9a4629607d4fff79912e8d0c86cd3914.tar.bz2
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.
-rw-r--r--Include/funcobject.h4
-rw-r--r--Include/methodobject.h5
-rw-r--r--Lib/pickle.py9
-rw-r--r--Lib/test/test_funcattrs.py4
-rw-r--r--Objects/funcobject.c28
-rw-r--r--Objects/methodobject.c46
-rw-r--r--Python/exceptions.c20
-rw-r--r--Python/modsupport.c16
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) {