and make sure we include the parent_hda when exporting histories.
Fixes
https://sentry.galaxyproject.org/share/issue/20ee27f675ef476588bbe8ff273eaee7/:
```
KeyError: '31e7840b5aedca43523bfcc8e2e9e56f'
File "galaxy/jobs/__init__.py", line 2039, in finish
task_wrapper = self.tool.exec_after_process(
File "galaxy/tools/__init__.py", line 3100, in exec_after_process
JobImportHistoryArchiveWrapper(self.app, job.id).cleanup_after_job()
File "galaxy/tools/imp_exp/__init__.py", line 81, in cleanup_after_job
model_store.perform_import(new_history, job=job, new_history=True)
File "galaxy/model/store/__init__.py", line 416, in perform_import
self._import_implicit_dataset_conversions(object_import_tracker)
File "galaxy/model/store/__init__.py", line 1280, in _import_implicit_dataset_conversions
idc.parent_hda = object_import_tracker.hdas_by_key[idc_attrs["parent_hda"]]
```
- Downstream I want a way to load plugins from outside the ConfiguredFileSources class.
- Adds some more typing as I went in there and refactored things.
Fixes https://sentry.galaxyproject.org/share/issue/509cadd762e749e1a782bb4806161ed3/:
```
AttributeError: 'NoneType' object has no attribute 'latest_workflow'
File "starlette/applications.py", line 123, in __call__
await self.middleware_stack(scope, receive, send)
File "starlette/middleware/errors.py", line 186, in __call__
raise exc
File "starlette/middleware/errors.py", line 164, in __call__
await self.app(scope, receive, _send)
File "starlette_context/middleware/raw_middleware.py", line 92, in __call__
await self.app(scope, receive, send_wrapper)
File "starlette/middleware/base.py", line 189, in __call__
with collapse_excgroups():
File "contextlib.py", line 155, in __exit__
self.gen.throw(typ, value, traceback)
File "starlette/_utils.py", line 93, in collapse_excgroups
raise exc
File "starlette/middleware/base.py", line 191, in __call__
response = await self.dispatch_func(request, call_next)
File "galaxy/webapps/galaxy/fast_app.py", line 108, in add_x_frame_options
response = await call_next(request)
File "starlette/middleware/base.py", line 165, in call_next
raise app_exc
File "starlette/middleware/base.py", line 151, in coro
await self.app(scope, receive_or_disconnect, send_no_error)
File "starlette/middleware/exceptions.py", line 62, in __call__
await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
File "starlette/_exception_handler.py", line 64, in wrapped_app
raise exc
File "starlette/_exception_handler.py", line 53, in wrapped_app
await app(scope, receive, sender)
File "starlette/routing.py", line 758, in __call__
await self.middleware_stack(scope, receive, send)
File "starlette/routing.py", line 778, in app
await route.handle(scope, receive, send)
File "starlette/routing.py", line 299, in handle
await self.app(scope, receive, send)
File "starlette/routing.py", line 79, in app
await wrap_app_handling_exceptions(app, request)(scope, receive, send)
File "starlette/_exception_handler.py", line 64, in wrapped_app
raise exc
File "starlette/_exception_handler.py", line 53, in wrapped_app
await app(scope, receive, sender)
File "starlette/routing.py", line 74, in app
response = await func(request)
File "fastapi/routing.py", line 278, in app
raw_response = await run_endpoint_function(
File "fastapi/routing.py", line 193, in run_endpoint_function
return await run_in_threadpool(dependant.call, **values)
File "starlette/concurrency.py", line 42, in run_in_threadpool
return await anyio.to_thread.run_sync(func, *args)
File "anyio/to_thread.py", line 56, in run_sync
return await get_async_backend().run_sync_in_worker_thread(
File "anyio/_backends/_asyncio.py", line 2144, in run_sync_in_worker_thread
return await future
File "anyio/_backends/_asyncio.py", line 851, in run
result = context.run(func, *args)
File "galaxy/webapps/galaxy/api/workflows.py", line 987, in refactor
return self.service.refactor(trans, workflow_id, payload, instance or False)
File "galaxy/webapps/galaxy/services/workflows.py", line 218, in refactor
return self._workflow_contents_manager.refactor(trans, stored_workflow, payload)
File "galaxy/managers/workflows.py", line 1963, in refactor
refactored_workflow, action_executions = self.do_refactor(trans, stored_workflow, refactor_request)
File "galaxy/managers/workflows.py", line 1947, in do_refactor
action_executions = refactor_executor.refactor(refactor_request)
File "galaxy/workflow/refactor/execute.py", line 77, in refactor
refactor_method(action, execution)
File "galaxy/workflow/refactor/execute.py", line 374, in _apply_upgrade_subworkflow
content_id = trans.security.encode_id(stored_workflow.latest_workflow.id)
```
You can reproduce this by creating a subworkflow, renaming the parent
workflow and then attempting to update the subworkflow step.
Fixes:
```
ExceptionGroup: unhandled errors in a TaskGroup
File "starlette/_utils.py", line 87, in collapse_excgroups
yield
File "starlette/middleware/base.py", line 190, in __call__
async with anyio.create_task_group() as task_group:
File "anyio/_backends/_asyncio.py", line 678, in __aexit__
raise BaseExceptionGroup(
AttributeError: 'NoneType' object has no attribute 'replace'
File "starlette/applications.py", line 123, in __call__
await self.middleware_stack(scope, receive, send)
File "starlette/middleware/errors.py", line 186, in __call__
raise exc
File "starlette/middleware/errors.py", line 164, in __call__
await self.app(scope, receive, _send)
File "starlette_context/middleware/raw_middleware.py", line 92, in __call__
await self.app(scope, receive, send_wrapper)
File "starlette/middleware/base.py", line 189, in __call__
with collapse_excgroups():
File "contextlib.py", line 155, in __exit__
self.gen.throw(typ, value, traceback)
File "starlette/_utils.py", line 93, in collapse_excgroups
raise exc
File "starlette/middleware/base.py", line 191, in __call__
response = await self.dispatch_func(request, call_next)
File "galaxy/webapps/galaxy/fast_app.py", line 108, in add_x_frame_options
response = await call_next(request)
File "starlette/middleware/base.py", line 165, in call_next
raise app_exc
File "starlette/middleware/base.py", line 151, in coro
await self.app(scope, receive_or_disconnect, send_no_error)
File "starlette/middleware/exceptions.py", line 62, in __call__
await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
File "starlette/_exception_handler.py", line 64, in wrapped_app
raise exc
File "starlette/_exception_handler.py", line 53, in wrapped_app
await app(scope, receive, sender)
File "starlette/routing.py", line 758, in __call__
await self.middleware_stack(scope, receive, send)
File "starlette/routing.py", line 778, in app
await route.handle(scope, receive, send)
File "starlette/routing.py", line 299, in handle
await self.app(scope, receive, send)
File "starlette/routing.py", line 79, in app
await wrap_app_handling_exceptions(app, request)(scope, receive, send)
File "starlette/_exception_handler.py", line 64, in wrapped_app
raise exc
File "starlette/_exception_handler.py", line 53, in wrapped_app
await app(scope, receive, sender)
File "starlette/routing.py", line 74, in app
response = await func(request)
File "fastapi/routing.py", line 278, in app
raw_response = await run_endpoint_function(
File "fastapi/routing.py", line 193, in run_endpoint_function
return await run_in_threadpool(dependant.call, **values)
File "starlette/concurrency.py", line 42, in run_in_threadpool
return await anyio.to_thread.run_sync(func, *args)
File "anyio/to_thread.py", line 56, in run_sync
return await get_async_backend().run_sync_in_worker_thread(
File "anyio/_backends/_asyncio.py", line 2144, in run_sync_in_worker_thread
return await future
File "anyio/_backends/_asyncio.py", line 851, in run
result = context.run(func, *args)
File "galaxy/webapps/galaxy/api/history_contents.py", line 465, in index
items = self.service.index(
File "galaxy/webapps/galaxy/services/history_contents.py", line 312, in index
return self.__index_v2(trans, history_id, params, serialization_params, filter_query_params, accept)
File "galaxy/webapps/galaxy/services/history_contents.py", line 992, in __index_v2
items = [
File "galaxy/webapps/galaxy/services/history_contents.py", line 993, in <listcomp>
self._serialize_content_item(
File "galaxy/webapps/galaxy/services/history_contents.py", line 1064, in _serialize_content_item
rval = serializer.serialize_to_view(
File "galaxy/managers/base.py", line 785, in serialize_to_view
return self.serialize(item, all_keys, **context)
File "galaxy/managers/hdas.py", line 614, in serialize
return super().serialize(hda, keys, user=user, **context)
File "galaxy/managers/datasets.py", line 770, in serialize
serialized = super().serialize(dataset_assoc, keys, **context)
File "galaxy/managers/base.py", line 698, in serialize
returned[key] = self.serializers[key](item, key, **context)
File "galaxy/managers/datasets.py", line 633, in <lambda>
"peek": lambda item, key, **context: item.display_peek() if item.peek and item.peek != "no peek" else None,
File "galaxy/model/__init__.py", line 4669, in display_peek
return self.datatype.display_peek(self)
File "galaxy/datatypes/qiime2.py", line 57, in display_peek
table += [make_row(pair) for pair in self._peek(dataset, simple=True)]
File "galaxy/datatypes/qiime2.py", line 57, in <listcomp>
table += [make_row(pair) for pair in self._peek(dataset, simple=True)]
File "galaxy/datatypes/qiime2.py", line 54, in make_row
return f"<tr><th>{pair[0]}</th><td>{html.escape(pair[1])}</td></tr>"
File "__init__.py", line 19, in escape
s = s.replace("&", "&") # Must be done first!
```
Performance: Workflows render fine without the data, data is fetched asynchronously from other queries, joins only happen across a couple of fields that are all indexed, avoiding serializing objects and such - the SQL is quite low-level.
Allow attaching a device ID on object stores that indicates they produce equivalent paths. Implement an API endpoint that allows swapping datsets between equivalent object stores if they have not been copied by new users.