Wie kann ich die in setup.py (setuptools) in meinem Paket definierte Version erhalten?

153

Wie kann ich die in setup.pymeinem Paket definierte Version (für --versionoder für andere Zwecke) erhalten?

Elmarco
quelle
3
Ich denke, dies ist ungefähr die gleiche Frage wie stackoverflow.com/questions/458550/… .
Zooko

Antworten:

246

Versionszeichenfolge der bereits installierten Distribution abfragen

Um die Version zur Laufzeit aus Ihrem Paket abzurufen (was Ihre Frage tatsächlich zu stellen scheint), können Sie Folgendes verwenden:

import pkg_resources  # part of setuptools
version = pkg_resources.require("MyProject")[0].version

Speichern Sie die Versionszeichenfolge zur Verwendung während der Installation

Wenn Sie in die andere Richtung gehen möchten (was andere Antwortautoren hier anscheinend gedacht haben), fügen Sie die Versionszeichenfolge in eine separate Datei ein und lesen Sie den Inhalt dieser Datei ein setup.py.

Sie können eine version.py in Ihrem Paket mit einer __version__Zeile erstellen und diese dann aus setup.py mit lesen execfile('mypackage/version.py'), sodass sie __version__im Namespace setup.py festgelegt wird.

Wenn Sie eine viel einfachere Methode wünschen, die mit allen Python-Versionen und sogar Nicht-Python-Sprachen funktioniert, die möglicherweise Zugriff auf die Versionszeichenfolge benötigen:

Speichern Sie die Versionszeichenfolge als einzigen Inhalt einer VERSIONNur- Text-Datei mit dem Namen z. B. und lesen Sie diese Datei während setup.py.

version_file = open(os.path.join(mypackage_root_dir, 'VERSION'))
version = version_file.read().strip()

Dieselbe VERSIONDatei funktioniert dann genauso gut in jedem anderen Programm, auch in Nicht-Python-Programmen, und Sie müssen die Versionszeichenfolge für alle Programme nur an einer Stelle ändern.

Warnung vor Rennbedingungen während der Installation

Übrigens, importieren Sie Ihr Paket NICHT aus Ihrer setup.py, wie in einer anderen Antwort hier vorgeschlagen: Es scheint für Sie zu funktionieren (da Sie bereits die Abhängigkeiten Ihres Pakets installiert haben), aber es wird neuen Benutzern Ihres Pakets Schaden zufügen , da sie Ihr Paket nicht installieren können, ohne zuerst die Abhängigkeiten manuell zu installieren.

PJ Eby
quelle
1
Dies ist hier einfach die beste Antwort: Am besten verwenden Sie execfile, wenn Sie setup.py benötigen, um die Version aus Ihrem Paket zu erhalten.
19.
2
execfilefunktioniert wirklich gut ... aber (leider) funktioniert nicht mit Python 3.
medmunds
9
... OK, für Python 2 und 3 with open('mypackage/version.py') as f: exec(f.read())anstelle von verwenden execfile('mypackage/version.py'). (Von stackoverflow.com/a/437857/647002 )
medmunds
5
Der Wreak-Chaos-Teil ist nicht unbedingt wahr ... er wird nur dann Chaos anrichten, wenn Ihre init .py-Datei im Paket Codeimport-Abhängigkeiten aufweist (direkt oder indirekt).
Pykler
2
Es scheint erwähnenswert zu sein, dass Django in seiner setup.py einen __import__- Aufruf ausführt . Macht '__import__' vs 'import' dies sicher? Es scheint ihnen keine Probleme zu bereiten.
user1978019
33

Beispielstudie: mymodule

Stellen Sie sich diese Konfiguration vor:

setup.py
mymodule/
        / __init__.py
        / version.py
        / myclasses.py

Stellen Sie sich dann ein übliches Szenario vor, in dem Sie Abhängigkeiten haben und setup.pywie folgt aussehen:

setup(...
    install_requires=['dep1','dep2', ...]
    ...)

Und ein Beispiel __init__.py:

from mymodule.myclasses import *
from mymodule.version import __version__

Und zum Beispiel myclasses.py:

# these are not installed on your system.
# importing mymodule.myclasses would give ImportError
import dep1
import dep2

Problem Nr. 1: Importieren mymodulewährend des Setups

Wenn Sie dann während des Setupssetup.py importieren, erhalten Sie höchstwahrscheinlich eine . Dies ist ein sehr häufiger Fehler, wenn Ihr Paket Abhängigkeiten aufweist. Wenn Ihr Paket keine anderen Abhängigkeiten als die integrierten enthält, sind Sie möglicherweise sicher. Dies ist jedoch keine gute Praxis. Der Grund dafür ist, dass es nicht zukunftssicher ist; Sagen Sie morgen, Ihr Code muss eine andere Abhängigkeit verbrauchen.mymoduleImportError

Problem Nr. 2: Wo ist mein __version__?

Wenn Sie codieren __version__in setup.pydann kann es nicht mit der Version übereinstimmen , dass Sie in Ihrem Modul versenden würden. Um konsistent zu sein, würden Sie es an einer Stelle ablegen und an derselben Stelle lesen, wenn Sie es benötigen. Bei Verwendung importkann das Problem Nr. 1 auftreten.

Lösung: à la setuptools

Sie würden eine Kombination aus verwenden open, execund ein dict für bieten exechinzuzufügen Variablen:

# setup.py
from setuptools import setup, find_packages
from distutils.util import convert_path

main_ns = {}
ver_path = convert_path('mymodule/version.py')
with open(ver_path) as ver_file:
    exec(ver_file.read(), main_ns)

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

Und in mymodule/version.pyder Version aussetzen:

__version__ = 'some.semantic.version'

Auf diese Weise wird die Version mit dem Modul geliefert, und Sie haben keine Probleme beim Einrichten eines Moduls mit fehlenden Abhängigkeiten (das noch installiert werden muss).

dnozay
quelle
2
Dies scheint die vernünftigste Lösung zu sein, da sie nur auf zulässigen Abhängigkeiten beruht.
Dogweather
Unterscheidet sich das in der Datei setup.py definierte Konzept der Paketversion von der Version ?
Variable
16

Die beste Technik besteht darin, __version__in Ihrem Produktcode zu definieren und ihn dann von dort in setup.py zu importieren. Dies gibt Ihnen einen Wert, den Sie in Ihrem laufenden Modul lesen können und der nur an einer Stelle definiert werden kann.

Die Werte in setup.py werden nicht installiert, und setup.py bleibt nach der Installation nicht erhalten.

Was ich (zum Beispiel) in Coverage.py gemacht habe:

# coverage/__init__.py
__version__ = "3.2"


# setup.py
from coverage import __version__

setup(
    name = 'coverage',
    version = __version__,
    ...
    )

UPDATE (2017): Coverage.py importiert sich nicht mehr selbst, um die Version zu erhalten. Durch das Importieren Ihres eigenen Codes kann es deinstalliert werden, da Ihr Produktcode versucht, Abhängigkeiten zu importieren, die noch nicht installiert sind, da sie durch setup.py installiert werden.

Ned Batchelder
quelle
4
@Evan: Ich bin mir nicht sicher, worum es bei "Nur diesen Wert aus der Quelle ziehen" geht. Wenn die darin enthaltene Datei __version__auf irgendeine Weise beschädigt ist, wird auch Ihr Import unterbrochen. Python kann nicht nur die gewünschten Anweisungen interpretieren. @pjeby ist richtig: Wenn Ihr Modul andere Module importieren muss, sind diese möglicherweise noch nicht installiert und es wird chaotisch. Diese Technik funktioniert, wenn Sie darauf achten, dass der Import keine lange Kette anderer Importe verursacht.
Ned Batchelder
1
@Ned Batchelder Ich bin mir ziemlich sicher, dass, wenn Sie die Version vor dem Import in die Quelldatei und nur in eine 'aus Modul-Importversion' setzen, nicht mehr von der Quelldatei lex als nötig. Außerdem, wer wird kaputten Code veröffentlichen? Wenn das Paket Abhängigkeiten benötigt, verwenden Sie setuptools oder warten Sie auf die Veröffentlichung von distutils2 später im Jahr.
Evan Plaice
15
@Evan, es tut mir leid, aber Sie sind falsch beim Importieren von Teildateien. Fügen Sie eine print-Anweisung am Ende eines langen Moduls ein und importieren Sie die erste in der Datei definierte Variable. Die print-Anweisung wird ausgeführt.
Ned Batchelder
23
Ich bin darauf hereingefallen, aber @pjeby hat völlig Recht: "Importieren Sie Ihr Paket übrigens NICHT aus Ihrer setup.py, wie in einer anderen Antwort hier vorgeschlagen: Es scheint für Sie zu funktionieren (da Sie bereits die Abhängigkeiten Ihres Pakets installiert haben ), aber es wird neuen Benutzern Ihres Pakets Schaden zufügen, da sie Ihr Paket nicht installieren können, ohne zuerst die Abhängigkeiten manuell zu installieren. " Ned, würde es Ihnen etwas ausmachen, Ihrer Antwort eine Warnung hinzuzufügen?
Dr. Jan-Philip Gehrcke
1
Wie @ Jan-PhilipGehrcke, wenn ich eine solche setup.py-Datei auf PyPI hochlade und dann pip install <packagename>die folgende Fehlermeldung erhalte : ImportError: No module named <packagename>. Bitte warnen Sie Ihre Leser, dass Sie solche setup.py-Dateien in Umgebungen, in denen das Paket noch nicht installiert ist, nicht ausführen können!
Kochfelder
14

Ihre Frage ist etwas vage, aber ich denke, Sie fragen, wie Sie sie spezifizieren sollen.

Sie müssen wie folgt definieren __version__:

__version__ = '1.4.4'

Und dann können Sie bestätigen, dass setup.py die gerade angegebene Version kennt:

% ./setup.py --version
1.4.4
Nathanismus
quelle
9

Ich war mit diesen Antworten nicht zufrieden ... wollte weder Setuptools benötigen noch ein ganzes separates Modul für eine einzelne Variable erstellen, also habe ich mir diese ausgedacht.

Wenn Sie sicher sind, dass das Hauptmodul im pep8-Stil vorliegt und dies auch bleibt:

version = '0.30.unknown'
with file('mypkg/mymod.py') as f:
    for line in f:
        if line.startswith('__version__'):
            _, _, version = line.replace("'", '').split()
            break

Wenn Sie besonders vorsichtig sein und einen echten Parser verwenden möchten:

import ast
version = '0.30.unknown2'
with file('mypkg/mymod.py') as f:
    for line in f:
        if line.startswith('__version__'):
            version = ast.parse(line).body[0].value.s
            break

setup.py ist so etwas wie ein Wegwerfmodul, also kein Problem, wenn es etwas hässlich ist.


Update: Lustigerweise habe ich mich in den letzten Jahren davon entfernt und angefangen, eine separate Datei im Paket namens zu verwenden meta.py. Ich habe dort viele Metadaten eingefügt, die ich möglicherweise häufig ändern möchte. Also nicht nur für einen Wert.

Gringo Suave
quelle
+1. Es ist relativ einfach, bewahrt die Versionsnummer an einem Ort auf, benötigt keine separate Datei, um sie zu enthalten, und legt die Importabhängigkeiten des Python-Moduls nicht von setup.py fest. Ich würde mich nicht einmal mit dem Kontextmanager in einem Programm beschäftigen, das so kurzlebig ist wie setup.py.
11sәɹoɈ
Viel schöner als Regex zu verwenden, danke. Sie können auch ast.get_docstring()mit einigen .split('\n')[0].strip()usw. verwenden, um automatisch descriptionaus der Quelle auszufüllen . Eine Sache weniger, um synchron zu bleiben
verloren
4

Erstellen Sie eine Datei in Ihrem Quellbaum, z. B. in yourbasedir / yourpackage / _version.py. Lassen Sie diese Datei nur eine einzige Codezeile enthalten, wie folgt:

__version__ = "1.1.0-r4704"

Öffnen Sie dann in Ihrer setup.py diese Datei und analysieren Sie die Versionsnummer wie folgt:

verstr = "unbekannt"
Versuchen:
    verstrline = open ('yourpackage / _version.py', "rt"). read ()
außer EnvironmentError:
    pass # Okay, es gibt keine Versionsdatei.
sonst:
    VSRE = r "^ __ version__ = ['\"] ([^' \ "] *) ['\"] "
    mo = re.search (VSRE, verstrline, re.M)
    wenn mo:
        verstr = mo.group (1)
    sonst:
        RuntimeError auslösen ("Version in yourpackage / _version.py konnte nicht gefunden werden")

Schließlich in yourbasedir/yourpackage/__init__.pyimport _version wie folgt:

__version__ = "unbekannt"
Versuchen:
    von _version import __version__
außer ImportError:
    # Wir laufen in einem Baum ohne _version.py, daher wissen wir nicht, was unsere Version ist.
    bestehen

Ein Beispiel für Code, der dies tut, ist das von mir verwaltete "pyutil" -Paket. (Siehe PyPI oder Google-Suche - Stackoverflow verbietet mir, einen Hyperlink in diese Antwort aufzunehmen.)

@pjeby ist richtig, dass Sie Ihr Paket nicht aus seiner eigenen setup.py importieren sollten. Das funktioniert, wenn Sie es testen, indem Sie einen neuen Python-Interpreter erstellen und als erstes setup.py ausführen: python setup.pyaber es gibt Fälle, in denen es nicht funktioniert. Das liegt daran, import youpackagedass Sie nicht das aktuelle Arbeitsverzeichnis für ein Verzeichnis mit dem Namen "yourpackage" lesen müssen, sondern im aktuellen sys.modulesnach einem Schlüssel "yourpackage" suchen und dann verschiedene Dinge tun müssen, wenn es nicht vorhanden ist. Es funktioniert also immer, wenn Sie es tun, python setup.pyweil Sie ein frisches, leeres haben sys.modules, aber das funktioniert im Allgemeinen nicht.

Was ist zum Beispiel, wenn py2exe Ihre setup.py im Rahmen des Packens einer Anwendung ausführt? Ich habe einen Fall wie diesen gesehen, in dem py2exe die falsche Versionsnummer auf ein Paket gesetzt hat, weil das Paket seine Versionsnummer von erhalten hatimport myownthingin seiner setup.py, aber eine andere Version dieses Pakets wurde zuvor während des py2exe-Laufs importiert. Was ist, wenn setuptools, easy_install, Distribute oder distutils2 versuchen, Ihr Paket als Teil eines Installationsprozesses für ein anderes Paket zu erstellen, das von Ihrem abhängt? Dann, ob Ihr Paket zum Zeitpunkt der Auswertung von setup.py importierbar ist oder ob bereits eine Version Ihres Pakets vorhanden ist, die während des Lebens dieses Python-Interpreters importiert wurde, oder ob für den Import Ihres Pakets zuerst andere Pakete installiert werden müssen oder hat Nebenwirkungen, kann die Ergebnisse ändern. Ich hatte mehrere Probleme mit dem Versuch, Python-Pakete wiederzuverwenden, was Probleme für Tools wie py2exe und setuptools verursachte, da deren setup.py das Paket selbst importiert, um seine Versionsnummer zu finden.

Übrigens spielt diese Technik gut mit Tools, mit denen die yourpackage/_version.pyDatei automatisch für Sie erstellt werden kann, z. B. indem Sie Ihren Revisionskontrollverlauf lesen und eine Versionsnummer schreiben, die auf dem neuesten Tag im Revisionskontrollverlauf basiert. Hier ist ein Tool, das das für darcs macht: http://tahoe-lafs.org/trac/darcsver/browser/trunk/README.rst und hier ist ein Code-Snippet, das dasselbe für git macht: http: // github .com / warner / python-ecdsa / blob / 0ed702a9d4057ecf33eea969b8cf280eaccd89a1 / setup.py # L34

Zooko
quelle
3

Dies sollte auch funktionieren, indem reguläre Ausdrücke verwendet werden und abhängig von den Metadatenfeldern ein Format wie das folgende verwendet wird:

__fieldname__ = 'value'

Verwenden Sie zu Beginn Ihrer setup.py Folgendes:

import re
main_py = open('yourmodule.py').read()
metadata = dict(re.findall("__([a-z]+)__ = '([^']+)'", main_py))

Danach können Sie die Metadaten in Ihrem Skript folgendermaßen verwenden:

print 'Author is:', metadata['author']
print 'Version is:', metadata['version']
thp
quelle
Wie ich oben sagte, verwenden Sie einfach str.split :)
Éric Araujo
Oh Gott nein. Sie analysieren Python in Python? Verwenden Sie mindestens eval (), child.
Slacy
3
Schlechte Ratschläge sollten vermieden werden, wenn dies so einfach ist.
Gringo Suave
3

Mit einer Struktur wie dieser:

setup.py
mymodule/
        / __init__.py
        / version.py
        / myclasses.py

wo version.py enthält:

__version__ = 'version_string'

Sie können dies in setup.py tun :

import sys

sys.path[0:0] = ['mymodule']

from version import __version__

Dies verursacht keine Probleme mit den Abhängigkeiten, die Sie in Ihrem mymodule / __ init__.py haben

Jahid
quelle
2

Um zu vermeiden, dass eine Datei importiert wird (und somit ihr Code ausgeführt wird), können Sie sie analysieren und das versionAttribut aus dem Syntaxbaum wiederherstellen :

# assuming 'path' holds the path to the file

import ast

with open(path, 'rU') as file:
    t = compile(file.read(), path, 'exec', ast.PyCF_ONLY_AST)
    for node in (n for n in t.body if isinstance(n, ast.Assign)):
        if len(node.targets) == 1:
            name = node.targets[0]
            if isinstance(name, ast.Name) and \
                    name.id in ('__version__', '__version_info__', 'VERSION'):
                v = node.value
                if isinstance(v, ast.Str):
                    version = v.s
                    break
                if isinstance(v, ast.Tuple):
                    r = []
                    for e in v.elts:
                        if isinstance(e, ast.Str):
                            r.append(e.s)
                        elif isinstance(e, ast.Num):
                            r.append(str(e.n))
                    version = '.'.join(r)
                    break

Dieser Code versucht, die __version__oder VERSION-Zuweisung auf der obersten Ebene der Modulrückgabe als Zeichenfolgenwert zu finden. Die rechte Seite kann entweder eine Zeichenfolge oder ein Tupel sein.

Mikhail Edoshin
quelle
3
Das sieht clever und kompliziert aus :) Es wäre einfacher, nur eine _version.py-Datei mit einer Zuweisung zu haben und diese mit open-read-str.split zu analysieren.
Éric Araujo
Vielen Dank für die Veröffentlichung. Ich denke, es ist zu komplex für diese Situation, aber sehr hilfreich, um zu verstehen, wie man das Problem angehen könnte. Ich mag es, dass es die Versionsnummer an einem Ort hält, keine separate Datei benötigt, um sie zu enthalten, und die Importabhängigkeiten des Python-Moduls nicht vom Setup-Skript auferlegt.
11sәɹoɈ
2

Es gibt tausend Möglichkeiten, eine Katze zu häuten - hier ist meine:

# Copied from (and hacked):
# https://github.com/pypa/virtualenv/blob/develop/setup.py#L42
def get_version(filename):
    import os
    import re

    here = os.path.dirname(os.path.abspath(__file__))
    f = open(os.path.join(here, filename))
    version_file = f.read()
    f.close()
    version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]",
                              version_file, re.M)
    if version_match:
        return version_match.group(1)
    raise RuntimeError("Unable to find version string.")
Marc Abramowitz
quelle
2

Bereinigen von https://stackoverflow.com/a/12413800 von @ gringo-suave:

from itertools import ifilter
from os import path
from ast import parse

with open(path.join('package_name', '__init__.py')) as f:
    __version__ = parse(next(ifilter(lambda line: line.startswith('__version__'),
                                     f))).body[0].value.s
BEIM
quelle
2

Das ist eklig und muss verfeinert werden (es kann sogar einen ungedeckten Mitgliederaufruf in pkg_resources geben, den ich verpasst habe), aber ich verstehe einfach nicht, warum dies nicht funktioniert oder warum es bisher niemand vorgeschlagen hat (Googeln hat es getan) nicht aufgedreht) ... beachte, dass dies Python 2.x ist und pkg_resources erfordern würde (seufz):

import pkg_resources

version_string = None
try:
    if pkg_resources.working_set is not None:
        disto_obj = pkg_resources.working_set.by_key.get('<my pkg name>', None)
        # (I like adding ", None" to gets)
        if disto_obj is not None:
            version_string = disto_obj.version
except Exception:
    # Do something
    pass
Mark Gerolimatos
quelle
2

Wir wollten die Meta - Informationen über unser Paket setzen pypackageryin __init__.py, konnte aber nicht , da sie von Drittanbietern Abhängigkeiten wie PJ Eby hat bereits (siehe seine Antwort und die Warnung in Bezug auf die Race - Bedingung) hingewiesen.

Wir haben es gelöst, indem wir ein separates Modul erstellt haben pypackagery_meta.py, das nur die Metainformationen enthält:

"""Define meta information about pypackagery package."""

__title__ = 'pypackagery'
__description__ = ('Package a subset of a monorepo and '
                   'determine the dependent packages.')
__url__ = 'https://github.com/Parquery/pypackagery'
__version__ = '1.0.0'
__author__ = 'Marko Ristin'
__author_email__ = '[email protected]'
__license__ = 'MIT'
__copyright__ = 'Copyright 2018 Parquery AG'

importierte dann die Metainformationen in packagery/__init__.py:

# ...

from pypackagery_meta import __title__, __description__, __url__, \
    __version__, __author__, __author_email__, \
    __license__, __copyright__

# ...

und schließlich verwendet in setup.py:

import pypackagery_meta

setup(
    name=pypackagery_meta.__title__,
    version=pypackagery_meta.__version__,
    description=pypackagery_meta.__description__,
    long_description=long_description,
    url=pypackagery_meta.__url__,
    author=pypackagery_meta.__author__,
    author_email=pypackagery_meta.__author_email__,
    # ...
    py_modules=['packagery', 'pypackagery_meta'],
 )

Sie müssen das Setup-Argument pypackagery_metain Ihr Paket aufnehmen py_modules. Andernfalls können Sie es bei der Installation nicht importieren, da es der gepackten Distribution fehlen würde.

marko.ristin
quelle
1

Erstellen Sie einfach und direkt eine Datei source/package_name/version.pymit dem folgenden Inhalt:

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
__version__ = "2.6.9"

Anschließend source/package_name/__init__.pyimportieren Sie in Ihre Datei die Version, die andere Benutzer verwenden können:

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
from .version import __version__

Jetzt können Sie dies anziehen setup.py

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
import re
import sys

try:
    filepath = 'source/package_name/version.py'
    version_file = open( filepath )
    __version__ ,= re.findall( '__version__ = "(.*)"', version_file.read() )

except Exception as error:
    __version__ = "0.0.1"
    sys.stderr.write( "Warning: Could not open '%s' due %s\n" % ( filepath, error ) )

finally:
    version_file.close()

Getestet dies mit Python 2.7, 3.3, 3.4, 3.5, 3.6und 3.7auf Linux, Windows und Mac OS. Ich habe mein Paket verwendet, das Integrations- und Unit-Tests für alle diese Plattformen enthält. Sie können die Ergebnisse von .travis.ymlund appveyor.ymlhier sehen:

  1. https://travis-ci.org/evandrocoan/debugtools/builds/527110800
  2. https://ci.appveyor.com/project/evandrocoan/pythondebugtools/builds/24245446

Eine alternative Version verwendet den Kontextmanager:

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
import re
import sys

try:
    filepath = 'source/package_name/version.py'

    with open( filepath ) as file:
        __version__ ,= re.findall( '__version__ = "(.*)"', file.read() )

except Exception as error:
    __version__ = "0.0.1"
    sys.stderr.write( "Warning: Could not open '%s' due %s\n" % ( filepath, error ) )

Sie können das codecsModul auch verwenden, um Unicode-Fehler sowohl in Python 2.7als auch in Python zu behandeln3.6

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
import re
import sys
import codecs

try:
    filepath = 'source/package_name/version.py'

    with codecs.open( filepath, 'r', errors='ignore' ) as file:
        __version__ ,= re.findall( '__version__ = "(.*)"', file.read() )

except Exception as error:
    __version__ = "0.0.1"
    sys.stderr.write( "Warning: Could not open '%s' due %s\n" % ( filepath, error ) )

Wenn Sie ein Python-Modul zu 100% in C / C ++ mit Python C-Erweiterungen schreiben, können Sie dasselbe tun, jedoch C / C ++ anstelle von Python verwenden.

Erstellen Sie in diesem Fall Folgendes setup.py:

#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
import re
import sys
import codecs
from setuptools import setup, Extension

try:
    filepath = 'source/version.h'

    with codecs.open( filepath, 'r', errors='ignore' ) as file:
        __version__ ,= re.findall( '__version__ = "(.*)"', file.read() )

except Exception as error:
    __version__ = "0.0.1"
    sys.stderr.write( "Warning: Could not open '%s' due %s\n" % ( filepath, error ) )

setup(
        name = 'package_name',
        version = __version__,

        package_data = {
                '': [ '**.txt', '**.md', '**.py', '**.h', '**.hpp', '**.c', '**.cpp' ],
            },

        ext_modules = [
            Extension(
                name = 'package_name',
                sources = [
                    'source/file.cpp',
                ],
                include_dirs = ['source'],
            )
        ],
    )

Welches liest die Version aus der Datei version.h:

const char* __version__ = "1.0.12";

Vergessen Sie jedoch nicht, das zu erstellen MANIFEST.in, um die version.hDatei einzuschließen:

include README.md
include LICENSE.txt

recursive-include source *.h

Und es ist in die Hauptanwendung integriert mit:

#include <Python.h>
#include "version.h"

// create the module
PyMODINIT_FUNC PyInit_package_name(void)
{
    PyObject* thismodule;
    ...

    // https://docs.python.org/3/c-api/arg.html#c.Py_BuildValue
    PyObject_SetAttrString( thismodule, "__version__", Py_BuildValue( "s", __version__ ) );

    ...
}

Verweise:

  1. Python Open File Fehler
  2. Definieren Sie eine globale in einem Python-Modul über eine C-API
  3. Wie füge ich Paketdaten in setuptools / Distribute ein?
  4. https://github.com/lark-parser/lark/blob/master/setup.py#L4
  5. Wie verwende ich setuptools-Pakete und ext_modules mit demselben Namen?
  6. Ist es möglich, Unterverzeichnisse mit dist utils (setup.py) als Teil der Paketdaten einzuschließen?
Benutzer
quelle
1

Bereitstellen der Konvention zur Paket- und Dateinamenkonvention für Indexpakete:

Beispiel für die Konvertierung der dynamischen Pip-Version:

  • Sieg:

    • test_pkg-1.0.0-cp36-cp36m-win_amd64.whl
    • test_pkg-1.0.0-py3.6-win-amd64.egg
  • Mac:

    • test_pkg-1.0.0-py3.7-macosx-10.12-x86_64.egg
    • test_pkg-1.0.0-py3.7-macosx-10.12-x86_64.whl
  • Linux:
    • test_pkg-1.0.0-cp36-cp36m-linux_x86_64.whl
from setuptools_scm import get_version

def _get_version():

     dev_version = str(".".join(map(str, str(get_version()).split("+")[0]\
            .split('.')[:-1])))

    return dev_version

Suchen Sie das Beispiel setup.py ruft die dynamische Pip-Version auf, die mit git commit übereinstimmt

setup(
    version=_get_version(),
    name=NAME,
    description=DESCRIPTION,
    long_description=LONG_DESCRIPTION,
    classifiers=CLASSIFIERS,

# add few more for wheel wheel package ...conversion

)
Narayana Reddy
quelle
0

Ich verwende eine Umgebungsvariable wie unten

VERSION = 0.0.0 python setup.py sdist bdist_wheel

In setup.py


import os

setup(
    version=os.environ['VERSION'],
    ...
)

Zur Konsistenzprüfung mit der Packer-Version verwende ich das folgende Skript.

PKG_VERSION=`python -c "import pkg; print(pkg.__version__)"`
if [ $PKG_VERSION == $VERSION ]; then
    python setup.py sdist bdist_wheel
else
    echo "Package version differs from set env variable"
fi
seongjoo
quelle