[PYTHON] __version__ Fallen und Best Practices

Wenn Sie ein Python-Modul verteilen, müssen Sie dessen Versionsnummer beschreiben.

Es sind zwei Beschreibungen erforderlich. Das Schreiben derselben Versionsnummer an zwei Stellen ist natürlich mühsam und eine Fehlerquelle, daher möchte ich dies an einer Stelle tun.

Beschreiben Sie die Versionsnummer in __version __. Py und

mylibrary/__version__.py


__version_info__ = (1, 0, 0)
__version__ = '.'.join(map(str, __version_info__))

Ich denke, die Konfiguration von "import __version __" aus "setup.py" und "__init __. Py" ist ein gängiges Muster.

\ _ \ _ Version \ _ \ _ Trap

Bei dieser Konfiguration liegt ein Problem vor. In "__init __. Py" (oder dem von "__init __. Py" importierten Kernmodul) Dies ist der Fall, wenn der Import eines nicht standardmäßigen Moduls beschrieben wird.

mylibrary/__init__.py


from __version__ import __version__
from core import MyClass  # mylibrary.Verknüpfungen zur Verwendung in MyClass

mylibrary/core.py


import numpy as np  #Import von externen Modulen, die von MyClass benötigt werden

class MyClass(object):
    pas

Wenn Sie in diesem Zustand setup.py ausführen, ...

Plötzlicher Importfehler


> py setup.py install
Traceback (most recent call last):
  File "C:\Users\hoge\git\example\setup.py", line 4, in <module>
    from mylibrary import __version__
  File "C:\Users\hoge\git\example\mylibrary\__init__.py", line 2, in <module>
    from .core import MyClass
  File "C:\Users\hoge\git\example\mylibrary\core.py", line 1, in <module>
    import numpy as np
ImportError: No module named numpy

Ein ImportError wird auftreten und das mylibrary-Modul kann nicht installiert werden.

Obwohl install_requires für die Installation von numpy in setup.py beschrieben ist Es ist ein "Schlüssel im sicheren" Zustand, dass die "setup.py" nicht ausgeführt und nicht installiert werden kann.

Best Practice [^ 1]

[^ 1]: Ich hatte wirklich vor, den diesjährigen Adventskalender als "Best Practice" zu schreiben, aber kurz bevor ich einen überwältigend einfachen Weg fand, war es totaler Mist. Es tut mir leid, also lasse ich es so wie es ist. Bitte beziehen Sie sich auf diejenigen, die eine transzendentale alte Umgebung verwenden, in der setup.cfg nicht verwendet werden kann.

Die Ursache ist, dass beim Importieren von "__version __. Py" auch "__init __. Py" im selben Verzeichnis importiert wird. Die Lösung besteht also darin, "__version __. Py" ohne Import zu laden ".

setup.py


#!/usr/bin/python

from setuptools import setup, find_packages
# from __version__ import __version__  #Löschen, Ursache von ImportError
import os

packages = find_packages()

ns = dict()
for package in packages:
    version_file = os.path.join(package, '__version__.py')
    if os.path.exists(version_file):
        with open(version_file, mode='rt') as f:
            eval(compile(f.read(), version_file, 'exec'), dict(), ns)
            break

__version__ = ns['__version__']
del ns

setup(
    version=__version__,
)

Ich werde es nicht im Detail erklären, aber ich suche nach "__version __. Py", bewerte es und extrahiere die Variable "version" direkt. Auf diese Weise können Sie vermeiden, "__init __. Py" zu importieren, was zu "ImportError" führt.

aber, Ich zögere es, zu viel anderen Code als Metadaten in setup.py zu schreiben Es ist nicht realistisch, ein nicht standardmäßiges Modul mit "__init __. Py" zu importieren.

Es scheint, dass so etwas wie "ein Fehler beim Versuch, die Versionsnummernbeschreibung an einer Stelle zu kombinieren" aufgetreten ist, leicht auftreten kann. Es ist kein Wunder, dass die Meinung, dass "\ _ \ _ Version \ _ \ _ ein Anti-Muster ist", herauskommt.

beste Übung

Schreiben Sie setup.py wie folgt.

setup.py


from setuptools import setup
setup()

Wie Sie sehen, müssen Sie nur "setup ()" aufrufen und die Einstellungen sind leer.

setup () ruft die fehlenden Einstellungen aus der Datei setup.cfg ab, falls vorhanden, unabhängig davon, ob sie leer ist oder nicht. Und es gibt eine Beschreibungsmethode in setup.cfg, die Dateien und Variablen angibt und auf diese verweist.

setup.cfg


[metadata]
version = attr: mylibrary.__version__.__version__

Dies hat den gleichen Effekt wie das Anwenden der Variablen "version" der Datei "mylibrary / __ version __. Py" auf die "version" von "setup ()".

Übrigens, mit dieser Methode sowie dem Importieren von "__version __. Py"

Die Referenz der in "setup.cfg" beschriebenen Variablen ist jedoch dieselbe wie "setup.py" in "Nicht die beste Vorgehensweise". Es scheint die Datei direkt zu "eval ()" zu machen, ohne den Importmechanismus zu durchlaufen, und es verursacht keinen ImportError.

Zusammenfassung

Zusätzlich zur Version können die meisten Dinge, die in "setup.py" beschrieben wurden, intelligent in "setup.cfg" beschrieben werden. Außerdem können nicht nur die Metadaten von setup (), sondern auch die Einstellungen wie flake8, py.test und nosetest zusammen beschrieben werden. Es hilft auch, das Verzeichnis sauber zu halten.

Da ich setup.cfg mit viel Aufwand einsetze, möchte ich das Beste daraus machen.

Recommended Posts

__version__ Fallen und Best Practices
Best Practices für Django views.py und urls.py (?)
DBSCAN-Praktiken und -Algorithmen
Lernen Sie Best Practices von Cookiecutter-Django
Erstellen und Ausführen von TOPPERS / ASP (Version 2020-03-10)
AWS Lambda-Entwicklung Meine Best Practices
Best Practices für Konfigurationsdateien in Flask