summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Finkler <3929834+DudeNr33@users.noreply.github.com>2022-05-27 19:12:05 +0200
committerGitHub <noreply@github.com>2022-05-27 19:12:05 +0200
commit4fb07dfe2c93ed9dd36bae223f75d632a7b498e2 (patch)
treed674414fe414cfaf3ee271f75390d633c4696bfc
parent3e2c1a0c9c990cba49ace02f51d4117a45686e39 (diff)
downloadpylint-git-4fb07dfe2c93ed9dd36bae223f75d632a7b498e2.tar.gz
Refactor and deprecate resolving of interface implementations in `pyreverse` (#6713)
* Remove variables and code paths that are always static in productive use. * Add deprecation warning if an interface defined through ``__implements__`` is found. * Update pylint/pyreverse/inspector.py Co-authored-by: Pierre Sassoulas <pierre.sassoulas@gmail.com> * Group changelog entries together with refactor from #6712 Co-authored-by: Pierre Sassoulas <pierre.sassoulas@gmail.com>
-rw-r--r--ChangeLog5
-rw-r--r--doc/whatsnew/2.14.rst13
-rw-r--r--pylint/pyreverse/inspector.py41
-rw-r--r--tests/pyreverse/test_inspector.py17
4 files changed, 47 insertions, 29 deletions
diff --git a/ChangeLog b/ChangeLog
index 0fb600683..46731bf91 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -181,6 +181,11 @@ Release date: TBA
Ref #6712
+* ``pyreverse``: Resolving and displaying implemented interfaces that are defined by the ``__implements__``
+ attribute has been deprecated and will be removed in 3.0.
+
+ Ref #6713
+
* ``interfaces.implements`` has been deprecated and will be removed in 3.0. Please use standard inheritance
patterns instead of ``__implements__``.
diff --git a/doc/whatsnew/2.14.rst b/doc/whatsnew/2.14.rst
index 3e4fd87e6..d5caee73a 100644
--- a/doc/whatsnew/2.14.rst
+++ b/doc/whatsnew/2.14.rst
@@ -281,10 +281,6 @@ Other Changes
Closes #6644
-* ``pylint.pyreverse.ASTWalker`` has been removed, as it was only used internally by a single child class.
-
- Ref #6712
-
Deprecations
============
@@ -387,6 +383,15 @@ Deprecations
Ref #5392
+* ``pylint.pyreverse.ASTWalker`` has been removed, as it was only used internally by a single child class.
+
+ Ref #6712
+
+* ``pyreverse``: Resolving and displaying implemented interfaces that are defined by the ``__implements__``
+ attribute has been deprecated and will be removed in 3.0.
+
+ Ref #6713
+
* ``is_class_subscriptable_pep585_with_postponed_evaluation_enabled`` has been deprecated.
Use ``is_postponed_evaluation_enabled(node) and is_node_in_type_annotation_context(node)``
instead.
diff --git a/pylint/pyreverse/inspector.py b/pylint/pyreverse/inspector.py
index 35e993f11..8f0f9bcfc 100644
--- a/pylint/pyreverse/inspector.py
+++ b/pylint/pyreverse/inspector.py
@@ -12,6 +12,7 @@ from __future__ import annotations
import collections
import os
import traceback
+import warnings
from collections.abc import Generator
from typing import Any, Callable, Optional
@@ -24,11 +25,6 @@ from pylint.pyreverse import utils
_WrapperFuncT = Callable[[Callable[[str], nodes.Module], str], Optional[nodes.Module]]
-def _iface_hdlr(_: nodes.NodeNG | Any) -> bool:
- """Handler used by interfaces to handle suspicious interface nodes."""
- return True
-
-
def _astroid_wrapper(
func: Callable[[str], nodes.Module], modname: str
) -> nodes.Module | None:
@@ -42,17 +38,13 @@ def _astroid_wrapper(
return None
-def interfaces(
- node: nodes.ClassDef,
- herited: bool = True,
- handler_func: Callable[[nodes.NodeNG | Any], bool] = _iface_hdlr,
-) -> Generator[Any, None, None]:
+def interfaces(node: nodes.ClassDef) -> Generator[Any, None, None]:
"""Return an iterator on interfaces implemented by the given class node."""
try:
implements = astroid.bases.Instance(node).getattr("__implements__")[0]
except astroid.exceptions.NotFoundError:
return
- if not herited and implements.frame(future=True) is not node:
+ if implements.frame(future=True) is not node:
return
found = set()
missing = False
@@ -60,7 +52,7 @@ def interfaces(
if iface is astroid.Uninferable:
missing = True
continue
- if iface not in found and handler_func(iface):
+ if iface not in found:
found.add(iface)
yield iface
if missing:
@@ -135,13 +127,9 @@ class Linker(IdGeneratorMixIn, utils.LocalsVisitor):
list of implemented interface _objects_ (only on astroid.Class nodes)
"""
- def __init__(
- self, project: Project, inherited_interfaces: bool = False, tag: bool = False
- ) -> None:
+ def __init__(self, project: Project, tag: bool = False) -> None:
IdGeneratorMixIn.__init__(self)
utils.LocalsVisitor.__init__(self)
- # take inherited interface in consideration or not
- self.inherited_interfaces = inherited_interfaces
# tag nodes or not
self.tag = tag
# visited project
@@ -196,8 +184,18 @@ class Linker(IdGeneratorMixIn, utils.LocalsVisitor):
self.handle_assignattr_type(assignattr, node)
# resolve implemented interface
try:
- ifaces = interfaces(node, self.inherited_interfaces)
- node.implements = list(ifaces) if ifaces is not None else []
+ ifaces = interfaces(node)
+ if ifaces is not None:
+ node.implements = list(ifaces)
+ # TODO: 3.0: Remove support for __implements__
+ warnings.warn(
+ "pyreverse will drop support for resolving and displaying implemented interfaces in pylint 3.0. "
+ "The implementation relies on the '__implements__' attribute proposed in PEP 245, which was rejected "
+ "in 2006.",
+ DeprecationWarning,
+ )
+ else:
+ node.implements = []
except astroid.InferenceError:
node.implements = []
@@ -213,11 +211,6 @@ class Linker(IdGeneratorMixIn, utils.LocalsVisitor):
if self.tag:
node.uid = self.generate_id()
- link_project = visit_project
- link_module = visit_module
- link_class = visit_classdef
- link_function = visit_functiondef
-
def visit_assignname(self, node: nodes.AssignName) -> None:
"""Visit an astroid.AssignName node.
diff --git a/tests/pyreverse/test_inspector.py b/tests/pyreverse/test_inspector.py
index db0f4656a..0fd54e8f8 100644
--- a/tests/pyreverse/test_inspector.py
+++ b/tests/pyreverse/test_inspector.py
@@ -110,7 +110,7 @@ def test_interfaces() -> None:
("Concrete0", ["MyIFace"]),
("Concrete1", ["MyIFace", "AnotherIFace"]),
("Concrete2", ["MyIFace", "AnotherIFace"]),
- ("Concrete23", ["MyIFace", "AnotherIFace"]),
+ ("Concrete23", []),
):
klass = module[klass]
assert [i.name for i in inspector.interfaces(klass)] == interfaces
@@ -130,3 +130,18 @@ def test_project_node(project: Project) -> None:
"data.suppliermodule_test",
]
assert sorted(project.keys()) == expected
+
+
+def test_interface_deprecation(project: Project) -> None:
+ linker = inspector.Linker(project)
+ cls = astroid.extract_node(
+ '''
+ class IMachin: pass
+
+ class Concrete: #@
+ """docstring"""
+ __implements__ = (IMachin,)
+ '''
+ )
+ with pytest.warns(DeprecationWarning):
+ linker.visit_classdef(cls)