Skip to content

Commit

Permalink
Refs #34482 -- Reverted "Fixed #32969 -- Fixed pickling HttpResponse …
Browse files Browse the repository at this point in the history
…and subclasses."

This reverts commit d7f5bfd.

Thanks Márton Salomváry for the report.
  • Loading branch information
felixxm committed Apr 12, 2023
1 parent 280ca14 commit 173034b
Show file tree
Hide file tree
Showing 5 changed files with 23 additions and 40 deletions.
19 changes: 0 additions & 19 deletions django/http/response.py
Expand Up @@ -369,31 +369,12 @@ class HttpResponse(HttpResponseBase):
"""

streaming = False
non_picklable_attrs = frozenset(
[
"resolver_match",
# Non-picklable attributes added by test clients.
"asgi_request",
"client",
"context",
"json",
"templates",
"wsgi_request",
]
)

def __init__(self, content=b"", *args, **kwargs):
super().__init__(*args, **kwargs)
# Content is a bytestring. See the `content` property methods.
self.content = content

def __getstate__(self):
obj_dict = self.__dict__.copy()
for attr in self.non_picklable_attrs:
if attr in obj_dict:
del obj_dict[attr]
return obj_dict

def __repr__(self):
return "<%(cls)s status_code=%(status_code)d%(content_type)s>" % {
"cls": self.__class__.__name__,
Expand Down
15 changes: 8 additions & 7 deletions django/template/response.py
Expand Up @@ -8,9 +8,7 @@ class ContentNotRenderedError(Exception):


class SimpleTemplateResponse(HttpResponse):
non_picklable_attrs = HttpResponse.non_picklable_attrs | frozenset(
["template_name", "context_data", "_post_render_callbacks"]
)
rendering_attrs = ["template_name", "context_data", "_post_render_callbacks"]

def __init__(
self,
Expand Down Expand Up @@ -57,11 +55,16 @@ def __getstate__(self):
Raise an exception if trying to pickle an unrendered response. Pickle
only rendered data, not the data used to construct the response.
"""
obj_dict = self.__dict__.copy()
if not self._is_rendered:
raise ContentNotRenderedError(
"The response content must be rendered before it can be pickled."
)
return super().__getstate__()
for attr in self.rendering_attrs:
if attr in obj_dict:
del obj_dict[attr]

return obj_dict

def resolve_template(self, template):
"""Accept a template object, path-to-template, or list of paths."""
Expand Down Expand Up @@ -142,9 +145,7 @@ def content(self, value):


class TemplateResponse(SimpleTemplateResponse):
non_picklable_attrs = SimpleTemplateResponse.non_picklable_attrs | frozenset(
["_request"]
)
rendering_attrs = SimpleTemplateResponse.rendering_attrs + ["_request"]

def __init__(
self,
Expand Down
4 changes: 2 additions & 2 deletions docs/releases/4.2.1.txt
Expand Up @@ -31,5 +31,5 @@ Bugfixes
language was used (:ticket:`34455`).

* Fixed a regression in Django 4.2 where creating copies and deep copies of
``HttpRequest`` and its subclasses didn't always work correctly
(:ticket:`34482`, :ticket:`34484`).
``HttpRequest``, ``HttpResponse``, and their subclasses didn't always work
correctly (:ticket:`34482`, :ticket:`34484`).
3 changes: 0 additions & 3 deletions docs/releases/4.2.txt
Expand Up @@ -416,9 +416,6 @@ fields modified in the custom ``save()`` methods should be added to the
Miscellaneous
-------------

* The undocumented ``SimpleTemplateResponse.rendering_attrs`` and
``TemplateResponse.rendering_attrs`` are renamed to ``non_picklable_attrs``.

* The undocumented ``django.http.multipartparser.parse_header()`` function is
removed. Use ``django.utils.http.parse_header_parameters()`` instead.

Expand Down
22 changes: 13 additions & 9 deletions tests/test_client/tests.py
Expand Up @@ -19,8 +19,8 @@
rather than the HTML rendered to the end-user.
"""
import copy
import itertools
import pickle
import tempfile
from unittest import mock

Expand Down Expand Up @@ -81,20 +81,24 @@ def test_get_view(self):
self.assertEqual(response.context["var"], "\xf2")
self.assertEqual(response.templates[0].name, "GET Template")

def test_pickling_response(self):
def test_copy_response(self):
tests = ["/cbv_view/", "/get_view/"]
for url in tests:
with self.subTest(url=url):
response = self.client.get(url)
dump = pickle.dumps(response)
response_from_pickle = pickle.loads(dump)
self.assertEqual(repr(response), repr(response_from_pickle))
response_copy = copy.copy(response)
self.assertEqual(repr(response), repr(response_copy))
self.assertIs(response_copy.client, response.client)
self.assertIs(response_copy.resolver_match, response.resolver_match)
self.assertIs(response_copy.wsgi_request, response.wsgi_request)

async def test_pickling_response_async(self):
async def test_copy_response_async(self):
response = await self.async_client.get("/async_get_view/")
dump = pickle.dumps(response)
response_from_pickle = pickle.loads(dump)
self.assertEqual(repr(response), repr(response_from_pickle))
response_copy = copy.copy(response)
self.assertEqual(repr(response), repr(response_copy))
self.assertIs(response_copy.client, response.client)
self.assertIs(response_copy.resolver_match, response.resolver_match)
self.assertIs(response_copy.asgi_request, response.asgi_request)

def test_query_string_encoding(self):
# WSGI requires latin-1 encoded strings.
Expand Down

0 comments on commit 173034b

Please sign in to comment.