在setup.py中包含非Python文件
我想知道怎么让 setup.py
包含一个不属于代码的文件?(具体来说,是一个许可证文件,但也可以是其他任何文件。)
我希望能够控制这个文件的位置。在原始的源文件夹里,这个文件在包的根目录下。(也就是说,它和最上面的 __init__.py
在同一层级。)我希望在安装包的时候,它能保持在那个位置,不管是什么操作系统。我该怎么做呢?
15 个回答
现在是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”——但把程序运行所需的重要文件遗漏掉就很糟糕了)
要实现你所描述的内容,需要两个步骤...
- 需要将文件添加到源代码压缩包中
- 需要修改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'])]
注意:以上步骤应该能以标准方式准确实现你所描述的内容,而无需任何扩展库。
最好的方法是使用 setuptools
的 package_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
。想了解更多信息,可以查看 添加非代码文件。