Consolidate load_post parametric drains

bim/handler.py was importing two feature-module internals
(wall_offset_gizmos.clear_caches, preview_base.discard_pending_previews)
to drain load-transient parametric state alongside the existing
tool.Parametric.heal_stale_edit_flags() call inside
_apply_save_file_invariants. Each new parametric drain added one
top-level import and one inline call — every load_post drain leaked
into handler.py's namespace.

Hide all three drains behind tool.Parametric.on_load_post(scene),
sited adjacent to heal_stale_edit_flags. The two feature-module
imports become late imports inside on_load_post — same pattern as
refresh_post_commit's existing `import bonsai.bim.handler` — which
sidesteps the tool.parametric -> bim.module.model.preview_base ->
bonsai.tool registration-time cycle.

The forward-compat AST contract that pinned "every module-scope
GenerationKeyedCache + clear_caches MUST be drained on load_post"
follows the call site to its new home — the test now walks
tool.Parametric.on_load_post instead of _apply_save_file_invariants.

No behaviour change. 45/45 affected bim tests pass
(test_handler_forward_compat, test_preview_base,
test_wall_offset_gizmos, test_parametric_registry).
ruff + black clean on all touched files.

Generated with the assistance of an AI coding tool.
This commit is contained in:
Gorgious56
2026-06-05 13:20:49 +02:00
parent 87bca20df7
commit 8faf9ff43d
3 changed files with 20 additions and 11 deletions
@@ -146,7 +146,7 @@ def _modules_with_module_scope_cache_and_clear():
yield path.stem
def test_apply_save_file_invariants_drains_every_module_scope_geom_cache(handler_tree: ast.Module) -> None:
def test_on_load_post_drains_every_module_scope_geom_cache() -> None:
"""Module-scope ``GenerationKeyedCache`` instances persist across file
loads — the counter they invalidate against is class-level and survives
a ``.blend`` reload. Without a ``load_post`` drain the cache may serve
@@ -154,9 +154,10 @@ def test_apply_save_file_invariants_drains_every_module_scope_geom_cache(handler
freed ``bpy.data``, raising ``ReferenceError`` on the next attribute read.
Pin: every model module that exposes both a module-scope cache and a
top-level ``clear_caches`` is called from ``_apply_save_file_invariants``,
top-level ``clear_caches`` is called from ``tool.Parametric.on_load_post``,
the central post-load drain."""
fn = _function_node(handler_tree, "_apply_save_file_invariants")
parametric_tree = ast.parse(PARAMETRIC_PATH.read_text(encoding="utf-8"))
fn = _function_node(parametric_tree, "on_load_post")
drained: set[str] = set()
for node in ast.walk(fn):
if (
@@ -170,7 +171,7 @@ def test_apply_save_file_invariants_drains_every_module_scope_geom_cache(handler
if missing:
pytest.fail(
"Module(s) expose a module-scope GenerationKeyedCache + clear_caches() but "
f"_apply_save_file_invariants does not drain them on load_post: {sorted(missing)}. "
f"tool.Parametric.on_load_post does not drain them on load_post: {sorted(missing)}. "
"Add a `<module>.clear_caches()` call so freshly-loaded files cannot serve "
"entries holding freed bpy.data references from the previous file."
)