Yanıtlar:
Süreyi çalışma zamanında paketinizin içinden almak için (sorunuzun gerçekten sorduğu soru), şunları kullanabilirsiniz:
import pkg_resources # part of setuptools
version = pkg_resources.require("MyProject")[0].version
Diğer yöne gitmek istiyorsanız (bu, diğer yanıt yazarlarının sorduğunuzu düşündüğü gibi görünüyor), sürüm dizesini ayrı bir dosyaya yerleştirin ve o dosyanın içeriğini okuyun setup.py
.
Bir ile paketinde bir version.py yapabiliriz __version__
ardından, hat kullanan setup.py dosyası onu okumak execfile('mypackage/version.py')
o ayarlar böylece, __version__
setup.py ad.
Tüm Python sürümleriyle ve hatta sürüm dizesine erişmesi gerekebilecek Python olmayan dillerle çalışacak daha basit bir yol istiyorsanız:
Sürüm dizesini, örneğin adlı bir düz metin dosyasının tek içeriği olarak saklayın VERSION
ve sırasında bu dosyayı okuyun setup.py
.
version_file = open(os.path.join(mypackage_root_dir, 'VERSION'))
version = version_file.read().strip()
Aynı VERSION
dosya Python olmayanlar da dahil olmak üzere başka herhangi bir programda da aynı şekilde çalışır ve sürüm dizesini tüm programlar için tek bir yerde değiştirmeniz yeterlidir.
Bu arada, paketinizi burada başka bir cevapta önerildiği gibi setup.py'den içe aktarmayın: sizin için çalışıyor gibi görünecektir (çünkü paketinizin bağımlılıkları zaten yüklüdür), ancak paketinizin yeni kullanıcılarını tahrip edecektir. , çünkü önce bağımlılıkları manuel olarak yüklemeden paketinizi kuramazlar.
execfile
gerçekten iyi çalışıyor ... ama (ne yazık ki) Python 3 ile çalışmıyor.
with open('mypackage/version.py') as f: exec(f.read())
yerine kullanın execfile('mypackage/version.py')
. ( Stackoverflow.com/a/437857/647002
mymodule
Bu yapılandırmayı düşünün:
setup.py
mymodule/
/ __init__.py
/ version.py
/ myclasses.py
Sonra bağımlılıkların olduğu ve setup.py
aşağıdaki gibi göründüğünüz bazı normal senaryoları düşünün :
setup(...
install_requires=['dep1','dep2', ...]
...)
Ve bir örnek __init__.py
:
from mymodule.myclasses import *
from mymodule.version import __version__
Örneğin myclasses.py
:
# these are not installed on your system.
# importing mymodule.myclasses would give ImportError
import dep1
import dep2
mymodule
Kurulum sırasında içe aktarmaEğer setup.py
ithalatınız mymodule
o zaman kurulum sırasında büyük olasılıkla bir alırsınız ImportError
. Paketinizin bağımlılıkları olduğunda bu çok yaygın bir hatadır. Paketinizde yerleşiklerden başka bağımlılıklar yoksa güvenli olabilirsiniz; ancak bu iyi bir uygulama değildir. Bunun nedeni geleceğe dönük olmamasıdır; yarın kodunuzun başka bir bağımlılık tüketmesi gerektiğini söyleyin.
__version__
?Eğer hardcode __version__
içinde setup.py
o zaman size modülünde gemi olacağını sürümünü aynı olmayabilir. Tutarlı olmak için, onu tek bir yere koyacak ve ihtiyacınız olduğunda aynı yerden okuyacaksınız. Kullanmanız import
1 numaralı sorunu çözebilir.
setuptools
Sen bir arada kullanmak istiyorsunuz open
, exec
ve bir dicti sağlamak exec
eklenti değişkenlere:
# 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__'],
...)
Ve mymodule/version.py
sürümü ortaya çıkarmak:
__version__ = 'some.semantic.version'
Bu şekilde, sürüm modülle birlikte gönderilir ve kurulum sırasında eksik bağımlılıkları olan (henüz yüklenmemiş) bir modülü almaya çalışırken sorun yaşarsınız.
En iyi teknik, __version__
ürün kodunuzda tanımlamak ve daha sonra oradan setup.py dosyasına aktarmaktır. Bu, çalışan modülünüzde okuyabileceğiniz bir değer verir ve bunu tanımlamak için yalnızca bir yer vardır.
Setup.py içindeki değerler kurulmaz ve setup.py kurulumdan sonra takılmaz.
Scope.py içinde ne yaptım (örneğin):
# coverage/__init__.py
__version__ = "3.2"
# setup.py
from coverage import __version__
setup(
name = 'coverage',
version = __version__,
...
)
GÜNCELLEME (2017): scope.py artık sürümü almak için kendini içe aktarmıyor. Kendi kodunuzu içe aktarmak, kodun kaldırılmasını sağlayabilir, çünkü ürün kodunuz henüz yüklenmemiş olan bağımlılıkları içe aktarmaya çalışır, çünkü setup.py bunları yükleyen şeydir.
__version__
içe aktarmanız da bozulur. Python sadece istediğiniz ifadeleri nasıl yorumlayacağını bilmiyor. @pjeby doğru: modülünüzün diğer modülleri içe aktarması gerekiyorsa, bunlar henüz yüklenmemiş olabilir ve kaotik olacaktır. Bu teknik, içe aktarma işleminin uzun bir başka ithalat zincirine neden olmadığına dikkat ederseniz işe yarar.
pip install <packagename>
, aşağıdaki hatayı alıyorum: ImportError: No module named <packagename>
. Lütfen okuyucunuzu, paketin henüz kurulu olmadığı ortamlarda setup.py dosyalarını çalıştıramayacağınıza UYARI!
Bu cevaplardan memnun değildim ... kurulum araçları gerektirmedi, tek bir değişken için ayrı bir modül yapmak istemedim, bu yüzden bunları buldum.
Ana modülün pep8 tarzında olduğundan ve bu şekilde kalacağından emin olduğunuzda:
version = '0.30.unknown'
with file('mypkg/mymod.py') as f:
for line in f:
if line.startswith('__version__'):
_, _, version = line.replace("'", '').split()
break
Daha dikkatli olmak ve gerçek bir ayrıştırıcı kullanmak istiyorsanız:
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 bir ıskarta modülüdür, bu yüzden biraz çirkinse sorun olmaz.
Güncelleme: yeterince komik Son yıllarda bundan uzaklaştım ve pakette ayrı bir dosya kullanmaya başladım meta.py
. Sık sık değiştirmek isteyebileceğim birçok meta veri koydum. Yani, sadece bir değer için değil.
ast.get_docstring()
bazılarıyla .split('\n')[0].strip()
otomatik doldurmak için vb description
kaynaktan. Senkronize tutmak için bir şey daha az
Kaynak ağacınızda, örneğin basedir / paketiniz / _version.py gibi bir dosya oluşturun. Bu dosyanın şu şekilde yalnızca tek bir kod satırı içermesine izin verin:
__version__ = "1.1.0-r4704"
Ardından setup.py dosyasında bu dosyayı açın ve sürüm numarasını şu şekilde ayrıştırın:
verstr = "bilinmiyor" Deneyin: verstrline = open ('paketiniz / _version.py', "rt"). read () EnvironmentError hariç: pass # Tamam, sürüm dosyası yok. Başka: VSRE = r "^ __ version__ = ['\"] ([^' \ "] *) ['\"] " mo = yeniden arama (VSRE, verstrline, re.M) eğer mo: verstr = mo.grup (1) Başka: Yükseltmek RuntimeError ("paketinizdeki sürümü bulamıyorum / _version.py")
Son olarak, yourbasedir/yourpackage/__init__.py
import_version'da şöyle:
__version__ = "bilinmiyor" Deneyin: from _version import Instagram Hesabındaki Resim ve Videoları __version__ ImportError hariç: # _Version.py olmayan bir ağaçta koşuyoruz, bu yüzden sürümümüzün ne olduğunu bilmiyoruz. geçmek
Bunu yapan bir kod örneği ben korumak "pyutil" paketidir. (Bkz. PyPI veya google arama - stackoverflow bu yanıta bir köprü eklememe izin vermiyor.)
@pjeby, paketinizi kendi setup.py dosyasından içe aktarmamanız gerektiği konusunda haklıdır. Bu, yeni bir Python yorumlayıcısı oluşturarak ve ilk şeyde setup.py uygulayarak test ettiğinizde işe yarayacaktır: python setup.py
ancak işe yaramayacağı durumlar vardır. Bunun nedeni import youpackage
, "paketiniz" adlı bir dizinin geçerli çalışma dizinini okumak anlamına gelmez, geçerli olana "paketiniz" sys.modules
anahtarını aramak ve sonra yoksa çeşitli şeyler yapmak anlamına gelir . Bu yüzden her zaman yaptığınızda işe python setup.py
yarar çünkü taze, boş bir şeyiniz vardır sys.modules
, ancak bu genel olarak çalışmaz.
Örneğin, py2exe, bir uygulamayı paketleme sürecinin bir parçası olarak setup.py dosyanızı çalıştırıyorsa ne olur? Paketin sürüm numarasını aldığı için py2exe'nin bir pakete yanlış sürüm numarasını koyacağı bir durum gördümimport myownthing
ancak bu paketin farklı bir sürümü daha önce py2exe çalıştırması sırasında içe aktarılmıştı. Benzer şekilde, setuptools, easy_install, dağıtma veya distutils2, paketinizi kendinize bağlı farklı bir paket kurma işleminin bir parçası olarak oluşturmaya çalışıyorsa ne olacak? Ardından, paketinizin setup.py değerlendirilirken içe aktarılıp aktarılmadığı veya paketinizin bu Python yorumlayıcısının ömrü boyunca içe aktarılmış bir sürümü olup olmadığı veya paketinizi içe aktarmanın önce başka paketlerin yüklenmesini gerektirip gerektirmediği , veya yan etkileri varsa, sonuçları değiştirebilir. Py2exe ve setuptools gibi araçlar için sorunlara neden olan Python paketlerini yeniden kullanmaya çalışırken çeşitli kurulumlar yaşadım çünkü onların setup.py sürüm numarasını bulmak için paketin kendisini içe aktarıyor.
Bu arada, bu teknik yourpackage/_version.py
, örneğin revizyon kontrol geçmişinizi okuyarak ve revizyon kontrol geçmişindeki en son etikete dayanan bir sürüm numarası yazarak dosyayı sizin için otomatik olarak oluşturmak için araçlarla güzelce oynar . İşte bunu darcs için yapan bir araç: http://tahoe-lafs.org/trac/darcsver/browser/trunk/README.rst ve git için aynı şeyi yapan bir kod snippet'i: http: // github .com / warner / python-ECDSA / damla / 0ed702a9d4057ecf33eea969b8cf280eaccd89a1 / setup.py # L34
Bu, düzenli ifadeler kullanarak ve meta veri alanlarına bağlı olarak aşağıdaki gibi bir biçime sahip olmalıdır:
__fieldname__ = 'value'
Setup.py dosyanızın başlangıcında aşağıdakileri kullanın:
import re
main_py = open('yourmodule.py').read()
metadata = dict(re.findall("__([a-z]+)__ = '([^']+)'", main_py))
Bundan sonra, betiğinizdeki meta verileri şu şekilde kullanabilirsiniz:
print 'Author is:', metadata['author']
print 'Version is:', metadata['version']
Böyle bir yapı ile:
setup.py
mymodule/
/ __init__.py
/ version.py
/ myclasses.py
Burada version.py şunları içerir:
__version__ = 'version_string'
Bunu setup.py'de yapabilirsiniz :
import sys
sys.path[0:0] = ['mymodule']
from version import __version__
Bu, mymodule / __ init__.py bağımlılığınızla ilgili herhangi bir soruna neden olmaz.
Bir dosyayı içe aktarmaktan (ve dolayısıyla kodunu yürütmekten) kaçınmak için dosyayı ayrıştırabilir ve version
sözdizim ağacından özniteliği kurtarabilirsiniz :
# 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
Bu kod , modül dönüşünün en üst düzeyindeki atamayı __version__
veya VERSION
dize değerini bulmaya çalışır . Sağ taraf bir dize veya bir demet olabilir.
Bir kediyi yüzmek için bin yol var - işte benim:
# 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.")
@ Gringo-suave'den https://stackoverflow.com/a/12413800 temizlenmesi :
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
Şimdi bu iğrenç ve bazı rafineri gerekiyor (pkg_resources içinde cevapsız bir üye çağrısı bile olabilir özledim), ama ben sadece bunun neden işe ya da neden kimse bugüne kadar önerdi görmüyorum (Googling vardır bunu açmadı) ... bunun Python 2.x olduğunu ve pkg_resources (sigh) gerektirdiğini unutmayın:
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
Bizim paketi hakkında meta bilgileri koymak istedim pypackagery
içinde __init__.py
ama PJ Eby zaten (onun cevabını ve yarış durumuyla ilgili uyarı bakınız) belirttiği gibi üçüncü taraf bağımlılıkları vardır değil çünkü yapamadım.
Yalnızca meta bilgileri pypackagery_meta.py
içeren ayrı bir modül oluşturarak çözdük :
"""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__ = 'marko.ristin@gmail.com'
__license__ = 'MIT'
__copyright__ = 'Copyright 2018 Parquery AG'
daha sonra meta bilgileri şuraya içe aktarın packagery/__init__.py
:
# ...
from pypackagery_meta import __title__, __description__, __url__, \
__version__, __author__, __author_email__, \
__license__, __copyright__
# ...
ve nihayetinde kullandı 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'],
)
Kurulum argümanıyla pypackagery_meta
paketinize dahil etmeniz gerekir py_modules
. Aksi takdirde, paketli dağıtım eksik olacağından kurulum sırasında içe aktaramazsınız.
Basit ve düz, source/package_name/version.py
aşağıdaki içeriklerle bir dosya oluşturun :
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
__version__ = "2.6.9"
Ardından, dosyanızda source/package_name/__init__.py
, diğer kişilerin kullanması için sürümü içe aktarırsınız:
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
from .version import __version__
Şimdi, bunu giyebilirsin 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()
Python ile bu test edilmiştir 2.7
, 3.3
, 3.4
, 3.5
, 3.6
ve 3.7
Linux, Windows ve Mac OS üzerinde. Tüm tez platformları için Entegrasyon ve Birim Testleri olan paketimde kullandım. Sonuçları buradan .travis.yml
ve appveyor.yml
buradan görebilirsiniz:
Alternatif bir sürümde bağlam yöneticisi kullanılıyor:
#!/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 ) )
Ayrıca kullanarak edilebilir codecs
unicode hataları işlemek için modül Python hem 2.7
ve3.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 ) )
Python C Uzantıları kullanarak C / C ++ 'da% 100 Python modülü yazıyorsanız, aynı şeyi yapabilirsiniz, ancak Python yerine C / C ++ kullanın.
Bu durumda aşağıdakileri oluşturun 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'],
)
],
)
Sürümü dosyadan okur version.h
:
const char* __version__ = "1.0.12";
Ancak, dosyayı MANIFEST.in
içerecek şekilde oluşturmayı unutmayın version.h
:
include README.md
include LICENSE.txt
recursive-include source *.h
Ve ana uygulamaya entegre edilmiştir:
#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__ ) );
...
}
Referanslar:
indeks paketleri için paketi sunucuya ve dosya adlandırma kuralına dağıtın:
pip dinamik sürüm dönüşümü örneği:
win:
Mac:
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
Örnek setup.py dosyasını bul git komutundan eşleşen dinamik pip sürümünü çağırır
setup(
version=_get_version(),
name=NAME,
description=DESCRIPTION,
long_description=LONG_DESCRIPTION,
classifiers=CLASSIFIERS,
# add few more for wheel wheel package ...conversion
)
Aşağıdaki gibi bir ortam değişkeni kullanıyorum
SÜRÜM = 0.0.0 python setup.py sdist bdist_wheel
Setup.py içinde
import os
setup(
version=os.environ['VERSION'],
...
)
Packer sürümü ile tutarlılık kontrolü için aşağıdaki komut dosyasını kullanıyorum.
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