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:
Adam Hopkins 2021-06-21 14:26:42 +03:00 committed by GitHub
parent 80fca9aef7
commit c543d19f8a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 138 additions and 8 deletions

View File

@ -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.exceptions import InvalidUsage
if TYPE_CHECKING:
from sanic import Sanic
from sanic.blueprints import Blueprint
class HTTPMethodView:
"""Simple class based implementation of view for the sanic.
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]]] = []
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):
handler = getattr(self, request.method.lower(), None)
return handler(request, *args, **kwargs)
@ -65,6 +106,31 @@ class HTTPMethodView:
view.__name__ = cls.__name__
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):
func.is_stream = True
@ -91,6 +157,11 @@ class CompositionView:
def __init__(self):
self.handlers = {}
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):
return self.name

View File

@ -77,6 +77,56 @@ def test_with_bp(app):
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):
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.text == "first method"
# response = view(request)
# assert response.body.decode() == "first method"
response = view(request)
assert response.body.decode() == "first method"
# if method in ["DELETE", "PATCH"]:
# request, response = getattr(app.test_client, method.lower())("/")
# assert response.text == "second method"
if method in ["DELETE", "PATCH"]:
request, response = getattr(app.test_client, method.lower())("/")
assert response.text == "second method"
# response = view(request)
# assert response.body.decode() == "second method"
response = view(request)
assert response.body.decode() == "second method"
@pytest.mark.parametrize("method", HTTP_METHODS)
@ -244,3 +294,12 @@ def test_composition_view_rejects_invalid_methods(app, method):
if method in ["DELETE", "PATCH"]:
request, response = getattr(app.test_client, method.lower())("/")
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()