Загрузить файлы в «venv/Lib/site-packages/starlette»
This commit is contained in:
1
venv/Lib/site-packages/starlette/__init__.py
Normal file
1
venv/Lib/site-packages/starlette/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
__version__ = "1.3.1"
|
||||||
65
venv/Lib/site-packages/starlette/_exception_handler.py
Normal file
65
venv/Lib/site-packages/starlette/_exception_handler.py
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from starlette._utils import is_async_callable
|
||||||
|
from starlette.concurrency import run_in_threadpool
|
||||||
|
from starlette.exceptions import HTTPException
|
||||||
|
from starlette.requests import Request
|
||||||
|
from starlette.types import ASGIApp, ExceptionHandler, Message, Receive, Scope, Send
|
||||||
|
from starlette.websockets import WebSocket
|
||||||
|
|
||||||
|
ExceptionHandlers = dict[Any, ExceptionHandler]
|
||||||
|
StatusHandlers = dict[int, ExceptionHandler]
|
||||||
|
|
||||||
|
|
||||||
|
def _lookup_exception_handler(exc_handlers: ExceptionHandlers, exc: Exception) -> ExceptionHandler | None:
|
||||||
|
for cls in type(exc).__mro__:
|
||||||
|
if cls in exc_handlers:
|
||||||
|
return exc_handlers[cls]
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def wrap_app_handling_exceptions(app: ASGIApp, conn: Request | WebSocket) -> ASGIApp:
|
||||||
|
exception_handlers: ExceptionHandlers
|
||||||
|
status_handlers: StatusHandlers
|
||||||
|
try:
|
||||||
|
exception_handlers, status_handlers = conn.scope["starlette.exception_handlers"]
|
||||||
|
except KeyError:
|
||||||
|
exception_handlers, status_handlers = {}, {}
|
||||||
|
|
||||||
|
async def wrapped_app(scope: Scope, receive: Receive, send: Send) -> None:
|
||||||
|
response_started = False
|
||||||
|
|
||||||
|
async def sender(message: Message) -> None:
|
||||||
|
nonlocal response_started
|
||||||
|
|
||||||
|
if message["type"] == "http.response.start":
|
||||||
|
response_started = True
|
||||||
|
await send(message)
|
||||||
|
|
||||||
|
try:
|
||||||
|
await app(scope, receive, sender)
|
||||||
|
except Exception as exc:
|
||||||
|
handler = None
|
||||||
|
|
||||||
|
if isinstance(exc, HTTPException):
|
||||||
|
handler = status_handlers.get(exc.status_code)
|
||||||
|
|
||||||
|
if handler is None:
|
||||||
|
handler = _lookup_exception_handler(exception_handlers, exc)
|
||||||
|
|
||||||
|
if handler is None:
|
||||||
|
raise exc
|
||||||
|
|
||||||
|
if response_started:
|
||||||
|
raise RuntimeError("Caught handled exception, but response already started.") from exc
|
||||||
|
|
||||||
|
if is_async_callable(handler):
|
||||||
|
response = await handler(conn, exc) # type: ignore[arg-type]
|
||||||
|
else:
|
||||||
|
response = await run_in_threadpool(handler, conn, exc) # type: ignore[arg-type]
|
||||||
|
if response is not None:
|
||||||
|
await response(scope, receive, sender)
|
||||||
|
|
||||||
|
return wrapped_app
|
||||||
111
venv/Lib/site-packages/starlette/_utils.py
Normal file
111
venv/Lib/site-packages/starlette/_utils.py
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import functools
|
||||||
|
import sys
|
||||||
|
from collections.abc import AsyncGenerator, Awaitable, Callable, Generator
|
||||||
|
from contextlib import AbstractAsyncContextManager, asynccontextmanager
|
||||||
|
from typing import Any, Generic, Protocol, TypeVar, overload
|
||||||
|
|
||||||
|
import anyio.abc
|
||||||
|
|
||||||
|
from starlette.types import Scope
|
||||||
|
|
||||||
|
if sys.version_info >= (3, 13): # pragma: no cover
|
||||||
|
from inspect import iscoroutinefunction
|
||||||
|
from typing import TypeIs
|
||||||
|
else: # pragma: no cover
|
||||||
|
from asyncio import iscoroutinefunction
|
||||||
|
|
||||||
|
from typing_extensions import TypeIs
|
||||||
|
|
||||||
|
if sys.version_info < (3, 11): # pragma: no cover
|
||||||
|
try:
|
||||||
|
from exceptiongroup import BaseExceptionGroup
|
||||||
|
except ImportError:
|
||||||
|
|
||||||
|
class BaseExceptionGroup(BaseException): # type: ignore[no-redef]
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
T = TypeVar("T")
|
||||||
|
AwaitableCallable = Callable[..., Awaitable[T]]
|
||||||
|
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def is_async_callable(obj: AwaitableCallable[T]) -> TypeIs[AwaitableCallable[T]]: ...
|
||||||
|
|
||||||
|
|
||||||
|
@overload
|
||||||
|
def is_async_callable(obj: Any) -> TypeIs[AwaitableCallable[Any]]: ...
|
||||||
|
|
||||||
|
|
||||||
|
def is_async_callable(obj: Any) -> Any:
|
||||||
|
while isinstance(obj, functools.partial):
|
||||||
|
obj = obj.func
|
||||||
|
|
||||||
|
return iscoroutinefunction(obj) or (callable(obj) and iscoroutinefunction(obj.__call__))
|
||||||
|
|
||||||
|
|
||||||
|
T_co = TypeVar("T_co", covariant=True)
|
||||||
|
|
||||||
|
|
||||||
|
class AwaitableOrContextManager(
|
||||||
|
Awaitable[T_co], AbstractAsyncContextManager[T_co], Protocol[T_co]
|
||||||
|
): ... # pragma: no branch
|
||||||
|
|
||||||
|
|
||||||
|
class SupportsAsyncClose(Protocol):
|
||||||
|
async def close(self) -> None: ... # pragma: no cover
|
||||||
|
|
||||||
|
|
||||||
|
SupportsAsyncCloseType = TypeVar("SupportsAsyncCloseType", bound=SupportsAsyncClose, covariant=False)
|
||||||
|
|
||||||
|
|
||||||
|
class AwaitableOrContextManagerWrapper(Generic[SupportsAsyncCloseType]):
|
||||||
|
__slots__ = ("aw", "entered")
|
||||||
|
|
||||||
|
def __init__(self, aw: Awaitable[SupportsAsyncCloseType]) -> None:
|
||||||
|
self.aw = aw
|
||||||
|
|
||||||
|
def __await__(self) -> Generator[Any, None, SupportsAsyncCloseType]:
|
||||||
|
return self.aw.__await__()
|
||||||
|
|
||||||
|
async def __aenter__(self) -> SupportsAsyncCloseType:
|
||||||
|
self.entered = await self.aw
|
||||||
|
return self.entered
|
||||||
|
|
||||||
|
async def __aexit__(self, *args: Any) -> None | bool:
|
||||||
|
await self.entered.close()
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
@asynccontextmanager
|
||||||
|
async def create_collapsing_task_group() -> AsyncGenerator[anyio.abc.TaskGroup, None]:
|
||||||
|
try:
|
||||||
|
async with anyio.create_task_group() as tg:
|
||||||
|
yield tg
|
||||||
|
except BaseExceptionGroup as excs:
|
||||||
|
if len(excs.exceptions) != 1:
|
||||||
|
raise
|
||||||
|
|
||||||
|
exc = excs.exceptions[0]
|
||||||
|
context = None if exc.__suppress_context__ else exc.__context__
|
||||||
|
raise exc from exc.__cause__ or context
|
||||||
|
|
||||||
|
|
||||||
|
def get_route_path(scope: Scope) -> str:
|
||||||
|
path: str = scope["path"]
|
||||||
|
root_path = scope.get("root_path", "")
|
||||||
|
if not root_path:
|
||||||
|
return path
|
||||||
|
|
||||||
|
if not path.startswith(root_path):
|
||||||
|
return path
|
||||||
|
|
||||||
|
if path == root_path:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
if path[len(root_path)] == "/":
|
||||||
|
return path[len(root_path) :]
|
||||||
|
|
||||||
|
return path
|
||||||
118
venv/Lib/site-packages/starlette/applications.py
Normal file
118
venv/Lib/site-packages/starlette/applications.py
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections.abc import Awaitable, Callable, Mapping, Sequence
|
||||||
|
from typing import Any, ParamSpec, TypeVar
|
||||||
|
|
||||||
|
from starlette.datastructures import State, URLPath
|
||||||
|
from starlette.middleware import Middleware, _MiddlewareFactory
|
||||||
|
from starlette.middleware.errors import ServerErrorMiddleware
|
||||||
|
from starlette.middleware.exceptions import ExceptionMiddleware
|
||||||
|
from starlette.requests import Request
|
||||||
|
from starlette.responses import Response
|
||||||
|
from starlette.routing import BaseRoute, Router
|
||||||
|
from starlette.types import ASGIApp, ExceptionHandler, Lifespan, Receive, Scope, Send
|
||||||
|
|
||||||
|
AppType = TypeVar("AppType", bound="Starlette")
|
||||||
|
P = ParamSpec("P")
|
||||||
|
|
||||||
|
|
||||||
|
class Starlette:
|
||||||
|
"""Creates an Starlette application."""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self: AppType,
|
||||||
|
debug: bool = False,
|
||||||
|
routes: Sequence[BaseRoute] | None = None,
|
||||||
|
middleware: Sequence[Middleware] | None = None,
|
||||||
|
exception_handlers: Mapping[Any, ExceptionHandler] | None = None,
|
||||||
|
lifespan: Lifespan[AppType] | None = None,
|
||||||
|
) -> None:
|
||||||
|
"""Initializes the application.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
debug: Boolean indicating if debug tracebacks should be returned on errors.
|
||||||
|
routes: A list of routes to serve incoming HTTP and WebSocket requests.
|
||||||
|
middleware: A list of middleware to run for every request. A starlette
|
||||||
|
application will always automatically include two middleware classes.
|
||||||
|
`ServerErrorMiddleware` is added as the very outermost middleware, to handle
|
||||||
|
any uncaught errors occurring anywhere in the entire stack.
|
||||||
|
`ExceptionMiddleware` is added as the very innermost middleware, to deal
|
||||||
|
with handled exception cases occurring in the routing or endpoints.
|
||||||
|
exception_handlers: A mapping of either integer status codes,
|
||||||
|
or exception class types onto callables which handle the exceptions.
|
||||||
|
Exception handler callables should be of the form
|
||||||
|
`handler(request, exc) -> response` and may be either standard functions, or
|
||||||
|
async functions.
|
||||||
|
lifespan: A lifespan context function, which can be used to perform
|
||||||
|
startup and shutdown tasks. This is a newer style that replaces the
|
||||||
|
`on_startup` and `on_shutdown` handlers. Use one or the other, not both.
|
||||||
|
"""
|
||||||
|
self.debug = debug
|
||||||
|
self.state = State()
|
||||||
|
self.router = Router(routes, lifespan=lifespan)
|
||||||
|
self.exception_handlers = {} if exception_handlers is None else dict(exception_handlers)
|
||||||
|
self.user_middleware = [] if middleware is None else list(middleware)
|
||||||
|
self.middleware_stack: ASGIApp | None = None
|
||||||
|
|
||||||
|
def build_middleware_stack(self) -> ASGIApp:
|
||||||
|
debug = self.debug
|
||||||
|
error_handler = None
|
||||||
|
exception_handlers: dict[Any, ExceptionHandler] = {}
|
||||||
|
|
||||||
|
for key, value in self.exception_handlers.items():
|
||||||
|
if key in (500, Exception):
|
||||||
|
error_handler = value
|
||||||
|
else:
|
||||||
|
exception_handlers[key] = value
|
||||||
|
|
||||||
|
middleware = (
|
||||||
|
[Middleware(ServerErrorMiddleware, handler=error_handler, debug=debug)]
|
||||||
|
+ self.user_middleware
|
||||||
|
+ [Middleware(ExceptionMiddleware, handlers=exception_handlers, debug=debug)]
|
||||||
|
)
|
||||||
|
|
||||||
|
app = self.router
|
||||||
|
for cls, args, kwargs in reversed(middleware):
|
||||||
|
app = cls(app, *args, **kwargs)
|
||||||
|
return app
|
||||||
|
|
||||||
|
@property
|
||||||
|
def routes(self) -> list[BaseRoute]:
|
||||||
|
return self.router.routes
|
||||||
|
|
||||||
|
def url_path_for(self, name: str, /, **path_params: Any) -> URLPath:
|
||||||
|
return self.router.url_path_for(name, **path_params)
|
||||||
|
|
||||||
|
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
|
||||||
|
scope["app"] = self
|
||||||
|
if self.middleware_stack is None:
|
||||||
|
self.middleware_stack = self.build_middleware_stack()
|
||||||
|
await self.middleware_stack(scope, receive, send)
|
||||||
|
|
||||||
|
def mount(self, path: str, app: ASGIApp, name: str | None = None) -> None:
|
||||||
|
self.router.mount(path, app=app, name=name) # pragma: no cover
|
||||||
|
|
||||||
|
def host(self, host: str, app: ASGIApp, name: str | None = None) -> None:
|
||||||
|
self.router.host(host, app=app, name=name) # pragma: no cover
|
||||||
|
|
||||||
|
def add_middleware(self, middleware_class: _MiddlewareFactory[P], *args: P.args, **kwargs: P.kwargs) -> None:
|
||||||
|
if self.middleware_stack is not None: # pragma: no cover
|
||||||
|
raise RuntimeError("Cannot add middleware after an application has started")
|
||||||
|
self.user_middleware.insert(0, Middleware(middleware_class, *args, **kwargs))
|
||||||
|
|
||||||
|
def add_exception_handler(
|
||||||
|
self,
|
||||||
|
exc_class_or_status_code: int | type[Exception],
|
||||||
|
handler: ExceptionHandler,
|
||||||
|
) -> None: # pragma: no cover
|
||||||
|
self.exception_handlers[exc_class_or_status_code] = handler
|
||||||
|
|
||||||
|
def add_route(
|
||||||
|
self,
|
||||||
|
path: str,
|
||||||
|
route: Callable[[Request], Awaitable[Response] | Response],
|
||||||
|
methods: list[str] | None = None,
|
||||||
|
name: str | None = None,
|
||||||
|
include_in_schema: bool = True,
|
||||||
|
) -> None: # pragma: no cover
|
||||||
|
self.router.add_route(path, route, methods=methods, name=name, include_in_schema=include_in_schema)
|
||||||
149
venv/Lib/site-packages/starlette/authentication.py
Normal file
149
venv/Lib/site-packages/starlette/authentication.py
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import functools
|
||||||
|
import inspect
|
||||||
|
from collections.abc import Callable, Sequence
|
||||||
|
from typing import Any, ParamSpec
|
||||||
|
from urllib.parse import urlencode
|
||||||
|
|
||||||
|
from starlette._utils import is_async_callable
|
||||||
|
from starlette.exceptions import HTTPException
|
||||||
|
from starlette.requests import HTTPConnection, Request
|
||||||
|
from starlette.responses import RedirectResponse
|
||||||
|
from starlette.websockets import WebSocket
|
||||||
|
|
||||||
|
_P = ParamSpec("_P")
|
||||||
|
|
||||||
|
|
||||||
|
def has_required_scope(conn: HTTPConnection, scopes: Sequence[str]) -> bool:
|
||||||
|
for scope in scopes:
|
||||||
|
if scope not in conn.auth.scopes:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def requires(
|
||||||
|
scopes: str | Sequence[str],
|
||||||
|
status_code: int = 403,
|
||||||
|
redirect: str | None = None,
|
||||||
|
) -> Callable[[Callable[_P, Any]], Callable[_P, Any]]:
|
||||||
|
scopes_list = [scopes] if isinstance(scopes, str) else list(scopes)
|
||||||
|
|
||||||
|
def decorator(
|
||||||
|
func: Callable[_P, Any],
|
||||||
|
) -> Callable[_P, Any]:
|
||||||
|
sig = inspect.signature(func)
|
||||||
|
for idx, parameter in enumerate(sig.parameters.values()):
|
||||||
|
if parameter.name == "request" or parameter.name == "websocket":
|
||||||
|
type_ = parameter.name
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
raise Exception(f'No "request" or "websocket" argument on function "{func}"')
|
||||||
|
|
||||||
|
if type_ == "websocket":
|
||||||
|
# Handle websocket functions. (Always async)
|
||||||
|
@functools.wraps(func)
|
||||||
|
async def websocket_wrapper(*args: _P.args, **kwargs: _P.kwargs) -> None:
|
||||||
|
websocket = kwargs.get("websocket", args[idx] if idx < len(args) else None)
|
||||||
|
assert isinstance(websocket, WebSocket), (
|
||||||
|
"Parameter with name 'websocket' is required to be of type 'WebSocket'"
|
||||||
|
f" not '{type(websocket).__name__}'"
|
||||||
|
)
|
||||||
|
|
||||||
|
if not has_required_scope(websocket, scopes_list):
|
||||||
|
await websocket.close()
|
||||||
|
else:
|
||||||
|
await func(*args, **kwargs)
|
||||||
|
|
||||||
|
return websocket_wrapper
|
||||||
|
|
||||||
|
elif is_async_callable(func):
|
||||||
|
# Handle async request/response functions.
|
||||||
|
@functools.wraps(func)
|
||||||
|
async def async_wrapper(*args: _P.args, **kwargs: _P.kwargs) -> Any:
|
||||||
|
request = kwargs.get("request", args[idx] if idx < len(args) else None)
|
||||||
|
assert isinstance(request, Request), (
|
||||||
|
f"Parameter with name 'request' is required to be of type 'Request' not '{type(request).__name__}'"
|
||||||
|
)
|
||||||
|
|
||||||
|
if not has_required_scope(request, scopes_list):
|
||||||
|
if redirect is not None:
|
||||||
|
orig_request_qparam = urlencode({"next": str(request.url)})
|
||||||
|
next_url = f"{request.url_for(redirect)}?{orig_request_qparam}"
|
||||||
|
return RedirectResponse(url=next_url, status_code=303)
|
||||||
|
raise HTTPException(status_code=status_code)
|
||||||
|
return await func(*args, **kwargs)
|
||||||
|
|
||||||
|
return async_wrapper
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Handle sync request/response functions.
|
||||||
|
@functools.wraps(func)
|
||||||
|
def sync_wrapper(*args: _P.args, **kwargs: _P.kwargs) -> Any:
|
||||||
|
request = kwargs.get("request", args[idx] if idx < len(args) else None)
|
||||||
|
assert isinstance(request, Request), (
|
||||||
|
f"Parameter with name 'request' is required to be of type 'Request' not '{type(request).__name__}'"
|
||||||
|
)
|
||||||
|
|
||||||
|
if not has_required_scope(request, scopes_list):
|
||||||
|
if redirect is not None:
|
||||||
|
orig_request_qparam = urlencode({"next": str(request.url)})
|
||||||
|
next_url = f"{request.url_for(redirect)}?{orig_request_qparam}"
|
||||||
|
return RedirectResponse(url=next_url, status_code=303)
|
||||||
|
raise HTTPException(status_code=status_code)
|
||||||
|
return func(*args, **kwargs)
|
||||||
|
|
||||||
|
return sync_wrapper
|
||||||
|
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
|
class AuthenticationError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class AuthenticationBackend:
|
||||||
|
async def authenticate(self, conn: HTTPConnection) -> tuple[AuthCredentials, BaseUser] | None:
|
||||||
|
raise NotImplementedError() # pragma: no cover
|
||||||
|
|
||||||
|
|
||||||
|
class AuthCredentials:
|
||||||
|
def __init__(self, scopes: Sequence[str] | None = None):
|
||||||
|
self.scopes = [] if scopes is None else list(scopes)
|
||||||
|
|
||||||
|
|
||||||
|
class BaseUser:
|
||||||
|
@property
|
||||||
|
def is_authenticated(self) -> bool:
|
||||||
|
raise NotImplementedError() # pragma: no cover
|
||||||
|
|
||||||
|
@property
|
||||||
|
def display_name(self) -> str:
|
||||||
|
raise NotImplementedError() # pragma: no cover
|
||||||
|
|
||||||
|
@property
|
||||||
|
def identity(self) -> str:
|
||||||
|
raise NotImplementedError() # pragma: no cover
|
||||||
|
|
||||||
|
|
||||||
|
class SimpleUser(BaseUser):
|
||||||
|
def __init__(self, username: str) -> None:
|
||||||
|
self.username = username
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_authenticated(self) -> bool:
|
||||||
|
return True
|
||||||
|
|
||||||
|
@property
|
||||||
|
def display_name(self) -> str:
|
||||||
|
return self.username
|
||||||
|
|
||||||
|
|
||||||
|
class UnauthenticatedUser(BaseUser):
|
||||||
|
@property
|
||||||
|
def is_authenticated(self) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def display_name(self) -> str:
|
||||||
|
return ""
|
||||||
Reference in New Issue
Block a user