summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTal Einat <taleinat+github@gmail.com>2018-09-10 18:47:29 +0300
committerGitHub <noreply@github.com>2018-09-10 18:47:29 +0300
commitd53f1cabe8837697df4acb70c9c6537461b5eeda (patch)
treef6998cc9ac6a95c57d15ac0f28bca3c2372066f9
parente9119a5de67d5525f3be957fda239143453513bd (diff)
downloadcpython-git-d53f1cabe8837697df4acb70c9c6537461b5eeda.tar.gz
[3.7] bpo-34621: fix uuid.UUID (un)pickling compatbility with older Python versions (<3.7) (GH-9133)
-rw-r--r--Lib/test/test_uuid.py137
-rw-r--r--Lib/uuid.py17
-rw-r--r--Misc/NEWS.d/next/Library/2018-09-10-15-54-58.bpo-34621.Uqj5x3.rst2
3 files changed, 156 insertions, 0 deletions
diff --git a/Lib/test/test_uuid.py b/Lib/test/test_uuid.py
index 7af1d7aec7..dc502b97ca 100644
--- a/Lib/test/test_uuid.py
+++ b/Lib/test/test_uuid.py
@@ -2,10 +2,13 @@ import unittest.mock
from test import support
import builtins
import contextlib
+import copy
import io
import os
+import pickle
import shutil
import subprocess
+import sys
py_uuid = support.import_fresh_module('uuid', blocked=['_uuid'])
c_uuid = support.import_fresh_module('uuid', fresh=['_uuid'])
@@ -311,6 +314,140 @@ class BaseTestUUID:
node2 = self.uuid.getnode()
self.assertEqual(node1, node2, '%012x != %012x' % (node1, node2))
+ def test_pickle_roundtrip(self):
+ def check(actual, expected):
+ self.assertEqual(actual, expected)
+ self.assertEqual(actual.is_safe, expected.is_safe)
+
+ with support.swap_item(sys.modules, 'uuid', self.uuid):
+ for is_safe in self.uuid.SafeUUID:
+ u = self.uuid.UUID('d82579ce6642a0de7ddf490a7aec7aa5',
+ is_safe=is_safe)
+ check(copy.copy(u), u)
+ check(copy.deepcopy(u), u)
+ for proto in range(pickle.HIGHEST_PROTOCOL + 1):
+ with self.subTest(protocol=proto):
+ check(pickle.loads(pickle.dumps(u, proto)), u)
+
+ def test_unpickle_previous_python_versions(self):
+ def check(actual, expected):
+ self.assertEqual(actual, expected)
+ self.assertEqual(actual.is_safe, expected.is_safe)
+
+ pickled_uuids = [
+ # Python 2.7, protocol 0
+ b'ccopy_reg\n_reconstructor\n(cuuid\nUUID\nc__builtin__\nobject\nN'
+ b'tR(dS\'int\'\nL287307832597519156748809049798316161701L\nsb.',
+ # Python 2.7, protocol 1
+ b'ccopy_reg\n_reconstructor\n(cuuid\nUUID\nc__builtin__\nobject\nN'
+ b'tR}U\x03intL287307832597519156748809049798316161701L\nsb.',
+ # Python 2.7, protocol 2
+ b'\x80\x02cuuid\nUUID\n)\x81}U\x03int\x8a\x11\xa5z\xecz\nI\xdf}'
+ b'\xde\xa0Bf\xcey%\xd8\x00sb.',
+ # Python 3.6, protocol 0
+ b'ccopy_reg\n_reconstructor\n(cuuid\nUUID\nc__builtin__\nobject\nN'
+ b'tR(dVint\nL287307832597519156748809049798316161701L\nsb.',
+ # Python 3.6, protocol 1
+ b'ccopy_reg\n_reconstructor\n(cuuid\nUUID\nc__builtin__\nobject\nN'
+ b'tR}X\x03\x00\x00\x00intL287307832597519156748809049798316161701L'
+ b'\nsb.',
+ # Python 3.6, protocol 2
+ b'\x80\x02cuuid\nUUID\n)\x81}X\x03\x00\x00\x00int\x8a\x11\xa5z\xec'
+ b'z\nI\xdf}\xde\xa0Bf\xcey%\xd8\x00sb.',
+ # Python 3.6, protocol 3
+ b'\x80\x03cuuid\nUUID\n)\x81}X\x03\x00\x00\x00int\x8a\x11\xa5z\xec'
+ b'z\nI\xdf}\xde\xa0Bf\xcey%\xd8\x00sb.',
+ # Python 3.6, protocol 4
+ b'\x80\x04\x95+\x00\x00\x00\x00\x00\x00\x00\x8c\x04uuid\x8c\x04UUI'
+ b'D\x93)\x81}\x8c\x03int\x8a\x11\xa5z\xecz\nI\xdf}\xde\xa0Bf\xcey%'
+ b'\xd8\x00sb.',
+ # Python 3.7, protocol 0
+ b'ccopy_reg\n_reconstructor\n(cuuid\nUUID\nc__builtin__\nobject\nN'
+ b'tR(dVint\nL287307832597519156748809049798316161701L\nsVis_safe\n'
+ b'cuuid\nSafeUUID\n(NtRsb.',
+ # Python 3.7, protocol 1
+ b'ccopy_reg\n_reconstructor\n(cuuid\nUUID\nc__builtin__\nobject\nN'
+ b'tR}(X\x03\x00\x00\x00intL287307832597519156748809049798316161701'
+ b'L\nX\x07\x00\x00\x00is_safecuuid\nSafeUUID\n(NtRub.',
+ # Python 3.7, protocol 2
+ b'\x80\x02cuuid\nUUID\n)\x81}(X\x03\x00\x00\x00int\x8a\x11\xa5z'
+ b'\xecz\nI\xdf}\xde\xa0Bf\xcey%\xd8\x00X\x07\x00\x00\x00is_safecuu'
+ b'id\nSafeUUID\nN\x85Rub.',
+ # Python 3.7, protocol 3
+ b'\x80\x03cuuid\nUUID\n)\x81}(X\x03\x00\x00\x00int\x8a\x11\xa5z'
+ b'\xecz\nI\xdf}\xde\xa0Bf\xcey%\xd8\x00X\x07\x00\x00\x00is_safecuu'
+ b'id\nSafeUUID\nN\x85Rub.',
+ # Python 3.7, protocol 4
+ b'\x80\x04\x95F\x00\x00\x00\x00\x00\x00\x00\x8c\x04uuid\x94\x8c'
+ b'\x04UUID\x93)\x81}(\x8c\x03int\x8a\x11\xa5z\xecz\nI\xdf}\xde\xa0'
+ b'Bf\xcey%\xd8\x00\x8c\x07is_safeh\x00\x8c\x08SafeUUID\x93N\x85Rub'
+ b'.',
+ ]
+ pickled_uuids_safe = [
+ # Python 3.7, protocol 0
+ b'ccopy_reg\n_reconstructor\n(cuuid\nUUID\nc__builtin__\nobject\nN'
+ b'tR(dVint\nL287307832597519156748809049798316161701L\nsVis_safe\n'
+ b'cuuid\nSafeUUID\n(I0\ntRsb.',
+ # Python 3.7, protocol 1
+ b'ccopy_reg\n_reconstructor\n(cuuid\nUUID\nc__builtin__\nobject\nN'
+ b'tR}(X\x03\x00\x00\x00intL287307832597519156748809049798316161701'
+ b'L\nX\x07\x00\x00\x00is_safecuuid\nSafeUUID\n(K\x00tRub.',
+ # Python 3.7, protocol 2
+ b'\x80\x02cuuid\nUUID\n)\x81}(X\x03\x00\x00\x00int\x8a\x11\xa5z'
+ b'\xecz\nI\xdf}\xde\xa0Bf\xcey%\xd8\x00X\x07\x00\x00\x00is_safecuu'
+ b'id\nSafeUUID\nK\x00\x85Rub.',
+ # Python 3.7, protocol 3
+ b'\x80\x03cuuid\nUUID\n)\x81}(X\x03\x00\x00\x00int\x8a\x11\xa5z'
+ b'\xecz\nI\xdf}\xde\xa0Bf\xcey%\xd8\x00X\x07\x00\x00\x00is_safecuu'
+ b'id\nSafeUUID\nK\x00\x85Rub.',
+ # Python 3.7, protocol 4
+ b'\x80\x04\x95G\x00\x00\x00\x00\x00\x00\x00\x8c\x04uuid\x94\x8c'
+ b'\x04UUID\x93)\x81}(\x8c\x03int\x8a\x11\xa5z\xecz\nI\xdf}\xde\xa0'
+ b'Bf\xcey%\xd8\x00\x8c\x07is_safeh\x00\x8c\x08SafeUUID\x93K\x00'
+ b'\x85Rub.',
+ ]
+ pickled_uuids_unsafe = [
+ # Python 3.7, protocol 0
+ b'ccopy_reg\n_reconstructor\n(cuuid\nUUID\nc__builtin__\nobject\nN'
+ b'tR(dVint\nL287307832597519156748809049798316161701L\nsVis_safe\n'
+ b'cuuid\nSafeUUID\n(I-1\ntRsb.',
+ # Python 3.7, protocol 1
+ b'ccopy_reg\n_reconstructor\n(cuuid\nUUID\nc__builtin__\nobject\nN'
+ b'tR}(X\x03\x00\x00\x00intL287307832597519156748809049798316161701'
+ b'L\nX\x07\x00\x00\x00is_safecuuid\nSafeUUID\n(J\xff\xff\xff\xfftR'
+ b'ub.',
+ # Python 3.7, protocol 2
+ b'\x80\x02cuuid\nUUID\n)\x81}(X\x03\x00\x00\x00int\x8a\x11\xa5z'
+ b'\xecz\nI\xdf}\xde\xa0Bf\xcey%\xd8\x00X\x07\x00\x00\x00is_safecuu'
+ b'id\nSafeUUID\nJ\xff\xff\xff\xff\x85Rub.',
+ # Python 3.7, protocol 3
+ b'\x80\x03cuuid\nUUID\n)\x81}(X\x03\x00\x00\x00int\x8a\x11\xa5z'
+ b'\xecz\nI\xdf}\xde\xa0Bf\xcey%\xd8\x00X\x07\x00\x00\x00is_safecuu'
+ b'id\nSafeUUID\nJ\xff\xff\xff\xff\x85Rub.',
+ # Python 3.7, protocol 4
+ b'\x80\x04\x95J\x00\x00\x00\x00\x00\x00\x00\x8c\x04uuid\x94\x8c'
+ b'\x04UUID\x93)\x81}(\x8c\x03int\x8a\x11\xa5z\xecz\nI\xdf}\xde\xa0'
+ b'Bf\xcey%\xd8\x00\x8c\x07is_safeh\x00\x8c\x08SafeUUID\x93J\xff'
+ b'\xff\xff\xff\x85Rub.',
+ ]
+
+ u = self.uuid.UUID('d82579ce6642a0de7ddf490a7aec7aa5')
+ u_safe = self.uuid.UUID('d82579ce6642a0de7ddf490a7aec7aa5',
+ is_safe=self.uuid.SafeUUID.safe)
+ u_unsafe = self.uuid.UUID('d82579ce6642a0de7ddf490a7aec7aa5',
+ is_safe=self.uuid.SafeUUID.unsafe)
+
+ with support.swap_item(sys.modules, 'uuid', self.uuid):
+ for pickled in pickled_uuids:
+ # is_safe was added in 3.7. When unpickling values from older
+ # versions, is_safe will be missing, so it should be set to
+ # SafeUUID.unknown.
+ check(pickle.loads(pickled), u)
+ for pickled in pickled_uuids_safe:
+ check(pickle.loads(pickled), u_safe)
+ for pickled in pickled_uuids_unsafe:
+ check(pickle.loads(pickled), u_unsafe)
+
# bpo-32502: UUID1 requires a 48-bit identifier, but hardware identifiers
# need not necessarily be 48 bits (e.g., EUI-64).
def test_uuid1_eui64(self):
diff --git a/Lib/uuid.py b/Lib/uuid.py
index 66383218e7..26faa1accd 100644
--- a/Lib/uuid.py
+++ b/Lib/uuid.py
@@ -204,6 +204,23 @@ class UUID:
self.__dict__['int'] = int
self.__dict__['is_safe'] = is_safe
+ def __getstate__(self):
+ state = self.__dict__
+ if self.is_safe != SafeUUID.unknown:
+ # is_safe is a SafeUUID instance. Return just its value, so that
+ # it can be un-pickled in older Python versions without SafeUUID.
+ state = state.copy()
+ state['is_safe'] = self.is_safe.value
+ return state
+
+ def __setstate__(self, state):
+ self.__dict__.update(state)
+ # is_safe was added in 3.7; it is also omitted when it is "unknown"
+ self.__dict__['is_safe'] = (
+ SafeUUID(state['is_safe'])
+ if 'is_safe' in state else SafeUUID.unknown
+ )
+
def __eq__(self, other):
if isinstance(other, UUID):
return self.int == other.int
diff --git a/Misc/NEWS.d/next/Library/2018-09-10-15-54-58.bpo-34621.Uqj5x3.rst b/Misc/NEWS.d/next/Library/2018-09-10-15-54-58.bpo-34621.Uqj5x3.rst
new file mode 100644
index 0000000000..1fd60608ae
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2018-09-10-15-54-58.bpo-34621.Uqj5x3.rst
@@ -0,0 +1,2 @@
+Fix un/pickling compatbility of uuid.UUID objects with older versions of
+Python (<3.7).