Deprecations

Names that still work but are on their way out, with what to use instead.

What is public

A name is public when it is listed in the __all__ of a package, and a package is public when it is reachable as an attribute of pylinkage (pylinkage.synthesis, pylinkage.optimization.collections, …). That is the whole rule. The policy below applies to those names and nothing else.

Two consequences:

  • Modules inside a package are implementation. from pylinkage.synthesis.path_generation import path_generation works today and may stop working without notice; from pylinkage.synthesis import path_generation is the promise. Newer modules carry a leading underscore to make this visible (population._member, synthesis._types); older ones will be renamed as they are touched.

  • Each name has one home. Ground lives in pylinkage.components, not also in pylinkage.dyads. The top-level package re-exports the definition path (Ground, Crank, RRRDyad, Linkage, …) for convenience; every other second location is a deprecated alias, listed below.

The surface is pinned name by name in tests/test_public_api.py, so it cannot change without a diff in that file.

Some public names are provisional: exported and documented, but their shape may still change in a minor release. They are the newer subsystems — pylinkage.topology, pylinkage.solver, the co-design family in pylinkage.optimization (co_optimize, CoOptimizationConfig, …), the multi-topology synthesis in pylinkage.synthesis (NBarSolution, generalized_synthesis, …), hierarchical composition in pylinkage.hypergraph, and the CAD export in pylinkage.visualizer. A change to one is announced in the changelog, not through a warning.

How deprecation works here

pylinkage follows semantic versioning. A public name is never removed without notice:

  1. Announced. The name keeps working and keeps resolving to the same object, but reading it raises a DeprecationWarning naming the replacement and the release that will drop it.

  2. Removed. No earlier than the next major release.

DeprecationWarning is silent by default in Python, so nothing in your output changes until you go looking. To see them:

python -W error::DeprecationWarning your_script.py   # fail on any use
python -W default::DeprecationWarning your_script.py # just print them

Under pytest, filterwarnings = ["error::DeprecationWarning"] in your config turns each one into a test failure, which is the cheapest way to find out whether an upgrade will affect you.

Currently deprecated

Name

Use instead

Removed in

pylinkage.components.Dyad

pylinkage.components.Component

2.0.0

pylinkage.components.ConnectedDyad

pylinkage.components.ConnectedComponent

2.0.0

pylinkage.dyads.Dyad

pylinkage.components.Component

2.0.0

pylinkage.dyads.ConnectedDyad

pylinkage.components.ConnectedComponent

2.0.0

pylinkage.synthesis.Dyad

pylinkage.synthesis.BurmesterDyad

2.0.0

pylinkage.dyads.Ground

pylinkage.components.Ground

2.0.0

pylinkage.dyads.PointTracker

pylinkage.components.PointTracker

2.0.0

pylinkage.dyads.Component

pylinkage.components.Component

2.0.0

pylinkage.dyads.ConnectedComponent

pylinkage.components.ConnectedComponent

2.0.0

pylinkage.dyads.Crank

pylinkage.actuators.Crank

2.0.0

pylinkage.dyads.ArcCrank

pylinkage.actuators.ArcCrank

2.0.0

pylinkage.dyads.LinearActuator

pylinkage.actuators.LinearActuator

2.0.0

pylinkage.dyads.Linkage

pylinkage.simulation.Linkage

2.0.0

pylinkage.optimization.Ensemble

pylinkage.population.Ensemble

2.0.0

pylinkage.assur.MobilityResult

pylinkage.topology.MobilityInfo

2.0.0

pylinkage.assur.StructuralAnalysis

pylinkage.topology.compute_mobility()

2.0.0

path_generation(n_orientation_samples=...)

orientation_resolution=

2.0.0

Why the dyads re-exports are going away

pylinkage.dyads used to re-export the frame, actuator and container classes “for convenience”, so pylinkage.dyads.Ground and pylinkage.components.Ground were both correct and tutorials disagreed on which to use. Under the one-home rule above, dyads defines dyads. The objects are unchanged.

Why MobilityResult and StructuralAnalysis are going away

They were exported from pylinkage.assur with no function anywhere in the package that produced them. The mobility analysis that exists is pylinkage.topology.compute_mobility(), which returns a MobilityInfo.

Why the Dyad names are going away

Three unrelated classes were reachable as Dyad, and only two of them were dyads at all:

  • pylinkage.assur.Dyad — an Assur group. A genuine dyad, and unaffected; it keeps its name.

  • pylinkage.synthesis.Dyad — a Burmester dyad: a circle point paired with a center point. Also a genuine dyad, but a specific kind, now named BurmesterDyad.

  • pylinkage.components.Dyad and pylinkage.dyads.Dyad — plain aliases of Component. Never dyads. A Ground point is a Component, and calling it a dyad is simply wrong.

The practical cost was that documentation cross-references could not tell the three apart, so a link on one class would take you to another. Since pylinkage.dyads annotated its anchor parameters with the alias, the anchor type on every RRRDyad, RRPDyad, PPDyad and FixedDyad pointed at a class the code never referred to.

Why n_orientation_samples is going away

It never denoted a number of samples. The value was folded into a per-axis grid resolution through max(6, round(n_samples ** (1 / free))), so the floor swallowed it: with four precision points, every value from 6 to 216 produced the same search. Measured on the README’s points, 6, 12, 36 and 72 all took the same time and returned the same ten solutions.

orientation_resolution is that per-axis number directly, so the cost model is readable: the grid holds orientation_resolution ** (n_points - 1) candidates. The default of 6 reproduces exactly what the old default did, and a value passed to n_orientation_samples is translated through the old formula, so neither changes any result.

Lowering the resolution is rarely a good trade. A coarser grid tends to return no solutions rather than fewer – on three of six test point sets, shrinking it took the result from ten solutions to zero. Use max_solutions to control cost; that one is monotone.

Migrating

Renaming the import is the whole change — the objects are unchanged, and the aliases still point at exactly what they always did:

# Before
from pylinkage.dyads import Dyad, ConnectedDyad
from pylinkage.synthesis import Dyad

# After
from pylinkage.components import Component, ConnectedComponent
from pylinkage.synthesis import BurmesterDyad
# Before
from pylinkage.dyads import Ground, Crank, RRRDyad, Linkage

# After -- one home per name, or the top-level shortcut
from pylinkage.components import Ground
from pylinkage.actuators import Crank
from pylinkage.dyads import RRRDyad
from pylinkage.simulation import Linkage

from pylinkage import Ground, Crank, RRRDyad, Linkage  # equivalent
# Before
from pylinkage.optimization import Ensemble

# After
from pylinkage.population import Ensemble
# Before
path_generation(points, n_orientation_samples=36)

# After -- same search, and the cost model is now visible
path_generation(points, orientation_resolution=6)

isinstance checks keep working through the transition, because the deprecated name and its replacement are the same object:

>>> from pylinkage.components import Component
>>> import pylinkage.dyads
>>> pylinkage.dyads.Dyad is Component   # emits DeprecationWarning
True