prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  deprecate CompositionView ? 
  Currently sanic offers a class called `CompositionView`
  
  I really am struggling to find any utility in this class, since 
  
  ```python
  from sanic.views import CompositionView
  
  def get_handler(request):
      return text("I am a get method")
  
  view = CompositionView()
  view.add(["GET"], get_handler)
  view.add(["POST", "PUT"], lambda request: text("I am a post/put method"))
  
  # Use the new view to handle requests to the base URL
  app.add_route(view, "/")
  ```
  
  
  Seems much more confusing to me than
  
  ```python
  def get_handler(request):
      return text("I am a get method")
  
  app.route("/", methods=["GET"])(get_handler)
  app.route("/", methods=["POST", "PUT"])(lambda request: text("I am a post/put method"))
  ```
  
  Can anyone offer a compelling use case for CompositionView?
  
  If not, I would suggest to deprecate it 
  
  
  https://github.com/sanic-org/sanic/blob/master/sanic/views.py
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `sanic/views.py`
  Content:
  ```
  1 from typing import Any, Callable, List
  2 
  3 from sanic.constants import HTTP_METHODS
  4 from sanic.exceptions import InvalidUsage
  5 
  6 
  7 class HTTPMethodView:
  8     """Simple class based implementation of view for the sanic.
  9     You should implement methods (get, post, put, patch, delete) for the class
  10     to every HTTP method you want to support.
  11 
  12     For example:
  13 
  14     .. code-block:: python
  15 
  16         class DummyView(HTTPMethodView):
  17             def get(self, request, *args, **kwargs):
  18                 return text('I am get method')
  19             def put(self, request, *args, **kwargs):
  20                 return text('I am put method')
  21 
  22     If someone tries to use a non-implemented method, there will be a
  23     405 response.
  24 
  25     If you need any url params just mention them in method definition:
  26 
  27     .. code-block:: python
  28 
  29         class DummyView(HTTPMethodView):
  30             def get(self, request, my_param_here, *args, **kwargs):
  31                 return text('I am get method with %s' % my_param_here)
  32 
  33     To add the view into the routing you could use
  34 
  35         1) ``app.add_route(DummyView.as_view(), '/')``, OR
  36         2) ``app.route('/')(DummyView.as_view())``
  37 
  38     To add any decorator you could set it into decorators variable
  39     """
  40 
  41     decorators: List[Callable[[Callable[..., Any]], Callable[..., Any]]] = []
  42 
  43     def dispatch_request(self, request, *args, **kwargs):
  44         handler = getattr(self, request.method.lower(), None)
  45         return handler(request, *args, **kwargs)
  46 
  47     @classmethod
  48     def as_view(cls, *class_args, **class_kwargs):
  49         """Return view function for use with the routing system, that
  50         dispatches request to appropriate handler method.
  51         """
  52 
  53         def view(*args, **kwargs):
  54             self = view.view_class(*class_args, **class_kwargs)
  55             return self.dispatch_request(*args, **kwargs)
  56 
  57         if cls.decorators:
  58             view.__module__ = cls.__module__
  59             for decorator in cls.decorators:
  60                 view = decorator(view)
  61 
  62         view.view_class = cls
  63         view.__doc__ = cls.__doc__
  64         view.__module__ = cls.__module__
  65         view.__name__ = cls.__name__
  66         return view
  67 
  68 
  69 def stream(func):
  70     func.is_stream = True
  71     return func
  72 
  73 
  74 class CompositionView:
  75     """Simple method-function mapped view for the sanic.
  76     You can add handler functions to methods (get, post, put, patch, delete)
  77     for every HTTP method you want to support.
  78 
  79     For example:
  80 
  81     .. code-block:: python
  82 
  83         view = CompositionView()
  84         view.add(['GET'], lambda request: text('I am get method'))
  85         view.add(['POST', 'PUT'], lambda request: text('I am post/put method'))
  86 
  87     If someone tries to use a non-implemented method, there will be a
  88     405 response.
  89     """
  90 
  91     def __init__(self):
  92         self.handlers = {}
  93         self.name = self.__class__.__name__
  94 
  95     def __name__(self):
  96         return self.name
  97 
  98     def add(self, methods, handler, stream=False):
  99         if stream:
  100             handler.is_stream = stream
  101         for method in methods:
  102             if method not in HTTP_METHODS:
  103                 raise InvalidUsage(f"{method} is not a valid HTTP method.")
  104 
  105             if method in self.handlers:
  106                 raise InvalidUsage(f"Method {method} is already registered.")
  107             self.handlers[method] = handler
  108 
  109     def __call__(self, request, *args, **kwargs):
  110         handler = self.handlers[request.method.upper()]
  111         return handler(request, *args, **kwargs)
  112
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/sanic/views.py b/sanic/views.py
  --- a/sanic/views.py
  +++ b/sanic/views.py
  @@ -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 @@
   
       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 @@
           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 @@
       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
  
verification_info: {"golden_diff": "diff --git a/sanic/views.py b/sanic/views.py\n--- a/sanic/views.py\n+++ b/sanic/views.py\n@@ -1,9 +1,25 @@\n-from typing import Any, Callable, List\n+from __future__ import annotations\n+\n+from typing import (\n+    TYPE_CHECKING,\n+    Any,\n+    Callable,\n+    Iterable,\n+    List,\n+    Optional,\n+    Union,\n+)\n+from warnings import warn\n \n from sanic.constants import HTTP_METHODS\n from sanic.exceptions import InvalidUsage\n \n \n+if TYPE_CHECKING:\n+    from sanic import Sanic\n+    from sanic.blueprints import Blueprint\n+\n+\n class HTTPMethodView:\n     \"\"\"Simple class based implementation of view for the sanic.\n     You should implement methods (get, post, put, patch, delete) for the class\n@@ -40,6 +56,31 @@\n \n     decorators: List[Callable[[Callable[..., Any]], Callable[..., Any]]] = []\n \n+    def __init_subclass__(\n+        cls,\n+        attach: Optional[Union[Sanic, Blueprint]] = None,\n+        uri: str = \"\",\n+        methods: Iterable[str] = frozenset({\"GET\"}),\n+        host: Optional[str] = None,\n+        strict_slashes: Optional[bool] = None,\n+        version: Optional[int] = None,\n+        name: Optional[str] = None,\n+        stream: bool = False,\n+        version_prefix: str = \"/v\",\n+    ) -> None:\n+        if attach:\n+            cls.attach(\n+                attach,\n+                uri=uri,\n+                methods=methods,\n+                host=host,\n+                strict_slashes=strict_slashes,\n+                version=version,\n+                name=name,\n+                stream=stream,\n+                version_prefix=version_prefix,\n+            )\n+\n     def dispatch_request(self, request, *args, **kwargs):\n         handler = getattr(self, request.method.lower(), None)\n         return handler(request, *args, **kwargs)\n@@ -65,6 +106,31 @@\n         view.__name__ = cls.__name__\n         return view\n \n+    @classmethod\n+    def attach(\n+        cls,\n+        to: Union[Sanic, Blueprint],\n+        uri: str,\n+        methods: Iterable[str] = frozenset({\"GET\"}),\n+        host: Optional[str] = None,\n+        strict_slashes: Optional[bool] = None,\n+        version: Optional[int] = None,\n+        name: Optional[str] = None,\n+        stream: bool = False,\n+        version_prefix: str = \"/v\",\n+    ) -> None:\n+        to.add_route(\n+            cls.as_view(),\n+            uri=uri,\n+            methods=methods,\n+            host=host,\n+            strict_slashes=strict_slashes,\n+            version=version,\n+            name=name,\n+            stream=stream,\n+            version_prefix=version_prefix,\n+        )\n+\n \n def stream(func):\n     func.is_stream = True\n@@ -91,6 +157,11 @@\n     def __init__(self):\n         self.handlers = {}\n         self.name = self.__class__.__name__\n+        warn(\n+            \"CompositionView has been deprecated and will be removed in \"\n+            \"v21.12. Please update your view to HTTPMethodView.\",\n+            DeprecationWarning,\n+        )\n \n     def __name__(self):\n         return self.name\n", "issue": "deprecate CompositionView ? \nCurrently sanic offers a class called `CompositionView`\r\n\r\nI really am struggling to find any utility in this class, since \r\n\r\n```python\r\nfrom sanic.views import CompositionView\r\n\r\ndef get_handler(request):\r\n    return text(\"I am a get method\")\r\n\r\nview = CompositionView()\r\nview.add([\"GET\"], get_handler)\r\nview.add([\"POST\", \"PUT\"], lambda request: text(\"I am a post/put method\"))\r\n\r\n# Use the new view to handle requests to the base URL\r\napp.add_route(view, \"/\")\r\n```\r\n\r\n\r\nSeems much more confusing to me than\r\n\r\n```python\r\ndef get_handler(request):\r\n    return text(\"I am a get method\")\r\n\r\napp.route(\"/\", methods=[\"GET\"])(get_handler)\r\napp.route(\"/\", methods=[\"POST\", \"PUT\"])(lambda request: text(\"I am a post/put method\"))\r\n```\r\n\r\nCan anyone offer a compelling use case for CompositionView?\r\n\r\nIf not, I would suggest to deprecate it \r\n\r\n\r\nhttps://github.com/sanic-org/sanic/blob/master/sanic/views.py\n", "before_files": [{"content": "from typing import Any, Callable, List\n\nfrom sanic.constants import HTTP_METHODS\nfrom sanic.exceptions import InvalidUsage\n\n\nclass HTTPMethodView:\n    \"\"\"Simple class based implementation of view for the sanic.\n    You should implement methods (get, post, put, patch, delete) for the class\n    to every HTTP method you want to support.\n\n    For example:\n\n    .. code-block:: python\n\n        class DummyView(HTTPMethodView):\n            def get(self, request, *args, **kwargs):\n                return text('I am get method')\n            def put(self, request, *args, **kwargs):\n                return text('I am put method')\n\n    If someone tries to use a non-implemented method, there will be a\n    405 response.\n\n    If you need any url params just mention them in method definition:\n\n    .. code-block:: python\n\n        class DummyView(HTTPMethodView):\n            def get(self, request, my_param_here, *args, **kwargs):\n                return text('I am get method with %s' % my_param_here)\n\n    To add the view into the routing you could use\n\n        1) ``app.add_route(DummyView.as_view(), '/')``, OR\n        2) ``app.route('/')(DummyView.as_view())``\n\n    To add any decorator you could set it into decorators variable\n    \"\"\"\n\n    decorators: List[Callable[[Callable[..., Any]], Callable[..., Any]]] = []\n\n    def dispatch_request(self, request, *args, **kwargs):\n        handler = getattr(self, request.method.lower(), None)\n        return handler(request, *args, **kwargs)\n\n    @classmethod\n    def as_view(cls, *class_args, **class_kwargs):\n        \"\"\"Return view function for use with the routing system, that\n        dispatches request to appropriate handler method.\n        \"\"\"\n\n        def view(*args, **kwargs):\n            self = view.view_class(*class_args, **class_kwargs)\n            return self.dispatch_request(*args, **kwargs)\n\n        if cls.decorators:\n            view.__module__ = cls.__module__\n            for decorator in cls.decorators:\n                view = decorator(view)\n\n        view.view_class = cls\n        view.__doc__ = cls.__doc__\n        view.__module__ = cls.__module__\n        view.__name__ = cls.__name__\n        return view\n\n\ndef stream(func):\n    func.is_stream = True\n    return func\n\n\nclass CompositionView:\n    \"\"\"Simple method-function mapped view for the sanic.\n    You can add handler functions to methods (get, post, put, patch, delete)\n    for every HTTP method you want to support.\n\n    For example:\n\n    .. code-block:: python\n\n        view = CompositionView()\n        view.add(['GET'], lambda request: text('I am get method'))\n        view.add(['POST', 'PUT'], lambda request: text('I am post/put method'))\n\n    If someone tries to use a non-implemented method, there will be a\n    405 response.\n    \"\"\"\n\n    def __init__(self):\n        self.handlers = {}\n        self.name = self.__class__.__name__\n\n    def __name__(self):\n        return self.name\n\n    def add(self, methods, handler, stream=False):\n        if stream:\n            handler.is_stream = stream\n        for method in methods:\n            if method not in HTTP_METHODS:\n                raise InvalidUsage(f\"{method} is not a valid HTTP method.\")\n\n            if method in self.handlers:\n                raise InvalidUsage(f\"Method {method} is already registered.\")\n            self.handlers[method] = handler\n\n    def __call__(self, request, *args, **kwargs):\n        handler = self.handlers[request.method.upper()]\n        return handler(request, *args, **kwargs)\n", "path": "sanic/views.py"}], "after_files": [{"content": "from __future__ import annotations\n\nfrom typing import (\n    TYPE_CHECKING,\n    Any,\n    Callable,\n    Iterable,\n    List,\n    Optional,\n    Union,\n)\nfrom warnings import warn\n\nfrom sanic.constants import HTTP_METHODS\nfrom sanic.exceptions import InvalidUsage\n\n\nif TYPE_CHECKING:\n    from sanic import Sanic\n    from sanic.blueprints import Blueprint\n\n\nclass HTTPMethodView:\n    \"\"\"Simple class based implementation of view for the sanic.\n    You should implement methods (get, post, put, patch, delete) for the class\n    to every HTTP method you want to support.\n\n    For example:\n\n    .. code-block:: python\n\n        class DummyView(HTTPMethodView):\n            def get(self, request, *args, **kwargs):\n                return text('I am get method')\n            def put(self, request, *args, **kwargs):\n                return text('I am put method')\n\n    If someone tries to use a non-implemented method, there will be a\n    405 response.\n\n    If you need any url params just mention them in method definition:\n\n    .. code-block:: python\n\n        class DummyView(HTTPMethodView):\n            def get(self, request, my_param_here, *args, **kwargs):\n                return text('I am get method with %s' % my_param_here)\n\n    To add the view into the routing you could use\n\n        1) ``app.add_route(DummyView.as_view(), '/')``, OR\n        2) ``app.route('/')(DummyView.as_view())``\n\n    To add any decorator you could set it into decorators variable\n    \"\"\"\n\n    decorators: List[Callable[[Callable[..., Any]], Callable[..., Any]]] = []\n\n    def __init_subclass__(\n        cls,\n        attach: Optional[Union[Sanic, Blueprint]] = None,\n        uri: str = \"\",\n        methods: Iterable[str] = frozenset({\"GET\"}),\n        host: Optional[str] = None,\n        strict_slashes: Optional[bool] = None,\n        version: Optional[int] = None,\n        name: Optional[str] = None,\n        stream: bool = False,\n        version_prefix: str = \"/v\",\n    ) -> None:\n        if attach:\n            cls.attach(\n                attach,\n                uri=uri,\n                methods=methods,\n                host=host,\n                strict_slashes=strict_slashes,\n                version=version,\n                name=name,\n                stream=stream,\n                version_prefix=version_prefix,\n            )\n\n    def dispatch_request(self, request, *args, **kwargs):\n        handler = getattr(self, request.method.lower(), None)\n        return handler(request, *args, **kwargs)\n\n    @classmethod\n    def as_view(cls, *class_args, **class_kwargs):\n        \"\"\"Return view function for use with the routing system, that\n        dispatches request to appropriate handler method.\n        \"\"\"\n\n        def view(*args, **kwargs):\n            self = view.view_class(*class_args, **class_kwargs)\n            return self.dispatch_request(*args, **kwargs)\n\n        if cls.decorators:\n            view.__module__ = cls.__module__\n            for decorator in cls.decorators:\n                view = decorator(view)\n\n        view.view_class = cls\n        view.__doc__ = cls.__doc__\n        view.__module__ = cls.__module__\n        view.__name__ = cls.__name__\n        return view\n\n    @classmethod\n    def attach(\n        cls,\n        to: Union[Sanic, Blueprint],\n        uri: str,\n        methods: Iterable[str] = frozenset({\"GET\"}),\n        host: Optional[str] = None,\n        strict_slashes: Optional[bool] = None,\n        version: Optional[int] = None,\n        name: Optional[str] = None,\n        stream: bool = False,\n        version_prefix: str = \"/v\",\n    ) -> None:\n        to.add_route(\n            cls.as_view(),\n            uri=uri,\n            methods=methods,\n            host=host,\n            strict_slashes=strict_slashes,\n            version=version,\n            name=name,\n            stream=stream,\n            version_prefix=version_prefix,\n        )\n\n\ndef stream(func):\n    func.is_stream = True\n    return func\n\n\nclass CompositionView:\n    \"\"\"Simple method-function mapped view for the sanic.\n    You can add handler functions to methods (get, post, put, patch, delete)\n    for every HTTP method you want to support.\n\n    For example:\n\n    .. code-block:: python\n\n        view = CompositionView()\n        view.add(['GET'], lambda request: text('I am get method'))\n        view.add(['POST', 'PUT'], lambda request: text('I am post/put method'))\n\n    If someone tries to use a non-implemented method, there will be a\n    405 response.\n    \"\"\"\n\n    def __init__(self):\n        self.handlers = {}\n        self.name = self.__class__.__name__\n        warn(\n            \"CompositionView has been deprecated and will be removed in \"\n            \"v21.12. Please update your view to HTTPMethodView.\",\n            DeprecationWarning,\n        )\n\n    def __name__(self):\n        return self.name\n\n    def add(self, methods, handler, stream=False):\n        if stream:\n            handler.is_stream = stream\n        for method in methods:\n            if method not in HTTP_METHODS:\n                raise InvalidUsage(f\"{method} is not a valid HTTP method.\")\n\n            if method in self.handlers:\n                raise InvalidUsage(f\"Method {method} is already registered.\")\n            self.handlers[method] = handler\n\n    def __call__(self, request, *args, **kwargs):\n        handler = self.handlers[request.method.upper()]\n        return handler(request, *args, **kwargs)\n", "path": "sanic/views.py"}]}