Skip to content

Commit 0a4e6a7

Browse files
authored
Merge pull request #16 from marcomq/async_py
Switch to Async py crate, add tests
2 parents e71c134 + bed1cef commit 0a4e6a7

File tree

9 files changed

+271
-498
lines changed

9 files changed

+271
-498
lines changed

Cargo.toml

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
[package]
22
name = "tauri-plugin-python"
3-
version = "0.3.6"
3+
version = "0.3.7"
44
authors = [ "Marco Mengelkoch" ]
55
description = "A tauri 2 plugin to use python code in the backend."
66
keywords = ["rust", "python", "tauri", "gui"]
77
edition = "2021"
8-
rust-version = "1.77.2"
98
exclude = ["/examples", "/webview-dist", "/webview-src", "/node_modules"]
109
links = "tauri-plugin-python"
1110
license = "MIT"
@@ -16,23 +15,23 @@ repository = "https://github.com/marcomq/tauri-plugin-python"
1615
tauri = { version = "2" }
1716
serde = { version = "1", features = ["derive"] }
1817
thiserror = "2"
18+
async-trait = "0.1"
19+
1920
lazy_static = "1.5.0"
20-
pyo3 = { version = "0.23.3", features=["auto-initialize", "generate-import-lib"], optional = true }
21-
rustpython-pylib = { version = "0.4.0" }
22-
rustpython-stdlib = { version = "0.4.0", features = ["threading"] }
23-
rustpython-vm = { version = "0.4.0", features = [
24-
"importlib",
25-
"serde",
26-
"threading",
27-
] }
21+
async_py = { version = "0.2.1", default-features = false }
22+
tokio = { version = "1", features = ["full"] }
2823
serde_json = "1.0.136"
2924
dunce = "1.0.5"
3025

3126
[build-dependencies]
3227
tauri-plugin = { version = "2", features = ["build"] }
3328

29+
[dev-dependencies]
30+
tauri = { version = "2", features = ["test"] }
31+
3432
[features]
3533
venv = []
36-
default = ["venv"] # auto load src-python/.venv
37-
# default = ["venv", "pyo3"] # enable to use pyo3 instead of rustpython
38-
pyo3 = ["dep:pyo3"]
34+
default = ["venv", "pyo3"] # auto load src-python/.venv
35+
# default = ["venv", "pyo3"] # enable to use pyo3 instead of rustpython.
36+
rustpython = ["async_py/rustpython"]
37+
pyo3 = ["async_py/pyo3"]
Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +0,0 @@
1-
2-
[env]
3-
PYO3_CONFIG_FILE = { value = "target/pyembed/pyo3-build-config-file.txt", relative = true }

src/commands.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,26 @@ pub(crate) async fn run_python<R: Runtime>(
1414
app: AppHandle<R>,
1515
payload: StringRequest,
1616
) -> Result<StringResponse> {
17-
app.run_python(payload)
17+
app.run_python(payload).await
1818
}
1919
#[command]
2020
pub(crate) async fn register_function<R: Runtime>(
2121
app: AppHandle<R>,
2222
payload: RegisterRequest,
2323
) -> Result<StringResponse> {
24-
app.register_function(payload)
24+
app.register_function(payload).await
2525
}
2626
#[command]
2727
pub(crate) async fn call_function<R: Runtime>(
2828
app: AppHandle<R>,
2929
payload: RunRequest,
3030
) -> Result<StringResponse> {
31-
app.call_function(payload)
31+
app.call_function(payload).await
3232
}
3333
#[command]
3434
pub(crate) async fn read_variable<R: Runtime>(
3535
app: AppHandle<R>,
3636
payload: StringRequest,
3737
) -> Result<StringResponse> {
38-
app.read_variable(payload)
38+
app.read_variable(payload).await
3939
}

src/error.rs

Lines changed: 3 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@
22
// © Copyright 2024, by Marco Mengelkoch
33
// Licensed under MIT License, see License file for more details
44
// git clone https://github.com/marcomq/tauri-plugin-python
5+
use async_py::PyRunnerError;
56

6-
#[cfg(feature = "pyo3")]
7-
use pyo3::{prelude::*, PyErr};
87
use serde::{ser::Serializer, Serialize};
98

109
pub type Result<T> = std::result::Result<T, Error>;
@@ -18,6 +17,8 @@ pub enum Error {
1817
#[cfg(mobile)]
1918
#[error(transparent)]
2019
PluginInvoke(#[from] tauri::plugin::mobile::PluginInvokeError),
20+
#[error(transparent)]
21+
PyRunner(#[from] PyRunnerError),
2122
}
2223

2324
impl Serialize for Error {
@@ -35,126 +36,6 @@ impl From<&str> for Error {
3536
}
3637
}
3738

38-
#[cfg(not(feature = "pyo3"))]
39-
impl From<rustpython_vm::PyRef<rustpython_vm::builtins::PyBaseException>> for Error {
40-
fn from(error: rustpython_vm::PyRef<rustpython_vm::builtins::PyBaseException>) -> Self {
41-
let msg = format!("{:?}", &error);
42-
println!("error: {}", &msg);
43-
if let Some(tb) = error.traceback() {
44-
println!("Traceback (most recent call last):");
45-
for trace in tb.iter() {
46-
let file = trace.frame.code.source_path.as_str();
47-
let original_line = trace.lineno.to_usize();
48-
let line = if file == "main.py" {
49-
original_line - 2 // sys.path import has 2 additional lines
50-
} else {
51-
original_line
52-
};
53-
println!(
54-
" File \"{file}\", line {line}, in {}",
55-
trace.frame.code.obj_name
56-
);
57-
}
58-
}
59-
Error::String(msg)
60-
}
61-
}
62-
63-
#[cfg(feature = "pyo3")]
64-
impl From<PyErr> for Error {
65-
fn from(error: PyErr) -> Self {
66-
let error_msg = match pyo3::Python::with_gil(|py| -> Result<Vec<String>> {
67-
let traceback_module = py.import("traceback")?;
68-
let traceback_object = error
69-
.traceback(py)
70-
.ok_or(pyo3::exceptions::PyWarning::new_err("No traceback found."))?;
71-
let extract_traceback = traceback_module.getattr("extract_tb")?;
72-
73-
// Get the formatted traceback lines
74-
let result = extract_traceback.call1((traceback_object,)).and_then(|r| {
75-
match r.extract::<Vec<PyObject>>() {
76-
Ok(v) => {
77-
let mut formatted_lines = Vec::new();
78-
for arg in v.iter() {
79-
let frame = arg.bind(py);
80-
81-
// Extract filename
82-
let filename = match frame.getattr("filename") {
83-
Ok(f) => match f.extract::<String>() {
84-
Ok(s) if s == "<string>".to_string() => {
85-
// Special handling for <string>
86-
frame.setattr("filename", "main.py")?;
87-
let lineno = frame.getattr("lineno")?.extract::<usize>()?;
88-
frame.setattr("lineno", lineno - 2)?;
89-
"main.py".to_string()
90-
}
91-
Ok(s) => s,
92-
Err(_) => "<unknown>".to_string(),
93-
},
94-
Err(_) => "<unknown>".to_string(),
95-
};
96-
97-
// Extract line number
98-
let lineno = match frame.getattr("lineno") {
99-
Ok(l) => match l.extract::<usize>() {
100-
Ok(n) => n,
101-
Err(_) => 0,
102-
},
103-
Err(_) => 0,
104-
};
105-
106-
// Extract function name
107-
let name = match frame.getattr("name") {
108-
Ok(n) => match n.extract::<String>() {
109-
Ok(s) => s,
110-
Err(_) => "<unknown>".to_string(),
111-
},
112-
Err(_) => "<unknown>".to_string(),
113-
};
114-
115-
// Extract line content (if available)
116-
let line = match frame.getattr("line") {
117-
Ok(l) => match l.extract::<Option<String>>() {
118-
Ok(Some(s)) => format!("\t{}", s),
119-
_ => "".to_string(),
120-
},
121-
Err(_) => "".to_string(),
122-
};
123-
124-
// Format the line like requested
125-
let formatted_line = format!(
126-
"File \"{}\", line {}, in {}\n{}",
127-
filename, lineno, name, line
128-
);
129-
130-
formatted_lines.push(formatted_line);
131-
}
132-
133-
Ok(formatted_lines)
134-
}
135-
Err(_) => Err(PyErr::new::<pyo3::exceptions::PyValueError, _>(
136-
"Failed to extract traceback",
137-
)),
138-
}
139-
})?;
140-
141-
// Add traceback header
142-
let mut full_traceback = vec!["Traceback (most recent call last):".to_string()];
143-
full_traceback.extend(result);
144-
145-
// Add error type and message
146-
full_traceback.push(error.to_string());
147-
148-
Ok(full_traceback)
149-
}) {
150-
Ok(formatted) => formatted.join("\n"),
151-
Err(_) => error.to_string(), // Fall back to simple error message
152-
};
153-
154-
Error::String(error_msg)
155-
}
156-
}
157-
15839
impl From<tauri::Error> for Error {
15940
fn from(error: tauri::Error) -> Self {
16041
Error::String(error.to_string())

0 commit comments

Comments
 (0)