From b290cffb989996c1cf8ffde444cad6130fef8249 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Mon, 16 Mar 2026 09:36:33 +0000 Subject: [PATCH 1/3] Performance Optimization: Replace synchronous file upload read with async aiofiles Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com> --- app/api/files.py | 7 ++++--- requirements.txt | 1 + tests/test_file_upload.py | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/api/files.py b/app/api/files.py index 2277e2e9..06435c9e 100644 --- a/app/api/files.py +++ b/app/api/files.py @@ -1,3 +1,4 @@ +import aiofiles """ File-related API endpoints """ @@ -1277,7 +1278,7 @@ async def ui_upload(request: Request, db: DbSession, file: UploadFile = File(... # enforcing the size limit during the read so memory usage stays bounded. try: written_size = 0 - with open(target_path, "wb") as f: + async with aiofiles.open(target_path, "wb") as f: chunk_size = 65536 # 64 KB chunks while True: chunk = await file.read(chunk_size) @@ -1286,14 +1287,14 @@ async def ui_upload(request: Request, db: DbSession, file: UploadFile = File(... written_size += len(chunk) if written_size > max_size: # Exceeded limit mid-stream; clean up and reject - f.close() + await f.close() os.remove(target_path) raise HTTPException( status_code=413, detail=f"File too large: exceeded {max_size} bytes during upload. " f"See SECURITY_AUDIT.md for configuration details.", ) - f.write(chunk) + await f.write(chunk) except HTTPException: raise except Exception as e: diff --git a/requirements.txt b/requirements.txt index 3448ae0a..f65c9262 100644 --- a/requirements.txt +++ b/requirements.txt @@ -58,3 +58,4 @@ sentry-sdk[fastapi,celery,sqlalchemy]>=2.20.0,<3.0.0 # GraphQL API strawberry-graphql[fastapi]>=0.243.0,<1.0.0 +aiofiles>=24.1.0 # Asynchronous file I/O support diff --git a/tests/test_file_upload.py b/tests/test_file_upload.py index 70826466..02cae836 100644 --- a/tests/test_file_upload.py +++ b/tests/test_file_upload.py @@ -345,7 +345,7 @@ class TestUploadErrorHandling: def test_upload_disk_write_failure(self, client: TestClient): """Test handling of disk write failures.""" - with patch("builtins.open", side_effect=IOError("Disk full")): + with patch("aiofiles.open", side_effect=IOError("Disk full")): pdf_content = b"%PDF-1.4\n%EOF" response = client.post( From 80a0ddcbfcd89c156d6cce65bb6275bdf35c061e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 16 Mar 2026 09:36:54 +0000 Subject: [PATCH 2/3] style: apply ruff auto-fix - Auto-formatted code with ruff format - Applied ruff linting fixes with --fix Co-authored-by: github-actions[bot] --- app/api/files.py | 1 + 1 file changed, 1 insertion(+) diff --git a/app/api/files.py b/app/api/files.py index 06435c9e..05606167 100644 --- a/app/api/files.py +++ b/app/api/files.py @@ -1,4 +1,5 @@ import aiofiles + """ File-related API endpoints """ From fffb7cf35728288ddeaf4927c6367405e57902b8 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Mon, 16 Mar 2026 09:44:59 +0000 Subject: [PATCH 3/3] Fix ruff linting errors resulting from aiofiles addition Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com> --- app/api/files.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/api/files.py b/app/api/files.py index 05606167..ddfc2cbb 100644 --- a/app/api/files.py +++ b/app/api/files.py @@ -1,5 +1,3 @@ -import aiofiles - """ File-related API endpoints """ @@ -13,6 +11,7 @@ import zipfile from datetime import datetime, timezone from typing import Annotated, List, Optional +import aiofiles from fastapi import APIRouter, Depends, File, HTTPException, Query, Request, UploadFile, status from fastapi.responses import StreamingResponse from sqlalchemy import asc, desc