[4.2.x] Refs #34482 -- Reverted "Fixed #32969 -- Fixed pickling HttpResponse and subclasses."

This reverts commit d7f5bfd241666c0a76e90208da1e9ef81aec44db.

Thanks Márton Salomváry for the report.

Backport of 173034b00589c083793d495e8b07e35be2cb1cf0 from main
This commit is contained in:
Mariusz Felisiak 2023-04-12 09:46:18 +02:00
parent 2feb9333e7
commit 791407fef1
5 changed files with 23 additions and 40 deletions

View File

@ -369,31 +369,12 @@ class HttpResponse(HttpResponseBase):
""" """
streaming = False 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): def __init__(self, content=b"", *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
# Content is a bytestring. See the `content` property methods. # Content is a bytestring. See the `content` property methods.
self.content = content 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): def __repr__(self):
return "<%(cls)s status_code=%(status_code)d%(content_type)s>" % { return "<%(cls)s status_code=%(status_code)d%(content_type)s>" % {
"cls": self.__class__.__name__, "cls": self.__class__.__name__,

View File

@ -8,9 +8,7 @@ class ContentNotRenderedError(Exception):
class SimpleTemplateResponse(HttpResponse): class SimpleTemplateResponse(HttpResponse):
non_picklable_attrs = HttpResponse.non_picklable_attrs | frozenset( rendering_attrs = ["template_name", "context_data", "_post_render_callbacks"]
["template_name", "context_data", "_post_render_callbacks"]
)
def __init__( def __init__(
self, self,
@ -57,11 +55,16 @@ class SimpleTemplateResponse(HttpResponse):
Raise an exception if trying to pickle an unrendered response. Pickle Raise an exception if trying to pickle an unrendered response. Pickle
only rendered data, not the data used to construct the response. only rendered data, not the data used to construct the response.
""" """
obj_dict = self.__dict__.copy()
if not self._is_rendered: if not self._is_rendered:
raise ContentNotRenderedError( raise ContentNotRenderedError(
"The response content must be rendered before it can be pickled." "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): def resolve_template(self, template):
"""Accept a template object, path-to-template, or list of paths.""" """Accept a template object, path-to-template, or list of paths."""
@ -142,9 +145,7 @@ class SimpleTemplateResponse(HttpResponse):
class TemplateResponse(SimpleTemplateResponse): class TemplateResponse(SimpleTemplateResponse):
non_picklable_attrs = SimpleTemplateResponse.non_picklable_attrs | frozenset( rendering_attrs = SimpleTemplateResponse.rendering_attrs + ["_request"]
["_request"]
)
def __init__( def __init__(
self, self,

View File

@ -31,5 +31,5 @@ Bugfixes
language was used (:ticket:`34455`). language was used (:ticket:`34455`).
* Fixed a regression in Django 4.2 where creating copies and deep copies of * Fixed a regression in Django 4.2 where creating copies and deep copies of
``HttpRequest`` and its subclasses didn't always work correctly ``HttpRequest``, ``HttpResponse``, and their subclasses didn't always work
(:ticket:`34482`, :ticket:`34484`). correctly (:ticket:`34482`, :ticket:`34484`).

View File

@ -416,9 +416,6 @@ fields modified in the custom ``save()`` methods should be added to the
Miscellaneous 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 * The undocumented ``django.http.multipartparser.parse_header()`` function is
removed. Use ``django.utils.http.parse_header_parameters()`` instead. removed. Use ``django.utils.http.parse_header_parameters()`` instead.

View File

@ -19,8 +19,8 @@ testing against the contexts and templates produced by a view,
rather than the HTML rendered to the end-user. rather than the HTML rendered to the end-user.
""" """
import copy
import itertools import itertools
import pickle
import tempfile import tempfile
from unittest import mock from unittest import mock
@ -81,20 +81,24 @@ class ClientTest(TestCase):
self.assertEqual(response.context["var"], "\xf2") self.assertEqual(response.context["var"], "\xf2")
self.assertEqual(response.templates[0].name, "GET Template") self.assertEqual(response.templates[0].name, "GET Template")
def test_pickling_response(self): def test_copy_response(self):
tests = ["/cbv_view/", "/get_view/"] tests = ["/cbv_view/", "/get_view/"]
for url in tests: for url in tests:
with self.subTest(url=url): with self.subTest(url=url):
response = self.client.get(url) response = self.client.get(url)
dump = pickle.dumps(response) response_copy = copy.copy(response)
response_from_pickle = pickle.loads(dump) self.assertEqual(repr(response), repr(response_copy))
self.assertEqual(repr(response), repr(response_from_pickle)) 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/") response = await self.async_client.get("/async_get_view/")
dump = pickle.dumps(response) response_copy = copy.copy(response)
response_from_pickle = pickle.loads(dump) self.assertEqual(repr(response), repr(response_copy))
self.assertEqual(repr(response), repr(response_from_pickle)) 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): def test_query_string_encoding(self):
# WSGI requires latin-1 encoded strings. # WSGI requires latin-1 encoded strings.