setuptools:包数据文件夹位置


94

我使用setuptools分发我的python包。现在,我需要分发其他数据文件。

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

我要避免的是:

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

我想拥有的是:

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

如果不是必须的话,我只是对拥有这么多子目录感到不满意。我找不到原因,为什么我必须将文件放在包目录中。使用如此众多的嵌套子目录恕我直言也很麻烦。还是有什么充分的理由可以证明这一限制?


8
我问了一个有关使用'data_files'分发资源(文档,图像等)的类似问题:stackoverflow.com/questions/5192386 / ... ...(两个)响应都说使用了'package_data'。现在,我正在使用包数据,但这意味着我必须将数据和文档放入包中,即,将其与源代码混合在一起。我不喜欢这个 复制源代码时,不仅会找到我要搜索的类定义,而且还会发现它们在我的RST,HTML和中间文件中的数十个提及。:-(
乔纳森·哈特利

2
我知道@JonathanHartley的响应很晚,但是您可以通过添加__init__.py文件使任何目录成为“包” ,即使该文件为空。因此,您可以将数据目录与一个空__init__.py文件分开,以使其看起来像一个包。这样可以防止源代码树中的grep拾取它们,但是python及其构建工具仍会将其识别为软件包。
dhj 2014年

@dhj一个有趣的主意,谢谢。
乔纳森·哈特利2014年

4
@dhj这种方法的唯一问题是python认为您已经安装了一个名为“ data”的软件包。如果您安装的另一个软件包尝试以相同的方式打包数据,则将安装两个冲突的“数据”软件包。
2013年

Answers:


111

选项1:作为软件包数据安装

将数据文件放在Python包的根目录中的主要优点是,它使您不必担心文件在用户系统上的位置,这些系统可能是Windows,Mac,Linux,某些移动平台或Egg内。您可以随时找到目录data无论安装在何处或如何安装,相对于Python软件包根。

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

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']}

7
非常感谢您帮助我适应情况。因此,我很乐意按照您(和其他所有人)的建议使用package_data。但是:仅仅是我发现将数据和文档放在包源目录中会带来麻烦吗?(例如grepping我的源代码会从我的文档中返回许多不需要的结果。每次使用grep时,我都可以在grep中添加“ --exclude-dir”参数,这在一个项目与另一个项目之间会有所不同,但这似乎很棘手)可能在包目录中包含一个'src'子目录而不会破坏导入,等等
乔纳森·哈特利

我通常只将包所需的数据文件放在包目录下。我会将docs安装为data_files。另外,您可以为grep设置一个shell别名,以忽略非Python文件,例如grep_py
Samplebias 2011年

嗨,samplebias。感谢您的更新。这不只是用grep,虽然,它的一切,从文本编辑器的搜索功能于文件CTAGS到AWK。我将尝试重组我的项目,按照您的建议将文档放入data_files中,看看效果如何。很快回来... :-)
乔纳森·哈特利

...看起来似乎还可以。感谢您将我设置在正确的轨道上。+50声望点好吃吗?
乔纳森·哈特利

谢谢!很高兴听到您的声音,很高兴它能解决您的问题,并且您正在取得进步!
Samplebias 2011年

13

我认为我找到了一个很好的折衷办法,可以让您保持以下结构:

/ #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”,因为数据可能与该模块无关,但是我想这是一个很好的折衷方案。


-4

我认为您基本上可以将任何东西作为参数* data_files *传递给setup()


嗯...我可以在distutils文档中看到它,但是在setuptools文档中却看不到它。无论如何,我最终将如何访问它?
phant0m 2010年

我认为data_files仅应用于在多个软件包之间共享的数据。例如,如果您从PyPI进行pip安装,则data_files中列出的文件将直接安装到主Python安装目录下的目录中。(即不在Python27 / Lib / site-packages / mypackage中,但与“ Python27 / Lib”并行)
乔纳森·哈特利
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.