Context
Follow-up cleanup from PR #360 (slice #354 — multi-class launch()).
_build_entry_model decides at build time whether a registered Controller class takes an options arg, by either adding a controller field to its entry model or not. Then _instantiate_controllers re-discovers that decision at runtime via hasattr(entry, "controller"):
# src/fastcs/launch.py:184-203
for id, entry in controllers_options.items():
cls = type_map[entry.type]
if hasattr(entry, "controller"):
controller = cls(entry.controller)
else:
controller = cls()
controller.set_id(id)
controllers.append(controller)
It works, but the build-time and runtime branches are kept in sync only by duck-typing on a dynamic Pydantic model. Touching either side without the other is a silent footgun.
What to build
Thread the "this class expects options" flag forward from _build_entry_model so _instantiate_controllers reads off the same source of truth. Two reasonable shapes:
type_map: dict[str, tuple[type[Controller], bool]] keyed by discriminator, value = (class, expects_options)
- A tiny
_RegisteredClass dataclass with cls, expects_options (and any future per-class metadata)
Either way, the hasattr check goes away and _instantiate_controllers becomes data-driven.
Acceptance criteria
Context
Follow-up cleanup from PR #360 (slice #354 — multi-class
launch())._build_entry_modeldecides at build time whether a registered Controller class takes an options arg, by either adding acontrollerfield to its entry model or not. Then_instantiate_controllersre-discovers that decision at runtime viahasattr(entry, "controller"):It works, but the build-time and runtime branches are kept in sync only by duck-typing on a dynamic Pydantic model. Touching either side without the other is a silent footgun.
What to build
Thread the "this class expects options" flag forward from
_build_entry_modelso_instantiate_controllersreads off the same source of truth. Two reasonable shapes:type_map: dict[str, tuple[type[Controller], bool]]keyed by discriminator, value = (class, expects_options)_RegisteredClassdataclass withcls,expects_options(and any future per-class metadata)Either way, the
hasattrcheck goes away and_instantiate_controllersbecomes data-driven.Acceptance criteria
_instantiate_controllersno longer useshasattron the entry modeltests/test_launch.pycases still green; no behaviour change for users