Voice et bot modif
This commit is contained in:
parent
189d56026b
commit
7333a22bcd
10774 changed files with 634644 additions and 933308 deletions
|
|
@ -11,39 +11,27 @@ import platform
|
|||
import re
|
||||
import sys
|
||||
import warnings
|
||||
from functools import wraps
|
||||
from pathlib import Path
|
||||
from types import MappingProxyType
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
from collections.abc import (
|
||||
Awaitable,
|
||||
Callable,
|
||||
Container,
|
||||
Dict,
|
||||
Final,
|
||||
Generator,
|
||||
Iterable,
|
||||
Iterator,
|
||||
List,
|
||||
Mapping,
|
||||
NoReturn,
|
||||
Optional,
|
||||
Pattern,
|
||||
Set,
|
||||
Sized,
|
||||
Tuple,
|
||||
Type,
|
||||
TypedDict,
|
||||
Union,
|
||||
cast,
|
||||
)
|
||||
from functools import wraps
|
||||
from pathlib import Path
|
||||
from re import Pattern
|
||||
from types import MappingProxyType
|
||||
from typing import TYPE_CHECKING, Any, Final, NoReturn, Optional, TypedDict, cast
|
||||
|
||||
from yarl import URL, __version__ as yarl_version
|
||||
|
||||
from . import hdrs
|
||||
from .abc import AbstractMatchInfo, AbstractRouter, AbstractView
|
||||
from .helpers import DEBUG
|
||||
from .helpers import DEBUG, DEFAULT_CHUNK_SIZE
|
||||
from .http import HttpVersion11
|
||||
from .typedefs import Handler, PathLike
|
||||
from .web_exceptions import (
|
||||
|
|
@ -75,17 +63,13 @@ __all__ = (
|
|||
if TYPE_CHECKING:
|
||||
from .web_app import Application
|
||||
|
||||
BaseDict = Dict[str, str]
|
||||
BaseDict = dict[str, str]
|
||||
else:
|
||||
BaseDict = dict
|
||||
|
||||
CIRCULAR_SYMLINK_ERROR = (
|
||||
(OSError,)
|
||||
if sys.version_info < (3, 10) and sys.platform.startswith("win32")
|
||||
else (RuntimeError,) if sys.version_info < (3, 13) else ()
|
||||
)
|
||||
CIRCULAR_SYMLINK_ERROR = (RuntimeError,) if sys.version_info < (3, 13) else ()
|
||||
|
||||
YARL_VERSION: Final[Tuple[int, ...]] = tuple(map(int, yarl_version.split(".")[:2]))
|
||||
YARL_VERSION: Final[tuple[int, ...]] = tuple(map(int, yarl_version.split(".")[:2]))
|
||||
|
||||
HTTP_METHOD_RE: Final[Pattern[str]] = re.compile(
|
||||
r"^[0-9A-Za-z!#\$%&'\*\+\-\.\^_`\|~]+$"
|
||||
|
|
@ -97,8 +81,8 @@ PATH_SEP: Final[str] = re.escape("/")
|
|||
|
||||
IS_WINDOWS: Final[bool] = platform.system() == "Windows"
|
||||
|
||||
_ExpectHandler = Callable[[Request], Awaitable[Optional[StreamResponse]]]
|
||||
_Resolve = Tuple[Optional["UrlMappingMatchInfo"], Set[str]]
|
||||
_ExpectHandler = Callable[[Request], Awaitable[StreamResponse | None]]
|
||||
_Resolve = tuple[Optional["UrlMappingMatchInfo"], set[str]]
|
||||
|
||||
html_escape = functools.partial(html.escape, quote=True)
|
||||
|
||||
|
|
@ -123,11 +107,11 @@ class _InfoDict(TypedDict, total=False):
|
|||
|
||||
|
||||
class AbstractResource(Sized, Iterable["AbstractRoute"]):
|
||||
def __init__(self, *, name: Optional[str] = None) -> None:
|
||||
def __init__(self, *, name: str | None = None) -> None:
|
||||
self._name = name
|
||||
|
||||
@property
|
||||
def name(self) -> Optional[str]:
|
||||
def name(self) -> str | None:
|
||||
return self._name
|
||||
|
||||
@property
|
||||
|
|
@ -173,10 +157,10 @@ class AbstractRoute(abc.ABC):
|
|||
def __init__(
|
||||
self,
|
||||
method: str,
|
||||
handler: Union[Handler, Type[AbstractView]],
|
||||
handler: Handler | type[AbstractView],
|
||||
*,
|
||||
expect_handler: Optional[_ExpectHandler] = None,
|
||||
resource: Optional[AbstractResource] = None,
|
||||
expect_handler: _ExpectHandler | None = None,
|
||||
resource: AbstractResource | None = None,
|
||||
) -> None:
|
||||
|
||||
if expect_handler is None:
|
||||
|
|
@ -235,11 +219,11 @@ class AbstractRoute(abc.ABC):
|
|||
|
||||
@property
|
||||
@abc.abstractmethod
|
||||
def name(self) -> Optional[str]:
|
||||
def name(self) -> str | None:
|
||||
"""Optional route's name, always equals to resource's name."""
|
||||
|
||||
@property
|
||||
def resource(self) -> Optional[AbstractResource]:
|
||||
def resource(self) -> AbstractResource | None:
|
||||
return self._resource
|
||||
|
||||
@abc.abstractmethod
|
||||
|
|
@ -250,7 +234,7 @@ class AbstractRoute(abc.ABC):
|
|||
def url_for(self, *args: str, **kwargs: str) -> URL:
|
||||
"""Construct url for route with additional params."""
|
||||
|
||||
async def handle_expect_header(self, request: Request) -> Optional[StreamResponse]:
|
||||
async def handle_expect_header(self, request: Request) -> StreamResponse | None:
|
||||
return await self._expect_handler(request)
|
||||
|
||||
|
||||
|
|
@ -258,11 +242,11 @@ class UrlMappingMatchInfo(BaseDict, AbstractMatchInfo):
|
|||
|
||||
__slots__ = ("_route", "_apps", "_current_app", "_frozen")
|
||||
|
||||
def __init__(self, match_dict: Dict[str, str], route: AbstractRoute) -> None:
|
||||
def __init__(self, match_dict: dict[str, str], route: AbstractRoute) -> None:
|
||||
super().__init__(match_dict)
|
||||
self._route = route
|
||||
self._apps: List[Application] = []
|
||||
self._current_app: Optional[Application] = None
|
||||
self._apps: list[Application] = []
|
||||
self._current_app: Application | None = None
|
||||
self._frozen = False
|
||||
|
||||
@property
|
||||
|
|
@ -278,14 +262,14 @@ class UrlMappingMatchInfo(BaseDict, AbstractMatchInfo):
|
|||
return self._route.handle_expect_header
|
||||
|
||||
@property
|
||||
def http_exception(self) -> Optional[HTTPException]:
|
||||
def http_exception(self) -> HTTPException | None:
|
||||
return None
|
||||
|
||||
def get_info(self) -> _InfoDict: # type: ignore[override]
|
||||
return self._route.get_info()
|
||||
|
||||
@property
|
||||
def apps(self) -> Tuple["Application", ...]:
|
||||
def apps(self) -> tuple["Application", ...]:
|
||||
return tuple(self._apps)
|
||||
|
||||
def add_app(self, app: "Application") -> None:
|
||||
|
|
@ -306,9 +290,7 @@ class UrlMappingMatchInfo(BaseDict, AbstractMatchInfo):
|
|||
if DEBUG: # pragma: no cover
|
||||
if app not in self._apps:
|
||||
raise RuntimeError(
|
||||
"Expected one of the following apps {!r}, got {!r}".format(
|
||||
self._apps, app
|
||||
)
|
||||
f"Expected one of the following apps {self._apps!r}, got {app!r}"
|
||||
)
|
||||
self._current_app = app
|
||||
|
||||
|
|
@ -332,9 +314,7 @@ class MatchInfoError(UrlMappingMatchInfo):
|
|||
return self._exception
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return "<MatchInfoError {}: {}>".format(
|
||||
self._exception.status, self._exception.reason
|
||||
)
|
||||
return f"<MatchInfoError {self._exception.status}: {self._exception.reason}>"
|
||||
|
||||
|
||||
async def _default_expect_handler(request: Request) -> None:
|
||||
|
|
@ -354,18 +334,18 @@ async def _default_expect_handler(request: Request) -> None:
|
|||
|
||||
|
||||
class Resource(AbstractResource):
|
||||
def __init__(self, *, name: Optional[str] = None) -> None:
|
||||
def __init__(self, *, name: str | None = None) -> None:
|
||||
super().__init__(name=name)
|
||||
self._routes: Dict[str, ResourceRoute] = {}
|
||||
self._any_route: Optional[ResourceRoute] = None
|
||||
self._allowed_methods: Set[str] = set()
|
||||
self._routes: dict[str, ResourceRoute] = {}
|
||||
self._any_route: ResourceRoute | None = None
|
||||
self._allowed_methods: set[str] = set()
|
||||
|
||||
def add_route(
|
||||
self,
|
||||
method: str,
|
||||
handler: Union[Type[AbstractView], Handler],
|
||||
handler: type[AbstractView] | Handler,
|
||||
*,
|
||||
expect_handler: Optional[_ExpectHandler] = None,
|
||||
expect_handler: _ExpectHandler | None = None,
|
||||
) -> "ResourceRoute":
|
||||
if route := self._routes.get(method, self._any_route):
|
||||
raise RuntimeError(
|
||||
|
|
@ -395,7 +375,7 @@ class Resource(AbstractResource):
|
|||
return None, self._allowed_methods
|
||||
|
||||
@abc.abstractmethod
|
||||
def _match(self, path: str) -> Optional[Dict[str, str]]:
|
||||
def _match(self, path: str) -> dict[str, str] | None:
|
||||
pass # pragma: no cover
|
||||
|
||||
def __len__(self) -> int:
|
||||
|
|
@ -408,7 +388,7 @@ class Resource(AbstractResource):
|
|||
|
||||
|
||||
class PlainResource(Resource):
|
||||
def __init__(self, path: str, *, name: Optional[str] = None) -> None:
|
||||
def __init__(self, path: str, *, name: str | None = None) -> None:
|
||||
super().__init__(name=name)
|
||||
assert not path or path.startswith("/")
|
||||
self._path = path
|
||||
|
|
@ -427,7 +407,7 @@ class PlainResource(Resource):
|
|||
assert len(prefix) > 1
|
||||
self._path = prefix + self._path
|
||||
|
||||
def _match(self, path: str) -> Optional[Dict[str, str]]:
|
||||
def _match(self, path: str) -> dict[str, str] | None:
|
||||
# string comparison is about 10 times faster than regexp matching
|
||||
if self._path == path:
|
||||
return {}
|
||||
|
|
@ -453,7 +433,7 @@ class DynamicResource(Resource):
|
|||
DYN_WITH_RE = re.compile(r"\{(?P<var>[_a-zA-Z][_a-zA-Z0-9]*):(?P<re>.+)\}")
|
||||
GOOD = r"[^{}/]+"
|
||||
|
||||
def __init__(self, path: str, *, name: Optional[str] = None) -> None:
|
||||
def __init__(self, path: str, *, name: str | None = None) -> None:
|
||||
super().__init__(name=name)
|
||||
self._orig_path = path
|
||||
pattern = ""
|
||||
|
|
@ -498,7 +478,7 @@ class DynamicResource(Resource):
|
|||
self._pattern = re.compile(re.escape(prefix) + self._pattern.pattern)
|
||||
self._formatter = prefix + self._formatter
|
||||
|
||||
def _match(self, path: str) -> Optional[Dict[str, str]]:
|
||||
def _match(self, path: str) -> dict[str, str] | None:
|
||||
match = self._pattern.fullmatch(path)
|
||||
if match is None:
|
||||
return None
|
||||
|
|
@ -518,13 +498,11 @@ class DynamicResource(Resource):
|
|||
|
||||
def __repr__(self) -> str:
|
||||
name = "'" + self.name + "' " if self.name is not None else ""
|
||||
return "<DynamicResource {name} {formatter}>".format(
|
||||
name=name, formatter=self._formatter
|
||||
)
|
||||
return f"<DynamicResource {name} {self._formatter}>"
|
||||
|
||||
|
||||
class PrefixResource(AbstractResource):
|
||||
def __init__(self, prefix: str, *, name: Optional[str] = None) -> None:
|
||||
def __init__(self, prefix: str, *, name: str | None = None) -> None:
|
||||
assert not prefix or prefix.startswith("/"), prefix
|
||||
assert prefix in ("", "/") or not prefix.endswith("/"), prefix
|
||||
super().__init__(name=name)
|
||||
|
|
@ -556,9 +534,9 @@ class StaticResource(PrefixResource):
|
|||
prefix: str,
|
||||
directory: PathLike,
|
||||
*,
|
||||
name: Optional[str] = None,
|
||||
expect_handler: Optional[_ExpectHandler] = None,
|
||||
chunk_size: int = 256 * 1024,
|
||||
name: str | None = None,
|
||||
expect_handler: _ExpectHandler | None = None,
|
||||
chunk_size: int = DEFAULT_CHUNK_SIZE,
|
||||
show_index: bool = False,
|
||||
follow_symlinks: bool = False,
|
||||
append_version: bool = False,
|
||||
|
|
@ -591,7 +569,7 @@ class StaticResource(PrefixResource):
|
|||
self,
|
||||
*,
|
||||
filename: PathLike,
|
||||
append_version: Optional[bool] = None,
|
||||
append_version: bool | None = None,
|
||||
) -> URL:
|
||||
if append_version is None:
|
||||
append_version = self._append_version
|
||||
|
|
@ -676,6 +654,10 @@ class StaticResource(PrefixResource):
|
|||
|
||||
async def _handle(self, request: Request) -> StreamResponse:
|
||||
filename = request.match_info["filename"]
|
||||
if Path(filename).is_absolute():
|
||||
# filename is an absolute path e.g. //network/share or D:\path
|
||||
# which could be a UNC path leading to NTLM credential theft
|
||||
raise HTTPNotFound()
|
||||
unresolved_path = self._directory.joinpath(filename)
|
||||
loop = asyncio.get_running_loop()
|
||||
return await loop.run_in_executor(
|
||||
|
|
@ -751,9 +733,7 @@ class StaticResource(PrefixResource):
|
|||
|
||||
def __repr__(self) -> str:
|
||||
name = "'" + self.name + "'" if self.name is not None else ""
|
||||
return "<StaticResource {name} {path} -> {directory!r}>".format(
|
||||
name=name, path=self._prefix, directory=self._directory
|
||||
)
|
||||
return f"<StaticResource {name} {self._prefix} -> {self._directory!r}>"
|
||||
|
||||
|
||||
class PrefixedSubAppResource(PrefixResource):
|
||||
|
|
@ -797,9 +777,7 @@ class PrefixedSubAppResource(PrefixResource):
|
|||
return iter(self._app.router.routes())
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return "<PrefixedSubAppResource {prefix} -> {app!r}>".format(
|
||||
prefix=self._prefix, app=self._app
|
||||
)
|
||||
return f"<PrefixedSubAppResource {self._prefix} -> {self._app!r}>"
|
||||
|
||||
|
||||
class AbstractRuleMatching(abc.ABC):
|
||||
|
|
@ -908,22 +886,20 @@ class ResourceRoute(AbstractRoute):
|
|||
def __init__(
|
||||
self,
|
||||
method: str,
|
||||
handler: Union[Handler, Type[AbstractView]],
|
||||
handler: Handler | type[AbstractView],
|
||||
resource: AbstractResource,
|
||||
*,
|
||||
expect_handler: Optional[_ExpectHandler] = None,
|
||||
expect_handler: _ExpectHandler | None = None,
|
||||
) -> None:
|
||||
super().__init__(
|
||||
method, handler, expect_handler=expect_handler, resource=resource
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return "<ResourceRoute [{method}] {resource} -> {handler!r}".format(
|
||||
method=self.method, resource=self._resource, handler=self.handler
|
||||
)
|
||||
return f"<ResourceRoute [{self.method}] {self._resource} -> {self.handler!r}"
|
||||
|
||||
@property
|
||||
def name(self) -> Optional[str]:
|
||||
def name(self) -> str | None:
|
||||
if self._resource is None:
|
||||
return None
|
||||
return self._resource.name
|
||||
|
|
@ -947,7 +923,7 @@ class SystemRoute(AbstractRoute):
|
|||
raise RuntimeError(".url_for() is not allowed for SystemRoute")
|
||||
|
||||
@property
|
||||
def name(self) -> Optional[str]:
|
||||
def name(self) -> str | None:
|
||||
return None
|
||||
|
||||
def get_info(self) -> _InfoDict:
|
||||
|
|
@ -965,14 +941,14 @@ class SystemRoute(AbstractRoute):
|
|||
return self._http_exception.reason
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return "<SystemRoute {self.status}: {self.reason}>".format(self=self)
|
||||
return f"<SystemRoute {self.status}: {self.reason}>"
|
||||
|
||||
|
||||
class View(AbstractView):
|
||||
async def _iter(self) -> StreamResponse:
|
||||
if self.request.method not in hdrs.METH_ALL:
|
||||
self._raise_allowed_methods()
|
||||
method: Optional[Callable[[], Awaitable[StreamResponse]]]
|
||||
method: Callable[[], Awaitable[StreamResponse]] | None
|
||||
method = getattr(self, self.request.method.lower(), None)
|
||||
if method is None:
|
||||
self._raise_allowed_methods()
|
||||
|
|
@ -989,7 +965,7 @@ class View(AbstractView):
|
|||
|
||||
|
||||
class ResourcesView(Sized, Iterable[AbstractResource], Container[AbstractResource]):
|
||||
def __init__(self, resources: List[AbstractResource]) -> None:
|
||||
def __init__(self, resources: list[AbstractResource]) -> None:
|
||||
self._resources = resources
|
||||
|
||||
def __len__(self) -> int:
|
||||
|
|
@ -1003,8 +979,8 @@ class ResourcesView(Sized, Iterable[AbstractResource], Container[AbstractResourc
|
|||
|
||||
|
||||
class RoutesView(Sized, Iterable[AbstractRoute], Container[AbstractRoute]):
|
||||
def __init__(self, resources: List[AbstractResource]):
|
||||
self._routes: List[AbstractRoute] = []
|
||||
def __init__(self, resources: list[AbstractResource]):
|
||||
self._routes: list[AbstractRoute] = []
|
||||
for resource in resources:
|
||||
for route in resource:
|
||||
self._routes.append(route)
|
||||
|
|
@ -1025,14 +1001,14 @@ class UrlDispatcher(AbstractRouter, Mapping[str, AbstractResource]):
|
|||
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self._resources: List[AbstractResource] = []
|
||||
self._named_resources: Dict[str, AbstractResource] = {}
|
||||
self._resources: list[AbstractResource] = []
|
||||
self._named_resources: dict[str, AbstractResource] = {}
|
||||
self._resource_index: dict[str, list[AbstractResource]] = {}
|
||||
self._matched_sub_app_resources: List[MatchedSubAppResource] = []
|
||||
self._matched_sub_app_resources: list[MatchedSubAppResource] = []
|
||||
|
||||
async def resolve(self, request: Request) -> UrlMappingMatchInfo:
|
||||
resource_index = self._resource_index
|
||||
allowed_methods: Set[str] = set()
|
||||
allowed_methods: set[str] = set()
|
||||
|
||||
# MatchedSubAppResource is primarily used to match on domain names
|
||||
# (though custom rules could match on other things). This means that
|
||||
|
|
@ -1112,15 +1088,15 @@ class UrlDispatcher(AbstractRouter, Mapping[str, AbstractResource]):
|
|||
)
|
||||
if not part.isidentifier():
|
||||
raise ValueError(
|
||||
"Incorrect route name {!r}, "
|
||||
f"Incorrect route name {name!r}, "
|
||||
"the name should be a sequence of "
|
||||
"python identifiers separated "
|
||||
"by dash, dot or column".format(name)
|
||||
"by dash, dot or column"
|
||||
)
|
||||
if name in self._named_resources:
|
||||
raise ValueError(
|
||||
"Duplicate {!r}, "
|
||||
"already handled by {!r}".format(name, self._named_resources[name])
|
||||
f"Duplicate {name!r}, "
|
||||
f"already handled by {self._named_resources[name]!r}"
|
||||
)
|
||||
self._named_resources[name] = resource
|
||||
self._resources.append(resource)
|
||||
|
|
@ -1155,7 +1131,7 @@ class UrlDispatcher(AbstractRouter, Mapping[str, AbstractResource]):
|
|||
resource_key = self._get_resource_index_key(resource)
|
||||
self._resource_index[resource_key].remove(resource)
|
||||
|
||||
def add_resource(self, path: str, *, name: Optional[str] = None) -> Resource:
|
||||
def add_resource(self, path: str, *, name: str | None = None) -> Resource:
|
||||
if path and not path.startswith("/"):
|
||||
raise ValueError("path should be started with / or be empty")
|
||||
# Reuse last added resource if path and name are the same
|
||||
|
|
@ -1175,10 +1151,10 @@ class UrlDispatcher(AbstractRouter, Mapping[str, AbstractResource]):
|
|||
self,
|
||||
method: str,
|
||||
path: str,
|
||||
handler: Union[Handler, Type[AbstractView]],
|
||||
handler: Handler | type[AbstractView],
|
||||
*,
|
||||
name: Optional[str] = None,
|
||||
expect_handler: Optional[_ExpectHandler] = None,
|
||||
name: str | None = None,
|
||||
expect_handler: _ExpectHandler | None = None,
|
||||
) -> AbstractRoute:
|
||||
resource = self.add_resource(path, name=name)
|
||||
return resource.add_route(method, handler, expect_handler=expect_handler)
|
||||
|
|
@ -1188,9 +1164,9 @@ class UrlDispatcher(AbstractRouter, Mapping[str, AbstractResource]):
|
|||
prefix: str,
|
||||
path: PathLike,
|
||||
*,
|
||||
name: Optional[str] = None,
|
||||
expect_handler: Optional[_ExpectHandler] = None,
|
||||
chunk_size: int = 256 * 1024,
|
||||
name: str | None = None,
|
||||
expect_handler: _ExpectHandler | None = None,
|
||||
chunk_size: int = DEFAULT_CHUNK_SIZE,
|
||||
show_index: bool = False,
|
||||
follow_symlinks: bool = False,
|
||||
append_version: bool = False,
|
||||
|
|
@ -1230,7 +1206,7 @@ class UrlDispatcher(AbstractRouter, Mapping[str, AbstractResource]):
|
|||
path: str,
|
||||
handler: Handler,
|
||||
*,
|
||||
name: Optional[str] = None,
|
||||
name: str | None = None,
|
||||
allow_head: bool = True,
|
||||
**kwargs: Any,
|
||||
) -> AbstractRoute:
|
||||
|
|
@ -1261,7 +1237,7 @@ class UrlDispatcher(AbstractRouter, Mapping[str, AbstractResource]):
|
|||
return self.add_route(hdrs.METH_DELETE, path, handler, **kwargs)
|
||||
|
||||
def add_view(
|
||||
self, path: str, handler: Type[AbstractView], **kwargs: Any
|
||||
self, path: str, handler: type[AbstractView], **kwargs: Any
|
||||
) -> AbstractRoute:
|
||||
"""Shortcut for add_route with ANY methods for a class-based view."""
|
||||
return self.add_route(hdrs.METH_ANY, path, handler, **kwargs)
|
||||
|
|
@ -1271,7 +1247,7 @@ class UrlDispatcher(AbstractRouter, Mapping[str, AbstractResource]):
|
|||
for resource in self._resources:
|
||||
resource.freeze()
|
||||
|
||||
def add_routes(self, routes: Iterable[AbstractRouteDef]) -> List[AbstractRoute]:
|
||||
def add_routes(self, routes: Iterable[AbstractRouteDef]) -> list[AbstractRoute]:
|
||||
"""Append routes to route table.
|
||||
|
||||
Parameter should be a sequence of RouteDef objects.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue