You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Python setup.py打包非代码数据并解决路径问题

Python项目资源文件打包与访问问题

项目结构

my-project
   venv
   tests
   setup.py
   abc
      __init__.py
      my_pckg
         __init__.py
         foo.py
      static
         data_0.txt
         sub_dir1
             data_1.yaml
         sub_dir2
             data_2.csv
             another file with space in name.pdf

当前foo.py代码

# abc/my_pckg/foo.py
import os
import yaml
with open(os.path.join("abc", "static", "sub_dir1", "data_1.yaml"), "r") as f:
    content = yaml.safe_load(f)

if __name__ == "__main__":
    print(content)

运行现状

  • 在my-project目录打开Python控制台执行from abc.my_pckg.foo import content,正常运行;
  • 执行python abc/my_pckg/foo.py,正常运行;
  • 打包成whl并在新环境安装后,运行报错找不到abc\static\data_1.yaml,安装后静态文件实际位于venv/Lib/site_packages/static。

疑问与需求

  1. 如何修正配置与代码,让三种运行方式都正常工作?
  2. 如何处理带空格的文件名another file with space in name.pdf?
  3. 是否应该用pkg_resources或importlib?具体怎么操作?

解答

1. 让三种运行方式正常工作的修正方案

打包配置修正(setup.py)

问题核心是打包时静态文件未归属到abc包目录,而是直接放到了site-packages根目录。修改setup.py,通过package_data指定静态文件归属:

from setuptools import setup, find_packages

setup(
    name="abc",
    version="0.1.0",
    packages=find_packages(),
    # 指定abc包下的静态文件
    package_data={
        "abc": [
            "static/*.txt",
            "static/sub_dir1/*.yaml",
            "static/sub_dir2/*.csv",
            "static/sub_dir2/*.pdf"
        ]
    },
    # 可选:配合MANIFEST.in自动包含文件
    include_package_data=True,
)

如果不想逐个列举文件,用递归通配符更高效:

package_data={
    "abc": ["static/**/*"],
},

同时创建MANIFEST.in文件(与setup.py同目录),确保打包时包含静态文件:

recursive-include abc/static *

代码读取逻辑修正

硬编码相对路径仅在开发环境有效,打包后包路径会变化,必须通过包元信息动态获取资源路径:

方案A:Python 3.7+ 推荐用importlib.resources(标准库)
# abc/my_pckg/foo.py
import yaml
from importlib.resources import files

# 定位到abc.static.sub_dir1下的data_1.yaml
resource_path = files("abc.static.sub_dir1").joinpath("data_1.yaml")
with open(resource_path, "r", encoding="utf-8") as f:
    content = yaml.safe_load(f)

if __name__ == "__main__":
    print(content)

Python 3.9+ 可使用更简洁的read_text直接读取内容:

from importlib.resources import read_text
content = yaml.safe_load(read_text("abc.static.sub_dir1", "data_1.yaml", encoding="utf-8"))
方案B:兼容旧版本用pkg_resources

若需支持Python 3.6及以下,使用pkg_resources(依赖setuptools):

# abc/my_pckg/foo.py
import yaml
import pkg_resources

# 获取资源文件的绝对路径
resource_path = pkg_resources.resource_filename(
    "abc", "static/sub_dir1/data_1.yaml"
)
with open(resource_path, "r", encoding="utf-8") as f:
    content = yaml.safe_load(f)

if __name__ == "__main__":
    print(content)

修改后,三种运行场景都会正确定位资源:开发环境读取项目内的abc/static,安装后读取site-packages中abc包下的static目录。


2. 带空格的文件名处理

带空格的文件名无需额外处理,只要打包配置正确匹配,读取时用上述importlib或pkg_resources方法即可。例如读取目标PDF文件:

# importlib写法
pdf_path = files("abc.static.sub_dir2").joinpath("another file with space in name.pdf")
# pkg_resources写法
pdf_path = pkg_resources.resource_filename(
    "abc", "static/sub_dir2/another file with space in name.pdf"
)

Python的文件操作会自动处理路径中的空格,无需手动转义。


3. pkg_resources vs importlib.resources选择

  • 优先用importlib.resources:它是Python标准库的一部分(3.7+),不依赖第三方包,API简洁现代,性能更优。
  • pkg_resources:属于setuptools组件,兼容性更强(支持Python 2和3.6以下),但会加载整个包的元数据,性能略差。

具体操作参考上述代码示例,根据项目Python版本要求选择即可。


内容的提问来源于stack exchange,提问作者Amin Ba

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 17:54:32