GeezerVison is showing github.com — Add minimal capi lifecycle support (#7648) · RustPython/RustPython@f122e3e
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: .cspell.dict/cpython.txt +1Lines changed: 1 addition & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -161,6 +161,7 @@ Pyfunc | 161 | 161 | pylifecycle | 162 | 162 | pymain | 163 | 163 | pyrepl | | 164 | +pystate | 164 | 165 | PYTHONTRACEMALLOC | 165 | 166 | PYTHONUTF8 | 166 | 167 | pythonw | |
Collapse file Copy file name to clipboardExpand all lines: .cspell.dict/python-more.txt +1Lines changed: 1 addition & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -189,6 +189,7 @@ pycodecs | 189 | 189 | pycs | 190 | 190 | pydatetime | 191 | 191 | pyexpat | | 192 | +PYGILSTATE | 192 | 193 | pyio | 193 | 194 | pymain | 194 | 195 | PYTHONAPI | |
Collapse file Copy file name to clipboardExpand all lines: Cargo.lock +10-7Lines changed: 10 additions & 7 deletions Collapse file Copy file name to clipboardExpand all lines: Cargo.toml +3-1Lines changed: 3 additions & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -10,6 +10,7 @@ repository.workspace = true | 10 | 10 | license.workspace = true | 11 | 11 |
| 12 | 12 | [features] | | 13 | +capi = ["dep:rustpython-capi", "threading"] | 13 | 14 | default = ["threading", "stdlib", "stdio", "importlib", "ssl-rustls", "host_env"] | 14 | 15 | host_env = ["rustpython-vm/host_env", "rustpython-stdlib?/host_env"] | 15 | 16 | importlib = ["rustpython-vm/importlib"] | @@ -31,6 +32,7 @@ tkinter = ["rustpython-stdlib/tkinter"] | 31 | 32 | winresource = "0.1" | 32 | 33 |
| 33 | 34 | [dependencies] | | 35 | +rustpython-capi = { workspace = true, optional = true } | 34 | 36 | rustpython-compiler = { workspace = true } | 35 | 37 | rustpython-pylib = { workspace = true, optional = true } | 36 | 38 | rustpython-stdlib = { workspace = true, optional = true, features = ["compiler"] } | @@ -140,6 +142,7 @@ repository = "https://github.com/RustPython/RustPython" | 140 | 142 | license = "MIT" | 141 | 143 |
| 142 | 144 | [workspace.dependencies] | | 145 | +rustpython-capi = { path = "crates/capi", version = "0.5.0" } | 143 | 146 | rustpython-compiler-core = { path = "crates/compiler-core", version = "0.5.0" } | 144 | 147 | rustpython-compiler = { path = "crates/compiler", version = "0.5.0" } | 145 | 148 | rustpython-codegen = { path = "crates/codegen", version = "0.5.0" } | @@ -257,7 +260,6 @@ rustls-platform-verifier = "0.7" | 257 | 260 | rustyline = "18" | 258 | 261 | serde = { package = "serde_core", version = "1.0.225", default-features = false, features = ["alloc"] } | 259 | 262 | schannel = "0.1.29" | 260 | | -scoped-tls = "1" | 261 | 263 | scopeguard = "1" | 262 | 264 | sha-1 = "0.10.0" | 263 | 265 | sha2 = "0.10.2" | |
Collapse file Copy file name to clipboard +25-13Lines changed: 25 additions & 13 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 1 | 1 | fn main() { | 2 | | - if std::env::var("CARGO_CFG_TARGET_OS").unwrap() == "windows" { | 3 | | - println!("cargo:rerun-if-changed=logo.ico"); | 4 | | - let mut res = winresource::WindowsResource::new(); | 5 | | - if std::path::Path::new("logo.ico").exists() { | 6 | | - res.set_icon("logo.ico"); | 7 | | - } else { | 8 | | - println!("cargo:warning=logo.ico not found, skipping icon embedding"); | 9 | | - return; | | 2 | + let target = std::env::var("CARGO_CFG_TARGET_OS").unwrap(); | | 3 | + let capi_enabled = std::env::var_os("CARGO_FEATURE_CAPI").is_some(); | | 4 | + | | 5 | + match target.as_str() { | | 6 | + "linux" if capi_enabled => { | | 7 | + println!("cargo:rustc-link-arg-bin=rustpython=-Wl,--export-dynamic"); | 10 | 8 | } | 11 | | - res.compile() | 12 | | - .map_err(|e| { | 13 | | - println!("cargo:warning=Failed to compile Windows resources: {e}"); | 14 | | - }) | 15 | | - .ok(); | | 9 | + "macos" if capi_enabled => { | | 10 | + println!("cargo:rustc-link-arg-bin=rustpython=-Wl,-export_dynamic"); | | 11 | + } | | 12 | + "windows" => { | | 13 | + println!("cargo:rerun-if-changed=logo.ico"); | | 14 | + let mut res = winresource::WindowsResource::new(); | | 15 | + if std::path::Path::new("logo.ico").exists() { | | 16 | + res.set_icon("logo.ico"); | | 17 | + } else { | | 18 | + println!("cargo:warning=logo.ico not found, skipping icon embedding"); | | 19 | + return; | | 20 | + } | | 21 | + res.compile() | | 22 | + .map_err(|e| { | | 23 | + println!("cargo:warning=Failed to compile Windows resources: {e}"); | | 24 | + }) | | 25 | + .ok(); | | 26 | + } | | 27 | + _ => {} | 16 | 28 | } | 17 | 29 | } |
Collapse file Copy file name to clipboard +3Lines changed: 3 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +[env] | | 2 | +PYO3_CONFIG_FILE = { value = "pyo3-rustpython.config", relative = true } | | 3 | +PYO3_NO_PYTHON = { value = "1" } |
Collapse file Copy file name to clipboard +26Lines changed: 26 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +[package] | | 2 | +name = "rustpython-capi" | | 3 | +description = "Minimal CPython C-API compatibility exports for RustPython" | | 4 | +version.workspace = true | | 5 | +authors.workspace = true | | 6 | +edition.workspace = true | | 7 | +rust-version.workspace = true | | 8 | +repository.workspace = true | | 9 | +license.workspace = true | | 10 | + | | 11 | +[lib] | | 12 | +crate-type = ["cdylib", "rlib"] | | 13 | + | | 14 | +[dependencies] | | 15 | +rustpython-vm = { workspace = true, features = ["threading"] } | | 16 | +rustpython-stdlib = {workspace = true, features = ["threading"] } | | 17 | + | | 18 | +[dev-dependencies] | | 19 | +pyo3 = { version = "0.28", features = ["auto-initialize", "abi3"] } | | 20 | + | | 21 | +[lints] | | 22 | +workspace = true | | 23 | + | | 24 | +[package.metadata.cargo-shear] | | 25 | +# Not a direct dependency (yet), but we need to enable threading support in the stdlib. | | 26 | +ignored = ["rustpython-stdlib"] |
Collapse file Copy file name to clipboard +5Lines changed: 5 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +implementation=CPython | | 2 | +version=3.14 | | 3 | +shared=true | | 4 | +abi3=true | | 5 | +suppress_build_script_link_lines=true |
Collapse file Copy file name to clipboard +19Lines changed: 19 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +#![allow(clippy::missing_safety_doc)] | | 2 | + | | 3 | +use crate::pylifecycle::MAIN_INTERP; | | 4 | +use rustpython_vm::Interpreter; | | 5 | +pub use rustpython_vm::PyObject; | | 6 | +use std::sync::MutexGuard; | | 7 | + | | 8 | +extern crate alloc; | | 9 | + | | 10 | +pub mod pylifecycle; | | 11 | +pub mod pystate; | | 12 | +pub mod refcount; | | 13 | + | | 14 | +/// Get main interpreter of this process. Will be None if it has not been initialized yet. | | 15 | +pub fn get_main_interpreter() -> MutexGuard<'static, Option<Interpreter>> { | | 16 | + MAIN_INTERP | | 17 | + .lock() | | 18 | + .expect("Failed to lock interpreter mutex") | | 19 | +} |
Collapse file Copy file name to clipboard +51Lines changed: 51 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +use crate::get_main_interpreter; | | 2 | +use crate::pystate::ensure_thread_has_vm_attached; | | 3 | +use core::ffi::c_int; | | 4 | +use rustpython_vm::Interpreter; | | 5 | +use rustpython_vm::vm::thread::ThreadedVirtualMachine; | | 6 | +use std::sync::Mutex; | | 7 | + | | 8 | +pub(crate) static MAIN_INTERP: Mutex<Option<Interpreter>> = Mutex::new(None); | | 9 | + | | 10 | +/// Request a thread local vm from the main interpreter | | 11 | +pub(crate) fn request_vm_from_interpreter() -> ThreadedVirtualMachine { | | 12 | + get_main_interpreter() | | 13 | + .as_ref() | | 14 | + .expect("Interpreter not initialized") | | 15 | + .enter(|vm| vm.new_thread()) | | 16 | +} | | 17 | + | | 18 | +#[unsafe(no_mangle)] | | 19 | +pub extern "C" fn Py_IsInitialized() -> c_int { | | 20 | + get_main_interpreter().is_some() as c_int | | 21 | +} | | 22 | + | | 23 | +#[unsafe(no_mangle)] | | 24 | +pub extern "C" fn Py_Initialize() { | | 25 | + Py_InitializeEx(0); | | 26 | +} | | 27 | + | | 28 | +#[unsafe(no_mangle)] | | 29 | +pub extern "C" fn Py_InitializeEx(_initsigs: c_int) { | | 30 | + let mut interp = get_main_interpreter(); | | 31 | + if interp.is_none() { | | 32 | + *interp = Interpreter::with_init(Default::default(), |_vm| {}).into(); | | 33 | + drop(interp); | | 34 | + ensure_thread_has_vm_attached(); | | 35 | + } | | 36 | +} | | 37 | + | | 38 | +#[unsafe(no_mangle)] | | 39 | +pub extern "C" fn Py_Finalize() { | | 40 | + let _ = Py_FinalizeEx(); | | 41 | +} | | 42 | + | | 43 | +#[unsafe(no_mangle)] | | 44 | +pub extern "C" fn Py_FinalizeEx() -> c_int { | | 45 | + 0 | | 46 | +} | | 47 | + | | 48 | +#[unsafe(no_mangle)] | | 49 | +pub extern "C" fn Py_IsFinalizing() -> c_int { | | 50 | + 0 | | 51 | +} |
|