Christian Heimes added the comment:

The new patch fixes some ref leaks, corner cases and adds some new unit
tests. All unit tests are passing but I'm leaking lots of references in
the register function.

Added file: http://bugs.python.org/file8909/py3k_post_import_hook3.patch

__________________________________
Tracker <[EMAIL PROTECTED]>
<http://bugs.python.org/issue1576>
__________________________________
Index: Python/import.c
===================================================================
--- Python/import.c	(Revision 59449)
+++ 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,239 @@
 			      "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);
+		Py_DECREF(module);
+		return NULL;
+	}
+
+	if (!PyModule_IsLoaded(module)) {
+		/* nothing to do here */
+		return module;
+	}
+	/* XXX check if module is in sys.modules ? */
+
+	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 = PyDict_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;
+	}
+
+	/* fire hooks */
+	it = PyObject_GetIter(hooks);
+	if (it == NULL) {
+		goto error;
+	}
+	while ((hook = PyIter_Next(it)) != NULL) {
+		o = PyObject_CallFunction(hook, "O", module);
+		if (o == NULL) {
+			goto error;
+		}
+		Py_DECREF(o);
+	}
+
+	/* Mark hooks as fired */
+	if (PyDict_SetItem(registry, mod_name, Py_None) < 0) {
+		goto error;
+	}
+
+    /* 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 = NULL, *hooks = NULL;
+	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 = PyDict_GetItem(registry, mod_name);
+	/* module may be already loaded, get the module object from sys */
+	if (hooks == NULL || hooks == Py_None) {
+		PyObject *o, *modules;
+		PyObject *module = NULL;
+
+		modules = PySys_GetObject("modules");
+		if (modules == NULL) {
+			goto error;
+		}
+		module = PyObject_GetItem(modules, mod_name);
+		if (module == NULL) {
+			PyErr_Clear();
+			/* somebody messed up sys.modules */
+			if (hooks == Py_None) {
+				; /* XXX error */
+			}
+		}
+		/* mark hooks as fired */
+		if (hooks == NULL) {
+			if (PyDict_SetItem(registry, mod_name, Py_None) < 0) {
+				goto error;
+			}
+		}
+		else {
+			Py_DECREF(hooks);
+		}
+		/* module is already loaded, fire hook immediately */
+		if (module != NULL) {
+			o = PyObject_CallFunction(callable, "O", module);
+			Py_DECREF(module);
+			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);
+		if (PyList_SetItem(hooks, 0, callable) < 0) {
+			goto error;
+		}
+		if (PyDict_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;
+	}
+	Py_DECREF(hooks);
+
+    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 +2205,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 +3124,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 +3197,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 +3212,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 59449)
+++ 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 59449)
+++ 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 59449)
+++ 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 59449)
+++ 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,59 @@
                          '"""Tokenization help for Python programs.\n')
         fp.close()
 
+class CallBack:
+    def __init__(self):
+        self.mods = {}
 
+    def __call__(self, mod):
+        self.mods[mod.__name__] = 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()
+        # an arbitrary module
+        if "telnetlib" in sys.modules:
+            del sys.modules["telnetlib"]
+        imp.register_post_import_hook(callback, "telnetlib")
+        imp.register_post_import_hook(callback, "sys")
+
+        # sys is already loaded and the callback is fired immediately
+        self.assert_("sys" in callback.mods, callback.mods)
+        self.assert_(callback.mods["sys"] is sys, callback.mods)
+        self.failIf("telnetlib" in callback.mods, callback.mods)
+        regc = sys.post_import_hooks.get("sys", False)
+        self.assert_(regc is None, regc)
+
+        regc = sys.post_import_hooks.get("telnetlib")
+        self.assert_(regc is not None, regc)
+        self.assert_(isinstance(regc, list), regc)
+        self.assert_(callback in regc, regc)
+
+        import telnetlib
+        self.assert_("telnetlib" in callback.mods, callback.mods)
+        self.assert_(callback.mods["telnetlib"] is telnetlib, 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 59449)
+++ 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

Reply via email to