{"id":31817784,"url":"https://github.com/marcomq/async_py","last_synced_at":"2026-01-20T17:31:42.606Z","repository":{"id":316706653,"uuid":"1063582958","full_name":"marcomq/async_py","owner":"marcomq","description":"Async Rust library to call python code and functions","archived":false,"fork":false,"pushed_at":"2025-10-04T12:09:22.000Z","size":145,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-10-04T14:19:49.379Z","etag":null,"topics":["async","pyo3","python","rust","rustpython"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/marcomq.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-24T20:39:40.000Z","updated_at":"2025-09-28T18:14:22.000Z","dependencies_parsed_at":"2025-09-26T08:37:36.466Z","dependency_job_id":"0a2956f9-cc3e-4da5-812d-5eab67499371","html_url":"https://github.com/marcomq/async_py","commit_stats":null,"previous_names":["marcomq/async_py"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/marcomq/async_py","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcomq%2Fasync_py","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcomq%2Fasync_py/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcomq%2Fasync_py/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcomq%2Fasync_py/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/marcomq","download_url":"https://codeload.github.com/marcomq/async_py/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcomq%2Fasync_py/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279006854,"owners_count":26084204,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-11T02:00:06.511Z","response_time":55,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["async","pyo3","python","rust","rustpython"],"created_at":"2025-10-11T10:25:10.994Z","updated_at":"2026-01-20T17:31:42.586Z","avatar_url":"https://github.com/marcomq.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# async_py\n\nA Rust library for calling Python code asynchronously using [pyo3](https://github.com/PyO3/pyo3) and [tokio](https://tokio.rs/).\n\nThis library provides a `PyRunner` that runs a Python interpreter in a dedicated \nbackground thread. Global variables are preserved within the runner's scope. \nYou can send Python code to this executor from any async Rust task, \nand it will be executed without blocking your application.\n\nDue to the Python Global Interpreter Lock (GIL), you cannot run Python code simultaneously. If one Python\ntask is performing a computation or a sleep, no other task can access the interpreter.\n\nUsing multiple `PyRunner` instances does not enable simultaneous Python code execution due to the GIL. \nHowever, it does allow you to isolate global variables between different runners.\n\n## Usage\n\nAdd `async_py` to your `Cargo.toml`:\n\n```toml\n[dependencies]\nasync_py = \"0.2.0\"\ntokio = { version = \"1\", features = [\"full\"] }\n```\n\n### Example\n\n```rust\nuse async_py::PyRunner;\n\n#[tokio::main]\nasync fn main() {\n    let runner = PyRunner::new();\n\n    let code = r#\"\ncounter = 0\ndef greet(name):\n    global counter\n    counter = counter + 1\n    s = \"\" if counter \u003c 2 else \"s\"\n    return f\"Hello {name}! Called {counter} time{s} from Python.\"\n\"#;\n    // Python function \"greet\" and variable \"counter\" is added to globals\n    runner.run(code).await.unwrap();\n\n    // Calling code\n    let result1 = runner.call_function(\"greet\", vec![\"World\".into()]).await.unwrap();\n    println!(\"{}\", result1.as_str().unwrap()); // Prints: Hello World! Called 1 time from Python.\n    let result2 = runner.call_function(\"greet\", vec![\"World\".into()]).await.unwrap();\n    println!(\"{}\", result2.as_str().unwrap()); // Prints: Hello World! Called 2 times from Python.\n}\n```\n\n### Async Python Example\n\n```rust\nuse async_py::PyRunner;\n\n#[tokio::main]\nasync fn main() {\n    let runner = PyRunner::new();\n    let code = r#\"\nimport asyncio\ncounter = 0\n\nasync def add_and_sleep(a, b, sleep_time):\n    global counter\n    await asyncio.sleep(sleep_time)\n    counter += 1\n    return a + b + counter\n\"#;\n\n    runner.run(code).await.unwrap();\n    let result1 = runner.call_async_function(\"add_and_sleep\", vec![5.into(), 10.into(), 1.into()]);\n    let result2 = runner.call_async_function(\"add_and_sleep\", vec![5.into(), 10.into(), 0.1.into()]);\n    let (result1, result2) = tokio::join!(result1, result2);\n    assert_eq!(result1.unwrap(), Value::Number(17.into()));\n    assert_eq!(result2.unwrap(), Value::Number(16.into()));\n}\n```\nBoth function calls are triggered to run async code at the same time. While the first call waits for the sleep,\nthe second can already start and also increment the counter first. Therefore,\nresult1 will wait longer and compute 5 + 10 + 2, while the result2 can compute 5 + 10 + 1.\n\nEach call will use its own event loop. This may not be very efficient and changed later.\n\nMake sure to use `call_async_function` for async python functions. Using `call_function` will\nprobably raise an error. \n`call_async_function` is not available for RustPython.\n\n### Thread Safety\n\nThe `PyRunner` is designed to be safely shared across multiple threads.\n\nYou can clone a `PyRunner` instance and move it into different threads. All commands sent from any clone are funneled through a channel to a single, dedicated OS thread that houses the Python interpreter. This design correctly handles Python's Global Interpreter Lock (GIL) by ensuring that only one Python operation is attempted at any given time within that interpreter instance.\n\nHere is an example of using `PyRunner` from multiple `std::thread`s:\n\n```rust\nuse async_py::{PyRunner, PyRunnerError};\nuse std::thread;\n\nfn main() -\u003e Result\u003c(), PyRunnerError\u003e {\n    let runner = PyRunner::new();\n    runner.run_sync(\"x = 0\")?;\n\n    let mut handles = vec![];\n\n    for i in 0..5 {\n        let runner_clone = runner.clone();\n        let handle = thread::spawn(move || {\n            // Use the _sync versions for non-async contexts\n            runner_clone.run_sync(\u0026format!(\"x += {}\", i)).unwrap();\n        });\n        handles.push(handle);\n    }\n\n    for handle in handles {\n        handle.join().unwrap();\n    }\n\n    let final_x = runner.read_variable_sync(\"x\")?;\n    // Expected: 0 + 1 + 2 + 3 + 4 = 10\n    assert_eq!(final_x, Value::Number(10.into()));\n\n    runner.stop_sync()?;\n    Ok(())\n}\n```\n\n### Using a venv\nIt is generally recommended to use a venv to install pip packages.\nWhile you cannot switch the interpreter version with this crate, you can use an\nexisting venv to load installed packages.\n\n```rust\nlet runner = PyRunner::new();\nrunner.set_venv(\u0026Path::new(\"/path/to/venv\")).await?;\n```\n\n### rustpython\nPyO3 has usually the best compatibility for python packages.\nBut if you want to use python on android, wasm, or if you don't want to use any\nexternal library, you can use [rustpython](https://github.com/RustPython/RustPython).\nKeep in mind that some essential packages like `os` are missing on rustpython.\n\nCargo.toml\n```toml\n[dependencies]\nasync_py = { features = [\"rustpython\"] } \n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarcomq%2Fasync_py","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmarcomq%2Fasync_py","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarcomq%2Fasync_py/lists"}