Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgraded to AnyIO 4.0 #2211

Merged
merged 25 commits into from
Jul 23, 2023
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
5c41699
Upgraded to AnyIO 4.0 and dropped Python 3.8 support
agronholm Jul 12, 2023
026ced3
Added conditional imports for ExceptionGroup
agronholm Jul 12, 2023
67e5a41
Fixed mypy errors
agronholm Jul 12, 2023
df70155
Fixed black errors
agronholm Jul 12, 2023
e42480f
Fixed last failure
agronholm Jul 13, 2023
b9f27e4
Added missing test dependency
agronholm Jul 13, 2023
9568e8e
Exclude "raise" in handle_assertion_error() from coverage
agronholm Jul 13, 2023
aa3874c
Exclude conditional import from coverage
agronholm Jul 13, 2023
6c71321
Simplified the changes
agronholm Jul 13, 2023
7225f96
Fixed linting errors
agronholm Jul 13, 2023
1e9096d
Dropped exceptiongroup as a test dependency
agronholm Jul 13, 2023
e1becdb
Restored compatibility with AnyIO 3.x
agronholm Jul 13, 2023
ac67df8
Fixed mypy errors
agronholm Jul 13, 2023
86fdcbf
Fixed linting errors
agronholm Jul 13, 2023
efe6cf5
Merge branch 'master' into anyio4
agronholm Jul 13, 2023
efef252
Merge branch 'master' into anyio4
agronholm Jul 13, 2023
9773d06
Use general ABCs for object stream type annotations for consistency
agronholm Jul 13, 2023
053b96b
Moved convert_excgroups() from tests to the main code base
agronholm Jul 22, 2023
68cc1a8
Updated anyio dependency to point to master
agronholm Jul 22, 2023
ef5f16a
Fixed linting error
agronholm Jul 22, 2023
cb6008b
Ignore coverage for a conditional import
agronholm Jul 22, 2023
fcb9056
Moved the exception group converter to middleware/base.py
agronholm Jul 22, 2023
c1454cf
Removed unnecessary imports
agronholm Jul 22, 2023
07ceb21
Fixed import errors
agronholm Jul 22, 2023
2dc718e
Merge branch 'master' into anyio4
Kludex Jul 23, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ types-contextvars==2.4.7.2
types-PyYAML==6.0.12.10
types-dataclasses==0.6.6
pytest==7.4.0
trio==0.21.0
trio==0.22.1
anyio@git+https://github.com/agronholm/anyio.git@detect-asyncio-native-cancel

# Documentation
mkdocs==1.4.3
Expand Down
3 changes: 3 additions & 0 deletions starlette/middleware/base.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import typing

import anyio
from anyio.abc import ObjectReceiveStream, ObjectSendStream

from starlette.background import BackgroundTask
from starlette.requests import ClientDisconnect, Request
Expand Down Expand Up @@ -107,6 +108,8 @@ async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:

async def call_next(request: Request) -> Response:
app_exc: typing.Optional[Exception] = None
send_stream: ObjectSendStream[typing.MutableMapping[str, typing.Any]]
recv_stream: ObjectReceiveStream[typing.MutableMapping[str, typing.Any]]
send_stream, recv_stream = anyio.create_memory_object_stream()

async def receive_or_disconnect() -> Message:
Expand Down
4 changes: 4 additions & 0 deletions starlette/middleware/wsgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import warnings

import anyio
from anyio.abc import ObjectReceiveStream, ObjectSendStream

from starlette.types import Receive, Scope, Send

Expand Down Expand Up @@ -72,6 +73,9 @@ async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:


class WSGIResponder:
stream_send: ObjectSendStream[typing.MutableMapping[str, typing.Any]]
stream_receive: ObjectReceiveStream[typing.MutableMapping[str, typing.Any]]

def __init__(self, app: typing.Callable, scope: Scope) -> None:
self.app = app
self.scope = scope
Expand Down
19 changes: 13 additions & 6 deletions starlette/testclient.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import anyio
import anyio.from_thread
from anyio.abc import ObjectReceiveStream, ObjectSendStream
from anyio.streams.stapled import StapledObjectStream

from starlette._utils import is_async_callable
Expand Down Expand Up @@ -737,12 +738,18 @@ def __enter__(self) -> "TestClient":
def reset_portal() -> None:
self.portal = None

self.stream_send = StapledObjectStream(
*anyio.create_memory_object_stream(math.inf)
)
self.stream_receive = StapledObjectStream(
*anyio.create_memory_object_stream(math.inf)
)
send1: ObjectSendStream[
typing.Optional[typing.MutableMapping[str, typing.Any]]
]
receive1: ObjectReceiveStream[
typing.Optional[typing.MutableMapping[str, typing.Any]]
]
send2: ObjectSendStream[typing.MutableMapping[str, typing.Any]]
receive2: ObjectReceiveStream[typing.MutableMapping[str, typing.Any]]
send1, receive1 = anyio.create_memory_object_stream(math.inf)
send2, receive2 = anyio.create_memory_object_stream(math.inf)
self.stream_send = StapledObjectStream(send1, receive1)
self.stream_receive = StapledObjectStream(send2, receive2)
self.task = portal.start_task_soon(self.lifespan)
portal.call(self.wait_startup)

Expand Down
19 changes: 19 additions & 0 deletions tests/exc_converter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from __future__ import annotations

import sys
from collections.abc import Generator
from contextlib import contextmanager

if sys.version_info < (3, 11): # pragma: no cover
from exceptiongroup import BaseExceptionGroup


@contextmanager
def convert_excgroups() -> Generator[None, None, None]:
try:
yield
except BaseException as exc:
while isinstance(exc, BaseExceptionGroup) and len(exc.exceptions) == 1:
exc = exc.exceptions[0]

raise exc
12 changes: 8 additions & 4 deletions tests/middleware/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
from starlette.testclient import TestClient
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer if we maintain the tests unchanged, and unpack the ExceptionGroup on the BaseHTTPMiddleware in case we have a single exception.

from starlette.types import ASGIApp, Message, Receive, Scope, Send

from ..exc_converter import convert_excgroups


class CustomMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request, call_next):
Expand Down Expand Up @@ -74,15 +76,15 @@ def test_custom_middleware(test_client_factory):
response = client.get("/")
assert response.headers["Custom-Header"] == "Example"

with pytest.raises(Exception) as ctx:
with pytest.raises(Exception) as ctx, convert_excgroups():
response = client.get("/exc")
assert str(ctx.value) == "Exc"

with pytest.raises(Exception) as ctx:
with pytest.raises(Exception) as ctx, convert_excgroups():
response = client.get("/exc-stream")
assert str(ctx.value) == "Faulty Stream"

with pytest.raises(RuntimeError):
with pytest.raises(RuntimeError), convert_excgroups():
response = client.get("/no-response")

with client.websocket_connect("/ws") as session:
Expand Down Expand Up @@ -210,7 +212,9 @@ async def homepage(request):
)

client = test_client_factory(app)
response = client.get("/")
with convert_excgroups():
response = client.get("/")

assert response.status_code == 200, response.content


Expand Down
4 changes: 3 additions & 1 deletion tests/middleware/test_wsgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

from starlette.middleware.wsgi import WSGIMiddleware, build_environ

from ..exc_converter import convert_excgroups


def hello_world(environ, start_response):
status = "200 OK"
Expand Down Expand Up @@ -66,7 +68,7 @@ def test_wsgi_exception(test_client_factory):
# The HTTP protocol implementations would catch this error and return 500.
app = WSGIMiddleware(raise_exception)
client = test_client_factory(app)
with pytest.raises(RuntimeError):
with pytest.raises(RuntimeError), convert_excgroups():
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can use the ExceptionGroup here. I'm fine with this break, since we intend to remove WSGIMiddleware.

client.get("/")


Expand Down
4 changes: 4 additions & 0 deletions tests/test_websockets.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import sys
from typing import Any, MutableMapping

import anyio
import pytest
from anyio.abc import ObjectReceiveStream, ObjectSendStream

from starlette import status
from starlette.types import Receive, Scope, Send
Expand Down Expand Up @@ -178,6 +180,8 @@ async def app(scope: Scope, receive: Receive, send: Send) -> None:


def test_websocket_concurrency_pattern(test_client_factory):
stream_send: ObjectSendStream[MutableMapping[str, Any]]
stream_receive: ObjectReceiveStream[MutableMapping[str, Any]]
stream_send, stream_receive = anyio.create_memory_object_stream()

async def reader(websocket):
Expand Down