From 29ae03e011014fa5d224fa092e92bb18f8b045a3 Mon Sep 17 00:00:00 2001 From: DudeNr33 <3929834+DudeNr33@users.noreply.github.com> Date: Fri, 27 May 2022 07:43:27 +0200 Subject: [PATCH 1/4] Remove variables and code paths that are always static in productive use. --- pylint/pyreverse/inspector.py | 28 +++++----------------------- tests/pyreverse/test_inspector.py | 2 +- 2 files changed, 6 insertions(+), 24 deletions(-) diff --git a/pylint/pyreverse/inspector.py b/pylint/pyreverse/inspector.py index 35e993f11b..5982a0f628 100644 --- a/pylint/pyreverse/inspector.py +++ b/pylint/pyreverse/inspector.py @@ -24,11 +24,6 @@ _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 +37,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 +51,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 +126,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,7 +183,7 @@ def visit_classdef(self, node: nodes.ClassDef) -> None: self.handle_assignattr_type(assignattr, node) # resolve implemented interface try: - ifaces = interfaces(node, self.inherited_interfaces) + ifaces = interfaces(node) node.implements = list(ifaces) if ifaces is not None else [] except astroid.InferenceError: node.implements = [] @@ -213,11 +200,6 @@ def visit_functiondef(self, node: nodes.FunctionDef) -> None: 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 db0f4656a0..c87b0f68f9 100644 --- a/tests/pyreverse/test_inspector.py +++ b/tests/pyreverse/test_inspector.py @@ -110,7 +110,7 @@ class Concrete23(Concrete1): pass ("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 From 66a79f660dbd4ae85b9c11bab52093288b567aae Mon Sep 17 00:00:00 2001 From: DudeNr33 <3929834+DudeNr33@users.noreply.github.com> Date: Fri, 27 May 2022 08:20:50 +0200 Subject: [PATCH 2/4] Add deprecation warning if an interface defined through ``__implements__`` is found. --- ChangeLog | 3 +++ doc/whatsnew/2.14.rst | 3 +++ pylint/pyreverse/inspector.py | 12 +++++++++++- tests/pyreverse/test_inspector.py | 15 +++++++++++++++ 4 files changed, 32 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index a9de5766ba..c0d3bf3052 100644 --- a/ChangeLog +++ b/ChangeLog @@ -182,6 +182,9 @@ Release date: TBA Ref #2287 +* ``pyreverse``: Resolving and displaying implemented interfaces that are defined by the ``__implements__`` + attribute has been deprecated and will be removed in 3.0. + * ``invalid-enum-extension``: Used when a class tries to extend an inherited Enum class. Closes #5501 diff --git a/doc/whatsnew/2.14.rst b/doc/whatsnew/2.14.rst index 8fa256f0af..c83dfcc171 100644 --- a/doc/whatsnew/2.14.rst +++ b/doc/whatsnew/2.14.rst @@ -383,6 +383,9 @@ Deprecations Ref #5392 +* ``pyreverse``: Resolving and displaying implemented interfaces that are defined by the ``__implements__`` + attribute has been deprecated and will be removed in 3.0. + * ``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 5982a0f628..61de3d5ced 100644 --- a/pylint/pyreverse/inspector.py +++ b/pylint/pyreverse/inspector.py @@ -12,6 +12,7 @@ import collections import os import traceback +import warnings from collections.abc import Generator from typing import Any, Callable, Optional @@ -184,7 +185,16 @@ def visit_classdef(self, node: nodes.ClassDef) -> None: # resolve implemented interface try: ifaces = interfaces(node) - node.implements = list(ifaces) if ifaces is not None else [] + if ifaces is not None: + node.implements = list(ifaces) + 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 = [] diff --git a/tests/pyreverse/test_inspector.py b/tests/pyreverse/test_inspector.py index c87b0f68f9..0fd54e8f81 100644 --- a/tests/pyreverse/test_inspector.py +++ b/tests/pyreverse/test_inspector.py @@ -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) From 8a3dc4443c212b9796a2177e0a5acee9bc04d9a3 Mon Sep 17 00:00:00 2001 From: Andreas Finkler <3929834+DudeNr33@users.noreply.github.com> Date: Fri, 27 May 2022 18:25:38 +0200 Subject: [PATCH 3/4] Update pylint/pyreverse/inspector.py Co-authored-by: Pierre Sassoulas --- pylint/pyreverse/inspector.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pylint/pyreverse/inspector.py b/pylint/pyreverse/inspector.py index 61de3d5ced..8f0f9bcfcd 100644 --- a/pylint/pyreverse/inspector.py +++ b/pylint/pyreverse/inspector.py @@ -187,6 +187,7 @@ def visit_classdef(self, node: nodes.ClassDef) -> None: 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 " From 5e2a150bab1b9d24530bc7de0ae72c3ae7e0fa5c Mon Sep 17 00:00:00 2001 From: DudeNr33 <3929834+DudeNr33@users.noreply.github.com> Date: Fri, 27 May 2022 18:27:26 +0200 Subject: [PATCH 4/4] Group changelog entries together with refactor from #6712 --- ChangeLog | 8 +++++--- doc/whatsnew/2.14.rst | 10 ++++++---- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/ChangeLog b/ChangeLog index 48cf8b406a..46731bf916 100644 --- a/ChangeLog +++ b/ChangeLog @@ -181,14 +181,16 @@ 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__``. Ref #2287 -* ``pyreverse``: Resolving and displaying implemented interfaces that are defined by the ``__implements__`` - attribute has been deprecated and will be removed in 3.0. - * ``invalid-enum-extension``: Used when a class tries to extend an inherited Enum class. Closes #5501 diff --git a/doc/whatsnew/2.14.rst b/doc/whatsnew/2.14.rst index 173e81db08..d5caee73a1 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,9 +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.