mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
Merge pull request #23186 from mvdbeek/annotate-trans
Annotate trans across the codebase
This commit is contained in:
@@ -5,6 +5,10 @@ Contains library functions
|
||||
import json
|
||||
import logging
|
||||
import os.path
|
||||
from typing import (
|
||||
Any,
|
||||
Protocol,
|
||||
)
|
||||
|
||||
from markupsafe import escape
|
||||
|
||||
@@ -12,10 +16,15 @@ from galaxy import (
|
||||
exceptions,
|
||||
util,
|
||||
)
|
||||
from galaxy.managers.collections import DatasetCollectionManager
|
||||
from galaxy.managers.collections_util import (
|
||||
api_payload_to_create_params,
|
||||
dictify_dataset_collection_instance,
|
||||
)
|
||||
from galaxy.managers.context import (
|
||||
ProvidesHistoryContext,
|
||||
ProvidesUserContext,
|
||||
)
|
||||
from galaxy.model import (
|
||||
HistoryDatasetAssociation,
|
||||
Library,
|
||||
@@ -32,7 +41,7 @@ from galaxy.util.path import (
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def validate_server_directory_upload(trans, server_dir):
|
||||
def validate_server_directory_upload(trans: ProvidesUserContext, server_dir):
|
||||
if server_dir in [None, "None", ""]:
|
||||
raise exceptions.RequestParameterInvalidException("Invalid or unspecified server_dir parameter")
|
||||
|
||||
@@ -81,7 +90,7 @@ def validate_server_directory_upload(trans, server_dir):
|
||||
return full_dir, import_dir_desc
|
||||
|
||||
|
||||
def validate_path_upload(trans):
|
||||
def validate_path_upload(trans: ProvidesUserContext):
|
||||
if not trans.app.config.allow_library_path_paste:
|
||||
raise exceptions.ConfigDoesNotAllowException(
|
||||
'"allow_path_paste" is not set to True in the Galaxy configuration file'
|
||||
@@ -93,12 +102,24 @@ def validate_path_upload(trans):
|
||||
)
|
||||
|
||||
|
||||
class CreatesLibraryCollections(Protocol):
|
||||
"""What LibraryActions._create_collection needs from its host class.
|
||||
|
||||
Hosts inject collection_manager; check_user_can_add_to_library_item comes
|
||||
from UsesLibraryMixinItems, which they all mix in as well.
|
||||
"""
|
||||
|
||||
collection_manager: DatasetCollectionManager
|
||||
|
||||
def check_user_can_add_to_library_item(self, trans: ProvidesUserContext, item, check_accessible: bool = True): ...
|
||||
|
||||
|
||||
class LibraryActions:
|
||||
"""
|
||||
Mixin for controllers that provide library functionality.
|
||||
"""
|
||||
|
||||
def _upload_dataset(self, trans, folder_id: int, payload):
|
||||
def _upload_dataset(self, trans: ProvidesHistoryContext, folder_id: int, payload):
|
||||
# Set up the traditional tool state/params
|
||||
cntrller = "api"
|
||||
tool_id = "upload1"
|
||||
@@ -106,6 +127,8 @@ class LibraryActions:
|
||||
datatypes_registry=trans.app.datatypes_registry, ext=payload.file_type
|
||||
)
|
||||
tool = trans.app.toolbox.get_tool(tool_id)
|
||||
if tool is None:
|
||||
raise exceptions.ToolMissingException(f"Tool '{tool_id}' is missing from the toolbox")
|
||||
state = tool.new_state(trans)
|
||||
populate_state(trans, tool.inputs, payload.model_dump(), state.inputs)
|
||||
tool_params = state.inputs
|
||||
@@ -160,7 +183,9 @@ class LibraryActions:
|
||||
raise exceptions.RequestParameterInvalidException("Upload failed")
|
||||
return output
|
||||
|
||||
def _get_server_dir_uploaded_datasets(self, trans, payload, full_dir, import_dir_desc, library_bunch):
|
||||
def _get_server_dir_uploaded_datasets(
|
||||
self, trans: ProvidesHistoryContext, payload, full_dir, import_dir_desc, library_bunch
|
||||
):
|
||||
files = self._get_server_dir_files(payload, full_dir, import_dir_desc)
|
||||
uploaded_datasets = []
|
||||
for file in files:
|
||||
@@ -206,9 +231,11 @@ class LibraryActions:
|
||||
raise exceptions.ObjectAttributeMissingException(f"The directory '{full_dir}' contains no valid files")
|
||||
return files
|
||||
|
||||
def _get_path_paste_uploaded_datasets(self, trans, params, library_bunch, response_code, message):
|
||||
def _get_path_paste_uploaded_datasets(
|
||||
self, trans: ProvidesHistoryContext, params, library_bunch, response_code, message
|
||||
):
|
||||
preserve_dirs = util.string_as_bool(params.get("preserve_dirs", False))
|
||||
uploaded_datasets = []
|
||||
uploaded_datasets: list = []
|
||||
files_and_folders, _response_code, _message = self._get_path_files_and_folders(params, preserve_dirs)
|
||||
if _response_code:
|
||||
return (uploaded_datasets, _response_code, _message)
|
||||
@@ -264,12 +291,14 @@ class LibraryActions:
|
||||
return None, response_code, message
|
||||
return None
|
||||
|
||||
def _make_library_uploaded_dataset(self, trans, params, name, path, type, library_bunch, in_folder=None):
|
||||
def _make_library_uploaded_dataset(
|
||||
self, trans: ProvidesHistoryContext, params, name, path, type, library_bunch, in_folder=None
|
||||
):
|
||||
link_data_only = params.get("link_data_only", "copy_files")
|
||||
uuid_str = params.get("uuid", None)
|
||||
file_type = params.get("file_type", None)
|
||||
library_bunch.replace_dataset = None # not valid for these types of upload
|
||||
uploaded_dataset = util.bunch.Bunch()
|
||||
uploaded_dataset: Any = util.bunch.Bunch()
|
||||
new_name = name
|
||||
# Remove compressed file extensions, if any, but only if
|
||||
# we're copying files into Galaxy's file space.
|
||||
@@ -300,7 +329,7 @@ class LibraryActions:
|
||||
trans.sa_session.commit()
|
||||
return uploaded_dataset
|
||||
|
||||
def _upload_library_dataset(self, trans, payload):
|
||||
def _upload_library_dataset(self, trans: ProvidesHistoryContext, payload):
|
||||
is_admin = trans.user_is_admin
|
||||
current_user_roles = trans.get_current_user_roles()
|
||||
folder = trans.sa_session.get(LibraryFolder, payload.folder_id)
|
||||
@@ -320,7 +349,7 @@ class LibraryActions:
|
||||
created_outputs_dict = self._upload_dataset(trans, folder.id, payload)
|
||||
return created_outputs_dict
|
||||
|
||||
def _create_folder(self, trans, payload):
|
||||
def _create_folder(self, trans: ProvidesUserContext, payload):
|
||||
is_admin = trans.user_is_admin
|
||||
current_user_roles = trans.get_current_user_roles()
|
||||
parent_folder = trans.sa_session.get(LibraryFolder, payload.folder_id)
|
||||
@@ -343,7 +372,7 @@ class LibraryActions:
|
||||
new_folder_dict = dict(created=new_folder)
|
||||
return new_folder_dict
|
||||
|
||||
def _create_collection(self, trans, payload, parent):
|
||||
def _create_collection(self: CreatesLibraryCollections, trans: ProvidesUserContext, payload, parent):
|
||||
# Not delegating to library_common, so need to check access to parent folder here.
|
||||
self.check_user_can_add_to_library_item(trans, parent, check_accessible=True)
|
||||
create_params = api_payload_to_create_params(payload.model_dump())
|
||||
@@ -356,13 +385,14 @@ class LibraryActions:
|
||||
)
|
||||
return [dataset_collection]
|
||||
|
||||
def _check_access(self, trans, is_admin, item, current_user_roles):
|
||||
def _check_access(self, trans: ProvidesUserContext, is_admin, item, current_user_roles):
|
||||
if isinstance(item, HistoryDatasetAssociation):
|
||||
# Make sure the user has the DATASET_ACCESS permission on the history_dataset_association.
|
||||
if not item:
|
||||
message = f"Invalid history dataset ({escape(str(item))}) specified."
|
||||
raise exceptions.ObjectNotFound(message)
|
||||
elif (
|
||||
assert item.dataset is not None
|
||||
if (
|
||||
not trans.app.security_agent.can_access_dataset(current_user_roles, item.dataset)
|
||||
and item.user == trans.user
|
||||
):
|
||||
@@ -385,7 +415,7 @@ class LibraryActions:
|
||||
message = f"You do not have permission to access the {escape(item_type)} with id ({str(item.id)})."
|
||||
raise exceptions.ItemAccessibilityException(message)
|
||||
|
||||
def _check_add(self, trans, is_admin, item, current_user_roles):
|
||||
def _check_add(self, trans: ProvidesUserContext, is_admin, item, current_user_roles):
|
||||
# Deny access if the user is not an admin and does not have the LIBRARY_ADD permission.
|
||||
if not (is_admin or trans.app.security_agent.can_add_library_item(current_user_roles, item)):
|
||||
message = f"You are not authorized to add an item to ({escape(item.name)})."
|
||||
|
||||
@@ -29,13 +29,13 @@ from typing import (
|
||||
import yaml
|
||||
|
||||
from galaxy.exceptions import ConfigurationError
|
||||
from galaxy.managers.context import ProvidesUserContext
|
||||
from galaxy.model import User
|
||||
from galaxy.schema.agents import (
|
||||
ActionSuggestion,
|
||||
ActionType,
|
||||
ConfidenceLevel,
|
||||
)
|
||||
from galaxy.work.context import SessionRequestContext
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from galaxy.config import GalaxyAppConfiguration
|
||||
@@ -353,7 +353,7 @@ class AgentRunState:
|
||||
class GalaxyAgentDependencies:
|
||||
"""Dependencies passed to Galaxy agents via dependency injection."""
|
||||
|
||||
trans: ProvidesUserContext
|
||||
trans: SessionRequestContext
|
||||
user: User
|
||||
config: "GalaxyAppConfiguration"
|
||||
# Callable to get agent instances, avoids circular import in base.py
|
||||
|
||||
@@ -13,7 +13,6 @@ from typing import (
|
||||
from sqlalchemy import select
|
||||
|
||||
from galaxy.agents import iwc
|
||||
from galaxy.managers.context import ProvidesUserContext
|
||||
from galaxy.managers.hdas import HDAManager
|
||||
from galaxy.managers.tools import DynamicToolManager
|
||||
from galaxy.model import UserDynamicToolAssociation
|
||||
@@ -40,6 +39,7 @@ from galaxy.schema.schema import (
|
||||
from galaxy.schema.workflows import InvokeWorkflowPayload
|
||||
from galaxy.structured_app import MinimalManagerApp
|
||||
from galaxy.tool_util_models.dynamic_tool_models import DynamicUnprivilegedToolCreatePayload
|
||||
from galaxy.work.context import SessionRequestContext
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -61,7 +61,7 @@ ID_FIELDS = {
|
||||
class AgentOperationsManager:
|
||||
"""Shared operations for AI agents, delegating to Galaxy's service layer."""
|
||||
|
||||
def __init__(self, app: MinimalManagerApp, trans: ProvidesUserContext):
|
||||
def __init__(self, app: MinimalManagerApp, trans: SessionRequestContext):
|
||||
self.app = app
|
||||
self.trans = trans
|
||||
self._tools_service: Any | None = None
|
||||
|
||||
@@ -246,7 +246,7 @@ class ToolRecommendationAgent(BaseGalaxyAgent):
|
||||
|
||||
try:
|
||||
panel_view = self.deps.config.default_panel_view or "default"
|
||||
toolbox_search = self.deps.trans.app.toolbox_search # type: ignore[attr-defined]
|
||||
toolbox_search = self.deps.trans.app.toolbox_search
|
||||
tool_ids = toolbox_search.search(query, panel_view, self.deps.config)
|
||||
|
||||
tools = []
|
||||
|
||||
@@ -977,7 +977,9 @@ class UniverseApplication(StructuredApp, GalaxyManagerApplication, InstallationT
|
||||
self.proxy_manager = ProxyManager(self.config)
|
||||
|
||||
# Must be initialized after job_config.
|
||||
self.workflow_scheduling_manager = scheduling_manager.WorkflowSchedulingManager(self)
|
||||
self.workflow_scheduling_manager = self._register_singleton(
|
||||
scheduling_manager.WorkflowSchedulingManager, scheduling_manager.WorkflowSchedulingManager(self)
|
||||
)
|
||||
|
||||
# Initialize workflow completion monitoring (manager is always available,
|
||||
# but monitor only runs on workflow scheduler processes)
|
||||
|
||||
@@ -28,6 +28,7 @@ from galaxy.config_watchers import ConfigWatchers
|
||||
from galaxy.job_metrics import JobMetrics
|
||||
from galaxy.jobs.manager import NoopManager
|
||||
from galaxy.managers.collections import DatasetCollectionManager
|
||||
from galaxy.managers.context import ProvidesAppContext
|
||||
from galaxy.managers.dbkeys import GenomeBuilds
|
||||
from galaxy.managers.hdas import HDAManager
|
||||
from galaxy.managers.histories import HistoryManager
|
||||
@@ -430,7 +431,7 @@ class MockTrans:
|
||||
|
||||
|
||||
class MockVisualizationsRegistry:
|
||||
def get_visualizations(self, trans, target):
|
||||
def get_visualizations(self, trans: ProvidesAppContext, target):
|
||||
return []
|
||||
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ Contains implementations of the authentication logic.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from galaxy.auth.util import (
|
||||
get_authenticators,
|
||||
@@ -11,6 +12,9 @@ from galaxy.auth.util import (
|
||||
from galaxy.exceptions import Conflict
|
||||
from galaxy.util import string_as_bool
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from galaxy.webapps.base.webapp import GalaxyWebTransaction
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -41,7 +45,7 @@ class AuthManager:
|
||||
break
|
||||
return message, status
|
||||
|
||||
def check_auto_registration(self, trans, login, password, no_password_check=False):
|
||||
def check_auto_registration(self, trans: "GalaxyWebTransaction", login, password, no_password_check=False):
|
||||
"""
|
||||
Checks the username/email & password using auth providers in order.
|
||||
If a match is found, returns the 'auto-register' option for that provider.
|
||||
|
||||
+10
-2
@@ -1,6 +1,10 @@
|
||||
import errno
|
||||
import logging
|
||||
from collections import namedtuple
|
||||
from typing import (
|
||||
Any,
|
||||
TYPE_CHECKING,
|
||||
)
|
||||
|
||||
import galaxy.auth.providers
|
||||
from galaxy.exceptions import Conflict
|
||||
@@ -12,6 +16,10 @@ from galaxy.util import (
|
||||
string_as_bool,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
# runtime cycle: galaxy.managers.context -> galaxy.structured_app -> galaxy.auth -> here
|
||||
from galaxy.managers.context import ProvidesAppContext
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
AUTH_CONF_XML = """<?xml version="1.0"?>
|
||||
@@ -73,8 +81,8 @@ def get_authenticators(auth_config_file, auth_config_file_set):
|
||||
return authenticators
|
||||
|
||||
|
||||
def parse_auth_results(trans, auth_results, options):
|
||||
auth_return = {}
|
||||
def parse_auth_results(trans: "ProvidesAppContext", auth_results, options):
|
||||
auth_return: dict[str, Any] = {}
|
||||
auth_result, auto_email, auto_username = auth_results[:3]
|
||||
auto_username = str(auto_username).lower()
|
||||
# make username unique
|
||||
|
||||
@@ -9,6 +9,11 @@ Additionally, this package implements functionalist's to request temporary acces
|
||||
credentials for cloud-based resource providers (e.g., Amazon AWS, Microsoft Azure).
|
||||
"""
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from galaxy.webapps.base.webapp import GalaxyWebTransaction
|
||||
|
||||
|
||||
class IdentityProvider:
|
||||
"""
|
||||
@@ -40,10 +45,10 @@ class IdentityProvider:
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def refresh(self, trans, token):
|
||||
def refresh(self, trans: "GalaxyWebTransaction", token):
|
||||
raise NotImplementedError()
|
||||
|
||||
def authenticate(self, trans, idphint=None):
|
||||
def authenticate(self, trans: "GalaxyWebTransaction", idphint=None):
|
||||
"""Runs for authentication process. Checks the database if a
|
||||
valid identity exists in the database; if yes, then the user
|
||||
is authenticated, if not, it generates a provider-specific
|
||||
@@ -56,7 +61,7 @@ class IdentityProvider:
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def callback(self, state_token: str, authz_code: str, trans, login_redirect_url):
|
||||
def callback(self, state_token: str, authz_code: str, trans: "GalaxyWebTransaction", login_redirect_url):
|
||||
"""Handles authentication call-backs from identity providers.
|
||||
|
||||
This process maps `state-token` to a user.
|
||||
@@ -72,10 +77,12 @@ class IdentityProvider:
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def disconnect(self, provider, trans, disconnect_redirect_url=None, email=None, association_id=None):
|
||||
def disconnect(
|
||||
self, provider, trans: "GalaxyWebTransaction", disconnect_redirect_url=None, email=None, association_id=None
|
||||
):
|
||||
raise NotImplementedError()
|
||||
|
||||
def logout(self, trans, post_user_logout_href=None):
|
||||
def logout(self, trans: "GalaxyWebTransaction", post_user_logout_href=None):
|
||||
"""
|
||||
Return a URL that will log the user out of the IDP. In OIDC this is
|
||||
called the 'end_session_endpoint'.
|
||||
|
||||
@@ -40,7 +40,10 @@ from .psa_authnz import (
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from galaxy.managers.context import ProvidesAppContext
|
||||
from galaxy.managers.context import (
|
||||
ProvidesAppContext,
|
||||
ProvidesUserContext,
|
||||
)
|
||||
from galaxy.webapps.base.webapp import GalaxyWebTransaction
|
||||
|
||||
OIDC_BACKEND_SCHEMA = resource_path(__name__, "xsd/oidc_backends_config.xsd")
|
||||
@@ -300,7 +303,7 @@ class AuthnzManager:
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def can_user_assume_authn(trans, authn_id):
|
||||
def can_user_assume_authn(trans: ProvidesUserContext, authn_id):
|
||||
qres = trans.sa_session.query(model.UserAuthnzToken).get(authn_id)
|
||||
if qres is None:
|
||||
msg = f"Authentication record with the given `authn_id` (`{trans.security.encode_id(authn_id)}`) not found."
|
||||
@@ -405,9 +408,13 @@ class AuthnzManager:
|
||||
log.exception(msg)
|
||||
return False, msg, None
|
||||
|
||||
def callback(self, provider, state_token, authz_code, trans, login_redirect_url, idphint=None):
|
||||
def callback(
|
||||
self, provider, state_token, authz_code, trans: GalaxyWebTransaction, login_redirect_url, idphint=None
|
||||
):
|
||||
try:
|
||||
success, message, backend = self._get_authnz_backend(provider, idphint=idphint)
|
||||
if backend is None:
|
||||
return False, f"Provider `{provider}` not found", (None, None)
|
||||
if success is False:
|
||||
return False, message, (None, None)
|
||||
return success, message, backend.callback(state_token, authz_code, trans, login_redirect_url)
|
||||
@@ -505,7 +512,7 @@ class AuthnzManager:
|
||||
return user
|
||||
return None
|
||||
|
||||
def logout(self, provider, trans, post_user_logout_href=None):
|
||||
def logout(self, provider, trans: GalaxyWebTransaction, post_user_logout_href=None):
|
||||
"""
|
||||
Log the user out of the identity provider.
|
||||
|
||||
@@ -525,6 +532,8 @@ class AuthnzManager:
|
||||
return False, f"IDP logout is not enabled for {provider}", None
|
||||
|
||||
success, message, backend = self._get_authnz_backend(provider)
|
||||
if backend is None:
|
||||
return False, f"Provider `{provider}` not found", None
|
||||
if success is False:
|
||||
return False, message, None
|
||||
return True, message, backend.logout(trans, post_user_logout_href)
|
||||
@@ -533,9 +542,11 @@ class AuthnzManager:
|
||||
log.exception(msg)
|
||||
return False, msg, None
|
||||
|
||||
def disconnect(self, provider, trans, email=None, disconnect_redirect_url=None, idphint=None):
|
||||
def disconnect(self, provider, trans: GalaxyWebTransaction, email=None, disconnect_redirect_url=None, idphint=None):
|
||||
try:
|
||||
success, message, backend = self._get_authnz_backend(provider, idphint=idphint)
|
||||
if backend is None:
|
||||
return False, f"Provider `{provider}` not found", None
|
||||
if success is False:
|
||||
return False, message, None
|
||||
return backend.disconnect(provider, trans, disconnect_redirect_url, email=email)
|
||||
|
||||
@@ -54,6 +54,7 @@ if TYPE_CHECKING:
|
||||
from social_core.strategy import HttpResponseProtocol
|
||||
|
||||
from galaxy.managers.context import ProvidesAppContext
|
||||
from galaxy.webapps.base.webapp import GalaxyWebTransaction
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -293,7 +294,7 @@ class PSAAuthnz(IdentityProvider):
|
||||
extra_data["expires"] = int(expires - time.time())
|
||||
user_authnz_token.set_extra_data(extra_data)
|
||||
|
||||
def refresh(self, trans, user_authnz_token):
|
||||
def refresh(self, trans: "GalaxyWebTransaction", user_authnz_token):
|
||||
if (
|
||||
not user_authnz_token
|
||||
or not user_authnz_token.extra_data
|
||||
@@ -318,13 +319,13 @@ class PSAAuthnz(IdentityProvider):
|
||||
def _try_to_locate_token_expiration(self, extra_data):
|
||||
return locate_token_expiration(extra_data)
|
||||
|
||||
def authenticate(self, trans, idphint=None) -> "HttpResponseProtocol":
|
||||
def authenticate(self, trans: "GalaxyWebTransaction", idphint=None) -> "HttpResponseProtocol":
|
||||
on_the_fly_config(trans.sa_session)
|
||||
strategy = Strategy(trans.request, trans.session, Storage, self.config)
|
||||
backend = self._load_backend(strategy, self.config["redirect_uri"])
|
||||
return do_auth(backend)
|
||||
|
||||
def callback(self, state_token, authz_code, trans, login_redirect_url):
|
||||
def callback(self, state_token, authz_code, trans: "GalaxyWebTransaction", login_redirect_url):
|
||||
on_the_fly_config(trans.sa_session)
|
||||
# Always set LOGIN_REDIRECT_URL to the base URL for pipeline steps
|
||||
# We'll adjust the final redirect based on fixed_delegated_auth after do_complete
|
||||
@@ -384,7 +385,9 @@ class PSAAuthnz(IdentityProvider):
|
||||
|
||||
return redirect_url, user
|
||||
|
||||
def disconnect(self, provider, trans, disconnect_redirect_url=None, email=None, association_id=None):
|
||||
def disconnect(
|
||||
self, provider, trans: "GalaxyWebTransaction", disconnect_redirect_url=None, email=None, association_id=None
|
||||
):
|
||||
on_the_fly_config(trans.sa_session)
|
||||
self.config[setting_name("DISCONNECT_REDIRECT_URL")] = (
|
||||
disconnect_redirect_url if disconnect_redirect_url is not None else ()
|
||||
@@ -397,7 +400,7 @@ class PSAAuthnz(IdentityProvider):
|
||||
return True, "", response_url
|
||||
return response.get("success", False), response.get("message", ""), ""
|
||||
|
||||
def logout(self, trans, post_user_logout_href=None):
|
||||
def logout(self, trans: "GalaxyWebTransaction", post_user_logout_href=None):
|
||||
"""
|
||||
Logout from the identity provider.
|
||||
|
||||
@@ -846,7 +849,7 @@ def sync_user_profile(strategy=None, details=None, user=None, **kwargs):
|
||||
_send_oidc_profile_update_notification(trans, user, updates)
|
||||
|
||||
|
||||
def _send_oidc_profile_update_notification(trans, user, updates: list[str]) -> None:
|
||||
def _send_oidc_profile_update_notification(trans: "ProvidesAppContext", user, updates: list[str]) -> None:
|
||||
if not trans.app.notification_manager.notifications_enabled:
|
||||
return
|
||||
try:
|
||||
|
||||
@@ -112,7 +112,9 @@ except ModuleNotFoundError:
|
||||
pass
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from galaxy.managers.context import ProvidesUserContext
|
||||
from galaxy.util.compression_utils import FileObjType
|
||||
from galaxy.webapps.base.webapp import GalaxyWebTransaction
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
# pysam 0.16.0.1 emits logs containing the word 'Error', this can confuse the stdout/stderr checkers.
|
||||
@@ -777,7 +779,9 @@ class BamNative(CompressedArchive, _BamOrSam):
|
||||
# Remove temp file and empty temporary directory
|
||||
os.rmdir(tmp_dir)
|
||||
|
||||
def get_chunk(self, trans, dataset: HasFileName, offset: int = 0, ck_size: int | None = None) -> str:
|
||||
def get_chunk(
|
||||
self, trans: "ProvidesUserContext | None", dataset: HasFileName, offset: int = 0, ck_size: int | None = None
|
||||
) -> str:
|
||||
if not offset == -1:
|
||||
try:
|
||||
with pysam.AlignmentFile(
|
||||
@@ -830,7 +834,7 @@ class BamNative(CompressedArchive, _BamOrSam):
|
||||
|
||||
def display_data(
|
||||
self,
|
||||
trans,
|
||||
trans: "GalaxyWebTransaction",
|
||||
dataset: DatasetHasHidProtocol,
|
||||
preview: bool = False,
|
||||
filename: str | None = None,
|
||||
@@ -2537,7 +2541,7 @@ class H5MLM(H5):
|
||||
|
||||
def display_data(
|
||||
self,
|
||||
trans,
|
||||
trans: "GalaxyWebTransaction",
|
||||
dataset: DatasetHasHidProtocol,
|
||||
preview: bool = False,
|
||||
filename: str | None = None,
|
||||
|
||||
@@ -34,6 +34,7 @@ from galaxy.datatypes.protocols import (
|
||||
HasExt,
|
||||
HasExtraFilesAndMetadata,
|
||||
HasFileName,
|
||||
HasHid,
|
||||
HasInfo,
|
||||
HasMetadata,
|
||||
HasName,
|
||||
@@ -69,6 +70,11 @@ from . import (
|
||||
if TYPE_CHECKING:
|
||||
from galaxy.datatypes.display_applications.application import DisplayApplication
|
||||
from galaxy.datatypes.registry import Registry
|
||||
from galaxy.managers.context import (
|
||||
ProvidesAppContext,
|
||||
ProvidesUserContext,
|
||||
)
|
||||
from galaxy.webapps.base.webapp import GalaxyWebTransaction
|
||||
|
||||
XSS_VULNERABLE_MIME_TYPES = [
|
||||
"image/svg+xml", # Unfiltered by Galaxy and may contain JS that would be executed by some browsers.
|
||||
@@ -397,7 +403,7 @@ class Data(metaclass=DataMeta):
|
||||
return error, msg, messagetype
|
||||
|
||||
def _archive_composite_dataset(
|
||||
self, trans, data: DatasetHasHidProtocol, headers: Headers, do_action: str = "zip"
|
||||
self, trans: "GalaxyWebTransaction", data: DatasetHasHidProtocol, headers: Headers, do_action: str = "zip"
|
||||
) -> tuple[ZipstreamWrapper | str, Headers]:
|
||||
# save a composite object into a compressed archive for downloading
|
||||
assert data.name
|
||||
@@ -518,7 +524,7 @@ class Data(metaclass=DataMeta):
|
||||
)
|
||||
return to_content_disposition(filename)
|
||||
|
||||
def _serve_file_download(self, headers, data, trans, to_ext, file_size, **kwd):
|
||||
def _serve_file_download(self, headers, data, trans: "GalaxyWebTransaction", to_ext, file_size, **kwd):
|
||||
if self.is_archive_download(trans.app.datatypes_registry, data.extension):
|
||||
return self._archive_composite_dataset(trans, data, headers, do_action=kwd.get("do_action", "zip"))
|
||||
else:
|
||||
@@ -529,7 +535,9 @@ class Data(metaclass=DataMeta):
|
||||
headers["Content-Disposition"] = self.download_content_disposition(data, to_ext, **kwd)
|
||||
return open(data.get_file_name(auth=ObjectStoreAuth(user=trans.user)), "rb"), headers
|
||||
|
||||
def _serve_binary_file_contents_as_text(self, trans, data, headers, file_size, max_peek_size):
|
||||
def _serve_binary_file_contents_as_text(
|
||||
self, trans: "ProvidesUserContext", data, headers, file_size, max_peek_size
|
||||
):
|
||||
# Use text/plain so the browser preserves whitespace and line endings
|
||||
# and does not attempt to interpret stray markup as HTML.
|
||||
headers["content-type"] = "text/plain; charset=utf-8"
|
||||
@@ -538,7 +546,7 @@ class Data(metaclass=DataMeta):
|
||||
with open(data.get_file_name(auth=ObjectStoreAuth(user=trans.user)), "rb") as fh:
|
||||
return unicodify(fh.read(max_peek_size)), headers
|
||||
|
||||
def _serve_file_contents(self, trans, data, headers, preview, file_size, max_peek_size):
|
||||
def _serve_file_contents(self, trans: "ProvidesUserContext", data, headers, preview, file_size, max_peek_size):
|
||||
from galaxy.datatypes import images
|
||||
|
||||
preview = util.string_as_bool(preview)
|
||||
@@ -559,7 +567,7 @@ class Data(metaclass=DataMeta):
|
||||
|
||||
def display_data(
|
||||
self,
|
||||
trans,
|
||||
trans: "GalaxyWebTransaction",
|
||||
dataset: DatasetHasHidProtocol,
|
||||
preview: bool = False,
|
||||
filename: str | None = None,
|
||||
@@ -671,7 +679,9 @@ class Data(metaclass=DataMeta):
|
||||
result += indicate_data_truncated()
|
||||
return result
|
||||
|
||||
def _yield_user_file_content(self, trans, from_dataset: HasCreatingJob, filename: str, headers: Headers) -> IO:
|
||||
def _yield_user_file_content(
|
||||
self, trans: "ProvidesAppContext", from_dataset: HasCreatingJob, filename: str, headers: Headers
|
||||
) -> IO:
|
||||
"""This method sets the content type header to text/plain if we don't trust html content."""
|
||||
if trans.app.config.sanitize_all_html and headers.get("content-type", None) == "text/html":
|
||||
# Check to see if this dataset's parent job is allowlisted
|
||||
@@ -790,7 +800,9 @@ class Data(metaclass=DataMeta):
|
||||
) -> Union["DisplayApplication", None]:
|
||||
return self.display_applications.get(key, default)
|
||||
|
||||
def get_display_applications_by_dataset(self, dataset: DatasetProtocol, trans) -> dict[str, "DisplayApplication"]:
|
||||
def get_display_applications_by_dataset(
|
||||
self, dataset: DatasetProtocol, trans: "GalaxyWebTransaction"
|
||||
) -> dict[str, "DisplayApplication"]:
|
||||
rval = {}
|
||||
for key, value in self.display_applications.items():
|
||||
value = value.filter_by_dataset(dataset, trans)
|
||||
@@ -851,7 +863,7 @@ class Data(metaclass=DataMeta):
|
||||
return datatypes_registry.get_converters_by_datatype(original_dataset.ext)
|
||||
|
||||
def find_conversion_destination(
|
||||
self, dataset: DatasetProtocol, accepted_formats: list[str], datatypes_registry, **kwd
|
||||
self, dataset: DatasetProtocol, accepted_formats: Iterable[Union[str, "Data"]], datatypes_registry, **kwd
|
||||
) -> tuple[bool, str | None, Any]:
|
||||
"""Returns ( direct_match, converted_ext, existing converted dataset )"""
|
||||
return datatypes_registry.find_conversion_destination_for_dataset_by_extensions(
|
||||
@@ -860,8 +872,8 @@ class Data(metaclass=DataMeta):
|
||||
|
||||
def convert_dataset(
|
||||
self,
|
||||
trans,
|
||||
original_dataset: DatasetHasHidProtocol,
|
||||
trans: "ProvidesUserContext",
|
||||
original_dataset: DatasetProtocol,
|
||||
target_type: str,
|
||||
return_output: bool = False,
|
||||
visible: bool = True,
|
||||
@@ -909,6 +921,9 @@ class Data(metaclass=DataMeta):
|
||||
value.visible = False
|
||||
if return_output:
|
||||
return converted_datasets
|
||||
# Only this message names the dataset by hid; library datasets do not have one
|
||||
# and reach conversion through the return_output callers instead.
|
||||
assert isinstance(original_dataset, HasHid)
|
||||
return f"The file conversion of {converter.name} on data {original_dataset.hid} has been added to the Queue."
|
||||
|
||||
# We need to clear associated files before we set metadata
|
||||
@@ -1048,7 +1063,7 @@ class Data(metaclass=DataMeta):
|
||||
dataset_source = p_dataproviders.dataset.DatasetDataProvider(dataset)
|
||||
return p_dataproviders.chunk.Base64ChunkDataProvider(dataset_source, **settings)
|
||||
|
||||
def _clean_and_set_mime_type(self, trans, mime: str, headers: Headers) -> None:
|
||||
def _clean_and_set_mime_type(self, trans: "ProvidesAppContext", mime: str, headers: Headers) -> None:
|
||||
if mime.lower() in XSS_VULNERABLE_MIME_TYPES:
|
||||
if not getattr(trans.app.config, "serve_xss_vulnerable_mimetypes", True):
|
||||
mime = DEFAULT_MIME_TYPE
|
||||
@@ -1330,7 +1345,7 @@ class ZarrDirectory(Directory):
|
||||
|
||||
def display_data(
|
||||
self,
|
||||
trans,
|
||||
trans: "GalaxyWebTransaction",
|
||||
dataset: DatasetHasHidProtocol,
|
||||
preview: bool = False,
|
||||
filename: str | None = None,
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from galaxy.model import (
|
||||
HistoryDatasetAssociation,
|
||||
User,
|
||||
)
|
||||
from galaxy.security.idencoding import IdEncodingHelper
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from galaxy.managers.context import ProvidesAppContext
|
||||
|
||||
def encode_dataset_user(trans, dataset, user):
|
||||
|
||||
def encode_dataset_user(trans: "ProvidesAppContext", dataset, user):
|
||||
# encode dataset id as usual
|
||||
# encode user id using the dataset create time as the key
|
||||
dataset_hash = trans.security.encode_id(dataset.id)
|
||||
@@ -17,7 +22,7 @@ def encode_dataset_user(trans, dataset, user):
|
||||
return dataset_hash, user_hash
|
||||
|
||||
|
||||
def decode_dataset_user(trans, dataset_hash, user_hash):
|
||||
def decode_dataset_user(trans: "ProvidesAppContext", dataset_hash, user_hash):
|
||||
# decode dataset id as usual
|
||||
# decode user id using the dataset create time as the key
|
||||
dataset_id = trans.security.decode_id(dataset_hash)
|
||||
|
||||
@@ -4,7 +4,10 @@ Location of protocols used in datatypes
|
||||
|
||||
from typing import Any
|
||||
|
||||
from typing_extensions import Protocol
|
||||
from typing_extensions import (
|
||||
Protocol,
|
||||
runtime_checkable,
|
||||
)
|
||||
|
||||
from galaxy.objectstore import ObjectStoreAuth
|
||||
|
||||
@@ -36,6 +39,7 @@ class HasFileName(Protocol):
|
||||
def get_file_name(self, sync_cache=True, auth: ObjectStoreAuth | None = None) -> str: ...
|
||||
|
||||
|
||||
@runtime_checkable
|
||||
class HasHid(Protocol):
|
||||
hid: str
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import tempfile
|
||||
from json import dumps
|
||||
from typing import (
|
||||
cast,
|
||||
TYPE_CHECKING,
|
||||
)
|
||||
|
||||
import pysam
|
||||
@@ -74,6 +75,10 @@ from galaxy.util.markdown import (
|
||||
)
|
||||
from . import dataproviders
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from galaxy.managers.context import ProvidesAppContext
|
||||
from galaxy.webapps.base.webapp import GalaxyWebTransaction
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
MAX_DATA_LINES = 100000
|
||||
@@ -142,7 +147,9 @@ class TabularData(Text):
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def get_chunk(self, trans, dataset: HasFileName, offset: int = 0, ck_size: int | None = None) -> str:
|
||||
def get_chunk(
|
||||
self, trans: "ProvidesAppContext", dataset: HasFileName, offset: int = 0, ck_size: int | None = None
|
||||
) -> str:
|
||||
ck_data, last_read = self._read_chunk(trans, dataset, offset, ck_size)
|
||||
return dumps(
|
||||
{
|
||||
@@ -152,7 +159,7 @@ class TabularData(Text):
|
||||
}
|
||||
)
|
||||
|
||||
def _read_chunk(self, trans, dataset: HasFileName, offset: int, ck_size: int | None = None):
|
||||
def _read_chunk(self, trans: "ProvidesAppContext", dataset: HasFileName, offset: int, ck_size: int | None = None):
|
||||
with compression_utils.get_fileobj(dataset.get_file_name()) as f:
|
||||
f.seek(offset)
|
||||
try:
|
||||
@@ -173,7 +180,7 @@ class TabularData(Text):
|
||||
|
||||
def display_data(
|
||||
self,
|
||||
trans,
|
||||
trans: "GalaxyWebTransaction",
|
||||
dataset: DatasetHasHidProtocol,
|
||||
preview: bool = False,
|
||||
filename: str | None = None,
|
||||
@@ -1631,7 +1638,9 @@ class ConnectivityTable(Tabular):
|
||||
i += 1
|
||||
return False
|
||||
|
||||
def get_chunk(self, trans, dataset: HasFileName, offset: int = 0, ck_size: int | None = None) -> str:
|
||||
def get_chunk(
|
||||
self, trans: "ProvidesAppContext", dataset: HasFileName, offset: int = 0, ck_size: int | None = None
|
||||
) -> str:
|
||||
ck_data, last_read = self._read_chunk(trans, dataset, offset, ck_size)
|
||||
try:
|
||||
# The ConnectivityTable format has several derivatives of which one is delimited by (multiple) spaces.
|
||||
|
||||
@@ -10,6 +10,7 @@ import subprocess
|
||||
import tempfile
|
||||
from typing import (
|
||||
IO,
|
||||
TYPE_CHECKING,
|
||||
)
|
||||
|
||||
import ijson
|
||||
@@ -42,6 +43,13 @@ from galaxy.util import (
|
||||
unicodify,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from galaxy.managers.context import (
|
||||
ProvidesAppContext,
|
||||
ProvidesUserContext,
|
||||
)
|
||||
from galaxy.webapps.base.webapp import GalaxyWebTransaction
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -207,7 +215,7 @@ class Ipynb(Json):
|
||||
|
||||
def display_data(
|
||||
self,
|
||||
trans,
|
||||
trans: "GalaxyWebTransaction",
|
||||
dataset: DatasetHasHidProtocol,
|
||||
preview: bool = False,
|
||||
filename: str | None = None,
|
||||
@@ -223,7 +231,7 @@ class Ipynb(Json):
|
||||
|
||||
def _display_data_trusted(
|
||||
self,
|
||||
trans,
|
||||
trans: "ProvidesUserContext",
|
||||
dataset: DatasetHasHidProtocol,
|
||||
preview: bool = False,
|
||||
filename: str | None = None,
|
||||
@@ -1280,7 +1288,9 @@ class Yaml(Text):
|
||||
"""Returns the mime type of the datatype"""
|
||||
return "application/yaml"
|
||||
|
||||
def _yield_user_file_content(self, trans, from_dataset: HasCreatingJob, filename: str, headers: Headers) -> IO:
|
||||
def _yield_user_file_content(
|
||||
self, trans: "ProvidesAppContext", from_dataset: HasCreatingJob, filename: str, headers: Headers
|
||||
) -> IO:
|
||||
# Override non-standard application/yaml mediatype with
|
||||
# text/plain, so preview is shown in preview iframe,
|
||||
# instead of downloading the file.
|
||||
|
||||
@@ -51,3 +51,16 @@ for entry in loads(error_codes_json):
|
||||
globals()[name] = error_code_obj
|
||||
error_codes_by_name[name] = error_code_obj
|
||||
error_codes_by_int_code[error_code_obj.code] = error_code_obj
|
||||
|
||||
|
||||
def __getattr__(name: str) -> ErrorCode:
|
||||
"""Expose the JSON-driven, dynamically-created error codes to static analysis.
|
||||
|
||||
The codes above are assigned via ``globals()[name] = ...``, which mypy cannot
|
||||
see, so attribute access like ``error_codes.ADMIN_REQUIRED`` is otherwise
|
||||
reported as an unknown attribute.
|
||||
"""
|
||||
try:
|
||||
return error_codes_by_name[name]
|
||||
except KeyError:
|
||||
raise AttributeError(name)
|
||||
|
||||
@@ -26,6 +26,29 @@ from .plugins import (
|
||||
FileSourcePluginsConfig,
|
||||
)
|
||||
|
||||
|
||||
class ProvidesFileSourcesTransaction(Protocol):
|
||||
"""The slice of a Galaxy transaction ProvidesFileSourcesUserContext reads."""
|
||||
|
||||
@property
|
||||
def anonymous(self) -> bool: ...
|
||||
|
||||
@property
|
||||
def user(self) -> Any: ...
|
||||
|
||||
@property
|
||||
def user_ftp_dir(self) -> str | None: ...
|
||||
|
||||
@property
|
||||
def user_is_admin(self) -> bool: ...
|
||||
|
||||
@property
|
||||
def user_vault(self) -> Any: ...
|
||||
|
||||
@property
|
||||
def app(self) -> Any: ...
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -371,7 +394,7 @@ OptionalUserContext = FileSourcesUserContext | None
|
||||
class ProvidesFileSourcesUserContext(FileSourcesUserContext, FileSourceDictifiable):
|
||||
"""Implement a FileSourcesUserContext from a Galaxy ProvidesUserContext (e.g. trans)."""
|
||||
|
||||
def __init__(self, trans, **kwargs):
|
||||
def __init__(self, trans: ProvidesFileSourcesTransaction, **kwargs):
|
||||
self.trans = trans
|
||||
|
||||
@property
|
||||
|
||||
@@ -9,10 +9,10 @@ from galaxy.agents import GalaxyAgentDependencies
|
||||
from galaxy.agents.registry import AgentRegistry
|
||||
from galaxy.agents.router import QueryRouterAgent
|
||||
from galaxy.config import GalaxyAppConfiguration
|
||||
from galaxy.managers.context import ProvidesUserContext
|
||||
from galaxy.managers.jobs import JobManager
|
||||
from galaxy.model import User
|
||||
from galaxy.schema.agents import AgentResponse
|
||||
from galaxy.work.context import SessionRequestContext
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -30,7 +30,7 @@ class AgentService:
|
||||
self.job_manager = job_manager
|
||||
self.registry = registry
|
||||
|
||||
def create_dependencies(self, trans: ProvidesUserContext, user: User) -> GalaxyAgentDependencies:
|
||||
def create_dependencies(self, trans: SessionRequestContext, user: User) -> GalaxyAgentDependencies:
|
||||
"""Create agent dependencies for dependency injection."""
|
||||
toolbox = trans.app.toolbox if hasattr(trans, "app") and hasattr(trans.app, "toolbox") else None
|
||||
return GalaxyAgentDependencies(
|
||||
@@ -47,7 +47,7 @@ class AgentService:
|
||||
self,
|
||||
agent_type: str,
|
||||
query: str,
|
||||
trans: ProvidesUserContext,
|
||||
trans: SessionRequestContext,
|
||||
user: User,
|
||||
context: dict[str, Any] | None = None,
|
||||
) -> AgentResponse:
|
||||
@@ -96,7 +96,7 @@ class AgentService:
|
||||
async def route_and_execute(
|
||||
self,
|
||||
query: str,
|
||||
trans: ProvidesUserContext,
|
||||
trans: SessionRequestContext,
|
||||
user: User,
|
||||
context: dict[str, Any] | None = None,
|
||||
agent_type: str = "auto",
|
||||
|
||||
@@ -65,7 +65,7 @@ from galaxy.structured_app import (
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from galaxy.managers.context import ProvidesAppContext
|
||||
from galaxy.managers.context import ProvidesUserContext
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -84,7 +84,7 @@ FunctionFilterParsersType = dict[str, Any]
|
||||
|
||||
|
||||
# ==== accessors from base/controller.py
|
||||
def security_check(trans, item, check_ownership=False, check_accessible=False):
|
||||
def security_check(trans: "ProvidesUserContext", item, check_ownership=False, check_accessible=False):
|
||||
"""
|
||||
Security checks for an item: checks if (a) user owns item or (b) item
|
||||
is accessible to user. This is a generic method for dealing with objects
|
||||
@@ -162,7 +162,7 @@ def encode_with_security(security: IdEncodingHelper, id: Any, kind: str | None =
|
||||
|
||||
|
||||
def get_object(
|
||||
trans: "ProvidesAppContext",
|
||||
trans: "ProvidesUserContext",
|
||||
id,
|
||||
class_name,
|
||||
check_ownership: bool = False,
|
||||
|
||||
@@ -177,6 +177,54 @@ class DatasetCollectionManager:
|
||||
|
||||
return dataset_collection
|
||||
|
||||
@overload
|
||||
def create(
|
||||
self,
|
||||
trans: ProvidesHistoryContext,
|
||||
parent: model.History,
|
||||
name,
|
||||
collection_type,
|
||||
element_identifiers=None,
|
||||
elements=None,
|
||||
implicit_collection_info=None,
|
||||
trusted_identifiers=None,
|
||||
hide_source_items: bool = False,
|
||||
tags=None,
|
||||
copy_elements: bool = False,
|
||||
history=None,
|
||||
set_hid: bool = True,
|
||||
flush=True,
|
||||
completed_job=None,
|
||||
output_name=None,
|
||||
fields: str | list["FieldDict"] | None = None,
|
||||
column_definitions=None,
|
||||
rows=None,
|
||||
) -> model.HistoryDatasetCollectionAssociation: ...
|
||||
|
||||
@overload
|
||||
def create(
|
||||
self,
|
||||
trans: ProvidesHistoryContext,
|
||||
parent: model.LibraryFolder,
|
||||
name,
|
||||
collection_type,
|
||||
element_identifiers=None,
|
||||
elements=None,
|
||||
implicit_collection_info=None,
|
||||
trusted_identifiers=None,
|
||||
hide_source_items: bool = False,
|
||||
tags=None,
|
||||
copy_elements: bool = False,
|
||||
history=None,
|
||||
set_hid: bool = True,
|
||||
flush=True,
|
||||
completed_job=None,
|
||||
output_name=None,
|
||||
fields: str | list["FieldDict"] | None = None,
|
||||
column_definitions=None,
|
||||
rows=None,
|
||||
) -> model.LibraryDatasetCollectionAssociation: ...
|
||||
|
||||
def create(
|
||||
self,
|
||||
trans: ProvidesHistoryContext,
|
||||
@@ -603,7 +651,12 @@ class DatasetCollectionManager:
|
||||
)
|
||||
|
||||
def __recursively_create_collections_for_identifiers(
|
||||
self, trans, element_identifiers, hide_source_items: bool, copy_elements: bool, history=None
|
||||
self,
|
||||
trans: ProvidesHistoryContext,
|
||||
element_identifiers,
|
||||
hide_source_items: bool,
|
||||
copy_elements: bool,
|
||||
history=None,
|
||||
):
|
||||
for element_identifier in element_identifiers:
|
||||
try:
|
||||
@@ -629,7 +682,7 @@ class DatasetCollectionManager:
|
||||
return element_identifiers
|
||||
|
||||
def __recursively_create_collections_for_elements(
|
||||
self, trans, elements, hide_source_items: bool, copy_elements: bool, history=None
|
||||
self, trans: ProvidesHistoryContext, elements, hide_source_items: bool, copy_elements: bool, history=None
|
||||
) -> None:
|
||||
if elements is self.ELEMENTS_UNINITIALIZED:
|
||||
return
|
||||
@@ -655,7 +708,12 @@ class DatasetCollectionManager:
|
||||
elements.update(new_elements)
|
||||
|
||||
def __load_elements(
|
||||
self, trans, element_identifiers, hide_source_items: bool = False, copy_elements: bool = False, history=None
|
||||
self,
|
||||
trans: ProvidesHistoryContext,
|
||||
element_identifiers,
|
||||
hide_source_items: bool = False,
|
||||
copy_elements: bool = False,
|
||||
history=None,
|
||||
) -> dict[str, HDCAElementObjectType]:
|
||||
elements: dict[str, HDCAElementObjectType] = {}
|
||||
for element_identifier in element_identifiers:
|
||||
@@ -669,7 +727,12 @@ class DatasetCollectionManager:
|
||||
return elements
|
||||
|
||||
def __load_element(
|
||||
self, trans, element_identifier, hide_source_items: bool, copy_elements: bool, history=None
|
||||
self,
|
||||
trans: ProvidesHistoryContext,
|
||||
element_identifier,
|
||||
hide_source_items: bool,
|
||||
copy_elements: bool,
|
||||
history=None,
|
||||
) -> HDCAElementObjectType:
|
||||
# if not isinstance( element_identifier, dict ):
|
||||
# # Is allowing this to just be the id of an hda too clever? Somewhat
|
||||
@@ -752,7 +815,7 @@ class DatasetCollectionManager:
|
||||
return self.__get_library_collection_instance(trans, id, **kwds)
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_dataset_collection(self, trans, encoded_id):
|
||||
def get_dataset_collection(self, trans: ProvidesAppContext, encoded_id):
|
||||
collection_id = int(trans.app.security.decode_id(encoded_id))
|
||||
collection = trans.sa_session.get(DatasetCollection, collection_id)
|
||||
return collection
|
||||
|
||||
@@ -46,6 +46,8 @@ from typing import (
|
||||
Any,
|
||||
cast,
|
||||
Literal,
|
||||
Optional,
|
||||
TYPE_CHECKING,
|
||||
)
|
||||
|
||||
from sqlalchemy import select
|
||||
@@ -69,6 +71,9 @@ from galaxy.security.vault import UserVaultWrapper
|
||||
from galaxy.structured_app import MinimalManagerApp
|
||||
from galaxy.util import bunch
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from galaxy.tools.parameters.dataset_matcher import DatasetMatcherFactory
|
||||
|
||||
|
||||
class ProvidesAppContext:
|
||||
"""For transaction-like objects to provide Galaxy convenience layer for
|
||||
@@ -314,6 +319,18 @@ class ProvidesHistoryContext(ProvidesUserContext):
|
||||
properties.
|
||||
"""
|
||||
|
||||
# set per-request by galaxy.tools.parameters.dataset_matcher while a tool
|
||||
# form is being evaluated
|
||||
dataset_matcher_factory: Optional["DatasetMatcherFactory"] = None
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_history(self, create: bool = False) -> History | None:
|
||||
"""Return the current history, optionally creating one when there is none.
|
||||
|
||||
Transactions do not always have an active history, so None is a valid
|
||||
response even when create is set.
|
||||
"""
|
||||
|
||||
@property
|
||||
@abc.abstractmethod
|
||||
def history(self) -> History | None:
|
||||
|
||||
@@ -981,7 +981,6 @@ class StorageOperationRunExecutor:
|
||||
self.run = run
|
||||
self.user = user
|
||||
self.current_task_id = current_task_id
|
||||
self.trans = SimpleNamespace(user=user)
|
||||
self.storage_operation_manager = storage_operation_manager
|
||||
self.quota_source_map = app.object_store.get_quota_source_map()
|
||||
self.target_quota_usage_at_start = 0
|
||||
@@ -1315,7 +1314,9 @@ class StorageOperationRunExecutor:
|
||||
# Queue cleanup for after DB commit to ensure crash safety.
|
||||
self._pending_cleanups.append((source_proxy, extra_files_path_name))
|
||||
else:
|
||||
self.dataset_manager.update_object_store_id(self.trans, dataset, self.run.target_object_store_id)
|
||||
self.dataset_manager.update_object_store_id_for_user(
|
||||
self.user, dataset, self.run.target_object_store_id
|
||||
)
|
||||
|
||||
self.additional_target_usage += quota_delta
|
||||
self.succeeded_count += 1
|
||||
|
||||
@@ -25,12 +25,17 @@ from galaxy.managers import (
|
||||
secured,
|
||||
users,
|
||||
)
|
||||
from galaxy.managers.context import (
|
||||
ProvidesHistoryContext,
|
||||
ProvidesUserContext,
|
||||
)
|
||||
from galaxy.model import (
|
||||
Dataset,
|
||||
DatasetHash,
|
||||
DatasetInstance,
|
||||
DatasetPermissions,
|
||||
HistoryDatasetAssociation,
|
||||
User,
|
||||
)
|
||||
from galaxy.model.db.role import (
|
||||
get_private_role_user_emails_dict,
|
||||
@@ -133,7 +138,10 @@ class DatasetManager(
|
||||
roles = user.all_roles_exploiting_cache() if user else []
|
||||
return self.app.security_agent.can_access_dataset(roles, dataset)
|
||||
|
||||
def update_object_store_id(self, trans, dataset, object_store_id: str):
|
||||
def update_object_store_id(self, trans: ProvidesUserContext, dataset, object_store_id: str):
|
||||
return self.update_object_store_id_for_user(trans.user, dataset, object_store_id)
|
||||
|
||||
def update_object_store_id_for_user(self, user: User | None, dataset, object_store_id: str):
|
||||
device_source_map = self.app.object_store.get_device_source_map()
|
||||
old_object_store_id = dataset.object_store_id
|
||||
new_object_store_id = object_store_id
|
||||
@@ -146,7 +154,7 @@ class DatasetManager(
|
||||
"Cannot swap object store IDs for object stores that don't share a device ID."
|
||||
)
|
||||
|
||||
if not self.app.security_agent.can_change_object_store_id(trans.user, dataset):
|
||||
if not self.app.security_agent.can_change_object_store_id(user, dataset):
|
||||
# TODO: probably want separate exceptions for doesn't own the dataset and dataset
|
||||
# has been shared.
|
||||
raise exceptions.InsufficientPermissionsException("Cannot change dataset permissions...")
|
||||
@@ -228,7 +236,7 @@ class DatasetRBACPermissions:
|
||||
self.manage = rbac_secured.ManageDatasetRBACPermission(app)
|
||||
|
||||
# TODO: temporary facade over security_agent
|
||||
def available_roles(self, trans, dataset, controller="root"):
|
||||
def available_roles(self, trans: ProvidesUserContext, dataset, controller="root"):
|
||||
return self.app.security_agent.get_legitimate_roles(trans, dataset, controller)
|
||||
|
||||
def get(self, dataset, flush=True):
|
||||
@@ -494,7 +502,7 @@ class DatasetAssociationManager(
|
||||
rval["modify_item_roles"] = role_name_id_pairs(modify_roles, private_role_emails, encode_id)
|
||||
return rval
|
||||
|
||||
def ensure_dataset_on_disk(self, trans, dataset: U):
|
||||
def ensure_dataset_on_disk(self, trans: ProvidesUserContext, dataset: U):
|
||||
# Not a guarantee data is really present, but excludes a lot of expected cases
|
||||
if not dataset.dataset:
|
||||
raise exceptions.InternalServerError("Item has no associated dataset.")
|
||||
@@ -546,7 +554,7 @@ class DatasetAssociationManager(
|
||||
)
|
||||
return True
|
||||
|
||||
def detect_datatype(self, trans, dataset_assoc: U):
|
||||
def detect_datatype(self, trans: ProvidesHistoryContext, dataset_assoc: U):
|
||||
"""Sniff and assign the datatype to a given dataset association (ldda or hda)"""
|
||||
session = self.session()
|
||||
self.ensure_can_change_datatype(dataset_assoc)
|
||||
@@ -558,7 +566,9 @@ class DatasetAssociationManager(
|
||||
session.commit()
|
||||
self.set_metadata(trans, dataset_assoc)
|
||||
|
||||
def set_metadata(self, trans, dataset_assoc: U, overwrite: bool = False, validate: bool = True) -> None:
|
||||
def set_metadata(
|
||||
self, trans: ProvidesHistoryContext, dataset_assoc: U, overwrite: bool = False, validate: bool = True
|
||||
) -> None:
|
||||
"""Trigger a job that detects and sets metadata on a given dataset association (ldda or hda)"""
|
||||
self.ensure_can_set_metadata(dataset_assoc)
|
||||
if overwrite:
|
||||
@@ -579,7 +589,7 @@ class DatasetAssociationManager(
|
||||
if spec.get("default"):
|
||||
setattr(data.metadata, name, spec.unwrap(spec.get("default")))
|
||||
|
||||
def update_permissions(self, trans, dataset_assoc: U, **kwd):
|
||||
def update_permissions(self, trans: ProvidesUserContext, dataset_assoc: U, **kwd):
|
||||
action = kwd.get("action", "set_permissions")
|
||||
if action not in ["remove_restrictions", "make_private", "set_permissions"]:
|
||||
raise exceptions.RequestParameterInvalidException(
|
||||
@@ -632,7 +642,7 @@ class DatasetAssociationManager(
|
||||
|
||||
self._set_permissions(trans, dataset_assoc, role_ids_dict)
|
||||
|
||||
def _set_permissions(self, trans, dataset_assoc: U, roles_dict):
|
||||
def _set_permissions(self, trans: ProvidesUserContext, dataset_assoc: U, roles_dict):
|
||||
raise exceptions.NotImplemented()
|
||||
|
||||
|
||||
|
||||
@@ -204,7 +204,9 @@ class DisplayApplicationsManager:
|
||||
)
|
||||
raise MessageException("You do not have permission to view this dataset at an external display application.")
|
||||
|
||||
def _can_access_dataset(self, trans, dataset_association, allow_admin=True, additional_roles=None):
|
||||
def _can_access_dataset(
|
||||
self, trans: ProvidesUserContext, dataset_association, allow_admin=True, additional_roles=None
|
||||
):
|
||||
roles = trans.get_current_user_roles()
|
||||
if additional_roles:
|
||||
roles = roles + additional_roles
|
||||
|
||||
@@ -7,15 +7,16 @@ from typing import (
|
||||
import yaml
|
||||
|
||||
from galaxy import exceptions
|
||||
from galaxy.managers.context import ProvidesUserContext
|
||||
from galaxy.util import in_directory
|
||||
|
||||
|
||||
def artifact_class(trans, as_dict: dict[str, Any], allow_in_directory: str | None = None):
|
||||
def artifact_class(trans: ProvidesUserContext | None, as_dict: dict[str, Any], allow_in_directory: str | None = None):
|
||||
object_id = as_dict.get("object_id", None)
|
||||
if as_dict.get("src", None) == "from_path":
|
||||
workflow_path = as_dict.get("path")
|
||||
allow = not trans or trans.user_is_admin
|
||||
allow = allow or (allow_in_directory and in_directory(workflow_path, allow_in_directory))
|
||||
allow = allow or bool(allow_in_directory and in_directory(workflow_path, allow_in_directory))
|
||||
|
||||
if not allow:
|
||||
raise exceptions.AdminRequiredException()
|
||||
|
||||
@@ -563,7 +563,7 @@ class FileSourceInstancesManager:
|
||||
def _save(self, user_file_source: UserFileSource) -> None:
|
||||
save_template_instance(self._sa_session, user_file_source)
|
||||
|
||||
def _to_model(self, trans, persisted_file_source: UserFileSource) -> UserFileSourceModel:
|
||||
def _to_model(self, trans: ProvidesUserContext, persisted_file_source: UserFileSource) -> UserFileSourceModel:
|
||||
file_source_type = persisted_file_source.template.configuration.type
|
||||
secrets = persisted_file_source.template_secrets or []
|
||||
uuid = str(persisted_file_source.uuid)
|
||||
|
||||
@@ -54,7 +54,10 @@ from galaxy.schema.schema import LibraryFolderContentsIndexQueryPayload
|
||||
from galaxy.security import RBACAgent
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from galaxy.managers.context import ProvidesUserContext
|
||||
from galaxy.managers.context import (
|
||||
ProvidesAppContext,
|
||||
ProvidesUserContext,
|
||||
)
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -63,7 +66,7 @@ log = logging.getLogger(__name__)
|
||||
class SecurityParams:
|
||||
"""Contains security data bundled for reusability."""
|
||||
|
||||
user_role_ids: list[model.Role]
|
||||
user_role_ids: list[int]
|
||||
security_agent: RBACAgent
|
||||
is_admin: bool
|
||||
|
||||
@@ -148,7 +151,7 @@ class FolderManager:
|
||||
folder = self.check_accessible(trans, folder)
|
||||
return folder
|
||||
|
||||
def check_modifyable(self, trans, folder):
|
||||
def check_modifyable(self, trans: "ProvidesUserContext", folder):
|
||||
"""
|
||||
Check whether the user can modify the folder (name and description).
|
||||
|
||||
@@ -165,7 +168,7 @@ class FolderManager:
|
||||
else:
|
||||
return folder
|
||||
|
||||
def check_manageable(self, trans, folder):
|
||||
def check_manageable(self, trans: "ProvidesUserContext", folder):
|
||||
"""
|
||||
Check whether the user can manage the folder.
|
||||
|
||||
@@ -182,14 +185,14 @@ class FolderManager:
|
||||
else:
|
||||
return folder
|
||||
|
||||
def check_accessible(self, trans, folder):
|
||||
def check_accessible(self, trans: "ProvidesUserContext", folder):
|
||||
"""
|
||||
Check whether the folder is accessible to current user.
|
||||
By default every folder is accessible (contents have their own permissions).
|
||||
"""
|
||||
return folder
|
||||
|
||||
def get_folder_dict(self, trans, folder):
|
||||
def get_folder_dict(self, trans: "ProvidesUserContext", folder):
|
||||
"""
|
||||
Return folder data in the form of a dictionary.
|
||||
|
||||
@@ -204,7 +207,13 @@ class FolderManager:
|
||||
folder_dict["update_time"] = folder.update_time
|
||||
return folder_dict
|
||||
|
||||
def create(self, trans, parent_folder_id: int, new_folder_name: str, new_folder_description: str | None = None):
|
||||
def create(
|
||||
self,
|
||||
trans: "ProvidesUserContext",
|
||||
parent_folder_id: int,
|
||||
new_folder_name: str,
|
||||
new_folder_description: str | None = None,
|
||||
):
|
||||
"""
|
||||
Create a new folder under the given folder.
|
||||
|
||||
@@ -240,7 +249,7 @@ class FolderManager:
|
||||
trans.app.security_agent.copy_library_permissions(trans, parent_folder, new_folder)
|
||||
return new_folder
|
||||
|
||||
def update(self, trans, folder, name=None, description=None):
|
||||
def update(self, trans: "ProvidesUserContext", folder, name=None, description=None):
|
||||
"""
|
||||
Update the given folder's name or description.
|
||||
|
||||
@@ -272,7 +281,7 @@ class FolderManager:
|
||||
trans.sa_session.commit()
|
||||
return folder
|
||||
|
||||
def delete(self, trans, folder, undelete=False):
|
||||
def delete(self, trans: "ProvidesUserContext", folder, undelete=False):
|
||||
"""
|
||||
Mark given folder deleted/undeleted based on the flag.
|
||||
|
||||
@@ -296,7 +305,7 @@ class FolderManager:
|
||||
trans.sa_session.commit()
|
||||
return folder
|
||||
|
||||
def get_current_roles(self, trans, folder):
|
||||
def get_current_roles(self, trans: "ProvidesUserContext", folder):
|
||||
"""
|
||||
Find all roles currently connected to relevant permissions
|
||||
on the folder.
|
||||
@@ -332,7 +341,7 @@ class FolderManager:
|
||||
add_library_item_role_list=role_name_id_pairs(add_roles, private_role_emails, encode_id),
|
||||
)
|
||||
|
||||
def can_add_item(self, trans, folder):
|
||||
def can_add_item(self, trans: "ProvidesUserContext", folder):
|
||||
"""
|
||||
Return true if the user has permissions to add item to the given folder.
|
||||
"""
|
||||
@@ -367,7 +376,7 @@ class FolderManager:
|
||||
raise MalformedId(f"Malformed folder id ( {str(encoded_folder_id)} ) specified, unable to decode.")
|
||||
return cut_id
|
||||
|
||||
def decode_folder_id(self, trans, encoded_folder_id):
|
||||
def decode_folder_id(self, trans: "ProvidesAppContext", encoded_folder_id):
|
||||
"""
|
||||
Decode the folder id given that it has already lost the prefixed 'F'.
|
||||
|
||||
@@ -381,7 +390,7 @@ class FolderManager:
|
||||
"""
|
||||
return trans.security.decode_id(encoded_folder_id, object_name="folder")
|
||||
|
||||
def cut_and_decode(self, trans, encoded_folder_id):
|
||||
def cut_and_decode(self, trans: "ProvidesAppContext", encoded_folder_id):
|
||||
"""
|
||||
Cuts the folder prefix (the prepended 'F') and returns the decoded id.
|
||||
|
||||
@@ -395,7 +404,7 @@ class FolderManager:
|
||||
|
||||
def get_contents(
|
||||
self,
|
||||
trans,
|
||||
trans: "ProvidesUserContext",
|
||||
folder: LibraryFolder,
|
||||
payload: LibraryFolderContentsIndexQueryPayload,
|
||||
) -> tuple[list[LibraryFolder | LibraryDataset], int]:
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.exc import (
|
||||
MultipleResultsFound,
|
||||
@@ -17,6 +19,9 @@ from galaxy.model import (
|
||||
)
|
||||
from galaxy.util import unicodify
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from galaxy.webapps.base.webapp import GalaxyWebTransaction
|
||||
|
||||
|
||||
def get_form_definitions(session):
|
||||
stmt = select(FormDefinition)
|
||||
@@ -33,7 +38,7 @@ def get_filtered_form_definitions_current(session, filter):
|
||||
return session.scalars(stmt)
|
||||
|
||||
|
||||
def get_form(trans, form_id):
|
||||
def get_form(trans: "GalaxyWebTransaction", form_id):
|
||||
"""Get a FormDefinition from the database by id."""
|
||||
form = trans.sa_session.query(FormDefinitionCurrent).get(trans.security.decode_id(form_id))
|
||||
if not form:
|
||||
|
||||
@@ -12,7 +12,10 @@ from galaxy.exceptions import (
|
||||
ReferenceDataError,
|
||||
RequestParameterInvalidException,
|
||||
)
|
||||
from galaxy.managers.context import ProvidesUserContext
|
||||
from galaxy.managers.context import (
|
||||
ProvidesHistoryContext,
|
||||
ProvidesUserContext,
|
||||
)
|
||||
from galaxy.model import User
|
||||
from galaxy.model.database_utils import is_postgres
|
||||
from galaxy.structured_app import (
|
||||
@@ -41,7 +44,7 @@ class GenomesManager:
|
||||
return False
|
||||
|
||||
def get_genome(
|
||||
self, trans: ProvidesUserContext, id: str, num: int, chrom: str, low: int, high: int, reference: bool
|
||||
self, trans: ProvidesHistoryContext, id: str, num: int, chrom: str, low: int, high: int, reference: bool
|
||||
) -> Any:
|
||||
if reference:
|
||||
region = self.genomes.reference(trans, dbkey=id, chrom=chrom, low=low, high=high)
|
||||
|
||||
@@ -19,6 +19,7 @@ from galaxy.schema.groups import (
|
||||
GroupUpdatePayload,
|
||||
)
|
||||
from galaxy.structured_app import MinimalManagerApp
|
||||
from galaxy.work.context import SessionRequestContext
|
||||
|
||||
|
||||
class GroupsManager:
|
||||
@@ -27,7 +28,7 @@ class GroupsManager:
|
||||
def __init__(self, app: MinimalManagerApp) -> None:
|
||||
self._app = app
|
||||
|
||||
def index(self, trans: ProvidesAppContext):
|
||||
def index(self, trans: SessionRequestContext):
|
||||
"""
|
||||
Displays a collection (list) of groups.
|
||||
"""
|
||||
@@ -39,7 +40,7 @@ class GroupsManager:
|
||||
rval.append(item)
|
||||
return rval
|
||||
|
||||
def create(self, trans: ProvidesAppContext, payload: GroupCreatePayload):
|
||||
def create(self, trans: SessionRequestContext, payload: GroupCreatePayload):
|
||||
"""
|
||||
Creates a new group.
|
||||
"""
|
||||
@@ -73,7 +74,7 @@ class GroupsManager:
|
||||
item["url"] = self._url_for(trans, "group", id=encoded_id)
|
||||
return [item]
|
||||
|
||||
def show(self, trans: ProvidesAppContext, group_id: int):
|
||||
def show(self, trans: SessionRequestContext, group_id: int):
|
||||
"""
|
||||
Displays information about a group.
|
||||
"""
|
||||
@@ -85,7 +86,7 @@ class GroupsManager:
|
||||
item["roles_url"] = self._url_for(trans, "group_roles", group_id=encoded_id)
|
||||
return item
|
||||
|
||||
def update(self, trans: ProvidesAppContext, group_id: int, payload: GroupUpdatePayload):
|
||||
def update(self, trans: SessionRequestContext, group_id: int, payload: GroupUpdatePayload):
|
||||
"""
|
||||
Modifies a group.
|
||||
"""
|
||||
@@ -138,7 +139,7 @@ class GroupsManager:
|
||||
trans.sa_session.add(group)
|
||||
trans.sa_session.commit()
|
||||
|
||||
def _url_for(self, trans, name, **kwargs):
|
||||
def _url_for(self, trans: SessionRequestContext, name, **kwargs):
|
||||
return trans.url_builder(name, **kwargs)
|
||||
|
||||
def _check_duplicated_group_name(self, sa_session: galaxy_scoped_session, group_name: str) -> None:
|
||||
|
||||
@@ -44,7 +44,11 @@ from galaxy.managers import (
|
||||
taggable,
|
||||
users,
|
||||
)
|
||||
from galaxy.managers.context import ProvidesHistoryContext
|
||||
from galaxy.managers.context import (
|
||||
ProvidesAppContext,
|
||||
ProvidesHistoryContext,
|
||||
ProvidesUserContext,
|
||||
)
|
||||
from galaxy.model import (
|
||||
HistoryDatasetAssociation,
|
||||
HistoryDatasetCollectionAssociation,
|
||||
@@ -79,7 +83,10 @@ from galaxy.tool_util_models.parameters import (
|
||||
FileRequestUri,
|
||||
)
|
||||
from galaxy.util.compression_utils import get_fileobj
|
||||
from galaxy.work.context import WorkRequestContext
|
||||
from galaxy.work.context import (
|
||||
SessionRequestContext,
|
||||
WorkRequestContext,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from galaxy.model import LibraryDatasetDatasetAssociation
|
||||
@@ -339,7 +346,7 @@ class HDAManager(
|
||||
# override to scope to history owner
|
||||
return self._user_annotation(hda, hda.user)
|
||||
|
||||
def _set_permissions(self, trans, hda, role_ids_dict):
|
||||
def _set_permissions(self, trans: ProvidesUserContext, hda, role_ids_dict):
|
||||
# The user associated the DATASET_ACCESS permission on the dataset with 1 or more roles. We
|
||||
# need to ensure that they did not associate roles that would cause accessibility problems.
|
||||
security_agent = trans.app.security_agent
|
||||
@@ -657,7 +664,7 @@ class HDASerializer( # datasets._UnflattenedMetadataDatasetAssociationSerialize
|
||||
keys = self._view_to_keys("inaccessible")
|
||||
return super().serialize(item, keys, user=user, **context)
|
||||
|
||||
def serialize_display_apps(self, item, key, trans=None, **context):
|
||||
def serialize_display_apps(self, item, key, trans: ProvidesAppContext | None = None, **context):
|
||||
"""
|
||||
Return dictionary containing new-style display app urls.
|
||||
"""
|
||||
@@ -681,7 +688,9 @@ class HDASerializer( # datasets._UnflattenedMetadataDatasetAssociationSerialize
|
||||
|
||||
return display_apps
|
||||
|
||||
def serialize_old_display_applications(self, item, key, trans=None, **context):
|
||||
# trans arrives through the serializer dispatch's **context, so it has to be optional here;
|
||||
# a request context is required in practice, for trans.request.base.
|
||||
def serialize_old_display_applications(self, item, key, trans: "SessionRequestContext | None" = None, **context):
|
||||
"""
|
||||
Return dictionary containing old-style display app urls.
|
||||
"""
|
||||
@@ -692,6 +701,7 @@ class HDASerializer( # datasets._UnflattenedMetadataDatasetAssociationSerialize
|
||||
and hda.state == HistoryDatasetAssociation.states.OK
|
||||
and not hda.deleted
|
||||
):
|
||||
assert trans is not None
|
||||
display_link_fn = hda.datatype.get_display_links
|
||||
for display_app in hda.datatype.get_display_types():
|
||||
target_frame, display_links = display_link_fn(
|
||||
|
||||
@@ -12,6 +12,7 @@ from typing import (
|
||||
cast,
|
||||
Literal,
|
||||
TYPE_CHECKING,
|
||||
Union,
|
||||
)
|
||||
from uuid import UUID
|
||||
|
||||
@@ -46,7 +47,10 @@ from galaxy.managers.base import (
|
||||
SortableManager,
|
||||
StorageCleanerManager,
|
||||
)
|
||||
from galaxy.managers.context import ProvidesUserContext
|
||||
from galaxy.managers.context import (
|
||||
ProvidesHistoryContext,
|
||||
ProvidesUserContext,
|
||||
)
|
||||
from galaxy.managers.export_tracker import StoreExportTracker
|
||||
from galaxy.model import (
|
||||
History,
|
||||
@@ -87,10 +91,17 @@ from galaxy.util.search import (
|
||||
parse_filters_structured,
|
||||
RawTextTerm,
|
||||
)
|
||||
from galaxy.work.context import SessionRequestContext
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from sqlalchemy.engine import ScalarResult
|
||||
|
||||
from galaxy.webapps.base.webapp import GalaxyWebTransaction
|
||||
|
||||
# "Current history" is a request/session-scoped concept: only trans objects backed by
|
||||
# a session (FastAPI SessionRequestContext) or the legacy web transaction implement it.
|
||||
CurrentHistoryContext = Union[SessionRequestContext, "GalaxyWebTransaction"]
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
INDEX_SEARCH_FILTERS = {
|
||||
@@ -321,14 +332,14 @@ class HistoryManager(sharable.SharableModelManager[model.History], deletable.Pur
|
||||
# def is_current_users_current_history( self, history, trans ):
|
||||
# pass
|
||||
|
||||
def get_current(self, trans):
|
||||
def get_current(self, trans: CurrentHistoryContext):
|
||||
"""
|
||||
Return the current history.
|
||||
"""
|
||||
# TODO: trans
|
||||
return trans.get_history()
|
||||
|
||||
def set_current(self, trans, history):
|
||||
def set_current(self, trans: CurrentHistoryContext, history):
|
||||
"""
|
||||
Set the current history.
|
||||
"""
|
||||
@@ -336,7 +347,7 @@ class HistoryManager(sharable.SharableModelManager[model.History], deletable.Pur
|
||||
trans.set_history(history)
|
||||
return history
|
||||
|
||||
def set_current_by_id(self, trans, history_id):
|
||||
def set_current_by_id(self, trans: CurrentHistoryContext, history_id):
|
||||
"""
|
||||
Set the current history by an id.
|
||||
"""
|
||||
@@ -382,16 +393,17 @@ class HistoryManager(sharable.SharableModelManager[model.History], deletable.Pur
|
||||
stmt = select(Job).where(Job.history == history).where(Job.state.in_(Job.non_ready_states))
|
||||
return self.session().scalars(stmt)
|
||||
|
||||
def queue_history_import(self, trans, archive_type, archive_source, target_history=None):
|
||||
def queue_history_import(self, trans: ProvidesHistoryContext, archive_type, archive_source, target_history=None):
|
||||
# Run job to do import.
|
||||
history_imp_tool = trans.app.toolbox.get_tool("__IMPORT_HISTORY__")
|
||||
assert history_imp_tool is not None
|
||||
incoming = {"__ARCHIVE_SOURCE__": archive_source, "__ARCHIVE_TYPE__": archive_type}
|
||||
job, *_ = history_imp_tool.execute(trans, incoming=incoming, history=target_history)
|
||||
trans.app.job_manager.enqueue(job, tool=history_imp_tool)
|
||||
return job
|
||||
|
||||
# TODO: remove this function when the legacy endpoint using it is removed
|
||||
def legacy_serve_ready_history_export(self, trans, jeha):
|
||||
def legacy_serve_ready_history_export(self, trans: "GalaxyWebTransaction", jeha):
|
||||
assert jeha.ready
|
||||
if jeha.compressed:
|
||||
trans.response.set_content_type("application/x-gzip")
|
||||
@@ -402,7 +414,7 @@ class HistoryManager(sharable.SharableModelManager[model.History], deletable.Pur
|
||||
archive = trans.app.object_store.get_filename(jeha.dataset, auth=ObjectStoreAuth(user=trans.user))
|
||||
return open(archive, mode="rb")
|
||||
|
||||
def get_ready_history_export_file_path(self, trans, jeha) -> str:
|
||||
def get_ready_history_export_file_path(self, trans: ProvidesUserContext, jeha) -> str:
|
||||
"""
|
||||
Serves the history export archive for use as a streaming response so the file
|
||||
doesn't need to be loaded into memory.
|
||||
@@ -411,7 +423,14 @@ class HistoryManager(sharable.SharableModelManager[model.History], deletable.Pur
|
||||
return trans.app.object_store.get_filename(jeha.dataset, auth=ObjectStoreAuth(user=trans.user))
|
||||
|
||||
def queue_history_export(
|
||||
self, trans, history, gzip=True, include_hidden=False, include_deleted=False, directory_uri=None, file_name=None
|
||||
self,
|
||||
trans: ProvidesHistoryContext,
|
||||
history,
|
||||
gzip=True,
|
||||
include_hidden=False,
|
||||
include_deleted=False,
|
||||
directory_uri=None,
|
||||
file_name=None,
|
||||
):
|
||||
# Convert options to booleans.
|
||||
if isinstance(gzip, str):
|
||||
@@ -437,12 +456,18 @@ class HistoryManager(sharable.SharableModelManager[model.History], deletable.Pur
|
||||
|
||||
# Run job to do export.
|
||||
history_exp_tool = trans.app.toolbox.get_tool(export_tool_id)
|
||||
assert history_exp_tool is not None
|
||||
job, *_ = history_exp_tool.execute(trans, incoming=params, history=history)
|
||||
trans.app.job_manager.enqueue(job, tool=history_exp_tool)
|
||||
return job
|
||||
|
||||
def get_sharing_extra_information(
|
||||
self, trans, item, users: set[model.User], errors: set[str], option: sharable.SharingOptions | None = None
|
||||
self,
|
||||
trans: ProvidesUserContext,
|
||||
item,
|
||||
users: set[model.User],
|
||||
errors: set[str],
|
||||
option: sharable.SharingOptions | None = None,
|
||||
) -> ShareHistoryExtra:
|
||||
"""Returns optional extra information about the datasets of the history that can be accessed by the users."""
|
||||
extra = ShareHistoryExtra()
|
||||
@@ -505,7 +530,7 @@ class HistoryManager(sharable.SharableModelManager[model.History], deletable.Pur
|
||||
)
|
||||
return bool(self.session().scalar(stmt))
|
||||
|
||||
def make_members_public(self, trans, item):
|
||||
def make_members_public(self, trans: ProvidesUserContext, item):
|
||||
"""Make the non-purged datasets in history public.
|
||||
Performs permissions check.
|
||||
"""
|
||||
@@ -686,7 +711,9 @@ class HistoryExportManager:
|
||||
self.app = app
|
||||
self.export_tracker = export_tracker
|
||||
|
||||
def get_task_exports(self, trans, history_id: int, limit: int | None = None, offset: int | None = None):
|
||||
def get_task_exports(
|
||||
self, trans: ProvidesHistoryContext, history_id: int, limit: int | None = None, offset: int | None = None
|
||||
):
|
||||
"""Returns task-based exports associated with this history"""
|
||||
history = self._history(trans, history_id)
|
||||
export_associations = self.export_tracker.get_object_exports(
|
||||
@@ -759,17 +786,18 @@ class HistoryExportManager:
|
||||
"export_metadata": export_metadata,
|
||||
}
|
||||
|
||||
def get_exports(self, trans, history_id: int):
|
||||
def get_exports(self, trans: ProvidesHistoryContext, history_id: int):
|
||||
"""Returns job-based exports associated with this history"""
|
||||
history = self._history(trans, history_id)
|
||||
matching_exports = history.exports
|
||||
return [self.serialize(trans, history_id, e) for e in matching_exports]
|
||||
|
||||
def serialize(self, trans, history_id: int, jeha: model.JobExportHistoryArchive) -> dict:
|
||||
def serialize(self, trans: ProvidesHistoryContext, history_id: int, jeha: model.JobExportHistoryArchive) -> dict:
|
||||
rval = jeha.to_dict()
|
||||
rval["type"] = "job"
|
||||
encoded_jeha_id = Security.security.encode_id(jeha.id)
|
||||
encoded_history_id = Security.security.encode_id(history_id)
|
||||
assert trans.url_builder
|
||||
api_url = trans.url_builder("history_archive_download", history_id=encoded_history_id, jeha_id=encoded_jeha_id)
|
||||
external_url = trans.url_builder(
|
||||
"history_archive_download", history_id=encoded_history_id, jeha_id="latest", qualified=True
|
||||
@@ -783,7 +811,9 @@ class HistoryExportManager:
|
||||
rval = trans.security.encode_all_ids(rval)
|
||||
return rval
|
||||
|
||||
def get_ready_jeha(self, trans, history_id: int, jeha_id: int | Literal["latest"] = "latest"):
|
||||
def get_ready_jeha(
|
||||
self, trans: ProvidesHistoryContext, history_id: int, jeha_id: int | Literal["latest"] = "latest"
|
||||
):
|
||||
history = self._history(trans, history_id)
|
||||
matching_exports = history.exports
|
||||
if jeha_id != "latest":
|
||||
@@ -797,7 +827,7 @@ class HistoryExportManager:
|
||||
|
||||
return jeha
|
||||
|
||||
def _history(self, trans, history_id: int) -> model.History:
|
||||
def _history(self, trans: ProvidesHistoryContext, history_id: int) -> model.History:
|
||||
history = self.app.history_manager.get_accessible(history_id, trans.user, current_history=trans.history)
|
||||
return history
|
||||
|
||||
@@ -995,7 +1025,7 @@ class HistorySerializer(sharable.SharableModelSerializer, deletable.PurgableSeri
|
||||
|
||||
return state
|
||||
|
||||
def serialize_contents(self, item, key, trans=None, user=None, **context):
|
||||
def serialize_contents(self, item, key, trans: ProvidesHistoryContext | None = None, user=None, **context):
|
||||
history = item
|
||||
returned = []
|
||||
for content in self.manager.contents_manager._union_of_contents_query(history).all():
|
||||
@@ -1005,7 +1035,7 @@ class HistorySerializer(sharable.SharableModelSerializer, deletable.PurgableSeri
|
||||
returned.append(serialized)
|
||||
return returned
|
||||
|
||||
def serialize_contents_states(self, item, key, trans=None, **context):
|
||||
def serialize_contents_states(self, item, key, trans: ProvidesHistoryContext | None = None, **context):
|
||||
"""
|
||||
Return a dictionary containing the counts of all contents in each state
|
||||
keyed by the distinct states.
|
||||
@@ -1053,7 +1083,7 @@ class HistoryDeserializer(sharable.SharableModelDeserializer, deletable.Purgable
|
||||
def deserialize_preferred_object_store_id(self, item, key, val, **context):
|
||||
preferred_object_store_id = val
|
||||
validation_error = validate_preferred_object_store_id(
|
||||
context["trans"], self.app.object_store, preferred_object_store_id
|
||||
context["trans"].user, self.app.object_store, preferred_object_store_id
|
||||
)
|
||||
if validation_error:
|
||||
raise ModelDeserializingError(validation_error)
|
||||
|
||||
@@ -45,6 +45,7 @@ from galaxy.managers import (
|
||||
taggable,
|
||||
tools,
|
||||
)
|
||||
from galaxy.managers.context import ProvidesHistoryContext
|
||||
from galaxy.managers.job_connections import JobConnectionsManager
|
||||
from galaxy.model import batch_fetch_job_state_summaries
|
||||
from galaxy.schema import ValueFilterQueryParams
|
||||
@@ -242,7 +243,9 @@ class HistoryContentsManager(base.SortableManager):
|
||||
.filter_by(history_id=history_id)
|
||||
)
|
||||
|
||||
def copy_contents(self, trans, history_id, payload: CopyDatasetsPayload) -> CopyDatasetsResponse:
|
||||
def copy_contents(
|
||||
self, trans: ProvidesHistoryContext, history_id, payload: CopyDatasetsPayload
|
||||
) -> CopyDatasetsResponse:
|
||||
user = trans.get_user()
|
||||
if not user:
|
||||
raise glx_exceptions.MessageException("Please login to copy datasets between histories.")
|
||||
|
||||
@@ -38,6 +38,7 @@ if TYPE_CHECKING:
|
||||
from galaxy.managers.context import ProvidesUserContext
|
||||
from galaxy.structured_app import MinimalManagerApp
|
||||
from galaxy.tools import Tool
|
||||
from galaxy.webapps.base.webapp import GalaxyWebTransaction
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -301,7 +302,7 @@ class InteractiveToolManager:
|
||||
self.sa_session.commit()
|
||||
self.propagator.remove_entry_point(entry_point)
|
||||
|
||||
def target_if_active(self, trans, entry_point: InteractiveToolEntryPoint) -> str | None:
|
||||
def target_if_active(self, trans: "GalaxyWebTransaction", entry_point: InteractiveToolEntryPoint) -> str | None:
|
||||
if entry_point.active and not entry_point.deleted:
|
||||
use_it_proxy_host_cfg = (
|
||||
not self.app.config.interactivetools_upstream_proxy and self.app.config.interactivetools_proxy_host
|
||||
@@ -354,7 +355,7 @@ class InteractiveToolManager:
|
||||
url_path += entry_point.entry_url.lstrip("/")
|
||||
return url_path
|
||||
|
||||
def access_entry_point_target(self, trans: "ProvidesUserContext", entry_point_id: int) -> str | None:
|
||||
def access_entry_point_target(self, trans: "GalaxyWebTransaction", entry_point_id: int) -> str | None:
|
||||
entry_point = self.sa_session.get(InteractiveToolEntryPoint, entry_point_id)
|
||||
assert entry_point
|
||||
if self.can_access_entry_point(trans, entry_point):
|
||||
|
||||
@@ -3,7 +3,10 @@ from galaxy.exceptions import (
|
||||
ObjectNotFound,
|
||||
)
|
||||
from galaxy.managers import base
|
||||
from galaxy.managers.context import ProvidesAppContext
|
||||
from galaxy.managers.context import (
|
||||
ProvidesAppContext,
|
||||
ProvidesUserContext,
|
||||
)
|
||||
from galaxy.schema.fields import DecodedDatabaseIdField
|
||||
from galaxy.schema.item_tags import ItemTagsCreatePayload
|
||||
from galaxy.structured_app import MinimalManagerApp
|
||||
@@ -16,13 +19,13 @@ class ItemTagsManager:
|
||||
self._app = app
|
||||
self._tag_handler = app.tag_handler
|
||||
|
||||
def index(self, trans: ProvidesAppContext, tagged_item_class: str, tagged_item_id: DecodedDatabaseIdField):
|
||||
def index(self, trans: ProvidesUserContext, tagged_item_class: str, tagged_item_id: DecodedDatabaseIdField):
|
||||
"""Displays a collection (list) of tags associated with an item."""
|
||||
tags = self._get_user_tags(trans, tagged_item_class, tagged_item_id)
|
||||
return [self._api_value(tag, trans, view="collection") for tag in tags]
|
||||
|
||||
def show(
|
||||
self, trans: ProvidesAppContext, tagged_item_class: str, tagged_item_id: DecodedDatabaseIdField, tag_name: str
|
||||
self, trans: ProvidesUserContext, tagged_item_class: str, tagged_item_id: DecodedDatabaseIdField, tag_name: str
|
||||
):
|
||||
"""Displays information about a tag associated with an item."""
|
||||
tag = self._get_item_tag_assoc(trans, tagged_item_class, tagged_item_id, tag_name)
|
||||
@@ -32,7 +35,7 @@ class ItemTagsManager:
|
||||
|
||||
def create(
|
||||
self,
|
||||
trans: ProvidesAppContext,
|
||||
trans: ProvidesUserContext,
|
||||
tagged_item_class: str,
|
||||
tagged_item_id: DecodedDatabaseIdField,
|
||||
tag_name: str,
|
||||
@@ -44,7 +47,7 @@ class ItemTagsManager:
|
||||
return self._api_value(tag, trans)
|
||||
|
||||
def delete(
|
||||
self, trans: ProvidesAppContext, tagged_item_class: str, tagged_item_id: DecodedDatabaseIdField, tag_name: str
|
||||
self, trans: ProvidesUserContext, tagged_item_class: str, tagged_item_id: DecodedDatabaseIdField, tag_name: str
|
||||
):
|
||||
"""Remove a tag from an item."""
|
||||
deleted = self._remove_items_tag(trans, tagged_item_class, tagged_item_id, tag_name)
|
||||
@@ -52,35 +55,35 @@ class ItemTagsManager:
|
||||
raise NoContentException("Failed to delete specified tag.")
|
||||
return deleted
|
||||
|
||||
def _get_tagged_item(self, trans, item_class_name, id, check_ownership=True):
|
||||
def _get_tagged_item(self, trans: ProvidesUserContext, item_class_name, id, check_ownership=True):
|
||||
tagged_item = base.get_object(
|
||||
trans, id, item_class_name, check_ownership=check_ownership, check_accessible=True
|
||||
)
|
||||
return tagged_item
|
||||
|
||||
def _get_user_tags(self, trans, item_class_name, id):
|
||||
def _get_user_tags(self, trans: ProvidesUserContext, item_class_name, id):
|
||||
user = trans.user
|
||||
tagged_item = self._get_tagged_item(trans, item_class_name, id)
|
||||
return [tag for tag in tagged_item.tags if tag.user == user]
|
||||
|
||||
def _remove_items_tag(self, trans, item_class_name, id, tag_name):
|
||||
def _remove_items_tag(self, trans: ProvidesUserContext, item_class_name, id, tag_name):
|
||||
user = trans.user
|
||||
tagged_item = self._get_tagged_item(trans, item_class_name, id)
|
||||
deleted = tagged_item and self._tag_handler.remove_item_tag(user, tagged_item, tag_name)
|
||||
trans.sa_session.commit()
|
||||
return deleted
|
||||
|
||||
def _apply_item_tag(self, trans, item_class_name, id, tag_name, tag_value=None):
|
||||
def _apply_item_tag(self, trans: ProvidesUserContext, item_class_name, id, tag_name, tag_value=None):
|
||||
user = trans.user
|
||||
tagged_item = self._get_tagged_item(trans, item_class_name, id)
|
||||
tag_assoc = self._tag_handler.apply_item_tag(user, tagged_item, tag_name, tag_value)
|
||||
trans.sa_session.commit()
|
||||
return tag_assoc
|
||||
|
||||
def _get_item_tag_assoc(self, trans, item_class_name, id, tag_name):
|
||||
def _get_item_tag_assoc(self, trans: ProvidesUserContext, item_class_name, id, tag_name):
|
||||
user = trans.user
|
||||
tagged_item = self._get_tagged_item(trans, item_class_name, id)
|
||||
return self._tag_handler._get_item_tag_assoc(user, tagged_item, tag_name)
|
||||
|
||||
def _api_value(self, tag, trans, view="element"):
|
||||
def _api_value(self, tag, trans: ProvidesAppContext, view="element"):
|
||||
return tag.to_dict(view=view, value_mapper={"id": trans.security.encode_id})
|
||||
|
||||
@@ -50,7 +50,11 @@ from galaxy.job_metrics import (
|
||||
Safety,
|
||||
)
|
||||
from galaxy.managers.collections import DatasetCollectionManager
|
||||
from galaxy.managers.context import ProvidesUserContext
|
||||
from galaxy.managers.context import (
|
||||
ProvidesAppContext,
|
||||
ProvidesHistoryContext,
|
||||
ProvidesUserContext,
|
||||
)
|
||||
from galaxy.managers.datasets import DatasetManager
|
||||
from galaxy.managers.hdas import (
|
||||
dereference_input_to_hda,
|
||||
@@ -384,7 +388,7 @@ class JobManager:
|
||||
return job.history is not None and self.history_manager.is_accessible(job.history, user)
|
||||
|
||||
def get_job_console_output(
|
||||
self, trans, job, stdout_position=-1, stdout_length=0, stderr_position=-1, stderr_length=0
|
||||
self, trans: ProvidesAppContext, job, stdout_position=-1, stdout_length=0, stderr_position=-1, stderr_length=0
|
||||
):
|
||||
if job is None:
|
||||
raise ObjectNotFound()
|
||||
@@ -1517,7 +1521,7 @@ class JobSearch:
|
||||
return stmt
|
||||
|
||||
|
||||
def view_show_job(trans, job: Job, full: bool) -> dict:
|
||||
def view_show_job(trans: ProvidesUserContext, job: Job, full: bool) -> dict:
|
||||
is_admin = trans.user_is_admin
|
||||
job_dict = job.to_dict("element", system_details=is_admin)
|
||||
if trans.app.config.expose_dataset_path and "command_line" not in job_dict:
|
||||
@@ -1900,7 +1904,7 @@ def summarize_jobs_to_dict(sa_session, jobs_source) -> JobsSummary | None:
|
||||
return rval
|
||||
|
||||
|
||||
def summarize_job_metrics(trans, job):
|
||||
def summarize_job_metrics(trans: ProvidesUserContext, job):
|
||||
"""Produce a dict-ified version of job metrics ready for tabular rendering.
|
||||
|
||||
Precondition: the caller has verified the job is accessible to the user
|
||||
@@ -1927,7 +1931,7 @@ def summarize_metrics(trans: ProvidesUserContext, job_metrics):
|
||||
return [d.dict() for d in dictifiable_metrics]
|
||||
|
||||
|
||||
def summarize_destination_params(trans, job):
|
||||
def summarize_destination_params(trans: ProvidesUserContext, job):
|
||||
"""Produce a dict-ified version of job destination parameters ready for tabular rendering.
|
||||
|
||||
Precondition: the caller has verified the job is accessible to the user
|
||||
@@ -1944,7 +1948,7 @@ def summarize_destination_params(trans, job):
|
||||
return destination_params
|
||||
|
||||
|
||||
def summarize_job_parameters(trans: ProvidesUserContext, job: Job) -> dict[str, Any]:
|
||||
def summarize_job_parameters(trans: ProvidesHistoryContext, job: Job) -> dict[str, Any]:
|
||||
"""Produce a dict-ified version of job parameters ready for tabular rendering.
|
||||
|
||||
Precondition: the caller has verified the job is accessible to the user
|
||||
|
||||
@@ -55,7 +55,10 @@ from galaxy.tool_util_models.parameters import (
|
||||
ToolParameterBundleModel,
|
||||
)
|
||||
from galaxy.util import safe_str_cmp
|
||||
from .context import ProvidesUserContext
|
||||
from .context import (
|
||||
ProvidesHistoryContext,
|
||||
ProvidesUserContext,
|
||||
)
|
||||
from .headers_encryption import (
|
||||
decrypt_headers_in_data,
|
||||
encrypt_headers_in_data,
|
||||
@@ -237,7 +240,7 @@ class LandingRequestManager:
|
||||
return self._tool_response(request)
|
||||
|
||||
def claim_workflow_landing_request(
|
||||
self, trans: ProvidesUserContext, uuid: UUID4, claim: ClaimLandingPayload | None
|
||||
self, trans: ProvidesHistoryContext, uuid: UUID4, claim: ClaimLandingPayload | None
|
||||
) -> WorkflowLandingRequest:
|
||||
request = self._get_workflow_landing_request(uuid)
|
||||
self._check_can_claim(trans, request, claim)
|
||||
@@ -246,7 +249,7 @@ class LandingRequestManager:
|
||||
self._save(request)
|
||||
return self._workflow_response(request)
|
||||
|
||||
def _ensure_workflow(self, trans: ProvidesUserContext, request: WorkflowLandingRequestModel):
|
||||
def _ensure_workflow(self, trans: ProvidesHistoryContext, request: WorkflowLandingRequestModel):
|
||||
if request.workflow_source_type == "trs_url" and isinstance(trans.app, StructuredApp):
|
||||
# trans is always structured app except for unit test
|
||||
assert request.workflow_source
|
||||
@@ -266,7 +269,7 @@ class LandingRequestManager:
|
||||
request = self._get_claimed_tool_landing_request(trans, uuid)
|
||||
return self._tool_response(request)
|
||||
|
||||
def get_workflow_landing_request(self, trans: ProvidesUserContext, uuid: UUID4) -> WorkflowLandingRequest:
|
||||
def get_workflow_landing_request(self, trans: ProvidesHistoryContext, uuid: UUID4) -> WorkflowLandingRequest:
|
||||
request = self._get_claimed_workflow_landing_request(trans, uuid)
|
||||
self._ensure_workflow(trans, request)
|
||||
return self._workflow_response(request)
|
||||
|
||||
@@ -4,6 +4,7 @@ from typing import (
|
||||
)
|
||||
|
||||
from galaxy.managers import base as manager_base
|
||||
from galaxy.managers.context import ProvidesUserContext
|
||||
from galaxy.managers.datasets import DatasetAssociationManager
|
||||
from galaxy.model import (
|
||||
LibraryDatasetDatasetAssociation,
|
||||
@@ -27,7 +28,7 @@ class LDDAManager(DatasetAssociationManager[LibraryDatasetDatasetAssociation]):
|
||||
"""
|
||||
super().__init__(app)
|
||||
|
||||
def get(self, trans, id: int, check_accessible=True) -> LibraryDatasetDatasetAssociation:
|
||||
def get(self, trans: ProvidesUserContext, id: int, check_accessible=True) -> LibraryDatasetDatasetAssociation:
|
||||
return manager_base.get_object(
|
||||
trans, id, "LibraryDatasetDatasetAssociation", check_ownership=False, check_accessible=check_accessible
|
||||
)
|
||||
@@ -41,7 +42,7 @@ class LDDAManager(DatasetAssociationManager[LibraryDatasetDatasetAssociation]):
|
||||
return True
|
||||
return item.user == user
|
||||
|
||||
def _set_permissions(self, trans, library_dataset, role_ids_dict):
|
||||
def _set_permissions(self, trans: ProvidesUserContext, library_dataset, role_ids_dict):
|
||||
# Check Git history for an older broken implementation, but it was broken
|
||||
# and security related and had not test coverage so it was deleted.
|
||||
raise NotImplementedError()
|
||||
|
||||
@@ -11,6 +11,10 @@ from sqlalchemy.exc import (
|
||||
from sqlalchemy.orm import Query
|
||||
|
||||
from galaxy import exceptions
|
||||
from galaxy.managers.context import (
|
||||
ProvidesAppContext,
|
||||
ProvidesUserContext,
|
||||
)
|
||||
from galaxy.managers.folders import FolderManager
|
||||
from galaxy.model import (
|
||||
Library,
|
||||
@@ -42,7 +46,7 @@ class LibraryManager:
|
||||
Interface/service object for interacting with libraries.
|
||||
"""
|
||||
|
||||
def get(self, trans, decoded_library_id: int, check_accessible: bool = True) -> Library:
|
||||
def get(self, trans: ProvidesUserContext, decoded_library_id: int, check_accessible: bool = True) -> Library:
|
||||
"""
|
||||
Get the library from the DB.
|
||||
|
||||
@@ -65,7 +69,9 @@ class LibraryManager:
|
||||
library = self.secure(trans, library, check_accessible)
|
||||
return library
|
||||
|
||||
def create(self, trans, name: str, description: str | None = "", synopsis: str | None = "") -> Library:
|
||||
def create(
|
||||
self, trans: ProvidesUserContext, name: str, description: str | None = "", synopsis: str | None = ""
|
||||
) -> Library:
|
||||
"""
|
||||
Create a new library.
|
||||
"""
|
||||
@@ -81,7 +87,7 @@ class LibraryManager:
|
||||
|
||||
def update(
|
||||
self,
|
||||
trans,
|
||||
trans: ProvidesUserContext,
|
||||
library: Library,
|
||||
name: str | None = None,
|
||||
description: str | None = None,
|
||||
@@ -116,7 +122,7 @@ class LibraryManager:
|
||||
trans.sa_session.commit()
|
||||
return library
|
||||
|
||||
def delete(self, trans, library: Library, undelete: bool | None = False) -> Library:
|
||||
def delete(self, trans: ProvidesUserContext, library: Library, undelete: bool | None = False) -> Library:
|
||||
"""
|
||||
Mark given library deleted/undeleted based on the flag.
|
||||
"""
|
||||
@@ -130,7 +136,7 @@ class LibraryManager:
|
||||
trans.sa_session.commit()
|
||||
return library
|
||||
|
||||
def list(self, trans, deleted: bool | None = False) -> tuple[Query, dict[str, set]]:
|
||||
def list(self, trans: ProvidesUserContext, deleted: bool | None = False) -> tuple[Query, dict[str, set]]:
|
||||
"""
|
||||
Return a list of libraries from the DB.
|
||||
|
||||
@@ -182,7 +188,7 @@ class LibraryManager:
|
||||
|
||||
return libraries, prefetched_ids
|
||||
|
||||
def secure(self, trans, library: Library, check_accessible: bool = True) -> Library:
|
||||
def secure(self, trans: ProvidesUserContext, library: Library, check_accessible: bool = True) -> Library:
|
||||
"""
|
||||
Check if library is accessible to user.
|
||||
|
||||
@@ -201,7 +207,7 @@ class LibraryManager:
|
||||
library = self.check_accessible(trans, library)
|
||||
return library
|
||||
|
||||
def check_accessible(self, trans, library: Library) -> Library:
|
||||
def check_accessible(self, trans: ProvidesUserContext, library: Library) -> Library:
|
||||
"""
|
||||
Check whether the library is accessible to current user.
|
||||
"""
|
||||
@@ -212,7 +218,9 @@ class LibraryManager:
|
||||
else:
|
||||
return library
|
||||
|
||||
def get_library_dict(self, trans, library: Library, prefetched_ids: dict[str, set] | None = None) -> dict:
|
||||
def get_library_dict(
|
||||
self, trans: ProvidesUserContext, library: Library, prefetched_ids: dict[str, set] | None = None
|
||||
) -> dict:
|
||||
"""
|
||||
Return library data in the form of a dictionary.
|
||||
|
||||
@@ -262,7 +270,7 @@ class LibraryManager:
|
||||
library_dict["can_user_manage"] = True
|
||||
return library_dict
|
||||
|
||||
def get_current_roles(self, trans, library: Library) -> dict:
|
||||
def get_current_roles(self, trans: ProvidesAppContext, library: Library) -> dict:
|
||||
"""
|
||||
Load all permissions currently related to the given library.
|
||||
|
||||
@@ -286,13 +294,13 @@ class LibraryManager:
|
||||
add_library_item_role_list=role_name_id_pairs(add_roles, private_role_emails, encode_id),
|
||||
)
|
||||
|
||||
def get_access_roles(self, trans, library: Library) -> set[Role]:
|
||||
def get_access_roles(self, trans: ProvidesAppContext, library: Library) -> set[Role]:
|
||||
"""
|
||||
Load access roles for all library permissions
|
||||
"""
|
||||
return set(library.get_access_roles(trans.app.security_agent))
|
||||
|
||||
def get_modify_roles(self, trans, library: Library) -> set[Role]:
|
||||
def get_modify_roles(self, trans: ProvidesAppContext, library: Library) -> set[Role]:
|
||||
"""
|
||||
Load modify roles for all library permissions
|
||||
"""
|
||||
@@ -302,7 +310,7 @@ class LibraryManager:
|
||||
)
|
||||
)
|
||||
|
||||
def get_manage_roles(self, trans, library: Library) -> set[Role]:
|
||||
def get_manage_roles(self, trans: ProvidesAppContext, library: Library) -> set[Role]:
|
||||
"""
|
||||
Load manage roles for all library permissions
|
||||
"""
|
||||
@@ -312,7 +320,7 @@ class LibraryManager:
|
||||
)
|
||||
)
|
||||
|
||||
def get_add_roles(self, trans, library: Library) -> set[Role]:
|
||||
def get_add_roles(self, trans: ProvidesAppContext, library: Library) -> set[Role]:
|
||||
"""
|
||||
Load add roles for all library permissions
|
||||
"""
|
||||
@@ -322,21 +330,21 @@ class LibraryManager:
|
||||
)
|
||||
)
|
||||
|
||||
def make_public(self, trans, library: Library) -> bool:
|
||||
def make_public(self, trans: ProvidesAppContext, library: Library) -> bool:
|
||||
"""
|
||||
Makes the given library public (removes all access roles)
|
||||
"""
|
||||
trans.app.security_agent.make_library_public(library)
|
||||
return self.is_public(trans, library)
|
||||
|
||||
def is_public(self, trans, library: Library) -> bool:
|
||||
def is_public(self, trans: ProvidesAppContext, library: Library) -> bool:
|
||||
"""
|
||||
Return true if lib is public.
|
||||
"""
|
||||
return trans.app.security_agent.library_is_public(library)
|
||||
|
||||
|
||||
def get_containing_library_from_library_dataset(trans, library_dataset) -> Library | None:
|
||||
def get_containing_library_from_library_dataset(trans: ProvidesAppContext, library_dataset) -> Library | None:
|
||||
"""Given a library_dataset, get the containing library"""
|
||||
folder = library_dataset.folder
|
||||
while folder.parent:
|
||||
|
||||
@@ -15,7 +15,11 @@ from galaxy.exceptions import (
|
||||
RequestParameterInvalidException,
|
||||
)
|
||||
from galaxy.managers.base import ModelManager
|
||||
from galaxy.managers.context import ProvidesUserContext
|
||||
from galaxy.managers.context import (
|
||||
ProvidesAppContext,
|
||||
ProvidesHistoryContext,
|
||||
ProvidesUserContext,
|
||||
)
|
||||
from galaxy.managers.lddas import LDDAManager
|
||||
from galaxy.model import (
|
||||
LibraryDataset,
|
||||
@@ -37,7 +41,7 @@ class LibraryDatasetsManager(ModelManager[LibraryDataset]):
|
||||
super().__init__(app)
|
||||
self.ldda_manager = LDDAManager(app)
|
||||
|
||||
def get(self, trans, decoded_library_dataset_id, check_accessible=True) -> LibraryDataset:
|
||||
def get(self, trans: ProvidesUserContext, decoded_library_dataset_id, check_accessible=True) -> LibraryDataset:
|
||||
"""
|
||||
Get the library dataset from the DB.
|
||||
|
||||
@@ -90,7 +94,7 @@ class LibraryDatasetsManager(ModelManager[LibraryDataset]):
|
||||
|
||||
def _set_from_dict(
|
||||
self,
|
||||
trans: ProvidesUserContext,
|
||||
trans: ProvidesHistoryContext,
|
||||
ldda: LibraryDatasetDatasetAssociation,
|
||||
new_data: dict[str, Any],
|
||||
flush: bool = True,
|
||||
@@ -166,7 +170,7 @@ class LibraryDatasetsManager(ModelManager[LibraryDataset]):
|
||||
validated_payload[key] = val
|
||||
return validated_payload
|
||||
|
||||
def secure(self, trans, ld, check_accessible=True, check_ownership=False):
|
||||
def secure(self, trans: ProvidesUserContext, ld, check_accessible=True, check_ownership=False):
|
||||
"""
|
||||
Check if library dataset is accessible to current user or the user is an admin.
|
||||
|
||||
@@ -185,7 +189,7 @@ class LibraryDatasetsManager(ModelManager[LibraryDataset]):
|
||||
ld = self.check_accessible(trans, ld)
|
||||
return ld
|
||||
|
||||
def check_accessible(self, trans, ld):
|
||||
def check_accessible(self, trans: ProvidesUserContext, ld):
|
||||
"""
|
||||
Check whether the current user has permissions to access library dataset.
|
||||
|
||||
@@ -204,7 +208,7 @@ class LibraryDatasetsManager(ModelManager[LibraryDataset]):
|
||||
else:
|
||||
return ld
|
||||
|
||||
def check_modifiable(self, trans, ld):
|
||||
def check_modifiable(self, trans: ProvidesUserContext, ld):
|
||||
"""
|
||||
Check whether the current user has permissions to modify library dataset.
|
||||
|
||||
@@ -225,7 +229,7 @@ class LibraryDatasetsManager(ModelManager[LibraryDataset]):
|
||||
else:
|
||||
return ld
|
||||
|
||||
def serialize(self, trans, ld: LibraryDataset) -> dict[str, Any]:
|
||||
def serialize(self, trans: ProvidesUserContext, ld: LibraryDataset) -> dict[str, Any]:
|
||||
"""Serialize the library dataset into a dictionary."""
|
||||
current_user_roles = trans.get_current_user_roles()
|
||||
|
||||
@@ -271,7 +275,7 @@ class LibraryDatasetsManager(ModelManager[LibraryDataset]):
|
||||
)
|
||||
return rval
|
||||
|
||||
def _build_path(self, trans, folder):
|
||||
def _build_path(self, trans: ProvidesAppContext, folder):
|
||||
"""
|
||||
Search the path upwards recursively and load the whole route of
|
||||
names and ids for breadcrumb building purposes.
|
||||
|
||||
@@ -20,9 +20,7 @@ import shutil
|
||||
import tempfile
|
||||
from datetime import datetime
|
||||
from re import Match
|
||||
from typing import (
|
||||
Any,
|
||||
)
|
||||
from typing import Any
|
||||
|
||||
import markdown
|
||||
|
||||
@@ -37,6 +35,11 @@ from galaxy.exceptions import (
|
||||
ObjectNotFound,
|
||||
ServerNotConfiguredForRequest,
|
||||
)
|
||||
from galaxy.managers.context import (
|
||||
ProvidesAppContext,
|
||||
ProvidesHistoryContext,
|
||||
ProvidesUserContext,
|
||||
)
|
||||
from galaxy.managers.jobs import (
|
||||
JobManager,
|
||||
summarize_job_metrics,
|
||||
@@ -107,7 +110,7 @@ def process_invocation_ids(f, workflow_markdown: str) -> str:
|
||||
return re.sub(VISUALIZATION_FENCED_BLOCK, process_block, workflow_markdown)
|
||||
|
||||
|
||||
def ready_galaxy_markdown_for_import(trans, external_galaxy_markdown):
|
||||
def ready_galaxy_markdown_for_import(trans: ProvidesAppContext, external_galaxy_markdown):
|
||||
"""Convert from encoded IDs to decoded numeric IDs for storing in the DB."""
|
||||
|
||||
_validate(external_galaxy_markdown, internal=False)
|
||||
@@ -138,11 +141,12 @@ def ready_galaxy_markdown_for_import(trans, external_galaxy_markdown):
|
||||
|
||||
|
||||
class GalaxyInternalMarkdownDirectiveHandler(metaclass=abc.ABCMeta):
|
||||
def walk(self, trans, internal_galaxy_markdown):
|
||||
def walk(self, trans: ProvidesHistoryContext, internal_galaxy_markdown):
|
||||
hda_manager = trans.app.hda_manager
|
||||
history_manager = trans.app.history_manager
|
||||
workflow_manager = trans.app.workflow_manager
|
||||
job_manager = JobManager(trans.app, history_manager)
|
||||
# not trans.app.job_manager, which is the job queue manager of the same name
|
||||
job_manager = trans.app[JobManager]
|
||||
collection_manager = trans.app.dataset_collection_manager
|
||||
|
||||
def _job_for_job_directive(object_type, object_id):
|
||||
@@ -325,10 +329,12 @@ class GalaxyInternalMarkdownDirectiveHandler(metaclass=abc.ABCMeta):
|
||||
|
||||
if container == "history_dataset_type":
|
||||
_check_object(object_id, match.group(0))
|
||||
assert object_id is not None
|
||||
hda = hda_manager.get_accessible(object_id, trans.user)
|
||||
return hda.extension or "data"
|
||||
elif container == "history_dataset_name":
|
||||
_check_object(object_id, match.group(0))
|
||||
assert object_id is not None
|
||||
hda = hda_manager.get_accessible(object_id, trans.user)
|
||||
return hda.name or ""
|
||||
elif container == "workflow_license":
|
||||
@@ -337,6 +343,7 @@ class GalaxyInternalMarkdownDirectiveHandler(metaclass=abc.ABCMeta):
|
||||
return _workflow_license_as_simple_markdown(stored_workflow)
|
||||
elif container == "invocation_time":
|
||||
_check_object(object_id, match.group(0))
|
||||
assert object_id is not None
|
||||
invocation = workflow_manager.get_invocation(trans, object_id)
|
||||
return _database_time_to_str(invocation.create_time)
|
||||
elif container == "generate_time":
|
||||
@@ -368,6 +375,7 @@ class GalaxyInternalMarkdownDirectiveHandler(metaclass=abc.ABCMeta):
|
||||
return _link_to_markdown(url, title)
|
||||
elif container == "history_dataset_as_image":
|
||||
_check_object(object_id, match.group(0))
|
||||
assert object_id is not None
|
||||
hda = hda_manager.get_accessible(object_id, trans.user)
|
||||
return f""
|
||||
else:
|
||||
@@ -394,7 +402,7 @@ class GalaxyInternalMarkdownDirectiveHandler(metaclass=abc.ABCMeta):
|
||||
)
|
||||
return export_markdown, export_markdown_embed_expanded
|
||||
|
||||
def _encode_line(self, trans, line):
|
||||
def _encode_line(self, trans: ProvidesAppContext, line):
|
||||
object_type = None
|
||||
object_id = None
|
||||
encoded_id = None
|
||||
@@ -531,7 +539,7 @@ class GalaxyInternalMarkdownDirectiveHandler(metaclass=abc.ABCMeta):
|
||||
|
||||
|
||||
class ReadyForExportMarkdownDirectiveHandler(GalaxyInternalMarkdownDirectiveHandler):
|
||||
def __init__(self, trans, extra_rendering_data=None):
|
||||
def __init__(self, trans: ProvidesHistoryContext, extra_rendering_data=None):
|
||||
extra_rendering_data = extra_rendering_data if extra_rendering_data is not None else {}
|
||||
self.trans = trans
|
||||
self.extra_rendering_data = extra_rendering_data
|
||||
@@ -646,7 +654,7 @@ class ReadyForExportMarkdownDirectiveHandler(GalaxyInternalMarkdownDirectiveHand
|
||||
return (line, False)
|
||||
|
||||
|
||||
def ready_galaxy_markdown_for_export(trans, internal_galaxy_markdown):
|
||||
def ready_galaxy_markdown_for_export(trans: ProvidesHistoryContext, internal_galaxy_markdown):
|
||||
"""Fill in details needed to render Galaxy flavored markdown.
|
||||
|
||||
Take it from a minimal internal version to an externally render-able version
|
||||
@@ -668,7 +676,7 @@ def ready_galaxy_markdown_for_export(trans, internal_galaxy_markdown):
|
||||
|
||||
|
||||
class ToBasicMarkdownDirectiveHandler(GalaxyInternalMarkdownDirectiveHandler):
|
||||
def __init__(self, trans):
|
||||
def __init__(self, trans: ProvidesHistoryContext):
|
||||
self.trans = trans
|
||||
|
||||
def _format_printable_time(self, time):
|
||||
@@ -918,7 +926,7 @@ class ToBasicMarkdownDirectiveHandler(GalaxyInternalMarkdownDirectiveHandler):
|
||||
return (line, False)
|
||||
|
||||
|
||||
def to_basic_markdown(trans, internal_galaxy_markdown: str) -> str:
|
||||
def to_basic_markdown(trans: ProvidesHistoryContext, internal_galaxy_markdown: str) -> str:
|
||||
"""Replace Galaxy Markdown extensions with plain Markdown for PDF/HTML export."""
|
||||
directive_handler = ToBasicMarkdownDirectiveHandler(trans)
|
||||
resolved_invocations_markdown = resolve_invocation_markdown(trans, internal_galaxy_markdown)
|
||||
@@ -964,7 +972,9 @@ def _check_can_convert_to_pdf_or_raise():
|
||||
raise ServerNotConfiguredForRequest("PDF conversion service not available.")
|
||||
|
||||
|
||||
def internal_galaxy_markdown_to_pdf(trans, internal_galaxy_markdown: str, document_type: PdfDocumentType) -> bytes:
|
||||
def internal_galaxy_markdown_to_pdf(
|
||||
trans: ProvidesHistoryContext, internal_galaxy_markdown: str, document_type: PdfDocumentType
|
||||
) -> bytes:
|
||||
_check_can_convert_to_pdf_or_raise()
|
||||
basic_markdown = to_basic_markdown(trans, internal_galaxy_markdown)
|
||||
config = trans.app.config
|
||||
@@ -1002,7 +1012,7 @@ def to_branded_pdf(basic_markdown: str, document_type: PdfDocumentType, config:
|
||||
return to_pdf_raw(branded_markdown, css_paths=css_paths)
|
||||
|
||||
|
||||
def populate_invocation_markdown(trans, invocation, workflow_markdown):
|
||||
def populate_invocation_markdown(trans: ProvidesHistoryContext, invocation, workflow_markdown):
|
||||
"""
|
||||
Resolve invocation objects to convert markdown to 'internal' representation.
|
||||
|
||||
@@ -1074,7 +1084,7 @@ def populate_invocation_markdown(trans, invocation, workflow_markdown):
|
||||
return galaxy_markdown
|
||||
|
||||
|
||||
def resolve_invocation_markdown(trans, workflow_markdown):
|
||||
def resolve_invocation_markdown(trans: ProvidesUserContext, workflow_markdown):
|
||||
"""Resolve invocation objects to convert markdown to 'internal' representation.
|
||||
|
||||
Replace references to abstract workflow parts with actual galaxy object IDs corresponding
|
||||
@@ -1089,7 +1099,7 @@ def resolve_invocation_markdown(trans, workflow_markdown):
|
||||
Hopefully this list will be expanded to include invocation_qc and step_output.
|
||||
"""
|
||||
|
||||
def get_invocation(trans, line):
|
||||
def get_invocation(trans: ProvidesUserContext, line):
|
||||
workflow_manager = trans.app.workflow_manager
|
||||
if invocation_id_match := re.search(INVOCATION_ID_PATTERN, line):
|
||||
invocation_id = int(invocation_id_match.group(1))
|
||||
@@ -1249,7 +1259,7 @@ def resolve_invocation_markdown(trans, workflow_markdown):
|
||||
return workflow_markdown
|
||||
|
||||
|
||||
def resolve_job_markdown(trans, job, job_markdown):
|
||||
def resolve_job_markdown(trans: ProvidesHistoryContext, job, job_markdown):
|
||||
"""Resolve job objects to convert tool markdown to 'internal' representation.
|
||||
|
||||
Replace references to abstract workflow parts with actual galaxy object IDs corresponding
|
||||
|
||||
@@ -332,7 +332,9 @@ class ObjectStoreInstancesManager:
|
||||
template = catalog.find_template_by(persisted_object_store.template_id, target_template_version)
|
||||
return template
|
||||
|
||||
def _to_model(self, trans, persisted_object_store: UserObjectStore) -> UserConcreteObjectStoreModel:
|
||||
def _to_model(
|
||||
self, trans: ProvidesUserContext, persisted_object_store: UserObjectStore
|
||||
) -> UserConcreteObjectStoreModel:
|
||||
quota = QuotaModel(source=None, enabled=False)
|
||||
object_store_type = persisted_object_store.template.configuration.type
|
||||
admin_badges = persisted_object_store.template.configuration.badges or []
|
||||
|
||||
@@ -34,6 +34,7 @@ from galaxy.managers import (
|
||||
sharable,
|
||||
)
|
||||
from galaxy.managers.context import (
|
||||
ProvidesAppContext,
|
||||
ProvidesHistoryContext,
|
||||
ProvidesUserContext,
|
||||
)
|
||||
@@ -269,7 +270,7 @@ class PageManager(sharable.SharableModelManager[model.Page], UsesAnnotations):
|
||||
stmt = stmt.offset(payload.offset)
|
||||
return trans.sa_session.scalars(stmt), total_matches
|
||||
|
||||
def create_page(self, trans, payload: CreatePagePayload):
|
||||
def create_page(self, trans: ProvidesUserContext, payload: CreatePagePayload):
|
||||
user = trans.get_user()
|
||||
if not user:
|
||||
raise exceptions.AuthenticationRequired("You must be logged in to create pages.")
|
||||
@@ -341,7 +342,7 @@ class PageManager(sharable.SharableModelManager[model.Page], UsesAnnotations):
|
||||
session.commit()
|
||||
return page
|
||||
|
||||
def update_page(self, trans, id: int, payload: UpdatePagePayload):
|
||||
def update_page(self, trans: ProvidesUserContext, id: int, payload: UpdatePagePayload):
|
||||
user = trans.get_user()
|
||||
if not user:
|
||||
raise exceptions.AuthenticationRequired("You must be logged in to update pages.")
|
||||
@@ -386,7 +387,7 @@ class PageManager(sharable.SharableModelManager[model.Page], UsesAnnotations):
|
||||
session.commit()
|
||||
return page
|
||||
|
||||
def save_new_revision(self, trans, page, payload):
|
||||
def save_new_revision(self, trans: ProvidesAppContext, page, payload):
|
||||
# Assumes security has already been checked by caller.
|
||||
content = payload.get("content", None)
|
||||
content_format = payload.get("content_format", None)
|
||||
@@ -420,18 +421,18 @@ class PageManager(sharable.SharableModelManager[model.Page], UsesAnnotations):
|
||||
session.commit()
|
||||
return page_revision
|
||||
|
||||
def list_revisions(self, trans, page, sort_desc: bool = False):
|
||||
def list_revisions(self, trans: ProvidesUserContext, page, sort_desc: bool = False):
|
||||
page = base.security_check(trans, page, check_ownership=False, check_accessible=True)
|
||||
return sorted(page.revisions, key=lambda r: r.create_time, reverse=sort_desc)
|
||||
|
||||
def get_revision(self, trans, page, revision_id):
|
||||
def get_revision(self, trans: ProvidesUserContext, page, revision_id):
|
||||
page = base.security_check(trans, page, check_ownership=False, check_accessible=True)
|
||||
revision = trans.sa_session.get(model.PageRevision, revision_id)
|
||||
if not revision or revision.page_id != page.id:
|
||||
raise exceptions.ObjectNotFound("Page revision not found")
|
||||
return revision
|
||||
|
||||
def restore_revision(self, trans, page, revision_id):
|
||||
def restore_revision(self, trans: ProvidesUserContext, page, revision_id):
|
||||
page = base.security_check(trans, page, check_ownership=True, check_accessible=True)
|
||||
old_revision = self.get_revision(trans, page, revision_id)
|
||||
# Build revision directly — content is already in internal format
|
||||
@@ -446,7 +447,7 @@ class PageManager(sharable.SharableModelManager[model.Page], UsesAnnotations):
|
||||
trans.sa_session.commit()
|
||||
return page_revision
|
||||
|
||||
def rewrite_content_for_import(self, trans, content, content_format: str):
|
||||
def rewrite_content_for_import(self, trans: ProvidesAppContext, content, content_format: str):
|
||||
if content_format == PageContentFormat.html.value:
|
||||
try:
|
||||
content = sanitize_html(content)
|
||||
@@ -466,7 +467,7 @@ class PageManager(sharable.SharableModelManager[model.Page], UsesAnnotations):
|
||||
)
|
||||
return content
|
||||
|
||||
def rewrite_content_for_export(self, trans, as_dict):
|
||||
def rewrite_content_for_export(self, trans: ProvidesHistoryContext, as_dict):
|
||||
content = as_dict["content"]
|
||||
content_format = as_dict.get("content_format", PageContentFormat.html.value)
|
||||
if content_format == PageContentFormat.html.value:
|
||||
@@ -554,7 +555,7 @@ class PageContentProcessor(HTMLParser):
|
||||
"wbr",
|
||||
}
|
||||
|
||||
def __init__(self, trans, render_embed_html_fn: Callable):
|
||||
def __init__(self, trans: ProvidesAppContext, render_embed_html_fn: Callable):
|
||||
HTMLParser.__init__(self)
|
||||
self.trans = trans
|
||||
self.ignore_content = False
|
||||
|
||||
@@ -20,6 +20,7 @@ from galaxy import (
|
||||
)
|
||||
from galaxy.exceptions import ActionInputError
|
||||
from galaxy.managers import base
|
||||
from galaxy.managers.context import ProvidesUserContext
|
||||
from galaxy.model import (
|
||||
Group,
|
||||
Quota,
|
||||
@@ -275,5 +276,5 @@ class QuotaManager:
|
||||
message += ", ".join(names)
|
||||
return message
|
||||
|
||||
def get_quota(self, trans, id: int, deleted: bool | None = None) -> model.Quota:
|
||||
def get_quota(self, trans: ProvidesUserContext, id: int, deleted: bool | None = None) -> model.Quota:
|
||||
return base.get_object(trans, id, "Quota", check_ownership=False, check_accessible=False, deleted=deleted)
|
||||
|
||||
@@ -37,6 +37,7 @@ from galaxy.managers import (
|
||||
users,
|
||||
)
|
||||
from galaxy.managers.base import combine_lists
|
||||
from galaxy.managers.context import ProvidesUserContext
|
||||
from galaxy.model import (
|
||||
User,
|
||||
UserShareAssociation,
|
||||
@@ -244,7 +245,7 @@ class SharableModelManager(
|
||||
return list(self._apply_fn_limit_offset_gen(items, limit, offset))
|
||||
|
||||
def get_sharing_extra_information(
|
||||
self, trans, item, users: set[User], errors: set[str], option: SharingOptions | None = None
|
||||
self, trans: ProvidesUserContext, item, users: set[User], errors: set[str], option: SharingOptions | None = None
|
||||
) -> ShareWithExtra | None:
|
||||
"""Returns optional extra information about the shareability of the given item.
|
||||
|
||||
@@ -252,7 +253,7 @@ class SharableModelManager(
|
||||
to provide the extra information, otherwise, it will be None by default."""
|
||||
return None
|
||||
|
||||
def make_members_public(self, trans, item):
|
||||
def make_members_public(self, trans: ProvidesUserContext, item):
|
||||
"""Make potential elements of this item public.
|
||||
|
||||
This method must be overridden in managers that need to change permissions of internal elements
|
||||
|
||||
@@ -72,7 +72,7 @@ class ToolDataManager:
|
||||
data_table.reload_from_files()
|
||||
return self._reload_data_table(table_name)
|
||||
|
||||
def get_field_file_path(self, trans, table_name: str, field_name: str, file_name: str) -> Path:
|
||||
def get_field_file_path(self, trans: ProvidesUserContext, table_name: str, field_name: str, file_name: str) -> Path:
|
||||
"""Get the absolute path to a given file name in the table field"""
|
||||
field_value = self._data_table_field(table_name, field_name)
|
||||
if table_name not in PUBLIC_TABLES and not trans.user_is_admin:
|
||||
|
||||
@@ -9,7 +9,7 @@ from galaxy.exceptions import (
|
||||
ObjectNotFound,
|
||||
RequestParameterInvalidException,
|
||||
)
|
||||
from galaxy.managers.context import ProvidesAppContext
|
||||
from galaxy.managers.context import ProvidesHistoryContext
|
||||
from galaxy.schema.schema import GenerateTourResponse
|
||||
from galaxy.schema.tours import (
|
||||
TourDetails,
|
||||
@@ -27,7 +27,7 @@ class ToursManager:
|
||||
self._app = app
|
||||
|
||||
def generate_tour(
|
||||
self, tool_id: str, tool_version: str, trans: ProvidesAppContext, performs_upload=True
|
||||
self, tool_id: str, tool_version: str, trans: ProvidesHistoryContext, performs_upload=True
|
||||
) -> GenerateTourResponse:
|
||||
"""
|
||||
Generate a tour designed for the given tool.
|
||||
@@ -51,7 +51,7 @@ class ToursManager:
|
||||
|
||||
|
||||
class TourGenerator:
|
||||
def __init__(self, trans: ProvidesAppContext, tool_id: str, tool_version: str, performs_upload=True) -> None:
|
||||
def __init__(self, trans: ProvidesHistoryContext, tool_id: str, tool_version: str, performs_upload=True) -> None:
|
||||
self._trans = trans
|
||||
self._tool: Tool = self._get_and_ensure_tool(tool_id, tool_version)
|
||||
self._use_datasets = True
|
||||
|
||||
@@ -9,6 +9,7 @@ import string
|
||||
import time
|
||||
from typing import (
|
||||
Any,
|
||||
TYPE_CHECKING,
|
||||
)
|
||||
|
||||
from markupsafe import escape
|
||||
@@ -32,6 +33,11 @@ from galaxy.managers import (
|
||||
deletable,
|
||||
)
|
||||
from galaxy.managers.base import combine_lists
|
||||
from galaxy.managers.context import (
|
||||
ProvidesAppContext,
|
||||
ProvidesHistoryContext,
|
||||
ProvidesUserContext,
|
||||
)
|
||||
from galaxy.model import (
|
||||
Job,
|
||||
User,
|
||||
@@ -57,6 +63,9 @@ from galaxy.structured_app import (
|
||||
from galaxy.util import now
|
||||
from galaxy.util.hash_util import new_secure_hash_v2
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from galaxy.webapps.base.webapp import GalaxyWebTransaction
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
PASSWORD_RESET_TEMPLATE = """
|
||||
@@ -88,7 +97,9 @@ class UserManager(base.ModelManager, deletable.PurgableManagerMixin):
|
||||
self.app_type = app_type
|
||||
super().__init__(app)
|
||||
|
||||
def register(self, trans, email=None, username=None, password=None, confirm=None, subscribe=False):
|
||||
def register(
|
||||
self, trans: "GalaxyWebTransaction", email=None, username=None, password=None, confirm=None, subscribe=False
|
||||
):
|
||||
"""
|
||||
Register a new user.
|
||||
"""
|
||||
@@ -164,7 +175,13 @@ class UserManager(base.ModelManager, deletable.PurgableManagerMixin):
|
||||
return user
|
||||
|
||||
def update_email(
|
||||
self, trans, user: User, new_email: str, *, commit: bool = True, send_activation_email: bool = True
|
||||
self,
|
||||
trans: ProvidesAppContext,
|
||||
user: User,
|
||||
new_email: str,
|
||||
*,
|
||||
commit: bool = True,
|
||||
send_activation_email: bool = True,
|
||||
) -> None:
|
||||
"""
|
||||
Update a user's email address, keeping the private role in sync and honoring activation settings.
|
||||
@@ -192,7 +209,7 @@ class UserManager(base.ModelManager, deletable.PurgableManagerMixin):
|
||||
if commit:
|
||||
session.commit()
|
||||
|
||||
def update_username(self, trans, user: User, new_username: str, *, commit: bool = True) -> None:
|
||||
def update_username(self, trans: ProvidesAppContext, user: User, new_username: str, *, commit: bool = True) -> None:
|
||||
"""
|
||||
Update a user's public name after validating it. Raises RequestParameterInvalidException on validation errors.
|
||||
"""
|
||||
@@ -378,7 +395,7 @@ class UserManager(base.ModelManager, deletable.PurgableManagerMixin):
|
||||
return util.safe_str_cmp(bootstrap_hash, provided_hash)
|
||||
|
||||
# ---- admin
|
||||
def is_admin(self, user: model.User | None, trans=None) -> bool:
|
||||
def is_admin(self, user: model.User | None, trans: ProvidesUserContext | None = None) -> bool:
|
||||
"""Return True if this user is an admin (or session is authenticated as admin).
|
||||
|
||||
Do not pass trans to simply check if an existing user object is an admin user,
|
||||
@@ -387,7 +404,7 @@ class UserManager(base.ModelManager, deletable.PurgableManagerMixin):
|
||||
if user is None:
|
||||
# Anonymous session or master_api_key used, if master_api_key is detected
|
||||
# return True.
|
||||
return trans and trans.user_is_admin
|
||||
return bool(trans and trans.user_is_admin)
|
||||
return self.app.config.is_admin_user(user)
|
||||
|
||||
def admins(self, filters=None, **kwargs):
|
||||
@@ -440,7 +457,7 @@ class UserManager(base.ModelManager, deletable.PurgableManagerMixin):
|
||||
return user
|
||||
|
||||
# ---- current
|
||||
def current_user(self, trans):
|
||||
def current_user(self, trans: ProvidesUserContext):
|
||||
# define here for single point of change and make more readable
|
||||
# TODO: trans
|
||||
return trans.user
|
||||
@@ -508,7 +525,7 @@ class UserManager(base.ModelManager, deletable.PurgableManagerMixin):
|
||||
else:
|
||||
return user, "User not found."
|
||||
|
||||
def __set_password(self, trans, user, password, confirm):
|
||||
def __set_password(self, trans: ProvidesUserContext, user, password, confirm):
|
||||
if not password:
|
||||
return "Please provide a new password."
|
||||
if user:
|
||||
@@ -537,14 +554,14 @@ class UserManager(base.ModelManager, deletable.PurgableManagerMixin):
|
||||
else:
|
||||
return "Failed to determine user, access denied."
|
||||
|
||||
def impersonate(self, trans, user):
|
||||
def impersonate(self, trans: "GalaxyWebTransaction", user):
|
||||
if not trans.app.config.allow_user_impersonation:
|
||||
raise exceptions.Message("User impersonation is not enabled in this instance of Galaxy.")
|
||||
raise exceptions.MessageException("User impersonation is not enabled in this instance of Galaxy.")
|
||||
if user:
|
||||
trans.handle_user_logout()
|
||||
trans.handle_user_login(user)
|
||||
else:
|
||||
raise exceptions.Message("Please provide a valid user.")
|
||||
raise exceptions.MessageException("Please provide a valid user.")
|
||||
|
||||
def send_activation_email(self, trans, email, username):
|
||||
"""
|
||||
@@ -582,7 +599,7 @@ class UserManager(base.ModelManager, deletable.PurgableManagerMixin):
|
||||
log.exception("Unable to send the activation email.")
|
||||
return False
|
||||
|
||||
def __get_activation_token(self, trans, email):
|
||||
def __get_activation_token(self, trans: ProvidesAppContext, email):
|
||||
"""
|
||||
Check for the activation token. Create new activation token and store it in the database if no token found.
|
||||
Flushes but does not commit—the caller is responsible for committing the transaction.
|
||||
@@ -591,6 +608,7 @@ class UserManager(base.ModelManager, deletable.PurgableManagerMixin):
|
||||
# Flush pending changes so the user is visible to the DB query below.
|
||||
session.flush()
|
||||
user = get_user_by_email(session, email, self.app.model.User)
|
||||
assert user is not None, f"User with email '{email}' not found while generating activation token."
|
||||
activation_token = user.activation_token
|
||||
if activation_token is None:
|
||||
activation_token = util.hash_util.new_secure_hash_v2(str(random.getrandbits(256)))
|
||||
@@ -599,7 +617,7 @@ class UserManager(base.ModelManager, deletable.PurgableManagerMixin):
|
||||
session.flush()
|
||||
return activation_token
|
||||
|
||||
def send_reset_email(self, trans, payload, **kwd):
|
||||
def send_reset_email(self, trans: "GalaxyWebTransaction", payload, **kwd):
|
||||
"""Reset the user's password. Send an email with token that allows a password change."""
|
||||
if self.app.config.smtp_server is None:
|
||||
return "Mail is not configured for this Galaxy instance and password reset information cannot be sent. Please contact your local Galaxy administrator."
|
||||
@@ -632,7 +650,7 @@ class UserManager(base.ModelManager, deletable.PurgableManagerMixin):
|
||||
log.warning(f"Failed to produce password reset token. User with email '{email}' not found.")
|
||||
return None
|
||||
|
||||
def get_reset_token(self, trans, email):
|
||||
def get_reset_token(self, trans: ProvidesAppContext, email):
|
||||
reset_user = self.by_email(email)
|
||||
if not reset_user:
|
||||
reset_user = self.by_email(email, case_sensitive=False)
|
||||
@@ -799,17 +817,21 @@ class UserDeserializer(base.ModelDeserializer):
|
||||
}
|
||||
self.deserializers.update(user_deserializers)
|
||||
|
||||
def deserialize_preferred_object_store_id(self, item: Any, key: Any, val: Any, trans=None, **context):
|
||||
def deserialize_preferred_object_store_id(
|
||||
self, item: Any, key: Any, val: Any, trans: ProvidesUserContext | None = None, **context
|
||||
):
|
||||
preferred_object_store_id = val
|
||||
validation_error = validate_preferred_object_store_id(trans, self.app.object_store, preferred_object_store_id)
|
||||
validation_error = validate_preferred_object_store_id(
|
||||
trans.user if trans else None, self.app.object_store, preferred_object_store_id
|
||||
)
|
||||
if validation_error:
|
||||
raise base.ModelDeserializingError(validation_error)
|
||||
return self.default_deserializer(item, key, preferred_object_store_id, **context)
|
||||
|
||||
def deserialize_username(self, item, key, username, trans=None, **context):
|
||||
def deserialize_username(self, item, key, username, trans: ProvidesAppContext | None = None, **context):
|
||||
# TODO: validate_publicname requires trans and should(?) raise exceptions
|
||||
# move validation to UserValidator and use self.app, exceptions instead
|
||||
validation_error = validate_publicname(trans, username, user=item)
|
||||
validation_error = validate_publicname(trans, username, user=item) # type: ignore[arg-type]
|
||||
if validation_error:
|
||||
raise base.ModelDeserializingError(validation_error)
|
||||
return self.default_deserializer(item, key, username, trans=trans, **context)
|
||||
@@ -827,14 +849,14 @@ class CurrentUserSerializer(UserSerializer):
|
||||
return self.serialize_current_anonymous_user(user, keys, **kwargs)
|
||||
return super(UserSerializer, self).serialize(user, keys, **kwargs)
|
||||
|
||||
def serialize_current_anonymous_user(self, user, keys, trans=None, **kwargs):
|
||||
def serialize_current_anonymous_user(self, user, keys, trans: ProvidesHistoryContext | None = None, **kwargs):
|
||||
# use the current history if any to get usage stats for trans' anonymous user
|
||||
# TODO: might be better as sep. Serializer class
|
||||
usage = 0
|
||||
usage: float = 0
|
||||
percent = None
|
||||
|
||||
if hasattr(trans, "history") and trans.history:
|
||||
usage = self.app.quota_agent.get_usage(trans, history=trans.history)
|
||||
if trans is not None and trans.history:
|
||||
usage = self.app.quota_agent.get_usage(trans, history=trans.history) or 0
|
||||
percent = self.app.quota_agent.get_percent(trans=trans, usage=usage)
|
||||
|
||||
# a very small subset of keys available
|
||||
|
||||
@@ -57,7 +57,11 @@ from galaxy.managers.base import (
|
||||
decode_id,
|
||||
security_check,
|
||||
)
|
||||
from galaxy.managers.context import ProvidesUserContext
|
||||
from galaxy.managers.context import (
|
||||
ProvidesAppContext,
|
||||
ProvidesHistoryContext,
|
||||
ProvidesUserContext,
|
||||
)
|
||||
from galaxy.managers.executables import artifact_class
|
||||
from galaxy.model import (
|
||||
History,
|
||||
@@ -296,7 +300,7 @@ class WorkflowsManager(sharable.SharableModelManager[model.StoredWorkflow], dele
|
||||
result = trans.sa_session.scalars(stmt).unique()
|
||||
return result, total_matches
|
||||
|
||||
def get_stored_workflow(self, trans, workflow_id, by_stored_id=True) -> StoredWorkflow:
|
||||
def get_stored_workflow(self, trans: ProvidesUserContext, workflow_id, by_stored_id=True) -> StoredWorkflow:
|
||||
"""Use a supplied ID (UUID or encoded stored workflow ID) to find
|
||||
a workflow.
|
||||
"""
|
||||
@@ -318,7 +322,7 @@ class WorkflowsManager(sharable.SharableModelManager[model.StoredWorkflow], dele
|
||||
raise exceptions.ObjectNotFound("No such workflow found.")
|
||||
return stored_workflow
|
||||
|
||||
def get_stored_accessible_workflow(self, trans, workflow_id, by_stored_id=True):
|
||||
def get_stored_accessible_workflow(self, trans: ProvidesUserContext, workflow_id, by_stored_id=True):
|
||||
"""Get a stored workflow from a encoded stored workflow id and
|
||||
make sure it accessible to the user.
|
||||
"""
|
||||
@@ -335,7 +339,7 @@ class WorkflowsManager(sharable.SharableModelManager[model.StoredWorkflow], dele
|
||||
|
||||
return stored_workflow
|
||||
|
||||
def attach_stored_workflow(self, trans, workflow):
|
||||
def attach_stored_workflow(self, trans: ProvidesUserContext, workflow):
|
||||
"""Attach and return stored workflow if possible."""
|
||||
# Imported Subworkflows are not created with a StoredWorkflow association
|
||||
# To properly serialize them we do need a StoredWorkflow, so we create and attach one here.
|
||||
@@ -346,7 +350,7 @@ class WorkflowsManager(sharable.SharableModelManager[model.StoredWorkflow], dele
|
||||
trans.sa_session.commit()
|
||||
return stored_workflow
|
||||
|
||||
def get_owned_workflow(self, trans, encoded_workflow_id):
|
||||
def get_owned_workflow(self, trans: ProvidesUserContext, encoded_workflow_id):
|
||||
"""Get a workflow (non-stored) from a encoded workflow id and
|
||||
make sure it accessible to the user.
|
||||
"""
|
||||
@@ -355,7 +359,7 @@ class WorkflowsManager(sharable.SharableModelManager[model.StoredWorkflow], dele
|
||||
self.check_security(trans, workflow, check_ownership=True)
|
||||
return workflow
|
||||
|
||||
def check_security(self, trans, has_workflow, check_ownership=True, check_accessible=True):
|
||||
def check_security(self, trans: ProvidesUserContext, has_workflow, check_ownership=True, check_accessible=True):
|
||||
"""check accessibility or ownership of workflows, storedworkflows, and
|
||||
workflowinvocations. Throw an exception or returns True if user has
|
||||
needed level of access.
|
||||
@@ -391,12 +395,12 @@ class WorkflowsManager(sharable.SharableModelManager[model.StoredWorkflow], dele
|
||||
|
||||
return True
|
||||
|
||||
def get_workflow_svg_from_id(self, trans, id, version=None, for_embed=False) -> bytes:
|
||||
def get_workflow_svg_from_id(self, trans: ProvidesHistoryContext, id, version=None, for_embed=False) -> bytes:
|
||||
stored = self.get_stored_accessible_workflow(trans, id)
|
||||
workflow = stored.get_internal_version(version)
|
||||
return self.get_workflow_svg(trans, workflow, for_embed=for_embed)
|
||||
|
||||
def get_workflow_svg(self, trans, workflow, for_embed=False) -> bytes:
|
||||
def get_workflow_svg(self, trans: ProvidesHistoryContext, workflow, for_embed=False) -> bytes:
|
||||
try:
|
||||
svg = self._workflow_to_svg_canvas(trans, workflow, for_embed=for_embed)
|
||||
s = STANDALONE_SVG_TEMPLATE % svg.tostring()
|
||||
@@ -407,7 +411,7 @@ class WorkflowsManager(sharable.SharableModelManager[model.StoredWorkflow], dele
|
||||
)
|
||||
raise exceptions.MessageException(message)
|
||||
|
||||
def _workflow_to_svg_canvas(self, trans, workflow, for_embed=False):
|
||||
def _workflow_to_svg_canvas(self, trans: ProvidesHistoryContext, workflow, for_embed=False):
|
||||
workflow_canvas = WorkflowCanvas()
|
||||
for step in workflow.steps:
|
||||
# Load from database representation
|
||||
@@ -425,7 +429,7 @@ class WorkflowsManager(sharable.SharableModelManager[model.StoredWorkflow], dele
|
||||
return workflow_canvas.finish(for_embed=for_embed)
|
||||
|
||||
def get_invocation(
|
||||
self, trans, decoded_invocation_id: int, check_ownership=True, check_accessible=True
|
||||
self, trans: ProvidesUserContext, decoded_invocation_id: int, check_ownership=True, check_accessible=True
|
||||
) -> WorkflowInvocation:
|
||||
workflow_invocation = _get_invocation(trans.sa_session, decoded_invocation_id)
|
||||
if not workflow_invocation:
|
||||
@@ -437,7 +441,7 @@ class WorkflowsManager(sharable.SharableModelManager[model.StoredWorkflow], dele
|
||||
)
|
||||
return workflow_invocation
|
||||
|
||||
def get_invocation_report(self, trans, invocation_id, **kwd):
|
||||
def get_invocation_report(self, trans: ProvidesUserContext, invocation_id, **kwd):
|
||||
decoded_workflow_invocation_id = (
|
||||
trans.security.decode_id(invocation_id) if isinstance(invocation_id, str) else invocation_id
|
||||
)
|
||||
@@ -458,7 +462,7 @@ class WorkflowsManager(sharable.SharableModelManager[model.StoredWorkflow], dele
|
||||
target_format=target_format,
|
||||
)
|
||||
|
||||
def request_invocation_cancellation(self, trans, decoded_invocation_id: int):
|
||||
def request_invocation_cancellation(self, trans: ProvidesUserContext, decoded_invocation_id: int):
|
||||
workflow_invocation = self.get_invocation(trans, decoded_invocation_id, check_ownership=True)
|
||||
cancelled = workflow_invocation.cancel()
|
||||
|
||||
@@ -471,7 +475,11 @@ class WorkflowsManager(sharable.SharableModelManager[model.StoredWorkflow], dele
|
||||
return workflow_invocation
|
||||
|
||||
def get_invocation_step(
|
||||
self, trans, decoded_workflow_invocation_step_id, check_ownership: bool = True, check_accessible: bool = True
|
||||
self,
|
||||
trans: ProvidesUserContext,
|
||||
decoded_workflow_invocation_step_id,
|
||||
check_ownership: bool = True,
|
||||
check_accessible: bool = True,
|
||||
) -> WorkflowInvocationStep:
|
||||
try:
|
||||
workflow_invocation_step = trans.sa_session.get(WorkflowInvocationStep, decoded_workflow_invocation_step_id)
|
||||
@@ -485,7 +493,7 @@ class WorkflowsManager(sharable.SharableModelManager[model.StoredWorkflow], dele
|
||||
)
|
||||
return workflow_invocation_step
|
||||
|
||||
def update_invocation_step(self, trans, decoded_workflow_invocation_step_id, action):
|
||||
def update_invocation_step(self, trans: ProvidesHistoryContext, decoded_workflow_invocation_step_id, action):
|
||||
if action is None:
|
||||
raise exceptions.RequestParameterMissingException(
|
||||
"Updating workflow invocation step requires an action parameter. "
|
||||
@@ -664,7 +672,7 @@ class WorkflowContentsManager(UsesAnnotations):
|
||||
created_workflow = self.build_workflow_from_raw_description(trans, raw_description, WorkflowCreateOptions())
|
||||
return created_workflow.workflow
|
||||
|
||||
def normalize_workflow_format(self, trans, as_dict):
|
||||
def normalize_workflow_format(self, trans: ProvidesUserContext, as_dict):
|
||||
"""Process incoming workflow descriptions for consumption by other methods.
|
||||
|
||||
Currently this mostly means converting format 2 workflows into standard Galaxy
|
||||
@@ -699,7 +707,7 @@ class WorkflowContentsManager(UsesAnnotations):
|
||||
|
||||
def build_workflow_from_raw_description(
|
||||
self,
|
||||
trans,
|
||||
trans: ProvidesHistoryContext,
|
||||
raw_workflow_description,
|
||||
workflow_create_options,
|
||||
source=None,
|
||||
@@ -763,7 +771,7 @@ class WorkflowContentsManager(UsesAnnotations):
|
||||
|
||||
def update_workflow_from_raw_description(
|
||||
self,
|
||||
trans,
|
||||
trans: ProvidesHistoryContext,
|
||||
stored_workflow: StoredWorkflow,
|
||||
raw_workflow_description: RawWorkflowDescription,
|
||||
workflow_update_options: WorkflowUpdateOptions,
|
||||
@@ -836,7 +844,7 @@ class WorkflowContentsManager(UsesAnnotations):
|
||||
|
||||
def _workflow_from_raw_description(
|
||||
self,
|
||||
trans,
|
||||
trans: ProvidesHistoryContext,
|
||||
raw_workflow_description,
|
||||
workflow_state_resolution_options,
|
||||
name,
|
||||
@@ -976,7 +984,7 @@ class WorkflowContentsManager(UsesAnnotations):
|
||||
|
||||
def workflow_to_dict(
|
||||
self,
|
||||
trans,
|
||||
trans: ProvidesHistoryContext,
|
||||
stored: StoredWorkflow,
|
||||
style: str = "export",
|
||||
version: int | None = None,
|
||||
@@ -1053,7 +1061,7 @@ class WorkflowContentsManager(UsesAnnotations):
|
||||
wf_dict["version"] = len(stored.workflows) - 1
|
||||
return wf_dict
|
||||
|
||||
def _sync_stored_workflow(self, trans, stored_workflow: StoredWorkflow) -> None:
|
||||
def _sync_stored_workflow(self, trans: ProvidesHistoryContext, stored_workflow: StoredWorkflow) -> None:
|
||||
if trans.user_is_admin:
|
||||
workflow_path = stored_workflow.from_path
|
||||
assert workflow_path is not None
|
||||
@@ -1083,7 +1091,7 @@ class WorkflowContentsManager(UsesAnnotations):
|
||||
workflow_path: str,
|
||||
stored_workflow: StoredWorkflow,
|
||||
workflow: Workflow,
|
||||
trans=None,
|
||||
trans: ProvidesHistoryContext | None = None,
|
||||
history: History | None = None,
|
||||
user: User | None = None,
|
||||
) -> None:
|
||||
@@ -1102,7 +1110,7 @@ class WorkflowContentsManager(UsesAnnotations):
|
||||
f.write(wf_dict["yaml_content"])
|
||||
|
||||
def _workflow_to_dict_run(
|
||||
self, trans: ProvidesUserContext, stored: StoredWorkflow, workflow: Workflow, history: History | None = None
|
||||
self, trans: ProvidesHistoryContext, stored: StoredWorkflow, workflow: Workflow, history: History | None = None
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
Builds workflow dictionary used by run workflow form
|
||||
@@ -1217,7 +1225,7 @@ class WorkflowContentsManager(UsesAnnotations):
|
||||
"workflow_resource_parameters": self._workflow_resource_parameters(trans, stored, workflow),
|
||||
}
|
||||
|
||||
def _workflow_to_dict_preview(self, trans, workflow):
|
||||
def _workflow_to_dict_preview(self, trans: ProvidesHistoryContext, workflow):
|
||||
"""
|
||||
Builds workflow dictionary containing input labels and values.
|
||||
Used to create embedded workflow previews.
|
||||
@@ -1325,6 +1333,9 @@ class WorkflowContentsManager(UsesAnnotations):
|
||||
continue
|
||||
if step.type == "tool":
|
||||
tool = trans.app.toolbox.get_tool(step.tool_id, step.tool_version)
|
||||
assert (
|
||||
tool is not None
|
||||
), f"Tool '{step.tool_id}' unexpectedly missing after successful runtime state computation"
|
||||
step_dict["tool_id"] = step.tool_id
|
||||
step_dict["tool_version"] = step.tool_version
|
||||
step_dict["label"] = step.label or tool.name
|
||||
@@ -1346,13 +1357,13 @@ class WorkflowContentsManager(UsesAnnotations):
|
||||
"steps": step_dicts,
|
||||
}
|
||||
|
||||
def _workflow_resource_parameters(self, trans, stored, workflow):
|
||||
def _workflow_resource_parameters(self, trans: ProvidesUserContext, stored, workflow):
|
||||
"""Get workflow scheduling resource parameters for this user and workflow or None if not configured."""
|
||||
return self._resource_mapper_function(trans=trans, stored_workflow=stored, workflow=workflow)
|
||||
|
||||
def _workflow_to_dict_editor(
|
||||
self,
|
||||
trans,
|
||||
trans: ProvidesHistoryContext,
|
||||
stored: StoredWorkflow | None,
|
||||
workflow: Workflow,
|
||||
tooltip: bool = True,
|
||||
@@ -1585,7 +1596,7 @@ class WorkflowContentsManager(UsesAnnotations):
|
||||
|
||||
def _workflow_to_dict_export(
|
||||
self,
|
||||
trans,
|
||||
trans: ProvidesHistoryContext,
|
||||
workflow: Workflow,
|
||||
stored: StoredWorkflow | None = None,
|
||||
internal: bool = False,
|
||||
@@ -1833,12 +1844,13 @@ class WorkflowContentsManager(UsesAnnotations):
|
||||
return data
|
||||
|
||||
def _workflow_to_dict_instance(
|
||||
self, trans, stored: StoredWorkflow, workflow: Workflow, legacy: bool = True
|
||||
self, trans: ProvidesAppContext, stored: StoredWorkflow, workflow: Workflow, legacy: bool = True
|
||||
) -> dict[str, Any]:
|
||||
encode = self.app.security.encode_id
|
||||
sa_session = self.app.model.context
|
||||
item = stored.to_dict(view="element")
|
||||
item["name"] = workflow.name
|
||||
assert trans.url_builder
|
||||
item["url"] = trans.url_builder("workflow", id=encode(stored.id))
|
||||
item["owner"] = stored.user.username
|
||||
item["email_hash"] = md5_hash_str(stored.user.email)
|
||||
@@ -1976,7 +1988,7 @@ class WorkflowContentsManager(UsesAnnotations):
|
||||
|
||||
def __load_subworkflows(
|
||||
self,
|
||||
trans,
|
||||
trans: ProvidesHistoryContext,
|
||||
step_dict,
|
||||
subworkflow_id_map,
|
||||
workflow_state_resolution_options,
|
||||
@@ -1997,7 +2009,7 @@ class WorkflowContentsManager(UsesAnnotations):
|
||||
|
||||
def __module_from_dict(
|
||||
self,
|
||||
trans,
|
||||
trans: ProvidesHistoryContext,
|
||||
steps: list[model.WorkflowStep],
|
||||
steps_by_external_id: dict[str, model.WorkflowStep],
|
||||
step_dict,
|
||||
@@ -2083,7 +2095,7 @@ class WorkflowContentsManager(UsesAnnotations):
|
||||
|
||||
def __load_subworkflow_from_step_dict(
|
||||
self,
|
||||
trans,
|
||||
trans: ProvidesHistoryContext,
|
||||
step_dict,
|
||||
subworkflow_id_map,
|
||||
workflow_state_resolution_options,
|
||||
@@ -2147,7 +2159,7 @@ class WorkflowContentsManager(UsesAnnotations):
|
||||
|
||||
def __build_subworkflow_from_url(
|
||||
self,
|
||||
trans,
|
||||
trans: ProvidesHistoryContext,
|
||||
url: str,
|
||||
resolving_urls: frozenset[str],
|
||||
) -> model.Workflow:
|
||||
@@ -2173,7 +2185,7 @@ class WorkflowContentsManager(UsesAnnotations):
|
||||
|
||||
def __build_subworkflow_from_trs_url(
|
||||
self,
|
||||
trans,
|
||||
trans: ProvidesHistoryContext,
|
||||
trs_url: str,
|
||||
resolving_urls: frozenset[str],
|
||||
) -> model.Workflow:
|
||||
@@ -2206,7 +2218,7 @@ class WorkflowContentsManager(UsesAnnotations):
|
||||
|
||||
def __build_subworkflow_from_trs_id(
|
||||
self,
|
||||
trans,
|
||||
trans: ProvidesHistoryContext,
|
||||
step_dict: dict,
|
||||
resolving_urls: frozenset[str],
|
||||
) -> model.Workflow:
|
||||
@@ -2227,7 +2239,11 @@ class WorkflowContentsManager(UsesAnnotations):
|
||||
return self.__build_subworkflow_from_trs_url(trans, trs_url, resolving_urls)
|
||||
|
||||
def __build_embedded_subworkflow(
|
||||
self, trans, data, workflow_state_resolution_options, resolving_urls: frozenset[str] = frozenset()
|
||||
self,
|
||||
trans: ProvidesHistoryContext,
|
||||
data,
|
||||
workflow_state_resolution_options,
|
||||
resolving_urls: frozenset[str] = frozenset(),
|
||||
):
|
||||
raw_workflow_description = self.ensure_raw_description(data)
|
||||
subworkflow = self.build_workflow_from_raw_description(
|
||||
@@ -2290,7 +2306,7 @@ class WorkflowContentsManager(UsesAnnotations):
|
||||
step.label = module.label = default_label
|
||||
|
||||
def do_refactor(
|
||||
self, trans: ProvidesUserContext, stored_workflow: StoredWorkflow, refactor_request: RefactorRequest
|
||||
self, trans: ProvidesHistoryContext, stored_workflow: StoredWorkflow, refactor_request: RefactorRequest
|
||||
):
|
||||
"""Apply supplied actions to either the latest version of the workflow or a specific version to build a new version."""
|
||||
# Get the workflow version to refactor (latest or specific version)
|
||||
@@ -2323,7 +2339,9 @@ class WorkflowContentsManager(UsesAnnotations):
|
||||
# we send back anyway
|
||||
return refactored_workflow, action_executions
|
||||
|
||||
def refactor(self, trans: ProvidesUserContext, stored_workflow: StoredWorkflow, refactor_request: RefactorRequest):
|
||||
def refactor(
|
||||
self, trans: ProvidesHistoryContext, stored_workflow: StoredWorkflow, refactor_request: RefactorRequest
|
||||
):
|
||||
refactored_workflow, action_executions = self.do_refactor(trans, stored_workflow, refactor_request)
|
||||
return RefactorResponse(
|
||||
action_executions=action_executions,
|
||||
@@ -2354,7 +2372,7 @@ class WorkflowContentsManager(UsesAnnotations):
|
||||
|
||||
def get_or_create_workflow_from_trs(
|
||||
self,
|
||||
trans: ProvidesUserContext,
|
||||
trans: ProvidesHistoryContext,
|
||||
trs_url: str | None,
|
||||
trs_id: str | None = None,
|
||||
trs_version: str | None = None,
|
||||
@@ -2376,7 +2394,7 @@ class WorkflowContentsManager(UsesAnnotations):
|
||||
return workflow
|
||||
|
||||
def create_workflow_from_trs_url(
|
||||
self, trans: ProvidesUserContext, trs_url: str, trs_server: str | None = None
|
||||
self, trans: ProvidesHistoryContext, trs_url: str, trs_server: str | None = None
|
||||
) -> StoredWorkflow:
|
||||
_, trs_tool_id, trs_version_id = self.trs_proxy.get_trs_id_and_version_from_trs_url(trs_url=trs_url)
|
||||
data = self.trs_proxy.get_version_from_trs_url(trs_url)
|
||||
@@ -2395,7 +2413,7 @@ class WorkflowContentsManager(UsesAnnotations):
|
||||
)
|
||||
return created_workflow.stored_workflow
|
||||
|
||||
def get_or_create_workflow_from_url(self, trans: ProvidesUserContext, url: str) -> StoredWorkflow:
|
||||
def get_or_create_workflow_from_url(self, trans: ProvidesHistoryContext, url: str) -> StoredWorkflow:
|
||||
"""Fetch and import a workflow from an arbitrary URL.
|
||||
|
||||
Supports various URL schemes including http://, https://, and base64://.
|
||||
|
||||
@@ -241,12 +241,17 @@ from galaxy.util.sanitize_html import sanitize_html
|
||||
if TYPE_CHECKING:
|
||||
from sqlalchemy.sql.expression import BindParameter
|
||||
|
||||
from galaxy.managers.context import (
|
||||
ProvidesAppContext,
|
||||
ProvidesUserContext,
|
||||
)
|
||||
from galaxy.objectstore import (
|
||||
BaseObjectStore,
|
||||
ObjectStorePopulator,
|
||||
QuotaSourceMap,
|
||||
)
|
||||
from galaxy.schema.invocation import InvocationMessageUnion
|
||||
from galaxy.webapps.base.webapp import GalaxyWebTransaction
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -5790,7 +5795,7 @@ class DatasetInstance(RepresentById, UsesCreateAndUpdateTime, _HasTable):
|
||||
return item
|
||||
return None
|
||||
|
||||
def get_converted_dataset_deps(self, trans, target_ext, use_cached_job=False):
|
||||
def get_converted_dataset_deps(self, trans: "ProvidesUserContext", target_ext, use_cached_job=False):
|
||||
"""
|
||||
Returns dict of { "dependency" => HDA }
|
||||
"""
|
||||
@@ -5802,7 +5807,13 @@ class DatasetInstance(RepresentById, UsesCreateAndUpdateTime, _HasTable):
|
||||
return {dep: self.get_converted_dataset(trans, dep, use_cached_job=use_cached_job) for dep in depends_list}
|
||||
|
||||
def get_converted_dataset(
|
||||
self, trans, target_ext, target_context=None, history=None, include_errored=False, use_cached_job=False
|
||||
self,
|
||||
trans: "ProvidesUserContext",
|
||||
target_ext,
|
||||
target_context=None,
|
||||
history=None,
|
||||
include_errored=False,
|
||||
use_cached_job=False,
|
||||
):
|
||||
"""
|
||||
Return converted dataset(s) if they exist, along with a dict of dependencies.
|
||||
@@ -5897,7 +5908,7 @@ class DatasetInstance(RepresentById, UsesCreateAndUpdateTime, _HasTable):
|
||||
return format in self.get_converter_types()
|
||||
|
||||
def find_conversion_destination(
|
||||
self, accepted_formats: list[str], **kwd
|
||||
self, accepted_formats: Iterable[Union[str, "Data"]], **kwd
|
||||
) -> tuple[bool, str | None, Optional["DatasetInstance"]]:
|
||||
"""Returns ( target_ext, existing converted dataset )"""
|
||||
return self.datatype.find_conversion_destination(self, accepted_formats, _get_datatypes_registry(), **kwd)
|
||||
@@ -5992,10 +6003,10 @@ class DatasetInstance(RepresentById, UsesCreateAndUpdateTime, _HasTable):
|
||||
return creating_job_associations[0].job
|
||||
return None
|
||||
|
||||
def get_display_applications(self, trans):
|
||||
def get_display_applications(self, trans: "GalaxyWebTransaction"):
|
||||
return self.datatype.get_display_applications_by_dataset(self, trans)
|
||||
|
||||
def get_datasources(self, trans):
|
||||
def get_datasources(self, trans: "ProvidesUserContext"):
|
||||
"""
|
||||
Returns datasources for dataset; if datasources are not available
|
||||
due to indexing, indexing is started. Return value is a dictionary
|
||||
@@ -6011,25 +6022,19 @@ class DatasetInstance(RepresentById, UsesCreateAndUpdateTime, _HasTable):
|
||||
msg = None
|
||||
data_source = source_list
|
||||
else:
|
||||
# Convert.
|
||||
if isinstance(source_list, str):
|
||||
source_list = [source_list]
|
||||
|
||||
# Loop through sources until viable one is found.
|
||||
for source in source_list:
|
||||
msg = self.convert_dataset(trans, source)
|
||||
# No message or PENDING means that source is viable. No
|
||||
# message indicates conversion was done and is successful.
|
||||
if not msg or msg == self.conversion_messages.PENDING:
|
||||
data_source = source
|
||||
break
|
||||
# Convert. Each data_sources entry names a single source.
|
||||
msg = self.convert_dataset(trans, source_list)
|
||||
# No message or PENDING means that source is viable. No
|
||||
# message indicates conversion was done and is successful.
|
||||
if not msg or msg == self.conversion_messages.PENDING:
|
||||
data_source = source_list
|
||||
|
||||
# Store msg.
|
||||
data_sources_dict[source_type] = {"name": data_source, "message": msg}
|
||||
|
||||
return data_sources_dict
|
||||
|
||||
def convert_dataset(self, trans, target_type):
|
||||
def convert_dataset(self, trans: "ProvidesUserContext", target_type):
|
||||
"""
|
||||
Converts a dataset to the target_type and returns a message indicating
|
||||
status of the conversion. None is returned to indicate that dataset
|
||||
@@ -6046,7 +6051,7 @@ class DatasetInstance(RepresentById, UsesCreateAndUpdateTime, _HasTable):
|
||||
return {"kind": self.conversion_messages.ERROR, "message": dep_error.value}
|
||||
|
||||
# Check dataset state and return any messages.
|
||||
msg = None
|
||||
msg: dict[str, Any] | Dataset.conversion_messages | None = None
|
||||
if converted_dataset and converted_dataset.state == Dataset.states.ERROR:
|
||||
stmt = select(JobToOutputDatasetAssociation.job_id).filter_by(dataset_id=converted_dataset.id).limit(1)
|
||||
job_id = trans.sa_session.scalars(stmt).first()
|
||||
@@ -6258,7 +6263,7 @@ class HistoryDatasetAssociation(DatasetInstance, HasTags, UsesAnnotations, HasNa
|
||||
|
||||
def to_library_dataset_dataset_association(
|
||||
self,
|
||||
trans,
|
||||
trans: "ProvidesUserContext",
|
||||
target_folder,
|
||||
replace_dataset=None,
|
||||
parent_id=None,
|
||||
@@ -11421,7 +11426,7 @@ class UserAddress(Base, RepresentById):
|
||||
# TODO: db migration to rename column, then use `desc`
|
||||
user: Mapped[Optional["User"]] = relationship(back_populates="addresses", order_by=sqlalchemy.desc("update_time"))
|
||||
|
||||
def to_dict(self, trans):
|
||||
def to_dict(self, trans: "ProvidesAppContext"):
|
||||
return {
|
||||
"id": trans.security.encode_id(self.id),
|
||||
"name": sanitize_html(self.name),
|
||||
|
||||
@@ -2,6 +2,7 @@ import logging
|
||||
import socket
|
||||
import sqlite3
|
||||
from datetime import timedelta
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from sqlalchemy import (
|
||||
and_,
|
||||
@@ -57,6 +58,12 @@ from galaxy.util import (
|
||||
)
|
||||
from galaxy.util.bunch import Bunch
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from galaxy.managers.context import (
|
||||
ProvidesAppContext,
|
||||
ProvidesUserContext,
|
||||
)
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -89,7 +96,7 @@ class GalaxyRBACAgent(RBACAgent):
|
||||
intermed.sort()
|
||||
return [_[-1] for _ in intermed]
|
||||
|
||||
def get_all_roles(self, trans, cntrller):
|
||||
def get_all_roles(self, trans: "ProvidesUserContext", cntrller):
|
||||
admin_controller = cntrller in ["library_admin"]
|
||||
roles = set()
|
||||
if not trans.user:
|
||||
@@ -123,7 +130,9 @@ class GalaxyRBACAgent(RBACAgent):
|
||||
roles.append(item_permission.role)
|
||||
return roles
|
||||
|
||||
def get_valid_roles(self, trans, item, query=None, page=None, page_limit=None, is_library_access=False):
|
||||
def get_valid_roles(
|
||||
self, trans: "ProvidesUserContext", item, query=None, page=None, page_limit=None, is_library_access=False
|
||||
):
|
||||
"""
|
||||
This method retrieves the list of possible roles that user can select
|
||||
in the item permissions form. Admins can select any role so the
|
||||
@@ -196,7 +205,7 @@ class GalaxyRBACAgent(RBACAgent):
|
||||
total_count = len(return_roles)
|
||||
return self.sort_by_attr(list(return_roles), "name"), total_count
|
||||
|
||||
def get_legitimate_roles(self, trans, item, cntrller):
|
||||
def get_legitimate_roles(self, trans: "ProvidesUserContext", item, cntrller):
|
||||
"""
|
||||
Return a sorted list of legitimate roles that can be associated with a permission on
|
||||
item where item is a Library or a Dataset. The cntrller param is the controller from
|
||||
@@ -290,7 +299,7 @@ class GalaxyRBACAgent(RBACAgent):
|
||||
break
|
||||
return ret_val
|
||||
|
||||
def get_actions_for_items(self, trans, action, permission_items):
|
||||
def get_actions_for_items(self, trans: "ProvidesAppContext", action, permission_items):
|
||||
# TODO: Rename this; it's a replacement for get_item_actions, but it
|
||||
# doesn't represent what it's really doing, which is confusing.
|
||||
# TODO: Make this work for other classes besides lib_datasets.
|
||||
@@ -309,18 +318,18 @@ class GalaxyRBACAgent(RBACAgent):
|
||||
#
|
||||
# If the dataset id has no corresponding action in its permissions,
|
||||
# then the returned permissions will not carry an entry for the dataset.
|
||||
ret_permissions = {}
|
||||
ret_permissions: dict[int, list] = {}
|
||||
if len(permission_items) > 0:
|
||||
# SM: NB: LibraryDatasets became Datasets for some odd reason.
|
||||
if isinstance(permission_items[0], LibraryDataset):
|
||||
ids = [item.library_dataset_id for item in permission_items]
|
||||
stmt = select(LibraryDatasetPermissions).where(
|
||||
library_dataset_stmt = select(LibraryDatasetPermissions).where(
|
||||
and_(
|
||||
LibraryDatasetPermissions.library_dataset_id.in_(ids),
|
||||
LibraryDatasetPermissions.action == action.action,
|
||||
)
|
||||
)
|
||||
permissions = trans.sa_session.scalars(stmt)
|
||||
permissions = trans.sa_session.scalars(library_dataset_stmt)
|
||||
# Massage the return data. We will return a list of permissions
|
||||
# for each library dataset. So we initialize the return list to
|
||||
# have an empty list for each dataset. Then each permission is
|
||||
@@ -334,10 +343,10 @@ class GalaxyRBACAgent(RBACAgent):
|
||||
elif isinstance(permission_items[0], Dataset):
|
||||
ids = [item.id for item in permission_items]
|
||||
|
||||
stmt = select(DatasetPermissions).where(
|
||||
dataset_stmt = select(DatasetPermissions).where(
|
||||
and_(DatasetPermissions.dataset_id.in_(ids), DatasetPermissions.action == action.action)
|
||||
)
|
||||
permissions = trans.sa_session.scalars(stmt)
|
||||
permissions = trans.sa_session.scalars(dataset_stmt)
|
||||
# Massage the return data. We will return a list of permissions
|
||||
# for each library dataset. So we initialize the return list to
|
||||
# have an empty list for each dataset. Then each permission is
|
||||
@@ -384,7 +393,7 @@ class GalaxyRBACAgent(RBACAgent):
|
||||
|
||||
return ret_permissions
|
||||
|
||||
def allow_action_on_libitems(self, trans, user_roles, action, items):
|
||||
def allow_action_on_libitems(self, trans: "ProvidesAppContext", user_roles, action, items):
|
||||
"""
|
||||
This should be the equivalent of allow_action defined on multiple items.
|
||||
It is meant to specifically replace allow_action for multiple
|
||||
@@ -437,7 +446,7 @@ class GalaxyRBACAgent(RBACAgent):
|
||||
return ret_allow_action
|
||||
|
||||
# DELETEME: SM: DO NOT TOUCH! This actually works.
|
||||
def dataset_access_mapping(self, trans, user_roles, datasets):
|
||||
def dataset_access_mapping(self, trans: "ProvidesAppContext", user_roles, datasets):
|
||||
"""
|
||||
For the given list of datasets, return a mapping of the datasets' ids
|
||||
to whether they can be accessed by the user or not. The datasets input
|
||||
@@ -452,7 +461,7 @@ class GalaxyRBACAgent(RBACAgent):
|
||||
can_access[dataset.id] = datasets_public_map[dataset.id] or datasets_allow_action_map[dataset.id]
|
||||
return can_access
|
||||
|
||||
def dataset_permission_map_for_access(self, trans, user_roles, libitems):
|
||||
def dataset_permission_map_for_access(self, trans: "ProvidesAppContext", user_roles, libitems):
|
||||
"""
|
||||
For a given list of library items (e.g., Datasets), return a map of the
|
||||
datasets' ids to whether they can have permission to use that action
|
||||
@@ -477,13 +486,13 @@ class GalaxyRBACAgent(RBACAgent):
|
||||
can_access[libitem.id] = libitems_public_map[libitem.id] or libitems_allow_action_map[libitem.id]
|
||||
return can_access
|
||||
|
||||
def item_permission_map_for_modify(self, trans, user_roles, libitems):
|
||||
def item_permission_map_for_modify(self, trans: "ProvidesAppContext", user_roles, libitems):
|
||||
return self.allow_action_on_libitems(trans, user_roles, self.permitted_actions.LIBRARY_MODIFY, libitems)
|
||||
|
||||
def item_permission_map_for_manage(self, trans, user_roles, libitems):
|
||||
def item_permission_map_for_manage(self, trans: "ProvidesAppContext", user_roles, libitems):
|
||||
return self.allow_action_on_libitems(trans, user_roles, self.permitted_actions.LIBRARY_MANAGE, libitems)
|
||||
|
||||
def item_permission_map_for_add(self, trans, user_roles, libitems):
|
||||
def item_permission_map_for_add(self, trans: "ProvidesAppContext", user_roles, libitems):
|
||||
return self.allow_action_on_libitems(trans, user_roles, self.permitted_actions.LIBRARY_ADD, libitems)
|
||||
|
||||
def can_access_dataset(self, user_roles, dataset: Dataset):
|
||||
@@ -519,26 +528,28 @@ class GalaxyRBACAgent(RBACAgent):
|
||||
roles, self.permitted_actions.LIBRARY_ACCESS, library
|
||||
)
|
||||
|
||||
def get_accessible_libraries(self, trans, user):
|
||||
def get_accessible_libraries(self, trans: "ProvidesAppContext", user):
|
||||
"""Return all data libraries that the received user can access"""
|
||||
accessible_libraries = []
|
||||
current_user_role_ids = [role.id for role in user.all_roles()]
|
||||
library_access_action = self.permitted_actions.LIBRARY_ACCESS.action
|
||||
|
||||
stmt = select(LibraryPermissions).where(LibraryPermissions.action == library_access_action).distinct()
|
||||
restricted_library_ids = [lp.library_id for lp in trans.sa_session.scalars(stmt)]
|
||||
restricted_stmt = (
|
||||
select(LibraryPermissions).where(LibraryPermissions.action == library_access_action).distinct()
|
||||
)
|
||||
restricted_library_ids = [lp.library_id for lp in trans.sa_session.scalars(restricted_stmt)]
|
||||
|
||||
stmt = select(LibraryPermissions).where(
|
||||
accessible_stmt = select(LibraryPermissions).where(
|
||||
and_(
|
||||
LibraryPermissions.action == library_access_action,
|
||||
LibraryPermissions.role_id.in_(current_user_role_ids),
|
||||
)
|
||||
)
|
||||
accessible_restricted_library_ids = [lp.library_id for lp in trans.sa_session.scalars(stmt)]
|
||||
accessible_restricted_library_ids = [lp.library_id for lp in trans.sa_session.scalars(accessible_stmt)]
|
||||
|
||||
# Filter to get libraries accessible by the current user. Get both
|
||||
# public libraries and restricted libraries accessible by the current user.
|
||||
stmt = (
|
||||
library_stmt = (
|
||||
select(Library)
|
||||
.where(
|
||||
and_(
|
||||
@@ -551,11 +562,11 @@ class GalaxyRBACAgent(RBACAgent):
|
||||
)
|
||||
.order_by(Library.name)
|
||||
)
|
||||
for library in trans.sa_session.scalars(stmt):
|
||||
for library in trans.sa_session.scalars(library_stmt):
|
||||
accessible_libraries.append(library)
|
||||
return accessible_libraries
|
||||
|
||||
def has_accessible_folders(self, trans, folder, user, roles, search_downward=True):
|
||||
def has_accessible_folders(self, trans: "ProvidesAppContext", folder, user, roles, search_downward=True):
|
||||
if (
|
||||
self.has_accessible_library_datasets(trans, folder, user, roles, search_downward=search_downward)
|
||||
or self.can_add_library_item(roles, folder)
|
||||
@@ -568,7 +579,7 @@ class GalaxyRBACAgent(RBACAgent):
|
||||
return self.has_accessible_folders(trans, active_folder, user, roles, search_downward=search_downward)
|
||||
return False
|
||||
|
||||
def has_accessible_library_datasets(self, trans, folder, user, roles, search_downward=True):
|
||||
def has_accessible_library_datasets(self, trans: "ProvidesAppContext", folder, user, roles, search_downward=True):
|
||||
stmt = select(LibraryDataset).where(
|
||||
and_(LibraryDataset.deleted == false(), LibraryDataset.folder_id == folder.id)
|
||||
)
|
||||
@@ -579,7 +590,7 @@ class GalaxyRBACAgent(RBACAgent):
|
||||
return self.__active_folders_have_accessible_library_datasets(trans, folder, user, roles)
|
||||
return False
|
||||
|
||||
def __active_folders_have_accessible_library_datasets(self, trans, folder, user, roles):
|
||||
def __active_folders_have_accessible_library_datasets(self, trans: "ProvidesAppContext", folder, user, roles):
|
||||
for active_folder in folder.active_folders:
|
||||
if self.has_accessible_library_datasets(trans, active_folder, user, roles):
|
||||
return True
|
||||
@@ -996,7 +1007,7 @@ WHERE history.user_id != :user_id and history_dataset_association.dataset_id = :
|
||||
self.associate_user_role(user, sharing_role)
|
||||
return sharing_role
|
||||
|
||||
def set_all_library_permissions(self, trans, library_item, permissions=None):
|
||||
def set_all_library_permissions(self, trans: "ProvidesAppContext", library_item, permissions=None):
|
||||
# Set new permissions on library_item, eliminating all current permissions
|
||||
flush_needed = False
|
||||
permissions = permissions or {}
|
||||
@@ -1016,6 +1027,7 @@ WHERE history.user_id != :user_id and history_dataset_association.dataset_id = :
|
||||
# Permission setting related to DATASET_MANAGE_PERMISSIONS was broken for a period of time,
|
||||
# so it is possible that some Datasets have no roles associated with the DATASET_MANAGE_PERMISSIONS
|
||||
# permission. In this case, we'll reset this permission to the library_item user's private role.
|
||||
assert library_item.dataset is not None
|
||||
if not library_item.dataset.has_manage_permissions_roles(self):
|
||||
# Well this looks like a bug, this should be looked at.
|
||||
# Default permissions above is single hash that keeps getting reeditted here
|
||||
@@ -1120,14 +1132,14 @@ WHERE history.user_id != :user_id and history_dataset_association.dataset_id = :
|
||||
"""
|
||||
return self.permitted_actions.DATASET_ACCESS.action not in [a.action for a in dataset.actions]
|
||||
|
||||
def dataset_is_unrestricted(self, trans, dataset):
|
||||
def dataset_is_unrestricted(self, trans: "ProvidesAppContext", dataset):
|
||||
"""
|
||||
Different implementation of the method above with signature:
|
||||
def dataset_is_public( self, dataset )
|
||||
"""
|
||||
return len(dataset.library_dataset_dataset_association.get_access_roles(self)) == 0
|
||||
|
||||
def dataset_is_private_to_user(self, trans, dataset):
|
||||
def dataset_is_private_to_user(self, trans: "ProvidesUserContext", dataset):
|
||||
"""
|
||||
If the Dataset object has exactly one access role and that is
|
||||
the current user's private role then we consider the dataset private.
|
||||
@@ -1156,7 +1168,7 @@ WHERE history.user_id != :user_id and history_dataset_association.dataset_id = :
|
||||
access_role = access_roles[0]
|
||||
return access_role.type == Role.types.PRIVATE
|
||||
|
||||
def datasets_are_public(self, trans, datasets):
|
||||
def datasets_are_public(self, trans: "ProvidesAppContext", datasets):
|
||||
"""
|
||||
Given a transaction object and a list of Datasets, return
|
||||
a mapping from Dataset ids to whether the Dataset is public
|
||||
@@ -1197,12 +1209,12 @@ WHERE history.user_id != :user_id and history_dataset_association.dataset_id = :
|
||||
if flush_needed:
|
||||
self.sa_session.commit()
|
||||
|
||||
def derive_roles_from_access(self, trans, item_id, cntrller, library=False, **kwd):
|
||||
def derive_roles_from_access(self, trans: "ProvidesUserContext", item_id, cntrller, library=False, **kwd):
|
||||
# Check the access permission on a dataset. If library is true, item_id refers to a library. If library
|
||||
# is False, item_id refers to a dataset ( item_id must currently be decoded before being sent ). The
|
||||
# cntrller param is the calling controller, which needs to be passed to get_legitimate_roles().
|
||||
msg = ""
|
||||
permissions = {}
|
||||
permissions: dict = {}
|
||||
# accessible will be True only if at least 1 user has every role in DATASET_ACCESS_in
|
||||
accessible = False
|
||||
# legitimate will be True only if all roles in DATASET_ACCESS_in are in the set of roles returned from
|
||||
@@ -1296,9 +1308,11 @@ WHERE history.user_id != :user_id and history_dataset_association.dataset_id = :
|
||||
permissions[self.get_action(v.action)] = in_roles
|
||||
return permissions, in_roles, error, msg
|
||||
|
||||
def copy_library_permissions(self, trans, source_library_item, target_library_item, user=None):
|
||||
def copy_library_permissions(
|
||||
self, trans: "ProvidesAppContext", source_library_item, target_library_item, user=None
|
||||
):
|
||||
# Copy all relevant permissions from source.
|
||||
permissions = {}
|
||||
permissions: dict = {}
|
||||
for role_assoc in source_library_item.actions:
|
||||
if role_assoc.action != self.permitted_actions.LIBRARY_ACCESS.action:
|
||||
# LIBRARY_ACCESS is a special permission that is set only at the library level.
|
||||
@@ -1324,7 +1338,7 @@ WHERE history.user_id != :user_id and history_dataset_association.dataset_id = :
|
||||
self.sa_session.add(lp)
|
||||
self.sa_session.commit()
|
||||
|
||||
def get_permitted_libraries(self, trans, user, actions):
|
||||
def get_permitted_libraries(self, trans: "ProvidesAppContext", user, actions):
|
||||
"""
|
||||
This method is historical (it is not currently used), but may be useful again at some
|
||||
point. It returns a dictionary whose keys are library objects and whose values are a
|
||||
|
||||
@@ -5,10 +5,17 @@ Galaxy Security
|
||||
|
||||
from typing import (
|
||||
Literal,
|
||||
TYPE_CHECKING,
|
||||
)
|
||||
|
||||
from galaxy.util.bunch import Bunch
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from galaxy.managers.context import (
|
||||
ProvidesAppContext,
|
||||
ProvidesUserContext,
|
||||
)
|
||||
|
||||
ActionModel = Literal["grant", "restrict"]
|
||||
|
||||
|
||||
@@ -111,7 +118,7 @@ class RBACAgent:
|
||||
def set_dataset_permission(self, dataset, permission):
|
||||
raise Exception("Unimplemented Method")
|
||||
|
||||
def set_all_library_permissions(self, trans, dataset, permissions):
|
||||
def set_all_library_permissions(self, trans: "ProvidesAppContext", dataset, permissions):
|
||||
raise Exception("Unimplemented Method")
|
||||
|
||||
def set_library_item_permission(self, library_item, permission):
|
||||
@@ -123,10 +130,10 @@ class RBACAgent:
|
||||
def make_library_public(self, library):
|
||||
raise Exception("Unimplemented Method")
|
||||
|
||||
def get_accessible_libraries(self, trans, user):
|
||||
def get_accessible_libraries(self, trans: "ProvidesAppContext", user):
|
||||
raise Exception("Unimplemented Method")
|
||||
|
||||
def get_permitted_libraries(self, trans, user, actions):
|
||||
def get_permitted_libraries(self, trans: "ProvidesAppContext", user, actions):
|
||||
raise Exception("Unimplemented Method")
|
||||
|
||||
def folder_is_public(self, library):
|
||||
@@ -144,13 +151,13 @@ class RBACAgent:
|
||||
def get_permissions(self, library_dataset):
|
||||
raise Exception("Unimplemented Method")
|
||||
|
||||
def get_all_roles(self, trans, cntrller):
|
||||
def get_all_roles(self, trans: "ProvidesUserContext", cntrller):
|
||||
raise Exception("Unimplemented Method")
|
||||
|
||||
def get_legitimate_roles(self, trans, item, cntrller):
|
||||
def get_legitimate_roles(self, trans: "ProvidesUserContext", item, cntrller):
|
||||
raise Exception("Unimplemented Method")
|
||||
|
||||
def derive_roles_from_access(self, trans, item_id, cntrller, library=False, **kwd):
|
||||
def derive_roles_from_access(self, trans: "ProvidesUserContext", item_id, cntrller, library=False, **kwd):
|
||||
raise Exception("Unimplemented Method")
|
||||
|
||||
def get_component_associations(self, **kwd):
|
||||
|
||||
@@ -7,6 +7,11 @@ user inputs - so these methods do not need to be escaped.
|
||||
|
||||
import logging
|
||||
import re
|
||||
from typing import (
|
||||
Any,
|
||||
Protocol,
|
||||
TYPE_CHECKING,
|
||||
)
|
||||
|
||||
import dns.resolver
|
||||
from dns.exception import DNSException
|
||||
@@ -18,6 +23,26 @@ from typing_extensions import LiteralString
|
||||
|
||||
from galaxy.objectstore import ObjectStore
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from galaxy.model import User
|
||||
|
||||
|
||||
class UserValidationContext(Protocol):
|
||||
"""What the user input validators need from a transaction.
|
||||
|
||||
Galaxy and the tool shed keep separate context hierarchies with their own
|
||||
app, config and User classes. Both offer a session and an app, so the
|
||||
validators ask for that much rather than naming either hierarchy and
|
||||
forcing one side to depend on the other.
|
||||
"""
|
||||
|
||||
@property
|
||||
def app(self) -> Any: ...
|
||||
|
||||
@property
|
||||
def sa_session(self) -> Any: ...
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# Email validity parameters
|
||||
@@ -75,7 +100,9 @@ def validate_publicname_str(publicname):
|
||||
return ""
|
||||
|
||||
|
||||
def validate_email(trans, email, user=None, check_dup=True, allow_empty=False, validate_domain=False):
|
||||
def validate_email(
|
||||
trans: UserValidationContext, email, user=None, check_dup=True, allow_empty=False, validate_domain=False
|
||||
):
|
||||
"""
|
||||
Validates the email format.
|
||||
Checks whether the domain is blocklisted in the disposable domains configuration.
|
||||
@@ -134,7 +161,7 @@ def extract_domain(email, base_only=False):
|
||||
return domain
|
||||
|
||||
|
||||
def validate_publicname(trans, publicname, user=None):
|
||||
def validate_publicname(trans: UserValidationContext, publicname, user=None):
|
||||
"""
|
||||
Check that publicname respects the minimum and maximum string length, the
|
||||
allowed characters, and that the username is not taken already.
|
||||
@@ -165,14 +192,16 @@ def transform_publicname(publicname):
|
||||
return publicname
|
||||
|
||||
|
||||
def validate_password(trans, password, confirm):
|
||||
def validate_password(trans: UserValidationContext, password, confirm):
|
||||
if password != confirm:
|
||||
return "Passwords do not match."
|
||||
return validate_password_str(password)
|
||||
|
||||
|
||||
def validate_preferred_object_store_id(trans, object_store: ObjectStore, preferred_object_store_id: str | None) -> str:
|
||||
return object_store.validate_selected_object_store_id(trans.user, preferred_object_store_id) or ""
|
||||
def validate_preferred_object_store_id(
|
||||
user: "User | None", object_store: ObjectStore, preferred_object_store_id: str | None
|
||||
) -> str:
|
||||
return object_store.validate_selected_object_store_id(user, preferred_object_store_id) or ""
|
||||
|
||||
|
||||
def is_email_banned(email: str, filepath: str | None, canonical_email_rules: dict | None) -> bool:
|
||||
|
||||
@@ -122,6 +122,7 @@ class MinimalApp(BasicSharedApp):
|
||||
class MinimalManagerApp(MinimalApp):
|
||||
# Minimal App that is sufficient to run Celery tasks
|
||||
amqp_internal_connection_obj: Connection | None
|
||||
vault: Vault
|
||||
execution_timer_factory: "ExecutionTimerFactory"
|
||||
carbon_intensity: float
|
||||
file_sources: ConfiguredFileSources
|
||||
@@ -174,7 +175,6 @@ class StructuredApp(MinimalManagerApp):
|
||||
tool_dependency_dir: str | None
|
||||
test_data_resolver: test_data.TestDataResolver
|
||||
trs_proxy: TrsProxy
|
||||
vault: Vault
|
||||
webhooks_registry: WebhooksRegistry
|
||||
queue_worker: Any # 'galaxy.queue_worker.GalaxyQueueWorker'
|
||||
data_provider_registry: Any # 'galaxy.visualization.data_providers.registry.DataProviderRegistry'
|
||||
|
||||
@@ -3,6 +3,7 @@ import re
|
||||
from urllib.parse import quote
|
||||
|
||||
from galaxy import util
|
||||
from galaxy.managers.context import ProvidesAppContext
|
||||
from galaxy.tool_shed.util import repository_util
|
||||
from galaxy.util.tool_shed import common_util
|
||||
|
||||
@@ -29,7 +30,7 @@ def can_eliminate_repository_dependency(metadata_dict, tool_shed_url, name, owne
|
||||
return True
|
||||
|
||||
|
||||
def clean_dependency_relationships(trans, metadata_dict, tool_shed_repository, tool_shed_url):
|
||||
def clean_dependency_relationships(trans: ProvidesAppContext, metadata_dict, tool_shed_repository, tool_shed_url):
|
||||
"""
|
||||
Repositories of type tool_dependency_definition allow for defining a
|
||||
package dependency at some point in the change log and then removing the
|
||||
|
||||
@@ -327,6 +327,12 @@ class ToolDataTable(Dictifiable):
|
||||
def is_current_version(self, other_version):
|
||||
return self._loaded_content_version == other_version
|
||||
|
||||
def get_fields(self) -> list[list[str]]:
|
||||
raise NotImplementedError("Abstract method")
|
||||
|
||||
def get_named_fields_list(self) -> list[dict[str | int, str]]:
|
||||
raise NotImplementedError("Abstract method")
|
||||
|
||||
def merge_tool_data_table(
|
||||
self,
|
||||
other_table: "ToolDataTable",
|
||||
|
||||
@@ -12,6 +12,7 @@ from typing import (
|
||||
Literal,
|
||||
Optional,
|
||||
TYPE_CHECKING,
|
||||
TypeAlias,
|
||||
Union,
|
||||
)
|
||||
from urllib.parse import urlparse
|
||||
@@ -70,6 +71,10 @@ from .views.interface import (
|
||||
from .views.static import StaticToolPanelView
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from galaxy.managers.context import (
|
||||
ProvidesHistoryContext,
|
||||
ProvidesUserContext,
|
||||
)
|
||||
from galaxy.model import (
|
||||
DynamicTool,
|
||||
User,
|
||||
@@ -78,6 +83,11 @@ if TYPE_CHECKING:
|
||||
from galaxy.model.tool_shed_install import ToolShedRepository
|
||||
from galaxy.tools import Tool
|
||||
from galaxy.tools.cache import ToolCache
|
||||
from galaxy.webapps.base.webapp import GalaxyWebTransaction
|
||||
from galaxy.work.context import SessionRequestContext
|
||||
|
||||
# both web transactions and FastAPI/agents request contexts render tool panels
|
||||
PanelViewTrans: TypeAlias = "GalaxyWebTransaction | SessionRequestContext"
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -315,7 +325,7 @@ class AbstractToolBox(ManagesIntegratedToolPanelMixin):
|
||||
if save_integrated_tool_panel:
|
||||
self._save_integrated_tool_panel()
|
||||
|
||||
def _default_panel_view(self, trans):
|
||||
def _default_panel_view(self, trans: "PanelViewTrans"):
|
||||
config = self.app.config
|
||||
if hasattr(config, "config_value_for_host"):
|
||||
config_value = config.config_value_for_host("default_panel_view", trans.host)
|
||||
@@ -1366,7 +1376,7 @@ class AbstractToolBox(ManagesIntegratedToolPanelMixin):
|
||||
self._tool_edam_topics = self._collect_tool_attribute_set("edam_topics")
|
||||
return self._tool_edam_topics
|
||||
|
||||
def package_tool(self, trans, tool_id):
|
||||
def package_tool(self, trans: "GalaxyWebTransaction", tool_id):
|
||||
"""
|
||||
Create a tarball with the tool's xml, help images, and test data.
|
||||
:param trans: the web transaction
|
||||
@@ -1471,7 +1481,7 @@ class AbstractToolBox(ManagesIntegratedToolPanelMixin):
|
||||
tool_panel_section_id = ""
|
||||
return tool_panel_section_id
|
||||
|
||||
def tool_panel_contents(self, trans, view=None, **kwds):
|
||||
def tool_panel_contents(self, trans: "PanelViewTrans", view=None, **kwds):
|
||||
"""Filter tool_panel contents for displaying for user."""
|
||||
if view is None:
|
||||
view = self._default_panel_view(trans)
|
||||
@@ -1495,7 +1505,9 @@ class AbstractToolBox(ManagesIntegratedToolPanelMixin):
|
||||
if elt:
|
||||
yield elt
|
||||
|
||||
def get_tool_to_dict(self, trans, tool: "Tool", tool_help: bool = False) -> dict[str, Any]:
|
||||
def get_tool_to_dict(
|
||||
self, trans: "ProvidesHistoryContext", tool: "Tool", tool_help: bool = False
|
||||
) -> dict[str, Any]:
|
||||
"""Return tool's panel payload.
|
||||
Use cache if present, store to cache otherwise.
|
||||
Note: The cached payload is specific to the calls from toolbox.
|
||||
@@ -1524,7 +1536,7 @@ class AbstractToolBox(ManagesIntegratedToolPanelMixin):
|
||||
|
||||
def to_dict(
|
||||
self,
|
||||
trans,
|
||||
trans: "PanelViewTrans",
|
||||
in_panel: bool = True,
|
||||
tool_help: bool = False,
|
||||
view: str | None = None,
|
||||
@@ -1553,7 +1565,7 @@ class AbstractToolBox(ManagesIntegratedToolPanelMixin):
|
||||
rval.append(self.get_tool_to_dict(trans, tool, tool_help=tool_help))
|
||||
return rval
|
||||
|
||||
def to_panel_view(self, trans, view="default_panel_view", **kwds):
|
||||
def to_panel_view(self, trans: "PanelViewTrans", view="default_panel_view", **kwds):
|
||||
"""
|
||||
Create a panel view representation of the toolbox.
|
||||
Uses the structure:
|
||||
@@ -1602,7 +1614,7 @@ class AbstractToolBox(ManagesIntegratedToolPanelMixin):
|
||||
else:
|
||||
return self._tool_versions_by_id.get(lineage_tool_version.id, {}).get(lineage_tool_version.version)
|
||||
|
||||
def _build_filter_method(self, trans):
|
||||
def _build_filter_method(self, trans: "ProvidesUserContext"):
|
||||
context = Bunch(toolbox=self, trans=trans)
|
||||
filters = self._filter_factory.build_filters(trans)
|
||||
return lambda element, item_type: _filter_for_panel(element, item_type, filters, context)
|
||||
|
||||
@@ -4,10 +4,14 @@ from copy import deepcopy
|
||||
from typing import (
|
||||
Protocol,
|
||||
runtime_checkable,
|
||||
TYPE_CHECKING,
|
||||
)
|
||||
|
||||
from galaxy.util import listify
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from galaxy.managers.context import ProvidesUserContext
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -63,7 +67,7 @@ class FilterFactory:
|
||||
self.__init_filters("section", getattr(config, "tool_section_filters", ""), self.default_filters)
|
||||
self.__init_filters("label", getattr(config, "tool_label_filters", ""), self.default_filters)
|
||||
|
||||
def build_filters(self, trans, **kwds):
|
||||
def build_filters(self, trans: "ProvidesUserContext", **kwds):
|
||||
"""
|
||||
Build list of filters to check tools against given current context.
|
||||
"""
|
||||
|
||||
@@ -10,6 +10,7 @@ from galaxy.util.odict import odict
|
||||
from .parser import ensure_tool_conf_item
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from galaxy.managers.context import ProvidesHistoryContext
|
||||
from galaxy.tools import Tool
|
||||
|
||||
|
||||
@@ -99,7 +100,9 @@ class ToolSection(UsesDictVisibleKeys, HasPanelItems):
|
||||
|
||||
return copy
|
||||
|
||||
def to_dict(self, trans, link_details=False, tool_help=False, toolbox=None, only_ids=False):
|
||||
def to_dict(
|
||||
self, trans: "ProvidesHistoryContext", link_details=False, tool_help=False, toolbox=None, only_ids=False
|
||||
):
|
||||
"""Return a dict that includes section's attributes.
|
||||
|
||||
if `only_ids` is `True`, we store only the ids of the section's tools in `section.tools`
|
||||
|
||||
@@ -267,7 +267,11 @@ if TYPE_CHECKING:
|
||||
from galaxy.app import UniverseApplication
|
||||
from galaxy.jobs import JobToolConfiguration
|
||||
from galaxy.jobs.job_destination import JobDestination
|
||||
from galaxy.managers.context import ProvidesUserContext
|
||||
from galaxy.managers.context import (
|
||||
ProvidesAppContext,
|
||||
ProvidesHistoryContext,
|
||||
ProvidesUserContext,
|
||||
)
|
||||
from galaxy.managers.jobs import JobSearch
|
||||
from galaxy.model import (
|
||||
DynamicTool,
|
||||
@@ -760,7 +764,7 @@ class DefaultToolState:
|
||||
self.rerun_remap_job_id = None
|
||||
self.inputs = {}
|
||||
|
||||
def initialize(self, trans, tool):
|
||||
def initialize(self, trans: "ProvidesHistoryContext", tool):
|
||||
"""
|
||||
Create a new `DefaultToolState` for this tool. It will be initialized
|
||||
with default values for inputs. Grouping elements are filled in recursively.
|
||||
@@ -2102,7 +2106,7 @@ class Tool(UsesDictVisibleKeys, MaybeToolParameterBundle):
|
||||
# outputs?
|
||||
return True
|
||||
|
||||
def new_state(self, trans):
|
||||
def new_state(self, trans: "ProvidesHistoryContext"):
|
||||
"""
|
||||
Create a new `DefaultToolState` for this tool. It will be initialized
|
||||
with default values for inputs. Grouping elements are filled in recursively.
|
||||
@@ -2333,7 +2337,7 @@ class Tool(UsesDictVisibleKeys, MaybeToolParameterBundle):
|
||||
|
||||
def completed_jobs(
|
||||
self,
|
||||
trans,
|
||||
trans: "ProvidesUserContext",
|
||||
use_cached_job: bool,
|
||||
all_params: list[ToolStateJobInstancePopulatedT],
|
||||
) -> dict[int, Job | None]:
|
||||
@@ -2391,7 +2395,7 @@ class Tool(UsesDictVisibleKeys, MaybeToolParameterBundle):
|
||||
|
||||
def handle_input(
|
||||
self,
|
||||
trans,
|
||||
trans: "ProvidesHistoryContext",
|
||||
incoming: ToolRequestT,
|
||||
history: History | None = None,
|
||||
use_cached_job: bool = DEFAULT_USE_CACHED_JOB,
|
||||
@@ -2480,7 +2484,7 @@ class Tool(UsesDictVisibleKeys, MaybeToolParameterBundle):
|
||||
|
||||
def handle_single_execution(
|
||||
self,
|
||||
trans,
|
||||
trans: "ProvidesHistoryContext",
|
||||
rerun_remap_job_id: int | None,
|
||||
execution_slice: ExecutionSlice,
|
||||
history: History,
|
||||
@@ -2569,7 +2573,7 @@ class Tool(UsesDictVisibleKeys, MaybeToolParameterBundle):
|
||||
params.append(input_param)
|
||||
return params
|
||||
|
||||
def get_static_param_values(self, trans):
|
||||
def get_static_param_values(self, trans: "ProvidesHistoryContext"):
|
||||
"""
|
||||
Returns a map of parameter names and values if the tool does not
|
||||
require any user input. Will raise an exception if any parameter
|
||||
@@ -2589,7 +2593,7 @@ class Tool(UsesDictVisibleKeys, MaybeToolParameterBundle):
|
||||
|
||||
def execute(
|
||||
self,
|
||||
trans,
|
||||
trans: "ProvidesHistoryContext",
|
||||
incoming: ToolStateJobInstancePopulatedT | None = None,
|
||||
history: History | None = None,
|
||||
set_output_hid: bool = DEFAULT_SET_OUTPUT_HID,
|
||||
@@ -2617,7 +2621,7 @@ class Tool(UsesDictVisibleKeys, MaybeToolParameterBundle):
|
||||
|
||||
def _execute(
|
||||
self,
|
||||
trans,
|
||||
trans: "ProvidesHistoryContext",
|
||||
incoming: ToolStateJobInstancePopulatedT | None = None,
|
||||
validated_parameters: JobInternalToolState | None = None,
|
||||
history: History | None = None,
|
||||
@@ -2678,7 +2682,7 @@ class Tool(UsesDictVisibleKeys, MaybeToolParameterBundle):
|
||||
return self.params_from_strings(param_dict, ignore_errors=ignore_errors)
|
||||
|
||||
def check_and_update_param_values(
|
||||
self, values, trans, update_values: bool = True, workflow_building_mode: bool = False
|
||||
self, values, trans: "ProvidesHistoryContext", update_values: bool = True, workflow_building_mode: bool = False
|
||||
):
|
||||
"""
|
||||
Check that all parameters have values, and fill in with default
|
||||
@@ -3000,7 +3004,7 @@ class Tool(UsesDictVisibleKeys, MaybeToolParameterBundle):
|
||||
os.remove(temp_file)
|
||||
return tarball_archive
|
||||
|
||||
def to_panel_entry(self, trans) -> dict[str, Any]:
|
||||
def to_panel_entry(self, trans: "ProvidesUserContext") -> dict[str, Any]:
|
||||
"""The complete per-tool panel/listing payload — see
|
||||
:class:`galaxy.tool_util.toolbox.entry.ToolPanelEntry` for the
|
||||
contract. ``to_dict`` layers io/help extras on top of this.
|
||||
@@ -3041,7 +3045,7 @@ class Tool(UsesDictVisibleKeys, MaybeToolParameterBundle):
|
||||
kwargs["config_file"] = None if not self.config_file else os.path.abspath(self.config_file)
|
||||
return ToolPanelEntry(**kwargs).model_dump(exclude_unset=True)
|
||||
|
||||
def to_dict(self, trans, link_details=False, io_details=False, tool_help=False):
|
||||
def to_dict(self, trans: "ProvidesHistoryContext", link_details=False, io_details=False, tool_help=False):
|
||||
"""Returns dict of tool.
|
||||
|
||||
``link_details`` is accepted for backwards compatibility and no
|
||||
@@ -3072,7 +3076,7 @@ class Tool(UsesDictVisibleKeys, MaybeToolParameterBundle):
|
||||
|
||||
def to_json(
|
||||
self,
|
||||
trans,
|
||||
trans: "ProvidesHistoryContext",
|
||||
kwd=None,
|
||||
job: Job | None = None,
|
||||
workflow_building_mode=False,
|
||||
@@ -3093,7 +3097,7 @@ class Tool(UsesDictVisibleKeys, MaybeToolParameterBundle):
|
||||
or workflow_building_mode is workflow_building_modes.DISABLED
|
||||
):
|
||||
# We don't need a history when exporting a workflow for the workflow editor or when downloading a workflow
|
||||
history = history or trans.get_history()
|
||||
history = history or trans.history
|
||||
if history is None and job is not None:
|
||||
assert job.history
|
||||
history = self.history_manager.get_owned(job.history.id, trans.user, current_history=trans.history)
|
||||
@@ -3319,7 +3323,7 @@ class Tool(UsesDictVisibleKeys, MaybeToolParameterBundle):
|
||||
raise exceptions.MessageException(unicodify(e))
|
||||
return message
|
||||
|
||||
def get_default_history_by_trans(self, trans, create=False):
|
||||
def get_default_history_by_trans(self, trans: "ProvidesHistoryContext", create=False):
|
||||
return trans.get_history(create=create)
|
||||
|
||||
@classmethod
|
||||
@@ -3832,7 +3836,7 @@ class DataManagerTool(OutputParameterJSONTool):
|
||||
else:
|
||||
raise Exception("Unknown data manager mode encountered type...")
|
||||
|
||||
def get_default_history_by_trans(self, trans, create=False):
|
||||
def get_default_history_by_trans(self, trans: "ProvidesHistoryContext", create=False):
|
||||
def _create_data_manager_history(user):
|
||||
history = History(name="Data Manager History (automatically created)", user=user)
|
||||
data_manager_association = model.DataManagerHistoryAssociation(user=user, history=history)
|
||||
@@ -3967,7 +3971,7 @@ class UnzipCollectionTool(DatabaseOperationTool):
|
||||
require_terminal_states = False
|
||||
require_dataset_ok = False
|
||||
|
||||
def produce_outputs(self, trans, out_data, output_collections, incoming, history, **kwds):
|
||||
def produce_outputs(self, trans: "ProvidesUserContext", out_data, output_collections, incoming, history, **kwds):
|
||||
has_collection = incoming["input"]
|
||||
if hasattr(has_collection, "element_type"):
|
||||
# It is a DCE
|
||||
@@ -3993,7 +3997,7 @@ class ZipCollectionTool(DatabaseOperationTool):
|
||||
require_terminal_states = False
|
||||
require_dataset_ok = False
|
||||
|
||||
def produce_outputs(self, trans, out_data, output_collections, incoming, history, **kwds):
|
||||
def produce_outputs(self, trans: "ProvidesUserContext", out_data, output_collections, incoming, history, **kwds):
|
||||
forward_o = incoming["input_forward"]
|
||||
reverse_o = incoming["input_reverse"]
|
||||
|
||||
@@ -4015,7 +4019,7 @@ class CrossProductFlatCollectionTool(DatabaseOperationTool):
|
||||
require_terminal_states = False
|
||||
require_dataset_ok = False
|
||||
|
||||
def produce_outputs(self, trans, out_data, output_collections, incoming, history, **kwds):
|
||||
def produce_outputs(self, trans: "ProvidesUserContext", out_data, output_collections, incoming, history, **kwds):
|
||||
input_a = incoming["input_a"]
|
||||
input_b = incoming["input_b"]
|
||||
join_identifier = incoming["join_identifier"]
|
||||
@@ -4051,7 +4055,7 @@ class CrossProductNestedCollectionTool(DatabaseOperationTool):
|
||||
require_terminal_states = False
|
||||
require_dataset_ok = False
|
||||
|
||||
def produce_outputs(self, trans, out_data, output_collections, incoming, history, **kwds):
|
||||
def produce_outputs(self, trans: "ProvidesUserContext", out_data, output_collections, incoming, history, **kwds):
|
||||
input_a = incoming["input_a"]
|
||||
input_b = incoming["input_b"]
|
||||
|
||||
@@ -4103,7 +4107,7 @@ class BuildListCollectionTool(DatabaseOperationTool):
|
||||
require_terminal_states = False
|
||||
require_dataset_ok = False
|
||||
|
||||
def produce_outputs(self, trans, out_data, output_collections, incoming, history, **kwds):
|
||||
def produce_outputs(self, trans: "ProvidesUserContext", out_data, output_collections, incoming, history, **kwds):
|
||||
new_elements = {}
|
||||
|
||||
for i, incoming_repeat in enumerate(incoming["datasets"]):
|
||||
@@ -4134,7 +4138,7 @@ class SplitPairedAndUnpairedTool(DatabaseOperationTool):
|
||||
require_terminal_states = False
|
||||
require_dataset_ok = False
|
||||
|
||||
def produce_outputs(self, trans, out_data, output_collections, incoming, history, **kwds):
|
||||
def produce_outputs(self, trans: "ProvidesUserContext", out_data, output_collections, incoming, history, **kwds):
|
||||
has_collection = incoming["input"]
|
||||
if hasattr(has_collection, "element_type"):
|
||||
# It is a DCE
|
||||
@@ -4218,7 +4222,7 @@ class ExtractDatasetCollectionTool(DatabaseOperationTool):
|
||||
require_terminal_states = False
|
||||
require_dataset_ok = False
|
||||
|
||||
def produce_outputs(self, trans, out_data, output_collections, incoming, history, **kwds):
|
||||
def produce_outputs(self, trans: "ProvidesUserContext", out_data, output_collections, incoming, history, **kwds):
|
||||
has_collection = incoming["input"]
|
||||
if hasattr(has_collection, "element_type"):
|
||||
# It is a DCE
|
||||
@@ -4261,7 +4265,7 @@ class MergeCollectionTool(DatabaseOperationTool):
|
||||
require_terminal_states = False
|
||||
require_dataset_ok = False
|
||||
|
||||
def produce_outputs(self, trans, out_data, output_collections, incoming, history, **kwds):
|
||||
def produce_outputs(self, trans: "ProvidesUserContext", out_data, output_collections, incoming, history, **kwds):
|
||||
input_lists = []
|
||||
|
||||
for incoming_repeat in incoming["inputs"]:
|
||||
@@ -4370,7 +4374,7 @@ class FilterDatasetsTool(DatabaseOperationTool):
|
||||
assert isinstance(element_object, model.DatasetInstance)
|
||||
return element_object.is_ok
|
||||
|
||||
def produce_outputs(self, trans, out_data, output_collections, incoming, history, **kwds):
|
||||
def produce_outputs(self, trans: "ProvidesUserContext", out_data, output_collections, incoming, history, **kwds):
|
||||
collection = incoming["input"]
|
||||
replacement_dataset = incoming.get("replacement")
|
||||
if hasattr(collection, "element_object"):
|
||||
@@ -4493,7 +4497,7 @@ class FlattenTool(DatabaseOperationTool):
|
||||
require_terminal_states = False
|
||||
require_dataset_ok = False
|
||||
|
||||
def produce_outputs(self, trans, out_data, output_collections, incoming, history, **kwds):
|
||||
def produce_outputs(self, trans: "ProvidesUserContext", out_data, output_collections, incoming, history, **kwds):
|
||||
hdca = incoming["input"]
|
||||
join_identifier = incoming["join_identifier"]
|
||||
new_elements = {}
|
||||
@@ -4531,7 +4535,7 @@ class NestTool(DatabaseOperationTool):
|
||||
require_terminal_states = False
|
||||
require_dataset_ok = False
|
||||
|
||||
def produce_outputs(self, trans, out_data, output_collections, incoming, history, **kwds):
|
||||
def produce_outputs(self, trans: "ProvidesUserContext", out_data, output_collections, incoming, history, **kwds):
|
||||
hdca = incoming["input"]
|
||||
new_elements = {}
|
||||
copied_datasets = []
|
||||
@@ -4563,7 +4567,7 @@ class SortTool(DatabaseOperationTool):
|
||||
require_terminal_states = True
|
||||
require_dataset_ok = False
|
||||
|
||||
def produce_outputs(self, trans, out_data, output_collections, incoming, history, **kwds):
|
||||
def produce_outputs(self, trans: "ProvidesUserContext", out_data, output_collections, incoming, history, **kwds):
|
||||
hdca = incoming["input"]
|
||||
sorttype = incoming["sort_type"]["sort_type"]
|
||||
new_elements = {}
|
||||
@@ -4631,7 +4635,7 @@ class HarmonizeTool(DatabaseOperationTool):
|
||||
}
|
||||
super().check_inputs_ready(input_datasets, filtered_collections, security)
|
||||
|
||||
def produce_outputs(self, trans, out_data, output_collections, incoming, history, **kwds):
|
||||
def produce_outputs(self, trans: "ProvidesUserContext", out_data, output_collections, incoming, history, **kwds):
|
||||
hdca1 = incoming["input1"]
|
||||
hdca2 = incoming.get("input2")
|
||||
elements1 = hdca1.collection.elements
|
||||
@@ -4690,7 +4694,9 @@ class HarmonizeTool(DatabaseOperationTool):
|
||||
output_with_selected_identifiers(old_elements1_dict, "output1")
|
||||
output_with_selected_identifiers(old_elements2_dict, "output2")
|
||||
|
||||
def _produce_outputs_with_optional_nulls(self, trans, output_collections, hdca1, elements1, history):
|
||||
def _produce_outputs_with_optional_nulls(
|
||||
self, trans: "ProvidesUserContext", output_collections, hdca1, elements1, history
|
||||
):
|
||||
"""When input2 is not provided, output1 is a copy of input1 and output2
|
||||
mirrors input1's structure but with expression.json null datasets."""
|
||||
object_store_populator = ObjectStorePopulator(trans.app, trans.user)
|
||||
@@ -4738,7 +4744,7 @@ class HarmonizeTool(DatabaseOperationTool):
|
||||
propagate_hda_tags=False,
|
||||
)
|
||||
|
||||
def _create_null_dataset(self, trans, history, object_store_populator):
|
||||
def _create_null_dataset(self, trans: "ProvidesUserContext", history, object_store_populator):
|
||||
"""Create a new HDA with expression.json null content (skipped marker)."""
|
||||
null_hda = HistoryDatasetAssociation(
|
||||
extension="expression.json",
|
||||
@@ -4763,7 +4769,7 @@ class HarmonizeTool(DatabaseOperationTool):
|
||||
class RelabelFromFileTool(DatabaseOperationTool):
|
||||
tool_type = "relabel_from_file"
|
||||
|
||||
def produce_outputs(self, trans, out_data, output_collections, incoming, history, **kwds):
|
||||
def produce_outputs(self, trans: "ProvidesUserContext", out_data, output_collections, incoming, history, **kwds):
|
||||
hdca = incoming["input"]
|
||||
how_type = incoming["how"]["how_select"]
|
||||
new_labels_dataset_assoc = incoming["how"]["labels"]
|
||||
@@ -4846,7 +4852,7 @@ class RelabelFromFileTool(DatabaseOperationTool):
|
||||
class ApplyRulesTool(DatabaseOperationTool):
|
||||
tool_type = "apply_rules"
|
||||
|
||||
def produce_outputs(self, trans, out_data, output_collections, incoming, history, **kwds):
|
||||
def produce_outputs(self, trans: "ProvidesUserContext", out_data, output_collections, incoming, history, **kwds):
|
||||
hdca = incoming["input"]
|
||||
rule_set = RuleSet(incoming["rules"])
|
||||
copied_datasets = []
|
||||
@@ -4884,7 +4890,7 @@ class TagFromFileTool(DatabaseOperationTool):
|
||||
# require_terminal_states = True
|
||||
# require_dataset_ok = False
|
||||
|
||||
def produce_outputs(self, trans, out_data, output_collections, incoming, history, **kwds):
|
||||
def produce_outputs(self, trans: "ProvidesUserContext", out_data, output_collections, incoming, history, **kwds):
|
||||
hdca = incoming["input"]
|
||||
how = incoming["how"]
|
||||
new_tags_dataset_assoc = incoming["tags"]
|
||||
@@ -4961,7 +4967,7 @@ class TagFromFileTool(DatabaseOperationTool):
|
||||
class FilterFromFileTool(DatabaseOperationTool):
|
||||
tool_type = "filter_from_file"
|
||||
|
||||
def produce_outputs(self, trans, out_data, output_collections, incoming, history, **kwds):
|
||||
def produce_outputs(self, trans: "ProvidesUserContext", out_data, output_collections, incoming, history, **kwds):
|
||||
hdca = incoming["input"]
|
||||
how_filter = incoming["how"]["how_filter"]
|
||||
filter_dataset_assoc = incoming["how"]["filter_source"]
|
||||
@@ -5017,7 +5023,7 @@ class DuplicateFileToCollectionTool(DatabaseOperationTool):
|
||||
require_terminal_states = False
|
||||
require_dataset_ok = False
|
||||
|
||||
def produce_outputs(self, trans, out_data, output_collections, incoming, history, **kwds):
|
||||
def produce_outputs(self, trans: "ProvidesUserContext", out_data, output_collections, incoming, history, **kwds):
|
||||
hda = incoming["input"]
|
||||
number = int(incoming["number"])
|
||||
element_identifier = incoming["element_identifier"]
|
||||
@@ -5042,7 +5048,7 @@ class ConvertSampleSheetTool(DatabaseOperationTool):
|
||||
require_terminal_states = False
|
||||
require_dataset_ok = False
|
||||
|
||||
def produce_outputs(self, trans, out_data, output_collections, incoming, history, **kwds):
|
||||
def produce_outputs(self, trans: "ProvidesUserContext", out_data, output_collections, incoming, history, **kwds):
|
||||
has_collection = incoming["input"]
|
||||
if hasattr(has_collection, "element_type"):
|
||||
# It is a DCE
|
||||
@@ -5117,7 +5123,7 @@ tool_types = {tool_class.tool_type: tool_class for tool_class in TOOL_CLASSES}
|
||||
# ---- Utility classes to be factored out -----------------------------------
|
||||
|
||||
|
||||
def _rerun_remap_job_id(trans, incoming, tool_id: str | None) -> int | None:
|
||||
def _rerun_remap_job_id(trans: "ProvidesAppContext", incoming, tool_id: str | None) -> int | None:
|
||||
rerun_remap_job_id = None
|
||||
if "rerun_remap_job_id" in incoming:
|
||||
try:
|
||||
|
||||
@@ -24,7 +24,10 @@ from galaxy.exceptions import (
|
||||
ToolInputsNotReadyException,
|
||||
)
|
||||
from galaxy.job_execution.actions.post import ActionBox
|
||||
from galaxy.managers.context import ProvidesHistoryContext
|
||||
from galaxy.managers.context import (
|
||||
ProvidesHistoryContext,
|
||||
ProvidesUserContext,
|
||||
)
|
||||
from galaxy.model import (
|
||||
Dataset,
|
||||
History,
|
||||
@@ -79,6 +82,7 @@ if TYPE_CHECKING:
|
||||
)
|
||||
from galaxy.tool_util.parser.output_objects import ToolOutput
|
||||
from galaxy.tools import Tool
|
||||
from galaxy.webapps.base.webapp import GalaxyWebTransaction
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -97,7 +101,7 @@ class ToolAction:
|
||||
def execute(
|
||||
self,
|
||||
tool: "Tool",
|
||||
trans,
|
||||
trans: ProvidesHistoryContext,
|
||||
incoming: ToolStateJobInstancePopulatedT | None = None,
|
||||
history: History | None = None,
|
||||
job_params=None,
|
||||
@@ -122,7 +126,7 @@ class ToolAction:
|
||||
dataset=None,
|
||||
tool=None,
|
||||
on_text=None,
|
||||
trans=None,
|
||||
trans: ProvidesHistoryContext | None = None,
|
||||
incoming=None,
|
||||
history=None,
|
||||
params=None,
|
||||
@@ -398,10 +402,12 @@ class DefaultToolAction(ToolAction):
|
||||
tool.visit_inputs(param_values, visitor)
|
||||
return input_dataset_collections
|
||||
|
||||
def _check_access(self, tool, trans):
|
||||
def _check_access(self, tool, trans: ProvidesUserContext):
|
||||
assert tool.allow_user_access(trans.user), f"User ({trans.user}) is not allowed to access this tool."
|
||||
|
||||
def _collect_inputs(self, tool, trans, incoming, history, current_user_roles, collection_info):
|
||||
def _collect_inputs(
|
||||
self, tool, trans: ProvidesHistoryContext, incoming, history, current_user_roles, collection_info
|
||||
):
|
||||
"""Collect history as well as input datasets and collections."""
|
||||
# Set history.
|
||||
if not history:
|
||||
@@ -442,7 +448,7 @@ class DefaultToolAction(ToolAction):
|
||||
def execute(
|
||||
self,
|
||||
tool: "Tool",
|
||||
trans,
|
||||
trans: ProvidesHistoryContext,
|
||||
incoming: ToolStateJobInstancePopulatedT | None = None,
|
||||
history: History | None = None,
|
||||
job_params=None,
|
||||
@@ -797,7 +803,7 @@ class DefaultToolAction(ToolAction):
|
||||
job.info = f"Redirected to: {redirect_url}"
|
||||
trans.sa_session.add(job)
|
||||
trans.sa_session.commit()
|
||||
trans.response.send_redirect(redirect_url)
|
||||
cast("GalaxyWebTransaction", trans).response.send_redirect(redirect_url)
|
||||
else:
|
||||
if flush_job:
|
||||
# Set HID and add to history.
|
||||
@@ -920,7 +926,7 @@ class DefaultToolAction(ToolAction):
|
||||
|
||||
def _wrapped_params(
|
||||
self,
|
||||
trans,
|
||||
trans: ProvidesHistoryContext,
|
||||
tool: "Tool",
|
||||
incoming: "ToolStateJobInstancePopulatedT",
|
||||
input_datasets: LegacyUnprefixedDict | None = None,
|
||||
@@ -950,7 +956,7 @@ class DefaultToolAction(ToolAction):
|
||||
return on_text_for_dataset_and_collections(dataset_hids=input_hids, collection_hids=collection_hids)
|
||||
|
||||
def _new_job_for_session(
|
||||
self, trans, tool: "Tool", history: History | None
|
||||
self, trans: ProvidesHistoryContext, tool: "Tool", history: History | None
|
||||
) -> tuple[Job, model.GalaxySession | None]:
|
||||
job = Job()
|
||||
job.galaxy_version = trans.app.config.version_major
|
||||
@@ -995,7 +1001,7 @@ class DefaultToolAction(ToolAction):
|
||||
)
|
||||
sa_session.add(association)
|
||||
|
||||
def _record_inputs(self, trans, tool, job, incoming, inp_data, inp_dataset_collections):
|
||||
def _record_inputs(self, trans: ProvidesHistoryContext, tool, job, incoming, inp_data, inp_dataset_collections):
|
||||
# FIXME: Don't need all of incoming here, just the defined parameters
|
||||
# from the tool. We need to deal with tools that pass all post
|
||||
# parameters to the command as a special case.
|
||||
@@ -1070,7 +1076,7 @@ class DefaultToolAction(ToolAction):
|
||||
job.add_output_dataset_collection(name, dataset_collection_instance)
|
||||
dataset_collection_instance.job = job
|
||||
|
||||
def _record_input_datasets(self, trans, job, inp_data):
|
||||
def _record_input_datasets(self, trans: ProvidesHistoryContext, job, inp_data):
|
||||
for name, dataset in inp_data.items():
|
||||
# TODO: figure out why can't pass dataset_id here.
|
||||
job.add_input_dataset(name, dataset=dataset)
|
||||
@@ -1081,7 +1087,7 @@ class DefaultToolAction(ToolAction):
|
||||
dataset=None,
|
||||
tool=None,
|
||||
on_text=None,
|
||||
trans=None,
|
||||
trans: ProvidesHistoryContext | None = None,
|
||||
incoming=None,
|
||||
history=None,
|
||||
params=None,
|
||||
@@ -1104,7 +1110,16 @@ class DefaultToolAction(ToolAction):
|
||||
)
|
||||
|
||||
def _get_default_data_name(
|
||||
self, dataset, tool, on_text=None, trans=None, incoming=None, history=None, params=None, job_params=None, **kwd
|
||||
self,
|
||||
dataset,
|
||||
tool,
|
||||
on_text=None,
|
||||
trans: ProvidesHistoryContext | None = None,
|
||||
incoming=None,
|
||||
history=None,
|
||||
params=None,
|
||||
job_params=None,
|
||||
**kwd,
|
||||
):
|
||||
name = tool.name
|
||||
if on_text:
|
||||
@@ -1122,7 +1137,7 @@ class OutputCollections:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
trans,
|
||||
trans: ProvidesHistoryContext,
|
||||
history,
|
||||
tool,
|
||||
tool_action,
|
||||
@@ -1146,8 +1161,8 @@ class OutputCollections:
|
||||
self.incoming = incoming
|
||||
self.params = params
|
||||
self.job_params = job_params
|
||||
self.out_collections = {}
|
||||
self.out_collection_instances = {}
|
||||
self.out_collections: dict[str, DatasetCollection] = {}
|
||||
self.out_collection_instances: dict[str, HistoryDatasetCollectionAssociation] = {}
|
||||
self.tags = tags # all inherited tags
|
||||
self.hdca_tags = hdca_tags # only tags inherited from input HDCAs
|
||||
|
||||
|
||||
@@ -4,8 +4,11 @@ import os
|
||||
|
||||
from galaxy.exceptions import RequestParameterMissingException
|
||||
from galaxy.job_execution.output_collect import copy_collection_metadata_from_target_dict
|
||||
from galaxy.managers.context import ProvidesHistoryContext
|
||||
from galaxy.model import (
|
||||
History,
|
||||
HistoryDatasetAssociation,
|
||||
HistoryDatasetCollectionAssociation,
|
||||
Job,
|
||||
)
|
||||
from galaxy.model.dataset_collections.matching import MatchingCollections
|
||||
@@ -39,7 +42,7 @@ class BaseUploadToolAction(ToolAction):
|
||||
def execute(
|
||||
self,
|
||||
tool,
|
||||
trans,
|
||||
trans: ProvidesHistoryContext,
|
||||
incoming: ToolStateJobInstancePopulatedT | None = None,
|
||||
history: History | None = None,
|
||||
job_params=None,
|
||||
@@ -69,7 +72,9 @@ class BaseUploadToolAction(ToolAction):
|
||||
rval = self._setup_job(tool, trans, incoming, dataset_upload_inputs, history, preferred_object_store_id)
|
||||
return rval
|
||||
|
||||
def _setup_job(self, tool, trans, incoming, dataset_upload_inputs, history, preferred_object_store_id):
|
||||
def _setup_job(
|
||||
self, tool, trans: ProvidesHistoryContext, incoming, dataset_upload_inputs, history, preferred_object_store_id
|
||||
):
|
||||
"""Take persisted uploads and create a job for given tool."""
|
||||
|
||||
def _create_job(self, *args, **kwds):
|
||||
@@ -81,7 +86,9 @@ class BaseUploadToolAction(ToolAction):
|
||||
|
||||
|
||||
class UploadToolAction(BaseUploadToolAction):
|
||||
def _setup_job(self, tool, trans, incoming, dataset_upload_inputs, history, preferred_object_store_id):
|
||||
def _setup_job(
|
||||
self, tool, trans: ProvidesHistoryContext, incoming, dataset_upload_inputs, history, preferred_object_store_id
|
||||
):
|
||||
check_timer = ExecutionTimer()
|
||||
uploaded_datasets = upload_common.get_uploaded_datasets(
|
||||
trans, "", incoming, dataset_upload_inputs, history=history
|
||||
@@ -105,7 +112,9 @@ class UploadToolAction(BaseUploadToolAction):
|
||||
|
||||
|
||||
class FetchUploadToolAction(BaseUploadToolAction):
|
||||
def _setup_job(self, tool, trans, incoming, dataset_upload_inputs, history, preferred_object_store_id):
|
||||
def _setup_job(
|
||||
self, tool, trans: ProvidesHistoryContext, incoming, dataset_upload_inputs, history, preferred_object_store_id
|
||||
):
|
||||
# Now replace references in requests with these.
|
||||
files = incoming.get("files", [])
|
||||
files_iter = iter(files)
|
||||
@@ -135,7 +144,7 @@ class FetchUploadToolAction(BaseUploadToolAction):
|
||||
|
||||
replace_file_srcs(request)
|
||||
|
||||
outputs = []
|
||||
outputs: list[HistoryDatasetAssociation | HistoryDatasetCollectionAssociation] = []
|
||||
for target in request.get("targets", []):
|
||||
destination = target.get("destination")
|
||||
destination_type = destination.get("type")
|
||||
@@ -161,7 +170,7 @@ class FetchUploadToolAction(BaseUploadToolAction):
|
||||
)
|
||||
|
||||
|
||||
def _precreate_fetched_hdas(trans, history, target, outputs):
|
||||
def _precreate_fetched_hdas(trans: ProvidesHistoryContext, history, target, outputs):
|
||||
for item in target.get("elements", []):
|
||||
name = item.get("name", None)
|
||||
if name is None:
|
||||
@@ -186,7 +195,7 @@ def _precreate_fetched_hdas(trans, history, target, outputs):
|
||||
item["object_id"] = data.id
|
||||
|
||||
|
||||
def _precreate_fetched_collection_instance(trans, history, target, outputs):
|
||||
def _precreate_fetched_collection_instance(trans: ProvidesHistoryContext, history, target, outputs):
|
||||
collection_type = target.get("collection_type")
|
||||
if not collection_type:
|
||||
# Can't precreate collections of unknown type at this time.
|
||||
|
||||
@@ -18,7 +18,11 @@ from galaxy.files.uris import (
|
||||
stream_to_file,
|
||||
validate_non_local,
|
||||
)
|
||||
from galaxy.managers.context import ProvidesUserContext
|
||||
from galaxy.managers.context import (
|
||||
ProvidesAppContext,
|
||||
ProvidesHistoryContext,
|
||||
ProvidesUserContext,
|
||||
)
|
||||
from galaxy.model import (
|
||||
DatasetPermissions,
|
||||
FormDefinition,
|
||||
@@ -43,7 +47,7 @@ def validate_datatype_extension(datatypes_registry, ext):
|
||||
raise RequestParameterInvalidException(f"Requested extension '{ext}' unknown, cannot upload dataset.")
|
||||
|
||||
|
||||
def persist_uploads(params, trans):
|
||||
def persist_uploads(params, trans: ProvidesAppContext):
|
||||
"""
|
||||
Turn any uploads in the submitted form to persisted files.
|
||||
"""
|
||||
@@ -87,7 +91,7 @@ class LibraryParams:
|
||||
|
||||
|
||||
def handle_library_params(
|
||||
trans, params, folder_id: int, replace_dataset: LibraryDataset | None = None
|
||||
trans: ProvidesAppContext, params, folder_id: int, replace_dataset: LibraryDataset | None = None
|
||||
) -> LibraryParams:
|
||||
session = trans.sa_session
|
||||
# FIXME: the received params has already been parsed by util.Params() by the time it reaches here,
|
||||
@@ -125,7 +129,7 @@ def handle_library_params(
|
||||
)
|
||||
|
||||
|
||||
def __new_history_upload(trans, uploaded_dataset, history=None, state=None):
|
||||
def __new_history_upload(trans: ProvidesHistoryContext, uploaded_dataset, history=None, state=None):
|
||||
if not history:
|
||||
history = trans.history
|
||||
hda = HistoryDatasetAssociation(
|
||||
@@ -143,12 +147,15 @@ def __new_history_upload(trans, uploaded_dataset, history=None, state=None):
|
||||
hda.state = hda.states.QUEUED
|
||||
history.add_dataset(hda, genome_build=uploaded_dataset.dbkey, quota=False)
|
||||
permissions = trans.app.security_agent.history_get_default_permissions(history)
|
||||
assert hda.dataset is not None
|
||||
trans.app.security_agent.set_all_dataset_permissions(hda.dataset, permissions, new=True, flush=False)
|
||||
trans.sa_session.commit()
|
||||
return hda
|
||||
|
||||
|
||||
def __new_library_upload(trans, cntrller, uploaded_dataset, library_bunch, tag_handler, state=None):
|
||||
def __new_library_upload(
|
||||
trans: ProvidesUserContext, cntrller, uploaded_dataset, library_bunch, tag_handler, state=None
|
||||
):
|
||||
current_user_roles = trans.get_current_user_roles()
|
||||
if not (
|
||||
(trans.user_is_admin and cntrller in ["library_admin", "api"])
|
||||
@@ -213,6 +220,7 @@ def __new_library_upload(trans, cntrller, uploaded_dataset, library_bunch, tag_h
|
||||
)
|
||||
else:
|
||||
# Copy the current user's DefaultUserPermissions to the new LibraryDatasetDatasetAssociation.dataset
|
||||
assert ldda.dataset is not None
|
||||
trans.app.security_agent.set_all_dataset_permissions(
|
||||
ldda.dataset, trans.app.security_agent.user_get_default_permissions(trans.user), new=True
|
||||
)
|
||||
@@ -252,7 +260,13 @@ def __new_library_upload(trans, cntrller, uploaded_dataset, library_bunch, tag_h
|
||||
|
||||
|
||||
def new_upload(
|
||||
trans: ProvidesUserContext, cntrller, uploaded_dataset, library_bunch=None, history=None, state=None, tag_list=None
|
||||
trans: ProvidesHistoryContext,
|
||||
cntrller,
|
||||
uploaded_dataset,
|
||||
library_bunch=None,
|
||||
history=None,
|
||||
state=None,
|
||||
tag_list=None,
|
||||
):
|
||||
tag_handler = trans.tag_handler
|
||||
if library_bunch:
|
||||
@@ -280,7 +294,9 @@ def new_upload(
|
||||
return upload_target_dataset_instance
|
||||
|
||||
|
||||
def get_uploaded_datasets(trans, cntrller, params, dataset_upload_inputs, library_bunch=None, history=None):
|
||||
def get_uploaded_datasets(
|
||||
trans: ProvidesHistoryContext, cntrller, params, dataset_upload_inputs, library_bunch=None, history=None
|
||||
):
|
||||
uploaded_datasets = []
|
||||
for dataset_upload_input in dataset_upload_inputs:
|
||||
uploaded_datasets.extend(dataset_upload_input.get_uploaded_datasets(trans, params))
|
||||
@@ -290,7 +306,7 @@ def get_uploaded_datasets(trans, cntrller, params, dataset_upload_inputs, librar
|
||||
return uploaded_datasets
|
||||
|
||||
|
||||
def create_paramfile(trans, uploaded_datasets):
|
||||
def create_paramfile(trans: ProvidesUserContext, uploaded_datasets):
|
||||
"""
|
||||
Create the upload tool's JSON "param" file.
|
||||
"""
|
||||
@@ -333,7 +349,7 @@ def create_paramfile(trans, uploaded_datasets):
|
||||
except Exception:
|
||||
purge_source = True
|
||||
try:
|
||||
user_ftp_dir = os.path.abspath(trans.user_ftp_dir)
|
||||
user_ftp_dir = os.path.abspath(trans.user_ftp_dir) if trans.user_ftp_dir is not None else None
|
||||
except Exception:
|
||||
user_ftp_dir = None
|
||||
if user_ftp_dir and uploaded_dataset.path.startswith(user_ftp_dir):
|
||||
@@ -379,7 +395,7 @@ def create_paramfile(trans, uploaded_datasets):
|
||||
|
||||
|
||||
def create_job(
|
||||
trans,
|
||||
trans: ProvidesHistoryContext,
|
||||
params,
|
||||
tool,
|
||||
json_file_path,
|
||||
@@ -395,7 +411,7 @@ def create_job(
|
||||
job = Job()
|
||||
trans.sa_session.add(job)
|
||||
job.galaxy_version = trans.app.config.version_major
|
||||
galaxy_session = trans.get_galaxy_session()
|
||||
galaxy_session = trans.galaxy_session
|
||||
if isinstance(galaxy_session, GalaxySession):
|
||||
job.session_id = galaxy_session.id
|
||||
if trans.user is not None:
|
||||
@@ -438,7 +454,7 @@ def create_job(
|
||||
return job, output
|
||||
|
||||
|
||||
def active_folders(trans, folder):
|
||||
def active_folders(trans: ProvidesAppContext, folder):
|
||||
# Stolen from galaxy.web.controllers.library_common (importing from which causes a circular issues).
|
||||
# Much faster way of retrieving all active sub-folders within a given folder than the
|
||||
# performance of the mapper. This query also eagerloads the permissions on each folder.
|
||||
|
||||
@@ -8,6 +8,7 @@ from galaxy.exceptions import (
|
||||
ItemAccessibilityException,
|
||||
UserRequiredException,
|
||||
)
|
||||
from galaxy.managers.context import ProvidesUserContext
|
||||
from galaxy.util import plugin_config
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@@ -64,7 +65,7 @@ class ErrorPlugin:
|
||||
roles = []
|
||||
return self.app.security_agent.can_access_dataset(roles, dataset.dataset)
|
||||
|
||||
def _check_invocation_accessibility(self, trans, invocation, user):
|
||||
def _check_invocation_accessibility(self, trans: ProvidesUserContext | None, invocation, user):
|
||||
if not user:
|
||||
raise UserRequiredException("User is not logged in", type="error")
|
||||
if not trans or not self.app.workflow_manager.check_security(trans, invocation, check_ownership=False):
|
||||
|
||||
+15
-15
@@ -49,13 +49,13 @@ from galaxy.tools.execution_helpers import (
|
||||
)
|
||||
from galaxy.tools.parameters.workflow_utils import is_runtime_value
|
||||
from galaxy.util.json import swap_inf_nan
|
||||
from galaxy.work.context import WorkRequestContext
|
||||
from ._types import (
|
||||
ToolRequestT,
|
||||
ToolStateJobInstancePopulatedT,
|
||||
)
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from galaxy.managers.context import ProvidesHistoryContext
|
||||
from galaxy.tools import Tool
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@@ -96,7 +96,7 @@ class MappingParameters(NamedTuple):
|
||||
assert self.validated_param_template is not None
|
||||
assert self.validated_param_combinations is not None
|
||||
|
||||
def example_params(self, trans: WorkRequestContext) -> ToolStateJobInstancePopulatedT:
|
||||
def example_params(self, trans: "ProvidesHistoryContext") -> ToolStateJobInstancePopulatedT:
|
||||
"""Representative per-job params for output-structure determination.
|
||||
|
||||
Normally returns ``param_combinations[0]``. When the request
|
||||
@@ -113,11 +113,11 @@ class MappingParameters(NamedTuple):
|
||||
return _resolve_template(self.param_template, trans)
|
||||
|
||||
|
||||
def _resolve_template(template: ToolRequestT, trans: WorkRequestContext) -> ToolStateJobInstancePopulatedT:
|
||||
def _resolve_template(template: ToolRequestT, trans: "ProvidesHistoryContext") -> ToolStateJobInstancePopulatedT:
|
||||
return {key: _resolve_template_value(value, trans) for key, value in template.items()}
|
||||
|
||||
|
||||
def _resolve_template_value(value: Any, trans: WorkRequestContext) -> Any:
|
||||
def _resolve_template_value(value: Any, trans: "ProvidesHistoryContext") -> Any:
|
||||
if isinstance(value, dict):
|
||||
values = value.get("values")
|
||||
if (
|
||||
@@ -138,7 +138,7 @@ def _resolve_template_value(value: Any, trans: WorkRequestContext) -> Any:
|
||||
|
||||
def _resolve_collection_ref(
|
||||
ref: dict[str, Any],
|
||||
trans: WorkRequestContext,
|
||||
trans: "ProvidesHistoryContext",
|
||||
raw_fallback: Any,
|
||||
) -> model.HistoryDatasetCollectionAssociation | model.DatasetCollectionElement | Any:
|
||||
src = ref.get("src")
|
||||
@@ -163,7 +163,7 @@ def _resolve_collection_ref(
|
||||
|
||||
|
||||
def execute_async(
|
||||
trans,
|
||||
trans: "ProvidesHistoryContext",
|
||||
tool: "Tool",
|
||||
mapping_params: MappingParameters,
|
||||
history: model.History,
|
||||
@@ -204,7 +204,7 @@ def execute_async(
|
||||
|
||||
|
||||
def execute(
|
||||
trans,
|
||||
trans: "ProvidesHistoryContext",
|
||||
tool: "Tool",
|
||||
mapping_params: MappingParameters,
|
||||
history: model.History,
|
||||
@@ -247,7 +247,7 @@ def execute(
|
||||
|
||||
|
||||
def _execute(
|
||||
trans,
|
||||
trans: "ProvidesHistoryContext",
|
||||
tool: "Tool",
|
||||
mapping_params: MappingParameters,
|
||||
history: model.History,
|
||||
@@ -465,7 +465,7 @@ class ExecutionTracker:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
trans,
|
||||
trans: "ProvidesHistoryContext",
|
||||
tool: "Tool",
|
||||
mapping_params: MappingParameters,
|
||||
collection_info: MatchingCollections | None,
|
||||
@@ -555,7 +555,7 @@ class ExecutionTracker:
|
||||
)
|
||||
return self._on_text
|
||||
|
||||
def output_name(self, trans, history, params, output):
|
||||
def output_name(self, trans: "ProvidesHistoryContext", history, params, output):
|
||||
on_text = self.on_text
|
||||
|
||||
try:
|
||||
@@ -619,7 +619,7 @@ class ExecutionTracker:
|
||||
leaf_subcollection_type=subcollection_mapping_type,
|
||||
)
|
||||
|
||||
def _structure_for_output(self, trans, tool_output):
|
||||
def _structure_for_output(self, trans: "ProvidesHistoryContext", tool_output):
|
||||
collection_info = self.collection_info
|
||||
assert collection_info
|
||||
structure = collection_info.structure
|
||||
@@ -640,7 +640,7 @@ class ExecutionTracker:
|
||||
|
||||
return structure
|
||||
|
||||
def _mapped_output_structure(self, trans, tool_output):
|
||||
def _mapped_output_structure(self, trans: "ProvidesHistoryContext", tool_output):
|
||||
collections_manager = trans.app.dataset_collection_manager
|
||||
output_structure = tool_output_to_structure(
|
||||
self.sliced_input_collection_structure, tool_output, collections_manager
|
||||
@@ -727,7 +727,7 @@ class ExecutionTracker:
|
||||
else:
|
||||
return None
|
||||
|
||||
def finalize_dataset_collections(self, trans):
|
||||
def finalize_dataset_collections(self, trans: "ProvidesHistoryContext"):
|
||||
# TODO: this probably needs to be reworked some, we should have the collection methods
|
||||
# return a list of changed objects to add to the session and flush and we should only
|
||||
# be finalizing collections to a depth of self.collection_info.structure. So for instance
|
||||
@@ -848,7 +848,7 @@ class ExecutionTracker:
|
||||
class ToolExecutionTracker(ExecutionTracker):
|
||||
def __init__(
|
||||
self,
|
||||
trans,
|
||||
trans: "ProvidesHistoryContext",
|
||||
tool: "Tool",
|
||||
mapping_params: MappingParameters,
|
||||
collection_info: MatchingCollections | None,
|
||||
@@ -893,7 +893,7 @@ class ToolExecutionTracker(ExecutionTracker):
|
||||
class WorkflowStepExecutionTracker(ExecutionTracker):
|
||||
def __init__(
|
||||
self,
|
||||
trans,
|
||||
trans: "ProvidesHistoryContext",
|
||||
tool: "Tool",
|
||||
mapping_params: MappingParameters,
|
||||
collection_info: MatchingCollections | None,
|
||||
|
||||
@@ -5,9 +5,12 @@ tool execution code, and tool action code.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from more_itertools import consecutive_groups
|
||||
|
||||
from galaxy.managers.context import ProvidesUserContext
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -16,11 +19,11 @@ class ToolExecutionCache:
|
||||
the same tool by the same user with slightly different parameters.
|
||||
"""
|
||||
|
||||
def __init__(self, trans):
|
||||
def __init__(self, trans: ProvidesUserContext):
|
||||
self.trans = trans
|
||||
self.current_user_roles = trans.get_current_user_roles()
|
||||
self.chrom_info = {}
|
||||
self.cached_collection_elements = {}
|
||||
self.chrom_info: dict[str, Any] = {}
|
||||
self.cached_collection_elements: dict[Any, Any] = {}
|
||||
|
||||
def get_chrom_info(self, tool_id, input_dbkey):
|
||||
genome_builds = self.trans.app.genome_builds
|
||||
|
||||
@@ -132,7 +132,7 @@ def contains_workflow_parameter(value, search=False):
|
||||
return False
|
||||
|
||||
|
||||
def is_runtime_context(trans, other_values):
|
||||
def is_runtime_context(trans: "ProvidesHistoryContext", other_values):
|
||||
if trans.workflow_building_mode:
|
||||
return True
|
||||
for context_value in other_values.values():
|
||||
@@ -241,14 +241,14 @@ class ToolParameter(UsesDictVisibleKeys):
|
||||
"""Return user friendly name for the parameter"""
|
||||
return self.label if self.label else self.name
|
||||
|
||||
def from_json(self, value, trans, other_values=None):
|
||||
def from_json(self, value, trans: "ProvidesHistoryContext", other_values=None):
|
||||
"""
|
||||
Convert a value from an HTML POST into the parameters preferred value
|
||||
format.
|
||||
"""
|
||||
return value
|
||||
|
||||
def get_initial_value(self, trans, other_values):
|
||||
def get_initial_value(self, trans: "ProvidesHistoryContext | None", other_values):
|
||||
"""
|
||||
Return the starting value of the parameter
|
||||
"""
|
||||
@@ -346,7 +346,7 @@ class ToolParameter(UsesDictVisibleKeys):
|
||||
value = sanitize_param(value)
|
||||
return value
|
||||
|
||||
def validate(self, value, trans=None) -> None:
|
||||
def validate(self, value, trans: "ProvidesHistoryContext | None" = None) -> None:
|
||||
if value in ["", None] and self.optional:
|
||||
return
|
||||
for validator in self.validators:
|
||||
@@ -355,7 +355,7 @@ class ToolParameter(UsesDictVisibleKeys):
|
||||
except ValueError as e:
|
||||
raise ParameterValueError(str(e), self.name, value) from None
|
||||
|
||||
def to_dict(self, trans, other_values=None):
|
||||
def to_dict(self, trans: "ProvidesHistoryContext", other_values=None):
|
||||
"""to_dict tool parameter. This can be overridden by subclasses."""
|
||||
other_values = other_values or {}
|
||||
tool_dict = self._dictify_view_keys()
|
||||
@@ -402,7 +402,7 @@ class SimpleTextToolParameter(ToolParameter):
|
||||
else:
|
||||
self.value = ""
|
||||
|
||||
def get_initial_value(self, trans, other_values):
|
||||
def get_initial_value(self, trans: "ProvidesHistoryContext | None", other_values):
|
||||
return self.value
|
||||
|
||||
|
||||
@@ -436,7 +436,7 @@ class TextToolParameter(SimpleTextToolParameter):
|
||||
self.value = input_source.get("value")
|
||||
self.area = input_source.get_bool("area", False)
|
||||
|
||||
def validate(self, value, trans=None):
|
||||
def validate(self, value, trans: "ProvidesHistoryContext | None" = None):
|
||||
search = self.type == "text"
|
||||
if not (
|
||||
trans
|
||||
@@ -455,7 +455,7 @@ class TextToolParameter(SimpleTextToolParameter):
|
||||
default_value = ""
|
||||
return default_value
|
||||
|
||||
def to_dict(self, trans, other_values=None):
|
||||
def to_dict(self, trans: "ProvidesHistoryContext", other_values=None):
|
||||
d = super().to_dict(trans)
|
||||
other_values = other_values or {}
|
||||
d["area"] = self.area
|
||||
@@ -504,7 +504,7 @@ class IntegerToolParameter(TextToolParameter):
|
||||
if self.min is not None or self.max is not None:
|
||||
self.validators.append(validation.InRangeValidator.simple_range_validator(self.min, self.max))
|
||||
|
||||
def from_json(self, value, trans, other_values=None):
|
||||
def from_json(self, value, trans: "ProvidesHistoryContext", other_values=None):
|
||||
other_values = other_values or {}
|
||||
try:
|
||||
return int(value)
|
||||
@@ -530,7 +530,7 @@ class IntegerToolParameter(TextToolParameter):
|
||||
return None
|
||||
raise ParameterValueError("an integer is required", self.name, value)
|
||||
|
||||
def get_initial_value(self, trans, other_values):
|
||||
def get_initial_value(self, trans: "ProvidesHistoryContext | None", other_values):
|
||||
if self.value is not None and self.value != "":
|
||||
return int(self.value)
|
||||
else:
|
||||
@@ -577,7 +577,7 @@ class FloatToolParameter(TextToolParameter):
|
||||
if self.min is not None or self.max is not None:
|
||||
self.validators.append(validation.InRangeValidator.simple_range_validator(self.min, self.max))
|
||||
|
||||
def from_json(self, value, trans, other_values=None):
|
||||
def from_json(self, value, trans: "ProvidesHistoryContext", other_values=None):
|
||||
other_values = other_values or {}
|
||||
try:
|
||||
return float(value)
|
||||
@@ -603,7 +603,7 @@ class FloatToolParameter(TextToolParameter):
|
||||
return None
|
||||
raise ParameterValueError("a float is required", self.name, value)
|
||||
|
||||
def get_initial_value(self, trans, other_values):
|
||||
def get_initial_value(self, trans: "ProvidesHistoryContext | None", other_values):
|
||||
if self.value is None:
|
||||
return None
|
||||
try:
|
||||
@@ -652,7 +652,7 @@ class BooleanToolParameter(ToolParameter):
|
||||
self.optional = input_source.get_bool("optional", False)
|
||||
self.checked = boolean_is_checked(input_source)
|
||||
|
||||
def from_json(self, value, trans, other_values=None):
|
||||
def from_json(self, value, trans: "ProvidesHistoryContext", other_values=None):
|
||||
return self.to_python(value)
|
||||
|
||||
def to_python(self, value, app=None):
|
||||
@@ -665,7 +665,7 @@ class BooleanToolParameter(ToolParameter):
|
||||
def to_json(self, value, app, use_security):
|
||||
return self.to_python(value, app)
|
||||
|
||||
def get_initial_value(self, trans, other_values):
|
||||
def get_initial_value(self, trans: "ProvidesHistoryContext | None", other_values):
|
||||
return self.checked
|
||||
|
||||
def to_param_dict_string(self, value, other_values=None):
|
||||
@@ -674,7 +674,7 @@ class BooleanToolParameter(ToolParameter):
|
||||
else:
|
||||
return self.falsevalue
|
||||
|
||||
def to_dict(self, trans, other_values=None):
|
||||
def to_dict(self, trans: "ProvidesHistoryContext", other_values=None):
|
||||
d = super().to_dict(trans)
|
||||
d["truevalue"] = self.truevalue
|
||||
d["falsevalue"] = self.falsevalue
|
||||
@@ -700,7 +700,7 @@ class FileToolParameter(ToolParameter):
|
||||
[('argument', None), ('help', ''), ('help_format', 'html'), ('hidden', False), ('is_dynamic', False), ('label', ''), ('model_class', 'FileToolParameter'), ('name', '_name'), ('optional', False), ('refresh_on_change', False), ('type', 'file'), ('value', None)]
|
||||
"""
|
||||
|
||||
def from_json(self, value, trans, other_values=None):
|
||||
def from_json(self, value, trans: "ProvidesHistoryContext", other_values=None):
|
||||
# Middleware or proxies may encode files in special ways (TODO: this
|
||||
# should be pluggable)
|
||||
if isinstance(value, FilesPayload):
|
||||
@@ -781,7 +781,7 @@ class FTPFileToolParameter(ToolParameter):
|
||||
self.optional = input_source.parse_optional(True)
|
||||
self.user_ftp_dir = ""
|
||||
|
||||
def get_initial_value(self, trans, other_values):
|
||||
def get_initial_value(self, trans: "ProvidesHistoryContext | None", other_values):
|
||||
if trans is not None:
|
||||
if trans.user is not None:
|
||||
self.user_ftp_dir = f"{trans.user_ftp_dir}/"
|
||||
@@ -803,7 +803,7 @@ class FTPFileToolParameter(ToolParameter):
|
||||
else:
|
||||
return lst[0]
|
||||
|
||||
def from_json(self, value, trans, other_values=None):
|
||||
def from_json(self, value, trans: "ProvidesHistoryContext", other_values=None):
|
||||
return self.to_python(value, trans.app, validate=True)
|
||||
|
||||
def to_json(self, value, app, use_security):
|
||||
@@ -831,7 +831,7 @@ class FTPFileToolParameter(ToolParameter):
|
||||
raise ValueError("The FTP directory is not configured.")
|
||||
return lst
|
||||
|
||||
def to_dict(self, trans, other_values=None):
|
||||
def to_dict(self, trans: "ProvidesHistoryContext", other_values=None):
|
||||
d = super().to_dict(trans)
|
||||
d["multiple"] = self.multiple
|
||||
return d
|
||||
@@ -855,7 +855,7 @@ class HiddenToolParameter(ToolParameter):
|
||||
self.value = input_source.get("value")
|
||||
self.hidden = True
|
||||
|
||||
def get_initial_value(self, trans, other_values):
|
||||
def get_initial_value(self, trans: "ProvidesHistoryContext | None", other_values):
|
||||
return self.value
|
||||
|
||||
def get_label(self):
|
||||
@@ -891,7 +891,7 @@ class ColorToolParameter(ToolParameter):
|
||||
self.value = get_color_value(input_source)
|
||||
self.rgb = input_source.get_bool("rgb", False)
|
||||
|
||||
def get_initial_value(self, trans, other_values):
|
||||
def get_initial_value(self, trans: "ProvidesHistoryContext | None", other_values):
|
||||
if self.value is not None:
|
||||
return self.value.lower()
|
||||
|
||||
@@ -922,22 +922,25 @@ class BaseURLToolParameter(HiddenToolParameter):
|
||||
super().__init__(tool, input_source)
|
||||
self.value = input_source.get("value", "")
|
||||
|
||||
def get_initial_value(self, trans, other_values):
|
||||
def get_initial_value(self, trans: "ProvidesHistoryContext | None", other_values):
|
||||
if trans is None:
|
||||
return self.value
|
||||
return self._get_value(trans)
|
||||
|
||||
def from_json(self, value, trans, other_values=None):
|
||||
def from_json(self, value, trans: "ProvidesHistoryContext", other_values=None):
|
||||
return self._get_value(trans)
|
||||
|
||||
def _get_value(self, trans):
|
||||
def _get_value(self, trans: "ProvidesHistoryContext"):
|
||||
try:
|
||||
if not self.value.startswith("/"):
|
||||
raise Exception("baseurl value must start with a /")
|
||||
assert trans.url_builder is not None
|
||||
return trans.url_builder(self.value, qualified=True)
|
||||
except Exception as e:
|
||||
log.debug('Url creation failed for "%s": %s', self.name, unicodify(e))
|
||||
return self.value
|
||||
|
||||
def to_dict(self, trans, other_values=None):
|
||||
def to_dict(self, trans: "ProvidesHistoryContext", other_values=None):
|
||||
d = super().to_dict(trans)
|
||||
return d
|
||||
|
||||
@@ -1004,14 +1007,16 @@ class SelectToolParameter(ToolParameter):
|
||||
self.legal_values.add(value)
|
||||
self.is_dynamic = (self.dynamic_options is not None) or (self.options is not None)
|
||||
|
||||
def _get_dynamic_options_call_other_values(self, trans, other_values):
|
||||
def _get_dynamic_options_call_other_values(self, trans: "ProvidesHistoryContext", other_values):
|
||||
call_other_values = ExpressionContext({"__trans__": trans})
|
||||
if other_values:
|
||||
call_other_values.parent = other_values.parent
|
||||
call_other_values.update(other_values.dict)
|
||||
return call_other_values
|
||||
|
||||
def get_options(self, trans, other_values) -> Sequence[ParameterOption | DrillDownOptionsDict]:
|
||||
def get_options(
|
||||
self, trans: "ProvidesHistoryContext", other_values
|
||||
) -> Sequence[ParameterOption | DrillDownOptionsDict]:
|
||||
if self.options:
|
||||
return self.options.get_options(trans, other_values)
|
||||
elif self.dynamic_options:
|
||||
@@ -1032,24 +1037,24 @@ class SelectToolParameter(ToolParameter):
|
||||
else:
|
||||
return [ParameterOption(*o) for o in self.static_options]
|
||||
|
||||
def get_legal_values(self, trans, other_values, value):
|
||||
def get_legal_values(self, trans: "ProvidesHistoryContext", other_values, value):
|
||||
"""
|
||||
determine the set of values of legal options
|
||||
"""
|
||||
options = cast(list[ParameterOption], self.get_options(trans, other_values))
|
||||
return {option.dataset or option.value for option in options}
|
||||
|
||||
def get_legal_names(self, trans, other_values):
|
||||
def get_legal_names(self, trans: "ProvidesHistoryContext", other_values):
|
||||
"""
|
||||
determine the set of values of legal options
|
||||
"""
|
||||
options = cast(list[ParameterOption], self.get_options(trans, other_values))
|
||||
return {option.name: option.value for option in options}
|
||||
|
||||
def from_json(self, value, trans, other_values=None):
|
||||
def from_json(self, value, trans: "ProvidesHistoryContext", other_values=None):
|
||||
return self._select_from_json(value, trans, other_values=other_values, require_legal_value=True)
|
||||
|
||||
def _select_from_json(self, value, trans, other_values=None, require_legal_value=True):
|
||||
def _select_from_json(self, value, trans: "ProvidesHistoryContext", other_values=None, require_legal_value=True):
|
||||
other_values = other_values or {}
|
||||
try:
|
||||
legal_values = self.get_legal_values(trans, other_values, value)
|
||||
@@ -1178,11 +1183,18 @@ class SelectToolParameter(ToolParameter):
|
||||
return history_item_dict_to_python(value, app, self.name)
|
||||
return super().to_python(value, app)
|
||||
|
||||
def get_initial_value(self, trans, other_values):
|
||||
try:
|
||||
options = cast(list[ParameterOption], self.get_options(trans, other_values))
|
||||
except ImplicitConversionRequired:
|
||||
return None
|
||||
def get_initial_value(self, trans: "ProvidesHistoryContext | None", other_values):
|
||||
options: list[ParameterOption]
|
||||
if trans is None:
|
||||
# Conditional case inference walks tool state without a transaction
|
||||
# (see galaxy.tools.parameters.visit_input_values); only statically
|
||||
# declared options can be resolved without one.
|
||||
options = [ParameterOption(*o) for o in self.static_options]
|
||||
else:
|
||||
try:
|
||||
options = cast(list[ParameterOption], self.get_options(trans, other_values))
|
||||
except ImplicitConversionRequired:
|
||||
return None
|
||||
if not options:
|
||||
return None
|
||||
value = [option.value for option in options if option.selected]
|
||||
@@ -1225,7 +1237,7 @@ class SelectToolParameter(ToolParameter):
|
||||
else:
|
||||
return []
|
||||
|
||||
def to_dict(self, trans, other_values=None):
|
||||
def to_dict(self, trans: "ProvidesHistoryContext", other_values=None):
|
||||
other_values = other_values or {}
|
||||
d = super().to_dict(trans, other_values)
|
||||
|
||||
@@ -1237,7 +1249,7 @@ class SelectToolParameter(ToolParameter):
|
||||
d["textable"] = is_runtime_context(trans, other_values)
|
||||
return d
|
||||
|
||||
def validate(self, value, trans=None):
|
||||
def validate(self, value, trans: "ProvidesHistoryContext | None" = None):
|
||||
if not value:
|
||||
super().validate(value, trans)
|
||||
if self.multiple:
|
||||
@@ -1276,7 +1288,7 @@ class GenomeBuildParameter(SelectToolParameter):
|
||||
self.static_options = [(value, key, False) for key, value in self._get_dbkey_names()]
|
||||
self.is_dynamic = True
|
||||
|
||||
def get_options(self, trans, other_values) -> Sequence[ParameterOption]:
|
||||
def get_options(self, trans: "ProvidesHistoryContext", other_values) -> Sequence[ParameterOption]:
|
||||
last_used_build = object()
|
||||
if trans.history:
|
||||
last_used_build = trans.history.genome_build
|
||||
@@ -1285,10 +1297,10 @@ class GenomeBuildParameter(SelectToolParameter):
|
||||
for dbkey, build_name in self._get_dbkey_names(trans=trans)
|
||||
]
|
||||
|
||||
def get_legal_values(self, trans, other_values, value):
|
||||
def get_legal_values(self, trans: "ProvidesHistoryContext", other_values, value):
|
||||
return {dbkey for dbkey, _ in self._get_dbkey_names(trans=trans)}
|
||||
|
||||
def to_dict(self, trans, other_values=None):
|
||||
def to_dict(self, trans: "ProvidesHistoryContext", other_values=None):
|
||||
# skip SelectToolParameter (the immediate parent) bc we need to get options in a different way here
|
||||
d = ToolParameter.to_dict(self, trans)
|
||||
|
||||
@@ -1302,7 +1314,7 @@ class GenomeBuildParameter(SelectToolParameter):
|
||||
|
||||
d.update(
|
||||
{
|
||||
"options": serialize_options(trans, options),
|
||||
"options": serialize_options(trans.security, options),
|
||||
"value": value,
|
||||
"display": self.display,
|
||||
"multiple": self.multiple,
|
||||
@@ -1311,7 +1323,7 @@ class GenomeBuildParameter(SelectToolParameter):
|
||||
|
||||
return d
|
||||
|
||||
def _get_dbkey_names(self, trans=None):
|
||||
def _get_dbkey_names(self, trans: "ProvidesHistoryContext | None" = None):
|
||||
if not self.tool:
|
||||
# Hack for unit tests, since we have no tool
|
||||
return read_dbnames(None)
|
||||
@@ -1341,7 +1353,7 @@ class SelectTagParameter(SelectToolParameter):
|
||||
self.default_value = input_source.get("value", None)
|
||||
self.is_dynamic = True
|
||||
|
||||
def from_json(self, value, trans, other_values=None):
|
||||
def from_json(self, value, trans: "ProvidesHistoryContext", other_values=None):
|
||||
other_values = other_values or {}
|
||||
if self.multiple:
|
||||
tag_list = []
|
||||
@@ -1384,7 +1396,7 @@ class SelectTagParameter(SelectToolParameter):
|
||||
tags.add(tag.user_value)
|
||||
return list(tags)
|
||||
|
||||
def get_options(self, trans, other_values) -> Sequence[ParameterOption]:
|
||||
def get_options(self, trans: "ProvidesHistoryContext", other_values) -> Sequence[ParameterOption]:
|
||||
"""
|
||||
Show tags
|
||||
"""
|
||||
@@ -1393,12 +1405,12 @@ class SelectTagParameter(SelectToolParameter):
|
||||
options.append(ParameterOption(f"Tags: {tag}", tag, False))
|
||||
return options
|
||||
|
||||
def get_initial_value(self, trans, other_values):
|
||||
def get_initial_value(self, trans: "ProvidesHistoryContext | None", other_values):
|
||||
if self.default_value is not None:
|
||||
return self.default_value
|
||||
return super().get_initial_value(trans, other_values)
|
||||
|
||||
def get_legal_values(self, trans, other_values, value):
|
||||
def get_legal_values(self, trans: "ProvidesHistoryContext", other_values, value):
|
||||
if self.data_ref not in other_values and not trans.workflow_building_mode:
|
||||
raise ValueError("Value for associated data reference not found (data_ref).")
|
||||
return set(self.get_tag_list(other_values))
|
||||
@@ -1406,7 +1418,7 @@ class SelectTagParameter(SelectToolParameter):
|
||||
def get_dependencies(self):
|
||||
return [self.data_ref]
|
||||
|
||||
def to_dict(self, trans, other_values=None):
|
||||
def to_dict(self, trans: "ProvidesHistoryContext", other_values=None):
|
||||
other_values = other_values or {}
|
||||
d = super().to_dict(trans, other_values=other_values)
|
||||
d["data_ref"] = self.data_ref
|
||||
@@ -1467,7 +1479,7 @@ class ColumnListParameter(SelectToolParameter):
|
||||
return value.strip()
|
||||
return value
|
||||
|
||||
def from_json(self, value, trans, other_values=None):
|
||||
def from_json(self, value, trans: "ProvidesHistoryContext", other_values=None):
|
||||
"""
|
||||
Label convention prepends column number with a 'c', but tool uses the integer. This
|
||||
removes the 'c' when entered into a workflow.
|
||||
@@ -1500,7 +1512,7 @@ class ColumnListParameter(SelectToolParameter):
|
||||
column = column.lower()[1:]
|
||||
return column
|
||||
|
||||
def get_column_list(self, trans, other_values):
|
||||
def get_column_list(self, trans: "ProvidesHistoryContext", other_values):
|
||||
"""
|
||||
Generate a select list containing the columns of the associated
|
||||
dataset (if found).
|
||||
@@ -1545,7 +1557,7 @@ class ColumnListParameter(SelectToolParameter):
|
||||
column_list = [c for c in column_list if c in this_column_list]
|
||||
return column_list
|
||||
|
||||
def get_options(self, trans, other_values) -> Sequence[ParameterOption]:
|
||||
def get_options(self, trans: "ProvidesHistoryContext", other_values) -> Sequence[ParameterOption]:
|
||||
"""
|
||||
Show column labels rather than c1..cn if use_header_names=True
|
||||
"""
|
||||
@@ -1585,12 +1597,12 @@ class ColumnListParameter(SelectToolParameter):
|
||||
options = [ParameterOption(f"Column: {col}", col, False) for col in column_list]
|
||||
return options
|
||||
|
||||
def get_initial_value(self, trans, other_values):
|
||||
def get_initial_value(self, trans: "ProvidesHistoryContext | None", other_values):
|
||||
if self.default_value is not None:
|
||||
return self.default_value
|
||||
return super().get_initial_value(trans, other_values)
|
||||
|
||||
def get_legal_values(self, trans, other_values, value):
|
||||
def get_legal_values(self, trans: "ProvidesHistoryContext", other_values, value):
|
||||
if self.data_ref not in other_values:
|
||||
raise ValueError("Value for associated data reference not found (data_ref).")
|
||||
legal_values = self.get_column_list(trans, other_values)
|
||||
@@ -1604,7 +1616,7 @@ class ColumnListParameter(SelectToolParameter):
|
||||
|
||||
return set(legal_values)
|
||||
|
||||
def is_file_empty(self, trans, other_values):
|
||||
def is_file_empty(self, trans: "ProvidesHistoryContext", other_values):
|
||||
for dataset in util.listify(other_values.get(self.data_ref)):
|
||||
# Use representative dataset if a dataset collection is parsed
|
||||
if isinstance(dataset, HistoryDatasetCollectionAssociation):
|
||||
@@ -1628,7 +1640,7 @@ class ColumnListParameter(SelectToolParameter):
|
||||
def get_dependencies(self):
|
||||
return [self.data_ref]
|
||||
|
||||
def to_dict(self, trans, other_values=None):
|
||||
def to_dict(self, trans: "ProvidesHistoryContext", other_values=None):
|
||||
other_values = other_values or {}
|
||||
d = super().to_dict(trans, other_values=other_values)
|
||||
d["data_ref"] = self.data_ref
|
||||
@@ -1701,7 +1713,7 @@ class DrillDownSelectToolParameter(SelectToolParameter):
|
||||
self.dynamic_options = None
|
||||
self.options = input_source.parse_drill_down_static_options(tool_data_path)
|
||||
|
||||
def _get_options_from_code(self, trans=None, other_values=None):
|
||||
def _get_options_from_code(self, trans: "ProvidesHistoryContext | None" = None, other_values=None):
|
||||
assert self.dynamic_options, Exception("dynamic_options was not specifed")
|
||||
call_other_values = ExpressionContext({"__trans__": trans, "__value__": None})
|
||||
if other_values:
|
||||
@@ -1713,7 +1725,9 @@ class DrillDownSelectToolParameter(SelectToolParameter):
|
||||
except Exception:
|
||||
return []
|
||||
|
||||
def get_options(self, trans=None, other_values=None) -> list[DrillDownOptionsDict]:
|
||||
def get_options(
|
||||
self, trans: "ProvidesHistoryContext | None" = None, other_values=None
|
||||
) -> list[DrillDownOptionsDict]:
|
||||
other_values = other_values or {}
|
||||
if self.is_dynamic:
|
||||
if self.dynamic_options:
|
||||
@@ -1722,7 +1736,7 @@ class DrillDownSelectToolParameter(SelectToolParameter):
|
||||
|
||||
return self.options
|
||||
|
||||
def get_legal_values(self, trans, other_values, value):
|
||||
def get_legal_values(self, trans: "ProvidesHistoryContext", other_values, value):
|
||||
def recurse_options(legal_values, options: list[DrillDownOptionsDict]):
|
||||
for option in options:
|
||||
legal_values.append(option["value"])
|
||||
@@ -1732,7 +1746,7 @@ class DrillDownSelectToolParameter(SelectToolParameter):
|
||||
recurse_options(legal_values, self.get_options(trans=trans, other_values=other_values))
|
||||
return legal_values
|
||||
|
||||
def from_json(self, value, trans, other_values=None):
|
||||
def from_json(self, value, trans: "ProvidesHistoryContext", other_values=None):
|
||||
other_values = other_values or {}
|
||||
legal_values = self.get_legal_values(trans, other_values, value)
|
||||
if not legal_values and trans.workflow_building_mode:
|
||||
@@ -1813,7 +1827,7 @@ class DrillDownSelectToolParameter(SelectToolParameter):
|
||||
rval = sanitize_param(rval)
|
||||
return rval
|
||||
|
||||
def get_initial_value(self, trans, other_values):
|
||||
def get_initial_value(self, trans: "ProvidesHistoryContext | None", other_values):
|
||||
def recurse_options(initial_values, options: list[DrillDownOptionsDict]):
|
||||
for option in options:
|
||||
if option["selected"]:
|
||||
@@ -1865,7 +1879,7 @@ class DrillDownSelectToolParameter(SelectToolParameter):
|
||||
def get_dependencies(self):
|
||||
return []
|
||||
|
||||
def to_dict(self, trans, other_values=None):
|
||||
def to_dict(self, trans: "ProvidesHistoryContext", other_values=None):
|
||||
other_values = other_values or {}
|
||||
# skip SelectToolParameter (the immediate parent) bc we need to get options in a different way here
|
||||
d = ToolParameter.to_dict(self, trans)
|
||||
@@ -1961,7 +1975,7 @@ class BaseDataToolParameter(ToolParameter):
|
||||
# with None" (which is a legitimate return for parameters with no formats).
|
||||
_ACCEPTABLE_EXTENSIONS_UNSET: Any = object()
|
||||
|
||||
def __init__(self, tool: Optional["Tool"], input_source, trans):
|
||||
def __init__(self, tool: Optional["Tool"], input_source, trans: "ProvidesHistoryContext | None"):
|
||||
super().__init__(tool, input_source)
|
||||
self.min = input_source.get("min")
|
||||
self.max = input_source.get("max")
|
||||
@@ -1993,7 +2007,7 @@ class BaseDataToolParameter(ToolParameter):
|
||||
self.tool.app.datatypes_registry
|
||||
) # can be None if self.tool.app is a ValidationContext
|
||||
|
||||
def _parse_formats(self, trans, input_source):
|
||||
def _parse_formats(self, trans: "ProvidesHistoryContext | None", input_source):
|
||||
"""
|
||||
Build list of classes for supported data formats
|
||||
"""
|
||||
@@ -2058,7 +2072,8 @@ class BaseDataToolParameter(ToolParameter):
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_initial_value(self, trans, other_values):
|
||||
def get_initial_value(self, trans: "ProvidesHistoryContext | None", other_values):
|
||||
assert trans is not None
|
||||
if trans.workflow_building_mode is workflow_building_modes.ENABLED or trans.app.name == "tool_shed":
|
||||
return RuntimeValue()
|
||||
if self.optional:
|
||||
@@ -2157,7 +2172,7 @@ class BaseDataToolParameter(ToolParameter):
|
||||
else:
|
||||
return app.model.context.get(HistoryDatasetAssociation, int(value))
|
||||
|
||||
def validate(self, value, trans=None):
|
||||
def validate(self, value, trans: "ProvidesHistoryContext | None" = None):
|
||||
def do_validate(v):
|
||||
for validator in self.validators:
|
||||
if (
|
||||
@@ -2297,7 +2312,7 @@ class DataToolParameter(BaseDataToolParameter):
|
||||
security stuff will dramatically alter this anyway.
|
||||
"""
|
||||
|
||||
def __init__(self, tool: Optional["Tool"], input_source, trans=None):
|
||||
def __init__(self, tool: Optional["Tool"], input_source, trans: "ProvidesHistoryContext | None" = None):
|
||||
input_source = ensure_input_source(input_source)
|
||||
super().__init__(tool, input_source, trans)
|
||||
self.load_contents = int(input_source.get("load_contents", 0))
|
||||
@@ -2348,7 +2363,7 @@ class DataToolParameter(BaseDataToolParameter):
|
||||
allow_uri_if_protocol = input_source.get("allow_uri_if_protocol", None)
|
||||
self.allow_uri_if_protocol = allow_uri_if_protocol.split(",") if allow_uri_if_protocol else []
|
||||
|
||||
def from_json(self, value, trans, other_values=None):
|
||||
def from_json(self, value, trans: "ProvidesHistoryContext", other_values=None):
|
||||
session = trans.sa_session
|
||||
|
||||
other_values = other_values or {}
|
||||
@@ -2358,6 +2373,7 @@ class DataToolParameter(BaseDataToolParameter):
|
||||
raise ParameterValueError("specify a dataset of the required format / build for parameter", self.name)
|
||||
if value in [None, "None", ""]:
|
||||
if self.default_object:
|
||||
assert trans.history is not None
|
||||
return raw_to_galaxy(trans.app, trans.history, self.default_object)
|
||||
return None
|
||||
batch_wrapper = False
|
||||
@@ -2529,7 +2545,7 @@ class DataToolParameter(BaseDataToolParameter):
|
||||
else:
|
||||
return []
|
||||
|
||||
def converter_safe(self, other_values, trans):
|
||||
def converter_safe(self, other_values, trans: "ProvidesHistoryContext"):
|
||||
if (
|
||||
self.tool is None
|
||||
or self.tool.has_multiple_pages
|
||||
@@ -2574,7 +2590,9 @@ class DataToolParameter(BaseDataToolParameter):
|
||||
ref = ref()
|
||||
return str(ref)
|
||||
|
||||
def to_dict(self, trans, other_values=None, pagination: ParameterPaginationT | None = None):
|
||||
def to_dict(
|
||||
self, trans: "ProvidesHistoryContext", other_values=None, pagination: ParameterPaginationT | None = None
|
||||
):
|
||||
other_values = other_values or {}
|
||||
d = super().to_dict(trans)
|
||||
self._fill_to_dict_static(d)
|
||||
@@ -2793,7 +2811,7 @@ class DataToolParameter(BaseDataToolParameter):
|
||||
make_hdca_entry(builder.security, hdca, name, keep=False, subcollection_type=subcollection_type)
|
||||
)
|
||||
|
||||
def _history_query(self, trans):
|
||||
def _history_query(self, trans: "ProvidesHistoryContext"):
|
||||
assert self.multiple
|
||||
dataset_collection_type_descriptions = trans.app.dataset_collection_manager.collection_type_descriptions
|
||||
# If multiple data parameter, treat like a list parameter.
|
||||
@@ -2803,7 +2821,7 @@ class DataToolParameter(BaseDataToolParameter):
|
||||
class DataCollectionToolParameter(BaseDataToolParameter):
|
||||
""" """
|
||||
|
||||
def __init__(self, tool: Optional["Tool"], input_source, trans=None):
|
||||
def __init__(self, tool: Optional["Tool"], input_source, trans: "ProvidesHistoryContext | None" = None):
|
||||
input_source = ensure_input_source(input_source)
|
||||
super().__init__(tool, input_source, trans)
|
||||
self._parse_formats(trans, input_source)
|
||||
@@ -2830,11 +2848,11 @@ class DataCollectionToolParameter(BaseDataToolParameter):
|
||||
def collection_types(self) -> list[str] | None:
|
||||
return self._collection_types
|
||||
|
||||
def _history_query(self, trans):
|
||||
def _history_query(self, trans: "ProvidesHistoryContext"):
|
||||
dataset_collection_type_descriptions = trans.app.dataset_collection_manager.collection_type_descriptions
|
||||
return query.HistoryQuery.from_parameter(self, dataset_collection_type_descriptions)
|
||||
|
||||
def match_collections(self, trans, history, dataset_collection_matcher):
|
||||
def match_collections(self, trans: "ProvidesHistoryContext", history, dataset_collection_matcher):
|
||||
dataset_collections = trans.app.dataset_collection_manager.history_dataset_collections(
|
||||
history, self._history_query(trans)
|
||||
)
|
||||
@@ -2850,7 +2868,7 @@ class DataCollectionToolParameter(BaseDataToolParameter):
|
||||
continue
|
||||
yield dataset_collection_instance, match.implicit_conversion
|
||||
|
||||
def match_multirun_collections(self, trans, history, dataset_collection_matcher):
|
||||
def match_multirun_collections(self, trans: "ProvidesHistoryContext", history, dataset_collection_matcher):
|
||||
for history_dataset_collection in history.active_visible_dataset_collections:
|
||||
if not self._history_query(trans).can_map_over(history_dataset_collection):
|
||||
continue
|
||||
@@ -2859,7 +2877,7 @@ class DataCollectionToolParameter(BaseDataToolParameter):
|
||||
if match:
|
||||
yield history_dataset_collection, match.implicit_conversion
|
||||
|
||||
def from_json(self, value, trans, other_values=None):
|
||||
def from_json(self, value, trans: "ProvidesHistoryContext", other_values=None):
|
||||
session = trans.sa_session
|
||||
|
||||
other_values = other_values or {}
|
||||
@@ -2870,6 +2888,7 @@ class DataCollectionToolParameter(BaseDataToolParameter):
|
||||
raise ParameterValueError("specify a dataset collection of the correct type", self.name)
|
||||
if value in [None, "None"]:
|
||||
if self.default_object:
|
||||
assert trans.history is not None
|
||||
return raw_to_galaxy(trans.app, trans.history, self.default_object)
|
||||
return None
|
||||
if isinstance(value, MutableMapping) and "values" in value:
|
||||
@@ -2933,7 +2952,9 @@ class DataCollectionToolParameter(BaseDataToolParameter):
|
||||
display_text = "No dataset collection."
|
||||
return display_text
|
||||
|
||||
def to_dict(self, trans, other_values=None, pagination: ParameterPaginationT | None = None):
|
||||
def to_dict(
|
||||
self, trans: "ProvidesHistoryContext", other_values=None, pagination: ParameterPaginationT | None = None
|
||||
):
|
||||
other_values = other_values or {}
|
||||
d = super().to_dict(trans)
|
||||
d["collection_types"] = self.collection_types
|
||||
@@ -3098,10 +3119,11 @@ class BaseJsonToolParameter(ToolParameter):
|
||||
class DirectoryUriToolParameter(SimpleTextToolParameter):
|
||||
"""galaxy.files URIs for directories."""
|
||||
|
||||
def validate(self, value, trans=None):
|
||||
def validate(self, value, trans: "ProvidesHistoryContext | None" = None):
|
||||
super().validate(value, trans=trans)
|
||||
if not value:
|
||||
return # value is not set yet, do not validate
|
||||
assert trans is not None
|
||||
# Skip file source validation in workflow building mode to allow workflows
|
||||
# referencing removed file sources to be exported/viewed. Users can then
|
||||
# download and edit them. Validation still occurs during tool execution.
|
||||
@@ -3135,7 +3157,7 @@ class RulesListToolParameter(BaseJsonToolParameter):
|
||||
super().__init__(tool, input_source)
|
||||
self.data_ref = input_source.get("data_ref", None)
|
||||
|
||||
def to_dict(self, trans, other_values=None):
|
||||
def to_dict(self, trans: "ProvidesHistoryContext", other_values=None):
|
||||
other_values = other_values or {}
|
||||
d = ToolParameter.to_dict(self, trans)
|
||||
if target := other_values.get(self.data_ref):
|
||||
@@ -3146,7 +3168,7 @@ class RulesListToolParameter(BaseJsonToolParameter):
|
||||
}
|
||||
return d
|
||||
|
||||
def validate(self, value, trans=None):
|
||||
def validate(self, value, trans: "ProvidesHistoryContext | None" = None):
|
||||
super().validate(value, trans=trans)
|
||||
if not isinstance(value, MutableMapping):
|
||||
raise ValueError("No rules specified for rules parameter.")
|
||||
|
||||
@@ -1,19 +1,26 @@
|
||||
from logging import getLogger
|
||||
from typing import (
|
||||
Any,
|
||||
TYPE_CHECKING,
|
||||
)
|
||||
|
||||
import galaxy.model
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from galaxy.managers.context import ProvidesHistoryContext
|
||||
|
||||
log = getLogger(__name__)
|
||||
|
||||
|
||||
def set_dataset_matcher_factory(trans, tool):
|
||||
def set_dataset_matcher_factory(trans: "ProvidesHistoryContext", tool):
|
||||
trans.dataset_matcher_factory = DatasetMatcherFactory(trans, tool)
|
||||
|
||||
|
||||
def unset_dataset_matcher_factory(trans):
|
||||
def unset_dataset_matcher_factory(trans: "ProvidesHistoryContext"):
|
||||
trans.dataset_matcher_factory = None
|
||||
|
||||
|
||||
def get_dataset_matcher_factory(trans):
|
||||
def get_dataset_matcher_factory(trans: "ProvidesHistoryContext"):
|
||||
dataset_matcher_factory = getattr(trans, "dataset_matcher_factory", None)
|
||||
return dataset_matcher_factory or DatasetMatcherFactory(trans)
|
||||
|
||||
@@ -21,11 +28,11 @@ def get_dataset_matcher_factory(trans):
|
||||
class DatasetMatcherFactory:
|
||||
""""""
|
||||
|
||||
def __init__(self, trans, tool=None):
|
||||
def __init__(self, trans: "ProvidesHistoryContext", tool=None):
|
||||
self._trans = trans
|
||||
self._tool = tool
|
||||
self._data_inputs = []
|
||||
self._matches_format_cache = {}
|
||||
self._data_inputs: list[Any] = []
|
||||
self._matches_format_cache: dict[str, dict[str, bool]] = {}
|
||||
if tool:
|
||||
valid_input_states = tool.valid_input_states
|
||||
else:
|
||||
@@ -96,7 +103,7 @@ class DatasetMatcher:
|
||||
and permission handling.
|
||||
"""
|
||||
|
||||
def __init__(self, dataset_matcher_factory, trans, param, other_values):
|
||||
def __init__(self, dataset_matcher_factory, trans: "ProvidesHistoryContext", param, other_values):
|
||||
self.dataset_matcher_factory = dataset_matcher_factory
|
||||
self.trans = trans
|
||||
self.param = param
|
||||
@@ -202,7 +209,7 @@ class HdcaImplicitMatch:
|
||||
|
||||
|
||||
class SummaryDatasetCollectionMatcher:
|
||||
def __init__(self, dataset_matcher_factory, trans, dataset_matcher):
|
||||
def __init__(self, dataset_matcher_factory, trans: "ProvidesHistoryContext", dataset_matcher):
|
||||
self.dataset_matcher_factory = dataset_matcher_factory
|
||||
self._trans = trans
|
||||
self.dataset_matcher = dataset_matcher
|
||||
@@ -238,7 +245,7 @@ class SummaryDatasetCollectionMatcher:
|
||||
|
||||
|
||||
class DatasetCollectionMatcher:
|
||||
def __init__(self, trans, dataset_matcher):
|
||||
def __init__(self, trans: "ProvidesHistoryContext", dataset_matcher):
|
||||
self.dataset_matcher = dataset_matcher
|
||||
self._trans = trans
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ from typing import (
|
||||
Literal,
|
||||
)
|
||||
|
||||
from galaxy.managers.context import ProvidesHistoryContext
|
||||
from galaxy.model import (
|
||||
DatasetCollectionElement,
|
||||
HistoryDatasetAssociation,
|
||||
@@ -39,7 +40,6 @@ from galaxy.util import (
|
||||
string_as_bool,
|
||||
)
|
||||
from galaxy.util.template import fill_template
|
||||
from galaxy.work.context import WorkRequestContext
|
||||
from . import validation
|
||||
from .cancelable_request import request
|
||||
|
||||
@@ -66,7 +66,7 @@ class Filter:
|
||||
"""Returns the name of any dependencies, otherwise None"""
|
||||
return None
|
||||
|
||||
def filter_options(self, options, trans, other_values):
|
||||
def filter_options(self, options, trans: ProvidesHistoryContext, other_values):
|
||||
"""Returns a list of options after the filter is applied"""
|
||||
raise TypeError("Abstract Method")
|
||||
|
||||
@@ -94,7 +94,7 @@ class StaticValueFilter(Filter):
|
||||
self.column = d_option.column_spec_to_index(column)
|
||||
self.keep = string_as_bool(elem.get("keep", "True"))
|
||||
|
||||
def filter_options(self, options: Sequence[ParameterOption], trans, other_values):
|
||||
def filter_options(self, options: Sequence[ParameterOption], trans: ProvidesHistoryContext, other_values):
|
||||
rval = []
|
||||
filter_value = self.value
|
||||
try:
|
||||
@@ -130,7 +130,7 @@ class RegexpFilter(Filter):
|
||||
self.column = d_option.column_spec_to_index(column)
|
||||
self.keep = string_as_bool(elem.get("keep", "True"))
|
||||
|
||||
def filter_options(self, options: Sequence[ParameterOption], trans, other_values):
|
||||
def filter_options(self, options: Sequence[ParameterOption], trans: ProvidesHistoryContext, other_values):
|
||||
rval = []
|
||||
filter_value = self.value
|
||||
try:
|
||||
@@ -186,7 +186,7 @@ class DataMetaFilter(Filter):
|
||||
def get_dependency_name(self):
|
||||
return self.ref_name
|
||||
|
||||
def filter_options(self, options: Sequence[ParameterOption], trans: WorkRequestContext | None, other_values):
|
||||
def filter_options(self, options: Sequence[ParameterOption], trans: ProvidesHistoryContext, other_values):
|
||||
options = list(options)
|
||||
if trans and trans.workflow_building_mode is workflow_building_modes.USE_HISTORY:
|
||||
# We're in the run form, can't possibly apply a data_meta filter.
|
||||
@@ -295,7 +295,7 @@ class ParamValueFilter(Filter):
|
||||
def get_dependency_name(self):
|
||||
return self.ref_name
|
||||
|
||||
def filter_options(self, options: Sequence[ParameterOption], trans, other_values):
|
||||
def filter_options(self, options: Sequence[ParameterOption], trans: ProvidesHistoryContext, other_values):
|
||||
ref = other_values.get(self.ref_name, None)
|
||||
if ref is None:
|
||||
ref = []
|
||||
@@ -348,7 +348,7 @@ class UniqueValueFilter(Filter):
|
||||
def get_dependency_name(self):
|
||||
return self.dynamic_option.dataset_ref_name
|
||||
|
||||
def filter_options(self, options: Sequence[ParameterOption], trans, other_values):
|
||||
def filter_options(self, options: Sequence[ParameterOption], trans: ProvidesHistoryContext, other_values):
|
||||
rval = []
|
||||
seen = set()
|
||||
for fields in options:
|
||||
@@ -377,7 +377,7 @@ class MultipleSplitterFilter(Filter):
|
||||
assert columns is not None, "Required 'column' attribute missing from filter"
|
||||
self.columns = [d_option.column_spec_to_index(column) for column in columns.split(",")]
|
||||
|
||||
def filter_options(self, options: Sequence[ParameterOption], trans, other_values):
|
||||
def filter_options(self, options: Sequence[ParameterOption], trans: ProvidesHistoryContext, other_values):
|
||||
rval = []
|
||||
for fields in options:
|
||||
for column in self.columns:
|
||||
@@ -412,7 +412,7 @@ class AttributeValueSplitterFilter(Filter):
|
||||
assert columns is not None, "Required 'column' attribute missing from filter"
|
||||
self.columns = [d_option.column_spec_to_index(column) for column in columns.split(",")]
|
||||
|
||||
def filter_options(self, options, trans, other_values):
|
||||
def filter_options(self, options, trans: ProvidesHistoryContext, other_values):
|
||||
attr_names = set()
|
||||
rval = []
|
||||
for fields in options:
|
||||
@@ -451,7 +451,7 @@ class AdditionalValueFilter(Filter):
|
||||
if self.index is not None:
|
||||
self.index = int(self.index)
|
||||
|
||||
def filter_options(self, options, trans, other_values):
|
||||
def filter_options(self, options, trans: ProvidesHistoryContext, other_values):
|
||||
rval = list(options)
|
||||
add_value = []
|
||||
for _ in range(self.dynamic_option.largest_index + 1):
|
||||
@@ -499,7 +499,7 @@ class RemoveValueFilter(Filter):
|
||||
self.multiple = string_as_bool(elem.get("multiple", "False"))
|
||||
self.separator = elem.get("separator", ",")
|
||||
|
||||
def filter_options(self, options, trans, other_values):
|
||||
def filter_options(self, options, trans: ProvidesHistoryContext, other_values):
|
||||
from galaxy.tools.wrappers import DatasetFilenameWrapper
|
||||
|
||||
if trans is not None and trans.workflow_building_mode:
|
||||
@@ -551,7 +551,7 @@ class SortByColumnFilter(Filter):
|
||||
self.column = d_option.column_spec_to_index(column)
|
||||
self.reverse = string_as_bool(elem.get("reverse_sort_order", "False"))
|
||||
|
||||
def filter_options(self, options, trans, other_values):
|
||||
def filter_options(self, options, trans: ProvidesHistoryContext, other_values):
|
||||
return sorted(options, key=lambda x: x[self.column], reverse=self.reverse)
|
||||
|
||||
|
||||
@@ -586,7 +586,7 @@ class DataTableFilter(Filter):
|
||||
assert self.data_table_column is not None, "Required 'data_table_column' attribute missing from filter"
|
||||
self.keep = string_as_bool(elem.get("keep", "True"))
|
||||
|
||||
def filter_options(self, options, trans, other_values):
|
||||
def filter_options(self, options, trans: ProvidesHistoryContext, other_values):
|
||||
# get column from data table, by index or column name
|
||||
entries = None
|
||||
try:
|
||||
@@ -786,7 +786,7 @@ class DynamicOptions:
|
||||
rval.append(depend)
|
||||
return rval
|
||||
|
||||
def get_fields(self, trans, other_values):
|
||||
def get_fields(self, trans: ProvidesHistoryContext, other_values):
|
||||
if self.dataset_ref_name:
|
||||
try:
|
||||
datasets = _get_ref_data(other_values, self.dataset_ref_name)
|
||||
@@ -914,7 +914,7 @@ class DynamicOptions:
|
||||
assert len(entries) == 1, "Cannot pass tool data bundle with more than 1 data entry per table"
|
||||
return next(iter(entries.values()))
|
||||
|
||||
def get_fields_by_value(self, value, trans, other_values):
|
||||
def get_fields_by_value(self, value, trans: ProvidesHistoryContext, other_values):
|
||||
"""
|
||||
Return a list of fields with column 'value' matching provided value.
|
||||
"""
|
||||
@@ -925,7 +925,7 @@ class DynamicOptions:
|
||||
rval.append(fields)
|
||||
return rval
|
||||
|
||||
def get_field_by_name_for_value(self, field_name, value, trans, other_values):
|
||||
def get_field_by_name_for_value(self, field_name, value, trans: ProvidesHistoryContext, other_values):
|
||||
"""
|
||||
Get contents of field by name for specified value.
|
||||
"""
|
||||
@@ -942,7 +942,7 @@ class DynamicOptions:
|
||||
rval.append(fields[field_index])
|
||||
return rval
|
||||
|
||||
def get_options(self, trans, other_values) -> Sequence[ParameterOption]:
|
||||
def get_options(self, trans: ProvidesHistoryContext, other_values) -> Sequence[ParameterOption]:
|
||||
|
||||
rval: list[ParameterOption] = []
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ from galaxy.util.dictifiable import UsesDictVisibleKeys
|
||||
from galaxy.util.expressions import ExpressionContext
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from galaxy.managers.context import ProvidesHistoryContext
|
||||
from galaxy.tools import Tool
|
||||
from galaxy.tools.parameters import ToolInputsT
|
||||
from galaxy.tools.parameters.basic import ToolParameter
|
||||
@@ -84,13 +85,13 @@ class Group(UsesDictVisibleKeys):
|
||||
"""
|
||||
return value
|
||||
|
||||
def get_initial_value(self, trans, context):
|
||||
def get_initial_value(self, trans: "ProvidesHistoryContext | None", context):
|
||||
"""
|
||||
Return the initial state/value for this group
|
||||
"""
|
||||
raise TypeError("Not implemented")
|
||||
|
||||
def to_dict(self, trans):
|
||||
def to_dict(self, trans: "ProvidesHistoryContext"):
|
||||
group_dict = self._dictify_view_keys()
|
||||
return group_dict
|
||||
|
||||
@@ -167,7 +168,7 @@ class Repeat(Group):
|
||||
raise
|
||||
return rval
|
||||
|
||||
def get_initial_value(self, trans, context):
|
||||
def get_initial_value(self, trans: "ProvidesHistoryContext | None", context):
|
||||
if self.inputs is None:
|
||||
raise Exception("Must set 'inputs' attribute to use.")
|
||||
rval = []
|
||||
@@ -179,7 +180,7 @@ class Repeat(Group):
|
||||
rval.append(rval_dict)
|
||||
return rval
|
||||
|
||||
def to_dict(self, trans):
|
||||
def to_dict(self, trans: "ProvidesHistoryContext"):
|
||||
if self.inputs is None:
|
||||
raise Exception("Must set 'inputs' attribute to use.")
|
||||
repeat_dict = super().to_dict(trans)
|
||||
@@ -235,7 +236,7 @@ class Section(Group):
|
||||
raise
|
||||
return rval
|
||||
|
||||
def get_initial_value(self, trans, context):
|
||||
def get_initial_value(self, trans: "ProvidesHistoryContext | None", context):
|
||||
if self.inputs is None:
|
||||
raise Exception("Must set 'inputs' attribute to use.")
|
||||
rval: dict[str, Any] = {}
|
||||
@@ -244,7 +245,7 @@ class Section(Group):
|
||||
rval[child_input.name] = child_input.get_initial_value(trans, child_context)
|
||||
return rval
|
||||
|
||||
def to_dict(self, trans):
|
||||
def to_dict(self, trans: "ProvidesHistoryContext"):
|
||||
if self.inputs is None:
|
||||
raise Exception("Must set 'inputs' attribute to use.")
|
||||
section_dict = super().to_dict(trans)
|
||||
@@ -328,7 +329,7 @@ class UploadDataset(Group):
|
||||
dbkey = parent_context.get("dbkey", dbkey)
|
||||
return dbkey
|
||||
|
||||
def get_datatype_ext(self, trans, context, parent_context=None):
|
||||
def get_datatype_ext(self, trans: "ProvidesHistoryContext", context, parent_context=None):
|
||||
ext = self.get_file_type(context, parent_context=parent_context)
|
||||
if ext in self.file_type_to_ext:
|
||||
ext = self.file_type_to_ext[
|
||||
@@ -336,7 +337,7 @@ class UploadDataset(Group):
|
||||
] # when using autodetect, we will use composite info from 'text', i.e. only the main file
|
||||
return ext
|
||||
|
||||
def get_datatype(self, trans, context, parent_context=None):
|
||||
def get_datatype(self, trans: "ProvidesHistoryContext", context, parent_context=None):
|
||||
ext = self.get_datatype_ext(trans, context, parent_context=parent_context)
|
||||
return trans.app.datatypes_registry.get_datatype_by_extension(ext)
|
||||
|
||||
@@ -347,7 +348,7 @@ class UploadDataset(Group):
|
||||
def group_title(self, context):
|
||||
return f"{self.title} ({context.get(self.file_type_name, self.default_file_type)})"
|
||||
|
||||
def title_by_index(self, trans, index, context):
|
||||
def title_by_index(self, trans: "ProvidesHistoryContext", index, context):
|
||||
d_type = self.get_datatype(trans, context)
|
||||
for i, (composite_name, composite_file) in enumerate(d_type.writable_files.items()):
|
||||
if i == index:
|
||||
@@ -398,7 +399,7 @@ class UploadDataset(Group):
|
||||
raise
|
||||
return rval
|
||||
|
||||
def get_file_count(self, trans, context):
|
||||
def get_file_count(self, trans: "ProvidesHistoryContext", context):
|
||||
file_count = context.get("file_count", "auto")
|
||||
if file_count == "auto":
|
||||
d_type = self.get_datatype(trans, context)
|
||||
@@ -406,9 +407,10 @@ class UploadDataset(Group):
|
||||
else:
|
||||
return int(file_count)
|
||||
|
||||
def get_initial_value(self, trans, context):
|
||||
def get_initial_value(self, trans: "ProvidesHistoryContext | None", context):
|
||||
if self.inputs is None:
|
||||
raise Exception("Must set 'inputs' attribute to use.")
|
||||
assert trans is not None
|
||||
file_count = self.get_file_count(trans, context)
|
||||
rval = []
|
||||
for i in range(file_count):
|
||||
@@ -419,7 +421,7 @@ class UploadDataset(Group):
|
||||
rval.append(rval_dict)
|
||||
return rval
|
||||
|
||||
def get_uploaded_datasets(self, trans, context, override_name=None, override_info=None):
|
||||
def get_uploaded_datasets(self, trans: "ProvidesHistoryContext", context, override_name=None, override_info=None):
|
||||
def get_data_file_filename(data_file, override_name=None, override_info=None, purge=True):
|
||||
dataset_name = override_name
|
||||
|
||||
@@ -510,6 +512,7 @@ class UploadDataset(Group):
|
||||
warnings.append("All FTP uploaded file selections were ignored.")
|
||||
elif ftp_files is not None and trans.user is not None: # look for files uploaded via FTP
|
||||
user_ftp_dir = trans.user_ftp_dir
|
||||
assert user_ftp_dir is not None
|
||||
assert not os.path.islink(user_ftp_dir), "User FTP directory cannot be a symbolic link"
|
||||
for dirpath, _dirnames, filenames in os.walk(user_ftp_dir):
|
||||
for filename in filenames:
|
||||
@@ -598,16 +601,14 @@ class UploadDataset(Group):
|
||||
# TODO: warning to the user (could happen if session has become invalid)
|
||||
else:
|
||||
user_ftp_dir = trans.user_ftp_dir
|
||||
assert user_ftp_dir is not None
|
||||
assert not os.path.islink(user_ftp_dir), "User FTP directory cannot be a symbolic link"
|
||||
for dirpath, _dirnames, filenames in os.walk(user_ftp_dir):
|
||||
for filename in filenames:
|
||||
path = relpath(os.path.join(dirpath, filename), user_ftp_dir)
|
||||
if not os.path.islink(os.path.join(dirpath, filename)):
|
||||
# Normalize filesystem paths
|
||||
if isinstance(path, str):
|
||||
valid_files.append(unicodedata.normalize("NFC", path))
|
||||
else:
|
||||
valid_files.append(path)
|
||||
valid_files.append(unicodedata.normalize("NFC", path))
|
||||
|
||||
else:
|
||||
ftp_files = []
|
||||
@@ -616,6 +617,7 @@ class UploadDataset(Group):
|
||||
log.warning(f"User passed an invalid file path in ftp_files: {ftp_file}")
|
||||
continue
|
||||
# TODO: warning to the user (could happen if file is already imported)
|
||||
assert user_ftp_dir is not None
|
||||
ftp_data_file = {
|
||||
"local_filename": os.path.abspath(os.path.join(user_ftp_dir, ftp_file)),
|
||||
"filename": os.path.basename(ftp_file),
|
||||
@@ -804,7 +806,7 @@ class Conditional(Group):
|
||||
raise
|
||||
return rval
|
||||
|
||||
def get_initial_value(self, trans, context):
|
||||
def get_initial_value(self, trans: "ProvidesHistoryContext | None", context):
|
||||
if self.test_param is None:
|
||||
raise Exception("Must set 'test_param' attribute to use.")
|
||||
# State for a conditional is a plain dictionary.
|
||||
@@ -823,7 +825,7 @@ class Conditional(Group):
|
||||
rval[child_input.name] = child_input.get_initial_value(trans, child_context)
|
||||
return rval
|
||||
|
||||
def to_dict(self, trans):
|
||||
def to_dict(self, trans: "ProvidesHistoryContext"):
|
||||
if self.test_param is None:
|
||||
raise Exception("Must set 'test_param' attribute to use.")
|
||||
cond_dict = super().to_dict(trans)
|
||||
@@ -843,7 +845,7 @@ class ConditionalWhen(UsesDictVisibleKeys):
|
||||
self.value = None
|
||||
self.inputs = None
|
||||
|
||||
def to_dict(self, trans):
|
||||
def to_dict(self, trans: "ProvidesHistoryContext"):
|
||||
if self.inputs is None:
|
||||
raise Exception("Must set 'inputs' attribute to use.")
|
||||
when_dict = self._dictify_view_keys()
|
||||
|
||||
@@ -3,11 +3,19 @@
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
from collections.abc import Iterable
|
||||
from typing import (
|
||||
Any,
|
||||
)
|
||||
|
||||
import h5py
|
||||
import numpy as np
|
||||
import yaml
|
||||
|
||||
from galaxy.managers.context import (
|
||||
ProvidesAppContext,
|
||||
ProvidesHistoryContext,
|
||||
)
|
||||
from galaxy.tools.parameters import populate_state
|
||||
from galaxy.tools.parameters.workflow_utils import workflow_building_modes
|
||||
from galaxy.util import (
|
||||
@@ -115,7 +123,7 @@ class ToolRecommendations:
|
||||
outputs = Dense(vocab_size, activation="sigmoid")(x)
|
||||
return Model(inputs=inputs, outputs=[outputs, weights])
|
||||
|
||||
def get_predictions(self, trans, tool_sequence, remote_model_url):
|
||||
def get_predictions(self, trans: ProvidesHistoryContext, tool_sequence, remote_model_url):
|
||||
"""
|
||||
Compute tool predictions
|
||||
"""
|
||||
@@ -126,7 +134,7 @@ class ToolRecommendations:
|
||||
recommended_tools = self.__compute_tool_prediction(trans, tool_sequence)
|
||||
return tool_sequence, recommended_tools
|
||||
|
||||
def __set_model(self, trans, remote_model_url):
|
||||
def __set_model(self, trans: ProvidesAppContext, remote_model_url):
|
||||
"""
|
||||
Create model and associated dictionaries for recommendations
|
||||
"""
|
||||
@@ -185,7 +193,7 @@ class ToolRecommendations:
|
||||
model_file.write(model_binary.content)
|
||||
return local_dir
|
||||
|
||||
def __get_tool_extensions(self, trans, tool_id):
|
||||
def __get_tool_extensions(self, trans: ProvidesHistoryContext, tool_id):
|
||||
"""
|
||||
Get the input and output extensions of a tool
|
||||
"""
|
||||
@@ -194,7 +202,7 @@ class ToolRecommendations:
|
||||
trans.workflow_building_mode = workflow_building_modes.ENABLED
|
||||
module = module_factory.from_dict(trans, payload)
|
||||
if "tool_state" not in payload:
|
||||
module_state = {}
|
||||
module_state: dict[str, Any] = {}
|
||||
populate_state(trans, module.get_inputs(), inputs, module_state, check=False)
|
||||
module.recover_state(module_state)
|
||||
inputs = module.get_all_inputs(connectable_only=True)
|
||||
@@ -207,7 +215,9 @@ class ToolRecommendations:
|
||||
output_extensions.extend(o_ext["extensions"])
|
||||
return input_extensions, output_extensions
|
||||
|
||||
def __filter_tool_predictions(self, trans, prediction_data, tool_ids, tool_scores, last_tool_name):
|
||||
def __filter_tool_predictions(
|
||||
self, trans: ProvidesHistoryContext, prediction_data, tool_ids, tool_scores, last_tool_name
|
||||
):
|
||||
"""
|
||||
Filter tool predictions based on datatype compatibility and tool connections.
|
||||
Add admin preferences to recommendations.
|
||||
@@ -216,7 +226,7 @@ class ToolRecommendations:
|
||||
# get the list of datatype extensions of the last tool of the tool sequence
|
||||
_, last_output_extensions = self.__get_tool_extensions(trans, self.all_tools[last_tool_name][0])
|
||||
prediction_data["o_extensions"] = list(set(last_output_extensions))
|
||||
t_ids_scores = zip(tool_ids, tool_scores)
|
||||
t_ids_scores: Iterable[tuple[Any, Any]] = zip(tool_ids, tool_scores)
|
||||
# form the payload of the predicted tools to be shown
|
||||
for child, score in t_ids_scores:
|
||||
c_dict = {}
|
||||
@@ -327,14 +337,14 @@ class ToolRecommendations:
|
||||
sorted_c_t, sorted_c_v = self.__get_predicted_tools(last_base_tools, pred_tool_names, last_tool_name, topk)
|
||||
return sorted_c_t, sorted_c_v
|
||||
|
||||
def __compute_tool_prediction(self, trans, tool_sequence):
|
||||
def __compute_tool_prediction(self, trans: ProvidesHistoryContext, tool_sequence):
|
||||
"""
|
||||
Compute the predicted tools for a tool sequences
|
||||
Return a payload with the tool sequences and recommended tools
|
||||
Return an empty payload with just the tool sequence if anything goes wrong within the try block
|
||||
"""
|
||||
topk = trans.app.config.topk_recommendations
|
||||
prediction_data = {}
|
||||
prediction_data: dict[str, Any] = {}
|
||||
tool_sequence = tool_sequence.split(",")[::-1]
|
||||
prediction_data["name"] = ",".join(tool_sequence)
|
||||
prediction_data["children"] = []
|
||||
@@ -351,15 +361,14 @@ class ToolRecommendations:
|
||||
except Exception:
|
||||
log.exception(f"Failed to find tool {tool_name} in model")
|
||||
return prediction_data
|
||||
sample = np.reshape(sample, (1, self.max_seq_len))
|
||||
model_input = np.reshape(sample, (1, self.max_seq_len))
|
||||
# boost the predicted scores using tools' usage
|
||||
weight_values = list(self.tool_weights_sorted.values())
|
||||
# predict next tools for a test path
|
||||
try:
|
||||
import tensorflow as tf
|
||||
|
||||
sample = tf.convert_to_tensor(sample, dtype=tf.int64)
|
||||
prediction, _ = self.loaded_model(sample, training=False)
|
||||
prediction, _ = self.loaded_model(tf.convert_to_tensor(model_input, dtype=tf.int64), training=False)
|
||||
except Exception as e:
|
||||
log.exception(e)
|
||||
return prediction_data
|
||||
|
||||
@@ -49,6 +49,7 @@ from galaxy.util import (
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from galaxy.datatypes.data import Data
|
||||
from galaxy.datatypes.registry import Registry
|
||||
from galaxy.job_execution.compute_environment import ComputeEnvironment
|
||||
from galaxy.model.metadata import MetadataCollection
|
||||
@@ -359,7 +360,7 @@ class DatasetFilenameWrapper(ToolParameterValueWrapper):
|
||||
compute_environment: Optional["ComputeEnvironment"] = None,
|
||||
identifier: str | None = None,
|
||||
io_type: str = "input",
|
||||
formats: list[str] | None = None,
|
||||
formats: Sequence[Union[str, "Data"]] | None = None,
|
||||
tool_evaluator: Optional["ToolEvaluator"] = None,
|
||||
) -> None:
|
||||
dataset_instance: DatasetInstance | None = None
|
||||
|
||||
@@ -12,6 +12,7 @@ from galaxy import util
|
||||
from galaxy.util.tool_shed import encoding_util
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from galaxy.webapps.base.webapp import GalaxyWebTransaction
|
||||
from .tool_shed_registry import Registry as ToolShedRegistry
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@@ -149,7 +150,7 @@ def get_tool_shed_repository_url(app: HasToolShedRegistry, tool_shed: str, owner
|
||||
return tool_shed_url
|
||||
|
||||
|
||||
def handle_galaxy_url(trans, **kwd):
|
||||
def handle_galaxy_url(trans: "GalaxyWebTransaction", **kwd):
|
||||
galaxy_url = kwd.get("galaxy_url", None)
|
||||
if galaxy_url:
|
||||
trans.set_cookie(galaxy_url, name="toolshedgalaxyurl")
|
||||
|
||||
@@ -21,6 +21,7 @@ from galaxy.datatypes.tabular import (
|
||||
)
|
||||
from galaxy.datatypes.xml import Phyloxml
|
||||
from galaxy.exceptions import RequestParameterInvalidException
|
||||
from galaxy.managers.context import ProvidesAppContext
|
||||
from galaxy.model import NoConverterException
|
||||
from galaxy.visualization.data_providers import genome
|
||||
from galaxy.visualization.data_providers.basic import (
|
||||
@@ -61,7 +62,7 @@ class DataProviderRegistry:
|
||||
"column_with_stats": ColumnDataProvider,
|
||||
}
|
||||
|
||||
def get_data_provider(self, trans, name=None, source="data", raw=False, original_dataset=None):
|
||||
def get_data_provider(self, trans: ProvidesAppContext, name=None, source="data", raw=False, original_dataset=None):
|
||||
"""
|
||||
Returns data provider matching parameter values. For standalone data
|
||||
sources, source parameter is ignored.
|
||||
|
||||
@@ -10,6 +10,10 @@ from galaxy.exceptions import (
|
||||
ObjectNotFound,
|
||||
ReferenceDataError,
|
||||
)
|
||||
from galaxy.managers.context import (
|
||||
ProvidesHistoryContext,
|
||||
ProvidesUserContext,
|
||||
)
|
||||
from galaxy.model import (
|
||||
HistoryDatasetAssociation,
|
||||
User,
|
||||
@@ -286,7 +290,7 @@ class Genomes:
|
||||
|
||||
return dbkeys
|
||||
|
||||
def chroms(self, trans, dbkey=None, num=None, chrom=None, low=None):
|
||||
def chroms(self, trans: ProvidesHistoryContext, dbkey=None, num=None, chrom=None, low=None):
|
||||
"""
|
||||
Returns a naturally sorted list of chroms/contigs for a given dbkey.
|
||||
Use either chrom or low to specify the starting chrom in the return list.
|
||||
@@ -364,7 +368,7 @@ class Genomes:
|
||||
|
||||
return False
|
||||
|
||||
def reference(self, trans, dbkey, chrom, low, high):
|
||||
def reference(self, trans: ProvidesUserContext, dbkey, chrom, low, high):
|
||||
"""
|
||||
Return reference data for a build.
|
||||
"""
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import logging
|
||||
|
||||
from galaxy.managers.context import ProvidesAppContext
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -29,7 +31,7 @@ def _deferred_source_uri(target_object) -> str | None:
|
||||
return None
|
||||
|
||||
|
||||
def is_object_applicable(trans, target_object, data_source_tests):
|
||||
def is_object_applicable(trans: ProvidesAppContext, target_object, data_source_tests):
|
||||
"""
|
||||
Run a visualization's data_source tests to find out if
|
||||
it can be applied to the target_object.
|
||||
|
||||
@@ -20,6 +20,7 @@ from galaxy.visualization.plugins.datasource_testing import is_object_applicable
|
||||
from galaxy.visualization.plugins.plugin import VisualizationPlugin
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from galaxy.managers.context import ProvidesAppContext
|
||||
from galaxy.structured_app import StructuredApp
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@@ -150,7 +151,7 @@ class VisualizationsRegistry:
|
||||
return self.plugins[key]
|
||||
|
||||
# -- building links to visualizations from objects --
|
||||
def get_visualizations(self, trans, target_object=None, embeddable=None):
|
||||
def get_visualizations(self, trans: "ProvidesAppContext", target_object=None, embeddable=None):
|
||||
"""
|
||||
Get the names of visualizations usable on the `target_object` and
|
||||
the urls to call in order to render the visualizations.
|
||||
@@ -166,7 +167,7 @@ class VisualizationsRegistry:
|
||||
result.append(vis_plugin.to_dict())
|
||||
return sorted(result, key=lambda k: k.get("html"))
|
||||
|
||||
def get_visualization(self, trans, visualization_name, target_object):
|
||||
def get_visualization(self, trans: "ProvidesAppContext", visualization_name, target_object):
|
||||
"""
|
||||
Return data to build a url to the visualization with the given
|
||||
`visualization_name` if it's applicable to `target_object` or
|
||||
|
||||
@@ -7,6 +7,7 @@ import json
|
||||
import logging
|
||||
import weakref
|
||||
from collections.abc import Callable
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import galaxy.exceptions
|
||||
import galaxy.util
|
||||
@@ -14,6 +15,7 @@ from galaxy.managers import (
|
||||
hdas as hda_manager,
|
||||
visualizations as visualization_manager,
|
||||
)
|
||||
from galaxy.managers.context import ProvidesUserContext
|
||||
from galaxy.model import (
|
||||
HistoryDatasetAssociation,
|
||||
LibraryDatasetDatasetAssociation,
|
||||
@@ -21,6 +23,9 @@ from galaxy.model import (
|
||||
)
|
||||
from galaxy.util import bunch
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from galaxy.webapps.base.webapp import GalaxyWebTransaction
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -59,7 +64,9 @@ class ResourceParser:
|
||||
hda=app[hda_manager.HDAManager],
|
||||
)
|
||||
|
||||
def parse_parameter_dictionary(self, trans, param_config_dict, query_params, param_modifiers=None):
|
||||
def parse_parameter_dictionary(
|
||||
self, trans: "GalaxyWebTransaction", param_config_dict, query_params, param_modifiers=None
|
||||
):
|
||||
"""
|
||||
Parse all expected params from the query dictionary `query_params`.
|
||||
|
||||
@@ -111,7 +118,7 @@ class ResourceParser:
|
||||
|
||||
return resources
|
||||
|
||||
def parse_config(self, trans, param_config_dict, query_params):
|
||||
def parse_config(self, trans: ProvidesUserContext, param_config_dict, query_params):
|
||||
"""
|
||||
Return `query_params` dict parsing only JSON serializable params.
|
||||
Complex params such as models, etc. are left as the original query value.
|
||||
@@ -149,7 +156,7 @@ class ResourceParser:
|
||||
|
||||
# TODO: I would LOVE to rip modifiers out completely
|
||||
def parse_parameter_modifiers(
|
||||
self, trans, param_modifiers, query_params
|
||||
self, trans: ProvidesUserContext, param_modifiers, query_params
|
||||
) -> dict[str, dict[str, ParameterType | None]]:
|
||||
"""
|
||||
Parse and return parameters that are meant to modify other parameters,
|
||||
@@ -177,7 +184,7 @@ class ResourceParser:
|
||||
|
||||
return parsed_modifiers
|
||||
|
||||
def parse_parameter_default(self, trans, param_config) -> ParameterType | None:
|
||||
def parse_parameter_default(self, trans: ProvidesUserContext, param_config) -> ParameterType | None:
|
||||
"""
|
||||
Parse any default values for the given param, defaulting the default
|
||||
to `None`.
|
||||
@@ -192,7 +199,9 @@ class ResourceParser:
|
||||
# (and adding this code to the xml parser)
|
||||
return self.parse_parameter(trans, param_config, default)
|
||||
|
||||
def parse_parameter(self, trans, expected_param_data, query_param, recurse=True, param_modifiers=None):
|
||||
def parse_parameter(
|
||||
self, trans: ProvidesUserContext, expected_param_data, query_param, recurse=True, param_modifiers=None
|
||||
):
|
||||
"""
|
||||
Use data in `expected_param_data` to parse `query_param` from a string into
|
||||
a resource usable directly by a template.
|
||||
|
||||
@@ -16,7 +16,10 @@ from http.cookies import (
|
||||
SimpleCookie,
|
||||
)
|
||||
from importlib import import_module
|
||||
from typing import NoReturn
|
||||
from typing import (
|
||||
NoReturn,
|
||||
TYPE_CHECKING,
|
||||
)
|
||||
from urllib.parse import urljoin
|
||||
|
||||
import routes
|
||||
@@ -31,6 +34,9 @@ from paste.response import HeaderDict
|
||||
from galaxy.util import smart_str
|
||||
from galaxy.util.resources import resource_string
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from galaxy.webapps.base.webapp import GalaxyWebTransaction
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
#: time of the most recent server startup
|
||||
@@ -281,7 +287,7 @@ class WebApplication:
|
||||
body_renderer = body_renderer or self._render_body
|
||||
return body_renderer(trans, body, environ, start_response)
|
||||
|
||||
def _render_body(self, trans, body, environ, start_response):
|
||||
def _render_body(self, trans: "GalaxyWebTransaction", body, environ, start_response):
|
||||
# Now figure out what we got back and try to get it to the browser in
|
||||
# a smart way
|
||||
if callable(body):
|
||||
@@ -299,7 +305,7 @@ class WebApplication:
|
||||
start_response(trans.response.wsgi_status(), trans.response.wsgi_headeritems())
|
||||
return self.make_body_iterable(trans, body)
|
||||
|
||||
def make_body_iterable(self, trans, body):
|
||||
def make_body_iterable(self, trans: "DefaultWebTransaction", body):
|
||||
if isinstance(body, (types.GeneratorType, list, tuple)):
|
||||
# Recursively stream the iterable
|
||||
return flatten(body)
|
||||
@@ -310,7 +316,7 @@ class WebApplication:
|
||||
# Worst case scenario
|
||||
return [smart_str(body)]
|
||||
|
||||
def handle_controller_exception(self, e, trans, method, kwargs):
|
||||
def handle_controller_exception(self, e, trans: "GalaxyWebTransaction", method, kwargs):
|
||||
"""
|
||||
Allow handling of exceptions raised in controller methods.
|
||||
"""
|
||||
@@ -366,6 +372,10 @@ class DefaultWebTransaction:
|
||||
self.environ = environ
|
||||
self.request = Request(environ)
|
||||
self.response = Response()
|
||||
# Set by WebApplication.handle_request() once the route is resolved.
|
||||
self.request_id: str | None = None
|
||||
self.controller: str | None = None
|
||||
self.action: str | None = None
|
||||
|
||||
@lazy_property
|
||||
def session(self):
|
||||
@@ -547,7 +557,7 @@ class Response:
|
||||
CHUNK_SIZE = 2**16
|
||||
|
||||
|
||||
def send_file(start_response, trans, body):
|
||||
def send_file(start_response, trans: "GalaxyWebTransaction", body):
|
||||
# If configured use X-Accel-Redirect header for nginx
|
||||
base = trans.app.config.nginx_x_accel_redirect_base
|
||||
apache_xsendfile = trans.app.config.apache_xsendfile
|
||||
|
||||
@@ -3,6 +3,7 @@ from functools import wraps
|
||||
from inspect import getfullargspec
|
||||
from json import loads
|
||||
from traceback import format_exc
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import paste.httpexceptions
|
||||
from pydantic import (
|
||||
@@ -26,6 +27,9 @@ from galaxy.util import (
|
||||
from galaxy.util.json import safe_dumps
|
||||
from galaxy.web.framework import url_for
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from galaxy.webapps.base.webapp import GalaxyWebTransaction
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
JSON_CONTENT_TYPE = "application/json; charset=UTF-8"
|
||||
@@ -59,7 +63,7 @@ def json(func, pretty=False):
|
||||
"""
|
||||
|
||||
@wraps(func)
|
||||
def call_and_format(self, trans, *args, **kwargs):
|
||||
def call_and_format(self, trans: "GalaxyWebTransaction", *args, **kwargs):
|
||||
# pull out any callback argument to the api endpoint and set the content type to json or javascript
|
||||
jsonp_callback = kwargs.pop(JSONP_CALLBACK_KEY, None)
|
||||
if jsonp_callback:
|
||||
@@ -84,7 +88,7 @@ def json_pretty(func):
|
||||
def require_login(verb="perform this action", use_panels=False):
|
||||
def argcatcher(func):
|
||||
@wraps(func)
|
||||
def decorator(self, trans, *args, **kwargs):
|
||||
def decorator(self, trans: "GalaxyWebTransaction", *args, **kwargs):
|
||||
if trans.get_user():
|
||||
return func(self, trans, *args, **kwargs)
|
||||
else:
|
||||
@@ -105,7 +109,7 @@ def require_login(verb="perform this action", use_panels=False):
|
||||
|
||||
def require_admin(func):
|
||||
@wraps(func)
|
||||
def decorator(self, trans, *args, **kwargs):
|
||||
def decorator(self, trans: "GalaxyWebTransaction", *args, **kwargs):
|
||||
if not trans.user_is_admin:
|
||||
msg = require_admin_message(trans.app.config, trans.get_user())
|
||||
trans.response.status = 403
|
||||
@@ -136,7 +140,7 @@ def do_not_cache(func):
|
||||
"""
|
||||
|
||||
@wraps(func)
|
||||
def set_nocache_headers(self, trans, *args, **kwargs):
|
||||
def set_nocache_headers(self, trans: "GalaxyWebTransaction", *args, **kwargs):
|
||||
trans.response.headers["Cache-Control"] = ["no-cache", "no-store", "must-revalidate"]
|
||||
trans.response.headers["Pragma"] = "no-cache"
|
||||
trans.response.headers["Expires"] = "0"
|
||||
@@ -152,7 +156,7 @@ def legacy_expose_api(func, to_json=True, user_required=True):
|
||||
"""
|
||||
|
||||
@wraps(func)
|
||||
def decorator(self, trans, *args, **kwargs):
|
||||
def decorator(self, trans: "GalaxyWebTransaction", *args, **kwargs):
|
||||
def error(environ, start_response):
|
||||
start_response(error_status, [("Content-type", "text/plain")])
|
||||
return [smart_str(error_message)]
|
||||
@@ -211,7 +215,7 @@ def legacy_expose_api(func, to_json=True, user_required=True):
|
||||
return expose(_save_orig_fn(decorator, func))
|
||||
|
||||
|
||||
def __extract_payload_from_request(trans, func, kwargs):
|
||||
def __extract_payload_from_request(trans: "GalaxyWebTransaction", func, kwargs):
|
||||
content_type = trans.request.headers.get("content-type", "")
|
||||
if content_type.startswith("application/x-www-form-urlencoded") or content_type.startswith("multipart/form-data"):
|
||||
# If the content type is a standard type such as multipart/form-data, the wsgi framework parses the request body
|
||||
@@ -276,7 +280,7 @@ def expose_api(func, to_json=True, user_required=True, user_or_session_required=
|
||||
"""
|
||||
|
||||
@wraps(func)
|
||||
def decorator(self, trans, *args, **kwargs):
|
||||
def decorator(self, trans: "GalaxyWebTransaction", *args, **kwargs):
|
||||
# errors passed in from trans._authenticate_api
|
||||
if trans.error_message:
|
||||
return __api_error_response(
|
||||
@@ -385,14 +389,14 @@ def format_return_as_json(rval, jsonp_callback=None, pretty=False):
|
||||
return json
|
||||
|
||||
|
||||
def __api_error_dict(trans, **kwds):
|
||||
def __api_error_dict(trans: "GalaxyWebTransaction", **kwds):
|
||||
error_dict = api_error_to_dict(debug=trans.debug, **kwds)
|
||||
exception = kwds.get("exception", None)
|
||||
# If we are given an status code directly - use it - otherwise check
|
||||
# the exception for a status_code attribute.
|
||||
if "status_code" in kwds:
|
||||
status_code = int(kwds.get("status_code"))
|
||||
elif hasattr(exception, "status_code"):
|
||||
status_code = int(kwds["status_code"])
|
||||
elif exception is not None and hasattr(exception, "status_code"):
|
||||
status_code = int(exception.status_code)
|
||||
else:
|
||||
status_code = 500
|
||||
@@ -405,7 +409,7 @@ def __api_error_dict(trans, **kwds):
|
||||
return error_dict
|
||||
|
||||
|
||||
def __api_error_response(trans, **kwds):
|
||||
def __api_error_response(trans: "GalaxyWebTransaction", **kwds):
|
||||
error_dict = __api_error_dict(trans, **kwds)
|
||||
return safe_dumps(error_dict)
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import logging
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from markupsafe import escape
|
||||
|
||||
@@ -7,6 +8,9 @@ from galaxy.util import (
|
||||
unicodify,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from galaxy.webapps.base.webapp import GalaxyWebTransaction
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -28,7 +32,7 @@ class GridColumn:
|
||||
self.format = format
|
||||
self.escape = escape
|
||||
|
||||
def get_value(self, trans, grid, item):
|
||||
def get_value(self, trans: "GalaxyWebTransaction", grid, item):
|
||||
if self.method:
|
||||
value = getattr(grid, self.method)(trans, item)
|
||||
elif self.key and hasattr(item, self.key):
|
||||
@@ -42,7 +46,7 @@ class GridColumn:
|
||||
else:
|
||||
return value
|
||||
|
||||
def sort(self, trans, query, ascending, column_name=None):
|
||||
def sort(self, trans: "GalaxyWebTransaction", query, ascending, column_name=None):
|
||||
"""Sort query using this column."""
|
||||
if column_name is None:
|
||||
column_name = self.key
|
||||
@@ -64,6 +68,8 @@ class GridData:
|
||||
model_class: type | None = None
|
||||
columns: list[GridColumn] = []
|
||||
default_limit: int = 1000
|
||||
# Subclasses provide the default sort column key.
|
||||
default_sort_key: str
|
||||
|
||||
def __init__(self):
|
||||
# If a column does not have a model class, set the column's model class
|
||||
@@ -72,7 +78,11 @@ class GridData:
|
||||
if not column.model_class:
|
||||
column.model_class = self.model_class
|
||||
|
||||
def __call__(self, trans, **kwargs):
|
||||
def apply_query_filter(self, query, **kwargs):
|
||||
# Subclasses override this to restrict the grid's base query.
|
||||
raise NotImplementedError
|
||||
|
||||
def __call__(self, trans: "GalaxyWebTransaction", **kwargs):
|
||||
limit = kwargs.get("limit", self.default_limit)
|
||||
offset = kwargs.get("offset", 0)
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import logging
|
||||
import os
|
||||
import time
|
||||
from collections import namedtuple
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from galaxy.util import (
|
||||
requests,
|
||||
@@ -17,6 +18,10 @@ from galaxy.util.lazy_process import (
|
||||
)
|
||||
from galaxy.web.framework import url_for
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from galaxy.config import GalaxyAppConfiguration
|
||||
from galaxy.webapps.base.webapp import GalaxyWebTransaction
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -31,23 +36,20 @@ class ProxyManager:
|
||||
"port",
|
||||
)
|
||||
|
||||
def __init__(self, config):
|
||||
for option in [
|
||||
"manage_dynamic_proxy",
|
||||
"dynamic_proxy_bind_port",
|
||||
"dynamic_proxy_bind_ip",
|
||||
"dynamic_proxy_debug",
|
||||
"dynamic_proxy_external_proxy",
|
||||
"dynamic_proxy_prefix",
|
||||
"proxy_session_map",
|
||||
"dynamic_proxy",
|
||||
"cookie_path",
|
||||
"dynamic_proxy_golang_noaccess",
|
||||
"dynamic_proxy_golang_clean_interval",
|
||||
"dynamic_proxy_golang_docker_address",
|
||||
"dynamic_proxy_golang_api_key",
|
||||
]:
|
||||
setattr(self, option, getattr(config, option))
|
||||
def __init__(self, config: "GalaxyAppConfiguration"):
|
||||
self.manage_dynamic_proxy = config.manage_dynamic_proxy
|
||||
self.dynamic_proxy_bind_port = config.dynamic_proxy_bind_port
|
||||
self.dynamic_proxy_bind_ip = config.dynamic_proxy_bind_ip
|
||||
self.dynamic_proxy_debug = config.dynamic_proxy_debug
|
||||
self.dynamic_proxy_external_proxy = config.dynamic_proxy_external_proxy
|
||||
self.dynamic_proxy_prefix = config.dynamic_proxy_prefix
|
||||
self.proxy_session_map = config.proxy_session_map
|
||||
self.dynamic_proxy = config.dynamic_proxy
|
||||
self.cookie_path = config.cookie_path
|
||||
self.dynamic_proxy_golang_noaccess = config.dynamic_proxy_golang_noaccess
|
||||
self.dynamic_proxy_golang_clean_interval = config.dynamic_proxy_golang_clean_interval
|
||||
self.dynamic_proxy_golang_docker_address = config.dynamic_proxy_golang_docker_address
|
||||
self.dynamic_proxy_golang_api_key = config.dynamic_proxy_golang_api_key
|
||||
|
||||
if self.manage_dynamic_proxy:
|
||||
self.lazy_process = self.__setup_lazy_process(config)
|
||||
@@ -64,7 +66,7 @@ class ProxyManager:
|
||||
|
||||
def setup_proxy(
|
||||
self,
|
||||
trans,
|
||||
trans: "GalaxyWebTransaction",
|
||||
host=DEFAULT_PROXY_TO_HOST,
|
||||
port=None,
|
||||
proxy_prefix="",
|
||||
@@ -105,14 +107,14 @@ class ProxyManager:
|
||||
"proxied_host": proxy_requests.host,
|
||||
}
|
||||
|
||||
def update_proxy(self, trans, **kwargs):
|
||||
def update_proxy(self, trans: "GalaxyWebTransaction", **kwargs):
|
||||
authentication = AuthenticationToken(trans)
|
||||
for k in kwargs.keys():
|
||||
if k not in self.valid_update_keys:
|
||||
raise Exception(f"Invalid proxy request update key: {k}")
|
||||
return self.proxy_ipc.update_requests(authentication, **kwargs)
|
||||
|
||||
def query_proxy(self, trans):
|
||||
def query_proxy(self, trans: "GalaxyWebTransaction"):
|
||||
authentication = AuthenticationToken(trans)
|
||||
return self.proxy_ipc.fetch_requests(authentication)
|
||||
|
||||
@@ -182,7 +184,7 @@ class GolangProxyLauncher:
|
||||
|
||||
|
||||
class AuthenticationToken:
|
||||
def __init__(self, trans):
|
||||
def __init__(self, trans: "GalaxyWebTransaction"):
|
||||
self.cookie_name = SECURE_COOKIE
|
||||
self.cookie_value = trans.get_cookie(self.cookie_name)
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import logging
|
||||
from collections.abc import Callable
|
||||
from typing import (
|
||||
Any,
|
||||
Protocol,
|
||||
TYPE_CHECKING,
|
||||
)
|
||||
|
||||
@@ -27,6 +28,10 @@ from galaxy.managers import (
|
||||
users,
|
||||
workflows,
|
||||
)
|
||||
from galaxy.managers.context import (
|
||||
ProvidesHistoryContext,
|
||||
ProvidesUserContext,
|
||||
)
|
||||
from galaxy.managers.forms import (
|
||||
get_filtered_form_definitions_current,
|
||||
get_form_definitions,
|
||||
@@ -62,6 +67,7 @@ from galaxy.workflow.modules import WorkflowModuleInjector
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from galaxy.structured_app import StructuredApp
|
||||
from galaxy.webapps.base.webapp import GalaxyWebTransaction
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -88,7 +94,9 @@ class BaseController:
|
||||
"""Returns the class object that a string denotes. Without this method, we'd have to do eval(<class_name>)."""
|
||||
return managers_base.get_class(class_name)
|
||||
|
||||
def get_object(self, trans, id, class_name, check_ownership=False, check_accessible=False, deleted=None):
|
||||
def get_object(
|
||||
self, trans: ProvidesUserContext, id, class_name, check_ownership=False, check_accessible=False, deleted=None
|
||||
):
|
||||
"""
|
||||
Convenience method to get a model object with the specified checks.
|
||||
"""
|
||||
@@ -103,20 +111,20 @@ class BaseController:
|
||||
# # meant to be overridden in SharableSecurityMixin
|
||||
# return item
|
||||
|
||||
def get_user(self, trans, id, check_ownership=False, check_accessible=False, deleted=None):
|
||||
def get_user(self, trans: ProvidesUserContext, id, check_ownership=False, check_accessible=False, deleted=None):
|
||||
return self.get_object(trans, id, "User", check_ownership=False, check_accessible=False, deleted=deleted)
|
||||
|
||||
def get_group(self, trans, id, check_ownership=False, check_accessible=False, deleted=None):
|
||||
def get_group(self, trans: ProvidesUserContext, id, check_ownership=False, check_accessible=False, deleted=None):
|
||||
return self.get_object(trans, id, "Group", check_ownership=False, check_accessible=False, deleted=deleted)
|
||||
|
||||
def get_role(self, trans, id, check_ownership=False, check_accessible=False, deleted=None):
|
||||
def get_role(self, trans: ProvidesUserContext, id, check_ownership=False, check_accessible=False, deleted=None):
|
||||
return self.get_object(trans, id, "Role", check_ownership=False, check_accessible=False, deleted=deleted)
|
||||
|
||||
# ---- parsing query params
|
||||
def decode_id(self, id):
|
||||
return managers_base.decode_id(self.app, id)
|
||||
|
||||
def encode_all_ids(self, trans, rval, recursive=False):
|
||||
def encode_all_ids(self, trans: ProvidesUserContext, rval, recursive=False):
|
||||
"""
|
||||
Encodes all integer values in the dict rval whose keys are 'id' or end with '_id'
|
||||
|
||||
@@ -176,7 +184,9 @@ Root = BaseController
|
||||
|
||||
|
||||
class BaseUIController(BaseController):
|
||||
def get_object(self, trans, id, class_name, check_ownership=False, check_accessible=False, deleted=None):
|
||||
def get_object(
|
||||
self, trans: ProvidesUserContext, id, class_name, check_ownership=False, check_accessible=False, deleted=None
|
||||
):
|
||||
try:
|
||||
return BaseController.get_object(
|
||||
self,
|
||||
@@ -193,13 +203,15 @@ class BaseUIController(BaseController):
|
||||
log.exception("Exception in get_object check for %s %s:", class_name, str(id))
|
||||
raise Exception(f"Server error retrieving {class_name} id ( {str(id)} ).")
|
||||
|
||||
def message_exception(self, trans, message, sanitize=True):
|
||||
def message_exception(self, trans: "GalaxyWebTransaction", message, sanitize=True):
|
||||
trans.response.status = 400
|
||||
return {"err_msg": util.sanitize_text(message) if sanitize else message}
|
||||
|
||||
|
||||
class BaseAPIController(BaseController):
|
||||
def get_object(self, trans, id, class_name, check_ownership=False, check_accessible=False, deleted=None):
|
||||
def get_object(
|
||||
self, trans: ProvidesUserContext, id, class_name, check_ownership=False, check_accessible=False, deleted=None
|
||||
):
|
||||
try:
|
||||
return BaseController.get_object(
|
||||
self,
|
||||
@@ -217,7 +229,7 @@ class BaseAPIController(BaseController):
|
||||
log.exception("Exception in get_object check for %s %s.", class_name, str(id))
|
||||
raise HTTPInternalServerError(comment=util.unicodify(e))
|
||||
|
||||
def not_implemented(self, trans, **kwd):
|
||||
def not_implemented(self, trans: ProvidesUserContext, **kwd):
|
||||
raise HTTPNotImplemented()
|
||||
|
||||
def _parse_serialization_params(self, kwd, default_view):
|
||||
@@ -253,7 +265,7 @@ class Datatype:
|
||||
class SharableItemSecurityMixin:
|
||||
"""Mixin for handling security for sharable items."""
|
||||
|
||||
def security_check(self, trans, item, check_ownership=False, check_accessible=False):
|
||||
def security_check(self, trans: ProvidesUserContext, item, check_ownership=False, check_accessible=False):
|
||||
"""Security checks for an item: checks if (a) user owns item or (b) item is accessible to user."""
|
||||
return managers_base.security_check(
|
||||
trans, item, check_ownership=check_ownership, check_accessible=check_accessible
|
||||
@@ -263,10 +275,12 @@ class SharableItemSecurityMixin:
|
||||
class UsesLibraryMixinItems(SharableItemSecurityMixin):
|
||||
get_object: Callable
|
||||
|
||||
def get_library_folder(self, trans, id: int, check_ownership=False, check_accessible=True):
|
||||
def get_library_folder(self, trans: ProvidesUserContext, id: int, check_ownership=False, check_accessible=True):
|
||||
return self.get_object(trans, id, "LibraryFolder", check_ownership=False, check_accessible=check_accessible)
|
||||
|
||||
def get_library_dataset_dataset_association(self, trans, id, check_ownership=False, check_accessible=True):
|
||||
def get_library_dataset_dataset_association(
|
||||
self, trans: ProvidesUserContext, id, check_ownership=False, check_accessible=True
|
||||
):
|
||||
# Deprecated in lieu to galaxy.managers.lddas.LDDAManager.get() but not
|
||||
# reusing that exactly because of subtle differences in exception handling
|
||||
# logic (API controller override get_object to be slightly different).
|
||||
@@ -274,7 +288,7 @@ class UsesLibraryMixinItems(SharableItemSecurityMixin):
|
||||
trans, id, "LibraryDatasetDatasetAssociation", check_ownership=False, check_accessible=check_accessible
|
||||
)
|
||||
|
||||
def get_library_dataset(self, trans, id, check_ownership=False, check_accessible=True):
|
||||
def get_library_dataset(self, trans: ProvidesUserContext, id, check_ownership=False, check_accessible=True):
|
||||
return self.get_object(trans, id, "LibraryDataset", check_ownership=False, check_accessible=check_accessible)
|
||||
|
||||
# TODO: it makes no sense that I can get roles from a user but not user.is_admin()
|
||||
@@ -283,14 +297,14 @@ class UsesLibraryMixinItems(SharableItemSecurityMixin):
|
||||
# return ( ( user.is_admin() )
|
||||
# or ( trans.app.security_agent.can_add_library_item( user.all_roles(), item ) ) )
|
||||
|
||||
def can_current_user_add_to_library_item(self, trans, item):
|
||||
def can_current_user_add_to_library_item(self, trans: ProvidesUserContext, item):
|
||||
if not trans.user:
|
||||
return False
|
||||
return trans.user_is_admin or trans.app.security_agent.can_add_library_item(
|
||||
trans.get_current_user_roles(), item
|
||||
)
|
||||
|
||||
def check_user_can_add_to_library_item(self, trans, item, check_accessible=True):
|
||||
def check_user_can_add_to_library_item(self, trans: ProvidesUserContext, item, check_accessible=True):
|
||||
"""
|
||||
Raise exception if user cannot add to the specified library item (i.e.
|
||||
Folder). Can set check_accessible to False if folder was loaded with
|
||||
@@ -311,7 +325,9 @@ class UsesLibraryMixinItems(SharableItemSecurityMixin):
|
||||
# Slight misuse of ItemOwnershipException?
|
||||
raise exceptions.ItemOwnershipException("User cannot add to library item.")
|
||||
|
||||
def _copy_hdca_to_library_folder(self, trans, hda_manager, from_hdca_id: int, folder_id: int, ldda_message=""):
|
||||
def _copy_hdca_to_library_folder(
|
||||
self, trans: ProvidesHistoryContext, hda_manager, from_hdca_id: int, folder_id: int, ldda_message=""
|
||||
):
|
||||
"""
|
||||
Fetches the collection identified by `from_hcda_id` and dispatches individual collection elements to
|
||||
_copy_hda_to_library_folder
|
||||
@@ -337,7 +353,13 @@ class UsesLibraryMixinItems(SharableItemSecurityMixin):
|
||||
]
|
||||
|
||||
def _copy_hda_to_library_folder(
|
||||
self, trans, hda_manager, from_hda_id: int, folder_id: int, ldda_message="", element_identifier=None
|
||||
self,
|
||||
trans: ProvidesHistoryContext,
|
||||
hda_manager,
|
||||
from_hda_id: int,
|
||||
folder_id: int,
|
||||
ldda_message="",
|
||||
element_identifier=None,
|
||||
):
|
||||
"""
|
||||
Copies hda ``from_hda_id`` to library folder ``folder_id``, optionally
|
||||
@@ -383,7 +405,7 @@ class UsesLibraryMixinItems(SharableItemSecurityMixin):
|
||||
return rval
|
||||
|
||||
def copy_hda_to_library_folder(
|
||||
self, trans, hda, library_folder, roles=None, ldda_message="", element_identifier=None
|
||||
self, trans: ProvidesUserContext, hda, library_folder, roles=None, ldda_message="", element_identifier=None
|
||||
):
|
||||
# PRECONDITION: permissions for this action on hda and library_folder have been checked
|
||||
roles = roles or []
|
||||
@@ -407,7 +429,7 @@ class UsesLibraryMixinItems(SharableItemSecurityMixin):
|
||||
# then finally, re-applies hda -> ldda for missing actions in _apply_hda_permissions_to_ldda??
|
||||
return ldda
|
||||
|
||||
def _apply_library_folder_permissions_to_ldda(self, trans, library_folder, ldda):
|
||||
def _apply_library_folder_permissions_to_ldda(self, trans: ProvidesUserContext, library_folder, ldda):
|
||||
"""
|
||||
Copy actions/roles from library folder to an ldda (and its library_dataset).
|
||||
"""
|
||||
@@ -417,7 +439,7 @@ class UsesLibraryMixinItems(SharableItemSecurityMixin):
|
||||
security_agent.copy_library_permissions(trans, library_folder, ldda.library_dataset)
|
||||
return security_agent.get_permissions(ldda)
|
||||
|
||||
def _apply_hda_permissions_to_ldda(self, trans, hda, ldda):
|
||||
def _apply_hda_permissions_to_ldda(self, trans: ProvidesUserContext, hda, ldda):
|
||||
"""
|
||||
Copy actions/roles from hda to ldda.library_dataset (and then ldda) if ldda
|
||||
doesn't already have roles for the given action.
|
||||
@@ -432,8 +454,11 @@ class UsesLibraryMixinItems(SharableItemSecurityMixin):
|
||||
|
||||
# except that: if DATASET_MANAGE_PERMISSIONS exists in the hda.dataset permissions,
|
||||
# we need to instead apply those roles to the LIBRARY_MANAGE permission to the library dataset
|
||||
dataset_manage_permissions_action = security_agent.get_action("DATASET_MANAGE_PERMISSIONS").action
|
||||
library_manage_permissions_action = security_agent.get_action("LIBRARY_MANAGE").action
|
||||
dataset_manage_permissions = security_agent.get_action("DATASET_MANAGE_PERMISSIONS")
|
||||
library_manage_permissions = security_agent.get_action("LIBRARY_MANAGE")
|
||||
assert dataset_manage_permissions is not None and library_manage_permissions is not None
|
||||
dataset_manage_permissions_action = dataset_manage_permissions.action
|
||||
library_manage_permissions_action = library_manage_permissions.action
|
||||
# TODO: test this and remove if in loop below
|
||||
# TODO: doesn't handle action.action
|
||||
# if dataset_manage_permissions_action in dataset_permissions_dict:
|
||||
@@ -454,11 +479,9 @@ class UsesLibraryMixinItems(SharableItemSecurityMixin):
|
||||
# NOTE: only apply an hda perm if it's NOT set in the library_dataset perms (don't overwrite)
|
||||
if action not in library_dataset_actions:
|
||||
for role in dataset_permissions_roles:
|
||||
ldps = LibraryDatasetPermissions(action, library_dataset, role)
|
||||
ldps = [ldps] if not isinstance(ldps, list) else ldps
|
||||
for ldp in ldps:
|
||||
trans.sa_session.add(ldp)
|
||||
flush_needed = True
|
||||
ldp = LibraryDatasetPermissions(action, library_dataset, role)
|
||||
trans.sa_session.add(ldp)
|
||||
flush_needed = True
|
||||
|
||||
if flush_needed:
|
||||
trans.sa_session.commit()
|
||||
@@ -473,22 +496,25 @@ class UsesVisualizationMixin(UsesLibraryMixinItems):
|
||||
Mixin for controllers that use Visualization objects.
|
||||
"""
|
||||
|
||||
app: "StructuredApp"
|
||||
slug_builder = SlugBuilder()
|
||||
|
||||
def get_visualization_config(self, trans, visualization):
|
||||
def get_visualization_config(self, trans: ProvidesUserContext, visualization):
|
||||
"""Returns a visualization's configuration."""
|
||||
latest_revision = visualization.latest_revision
|
||||
config = latest_revision.config
|
||||
return config
|
||||
|
||||
def get_hda_or_ldda(self, trans, hda_ldda, dataset_id):
|
||||
def get_hda_or_ldda(self, trans: "GalaxyWebTransaction", hda_ldda, dataset_id):
|
||||
"""Returns either HDA or LDDA for hda/ldda and id combination."""
|
||||
if hda_ldda == "hda":
|
||||
return self.get_hda(trans, dataset_id, check_ownership=False, check_accessible=True)
|
||||
else:
|
||||
return self.get_library_dataset_dataset_association(trans, dataset_id)
|
||||
|
||||
def get_hda(self, trans, dataset_id, check_ownership=True, check_accessible=False, check_state=True):
|
||||
def get_hda(
|
||||
self, trans: "GalaxyWebTransaction", dataset_id, check_ownership=True, check_accessible=False, check_state=True
|
||||
):
|
||||
"""
|
||||
Get an HDA object by id performing security checks using
|
||||
the current transaction.
|
||||
@@ -528,7 +554,7 @@ class UsesVisualizationMixin(UsesLibraryMixinItems):
|
||||
)
|
||||
return data
|
||||
|
||||
def _get_genome_data(self, trans, dataset, dbkey=None):
|
||||
def _get_genome_data(self, trans: ProvidesHistoryContext, dataset, dbkey=None):
|
||||
"""
|
||||
Returns genome-wide data for dataset if available; if not, message is returned.
|
||||
"""
|
||||
@@ -551,7 +577,7 @@ class UsesVisualizationMixin(UsesLibraryMixinItems):
|
||||
if isinstance(dataset.datatype, ChromatinInteractions):
|
||||
source = "data"
|
||||
|
||||
data_provider = trans.app.data_provider_registry.get_data_provider(
|
||||
data_provider = self.app.data_provider_registry.get_data_provider(
|
||||
trans, original_dataset=dataset, source=source
|
||||
)
|
||||
# HACK: pass in additional params which are used for only some
|
||||
@@ -589,7 +615,7 @@ class UsesStoredWorkflowMixin(SharableItemSecurityMixin, UsesAnnotations):
|
||||
app: "StructuredApp"
|
||||
slug_builder = SlugBuilder()
|
||||
|
||||
def get_stored_workflow(self, trans, id, check_ownership=True, check_accessible=False):
|
||||
def get_stored_workflow(self, trans: ProvidesUserContext, id, check_ownership=True, check_accessible=False):
|
||||
"""Get a StoredWorkflow from the database by id, verifying ownership."""
|
||||
# Load workflow from database
|
||||
workflow_contents_manager = workflows.WorkflowsManager(self.app)
|
||||
@@ -607,7 +633,7 @@ class UsesStoredWorkflowMixin(SharableItemSecurityMixin, UsesAnnotations):
|
||||
|
||||
return workflow
|
||||
|
||||
def get_stored_workflow_steps(self, trans, stored_workflow: StoredWorkflow):
|
||||
def get_stored_workflow_steps(self, trans: ProvidesHistoryContext, stored_workflow: StoredWorkflow):
|
||||
"""Restores states for a stored workflow's steps."""
|
||||
module_injector = WorkflowModuleInjector(trans)
|
||||
workflow = stored_workflow.latest_workflow
|
||||
@@ -618,7 +644,7 @@ class UsesStoredWorkflowMixin(SharableItemSecurityMixin, UsesAnnotations):
|
||||
except exceptions.ToolMissingException:
|
||||
pass
|
||||
|
||||
def _import_shared_workflow(self, trans, stored: StoredWorkflow):
|
||||
def _import_shared_workflow(self, trans: ProvidesUserContext, stored: StoredWorkflow):
|
||||
"""Imports a shared workflow"""
|
||||
# Copy workflow.
|
||||
imported_stored = StoredWorkflow()
|
||||
@@ -642,7 +668,7 @@ class UsesStoredWorkflowMixin(SharableItemSecurityMixin, UsesAnnotations):
|
||||
session.commit()
|
||||
return imported_stored
|
||||
|
||||
def _workflow_to_dict(self, trans, stored: StoredWorkflow) -> dict[str, Any]:
|
||||
def _workflow_to_dict(self, trans: ProvidesHistoryContext, stored: StoredWorkflow) -> dict[str, Any]:
|
||||
"""
|
||||
Converts a workflow to a dict of attributes suitable for exporting.
|
||||
"""
|
||||
@@ -656,7 +682,7 @@ class UsesStoredWorkflowMixin(SharableItemSecurityMixin, UsesAnnotations):
|
||||
class UsesFormDefinitionsMixin:
|
||||
"""Mixin for controllers that use Galaxy form objects."""
|
||||
|
||||
def get_all_forms(self, trans, all_versions=False, filter=None, form_type="All"):
|
||||
def get_all_forms(self, trans: ProvidesUserContext, all_versions=False, filter=None, form_type="All"):
|
||||
"""
|
||||
Return all the latest forms from the form_definition_current table
|
||||
if all_versions is set to True. Otherwise return all the versions
|
||||
@@ -673,7 +699,7 @@ class UsesFormDefinitionsMixin:
|
||||
else:
|
||||
return [fdc.latest_form for fdc in fdc_list if fdc.latest_form.type == form_type]
|
||||
|
||||
def save_widget_field(self, trans, field_obj, widget_name, **kwd):
|
||||
def save_widget_field(self, trans: ProvidesUserContext, field_obj, widget_name, **kwd):
|
||||
# Save a form_builder field object
|
||||
params = util.Params(kwd)
|
||||
if isinstance(field_obj, trans.model.UserAddress):
|
||||
@@ -689,7 +715,7 @@ class UsesFormDefinitionsMixin:
|
||||
trans.sa_session.add(field_obj)
|
||||
trans.sa_session.commit()
|
||||
|
||||
def get_form_values(self, trans, user, form_definition, **kwd):
|
||||
def get_form_values(self, trans: ProvidesUserContext, user, form_definition, **kwd):
|
||||
"""
|
||||
Returns the name:value dictionary containing all the form values
|
||||
"""
|
||||
@@ -699,6 +725,7 @@ class UsesFormDefinitionsMixin:
|
||||
field_type = field["type"]
|
||||
field_name = field["name"]
|
||||
input_value = params.get(field_name, "")
|
||||
field_value: int | str | bool
|
||||
if field_type == AddressField.__name__:
|
||||
input_text_value = util.restore_text(input_value)
|
||||
if input_text_value == "new":
|
||||
@@ -736,7 +763,7 @@ class SharableMixin:
|
||||
|
||||
@web.expose
|
||||
@web.require_login("modify Galaxy items")
|
||||
def set_slug_async(self, trans, id, new_slug):
|
||||
def set_slug_async(self, trans: "GalaxyWebTransaction", id, new_slug):
|
||||
item = self.get_item(trans, id)
|
||||
if item:
|
||||
# Only update slug if slug is not already in use.
|
||||
@@ -757,38 +784,60 @@ class SharableMixin:
|
||||
|
||||
@web.expose
|
||||
@web.require_login("share Galaxy items")
|
||||
def share(self, trans, id=None, email="", **kwd):
|
||||
def share(self, trans: "GalaxyWebTransaction", id=None, email="", **kwd):
|
||||
"""Handle sharing an item with a particular user."""
|
||||
raise NotImplementedError()
|
||||
|
||||
@web.expose
|
||||
def display_by_username_and_slug(self, trans, username, slug, **kwargs):
|
||||
def display_by_username_and_slug(self, trans: "GalaxyWebTransaction", username, slug, **kwargs):
|
||||
"""Display item by username and slug."""
|
||||
# Ensure slug is in the correct format.
|
||||
slug = slug.encode("latin1").decode("utf-8")
|
||||
self._display_by_username_and_slug(trans, username, slug, **kwargs)
|
||||
|
||||
def _display_by_username_and_slug(self, trans, username, slug, **kwargs):
|
||||
def _display_by_username_and_slug(self, trans: "GalaxyWebTransaction", username, slug, **kwargs):
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_item(self, trans, id):
|
||||
def get_item(self, trans: "GalaxyWebTransaction", id):
|
||||
"""Return item based on id."""
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class LooksUpTaggedItems(Protocol):
|
||||
"""What the tag helpers need from the object they run on.
|
||||
|
||||
get_object comes from the BaseController subclass UsesTagsMixin is combined
|
||||
with, _get_tagged_item from the mixin itself.
|
||||
"""
|
||||
|
||||
def get_object(
|
||||
self,
|
||||
trans: ProvidesUserContext,
|
||||
id,
|
||||
class_name,
|
||||
check_ownership=False,
|
||||
check_accessible=False,
|
||||
deleted=None,
|
||||
): ...
|
||||
|
||||
def _get_tagged_item(self, trans: ProvidesUserContext, item_class_name, id, check_ownership=True): ...
|
||||
|
||||
|
||||
class UsesTagsMixin(SharableItemSecurityMixin):
|
||||
def _get_user_tags(self, trans, item_class_name, id):
|
||||
def _get_user_tags(self: LooksUpTaggedItems, trans: ProvidesUserContext, item_class_name, id):
|
||||
user = trans.user
|
||||
tagged_item = self._get_tagged_item(trans, item_class_name, id)
|
||||
return [tag for tag in tagged_item.tags if tag.user == user]
|
||||
|
||||
def _get_tagged_item(self, trans, item_class_name, id, check_ownership=True):
|
||||
def _get_tagged_item(
|
||||
self: LooksUpTaggedItems, trans: ProvidesUserContext, item_class_name, id, check_ownership=True
|
||||
):
|
||||
tagged_item = self.get_object(
|
||||
trans, id, item_class_name, check_ownership=check_ownership, check_accessible=True
|
||||
)
|
||||
return tagged_item
|
||||
|
||||
def _remove_items_tag(self, trans, item_class_name, id, tag_name):
|
||||
def _remove_items_tag(self: LooksUpTaggedItems, trans: ProvidesUserContext, item_class_name, id, tag_name):
|
||||
"""Remove a tag from an item."""
|
||||
user = trans.user
|
||||
tagged_item = self._get_tagged_item(trans, item_class_name, id)
|
||||
@@ -796,27 +845,29 @@ class UsesTagsMixin(SharableItemSecurityMixin):
|
||||
trans.sa_session.commit()
|
||||
return deleted
|
||||
|
||||
def _apply_item_tag(self, trans, item_class_name, id, tag_name, tag_value=None):
|
||||
def _apply_item_tag(
|
||||
self: LooksUpTaggedItems, trans: ProvidesUserContext, item_class_name, id, tag_name, tag_value=None
|
||||
):
|
||||
user = trans.user
|
||||
tagged_item = self._get_tagged_item(trans, item_class_name, id)
|
||||
tag_assoc = trans.tag_handler.apply_item_tag(user, tagged_item, tag_name, tag_value)
|
||||
trans.sa_session.commit()
|
||||
return tag_assoc
|
||||
|
||||
def _get_item_tag_assoc(self, trans, item_class_name, id, tag_name):
|
||||
def _get_item_tag_assoc(self: LooksUpTaggedItems, trans: ProvidesUserContext, item_class_name, id, tag_name):
|
||||
user = trans.user
|
||||
tagged_item = self._get_tagged_item(trans, item_class_name, id)
|
||||
log.debug(f"In get_item_tag_assoc with tagged_item {tagged_item}")
|
||||
return trans.tag_handler._get_item_tag_assoc(user, tagged_item, tag_name)
|
||||
|
||||
def set_tags_from_list(self, trans, item, new_tags_list, user=None):
|
||||
def set_tags_from_list(self, trans: ProvidesUserContext, item, new_tags_list, user=None):
|
||||
return trans.tag_handler.set_tags_from_list(user, item, new_tags_list)
|
||||
|
||||
|
||||
class UsesExtendedMetadataMixin(SharableItemSecurityMixin):
|
||||
"""Mixin for getting and setting item extended metadata."""
|
||||
|
||||
def get_item_extended_metadata_obj(self, trans, item):
|
||||
def get_item_extended_metadata_obj(self, trans: ProvidesUserContext, item):
|
||||
"""
|
||||
Given an item object (such as a LibraryDatasetDatasetAssociation), find the object
|
||||
of the associated extended metadata
|
||||
@@ -825,10 +876,10 @@ class UsesExtendedMetadataMixin(SharableItemSecurityMixin):
|
||||
return item.extended_metadata
|
||||
return None
|
||||
|
||||
def set_item_extended_metadata_obj(self, trans, item, extmeta_obj, check_writable=False):
|
||||
def set_item_extended_metadata_obj(self, trans: ProvidesUserContext, item, extmeta_obj, check_writable=False):
|
||||
if item.__class__ == LibraryDatasetDatasetAssociation:
|
||||
if not check_writable or trans.app.security_agent.can_modify_library_item(
|
||||
trans.get_current_user_roles(), item, trans.user
|
||||
trans.get_current_user_roles(), item
|
||||
):
|
||||
item.extended_metadata = extmeta_obj
|
||||
trans.sa_session.commit()
|
||||
@@ -842,10 +893,10 @@ class UsesExtendedMetadataMixin(SharableItemSecurityMixin):
|
||||
item.extended_metadata = extmeta_obj
|
||||
trans.sa_session.commit()
|
||||
|
||||
def unset_item_extended_metadata_obj(self, trans, item, check_writable=False):
|
||||
def unset_item_extended_metadata_obj(self, trans: ProvidesUserContext, item, check_writable=False):
|
||||
if item.__class__ == LibraryDatasetDatasetAssociation:
|
||||
if not check_writable or trans.app.security_agent.can_modify_library_item(
|
||||
trans.get_current_user_roles(), item, trans.user
|
||||
trans.get_current_user_roles(), item
|
||||
):
|
||||
item.extended_metadata = None
|
||||
trans.sa_session.commit()
|
||||
@@ -859,7 +910,7 @@ class UsesExtendedMetadataMixin(SharableItemSecurityMixin):
|
||||
item.extended_metadata = None
|
||||
trans.sa_session.commit()
|
||||
|
||||
def create_extended_metadata(self, trans, extmeta):
|
||||
def create_extended_metadata(self, trans: ProvidesUserContext, extmeta):
|
||||
"""
|
||||
Create/index an extended metadata object. The returned object is
|
||||
not associated with any items
|
||||
@@ -873,7 +924,7 @@ class UsesExtendedMetadataMixin(SharableItemSecurityMixin):
|
||||
trans.sa_session.commit()
|
||||
return ex_meta
|
||||
|
||||
def delete_extended_metadata(self, trans, item):
|
||||
def delete_extended_metadata(self, trans: ProvidesUserContext, item):
|
||||
if item.__class__ == ExtendedMetadata:
|
||||
trans.sa_session.delete(item)
|
||||
trans.sa_session.commit()
|
||||
|
||||
@@ -199,7 +199,7 @@ class WebApplication(base.WebApplication):
|
||||
directories=paths, module_directory=galaxy_app.config.template_cache_path, collection_size=500
|
||||
)
|
||||
|
||||
def handle_controller_exception(self, e, trans, method, kwargs):
|
||||
def handle_controller_exception(self, e, trans: "GalaxyWebTransaction", method, kwargs):
|
||||
if not isinstance(e, HTTPException):
|
||||
# We're still logging too much here but at least it's not logging webob.exc.HTTPFound and friends
|
||||
log.debug(f"Encountered exception in controller method: {method}", exc_info=True)
|
||||
@@ -221,7 +221,7 @@ class WebApplication(base.WebApplication):
|
||||
trans.response.status = e.status_code
|
||||
return trans.show_message(sanitize_html(e.err_msg), e.type)
|
||||
|
||||
def make_body_iterable(self, trans, body):
|
||||
def make_body_iterable(self, trans: "base.DefaultWebTransaction", body):
|
||||
return base.WebApplication.make_body_iterable(self, trans, body)
|
||||
|
||||
def transaction_chooser(self, environ, galaxy_app: BasicSharedApp, session_cookie: str):
|
||||
@@ -1128,7 +1128,7 @@ class GalaxyWebTransaction(base.DefaultWebTransaction, context.ProvidesHistoryCo
|
||||
return url_for(path, qualified=True)
|
||||
|
||||
|
||||
def create_new_session(trans, prev_galaxy_session=None, user_for_new_session=None):
|
||||
def create_new_session(trans: "GalaxyWebTransaction", prev_galaxy_session=None, user_for_new_session=None):
|
||||
"""
|
||||
Create a new GalaxySession for this request, possibly with a connection
|
||||
to a previous session (in `prev_galaxy_session`) and an existing user
|
||||
|
||||
@@ -30,6 +30,7 @@ from galaxy.webapps.galaxy.api import (
|
||||
DependsOnUser,
|
||||
Router,
|
||||
)
|
||||
from galaxy.work.context import SessionRequestContext
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -90,7 +91,7 @@ class AgentAPI:
|
||||
async def query_agent(
|
||||
self,
|
||||
request: AgentQueryRequest,
|
||||
trans: ProvidesUserContext = DependsOnTrans,
|
||||
trans: SessionRequestContext = DependsOnTrans,
|
||||
user: User = DependsOnUser,
|
||||
) -> AgentQueryResponse:
|
||||
"""Query an AI agent. Use agent_type='auto' for automatic routing.
|
||||
@@ -129,7 +130,7 @@ class AgentAPI:
|
||||
job_id: DecodedDatabaseIdField | None = Body(None, description="Job ID for context"),
|
||||
error_details: dict[str, Any] | None = Body(None, description="Additional error details"),
|
||||
save_exchange: bool | None = Body(None, description="Save exchange for feedback tracking. Defaults to false."),
|
||||
trans: ProvidesUserContext = DependsOnTrans,
|
||||
trans: SessionRequestContext = DependsOnTrans,
|
||||
user: User = DependsOnUser,
|
||||
) -> AgentResponse:
|
||||
"""Analyze job errors and provide debugging assistance.
|
||||
@@ -181,7 +182,7 @@ class AgentAPI:
|
||||
query: str = Body(..., description="Description of the tool to create"),
|
||||
context: dict[str, Any] | None = Body(None, description="Additional context for tool creation"),
|
||||
save_exchange: bool | None = Body(None, description="Save exchange for feedback tracking. Defaults to false."),
|
||||
trans: ProvidesUserContext = DependsOnTrans,
|
||||
trans: SessionRequestContext = DependsOnTrans,
|
||||
user: User = DependsOnUser,
|
||||
) -> AgentResponse:
|
||||
"""Create a custom Galaxy tool.
|
||||
@@ -216,7 +217,7 @@ class AgentAPI:
|
||||
async def history_summary(
|
||||
self,
|
||||
history_id: str = Body(..., embed=True, description="Encoded id of the history to summarize."),
|
||||
trans: ProvidesUserContext = DependsOnTrans,
|
||||
trans: SessionRequestContext = DependsOnTrans,
|
||||
user: User = DependsOnUser,
|
||||
) -> AgentResponse:
|
||||
"""Produce a comprehensive markdown report for a history's analysis.
|
||||
|
||||
@@ -27,7 +27,9 @@ from galaxy.exceptions import (
|
||||
)
|
||||
from galaxy.managers.agents import AgentService
|
||||
from galaxy.managers.chat import ChatManager
|
||||
from galaxy.managers.context import ProvidesUserContext
|
||||
from galaxy.managers.context import (
|
||||
ProvidesUserContext,
|
||||
)
|
||||
from galaxy.managers.jobs import JobManager
|
||||
from galaxy.managers.markdown_util import ready_galaxy_markdown_for_export
|
||||
from galaxy.managers.workflows import WorkflowsManager
|
||||
@@ -50,6 +52,7 @@ from galaxy.webapps.galaxy.api import (
|
||||
DependsOnUser,
|
||||
Router,
|
||||
)
|
||||
from galaxy.work.context import SessionRequestContext
|
||||
|
||||
# Import agent system
|
||||
try:
|
||||
@@ -129,7 +132,7 @@ class ChatAPI:
|
||||
payload: ChatPayload | None = None,
|
||||
query: str | None = Query(default=None, description="Query string for general chat"),
|
||||
agent_type: str = Query(default="auto", description="Agent type to use for the query"),
|
||||
trans: ProvidesUserContext = DependsOnTrans,
|
||||
trans: SessionRequestContext = DependsOnTrans,
|
||||
user: User = DependsOnUser,
|
||||
) -> ChatResponse:
|
||||
"""GalaxyAI endpoint - handles both job-based and general chat queries
|
||||
@@ -409,7 +412,7 @@ class ChatAPI:
|
||||
workflow_id: str = Path(..., description="Workflow ID to generate the report for"),
|
||||
version: int | None = Query(None, description="Version of the workflow"),
|
||||
instance: bool = Query(False, description="Whether the workflow_id is an instance ID"),
|
||||
trans: ProvidesUserContext = DependsOnTrans,
|
||||
trans: SessionRequestContext = DependsOnTrans,
|
||||
user: User = DependsOnUser,
|
||||
) -> WorkflowReportResponse:
|
||||
"""Generate a report for the specified workflow."""
|
||||
@@ -563,7 +566,7 @@ class ChatAPI:
|
||||
self,
|
||||
query: str,
|
||||
agent_type: str,
|
||||
trans: ProvidesUserContext,
|
||||
trans: SessionRequestContext,
|
||||
user: User,
|
||||
job=None,
|
||||
context: dict[str, Any] | None = None,
|
||||
@@ -576,7 +579,7 @@ class ChatAPI:
|
||||
self,
|
||||
query: str,
|
||||
agent_type: str,
|
||||
trans: ProvidesUserContext,
|
||||
trans: SessionRequestContext,
|
||||
user: User,
|
||||
job=None,
|
||||
context: dict[str, Any] | None = None,
|
||||
|
||||
@@ -11,6 +11,7 @@ from galaxy.web import (
|
||||
expose_api,
|
||||
require_admin,
|
||||
)
|
||||
from galaxy.webapps.base.webapp import GalaxyWebTransaction
|
||||
from . import BaseGalaxyAPIController
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@@ -23,7 +24,7 @@ class ContainerResolutionAPIController(BaseGalaxyAPIController):
|
||||
|
||||
@expose_api
|
||||
@require_admin
|
||||
def index(self, trans, **kwd):
|
||||
def index(self, trans: GalaxyWebTransaction, **kwd):
|
||||
"""
|
||||
GET /api/container_resolvers
|
||||
"""
|
||||
@@ -31,7 +32,7 @@ class ContainerResolutionAPIController(BaseGalaxyAPIController):
|
||||
|
||||
@expose_api
|
||||
@require_admin
|
||||
def show(self, trans, index):
|
||||
def show(self, trans: GalaxyWebTransaction, index):
|
||||
"""
|
||||
GET /api/container_resolvers/<id>
|
||||
"""
|
||||
@@ -39,7 +40,7 @@ class ContainerResolutionAPIController(BaseGalaxyAPIController):
|
||||
|
||||
@expose_api
|
||||
@require_admin
|
||||
def resolve(self, trans, index=None, **kwds):
|
||||
def resolve(self, trans: GalaxyWebTransaction, index=None, **kwds):
|
||||
"""
|
||||
GET /api/container_resolvers/resolve
|
||||
GET /api/container_resolvers/{index}/resolve
|
||||
@@ -69,7 +70,7 @@ class ContainerResolutionAPIController(BaseGalaxyAPIController):
|
||||
|
||||
@expose_api
|
||||
@require_admin
|
||||
def resolve_toolbox(self, trans, **kwds):
|
||||
def resolve_toolbox(self, trans: GalaxyWebTransaction, **kwds):
|
||||
"""
|
||||
GET /api/container_resolvers/toolbox
|
||||
GET /api/container_resolvers/{index}/toolbox
|
||||
@@ -89,7 +90,7 @@ class ContainerResolutionAPIController(BaseGalaxyAPIController):
|
||||
|
||||
@expose_api
|
||||
@require_admin
|
||||
def resolve_toolbox_with_install(self, trans, payload, **kwds):
|
||||
def resolve_toolbox_with_install(self, trans: GalaxyWebTransaction, payload, **kwds):
|
||||
"""
|
||||
POST /api/container_resolvers/toolbox/install
|
||||
POST /api/container_resolvers/{index}/toolbox/install
|
||||
@@ -108,7 +109,7 @@ class ContainerResolutionAPIController(BaseGalaxyAPIController):
|
||||
|
||||
@expose_api
|
||||
@require_admin
|
||||
def resolve_with_install(self, trans, payload, **kwds):
|
||||
def resolve_with_install(self, trans: GalaxyWebTransaction, payload, **kwds):
|
||||
"""
|
||||
POST /api/container_resolvers/resolve/install
|
||||
POST /api/container_resolvers/{index}/resolve/install
|
||||
|
||||
@@ -18,6 +18,7 @@ from galaxy.webapps.base.controller import (
|
||||
UsesLibraryMixinItems,
|
||||
UsesStoredWorkflowMixin,
|
||||
)
|
||||
from galaxy.webapps.base.webapp import GalaxyWebTransaction
|
||||
from . import (
|
||||
BaseGalaxyAPIController,
|
||||
depends,
|
||||
@@ -33,10 +34,10 @@ class BaseExtendedMetadataController(
|
||||
):
|
||||
exmeta_item_id: str
|
||||
|
||||
def _get_item_from_id(self, trans, idstr, check_writable=True) -> T | None: ...
|
||||
def _get_item_from_id(self, trans: GalaxyWebTransaction, idstr, check_writable=True) -> T | None: ...
|
||||
|
||||
@web.expose_api
|
||||
def index(self, trans, **kwd):
|
||||
def index(self, trans: GalaxyWebTransaction, **kwd):
|
||||
idnum = kwd[self.exmeta_item_id]
|
||||
item = self._get_item_from_id(trans, idnum, check_writable=False)
|
||||
if item is not None:
|
||||
@@ -45,7 +46,7 @@ class BaseExtendedMetadataController(
|
||||
return ex_meta.data
|
||||
|
||||
@web.expose_api
|
||||
def create(self, trans, payload, **kwd):
|
||||
def create(self, trans: GalaxyWebTransaction, payload, **kwd):
|
||||
idnum = kwd[self.exmeta_item_id]
|
||||
item = self._get_item_from_id(trans, idnum, check_writable=True)
|
||||
if item is not None:
|
||||
@@ -61,7 +62,9 @@ class LibraryDatasetExtendMetadataController(BaseExtendedMetadataController[mode
|
||||
controller_name = "library_dataset_extended_metadata"
|
||||
exmeta_item_id = "library_content_id"
|
||||
|
||||
def _get_item_from_id(self, trans, idstr, check_writable=True) -> model.LibraryDatasetDatasetAssociation | None:
|
||||
def _get_item_from_id(
|
||||
self, trans: GalaxyWebTransaction, idstr, check_writable=True
|
||||
) -> model.LibraryDatasetDatasetAssociation | None:
|
||||
if check_writable:
|
||||
item = self.get_library_dataset_dataset_association(trans, idstr)
|
||||
if trans.app.security_agent.can_modify_library_item(trans.get_current_user_roles(), item):
|
||||
@@ -78,7 +81,9 @@ class HistoryDatasetExtendMetadataController(BaseExtendedMetadataController[mode
|
||||
exmeta_item_id = "history_content_id"
|
||||
hda_manager: managers.hdas.HDAManager = depends(managers.hdas.HDAManager)
|
||||
|
||||
def _get_item_from_id(self, trans, idstr, check_writable=True) -> model.HistoryDatasetAssociation | None:
|
||||
def _get_item_from_id(
|
||||
self, trans: GalaxyWebTransaction, idstr, check_writable=True
|
||||
) -> model.HistoryDatasetAssociation | None:
|
||||
decoded_idstr = self.decode_id(idstr)
|
||||
if check_writable:
|
||||
return self.hda_manager.get_owned(decoded_idstr, trans.user, current_history=trans.history)
|
||||
|
||||
@@ -23,6 +23,7 @@ from galaxy.webapps.galaxy.api import (
|
||||
)
|
||||
from galaxy.webapps.galaxy.api.common import FolderIdPathParam
|
||||
from galaxy.webapps.galaxy.services.library_folder_contents import LibraryFolderContentsService
|
||||
from galaxy.work.context import SessionRequestContext
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -117,7 +118,7 @@ class FastAPILibraryFoldersContents:
|
||||
def create(
|
||||
self,
|
||||
folder_id: FolderIdPathParam,
|
||||
trans: ProvidesUserContext = DependsOnTrans,
|
||||
trans: SessionRequestContext = DependsOnTrans,
|
||||
payload: CreateLibraryFilePayload = Body(...),
|
||||
):
|
||||
return self.service.create(trans, folder_id, payload)
|
||||
|
||||
@@ -16,6 +16,7 @@ from galaxy.model import FormDefinition
|
||||
from galaxy.schema.fields import DecodedDatabaseIdField
|
||||
from galaxy.util import XML
|
||||
from galaxy.webapps.base.controller import url_for
|
||||
from galaxy.webapps.base.webapp import GalaxyWebTransaction
|
||||
from galaxy.webapps.galaxy.api import (
|
||||
depends,
|
||||
DependsOnTrans,
|
||||
@@ -50,7 +51,7 @@ class FastAPIForms:
|
||||
|
||||
class FormDefinitionAPIController(BaseGalaxyAPIController):
|
||||
@web.legacy_expose_api
|
||||
def index(self, trans, **kwd):
|
||||
def index(self, trans: GalaxyWebTransaction, **kwd):
|
||||
"""
|
||||
GET /api/forms
|
||||
Displays a collection (list) of forms.
|
||||
@@ -70,7 +71,7 @@ class FormDefinitionAPIController(BaseGalaxyAPIController):
|
||||
return rval
|
||||
|
||||
@web.legacy_expose_api
|
||||
def show(self, trans, id, **kwd):
|
||||
def show(self, trans: GalaxyWebTransaction, id, **kwd):
|
||||
"""
|
||||
GET /api/forms/{encoded_form_id}
|
||||
Displays information about a form.
|
||||
@@ -96,7 +97,7 @@ class FormDefinitionAPIController(BaseGalaxyAPIController):
|
||||
return item
|
||||
|
||||
@web.legacy_expose_api
|
||||
def create(self, trans, payload, **kwd):
|
||||
def create(self, trans: GalaxyWebTransaction, payload, **kwd):
|
||||
"""
|
||||
POST /api/forms
|
||||
Creates a new form.
|
||||
|
||||
@@ -10,6 +10,7 @@ from fastapi.responses import Response
|
||||
|
||||
from galaxy.managers.context import ProvidesUserContext
|
||||
from galaxy.managers.genomes import GenomesManager
|
||||
from galaxy.work.context import SessionRequestContext
|
||||
from . import (
|
||||
depends,
|
||||
DependsOnTrans,
|
||||
@@ -82,7 +83,7 @@ class FastAPIGenomes:
|
||||
)
|
||||
def show(
|
||||
self,
|
||||
trans: ProvidesUserContext = DependsOnTrans,
|
||||
trans: SessionRequestContext = DependsOnTrans,
|
||||
id: str = IdPathParam,
|
||||
reference: bool = ReferenceQueryParam,
|
||||
num: int = NumQueryParam,
|
||||
|
||||
@@ -4,7 +4,6 @@ API operations on Group objects.
|
||||
|
||||
import logging
|
||||
|
||||
from galaxy.managers.context import ProvidesAppContext
|
||||
from galaxy.managers.group_roles import GroupRolesManager
|
||||
from galaxy.model.db.role import get_private_role_user_emails_dict
|
||||
from galaxy.schema.fields import Security
|
||||
@@ -21,13 +20,16 @@ from galaxy.webapps.galaxy.api.common import (
|
||||
GroupIDPathParam,
|
||||
RoleIDPathParam,
|
||||
)
|
||||
from galaxy.work.context import SessionRequestContext
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
router = Router(tags=["group_roles"])
|
||||
|
||||
|
||||
def group_role_to_model(trans, group_id: int, role, displayed_name: str | None = None) -> GroupRoleResponse:
|
||||
def group_role_to_model(
|
||||
trans: SessionRequestContext, group_id: int, role, displayed_name: str | None = None
|
||||
) -> GroupRoleResponse:
|
||||
encoded_group_id = Security.security.encode_id(group_id)
|
||||
encoded_role_id = Security.security.encode_id(role.id)
|
||||
url = trans.url_builder("group_role", group_id=encoded_group_id, role_id=encoded_role_id)
|
||||
@@ -48,7 +50,7 @@ class FastAPIGroupRoles:
|
||||
def index(
|
||||
self,
|
||||
group_id: GroupIDPathParam,
|
||||
trans: ProvidesAppContext = DependsOnTrans,
|
||||
trans: SessionRequestContext = DependsOnTrans,
|
||||
) -> GroupRoleListResponse:
|
||||
group_roles = self.manager.index(trans, group_id)
|
||||
role_ids = {gr.role.id for gr in group_roles}
|
||||
@@ -70,7 +72,7 @@ class FastAPIGroupRoles:
|
||||
self,
|
||||
group_id: GroupIDPathParam,
|
||||
role_id: RoleIDPathParam,
|
||||
trans: ProvidesAppContext = DependsOnTrans,
|
||||
trans: SessionRequestContext = DependsOnTrans,
|
||||
) -> GroupRoleResponse:
|
||||
role = self.manager.show(trans, role_id, group_id)
|
||||
return group_role_to_model(trans, group_id, role)
|
||||
@@ -80,7 +82,7 @@ class FastAPIGroupRoles:
|
||||
self,
|
||||
group_id: GroupIDPathParam,
|
||||
role_id: RoleIDPathParam,
|
||||
trans: ProvidesAppContext = DependsOnTrans,
|
||||
trans: SessionRequestContext = DependsOnTrans,
|
||||
) -> GroupRoleResponse:
|
||||
role = self.manager.update(trans, role_id, group_id)
|
||||
return group_role_to_model(trans, group_id, role)
|
||||
@@ -90,7 +92,7 @@ class FastAPIGroupRoles:
|
||||
self,
|
||||
group_id: GroupIDPathParam,
|
||||
role_id: RoleIDPathParam,
|
||||
trans: ProvidesAppContext = DependsOnTrans,
|
||||
trans: SessionRequestContext = DependsOnTrans,
|
||||
) -> GroupRoleResponse:
|
||||
role = self.manager.delete(trans, role_id, group_id)
|
||||
return group_role_to_model(trans, group_id, role)
|
||||
|
||||
@@ -4,7 +4,6 @@ API operations on Group objects.
|
||||
|
||||
import logging
|
||||
|
||||
from galaxy.managers.context import ProvidesAppContext
|
||||
from galaxy.managers.group_users import GroupUsersManager
|
||||
from galaxy.schema.fields import Security
|
||||
from galaxy.schema.schema import (
|
||||
@@ -20,13 +19,14 @@ from galaxy.webapps.galaxy.api.common import (
|
||||
GroupIDPathParam,
|
||||
UserIdPathParam,
|
||||
)
|
||||
from galaxy.work.context import SessionRequestContext
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
router = Router(tags=["group_users"])
|
||||
|
||||
|
||||
def group_user_to_model(trans, group_id, user) -> GroupUserResponse:
|
||||
def group_user_to_model(trans: SessionRequestContext, group_id, user) -> GroupUserResponse:
|
||||
encoded_group_id = Security.security.encode_id(group_id)
|
||||
encoded_user_id = Security.security.encode_id(user.id)
|
||||
url = trans.url_builder("group_user", group_id=encoded_group_id, user_id=encoded_user_id)
|
||||
@@ -46,7 +46,7 @@ class FastAPIGroupUsers:
|
||||
def index(
|
||||
self,
|
||||
group_id: GroupIDPathParam,
|
||||
trans: ProvidesAppContext = DependsOnTrans,
|
||||
trans: SessionRequestContext = DependsOnTrans,
|
||||
) -> GroupUserListResponse:
|
||||
"""
|
||||
GET /api/groups/{encoded_group_id}/users
|
||||
@@ -66,7 +66,7 @@ class FastAPIGroupUsers:
|
||||
self,
|
||||
group_id: GroupIDPathParam,
|
||||
user_id: UserIdPathParam,
|
||||
trans: ProvidesAppContext = DependsOnTrans,
|
||||
trans: SessionRequestContext = DependsOnTrans,
|
||||
) -> GroupUserResponse:
|
||||
"""
|
||||
Displays information about a group user.
|
||||
@@ -84,7 +84,7 @@ class FastAPIGroupUsers:
|
||||
self,
|
||||
group_id: GroupIDPathParam,
|
||||
user_id: UserIdPathParam,
|
||||
trans: ProvidesAppContext = DependsOnTrans,
|
||||
trans: SessionRequestContext = DependsOnTrans,
|
||||
) -> GroupUserResponse:
|
||||
"""
|
||||
PUT /api/groups/{encoded_group_id}/users/{encoded_user_id}
|
||||
@@ -103,7 +103,7 @@ class FastAPIGroupUsers:
|
||||
self,
|
||||
group_id: GroupIDPathParam,
|
||||
user_id: UserIdPathParam,
|
||||
trans: ProvidesAppContext = DependsOnTrans,
|
||||
trans: SessionRequestContext = DependsOnTrans,
|
||||
) -> GroupUserResponse:
|
||||
"""
|
||||
DELETE /api/groups/{encoded_group_id}/users/{encoded_user_id}
|
||||
|
||||
@@ -7,7 +7,6 @@ from typing import Annotated
|
||||
|
||||
from fastapi import Body
|
||||
|
||||
from galaxy.managers.context import ProvidesAppContext
|
||||
from galaxy.managers.groups import GroupsManager
|
||||
from galaxy.schema.groups import (
|
||||
GroupCreatePayload,
|
||||
@@ -21,6 +20,7 @@ from galaxy.webapps.galaxy.api import (
|
||||
Router,
|
||||
)
|
||||
from galaxy.webapps.galaxy.api.common import GroupIDPathParam
|
||||
from galaxy.work.context import SessionRequestContext
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -39,7 +39,7 @@ class FastAPIGroups:
|
||||
)
|
||||
def index(
|
||||
self,
|
||||
trans: ProvidesAppContext = DependsOnTrans,
|
||||
trans: SessionRequestContext = DependsOnTrans,
|
||||
) -> GroupListResponse:
|
||||
return self.manager.index(trans)
|
||||
|
||||
@@ -52,7 +52,7 @@ class FastAPIGroups:
|
||||
def create(
|
||||
self,
|
||||
payload: Annotated[GroupCreatePayload, Body(...)],
|
||||
trans: ProvidesAppContext = DependsOnTrans,
|
||||
trans: SessionRequestContext = DependsOnTrans,
|
||||
) -> GroupListResponse:
|
||||
return self.manager.create(trans, payload)
|
||||
|
||||
@@ -65,7 +65,7 @@ class FastAPIGroups:
|
||||
def show(
|
||||
self,
|
||||
group_id: GroupIDPathParam,
|
||||
trans: ProvidesAppContext = DependsOnTrans,
|
||||
trans: SessionRequestContext = DependsOnTrans,
|
||||
) -> GroupResponse:
|
||||
return self.manager.show(trans, group_id)
|
||||
|
||||
@@ -79,18 +79,18 @@ class FastAPIGroups:
|
||||
self,
|
||||
group_id: GroupIDPathParam,
|
||||
payload: Annotated[GroupUpdatePayload, Body(...)],
|
||||
trans: ProvidesAppContext = DependsOnTrans,
|
||||
trans: SessionRequestContext = DependsOnTrans,
|
||||
) -> GroupResponse:
|
||||
return self.manager.update(trans, group_id, payload)
|
||||
|
||||
@router.delete("/api/groups/{group_id}", require_admin=True)
|
||||
def delete(self, group_id: GroupIDPathParam, trans: ProvidesAppContext = DependsOnTrans):
|
||||
def delete(self, group_id: GroupIDPathParam, trans: SessionRequestContext = DependsOnTrans):
|
||||
self.manager.delete(trans, group_id)
|
||||
|
||||
@router.post("/api/groups/{group_id}/purge", require_admin=True)
|
||||
def purge(self, group_id: GroupIDPathParam, trans: ProvidesAppContext = DependsOnTrans):
|
||||
def purge(self, group_id: GroupIDPathParam, trans: SessionRequestContext = DependsOnTrans):
|
||||
self.manager.purge(trans, group_id)
|
||||
|
||||
@router.post("/api/groups/{group_id}/undelete", require_admin=True)
|
||||
def undelete(self, group_id: GroupIDPathParam, trans: ProvidesAppContext = DependsOnTrans):
|
||||
def undelete(self, group_id: GroupIDPathParam, trans: SessionRequestContext = DependsOnTrans):
|
||||
self.manager.undelete(trans, group_id)
|
||||
|
||||
@@ -97,6 +97,7 @@ from galaxy.webapps.galaxy.api.common import (
|
||||
)
|
||||
from galaxy.webapps.galaxy.services.histories import HistoriesService
|
||||
from galaxy.webapps.galaxy.services.workflows import WorkflowsService
|
||||
from galaxy.work.context import SessionRequestContext
|
||||
from .common import HistoryIDPathParam
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@@ -215,7 +216,7 @@ class FastAPIHistories:
|
||||
def index(
|
||||
self,
|
||||
response: Response,
|
||||
trans: ProvidesHistoryContext = DependsOnTrans,
|
||||
trans: SessionRequestContext = DependsOnTrans,
|
||||
limit: int | None = LimitQueryParam,
|
||||
offset: int | None = OffsetQueryParam,
|
||||
show_own: bool = ShowOwnQueryParam,
|
||||
@@ -263,7 +264,7 @@ class FastAPIHistories:
|
||||
)
|
||||
def count(
|
||||
self,
|
||||
trans: ProvidesHistoryContext = DependsOnTrans,
|
||||
trans: SessionRequestContext = DependsOnTrans,
|
||||
) -> int:
|
||||
return self.service.count(trans)
|
||||
|
||||
@@ -274,7 +275,7 @@ class FastAPIHistories:
|
||||
)
|
||||
def index_deleted(
|
||||
self,
|
||||
trans: ProvidesHistoryContext = DependsOnTrans,
|
||||
trans: SessionRequestContext = DependsOnTrans,
|
||||
filter_query_params: FilterQueryParams = Depends(get_filter_query_params),
|
||||
serialization_params: SerializationParams = Depends(query_serialization_params),
|
||||
all: bool | None = AllHistoriesQueryParam,
|
||||
@@ -476,7 +477,7 @@ class FastAPIHistories:
|
||||
)
|
||||
def create(
|
||||
self,
|
||||
trans: ProvidesHistoryContext = DependsOnTrans,
|
||||
trans: SessionRequestContext = DependsOnTrans,
|
||||
payload: CreateHistoryPayload = Depends(CreateHistoryFormData.as_form), # type: ignore[attr-defined]
|
||||
payload_as_json: Any | None = Depends(try_get_request_body_as_json),
|
||||
serialization_params: SerializationParams = Depends(query_serialization_params),
|
||||
|
||||
@@ -1222,6 +1222,6 @@ class FastAPIHistoryContents:
|
||||
rval = self.service.materialize(trans, materialize_request)
|
||||
return rval
|
||||
|
||||
def _download_collection(self, trans, id):
|
||||
def _download_collection(self, trans: ProvidesHistoryContext, id):
|
||||
archive = self.service.get_dataset_collection_archive_for_download(trans, id)
|
||||
return GalaxyStreamingResponse(archive.response(), headers=archive.get_headers())
|
||||
|
||||
@@ -153,7 +153,7 @@ class JobFilesAPIController(BaseGalaxyAPIController):
|
||||
return {"message": "ok"}
|
||||
|
||||
@expose_api_anonymous_and_sessionless
|
||||
def tus_patch(self, trans, **kwds):
|
||||
def tus_patch(self, trans: ProvidesAppContext, **kwds):
|
||||
"""
|
||||
Exposed as PATCH /api/job_files/resumable_upload.
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ related to running and queued jobs.
|
||||
from galaxy.job_execution.ports import JobPortsView
|
||||
from galaxy.structured_app import StructuredApp
|
||||
from galaxy.web import expose_api_anonymous_and_sessionless
|
||||
from galaxy.webapps.base.webapp import GalaxyWebTransaction
|
||||
from . import BaseGalaxyAPIController
|
||||
|
||||
|
||||
@@ -22,7 +23,7 @@ class JobPortsAPIController(BaseGalaxyAPIController):
|
||||
self._job_ports_view = JobPortsView(app)
|
||||
|
||||
@expose_api_anonymous_and_sessionless
|
||||
def create(self, trans, job_id, payload, **kwargs):
|
||||
def create(self, trans: GalaxyWebTransaction, job_id, payload, **kwargs):
|
||||
"""
|
||||
create( self, trans, job_id, payload, **kwargs )
|
||||
* POST /api/jobs/{job_id}/ports
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user