PageRenderTime 274ms CodeModel.GetById 100ms app.highlight 16ms RepoModel.GetById 155ms app.codeStats 1ms

/Lib/test/test_xpickle.py

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