Christian Heimes added the comment:
I've moved the result = PyImport_NotifyPostImport(result); inside the
protected block. It's now protected by the import lock. I've also added
the lock protection to the register function.
The notify method is now exposed through the imp module, too.
I've checked multiple code paths. They all end up in
PyImport_ImportModuleLevel(): builtins.__import__(), PyImport_Import(),
PyImport_ImportModule(). Only PyImport_ImportFrozenModule() doesn't use
the code path in imp_init_frozen().
Added file: http://bugs.python.org/file8906/py3k_post_import_hook2.patch
__________________________________
Tracker <[EMAIL PROTECTED]>
<http://bugs.python.org/issue1576>
__________________________________
Index: Python/import.c
===================================================================
--- Python/import.c (Revision 59442)
+++ Python/import.c (Arbeitskopie)
@@ -161,7 +161,7 @@
void
_PyImportHooks_Init(void)
{
- PyObject *v, *path_hooks = NULL, *zimpimport;
+ PyObject *v, *path_hooks = NULL, *zimpimport, *pihr;
int err = 0;
/* adding sys.path_hooks and sys.path_importer_cache, setting up
@@ -198,6 +198,14 @@
);
}
+ pihr = PyDict_New();
+ if (pihr == NULL ||
+ PySys_SetObject("post_import_hooks", pihr) != 0) {
+ PyErr_Print();
+ Py_FatalError("initialization of post import hook registry "
+ "failed");
+ }
+
zimpimport = PyImport_ImportModule("zipimport");
if (zimpimport == NULL) {
PyErr_Clear(); /* No zip import module -- okay */
@@ -623,6 +631,201 @@
"sys.modules failed");
}
+/* post import hook API */
+PyObject *
+PyImport_GetPostImportHooks(void)
+{
+ PyObject *pihr;
+
+ pihr = PySys_GetObject("post_import_hooks");
+ /* This should only happen during initialization */
+ if (pihr == NULL)
+ return NULL;
+
+ if (!PyDict_Check(pihr)) {
+ PyErr_SetString(PyExc_TypeError,
+ "post import registry is not a dict");
+ }
+
+ Py_INCREF(pihr);
+ return pihr;
+}
+
+PyObject *
+PyImport_NotifyPostImport(PyObject *module)
+{
+ static PyObject *name = NULL;
+ PyObject *mod_name = NULL, *registry = NULL, *o;
+ PyObject *hooks = NULL, *hook, *it = NULL;
+ int status = -1;
+
+ if (module == NULL)
+ return NULL;
+ if (!PyModule_Check(module)) {
+ PyErr_Format(PyExc_TypeError,
+ "A module object was expected, got '%.200s'",
+ Py_Type(module)->tp_name);
+ return NULL;
+ }
+
+ if (!PyModule_IsLoaded(module)) {
+ /* nothing to do here */
+ return module;
+ }
+
+ registry = PyImport_GetPostImportHooks();
+ if (registry == NULL) {
+ /* This should only happen during initialization */
+ return module;
+ }
+
+ if (name == NULL) {
+ name = PyUnicode_InternFromString("__name__");
+ if (name == NULL)
+ return NULL;
+ }
+
+ mod_name = PyObject_GetAttr(module, name);
+ if (mod_name == NULL) {
+ goto error;
+ }
+ if (!PyUnicode_Check(mod_name)) {
+ PyObject *repr;
+ char *name;
+
+ repr = PyObject_Repr(module);
+ name = repr ? PyUnicode_AsString(repr) : "<unknown>";
+ PyErr_Format(PyExc_TypeError,
+ "Module __name__ attribute of '%.200s' is not "
+ "string", name);
+ Py_XDECREF(repr);
+ goto error;
+ }
+
+ hooks = PyObject_GetItem(registry, mod_name);
+ if (hooks == NULL || hooks == Py_None) {
+ /* Either no hooks are defined or they are already fired */
+ if (hooks == NULL) {
+ PyErr_Clear();
+ }
+ Py_DECREF(mod_name);
+ Py_DECREF(registry);
+ return module;
+ }
+ if (!PyList_Check(hooks)) {
+ PyErr_Format(PyExc_TypeError,
+ "expected None or list of hooks, got '%.200s'",
+ Py_Type(hooks)->tp_name);
+ goto error;
+ }
+
+ it = PyObject_GetIter(hooks);
+ if (it == NULL) {
+ goto error;
+ }
+ while ((hook = PyIter_Next(it)) != NULL) {
+ o = PyObject_CallFunctionObjArgs(hook, module, NULL);
+ if (o == NULL) {
+ goto error;
+ }
+ Py_DECREF(o);
+ }
+
+ /* end: */
+ status = 0;
+ error:
+ Py_XDECREF(mod_name);
+ Py_XDECREF(hooks);
+ Py_XDECREF(it);
+ Py_XDECREF(registry);
+ if (status < 0)
+ return NULL;
+ else
+ return module;
+}
+
+PyObject *
+PyImport_RegisterPostImportHook(PyObject *callable, PyObject *mod_name)
+{
+ PyObject *registry, *hooks;
+ int status = -1;
+
+ if (!PyCallable_Check(callable)) {
+ PyErr_SetString(PyExc_TypeError, "expected callable");
+ goto error;
+ }
+ if (!PyUnicode_Check(mod_name)) {
+ PyErr_SetString(PyExc_TypeError, "expected string");
+ goto error;
+ }
+
+ registry = PyImport_GetPostImportHooks();
+ if (registry == NULL)
+ goto error;
+
+ lock_import();
+
+ hooks = PyObject_GetItem(registry, mod_name);
+ /* module already loaded, fire hook immediately */
+ if (hooks == Py_None) {
+ PyObject *o, *module, *modules;
+
+ modules = PySys_GetObject("modules");
+ if (modules == NULL)
+ goto error;
+ module = PyObject_GetItem(modules, mod_name);
+ if (module == NULL)
+ goto error;
+
+ o = PyObject_CallFunctionObjArgs(callable, module, NULL);
+ if (o == NULL)
+ goto error;
+ else {
+ Py_DECREF(o);
+ goto end;
+ }
+ }
+ /* no hook registered so far */
+ if (hooks == NULL) {
+ PyErr_Clear();
+ hooks = PyList_New(1);
+ if (hooks == NULL) {
+ goto error;
+ }
+ Py_INCREF(callable);
+ PyList_SET_ITEM(hooks, 0, callable);
+ if (PyObject_SetItem(registry, mod_name, hooks) < 0) {
+ goto error;
+ }
+ goto end;
+ }
+ /* append a new callable */
+ if (!PyList_Check(hooks)) {
+ PyErr_Format(PyExc_TypeError,
+ "expected list of hooks, got '%.200s'",
+ Py_Type(hooks)->tp_name);
+ goto error;
+ }
+
+ if (PyList_Append(hooks, callable) < 0) {
+ goto error;
+ }
+
+ end:
+ status = 0;
+ error:
+ Py_XDECREF(registry);
+ if (unlock_import() < 0) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "not holding the import lock");
+ return NULL;
+ }
+ if (status < 0)
+ return NULL;
+ else
+ Py_RETURN_NONE;
+}
+
/* Execute a code object in a module and return the module object
* WITH INCREMENTED REFERENCE COUNT. If an error occurs, name is
* removed from sys.modules, to avoid leaving damaged module objects
@@ -1964,33 +2167,14 @@
return tail;
}
-/* For DLL compatibility */
-#undef PyImport_ImportModuleEx
PyObject *
-PyImport_ImportModuleEx(char *name, PyObject *globals, PyObject *locals,
- PyObject *fromlist)
-{
- PyObject *result;
- lock_import();
- result = import_module_level(name, globals, locals, fromlist, -1);
- if (unlock_import() < 0) {
- Py_XDECREF(result);
- PyErr_SetString(PyExc_RuntimeError,
- "not holding the import lock");
- return NULL;
- }
- return result;
-}
-#define PyImport_ImportModuleEx(n, g, l, f) \
- PyImport_ImportModuleLevel(n, g, l, f, -1);
-
-PyObject *
PyImport_ImportModuleLevel(char *name, PyObject *globals, PyObject *locals,
PyObject *fromlist, int level)
{
PyObject *result;
lock_import();
result = import_module_level(name, globals, locals, fromlist, level);
+ result = PyImport_NotifyPostImport(result);
if (unlock_import() < 0) {
Py_XDECREF(result);
PyErr_SetString(PyExc_RuntimeError,
@@ -2902,6 +3086,30 @@
return PyModule_New(name);
}
+static PyObject *
+imp_register_post_import_hook(PyObject *self, PyObject *args)
+{
+ PyObject *callable, *mod_name;
+
+ if (!PyArg_ParseTuple(args, "OO:register_post_import_hook",
+ &callable, &mod_name))
+ return NULL;
+
+ return PyImport_RegisterPostImportHook(callable, mod_name);
+}
+
+static PyObject *
+imp_post_import_notify(PyObject *self, PyObject *args)
+{
+ PyObject *mod;
+
+ if (!PyArg_ParseTuple(args, "O:post_import_notify", &mod))
+ return NULL;
+
+ Py_INCREF(mod);
+ return PyImport_NotifyPostImport(mod);
+}
+
/* Doc strings */
PyDoc_STRVAR(doc_imp,
@@ -2951,6 +3159,12 @@
Release the interpreter's import lock.\n\
On platforms without threads, this function does nothing.");
+PyDoc_STRVAR(doc_register_post_import_hook,
+"register_post_import_hook(callable, module_name) -> None");
+
+PyDoc_STRVAR(doc_post_import_notify,
+"post_import_notify(module) -> None");
+
static PyMethodDef imp_methods[] = {
{"find_module", imp_find_module, METH_VARARGS, doc_find_module},
{"get_magic", imp_get_magic, METH_NOARGS, doc_get_magic},
@@ -2960,6 +3174,10 @@
{"lock_held", imp_lock_held, METH_NOARGS, doc_lock_held},
{"acquire_lock", imp_acquire_lock, METH_NOARGS, doc_acquire_lock},
{"release_lock", imp_release_lock, METH_NOARGS, doc_release_lock},
+ {"register_post_import_hook", imp_register_post_import_hook,
+ METH_VARARGS, doc_register_post_import_hook},
+ {"post_import_notify", imp_post_import_notify, METH_VARARGS,
+ doc_post_import_notify},
/* The rest are obsolete */
{"get_frozen_object", imp_get_frozen_object, METH_VARARGS},
{"init_builtin", imp_init_builtin, METH_VARARGS},
Index: Include/moduleobject.h
===================================================================
--- Include/moduleobject.h (Revision 59442)
+++ Include/moduleobject.h (Arbeitskopie)
@@ -16,6 +16,7 @@
PyAPI_FUNC(PyObject *) PyModule_GetDict(PyObject *);
PyAPI_FUNC(const char *) PyModule_GetName(PyObject *);
PyAPI_FUNC(const char *) PyModule_GetFilename(PyObject *);
+PyAPI_FUNC(int) PyModule_IsLoaded(PyObject *);
PyAPI_FUNC(void) _PyModule_Clear(PyObject *);
#ifdef __cplusplus
Index: Include/import.h
===================================================================
--- Include/import.h (Revision 59442)
+++ Include/import.h (Arbeitskopie)
@@ -17,13 +17,6 @@
PyAPI_FUNC(PyObject *) PyImport_ImportModuleLevel(char *name,
PyObject *globals, PyObject *locals, PyObject *fromlist, int level);
-/* For DLL compatibility */
-#undef PyImport_ImportModuleEx
-PyAPI_FUNC(PyObject *) PyImport_ImportModuleEx(
- char *name, PyObject *globals, PyObject *locals, PyObject *fromlist);
-#define PyImport_ImportModuleEx(n, g, l, f) \
- PyImport_ImportModuleLevel(n, g, l, f, -1)
-
PyAPI_FUNC(PyObject *) PyImport_GetImporter(PyObject *path);
PyAPI_FUNC(PyObject *) PyImport_Import(PyObject *name);
PyAPI_FUNC(PyObject *) PyImport_ReloadModule(PyObject *m);
@@ -38,6 +31,12 @@
PyAPI_FUNC(PyObject *)_PyImport_FindExtension(char *, char *);
PyAPI_FUNC(PyObject *)_PyImport_FixupExtension(char *, char *);
+/* post import hook API */
+PyAPI_FUNC(PyObject *) PyImport_GetPostImportHooks(void);
+PyAPI_FUNC(PyObject *) PyImport_NotifyPostImport(PyObject *module);
+PyAPI_FUNC(PyObject *) PyImport_RegisterPostImportHook(
+ PyObject *callable, PyObject *mod_name);
+
struct _inittab {
char *name;
void (*initfunc)(void);
Index: Objects/moduleobject.c
===================================================================
--- Objects/moduleobject.c (Revision 59442)
+++ Objects/moduleobject.c (Arbeitskopie)
@@ -146,6 +146,17 @@
}
+/* Dummy method, always returns true */
+int
+PyModule_IsLoaded(PyObject *m)
+{
+ if (!PyModule_Check(m)) {
+ PyErr_BadArgument();
+ return -1;
+ }
+ return 1;
+}
+
/* Methods */
static int
Index: Lib/test/test_imp.py
===================================================================
--- Lib/test/test_imp.py (Revision 59442)
+++ Lib/test/test_imp.py (Arbeitskopie)
@@ -1,4 +1,5 @@
import imp
+import sys
import thread
import unittest
from test import test_support
@@ -60,11 +61,41 @@
'"""Tokenization help for Python programs.\n')
fp.close()
+class CallBack:
+ def __init__(self):
+ self.mods = []
+ def __call__(self, mod):
+ self.mods.append(mod)
+class PostImportHookTests(unittest.TestCase):
+
+ def setUp(self):
+ self.pihr = sys.post_import_hooks.copy()
+
+ def tearDown(self):
+ sys.post_import_hooks = self.pihr
+
+ def test_registry(self):
+ reg = sys.post_import_hooks
+ self.assert_(isinstance(reg, dict))
+
+ def test_register_callback(self):
+ callback = CallBack()
+ imp.register_post_import_hook(callback, "sys")
+ regc = sys.post_import_hooks.get("sys")
+ self.assert_(callback in regc, regc)
+ self.assert_(sys in callback.mods, callback.mods)
+
+ def test_post_import_notify(self):
+ imp.post_import_notify(sys)
+ self.failUnlessRaises(TypeError, imp.post_import_notify, None)
+ self.failUnlessRaises(TypeError, imp.post_import_notify, object())
+
def test_main():
test_support.run_unittest(
LockTests,
ImportTests,
+ PostImportHookTests,
)
if __name__ == "__main__":
Index: Modules/gcmodule.c
===================================================================
--- Modules/gcmodule.c (Revision 59442)
+++ Modules/gcmodule.c (Arbeitskopie)
@@ -271,6 +271,10 @@
* generation being collected, which can be recognized
* because only they have positive gc_refs.
*/
+#ifdef Py_DEBUG
+ if (gc->gc.gc_refs == 0)
+ _PyObject_Dump(op);
+#endif
assert(gc->gc.gc_refs != 0); /* else refcount was too small */
if (gc->gc.gc_refs > 0)
gc->gc.gc_refs--;
_______________________________________________
Python-bugs-list mailing list
Unsubscribe:
http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com