Coverage for src/domain/validation/preset_registry.py: 100%

1"""Preset registry for built-in validation configuration presets.

2

3 This module provides the PresetRegistry class for loading and managing built-in

4 validation configuration presets. Presets are YAML files bundled with the package

5 and discovered via importlib.resources.

6

7 Key types:

8- PresetRegistry: Class with get(), list_presets() methods for preset access

9"""

10

11 from __future__ import annotations

12

13 from importlib import resources

14 from typing import TYPE_CHECKING, Any, ClassVar

15

16 import yaml

17

18 from src.domain.validation.config import PresetNotFoundError

19 from src.domain.validation.config_loader import _build_config

20

21 if TYPE_CHECKING:

22 from src.domain.validation.config import ValidationConfig

23

24

25 class PresetRegistry:

26 """Registry for built-in validation configuration presets.

27

28 Presets are YAML files stored in the src/domain/validation/presets/ package

29 and discovered via importlib.resources for wheel compatibility.

30

31 Example:

32 >>> registry = PresetRegistry()

33 >>> config = registry.get("python-uv")

34 >>> print(config.commands.test.command)

35 'uv run pytest'

36 """

37

38 # Package containing preset YAML files

39 _PRESETS_PACKAGE: ClassVar[str] = "src.domain.validation.presets"

40

41 # Mapping of preset names to YAML filenames

42 _PRESET_FILES: ClassVar[dict[str, str]] = {

43 "go": "go.yaml",

44 "node-npm": "node-npm.yaml",

45 "python-uv": "python-uv.yaml",

46 "rust": "rust.yaml",

47 }

48

49 def get(self, name: str) -> ValidationConfig:

50 """Load and return a preset configuration by name.

51

52 Args:

53 name: Name of the preset (e.g., "python-uv", "go").

54

55 Returns:

56 ValidationConfig instance with preset values.

57

58 Raises:

59 PresetNotFoundError: If the preset name is not recognized.

60

61 Example:

62 >>> registry = PresetRegistry()

63 >>> config = registry.get("go")

64 >>> print(config.commands.test.command)

65 'go test ./...'

66 """

67 if name not in self._PRESET_FILES:

68 raise PresetNotFoundError(name, self.list_presets())

69

70 data = self._load_preset_yaml(name)

71 return _build_config(data)

72

73 def list_presets(self) -> list[str]:

74 """Return a sorted list of available preset names.

75

76 Returns:

77 List of preset names in alphabetical order.

78

79 Example:

80 >>> registry = PresetRegistry()

81 >>> registry.list_presets()

82 ['go', 'node-npm', 'python-uv', 'rust']

83 """

84 return sorted(self._PRESET_FILES.keys())

85

86 def _load_preset_yaml(self, name: str) -> dict[str, Any]:

87 """Load and parse a preset YAML file.

88

89 Args:

90 name: Name of the preset.

91

92 Returns:

93 Parsed YAML dictionary.

94

95 Raises:

96 PresetNotFoundError: If the preset file cannot be loaded.

97 """

98 filename = self._PRESET_FILES[name]

99 try:

100 package_files = resources.files(self._PRESETS_PACKAGE)

101 preset_file = package_files.joinpath(filename)

102 content = preset_file.read_text(encoding="utf-8")

103 data = yaml.safe_load(content)

104 return data if data is not None else {}

105 except (ModuleNotFoundError, FileNotFoundError, TypeError) as e:

106 raise PresetNotFoundError(name, self.list_presets()) from e