Adding allow route overwrite option in blueprint (#2716)
* Adding allow route overwrite option * Add test case for route overwriting after bp copy * Fix test * Fix * Add test case `test_bp_allow_override` * Remove conflicted future routes when overwriting is allowed * Improved test test_bp_copy_with_route_overwriting * Fix type * Fix type 2 * Add `test_bp_copy_without_route_overwriting` case * make `allow_route_overwrite` flag to be internal * Remove unwanted test case --------- Co-authored-by: Adam Hopkins <adam@amhopkins.com>
This commit is contained in:
parent
4068a0d83d
commit
e374409567
|
@ -417,8 +417,11 @@ class Sanic(StaticHandleMixin, BaseSanic, StartupMixin, metaclass=TouchUpMeta):
|
||||||
def _apply_listener(self, listener: FutureListener):
|
def _apply_listener(self, listener: FutureListener):
|
||||||
return self.register_listener(listener.listener, listener.event)
|
return self.register_listener(listener.listener, listener.event)
|
||||||
|
|
||||||
def _apply_route(self, route: FutureRoute) -> List[Route]:
|
def _apply_route(
|
||||||
|
self, route: FutureRoute, overwrite: bool = False
|
||||||
|
) -> List[Route]:
|
||||||
params = route._asdict()
|
params = route._asdict()
|
||||||
|
params["overwrite"] = overwrite
|
||||||
websocket = params.pop("websocket", False)
|
websocket = params.pop("websocket", False)
|
||||||
subprotocols = params.pop("subprotocols", None)
|
subprotocols = params.pop("subprotocols", None)
|
||||||
|
|
||||||
|
|
|
@ -93,6 +93,7 @@ class Blueprint(BaseSanic):
|
||||||
"_future_listeners",
|
"_future_listeners",
|
||||||
"_future_exceptions",
|
"_future_exceptions",
|
||||||
"_future_signals",
|
"_future_signals",
|
||||||
|
"_allow_route_overwrite",
|
||||||
"copied_from",
|
"copied_from",
|
||||||
"ctx",
|
"ctx",
|
||||||
"exceptions",
|
"exceptions",
|
||||||
|
@ -119,6 +120,7 @@ class Blueprint(BaseSanic):
|
||||||
):
|
):
|
||||||
super().__init__(name=name)
|
super().__init__(name=name)
|
||||||
self.reset()
|
self.reset()
|
||||||
|
self._allow_route_overwrite = False
|
||||||
self.copied_from = ""
|
self.copied_from = ""
|
||||||
self.ctx = SimpleNamespace()
|
self.ctx = SimpleNamespace()
|
||||||
self.host = host
|
self.host = host
|
||||||
|
@ -169,6 +171,7 @@ class Blueprint(BaseSanic):
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
self._apps: Set[Sanic] = set()
|
self._apps: Set[Sanic] = set()
|
||||||
|
self._allow_route_overwrite = False
|
||||||
self.exceptions: List[RouteHandler] = []
|
self.exceptions: List[RouteHandler] = []
|
||||||
self.listeners: Dict[str, List[ListenerType[Any]]] = {}
|
self.listeners: Dict[str, List[ListenerType[Any]]] = {}
|
||||||
self.middlewares: List[MiddlewareType] = []
|
self.middlewares: List[MiddlewareType] = []
|
||||||
|
@ -182,6 +185,7 @@ class Blueprint(BaseSanic):
|
||||||
url_prefix: Optional[Union[str, Default]] = _default,
|
url_prefix: Optional[Union[str, Default]] = _default,
|
||||||
version: Optional[Union[int, str, float, Default]] = _default,
|
version: Optional[Union[int, str, float, Default]] = _default,
|
||||||
version_prefix: Union[str, Default] = _default,
|
version_prefix: Union[str, Default] = _default,
|
||||||
|
allow_route_overwrite: Union[bool, Default] = _default,
|
||||||
strict_slashes: Optional[Union[bool, Default]] = _default,
|
strict_slashes: Optional[Union[bool, Default]] = _default,
|
||||||
with_registration: bool = True,
|
with_registration: bool = True,
|
||||||
with_ctx: bool = False,
|
with_ctx: bool = False,
|
||||||
|
@ -225,6 +229,8 @@ class Blueprint(BaseSanic):
|
||||||
new_bp.strict_slashes = strict_slashes
|
new_bp.strict_slashes = strict_slashes
|
||||||
if not isinstance(version_prefix, Default):
|
if not isinstance(version_prefix, Default):
|
||||||
new_bp.version_prefix = version_prefix
|
new_bp.version_prefix = version_prefix
|
||||||
|
if not isinstance(allow_route_overwrite, Default):
|
||||||
|
new_bp._allow_route_overwrite = allow_route_overwrite
|
||||||
|
|
||||||
for key, value in attrs_backup.items():
|
for key, value in attrs_backup.items():
|
||||||
setattr(self, key, value)
|
setattr(self, key, value)
|
||||||
|
@ -360,7 +366,9 @@ class Blueprint(BaseSanic):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
registered.add(apply_route)
|
registered.add(apply_route)
|
||||||
route = app._apply_route(apply_route)
|
route = app._apply_route(
|
||||||
|
apply_route, overwrite=self._allow_route_overwrite
|
||||||
|
)
|
||||||
|
|
||||||
# If it is a copied BP, then make sure all of the names of routes
|
# If it is a copied BP, then make sure all of the names of routes
|
||||||
# matchup with the new BP name
|
# matchup with the new BP name
|
||||||
|
|
|
@ -159,7 +159,11 @@ class RouteMixin(BaseMixin, metaclass=SanicMeta):
|
||||||
error_format,
|
error_format,
|
||||||
route_context,
|
route_context,
|
||||||
)
|
)
|
||||||
|
overwrite = getattr(self, "_allow_route_overwrite", False)
|
||||||
|
if overwrite:
|
||||||
|
self._future_routes = set(
|
||||||
|
filter(lambda x: x.uri != uri, self._future_routes)
|
||||||
|
)
|
||||||
self._future_routes.add(route)
|
self._future_routes.add(route)
|
||||||
|
|
||||||
args = list(signature(handler).parameters.keys())
|
args = list(signature(handler).parameters.keys())
|
||||||
|
@ -182,7 +186,7 @@ class RouteMixin(BaseMixin, metaclass=SanicMeta):
|
||||||
handler.is_stream = stream
|
handler.is_stream = stream
|
||||||
|
|
||||||
if apply:
|
if apply:
|
||||||
self._apply_route(route)
|
self._apply_route(route, overwrite=overwrite)
|
||||||
|
|
||||||
if static:
|
if static:
|
||||||
return route, handler
|
return route, handler
|
||||||
|
|
|
@ -80,6 +80,7 @@ class Router(BaseRouter):
|
||||||
unquote: bool = False,
|
unquote: bool = False,
|
||||||
static: bool = False,
|
static: bool = False,
|
||||||
version_prefix: str = "/v",
|
version_prefix: str = "/v",
|
||||||
|
overwrite: bool = False,
|
||||||
error_format: Optional[str] = None,
|
error_format: Optional[str] = None,
|
||||||
) -> Union[Route, List[Route]]:
|
) -> Union[Route, List[Route]]:
|
||||||
"""
|
"""
|
||||||
|
@ -122,6 +123,7 @@ class Router(BaseRouter):
|
||||||
name=name,
|
name=name,
|
||||||
strict=strict_slashes,
|
strict=strict_slashes,
|
||||||
unquote=unquote,
|
unquote=unquote,
|
||||||
|
overwrite=overwrite,
|
||||||
)
|
)
|
||||||
|
|
||||||
if isinstance(host, str):
|
if isinstance(host, str):
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
from sanic import Blueprint, Sanic
|
import pytest
|
||||||
|
|
||||||
|
from sanic_routing.exceptions import RouteExists
|
||||||
|
|
||||||
|
from sanic import Blueprint, Request, Sanic
|
||||||
from sanic.response import text
|
from sanic.response import text
|
||||||
|
|
||||||
|
|
||||||
|
@ -74,3 +78,76 @@ def test_bp_copy(app: Sanic):
|
||||||
assert "test_bp_copy.test_bp4.handle_request" in route_names
|
assert "test_bp_copy.test_bp4.handle_request" in route_names
|
||||||
assert "test_bp_copy.test_bp5.handle_request" in route_names
|
assert "test_bp_copy.test_bp5.handle_request" in route_names
|
||||||
assert "test_bp_copy.test_bp6.handle_request" in route_names
|
assert "test_bp_copy.test_bp6.handle_request" in route_names
|
||||||
|
|
||||||
|
|
||||||
|
def test_bp_copy_without_route_overwriting(app: Sanic):
|
||||||
|
bpv1 = Blueprint("bp_v1", version=1, url_prefix="my_api")
|
||||||
|
|
||||||
|
@bpv1.route("/")
|
||||||
|
async def handler(request: Request):
|
||||||
|
return text("v1")
|
||||||
|
|
||||||
|
app.blueprint(bpv1)
|
||||||
|
|
||||||
|
bpv2 = bpv1.copy("bp_v2", version=2, allow_route_overwrite=False)
|
||||||
|
bpv3 = bpv1.copy(
|
||||||
|
"bp_v3",
|
||||||
|
version=3,
|
||||||
|
allow_route_overwrite=False,
|
||||||
|
with_registration=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
with pytest.raises(RouteExists, match="Route already registered*"):
|
||||||
|
|
||||||
|
@bpv2.route("/")
|
||||||
|
async def handler(request: Request):
|
||||||
|
return text("v2")
|
||||||
|
|
||||||
|
app.blueprint(bpv2)
|
||||||
|
|
||||||
|
with pytest.raises(RouteExists, match="Route already registered*"):
|
||||||
|
|
||||||
|
@bpv3.route("/")
|
||||||
|
async def handler(request: Request):
|
||||||
|
return text("v3")
|
||||||
|
|
||||||
|
app.blueprint(bpv3)
|
||||||
|
|
||||||
|
|
||||||
|
def test_bp_copy_with_route_overwriting(app: Sanic):
|
||||||
|
bpv1 = Blueprint("bp_v1", version=1, url_prefix="my_api")
|
||||||
|
|
||||||
|
@bpv1.route("/")
|
||||||
|
async def handler(request: Request):
|
||||||
|
return text("v1")
|
||||||
|
|
||||||
|
app.blueprint(bpv1)
|
||||||
|
|
||||||
|
bpv2 = bpv1.copy("bp_v2", version=2, allow_route_overwrite=True)
|
||||||
|
bpv3 = bpv1.copy(
|
||||||
|
"bp_v3", version=3, allow_route_overwrite=True, with_registration=False
|
||||||
|
)
|
||||||
|
|
||||||
|
@bpv2.route("/")
|
||||||
|
async def handler(request: Request):
|
||||||
|
return text("v2")
|
||||||
|
|
||||||
|
app.blueprint(bpv2)
|
||||||
|
|
||||||
|
@bpv3.route("/")
|
||||||
|
async def handler(request: Request):
|
||||||
|
return text("v3")
|
||||||
|
|
||||||
|
app.blueprint(bpv3)
|
||||||
|
|
||||||
|
_, response = app.test_client.get("/v1/my_api")
|
||||||
|
assert response.status == 200
|
||||||
|
assert response.text == "v1"
|
||||||
|
|
||||||
|
_, response = app.test_client.get("/v2/my_api")
|
||||||
|
assert response.status == 200
|
||||||
|
assert response.text == "v2"
|
||||||
|
|
||||||
|
_, response = app.test_client.get("/v3/my_api")
|
||||||
|
assert response.status == 200
|
||||||
|
assert response.text == "v3"
|
||||||
|
|
Loading…
Reference in New Issue
Block a user