我希望我的包的版本号位于一个地方,在那里所有需要它的东西都可以引用它。
我在这本Python指南中找到了一些关于单一采购包版本的建议,并决定尝试#4,将其存储在我的项目根目录中一个名为VERSION
的简单文本文件中。
这是我项目目录树的缩短版本(你可以在GitHub上看到完整的项目):
.
├── MANIFEST.in
├── README.md
├── setup.py
├── VERSION
├── src/
│ └── fluidspaces/
│ ├── __init__.py
│ ├── __main__.py
│ ├── i3_commands.py
│ ├── rofi_commands.py
│ ├── workspace.py
│ └── workspaces.py
└── tests/
├── test_workspace.py
└── test_workspaces.py
由于VERSION
和setup.py
是兄弟,所以读取安装脚本中的版本文件并对其执行任何操作都非常容易
但是VERSION
和src/fluidspaces/__main__.py
不是兄弟,主模块不知道项目根的路径,所以我不能使用这种方法。
导游有这样的提醒:
警告:使用这种方法,您必须确保VERSION文件包含在所有源和二进制发行版中(例如,将include VERSION添加到MANIFEST.in)。
这似乎是合理的-包模块不需要项目根路径,版本文件可以在构建时复制到包中以便于访问-但我在清单中添加了这一行,版本文件似乎仍然没有显示在构建中的任何位置。
为了构建,我从项目根目录和虚拟机中运行pip install -U .
。以下是在<virtualenv>/lib/python3.6/site-packages
中创建的文件夹:
fluidspaces/
├── i3_commands.py
├── __init__.py
├── __main__.py
├── __pycache__/ # contents snipped
├── rofi_commands.py
├── workspace.py
└── workspaces.py
fluidspaces-0.1.0-py3.6.egg-info/
├── dependency_links.txt
├── entry_points.txt
├── installed-files.txt
├── PKG-INFO
├── SOURCES.txt
└── top_level.txt
我的更多配置文件:
歧管:
include README.md
include VERSION
graft src
prune tests
设置.py:
#!/usr/bin/env python3
from setuptools import setup, find_packages
def readme():
'''Get long description from readme file'''
with open('README.md') as f:
return f.read()
def version():
'''Get version from version file'''
with open('VERSION') as f:
return f.read().strip()
setup(
name='fluidspaces',
version=version(),
description='Navigate i3wm named containers',
long_description=readme(),
author='Peter Henry',
author_email='me@peterhenry.net',
url='https://github.com/mosbasik/fluidspaces',
license='MIT',
classifiers=[
'Development Status :: 3 - Alpha',
'Programming Language :: Python :: 3.6',
],
packages=find_packages('src'),
include_package_data=True,
package_dir={'': 'src'},
package_data={'': ['VERSION']},
setup_requires=[
'pytest-runner',
],
tests_require=[
'pytest',
],
entry_points={
'console_scripts': [
'fluidspaces = fluidspaces.__main__:main',
],
},
python_requires='~=3.6',
)
我发现了这个SO问题。有没有python函数可以获取"data_files"根目录?这让我觉得pkg_resources
库是解决我问题的答案,但我一直不知道如何在我的情况下使用它。
我遇到了麻烦,因为我发现的大多数示例都将python包直接放在项目根目录中,而不是隔离在src/
目录中。我之所以使用src/
目录,是因为有以下建议:
- PyTest:良好实践:应用程序代码之外的测试
- Ionel Cristian Mărieș-打包Python库
- Hynek Schlawack-测试和包装
我发现并尝试过扭转的其他旋钮是setup()
的package_data
、include_package_data
和data_files
。不知道他们有多宽容。用这些声明的东西和清单中声明的东西之间似乎有一些相互作用,但我不确定细节。
在Freenode上的#python IRC频道中与一些人就这个问题进行了交谈。我学到了:
pkg_resources
可能是我应该如何完成我的要求,但它需要将版本文件放在包目录中,而不是项目根目录中- 在
setup.py
中,我可以在不导入包本身的情况下从包目录中读取这样的版本文件(出于一些原因,这是否定的),但这需要对从根到包的路径进行硬编码,我希望避免这种情况
最终,我决定使用setuptools_scm
包从我的git标记而不是从我的repo中的文件中获取版本信息(其他人正在使用他们的包,他们的论点令人信服)。
因此,我很容易就在setup.py
中获得了我的版本号:
设置.py:
from setuptools import setup, find_packages
def readme():
'''Get long description from readme file'''
with open('README.md') as f:
return f.read()
setup(
name='fluidspaces',
use_scm_version=True, # use this instead of version
description='Navigate i3wm named containers',
long_description=readme(),
author='Peter Henry',
author_email='me@peterhenry.net',
url='https://github.com/mosbasik/fluidspaces',
license='MIT',
classifiers=[
'Development Status :: 3 - Alpha',
'Programming Language :: Python :: 3.6',
],
packages=find_packages('src'),
package_dir={'': 'src'},
setup_requires=[
'pytest-runner',
'setuptools_scm', # require package for setup
],
tests_require=[
'pytest',
],
entry_points={
'console_scripts': [
'fluidspaces = fluidspaces.__main__:main',
],
},
python_requires='~=3.6',
)
但我最终不得不有一个硬编码的路径,指示项目根相对于包代码应该是什么,这是我以前一直避免的。我认为setuptools_scm GitHub repo上的这个问题可能是为什么这是必要的。
src/fluidspaces/__main__.py:
import argparse
from setuptools_scm import get_version # import this function
def main(args=None):
# set up command line argument parsing
parser = argparse.ArgumentParser()
parser.add_argument('-V', '--version',
action='version',
version=get_version(root='../..', relative_to=__file__)) # and call it here
对于仍在寻找答案的人来说,下面是我对单一采购包版本指南的第4个变体的尝试。值得注意的是,当有其他更简单的解决方案时,你为什么会选择这种解决方案。正如链接所指出的,当您有外部工具(例如CI/CD工具)可能也想轻松检查版本时,这种方法非常有用。
文件树
myproj
├── MANIFEST.in
├── myproj
│ ├── VERSION
│ └── __init__.py
└── setup.py
myproj/VERSION
1.4.2
中的歧管
include myproj/VERSION
设置.py
with open('myproj/VERSION') as version_file:
version = version_file.read().strip()
setup(
...
version=version,
...
include_package_data=True, # needed for the VERSION file
...
)
myproj/__init__.py
import pkgutil
__name__ = 'myproj'
__version__ = pkgutil.get_data(__name__, 'VERSION').decode()
值得注意的是,在setup.cfg中设置配置是一个很好的、干净的替代方案,可以将所有内容都包含在setup.py设置函数中。您可以执行以下操作,而不是读取setup.py中的版本,然后将其包含在函数中:
设置.cfg
[metadata]
name = my_package
version = attr: myproj.VERSION
在完整的示例中,我选择将所有内容都保留在setup.py中,以方便少一个文件,并且不确定cfg解决方案是否会剥离version文件中版本周围的潜在空白。