feat(sensors): open lidar model factory and make LidarModel generic in its parameters - #178
Merged
Merged
Conversation
janickm
commented
Sep 9, 2026
janickm
force-pushed
the
dev/janickm/lidar-model-factory
branch
from
September 9, 2026 08:58
3dde086 to
fe34be6
Compare
… registry Applies the treatment the camera model hierarchy already received to the lidar hierarchy. `LidarModel.maybe_from_parameters` was a static method on the abstract base holding a closed if/elif dispatch table over every concrete subclass, and `StructuredLidarModel` shadowed it with a second copy that had to be kept in sync by hand. Static methods do not dispatch, so which of the two tables ran depended only on the class name the caller happened to spell, and neither could construct a lidar model defined outside NCore. Replace both with a module-level `lidar_model_from_parameters()` dispatching on the parameter type via `functools.singledispatch`, plus `maybe_lidar_model_from_parameters()` for the optional case and `register_lidar_model` for out-of-tree models. As with the camera factory, the registry sits behind a separate private symbol so the public entry point can carry `typing.overload` signatures. Both static methods are kept as deprecated forwarders. Make `LidarModel` generic in its parameter type and declare `get_parameters()` on it, so a `LidarModel` stays statically useful without narrowing to a concrete model first. The parameter type is covariant: it appears only in a return position, and invariance would leave concrete instantiations with no common `LidarModel[...]` supertype. Plain unparameterized `LidarModel` annotations and isinstance checks are unaffected. `BaseLidarModelParameters` gains the JSON (de)serialization interface and a non-abstract `type()`, matching `CameraModelParameters`, so parameters can be serialized through the abstract type; the concrete leaf no longer needs its own mixin. Note the deprecated `StructuredLidarModel.maybe_from_parameters` keeps the base's parameter type rather than narrowing it. Narrowing it is a contravariance violation that `ty` rejects once the base is widened; it was invisible before only because both copies named the same concrete union.
janickm
force-pushed
the
dev/janickm/lidar-model-factory
branch
from
September 9, 2026 12:06
fe34be6 to
6af06a6
Compare
Merged
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.
Applies the treatment the camera model hierarchy received in #175 / #177 to the lidar hierarchy. Same shape, so the review should feel familiar.
The closed dispatch table, twice
LidarModel.maybe_from_parameterswas a static method on the abstract base holding a closedif/eliftable over every concrete subclass — andStructuredLidarModelshadowed it with a second copy that had to be kept in sync by hand. Since static methods do not dispatch, which of the two tables ran depended only on the class name the caller happened to spell, and neither could construct a lidar model defined outside NCore.Both are replaced by a module-level
lidar_model_from_parameters()dispatching on the parameter type viafunctools.singledispatch, withmaybe_lidar_model_from_parameters()for the optional case andregister_lidar_modelfor out-of-tree models. Both static methods are kept as deprecated forwarders.Generic model
LidarModeldeclared noget_parameters— only the concrete leaf did, so aLidarModel-typed value was a dead end. It is now generic in its parameter type and declares it. The parameter is covariant: it appears only in a return position, and invariance would leave concrete instantiations with no commonLidarModel[...]supertype. Plain unparameterizedLidarModelannotations and isinstance checks are unaffected.Serialization on the abstract base
BaseLidarModelParameterswas not even an ABC (@dataclass class BaseLidarModelParameters: pass); the JSON mixin andtype()sat only on the concrete leaf, so code holding the abstract type could not serialize. It now carries both,type()non-abstract so pre-existing out-of-tree subclasses stay instantiable, and the leaf drops its own mixin.One thing worth a look
The deprecated
StructuredLidarModel.maybe_from_parameterskeeps the base's parameter type rather than narrowing it to the structured parameters. Narrowing it is a contravariance violation thattyrejects once the base is widened — it was invisible before only because both copies happened to name the same concrete union. The structured-ness of the result is enforced at runtime instead.Verified: 33/33 tests on 3.8 and 3.11,
tyaspect clean,//:format.checkclean.