|
10 | 10 | import logging |
11 | 11 | import math |
12 | 12 | from operator import itemgetter, attrgetter |
| 13 | +import pickletools |
13 | 14 | import platform |
14 | 15 | import random |
15 | 16 | import shutil |
|
50 | 51 | from cloudpickle.cloudpickle import _lookup_module_and_qualname |
51 | 52 |
|
52 | 53 | from .testutils import subprocess_pickle_echo |
| 54 | +from .testutils import subprocess_pickle_string |
53 | 55 | from .testutils import assert_run_python_script |
54 | 56 | from .testutils import subprocess_worker |
55 | 57 |
|
56 | 58 | from _cloudpickle_testpkg import relative_imports_factory |
57 | 59 |
|
58 | 60 |
|
59 | 61 | _TEST_GLOBAL_VARIABLE = "default_value" |
| 62 | +_TEST_GLOBAL_VARIABLE2 = "another_value" |
60 | 63 |
|
61 | 64 |
|
62 | 65 | class RaiserOnPickle(object): |
@@ -622,6 +625,12 @@ def __reduce__(self): |
622 | 625 | assert hasattr(depickled_mod.__builtins__, "abs") |
623 | 626 | assert depickled_mod.f(-1) == 1 |
624 | 627 |
|
| 628 | + # Additional check testing that the issue #425 is fixed: without the |
| 629 | + # fix for #425, `mod.f` would not have access to `__builtins__`, and |
| 630 | + # thus calling `mod.f(-1)` (which relies on the `abs` builtin) would |
| 631 | + # fail. |
| 632 | + assert mod.f(-1) == 1 |
| 633 | + |
625 | 634 | def test_load_dynamic_module_in_grandchild_process(self): |
626 | 635 | # Make sure that when loaded, a dynamic module preserves its dynamic |
627 | 636 | # property. Otherwise, this will lead to an ImportError if pickled in |
@@ -2107,8 +2116,8 @@ def inner_function(): |
2107 | 2116 | return _TEST_GLOBAL_VARIABLE |
2108 | 2117 | return inner_function |
2109 | 2118 |
|
2110 | | - globals_ = cloudpickle.cloudpickle._extract_code_globals( |
2111 | | - function_factory.__code__) |
| 2119 | + globals_ = set(cloudpickle.cloudpickle._extract_code_globals( |
| 2120 | + function_factory.__code__).keys()) |
2112 | 2121 | assert globals_ == {'_TEST_GLOBAL_VARIABLE'} |
2113 | 2122 |
|
2114 | 2123 | depickled_factory = pickle_depickle(function_factory, |
@@ -2225,10 +2234,13 @@ def method(self, arg: type_) -> type_: |
2225 | 2234 |
|
2226 | 2235 | def check_annotations(obj, expected_type, expected_type_str): |
2227 | 2236 | assert obj.__annotations__["attribute"] == expected_type |
2228 | | - if sys.version_info >= (3, 10): |
2229 | | - # In Python 3.10, type annotations are stored as strings. |
| 2237 | + if sys.version_info >= (3, 11): |
| 2238 | + # In Python 3.11, type annotations are stored as strings. |
2230 | 2239 | # See PEP 563 for more details: |
2231 | 2240 | # https://www.python.org/dev/peps/pep-0563/ |
| 2241 | + # Originaly scheduled for 3.10, then postponed. |
| 2242 | + # See this for more details: |
| 2243 | + # https://mail.python.org/archives/list/[email protected]/thread/CLVXXPQ2T2LQ5MP2Y53VVQFCXYWQJHKZ/ |
2232 | 2244 | assert ( |
2233 | 2245 | obj.method.__annotations__["arg"] |
2234 | 2246 | == expected_type_str |
@@ -2339,6 +2351,32 @@ def __type__(self): |
2339 | 2351 | o = MyClass() |
2340 | 2352 | pickle_depickle(o, protocol=self.protocol) |
2341 | 2353 |
|
| 2354 | + @pytest.mark.skipif( |
| 2355 | + sys.version_info < (3, 7), |
| 2356 | + reason="Determinism can only be guaranteed for Python 3.7+" |
| 2357 | + ) |
| 2358 | + def test_deterministic_pickle_bytes_for_function(self): |
| 2359 | + # Ensure that functions with references to several global names are |
| 2360 | + # pickled to fixed bytes that do not depend on the PYTHONHASHSEED of |
| 2361 | + # the Python process. |
| 2362 | + vals = set() |
| 2363 | + |
| 2364 | + def func_with_globals(): |
| 2365 | + return _TEST_GLOBAL_VARIABLE + _TEST_GLOBAL_VARIABLE2 |
| 2366 | + |
| 2367 | + for i in range(5): |
| 2368 | + vals.add( |
| 2369 | + subprocess_pickle_string(func_with_globals, |
| 2370 | + protocol=self.protocol, |
| 2371 | + add_env={"PYTHONHASHSEED": str(i)})) |
| 2372 | + if len(vals) > 1: |
| 2373 | + # Print additional debug info on stdout with dis: |
| 2374 | + for val in vals: |
| 2375 | + pickletools.dis(val) |
| 2376 | + pytest.fail( |
| 2377 | + "Expected a single deterministic payload, got %d/5" % len(vals) |
| 2378 | + ) |
| 2379 | + |
2342 | 2380 |
|
2343 | 2381 | class Protocol2CloudPickleTest(CloudPickleTest): |
2344 | 2382 |
|
|
0 commit comments