dmPython 安装后 python -m 报错:.pth + os.execve 的排查实录
背景
信创项目,业务系统从 MySQL 迁移到达梦数据库。安装 dmPython 驱动后,import dmPython 正常,但 python3 -m pip 突然报错:
bash
$ python3 -m pip install xxx
/usr/bin/python3: No module named -V
信创环境是手动编译的 Python,pip 可执行文件不在 PATH 里,只能用 python3 -m pip 调用。而现在整个 -m 机制都报错,模块名变成了 -V 或其他奇怪参数。
排查过程
常规检查(无果)
- 环境变量 ---
PYTHONPATH、PATH正常 - 重装 pip --- 问题依旧
- 查看 dmPython 文件 --- 除了多出一些文件,没发现明显异常
strace 定位 .pth
用 strace -f 跟踪系统调用,发现 CPython 初始化时读取了 dmPython.pth:
bash
$ strace -f python3 -m pip 2>&1 | grep pth
openat(AT_FDCWD, "/site-packages/dmPython.pth", O_RDONLY) = 3
查看内容:
bash
$ cat /site-packages/dmPython.pth
import dmdpi
继续查看 dmdpi.py:
python
import os, sys
if 'LD_LIBRARY_PATH' not in os.environ:
os.environ['LD_LIBRARY_PATH'] = dpi_path
if sys.argv[0] == '':
os.execve(sys.executable, [sys.executable, ...], os.environ)
else:
os.execve(sys.executable, [sys.executable] + sys.argv, os.environ)
问题初步定位:.pth 文件触发了 import dmdpi,dmdpi.py 又执行了 os.execve。
根因分析
CPython 的 -m 参数处理机制
执行 python3 -m pip install xxx 时:
- C 层解析命令行 ,模块名
pip被单独保存 ,不在sys.argv里 sys.argv = ['-m', 'install', 'xxx'](只含-m和模块名之后的参数)- import site → 处理 .pth → 执行
import dmdpi - site 走完 → runpy 用单独保存的模块名执行
问题在第 3 步
dmdpi.py 在 site 初始化中途 执行 os.execve,用 sys.argv 拼接新命令:
ini
原始命令:python3 -m pip install xxx
sys.argv = ['-m', 'install', 'xxx'] ← 模块名 pip 已移除
execve 执行:python3 -m install xxx ← install 被当成模块名
实际报错显示 python3 -m -V,就是因为 sys.argv 里 -m 后面的参数被当成了模块名。
验证 site 阶段的 sys.argv
写个 usercustomize.py 打印 sys.argv:
bash
$ cat /site-packages/usercustomize.py
import sys, os
os.environ['ARGV_DEBUG'] = str(sys.argv)
$ python3 -m pip --version
$ echo $ARGV_DEBUG
['-m', '--version'] # 模块名 pip 确实不在
为什么 import dmPython 没问题
dmPython.so 内置了 RPATH($ORIGIN/dmPython.libs),能自己找到 libdmdpi.so。根本不需要:
- dmdpi.py 设置 LD_LIBRARY_PATH
- os.execve 替换进程
dmdpi.py 是多余的,且破坏了正常启动流程。
修复
删除 .pth 文件:
bash
rm /site-packages/dmPython.pth
删除后 python3 -m pip 恢复正常,import dmPython 也正常。
后续
问题反馈给达梦官方,后续版本改为 wheel 格式。
旧版(egg)文件结构:
arduino
dmPython.pth ← 触发 import dmdpi
dmdpi.py ← 执行 os.execve,导致问题
dpi/libdmdpi.so ← 依赖库单独存放
dmPython.cpython-xxx.so
dmpython-2.5.5.dist-info/
新版(wheel)文件结构:
bash
dmPython.cpython-310-x86_64-linux-gnu.so
dmPython.libs/libcrypto-xxx.so
dmpython-2.5.32.dist-info/
对比:.pth 没了,dmdpi.py 没了,dpi/ 目录没了。所有依赖编译进 .so 或通过 dmPython.libs/ 加载,不再需要在 site 初始化阶段执行代码。
教训
.pth文件里的import会在 site 初始化时无条件执行.pth里如果用os.execve,site 初始化阶段sys.argv是残缺的(-m模块名已被 CPython 移除)strace是排查解释器级别问题的利器