summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Graham <timograham@gmail.com>2014-10-31 14:26:27 -0400
committerTim Graham <timograham@gmail.com>2014-11-12 19:04:45 +0100
commit42b5e4feeacf7cfa57867bf9fd5a6046de8c1cd3 (patch)
tree41ab0e055e2c3a5870572b4b70ec7e89112a5784
parent4e9a6c94e6bea805e089df2dee2d4ab2c902c827 (diff)
Fixed #23730 -- Moved support for SimpleCookie HIGHEST_PROTOCOL pickling to http.cookie.
This fix is necessary for Python 3.5 compatibility (refs #23763). Thanks Berker Peksag for review.
-rw-r--r--django/http/cookie.py20
-rw-r--r--django/http/response.py11
-rw-r--r--django/template/response.py2
-rw-r--r--tests/httpwrappers/tests.py16
4 files changed, 34 insertions, 15 deletions
diff --git a/django/http/cookie.py b/django/http/cookie.py
index 7084c87766..3bd9065d3a 100644
--- a/django/http/cookie.py
+++ b/django/http/cookie.py
@@ -1,4 +1,5 @@
from __future__ import unicode_literals
+import sys
from django.utils.encoding import force_str
from django.utils import six
@@ -15,12 +16,29 @@ try:
except http_cookies.CookieError:
_cookie_allows_colon_in_names = False
-if _cookie_encodes_correctly and _cookie_allows_colon_in_names:
+# Cookie pickling bug is fixed in Python 2.7.9 and Python 3.4.3+
+# http://bugs.python.org/issue22775
+cookie_pickles_properly = (
+ (sys.version_info[:2] == (2, 7) and sys.version_info >= (2, 7, 9)) or
+ sys.version_info >= (3, 4, 3)
+)
+
+if _cookie_encodes_correctly and _cookie_allows_colon_in_names and cookie_pickles_properly:
SimpleCookie = http_cookies.SimpleCookie
else:
Morsel = http_cookies.Morsel
class SimpleCookie(http_cookies.SimpleCookie):
+ if not cookie_pickles_properly:
+ def __setitem__(self, key, value):
+ # Apply the fix from http://bugs.python.org/issue22775 where
+ # it's not fixed in Python itself
+ if isinstance(value, Morsel):
+ # allow assignment of constructed Morsels (e.g. for pickling)
+ dict.__setitem__(self, key, value)
+ else:
+ super(SimpleCookie, self).__setitem__(key, value)
+
if not _cookie_encodes_correctly:
def value_encode(self, val):
# Some browsers do not support quoted-string from RFC 2109,
diff --git a/django/http/response.py b/django/http/response.py
index 9e8280a307..3edf10d1e8 100644
--- a/django/http/response.py
+++ b/django/http/response.py
@@ -206,17 +206,6 @@ class HttpResponseBase(six.Iterator):
def __getitem__(self, header):
return self._headers[header.lower()][1]
- def __getstate__(self):
- # SimpleCookie is not pickleable with pickle.HIGHEST_PROTOCOL, so we
- # serialize to a string instead
- state = self.__dict__.copy()
- state['cookies'] = str(state['cookies'])
- return state
-
- def __setstate__(self, state):
- self.__dict__.update(state)
- self.cookies = SimpleCookie(self.cookies)
-
def has_header(self, header):
"""Case-insensitive check for a header."""
return header.lower() in self._headers
diff --git a/django/template/response.py b/django/template/response.py
index f43a4e0f53..53d2795868 100644
--- a/django/template/response.py
+++ b/django/template/response.py
@@ -39,7 +39,7 @@ class SimpleTemplateResponse(HttpResponse):
rendered, and that the pickled state only includes rendered
data, not the data used to construct the response.
"""
- obj_dict = super(SimpleTemplateResponse, self).__getstate__()
+ obj_dict = self.__dict__.copy()
if not self._is_rendered:
raise ContentNotRenderedError('The response content must be '
'rendered before it can be pickled.')
diff --git a/tests/httpwrappers/tests.py b/tests/httpwrappers/tests.py
index 4e705e2aeb..cc324b76cd 100644
--- a/tests/httpwrappers/tests.py
+++ b/tests/httpwrappers/tests.py
@@ -631,7 +631,7 @@ class CookieTests(unittest.TestCase):
c = SimpleCookie()
c['test'] = "An,awkward;value"
c2 = SimpleCookie()
- c2.load(c.output())
+ c2.load(c.output()[12:])
self.assertEqual(c['test'].value, c2['test'].value)
def test_decode_2(self):
@@ -641,7 +641,7 @@ class CookieTests(unittest.TestCase):
c = SimpleCookie()
c['test'] = b"\xf0"
c2 = SimpleCookie()
- c2.load(c.output())
+ c2.load(c.output()[12:])
self.assertEqual(c['test'].value, c2['test'].value)
def test_nonstandard_keys(self):
@@ -678,3 +678,15 @@ class CookieTests(unittest.TestCase):
r = HttpResponse()
r.set_cookie("a:.b/", 1)
self.assertEqual(len(r.cookies.bad_cookies), 1)
+
+ def test_pickle(self):
+ rawdata = 'Customer="WILE_E_COYOTE"; Path=/acme; Version=1'
+ expected_output = 'Set-Cookie: %s' % rawdata
+
+ C = SimpleCookie()
+ C.load(rawdata)
+ self.assertEqual(C.output(), expected_output)
+
+ for proto in range(pickle.HIGHEST_PROTOCOL + 1):
+ C1 = pickle.loads(pickle.dumps(C, protocol=proto))
+ self.assertEqual(C1.output(), expected_output)