Skip to content

Commit 22fe050

Browse files
committed
Refactor the dict.get detection code to be easier to understand
Also add support for astroid.Index unpacking, which is gone in Python 3.9.
1 parent 8adc420 commit 22fe050

File tree

1 file changed

+41
-18
lines changed

1 file changed

+41
-18
lines changed

pylint/checkers/refactoring.py

Lines changed: 41 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -538,35 +538,58 @@ def _check_superfluous_else_continue(self, node):
538538
node, msg_id="no-else-continue", returning_node_class=astroid.Continue
539539
)
540540

541-
def _check_consider_get(self, node):
542-
def type_and_name_are_equal(node_a, node_b):
543-
for _type in [astroid.Name, astroid.AssignName]:
544-
if all(isinstance(_node, _type) for _node in [node_a, node_b]):
545-
return node_a.name == node_b.name
546-
if all(isinstance(_node, astroid.Const) for _node in [node_a, node_b]):
547-
return node_a.value == node_b.value
541+
@staticmethod
542+
def _type_and_name_are_equal(node_a, node_b):
543+
for _type in [astroid.Name, astroid.AssignName]:
544+
if all(isinstance(_node, _type) for _node in [node_a, node_b]):
545+
return node_a.name == node_b.name
546+
if all(isinstance(_node, astroid.Const) for _node in [node_a, node_b]):
547+
return node_a.value == node_b.value
548+
return False
549+
550+
def _is_dict_get_block(self, node):
551+
552+
# "if <compare node>"
553+
if not isinstance(node.test, astroid.Compare):
554+
return False
555+
556+
# Does not have a single statement in the guard's body
557+
if len(node.body) != 1:
548558
return False
549559

550-
if_block_ok = (
551-
isinstance(node.test, astroid.Compare)
552-
and len(node.body) == 1
553-
and isinstance(node.body[0], astroid.Assign)
554-
and isinstance(node.body[0].value, astroid.Subscript)
555-
and type_and_name_are_equal(node.body[0].value.value, node.test.ops[0][1])
556-
and isinstance(node.body[0].value.slice, astroid.Index)
557-
and type_and_name_are_equal(node.body[0].value.slice.value, node.test.left)
560+
# Look for a single variable assignment on the LHS and a subscript on RHS
561+
stmt = node.body[0]
562+
if not (
563+
isinstance(stmt, astroid.Assign)
558564
and len(node.body[0].targets) == 1
559565
and isinstance(node.body[0].targets[0], astroid.AssignName)
560-
and isinstance(utils.safe_infer(node.test.ops[0][1]), astroid.Dict)
561-
)
566+
and isinstance(stmt.value, astroid.Subscript)
567+
):
568+
return False
562569

570+
# The subscript's slice needs to be the same as the test variable.
571+
# Python 3.9 we no longer have the `Index` node.
572+
slice_value = stmt.value.slice
573+
if isinstance(slice_value, astroid.Index):
574+
slice_value = slice_value.value
575+
if not (
576+
self._type_and_name_are_equal(stmt.value.value, node.test.ops[0][1])
577+
and self._type_and_name_are_equal(slice_value, node.test.left)
578+
):
579+
return False
580+
581+
# The object needs to be a dictionary instance
582+
return isinstance(utils.safe_infer(node.test.ops[0][1]), astroid.Dict)
583+
584+
def _check_consider_get(self, node):
585+
if_block_ok = self._is_dict_get_block(node)
563586
if if_block_ok and not node.orelse:
564587
self.add_message("consider-using-get", node=node)
565588
elif (
566589
if_block_ok
567590
and len(node.orelse) == 1
568591
and isinstance(node.orelse[0], astroid.Assign)
569-
and type_and_name_are_equal(
592+
and self._type_and_name_are_equal(
570593
node.orelse[0].targets[0], node.body[0].targets[0]
571594
)
572595
and len(node.orelse[0].targets) == 1

0 commit comments

Comments
 (0)