From 54e0ff78ef32fc36eaada240d8691127fd033307 Mon Sep 17 00:00:00 2001 From: mvdbeek Date: Thu, 30 Oct 2025 11:18:27 +0100 Subject: [PATCH 1/3] Replace tuswsgi with tuspyserver --- .../dependencies/pinned-requirements.txt | 2 +- lib/galaxy/webapps/galaxy/buildapp.py | 25 ------------------- lib/galaxy/webapps/galaxy/fast_app.py | 21 ++++++++++++++++ packages/web_apps/setup.cfg | 2 +- pyproject.toml | 5 +++- 5 files changed, 27 insertions(+), 28 deletions(-) diff --git a/lib/galaxy/dependencies/pinned-requirements.txt b/lib/galaxy/dependencies/pinned-requirements.txt index c1434db0261a..bcd8fab320ee 100644 --- a/lib/galaxy/dependencies/pinned-requirements.txt +++ b/lib/galaxy/dependencies/pinned-requirements.txt @@ -224,7 +224,7 @@ tomli==2.3.0 ; python_full_version < '3.11' tornado==6.5.2 tqdm==4.67.1 tuspy==1.1.0 -tuswsgi==0.5.5 +tuspyserver==4.2.0 typing-extensions==4.15.0 typing-inspection==0.4.2 tzdata==2025.2 diff --git a/lib/galaxy/webapps/galaxy/buildapp.py b/lib/galaxy/webapps/galaxy/buildapp.py index 16cbf2d9968f..7b13577d43b9 100644 --- a/lib/galaxy/webapps/galaxy/buildapp.py +++ b/lib/galaxy/webapps/galaxy/buildapp.py @@ -11,7 +11,6 @@ from urllib.parse import urljoin from paste import httpexceptions -from tuswsgi import TusMiddleware import galaxy.app import galaxy.datatypes.registry @@ -1084,30 +1083,6 @@ def wrap_in_middleware(app, global_conf, application_stack, **local_conf): from galaxy.web.framework.middleware.translogger import TransLogger app = wrap_if_allowed(app, stack, TransLogger) - # TUS upload middleware - app = wrap_if_allowed( - app, - stack, - TusMiddleware, - kwargs={ - "upload_path": urljoin(f"{application_stack.config.galaxy_url_prefix}/", "api/upload/resumable_upload"), - "tmp_dir": application_stack.config.tus_upload_store or application_stack.config.new_file_path, - "max_size": application_stack.config.maximum_upload_file_size, - }, - ) - # TUS upload middleware for job files.... - app = wrap_if_allowed( - app, - stack, - TusMiddleware, - kwargs={ - "upload_path": urljoin(f"{application_stack.config.galaxy_url_prefix}/", "api/job_files/resumable_upload"), - "tmp_dir": application_stack.config.tus_upload_store_job_files - or application_stack.config.tus_upload_store - or application_stack.config.new_file_path, - "max_size": application_stack.config.maximum_upload_file_size, - }, - ) # X-Forwarded-Host handling app = wrap_if_allowed(app, stack, XForwardedHostMiddleware) # Request ID middleware diff --git a/lib/galaxy/webapps/galaxy/fast_app.py b/lib/galaxy/webapps/galaxy/fast_app.py index fa8ddd6aa072..b25cdb51a20e 100644 --- a/lib/galaxy/webapps/galaxy/fast_app.py +++ b/lib/galaxy/webapps/galaxy/fast_app.py @@ -1,6 +1,7 @@ from typing import ( Any, ) +from urllib.parse import urljoin from a2wsgi import WSGIMiddleware from fastapi import ( @@ -9,6 +10,7 @@ ) from fastapi.openapi.constants import REF_TEMPLATE from starlette.middleware.cors import CORSMiddleware +from tuspyserver import create_tus_router from galaxy.schema.generics import CustomJsonSchema from galaxy.version import VERSION @@ -165,6 +167,24 @@ def get_openapi_schema() -> dict[str, Any]: ) +def include_tus(app: FastAPI, gx_app): + config = gx_app.config + root_path = "" if config.galaxy_url_prefix == "/" else config.galaxy_url_prefix + prefix = urljoin(root_path, "api/upload/resumable_upload") + upload_tus_router = create_tus_router( + prefix=prefix, + files_dir=config.tus_upload_store or config.new_file_path, + max_size=config.maximum_upload_file_size, + ) + job_files_tus_router = create_tus_router( + prefix=urljoin(root_path, "api/job_files/resumable_upload"), + files_dir=config.tus_upload_store_job_files or config.tus_upload_store or config.new_file_path, + max_size=config.maximum_upload_file_size, + ) + app.include_router(upload_tus_router) + app.include_router(job_files_tus_router) + + def initialize_fast_app(gx_wsgi_webapp, gx_app): root_path = "" if gx_app.config.galaxy_url_prefix == "/" else gx_app.config.galaxy_url_prefix app = get_fastapi_instance(root_path=root_path) @@ -178,6 +198,7 @@ def initialize_fast_app(gx_wsgi_webapp, gx_app): include_legacy_openapi(app, gx_app) wsgi_handler = WSGIMiddleware(gx_wsgi_webapp) gx_app.haltables.append(("WSGI Middleware threadpool", wsgi_handler.executor.shutdown)) + include_tus(app, gx_app) app.mount("/", wsgi_handler) # type: ignore[arg-type] if gx_app.config.galaxy_url_prefix != "/": parent_app = FastAPI() diff --git a/packages/web_apps/setup.cfg b/packages/web_apps/setup.cfg index 759204cf599b..369958790750 100644 --- a/packages/web_apps/setup.cfg +++ b/packages/web_apps/setup.cfg @@ -60,7 +60,7 @@ install_requires = SQLAlchemy>=2.0.37,<2.1,!=2.0.41 starlette starlette-context - tuswsgi + tuspyserver typing-extensions uvicorn uvloop>=0.21.0 diff --git a/pyproject.toml b/pyproject.toml index 1aea3f00e225..44ed4f144d04 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -101,8 +101,8 @@ dependencies = [ "starlette-context", "svgwrite", "tifffile", - "tuswsgi", "typing-extensions", + "tuspyserver", "uvicorn!=0.28.0", # https://github.com/galaxyproject/galaxy/issues/17669 "uvloop>=0.21.0", # Python 3.13 support "WebOb>=1.8.9", # Python 3.13 support @@ -257,6 +257,9 @@ constraint-dependencies = [ "limits>=2.5.0", # prefer not downgrading this to upgrading packaging "scipy>=1.14.1; python_version>='3.10'", # Python 3.13 support ] +override-dependencies = [ + "fastapi; sys_platform == 'never'", +] default-groups = [] extra-index-url = ["https://wheels.galaxyproject.org/simple"] index-strategy = "unsafe-best-match" From 5b70045791064eebe28a283b917e2d5d00c812bc Mon Sep 17 00:00:00 2001 From: mvdbeek Date: Thu, 30 Oct 2025 15:02:27 +0100 Subject: [PATCH 2/3] Use fastapi instead of fastapi-slim --- lib/galaxy/dependencies/pinned-requirements.txt | 2 +- lib/galaxy/webapps/galaxy/buildapp.py | 1 - pyproject.toml | 5 +---- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/galaxy/dependencies/pinned-requirements.txt b/lib/galaxy/dependencies/pinned-requirements.txt index bcd8fab320ee..430f6620d443 100644 --- a/lib/galaxy/dependencies/pinned-requirements.txt +++ b/lib/galaxy/dependencies/pinned-requirements.txt @@ -68,7 +68,7 @@ edam-ontology==1.25.2 email-validator==2.3.0 et-xmlfile==2.0.0 exceptiongroup==1.3.0 ; python_full_version < '3.11' -fastapi-slim==0.118.3 +fastapi==0.118.3 filelock==3.19.1 ; python_full_version < '3.10' filelock==3.20.0 ; python_full_version >= '3.10' fissix==24.4.24 diff --git a/lib/galaxy/webapps/galaxy/buildapp.py b/lib/galaxy/webapps/galaxy/buildapp.py index 7b13577d43b9..bdcc6815c586 100644 --- a/lib/galaxy/webapps/galaxy/buildapp.py +++ b/lib/galaxy/webapps/galaxy/buildapp.py @@ -8,7 +8,6 @@ import threading import traceback from typing import Optional -from urllib.parse import urljoin from paste import httpexceptions diff --git a/pyproject.toml b/pyproject.toml index 44ed4f144d04..217d0baced7c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,7 +37,7 @@ dependencies = [ "docutils!=0.17,!=0.17.1", "dparse", "edam-ontology", - "fastapi-slim>=0.111.0,!=0.119.*,!=0.120.0,!=0.120.1,!=0.120.2", # https://github.com/fastapi/fastapi/pull/13918 + "fastapi>=0.111.0,!=0.119.*,!=0.120.0,!=0.120.1,!=0.120.2", # https://github.com/fastapi/fastapi/pull/13918 "fissix", "fs", "future>=1.0.0", # Python 3.12 support @@ -257,9 +257,6 @@ constraint-dependencies = [ "limits>=2.5.0", # prefer not downgrading this to upgrading packaging "scipy>=1.14.1; python_version>='3.10'", # Python 3.13 support ] -override-dependencies = [ - "fastapi; sys_platform == 'never'", -] default-groups = [] extra-index-url = ["https://wheels.galaxyproject.org/simple"] index-strategy = "unsafe-best-match" From 8c085df65fc85313ba37a63e55c18d89c98813b9 Mon Sep 17 00:00:00 2001 From: Marius van den Beek Date: Thu, 30 Oct 2025 18:43:58 +0100 Subject: [PATCH 3/3] Build tus prefix inline Co-authored-by: Nicola Soranzo --- lib/galaxy/webapps/galaxy/fast_app.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/galaxy/webapps/galaxy/fast_app.py b/lib/galaxy/webapps/galaxy/fast_app.py index b25cdb51a20e..87c0f7e78d76 100644 --- a/lib/galaxy/webapps/galaxy/fast_app.py +++ b/lib/galaxy/webapps/galaxy/fast_app.py @@ -170,9 +170,8 @@ def get_openapi_schema() -> dict[str, Any]: def include_tus(app: FastAPI, gx_app): config = gx_app.config root_path = "" if config.galaxy_url_prefix == "/" else config.galaxy_url_prefix - prefix = urljoin(root_path, "api/upload/resumable_upload") upload_tus_router = create_tus_router( - prefix=prefix, + prefix=urljoin(root_path, "api/upload/resumable_upload"), files_dir=config.tus_upload_store or config.new_file_path, max_size=config.maximum_upload_file_size, )