CBV alternate attach; CompositionView deprecate (#2170)
* Deprecate composition view and add alternate methods to attach CBV * Add args to CBV attaching
This commit is contained in:
parent
80fca9aef7
commit
c543d19f8a
|
@ -1,9 +1,25 @@
|
||||||
from typing import Any, Callable, List
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import (
|
||||||
|
TYPE_CHECKING,
|
||||||
|
Any,
|
||||||
|
Callable,
|
||||||
|
Iterable,
|
||||||
|
List,
|
||||||
|
Optional,
|
||||||
|
Union,
|
||||||
|
)
|
||||||
|
from warnings import warn
|
||||||
|
|
||||||
from sanic.constants import HTTP_METHODS
|
from sanic.constants import HTTP_METHODS
|
||||||
from sanic.exceptions import InvalidUsage
|
from sanic.exceptions import InvalidUsage
|
||||||
|
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from sanic import Sanic
|
||||||
|
from sanic.blueprints import Blueprint
|
||||||
|
|
||||||
|
|
||||||
class HTTPMethodView:
|
class HTTPMethodView:
|
||||||
"""Simple class based implementation of view for the sanic.
|
"""Simple class based implementation of view for the sanic.
|
||||||
You should implement methods (get, post, put, patch, delete) for the class
|
You should implement methods (get, post, put, patch, delete) for the class
|
||||||
|
@ -40,6 +56,31 @@ class HTTPMethodView:
|
||||||
|
|
||||||
decorators: List[Callable[[Callable[..., Any]], Callable[..., Any]]] = []
|
decorators: List[Callable[[Callable[..., Any]], Callable[..., Any]]] = []
|
||||||
|
|
||||||
|
def __init_subclass__(
|
||||||
|
cls,
|
||||||
|
attach: Optional[Union[Sanic, Blueprint]] = None,
|
||||||
|
uri: str = "",
|
||||||
|
methods: Iterable[str] = frozenset({"GET"}),
|
||||||
|
host: Optional[str] = None,
|
||||||
|
strict_slashes: Optional[bool] = None,
|
||||||
|
version: Optional[int] = None,
|
||||||
|
name: Optional[str] = None,
|
||||||
|
stream: bool = False,
|
||||||
|
version_prefix: str = "/v",
|
||||||
|
) -> None:
|
||||||
|
if attach:
|
||||||
|
cls.attach(
|
||||||
|
attach,
|
||||||
|
uri=uri,
|
||||||
|
methods=methods,
|
||||||
|
host=host,
|
||||||
|
strict_slashes=strict_slashes,
|
||||||
|
version=version,
|
||||||
|
name=name,
|
||||||
|
stream=stream,
|
||||||
|
version_prefix=version_prefix,
|
||||||
|
)
|
||||||
|
|
||||||
def dispatch_request(self, request, *args, **kwargs):
|
def dispatch_request(self, request, *args, **kwargs):
|
||||||
handler = getattr(self, request.method.lower(), None)
|
handler = getattr(self, request.method.lower(), None)
|
||||||
return handler(request, *args, **kwargs)
|
return handler(request, *args, **kwargs)
|
||||||
|
@ -65,6 +106,31 @@ class HTTPMethodView:
|
||||||
view.__name__ = cls.__name__
|
view.__name__ = cls.__name__
|
||||||
return view
|
return view
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def attach(
|
||||||
|
cls,
|
||||||
|
to: Union[Sanic, Blueprint],
|
||||||
|
uri: str,
|
||||||
|
methods: Iterable[str] = frozenset({"GET"}),
|
||||||
|
host: Optional[str] = None,
|
||||||
|
strict_slashes: Optional[bool] = None,
|
||||||
|
version: Optional[int] = None,
|
||||||
|
name: Optional[str] = None,
|
||||||
|
stream: bool = False,
|
||||||
|
version_prefix: str = "/v",
|
||||||
|
) -> None:
|
||||||
|
to.add_route(
|
||||||
|
cls.as_view(),
|
||||||
|
uri=uri,
|
||||||
|
methods=methods,
|
||||||
|
host=host,
|
||||||
|
strict_slashes=strict_slashes,
|
||||||
|
version=version,
|
||||||
|
name=name,
|
||||||
|
stream=stream,
|
||||||
|
version_prefix=version_prefix,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def stream(func):
|
def stream(func):
|
||||||
func.is_stream = True
|
func.is_stream = True
|
||||||
|
@ -91,6 +157,11 @@ class CompositionView:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.handlers = {}
|
self.handlers = {}
|
||||||
self.name = self.__class__.__name__
|
self.name = self.__class__.__name__
|
||||||
|
warn(
|
||||||
|
"CompositionView has been deprecated and will be removed in "
|
||||||
|
"v21.12. Please update your view to HTTPMethodView.",
|
||||||
|
DeprecationWarning,
|
||||||
|
)
|
||||||
|
|
||||||
def __name__(self):
|
def __name__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
|
@ -77,6 +77,56 @@ def test_with_bp(app):
|
||||||
assert response.text == "I am get method"
|
assert response.text == "I am get method"
|
||||||
|
|
||||||
|
|
||||||
|
def test_with_attach(app):
|
||||||
|
class DummyView(HTTPMethodView):
|
||||||
|
def get(self, request):
|
||||||
|
return text("I am get method")
|
||||||
|
|
||||||
|
DummyView.attach(app, "/")
|
||||||
|
|
||||||
|
request, response = app.test_client.get("/")
|
||||||
|
|
||||||
|
assert response.text == "I am get method"
|
||||||
|
|
||||||
|
|
||||||
|
def test_with_sub_init(app):
|
||||||
|
class DummyView(HTTPMethodView, attach=app, uri="/"):
|
||||||
|
def get(self, request):
|
||||||
|
return text("I am get method")
|
||||||
|
|
||||||
|
request, response = app.test_client.get("/")
|
||||||
|
|
||||||
|
assert response.text == "I am get method"
|
||||||
|
|
||||||
|
|
||||||
|
def test_with_attach_and_bp(app):
|
||||||
|
bp = Blueprint("test_text")
|
||||||
|
|
||||||
|
class DummyView(HTTPMethodView):
|
||||||
|
def get(self, request):
|
||||||
|
return text("I am get method")
|
||||||
|
|
||||||
|
DummyView.attach(bp, "/")
|
||||||
|
|
||||||
|
app.blueprint(bp)
|
||||||
|
request, response = app.test_client.get("/")
|
||||||
|
|
||||||
|
assert response.text == "I am get method"
|
||||||
|
|
||||||
|
|
||||||
|
def test_with_sub_init_and_bp(app):
|
||||||
|
bp = Blueprint("test_text")
|
||||||
|
|
||||||
|
class DummyView(HTTPMethodView, attach=bp, uri="/"):
|
||||||
|
def get(self, request):
|
||||||
|
return text("I am get method")
|
||||||
|
|
||||||
|
app.blueprint(bp)
|
||||||
|
request, response = app.test_client.get("/")
|
||||||
|
|
||||||
|
assert response.text == "I am get method"
|
||||||
|
|
||||||
|
|
||||||
def test_with_bp_with_url_prefix(app):
|
def test_with_bp_with_url_prefix(app):
|
||||||
bp = Blueprint("test_text", url_prefix="/test1")
|
bp = Blueprint("test_text", url_prefix="/test1")
|
||||||
|
|
||||||
|
@ -218,15 +268,15 @@ def test_composition_view_runs_methods_as_expected(app, method):
|
||||||
assert response.status == 200
|
assert response.status == 200
|
||||||
assert response.text == "first method"
|
assert response.text == "first method"
|
||||||
|
|
||||||
# response = view(request)
|
response = view(request)
|
||||||
# assert response.body.decode() == "first method"
|
assert response.body.decode() == "first method"
|
||||||
|
|
||||||
# if method in ["DELETE", "PATCH"]:
|
if method in ["DELETE", "PATCH"]:
|
||||||
# request, response = getattr(app.test_client, method.lower())("/")
|
request, response = getattr(app.test_client, method.lower())("/")
|
||||||
# assert response.text == "second method"
|
assert response.text == "second method"
|
||||||
|
|
||||||
# response = view(request)
|
response = view(request)
|
||||||
# assert response.body.decode() == "second method"
|
assert response.body.decode() == "second method"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("method", HTTP_METHODS)
|
@pytest.mark.parametrize("method", HTTP_METHODS)
|
||||||
|
@ -244,3 +294,12 @@ def test_composition_view_rejects_invalid_methods(app, method):
|
||||||
if method in ["DELETE", "PATCH"]:
|
if method in ["DELETE", "PATCH"]:
|
||||||
request, response = getattr(app.test_client, method.lower())("/")
|
request, response = getattr(app.test_client, method.lower())("/")
|
||||||
assert response.status == 405
|
assert response.status == 405
|
||||||
|
|
||||||
|
|
||||||
|
def test_composition_view_deprecation():
|
||||||
|
message = (
|
||||||
|
"CompositionView has been deprecated and will be removed in v21.12. "
|
||||||
|
"Please update your view to HTTPMethodView."
|
||||||
|
)
|
||||||
|
with pytest.warns(DeprecationWarning, match=message):
|
||||||
|
CompositionView()
|
||||||
|
|
Loading…
Reference in New Issue
Block a user