Three review findings on the standards data layer, each a value the layer
let through that a backend would then write as a silent wrong number or a
drawing that can no longer be saved:
* validate_textstyle never checked math.isfinite, unlike its siblings; nan
and inf passed every comparison and reached the STYLE record verbatim.
It now has the same _number helper dimstyles and mleaderstyles use.
* The three private _ILLEGAL_NAME_CHARS copies omitted \n \r \t that
security._FORBIDDEN_SYMBOL_CHARS holds, so 'a\nb' was accepted as a text
style, DIMTXSTY or leader text style and ezdxf refused the save. The rule
now lives once in engineering/standards/names.py (fastmcp-free), the
three modules import it, and a test pins its character set equal to
security's and its output equal to illegal_symbol_name_chars.
* DIMBLK/DIMBLK1/DIMBLK2 were ("str",): any string went through, and the
test pinned '_OBLIQUE', which ezdxf's writer refuses at doc.write while
the COM engine wants exactly that spelling. They are now ("arrow",):
canonical_arrowhead accepts a built-in with or without its underscore in
any case (and '', '.', CLOSEDFILLED for the closed filled default), emits
AutoCAD's documented block-name spelling (also what ezdxf reports after a
load), and holds user block names to the symbol-name rule; ARROWHEAD_BLOCKS
is pinned equal to ezdxf's ARROWS vocabulary, and ezdxf_arrowhead is the
one translation the headless writer needs, proved by exporting all twenty
headlessly and reading each back as its canonical name.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FKFStjEFzpGyLq5bMpWxTd
39 lines
1.7 KiB
Python
39 lines
1.7 KiB
Python
"""The DXF symbol-table name rule, in one place, for the standards data.
|
|
|
|
``security.illegal_symbol_name_chars`` is the same rule at the tool layer; it
|
|
sits behind a fastmcp import and this package stays free of fastmcp, so the
|
|
rule is restated here and ``tests/test_standards_presets.py`` pins the two
|
|
character sets equal. A style, block or font name that passes here is one
|
|
ezdxf will save and AutoCAD will load; ezdxf's own check fires at
|
|
``doc.write``, long after a create tool has already said ``ok``.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
__all__ = ["ILLEGAL_NAME_CHARS", "check_name", "illegal_name_chars"]
|
|
|
|
#: Characters DXF forbids in a symbol-table name, plus the whitespace that
|
|
#: ends a SendCommand segment — byte-for-byte ``security._FORBIDDEN_SYMBOL_CHARS``.
|
|
ILLEGAL_NAME_CHARS: frozenset[str] = frozenset('<>/\\":;?*|,=`') | frozenset("\n\r\t")
|
|
|
|
|
|
def illegal_name_chars(name: str) -> list[str]:
|
|
"""The characters in ``name`` a DXF symbol-table name cannot hold, sorted."""
|
|
return sorted({c for c in name if c in ILLEGAL_NAME_CHARS or ord(c) < 32})
|
|
|
|
|
|
def check_name(key: str, value: Any, *, what: str = "name") -> str:
|
|
"""``value`` as a stripped, non-empty, legal symbol-table name, or a
|
|
``TypeError`` / ``ValueError`` naming ``key`` (``what`` words the refusal)."""
|
|
if not isinstance(value, str):
|
|
raise TypeError(f"{key}: expected a string, got {type(value).__name__}")
|
|
text = value.strip()
|
|
if not text:
|
|
raise ValueError(f"{key}: a {what} cannot be empty")
|
|
bad = illegal_name_chars(text)
|
|
if bad:
|
|
raise ValueError(f"{key}: {text!r} contains characters DXF forbids in a name: {bad}")
|
|
return text
|