Merge in latest from sanic-routing branch
This commit is contained in:
@@ -1,19 +0,0 @@
|
||||
class Base(type):
|
||||
def __new__(cls, name, bases, attrs):
|
||||
init = attrs.get("__init__")
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
nonlocal init
|
||||
for base in type(self).__bases__:
|
||||
if base.__name__ != "BaseMixin":
|
||||
base.__init__(self, *args, **kwargs)
|
||||
|
||||
if init:
|
||||
init(self, *args, **kwargs)
|
||||
|
||||
attrs["__init__"] = __init__
|
||||
return type.__new__(cls, name, bases, attrs)
|
||||
|
||||
|
||||
class BaseMixin(metaclass=Base):
|
||||
...
|
||||
@@ -1,5 +1,3 @@
|
||||
from enum import Enum, auto
|
||||
from functools import partial
|
||||
from typing import Set
|
||||
|
||||
from sanic.models.futures import FutureException
|
||||
@@ -29,6 +27,9 @@ class ExceptionMixin:
|
||||
nonlocal apply
|
||||
nonlocal exceptions
|
||||
|
||||
if isinstance(exceptions[0], list):
|
||||
exceptions = tuple(*exceptions)
|
||||
|
||||
future_exception = FutureException(handler, exceptions)
|
||||
self._future_exceptions.add(future_exception)
|
||||
if apply:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from enum import Enum, auto
|
||||
from functools import partial
|
||||
from typing import Any, Callable, Coroutine, Optional, Set, Union
|
||||
from typing import Any, Callable, Coroutine, List, Optional, Set, Union
|
||||
|
||||
from sanic.models.futures import FutureListener
|
||||
|
||||
@@ -17,7 +17,7 @@ class ListenerEvent(str, Enum):
|
||||
|
||||
class ListenerMixin:
|
||||
def __init__(self, *args, **kwargs) -> None:
|
||||
self._future_listeners: Set[FutureListener] = set()
|
||||
self._future_listeners: List[FutureListener] = list()
|
||||
|
||||
def _apply_listener(self, listener: FutureListener):
|
||||
raise NotImplementedError
|
||||
@@ -51,7 +51,7 @@ class ListenerMixin:
|
||||
nonlocal apply
|
||||
|
||||
future_listener = FutureListener(listener, event)
|
||||
self._future_listeners.add(future_listener)
|
||||
self._future_listeners.append(future_listener)
|
||||
if apply:
|
||||
self._apply_listener(future_listener)
|
||||
return listener
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
from functools import partial
|
||||
from typing import Set
|
||||
from typing import List
|
||||
|
||||
from sanic.models.futures import FutureMiddleware
|
||||
|
||||
|
||||
class MiddlewareMixin:
|
||||
def __init__(self, *args, **kwargs) -> None:
|
||||
self._future_middleware: Set[FutureMiddleware] = set()
|
||||
self._future_middleware: List[FutureMiddleware] = list()
|
||||
|
||||
def _apply_middleware(self, middleware: FutureMiddleware):
|
||||
raise NotImplementedError
|
||||
@@ -30,7 +30,7 @@ class MiddlewareMixin:
|
||||
nonlocal apply
|
||||
|
||||
future_middleware = FutureMiddleware(middleware, attach_to)
|
||||
self._future_middleware.add(future_middleware)
|
||||
self._future_middleware.append(future_middleware)
|
||||
if apply:
|
||||
self._apply_middleware(future_middleware)
|
||||
return middleware
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
from functools import partial
|
||||
from inspect import signature
|
||||
from pathlib import PurePath
|
||||
from typing import Iterable, List, Optional, Set, Union
|
||||
|
||||
from sanic_routing.route import Route
|
||||
from sanic_routing.route import Route # type: ignore
|
||||
|
||||
from sanic.constants import HTTP_METHODS
|
||||
from sanic.models.futures import FutureRoute, FutureStatic
|
||||
@@ -36,6 +35,8 @@ class RouteMixin:
|
||||
apply: bool = True,
|
||||
subprotocols: Optional[List[str]] = None,
|
||||
websocket: bool = False,
|
||||
unquote: bool = False,
|
||||
static: bool = False,
|
||||
):
|
||||
"""
|
||||
Decorate a function to be registered as a route
|
||||
@@ -52,9 +53,6 @@ class RouteMixin:
|
||||
:return: tuple of routes, decorated function
|
||||
"""
|
||||
|
||||
if websocket:
|
||||
self.enable_websocket()
|
||||
|
||||
# Fix case where the user did not prefix the URL with a /
|
||||
# and will probably get confused as to why it's not working
|
||||
if not uri.startswith("/"):
|
||||
@@ -63,6 +61,9 @@ class RouteMixin:
|
||||
if strict_slashes is None:
|
||||
strict_slashes = self.strict_slashes
|
||||
|
||||
if not methods and not websocket:
|
||||
methods = frozenset({"GET"})
|
||||
|
||||
def decorator(handler):
|
||||
nonlocal uri
|
||||
nonlocal methods
|
||||
@@ -74,39 +75,43 @@ class RouteMixin:
|
||||
nonlocal ignore_body
|
||||
nonlocal subprotocols
|
||||
nonlocal websocket
|
||||
nonlocal static
|
||||
|
||||
if isinstance(handler, tuple):
|
||||
# if a handler fn is already wrapped in a route, the handler
|
||||
# variable will be a tuple of (existing routes, handler fn)
|
||||
_, handler = handler
|
||||
|
||||
if websocket:
|
||||
websocket_handler = partial(
|
||||
self._websocket_handler,
|
||||
handler,
|
||||
subprotocols=subprotocols,
|
||||
)
|
||||
websocket_handler.__name__ = (
|
||||
"websocket_handler_" + handler.__name__
|
||||
)
|
||||
websocket_handler.is_websocket = True
|
||||
handler = websocket_handler
|
||||
name = self._generate_name(name, handler)
|
||||
|
||||
# TODO:
|
||||
# - THink this thru.... do we want all routes namespaced?
|
||||
# -
|
||||
name = self._generate_name(handler, name)
|
||||
if isinstance(host, str):
|
||||
host = frozenset([host])
|
||||
elif host and not isinstance(host, frozenset):
|
||||
try:
|
||||
host = frozenset(host)
|
||||
except TypeError:
|
||||
raise ValueError(
|
||||
"Expected either string or Iterable of host strings, "
|
||||
"not %s" % host
|
||||
)
|
||||
|
||||
if isinstance(subprotocols, (list, tuple, set)):
|
||||
subprotocols = frozenset(subprotocols)
|
||||
|
||||
route = FutureRoute(
|
||||
handler,
|
||||
uri,
|
||||
frozenset(methods),
|
||||
None if websocket else frozenset([x.upper() for x in methods]),
|
||||
host,
|
||||
strict_slashes,
|
||||
stream,
|
||||
version,
|
||||
name,
|
||||
ignore_body,
|
||||
websocket,
|
||||
subprotocols,
|
||||
unquote,
|
||||
static,
|
||||
)
|
||||
|
||||
self._future_routes.add(route)
|
||||
@@ -441,6 +446,7 @@ class RouteMixin:
|
||||
subprotocols: Optional[List[str]] = None,
|
||||
version: Optional[int] = None,
|
||||
name: Optional[str] = None,
|
||||
apply: bool = True,
|
||||
):
|
||||
"""
|
||||
Decorate a function to be registered as a websocket route
|
||||
@@ -543,12 +549,16 @@ class RouteMixin:
|
||||
:rtype: List[sanic.router.Route]
|
||||
"""
|
||||
|
||||
if not name.startswith(self.name + "."):
|
||||
name = f"{self.name}.{name}"
|
||||
name = self._generate_name(name)
|
||||
|
||||
if strict_slashes is None and self.strict_slashes is not None:
|
||||
strict_slashes = self.strict_slashes
|
||||
|
||||
if not isinstance(file_or_directory, (str, bytes, PurePath)):
|
||||
raise ValueError(
|
||||
f"Static route must be a valid path, not {file_or_directory}"
|
||||
)
|
||||
|
||||
static = FutureStatic(
|
||||
uri,
|
||||
file_or_directory,
|
||||
@@ -566,5 +576,29 @@ class RouteMixin:
|
||||
if apply:
|
||||
self._apply_static(static)
|
||||
|
||||
def _generate_name(self, handler, name: str) -> str:
|
||||
return name or handler.__name__
|
||||
def _generate_name(self, *objects) -> str:
|
||||
name = None
|
||||
|
||||
for obj in objects:
|
||||
if obj:
|
||||
if isinstance(obj, str):
|
||||
name = obj
|
||||
break
|
||||
|
||||
try:
|
||||
name = obj.name
|
||||
except AttributeError:
|
||||
try:
|
||||
name = obj.__name__
|
||||
except AttributeError:
|
||||
continue
|
||||
else:
|
||||
break
|
||||
|
||||
if not name:
|
||||
raise Exception("...")
|
||||
|
||||
if not name.startswith(f"{self.name}."):
|
||||
name = f"{self.name}.{name}"
|
||||
|
||||
return name
|
||||
|
||||
Reference in New Issue
Block a user