espnet3.contract.stages.check_stage_contract
espnet3.contract.stages.check_stage_contract
espnet3.contract.stages.check_stage_contract(cls: type) → None
Raise unless cls declares a valid, complete stages tuple.
Checked once, when cls is defined (BaseSystem.__init_subclass__): stages is a non-empty tuple of StageSpec with distinct names, each naming one or more non-empty identifier config role(s) and a callable method of that name on cls. Every public callable cls defines (not starting with _) must also be one of the declared stage names - a system has no other public surface.
The “public method” scan intentionally looks only at vars(cls) (cls.__dict__), not the full MRO: it checks what cls itself newly defines or overrides, not methods it merely inherits unchanged. This is deliberate, not an oversight - each class in the hierarchy is checked once, when it is itself defined (BaseSystem is checked explicitly below; every subclass is checked by __init_subclass__), against the stages tuple in effect at that point. A method already validated on a parent does not need re-validating on every child that leaves it untouched. The one case this does not catch: a subclass that narrows stages (drops a name a parent declared) without also removing or privatizing the inherited method of that name - the now-undeclared method stays inherited, not locally defined, so this scan does not see it. No system in this codebase does that today; every subclass only adds to or overrides BaseSystem.stages, never narrows it.
- Parameters:cls – The system class being defined.
- Raises:StageContractError –
stagesis malformed, a declared stage has no matching method, or a public method is not declared as a stage.
Examples
>>> class BadSystem:
... stages = ()
>>> check_stage_contract(BadSystem)
Traceback (most recent call last):
espnet3.contract.stages.StageContractError: BadSystem.stages must ...