Fixed mypy
This commit is contained in:
@@ -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__ = [
|
||||||
|
@@ -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,17 +102,20 @@ 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,)
|
||||||
if isinstance(paths, str):
|
|
||||||
paths = (paths,)
|
|
||||||
for method in methods:
|
|
||||||
if isinstance(paths, str):
|
|
||||||
self._tree.register(paths, method, handler, recursive)
|
|
||||||
else:
|
else:
|
||||||
for path in paths:
|
_methods = tuple(methods)
|
||||||
|
_paths: Tuple[str, ...]
|
||||||
|
if isinstance(paths, str):
|
||||||
|
_paths = (paths,)
|
||||||
|
else:
|
||||||
|
_paths = tuple(paths)
|
||||||
|
for method in _methods:
|
||||||
|
for path in _paths:
|
||||||
self._tree.register(path, method, handler, recursive)
|
self._tree.register(path, method, handler, recursive)
|
||||||
return handler
|
return handler
|
||||||
|
|
||||||
|
@@ -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']
|
|
||||||
|
@@ -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])
|
||||||
|
@@ -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
|
||||||
|
@@ -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()
|
||||||
|
@@ -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'
|
||||||
|
]
|
||||||
|
Reference in New Issue
Block a user