답변:
런타임에 패키지 내부에서 버전을 검색하려면 (질문이 실제로 묻는 것) 다음을 사용할 수 있습니다.
import pkg_resources # part of setuptools
version = pkg_resources.require("MyProject")[0].version
다른 방법으로 돌아가고 싶다면 (여기서 다른 답변 작성자가 요청한 것으로 보이는 것처럼 보임) 버전 문자열을 별도의 파일에 넣고에서 해당 파일의 내용을 읽으십시오 setup.py
.
패키지에서 한 __version__
줄로 version.py를 만든 다음을 사용하여 setup.py에서 읽어서 setup.py 네임 스페이스에서 execfile('mypackage/version.py')
설정할 수 __version__
있습니다.
버전 문자열에 액세스해야하는 모든 Python 버전 및 비 Python 언어에서 작동하는 훨씬 간단한 방법을 원할 경우 :
버전 문자열을 일반 텍스트 파일 (예 : eg)의 유일한 내용으로 저장하고 VERSION
그 동안 해당 파일을 읽습니다 setup.py
.
version_file = open(os.path.join(mypackage_root_dir, 'VERSION'))
version = version_file.read().strip()
그러면 동일한 VERSION
파일이 다른 프로그램, 심지어 비 Python 파일에서도 동일 하게 작동하며 모든 프로그램에 대해 한 곳에서 버전 문자열 만 변경하면됩니다.
그건 그렇고, 다른 대답에서 제안 된 것처럼 setup.py에서 패키지를 가져 오지 마십시오. 패키지의 종속성이 이미 설치되어 있기 때문에 효과가있는 것처럼 보이지만 패키지의 새로운 사용자에게 혼란을 줄 수 있습니다 종속성을 수동으로 설치하지 않으면 패키지를 설치할 수 없습니다.
execfile
정말 잘 작동하지만 ... (슬프게도) 파이썬 3에서는 작동하지 않습니다.
with open('mypackage/version.py') as f: exec(f.read())
대신에 execfile('mypackage/version.py')
. ( stackoverflow.com/a/437857/647002에서 )
mymodule
이 구성을 상상해보십시오.
setup.py
mymodule/
/ __init__.py
/ version.py
/ myclasses.py
그런 다음 의존성이 있고 setup.py
다음과 같은 일반적인 시나리오를 상상하십시오 .
setup(...
install_requires=['dep1','dep2', ...]
...)
그리고 예 __init__.py
:
from mymodule.myclasses import *
from mymodule.version import __version__
그리고 예를 들면 myclasses.py
:
# these are not installed on your system.
# importing mymodule.myclasses would give ImportError
import dep1
import dep2
mymodule
설정 중귀하의 경우 setup.py
수입은 mymodule
다음 설치하는 동안 당신은 일 가능성을 얻을 것ImportError
. 패키지에 종속성이있는 경우 매우 일반적인 오류입니다. 패키지에 내장 이외의 다른 종속성이없는 경우 안전 할 수 있습니다. 그러나 이것은 좋은 습관이 아닙니다. 그 이유는 미래에 대비할 수 없기 때문입니다. 내일 코드에서 다른 종속성을 사용해야한다고 말하십시오.
__version__
?당신이 하드 코딩하는 경우 __version__
에 setup.py
그것은 당신이 당신의 모듈에 출시 할 것이라는 버전과 일치하지 않을 수 있습니다. 일관성을 유지하려면 한 장소에 놓고 필요할 때 같은 장소에서 읽으십시오. 사용import
하면 문제 # 1이 발생할 수 있습니다.
setuptools
당신의 조합을 사용 open
, exec
그리고에 대한 딕셔너리를 제공하는 exec
추가 변수 :
# 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__'],
...)
그리고 mymodule/version.py
버전 을 공개하십시오 :
__version__ = 'some.semantic.version'
이 방법으로, 버전은 모듈과 함께 제공되며, 설치 중에 종속성이없는 모듈을 가져 오려고 시도하는 동안 문제가 발생하지 않습니다 (아직 설치해야 함).
가장 좋은 방법은 __version__
제품 코드에서 정의한 다음 거기에서 setup.py로 가져 오는 것입니다. 이를 통해 실행중인 모듈에서 읽을 수있는 값을 정의 할 수있는 위치는 한 곳뿐입니다.
setup.py의 값이 설치되어 있지 않으며 설치 후 setup.py가 붙어 있지 않습니다.
coverage.py에서 내가 한 일 (예 :)
# coverage/__init__.py
__version__ = "3.2"
# setup.py
from coverage import __version__
setup(
name = 'coverage',
version = __version__,
...
)
업데이트 (2017) : coverage.py가 더 이상 버전을 가져 오기 위해 가져 오지 않습니다. 제품 코드는 setup.py가 설치되어 있기 때문에 아직 설치되지 않은 종속성을 가져 오려고하기 때문에 고유 코드를 가져 오면 제거 할 수 있습니다.
__version__
어떤 식 으로든 손상된 경우 가져 오기도 손상됩니다. 파이썬은 원하는 문장만을 해석하는 방법을 모릅니다. @pjeby가 맞습니다. 모듈이 다른 모듈을 가져와야 할 경우 아직 설치되지 않았을 수 있으며 혼란 스러울 수 있습니다. 이 기술은 수입품이 다른 수입품의 긴 체인을 유발하지 않도록주의 할 때 효과적입니다.
pip install <packagename>
다음과 같은 오류가 발생 ImportError: No module named <packagename>
합니다. 패키지가 아직 설치되지 않은 환경에서 이와 같은 setup.py 파일을 실행할 수 없다는 것을 독자들에게 경고하십시오!
나는이 답변에 만족하지 못했습니다 ... setuptools를 필요로하지 않고 단일 변수에 대해 별도의 모듈을 만들지 않기 때문에 이것들을 생각해 냈습니다.
메인 모듈이 pep8 스타일이고 확실하게 유지 될 때 :
version = '0.30.unknown'
with file('mypkg/mymod.py') as f:
for line in f:
if line.startswith('__version__'):
_, _, version = line.replace("'", '').split()
break
추가로주의를 기울이고 실제 파서를 사용하려는 경우 :
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는 다소 낡은 모듈이므로 조금 추한 경우에는 문제가되지 않습니다.
업데이트 : 재미있었습니다. 최근 몇 년 동안이에서 멀어지고 패키지에서 별도의 파일을 사용하기 시작했습니다 meta.py
. 나는 자주 변경하고 싶을 정도로 많은 메타 데이터를 거기에 넣었다. 따라서 하나의 가치 만이 아닙니다.
ast.get_docstring()
일부와 함께 사용할 수 있습니다 . 동기화해야 할 한 가지 적은.split('\n')[0].strip()
description
소스 트리에서 파일을 작성하십시오 (예 : yourbasedir / yourpackage / _version.py). 해당 파일에 다음과 같이 한 줄의 코드 만 포함 시키십시오.
__version__ = "1.1.0-r4704"
그런 다음 setup.py에서 해당 파일을 열고 다음과 같이 버전 번호를 구문 분석하십시오.
verstr = "알 수 없음" 시험: verstrline = open ( 'yourpackage / _version.py', "rt"). read () EnvironmentError를 제외하고 : pass # 좋아요, 버전 파일이 없습니다. 그밖에: VSRE = r "^ __ version__ = [ '\"] ([^'\ "] *) [ '\"] " mo = 재검색 (VSRE, verstrline, re.M) mo 인 경우 : verstr = mo.group (1) 그밖에: 런타임 오류 발생 ( "package / _version.py에서 버전을 찾을 수 없음")
마지막으로 yourbasedir/yourpackage/__init__.py
import _version에서 다음과 같이하십시오.
__version__ = "알 수 없음" 시험: _version import __version__에서 ImportError를 제외하고 : # _version.py가없는 트리에서 실행 중이므로 버전이 무엇인지 모릅니다. 통과하다
이를 수행하는 코드의 예는 내가 관리하는 "pyutil"패키지입니다. (PyPI 또는 Google 검색 참조-stackoverflow로 인해이 답변에 하이퍼 링크가 포함되지 않습니다.)
@pjeby는 자신의 setup.py에서 패키지를 가져 와서는 안됩니다. 새로운 파이썬 인터프리터를 만들고 setup.py를 먼저 실행하여 테스트 할 때 작동 python setup.py
하지만 작동하지 않는 경우가 있습니다. 의 때문 import youpackage
"yourpackage"라는 이름의 디렉토리에 대한 현재 작업 디렉토리를 읽을 것을 의미하지 않는다, 현재에 보는 의미 sys.modules
핵심 "yourpackage"에 대한 한 후이없는 경우 다양한 일을 할 수 있습니다. 따라서 python setup.py
비어 sys.modules
있고 비어 있기 때문에 항상 작동 하지만 일반적으로는 작동하지 않습니다.
예를 들어 py2exe가 응용 프로그램을 패키징하는 과정의 일부로 setup.py를 실행중인 경우 어떻게해야합니까? 패키지가 버전 번호를 가져 오기 때문에 py2exe가 패키지에 잘못된 버전 번호를 넣는 경우를 보았습니다.import myownthing
setup.py에 있지만 py2exe 실행 중에 해당 패키지의 다른 버전을 이전에 가져 왔습니다. 마찬가지로 setuptools, easy_install, distribution 또는 distutils2가 사용자에 따라 다른 패키지를 설치하는 프로세스의 일부로 패키지를 빌드하려고하면 어떻게합니까? 그런 다음 setup.py를 평가할 때 패키지를 가져올 수 있는지 또는이 Python 인터프리터 수명 동안 가져온 패키지 버전이 있는지 또는 패키지를 가져 오기 위해 다른 패키지를 먼저 설치해야하는지 여부 부작용이 있거나 결과가 바뀔 수 있습니다. py2exe 및 setuptools와 같은 도구에서 setup.py가 버전 번호를 찾기 위해 패키지 자체를 가져 오기 때문에 py2exe 및 setuptools와 같은 도구에 문제를 일으키는 Python 패키지를 재사용하려고 시도하는 데 어려움을 겪었습니다.
그건 그렇고,이 기술은 yourpackage/_version.py
수정 제어 기록을 읽고 수정 제어 기록의 최신 태그를 기반으로 버전 번호를 작성하는 등 자동으로 파일 을 자동으로 생성하는 도구와 함께 잘 작동 합니다. 다음은 darcs에 해당하는 도구입니다. http://tahoe-lafs.org/trac/darcsver/browser/trunk/README.rst 여기 git에 대해 동일한 작업을 수행하는 코드 스 니펫이 있습니다. http : // github .com / warner / python-ecdsa / blob / 0ed702a9d4057ecf33eea969b8cf280eaccd89a1 / setup.py # L34
정규식을 사용하고 메타 데이터 필드에 따라 다음과 같은 형식을 갖도록 작동해야합니다.
__fieldname__ = 'value'
setup.py의 시작 부분에서 다음을 사용하십시오.
import re
main_py = open('yourmodule.py').read()
metadata = dict(re.findall("__([a-z]+)__ = '([^']+)'", main_py))
그런 다음 스크립트에서 메타 데이터를 다음과 같이 사용할 수 있습니다.
print 'Author is:', metadata['author']
print 'Version is:', metadata['version']
파일 가져 오기 (및 코드 실행)를 피하기 위해 파일을 구문 분석 version
하고 구문 트리에서 속성을 복구 할 수 있습니다 .
# 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
이 코드는 모듈 반환의 최상위 수준에서 __version__
또는 VERSION
할당이 문자열 값임을 찾으려고 시도 합니다. 오른쪽은 문자열 또는 튜플 일 수 있습니다.
고양이를 깎는 방법은 수천 가지입니다. 여기 내 것이 있습니다 :
# 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에서 https://stackoverflow.com/a/12413800 정리 :
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
이제 이것은 거칠고 정제가 필요합니다 (내가 놓친 pkg_resources에서 발견되지 않은 회원 전화가있을 수도 있습니다). 그러나 왜 이것이 작동하지 않는지 또는 아무도 그것을 제안하지 않은 이유는 알지 못합니다 (구글링은 이것은 파이썬 2.x이므로 pkg_resources (sigh)가 필요합니다.
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
패키지 pypackagery
에 대한 메타 정보를 넣고 싶었습니다.__init__.py
PJ Eby가 이미 지적한 것처럼 타사 종속성을 가지고 있기 때문에 (그의 대답과 경쟁 조건에 대한 경고 참조).
메타 정보 만pypackagery_meta.py
포함 된 별도의 모듈 을 만들어서 해결했습니다 .
"""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'
그런 다음 메타 정보를 다음으로 가져 왔습니다 packagery/__init__.py
.
# ...
from pypackagery_meta import __title__, __description__, __url__, \
__version__, __author__, __author_email__, \
__license__, __copyright__
# ...
그리고 마침내 그것을 사용했습니다 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'],
)
설치 인수 pypackagery_meta
를 사용하여 패키지에 포함시켜야합니다 py_modules
. 그렇지 않으면 패키지 배포에 부족하므로 설치시 가져올 수 없습니다.
간단하고 직설적으로 source/package_name/version.py
다음 내용으로 불리는 파일을 만듭니다 .
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
__version__ = "2.6.9"
그런 다음 파일 source/package_name/__init__.py
에서 다른 사람이 사용할 수있는 버전을 가져옵니다.
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
from .version import __version__
자, 이것을 입을 수 있습니다 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()
파이썬이 테스트 2.7
, 3.3
, 3.4
, 3.5
, 3.6
및 3.7
리눅스, 윈도우와 맥 OS에. 모든 플랫폼에 대한 통합 및 단위 테스트가있는 패키지를 사용했습니다. 당신의 결과를 볼 수 .travis.yml
와 appveyor.yml
여기를 :
대체 버전은 컨텍스트 관리자를 사용하고 있습니다.
#!/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 ) )
또한 codecs
모듈을 사용하여 파이썬 2.7
과 유니 코드 모두에서 유니 코드 오류를 처리 할 수 있습니다3.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 Extensions를 사용하여 C / C ++에서 Python 모듈을 100 % 작성하는 경우 동일한 작업을 수행 할 수 있지만 Python 대신 C / C ++를 사용합니다.
이 경우 다음을 작성하십시오 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'],
)
],
)
파일에서 버전을 읽는 것 version.h
:
const char* __version__ = "1.0.12";
그러나 파일 MANIFEST.in
을 포함하도록 version.h
파일 을 작성하는 것을 잊지 마십시오 .
include README.md
include LICENSE.txt
recursive-include source *.h
그리고 다음과 같이 기본 응용 프로그램에 통합됩니다.
#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__ ) );
...
}
참고 문헌 :
인덱스 패키지를 위해 패키지를 서버에 배치하고 파일 이름 지정 규칙을 작성하십시오.
pip 동적 버전 변환의 예 :
승리:
맥:
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
샘플 setup.py를 찾으십시오 .git commit에서 동적 pip 버전 일치를 호출합니다.
setup(
version=_get_version(),
name=NAME,
description=DESCRIPTION,
long_description=LONG_DESCRIPTION,
classifiers=CLASSIFIERS,
# add few more for wheel wheel package ...conversion
)
아래와 같이 환경 변수를 사용하고 있습니다
VERSION = 0.0.0 python setup.py sdist bdist_wheel
setup.py에서
import os
setup(
version=os.environ['VERSION'],
...
)
패커 버전과의 일관성 검사를 위해 아래 스크립트를 사용하고 있습니다.
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