From e711c812de799b1f47c4452cefe75e0ccea9a662 Mon Sep 17 00:00:00 2001 From: Giovanni Barillari Date: Mon, 2 Dec 2024 23:47:28 +0100 Subject: [PATCH] Upgrade to PyO3 0.23 (#454) * Upgrade to PyO3 0.23 * Drop Python 3.8 support --- .github/workflows/build.yml | 12 ++++----- .github/workflows/release.yml | 12 ++++----- .github/workflows/test.yml | 3 --- Cargo.lock | 24 ++++++++--------- Cargo.toml | 4 +-- README.md | 2 +- pyproject.toml | 3 +-- src/asgi/callbacks.rs | 12 ++++----- src/asgi/io.rs | 47 +++++++++++++++++--------------- src/asgi/utils.rs | 29 +++++++++----------- src/asyncio.rs | 2 +- src/callbacks.rs | 16 +++++------ src/conversion.rs | 18 +++++-------- src/rsgi/callbacks.rs | 12 ++++----- src/rsgi/io.rs | 46 +++++++++++++++++++++----------- src/rsgi/mod.rs | 4 +-- src/rsgi/types.rs | 10 +++---- src/runtime.rs | 50 ++++++++++++++++------------------- src/tcp.rs | 11 ++++---- src/utils.rs | 2 +- src/wsgi/callbacks.rs | 6 ++--- src/wsgi/types.rs | 6 ++--- 22 files changed, 168 insertions(+), 163 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e2f9c11c..006a055d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,7 +4,7 @@ on: workflow_dispatch env: MATURIN_VERSION: 1.7.4 - PY_ALL: 3.8 3.9 3.10 3.11 3.12 3.13 pypy3.8 pypy3.9 pypy3.10 + PY_ALL: 3.9 3.10 3.11 3.12 3.13 pypy3.9 pypy3.10 jobs: wheels: @@ -23,13 +23,13 @@ jobs: platform: linux target: x86_64 manylinux: auto - interpreter: pypy3.8 pypy3.9 pypy3.10 + interpreter: pypy3.9 pypy3.10 - os: macos target: x86_64 - interpreter: pypy3.8 pypy3.9 pypy3.10 + interpreter: pypy3.9 pypy3.10 - os: macos target: aarch64 - interpreter: 3.8 3.9 pypy3.8 pypy3.9 pypy3.10 + interpreter: 3.9 pypy3.9 pypy3.10 - os: ubuntu platform: linux target: aarch64 @@ -44,7 +44,7 @@ jobs: manylinux: musllinux_1_1 - os: windows target: x86_64 - interpreter: pypy3.8 pypy3.9 pypy3.10 + interpreter: pypy3.9 pypy3.10 exclude: - os: windows target: aarch64 @@ -84,7 +84,7 @@ jobs: matrix: os: [ubuntu-latest, macos-latest, macos-14, windows-latest] manylinux: [auto] - interpreter: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] + interpreter: ["3.9", "3.10", "3.11", "3.12", "3.13"] include: - os: ubuntu-latest platform: linux diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f2d30443..47863c3d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,7 +7,7 @@ on: env: MATURIN_VERSION: 1.7.4 - PY_ALL: 3.8 3.9 3.10 3.11 3.12 3.13 pypy3.8 pypy3.9 pypy3.10 + PY_ALL: 3.9 3.10 3.11 3.12 3.13 pypy3.9 pypy3.10 jobs: sdist: @@ -46,13 +46,13 @@ jobs: platform: linux target: x86_64 manylinux: auto - interpreter: pypy3.8 pypy3.9 pypy3.10 + interpreter: pypy3.9 pypy3.10 - os: macos target: x86_64 - interpreter: pypy3.8 pypy3.9 pypy3.10 + interpreter: pypy3.9 pypy3.10 - os: macos target: aarch64 - interpreter: pypy3.8 pypy3.9 pypy3.10 + interpreter: pypy3.9 pypy3.10 - os: ubuntu platform: linux target: aarch64 @@ -67,7 +67,7 @@ jobs: manylinux: musllinux_1_1 - os: windows target: x86_64 - interpreter: pypy3.8 pypy3.9 pypy3.10 + interpreter: pypy3.9 pypy3.10 exclude: - os: windows target: aarch64 @@ -102,7 +102,7 @@ jobs: matrix: os: [ubuntu-latest, macos-13, macos-14, windows-latest] manylinux: [auto] - interpreter: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"] + interpreter: ["3.9", "3.10", "3.11", "3.12", "3.13"] include: - os: ubuntu-latest platform: linux diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index bf84996a..f939fc9d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,7 +19,6 @@ jobs: fail-fast: false matrix: python-version: - - '3.8' - '3.9' - '3.10' - '3.11' @@ -50,7 +49,6 @@ jobs: fail-fast: false matrix: python-version: - - '3.8' - '3.9' - '3.10' - '3.11' @@ -81,7 +79,6 @@ jobs: fail-fast: false matrix: python-version: - - '3.8' - '3.9' - '3.10' - '3.11' diff --git a/Cargo.lock b/Cargo.lock index ce9d6b98..2c1ee71a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -970,9 +970,9 @@ dependencies = [ [[package]] name = "pyo3" -version = "0.22.6" +version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f402062616ab18202ae8319da13fa4279883a2b8a9d9f83f20dbade813ce1884" +checksum = "f54b3d09cbdd1f8c20650b28e7b09e338881482f4aa908a5f61a00c98fba2690" dependencies = [ "anyhow", "cfg-if", @@ -989,9 +989,9 @@ dependencies = [ [[package]] name = "pyo3-build-config" -version = "0.22.6" +version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b14b5775b5ff446dd1056212d778012cbe8a0fbffd368029fd9e25b514479c38" +checksum = "3015cf985888fe66cfb63ce0e321c603706cd541b7aec7ddd35c281390af45d8" dependencies = [ "once_cell", "python3-dll-a", @@ -1000,9 +1000,9 @@ dependencies = [ [[package]] name = "pyo3-ffi" -version = "0.22.6" +version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ab5bcf04a2cdcbb50c7d6105de943f543f9ed92af55818fd17b660390fc8636" +checksum = "6fca7cd8fd809b5ac4eefb89c1f98f7a7651d3739dfb341ca6980090f554c270" dependencies = [ "libc", "pyo3-build-config", @@ -1010,9 +1010,9 @@ dependencies = [ [[package]] name = "pyo3-log" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ac84e6eec1159bc2a575c9ae6723baa6ee9d45873e9bebad1e3ad7e8d28a443" +checksum = "3eb421dc86d38d08e04b927b02424db480be71b777fa3a56f32e2f2a3a1a3b08" dependencies = [ "arc-swap", "log", @@ -1021,9 +1021,9 @@ dependencies = [ [[package]] name = "pyo3-macros" -version = "0.22.6" +version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fd24d897903a9e6d80b968368a34e1525aeb719d568dba8b3d4bfa5dc67d453" +checksum = "34e657fa5379a79151b6ff5328d9216a84f55dc93b17b08e7c3609a969b73aa0" dependencies = [ "proc-macro2", "pyo3-macros-backend", @@ -1033,9 +1033,9 @@ dependencies = [ [[package]] name = "pyo3-macros-backend" -version = "0.22.6" +version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36c011a03ba1e50152b4b394b479826cad97e7a21eb52df179cd91ac411cbfbe" +checksum = "295548d5ffd95fd1981d2d3cf4458831b21d60af046b729b6fd143b0ba7aee2f" dependencies = [ "heck", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index 349c28a9..09f7903c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,8 +43,8 @@ pem = "=3.0" percent-encoding = "=2.3" pin-project = "1.1" pkcs8 = { version = "=0.10", features = ["encryption", "pkcs5"] } -pyo3 = { version = "=0.22", features = ["anyhow", "extension-module", "generate-import-lib"] } -pyo3-log = "=0.11" +pyo3 = { version = "=0.23", features = ["anyhow", "extension-module", "generate-import-lib"] } +pyo3-log = "=0.12" rustls-pemfile = "2.2" socket2 = { version = "0.5", features = ["all"] } tls-listener = { version = "=0.10", features = ["rustls"] } diff --git a/README.md b/README.md index 087f68d1..1ad65609 100644 --- a/README.md +++ b/README.md @@ -277,7 +277,7 @@ You might test the effect such optimizations cause over your application and dec Granian is currently under active development. -Granian is compatible with Python 3.8 and above versions. +Granian is compatible with Python 3.9 and above versions. ## License diff --git a/pyproject.toml b/pyproject.toml index fb080770..55aaadc0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,6 @@ classifiers = [ 'Operating System :: Microsoft :: Windows', 'Operating System :: POSIX :: Linux', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', @@ -32,7 +31,7 @@ dynamic = [ 'version', ] -requires-python = '>=3.8' +requires-python = '>=3.9' dependencies = [ 'click>=8.0.0', 'uvloop>=0.18.0; sys_platform != "win32" and platform_python_implementation == "CPython"', diff --git a/src/asgi/callbacks.rs b/src/asgi/callbacks.rs index d2cc696e..e437017a 100644 --- a/src/asgi/callbacks.rs +++ b/src/asgi/callbacks.rs @@ -43,14 +43,14 @@ pub(crate) struct CallbackWatcherHTTP { #[pyo3(get)] proto: Py, #[pyo3(get)] - scope: PyObject, + scope: Py, } impl CallbackWatcherHTTP { pub fn new(py: Python, proto: HTTPProtocol, scope: Bound) -> Self { Self { proto: Py::new(py, proto).unwrap(), - scope: scope.into_py(py), + scope: scope.unbind(), } } } @@ -62,7 +62,7 @@ impl CallbackWatcherHTTP { } fn err(&self, err: Bound) { - callback_impl_done_err!(self, &PyErr::from_value_bound(err)); + callback_impl_done_err!(self, &PyErr::from_value(err)); } } @@ -71,14 +71,14 @@ pub(crate) struct CallbackWatcherWebsocket { #[pyo3(get)] proto: Py, #[pyo3(get)] - scope: PyObject, + scope: Py, } impl CallbackWatcherWebsocket { pub fn new(py: Python, proto: WebsocketProtocol, scope: Bound) -> Self { Self { proto: Py::new(py, proto).unwrap(), - scope: scope.into_py(py), + scope: scope.unbind(), } } } @@ -90,7 +90,7 @@ impl CallbackWatcherWebsocket { } fn err(&self, err: Bound) { - callback_impl_done_err!(self, &PyErr::from_value_bound(err)); + callback_impl_done_err!(self, &PyErr::from_value(err)); } } diff --git a/src/asgi/io.rs b/src/asgi/io.rs index f0ccaf16..a4a112f5 100644 --- a/src/asgi/io.rs +++ b/src/asgi/io.rs @@ -87,6 +87,8 @@ impl ASGIHTTPProtocol { close: bool, ) -> PyResult> { let flow_hld = self.flow_tx_waiter.clone(); + let pynone = py.None(); + future_into_py_futlike(self.rt.clone(), py, async move { match tx.send(Ok(body.into())).await { Ok(()) => { @@ -99,7 +101,7 @@ impl ASGIHTTPProtocol { flow_hld.notify_one(); } } - Ok(()) + Ok(pynone) }) } @@ -116,9 +118,9 @@ impl ASGIHTTPProtocol { return future_into_py_futlike(self.rt.clone(), py, async move { let () = flow_hld.notified().await; Python::with_gil(|py| { - let dict = PyDict::new_bound(py); + let dict = PyDict::new(py); dict.set_item(pyo3::intern!(py, "type"), pyo3::intern!(py, "http.disconnect"))?; - Ok(dict.to_object(py)) + Ok(dict.into_any().unbind()) }) }); } @@ -144,18 +146,18 @@ impl ASGIHTTPProtocol { match chunk { Ok(data) => Python::with_gil(|py| { - let dict = PyDict::new_bound(py); + let dict = PyDict::new(py); dict.set_item(pyo3::intern!(py, "type"), pyo3::intern!(py, "http.request"))?; dict.set_item(pyo3::intern!(py, "body"), BytesToPy(data))?; dict.set_item(pyo3::intern!(py, "more_body"), more_body)?; - Ok(dict.to_object(py)) + Ok(dict.into_any().unbind()) }), _ => { flow_hld.notify_one(); Python::with_gil(|py| { - let dict = PyDict::new_bound(py); + let dict = PyDict::new(py); dict.set_item(pyo3::intern!(py, "type"), pyo3::intern!(py, "http.disconnect"))?; - Ok(dict.to_object(py)) + Ok(dict.into_any().unbind()) }) } } @@ -323,6 +325,7 @@ impl ASGIWebsocketProtocol { let accepted = self.accepted.clone(); let rx = self.ws_rx.clone(); let tx = self.ws_tx.clone(); + let pynone = py.None(); future_into_py_iter(self.rt.clone(), py, async move { if let Some(mut upgrade) = upgrade { @@ -339,7 +342,7 @@ impl ASGIWebsocketProtocol { *wtx = Some(tx); *wrx = Some(rx); accepted.store(true, atomic::Ordering::Relaxed); - return Ok(()); + return Ok(pynone); } } } @@ -352,15 +355,16 @@ impl ASGIWebsocketProtocol { fn send_message<'p>(&self, py: Python<'p>, data: Message) -> PyResult> { let transport = self.ws_tx.clone(); let closed = self.closed.clone(); + let pynone = py.None(); future_into_py_futlike(self.rt.clone(), py, async move { if let Some(ws) = &mut *(transport.lock().await) { match ws.send(data).await { - Ok(()) => return Ok(()), + Ok(()) => return Ok(pynone), _ => { if closed.load(atomic::Ordering::Relaxed) { log::info!("Attempted to write to a closed websocket"); - return Ok(()); + return Ok(pynone); } } }; @@ -374,6 +378,7 @@ impl ASGIWebsocketProtocol { let closed = self.closed.clone(); let ws_rx = self.ws_rx.clone(); let ws_tx = self.ws_tx.clone(); + let pynone = py.None(); future_into_py_iter(self.rt.clone(), py, async move { if let Some(tx) = ws_tx.lock().await.take() { @@ -382,7 +387,7 @@ impl ASGIWebsocketProtocol { .close() .await; } - Ok(()) + Ok(pynone) }) } @@ -416,9 +421,9 @@ impl ASGIWebsocketProtocol { let accepted = accepted.load(atomic::Ordering::Relaxed); if !accepted { return Python::with_gil(|py| { - let dict = PyDict::new_bound(py); + let dict = PyDict::new(py); dict.set_item(pyo3::intern!(py, "type"), pyo3::intern!(py, "websocket.connect"))?; - Ok(dict.to_object(py)) + Ok(dict.into_any().unbind()) }); } @@ -444,7 +449,7 @@ impl ASGIWebsocketProtocol { Ok(ASGIMessageType::WSAccept(subproto)) => self.accept(py, subproto), Ok(ASGIMessageType::WSClose) => self.close(py), Ok(ASGIMessageType::WSMessage(message)) => self.send_message(py, message), - _ => future_into_py_iter::<_, _, PyErr>(self.rt.clone(), py, async { error_message!() }), + _ => future_into_py_iter::<_, _>(self.rt.clone(), py, async { error_message!() }), } } } @@ -549,26 +554,26 @@ fn ws_message_into_rs(py: Python, message: &Bound) -> PyResult fn ws_message_into_py(message: Message) -> PyResult { match message { Message::Binary(message) => Python::with_gil(|py| { - let dict = PyDict::new_bound(py); + let dict = PyDict::new(py); dict.set_item(pyo3::intern!(py, "type"), pyo3::intern!(py, "websocket.receive"))?; - dict.set_item(pyo3::intern!(py, "bytes"), PyBytes::new_bound(py, &message[..]))?; - Ok(dict.to_object(py)) + dict.set_item(pyo3::intern!(py, "bytes"), PyBytes::new(py, &message[..]))?; + Ok(dict.into_any().unbind()) }), Message::Text(message) => Python::with_gil(|py| { - let dict = PyDict::new_bound(py); + let dict = PyDict::new(py); dict.set_item(pyo3::intern!(py, "type"), pyo3::intern!(py, "websocket.receive"))?; dict.set_item(pyo3::intern!(py, "text"), message)?; - Ok(dict.to_object(py)) + Ok(dict.into_any().unbind()) }), Message::Close(frame) => Python::with_gil(|py| { let close_code: u16 = match frame { Some(frame) => frame.code.into(), _ => 1005, }; - let dict = PyDict::new_bound(py); + let dict = PyDict::new(py); dict.set_item(pyo3::intern!(py, "type"), pyo3::intern!(py, "websocket.disconnect"))?; dict.set_item(pyo3::intern!(py, "code"), close_code)?; - Ok(dict.to_object(py)) + Ok(dict.into_any().unbind()) }), v => { log::warn!("Unsupported websocket message received {:?}", v); diff --git a/src/asgi/utils.rs b/src/asgi/utils.rs index 0c8f2e12..35618920 100644 --- a/src/asgi/utils.rs +++ b/src/asgi/utils.rs @@ -42,13 +42,13 @@ pub(super) fn build_scope<'p>( path: &'p str, query_string: &'p str, ) -> PyResult> { - let scope = PyDict::new_bound(py); + let scope = PyDict::new(py); scope.set_item( pyo3::intern!(py, "asgi"), ASGI_VERSION .get_or_try_init(py, || { - let rv = PyDict::new_bound(py); + let rv = PyDict::new(py); rv.set_item("version", "3.0")?; rv.set_item("spec_version", "2.3")?; Ok::(rv.into()) @@ -59,8 +59,8 @@ pub(super) fn build_scope<'p>( pyo3::intern!(py, "extensions"), ASGI_EXTENSIONS .get_or_try_init(py, || { - let rv = PyDict::new_bound(py); - rv.set_item("http.response.pathsend", PyDict::new_bound(py))?; + let rv = PyDict::new(py); + rv.set_item("http.response.pathsend", PyDict::new(py))?; Ok::(rv.into()) })? .bind(py), @@ -71,25 +71,22 @@ pub(super) fn build_scope<'p>( scope.set_item(pyo3::intern!(py, "client"), client)?; scope.set_item(pyo3::intern!(py, "scheme"), scheme)?; scope.set_item(pyo3::intern!(py, "path"), path)?; - scope.set_item(pyo3::intern!(py, "raw_path"), PyBytes::new_bound(py, path.as_bytes()))?; + scope.set_item(pyo3::intern!(py, "raw_path"), PyBytes::new(py, path.as_bytes()))?; scope.set_item( pyo3::intern!(py, "query_string"), - PyBytes::new_bound(py, query_string.as_bytes()), + PyBytes::new(py, query_string.as_bytes()), )?; - let headers = PyList::empty_bound(py); + let headers = PyList::empty(py); for (key, value) in &req.headers { headers.append(( - PyBytes::new_bound(py, key.as_str().as_bytes()), - PyBytes::new_bound(py, value.as_bytes()), + PyBytes::new(py, key.as_str().as_bytes()), + PyBytes::new(py, value.as_bytes()), ))?; } if !req.headers.contains_key(header::HOST) { let host = req.uri.authority().map_or("", Authority::as_str); - headers.insert( - 0, - (PyBytes::new_bound(py, b"host"), PyBytes::new_bound(py, host.as_bytes())), - )?; + headers.insert(0, (PyBytes::new(py, b"host"), PyBytes::new(py, host.as_bytes())))?; } scope.set_item(pyo3::intern!(py, "headers"), headers)?; @@ -136,14 +133,14 @@ pub(super) fn build_scope_ws<'p>( )?; scope.set_item( pyo3::intern!(py, "subprotocols"), - PyList::new_bound( + PyList::new( py, req.headers .get_all("Sec-WebSocket-Protocol") .iter() - .map(|v| PyString::new_bound(py, v.to_str().unwrap())) + .map(|v| PyString::new(py, v.to_str().unwrap())) .collect::>>(), - ), + )?, )?; Ok(scope) } diff --git a/src/asyncio.rs b/src/asyncio.rs index f6b1b38a..3376c8fc 100644 --- a/src/asyncio.rs +++ b/src/asyncio.rs @@ -6,7 +6,7 @@ static CONTEXT: GILOnceCell = GILOnceCell::new(); fn contextvars(py: Python) -> PyResult<&Bound> { Ok(CONTEXTVARS - .get_or_try_init(py, || py.import_bound("contextvars").map(Into::into))? + .get_or_try_init(py, || py.import("contextvars").map(Into::into))? .bind(py)) } diff --git a/src/callbacks.rs b/src/callbacks.rs index 2944fbff..d83583b6 100644 --- a/src/callbacks.rs +++ b/src/callbacks.rs @@ -78,9 +78,9 @@ impl PyIterAwaitable { } } - pub(crate) fn set_result(&self, result: PyResult>) { + pub(crate) fn set_result(&self, result: PyResult) { let mut res = self.result.write().unwrap(); - *res = Some(Python::with_gil(|py| result.map(|v| v.into_py(py)))); + *res = Some(result); } } @@ -138,17 +138,17 @@ impl PyFutureAwaitable { Ok((Py::new(py, self)?, cancel_tx)) } - pub(crate) fn set_result(&self, result: PyResult>, aw: Py) { + pub(crate) fn set_result(&self, result: PyResult, aw: Py) { Python::with_gil(|py| { let mut state = self.state.write().unwrap(); if !matches!(&mut *state, PyFutureAwaitableState::Pending) { return; } - *state = PyFutureAwaitableState::Completed(result.map(|v| v.into_py(py))); + *state = PyFutureAwaitableState::Completed(result); let ack = self.ack.read().unwrap(); if let Some((cb, ctx)) = &*ack { - let _ = self.event_loop.clone_ref(py).call_method_bound( + let _ = self.event_loop.clone_ref(py).call_method( py, pyo3::intern!(py, "call_soon_threadsafe"), (cb, aw), @@ -198,7 +198,7 @@ impl PyFutureAwaitable { #[pyo3(signature = (cb, context=None))] fn add_done_callback(pyself: PyRef<'_, Self>, cb: PyObject, context: Option) -> PyResult<()> { let py = pyself.py(); - let kwctx = pyo3::types::PyDict::new_bound(py); + let kwctx = pyo3::types::PyDict::new(py); kwctx.set_item(pyo3::intern!(py, "context"), context)?; let state = pyself.state.read().unwrap(); @@ -211,7 +211,7 @@ impl PyFutureAwaitable { _ => { drop(state); let event_loop = pyself.event_loop.clone_ref(py); - event_loop.call_method_bound(py, pyo3::intern!(py, "call_soon"), (cb, pyself), Some(&kwctx))?; + event_loop.call_method(py, pyo3::intern!(py, "call_soon"), (cb, pyself), Some(&kwctx))?; Ok(()) } } @@ -244,7 +244,7 @@ impl PyFutureAwaitable { drop(ack); drop(state); - let _ = event_loop.call_method_bound(py, pyo3::intern!(py, "call_soon"), (cb, pyself), Some(ctx.bind(py))); + let _ = event_loop.call_method(py, pyo3::intern!(py, "call_soon"), (cb, pyself), Some(ctx.bind(py))); } true diff --git a/src/conversion.rs b/src/conversion.rs index 56c18844..875dbdcf 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -1,20 +1,16 @@ -use pyo3::prelude::*; +use pyo3::{prelude::*, IntoPyObjectExt}; use crate::workers::{HTTP1Config, HTTP2Config}; pub(crate) struct BytesToPy(pub hyper::body::Bytes); -impl IntoPy for BytesToPy { - #[inline] - fn into_py(self, py: Python) -> PyObject { - self.0.as_ref().into_py(py) - } -} +impl<'p> IntoPyObject<'p> for BytesToPy { + type Target = PyAny; + type Output = Bound<'p, Self::Target>; + type Error = PyErr; -impl ToPyObject for BytesToPy { - #[inline] - fn to_object(&self, py: Python<'_>) -> PyObject { - self.0.as_ref().into_py(py) + fn into_pyobject(self, py: Python<'p>) -> Result { + self.0.as_ref().into_bound_py_any(py) } } diff --git a/src/rsgi/callbacks.rs b/src/rsgi/callbacks.rs index b67081b7..94a97c74 100644 --- a/src/rsgi/callbacks.rs +++ b/src/rsgi/callbacks.rs @@ -38,14 +38,14 @@ pub(crate) struct CallbackWatcherHTTP { #[pyo3(get)] proto: Py, #[pyo3(get)] - scope: PyObject, + scope: Py, } impl CallbackWatcherHTTP { pub fn new(py: Python, proto: HTTPProtocol, scope: HTTPScope) -> Self { Self { proto: Py::new(py, proto).unwrap(), - scope: scope.into_py(py), + scope: Py::new(py, scope).unwrap(), } } } @@ -57,7 +57,7 @@ impl CallbackWatcherHTTP { } fn err(&self, err: Bound) { - callback_impl_done_err!(self, &PyErr::from_value_bound(err)); + callback_impl_done_err!(self, &PyErr::from_value(err)); } } @@ -66,14 +66,14 @@ pub(crate) struct CallbackWatcherWebsocket { #[pyo3(get)] proto: Py, #[pyo3(get)] - scope: PyObject, + scope: Py, } impl CallbackWatcherWebsocket { pub fn new(py: Python, proto: WebsocketProtocol, scope: WebsocketScope) -> Self { Self { proto: Py::new(py, proto).unwrap(), - scope: scope.into_py(py), + scope: Py::new(py, scope).unwrap(), } } } @@ -85,7 +85,7 @@ impl CallbackWatcherWebsocket { } fn err(&self, err: Bound) { - callback_impl_done_err!(self, &PyErr::from_value_bound(err)); + callback_impl_done_err!(self, &PyErr::from_value(err)); } } diff --git a/src/rsgi/io.rs b/src/rsgi/io.rs index cd813ea1..cbf0f3a4 100644 --- a/src/rsgi/io.rs +++ b/src/rsgi/io.rs @@ -1,9 +1,12 @@ use futures::{sink::SinkExt, StreamExt, TryStreamExt}; use http_body_util::BodyExt; use hyper::body; -use pyo3::prelude::*; -use pyo3::pybacked::PyBackedStr; -use pyo3::types::{PyBytes, PyString}; +use pyo3::{ + prelude::*, + pybacked::PyBackedStr, + types::{PyBytes, PyString}, + IntoPyObjectExt, +}; use std::{ borrow::Cow, sync::{atomic, Arc, Mutex, RwLock}, @@ -40,9 +43,11 @@ impl RSGIHTTPStreamTransport { fn send_bytes<'p>(&self, py: Python<'p>, data: Cow<[u8]>) -> PyResult> { let transport = self.tx.clone(); let bdata: Box<[u8]> = data.into(); + let pynone = py.None(); + future_into_py_futlike(self.rt.clone(), py, async move { match transport.send(Ok(body::Bytes::from(bdata))).await { - Ok(()) => Ok(()), + Ok(()) => Ok(pynone), _ => error_stream!(), } }) @@ -50,9 +55,11 @@ impl RSGIHTTPStreamTransport { fn send_str<'p>(&self, py: Python<'p>, data: String) -> PyResult> { let transport = self.tx.clone(); + let pynone = py.None(); + future_into_py_futlike(self.rt.clone(), py, async move { match transport.send(Ok(body::Bytes::from(data))).await { - Ok(()) => Ok(()), + Ok(()) => Ok(pynone), _ => error_stream!(), } }) @@ -88,7 +95,10 @@ impl RSGIHTTPProtocol { if let Some(body) = self.body.lock().unwrap().take() { return future_into_py_iter(self.rt.clone(), py, async move { match body.collect().await { - Ok(data) => Ok(BytesToPy(data.to_bytes())), + Ok(data) => { + let bytes = BytesToPy(data.to_bytes()); + Ok(Python::with_gil(|py| bytes.into_py_any(py))?) + } _ => error_stream!(), } }); @@ -125,7 +135,7 @@ impl RSGIHTTPProtocol { BytesToPy(body::Bytes::new()) } }; - Ok(bytes) + Python::with_gil(|py| bytes.into_py_any(py)) }) } @@ -240,10 +250,12 @@ impl RSGIWebsocketTransport { fn send_bytes<'p>(&self, py: Python<'p>, data: Cow<[u8]>) -> PyResult> { let transport = self.tx.clone(); let bdata: Box<[u8]> = data.into(); + let pynone = py.None(); + future_into_py_iter(self.rt.clone(), py, async move { if let Ok(mut stream) = transport.try_lock() { return match stream.send(bdata[..].into()).await { - Ok(()) => Ok(()), + Ok(()) => Ok(pynone), _ => error_stream!(), }; } @@ -253,10 +265,12 @@ impl RSGIWebsocketTransport { fn send_str<'p>(&self, py: Python<'p>, data: String) -> PyResult> { let transport = self.tx.clone(); + let pynone = py.None(); + future_into_py_iter(self.rt.clone(), py, async move { if let Ok(mut stream) = transport.try_lock() { return match stream.send(Message::Text(data)).await { - Ok(()) => Ok(()), + Ok(()) => Ok(pynone), _ => error_stream!(), }; } @@ -379,7 +393,7 @@ impl RSGIWebsocketProtocol { Ok(Python::with_gil(|py| { let pytransport = Py::new(py, RSGIWebsocketTransport::new(rth, stream)).unwrap(); *trx = Some(pytransport.clone_ref(py)); - pytransport + pytransport.into_any() })) } _ => error_proto!(), @@ -394,12 +408,14 @@ impl RSGIWebsocketProtocol { fn message_into_py(message: Message) -> PyResult { match message { Message::Binary(message) => Ok(Python::with_gil(|py| { - WebsocketInboundBytesMessage::new(PyBytes::new_bound(py, &message).unbind()).into_py(py) - })), + WebsocketInboundBytesMessage::new(PyBytes::new(py, &message).unbind()).into_py_any(py) + })?), Message::Text(message) => Ok(Python::with_gil(|py| { - WebsocketInboundTextMessage::new(PyString::new_bound(py, &message).unbind()).into_py(py) - })), - Message::Close(_) => Ok(Python::with_gil(|py| WebsocketInboundCloseMessage::new().into_py(py))), + WebsocketInboundTextMessage::new(PyString::new(py, &message).unbind()).into_py_any(py) + })?), + Message::Close(_) => Ok(Python::with_gil(|py| { + WebsocketInboundCloseMessage::new().into_py_any(py) + })?), v => { log::warn!("Unsupported websocket message received {:?}", v); error_proto!() diff --git a/src/rsgi/mod.rs b/src/rsgi/mod.rs index e27bf4d0..6c18eed2 100644 --- a/src/rsgi/mod.rs +++ b/src/rsgi/mod.rs @@ -8,8 +8,8 @@ pub(crate) mod serve; mod types; pub(crate) fn init_pymodule(py: Python, module: &Bound) -> PyResult<()> { - module.add("RSGIProtocolError", py.get_type_bound::())?; - module.add("RSGIProtocolClosed", py.get_type_bound::())?; + module.add("RSGIProtocolError", py.get_type::())?; + module.add("RSGIProtocolClosed", py.get_type::())?; module.add_class::()?; module.add_class::()?; module.add_class::()?; diff --git a/src/rsgi/types.rs b/src/rsgi/types.rs index 49d86bd0..0f38863e 100644 --- a/src/rsgi/types.rs +++ b/src/rsgi/types.rs @@ -68,7 +68,7 @@ impl RSGIHeaders { } fn __iter__<'p>(&self, py: Python<'p>) -> PyResult> { - PyIterator::from_bound_object(&PyList::new_bound(py, self.keys())) + PyIterator::from_object(PyList::new(py, self.keys())?.as_any()) } fn __len__(&self) -> usize { @@ -79,7 +79,7 @@ impl RSGIHeaders { fn get(&self, py: Python, key: &str, default: Option) -> Option { match self.inner.get(key) { Some(val) => match val.to_str() { - Ok(string) => Some(PyString::new_bound(py, string).into()), + Ok(string) => Some(PyString::new(py, string).into()), _ => default, }, _ => default, @@ -87,13 +87,13 @@ impl RSGIHeaders { } #[pyo3(signature = (key))] - fn get_all<'p>(&self, py: Python<'p>, key: &'p str) -> Bound<'p, PyList> { - PyList::new_bound( + fn get_all<'p>(&self, py: Python<'p>, key: &'p str) -> PyResult> { + PyList::new( py, self.inner .get_all(key) .iter() - .map(|v| PyString::new_bound(py, v.to_str().unwrap())) + .map(|v| PyString::new(py, v.to_str().unwrap())) .collect::>>(), ) } diff --git a/src/runtime.rs b/src/runtime.rs index df785b99..aff85c12 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -1,4 +1,6 @@ use pyo3::prelude::*; +#[cfg(windows)] +use pyo3::IntoPyObjectExt; use std::{ future::Future, sync::{Arc, Mutex}, @@ -141,11 +143,10 @@ pub(crate) fn init_runtime_st(blocking_threads: usize, py_loop: Arc) - // but for "quick" operations it's something like 12% faster. #[allow(unused_must_use)] #[cfg(not(target_os = "linux"))] -pub(crate) fn future_into_py_iter(rt: R, py: Python, fut: F) -> PyResult> +pub(crate) fn future_into_py_iter(rt: R, py: Python, fut: F) -> PyResult> where R: Runtime + ContextExt + Clone, - F: Future> + Send + 'static, - T: IntoPy + Send + 'static, + F: Future> + Send + 'static, { let aw = Py::new(py, PyIterAwaitable::new())?; let py_fut = aw.clone_ref(py); @@ -168,11 +169,10 @@ where // MacOS works best with original impl, Windows still needs further analysis. #[cfg(target_os = "linux")] #[inline(always)] -pub(crate) fn future_into_py_iter(rt: R, py: Python, fut: F) -> PyResult> +pub(crate) fn future_into_py_iter(rt: R, py: Python, fut: F) -> PyResult> where R: Runtime + ContextExt + Clone, - F: Future> + Send + 'static, - T: IntoPy + Send + 'static, + F: Future> + Send + 'static, { future_into_py_futlike(rt, py, fut) } @@ -184,11 +184,10 @@ where // and for "long" operations it's something like 6% faster than `future_into_py_iter`. #[allow(unused_must_use)] #[cfg(unix)] -pub(crate) fn future_into_py_futlike(rt: R, py: Python, fut: F) -> PyResult> +pub(crate) fn future_into_py_futlike(rt: R, py: Python, fut: F) -> PyResult> where R: Runtime + ContextExt + Clone, - F: Future> + Send + 'static, - T: IntoPy + Send + 'static, + F: Future> + Send + 'static, { let event_loop = rt.py_event_loop(py); let (aw, cancel_tx) = PyFutureAwaitable::new(event_loop).to_spawn(py)?; @@ -220,11 +219,10 @@ where #[allow(unused_must_use)] #[cfg(windows)] -pub(crate) fn future_into_py_futlike(rt: R, py: Python, fut: F) -> PyResult> +pub(crate) fn future_into_py_futlike(rt: R, py: Python, fut: F) -> PyResult> where R: Runtime + ContextExt + Clone, - F: Future> + Send + 'static, - T: IntoPy + Send + 'static, + F: Future> + Send + 'static, { let event_loop = rt.py_event_loop(py); let event_loop_ref = event_loop.clone_ref(py); @@ -247,8 +245,8 @@ where let _ = rb.run(move || { Python::with_gil(|py| { let (cb, value) = match result { - Ok(val) => (fut_ref.getattr(py, pyo3::intern!(py, "set_result")).unwrap(), val.into_py(py)), - Err(err) => (fut_ref.getattr(py, pyo3::intern!(py, "set_exception")).unwrap(), err.into_py(py)) + Ok(val) => (fut_ref.getattr(py, pyo3::intern!(py, "set_result")).unwrap(), val), + Err(err) => (fut_ref.getattr(py, pyo3::intern!(py, "set_exception")).unwrap(), err.into_py_any(py).unwrap()) }; let _ = event_loop_ref.call_method1(py, pyo3::intern!(py, "call_soon_threadsafe"), (PyFutureResultSetter, cb, value)); drop(fut_ref); @@ -258,7 +256,7 @@ where }, () = cancel_tx.notified() => { let _ = rb.run(move || { - Python::with_gil(|py| { + Python::with_gil(|_| { drop(fut_ref); drop(event_loop_ref); }); @@ -273,35 +271,33 @@ where #[allow(clippy::unnecessary_wraps)] #[inline(always)] pub(crate) fn empty_future_into_py(py: Python) -> PyResult> { - Ok(PyEmptyAwaitable.into_py(py).into_bound(py)) + Ok(PyEmptyAwaitable.into_pyobject(py)?.into_any()) } #[allow(unused_must_use)] -pub(crate) fn run_until_complete(rt: R, event_loop: Bound, fut: F) -> PyResult +pub(crate) fn run_until_complete(rt: R, event_loop: Bound, fut: F) -> PyResult<()> where R: Runtime + ContextExt + Clone, - F: Future> + Send + 'static, - T: Send + Sync + 'static, + F: Future> + Send + 'static, { - let py = event_loop.py(); let result_tx = Arc::new(Mutex::new(None)); let result_rx = Arc::clone(&result_tx); let py_fut = event_loop.call_method0("create_future")?; - let loop_tx = event_loop.clone().into_py(py); - let future_tx = py_fut.clone().into_py(py); + let loop_tx = event_loop.clone().unbind(); + let future_tx = py_fut.clone().unbind(); rt.spawn(async move { - let val = fut.await; + let _ = fut.await; if let Ok(mut result) = result_tx.lock() { - *result = Some(val.unwrap()); + *result = Some(()); } // NOTE: we don't care if we block the runtime. // `run_until_complete` is used only for the workers main loop. Python::with_gil(move |py| { let res_method = future_tx.getattr(py, "set_result").unwrap(); - let _ = loop_tx.call_method_bound(py, "call_soon_threadsafe", (res_method, py.None()), None); + let _ = loop_tx.call_method(py, "call_soon_threadsafe", (res_method, py.None()), None); drop(future_tx); drop(loop_tx); }); @@ -309,8 +305,8 @@ where event_loop.call_method1("run_until_complete", (py_fut,))?; - let result = result_rx.lock().unwrap().take().unwrap(); - Ok(result) + result_rx.lock().unwrap().take().unwrap(); + Ok(()) } pub(crate) fn block_on_local(rt: &RuntimeWrapper, local: LocalSet, fut: F) diff --git a/src/tcp.rs b/src/tcp.rs index f604966a..dd62f582 100644 --- a/src/tcp.rs +++ b/src/tcp.rs @@ -1,5 +1,4 @@ -use pyo3::prelude::*; -use pyo3::types::PyType; +use pyo3::{prelude::*, types::PyType, IntoPyObjectExt}; use std::net::{IpAddr, SocketAddr, TcpListener}; #[cfg(unix)] @@ -53,23 +52,23 @@ impl ListenerHolder { #[cfg(unix)] pub fn __getstate__(&self, py: Python) -> PyObject { let fd = self.socket.as_raw_fd(); - (fd.into_py(py),).to_object(py) + (fd,).into_py_any(py).unwrap() } #[cfg(windows)] pub fn __getstate__(&self, py: Python) -> PyObject { let fd = self.socket.as_raw_socket(); - (fd.into_py(py),).to_object(py) + (fd,).into_py_any(py).unwrap() } #[cfg(unix)] pub fn get_fd(&self, py: Python) -> PyObject { - self.socket.as_raw_fd().into_py(py).to_object(py) + self.socket.as_raw_fd().into_py_any(py).unwrap() } #[cfg(windows)] pub fn get_fd(&self, py: Python) -> PyObject { - self.socket.as_raw_socket().into_py(py).to_object(py) + self.socket.as_raw_socket().into_py_any(py).unwrap() } } diff --git a/src/utils.rs b/src/utils.rs index 711c95e1..4ad10e8a 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -43,7 +43,7 @@ fn trim_end(data: &[u8]) -> &[u8] { #[inline] pub(crate) fn log_application_callable_exception(err: &pyo3::PyErr) { let tb = pyo3::Python::with_gil(|py| { - let tb = match err.traceback_bound(py).map(|t| t.format()) { + let tb = match err.traceback(py).map(|t| t.format()) { Some(Ok(tb)) => tb, _ => String::new(), }; diff --git a/src/wsgi/callbacks.rs b/src/wsgi/callbacks.rs index 49ca894f..5946292a 100644 --- a/src/wsgi/callbacks.rs +++ b/src/wsgi/callbacks.rs @@ -67,7 +67,7 @@ fn run_callback( let _ = Python::with_gil(|py| -> PyResult<()> { let proto = Py::new(py, WSGIProtocol::new(tx))?; let callback = cbs.get().cb.clone_ref(py); - let environ = PyDict::new_bound(py); + let environ = PyDict::new(py); environ.set_item(pyo3::intern!(py, "SERVER_PROTOCOL"), version)?; environ.set_item(pyo3::intern!(py, "SERVER_NAME"), server.0)?; environ.set_item(pyo3::intern!(py, "SERVER_PORT"), server.1)?; @@ -75,7 +75,7 @@ fn run_callback( environ.set_item(pyo3::intern!(py, "REQUEST_METHOD"), parts.method.as_str())?; environ.set_item( pyo3::intern!(py, "PATH_INFO"), - PyBytes::new_bound(py, &path).call_method1(pyo3::intern!(py, "decode"), (pyo3::intern!(py, "latin1"),))?, + PyBytes::new(py, &path).call_method1(pyo3::intern!(py, "decode"), (pyo3::intern!(py, "latin1"),))?, )?; environ.set_item(pyo3::intern!(py, "QUERY_STRING"), query_string)?; environ.set_item(pyo3::intern!(py, "wsgi.url_scheme"), scheme)?; @@ -92,7 +92,7 @@ fn run_callback( content_len.to_str().unwrap_or_default(), )?; } - environ.update(headers.into_py_dict_bound(py).as_mapping())?; + environ.update(headers.into_py_dict(py).unwrap().as_mapping())?; if let Err(err) = callback.call1(py, (proto.clone_ref(py), environ)) { log_application_callable_exception(&err); diff --git a/src/wsgi/types.rs b/src/wsgi/types.rs index deeb567b..9a26233f 100644 --- a/src/wsgi/types.rs +++ b/src/wsgi/types.rs @@ -147,7 +147,7 @@ impl WSGIBody { } #[pyo3(signature = (_hint=None))] - fn readlines<'p>(&self, py: Python<'p>, _hint: Option) -> Bound<'p, PyList> { + fn readlines<'p>(&self, py: Python<'p>, _hint: Option) -> PyResult> { let inner = self.inner.clone(); let data = py.allow_threads(|| { self.rt.inner.block_on(async move { @@ -159,8 +159,8 @@ impl WSGIBody { }); let lines: Vec> = data .split(|&c| c == LINE_SPLIT) - .map(|item| PyBytes::new_bound(py, item)) + .map(|item| PyBytes::new(py, item)) .collect(); - PyList::new_bound(py, lines) + PyList::new(py, lines) } }