【问题标题】:setuptools: package data folder locationsetuptools:包数据文件夹位置
【发布时间】:2011-05-29 23:32:36
【问题描述】:

我使用 setuptools 来分发我的 python 包。现在我需要分发额外的数据文件。

根据我从 setuptools 文档中收集到的信息,我需要将数据文件放在包目录中。但是,我宁愿将我的数据文件放在根目录的子目录中。

我想避免的:

/ #root
|- src/
|  |- mypackage/
|  |  |- data/
|  |  |  |- resource1
|  |  |  |- [...]
|  |  |- __init__.py
|  |  |- [...]
|- setup.py

我想拥有什么:

/ #root
|- data/
|  |- resource1
|  |- [...]
|- src/
|  |- mypackage/
|  |  |- __init__.py
|  |  |- [...]
|- setup.py

如果不是必需的话,我只是对拥有这么多子目录感到不自在。我找不到原因,为什么我 /have/ 将文件放在包目录中。恕我直言,使用如此多的嵌套子目录也很麻烦。或者有什么好的理由可以证明这种限制是合理的?

【问题讨论】:

  • 我问了一个关于使用“data_files”分发资源(文档、图像等)的类似问题:stackoverflow.com/questions/5192386/… ...并且(两个)回复都说要使用“package_data”。现在我正在使用包数据,但这意味着我必须将我的数据和文档放在我的包中,即混合在我的源代码中。我不喜欢这个。在查找我的源代码时,我不仅找到了我正在搜索的类定义,而且还找到了它们在我的 RST、HTML 和中间文件中得到的数十个提及。 :-(
  • 我知道这个回复很晚,@JonathanHartley,但是您可以通过添加一个 __init__.py 文件将任何目录设为“包”,即使该文件是空白的。因此,您可以将数据目录与空的__init__.py 文件分开,使其看起来像一个包。这应该可以防止 grep 从你的源代码树中获取它们,但它仍然会被 python 及其构建工具识别为一个包。
  • @dhj 一个有趣的想法,谢谢。
  • @dhj 这种方法的唯一问题是 python 认为你已经安装了一个名为“data”的包。如果您安装的另一个包试图以相同的方式打包数据,您将安装两个冲突的“数据”包。

标签: python setuptools


【解决方案1】:

我认为您基本上可以将任何内容作为参数 *data_files* 提供给 setup()

【讨论】:

  • 嗯...我可以看到它在 distutils 文档中,但在 setuptools 文档中看不到它。无论如何,我最终将如何访问它?
  • 我认为 data_files 应该只用于在几个包之间共享的数据。例如,如果您从 PyPI 进行 pip install,那么 data_files 中列出的文件将直接安装到您的主要 Python 安装目录下的目录中。 (即不在 Python27/Lib/site-packages/mypackage 中,但与 'Python27/Lib' 并行)
【解决方案2】:

选项 1:作为包数据安装

将数据文件放在 Python 包的根目录中的主要优点 是它让您不必担心文件将保存在用户的 系统,可能是 Windows、Mac、Linux、某些移动平台或 Egg 内部。你可以 始终找到相对于 Python 包根目录的目录 data,无论它安装在何处或如何安装。

例如,如果我有这样的项目布局:

project/
    foo/
        __init__.py
        data/
            resource1/
                foo.txt

您可以向__init__.py 添加一个函数来定位数据的绝对路径 文件:

import os

_ROOT = os.path.abspath(os.path.dirname(__file__))
def get_data(path):
    return os.path.join(_ROOT, 'data', path)

print get_data('resource1/foo.txt')

输出:

/Users/pat/project/foo/data/resource1/foo.txt

项目安装为 Egg 后,data 的路径会改变,但代码不需要改变:

/Users/pat/virtenv/foo/lib/python2.6/site-packages/foo-0.0.0-py2.6.egg/foo/data/resource1/foo.txt

选项 2:安装到固定位置

另一种方法是将您的数据放在 Python 包之外,然后 要么:

  1. 通过配置文件传入data的位置, 命令行参数或
  2. 将位置嵌入到您的 Python 代码中。

如果您计划分发您的项目,这将是不可取的。如果您真的想要这样做,您可以通过传入元组列表来指定每组文件的目标,从而将data 安装在目标系统上的任意位置:

from setuptools import setup
setup(
    ...
    data_files=[
        ('/var/data1', ['data/foo.txt']),
        ('/var/data2', ['data/bar.txt'])
        ]
    )

更新:递归 grep Python 文件的 shell 函数示例:

atlas% function grep_py { find . -name '*.py' -exec grep -Hn $* {} \; }
atlas% grep_py ": \["
./setup.py:9:    package_data={'foo': ['data/resource1/foo.txt']}

【讨论】:

  • 非常感谢您帮助我适应这种情况。因此,我很高兴按照您(和其他所有人)的建议使用 package_data 运行。然而:只有我发现将他们的数据和文档放在他们的包源目录中会很不方便吗? (例如 grepping 我的源代码会从我的文档中返回几十个不需要的命中。每次我使用它时,我都可以向 grep 添加 '--exclude-dir' 参数,这会因项目而异,但这似乎很恶心)是可以在我的包目录中包含一个“src”子目录而不破坏导入等
  • 我一般只把包需要的数据文件放在包目录下。我会将文档安装为data_files。此外,您可以为 grep 提供一个 shell 别名来忽略非 Python 文件,例如 grep_py
  • 嘿样本偏差。感谢您的更新。它不仅仅是 grep,它是一切,从文本编辑器在文件中搜索到 ctags 再到 awk。我将尝试按照您的建议重组我的项目以将文档放入 data_files 中,看看效果如何。很快回来... :-)
  • ...这似乎工作正常。感谢您让我走上正轨。 +50 声望点好吃吗?
  • 谢谢!很高兴听到,很高兴它成功了,你正在取得进展!
【解决方案3】:

我想我找到了一个很好的折衷方案,可以让您保持以下结构:

/ #root
|- data/
|  |- resource1
|  |- [...]
|- src/
|  |- mypackage/
|  |  |- __init__.py
|  |  |- [...]
|- setup.py

您应该将数据安装为 package_data,以避免 samplebias 答案中描述的问题,但为了保持文件结构,您应该添加到 setup.py:

try:
    os.symlink('../../data', 'src/mypackage/data')
    setup(
        ...
        package_data = {'mypackage': ['data/*']}
        ...
    )
finally:
    os.unlink('src/mypackage/data')

通过这种方式,我们可以“及时”创建适当的结构,并保持我们的源代码树井井有条。

要在您的代码中访问此类数据文件,您“只需”使用:

data = resource_filename(Requirement.parse("main_package"), 'mypackage/data')

我仍然不喜欢在代码中指定“mypackage”,因为数据可能与此模块无关,但我想这是一个很好的折衷方案。

【讨论】:

    猜你喜欢
    • 2015-06-12
    • 1970-01-01
    • 2012-01-23
    • 1970-01-01
    • 2018-05-31
    • 1970-01-01
    • 1970-01-01
    • 2020-12-17
    相关资源
    最近更新 更多