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__":