This PEP has been withdrawn by its author, as much of the detailed designis no longer valid following the migration to importlib in Python 3.3.
This PEP proposes enhancements for the import machinery to addpost import hooks. It is intended primarily to support the wideruse of abstract base classes that is expected in Python 3.0.
The PEP originally started as a combined PEP for lazy imports andpost import hooks. After some discussion on the python-dev mailinglist the PEP was parted in two separate PEPs.[1]
Python has no API to hook into the import machinery and execute codeafter a module is successfully loaded. The import hooks ofPEP 302 areabout finding modules and loading modules but they were not designed toas post import hooks.
A use case for a post import hook is mentioned in Alyssa (Nick) Coghlan’s initialposting[2]. about callbacks on module import. It was found during thedevelopment of Python 3.0 and its ABCs. We wanted to register classeslike decimal.Decimal with an ABC but the module should not be importedon every interpreter startup. Alyssa came up with this example:
@imp.when_imported('decimal')defregister(decimal):Inexact.register(decimal.Decimal)
The functionregister is registered as callback for the module named‘decimal’. When decimal is imported the function is called with themodule object as argument.
While this particular example isn’t necessary in practice, (asdecimal.Decimal will inherit from the appropriate abstract Number baseclass in 2.6 and 3.0), it still illustrates the principle.
PJE’s peak.util.imports[3] implements post load hooks. Myimplementation shares a lot with his and it’s partly based on his ideas.
Post import hooks are called after a module has been loaded. The hooksare callable which take one argument, the module instance. They areregistered by the dotted name of the module, e.g. ‘os’ or ‘os.path’.
The callable are stored in the dictsys.post_import_hooks whichis a mapping from names (as string) to a list of callables or None.
sys.post_import_hooks contains no entry for the module
The import hook registry contains an entrysys.post_import_hooks[“name”] = [hook1]
The import machinery checks if sys.post_import_hooks contains post importhooks for the newly loaded module. If hooks are found then the hooks arecalled in the order they were registered with the module instance as firstargument. The processing of the hooks is stopped when a method raises anexception. At the end the entry for the module name set to None, evenwhen an error has occurred.
Additionally the new__notified__ slot of the module object is settoTrue in order to prevent infinity recursions when the notificationmethod is called inside a hook. For object which don’t subclass fromPyModule a new attribute is added instead.
The import hooks are neither called nor removed from the registry. Itmay be possible to load the module later.
The hook is fired immediately.
The import hook system guarantees certain invariants. XXX
A Python implementation may look like:
defnotify(name):try:module=sys.modules[name]exceptKeyError:raiseImportError("Module%s has not been imported"%(name,))ifmodule.__notified__:returntry:module.__notified__=Trueif'.'inname:notify(name[:name.rfind('.')])forcallbackinpost_import_hooks[name]:callback(module)finally:post_import_hooks[name]=NoneXXX
PyObject*PyImport_GetPostImportHooks(void)PyObject*PyImport_NotifyLoadedByModule(PyObject*module)PyObject*PyImport_NotifyLoadedByName(constchar*name)PyImport_NotifyLoadedByName("a.b.c") callsPyImport_NotifyLoadedByModule() fora,a.b anda.b.cin that particular order. The modules are retrieved fromsys.modules. If a module can’t be retrieved, an exception is raisedotherwise the a borrowed reference tomodname is returned.The hook calls always start with the prime parent module.The caller of PyImport_NotifyLoadedByName() must hold the import lock!PyObject*PyImport_RegisterPostImportHook(PyObject*callable,PyObject*mod_name)callable for the modulemod_nameintPyModule_GetNotified(PyObject*module)__notified__ slot / attribute.intPyModule_SetNotified(PyObject*module,intstatus)__notified__ slot / attribute.ThePyImport_NotifyLoadedByModule() method is called insideimport_submodule(). The import system makes sure that the import lockis acquired and the hooks for the parent modules are already called.
The import hook registry and two new API methods are exposed through thesys andimp module.
sys.post_import_hooks{"name":[hook1,hook2],...}
imp.register_post_import_hook(hook:"callable",name:str)imp.notify_module_loaded(module:"moduleinstance")->modulemodule.__notified__The when_imported function decorator is also in the imp module,which is equivalent to:
defwhen_imported(name):defregister(hook):register_post_import_hook(hook,name)returnregister
The when_imported decorator hasn’t been written.
The code contains several XXX comments. They are mostly about errorhandling in edge cases.
The new features and API don’t conflict with old import system of Pythonand don’t cause any backward compatibility issues for most software.However systems like PEAK and Zope which implement their own lazy importmagic need to follow some rules.
The post import hooks carefully designed to cooperate with existingdeferred and lazy import systems. It’s the suggestion of the PEP authorto replace own on-load-hooks with the new hook API. The alternativelazy or deferred imports will still work but the implementations mustcall theimp.notify_module_loaded function.
A reference implementation is already written and is available in thepy3k-importhook branch.[4] It still requires some cleanups,documentation updates and additional unit tests.
Alyssa Coghlan, for proof reading and the initial discussionPhillip J. Eby, for his implementation in PEAK and help with my own implementation
This document has been placed in the public domain.
Source:https://github.com/python/peps/blob/main/peps/pep-0369.rst
Last modified:2025-02-01 08:59:27 GMT