Skip to content

Commit 507dfc5

Browse files
Apply infer_kwarg_from_call() to more checks (#8775)
These mostly solve false negatives for various checks, save for one false positive for `use-maxsplit-arg`.
1 parent bafb229 commit 507dfc5

10 files changed

+68
-28
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Apply ``infer_kwarg_from_call()`` to more checks
2+
3+
These mostly solve false negatives for various checks,
4+
save for one false positive for ``use-maxsplit-arg``.
5+
6+
Closes #7761

pylint/checkers/refactoring/recommendation_checker.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,17 +127,22 @@ def _check_use_maxsplit_arg(self, node: nodes.Call) -> None:
127127
):
128128
return
129129

130+
confidence = HIGH
130131
try:
131132
sep = utils.get_argument_from_call(node, 0, "sep")
132133
except utils.NoSuchArgumentError:
133-
return
134+
sep = utils.infer_kwarg_from_call(node, keyword="sep")
135+
confidence = INFERENCE
136+
if not sep:
137+
return
134138

135139
try:
136140
# Ignore if maxsplit arg has been set
137141
utils.get_argument_from_call(node, 1, "maxsplit")
138142
return
139143
except utils.NoSuchArgumentError:
140-
pass
144+
if utils.infer_kwarg_from_call(node, keyword="maxsplit"):
145+
return
141146

142147
if isinstance(node.parent, nodes.Subscript):
143148
try:
@@ -171,7 +176,12 @@ def _check_use_maxsplit_arg(self, node: nodes.Call) -> None:
171176
+ new_fn
172177
+ f"({sep.as_string()}, maxsplit=1)[{subscript_value}]"
173178
)
174-
self.add_message("use-maxsplit-arg", node=node, args=(new_name,))
179+
self.add_message(
180+
"use-maxsplit-arg",
181+
node=node,
182+
args=(new_name,),
183+
confidence=confidence,
184+
)
175185

176186
@utils.only_required_for_messages(
177187
"consider-using-enumerate",

pylint/checkers/refactoring/refactoring_checker.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2203,12 +2203,14 @@ def _check_unnecessary_list_index_lookup(
22032203
):
22042204
return
22052205

2206+
preliminary_confidence = HIGH
22062207
try:
22072208
iterable_arg = utils.get_argument_from_call(
22082209
node.iter, position=0, keyword="iterable"
22092210
)
22102211
except utils.NoSuchArgumentError:
2211-
return
2212+
iterable_arg = utils.infer_kwarg_from_call(node.iter, keyword="iterable")
2213+
preliminary_confidence = INFERENCE
22122214

22132215
if not isinstance(iterable_arg, nodes.Name):
22142216
return
@@ -2228,6 +2230,13 @@ def _check_unnecessary_list_index_lookup(
22282230
# is not redundant, hence we should not report an error.
22292231
return
22302232

2233+
# Preserve preliminary_confidence if it was INFERENCE
2234+
confidence = (
2235+
preliminary_confidence
2236+
if preliminary_confidence == INFERENCE
2237+
else confidence
2238+
)
2239+
22312240
iterating_object_name = iterable_arg.name
22322241
value_variable = node.target.elts[1]
22332242

pylint/checkers/stdlib.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -619,7 +619,7 @@ def _check_lru_cache_decorators(self, node: nodes.FunctionDef) -> None:
619619
d_node, position=0, keyword="maxsize"
620620
)
621621
except utils.NoSuchArgumentError:
622-
break
622+
arg = utils.infer_kwarg_from_call(d_node, "maxsize")
623623

624624
if not isinstance(arg, nodes.Const) or arg.value is not None:
625625
break

tests/functional/m/method_cache_max_size_none.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ def my_func(self, param):
4545
def my_func(self, param):
4646
return param + 1
4747

48+
maxsizeKwarg = {"maxsize": None}
49+
@lru_cache(**maxsizeKwarg) # [method-cache-max-size-none]
50+
def my_func(self, param):
51+
return param + 1
52+
4853

4954
class MyClassWithMethodsAndMaxSize:
5055
@lru_cache(maxsize=1)

tests/functional/m/method_cache_max_size_none.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ method-cache-max-size-none:34:5:34:38:MyClassWithMethods.my_func:'lru_cache(maxs
44
method-cache-max-size-none:38:5:38:24:MyClassWithMethods.my_func:'lru_cache(maxsize=None)' or 'cache' will keep all method args alive indefinitely, including 'self':INFERENCE
55
method-cache-max-size-none:43:5:43:24:MyClassWithMethods.my_func:'lru_cache(maxsize=None)' or 'cache' will keep all method args alive indefinitely, including 'self':INFERENCE
66
method-cache-max-size-none:44:5:44:24:MyClassWithMethods.my_func:'lru_cache(maxsize=None)' or 'cache' will keep all method args alive indefinitely, including 'self':INFERENCE
7-
method-cache-max-size-none:74:5:74:40:MyClassWithMethodsAndMaxSize.my_func:'lru_cache(maxsize=None)' or 'cache' will keep all method args alive indefinitely, including 'self':INFERENCE
7+
method-cache-max-size-none:49:5:49:30:MyClassWithMethods.my_func:'lru_cache(maxsize=None)' or 'cache' will keep all method args alive indefinitely, including 'self':INFERENCE
8+
method-cache-max-size-none:79:5:79:40:MyClassWithMethodsAndMaxSize.my_func:'lru_cache(maxsize=None)' or 'cache' will keep all method args alive indefinitely, including 'self':INFERENCE

tests/functional/u/unnecessary/unnecessary_list_index_lookup.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,9 @@ def return_start(start):
136136

137137
result = [my_list[idx] for idx, val in enumerate(iterable=my_list)] # [unnecessary-list-index-lookup]
138138

139+
iterable_kwarg = {"iterable": my_list}
140+
result = [my_list[idx] for idx, val in enumerate(**iterable_kwarg)] # [unnecessary-list-index-lookup]
141+
139142
for idx, val in enumerate():
140143
print(my_list[idx])
141144

tests/functional/u/unnecessary/unnecessary_list_index_lookup.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ unnecessary-list-index-lookup:119:10:119:21::Unnecessary list index lookup, use
88
unnecessary-list-index-lookup:122:10:122:21::Unnecessary list index lookup, use 'val' instead:INFERENCE
99
unnecessary-list-index-lookup:135:10:135:21::Unnecessary list index lookup, use 'val' instead:HIGH
1010
unnecessary-list-index-lookup:137:10:137:22::Unnecessary list index lookup, use 'val' instead:HIGH
11+
unnecessary-list-index-lookup:140:10:140:22::Unnecessary list index lookup, use 'val' instead:INFERENCE

tests/functional/u/use/use_maxsplit_arg.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,14 @@
1717

1818

1919
# Test varying maxsplit argument -- all these will be okay
20+
max_split = {"maxsplit": 2}
2021
# ## str.split() tests
2122
good_split = '1,2,3'.split(sep=',', maxsplit=1)[-1]
2223
good_split = '1,2,3'.split(sep=',', maxsplit=1)[0]
2324
good_split = '1,2,3'.split(sep=',', maxsplit=2)[-1]
2425
good_split = '1,2,3'.split(sep=',', maxsplit=2)[0]
2526
good_split = '1,2,3'.split(sep=',', maxsplit=2)[1]
27+
good_split = '1,2,3'.split(sep=',', **max_split)[1]
2628

2729
# ## str.rsplit() tests
2830
good_split = '1,2,3'.rsplit(sep=',', maxsplit=1)[-1]
@@ -91,9 +93,11 @@ class Bar():
9193
print(source.split('.')[i])
9294
i = i + 1
9395

96+
sepNone = {"sep": None}
9497
# Test for crash when sep is given by keyword
9598
# https://github.com/pylint-dev/pylint/issues/5737
9699
get_last = SEQ.split(sep=None)[-1] # [use-maxsplit-arg]
100+
get_last = SEQ.split(**sepNone)[-1] # [use-maxsplit-arg]
97101

98102

99103
class FalsePositive4857:
Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,23 @@
1-
use-maxsplit-arg:5:12:5:30::Use '1,2,3'.split(',', maxsplit=1)[0] instead:UNDEFINED
2-
use-maxsplit-arg:6:11:6:35::"Use '1,2,3'[::-1].split(',', maxsplit=1)[0] instead":UNDEFINED
3-
use-maxsplit-arg:9:12:9:26::Use SEQ.split(',', maxsplit=1)[0] instead:UNDEFINED
4-
use-maxsplit-arg:10:11:10:25::Use SEQ.rsplit(',', maxsplit=1)[-1] instead:UNDEFINED
5-
use-maxsplit-arg:11:12:11:27::Use SEQ.split(',', maxsplit=1)[0] instead:UNDEFINED
6-
use-maxsplit-arg:12:11:12:26::Use SEQ.rsplit(',', maxsplit=1)[-1] instead:UNDEFINED
7-
use-maxsplit-arg:45:12:45:36::Use Foo.class_str.split(',', maxsplit=1)[0] instead:UNDEFINED
8-
use-maxsplit-arg:46:11:46:35::Use Foo.class_str.rsplit(',', maxsplit=1)[-1] instead:UNDEFINED
9-
use-maxsplit-arg:47:12:47:37::Use Foo.class_str.split(',', maxsplit=1)[0] instead:UNDEFINED
10-
use-maxsplit-arg:48:11:48:36::Use Foo.class_str.rsplit(',', maxsplit=1)[-1] instead:UNDEFINED
11-
use-maxsplit-arg:56:12:56:40::Use test.get_string().split(',', maxsplit=1)[0] instead:UNDEFINED
12-
use-maxsplit-arg:57:11:57:39::Use test.get_string().rsplit(',', maxsplit=1)[-1] instead:UNDEFINED
13-
use-maxsplit-arg:66:10:66:22::Use s.split(' ', maxsplit=1)[0] instead:UNDEFINED
14-
use-maxsplit-arg:67:10:67:22::Use s.rsplit(' ', maxsplit=1)[-1] instead:UNDEFINED
15-
use-maxsplit-arg:76:6:76:26::Use Bar.split.split(',', maxsplit=1)[0] instead:UNDEFINED
16-
use-maxsplit-arg:77:6:77:26::Use Bar.split.rsplit(',', maxsplit=1)[-1] instead:UNDEFINED
17-
use-maxsplit-arg:78:6:78:27::Use Bar.split.split(',', maxsplit=1)[0] instead:UNDEFINED
18-
use-maxsplit-arg:79:6:79:27::Use Bar.split.rsplit(',', maxsplit=1)[-1] instead:UNDEFINED
19-
use-maxsplit-arg:82:4:82:23::Use '1,2,3'.split('\n', maxsplit=1)[0] instead:UNDEFINED
20-
use-maxsplit-arg:83:4:83:26::Use '1,2,3'.rsplit('split', maxsplit=1)[-1] instead:UNDEFINED
21-
use-maxsplit-arg:84:4:84:28::Use '1,2,3'.split('rsplit', maxsplit=1)[0] instead:UNDEFINED
22-
use-maxsplit-arg:96:11:96:30::Use SEQ.rsplit(None, maxsplit=1)[-1] instead:UNDEFINED
1+
use-maxsplit-arg:5:12:5:30::Use '1,2,3'.split(',', maxsplit=1)[0] instead:HIGH
2+
use-maxsplit-arg:6:11:6:35::"Use '1,2,3'[::-1].split(',', maxsplit=1)[0] instead":HIGH
3+
use-maxsplit-arg:9:12:9:26::Use SEQ.split(',', maxsplit=1)[0] instead:HIGH
4+
use-maxsplit-arg:10:11:10:25::Use SEQ.rsplit(',', maxsplit=1)[-1] instead:HIGH
5+
use-maxsplit-arg:11:12:11:27::Use SEQ.split(',', maxsplit=1)[0] instead:HIGH
6+
use-maxsplit-arg:12:11:12:26::Use SEQ.rsplit(',', maxsplit=1)[-1] instead:HIGH
7+
use-maxsplit-arg:47:12:47:36::Use Foo.class_str.split(',', maxsplit=1)[0] instead:HIGH
8+
use-maxsplit-arg:48:11:48:35::Use Foo.class_str.rsplit(',', maxsplit=1)[-1] instead:HIGH
9+
use-maxsplit-arg:49:12:49:37::Use Foo.class_str.split(',', maxsplit=1)[0] instead:HIGH
10+
use-maxsplit-arg:50:11:50:36::Use Foo.class_str.rsplit(',', maxsplit=1)[-1] instead:HIGH
11+
use-maxsplit-arg:58:12:58:40::Use test.get_string().split(',', maxsplit=1)[0] instead:HIGH
12+
use-maxsplit-arg:59:11:59:39::Use test.get_string().rsplit(',', maxsplit=1)[-1] instead:HIGH
13+
use-maxsplit-arg:68:10:68:22::Use s.split(' ', maxsplit=1)[0] instead:HIGH
14+
use-maxsplit-arg:69:10:69:22::Use s.rsplit(' ', maxsplit=1)[-1] instead:HIGH
15+
use-maxsplit-arg:78:6:78:26::Use Bar.split.split(',', maxsplit=1)[0] instead:HIGH
16+
use-maxsplit-arg:79:6:79:26::Use Bar.split.rsplit(',', maxsplit=1)[-1] instead:HIGH
17+
use-maxsplit-arg:80:6:80:27::Use Bar.split.split(',', maxsplit=1)[0] instead:HIGH
18+
use-maxsplit-arg:81:6:81:27::Use Bar.split.rsplit(',', maxsplit=1)[-1] instead:HIGH
19+
use-maxsplit-arg:84:4:84:23::Use '1,2,3'.split('\n', maxsplit=1)[0] instead:HIGH
20+
use-maxsplit-arg:85:4:85:26::Use '1,2,3'.rsplit('split', maxsplit=1)[-1] instead:HIGH
21+
use-maxsplit-arg:86:4:86:28::Use '1,2,3'.split('rsplit', maxsplit=1)[0] instead:HIGH
22+
use-maxsplit-arg:99:11:99:30::Use SEQ.rsplit(None, maxsplit=1)[-1] instead:HIGH
23+
use-maxsplit-arg:100:11:100:31::Use SEQ.rsplit(None, maxsplit=1)[-1] instead:INFERENCE

0 commit comments

Comments
 (0)