mirror of https://github.com/tiangolo/fastapi.git
✨ Add support for declaring `UploadFile` parameters without explicit `File()` (#4469)
This commit is contained in:
parent
59b1f353b3
commit
1bf55200a9
|
|
@ -17,7 +17,7 @@ Import `File` and `UploadFile` from `fastapi`:
|
||||||
{!../../../docs_src/request_files/tutorial001.py!}
|
{!../../../docs_src/request_files/tutorial001.py!}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Define `File` parameters
|
## Define `File` Parameters
|
||||||
|
|
||||||
Create file parameters the same way you would for `Body` or `Form`:
|
Create file parameters the same way you would for `Body` or `Form`:
|
||||||
|
|
||||||
|
|
@ -41,7 +41,7 @@ Have in mind that this means that the whole contents will be stored in memory. T
|
||||||
|
|
||||||
But there are several cases in which you might benefit from using `UploadFile`.
|
But there are several cases in which you might benefit from using `UploadFile`.
|
||||||
|
|
||||||
## `File` parameters with `UploadFile`
|
## `File` Parameters with `UploadFile`
|
||||||
|
|
||||||
Define a `File` parameter with a type of `UploadFile`:
|
Define a `File` parameter with a type of `UploadFile`:
|
||||||
|
|
||||||
|
|
@ -51,6 +51,7 @@ Define a `File` parameter with a type of `UploadFile`:
|
||||||
|
|
||||||
Using `UploadFile` has several advantages over `bytes`:
|
Using `UploadFile` has several advantages over `bytes`:
|
||||||
|
|
||||||
|
* You don't have to use `File()` in the default value.
|
||||||
* It uses a "spooled" file:
|
* It uses a "spooled" file:
|
||||||
* A file stored in memory up to a maximum size limit, and after passing this limit it will be stored in disk.
|
* A file stored in memory up to a maximum size limit, and after passing this limit it will be stored in disk.
|
||||||
* This means that it will work well for large files like images, videos, large binaries, etc. without consuming all the memory.
|
* This means that it will work well for large files like images, videos, large binaries, etc. without consuming all the memory.
|
||||||
|
|
@ -113,7 +114,31 @@ The way HTML forms (`<form></form>`) sends the data to the server normally uses
|
||||||
|
|
||||||
This is not a limitation of **FastAPI**, it's part of the HTTP protocol.
|
This is not a limitation of **FastAPI**, it's part of the HTTP protocol.
|
||||||
|
|
||||||
## Multiple file uploads
|
## Optional File Upload
|
||||||
|
|
||||||
|
You can make a file optional by using standard type annotations:
|
||||||
|
|
||||||
|
=== "Python 3.6 and above"
|
||||||
|
|
||||||
|
```Python hl_lines="9 17"
|
||||||
|
{!> ../../../docs_src/request_files/tutorial001_02.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "Python 3.9 and above"
|
||||||
|
|
||||||
|
```Python hl_lines="7 14"
|
||||||
|
{!> ../../../docs_src/request_files/tutorial001_02_py310.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
## `UploadFile` with Additional Metadata
|
||||||
|
|
||||||
|
You can also use `File()` with `UploadFile` to set additional parameters in `File()`, for example additional metadata:
|
||||||
|
|
||||||
|
```Python hl_lines="13"
|
||||||
|
{!../../../docs_src/request_files/tutorial001_03.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Multiple File Uploads
|
||||||
|
|
||||||
It's possible to upload several files at the same time.
|
It's possible to upload several files at the same time.
|
||||||
|
|
||||||
|
|
@ -140,6 +165,22 @@ You will receive, as declared, a `list` of `bytes` or `UploadFile`s.
|
||||||
|
|
||||||
**FastAPI** provides the same `starlette.responses` as `fastapi.responses` just as a convenience for you, the developer. But most of the available responses come directly from Starlette.
|
**FastAPI** provides the same `starlette.responses` as `fastapi.responses` just as a convenience for you, the developer. But most of the available responses come directly from Starlette.
|
||||||
|
|
||||||
|
### Multiple File Uploads with Additional Metadata
|
||||||
|
|
||||||
|
And the same way as before, you can use `File()` to set additional parameters, even for `UploadFile`:
|
||||||
|
|
||||||
|
=== "Python 3.6 and above"
|
||||||
|
|
||||||
|
```Python hl_lines="18"
|
||||||
|
{!> ../../../docs_src/request_files/tutorial003.py!}
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "Python 3.9 and above"
|
||||||
|
|
||||||
|
```Python hl_lines="16"
|
||||||
|
{!> ../../../docs_src/request_files/tutorial003_py39.py!}
|
||||||
|
```
|
||||||
|
|
||||||
## Recap
|
## Recap
|
||||||
|
|
||||||
Use `File` to declare files to be uploaded as input parameters (as form data).
|
Use `File` to declare files to be uploaded as input parameters (as form data).
|
||||||
|
|
|
||||||
|
|
@ -9,5 +9,5 @@ async def create_file(file: bytes = File(...)):
|
||||||
|
|
||||||
|
|
||||||
@app.post("/uploadfile/")
|
@app.post("/uploadfile/")
|
||||||
async def create_upload_file(file: UploadFile = File(...)):
|
async def create_upload_file(file: UploadFile):
|
||||||
return {"filename": file.filename}
|
return {"filename": file.filename}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from fastapi import FastAPI, File, UploadFile
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/files/")
|
||||||
|
async def create_file(file: Optional[bytes] = File(None)):
|
||||||
|
if not file:
|
||||||
|
return {"message": "No file sent"}
|
||||||
|
else:
|
||||||
|
return {"file_size": len(file)}
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/uploadfile/")
|
||||||
|
async def create_upload_file(file: Optional[UploadFile] = None):
|
||||||
|
if not file:
|
||||||
|
return {"message": "No upload file sent"}
|
||||||
|
else:
|
||||||
|
return {"filename": file.filename}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
from fastapi import FastAPI, File, UploadFile
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/files/")
|
||||||
|
async def create_file(file: bytes | None = File(None)):
|
||||||
|
if not file:
|
||||||
|
return {"message": "No file sent"}
|
||||||
|
else:
|
||||||
|
return {"file_size": len(file)}
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/uploadfile/")
|
||||||
|
async def create_upload_file(file: UploadFile | None = None):
|
||||||
|
if not file:
|
||||||
|
return {"message": "No upload file sent"}
|
||||||
|
else:
|
||||||
|
return {"filename": file.filename}
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
from fastapi import FastAPI, File, UploadFile
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/files/")
|
||||||
|
async def create_file(file: bytes = File(..., description="A file read as bytes")):
|
||||||
|
return {"file_size": len(file)}
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/uploadfile/")
|
||||||
|
async def create_upload_file(
|
||||||
|
file: UploadFile = File(..., description="A file read as UploadFile")
|
||||||
|
):
|
||||||
|
return {"filename": file.filename}
|
||||||
|
|
@ -12,7 +12,7 @@ async def create_files(files: List[bytes] = File(...)):
|
||||||
|
|
||||||
|
|
||||||
@app.post("/uploadfiles/")
|
@app.post("/uploadfiles/")
|
||||||
async def create_upload_files(files: List[UploadFile] = File(...)):
|
async def create_upload_files(files: List[UploadFile]):
|
||||||
return {"filenames": [file.filename for file in files]}
|
return {"filenames": [file.filename for file in files]}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ async def create_files(files: list[bytes] = File(...)):
|
||||||
|
|
||||||
|
|
||||||
@app.post("/uploadfiles/")
|
@app.post("/uploadfiles/")
|
||||||
async def create_upload_files(files: list[UploadFile] = File(...)):
|
async def create_upload_files(files: list[UploadFile]):
|
||||||
return {"filenames": [file.filename for file in files]}
|
return {"filenames": [file.filename for file in files]}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
from fastapi import FastAPI, File, UploadFile
|
||||||
|
from fastapi.responses import HTMLResponse
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/files/")
|
||||||
|
async def create_files(
|
||||||
|
files: List[bytes] = File(..., description="Multiple files as bytes")
|
||||||
|
):
|
||||||
|
return {"file_sizes": [len(file) for file in files]}
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/uploadfiles/")
|
||||||
|
async def create_upload_files(
|
||||||
|
files: List[UploadFile] = File(..., description="Multiple files as UploadFile")
|
||||||
|
):
|
||||||
|
return {"filenames": [file.filename for file in files]}
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/")
|
||||||
|
async def main():
|
||||||
|
content = """
|
||||||
|
<body>
|
||||||
|
<form action="/files/" enctype="multipart/form-data" method="post">
|
||||||
|
<input name="files" type="file" multiple>
|
||||||
|
<input type="submit">
|
||||||
|
</form>
|
||||||
|
<form action="/uploadfiles/" enctype="multipart/form-data" method="post">
|
||||||
|
<input name="files" type="file" multiple>
|
||||||
|
<input type="submit">
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
"""
|
||||||
|
return HTMLResponse(content=content)
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
from fastapi import FastAPI, File, UploadFile
|
||||||
|
from fastapi.responses import HTMLResponse
|
||||||
|
|
||||||
|
app = FastAPI()
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/files/")
|
||||||
|
async def create_files(
|
||||||
|
files: list[bytes] = File(..., description="Multiple files as bytes")
|
||||||
|
):
|
||||||
|
return {"file_sizes": [len(file) for file in files]}
|
||||||
|
|
||||||
|
|
||||||
|
@app.post("/uploadfiles/")
|
||||||
|
async def create_upload_files(
|
||||||
|
files: list[UploadFile] = File(..., description="Multiple files as UploadFile")
|
||||||
|
):
|
||||||
|
return {"filenames": [file.filename for file in files]}
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/")
|
||||||
|
async def main():
|
||||||
|
content = """
|
||||||
|
<body>
|
||||||
|
<form action="/files/" enctype="multipart/form-data" method="post">
|
||||||
|
<input name="files" type="file" multiple>
|
||||||
|
<input type="submit">
|
||||||
|
</form>
|
||||||
|
<form action="/uploadfiles/" enctype="multipart/form-data" method="post">
|
||||||
|
<input name="files" type="file" multiple>
|
||||||
|
<input type="submit">
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
"""
|
||||||
|
return HTMLResponse(content=content)
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
from typing import Any, Callable, Iterable, Type, TypeVar
|
from typing import Any, Callable, Dict, Iterable, Type, TypeVar
|
||||||
|
|
||||||
from starlette.datastructures import URL as URL # noqa: F401
|
from starlette.datastructures import URL as URL # noqa: F401
|
||||||
from starlette.datastructures import Address as Address # noqa: F401
|
from starlette.datastructures import Address as Address # noqa: F401
|
||||||
|
|
@ -20,6 +20,10 @@ class UploadFile(StarletteUploadFile):
|
||||||
raise ValueError(f"Expected UploadFile, received: {type(v)}")
|
raise ValueError(f"Expected UploadFile, received: {type(v)}")
|
||||||
return v
|
return v
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None:
|
||||||
|
field_schema.update({"type": "string", "format": "binary"})
|
||||||
|
|
||||||
|
|
||||||
class DefaultPlaceholder:
|
class DefaultPlaceholder:
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -390,6 +390,8 @@ def get_param_field(
|
||||||
field.required = required
|
field.required = required
|
||||||
if not had_schema and not is_scalar_field(field=field):
|
if not had_schema and not is_scalar_field(field=field):
|
||||||
field.field_info = params.Body(field_info.default)
|
field.field_info = params.Body(field_info.default)
|
||||||
|
if not had_schema and lenient_issubclass(field.type_, UploadFile):
|
||||||
|
field.field_info = params.File(field_info.default)
|
||||||
|
|
||||||
return field
|
return field
|
||||||
|
|
||||||
|
|
@ -701,25 +703,6 @@ def get_missing_field_error(loc: Tuple[str, ...]) -> ErrorWrapper:
|
||||||
return missing_field_error
|
return missing_field_error
|
||||||
|
|
||||||
|
|
||||||
def get_schema_compatible_field(*, field: ModelField) -> ModelField:
|
|
||||||
out_field = field
|
|
||||||
if lenient_issubclass(field.type_, UploadFile):
|
|
||||||
use_type: type = bytes
|
|
||||||
if field.shape in sequence_shapes:
|
|
||||||
use_type = List[bytes]
|
|
||||||
out_field = create_response_field(
|
|
||||||
name=field.name,
|
|
||||||
type_=use_type,
|
|
||||||
class_validators=field.class_validators,
|
|
||||||
model_config=field.model_config,
|
|
||||||
default=field.default,
|
|
||||||
required=field.required,
|
|
||||||
alias=field.alias,
|
|
||||||
field_info=field.field_info,
|
|
||||||
)
|
|
||||||
return out_field
|
|
||||||
|
|
||||||
|
|
||||||
def get_body_field(*, dependant: Dependant, name: str) -> Optional[ModelField]:
|
def get_body_field(*, dependant: Dependant, name: str) -> Optional[ModelField]:
|
||||||
flat_dependant = get_flat_dependant(dependant)
|
flat_dependant = get_flat_dependant(dependant)
|
||||||
if not flat_dependant.body_params:
|
if not flat_dependant.body_params:
|
||||||
|
|
@ -729,9 +712,8 @@ def get_body_field(*, dependant: Dependant, name: str) -> Optional[ModelField]:
|
||||||
embed = getattr(field_info, "embed", None)
|
embed = getattr(field_info, "embed", None)
|
||||||
body_param_names_set = {param.name for param in flat_dependant.body_params}
|
body_param_names_set = {param.name for param in flat_dependant.body_params}
|
||||||
if len(body_param_names_set) == 1 and not embed:
|
if len(body_param_names_set) == 1 and not embed:
|
||||||
final_field = get_schema_compatible_field(field=first_param)
|
check_file_field(first_param)
|
||||||
check_file_field(final_field)
|
return first_param
|
||||||
return final_field
|
|
||||||
# If one field requires to embed, all have to be embedded
|
# If one field requires to embed, all have to be embedded
|
||||||
# in case a sub-dependency is evaluated with a single unique body field
|
# in case a sub-dependency is evaluated with a single unique body field
|
||||||
# That is combined (embedded) with other body fields
|
# That is combined (embedded) with other body fields
|
||||||
|
|
@ -740,7 +722,7 @@ def get_body_field(*, dependant: Dependant, name: str) -> Optional[ModelField]:
|
||||||
model_name = "Body_" + name
|
model_name = "Body_" + name
|
||||||
BodyModel: Type[BaseModel] = create_model(model_name)
|
BodyModel: Type[BaseModel] = create_model(model_name)
|
||||||
for f in flat_dependant.body_params:
|
for f in flat_dependant.body_params:
|
||||||
BodyModel.__fields__[f.name] = get_schema_compatible_field(field=f)
|
BodyModel.__fields__[f.name] = f
|
||||||
required = any(True for f in flat_dependant.body_params if f.required)
|
required = any(True for f in flat_dependant.body_params if f.required)
|
||||||
|
|
||||||
BodyFieldInfo_kwargs: Dict[str, Any] = dict(default=None)
|
BodyFieldInfo_kwargs: Dict[str, Any] = dict(default=None)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,157 @@
|
||||||
|
from fastapi.testclient import TestClient
|
||||||
|
|
||||||
|
from docs_src.request_files.tutorial001_02 import app
|
||||||
|
|
||||||
|
client = TestClient(app)
|
||||||
|
|
||||||
|
openapi_schema = {
|
||||||
|
"openapi": "3.0.2",
|
||||||
|
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||||
|
"paths": {
|
||||||
|
"/files/": {
|
||||||
|
"post": {
|
||||||
|
"summary": "Create File",
|
||||||
|
"operationId": "create_file_files__post",
|
||||||
|
"requestBody": {
|
||||||
|
"content": {
|
||||||
|
"multipart/form-data": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/Body_create_file_files__post"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful Response",
|
||||||
|
"content": {"application/json": {"schema": {}}},
|
||||||
|
},
|
||||||
|
"422": {
|
||||||
|
"description": "Validation Error",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/HTTPValidationError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/uploadfile/": {
|
||||||
|
"post": {
|
||||||
|
"summary": "Create Upload File",
|
||||||
|
"operationId": "create_upload_file_uploadfile__post",
|
||||||
|
"requestBody": {
|
||||||
|
"content": {
|
||||||
|
"multipart/form-data": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/Body_create_upload_file_uploadfile__post"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful Response",
|
||||||
|
"content": {"application/json": {"schema": {}}},
|
||||||
|
},
|
||||||
|
"422": {
|
||||||
|
"description": "Validation Error",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/HTTPValidationError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"Body_create_file_files__post": {
|
||||||
|
"title": "Body_create_file_files__post",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"file": {"title": "File", "type": "string", "format": "binary"}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"Body_create_upload_file_uploadfile__post": {
|
||||||
|
"title": "Body_create_upload_file_uploadfile__post",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"file": {"title": "File", "type": "string", "format": "binary"}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"HTTPValidationError": {
|
||||||
|
"title": "HTTPValidationError",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"detail": {
|
||||||
|
"title": "Detail",
|
||||||
|
"type": "array",
|
||||||
|
"items": {"$ref": "#/components/schemas/ValidationError"},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"ValidationError": {
|
||||||
|
"title": "ValidationError",
|
||||||
|
"required": ["loc", "msg", "type"],
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"loc": {
|
||||||
|
"title": "Location",
|
||||||
|
"type": "array",
|
||||||
|
"items": {"type": "string"},
|
||||||
|
},
|
||||||
|
"msg": {"title": "Message", "type": "string"},
|
||||||
|
"type": {"title": "Error Type", "type": "string"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_openapi_schema():
|
||||||
|
response = client.get("/openapi.json")
|
||||||
|
assert response.status_code == 200, response.text
|
||||||
|
assert response.json() == openapi_schema
|
||||||
|
|
||||||
|
|
||||||
|
def test_post_form_no_body():
|
||||||
|
response = client.post("/files/")
|
||||||
|
assert response.status_code == 200, response.text
|
||||||
|
assert response.json() == {"message": "No file sent"}
|
||||||
|
|
||||||
|
|
||||||
|
def test_post_uploadfile_no_body():
|
||||||
|
response = client.post("/uploadfile/")
|
||||||
|
assert response.status_code == 200, response.text
|
||||||
|
assert response.json() == {"message": "No upload file sent"}
|
||||||
|
|
||||||
|
|
||||||
|
def test_post_file(tmp_path):
|
||||||
|
path = tmp_path / "test.txt"
|
||||||
|
path.write_bytes(b"<file content>")
|
||||||
|
|
||||||
|
client = TestClient(app)
|
||||||
|
with path.open("rb") as file:
|
||||||
|
response = client.post("/files/", files={"file": file})
|
||||||
|
assert response.status_code == 200, response.text
|
||||||
|
assert response.json() == {"file_size": 14}
|
||||||
|
|
||||||
|
|
||||||
|
def test_post_upload_file(tmp_path):
|
||||||
|
path = tmp_path / "test.txt"
|
||||||
|
path.write_bytes(b"<file content>")
|
||||||
|
|
||||||
|
client = TestClient(app)
|
||||||
|
with path.open("rb") as file:
|
||||||
|
response = client.post("/uploadfile/", files={"file": file})
|
||||||
|
assert response.status_code == 200, response.text
|
||||||
|
assert response.json() == {"filename": "test.txt"}
|
||||||
|
|
@ -0,0 +1,169 @@
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from fastapi.testclient import TestClient
|
||||||
|
|
||||||
|
from ...utils import needs_py310
|
||||||
|
|
||||||
|
openapi_schema = {
|
||||||
|
"openapi": "3.0.2",
|
||||||
|
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||||
|
"paths": {
|
||||||
|
"/files/": {
|
||||||
|
"post": {
|
||||||
|
"summary": "Create File",
|
||||||
|
"operationId": "create_file_files__post",
|
||||||
|
"requestBody": {
|
||||||
|
"content": {
|
||||||
|
"multipart/form-data": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/Body_create_file_files__post"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful Response",
|
||||||
|
"content": {"application/json": {"schema": {}}},
|
||||||
|
},
|
||||||
|
"422": {
|
||||||
|
"description": "Validation Error",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/HTTPValidationError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/uploadfile/": {
|
||||||
|
"post": {
|
||||||
|
"summary": "Create Upload File",
|
||||||
|
"operationId": "create_upload_file_uploadfile__post",
|
||||||
|
"requestBody": {
|
||||||
|
"content": {
|
||||||
|
"multipart/form-data": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/Body_create_upload_file_uploadfile__post"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful Response",
|
||||||
|
"content": {"application/json": {"schema": {}}},
|
||||||
|
},
|
||||||
|
"422": {
|
||||||
|
"description": "Validation Error",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/HTTPValidationError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"Body_create_file_files__post": {
|
||||||
|
"title": "Body_create_file_files__post",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"file": {"title": "File", "type": "string", "format": "binary"}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"Body_create_upload_file_uploadfile__post": {
|
||||||
|
"title": "Body_create_upload_file_uploadfile__post",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"file": {"title": "File", "type": "string", "format": "binary"}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"HTTPValidationError": {
|
||||||
|
"title": "HTTPValidationError",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"detail": {
|
||||||
|
"title": "Detail",
|
||||||
|
"type": "array",
|
||||||
|
"items": {"$ref": "#/components/schemas/ValidationError"},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"ValidationError": {
|
||||||
|
"title": "ValidationError",
|
||||||
|
"required": ["loc", "msg", "type"],
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"loc": {
|
||||||
|
"title": "Location",
|
||||||
|
"type": "array",
|
||||||
|
"items": {"type": "string"},
|
||||||
|
},
|
||||||
|
"msg": {"title": "Message", "type": "string"},
|
||||||
|
"type": {"title": "Error Type", "type": "string"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="client")
|
||||||
|
def get_client():
|
||||||
|
from docs_src.request_files.tutorial001_02_py310 import app
|
||||||
|
|
||||||
|
client = TestClient(app)
|
||||||
|
return client
|
||||||
|
|
||||||
|
|
||||||
|
@needs_py310
|
||||||
|
def test_openapi_schema(client: TestClient):
|
||||||
|
response = client.get("/openapi.json")
|
||||||
|
assert response.status_code == 200, response.text
|
||||||
|
assert response.json() == openapi_schema
|
||||||
|
|
||||||
|
|
||||||
|
@needs_py310
|
||||||
|
def test_post_form_no_body(client: TestClient):
|
||||||
|
response = client.post("/files/")
|
||||||
|
assert response.status_code == 200, response.text
|
||||||
|
assert response.json() == {"message": "No file sent"}
|
||||||
|
|
||||||
|
|
||||||
|
@needs_py310
|
||||||
|
def test_post_uploadfile_no_body(client: TestClient):
|
||||||
|
response = client.post("/uploadfile/")
|
||||||
|
assert response.status_code == 200, response.text
|
||||||
|
assert response.json() == {"message": "No upload file sent"}
|
||||||
|
|
||||||
|
|
||||||
|
@needs_py310
|
||||||
|
def test_post_file(tmp_path: Path, client: TestClient):
|
||||||
|
path = tmp_path / "test.txt"
|
||||||
|
path.write_bytes(b"<file content>")
|
||||||
|
|
||||||
|
with path.open("rb") as file:
|
||||||
|
response = client.post("/files/", files={"file": file})
|
||||||
|
assert response.status_code == 200, response.text
|
||||||
|
assert response.json() == {"file_size": 14}
|
||||||
|
|
||||||
|
|
||||||
|
@needs_py310
|
||||||
|
def test_post_upload_file(tmp_path: Path, client: TestClient):
|
||||||
|
path = tmp_path / "test.txt"
|
||||||
|
path.write_bytes(b"<file content>")
|
||||||
|
|
||||||
|
with path.open("rb") as file:
|
||||||
|
response = client.post("/uploadfile/", files={"file": file})
|
||||||
|
assert response.status_code == 200, response.text
|
||||||
|
assert response.json() == {"filename": "test.txt"}
|
||||||
|
|
@ -0,0 +1,159 @@
|
||||||
|
from fastapi.testclient import TestClient
|
||||||
|
|
||||||
|
from docs_src.request_files.tutorial001_03 import app
|
||||||
|
|
||||||
|
client = TestClient(app)
|
||||||
|
|
||||||
|
openapi_schema = {
|
||||||
|
"openapi": "3.0.2",
|
||||||
|
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||||
|
"paths": {
|
||||||
|
"/files/": {
|
||||||
|
"post": {
|
||||||
|
"summary": "Create File",
|
||||||
|
"operationId": "create_file_files__post",
|
||||||
|
"requestBody": {
|
||||||
|
"content": {
|
||||||
|
"multipart/form-data": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/Body_create_file_files__post"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": True,
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful Response",
|
||||||
|
"content": {"application/json": {"schema": {}}},
|
||||||
|
},
|
||||||
|
"422": {
|
||||||
|
"description": "Validation Error",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/HTTPValidationError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/uploadfile/": {
|
||||||
|
"post": {
|
||||||
|
"summary": "Create Upload File",
|
||||||
|
"operationId": "create_upload_file_uploadfile__post",
|
||||||
|
"requestBody": {
|
||||||
|
"content": {
|
||||||
|
"multipart/form-data": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/Body_create_upload_file_uploadfile__post"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": True,
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful Response",
|
||||||
|
"content": {"application/json": {"schema": {}}},
|
||||||
|
},
|
||||||
|
"422": {
|
||||||
|
"description": "Validation Error",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/HTTPValidationError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"Body_create_file_files__post": {
|
||||||
|
"title": "Body_create_file_files__post",
|
||||||
|
"required": ["file"],
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"file": {
|
||||||
|
"title": "File",
|
||||||
|
"type": "string",
|
||||||
|
"description": "A file read as bytes",
|
||||||
|
"format": "binary",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"Body_create_upload_file_uploadfile__post": {
|
||||||
|
"title": "Body_create_upload_file_uploadfile__post",
|
||||||
|
"required": ["file"],
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"file": {
|
||||||
|
"title": "File",
|
||||||
|
"type": "string",
|
||||||
|
"description": "A file read as UploadFile",
|
||||||
|
"format": "binary",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"HTTPValidationError": {
|
||||||
|
"title": "HTTPValidationError",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"detail": {
|
||||||
|
"title": "Detail",
|
||||||
|
"type": "array",
|
||||||
|
"items": {"$ref": "#/components/schemas/ValidationError"},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"ValidationError": {
|
||||||
|
"title": "ValidationError",
|
||||||
|
"required": ["loc", "msg", "type"],
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"loc": {
|
||||||
|
"title": "Location",
|
||||||
|
"type": "array",
|
||||||
|
"items": {"type": "string"},
|
||||||
|
},
|
||||||
|
"msg": {"title": "Message", "type": "string"},
|
||||||
|
"type": {"title": "Error Type", "type": "string"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_openapi_schema():
|
||||||
|
response = client.get("/openapi.json")
|
||||||
|
assert response.status_code == 200, response.text
|
||||||
|
assert response.json() == openapi_schema
|
||||||
|
|
||||||
|
|
||||||
|
def test_post_file(tmp_path):
|
||||||
|
path = tmp_path / "test.txt"
|
||||||
|
path.write_bytes(b"<file content>")
|
||||||
|
|
||||||
|
client = TestClient(app)
|
||||||
|
with path.open("rb") as file:
|
||||||
|
response = client.post("/files/", files={"file": file})
|
||||||
|
assert response.status_code == 200, response.text
|
||||||
|
assert response.json() == {"file_size": 14}
|
||||||
|
|
||||||
|
|
||||||
|
def test_post_upload_file(tmp_path):
|
||||||
|
path = tmp_path / "test.txt"
|
||||||
|
path.write_bytes(b"<file content>")
|
||||||
|
|
||||||
|
client = TestClient(app)
|
||||||
|
with path.open("rb") as file:
|
||||||
|
response = client.post("/uploadfile/", files={"file": file})
|
||||||
|
assert response.status_code == 200, response.text
|
||||||
|
assert response.json() == {"filename": "test.txt"}
|
||||||
|
|
@ -0,0 +1,194 @@
|
||||||
|
from fastapi.testclient import TestClient
|
||||||
|
|
||||||
|
from docs_src.request_files.tutorial003 import app
|
||||||
|
|
||||||
|
client = TestClient(app)
|
||||||
|
|
||||||
|
openapi_schema = {
|
||||||
|
"openapi": "3.0.2",
|
||||||
|
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||||
|
"paths": {
|
||||||
|
"/files/": {
|
||||||
|
"post": {
|
||||||
|
"summary": "Create Files",
|
||||||
|
"operationId": "create_files_files__post",
|
||||||
|
"requestBody": {
|
||||||
|
"content": {
|
||||||
|
"multipart/form-data": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/Body_create_files_files__post"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": True,
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful Response",
|
||||||
|
"content": {"application/json": {"schema": {}}},
|
||||||
|
},
|
||||||
|
"422": {
|
||||||
|
"description": "Validation Error",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/HTTPValidationError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/uploadfiles/": {
|
||||||
|
"post": {
|
||||||
|
"summary": "Create Upload Files",
|
||||||
|
"operationId": "create_upload_files_uploadfiles__post",
|
||||||
|
"requestBody": {
|
||||||
|
"content": {
|
||||||
|
"multipart/form-data": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/Body_create_upload_files_uploadfiles__post"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": True,
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful Response",
|
||||||
|
"content": {"application/json": {"schema": {}}},
|
||||||
|
},
|
||||||
|
"422": {
|
||||||
|
"description": "Validation Error",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/HTTPValidationError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/": {
|
||||||
|
"get": {
|
||||||
|
"summary": "Main",
|
||||||
|
"operationId": "main__get",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful Response",
|
||||||
|
"content": {"application/json": {"schema": {}}},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"Body_create_files_files__post": {
|
||||||
|
"title": "Body_create_files_files__post",
|
||||||
|
"required": ["files"],
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"files": {
|
||||||
|
"title": "Files",
|
||||||
|
"type": "array",
|
||||||
|
"items": {"type": "string", "format": "binary"},
|
||||||
|
"description": "Multiple files as bytes",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"Body_create_upload_files_uploadfiles__post": {
|
||||||
|
"title": "Body_create_upload_files_uploadfiles__post",
|
||||||
|
"required": ["files"],
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"files": {
|
||||||
|
"title": "Files",
|
||||||
|
"type": "array",
|
||||||
|
"items": {"type": "string", "format": "binary"},
|
||||||
|
"description": "Multiple files as UploadFile",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"HTTPValidationError": {
|
||||||
|
"title": "HTTPValidationError",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"detail": {
|
||||||
|
"title": "Detail",
|
||||||
|
"type": "array",
|
||||||
|
"items": {"$ref": "#/components/schemas/ValidationError"},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"ValidationError": {
|
||||||
|
"title": "ValidationError",
|
||||||
|
"required": ["loc", "msg", "type"],
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"loc": {
|
||||||
|
"title": "Location",
|
||||||
|
"type": "array",
|
||||||
|
"items": {"type": "string"},
|
||||||
|
},
|
||||||
|
"msg": {"title": "Message", "type": "string"},
|
||||||
|
"type": {"title": "Error Type", "type": "string"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_openapi_schema():
|
||||||
|
response = client.get("/openapi.json")
|
||||||
|
assert response.status_code == 200, response.text
|
||||||
|
assert response.json() == openapi_schema
|
||||||
|
|
||||||
|
|
||||||
|
def test_post_files(tmp_path):
|
||||||
|
path = tmp_path / "test.txt"
|
||||||
|
path.write_bytes(b"<file content>")
|
||||||
|
path2 = tmp_path / "test2.txt"
|
||||||
|
path2.write_bytes(b"<file content2>")
|
||||||
|
|
||||||
|
client = TestClient(app)
|
||||||
|
with path.open("rb") as file, path2.open("rb") as file2:
|
||||||
|
response = client.post(
|
||||||
|
"/files/",
|
||||||
|
files=(
|
||||||
|
("files", ("test.txt", file)),
|
||||||
|
("files", ("test2.txt", file2)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
assert response.status_code == 200, response.text
|
||||||
|
assert response.json() == {"file_sizes": [14, 15]}
|
||||||
|
|
||||||
|
|
||||||
|
def test_post_upload_file(tmp_path):
|
||||||
|
path = tmp_path / "test.txt"
|
||||||
|
path.write_bytes(b"<file content>")
|
||||||
|
path2 = tmp_path / "test2.txt"
|
||||||
|
path2.write_bytes(b"<file content2>")
|
||||||
|
|
||||||
|
client = TestClient(app)
|
||||||
|
with path.open("rb") as file, path2.open("rb") as file2:
|
||||||
|
response = client.post(
|
||||||
|
"/uploadfiles/",
|
||||||
|
files=(
|
||||||
|
("files", ("test.txt", file)),
|
||||||
|
("files", ("test2.txt", file2)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
assert response.status_code == 200, response.text
|
||||||
|
assert response.json() == {"filenames": ["test.txt", "test2.txt"]}
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_root():
|
||||||
|
client = TestClient(app)
|
||||||
|
response = client.get("/")
|
||||||
|
assert response.status_code == 200, response.text
|
||||||
|
assert b"<form" in response.content
|
||||||
|
|
@ -0,0 +1,223 @@
|
||||||
|
import pytest
|
||||||
|
from fastapi import FastAPI
|
||||||
|
from fastapi.testclient import TestClient
|
||||||
|
|
||||||
|
from ...utils import needs_py39
|
||||||
|
|
||||||
|
openapi_schema = {
|
||||||
|
"openapi": "3.0.2",
|
||||||
|
"info": {"title": "FastAPI", "version": "0.1.0"},
|
||||||
|
"paths": {
|
||||||
|
"/files/": {
|
||||||
|
"post": {
|
||||||
|
"summary": "Create Files",
|
||||||
|
"operationId": "create_files_files__post",
|
||||||
|
"requestBody": {
|
||||||
|
"content": {
|
||||||
|
"multipart/form-data": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/Body_create_files_files__post"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": True,
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful Response",
|
||||||
|
"content": {"application/json": {"schema": {}}},
|
||||||
|
},
|
||||||
|
"422": {
|
||||||
|
"description": "Validation Error",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/HTTPValidationError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/uploadfiles/": {
|
||||||
|
"post": {
|
||||||
|
"summary": "Create Upload Files",
|
||||||
|
"operationId": "create_upload_files_uploadfiles__post",
|
||||||
|
"requestBody": {
|
||||||
|
"content": {
|
||||||
|
"multipart/form-data": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/Body_create_upload_files_uploadfiles__post"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": True,
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful Response",
|
||||||
|
"content": {"application/json": {"schema": {}}},
|
||||||
|
},
|
||||||
|
"422": {
|
||||||
|
"description": "Validation Error",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/HTTPValidationError"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/": {
|
||||||
|
"get": {
|
||||||
|
"summary": "Main",
|
||||||
|
"operationId": "main__get",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful Response",
|
||||||
|
"content": {"application/json": {"schema": {}}},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"Body_create_files_files__post": {
|
||||||
|
"title": "Body_create_files_files__post",
|
||||||
|
"required": ["files"],
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"files": {
|
||||||
|
"title": "Files",
|
||||||
|
"type": "array",
|
||||||
|
"items": {"type": "string", "format": "binary"},
|
||||||
|
"description": "Multiple files as bytes",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"Body_create_upload_files_uploadfiles__post": {
|
||||||
|
"title": "Body_create_upload_files_uploadfiles__post",
|
||||||
|
"required": ["files"],
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"files": {
|
||||||
|
"title": "Files",
|
||||||
|
"type": "array",
|
||||||
|
"items": {"type": "string", "format": "binary"},
|
||||||
|
"description": "Multiple files as UploadFile",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"HTTPValidationError": {
|
||||||
|
"title": "HTTPValidationError",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"detail": {
|
||||||
|
"title": "Detail",
|
||||||
|
"type": "array",
|
||||||
|
"items": {"$ref": "#/components/schemas/ValidationError"},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"ValidationError": {
|
||||||
|
"title": "ValidationError",
|
||||||
|
"required": ["loc", "msg", "type"],
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"loc": {
|
||||||
|
"title": "Location",
|
||||||
|
"type": "array",
|
||||||
|
"items": {"type": "string"},
|
||||||
|
},
|
||||||
|
"msg": {"title": "Message", "type": "string"},
|
||||||
|
"type": {"title": "Error Type", "type": "string"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="app")
|
||||||
|
def get_app():
|
||||||
|
from docs_src.request_files.tutorial003_py39 import app
|
||||||
|
|
||||||
|
return app
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(name="client")
|
||||||
|
def get_client(app: FastAPI):
|
||||||
|
|
||||||
|
client = TestClient(app)
|
||||||
|
return client
|
||||||
|
|
||||||
|
|
||||||
|
@needs_py39
|
||||||
|
def test_openapi_schema(client: TestClient):
|
||||||
|
response = client.get("/openapi.json")
|
||||||
|
assert response.status_code == 200, response.text
|
||||||
|
assert response.json() == openapi_schema
|
||||||
|
|
||||||
|
|
||||||
|
file_required = {
|
||||||
|
"detail": [
|
||||||
|
{
|
||||||
|
"loc": ["body", "files"],
|
||||||
|
"msg": "field required",
|
||||||
|
"type": "value_error.missing",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@needs_py39
|
||||||
|
def test_post_files(tmp_path, app: FastAPI):
|
||||||
|
path = tmp_path / "test.txt"
|
||||||
|
path.write_bytes(b"<file content>")
|
||||||
|
path2 = tmp_path / "test2.txt"
|
||||||
|
path2.write_bytes(b"<file content2>")
|
||||||
|
|
||||||
|
client = TestClient(app)
|
||||||
|
with path.open("rb") as file, path2.open("rb") as file2:
|
||||||
|
response = client.post(
|
||||||
|
"/files/",
|
||||||
|
files=(
|
||||||
|
("files", ("test.txt", file)),
|
||||||
|
("files", ("test2.txt", file2)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
assert response.status_code == 200, response.text
|
||||||
|
assert response.json() == {"file_sizes": [14, 15]}
|
||||||
|
|
||||||
|
|
||||||
|
@needs_py39
|
||||||
|
def test_post_upload_file(tmp_path, app: FastAPI):
|
||||||
|
path = tmp_path / "test.txt"
|
||||||
|
path.write_bytes(b"<file content>")
|
||||||
|
path2 = tmp_path / "test2.txt"
|
||||||
|
path2.write_bytes(b"<file content2>")
|
||||||
|
|
||||||
|
client = TestClient(app)
|
||||||
|
with path.open("rb") as file, path2.open("rb") as file2:
|
||||||
|
response = client.post(
|
||||||
|
"/uploadfiles/",
|
||||||
|
files=(
|
||||||
|
("files", ("test.txt", file)),
|
||||||
|
("files", ("test2.txt", file2)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
assert response.status_code == 200, response.text
|
||||||
|
assert response.json() == {"filenames": ["test.txt", "test2.txt"]}
|
||||||
|
|
||||||
|
|
||||||
|
@needs_py39
|
||||||
|
def test_get_root(app: FastAPI):
|
||||||
|
client = TestClient(app)
|
||||||
|
response = client.get("/")
|
||||||
|
assert response.status_code == 200, response.text
|
||||||
|
assert b"<form" in response.content
|
||||||
Loading…
Reference in New Issue