New submission from Christian Heimes:

I've written a rough draft for a post import hook as discussed on the
python 3000 mailing list. The implementation is far from perfect. It
requires more unit tests, a code review and reference count checks.

----------
assignee: tiran
components: Interpreter Core
files: py3k_post_import_hook.patch
keywords: py3k
messages: 58323
nosy: ncoghlan, pje, tiran
priority: normal
severity: normal
status: open
title: First draft of a post import hook
type: rfe
versions: Python 3.0
Added file: http://bugs.python.org/file8900/py3k_post_import_hook.patch

__________________________________
Tracker <[EMAIL PROTECTED]>
<http://bugs.python.org/issue1576>
__________________________________
Index: Python/import.c
===================================================================
--- Python/import.c	(Revision 59441)
+++ 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,186 @@
 			      "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_BadInternalCall();
+		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)) {
+		PyErr_Format(PyExc_TypeError,
+			     "Module name %.200s of %.200s is not string",
+			     PyObject_Repr(mod_name),
+			     PyObject_Repr(module));
+		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",
+			     PyObject_Repr(hooks));
+		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);
+	}
+
+	status = 0;
+    error:
+	Py_XDECREF(mod_name);
+	Py_XDECREF(hooks);
+	Py_XDECREF(it);
+	Py_XDECREF(registry);
+	if (status == -1)
+		return NULL;
+	else
+		return module;
+}
+
+PyObject *
+PyImport_RegisterPostImportHook(PyObject *callable, PyObject *mod_name)
+{
+	PyObject *registry, *hooks;
+
+	if (!PyCallable_Check(callable)) {
+		PyErr_SetString(PyExc_TypeError, "expected callable");
+		return NULL;
+	}
+	if (!PyUnicode_Check(mod_name)) {
+		PyErr_SetString(PyExc_TypeError, "expected string");
+		return NULL;
+	}
+
+	registry = PyImport_GetPostImportHooks();
+	if (registry == NULL)
+		return NULL;
+
+	hooks = PyObject_GetItem(registry, mod_name);
+	/* module already loaded, fire hook immediately */
+	if (hooks == Py_None) {
+		PyObject *o, *module, *modules;
+
+		Py_DECREF(registry);
+
+		modules = PySys_GetObject("modules");
+		if (modules == NULL)
+			return NULL;
+		module = PyObject_GetItem(modules, mod_name);
+		if (module == NULL)
+			return NULL;
+		
+		o = PyObject_CallFunctionObjArgs(callable, module, NULL);
+		if (o == NULL)
+			return NULL;
+		else {
+			Py_DECREF(o);
+			Py_RETURN_NONE;
+		}
+	}
+	/* 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;
+		}
+		Py_DECREF(registry);
+		Py_RETURN_NONE;
+	}
+	/* append a new callable */
+	if (!PyList_Check(hooks)) {
+		PyErr_Format(PyExc_TypeError,
+			     "expected list as hooks, got %.200s",
+			     PyObject_Repr(hooks));
+		goto error;
+	}
+
+	if (PyList_Append(hooks, callable) < 0) {
+		goto error;
+	}
+
+	Py_DECREF(registry);
+	Py_RETURN_NONE;
+
+error:
+	Py_DECREF(registry);
+	return NULL;
+}
+
 /* 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
@@ -1979,7 +2167,7 @@
 				"not holding the import lock");
 		return NULL;
 	}
-	return result;
+	return PyImport_NotifyPostImport(result);
 }
 #define PyImport_ImportModuleEx(n, g, l, f) \
 	PyImport_ImportModuleLevel(n, g, l, f, -1);
@@ -1997,7 +2185,7 @@
 				"not holding the import lock");
 		return NULL;
 	}
-	return result;
+	return PyImport_NotifyPostImport(result);
 }
 
 /* Return the package that an import is being performed in.  If globals comes
@@ -2902,6 +3090,18 @@
 	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);
+}
+
 /* Doc strings */
 
 PyDoc_STRVAR(doc_imp,
@@ -2951,6 +3151,9 @@
 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");
+
 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 +3163,8 @@
 	{"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},
 	/* 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 59441)
+++ 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 59441)
+++ Include/import.h	(Arbeitskopie)
@@ -38,6 +38,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 59441)
+++ 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 59441)
+++ 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,36 @@
                          '"""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_main():
     test_support.run_unittest(
                 LockTests,
                 ImportTests,
+                PostImportHookTests,
             )
 
 if __name__ == "__main__":
_______________________________________________
Python-bugs-list mailing list 
Unsubscribe: 
http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com

Reply via email to