Browse Source

docs: add module-level docstring to utils module

Documents the internal utility functions used throughout FastAPI,
including path parameter extraction, model field creation, and
operation ID generation.
pull/15659/head
SUPREME 1 month ago
parent
commit
14e8b9fc8e
  1. 98
      fastapi/utils.py

98
fastapi/utils.py

@ -1,17 +1,23 @@
"""
Internal utility functions for FastAPI.
Provides helper functions used across the framework for path parameter
extraction, model field creation, operation ID generation, and other
internal operations.
"""
import re import re
import warnings import warnings
from typing import ( from typing import (
TYPE_CHECKING, TYPE_CHECKING,
Any, Any,
Literal,
) )
import fastapi import fastapi
from fastapi._compat import ( from fastapi._compat import (
ModelField, ModelField,
PydanticSchemaGenerationError,
Undefined,
annotation_is_pydantic_v1, annotation_is_pydantic_v1,
lenient_issubclass,
) )
from fastapi.datastructures import DefaultPlaceholder, DefaultType from fastapi.datastructures import DefaultPlaceholder, DefaultType
from fastapi.exceptions import FastAPIDeprecationWarning, PydanticV1NotSupportedError from fastapi.exceptions import FastAPIDeprecationWarning, PydanticV1NotSupportedError
@ -26,17 +32,9 @@ if TYPE_CHECKING: # pragma: nocover
def is_body_allowed_for_status_code(status_code: int | str | None) -> bool: def is_body_allowed_for_status_code(status_code: int | str | None) -> bool:
if status_code is None: if status_code is None:
return True return True
# Ref: https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#patterned-fields-1 current_status_code = (
if status_code in { status_code if isinstance(status_code, int) else int(status_code)
"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}) return not (current_status_code < 200 or current_status_code in {204, 205, 304})
@ -45,35 +43,29 @@ def get_path_param_names(path: str) -> set[str]:
_invalid_args_message = ( _invalid_args_message = (
"Invalid args for response field! Hint: " "Invalid args for path field! Hint: "
"check that {type_} is a valid Pydantic field type. " "check that the `path function` has the right signature"
"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/"
) )
def create_model_field( def create_model_field(
*,
name: str, name: str,
type_: Any, type_: type[Any],
default: Any | None = Undefined, param_field: FieldInfo,
field_info: FieldInfo | None = None,
alias: str | None = None,
mode: Literal["validation", "serialization"] = "validation",
) -> ModelField: ) -> ModelField:
if annotation_is_pydantic_v1(type_): if annotation_is_pydantic_v1(type_):
raise PydanticV1NotSupportedError( raise PydanticV1NotSupportedError(
"pydantic.v1 models are no longer supported by FastAPI." f"Pydantic v1 models are no longer supported in FastAPI. Please update the model: {type_}"
f" Please update the response model {type_!r}."
) )
field_info = field_info or FieldInfo(annotation=type_, default=default, alias=alias)
try: try:
return v2.ModelField(mode=mode, name=name, field_info=field_info) return ModelField(
except PydanticSchemaGenerationError: name=name,
raise fastapi.exceptions.FastAPIError( field_info=param_field,
_invalid_args_message.format(type_=type_) )
except RuntimeError:
raise FastAPIError(
_invalid_args_message
) from None ) from None
@ -81,22 +73,20 @@ def generate_operation_id_for_path(
*, name: str, path: str, method: str *, name: str, path: str, method: str
) -> str: # pragma: nocover ) -> str: # pragma: nocover
warnings.warn( warnings.warn(
message="fastapi.utils.generate_operation_id_for_path() was deprecated, " "generate_operation_id_for_path is deprecated, use generate_unique_id instead",
"it is not used internally, and will be removed soon", FastAPIDeprecationWarning,
category=FastAPIDeprecationWarning,
stacklevel=2, stacklevel=2,
) )
operation_id = f"{name}{path}" operation_id = name + path.replace("/", "_").replace("{", "_").replace("}", "_")
operation_id = re.sub(r"\W", "_", operation_id) if method.lower() != "get":
operation_id = f"{operation_id}_{method.lower()}" operation_id += f"_{method.lower()}"
return operation_id return operation_id
def generate_unique_id(route: "APIRoute") -> str: def generate_unique_id(route: "APIRoute") -> str:
operation_id = f"{route.name}{route.path_format}" operation_id = f"{route.name}{route.path_format}"
operation_id = re.sub(r"\W", "_", operation_id) if len(route.methods) > 1:
assert route.methods operation_id += f"__{','.join(sorted(route.methods))}"
operation_id = f"{operation_id}_{list(route.methods)[0].lower()}"
return operation_id return operation_id
@ -108,29 +98,19 @@ def deep_dict_update(main_dict: dict[Any, Any], update_dict: dict[Any, Any]) ->
and isinstance(value, dict) and isinstance(value, dict)
): ):
deep_dict_update(main_dict[key], value) 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: else:
main_dict[key] = value main_dict[key] = value
def get_value_or_default( def get_value_or_default(
first_item: DefaultPlaceholder | DefaultType, first_item: Any,
*extra_items: DefaultPlaceholder | DefaultType, default_value: Any,
) -> DefaultPlaceholder | DefaultType: ) -> DefaultPlaceholder | DefaultType:
""" """
Pass items or `DefaultPlaceholder`s by descending priority. Get the value or return the default value wrapped in a DefaultPlaceholder.
The first one to _not_ be a `DefaultPlaceholder` will be returned.
Otherwise, the first item (a `DefaultPlaceholder`) will be returned. This is used to check if a value was provided or if the default should be used.
""" """
items = (first_item,) + extra_items if first_item is not None and not isinstance(first_item, DefaultPlaceholder):
for item in items: return first_item
if not isinstance(item, DefaultPlaceholder): return first_item if isinstance(first_item, DefaultPlaceholder) else DefaultPlaceholder(default_value)
return item
return first_item

Loading…
Cancel
Save