From 9251520f661c73a9243ef77b177da9abedec3cfd Mon Sep 17 00:00:00 2001 From: LanDinh Date: Sat, 1 May 2021 23:33:05 +0200 Subject: [PATCH] Allow Views to return the more generic HttpResponseBase instead of HttpResponse, to allow returning StreamingHttpResponse (#607) * Add type hints for the postgres CursorDebugWrapper, expand the BaseCursorDebugWrapper. * Fix how Optinal gets applied. * Fix optional handling further. * Adjust postgres debugcursorwrapper to look more like the implementation. * Apply review feedback. * Use the more generic HttpResponseBase when appriopriate. * Fix failing test and add new test. * Remove the other test again, it was the wrong location. Add new tests in the correct location. Co-authored-by: LanDinh --- django-stubs/views/generic/base.pyi | 17 +++++++++-------- tests/typecheck/utils/test_decorators.yml | 6 +++--- tests/typecheck/views/generic/test_edit.yml | 18 ++++++++++++++++++ 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/django-stubs/views/generic/base.pyi b/django-stubs/views/generic/base.pyi index d9eb72e..bb9d6c8 100644 --- a/django-stubs/views/generic/base.pyi +++ b/django-stubs/views/generic/base.pyi @@ -1,6 +1,7 @@ from typing import Any, Callable, Dict, List, Optional, Type from django import http +from django.http.response import HttpResponseBase class ContextMixin: def get_context_data(self, **kwargs: Any) -> Dict[str, Any]: ... @@ -12,9 +13,9 @@ class View: kwargs: Any = ... def __init__(self, **kwargs: Any) -> None: ... @classmethod - def as_view(cls: Any, **initkwargs: Any) -> Callable[..., http.HttpResponse]: ... + def as_view(cls: Any, **initkwargs: Any) -> Callable[..., HttpResponseBase]: ... def setup(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> None: ... - def dispatch(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> http.HttpResponse: ... + def dispatch(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> HttpResponseBase: ... def http_method_not_allowed(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> http.HttpResponse: ... def options(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> http.HttpResponse: ... @@ -36,9 +37,9 @@ class RedirectView(View): pattern_name: Optional[str] = ... query_string: bool = ... def get_redirect_url(self, *args: Any, **kwargs: Any) -> Optional[str]: ... - def get(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> http.HttpResponse: ... - def head(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> http.HttpResponse: ... - def post(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> http.HttpResponse: ... - def delete(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> http.HttpResponse: ... - def put(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> http.HttpResponse: ... - def patch(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> http.HttpResponse: ... + def get(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> HttpResponseBase: ... + def head(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> HttpResponseBase: ... + def post(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> HttpResponseBase: ... + def delete(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> HttpResponseBase: ... + def put(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> HttpResponseBase: ... + def patch(self, request: http.HttpRequest, *args: Any, **kwargs: Any) -> HttpResponseBase: ... diff --git a/tests/typecheck/utils/test_decorators.yml b/tests/typecheck/utils/test_decorators.yml index b347938..31a02f8 100644 --- a/tests/typecheck/utils/test_decorators.yml +++ b/tests/typecheck/utils/test_decorators.yml @@ -11,10 +11,10 @@ from django.views.generic.base import View from django.utils.decorators import method_decorator from django.contrib.auth.decorators import login_required - from django.http.response import HttpResponse + from django.http.response import HttpResponseBase from django.http.request import HttpRequest class TestView(View): @method_decorator(login_required) - def dispatch(self, request: HttpRequest, *args, **kwargs) -> HttpResponse: + def dispatch(self, request: HttpRequest, *args, **kwargs) -> HttpResponseBase: return super().dispatch(request, *args, **kwargs) - reveal_type(dispatch) # N: Revealed type is 'def (self: main.TestView, request: django.http.request.HttpRequest, *args: Any, **kwargs: Any) -> django.http.response.HttpResponse' + reveal_type(dispatch) # N: Revealed type is 'def (self: main.TestView, request: django.http.request.HttpRequest, *args: Any, **kwargs: Any) -> django.http.response.HttpResponseBase' diff --git a/tests/typecheck/views/generic/test_edit.yml b/tests/typecheck/views/generic/test_edit.yml index 4e49280..3cd62ec 100644 --- a/tests/typecheck/views/generic/test_edit.yml +++ b/tests/typecheck/views/generic/test_edit.yml @@ -10,3 +10,21 @@ """Ensure that form can have type AuthenticationForm.""" form.get_user() return HttpResponseRedirect(self.get_success_url()) +- case: dispatch_http_response + main: | + from django.http import HttpResponse + from django.views.generic.base import View + + class MyView(View): + def dispatch(self, request, *args, **kwargs) -> HttpResponse: + response: HttpResponse + return response +- case: dispatch_streaming_http_response + main: | + from django.http import StreamingHttpResponse + from django.views.generic.base import View + + class MyView(View): + def dispatch(self, request, *args, **kwargs) -> StreamingHttpResponse: + response: StreamingHttpResponse + return response