1
0
mirror of https://github.com/checktheroads/hyperglass synced 2024-05-11 05:55:08 +00:00
2021-09-10 01:18:38 -07:00

105 lines
2.8 KiB
Python

"""Data models used throughout hyperglass."""
# Standard Library
import re
from typing import Type, TypeVar
# Third Party
from pydantic import HttpUrl, BaseModel
# Project
from hyperglass.util import snake_to_camel
def clean_name(_name: str) -> str:
"""Remove unsupported characters from field names.
Converts any "desirable" seperators to underscore, then removes all
characters that are unsupported in Python class variable names.
Also removes leading numbers underscores.
"""
_replaced = re.sub(r"[\-|\.|\@|\~|\:\/|\s]", "_", _name)
_scrubbed = "".join(re.findall(r"([a-zA-Z]\w+|\_+)", _replaced))
return _scrubbed.lower()
AsUIModel = TypeVar("AsUIModel", bound="BaseModel")
class HyperglassModel(BaseModel):
"""Base model for all hyperglass configuration models."""
class Config:
"""Pydantic model configuration.
See https://pydantic-docs.helpmanual.io/usage/model_config
"""
validate_all = True
extra = "forbid"
validate_assignment = True
alias_generator = clean_name
json_encoders = {HttpUrl: lambda v: str(v)}
def export_json(self, *args, **kwargs):
"""Return instance as JSON."""
export_kwargs = {"by_alias": True, "exclude_unset": False}
for key in export_kwargs.keys():
export_kwargs.pop(key, None)
return self.json(*args, **export_kwargs, **kwargs)
def export_dict(self, *args, **kwargs):
"""Return instance as dictionary."""
export_kwargs = {"by_alias": True, "exclude_unset": False}
for key in export_kwargs.keys():
export_kwargs.pop(key, None)
return self.dict(*args, **export_kwargs, **kwargs)
def export_yaml(self, *args, **kwargs):
"""Return instance as YAML."""
# Standard Library
import json
# Third Party
import yaml
export_kwargs = {
"by_alias": kwargs.pop("by_alias", True),
"exclude_unset": kwargs.pop("by_alias", False),
}
return yaml.safe_dump(
json.loads(self.export_json(**export_kwargs)), *args, **kwargs
)
class HyperglassModelExtra(HyperglassModel):
"""Model for hyperglass configuration models with dynamic fields."""
class Config:
"""Pydantic model configuration."""
extra = "allow"
class HyperglassUIModel(HyperglassModel):
"""Base class for UI configuration parameters."""
class Config:
"""Pydantic model configuration."""
alias_generator = snake_to_camel
allow_population_by_field_name = True
def as_ui_model(name: str, model: Type[AsUIModel]) -> Type[AsUIModel]:
"""Override a model's configuration to confirm to a UI model."""
return type(name, (model, HyperglassUIModel), {})