Sürümü python paketine yerleştirmenin standart yolu nedir?


264

Aşağıdakileri yapabileceğim şekilde sürüm dizesini bir python paketi ile ilişkilendirmenin standart bir yolu var mı?

import foo
print foo.version

Ben küçük / büyük dizeleri setup.pyzaten belirtildiği için, herhangi bir ekstra hardcoding olmadan bu veri almak için bir yol olduğunu hayal ediyorum . Bulduğum alternatif çözüm import __version__benim foo/__init__.pyve daha sonra __version__.pyüretti setup.py.


7
: Bilginize, çok iyi bir bakış var packaging.python.org/en/latest/...
ionelmc

1
Yüklü bir paketin sürümü, setuptools ile meta verilerden alınabilir , bu nedenle çoğu durumda sürümü yalnızca koymak setup.pyyeterlidir. Bu soruya bakın .
saaj

2
FYI, temel olarak sürüm numarası için tek bir gerçek kaynağı (hem kurulum hem de çalışma zamanında) korumak için 5 ortak model vardır .
KF Lin

@ionelmc Python'un belgeleri tek kaynak için 7 farklı seçenek listeler . Bu " tek bir gerçek kaynağı " kavramıyla çelişmez mi?
Stevoisiak

@StevenVascellaro ne istediğinden emin değil. Orada listelenen pek çok yol var çünkü ambalaj kılavuzunun görüşülmesini istemiyor.
ionelmc

Yanıtlar:


136

Sorunuza doğrudan bir cevap değil, ama ismini vermeyi düşünmelisiniz __version__, değil version.

Bu neredeyse yarı standarttır. Standart kitaplıktaki birçok modül kullanılır __version__ve bu da birçok 3. taraf modülünde kullanılır, bu yüzden yarı standarttır.

Genellikle, __version__bir dizedir, ancak bazen aynı zamanda bir şamandıra veya demettir.

Düzenleme: S.Lott tarafından belirtildiği gibi (Teşekkür ederim!), PEP 8 açıkça söylüyor:

Modül Seviyesi Dunder İsimleri

Gibi (iki ön ve iki arka alt çizgi ile örneğin isim) Modül düzeyi "Dunders" __all__, __author__, __version__, vb modülü docstringe sonra ancak hariç herhangi bir ithalat ifadeleri önce yerleştirilmelidir __future__ithalat.

Sürüm numarasının PEP 440'ta ( PEP 386 bu standardın önceki bir sürümü) açıklanan biçime uygun olduğundan da emin olmalısınız .


9
Bir dize olmalı ve grup sürümü için bir version_info olmalıdır .
James Antill

James: Neden __version_info__özellikle? (Hangi kendi çift alt çizgi sözcüğünüzü "icat eder.) [James yorum yaptığında, alt çizgiler yorumlarda hiçbir şey yapmadı, şimdi vurgu olduğunu gösteriyorlar, bu yüzden James de gerçekten yazdı __version_info__. --- ed.]

Ne hakkında bir şey görebilirsiniz sürümü de demeliyim packages.python.org/distribute/... sayfası hakkında dağıtmak olduğunu Yani, ancak sürüm numarası anlamı bir fiili standart haline geliyor.
sienkiew

2
Sağ. Bu KEP'lerin birbiriyle çeliştikleri anlaşılıyor. PEP 8, "if" ve "crud" diyor, bu yüzden VCS anahtar kelime genişletmesini kullanarak gerçekten desteklemiyor. Ayrıca, farklı bir VCS'ye geçerseniz, düzeltme bilgilerini kaybedersiniz. Bu nedenle, en azından daha büyük projeler için tek bir kaynak dosyaya yerleştirilmiş PEP 386/440 uyumlu sürüm bilgilerinin kullanılmasını öneririm.
oefe

2
Bu sürümü nereye koyardınız . Bunun kabul edilen sürüm olduğunu düşünürsek, bu ek bilgiyi burada görmek isterim.
darkgaze

120

_version.pySürüm bilgilerini depolamak için "bir kez toplatılmış yer" olarak tek bir dosya kullanıyorum :

  1. Bir __version__özellik sağlar.

  2. Standart meta veri sürümünü sağlar. Bu nedenle pkg_resourcespaket meta verilerini (EGG-INFO ve / veya PKG-INFO, PEP 0345) ayrıştıran diğer araçlar tarafından algılanacaktır .

  3. Paketinizi oluştururken paketinizi (veya başka bir şeyi) almaz, bu da bazı durumlarda sorunlara neden olabilir. (Bunun hangi sorunlara neden olabileceğiyle ilgili aşağıdaki yorumlara bakın.)

  4. Sürüm numarasının yazıldığı tek bir yer vardır, bu nedenle sürüm numarası değiştiğinde değiştirilecek tek bir yer vardır ve tutarsız sürümlerin şansı daha azdır.

Nasıl çalışır: sürüm numarasını saklamak için "bir kanonik yer", Python paketinizde bulunan "_version.py" adlı bir .py dosyasıdır myniftyapp/_version.py. Bu dosya bir Python modülüdür, ancak setup.py dosyanız içe aktarmaz! (Bu özellik 3'ü yener.) Bunun yerine setup.py, bu dosyanın içeriğinin çok basit olduğunu bilir, şuna benzer:

__version__ = "3.6.5"

Ve setup.py dosyanız dosyayı açar ve aşağıdaki kodla ayrıştırır:

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,))

Daha sonra setup.py dosyanız bu dizeyi "version" argümanının değeri olarak iletir setup(), böylece özellik 2'yi tatmin eder.

Özellik 1'i karşılamak için, paketinizi (çalışma zamanında, kurulum zamanında değil!) _Version dosyasını şu şekilde içe aktarabilirsiniz myniftyapp/__init__.py:

from _version import __version__

İşte bu tekniğin yıllardır kullandığım bir örneği .

Bu örnekteki kod biraz daha karmaşıktır, ancak bu yoruma yazdığım basitleştirilmiş örnek tam bir uygulama olmalıdır.

İşte versiyonunu ithal örnek kod .

Bu yaklaşımla ilgili yanlış bir şey görürseniz lütfen bize bildirin.


8
# 3'ü motive eden sorunları açıklar mısınız? Glyph, "setuptools, setup.py komutunuz çalıştığında kodunuzun sistemde hiçbir yerde olmadığını iddia etmeyi sever" ile ilgili bir şey olduğunu söyledi, ancak ayrıntılar beni ve diğerlerini ikna etmeye yardımcı olacaktır.
Ivan Kozik

2
@Iva Şimdi, araç bunu hangi sırayla yapmalı? Hatta deps bilmiyor olabilir (bugün Setuptools / Pip / Virtualenv sisteminde) olan bu sizin değerlendirir kadar setup.py. Ayrıca, önce tam derinlik yapmaya ve bunu yapmadan önce tüm depsleri yapmaya çalışırsa, dairesel deps olsaydı sıkışırdı. Ancak, bağımlılıkları yüklemeden önce bu paketi oluşturmaya çalışırsa, paketinizi kendinizden içe aktarırsanız, setup.pymutlaka bölümlerini veya bölümlerinin doğru sürümlerini içe aktaramayacaktır.
Zooko

3
Ayrıştırmak yerine "setup.py" dosyasından "version.py" dosyasını yazabilir misiniz ? Bu daha basit görünüyor.
Jonathan Hartley

3
Jonathan Hartley: "setup.py" dosyanızın ayrıştırmak yerine "version.py" dosyasını yazmasının biraz daha basit olacağını kabul ediyorum, ancak setup.py'nizi düzenlediğinizde tutarsızlık için bir pencere açacaktır. yeni sürüme sahip olmak için henüz version.py dosyasını güncellemek için setup.py dosyasını henüz yürütmediniz. Standart sürümün küçük bir ayrı dosyada yer almasının bir başka nedeni , revizyon kontrol durumunuzu okuyan araçlar gibi diğer araçların sürüm dosyasını yazmasını kolaylaştırmasıdır.
Zooko

3
Benzer yaklaşım, execfile("myniftyapp/_version.py")sürüm kodunu manuel olarak ayrıştırmaya çalışmak yerine setup.py içinden. Önerilen stackoverflow.com/a/2073599/647002 - tartışma da yardımcı olabilir.
medmunds

97

2017-05 yeniden yazıldı

On yıldan uzun bir süre Python kodu yazıp çeşitli paketleri yönettikten sonra DIY'nin belki de en iyi yaklaşım olmadığı sonucuna vardım.

pbrPaketlerimde sürüm oluşturma ile ilgili paketi kullanmaya başladım . Git'i SCM'niz olarak kullanıyorsanız, bu, iş akışınıza sihir gibi sığacak ve çalışma haftalarınızı koruyacaktır (sorunun ne kadar karmaşık olabileceği konusunda şaşıracaksınız).

Bugün itibariyle pbr 11. sırada en çok kullanılan python paketi ve bu seviyeye ulaşmak herhangi bir kirli hileler içermiyordu: sadece bir tane: ortak bir ambalaj problemini çok basit bir şekilde düzeltmek.

pbr paket bakım yükünün daha fazlasını yapabilir, sürüm oluşturma ile sınırlı değildir, ancak tüm avantajlarını benimsemeye zorlamaz.

Yani nasıl bir taahhütte pbr benimsemek görünüyor hakkında bir fikir vermek için pbr ambalaj swiching bir göz var

Muhtemelen versiyonun depoda hiç saklanmadığını gözlemlediniz. PBR, Git dallarından ve etiketlerinden algılar.

Git deposu olmadığında ne olacağı konusunda endişelenmenize gerek yok, çünkü uygulamaları paketlediğinizde veya yüklediğinizde pbr "derliyor" ve sürümü önbelleğe alıyor, böylece git üzerinde çalışma zamanı bağımlılığı yok.

Eski çözüm

İşte şimdiye kadar gördüğüm en iyi çözüm ve bunun nedenini açıklıyor:

İçinde 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'

İçinde yourpackage/__init__.py:

from .version import __version__

İçinde setup.py:

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

Daha iyi görünen başka bir yaklaşım biliyorsanız bana bildirin.


12
Hata. Python 3'te execfile () yoktur, bu nedenle exec (open (). read ()) kullanmak daha iyidir.
Christophe Vu-Brugier

4
neden from .version import __version__setup.py'de de olmasın ?
Nisan

4
@Aprillion Paket setup.pyçalışırken yüklenmediğinden - deneyin, bir hata alırsınız (veya en azından yaptım :-))
darthbith

3
Pbr bağlantısı kötü bir ağ geçidi ile sonuçlanır.
MERose

4
şüphesiz pbr harika bir araçtır, ancak soruyu ele alamadısınız. Mevcut sürüme veya kurulu pakete bpr ile nasıl erişebilirsiniz .
nad2000

29

Ertelenmiş PEP 396'ya (Modül Sürüm Numaraları) göre , bunu yapmanın önerilen bir yolu vardır. Gerekçelerle, modüllerin izleyeceği (kabul edilebilir şekilde isteğe bağlı) bir standardı açıklar. İşte bir pasaj:

3) Bir modül (veya paket) bir sürüm numarası içerdiğinde, __version__öznitelikte sürüm mevcut olmalıdır .

4) Bir ad alanı paketinin içinde yaşayan modüller için, modül bu __version__özelliği içermelidir . Ad alanı paketinin kendisi kendi __version__özniteliğini İÇERMEMELİDİR .

5) Özelliğin __version__değeri bir dize OLMALIDIR.


13
Bu KEP kabul edilmez / standartlaştırılmaz, ertelenir (ilgisizlik nedeniyle). Bu nedenle, " standart bir yol var " olduğunu belirtmek biraz yanıltıcıdır .
dokumacı

@weaver: Ah benim! Yeni bir şey öğrendim. Bunun kontrol edilmesi gereken bir şey olduğunu bilmiyordum.
Oddthinking

4
Bunun standart olmadığını belirtmek için düzenlendi. Şimdi utanıyorum, çünkü projelerden bu "standardı" takip etmelerini isteyen özellik talepleri dile getirdim.
Oddthinking

1
Belki de ilginizi çektiği için bu PEP üzerinde standardizasyon çalışmalarını üstlenmelisiniz :)
dokumacı

Bu, tek bir modülün sürümlendirilmesinde işe yarar, ancak tam bir projenin sürümlendirilmesinde uygulanacağından emin değilim.
Stevoisiak

21

Bu muhtemelen çok geç olsa da, önceki cevaba göre biraz daha basit bir alternatif var:

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

(Sürüm numaralarının otomatik artan bölümlerini bir dizeye dönüştürmek oldukça basit olacaktır str().)

Tabii ki, gördüğüm kadarıyla, insanlar kullanırken daha önce belirtilen sürüm gibi bir şey kullanmaya eğilimlidirler __version_info__ve bu yüzden onu bir dizi ints olarak saklarlar; Bununla birlikte, bunu merak etmiyorum, merak veya otomatik artım dışında herhangi bir amaç için sürüm numaralarının bölümlerine toplama ve çıkarma gibi matematiksel işlemleri gerçekleştireceğinizden şüphe duyduğum için (ve o zaman bile, int()ve str()oldukça kolay kullanılabilir). (Öte yandan, başka birinin kodunun bir dize demetinden ziyade sayısal bir demet beklemesi ve bu nedenle başarısız olma olasılığı vardır.)

Bu, elbette, kendi görüşüm ve başkalarının sayısal bir demet kullanma konusundaki girdilerini memnuniyetle istiyorum.


Shezi'nin hatırlattığı gibi, sayı dizelerinin (sözcüksel) karşılaştırmaları mutlaka doğrudan sayısal karşılaştırmalar ile aynı sonuca sahip değildir; bunun için baştaki sıfırlar gerekli olacaktır. Sonuçta, __version_info__bir dizi tamsayı değeri olarak depolamak (ya da her ne denirse), daha verimli sürüm karşılaştırmaları yapılmasını mümkün kılacaktır.


12
nice (+1), ancak dizeler yerine sayıları tercih etmez misiniz? ör.__version_info__ = (1,2,3)
orip

3
Sürüm numaraları 9'u aştığında dizelerin karşılaştırılması tehlikeli olabilir, çünkü '10' <'2'.
D Coetzee

13
Ben de bunu ama ints adres hafifçe tweaked .. __version_info__ = (0, 1, 0) __version__ = '.'.join(map(str, __version_info__))
rh0dium

2
İlgili sorun __version__ = '.'.join(__version_info__)DİR __version_info__ = ('1', '2', 'beta')olacak 1.2.beta, değil 1.2betaya1.2 beta
Nagisa

4
Ben bu yaklaşım ile sorun nerede __version__ ilan kod satırları koymak olduğunu düşünüyorum. Setup.py'daysa, programınız bunları paketinden içe aktaramaz. Belki de bu sizin için bir sorun değildir, bu durumda, iyi. Programınıza girerse, setup.py'niz bunları içe aktarabilir, ancak programınızın bağımlılıkları henüz yüklenmediğinde setup.py yükleme sırasında çalıştığından (setup.py bağımlılıkların ne olduğunu belirlemek için kullanılır. .) Bu nedenle Zooko'nun cevabı: ürün paketini içe aktarmadan bir ürün kaynak dosyasının değerini manuel olarak ayrıştırın
Jonathan Hartley

11

Bu çözümlerin birçoğu gitsürüm etiketlerini yok sayar, bu da sürümü birden çok yerde izlemeniz gerektiği anlamına gelir (kötü). Buna şu hedeflerle yaklaştım:

  • Repodaki bir etiketten tüm python sürüm referanslarını gittüretme
  • Otomat git tag/ pushve setup.py uploadhiçbir giriş alır tek komutla adımları.

Nasıl çalışır:

  1. Bir make releasekomuttan, git deposundaki son etiketli sürüm bulunur ve artırılır. Etiketi geri itilir origin.

  2. MakefileDepolar sürüm src/_version.pyo tarafından okunacaktır nerede setup.pyve ayrıca açıklamasında dahil. Kaynak kontrolünü kontrol etmeyin _version.py!

  3. setup.pycommand yeni sürüm dizesini şundan okur package.__version__.

Detaylar:

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

releaseHedef her zaman 3 versiyon rakamı artırır, ancak kullanabilir next_minor_verveya next_major_verdiğer rakamları artırmak için. Komutlar versionbump.py, repo kök dizinine denetlenen komut dosyasına dayanır

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

Bu, sürüm numarasının nasıl işleneceğini ve artırılacağını ağır şekilde kaldırır git.

__init__.py

my_module/_version.pyDosya aktarılır my_module/__init__.py. Modülünüzle birlikte dağıtılmasını istediğiniz statik kurulum yapılandırmalarını buraya yerleştirin.

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

setup.py

Son adım, sürüm bilgisini my_modulemodülden okumaktır .

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

Elbette, tüm bunların işe yaraması için, başlamanızda deponuzda en az bir sürüm etiketi bulunmalıdır.

git tag -a v0.0.1

1
gerçekten - tüm bu iş parçacığı bu tartışmada bir VCS çok önemli olduğunu unutuyor. sadece bir obs: sürüm artışı manuel bir işlem olarak kalmalıdır, bu yüzden tercih edilen yol 1 olacaktır. manuel olarak bir etiket oluşturun ve itin 2. VCS araçlarının bu etiketi keşfetmesine ve gerektiğinde depolamasına izin verin (vay canına - bu SO düzenleme arayüzü gerçekten sakatlanıyor - Sadece yeni satırlarla başa çıkmak için bunu bir düzine kez düzenlemek zorunda kaldım ve hala çalışmıyor! @ # $% ^ & *)
axd

Python için versionbump.pyharika bir tampon paketimiz olduğunda kullanmaya gerek yok .
Oran

@Oran versionbump'a baktım. Dokümanlar çok net değil ve etiketlemeyi çok iyi işlemiyor. Testlerimde çökmesine neden olan durumlara giriyor gibi görünüyor: subprocess.CalledProcessError: '[' git ',' commit ',' -F ',' / var / folders / rl / tjyk4hns7kndnx035p26wg692g_7t8 / T / tmppishngbo '] 'sıfırdan farklı çıkış durumu 1 döndürdü.
cmcginty

1
Neden _version.pysürüm kontrolü ile takip edilmemeli ?
Stevoisiak

1
@StevenVascellaro Bu, sürüm sürecini zorlaştırır. Artık etiketlerinizin ve taahhütlerinizin _version.py'deki değerle eşleştiğinden emin olmalısınız. Tek bir sürüm etiketi kullanmak daha temiz IMO'dur ve doğrudan github kullanıcı arayüzünden bir sürüm oluşturabileceğiniz anlamına gelir.
cmcginty

10

Paket dir bir JSON dosyası kullanın. Bu Zooko'nun gereksinimlerine uygundur.

İçinde pkg_dir/pkg_info.json:

{"version": "0.1.0"}

İçinde 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'],
    ...
    )

İçinde 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']

Ayrıca pkg_info.jsonyazar gibi başka bilgiler de ekledim. Ben meta veri yönetimini otomatikleştirebilirim çünkü JSON kullanmayı seviyorum.


Meta veri yönetimini otomatikleştirmek için json'un nasıl kullanılacağını açıklayabilir misiniz? Teşekkürler!
ryanjdillon

6

Ayrıca kayda değer bir yanı sıra __version__yarı-std olmasıdır. python __version_info__da bir demet olduğu gibi, basit durumlarda sadece şöyle bir şey yapabilirsiniz:

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

... ve __version__dizeyi bir dosyadan ya da her neyse alabilirsiniz.


1
Bu kullanımın kökeni ile ilgili referanslarınız / bağlantılarınız var __version_info__mı?
Craig McQueen

3
Peki sys.version ile sys.version_info arasındaki eşleme. Yani: docs.python.org/library/sys.html#sys.version_info
James

7
Eşlemeyi diğer yönde yapmak daha kolaydır ( __version_info__ = (1, 2, 3)ve__version__ = '.'.join(map(str, __version_info__)) ) .
Eric O Lebigot

2
@EOL - __version__ = '.'.join(str(i) for i in __version_info__)- biraz daha uzun ama daha pitonik.
ArtOfWarfare

2
Gerçekten ihtiyaç duyulmayan ve anlamını ifade etmek biraz zor olan ( ianlamı olmayan, version_numbiraz uzun ve belirsiz…) bir kukla değişken içerdiğinden, önerdiğiniz şeyin açıkça daha pitonik olduğundan emin değilim . Hatta map()burada kullanılması gereken güçlü bir ipucu olarak Python'un varlığını bile alıyorum , çünkü burada yapmamız gereken tipik kullanım durumu map()(mevcut bir işlevle kullanın) —Başka pek çok makul kullanım görmüyorum.
Eric O Lebigot

5

Bir sürüm dizesini bir python paketine gömmenin standart bir yolu yok gibi görünüyor. Gördüğüm çoğu paket çözümünüzün bazı varyantlarını kullanıyor, yani eitner

  1. Sürüm gömün setup.pyve sahip setup.py(örneğin bir modül oluşturmak version.pyiçin paket tarafından ithal olan tek versiyon bilgisi içeren) veya

  2. Ters: sürüm paketin kendisi hakkında bilgi ve ithalat koymak olduğunu sürümü ayarlamak için setup.py


Tavsiyenizi beğendim, ancak bu modülü setup.py'dan nasıl oluşturabilirim?
sorin

1
Seçenek (1) fikrini seviyorum, Zooko'nun sürüm numarasını bir dosyadan ayrıştırma cevabından daha basit görünüyor. Ancak bir geliştirici deponuzu klonladığında version.py dosyasının oluşturulduğundan emin olamazsınız. Setup.py'daki sürüm numarasını değiştirebileceğiniz küçük kırışıklığı açan version.py öğesini denetlemediğiniz sürece, kesinleştirme, serbest bırakma ve daha sonra version.py için değişikliği taahhüt etme (eğik çizgi kullanmayı unutmayın).
Jonathan Hartley

Muhtemelen modülü fp: fp.write ("__ version__ == '% s' \ n"% (__version__,) olarak açık ("mypackage / version.py", "w") gibi bir şey kullanarak oluşturabilirsiniz. ) "" "
Jonathan Hartley

1
Seçenek 2'nin, JAB'ın cevabına yapılan yorumlarda belirtildiği gibi kurulum sırasında başarısız olmaya yatkın olduğunu düşünüyorum.
Jonathan Hartley

Buna ne dersin? __Version__ = '0.0.1' "(sürümün elbette bir dizedir) yazılımınızın ana paketinin __init__.py" dizinine yerleştirilir. Ardından 2. noktaya gidin: Kurulumda .__ init__ import __version__ öğesinden v olarak yapın ve v değişkenini setup.py sürümünüz olarak ayarlayın. Sonra benim paketi olarak benim ithalat, benim sürüm yazdırın __ sürümü yazdırır. Sürüm, yazılımla ilgili başka bir şeyi içe aktarmayan bir dosyada, tüm koddan erişilebilen tek bir yerde saklanır.
SeF

5

ok onu ilginç bir şekilde ele alıyor.

Şimdi ( 2e5031b'den beri )

İçinde arrow/__init__.py:

__version__ = 'x.y.z'

İçinde setup.py:

from arrow import __version__

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

Önce

İçinde arrow/__init__.py:

__version__ = 'x.y.z'
VERSION = __version__

İçinde setup.py:

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

file_text = read(fpath('arrow/__init__.py'))

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

nedir file_text?
ely

2
güncellenmiş çözüm aslında zararlıdır. Setup.py çalışırken, yerel dosya yolundan paketin sürümünü mutlaka görmez. Önceden yüklenmiş bir sürüme geri dönebilir, örneğin pip install -e .test sırasında geliştirme dalında çalıştırılması veya başka bir şey. setup.py, dağıtım için parametreleri belirlemek amacıyla yükleme aşamasında olduğu paketi içe aktarmaya kesinlikle güvenmemelidir. Amanın.
ely

Evet okun neden bu çözüme gerilemeye karar verdiğini bilmiyorum. Ayrıca, taahhüt mesajı " Modern Python standartlarıyla setup.py güncellendi" diyor ... 🤷
Anto

4

Ayrıca başka bir stil gördüm:

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

1
Evet, ben de gördüm. BTW her cevap başka bir stil alır, bu yüzden şimdi hangi stilin "standart" olduğunu bilmiyorum. Bahsedilen PEP'leri
arıyoruz

Başka bir yol görüldü; Mongo'nun Python istemcisi, alt çizgi olmadan düz sürümü kullanır. Yani bu işe yarıyor; $ python >>> ithalat pymongo >>> pymongo.version '2.7'
AnneTheAgile

Uygulamak .VERSION, uygulamak zorunda olmadığınız anlamına gelmez __version__.
Acumenus

Bu djangoprojede uygulanmayı gerektiriyor mu ?
Stevoisiak

3

kullanılması setuptoolsvepbr

Sürümü yönetmenin standart bir yolu yoktur, ancak paketlerinizi yönetmenin standart yolu budur setuptools.

Sürümü yönetmek için genel olarak bulduğum en iyi çözüm setuptools, pbruzantı ile kullanmaktır . Bu benim standart sürüm yönetim biçimim.

Projenizi tam paketleme için kurmak basit projeler için aşırı olabilir, ancak sürümü yönetmeniz gerekiyorsa, her şeyi ayarlamak için muhtemelen doğru seviyededir. Bunu yapmak, paketinizi PyPi'de serbest bırakılabilir hale getirir, böylece herkes Pip ile indirip kullanabilir.

PBR, çoğu meta veriyi setup.pyaraçların dışına çıkarır ve setup.cfgdaha sonra çoğu meta veri için kaynak olarak kullanılan ve sürüm içerebilen bir dosyaya taşır . Bu, meta verilerin pyinstallergerektiğinde benzer bir şey kullanılarak yürütülebilir bir dosyaya paketlenmesini sağlar (eğer öyleyse, muhtemelen bu bilgiye ihtiyacınız olacaktır ) ve meta verileri diğer paket yönetimi / kurulum komut dosyalarından ayırır. Sürüm dizesini doğrudan setup.cfgmanuel olarak güncelleyebilirsiniz ;*.egg-info paket sürümlerinizi oluştururken . Komut dosyalarınız daha sonra çeşitli yöntemler kullanarak meta verilerden sürüme erişebilir (bu işlemler aşağıdaki bölümlerde açıklanmıştır).

Git'i VCS / SCM için kullanırken, bu kurulum daha da iyidir, çünkü Git'ten birçok meta veri çeker, böylece deponuz sürüm, yazarlar, değişiklik günlükleri, Özellikle sürüm için, repo'daki git etiketlerini temel alan geçerli işlem için bir sürüm dizesi oluşturur.

PBR sürüm, yazar, changelog ve diğer bilgileri doğrudan git deponuzdan çekeceğinden setup.cfg, paketiniz için bir dağıtım oluşturulduğunda (kullanaraksetup.py )

Gerçek zamanlı geçerli sürüm

setuptoolsaşağıdakileri kullanarak en son bilgileri gerçek zamanlı olarak çeker setup.py:

python setup.py --version

Bu, en son sürümü, setup.cfgdosyada veya git deposundan, en son yapılan işleme ve depoda bulunan etiketlere göre alır. Bu komut bir dağıtımdaki sürümü güncellemez.

Sürümü güncelleme

setup.py(Örneğin py setup.py sdist,) ile bir dağıtım oluşturduğunuzda , geçerli bilgilerin tümü çıkarılır ve dağıtımda saklanır. Bu esas olarak setup.py --versionkomutu çalıştırır ve daha sonra bu sürüm bilgilerini package.egg-infodağıtım meta verilerini depolayan bir dosya kümesindeki klasöre depolar.

Sürüm meta verilerini güncelleme işlemi hakkında not:

Sürüm verilerini git'ten almak için pbr kullanmıyorsanız, setup.cfg dosyanızı doğrudan yeni sürüm bilgileriyle güncelleyin (yeterince kolay, ancak bunun sürüm işleminizin standart bir parçası olduğundan emin olun).

Git kullanıyorsanız ve bir kaynak veya ikili dağıtım oluşturmanız gerekmiyorsa ( komutlardan python setup.py sdistbirini veya python setup.py bdist_xxxkomutlardan birini kullanarak ) git repo bilgilerini <mypackage>.egg-infometa veri klasörünüze güncellemenin en basit yolu python setup.py installkomutu çalıştırmaktır . Bu, git repo'dan meta verileri çekme ile ilgili tüm PBR işlevlerini çalıştıracak ve yerel .egg-infoklasörünüzü güncelleyecek, tanımladığınız giriş noktaları için komut dosyası yürütülebilir dosyaları ve bu komutu çalıştırdığınızda çıktıdan görebileceğiniz diğer işlevleri yükleyecektir.

O Not .egg-infoklasörü genelde Git saklanmasını hariçtir standart Python kendini Repo .gitignore(örneğin itibaren dosyalar Gitignore.IO sizin kaynağından üretilebilir olarak). Hariç tutulursa, meta verileri yayınlanmadan önce yerel olarak güncellemek için standart bir "yayınlama işlemine" sahip olduğunuzdan emin olun ve PyPi.org'a yüklediğiniz veya başka bir şekilde yüklediğiniz paketlerin doğru sürüme sahip olması için bu verileri içermesi gerekir. İsterseniz Git (yani ilave edersen göz ardı ediliyor belirli dosyaları hariç tutabilirsiniz bu bilgiyi içerecek şekilde Repo !*.egg-info/PKG_INFOiçin .gitignore)

Bir komut dosyasından sürüme erişme

Meta verilere, paketin kendisindeki Python komut dosyalarındaki mevcut derlemeden erişebilirsiniz. Sürüm için, örneğin, şimdiye kadar buldum bunu yapmanın birkaç yolu vardır:

## This one is a new built-in as of Python 3.8.0 should become the standard
from importlib-metadata import version

v0 = version("mypackage")
print('v0 {}'.format(v0))

## I don't like this one because the version method is hidden
import pkg_resources  # part of setuptools

v1 = pkg_resources.require("mypackage")[0].version
print('v1 {}'.format(v1))

# Probably best for pre v3.8.0 - the output without .version is just a longer string with
# both the package name, a space, and the version string
import pkg_resources  # part of setuptools

v2 = pkg_resources.get_distribution('mypackage').version
print('v2 {}'.format(v2))

## This one seems to be slower, and with pyinstaller makes the exe a lot bigger
from pbr.version import VersionInfo

v3 = VersionInfo('mypackage').release_string()
print('v3 {}'.format(v3))

__init__.pySürüm bilgilerini aşağıdaki gibi diğer bazı yanıtlara benzer şekilde çıkarmak için bunlardan birini doğrudan paketinize koyabilirsiniz :

__all__ = (
    '__version__',
    'my_package_name'
)

import pkg_resources  # part of setuptools

__version__ = pkg_resources.get_distribution("mypackage").version

Şimdi soruya doğrudan cevap vermek için oy başına yeniden biçimlendirildi.
LightCC

1

En basit güvenilir çözümü bulmaya çalıştıktan birkaç saat sonra, parçalar şunlardır:

"/ mypackage" paketinizin İÇİNDE bir version.py dosyası oluşturun:

# 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__ = '1.2.7'

setup.py içinde:

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

ana klasörde init .py:

from .version import __version__

exec()Modül alınabilir önce setup.py çalıştırılan beri fonksiyonu, herhangi bir ithalat senaryo dışında çalışır. Yine de sürüm numarasını tek bir yerde tek bir yerde yönetmeniz gerekir, ancak ne yazık ki setup.py'de değildir. (bu dezavantaj, ancak hiçbir ithalat hatası olması ters)


1

Bu soru ilk sorulduğundan beri tek tip versiyonlama ve konvansiyonları desteklemek için birçok çalışma tamamlanmıştır . Lezzetli seçenekler artık Python Ambalaj Kullanım Kılavuzu'nda ayrıntılı olarak açıklanmaktadır . Ayrıca dikkat çeken nokta, sürüm numarası şemalarının Python'da PEP 440 başına nispeten katı olmasıdır ve bu nedenle paketiniz Peynir Dükkânına serbest bırakılacaksa, aklı başında tutmak önemlidir .

Sürüm seçeneklerinin kısaltılmış dökümü:

  1. setup.py( Setuptools) içindeki dosyayı okuyun ) ve sürümü edinin.
  2. Harici bir oluşturma aracı kullanın (her ikisini de güncellemek için) __init__.py kaynak kontrolünün yanı sıra ), örneğin bump2version , değişiklikler veya lezzet. .
  3. Değeri, __version__belirli bir modülde genel bir değişkene ayarlayın .
  4. Hem setup.py hem de okunacak kod için değeri basit bir SÜRÜM metin dosyasına yerleştirin.
  5. Değeri bir setup.pysürüm aracılığıyla ayarlayın ve çalışma zamanında almak için importlib.metadata komutunu kullanın. (Uyarı, 3.8 öncesi ve sonrası 3.8 sürümleri vardır.)
  6. Değeri __version__in olarak ayarlayın sample/__init__.pyve örneği içeri aktarın setup.py.
  7. Kaynak denetiminden sürüm oluşturmayı ayıklamak için setuptools_scm komutunu kullanın , böylece kod değil kurallı başvuru olur.

NOT (7) olabileceği en modern yaklaşım (build meta otomasyon tarafından yayınlanan kod bağımsızdır). Ayrıca NOT kurulum paketi serbest bırakılması için kullanılırsa basit o python3 setup.py --versiondoğrudan versiyonunu bildirir.


-1

Değer için, NumPy distutils kullanıyorsanız , revizyon numarasını değişkene gömen numpy.distutils.misc_util.Configurationbir make_svn_version_py()yöntem vardır .package.__svn_version__version


Daha fazla ayrıntı veya bunun nasıl çalışacağına dair bir örnek verebilir misiniz?
Stevoisiak

Hmm. 2020'de bu (her zaman mıydı?) FORTRAN içindir . "Numpy.distutils paketi, Fortran kaynakları ile başa çıkmak için standart Python dağıtımlarını genişleten NumPy'nin bir parçasıdır."
ingyhere

-1
  1. version.pyYalnızca dosyada __version__ = <VERSION>param olan bir dosya kullanın . Gelen setup.pydosyanın içe __version__param ve 's değerini koymak setup.pyböyle dosyası: version=__version__
  2. Başka bir yol da sadece bir setup.pydosya kullanmaktır version=<CURRENT_VERSION>- CURRENT_VERSION sabit kodlanmıştır.

Her yeni etiket oluşturduğumuzda (yeni bir paket sürümü yayınlamaya hazır) dosyadaki sürümü manuel olarak değiştirmek istemediğimizden, aşağıdakileri kullanabiliriz.

Tamponlama paketini tavsiye ederim . Bir versiyonu çarpmak için yıllardır kullanıyorum.

henüz yoksa version=<VERSION>, setup.pydosyanıza ekleyerek başlayın .

Bir sürümü her çıkardığınızda kısa bir komut dosyası kullanmalısınız:

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

Sonra her repo için bir dosya ekleyin .bumpversion.cfg:

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

Not:

  • Diğer yayınlarda önerildiği gibi dosya __version__altındaki parametreyi kullanabilir version.pyve bumpversion dosyasını şu şekilde güncelleyebilirsiniz: [bumpversion:file:<RELATIVE_PATH_TO_VERSION_FILE>]
  • Sen gerekir git commit veya git resetaksi takdirde kirli repo hatası alırsınız, sizin repo her şeyi.
  • Sanal ortamınızın, çalışmazsa, bumpversion paketini içerdiğinden emin olun.

@ cmcginty Gecikme için özür dilerim, lütfen cevabımı kontrol et ^ ^ ^ - deponuzdaki her şeyi sıfırla git veya git sıfırlamanız gerektiğini ve sanal ortamınızın paketini içerdiğinden emin olun, bumpversionçalışmaz. En son sürümü kullanın.
Oran

Burada hangi çözümün önerildiği konusunda biraz net değilim. Sürümü manuel olarak izlemenizi version.pyveya izlemenizi mi tavsiye ediyorsunuz bumpversion?
Stevoisiak

@StevenVascellaro Bumpversion kullanmanızı öneririm, asla manuel sürüm kullanmayın. Açıklamaya çalıştığım şey, setup.py dosyasındaki sürümü güncellemek için daha iyi bir tamponlama yönlendirebilmeniz veya henüz daha iyi bir şekilde version.py dosyasını güncellemek için kullanabilmenizdir. Version.py dosyasını güncellemek ve __version__param değerini setup.py dosyasına almak daha yaygın bir uygulamadır . Çözümüm üretimde kullanılıyor ve bu yaygın bir uygulamadır. Not: Açıkça söylemek gerekirse, bumpversion'u bir komut dosyasının parçası olarak kullanmak en iyi çözümdür, CI'nize koyun ve otomatik işlem olacaktır.
Oran

-3

CVS (veya RCS) kullanıyorsanız ve hızlı bir çözüm istiyorsanız, şunları kullanabilirsiniz:

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

(Elbette, revizyon numarası sizin için CVS ile değiştirilecektir.)

Bu size yazdırmaya uygun bir sürüm ve içe aktardığınız modülün en azından beklenen sürüme sahip olup olmadığını kontrol etmek için kullanabileceğiniz bir sürüm bilgisi sağlar:

import my_module
assert my_module.__version_info__ >= (1, 1)

Hangi dosyaya kaydetmeyi kaydetmenizi öneriyorsunuz __version__? Bu çözümle sürüm numarası nasıl artırılır?
Stevoisiak
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.