mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-10-18 12:59:12 +00:00
fix(harness): load nested schema defaults, and merge caller config at leaf level
load_config_defaults read only top-level properties. An object property carries
its defaults on its children, not on itself, so everything nested was dropped --
2,386 defaults across 37 of 44 plugins, soccer-scoreboard alone losing 539 of
565. render_plugin_matrix's comment says the plugin then "behaves like a real
install", which for most of the fleet it did not.
_defaults_from_properties now recurses. merge_config deep-merges the caller's
config onto the result so an override lands at the leaf: a shallow merge would
let -c '{"nhl": {"enabled": true}}' replace the whole nhl subtree and discard
every other nhl default, which is the same class of bug being fixed here.
Measured before/after across all 49 installed plugins on the rig: **no render
changed** -- identical PASS/FAIL counts, byte-identical output, goldens intact.
Plugins already fall back to the same values internally via config.get(key,
default), so supplying them explicitly agrees with what they were doing. The
defaults really are arriving now:
ufc-scoreboard 9 -> 87 defaults
ledmatrix-flights 51 -> 95
masters-tournament 10 -> 51
cricket-scoreboard 22 -> 50
tide-display 12 -> 18
and hockey-scoreboard, which used to load nhl.enabled=None, now gets
nhl.enabled=True with its full display_modes block.
Caveat worth carrying: the eight plugins with the most nested config
(soccer, baseball, basketball, hockey, lacrosse, football, afl, nrl -- 1,634 of
the 2,386 dropped defaults, 68%) could not be measured. They import
src.common.sports_shared, which the test rig's core branch predates, so they
fail to load there identically before and after. Re-run this comparison against
a core that has that module before trusting the "nothing changed" result for
them; those are exactly the plugins whose renders should change most.
Closes #531.
This commit is contained in:
@@ -33,6 +33,45 @@ def load_manifest(plugin_dir: Union[str, Path]) -> Dict[str, Any]:
|
||||
return json.load(f)
|
||||
|
||||
|
||||
def _defaults_from_properties(properties: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""Defaults for one `properties` block, recursing into nested objects.
|
||||
|
||||
An object property carries its defaults on its children, not on itself, so
|
||||
reading only the top level dropped everything nested. That is most of the
|
||||
fleet: config organised by league, or under customization/display_options,
|
||||
lost 2,386 defaults across 37 of 44 plugins -- soccer-scoreboard alone lost
|
||||
539 of 565 -- and the harness rendered them with a config no install would
|
||||
ever have.
|
||||
"""
|
||||
defaults: Dict[str, Any] = {}
|
||||
for key, prop in (properties or {}).items():
|
||||
if not isinstance(prop, dict):
|
||||
continue
|
||||
if prop.get('type') == 'object' and isinstance(prop.get('properties'), dict):
|
||||
nested = _defaults_from_properties(prop['properties'])
|
||||
if nested:
|
||||
defaults[key] = nested
|
||||
elif 'default' in prop:
|
||||
defaults[key] = prop['default']
|
||||
return defaults
|
||||
|
||||
|
||||
def merge_config(base: Dict[str, Any], override: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""Deep-merge override onto base, without dropping sibling defaults.
|
||||
|
||||
A shallow merge would let `-c '{"nhl": {"enabled": true}}'` replace the whole
|
||||
nhl subtree and silently discard every other nhl default -- the same class of
|
||||
bug this function exists to fix.
|
||||
"""
|
||||
merged = dict(base)
|
||||
for key, value in (override or {}).items():
|
||||
if isinstance(value, dict) and isinstance(merged.get(key), dict):
|
||||
merged[key] = merge_config(merged[key], value)
|
||||
else:
|
||||
merged[key] = value
|
||||
return merged
|
||||
|
||||
|
||||
def load_config_defaults(plugin_dir: Union[str, Path]) -> Dict[str, Any]:
|
||||
"""Extract default values from a plugin's config_schema.json (empty if none)."""
|
||||
schema_path = Path(plugin_dir) / 'config_schema.json'
|
||||
@@ -40,11 +79,7 @@ def load_config_defaults(plugin_dir: Union[str, Path]) -> Dict[str, Any]:
|
||||
return {}
|
||||
with open(schema_path, 'r') as f:
|
||||
schema = json.load(f)
|
||||
defaults: Dict[str, Any] = {}
|
||||
for key, prop in schema.get('properties', {}).items():
|
||||
if isinstance(prop, dict) and 'default' in prop:
|
||||
defaults[key] = prop['default']
|
||||
return defaults
|
||||
return _defaults_from_properties(schema.get('properties', {}))
|
||||
|
||||
|
||||
def load_harness_spec(plugin_dir: Union[str, Path]) -> Dict[str, Any]:
|
||||
|
||||
Reference in New Issue
Block a user