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

Python Enhancement Proposals

PEP 397 – Python-Launcher für Windows

Autor:
Mark Hammond <mhammond at skippinet.com.au>, Martin von Löwis <martin at v.loewis.de>
Status:
Final
Typ:
Standards Track
Erstellt:
15-März-2011
Python-Version:
3.3
Post-History:
21-Jul-2011, 17-Mai-2011, 15-März-2011
Resolution:
Python-Dev Nachricht

Inhaltsverzeichnis

Zusammenfassung

Dieser PEP beschreibt einen Python-Launcher für die Windows-Plattform. Ein Python-Launcher ist eine einzelne ausführbare Datei, die eine Reihe von Heuristiken verwendet, um eine Python-ausführbare Datei zu lokalisieren und diese mit einer angegebenen Kommandozeile zu starten.

Begründung

Windows bietet „Dateizuordnungen“, sodass eine ausführbare Datei mit einer Dateiendung verknüpft werden kann, was die direkte Ausführung von Skripten in einigen Kontexten ermöglicht (z. B. Doppelklick auf die Datei im Windows Explorer). Bisher wurde die Strategie „zuletzt installierte Python-Version gewinnt“ angewendet, und obwohl nicht ideal, war sie aufgrund der konservativen Änderungen in Python 2.x-Releases im Allgemeinen praktikabel. Da Python 3.x-Skripte oft syntaktisch inkompatibel mit Python 2.x-Skripten sind, muss eine andere Strategie verwendet werden, um Dateien mit der Endung „.py“ die Verwendung einer anderen ausführbaren Datei zu ermöglichen, basierend auf der Python-Version, die das Skript anvisiert. Dies geschieht durch Übernahme bestehender Praktiken eines anderen Betriebssystems – Skripte können die benötigte Python-Version über eine „Shebang“-Zeile angeben, wie unten beschrieben.

Unix-ähnliche Betriebssysteme (im Folgenden in diesem PEP einfach als „Unix“ bezeichnet) erlauben die Ausführung von Skripten, als wären sie ausführbare Images, indem sie das Skript auf eine „Shebang“-Zeile untersuchen, die die tatsächliche ausführbare Datei angibt, die zum Ausführen des Skripts verwendet werden soll. Dies ist im Detail im Handbuch execve(2) [1] beschrieben, und obwohl für diese Funktion eine Benutzerdokumentation erstellt wird, beschreibt dieses Handbuch für die Zwecke dieses PEP eine gültige Shebang-Zeile.

Darüber hinaus bieten diese Betriebssysteme symbolische Links zu Python-ausführbaren Dateien in bekannten Verzeichnissen. Zum Beispiel werden viele Systeme einen Link /usr/bin/python haben, der auf eine bestimmte Version von Python verweist, die unter dem Betriebssystem installiert ist. Diese symbolischen Links ermöglichen die Ausführung von Python, unabhängig davon, wo Python auf dem Computer installiert ist (d. h. ohne dass der Pfad, an dem Python tatsächlich installiert ist, in der Shebang-Zeile oder in der PATH-Umgebungsvariablen referenziert werden muss). PEP 394 „Der „python“-Befehl auf Unix-ähnlichen Systemen“ beschreibt zusätzliche Konventionen für eine feingranulare Angabe einer bestimmten Python-Version.

Diese beiden kombinierten Einrichtungen ermöglichen eine portable und einigermaßen vorhersagbare Möglichkeit, sowohl Python interaktiv zu starten als auch Python-Skripte auszuführen. Dieser PEP beschreibt eine Implementierung eines Launchers, der dieselben Vorteile für Python auf der Windows-Plattform bieten kann und somit dem Launcher ermöglicht, die ausführbare Datei zu sein, die mit '.py'-Dateien verknüpft ist, um mehrere Python-Versionen gleichzeitig zu unterstützen.

Obwohl dieser PEP die Möglichkeit bietet, eine Shebang-Zeile zu verwenden, die sowohl unter Windows als auch unter Unix funktionieren sollte, ist dies nicht die Hauptmotivation dieses PEP – die Hauptmotivation ist es, die Angabe einer bestimmten Version zu ermöglichen, ohne neue Syntax oder Konventionen zu erfinden, um sie zu beschreiben.

Spezifikation

Dieser PEP spezifiziert die Funktionen des Launchers; eine Prototypimplementierung ist in [3] enthalten, die zusammen mit dem Windows-Installer von Python verteilt wird, aber auch separat erhältlich sein wird (aber zusammen mit dem Python-Installer veröffentlicht wird). Neue Funktionen können dem Launcher hinzugefügt werden, solange die hier vorgeschriebenen Funktionen weiterhin funktionieren.

Installation

Der Launcher wird in 2 Versionen geliefert – eine als Konsolenprogramm und eine als „Windows“-Programm (d. h. GUI). Diese beiden Launcher entsprechen den ausführbaren Dateien 'python.exe' und 'pythonw.exe', die derzeit mit Python ausgeliefert werden. Der Konsolen-Launcher wird 'py.exe' und der Windows-Launcher 'pyw.exe' heißen. Die „Windows“-Version (d. h. GUI) des Launchers versucht, pythonw.exe zu finden und zu starten, auch wenn eine virtuelle Shebang-Zeile lediglich „python“ angibt – tatsächlich wird die nachgestellte „w“-Notation in der virtuellen Shebang-Zeile überhaupt nicht unterstützt.

Der Launcher wird in das Windows-Verzeichnis installiert (siehe Diskussion unten), wenn er von einem berechtigten Benutzer installiert wird. Der eigenständige Installer fragt nach einem alternativen Installationsort und fügt diesen Ort zum PATH des Benutzers hinzu.

Die Installation im Windows-Verzeichnis ist eine 32-Bit-Ausführungsdatei (siehe Diskussion); der eigenständige Installer kann auch anbieten, 64-Bit-Versionen des Launchers zu installieren.

Die Installation des Launchers wird in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\CurrentVersion\SharedDLLs mit einem Referenzzähler registriert. Sie enthält eine Versionsressource, die mit der Versionsnummer der PythonXY.dll übereinstimmt, mit der sie verteilt wird. Unabhängige Installationen überschreiben ältere Versionen des Launchers mit neueren Versionen. Eigenständige Releases verwenden ein Release-Level von 0x10 in FIELD3 der CPython-Version, auf der sie basieren.

Nach der Installation wird die „Konsolen“-Version des Launchers mit .py-Dateien und die „Windows“-Version mit .pyw-Dateien verknüpft.

Der Launcher ist nicht an eine bestimmte Python-Version gebunden – z. B. sollte ein mit Python 3.3 verteilter Launcher jede Python 2.x- und Python 3.x-Version lokalisieren und ausführen können. Die Binärdateien des Launchers haben jedoch eine Versionsressource, die mit der Versionsressource in den Python-Binärdateien übereinstimmt, mit denen sie veröffentlicht werden.

Starten von Python-Skripten

Der Launcher ist auf das Starten von Python-Skripten beschränkt. Er ist nicht als allgemeiner Skript-Launcher oder Shebang-Prozessor gedacht.

Der Launcher unterstützt die Syntax von Shebang-Zeilen, wie in [1] beschrieben, einschließlich aller aufgeführten Einschränkungen.

Der Launcher unterstützt Shebang-Zeilen, die auf Python-ausführbare Dateien mit den Präfixen „/usr/bin/“, „/usr/local/bin“ und „/usr/bin/env *“ verweisen, sowie auf ausführbare Dateien, die ohne diese Präfixe angegeben sind.

Zum Beispiel sollte eine Shebang-Zeile wie ‚#! /usr/bin/python‘ funktionieren, auch wenn unwahrscheinlich ist, dass eine ausführbare Datei im relativen Windows-Verzeichnis „\usr\bin“ existiert. Dies bedeutet, dass viele Skripte eine einzelne Shebang-Zeile verwenden können und wahrscheinlich unter Windows und Unix ohne Änderung funktionieren.

Der Launcher unterstützt vollständig qualifizierte Pfade zu ausführbaren Dateien. Obwohl dies das Skript inhärent nicht portabel macht, ist es eine Funktion, die von Unix angeboten wird und für Windows-Benutzer in einigen Fällen nützlich wäre.

Der Launcher kann Implementierungen außer CPython unterstützen, wie z. B. Jython und IronPython. Da jedoch auf Unix keine gemeinsamen Links (wie „/usr/bin/jython“) vorhanden sind und der Launcher die Installationsorte dieser Implementierungen unter Windows nicht automatisch finden kann, unterstützt der Launcher dies über Anpassungsoptionen. Skripte, die dies nutzen, werden nicht portabel sein (da diese Anpassungsoptionen so eingestellt werden müssen, dass sie die Konfiguration des Rechners widerspiegeln, auf dem der Launcher läuft), aber diese Fähigkeit wird dennoch als lohnenswert erachtet.

Unter Unix kann der Benutzer steuern, welche spezifische Python-Version verwendet wird, indem er die Links in /usr/bin an die gewünschte Version anpasst. Da der Launcher unter Windows keine Windows-Links verwendet, werden Anpassungsoptionen (die sowohl über Umgebungsvariablen als auch INI-Dateien zugänglich sind) verwendet, um die Semantik für die Bestimmung der zu verwendenden Python-Version zu überschreiben. Zum Beispiel wird, während eine Shebang-Zeile wie „/usr/bin/python2“ automatisch eine Python 2.x-Implementierung findet, eine Umgebungsvariable genau festlegen können, welche Python 2.x-Implementierung ausgewählt wird. Gleiches gilt für „/usr/bin/python“ und „/usr/bin/python3“. Dies wird später in diesem PEP detailliert beschrieben.

Shebang-Zeilen-Parsing

Wenn das erste Kommandozeilenargument nicht mit einem Bindestrich („-“) beginnt, wird versucht, dieses Argument als Datei zu öffnen und gemäß den Regeln in [1] auf eine Shebang-Zeile zu parsen.

#! interpreter [optional-arg]

Nach dem Parsen wird der Befehl nach folgenden Regeln kategorisiert:

  • Wenn der Befehl mit der Definition eines benutzerdefinierten Befehls gefolgt von einem Leerzeichen (einschließlich eines Zeilenumbruchs) beginnt, wird der benutzerdefinierte Befehl verwendet. Siehe unten für eine Beschreibung von benutzerdefinierten Befehlen.
  • Der Launcher definiert eine Reihe von Präfixen, die als Unix-kompatible Befehle zum Starten von Python betrachtet werden, nämlich „/usr/bin/python“, „/usr/local/bin/python“, „/usr/bin/env python“ und „python“. Wenn ein Befehl mit einer dieser Zeichenketten beginnt, wird er als „virtueller Befehl“ behandelt und die in Python-Versionsqualifizierern (unten) beschriebenen Regeln werden verwendet, um die ausführbare Datei zu lokalisieren.
  • Andernfalls wird angenommen, dass der Befehl direkt ausführbar ist, d. h. ein vollständig qualifizierter Pfad (oder ein Verweis auf eine ausführbare Datei in der PATH-Umgebungsvariable), optional gefolgt von Argumenten. Der Inhalt der Zeichenkette wird nicht geparst – er wird direkt an die Windows CreateProcess-Funktion übergeben, nachdem der Name des Skripts und die Kommandozeilenargumente des Launchers angehängt wurden. Dies bedeutet, dass die von CreateProcess verwendeten Regeln angewendet werden, einschließlich der Behandlung von relativen Pfadnamen und ausführbaren Referenzen ohne Erweiterungen. Beachten Sie, dass der Windows-Befehlsinterpreter nicht verwendet wird, sodass spezielle Regeln des Befehlsinterpreters (wie das automatische Anhängen von Erweiterungen außer „.exe“, die Unterstützung für Batch-Dateien usw.) nicht verwendet werden.

Die Verwendung von „virtuellen“ Shebang-Zeilen wird empfohlen, da dies die Angabe portabler Shebang-Zeilen ermöglichen sollte, die auf mehreren Betriebssystemen und unterschiedlichen Installationen desselben Betriebssystems funktionieren.

Wenn das erste Argument nicht als Datei geöffnet werden kann oder wenn keine gültige Shebang-Zeile gefunden wird, verhält sich der Launcher so, als wäre eine Shebang-Zeile von ‚#!python‘ gefunden worden – d. h. ein Standard-Python-Interpreter wird gesucht und die Argumente werden an diesen übergeben. Wenn jedoch eine gültige Shebang-Zeile gefunden wird, der durch diese Zeile angegebene Prozess aber nicht gestartet werden kann, wird der Standard-Interpreter nicht gestartet – der Fehler beim Erstellen des angegebenen Kindprozesses bewirkt, dass der Launcher eine entsprechende Meldung anzeigt und mit einem spezifischen Exit-Code beendet wird.

Konfigurationsdatei

Zwei .ini-Dateien werden vom Launcher durchsucht: py.ini im „Application Data“-Verzeichnis des aktuellen Benutzers (d. h. das Verzeichnis, das durch Aufruf der Windows-Funktion SHGetFolderPath mit CSIDL_LOCAL_APPDATA zurückgegeben wird, %USERPROFILE%\AppData\Local unter Vista+, %USERPROFILE%\Local Settings\Application Data unter XP) und py.ini im selben Verzeichnis wie der Launcher. Dieselben .ini-Dateien werden sowohl für die „Konsolen“-Version des Launchers (d. h. py.exe) als auch für die „Windows“-Version (d. h. pyw.exe) verwendet.

Die im „Application Directory“ angegebene Anpassung hat Vorrang vor der neben der ausführbaren Datei, sodass ein Benutzer, der möglicherweise keine Schreibrechte für die .ini-Datei neben dem Launcher hat, Befehle in dieser globalen .ini-Datei überschreiben kann.

Virtuelle Befehle in Shebang-Zeilen

Virtuelle Befehle sind Shebang-Zeilen, die mit Zeichenfolgen beginnen, die unter Unix-Plattformen funktionieren sollten – Beispiele hierfür sind „/usr/bin/python“, „/usr/bin/env python“ und „python“. Optional kann der virtuelle Befehl mit einem Versionsqualifizierer (siehe unten) erweitert werden, wie z. B. „/usr/bin/python2“ oder „/usr/bin/python3.2“. Der ausgeführte Befehl basiert auf den Regeln, die unter Python-Versionsqualifizierern beschrieben sind.

Benutzerdefinierte Befehle

Der Launcher unterstützt die Möglichkeit, „benutzerdefinierte Befehle“ in einer Windows-.ini-Datei (d. h. einer Datei, die von der Windows-Funktion GetPrivateProfileString geparst werden kann) zu definieren. Ein Abschnitt namens ‚[commands]‘ kann mit Schlüsselnamen erstellt werden, die den virtuellen Befehl definieren, und dem Wert, der die tatsächliche Kommandozeile für diesen virtuellen Befehl angibt.

Wenn zum Beispiel eine INI-Datei den folgenden Inhalt hat

[commands]
vpython=c:\bin\vpython.exe -foo

Dann führt eine Shebang-Zeile wie ‚#! vpython‘ in einem Skript namens ‚doit.py‘ dazu, dass der Launcher die Kommandozeile c:\bin\vpython.exe -foo doit.py verwendet.

Die genauen Details zu den Namen, Speicherorten und der Suchreihenfolge der .ini-Dateien finden Sie in der Dokumentation des Launchers [4].

Python-Versionsqualifizierer

Einige der beschriebenen Funktionen ermöglichen die Verwendung eines optionalen Python-Versionsqualifizierers.

Ein Versionsqualifizierer beginnt mit einer Hauptversionsnummer und kann optional von einem Punkt („.“) und einer Nebenversionsangabe gefolgt werden. Wenn der Nebenqualifizierer angegeben ist, kann er optional von „-32“ gefolgt werden, um die 32-Bit-Implementierung dieser Version anzugeben. Beachten Sie, dass kein „-64“-Qualifizierer erforderlich ist, da dies die Standardimplementierung ist (siehe unten).

Unter 64-Bit-Windows mit sowohl 32-Bit- als auch 64-Bit-Implementierungen derselben (Haupt.Neben-) Python-Version wird immer die 64-Bit-Version bevorzugt. Dies gilt sowohl für 32-Bit- als auch für 64-Bit-Implementierungen des Launchers – ein 32-Bit-Launcher bevorzugt die Ausführung einer 64-Bit-Python-Installation der angegebenen Version, falls verfügbar. Dies dient dazu, das Verhalten des Launchers vorhersagbar zu machen, wenn nur die auf dem PC installierten Versionen bekannt sind, und unabhängig von der Reihenfolge ihrer Installation (d. h. ohne zu wissen, ob zuletzt eine 32- oder 64-Bit-Version von Python und der entsprechende Launcher installiert wurde). Wie oben erwähnt, kann ein optionales „-32“-Suffix auf einer Versionsangabe verwendet werden, um dieses Verhalten zu ändern.

Wenn keine Nebenversionsqualifizierer in einem Befehl gefunden werden, kann die Umgebungsvariable PY_PYTHON gesetzt werden, um den Standard-Versionsqualifizierer anzugeben – der Standardwert ist „2“. Beachten Sie, dass dieser Wert nur eine Hauptversion (z. B. „2“) oder einen Haupt.Neben-Qualifizierer (z. B. „2.6“) oder sogar Haupt.Neben-32 angeben kann.

Wenn keine Nebenversionsqualifizierer gefunden werden, kann die Umgebungsvariable PY_PYTHON{major} (wobei {major} der Hauptversionsqualifizierer ist, wie oben ermittelt) gesetzt werden, um die vollständige Version anzugeben. Wenn keine solche Option gefunden wird, listet der Launcher die installierten Python-Versionen auf und verwendet die neueste Nebenversion, die für die Hauptversion gefunden wurde, welche wahrscheinlich, aber nicht garantiert, die zuletzt installierte Version dieser Familie ist.

Zusätzlich zu Umgebungsvariablen können dieselben Einstellungen in der vom Launcher verwendeten .INI-Datei konfiguriert werden. Der Abschnitt in der INI-Datei heißt [defaults] und der Schlüsselname ist derselbe wie bei den Umgebungsvariablen ohne das führende PY_-Präfix (und beachten Sie, dass die Schlüsselnamen in der INI-Datei nicht zwischen Groß- und Kleinschreibung unterscheiden). Der Inhalt einer Umgebungsvariable überschreibt die in der INI-Datei angegebenen Werte.

Befehlszeilenverarbeitung

Nur das erste Kommandozeilenargument wird auf eine Shebang-Zeile geprüft und nur, wenn dieses Argument nicht mit einem ‚-‘ beginnt.

Wenn das einzige Kommandozeilenargument „-h“ oder „–help“ ist, gibt der Launcher ein kleines Banner und die Kommandozeilenverwendung aus und übergibt das Argument dann an die Standard-Python-Version. Dies führt dazu, dass die Hilfe für den Launcher gefolgt von der Hilfe für Python selbst ausgegeben wird. Die Ausgabe des Launchers wird klar darauf hinweisen, dass die erweiterten Hilfsinformationen vom Launcher und nicht von Python stammen.

Als Zugeständnis an das interaktive Starten von Python unterstützt der Launcher das erste Kommandozeilenargument optional als Bindestrich („-“) gefolgt von einem Versionsqualifizierer, wie oben beschrieben, um eine bestimmte Version zu benennen. Zum Beispiel kann „py.exe“ die neueste installierte Python 2.x-Implementierung finden und starten, während eine Kommandozeile wie „py.exe -3“ die neueste installierte Python 3.x-Implementierung angeben könnte, und „py.exe -2.6-32“ könnte eine 32-Bit-Implementierung von Python 2.6 angeben, die gesucht und gestartet werden soll. Wenn eine Python 2.x-Implementierung mit der -3-Flagge gestartet werden soll, müsste die Kommandozeile ähnlich wie „py.exe -2 -3“ lauten (oder die spezifische Python-Version könnte offensichtlich manuell ohne diesen Launcher gestartet werden). Beachten Sie, dass diese Funktion nicht mit Shebang-Verarbeitung verwendet werden kann, da die Datei, die auf eine Shebang-Zeile gescannt wird, und dieses Argument beide das erste Argument sein müssen und daher gegenseitig ausgeschlossen sind.

Alle anderen Argumente werden unverändert an den Kind-Python-Prozess übergeben.

Prozessstart

Der Launcher bietet einige Annehmlichkeiten für interaktiv arbeitende Python-Entwickler – zum Beispiel startet der Launcher ohne Kommandozeilenargumente die Standard-Python-Version ohne Kommandozeilenargumente. Darüber hinaus werden Kommandozeilenargumente unterstützt, um die interaktive Ausführung einer bestimmten Python-Version zu ermöglichen – diese Annehmlichkeiten dürfen jedoch den primären Zweck des Startens von Skripten nicht beeinträchtigen und müssen bei Bedarf leicht zu umgehen sein.

Der Launcher erstellt einen Subprozess, um den eigentlichen Interpreter zu starten. Die Begründung dafür finden Sie im Abschnitt **Diskussion** unten.

Diskussion

Es mag überraschend sein, dass der Launcher im Windows-Verzeichnis und nicht im System32-Verzeichnis installiert wird. Der Grund dafür ist, dass das System32-Verzeichnis auf einem 64-Bit-System nicht im Pfad eines 32-Bit-Prozesses enthalten ist. Das Windows-Verzeichnis ist jedoch immer im Pfad enthalten.

Der im Windows-Verzeichnis installierte Launcher ist eine 32-Bit-Ausführungsdatei, sodass der 32-Bit-CPython-Installer dasselbe Binärprogramm für sowohl 32-Bit- als auch 64-Bit-Windows-Installationen bereitstellen kann.

Idealerweise würde der Launcher-Prozess Python direkt innerhalb desselben Prozesses ausführen, hauptsächlich damit der Elternprozess des Launchers den Launcher beenden und den Python-Interpreter beenden lassen könnte. Wenn der Launcher Python als Subprozess ausführt und der Elternprozess des Launchers den Launcher beendet, bleibt der Python-Prozess unberührt.

Es gibt jedoch eine Reihe praktischer Probleme im Zusammenhang mit diesem Ansatz. Windows unterstützt nicht die Unix-Funktionsfamilie execv*, sodass dies nur durch dynamisches Laden der Python-DLL durch den Launcher geschehen könnte. Dies hätte jedoch eine Reihe von Nebeneffekten. Der schwerwiegendste Nebeneffekt ist, dass der Wert von sys.executable auf den Launcher und nicht auf die Python-Implementierung verweisen würde. Viele Python-Skripte verwenden den Wert von sys.executable, um Kindprozesse zu starten, und diese Skripte könnten fehlschlagen, wenn der Launcher verwendet wird. Betrachten Sie ein „Eltern“-Skript mit einer Shebang-Zeile von ‚#! /usr/bin/python3‘, das versucht, ein Kindskript (ohne Shebang) über sys.executable zu starten – derzeit wird das Kind mit derselben Version gestartet, die das Eltertskript ausführt. Wenn sys.executable auf den Launcher verweisen würde, würde das Kind wahrscheinlich mit einer Python 2.x-Version ausgeführt und wahrscheinlich mit einem SyntaxError fehlschlagen.

Eine weitere Hürde ist die Unterstützung alternativer Python-Implementierungen über die Funktion „benutzerdefinierte Befehle“, die oben beschrieben wurde, bei der das dynamische Laden des Befehls in eine laufende ausführbare Datei nicht möglich ist.

Die letzte Hürde sind die oben genannten Regeln bezüglich 64-Bit- und 32-Bit-Programmen – ein 32-Bit-Launcher könnte die 64-Bit-Version von Python nicht laden und umgekehrt.

Angesichts dieser Überlegungen wird der Launcher seinen Befehl in einem Kindprozess ausführen, lebendig bleiben, solange der Kindprozess ausgeführt wird, und dann mit demselben Exit-Code beenden, der vom Kind zurückgegeben wurde. Um Bedenken hinsichtlich der Beendigung des Launchers, die das Kind nicht beendet, zu zerstreuen, wird die Win32 Job API verwendet, um sicherzustellen, dass der Kindprozess automatisch beendet wird, wenn der Elternprozess beendet wird (obwohl Kinder dieses Kindprozesses wie bisher fortfahren werden). Da diese Windows-API ab Windows XP verfügbar ist, funktioniert dieser Launcher nicht unter Windows 2000 oder früher.

Referenzen


Quelle: https://github.com/python/peps/blob/main/peps/pep-0397.rst

Zuletzt geändert: 2025-02-01 08:55:40 GMT