mirror of https://github.com/django/django.git
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.
This commit is contained in:
parent
4e9a6c94e6
commit
42b5e4feea
|
@ -1,4 +1,5 @@
|
||||||
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:
|
||||||
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):
|
||||||
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):
|
||||||
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):
|
||||||
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):
|
||||||
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):
|
||||||
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)
|
||||||
|
|
Loading…
Reference in New Issue