Coverage for /home/runner/work/coxbuild/coxbuild/src/coxbuild/configurations/builders.py: 70%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1import json
2import os
3from abc import ABC, abstractmethod
4from pathlib import Path
5from typing import Any
7import yaml
9from . import Configuration
12class ConfigurationBuilder(ABC):
13 @abstractmethod
14 def build(self, config: Configuration) -> None: pass
17class EnvironmentConfigurationBuilder(ConfigurationBuilder):
18 def build(self, config: Configuration) -> None:
19 env = config.env()
20 envbuild = env.section("coxbuild")
21 for key in os.environ:
22 env[key] = os.getenv(key)
23 for key in envbuild:
24 config[key] = envbuild[key]
27class DictionaryConfigurationBuilder(ConfigurationBuilder):
28 """
29 Use dictionary to build configuration.
31 Nested dictionary will be converted to a subsection.
32 To avoid this behavior, use an entry with key '__keep__'.
33 """
35 def __init__(self, data: dict[str, Any]) -> None:
36 super().__init__()
37 self.data = data
39 def build(self, config: Configuration) -> None:
40 for key in self.data:
41 value = self.data[key]
43 if isinstance(value, dict):
44 if "__keep__" in value:
45 del value["__keep__"]
46 config[key] = value
47 else:
48 sub = config.section(key)
49 DictionaryConfigurationBuilder(value).build(sub)
50 else:
51 config[key] = value
54class JsonConfigurationBuilder(ConfigurationBuilder):
55 def __init__(self, path: Path) -> None:
56 super().__init__()
57 self.path = path
59 def build(self, config: Configuration) -> None:
60 if not self.path.exists():
61 return
62 DictionaryConfigurationBuilder(
63 json.loads(self.path.read_text())).build(config)
66class YamlConfigurationBuilder(ConfigurationBuilder):
67 def __init__(self, path: Path) -> None:
68 super().__init__()
69 self.path = path
71 def build(self, config: Configuration) -> None:
72 if not self.path.exists():
73 return
74 DictionaryConfigurationBuilder(
75 yaml.safe_load(self.path.read_text())).build(config)
78class ConfigurationBuilderCollection(ConfigurationBuilder):
79 def __init__(self) -> None:
80 super().__init__()
81 self.builders: list[ConfigurationBuilder] = []
83 def add(self, builder: ConfigurationBuilder) -> "ConfigurationBuilderCollection":
84 self.builders.append(builder)
85 return self
87 def build(self, config: Configuration) -> None:
88 for builder in self.builders:
89 builder.build(config)
92def getDefaultBuilder() -> ConfigurationBuilderCollection:
93 """Use environment variables and file buildcox.[json/yaml] to build configuration."""
94 return ConfigurationBuilderCollection() \
95 .add(EnvironmentConfigurationBuilder()) \
96 .add(JsonConfigurationBuilder(Path("buildcox.json"))) \
97 .add(YamlConfigurationBuilder(Path("buildcox.yaml"))) \
98 .add(YamlConfigurationBuilder(Path("buildcox.yml")))