Skip to content

feat(sensors): open lidar model factory and make LidarModel generic in its parameters - #178

Merged
janickm merged 1 commit into
NVIDIA:mainfrom
janickm:dev/janickm/lidar-model-factory
Sep 9, 2026
Merged

feat(sensors): open lidar model factory and make LidarModel generic in its parameters#178
janickm merged 1 commit into
NVIDIA:mainfrom
janickm:dev/janickm/lidar-model-factory

Conversation

@janickm

@janickm janickm commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator

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_parameters was a static method on the abstract base holding a closed if/elif table over every concrete subclass — and StructuredLidarModel shadowed 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 via functools.singledispatch, with maybe_lidar_model_from_parameters() for the optional case and register_lidar_model for out-of-tree models. Both static methods are kept as deprecated forwarders.

Generic model

LidarModel declared no get_parameters — only the concrete leaf did, so a LidarModel-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 common LidarModel[...] supertype. Plain unparameterized LidarModel annotations and isinstance checks are unaffected.

Serialization on the abstract base

BaseLidarModelParameters was not even an ABC (@dataclass class BaseLidarModelParameters: pass); the JSON mixin and type() 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_parameters keeps the base's parameter type rather than narrowing it to the structured parameters. Narrowing it is a contravariance violation that ty rejects 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, ty aspect clean, //:format.check clean.

Comment thread ncore/impl/sensors/lidar_test.py
@janickm janickm self-assigned this Sep 9, 2026
@janickm
janickm force-pushed the dev/janickm/lidar-model-factory branch from 3dde086 to fe34be6 Compare September 9, 2026 08:58
@janickm
janickm added this pull request to the merge queue Sep 9, 2026
@janickm
janickm removed this pull request from the merge queue due to a manual request Sep 9, 2026
… 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
janickm force-pushed the dev/janickm/lidar-model-factory branch from fe34be6 to 6af06a6 Compare September 9, 2026 12:06
@janickm
janickm added this pull request to the merge queue Sep 9, 2026
Merged via the queue into NVIDIA:main with commit b2afc10 Sep 9, 2026
5 checks passed
@janickm
janickm deleted the dev/janickm/lidar-model-factory branch September 9, 2026 13:24
@janickm janickm mentioned this pull request Sep 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant