aboutsummaryrefslogtreecommitdiff
path: root/.venv/lib/python3.12/site-packages/fastapi/utils.py
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/fastapi/utils.py')
-rw-r--r--.venv/lib/python3.12/site-packages/fastapi/utils.py220
1 files changed, 220 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/fastapi/utils.py b/.venv/lib/python3.12/site-packages/fastapi/utils.py
new file mode 100644
index 00000000..4c7350fe
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/fastapi/utils.py
@@ -0,0 +1,220 @@
+import re
+import warnings
+from dataclasses import is_dataclass
+from typing import (
+ TYPE_CHECKING,
+ Any,
+ Dict,
+ MutableMapping,
+ Optional,
+ Set,
+ Type,
+ Union,
+ cast,
+)
+from weakref import WeakKeyDictionary
+
+import fastapi
+from fastapi._compat import (
+ PYDANTIC_V2,
+ BaseConfig,
+ ModelField,
+ PydanticSchemaGenerationError,
+ Undefined,
+ UndefinedType,
+ Validator,
+ lenient_issubclass,
+)
+from fastapi.datastructures import DefaultPlaceholder, DefaultType
+from pydantic import BaseModel, create_model
+from pydantic.fields import FieldInfo
+from typing_extensions import Literal
+
+if TYPE_CHECKING: # pragma: nocover
+ from .routing import APIRoute
+
+# Cache for `create_cloned_field`
+_CLONED_TYPES_CACHE: MutableMapping[Type[BaseModel], Type[BaseModel]] = (
+ WeakKeyDictionary()
+)
+
+
+def is_body_allowed_for_status_code(status_code: Union[int, str, None]) -> bool:
+ if status_code is None:
+ return True
+ # Ref: https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#patterned-fields-1
+ if status_code in {
+ "default",
+ "1XX",
+ "2XX",
+ "3XX",
+ "4XX",
+ "5XX",
+ }:
+ return True
+ current_status_code = int(status_code)
+ return not (current_status_code < 200 or current_status_code in {204, 205, 304})
+
+
+def get_path_param_names(path: str) -> Set[str]:
+ return set(re.findall("{(.*?)}", path))
+
+
+def create_model_field(
+ name: str,
+ type_: Any,
+ class_validators: Optional[Dict[str, Validator]] = None,
+ default: Optional[Any] = Undefined,
+ required: Union[bool, UndefinedType] = Undefined,
+ model_config: Type[BaseConfig] = BaseConfig,
+ field_info: Optional[FieldInfo] = None,
+ alias: Optional[str] = None,
+ mode: Literal["validation", "serialization"] = "validation",
+) -> ModelField:
+ class_validators = class_validators or {}
+ if PYDANTIC_V2:
+ field_info = field_info or FieldInfo(
+ annotation=type_, default=default, alias=alias
+ )
+ else:
+ field_info = field_info or FieldInfo()
+ kwargs = {"name": name, "field_info": field_info}
+ if PYDANTIC_V2:
+ kwargs.update({"mode": mode})
+ else:
+ kwargs.update(
+ {
+ "type_": type_,
+ "class_validators": class_validators,
+ "default": default,
+ "required": required,
+ "model_config": model_config,
+ "alias": alias,
+ }
+ )
+ try:
+ return ModelField(**kwargs) # type: ignore[arg-type]
+ except (RuntimeError, PydanticSchemaGenerationError):
+ raise fastapi.exceptions.FastAPIError(
+ "Invalid args for response field! Hint: "
+ f"check that {type_} is a valid Pydantic field type. "
+ "If you are using a return type annotation that is not a valid Pydantic "
+ "field (e.g. Union[Response, dict, None]) you can disable generating the "
+ "response model from the type annotation with the path operation decorator "
+ "parameter response_model=None. Read more: "
+ "https://fastapi.tiangolo.com/tutorial/response-model/"
+ ) from None
+
+
+def create_cloned_field(
+ field: ModelField,
+ *,
+ cloned_types: Optional[MutableMapping[Type[BaseModel], Type[BaseModel]]] = None,
+) -> ModelField:
+ if PYDANTIC_V2:
+ return field
+ # cloned_types caches already cloned types to support recursive models and improve
+ # performance by avoiding unnecessary cloning
+ if cloned_types is None:
+ cloned_types = _CLONED_TYPES_CACHE
+
+ original_type = field.type_
+ if is_dataclass(original_type) and hasattr(original_type, "__pydantic_model__"):
+ original_type = original_type.__pydantic_model__
+ use_type = original_type
+ if lenient_issubclass(original_type, BaseModel):
+ original_type = cast(Type[BaseModel], original_type)
+ use_type = cloned_types.get(original_type)
+ if use_type is None:
+ use_type = create_model(original_type.__name__, __base__=original_type)
+ cloned_types[original_type] = use_type
+ for f in original_type.__fields__.values():
+ use_type.__fields__[f.name] = create_cloned_field(
+ f, cloned_types=cloned_types
+ )
+ new_field = create_model_field(name=field.name, type_=use_type)
+ new_field.has_alias = field.has_alias # type: ignore[attr-defined]
+ new_field.alias = field.alias # type: ignore[misc]
+ new_field.class_validators = field.class_validators # type: ignore[attr-defined]
+ new_field.default = field.default # type: ignore[misc]
+ new_field.required = field.required # type: ignore[misc]
+ new_field.model_config = field.model_config # type: ignore[attr-defined]
+ new_field.field_info = field.field_info
+ new_field.allow_none = field.allow_none # type: ignore[attr-defined]
+ new_field.validate_always = field.validate_always # type: ignore[attr-defined]
+ if field.sub_fields: # type: ignore[attr-defined]
+ new_field.sub_fields = [ # type: ignore[attr-defined]
+ create_cloned_field(sub_field, cloned_types=cloned_types)
+ for sub_field in field.sub_fields # type: ignore[attr-defined]
+ ]
+ if field.key_field: # type: ignore[attr-defined]
+ new_field.key_field = create_cloned_field( # type: ignore[attr-defined]
+ field.key_field, # type: ignore[attr-defined]
+ cloned_types=cloned_types,
+ )
+ new_field.validators = field.validators # type: ignore[attr-defined]
+ new_field.pre_validators = field.pre_validators # type: ignore[attr-defined]
+ new_field.post_validators = field.post_validators # type: ignore[attr-defined]
+ new_field.parse_json = field.parse_json # type: ignore[attr-defined]
+ new_field.shape = field.shape # type: ignore[attr-defined]
+ new_field.populate_validators() # type: ignore[attr-defined]
+ return new_field
+
+
+def generate_operation_id_for_path(
+ *, name: str, path: str, method: str
+) -> str: # pragma: nocover
+ warnings.warn(
+ "fastapi.utils.generate_operation_id_for_path() was deprecated, "
+ "it is not used internally, and will be removed soon",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ operation_id = f"{name}{path}"
+ operation_id = re.sub(r"\W", "_", operation_id)
+ operation_id = f"{operation_id}_{method.lower()}"
+ return operation_id
+
+
+def generate_unique_id(route: "APIRoute") -> str:
+ operation_id = f"{route.name}{route.path_format}"
+ operation_id = re.sub(r"\W", "_", operation_id)
+ assert route.methods
+ operation_id = f"{operation_id}_{list(route.methods)[0].lower()}"
+ return operation_id
+
+
+def deep_dict_update(main_dict: Dict[Any, Any], update_dict: Dict[Any, Any]) -> None:
+ for key, value in update_dict.items():
+ if (
+ key in main_dict
+ and isinstance(main_dict[key], dict)
+ and isinstance(value, dict)
+ ):
+ deep_dict_update(main_dict[key], value)
+ elif (
+ key in main_dict
+ and isinstance(main_dict[key], list)
+ and isinstance(update_dict[key], list)
+ ):
+ main_dict[key] = main_dict[key] + update_dict[key]
+ else:
+ main_dict[key] = value
+
+
+def get_value_or_default(
+ first_item: Union[DefaultPlaceholder, DefaultType],
+ *extra_items: Union[DefaultPlaceholder, DefaultType],
+) -> Union[DefaultPlaceholder, DefaultType]:
+ """
+ Pass items or `DefaultPlaceholder`s by descending priority.
+
+ The first one to _not_ be a `DefaultPlaceholder` will be returned.
+
+ Otherwise, the first item (a `DefaultPlaceholder`) will be returned.
+ """
+ items = (first_item,) + extra_items
+ for item in items:
+ if not isinstance(item, DefaultPlaceholder):
+ return item
+ return first_item