From 7876ebed97694ef59a87bc324cf72ddf255862db Mon Sep 17 00:00:00 2001 From: Matthias Bernt Date: Wed, 23 Oct 2024 18:43:10 +0200 Subject: [PATCH 1/5] quota: do not complain on no-change of default also no message --- lib/galaxy/managers/quotas.py | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/lib/galaxy/managers/quotas.py b/lib/galaxy/managers/quotas.py index 8b91b94a94a8..3c7c6e361b43 100644 --- a/lib/galaxy/managers/quotas.py +++ b/lib/galaxy/managers/quotas.py @@ -184,27 +184,25 @@ def set_quota_default(self, quota, params) -> str: if params.default != "no": self.quota_agent.set_default_quota(params.default, quota) message = f"Quota '{quota.name}' is now the default for {params.default} users." + elif quota.default: + message = f"Quota '{quota.name}' is no longer the default for {quota.default[0].type} users." + for dqa in quota.default: + self.sa_session.delete(dqa) + with transaction(self.sa_session): + self.sa_session.commit() else: - if quota.default: - message = f"Quota '{quota.name}' is no longer the default for {quota.default[0].type} users." - for dqa in quota.default: - self.sa_session.delete(dqa) - with transaction(self.sa_session): - self.sa_session.commit() - else: - message = f"Quota '{quota.name}' is not a default." + message = "" return message def unset_quota_default(self, quota, params=None) -> str: - if not quota.default: - raise ActionInputError(f"Quota '{quota.name}' is not a default.") - else: + message = "" + if quota.default: message = f"Quota '{quota.name}' is no longer the default for {quota.default[0].type} users." for dqa in quota.default: self.sa_session.delete(dqa) with transaction(self.sa_session): self.sa_session.commit() - return message + return message def delete_quota(self, quota, params=None) -> str: quotas = util.listify(quota) From ea3626e3638d6171d399268954f2122741f37265 Mon Sep 17 00:00:00 2001 From: mvdbeek Date: Tue, 17 Dec 2024 08:18:08 +0100 Subject: [PATCH 2/5] Fix importing shared workflows with deeply nested subworkflows We need to pass the user also to the workflow copy method when copying subworkflow steps. Adds a few bonus type annotations that made it easier to debug. Fixes https://sentry.galaxyproject.org/share/issue/454be6bbb98b43a1aa504309e1c6bc7b/:: ``` NotNullViolation: null value in column "user_id" of relation "stored_workflow" violates not-null constraint DETAIL: Failing row contains (296863, 2024-12-16 17:04:34.329066, 2024-12-16 17:04:34.329067, null, null, TreeValGal bed to bigwig, f, f, null, f, null, t). File "sqlalchemy/engine/base.py", line 2116, in _exec_insertmany_context dialect.do_execute( File "sqlalchemy/engine/default.py", line 924, in do_execute cursor.execute(statement, parameters) IntegrityError: (psycopg2.errors.NotNullViolation) null value in column "user_id" of relation "stored_workflow" violates not-null constraint DETAIL: Failing row contains (296863, 2024-12-16 17:04:34.329066, 2024-12-16 17:04:34.329067, null, null, TreeValGal bed to bigwig, f, f, null, f, null, t). [SQL: INSERT INTO stored_workflow (create_time, update_time, user_id, latest_workflow_id, name, deleted, hidden, importable, slug, from_path, published) SELECT p0::TIMESTAMP WITHOUT TIME ZONE, p1::TIMESTAMP WITHOUT TIME ZONE, p2::INTEGER, p3::INTEGER, p4:: ... 1138 characters truncated ... p9, p10, sen_counter) ORDER BY sen_counter RETURNING stored_workflow.id, stored_workflow.id AS id__1] [parameters: {'from_path__0': None, 'user_id__0': 91803, 'name__0': 'TreeValGalBaseOneHaplotype', 'create_time__0': datetime.datetime(2024, 12, 16, 17, 4, 34, 329062), 'update_time__0': datetime.datetime(2024, 12, 16, 17, 4, 34, 329065), 'deleted__0': False, 'published__0': False, 'importable__0': False, 'latest_workflow_id__0': None, 'hidden... File "galaxy/web/framework/middleware/error.py", line 167, in __call__ app_iter = self.application(environ, sr_checker) File "/cvmfs/main.galaxyproject.org/venv/lib/python3.11/site-packages/paste/httpexceptions.py", line 635, in __call__ return self.application(environ, start_response) File "galaxy/web/framework/base.py", line 176, in __call__ return self.handle_request(request_id, path_info, environ, start_response) File "galaxy/web/framework/base.py", line 271, in handle_request body = method(trans, **kwargs) File "galaxy/web/framework/decorators.py", line 94, in decorator return func(self, trans, *args, **kwargs) File "galaxy/webapps/galaxy/controllers/workflow.py", line 125, in imp self._import_shared_workflow(trans, stored) File "galaxy/webapps/base/controller.py", line 1161, in _import_shared_workflow session.commit() File "sqlalchemy/orm/scoping.py", line 597, in commit return self._proxied.commit() File "sqlalchemy/orm/session.py", line 2017, in commit trans.commit(_to_root=True) File "", line 2, in commit # Copyright (C) 2005-2024 the SQLAlchemy authors and contributors File "sqlalchemy/orm/state_changes.py", line 139, in _go ret_value = fn(self, *arg, **kw) File "sqlalchemy/orm/session.py", line 1302, in commit self._prepare_impl() File "", line 2, in _prepare_impl # Copyright (C) 2005-2024 the SQLAlchemy authors and contributors File "sqlalchemy/orm/state_changes.py", line 139, in _go ret_value = fn(self, *arg, **kw) File "sqlalchemy/orm/session.py", line 1277, in _prepare_impl self.session.flush() File "sqlalchemy/orm/session.py", line 4341, in flush self._flush(objects) File "sqlalchemy/orm/session.py", line 4476, in _flush with util.safe_reraise(): File "sqlalchemy/util/langhelpers.py", line 146, in __exit__ raise exc_value.with_traceback(exc_tb) File "sqlalchemy/orm/session.py", line 4437, in _flush flush_context.execute() File "sqlalchemy/orm/unitofwork.py", line 466, in execute rec.execute(self) File "sqlalchemy/orm/unitofwork.py", line 642, in execute util.preloaded.orm_persistence.save_obj( File "sqlalchemy/orm/persistence.py", line 93, in save_obj _emit_insert_statements( File "sqlalchemy/orm/persistence.py", line 1143, in _emit_insert_statements result = connection.execute( File "sqlalchemy/engine/base.py", line 1418, in execute return meth( File "sqlalchemy/sql/elements.py", line 515, in _execute_on_connection return connection._execute_clauseelement( File "sqlalchemy/engine/base.py", line 1640, in _execute_clauseelement ret = self._execute_context( File "sqlalchemy/engine/base.py", line 1844, in _execute_context return self._exec_insertmany_context(dialect, context) File "sqlalchemy/engine/base.py", line 2124, in _exec_insertmany_context self._handle_dbapi_exception( File "sqlalchemy/engine/base.py", line 2353, in _handle_dbapi_exception raise sqlalchemy_exception.with_traceback(exc_info[2]) from e File "sqlalchemy/engine/base.py", line 2116, in _exec_insertmany_context dialect.do_execute( File "sqlalchemy/engine/default.py", line 924, in do_execute cursor.execute(statement, parameters) ``` --- lib/galaxy/model/__init__.py | 10 +++++----- lib/galaxy/webapps/base/controller.py | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index 9843696f0db8..77480d6ffa75 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -7616,7 +7616,7 @@ class StoredWorkflow(Base, HasTags, Dictifiable, RepresentById, UsesCreateAndUpd order_by=lambda: -Workflow.id, cascade_backrefs=False, ) - latest_workflow = relationship( + latest_workflow: Mapped["Workflow"] = relationship( "Workflow", post_update=True, primaryjoin=(lambda: StoredWorkflow.latest_workflow_id == Workflow.id), @@ -7722,7 +7722,7 @@ def show_in_tool_panel(self, user_id): ) return bool(sa_session.scalar(stmt)) - def copy_tags_from(self, target_user, source_workflow): + def copy_tags_from(self, target_user, source_workflow: "StoredWorkflow"): # Override to only copy owner tags. for src_swta in source_workflow.owner_tags: new_swta = src_swta.copy() @@ -8238,10 +8238,10 @@ def copy_to(self, copied_step, step_mapping, user=None): copied_step.subworkflow = subworkflow copied_subworkflow = subworkflow else: - # Can this even happen, building a workflow with a subworkflow you don't own ? - copied_subworkflow = subworkflow.copy() + # Importing a shared workflow with a subworkflow step + copied_subworkflow = subworkflow.copy(user=user) stored_workflow = StoredWorkflow( - user, name=copied_subworkflow.name, workflow=copied_subworkflow, hidden=True + user=user, name=copied_subworkflow.name, workflow=copied_subworkflow, hidden=True ) copied_subworkflow.stored_workflow = stored_workflow copied_step.subworkflow = copied_subworkflow diff --git a/lib/galaxy/webapps/base/controller.py b/lib/galaxy/webapps/base/controller.py index 83c3ae620560..4f5d85de86d1 100644 --- a/lib/galaxy/webapps/base/controller.py +++ b/lib/galaxy/webapps/base/controller.py @@ -1144,7 +1144,7 @@ def get_stored_workflow_steps(self, trans, stored_workflow: model.StoredWorkflow except exceptions.ToolMissingException: pass - def _import_shared_workflow(self, trans, stored): + def _import_shared_workflow(self, trans, stored: model.StoredWorkflow): """Imports a shared workflow""" # Copy workflow. imported_stored = model.StoredWorkflow() From 2406c879f24f289da0a2f8dfbbb27eea36931911 Mon Sep 17 00:00:00 2001 From: mvdbeek Date: Wed, 18 Dec 2024 14:54:26 +0100 Subject: [PATCH 3/5] Make user a required argument --- lib/galaxy/model/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/galaxy/model/__init__.py b/lib/galaxy/model/__init__.py index 77480d6ffa75..38aac43223e2 100644 --- a/lib/galaxy/model/__init__.py +++ b/lib/galaxy/model/__init__.py @@ -7895,7 +7895,7 @@ def top_level_stored_workflow(self): """ return self.top_level_workflow.stored_workflow - def copy(self, user=None): + def copy(self, user: User): """Copy a workflow for a new StoredWorkflow object. Pass user if user-specific information needed. @@ -8206,7 +8206,7 @@ def workflow_output_for(self, output_name): break return target_output - def copy_to(self, copied_step, step_mapping, user=None): + def copy_to(self, copied_step: "WorkflowStep", step_mapping: Dict[int, "WorkflowStep"], user: User): copied_step.order_index = self.order_index copied_step.type = self.type copied_step.tool_id = self.tool_id From 173f17073618f7f8700a80e1cef49f4bc4715771 Mon Sep 17 00:00:00 2001 From: davelopez <46503462+davelopez@users.noreply.github.com> Date: Wed, 18 Dec 2024 18:33:45 +0100 Subject: [PATCH 4/5] Fix reactive reference for selection change warning dismissal --- .../CurrentHistory/HistoryOperations/SelectionChangeWarning.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/components/History/CurrentHistory/HistoryOperations/SelectionChangeWarning.vue b/client/src/components/History/CurrentHistory/HistoryOperations/SelectionChangeWarning.vue index cfb3c17149bb..3f1d7c8a468f 100644 --- a/client/src/components/History/CurrentHistory/HistoryOperations/SelectionChangeWarning.vue +++ b/client/src/components/History/CurrentHistory/HistoryOperations/SelectionChangeWarning.vue @@ -31,7 +31,7 @@ function onDoNotShowAgain() { watch( () => props.querySelectionBreak, () => { - dismissCountDown.value = showSelectionQueryBreakWarning ? dismissSecs.value : 0; + dismissCountDown.value = showSelectionQueryBreakWarning.value ? dismissSecs.value : 0; } ); From c67ffe8c764ef29d5025dd00319bf68ba2ea9895 Mon Sep 17 00:00:00 2001 From: Nicola Soranzo Date: Mon, 16 Dec 2024 15:02:48 +0000 Subject: [PATCH 5/5] Fix test_singularity_container_test test (which started failing since ubuntu-latest was moved to 24.04) by upgrading apptainer to >=1.3.3 to include https://github.com/apptainer/apptainer/pull/2262 . Fix: ``` > assert "samtools:1.0--1" in results["passed"], results E AssertionError: E {'failed': [{'commands': ['python -c "import pyBigWig; assert(pyBigWig.numpy ' E '== 1); assert(pyBigWig.remote == 1)"'], E 'container': 'pybigwig:0.3.22--py36h54a71a5_0', E 'errors': [{'command': 'python -c "import pyBigWig; ' E 'assert(pyBigWig.numpy == 1); ' E 'assert(pyBigWig.remote == 1)"', E 'output': '\x1b[91mERROR : Could not write info to ' E 'setgroups: Permission denied\n' E '\x1b[0m\x1b[91mERROR : Error while ' E 'waiting event for user namespace mappings: ' E 'no event received\n' E '\x1b[0m'}, E {'import': 'pyBigWig', E 'output': '\x1b[91mERROR : Could not write info to ' E 'setgroups: Permission denied\n' E '\x1b[0m\x1b[91mERROR : Error while ' E 'waiting event for user namespace mappings: ' E 'no event received\n' E '\x1b[0m'}], E 'import_lang': 'python -c', E 'imports': ['pyBigWig']}, E {'commands': ['samtools view --help 2>&1 | grep Notes > /dev/null'], E 'container': 'samtools:1.0--1', E 'errors': [{'command': 'samtools view --help 2>&1 | grep Notes > ' E '/dev/null', E 'output': '\x1b[91mERROR : Could not write info to ' E 'setgroups: Permission denied\n' E '\x1b[0m\x1b[91mERROR : Error while ' E 'waiting event for user namespace mappings: ' E 'no event received\n' E '\x1b[0m'}], E 'import_lang': 'python -c'}], E 'notest': ['yasm:1.3.0--0'], E 'passed': []} E assert 'samtools:1.0--1' in [] ``` Also: - Improve assertion messages to facilitate debugging --- .github/workflows/mulled.yaml | 2 ++ pytest.ini | 2 +- .../mulled/test_mulled_update_singularity_containers.py | 6 +++--- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/mulled.yaml b/.github/workflows/mulled.yaml index 4d33cc3febd7..6d36a454d807 100644 --- a/.github/workflows/mulled.yaml +++ b/.github/workflows/mulled.yaml @@ -44,6 +44,8 @@ jobs: key: tox-cache-${{ runner.os }}-${{ steps.full-python-version.outputs.version }}-${{ hashFiles('galaxy root/requirements.txt') }}-mulled - name: Install Apptainer's singularity uses: eWaterCycle/setup-apptainer@v2 + with: + apptainer-version: 1.3.6 # https://github.com/eWaterCycle/setup-apptainer/pull/68 - name: Install tox run: pip install tox - name: Run tests diff --git a/pytest.ini b/pytest.ini index 7c1d40306310..e2e406ef82ba 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,5 +1,5 @@ [pytest] -addopts = --doctest-continue-on-failure --verbosity=1 +addopts = --doctest-continue-on-failure --verbosity=1 --showlocals asyncio_mode = auto log_level = DEBUG # Install pytest-memray and set memray to true here to enable memory profiling of tests diff --git a/test/unit/tool_util/mulled/test_mulled_update_singularity_containers.py b/test/unit/tool_util/mulled/test_mulled_update_singularity_containers.py index 728757b52f31..9ec4e47b44f8 100644 --- a/test/unit/tool_util/mulled/test_mulled_update_singularity_containers.py +++ b/test/unit/tool_util/mulled/test_mulled_update_singularity_containers.py @@ -46,6 +46,6 @@ def test_singularity_container_test(tmp_path) -> None: "singularity", tmp_path, ) - assert "samtools:1.0--1" in results["passed"] - assert "pybigwig:0.3.22--py36h54a71a5_0" in results["passed"] - assert "yasm:1.3.0--0" in results["notest"] + assert "samtools:1.0--1" in results["passed"], results + assert "pybigwig:0.3.22--py36h54a71a5_0" in results["passed"], results + assert "yasm:1.3.0--0" in results["notest"], results