diff --git a/tests/test_request_params/test_file/test_list.py b/tests/test_request_params/test_file/test_list.py index 426b1f9ea..8722ce5ab 100644 --- a/tests/test_request_params/test_file/test_list.py +++ b/tests/test_request_params/test_file/test_list.py @@ -1,11 +1,10 @@ from typing import List import pytest -from dirty_equals import IsDict, IsOneOf, IsPartialDict -from fastapi import FastAPI, File, Form, UploadFile +from dirty_equals import IsDict +from fastapi import FastAPI, File, UploadFile from fastapi._compat import PYDANTIC_V2 from fastapi.testclient import TestClient -from pydantic import BaseModel from typing_extensions import Annotated from tests.utils import needs_pydanticv2 @@ -28,45 +27,11 @@ async def read_list_uploadfile(p: Annotated[List[UploadFile], File()]): return {"file_size": [file.size for file in p]} -class FormModelListBytes(BaseModel): - p: List[bytes] = File() - - -@app.post("/model-list-bytes", operation_id="model_list_bytes") -async def read_model_list_bytes( - p: Annotated[ - FormModelListBytes, - Form( - media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed - ), - ], -): - return {"file_size": [len(file) for file in p.p]} - - -class FormModelListUploadFile(BaseModel): - p: List[UploadFile] = File() - - -@app.post("/model-list-uploadfile", operation_id="model_list_uploadfile") -async def read_model_list_uploadfile( - p: Annotated[ - FormModelListUploadFile, - Form( - media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed - ), - ], -): - return {"file_size": [file.size for file in p.p]} - - @pytest.mark.parametrize( "path", [ "/list-bytes", - "/model-list-bytes", "/list-uploadfile", - "/model-list-uploadfile", ], ) def test_list_schema(path: str): @@ -107,9 +72,7 @@ def test_list_schema(path: str): "path", [ "/list-bytes", - "/model-list-bytes", "/list-uploadfile", - "/model-list-uploadfile", ], ) def test_list_missing(path: str): @@ -123,7 +86,7 @@ def test_list_missing(path: str): "type": "missing", "loc": ["body", "p"], "msg": "Field required", - "input": IsOneOf(None, {}), + "input": None, } ] } @@ -145,9 +108,7 @@ def test_list_missing(path: str): "path", [ "/list-bytes", - "/model-list-bytes", "/list-uploadfile", - "/model-list-uploadfile", ], ) def test_list(path: str): @@ -173,38 +134,6 @@ async def read_list_uploadfile_alias( return {"file_size": [file.size for file in p]} -class FormModelListBytesAlias(BaseModel): - p: List[bytes] = File(alias="p_alias") - - -@app.post("/model-list-bytes-alias", operation_id="model_list_bytes_alias") -async def read_model_list_bytes_alias( - p: Annotated[ - FormModelListBytesAlias, - Form( - media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed - ), - ], -): - return {"file_size": [len(file) for file in p.p]} - - -class FormModelListUploadFileAlias(BaseModel): - p: List[UploadFile] = File(alias="p_alias") - - -@app.post("/model-list-uploadfile-alias", operation_id="model_list_uploadfile_alias") -async def read_model_list_uploadfile_alias( - p: Annotated[ - FormModelListUploadFileAlias, - Form( - media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed - ), - ], -): - return {"file_size": [file.size for file in p.p]} - - @pytest.mark.xfail( raises=AssertionError, condition=PYDANTIC_V2, @@ -215,9 +144,7 @@ async def read_model_list_uploadfile_alias( "path", [ "/list-bytes-alias", - "/model-list-bytes-alias", "/list-uploadfile-alias", - "/model-list-uploadfile-alias", ], ) def test_list_alias_schema(path: str): @@ -258,25 +185,7 @@ def test_list_alias_schema(path: str): "path", [ "/list-bytes-alias", - pytest.param( - "/model-list-bytes-alias", - marks=pytest.mark.xfail( - raises=AssertionError, - strict=False, - condition=PYDANTIC_V2, - reason="Fails only with PDv2 model", - ), - ), "/list-uploadfile-alias", - pytest.param( - "/model-list-uploadfile-alias", - marks=pytest.mark.xfail( - raises=AssertionError, - strict=False, - condition=PYDANTIC_V2, - reason="Fails only with PDv2 model", - ), - ), ], ) def test_list_alias_missing(path: str): @@ -288,9 +197,9 @@ def test_list_alias_missing(path: str): "detail": [ { "type": "missing", - "loc": ["body", "p_alias"], # model-list-*-alias fail here + "loc": ["body", "p_alias"], "msg": "Field required", - "input": IsOneOf(None, {}), + "input": None, } ] } @@ -312,31 +221,13 @@ def test_list_alias_missing(path: str): "path", [ "/list-bytes-alias", - pytest.param( - "/model-list-bytes-alias", - marks=pytest.mark.xfail( - raises=AssertionError, - condition=PYDANTIC_V2, - reason="Fails only with PDv2 model", - strict=False, - ), - ), "/list-uploadfile-alias", - pytest.param( - "/model-list-uploadfile-alias", - marks=pytest.mark.xfail( - raises=AssertionError, - condition=PYDANTIC_V2, - reason="Fails only with PDv2 model", - strict=False, - ), - ), ], ) def test_list_alias_by_name(path: str): client = TestClient(app) response = client.post(path, files=[("p", b"hello"), ("p", b"world")]) - assert response.status_code == 422 # model-list-uploadfile-alias fail here + assert response.status_code == 422 assert response.json() == IsDict( { "detail": [ @@ -344,10 +235,7 @@ def test_list_alias_by_name(path: str): "type": "missing", "loc": ["body", "p_alias"], "msg": "Field required", - "input": IsOneOf( # model-list-bytes-alias fail here - None, - {"p": [IsPartialDict({"size": 5}), IsPartialDict({"size": 5})]}, - ), + "input": None, } ] } @@ -369,33 +257,13 @@ def test_list_alias_by_name(path: str): "path", [ "/list-bytes-alias", - pytest.param( - "/model-list-bytes-alias", - marks=pytest.mark.xfail( - raises=AssertionError, - strict=False, - condition=PYDANTIC_V2, - reason="Fails only with PDv2 model", - ), - ), "/list-uploadfile-alias", - pytest.param( - "/model-list-uploadfile-alias", - marks=pytest.mark.xfail( - raises=AssertionError, - strict=False, - condition=PYDANTIC_V2, - reason="Fails only with PDv2 model", - ), - ), ], ) def test_list_alias_by_alias(path: str): client = TestClient(app) response = client.post(path, files=[("p_alias", b"hello"), ("p_alias", b"world")]) - assert response.status_code == 200, ( # model-list-*-alias fail here - response.text - ) + assert response.status_code == 200, response.text assert response.json() == {"file_size": [5, 5]} @@ -420,52 +288,12 @@ def read_list_uploadfile_validation_alias( return {"file_size": [file.size for file in p]} -class FormModelRequiredBytesValidationAlias(BaseModel): - p: List[bytes] = File(validation_alias="p_val_alias") - - -@app.post( - "/model-list-bytes-validation-alias", - operation_id="model_list_bytes_validation_alias", -) -def read_model_list_bytes_validation_alias( - p: Annotated[ - FormModelRequiredBytesValidationAlias, - Form( - media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed - ), - ], -): - return {"file_size": [len(file) for file in p.p]} # pragma: no cover - - -class FormModelRequiredUploadFileValidationAlias(BaseModel): - p: List[UploadFile] = File(validation_alias="p_val_alias") - - -@app.post( - "/model-list-uploadfile-validation-alias", - operation_id="model_list_uploadfile_validation_alias", -) -def read_model_list_uploadfile_validation_alias( - p: Annotated[ - FormModelRequiredUploadFileValidationAlias, - Form( - media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed - ), - ], -): - return {"file_size": [file.size for file in p.p]} # pragma: no cover - - @needs_pydanticv2 @pytest.mark.parametrize( "path", [ "/list-bytes-validation-alias", - "/model-list-uploadfile-validation-alias", "/list-uploadfile-validation-alias", - "/model-list-bytes-validation-alias", ], ) def test_list_validation_alias_schema(path: str): @@ -510,12 +338,10 @@ def test_list_validation_alias_schema(path: str): "/list-bytes-validation-alias", marks=pytest.mark.xfail(raises=AssertionError, strict=False), ), - "/model-list-bytes-validation-alias", pytest.param( "/list-uploadfile-validation-alias", marks=pytest.mark.xfail(raises=AssertionError, strict=False), ), - "/model-list-uploadfile-validation-alias", ], ) def test_list_validation_alias_missing(path: str): @@ -531,7 +357,7 @@ def test_list_validation_alias_missing(path: str): "p_val_alias", ], "msg": "Field required", - "input": IsOneOf(None, {}), + "input": None, } ] } @@ -545,15 +371,10 @@ def test_list_validation_alias_missing(path: str): "/list-bytes-validation-alias", marks=pytest.mark.xfail(raises=AssertionError, strict=False), ), - pytest.param( - "/model-list-bytes-validation-alias", - marks=pytest.mark.xfail(raises=AssertionError, strict=False), - ), pytest.param( "/list-uploadfile-validation-alias", marks=pytest.mark.xfail(raises=AssertionError, strict=False), ), - "/model-list-uploadfile-validation-alias", ], ) def test_list_validation_alias_by_name(path: str): @@ -563,16 +384,13 @@ def test_list_validation_alias_by_name(path: str): response.text ) - assert response.json() == { + assert response.json() == { # pragma: no cover "detail": [ { "type": "missing", "loc": ["body", "p_val_alias"], "msg": "Field required", - "input": IsOneOf( # /model-list-bytes-validation-alias fails here - None, - {"p": [IsPartialDict({"size": 5}), IsPartialDict({"size": 5})]}, - ), + "input": None, } ] } @@ -584,9 +402,7 @@ def test_list_validation_alias_by_name(path: str): "path", [ "/list-bytes-validation-alias", - "/model-list-bytes-validation-alias", "/list-uploadfile-validation-alias", - "/model-list-uploadfile-validation-alias", ], ) def test_list_validation_alias_by_validation_alias(path: str): @@ -594,7 +410,7 @@ def test_list_validation_alias_by_validation_alias(path: str): response = client.post( path, files=[("p_val_alias", b"hello"), ("p_val_alias", b"world")] ) - assert response.status_code == 200, response.text # all 4 fail here + assert response.status_code == 200, response.text # all 2 fail here assert response.json() == {"file_size": [5, 5]} # pragma: no cover @@ -624,52 +440,12 @@ def read_list_uploadfile_alias_and_validation_alias( return {"file_size": [file.size for file in p]} -class FormModelRequiredBytesAliasAndValidationAlias(BaseModel): - p: List[bytes] = File(alias="p_alias", validation_alias="p_val_alias") - - -@app.post( - "/model-list-bytes-alias-and-validation-alias", - operation_id="model_list_bytes_alias_and_validation_alias", -) -def read_model_list_bytes_alias_and_validation_alias( - p: Annotated[ - FormModelRequiredBytesAliasAndValidationAlias, - Form( - media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed - ), - ], -): - return {"file_size": [len(file) for file in p.p]} # pragma: no cover - - -class FormModelRequiredUploadFileAliasAndValidationAlias(BaseModel): - p: List[UploadFile] = File(alias="p_alias", validation_alias="p_val_alias") - - -@app.post( - "/model-list-uploadfile-alias-and-validation-alias", - operation_id="model_list_uploadfile_alias_and_validation_alias", -) -def read_model_list_uploadfile_alias_and_validation_alias( - p: Annotated[ - FormModelRequiredUploadFileAliasAndValidationAlias, - Form( - media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed - ), - ], -): - return {"file_size": [file.size for file in p.p]} # pragma: no cover - - @needs_pydanticv2 @pytest.mark.parametrize( "path", [ "/list-bytes-alias-and-validation-alias", - "/model-list-bytes-alias-and-validation-alias", "/list-uploadfile-alias-and-validation-alias", - "/model-list-uploadfile-alias-and-validation-alias", ], ) def test_list_alias_and_validation_alias_schema(path: str): @@ -714,12 +490,10 @@ def test_list_alias_and_validation_alias_schema(path: str): "/list-bytes-alias-and-validation-alias", marks=pytest.mark.xfail(raises=AssertionError, strict=False), ), - "/model-list-bytes-alias-and-validation-alias", pytest.param( "/list-uploadfile-alias-and-validation-alias", marks=pytest.mark.xfail(raises=AssertionError, strict=False), ), - "/model-list-uploadfile-alias-and-validation-alias", ], ) def test_list_alias_and_validation_alias_missing(path: str): @@ -735,7 +509,7 @@ def test_list_alias_and_validation_alias_missing(path: str): "p_val_alias", # /list-*-alias-and-validation-alias fail here ], "msg": "Field required", - "input": IsOneOf(None, {}), + "input": None, } ] } @@ -747,9 +521,7 @@ def test_list_alias_and_validation_alias_missing(path: str): "path", [ "/list-bytes-alias-and-validation-alias", - "/model-list-bytes-alias-and-validation-alias", "/list-uploadfile-alias-and-validation-alias", - "/model-list-uploadfile-alias-and-validation-alias", ], ) def test_list_alias_and_validation_alias_by_name(path: str): @@ -766,11 +538,7 @@ def test_list_alias_and_validation_alias_by_name(path: str): "p_val_alias", # /list-*-alias-and-validation-alias fail here ], "msg": "Field required", - "input": IsOneOf( - None, - # /model-list-*-alias-and-validation-alias fail here - {"p": [IsPartialDict({"size": 5}), IsPartialDict({"size": 5})]}, - ), + "input": None, } ] } @@ -784,15 +552,10 @@ def test_list_alias_and_validation_alias_by_name(path: str): "/list-bytes-alias-and-validation-alias", marks=pytest.mark.xfail(raises=AssertionError, strict=False), ), - pytest.param( - "/model-list-bytes-alias-and-validation-alias", - marks=pytest.mark.xfail(raises=AssertionError, strict=False), - ), pytest.param( "/list-uploadfile-alias-and-validation-alias", marks=pytest.mark.xfail(raises=AssertionError, strict=False), ), - "/model-list-uploadfile-alias-and-validation-alias", ], ) def test_list_alias_and_validation_alias_by_alias(path: str): @@ -802,22 +565,13 @@ def test_list_alias_and_validation_alias_by_alias(path: str): response.text # /list-*-alias-and-validation-alias fails here ) - assert response.json() == { + assert response.json() == { # pragma: no cover "detail": [ { "type": "missing", "loc": ["body", "p_val_alias"], "msg": "Field required", - "input": IsOneOf( - None, - # /model-list-bytes-alias-and-validation-alias fails here - { - "p_alias": [ - IsPartialDict({"size": 5}), - IsPartialDict({"size": 5}), - ] - }, - ), + "input": None, } ] } @@ -829,9 +583,7 @@ def test_list_alias_and_validation_alias_by_alias(path: str): "path", [ "/list-bytes-alias-and-validation-alias", - "/model-list-bytes-alias-and-validation-alias", "/list-uploadfile-alias-and-validation-alias", - "/model-list-uploadfile-alias-and-validation-alias", ], ) def test_list_alias_and_validation_alias_by_validation_alias(path: str): @@ -839,7 +591,7 @@ def test_list_alias_and_validation_alias_by_validation_alias(path: str): response = client.post( path, files=[("p_val_alias", b"hello"), ("p_val_alias", b"world")] ) - assert response.status_code == 200, ( # all 4 fail here + assert response.status_code == 200, ( # all 2 fail here response.text ) assert response.json() == {"file_size": [5, 5]} # pragma: no cover diff --git a/tests/test_request_params/test_file/test_optional.py b/tests/test_request_params/test_file/test_optional.py index 0e584145b..14fc0a220 100644 --- a/tests/test_request_params/test_file/test_optional.py +++ b/tests/test_request_params/test_file/test_optional.py @@ -2,10 +2,9 @@ from typing import Optional import pytest from dirty_equals import IsDict -from fastapi import FastAPI, File, Form, UploadFile +from fastapi import FastAPI, File, UploadFile from fastapi._compat import PYDANTIC_V2 from fastapi.testclient import TestClient -from pydantic import BaseModel from typing_extensions import Annotated from tests.utils import needs_pydanticv2 @@ -28,45 +27,11 @@ async def read_optional_uploadfile(p: Annotated[Optional[UploadFile], File()] = return {"file_size": p.size if p else None} -class FormModelOptionalBytes(BaseModel): - p: Optional[bytes] = File(default=None) - - -@app.post("/model-optional-bytes", operation_id="model_optional_bytes") -async def read_model_optional_bytes( - p: Annotated[ - FormModelOptionalBytes, - Form( - media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed - ), - ], -): - return {"file_size": len(p.p) if p.p else None} - - -class FormModelOptionalUploadFile(BaseModel): - p: Optional[UploadFile] = File(default=None) - - -@app.post("/model-optional-uploadfile", operation_id="model_optional_uploadfile") -async def read_model_optional_uploadfile( - p: Annotated[ - FormModelOptionalUploadFile, - Form( - media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed - ), - ], -): - return {"file_size": p.p.size if p.p else None} - - @pytest.mark.parametrize( "path", [ "/optional-bytes", - "/model-optional-bytes", "/optional-uploadfile", - "/model-optional-uploadfile", ], ) def test_optional_schema(path: str): @@ -100,9 +65,7 @@ def test_optional_schema(path: str): "path", [ "/optional-bytes", - "/model-optional-bytes", "/optional-uploadfile", - "/model-optional-uploadfile", ], ) def test_optional_missing(path: str): @@ -116,9 +79,7 @@ def test_optional_missing(path: str): "path", [ "/optional-bytes", - "/model-optional-bytes", "/optional-uploadfile", - "/model-optional-uploadfile", ], ) def test_optional(path: str): @@ -146,40 +107,6 @@ async def read_optional_uploadfile_alias( return {"file_size": p.size if p else None} -class FormModelOptionalBytesAlias(BaseModel): - p: Optional[bytes] = File(default=None, alias="p_alias") - - -@app.post("/model-optional-bytes-alias", operation_id="model_optional_bytes_alias") -async def read_model_optional_bytes_alias( - p: Annotated[ - FormModelOptionalBytesAlias, - Form( - media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed - ), - ], -): - return {"file_size": len(p.p) if p.p else None} - - -class FormModelOptionalUploadFileAlias(BaseModel): - p: Optional[UploadFile] = File(default=None, alias="p_alias") - - -@app.post( - "/model-optional-uploadfile-alias", operation_id="model_optional_uploadfile_alias" -) -async def read_model_optional_uploadfile_alias( - p: Annotated[ - FormModelOptionalUploadFileAlias, - Form( - media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed - ), - ], -): - return {"file_size": p.p.size if p.p else None} - - @pytest.mark.xfail( raises=AssertionError, condition=PYDANTIC_V2, @@ -190,9 +117,7 @@ async def read_model_optional_uploadfile_alias( "path", [ "/optional-bytes-alias", - "/model-optional-bytes-alias", "/optional-uploadfile-alias", - "/model-optional-uploadfile-alias", ], ) def test_optional_alias_schema(path: str): @@ -226,9 +151,7 @@ def test_optional_alias_schema(path: str): "path", [ "/optional-bytes-alias", - "/model-optional-bytes-alias", "/optional-uploadfile-alias", - "/model-optional-uploadfile-alias", ], ) def test_optional_alias_missing(path: str): @@ -242,64 +165,28 @@ def test_optional_alias_missing(path: str): "path", [ "/optional-bytes-alias", - pytest.param( - "/model-optional-bytes-alias", - marks=pytest.mark.xfail( - raises=AssertionError, - condition=PYDANTIC_V2, - reason="Fails only with PDv2 model", - strict=False, - ), - ), "/optional-uploadfile-alias", - pytest.param( - "/model-optional-uploadfile-alias", - marks=pytest.mark.xfail( - raises=AssertionError, - condition=PYDANTIC_V2, - reason="Fails only with PDv2 model", - strict=False, - ), - ), ], ) def test_optional_alias_by_name(path: str): client = TestClient(app) response = client.post(path, files=[("p", b"hello")]) assert response.status_code == 200 - assert response.json() == {"file_size": None} # model-optional-*-alias fail here + assert response.json() == {"file_size": None} @pytest.mark.parametrize( "path", [ "/optional-bytes-alias", - pytest.param( - "/model-optional-bytes-alias", - marks=pytest.mark.xfail( - raises=AssertionError, - strict=False, - condition=PYDANTIC_V2, - reason="Fails only with PDv2 model", - ), - ), "/optional-uploadfile-alias", - pytest.param( - "/model-optional-uploadfile-alias", - marks=pytest.mark.xfail( - raises=AssertionError, - strict=False, - condition=PYDANTIC_V2, - reason="Fails only with PDv2 model", - ), - ), ], ) def test_optional_alias_by_alias(path: str): client = TestClient(app) response = client.post(path, files=[("p_alias", b"hello")]) assert response.status_code == 200, response.text - assert response.json() == {"file_size": 5} # model-optional-*-alias fail here + assert response.json() == {"file_size": 5} # ===================================================================================== @@ -325,52 +212,12 @@ def read_optional_uploadfile_validation_alias( return {"file_size": p.size if p else None} -class FormModelOptionalBytesValidationAlias(BaseModel): - p: Optional[bytes] = File(default=None, validation_alias="p_val_alias") - - -@app.post( - "/model-optional-bytes-validation-alias", - operation_id="model_optional_bytes_validation_alias", -) -def read_model_optional_bytes_validation_alias( - p: Annotated[ - FormModelOptionalBytesValidationAlias, - Form( - media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed - ), - ], -): - return {"file_size": len(p.p) if p.p else None} - - -class FormModelOptionalUploadFileValidationAlias(BaseModel): - p: Optional[UploadFile] = File(default=None, validation_alias="p_val_alias") - - -@app.post( - "/model-optional-uploadfile-validation-alias", - operation_id="model_optional_uploadfile_validation_alias", -) -def read_model_optional_uploadfile_validation_alias( - p: Annotated[ - FormModelOptionalUploadFileValidationAlias, - Form( - media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed - ), - ], -): - return {"file_size": p.p.size if p.p else None} - - @needs_pydanticv2 @pytest.mark.parametrize( "path", [ "/optional-bytes-validation-alias", - "/model-optional-uploadfile-validation-alias", "/optional-uploadfile-validation-alias", - "/model-optional-bytes-validation-alias", ], ) def test_optional_validation_alias_schema(path: str): @@ -405,9 +252,7 @@ def test_optional_validation_alias_schema(path: str): "path", [ "/optional-bytes-validation-alias", - "/model-optional-bytes-validation-alias", "/optional-uploadfile-validation-alias", - "/model-optional-uploadfile-validation-alias", ], ) def test_optional_validation_alias_missing(path: str): @@ -425,12 +270,10 @@ def test_optional_validation_alias_missing(path: str): "/optional-bytes-validation-alias", marks=pytest.mark.xfail(raises=AssertionError, strict=False), ), - "/model-optional-bytes-validation-alias", pytest.param( "/optional-uploadfile-validation-alias", marks=pytest.mark.xfail(raises=AssertionError, strict=False), ), - "/model-optional-uploadfile-validation-alias", ], ) def test_optional_validation_alias_by_name(path: str): @@ -450,23 +293,16 @@ def test_optional_validation_alias_by_name(path: str): "/optional-bytes-validation-alias", marks=pytest.mark.xfail(raises=AssertionError, strict=False), ), - pytest.param( - "/model-optional-bytes-validation-alias", - marks=pytest.mark.xfail(raises=AssertionError, strict=False), - ), pytest.param( "/optional-uploadfile-validation-alias", marks=pytest.mark.xfail(raises=AssertionError, strict=False), ), - "/model-optional-uploadfile-validation-alias", ], ) def test_optional_validation_alias_by_validation_alias(path: str): client = TestClient(app) response = client.post(path, files=[("p_val_alias", b"hello")]) - assert response.status_code == 200, ( - response.text # /model-optional-bytes-validation-alias fail here - ) + assert response.status_code == 200, response.text assert response.json() == {"file_size": 5} # /optional-*-validation-alias fail here @@ -498,56 +334,12 @@ def read_optional_uploadfile_alias_and_validation_alias( return {"file_size": p.size if p else None} -class FormModelOptionalBytesAliasAndValidationAlias(BaseModel): - p: Optional[bytes] = File( - default=None, alias="p_alias", validation_alias="p_val_alias" - ) - - -@app.post( - "/model-optional-bytes-alias-and-validation-alias", - operation_id="model_optional_bytes_alias_and_validation_alias", -) -def read_model_optional_bytes_alias_and_validation_alias( - p: Annotated[ - FormModelOptionalBytesAliasAndValidationAlias, - Form( - media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed - ), - ], -): - return {"file_size": len(p.p) if p.p else None} - - -class FormModelOptionalUploadFileAliasAndValidationAlias(BaseModel): - p: Optional[UploadFile] = File( - default=None, alias="p_alias", validation_alias="p_val_alias" - ) - - -@app.post( - "/model-optional-uploadfile-alias-and-validation-alias", - operation_id="model_optional_uploadfile_alias_and_validation_alias", -) -def read_model_optional_uploadfile_alias_and_validation_alias( - p: Annotated[ - FormModelOptionalUploadFileAliasAndValidationAlias, - Form( - media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed - ), - ], -): - return {"file_size": p.p.size if p.p else None} - - @needs_pydanticv2 @pytest.mark.parametrize( "path", [ "/optional-bytes-alias-and-validation-alias", - "/model-optional-bytes-alias-and-validation-alias", "/optional-uploadfile-alias-and-validation-alias", - "/model-optional-uploadfile-alias-and-validation-alias", ], ) def test_optional_alias_and_validation_alias_schema(path: str): @@ -582,9 +374,7 @@ def test_optional_alias_and_validation_alias_schema(path: str): "path", [ "/optional-bytes-alias-and-validation-alias", - "/model-optional-bytes-alias-and-validation-alias", "/optional-uploadfile-alias-and-validation-alias", - "/model-optional-uploadfile-alias-and-validation-alias", ], ) def test_optional_alias_and_validation_alias_missing(path: str): @@ -599,9 +389,7 @@ def test_optional_alias_and_validation_alias_missing(path: str): "path", [ "/optional-bytes-alias-and-validation-alias", - "/model-optional-bytes-alias-and-validation-alias", "/optional-uploadfile-alias-and-validation-alias", - "/model-optional-uploadfile-alias-and-validation-alias", ], ) def test_optional_alias_and_validation_alias_by_name(path: str): @@ -619,21 +407,17 @@ def test_optional_alias_and_validation_alias_by_name(path: str): "/optional-bytes-alias-and-validation-alias", marks=pytest.mark.xfail(raises=AssertionError, strict=False), ), - "/model-optional-bytes-alias-and-validation-alias", pytest.param( "/optional-uploadfile-alias-and-validation-alias", marks=pytest.mark.xfail(raises=AssertionError, strict=False), ), - "/model-optional-uploadfile-alias-and-validation-alias", ], ) def test_optional_alias_and_validation_alias_by_alias(path: str): client = TestClient(app) response = client.post(path, files=[("p_alias", b"hello")]) assert response.status_code == 200, response.text - assert response.json() == { - "file_size": None # model-optional-*-alias-and-validation-alias fail here - } + assert response.json() == {"file_size": None} @needs_pydanticv2 @@ -644,23 +428,16 @@ def test_optional_alias_and_validation_alias_by_alias(path: str): "/optional-bytes-alias-and-validation-alias", marks=pytest.mark.xfail(raises=AssertionError, strict=False), ), - pytest.param( - "/model-optional-bytes-alias-and-validation-alias", - marks=pytest.mark.xfail(raises=AssertionError, strict=False), - ), pytest.param( "/optional-uploadfile-alias-and-validation-alias", marks=pytest.mark.xfail(raises=AssertionError, strict=False), ), - "/model-optional-uploadfile-alias-and-validation-alias", ], ) def test_optional_alias_and_validation_alias_by_validation_alias(path: str): client = TestClient(app) response = client.post(path, files=[("p_val_alias", b"hello")]) - assert response.status_code == 200, ( - response.text # model-optional-bytes-alias-and-validation-alias fails here - ) + assert response.status_code == 200, response.text assert response.json() == { "file_size": 5 } # /optional-*-alias-and-validation-alias fail here diff --git a/tests/test_request_params/test_file/test_optional_list.py b/tests/test_request_params/test_file/test_optional_list.py index 13bc55e9f..f266642a6 100644 --- a/tests/test_request_params/test_file/test_optional_list.py +++ b/tests/test_request_params/test_file/test_optional_list.py @@ -2,10 +2,9 @@ from typing import List, Optional import pytest from dirty_equals import IsDict -from fastapi import FastAPI, File, Form, UploadFile +from fastapi import FastAPI, File, UploadFile from fastapi._compat import PYDANTIC_V2 from fastapi.testclient import TestClient -from pydantic import BaseModel from typing_extensions import Annotated from tests.utils import needs_pydanticv2 @@ -30,45 +29,11 @@ async def read_optional_list_uploadfile( return {"file_size": [file.size for file in p] if p else None} -class FormModelOptionalListBytes(BaseModel): - p: Optional[List[bytes]] = File(default=None) - - -@app.post("/model-optional-list-bytes") -async def read_model_optional_list_bytes( - p: Annotated[ - FormModelOptionalListBytes, - Form( - media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed - ), - ], -): - return {"file_size": [len(file) for file in p.p] if p.p else None} - - -class FormModelOptionalListUploadFile(BaseModel): - p: Optional[List[UploadFile]] = File(default=None) - - -@app.post("/model-optional-list-uploadfile") -async def read_model_optional_list_uploadfile( - p: Annotated[ - FormModelOptionalListUploadFile, - Form( - media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed - ), - ], -): - return {"file_size": [file.size for file in p.p] if p.p else None} - - @pytest.mark.parametrize( "path", [ "/optional-list-bytes", - "/model-optional-list-bytes", "/optional-list-uploadfile", - "/model-optional-list-uploadfile", ], ) def test_optional_list_schema(path: str): @@ -109,9 +74,7 @@ def test_optional_list_schema(path: str): "path", [ "/optional-list-bytes", - "/model-optional-list-bytes", "/optional-list-uploadfile", - "/model-optional-list-uploadfile", ], ) def test_optional_list_missing(path: str): @@ -133,17 +96,7 @@ def test_optional_list_missing(path: str): strict=False, ), ), - pytest.param( - "/model-optional-list-bytes", - marks=pytest.mark.xfail( - raises=(TypeError, AssertionError), - condition=PYDANTIC_V2, - reason="Fails only with PDv2 due to #14297", - strict=False, - ), - ), "/optional-list-uploadfile", - "/model-optional-list-uploadfile", ], ) def test_optional_list(path: str): @@ -171,38 +124,6 @@ async def read_optional_list_uploadfile_alias( return {"file_size": [file.size for file in p] if p else None} -class FormModelOptionalListBytesAlias(BaseModel): - p: Optional[List[bytes]] = File(default=None, alias="p_alias") - - -@app.post("/model-optional-list-bytes-alias") -async def read_model_optional_list_bytes_alias( - p: Annotated[ - FormModelOptionalListBytesAlias, - Form( - media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed - ), - ], -): - return {"file_size": [len(file) for file in p.p] if p.p else None} - - -class FormModelOptionalListUploadFileAlias(BaseModel): - p: Optional[List[UploadFile]] = File(default=None, alias="p_alias") - - -@app.post("/model-optional-list-uploadfile-alias") -async def read_model_optional_list_uploadfile_alias( - p: Annotated[ - FormModelOptionalListUploadFileAlias, - Form( - media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed - ), - ], -): - return {"file_size": [file.size for file in p.p] if p.p else None} - - @pytest.mark.xfail( raises=AssertionError, condition=PYDANTIC_V2, @@ -213,9 +134,7 @@ async def read_model_optional_list_uploadfile_alias( "path", [ "/optional-list-bytes-alias", - "/model-optional-list-bytes-alias", "/optional-list-uploadfile-alias", - "/model-optional-list-uploadfile-alias", ], ) def test_optional_list_alias_schema(path: str): @@ -256,9 +175,7 @@ def test_optional_list_alias_schema(path: str): "path", [ "/optional-list-bytes-alias", - "/model-optional-list-bytes-alias", "/optional-list-uploadfile-alias", - "/model-optional-list-uploadfile-alias", ], ) def test_optional_list_alias_missing(path: str): @@ -272,33 +189,13 @@ def test_optional_list_alias_missing(path: str): "path", [ "/optional-list-bytes-alias", - pytest.param( - "/model-optional-list-bytes-alias", - marks=pytest.mark.xfail( - raises=AssertionError, - condition=PYDANTIC_V2, - reason="Fails only with PDv2 model", - strict=False, - ), - ), "/optional-list-uploadfile-alias", - pytest.param( - "/model-optional-list-uploadfile-alias", - marks=pytest.mark.xfail( - raises=AssertionError, - condition=PYDANTIC_V2, - reason="Fails only with PDv2 model", - strict=False, - ), - ), ], ) def test_optional_list_alias_by_name(path: str): client = TestClient(app) response = client.post(path, files=[("p", b"hello"), ("p", b"world")]) - assert response.status_code == 200, ( - response.text # model-optional-list-*-alias fail here - ) + assert response.status_code == 200, response.text assert response.json() == {"file_size": None} @@ -314,34 +211,14 @@ def test_optional_list_alias_by_name(path: str): reason="Fails only with PDv2 model due to #14297", ), ), - pytest.param( - "/model-optional-list-bytes-alias", - marks=pytest.mark.xfail( - raises=(TypeError, AssertionError), - strict=False, - condition=PYDANTIC_V2, - reason="Fails only with PDv2 model due to #14297", - ), - ), "/optional-list-uploadfile-alias", - pytest.param( - "/model-optional-list-uploadfile-alias", - marks=pytest.mark.xfail( - raises=AssertionError, - strict=False, - condition=PYDANTIC_V2, - reason="Fails only with PDv2 model", - ), - ), ], ) def test_optional_list_alias_by_alias(path: str): client = TestClient(app) response = client.post(path, files=[("p_alias", b"hello"), ("p_alias", b"world")]) assert response.status_code == 200, response.text - assert response.json() == { - "file_size": [5, 5] # /model-optional-list-uploadfile-alias fails here - } + assert response.json() == {"file_size": [5, 5]} # ===================================================================================== @@ -364,46 +241,12 @@ def read_optional_list_uploadfile_validation_alias( return {"file_size": [file.size for file in p] if p else None} -class FormModelOptionalListBytesValidationAlias(BaseModel): - p: Optional[List[bytes]] = File(default=None, validation_alias="p_val_alias") - - -@app.post("/model-optional-list-bytes-validation-alias") -def read_model_optional_list_bytes_validation_alias( - p: Annotated[ - FormModelOptionalListBytesValidationAlias, - Form( - media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed - ), - ], -): - return {"file_size": [len(file) for file in p.p] if p.p else None} - - -class FormModelOptionalListUploadFileValidationAlias(BaseModel): - p: Optional[List[UploadFile]] = File(default=None, validation_alias="p_val_alias") - - -@app.post("/model-optional-list-uploadfile-validation-alias") -def read_model_optional_list_uploadfile_validation_alias( - p: Annotated[ - FormModelOptionalListUploadFileValidationAlias, - Form( - media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed - ), - ], -): - return {"file_size": [file.size for file in p.p] if p.p else None} - - @needs_pydanticv2 @pytest.mark.parametrize( "path", [ "/optional-list-bytes-validation-alias", - "/model-optional-list-uploadfile-validation-alias", "/optional-list-uploadfile-validation-alias", - "/model-optional-list-bytes-validation-alias", ], ) def test_optional_validation_alias_schema(path: str): @@ -445,9 +288,7 @@ def test_optional_validation_alias_schema(path: str): "path", [ "/optional-list-bytes-validation-alias", - "/model-optional-list-bytes-validation-alias", "/optional-list-uploadfile-validation-alias", - "/model-optional-list-uploadfile-validation-alias", ], ) def test_optional_validation_alias_missing(path: str): @@ -469,19 +310,10 @@ def test_optional_validation_alias_missing(path: str): reason="Fails due to #14297", ), ), - pytest.param( - "/model-optional-list-bytes-validation-alias", - marks=pytest.mark.xfail( - raises=(TypeError, AssertionError), - strict=False, - reason="Fails due to #14297", - ), - ), pytest.param( "/optional-list-uploadfile-validation-alias", marks=pytest.mark.xfail(raises=AssertionError, strict=False), ), - "/model-optional-list-uploadfile-validation-alias", ], ) def test_optional_validation_alias_by_name(path: str): @@ -499,9 +331,7 @@ def test_optional_validation_alias_by_name(path: str): "path", [ "/optional-list-bytes-validation-alias", - "/model-optional-list-bytes-validation-alias", "/optional-list-uploadfile-validation-alias", - "/model-optional-list-uploadfile-validation-alias", ], ) def test_optional_validation_alias_by_validation_alias(path: str): @@ -509,9 +339,7 @@ def test_optional_validation_alias_by_validation_alias(path: str): response = client.post( path, files=[("p_val_alias", b"hello"), ("p_val_alias", b"world")] ) - assert response.status_code == 200, ( - response.text # /model-optional-list-*-validation-alias fail here - ) + assert response.status_code == 200, response.text assert response.json() == { "file_size": [5, 5] # /optional-list-*-validation-alias fail here } @@ -540,50 +368,12 @@ def read_optional_list_uploadfile_alias_and_validation_alias( return {"file_size": [file.size for file in p] if p else None} -class FormModelOptionalListBytesAliasAndValidationAlias(BaseModel): - p: Optional[List[bytes]] = File( - default=None, alias="p_alias", validation_alias="p_val_alias" - ) - - -@app.post("/model-optional-list-bytes-alias-and-validation-alias") -def read_model_optional_list_bytes_alias_and_validation_alias( - p: Annotated[ - FormModelOptionalListBytesAliasAndValidationAlias, - Form( - media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed - ), - ], -): - return {"file_size": [len(file) for file in p.p] if p.p else None} - - -class FormModelOptionalListUploadFileAliasAndValidationAlias(BaseModel): - p: Optional[List[UploadFile]] = File( - default=None, alias="p_alias", validation_alias="p_val_alias" - ) - - -@app.post("/model-optional-list-uploadfile-alias-and-validation-alias") -def read_model_optional_list_uploadfile_alias_and_validation_alias( - p: Annotated[ - FormModelOptionalListUploadFileAliasAndValidationAlias, - Form( - media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed - ), - ], -): - return {"file_size": [file.size for file in p.p] if p.p else None} - - @needs_pydanticv2 @pytest.mark.parametrize( "path", [ "/optional-list-bytes-alias-and-validation-alias", - "/model-optional-list-bytes-alias-and-validation-alias", "/optional-list-uploadfile-alias-and-validation-alias", - "/model-optional-list-uploadfile-alias-and-validation-alias", ], ) def test_optional_list_alias_and_validation_alias_schema(path: str): @@ -625,9 +415,7 @@ def test_optional_list_alias_and_validation_alias_schema(path: str): "path", [ "/optional-list-bytes-alias-and-validation-alias", - "/model-optional-list-bytes-alias-and-validation-alias", "/optional-list-uploadfile-alias-and-validation-alias", - "/model-optional-list-uploadfile-alias-and-validation-alias", ], ) def test_optional_list_alias_and_validation_alias_missing(path: str): @@ -642,9 +430,7 @@ def test_optional_list_alias_and_validation_alias_missing(path: str): "path", [ "/optional-list-bytes-alias-and-validation-alias", - "/model-optional-list-bytes-alias-and-validation-alias", "/optional-list-uploadfile-alias-and-validation-alias", - "/model-optional-list-uploadfile-alias-and-validation-alias", ], ) def test_optional_list_alias_and_validation_alias_by_name(path: str): @@ -666,19 +452,10 @@ def test_optional_list_alias_and_validation_alias_by_name(path: str): reason="Fails due to #14297", ), ), - pytest.param( - "/model-optional-list-bytes-alias-and-validation-alias", - marks=pytest.mark.xfail( - raises=(TypeError, AssertionError), - strict=False, - reason="Fails due to #14297", - ), - ), pytest.param( "/optional-list-uploadfile-alias-and-validation-alias", marks=pytest.mark.xfail(raises=AssertionError, strict=False), ), - "/model-optional-list-uploadfile-alias-and-validation-alias", ], ) def test_optional_list_alias_and_validation_alias_by_alias(path: str): @@ -696,9 +473,7 @@ def test_optional_list_alias_and_validation_alias_by_alias(path: str): "path", [ "/optional-list-bytes-alias-and-validation-alias", - "/model-optional-list-bytes-alias-and-validation-alias", "/optional-list-uploadfile-alias-and-validation-alias", - "/model-optional-list-uploadfile-alias-and-validation-alias", ], ) def test_optional_list_alias_and_validation_alias_by_validation_alias(path: str): @@ -706,9 +481,7 @@ def test_optional_list_alias_and_validation_alias_by_validation_alias(path: str) response = client.post( path, files=[("p_val_alias", b"hello"), ("p_val_alias", b"world")] ) - assert response.status_code == 200, ( - response.text # /model-optional-list-*-alias-and-validation-alias fails here - ) + assert response.status_code == 200, response.text assert response.json() == { "file_size": [5, 5] # /optional-list-*-alias-and-validation-alias fail here } diff --git a/tests/test_request_params/test_file/test_required.py b/tests/test_request_params/test_file/test_required.py index 580bb9cd4..e50597370 100644 --- a/tests/test_request_params/test_file/test_required.py +++ b/tests/test_request_params/test_file/test_required.py @@ -1,9 +1,8 @@ import pytest -from dirty_equals import IsDict, IsOneOf, IsPartialDict -from fastapi import FastAPI, File, Form, UploadFile +from dirty_equals import IsDict +from fastapi import FastAPI, File, UploadFile from fastapi._compat import PYDANTIC_V2 from fastapi.testclient import TestClient -from pydantic import BaseModel from typing_extensions import Annotated from tests.utils import needs_pydanticv2 @@ -26,45 +25,11 @@ async def read_required_uploadfile(p: Annotated[UploadFile, File()]): return {"file_size": p.size} -class FormModelRequiredBytes(BaseModel): - p: bytes = File() - - -@app.post("/model-required-bytes", operation_id="model_required_bytes") -async def read_model_required_bytes( - p: Annotated[ - FormModelRequiredBytes, - Form( - media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed - ), - ], -): - return {"file_size": len(p.p)} - - -class FormModelRequiredUploadFile(BaseModel): - p: UploadFile = File() - - -@app.post("/model-required-uploadfile", operation_id="model_required_uploadfile") -async def read_model_required_uploadfile( - p: Annotated[ - FormModelRequiredUploadFile, - Form( - media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed - ), - ], -): - return {"file_size": p.p.size} - - @pytest.mark.parametrize( "path", [ "/required-bytes", - "/model-required-bytes", "/required-uploadfile", - "/model-required-uploadfile", ], ) def test_required_schema(path: str): @@ -85,9 +50,7 @@ def test_required_schema(path: str): "path", [ "/required-bytes", - "/model-required-bytes", "/required-uploadfile", - "/model-required-uploadfile", ], ) def test_required_missing(path: str): @@ -101,7 +64,7 @@ def test_required_missing(path: str): "type": "missing", "loc": ["body", "p"], "msg": "Field required", - "input": IsOneOf(None, {}), + "input": None, } ] } @@ -123,9 +86,7 @@ def test_required_missing(path: str): "path", [ "/required-bytes", - "/model-required-bytes", "/required-uploadfile", - "/model-required-uploadfile", ], ) def test_required(path: str): @@ -151,40 +112,6 @@ async def read_required_uploadfile_alias( return {"file_size": p.size} -class FormModelRequiredBytesAlias(BaseModel): - p: bytes = File(alias="p_alias") - - -@app.post("/model-required-bytes-alias", operation_id="model_required_bytes_alias") -async def read_model_required_bytes_alias( - p: Annotated[ - FormModelRequiredBytesAlias, - Form( - media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed - ), - ], -): - return {"file_size": len(p.p)} - - -class FormModelRequiredUploadFileAlias(BaseModel): - p: UploadFile = File(alias="p_alias") - - -@app.post( - "/model-required-uploadfile-alias", operation_id="model_required_uploadfile_alias" -) -async def read_model_required_uploadfile_alias( - p: Annotated[ - FormModelRequiredUploadFileAlias, - Form( - media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed - ), - ], -): - return {"file_size": p.p.size} - - @pytest.mark.xfail( raises=AssertionError, condition=PYDANTIC_V2, @@ -195,9 +122,7 @@ async def read_model_required_uploadfile_alias( "path", [ "/required-bytes-alias", - "/model-required-bytes-alias", "/required-uploadfile-alias", - "/model-required-uploadfile-alias", ], ) def test_required_alias_schema(path: str): @@ -218,25 +143,7 @@ def test_required_alias_schema(path: str): "path", [ "/required-bytes-alias", - pytest.param( - "/model-required-bytes-alias", - marks=pytest.mark.xfail( - raises=AssertionError, - strict=False, - condition=PYDANTIC_V2, - reason="Fails only with PDv2 model", - ), - ), "/required-uploadfile-alias", - pytest.param( - "/model-required-uploadfile-alias", - marks=pytest.mark.xfail( - raises=AssertionError, - strict=False, - condition=PYDANTIC_V2, - reason="Fails only with PDv2 model", - ), - ), ], ) def test_required_alias_missing(path: str): @@ -248,9 +155,9 @@ def test_required_alias_missing(path: str): "detail": [ { "type": "missing", - "loc": ["body", "p_alias"], # model-required-*-alias fail here + "loc": ["body", "p_alias"], "msg": "Field required", - "input": IsOneOf(None, {}), + "input": None, } ] } @@ -272,31 +179,13 @@ def test_required_alias_missing(path: str): "path", [ "/required-bytes-alias", - pytest.param( - "/model-required-bytes-alias", - marks=pytest.mark.xfail( - raises=AssertionError, - condition=PYDANTIC_V2, - reason="Fails only with PDv2 model", - strict=False, - ), - ), "/required-uploadfile-alias", - pytest.param( - "/model-required-uploadfile-alias", - marks=pytest.mark.xfail( - raises=AssertionError, - condition=PYDANTIC_V2, - reason="Fails only with PDv2 model", - strict=False, - ), - ), ], ) def test_required_alias_by_name(path: str): client = TestClient(app) response = client.post(path, files=[("p", b"hello")]) - assert response.status_code == 422 # model-required-upload-alias fail here + assert response.status_code == 422 assert response.json() == IsDict( { "detail": [ @@ -304,11 +193,7 @@ def test_required_alias_by_name(path: str): "type": "missing", "loc": ["body", "p_alias"], "msg": "Field required", - "input": IsOneOf( # model-required-bytes-alias fail here - None, - {"p": IsPartialDict({"size": 5})}, - {"p": b"hello"}, # ToDo: check this - ), + "input": None, } ] } @@ -330,33 +215,13 @@ def test_required_alias_by_name(path: str): "path", [ "/required-bytes-alias", - pytest.param( - "/model-required-bytes-alias", - marks=pytest.mark.xfail( - raises=AssertionError, - strict=False, - condition=PYDANTIC_V2, - reason="Fails only with PDv2 model", - ), - ), "/required-uploadfile-alias", - pytest.param( - "/model-required-uploadfile-alias", - marks=pytest.mark.xfail( - raises=AssertionError, - strict=False, - condition=PYDANTIC_V2, - reason="Fails only with PDv2 model", - ), - ), ], ) def test_required_alias_by_alias(path: str): client = TestClient(app) response = client.post(path, files=[("p_alias", b"hello")]) - assert response.status_code == 200, ( # model-required-*-alias fail here - response.text - ) + assert response.status_code == 200, response.text assert response.json() == {"file_size": 5} @@ -383,52 +248,12 @@ def read_required_uploadfile_validation_alias( return {"file_size": p.size} -class FormModelRequiredBytesValidationAlias(BaseModel): - p: bytes = File(validation_alias="p_val_alias") - - -@app.post( - "/model-required-bytes-validation-alias", - operation_id="model_required_bytes_validation_alias", -) -def read_model_required_bytes_validation_alias( - p: Annotated[ - FormModelRequiredBytesValidationAlias, - Form( - media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed - ), - ], -): - return {"file_size": len(p.p)} # pragma: no cover - - -class FormModelRequiredUploadFileValidationAlias(BaseModel): - p: UploadFile = File(validation_alias="p_val_alias") - - -@app.post( - "/model-required-uploadfile-validation-alias", - operation_id="model_required_uploadfile_validation_alias", -) -def read_model_required_uploadfile_validation_alias( - p: Annotated[ - FormModelRequiredUploadFileValidationAlias, - Form( - media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed - ), - ], -): - return {"file_size": p.p.size} - - @needs_pydanticv2 @pytest.mark.parametrize( "path", [ "/required-bytes-validation-alias", - "/model-required-uploadfile-validation-alias", "/required-uploadfile-validation-alias", - "/model-required-bytes-validation-alias", ], ) def test_required_validation_alias_schema(path: str): @@ -457,12 +282,10 @@ def test_required_validation_alias_schema(path: str): "/required-bytes-validation-alias", marks=pytest.mark.xfail(raises=AssertionError, strict=False), ), - "/model-required-bytes-validation-alias", pytest.param( "/required-uploadfile-validation-alias", marks=pytest.mark.xfail(raises=AssertionError, strict=False), ), - "/model-required-uploadfile-validation-alias", ], ) def test_required_validation_alias_missing(path: str): @@ -478,7 +301,7 @@ def test_required_validation_alias_missing(path: str): "p_val_alias", ], "msg": "Field required", - "input": IsOneOf(None, {}), + "input": None, } ] } @@ -492,15 +315,10 @@ def test_required_validation_alias_missing(path: str): "/required-bytes-validation-alias", marks=pytest.mark.xfail(raises=AssertionError, strict=False), ), - pytest.param( - "/model-required-bytes-validation-alias", - marks=pytest.mark.xfail(raises=AssertionError, strict=False), - ), pytest.param( "/required-uploadfile-validation-alias", marks=pytest.mark.xfail(raises=AssertionError, strict=False), ), - "/model-required-uploadfile-validation-alias", ], ) def test_required_validation_alias_by_name(path: str): @@ -510,15 +328,13 @@ def test_required_validation_alias_by_name(path: str): response.text ) - assert response.json() == { + assert response.json() == { # pragma: no cover "detail": [ { "type": "missing", "loc": ["body", "p_val_alias"], "msg": "Field required", - "input": IsOneOf( # /model-required-bytes-validation-alias fails here - None, {"p": IsPartialDict({"size": 5})} - ), + "input": None, } ] } @@ -532,24 +348,19 @@ def test_required_validation_alias_by_name(path: str): "/required-bytes-validation-alias", marks=pytest.mark.xfail(raises=AssertionError, strict=False), ), - pytest.param( - "/model-required-bytes-validation-alias", - marks=pytest.mark.xfail(raises=AssertionError, strict=False), - ), pytest.param( "/required-uploadfile-validation-alias", marks=pytest.mark.xfail(raises=AssertionError, strict=False), ), - "/model-required-uploadfile-validation-alias", ], ) def test_required_validation_alias_by_validation_alias(path: str): client = TestClient(app) response = client.post(path, files=[("p_val_alias", b"hello")]) - assert response.status_code == 200, ( # all 3 fail here + assert response.status_code == 200, ( # all 2 fail here response.text ) - assert response.json() == {"file_size": 5} + assert response.json() == {"file_size": 5} # pragma: no cover # ===================================================================================== @@ -576,52 +387,12 @@ def read_required_uploadfile_alias_and_validation_alias( return {"file_size": p.size} -class FormModelRequiredBytesAliasAndValidationAlias(BaseModel): - p: bytes = File(alias="p_alias", validation_alias="p_val_alias") - - -@app.post( - "/model-required-bytes-alias-and-validation-alias", - operation_id="model_required_bytes_alias_and_validation_alias", -) -def read_model_required_bytes_alias_and_validation_alias( - p: Annotated[ - FormModelRequiredBytesAliasAndValidationAlias, - Form( - media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed - ), - ], -): - return {"file_size": len(p.p)} # pragma: no cover - - -class FormModelRequiredUploadFileAliasAndValidationAlias(BaseModel): - p: UploadFile = File(alias="p_alias", validation_alias="p_val_alias") - - -@app.post( - "/model-required-uploadfile-alias-and-validation-alias", - operation_id="model_required_uploadfile_alias_and_validation_alias", -) -def read_model_required_uploadfile_alias_and_validation_alias( - p: Annotated[ - FormModelRequiredUploadFileAliasAndValidationAlias, - Form( - media_type="multipart/form-data" # Remove media_type when https://github.com/fastapi/fastapi/pull/14343 is fixed - ), - ], -): - return {"file_size": p.p.size} - - @needs_pydanticv2 @pytest.mark.parametrize( "path", [ "/required-bytes-alias-and-validation-alias", - "/model-required-bytes-alias-and-validation-alias", "/required-uploadfile-alias-and-validation-alias", - "/model-required-uploadfile-alias-and-validation-alias", ], ) def test_required_alias_and_validation_alias_schema(path: str): @@ -650,12 +421,10 @@ def test_required_alias_and_validation_alias_schema(path: str): "/required-bytes-alias-and-validation-alias", marks=pytest.mark.xfail(raises=AssertionError, strict=False), ), - "/model-required-bytes-alias-and-validation-alias", pytest.param( "/required-uploadfile-alias-and-validation-alias", marks=pytest.mark.xfail(raises=AssertionError, strict=False), ), - "/model-required-uploadfile-alias-and-validation-alias", ], ) def test_required_alias_and_validation_alias_missing(path: str): @@ -671,7 +440,7 @@ def test_required_alias_and_validation_alias_missing(path: str): "p_val_alias", # /required-*-alias-and-validation-alias fail here ], "msg": "Field required", - "input": IsOneOf(None, {}), + "input": None, } ] } @@ -685,12 +454,10 @@ def test_required_alias_and_validation_alias_missing(path: str): "/required-bytes-alias-and-validation-alias", marks=pytest.mark.xfail(raises=AssertionError, strict=False), ), - "/model-required-bytes-alias-and-validation-alias", pytest.param( "/required-uploadfile-alias-and-validation-alias", marks=pytest.mark.xfail(raises=AssertionError, strict=False), ), - "/model-required-uploadfile-alias-and-validation-alias", ], ) def test_required_alias_and_validation_alias_by_name(path: str): @@ -707,7 +474,7 @@ def test_required_alias_and_validation_alias_by_name(path: str): "p_val_alias", # /required-*-alias-and-validation-alias fail here ], "msg": "Field required", - "input": IsOneOf(None, {"p": IsPartialDict({"size": 5})}), + "input": None, } ] } @@ -721,15 +488,10 @@ def test_required_alias_and_validation_alias_by_name(path: str): "/required-bytes-alias-and-validation-alias", marks=pytest.mark.xfail(raises=AssertionError, strict=False), ), - pytest.param( - "/model-required-bytes-alias-and-validation-alias", - marks=pytest.mark.xfail(raises=AssertionError, strict=False), - ), pytest.param( "/required-uploadfile-alias-and-validation-alias", marks=pytest.mark.xfail(raises=AssertionError, strict=False), ), - "/model-required-uploadfile-alias-and-validation-alias", ], ) def test_required_alias_and_validation_alias_by_alias(path: str): @@ -739,17 +501,13 @@ def test_required_alias_and_validation_alias_by_alias(path: str): response.text # /required-*-alias-and-validation-alias fails here ) - assert response.json() == { + assert response.json() == { # pragma: no cover "detail": [ { "type": "missing", "loc": ["body", "p_val_alias"], "msg": "Field required", - "input": IsOneOf( - None, - # /model-required-uploadfile-alias-and-validation-alias fails here - {"p_alias": IsPartialDict({"size": 5})}, - ), + "input": None, } ] } @@ -763,21 +521,16 @@ def test_required_alias_and_validation_alias_by_alias(path: str): "/required-bytes-alias-and-validation-alias", marks=pytest.mark.xfail(raises=AssertionError, strict=False), ), - pytest.param( - "/model-required-bytes-alias-and-validation-alias", - marks=pytest.mark.xfail(raises=AssertionError, strict=False), - ), pytest.param( "/required-uploadfile-alias-and-validation-alias", marks=pytest.mark.xfail(raises=AssertionError, strict=False), ), - "/model-required-uploadfile-alias-and-validation-alias", ], ) def test_required_alias_and_validation_alias_by_validation_alias(path: str): client = TestClient(app) response = client.post(path, files=[("p_val_alias", b"hello")]) - assert response.status_code == 200, ( # all 3 fail here + assert response.status_code == 200, ( # all 2 fail here response.text ) - assert response.json() == {"file_size": 5} + assert response.json() == {"file_size": 5} # pragma: no cover