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
相关产品推荐
相关产品推荐

