| | from __future__ import annotations |
| |
|
| | import os |
| | import inspect |
| | import weakref |
| | from typing import ( |
| | IO, |
| | TYPE_CHECKING, |
| | Any, |
| | Type, |
| | Union, |
| | Generic, |
| | TypeVar, |
| | Callable, |
| | Iterable, |
| | Optional, |
| | AsyncIterable, |
| | cast, |
| | ) |
| | from datetime import date, datetime |
| | from typing_extensions import ( |
| | List, |
| | Unpack, |
| | Literal, |
| | ClassVar, |
| | Protocol, |
| | Required, |
| | ParamSpec, |
| | TypedDict, |
| | TypeGuard, |
| | final, |
| | override, |
| | runtime_checkable, |
| | ) |
| |
|
| | import pydantic |
| | from pydantic.fields import FieldInfo |
| |
|
| | from ._types import ( |
| | Body, |
| | IncEx, |
| | Query, |
| | ModelT, |
| | Headers, |
| | Timeout, |
| | NotGiven, |
| | AnyMapping, |
| | HttpxRequestFiles, |
| | ) |
| | from ._utils import ( |
| | PropertyInfo, |
| | is_list, |
| | is_given, |
| | json_safe, |
| | lru_cache, |
| | is_mapping, |
| | parse_date, |
| | coerce_boolean, |
| | parse_datetime, |
| | strip_not_given, |
| | extract_type_arg, |
| | is_annotated_type, |
| | is_type_alias_type, |
| | strip_annotated_type, |
| | ) |
| | from ._compat import ( |
| | PYDANTIC_V1, |
| | ConfigDict, |
| | GenericModel as BaseGenericModel, |
| | get_args, |
| | is_union, |
| | parse_obj, |
| | get_origin, |
| | is_literal_type, |
| | get_model_config, |
| | get_model_fields, |
| | field_get_default, |
| | ) |
| | from ._constants import RAW_RESPONSE_HEADER |
| |
|
| | if TYPE_CHECKING: |
| | from pydantic_core.core_schema import ModelField, ModelSchema, LiteralSchema, ModelFieldsSchema |
| |
|
| | __all__ = ["BaseModel", "GenericModel"] |
| |
|
| | _T = TypeVar("_T") |
| | _BaseModelT = TypeVar("_BaseModelT", bound="BaseModel") |
| |
|
| | P = ParamSpec("P") |
| |
|
| |
|
| | @runtime_checkable |
| | class _ConfigProtocol(Protocol): |
| | allow_population_by_field_name: bool |
| |
|
| |
|
| | class BaseModel(pydantic.BaseModel): |
| | if PYDANTIC_V1: |
| |
|
| | @property |
| | @override |
| | def model_fields_set(self) -> set[str]: |
| | |
| | return self.__fields_set__ |
| |
|
| | class Config(pydantic.BaseConfig): |
| | extra: Any = pydantic.Extra.allow |
| | else: |
| | model_config: ClassVar[ConfigDict] = ConfigDict( |
| | extra="allow", defer_build=coerce_boolean(os.environ.get("DEFER_PYDANTIC_BUILD", "true")) |
| | ) |
| |
|
| | if TYPE_CHECKING: |
| | _request_id: Optional[str] = None |
| | """The ID of the request, returned via the `request-id` header. Useful for debugging requests and reporting issues to Anthropic. |
| | This will **only** be set for the top-level response object, it will not be defined for nested objects. For example: |
| | |
| | ```py |
| | message = await client.messages.create(...) |
| | message._request_id # req_xxx |
| | message.usage._request_id # raises `AttributeError` |
| | ``` |
| | |
| | Note: unlike other properties that use an `_` prefix, this property |
| | *is* public. Unless documented otherwise, all other `_` prefix properties, |
| | methods and modules are *private*. |
| | """ |
| |
|
| | def to_dict( |
| | self, |
| | *, |
| | mode: Literal["json", "python"] = "python", |
| | use_api_names: bool = True, |
| | exclude_unset: bool = True, |
| | exclude_defaults: bool = False, |
| | exclude_none: bool = False, |
| | warnings: bool = True, |
| | ) -> dict[str, object]: |
| | """Recursively generate a dictionary representation of the model, optionally specifying which fields to include or exclude. |
| | |
| | By default, fields that were not set by the API will not be included, |
| | and keys will match the API response, *not* the property names from the model. |
| | |
| | For example, if the API responds with `"fooBar": true` but we've defined a `foo_bar: bool` property, |
| | the output will use the `"fooBar"` key (unless `use_api_names=False` is passed). |
| | |
| | Args: |
| | mode: |
| | If mode is 'json', the dictionary will only contain JSON serializable types. e.g. `datetime` will be turned into a string, `"2024-3-22T18:11:19.117000Z"`. |
| | If mode is 'python', the dictionary may contain any Python objects. e.g. `datetime(2024, 3, 22)` |
| | |
| | use_api_names: Whether to use the key that the API responded with or the property name. Defaults to `True`. |
| | exclude_unset: Whether to exclude fields that have not been explicitly set. |
| | exclude_defaults: Whether to exclude fields that are set to their default value from the output. |
| | exclude_none: Whether to exclude fields that have a value of `None` from the output. |
| | warnings: Whether to log warnings when invalid fields are encountered. This is only supported in Pydantic v2. |
| | """ |
| | return self.model_dump( |
| | mode=mode, |
| | by_alias=use_api_names, |
| | exclude_unset=exclude_unset, |
| | exclude_defaults=exclude_defaults, |
| | exclude_none=exclude_none, |
| | warnings=warnings, |
| | ) |
| |
|
| | def to_json( |
| | self, |
| | *, |
| | indent: int | None = 2, |
| | use_api_names: bool = True, |
| | exclude_unset: bool = True, |
| | exclude_defaults: bool = False, |
| | exclude_none: bool = False, |
| | warnings: bool = True, |
| | ) -> str: |
| | """Generates a JSON string representing this model as it would be received from or sent to the API (but with indentation). |
| | |
| | By default, fields that were not set by the API will not be included, |
| | and keys will match the API response, *not* the property names from the model. |
| | |
| | For example, if the API responds with `"fooBar": true` but we've defined a `foo_bar: bool` property, |
| | the output will use the `"fooBar"` key (unless `use_api_names=False` is passed). |
| | |
| | Args: |
| | indent: Indentation to use in the JSON output. If `None` is passed, the output will be compact. Defaults to `2` |
| | use_api_names: Whether to use the key that the API responded with or the property name. Defaults to `True`. |
| | exclude_unset: Whether to exclude fields that have not been explicitly set. |
| | exclude_defaults: Whether to exclude fields that have the default value. |
| | exclude_none: Whether to exclude fields that have a value of `None`. |
| | warnings: Whether to show any warnings that occurred during serialization. This is only supported in Pydantic v2. |
| | """ |
| | return self.model_dump_json( |
| | indent=indent, |
| | by_alias=use_api_names, |
| | exclude_unset=exclude_unset, |
| | exclude_defaults=exclude_defaults, |
| | exclude_none=exclude_none, |
| | warnings=warnings, |
| | ) |
| |
|
| | @override |
| | def __str__(self) -> str: |
| | |
| | return f"{self.__repr_name__()}({self.__repr_str__(', ')})" |
| |
|
| | |
| | |
| | @classmethod |
| | @override |
| | def construct( |
| | __cls: Type[ModelT], |
| | _fields_set: set[str] | None = None, |
| | **values: object, |
| | ) -> ModelT: |
| | m = __cls.__new__(__cls) |
| | fields_values: dict[str, object] = {} |
| |
|
| | config = get_model_config(__cls) |
| | populate_by_name = ( |
| | config.allow_population_by_field_name |
| | if isinstance(config, _ConfigProtocol) |
| | else config.get("populate_by_name") |
| | ) |
| |
|
| | if _fields_set is None: |
| | _fields_set = set() |
| |
|
| | model_fields = get_model_fields(__cls) |
| | for name, field in model_fields.items(): |
| | key = field.alias |
| | if key is None or (key not in values and populate_by_name): |
| | key = name |
| |
|
| | if key in values: |
| | fields_values[name] = _construct_field(value=values[key], field=field, key=key) |
| | _fields_set.add(name) |
| | else: |
| | fields_values[name] = field_get_default(field) |
| |
|
| | extra_field_type = _get_extra_fields_type(__cls) |
| |
|
| | _extra = {} |
| | for key, value in values.items(): |
| | if key not in model_fields: |
| | parsed = construct_type(value=value, type_=extra_field_type) if extra_field_type is not None else value |
| |
|
| | if PYDANTIC_V1: |
| | _fields_set.add(key) |
| | fields_values[key] = parsed |
| | else: |
| | _extra[key] = parsed |
| |
|
| | object.__setattr__(m, "__dict__", fields_values) |
| |
|
| | if PYDANTIC_V1: |
| | |
| | m._init_private_attributes() |
| |
|
| | |
| | object.__setattr__(m, "__fields_set__", _fields_set) |
| | else: |
| | |
| | object.__setattr__(m, "__pydantic_private__", None) |
| | object.__setattr__(m, "__pydantic_extra__", _extra) |
| | object.__setattr__(m, "__pydantic_fields_set__", _fields_set) |
| |
|
| | return m |
| |
|
| | if not TYPE_CHECKING: |
| | |
| | |
| | |
| | model_construct = construct |
| |
|
| | if PYDANTIC_V1: |
| | |
| | |
| | |
| | |
| |
|
| | @override |
| | def model_dump( |
| | self, |
| | *, |
| | mode: Literal["json", "python"] | str = "python", |
| | include: IncEx | None = None, |
| | exclude: IncEx | None = None, |
| | context: Any | None = None, |
| | by_alias: bool | None = None, |
| | exclude_unset: bool = False, |
| | exclude_defaults: bool = False, |
| | exclude_none: bool = False, |
| | exclude_computed_fields: bool = False, |
| | round_trip: bool = False, |
| | warnings: bool | Literal["none", "warn", "error"] = True, |
| | fallback: Callable[[Any], Any] | None = None, |
| | serialize_as_any: bool = False, |
| | ) -> dict[str, Any]: |
| | """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump |
| | |
| | Generate a dictionary representation of the model, optionally specifying which fields to include or exclude. |
| | |
| | Args: |
| | mode: The mode in which `to_python` should run. |
| | If mode is 'json', the output will only contain JSON serializable types. |
| | If mode is 'python', the output may contain non-JSON-serializable Python objects. |
| | include: A set of fields to include in the output. |
| | exclude: A set of fields to exclude from the output. |
| | context: Additional context to pass to the serializer. |
| | by_alias: Whether to use the field's alias in the dictionary key if defined. |
| | exclude_unset: Whether to exclude fields that have not been explicitly set. |
| | exclude_defaults: Whether to exclude fields that are set to their default value. |
| | exclude_none: Whether to exclude fields that have a value of `None`. |
| | exclude_computed_fields: Whether to exclude computed fields. |
| | While this can be useful for round-tripping, it is usually recommended to use the dedicated |
| | `round_trip` parameter instead. |
| | round_trip: If True, dumped values should be valid as input for non-idempotent types such as Json[T]. |
| | warnings: How to handle serialization errors. False/"none" ignores them, True/"warn" logs errors, |
| | "error" raises a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError]. |
| | fallback: A function to call when an unknown value is encountered. If not provided, |
| | a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError] error is raised. |
| | serialize_as_any: Whether to serialize fields with duck-typing serialization behavior. |
| | |
| | Returns: |
| | A dictionary representation of the model. |
| | """ |
| | if mode not in {"json", "python"}: |
| | raise ValueError("mode must be either 'json' or 'python'") |
| | if round_trip != False: |
| | raise ValueError("round_trip is only supported in Pydantic v2") |
| | if warnings != True: |
| | raise ValueError("warnings is only supported in Pydantic v2") |
| | if context is not None: |
| | raise ValueError("context is only supported in Pydantic v2") |
| | if serialize_as_any != False: |
| | raise ValueError("serialize_as_any is only supported in Pydantic v2") |
| | if fallback is not None: |
| | raise ValueError("fallback is only supported in Pydantic v2") |
| | if exclude_computed_fields != False: |
| | raise ValueError("exclude_computed_fields is only supported in Pydantic v2") |
| | dumped = super().dict( |
| | include=include, |
| | exclude=exclude, |
| | by_alias=by_alias if by_alias is not None else False, |
| | exclude_unset=exclude_unset, |
| | exclude_defaults=exclude_defaults, |
| | exclude_none=exclude_none, |
| | ) |
| |
|
| | return cast("dict[str, Any]", json_safe(dumped)) if mode == "json" else dumped |
| |
|
| | @override |
| | def model_dump_json( |
| | self, |
| | *, |
| | indent: int | None = None, |
| | ensure_ascii: bool = False, |
| | include: IncEx | None = None, |
| | exclude: IncEx | None = None, |
| | context: Any | None = None, |
| | by_alias: bool | None = None, |
| | exclude_unset: bool = False, |
| | exclude_defaults: bool = False, |
| | exclude_none: bool = False, |
| | exclude_computed_fields: bool = False, |
| | round_trip: bool = False, |
| | warnings: bool | Literal["none", "warn", "error"] = True, |
| | fallback: Callable[[Any], Any] | None = None, |
| | serialize_as_any: bool = False, |
| | ) -> str: |
| | """Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump_json |
| | |
| | Generates a JSON representation of the model using Pydantic's `to_json` method. |
| | |
| | Args: |
| | indent: Indentation to use in the JSON output. If None is passed, the output will be compact. |
| | include: Field(s) to include in the JSON output. Can take either a string or set of strings. |
| | exclude: Field(s) to exclude from the JSON output. Can take either a string or set of strings. |
| | by_alias: Whether to serialize using field aliases. |
| | exclude_unset: Whether to exclude fields that have not been explicitly set. |
| | exclude_defaults: Whether to exclude fields that have the default value. |
| | exclude_none: Whether to exclude fields that have a value of `None`. |
| | round_trip: Whether to use serialization/deserialization between JSON and class instance. |
| | warnings: Whether to show any warnings that occurred during serialization. |
| | |
| | Returns: |
| | A JSON string representation of the model. |
| | """ |
| | if round_trip != False: |
| | raise ValueError("round_trip is only supported in Pydantic v2") |
| | if warnings != True: |
| | raise ValueError("warnings is only supported in Pydantic v2") |
| | if context is not None: |
| | raise ValueError("context is only supported in Pydantic v2") |
| | if serialize_as_any != False: |
| | raise ValueError("serialize_as_any is only supported in Pydantic v2") |
| | if fallback is not None: |
| | raise ValueError("fallback is only supported in Pydantic v2") |
| | if ensure_ascii != False: |
| | raise ValueError("ensure_ascii is only supported in Pydantic v2") |
| | if exclude_computed_fields != False: |
| | raise ValueError("exclude_computed_fields is only supported in Pydantic v2") |
| | return super().json( |
| | indent=indent, |
| | include=include, |
| | exclude=exclude, |
| | by_alias=by_alias if by_alias is not None else False, |
| | exclude_unset=exclude_unset, |
| | exclude_defaults=exclude_defaults, |
| | exclude_none=exclude_none, |
| | ) |
| |
|
| |
|
| | def _construct_field(value: object, field: FieldInfo, key: str) -> object: |
| | if value is None: |
| | return field_get_default(field) |
| |
|
| | if PYDANTIC_V1: |
| | type_ = cast(type, field.outer_type_) |
| | else: |
| | type_ = field.annotation |
| |
|
| | if type_ is None: |
| | raise RuntimeError(f"Unexpected field type is None for {key}") |
| |
|
| | return construct_type(value=value, type_=type_, metadata=getattr(field, "metadata", None)) |
| |
|
| |
|
| | def _get_extra_fields_type(cls: type[pydantic.BaseModel]) -> type | None: |
| | if PYDANTIC_V1: |
| | |
| | return None |
| |
|
| | schema = cls.__pydantic_core_schema__ |
| | if schema["type"] == "model": |
| | fields = schema["schema"] |
| | if fields["type"] == "model-fields": |
| | extras = fields.get("extras_schema") |
| | if extras and "cls" in extras: |
| | |
| | return extras["cls"] |
| |
|
| | return None |
| |
|
| |
|
| | def is_basemodel(type_: type) -> bool: |
| | """Returns whether or not the given type is either a `BaseModel` or a union of `BaseModel`""" |
| | if is_union(type_): |
| | for variant in get_args(type_): |
| | if is_basemodel(variant): |
| | return True |
| |
|
| | return False |
| |
|
| | return is_basemodel_type(type_) |
| |
|
| |
|
| | def is_basemodel_type(type_: type) -> TypeGuard[type[BaseModel] | type[GenericModel]]: |
| | origin = get_origin(type_) or type_ |
| | if not inspect.isclass(origin): |
| | return False |
| | return issubclass(origin, BaseModel) or issubclass(origin, GenericModel) |
| |
|
| |
|
| | def build( |
| | base_model_cls: Callable[P, _BaseModelT], |
| | *args: P.args, |
| | **kwargs: P.kwargs, |
| | ) -> _BaseModelT: |
| | """Construct a BaseModel class without validation. |
| | |
| | This is useful for cases where you need to instantiate a `BaseModel` |
| | from an API response as this provides type-safe params which isn't supported |
| | by helpers like `construct_type()`. |
| | |
| | ```py |
| | build(MyModel, my_field_a="foo", my_field_b=123) |
| | ``` |
| | """ |
| | if args: |
| | raise TypeError( |
| | "Received positional arguments which are not supported; Keyword arguments must be used instead", |
| | ) |
| |
|
| | return cast(_BaseModelT, construct_type(type_=base_model_cls, value=kwargs)) |
| |
|
| |
|
| | def construct_type_unchecked(*, value: object, type_: type[_T]) -> _T: |
| | """Loose coercion to the expected type with construction of nested values. |
| | |
| | Note: the returned value from this function is not guaranteed to match the |
| | given type. |
| | """ |
| | return cast(_T, construct_type(value=value, type_=type_)) |
| |
|
| |
|
| | def construct_type(*, value: object, type_: object, metadata: Optional[List[Any]] = None) -> object: |
| | """Loose coercion to the expected type with construction of nested values. |
| | |
| | If the given value does not match the expected type then it is returned as-is. |
| | """ |
| |
|
| | |
| | |
| | original_type = None |
| |
|
| | |
| | |
| | type_ = cast("type[object]", type_) |
| | if is_type_alias_type(type_): |
| | original_type = type_ |
| | type_ = type_.__value__ |
| |
|
| | |
| | if metadata is not None and len(metadata) > 0: |
| | meta: tuple[Any, ...] = tuple(metadata) |
| | elif is_annotated_type(type_): |
| | meta = get_args(type_)[1:] |
| | type_ = extract_type_arg(type_, 0) |
| | else: |
| | meta = tuple() |
| |
|
| | |
| | |
| | origin = get_origin(type_) or type_ |
| | args = get_args(type_) |
| |
|
| | if is_union(origin): |
| | try: |
| | return validate_type(type_=cast("type[object]", original_type or type_), value=value) |
| | except Exception: |
| | pass |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | discriminator = _build_discriminated_union_meta(union=type_, meta_annotations=meta) |
| | if discriminator and is_mapping(value): |
| | variant_value = value.get(discriminator.field_alias_from or discriminator.field_name) |
| | if variant_value and isinstance(variant_value, str): |
| | variant_type = discriminator.mapping.get(variant_value) |
| | if variant_type: |
| | return construct_type(type_=variant_type, value=value) |
| |
|
| | |
| | for variant in args: |
| | try: |
| | return construct_type(value=value, type_=variant) |
| | except Exception: |
| | continue |
| |
|
| | raise RuntimeError(f"Could not convert data into a valid instance of {type_}") |
| |
|
| | if origin == dict: |
| | if not is_mapping(value): |
| | return value |
| |
|
| | _, items_type = get_args(type_) |
| | return {key: construct_type(value=item, type_=items_type) for key, item in value.items()} |
| |
|
| | if ( |
| | not is_literal_type(type_) |
| | and inspect.isclass(origin) |
| | and (issubclass(origin, BaseModel) or issubclass(origin, GenericModel)) |
| | ): |
| | if is_list(value): |
| | return [cast(Any, type_).construct(**entry) if is_mapping(entry) else entry for entry in value] |
| |
|
| | if is_mapping(value): |
| | if issubclass(type_, BaseModel): |
| | return type_.construct(**value) |
| |
|
| | return cast(Any, type_).construct(**value) |
| |
|
| | if origin == list: |
| | if not is_list(value): |
| | return value |
| |
|
| | inner_type = args[0] |
| | return [construct_type(value=entry, type_=inner_type) for entry in value] |
| |
|
| | if origin == float: |
| | if isinstance(value, int): |
| | coerced = float(value) |
| | if coerced != value: |
| | return value |
| | return coerced |
| |
|
| | return value |
| |
|
| | if type_ == datetime: |
| | try: |
| | return parse_datetime(value) |
| | except Exception: |
| | return value |
| |
|
| | if type_ == date: |
| | try: |
| | return parse_date(value) |
| | except Exception: |
| | return value |
| |
|
| | return value |
| |
|
| |
|
| | @runtime_checkable |
| | class CachedDiscriminatorType(Protocol): |
| | __discriminator__: DiscriminatorDetails |
| |
|
| |
|
| | DISCRIMINATOR_CACHE: weakref.WeakKeyDictionary[type, DiscriminatorDetails] = weakref.WeakKeyDictionary() |
| |
|
| |
|
| | class DiscriminatorDetails: |
| | field_name: str |
| | """The name of the discriminator field in the variant class, e.g. |
| | |
| | ```py |
| | class Foo(BaseModel): |
| | type: Literal['foo'] |
| | ``` |
| | |
| | Will result in field_name='type' |
| | """ |
| |
|
| | field_alias_from: str | None |
| | """The name of the discriminator field in the API response, e.g. |
| | |
| | ```py |
| | class Foo(BaseModel): |
| | type: Literal['foo'] = Field(alias='type_from_api') |
| | ``` |
| | |
| | Will result in field_alias_from='type_from_api' |
| | """ |
| |
|
| | mapping: dict[str, type] |
| | """Mapping of discriminator value to variant type, e.g. |
| | |
| | {'foo': FooVariant, 'bar': BarVariant} |
| | """ |
| |
|
| | def __init__( |
| | self, |
| | *, |
| | mapping: dict[str, type], |
| | discriminator_field: str, |
| | discriminator_alias: str | None, |
| | ) -> None: |
| | self.mapping = mapping |
| | self.field_name = discriminator_field |
| | self.field_alias_from = discriminator_alias |
| |
|
| |
|
| | def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any, ...]) -> DiscriminatorDetails | None: |
| | cached = DISCRIMINATOR_CACHE.get(union) |
| | if cached is not None: |
| | return cached |
| |
|
| | discriminator_field_name: str | None = None |
| |
|
| | for annotation in meta_annotations: |
| | if isinstance(annotation, PropertyInfo) and annotation.discriminator is not None: |
| | discriminator_field_name = annotation.discriminator |
| | break |
| |
|
| | if not discriminator_field_name: |
| | return None |
| |
|
| | mapping: dict[str, type] = {} |
| | discriminator_alias: str | None = None |
| |
|
| | for variant in get_args(union): |
| | variant = strip_annotated_type(variant) |
| | if is_basemodel_type(variant): |
| | if PYDANTIC_V1: |
| | field_info = cast("dict[str, FieldInfo]", variant.__fields__).get(discriminator_field_name) |
| | if not field_info: |
| | continue |
| |
|
| | |
| | discriminator_alias = field_info.alias |
| |
|
| | if (annotation := getattr(field_info, "annotation", None)) and is_literal_type(annotation): |
| | for entry in get_args(annotation): |
| | if isinstance(entry, str): |
| | mapping[entry] = variant |
| | else: |
| | field = _extract_field_schema_pv2(variant, discriminator_field_name) |
| | if not field: |
| | continue |
| |
|
| | |
| | discriminator_alias = field.get("serialization_alias") |
| |
|
| | field_schema = field["schema"] |
| |
|
| | if field_schema["type"] == "literal": |
| | for entry in cast("LiteralSchema", field_schema)["expected"]: |
| | if isinstance(entry, str): |
| | mapping[entry] = variant |
| |
|
| | if not mapping: |
| | return None |
| |
|
| | details = DiscriminatorDetails( |
| | mapping=mapping, |
| | discriminator_field=discriminator_field_name, |
| | discriminator_alias=discriminator_alias, |
| | ) |
| | DISCRIMINATOR_CACHE.setdefault(union, details) |
| | return details |
| |
|
| |
|
| | def _extract_field_schema_pv2(model: type[BaseModel], field_name: str) -> ModelField | None: |
| | schema = model.__pydantic_core_schema__ |
| | if schema["type"] == "definitions": |
| | schema = schema["schema"] |
| |
|
| | if schema["type"] != "model": |
| | return None |
| |
|
| | schema = cast("ModelSchema", schema) |
| | fields_schema = schema["schema"] |
| | if fields_schema["type"] != "model-fields": |
| | return None |
| |
|
| | fields_schema = cast("ModelFieldsSchema", fields_schema) |
| | field = fields_schema["fields"].get(field_name) |
| | if not field: |
| | return None |
| |
|
| | return cast("ModelField", field) |
| |
|
| |
|
| | def validate_type(*, type_: type[_T], value: object) -> _T: |
| | """Strict validation that the given value matches the expected type""" |
| | if inspect.isclass(type_) and issubclass(type_, pydantic.BaseModel): |
| | return cast(_T, parse_obj(type_, value)) |
| |
|
| | return cast(_T, _validate_non_model_type(type_=type_, value=value)) |
| |
|
| |
|
| | def set_pydantic_config(typ: Any, config: pydantic.ConfigDict) -> None: |
| | """Add a pydantic config for the given type. |
| | |
| | Note: this is a no-op on Pydantic v1. |
| | """ |
| | setattr(typ, "__pydantic_config__", config) |
| |
|
| |
|
| | def add_request_id(obj: BaseModel, request_id: str | None) -> None: |
| | obj._request_id = request_id |
| |
|
| | |
| | |
| | |
| | if PYDANTIC_V1: |
| | try: |
| | exclude_fields = obj.__exclude_fields__ |
| | except AttributeError: |
| | cast(Any, obj).__exclude_fields__ = {"_request_id", "__exclude_fields__"} |
| | else: |
| | cast(Any, obj).__exclude_fields__ = {*(exclude_fields or {}), "_request_id", "__exclude_fields__"} |
| |
|
| |
|
| | |
| | |
| | if TYPE_CHECKING: |
| | GenericModel = BaseModel |
| | else: |
| |
|
| | class GenericModel(BaseGenericModel, BaseModel): |
| | pass |
| |
|
| |
|
| | if not PYDANTIC_V1: |
| | from pydantic import TypeAdapter as _TypeAdapter, computed_field as computed_field |
| |
|
| | _CachedTypeAdapter = cast("TypeAdapter[object]", lru_cache(maxsize=None)(_TypeAdapter)) |
| |
|
| | if TYPE_CHECKING: |
| | from pydantic import TypeAdapter |
| | else: |
| | TypeAdapter = _CachedTypeAdapter |
| |
|
| | def _validate_non_model_type(*, type_: type[_T], value: object) -> _T: |
| | return TypeAdapter(type_).validate_python(value) |
| |
|
| | elif not TYPE_CHECKING: |
| |
|
| | class RootModel(GenericModel, Generic[_T]): |
| | """Used as a placeholder to easily convert runtime types to a Pydantic format |
| | to provide validation. |
| | |
| | For example: |
| | ```py |
| | validated = RootModel[int](__root__="5").__root__ |
| | # validated: 5 |
| | ``` |
| | """ |
| |
|
| | __root__: _T |
| |
|
| | def _validate_non_model_type(*, type_: type[_T], value: object) -> _T: |
| | model = _create_pydantic_model(type_).validate(value) |
| | return cast(_T, model.__root__) |
| |
|
| | def _create_pydantic_model(type_: _T) -> Type[RootModel[_T]]: |
| | return RootModel[type_] |
| |
|
| | def TypeAdapter(*_args: Any, **_kwargs: Any) -> Any: |
| | raise RuntimeError("attempted to use TypeAdapter in pydantic v1") |
| |
|
| | def computed_field(func: Any | None = None, /, **__: Any) -> Any: |
| | def _exc_func(*_: Any, **__: Any) -> Any: |
| | raise RuntimeError("attempted to use computed_field in pydantic v1") |
| |
|
| | def _dec(*_: Any, **__: Any) -> Any: |
| | return _exc_func |
| |
|
| | if func is not None: |
| | return _dec(func) |
| | else: |
| | return _dec |
| |
|
| |
|
| | class FinalRequestOptionsInput(TypedDict, total=False): |
| | method: Required[str] |
| | url: Required[str] |
| | params: Query |
| | headers: Headers |
| | max_retries: int |
| | timeout: float | Timeout | None |
| | files: HttpxRequestFiles | None |
| | idempotency_key: str |
| | content: Union[bytes, bytearray, IO[bytes], Iterable[bytes], AsyncIterable[bytes], None] |
| | json_data: Body |
| | extra_json: AnyMapping |
| | follow_redirects: bool |
| |
|
| |
|
| | @final |
| | class FinalRequestOptions(pydantic.BaseModel): |
| | method: str |
| | url: str |
| | params: Query = {} |
| | headers: Union[Headers, NotGiven] = NotGiven() |
| | max_retries: Union[int, NotGiven] = NotGiven() |
| | timeout: Union[float, Timeout, None, NotGiven] = NotGiven() |
| | files: Union[HttpxRequestFiles, None] = None |
| | idempotency_key: Union[str, None] = None |
| | post_parser: Union[Callable[[Any], Any], NotGiven] = NotGiven() |
| | follow_redirects: Union[bool, None] = None |
| |
|
| | content: Union[bytes, bytearray, IO[bytes], Iterable[bytes], AsyncIterable[bytes], None] = None |
| | |
| | |
| | json_data: Union[Body, None] = None |
| | extra_json: Union[AnyMapping, None] = None |
| |
|
| | if PYDANTIC_V1: |
| |
|
| | class Config(pydantic.BaseConfig): |
| | arbitrary_types_allowed: bool = True |
| | else: |
| | model_config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True) |
| |
|
| | def get_max_retries(self, max_retries: int) -> int: |
| | if isinstance(self.max_retries, NotGiven): |
| | return max_retries |
| | return self.max_retries |
| |
|
| | def _strip_raw_response_header(self) -> None: |
| | if not is_given(self.headers): |
| | return |
| |
|
| | if self.headers.get(RAW_RESPONSE_HEADER): |
| | self.headers = {**self.headers} |
| | self.headers.pop(RAW_RESPONSE_HEADER) |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | @classmethod |
| | def construct( |
| | cls, |
| | _fields_set: set[str] | None = None, |
| | **values: Unpack[FinalRequestOptionsInput], |
| | ) -> FinalRequestOptions: |
| | kwargs: dict[str, Any] = { |
| | |
| | |
| | key: strip_not_given(value) |
| | for key, value in values.items() |
| | } |
| | if PYDANTIC_V1: |
| | return cast(FinalRequestOptions, super().construct(_fields_set, **kwargs)) |
| | return super().model_construct(_fields_set, **kwargs) |
| |
|
| | if not TYPE_CHECKING: |
| | |
| | model_construct = construct |
| |
|