Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/starlette/_utils.py: 53%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1from __future__ import annotations
3import functools
4import sys
5from collections.abc import Awaitable, Generator
6from contextlib import AbstractAsyncContextManager, contextmanager
7from typing import Any, Callable, Generic, Protocol, TypeVar, overload
9from starlette.types import Scope
11if sys.version_info >= (3, 13): # pragma: no cover
12 from inspect import iscoroutinefunction
13 from typing import TypeIs
14else: # pragma: no cover
15 from asyncio import iscoroutinefunction
17 from typing_extensions import TypeIs
19has_exceptiongroups = True
20if sys.version_info < (3, 11): # pragma: no cover
21 try:
22 from exceptiongroup import BaseExceptionGroup # type: ignore[unused-ignore,import-not-found]
23 except ImportError:
24 has_exceptiongroups = False
26T = TypeVar("T")
27AwaitableCallable = Callable[..., Awaitable[T]]
30@overload
31def is_async_callable(obj: AwaitableCallable[T]) -> TypeIs[AwaitableCallable[T]]: ...
34@overload
35def is_async_callable(obj: Any) -> TypeIs[AwaitableCallable[Any]]: ...
38def is_async_callable(obj: Any) -> Any:
39 while isinstance(obj, functools.partial):
40 obj = obj.func
42 return iscoroutinefunction(obj) or (callable(obj) and iscoroutinefunction(obj.__call__))
45T_co = TypeVar("T_co", covariant=True)
48class AwaitableOrContextManager(Awaitable[T_co], AbstractAsyncContextManager[T_co], Protocol[T_co]): ...
51class SupportsAsyncClose(Protocol):
52 async def close(self) -> None: ... # pragma: no cover
55SupportsAsyncCloseType = TypeVar("SupportsAsyncCloseType", bound=SupportsAsyncClose, covariant=False)
58class AwaitableOrContextManagerWrapper(Generic[SupportsAsyncCloseType]):
59 __slots__ = ("aw", "entered")
61 def __init__(self, aw: Awaitable[SupportsAsyncCloseType]) -> None:
62 self.aw = aw
64 def __await__(self) -> Generator[Any, None, SupportsAsyncCloseType]:
65 return self.aw.__await__()
67 async def __aenter__(self) -> SupportsAsyncCloseType:
68 self.entered = await self.aw
69 return self.entered
71 async def __aexit__(self, *args: Any) -> None | bool:
72 await self.entered.close()
73 return None
76@contextmanager
77def collapse_excgroups() -> Generator[None, None, None]:
78 try:
79 yield
80 except BaseException as exc:
81 if has_exceptiongroups: # pragma: no cover
82 while isinstance(exc, BaseExceptionGroup) and len(exc.exceptions) == 1:
83 exc = exc.exceptions[0]
85 raise exc
88def get_route_path(scope: Scope) -> str:
89 path: str = scope["path"]
90 root_path = scope.get("root_path", "")
91 if not root_path:
92 return path
94 if not path.startswith(root_path):
95 return path
97 if path == root_path:
98 return ""
100 if path[len(root_path)] == "/":
101 return path[len(root_path) :]
103 return path