[issue1576] First draft of a post import hook
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) { +
[issue1576] First draft of a post import hook
Changes by Christian Heimes: Removed file: http://bugs.python.org/file8906/py3k_post_import_hook2.patch __ Tracker [EMAIL PROTECTED] http://bugs.python.org/issue1576 __ ___ Python-bugs-list mailing list Unsubscribe: http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com
[issue1576] First draft of a post import hook
Christian Heimes added the comment: UPDATES: * no ref leaks * PyModule_IsLazy(mod) checks mod.__lazy_import__ attribute (we can argue about a nice name later) * Added imp.is_lazy(mod_or_name) Added file: http://bugs.python.org/file8914/py3k_post_import_hook4.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 */ @@ -369,6 +377,7 @@ path, argv, ps1, ps2, last_type, last_value, last_traceback, path_hooks, path_importer_cache, meta_path, + post_import_hooks, NULL }; @@ -623,6 +632,228 @@ 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); + } + 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 (name == NULL) { + name = PyUnicode_InternFromString(__name__); + if (name == NULL) { + return NULL; + } + } + + 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_IsLazy(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; + } + + 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(); + } + goto end; + } + 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_CallFunctionObjArgs(hook, module, NULL); + Py_DECREF(hook); + 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(it); + 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
[issue1576] First draft of a post import hook
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)
[issue1576] First draft of a post import hook
Phillip J. Eby added the comment: It also needs to hold the import lock during both the register and notify operations. In addition, the notify operation needs to be exposed for calling from Python (so that lazy module implementations can interop). Finally, it's not clear to me whether there's any way one of the import APIs can exit with the module added to sys.modules, but *without* the notify operation being called. It seems to me that those code paths were cleared up in 2.4 (i.e. failure to import causes the module to be removed from sys.modules), but it might be good to doublecheck that. __ Tracker [EMAIL PROTECTED] http://bugs.python.org/issue1576 __ ___ Python-bugs-list mailing list Unsubscribe: http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com
[issue1576] First draft of a post import hook
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
[issue1576] First draft of a post import hook
Changes by Christian Heimes: Removed file: http://bugs.python.org/file8900/py3k_post_import_hook.patch __ Tracker [EMAIL PROTECTED] http://bugs.python.org/issue1576 __ ___ Python-bugs-list mailing list Unsubscribe: http://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com