interim
This commit is contained in:
parent
967c4e6a4e
commit
a434ffa8b7
94
sanic/app.py
94
sanic/app.py
@ -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()
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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"])
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user