Skip to content
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

Check py-version for async unnecessary-dunder-call #7549

Merged
merged 2 commits into from Oct 1, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions doc/whatsnew/fragments/7529.false_positive
@@ -0,0 +1,4 @@
Fix the message for ``unnecessary-dunder-call`` for ``__aiter__`` and ``__aneext__``. Also
only emit the warning when ``py-version`` >= 3.10.

Closes #7529
12 changes: 8 additions & 4 deletions pylint/checkers/dunder_methods.py
Expand Up @@ -105,8 +105,6 @@
"__floor__": "Use math.floor function",
"__ceil__": "Use math.ceil function",
"__enter__": "Invoke context manager directly",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should handle this generically by changing this datastructure (DUNDER_METHODS) to include the min version of the dunder method. Maybe:

DUNDER_METHODS = {
    "3.10": {
       "__aiter__": "Use iter built-in function",
       "__anext__": "Use next built-in function",
    }
    "default" : {
        "__index__": "Use index method",
        "__round__": "Use round built-in function",
        ...
    }
}

"__aiter__": "Use iter built-in function",
"__anext__": "Use next built-in function",
"__aenter__": "Invoke context manager directly",
"__copy__": "Use copy.copy function",
"__deepcopy__": "Use copy.deepcopy function",
Expand Down Expand Up @@ -143,6 +141,12 @@ class DunderCallChecker(BaseChecker):
}
options = ()

def open(self) -> None:
self._dunder_methods = dict(DUNDER_METHODS)
if self.linter.config.py_version >= (3, 10):
self._dunder_methods["__aiter__"] = "Use aiter built-in function"
self._dunder_methods["__anext__"] = "Use anext built-in function"

@staticmethod
def within_dunder_def(node: nodes.NodeNG) -> bool:
"""Check if dunder method call is within a dunder method definition."""
Expand All @@ -161,7 +165,7 @@ def visit_call(self, node: nodes.Call) -> None:
"""Check if method being called is an unnecessary dunder method."""
if (
isinstance(node.func, nodes.Attribute)
and node.func.attrname in DUNDER_METHODS
and node.func.attrname in self._dunder_methods
and not self.within_dunder_def(node)
and not (
isinstance(node.func.expr, nodes.Call)
Expand All @@ -177,7 +181,7 @@ def visit_call(self, node: nodes.Call) -> None:
self.add_message(
"unnecessary-dunder-call",
node=node,
args=(node.func.attrname, DUNDER_METHODS[node.func.attrname]),
args=(node.func.attrname, self._dunder_methods[node.func.attrname]),
confidence=HIGH,
)

Expand Down
@@ -0,0 +1,15 @@
"""Checks for unnecessary-dunder-call on __aiter__/__anext__ with py-version=3.10."""


class MyClass:
"""A class implementing __aiter__ and __anext__."""

def __aiter__(self):
...

async def __anext__(self):
...


MyClass().__aiter__() # [unnecessary-dunder-call]
MyClass().__anext__() # [unnecessary-dunder-call]
@@ -0,0 +1,2 @@
[master]
py-version=3.10
@@ -0,0 +1,2 @@
unnecessary-dunder-call:14:0:14:21::Unnecessarily calls dunder method __aiter__. Use aiter built-in function.:HIGH
unnecessary-dunder-call:15:0:15:21::Unnecessarily calls dunder method __anext__. Use anext built-in function.:HIGH
@@ -0,0 +1,15 @@
"""Checks for unnecessary-dunder-call on __aiter__/__anext__ with py-version=3.9."""


class MyClass:
"""A class implementing __aiter__ and __anext__."""

def __aiter__(self):
...

async def __anext__(self):
...


MyClass().__aiter__()
MyClass().__anext__()
@@ -0,0 +1,2 @@
[MAIN]
py-version=3.9