|
27 | 27 | from apache_beam.internal import module_test |
28 | 28 | from apache_beam.internal.cloudpickle_pickler import dumps |
29 | 29 | from apache_beam.internal.cloudpickle_pickler import loads |
| 30 | +from apache_beam.utils import shared |
| 31 | + |
| 32 | +GLOBAL_DICT_REF = module_test.GLOBAL_DICT |
| 33 | + |
| 34 | + |
| 35 | +# Allow weakref to dict |
| 36 | +class DictWrapper(dict): |
| 37 | + pass |
| 38 | + |
| 39 | + |
| 40 | +MAIN_MODULE_DICT = DictWrapper() |
| 41 | + |
| 42 | + |
| 43 | +def acquire_dict(): |
| 44 | + return DictWrapper() |
30 | 45 |
|
31 | 46 |
|
32 | 47 | class PicklerTest(unittest.TestCase): |
33 | 48 |
|
34 | 49 | NO_MAPPINGPROXYTYPE = not hasattr(types, "MappingProxyType") |
35 | 50 |
|
| 51 | + def test_globals_main_are_pickled_by_value(self): |
| 52 | + self.assertIsNot(MAIN_MODULE_DICT, loads(dumps(lambda: MAIN_MODULE_DICT))()) |
| 53 | + |
| 54 | + def test_globals_shared_are_pickled_by_reference(self): |
| 55 | + shared_handler = shared.Shared() |
| 56 | + original_dict = shared_handler.acquire(acquire_dict) |
| 57 | + |
| 58 | + unpickled_dict = loads( |
| 59 | + dumps(lambda: shared_handler.acquire(acquire_dict)))() |
| 60 | + |
| 61 | + self.assertIs(original_dict, unpickled_dict) |
| 62 | + |
| 63 | + def test_module_globals_are_pickled_by_value_when_directly_referenced(self): |
| 64 | + global_dict = loads(dumps(module_test.GLOBAL_DICT)) |
| 65 | + |
| 66 | + self.assertIsNot(module_test.GLOBAL_DICT, global_dict) |
| 67 | + |
| 68 | + def test_function_main_with_explicit_module_reference_pickles_by_reference( |
| 69 | + self): |
| 70 | + def returns_global_dict(): |
| 71 | + return module_test.GLOBAL_DICT |
| 72 | + |
| 73 | + self.assertIs(module_test.GLOBAL_DICT, loads(dumps(returns_global_dict))()) |
| 74 | + |
| 75 | + def test_function_main_with_indirect_module_reference_pickles_by_value(self): |
| 76 | + def returns_global_dict(): |
| 77 | + return GLOBAL_DICT_REF |
| 78 | + |
| 79 | + self.assertIsNot( |
| 80 | + module_test.GLOBAL_DICT, loads(dumps(returns_global_dict))()) |
| 81 | + |
| 82 | + def test_function_referencing_unpicklable_object_works_when_imported(self): |
| 83 | + self.assertEqual( |
| 84 | + module_test.UNPICKLABLE_INSTANCE, |
| 85 | + loads(dumps(module_test.fn_returns_unpicklable))()) |
| 86 | + |
| 87 | + def test_closure_with_unpicklable_object_fails_when_imported(self): |
| 88 | + # The entire closure is pickled by value, and therefore module_test is |
| 89 | + # not imported. Requires the module global to be pickled by value. |
| 90 | + with self.assertRaises(Exception): |
| 91 | + loads(dumps(module_test.closure_contains_unpicklable())) |
| 92 | + |
| 93 | + def test_closure_with_explicit_self_import_can_reference_unpicklable_objects( |
| 94 | + self): |
| 95 | + # The closure imports module_test within the function definition |
| 96 | + # and returns self.UNPICKLABLE_INSTANCE. This allows cloudpickle |
| 97 | + # to use submimort to reference module_test.UNPICKLABLE_INSTANCE |
| 98 | + self.assertIs( |
| 99 | + module_test.UNPICKLABLE_INSTANCE, |
| 100 | + loads(dumps(module_test.closure_contains_unpicklable_imports_self()))()) |
| 101 | + |
| 102 | + def test_closure_main_can_reference_unpicklable_module_objects(self): |
| 103 | + def outer(): |
| 104 | + def inner(): |
| 105 | + return module_test.UNPICKLABLE_INSTANCE |
| 106 | + |
| 107 | + return inner |
| 108 | + |
| 109 | + # Uses subimport to reference module_test.UNPICKLABLE_INSTANCE rather than |
| 110 | + # recreate. |
| 111 | + self.assertIs(module_test.UNPICKLABLE_INSTANCE, loads(dumps(outer()))()) |
| 112 | + |
36 | 113 | def test_pickle_nested_enum_descriptor(self): |
37 | 114 | NestedEnum = proto2_coder_test_messages_pb2.MessageD.NestedEnum |
38 | 115 |
|
|
0 commit comments