From: Adrian Garcia Badaracco <1755071+adriangb@users.noreply.github.com> Date: Thu, 3 Nov 2022 12:06:52 +0000 (-0500) Subject: 🐛 Close FormData (uploaded files) after the request is done (#5465) X-Git-Tag: 0.86.0~19 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=ac9f56ea5ecc738eabd9282feae4679852155669;p=thirdparty%2Ffastapi%2Ffastapi.git 🐛 Close FormData (uploaded files) after the request is done (#5465) Co-authored-by: Sebastián Ramírez --- diff --git a/fastapi/routing.py b/fastapi/routing.py index 7caf018b55..8c0bec5e61 100644 --- a/fastapi/routing.py +++ b/fastapi/routing.py @@ -3,6 +3,7 @@ import dataclasses import email.message import inspect import json +from contextlib import AsyncExitStack from enum import Enum, IntEnum from typing import ( Any, @@ -190,6 +191,9 @@ def get_request_handler( if body_field: if is_body_form: body = await request.form() + stack = request.scope.get("fastapi_astack") + assert isinstance(stack, AsyncExitStack) + stack.push_async_callback(body.close) else: body_bytes = await request.body() if body_bytes: diff --git a/tests/test_datastructures.py b/tests/test_datastructures.py index 43f1a116cb..2e6217d34e 100644 --- a/tests/test_datastructures.py +++ b/tests/test_datastructures.py @@ -1,6 +1,10 @@ +from pathlib import Path +from typing import List + import pytest -from fastapi import UploadFile +from fastapi import FastAPI, UploadFile from fastapi.datastructures import Default +from fastapi.testclient import TestClient def test_upload_file_invalid(): @@ -20,3 +24,25 @@ def test_default_placeholder_bool(): placeholder_b = Default("") assert placeholder_a assert not placeholder_b + + +def test_upload_file_is_closed(tmp_path: Path): + path = tmp_path / "test.txt" + path.write_bytes(b"") + app = FastAPI() + + testing_file_store: List[UploadFile] = [] + + @app.post("/uploadfile/") + def create_upload_file(file: UploadFile): + testing_file_store.append(file) + return {"filename": file.filename} + + 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"} + + assert testing_file_store + assert testing_file_store[0].file.closed