feat(python): block contents (create_entity(..., block=))

BlockEntityAdd adds a validated entity to a block definition (owner set by the
host, same validation as a model-space entity, self-nesting inserts refused),
reusing the scene's block-editor routing. Deleting a block member now also
drops its handle from the block record. Real-IPC audit with an attribute
definition inside a block, DWG/DXF persistence and undo; IPC and Python unit
tests; docs.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
Felix RiestraandClaude Sonnet 5 committed 2026-09-21 16:54:30 +02:00
1 parent 8d20d280fe
commit b1edc9f578
8 files changed
+251 -7

No files matched your search

+9 -3
View File
@@ -473,11 +473,17 @@ class _Document:
def transaction(self, label):
return _Transaction(self, label)
def create_entity(self, kind, **properties):
"""Create one mapped entity and return its live document descriptor."""
def create_entity(self, kind, block=None, **properties):
"""Create one mapped entity and return its live document descriptor.
With `block="Name"` the entity is added to that block definition
instead of the drawing (its owner is set by the host)."""
if "kind" in properties or "handle" in properties:
raise ValueError("kind and handle are managed by create_entity")
handle = ocs.add(dict(kind=kind, **_coerce_points(kind, properties)))
entity = dict(kind=kind, **_coerce_points(kind, properties))
if block is not None:
handle = ocs.add_to_block(str(block), entity)
else:
handle = ocs.add(entity)
return self.entities[handle]
def delete_entity(self, entity):
+25
View File
@@ -4263,6 +4263,31 @@ mod ocs {
.ok_or_else(|| vm.new_runtime_error("ocs: not running inside a PY_ command".to_owned()))?
}
/// Add a new entity, built from `entity` like `add`, to the block definition
/// `block`. Returns the new entity's handle.
#[cfg(feature = "experimental-host-model")]
#[pyfunction]
fn add_to_block(block: String, entity: PyObjectRef, vm: &VirtualMachine) -> PyResult<u64> {
let dict = entity.try_into_value::<rustpython_vm::builtins::PyDictRef>(vm)?;
let mut built = dict_to_entity(&dict, vm)?;
let result = host_ctx::with_host(|host| -> PyResult<Result<Handle, String>> {
let owner = host
.document()
.block_records
.get(block.trim())
.map(|record| record.handle)
.ok_or_else(|| vm.new_runtime_error(format!("ocs.add_to_block: block {block:?} does not exist")))?;
built.common_mut().owner_handle = owner;
ocs_plugin_api::entity_coverage::validate_new_canvas_entity(&built)
.map_err(|error| vm.new_value_error(error))?;
Ok(host.table_operation(ocs_plugin_api::host::TableOperation::BlockEntityAdd { block, entity: built }))
})
.ok_or_else(|| vm.new_runtime_error("ocs: not running inside a PY_ command".to_owned()))??;
result
.map(|handle| handle.value())
.map_err(|error| vm.new_runtime_error(format!("ocs.add_to_block: {error}")))
}
/// Update `handle` by merging `entity`'s keys onto its *current* value —
/// a key `entity` doesn't mention is left exactly as it was, not reset to
/// a type default (Felix's Phase 1 review: reconstructing from a partial