mirror of
https://github.com/IfcOpenShell/IfcOpenShell.git
synced 2026-09-21 14:23:53 +00:00
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:
@@ -41,7 +41,6 @@ from bonsai.bim.decorator_cache import (
|
|||||||
from bonsai.bim.ifc import IfcStore, get_cache_or_detect_lock
|
from bonsai.bim.ifc import IfcStore, get_cache_or_detect_lock
|
||||||
from bonsai.bim.module.aggregate.decorator import AggregateDecorator
|
from bonsai.bim.module.aggregate.decorator import AggregateDecorator
|
||||||
from bonsai.bim.module.georeference.decorator import GeoreferenceDecorator
|
from bonsai.bim.module.georeference.decorator import GeoreferenceDecorator
|
||||||
from bonsai.bim.module.model import wall_offset_gizmos
|
|
||||||
from bonsai.bim.module.model.array import (
|
from bonsai.bim.module.model.array import (
|
||||||
ArrayPreviewDecorator,
|
ArrayPreviewDecorator,
|
||||||
ArraySelectionHighlightDecorator,
|
ArraySelectionHighlightDecorator,
|
||||||
@@ -53,7 +52,6 @@ from bonsai.bim.module.model.decorator import (
|
|||||||
WallAxisDecorator,
|
WallAxisDecorator,
|
||||||
WallFilletPreviewDecorator,
|
WallFilletPreviewDecorator,
|
||||||
)
|
)
|
||||||
from bonsai.bim.module.model.preview_base import discard_pending_previews
|
|
||||||
from bonsai.bim.module.model.wall import WallGizmoPreviewDecorator
|
from bonsai.bim.module.model.wall import WallGizmoPreviewDecorator
|
||||||
from bonsai.bim.module.nest.decorator import NestDecorator
|
from bonsai.bim.module.nest.decorator import NestDecorator
|
||||||
|
|
||||||
@@ -436,8 +434,8 @@ def subscribe_to_viewport_shading_changes():
|
|||||||
|
|
||||||
def _apply_save_file_invariants(scene: bpy.types.Scene) -> None:
|
def _apply_save_file_invariants(scene: bpy.types.Scene) -> None:
|
||||||
"""Invariants enforced on every load_post: msgbus subscription, IFC owner
|
"""Invariants enforced on every load_post: msgbus subscription, IFC owner
|
||||||
settings, scene-bound caches, draft-flag healing, multi-instance lock probe,
|
settings, scene-bound caches, load-transient parametric state, and the
|
||||||
and previews discarded so saved preview state never resurfaces on reopen."""
|
multi-instance lock probe."""
|
||||||
global global_subscription_owner
|
global global_subscription_owner
|
||||||
active_object_key = bpy.types.LayerObjects, "active"
|
active_object_key = bpy.types.LayerObjects, "active"
|
||||||
bpy.msgbus.subscribe_rna(
|
bpy.msgbus.subscribe_rna(
|
||||||
@@ -448,9 +446,7 @@ def _apply_save_file_invariants(scene: bpy.types.Scene) -> None:
|
|||||||
ifcopenshell.api.owner.settings.get_application = get_application
|
ifcopenshell.api.owner.settings.get_application = get_application
|
||||||
AuthoringData.type_thumbnails = {}
|
AuthoringData.type_thumbnails = {}
|
||||||
|
|
||||||
tool.Parametric.heal_stale_edit_flags()
|
tool.Parametric.on_load_post(scene)
|
||||||
discard_pending_previews(scene)
|
|
||||||
wall_offset_gizmos.clear_caches()
|
|
||||||
|
|
||||||
if tool.Ifc.get() and bpy.data.is_saved:
|
if tool.Ifc.get() and bpy.data.is_saved:
|
||||||
props = tool.Blender.get_bim_props()
|
props = tool.Blender.get_bim_props()
|
||||||
|
|||||||
@@ -271,6 +271,18 @@ class Parametric(bonsai.core.tool.Parametric):
|
|||||||
for obj in bpy.data.objects:
|
for obj in bpy.data.objects:
|
||||||
cls._validated_editing_feature(obj)
|
cls._validated_editing_feature(obj)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def on_load_post(cls, scene: bpy.types.Scene) -> None:
|
||||||
|
"""Drain load-transient parametric state on a freshly opened scene
|
||||||
|
so no draft edit flag, preview flag, or cache entry persists from
|
||||||
|
the saved file."""
|
||||||
|
from bonsai.bim.module.model import wall_offset_gizmos
|
||||||
|
from bonsai.bim.module.model.preview_base import discard_pending_previews
|
||||||
|
|
||||||
|
cls.heal_stale_edit_flags()
|
||||||
|
discard_pending_previews(scene)
|
||||||
|
wall_offset_gizmos.clear_caches()
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_pending_edits(cls) -> list[tuple[bpy.types.Object, str]]:
|
def get_pending_edits(cls) -> list[tuple[bpy.types.Object, str]]:
|
||||||
"""``(object, finish_operator_bl_idname)`` pairs for every object
|
"""``(object, finish_operator_bl_idname)`` pairs for every object
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ def _modules_with_module_scope_cache_and_clear():
|
|||||||
yield path.stem
|
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
|
"""Module-scope ``GenerationKeyedCache`` instances persist across file
|
||||||
loads — the counter they invalidate against is class-level and survives
|
loads — the counter they invalidate against is class-level and survives
|
||||||
a ``.blend`` reload. Without a ``load_post`` drain the cache may serve
|
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.
|
freed ``bpy.data``, raising ``ReferenceError`` on the next attribute read.
|
||||||
|
|
||||||
Pin: every model module that exposes both a module-scope cache and a
|
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."""
|
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()
|
drained: set[str] = set()
|
||||||
for node in ast.walk(fn):
|
for node in ast.walk(fn):
|
||||||
if (
|
if (
|
||||||
@@ -170,7 +171,7 @@ def test_apply_save_file_invariants_drains_every_module_scope_geom_cache(handler
|
|||||||
if missing:
|
if missing:
|
||||||
pytest.fail(
|
pytest.fail(
|
||||||
"Module(s) expose a module-scope GenerationKeyedCache + clear_caches() but "
|
"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 "
|
"Add a `<module>.clear_caches()` call so freshly-loaded files cannot serve "
|
||||||
"entries holding freed bpy.data references from the previous file."
|
"entries holding freed bpy.data references from the previous file."
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user