merge: track-a task 1
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FKFStjEFzpGyLq5bMpWxTd
This commit is contained in:
@@ -4453,12 +4453,20 @@ class EzdxfBackend(AutoCADBackend):
|
|||||||
}
|
}
|
||||||
if layer:
|
if layer:
|
||||||
attribs["layer"] = layer
|
attribs["layer"] = layer
|
||||||
if attributes:
|
# `add_blockref` never checks the name, and `add_auto_attribs`
|
||||||
ref = msp.add_auto_blockref(
|
# silently skips autofill when `Insert.block()` resolves to None,
|
||||||
name, (float(x), float(y)), attributes, dxfattribs=attribs
|
# so a typo'd name used to yield a dangling INSERT that dropped its
|
||||||
)
|
# attribute values and that `doc.audit()` later deletes. Refuse
|
||||||
else:
|
# before writing, as COM's `InsertBlock` does for an unknown name.
|
||||||
|
if name not in self._doc.blocks:
|
||||||
|
raise ValueError(f"block {name!r} is not defined")
|
||||||
ref = msp.add_blockref(name, (float(x), float(y)), dxfattribs=attribs)
|
ref = msp.add_blockref(name, (float(x), float(y)), dxfattribs=attribs)
|
||||||
|
if attributes:
|
||||||
|
# `add_auto_blockref` wraps the INSERT in an anonymous *U block,
|
||||||
|
# so the returned handle names `*U1` and carries no ATTRIBs.
|
||||||
|
# `add_auto_attribs` attaches ATTRIBs to this INSERT for every
|
||||||
|
# ATTDEF the definition has; values for tags it lacks are ignored.
|
||||||
|
ref.add_auto_attribs({str(k): str(v) for k, v in attributes.items()})
|
||||||
self._mark_dirty()
|
self._mark_dirty()
|
||||||
return _entity_info_dxf(ref)
|
return _entity_info_dxf(ref)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
"""block_insert(attributes=...) must reference the named block, not an anonymous wrapper.
|
||||||
|
|
||||||
|
ezdxf's `add_auto_blockref` wraps the INSERT in an anonymous `*U` block. The
|
||||||
|
handle it returns then reports block_name `*U1`, and `block_get_attributes`
|
||||||
|
finds no ATTRIBs on it — an insert with attributes silently lost them.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
pytestmark = pytest.mark.asyncio
|
||||||
|
|
||||||
|
|
||||||
|
def _define_tagged_block(backend, name: str = "TB") -> None:
|
||||||
|
blk = backend._doc.blocks.new(name=name)
|
||||||
|
blk.add_line((0, 0), (10, 0))
|
||||||
|
blk.add_attdef("TAG", insert=(0, 3), text="", dxfattribs={"height": 2.5})
|
||||||
|
|
||||||
|
|
||||||
|
async def test_insert_with_attributes_references_the_named_block(backend):
|
||||||
|
_define_tagged_block(backend)
|
||||||
|
ref = await backend.block_insert("TB", 10, 10, attributes={"TAG": "P-101"})
|
||||||
|
|
||||||
|
info = await backend.entity_get(ref.handle)
|
||||||
|
assert info.properties["block_name"] == "TB"
|
||||||
|
assert await backend.block_get_attributes(ref.handle) == {"TAG": "P-101"}
|
||||||
|
anonymous = [b.name for b in backend._doc.blocks if b.name.startswith("*U")]
|
||||||
|
assert anonymous == [], "no anonymous wrapper block may be created"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_insert_ignores_values_for_tags_the_block_does_not_define(backend):
|
||||||
|
_define_tagged_block(backend)
|
||||||
|
ref = await backend.block_insert("TB", 0, 0, attributes={"TAG": "V-1", "NOPE": "x"})
|
||||||
|
assert await backend.block_get_attributes(ref.handle) == {"TAG": "V-1"}
|
||||||
|
|
||||||
|
|
||||||
|
async def test_insert_without_attributes_is_unchanged(backend):
|
||||||
|
_define_tagged_block(backend)
|
||||||
|
ref = await backend.block_insert("TB", 0, 0)
|
||||||
|
info = await backend.entity_get(ref.handle)
|
||||||
|
assert info.properties["block_name"] == "TB"
|
||||||
|
assert await backend.block_get_attributes(ref.handle) == {}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("attributes", [{"TAG": "P-101"}, None], ids=["with-attrs", "no-attrs"])
|
||||||
|
async def test_insert_of_undefined_block_is_refused_before_writing(backend, attributes):
|
||||||
|
# `add_blockref` never checks the name and `add_auto_attribs` skips autofill
|
||||||
|
# when the definition is missing, so a typo'd name used to return a handle
|
||||||
|
# whose attribute values were dropped and which `doc.audit()` later deletes.
|
||||||
|
before = len(list(backend._msp()))
|
||||||
|
with pytest.raises(ValueError, match="'NO_SUCH_BLOCK' is not defined"):
|
||||||
|
await backend.block_insert("NO_SUCH_BLOCK", 5, 5, attributes=attributes)
|
||||||
|
assert len(list(backend._msp())) == before, "a refused insert must not write"
|
||||||
|
assert [b.name for b in backend._doc.blocks if b.name == "NO_SUCH_BLOCK"] == []
|
||||||
Reference in New Issue
Block a user