Tim Peters | c0c9370 | 2003-02-13 19:30:57 +0000 | [diff] [blame] | 1 | # test_pickle dumps and loads pickles via pickle.py. |
| 2 | # test_cpickle does the same, but via the cPickle module. |
| 3 | # This test covers the other two cases, making pickles with one module and |
Collin Winter | f8089c7 | 2009-04-09 16:46:46 +0000 | [diff] [blame] | 4 | # loading them via the other. It also tests backwards compatibility with |
| 5 | # previous version of Python by bouncing pickled objects through Python 2.4 |
| 6 | # and Python 2.5 running this file. |
Tim Peters | c0c9370 | 2003-02-13 19:30:57 +0000 | [diff] [blame] | 7 | |
Tim Peters | c0c9370 | 2003-02-13 19:30:57 +0000 | [diff] [blame] | 8 | import cPickle |
Collin Winter | f8089c7 | 2009-04-09 16:46:46 +0000 | [diff] [blame] | 9 | import os |
| 10 | import os.path |
| 11 | import pickle |
| 12 | import subprocess |
| 13 | import sys |
| 14 | import types |
| 15 | import unittest |
Tim Peters | c0c9370 | 2003-02-13 19:30:57 +0000 | [diff] [blame] | 16 | |
| 17 | from test import test_support |
Collin Winter | f8089c7 | 2009-04-09 16:46:46 +0000 | [diff] [blame] | 18 | |
| 19 | # Most distro-supplied Pythons don't include the tests |
| 20 | # or test support files, and some don't include a way to get these back even if |
| 21 | # you're will to install extra packages (like Ubuntu). Doing things like this |
| 22 | # "provides" a pickletester module for older versions of Python that may be |
| 23 | # installed without it. Note that one other design for this involves messing |
| 24 | # with sys.path, which is less precise. |
| 25 | mod_path = os.path.abspath(os.path.join(os.path.dirname(__file__), |
| 26 | "pickletester.py")) |
| 27 | pickletester = types.ModuleType("test.pickletester") |
Ezio Melotti | dde5b94 | 2010-02-03 05:37:26 +0000 | [diff] [blame] | 28 | exec compile(open(mod_path).read(), mod_path, 'exec') in pickletester.__dict__ |
Collin Winter | f8089c7 | 2009-04-09 16:46:46 +0000 | [diff] [blame] | 29 | AbstractPickleTests = pickletester.AbstractPickleTests |
| 30 | if pickletester.__name__ in sys.modules: |
| 31 | raise RuntimeError("Did not expect to find test.pickletester loaded") |
| 32 | sys.modules[pickletester.__name__] = pickletester |
| 33 | |
Tim Peters | c0c9370 | 2003-02-13 19:30:57 +0000 | [diff] [blame] | 34 | |
| 35 | class DumpCPickle_LoadPickle(AbstractPickleTests): |
| 36 | |
| 37 | error = KeyError |
| 38 | |
Collin Winter | f8089c7 | 2009-04-09 16:46:46 +0000 | [diff] [blame] | 39 | def dumps(self, arg, proto=0, fast=False): |
Tim Peters | c0c9370 | 2003-02-13 19:30:57 +0000 | [diff] [blame] | 40 | # Ignore fast |
| 41 | return cPickle.dumps(arg, proto) |
| 42 | |
| 43 | def loads(self, buf): |
| 44 | # Ignore fast |
| 45 | return pickle.loads(buf) |
| 46 | |
| 47 | class DumpPickle_LoadCPickle(AbstractPickleTests): |
| 48 | |
| 49 | error = cPickle.BadPickleGet |
| 50 | |
Collin Winter | f8089c7 | 2009-04-09 16:46:46 +0000 | [diff] [blame] | 51 | def dumps(self, arg, proto=0, fast=False): |
Tim Peters | c0c9370 | 2003-02-13 19:30:57 +0000 | [diff] [blame] | 52 | # Ignore fast |
| 53 | return pickle.dumps(arg, proto) |
| 54 | |
| 55 | def loads(self, buf): |
| 56 | # Ignore fast |
| 57 | return cPickle.loads(buf) |
| 58 | |
Serhiy Storchaka | 0d8fe3b | 2015-01-27 22:44:45 +0200 | [diff] [blame] | 59 | def have_python_version(name, cache={}): |
Antoine Pitrou | 739a59c | 2010-10-10 14:53:36 +0000 | [diff] [blame] | 60 | """Check whether the given name is a valid Python binary and has |
| 61 | test.test_support. |
Collin Winter | f8089c7 | 2009-04-09 16:46:46 +0000 | [diff] [blame] | 62 | |
| 63 | This respects your PATH. |
| 64 | |
| 65 | Args: |
| 66 | name: short string name of a Python binary such as "python2.4". |
| 67 | |
| 68 | Returns: |
| 69 | True if the name is valid, False otherwise. |
| 70 | """ |
Serhiy Storchaka | 0d8fe3b | 2015-01-27 22:44:45 +0200 | [diff] [blame] | 71 | if name not in cache: |
| 72 | cache[name] = os.system(name + ' -c "import test.test_support"') == 0 |
| 73 | return cache[name] |
Collin Winter | f8089c7 | 2009-04-09 16:46:46 +0000 | [diff] [blame] | 74 | |
| 75 | |
| 76 | class AbstractCompatTests(AbstractPickleTests): |
| 77 | |
| 78 | module = None |
| 79 | python = None |
| 80 | error = None |
| 81 | |
| 82 | def setUp(self): |
| 83 | self.assertTrue(self.python) |
| 84 | self.assertTrue(self.module) |
| 85 | self.assertTrue(self.error) |
Serhiy Storchaka | 0d8fe3b | 2015-01-27 22:44:45 +0200 | [diff] [blame] | 86 | test_support.requires("xpickle") |
| 87 | if not have_python_version(self.python): |
| 88 | self.skipTest('%s not available' % self.python) |
Collin Winter | f8089c7 | 2009-04-09 16:46:46 +0000 | [diff] [blame] | 89 | |
| 90 | def send_to_worker(self, python, obj, proto): |
| 91 | """Bounce a pickled object through another version of Python. |
| 92 | |
| 93 | This will pickle the object, send it to a child process where it will be |
| 94 | unpickled, then repickled and sent back to the parent process. |
| 95 | |
| 96 | Args: |
| 97 | python: the name of the Python binary to start. |
| 98 | obj: object to pickle. |
| 99 | proto: pickle protocol number to use. |
| 100 | |
| 101 | Returns: |
| 102 | The pickled data received from the child process. |
| 103 | """ |
| 104 | # Prevent the subprocess from picking up invalid .pyc files. |
| 105 | target = __file__ |
| 106 | if target[-1] in ("c", "o"): |
| 107 | target = target[:-1] |
| 108 | |
| 109 | data = self.module.dumps((proto, obj), proto) |
| 110 | worker = subprocess.Popen([python, target, "worker"], |
| 111 | stdin=subprocess.PIPE, |
| 112 | stdout=subprocess.PIPE, |
| 113 | stderr=subprocess.PIPE) |
| 114 | stdout, stderr = worker.communicate(data) |
| 115 | if worker.returncode != 0: |
| 116 | raise RuntimeError(stderr) |
| 117 | return stdout |
| 118 | |
| 119 | def dumps(self, arg, proto=0, fast=False): |
| 120 | return self.send_to_worker(self.python, arg, proto) |
| 121 | |
| 122 | def loads(self, input): |
| 123 | return self.module.loads(input) |
| 124 | |
| 125 | # These tests are disabled because they require some special setup |
| 126 | # on the worker that's hard to keep in sync. |
Serhiy Storchaka | 0d8fe3b | 2015-01-27 22:44:45 +0200 | [diff] [blame] | 127 | test_global_ext1 = None |
| 128 | test_global_ext2 = None |
| 129 | test_global_ext4 = None |
Collin Winter | f8089c7 | 2009-04-09 16:46:46 +0000 | [diff] [blame] | 130 | |
| 131 | # This is a cut-down version of pickletester's test_float. Backwards |
| 132 | # compatibility for the values in for_bin_protos was explicitly broken in |
| 133 | # r68903 to fix a bug. |
| 134 | def test_float(self): |
| 135 | for_bin_protos = [4.94e-324, 1e-310] |
| 136 | neg_for_bin_protos = [-x for x in for_bin_protos] |
| 137 | test_values = [0.0, 7e-308, 6.626e-34, 0.1, 0.5, |
| 138 | 3.14, 263.44582062374053, 6.022e23, 1e30] |
| 139 | test_proto0_values = test_values + [-x for x in test_values] |
| 140 | test_values = test_proto0_values + for_bin_protos + neg_for_bin_protos |
| 141 | |
| 142 | for value in test_proto0_values: |
| 143 | pickle = self.dumps(value, 0) |
| 144 | got = self.loads(pickle) |
| 145 | self.assertEqual(value, got) |
| 146 | |
| 147 | for proto in pickletester.protocols[1:]: |
| 148 | for value in test_values: |
| 149 | pickle = self.dumps(value, proto) |
| 150 | got = self.loads(pickle) |
| 151 | self.assertEqual(value, got) |
| 152 | |
| 153 | # Backwards compatibility was explicitly broken in r67934 to fix a bug. |
Serhiy Storchaka | 0d8fe3b | 2015-01-27 22:44:45 +0200 | [diff] [blame] | 154 | test_unicode_high_plane = None |
Collin Winter | f8089c7 | 2009-04-09 16:46:46 +0000 | [diff] [blame] | 155 | |
Antoine Pitrou | 09debc9 | 2011-10-04 12:00:13 +0200 | [diff] [blame] | 156 | # This tests a fix that's in 2.7 only |
Serhiy Storchaka | 0d8fe3b | 2015-01-27 22:44:45 +0200 | [diff] [blame] | 157 | test_dynamic_class = None |
Antoine Pitrou | 09debc9 | 2011-10-04 12:00:13 +0200 | [diff] [blame] | 158 | |
Serhiy Storchaka | 0d8fe3b | 2015-01-27 22:44:45 +0200 | [diff] [blame] | 159 | # This is a cut-down version of pickletester's test_unicode. Backwards |
| 160 | # compatibility was explicitly broken in r67934 to fix a bug. |
Serhiy Storchaka | 0d8fe3b | 2015-01-27 22:44:45 +0200 | [diff] [blame] | 161 | def test_unicode(self): |
Serhiy Storchaka | d484605 | 2015-01-31 09:25:16 +0200 | [diff] [blame] | 162 | if not test_support.have_unicode: |
| 163 | # Python 2.5 has no unittest.skipUnless |
| 164 | self.skipTest('no unicode support') |
Serhiy Storchaka | 0d8fe3b | 2015-01-27 22:44:45 +0200 | [diff] [blame] | 165 | endcases = [u'', u'<\\u>', u'<\\%c>' % 0x1234, u'<\n>', u'<\\>'] |
| 166 | for proto in pickletester.protocols: |
| 167 | for u in endcases: |
| 168 | p = self.dumps(u, proto) |
| 169 | u2 = self.loads(p) |
| 170 | self.assertEqual(u2, u) |
Collin Winter | f8089c7 | 2009-04-09 16:46:46 +0000 | [diff] [blame] | 171 | |
| 172 | |
| 173 | # Test backwards compatibility with Python 2.4. |
Serhiy Storchaka | 0d8fe3b | 2015-01-27 22:44:45 +0200 | [diff] [blame] | 174 | class CPicklePython24Compat(AbstractCompatTests): |
Collin Winter | f8089c7 | 2009-04-09 16:46:46 +0000 | [diff] [blame] | 175 | |
Serhiy Storchaka | 0d8fe3b | 2015-01-27 22:44:45 +0200 | [diff] [blame] | 176 | module = cPickle |
| 177 | python = "python2.4" |
| 178 | error = cPickle.BadPickleGet |
Collin Winter | f8089c7 | 2009-04-09 16:46:46 +0000 | [diff] [blame] | 179 | |
Serhiy Storchaka | 0d8fe3b | 2015-01-27 22:44:45 +0200 | [diff] [blame] | 180 | # Disable these tests for Python 2.4. Making them pass would require |
| 181 | # nontrivially monkeypatching the pickletester module in the worker. |
| 182 | test_reduce_calls_base = None |
| 183 | test_reduce_ex_calls_base = None |
Collin Winter | f8089c7 | 2009-04-09 16:46:46 +0000 | [diff] [blame] | 184 | |
| 185 | class PicklePython24Compat(CPicklePython24Compat): |
| 186 | |
| 187 | module = pickle |
| 188 | error = KeyError |
| 189 | |
| 190 | |
| 191 | # Test backwards compatibility with Python 2.5. |
Serhiy Storchaka | 0d8fe3b | 2015-01-27 22:44:45 +0200 | [diff] [blame] | 192 | class CPicklePython25Compat(AbstractCompatTests): |
Collin Winter | f8089c7 | 2009-04-09 16:46:46 +0000 | [diff] [blame] | 193 | |
Serhiy Storchaka | 0d8fe3b | 2015-01-27 22:44:45 +0200 | [diff] [blame] | 194 | module = cPickle |
| 195 | python = "python2.5" |
| 196 | error = cPickle.BadPickleGet |
Collin Winter | f8089c7 | 2009-04-09 16:46:46 +0000 | [diff] [blame] | 197 | |
| 198 | class PicklePython25Compat(CPicklePython25Compat): |
| 199 | |
| 200 | module = pickle |
| 201 | error = KeyError |
| 202 | |
| 203 | |
| 204 | # Test backwards compatibility with Python 2.6. |
Serhiy Storchaka | 0d8fe3b | 2015-01-27 22:44:45 +0200 | [diff] [blame] | 205 | class CPicklePython26Compat(AbstractCompatTests): |
Collin Winter | f8089c7 | 2009-04-09 16:46:46 +0000 | [diff] [blame] | 206 | |
Serhiy Storchaka | 0d8fe3b | 2015-01-27 22:44:45 +0200 | [diff] [blame] | 207 | module = cPickle |
| 208 | python = "python2.6" |
| 209 | error = cPickle.BadPickleGet |
Collin Winter | f8089c7 | 2009-04-09 16:46:46 +0000 | [diff] [blame] | 210 | |
| 211 | class PicklePython26Compat(CPicklePython26Compat): |
| 212 | |
| 213 | module = pickle |
| 214 | error = KeyError |
| 215 | |
| 216 | |
Serhiy Storchaka | 0d8fe3b | 2015-01-27 22:44:45 +0200 | [diff] [blame] | 217 | class CPicklePython27Compat(AbstractCompatTests): |
| 218 | |
| 219 | module = cPickle |
| 220 | python = "python2.7" |
| 221 | error = cPickle.BadPickleGet |
| 222 | |
Serhiy Storchaka | d484605 | 2015-01-31 09:25:16 +0200 | [diff] [blame] | 223 | class PicklePython27Compat(CPicklePython27Compat): |
Serhiy Storchaka | 0d8fe3b | 2015-01-27 22:44:45 +0200 | [diff] [blame] | 224 | |
| 225 | module = pickle |
| 226 | error = KeyError |
| 227 | |
| 228 | |
Collin Winter | f8089c7 | 2009-04-09 16:46:46 +0000 | [diff] [blame] | 229 | def worker_main(in_stream, out_stream): |
| 230 | message = cPickle.load(in_stream) |
| 231 | protocol, obj = message |
| 232 | cPickle.dump(obj, out_stream, protocol) |
| 233 | |
| 234 | |
Tim Peters | c0c9370 | 2003-02-13 19:30:57 +0000 | [diff] [blame] | 235 | def test_main(): |
Walter Dörwald | 21d3a32 | 2003-05-01 17:45:56 +0000 | [diff] [blame] | 236 | test_support.run_unittest( |
| 237 | DumpCPickle_LoadPickle, |
Collin Winter | f8089c7 | 2009-04-09 16:46:46 +0000 | [diff] [blame] | 238 | DumpPickle_LoadCPickle, |
| 239 | CPicklePython24Compat, |
| 240 | CPicklePython25Compat, |
| 241 | CPicklePython26Compat, |
Serhiy Storchaka | 0d8fe3b | 2015-01-27 22:44:45 +0200 | [diff] [blame] | 242 | CPicklePython27Compat, |
Collin Winter | f8089c7 | 2009-04-09 16:46:46 +0000 | [diff] [blame] | 243 | PicklePython24Compat, |
| 244 | PicklePython25Compat, |
| 245 | PicklePython26Compat, |
Serhiy Storchaka | 0d8fe3b | 2015-01-27 22:44:45 +0200 | [diff] [blame] | 246 | PicklePython27Compat, |
Walter Dörwald | 21d3a32 | 2003-05-01 17:45:56 +0000 | [diff] [blame] | 247 | ) |
Tim Peters | c0c9370 | 2003-02-13 19:30:57 +0000 | [diff] [blame] | 248 | |
| 249 | if __name__ == "__main__": |
Collin Winter | f8089c7 | 2009-04-09 16:46:46 +0000 | [diff] [blame] | 250 | if "worker" in sys.argv: |
| 251 | worker_main(sys.stdin, sys.stdout) |
| 252 | else: |
| 253 | test_main() |