Skip to content

Commit 5e7c548

Browse files
committed
add lazy initialized sync runtime
1 parent 4aef0e7 commit 5e7c548

File tree

3 files changed

+17
-23
lines changed

3 files changed

+17
-23
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ categories = ["api-bindings", "asynchronous", "external-ffi-bindings"]
1414
dunce = "1.0.4"
1515
serde_json = "1.0.114"
1616
thiserror = "2.0"
17+
once_cell = "1.19"
1718
tokio = { version = "1.36.0", features = ["sync", "macros"] }
1819

1920
[features]

src/lib.rs

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,19 @@ mod pyo3_runner;
1010
#[cfg(feature = "rustpython")]
1111
mod rustpython_runner;
1212

13+
use once_cell::sync::Lazy;
1314
use serde_json::Value;
1415
use std::path::{Path, PathBuf};
1516
use std::thread;
1617
use thiserror::Error;
18+
use tokio::runtime::Runtime;
1719
use tokio::sync::{mpsc, oneshot};
1820

21+
/// A lazily-initialized global Tokio runtime for synchronous functions.
22+
static SYNC_RUNTIME: Lazy<Runtime> = Lazy::new(|| {
23+
Runtime::new().expect("Failed to create a new Tokio runtime for sync functions")
24+
});
25+
1926
#[derive(Debug)]
2027
pub(crate) enum CmdType {
2128
RunFile(PathBuf),
@@ -143,9 +150,7 @@ impl PyRunner {
143150
///
144151
/// **Note:** Calling this from an existing async runtime can lead to panics.
145152
pub fn run_sync(&self, code: &str) -> Result<(), PyRunnerError> {
146-
let rt =
147-
tokio::runtime::Runtime::new().map_err(|e| PyRunnerError::PyError(e.to_string()))?;
148-
rt.block_on(self.run(code))
153+
SYNC_RUNTIME.block_on(self.run(code))
149154
}
150155

151156
/// Asynchronously runs a python file.
@@ -166,9 +171,7 @@ impl PyRunner {
166171
///
167172
/// **Note:** Calling this from an existing async runtime can lead to panics.
168173
pub fn run_file_sync(&self, file: &Path) -> Result<(), PyRunnerError> {
169-
let rt =
170-
tokio::runtime::Runtime::new().map_err(|e| PyRunnerError::PyError(e.to_string()))?;
171-
rt.block_on(self.run_file(file))
174+
SYNC_RUNTIME.block_on(self.run_file(file))
172175
}
173176

174177
/// Asynchronously evaluates a single Python expression.
@@ -190,9 +193,7 @@ impl PyRunner {
190193
///
191194
/// **Note:** Calling this from an existing async runtime can lead to panics.
192195
pub fn eval_sync(&self, code: &str) -> Result<Value, PyRunnerError> {
193-
let rt =
194-
tokio::runtime::Runtime::new().map_err(|e| PyRunnerError::PyError(e.to_string()))?;
195-
rt.block_on(self.eval(code))
196+
SYNC_RUNTIME.block_on(self.eval(code))
196197
}
197198

198199
/// Asynchronously reads a variable from the Python interpreter's global scope.
@@ -214,9 +215,7 @@ impl PyRunner {
214215
///
215216
/// **Note:** Calling this from an existing async runtime can lead to panics.
216217
pub fn read_variable_sync(&self, var_name: &str) -> Result<Value, PyRunnerError> {
217-
let rt =
218-
tokio::runtime::Runtime::new().map_err(|e| PyRunnerError::PyError(e.to_string()))?;
219-
rt.block_on(self.read_variable(var_name))
218+
SYNC_RUNTIME.block_on(self.read_variable(var_name))
220219
}
221220

222221
/// Asynchronously calls a Python function in the interpreter's global scope.
@@ -255,8 +254,7 @@ impl PyRunner {
255254
name: &str,
256255
args: Vec<Value>,
257256
) -> Result<Value, PyRunnerError> {
258-
let rt = tokio::runtime::Runtime::new().map_err(|e| PyRunnerError::PyError(e.to_string()))?;
259-
rt.block_on(self.call_function(name, args))
257+
SYNC_RUNTIME.block_on(self.call_function(name, args))
260258
}
261259

262260
/// Asynchronously calls an async Python function in the interpreter's global scope.
@@ -293,9 +291,7 @@ impl PyRunner {
293291
name: &str,
294292
args: Vec<Value>,
295293
) -> Result<Value, PyRunnerError> {
296-
let rt =
297-
tokio::runtime::Runtime::new().map_err(|e| PyRunnerError::PyError(e.to_string()))?;
298-
rt.block_on(self.call_async_function(name, args))
294+
SYNC_RUNTIME.block_on(self.call_async_function(name, args))
299295
}
300296

301297
/// Stops the Python execution thread gracefully.
@@ -312,9 +308,7 @@ impl PyRunner {
312308
///
313309
/// **Note:** Calling this from an existing async runtime can lead to panics.
314310
pub fn stop_sync(&self) -> Result<(), PyRunnerError> {
315-
let rt =
316-
tokio::runtime::Runtime::new().map_err(|e| PyRunnerError::PyError(e.to_string()))?;
317-
rt.block_on(self.stop())
311+
SYNC_RUNTIME.block_on(self.stop())
318312
}
319313

320314
/// Set python venv environment folder (does not change interpreter)
@@ -360,9 +354,7 @@ impl PyRunner {
360354
///
361355
/// **Note:** Calling this from an existing async runtime can lead to panics.
362356
pub fn set_venv_sync(&self, venv_path: &Path) -> Result<(), PyRunnerError> {
363-
let rt =
364-
tokio::runtime::Runtime::new().map_err(|e| PyRunnerError::PyError(e.to_string()))?;
365-
rt.block_on(self.set_venv(venv_path))
357+
SYNC_RUNTIME.block_on(self.set_venv(venv_path))
366358
}
367359
}
368360

0 commit comments

Comments
 (0)