fix(blocks): attribute values are validated before any write; null/bool/objects refused by tag

block_insert and block_set_attributes applied str() to whatever arrived, so
a JSON null became the text None and a non-mapping failed after the INSERT
was written. validate_attribute_values (backends/block_specs.py) runs first
on both engines: strings pass the one-line rule, ints and finite floats are
written as plain digits, everything else is a TypeError naming the tag.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FKFStjEFzpGyLq5bMpWxTd
This commit is contained in:
Umut CAN
2026-09-17 23:59:17 +03:00
co-authored by Claude Opus 5
parent b753a12bb6
commit 7f79497928
5 changed files with 156 additions and 10 deletions
+14 -1
View File
@@ -3011,7 +3011,15 @@ async def block_insert(
layer: Annotated[str | None, "Layer name"] = None,
ctx: Context = None,
) -> dict:
"""Insert a block and optionally set attribute values."""
"""Insert a block and optionally set attribute values.
Refused before any write, on both engines: an undefined or layout block
name, and an attribute value that is not text — a string is written as
is (one line), an int or finite float as its plain digits (`101` →
`"101"`), while `null`, booleans, lists and objects are refused naming
the tag (they used to be written as their Python repr). Values for tags
the block does not define are ignored.
"""
await ctx.info(f"Inserting block '{name}' at ({x},{y})")
result = await _backend(ctx).block_insert(
name, x, y, scale_x, scale_y, rotation, attributes, layer
@@ -3085,6 +3093,11 @@ async def block_set_attributes(
) -> dict:
"""Update attribute values in a block reference.
Same value rule as `block_insert`: strings and numbers are written,
`null` / booleans / lists / objects are refused naming the tag before
anything changes. `updated_tags` lists the tags that exist on the
reference and were written; unknown tags are ignored.
A multi-line attribute takes `Line1\\PLine2` and keeps every line; the
value written is the one `block_get_attributes` reads back and
`block_explode` bursts, on both engines.