| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980 |
- """Internal utilities for working with Pydantic types and data."""
- from __future__ import annotations
- import json
- from contextlib import suppress
- from functools import lru_cache
- from typing import TYPE_CHECKING
- import pydantic
- from packaging.version import Version
- from pydantic import ValidationError
- if TYPE_CHECKING:
- from typing import Any, Final
- from pydantic import BaseModel
- IS_PYDANTIC_V2: Final[bool] = Version(pydantic.VERSION).major >= 2
- @lru_cache
- def gql_typename(cls: type[BaseModel]) -> str:
- """Get the GraphQL typename for a Pydantic model."""
- if (field := cls.model_fields.get("typename__")) and (typename := field.default):
- return typename
- raise TypeError(f"Cannot extract GraphQL typename from: {cls.__qualname__!r}.")
- if IS_PYDANTIC_V2:
- import pydantic_core # pydantic_core is only installed by pydantic v2
- def from_json(s: str | bytes) -> Any:
- """Quickly deserialize a JSON string to a Python object."""
- return pydantic_core.from_json(s)
- def to_json(v: Any) -> str:
- """Quickly serialize a (possibly Pydantic) object to a JSON string."""
- return pydantic_core.to_json(v, by_alias=True, round_trip=True).decode("utf-8")
- def pydantic_isinstance(
- v: Any, classinfo: type[BaseModel] | tuple[type[BaseModel], ...]
- ) -> bool:
- """Return True if the object could be parsed into the given Pydantic type.
- This is like a more lenient version of `isinstance()` for use with Pydantic.
- In Pydantic v2, should be fast since the underlying implementation is in Rust,
- and it may be preferable over `try:...except ValidationError:...`.
- See: https://docs.pydantic.dev/latest/api/pydantic_core/#pydantic_core.SchemaValidator.isinstance_python
- """
- if isinstance(classinfo, tuple):
- return any(
- cls.__pydantic_validator__.isinstance_python(v) for cls in classinfo
- )
- cls = classinfo
- return cls.__pydantic_validator__.isinstance_python(v)
- else:
- # Pydantic v1 fallback implementations.
- # These may be noticeably slower, but their primary goal is to ensure
- # compatibility with Pydantic v1 so long as we need to support it.
- from pydantic.json import pydantic_encoder # Only valid in pydantic v1
- def from_json(s: str | bytes) -> Any:
- return json.loads(s)
- def to_json(v: Any) -> str:
- return json.dumps(v, default=pydantic_encoder)
- def pydantic_isinstance(
- v: Any, classinfo: type[BaseModel] | tuple[type[BaseModel], ...]
- ) -> bool:
- classes = classinfo if isinstance(classinfo, tuple) else (classinfo,)
- for cls in classes:
- with suppress(ValidationError):
- cls.model_validate(v)
- return True
- return False
|