Jac*_*ing 62 python distutils setuptools
使用distutils,setuptools等,包版本指定在setup.py:
# file: setup.py
...
setup(
name='foobar',
version='1.0.0',
# other attributes
)
Run Code Online (Sandbox Code Playgroud)
我希望能够从包中访问相同的版本号:
>>> import foobar
>>> foobar.__version__
'1.0.0'
Run Code Online (Sandbox Code Playgroud)
我可以添加__version__ = '1.0.0'到我的包的__init__.py,但我还想在我的包中包含其他导入以创建包的简化接口:
# file: __init__.py
from foobar import foo
from foobar.bar import Bar
__version__ = '1.0.0'
Run Code Online (Sandbox Code Playgroud)
和
# file: setup.py
from foobar import __version__
...
setup(
name='foobar',
version=__version__,
# other attributes
)
Run Code Online (Sandbox Code Playgroud)
但是,foobar如果导入其他尚未安装的软件包,这些额外的导入可能会导致安装失败.使用setup.py和软件包共享软件包版本的正确方法是什么?
Mar*_*ers 74
setup.py仅设置版本,并阅读您自己的版本pkg_resources,有效地查询setuptools元数据:
文件: setup.py
setup(
name='foobar',
version='1.0.0',
# other attributes
)
Run Code Online (Sandbox Code Playgroud)
文件: __init__.py
from pkg_resources import get_distribution
__version__ = get_distribution('foobar').version
Run Code Online (Sandbox Code Playgroud)
要在所有情况下都能正常工作,您最终可以在不安装它的情况下运行它,测试DistributionNotFound和分发位置:
from pkg_resources import get_distribution, DistributionNotFound
import os.path
try:
_dist = get_distribution('foobar')
# Normalize case for Windows systems
dist_loc = os.path.normcase(_dist.location)
here = os.path.normcase(__file__)
if not here.startswith(os.path.join(dist_loc, 'foobar')):
# not installed, but there is another version that *is*
raise DistributionNotFound
except DistributionNotFound:
__version__ = 'Please install this project with setup.py'
else:
__version__ = _dist.version
Run Code Online (Sandbox Code Playgroud)
Zer*_*eus 18
我不相信这有一个规范的答案,但我的方法(直接复制或稍微调整我在其他地方看到的)如下:
文件夹层次结构(仅限相关文件):
package_root/
|- main_package/
| |- __init__.py
| `- _version.py
`- setup.py
Run Code Online (Sandbox Code Playgroud)
main_package/_version.py:
"""Version information."""
# The following line *must* be the last in the module, exactly as formatted:
__version__ = "1.0.0"
Run Code Online (Sandbox Code Playgroud)
main_package/__init__.py:
"""Something nice and descriptive."""
from main_package.some_module import some_function_or_class
# ... etc.
from main_package._version import __version__
__all__ = (
some_function_or_class,
# ... etc.
)
Run Code Online (Sandbox Code Playgroud)
setup.py:
from setuptools import setup
setup(
version=open("main_package/_version.py").readlines()[-1].split()[-1].strip("\"'"),
# ... etc.
)
Run Code Online (Sandbox Code Playgroud)
......作为罪恶是丑陋的......但是它有效,而且我已经在人们分发的包裹中看到它或类似的东西,如果有的话,我希望知道更好的方式.
Ray*_*Luo 13
我同意@ stefano-m的哲学:
在源代码中拥有version ="xyz"并在setup.py中解析它绝对是正确的解决方案,恕我直言.比依赖运行时魔术(反过来)要好得多.
这个答案来自@ zero-piraeus的回答.重点是"不要在setup.py中使用导入,而是从文件中读取版本".
我使用正则表达式来解析__version__它,所以它根本不需要是专用文件的最后一行.事实上,我仍然把单一的事实来源__version__放在我的项目中__init__.py.
文件夹层次结构(仅限相关文件):
package_root/
|- main_package/
| `- __init__.py
`- setup.py
Run Code Online (Sandbox Code Playgroud)
main_package/__init__.py:
# You can have other dependency if you really need to
from main_package.some_module import some_function_or_class
# Define your version number in the way you mother told you,
# which is so straightforward that even your grandma will understand.
__version__ = "1.2.3"
__all__ = (
some_function_or_class,
# ... etc.
)
Run Code Online (Sandbox Code Playgroud)
setup.py:
from setuptools import setup
import re, io
__version__ = re.search(
r'__version__\s*=\s*[\'"]([^\'"]*)[\'"]', # It excludes inline comment too
io.open('main_package/__init__.py', encoding='utf_8_sig').read()
).group(1)
# The beautiful part is, I don't even need to check exceptions here.
# If something messes up, let the build process fail noisy, BEFORE my release!
setup(
version=__version__,
# ... etc.
)
Run Code Online (Sandbox Code Playgroud)
......这仍然不理想......但它确实有效.
顺便说一句,此时你可以用这种方式测试你的新玩具:
python setup.py --version
1.2.3
Run Code Online (Sandbox Code Playgroud)
PS:这个官方Python打包文档(及其镜像)描述了更多选项.它的第一个选择也是使用正则表达式.(取决于你使用的确切正则表达式,它可能会也可能不会处理版本字符串中的引号.虽然通常不是一个大问题.)
PPS:ADAL Python中的修复现在被反向移植到这个答案中.
setuptools 46.4.0 添加了基本的抽象语法树分析支持,以便setup.cfg attr: 指令无需导入包的依赖项即可工作。这使得可以拥有软件包版本的单一真实来源,从而使 setupstools 46.4.0 发布之前发布的先前答案中的许多解决方案过时。
如果 __version__ 在 yourpackage.__init__.py 中初始化,并且以下元数据添加到包的 setup.cfg 文件中,现在可以避免将版本传递给 setup.py 中的 setuptools.setup 函数。通过此配置,setuptools.setup 函数将自动从 yourpackage.__init__.py 解析包版本,并且您可以在应用程序中需要的地方自由导入 __version__.py。
setup.py没有传递给 setup 的版本
from setuptools import setup
setup(
name="yourpackage"
)
Run Code Online (Sandbox Code Playgroud)
你的包.____init__.py
__version__ = '0.2.0'
Run Code Online (Sandbox Code Playgroud)
安装程序.cfg
[metadata]
version = attr: package.__version__
Run Code Online (Sandbox Code Playgroud)
您的应用程序中的某些模块
from yourpackage import __version__ as expected_version
from pkg_distribution import get_distribution
installed_version = get_distribution("yourpackage").version
assert expected_version != installed_version
Run Code Online (Sandbox Code Playgroud)