setuptools:包数据文件夹位置
我用setuptools来发布我的Python包。现在我需要发布一些额外的数据文件。
根据我从setuptools的文档中了解到的,我需要把数据文件放在包的目录里。不过,我更希望把这些数据文件放在根目录下的一个子目录里。
我想避免的情况是:
/ #root
|- src/
| |- mypackage/
| | |- data/
| | | |- resource1
| | | |- [...]
| | |- __init__.py
| | |- [...]
|- setup.py
我希望得到的情况是:
/ #root
|- data/
| |- resource1
| |- [...]
|- src/
| |- mypackage/
| | |- __init__.py
| | |- [...]
|- setup.py
我觉得如果没有必要的话,放这么多子目录让我不太舒服。我找不到为什么我必须把文件放在包目录里的理由。对我来说,处理这么多嵌套的子目录也很麻烦。难道有什么好的理由能解释这个限制吗?
4 个回答
我可以使用 importlib_resources
或者 importlib.resources
(这取决于你使用的Python版本)。
https://importlib-resources.readthedocs.io/en/latest/using.html
我想我找到了一种不错的折中方案,这样你就可以保持以下的结构:
/ #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',因为这些数据可能和这个模块没有直接关系,但我想这也是个不错的折中方案。
选项 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:安装到固定位置
- 通过配置文件、命令行参数来传入
data
的位置,或者 - 把位置嵌入到你的 Python 代码里。
如果你打算分发你的项目,这种方法就不太理想。如果你真的想这么做,你可以通过传入一个包含文件组的目标位置的元组列表,来把 data
安装到你想要的地方:
from setuptools import setup
setup(
...
data_files=[
('/var/data1', ['data/foo.txt']),
('/var/data2', ['data/bar.txt'])
]
)
更新:一个递归查找 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']}