Fixed mypy

This commit is contained in:
2024-11-13 12:12:49 +08:00
parent ee6e645cc1
commit 8f0320f262
7 changed files with 95 additions and 76 deletions

View File

@@ -1,7 +1,8 @@
from ._app import BugisApp from ._app import BugisApp
from ._http_method import HttpMethod from ._http_method import HttpMethod
from ._http_context import HttpContext from ._http_context import HttpContext
from ._tree import Tree, PathHandler, PathIterator from ._tree import Tree, PathIterator
from ._path_handler import PathHandler
__all__ = [ __all__ = [

View File

@@ -2,7 +2,7 @@ from abc import ABC, abstractmethod
from asyncio import Queue, AbstractEventLoop from asyncio import Queue, AbstractEventLoop
from asyncio import get_running_loop from asyncio import get_running_loop
from logging import getLogger from logging import getLogger
from typing import Callable, Awaitable, Any, Mapping, Sequence, Optional, Unpack from typing import Callable, Awaitable, Any, Mapping, Sequence, Optional, Unpack, Tuple
from pwo import Maybe, AsyncQueueIterator from pwo import Maybe, AsyncQueueIterator
@@ -22,7 +22,7 @@ from ._types.asgi import LifespanScope, HTTPScope as ASGIHTTPScope, WebSocketSco
log = getLogger(__name__) log = getLogger(__name__)
type HttpHandler = Callable[[HttpContext, Unpack], Awaitable[None]] type HttpHandler = Callable[[HttpContext, Unpack[Any]], Awaitable[None]]
class AbstractBugisApp(ABC): class AbstractBugisApp(ABC):
@@ -102,18 +102,21 @@ class BugisApp(AbstractBugisApp):
def wrapped(handler: HttpHandler) -> HttpHandler: def wrapped(handler: HttpHandler) -> HttpHandler:
nonlocal methods nonlocal methods
nonlocal paths nonlocal paths
_methods: Tuple[Optional[HttpMethod], ...]
if methods is None: if methods is None:
methods = (None,) _methods = (None,)
elif isinstance(methods, HttpMethod): elif isinstance(methods, HttpMethod):
methods = (methods,) _methods = (methods,)
else:
_methods = tuple(methods)
_paths: Tuple[str, ...]
if isinstance(paths, str): if isinstance(paths, str):
paths = (paths,) _paths = (paths,)
for method in methods: else:
if isinstance(paths, str): _paths = tuple(paths)
self._tree.register(paths, method, handler, recursive) for method in _methods:
else: for path in _paths:
for path in paths: self._tree.register(path, method, handler, recursive)
self._tree.register(path, method, handler, recursive)
return handler return handler
return wrapped return wrapped

View File

@@ -5,14 +5,8 @@ from typing import (
List, List,
) )
from ._types import NodeType from ._types import NodeType
# from ._path_handler import PathHandler from ._path_handler import PathHandler
# from ._path_matcher import PathMatcher from ._path_matcher import PathMatcher
@dataclass
class Node:
key: NodeType
parent: Optional['Node']
children: Dict[NodeType, 'Node']
handlers: List['PathHandler']
path_matchers: List['PathMatcher']

View File

@@ -1,12 +1,24 @@
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from typing import Sequence, Mapping, Any from typing import (
from ._http_method import HttpMethod Sequence,
Dict,
Optional
)
from dataclasses import dataclass, field
from ._http_context import HttpContext from ._http_context import HttpContext
from ._types import PathMatcherResult, Matches
@dataclass
class Matches:
kwargs: Dict[str, str] = field(default_factory=dict)
path: Optional[Sequence[str]] = None
unmatched_paths: Sequence[str] = field(default_factory=list)
class PathHandler(ABC): class PathHandler(ABC):
recursive: bool
@abstractmethod @abstractmethod
async def handle_request(self, ctx: HttpContext, captured: Matches) -> None: async def handle_request(self, ctx: HttpContext, captured: Matches) -> None:
@@ -18,4 +30,4 @@ class PathHandler(ABC):
raise NotImplementedError() raise NotImplementedError()
type PathHandlers = ('PathHandler' | Sequence['PathHandler']) type PathHandlers = (PathHandler | Sequence[PathHandler])

View File

@@ -1,23 +1,31 @@
from fnmatch import fnmatch from fnmatch import fnmatch
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from typing import Optional, Sequence, Dict, Mapping from typing import Optional, Sequence, Dict, List, Union
from dataclasses import dataclass
from ._path_handler import PathHandler from ._path_handler import PathHandler
from ._types import NodeType, PathMatcherResult from ._types import NodeType, PathMatcherResult
from ._node import Node
@dataclass
class Node:
key: NodeType
parent: Optional[Union['Node', 'PathMatcher']]
children: Dict[NodeType, 'Node']
handlers: List[PathHandler]
path_matchers: List['PathMatcher']
class PathMatcher(ABC): class PathMatcher(ABC):
parent: Optional[Node] parent: Optional[Union['Node', 'PathMatcher']]
children: Dict[NodeType, Node] children: Dict[NodeType, Node]
handlers: Sequence[PathHandler] handlers: List[PathHandler]
path_matchers: Sequence['PathMatcher'] path_matchers: List['PathMatcher']
def __init__(self, def __init__(self,
parent: Optional[Node], parent: Optional[Union['Node', 'PathMatcher']],
children: Dict[NodeType, Node], children: Dict[NodeType, Node],
handlers: Sequence[PathHandler], handlers: List[PathHandler],
path_matchers: Sequence['PathMatcher'] path_matchers: List['PathMatcher']
): ):
self.parent = parent self.parent = parent
self.children = children self.children = children
@@ -34,10 +42,10 @@ class StrMatcher(PathMatcher):
def __init__(self, def __init__(self,
name: str, name: str,
parent: Optional[Node], parent: Optional[Node | PathMatcher],
children: Dict[NodeType, Node], children: Dict[NodeType, Node],
handlers: Sequence[PathHandler], handlers: List[PathHandler],
path_matchers: Sequence[PathMatcher], path_matchers: List[PathMatcher],
): ):
super().__init__(parent, children, handlers, path_matchers) super().__init__(parent, children, handlers, path_matchers)
self.name = name self.name = name
@@ -54,10 +62,10 @@ class IntMatcher(PathMatcher):
def __init__(self, def __init__(self,
name: str, name: str,
parent: Optional[Node], parent: Optional[Node | PathMatcher],
children: Dict[NodeType, Node], children: Dict[NodeType, Node],
handlers: Sequence[PathHandler], handlers: List[PathHandler],
path_matchers: Sequence[PathMatcher], path_matchers: List[PathMatcher],
): ):
super().__init__(parent, children, handlers, path_matchers) super().__init__(parent, children, handlers, path_matchers)
self.name = name self.name = name
@@ -68,6 +76,8 @@ class IntMatcher(PathMatcher):
return {self.name: int(path[0])} return {self.name: int(path[0])}
except ValueError: except ValueError:
return None return None
else:
return None
class GlobMatcher(PathMatcher): class GlobMatcher(PathMatcher):
@@ -75,10 +85,10 @@ class GlobMatcher(PathMatcher):
def __init__(self, def __init__(self,
pattern: str, pattern: str,
parent: Optional[Node], parent: Optional[Node | PathMatcher],
children: Dict[NodeType, Node], children: Dict[NodeType, Node],
handlers: Sequence[PathHandler], handlers: List[PathHandler],
path_matchers: Sequence[PathMatcher], path_matchers: List[PathMatcher],
): ):
super().__init__(parent, children, handlers, path_matchers) super().__init__(parent, children, handlers, path_matchers)
self.pattern = pattern self.pattern = pattern

View File

@@ -10,7 +10,6 @@ from typing import (
Tuple, Tuple,
Mapping, Mapping,
Any, Any,
Dict
) )
from typing_extensions import Unpack from typing_extensions import Unpack
from urllib.parse import urlparse from urllib.parse import urlparse
@@ -19,10 +18,9 @@ from pwo import Maybe, index_of_with_escape
from ._http_context import HttpContext from ._http_context import HttpContext
from ._http_method import HttpMethod from ._http_method import HttpMethod
from ._node import Node
from ._path_handler import PathHandler from ._path_handler import PathHandler
from ._path_matcher import PathMatcher, IntMatcher, GlobMatcher, StrMatcher from ._path_matcher import PathMatcher, IntMatcher, GlobMatcher, StrMatcher, Node
from ._types import NodeType, PathMatcherResult, Matches from ._types import NodeType, Matches
class Tree: class Tree:
@@ -32,11 +30,11 @@ class Tree:
def search(self, path: Generator[str, None, None], method: HttpMethod) \ def search(self, path: Generator[str, None, None], method: HttpMethod) \
-> Optional[Tuple[Node | PathMatcher, Matches]]: -> Optional[Tuple[Node | PathMatcher, Matches]]:
path: List = list(path) paths: List[str] = list(path)
result = self.root result: Node | PathMatcher = self.root
matches = Matches() matches = Matches()
it, i = iter((it for it in path)), -1 it, i = iter((it for it in paths)), -1
while True: while True:
node = result node = result
leaf, i = next(it, None), i + 1 leaf, i = next(it, None), i + 1
@@ -45,7 +43,7 @@ class Tree:
child = node.children.get(leaf) child = node.children.get(leaf)
if child is None and isinstance(leaf, str): if child is None and isinstance(leaf, str):
for matcher in node.path_matchers: for matcher in node.path_matchers:
match = matcher.match(path[i:]) match = matcher.match(paths[i:])
if match is not None: if match is not None:
if isinstance(match, Mapping): if isinstance(match, Mapping):
matches.kwargs.update(match) matches.kwargs.update(match)
@@ -60,16 +58,16 @@ class Tree:
child = result.children.get(method) child = result.children.get(method)
if child is not None: if child is not None:
result = child result = child
matches.unmatched_paths = path[i:] matches.unmatched_paths = paths[i:]
return None if result == self.root else (result, matches) return None if result == self.root else (result, matches)
def add(self, path: Generator[str, None, None], method: Optional[HttpMethod], *path_handlers: PathHandler) -> Node: def add(self, path: Generator[str, None, None], method: Optional[HttpMethod], *path_handlers: PathHandler) -> Node | PathMatcher:
lineage: Generator[NodeType, None, None] = (it for it in lineage: Generator[NodeType, None, None] = (it for it in
chain(path, chain(path,
Maybe.of_nullable(method) Maybe.of_nullable(method)
.map(lambda it: [it]) .map(lambda it: [it])
.or_else([]))) .or_else([])))
result = self.root result: Node | PathMatcher = self.root
it = iter(lineage) it = iter(lineage)
while True: while True:
@@ -92,14 +90,14 @@ class Tree:
result = new_node result = new_node
key = next(it, None) key = next(it, None)
result.handlers = tuple(chain(result.handlers, path_handlers)) result.handlers = list(chain(result.handlers, path_handlers))
return result return result
def register(self, def register(self,
path: str, path: str,
method: Optional[HttpMethod], method: Optional[HttpMethod],
callback: Callable[[HttpContext, Unpack], Awaitable[None]], callback: Callable[[HttpContext, Unpack[Any]], Awaitable[None]],
recursive) -> None: recursive: bool) -> None:
class Handler(PathHandler): class Handler(PathHandler):
async def handle_request(self, ctx: HttpContext, captured: Matches) -> None: async def handle_request(self, ctx: HttpContext, captured: Matches) -> None:
@@ -141,7 +139,7 @@ class Tree:
# return (handler, unmatched) # return (handler, unmatched)
return None return None
def parse(self, leaf : str, parent : Node | PathMatcher) -> Node | PathMatcher: def parse(self, leaf: str, parent: Optional[Node | PathMatcher]) -> Node | PathMatcher:
start = 0 start = 0
result = index_of_with_escape(leaf, '${', '\\', 0) result = index_of_with_escape(leaf, '${', '\\', 0)
if result >= 0: if result >= 0:
@@ -208,7 +206,7 @@ class PathIterator:
class NodeAncestryIterator: class NodeAncestryIterator:
node: Node node: Node | PathMatcher
def __init__(self, node: Node): def __init__(self, node: Node):
self.node = node self.node = node
@@ -216,7 +214,7 @@ class NodeAncestryIterator:
def __iter__(self) -> Self: def __iter__(self) -> Self:
return self return self
def __next__(self) -> Node: def __next__(self) -> Node | PathMatcher:
parent = self.node.parent parent = self.node.parent
if parent is None: if parent is None:
raise StopIteration() raise StopIteration()

View File

@@ -11,27 +11,16 @@ from typing import (
Mapping, Mapping,
Sequence Sequence
) )
from dataclasses import dataclass, field
from bugis.core._http_method import HttpMethod from bugis.core._http_method import HttpMethod
from bugis.core._path_handler import PathHandler, Matches
type StrOrStrings = (str | Sequence[str]) type StrOrStrings = (str | Sequence[str])
type NodeType = (str | HttpMethod) type NodeType = (str | HttpMethod)
type PathHandlers = ('PathHandler' | Sequence['PathHandler']) type PathMatcherResult = Mapping[str, Any] | Sequence[str]
type PathMatcherResult = Mapping[str, str] | Sequence[str]
@dataclass
class Matches:
kwargs: Dict[str, str] = field(default_factory=dict)
path: Optional[Sequence[str]] = None
unmatched_paths: [Sequence[str]] = field(default_factory=list)
class ASGIVersions(TypedDict): class ASGIVersions(TypedDict):
@@ -81,7 +70,7 @@ class LifespanScope(TypedDict):
class RSGI: class RSGI:
class Scope(TypedDict): class Scope(TypedDict):
proto: Literal['http'] = 'http' proto: Literal['http'] # = 'http'
rsgi_version: str rsgi_version: str
http_version: str http_version: str
server: str server: str
@@ -93,3 +82,15 @@ class RSGI:
headers: Mapping[str, str] headers: Mapping[str, str]
authority: Optional[str] authority: Optional[str]
__all__ = [
'HttpMethod',
'HTTPScope',
'LifespanScope',
'RSGI',
'ASGIVersions',
'WebSocketScope',
'PathHandler',
'NodeType',
'Matches'
]