Version prefix (#2137)
* Add version prefixing * Versioning tests * Testing BP group properties
This commit is contained in:
parent
28ba8e53df
commit
3a6fac7d59
|
@ -58,13 +58,20 @@ class BlueprintGroup(MutableSequence):
|
|||
app.blueprint(bpg)
|
||||
"""
|
||||
|
||||
__slots__ = ("_blueprints", "_url_prefix", "_version", "_strict_slashes")
|
||||
__slots__ = (
|
||||
"_blueprints",
|
||||
"_url_prefix",
|
||||
"_version",
|
||||
"_strict_slashes",
|
||||
"_version_prefix",
|
||||
)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
url_prefix: Optional[str] = None,
|
||||
version: Optional[Union[int, str, float]] = None,
|
||||
strict_slashes: Optional[bool] = None,
|
||||
version_prefix: str = "/v",
|
||||
):
|
||||
"""
|
||||
Create a new Blueprint Group
|
||||
|
@ -77,6 +84,7 @@ class BlueprintGroup(MutableSequence):
|
|||
self._blueprints: List[Blueprint] = []
|
||||
self._url_prefix = url_prefix
|
||||
self._version = version
|
||||
self._version_prefix = version_prefix
|
||||
self._strict_slashes = strict_slashes
|
||||
|
||||
@property
|
||||
|
@ -116,6 +124,15 @@ class BlueprintGroup(MutableSequence):
|
|||
"""
|
||||
return self._strict_slashes
|
||||
|
||||
@property
|
||||
def version_prefix(self) -> str:
|
||||
"""
|
||||
Version prefix; defaults to ``/v``
|
||||
|
||||
:return: str
|
||||
"""
|
||||
return self._version_prefix
|
||||
|
||||
def __iter__(self):
|
||||
"""
|
||||
Tun the class Blueprint Group into an Iterable item
|
||||
|
@ -186,6 +203,9 @@ class BlueprintGroup(MutableSequence):
|
|||
for _attr in ["version", "strict_slashes"]:
|
||||
if getattr(bp, _attr) is None:
|
||||
setattr(bp, _attr, getattr(self, _attr))
|
||||
if bp.version_prefix == "/v":
|
||||
bp.version_prefix = self._version_prefix
|
||||
|
||||
return bp
|
||||
|
||||
def append(self, value: "sanic.Blueprint") -> None:
|
||||
|
|
|
@ -72,6 +72,7 @@ class Blueprint(BaseSanic):
|
|||
host: Optional[str] = None,
|
||||
version: Optional[Union[int, str, float]] = None,
|
||||
strict_slashes: Optional[bool] = None,
|
||||
version_prefix: str = "/v",
|
||||
):
|
||||
super().__init__()
|
||||
|
||||
|
@ -91,6 +92,7 @@ class Blueprint(BaseSanic):
|
|||
else url_prefix
|
||||
)
|
||||
self.version = version
|
||||
self.version_prefix = version_prefix
|
||||
self.websocket_routes: List[Route] = []
|
||||
|
||||
def __repr__(self) -> str:
|
||||
|
@ -143,7 +145,13 @@ class Blueprint(BaseSanic):
|
|||
return super().signal(event, *args, **kwargs)
|
||||
|
||||
@staticmethod
|
||||
def group(*blueprints, url_prefix="", version=None, strict_slashes=None):
|
||||
def group(
|
||||
*blueprints,
|
||||
url_prefix="",
|
||||
version=None,
|
||||
strict_slashes=None,
|
||||
version_prefix: str = "/v",
|
||||
):
|
||||
"""
|
||||
Create a list of blueprints, optionally grouping them under a
|
||||
general URL prefix.
|
||||
|
@ -169,6 +177,7 @@ class Blueprint(BaseSanic):
|
|||
url_prefix=url_prefix,
|
||||
version=version,
|
||||
strict_slashes=strict_slashes,
|
||||
version_prefix=version_prefix,
|
||||
)
|
||||
for bp in chain(blueprints):
|
||||
bps.append(bp)
|
||||
|
@ -186,6 +195,8 @@ class Blueprint(BaseSanic):
|
|||
|
||||
self._apps.add(app)
|
||||
url_prefix = options.get("url_prefix", self.url_prefix)
|
||||
opt_version = options.get("version", None)
|
||||
opt_version_prefix = options.get("version_prefix", self.version_prefix)
|
||||
|
||||
routes = []
|
||||
middleware = []
|
||||
|
@ -200,6 +211,21 @@ class Blueprint(BaseSanic):
|
|||
# Prepend the blueprint URI prefix if available
|
||||
uri = url_prefix + future.uri if url_prefix else future.uri
|
||||
|
||||
version_prefix = self.version_prefix
|
||||
for prefix in (
|
||||
future.version_prefix,
|
||||
opt_version_prefix,
|
||||
):
|
||||
if prefix and prefix != "/v":
|
||||
version_prefix = prefix
|
||||
break
|
||||
|
||||
version = self.version
|
||||
for v in (future.version, opt_version, self.version):
|
||||
if v is not None:
|
||||
version = v
|
||||
break
|
||||
|
||||
strict_slashes = (
|
||||
self.strict_slashes
|
||||
if future.strict_slashes is None
|
||||
|
@ -215,13 +241,14 @@ class Blueprint(BaseSanic):
|
|||
future.host or self.host,
|
||||
strict_slashes,
|
||||
future.stream,
|
||||
future.version or self.version,
|
||||
version,
|
||||
name,
|
||||
future.ignore_body,
|
||||
future.websocket,
|
||||
future.subprotocols,
|
||||
future.unquote,
|
||||
future.static,
|
||||
version_prefix,
|
||||
)
|
||||
|
||||
route = app._apply_route(apply_route)
|
||||
|
|
|
@ -53,6 +53,7 @@ class RouteMixin:
|
|||
websocket: bool = False,
|
||||
unquote: bool = False,
|
||||
static: bool = False,
|
||||
version_prefix: str = "/v",
|
||||
):
|
||||
"""
|
||||
Decorate a function to be registered as a route
|
||||
|
@ -66,6 +67,8 @@ class RouteMixin:
|
|||
:param name: user defined route name for url_for
|
||||
:param ignore_body: whether the handler should ignore request
|
||||
body (eg. GET requests)
|
||||
:param version_prefix: URL path that should be before the version
|
||||
value; default: ``/v``
|
||||
:return: tuple of routes, decorated function
|
||||
"""
|
||||
|
||||
|
@ -92,6 +95,7 @@ class RouteMixin:
|
|||
nonlocal subprotocols
|
||||
nonlocal websocket
|
||||
nonlocal static
|
||||
nonlocal version_prefix
|
||||
|
||||
if isinstance(handler, tuple):
|
||||
# if a handler fn is already wrapped in a route, the handler
|
||||
|
@ -128,6 +132,7 @@ class RouteMixin:
|
|||
subprotocols,
|
||||
unquote,
|
||||
static,
|
||||
version_prefix,
|
||||
)
|
||||
|
||||
self._future_routes.add(route)
|
||||
|
@ -168,6 +173,7 @@ class RouteMixin:
|
|||
version: Optional[int] = None,
|
||||
name: Optional[str] = None,
|
||||
stream: bool = False,
|
||||
version_prefix: str = "/v",
|
||||
):
|
||||
"""A helper method to register class instance or
|
||||
functions as a handler to the application url
|
||||
|
@ -182,6 +188,8 @@ class RouteMixin:
|
|||
:param version:
|
||||
:param name: user defined route name for url_for
|
||||
:param stream: boolean specifying if the handler is a stream handler
|
||||
:param version_prefix: URL path that should be before the version
|
||||
value; default: ``/v``
|
||||
:return: function or class instance
|
||||
"""
|
||||
# Handle HTTPMethodView differently
|
||||
|
@ -214,6 +222,7 @@ class RouteMixin:
|
|||
stream=stream,
|
||||
version=version,
|
||||
name=name,
|
||||
version_prefix=version_prefix,
|
||||
)(handler)
|
||||
return handler
|
||||
|
||||
|
@ -226,6 +235,7 @@ class RouteMixin:
|
|||
version: Optional[int] = None,
|
||||
name: Optional[str] = None,
|
||||
ignore_body: bool = True,
|
||||
version_prefix: str = "/v",
|
||||
):
|
||||
"""
|
||||
Add an API URL under the **GET** *HTTP* method
|
||||
|
@ -236,6 +246,8 @@ class RouteMixin:
|
|||
URLs need to terminate with a */*
|
||||
:param version: API Version
|
||||
:param name: Unique name that can be used to identify the Route
|
||||
:param version_prefix: URL path that should be before the version
|
||||
value; default: ``/v``
|
||||
:return: Object decorated with :func:`route` method
|
||||
"""
|
||||
return self.route(
|
||||
|
@ -246,6 +258,7 @@ class RouteMixin:
|
|||
version=version,
|
||||
name=name,
|
||||
ignore_body=ignore_body,
|
||||
version_prefix=version_prefix,
|
||||
)
|
||||
|
||||
def post(
|
||||
|
@ -256,6 +269,7 @@ class RouteMixin:
|
|||
stream: bool = False,
|
||||
version: Optional[int] = None,
|
||||
name: Optional[str] = None,
|
||||
version_prefix: str = "/v",
|
||||
):
|
||||
"""
|
||||
Add an API URL under the **POST** *HTTP* method
|
||||
|
@ -266,6 +280,8 @@ class RouteMixin:
|
|||
URLs need to terminate with a */*
|
||||
:param version: API Version
|
||||
:param name: Unique name that can be used to identify the Route
|
||||
:param version_prefix: URL path that should be before the version
|
||||
value; default: ``/v``
|
||||
:return: Object decorated with :func:`route` method
|
||||
"""
|
||||
return self.route(
|
||||
|
@ -276,6 +292,7 @@ class RouteMixin:
|
|||
stream=stream,
|
||||
version=version,
|
||||
name=name,
|
||||
version_prefix=version_prefix,
|
||||
)
|
||||
|
||||
def put(
|
||||
|
@ -286,6 +303,7 @@ class RouteMixin:
|
|||
stream: bool = False,
|
||||
version: Optional[int] = None,
|
||||
name: Optional[str] = None,
|
||||
version_prefix: str = "/v",
|
||||
):
|
||||
"""
|
||||
Add an API URL under the **PUT** *HTTP* method
|
||||
|
@ -296,6 +314,8 @@ class RouteMixin:
|
|||
URLs need to terminate with a */*
|
||||
:param version: API Version
|
||||
:param name: Unique name that can be used to identify the Route
|
||||
:param version_prefix: URL path that should be before the version
|
||||
value; default: ``/v``
|
||||
:return: Object decorated with :func:`route` method
|
||||
"""
|
||||
return self.route(
|
||||
|
@ -306,6 +326,7 @@ class RouteMixin:
|
|||
stream=stream,
|
||||
version=version,
|
||||
name=name,
|
||||
version_prefix=version_prefix,
|
||||
)
|
||||
|
||||
def head(
|
||||
|
@ -316,6 +337,7 @@ class RouteMixin:
|
|||
version: Optional[int] = None,
|
||||
name: Optional[str] = None,
|
||||
ignore_body: bool = True,
|
||||
version_prefix: str = "/v",
|
||||
):
|
||||
"""
|
||||
Add an API URL under the **HEAD** *HTTP* method
|
||||
|
@ -334,6 +356,8 @@ class RouteMixin:
|
|||
:param ignore_body: whether the handler should ignore request
|
||||
body (eg. GET requests), defaults to True
|
||||
:type ignore_body: bool, optional
|
||||
:param version_prefix: URL path that should be before the version
|
||||
value; default: ``/v``
|
||||
:return: Object decorated with :func:`route` method
|
||||
"""
|
||||
return self.route(
|
||||
|
@ -344,6 +368,7 @@ class RouteMixin:
|
|||
version=version,
|
||||
name=name,
|
||||
ignore_body=ignore_body,
|
||||
version_prefix=version_prefix,
|
||||
)
|
||||
|
||||
def options(
|
||||
|
@ -354,6 +379,7 @@ class RouteMixin:
|
|||
version: Optional[int] = None,
|
||||
name: Optional[str] = None,
|
||||
ignore_body: bool = True,
|
||||
version_prefix: str = "/v",
|
||||
):
|
||||
"""
|
||||
Add an API URL under the **OPTIONS** *HTTP* method
|
||||
|
@ -372,6 +398,8 @@ class RouteMixin:
|
|||
:param ignore_body: whether the handler should ignore request
|
||||
body (eg. GET requests), defaults to True
|
||||
:type ignore_body: bool, optional
|
||||
:param version_prefix: URL path that should be before the version
|
||||
value; default: ``/v``
|
||||
:return: Object decorated with :func:`route` method
|
||||
"""
|
||||
return self.route(
|
||||
|
@ -382,6 +410,7 @@ class RouteMixin:
|
|||
version=version,
|
||||
name=name,
|
||||
ignore_body=ignore_body,
|
||||
version_prefix=version_prefix,
|
||||
)
|
||||
|
||||
def patch(
|
||||
|
@ -392,6 +421,7 @@ class RouteMixin:
|
|||
stream=False,
|
||||
version: Optional[int] = None,
|
||||
name: Optional[str] = None,
|
||||
version_prefix: str = "/v",
|
||||
):
|
||||
"""
|
||||
Add an API URL under the **PATCH** *HTTP* method
|
||||
|
@ -412,6 +442,8 @@ class RouteMixin:
|
|||
:param ignore_body: whether the handler should ignore request
|
||||
body (eg. GET requests), defaults to True
|
||||
:type ignore_body: bool, optional
|
||||
:param version_prefix: URL path that should be before the version
|
||||
value; default: ``/v``
|
||||
:return: Object decorated with :func:`route` method
|
||||
"""
|
||||
return self.route(
|
||||
|
@ -422,6 +454,7 @@ class RouteMixin:
|
|||
stream=stream,
|
||||
version=version,
|
||||
name=name,
|
||||
version_prefix=version_prefix,
|
||||
)
|
||||
|
||||
def delete(
|
||||
|
@ -432,6 +465,7 @@ class RouteMixin:
|
|||
version: Optional[int] = None,
|
||||
name: Optional[str] = None,
|
||||
ignore_body: bool = True,
|
||||
version_prefix: str = "/v",
|
||||
):
|
||||
"""
|
||||
Add an API URL under the **DELETE** *HTTP* method
|
||||
|
@ -442,6 +476,8 @@ class RouteMixin:
|
|||
URLs need to terminate with a */*
|
||||
:param version: API Version
|
||||
:param name: Unique name that can be used to identify the Route
|
||||
:param version_prefix: URL path that should be before the version
|
||||
value; default: ``/v``
|
||||
:return: Object decorated with :func:`route` method
|
||||
"""
|
||||
return self.route(
|
||||
|
@ -452,6 +488,7 @@ class RouteMixin:
|
|||
version=version,
|
||||
name=name,
|
||||
ignore_body=ignore_body,
|
||||
version_prefix=version_prefix,
|
||||
)
|
||||
|
||||
def websocket(
|
||||
|
@ -463,6 +500,7 @@ class RouteMixin:
|
|||
version: Optional[int] = None,
|
||||
name: Optional[str] = None,
|
||||
apply: bool = True,
|
||||
version_prefix: str = "/v",
|
||||
):
|
||||
"""
|
||||
Decorate a function to be registered as a websocket route
|
||||
|
@ -474,6 +512,8 @@ class RouteMixin:
|
|||
:param subprotocols: optional list of str with supported subprotocols
|
||||
:param name: A unique name assigned to the URL so that it can
|
||||
be used with :func:`url_for`
|
||||
:param version_prefix: URL path that should be before the version
|
||||
value; default: ``/v``
|
||||
:return: tuple of routes, decorated function
|
||||
"""
|
||||
return self.route(
|
||||
|
@ -486,6 +526,7 @@ class RouteMixin:
|
|||
apply=apply,
|
||||
subprotocols=subprotocols,
|
||||
websocket=True,
|
||||
version_prefix=version_prefix,
|
||||
)
|
||||
|
||||
def add_websocket_route(
|
||||
|
@ -497,6 +538,7 @@ class RouteMixin:
|
|||
subprotocols=None,
|
||||
version: Optional[int] = None,
|
||||
name: Optional[str] = None,
|
||||
version_prefix: str = "/v",
|
||||
):
|
||||
"""
|
||||
A helper method to register a function as a websocket route.
|
||||
|
@ -513,6 +555,8 @@ class RouteMixin:
|
|||
handshake
|
||||
:param name: A unique name assigned to the URL so that it can
|
||||
be used with :func:`url_for`
|
||||
:param version_prefix: URL path that should be before the version
|
||||
value; default: ``/v``
|
||||
:return: Objected decorated by :func:`websocket`
|
||||
"""
|
||||
return self.websocket(
|
||||
|
@ -522,6 +566,7 @@ class RouteMixin:
|
|||
subprotocols=subprotocols,
|
||||
version=version,
|
||||
name=name,
|
||||
version_prefix=version_prefix,
|
||||
)(handler)
|
||||
|
||||
def static(
|
||||
|
|
|
@ -23,6 +23,7 @@ class FutureRoute(NamedTuple):
|
|||
subprotocols: Optional[List[str]]
|
||||
unquote: bool
|
||||
static: bool
|
||||
version_prefix: str
|
||||
|
||||
|
||||
class FutureListener(NamedTuple):
|
||||
|
|
|
@ -73,6 +73,7 @@ class Router(BaseRouter):
|
|||
name: Optional[str] = None,
|
||||
unquote: bool = False,
|
||||
static: bool = False,
|
||||
version_prefix: str = "/v",
|
||||
) -> Union[Route, List[Route]]:
|
||||
"""
|
||||
Add a handler to the router
|
||||
|
@ -103,7 +104,7 @@ class Router(BaseRouter):
|
|||
"""
|
||||
if version is not None:
|
||||
version = str(version).strip("/").lstrip("v")
|
||||
uri = "/".join([f"/v{version}", uri.lstrip("/")])
|
||||
uri = "/".join([f"{version_prefix}{version}", uri.lstrip("/")])
|
||||
|
||||
params = dict(
|
||||
path=uri,
|
||||
|
|
|
@ -218,3 +218,32 @@ def test_blueprint_group_insert():
|
|||
assert group.blueprints[1].strict_slashes is False
|
||||
assert group.blueprints[2].strict_slashes is True
|
||||
assert group.blueprints[0].url_prefix == "/test"
|
||||
|
||||
|
||||
def test_bp_group_properties():
|
||||
blueprint_1 = Blueprint("blueprint_1", url_prefix="/bp1")
|
||||
blueprint_2 = Blueprint("blueprint_2", url_prefix="/bp2")
|
||||
group = Blueprint.group(
|
||||
blueprint_1,
|
||||
blueprint_2,
|
||||
version=1,
|
||||
version_prefix="/api/v",
|
||||
url_prefix="/grouped",
|
||||
strict_slashes=True,
|
||||
)
|
||||
|
||||
assert group.version_prefix == "/api/v"
|
||||
assert blueprint_1.version_prefix == "/api/v"
|
||||
assert blueprint_2.version_prefix == "/api/v"
|
||||
|
||||
assert group.version == 1
|
||||
assert blueprint_1.version == 1
|
||||
assert blueprint_2.version == 1
|
||||
|
||||
assert group.strict_slashes
|
||||
assert blueprint_1.strict_slashes
|
||||
assert blueprint_2.strict_slashes
|
||||
|
||||
assert group.url_prefix == "/grouped"
|
||||
assert blueprint_1.url_prefix == "/grouped/bp1"
|
||||
assert blueprint_2.url_prefix == "/grouped/bp2"
|
||||
|
|
141
tests/test_versioning.py
Normal file
141
tests/test_versioning.py
Normal file
|
@ -0,0 +1,141 @@
|
|||
import pytest
|
||||
|
||||
from sanic import Blueprint, text
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def handler():
|
||||
def handler(_):
|
||||
return text("Done.")
|
||||
|
||||
return handler
|
||||
|
||||
|
||||
def test_route(app, handler):
|
||||
app.route("/", version=1)(handler)
|
||||
|
||||
_, response = app.test_client.get("/v1")
|
||||
assert response.status == 200
|
||||
|
||||
|
||||
def test_bp(app, handler):
|
||||
bp = Blueprint(__file__, version=1)
|
||||
bp.route("/")(handler)
|
||||
app.blueprint(bp)
|
||||
|
||||
_, response = app.test_client.get("/v1")
|
||||
assert response.status == 200
|
||||
|
||||
|
||||
def test_bp_use_route(app, handler):
|
||||
bp = Blueprint(__file__, version=1)
|
||||
bp.route("/", version=1.1)(handler)
|
||||
app.blueprint(bp)
|
||||
|
||||
_, response = app.test_client.get("/v1.1")
|
||||
assert response.status == 200
|
||||
|
||||
|
||||
def test_bp_group(app, handler):
|
||||
bp = Blueprint(__file__)
|
||||
bp.route("/")(handler)
|
||||
group = Blueprint.group(bp, version=1)
|
||||
app.blueprint(group)
|
||||
|
||||
_, response = app.test_client.get("/v1")
|
||||
assert response.status == 200
|
||||
|
||||
|
||||
def test_bp_group_use_bp(app, handler):
|
||||
bp = Blueprint(__file__, version=1.1)
|
||||
bp.route("/")(handler)
|
||||
group = Blueprint.group(bp, version=1)
|
||||
app.blueprint(group)
|
||||
|
||||
_, response = app.test_client.get("/v1.1")
|
||||
assert response.status == 200
|
||||
|
||||
|
||||
def test_bp_group_use_registration(app, handler):
|
||||
bp = Blueprint(__file__, version=1.1)
|
||||
bp.route("/")(handler)
|
||||
group = Blueprint.group(bp, version=1)
|
||||
app.blueprint(group, version=1.2)
|
||||
|
||||
_, response = app.test_client.get("/v1.2")
|
||||
assert response.status == 200
|
||||
|
||||
|
||||
def test_bp_group_use_route(app, handler):
|
||||
bp = Blueprint(__file__, version=1.1)
|
||||
bp.route("/", version=1.3)(handler)
|
||||
group = Blueprint.group(bp, version=1)
|
||||
app.blueprint(group, version=1.2)
|
||||
|
||||
_, response = app.test_client.get("/v1.3")
|
||||
assert response.status == 200
|
||||
|
||||
|
||||
def test_version_prefix_route(app, handler):
|
||||
app.route("/", version=1, version_prefix="/api/v")(handler)
|
||||
|
||||
_, response = app.test_client.get("/api/v1")
|
||||
assert response.status == 200
|
||||
|
||||
|
||||
def test_version_prefix_bp(app, handler):
|
||||
bp = Blueprint(__file__, version=1, version_prefix="/api/v")
|
||||
bp.route("/")(handler)
|
||||
app.blueprint(bp)
|
||||
|
||||
_, response = app.test_client.get("/api/v1")
|
||||
assert response.status == 200
|
||||
|
||||
|
||||
def test_version_prefix_bp_use_route(app, handler):
|
||||
bp = Blueprint(__file__, version=1, version_prefix="/ignore/v")
|
||||
bp.route("/", version=1.1, version_prefix="/api/v")(handler)
|
||||
app.blueprint(bp)
|
||||
|
||||
_, response = app.test_client.get("/api/v1.1")
|
||||
assert response.status == 200
|
||||
|
||||
|
||||
def test_version_prefix_bp_group(app, handler):
|
||||
bp = Blueprint(__file__)
|
||||
bp.route("/")(handler)
|
||||
group = Blueprint.group(bp, version=1, version_prefix="/api/v")
|
||||
app.blueprint(group)
|
||||
|
||||
_, response = app.test_client.get("/api/v1")
|
||||
assert response.status == 200
|
||||
|
||||
|
||||
def test_version_prefix_bp_group_use_bp(app, handler):
|
||||
bp = Blueprint(__file__, version=1.1, version_prefix="/api/v")
|
||||
bp.route("/")(handler)
|
||||
group = Blueprint.group(bp, version=1, version_prefix="/ignore/v")
|
||||
app.blueprint(group)
|
||||
|
||||
_, response = app.test_client.get("/api/v1.1")
|
||||
assert response.status == 200
|
||||
|
||||
|
||||
def test_version_prefix_bp_group_use_registration(app, handler):
|
||||
bp = Blueprint(__file__, version=1.1, version_prefix="/alsoignore/v")
|
||||
bp.route("/")(handler)
|
||||
group = Blueprint.group(bp, version=1, version_prefix="/ignore/v")
|
||||
app.blueprint(group, version=1.2, version_prefix="/api/v")
|
||||
|
||||
_, response = app.test_client.get("/api/v1.2")
|
||||
assert response.status == 200
|
||||
|
||||
|
||||
def test_version_prefix_bp_group_use_route(app, handler):
|
||||
bp = Blueprint(__file__, version=1.1, version_prefix="/alsoignore/v")
|
||||
bp.route("/", version=1.3, version_prefix="/api/v")(handler)
|
||||
group = Blueprint.group(bp, version=1, version_prefix="/ignore/v")
|
||||
app.blueprint(group, version=1.2, version_prefix="/stillignoring/v")
|
||||
|
||||
_, response = app.test_client.get("/api/v1.3")
|
||||
assert response.status == 200
|
Loading…
Reference in New Issue
Block a user