Edit on GitHub

serde.yaml

Serialize and Deserialize in YAML format. This module depends on pyyaml package.

  1"""
  2Serialize and Deserialize in YAML format. This module depends on
  3[pyyaml](https://pypi.org/project/PyYAML/) package.
  4"""
  5
  6from typing import Any, overload
  7
  8import yaml
  9
 10from .compat import SerdeError, T
 11from .de import Deserializer, from_dict
 12from .se import Serializer, to_dict
 13
 14__all__ = ["from_yaml", "to_yaml", "deserialize_yaml_numbers"]
 15
 16
 17class YamlSerializer(Serializer[str]):
 18    @classmethod
 19    def serialize(cls, obj: Any, **opts: Any) -> str:
 20        return yaml.safe_dump(obj, **opts)  # type: ignore
 21
 22
 23class YamlDeserializer(Deserializer[str]):
 24    @classmethod
 25    def deserialize(cls, data: str, **opts: Any) -> Any:
 26        return yaml.safe_load(data, **opts)
 27
 28
 29def deserialize_yaml_numbers(value: Any) -> float:
 30    """
 31    Convert YAML numbers to float, accepting ints/floats and numeric strings (e.g. scientific
 32    notation). Rejects booleans and non-numeric inputs.
 33    """
 34    if isinstance(value, bool):
 35        raise SerdeError(f"Expected YAML number but got boolean {value!r}")
 36    if isinstance(value, (int, float)):
 37        return float(value)
 38    if isinstance(value, str):
 39        try:
 40            return float(value)
 41        except ValueError as e:
 42            raise SerdeError(f"Could not convert YAML number string {value!r} to float") from e
 43    raise SerdeError(f"Expected YAML number but got {type(value).__name__}")
 44
 45
 46def to_yaml(
 47    obj: Any,
 48    cls: Any | None = None,
 49    se: type[Serializer[str]] = YamlSerializer,
 50    reuse_instances: bool = False,
 51    convert_sets: bool = True,
 52    skip_none: bool = False,
 53    **opts: Any,
 54) -> str:
 55    """
 56    Serialize the object into YAML.
 57
 58    You can pass any serializable `obj`. If you supply keyword arguments other than `se`,
 59    they will be passed in `yaml.safe_dump` function.
 60
 61    * `skip_none`: When set to True, any field in the class with a None value is excluded from the
 62    serialized output. Defaults to False.
 63
 64    If you want to use the other yaml package, you can subclass `YamlSerializer` and implement
 65    your own logic.
 66    """
 67    return se.serialize(
 68        to_dict(
 69            obj,
 70            c=cls,
 71            reuse_instances=reuse_instances,
 72            convert_sets=convert_sets,
 73            skip_none=skip_none,
 74        ),
 75        **opts,
 76    )
 77
 78
 79@overload
 80def from_yaml(
 81    c: type[T],
 82    s: str,
 83    de: type[Deserializer[str]] = YamlDeserializer,
 84    coerce_numbers: bool = True,
 85    **opts: Any,
 86) -> T: ...
 87
 88
 89# For Union, Optional etc.
 90@overload
 91def from_yaml(
 92    c: Any,
 93    s: str,
 94    de: type[Deserializer[str]] = YamlDeserializer,
 95    coerce_numbers: bool = True,
 96    **opts: Any,
 97) -> Any: ...
 98
 99
100def from_yaml(
101    c: Any,
102    s: str,
103    de: type[Deserializer[str]] = YamlDeserializer,
104    coerce_numbers: bool = True,
105    **opts: Any,
106) -> Any:
107    """
108    `c` is a class object and `s` is YAML string. If you supply keyword arguments other than `de`,
109    they will be passed in `yaml.safe_load` function.
110
111    * `coerce_numbers`: When True (default), numeric YAML scalars or numeric strings (e.g.
112      "1e-3") are coerced to floats when the target type is float. Booleans are rejected.
113
114    If you want to use the other yaml package, you can subclass `YamlDeserializer` and implement
115    your own logic.
116    """
117    deserialize_numbers = deserialize_yaml_numbers if coerce_numbers else None
118    return from_dict(
119        c,
120        de.deserialize(s, **opts),
121        reuse_instances=False,
122        deserialize_numbers=deserialize_numbers,
123    )
def from_yaml( c: Any, s: str, de: type[serde.de.Deserializer[str]] = <class 'serde.yaml.YamlDeserializer'>, coerce_numbers: bool = True, **opts: Any) -> Any:
101def from_yaml(
102    c: Any,
103    s: str,
104    de: type[Deserializer[str]] = YamlDeserializer,
105    coerce_numbers: bool = True,
106    **opts: Any,
107) -> Any:
108    """
109    `c` is a class object and `s` is YAML string. If you supply keyword arguments other than `de`,
110    they will be passed in `yaml.safe_load` function.
111
112    * `coerce_numbers`: When True (default), numeric YAML scalars or numeric strings (e.g.
113      "1e-3") are coerced to floats when the target type is float. Booleans are rejected.
114
115    If you want to use the other yaml package, you can subclass `YamlDeserializer` and implement
116    your own logic.
117    """
118    deserialize_numbers = deserialize_yaml_numbers if coerce_numbers else None
119    return from_dict(
120        c,
121        de.deserialize(s, **opts),
122        reuse_instances=False,
123        deserialize_numbers=deserialize_numbers,
124    )

c is a class object and s is YAML string. If you supply keyword arguments other than de, they will be passed in yaml.safe_load function.

  • coerce_numbers: When True (default), numeric YAML scalars or numeric strings (e.g. "1e-3") are coerced to floats when the target type is float. Booleans are rejected.

If you want to use the other yaml package, you can subclass YamlDeserializer and implement your own logic.

def to_yaml( obj: Any, cls: typing.Any | None = None, se: type[serde.se.Serializer[str]] = <class 'serde.yaml.YamlSerializer'>, reuse_instances: bool = False, convert_sets: bool = True, skip_none: bool = False, **opts: Any) -> str:
47def to_yaml(
48    obj: Any,
49    cls: Any | None = None,
50    se: type[Serializer[str]] = YamlSerializer,
51    reuse_instances: bool = False,
52    convert_sets: bool = True,
53    skip_none: bool = False,
54    **opts: Any,
55) -> str:
56    """
57    Serialize the object into YAML.
58
59    You can pass any serializable `obj`. If you supply keyword arguments other than `se`,
60    they will be passed in `yaml.safe_dump` function.
61
62    * `skip_none`: When set to True, any field in the class with a None value is excluded from the
63    serialized output. Defaults to False.
64
65    If you want to use the other yaml package, you can subclass `YamlSerializer` and implement
66    your own logic.
67    """
68    return se.serialize(
69        to_dict(
70            obj,
71            c=cls,
72            reuse_instances=reuse_instances,
73            convert_sets=convert_sets,
74            skip_none=skip_none,
75        ),
76        **opts,
77    )

Serialize the object into YAML.

You can pass any serializable obj. If you supply keyword arguments other than se, they will be passed in yaml.safe_dump function.

  • skip_none: When set to True, any field in the class with a None value is excluded from the serialized output. Defaults to False.

If you want to use the other yaml package, you can subclass YamlSerializer and implement your own logic.

def deserialize_yaml_numbers(value: Any) -> float:
30def deserialize_yaml_numbers(value: Any) -> float:
31    """
32    Convert YAML numbers to float, accepting ints/floats and numeric strings (e.g. scientific
33    notation). Rejects booleans and non-numeric inputs.
34    """
35    if isinstance(value, bool):
36        raise SerdeError(f"Expected YAML number but got boolean {value!r}")
37    if isinstance(value, (int, float)):
38        return float(value)
39    if isinstance(value, str):
40        try:
41            return float(value)
42        except ValueError as e:
43            raise SerdeError(f"Could not convert YAML number string {value!r} to float") from e
44    raise SerdeError(f"Expected YAML number but got {type(value).__name__}")

Convert YAML numbers to float, accepting ints/floats and numeric strings (e.g. scientific notation). Rejects booleans and non-numeric inputs.