I have the beginning of a GUI application written in Rust. I'm using the Py03 crate to run a python function inside my Rust app. I have my python function (test_func) inside a file "test_funcs.py" and this file is located in the same directory as my Rust binary. I also have a copy of this file in the root directory too. The problem is, whilst Py0 is able to find my .py file, it can't seem to find the function I'm trying to call.
Here is the key bit of code:
MyAppMessage::SecondButton => {
Python::with_gil(|py| {
let my_module = PyModule::new(py, "test_funcs").expect("Could not load Python module");
let _load_json = my_module.getattr("test_func").expect("Could not find Python function");
});
}
Here is my error message:
thread 'main' panicked at 'Could not find Python function: PyErr { type: <class 'AttributeError'>, value: AttributeError("module 'test_funcs' has no attribute 'test_func'"), traceback: None }', src/main.rs:78:69
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Here is my full main.rs:
use rfd::FileDialog;
use std::fs::File;
use std::io::Read;
use serde_json::Value;
use pyo3::prelude::*;
use iced::widget::{Button, Column, Container, Text};
use iced::{Element, Settings, Sandbox};
fn main() -> Result<(), iced::Error> {
pyo3::prepare_freethreaded_python();
MyApp::run(Settings::default())
}
struct MyApp {
file_path: Option<String>,
file_contents: String,
}
#[derive(Debug, Clone, Copy)]
enum MyAppMessage {
OpenFile,
SecondButton,
}
impl Sandbox for MyApp {
type Message = MyAppMessage;
fn new() -> Self {
MyApp {
file_path: None,
file_contents: String::new(),
}
}
fn title(&self) -> String {
String::from("My App")
}
fn update(&mut self, message: Self::Message) {
match message {
MyAppMessage::OpenFile => {
let file = FileDialog::new()
.add_filter("JSON Files", &["json"])
.pick_file();
match file {
Some(file_path) => {
self.file_path = Some(file_path.to_string_lossy().into_owned());
let mut file = File::open(&file_path).expect("Could not open file");
let mut contents = String::new();
file.read_to_string(&mut contents).expect("Could not read file");
let json: Value =
serde_json::from_str(&contents).expect("Could not parse JSON");
let pretty_json =
serde_json::to_string_pretty(&json).expect("Could not pretty print JSON");
self.file_contents = pretty_json.clone();
println!("File contents: {}", pretty_json);
println!("Selected file: {:?}", file_path);
}
None => println!("No file selected"),
}
}
MyAppMessage::SecondButton => {
Python::with_gil(|py| {
let my_module = PyModule::new(py, "test_funcs").expect("Could not load Python module");
let _load_json = my_module.getattr("test_func").expect("Could not find Python function");
});
}
}
}
fn view(&self) -> Element<Self::Message> {
let open_file_button = Button::new(Text::new("Open File"))
.on_press(MyAppMessage::OpenFile);
let second_button = Button::new(Text::new("Second Button"))
.on_press(MyAppMessage::SecondButton);
let mut col = Column::new();
col = col.push(open_file_button);
col = col.push(second_button);
if let Some(file_path) = &self.file_path {
col = col.push(Text::new(file_path).width(iced::Length::Fill));
}
Container::new(col)
.center_x()
.center_y()
.width(iced::Length::Fill)
.height(iced::Length::Fill)
.into()
}
}
Here is my .py file, the function is just a test function:
def test_func():
print("hello world")
Here is my cargo.toml:
[package]
name = "eef-gui"
version = "0.1.0"
edition = "2021"
build = "build.rs"
[dependencies]
iced = "0.9.0"
native-dialog = "0.6.3"
rfd = "*"
serde_json = "1.0"
[dependencies.pyo3]
version = "0.19.0"
[features]
extension-module = ["pyo3/extension-module"]
[build-dependencies]
pkg-config = "0.3"
I know pyo is finding the module because when I print 'my_module' I can see this:
<module 'test_funcs'>
Any helps is appreciated.
Cheers