#34484 closed Bug (fixed)
HttpRequest.__deepcopy__ doesn't deepcopy attributes
Reported by: | Adam Johnson | Owned by: | Mariusz Felisiak |
---|---|---|---|
Component: | HTTP handling | Version: | 4.2 |
Severity: | Release blocker | Keywords: | |
Cc: | Triage Stage: | Accepted | |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description (last modified by )
Regression in Django 4.2. Deepcopying a HttpRequest no longer deepcopies its attributes, including attached ones like session
.
Leads to test pollution where a request is created in setUpTestData, for example:
from django.test import TestCase from django.test import RequestFactory class ExampleTests(TestCase): @classmethod def setUpTestData(cls): cls.request = RequestFactory().get("/") cls.request.session = {} def test_adding(self): self.request.session["foo"] = 1 self.assertEqual(self.request.session, {"foo": 1}) def test_looking(self): self.assertEqual(self.request.session, {})
Leading to:
test_adding (test_regression.ExampleTests.test_adding) ... ok test_looking (test_regression.ExampleTests.test_looking) ... FAIL ====================================================================== FAIL: test_looking (test_regression.ExampleTests.test_looking) ---------------------------------------------------------------------- Traceback (most recent call last): File "/Users/chainz/Documents/Projects/django/tests/test_regression.py", line 16, in test_looking self.assertEqual(self.request.session, {}) AssertionError: {'foo': 1} != {} - {'foo': 1} + {}
(Simplified from a real test suite.)
Bisected to #29186 / 6220c445c40a6a7f4d442de8bde2628346153963, using these commands to run the above test case:
git bisect start facc153af7 ff8e5eacda git bisect run sh -c 'cd tests && ./runtests.py test_regression -v 2'
I see #34482 was also just opened as a regression from that same ticket.
Change History (9)
comment:1 by , 21 months ago
Description: | modified (diff) |
---|
comment:2 by , 21 months ago
comment:3 by , 21 months ago
Adam, Would you like to revert both? (d7f5bfd241666c0a76e90208da1e9ef81aec44db and 6220c445c40a6a7f4d442de8bde2628346153963.)
comment:4 by , 21 months ago
Maybe...
On the HttpResponse change, it does seem quite hacky to have HttpResponse know about the attributes the test client adds and remove them at pickle time. It will cause issues in tests that end up copying responses and expect the attributes to still exist.
There are definitely tests out there that make requests within setUp and store the responses for assertions within actual test methods - this is a pattern that Will Vincent promotes in his books... The issue will occur if such a test is converted to use setUpTestData, which can easily happen since it's promoted for speed.
comment:5 by , 21 months ago
Triage Stage: | Unreviewed → Accepted |
---|
comment:6 by , 21 months ago
Owner: | changed from | to
---|---|
Status: | new → assigned |
I think there are a couple of issues in the original patch, which is why we have two tickets:
(1)
__getstate__
completely drops attributes, so unpickled request objects end up missing attributes, hence #34482. Dropping attributes entirely in__getstate__
should only be done where the interface of the class works with a missing private attribute, otherwise unpickling is broken.(2)
__deepcopy__
performs a shallow copy of the object, hence this ticket. It looks like this was added after this comment from Mariusz during review, in order to fix test failures. I think in reality the failures were exposing problems in the__getstate__
implementation, and adding a__deepcopy__
only papered over them.I reproduced the failure Mariusz talked about in that comment, by removing
__deepcopy__
:A missing attribute is exactly the problem as in (1).
I lean towards reverting the patch, and then working on a new one. This stuff is hard to get right, and it would be better to work on it calmly rather than rushing through a fix. I'll also note the patch also missed tests for
WSGIRequest
andASGIRequest
.