|
22 | 22 | _CALLABLE_STR_RE = re.compile(r"^[A-Za-z_][A-Za-z0-9_\\.]*:[A-Za-z_][A-Za-z0-9_]*$") |
23 | 23 | _CALLABLE_STR_WITH_DIR_RE = re.compile(r"^\{DIR\}(?:\.[A-Za-z_][A-Za-z0-9_]*)*:[A-Za-z_][A-Za-z0-9_]*$") |
24 | 24 |
|
25 | | -_CONFIGCLASS_METHODS = ["to_dict", "from_dict", "replace", "copy", "validate", "post_init_diff"] |
| 25 | +_CONFIGCLASS_METHODS = ["to_dict", "from_dict", "replace", "copy", "validate"] |
26 | 26 | """List of class methods added at runtime to dataclass.""" |
27 | 27 |
|
28 | 28 | """ |
@@ -113,7 +113,6 @@ class EnvCfg: |
113 | 113 | setattr(cls, "replace", _replace_class_with_kwargs) |
114 | 114 | setattr(cls, "copy", _copy_class) |
115 | 115 | setattr(cls, "validate", _validate) |
116 | | - setattr(cls, "post_init_diff", _post_init_diff) |
117 | 116 | # wrap around dataclass |
118 | 117 | cls = dataclass(cls, **kwargs) |
119 | 118 | # return wrapped class |
@@ -498,30 +497,20 @@ def _custom_post_init(obj): |
498 | 497 |
|
499 | 498 |
|
500 | 499 | def _combined_function(f1: Callable, f2: Callable) -> Callable: |
501 | | - """Combine a user ``__post_init__`` with the configclass deep-copy hook. |
502 | | -
|
503 | | - Before *f1* (the user hook) runs, a shallow snapshot of every scalar/tuple |
504 | | - field is taken. After both hooks finish, the snapshot is stored on the |
505 | | - object so that :meth:`post_init_diff` can report which fields were silently |
506 | | - changed by ``__post_init__``. |
| 500 | + """Combine two functions into one by calling them sequentially. |
507 | 501 |
|
508 | 502 | Args: |
509 | | - f1: The user-defined ``__post_init__``. |
510 | | - f2: The configclass ``_custom_post_init`` (deep-copy + ResolvableString wrapping). |
| 503 | + f1: The first function to call (user-defined ``__post_init__``). |
| 504 | + f2: The second function to call (configclass ``_custom_post_init``). |
511 | 505 |
|
512 | 506 | Returns: |
513 | 507 | The combined function. |
514 | 508 | """ |
515 | 509 |
|
516 | 510 | def _combined(*args, **kwargs): |
517 | | - obj = args[0] |
518 | | - # Snapshot scalar / tuple field values before the user hook runs. |
519 | | - before = _snapshot_fields(obj) |
520 | | - _logger.debug("Running __post_init__ for %s", type(obj).__name__) |
| 511 | + _logger.debug("Running __post_init__ for %s", type(args[0]).__name__) |
521 | 512 | f1(*args, **kwargs) |
522 | 513 | f2(*args, **kwargs) |
523 | | - after = _snapshot_fields(obj) |
524 | | - obj.__post_init_field_diff__ = _compute_field_diff(before, after) |
525 | 514 |
|
526 | 515 | return _combined |
527 | 516 |
|
@@ -607,53 +596,6 @@ def _wrap(): |
607 | 596 | return _wrap |
608 | 597 |
|
609 | 598 |
|
610 | | -""" |
611 | | -Post-init diff helpers. |
612 | | -""" |
613 | | - |
614 | | - |
615 | | -def _snapshot_fields(obj: object, prefix: str = "") -> dict[str, Any]: |
616 | | - """Capture a flat ``{dotted.path: value}`` snapshot of scalar and tuple fields. |
617 | | -
|
618 | | - Nested configclass objects are walked recursively so that changes at any |
619 | | - depth are captured. |
620 | | - """ |
621 | | - snap: dict[str, Any] = {} |
622 | | - for key in list(getattr(obj, "__dataclass_fields__", {})): |
623 | | - value = getattr(obj, key, MISSING) |
624 | | - if value is MISSING: |
625 | | - continue |
626 | | - full_key = f"{prefix}{key}" |
627 | | - if hasattr(value, "__dataclass_fields__"): |
628 | | - snap.update(_snapshot_fields(value, prefix=f"{full_key}.")) |
629 | | - elif isinstance(value, (int, float, bool, str, type(None), tuple)): |
630 | | - snap[full_key] = value |
631 | | - return snap |
632 | | - |
633 | | - |
634 | | -def _compute_field_diff(before: dict[str, Any], after: dict[str, Any]) -> dict[str, tuple[Any, Any]]: |
635 | | - """Return ``{field: (old_value, new_value)}`` for fields that changed.""" |
636 | | - diff: dict[str, tuple[Any, Any]] = {} |
637 | | - for key in before: |
638 | | - if key in after and before[key] != after[key]: |
639 | | - diff[key] = (before[key], after[key]) |
640 | | - return diff |
641 | | - |
642 | | - |
643 | | -def _post_init_diff(obj: object) -> dict[str, tuple[Any, Any]]: |
644 | | - """Return fields changed by ``__post_init__`` as ``{field: (before, after)}``. |
645 | | -
|
646 | | - Only scalar and tuple fields are tracked (mutable containers like lists and |
647 | | - dicts are excluded because their identity changes during deep-copy). |
648 | | -
|
649 | | - Returns: |
650 | | - A dictionary mapping dotted field paths to ``(old_value, new_value)`` |
651 | | - tuples. Empty when no ``__post_init__`` was defined or no scalar |
652 | | - fields were modified. |
653 | | - """ |
654 | | - return getattr(obj, "__post_init_field_diff__", {}) |
655 | | - |
656 | | - |
657 | 599 | def resolve_cfg_presets(cfg: object) -> object: |
658 | 600 | """Recursively replace preset-wrapper fields with their *default* preset. |
659 | 601 |
|
|
0 commit comments