diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 77d6997..9ba7c32 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,6 +22,7 @@ jobs: - '3.12' - '3.13' - '3.13t' + - pypy3.11 env: UV_PYTHON: ${{ matrix.python-version }} diff --git a/granian/server/common.py b/granian/server/common.py index 15f41c8..de0cc1c 100644 --- a/granian/server/common.py +++ b/granian/server/common.py @@ -205,7 +205,7 @@ class AbstractServer(Generic[WT]): return # uneeded? ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) - ctx.load_cert_chain(cert, key, password) + ctx.load_cert_chain(str(cert.resolve()), str(key.resolve()), password) #: build ctx if client_verify and not ca: logger.warning('SSL client verification requires a CA certificate, ignoring') @@ -216,7 +216,7 @@ class AbstractServer(Generic[WT]): str(key.resolve()), password, str(ca.resolve()) if ca else None, - [item.resolve() for item in crl], + [str(item.resolve()) for item in crl], client_verify, ) diff --git a/src/asyncio.rs b/src/asyncio.rs index 021cd34..ecff13e 100644 --- a/src/asyncio.rs +++ b/src/asyncio.rs @@ -22,6 +22,8 @@ pub(crate) fn empty_context(py: Python) -> PyResult<&Bound> { .bind(py)) } +#[cfg(not(PyPy))] +#[inline(always)] pub(crate) fn copy_context(py: Python) -> PyObject { let ctx = unsafe { let ptr = pyo3::ffi::PyContext_CopyCurrent(); @@ -29,3 +31,9 @@ pub(crate) fn copy_context(py: Python) -> PyObject { }; ctx.unbind() } + +#[cfg(PyPy)] +#[inline(always)] +pub(crate) fn copy_context(py: Python) -> PyObject { + contextvars(py).unwrap().call_method0("copy_context").unwrap().unbind() +} diff --git a/src/callbacks.rs b/src/callbacks.rs index 6b72d81..2be4eb4 100644 --- a/src/callbacks.rs +++ b/src/callbacks.rs @@ -233,6 +233,7 @@ impl CallbackScheduler { self.schedule_fn.set(val).unwrap(); } + #[cfg(not(PyPy))] fn _run(pyself: Py, py: Python, coro: PyObject) { let ctx = copy_context(py); let state = Arc::new(CallbackSchedulerState { @@ -251,6 +252,19 @@ impl CallbackScheduler { pyo3::ffi::PyContext_Exit(ctx.as_ptr()); } } + + #[cfg(PyPy)] + fn _run(pyself: Py, py: Python, coro: PyObject) { + let ctx = copy_context(py); + let state = Arc::new(CallbackSchedulerState { + sched: pyself.clone_ref(py), + coro, + ctx: ctx.clone_ref(py), + }); + + let step = Py::new(py, CallbackSchedulerStep { state }).unwrap(); + _ = ctx.call_method1(py, pyo3::intern!(py, "run"), (step,)); + } } pub(crate) struct CallbackSchedulerState { diff --git a/tests/test_https.py b/tests/test_https.py index dd2f86d..1212835 100644 --- a/tests/test_https.py +++ b/tests/test_https.py @@ -24,7 +24,7 @@ async def test_http_scope(server_tls, runtime_mode): async def test_asgi_ws_scope(asgi_server, runtime_mode): ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) localhost_pem = pathlib.Path.cwd() / 'tests' / 'fixtures' / 'tls' / 'cert.pem' - ssl_context.load_verify_locations(localhost_pem) + ssl_context.load_verify_locations(str(localhost_pem)) async with asgi_server(runtime_mode, tls=True) as port: async with websockets.connect(f'wss://localhost:{port}/ws_info?test=true', ssl=ssl_context) as ws: @@ -39,7 +39,7 @@ async def test_asgi_ws_scope(asgi_server, runtime_mode): async def test_rsgi_ws_scope(rsgi_server, runtime_mode): ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) localhost_pem = pathlib.Path.cwd() / 'tests' / 'fixtures' / 'tls' / 'cert.pem' - ssl_context.load_verify_locations(localhost_pem) + ssl_context.load_verify_locations(str(localhost_pem)) async with rsgi_server(runtime_mode, tls=True) as port: async with websockets.connect(f'wss://localhost:{port}/ws_info?test=true', ssl=ssl_context) as ws: diff --git a/tests/test_rsgi.py b/tests/test_rsgi.py index fde14d2..55e10ac 100644 --- a/tests/test_rsgi.py +++ b/tests/test_rsgi.py @@ -1,4 +1,5 @@ import os +import platform import httpx import pytest @@ -49,6 +50,7 @@ async def test_body_large(rsgi_server, runtime_mode): @pytest.mark.asyncio +@pytest.mark.skipif(platform.python_implementation() == 'PyPy', reason='RSGI stream broken on PyPy') @pytest.mark.parametrize('runtime_mode', ['mt', 'st']) async def test_body_stream_req(rsgi_server, runtime_mode): data = ''.join([f'{idx}test'.zfill(8) for idx in range(0, 5000)]) @@ -60,6 +62,7 @@ async def test_body_stream_req(rsgi_server, runtime_mode): @pytest.mark.asyncio +@pytest.mark.skipif(platform.python_implementation() == 'PyPy', reason='RSGI stream broken on PyPy') @pytest.mark.parametrize('runtime_mode', ['mt', 'st']) async def test_body_stream_res(rsgi_server, runtime_mode): async with rsgi_server(runtime_mode) as port: