anton
anton is a Python library for auto instantiating YAML or JSON definitions to user defined dataclasses.
Avoid boilerplate code of YAML or JSON loading and specific runtime type checking before the objects are created.
Note
Currently anton only supports Python3.8.
Support for Python3.8+ and missing python types will be coming in further versions of the project.
Usage
Consider a hypothetical YAML configuration file being used:
# saved in the file: index.yaml
"""
point1:
x: 10
y: 10
point2:
x: 20
y: 20
line_segment1:
first_point:
x: 0
y: 0
second_point:
x: 10
y: 10
line_segment2:
first_point:
x: 0
y: 10
second_point:
x: 10
y: 0
Assuming the dataclass definitions as:
from dataclasses import dataclass
@dataclass
class Point:
x: int
y: int
@dataclass
class LineSegment:
first_point: Point
second_point: Point
Before anton, to load the YAML configuration one had to ensure types in the YAML files are right and also ensure the exact keys are present in the YAML configuration.
# Define the dataclass
from dataclasses import dataclass
@dataclass
class CustomInput:
point1: Point
point2: Point
line_segment1: LineSegment
line_segment2: LineSegment
# Loading the YAML configuration to the dataclass:
import typing
import yaml
with open("index.yaml") as stream:
try:
conf_dict = yaml.safe_load(stream)
except yaml.YAMLError as exc:
raise exc
if not all(key in conf_dict.keys() for key in ["point1", "point2", "line_segment1", "line_segment2"]):
raise ValueError("Input all the required keys.")
if not (isinstance(conf_dict["point1"], typing.Dict) and all(arg in conf_dict["point1"].keys() for arg in ["x", "y"])):
raise ValueError("Input `point1` as a mapping with keys `x` and `y`.")
if not (isinstance(conf_dict["point2"], typing.Dict) and all(arg in conf_dict["point2"].keys() for arg in ["x", "y"])):
raise ValueError("Input `point2` as a mapping with keys `x` and `y`.")
if not (
isinstance(conf_dict["line_segment1"], typing.Dict)
and all(arg in conf_dict["line_segment1"].keys() for arg in ["first_point", "second_point"])
):
raise ValueError("Input `line_segment1` as a mapping with keys `first_point` and `second_point`.")
if not (
isinstance(conf_dict["line_segment2"], typing.Dict)
and all(arg in conf_dict["line_segment2"].keys() for arg in ["first_point", "second_point"])
):
raise ValueError("Input `line_segment2` as a mapping with keys `first_point` and `second_point`.")
custom_input = CustomInput(
point1=Point(**conf_dict["point1"]),
point2=Point(**conf_dict["point2"]),
line_segment1=LineSegment(
first_point=conf_dict["line_segment1"]["first_point"],
second_point=conf_dict["line_segment1"]["second_point"],
),
line_segment2=LineSegment(
first_point=conf_dict["line_segment2"]["first_point"],
second_point=conf_dict["line_segment2"]["second_point"],
),
)
With anton, all the boilerplate can be avoided by using the decorators yaml_conf and json_conf that wraps the dataclasses.dataclass decorator to provide auto instantiation from YAML and JSON definitions respectively with runtime type checking of values. This helps avoid writing the biolerplate code for loading these definitions.
import anton
@anton.yaml_conf(conf_path="index.yaml")
class CustomInput:
point1: Point
point2: Point
line_segment1: LineSegment
line_segment2: LineSegment