Coverage for src / rhiza / models.py: 100%

31 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2025-12-29 01:59 +0000

1"""Data models for Rhiza configuration. 

2 

3This module defines dataclasses that represent the structure of Rhiza 

4configuration files, making it easier to work with them without frequent 

5YAML parsing. 

6""" 

7 

8from dataclasses import dataclass, field 

9from pathlib import Path 

10 

11import yaml 

12 

13 

14@dataclass 

15class RhizaTemplate: 

16 """Represents the structure of .github/rhiza/template.yml. 

17 

18 Attributes: 

19 template_repository: The GitHub or GitLab repository containing templates (e.g., "jebel-quant/rhiza"). 

20 Can be None if not specified in the template file. 

21 template_branch: The branch to use from the template repository. 

22 Can be None if not specified in the template file (defaults to "main" when creating). 

23 template_host: The git hosting platform ("github" or "gitlab"). 

24 Defaults to "github" if not specified in the template file. 

25 include: List of paths to include from the template repository. 

26 exclude: List of paths to exclude from the template repository (default: empty list). 

27 """ 

28 

29 template_repository: str | None = None 

30 template_branch: str | None = None 

31 template_host: str = "github" 

32 include: list[str] = field(default_factory=list) 

33 exclude: list[str] = field(default_factory=list) 

34 

35 @classmethod 

36 def from_yaml(cls, file_path: Path) -> "RhizaTemplate": 

37 """Load RhizaTemplate from a YAML file. 

38 

39 Args: 

40 file_path: Path to the template.yml file. 

41 

42 Returns: 

43 The loaded template configuration. 

44 

45 Raises: 

46 FileNotFoundError: If the file does not exist. 

47 yaml.YAMLError: If the YAML is malformed. 

48 ValueError: If the file is empty. 

49 """ 

50 with open(file_path) as f: 

51 config = yaml.safe_load(f) 

52 

53 if not config: 

54 raise ValueError("Template file is empty") 

55 

56 return cls( 

57 template_repository=config.get("template-repository"), 

58 template_branch=config.get("template-branch"), 

59 template_host=config.get("template-host", "github"), 

60 include=config.get("include", []), 

61 exclude=config.get("exclude", []), 

62 ) 

63 

64 def to_yaml(self, file_path: Path) -> None: 

65 """Save RhizaTemplate to a YAML file. 

66 

67 Args: 

68 file_path: Path where the template.yml file should be saved. 

69 """ 

70 # Ensure parent directory exists 

71 file_path.parent.mkdir(parents=True, exist_ok=True) 

72 

73 # Convert to dictionary with YAML-compatible keys 

74 config = {} 

75 

76 # Only include template-repository if it's not None 

77 if self.template_repository: 

78 config["template-repository"] = self.template_repository 

79 

80 # Only include template-branch if it's not None 

81 if self.template_branch: 

82 config["template-branch"] = self.template_branch 

83 

84 # Only include template-host if it's not the default "github" 

85 if self.template_host and self.template_host != "github": 

86 config["template-host"] = self.template_host 

87 

88 # Include is always present as it's a required field for the config to be useful 

89 config["include"] = self.include 

90 

91 # Only include exclude if it's not empty 

92 if self.exclude: 

93 config["exclude"] = self.exclude 

94 

95 with open(file_path, "w") as f: 

96 yaml.dump(config, f, default_flow_style=False, sort_keys=False)