From c6452cdd0889b447322d1725feb9d96be37bb902 Mon Sep 17 00:00:00 2001 From: HannesDrescher Date: Tue, 4 Jun 2024 15:31:02 +0200 Subject: [PATCH 1/2] tests: add testcase that shows that pyreverse will not extract the inheritance link for a flat folder as described in https://github.com/pylint-dev/pylint/issues/7686 --- pylint/testutils/pyreverse.py | 29 ++++++++++++++ .../inheritance/complex_inheritance.mmd | 11 ++++++ .../inheritance/complex_inheritance.py | 24 ++++++++++++ .../modules/complex_inheritance/__init__.py | 0 .../modules/complex_inheritance/child.py | 9 +++++ .../complex_inheritance.mmd | 6 +++ .../complex_inheritance.rc | 2 + .../modules/complex_inheritance/parent.py | 7 ++++ tests/pyreverse/test_pyreverse_functional.py | 39 +++++++++++++++++++ 9 files changed, 127 insertions(+) create mode 100644 tests/pyreverse/functional/class_diagrams/inheritance/complex_inheritance.mmd create mode 100644 tests/pyreverse/functional/class_diagrams/inheritance/complex_inheritance.py create mode 100644 tests/pyreverse/functional/modules/complex_inheritance/__init__.py create mode 100644 tests/pyreverse/functional/modules/complex_inheritance/child.py create mode 100644 tests/pyreverse/functional/modules/complex_inheritance/complex_inheritance.mmd create mode 100644 tests/pyreverse/functional/modules/complex_inheritance/complex_inheritance.rc create mode 100644 tests/pyreverse/functional/modules/complex_inheritance/parent.py diff --git a/pylint/testutils/pyreverse.py b/pylint/testutils/pyreverse.py index c621f9e7a9..fe05acc9cf 100644 --- a/pylint/testutils/pyreverse.py +++ b/pylint/testutils/pyreverse.py @@ -110,6 +110,35 @@ def get_functional_test_files( return test_files +def get_functional_test_modules( + root_directory: Path, +) -> list[FunctionalPyreverseTestfile]: + """Get all functional test files from the given directory.""" + test_files = [] + for path in root_directory.rglob("__init__.py"): + module_directory = path.parent + module_name = module_directory.name + config_file = module_directory / f"{module_name}.rc" + if config_file.exists(): + test_files.append( + FunctionalPyreverseTestfile( + source=module_directory, options=_read_config(config_file) + ) + ) + else: + test_files.append( + FunctionalPyreverseTestfile( + source=module_directory, + options={ + "source_roots": [], + "output_formats": ["mmd"], + "command_line_args": [], + }, + ) + ) + return test_files + + def _read_config(config_file: Path) -> TestFileOptions: config = configparser.ConfigParser() config.read(str(config_file)) diff --git a/tests/pyreverse/functional/class_diagrams/inheritance/complex_inheritance.mmd b/tests/pyreverse/functional/class_diagrams/inheritance/complex_inheritance.mmd new file mode 100644 index 0000000000..9b48a3d31f --- /dev/null +++ b/tests/pyreverse/functional/class_diagrams/inheritance/complex_inheritance.mmd @@ -0,0 +1,11 @@ +classDiagram + class AbstractParent { + } + class Child { + } + class ConcreteChild { + } + class GenericParent { + } + Child --|> AbstractParent + ConcreteChild --|> GenericParent diff --git a/tests/pyreverse/functional/class_diagrams/inheritance/complex_inheritance.py b/tests/pyreverse/functional/class_diagrams/inheritance/complex_inheritance.py new file mode 100644 index 0000000000..94fab0fd33 --- /dev/null +++ b/tests/pyreverse/functional/class_diagrams/inheritance/complex_inheritance.py @@ -0,0 +1,24 @@ +from abc import ABC +from dataclasses import dataclass +from typing import Generic, TypeVar + + +@dataclass +class AbstractParent(ABC): + """parent class""" + + +@dataclass +class Child(AbstractParent): + """child class""" + + +GenericType = TypeVar("GenericType") + + +class GenericParent(Generic[GenericType]): + """parent class""" + + +class ConcreteChild(GenericParent[int]): + """child class""" diff --git a/tests/pyreverse/functional/modules/complex_inheritance/__init__.py b/tests/pyreverse/functional/modules/complex_inheritance/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/pyreverse/functional/modules/complex_inheritance/child.py b/tests/pyreverse/functional/modules/complex_inheritance/child.py new file mode 100644 index 0000000000..7a676afda7 --- /dev/null +++ b/tests/pyreverse/functional/modules/complex_inheritance/child.py @@ -0,0 +1,9 @@ +from dataclasses import dataclass + +from parent import AbstractParent + + +@dataclass +class Child(AbstractParent): + """child class""" + pass diff --git a/tests/pyreverse/functional/modules/complex_inheritance/complex_inheritance.mmd b/tests/pyreverse/functional/modules/complex_inheritance/complex_inheritance.mmd new file mode 100644 index 0000000000..bc505a79ba --- /dev/null +++ b/tests/pyreverse/functional/modules/complex_inheritance/complex_inheritance.mmd @@ -0,0 +1,6 @@ +classDiagram + class Child { + } + class AbstractParent { + } + Child --|> AbstractParent diff --git a/tests/pyreverse/functional/modules/complex_inheritance/complex_inheritance.rc b/tests/pyreverse/functional/modules/complex_inheritance/complex_inheritance.rc new file mode 100644 index 0000000000..3af8d24084 --- /dev/null +++ b/tests/pyreverse/functional/modules/complex_inheritance/complex_inheritance.rc @@ -0,0 +1,2 @@ +[testoptions] +command_line_args=-ASmy diff --git a/tests/pyreverse/functional/modules/complex_inheritance/parent.py b/tests/pyreverse/functional/modules/complex_inheritance/parent.py new file mode 100644 index 0000000000..301fbd2030 --- /dev/null +++ b/tests/pyreverse/functional/modules/complex_inheritance/parent.py @@ -0,0 +1,7 @@ +from abc import ABC +from dataclasses import dataclass + + +@dataclass +class AbstractParent(ABC): + """parent class""" diff --git a/tests/pyreverse/test_pyreverse_functional.py b/tests/pyreverse/test_pyreverse_functional.py index 715ad3dada..d4d97a8055 100644 --- a/tests/pyreverse/test_pyreverse_functional.py +++ b/tests/pyreverse/test_pyreverse_functional.py @@ -10,12 +10,18 @@ from pylint.testutils.pyreverse import ( FunctionalPyreverseTestfile, get_functional_test_files, + get_functional_test_modules, ) FUNCTIONAL_DIR = Path(__file__).parent / "functional" CLASS_DIAGRAMS_DIR = FUNCTIONAL_DIR / "class_diagrams" CLASS_DIAGRAM_TESTS = get_functional_test_files(CLASS_DIAGRAMS_DIR) CLASS_DIAGRAM_TEST_IDS = [testfile.source.stem for testfile in CLASS_DIAGRAM_TESTS] +MODULE_DIAGRAMS_DIR = FUNCTIONAL_DIR / "modules" +MODULE_DIAGRAM_TESTS = get_functional_test_modules(MODULE_DIAGRAMS_DIR) +MODULE_DIAGRAM_TEST_IDS = [ + testmodule.source.name for testmodule in MODULE_DIAGRAM_TESTS +] @pytest.mark.parametrize( @@ -49,3 +55,36 @@ def test_class_diagrams(testfile: FunctionalPyreverseTestfile, tmp_path: Path) - assert testfile.source.with_suffix(f".{output_format}").read_text( encoding="utf8" ) == (tmp_path / f"classes.{output_format}").read_text(encoding="utf8") + + +@pytest.mark.parametrize( + "testmodule", + MODULE_DIAGRAM_TESTS, + ids=MODULE_DIAGRAM_TEST_IDS, +) +def test_modules(testmodule: FunctionalPyreverseTestfile, tmp_path: Path) -> None: + input_path = testmodule.source + if testmodule.options["source_roots"]: + source_roots = ",".join( + [ + os.path.realpath( + os.path.expanduser(os.path.join(input_path, source_root)) + ) + for source_root in testmodule.options["source_roots"] + ] + ) + else: + source_roots = "" + for output_format in testmodule.options["output_formats"]: + output_file = input_path / f"{input_path.name}.{output_format}" + with pytest.raises(SystemExit) as sys_exit: + args = ["-o", f"{output_format}", "-d", str(tmp_path)] + if source_roots: + args += ["--source-roots", source_roots] + args.extend(testmodule.options["command_line_args"]) + args += [str(input_path)] + Run(args) + assert sys_exit.value.code == 0 + assert output_file.read_text(encoding="utf8") == ( + tmp_path / f"classes.{output_format}" + ).read_text(encoding="utf8") From d99f984d071c62dad5203a88b32f1c17c2894e69 Mon Sep 17 00:00:00 2001 From: HannesDrescher Date: Sat, 15 Jun 2024 18:53:14 +0200 Subject: [PATCH 2/2] change: rename all occurrences of module to package and treat every subdirectory as an own testcase --- pylint/testutils/pyreverse.py | 15 +++++----- .../complex_inheritance/__init__.py | 0 .../complex_inheritance/child.py | 0 .../complex_inheritance.mmd | 0 .../complex_inheritance.rc | 0 .../complex_inheritance/parent.py | 0 tests/pyreverse/test_pyreverse_functional.py | 28 +++++++++---------- 7 files changed, 21 insertions(+), 22 deletions(-) rename tests/pyreverse/functional/{modules => packages}/complex_inheritance/__init__.py (100%) rename tests/pyreverse/functional/{modules => packages}/complex_inheritance/child.py (100%) rename tests/pyreverse/functional/{modules => packages}/complex_inheritance/complex_inheritance.mmd (100%) rename tests/pyreverse/functional/{modules => packages}/complex_inheritance/complex_inheritance.rc (100%) rename tests/pyreverse/functional/{modules => packages}/complex_inheritance/parent.py (100%) diff --git a/pylint/testutils/pyreverse.py b/pylint/testutils/pyreverse.py index fe05acc9cf..9e66471831 100644 --- a/pylint/testutils/pyreverse.py +++ b/pylint/testutils/pyreverse.py @@ -110,25 +110,24 @@ def get_functional_test_files( return test_files -def get_functional_test_modules( +def get_functional_test_packages( root_directory: Path, ) -> list[FunctionalPyreverseTestfile]: - """Get all functional test files from the given directory.""" + """Treat every subdirectory as a package and get all functional test files.""" test_files = [] - for path in root_directory.rglob("__init__.py"): - module_directory = path.parent - module_name = module_directory.name - config_file = module_directory / f"{module_name}.rc" + for package_directory in root_directory.iterdir(): + package_name = package_directory.name + config_file = package_directory / f"{package_name}.rc" if config_file.exists(): test_files.append( FunctionalPyreverseTestfile( - source=module_directory, options=_read_config(config_file) + source=package_directory, options=_read_config(config_file) ) ) else: test_files.append( FunctionalPyreverseTestfile( - source=module_directory, + source=package_directory, options={ "source_roots": [], "output_formats": ["mmd"], diff --git a/tests/pyreverse/functional/modules/complex_inheritance/__init__.py b/tests/pyreverse/functional/packages/complex_inheritance/__init__.py similarity index 100% rename from tests/pyreverse/functional/modules/complex_inheritance/__init__.py rename to tests/pyreverse/functional/packages/complex_inheritance/__init__.py diff --git a/tests/pyreverse/functional/modules/complex_inheritance/child.py b/tests/pyreverse/functional/packages/complex_inheritance/child.py similarity index 100% rename from tests/pyreverse/functional/modules/complex_inheritance/child.py rename to tests/pyreverse/functional/packages/complex_inheritance/child.py diff --git a/tests/pyreverse/functional/modules/complex_inheritance/complex_inheritance.mmd b/tests/pyreverse/functional/packages/complex_inheritance/complex_inheritance.mmd similarity index 100% rename from tests/pyreverse/functional/modules/complex_inheritance/complex_inheritance.mmd rename to tests/pyreverse/functional/packages/complex_inheritance/complex_inheritance.mmd diff --git a/tests/pyreverse/functional/modules/complex_inheritance/complex_inheritance.rc b/tests/pyreverse/functional/packages/complex_inheritance/complex_inheritance.rc similarity index 100% rename from tests/pyreverse/functional/modules/complex_inheritance/complex_inheritance.rc rename to tests/pyreverse/functional/packages/complex_inheritance/complex_inheritance.rc diff --git a/tests/pyreverse/functional/modules/complex_inheritance/parent.py b/tests/pyreverse/functional/packages/complex_inheritance/parent.py similarity index 100% rename from tests/pyreverse/functional/modules/complex_inheritance/parent.py rename to tests/pyreverse/functional/packages/complex_inheritance/parent.py diff --git a/tests/pyreverse/test_pyreverse_functional.py b/tests/pyreverse/test_pyreverse_functional.py index d4d97a8055..beeb0330be 100644 --- a/tests/pyreverse/test_pyreverse_functional.py +++ b/tests/pyreverse/test_pyreverse_functional.py @@ -10,17 +10,17 @@ from pylint.testutils.pyreverse import ( FunctionalPyreverseTestfile, get_functional_test_files, - get_functional_test_modules, + get_functional_test_packages, ) FUNCTIONAL_DIR = Path(__file__).parent / "functional" CLASS_DIAGRAMS_DIR = FUNCTIONAL_DIR / "class_diagrams" CLASS_DIAGRAM_TESTS = get_functional_test_files(CLASS_DIAGRAMS_DIR) CLASS_DIAGRAM_TEST_IDS = [testfile.source.stem for testfile in CLASS_DIAGRAM_TESTS] -MODULE_DIAGRAMS_DIR = FUNCTIONAL_DIR / "modules" -MODULE_DIAGRAM_TESTS = get_functional_test_modules(MODULE_DIAGRAMS_DIR) -MODULE_DIAGRAM_TEST_IDS = [ - testmodule.source.name for testmodule in MODULE_DIAGRAM_TESTS +PACKAGE_DIAGRAMS_DIR = FUNCTIONAL_DIR / "packages" +PACKAGE_DIAGRAM_TESTS = get_functional_test_packages(PACKAGE_DIAGRAMS_DIR) +PACKAGE_DIAGRAM_TEST_IDS = [ + test_package.source.name for test_package in PACKAGE_DIAGRAM_TESTS ] @@ -58,30 +58,30 @@ def test_class_diagrams(testfile: FunctionalPyreverseTestfile, tmp_path: Path) - @pytest.mark.parametrize( - "testmodule", - MODULE_DIAGRAM_TESTS, - ids=MODULE_DIAGRAM_TEST_IDS, + "test_package", + PACKAGE_DIAGRAM_TESTS, + ids=PACKAGE_DIAGRAM_TEST_IDS, ) -def test_modules(testmodule: FunctionalPyreverseTestfile, tmp_path: Path) -> None: - input_path = testmodule.source - if testmodule.options["source_roots"]: +def test_packages(test_package: FunctionalPyreverseTestfile, tmp_path: Path) -> None: + input_path = test_package.source + if test_package.options["source_roots"]: source_roots = ",".join( [ os.path.realpath( os.path.expanduser(os.path.join(input_path, source_root)) ) - for source_root in testmodule.options["source_roots"] + for source_root in test_package.options["source_roots"] ] ) else: source_roots = "" - for output_format in testmodule.options["output_formats"]: + for output_format in test_package.options["output_formats"]: output_file = input_path / f"{input_path.name}.{output_format}" with pytest.raises(SystemExit) as sys_exit: args = ["-o", f"{output_format}", "-d", str(tmp_path)] if source_roots: args += ["--source-roots", source_roots] - args.extend(testmodule.options["command_line_args"]) + args.extend(test_package.options["command_line_args"]) args += [str(input_path)] Run(args) assert sys_exit.value.code == 0