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
« prev ^ index » next coverage.py v7.13.1, created at 2025-12-29 01:59 +0000
1"""Data models for Rhiza configuration.
3This module defines dataclasses that represent the structure of Rhiza
4configuration files, making it easier to work with them without frequent
5YAML parsing.
6"""
8from dataclasses import dataclass, field
9from pathlib import Path
11import yaml
14@dataclass
15class RhizaTemplate:
16 """Represents the structure of .github/rhiza/template.yml.
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 """
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)
35 @classmethod
36 def from_yaml(cls, file_path: Path) -> "RhizaTemplate":
37 """Load RhizaTemplate from a YAML file.
39 Args:
40 file_path: Path to the template.yml file.
42 Returns:
43 The loaded template configuration.
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)
53 if not config:
54 raise ValueError("Template file is empty")
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 )
64 def to_yaml(self, file_path: Path) -> None:
65 """Save RhizaTemplate to a YAML file.
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)
73 # Convert to dictionary with YAML-compatible keys
74 config = {}
76 # Only include template-repository if it's not None
77 if self.template_repository:
78 config["template-repository"] = self.template_repository
80 # Only include template-branch if it's not None
81 if self.template_branch:
82 config["template-branch"] = self.template_branch
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
88 # Include is always present as it's a required field for the config to be useful
89 config["include"] = self.include
91 # Only include exclude if it's not empty
92 if self.exclude:
93 config["exclude"] = self.exclude
95 with open(file_path, "w") as f:
96 yaml.dump(config, f, default_flow_style=False, sort_keys=False)