Frage

Gibt es eine Standardmethode Version String mit einem Python-Paket in einer solchen Art und Weise zu verbinden, dass ich folgendes tun könnte?

import foo
print foo.version

Ich könnte mir vorstellen, es gibt eine Möglichkeit, dass die Daten ohne zusätzliche Hardcoding abzurufen, da Moll / Dur-Strings bereits in setup.py angegeben sind. Alternative Lösung, die ich war gefunden import __version__ in meinem foo/__init__.py zu haben und dann __version__.py von setup.py erzeugt hat.

War es hilfreich?

Lösung

Nicht direkt eine Antwort auf Ihre Frage, aber man sollte es für __version__ zu nennen, nicht version.

Das ist fast ein Quasi-Standard. Viele Module in der Standardbibliothek Verwendung __version__, und dies auch in Los von 3rd-Party-Module, so ist es der quasi-Standard.

Normalerweise ist __version__ eine Zeichenfolge, aber manchmal auch, es ist ein Schwimmer oder Tupels.

Edit: wie erwähnt durch S.Lott (Danke), PEP 8 sagt es ausdrücklich:

  

Version Buchhaltung

     

Wenn Sie haben, Subversion, CVS oder RCS crud in der Quelldatei,   tun Sie es wie folgt.

    __version__ = "$Revision: 63990 $"
    # $Source$
     

Diese Zeilen sollen nach der Modul docstring aufgenommen werden, bevor   ein anderer Code, durch eine Leerzeile oberhalb und unterhalb getrennt ist.

Sie sollten auch sicherstellen, dass die Versionsnummer auf das Format in PEP 440 ( PEP 386 eine frühere Version dieses Standards).

Andere Tipps

Ich verwende eine einzige _version.py Datei als „einmal cannonical Ort“ zum Speichern von Versionsinformationen:

  1. Es bietet ein __version__ Attribut.

  2. Es bietet die Standard-Metadaten-Version. Daher wird es durch pkg_resources oder andere Werkzeuge erfasst werden, dass das Paket-Metadaten (EGG-INFO und / oder PKG-INFO, PEP 0345).

  3. parsen
  4. Es importiert nicht Ihr Paket (oder etwas anderes), wenn Ihr Paket bauen, die Probleme in einigen Situationen führen kann. (Siehe die folgenden Erläuterungen darüber, welche Probleme das verursachen kann.)

  5. Es gibt nur einen Ort, der die Versionsnummer wird aufgeschrieben, so gibt es nur einen Ort, um es zu ändern, wenn die Versionsnummer ändert sich, und es gibt weniger Chancen inkonsistenter Versionen.

Hier

ist, wie es funktioniert: der „ein kanonischer Ort“, um die Versionsnummer zu speichern, wird .py Datei mit dem Namen „_version.py“, die in Ihrem Python-Paket ist zum Beispiel in myniftyapp/_version.py. Diese Datei ist ein Python-Modul, aber Ihr setup.py nicht importieren Sie es nicht! (Das würde besiegen Feature 3.) Statt Ihrer setup.py weiß, dass der Inhalt dieser Datei ist sehr einfach, so etwas wie:

__version__ = "3.6.5"

Und damit Ihre setup.py öffnet die Datei und analysiert es mit Code wie:

import re
VERSIONFILE="myniftyapp/_version.py"
verstrline = open(VERSIONFILE, "rt").read()
VSRE = r"^__version__ = ['\"]([^'\"]*)['\"]"
mo = re.search(VSRE, verstrline, re.M)
if mo:
    verstr = mo.group(1)
else:
    raise RuntimeError("Unable to find version string in %s." % (VERSIONFILE,))

Dann wird Ihr setup.py gibt diese Zeichenfolge als Wert des „Version“ Arguments setup(), so befriedigend Merkmal 2.

Merkmal 1 zu erfüllen, Sie Ihr Paket haben kann, importieren Sie die _version Datei von myniftyapp/__init__.py wie folgt aus (zur Laufzeit, überhaupt nicht Rüstzeit!):

from _version import __version__

Hier ist ein Beispiel für diese Technik dass ich habe seit Jahren verwendet wird.

Der Code in diesem Beispiel ist ein wenig komplizierter, aber das vereinfachte Beispiel, dass ich in diesen Kommentar schrieb sollte eine vollständige Implementierung sein.

Hier ist Beispielcode des Imports die Version .

Wenn Sie etwas falsch mit diesem Ansatz zu sehen, lassen Sie es mich wissen.

Rewritten 2017-05

Nach mehr als 10 Jahren von Python-Code zu schreiben und verschiedene Pakete zu verwalten ich zu dem Schluss gekommen, dass DIY ist vielleicht nicht der beste Ansatz.

Ich begann pbr Paket mit in meinen Paketen mit Versionsverwaltung handelt. Wenn Sie git als SCM verwenden, wird dies wie Magie in Ihren Arbeitsablauf passen, Ihre Arbeitswochen Spar (Sie werden überrascht sein, wie komplex das Problem sein kann).

Ab heute pbr ist auf Platz # 11 am häufigsten verwendete Python-Paket und erreichen dieses Niveau didn‘ t umfassen alle schmutzigen Tricks: war nur eine:. ein gemeinsames Verpackungsproblem auf sehr einfache Art und Weise zur Festsetzung

pbr kann mehr von dem Paketverwaltungsaufwand tun, ist nicht auf der Versionierung beschränkt, sondern es macht Dich nicht alle seine Vorteile erlassen erzwingen.

So Ihnen eine Idee zu geben, wie es pbr annehmen sieht in einem einen Blick swiching Verpackung pbr

Wahrscheinlich würden Sie beobachtet, dass die Version nicht in dem Repository gespeichert ist. PBR erkennt es aus Git Zweige und Tags.

Keine Notwendigkeit, sich Sorgen zu machen, was passiert, wenn Sie nicht über ein Git Repository, weil pbr bedeutet „kompiliert“ und die Version Cache, wenn Sie die Anwendungen verpacken oder installieren, so dass es keine Laufzeitabhängigkeit git.

Alte Lösung

Hier ist die beste Lösung, die ich bisher gesehen habe und es erklärt auch, warum:

Innerhalb yourpackage/version.py:

# Store the version here so:
# 1) we don't load dependencies by storing it in __init__.py
# 2) we can import it in setup.py for the same reason
# 3) we can import it into your module module
__version__ = '0.12'

Innerhalb yourpackage/__init__.py:

from .version import __version__

Innerhalb setup.py:

exec(open('yourpackage/version.py').read())
setup(
    ...
    version=__version__,
    ...

Wenn Sie einen anderen Ansatz kennen, besser zu sein scheint, lassen Sie es mich wissen.

Per dem latenten PEP 396 (Modul Versionsnummern) , gibt es eine vorgeschlagene Art und Weise, dies zu tun. Es beschreibt, mit Begründung, ein (zugegebenermaßen optional) Standard für die Module zu folgen. Hier ist ein Ausschnitt:

  

3) Wenn ein Modul (oder Paket) eine Versionsnummer enthält, sollte die Version im __version__ Attribute zur Verfügung.

     

4)   Für Module, die in einem Namespace-Paket leben, sollte das Modul das __version__ Attribut enthalten. Das Namespace-Paket selbst sollte nicht sein eigenes __version__ Attribut enthalten.

     

5)   Der Wert des __version__ Attribut sollte ein String sein.

Obwohl dies ist wahrscheinlich viel zu spät, gibt es eine etwas einfachere Alternative zur vorherige Antwort:

__version_info__ = ('1', '2', '3')
__version__ = '.'.join(__version_info__)

(Und es wäre recht einfach mit automatischer Erhöhung Teile der Versionsnummern auf einen String str() verwenden zu konvertieren.)

Natürlich, von dem, was ich gesehen habe, neigen die Menschen so etwas wie die zuvor genannten Version zu verwenden, wenn __version_info__ verwenden, und als solcher Speicher als ein Tupel von Ints; aber ich verstehe nicht ganz den Punkt sieht, dass man, wie ich bezweifle, gibt es Situationen, in denen Sie mathematische Operationen wie Addition und Subtraktion auf Teile der Versionsnummern für jeden Zweck neben Neugier oder Auto-Inkrementierung (und selbst dann durchführen würden, int() und str() kann ziemlich leicht verwendet werden). (Auf der anderen Seite gibt es die Möglichkeit, Code von jemand anderem eine numerische Tupel eher als ein Stringtupel erwartet und somit versagt.)

Das ist, natürlich, meine eigene Meinung, und ich würde gerne wie andere -Eingang auf einen numerischen Tupel verwendet wird.


Als Shezi mich daran erinnert, (lexikalische) Vergleiche der Zahlenketten müssen nicht unbedingt das gleiche Ergebnis wie direkte numerische Vergleiche; führende Nullen würden dafür zu schaffen, erforderlich. Also am Ende, __version_info__ Speichern (oder was auch immer, es würde genannt werden) als Tupel von ganzzahligen Werten würde für eine effizientere Version Vergleiche ermöglichen.

Ich verwende eine JSON-Datei im Paket Richt. Das paßt Zooko Anforderungen.

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 in pkg_info.json, wie Autor. ich wie JSON zu verwenden, weil ich Management von Metadaten automatisieren kann.

Viele dieser Lösungen ignorieren hier git Version Tags, die immer noch bedeutet, dass Sie Version an mehreren Stellen zu verfolgen haben (sehr schlecht). Ich näherte ich dies mit den folgenden Zielen:

  • leitet alle Python-Version Referenzen von einem Tag in der git Repo
  • Automatisieren git tag / push und setup.py upload Schritte mit einem einzigen Befehl, die keine Eingaben nehmen.

Wie es funktioniert:

  1. Von einem make release Befehl wird die letzte markierte Version im git Repo gefunden und erhöht. Der Tag zurückgeschoben zu origin.

  2. Die Makefile speichert die Version in src/_version.py, wo sie von setup.py gelesen wird und auch in der Pressemitteilung. Sie _version.py in der Quellcodeverwaltung nicht überprüfen!

  3. setup.py Befehl liest die neue Version Zeichenfolge aus package.__version__.

Details:

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

Das release Ziel immer erhöht die dritte Version digit, aber Sie können die next_minor_ver oder next_major_ver verwenden, um die anderen Ziffern zu erhöhen. Die Befehle setzen auf dem versionbump.py-Skript, das in die Wurzel des Repo geprüft

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 ist das Heben schwerer Lasten wie zu verarbeiten und die Versionsnummer von git zu erhöhen.

__ init __. Py

Die my_module/_version.py Datei wird in my_module/__init__.py importiert. Setzen Sie eventuell vorhandene statische installieren Config hier, dass Sie mit Ihrem Modul verteilt werden sollen.

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

setup.py

Der letzte Schritt ist die Versionsinfo von einem my_module Modul zu lesen.

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__'],
    ...
    ...
)

Natürlich, für all dies funktioniert, müssen Sie mindestens eine Version Tag in Ihrem Repo haben müssen würden starten.

git tag -a v0.0.1

Es scheint nicht ein Standard-Weg, um eine Versionszeichenfolge in einem Python-Paket einbetten. Die meisten Pakete Ich habe verwenden eine Variante Ihrer Lösung gesehen, das heißt Eitner

  1. einbetten Version in setup.py und haben setup.py ein Modul erzeugen (z version.py) nur Versionsinformationen enthalten, die von Ihrem Paket importiert wird, oder

  2. Die Rückseite: legen Sie die Versionsinformationen in Ihrem Paket selbst und Import , die die Version in setup.py einstellen

Erwähnenswert ist auch, dass ebenso wie __version__ ein halb std zu sein. so in Python ist __version_info__ die ein Tupel, in den einfachen Fällen einfach etwas tun können, wie:

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

... und Sie können den __version__ String aus einer Datei erhalten, oder was auch immer.

Ich sah auch einen anderen Stil:

>>> django.VERSION
(1, 1, 0, 'final', 0)

Pfeil behandelt sie auf eine interessante Weise.

In arrow/__init__.py:

__version__ = '0.8.0'
VERSION = __version__

In setup.py:

def grep(attrname):
    pattern = r"{0}\W*=\W*'([^']+)'".format(attrname)
    strval, = re.findall(pattern, file_text)
    return strval

setup(
    name='arrow',
    version=grep('__version__'),
    # [...]
)

Für das, was es wert ist, wenn Sie NumPy distutils verwenden, numpy.distutils.misc_util.Configuration hat eine make_svn_version_py() Verfahren, das die Revisionsnummer innerhalb package.__svn_version__ in der variablen version einbettet.

  1. Verwenden Sie eine version.py Datei nur mit __version__ = <VERSION> param in der Datei. In der setup.py Datei des __version__ param importieren und setzt es Wert in der setup.py-Datei wie folgt: version=__version__
  2. Eine andere Möglichkeit ist nur eine setup.py Datei mit version=<CURRENT_VERSION> zu verwenden - das CURRENT_VERSION hartcodiert ist
  3. .

Da wir nicht wollen, manuell die Version in der Datei, die wir jedes Mal ändern, erstellen Sie einen neuen Tag (bereit, eine Version neue Paket veröffentlichen), können wir verwenden die folgende ..

Ich empfehle bumpversion Paket. Ich habe es seit Jahren mit einer Version stoßen.

Start von version=<VERSION> auf Ihre setup.py Datei hinzufügen, wenn Sie es nicht bereits haben.

Sie sollten ein kurzes Skript wie folgt verwenden, jedesmal wenn Sie stoßen eine Version:

bumpversion (patch|minor|major) - choose only one option
git push
git push --tags

Dann fügen Sie eine Datei pro repo genannt: .bumpversion.cfg:

[bumpversion]
current_version = <CURRENT_TAG>
commit = True
tag = True
tag_name = {new_version}
[bumpversion:file:<RELATIVE_PATH_TO_SETUP_FILE>]

Hinweis:

  • Sie können __version__ Parameter unter version.py-Datei verwenden, wie es in anderen Beiträgen vorgeschlagen wurde und aktualisieren Sie die bumpversion-Datei wie folgt: [bumpversion:file:<RELATIVE_PATH_TO_VERSION_FILE>]
  • Sie muss git commit oder alles in Ihrem Repo git reset, sonst werden Sie einen schmutzigen Repo-Fehler.
  • Stellen Sie sicher, dass Ihre virtuelle Umgebung das Paket von bumpversion enthält, ohne sie wird es nicht funktionieren.

Wenn Sie CVS (oder RCS) und wollen eine schnelle Lösung, können Sie:

__version__ = "$Revision: 1.1 $"[11:-2]
__version_info__ = tuple([int(s) for s in __version__.split(".")])

(Natürlich wird die Revisionsnummer für Sie durch CVS ersetzt werden.)

Dies gibt Ihnen eine druckfreundliche Version und eine Version Info, die Sie überprüfen können, ob das Modul, das Sie importieren, zumindest die erwartete Version:

import my_module
assert my_module.__version_info__ >= (1, 1)
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top