Files
Umut CANandClaude Opus 5 827719fdd0 fix(task-9): finite text-style numbers, one symbol-name rule, canonical DIMBLK arrowheads
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
2026-09-16 21:33:05 +03:00

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