|
1 | 1 | from __future__ import division |
2 | 2 |
|
| 3 | +import abc |
| 4 | + |
3 | 5 | import base64 |
4 | 6 | import functools |
5 | 7 | import imp |
|
14 | 16 | import sys |
15 | 17 | import textwrap |
16 | 18 | import unittest |
| 19 | +import weakref |
17 | 20 |
|
18 | 21 | try: |
19 | 22 | from StringIO import StringIO |
|
43 | 46 | from .testutils import subprocess_pickle_echo |
44 | 47 |
|
45 | 48 |
|
| 49 | +HAVE_WEAKSET = sys.version_info >= (2, 7) |
| 50 | + |
| 51 | + |
46 | 52 | def pickle_depickle(obj): |
47 | 53 | """Helper function to test whether object pickled with cloudpickle can be |
48 | 54 | depickled with pickle |
@@ -592,6 +598,62 @@ def test_logger(self): |
592 | 598 | self.assertEqual(out.strip().decode(), |
593 | 599 | 'INFO:cloudpickle.dummy_test_logger:hello') |
594 | 600 |
|
| 601 | + def test_abc(self): |
| 602 | + |
| 603 | + @abc.abstractmethod |
| 604 | + def foo(self): |
| 605 | + raise NotImplementedError('foo') |
| 606 | + |
| 607 | + # Invoke the metaclass directly rather than using class syntax for |
| 608 | + # python 2/3 compat. |
| 609 | + AbstractClass = abc.ABCMeta('AbstractClass', (object,), {'foo': foo}) |
| 610 | + |
| 611 | + class ConcreteClass(AbstractClass): |
| 612 | + def foo(self): |
| 613 | + return 'it works!' |
| 614 | + |
| 615 | + depickled_base = pickle_depickle(AbstractClass) |
| 616 | + depickled_class = pickle_depickle(ConcreteClass) |
| 617 | + depickled_instance = pickle_depickle(ConcreteClass()) |
| 618 | + |
| 619 | + self.assertEqual(depickled_class().foo(), 'it works!') |
| 620 | + self.assertEqual(depickled_instance.foo(), 'it works!') |
| 621 | + |
| 622 | + # It should still be invalid to construct an instance of the abstract |
| 623 | + # class without implementing its methods. |
| 624 | + with self.assertRaises(TypeError): |
| 625 | + depickled_base() |
| 626 | + |
| 627 | + class DepickledBaseSubclass(depickled_base): |
| 628 | + def foo(self): |
| 629 | + return 'it works for realz!' |
| 630 | + |
| 631 | + self.assertEqual(DepickledBaseSubclass().foo(), 'it works for realz!') |
| 632 | + |
| 633 | + @pytest.mark.skipif(not HAVE_WEAKSET, reason="WeakSet doesn't exist") |
| 634 | + def test_weakset_identity_preservation(self): |
| 635 | + # Test that weaksets don't lose all their inhabitants if they're |
| 636 | + # pickled in a larger data structure that includes other references to |
| 637 | + # their inhabitants. |
| 638 | + |
| 639 | + class SomeClass(object): |
| 640 | + def __init__(self, x): |
| 641 | + self.x = x |
| 642 | + |
| 643 | + obj1, obj2, obj3 = SomeClass(1), SomeClass(2), SomeClass(3) |
| 644 | + |
| 645 | + things = [weakref.WeakSet([obj1, obj2]), obj1, obj2, obj3] |
| 646 | + result = pickle_depickle(things) |
| 647 | + |
| 648 | + weakset, depickled1, depickled2, depickled3 = result |
| 649 | + |
| 650 | + self.assertEqual(depickled1.x, 1) |
| 651 | + self.assertEqual(depickled2.x, 2) |
| 652 | + self.assertEqual(depickled3.x, 3) |
| 653 | + self.assertEqual(len(weakset), 2) |
| 654 | + |
| 655 | + self.assertEqual(set(weakset), set([depickled1, depickled2])) |
| 656 | + |
595 | 657 |
|
596 | 658 | if __name__ == '__main__': |
597 | 659 | unittest.main() |
0 commit comments