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

如何将Python包目录外的ABOUT.md正确纳入wheel打包流程

问题背景

我开发了一款可展示项目ABOUT.md文件内容的Web应用,项目文件结构如下:

project_folder/
  main_package/
    assets/icon.png
    __init__.py
    app.py
  .gitignore # 及其他配套文件
  README.md
  ABOUT.md
  setup.cfg
  setup.py

app.py中的Web服务负责渲染并对外提供ABOUT.md的内容,原有路径读取实现如下:

from pathlib import Path
from main_package import __file__ as mpfile

# 第一级parent为__init__.py所在的main_package目录
ABOUT_MD = Path(mpfile).parent.parent / 'ABOUT.md'

该逻辑在未构建的本地开发环境可正常运行,但项目打包为wheel安装到其他环境后,会抛出文件找不到的异常,功能失效。

之前曾尝试修改setup.cfg将ABOUT.md纳入包数据范围,配置如下:

[options.package_data]
main_package = 
  ../ABOUT.md
  assets/*

但该配置会将ABOUT.md直接复制到site_packages根目录,不符合包文件存放规范,整洁性差。

核心需求

  • ABOUT.md保留在项目根目录存放,保证GitHub仓库网页端可直接访问查看
  • 项目构建、发布后,用户通过pip安装的包可以正常读取该文件,无路径错误

已排除的方案

初步设想

曾考虑修改构建系统逻辑,构建wheel时自动将根目录的ABOUT.md复制到main_package/assets/ABOUT.md路径下,再在app.py中增加分支判断,根据运行环境加载对应路径的文件,但暂不清楚如何配置构建系统实现该自动复制操作。

2022-07-18更新:排除链接类方案的原因

针对硬链接、软链接的实现思路,均不适用:

  • 硬链接:链接关系无法通过Git同步,其他设备拉取代码后会被识别为两个独立文件,需要额外配置钩子做内容同步,还会占用双倍磁盘空间
  • 软链接(符号链接):磁盘占用低,但Git仓库的Web视图无法跟随软链接读取目标内容,只会显示软链接本身的纯文本路径,导致根目录的ABOUT.md无法在网页端正常查看

可行实现方案

直接采用最初设想的构建时自动复制逻辑即可,不需要引入额外依赖,也没有黑魔法,完全符合所有需求:

1. 配置构建时自动复制逻辑

在setup.py中添加自定义构建命令,构建wheel时临时将根目录的ABOUT.md复制到包内assets目录,构建完成后自动删除本地临时文件,避免污染开发仓库:

import os
import shutil
from pathlib import Path
from setuptools import setup
from setuptools.command.build_py import build_py

ROOT = Path(__file__).parent

class BuildWithAbout(build_py):
    def run(self):
        # 构建前复制文件到包内目录
        src = ROOT / 'ABOUT.md'
        target_dir = ROOT / 'main_package' / 'assets'
        target = target_dir / 'ABOUT.md'
        target_dir.mkdir(exist_ok=True)
        shutil.copy2(src, target)
        # 执行原有构建流程
        super().run()
        # 构建结束删除临时复制的文件,避免开发目录出现冗余文件
        if target.exists():
            os.remove(target)

setup(
    cmdclass={'build_py': BuildWithAbout},
    # 保留原有setup的其他配置参数即可
)

同时修改setup.cfg,删除之前错误的../ABOUT.md配置,调整包数据规则如下:

[options]
include_package_data = True

[options.package_data]
main_package = 
  assets/*

该配置的效果:

  • 本地开发时,main_package/assets下不会存在冗余的ABOUT.md副本,仓库始终只有根目录一份源文件,GitHub网页端可正常访问
  • 构建wheel包时,ABOUT.md会被自动临时复制到包内assets目录,随包一起打包,构建完成后本地临时文件自动清理
  • 安装后的包中,ABOUT.md会存放在main_package/assets/路径下,完全在包目录内部,不会散落到site-packages根目录,符合Python打包规范

2. 调整app.py的路径读取逻辑

替换原有硬编码路径的写法,增加环境兼容判断,优先读取包内打包的文件,fallback到开发环境的根目录路径:

from pathlib import Path
from importlib.resources import files
import main_package

def load_about_path() -> Path:
    # 优先读取包内资源(生产安装环境)
    pkg_path = files(main_package) / 'assets' / 'ABOUT.md'
    if pkg_path.is_file():
        return pkg_path
    # 适配本地开发环境
    dev_path = Path(main_package.__file__).parent.parent / 'ABOUT.md'
    if dev_path.is_file():
        return dev_path
    raise RuntimeError("ABOUT.md 不存在,请检查包安装完整性")

ABOUT_MD = load_about_path()

注:如果需要兼容Python 3.9以下版本,安装importlib_resources backport包,将导入语句替换为from importlib_resources import files即可

该实现不需要手动维护多份文件,不需要处理链接的兼容性问题,构建流程无人工操作,完全满足所有需求。


内容的提问来源于stack exchange,提问作者David Davó

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 11:57:11