【问题标题】:Cross-platform support for `data_files` with `setup.py` + `pip`使用 `setup.py` + `pip` 跨平台支持`data_files`
【发布时间】:2018-07-19 09:39:08
【问题描述】:

data_filessetup.py(与pip 兼容)传送的跨平台方式是什么?

从官方文档来看,需要这样写:

setup(...,
    data_files=[('bitmaps', ['bm/b1.gif', 'bm/b2.gif']),
                ('config', ['cfg/data.cfg']),
                ('/etc/init.d', ['init-script'])]
    )

'bitmaps' 等是这些文件应该存放的子目录(相对于sys.prefix)。

但是,对于标准子目录将取决于系统的跨平台安装,这不是最佳选择。 此外,在开发人员模式下安装软件包不会将文件放置在安装后的位置,从而使查找/使用资源的过程最终难以调试/令人讨厌。

我查看了appdirs,但在安装过程中似乎很难使其正常工作,例如如果使用用户目录存储数据,这实际上与我的开发环境相关。

我问这个的原因是因为我有一个小的 Python 包,它实现了一个简单的 GUI,我想附带一个图标。

为了记录,我可以处理 setup.pysetuptools

【问题讨论】:

  • 请注意,绝对目标路径无论如何都不适用于pip,因为所有绝对data_files 目标都将相对于site-packages,有关更多详细信息,请参阅我的答案here。对于捆绑资源文件,我宁愿使用package_data 并将文件放在某个包目录下。然后您可以跨平台方式引用带有pkg_resources.resource_file()等的文件。
  • 感谢您的提示。我最终将package_datapkg_resources.resource_filename() 结合使用。奇怪的是,使用package_data,我无法使用 Python 2 构建包(尽管生成的包与 Python 2 兼容)。如果您弥补您的评论作为答案,我很乐意接受。
  • 你是在构建源 dist 还是*?你得到什么错误?如果您使用错误详细信息更新您的问题(或提出新问题并在此处链接),我会尽力提供帮助。

标签: python pip setup.py


【解决方案1】:

正如 cmets 中所建议的,对于捆绑资源文件,我宁愿使用 package_data 并将文件放在某个包目录下。示例:

project
├── pkg1
│   ├── __init__.py
│   └── icons
│       └── image.png
└── pkg2
    └── __init__.py

setup.py 脚本中打包:

from setuptools import setup


setup(
    ...
    package_data={'pkg1': ['icons/image.png']},
)

在代码中访问资源文件的更新:

Python 3.7 引入了importlib.resources,它取代了旧的pkg_resources 功能并提供利用pathlib的现代资源机制:

filepath = importlib_resources.path('pkg1', 'icons/image.png')

对于 Python 3.6 及更早版本,有一个名为 importlib_resources 的反向端口。因此,与版本无关的示例是:

import sys

if sys.version_info >= (3, 7):
    from importlib import resources as importlib_resources
else:
    import importlib_resources

filepath = importlib_resources.path('pkg1', 'icons/image.png')

尽可能使用importlib_resources 而不是pkg_resources

原始答案,仅供历史参考

要在代码中引用资源文件,请使用pkg_resources

import pkg_resources

filepath = pkg_resources.resource_filename('pkg1', 'icons/image.png')

跨平台支持因此由pkg_resources 处理。可用资源访问功能请参考ResourceManager API

【讨论】:

  • sys.version 应该是sys.version_info (doc)
【解决方案2】:

更新 查看来自@hoeflingsolution

data_files 选项指定要为您的包上传的文件。

来自docs

文件中没有目录信息用于确定安装文件的最终位置;仅使用文件名。

如果要构建跨平台目录路径,请使用os 模块

import os

path = os.path.join('root', 'directory')

如果您想在有人尝试构建您的包时确定平台,请使用sys 模块

import sys

current_platform = sys.platform.lower()
is_windows = current_platform.startswith('win')
is_ubuntu =  'ubuntu' in current_platform
is_mac = 'darwin' in current_platform


if is_windows:
     ... # windows specific dayta

elif is_ubuntu:
     ....

else:
    raise Exception('Platform not supported')

【讨论】:

  • 这不会在构建时定义目录(就像使用 appdirs 一样)因此渲染这种方法对跨平台场景几乎没有任何帮助?