Skip to content

Issue 9519 super init with non self arg #10190

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 34 additions & 5 deletions pylint/checkers/typecheck.py
Original file line number Diff line number Diff line change
Expand Up @@ -1448,6 +1448,20 @@ def _check_isinstance_args(self, node: nodes.Call, callable_name: str) -> None:
confidence=INFERENCE,
)

def _is_super(self, node: nodes.Call) -> bool:
"""True if this node is a call to super()."""
if (
isinstance(node.func, nodes.Attribute)
and node.func.attrname == "__init__"
and isinstance(node.func.expr, nodes.Call)
and isinstance(node.func.expr.func, nodes.Name)
and node.func.expr.func.name == "super"
):
inferred = safe_infer(node.func.expr)
if isinstance(inferred, astroid.objects.Super):
return True
return False

# pylint: disable = too-many-branches, too-many-locals, too-many-statements
def visit_call(self, node: nodes.Call) -> None:
"""Check that called functions/methods are inferred to callable objects,
Expand All @@ -1464,22 +1478,37 @@ def visit_call(self, node: nodes.Call) -> None:
# those errors are handled by different warnings.
return

# Build the set of keyword arguments, checking for duplicate keywords,
# and count the positional arguments.
call_site = astroid.arguments.CallSite.from_call(node)

if called.args.args is None:
if called.name == "isinstance":
# Verify whether second argument of isinstance is a valid type
self._check_isinstance_args(node, callable_name)
# Built-in functions have no argument information.
# Built-in functions have no argument information, but we know
# super() only takes self.
if self._is_super(node) and utils.is_builtin_object(called):
if (
call_site
and call_site.positional_arguments
and (first_arg := call_site.positional_arguments[0])
and not (
isinstance(first_arg, nodes.Name) and first_arg.name == "self"
)
):
self.add_message(
"too-many-function-args",
node=node,
args=(callable_name,),
)
return

if len(called.argnames()) != len(set(called.argnames())):
# Duplicate parameter name (see duplicate-argument). We can't really
# make sense of the function call in this case, so just return.
return

# Build the set of keyword arguments, checking for duplicate keywords,
# and count the positional arguments.
call_site = astroid.arguments.CallSite.from_call(node)

# Warn about duplicated keyword arguments, such as `f=24, **{'f': 24}`
for keyword in call_site.duplicated_keywords:
self.add_message("repeated-keyword", node=node, args=(keyword,))
Expand Down
15 changes: 15 additions & 0 deletions tests/regrtest_data/super_init_with_non_self_argument.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
"""
https://github.com/pylint-dev/pylint/issues/9519
"""

# pylint: disable=missing-class-docstring,missing-function-docstring,too-few-public-methods
class Window:
def print_text(self, txt):
print(f'{__class__} {txt}')


class Win(Window):
def __init__(self, txt):
super().__init__(txt)

Win('hello')
14 changes: 14 additions & 0 deletions tests/test_self.py
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,20 @@ def test_wrong_import_position_when_others_disabled(self) -> None:
actual_output = actual_output[actual_output.find("\n") :]
assert self._clean_paths(expected_output.strip()) == actual_output.strip()

def test_super_init_with_non_self_argument(self) -> None:
module1 = join(HERE, "regrtest_data", "super_init_with_non_self_argument.py")
args = [module1, "-rn", "-sn"]
out = StringIO()
self._run_pylint(args, out=out)
actual_output = self._clean_paths(out.getvalue().strip())
expected = textwrap.dedent(
f"""
************* Module super_init_with_non_self_argument
{module1}:13:8: E1121: Too many positional arguments for method call (too-many-function-args)
"""
)
assert self._clean_paths(expected.strip()) == actual_output.strip()

def test_progress_reporting(self) -> None:
module1 = join(HERE, "regrtest_data", "import_something.py")
module2 = join(HERE, "regrtest_data", "wrong_import_position.py")
Expand Down
Loading