[Pather / AutoTool] rework route kwargs into tool_options, and actualyl pass them through AutoTool

This commit is contained in:
Jan Petykiewicz 2026-07-12 20:32:59 -07:00
commit 67afee7704
11 changed files with 865 additions and 130 deletions

View file

@ -1,11 +1,12 @@
from collections.abc import Sequence
from typing import Any, Literal, Never
import inspect
import pytest
import numpy
from numpy import pi
from masque import MinimumStatus, Pather, Library, Port, RouteError, RouteFailurePolicy
from masque import MinimumStatus, Pather, PortPather, Library, Port, RouteError, RouteFailurePolicy
from masque.builder.planner import RoutePortContext, RoutingPlanner
from masque.builder.planner.planner import NoLegalRouteError
from masque.builder.tools import BendOffer, PathTool, RenderStep, StraightOffer, Tool, UOffer
@ -199,7 +200,7 @@ def test_route_error_reports_preferred_minimum_and_request_details() -> None:
)
with pytest.raises(RouteError) as exc_info:
p.ccw('A', 1, out_ptype='wide', diagnostic_context='tool-value')
p.ccw('A', 1, out_ptype='wide', tool_options={'diagnostic_context': 'tool-value'})
details = exc_info.value.details
assert isinstance(exc_info.value, BuildError)
@ -211,7 +212,7 @@ def test_route_error_reports_preferred_minimum_and_request_details() -> None:
'ccw': True,
'length': 1,
'out_ptype': 'wide',
'diagnostic_context': 'tool-value',
'tool_options': {'diagnostic_context': 'tool-value'},
}
assert details.resolved_length == 1
assert details.resolved_jog is None
@ -705,7 +706,7 @@ def test_pather_uturn_rejects_routing_bounds(kwargs: dict[str, int]) -> None:
p = Pather(Library(), tools=PathTool(layer='M1', width=1, ptype='wire'))
p.pattern.ports['A'] = Port((0, 0), rotation=0, ptype='wire')
with pytest.raises(BuildError, match='Unsupported routing bounds for uturn'):
with pytest.raises(TypeError, match='unexpected keyword argument'):
p.uturn('A', 4, **kwargs)
def test_pather_uturn_omitted_length_uses_minimum_length_route() -> None:
@ -935,3 +936,74 @@ def test_pather_uturn_failed_two_bend_route_is_atomic() -> None:
assert numpy.allclose(p.pattern.ports['A'].offset, (0, 0))
assert p.pattern.ports['A'].rotation == 0
assert len(p._paths['A']) == 0
@pytest.mark.parametrize('route_type', [Pather, PortPather])
@pytest.mark.parametrize('name', ['trace', 'trace_to', 'straight', 'bend', 'ccw', 'cw', 'jog', 'uturn', 'trace_into'])
def test_routing_entry_points_have_no_catch_all_kwargs(route_type: type, name: str) -> None:
parameters = inspect.signature(getattr(route_type, name)).parameters.values()
assert all(parameter.kind is not inspect.Parameter.VAR_KEYWORD for parameter in parameters)
def test_routing_typo_fails_before_tool_lookup() -> None:
tool = RequestCountingTool()
p = Pather(Library(), tools=tool, render='deferred')
with pytest.raises(TypeError, match='unexpected keyword argument'):
p.straight('A', lenght=10) # type: ignore[call-arg]
assert tool.offer_calls == 0
@pytest.mark.parametrize('tool_options', [{1: 'bad'}, {'ccw': False}, {'out_ptype': 'bad'}])
def test_tool_options_reject_invalid_or_reserved_keys(tool_options: dict[Any, Any]) -> None:
tool = RequestCountingTool()
p = Pather(Library(), tools=tool, render='deferred')
with pytest.raises(BuildError, match='tool_options'):
p.trace('A', None, tool_options=tool_options)
assert tool.offer_calls == 0
@pytest.mark.parametrize(
'operation',
[
lambda p: p.jog('A', numpy.nan, length=1),
lambda p: p.uturn('A', numpy.inf, length=1),
lambda p: p.straight('A', x=numpy.nan),
],
ids=['jog-offset', 'uturn-offset', 'position-bound'],
)
def test_nonfinite_route_geometry_fails_before_offer_query(operation: Any) -> None:
tool = RequestCountingTool()
p = Pather(
Library(),
ports={'A': Port((0, 0), rotation=0, ptype='wire')},
tools=tool,
render='deferred',
)
with pytest.raises(BuildError, match='finite'):
operation(p)
assert tool.offer_calls == 0
@pytest.mark.parametrize('spacing', [-1, numpy.nan, numpy.inf])
def test_invalid_bundle_spacing_fails_before_offer_query(spacing: float) -> None:
tool = RequestCountingTool()
p = Pather(
Library(),
ports={
'A': Port((0, 0), rotation=0, ptype='wire'),
'B': Port((0, 4), rotation=0, ptype='wire'),
},
tools=tool,
render='deferred',
)
with pytest.raises(BuildError, match='spacing'):
p.trace(['A', 'B'], True, xmin=-10, spacing=spacing)
assert tool.offer_calls == 0