/Lib/test/test_xpickle.py

http://unladen-swallow.googlecode.com/ · Python · 227 lines · 135 code · 48 blank · 44 comment · 18 complexity · 11db8ebdf894adb747f94238eea5baff MD5 · raw file

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