Skip to content

Commit

Permalink
Extended consider-using-tuple check to cover 'in' comparisons (pylint…
Browse files Browse the repository at this point in the history
  • Loading branch information
cdce8p committed Jul 29, 2021
1 parent 68a22eb commit ca3bc53
Show file tree
Hide file tree
Showing 12 changed files with 75 additions and 22 deletions.
4 changes: 4 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ Release date: TBA

Closes #3878

* ``CodeStyleChecker``

* Extended ``consider-using-tuple`` check to cover ``in`` comparisons.


What's New in Pylint 2.9.6?
===========================
Expand Down
8 changes: 8 additions & 0 deletions doc/whatsnew/2.10.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ New checkers
Closes #3826


Extensions
==========

* ``CodeStyleChecker``

* Extended ``consider-using-tuple`` check to cover ``in`` comparisons.


Other Changes
=============

Expand Down
2 changes: 1 addition & 1 deletion pylint/checkers/classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -981,7 +981,7 @@ def _check_unused_private_attributes(self, node: astroid.ClassDef) -> None:
(
# If assigned to cls.attrib, can be accessed by cls/self
assign_attr.expr.name == "cls"
and attribute.expr.name in ["cls", "self"]
and attribute.expr.name in ("cls", "self")
)
# If assigned to self.attrib, can only be accessed by self
# Or if __new__ was used, the returned object names are acceptable
Expand Down
2 changes: 1 addition & 1 deletion pylint/checkers/python3.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ def _in_iterating_context(node):
elif (
isinstance(parent, astroid.Compare)
and len(parent.ops) == 1
and parent.ops[0][0] in ["in", "not in"]
and parent.ops[0][0] in ("in", "not in")
):
return True
# Also if it's an `yield from`, that's fair
Expand Down
2 changes: 1 addition & 1 deletion pylint/checkers/refactoring/refactoring_checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -943,7 +943,7 @@ def _check_consider_using_generator(self, node):
# remove square brackets '[]'
inside_comp = node.args[0].as_string()[1:-1]
call_name = node.func.name
if call_name in ["any", "all"]:
if call_name in ("any", "all"):
self.add_message(
"use-a-generator",
node=node,
Expand Down
2 changes: 1 addition & 1 deletion pylint/checkers/stdlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -558,7 +558,7 @@ def _check_redundant_assert(self, node, infer):
isinstance(infer, astroid.BoundMethod)
and node.args
and isinstance(node.args[0], astroid.Const)
and infer.name in ["assertTrue", "assertFalse"]
and infer.name in ("assertTrue", "assertFalse")
):
self.add_message(
"redundant-unittest-assert",
Expand Down
4 changes: 2 additions & 2 deletions pylint/checkers/typecheck.py
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@ def _emit_no_member(node, owner, owner_name, ignored_mixins=True, ignored_none=T
and isinstance(owner.parent, astroid.ClassDef)
and owner.parent.name == "EnumMeta"
and owner_name == "__members__"
and node.attrname in ["items", "values", "keys"]
and node.attrname in ("items", "values", "keys")
):
# Avoid false positive on Enum.__members__.{items(), values, keys}
# See https://github.com/PyCQA/pylint/issues/4123
Expand Down Expand Up @@ -1781,7 +1781,7 @@ def visit_compare(self, node):
return

op, right = node.ops[0]
if op in ["in", "not in"]:
if op in ("in", "not in"):
self._check_membership_test(right)

@check_messages(
Expand Down
25 changes: 15 additions & 10 deletions pylint/extensions/code_style.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import List, Set, Tuple, Type, Union
from typing import List, Set, Tuple, Type

import astroid
from astroid.node_classes import NodeNG
Expand Down Expand Up @@ -53,11 +53,18 @@ def visit_dict(self, node: astroid.Dict) -> None:

@check_messages("consider-using-tuple")
def visit_for(self, node: astroid.For) -> None:
self._check_inplace_defined_list_set(node)
self._check_inplace_defined_list_set(node.iter)

@check_messages("consider-using-tuple")
def visit_comprehension(self, node: astroid.Comprehension) -> None:
self._check_inplace_defined_list_set(node)
self._check_inplace_defined_list_set(node.iter)

@check_messages("consider-using-tuple")
def visit_compare(self, node: astroid.Compare) -> None:
for op, comparator in node.ops:
if op != "in":
continue
self._check_inplace_defined_list_set(comparator)

def _check_dict_consider_namedtuple_dataclass(self, node: astroid.Dict) -> None:
"""Check if dictionary values can be replaced by Namedtuple or Dataclass."""
Expand Down Expand Up @@ -128,17 +135,15 @@ def _check_dict_consider_namedtuple_dataclass(self, node: astroid.Dict) -> None:
self.add_message("consider-using-namedtuple-or-dataclass", node=node)
return

def _check_inplace_defined_list_set(
self, node: Union[astroid.For, astroid.Comprehension]
) -> None:
def _check_inplace_defined_list_set(self, node: NodeNG) -> None:
"""Check if inplace defined list / set can be replaced by a tuple."""
if isinstance(node.iter, (astroid.List, astroid.Set)) and not any(
isinstance(item, astroid.Starred) for item in node.iter.elts
if isinstance(node, (astroid.List, astroid.Set)) and not any(
isinstance(item, astroid.Starred) for item in node.elts
):
self.add_message(
"consider-using-tuple",
node=node.iter,
args=(f" instead of {node.iter.__class__.__qualname__.lower()}"),
node=node,
args=(f" instead of {node.__class__.__qualname__.lower()}",),
)


Expand Down
2 changes: 1 addition & 1 deletion pylint/lint/pylinter.py
Original file line number Diff line number Diff line change
Expand Up @@ -717,7 +717,7 @@ def any_fail_on_issues(self):
def disable_noerror_messages(self):
for msgcat, msgids in self.msgs_store._msgs_by_category.items():
# enable only messages with 'error' severity and above ('fatal')
if msgcat in ["E", "F"]:
if msgcat in ("E", "F"):
for msgid in msgids:
self.enable(msgid)
else:
Expand Down
2 changes: 1 addition & 1 deletion script/bump_changelog.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ def do_checks(content, next_version, version, version_type):
wn_next_version = get_whats_new(next_version)
wn_this_version = get_whats_new(version)
# There is only one field where the release date is TBA
if version_type in [VersionType.MAJOR, VersionType.MINOR]:
if version_type in (VersionType.MAJOR, VersionType.MINOR):
assert (
content.count(RELEASE_DATE_TEXT) <= 1
), f"There should be only one release date 'TBA' ({version}) {err}"
Expand Down
27 changes: 27 additions & 0 deletions tests/functional/ext/code_style/code_style_consider_using_tuple.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,30 @@

[x for x in [*var, 2]]
[x for x in {*var, 2}]


# -----
# Suggest tuple for `in` comparisons
x in var
x in (1, 2, 3)
x in [1, 2, 3] # [consider-using-tuple]

if x in var:
pass
if x in (1, 2, 3):
pass
if x in [1, 2, 3]: # [consider-using-tuple]
pass
if x in {1, 2, 3}: # [consider-using-tuple]
pass

42 if x in [1, 2, 3] else None # [consider-using-tuple]
assert x in [1, 2, 3] # [consider-using-tuple]
(x for x in var if x in [1, 2, 3]) # [consider-using-tuple]
while x in [1, 2, 3]: # [consider-using-tuple]
break

# Stacked operators, rightmost pair is evaluated first
# Doesn't make much sense in practice since `in` will only return `bool`
True == x in [1, 2, 3] # [consider-using-tuple] # noqa: E712
1 >= x in [1, 2, 3] # [consider-using-tuple] # noqa: E712
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
consider-using-tuple:9:9::Consider using an in-place tuple instead of list
consider-using-tuple:14:12::Consider using an in-place tuple instead of list
consider-using-tuple:15:12::Consider using an in-place tuple instead of set
consider-using-tuple:19:12::Consider using an in-place tuple instead of list
consider-using-tuple:9:9::Consider using an in-place tuple instead of list:HIGH
consider-using-tuple:14:12::Consider using an in-place tuple instead of list:HIGH
consider-using-tuple:15:12::Consider using an in-place tuple instead of set:HIGH
consider-using-tuple:19:12::Consider using an in-place tuple instead of list:HIGH
consider-using-tuple:36:5::Consider using an in-place tuple instead of list:HIGH
consider-using-tuple:42:8::Consider using an in-place tuple instead of list:HIGH
consider-using-tuple:44:8::Consider using an in-place tuple instead of set:HIGH
consider-using-tuple:47:11::Consider using an in-place tuple instead of list:HIGH
consider-using-tuple:48:12::Consider using an in-place tuple instead of list:HIGH
consider-using-tuple:49:24::Consider using an in-place tuple instead of list:HIGH
consider-using-tuple:50:11::Consider using an in-place tuple instead of list:HIGH
consider-using-tuple:55:13::Consider using an in-place tuple instead of list:HIGH
consider-using-tuple:56:10::Consider using an in-place tuple instead of list:HIGH

0 comments on commit ca3bc53

Please sign in to comment.