failed WRONG_FIX UNSUBMITTED wrong_fix_unsubmitted(None) · None tool calls · 0 s · fastapi/fastapi
🐛 Fix parameter aliases This PR fixes the rest of failing tests in #14358. --- **Note:** for `validation_aliases` only string value is currently supported. We can later think of supporting `AliasPath` and `AliasChoices` **Will also fix issues:** <details> * https://github.com/fastapi/fastapi/discussions/8535 * https://github.com/fastapi/fastapi/discussions/10014 </details>
| # | Tool | Arguments | Result |
|---|---|---|---|
| No trace captured. | |||
--- a/fastapi/_compat/v2.py
+++ b/fastapi/_compat/v2.py
@@ -110,6 +110,18 @@ def alias(self) -> str:
a = self.field_info.alias
return a if a is not None else self.name
+ @property
+ def validation_alias(self) -> Union[str, None]:
+ va = self.field_info.validation_alias
+ if isinstance(va, str) and va:
+ return va
+ return None
+
+ @property
+ def serialization_alias(self) -> Union[str, None]:
+ sa = self.field_info.serialization_alias
+ return sa or None
+
@property
def required(self) -> bool:
return self.field_info.is_required()
@@ -243,12 +255,18 @@ def get_schema_from_model_field(
if (separate_input_output_schemas or _has_computed_fields(field))
else "validation"
)
+ field_alias = (
+ (field.validation_alias or field.alias)
+ if field.mode == "validation"
+ else (field.serialization_alias or field.alias)
+ )
+
# This expects that GenerateJsonSchema was already used to generate the definitions
json_schema = field_mapping[(field, override_mode or field.mode)]
if "$ref" not in json_schema:
# TODO remove when deprecating Pydantic v1
# Ref: https://github.com/pydantic/pydantic/blob/d61792cc42c80b13b23e3ffa74bc37ec7c77f7d1/pydantic/schema.py#L207
- json_schema["title"] = field.field_info.title or field.alias.title().replace(
+ json_schema["title"] = field.field_info.title or field_alias.title().replace(
"_", " "
)
return json_schema
--- a/fastapi/dependencies/utils.py
+++ b/fastapi/dependencies/utils.py
@@ -752,7 +752,7 @@ def _validate_value_with_model_field(
def _get_multidict_value(
field: ModelField, values: Mapping[str, Any], alias: Union[str, None] = None
) -> Any:
- alias = alias or field.alias
+ alias = alias or get_validation_alias(field)
if is_sequence_field(field) and isinstance(values, (ImmutableMultiDict, Headers)):
value = values.getlist(alias)
else:
@@ -809,15 +809,13 @@ def request_params_to_args(
field.field_info, "convert_underscores", default_convert_underscores
)
if convert_underscores:
- alias = (
- field.alias
- if field.alias != field.name
- else field.name.replace("_", "-")
- )
+ alias = get_validation_alias(field)
+ if alias == field.name:
+ alias = alias.replace("_", "-")
value = _get_multidict_value(field, received_params, alias=alias)
if value is not None:
- params_to_process[field.alias] = value
- processed_keys.add(alias or field.alias)
+ params_to_process[get_validation_alias(field)] = value
+ processed_keys.add(alias or get_validation_alias(field))
for key in received_params.keys():
if key not in processed_keys:
@@ -847,7 +845,7 @@ def request_params_to_args(
assert isinstance(field_info, (params.Param, temp_pydantic_v1_params.Param)), (
"Params must be subclasses of Param"
)
- loc = (field_info.in_.value, field.alias)
+ loc = (field_info.in_.value, get_validation_alias(field))
v_, errors_ = _validate_value_with_model_field(
field=field, value=value, values=values, loc=loc
)
@@ -936,8 +934,8 @@ async def process_fn(
tg.start_soon(process_fn, sub_value.read)
value = serialize_sequence_value(field=field, value=results)
if value is not None:
- values[field.alias] = value
- field_aliases = {field.alias for field in body_fields}
+ values[get_validation_alias(field)] = value
+ field_aliases = {get_validation_alias(field) for field in body_fields}
for key in received_body.keys():
if key not in field_aliases:
param_values = received_body.getlist(key)
@@ -979,11 +977,11 @@ async def request_body_to_args(
)
return {first_field.name: v_}, errors_
for field in body_fields:
- loc = ("body", field.alias)
+ loc = ("body", get_validation_alias(field))
value: Optional[Any] = None
if body_to_process is not None:
try:
- value = body_to_process.get(field.alias)
+ value = body_to_process.get(get_validation_alias(field))
# If the received body is a list, not a dict
except AttributeError:
errors.append(get_missing_field_error(loc))
@@ -1062,3 +1060,8 @@ def get_body_field(
field_info=BodyFieldInfo(**BodyFieldInfo_kwargs),
)
return final_field
+
+
+def get_validation_alias(field: ModelField) -> str:
+ va = getattr(field, "validation_alias", None)
+ return va or field.alias
--- a/fastapi/openapi/utils.py
+++ b/fastapi/openapi/utils.py
@@ -19,6 +19,7 @@
_get_flat_fields_from_params,
get_flat_dependant,
get_flat_params,
+ get_validation_alias,
)
from fastapi.encoders import jsonable_encoder
from fastapi.openapi.constants import METHODS_WITH_BODY, REF_PREFIX
@@ -141,15 +142,15 @@ def _get_openapi_operation_parameters(
field_mapping=field_mapping,
separate_input_output_schemas=separate_input_output_schemas,
)
- name = param.alias
+ name = get_validation_alias(param)
convert_underscores = getattr(
param.field_info,
"convert_underscores",
default_convert_underscores,
)
if (
param_type == ParamTypes.header
- and param.alias == param.name
+ and name == param.name
and convert_underscores
):
name = param.name.replace("_", "-")
--- a/fastapi/params.py
+++ b/fastapi/params.py
@@ -115,6 +115,10 @@ def __init__(
else:
kwargs["deprecated"] = deprecated
if PYDANTIC_V2:
+ if serialization_alias in (_Unset, None) and isinstance(alias, str):
+ serialization_alias = alias
+ if validation_alias in (_Unset, None):
+ validation_alias = alias
kwargs.update(
{
"annotation": annotation,
@@ -571,6 +575,10 @@ def __init__(
else:
kwargs["deprecated"] = deprecated
if PYDANTIC_V2:
+ if serialization_alias in (_Unset, None) and isinstance(alias, str):
+ serialization_alias = alias
+ if validation_alias in (_Unset, None):
+ validation_alias = alias
kwargs.update(
{
"annotation": annotation,
==================================== ERRORS ====================================
______ ERROR collecting tests/test_request_params/test_file/test_list.py _______
tests/test_request_params/test_file/test_list.py:19: in <module>
@app.post("/list-bytes", operation_id="list_bytes")
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
fastapi/routing.py:1128: in decorator
self.add_api_route(
fastapi/routing.py:1067: in add_api_route
route = route_class(
fastapi/routing.py:686: in __init__
self.dependant = get_dependant(
fastapi/dependencies/utils.py:288: in get_dependant
param_details = analyze_param(
fastapi/dependencies/utils.py:525: in analyze_param
ensure_multipart_is_installed()
fastapi/dependencies/utils.py:121: in ensure_multipart_is_installed
raise RuntimeError(multipart_not_installed_error) from None
E RuntimeError: Form data requires "python-multipart" to be installed.
E You can install "python-multipart" with:
E
E pip install python-multipart
=============================== warnings summary ===============================
../../../../../../../Users/jp/repos/kaggle-gemini-coding-agent-post-training/.envs/overlays/starlette-0.50.0-py3-none-any/starlette/testclient.py:45
/Users/jp/repos/kaggle-gemini-coding-agent-post-training/.envs/overlays/starlette-0.50.0-py3-none-any/starlette/testclient.py:45: DeprecationWarning: The anyio.abc.BlockingPortal alias is deprecated, use anyio.from_thread.BlockingPortal instead.
_PortalFactoryType = Callable[[], AbstractContextManager[anyio.abc.BlockingPortal]]
-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!
1 warning, 1 error in 1.18s