Source code for czml3.base

from __future__ import annotations

from collections.abc import Iterator
from typing import TYPE_CHECKING, Any, cast

from pydantic import BaseModel, ConfigDict, Field, ValidationError, model_validator

from ._compat import Self, dataclass_transform
from .errors import humanise_validation_error

if TYPE_CHECKING:
    # Imported under TYPE_CHECKING only so that type checkers keep treating
    # subclasses of BaseCZMLObject as pydantic models; at runtime the metaclass
    # is taken from BaseModel itself to avoid depending on a private import path.
    from pydantic._internal._model_construction import ModelMetaclass
else:
    ModelMetaclass = type(BaseModel)

NON_DELETE_PROPERTIES = ["id", "delete"]


@dataclass_transform(kw_only_default=True, field_specifiers=(Field,))
class _HumanErrorsMeta(ModelMetaclass):
    """Rewrites the error raised by ``Model(...)`` into something readable.

    The rewrite is hooked in here rather than on ``__init__`` deliberately: a
    model that defines its own ``__init__`` opts out of pydantic's native
    construction path, which both slows down nested validation and discards the
    union information that :func:`~czml3.errors.humanise_validation_error` needs.
    """

    def __call__(cls, *args: Any, **kwargs: Any) -> Any:
        try:
            return super().__call__(*args, **kwargs)
        except ValidationError as error:
            raise humanise_validation_error(error, cast(type[BaseModel], cls)) from None


[docs] class BaseCZMLObject(BaseModel, metaclass=_HumanErrorsMeta): model_config = ConfigDict(extra="forbid")
[docs] @classmethod def model_validate(cls, *args: Any, **kwargs: Any) -> Self: """Validate an object against this model. args and kwargs are passed to `BaseModel.model_validate()`. :return: The validated model instance :raises pydantic.ValidationError: If the object is invalid, with a human-readable message """ try: return super().model_validate(*args, **kwargs) except ValidationError as error: raise humanise_validation_error(error, cls) from None
[docs] @classmethod def model_validate_json(cls, *args: Any, **kwargs: Any) -> Self: """Validate a JSON string against this model. args and kwargs are passed to `BaseModel.model_validate_json()`. :return: The validated model instance :raises pydantic.ValidationError: If the JSON is invalid, with a human-readable message """ try: return super().model_validate_json(*args, **kwargs) except ValidationError as error: raise humanise_validation_error(error, cls) from None
[docs] @model_validator(mode="after") def check_delete(self) -> Self: if hasattr(self, "delete") and self.delete: for k in type(self).model_fields: if k not in NON_DELETE_PROPERTIES and getattr(self, k) is not None: setattr(self, k, None) return self
def __repr_args__(self) -> Iterator[tuple[str | None, Any]]: # Only show the properties that are set: most CZML objects have dozens # of optional properties, almost all of them None. for name, value in super().__repr_args__(): if value is not None: yield name, value def __str__(self) -> str: return self.to_json()
[docs] def dumps(self, **kwargs: Any) -> str: """Serialize the object to a JSON string. kwargs are passed to `BaseModel.model_dump_json()`. :return: JSON string representation of the object with None values excluded :rtype: str """ return self.model_dump_json(exclude_none=True, **kwargs)
[docs] def to_json(self, *, indent: int = 4, **kwargs: Any) -> str: """Return the object as a formatted JSON string. kwargs are passed to `BaseModel.model_dump_json()`. :param indent: Number of spaces for indentation, defaults to 4 :type indent: int, optional :return: Formatted JSON string representation with None values excluded :rtype: str """ return self.model_dump_json(exclude_none=True, indent=indent, **kwargs)
[docs] def to_dict(self, **kwargs: Any) -> dict[str, Any]: """Return the object as a dictionary. kwargs are passed to `BaseModel.model_dump()`. :return: Dictionary representation of the object with None values excluded :rtype: dict """ return self.model_dump(exclude_none=True, **kwargs)