diff options
author | Nick Drozd <nicholasdrozd@gmail.com> | 2023-04-02 05:41:26 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-04-02 11:41:26 +0200 |
commit | 3b42318eddaf0fded622179b19c7bcfcead26893 (patch) | |
tree | 502297ad801e6f05d56daff86500c74ba0ee8036 | |
parent | f7bd67604f395cd50734b9559acc37ae5d34ce4b (diff) | |
download | pylint-git-3b42318eddaf0fded622179b19c7bcfcead26893.tar.gz |
Add Pyreverse option to exclude standalone nodes (#8520)
* Add Pyreverse option to exclude standalone nodes
* Add test
* Add package test
* Fix test names
* Clean up test files
-rw-r--r-- | doc/whatsnew/fragments/8476.feature | 3 | ||||
-rw-r--r-- | pylint/pyreverse/main.py | 8 | ||||
-rw-r--r-- | pylint/pyreverse/writer.py | 13 | ||||
-rw-r--r-- | pylint/testutils/pyreverse.py | 2 | ||||
-rw-r--r-- | tests/pyreverse/conftest.py | 8 | ||||
-rw-r--r-- | tests/pyreverse/data/classes_no_standalone.dot | 12 | ||||
-rw-r--r-- | tests/pyreverse/data/packages_no_standalone.dot | 7 | ||||
-rw-r--r-- | tests/pyreverse/functional/class_diagrams/inheritance/no_standalone.mmd | 6 | ||||
-rw-r--r-- | tests/pyreverse/functional/class_diagrams/inheritance/no_standalone.py | 5 | ||||
-rw-r--r-- | tests/pyreverse/functional/class_diagrams/inheritance/no_standalone.rc | 2 | ||||
-rw-r--r-- | tests/pyreverse/test_writer.py | 18 |
11 files changed, 84 insertions, 0 deletions
diff --git a/doc/whatsnew/fragments/8476.feature b/doc/whatsnew/fragments/8476.feature new file mode 100644 index 000000000..56c0be201 --- /dev/null +++ b/doc/whatsnew/fragments/8476.feature @@ -0,0 +1,3 @@ +Add Pyreverse option to exclude standalone nodes from diagrams with `--no-standalone`. + +Closes #8476 diff --git a/pylint/pyreverse/main.py b/pylint/pyreverse/main.py index 975e432e4..58128bb57 100644 --- a/pylint/pyreverse/main.py +++ b/pylint/pyreverse/main.py @@ -158,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/writer.py b/pylint/pyreverse/writer.py index cb711dd10..b5cab0a66 100644 --- a/pylint/pyreverse/writer.py +++ b/pylint/pyreverse/writer.py @@ -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,6 +81,13 @@ 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() + 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_=NodeType.CLASS, diff --git a/pylint/testutils/pyreverse.py b/pylint/testutils/pyreverse.py index ba79ebf8b..24fddad77 100644 --- a/pylint/testutils/pyreverse.py +++ b/pylint/testutils/pyreverse.py @@ -37,6 +37,7 @@ class PyreverseConfig( all_ancestors: bool | None = None, show_associated: int | None = None, all_associated: bool | None = None, + no_standalone: bool = False, show_builtin: bool = False, show_stdlib: bool = False, module_names: bool | None = None, @@ -59,6 +60,7 @@ class PyreverseConfig( self.all_ancestors = all_ancestors self.show_associated = show_associated self.all_associated = all_associated + self.no_standalone = no_standalone self.show_builtin = show_builtin self.show_stdlib = show_stdlib self.module_names = module_names diff --git a/tests/pyreverse/conftest.py b/tests/pyreverse/conftest.py index b281c5bee..9e1741f0a 100644 --- a/tests/pyreverse/conftest.py +++ b/tests/pyreverse/conftest.py @@ -29,6 +29,14 @@ def colorized_dot_config() -> PyreverseConfig: @pytest.fixture() +def no_standalone_dot_config() -> PyreverseConfig: + return PyreverseConfig( + output_format="dot", + no_standalone=True, + ) + + +@pytest.fixture() def puml_config() -> PyreverseConfig: return PyreverseConfig( output_format="puml", diff --git a/tests/pyreverse/data/classes_no_standalone.dot b/tests/pyreverse/data/classes_no_standalone.dot new file mode 100644 index 000000000..7cffc0a38 --- /dev/null +++ b/tests/pyreverse/data/classes_no_standalone.dot @@ -0,0 +1,12 @@ +digraph "classes_no_standalone" { +rankdir=BT +charset="utf-8" +"data.clientmodule_test.Ancestor" [color="black", fontcolor="black", label=<{Ancestor|attr : str<br ALIGN="LEFT"/>cls_member<br ALIGN="LEFT"/>|get_value()<br ALIGN="LEFT"/>set_value(value)<br ALIGN="LEFT"/>}>, shape="record", style="solid"]; +"data.suppliermodule_test.DoNothing" [color="black", fontcolor="black", label=<{DoNothing|<br ALIGN="LEFT"/>|}>, shape="record", style="solid"]; +"data.suppliermodule_test.DoNothing2" [color="black", fontcolor="black", label=<{DoNothing2|<br ALIGN="LEFT"/>|}>, shape="record", style="solid"]; +"data.clientmodule_test.Specialization" [color="black", fontcolor="black", label=<{Specialization|TYPE : str<br ALIGN="LEFT"/>relation<br ALIGN="LEFT"/>relation2<br ALIGN="LEFT"/>top : str<br ALIGN="LEFT"/>|from_value(value: int)<br ALIGN="LEFT"/>increment_value(): None<br ALIGN="LEFT"/>transform_value(value: int): int<br ALIGN="LEFT"/>}>, shape="record", style="solid"]; +"data.clientmodule_test.Specialization" -> "data.clientmodule_test.Ancestor" [arrowhead="empty", arrowtail="none"]; +"data.suppliermodule_test.DoNothing" -> "data.clientmodule_test.Ancestor" [arrowhead="diamond", arrowtail="none", fontcolor="green", label="cls_member", style="solid"]; +"data.suppliermodule_test.DoNothing" -> "data.clientmodule_test.Specialization" [arrowhead="diamond", arrowtail="none", fontcolor="green", label="relation", style="solid"]; +"data.suppliermodule_test.DoNothing2" -> "data.clientmodule_test.Specialization" [arrowhead="odiamond", arrowtail="none", fontcolor="green", label="relation2", style="solid"]; +} diff --git a/tests/pyreverse/data/packages_no_standalone.dot b/tests/pyreverse/data/packages_no_standalone.dot new file mode 100644 index 000000000..008c9c5d0 --- /dev/null +++ b/tests/pyreverse/data/packages_no_standalone.dot @@ -0,0 +1,7 @@ +digraph "packages_no_standalone" { +rankdir=BT +charset="utf-8" +"data.clientmodule_test" [color="black", label=<data.clientmodule_test>, shape="box", style="solid"]; +"data.suppliermodule_test" [color="black", label=<data.suppliermodule_test>, shape="box", style="solid"]; +"data.clientmodule_test" -> "data.suppliermodule_test" [arrowhead="open", arrowtail="none"]; +} diff --git a/tests/pyreverse/functional/class_diagrams/inheritance/no_standalone.mmd b/tests/pyreverse/functional/class_diagrams/inheritance/no_standalone.mmd new file mode 100644 index 000000000..646d8220d --- /dev/null +++ b/tests/pyreverse/functional/class_diagrams/inheritance/no_standalone.mmd @@ -0,0 +1,6 @@ +classDiagram + class A { + } + class B { + } + B --|> A diff --git a/tests/pyreverse/functional/class_diagrams/inheritance/no_standalone.py b/tests/pyreverse/functional/class_diagrams/inheritance/no_standalone.py new file mode 100644 index 000000000..3d881d4c0 --- /dev/null +++ b/tests/pyreverse/functional/class_diagrams/inheritance/no_standalone.py @@ -0,0 +1,5 @@ +class A: pass + +class B(A): pass + +class C: pass diff --git a/tests/pyreverse/functional/class_diagrams/inheritance/no_standalone.rc b/tests/pyreverse/functional/class_diagrams/inheritance/no_standalone.rc new file mode 100644 index 000000000..c17e41cfb --- /dev/null +++ b/tests/pyreverse/functional/class_diagrams/inheritance/no_standalone.rc @@ -0,0 +1,2 @@ +[testoptions] +command_line_args=--no-standalone diff --git a/tests/pyreverse/test_writer.py b/tests/pyreverse/test_writer.py index 2897ca054..37a4b4f19 100644 --- a/tests/pyreverse/test_writer.py +++ b/tests/pyreverse/test_writer.py @@ -35,6 +35,7 @@ _DEFAULTS = { "show_stdlib": False, "only_classnames": False, "output_directory": "", + "no_standalone": False, } TEST_DATA_DIR = os.path.join(os.path.dirname(__file__), "..", "data") @@ -45,6 +46,7 @@ PUML_FILES = ["packages_No_Name.puml", "classes_No_Name.puml"] COLORIZED_PUML_FILES = ["packages_colorized.puml", "classes_colorized.puml"] MMD_FILES = ["packages_No_Name.mmd", "classes_No_Name.mmd"] HTML_FILES = ["packages_No_Name.html", "classes_No_Name.html"] +NO_STANDALONE_FILES = ["classes_no_standalone.dot", "packages_no_standalone.dot"] class Config: @@ -88,6 +90,15 @@ def setup_colorized_dot( @pytest.fixture() +def setup_no_standalone_dot( + no_standalone_dot_config: PyreverseConfig, get_project: GetProjectCallable +) -> Iterator[None]: + writer = DiagramWriter(no_standalone_dot_config) + project = get_project(TEST_DATA_DIR, name="no_standalone") + yield from _setup(project, no_standalone_dot_config, writer) + + +@pytest.fixture() def setup_puml( puml_config: PyreverseConfig, get_project: GetProjectCallable ) -> Iterator[None]: @@ -138,6 +149,7 @@ def _setup( for fname in ( DOT_FILES + COLORIZED_DOT_FILES + + NO_STANDALONE_FILES + PUML_FILES + COLORIZED_PUML_FILES + MMD_FILES @@ -161,6 +173,12 @@ def test_colorized_dot_files(generated_file: str) -> None: _assert_files_are_equal(generated_file) +@pytest.mark.usefixtures("setup_no_standalone_dot") +@pytest.mark.parametrize("generated_file", NO_STANDALONE_FILES) +def test_no_standalone_dot_files(generated_file: str) -> None: + _assert_files_are_equal(generated_file) + + @pytest.mark.usefixtures("setup_puml") @pytest.mark.parametrize("generated_file", PUML_FILES) def test_puml_files(generated_file: str) -> None: |