fix(task-21): the applied ISO layer set is per document — it follows document_activate on ezdxf and is cleared on every document switch on both engines
`drawing_apply_iso_layers` recorded its standard in `_active_layer_set` on
the backend, and `_role_layer` reads it ahead of the PlanSpec to decide where
`dimension_auto` / `construction_xline` file their output. It was neither a
`_DocState` field nor cleared by `_reset_document_state`, so an iso13567
sheet next to a mech one pushed the mech drawing's dimensions onto
`M-DIMEN-T-N` (created on demand), silently — and `drawing_critique` could
not see it because the same stale set drove the critique. The attribute
predates the registry (v1.5 `drawing_new` did not reset it either), but the
per-document claim of Task 21 covered it and the measured attribute list
missed it.
- `_DocState.active_layer_set` + `_ActiveField("active_layer_set")` on
`EzdxfBackend`: the set follows the active entry, bidirectionally.
- `PremiumContract._reset_document_state` clears it (declared on the mixin
like `_plan_spec`), which fixes COM's `_ensure_document_state` switch and
`drawing_new` / `drawing_open` / `disconnect` on both engines.
- tests: `test_active_layer_set_is_per_document` (headless registry, the
reviewer's repro plus the reverse direction) and the COM activate test now
also asserts the set is cleared and `_role_layer('dim')` is back on DIM.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FKFStjEFzpGyLq5bMpWxTd
This commit is contained in:
co-authored by
Claude Opus 5
parent
64cec37cc7
commit
b1c93cdd2a
@@ -36,11 +36,18 @@ class PremiumContract(ABC):
|
|||||||
|
|
||||||
_plan_spec: PlanSpec | None = None
|
_plan_spec: PlanSpec | None = None
|
||||||
_preflight_result: Any = None
|
_preflight_result: Any = None
|
||||||
|
# The standard `drawing_apply_iso_layers` last bootstrapped on *this*
|
||||||
|
# drawing (None until it runs) — `_active_layer_set_id` reads it ahead of
|
||||||
|
# the PlanSpec. Drawing-scoped like the plan: a live backend clears it
|
||||||
|
# when AutoCAD's active document changes, the headless registry keeps one
|
||||||
|
# per open document.
|
||||||
|
_active_layer_set: LayerSetId | None = None
|
||||||
|
|
||||||
def _reset_document_state(self) -> None:
|
def _reset_document_state(self) -> None:
|
||||||
"""Clear metadata that is valid only for the current drawing."""
|
"""Clear metadata that is valid only for the current drawing."""
|
||||||
self._plan_spec = None
|
self._plan_spec = None
|
||||||
self._preflight_result = None
|
self._preflight_result = None
|
||||||
|
self._active_layer_set = None
|
||||||
self._gdt_datums_defined = set()
|
self._gdt_datums_defined = set()
|
||||||
self._gdt_datums_referenced = set()
|
self._gdt_datums_referenced = set()
|
||||||
|
|
||||||
|
|||||||
@@ -977,6 +977,11 @@ class _DocState:
|
|||||||
transaction_stack: list[Path] = field(default_factory=list)
|
transaction_stack: list[Path] = field(default_factory=list)
|
||||||
plan_spec: Any = None
|
plan_spec: Any = None
|
||||||
preflight_result: Any = None
|
preflight_result: Any = None
|
||||||
|
# `drawing_apply_iso_layers` records the standard here so `_role_layer`
|
||||||
|
# files dims / scaffolding on that standard's layer; it is per drawing,
|
||||||
|
# not per backend (an iso13567 sheet next to a mech one must not push
|
||||||
|
# the mech drawing's dimensions onto M-DIMEN-T-N).
|
||||||
|
active_layer_set: str | None = None
|
||||||
gdt_datums_defined: set = field(default_factory=set)
|
gdt_datums_defined: set = field(default_factory=set)
|
||||||
gdt_datums_referenced: set = field(default_factory=set)
|
gdt_datums_referenced: set = field(default_factory=set)
|
||||||
activated_seq: int = 0
|
activated_seq: int = 0
|
||||||
@@ -1033,6 +1038,7 @@ class EzdxfBackend(AutoCADBackend):
|
|||||||
_transaction_stack = _ActiveField("transaction_stack")
|
_transaction_stack = _ActiveField("transaction_stack")
|
||||||
_plan_spec = _ActiveField("plan_spec")
|
_plan_spec = _ActiveField("plan_spec")
|
||||||
_preflight_result = _ActiveField("preflight_result")
|
_preflight_result = _ActiveField("preflight_result")
|
||||||
|
_active_layer_set = _ActiveField("active_layer_set")
|
||||||
_gdt_datums_defined = _ActiveField("gdt_datums_defined")
|
_gdt_datums_defined = _ActiveField("gdt_datums_defined")
|
||||||
_gdt_datums_referenced = _ActiveField("gdt_datums_referenced")
|
_gdt_datums_referenced = _ActiveField("gdt_datums_referenced")
|
||||||
|
|
||||||
|
|||||||
@@ -88,6 +88,39 @@ async def test_per_document_state_follows_the_active_entry(backend):
|
|||||||
assert backend._dirty is False
|
assert backend._dirty is False
|
||||||
|
|
||||||
|
|
||||||
|
async def test_active_layer_set_is_per_document(backend):
|
||||||
|
"""The standard ``drawing_apply_iso_layers`` bootstrapped is per drawing.
|
||||||
|
|
||||||
|
It drives ``_role_layer`` — where ``dimension_auto`` and
|
||||||
|
``construction_xline`` file their output — and the critique that checks
|
||||||
|
them. Held on the backend it bled across documents: an iso13567 sheet
|
||||||
|
next to a mech one pushed the mech drawing's dimensions onto
|
||||||
|
``M-DIMEN-T-N`` (created on demand), silently, and ``drawing_critique``
|
||||||
|
could not see it because the same stale set drove the critique.
|
||||||
|
"""
|
||||||
|
await backend.drawing_apply_iso_layers("iso13567")
|
||||||
|
assert backend._role_layer("dim") == "M-DIMEN-T-N"
|
||||||
|
|
||||||
|
second = (await backend.drawing_new())["document"]
|
||||||
|
assert backend._active_layer_set is None, "a fresh drawing has no explicit set"
|
||||||
|
assert backend._role_layer("dim") == "DIM"
|
||||||
|
line = await backend.entity_create_line(0, 0, 50, 0)
|
||||||
|
dims = await backend.dimension_auto([line.handle], style="chain")
|
||||||
|
assert dims[0].layer == "DIM"
|
||||||
|
assert "M-DIMEN-T-N" not in [lyr.name for lyr in await backend.layer_list()]
|
||||||
|
|
||||||
|
# Bidirectional: switching back restores the first document's set …
|
||||||
|
await backend.document_activate("untitled-1")
|
||||||
|
assert backend._active_layer_set == "iso13567"
|
||||||
|
assert backend._role_layer("construction") == "M-CONST-E-N"
|
||||||
|
# … and a set applied on B while A is open does not reach A.
|
||||||
|
await backend.document_activate(second)
|
||||||
|
await backend.drawing_apply_iso_layers("pid")
|
||||||
|
assert backend._role_layer("dim") == "DIM"
|
||||||
|
await backend.document_activate("untitled-1")
|
||||||
|
assert backend._active_layer_set == "iso13567"
|
||||||
|
|
||||||
|
|
||||||
async def test_opening_a_file_twice_reloads_it_instead_of_registering_twice(backend, tmp_path):
|
async def test_opening_a_file_twice_reloads_it_instead_of_registering_twice(backend, tmp_path):
|
||||||
path = tmp_path / "part.dxf"
|
path = tmp_path / "part.dxf"
|
||||||
await backend.drawing_save(str(path))
|
await backend.drawing_save(str(path))
|
||||||
@@ -495,6 +528,7 @@ async def test_com_activate_calls_activate_and_resets_document_scope(com_backend
|
|||||||
backend, app = com_backend
|
backend, app = com_backend
|
||||||
await backend._ensure_document_state()
|
await backend._ensure_document_state()
|
||||||
backend._plan_spec = object()
|
backend._plan_spec = object()
|
||||||
|
backend._active_layer_set = "iso13567"
|
||||||
result = await backend.document_activate("Second.dwg")
|
result = await backend.document_activate("Second.dwg")
|
||||||
assert result == {
|
assert result == {
|
||||||
"ok": True,
|
"ok": True,
|
||||||
@@ -505,6 +539,8 @@ async def test_com_activate_calls_activate_and_resets_document_scope(com_backend
|
|||||||
}
|
}
|
||||||
assert app.Documents._docs[1].calls == [("Activate", ())]
|
assert app.Documents._docs[1].calls == [("Activate", ())]
|
||||||
assert backend._plan_spec is None, "a document switch clears drawing-scoped state"
|
assert backend._plan_spec is None, "a document switch clears drawing-scoped state"
|
||||||
|
assert backend._active_layer_set is None, "the layer set is drawing-scoped too"
|
||||||
|
assert backend._role_layer("dim") == "DIM"
|
||||||
with pytest.raises(ValueError, match="no open document named 'Third.dwg'"):
|
with pytest.raises(ValueError, match="no open document named 'Third.dwg'"):
|
||||||
await backend.document_activate("Third.dwg")
|
await backend.document_activate("Third.dwg")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user