masque/MIGRATION.md

8.4 KiB

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

# 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:

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.

(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:

# 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:

# 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:

  • SimpleTool for the simple "one straight generator + one bend cell" case
  • AutoTool if you need transitions, multiple candidate straights/bends, or S-bends/U-bends

Old BasicTool

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

from masque.builder.tools import AutoTool

tool = AutoTool(
    straights=[
        AutoTool.Straight(
            ptype='m1wire',
            fn=make_straight,
            in_port_name='input',
            out_port_name='output',
        ),
    ],
    bends=[
        AutoTool.Bend(
            abstract=lib.abstract('bend'),
            in_port_name='input',
            out_port_name='output',
            clockwise=True,
        ),
    ],
    sbends=[],
    transitions={
        ('m2wire', 'm1wire'): AutoTool.Transition(
            lib.abstract('via'),
            'top',
            'bottom',
        ),
    },
    default_out_ptype='m1wire',
)

The key differences are:

  • BasicTool -> SimpleTool or AutoTool
  • straight=(fn, in_name, out_name) -> straights=[AutoTool.Straight(...)]
  • bend=(abstract, in_name, out_name) -> bends=[AutoTool.Bend(...)]
  • transition keys are now (external_ptype, internal_ptype) tuples
  • transitions use AutoTool.Transition(...) instead of raw tuples

If your old BasicTool usage did not rely on transitions or multiple routing options, SimpleTool is the closest replacement.

Custom Tool subclasses

If you maintain your own Tool subclass, the interface changed:

  • Tool.path(...) became Tool.traceL(...)
  • Tool.traceS(...) and Tool.traceU(...) were added for native S/U routes
  • planL() / planS() / planU() remain the planning hooks used by deferred rendering

In practice, a minimal old implementation like:

class MyTool(Tool):
    def path(self, ccw, length, **kwargs):
        ...

should now become:

class MyTool(Tool):
    def traceL(self, ccw, length, **kwargs):
        ...

If you do not implement traceS() or traceU(), the unified pather will either fall back to the planning hooks or synthesize those routes from simpler steps where possible.

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:

port.mirror(0)   # changed both offset and orientation

New behavior:

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
  • SimpleTool
  • 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 SimpleTool or 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.