From 61dab21990268594cee3d5dc8772945c1e709a98 Mon Sep 17 00:00:00 2001 From: mvdbeek Date: Wed, 16 Feb 2022 21:06:34 +0100 Subject: [PATCH 1/4] Use a2wsgi to serve WSGI app instead of the deprecated starlette WSGIMiddleware that was vendored by fastAPI. I'm also hoping this fixes the weird exceptions a la: ``` uvicorn.error ERROR 2022-02-14 11:59:42,141 [pN:main,p:4823,tN:Thread-216] Exception in ASGI application Traceback (most recent call last): File "/home/runner/work/galaxy/galaxy/galaxy root/.venv/lib/python3.7/site-packages/uvicorn/protocols/http/h11_impl.py", line 364, in run_asgi result = await app(self.scope, self.receive, self.send) File "/home/runner/work/galaxy/galaxy/galaxy root/.venv/lib/python3.7/site-packages/uvicorn/middleware/proxy_headers.py", line 75, in __call__ return await self.app(scope, receive, send) File "/home/runner/work/galaxy/galaxy/galaxy root/.venv/lib/python3.7/site-packages/fastapi/applications.py", line 212, in __call__ await super().__call__(scope, receive, send) File "/home/runner/work/galaxy/galaxy/galaxy root/.venv/lib/python3.7/site-packages/starlette/applications.py", line 112, in __call__ await self.middleware_stack(scope, receive, send) File "/home/runner/work/galaxy/galaxy/galaxy root/.venv/lib/python3.7/site-packages/starlette/middleware/errors.py", line 159, in __call__ await self.app(scope, receive, _send) File "/home/runner/work/galaxy/galaxy/galaxy root/.venv/lib/python3.7/site-packages/starlette/exceptions.py", line 71, in __call__ await self.app(scope, receive, sender) File "/home/runner/work/galaxy/galaxy/galaxy root/.venv/lib/python3.7/site-packages/starlette/routing.py", line 656, in __call__ await route.handle(scope, receive, send) File "/home/runner/work/galaxy/galaxy/galaxy root/.venv/lib/python3.7/site-packages/starlette/routing.py", line 408, in handle await self.app(scope, receive, send) File "/home/runner/work/galaxy/galaxy/galaxy root/.venv/lib/python3.7/site-packages/fastapi/applications.py", line 212, in __call__ await super().__call__(scope, receive, send) File "/home/runner/work/galaxy/galaxy/galaxy root/.venv/lib/python3.7/site-packages/starlette/applications.py", line 112, in __call__ await self.middleware_stack(scope, receive, send) File "/home/runner/work/galaxy/galaxy/galaxy root/.venv/lib/python3.7/site-packages/starlette/middleware/errors.py", line 159, in __call__ await self.app(scope, receive, _send) File "/home/runner/work/galaxy/galaxy/galaxy root/.venv/lib/python3.7/site-packages/starlette_context/middleware/raw_middleware.py", line 96, in __call__ await self.app(scope, receive, send_wrapper) File "/home/runner/work/galaxy/galaxy/galaxy root/.venv/lib/python3.7/site-packages/starlette/middleware/base.py", line 65, in __call__ task_group.cancel_scope.cancel() File "/home/runner/work/galaxy/galaxy/galaxy root/.venv/lib/python3.7/site-packages/anyio/_backends/_asyncio.py", line 574, in __aexit__ raise exceptions[0] File "/home/runner/work/galaxy/galaxy/galaxy root/.venv/lib/python3.7/site-packages/anyio/_backends/_asyncio.py", line 611, in _run_wrapped_task await coro File "/home/runner/work/galaxy/galaxy/galaxy root/.venv/lib/python3.7/site-packages/starlette/middleware/base.py", line 34, in coro await self.app(scope, request.receive, send_stream.send) File "/home/runner/work/galaxy/galaxy/galaxy root/.venv/lib/python3.7/site-packages/starlette/exceptions.py", line 71, in __call__ await self.app(scope, receive, sender) File "/home/runner/work/galaxy/galaxy/galaxy root/.venv/lib/python3.7/site-packages/starlette/routing.py", line 656, in __call__ await route.handle(scope, receive, send) File "/home/runner/work/galaxy/galaxy/galaxy root/.venv/lib/python3.7/site-packages/starlette/routing.py", line 408, in handle await self.app(scope, receive, send) File "/home/runner/work/galaxy/galaxy/galaxy root/.venv/lib/python3.7/site-packages/starlette/middleware/wsgi.py", line 64, in __call__ await responder(receive, send) File "/home/runner/work/galaxy/galaxy/galaxy root/.venv/lib/python3.7/site-packages/starlette/middleware/wsgi.py", line 91, in __call__ await anyio.to_thread.run_sync(self.wsgi, environ, self.start_response) File "/home/runner/work/galaxy/galaxy/galaxy root/.venv/lib/python3.7/site-packages/anyio/_backends/_asyncio.py", line 572, in __aexit__ raise ExceptionGroup(exceptions) anyio._backends._asyncio.ExceptionGroup: 2 exceptions were raised in the task group: ---------------------------- Traceback (most recent call last): File "/home/runner/work/galaxy/galaxy/galaxy root/.venv/lib/python3.7/site-packages/anyio/_backends/_asyncio.py", line 611, in _run_wrapped_task await coro File "/home/runner/work/galaxy/galaxy/galaxy root/.venv/lib/python3.7/site-packages/starlette/middleware/wsgi.py", line 98, in sender await send(message) File "/home/runner/work/galaxy/galaxy/galaxy root/.venv/lib/python3.7/site-packages/starlette/exceptions.py", line 68, in sender await send(message) File "/home/runner/work/galaxy/galaxy/galaxy root/.venv/lib/python3.7/site-packages/anyio/streams/memory.py", line 193, in send self.send_nowait(item) File "/home/runner/work/galaxy/galaxy/galaxy root/.venv/lib/python3.7/site-packages/anyio/streams/memory.py", line 177, in send_nowait raise BrokenResourceError anyio.BrokenResourceError ---------------------------- Traceback (most recent call last): File "/home/runner/work/galaxy/galaxy/galaxy root/.venv/lib/python3.7/site-packages/starlette/middleware/wsgi.py", line 91, in __call__ await anyio.to_thread.run_sync(self.wsgi, environ, self.start_response) File "/home/runner/work/galaxy/galaxy/galaxy root/.venv/lib/python3.7/site-packages/anyio/to_thread.py", line 29, in run_sync limiter=limiter) File "/home/runner/work/galaxy/galaxy/galaxy root/.venv/lib/python3.7/site-packages/anyio/_backends/_asyncio.py", line 818, in run_sync_in_worker_thread return await future File "/home/runner/work/galaxy/galaxy/galaxy root/.venv/lib/python3.7/site-packages/anyio/_backends/_asyncio.py", line 754, in run result = context.run(func, *args) File "/home/runner/work/galaxy/galaxy/galaxy root/.venv/lib/python3.7/site-packages/starlette/middleware/wsgi.py", line 132, in wsgi self.stream_send.send, {"type": "http.response.body", "body": b""} File "/home/runner/work/galaxy/galaxy/galaxy root/.venv/lib/python3.7/site-packages/anyio/from_thread.py", line 35, in run return asynclib.run_async_from_thread(func, *args) File "/home/runner/work/galaxy/galaxy/galaxy root/.venv/lib/python3.7/site-packages/anyio/_backends/_asyncio.py", line 847, in run_async_from_thread return f.result() File "/opt/hostedtoolcache/Python/3.7.12/x64/lib/python3.7/concurrent/futures/_base.py", line 435, in result return self.__get_result() File "/opt/hostedtoolcache/Python/3.7.12/x64/lib/python3.7/concurrent/futures/_base.py", line 384, in __get_result raise self._exception File "/home/runner/work/galaxy/galaxy/galaxy root/.venv/lib/python3.7/site-packages/anyio/streams/memory.py", line 193, in send self.send_nowait(item) File "/home/runner/work/galaxy/galaxy/galaxy root/.venv/lib/python3.7/site-packages/anyio/streams/memory.py", line 177, in send_nowait raise BrokenResourceError anyio.BrokenResourceError ``` --- lib/galaxy/dependencies/dev-requirements.txt | 1 + lib/galaxy/dependencies/pinned-requirements.txt | 1 + lib/galaxy/webapps/galaxy/fast_app.py | 2 +- lib/galaxy/webapps/reports/fast_app.py | 2 +- lib/tool_shed/webapp/fast_app.py | 2 +- packages/webapps/requirements.txt | 1 + pyproject.toml | 1 + test/unit/webapps/test_send_file.py | 2 +- 8 files changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/galaxy/dependencies/dev-requirements.txt b/lib/galaxy/dependencies/dev-requirements.txt index 1513ba45325..e809b654c97 100644 --- a/lib/galaxy/dependencies/dev-requirements.txt +++ b/lib/galaxy/dependencies/dev-requirements.txt @@ -1,5 +1,6 @@ --extra-index-url https://wheels.galaxyproject.org/simple +a2wsgi==1.4.0; python_version >= "3.6" and python_version < "4.0" adal==1.2.7 aiofiles==0.8.0; python_version >= "3.6" and python_version < "4.0" alabaster==0.7.12; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" diff --git a/lib/galaxy/dependencies/pinned-requirements.txt b/lib/galaxy/dependencies/pinned-requirements.txt index 1aaed9f9450..1ac221dfb5b 100644 --- a/lib/galaxy/dependencies/pinned-requirements.txt +++ b/lib/galaxy/dependencies/pinned-requirements.txt @@ -1,5 +1,6 @@ --extra-index-url https://wheels.galaxyproject.org/simple +a2wsgi==1.4.0; python_version >= "3.6" and python_version < "4.0" adal==1.2.7 aiofiles==0.8.0; python_version >= "3.6" and python_version < "4.0" amqp==5.0.9; python_version >= "3.7" diff --git a/lib/galaxy/webapps/galaxy/fast_app.py b/lib/galaxy/webapps/galaxy/fast_app.py index ebb28541572..ca99572d429 100644 --- a/lib/galaxy/webapps/galaxy/fast_app.py +++ b/lib/galaxy/webapps/galaxy/fast_app.py @@ -1,8 +1,8 @@ from pathlib import Path from typing import cast +from a2wsgi import WSGIMiddleware from fastapi import FastAPI, Request -from fastapi.middleware.wsgi import WSGIMiddleware from starlette.middleware.cors import CORSMiddleware from starlette.responses import ( FileResponse, diff --git a/lib/galaxy/webapps/reports/fast_app.py b/lib/galaxy/webapps/reports/fast_app.py index f5affed37a9..e03fb02fdd3 100644 --- a/lib/galaxy/webapps/reports/fast_app.py +++ b/lib/galaxy/webapps/reports/fast_app.py @@ -1,5 +1,5 @@ +from a2wsgi import WSGIMiddleware from fastapi import FastAPI -from fastapi.middleware.wsgi import WSGIMiddleware from galaxy.webapps.base.api import ( add_exception_handler, diff --git a/lib/tool_shed/webapp/fast_app.py b/lib/tool_shed/webapp/fast_app.py index 2d0c70d4d97..9fef316b764 100644 --- a/lib/tool_shed/webapp/fast_app.py +++ b/lib/tool_shed/webapp/fast_app.py @@ -1,5 +1,5 @@ +from a2wsgi import WSGIMiddleware from fastapi import FastAPI -from fastapi.middleware.wsgi import WSGIMiddleware from galaxy.webapps.base.api import ( add_exception_handler, diff --git a/packages/webapps/requirements.txt b/packages/webapps/requirements.txt index 7a195cd8db1..a6c4c554e94 100644 --- a/packages/webapps/requirements.txt +++ b/packages/webapps/requirements.txt @@ -1,3 +1,4 @@ +a2wsgi galaxy-app Cheetah3 fastapi>=0.68.2,!=0.69.0,!=0.70.0,!=0.70.1 diff --git a/pyproject.toml b/pyproject.toml index f0cc84f1e01..09114e68eec 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,6 +14,7 @@ name = "galaxyproject" url = "https://wheels.galaxyproject.org/simple" [tool.poetry.dependencies] +a2wsgi = "*" aiofiles = "*" Babel = "*" bdbag = "*" diff --git a/test/unit/webapps/test_send_file.py b/test/unit/webapps/test_send_file.py index 2ee0bdd248c..755ce16a120 100644 --- a/test/unit/webapps/test_send_file.py +++ b/test/unit/webapps/test_send_file.py @@ -1,8 +1,8 @@ import tempfile import pytest +from a2wsgi import WSGIMiddleware from fastapi.applications import FastAPI -from fastapi.middleware.wsgi import WSGIMiddleware from fastapi.testclient import TestClient from galaxy.util.bunch import Bunch From 2eb56c2bd010d4297a5576f7f0a7d36bdb7258b2 Mon Sep 17 00:00:00 2001 From: mvdbeek Date: Thu, 17 Feb 2022 11:54:11 +0100 Subject: [PATCH 2/4] Make history tests rerunnable by generating unique history names --- lib/galaxy_test/api/test_histories.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/galaxy_test/api/test_histories.py b/lib/galaxy_test/api/test_histories.py index 4d1f08c842b..6116a5f6a54 100644 --- a/lib/galaxy_test/api/test_histories.py +++ b/lib/galaxy_test/api/test_histories.py @@ -1,4 +1,5 @@ import time +from uuid import uuid4 from requests import ( put @@ -245,7 +246,7 @@ class ImportExportTests(BaseHistories): self.dataset_collection_populator = DatasetCollectionPopulator(self.galaxy_interactor) def test_import_export(self): - history_name = "for_export_default" + history_name = f"for_export_default_{uuid4()}" history_id = self.dataset_populator.setup_history_for_export_testing(history_name) imported_history_id = self._reimport_history(history_id, history_name, wait_on_history_length=2) @@ -272,7 +273,7 @@ class ImportExportTests(BaseHistories): self._import_history_and_wait(import_data, "API Test History", wait_on_history_length=2) def test_import_export_include_deleted(self): - history_name = "for_export_include_deleted" + history_name = f"for_export_include_deleted_{uuid4()}" history_id = self.dataset_populator.new_history(name=history_name) self.dataset_populator.new_dataset(history_id, content="1 2 3") deleted_hda = self.dataset_populator.new_dataset(history_id, content="1 2 3", wait=True) @@ -300,7 +301,7 @@ class ImportExportTests(BaseHistories): @skip_without_tool("job_properties") def test_import_export_failed_job(self): - history_name = "for_export_include_failed_job" + history_name = f"for_export_include_failed_job_{uuid4()}" history_id = self.dataset_populator.new_history(name=history_name) self.dataset_populator.run_tool_raw('job_properties', inputs={'failbool': True}, history_id=history_id) self.dataset_populator.wait_for_history(history_id, assert_ok=False) @@ -317,7 +318,7 @@ class ImportExportTests(BaseHistories): self._check_imported_dataset(history_id=imported_history_id, hid=1, assert_ok=False, hda_checker=check_failed, job_checker=check_failed) def test_import_metadata_regeneration(self): - history_name = "for_import_metadata_regeneration" + history_name = f"for_import_metadata_regeneration_{uuid4()}" history_id = self.dataset_populator.new_history(name=history_name) self.dataset_populator.new_dataset(history_id, content=open(self.test_data_resolver.get_filename("1.bam"), 'rb'), file_type='bam', wait=True) imported_history_id = self._reimport_history(history_id, history_name) @@ -337,13 +338,14 @@ class ImportExportTests(BaseHistories): self.dataset_populator.wait_for_history_jobs(imported_history_id, assert_ok=True) bai_metadata = import_bam_metadata["meta_files"][0] assert bai_metadata["file_type"] == "bam_index" + assert 'api/' in bai_metadata["download_url"], bai_metadata["download_url"] api_url = bai_metadata["download_url"].split("api/", 1)[1] bai_response = self._get(api_url) self._assert_status_code_is(bai_response, 200) assert len(bai_response.content) > 4 def test_import_export_collection(self): - history_name = "for_export_with_collections" + history_name = f"for_export_with_collections_{uuid4()}" history_id = self.dataset_populator.new_history(name=history_name) self.dataset_collection_populator.create_list_in_history(history_id, contents=["Hello", "World"], direct_upload=True) @@ -365,7 +367,7 @@ class ImportExportTests(BaseHistories): self._check_imported_collection(imported_history_id, hid=1, collection_type="list", elements_checker=check_elements) def test_import_export_nested_collection(self): - history_name = "for_export_with_nested_collections" + history_name = f"for_export_with_nested_collections_{uuid4()}" history_id = self.dataset_populator.new_history(name=history_name) self.dataset_collection_populator.create_list_of_pairs_in_history(history_id) From 1aaf9c02c1218c054dae81449f5cd6bfc9bc8499 Mon Sep 17 00:00:00 2001 From: mvdbeek Date: Thu, 17 Feb 2022 13:07:04 +0100 Subject: [PATCH 3/4] Fix path lookup for metdata_file And use trans.url_builder in model serializers if possible. --- lib/galaxy/managers/base.py | 8 ++++++-- lib/galaxy/managers/datasets.py | 5 +++-- lib/galaxy/managers/hdas.py | 6 ++++-- lib/galaxy/webapps/base/webapp.py | 3 ++- lib/galaxy/webapps/galaxy/api/__init__.py | 13 +++++++++++-- lib/galaxy/webapps/galaxy/buildapp.py | 2 +- 6 files changed, 27 insertions(+), 10 deletions(-) diff --git a/lib/galaxy/managers/base.py b/lib/galaxy/managers/base.py index 298ebc0b2b3..10b507250c7 100644 --- a/lib/galaxy/managers/base.py +++ b/lib/galaxy/managers/base.py @@ -576,8 +576,6 @@ class ModelSerializer(HasAModelManager[T]): keys_to_serialize = [ 'id', 'name', 'attr1', 'attr2', ... ] item_dict = MySerializer.serialize( my_item, keys_to_serialize ) """ - #: 'service' to use for getting urls - use class var to allow overriding when testing - url_for = staticmethod(gx_url_for) default_view: Optional[str] views: Dict[str, List[str]] @@ -604,6 +602,12 @@ class ModelSerializer(HasAModelManager[T]): self.views = {} self.default_view = None + @staticmethod + def url_for(*args, context=None, **kwargs): + trans = context and context.get('trans') + url_for = trans and trans.url_builder or gx_url_for + return url_for(*args, **kwargs) + def add_serializers(self): """ Register a map of attribute keys -> serializing functions that will serialize diff --git a/lib/galaxy/managers/datasets.py b/lib/galaxy/managers/datasets.py index cf3bd0128bf..08358d9d49b 100644 --- a/lib/galaxy/managers/datasets.py +++ b/lib/galaxy/managers/datasets.py @@ -527,10 +527,11 @@ class _UnflattenedMetadataDatasetAssociationSerializer(base.ModelSerializer[T], if getattr(dataset_assoc.metadata, meta_type, None): meta_files.append( dict(file_type=meta_type, - download_url=self.url_for('history_contents_metadata_file', + download_url=self.url_for('get_metadata_file', history_id=self.app.security.encode_id(dataset_assoc.history_id), history_content_id=self.app.security.encode_id(dataset_assoc.id), - metadata_file=meta_type))) + query_params={'metadata_file': meta_type}, + context=context))) return meta_files def serialize_metadata(self, item, key, excluded=None, **context): diff --git a/lib/galaxy/managers/hdas.py b/lib/galaxy/managers/hdas.py index fd59c1585a9..4eca4e0d202 100644 --- a/lib/galaxy/managers/hdas.py +++ b/lib/galaxy/managers/hdas.py @@ -427,13 +427,15 @@ class HDASerializer( # datasets._UnflattenedMetadataDatasetAssociationSerialize # see also: https://sentry.galaxyproject.org/galaxy/galaxy-main/group/20769/events/9352883/ 'url': lambda item, key, **context: self.url_for('history_content', history_id=self.app.security.encode_id(item.history_id), - id=self.app.security.encode_id(item.id)), + id=self.app.security.encode_id(item.id), + context=context), 'urls': self.serialize_urls, # TODO: backwards compat: need to go away 'download_url': lambda item, key, **context: self.url_for('history_contents_display', history_id=self.app.security.encode_id(item.history.id), - history_content_id=self.app.security.encode_id(item.id)), + history_content_id=self.app.security.encode_id(item.id), + context=context), 'parent_id': self.serialize_id, # TODO: to DatasetAssociationSerializer 'accessible': lambda item, key, user=None, **c: self.manager.is_accessible(item, user, **c), diff --git a/lib/galaxy/webapps/base/webapp.py b/lib/galaxy/webapps/base/webapp.py index 9a99007f1af..e16a0aaba4b 100644 --- a/lib/galaxy/webapps/base/webapp.py +++ b/lib/galaxy/webapps/base/webapp.py @@ -192,7 +192,8 @@ def config_allows_origin(origin_raw, config): def url_builder(*args, **kwargs) -> str: - """Wrapper around the uWSGI version of the function for reversing URLs.""" + """Wrapper around the WSGI version of the function for reversing URLs.""" + kwargs.update(kwargs.pop('query_params', {})) return url_for(*args, **kwargs) diff --git a/lib/galaxy/webapps/galaxy/api/__init__.py b/lib/galaxy/webapps/galaxy/api/__init__.py index bc08740bc52..49ea15124b5 100644 --- a/lib/galaxy/webapps/galaxy/api/__init__.py +++ b/lib/galaxy/webapps/galaxy/api/__init__.py @@ -10,6 +10,7 @@ from typing import ( Type, TypeVar, ) +from urllib.parse import urlencode from fastapi import ( Cookie, @@ -149,12 +150,20 @@ class UrlBuilder: def __call__(self, name: str, **path_params): qualified = path_params.pop("qualified", False) + # starlette does not support query parameters in url_path_for: https://github.com/encode/starlette/issues/560 + query_params = path_params.pop('query_params', None) try: if qualified: - return self.request.url_for(name, **path_params) - return self.request.app.url_path_for(name, **path_params) + url = self.request.url_for(name, **path_params) + else: + url = self.request.app.url_path_for(name, **path_params) + if query_params: + url = f"{url}?{urlencode(query_params)}" + return url except NoMatchFound: # Fallback to legacy url_for + if query_params: + path_params.update(query_params) return web.url_for(name, **path_params) diff --git a/lib/galaxy/webapps/galaxy/buildapp.py b/lib/galaxy/webapps/galaxy/buildapp.py index 7a1f3989a00..44b08ca5c8b 100644 --- a/lib/galaxy/webapps/galaxy/buildapp.py +++ b/lib/galaxy/webapps/galaxy/buildapp.py @@ -297,7 +297,7 @@ def populate_api_routes(webapp, app): controller='datasets', action='show_inheritance_chain', conditions=dict(method=["GET"])) - webapp.mapper.connect("history_contents_metadata_file", + webapp.mapper.connect("get_metadata_file", "/api/histories/{history_id}/contents/{history_content_id}/metadata_file", controller="datasets", action="get_metadata_file", From 9732f8c7c9168bdb4f26fb5d62329073f51f967d Mon Sep 17 00:00:00 2001 From: mvdbeek Date: Thu, 17 Feb 2022 13:34:20 +0100 Subject: [PATCH 4/4] Fix up unit tests that attempt to set url_for --- test/unit/app/managers/test_DatasetManager.py | 5 ++--- test/unit/app/managers/test_HDAManager.py | 5 ++--- test/unit/app/managers/test_HDCAManager.py | 5 ++--- test/unit/app/managers/test_HistoryManager.py | 6 ++---- 4 files changed, 8 insertions(+), 13 deletions(-) diff --git a/test/unit/app/managers/test_DatasetManager.py b/test/unit/app/managers/test_DatasetManager.py index c070b3583f5..f66db1d3457 100644 --- a/test/unit/app/managers/test_DatasetManager.py +++ b/test/unit/app/managers/test_DatasetManager.py @@ -1,6 +1,7 @@ """ """ import unittest +from unittest import mock import sqlalchemy @@ -208,9 +209,7 @@ def testable_url_for(*a, **k): return f'(fake url): {a}, {k}' -DatasetSerializer.url_for = staticmethod(testable_url_for) - - +@mock.patch('galaxy.managers.datasets.DatasetSerializer.url_for', testable_url_for) class DatasetSerializerTestCase(BaseTestCase): def set_up_managers(self): diff --git a/test/unit/app/managers/test_HDAManager.py b/test/unit/app/managers/test_HDAManager.py index bdf491c1de1..31f48b11d59 100644 --- a/test/unit/app/managers/test_HDAManager.py +++ b/test/unit/app/managers/test_HDAManager.py @@ -1,4 +1,5 @@ import unittest +from unittest import mock import sqlalchemy @@ -346,9 +347,7 @@ def testable_url_for(*a, **k): return f'(fake url): {a}, {k}' -hdas.HDASerializer.url_for = staticmethod(testable_url_for) - - +@mock.patch('galaxy.managers.hdas.HDASerializer.url_for', testable_url_for) class HDASerializerTestCase(HDATestCase): def set_up_managers(self): diff --git a/test/unit/app/managers/test_HDCAManager.py b/test/unit/app/managers/test_HDCAManager.py index c9a3f250e8b..382f1e0fca3 100644 --- a/test/unit/app/managers/test_HDCAManager.py +++ b/test/unit/app/managers/test_HDCAManager.py @@ -1,4 +1,5 @@ import unittest +from unittest import mock from galaxy.managers import ( collections, @@ -57,9 +58,7 @@ def testable_url_for(*a, **k): return f'(fake url): {a}, {k}' -hdcas.HDCASerializer.url_for = staticmethod(testable_url_for) - - +@mock.patch('galaxy.managers.hdcas.HDCASerializer.url_for', testable_url_for) class HDCASerializerTestCase(HDCATestCase): def set_up_managers(self): diff --git a/test/unit/app/managers/test_HistoryManager.py b/test/unit/app/managers/test_HistoryManager.py index 9dcb1a9c85b..38cd01bc6b9 100644 --- a/test/unit/app/managers/test_HistoryManager.py +++ b/test/unit/app/managers/test_HistoryManager.py @@ -390,10 +390,8 @@ def testable_url_for(*a, **k): return f'(fake url): {a}, {k}' -HistorySerializer.url_for = staticmethod(testable_url_for) -hdas.HDASerializer.url_for = staticmethod(testable_url_for) - - +@mock.patch('galaxy.managers.histories.HistorySerializer.url_for', testable_url_for) +@mock.patch('galaxy.managers.hdas.HDASerializer.url_for', testable_url_for) class HistorySerializerTestCase(BaseTestCase): def set_up_managers(self):