如何在 setuptools/distutils 中包含包数据?

在使用 setuptools 时,我无法让安装程序拉入任何 package_data文件。我读到的所有资料都说下面的方法是正确的。有人能给点建议吗?

setup(
name='myapp',
packages=find_packages(),
package_data={
'myapp': ['data/*.txt'],
},
include_package_data=True,
zip_safe=False,
install_requires=['distribute'],
)

其中 myapp/data/是数据文件的位置。

119802 次浏览

更新 : 该答案已经过时,信息不再有效。所有 setup.py 配置都应该使用 import setuptools。我在 https://stackoverflow.com/a/49501350/64313中添加了一个更完整的答案


- 我用蒸馏酒解决了这个问题-看起来分发系统已经废弃或者坏了。

from distutils.core import setup


setup(
name='myapp',
packages=['myapp'],
package_data={
'myapp': ['data/*.txt'],
},
)

将包含包数据的文件夹移动到模块文件夹为我解决了这个问题。

看这个问题: 在“ python setup.py install”中忽略 MANIFEST.in ——没有安装数据文件?

我也遇到过同样的问题,解决方法就是移除 include_package_data=True

在这里读书之后,我意识到 include_package_data的目标是包含来自 版本控制的文件,而不是像名字所暗示的那样仅仅“包含包数据”。来自文件:

数据文件[ include _ package _ data ]必须在 CVS 或 Subversion 控制下

...

如果希望对包含哪些文件进行更细粒度的控制(例如,如果 您的包目录中有文档文件并希望排除 ) ,那么你也可以使用 package_data关键字。

去掉这个参数就修复了它,这也是为什么当你切换到 distutils 时,它也能工作的原因,因为它不需要这个参数。

我知道这是一个老问题,但是对于通过谷歌找到这里的人来说: package_data是一个低级的,肮脏的谎言。它只在构建 二进制包(python setup.py bdist ...)时使用,而在构建源包(python setup.py sdist ...)时使用 没有。当然,这是荒谬的——人们可能会认为构建源发行版会产生一个文件集合,这些文件可以发送给其他人来构建二进制发行版。

在任何情况下,对于二进制和源发行版,使用 MANIFEST.in都可以使用 都有

按照@Joe 的建议删除 include_package_data=True线路对我也有效。

更详细地说,我有 没有MANIFEST.in文件。我使用 Git 而不是 CVS。

储存库采用这种形式:

/myrepo
- .git/
- setup.py
- myproject
- __init__.py
- some_mod
- __init__.py
- animals.py
- rocks.py
- config
- __init__.py
- settings.py
- other_settings.special
- cool.huh
- other_settings.xml
- words
- __init__.py
word_set.txt

返回文章页面

from setuptools import setup, find_packages
import os.path


setup (
name='myproject',
version = "4.19",
packages = find_packages(),
# package_dir={'mypkg': 'src/mypkg'},  # didnt use this.
package_data = {
# If any package contains *.txt or *.rst files, include them:
'': ['*.txt', '*.xml', '*.special', '*.huh'],
},


#
# Oddly enough, include_package_data=True prevented package_data from working.
# include_package_data=True, # Commented out.
data_files=[
#               ('bitmaps', ['bm/b1.gif', 'bm/b2.gif']),
('/opt/local/myproject/etc', ['myproject/config/settings.py', 'myproject/config/other_settings.special']),
('/opt/local/myproject/etc', [os.path.join('myproject/config', 'cool.huh')]),
#
('/opt/local/myproject/etc', [os.path.join('myproject/config', 'other_settings.xml')]),
('/opt/local/myproject/data', [os.path.join('myproject/words', 'word_set.txt')]),
],


install_requires=[ 'jsonschema',
'logging', ],


entry_points = {
'console_scripts': [
# Blah...
], },
)

我为源发行版运行 python setup.py sdist(还没有尝试二进制)。

在一个全新的虚拟环境中,我有一个 myproject-4.19.tar.gz文件, 我用

(venv) pip install ~/myproject-4.19.tar.gz
...

除了将所有东西都安装到我的虚拟环境的 site-packages之外,这些特殊的数据文件还将被安装到 /opt/local/myproject/data/opt/local/myproject/etc

include_package_data=True为我工作。

如果使用 git,请记住在 install_requires中包含 setuptools-git。比起 Manifest或者包含 package_data中的所有路径(在我的例子中,它是一个带有各种静态的 django 应用程序) ,这要简单得多

(粘贴我的评论,因为 K3-rnc提到它实际上是有帮助的)

这是一个古老的问题,但是... Python 的包管理确实有很多不足之处。因此,我有一个使用 pip 本地安装到指定目录的用例,令人惊讶的是 package _ data 和 data _ files 路径都不能工作。我并不热衷于向回购文件中添加另一个文件,所以我最终使用 data _ files 和 setup.py 选项—— install-data; 诸如此类

pip install . --install-option="--install-data=$PWD/package" -t package

我有同样的问题了几天,但即使是这个帖子也不能帮助我,因为一切都是混乱的。所以我研究了一下,发现了以下的解决办法:

基本上,在这种情况下,你应该这样做:

from setuptools import setup


setup(
name='myapp',
packages=['myapp'],
package_dir={'myapp':'myapp'}, # the one line where all the magic happens
package_data={
'myapp': ['data/*.txt'],
},
)

这里有完整的其他堆栈溢出答案

使用 setup.cfg (setuptools ≥30.3.0)

从 setuptools 30.3.0(发布于2016-12-08)开始,您可以将 setup.py保持非常小,并将配置移动到 setup.cfg文件。使用这种方法,您可以将包数据放在 [options.package_data]部分中:

[options.package_data]
* = *.txt, *.rst
hello = *.msg

在这种情况下,您的 setup.py可以短至:

from setuptools import setup
setup()

有关更多信息,请参见 使用 setup.cfg 文件配置安装程序

正如在 PEP 518中提出的那样,有 一些关于贬低 setup.cfg的言论支持 pyproject.toml,但是在2020-02-21年这仍然是临时的。

只要删掉这句话:

include_package_data=True,

从您的安装脚本,它将工作得很好。(刚刚用最新的 setuptools 进行了测试。)

我在同一个问题上遇到了这个帖子。

我的经验 自相矛盾的经验在其他答案。 include_package_data=True < strong > 确实将数据包含在 在 setuptools中的解释 文件 缺乏上下文和故障排除技巧,但 include_package_data的工作就像广告上说的那样。

我的设置:

  • Windows/Cygwin
  • Git 版本2.21.0
  • Python 3.8.1 Windows 发行版
  • setuptools v47.3.1
  • check-manifest0.42

这是我的指南。

如何包含包数据

下面是我在 PyPI 上发布的一个项目的文件结构。 (它在 __main__.py中安装应用程序)。

├── LICENSE.md
├── MANIFEST.in
├── my_package
│   ├── __init__.py
│   ├── __main__.py
│   └── _my_data          <---- folder with data
│       ├── consola.ttf   <---- data file
│       └── icon.png      <---- data file
├── README.md
└── setup.py

起点

中的 setuptools.setup()的一般起点 setup.py.

setuptools.setup(
...
packages=setuptools.find_packages(),
...
)

setuptools.find_packages()包括我在 我唯一的包裹是 my_package

包含我的数据的子文件夹 _my_data不被视为 因为它不包含 __init__.py, 所以 find_packages()找不到它。

一个经常被引用的解决方案,但是 不正确,是把一个空 _my_data文件夹中的 __init__.py文件。

这个 是的使它成为一个包,所以它包含文件夹 发行版中的 _my_data。但是里面的数据文件 _my_data不包括在内

因此,使 _my_data成为一个包 没用的

解决办法是:

  • sdist已经包含了数据文件
  • 添加 include_package_data=True以包含 bdist中的数据文件

实验(如何测试溶液)

有三个步骤可以让这个实验变得可重复:

$ rm -fr build/ dist/ my_package.egg-info/
$ check-manifest
$ python setup.py sdist bdist_wheel

我将一步一步地分解这些内容:

  1. 清理旧建筑:
$ rm -fr build/ dist/ my_package.egg-info/
  1. 运行 check-manifest以确保 MANIFEST.in 火柴 版本控制下的文件的 Git 索引:
$ check-manifest

如果 MANIFEST.in还不存在,那么从 Git 获取 创造它 版本控制下的文件索引:

$ check-manifest --create

下面是创建的 MANIFEST.in:

include *.md
recursive-include my_package *.png
recursive-include my_package *.ttf

没有理由手动编辑此文件。

只要 应该在版本控制下的一切都是 在版本管制下(即是 Git 索引的 一部分) , check-manifest --create做了正确的事。

注意: 文件是 Git 索引的 没有部分,如果它们是:

  • .gitignore中被忽略
  • 排除在 .git/info/exclude之外
  • 或者仅仅是尚未被 补充到索引的 新的文件

如果有任何文件处于版本控制之下,则 不应该为 在版本控制下,check-manifest会发出警告,然后 指定建议从 Git 索引中删除哪些文件。

  1. 建造:
$ python setup.py sdist bdist_wheel

现在检查 sdist(源分布)和 bdist_wheel (构建分发版)查看它们是否包含数据文件。

查看 sdist的内容(只有相关的行是 如下所示) :

$ tar --list -f dist/my_package-0.0.1a6.tar.gz
my_package-0.0.1a6/
...
my_package-0.0.1a6/my_package/__init__.py
my_package-0.0.1a6/my_package/__main__.py
my_package-0.0.1a6/my_package/_my_data/
my_package-0.0.1a6/my_package/_my_data/consola.ttf <-- yay!
my_package-0.0.1a6/my_package/_my_data/icon.png    <-- yay!
...

因此 sdist已经包含了数据文件,因为它们是 列在 MANIFEST.in。没有额外的做包括 sdist中的数据文件。

查看 bdist的内容(它是一个. zip 文件,已解析 连同 zipfile.ZipFile) :

$ python check-whl.py
my_package/__init__.py
my_package/__main__.py
my_package-0.0.1a6.dist-info/LICENSE.md
my_package-0.0.1a6.dist-info/METADATA
my_package-0.0.1a6.dist-info/WHEEL
my_package-0.0.1a6.dist-info/entry_points.txt
my_package-0.0.1a6.dist-info/top_level.txt
my_package-0.0.1a6.dist-info/RECORD

注意: 您需要创建自己的 check-whl.py脚本来生成 只有三行:

from zipfile import ZipFile
path = "dist/my_package-0.0.1a6-py3-none-any.whl" # <-- CHANGE
print('\n'.join(ZipFile(path).namelist()))

正如所料,bdist缺少数据文件。

_my_data文件夹完全不见了。

如果我创建一个 _my_data/__init__.py会怎样 我发现 数据文件还没找到! _my_data/文件夹 包括在内,但它不包含数据 文件!

解决方案

与其他人的经验相反,是的的工作方式是:

setuptools.setup(
...
packages=setuptools.find_packages(),
include_package_data=True, # <-- adds data files to bdist
...
)

修复完成后,重做实验:

$ rm -fr build/ dist/ my_package.egg-info/
$ check-manifest
$ python.exe setup.py sdist bdist_wheel

确保 sdist仍然保存着数据文件:

$ tar --list -f dist/my_package-0.0.1a6.tar.gz
my_package-0.0.1a6/
...
my_package-0.0.1a6/my_package/__init__.py
my_package-0.0.1a6/my_package/__main__.py
my_package-0.0.1a6/my_package/_my_data/
my_package-0.0.1a6/my_package/_my_data/consola.ttf <-- yay!
my_package-0.0.1a6/my_package/_my_data/icon.png    <-- yay!
...

看看 bdist的内容:

$ python check-whl.py
my_package/__init__.py
my_package/__main__.py
my_package/_my_data/consola.ttf        <--- yay!
my_package/_my_data/icon.png           <--- yay!
my_package-0.0.1a6.dist-info/LICENSE.md
my_package-0.0.1a6.dist-info/METADATA
my_package-0.0.1a6.dist-info/WHEEL
my_package-0.0.1a6.dist-info/entry_points.txt
my_package-0.0.1a6.dist-info/top_level.txt
my_package-0.0.1a6.dist-info/RECORD

如何 没有测试数据文件是否包含

我建议使用概述的方法进行故障排除/测试 检查 sdistbdist

在可编辑模式下的 pip 安装不是有效的测试

注意: pip install -e . 没有显示数据文件是否 包括在 bdist

符号链接使安装的行为与 数据文件包括在内(因为它们已经存在于 开发商的电脑)。

pip install my_package之后,数据文件位于 虚拟环境的 lib/site-packages/my_package/文件夹, 使用上面列表中显示的完全相同的文件结构 whl的内容。

发布到 TestPyPI 是一种缓慢的测试方法

发布到 TestPyPI,然后安装并查看 lib/site-packages/my_packages是一个有效的测试,但它也是 很费时间。

目录结构如:

foo/
├── foo
│   ├── __init__.py
│   ├── a.py
│   └── data.txt
└── setup.py

setup.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-


from setuptools import setup




NAME = 'foo'
DESCRIPTION = 'Test library to check how setuptools works'
URL = 'https://none.com'
EMAIL = 'gzorp@bzorp.com'
AUTHOR = 'KT'
REQUIRES_PYTHON = '>=3.6.0'


setup(
name=NAME,
version='0.0.0',
description=DESCRIPTION,
author=AUTHOR,
author_email=EMAIL,
python_requires=REQUIRES_PYTHON,
url=URL,
license='MIT',
classifiers=[
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
],
packages=['foo'],
package_data={'foo': ['data.txt']},
include_package_data=True,
install_requires=[],
extras_require={},
cmdclass={},
)

python setup.py bdist_wheel成功了。

像这个帖子里的其他人一样,我对长寿和 还是缺乏清晰度的组合感到非常惊讶,但是对我来说最好的答案是使用 check-manifest,就像@mike-eyes 在答案中推荐的那样

因此,只使用 setup.cfg而不使用 setup.py以及包中所需的其他文本和 python 文件,对我有效的方法是将其保存在 setup.cfg 中:

[options]
packages = find:
include_package_data = true

并根据 check-manifest输出更新 MANIFEST.in:

include *.in
include *.txt
include *.yml
include LICENSE
include tox.ini
recursive-include mypkg *.py
recursive-include mypkg *.txt

Setuptools 62.3.0开始,现在可以使用 递归通配符递归通配符("**")递归地包含(子)目录。这样,您可以包括整个文件夹,其中包含所有文件夹和文件。

例如,在使用 pyproject.toml文件时,以下是递归包含两个文件夹的方式:

[tool.setuptools.package-data]
"ema_workbench.examples.data" = ["**"]
"ema_workbench.examples.models" = ["**"]

但是您也只能在一个文件夹和所有子文件夹中包含特定的文件类型。如果要包含所有标记(.md)文件,例如:

[tool.setuptools.package-data]
"ema_workbench.examples.data" = ["**/*.md"]

当使用 setup.pysetup.cfg时,它也应该起作用。

有关详细信息,请参阅 https://github.com/pypa/setuptools/pull/3309