Kaydet (Commit) 42b5e4fe authored tarafından Tim Graham's avatar Tim Graham

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.
üst 4e9a6c94
from __future__ import unicode_literals from __future__ import unicode_literals
import sys
from django.utils.encoding import force_str from django.utils.encoding import force_str
from django.utils import six from django.utils import six
...@@ -15,12 +16,29 @@ try: ...@@ -15,12 +16,29 @@ try:
except http_cookies.CookieError: except http_cookies.CookieError:
_cookie_allows_colon_in_names = False _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 SimpleCookie = http_cookies.SimpleCookie
else: else:
Morsel = http_cookies.Morsel Morsel = http_cookies.Morsel
class SimpleCookie(http_cookies.SimpleCookie): 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: if not _cookie_encodes_correctly:
def value_encode(self, val): def value_encode(self, val):
# Some browsers do not support quoted-string from RFC 2109, # Some browsers do not support quoted-string from RFC 2109,
......
...@@ -206,17 +206,6 @@ class HttpResponseBase(six.Iterator): ...@@ -206,17 +206,6 @@ class HttpResponseBase(six.Iterator):
def __getitem__(self, header): def __getitem__(self, header):
return self._headers[header.lower()][1] 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): def has_header(self, header):
"""Case-insensitive check for a header.""" """Case-insensitive check for a header."""
return header.lower() in self._headers return header.lower() in self._headers
......
...@@ -39,7 +39,7 @@ class SimpleTemplateResponse(HttpResponse): ...@@ -39,7 +39,7 @@ class SimpleTemplateResponse(HttpResponse):
rendered, and that the pickled state only includes rendered rendered, and that the pickled state only includes rendered
data, not the data used to construct the response. 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: if not self._is_rendered:
raise ContentNotRenderedError('The response content must be ' raise ContentNotRenderedError('The response content must be '
'rendered before it can be pickled.') 'rendered before it can be pickled.')
......
...@@ -631,7 +631,7 @@ class CookieTests(unittest.TestCase): ...@@ -631,7 +631,7 @@ class CookieTests(unittest.TestCase):
c = SimpleCookie() c = SimpleCookie()
c['test'] = "An,awkward;value" c['test'] = "An,awkward;value"
c2 = SimpleCookie() c2 = SimpleCookie()
c2.load(c.output()) c2.load(c.output()[12:])
self.assertEqual(c['test'].value, c2['test'].value) self.assertEqual(c['test'].value, c2['test'].value)
def test_decode_2(self): def test_decode_2(self):
...@@ -641,7 +641,7 @@ class CookieTests(unittest.TestCase): ...@@ -641,7 +641,7 @@ class CookieTests(unittest.TestCase):
c = SimpleCookie() c = SimpleCookie()
c['test'] = b"\xf0" c['test'] = b"\xf0"
c2 = SimpleCookie() c2 = SimpleCookie()
c2.load(c.output()) c2.load(c.output()[12:])
self.assertEqual(c['test'].value, c2['test'].value) self.assertEqual(c['test'].value, c2['test'].value)
def test_nonstandard_keys(self): def test_nonstandard_keys(self):
...@@ -678,3 +678,15 @@ class CookieTests(unittest.TestCase): ...@@ -678,3 +678,15 @@ class CookieTests(unittest.TestCase):
r = HttpResponse() r = HttpResponse()
r.set_cookie("a:.b/", 1) r.set_cookie("a:.b/", 1)
self.assertEqual(len(r.cookies.bad_cookies), 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)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment