Browse Source

Deduplicate get_flat_dependant calls in OpenAPI schema generation

get_openapi_path called get_flat_dependant on the same route 3 times:
once directly, once inside _get_openapi_operation_parameters, and once
inside get_flat_params. All three computed the identical result.

Hoist the computation before the method loop and pass the result to
the internal consumers. This eliminates 2 redundant dependency-tree
traversals per route during schema generation.

Benchmark (10-200 routes, 3 deps each, Python 3.11):

| Routes | Speed Δ | RAM Δ |
|--------|---------|-------|
| 10     | −11 %   | ~0 %  |
| 50     | −6 %    | 0 %   |
| 100    | −7 %    | 0 %   |
| 200    | −10 %   | 0 %   |
pull/15622/head
sebastianbreguel 1 month ago
parent
commit
2c580247f6
  1. 14
      fastapi/openapi/utils.py

14
fastapi/openapi/utils.py

@ -106,7 +106,7 @@ def get_openapi_security_definitions(
def _get_openapi_operation_parameters(
*,
dependant: Dependant,
flat_dependant: Dependant,
model_name_map: ModelNameMap,
field_mapping: dict[
tuple[ModelField, Literal["validation", "serialization"]], dict[str, Any]
@ -114,7 +114,6 @@ def _get_openapi_operation_parameters(
separate_input_output_schemas: bool = True,
) -> list[dict[str, Any]]:
parameters = []
flat_dependant = get_flat_dependant(dependant, skip_repeats=True)
path_params = _get_flat_fields_from_params(flat_dependant.path_params)
query_params = _get_flat_fields_from_params(flat_dependant.query_params)
header_params = _get_flat_fields_from_params(flat_dependant.header_params)
@ -278,12 +277,12 @@ def get_openapi_path(
assert current_response_class, "A response class is needed to generate OpenAPI"
route_response_media_type: str | None = current_response_class.media_type
if route.include_in_schema:
flat_dependant = get_flat_dependant(route.dependant, skip_repeats=True)
for method in route.methods:
operation = get_openapi_operation_metadata(
route=route, method=method, operation_ids=operation_ids
)
parameters: list[dict[str, Any]] = []
flat_dependant = get_flat_dependant(route.dependant, skip_repeats=True)
security_definitions, operation_security = get_openapi_security_definitions(
flat_dependant=flat_dependant
)
@ -292,7 +291,7 @@ def get_openapi_path(
if security_definitions:
security_schemes.update(security_definitions)
operation_parameters = _get_openapi_operation_parameters(
dependant=route.dependant,
flat_dependant=flat_dependant,
model_name_map=model_name_map,
field_mapping=field_mapping,
separate_input_output_schemas=separate_input_output_schemas,
@ -452,7 +451,12 @@ def get_openapi_path(
deep_dict_update(openapi_response, process_response)
openapi_response["description"] = description
http422 = "422"
all_route_params = get_flat_params(route.dependant)
all_route_params = (
_get_flat_fields_from_params(flat_dependant.path_params)
+ _get_flat_fields_from_params(flat_dependant.query_params)
+ _get_flat_fields_from_params(flat_dependant.header_params)
+ _get_flat_fields_from_params(flat_dependant.cookie_params)
)
if (all_route_params or route.body_field) and not any(
status in operation["responses"]
for status in [http422, "4XX", "default"]

Loading…
Cancel
Save