diff --git a/Cargo.lock b/Cargo.lock index dfa9263..9be044b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -681,9 +681,9 @@ dependencies = [ [[package]] name = "pyo3" -version = "0.25.1" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8970a78afe0628a3e3430376fc5fd76b6b45c4d43360ffd6cdd40bdde72b682a" +checksum = "7ba0117f4212101ee6544044dae45abe1083d30ce7b29c4b5cbdfa2354e07383" dependencies = [ "indoc", "libc", @@ -698,9 +698,9 @@ dependencies = [ [[package]] name = "pyo3-async-runtimes" -version = "0.25.0" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d73cc6b1b7d8b3cef02101d37390dbdfe7e450dfea14921cae80a9534ba59ef2" +checksum = "e6ee6d4cb3e8d5b925f5cdb38da183e0ff18122eb2048d4041c9e7034d026e23" dependencies = [ "futures", "once_cell", @@ -711,19 +711,18 @@ dependencies = [ [[package]] name = "pyo3-build-config" -version = "0.25.1" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "458eb0c55e7ece017adeba38f2248ff3ac615e53660d7c71a238d7d2a01c7598" +checksum = "4fc6ddaf24947d12a9aa31ac65431fb1b851b8f4365426e182901eabfb87df5f" dependencies = [ - "once_cell", "target-lexicon", ] [[package]] name = "pyo3-ffi" -version = "0.25.1" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7114fe5457c61b276ab77c5055f206295b812608083644a5c5b2640c3102565c" +checksum = "025474d3928738efb38ac36d4744a74a400c901c7596199e20e45d98eb194105" dependencies = [ "libc", "pyo3-build-config", @@ -731,9 +730,9 @@ dependencies = [ [[package]] name = "pyo3-macros" -version = "0.25.1" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8725c0a622b374d6cb051d11a0983786448f7785336139c3c94f5aa6bef7e50" +checksum = "2e64eb489f22fe1c95911b77c44cc41e7c19f3082fc81cce90f657cdc42ffded" dependencies = [ "proc-macro2", "pyo3-macros-backend", @@ -743,9 +742,9 @@ dependencies = [ [[package]] name = "pyo3-macros-backend" -version = "0.25.1" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4109984c22491085343c05b0dbc54ddc405c3cf7b4374fc533f5c3313a572ccc" +checksum = "100246c0ecf400b475341b8455a9213344569af29a3c841d29270e53102e0fcf" dependencies = [ "heck", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index ea607d7..5f5e25d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,8 +31,8 @@ http-rewriter = { git = "ssh://git@github.com/platformatic/http-rewriter" } # Default enable napi4 feature, see https://nodejs.org/api/n-api.html#node-api-version-matrix napi = { version = "3", default-features = false, features = ["napi4", "tokio_rt", "async"], optional = true } napi-derive = { version = "3", optional = true } -pyo3 = { version = "0.25.1", features = ["experimental-async"] } -pyo3-async-runtimes = { version = "0.25.0", features = ["tokio-runtime"] } +pyo3 = { version = "0.26.0", features = ["experimental-async"] } +pyo3-async-runtimes = { version = "0.26.0", features = ["tokio-runtime"] } thiserror = "2.0.12" tokio = { version = "1.45.1", features = ["full"] } libc = "0.2" diff --git a/src/asgi/http.rs b/src/asgi/http.rs index cfa7420..cb9de67 100644 --- a/src/asgi/http.rs +++ b/src/asgi/http.rs @@ -527,8 +527,8 @@ mod tests { #[test] fn test_http_connection_scope_into_pyobject() { - pyo3::prepare_freethreaded_python(); - Python::with_gil(|py| { + Python::initialize(); + Python::attach(|py| { let scope = HttpConnectionScope { http_version: HttpVersion::V1_1, method: HttpMethod::Get, @@ -577,8 +577,8 @@ mod tests { #[test] fn test_http_receive_message_into_pyobject() { - pyo3::prepare_freethreaded_python(); - Python::with_gil(|py| { + Python::initialize(); + Python::attach(|py| { let message = HttpReceiveMessage::Request { body: vec![1, 2, 3], more_body: true, @@ -596,8 +596,8 @@ mod tests { #[test] fn test_http_send_message_from_pyobject() { - pyo3::prepare_freethreaded_python(); - Python::with_gil(|py| { + Python::initialize(); + Python::attach(|py| { let dict = PyDict::new(py); dict.set_item("type", "http.response.start").unwrap(); dict.set_item("status", 200).unwrap(); diff --git a/src/asgi/http_method.rs b/src/asgi/http_method.rs index 55a9eda..35166bb 100644 --- a/src/asgi/http_method.rs +++ b/src/asgi/http_method.rs @@ -119,7 +119,7 @@ mod tests { #[test] fn test_http_method_pyobject_conversion() { - Python::with_gil(|py| { + Python::attach(|py| { let tests = vec![ (HttpMethod::Get, "GET"), (HttpMethod::Post, "POST"), diff --git a/src/asgi/http_version.rs b/src/asgi/http_version.rs index dc6a705..d5302a1 100644 --- a/src/asgi/http_version.rs +++ b/src/asgi/http_version.rs @@ -71,7 +71,7 @@ mod tests { #[test] fn test_http_version_pyobject_conversion() { - Python::with_gil(|py| { + Python::attach(|py| { let tests = vec![ (HttpVersion::V1_0, "1.0"), (HttpVersion::V1_1, "1.1"), diff --git a/src/asgi/info.rs b/src/asgi/info.rs index 55bbead..de07f6b 100644 --- a/src/asgi/info.rs +++ b/src/asgi/info.rs @@ -73,7 +73,7 @@ mod tests { #[test] fn test_asgi_info_pyobject_conversion() { - Python::with_gil(|py| { + Python::attach(|py| { let asgi_info = AsgiInfo::new("3.0", "2.5"); // Convert AsgiInfo to PyObject diff --git a/src/asgi/lifespan.rs b/src/asgi/lifespan.rs index cccb3ec..61c0490 100644 --- a/src/asgi/lifespan.rs +++ b/src/asgi/lifespan.rs @@ -107,7 +107,7 @@ mod tests { #[test] fn test_lifespan_scope_into_pyobject() { - Python::with_gil(|py| { + Python::attach(|py| { let lifespan_scope = LifespanScope { state: None }; let py_obj = lifespan_scope.into_pyobject(py).unwrap(); assert_eq!( @@ -129,7 +129,7 @@ mod tests { #[test] fn test_lifespan_receive_message_into_pyobject() { - Python::with_gil(|py| { + Python::attach(|py| { let message = LifespanReceiveMessage::LifespanStartup; let py_obj = message.into_pyobject(py).unwrap(); assert_eq!( @@ -148,7 +148,7 @@ mod tests { #[test] fn test_lifespan_send_message_from_pyobject() { - Python::with_gil(|py| { + Python::attach(|py| { let dict = PyDict::new(py); dict.set_item("type", "lifespan.shutdown.complete").unwrap(); let message: LifespanSendMessage = dict.extract().unwrap(); @@ -163,7 +163,7 @@ mod tests { #[test] fn test_lifespan_send_message_from_pyobject_error_cases() { - Python::with_gil(|py| { + Python::attach(|py| { // Test missing 'type' key let dict = PyDict::new(py); let result: Result = dict.extract(); @@ -261,7 +261,7 @@ mod tests { #[test] fn test_lifespan_scope_with_populated_state() { - Python::with_gil(|py| { + Python::attach(|py| { // Create a state dictionary with some data let state_dict = PyDict::new(py); state_dict.set_item("initialized", true).unwrap(); diff --git a/src/asgi/mod.rs b/src/asgi/mod.rs index 05b8a69..01aa427 100644 --- a/src/asgi/mod.rs +++ b/src/asgi/mod.rs @@ -62,12 +62,12 @@ pub use websocket::{ /// Handle to a shared Python event loop pub struct EventLoopHandle { - event_loop: PyObject, + event_loop: Py, } impl EventLoopHandle { /// Get the Python event loop object - pub fn event_loop(&self) -> &PyObject { + pub fn event_loop(&self) -> &Py { &self.event_loop } } @@ -75,7 +75,7 @@ impl EventLoopHandle { impl Drop for EventLoopHandle { fn drop(&mut self) { // Stop the Python event loop when the last handle is dropped - Python::with_gil(|py| { + Python::attach(|py| { if let Err(e) = self.event_loop.bind(py).call_method0("stop") { eprintln!("Failed to stop Python event loop: {e}"); } @@ -111,10 +111,10 @@ fn create_event_loop_handle() -> Result { ensure_python_symbols_global(); // Initialize Python if not already initialized - pyo3::prepare_freethreaded_python(); + Python::initialize(); // Create event loop - let event_loop = Python::with_gil(|py| -> Result { + let event_loop = Python::attach(|py| -> Result, HandlerError> { let asyncio = py.import("asyncio")?; let event_loop = asyncio.call_method0("new_event_loop")?; let event_loop_py = event_loop.unbind(); @@ -139,7 +139,7 @@ pub struct Asgi { // Shared Python event loop handle event_loop_handle: Arc, // ASGI app function - app_function: PyObject, + app_function: Py, } unsafe impl Send for Asgi {} @@ -160,7 +160,7 @@ impl Asgi { let event_loop_handle = ensure_python_event_loop()?; // Load Python app - let app_function = Python::with_gil(|py| -> Result { + let app_function = Python::attach(|py| -> Result, HandlerError> { // Load and compile Python module let entrypoint = docroot .join(format!("{}.py", target.file)) @@ -241,7 +241,7 @@ impl Handler for Asgi { tokio::spawn(collect_response_messages(tx_receiver, response_tx)); // Submit the ASGI app call to Python event loop - Python::with_gil(|py| { + Python::attach(|py| { let scope_py = scope.into_pyobject(py)?; let coro = self .app_function @@ -361,8 +361,8 @@ fn setup_python_paths(py: Python, docroot: &Path) -> PyResult<()> { } /// Start a Python thread that runs the event loop forever -fn start_python_event_loop_thread(event_loop: PyObject) { - Python::with_gil(|py| { +fn start_python_event_loop_thread(event_loop: Py) { + Python::attach(|py| { // Set the event loop for this thread and run it let asyncio = py.import("asyncio")?; asyncio.call_method1("set_event_loop", (event_loop.bind(py),))?; diff --git a/src/asgi/receiver.rs b/src/asgi/receiver.rs index c82b058..e8c9622 100644 --- a/src/asgi/receiver.rs +++ b/src/asgi/receiver.rs @@ -50,7 +50,7 @@ impl Receiver { ReceiverType::Http(rx) => { let message = rx.lock().await.recv().await; if let Some(msg) = message { - Python::with_gil(|py| Ok(msg.into_pyobject(py)?.unbind())) + Python::attach(|py| Ok(msg.into_pyobject(py)?.unbind())) } else { Err(PyValueError::new_err("No message received")) } @@ -58,7 +58,7 @@ impl Receiver { ReceiverType::WebSocket(rx) => { let message = rx.lock().await.recv().await; if let Some(msg) = message { - Python::with_gil(|py| Ok(msg.into_pyobject(py)?.unbind())) + Python::attach(|py| Ok(msg.into_pyobject(py)?.unbind())) } else { Err(PyValueError::new_err("No message received")) } @@ -66,7 +66,7 @@ impl Receiver { ReceiverType::Lifespan(rx) => { let message = rx.lock().await.recv().await; if let Some(msg) = message { - Python::with_gil(|py| Ok(msg.into_pyobject(py)?.unbind())) + Python::attach(|py| Ok(msg.into_pyobject(py)?.unbind())) } else { Err(PyValueError::new_err("No message received")) } diff --git a/src/asgi/sender.rs b/src/asgi/sender.rs index 3e8dd2f..602eeb6 100644 --- a/src/asgi/sender.rs +++ b/src/asgi/sender.rs @@ -52,12 +52,12 @@ impl Sender { #[pymethods] impl Sender { - async fn __call__(&mut self, args: Py) -> PyResult { + async fn __call__(&mut self, args: Py) -> PyResult> { // Create acknowledgment channel let (ack_tx, ack_rx) = oneshot::channel::<()>(); // Send message with acknowledgment channel - let send_result: PyResult<()> = Python::with_gil(|py| { + let send_result: PyResult<()> = Python::attach(|py| { let args_dict = args.bind(py); match &self.0 { SenderType::Http(tx) => { @@ -95,7 +95,7 @@ impl Sender { // Wait for acknowledgment match ack_rx.await { - Ok(()) => Python::with_gil(|py| Ok(py.None())), + Ok(()) => Python::attach(|py| Ok(py.None())), Err(_) => Err(PyValueError::new_err("message not acknowledged")), } } diff --git a/src/asgi/websocket.rs b/src/asgi/websocket.rs index 84a0d34..b2aa8fd 100644 --- a/src/asgi/websocket.rs +++ b/src/asgi/websocket.rs @@ -302,7 +302,7 @@ mod tests { #[test] fn test_websocket_connection_scope_into_pyobject() { - Python::with_gil(|py| { + Python::attach(|py| { let scope = WebSocketConnectionScope { http_version: HttpVersion::V2_0, scheme: "ws".to_string(), @@ -353,7 +353,7 @@ mod tests { #[test] fn test_websocket_receive_message_into_pyobject() { - Python::with_gil(|py| { + Python::attach(|py| { let connect_msg = WebSocketReceiveMessage::Connect; let dict = connect_msg.into_pyobject(py).unwrap(); assert_eq!( @@ -398,7 +398,7 @@ mod tests { #[test] fn test_websocket_send_message_from_pyobject() { - Python::with_gil(|py| { + Python::attach(|py| { let dict = PyDict::new(py); dict.set_item("type", "websocket.accept").unwrap(); dict.set_item("subprotocol", "chat").unwrap();