-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New checker - Detect use of unnecessary ellipsis (#5470)
Closes #5460
- Loading branch information
Showing
8 changed files
with
161 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
"""Ellipsis checker for Python code | ||
""" | ||
from astroid import nodes | ||
|
||
from pylint.checkers import BaseChecker | ||
from pylint.checkers.utils import check_messages | ||
from pylint.interfaces import IAstroidChecker | ||
from pylint.lint import PyLinter | ||
|
||
|
||
class EllipsisChecker(BaseChecker): | ||
__implements__ = (IAstroidChecker,) | ||
name = "unnecessary_ellipsis" | ||
msgs = { | ||
"W2301": ( | ||
"Unnecessary ellipsis constant", | ||
"unnecessary-ellipsis", | ||
"Used when the ellipsis constant is encountered and can be avoided. " | ||
"A line of code consisting of an ellipsis is unnecessary if " | ||
"there is a docstring on the preceding line or if there is a " | ||
"statement in the same scope.", | ||
) | ||
} | ||
|
||
@check_messages("unnecessary-ellipsis") | ||
def visit_const(self, node: nodes.Const) -> None: | ||
"""Check if the ellipsis constant is used unnecessarily. | ||
Emit a warning when: | ||
- A line consisting of an ellipsis is preceded by a docstring. | ||
- A statement exists in the same scope as the ellipsis. | ||
For example: A function consisting of an ellipsis followed by a | ||
return statement on the next line. | ||
""" | ||
if ( | ||
node.pytype() == "builtins.Ellipsis" | ||
and not isinstance(node.parent, (nodes.Assign, nodes.AnnAssign, nodes.Call)) | ||
and ( | ||
len(node.parent.parent.child_sequence(node.parent)) > 1 | ||
or ( | ||
isinstance(node.parent.parent, (nodes.ClassDef, nodes.FunctionDef)) | ||
and (node.parent.parent.doc is not None) | ||
) | ||
) | ||
): | ||
self.add_message("unnecessary-ellipsis", node=node) | ||
|
||
|
||
def register(linter: PyLinter) -> None: | ||
"""required method to auto register this checker""" | ||
linter.register_checker(EllipsisChecker(linter)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,7 +3,6 @@ | |
|
||
class Class: | ||
attr: int | ||
... | ||
|
||
|
||
# `bar` definitely does not exist here, but in a complex scenario, | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
"""Emit a warning when the ellipsis constant is used and can be avoided""" | ||
|
||
# pylint: disable=missing-docstring, too-few-public-methods | ||
|
||
from typing import List, overload, Union | ||
|
||
# Ellipsis and preceding statement | ||
try: | ||
A = 2 | ||
except ValueError: | ||
A = 24 | ||
... # [unnecessary-ellipsis] | ||
|
||
def ellipsis_and_subsequent_statement(): | ||
... # [unnecessary-ellipsis] | ||
return 0 | ||
|
||
# The parent of ellipsis is an assignment | ||
B = ... | ||
C = [..., 1, 2, 3] | ||
|
||
# The parent of ellipsis is a call | ||
if "X" is type(...): | ||
... | ||
|
||
def docstring_only(): | ||
'''In Python, stubbed functions often have a body that contains just a | ||
single `...` constant, indicating that the function doesn't do | ||
anything. However, a stubbed function can also have just a | ||
docstring, and function with a docstring and no body also does | ||
nothing. | ||
''' | ||
|
||
|
||
# This function has no docstring, so it needs a `...` constant. | ||
def ellipsis_only(): | ||
... | ||
|
||
|
||
def docstring_and_ellipsis(): | ||
'''This function doesn't do anything, but it has a docstring, so its | ||
`...` constant is useless clutter. | ||
NEW CHECK: unnecessary-ellipsis | ||
This would check for stubs with both docstrings and `...` | ||
constants, suggesting the removal of the useless `...` | ||
constants | ||
''' | ||
... # [unnecessary-ellipsis] | ||
|
||
|
||
class DocstringOnly: | ||
'''The same goes for class stubs: docstring, or `...`, but not both. | ||
''' | ||
|
||
|
||
# No problem | ||
class EllipsisOnly: | ||
... | ||
|
||
|
||
class DocstringAndEllipsis: | ||
'''Whoops! Mark this one as bad too. | ||
''' | ||
... # [unnecessary-ellipsis] | ||
|
||
|
||
# Function overloading | ||
@overload | ||
def summarize(data: int) -> float: ... | ||
|
||
|
||
@overload | ||
def summarize(data: str) -> str: ... | ||
|
||
|
||
def summarize(data): | ||
if isinstance(data, str): | ||
... | ||
return float(data) | ||
|
||
|
||
|
||
# Method overloading | ||
class MyIntegerList(List[int]): | ||
@overload | ||
def __getitem__(self, index: int) -> int: ... | ||
|
||
@overload | ||
def __getitem__(self, index: slice) -> List[int]: ... | ||
|
||
def __getitem__(self, index: Union[int, slice]) -> Union[int, List[int]]: | ||
if isinstance(index, int): | ||
... | ||
elif isinstance(index, slice): | ||
... | ||
else: | ||
raise TypeError(...) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
unnecessary-ellipsis:12:4:12:7::Unnecessary ellipsis constant:UNDEFINED | ||
unnecessary-ellipsis:15:4:15:7:ellipsis_and_subsequent_statement:Unnecessary ellipsis constant:UNDEFINED | ||
unnecessary-ellipsis:50:4:50:7:docstring_and_ellipsis:Unnecessary ellipsis constant:UNDEFINED | ||
unnecessary-ellipsis:66:4:66:7:DocstringAndEllipsis:Unnecessary ellipsis constant:UNDEFINED |