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 ._http_method import HttpMethod
from ._http_context import HttpContext
from ._tree import Tree, PathHandler, PathIterator
from ._tree import Tree, PathIterator
from ._path_handler import PathHandler
__all__ = [

View File

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

View File

@@ -5,14 +5,8 @@ from typing import (
List,
)
from ._types import NodeType
# from ._path_handler import PathHandler
# from ._path_matcher import PathMatcher
from ._path_handler import PathHandler
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 typing import Sequence, Mapping, Any
from ._http_method import HttpMethod
from typing import (
Sequence,
Dict,
Optional
)
from dataclasses import dataclass, field
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):
recursive: bool
@abstractmethod
async def handle_request(self, ctx: HttpContext, captured: Matches) -> None:
@@ -18,4 +30,4 @@ class PathHandler(ABC):
raise NotImplementedError()
type PathHandlers = ('PathHandler' | Sequence['PathHandler'])
type PathHandlers = (PathHandler | Sequence[PathHandler])

View File

@@ -1,23 +1,31 @@
from fnmatch import fnmatch
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 ._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):
parent: Optional[Node]
parent: Optional[Union['Node', 'PathMatcher']]
children: Dict[NodeType, Node]
handlers: Sequence[PathHandler]
path_matchers: Sequence['PathMatcher']
handlers: List[PathHandler]
path_matchers: List['PathMatcher']
def __init__(self,
parent: Optional[Node],
parent: Optional[Union['Node', 'PathMatcher']],
children: Dict[NodeType, Node],
handlers: Sequence[PathHandler],
path_matchers: Sequence['PathMatcher']
handlers: List[PathHandler],
path_matchers: List['PathMatcher']
):
self.parent = parent
self.children = children
@@ -34,10 +42,10 @@ class StrMatcher(PathMatcher):
def __init__(self,
name: str,
parent: Optional[Node],
parent: Optional[Node | PathMatcher],
children: Dict[NodeType, Node],
handlers: Sequence[PathHandler],
path_matchers: Sequence[PathMatcher],
handlers: List[PathHandler],
path_matchers: List[PathMatcher],
):
super().__init__(parent, children, handlers, path_matchers)
self.name = name
@@ -54,10 +62,10 @@ class IntMatcher(PathMatcher):
def __init__(self,
name: str,
parent: Optional[Node],
parent: Optional[Node | PathMatcher],
children: Dict[NodeType, Node],
handlers: Sequence[PathHandler],
path_matchers: Sequence[PathMatcher],
handlers: List[PathHandler],
path_matchers: List[PathMatcher],
):
super().__init__(parent, children, handlers, path_matchers)
self.name = name
@@ -68,6 +76,8 @@ class IntMatcher(PathMatcher):
return {self.name: int(path[0])}
except ValueError:
return None
else:
return None
class GlobMatcher(PathMatcher):
@@ -75,10 +85,10 @@ class GlobMatcher(PathMatcher):
def __init__(self,
pattern: str,
parent: Optional[Node],
parent: Optional[Node | PathMatcher],
children: Dict[NodeType, Node],
handlers: Sequence[PathHandler],
path_matchers: Sequence[PathMatcher],
handlers: List[PathHandler],
path_matchers: List[PathMatcher],
):
super().__init__(parent, children, handlers, path_matchers)
self.pattern = pattern

View File

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

View File

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