Skip to content

RM Steps

DummyStep

Bases: RmStepBase

Source code in passengersim/config/rm_steps.py
class DummyStep(RmStepBase):
    step_type: Literal["dummy"]

step_type instance-attribute

step_type: Literal['dummy']

RmStepBase

Bases: BaseModel

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.

Source code in passengersim/config/rm_steps.py
class RmStepBase(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)

        if inspect.isabstract(cls):
            return  # do not consider intermediate abstract base classes

        annotations = inspect.get_annotations(cls, eval_str=True)
        assert "step_type" in annotations, "step_type not in annotations"
        annotation_step_type = typing.get_origin(annotations["step_type"])
        assert annotation_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]
        if cls.__name__.lower().endswith("step"):
            assert found_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:
            assert found_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)

    @classmethod
    def as_pydantic_field(cls):
        """Pydantic field type as a union of all subclasses, discriminated on step_type."""
        if len(cls.__subclasses) > 1:
            return Annotated[
                reduce(operator.__or__, cls.__subclasses),
                Field(discriminator="step_type"),
            ]
        else:  # only the DummyStep
            return Annotated[reduce(operator.__or__, cls.__subclasses), Field()]

    def _factory(self):
        """Generate a deep copy of this RmStep."""
        return self.model_copy(deep=True)

__subclasses class-attribute

__subclasses: set[type[RmStepBase]] = set()

__init_subclass__

__init_subclass__(**kwargs)

Capture a list of all concrete subclasses, including nested levels

Source code in passengersim/config/rm_steps.py
def __init_subclass__(cls, **kwargs):
    """Capture a list of all concrete subclasses, including nested levels"""
    super().__init_subclass__(**kwargs)

    if inspect.isabstract(cls):
        return  # do not consider intermediate abstract base classes

    annotations = inspect.get_annotations(cls, eval_str=True)
    assert "step_type" in annotations, "step_type not in annotations"
    annotation_step_type = typing.get_origin(annotations["step_type"])
    assert annotation_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]
    if cls.__name__.lower().endswith("step"):
        assert found_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:
        assert found_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_field classmethod

as_pydantic_field()

Pydantic field type as a union of all subclasses, discriminated on step_type.

Source code in passengersim/config/rm_steps.py
@classmethod
def as_pydantic_field(cls):
    """Pydantic field type as a union of all subclasses, discriminated on step_type."""
    if len(cls.__subclasses) > 1:
        return Annotated[
            reduce(operator.__or__, cls.__subclasses),
            Field(discriminator="step_type"),
        ]
    else:  # only the DummyStep
        return Annotated[reduce(operator.__or__, cls.__subclasses), Field()]