This commit is contained in:
Adam Hopkins 2021-02-04 00:42:24 +02:00
parent 967c4e6a4e
commit a434ffa8b7
8 changed files with 124 additions and 64 deletions

View File

@ -16,7 +16,7 @@ from urllib.parse import urlencode, urlunparse
from sanic_routing.route import Route from sanic_routing.route import Route
from sanic import reloader_helpers from sanic import reloader_helpers, websocket
from sanic.asgi import ASGIApp from sanic.asgi import ASGIApp
from sanic.base import BaseSanic from sanic.base import BaseSanic
from sanic.blueprint_group import BlueprintGroup from sanic.blueprint_group import BlueprintGroup
@ -224,7 +224,26 @@ class Sanic(BaseSanic):
return self.register_listener(listener.listener, listener.event) return self.register_listener(listener.listener, listener.event)
def _apply_route(self, route: FutureRoute) -> Route: def _apply_route(self, route: FutureRoute) -> Route:
return self.router.add(**route._asdict()) # TODO:
# - move websocket handler out and attach it when applying
params = route._asdict()
websocket = params.pop("websocket", False)
subprotocols = params.pop("subprotocols", None)
if websocket:
self.enable_websocket()
websocket_handler = partial(
self._websocket_handler,
route.handler,
subprotocols=subprotocols,
)
websocket_handler.__name__ = (
"websocket_handler_" + route.handler.__name__
)
websocket_handler.is_websocket = True
params["handler"] = websocket_handler
return self.router.add(**params)
def _apply_static(self, static: FutureStatic) -> Route: def _apply_static(self, static: FutureStatic) -> Route:
return static_register(self, static) return static_register(self, static)
@ -339,7 +358,7 @@ class Sanic(BaseSanic):
out = uri out = uri
# find all the parameters we will need to build in the URL # find all the parameters we will need to build in the URL
matched_params = re.findall(self.router.parameter_pattern, uri) # matched_params = re.findall(self.router.parameter_pattern, uri)
# _method is only a placeholder now, don't know how to support it # _method is only a placeholder now, don't know how to support it
kwargs.pop("_method", None) kwargs.pop("_method", None)
@ -364,45 +383,45 @@ class Sanic(BaseSanic):
if "://" in netloc[:8]: if "://" in netloc[:8]:
netloc = netloc.split("://", 1)[-1] netloc = netloc.split("://", 1)[-1]
for match in matched_params: # for match in matched_params:
name, _type, pattern = self.router.parse_parameter_string(match) # name, _type, pattern = self.router.parse_parameter_string(match)
# we only want to match against each individual parameter # # we only want to match against each individual parameter
specific_pattern = f"^{pattern}$" # specific_pattern = f"^{pattern}$"
supplied_param = None # supplied_param = None
if name in kwargs: # if name in kwargs:
supplied_param = kwargs.get(name) # supplied_param = kwargs.get(name)
del kwargs[name] # del kwargs[name]
else: # else:
raise URLBuildError( # raise URLBuildError(
f"Required parameter `{name}` was not passed to url_for" # f"Required parameter `{name}` was not passed to url_for"
) # )
supplied_param = str(supplied_param) # supplied_param = str(supplied_param)
# determine if the parameter supplied by the caller passes the test # # determine if the parameter supplied by the caller passes the test
# in the URL # # in the URL
passes_pattern = re.match(specific_pattern, supplied_param) # passes_pattern = re.match(specific_pattern, supplied_param)
if not passes_pattern: # if not passes_pattern:
if _type != str: # if _type != str:
type_name = _type.__name__ # type_name = _type.__name__
msg = ( # msg = (
f'Value "{supplied_param}" ' # f'Value "{supplied_param}" '
f"for parameter `{name}` does not " # f"for parameter `{name}` does not "
f"match pattern for type `{type_name}`: {pattern}" # f"match pattern for type `{type_name}`: {pattern}"
) # )
else: # else:
msg = ( # msg = (
f'Value "{supplied_param}" for parameter `{name}` ' # f'Value "{supplied_param}" for parameter `{name}` '
f"does not satisfy pattern {pattern}" # f"does not satisfy pattern {pattern}"
) # )
raise URLBuildError(msg) # raise URLBuildError(msg)
# replace the parameter in the URL with the supplied value # # replace the parameter in the URL with the supplied value
replacement_regex = f"(<{name}.*?>)" # replacement_regex = f"(<{name}.*?>)"
out = re.sub(replacement_regex, supplied_param, out) # out = re.sub(replacement_regex, supplied_param, out)
# parse the remainder of the keyword arguments into a querystring # parse the remainder of the keyword arguments into a querystring
query_string = urlencode(kwargs, doseq=True) if kwargs else "" query_string = urlencode(kwargs, doseq=True) if kwargs else ""
@ -826,6 +845,9 @@ class Sanic(BaseSanic):
await result await result
async def _run_request_middleware(self, request, request_name=None): async def _run_request_middleware(self, request, request_name=None):
print(self.request_middleware)
print(self.named_request_middleware)
print(request_name)
# The if improves speed. I don't know why # The if improves speed. I don't know why
named_middleware = self.named_request_middleware.get( named_middleware = self.named_request_middleware.get(
request_name, deque() request_name, deque()

View File

@ -119,6 +119,8 @@ class Blueprint(BaseSanic):
future.version or self.version, future.version or self.version,
future.name, future.name,
future.ignore_body, future.ignore_body,
future.websocket,
future.subprotocols,
) )
route = app._apply_route(apply_route) route = app._apply_route(apply_route)
@ -136,6 +138,7 @@ class Blueprint(BaseSanic):
route_names = [route.name for route in routes if route] route_names = [route.name for route in routes if route]
# Middleware # Middleware
if route_names:
for future in self._future_middleware: for future in self._future_middleware:
app._apply_middleware(future, route_names) app._apply_middleware(future, route_names)

View File

@ -1,6 +1,6 @@
from enum import Enum, auto from enum import Enum, auto
from functools import partial from functools import partial
from typing import Set from typing import List
from sanic.models.futures import FutureListener from sanic.models.futures import FutureListener
@ -17,7 +17,7 @@ class ListenerEvent(str, Enum):
class ListenerMixin: class ListenerMixin:
def __init__(self, *args, **kwargs) -> None: def __init__(self, *args, **kwargs) -> None:
self._future_listeners: Set[FutureListener] = set() self._future_listeners: List[FutureListener] = list()
def _apply_listener(self, listener: FutureListener): def _apply_listener(self, listener: FutureListener):
raise NotImplementedError raise NotImplementedError
@ -32,7 +32,7 @@ class ListenerMixin:
nonlocal apply nonlocal apply
future_listener = FutureListener(listener, event) future_listener = FutureListener(listener, event)
self._future_listeners.add(future_listener) self._future_listeners.append(future_listener)
if apply: if apply:
self._apply_listener(future_listener) self._apply_listener(future_listener)
return listener return listener

View File

@ -1,12 +1,12 @@
from functools import partial from functools import partial
from typing import Set from typing import List
from sanic.models.futures import FutureMiddleware from sanic.models.futures import FutureMiddleware
class MiddlewareMixin: class MiddlewareMixin:
def __init__(self, *args, **kwargs) -> None: def __init__(self, *args, **kwargs) -> None:
self._future_middleware: Set[FutureMiddleware] = set() self._future_middleware: List[FutureMiddleware] = list()
def _apply_middleware(self, middleware: FutureMiddleware): def _apply_middleware(self, middleware: FutureMiddleware):
raise NotImplementedError raise NotImplementedError
@ -27,7 +27,7 @@ class MiddlewareMixin:
nonlocal apply nonlocal apply
future_middleware = FutureMiddleware(middleware, attach_to) future_middleware = FutureMiddleware(middleware, attach_to)
self._future_middleware.add(future_middleware) self._future_middleware.append(future_middleware)
if apply: if apply:
self._apply_middleware(future_middleware) self._apply_middleware(future_middleware)
return middleware return middleware

View File

@ -52,11 +52,6 @@ class RouteMixin:
of type :class:`FutureRoute` of type :class:`FutureRoute`
""" """
# TODO:
# - run when applying future, not here
if websocket:
self.enable_websocket()
# Fix case where the user did not prefix the URL with a / # Fix case where the user did not prefix the URL with a /
# and will probably get confused as to why it's not working # and will probably get confused as to why it's not working
if not uri.startswith("/"): if not uri.startswith("/"):
@ -85,20 +80,6 @@ class RouteMixin:
# variable will be a tuple of (existing routes, handler fn) # variable will be a tuple of (existing routes, handler fn)
_, handler = handler _, handler = handler
# TODO:
# - move websocket handler out and attach it when applying
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
# TODO: # TODO:
# - THink this thru.... do we want all routes namespaced? # - THink this thru.... do we want all routes namespaced?
# - # -
@ -119,6 +100,8 @@ class RouteMixin:
version, version,
name, name,
ignore_body, ignore_body,
websocket,
subprotocols,
) )
self._future_routes.add(route) self._future_routes.add(route)

View File

@ -13,6 +13,8 @@ FutureRoute = namedtuple(
"version", "version",
"name", "name",
"ignore_body", "ignore_body",
"websocket",
"subprotocols",
], ],
) )
FutureListener = namedtuple("FutureListener", ["listener", "event"]) FutureListener = namedtuple("FutureListener", ["listener", "event"])

View File

@ -1,7 +1,7 @@
from functools import lru_cache from functools import lru_cache
from typing import FrozenSet, Iterable, List, Optional, Union from typing import FrozenSet, Iterable, List, Optional, Union
from sanic_routing import BaseRouter from sanic_routing import BaseRouter, route
from sanic_routing.exceptions import NoMethod from sanic_routing.exceptions import NoMethod
from sanic_routing.exceptions import NotFound as RoutingNotFound from sanic_routing.exceptions import NotFound as RoutingNotFound
from sanic_routing.route import Route from sanic_routing.route import Route
@ -157,3 +157,26 @@ class Router(BaseRouter):
): ):
handler = getattr(handler.view_class, request.method.lower()) handler = getattr(handler.view_class, request.method.lower())
return hasattr(handler, "is_stream") return hasattr(handler, "is_stream")
# @lru_cache(maxsize=ROUTER_CACHE_SIZE)
def find_route_by_view_name(self, view_name, name=None):
"""
Find a route in the router based on the specified view name.
:param view_name: string of view name to search by
:param kwargs: additional params, usually for static files
:return: tuple containing (uri, Route)
"""
if not view_name:
return None, None
if view_name == "static" or view_name.endswith(".static"):
looking_for = f"_static_{name}"
route = self.name_index.get(looking_for)
else:
route = self.name_index.get(view_name)
if not route:
return None, None
return route.path, route

View File

@ -353,6 +353,29 @@ def test_bp_middleware(app):
assert response.text == "FAIL" assert response.text == "FAIL"
def test_bp_middleware_with_route(app):
blueprint = Blueprint("test_bp_middleware")
@blueprint.middleware("response")
async def process_response(request, response):
return text("OK")
@app.route("/")
async def handler(request):
return text("FAIL")
@blueprint.route("/bp")
async def bp_handler(request):
return text("FAIL")
app.blueprint(blueprint)
request, response = app.test_client.get("/bp")
assert response.status == 200
assert response.text == "OK"
def test_bp_middleware_order(app): def test_bp_middleware_order(app):
blueprint = Blueprint("test_bp_middleware_order") blueprint = Blueprint("test_bp_middleware_order")
order = list() order = list()
@ -715,6 +738,9 @@ def test_static_blueprint_name(app: Sanic, static_file_directory, file_name):
) )
app.blueprint(bp) app.blueprint(bp)
print(app.router.name_index)
print(app.router.static_routes)
print(app.router.dynamic_routes)
uri = app.url_for("static", name="static.testing") uri = app.url_for("static", name="static.testing")
assert uri == "/static/test.file" assert uri == "/static/test.file"
@ -825,6 +851,7 @@ def test_strict_slashes_behavior_adoption(app):
assert app.test_client.get("/test")[1].status == 200 assert app.test_client.get("/test")[1].status == 200
assert app.test_client.get("/test/")[1].status == 404 assert app.test_client.get("/test/")[1].status == 404
app.router.finalized = False
bp = Blueprint("bp") bp = Blueprint("bp")
@bp.get("/one", strict_slashes=False) @bp.get("/one", strict_slashes=False)