Python打包:数据文件已正确放置在tar.gz文件中,但未安装到虚拟环境中。

49

我无法将项目package_fiddler正确安装到我的虚拟环境中。

我已经找到了原因,MANIFEST.in负责将非.py文件放置在生成的Package_fiddler-0.0.0.tar.gz,该文件是通过执行python setup.py sdist时生成的。

然后我执行了以下操作:

(virt_envir)$ pip install dist/Package_fiddler-0.0.0.tar.gz

但这并没有安装数据文件或将包安装到/home/username/.virtualenvs/virt_envir/local/lib/python2.7/site-packages

我尝试了许多setup参数的配置:package_datainclude_package_datadata_files,但每次似乎都使用了错误的配置。

哪种package_data和/或include_package_data和/或data_files的配置将正确地将package_fiddler安装到我的虚拟环境中?

项目目录树

.
├── MANIFEST.in
├── package_fiddler
│   ├── data
│   │   ├── example.html
│   │   └── stylesheets
│   │       └── example.css
│   └── __init__.py
├── README.rst
└── setup.py

setup.py

from setuptools import setup


setup(
    name='Package_fiddler',
    entry_points={
    'console_scripts': ['package_fiddler = package_fiddler:main', ],},
    long_description=open('README.rst').read(),
    packages=['package_fiddler',])

MANIFEST.in

include README.rst
recursive-include package_fiddler/data *

我尝试了哪些setup.py配置(以上代码库)?

配置1:

添加:

package_data={"": ['package_fiddler/data/*',]}

配置2

添加:

package_data={"": ['*.html', '*.css', '*.rst']}

配置3

添加:

include_package_data=True

配置4

添加:

package_data={"": ['package_fiddler/data',]}

移除:

packages=['package_fiddler',]

配置5(克里斯的建议)

添加:

package_data={"data": ['package_fiddler/data',]}

移除:

packages=['package_fiddler',]

配置6

添加:

package_data={"": ['package_fiddler/data/*',]}

移除:

packages=['package_fiddler',]

这些配置导致在/home/username/.virtualenvs/virt_envir/local/lib/python2.7/site-packages上没有任何文件被安装。

编辑

Toshio Kuratomi:

为了更清晰地阐述问题,我在原帖中使用了最简单的树形结构,但实际上我的树形结构看起来更像下面的树。对于这个树,仅在stylesheets中放置__init__.py,竟然可以正确安装texts文件夹中的所有数据文件!这让我感到困惑。

树形结构 2(以某种方式正确安装了所有数据文件!!)

.
├── MANIFEST.in
├── package_fiddler
│   │── stylesheets
|   |     ├── __init__.py
|   |     ├── example.css  
|   |     └── other
|   |          └── example2.css
|   |__ texts
|   |     ├── example.txt  
|   |     └── other
|   |          └── example2.txt
│   └── __init__.py
├── README.rst
└── setup.py
5个回答

37

我找到了一个对我有效的解决方案 在这里

使用 setuptools==2.0.2,我执行了以下操作:

setuptools.setup(
    ...
    packages=setuptools.find_packages(),
    include_package_data=True,  # use MANIFEST.in during install
    ...
)

8
好的!这个功能几乎解决了所有问题,我很惊讶这是我第一次听说它。我认为这应该成为被接受的答案,而不是 __init__.py 的把戏。 - rspeer
1
这确实是正确的答案,解决了我所有的问题。__init__.py的hack对我没有用,因为我已经在我的树中放置了init文件!此外,init技巧对于非Python数据文件也无济于事。 - gaborous
这个方法是有效的。注意:虽然答案中没有明确提到,但在setup.py中删除package_data = ...并从数据子目录中删除__init__.py文件:使用此方法不需要它们。只需使用MANIFEST.in并设置include_package_data=True即可。我遇到的另一件事是,MANIFEST.in中的数据文件需要位于Python代码子树下(至少一个检测到的包的子树),否则它们将无法安装。 - init_js
另请注意,如果您打算稍后在运行时使用 os.path.dirname(__file__) 路径算法从 MANIFEST.in 加载文件,则添加 zip_safe=False 可能有助于确保正确性(省略 zip_false 将让 bdist_egg 通过文件检查推断是否安全)文档。参见 其他答案 - init_js

28

我个人不喜欢setuptools将代码和数据概念上和实现方式上混在一起。我认为正是这种实现方式使您陷入了困境。要让setuptools能够找到并使用package_data,数据需要驻留在一个Python包内部。Python包可以是一个目录,但该目录中必须有一个名为__init__.py的文件。因此,看起来您需要以下(可以为空)文件:

./package_fiddler/data/__init__.py
./package_fiddler/data/stylesheets/__init__.py

2
我之前也遇到过类似的问题,确实需要 init.py 文件。https://dev59.com/h2865IYBdhLWcg3wi_M2 - ForceMagic
2
我同意。奇怪的是,在我的帖子的编辑部分中,只需要一个嵌套数据文件中的__init__.py文件就可以正确安装使用tree2项目! - Bentley4
7
__init__.py 的作用是将一个文件夹变成可导入的模块 -- 这在数据目录中通常是不必要的。请参考我在这里的答案 - Leo
@Leo 是的,__init__.py 真的很糟糕,但是 package_datadistutils 的一个特性,而 setuptools 却忽略了它。此外,在发布 sdists 时,package_data 不起作用。总的来说,Python 的打包方式很糟糕;如果人们更喜欢 hacky 的 __init__.py 解决方案而不是不一致的 package_data 解决方案,请让他们自行决定。 - Herbert
直到今天我还遇到了一个问题,即打包时未包含 package_data。我不得不使用 manifest.in 文件来包含相应的内容,并在数据文件夹中放置空的 __init__.py 文件,并需要使用 setup 中的 packages 参数显式地添加数据文件夹。否则,我只能使用 ext_modules。这真是个噩梦般的圣诞节后遗症。 - avans

15

在"setup.py"中包含软件包数据的最简单方法是这样的:

package_data = {'<package name>': ['<path to data file within package dir>']}

所以以你的例子为例:

package_data = {'package_fiddler': ['data/*', 'data/stylesheets/*']}

package_data 是一个字典,其中的键是安装程序中包含的软件包的名称。这些键下的值应该是包目录内特定文件路径或通配符/通配符的列表。

您还需要包括标志:

zip_safe=False

如果您希望能够解析文件系统路径到您的数据,请在 setup(...) 中设置。否则,您可以使用 pkg_resources 来实现:http://peak.telecommunity.com/DevCenter/PythonEggs#accessing-package-resources

在 "data" 目录中,您绝对不需要一个 __init__.py 文件 - 该目录不是一个模块,也不应该被导入。


3
是的,这个可以工作。但是你必须在 MANIFEST.insetup.py 中复制路径并保持同步。 - warvariuc
1
这个,这个,这个!最糟糕的是文档让人们相信不需要 MANIFEST.in,但我没有找到不使用它就能使其工作的方法。:( - K.-Michael Aye
这个方案是可行的,也是最有用的答案,唯一让我困惑的是没有意识到路径是相对于包文件夹而非setup.py所在的父文件夹。虽然答案中的示例已经足够清楚了,但是如果你仍然无法成功,请特别注意这个细节。 - David Parks

-2

使用

package_data={"data": ['package_fiddler/data',]}

替代

packages=['package_fiddler',]

它并没有改变任何东西。在重新打包后,当我使用 pip 安装该项目时,虚拟环境中仍然找不到任何内容。 - Bentley4
你在 setup.py 中使用了 from setuptools import setup 还是 from distutils.core import setup - Bentley4

-2

这对我有用。希望能帮到你。

package_data={
    "package_fiddler": [
        '\*.\*',
        '\*/\*.\*',
        '\*/\*/\*.\*',
    ],
},

4
我认为这是一种ASCII图形 =) - devforfu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接