Skip to content

Commit ca3bc53

Browse files
authored
Extended consider-using-tuple check to cover 'in' comparisons (#4768)
1 parent 68a22eb commit ca3bc53

File tree

12 files changed

+75
-22
lines changed

12 files changed

+75
-22
lines changed

ChangeLog

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ Release date: TBA
3636

3737
Closes #3878
3838

39+
* ``CodeStyleChecker``
40+
41+
* Extended ``consider-using-tuple`` check to cover ``in`` comparisons.
42+
3943

4044
What's New in Pylint 2.9.6?
4145
===========================

doc/whatsnew/2.10.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,14 @@ New checkers
1717
Closes #3826
1818

1919

20+
Extensions
21+
==========
22+
23+
* ``CodeStyleChecker``
24+
25+
* Extended ``consider-using-tuple`` check to cover ``in`` comparisons.
26+
27+
2028
Other Changes
2129
=============
2230

pylint/checkers/classes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -981,7 +981,7 @@ def _check_unused_private_attributes(self, node: astroid.ClassDef) -> None:
981981
(
982982
# If assigned to cls.attrib, can be accessed by cls/self
983983
assign_attr.expr.name == "cls"
984-
and attribute.expr.name in ["cls", "self"]
984+
and attribute.expr.name in ("cls", "self")
985985
)
986986
# If assigned to self.attrib, can only be accessed by self
987987
# Or if __new__ was used, the returned object names are acceptable

pylint/checkers/python3.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ def _in_iterating_context(node):
156156
elif (
157157
isinstance(parent, astroid.Compare)
158158
and len(parent.ops) == 1
159-
and parent.ops[0][0] in ["in", "not in"]
159+
and parent.ops[0][0] in ("in", "not in")
160160
):
161161
return True
162162
# Also if it's an `yield from`, that's fair

pylint/checkers/refactoring/refactoring_checker.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -943,7 +943,7 @@ def _check_consider_using_generator(self, node):
943943
# remove square brackets '[]'
944944
inside_comp = node.args[0].as_string()[1:-1]
945945
call_name = node.func.name
946-
if call_name in ["any", "all"]:
946+
if call_name in ("any", "all"):
947947
self.add_message(
948948
"use-a-generator",
949949
node=node,

pylint/checkers/stdlib.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -558,7 +558,7 @@ def _check_redundant_assert(self, node, infer):
558558
isinstance(infer, astroid.BoundMethod)
559559
and node.args
560560
and isinstance(node.args[0], astroid.Const)
561-
and infer.name in ["assertTrue", "assertFalse"]
561+
and infer.name in ("assertTrue", "assertFalse")
562562
):
563563
self.add_message(
564564
"redundant-unittest-assert",

pylint/checkers/typecheck.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,7 @@ def _emit_no_member(node, owner, owner_name, ignored_mixins=True, ignored_none=T
518518
and isinstance(owner.parent, astroid.ClassDef)
519519
and owner.parent.name == "EnumMeta"
520520
and owner_name == "__members__"
521-
and node.attrname in ["items", "values", "keys"]
521+
and node.attrname in ("items", "values", "keys")
522522
):
523523
# Avoid false positive on Enum.__members__.{items(), values, keys}
524524
# See https://github.com/PyCQA/pylint/issues/4123
@@ -1781,7 +1781,7 @@ def visit_compare(self, node):
17811781
return
17821782

17831783
op, right = node.ops[0]
1784-
if op in ["in", "not in"]:
1784+
if op in ("in", "not in"):
17851785
self._check_membership_test(right)
17861786

17871787
@check_messages(

pylint/extensions/code_style.py

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import List, Set, Tuple, Type, Union
1+
from typing import List, Set, Tuple, Type
22

33
import astroid
44
from astroid.node_classes import NodeNG
@@ -53,11 +53,18 @@ def visit_dict(self, node: astroid.Dict) -> None:
5353

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

5858
@check_messages("consider-using-tuple")
5959
def visit_comprehension(self, node: astroid.Comprehension) -> None:
60-
self._check_inplace_defined_list_set(node)
60+
self._check_inplace_defined_list_set(node.iter)
61+
62+
@check_messages("consider-using-tuple")
63+
def visit_compare(self, node: astroid.Compare) -> None:
64+
for op, comparator in node.ops:
65+
if op != "in":
66+
continue
67+
self._check_inplace_defined_list_set(comparator)
6168

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

131-
def _check_inplace_defined_list_set(
132-
self, node: Union[astroid.For, astroid.Comprehension]
133-
) -> None:
138+
def _check_inplace_defined_list_set(self, node: NodeNG) -> None:
134139
"""Check if inplace defined list / set can be replaced by a tuple."""
135-
if isinstance(node.iter, (astroid.List, astroid.Set)) and not any(
136-
isinstance(item, astroid.Starred) for item in node.iter.elts
140+
if isinstance(node, (astroid.List, astroid.Set)) and not any(
141+
isinstance(item, astroid.Starred) for item in node.elts
137142
):
138143
self.add_message(
139144
"consider-using-tuple",
140-
node=node.iter,
141-
args=(f" instead of {node.iter.__class__.__qualname__.lower()}"),
145+
node=node,
146+
args=(f" instead of {node.__class__.__qualname__.lower()}",),
142147
)
143148

144149

pylint/lint/pylinter.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -717,7 +717,7 @@ def any_fail_on_issues(self):
717717
def disable_noerror_messages(self):
718718
for msgcat, msgids in self.msgs_store._msgs_by_category.items():
719719
# enable only messages with 'error' severity and above ('fatal')
720-
if msgcat in ["E", "F"]:
720+
if msgcat in ("E", "F"):
721721
for msgid in msgids:
722722
self.enable(msgid)
723723
else:

script/bump_changelog.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ def do_checks(content, next_version, version, version_type):
136136
wn_next_version = get_whats_new(next_version)
137137
wn_this_version = get_whats_new(version)
138138
# There is only one field where the release date is TBA
139-
if version_type in [VersionType.MAJOR, VersionType.MINOR]:
139+
if version_type in (VersionType.MAJOR, VersionType.MINOR):
140140
assert (
141141
content.count(RELEASE_DATE_TEXT) <= 1
142142
), f"There should be only one release date 'TBA' ({version}) {err}"

tests/functional/ext/code_style/code_style_consider_using_tuple.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,30 @@
2727

2828
[x for x in [*var, 2]]
2929
[x for x in {*var, 2}]
30+
31+
32+
# -----
33+
# Suggest tuple for `in` comparisons
34+
x in var
35+
x in (1, 2, 3)
36+
x in [1, 2, 3] # [consider-using-tuple]
37+
38+
if x in var:
39+
pass
40+
if x in (1, 2, 3):
41+
pass
42+
if x in [1, 2, 3]: # [consider-using-tuple]
43+
pass
44+
if x in {1, 2, 3}: # [consider-using-tuple]
45+
pass
46+
47+
42 if x in [1, 2, 3] else None # [consider-using-tuple]
48+
assert x in [1, 2, 3] # [consider-using-tuple]
49+
(x for x in var if x in [1, 2, 3]) # [consider-using-tuple]
50+
while x in [1, 2, 3]: # [consider-using-tuple]
51+
break
52+
53+
# Stacked operators, rightmost pair is evaluated first
54+
# Doesn't make much sense in practice since `in` will only return `bool`
55+
True == x in [1, 2, 3] # [consider-using-tuple] # noqa: E712
56+
1 >= x in [1, 2, 3] # [consider-using-tuple] # noqa: E712
Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
1-
consider-using-tuple:9:9::Consider using an in-place tuple instead of list
2-
consider-using-tuple:14:12::Consider using an in-place tuple instead of list
3-
consider-using-tuple:15:12::Consider using an in-place tuple instead of set
4-
consider-using-tuple:19:12::Consider using an in-place tuple instead of list
1+
consider-using-tuple:9:9::Consider using an in-place tuple instead of list:HIGH
2+
consider-using-tuple:14:12::Consider using an in-place tuple instead of list:HIGH
3+
consider-using-tuple:15:12::Consider using an in-place tuple instead of set:HIGH
4+
consider-using-tuple:19:12::Consider using an in-place tuple instead of list:HIGH
5+
consider-using-tuple:36:5::Consider using an in-place tuple instead of list:HIGH
6+
consider-using-tuple:42:8::Consider using an in-place tuple instead of list:HIGH
7+
consider-using-tuple:44:8::Consider using an in-place tuple instead of set:HIGH
8+
consider-using-tuple:47:11::Consider using an in-place tuple instead of list:HIGH
9+
consider-using-tuple:48:12::Consider using an in-place tuple instead of list:HIGH
10+
consider-using-tuple:49:24::Consider using an in-place tuple instead of list:HIGH
11+
consider-using-tuple:50:11::Consider using an in-place tuple instead of list:HIGH
12+
consider-using-tuple:55:13::Consider using an in-place tuple instead of list:HIGH
13+
consider-using-tuple:56:10::Consider using an in-place tuple instead of list:HIGH

0 commit comments

Comments
 (0)