Skip to content
Snippets Groups Projects
Commit b604b267 authored by Bryce Guinta's avatar Bryce Guinta
Browse files

Fix duplicate values in inferred dictionary node from dictionary unpacking

parent 9b5aa979
No related branches found
No related tags found
No related merge requests found
Loading
Loading
@@ -50,6 +50,12 @@ Change log for the astroid package (used to be astng)
 
Close #98
 
* Stop duplicate nodes with the same key values
from appearing in dictionaries from dictionary
unpacking
Close PyCQA/pylint#1843
 
2017-12-15 -- 1.6.0
 
Loading
Loading
Loading
Loading
@@ -89,6 +89,29 @@ def infer_map(self, context=None):
yield new_seq
 
 
def _update_with_replacement(lhs_dict, rhs_dict):
"""Delete nodes that equate to duplicate keys
Since an astroid node doesn't 'equal' another node with the same value,
this function uses the as_string method to make sure duplicate keys
don't get through
Note that both the key and the value are astroid nodes
Fixes issue with DictUnpack causing duplicte keys
in inferred Dict items
:param dict(nodes.NodeNG, nodes.NodeNG) lhs_dict: Dictionary to 'merge' nodes into
:param dict(nodes.NodeNG, nodes.NodeNG) rhs_dict: Dictionary with nodes to pull from
:return dict(nodes.NodeNG, nodes.NodeNG): merged dictionary of nodes
"""
combined_dict = itertools.chain(lhs_dict.items(), rhs_dict.items())
# Overwrite keys which have the same string values
string_map = {key.as_string(): (key, value) for key, value in combined_dict}
# Return to dictionary
return dict(string_map.values())
def _infer_map(node, context):
"""Infer all values based on Dict.items"""
values = {}
Loading
Loading
@@ -100,14 +123,15 @@ def _infer_map(node, context):
if not isinstance(double_starred, nodes.Dict):
raise exceptions.InferenceError(node=node,
context=context)
values.update(_infer_map(double_starred, context))
unpack_items = _infer_map(double_starred, context)
values = _update_with_replacement(values, unpack_items)
else:
key = helpers.safe_infer(name, context=context)
value = helpers.safe_infer(value, context=context)
if any(elem in (None, util.Uninferable) for elem in (key, value)):
raise exceptions.InferenceError(node=node,
context=context)
values[key] = value
values = _update_with_replacement(values, {key: value})
return values
 
 
Loading
Loading
File deleted
Loading
Loading
@@ -1925,6 +1925,30 @@ class InferenceTest(resources.SysPathSetup, unittest.TestCase):
node = extract_node(code)
self.assertInferDict(node, expected_value)
 
@test_utils.require_version('3.5')
def test_dict_inference_unpack_repeated_key(self):
"""Make sure astroid does not infer repeated keys in a dictionary
Regression test for https://github.com/PyCQA/pylint/issues/1843
"""
code = """
base = {'data': 0}
new = {**base, 'data': 1} #@
new2 = {'data': 1, **base} #@ # Make sure overwrite works
a = 'd' + 'ata'
b3 = {**base, a: 3} #@ Make sure keys are properly inferred
b4 = {a: 3, **base} #@
"""
ast = extract_node(code)
final_values = (
"{'data': 1}",
"{'data': 0}",
"{'data': 3}",
"{'data': 0}",
)
for node, final_value in zip(ast, final_values):
assert node.targets[0].inferred()[0].as_string() == final_value
def test_dict_invalid_args(self):
invalid_values = [
'dict(*1)',
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment