Skip to content

Commit 49653f4

Browse files
hmc-cs-mdrissiMehdi Drissijacobtylerwalls
authored
Extend safe_infer's sense of ambiguity to arg names and defaults (#8668)
Co-authored-by: Mehdi Drissi <[email protected]> Co-authored-by: Jacob Walls <[email protected]>
1 parent 5bb1fd1 commit 49653f4

File tree

3 files changed

+82
-4
lines changed

3 files changed

+82
-4
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Extend concept of "function ambiguity" in ``safe_infer()`` from
2+
differing number of function arguments to differing set of argument names.
3+
4+
Solves false positives in ``tensorflow``.
5+
6+
Closes #3613

pylint/checkers/utils.py

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1381,12 +1381,10 @@ def safe_infer(
13811381
return None
13821382
if (
13831383
isinstance(inferred, nodes.FunctionDef)
1384-
and inferred.args.args is not None
13851384
and isinstance(value, nodes.FunctionDef)
1386-
and value.args.args is not None
1387-
and len(inferred.args.args) != len(value.args.args)
1385+
and function_arguments_are_ambiguous(inferred, value)
13881386
):
1389-
return None # Different number of arguments indicates ambiguity
1387+
return None
13901388
except astroid.InferenceError:
13911389
return None # There is some kind of ambiguity
13921390
except StopIteration:
@@ -1408,6 +1406,33 @@ def infer_all(
14081406
raise AstroidError from e
14091407

14101408

1409+
def function_arguments_are_ambiguous(
1410+
func1: nodes.FunctionDef, func2: nodes.FunctionDef
1411+
) -> bool:
1412+
if func1.argnames() != func2.argnames():
1413+
return True
1414+
# Check ambiguity among function default values
1415+
pairs_of_defaults = [
1416+
(func1.args.defaults, func2.args.defaults),
1417+
(func1.args.kw_defaults, func2.args.kw_defaults),
1418+
]
1419+
for zippable_default in pairs_of_defaults:
1420+
if None in zippable_default:
1421+
continue
1422+
if len(zippable_default[0]) != len(zippable_default[1]):
1423+
return True
1424+
for default1, default2 in zip(*zippable_default):
1425+
if isinstance(default1, nodes.Const) and isinstance(default2, nodes.Const):
1426+
if default1.value != default2.value:
1427+
return True
1428+
elif isinstance(default1, nodes.Name) and isinstance(default2, nodes.Name):
1429+
if default1.name != default2.name:
1430+
return True
1431+
else:
1432+
return True
1433+
return False
1434+
1435+
14111436
def has_known_bases(
14121437
klass: nodes.ClassDef, context: InferenceContext | None = None
14131438
) -> bool:

tests/functional/u/unexpected_keyword_arg.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,3 +116,50 @@ def test_no_return():
116116

117117

118118
test_no_return(internal_arg=2) # [unexpected-keyword-arg]
119+
120+
121+
def ambiguous_func1(arg1):
122+
print(arg1)
123+
124+
125+
def ambiguous_func2(other_arg1):
126+
print(other_arg1)
127+
128+
129+
func1 = ambiguous_func1 if unknown else ambiguous_func2
130+
func1(other_arg1=1)
131+
132+
133+
def ambiguous_func3(arg1=None):
134+
print(arg1)
135+
136+
137+
func2 = ambiguous_func1 if unknown else ambiguous_func3
138+
func2()
139+
140+
141+
def ambiguous_func4(arg1=print):
142+
print(arg1)
143+
144+
145+
def ambiguous_func5(arg1=input):
146+
print(arg1)
147+
148+
149+
def ambiguous_func6(arg1=42):
150+
print(arg1)
151+
152+
153+
# Two functions with same keyword argument but different defaults (names)
154+
func3 = ambiguous_func4 if unknown else ambiguous_func5
155+
func3()
156+
157+
158+
# Two functions with same keyword argument but different defaults (constants)
159+
func4 = ambiguous_func3 if unknown else ambiguous_func6
160+
func4()
161+
162+
163+
# Two functions with same keyword argument but mixed defaults (names, constant)
164+
func5 = ambiguous_func3 if unknown else ambiguous_func5
165+
func5()

0 commit comments

Comments
 (0)