Python相对导入问题:外部调用hug API子进程报错求助
问题描述
原本在demo/mypkg目录内直接执行hug -f api.py或python startserver.py都能正常运行Hug API,但为了实现从包外部启动并支持PyInstaller打包,修改为相对导入并添加__init__.py、__main__.py后,执行python -m mypkg时出现相对导入错误:
File "...\demo\mypkg\api.py", line 2, in <module> from .a import greeting ImportError: attempted relative import with no known parent package
核心原因是子进程中hug -f apipath直接把api.py作为脚本运行,此时它不在包上下文里,相对导入规则失效。
初始正常场景
项目结构
├── demo │ ├── mypkg │ │ ├── a.py │ │ ├── b.py │ │ ├── api.py │ │ └── startserver.py │ └── readme.md
关键代码(可正常运行版本)
- a.py
from b import name def greeting(): return name()
- api.py
import hug from a import greeting @hug.get('/ping') def ping(): return {"response": "pong"} @hug.get('/hello') def hello(): return {"response": greeting()}
- startserver.py
import subprocess with open('testapi.log', 'w') as fd: subprocess.run(['hug', '-f', 'api.py'], stdout=fd , stderr=subprocess.STDOUT, bufsize=0)
在demo/mypkg目录内用虚拟环境执行hug -f api.py或python startserver.py,API可正常访问/ping和/hello接口。
修改后出现问题的场景
修改后的项目结构
├── demo │ ├── mypkg │ │ ├── __init__.py │ │ ├── __main__.py │ │ ├── a.py │ │ ├── b.py │ │ ├── api.py │ │ └── startserver.py │ └── readme.md
修改后的关键代码
- a.py(改为相对导入)
from .b import name def greeting(): return name()
- api.py(改为相对导入)
import hug from .a import greeting @hug.get('/ping') def ping(): return {"response": "pong"} @hug.get('/hello') def hello(): return {"response": greeting()}
- main.py
print('inside __main__.py') import traceback from .startserver import start def main(): try: start() except Exception: print(traceback.format_exc()) if __name__ == "__main__": print('... inside name == main ...') main()
- startserver.py(调整路径获取逻辑)
import subprocess import traceback import os from pathlib import Path def start(): try: currentpath = Path(__file__) print(f'Currently executing from {currentpath}') apipath = os.path.join(currentpath.parent, 'api.py') print(f'parse api path is {apipath}') print('inside startserver start()') with open('testapi.log', 'w') as fd: subprocess.run(['hug', '-f', apipath], stdout=fd , stderr=subprocess.STDOUT, bufsize=0) except Exception: print(traceback.format_exc())
执行python -m mypkg时触发相对导入错误。
解决方案
方案1:让子进程在包上下文运行Hug
不要直接执行api.py脚本,而是通过模块方式启动Hug,保留包上下文以支持相对导入。修改startserver.py的start函数:
import sys # 需新增导入 import subprocess import traceback from pathlib import Path def start(): try: # 获取demo目录路径(mypkg的父目录) current_dir = Path(__file__).parent demo_dir = current_dir.parent print(f'Working dir set to: {demo_dir}') with open('testapi.log', 'w') as fd: # 以模块方式启动Hug,指定mypkg.api作为目标模块 subprocess.run( [sys.executable, '-m', 'hug', '-f', 'mypkg.api'], stdout=fd, stderr=subprocess.STDOUT, bufsize=0, cwd=demo_dir # 切换到demo目录作为工作目录,确保能识别mypkg包 ) except Exception: print(traceback.format_exc())
方案2:修改代码兼容两种运行方式
调整api.py和a.py的导入逻辑,让它们既能作为包模块运行,也能作为独立脚本运行:
- api.py
import hug # 兼容相对导入与直接运行场景 try: from .a import greeting except ImportError: from a import greeting @hug.get('/ping') def ping(): return {"response": "pong"} @hug.get('/hello') def hello(): return {"response": greeting()}
- a.py
# 兼容相对导入与直接运行场景 try: from .b import name except ImportError: from b import name def greeting(): return name()
这种方式无需修改启动逻辑,同时支持包内、包外两种运行场景,也能兼容PyInstaller打包。
方案3:PyInstaller打包适配
如果选择方案1,打包时需确保PyInstaller能正确识别mypkg的包结构,可通过以下方式处理:
- 直接用命令行指定隐式导入:
pyinstaller --hidden-import mypkg.a --hidden-import mypkg.b mypkg/__main__.py
- 编写
spec文件(更可控):
# mypkg.spec block_cipher = None a = Analysis( ['mypkg/__main__.py'], pathex=['demo'], # 指定demo目录为搜索路径 binaries=[], datas=[], hiddenimports=['mypkg.a', 'mypkg.b'], hookspath=[], hooksconfig={}, runtime_hooks=[], excludes=[], win_no_prefer_redirects=False, win_private_assemblies=False, cipher=block_cipher, noarchive=False, ) pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) exe = EXE( pyz, a.scripts, a.binaries, a.zipfiles, a.datas, [], name='mypkg', debug=False, bootloader_ignore_signals=False, strip=False, upx=True, upx_exclude=[], runtime_tmpdir=None, console=True, disable_windowed_traceback=False, target_arch=None, codesign_identity=None, entitlements_file=None, )
执行pyinstaller mypkg.spec完成打包。
内容的提问来源于stack exchange,提问作者Impostor Syndrome
相关产品推荐
相关产品推荐

