fastapi/_compat/shared.py PYTHON 215 lines View on github.com → Search inside
1import types2import typing3import warnings4from collections import deque5from collections.abc import Mapping, Sequence6from dataclasses import is_dataclass7from typing import (8    Annotated,9    Any,10    TypeGuard,11    TypeVar,12    Union,13    get_args,14    get_origin,15)1617from fastapi.types import UnionType18from pydantic import BaseModel19from pydantic.version import VERSION as PYDANTIC_VERSION20from starlette.datastructures import UploadFile2122_T = TypeVar("_T")2324# Copy from Pydantic: pydantic/_internal/_typing_extra.py25WithArgsTypes: tuple[Any, ...] = (26    typing._GenericAlias,  # type: ignore[attr-defined]  # ty: ignore[unresolved-attribute]27    types.GenericAlias,28    types.UnionType,29)  # pyright: ignore[reportAttributeAccessIssue]3031PYDANTIC_VERSION_MINOR_TUPLE = tuple(int(x) for x in PYDANTIC_VERSION.split(".")[:2])323334sequence_annotation_to_type = {35    Sequence: list,36    list: list,37    tuple: tuple,38    set: set,39    frozenset: frozenset,40    deque: deque,41}4243sequence_types: tuple[type[Any], ...] = tuple(sequence_annotation_to_type.keys())444546# Copy of Pydantic: pydantic/_internal/_utils.py with added TypeGuard47def lenient_issubclass(48    cls: Any, class_or_tuple: type[_T] | tuple[type[_T], ...] | None49) -> TypeGuard[type[_T]]:50    try:51        return isinstance(cls, type) and issubclass(cls, class_or_tuple)  # type: ignore[arg-type]  # ty: ignore[invalid-argument-type]52    except TypeError:  # pragma: no cover53        if isinstance(cls, WithArgsTypes):54            return False55        raise  # pragma: no cover565758def _annotation_is_sequence(annotation: type[Any] | None) -> bool:59    if lenient_issubclass(annotation, (str, bytes)):60        return False61    return lenient_issubclass(annotation, sequence_types)626364def field_annotation_is_sequence(annotation: type[Any] | None) -> bool:65    origin = get_origin(annotation)66    if origin is Union or origin is UnionType:67        for arg in get_args(annotation):68            if field_annotation_is_sequence(arg):69                return True70        return False71    return _annotation_is_sequence(annotation) or _annotation_is_sequence(72        get_origin(annotation)73    )747576def value_is_sequence(value: Any) -> bool:77    return isinstance(value, sequence_types) and not isinstance(value, (str, bytes))787980def _annotation_is_complex(annotation: type[Any] | None) -> bool:81    return (82        lenient_issubclass(annotation, (BaseModel, Mapping, UploadFile))83        or _annotation_is_sequence(annotation)84        or is_dataclass(annotation)85    )868788def field_annotation_is_complex(annotation: type[Any] | None) -> bool:89    origin = get_origin(annotation)90    if origin is Union or origin is UnionType:91        return any(field_annotation_is_complex(arg) for arg in get_args(annotation))9293    if origin is Annotated:94        return field_annotation_is_complex(get_args(annotation)[0])9596    return (97        _annotation_is_complex(annotation)98        or _annotation_is_complex(origin)99        or hasattr(origin, "__pydantic_core_schema__")100        or hasattr(origin, "__get_pydantic_core_schema__")101    )102103104def field_annotation_is_scalar(annotation: Any) -> bool:105    # handle Ellipsis here to make tuple[int, ...] work nicely106    return annotation is Ellipsis or not field_annotation_is_complex(annotation)107108109def field_annotation_is_scalar_sequence(annotation: type[Any] | None) -> bool:110    origin = get_origin(annotation)111    if origin is Union or origin is UnionType:112        at_least_one_scalar_sequence = False113        for arg in get_args(annotation):114            if field_annotation_is_scalar_sequence(arg):115                at_least_one_scalar_sequence = True116                continue117            elif not field_annotation_is_scalar(arg):118                return False119        return at_least_one_scalar_sequence120    return field_annotation_is_sequence(annotation) and all(121        field_annotation_is_scalar(sub_annotation)122        for sub_annotation in get_args(annotation)123    )124125126def is_bytes_or_nonable_bytes_annotation(annotation: Any) -> bool:127    if lenient_issubclass(annotation, bytes):128        return True129    origin = get_origin(annotation)130    if origin is Union or origin is UnionType:131        for arg in get_args(annotation):132            if lenient_issubclass(arg, bytes):133                return True134    return False135136137def is_uploadfile_or_nonable_uploadfile_annotation(annotation: Any) -> bool:138    if lenient_issubclass(annotation, UploadFile):139        return True140    origin = get_origin(annotation)141    if origin is Union or origin is UnionType:142        for arg in get_args(annotation):143            if lenient_issubclass(arg, UploadFile):144                return True145    return False146147148def is_bytes_sequence_annotation(annotation: Any) -> bool:149    origin = get_origin(annotation)150    if origin is Union or origin is UnionType:151        at_least_one = False152        for arg in get_args(annotation):153            if is_bytes_sequence_annotation(arg):154                at_least_one = True155                continue156        return at_least_one157    return field_annotation_is_sequence(annotation) and all(158        is_bytes_or_nonable_bytes_annotation(sub_annotation)159        for sub_annotation in get_args(annotation)160    )161162163def is_uploadfile_sequence_annotation(annotation: Any) -> bool:164    origin = get_origin(annotation)165    if origin is Union or origin is UnionType:166        at_least_one = False167        for arg in get_args(annotation):168            if is_uploadfile_sequence_annotation(arg):169                at_least_one = True170                continue171        return at_least_one172    return field_annotation_is_sequence(annotation) and all(173        is_uploadfile_or_nonable_uploadfile_annotation(sub_annotation)174        for sub_annotation in get_args(annotation)175    )176177178def is_pydantic_v1_model_instance(obj: Any) -> bool:179    # TODO: remove this function once the required version of Pydantic fully180    # removes pydantic.v1181    try:182        with warnings.catch_warnings():183            warnings.simplefilter("ignore", UserWarning)184            from pydantic import v1185    except ImportError:  # pragma: no cover186        return False187    return isinstance(obj, v1.BaseModel)188189190def is_pydantic_v1_model_class(cls: Any) -> bool:191    # TODO: remove this function once the required version of Pydantic fully192    # removes pydantic.v1193    try:194        with warnings.catch_warnings():195            warnings.simplefilter("ignore", UserWarning)196            from pydantic import v1197    except ImportError:  # pragma: no cover198        return False199    return lenient_issubclass(cls, v1.BaseModel)200201202def annotation_is_pydantic_v1(annotation: Any) -> bool:203    if is_pydantic_v1_model_class(annotation):204        return True205    origin = get_origin(annotation)206    if origin is Union or origin is UnionType:207        for arg in get_args(annotation):208            if is_pydantic_v1_model_class(arg):209                return True210    if field_annotation_is_sequence(annotation):211        for sub_annotation in get_args(annotation):212            if annotation_is_pydantic_v1(sub_annotation):213                return True214    return False

Code quality findings 17

Ensure functions have docstrings for documentation
missing-docstring
def lenient_issubclass(
Overuse may indicate design issues; consider polymorphism
isinstance-overuse
return isinstance(cls, type) and issubclass(cls, class_or_tuple) # type: ignore[arg-type] # ty: ignore[invalid-argument-type]
Overuse may indicate design issues; consider polymorphism
isinstance-overuse
if isinstance(cls, WithArgsTypes):
Ensure functions have docstrings for documentation
missing-docstring
def field_annotation_is_sequence(annotation: type[Any] | None) -> bool:
Ensure functions have docstrings for documentation
missing-docstring
def value_is_sequence(value: Any) -> bool:
Overuse may indicate design issues; consider polymorphism
isinstance-overuse
return isinstance(value, sequence_types) and not isinstance(value, (str, bytes))
Ensure functions have docstrings for documentation
missing-docstring
def field_annotation_is_complex(annotation: type[Any] | None) -> bool:
Ensure functions have docstrings for documentation
missing-docstring
def field_annotation_is_scalar(annotation: Any) -> bool:
Ensure functions have docstrings for documentation
missing-docstring
def field_annotation_is_scalar_sequence(annotation: type[Any] | None) -> bool:
Ensure functions have docstrings for documentation
missing-docstring
def is_bytes_or_nonable_bytes_annotation(annotation: Any) -> bool:
Ensure functions have docstrings for documentation
missing-docstring
def is_uploadfile_or_nonable_uploadfile_annotation(annotation: Any) -> bool:
Ensure functions have docstrings for documentation
missing-docstring
def is_bytes_sequence_annotation(annotation: Any) -> bool:
Ensure functions have docstrings for documentation
missing-docstring
def is_uploadfile_sequence_annotation(annotation: Any) -> bool:
Ensure functions have docstrings for documentation
missing-docstring
def is_pydantic_v1_model_instance(obj: Any) -> bool:
Overuse may indicate design issues; consider polymorphism
isinstance-overuse
return isinstance(obj, v1.BaseModel)
Ensure functions have docstrings for documentation
missing-docstring
def is_pydantic_v1_model_class(cls: Any) -> bool:
Ensure functions have docstrings for documentation
missing-docstring
def annotation_is_pydantic_v1(annotation: Any) -> bool:

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.