PEP 369 – Post-Import-Hooks
- Autor:
- Christian Heimes <christian at python.org>
- Status:
- Zurückgezogen
- Typ:
- Standards Track
- Erstellt:
- 02-Jan-2008
- Python-Version:
- 2.6, 3.0
- Post-History:
- 02-Dez-2012
Inhaltsverzeichnis
Withdrawal Notice
Dieser PEP wurde von seinem Autor zurückgezogen, da ein Großteil des detaillierten Designs nach der Migration zu importlib in Python 3.3 nicht mehr gültig ist.
Zusammenfassung
Dieser PEP schlägt Erweiterungen für die Import-Maschinerie vor, um Post-Import-Hooks hinzuzufügen. Er ist primär dazu gedacht, die breitere Nutzung von abstrakten Basisklassen zu unterstützen, die in Python 3.0 erwartet wird.
Der PEP begann ursprünglich als kombinierter PEP für Lazy-Imports und Post-Import-Hooks. Nach einigen Diskussionen auf der python-dev-Mailingliste wurde der PEP in zwei separate PEPs aufgeteilt. [1]
Begründung
Python hat keine API, um in die Import-Maschinerie einzugreifen und Code *nach* erfolgreichem Laden eines Moduls auszuführen. Die Import-Hooks von PEP 302 befassen sich mit dem Finden und Laden von Modulen, waren aber nicht als Post-Import-Hooks konzipiert.
Anwendungsfälle
Ein Anwendungsfall für einen Post-Import-Hook wird in Alyssa (Nick) Coghlans ursprünglicher Veröffentlichung [2] erwähnt, über Rückrufe beim Modulimport. Dies wurde während der Entwicklung von Python 3.0 und seinen ABCs festgestellt. Wir wollten Klassen wie decimal.Decimal bei einer ABC registrieren, aber das Modul sollte nicht bei jedem Interpreterstart importiert werden. Alyssa kam mit diesem Beispiel
@imp.when_imported('decimal')
def register(decimal):
Inexact.register(decimal.Decimal)
Die Funktion register wird als Rückruf für das Modul mit dem Namen 'decimal' registriert. Wenn decimal importiert wird, wird die Funktion mit dem Modulobjekt als Argument aufgerufen.
Obwohl dieses spezielle Beispiel praktisch nicht notwendig ist (da decimal.Decimal in 2.6 und 3.0 von der entsprechenden abstrakten Basisklasse Number erben wird), illustriert es dennoch das Prinzip.
Bestehende Implementierungen
PJE's peak.util.imports [3] implementiert Post-Load-Hooks. Meine Implementierung teilt viel mit seiner und basiert teilweise auf seinen Ideen.
Implementierung von Post-Import-Hooks
Post-Import-Hooks werden aufgerufen, nachdem ein Modul geladen wurde. Die Hooks sind aufrufbare Objekte, die ein Argument entgegennehmen: die Modulinstanz. Sie werden unter dem gepunkteten Namen des Moduls registriert, z. B. 'os' oder 'os.path'.
Die aufrufbaren Objekte werden im Dictionary sys.post_import_hooks gespeichert, das eine Abbildung von Namen (als String) auf eine Liste von aufrufbaren Objekten oder None ist.
Zustände
Kein Hook wurde registriert
sys.post_import_hooks enthält keinen Eintrag für das Modul
Ein Hook ist registriert und das Modul ist noch nicht geladen
Die Import-Hook-Registrierung enthält einen Eintrag sys.post_import_hooks["name"] = [hook1]
Ein Modul wurde erfolgreich geladen
Die Import-Maschinerie prüft, ob sys.post_import_hooks Post-Import-Hooks für das neu geladene Modul enthält. Wenn Hooks gefunden werden, werden die Hooks in der Reihenfolge aufgerufen, in der sie registriert wurden, mit der Modulinstanz als erstem Argument. Die Verarbeitung der Hooks wird gestoppt, wenn eine Methode eine Ausnahme auslöst. Am Ende wird der Eintrag für den Modulnamen auf None gesetzt, auch wenn ein Fehler aufgetreten ist.
Zusätzlich wird der neue __notified__ Slot des Modulobjekts auf True gesetzt, um unendliche Rekursionen zu verhindern, wenn die Benachrichtigungsmethode innerhalb eines Hooks aufgerufen wird. Für Objekte, die nicht von PyModule abgeleitet sind, wird stattdessen ein neues Attribut hinzugefügt.
Ein Modul kann nicht geladen werden
Die Import-Hooks werden weder aufgerufen noch aus der Registrierung entfernt. Es ist möglicherweise möglich, das Modul später zu laden.
Ein Hook ist registriert, aber das Modul ist bereits geladen
Der Hook wird sofort ausgelöst.
Invarianten
Das Import-Hook-System garantiert bestimmte Invarianten. XXX
Beispiel-Python-Implementierung
Eine Python-Implementierung könnte wie folgt aussehen:
def notify(name):
try:
module = sys.modules[name]
except KeyError:
raise ImportError("Module %s has not been imported" % (name,))
if module.__notified__:
return
try:
module.__notified__ = True
if '.' in name:
notify(name[:name.rfind('.')])
for callback in post_import_hooks[name]:
callback(module)
finally:
post_import_hooks[name] = None
XXX
C API
Neue C-API-Funktionen
PyObject* PyImport_GetPostImportHooks(void)- Gibt das Dictionary sys.post_import_hooks oder NULL zurück.
PyObject* PyImport_NotifyLoadedByModule(PyObject *module)- Benachrichtigt das Post-Import-System, dass ein Modul angefordert wurde. Gibt eine ausgeliehene Referenz auf dasselbe Modulobjekt zurück oder NULL, wenn ein Fehler aufgetreten ist. Die Funktion ruft nur die Hooks für das Modul selbst und nicht für seine Eltern auf. Die Funktion muss mit der erworbenen Import-Sperre aufgerufen werden.
PyObject* PyImport_NotifyLoadedByName(const char *name)PyImport_NotifyLoadedByName("a.b.c")ruftPyImport_NotifyLoadedByModule()füra,a.bunda.b.cin dieser Reihenfolge auf. Die Module werden aussys.modulesabgerufen. Wenn ein Modul nicht abgerufen werden kann, wird eine Ausnahme ausgelöst, andernfalls wird eine ausgeliehene Referenz aufmodnamezurückgegeben. Die Hook-Aufrufe beginnen immer mit dem primären Elternmodul. Der Aufrufer von PyImport_NotifyLoadedByName() muss die Import-Sperre halten!PyObject* PyImport_RegisterPostImportHook(PyObject *callable, PyObject *mod_name)- Registriert einen neuen Hook
callablefür das Modulmod_name int PyModule_GetNotified(PyObject *module)- Gibt den Status des
__notified__Slots / Attributs zurück. int PyModule_SetNotified(PyObject *module, int status)- Setzt den Status des
__notified__Slots / Attributs.
Die Methode PyImport_NotifyLoadedByModule() wird innerhalb von import_submodule() aufgerufen. Das Import-System stellt sicher, dass die Import-Sperre erworben wird und die Hooks für die übergeordneten Module bereits aufgerufen wurden.
Python API
Die Import-Hook-Registrierung und zwei neue API-Methoden werden über das sys- und imp-Modul freigegeben.
sys.post_import_hooks- Das Dictionary enthält die Post-Import-Hooks.
{"name" : [hook1, hook2], ...}
imp.register_post_import_hook(hook: "callable", name: str)- Registriert einen neuen Hook hook für das Modul name.
imp.notify_module_loaded(module: "module instance") -> module- Benachrichtigt das System, dass ein Modul geladen wurde. Die Methode wird zur Kompatibilität mit bestehenden Lazy-/Deferred-Import-Erweiterungen bereitgestellt.
module.__notified__- Ein Slot einer Modulinstanz. XXX
Die Funktion when_imported ist ebenfalls im imp-Modul enthalten, was äquivalent ist zu
def when_imported(name):
def register(hook):
register_post_import_hook(hook, name)
return register
- imp.when_imported(name) -> decorator function
- for @when_imported(name) def hook(module): pass
Offene Themen
Der when_imported-Decorator wurde noch nicht geschrieben.
Der Code enthält mehrere XXX-Kommentare. Sie beziehen sich hauptsächlich auf die Fehlerbehandlung in Randfällen.
Abwärtskompatibilität
Die neuen Funktionen und die API stehen nicht im Konflikt mit dem alten Importsystem von Python und verursachen für die meisten Programme keine Rückwärtskompatibilitätsprobleme. Systeme wie PEAK und Zope, die ihre eigene Lazy-Import-Magie implementieren, müssen jedoch einige Regeln befolgen.
Die Post-Import-Hooks wurden sorgfältig entwickelt, um mit bestehenden Deferred- und Lazy-Import-Systemen zusammenzuarbeiten. Es ist der Vorschlag des PEP-Autors, eigene On-Load-Hooks durch die neue Hook-API zu ersetzen. Die alternativen Lazy- oder Deferred-Imports funktionieren weiterhin, aber die Implementierungen müssen die Funktion imp.notify_module_loaded aufrufen.
Referenzimplementierung
Eine Referenzimplementierung wurde bereits geschrieben und ist im py3k-importhook-Branch verfügbar. [4] Sie erfordert noch einige Bereinigungen, Aktualisierungen der Dokumentation und zusätzliche Unit-Tests.
Danksagungen
Alyssa Coghlan, für Korrekturlesen und die anfängliche Diskussion Phillip J. Eby, für seine Implementierung in PEAK und Hilfe bei meiner eigenen Implementierung
Urheberrecht
Dieses Dokument wurde gemeinfrei erklärt.
Referenzen
Source: https://github.com/python/peps/blob/main/peps/pep-0369.rst
Last modified: 2025-02-01 08:59:27 GMT