在setup.py中包含非Python文件

288 投票
15 回答
195692 浏览
提问于 2025-04-15 15:18

我想知道怎么让 setup.py 包含一个不属于代码的文件?(具体来说,是一个许可证文件,但也可以是其他任何文件。)

我希望能够控制这个文件的位置。在原始的源文件夹里,这个文件在包的根目录下。(也就是说,它和最上面的 __init__.py 在同一层级。)我希望在安装包的时候,它能保持在那个位置,不管是什么操作系统。我该怎么做呢?

15 个回答

32

现在是2019年,这里有一些有效的方法——尽管网上有各种建议,我发现一个半公开的做法是使用 setuptools_scm,并把它作为选项传递给 setuptools.setup。这样做可以把你在版本控制系统(比如git)中管理的所有数据文件都包含到你的轮子包(wheel package)里,并且在从git仓库用“pip install”安装时,会把这些文件一并带上。

所以,我只是在“setup.py”的设置调用中添加了这两行代码。无需额外安装或导入任何东西:

    setup_requires=['setuptools_scm'],
    include_package_data=True,

不需要手动列出包数据,或者在MANIFEST.in文件中指定——只要文件是被版本控制的,它就会被包含在包里。“setuptools_scm”的文档强调了如何从提交的位置生成版本号,但忽略了添加数据文件这个非常重要的部分。(我并不在乎我的中间轮子文件名是“*0.2.2.dev45+g3495a1f”还是我手动输入的硬编码版本号“0.3.0dev0”——但把程序运行所需的重要文件遗漏掉就很糟糕了)

45

要实现你所描述的内容,需要两个步骤...

  • 需要将文件添加到源代码压缩包中
  • 需要修改setup.py,以便将数据文件安装到源代码路径

第一步:将文件添加到源代码压缩包中,需在MANIFEST中包含它

在包含setup.py的文件夹中创建一个MANIFEST模板

MANIFEST基本上是一个文本文件,列出了所有将包含在源代码压缩包中的文件。

以下是我项目的MANIFEST示例:

  • CHANGELOG.txt
  • INSTALL.txt
  • LICENSE.txt
  • pypreprocessor.py
  • README.txt
  • setup.py
  • test.py
  • TODO.txt

注意:虽然sdist 确实会自动添加一些文件,但我更喜欢明确指定它们,以确保不会猜测它会添加哪些文件。

第二步:要将数据文件安装到源代码文件夹中,修改setup.py

因为你想将一个数据文件(LICENSE.txt)添加到源代码安装文件夹,所以需要修改数据安装路径,使其与源代码安装路径匹配。这是必要的,因为默认情况下,数据文件会安装到与源文件不同的位置。

要修改数据安装目录以匹配源安装目录...

从distutils中提取安装目录信息:

from distutils.command.install import INSTALL_SCHEMES

修改数据安装目录以匹配源安装目录:

for scheme in INSTALL_SCHEMES.values():
    scheme['data'] = scheme['purelib']

然后,将数据文件和位置添加到setup()中:

data_files=[('', ['LICENSE.txt'])]

注意:以上步骤应该能以标准方式准确实现你所描述的内容,而无需任何扩展库。

315

最好的方法是使用 setuptoolspackage_data 指令。这意味着你需要用 setuptools(或者 distribute)来替代 distutils,不过这个转换非常简单。

下面是一个完整的(但没有测试过的)例子:

from setuptools import setup, find_packages

setup(
    name='your_project_name',
    version='0.1',
    description='A description.',
    packages=find_packages(exclude=['ez_setup', 'tests', 'tests.*']),
    package_data={'': ['license.txt']},
    include_package_data=True,
    install_requires=[],
)

注意这里几个关键的行:

package_data={'': ['license.txt']},
include_package_data=True,

package_data 是一个字典,里面包含包的名称(空表示所有包)和一个模式列表(可以包含通配符)。例如,如果你只想指定包内的文件,也可以这样做:

package_data={'yourpackage': ['*.txt', 'path/to/resources/*.txt']}

这里的解决方案绝对不是把你的非 py 文件重命名为 .py 后缀。

想了解更多信息,可以查看 Ian Bicking 的演示文稿

更新:另一种[更好的]方法

如果你只是想控制源代码分发(sdist)的内容,并且有一些文件在包外(比如顶层目录),那么可以添加一个 MANIFEST.in 文件。关于这个文件的格式,可以查看 Python 文档

在写这个回复后,我发现使用 MANIFEST.in 通常是一个更简单的方法,可以确保你的源代码分发(tar.gz)包含你需要的文件。

例如,如果你想从顶层包含 requirements.txt 文件,递归地包含顶层的 "data" 目录:

include requirements.txt
recursive-include data *

不过,为了让这些文件在安装时被复制到包的文件夹(在 site-packages 里),你需要在 setup() 函数中添加 include_package_data=True。想了解更多信息,可以查看 添加非代码文件

撰写回答