classRmStepBase(BaseModel,extra="forbid"):""" Base class for RmSteps. Every non-abstract derived class must have a `step_type` class attribute, annotated with a `Literal` value type. See `DummyStep` below for an example. """__subclasses:ClassVar[set[type[RmStepBase]]]=set()def__init_subclass__(cls,**kwargs):"""Capture a list of all concrete subclasses, including nested levels"""super().__init_subclass__(**kwargs)ifinspect.isabstract(cls):return# do not consider intermediate abstract base classesannotations=inspect.get_annotations(cls,eval_str=True)assert"step_type"inannotations,"step_type not in annotations"annotation_step_type=typing.get_origin(annotations["step_type"])assertannotation_step_type==Literal,(f"annotation {annotations['step_type']} for `{cls.__name__}.step_type` "f"is not Literal but {annotation_step_type}")found_step_type=typing.get_args(annotations["step_type"])[0]ifcls.__name__.lower().endswith("step"):assertfound_step_type==cls.__name__.lower()[:-4],(f"annotation Literal value {found_step_type!r} "f"for `{cls.__name__}.step_type` is not the same as the class name "f"(omitting 'step' suffix)")else:assertfound_step_type==cls.__name__.lower(),(f"annotation Literal value {found_step_type!r} "f"for `{cls.__name__}.step_type` is not the same as the class name")cls.__subclasses.add(cls)@classmethoddefas_pydantic_field(cls):"""Pydantic field type as a union of all subclasses, discriminated on step_type."""iflen(cls.__subclasses)>1:returnAnnotated[reduce(operator.__or__,cls.__subclasses),Field(discriminator="step_type"),]else:# only the DummyStepreturnAnnotated[reduce(operator.__or__,cls.__subclasses),Field()]def_factory(self):"""Generate a deep copy of this RmStep."""returnself.model_copy(deep=True)
__subclassesclass-attribute
__subclasses:set[type[RmStepBase]]=set()
__init_subclass__
__init_subclass__(**kwargs)
Capture a list of all concrete subclasses, including nested levels
def__init_subclass__(cls,**kwargs):"""Capture a list of all concrete subclasses, including nested levels"""super().__init_subclass__(**kwargs)ifinspect.isabstract(cls):return# do not consider intermediate abstract base classesannotations=inspect.get_annotations(cls,eval_str=True)assert"step_type"inannotations,"step_type not in annotations"annotation_step_type=typing.get_origin(annotations["step_type"])assertannotation_step_type==Literal,(f"annotation {annotations['step_type']} for `{cls.__name__}.step_type` "f"is not Literal but {annotation_step_type}")found_step_type=typing.get_args(annotations["step_type"])[0]ifcls.__name__.lower().endswith("step"):assertfound_step_type==cls.__name__.lower()[:-4],(f"annotation Literal value {found_step_type!r} "f"for `{cls.__name__}.step_type` is not the same as the class name "f"(omitting 'step' suffix)")else:assertfound_step_type==cls.__name__.lower(),(f"annotation Literal value {found_step_type!r} "f"for `{cls.__name__}.step_type` is not the same as the class name")cls.__subclasses.add(cls)
as_pydantic_fieldclassmethod
as_pydantic_field()
Pydantic field type as a union of all subclasses, discriminated on step_type.
@classmethoddefas_pydantic_field(cls):"""Pydantic field type as a union of all subclasses, discriminated on step_type."""iflen(cls.__subclasses)>1:returnAnnotated[reduce(operator.__or__,cls.__subclasses),Field(discriminator="step_type"),]else:# only the DummyStepreturnAnnotated[reduce(operator.__or__,cls.__subclasses),Field()]