Jak mogę uzyskać wersję zdefiniowaną w setup.py (setuptools) w moim pakiecie?

Odpowiedzi:

246

Zbadaj ciąg wersji już zainstalowanej dystrybucji

Aby pobrać wersję z pakietu w czasie wykonywania (o co faktycznie chodzi w pytaniu), możesz użyć:

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

Przechowuj ciąg wersji do użycia podczas instalacji

Jeśli chcesz przejść w drugą stronę (co wydaje się być tym, o czym inni autorzy odpowiedzi myśleli, że pytasz), umieść ciąg wersji w oddzielnym pliku i przeczytaj zawartość tego pliku setup.py.

Możesz utworzyć version.py w swoim pakiecie z __version__linią, a następnie przeczytać go z pliku setup.py za pomocą execfile('mypackage/version.py'), aby ustawić go __version__w przestrzeni nazw setup.py.

Jeśli potrzebujesz prostszego sposobu, który będzie działał ze wszystkimi wersjami Pythona, a nawet językami innymi niż Python, które mogą wymagać dostępu do ciągu wersji:

Zapisz wersję jako jedyną zawartość zwykłego pliku tekstowego o nazwie np. VERSIONI przeczytaj ten plik w trakcie setup.py.

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

Ten sam VERSIONplik będzie działał wtedy dokładnie tak samo dobrze w każdym innym programie, nawet innym niż Python, i wystarczy zmienić ciąg znaków wersji w jednym miejscu dla wszystkich programów.

Ostrzeżenie o stanie wyścigu podczas instalacji

Nawiasem mówiąc, NIE importuj swojego pakietu z pliku setup.py, jak sugeruje to w innej odpowiedzi tutaj: wydaje się, że działa on dla Ciebie (ponieważ masz już zainstalowane zależności pakietu), ale sieją spustoszenie wśród nowych użytkowników Twojego pakietu , ponieważ nie będą w stanie zainstalować pakietu bez ręcznej instalacji zależności.

PJ Eby
źródło
1
To jest po prostu najlepsza odpowiedź: najlepszym sposobem jest użycie pliku wykonywalnego, jeśli potrzebujesz setup.py, aby pobrać wersję z pakietu.
trinth
2
execfiledziała bardzo dobrze ... ale (niestety) nie działa z Pythonem 3.
medmunds
9
... OK, do używania Pythona 2 i 3 with open('mypackage/version.py') as f: exec(f.read())zamiast execfile('mypackage/version.py'). (Od stackoverflow.com/a/437857/647002 )
medmunds
5
Część siać spustoszenie niekoniecznie musi być prawdą ... spowoduje to tylko spustoszenie, jeśli plik init .py w pakiecie ma zależności od importu kodu (bezpośrednio lub pośrednio).
Pykler
2
Warto wspomnieć, że django wykonuje wywołanie __import__ w swoim setup.py . Czy „__import__” w porównaniu z „importem” zapewnia bezpieczeństwo? Wydaje się, że nie powoduje to żadnych problemów.
user1978019
33

przykładowe badanie: mymodule

Wyobraź sobie taką konfigurację:

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

Następnie wyobraź sobie zwykły scenariusz, w którym masz zależności i setup.pywygląda tak:

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

I przykład __init__.py:

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

I na przykład myclasses.py:

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

problem nr 1: importowanie mymodulepodczas instalacji

Jeśli setup.pyimportujesz, mymoduleto podczas konfiguracji najprawdopodobniej otrzymasz plik ImportError. Jest to bardzo częsty błąd, gdy pakiet ma zależności. Jeśli twój pakiet nie ma innych zależności niż wbudowane, możesz być bezpieczny; jednak nie jest to dobra praktyka. Powodem tego jest to, że nie jest przyszłościowe; powiedz, że jutro twój kod musi zużywać inną zależność.

problem nr 2: gdzie jest mój __version__?

Jeśli zakodujesz __version__na stałe setup.py, może to nie odpowiadać wersji, którą dostarczysz w swoim module. Aby zachować spójność, umieściłbyś go w jednym miejscu i czytał z tego samego miejsca, kiedy tego potrzebujesz. Korzystanie z importciebie może mieć problem nr 1.

rozwiązanie: à la setuptools

Można by użyć kombinacji open, execi zapewniają dict dla execzmiennych dodają:

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

I mymodule/version.pyujawnij wersję:

__version__ = 'some.semantic.version'

W ten sposób wersja jest dostarczana z modułem i nie ma problemów podczas instalacji podczas próby zaimportowania modułu z brakującymi zależnościami (jeszcze do zainstalowania).

dnozay
źródło
2
Wydaje się, że jest to najbardziej rozsądne rozwiązanie, ponieważ opiera się tylko na dozwolonych zależnościach.
Dogweather
Czy koncepcja wersji pakietu zdefiniowana w pliku setup.py różni się od wersji ?
zmienna
16

Najlepszą techniką jest zdefiniowanie __version__kodu produktu, a następnie zaimportowanie go stamtąd do pliku setup.py. Daje to wartość, którą możesz odczytać w uruchomionym module i masz tylko jedno miejsce, aby ją zdefiniować.

Wartości w setup.py nie są instalowane, a setup.py nie utrzymuje się po instalacji.

Co zrobiłem (na przykład) w Cover.py:

# coverage/__init__.py
__version__ = "3.2"


# setup.py
from coverage import __version__

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

UPDATE (2017): Coverage.py nie importuje się już, aby pobrać wersję. Zaimportowanie własnego kodu może uniemożliwić jego odinstalowanie, ponieważ kod produktu będzie próbował zaimportować zależności, które nie są jeszcze zainstalowane, ponieważ instaluje je plik setup.py.

Ned Batchelder
źródło
4
@Evan: Nie jestem pewien, do czego zmierzasz, mówiąc o „pobraniu tylko tej wartości ze źródła”. Jeśli plik, __version__w którym się znajduje, jest w jakiś sposób uszkodzony, wówczas import również zostanie uszkodzony. Python nie wie, jak interpretować tylko te instrukcje, które chcesz. @pjeby ma rację: jeśli twój moduł musi zaimportować inne moduły, mogą nie zostać jeszcze zainstalowane i będzie chaotycznie. Ta technika działa, jeśli uważasz, że import nie powoduje długiego łańcucha innych importów.
Ned Batchelder
1
@Ned Batchelder Jestem prawie pewien, że jeśli umieścisz wersję przed jakimkolwiek importem w pliku źródłowym i tylko do wersji „z importu modułu”, nie będzie ona leksować więcej pliku źródłowego niż to konieczne. Poza tym, kto wypuści zepsuty kod? Jeśli pakiet wymaga zależności, użyj setuptools lub poczekaj na wydanie distutils2 później w tym roku.
Evan Plaice
15
@Evan, przepraszam, ale mylisz się co do importowania częściowych plików. Spróbuj umieścić instrukcję print na końcu długiego modułu i zaimportuj pierwszą zmienną zdefiniowaną w pliku. Instrukcja print zostanie wykonana.
Ned Batchelder
23
Dałem się na to nabrać, ale @pjeby ma całkowitą rację: "Przy okazji, NIE importuj swojego pakietu z twojego setup.py, jak sugeruje to w innej odpowiedzi tutaj: będzie on działał dla ciebie (ponieważ masz już zainstalowane zależności twojego pakietu ), ale będzie siać spustoszenie wśród nowych użytkowników Twojego pakietu, ponieważ nie będą oni mogli zainstalować Twojego pakietu bez wcześniejszej ręcznej instalacji zależności. ” Ned, czy mógłbyś dodać ostrzeżenie do swojej odpowiedzi?
Dr Jan-Philip Gehrcke
1
Jak @ Jan-PhilipGehrcke kiedy przesłać plik setup.py podobnego do PyPI a następnie pip install <packagename>, pojawia się następujący błąd: ImportError: No module named <packagename>. OSTRZEGAJ swoich czytelników, że nie możesz uruchamiać takich plików setup.py w środowiskach, w których pakiet nie jest jeszcze zainstalowany!
płyty grzejne
14

Twoje pytanie jest trochę niejasne, ale myślę, że pytasz, jak je sprecyzować.

Musisz zdefiniować w ten __version__sposób:

__version__ = '1.4.4'

Następnie możesz potwierdzić, że setup.py wie o podanej wersji:

% ./setup.py --version
1.4.4
jatanizm
źródło
9

Nie byłem zadowolony z tych odpowiedzi ... nie chciałem wymagać narzędzi konfiguracyjnych ani tworzyć całego oddzielnego modułu dla jednej zmiennej, więc wymyśliłem je.

Gdy jesteś pewien, że główny moduł jest w stylu pep8 i taki pozostanie:

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

Jeśli chcesz być bardzo ostrożny i użyć prawdziwego parsera:

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 jest w pewnym sensie jednorazowym modułem, więc nie stanowi problemu, jeśli jest trochę brzydki.


Aktualizacja: dość zabawne Odszedłem od tego w ostatnich latach i zacząłem używać oddzielnego pliku w pakiecie o nazwie meta.py. Umieściłem tam wiele metadanych, które chciałbym często zmieniać. A więc nie tylko dla jednej wartości.

Gringo Suave
źródło
+1. Jest stosunkowo prosty, utrzymuje numer wersji w jednym miejscu, nie wymaga oddzielnego pliku do jego przechowywania i nie narzuca zależności importu modułu python na setup.py. Nawet nie zawracałbym sobie głowy menadżerem kontekstu w programie tak krótkotrwałym jak setup.py.
ʇsәɹoɈ
O wiele ładniejsze niż używanie regex, dzięki. Możesz także użyć ast.get_docstring()z niektórymi .split('\n')[0].strip()itp., Aby automatycznie wypełnić descriptionze źródła. Jedna rzecz mniej do synchronizacji
przegrana
4

Utwórz plik w swoim drzewie źródłowym, np. W yourbasedir / yourpackage / _version.py. Niech ten plik zawiera tylko jedną linię kodu, na przykład:

__version__ = "1.1.0-r4704"

Następnie w swoim setup.py otwórz ten plik i przeanalizuj numer wersji w następujący sposób:

verstr = "nieznany"
próbować:
    verstrline = open ('yourpackage / _version.py', "rt"). read ()
z wyjątkiem EnvironmentError:
    pass # OK, brak pliku wersji.
jeszcze:
    VSRE = r "^ __ wersja__ = ['\"] ([^' \ "] *) ['\"] "
    mo = re.search (VSRE, verstrline, re.M)
    jeśli mo:
        verstr = mo.group (1)
    jeszcze:
        podnieść RuntimeError ("nie można znaleźć wersji w twoim pakiecie / _version.py")

Na koniec w yourbasedir/yourpackage/__init__.pyimport _version w ten sposób:

__version__ = "nieznane"
próbować:
    z _version import __version__
oprócz ImportError:
    # Pracujemy w drzewie, które nie ma _version.py, więc nie wiemy, jaka jest nasza wersja.
    przechodzić

Przykładem kodu, który to robi, jest pakiet „pyutil”, którym się opiekuję. (Zobacz PyPI lub wyszukiwarkę Google - stackoverflow zabrania mi umieszczania hiperłącza do niego w tej odpowiedzi).

@pjeby ma rację, że nie powinieneś importować swojego pakietu z jego własnego pliku setup.py. To zadziała, gdy przetestujesz go, tworząc nowy interpreter Pythona i uruchamiając w nim setup.py: python setup.pyale są przypadki, kiedy to nie zadziała. To dlatego, import youpackageże nie oznacza odczytywania bieżącego katalogu roboczego dla katalogu o nazwie „twój pakiet”, oznacza to szukanie w sys.modulesnim klucza „twój pakiet”, a następnie robienie różnych rzeczy, jeśli go tam nie ma. Więc zawsze działa, gdy robisz, python setup.pyponieważ masz świeży, pusty sys.modules, ale to ogólnie nie działa.

Na przykład, co się stanie, jeśli py2exe wykonuje plik setup.py w ramach procesu pakowania aplikacji? Widziałem taki przypadek, w którym py2exe umieściłby nieprawidłowy numer wersji na pakiecie, ponieważ pakiet pobierał numer wersji zimport myownthingw pliku setup.py, ale inna wersja tego pakietu została wcześniej zaimportowana podczas uruchamiania py2exe. Podobnie, co się stanie, jeśli setuptools, easy_install, dystrybucja lub distutils2 próbują zbudować Twój pakiet w ramach procesu instalowania innego pakietu, który zależy od Twojego? Następnie, czy twój pakiet jest możliwy do zaimportowania w czasie, gdy jego setup.py jest oceniany, czy też istnieje już wersja twojego pakietu, która została zaimportowana podczas życia tego interpretera Pythona, lub czy import twojego pakietu wymaga zainstalowania innych pakietów lub ma skutki uboczne, może zmienić wyniki. Miałem kilka problemów z próbą ponownego użycia pakietów Pythona, co spowodowało problemy z narzędziami takimi jak py2exe i setuptools, ponieważ ich setup.py importuje sam pakiet w celu znalezienia numeru wersji.

Nawiasem mówiąc, ta technika dobrze współgra z narzędziami do automatycznego tworzenia yourpackage/_version.pypliku, na przykład czytając historię kontroli wersji i wypisując numer wersji w oparciu o najnowszy znacznik w historii kontroli wersji. Oto narzędzie, które robi to dla darcs: http://tahoe-lafs.org/trac/darcsver/browser/trunk/README.rst, a tutaj jest fragment kodu, który robi to samo dla git: http: // github .com / warner / python-ecdsa / blob / 0ed702a9d4057ecf33eea969b8cf280eaccd89a1 / setup.py # L34

Zooko
źródło
3

Powinno to również zadziałać, używając wyrażeń regularnych i zależnie od pól metadanych, aby mieć taki format:

__fieldname__ = 'value'

Użyj poniższego na początku swojego setup.py:

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

Następnie możesz użyć metadanych w swoim skrypcie w następujący sposób:

print 'Author is:', metadata['author']
print 'Version is:', metadata['version']
thp
źródło
Jak powiedziałem powyżej, po prostu użyj str.split :)
Éric Araujo
O Boże nie. Analizujesz Python w Pythonie? Przynajmniej użyj eval (), child.
slacy
3
Słaba rada slacy, eval powinna być unikana, gdy jest taka łatwa.
Gringo Suave
3

Z taką strukturą:

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

gdzie version.py zawiera:

__version__ = 'version_string'

Możesz to zrobić w setup.py :

import sys

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

from version import __version__

Nie spowoduje to żadnych problemów z zależnościami, które masz w swoim mymodule / __ init__.py

Jahid
źródło
2

Aby uniknąć importowania pliku (a tym samym wykonywania jego kodu), można go przeanalizować i odzyskać versionatrybut z drzewa składni:

# 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

Ten kod próbuje znaleźć przypisanie __version__lub VERSIONna najwyższym poziomie zwracanego modułu jest wartością ciągu. Prawa strona może być ciągiem lub krotką.

Michaił Edoshin
źródło
3
Wygląda to na sprytne i skomplikowane :) Byłoby prościej mieć po prostu plik _version.py zawierający jedno przypisanie i przeanalizować go za pomocą open-read-str.split.
Éric Araujo
Dzięki za opublikowanie tego. Myślę, że jest to zbyt skomplikowane w tej sytuacji, ale bardzo pomocne w zrozumieniu, jak można podejść do problemu. Podoba mi się, że utrzymuje numer wersji w jednym miejscu, nie wymaga oddzielnego pliku do jego przechowywania i nie narzuca zależności importu modułu python na skrypt instalacyjny.
ʇsәɹoɈ
2

Istnieje tysiąc sposobów na oskórowanie kota - oto moje:

# 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
źródło
2

Czyszczenie https://stackoverflow.com/a/12413800 z @ 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
W
źródło
2

Teraz jest to obrzydliwe i wymaga pewnego dopracowania (może nawet być odkryte wezwanie członka w pkg_resources, które przegapiłem), ale po prostu nie rozumiem, dlaczego to nie działa, ani dlaczego nikt nie zasugerował tego do tej pory (wyszukiwanie w Google ma nie włączyłem tego) ... zauważ, że to jest Python 2.xi wymagałoby użycia pkg_resources (westchnienie):

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
źródło
2

Chcieliśmy umieścić meta informacje na temat naszego pakietu pypackageryw __init__.py, ale nie mógł, ponieważ ma zależności innych firm jako PJ Eby już wspomniano (patrz jego odpowiedź i ostrzeżenia dotyczące sytuacji wyścigu).

Rozwiązaliśmy to, tworząc osobny moduł, pypackagery_meta.pyktóry zawiera tylko metainformacje:

"""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'

następnie zaimportował metainformacje w packagery/__init__.py:

# ...

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

# ...

i ostatecznie użyłem go w 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'],
 )

Musisz dołączyć pypackagery_metado swojego pakietu z py_modulesargumentem konfiguracji. W przeciwnym razie nie możesz go zaimportować podczas instalacji, ponieważ w pakiecie dystrybucyjnym byłoby go brakuje.

marko.ristin
źródło
1

Prosty i prosty, utwórz plik o nazwie source/package_name/version.pyo następującej zawartości:

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

Następnie w swoim pliku source/package_name/__init__.pyimportujesz wersję, z której mogą korzystać inne osoby:

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

Teraz możesz to założyć 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()

Przetestowane z Pythonem 2.7, 3.3, 3.4, 3.5, 3.6i 3.7na Linux, Windows i Mac OS. Użyłem na moim pakiecie, który ma testy integracji i testów jednostkowych dla wszystkich tych platform. Można zobaczyć wyniki .travis.ymli appveyor.ymltutaj:

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

Alternatywna wersja korzysta z menedżera kontekstu:

#!/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 ) )

Możesz także używać codecsmodułu do obsługi błędów Unicode zarówno w Pythonie, jak 2.7i3.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 ) )

Jeśli piszesz moduł Pythona w 100% w C / C ++ przy użyciu Python C Extensions, możesz zrobić to samo, ale używając C / C ++ zamiast Python.

W takim przypadku utwórz 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'],
            )
        ],
    )

Który czyta wersję z pliku version.h:

const char* __version__ = "1.0.12";

Ale nie zapomnij utworzyć pliku, MANIFEST.inaby dołączyć version.hplik:

include README.md
include LICENSE.txt

recursive-include source *.h

I jest zintegrowany z główną aplikacją za pomocą:

#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__ ) );

    ...
}

Bibliografia:

  1. Błąd otwierania pliku w Pythonie
  2. Zdefiniuj globalną w module Pythona z C API
  3. Jak dołączyć dane pakietu do setuptools / Distribute?
  4. https://github.com/lark-parser/lark/blob/master/setup.py#L4
  5. Jak używać pakietów setuptools i ext_modules o tej samej nazwie?
  6. Czy możliwe jest dołączenie podkatalogów za pomocą narzędzia dist utils (setup.py) jako części danych pakietu?
użytkownik
źródło
1

wdrożyć pakiet na serwerze i konwencję nazewnictwa plików dla pakietów indeksów:

przykład dynamicznej konwersji wersji pip:

  • zdobyć:

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

    • 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

Znajdź przykład setup.py wywołuje dynamiczne dopasowanie wersji pip z git commit

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

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

)
Narayana Reddy
źródło
0

Używam zmiennej środowiskowej, jak poniżej

WERSJA = 0.0.0 python setup.py sdist bdist_wheel

W setup.py


import os

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

Do sprawdzenia spójności z wersją pakera używam poniższego skryptu.

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
źródło