From 198b5d462bb1acaa5056cd5356844e50abdf2572 Mon Sep 17 00:00:00 2001 From: Gaotax2006 Date: Fri, 19 Jun 2026 03:31:14 +0800 Subject: [PATCH] fix: reconstruct nested dataclass lists in MC _dict_to_config (#12) _dataclass_to_dict flattens nested lists (RevenueStream, VestBucket) to dicts but _dict_to_config did a flat setattr, destroying structure. Fix: detect list fields whose originals are dataclass lists and recursively reconstruct via _dict_to_dataclass helper. Closes kcolbchain/token-simulator #12 --- token_simulator/monte_carlo.py | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/token_simulator/monte_carlo.py b/token_simulator/monte_carlo.py index 15976d6..99bf9cf 100644 --- a/token_simulator/monte_carlo.py +++ b/token_simulator/monte_carlo.py @@ -118,14 +118,42 @@ def _dataclass_to_dict(obj: Any) -> dict: def _dict_to_config(d: dict) -> SimConfig: - """Convert a flat dict back to a SimConfig (lossy — only scalar fields).""" + """Convert a flat dict back to a SimConfig. + + Handles nested dataclass lists (RevenueStream, VestBucket) that + were flattened by ``_dataclass_to_dict``. + """ cfg = SimConfig() for key, value in d.items(): - if hasattr(cfg, key): + if not hasattr(cfg, key): + continue + orig = getattr(cfg, key) + if isinstance(orig, list) and orig and is_dataclass(orig[0]): + # Reconstruct list of dataclass items. + cls = type(orig[0]) + setattr(cfg, key, [_dict_to_dataclass(cls, item) for item in value]) + elif isinstance(orig, dict) and value and isinstance(value, dict): + # Nested dict → dataclass. + setattr(cfg, key, _dict_to_dataclass(type(orig), value)) + else: setattr(cfg, key, value) return cfg +def _dict_to_dataclass(cls: type, d: dict) -> Any: + """Reconstruct a dataclass instance from a flat dict.""" + inst = cls() + for key, value in d.items(): + if hasattr(inst, key): + orig = getattr(inst, key) + if isinstance(orig, list) and value and isinstance(value, list) and is_dataclass(orig[0] if orig else object()): + cls_item = type(orig[0]) + setattr(inst, key, [_dict_to_dataclass(cls_item, item) for item in value]) + else: + setattr(inst, key, value) + return inst + + def mc_run( config: SimConfig, n: int = MC_DEFAULT_TRIALS,