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

setup.cfg写保护时setuptools执行sdist打包报权限错误的解决方案

setuptools打包只读setup.cfg触发权限错误的解决方案

问题场景

打包名为my_module的小型模块时,项目通过setup.cfg与setup.py配对定义构建规则,两个文件均为只读属性——受版本控制系统规则限制,所有未显式标记为可编辑的文件都会被自动设置为只读权限。

现有setup.cfg配置

[metadata]
name = my_module
...

[options]
package_dir =
    = src
packages = find:
python_requires = >=3.6
install_requires =
    ...

[options.packages.find]
where = src

项目目录结构

my_module_folder/
  setup.cfg
  setup.py
  src/
     my_module/
       __init__.py
       ...

报错表现

执行sdist打包命令时触发权限错误,完整输出如下:

$ python3 setup.py sdist
running sdist
running egg_info
writing src/my_module.egg-info/PKG-INFO
writing dependency_links to src/my_module.egg-info/dependency_links.txt
writing requirements to src/my_module.egg-info/requires.txt
writing top-level names to src/my_module.egg-info/top_level.txt
reading manifest file 'src/my_module.egg-info/SOURCES.txt'
writing manifest file 'src/my_module.egg-info/SOURCES.txt'

running check

creating my_module-1.0.0
creating my_module-1.0.0/src
...
copying files to my_module-1.0.0...
copying setup.cfg -> my_module-1.0.0
copying setup.py -> my_module-1.0.0
copying src/my_module/__init__.py -> my_module-1.0.0/src/my_module
...
Writing my_module-1.0.0/setup.cfg
error: [Errno 13] Permission denied: 'my_module-1.0.0/setup.cfg'

根因分析

setuptools执行sdist打包流程时,首先会将只读的setup.cfg复制到待打包的临时构建目录,后续会尝试覆写该文件,追加固定的[egg_info]配置段:

[egg_info]
tag_build =
tag_date = 0

文件复制过程中会完整保留源文件的只读权限,导致后续覆写操作无权限执行,最终抛出Permission denied错误。

可选解决思路

不建议使用全局修改umask、直接修改源码目录文件权限的粗暴方案,可按优先级选择以下更干净的实现方式:

  • 最优方案:提前在源码目录的setup.cfg中预先写入上述[egg_info]配置段。setuptools检测到对应配置已存在时,会直接跳过覆写临时目录文件的步骤,从根源避免写入只读文件的操作。该方案不需要修改任何构建逻辑,也不会破坏版本控制系统的文件权限规则,侵入性为0。
  • 兼容方案:升级本地setuptools版本。旧版本setuptools复制文件到临时构建目录时不会重置文件权限,新版本已经针对VCS管控下只读源文件的打包场景做了适配,临时目录内的构建文件会默认添加所有者写权限,不会触发该类报错。
  • 定制方案:如果需要保留现有setuptools版本、也不想修改原有setup.cfg配置,可以在setup.py中重写sdist命令的文件复制钩子,仅对临时构建目录下的输出文件补充写权限,完全不触碰源码目录的原文件权限,实现方式如下:
from setuptools import setup
from setuptools.command.sdist import sdist as _sdist
import os
import stat

class Sdist(_sdist):
    def copy_file(self, infile, outfile, *args, **kwargs):
        # 执行原有的文件复制逻辑
        copy_result = super().copy_file(infile, outfile, *args, **kwargs)
        # 仅为构建临时目录下的输出文件添加所有者写权限
        if os.path.isfile(outfile):
            file_mode = os.stat(outfile).st_mode
            os.chmod(outfile, file_mode | stat.S_IWUSR)
        return copy_result

setup(cmdclass={"sdist": Sdist})

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 03:16:07