PyO3导入含相对导入的本地Python文件报错的解决方法咨询
解决PyO3导入含相对导入的Python模块时的ImportError问题
问题原因
Python的相对导入依赖于模块所处的包上下文,当PyO3直接加载单个.py文件时,Python不会将该文件视为包的一部分,因此from .test2 import hello这类相对导入会触发ImportError。
解决方案(无需修改Python代码)
方案1:将目录转为Python常规包(推荐用于大型项目迁移)
这是最符合Python模块规范的方式,仅需添加一个空文件,无需改动现有Python代码:
- 在
test1.py和test2.py所在的目录下创建一个空的__init__.py文件,将该目录标记为Python包(比如包名为my_py_package)。 - 在Rust的PyO3代码中,先将包所在目录添加到Python的
sys.path,再通过包名+模块名导入test1:
use pyo3::prelude::*; use pyo3::types::PyList; fn main() -> PyResult<()> { Python::with_gil(|py| { // 获取包所在目录的绝对路径 let package_dir = std::env::current_dir()?; let package_dir_str = package_dir.to_str().ok_or_else(|| { PyErr::new::<pyo3::exceptions::PyUnicodeDecodeError, _>("无法解析目录路径") })?; // 将目录添加到sys.path let sys = py.import("sys")?; let sys_path: &PyList = sys.getattr("path")?.downcast()?; sys_path.insert(0, package_dir_str)?; // 通过包名导入test1模块 let test1 = py.import("my_py_package.test1")?; // 调用模块中的hello函数 test1.call_method0("hello")?; Ok(()) }) }
方案2:手动加载模块并设置包上下文(无需额外文件)
如果不想创建__init__.py,可以通过importlib.util手动加载模块,并强制设置__package__属性,让Python识别相对导入的上下文:
use pyo3::prelude::*; fn main() -> PyResult<()> { Python::with_gil(|py| { // 获取test1.py所在的绝对目录 let module_dir = std::env::current_dir()?; let module_dir_str = module_dir.to_str().ok_or_else(|| { PyErr::new::<pyo3::exceptions::PyUnicodeDecodeError, _>("无法解析目录路径") })?; // 执行Python代码手动加载test1,设置__package__以支持相对导入 let load_code = format!(r#" import importlib.util import sys sys.path.insert(0, "{}") # 加载test1模块 spec = importlib.util.spec_from_file_location("test1", "test1.py") test1 = importlib.util.module_from_spec(spec) # 设置__package__让相对导入生效(空字符串表示当前包) test1.__package__ = "" spec.loader.exec_module(test1) "#, module_dir_str); // 执行代码并获取test1模块对象 let test1 = py.eval(load_code.as_str(), None, None)?.extract::<Py<PyAny>>()?; // 调用hello函数 test1.call_method0("hello")?; Ok(()) }) }
关键说明
- 两种方案的核心都是让Python将
test1.py视为某个包的成员,从而激活相对导入的逻辑。 - 方案1更适合大型项目迁移,因为它遵循Python的包结构规范,后续扩展模块时无需额外调整加载逻辑。
内容的提问来源于stack exchange,提问作者cegom64089
相关产品推荐
相关产品推荐

