Resolve some more tests
This commit is contained in:
144
sanic/app.py
144
sanic/app.py
@@ -16,7 +16,7 @@ from urllib.parse import urlencode, urlunparse
|
||||
|
||||
from sanic_routing.route import Route
|
||||
|
||||
from sanic import reloader_helpers, websocket
|
||||
from sanic import reloader_helpers
|
||||
from sanic.asgi import ASGIApp
|
||||
from sanic.base import BaseSanic
|
||||
from sanic.blueprint_group import BlueprintGroup
|
||||
@@ -114,6 +114,8 @@ class Sanic(BaseSanic):
|
||||
if self.config.REGISTER:
|
||||
self.__class__.register_app(self)
|
||||
|
||||
self.router.ctx.app = self
|
||||
|
||||
@property
|
||||
def loop(self):
|
||||
"""Synonymous with asyncio.get_event_loop().
|
||||
@@ -230,7 +232,6 @@ class Sanic(BaseSanic):
|
||||
websocket = params.pop("websocket", False)
|
||||
subprotocols = params.pop("subprotocols", None)
|
||||
|
||||
|
||||
if websocket:
|
||||
self.enable_websocket()
|
||||
websocket_handler = partial(
|
||||
@@ -294,6 +295,12 @@ class Sanic(BaseSanic):
|
||||
else:
|
||||
self.blueprints[blueprint.name] = blueprint
|
||||
self._blueprint_order.append(blueprint)
|
||||
|
||||
if (
|
||||
self.strict_slashes is not None
|
||||
and blueprint.strict_slashes is None
|
||||
):
|
||||
blueprint.strict_slashes = self.strict_slashes
|
||||
blueprint.register(self, options)
|
||||
|
||||
def url_for(self, view_name: str, **kwargs):
|
||||
@@ -319,30 +326,28 @@ class Sanic(BaseSanic):
|
||||
# find the route by the supplied view name
|
||||
kw: Dict[str, str] = {}
|
||||
# special static files url_for
|
||||
if view_name == "static":
|
||||
kw.update(name=kwargs.pop("name", "static"))
|
||||
elif view_name.endswith(".static"): # blueprint.static
|
||||
kwargs.pop("name", None)
|
||||
|
||||
if "." not in view_name:
|
||||
view_name = f"{self.name}.{view_name}"
|
||||
|
||||
if view_name.endswith(".static"):
|
||||
name = kwargs.pop("name", None)
|
||||
if name:
|
||||
view_name = view_name.replace("static", name)
|
||||
kw.update(name=view_name)
|
||||
|
||||
uri, route = self.router.find_route_by_view_name(view_name, **kw)
|
||||
if not (uri and route):
|
||||
route = self.router.find_route_by_view_name(view_name, **kw)
|
||||
if not route:
|
||||
raise URLBuildError(
|
||||
f"Endpoint with name `{view_name}` was not found"
|
||||
)
|
||||
|
||||
# If the route has host defined, split that off
|
||||
# TODO: Retain netloc and path separately in Route objects
|
||||
host = uri.find("/")
|
||||
if host > 0:
|
||||
host, uri = uri[:host], uri[host:]
|
||||
else:
|
||||
host = None
|
||||
uri = route.path
|
||||
|
||||
if view_name == "static" or view_name.endswith(".static"):
|
||||
filename = kwargs.pop("filename", None)
|
||||
if getattr(route.ctx, "static", None):
|
||||
filename = kwargs.pop("filename", "")
|
||||
# it's static folder
|
||||
if "<file_uri:" in uri:
|
||||
if "file_uri" in uri:
|
||||
folder_ = uri.split("<file_uri:", 1)[0]
|
||||
if folder_.endswith("/"):
|
||||
folder_ = folder_[:-1]
|
||||
@@ -350,22 +355,36 @@ class Sanic(BaseSanic):
|
||||
if filename.startswith("/"):
|
||||
filename = filename[1:]
|
||||
|
||||
uri = f"{folder_}/{filename}"
|
||||
kwargs["file_uri"] = filename
|
||||
|
||||
if uri != "/" and uri.endswith("/"):
|
||||
uri = uri[:-1]
|
||||
|
||||
out = uri
|
||||
if not uri.startswith("/"):
|
||||
uri = f"/{uri}"
|
||||
|
||||
# find all the parameters we will need to build in the URL
|
||||
# matched_params = re.findall(self.router.parameter_pattern, uri)
|
||||
out = uri
|
||||
|
||||
# _method is only a placeholder now, don't know how to support it
|
||||
kwargs.pop("_method", None)
|
||||
anchor = kwargs.pop("_anchor", "")
|
||||
# _external need SERVER_NAME in config or pass _server arg
|
||||
external = kwargs.pop("_external", False)
|
||||
host = kwargs.pop("_host", None)
|
||||
external = kwargs.pop("_external", False) or bool(host)
|
||||
scheme = kwargs.pop("_scheme", "")
|
||||
if route.ctx.hosts and external:
|
||||
if not host and len(route.ctx.hosts) > 1:
|
||||
raise ValueError(
|
||||
f"Host is ambiguous: {', '.join(route.ctx.hosts)}"
|
||||
)
|
||||
elif host and host not in route.ctx.hosts:
|
||||
raise ValueError(
|
||||
f"Requested host ({host}) is not available for this "
|
||||
f"route: {route.ctx.hosts}"
|
||||
)
|
||||
elif not host:
|
||||
host = list(route.ctx.hosts)[0]
|
||||
|
||||
if scheme and not external:
|
||||
raise ValueError("When specifying _scheme, _external must be True")
|
||||
|
||||
@@ -383,45 +402,49 @@ class Sanic(BaseSanic):
|
||||
if "://" in netloc[:8]:
|
||||
netloc = netloc.split("://", 1)[-1]
|
||||
|
||||
# for match in matched_params:
|
||||
# name, _type, pattern = self.router.parse_parameter_string(match)
|
||||
# # we only want to match against each individual parameter
|
||||
# specific_pattern = f"^{pattern}$"
|
||||
# supplied_param = None
|
||||
# find all the parameters we will need to build in the URL
|
||||
# matched_params = re.findall(self.router.parameter_pattern, uri)
|
||||
route.finalize_params()
|
||||
for params in route.params.values():
|
||||
# name, _type, pattern = self.router.parse_parameter_string(match)
|
||||
# we only want to match against each individual parameter
|
||||
|
||||
# if name in kwargs:
|
||||
# supplied_param = kwargs.get(name)
|
||||
# del kwargs[name]
|
||||
# else:
|
||||
# raise URLBuildError(
|
||||
# f"Required parameter `{name}` was not passed to url_for"
|
||||
# )
|
||||
for idx, param_info in enumerate(params):
|
||||
try:
|
||||
supplied_param = str(kwargs.pop(param_info.name))
|
||||
except KeyError:
|
||||
raise URLBuildError(
|
||||
f"Required parameter `{param_info.name}` was not "
|
||||
"passed to url_for"
|
||||
)
|
||||
|
||||
# supplied_param = str(supplied_param)
|
||||
# # determine if the parameter supplied by the caller passes the test
|
||||
# # in the URL
|
||||
# passes_pattern = re.match(specific_pattern, supplied_param)
|
||||
# determine if the parameter supplied by the caller
|
||||
# passes the test in the URL
|
||||
if param_info.pattern:
|
||||
passes_pattern = param_info.pattern.match(supplied_param)
|
||||
if not passes_pattern:
|
||||
if idx + 1 == len(params):
|
||||
if param_info.cast != str:
|
||||
msg = (
|
||||
f'Value "{supplied_param}" '
|
||||
f"for parameter `{param_info.name}` does "
|
||||
"not match pattern for type "
|
||||
f"`{param_info.cast.__name__}`: "
|
||||
f"{param_info.pattern.pattern}"
|
||||
)
|
||||
else:
|
||||
msg = (
|
||||
f'Value "{supplied_param}" for parameter '
|
||||
f"`{param_info.name}` does not satisfy "
|
||||
f"pattern {param_info.pattern.pattern}"
|
||||
)
|
||||
raise URLBuildError(msg)
|
||||
else:
|
||||
continue
|
||||
|
||||
# if not passes_pattern:
|
||||
# if _type != str:
|
||||
# type_name = _type.__name__
|
||||
|
||||
# msg = (
|
||||
# f'Value "{supplied_param}" '
|
||||
# f"for parameter `{name}` does not "
|
||||
# f"match pattern for type `{type_name}`: {pattern}"
|
||||
# )
|
||||
# else:
|
||||
# msg = (
|
||||
# f'Value "{supplied_param}" for parameter `{name}` '
|
||||
# f"does not satisfy pattern {pattern}"
|
||||
# )
|
||||
# raise URLBuildError(msg)
|
||||
|
||||
# # replace the parameter in the URL with the supplied value
|
||||
# replacement_regex = f"(<{name}.*?>)"
|
||||
|
||||
# out = re.sub(replacement_regex, supplied_param, out)
|
||||
# replace the parameter in the URL with the supplied value
|
||||
replacement_regex = f"(<{param_info.name}.*?>)"
|
||||
out = re.sub(replacement_regex, supplied_param, out)
|
||||
|
||||
# parse the remainder of the keyword arguments into a querystring
|
||||
query_string = urlencode(kwargs, doseq=True) if kwargs else ""
|
||||
@@ -845,9 +868,6 @@ class Sanic(BaseSanic):
|
||||
await result
|
||||
|
||||
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
|
||||
named_middleware = self.named_request_middleware.get(
|
||||
request_name, deque()
|
||||
|
||||
@@ -109,22 +109,35 @@ class Blueprint(BaseSanic):
|
||||
# Prepend the blueprint URI prefix if available
|
||||
uri = url_prefix + future.uri if url_prefix else future.uri
|
||||
|
||||
strict_slashes = (
|
||||
self.strict_slashes
|
||||
if future.strict_slashes is None
|
||||
and self.strict_slashes is not None
|
||||
else future.strict_slashes
|
||||
)
|
||||
|
||||
print(uri, strict_slashes)
|
||||
|
||||
apply_route = FutureRoute(
|
||||
future.handler,
|
||||
uri[1:] if uri.startswith("//") else uri,
|
||||
future.methods,
|
||||
future.host or self.host,
|
||||
future.strict_slashes,
|
||||
strict_slashes,
|
||||
future.stream,
|
||||
future.version or self.version,
|
||||
future.name,
|
||||
future.ignore_body,
|
||||
future.websocket,
|
||||
future.subprotocols,
|
||||
future.unquote,
|
||||
future.static,
|
||||
)
|
||||
|
||||
route = app._apply_route(apply_route)
|
||||
operation = routes.extend if isinstance(route, list) else routes.append
|
||||
operation = (
|
||||
routes.extend if isinstance(route, list) else routes.append
|
||||
)
|
||||
operation(route)
|
||||
|
||||
# Static Files
|
||||
@@ -149,6 +162,3 @@ class Blueprint(BaseSanic):
|
||||
# Event listeners
|
||||
for listener in self._future_listeners:
|
||||
app._apply_listener(listener)
|
||||
|
||||
def _generate_name(self, handler, name: str) -> str:
|
||||
return f"{self.name}.{name or handler.__name__}"
|
||||
|
||||
@@ -36,6 +36,8 @@ class RouteMixin:
|
||||
apply=True,
|
||||
subprotocols=None,
|
||||
websocket=False,
|
||||
unquote=False,
|
||||
static=False,
|
||||
):
|
||||
"""Create a blueprint route from a decorated function.
|
||||
|
||||
@@ -74,21 +76,28 @@ 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
|
||||
|
||||
# TODO:
|
||||
# - THink this thru.... do we want all routes namespaced?
|
||||
# -
|
||||
name = self._generate_name(handler, name)
|
||||
name = self._generate_name(name, handler)
|
||||
|
||||
if isinstance(host, str):
|
||||
host = frozenset([host])
|
||||
elif host and not isinstance(host, frozenset):
|
||||
host = frozenset(host)
|
||||
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,
|
||||
@@ -102,6 +111,8 @@ class RouteMixin:
|
||||
ignore_body,
|
||||
websocket,
|
||||
subprotocols,
|
||||
unquote,
|
||||
static,
|
||||
)
|
||||
|
||||
self._future_routes.add(route)
|
||||
@@ -499,12 +510,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,
|
||||
@@ -522,5 +537,25 @@ 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:
|
||||
continue
|
||||
else:
|
||||
break
|
||||
|
||||
if not name:
|
||||
raise Exception("...")
|
||||
|
||||
if not name.startswith(f"{self.name}."):
|
||||
name = f"{self.name}.{name}"
|
||||
|
||||
return name
|
||||
|
||||
@@ -15,6 +15,8 @@ FutureRoute = namedtuple(
|
||||
"ignore_body",
|
||||
"websocket",
|
||||
"subprotocols",
|
||||
"unquote",
|
||||
"static",
|
||||
],
|
||||
)
|
||||
FutureListener = namedtuple("FutureListener", ["listener", "event"])
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from functools import lru_cache
|
||||
from typing import FrozenSet, Iterable, List, Optional, Union
|
||||
|
||||
from sanic_routing import BaseRouter, route
|
||||
from sanic_routing import BaseRouter
|
||||
from sanic_routing.exceptions import NoMethod
|
||||
from sanic_routing.exceptions import NotFound as RoutingNotFound
|
||||
from sanic_routing.route import Route
|
||||
@@ -37,7 +37,7 @@ class Router(BaseRouter):
|
||||
route, handler, params = self.resolve(
|
||||
path=request.path,
|
||||
method=request.method,
|
||||
extra={"host": request.headers.get("host")}
|
||||
extra={"host": request.headers.get("host")},
|
||||
)
|
||||
except RoutingNotFound as e:
|
||||
raise NotFound("Requested URL {} not found".format(e.path))
|
||||
@@ -75,6 +75,8 @@ class Router(BaseRouter):
|
||||
ignore_body: bool = False,
|
||||
version: Union[str, float, int] = None,
|
||||
name: Optional[str] = None,
|
||||
unquote: bool = False,
|
||||
static: bool = False,
|
||||
) -> Union[Route, List[Route]]:
|
||||
"""
|
||||
Add a handler to the router
|
||||
@@ -118,6 +120,7 @@ class Router(BaseRouter):
|
||||
methods=methods,
|
||||
name=name,
|
||||
strict=strict_slashes,
|
||||
unquote=unquote,
|
||||
)
|
||||
|
||||
if isinstance(host, str):
|
||||
@@ -134,6 +137,8 @@ class Router(BaseRouter):
|
||||
route = super().add(**params)
|
||||
route.ctx.ignore_body = ignore_body
|
||||
route.ctx.stream = stream
|
||||
route.ctx.hosts = hosts
|
||||
route.ctx.static = static
|
||||
|
||||
routes.append(route)
|
||||
|
||||
@@ -168,15 +173,19 @@ class Router(BaseRouter):
|
||||
:return: tuple containing (uri, Route)
|
||||
"""
|
||||
if not view_name:
|
||||
return None, None
|
||||
return 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)
|
||||
name = self.ctx.app._generate_name(view_name)
|
||||
route = self.name_index.get(name)
|
||||
|
||||
if not route:
|
||||
return None, None
|
||||
return None
|
||||
|
||||
return route.path, route
|
||||
return route
|
||||
|
||||
@property
|
||||
def routes_all(self):
|
||||
return {
|
||||
**self.static_routes,
|
||||
**self.dynamic_routes,
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ from re import sub
|
||||
from time import gmtime, strftime
|
||||
from urllib.parse import unquote
|
||||
|
||||
from sanic_routing.patterns import REGEX_TYPES
|
||||
|
||||
from sanic.compat import stat_async
|
||||
from sanic.exceptions import (
|
||||
ContentRangeError,
|
||||
@@ -157,11 +159,11 @@ def register(
|
||||
# If we're not trying to match a file directly,
|
||||
# serve from the folder
|
||||
if not path.isfile(file_or_directory):
|
||||
uri += "<file_uri:" + static.pattern + ">"
|
||||
uri += "/<file_uri:path>"
|
||||
|
||||
# special prefix for static files
|
||||
if not static.name.startswith("_static_"):
|
||||
name = f"_static_{static.name}"
|
||||
# if not static.name.startswith("_static_"):
|
||||
# name = f"_static_{static.name}"
|
||||
|
||||
_handler = wraps(_static_request_handler)(
|
||||
partial(
|
||||
@@ -174,11 +176,13 @@ def register(
|
||||
)
|
||||
)
|
||||
|
||||
_routes, _ = app.route(
|
||||
route, _ = app.route(
|
||||
uri=uri,
|
||||
methods=["GET", "HEAD"],
|
||||
name=name,
|
||||
host=static.host,
|
||||
strict_slashes=static.strict_slashes,
|
||||
static=True,
|
||||
)(_handler)
|
||||
return _routes
|
||||
|
||||
return route
|
||||
|
||||
Reference in New Issue
Block a user