402 Stimmen

Standardmethode zum Einbetten von Versionen in Python-Pakete?

Gibt es einen Standardweg zu assoziieren Version String mit einem Python-Paket in einer solchen Weise, dass ich das folgende tun könnte?

import foo
print(foo.version)

Ich könnte mir vorstellen, dass es eine Möglichkeit gibt, diese Daten ohne zusätzliche Hardcodierung abzurufen, da Minor/Hauptstrings in setup.py bereits. Eine alternative Lösung, die ich gefunden habe, war, dass ich import __version__ in meinem foo/__init__.py und haben dann __version__.py erzeugt durch setup.py .

17voto

cmcginty Punkte 106764

Viele dieser Lösungen hier ignorieren git Versions-Tags, was immer noch bedeutet, dass man die Version an mehreren Stellen verfolgen muss (schlecht). Ich habe dies mit den folgenden Zielen angegangen:

  • Ableitung aller Python-Versionsreferenzen aus einem Tag in der git Repo
  • Automatisieren Sie git tag / push y setup.py upload Schritte mit einem einzigen Befehl, der keine Eingaben erfordert.

Wie es funktioniert:

  1. Von einem make release wird die letzte markierte Version im Git-Repository gefunden und hochgezählt. Das Tag wird zurück nach origin .

  2. En Makefile speichert die Version in src/_version.py wo sie gelesen wird von setup.py und ebenfalls in der Mitteilung enthalten. Nicht prüfen _version.py in die Versionskontrolle!

  3. setup.py Befehl liest die neue Versionszeichenfolge aus package.__version__ .

Einzelheiten:

Makefile

# remove optional 'v' and trailing hash "v1.0-N-HASH" -> "v1.0-N"
git_describe_ver = $(shell git describe --tags | sed -E -e 's/^v//' -e 's/(.*)-.*/\1/')
git_tag_ver      = $(shell git describe --abbrev=0)
next_patch_ver = $(shell python versionbump.py --patch $(call git_tag_ver))
next_minor_ver = $(shell python versionbump.py --minor $(call git_tag_ver))
next_major_ver = $(shell python versionbump.py --major $(call git_tag_ver))

.PHONY: ${MODULE}/_version.py
${MODULE}/_version.py:
    echo '__version__ = "$(call git_describe_ver)"' > $@

.PHONY: release
release: test lint mypy
    git tag -a $(call next_patch_ver)
    $(MAKE) ${MODULE}/_version.py
    python setup.py check sdist upload # (legacy "upload" method)
    # twine upload dist/*  (preferred method)
    git push origin master --tags

En release target erhöht immer die 3. Versionsstelle, aber Sie können die next_minor_ver o next_major_ver um die anderen Ziffern zu erhöhen. Die Befehle stützen sich auf die versionbump.py Skript, das in den Root des Repo eingecheckt wird

versionbump.py

"""An auto-increment tool for version strings."""

import sys
import unittest

import click
from click.testing import CliRunner  # type: ignore

__version__ = '0.1'

MIN_DIGITS = 2
MAX_DIGITS = 3

@click.command()
@click.argument('version')
@click.option('--major', 'bump_idx', flag_value=0, help='Increment major number.')
@click.option('--minor', 'bump_idx', flag_value=1, help='Increment minor number.')
@click.option('--patch', 'bump_idx', flag_value=2, default=True, help='Increment patch number.')
def cli(version: str, bump_idx: int) -> None:
    """Bumps a MAJOR.MINOR.PATCH version string at the specified index location or 'patch' digit. An
    optional 'v' prefix is allowed and will be included in the output if found."""
    prefix = version[0] if version[0].isalpha() else ''
    digits = version.lower().lstrip('v').split('.')

    if len(digits) > MAX_DIGITS:
        click.secho('ERROR: Too many digits', fg='red', err=True)
        sys.exit(1)

    digits = (digits + ['0'] * MAX_DIGITS)[:MAX_DIGITS]  # Extend total digits to max.
    digits[bump_idx] = str(int(digits[bump_idx]) + 1)  # Increment the desired digit.

    # Zero rightmost digits after bump position.
    for i in range(bump_idx + 1, MAX_DIGITS):
        digits[i] = '0'
    digits = digits[:max(MIN_DIGITS, bump_idx + 1)]  # Trim rightmost digits.
    click.echo(prefix + '.'.join(digits), nl=False)

if __name__ == '__main__':
    cli()  # pylint: disable=no-value-for-parameter

Dies erledigt die schwere Aufgabe, die Versionsnummer von git .

__init__.py

En my_module/_version.py Datei wird importiert in my_module/__init__.py . Fügen Sie hier die statische Installationskonfiguration ein, die Sie mit Ihrem Modul verteilen möchten.

from ._version import __version__
__author__ = ''
__email__ = ''

setup.py

Der letzte Schritt ist das Lesen der Versionsinformationen aus der Datei my_module Modul.

from setuptools import setup, find_packages

pkg_vars  = {}

with open("{MODULE}/_version.py") as fp:
    exec(fp.read(), pkg_vars)

setup(
    version=pkg_vars['__version__'],
    ...
    ...
)

Damit das alles funktioniert, müssen Sie natürlich mindestens ein Versions-Tag in Ihrem Repo haben.

git tag -a v0.0.1

16voto

Andy Lee Punkte 850

Ich verwende eine JSON-Datei im Paketverzeichnis. Dies entspricht den Anforderungen von Zooko.

Innerhalb pkg_dir/pkg_info.json :

{"version": "0.1.0"}

Innerhalb setup.py :

from distutils.core import setup
import json

with open('pkg_dir/pkg_info.json') as fp:
    _info = json.load(fp)

setup(
    version=_info['version'],
    ...
    )

Innerhalb pkg_dir/__init__.py :

import json
from os.path import dirname

with open(dirname(__file__) + '/pkg_info.json') as fp:
    _info = json.load(fp)

__version__ = _info['version']

Ich habe auch andere Informationen eingefügt pkg_info.json , wie Autor. I verwende gerne JSON, weil ich damit die Verwaltung der Metadaten automatisieren kann.

12voto

ingyhere Punkte 10268

Viele Arbeiten zur einheitlichen Versionierung und zur Unterstützung von Konventionen wurden bereits abgeschlossen. seit diese Frage zum ersten Mal gestellt wurde . Schmackhafte Optionen sind jetzt detailliert en el Python Packaging Benutzerhandbuch . Erwähnenswert ist auch, dass Versionsnummernschemata sind in Python gemäß PEP 440 relativ streng und daher ist es wichtig, die Dinge in einem vernünftigen Rahmen zu halten, wenn Ihr Paket für die Öffentlichkeit freigegeben werden soll. Käseladen .

Hier finden Sie eine kurze Übersicht über die Versionsoptionen:

  1. Lesen Sie die Datei in setup.py ( setuptools ) und erhalten Sie die Version.
  2. Verwenden Sie ein externes Build-Tool (um sowohl die __init__.py sowie Quellensicherung), z.B. bump2version , Änderungen o zest.releaser .
  3. Setzen Sie den Wert auf einen __version__ globale Variable in einem bestimmten Modul.
  4. Legen Sie den Wert in einer einfachen VERSION-Textdatei ab, die sowohl setup.py als auch der Code lesen können.
  5. Setzen Sie den Wert über eine setup.py Freigabe und Verwendung importlib.metadata um sie zur Laufzeit abzurufen. (Achtung, es gibt Versionen vor 3.8 und nach 3.8).
  6. Setzen Sie den Wert auf __version__ en sample/__init__.py und importieren Sie die Probe in setup.py .
  7. 使用する setuptools_scm um die Versionierung aus der Versionskontrolle zu extrahieren, so dass es sich um die kanonische Referenz handelt, nicht um Code.

ANMERKUNG dass (7) möglicherweise die modernster Ansatz (Build-Metadaten sind unabhängig vom Code und werden durch Automatisierung veröffentlicht). Auch ANMERKUNG dass, wenn Setup für die Paketfreigabe verwendet wird, eine einfache python3 setup.py --version wird die Version direkt gemeldet.

7voto

James Antill Punkte 2767

Ebenfalls erwähnenswert ist, dass neben den __version__ als Semi-Std. in Python ist also __version_info__ was ein Tupel ist, kann man in den einfachen Fällen einfach etwas tun wie:

__version__ = '1.2.3'
__version_info__ = tuple([ int(num) for num in __version__.split('.')])

...und Sie können die __version__ Zeichenkette aus einer Datei, oder was auch immer.

6voto

dF. Punkte 70587

Es scheint keinen Standardweg zu geben, um einen Versionsstring in ein Python-Paket einzubetten. Die meisten Pakete, die ich gesehen habe, verwenden eine Variante Ihrer Lösung, d.h. eitner

  1. Einbetten der Version in setup.py und haben setup.py ein Modul erzeugen (z.B. version.py ), das nur Versionsinformationen enthält, die von Ihrem Paket importiert werden, oder

  2. Umgekehrt: Legen Sie die Versionsinformationen in Ihr Paket selbst, und importieren Sie que zum Einstellen der Version in setup.py

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X