[pather / RouteError] improve error reporting
This commit is contained in:
parent
1b84c87d9b
commit
4d9aaf2fd9
4 changed files with 118 additions and 25 deletions
|
|
@ -1,9 +1,10 @@
|
|||
from collections.abc import Iterator
|
||||
from types import FunctionType
|
||||
import traceback
|
||||
|
||||
import pytest
|
||||
|
||||
from ..builder import Pather, PathTool
|
||||
from ..builder import Pather, PathTool, RouteError
|
||||
from ..error import BuildError, LibraryError
|
||||
from ..library import (
|
||||
INameView,
|
||||
|
|
@ -288,9 +289,19 @@ def test_build_library_error_preserves_route_call_site() -> None:
|
|||
|
||||
message = str(exc_info.value)
|
||||
assert 'Cause: Unable to plan trace_to route' in message
|
||||
assert 'Stack trace:' in message
|
||||
assert 'File "/project/user_recipe.py"' in message
|
||||
assert 'in _external_failing_route_recipe' in message
|
||||
assert 'Stack trace:' not in message
|
||||
|
||||
route_error = exc_info.value.__cause__
|
||||
assert isinstance(route_error, RouteError)
|
||||
assert route_error.__traceback__ is not None
|
||||
route_frames = traceback.extract_tb(route_error.__traceback__)
|
||||
assert sum(frame.filename == '/project/user_recipe.py' for frame in route_frames) == 1
|
||||
assert not any('/builder/planner/' in frame.filename for frame in route_frames)
|
||||
assert route_error.__cause__ is not None
|
||||
assert any('/builder/planner/' in frame.filename for frame in route_error._call_stack)
|
||||
|
||||
native = ''.join(traceback.format_exception(exc_info.value))
|
||||
assert native.count('/project/user_recipe.py') == 1
|
||||
|
||||
|
||||
def test_build_library_depends_on_supports_hidden_dependencies_for_partial_validation() -> None:
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ from collections.abc import Sequence
|
|||
from typing import Any, Literal, Never
|
||||
from types import FunctionType
|
||||
import inspect
|
||||
import traceback
|
||||
|
||||
import pytest
|
||||
import numpy
|
||||
|
|
@ -39,6 +40,14 @@ def _external_failing_ccw(pather: Pather) -> None:
|
|||
pather.ccw('A', 10, out_ptype='optical')
|
||||
|
||||
|
||||
def _external_failing_trace_to(pather: Pather) -> None:
|
||||
pather.trace_to('A', True, length=10, out_ptype='optical')
|
||||
|
||||
|
||||
def _external_failing_portpather_ccw(pather: Pather) -> None:
|
||||
pather.at('A').ccw(10, out_ptype='optical')
|
||||
|
||||
|
||||
class FirstPortOnlyTraceTool(PlanningOnlyTool):
|
||||
def __init__(self) -> None:
|
||||
self.render_calls = 0
|
||||
|
|
@ -291,11 +300,72 @@ def test_route_error_reports_external_pather_call_site() -> None:
|
|||
external_call(p)
|
||||
|
||||
message = str(exc_info.value)
|
||||
assert 'Stack trace:' in message
|
||||
assert 'File "/project/user_layout.py"' in message
|
||||
assert 'in _external_failing_ccw' in message
|
||||
assert '/masque/builder/pather.py' not in message
|
||||
assert '/masque/builder/planner/' not in message
|
||||
assert 'Stack trace:' not in message
|
||||
|
||||
route_error = exc_info.value
|
||||
native_frames = traceback.extract_tb(route_error.__traceback__)
|
||||
assert native_frames
|
||||
assert [frame.name for frame in native_frames if frame.filename.endswith('/builder/pather.py')] == [
|
||||
'ccw',
|
||||
'bend',
|
||||
]
|
||||
assert not any('/builder/planner/' in frame.filename for frame in native_frames)
|
||||
|
||||
native = ''.join(traceback.format_exception(route_error))
|
||||
assert native.count('/project/user_layout.py') == 1
|
||||
assert '\nStack trace:\n' not in native
|
||||
assert route_error.__cause__ is not None
|
||||
|
||||
assert isinstance(route_error._call_stack, tuple)
|
||||
assert sum(frame.filename == '/project/user_layout.py' for frame in route_error._call_stack) == 1
|
||||
assert any(frame.filename.endswith('/builder/pather.py') for frame in route_error._call_stack)
|
||||
assert any('/builder/planner/' in frame.filename for frame in route_error._call_stack)
|
||||
|
||||
|
||||
def test_route_error_direct_trace_to_starts_native_traceback_at_caller() -> None:
|
||||
p = Pather(
|
||||
Library(),
|
||||
ports={'A': Port((0, 0), rotation=0, ptype='wire')},
|
||||
tools=PathTool(layer='M1', width=2, ptype='wire'),
|
||||
render='deferred',
|
||||
)
|
||||
external_call = FunctionType(
|
||||
_external_failing_trace_to.__code__.replace(co_filename='/project/direct_route.py'),
|
||||
globals(),
|
||||
)
|
||||
|
||||
with pytest.raises(RouteError) as exc_info:
|
||||
external_call(p)
|
||||
|
||||
frames = traceback.extract_tb(exc_info.value.__traceback__)
|
||||
assert sum(frame.filename == '/project/direct_route.py' for frame in frames) == 1
|
||||
assert not any(frame.filename.endswith('/builder/pather.py') for frame in frames)
|
||||
assert not any('/builder/planner/' in frame.filename for frame in frames)
|
||||
|
||||
|
||||
def test_route_error_portpather_keeps_only_forwarding_frames() -> None:
|
||||
p = Pather(
|
||||
Library(),
|
||||
ports={'A': Port((0, 0), rotation=0, ptype='wire')},
|
||||
tools=PathTool(layer='M1', width=2, ptype='wire'),
|
||||
render='deferred',
|
||||
)
|
||||
external_call = FunctionType(
|
||||
_external_failing_portpather_ccw.__code__.replace(co_filename='/project/selected_route.py'),
|
||||
globals(),
|
||||
)
|
||||
|
||||
with pytest.raises(RouteError) as exc_info:
|
||||
external_call(p)
|
||||
|
||||
frames = traceback.extract_tb(exc_info.value.__traceback__)
|
||||
assert sum(frame.filename == '/project/selected_route.py' for frame in frames) == 1
|
||||
assert [frame.name for frame in frames if frame.filename.endswith('/builder/pather.py')] == [
|
||||
'ccw',
|
||||
'bend',
|
||||
'trace_to',
|
||||
]
|
||||
assert not any('/builder/planner/' in frame.filename for frame in frames)
|
||||
|
||||
|
||||
def test_route_error_reports_failed_minimum_diagnosis(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue