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