fix(understand): a truncated DXF is refused by the snapshot, not left hanging

ezdxf's tag reader raises StopIteration on a file that ends early, for example
after an interrupted save or a partial copy. read_snapshot caught only OSError
and DXFError, so the StopIteration escaped. asyncio.to_thread cannot hand
StopIteration back to an awaiting caller, so every track H tool reading such
a file would have hung instead of refusing it.

The review of drawing_diff found this in its own reader. The snapshot now
does the same: every parse or walk failure except MemoryError becomes the
existing "is not a readable DXF file" refusal. A new test checks this both
directly and through a worker thread with a 10 s timeout. It failed with
StopIteration before this change and passes after it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FKFStjEFzpGyLq5bMpWxTd
This commit is contained in:
Umut CAN
2026-09-24 12:22:23 +03:00
co-authored by Claude Opus 5.5
parent 704a44b190
commit efa1ea8182
2 changed files with 27 additions and 4 deletions
+9 -4
View File
@@ -297,11 +297,16 @@ def records_from_doc(doc, *, source: str) -> Snapshot:
def _read(path: str, *, source: str) -> Snapshot:
# Every parse or walk failure is one refusal. A truncated file makes ezdxf's
# tag reader raise StopIteration, which asyncio.to_thread cannot hand back to
# an awaiting tool - uncaught, the tool would never answer.
try:
doc = ezdxf.readfile(path)
except (OSError, ezdxf.DXFError) as exc:
raise ValueError(f"{path} is not a readable DXF file: {exc}") from exc
return records_from_doc(doc, source=source)
return records_from_doc(ezdxf.readfile(path), source=source)
except MemoryError:
raise
except Exception as exc: # noqa: BLE001 - see above
reason = str(exc) or type(exc).__name__
raise ValueError(f"{path} is not a readable DXF file: {reason}") from exc
def read_snapshot(path: str) -> Snapshot: