Repository navigation
refactor: define and freeze the public surface - #49
Merged
Merged
Conversation
Closes #22 and #23. A name is public when it is in a package's __all__; modules inside a package are implementation; each name has one home. That rule is now stated on the Deprecations page and enforced by tests/test_public_api.py, which pins every package's __all__ name by name, so the surface cannot change without a diff in that file. Applying the rule to the 379 names that were exported: - 54 implementation names leave __all__ but stay importable: numba kernels in solver, per-entity serialization halves and typing aliases in mechanism and synthesis, styling constants in visualizer, lookup helpers in assur, _AnchorProxy. No warning, since nothing outside the package imports them and star-import is not a promise. - 11 sibling re-exports become deprecated aliases that warn: the eight frame/actuator/container names in dyads, optimization.Ensemble, and assur.MobilityResult / StructuralAnalysis, which nothing produced. Same objects, same isinstance behaviour. - 80 names are marked provisional in the freeze: topology, solver, co-design, multi-topology synthesis, hierarchical hypergraphs, CAD export. Exported and documented, not yet promised. - Promotions: every subpackage is a top-level attribute (only five were), the definition path is importable from pylinkage directly, and mechanism exports TrackerJoint and ArcDriverLink, which leggedsnake and the editor backend were fetching from submodules. For the definition path itself, the README gains a "Which API should I use?" section naming components / actuators / dyads / simulation and saying in one line each what MechanismBuilder, hypergraph, assur and Mechanism are for; the API reference index opens with the same guidance. README and changelog link the policy. Nothing is removed. The full suite passes with DeprecationWarning as an error, so no internal path trips its own shims.
optimization, visualizer, solver and the top-level package load some names on first access through a module __getattr__ that returns object. That is invisible to a type checker: leggedsnake importing CoOptimizationConfig from pylinkage.optimization, as the public-surface rule now asks it to, got "object not callable" and "not valid as a type" from mypy. Each of those packages now carries a TYPE_CHECKING block importing the same names eagerly. Runtime behaviour is unchanged; the block never executes.
Owner
Author
|
I'll merge for now, and do thorough testing before cutting the release. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #22 and #23.
The rule
A name is public when it is in a package's
__all__; modules inside a package are implementation; each name has one home. The top-level package re-exports the definition path as a shortcut; every other second location is a deprecated alias.Stated on the Deprecations page (new "What is public" section), and enforced by
tests/test_public_api.py, which pins every package's__all__name by name. The surface cannot change without a diff in that file.Applying it to the 379 exported names
topology,solver, co-design, multi-topology synthesis, hierarchical hypergraphs, CAD export)__all__, stay importable, no warning — nothing outside the package imports them andimport *is not a promise. Numba kernels, per-entity serialization halves, typing aliases, styling constants,_AnchorProxy,MutableAgent, …DeprecationWarningnaming the home. The eight frame/actuator/container names indyads;optimization.Ensemble;assur.MobilityResult/StructuralAnalysis, which nothing in the package ever producedGround,Crank,RRRDyad, … importable frompylinkage;mechanism.TrackerJointandArcDriverLinkexported (leggedsnake and the editor backend were fetching them from submodules);dimensionsandexceptionsget an__all__Nothing is removed. Minor-release safe by construction.
#23
README gains a "Which API should I use?" section:
components/actuators/dyads/simulationis the path, with one line each forMechanismBuilder,hypergraph,assur,Mechanism. The API reference index opens with the same guidance instead of an alphabetical list; getting-started says it in one sentence. README and changelog link the policy.Also
The second commit adds
TYPE_CHECKINGblocks mirroring the lazy-load tables inoptimization,visualizer,solverand the top level. Without them a type checker sees lazily loaded names asobject, which broke leggedsnake's mypy the moment it importedCoOptimizationConfigfrom the package as the rule asks.Downstream
>=1.2.0.Agent/MutableAgenttoMember.Both are proof the rule holds for the two real consumers.
Verification
-W error::DeprecationWarning: 2552 passed. No internal path trips its own shims.ruff,mypyclean.main+main; mypy 9 → 6 errors.