Czy istnieje standardowy sposób na powiązanie ciągu wersji z pakietem Pythona w taki sposób, że mógłbym wykonać następujące czynności?
import foo
print foo.version
Wyobrażam sobie, że istnieje sposób na odzyskanie tych danych bez dodatkowego kodowania, ponieważ łańcuchy drobne / główne są setup.py
już określone . Alternatywne rozwiązanie, które znalazłem, miałem import __version__
w swoim, foo/__init__.py
a następnie __version__.py
wygenerowałem setup.py
.
setup.py
wystarczy wstawienie tylko wersji . Zobacz to pytanie .Odpowiedzi:
Nie jest to bezpośrednia odpowiedź na twoje pytanie, ale powinieneś rozważyć nazywanie go
__version__
, nieversion
.To prawie quasi-standard. Wiele modułów w standardowej bibliotece używa
__version__
, a to jest także używane w wielu modułach innych firm, więc jest to quasi-standard.Zwykle
__version__
jest sznurkiem, ale czasami jest również zmiennoprzecinkowy lub krotkę.Edycja: jak wspomniał S.Lott (Dziękuję!), PEP 8 mówi wprost:
Należy również upewnić się, że numer wersji jest zgodny z formatem opisanym w PEP 440 ( PEP 386 poprzednia wersja tego standardu).
źródło
__version_info__
konkretnie? (Które „wymyśla” twoje słowo z podwójnym podkreśleniem.) [Kiedy James skomentował, podkreślenia nie zrobiły nic w komentarzach, teraz wskazują na nacisk, więc James też naprawdę napisał__version_info__
. --- wyd.]Używam jednego
_version.py
pliku jako „niegdyś kanonicznego miejsca” do przechowywania informacji o wersji:Zapewnia
__version__
atrybut.Zapewnia standardową wersję metadanych. Dlatego zostanie wykryty przez
pkg_resources
inne narzędzia przetwarzające metadane pakietu (EGG-INFO i / lub PKG-INFO, PEP 0345).Podczas importowania pakietu nie importuje pakietu (ani niczego innego), co może powodować problemy w niektórych sytuacjach. (Zobacz poniższe komentarze dotyczące problemów, jakie może to powodować).
Jest tylko jedno miejsce, w którym zapisany jest numer wersji, więc jest tylko jedno miejsce, aby go zmienić, gdy zmienia się numer wersji, i istnieje mniejsze prawdopodobieństwo niespójności wersji.
Oto jak to działa: „jednym kanonicznym miejscem” do przechowywania numeru wersji jest plik .py o nazwie „_version.py”, który znajduje się w pakiecie Pythona, na przykład w
myniftyapp/_version.py
. Ten plik jest modułem Python, ale plik setup.py go nie importuje! (Spowodowałoby to porażkę funkcji 3.) Zamiast tego plik setup.py wie, że zawartość tego pliku jest bardzo prosta, na przykład:I tak plik setup.py otwiera plik i analizuje go za pomocą kodu takiego jak:
Następnie plik setup.py przekazuje ten ciąg znaków jako wartość argumentu „version”
setup()
, co spełnia funkcję 2.Aby spełnić funkcję 1, możesz poprosić pakiet (w czasie wykonywania, a nie w czasie instalacji!) Zaimportować plik _version z
myniftyapp/__init__.py
następującego:Oto przykład tej techniki , której używam od lat.
Kod w tym przykładzie jest nieco bardziej skomplikowany, ale uproszczony przykład, który napisałem w tym komentarzu, powinien być kompletną implementacją.
Oto przykładowy kod importowania wersji .
Jeśli zauważysz coś złego w tym podejściu, daj mi znać.
źródło
setup.py
. Ponadto, jeśli spróbowałby wykonać pełną głębokość na początku i wykonać wszystkie operacje depresji przed wykonaniem tej operacji, utknąłby, gdyby istniały operacje depresji kołowej. Ale jeśli spróbuje zbudować ten pakiet przed zainstalowaniem zależności, to jeśli zaimportujesz pakiet z twojegosetup.py
, niekoniecznie będzie w stanie zaimportować jego deps lub odpowiednie wersje swoich deps.execfile("myniftyapp/_version.py")
w pliku setup.py, zamiast próbować ręcznie parsować kod wersji. Sugerowane w stackoverflow.com/a/2073599/647002 - dyskusja też może być pomocna.Przepisane 2017-05
Po ponad dziesięciu latach pisania kodu Python i zarządzania różnymi pakietami doszedłem do wniosku, że majsterkowanie może nie jest najlepszym podejściem.
Zacząłem używać
pbr
pakietu do zarządzania wersjami w moich pakietach. Jeśli używasz git jako SCM, będzie to pasować do twojej pracy jak magia, oszczędzając twoje tygodnie pracy (będziesz zaskoczony, jak skomplikowany może być problem).Na dzień dzisiejszy pbr zajmuje 11 miejsce wśród najczęściej używanych pakietów Pythona, a osiągnięcie tego poziomu nie zawierało żadnych brudnych sztuczek: było tylko jedno: naprawienie typowego problemu z pakowaniem w bardzo prosty sposób.
pbr
może znieść więcej obciążeń związanych z utrzymaniem pakietu, nie ogranicza się do wersji, ale nie zmusza cię do przyjęcia wszystkich jego zalet.Aby dać ci wyobrażenie o tym, jak to wygląda zaimplementować pbr w jednym zatwierdzeniu, spójrz na opakowanie pbr
Prawdopodobnie zauważyłeś, że wersja wcale nie jest przechowywana w repozytorium. PBR wykrywa go z gałęzi i tagów Git.
Nie musisz się martwić, co się stanie, gdy nie będziesz mieć repozytorium git, ponieważ pbr „kompiluje” i buforuje wersję podczas pakowania lub instalowania aplikacji, więc nie ma zależności od środowiska uruchomieniowego od git.
Stare rozwiązanie
Oto najlepsze rozwiązanie, jakie do tej pory widziałem, i wyjaśnia również, dlaczego:
Wewnątrz
yourpackage/version.py
:Wewnątrz
yourpackage/__init__.py
:Wewnątrz
setup.py
:Jeśli znasz inne podejście, które wydaje się lepsze, daj mi znać.
źródło
from .version import __version__
w setup.py?setup.py
jest uruchomiony - spróbuj, pojawi się błąd (a przynajmniej tak zrobiłem :-))Zgodnie z odroczonym PEP 396 (Numery wersji modułów) istnieje proponowany sposób, aby to zrobić. Opisuje, wraz z uzasadnieniem, (co prawda opcjonalny) standard dla kolejnych modułów. Oto fragment:
źródło
Chociaż prawdopodobnie jest to zdecydowanie za późno, istnieje nieco prostsza alternatywa dla poprzedniej odpowiedzi:
(I byłoby dość łatwo przekonwertować automatycznie zwiększające części numerów wersji na ciąg znaków za pomocą
str()
.)Oczywiście z tego, co widziałem, ludzie używają podczas używania czegoś takiego jak wcześniej wspomniana wersja
__version_info__
i jako taki przechowują ją jako krotkę int; nie widzę jednak sensu, aby to robić, ponieważ wątpię, by były sytuacje, w których można by wykonywać operacje matematyczne, takie jak dodawanie i odejmowanie części numerów wersji w jakimkolwiek celu oprócz ciekawości lub automatycznej inkrementacji (a nawet wtedy,int()
istr()
może być używany dość łatwo). (Z drugiej strony istnieje możliwość, że czyjś kod spodziewa się krotki numerycznej zamiast krotkowej i tym samym zawiedzie.)Jest to oczywiście mój własny pogląd iz chęcią bym podoba innym wkład w użycie krotki numerycznej.
Jak przypomniał mi Shezi, (leksykalne) porównania ciągów liczbowych niekoniecznie mają taki sam wynik jak bezpośrednie porównania numeryczne; do tego potrzebne byłyby początkowe zera. W końcu przechowywanie
__version_info__
(lub jakkolwiek by to nazwano) jako krotkę wartości całkowitych pozwoliłoby na bardziej wydajne porównania wersji.źródło
__version_info__ = (1,2,3)
__version_info__ = (0, 1, 0) __version__ = '.'.join(map(str, __version_info__))
__version__ = '.'.join(__version_info__)
tym__version_info__ = ('1', '2', 'beta')
, że stanie się1.2.beta
, nie1.2beta
lub1.2 beta
Wiele z tych rozwiązań ignoruje
git
tagi wersji, co wciąż oznacza, że musisz śledzić wersję w wielu miejscach (źle). Do tego podszedłem z następującymi celami:git
repozytoriumgit tag
/push
isetup.py upload
kroki za pomocą jednego polecenia, które nie wymaga żadnych danych wejściowych.Jak to działa:
Z
make release
polecenia ostatnia otagowana wersja w repozytorium git zostaje znaleziona i zwiększona. Tag został zepchnięty z powrotem doorigin
.W
Makefile
sklepach wersja wsrc/_version.py
którym zostanie on odczytany przezsetup.py
, a także zawarte w komunikacie. Nie sprawdzaj_version.py
kontroli źródła!setup.py
polecenie odczytuje ciąg nowej wersji zpackage.__version__
.Detale:
Makefile
release
Cel zawsze zwiększa 3rd wersji cyfrę, ale można użyćnext_minor_ver
lubnext_major_ver
, aby zwiększyć pozostałych cyfr. Polecenia polegają naversionbump.py
skrypcie sprawdzonym w katalogu głównym repozytoriumversionbump.py
To sprawia, że ciężkie podnoszenie, jak przetwarzać i zwiększać numer wersji
git
.__init__.py
my_module/_version.py
Plik jest importowany domy_module/__init__.py
. Umieść tutaj dowolną konfigurację instalacji statycznej, którą chcesz dystrybuować z modułem.setup.py
Ostatnim krokiem jest odczytanie informacji o wersji z
my_module
modułu.Oczywiście, aby wszystko to działało, musisz mieć co najmniej jeden tag wersji w repozytorium, aby rozpocząć.
źródło
versionbump.py
gdy mamy niesamowity pakiet bumpversion dla Pythona._version.py
należy śledzić za pomocą kontroli wersji?Używam pliku JSON w pakiecie reż. To pasuje do wymagań Zooko.
Wewnątrz
pkg_dir/pkg_info.json
:Wewnątrz
setup.py
:Wewnątrz
pkg_dir/__init__.py
:Podaję także inne informacje
pkg_info.json
, takie jak autor. Lubię używać JSON, ponieważ mogę zautomatyzować zarządzanie metadanymi.źródło
Warto również zauważyć, że oprócz tego, że
__version__
jest się pół-standardowym. w pythonie jest__version_info__
to krotka, w prostych przypadkach możesz po prostu zrobić coś takiego:... i możesz pobrać
__version__
ciąg z pliku lub cokolwiek innego.źródło
__version_info__
?__version_info__ = (1, 2, 3)
i__version__ = '.'.join(map(str, __version_info__))
).__version__ = '.'.join(str(i) for i in __version_info__)
- nieco dłuższy, ale bardziej pythoniczny.i
nie ma znaczenia,version_num
jest trochę długie i dwuznaczne…). Uważam nawet istnieniemap()
w Pythonie za silną wskazówkę, że należy go tutaj użyć, ponieważ musimy tutaj zrobić typowy przypadekmap()
użycia (użycie z istniejącą funkcją) - nie widzę wielu innych uzasadnionych zastosowań.Wydaje się, że nie ma standardowego sposobu na osadzenie ciągu wersji w pakiecie Pythona. Większość pakietów, które widziałem, używa jakiegoś wariantu twojego rozwiązania, np. Eitner
Osadź wersję
setup.py
isetup.py
wygeneruj moduł (np.version.py
) Zawierający tylko informacje o wersji, który jest importowany przez twój pakiet lubOdwrotnie: umieść informacje o wersji w samym pakiecie i zaimportuj ją , aby ustawić wersję
setup.py
źródło
strzała radzi sobie z tym w ciekawy sposób.
Teraz (od 2e5031b )
W
arrow/__init__.py
:W
setup.py
:Przed
W
arrow/__init__.py
:W
setup.py
:źródło
file_text
?pip install -e .
w gałęzi programistycznej lub coś innego podczas testowania. setup.py absolutnie nie powinien polegać na importowaniu pakietu, który jest w trakcie instalowania, aby określić parametry wdrożenia. Yikes.Widziałem też inny styl:
źródło
.VERSION
nie oznacza, że nie musisz implementować__version__
.django
w projekcie?Korzystanie
setuptools
ipbr
Nie ma standardowego sposobu zarządzania wersją, ale standardowy sposób zarządzania pakietami to
setuptools
.Najlepszym rozwiązaniem, jakie znalazłem ogólnie do zarządzania wersją, jest użycie
setuptools
zpbr
rozszerzeniem. To jest teraz mój standardowy sposób zarządzania wersją.Ustawienie projektu na pełne pakowanie może być przesadą w przypadku prostych projektów, ale jeśli potrzebujesz zarządzać wersją, prawdopodobnie jesteś na odpowiednim poziomie, aby wszystko skonfigurować. Dzięki temu Twój pakiet można zwolnić w PyPi, dzięki czemu każdy może go pobrać i używać z Pip.
PBR przenosi większość metadanych z
setup.py
narzędzi dosetup.cfg
pliku, który jest następnie wykorzystywany jako źródło większości metadanych, które mogą obejmować wersję. Umożliwia to spakowanie metadanych do pliku wykonywalnego przy użyciu czegoś takiego jak wpyinstaller
razie potrzeby (jeśli tak, prawdopodobnie będziesz potrzebować tych informacji ) i oddziela metadane od innych skryptów zarządzania / konfiguracji pakietu. Możesz bezpośrednio zaktualizować ciąg wersjisetup.cfg
ręcznie, a zostanie on pobrany do*.egg-info
folderu podczas budowania wydań pakietu. Skrypty mogą następnie uzyskać dostęp do wersji z metadanych przy użyciu różnych metod (procesy te opisano w poniższych sekcjach).Podczas korzystania z Git dla VCS / SCM ta konfiguracja jest jeszcze lepsza, ponieważ pobierze wiele metadanych z Git, dzięki czemu Twoje repozytorium może być głównym źródłem prawdy dla niektórych metadanych, w tym wersji, autorów, dzienników zmian, itp. Dla konkretnej wersji utworzy ciąg wersji dla bieżącego zatwierdzenia na podstawie znaczników git w repozytorium.
setup.py
orazsetup.cfg
plik z metadanymi.Ponieważ PBR pobierze wersję, autora, dziennik zmian i inne informacje bezpośrednio z repozytorium git, więc niektóre metadane
setup.cfg
można pominąć i wygenerować automatycznie za każdym razem, gdy tworzona jest dystrybucja pakietu (za pomocąsetup.py
)Aktualna wersja w czasie rzeczywistym
setuptools
pobierze najnowsze informacje w czasie rzeczywistym, używającsetup.py
:Spowoduje to pobranie najnowszej wersji z
setup.cfg
pliku lub z repozytorium git na podstawie ostatniego zatwierdzenia i znaczników istniejących w repozytorium. To polecenie nie aktualizuje jednak wersji w dystrybucji.Aktualizacja wersji
Po utworzeniu dystrybucji za pomocą
setup.py
(py setup.py sdist
np.) Wszystkie bieżące informacje zostaną wyodrębnione i zapisane w dystrybucji. Zasadniczo uruchamia tosetup.py --version
polecenie, a następnie przechowuje informacje o wersji wpackage.egg-info
folderze w zestawie plików przechowujących metadane dystrybucji.Dostęp do wersji ze skryptu
Możesz uzyskać dostęp do metadanych z bieżącej wersji w skryptach Python w samym pakiecie. Na przykład w przypadku wersji istnieje kilka sposobów, aby to zrobić:
Możesz umieścić jeden z nich bezpośrednio w swoim
__init__.py
pakiecie, aby wyodrębnić informacje o wersji w następujący sposób, podobnie do niektórych innych odpowiedzi:źródło
Po kilku godzinach prób znalezienia najprostszego niezawodnego rozwiązania, oto części:
utwórz plik version.py Wewnątrz folderu paczki „/ mypackage”:
w setup.py:
w głównym folderze init .py:
exec()
Funkcja uruchamia skrypt poza jakichkolwiek importu, ponieważ setup.py jest prowadzony przed moduł może być importowany. Nadal wystarczy zarządzać numerem wersji w jednym pliku w jednym miejscu, ale niestety nie ma go w pliku setup.py. (to wada, ale brak błędów importu to wada)źródło
Wiele pracy nad jednolitym wersjonowaniem i wspieraniem konwencji zostało zakończonych od pierwszego pytania . Smaczne opcje są teraz szczegółowo opisane w Podręczniku użytkownika Python Packaging . Warto również zauważyć, że schematy numerów wersji są stosunkowo surowe w Pythonie na PEP 440 , więc utrzymanie rozsądku jest niezwykle ważne, jeśli pakiet zostanie wydany w sklepie z serami .
Oto skrócony podział opcji wersji:
setup.py
( setuptools ) i pobierz wersję.__init__.py
kontrolę źródła, jak i bump2version , zmiany lub zest.releaser .__version__
zmienną globalną w określonym module.setup.py
wydania i użyj importlib.metadata, aby pobrać ją w czasie wykonywania. (Uwaga, istnieją wersje wcześniejsze niż 3.8 i nowsze).__version__
wsample/__init__.py
i importu w próbcesetup.py
.Zauważ, że (7) może być najnowocześniejszym podejściem (metadane kompilacji są niezależne od kodu publikowanego przez automatyzację). Również Uwaga , że jeśli ustawienie służy do wydania pakietu że prosty
python3 setup.py --version
zgłosi wersję bezpośrednio.źródło
Jeśli chodzi o to, co jest warte, jeśli używasz distutils NumPy,
numpy.distutils.misc_util.Configuration
mamake_svn_version_py()
metodę, która osadza numer zmianypackage.__svn_version__
w zmiennejversion
.źródło
version.py
pliku tylko z__version__ = <VERSION>
parametrami w pliku. Wsetup.py
pliku zaimportuj__version__
parametr i umieść jego wartość wsetup.py
pliku w następujący sposób:version=__version__
setup.py
pliku zversion=<CURRENT_VERSION>
- CURRENT_VERSION jest zakodowany na stałe.Ponieważ nie chcemy ręcznie zmieniać wersji w pliku za każdym razem, gdy tworzymy nowy znacznik (gotowy do wydania nowej wersji pakietu), możemy użyć następujących ...
Bardzo polecam pakiet bumpversion . Używam go od lat do wypuszczania wersji.
zacznij od dodania
version=<VERSION>
dosetup.py
pliku, jeśli jeszcze go nie masz.Za każdym razem, gdy wybierasz wersję, powinieneś używać takiego krótkiego skryptu:
Następnie dodaj jeden plik do repozytorium o nazwie
.bumpversion.cfg
:Uwaga:
__version__
parametru wversion.py
pliku tak, jak sugerowano to w innych postach i zaktualizować plik bumpversion w następujący sposób:[bumpversion:file:<RELATIVE_PATH_TO_VERSION_FILE>]
git commit
albogit reset
wszystko w swoim repo, inaczej dostaniesz brudne błąd repo.źródło
bumpversion
, bez niego nie będzie działać. Użyj najnowszej wersji.version.py
, czy śledzenie jej za pomocąbumpversion
?__version__
wartości parametru do pliku setup.py. Moje rozwiązanie jest wykorzystywane w produkcji i jest to powszechna praktyka. Uwaga: dla jasności użycie bumpversion jako części skryptu jest najlepszym rozwiązaniem, włóż go do CI i będzie to działanie automatyczne.Jeśli używasz CVS (lub RCS) i chcesz szybkiego rozwiązania, możesz użyć:
(Oczywiście numer wersji zostanie zastąpiony przez CVS.)
Daje to wersję do wydruku i informacje o wersji, których możesz użyć, aby sprawdzić, czy importowany moduł ma co najmniej oczekiwaną wersję:
źródło
__version__
? Jak zwiększyć numer wersji za pomocą tego rozwiązania?