PEP 304 – Steuerung der Erzeugung von Bytecode-Dateien
- Autor:
- Skip Montanaro
- Status:
- Zurückgezogen
- Typ:
- Standards Track
- Erstellt:
- 22. Jan. 2003
- Post-History:
- 27. Jan. 2003, 31. Jan. 2003, 17. Jun. 2005
Inhaltsverzeichnis
Historischer Hinweis
Obwohl diese ursprüngliche PEP zurückgezogen wurde, wurde eine Variante dieser Funktion schließlich für Python 3.8 in https://bugs.python.org/issue33499 implementiert.
Mehrere der ursprünglich in dieser PEP aufgeworfenen Probleme und Bedenken wurden in den dazwischen liegenden Jahren durch andere Änderungen behoben.
- die Einführung des isolierten Modus zur Bewältigung potenzieller Sicherheitsbedenken
- der Wechsel zu
importlib, einer vollständigen Import-Hook-basierten Implementierung des Import-Systems - PEP 3147s Änderung des Layouts des Bytecode-Caches zur Verwendung von Unterverzeichnissen
__pycache__, einschließlich dersource_to_cache(path)undcache_to_source(path)APIs, die es dem Interpreter ermöglichen, die Umleitung zu einem separaten Cache-Verzeichnis automatisch zu handhaben.
Zusammenfassung
Diese PEP skizziert einen Mechanismus zur Steuerung der Erzeugung und des Speicherorts von kompilierten Python-Bytecode-Dateien. Diese Idee entstand ursprünglich als Patch-Anfrage [1] und entwickelte sich zu einem Diskussionsstrang auf der python-dev Mailingliste [2]. Die Einführung einer Umgebungsvariable wird es Personen, die Python oder Python-basierte Drittanbieterpakete installieren, ermöglichen zu steuern, ob Bytecode-Dateien zur Installationszeit generiert werden sollen und, falls ja, wohin sie geschrieben werden sollen. Sie wird es auch Benutzern ermöglichen zu steuern, ob Bytecode-Dateien zur Laufzeit von Anwendungen generiert werden sollen und, falls ja, wohin sie geschrieben werden sollen.
Vorschlag
Fügen Sie eine neue Umgebungsvariable, PYTHONBYTECODEBASE, zu den Umgebungsvariablen hinzu, die Python versteht. PYTHONBYTECODEBASE wird wie folgt interpretiert:
- Wenn nicht definiert, wird Python-Bytecode genauso generiert wie derzeit. sys.bytecodebase wird auf das Stammverzeichnis gesetzt (entweder / unter Unix und Mac OSX oder das Stammverzeichnis des Start- (Installations-???) Laufwerks – typischerweise
C:\– unter Windows). - Wenn definiert und es sich auf ein vorhandenes Verzeichnis bezieht, auf das der Benutzer Schreibberechtigung hat, wird sys.bytecodebase auf dieses Verzeichnis gesetzt und Bytecode-Dateien werden in eine Verzeichnisstruktur geschrieben, die an diesem Ort verwurzelt ist.
- Wenn definiert, aber leer, wird sys.bytecodebase auf None gesetzt und die Erzeugung von Bytecode-Dateien wird vollständig unterdrückt.
- Wenn definiert und eine der folgenden Bedingungen zutrifft:
- es bezieht sich nicht auf ein Verzeichnis,
- es bezieht sich auf ein Verzeichnis, aber nicht auf eines, für das der Benutzer Schreibberechtigung hat
wird eine Warnung angezeigt, sys.bytecodebase wird auf None gesetzt und die Erzeugung von Bytecode-Dateien wird vollständig unterdrückt.
Nach der Startinitialisierung beziehen sich alle Laufzeitreferenzen auf sys.bytecodebase, nicht auf die Umgebungsvariable PYTHONBYTECODEBASE. sys.path wird nicht geändert.
Aus dem Vorhergehenden sehen wir, dass sys.bytecodebase nur zwei gültige Arten von Werten annehmen kann: None oder eine Zeichenkette, die sich auf ein gültiges Verzeichnis im System bezieht.
Beim Import funktioniert diese Erweiterung wie folgt:
- Die normale Suche nach einem Modul wird durchgeführt. Die Suchreihenfolge ist ungefähr: dynamisch geladenes Erweiterungsmodul, Python-Quelldatei, Python-Bytecode-Datei. Die einzige Zeit, in der dieser Mechanismus zum Tragen kommt, ist, wenn eine Python-Quelldatei gefunden wird.
- Sobald ein Quellmodul gefunden wurde, wird versucht, eine Byte-kompilierte Datei im selben Verzeichnis zu lesen. (Dies ist dasselbe wie zuvor.)
- Wenn keine Byte-kompilierte Datei gefunden wird, wird versucht, eine Byte-kompilierte Datei aus dem erweiterten Verzeichnis zu lesen.
- Wenn Bytecode-Erzeugung erforderlich ist, wird der generierte Bytecode, wenn möglich, in das erweiterte Verzeichnis geschrieben.
Beachten Sie, dass diese PEP ausdrücklich *nicht* die Modul-für-Modul- oder Verzeichnis-für-Verzeichnis-Kontrolle über die Behandlung von Bytecode-Dateien betrifft.
Glossar
- „Bytecode-Basis“ bezieht sich auf die aktuelle Einstellung von sys.bytecodebase.
- „Erweitertes Verzeichnis“ bezieht sich auf das Verzeichnis, das aus der Bytecode-Basis und dem Verzeichnisnamen der Quelldatei gebildet wird.
- PYTHONBYTECODEBASE bezieht sich auf die Umgebungsvariable, wenn dies notwendig ist, um sie von „Bytecode-Basis“ zu unterscheiden.
Auffinden von Bytecode-Dateien
Wenn der Interpreter nach einem Modul sucht, verwendet er wie üblich sys.path. Wenn jedoch eine mögliche Bytecode-Datei berücksichtigt wird, kann eine zusätzliche Suche nach einer Bytecode-Datei durchgeführt werden. Zuerst wird die Bytecode-Datei unter Verwendung des Verzeichnisses in sys.path, das die Quelldatei enthält (das aktuelle Verhalten), überprüft. Wenn dort keine gültige Bytecode-Datei gefunden wird (entweder existiert keine oder sie ist veraltet) und die Bytecode-Basis nicht None ist, wird eine zweite Suche unter Verwendung des Verzeichnisses in sys.path, das entsprechend mit der Bytecode-Basis präfixiert ist, durchgeführt.
Schreiben von Bytecode-Dateien
Wenn die Bytecode-Basis nicht None ist, wird eine neue Bytecode-Datei in das entsprechende erweiterte Verzeichnis geschrieben, niemals direkt in ein Verzeichnis in sys.path.
Definition von erweiterten Verzeichnissen
Konzeptionell ist das erweiterte Verzeichnis für eine Bytecode-Datei das Verzeichnis, in dem sich die Quelldatei befindet, präfixiert mit der Bytecode-Basis. In einer Unix-Umgebung wäre dies:
pcb = os.path.abspath(sys.bytecodebase)
if sourcefile[0] == os.sep: sourcefile = sourcefile[1:]
augdir = os.path.join(pcb, os.path.dirname(sourcefile))
Unter Windows, das keinen einheitlichen Verzeichnisbaum hat, wird der Laufwerksbuchstabe des Verzeichnisses, das die Quelldatei enthält, als Verzeichnisbestandteil behandelt, nachdem der nachgestellte Doppelpunkt entfernt wurde. Das erweiterte Verzeichnis wird somit abgeleitet als:
pcb = os.path.abspath(sys.bytecodebase)
drive, base = os.path.splitdrive(os.path.dirname(sourcefile))
drive = drive[:-1]
if base[0] == "\\": base = base[1:]
augdir = os.path.join(pcb, drive, base)
Festlegen des Speicherorts der Bytecode-Basis
Während des Programmstarts wird der Wert der Umgebungsvariablen PYTHONBYTECODEBASE absolut gemacht, auf Gültigkeit geprüft und zum sys-Modul hinzugefügt, effektiv:
pcb = os.path.abspath(os.environ["PYTHONBYTECODEBASE"])
probe = os.path.join(pcb, "foo")
try:
open(probe, "w")
except IOError:
sys.bytecodebase = None
else:
os.unlink(probe)
sys.bytecodebase = pcb
Dies ermöglicht es dem Benutzer, die Bytecode-Basis als relativen Pfad anzugeben, aber sie während der Programmausführung keinen Änderungen am aktuellen Arbeitsverzeichnis auszusetzen. (Ich kann mir nicht vorstellen, dass man möchte, dass sie sich während der Programmausführung verschiebt.)
An sys.bytecodebase ist nichts Besonderes. Der Benutzer kann es zur Laufzeit ändern, wenn gewünscht, aber normalerweise wird es nicht geändert.
Begründung
In vielen Umgebungen ist es für Nicht-Root-Benutzer nicht möglich, in Verzeichnisse zu schreiben, die Python-Quelldateien enthalten. Die meiste Zeit ist dies kein Problem, da Python-Quellcode im Allgemeinen während der Installation Byte-kompiliert wird. Es gibt jedoch Situationen, in denen Bytecode-Dateien entweder fehlen oder aktualisiert werden müssen. Wenn das Verzeichnis, das die Quelldatei enthält, für den aktuellen Benutzer nicht beschreibbar ist, wird jedes Mal, wenn ein Programm, das das Modul importiert, ausgeführt wird, eine Leistungsbuße fällig. [3] Unter bestimmten Umständen können auch Warnmeldungen generiert werden. Wenn das Verzeichnis beschreibbar ist, können fast gleichzeitige Versuche, die Bytecode-Datei von zwei separaten Prozessen zu schreiben, auftreten, was zu Dateibeschädigungen führt. [4]
In Umgebungen mit verfügbaren RAM-Disks kann es aus Leistungsgründen wünschenswert sein, Bytecode-Dateien in ein Verzeichnis auf einer solchen Festplatte zu schreiben. Ebenso kann es in Umgebungen, in denen Python-Quellcode auf Netzwerklaufwerken liegt, wünschenswert sein, Bytecode-Dateien auf lokalen Festplatten zu cachen.
Alternativen
Die einzige andere bisher vorgeschlagene Alternative [1] scheint die Hinzufügung einer -R Option zum Interpreter zu sein, um das Schreiben von Bytecode-Dateien vollständig zu deaktivieren. Dieser Vorschlag subsumiert dies. Das Hinzufügen einer Kommandozeilenoption ist sicherlich möglich, aber wahrscheinlich nicht ausreichend, da die Kommandozeile des Interpreters während der Installation (früh während des Programmstarts???) nicht leicht verfügbar ist.
Probleme
- Interpretation des __file__-Attributs eines Moduls. Ich glaube, dass das __file__-Attribut eines Moduls den tatsächlichen Speicherort der Bytecode-Datei widerspiegeln sollte. Wenn Leute den Quellcode eines Moduls finden wollen, sollten sie imp.find_module(module) verwenden.
- Sicherheit – Was passiert, wenn root PYTHONBYTECODEBASE gesetzt hat? Ja, dies kann ein Sicherheitsrisiko darstellen, aber viele andere Dinge, die der Root-Benutzer tut, auch. Der Root-Benutzer sollte PYTHONBYTECODEBASE wahrscheinlich nicht setzen, außer vielleicht während der Installation. Dennoch kann dieses Problem möglicherweise minimiert werden. Wenn der Interpreter als Root ausgeführt wird, sollte er prüfen, ob PYTHONBYTECODEBASE auf ein Verzeichnis verweist, das für jeden außer Root beschreibbar ist. Wenn dies der Fall ist, könnte er eine Ausnahme oder Warnung auslösen und sys.bytecodebase auf None setzen. Oder siehe nächster Punkt.
- Mehr Sicherheit – Was passiert, wenn PYTHONBYTECODEBASE auf ein allgemeines Verzeichnis (z. B. /tmp) verweist? In diesem Fall sollte das Laden einer bereits vorhandenen Bytecode-Datei nur dann erfolgen, wenn die Datei dem aktuellen Benutzer oder Root gehört. (Spielt das unter Windows eine Rolle?)
- Die Interaktion dieser PEP mit Import-Hooks wurde noch nicht berücksichtigt. Tatsächlich könnte der beste Weg, diese Idee zu implementieren, als Import-Hook sein. Siehe PEP 302.
- In der aktuellen (vor PEP 304) Umgebung ist es sicher, eine Quelldatei nach der Erstellung der entsprechenden Bytecode-Datei zu löschen, da sie sich im selben Verzeichnis befinden. Mit PEP 304, wie sie derzeit definiert ist, ist dies nicht der Fall. Eine Bytecode-Datei im erweiterten Verzeichnis wird nur berücksichtigt, wenn die Quelldatei vorhanden ist, und wird daher beim Suchen nach Moduldateien, die auf „.pyc“ enden, nie berücksichtigt. Ich denke, dieses Verhalten muss sich möglicherweise ändern.
Beispiele
In den folgenden Beispielen befindet sich der Quellcode von urllib in /usr/lib/python2.3/urllib.py und /usr/lib/python2.3 ist in sys.path, ist aber für den aktuellen Benutzer nicht beschreibbar.
- Die Bytecode-Basis ist /tmp. /usr/lib/python2.3/urllib.pyc existiert und ist gültig. Wenn urllib importiert wird, werden die Inhalte von /usr/lib/python2.3/urllib.pyc verwendet. Das erweiterte Verzeichnis wird nicht konsultiert. Es wird keine andere Bytecode-Datei generiert.
- Die Bytecode-Basis ist /tmp. /usr/lib/python2.3/urllib.pyc existiert, ist aber veraltet. Wenn urllib importiert wird, wird die generierte Bytecode-Datei nach urllib.pyc im erweiterten Verzeichnis geschrieben, das den Wert /tmp/usr/lib/python2.3 hat. Zwischenverzeichnisse werden bei Bedarf erstellt.
- Die Bytecode-Basis ist None. Es wird keine urllib.pyc-Datei gefunden. Wenn urllib importiert wird, wird keine Bytecode-Datei geschrieben.
- Die Bytecode-Basis ist /tmp. Es wird keine urllib.pyc-Datei gefunden. Wenn urllib importiert wird, wird die generierte Bytecode-Datei in das erweiterte Verzeichnis geschrieben, das den Wert /tmp/usr/lib/python2.3 hat. Zwischenverzeichnisse werden bei Bedarf erstellt.
- Beim Start ist PYTHONBYTECODEBASE /tmp/foobar, das nicht existiert. Eine Warnung wird ausgegeben, sys.bytecodebase wird auf None gesetzt und es werden während der Programmausführung keine Bytecode-Dateien geschrieben, es sei denn, sys.bytecodebase wird später geändert, um auf ein gültiges, beschreibbares Verzeichnis zu verweisen.
- Beim Start ist PYTHONBYTECODEBASE auf / gesetzt, das existiert, aber für den aktuellen Benutzer nicht beschreibbar ist. Eine Warnung wird ausgegeben, sys.bytecodebase wird auf None gesetzt und es werden während der Programmausführung keine Bytecode-Dateien geschrieben, es sei denn, sys.bytecodebase wird später geändert, um auf ein gültiges, beschreibbares Verzeichnis zu verweisen. Beachten Sie, dass, obwohl das für eine bestimmte Bytecode-Datei konstruierte erweiterte Verzeichnis für den aktuellen Benutzer beschreibbar sein mag, das entscheidende Kriterium ist, dass das Bytecode-Basisverzeichnis selbst beschreibbar ist.
- Beim Start ist PYTHONBYTECODEBASE auf die leere Zeichenkette gesetzt. sys.bytecodebase wird auf None gesetzt. Es wird jedoch keine Warnung generiert. Wenn beim Importieren von urllib keine urllib.pyc-Datei gefunden wird, wird keine Bytecode-Datei geschrieben.
In den folgenden Windows-Beispielen befindet sich der Quellcode von urllib in C:\PYTHON22\urllib.py. C:\PYTHON22 ist in sys.path, ist aber für den aktuellen Benutzer nicht beschreibbar.
- Die Bytecode-Basis ist auf
C:\TEMPgesetzt.C:\PYTHON22\urllib.pycexistiert und ist gültig. Wenn urllib importiert wird, werden die Inhalte vonC:\PYTHON22\urllib.pycverwendet. Das erweiterte Verzeichnis wird nicht konsultiert. - Die Bytecode-Basis ist auf
C:\TEMPgesetzt.C:\PYTHON22\urllib.pycexistiert, ist aber veraltet. Wenn urllib importiert wird, wird eine neue Bytecode-Datei in das erweiterte Verzeichnis geschrieben, das den WertC:\TEMP\C\PYTHON22hat. Zwischenverzeichnisse werden bei Bedarf erstellt. - Beim Start ist PYTHONBYTECODEBASE auf
TEMPgesetzt und das aktuelle Arbeitsverzeichnis beim Programmstart istH:\NET. Die potenzielle Bytecode-Basis ist daherH:\NET\TEMP. Wenn dieses Verzeichnis existiert und für den aktuellen Benutzer beschreibbar ist, wird sys.bytecodebase auf diesen Wert gesetzt. Andernfalls wird eine Warnung ausgegeben und sys.bytecodebase auf None gesetzt. - Die Bytecode-Basis ist
C:\TEMP. Es wird keine urllib.pyc-Datei gefunden. Wenn urllib importiert wird, wird die generierte Bytecode-Datei in das erweiterte Verzeichnis geschrieben, das den WertC:\TEMP\C\PYTHON22hat. Zwischenverzeichnisse werden bei Bedarf erstellt.
Implementierung
Siehe den Patch auf Sourceforge. [6]
Referenzen
Urheberrecht
Dieses Dokument wurde gemeinfrei erklärt.
Quelle: https://github.com/python/peps/blob/main/peps/pep-0304.rst
Zuletzt geändert: 2025-02-01 08:59:27 GMT