diff options
author | peng weikang <pengwk@pengwk.com> | 2021-04-14 02:38:17 +0800 |
---|---|---|
committer | Stefan Behnel <stefan_ml@behnel.de> | 2021-04-14 13:37:06 +0200 |
commit | eb6d31a5e3046d7d7d324a5861496609135cc2c1 (patch) | |
tree | aa8eccd727ac3af9eb4f157d3417d08dbc30ea3b | |
parent | 3ae58fea907a5abbc34f1792242a884c194ada5e (diff) | |
download | cython-eb6d31a5e3046d7d7d324a5861496609135cc2c1.tar.gz |
Remove incorrect dict unpacking optimisation that leaked external dict changes into the result (GH-4091)
Closes https://github.com/cython/cython/issues/3227
-rw-r--r-- | Cython/Compiler/ExprNodes.py | 11 | ||||
-rw-r--r-- | tests/run/dict.pyx | 19 | ||||
-rw-r--r-- | tests/run/kwargs_passthrough.pyx | 32 |
3 files changed, 42 insertions, 20 deletions
diff --git a/Cython/Compiler/ExprNodes.py b/Cython/Compiler/ExprNodes.py index 86bfa21c7..4b424d50e 100644 --- a/Cython/Compiler/ExprNodes.py +++ b/Cython/Compiler/ExprNodes.py @@ -6669,22 +6669,13 @@ class MergedDictNode(ExprNode): return dict_type def analyse_types(self, env): - args = [ + self.keyword_args = [ arg.analyse_types(env).coerce_to_pyobject(env).as_none_safe_node( # FIXME: CPython's error message starts with the runtime function name 'argument after ** must be a mapping, not NoneType') for arg in self.keyword_args ] - if len(args) == 1 and args[0].type is dict_type: - # strip this intermediate node and use the bare dict - arg = args[0] - if arg.is_name and arg.entry.is_arg and len(arg.entry.cf_assignments) == 1: - # passing **kwargs through to function call => allow NULL - arg.allow_null = True - return arg - - self.keyword_args = args return self def may_be_none(self): diff --git a/tests/run/dict.pyx b/tests/run/dict.pyx index d51cef7da..691b78635 100644 --- a/tests/run/dict.pyx +++ b/tests/run/dict.pyx @@ -117,3 +117,22 @@ def item_creation_sideeffect(L, sideeffect, unhashable): [2, 4, 5] """ return {1:2, sideeffect(2): 3, 3: 4, unhashable(4): 5, sideeffect(5): 6} + + +def dict_unpacking_not_for_arg_create_a_copy(): + """ + >>> dict_unpacking_not_for_arg_create_a_copy() + [('a', 'modified'), ('b', 'original')] + [('a', 'original'), ('b', 'original')] + """ + data = {'a': 'original', 'b': 'original'} + + func = lambda: {**data} + + call_once = func() + call_once['a'] = 'modified' + + call_twice = func() + + print(sorted(call_once.items())) + print(sorted(call_twice.items())) diff --git a/tests/run/kwargs_passthrough.pyx b/tests/run/kwargs_passthrough.pyx index 704638a55..576306efd 100644 --- a/tests/run/kwargs_passthrough.pyx +++ b/tests/run/kwargs_passthrough.pyx @@ -1,7 +1,6 @@ -cimport cython +import cython - -@cython.test_fail_if_path_exists('//MergedDictNode') +#@cython.test_fail_if_path_exists('//MergedDictNode') def wrap_passthrough(f): """ >>> def f(a=1): return a @@ -80,7 +79,7 @@ def wrap_passthrough_more(f): return wrapper -@cython.test_fail_if_path_exists('//MergedDictNode') +#@cython.test_fail_if_path_exists('//MergedDictNode') def wrap_passthrough2(f): """ >>> def f(a=1): return a @@ -99,7 +98,7 @@ def wrap_passthrough2(f): return wrapper -@cython.test_fail_if_path_exists('//MergedDictNode') +#@cython.test_fail_if_path_exists('//MergedDictNode') def wrap_modify(f): """ >>> def f(a=1, test=2): @@ -123,7 +122,7 @@ def wrap_modify(f): return wrapper -@cython.test_fail_if_path_exists('//MergedDictNode') +#@cython.test_fail_if_path_exists('//MergedDictNode') def wrap_modify_mix(f): """ >>> def f(a=1, test=2): @@ -175,7 +174,21 @@ def wrap_modify_func(f): return wrapper -@cython.test_assert_path_exists('//MergedDictNode') +def modify_in_function(): + """ + >>> modify_in_function() + {'foo': 'bar'} + {'foo': 'bar'} + """ + def inner(**kwds): + kwds['foo'] = 'modified' + d = {'foo': 'bar'} + print(d) + inner(**d) + print(d) + + +#@cython.test_assert_path_exists('//MergedDictNode') def wrap_modify_func_mix(f): """ >>> def f(a=1, test=2): @@ -203,12 +216,11 @@ def wrap_modify_func_mix(f): return wrapper -@cython.test_fail_if_path_exists('//MergedDictNode') +#@cython.test_fail_if_path_exists('//MergedDictNode') def wrap_reassign(f): """ >>> def f(a=1, test=2): ... return a, test - >>> wrapped = wrap_reassign(f) >>> wrapped(1) CALLED @@ -227,7 +239,7 @@ def wrap_reassign(f): return wrapper -@cython.test_fail_if_path_exists('//MergedDictNode') +#@cython.test_fail_if_path_exists('//MergedDictNode') def kwargs_metaclass(**kwargs): """ >>> K = kwargs_metaclass() |