From ead4fde0ed518640f3092bf9dd28b026419e2795 Mon Sep 17 00:00:00 2001 From: Timothy McCurrach Date: Sun, 12 Dec 2021 00:05:15 +0000 Subject: [PATCH] Make api_view respect standard wrapper assignments --- rest_framework/decorators.py | 20 ++++---------------- tests/test_decorators.py | 10 ++++++++++ 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/rest_framework/decorators.py b/rest_framework/decorators.py index 30b9d84d4e..7ba43d37c8 100644 --- a/rest_framework/decorators.py +++ b/rest_framework/decorators.py @@ -7,6 +7,7 @@ methods on viewsets that should be included by routers. """ import types +from functools import update_wrapper from django.forms.utils import pretty_name @@ -22,18 +23,8 @@ def api_view(http_method_names=None): def decorator(func): - WrappedAPIView = type( - 'WrappedAPIView', - (APIView,), - {'__doc__': func.__doc__} - ) - - # Note, the above allows us to set the docstring. - # It is the equivalent of: - # - # class WrappedAPIView(APIView): - # pass - # WrappedAPIView.__doc__ = func.doc <--- Not possible to do this + class WrappedAPIView(APIView): + pass # api_view applied without (method_names) assert not(isinstance(http_method_names, types.FunctionType)), \ @@ -52,9 +43,6 @@ def handler(self, *args, **kwargs): for method in http_method_names: setattr(WrappedAPIView, method.lower(), handler) - WrappedAPIView.__name__ = func.__name__ - WrappedAPIView.__module__ = func.__module__ - WrappedAPIView.renderer_classes = getattr(func, 'renderer_classes', APIView.renderer_classes) @@ -73,7 +61,7 @@ def handler(self, *args, **kwargs): WrappedAPIView.schema = getattr(func, 'schema', APIView.schema) - return WrappedAPIView.as_view() + return update_wrapper(WrappedAPIView.as_view(), func) return decorator diff --git a/tests/test_decorators.py b/tests/test_decorators.py index 99ba13e60c..116d6f1be4 100644 --- a/tests/test_decorators.py +++ b/tests/test_decorators.py @@ -162,6 +162,16 @@ def view(request): assert isinstance(view.cls.schema, CustomSchema) + def test_wrapper_assignments(self): + @api_view(["GET"]) + def test_view(request): + """example docstring""" + pass + + assert test_view.__name__ == "test_view" + assert test_view.__doc__ == "example docstring" + assert test_view.__qualname__ == "DecoratorTestCase.test_wrapper_assignments..test_view" + class ActionDecoratorTestCase(TestCase):