summaryrefslogtreecommitdiff
path: root/pylint/pyreverse
diff options
context:
space:
mode:
Diffstat (limited to 'pylint/pyreverse')
-rw-r--r--pylint/pyreverse/__init__.py4
-rw-r--r--pylint/pyreverse/diadefslib.py17
-rw-r--r--pylint/pyreverse/diagrams.py27
-rw-r--r--pylint/pyreverse/dot_printer.py6
-rw-r--r--pylint/pyreverse/inspector.py53
-rw-r--r--pylint/pyreverse/main.py49
-rw-r--r--pylint/pyreverse/mermaidjs_printer.py9
-rw-r--r--pylint/pyreverse/plantuml_printer.py11
-rw-r--r--pylint/pyreverse/printer.py6
-rw-r--r--pylint/pyreverse/printer_factory.py6
-rw-r--r--pylint/pyreverse/utils.py9
-rw-r--r--pylint/pyreverse/vcg_printer.py303
-rw-r--r--pylint/pyreverse/writer.py31
13 files changed, 91 insertions, 440 deletions
diff --git a/pylint/pyreverse/__init__.py b/pylint/pyreverse/__init__.py
index 458c0f35d..175e9cb67 100644
--- a/pylint/pyreverse/__init__.py
+++ b/pylint/pyreverse/__init__.py
@@ -1,6 +1,6 @@
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
-# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Pyreverse.extensions."""
diff --git a/pylint/pyreverse/diadefslib.py b/pylint/pyreverse/diadefslib.py
index 85b23052e..3b7694823 100644
--- a/pylint/pyreverse/diadefslib.py
+++ b/pylint/pyreverse/diadefslib.py
@@ -1,6 +1,6 @@
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
-# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Handle diagram generation options for class diagram or default diagrams."""
@@ -12,6 +12,7 @@ from typing import Any
import astroid
from astroid import nodes
+from astroid.modutils import is_stdlib_module
from pylint.pyreverse.diagrams import ClassDiagram, PackageDiagram
from pylint.pyreverse.inspector import Linker, Project
@@ -67,10 +68,14 @@ class DiaDefGenerator:
return self.anc_level, self.association_level
def show_node(self, node: nodes.ClassDef) -> bool:
- """True if builtins and not show_builtins."""
- if self.config.show_builtin:
- return True
- return node.root().name != "builtins" # type: ignore[no-any-return]
+ """Determine if node should be shown based on config."""
+ if node.root().name == "builtins":
+ return self.config.show_builtin # type: ignore[no-any-return]
+
+ if is_stdlib_module(node.root().name):
+ return self.config.show_stdlib # type: ignore[no-any-return]
+
+ return True
def add_class(self, node: nodes.ClassDef) -> None:
"""Visit one class and add it to diagram."""
diff --git a/pylint/pyreverse/diagrams.py b/pylint/pyreverse/diagrams.py
index 4437d3c4e..01bce7dc3 100644
--- a/pylint/pyreverse/diagrams.py
+++ b/pylint/pyreverse/diagrams.py
@@ -1,6 +1,6 @@
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
-# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Diagram objects."""
@@ -13,7 +13,7 @@ import astroid
from astroid import nodes, util
from pylint.checkers.utils import decorated_with_property
-from pylint.pyreverse.utils import FilterMixIn, is_interface
+from pylint.pyreverse.utils import FilterMixIn
class Figure:
@@ -50,7 +50,13 @@ class DiagramEntity(Figure):
) -> None:
super().__init__()
self.title = title
- self.node: nodes.NodeNG = node if node else nodes.NodeNG()
+ self.node: nodes.NodeNG = node or nodes.NodeNG(
+ lineno=None,
+ col_offset=None,
+ end_lineno=None,
+ end_col_offset=None,
+ parent=None,
+ )
self.shape = self.default_shape
@@ -195,11 +201,7 @@ class ClassDiagram(Figure, FilterMixIn):
node = obj.node
obj.attrs = self.get_attrs(node)
obj.methods = self.get_methods(node)
- # shape
- if is_interface(node):
- obj.shape = "interface"
- else:
- obj.shape = "class"
+ obj.shape = "class"
# inheritance link
for par_node in node.ancestors(recurs=False):
try:
@@ -207,13 +209,6 @@ class ClassDiagram(Figure, FilterMixIn):
self.add_relationship(obj, par_obj, "specialization")
except KeyError:
continue
- # implements link
- for impl_node in node.implements:
- try:
- impl_obj = self.object_from_node(impl_node)
- self.add_relationship(obj, impl_obj, "implements")
- except KeyError:
- continue
# associations & aggregations links
for name, values in list(node.aggregations_type.items()):
diff --git a/pylint/pyreverse/dot_printer.py b/pylint/pyreverse/dot_printer.py
index 99cb17e97..edaea2384 100644
--- a/pylint/pyreverse/dot_printer.py
+++ b/pylint/pyreverse/dot_printer.py
@@ -1,6 +1,6 @@
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
-# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Class to generate files in dot format and image formats supported by Graphviz."""
@@ -25,13 +25,11 @@ class HTMLLabels(Enum):
ALLOWED_CHARSETS: frozenset[str] = frozenset(("utf-8", "iso-8859-1", "latin1"))
SHAPES: dict[NodeType, str] = {
NodeType.PACKAGE: "box",
- NodeType.INTERFACE: "record",
NodeType.CLASS: "record",
}
# pylint: disable-next=consider-using-namedtuple-or-dataclass
ARROWS: dict[EdgeType, dict[str, str]] = {
EdgeType.INHERITS: {"arrowtail": "none", "arrowhead": "empty"},
- EdgeType.IMPLEMENTS: {"arrowtail": "node", "arrowhead": "empty", "style": "dashed"},
EdgeType.ASSOCIATION: {
"fontcolor": "green",
"arrowtail": "none",
diff --git a/pylint/pyreverse/inspector.py b/pylint/pyreverse/inspector.py
index 523ff8171..0cabe9473 100644
--- a/pylint/pyreverse/inspector.py
+++ b/pylint/pyreverse/inspector.py
@@ -1,6 +1,6 @@
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
-# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Visitor doing some post-processing on the astroid tree.
@@ -12,13 +12,11 @@ from __future__ import annotations
import collections
import os
import traceback
-import warnings
from abc import ABC, abstractmethod
-from collections.abc import Generator
-from typing import Any, Callable, Optional
+from typing import Callable, Optional
import astroid
-from astroid import nodes, util
+from astroid import nodes
from pylint import constants
from pylint.pyreverse import utils
@@ -39,27 +37,6 @@ def _astroid_wrapper(
return 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 implements.frame(future=True) is not node:
- return
- found = set()
- missing = False
- for iface in nodes.unpack_infer(implements):
- if isinstance(iface, util.UninferableBase):
- missing = True
- continue
- if iface not in found:
- found.add(iface)
- yield iface
- if missing:
- raise astroid.exceptions.InferenceError()
-
-
class IdGeneratorMixIn:
"""Mixin adding the ability to generate integer uid."""
@@ -129,9 +106,6 @@ class Linker(IdGeneratorMixIn, utils.LocalsVisitor):
* aggregations_type
as instance_attrs_type but for aggregations relationships
-
- * implements,
- list of implemented interface _objects_ (only on astroid.Class nodes)
"""
def __init__(self, project: Project, tag: bool = False) -> None:
@@ -172,7 +146,6 @@ class Linker(IdGeneratorMixIn, utils.LocalsVisitor):
"""Visit an astroid.Class node.
* set the locals_type and instance_attrs_type mappings
- * set the implements list and build it
* optionally tag the node with a unique id
"""
if hasattr(node, "locals_type"):
@@ -194,24 +167,6 @@ class Linker(IdGeneratorMixIn, utils.LocalsVisitor):
if not isinstance(assignattr, nodes.Unknown):
self.associations_handler.handle(assignattr, node)
self.handle_assignattr_type(assignattr, node)
- # resolve implemented interface
- try:
- ifaces = interfaces(node)
- if ifaces is not None:
- node.implements = list(ifaces)
- if node.implements:
- # 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 = []
def visit_functiondef(self, node: nodes.FunctionDef) -> None:
"""Visit an astroid.Function node.
diff --git a/pylint/pyreverse/main.py b/pylint/pyreverse/main.py
index a2dc94734..58128bb57 100644
--- a/pylint/pyreverse/main.py
+++ b/pylint/pyreverse/main.py
@@ -1,6 +1,6 @@
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
-# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Create UML diagrams for classes and modules in <packages>."""
@@ -27,7 +27,6 @@ from pylint.typing import Options
DIRECTLY_SUPPORTED_FORMATS = (
"dot",
- "vcg",
"puml",
"plantuml",
"mmd",
@@ -35,23 +34,16 @@ DIRECTLY_SUPPORTED_FORMATS = (
)
DEFAULT_COLOR_PALETTE = (
- "aliceblue",
- "antiquewhite",
- "aquamarine",
- "burlywood",
- "cadetblue",
- "chartreuse",
- "chocolate",
- "coral",
- "cornflowerblue",
- "cyan",
- "darkgoldenrod",
- "darkseagreen",
- "dodgerblue",
- "forestgreen",
- "gold",
- "hotpink",
- "mediumspringgreen",
+ # colorblind scheme taken from https://personal.sron.nl/~pault/
+ "#77AADD", # light blue
+ "#99DDFF", # light cyan
+ "#44BB99", # mint
+ "#BBCC33", # pear
+ "#AAAA00", # olive
+ "#EEDD88", # light yellow
+ "#EE8866", # orange
+ "#FFAABB", # pink
+ "#DDDDDD", # pale grey
)
OPTIONS: Options = (
@@ -138,6 +130,15 @@ OPTIONS: Options = (
},
),
(
+ "show-stdlib",
+ {
+ "short": "L",
+ "action": "store_true",
+ "default": False,
+ "help": "include standard library objects in representation of classes",
+ },
+ ),
+ (
"module-names",
{
"short": "m",
@@ -157,6 +158,14 @@ OPTIONS: Options = (
},
),
(
+ "no-standalone",
+ {
+ "action": "store_true",
+ "default": False,
+ "help": "only show nodes with connections",
+ },
+ ),
+ (
"output",
{
"short": "o",
diff --git a/pylint/pyreverse/mermaidjs_printer.py b/pylint/pyreverse/mermaidjs_printer.py
index a8f3c576b..61d0d7948 100644
--- a/pylint/pyreverse/mermaidjs_printer.py
+++ b/pylint/pyreverse/mermaidjs_printer.py
@@ -1,6 +1,6 @@
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
-# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Class to generate files in mermaidjs format."""
@@ -17,12 +17,10 @@ class MermaidJSPrinter(Printer):
NODES: dict[NodeType, str] = {
NodeType.CLASS: "class",
- NodeType.INTERFACE: "class",
NodeType.PACKAGE: "class",
}
ARROWS: dict[EdgeType, str] = {
EdgeType.INHERITS: "--|>",
- EdgeType.IMPLEMENTS: "..|>",
EdgeType.ASSOCIATION: "--*",
EdgeType.AGGREGATION: "--o",
EdgeType.USES: "-->",
@@ -46,7 +44,6 @@ class MermaidJSPrinter(Printer):
# pylint: disable=duplicate-code
if properties is None:
properties = NodeProperties(label=name)
- stereotype = "~~Interface~~" if type_ is NodeType.INTERFACE else ""
nodetype = self.NODES[type_]
body = []
if properties.attrs:
@@ -60,7 +57,7 @@ class MermaidJSPrinter(Printer):
line += f" {get_annotation_label(func.returns)}"
body.append(line)
name = name.split(".")[-1]
- self.emit(f"{nodetype} {name}{stereotype} {{")
+ self.emit(f"{nodetype} {name} {{")
self._inc_indent()
for line in body:
self.emit(line)
diff --git a/pylint/pyreverse/plantuml_printer.py b/pylint/pyreverse/plantuml_printer.py
index de3f983b7..5f703b62e 100644
--- a/pylint/pyreverse/plantuml_printer.py
+++ b/pylint/pyreverse/plantuml_printer.py
@@ -1,6 +1,6 @@
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
-# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Class to generate files in dot format and image formats supported by Graphviz."""
@@ -17,12 +17,10 @@ class PlantUmlPrinter(Printer):
NODES: dict[NodeType, str] = {
NodeType.CLASS: "class",
- NodeType.INTERFACE: "class",
NodeType.PACKAGE: "package",
}
ARROWS: dict[EdgeType, str] = {
EdgeType.INHERITS: "--|>",
- EdgeType.IMPLEMENTS: "..|>",
EdgeType.ASSOCIATION: "--*",
EdgeType.AGGREGATION: "--o",
EdgeType.USES: "-->",
@@ -56,10 +54,9 @@ class PlantUmlPrinter(Printer):
"""
if properties is None:
properties = NodeProperties(label=name)
- stereotype = " << interface >>" if type_ is NodeType.INTERFACE else ""
nodetype = self.NODES[type_]
if properties.color and properties.color != self.DEFAULT_COLOR:
- color = f" #{properties.color}"
+ color = f" #{properties.color.lstrip('#')}"
else:
color = ""
body = []
@@ -76,7 +73,7 @@ class PlantUmlPrinter(Printer):
label = properties.label if properties.label is not None else name
if properties.fontcolor and properties.fontcolor != self.DEFAULT_COLOR:
label = f"<color:{properties.fontcolor}>{label}</color>"
- self.emit(f'{nodetype} "{label}" as {name}{stereotype}{color} {{')
+ self.emit(f'{nodetype} "{label}" as {name}{color} {{')
self._inc_indent()
for line in body:
self.emit(line)
diff --git a/pylint/pyreverse/printer.py b/pylint/pyreverse/printer.py
index cdbf7e3c8..f08c74602 100644
--- a/pylint/pyreverse/printer.py
+++ b/pylint/pyreverse/printer.py
@@ -1,6 +1,6 @@
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
-# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Base class defining the interface for a printer."""
@@ -17,13 +17,11 @@ from pylint.pyreverse.utils import get_annotation_label
class NodeType(Enum):
CLASS = "class"
- INTERFACE = "interface"
PACKAGE = "package"
class EdgeType(Enum):
INHERITS = "inherits"
- IMPLEMENTS = "implements"
ASSOCIATION = "association"
AGGREGATION = "aggregation"
USES = "uses"
diff --git a/pylint/pyreverse/printer_factory.py b/pylint/pyreverse/printer_factory.py
index 41e8b46c8..fdbe480ed 100644
--- a/pylint/pyreverse/printer_factory.py
+++ b/pylint/pyreverse/printer_factory.py
@@ -1,6 +1,6 @@
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
-# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
from __future__ import annotations
@@ -8,10 +8,8 @@ from pylint.pyreverse.dot_printer import DotPrinter
from pylint.pyreverse.mermaidjs_printer import HTMLMermaidJSPrinter, MermaidJSPrinter
from pylint.pyreverse.plantuml_printer import PlantUmlPrinter
from pylint.pyreverse.printer import Printer
-from pylint.pyreverse.vcg_printer import VCGPrinter
filetype_to_printer: dict[str, type[Printer]] = {
- "vcg": VCGPrinter,
"plantuml": PlantUmlPrinter,
"puml": PlantUmlPrinter,
"mmd": MermaidJSPrinter,
diff --git a/pylint/pyreverse/utils.py b/pylint/pyreverse/utils.py
index 078bc1b7e..6294773b2 100644
--- a/pylint/pyreverse/utils.py
+++ b/pylint/pyreverse/utils.py
@@ -1,6 +1,6 @@
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
-# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
"""Generic classes/functions for pyreverse core/extensions."""
@@ -72,11 +72,6 @@ def get_visibility(name: str) -> str:
return visibility
-def is_interface(node: nodes.ClassDef) -> bool:
- # bw compatibility
- return node.type == "interface" # type: ignore[no-any-return]
-
-
def is_exception(node: nodes.ClassDef) -> bool:
# bw compatibility
return node.type == "exception" # type: ignore[no-any-return]
diff --git a/pylint/pyreverse/vcg_printer.py b/pylint/pyreverse/vcg_printer.py
deleted file mode 100644
index b9e2e94f3..000000000
--- a/pylint/pyreverse/vcg_printer.py
+++ /dev/null
@@ -1,303 +0,0 @@
-# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
-# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
-
-"""Functions to generate files readable with George Sander's vcg
-(Visualization of Compiler Graphs).
-
-You can download vcg at https://rw4.cs.uni-sb.de/~sander/html/gshome.html
-Note that vcg exists as a debian package.
-See vcg's documentation for explanation about the different values that
-maybe used for the functions parameters.
-"""
-
-from __future__ import annotations
-
-from collections.abc import Mapping
-from typing import Any
-
-from pylint.pyreverse.printer import EdgeType, Layout, NodeProperties, NodeType, Printer
-
-ATTRS_VAL = {
- "algos": (
- "dfs",
- "tree",
- "minbackward",
- "left_to_right",
- "right_to_left",
- "top_to_bottom",
- "bottom_to_top",
- "maxdepth",
- "maxdepthslow",
- "mindepth",
- "mindepthslow",
- "mindegree",
- "minindegree",
- "minoutdegree",
- "maxdegree",
- "maxindegree",
- "maxoutdegree",
- ),
- "booleans": ("yes", "no"),
- "colors": (
- "black",
- "white",
- "blue",
- "red",
- "green",
- "yellow",
- "magenta",
- "lightgrey",
- "cyan",
- "darkgrey",
- "darkblue",
- "darkred",
- "darkgreen",
- "darkyellow",
- "darkmagenta",
- "darkcyan",
- "gold",
- "lightblue",
- "lightred",
- "lightgreen",
- "lightyellow",
- "lightmagenta",
- "lightcyan",
- "lilac",
- "turquoise",
- "aquamarine",
- "khaki",
- "purple",
- "yellowgreen",
- "pink",
- "orange",
- "orchid",
- ),
- "shapes": ("box", "ellipse", "rhomb", "triangle"),
- "textmodes": ("center", "left_justify", "right_justify"),
- "arrowstyles": ("solid", "line", "none"),
- "linestyles": ("continuous", "dashed", "dotted", "invisible"),
-}
-
-# meaning of possible values:
-# O -> string
-# 1 -> int
-# list -> value in list
-GRAPH_ATTRS = {
- "title": 0,
- "label": 0,
- "color": ATTRS_VAL["colors"],
- "textcolor": ATTRS_VAL["colors"],
- "bordercolor": ATTRS_VAL["colors"],
- "width": 1,
- "height": 1,
- "borderwidth": 1,
- "textmode": ATTRS_VAL["textmodes"],
- "shape": ATTRS_VAL["shapes"],
- "shrink": 1,
- "stretch": 1,
- "orientation": ATTRS_VAL["algos"],
- "vertical_order": 1,
- "horizontal_order": 1,
- "xspace": 1,
- "yspace": 1,
- "layoutalgorithm": ATTRS_VAL["algos"],
- "late_edge_labels": ATTRS_VAL["booleans"],
- "display_edge_labels": ATTRS_VAL["booleans"],
- "dirty_edge_labels": ATTRS_VAL["booleans"],
- "finetuning": ATTRS_VAL["booleans"],
- "manhattan_edges": ATTRS_VAL["booleans"],
- "smanhattan_edges": ATTRS_VAL["booleans"],
- "port_sharing": ATTRS_VAL["booleans"],
- "edges": ATTRS_VAL["booleans"],
- "nodes": ATTRS_VAL["booleans"],
- "splines": ATTRS_VAL["booleans"],
-}
-NODE_ATTRS = {
- "title": 0,
- "label": 0,
- "color": ATTRS_VAL["colors"],
- "textcolor": ATTRS_VAL["colors"],
- "bordercolor": ATTRS_VAL["colors"],
- "width": 1,
- "height": 1,
- "borderwidth": 1,
- "textmode": ATTRS_VAL["textmodes"],
- "shape": ATTRS_VAL["shapes"],
- "shrink": 1,
- "stretch": 1,
- "vertical_order": 1,
- "horizontal_order": 1,
-}
-EDGE_ATTRS = {
- "sourcename": 0,
- "targetname": 0,
- "label": 0,
- "linestyle": ATTRS_VAL["linestyles"],
- "class": 1,
- "thickness": 0,
- "color": ATTRS_VAL["colors"],
- "textcolor": ATTRS_VAL["colors"],
- "arrowcolor": ATTRS_VAL["colors"],
- "backarrowcolor": ATTRS_VAL["colors"],
- "arrowsize": 1,
- "backarrowsize": 1,
- "arrowstyle": ATTRS_VAL["arrowstyles"],
- "backarrowstyle": ATTRS_VAL["arrowstyles"],
- "textmode": ATTRS_VAL["textmodes"],
- "priority": 1,
- "anchor": 1,
- "horizontal_order": 1,
-}
-SHAPES: dict[NodeType, str] = {
- NodeType.PACKAGE: "box",
- NodeType.CLASS: "box",
- NodeType.INTERFACE: "ellipse",
-}
-# pylint: disable-next=consider-using-namedtuple-or-dataclass
-ARROWS: dict[EdgeType, dict[str, str | int]] = {
- EdgeType.USES: {
- "arrowstyle": "solid",
- "backarrowstyle": "none",
- "backarrowsize": 0,
- },
- EdgeType.INHERITS: {
- "arrowstyle": "solid",
- "backarrowstyle": "none",
- "backarrowsize": 10,
- },
- EdgeType.IMPLEMENTS: {
- "arrowstyle": "solid",
- "backarrowstyle": "none",
- "linestyle": "dotted",
- "backarrowsize": 10,
- },
- EdgeType.ASSOCIATION: {
- "arrowstyle": "solid",
- "backarrowstyle": "none",
- "textcolor": "green",
- },
- EdgeType.AGGREGATION: {
- "arrowstyle": "solid",
- "backarrowstyle": "none",
- "textcolor": "green",
- },
-}
-ORIENTATION: dict[Layout, str] = {
- Layout.LEFT_TO_RIGHT: "left_to_right",
- Layout.RIGHT_TO_LEFT: "right_to_left",
- Layout.TOP_TO_BOTTOM: "top_to_bottom",
- Layout.BOTTOM_TO_TOP: "bottom_to_top",
-}
-
-# Misc utilities ###############################################################
-
-
-class VCGPrinter(Printer):
- def _open_graph(self) -> None:
- """Emit the header lines."""
- self.emit("graph:{\n")
- self._inc_indent()
- self._write_attributes(
- GRAPH_ATTRS,
- title=self.title,
- layoutalgorithm="dfs",
- late_edge_labels="yes",
- port_sharing="no",
- manhattan_edges="yes",
- )
- if self.layout:
- self._write_attributes(GRAPH_ATTRS, orientation=ORIENTATION[self.layout])
-
- def _close_graph(self) -> None:
- """Emit the lines needed to properly close the graph."""
- self._dec_indent()
- self.emit("}")
-
- def emit_node(
- self,
- name: str,
- type_: NodeType,
- properties: NodeProperties | None = None,
- ) -> None:
- """Create a new node.
-
- Nodes can be classes, packages, participants etc.
- """
- if properties is None:
- properties = NodeProperties(label=name)
- elif properties.label is None:
- properties.label = name
- self.emit(f'node: {{title:"{name}"', force_newline=False)
- self._write_attributes(
- NODE_ATTRS,
- label=self._build_label_for_node(properties),
- shape=SHAPES[type_],
- )
- self.emit("}")
-
- @staticmethod
- def _build_label_for_node(properties: NodeProperties) -> str:
- fontcolor = "\f09" if properties.fontcolor == "red" else ""
- label = rf"\fb{fontcolor}{properties.label}\fn"
- if properties.attrs is None and properties.methods is None:
- # return a compact form which only displays the classname in a box
- return label
- attrs = properties.attrs or []
- methods = properties.methods or []
- method_names = [func.name for func in methods]
- # box width for UML like diagram
- maxlen = max(len(name) for name in [properties.label] + method_names + attrs)
- line = "_" * (maxlen + 2)
- label = rf"{label}\n\f{line}"
- for attr in attrs:
- label = rf"{label}\n\f08{attr}"
- if attrs:
- label = rf"{label}\n\f{line}"
- for func in method_names:
- label = rf"{label}\n\f10{func}()"
- return label
-
- def emit_edge(
- self,
- from_node: str,
- to_node: str,
- type_: EdgeType,
- label: str | None = None,
- ) -> None:
- """Create an edge from one node to another to display relationships."""
- self.emit(
- f'edge: {{sourcename:"{from_node}" targetname:"{to_node}"',
- force_newline=False,
- )
- attributes = ARROWS[type_]
- if label:
- attributes["label"] = label
- self._write_attributes(
- EDGE_ATTRS,
- **attributes,
- )
- self.emit("}")
-
- def _write_attributes(
- self, attributes_dict: Mapping[str, Any], **args: Any
- ) -> None:
- """Write graph, node or edge attributes."""
- for key, value in args.items():
- try:
- _type = attributes_dict[key]
- except KeyError as e:
- raise AttributeError(
- f"no such attribute {key}\npossible attributes are {attributes_dict.keys()}"
- ) from e
-
- if not _type:
- self.emit(f'{key}:"{value}"\n')
- elif _type == 1:
- self.emit(f"{key}:{int(value)}\n")
- elif value in _type:
- self.emit(f"{key}:{value}\n")
- else:
- raise ValueError(
- f"value {value} isn't correct for attribute {key} correct values are {type}"
- )
diff --git a/pylint/pyreverse/writer.py b/pylint/pyreverse/writer.py
index d92f4e2e5..58e967115 100644
--- a/pylint/pyreverse/writer.py
+++ b/pylint/pyreverse/writer.py
@@ -1,8 +1,8 @@
# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
-# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
-# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+# For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
-"""Utilities for creating VCG and Dot diagrams."""
+"""Utilities for creating diagrams."""
from __future__ import annotations
@@ -57,6 +57,12 @@ class DiagramWriter:
# sorted to get predictable (hence testable) results
for module in sorted(diagram.modules(), key=lambda x: x.title):
module.fig_id = module.node.qname()
+ if self.config.no_standalone and not any(
+ module in (rel.from_object, rel.to_object)
+ for rel in diagram.get_relationships("depends")
+ ):
+ continue
+
self.printer.emit_node(
module.fig_id,
type_=NodeType.PACKAGE,
@@ -75,9 +81,17 @@ class DiagramWriter:
# sorted to get predictable (hence testable) results
for obj in sorted(diagram.objects, key=lambda x: x.title): # type: ignore[no-any-return]
obj.fig_id = obj.node.qname()
- type_ = NodeType.INTERFACE if obj.shape == "interface" else NodeType.CLASS
+ if self.config.no_standalone and not any(
+ obj in (rel.from_object, rel.to_object)
+ for rel_type in ("specialization", "association", "aggregation")
+ for rel in diagram.get_relationships(rel_type)
+ ):
+ continue
+
self.printer.emit_node(
- obj.fig_id, type_=type_, properties=self.get_class_properties(obj)
+ obj.fig_id,
+ type_=NodeType.CLASS,
+ properties=self.get_class_properties(obj),
)
# inheritance links
for rel in diagram.get_relationships("specialization"):
@@ -86,13 +100,6 @@ class DiagramWriter:
rel.to_object.fig_id,
type_=EdgeType.INHERITS,
)
- # implementation links
- for rel in diagram.get_relationships("implements"):
- self.printer.emit_edge(
- rel.from_object.fig_id,
- rel.to_object.fig_id,
- type_=EdgeType.IMPLEMENTS,
- )
# generate associations
for rel in diagram.get_relationships("association"):
self.printer.emit_edge(