I looked at schema which would work, but I rather would like to use pydantic since it incorporates the type validation and python object creation. So we have less code too manage. Here is an example how it would look like
from pydantic import BaseModel, field_validator, model_validator
from datetime import datetime
from isoduration import parse_duration
from isoduration.types import Duration
from pydantic_yaml import parse_yaml_raw_as
from typing import Self
class NamedBaseModel(BaseModel):
"""Our custom Pydantic model."""
name: str
def __init__(self, /, **data):
name_and_spec = {}
assert(len(data.keys()) == 1)
assert(len(data.values()) == 1)
name_and_spec["name"] = next(iter(data.keys()))
name_and_spec.update(next(iter(data.values())))
super().__init__(**name_and_spec)
class CycleTaskInput(NamedBaseModel):
"""Our custom Pydantic model."""
date: datetime | None = None
lag: str | None = None
@model_validator(mode='after')
def check_lag_xor_date_is_set(self) -> Self:
if self.lag is not None and self.date is not None:
msg = "Only one key 'lag' or 'date' is allowed. Not both."
raise ValueError(msg)
return self
@field_validator('lag')
@classmethod
def _is_duration(cls, value: str | None) -> Duration | None: # noqa
"""Check if is Duration."""
if value is None:
return None
return parse_duration(value)
# returns an instance
print('Passing {"grid_file": {"date": "2027-01-01T00:00"}}')
cycle_task_input = CycleTaskInput.model_validate_json('{"grid_file": {"date": "2027-01-01T00:00"}}')
print(cycle_task_input.name, cycle_task_input.date)
print()
print('Passing with typo {"grid_file": {"date": "2027-01-01T00:00typo"}}')
try:
CycleTaskInput.model_validate_json('{"grid_file": {"date": "2027-01-01T00:00typo"}}') # typo in date
except Exception as err:
print(err)
print()
print('Passing with lag and date {"grid_file": {"date": "2027-01-01T00:00", "lag": "-P2M"}}')
try:
CycleTaskInput.model_validate_json('{"grid_file": {"date": "2027-01-01T00:00", "lag": "-P2M"}}')
except Exception as err:
print(err)
print()
print('Passing yml "grid_file:\\n date: \'2027-01-01T00:00\'"')
yml = "grid_file:\n date: '2027-01-01T00:00'"
print(parse_yaml_raw_as(CycleTaskInput, yml))
Output
Passing {"grid_file": {"date": "2027-01-01T00:00"}}
grid_file 2027-01-01 00:00:00
Passing with typo {"grid_file": {"date": "2027-01-01T00:00typo"}}
1 validation error for CycleTaskInput
date
Input should be a valid datetime or date, unexpected extra characters at the end of the input [type=datetime_from_date_parsing, input_value='2027-01-01T00:00typo', input_type=str]
For further information visit https://errors.pydantic.dev/2.8/v/datetime_from_date_parsing
Passing with lag and date {"grid_file": {"date": "2027-01-01T00:00", "lag": "-P2M"}}
1 validation error for CycleTaskInput
Value error, Only one key 'lag' or 'date' is allowed. Not both. [type=value_error, input_value={'name': 'grid_file', 'da...1T00:00', 'lag': '-P2M'}, input_type=dict]
For further information visit https://errors.pydantic.dev/2.8/v/value_error
Passing yml "grid_file:\n date: '2027-01-01T00:00'"
name='grid_file' date=datetime.datetime(2027, 1, 1, 0, 0) lag=None
I looked at schema which would work, but I rather would like to use pydantic since it incorporates the type validation and python object creation. So we have less code too manage. Here is an example how it would look like
Output
What do you think @leclairm?