Commit eb6d31a5 authored by peng weikang's avatar peng weikang Committed by Stefan Behnel

Remove incorrect dict unpacking optimisation that leaked external dict changes...

Remove incorrect dict unpacking optimisation that leaked external dict changes into the result (GH-4091)

Closes https://github.com/cython/cython/issues/3227
parent 3ae58fea
...@@ -6669,22 +6669,13 @@ class MergedDictNode(ExprNode): ...@@ -6669,22 +6669,13 @@ class MergedDictNode(ExprNode):
return dict_type return dict_type
def analyse_types(self, env): def analyse_types(self, env):
args = [ self.keyword_args = [
arg.analyse_types(env).coerce_to_pyobject(env).as_none_safe_node( arg.analyse_types(env).coerce_to_pyobject(env).as_none_safe_node(
# FIXME: CPython's error message starts with the runtime function name # FIXME: CPython's error message starts with the runtime function name
'argument after ** must be a mapping, not NoneType') 'argument after ** must be a mapping, not NoneType')
for arg in self.keyword_args 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 return self
def may_be_none(self): def may_be_none(self):
......
...@@ -117,3 +117,22 @@ def item_creation_sideeffect(L, sideeffect, unhashable): ...@@ -117,3 +117,22 @@ def item_creation_sideeffect(L, sideeffect, unhashable):
[2, 4, 5] [2, 4, 5]
""" """
return {1:2, sideeffect(2): 3, 3: 4, unhashable(4): 5, sideeffect(5): 6} 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()))
cimport cython import cython
#@cython.test_fail_if_path_exists('//MergedDictNode')
@cython.test_fail_if_path_exists('//MergedDictNode')
def wrap_passthrough(f): def wrap_passthrough(f):
""" """
>>> def f(a=1): return a >>> def f(a=1): return a
...@@ -80,7 +79,7 @@ def wrap_passthrough_more(f): ...@@ -80,7 +79,7 @@ def wrap_passthrough_more(f):
return wrapper return wrapper
@cython.test_fail_if_path_exists('//MergedDictNode') #@cython.test_fail_if_path_exists('//MergedDictNode')
def wrap_passthrough2(f): def wrap_passthrough2(f):
""" """
>>> def f(a=1): return a >>> def f(a=1): return a
...@@ -99,7 +98,7 @@ def wrap_passthrough2(f): ...@@ -99,7 +98,7 @@ def wrap_passthrough2(f):
return wrapper return wrapper
@cython.test_fail_if_path_exists('//MergedDictNode') #@cython.test_fail_if_path_exists('//MergedDictNode')
def wrap_modify(f): def wrap_modify(f):
""" """
>>> def f(a=1, test=2): >>> def f(a=1, test=2):
...@@ -123,7 +122,7 @@ def wrap_modify(f): ...@@ -123,7 +122,7 @@ def wrap_modify(f):
return wrapper return wrapper
@cython.test_fail_if_path_exists('//MergedDictNode') #@cython.test_fail_if_path_exists('//MergedDictNode')
def wrap_modify_mix(f): def wrap_modify_mix(f):
""" """
>>> def f(a=1, test=2): >>> def f(a=1, test=2):
...@@ -175,7 +174,21 @@ def wrap_modify_func(f): ...@@ -175,7 +174,21 @@ def wrap_modify_func(f):
return wrapper 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 wrap_modify_func_mix(f):
""" """
>>> def f(a=1, test=2): >>> def f(a=1, test=2):
...@@ -203,12 +216,11 @@ def wrap_modify_func_mix(f): ...@@ -203,12 +216,11 @@ def wrap_modify_func_mix(f):
return wrapper return wrapper
@cython.test_fail_if_path_exists('//MergedDictNode') #@cython.test_fail_if_path_exists('//MergedDictNode')
def wrap_reassign(f): def wrap_reassign(f):
""" """
>>> def f(a=1, test=2): >>> def f(a=1, test=2):
... return a, test ... return a, test
>>> wrapped = wrap_reassign(f) >>> wrapped = wrap_reassign(f)
>>> wrapped(1) >>> wrapped(1)
CALLED CALLED
...@@ -227,7 +239,7 @@ def wrap_reassign(f): ...@@ -227,7 +239,7 @@ def wrap_reassign(f):
return wrapper return wrapper
@cython.test_fail_if_path_exists('//MergedDictNode') #@cython.test_fail_if_path_exists('//MergedDictNode')
def kwargs_metaclass(**kwargs): def kwargs_metaclass(**kwargs):
""" """
>>> K = kwargs_metaclass() >>> K = kwargs_metaclass()
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment