pyreverse.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. # Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  2. # For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
  3. # Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
  4. from __future__ import annotations
  5. import argparse
  6. import configparser
  7. import shlex
  8. from pathlib import Path
  9. from typing import NamedTuple, TypedDict
  10. from pylint.pyreverse.main import DEFAULT_COLOR_PALETTE
  11. # This class could and should be replaced with a simple dataclass when support for Python < 3.7 is dropped.
  12. # A NamedTuple is not possible as some tests need to modify attributes during the test.
  13. class PyreverseConfig(
  14. argparse.Namespace
  15. ): # pylint: disable=too-many-instance-attributes, too-many-arguments
  16. """Holds the configuration options for Pyreverse.
  17. The default values correspond to the defaults of the options' parser.
  18. """
  19. # pylint: disable=too-many-locals
  20. def __init__(
  21. self,
  22. *,
  23. mode: str = "PUB_ONLY",
  24. classes: list[str] | None = None,
  25. show_ancestors: int | None = None,
  26. all_ancestors: bool | None = None,
  27. show_associated: int | None = None,
  28. all_associated: bool | None = None,
  29. no_standalone: bool = False,
  30. show_builtin: bool = False,
  31. show_stdlib: bool = False,
  32. module_names: bool | None = None,
  33. only_classnames: bool = False,
  34. output_format: str = "dot",
  35. colorized: bool = False,
  36. max_color_depth: int = 2,
  37. max_depth: int | None = None,
  38. color_palette: tuple[str, ...] = DEFAULT_COLOR_PALETTE,
  39. ignore_list: tuple[str, ...] = tuple(),
  40. project: str = "",
  41. output_directory: str = "",
  42. ) -> None:
  43. super().__init__()
  44. self.mode = mode
  45. if classes:
  46. self.classes = classes
  47. else:
  48. self.classes = []
  49. self.show_ancestors = show_ancestors
  50. self.all_ancestors = all_ancestors
  51. self.show_associated = show_associated
  52. self.all_associated = all_associated
  53. self.no_standalone = no_standalone
  54. self.show_builtin = show_builtin
  55. self.show_stdlib = show_stdlib
  56. self.module_names = module_names
  57. self.only_classnames = only_classnames
  58. self.output_format = output_format
  59. self.colorized = colorized
  60. self.max_depth = max_depth
  61. self.max_color_depth = max_color_depth
  62. self.color_palette = color_palette
  63. self.ignore_list = ignore_list
  64. self.project = project
  65. self.output_directory = output_directory
  66. # pylint: enable=too-many-locals
  67. class TestFileOptions(TypedDict):
  68. source_roots: list[str]
  69. output_formats: list[str]
  70. command_line_args: list[str]
  71. class FunctionalPyreverseTestfile(NamedTuple):
  72. """Named tuple containing the test file and the expected output."""
  73. source: Path
  74. options: TestFileOptions
  75. def get_functional_test_files(
  76. root_directory: Path,
  77. ) -> list[FunctionalPyreverseTestfile]:
  78. """Get all functional test files from the given directory."""
  79. test_files = []
  80. for path in root_directory.rglob("*.py"):
  81. if path.stem.startswith("_"):
  82. continue
  83. config_file = path.with_suffix(".rc")
  84. if config_file.exists():
  85. test_files.append(
  86. FunctionalPyreverseTestfile(
  87. source=path, options=_read_config(config_file)
  88. )
  89. )
  90. else:
  91. test_files.append(
  92. FunctionalPyreverseTestfile(
  93. source=path,
  94. options={
  95. "source_roots": [],
  96. "output_formats": ["mmd"],
  97. "command_line_args": [],
  98. },
  99. )
  100. )
  101. return test_files
  102. def _read_config(config_file: Path) -> TestFileOptions:
  103. config = configparser.ConfigParser()
  104. config.read(str(config_file))
  105. source_roots = config.get("testoptions", "source_roots", fallback=None)
  106. return {
  107. "source_roots": source_roots.split(",") if source_roots else [],
  108. "output_formats": config.get(
  109. "testoptions", "output_formats", fallback="mmd"
  110. ).split(","),
  111. "command_line_args": shlex.split(
  112. config.get("testoptions", "command_line_args", fallback="")
  113. ),
  114. }