Commit 0919f3e1 authored by Stefan Behnel's avatar Stefan Behnel

When passing keyword arguments from unpacked dict literals, prevent duplicate...

When passing keyword arguments from unpacked dict literals, prevent duplicate dict keys from raising an error and instead overwrite the previous one.
Closes GH-2963.
parent 1cae8183
...@@ -66,6 +66,11 @@ Bugs fixed ...@@ -66,6 +66,11 @@ Bugs fixed
the first character if multiple characters should have been returned. the first character if multiple characters should have been returned.
They now use the original Python methods again. They now use the original Python methods again.
* Nested dict literals in function call kwargs could incorrectly raise an
error about duplicate keyword arguments, which are allowed when passing
them from dict literals.
(Github issue #2963)
* Item access (subscripting) with integer indices/keys always tried the * Item access (subscripting) with integer indices/keys always tried the
Sequence protocol before the Mapping protocol, which diverged from Python Sequence protocol before the Mapping protocol, which diverged from Python
semantics. It now passes through the Mapping protocol first when supported. semantics. It now passes through the Mapping protocol first when supported.
......
...@@ -4422,25 +4422,25 @@ class ConstantFolding(Visitor.VisitorTransform, SkipDeclarations): ...@@ -4422,25 +4422,25 @@ class ConstantFolding(Visitor.VisitorTransform, SkipDeclarations):
args = [] args = []
items = [] items = []
def add(arg): def add(parent, arg):
if arg.is_dict_literal: if arg.is_dict_literal:
if items: if items and items[-1].reject_duplicates == arg.reject_duplicates:
items[0].key_value_pairs.extend(arg.key_value_pairs) items[-1].key_value_pairs.extend(arg.key_value_pairs)
else: else:
items.append(arg) items.append(arg)
elif isinstance(arg, ExprNodes.MergedDictNode): elif isinstance(arg, ExprNodes.MergedDictNode) and parent.reject_duplicates == arg.reject_duplicates:
for child_arg in arg.keyword_args: for child_arg in arg.keyword_args:
add(child_arg) add(arg, child_arg)
else: else:
if items: if items:
args.append(items[0]) args.extend(items)
del items[:] del items[:]
args.append(arg) args.append(arg)
for arg in node.keyword_args: for arg in node.keyword_args:
add(arg) add(node, arg)
if items: if items:
args.append(items[0]) args.extend(items)
if len(args) == 1: if len(args) == 1:
arg = args[0] arg = args[0]
......
...@@ -464,6 +464,10 @@ def unpack_dict_simple(it): ...@@ -464,6 +464,10 @@ def unpack_dict_simple(it):
return {**it} return {**it}
@cython.test_assert_path_exists('//MergedDictNode')
@cython.test_fail_if_path_exists(
'//MergedDictNode//MergedDictNode',
)
def unpack_dict_from_iterable(it): def unpack_dict_from_iterable(it):
""" """
>>> d = unpack_dict_from_iterable(dict(a=1, b=2, c=3)) >>> d = unpack_dict_from_iterable(dict(a=1, b=2, c=3))
...@@ -536,3 +540,28 @@ def unpack_dict_keep_originals(a, b, c): ...@@ -536,3 +540,28 @@ def unpack_dict_keep_originals(a, b, c):
True True
""" """
return {**a, **b, 2: 4, **c} return {**a, **b, 2: 4, **c}
@cython.test_assert_path_exists(
'//MergedDictNode',
'//MergedDictNode//MergedDictNode',
'//MergedDictNode//MergedDictNode//DictNode',
)
def unpack_in_call(f):
"""
>>> def f(a=1, test=2, **kwargs):
... return a, test, sorted(kwargs.items())
>>> wrapped = unpack_in_call(f)
>>> wrapped(1)
(1, 1, [('more', 2)])
>>> wrapped(test='overwritten')
(1, 1, [('more', 2)])
>>> wrapped(b=3)
(1, 1, [('b', 3), ('more', 2)])
>>> wrapped(more=4)
Traceback (most recent call last):
TypeError: function() got multiple values for keyword argument 'more'
"""
def wrapper(*args, **kwargs):
return f(*args, more=2, **{**kwargs, 'test': 1})
return wrapper
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