Skip to content

marshmallow

frequenz.quantities.experimental.marshmallow ¤

Custom marshmallow fields and schema.

This module provides custom marshmallow fields for quantities and a QuantitySchema class to be used as base schema for dataclasses containing quantities.

Danger

This module contains experimental features for which the API is not yet stable.

Any module or class in this package may be removed or changed in a future release, even in minor or patch releases.

Attributes¤

frequenz.quantities.experimental.marshmallow.QUANTITY_FIELD_CLASSES module-attribute ¤

The mapping from Quantity subclasses to their corresponding field subclasses.

This mapping is used in QuantitySchema.TYPE_MAPPING to determine the correct field class for each Quantity subclass.

frequenz.quantities.experimental.marshmallow.serialize_as_string_default module-attribute ¤

serialize_as_string_default: ContextVar[bool] = ContextVar(
    "serialize_as_string_default", default=False
)

The context variable controlling the default serialization format for quantities.

If True, quantities are serialized as strings with units; if False, as floats. This can be overridden on a per-field basis using the serialize_as_string metadata attribute.

Classes¤

frequenz.quantities.experimental.marshmallow.ApparentPowerField ¤

Bases: _QuantityField

A custom field for ApparentPower objects.

Source code in src/frequenz/quantities/experimental/marshmallow.py
class ApparentPowerField(_QuantityField):
    """A custom field for [`ApparentPower`][frequenz.quantities.ApparentPower] objects."""

    field_type = ApparentPower
Methods:¤
__init__ ¤
__init__(*args: Any, **kwargs: Any) -> None

Initialize the field.

Source code in src/frequenz/quantities/experimental/marshmallow.py
def __init__(self, *args: Any, **kwargs: Any) -> None:
    """Initialize the field."""
    self.serialize_as_string_override = kwargs.pop("serialize_as_string", None)
    super().__init__(*args, **kwargs)

frequenz.quantities.experimental.marshmallow.CurrentField ¤

Bases: _QuantityField

A custom field for Current objects.

Source code in src/frequenz/quantities/experimental/marshmallow.py
class CurrentField(_QuantityField):
    """A custom field for [`Current`][frequenz.quantities.Current] objects."""

    field_type = Current
Methods:¤
__init__ ¤
__init__(*args: Any, **kwargs: Any) -> None

Initialize the field.

Source code in src/frequenz/quantities/experimental/marshmallow.py
def __init__(self, *args: Any, **kwargs: Any) -> None:
    """Initialize the field."""
    self.serialize_as_string_override = kwargs.pop("serialize_as_string", None)
    super().__init__(*args, **kwargs)

frequenz.quantities.experimental.marshmallow.EnergyField ¤

Bases: _QuantityField

A custom field for Energy objects.

Source code in src/frequenz/quantities/experimental/marshmallow.py
class EnergyField(_QuantityField):
    """A custom field for [`Energy`][frequenz.quantities.Energy] objects."""

    field_type = Energy
Methods:¤
__init__ ¤
__init__(*args: Any, **kwargs: Any) -> None

Initialize the field.

Source code in src/frequenz/quantities/experimental/marshmallow.py
def __init__(self, *args: Any, **kwargs: Any) -> None:
    """Initialize the field."""
    self.serialize_as_string_override = kwargs.pop("serialize_as_string", None)
    super().__init__(*args, **kwargs)

frequenz.quantities.experimental.marshmallow.FrequencyField ¤

Bases: _QuantityField

A custom field for Frequency objects.

Source code in src/frequenz/quantities/experimental/marshmallow.py
class FrequencyField(_QuantityField):
    """A custom field for [`Frequency`][frequenz.quantities.Frequency] objects."""

    field_type = Frequency
Methods:¤
__init__ ¤
__init__(*args: Any, **kwargs: Any) -> None

Initialize the field.

Source code in src/frequenz/quantities/experimental/marshmallow.py
def __init__(self, *args: Any, **kwargs: Any) -> None:
    """Initialize the field."""
    self.serialize_as_string_override = kwargs.pop("serialize_as_string", None)
    super().__init__(*args, **kwargs)

frequenz.quantities.experimental.marshmallow.PercentageField ¤

Bases: _QuantityField

A custom field for Percentage objects.

Source code in src/frequenz/quantities/experimental/marshmallow.py
class PercentageField(_QuantityField):
    """A custom field for [`Percentage`][frequenz.quantities.Percentage] objects."""

    field_type = Percentage
Methods:¤
__init__ ¤
__init__(*args: Any, **kwargs: Any) -> None

Initialize the field.

Source code in src/frequenz/quantities/experimental/marshmallow.py
def __init__(self, *args: Any, **kwargs: Any) -> None:
    """Initialize the field."""
    self.serialize_as_string_override = kwargs.pop("serialize_as_string", None)
    super().__init__(*args, **kwargs)

frequenz.quantities.experimental.marshmallow.PowerField ¤

Bases: _QuantityField

A custom field for Power objects.

Source code in src/frequenz/quantities/experimental/marshmallow.py
class PowerField(_QuantityField):
    """A custom field for [`Power`][frequenz.quantities.Power] objects."""

    field_type = Power
Methods:¤
__init__ ¤
__init__(*args: Any, **kwargs: Any) -> None

Initialize the field.

Source code in src/frequenz/quantities/experimental/marshmallow.py
def __init__(self, *args: Any, **kwargs: Any) -> None:
    """Initialize the field."""
    self.serialize_as_string_override = kwargs.pop("serialize_as_string", None)
    super().__init__(*args, **kwargs)

frequenz.quantities.experimental.marshmallow.QuantitySchema ¤

Bases: Schema

A schema for quantities.

Example:

from dataclasses import dataclass, field
from marshmallow_dataclass import class_schema
from marshmallow.validate import Range
from frequenz.quantities import Percentage
from frequenz.quantities.experimental.marshmallow import (
    QuantitySchema,
    serialize_as_string_default,
)

@dataclass
class Config:
    percentage_always_as_string: Percentage = field(
        default_factory=lambda: Percentage.from_percent(25.0),
        metadata={
            "metadata": {
                "description": "A percentage field",
            },
            "validate": Range(Percentage.zero(), Percentage.from_percent(100.0)),
            "serialize_as_string": True,
        },
    )

    percentage_always_as_float: Percentage = field(
        default_factory=lambda: Percentage.from_percent(25.0),
        metadata={
            "metadata": {
                "description": "A percentage field",
            },
            "validate": Range(Percentage.zero(), Percentage.from_percent(100.0)),
            "serialize_as_string": False,
        },
    )

    percentage_serialized_as_schema_default: Percentage = field(
        default_factory=lambda: Percentage.from_percent(25.0),
        metadata={
            "metadata": {
                "description": "A percentage field",
            },
            "validate": Range(Percentage.zero(), Percentage.from_percent(100.0)),
        },
    )

config_obj = Config()
Schema = class_schema(Config, base_schema=QuantitySchema)
schema = Schema()

# Default serialization (as float)
result = schema.dump(config_obj)
assert result["percentage_serialized_as_schema_default"] == 25.0

# Override default serialization to string
serialize_as_string_default.set(True)
result = schema.dump(config_obj)
assert result["percentage_serialized_as_schema_default"] == "25.0 %"
serialize_as_string_default.set(False) # Reset context

# Per-field configuration always takes precedence
assert result["percentage_always_as_string"] == "25.0 %"
assert result["percentage_always_as_float"] == 25.0

Source code in src/frequenz/quantities/experimental/marshmallow.py
class QuantitySchema(Schema):
    """A schema for quantities.

    Example:
    ```python
    from dataclasses import dataclass, field
    from marshmallow_dataclass import class_schema
    from marshmallow.validate import Range
    from frequenz.quantities import Percentage
    from frequenz.quantities.experimental.marshmallow import (
        QuantitySchema,
        serialize_as_string_default,
    )

    @dataclass
    class Config:
        percentage_always_as_string: Percentage = field(
            default_factory=lambda: Percentage.from_percent(25.0),
            metadata={
                "metadata": {
                    "description": "A percentage field",
                },
                "validate": Range(Percentage.zero(), Percentage.from_percent(100.0)),
                "serialize_as_string": True,
            },
        )

        percentage_always_as_float: Percentage = field(
            default_factory=lambda: Percentage.from_percent(25.0),
            metadata={
                "metadata": {
                    "description": "A percentage field",
                },
                "validate": Range(Percentage.zero(), Percentage.from_percent(100.0)),
                "serialize_as_string": False,
            },
        )

        percentage_serialized_as_schema_default: Percentage = field(
            default_factory=lambda: Percentage.from_percent(25.0),
            metadata={
                "metadata": {
                    "description": "A percentage field",
                },
                "validate": Range(Percentage.zero(), Percentage.from_percent(100.0)),
            },
        )

    config_obj = Config()
    Schema = class_schema(Config, base_schema=QuantitySchema)
    schema = Schema()

    # Default serialization (as float)
    result = schema.dump(config_obj)
    assert result["percentage_serialized_as_schema_default"] == 25.0

    # Override default serialization to string
    serialize_as_string_default.set(True)
    result = schema.dump(config_obj)
    assert result["percentage_serialized_as_schema_default"] == "25.0 %"
    serialize_as_string_default.set(False) # Reset context

    # Per-field configuration always takes precedence
    assert result["percentage_always_as_string"] == "25.0 %"
    assert result["percentage_always_as_float"] == 25.0
    ```
    """

    TYPE_MAPPING: dict[type, type[Field[Any]]] = QUANTITY_FIELD_CLASSES
    """The field class to use for each [`Quantity`][frequenz.quantities.Quantity] subclass."""
Attributes¤
TYPE_MAPPING class-attribute instance-attribute ¤
TYPE_MAPPING: dict[type, type[Field[Any]]] = (
    QUANTITY_FIELD_CLASSES
)

The field class to use for each Quantity subclass.

frequenz.quantities.experimental.marshmallow.ReactivePowerField ¤

Bases: _QuantityField

A custom field for ReactivePower objects.

Source code in src/frequenz/quantities/experimental/marshmallow.py
class ReactivePowerField(_QuantityField):
    """A custom field for [`ReactivePower`][frequenz.quantities.ReactivePower] objects."""

    field_type = ReactivePower
Methods:¤
__init__ ¤
__init__(*args: Any, **kwargs: Any) -> None

Initialize the field.

Source code in src/frequenz/quantities/experimental/marshmallow.py
def __init__(self, *args: Any, **kwargs: Any) -> None:
    """Initialize the field."""
    self.serialize_as_string_override = kwargs.pop("serialize_as_string", None)
    super().__init__(*args, **kwargs)

frequenz.quantities.experimental.marshmallow.TemperatureField ¤

Bases: _QuantityField

A custom field for Temperature objects.

Source code in src/frequenz/quantities/experimental/marshmallow.py
class TemperatureField(_QuantityField):
    """A custom field for [`Temperature`][frequenz.quantities.Temperature] objects."""

    field_type = Temperature
Methods:¤
__init__ ¤
__init__(*args: Any, **kwargs: Any) -> None

Initialize the field.

Source code in src/frequenz/quantities/experimental/marshmallow.py
def __init__(self, *args: Any, **kwargs: Any) -> None:
    """Initialize the field."""
    self.serialize_as_string_override = kwargs.pop("serialize_as_string", None)
    super().__init__(*args, **kwargs)

frequenz.quantities.experimental.marshmallow.VoltageField ¤

Bases: _QuantityField

A custom field for Voltage objects.

Source code in src/frequenz/quantities/experimental/marshmallow.py
class VoltageField(_QuantityField):
    """A custom field for [`Voltage`][frequenz.quantities.Voltage] objects."""

    field_type = Voltage
Methods:¤
__init__ ¤
__init__(*args: Any, **kwargs: Any) -> None

Initialize the field.

Source code in src/frequenz/quantities/experimental/marshmallow.py
def __init__(self, *args: Any, **kwargs: Any) -> None:
    """Initialize the field."""
    self.serialize_as_string_override = kwargs.pop("serialize_as_string", None)
    super().__init__(*args, **kwargs)