PEP 396 – Modulversionsnummern
- Autor:
- Barry Warsaw <barry at python.org>
- Status:
- Zurückgezogen
- Typ:
- Informational
- Thema:
- Packaging
- Erstellt:
- 16. März 2011
- Post-History:
- 05. April 2011
Zusammenfassung
Da es nützlich und üblich ist, Versionsnummern für Python-Module anzugeben, und da sich verschiedene Wege, dies zu tun, organisch in der Python-Community entwickelt haben, ist es nützlich, Standardkonventionen für Modulautoren zu etablieren, an die sie sich halten und auf die sie verweisen können. Dieser informative PEP beschreibt Best Practices für Python-Modulautoren, die die Versionsnummer ihres Python-Moduls definieren möchten.
Die Einhaltung dieses PEP ist optional. Andere Python-Tools (wie z. B. distutils2 [1]) können jedoch angepasst werden, um die hier definierten Konventionen zu verwenden.
PEP-Ablehnung/Rücknahme
Dieser PEP wurde am 14.04.2021 formell abgelehnt. Das Packaging-Ökosystem hat sich in den Jahren seit dem ersten Schreiben dieses PEP erheblich verändert, und APIs wie importlib.metadata.version() [11] bieten eine viel bessere Erfahrung.
Diese Ablehnung wurde am 21.10.2024 als Rücknahme neu klassifiziert, da der vorherige Zustand missinterpretiert wurde [12], als würde vorgeschlagen, dass *keine* Module __version__-Attribute definieren sollten, was definitiv nicht der Fall ist.
Module können weiterhin nach Belieben __version__ definieren. Die Entscheidung, dies nicht zu tun, beeinträchtigt jedoch nicht das Abrufen von Versionsinformationen für installierte Verteilungspakete, sodass ein Informational PEP nicht das richtige Werkzeug ist, um Community-Konventionen zur Verwendung von Modul-__version__-Attributen zu dokumentieren (sie sind besser im Python Packaging User Guide abgedeckt).
User Stories
Alice schreibt ein neues Modul namens alice, das sie mit anderen Python-Entwicklern teilen möchte. alice ist ein einfaches Modul und befindet sich in einer Datei, alice.py. Alice möchte eine Versionsnummer angeben, damit ihre Benutzer erkennen können, welche Version sie verwenden. Da ihr Modul vollständig in einer Datei liegt, möchte sie die Versionsnummer zu dieser Datei hinzufügen.
Bob hat ein Modul namens bob geschrieben, das er mit vielen Benutzern geteilt hat. bob.py enthält der Bequemlichkeit seiner Benutzer halber eine Versionsnummer. Bob erfährt vom Cheeseshop [2] und fügt mit klassischen Distutils ein einfaches Packaging hinzu, um *The Bob Bundle* zum Cheeseshop hochladen zu können. Da bob.py bereits eine Versionsnummer angibt, auf die seine Benutzer programmgesteuert zugreifen können, möchte er, dass dieselbe API weiterhin funktioniert, auch wenn seine Benutzer sie jetzt vom Cheeseshop erhalten.
Carol pflegt mehrere Namespace-Pakete, die jeweils unabhängig entwickelt und vertrieben werden. Damit ihre Benutzer Abhängigkeiten von den richtigen Versionen ihrer Pakete angeben können, gibt sie die Versionsnummern in der setup.py-Datei des Namespace-Pakets an. Da Carol nur eine Versionsnummer pro Paket aktualisieren möchte, gibt sie die Versionsnummer in ihrem Modul an und lässt die setup.py beim Erstellen des *sdist*-Archivs die Modulversionsnummer extrahieren.
David pflegt ein Paket in der Standardbibliothek und erstellt auch eigenständige Versionen für andere Python-Versionen. Die Kopie der Standardbibliothek definiert die Versionsnummer im Modul, und dieselbe Versionsnummer wird auch für die eigenständigen Distributionen verwendet.
Begründung
Python-Module, sowohl in der Standardbibliothek als auch von Drittanbietern, enthalten seit langem Versionsnummern. Es gibt etablierte De-facto-Standards zur Beschreibung von Versionsnummern, und im Laufe der Jahre haben sich viele Ad-hoc-Methoden organisch entwickelt. Oftmals können Versionsnummern programmatisch aus einem Modul abgerufen werden, indem das Modul importiert und ein Attribut inspiziert wird. Klassische Python distutils setup()-Funktionen [3] beschreiben ein version-Argument, bei dem die Versionsnummer der Veröffentlichung angegeben werden kann. PEP 8 beschreibt die Verwendung eines Modulattributs namens __version__ zur Aufzeichnung von „Subversion, CVS oder RCS“-Versionsstrings mithilfe von Schlüsselwortexpansion. In den E-Mail-Archiven des PEP-Autors reicht das früheste Beispiel für die Verwendung eines __version__-Modulattributs durch unabhängige Modulentwickler bis ins Jahr 1995 zurück.
Ein weiteres Beispiel für Versionsinformationen ist das sqlite3 [5]-Modul mit seinen Attributen sqlite_version_info, version und version_info. Es ist möglicherweise nicht sofort ersichtlich, welches Attribut eine Versionsnummer für das Modul und welches eine Versionsnummer für die zugrunde liegende SQLite3-Bibliothek enthält.
Dieser informative PEP kodifiziert etablierte Praktiken und empfiehlt Standardwege zur Beschreibung von Modulversionsnummern zusammen mit einigen Anwendungsfällen, wann – und wann *nicht* – diese enthalten sein sollten. Die Annahme durch Modulautoren ist rein freiwillig; Packaging-Tools in der Standardbibliothek bieten optionale Unterstützung für die hier definierten Standards, und andere Tools im Python-Universum können sich ebenfalls daran halten.
Spezifikation
- Im Allgemeinen SOLLTEN Module in der Standardbibliothek KEINE Versionsnummern haben. Sie tragen implizit die Versionsnummer der Python-Veröffentlichung, in der sie enthalten sind.
- Auf Einzelfallbasis KÖNNEN Module der Standardbibliothek, die auch als eigenständige Form für andere Python-Versionen veröffentlicht werden, eine Modulversionsnummer enthalten, wenn sie in der Standardbibliothek enthalten sind, und SOLLTEN eine Versionsnummer enthalten, wenn sie separat verpackt werden.
- Wenn ein Modul (oder Paket) eine Versionsnummer enthält, SOLLTE die Version im Attribut
__version__verfügbar sein. - Für Module, die sich innerhalb eines Namespace-Pakets befinden, SOLLTE das Modul das Attribut
__version__enthalten. Das Namespace-Paket selbst SOLLTE KEIN eigenes Attribut__version__enthalten. - Der Wert des
__version__-Attributs SOLLTE ein String sein. - Modulversionsnummern SOLLTEN dem in PEP 386 spezifizierten normalisierten Versionsformat entsprechen.
- Modulversionsnummern SOLLTEN KEINE revisionsnummern des Versionskontrollsystems oder andere semantisch unterschiedliche Versionsnummern (z. B. die Versionsnummer einer zugrunde liegenden Bibliothek) enthalten.
- Das
version-Attribut in einer klassischen distutilssetup.py-Datei oder das PEP 345Version-Metadatenfeld SOLLTE vom__version__-Feld abgeleitet werden oder umgekehrt.
Beispiele
Abrufen der Versionsnummer von einem Drittanbieterpaket
>>> import bzrlib
>>> bzrlib.__version__
'2.3.0'
Abrufen der Versionsnummer von einem Paket der Standardbibliothek, das auch als eigenständiges Modul vertrieben wird
>>> import email
>>> email.__version__
'5.1.0'
Versionsnummern für Namespace-Pakete
>>> import flufl.i18n
>>> import flufl.enum
>>> import flufl.lock
>>> print flufl.i18n.__version__
1.0.4
>>> print flufl.enum.__version__
3.1
>>> print flufl.lock.__version__
2.1
>>> import flufl
>>> flufl.__version__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute '__version__'
>>>
Ableitung
Modulversionsnummern können an mindestens zwei Stellen und manchmal auch an mehr Stellen auftreten. Gemäß diesem PEP sind sie beispielsweise programmatisch über das __version__-Attribut des Moduls verfügbar. In einer klassischen distutils setup.py-Datei nimmt die setup()-Funktion ein version-Argument entgegen, während die distutils2 setup.cfg-Datei einen version-Schlüssel hat. Die Versionsnummer muss auch in die PEP 345-Metadaten gelangen, vorzugsweise beim Erstellen des *sdist*-Archivs. Es ist wünschenswert, dass Modulautoren die Versionsnummer nur einmal angeben und alle anderen Verwendungen von dieser einzigen Definition abgeleitet werden.
Dies könnte auf verschiedene Arten geschehen, von denen einige unten skizziert sind. Diese sind nur zur Veranschaulichung enthalten und nicht als endgültig, vollständig oder allumfassend gedacht. Andere Ansätze sind möglich, und einige der unten aufgeführten haben möglicherweise Einschränkungen, die ihre Verwendung in bestimmten Situationen verhindern.
Nehmen wir an, Elle fügt dieses Attribut zu ihrer Moduldatei elle.py hinzu
__version__ = '3.1.1'
Klassische Distutils
In klassischen Distutils ist der einfachste Weg, den Versionsstring in die setup()-Funktion in setup.py einzufügen, etwas wie folgt zu tun
from elle import __version__
setup(name='elle', version=__version__)
Nach Erfahrung des PEP-Autors kann dies jedoch in einigen Fällen fehlschlagen, z. B. wenn das Modul die automatische Python-3-Konvertierung über das 2to3-Programm verwendet (da setup.py von Python 3 ausgeführt wird, bevor das elle-Modul konvertiert wurde).
In diesem Fall ist es nicht viel schwieriger, ein kleines Stück Code zu schreiben, um die __version__ aus der Datei zu parsen, anstatt sie zu importieren. Ohne zu viele Details zu nennen, ist es wahrscheinlich, dass Module wie distutils2 eine Möglichkeit bieten werden, Versionsstrings aus Dateien zu parsen. Z. B.
from distutils2 import get_version
setup(name='elle', version=get_version('elle.py'))
Distutils2
Da der distutils2-Stil setup.cfg deklarativ ist, können wir keinen Code ausführen, um das Attribut __version__ zu extrahieren, weder über Import noch über Parsen.
In Absprache mit distutils-sig [9] werden zwei Optionen vorgeschlagen. Beide beinhalten die Speicherung der Versionsnummer in einer Datei und die Deklaration dieser Datei in der setup.cfg. Wenn der gesamte Inhalt der Datei die Versionsnummer enthält, wird der Schlüssel version-file verwendet.
[metadata]
version-file: version.txt
Wenn die Versionsnummer in einer größeren Datei enthalten ist, z. B. aus Python-Code, so dass die Datei geparst werden muss, um die Version zu extrahieren, wird der Schlüssel version-from-file verwendet.
[metadata]
version-from-file: elle.py
Eine Parsing-Methode, ähnlich der oben beschriebenen, wird für die Datei durchgeführt, die nach dem Doppelpunkt benannt ist. Das genaue Rezept dafür wird im entsprechenden distutils2-Entwicklungsforum diskutiert.
Eine Alternative ist, die Versionsnummer nur in setup.cfg zu definieren und das Modul pkgutil [8] zu verwenden, um es programmatisch verfügbar zu machen. Z. B. in elle.py
from distutils2._backport import pkgutil
__version__ = pkgutil.get_distribution('elle').metadata['version']
PEP 376 Metadaten
PEP 376 definiert einen Standard für statische Metadaten, beschreibt aber nicht den Prozess, durch den diese Metadaten erstellt werden. Es ist sehr wünschenswert, dass die abgeleiteten Versionsinformationen zur Build-Zeit und nicht zur Installationszeit in die PEP 376 .dist-info-Metadaten gelangen. So sind die Metadaten zur Introspektion verfügbar, auch wenn der Code nicht installiert ist.
Referenzen
Urheberrecht
Dieses Dokument wurde gemeinfrei erklärt.
Quelle: https://github.com/python/peps/blob/main/peps/pep-0396.rst
Zuletzt geändert: 2025-02-01 08:50:23 GMT