426 lines
14 KiB
Markdown
426 lines
14 KiB
Markdown
# Migration Guide
|
|
|
|
This guide covers changes between the git tag `release` and the current tree.
|
|
At `release`, `masque.__version__` was `3.3`; the current tree reports `3.4`.
|
|
|
|
Most downstream changes are in `masque/builder/*`, but there are a few other
|
|
API changes that may require code updates.
|
|
|
|
## Routing API: renamed and consolidated
|
|
|
|
The routing helpers were consolidated into a single implementation in
|
|
`masque/builder/pather.py`.
|
|
|
|
The biggest migration point is that the old routing verbs were renamed:
|
|
|
|
| Old API | New API |
|
|
| --- | --- |
|
|
| `Pather.path(...)` | `Pather.trace(...)` |
|
|
| `Pather.path_to(...)` | `Pather.trace_to(...)` |
|
|
| `Pather.mpath(...)` | `Pather.trace(...)` / `Pather.trace_to(...)` with multiple ports |
|
|
| `Pather.pathS(...)` | `Pather.jog(...)` |
|
|
| `Pather.pathU(...)` | `Pather.uturn(...)` |
|
|
| `Pather.path_into(...)` | `Pather.trace_into(...)` |
|
|
| `Pather.path_from(src, dst)` | `Pather.at(src).trace_into(dst)` |
|
|
| `RenderPather.path(...)` | `Pather(..., auto_render=False).trace(...)` |
|
|
| `RenderPather.path_to(...)` | `Pather(..., auto_render=False).trace_to(...)` |
|
|
| `RenderPather.mpath(...)` | `Pather(..., auto_render=False).trace(...)` / `Pather(..., auto_render=False).trace_to(...)` |
|
|
| `RenderPather.pathS(...)` | `Pather(..., auto_render=False).jog(...)` |
|
|
| `RenderPather.pathU(...)` | `Pather(..., auto_render=False).uturn(...)` |
|
|
| `RenderPather.path_into(...)` | `Pather(..., auto_render=False).trace_into(...)` |
|
|
| `RenderPather.path_from(src, dst)` | `Pather(..., auto_render=False).at(src).trace_into(dst)` |
|
|
|
|
There are also new convenience wrappers:
|
|
|
|
- `straight(...)` for `trace_to(..., ccw=None, ...)`
|
|
- `ccw(...)` for `trace_to(..., ccw=True, ...)`
|
|
- `cw(...)` for `trace_to(..., ccw=False, ...)`
|
|
- `jog(...)` for S-bends
|
|
- `uturn(...)` for U-bends
|
|
|
|
Important: `Pather.path()` is no longer the routing API. It now forwards to
|
|
`Pattern.path()` and creates a geometric `Path` element. Any old routing code
|
|
that still calls `pather.path(...)` must be renamed.
|
|
|
|
### Common rewrites
|
|
|
|
```python
|
|
# old
|
|
pather.path('VCC', False, 6_000)
|
|
pather.path_to('VCC', None, x=0)
|
|
pather.mpath(['GND', 'VCC'], True, xmax=-10_000, spacing=5_000)
|
|
pather.pathS('VCC', offset=-2_000, length=8_000)
|
|
pather.pathU('VCC', offset=4_000, length=5_000)
|
|
pather.path_into('src', 'dst')
|
|
pather.path_from('src', 'dst')
|
|
|
|
# new
|
|
pather.cw('VCC', 6_000)
|
|
pather.straight('VCC', x=0)
|
|
pather.ccw(['GND', 'VCC'], xmax=-10_000, spacing=5_000)
|
|
pather.jog('VCC', offset=-2_000, length=8_000)
|
|
pather.uturn('VCC', offset=4_000, length=5_000)
|
|
pather.trace_into('src', 'dst')
|
|
pather.at('src').trace_into('dst')
|
|
```
|
|
|
|
If you prefer the more explicit spelling, `trace(...)` and `trace_to(...)`
|
|
remain the underlying primitives:
|
|
|
|
```python
|
|
pather.trace('VCC', False, 6_000)
|
|
pather.trace_to('VCC', None, x=0)
|
|
```
|
|
|
|
## `PortPather` and `.at(...)`
|
|
|
|
Routing can now be written in a fluent style via `.at(...)`, which returns a
|
|
`PortPather`.
|
|
|
|
```python
|
|
(rpather.at('VCC')
|
|
.trace(False, length=6_000)
|
|
.trace_to(None, x=0)
|
|
)
|
|
```
|
|
|
|
This is additive, not required for migration. Existing code can stay with the
|
|
non-fluent `Pather` methods after renaming the verbs above.
|
|
|
|
Old `PortPather` helper names were also cleaned up:
|
|
|
|
| Old API | New API |
|
|
| --- | --- |
|
|
| `save_copy(...)` | `mark(...)` |
|
|
| `rename_to(...)` | `rename(...)` |
|
|
|
|
Example:
|
|
|
|
```python
|
|
# old
|
|
pp.save_copy('branch')
|
|
pp.rename_to('feed')
|
|
|
|
# new
|
|
pp.mark('branch')
|
|
pp.rename('feed')
|
|
```
|
|
|
|
## Imports and module layout
|
|
|
|
`Pather` now provides the remaining builder/routing surface in
|
|
`masque/builder/pather.py`. The old module files
|
|
`masque/builder/builder.py` and `masque/builder/renderpather.py` were removed.
|
|
|
|
Update imports like this:
|
|
|
|
```python
|
|
# old
|
|
from masque.builder.builder import Builder
|
|
from masque.builder.renderpather import RenderPather
|
|
|
|
# new
|
|
from masque.builder import Pather
|
|
|
|
builder = Pather(...)
|
|
deferred = Pather(..., auto_render=False)
|
|
```
|
|
|
|
Top-level imports from `masque` also continue to work.
|
|
|
|
`Pather` now defaults to `auto_render=True`, so plain construction replaces the
|
|
old `Builder` behavior. Use `Pather(..., auto_render=False)` where you
|
|
previously used `RenderPather`.
|
|
|
|
## `BasicTool` was replaced
|
|
|
|
`BasicTool` is no longer exported. Use `AutoTool` for reusable straight, bend,
|
|
transition, and S-bend primitives.
|
|
|
|
### Old `BasicTool`
|
|
|
|
```python
|
|
from masque.builder.tools import BasicTool
|
|
|
|
tool = BasicTool(
|
|
straight=(make_straight, 'input', 'output'),
|
|
bend=(lib.abstract('bend'), 'input', 'output'),
|
|
transitions={
|
|
'm2wire': (lib.abstract('via'), 'top', 'bottom'),
|
|
},
|
|
)
|
|
```
|
|
|
|
### New `AutoTool`
|
|
|
|
```python
|
|
from masque.builder import AutoTool
|
|
|
|
tool = (
|
|
AutoTool()
|
|
.add_straight('m1wire', make_straight, 'input')
|
|
.add_bend(lib.abstract('bend'), 'input', 'output', clockwise=True)
|
|
.add_transition(lib.abstract('via'), 'top', 'bottom')
|
|
)
|
|
```
|
|
|
|
The key differences are:
|
|
|
|
- `BasicTool` -> `AutoTool`
|
|
- `straight=(fn, in_name, out_name)` -> `add_straight(ptype, fn, in_name)`
|
|
- `bend=(abstract, in_name, out_name)` -> `add_bend(abstract, in_name, out_name)`
|
|
- transitions are registered with `add_transition(abstract, external_port, internal_port)`
|
|
- transitions are bidirectional by default; pass `one_way=True` to inhibit the reverse adapter
|
|
|
|
## Custom `Tool` subclasses
|
|
|
|
If you maintain your own `Tool` subclass, the interface changed:
|
|
|
|
- `primitive_offers()` is now the planning boundary
|
|
- `render()` consumes committed primitive render tokens
|
|
- `Tool.path(...)`, `traceL()`, `traceS()`, `traceU()`, `planL()`,
|
|
`planS()`, and `planU()` are no longer part of the public `Tool` API
|
|
|
|
In practice, a minimal old implementation like:
|
|
|
|
```python
|
|
class MyTool(Tool):
|
|
def path(self, ccw, length, **kwargs):
|
|
...
|
|
```
|
|
|
|
should now become:
|
|
|
|
```python
|
|
from collections.abc import Sequence
|
|
from typing import Any
|
|
|
|
from masque import Port
|
|
from masque.builder import RenderStep, StraightOffer, Tool
|
|
|
|
|
|
class MyTool(Tool):
|
|
def primitive_offers(self, kind, *, in_ptype=None, out_ptype=None, **kwargs):
|
|
if kind != 'straight':
|
|
return ()
|
|
|
|
def endpoint(length):
|
|
ptype = out_ptype or in_ptype
|
|
return Port((length, 0), rotation=3.141592653589793, ptype=ptype)
|
|
|
|
def commit(length):
|
|
return {'length': length}
|
|
|
|
return (StraightOffer(
|
|
in_ptype=in_ptype,
|
|
out_ptype=out_ptype or in_ptype,
|
|
endpoint_planner=endpoint,
|
|
commit_planner=commit,
|
|
),)
|
|
|
|
def render(self, batch: Sequence[RenderStep], **kwargs: Any):
|
|
...
|
|
```
|
|
|
|
If a tool does not provide a primitive kind, return `()` for that kind. `Pather`
|
|
will compose available primitive offers where the route family allows it.
|
|
|
|
### Primitive offers
|
|
|
|
Tools describe legal routing primitives through `Tool.primitive_offers()`.
|
|
`Pather` composes those primitive offers to implement `trace()`, `jog()`,
|
|
`uturn()`, and `trace_into()`.
|
|
|
|
For custom tools, construct the concrete offer class that matches the primitive
|
|
you are exposing:
|
|
|
|
- `StraightOffer` for non-turning length-parameterized primitives
|
|
- `BendOffer` for single-turn length-parameterized primitives
|
|
- `SOffer` for S-like jog-parameterized primitives
|
|
- `UOffer` for U-like jog-parameterized primitives
|
|
|
|
`PrimitiveOffer` is the shared base type used for generic annotations and
|
|
common callback behavior. It is not the normal class users should instantiate.
|
|
The concrete offer classes carry the semantic fields (`length_domain`,
|
|
`jog_domain`, `ccw`) so tools do not need to encode primitive identity in
|
|
strings.
|
|
|
|
Minimal straight-only example:
|
|
|
|
```python
|
|
from collections.abc import Sequence
|
|
from typing import Literal
|
|
|
|
from masque import Port
|
|
from masque.builder import RenderStep, StraightOffer, Tool
|
|
|
|
|
|
class MyTool(Tool):
|
|
def primitive_offers(
|
|
self,
|
|
kind: Literal['straight', 'bend', 's', 'u'],
|
|
*,
|
|
in_ptype=None,
|
|
out_ptype=None,
|
|
**kwargs,
|
|
):
|
|
if kind != 'straight':
|
|
return ()
|
|
|
|
def endpoint(length):
|
|
ptype = out_ptype or in_ptype
|
|
return Port((length, 0), rotation=3.141592653589793, ptype=ptype)
|
|
|
|
def commit(length):
|
|
return {'length': length}
|
|
|
|
return (StraightOffer(
|
|
in_ptype=in_ptype,
|
|
out_ptype=out_ptype or in_ptype,
|
|
endpoint_planner=endpoint,
|
|
commit_planner=commit,
|
|
),)
|
|
|
|
def render(self, batch: Sequence[RenderStep], **kwargs):
|
|
...
|
|
```
|
|
|
|
Primitive offers are local planning objects:
|
|
|
|
- `endpoint_at(parameter)` returns the local output `Port`
|
|
- `cost_at(parameter)` returns an additive scalar route-selection cost
|
|
- `bbox_at(parameter)` returns local primitive bounds when a footprint hook is supplied
|
|
- `parameterized_bbox` may carry opaque future-router footprint metadata
|
|
- `commit(parameter)` returns opaque render data consumed later by `render()`
|
|
- `(min, max)` parameter domains are half-open; `(value, value)` is a fixed singleton
|
|
- selected parameter values must be finite; domains may use infinite open bounds but not `NaN`
|
|
- `None` and `"unk"` ptypes are wildcards; concrete ptype mismatches reject an offer
|
|
|
|
Heterogeneous `StraightOffer` and `SOffer` objects may be used as ptype
|
|
adapters. Requested `out_ptype` constrains only the final route endpoint; any
|
|
intermediate ptypes are chosen by the route solver.
|
|
|
|
`Tool` subclasses must override `primitive_offers()` and return `()` themselves
|
|
for recognized unsupported kinds. There is no route-level `plan*()` fallback.
|
|
Omitted-length S/U behavior comes from direct `SOffer` and `UOffer` endpoint
|
|
domains or from composed straight/bend primitives.
|
|
|
|
Offer constructors accept split `endpoint_planner` and `commit_planner`
|
|
callbacks. Provide both callbacks or override the offer methods in a subclass;
|
|
partial callback configurations are rejected during offer construction.
|
|
|
|
When writing direct primitive offers, declare the actual endpoint ptype
|
|
produced by the offer if it can differ from the requested value; `Pather`
|
|
validates evaluated endpoints against the declared offer ptype.
|
|
|
|
Stable imports for custom tool authors live in `masque.builder`. The
|
|
`masque.builder.planner` module is an internal planner implementation; do not
|
|
import it from user code.
|
|
|
|
`trace_into()` uses the same primitive-offer route selection and now searches
|
|
bounded route topologies with up to four bend roles. This preserves the common
|
|
straight, bend, S-like, U-like, and dogleg cases while allowing routes that
|
|
need an additional bounded bend pair. Among legal bounded candidates,
|
|
`trace_into()` selects the lowest total primitive-offer cost; bend count and
|
|
step count are used only to break exact cost ties.
|
|
|
|
Explicit-length `jog()` routes may also be satisfied by composing a straight
|
|
primitive before or after an omitted-length native S primitive. `uturn()` routes
|
|
may compose a straight primitive before an omitted-length native U primitive.
|
|
These compositions are used when they are the lowest-cost legal route for the
|
|
explicit request.
|
|
|
|
`AutoTool` can attach `bbox_at()` hooks to its primitive offers by rendering the
|
|
selected primitive into a temporary pattern and measuring it. If the rendered
|
|
primitive contains reusable refs, pass the source library as `bbox_library=...`;
|
|
normal routing does not require this.
|
|
|
|
### Omitted-length routing
|
|
|
|
Single-port omitted-length calls now evaluate legal primitive routes at their
|
|
minimum legal length-like parameter, or at their intrinsic endpoint length when
|
|
the requested offset fixes the primitive geometry. Cost then selects among
|
|
those minimum-length candidates:
|
|
|
|
```python
|
|
pather.trace('A', None) # minimum straight-like route
|
|
pather.jog('A', offset=2) # minimum S-like route for that offset
|
|
pather.uturn('A', offset=4) # minimum U-like route for that offset
|
|
```
|
|
|
|
For U-turns, use explicit `length=0` to request the old zero-public-length
|
|
shape:
|
|
|
|
```python
|
|
pather.uturn('A', offset=4, length=0)
|
|
```
|
|
|
|
## Transform semantics changed
|
|
|
|
The other major user-visible change is that `mirror()` and `rotate()` are now
|
|
treated more consistently as intrinsic transforms on low-level objects.
|
|
|
|
The practical migration rule is:
|
|
|
|
- use `mirror()` / `rotate()` when you want to change the object relative to its
|
|
own origin
|
|
- use `flip_across(...)`, `rotate_around(...)`, or container-level transforms
|
|
when you want to move the object in its parent coordinate system
|
|
|
|
### Example: `Port`
|
|
|
|
Old behavior:
|
|
|
|
```python
|
|
port.mirror(0) # changed both offset and orientation
|
|
```
|
|
|
|
New behavior:
|
|
|
|
```python
|
|
port.mirror(0) # changes orientation only
|
|
port.flip_across(axis=0) # old "mirror in the parent pattern" behavior
|
|
```
|
|
|
|
### What to audit
|
|
|
|
Check code that calls:
|
|
|
|
- `Port.mirror(...)`
|
|
- `Ref.rotate(...)`
|
|
- `Ref.mirror(...)`
|
|
- `Label.rotate_around(...)` / `Label.mirror(...)`
|
|
|
|
If that code expected offsets or repetition grids to move automatically, it
|
|
needs updating. For whole-pattern transforms, prefer calling `Pattern.mirror()`
|
|
or `Pattern.rotate_around(...)` at the container level.
|
|
|
|
## Other user-facing changes
|
|
|
|
### DXF environments
|
|
|
|
If you install the DXF extra, the supported `ezdxf` baseline moved from
|
|
`~=1.0.2` to `~=1.4`. Any pinned environments should be updated accordingly.
|
|
|
|
### New exports
|
|
|
|
These are additive, but available now from `masque` and `masque.builder`:
|
|
|
|
- `PortPather`
|
|
- `AutoTool`
|
|
- `boolean`
|
|
|
|
## Minimal migration checklist
|
|
|
|
If your code uses the routing stack, do these first:
|
|
|
|
1. Replace `path`/`path_to`/`mpath`/`path_into` calls with
|
|
`trace`/`trace_to`/multi-port `trace`/`trace_into`.
|
|
2. Replace `BasicTool` with `AutoTool`.
|
|
3. Fix imports that still reference `masque.builder.builder` or
|
|
`masque.builder.renderpather`.
|
|
4. Audit any low-level `mirror()` usage, especially on `Port` and `Ref`.
|
|
|
|
If your code only uses `Pattern`, `Library`, `place()`, and `plug()` without the
|
|
routing helpers, you may not need any changes beyond the transform audit and any
|
|
stale imports.
|