Skip to content

danielnaumau/pydantic-yaml

 
 

Repository files navigation

pydantic-yaml

PyPI version Documentation Status Unit Tests

This is a small helper library that adds some YAML capabilities to pydantic, namely dumping to yaml via the yaml_model.yaml() function, and parsing from strings/files using YamlModel.parse_raw() and YamlModel.parse_file(). It also adds Enum subclasses that get dumped to YAML as strings or integers, and fixes dumping of some typical types.

Documentation on ReadTheDocs.org

Basic Usage

Typical usage is seen below. See the pydantic docs for more usage examples.

from pydantic import BaseModel, validator
from pydantic_yaml import YamlStrEnum, YamlModel


class MyEnum(YamlStrEnum):
    """This is a custom enumeration that is YAML-safe."""
    
    a = "a"
    b = "b"

class InnerModel(BaseModel):
    """This is a normal pydantic model that can be used as an inner class."""
    
    fld: float = 1.0

class MyModel(YamlModel):
    """This is our custom class, with a `.yaml()` method.
    
    The `parse_raw()` and `parse_file()` methods are also updated to be able to
    handle `content_type='application/yaml'`, `protocol="yaml"` and file names
    ending with `.yml`/`.yaml`
    """

    x: int = 1
    e: MyEnum = MyEnum.a
    m: InnerModel = InnerModel()

    @validator('x')
    def _chk_x(cls, v: int) -> int:  # noqa
        """You can add your normal pydantic validators, like this one."""
        assert v > 0
        return v

m1 = MyModel(x=2, e="b", m=InnerModel(fld=1.5))

# This dumps to YAML and JSON respectively
yml = m1.yaml()
jsn = m1.json()

m2 = MyModel.parse_raw(yml)  # This automatically assumes YAML
assert m1 == m2

m3 = MyModel.parse_raw(jsn)  # This will fallback to JSON
assert m1 == m3

m4 = MyModel.parse_raw(yml, proto="yaml")
assert m1 == m4

m5 = MyModel.parse_raw(yml, content_type="application/yaml")
assert m1 == m5

Installation

pip install pydantic_yaml

Make sure to install ruamel.yaml or pyyaml as well. These are optional dependencies:

pip install pydantic_yaml[ruamel]

pip install pydantic_yaml[pyyaml]

Mixin Class

Version 0.5.0 adds a YamlModelMixin which can be used to add YAML functionality on top of, or alongside, other base classes:

from typing import List

from pydantic import BaseModel
from pydantic_yaml import YamlModelMixin


class MyBase(BaseModel):
    """This is a normal."""
    x: str = "x"

class ExtModel(YamlModelMixin, MyBase):
    """This model can be sent to/read from YAML."""
    y: List[int] = [1, 2, 3]  # and you can define additional fields, if you want

Note that this YamlModelMixin must be before any BaseModel-derived classes. This will hopefully be resolved in Pydantic 2.0 (see this discussion for more details). If you know a better way of implementing this, please make raise an issue or create a PR!

Configuration

You can configure the function used to dump and load the YAML by using the Config inner class, as in Pydantic:

class MyModel(YamlModel):
    # ...
    class Config:
        # You can override these fields:
        yaml_dumps = my_custom_dumper
        yaml_loads = lambda x: MyModel()
        # As well as other Pydantic configuration:
        allow_mutation = False

Versioned Models

Since YAML is often used for config files, there is also a SemVer str-like class and VersionedYamlModel base class.

The version attribute is parsed according to the SemVer (Semantic Versioning) specification. It's constrained between the min_version and max_version specified by your models' Config inner class (similar to regular pydantic models).

Usage example

from pydantic import ValidationError
from pydantic_yaml import SemVer, VersionedYamlModel

class A(VersionedYamlModel):
    """Model with min, max constraints as None."""

    foo: str = "bar"


class B(VersionedYamlModel):
    """Model with a maximum version set."""
    
    foo: str = "bar"

    class Config:
        min_version = "2.0.0"

ex_yml = """
version: 1.0.0
foo: baz
"""

a = A.parse_raw(ex_yml)
assert a.version == SemVer("1.0.0")
assert a.foo == "baz"

try:
    B.parse_raw(ex_yml)
except ValidationError as e:
    print("Correctly got ValidationError:", e, sep="\n")

About

YAML support for Pydantic models

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Python 100.0%