Validating Runtime Data¶
The data model can be imported and used to validate config loaded from YAML files. Two different approaches to runtime validation are demonstrated:
demos/pydantic/load_config.py
"""Demoing how to use Pydantic to get schema-valid config from a YAML file."""
from pathlib import Path
from pprint import pprint
from typing import Any
import yaml
from pydantic import ValidationError
from config_schema import ConfigV1
def get_config(file: Path = Path.cwd() / "config.yaml") -> ConfigV1:
"""Get validated config as an instance of the data model."""
with open(file) as f:
raw_config: dict[str, Any] = yaml.safe_load(f)
return ConfigV1(**raw_config)
def get_config_as_dict(file: Path = Path.cwd() / "config.yaml") -> dict[str, Any]:
"""Get config as a dictionary that has been validated against the data model."""
with open(file) as f:
raw_config: dict[str, Any] = yaml.safe_load(f)
ConfigV1.model_validate(raw_config)
return raw_config
if __name__ == "__main__":
try:
print("\n(1) config as ConfigV1 object:")
print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
config = get_config()
pprint(config.model_dump(), indent=2)
print("\n(2) config as dict:")
print("~~~~~~~~~~~~~~~~~~~")
config_dict = get_config_as_dict()
pprint(config_dict, indent=2)
except ValidationError as e:
print(e)
Given a config file,
demos/pydantic/config.yaml
# example config valyes that we will use Pydantic to validate
SCHEMA_VERSION: "0.1"
PROJECT_ID: 012345
PROJECT_ENV: prod
PROJECT_URL: http://foo.com/bar.html
USER_CERT:
secret_resource_name: http://foo.com/secrets/
filename: README.md
USERNAME:
env_var: foo-bar-1
The output from load_config.py
is,
(1) config as ConfigV1 object:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{ 'PROJECT_ENV': 'prod',
'PROJECT_ID': 5349,
'PROJECT_URL': 'http://foo.com/bar.html',
'SCHEMA_VERSION': '0.1',
'USERNAME': {'env_var': 'foo-bar-1'},
'USER_CERT': { 'filename': PosixPath('README.md'),
'secret_resource_name': Url('http://foo.com/secrets/')},
'USER_TAG': None}
(2) config as dict:
~~~~~~~~~~~~~~~~~~~
{ 'PROJECT_ENV': 'prod',
'PROJECT_ID': 5349,
'PROJECT_URL': 'http://foo.com/bar.html',
'SCHEMA_VERSION': '0.1',
'USERNAME': {'env_var': 'foo-bar-1'},
'USER_CERT': { 'filename': 'README.md',
'secret_resource_name': 'http://foo.com/secrets/'}}
Note how config not defined in the schema has been allowed to pass through when valudating the config data held in a dictionary. When instantiating the data model it is silently ignored.
If we manually invalidate a couple of the config values then we can also take a look at how errors are formatted:
(1) config as ConfigV1 object:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2 validation errors for ConfigV1
PROJECT_ENV
Input should be 'dev', 'test' or 'prod' [type=literal_error, input_value='run', input_type=str]
For further information visit https://errors.pydantic.dev/2.6/v/literal_error
USER_CERT.filename
Path does not point to a file [type=path_not_file, input_value='README.mdz', input_type=str]