Skip to content

Configuration

configuration

Representations of parsed configuration files.

AxesGroupModel = dict[IdentifierString, AxisModel] module-attribute

Type alias for the axes block in a configuration file.

AxisModel = Annotated[ContinuousAxisModel | CategoricalAxisModel, Field(discriminator='kind')] module-attribute

Discriminated union of all axis configuration models.

ModuleConfigurationValue = ModuleBase | str module-attribute

A module configuration value, either expanded config or shorthand text.

ModuleGroupModel = Annotated[dict[IdentifierString, ModuleConfigurationValue], BeforeValidator(_to_default_dict)] module-attribute

Module group configuration model for flepimop2.

ParameterConfigurationValue = Annotated[ModuleConfigurationValue, BeforeValidator(_coerce_parameter_configuration_value)] module-attribute

A parameter value, including bare numeric shorthand for fixed parameters.

ParameterGroupModel = Annotated[dict[IdentifierString, ParameterConfigurationValue], BeforeValidator(_to_default_dict)] module-attribute

Parameter group configuration model for flepimop2.

CategoricalAxisModel

Bases: BaseModel

Configuration model for a categorical axis.

Attributes:

Name Type Description
kind Literal['categorical']

Discriminator field; always "categorical".

labels tuple[str, ...]

Ordered sequence of string category labels.

values tuple[int, ...]

Integer values associated with each label (e.g. for ordinal data or spline interpolation). Must be the same length as labels. Defaults to 1, 2, ..., N where N is len(labels).

Examples:

>>> from flepimop2.configuration._axes import CategoricalAxisModel
>>> CategoricalAxisModel(labels=("foo", "bar", "baz"))
CategoricalAxisModel(kind='categorical', labels=('foo', 'bar', 'baz'), values=(1, 2, 3))

ConfigurationModel

Bases: YamlSerializableBaseModel

Configuration model for flepimop2.

This model serves as the parent container for a parsed configuration file.

Attributes:

Name Type Description
name str | None

An optional name for the configuration.

engines ModuleGroupModel

A dictionary of engine configurations.

systems ModuleGroupModel

A dictionary of system configurations.

backends ModuleGroupModel

A dictionary of backend configurations.

process ModuleGroupModel

A dictionary of process configurations.

parameters ParameterGroupModel

A dictionary of parameter configurations.

simulate dict[IdentifierString, SimulateSpecificationModel]

A dictionary of simulation configurations.

patch(other, *, conflict=PatchConflictMode.ERROR)

Patch this configuration with another configuration.

This method treats other as the incoming patch. Top-level sections are merged entry-by-entry; when both entries are ModuleBase instances their patch() implementations are used.

Parameters:

Name Type Description Default
other Self

The patch to apply to this configuration.

required
conflict PatchConflictMode

How to handle duplicate top-level entry names.

ERROR

Returns:

Type Description
Self

The patched configuration.

Raises:

Type Description
ValueError

If there are sub-top level key conflicts and conflict is PatchConflictMode.ERROR.

Source code in src/flepimop2/configuration/_configuration.py
def patch(
    self,
    other: Self,
    *,
    conflict: PatchConflictMode = PatchConflictMode.ERROR,
) -> Self:
    """
    Patch this configuration with another configuration.

    This method treats `other` as the incoming patch. Top-level sections are
    merged entry-by-entry; when both entries are `ModuleBase` instances their
    `patch()` implementations are used.

    Args:
        other: The patch to apply to this configuration.
        conflict: How to handle duplicate top-level entry names.

    Returns:
        The patched configuration.

    Raises:
        ValueError: If there are sub-top level key conflicts and `conflict` is
            `PatchConflictMode.ERROR`.
    """
    section_conflicts = {
        section_name: self._collect_patch_conflicts(
            getattr(self, section_name),
            getattr(other, section_name),
        )
        for section_name in (
            "axes",
            "engines",
            "systems",
            "backends",
            "process",
            "parameters",
            "scenarios",
            "simulate",
        )
    }
    section_conflicts = {
        section_name: conflicts
        for section_name, conflicts in section_conflicts.items()
        if conflicts
    }
    if conflict is PatchConflictMode.ERROR and section_conflicts:
        details = "; ".join(
            f"{section_name}={conflicts!r}"
            for section_name, conflicts in section_conflicts.items()
        )
        msg = (
            "Cannot patch configuration under conflict='error'; duplicate "
            f"section keys: {details}."
        )
        raise ValueError(msg)

    sections = {
        "axes": self._patch_section(
            self.axes,
            other.axes,
            conflict=conflict,
        ),
        "engines": self._patch_section(
            self.engines,
            other.engines,
            conflict=conflict,
        ),
        "systems": self._patch_section(
            self.systems,
            other.systems,
            conflict=conflict,
        ),
        "backends": self._patch_section(
            self.backends,
            other.backends,
            conflict=conflict,
        ),
        "process": self._patch_section(
            self.process,
            other.process,
            conflict=conflict,
        ),
        "parameters": self._patch_section(
            self.parameters,
            other.parameters,
            conflict=conflict,
        ),
        "scenarios": self._patch_section(
            self.scenarios,
            other.scenarios,
            conflict=conflict,
        ),
        "simulate": self._patch_section(
            self.simulate,
            other.simulate,
            conflict=conflict,
        ),
    }

    name_payload: dict[str, str | None] = {}
    if "name" in other.model_fields_set:
        name_payload["name"] = other.name
    elif "name" in self.model_fields_set:
        name_payload["name"] = self.name

    return type(self).model_validate({**name_payload, **sections})

to_yaml_data()

Convert the configuration into its normalized YAML representation.

This preserves parsing/patching semantics while allowing the emitted YAML to use a more compact, user-facing structure.

Returns:

Type Description
object

A YAML-ready representation of the configuration.

Source code in src/flepimop2/configuration/_configuration.py
def to_yaml_data(self) -> object:
    """
    Convert the configuration into its normalized YAML representation.

    This preserves parsing/patching semantics while allowing the emitted
    YAML to use a more compact, user-facing structure.

    Returns:
        A YAML-ready representation of the configuration.
    """
    data = super().to_yaml_data()
    if not isinstance(data, dict):
        return data

    if not data.get("name"):
        data.pop("name", None)

    if self.parameters:
        build_parameter = import_module("flepimop2.parameter.abc").build
        data["parameters"] = {
            name: build_parameter(parameter).to_yaml_data()
            if isinstance(parameter, ModuleBase | str)
            else parameter
            for name, parameter in self.parameters.items()
        }

    ordered_data = {}
    if name := data.get("name"):
        ordered_data["name"] = name

    for section_name in _CONFIGURATION_SECTION_ORDER[1:]:
        if not (section := data.get(section_name)):
            continue
        if (
            section_name in _LIST_VIEW_SECTIONS
            and isinstance(section, dict)
            and len(section) == 1
            and "default" in section
        ):
            section = [section["default"]]
        ordered_data[section_name] = section

    ordered_data.update({
        key: value
        for key, value in data.items()
        if key not in _CONFIGURATION_SECTION_ORDER
    })
    return ordered_data

ContinuousAxisModel

Bases: BaseModel

Configuration model for a continuous (float) axis.

Attributes:

Name Type Description
kind Literal['continuous']

Discriminator field; always "continuous".

domain tuple[float, float]

A [lo, hi] pair of floats defining the axis extent.

size int

Number of evenly-spaced points. Required for continuous axes.

spacing Literal['linear', 'log']

Whether points are spaced "linear" or "log"-uniformly.

Examples:

>>> from flepimop2.configuration._axes import ContinuousAxisModel
>>> ContinuousAxisModel(domain=(0.0, 12.0), size=5, spacing="linear")
ContinuousAxisModel(kind='continuous', domain=(0.0, 12.0), size=5, spacing='linear')

SimulateSpecificationModel

Bases: BaseModel

Model for specifying a simulation for flepimop2.

Attributes:

Name Type Description
engine IdentifierString

The name of the engine to use for the simulation.

system IdentifierString

The name of the system to simulate.

backend IdentifierString

The name of the backend to use for the simulation.

times RangeSpec

A list of time points at which to perform the simulation.

params dict[str, float] | None

Optional dictionary of parameters for the simulation.

scenario IdentifierString | None

Optional name of the scenario to use for the simulation.

t_eval property

Get the evaluation times as a NumPy array.

Returns:

Type Description
Float64NDArray

A NumPy array of evaluation times.

to_yaml_data()

Convert the simulation specification into YAML-ready Python objects.

Returns:

Type Description
object

A YAML-ready representation of the simulation specification.

Source code in src/flepimop2/configuration/_simulate.py
def to_yaml_data(self) -> object:
    """
    Convert the simulation specification into YAML-ready Python objects.

    Returns:
        A YAML-ready representation of the simulation specification.
    """
    data = _model_to_yaml_mapping(self)
    if not data.get("params"):
        data.pop("params", None)
    if data.get("scenario") is None:
        data.pop("scenario", None)
    return data

YamlSerializableBaseModel

Bases: BaseModel

Base model with YAML serialization support.

Example

class DemoModel(YamlSerializableBaseModel): ... name: str payload = DemoModel(name="demo").safe_dump() print(payload, end="") name: demo DemoModel.safe_load(payload) DemoModel(name='demo')

from_yaml(file, encoding='utf-8', **kwargs) classmethod

Deserialize a YAML file to an instance of the model.

Parameters:

Name Type Description Default
file Path

Path to the YAML file to read.

required
encoding str

Encoding of the YAML file.

'utf-8'
**kwargs Any

Additional keyword arguments to pass to Path.read_text.

{}

Returns:

Type Description
Self

An instance of the model.

Source code in src/flepimop2/configuration/_yaml.py
@classmethod
def from_yaml(cls, file: Path, encoding: str = "utf-8", **kwargs: Any) -> Self:
    """
    Deserialize a YAML file to an instance of the model.

    Args:
        file: Path to the YAML file to read.
        encoding: Encoding of the YAML file.
        **kwargs: Additional keyword arguments to pass to `Path.read_text`.

    Returns:
        An instance of the model.
    """
    return cls.safe_load(file.read_text(encoding=encoding, **kwargs))

safe_dump()

Serialize the model to a YAML document.

Returns:

Type Description
str

The serialized YAML document.

Examples:

>>> class DemoModel(YamlSerializableBaseModel):
...     name: str
>>> print(DemoModel(name="demo").safe_dump(), end="")
name: demo
Source code in src/flepimop2/configuration/_yaml.py
def safe_dump(self) -> str:
    """
    Serialize the model to a YAML document.

    Returns:
        The serialized YAML document.

    Examples:
        >>> class DemoModel(YamlSerializableBaseModel):
        ...     name: str
        >>> print(DemoModel(name="demo").safe_dump(), end="")
        name: demo
    """
    return yaml_dump(
        self.to_yaml_data(),
        Dumper=Flepimop2YamlDumper,
        default_flow_style=False,
        sort_keys=False,
    )

safe_load(contents) classmethod

Deserialize YAML text to an instance of the model.

Parameters:

Name Type Description Default
contents str

The YAML document to deserialize.

required

Returns:

Type Description
Self

An instance of the model.

Examples:

>>> class DemoModel(YamlSerializableBaseModel):
...     name: str
>>> DemoModel.safe_load("name: demo")
DemoModel(name='demo')
Source code in src/flepimop2/configuration/_yaml.py
@classmethod
def safe_load(cls, contents: str) -> Self:
    """
    Deserialize YAML text to an instance of the model.

    Args:
        contents: The YAML document to deserialize.

    Returns:
        An instance of the model.

    Examples:
        >>> class DemoModel(YamlSerializableBaseModel):
        ...     name: str
        >>> DemoModel.safe_load("name: demo")
        DemoModel(name='demo')
    """
    return cls.model_validate(yaml_safe_load(contents))

to_yaml(file, encoding='utf-8', **kwargs)

Serialize the model to a YAML file.

Parameters:

Name Type Description Default
file Path

Path to the YAML file to write.

required
encoding str

Encoding of the YAML file.

'utf-8'
**kwargs Any

Additional keyword arguments to pass to Path.write_text.

{}
Source code in src/flepimop2/configuration/_yaml.py
def to_yaml(self, file: Path, encoding: str = "utf-8", **kwargs: Any) -> None:
    """
    Serialize the model to a YAML file.

    Args:
        file: Path to the YAML file to write.
        encoding: Encoding of the YAML file.
        **kwargs: Additional keyword arguments to pass to `Path.write_text`.
    """
    file.write_text(self.safe_dump(), encoding=encoding, **kwargs)

to_yaml_data()

Convert the model into YAML-ready Python objects.

Subclasses can override this method to control both the serialized representation and any YAML style wrappers used by the project dumper.

Returns:

Type Description
object

A YAML-ready representation of the model.

Examples:

>>> class DemoModel(YamlSerializableBaseModel):
...     name: str
...     dims: tuple[int, int]
>>> DemoModel(name="demo", dims=(1, 2)).to_yaml_data()
{'name': 'demo', 'dims': [1, 2]}
Source code in src/flepimop2/configuration/_yaml.py
def to_yaml_data(self) -> object:
    """
    Convert the model into YAML-ready Python objects.

    Subclasses can override this method to control both the serialized
    representation and any YAML style wrappers used by the project dumper.

    Returns:
        A YAML-ready representation of the model.

    Examples:
        >>> class DemoModel(YamlSerializableBaseModel):
        ...     name: str
        ...     dims: tuple[int, int]
        >>> DemoModel(name="demo", dims=(1, 2)).to_yaml_data()
        {'name': 'demo', 'dims': [1, 2]}
    """
    return _model_to_yaml_mapping(self)

yaml_mapping(values=(), *, flow_style=None)

Wrap a mapping with YAML style metadata.

Module authors can return this from to_yaml_data() when a specific subtree should use flow style, e.g. {a: 1, b: 2}.

Returns:

Type Description
YamlFormattedMapping[K, V]

A mapping wrapper carrying the requested YAML flow-style hint.

Examples:

>>> wrapped = yaml_mapping({"kind": "demo"}, flow_style=True)
>>> dict(wrapped)
{'kind': 'demo'}
>>> wrapped.flow_style
True
Source code in src/flepimop2/configuration/_yaml.py
def yaml_mapping(
    values: Mapping[K, V] | Iterable[tuple[K, V]] = (),
    *,
    flow_style: bool | None = None,
) -> YamlFormattedMapping[K, V]:
    """
    Wrap a mapping with YAML style metadata.

    Module authors can return this from `to_yaml_data()` when a specific
    subtree should use flow style, e.g. `{a: 1, b: 2}`.

    Returns:
        A mapping wrapper carrying the requested YAML flow-style hint.

    Examples:
        >>> wrapped = yaml_mapping({"kind": "demo"}, flow_style=True)
        >>> dict(wrapped)
        {'kind': 'demo'}
        >>> wrapped.flow_style
        True
    """
    return YamlFormattedMapping(values, flow_style=flow_style)

yaml_sequence(values=(), *, flow_style=None)

Wrap a sequence with YAML style metadata.

Module authors can return this from to_yaml_data() when a specific subtree should use flow style, e.g. [a, b, c].

Returns:

Type Description
YamlFormattedSequence[T]

A sequence wrapper carrying the requested YAML flow-style hint.

Examples:

>>> wrapped = yaml_sequence(("a", "b"), flow_style=True)
>>> list(wrapped)
['a', 'b']
>>> wrapped.flow_style
True
Source code in src/flepimop2/configuration/_yaml.py
def yaml_sequence(
    values: Iterable[T] = (),
    *,
    flow_style: bool | None = None,
) -> YamlFormattedSequence[T]:
    """
    Wrap a sequence with YAML style metadata.

    Module authors can return this from `to_yaml_data()` when a specific
    subtree should use flow style, e.g. `[a, b, c]`.

    Returns:
        A sequence wrapper carrying the requested YAML flow-style hint.

    Examples:
        >>> wrapped = yaml_sequence(("a", "b"), flow_style=True)
        >>> list(wrapped)
        ['a', 'b']
        >>> wrapped.flow_style
        True
    """
    return YamlFormattedSequence(values, flow_style=flow_style)