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
# Documentation
mkdocs==1.4.3
+import sys
import typing
+from contextlib import contextmanager
import anyio
+from anyio.abc import ObjectReceiveStream, ObjectSendStream
from starlette.background import BackgroundTask
from starlette.requests import ClientDisconnect, Request
from starlette.responses import ContentStream, Response, StreamingResponse
from starlette.types import ASGIApp, Message, Receive, Scope, Send
+if sys.version_info < (3, 11): # pragma: no cover
+ from exceptiongroup import BaseExceptionGroup
+
RequestResponseEndpoint = typing.Callable[[Request], typing.Awaitable[Response]]
DispatchFunction = typing.Callable[
[Request, RequestResponseEndpoint], typing.Awaitable[Response]
T = typing.TypeVar("T")
+@contextmanager
+def _convert_excgroups() -> typing.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
+
+
class _CachedRequest(Request):
"""
If the user calls Request.body() from their dispatch function
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:
response.raw_headers = message["headers"]
return response
- async with anyio.create_task_group() as task_group:
- response = await self.dispatch_func(request, call_next)
- await response(scope, wrapped_receive, send)
- response_sent.set()
+ with _convert_excgroups():
+ async with anyio.create_task_group() as task_group:
+ response = await self.dispatch_func(request, call_next)
+ await response(scope, wrapped_receive, send)
+ response_sent.set()
async def dispatch(
self, request: Request, call_next: RequestResponseEndpoint
import warnings
import anyio
+from anyio.abc import ObjectReceiveStream, ObjectSendStream
from starlette.types import Receive, Scope, Send
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
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
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)
from starlette.middleware.wsgi import WSGIMiddleware, build_environ
+if sys.version_info < (3, 11): # pragma: no cover
+ from exceptiongroup import ExceptionGroup
+
def hello_world(environ, start_response):
status = "200 OK"
# 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(ExceptionGroup) as exc:
client.get("/")
+ assert len(exc.value.exceptions) == 1
+ assert isinstance(exc.value.exceptions[0], RuntimeError)
+
def test_wsgi_exc_info(test_client_factory):
# Note that we're testing the WSGI app directly here.
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
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):