Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python Enhancement Proposals

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") ruft PyImport_NotifyLoadedByModule() für a, a.b und a.b.c in dieser Reihenfolge auf. Die Module werden aus sys.modules abgerufen. Wenn ein Modul nicht abgerufen werden kann, wird eine Ausnahme ausgelöst, andernfalls wird eine ausgeliehene Referenz auf modname zurü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 callable für das Modul mod_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

Referenzen


Source: https://github.com/python/peps/blob/main/peps/pep-0369.rst

Last modified: 2025-02-01 08:59:27 GMT