5

我希望我的包的版本号位于一个地方,所有需要它的东西都可以引用它。

我在此 Python guide to Single Sourcing the Package Version中找到了一些建议,并决定尝试 #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

因为VERSIONsetup.py是兄弟姐妹,所以很容易阅读安装脚本中的版本文件并用它做任何我想做的事情。

但是不是兄弟姐妹VERSIONsrc/fluidspaces/__main__.py主模块不知道项目根目录的路径,所以我不能使用这种方法。

该指南有这样的提醒:

警告:使用这种方法,您必须确保 VERSION 文件包含在您的所有源代码和二进制发行版中(例如,将 include VERSION 添加到您的 MANIFEST.in)。

这似乎是合理的 - 而不是需要项目根路径的包模块,版本文件可以在构建时复制到包中以便于访问 - 但我将该行添加到清单中并且版本文件似乎仍然没有显示在任何地方构建。

要构建,我pip install -U .从项目根目录和 virtualenv 内部运行。以下是在其中创建的文件夹<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

我的更多配置文件:

清单.in

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 question Any python function to get “data_files” root directory?这让我认为pkg_resources图书馆是我问题的答案,但我无法弄清楚如何在我的情况下使用它。

我遇到了麻烦,因为我发现的大多数示例都直接在项目根目录中包含 python 包,而不是在src/目录中隔离。src/由于以下建议,我正在使用目录:

我发现并尝试稍微扭曲的其他旋钮是package_data,include_package_datadata_fileskwargs setup()。不知道它们有多相关。似乎用这些声明的事物与清单中声明的​​事物之间存在一些相互作用,但我不确定细节。

4

2 回答 2

2

在 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
于 2017-10-23T06:25:36.903 回答
1

对于仍在寻找答案的人们,以下是我尝试遵循单一采购包版本指南的品种#4 。值得注意的是,当有其他更简单的解决方案时,为什么您可能会选择此解决方案。如链接所述,当您拥有可能还希望轻松检查版本的外部工具(例如 CI/CD 工具)时,此方法很有用。

文件树

myproj
├── MANIFEST.in
├── myproj
│   ├── VERSION
│   └── __init__.py
└── setup.py

我的项目/版本

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 中,以便减少一个文件,并且不确定 VERSION 文件中版本周围的潜在空白是否会被 cfg 解决方案剥离。

于 2020-02-04T22:20:57.670 回答