Source code for simvx.editor.error_nav
"""Error-navigation helpers — extract source locations from Python tracebacks."""
import re
from pathlib import Path
__all__ = ["parse_traceback_source"]
_FRAME_RE = re.compile(r'File "([^"]+)", line (\d+)')
[docs]
def parse_traceback_source(
tb: str, project_path: Path | str | None = None,
) -> tuple[str, int] | None:
"""Return ``(file_path, line)`` of the last user frame in *tb*, or ``None``.
When *project_path* is given, the last frame whose path sits inside the
project wins — this hides engine/site-packages frames that sandwich user
code in real tracebacks. Falls back to the last ``File "…", line N`` match
overall if no project-local frame is present.
"""
frames = _FRAME_RE.findall(tb)
if not frames:
return None
if project_path is not None:
try:
root = Path(project_path).resolve()
except (OSError, ValueError):
root = None
if root is not None:
for path_str, line_str in reversed(frames):
try:
candidate = Path(path_str).resolve()
except (OSError, ValueError):
continue
try:
candidate.relative_to(root)
except ValueError:
continue
return str(candidate), int(line_str)
path_str, line_str = frames[-1]
return path_str, int(line_str)