Python读取配置文件失败主因是路径未对齐:conftest.py中用相对路径读config.ini会因pytest启动位置不同而失效;正确做法是以os.path.abspath(__file__)为基准向上查找,并用utf-8-sig编码防BOM报错。

Python测试脚本读取不到配置文件,90%不是配置写错了,而是 conftest.py 里路径逻辑没对齐当前工作目录或包结构 —— 尤其当 pytest 在不同层级执行(比如根目录 vs tests/ 子目录)时,相对路径会直接失效。
conftest.py 中用相对路径读 config.ini 必然失败的典型场景
很多人在 conftest.py 里这样写:
import configparser
cf = configparser.ConfigParser()
cf.read("config.ini") # ❌ 错误:依赖当前工作目录,pytest 启动位置一变就找不到
问题在于:pytest 执行时的 os.getcwd() 是你敲命令的地方(比如项目根目录),而 conftest.py 可能放在 tests/ 下;这时 "config.ini" 会被当成在 tests/config.ini 查找,但文件实际在项目根目录。
- 用
os.getcwd()获取路径 → 拿到的是命令行所在目录,不是conftest.py所在目录 - 用
__file__但没转绝对路径 →os.path.dirname(__file__)返回的是conftest.py的目录,但拼接后仍可能因斜杠或编码出错 - 没检查文件是否存在就直接
read()→ 报FileNotFoundError但不告诉你具体路径在哪
正确做法:用 __file__ 定位 conftest.py 所在目录再向上/向下找 config
核心原则:所有路径起点必须是 conftest.py 自身位置,而不是“你以为的当前目录”。
立即学习“Python免费学习笔记(深入)”;
- 获取
conftest.py所在目录:os.path.dirname(os.path.abspath(__file__)) - 向上一级找 config(常见布局:config.ini 在项目根目录,conftest.py 在 tests/ 下):
os.path.join(script_dir, "..", "config.ini") - 用
os.path.normpath()清理路径中的..和斜杠混用:os.path.normpath(path) - 读取前先检查:
if not os.path.isfile(config_path): raise FileNotFoundError(f"Config not found: {config_path}")
示例代码片段:
import os
import configparser
<p>script_dir = os.path.dirname(os.path.abspath(<strong>file</strong>))
config_path = os.path.normpath(os.path.join(script_dir, "..", "config.ini"))</p><p>if not os.path.isfile(config_path):
raise FileNotFoundError(f"Config not found: {config_path}")</p><p>cf = configparser.ConfigParser()
cf.read(config_path, encoding="utf-8-sig") # utf-8-sig 防记事本 BOM 头报错
PyInstaller 打包后 conftest.py 读不到 config.ini 怎么办
打包后 conftest.py 被编译进 .exe,但 config.ini 是外部文件,路径逻辑必须适配运行时环境。
- 不能依赖
__file__(pyinstaller 打包后它指向临时解压路径或根本不可读) - 改用
sys._MEIPASS:pyinstaller 运行时会把资源解压到这个临时目录 - 但注意:你得把
config.ini显式加进打包资源(用--add-data),否则sys._MEIPASS里没有它 - 兼容开发和打包两种模式:先尝试
sys._MEIPASS,失败再 fallback 到基于__file__的路径
简化判断逻辑:
import os
import sys
import configparser
<p>def get_config_path():
if getattr(sys, 'frozen', False):</p><h1>打包模式</h1><pre class="brush:php;toolbar:false;"> base_path = sys._MEIPASS
else:
# 开发模式
base_path = os.path.dirname(os.path.abspath(__file__))
return os.path.join(base_path, "..", "config.ini")config_path = os.path.normpath(get_config_path())
为什么 configparser.MissingSectionHeaderError 总在修改 config.ini 后出现
不是语法错了,大概率是 Windows 记事本保存时加了 BOM 头 —— 文件开头多出 \ufeff,configparser 读到第一行就懵了,以为没 section。
- 错误现象:
configparser.MissingSectionHeaderError: File contains no section headers - 验证方法:用 VS Code 或 Notepad++ 打开 config.ini,看编码是否显示为 “UTF-8 with BOM”
- 解决方式:读取时强制用
encoding="utf-8-sig"(自动跳过 BOM),或者让编辑器保存为 “UTF-8 without BOM” - 别用
open(..., encoding="utf-8")再传给cf.read_file(),那会绕过utf-8-sig的 BOM 处理逻辑
正确写法只有这一种:
cf.read(config_path, encoding="utf-8-sig")
路径问题从来不是“试试看”,而是每次都要明确回答三个问题:这个文件物理存在哪?Python 当前认为它在哪?两者怎么对齐。尤其在 conftest.py 这种 pytest 自动加载的模块里,少一次 os.path.abspath(__file__),就多一个半夜排查的线上 bug。


















