test_xpickle.py 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  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
  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.
  7. import cPickle
  8. import os
  9. import os.path
  10. import pickle
  11. import subprocess
  12. import sys
  13. import types
  14. import unittest
  15. from test import test_support
  16. # Most distro-supplied Pythons don't include the tests
  17. # or test support files, and some don't include a way to get these back even if
  18. # you're will to install extra packages (like Ubuntu). Doing things like this
  19. # "provides" a pickletester module for older versions of Python that may be
  20. # installed without it. Note that one other design for this involves messing
  21. # with sys.path, which is less precise.
  22. mod_path = os.path.abspath(os.path.join(os.path.dirname(__file__),
  23. "pickletester.py"))
  24. pickletester = types.ModuleType("test.pickletester")
  25. exec compile(open(mod_path).read(), mod_path, 'exec') in pickletester.__dict__
  26. AbstractPickleTests = pickletester.AbstractPickleTests
  27. if pickletester.__name__ in sys.modules:
  28. raise RuntimeError("Did not expect to find test.pickletester loaded")
  29. sys.modules[pickletester.__name__] = pickletester
  30. class DumpCPickle_LoadPickle(AbstractPickleTests):
  31. error = KeyError
  32. def dumps(self, arg, proto=0, fast=False):
  33. # Ignore fast
  34. return cPickle.dumps(arg, proto)
  35. def loads(self, buf):
  36. # Ignore fast
  37. return pickle.loads(buf)
  38. class DumpPickle_LoadCPickle(AbstractPickleTests):
  39. error = cPickle.BadPickleGet
  40. def dumps(self, arg, proto=0, fast=False):
  41. # Ignore fast
  42. return pickle.dumps(arg, proto)
  43. def loads(self, buf):
  44. # Ignore fast
  45. return cPickle.loads(buf)
  46. def have_python_version(name, cache={}):
  47. """Check whether the given name is a valid Python binary and has
  48. test.test_support.
  49. This respects your PATH.
  50. Args:
  51. name: short string name of a Python binary such as "python2.4".
  52. Returns:
  53. True if the name is valid, False otherwise.
  54. """
  55. if name not in cache:
  56. cache[name] = os.system(name + ' -c "import test.test_support"') == 0
  57. return cache[name]
  58. class AbstractCompatTests(AbstractPickleTests):
  59. module = None
  60. python = None
  61. error = None
  62. def setUp(self):
  63. self.assertTrue(self.python)
  64. self.assertTrue(self.module)
  65. self.assertTrue(self.error)
  66. test_support.requires("xpickle")
  67. if not have_python_version(self.python):
  68. self.skipTest('%s not available' % self.python)
  69. def send_to_worker(self, python, obj, proto):
  70. """Bounce a pickled object through another version of Python.
  71. This will pickle the object, send it to a child process where it will be
  72. unpickled, then repickled and sent back to the parent process.
  73. Args:
  74. python: the name of the Python binary to start.
  75. obj: object to pickle.
  76. proto: pickle protocol number to use.
  77. Returns:
  78. The pickled data received from the child process.
  79. """
  80. # Prevent the subprocess from picking up invalid .pyc files.
  81. target = __file__
  82. if target[-1] in ("c", "o"):
  83. target = target[:-1]
  84. data = self.module.dumps((proto, obj), proto)
  85. worker = subprocess.Popen([python, target, "worker"],
  86. stdin=subprocess.PIPE,
  87. stdout=subprocess.PIPE,
  88. stderr=subprocess.PIPE)
  89. stdout, stderr = worker.communicate(data)
  90. if worker.returncode != 0:
  91. raise RuntimeError(stderr)
  92. return stdout
  93. def dumps(self, arg, proto=0, fast=False):
  94. return self.send_to_worker(self.python, arg, proto)
  95. def loads(self, input):
  96. return self.module.loads(input)
  97. # These tests are disabled because they require some special setup
  98. # on the worker that's hard to keep in sync.
  99. test_global_ext1 = None
  100. test_global_ext2 = None
  101. test_global_ext4 = None
  102. # This is a cut-down version of pickletester's test_float. Backwards
  103. # compatibility for the values in for_bin_protos was explicitly broken in
  104. # r68903 to fix a bug.
  105. def test_float(self):
  106. for_bin_protos = [4.94e-324, 1e-310]
  107. neg_for_bin_protos = [-x for x in for_bin_protos]
  108. test_values = [0.0, 7e-308, 6.626e-34, 0.1, 0.5,
  109. 3.14, 263.44582062374053, 6.022e23, 1e30]
  110. test_proto0_values = test_values + [-x for x in test_values]
  111. test_values = test_proto0_values + for_bin_protos + neg_for_bin_protos
  112. for value in test_proto0_values:
  113. pickle = self.dumps(value, 0)
  114. got = self.loads(pickle)
  115. self.assertEqual(value, got)
  116. for proto in pickletester.protocols[1:]:
  117. for value in test_values:
  118. pickle = self.dumps(value, proto)
  119. got = self.loads(pickle)
  120. self.assertEqual(value, got)
  121. # Backwards compatibility was explicitly broken in r67934 to fix a bug.
  122. test_unicode_high_plane = None
  123. # This tests a fix that's in 2.7 only
  124. test_dynamic_class = None
  125. # This is a cut-down version of pickletester's test_unicode. Backwards
  126. # compatibility was explicitly broken in r67934 to fix a bug.
  127. def test_unicode(self):
  128. if not test_support.have_unicode:
  129. # Python 2.5 has no unittest.skipUnless
  130. self.skipTest('no unicode support')
  131. endcases = [u'', u'<\\u>', u'<\\%c>' % 0x1234, u'<\n>', u'<\\>']
  132. for proto in pickletester.protocols:
  133. for u in endcases:
  134. p = self.dumps(u, proto)
  135. u2 = self.loads(p)
  136. self.assertEqual(u2, u)
  137. # The ability to pickle recursive objects was added in 2.7.11 to fix
  138. # a crash in CPickle (issue #892902).
  139. test_recursive_list_subclass_and_inst = None
  140. test_recursive_tuple_subclass_and_inst = None
  141. test_recursive_dict_subclass_and_inst = None
  142. test_recursive_set_and_inst = None
  143. test_recursive_frozenset_and_inst = None
  144. # Test backwards compatibility with Python 2.4.
  145. class CPicklePython24Compat(AbstractCompatTests):
  146. module = cPickle
  147. python = "python2.4"
  148. error = cPickle.BadPickleGet
  149. # Disable these tests for Python 2.4. Making them pass would require
  150. # nontrivially monkeypatching the pickletester module in the worker.
  151. test_reduce_calls_base = None
  152. test_reduce_ex_calls_base = None
  153. class PicklePython24Compat(CPicklePython24Compat):
  154. module = pickle
  155. error = KeyError
  156. # Test backwards compatibility with Python 2.5.
  157. class CPicklePython25Compat(AbstractCompatTests):
  158. module = cPickle
  159. python = "python2.5"
  160. error = cPickle.BadPickleGet
  161. class PicklePython25Compat(CPicklePython25Compat):
  162. module = pickle
  163. error = KeyError
  164. # Test backwards compatibility with Python 2.6.
  165. class CPicklePython26Compat(AbstractCompatTests):
  166. module = cPickle
  167. python = "python2.6"
  168. error = cPickle.BadPickleGet
  169. class PicklePython26Compat(CPicklePython26Compat):
  170. module = pickle
  171. error = KeyError
  172. class CPicklePython27Compat(AbstractCompatTests):
  173. module = cPickle
  174. python = "python2.7"
  175. error = cPickle.BadPickleGet
  176. class PicklePython27Compat(CPicklePython27Compat):
  177. module = pickle
  178. error = KeyError
  179. def worker_main(in_stream, out_stream):
  180. message = cPickle.load(in_stream)
  181. protocol, obj = message
  182. cPickle.dump(obj, out_stream, protocol)
  183. def test_main():
  184. test_support.run_unittest(
  185. DumpCPickle_LoadPickle,
  186. DumpPickle_LoadCPickle,
  187. CPicklePython24Compat,
  188. CPicklePython25Compat,
  189. CPicklePython26Compat,
  190. CPicklePython27Compat,
  191. PicklePython24Compat,
  192. PicklePython25Compat,
  193. PicklePython26Compat,
  194. PicklePython27Compat,
  195. )
  196. if __name__ == "__main__":
  197. if "worker" in sys.argv:
  198. worker_main(sys.stdin, sys.stdout)
  199. else:
  200. test_main()