首页 > 解决方案 > 从 python 包资源中提取文件夹

问题描述

在我试图分发的本地版本的包中,我有以下代码:

shutil.copytree(WWW_LOCATION, dir_path)

WWW_LOCATION是我的 python 模块的子文件夹,其中包含一些静态文件和文件夹:

dv
  \dv
     mytool.py
     \www_folder
       \somefolders_and_files
  setup.py
  MANIFEST.in
  README.md
  LICENSE
  setup.cfg

在我的代码中,执行后,我需要将整个文件夹与一些动态生成的文件一起复制到用户指定的位置。这在本地工作得很好,但我读过它是为了通过 pypi 分发,我必须小心,因为文件可能会被压缩。

这个答案解释了如何访问资源中的东西(=在python中读取它们),但是一次只能访问一个文件。将文件夹内容复制到指定位置的安全方法是什么?

标签: pythonfilepackage

解决方案


“更简单”的解决方案是zip_safe=False在您的包设置(setup.pysetup.cfg)中进行设置,以避免您的包被安装为压缩鸡蛋。由于大多数安装都是使用 . 完成的pip,它从不创建压缩的 egg 安装,因此现在运行到压缩包安装中的情况更加罕见。

然后,您只需要担心有人将包手动压缩到 zipfile 中以添加到sys.path,您可以选择不支持的用例。那是另一种形式的压缩包。egg 是一个可安装项目(由 支持pkg_resources)的 zip 文件,并将它们存储在sys.path. pkg_resources只能支持后者,不能支持前者。

如果您确实想支持压缩鸡蛋,那么对于您的特定情况,使用pkg_resourcesAPI 进行资源提取会更容易,因为虽然它可能“更慢”,但它也支持完整的目录树。从resource_filename()文档中:

如果命名资源是一个目录,那么该目录中的所有资源(包括子目录)也会被提取。

我会这样使用它:

try:
    www_location = pkg_resources.resource_filename("dv", "www_folder")
    shutil.copytree(www_location, dir_path)
finally:
    pkg_resources.cleanup_resources()

在直接添加到的 zipfile 中找到的包内的资源sys.path无法通过pkg_resources. 为此,您需要较新的importlib.resources模块(或backport),但此 API 不支持任意目录结构。importlib.resources.path()函数文档指出:

是符合Package要求的名称或模块对象。resource是要在包中打开的资源的名称;它可能不包含路径分隔符,也可能没有子资源(即它不能是目录)。

(粗体强调我的)。

虽然您可以使用 查找包中的目录importlib.resources.contents(),但您实际上无法访问这些目录的内容,除非它们本身就是 Python 包(因此__init__.py其中有一个文件)。传统的非压缩包的这些功能的实现仍然可以让你在使用时访问目录,当包包含在存档importlib.resources.path()中时你不能这样做。.zip

importlib.resources是更好的、面向未来的支持途径。为了支持这一点,您可以在 source 和 wheel 分发中www_folder压缩资源树,然后使用该 zipfile 对象中的内容并将其提取到目的地。with importlib.resources.path("dv", "www_folder.zip") as www_location: www_zip = zipfile.open(www_location)


推荐阅读