From 27618aa2e8b4052e8a742f01e763267d27f0e8d6 Mon Sep 17 00:00:00 2001 From: Zanie Adkins Date: Sat, 3 Jun 2023 08:37:41 -0500 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=20Update=20`create=5Fcloned=5Ffield`?= =?UTF-8?q?=20to=20use=20a=20global=20cache=20and=20improve=20startup=20pe?= =?UTF-8?q?rformance=20(#4645)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Sebastián Ramírez Co-authored-by: Huon Wilson --- fastapi/utils.py | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/fastapi/utils.py b/fastapi/utils.py index d8be53c57..9b9ebcb85 100644 --- a/fastapi/utils.py +++ b/fastapi/utils.py @@ -2,7 +2,18 @@ import re import warnings from dataclasses import is_dataclass from enum import Enum -from typing import TYPE_CHECKING, Any, Dict, Optional, Set, Type, Union, cast +from typing import ( + TYPE_CHECKING, + Any, + Dict, + MutableMapping, + Optional, + Set, + Type, + Union, + cast, +) +from weakref import WeakKeyDictionary import fastapi from fastapi.datastructures import DefaultPlaceholder, DefaultType @@ -16,6 +27,11 @@ from pydantic.utils import lenient_issubclass 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: @@ -98,11 +114,13 @@ def create_response_field( def create_cloned_field( field: ModelField, *, - cloned_types: Optional[Dict[Type[BaseModel], Type[BaseModel]]] = None, + cloned_types: Optional[MutableMapping[Type[BaseModel], Type[BaseModel]]] = None, ) -> ModelField: - # _cloned_types has already cloned types, to support recursive models + # cloned_types caches already cloned types to support recursive models and improve + # performance by avoiding unecessary cloning if cloned_types is None: - cloned_types = {} + 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__