Skip to content

Commit 439f557

Browse files
committed
refactor: BrandBootstrapConfig
1 parent 9c9b0da commit 439f557

File tree

2 files changed

+39
-47
lines changed

2 files changed

+39
-47
lines changed

examples/brand/_brand.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,8 @@ typography:
104104

105105
defaults:
106106
bootstrap:
107-
my-pink: "$brand-pink"
107+
defaults:
108+
my-pink: "$brand-pink"
108109
shiny:
109110
theme:
110111
preset: shiny

shiny/ui/_theme_brand.py

Lines changed: 37 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,8 @@ def warn_or_raise_unmapped_variable(unmapped: str):
122122

123123

124124
class BrandBootstrapConfigFromYaml:
125+
"""Validate a Bootstrap config from a YAML source"""
126+
125127
def __init__(
126128
self,
127129
path: str,
@@ -133,7 +135,8 @@ def __init__(
133135
rules: Any = None,
134136
):
135137

136-
self.path = path
138+
# TODO: Remove `path` and handle in try/except block in caller
139+
self._path = path
137140
self.version = version
138141
self.preset: str | None = self._validate_str(preset, "preset")
139142
self.functions: str | None = self._validate_str(functions, "functions")
@@ -148,29 +151,31 @@ def _validate_str(self, x: Any, param: str) -> str | None:
148151
return x
149152

150153
raise ValueError(
151-
f"Invalid brand `{self.path}.{param}`. Must be a string or empty."
154+
f"Invalid brand `{self._path}.{param}`. Must be a string or empty."
152155
)
153156

154157
def _validate_defaults(self, x: Any) -> dict[str, YamlScalarType] | None:
155158
if x is None:
156159
return None
157160

158-
path = self.path
159-
if path == "defaults.shiny.theme":
160-
path += ".defaults"
161-
162161
if not isinstance(x, dict):
163-
raise ValueError(f"Invalid brand `{path}`, must be a dictionary.")
162+
raise ValueError(
163+
f"Invalid brand `{self._path}.defaults`, must be a dictionary."
164+
)
164165

165166
y: dict[Any, Any] = x
166167

167168
if not all([isinstance(k, str) for k in y.keys()]):
168-
raise ValueError(f"Invalid brand `{path}`, all keys must be strings.")
169+
raise ValueError(
170+
f"Invalid brand `{self._path}.defaults`, all keys must be strings."
171+
)
169172

170173
if not all(
171174
[v is None or isinstance(v, (str, int, float, bool)) for v in y.values()]
172175
):
173-
raise ValueError(f"Invalid brand `{path}`, all values must be scalar.")
176+
raise ValueError(
177+
f"Invalid brand `{self._path}.defaults`, all values must be scalar."
178+
)
174179

175180
res: dict[str, YamlScalarType] = y
176181
return res
@@ -217,52 +222,38 @@ def from_brand(cls, brand: Brand):
217222
if not brand.defaults:
218223
return cls(version=v_bootstrap, preset="shiny")
219224

220-
defaults: dict[str, YamlScalarType] = {}
221-
222-
d_bootstrap = cls._brand_defaults_bootstrap(brand)
223-
d_shiny = cls._brand_defaults_shiny(brand)
224-
225-
defaults.update(d_bootstrap.defaults or {})
226-
defaults.update(d_shiny.defaults or {})
225+
shiny_args = {}
226+
if "shiny" in brand.defaults and "theme" in brand.defaults["shiny"]:
227+
shiny_args = brand.defaults["shiny"]["theme"]
227228

228-
return cls(
229-
version=d_shiny.version or d_bootstrap.version or v_bootstrap,
230-
preset=d_shiny.preset or d_bootstrap.preset,
231-
functions=d_shiny.functions,
232-
defaults=defaults,
233-
mixins=d_shiny.mixins,
234-
rules=d_shiny.rules,
229+
shiny = BrandBootstrapConfigFromYaml(
230+
path="defaults.shiny.theme",
231+
**shiny_args,
235232
)
236233

237-
@staticmethod
238-
def _brand_defaults_shiny(brand: Brand) -> BrandBootstrapConfigFromYaml:
239-
if (
240-
not brand.defaults
241-
or not isinstance(brand.defaults.get("shiny"), dict)
242-
or not isinstance(brand.defaults["shiny"].get("theme"), dict)
243-
):
244-
return BrandBootstrapConfigFromYaml(path="defaults.shiny.theme")
234+
bs_args = {}
235+
if "bootstrap" in brand.defaults:
236+
bs_args = brand.defaults["bootstrap"]
245237

246-
return BrandBootstrapConfigFromYaml(
247-
path="defaults.shiny.theme",
248-
**brand.defaults["shiny"]["theme"],
238+
bootstrap = BrandBootstrapConfigFromYaml(
239+
path="defaults.bootstrap",
240+
**bs_args,
249241
)
250242

251-
@staticmethod
252-
def _brand_defaults_bootstrap(brand: Brand) -> BrandBootstrapConfigFromYaml:
253-
if not brand.defaults or not isinstance(brand.defaults.get("bootstrap"), dict):
254-
return BrandBootstrapConfigFromYaml(path="defaults.bootstrap")
243+
# now combine bootstrap and shiny config options in a way that makes sense
244+
def join_str(x: str | None, y: str | None):
245+
return "\n".join([z for z in [x, y] if z is not None])
255246

256-
bootstrap: dict[str, Any] = brand.defaults["bootstrap"]
257-
defaults: dict[str, Any] = {
258-
k: v for k, v in bootstrap.items() if k not in ("version", "preset")
259-
}
247+
defaults = bootstrap.defaults or {}
248+
defaults.update(shiny.defaults or {})
260249

261-
return BrandBootstrapConfigFromYaml(
262-
path="defaults.bootstrap",
263-
version=bootstrap.get("version"),
264-
preset=bootstrap.get("preset"),
250+
return cls(
251+
version=shiny.version or bootstrap.version or v_bootstrap,
252+
preset=shiny.preset or bootstrap.preset,
253+
functions=join_str(bootstrap.functions, shiny.functions),
265254
defaults=defaults,
255+
mixins=join_str(bootstrap.mixins, shiny.mixins),
256+
rules=join_str(bootstrap.rules, shiny.rules),
266257
)
267258

268259

0 commit comments

Comments
 (0)