Skip to content

Commit b11713e

Browse files
authored
better HTTPException.get_response annotation (#3072)
2 parents 5d9a403 + 1131dbd commit b11713e

File tree

2 files changed

+37
-20
lines changed

2 files changed

+37
-20
lines changed

CHANGES.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ Unreleased
1212
- `Request.json` annotation is more accurate. :issue:`3067`
1313
- Traceback rendering handles when thd line number is beyond the available
1414
source lines. :issue:`3044`
15+
- `HTTPException.get_response` annotation and doc better conveys the
16+
distinction between WSGI and sans-IO responses. :issue:`3056`
1517

1618

1719
Version 3.1.3

src/werkzeug/exceptions.py

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ def application(request):
5959
from _typeshed.wsgi import WSGIEnvironment
6060

6161
from .datastructures import WWWAuthenticate
62-
from .sansio.response import Response
62+
from .sansio.response import Response as SansIOResponse
6363
from .wrappers.request import Request as WSGIRequest
6464
from .wrappers.response import Response as WSGIResponse
6565

@@ -79,7 +79,7 @@ class HTTPException(Exception):
7979
def __init__(
8080
self,
8181
description: str | None = None,
82-
response: Response | None = None,
82+
response: SansIOResponse | None = None,
8383
) -> None:
8484
super().__init__()
8585
if description is not None:
@@ -129,27 +129,42 @@ def get_headers(
129129
"""Get a list of headers."""
130130
return [("Content-Type", "text/html; charset=utf-8")]
131131

132+
@t.overload
133+
def get_response(
134+
self,
135+
environ: WSGIEnvironment | WSGIRequest | None = ...,
136+
scope: None = None,
137+
) -> WSGIResponse: ...
138+
@t.overload
139+
def get_response(
140+
self,
141+
environ: None = None,
142+
scope: dict[str, t.Any] = ...,
143+
) -> SansIOResponse: ...
132144
def get_response(
133145
self,
134146
environ: WSGIEnvironment | WSGIRequest | None = None,
135147
scope: dict[str, t.Any] | None = None,
136-
) -> Response:
137-
"""Get a response object. If one was passed to the exception
138-
it's returned directly.
139-
140-
:param environ: the optional environ for the request. This
141-
can be used to modify the response depending
142-
on how the request looked like.
143-
:return: a :class:`Response` object or a subclass thereof.
148+
) -> WSGIResponse | SansIOResponse:
149+
"""Get a response object.
150+
151+
:param environ: A WSGI environ dict or request object. If given, may be
152+
used to customize the response based on the request.
153+
:param scope: An ASGI scope dict. If given, may be used to customize the
154+
response based on the request.
155+
:return: A WSGI :class:`werkzeug.wrappers.Response` if called without
156+
arguments or with ``environ``. A sans-IO
157+
:class:`werkzeug.sansio.Response` for ASGI if called with
158+
``scope``.
144159
"""
145-
from .wrappers.response import Response as WSGIResponse # noqa: F811
160+
from .wrappers.response import Response
146161

147162
if self.response is not None:
148163
return self.response
149164
if environ is not None:
150165
environ = _get_environ(environ)
151166
headers = self.get_headers(environ, scope)
152-
return WSGIResponse(self.get_body(environ, scope), self.code, headers)
167+
return Response(self.get_body(environ, scope), self.code, headers)
153168

154169
def __call__(
155170
self, environ: WSGIEnvironment, start_response: StartResponse
@@ -160,7 +175,7 @@ def __call__(
160175
:param start_response: the response callable provided by the WSGI
161176
server.
162177
"""
163-
response = t.cast("WSGIResponse", self.get_response(environ))
178+
response = self.get_response(environ)
164179
return response(environ, start_response)
165180

166181
def __str__(self) -> str:
@@ -295,7 +310,7 @@ class Unauthorized(HTTPException):
295310
def __init__(
296311
self,
297312
description: str | None = None,
298-
response: Response | None = None,
313+
response: SansIOResponse | None = None,
299314
www_authenticate: None | (WWWAuthenticate | t.Iterable[WWWAuthenticate]) = None,
300315
) -> None:
301316
super().__init__(description, response)
@@ -364,7 +379,7 @@ def __init__(
364379
self,
365380
valid_methods: t.Iterable[str] | None = None,
366381
description: str | None = None,
367-
response: Response | None = None,
382+
response: SansIOResponse | None = None,
368383
) -> None:
369384
"""Takes an optional list of valid http methods
370385
starting with werkzeug 0.3 the list will be mandatory."""
@@ -522,7 +537,7 @@ def __init__(
522537
length: int | None = None,
523538
units: str = "bytes",
524539
description: str | None = None,
525-
response: Response | None = None,
540+
response: SansIOResponse | None = None,
526541
) -> None:
527542
"""Takes an optional `Content-Range` header value based on ``length``
528543
parameter.
@@ -647,7 +662,7 @@ class _RetryAfter(HTTPException):
647662
def __init__(
648663
self,
649664
description: str | None = None,
650-
response: Response | None = None,
665+
response: SansIOResponse | None = None,
651666
retry_after: datetime | int | None = None,
652667
) -> None:
653668
super().__init__(description, response)
@@ -737,7 +752,7 @@ class InternalServerError(HTTPException):
737752
def __init__(
738753
self,
739754
description: str | None = None,
740-
response: Response | None = None,
755+
response: SansIOResponse | None = None,
741756
original_exception: BaseException | None = None,
742757
) -> None:
743758
#: The original exception that caused this 500 error. Can be
@@ -859,7 +874,7 @@ def __init__(
859874
self.mapping.update(extra)
860875

861876
def __call__(
862-
self, code: int | Response, *args: t.Any, **kwargs: t.Any
877+
self, code: int | SansIOResponse, *args: t.Any, **kwargs: t.Any
863878
) -> t.NoReturn:
864879
from .sansio.response import Response
865880

@@ -872,7 +887,7 @@ def __call__(
872887
raise self.mapping[code](*args, **kwargs)
873888

874889

875-
def abort(status: int | Response, *args: t.Any, **kwargs: t.Any) -> t.NoReturn:
890+
def abort(status: int | SansIOResponse, *args: t.Any, **kwargs: t.Any) -> t.NoReturn:
876891
"""Raises an :py:exc:`HTTPException` for the given status code or WSGI
877892
application.
878893

0 commit comments

Comments
 (0)