Source code for disdrodb.utils.pydantic
# -----------------------------------------------------------------------------.
# Copyright (c) 2021-2026 DISDRODB developers
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# -----------------------------------------------------------------------------.
"""Definition of pydantic validation custom class."""
from pydantic import BaseModel, ConfigDict, ValidationError
[docs]
def format_validation_error(validation_error: Exception) -> str:
"""Format a Pydantic ValidationError for better readability."""
if not isinstance(validation_error, ValidationError):
return str(validation_error)
def _shorten(value, max_len=200):
"""Safely truncate long inputs."""
text = repr(value)
if len(text) > max_len:
return text[: max_len - 5] + " ...]"
return text
model_name_attr = getattr(validation_error, "title", None)
model_name = model_name_attr() if callable(model_name_attr) else model_name_attr or "UnknownModel"
formatted_errors = [f"Validation errors in {model_name}:"]
for err in validation_error.errors():
path = ".".join(str(loc) for loc in err["loc"]) or "<model root>"
msg = err["msg"]
err_type = err["type"]
# Handles both "Value error, ..." and "Value error: ..."
if msg.lower().startswith("value error"):
msg = msg.split(",", 1)[-1] if "," in msg else msg.split(":", 1)[-1]
msg = msg.strip()
# Model-level (root) errors (raise in after or before)
if path == "<model root>":
formatted = f" • {msg}"
elif err_type == "missing":
formatted = f" • Missing field '{path}': {msg}"
elif "input" in err:
formatted = f" • Field '{path}': {msg} (got: {_shorten(err['input'])})"
else:
formatted = f" • Field '{path}': {msg}"
formatted_errors.append(formatted)
return "\n".join(formatted_errors)
[docs]
class CustomBaseModel(BaseModel):
"""Custom pydantic BaseModel.
Forbid extra keys.
Hide URLs in error message.
Simplify error message.
"""
model_config = ConfigDict(extra="forbid", hide_error_urls=True)
# Override the standard ValidationError print behavior
def __init__(self, **data):
try:
super().__init__(**data)
except ValidationError as e:
formatted = format_validation_error(e)
# Raise a new simplified exception
raise ValueError(formatted) from None