From 8f0320f262b2d6c7eed71ca680d95d14440956a0 Mon Sep 17 00:00:00 2001 From: Walter Oggioni Date: Wed, 13 Nov 2024 12:12:49 +0800 Subject: [PATCH] Fixed mypy --- core/src/bugis/core/__init__.py | 3 +- core/src/bugis/core/_app.py | 25 ++++++++------ core/src/bugis/core/_node.py | 12 ++----- core/src/bugis/core/_path_handler.py | 22 +++++++++--- core/src/bugis/core/_path_matcher.py | 46 ++++++++++++++++---------- core/src/bugis/core/_tree.py | 32 +++++++++--------- core/src/bugis/core/_types/__init__.py | 31 ++++++++--------- 7 files changed, 95 insertions(+), 76 deletions(-) diff --git a/core/src/bugis/core/__init__.py b/core/src/bugis/core/__init__.py index ffec733..0e5a021 100644 --- a/core/src/bugis/core/__init__.py +++ b/core/src/bugis/core/__init__.py @@ -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__ = [ diff --git a/core/src/bugis/core/_app.py b/core/src/bugis/core/_app.py index f69c2b9..2b3089b 100644 --- a/core/src/bugis/core/_app.py +++ b/core/src/bugis/core/_app.py @@ -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 diff --git a/core/src/bugis/core/_node.py b/core/src/bugis/core/_node.py index 741df27..afafaa2 100644 --- a/core/src/bugis/core/_node.py +++ b/core/src/bugis/core/_node.py @@ -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'] diff --git a/core/src/bugis/core/_path_handler.py b/core/src/bugis/core/_path_handler.py index ea9240a..9ba6aa0 100644 --- a/core/src/bugis/core/_path_handler.py +++ b/core/src/bugis/core/_path_handler.py @@ -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]) diff --git a/core/src/bugis/core/_path_matcher.py b/core/src/bugis/core/_path_matcher.py index 724074d..1e94aad 100644 --- a/core/src/bugis/core/_path_matcher.py +++ b/core/src/bugis/core/_path_matcher.py @@ -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 diff --git a/core/src/bugis/core/_tree.py b/core/src/bugis/core/_tree.py index 813b7d2..bc72d69 100644 --- a/core/src/bugis/core/_tree.py +++ b/core/src/bugis/core/_tree.py @@ -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() diff --git a/core/src/bugis/core/_types/__init__.py b/core/src/bugis/core/_types/__init__.py index eae7b4d..73fc6b0 100644 --- a/core/src/bugis/core/_types/__init__.py +++ b/core/src/bugis/core/_types/__init__.py @@ -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' +]