mirror of
https://github.com/fastapi/fastapi.git
synced 2026-10-30 13:51:06 -04:00
148 lines
4.1 KiB
Python
148 lines
4.1 KiB
Python
from io import BytesIO
|
|
from typing import Annotated
|
|
|
|
import pytest
|
|
from fastapi import Depends, FastAPI, File, UploadFile
|
|
from fastapi.dependencies.utils import _read_file_bytes
|
|
from fastapi.testclient import TestClient
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"content",
|
|
[
|
|
pytest.param(b"hello", id="memory"),
|
|
pytest.param(b"x" * (1024 * 1024 + 1), id="disk"),
|
|
],
|
|
)
|
|
def test_file_bytes_shared_by_dependency_and_endpoint(content: bytes):
|
|
app = FastAPI()
|
|
|
|
def inspect_file(file: Annotated[bytes, File()]):
|
|
return file.decode()
|
|
|
|
@app.post("/")
|
|
def upload(
|
|
inspected: Annotated[str, Depends(inspect_file)],
|
|
file: Annotated[bytes, File()],
|
|
):
|
|
return {"dependency": inspected, "endpoint": file.decode()}
|
|
|
|
client = TestClient(app)
|
|
response = client.post("/", files={"file": ("hello.txt", content)})
|
|
assert response.status_code == 200
|
|
assert response.json() == {
|
|
"dependency": content.decode(),
|
|
"endpoint": content.decode(),
|
|
}
|
|
|
|
|
|
def test_file_bytes_list_shared_by_dependency_and_endpoint():
|
|
app = FastAPI()
|
|
|
|
def inspect_files(files: Annotated[list[bytes], File()]):
|
|
return [file.decode() for file in files]
|
|
|
|
@app.post("/")
|
|
def upload(
|
|
inspected: Annotated[list[str], Depends(inspect_files)],
|
|
files: Annotated[list[bytes], File()],
|
|
):
|
|
return {"dependency": inspected, "endpoint": [file.decode() for file in files]}
|
|
|
|
client = TestClient(app)
|
|
response = client.post(
|
|
"/",
|
|
files=[
|
|
("files", ("one.txt", b"one")),
|
|
("files", ("two.txt", b"two")),
|
|
],
|
|
)
|
|
assert response.status_code == 200
|
|
assert response.json() == {
|
|
"dependency": ["one", "two"],
|
|
"endpoint": ["one", "two"],
|
|
}
|
|
|
|
|
|
def test_bytes_dependency_preserves_upload_file_for_endpoint():
|
|
app = FastAPI()
|
|
|
|
def inspect_file(file: Annotated[bytes, File()]):
|
|
return file.decode()
|
|
|
|
@app.post("/")
|
|
async def upload(
|
|
inspected: Annotated[str, Depends(inspect_file)],
|
|
file: Annotated[UploadFile, File()],
|
|
):
|
|
content = await file.read()
|
|
second_read = await file.read()
|
|
return {
|
|
"dependency": inspected,
|
|
"endpoint": content.decode(),
|
|
"second_read": second_read.decode(),
|
|
}
|
|
|
|
client = TestClient(app)
|
|
response = client.post("/", files={"file": ("hello.txt", b"hello")})
|
|
assert response.status_code == 200
|
|
assert response.json() == {
|
|
"dependency": "hello",
|
|
"endpoint": "hello",
|
|
"second_read": "",
|
|
}
|
|
|
|
|
|
def test_automatic_file_read_preserves_nonzero_position():
|
|
app = FastAPI()
|
|
|
|
async def read_prefix(file: Annotated[UploadFile, File()]):
|
|
return (await file.read(2)).decode()
|
|
|
|
def inspect_remainder(
|
|
prefix: Annotated[str, Depends(read_prefix)],
|
|
file: Annotated[bytes, File()],
|
|
):
|
|
return {"prefix": prefix, "remainder": file.decode()}
|
|
|
|
@app.post("/")
|
|
async def upload(
|
|
inspected: Annotated[dict[str, str], Depends(inspect_remainder)],
|
|
file: Annotated[UploadFile, File()],
|
|
):
|
|
position = file.file.tell()
|
|
remainder = await file.read()
|
|
return {
|
|
"dependency": inspected,
|
|
"position": position,
|
|
"remainder": remainder.decode(),
|
|
}
|
|
|
|
client = TestClient(app)
|
|
response = client.post("/", files={"file": ("hello.txt", b"hello")})
|
|
assert response.status_code == 200
|
|
assert response.json() == {
|
|
"dependency": {"prefix": "he", "remainder": "llo"},
|
|
"position": 2,
|
|
"remainder": "llo",
|
|
}
|
|
|
|
|
|
@pytest.mark.anyio
|
|
async def test_failed_automatic_file_read_restores_position(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
):
|
|
with BytesIO(b"hello") as stream:
|
|
file = UploadFile(file=stream)
|
|
stream.seek(2)
|
|
|
|
async def failing_read(size: int = -1) -> bytes:
|
|
stream.read(1)
|
|
raise OSError("read failed")
|
|
|
|
monkeypatch.setattr(file, "read", failing_read)
|
|
with pytest.raises(OSError, match="read failed"):
|
|
await _read_file_bytes(file)
|
|
|
|
assert stream.tell() == 2
|