Skip to content

Commit 914091d

Browse files
committed
Add tests.
1 parent 56f1aa1 commit 914091d

File tree

2 files changed

+103
-0
lines changed

2 files changed

+103
-0
lines changed

sdks/python/apache_beam/internal/cloudpickle_pickler_test.py

+71
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,83 @@
2727
from apache_beam.internal import module_test
2828
from apache_beam.internal.cloudpickle_pickler import dumps
2929
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()
3045

3146

3247
class PicklerTest(unittest.TestCase):
3348

3449
NO_MAPPINGPROXYTYPE = not hasattr(types, "MappingProxyType")
3550

51+
def test_module_global_pickled_by_value(self):
52+
self.assertIsNot(
53+
module_test.GLOBAL_DICT, loads(dumps(module_test.GLOBAL_DICT)))
54+
55+
def test_main_shared_global_pickled_by_ref(self):
56+
shared_handler = shared.Shared()
57+
original_dict = shared_handler.acquire(acquire_dict)
58+
59+
unpickled_dict = loads(
60+
dumps(lambda: shared_handler.acquire(acquire_dict)))()
61+
62+
self.assertIs(original_dict, unpickled_dict)
63+
64+
def test_function_defined_in_main_pickles_module_global_by_ref(self):
65+
def returns_global_dict():
66+
return module_test.GLOBAL_DICT
67+
68+
self.assertIs(module_test.GLOBAL_DICT, loads(dumps(returns_global_dict))())
69+
70+
def test_function_defined_in_main_pickles_module_global_ref_by_value(self):
71+
def returns_global_dict():
72+
return GLOBAL_DICT_REF
73+
74+
self.assertIsNot(
75+
module_test.GLOBAL_DICT, loads(dumps(returns_global_dict))())
76+
77+
def test_fn_contains_unpicklable_by_ref(self):
78+
self.assertEqual(
79+
module_test.UNPICLABLE_INSTANCE,
80+
loads(dumps(module_test.fn_returns_unpicklable))())
81+
82+
def test_imported_closure_contains_unpicklable_by_value_fails(self):
83+
# The entire closure is pickled by value, and therefore module_test is
84+
# not imported. Requires the module global to be pickled by value.
85+
with self.assertRaises(Exception):
86+
loads(dumps(module_test.closure_contains_unpicklable()))
87+
88+
def test_imported_closure_contains_unpicklable_imports_self(self):
89+
# The closure imports module_test within the function definition
90+
# and returns self.UNPICLABLE_INSTANCE. This allows cloudpickle
91+
# to use submimort to reference module_test.UNPICLABLE_INSTANCE
92+
self.assertIs(
93+
module_test.UNPICLABLE_INSTANCE,
94+
loads(dumps(module_test.closure_contains_unpicklable_imports_self()))())
95+
96+
def test_main_closure_contains_imported_unpicklable_by_ref(self):
97+
def outer():
98+
def inner():
99+
return module_test.UNPICLABLE_INSTANCE
100+
101+
return inner
102+
103+
# Uses subimport to reference module_test.UNPICLABLE_INSTANCE rather than
104+
# recreate.
105+
loads(dumps(outer()))
106+
36107
def test_pickle_nested_enum_descriptor(self):
37108
NestedEnum = proto2_coder_test_messages_pb2.MessageD.NestedEnum
38109

sdks/python/apache_beam/internal/module_test.py

+32
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,38 @@
2323
import sys
2424
from typing import Any
2525

26+
GLOBAL_DICT = {}
27+
28+
29+
class UnPicklable:
30+
def __init__(self, x):
31+
self.x = x
32+
33+
def __reduce__(self):
34+
return NotImplemented
35+
36+
37+
UNPICLABLE_INSTANCE = UnPicklable(5)
38+
39+
40+
def closure_contains_unpicklable():
41+
def inner():
42+
return UNPICLABLE_INSTANCE
43+
44+
return inner
45+
46+
47+
def closure_contains_unpicklable_imports_self():
48+
def inner():
49+
import apache_beam.internal.module_test as self_module
50+
return self_module.UNPICLABLE_INSTANCE
51+
52+
return inner
53+
54+
55+
def fn_returns_unpicklable():
56+
return UNPICLABLE_INSTANCE
57+
2658

2759
class TopClass(object):
2860
class NestedClass(object):

0 commit comments

Comments
 (0)