Here's how I'd do it.
Option 1: Modify sys.path
(for resolving the module path at runtime)
// 'import sys'
let sys = py.import("sys")?;
// 'sys.path = ["./pyth"] + sys.path'
let mut sys_path: Vec<String> = sys.get(py, "path")?.extract(py)?;
sys_path.insert(0, "./pyth".into());
sys.add(py, "path", sys_path);
// // alternatively, all of the above can be written as:
// py.run(r#"
// import sys
// sys.path = ["./pyth"] + sys.path
// "#, None, None)?;
// 'import fibo'; (this should work now)
let m = py.import("fibo")?;
// 'fibo.fib(2)'
let out: i32 = m.call(py, "fib", (2,), None)?.extract(py)?;
println!("successfully found fibo.py at runtime. Output: {:?}", out);
Because this resolves at runtime, the module will need to be somewhere you can easily find it. The way I wrote it looks in the current directory (as does @withkittens' answer), meaning your program won't work if run from somewhere other than the project root.
Option 2: Make your own module (this resolves at compiletime)
(IMPORTANT: This solution is incomplete. See this post!)
// the contents of fibo.py, embedded into the program as string data.
const FIBO_PY: &'static str = include_str!("../pyth/fibo.py");
// make a module with nothing in it
let m = PyModule::new(py, "fibo_2")?;
// run/eval the contents as if they were placed at the top of the module.
// Basically, we use `fibo_2.__dict__` as both our globals and locals, so that
// anything defined by the script appears in there.
let m_locals = m.get(py, "__dict__")?.extract(py)?;
py.run(FIBO_PY, Some(&m_locals), None);
let out: i32 = m.call(py, "fib", (2,), None)?.extract(py)?;
println!("successfully found fibo.py at compiletime. Output: {:?}", out);
The trick here is that run
modifies the locals dict you give it. x.__dict__
is a special object in python used when looking up attributes like x.thing
, so adding entries to it makes them appear as members on the object.