diff --git a/django/core/handlers/wsgi.py b/django/core/handlers/wsgi.py index 7c94758e86..3f69bfaaae 100644 --- a/django/core/handlers/wsgi.py +++ b/django/core/handlers/wsgi.py @@ -16,9 +16,6 @@ from django.utils.encoding import force_str, force_text from django.utils.functional import cached_property from django.utils import six -# For backwards compatibility -- lots of code uses this in the wild! -from django.http.response import REASON_PHRASES as STATUS_CODE_TEXT # NOQA - logger = logging.getLogger('django.request') # encode() and decode() expect the charset to be a native string. diff --git a/django/http/response.py b/django/http/response.py index 560e2ac7a5..50c7a0e04a 100644 --- a/django/http/response.py +++ b/django/http/response.py @@ -17,73 +17,10 @@ from django.utils import six, timezone from django.utils.encoding import force_bytes, force_text, force_str, iri_to_uri from django.utils.http import cookie_date from django.utils.six.moves import map +from django.utils.six.moves.http_client import responses from django.utils.six.moves.urllib.parse import urlparse -# See http://www.iana.org/assignments/http-status-codes -REASON_PHRASES = { - 100: 'CONTINUE', - 101: 'SWITCHING PROTOCOLS', - 102: 'PROCESSING', - 200: 'OK', - 201: 'CREATED', - 202: 'ACCEPTED', - 203: 'NON-AUTHORITATIVE INFORMATION', - 204: 'NO CONTENT', - 205: 'RESET CONTENT', - 206: 'PARTIAL CONTENT', - 207: 'MULTI-STATUS', - 208: 'ALREADY REPORTED', - 226: 'IM USED', - 300: 'MULTIPLE CHOICES', - 301: 'MOVED PERMANENTLY', - 302: 'FOUND', - 303: 'SEE OTHER', - 304: 'NOT MODIFIED', - 305: 'USE PROXY', - 306: 'RESERVED', - 307: 'TEMPORARY REDIRECT', - 308: 'PERMANENT REDIRECT', - 400: 'BAD REQUEST', - 401: 'UNAUTHORIZED', - 402: 'PAYMENT REQUIRED', - 403: 'FORBIDDEN', - 404: 'NOT FOUND', - 405: 'METHOD NOT ALLOWED', - 406: 'NOT ACCEPTABLE', - 407: 'PROXY AUTHENTICATION REQUIRED', - 408: 'REQUEST TIMEOUT', - 409: 'CONFLICT', - 410: 'GONE', - 411: 'LENGTH REQUIRED', - 412: 'PRECONDITION FAILED', - 413: 'REQUEST ENTITY TOO LARGE', - 414: 'REQUEST-URI TOO LONG', - 415: 'UNSUPPORTED MEDIA TYPE', - 416: 'REQUESTED RANGE NOT SATISFIABLE', - 417: 'EXPECTATION FAILED', - 418: "I'M A TEAPOT", - 422: 'UNPROCESSABLE ENTITY', - 423: 'LOCKED', - 424: 'FAILED DEPENDENCY', - 426: 'UPGRADE REQUIRED', - 428: 'PRECONDITION REQUIRED', - 429: 'TOO MANY REQUESTS', - 431: 'REQUEST HEADER FIELDS TOO LARGE', - 500: 'INTERNAL SERVER ERROR', - 501: 'NOT IMPLEMENTED', - 502: 'BAD GATEWAY', - 503: 'SERVICE UNAVAILABLE', - 504: 'GATEWAY TIMEOUT', - 505: 'HTTP VERSION NOT SUPPORTED', - 506: 'VARIANT ALSO NEGOTIATES', - 507: 'INSUFFICIENT STORAGE', - 508: 'LOOP DETECTED', - 510: 'NOT EXTENDED', - 511: 'NETWORK AUTHENTICATION REQUIRED', -} - - _charset_from_content_type_re = re.compile(r';\s*charset=(?P[^\s;]+)', re.I) @@ -118,8 +55,7 @@ class HttpResponseBase(six.Iterator): if reason is not None: self.reason_phrase = reason elif self.reason_phrase is None: - self.reason_phrase = REASON_PHRASES.get(self.status_code, - 'UNKNOWN STATUS CODE') + self.reason_phrase = responses.get(self.status_code, 'Unknown Status Code') self._charset = charset if content_type is None: content_type = '%s; charset=%s' % (settings.DEFAULT_CONTENT_TYPE, diff --git a/docs/ref/request-response.txt b/docs/ref/request-response.txt index 41e6d3dcdc..ffe2bf4cd3 100644 --- a/docs/ref/request-response.txt +++ b/docs/ref/request-response.txt @@ -629,6 +629,13 @@ Attributes The HTTP reason phrase for the response. + .. versionchanged:: 1.9 + + ``reason_phrase`` no longer defaults to all capital letters. It now + uses the `HTTP standard's`_ default reason phrases. + + .. _`HTTP standard's`: https://www.ietf.org/rfc/rfc2616.txt + .. attribute:: HttpResponse.streaming This is always ``False``. @@ -980,6 +987,13 @@ Attributes The HTTP reason phrase for the response. + .. versionchanged:: 1.9 + + ``reason_phrase`` no longer defaults to all capital letters. It now + uses the `HTTP standard's`_ default reason phrases. + + .. _`HTTP standard's`: https://www.ietf.org/rfc/rfc2616.txt + .. attribute:: StreamingHttpResponse.streaming This is always ``True``. diff --git a/docs/releases/1.9.txt b/docs/releases/1.9.txt index e15c1d9b60..e51d3b0afe 100644 --- a/docs/releases/1.9.txt +++ b/docs/releases/1.9.txt @@ -171,6 +171,13 @@ Miscellaneous * CSS and images in ``contrib.admin`` to support Internet Explorer 6 & 7 have been removed as these browsers have reached end-of-life. +* ``django.http.responses.REASON_PHRASES`` and + ``django.core.handlers.wsgi.STATUS_CODE_TEXT`` have been removed. Use + Python's stdlib instead: :data:`http.client.responses` for Python 3 and + `httplib.responses`_ for Python 2. + + .. _`httplib.responses`: https://docs.python.org/2/library/httplib.html#httplib.responses + .. _deprecated-features-1.9: Features deprecated in 1.9 diff --git a/tests/responses/tests.py b/tests/responses/tests.py index 892c09b857..bf7a0e12ef 100644 --- a/tests/responses/tests.py +++ b/tests/responses/tests.py @@ -50,9 +50,9 @@ class HttpResponseBaseTests(SimpleTestCase): class HttpResponseTests(SimpleTestCase): def test_status_code(self): - resp = HttpResponse(status=418) - self.assertEqual(resp.status_code, 418) - self.assertEqual(resp.reason_phrase, "I'M A TEAPOT") + resp = HttpResponse(status=503) + self.assertEqual(resp.status_code, 503) + self.assertEqual(resp.reason_phrase, "Service Unavailable") def test_reason_phrase(self): reason = "I'm an anarchist coffee pot on crack."