You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

142 lines
5.3 KiB

# path: fastapi/_compat/v1.py
"""
Pydantic V1 compatibility module with lazy loading and controlled warnings.
This module acts as a transparent proxy to pydantic.v1, loading it only when needed
and providing controlled warnings for Python 3.14+ usage.
"""
from __future__ import annotations
import importlib
import os
import sys
import warnings
from copy import copy as _copy
from typing import Any, Dict, List, Sequence, Tuple
from typing_extensions import Literal
# Never import pydantic.v1 at import-time of this file.
# Load on demand in __getattr__ (PEP 562).
# Legacy FastAPI sentinel used by v1 params
RequiredParam = Ellipsis
_pv1 = None
_warned = False
def _load():
global _pv1, _warned
if _pv1 is not None:
return _pv1
if sys.version_info >= (3, 14):
msg = "Pydantic v1 em Python 3.14+ é desaconselhado/deprecado. Migre para v2."
if os.getenv("FASTAPI_PYDANTIC_V1_STRICT") == "1":
raise RuntimeError(msg)
if not _warned:
warnings.warn(msg, DeprecationWarning, stacklevel=3)
_warned = True
_pv1 = importlib.import_module("pydantic.v1")
return _pv1
def __getattr__(name: str) -> Any:
if name == "RequiredParam":
return Ellipsis
mod = _load()
# tenta direto no módulo principal
if hasattr(mod, name):
return getattr(mod, name)
# tenta submódulos comuns
for sub in ("fields","schema","networks","types","color","class_validators",
"error_wrappers","errors","typing","utils"):
submod = getattr(mod, sub, None)
if submod and hasattr(submod, name):
return getattr(submod, name)
raise AttributeError(name)
# ---------- Wrappers usados pelo core FastAPI (mínimos) ----------
def _normalize_errors(errors: Sequence[Any]) -> List[Dict[str, Any]]:
pv1 = _load()
RequestErrorModel = pv1.create_model("Request")
out: List[Any] = []
for err in errors:
if isinstance(err, pv1.error_wrappers.ErrorWrapper):
out.extend(pv1.ValidationError(errors=[err], model=RequestErrorModel).errors())
elif isinstance(err, list):
out.extend(_normalize_errors(err))
else:
out.append(err)
return out
def _regenerate_error_with_loc(*, errors: Sequence[Any], loc_prefix: Tuple[Any, ...]) -> List[Dict[str, Any]]:
return [{**e, "loc": loc_prefix + tuple(e.get("loc", ())) } for e in _normalize_errors(errors)]
def _model_rebuild(model) -> None:
model.update_forward_refs()
def _model_dump(model, mode: Literal["json","python"]="json", **kwargs: Any) -> Any:
return model.dict(**kwargs)
def _get_model_config(model) -> Any:
return getattr(model, "__config__", None)
def get_schema_from_model_field(*, field, model_name_map, field_mapping: Dict[Tuple[Any, Literal["validation","serialization"]], Dict[str, Any]], separate_input_output_schemas: bool=True) -> Dict[str, Any]:
schema = _load().schema
ref = "#/components/schemas"
return schema.field_schema(field, model_name_map=model_name_map, ref_prefix=ref)[0]
def get_definitions(*, fields: List[Any], model_name_map, separate_input_output_schemas: bool=True):
schema = _load().schema
models = schema.get_flat_models_from_fields(fields, known_models=set())
definitions: Dict[str, Dict[str, Any]] = {}
for m in models:
m_schema, m_defs, _ = schema.model_process_schema(m, model_name_map=model_name_map, ref_prefix="#/components/schemas")
definitions.update(m_defs)
definitions[model_name_map[m]] = m_schema
return {}, definitions
def get_model_fields(model) -> List[Any]:
return list(getattr(model, "__fields__", {}).values())
def is_bytes_field(field) -> bool:
return _load().utils.lenient_issubclass(field.type_, bytes)
def is_bytes_sequence_field(field) -> bool:
f = _load().fields
shapes = {f.SHAPE_LIST, f.SHAPE_SET, f.SHAPE_FROZENSET, f.SHAPE_TUPLE, f.SHAPE_SEQUENCE, f.SHAPE_TUPLE_ELLIPSIS}
return field.shape in shapes and _load().utils.lenient_issubclass(field.type_, bytes)
def is_scalar_field(field) -> bool:
f = _load().fields
pv1 = _load()
return (field.shape == f.SHAPE_SINGLETON
and not pv1.utils.lenient_issubclass(field.type_, pv1.BaseModel)
and not pv1.utils.lenient_issubclass(field.type_, dict))
def is_sequence_field(field) -> bool:
f = _load().fields
return field.shape in {f.SHAPE_LIST, f.SHAPE_SET, f.SHAPE_FROZENSET, f.SHAPE_TUPLE, f.SHAPE_SEQUENCE, f.SHAPE_TUPLE_ELLIPSIS}
def is_scalar_sequence_field(field) -> bool:
f = _load().fields
pv1 = _load()
if field.shape in {f.SHAPE_LIST, f.SHAPE_SET, f.SHAPE_FROZENSET, f.SHAPE_TUPLE, f.SHAPE_SEQUENCE, f.SHAPE_TUPLE_ELLIPSIS}:
return not pv1.utils.lenient_issubclass(field.type_, pv1.BaseModel) and all(is_scalar_field(sf) for sf in (field.sub_fields or []))
return False
from copy import copy as _copy
def copy_field_info(*, field_info, annotation: Any):
return _copy(field_info)
def serialize_sequence_value(*, field, value):
f = _load().fields
mapping = { f.SHAPE_LIST: list, f.SHAPE_SET: set, f.SHAPE_TUPLE: tuple, f.SHAPE_SEQUENCE: list, f.SHAPE_TUPLE_ELLIPSIS: list }
return mapping[field.shape](value)
# Type aliases for backward compatibility
GetJsonSchemaHandler = Any
JsonSchemaValue = dict[str, Any]
CoreSchema = Any
Url = Any