diff --git a/lib/galaxy/config/schemas/tool_shed_config_schema.yml b/lib/galaxy/config/schemas/tool_shed_config_schema.yml index 2a1eee2b533f..b7b6bfee049e 100644 --- a/lib/galaxy/config/schemas/tool_shed_config_schema.yml +++ b/lib/galaxy/config/schemas/tool_shed_config_schema.yml @@ -102,12 +102,12 @@ mapping: the repositories and tools within the Tool Shed given that you specify the following two config options. - tool_state_cache_dir: + model_cache_dir: type: str - default: database/tool_state_cache + default: database/model_cache required: false desc: | - Cache directory for tool state. + Cache directory for Pydantic model objects. repo_name_boost: type: float diff --git a/lib/tool_shed/managers/tool_state_cache.py b/lib/tool_shed/managers/model_cache.py similarity index 82% rename from lib/tool_shed/managers/tool_state_cache.py rename to lib/tool_shed/managers/model_cache.py index 010ab288a334..94d23d61a0e0 100644 --- a/lib/tool_shed/managers/tool_state_cache.py +++ b/lib/tool_shed/managers/model_cache.py @@ -3,13 +3,28 @@ from typing import ( Any, Dict, + Generic, Optional, + Type, + TypeVar, ) +from pydantic import BaseModel + +from galaxy.util.hash_util import md5_hash_str + + RAW_CACHED_JSON = Dict[str, Any] -class ToolStateCache: +def hash_model(model_class: Type[BaseModel]) -> str: + return md5_hash_str(json.dumps(model_class.model_json_schema())) + + +M = TypeVar("M", bound=BaseModel) + + +class ModelCache(Generic[M]): _cache_directory: str def __init__(self, cache_directory: str): diff --git a/lib/tool_shed/managers/tools.py b/lib/tool_shed/managers/tools.py index 2e151e0aa73c..a8b873afd6ca 100644 --- a/lib/tool_shed/managers/tools.py +++ b/lib/tool_shed/managers/tools.py @@ -8,6 +8,8 @@ Tuple, ) +from pydantic import BaseModel + from galaxy import exceptions from galaxy.exceptions import ( InternalServerError, @@ -148,12 +150,12 @@ def get_repository_metadata_tool_dict( def tool_input_models_cached_for( trans: ProvidesRepositoriesContext, trs_tool_id: str, tool_version: str, repository_clone_url: Optional[str] = None ) -> ToolParameterBundleModel: - tool_state_cache = trans.app.tool_state_cache - raw_json = tool_state_cache.get_cache_entry_for(trs_tool_id, tool_version) + model_cache = trans.app.model_cache + raw_json = model_cache.get_cache_entry_for(trs_tool_id, tool_version) if raw_json is not None: return tool_parameter_bundle_from_json(raw_json) bundle = tool_input_models_for(trans, trs_tool_id, tool_version, repository_clone_url=repository_clone_url) - tool_state_cache.insert_cache_entry_for(trs_tool_id, tool_version, bundle.dict()) + model_cache.insert_cache_entry_for(trs_tool_id, tool_version, bundle.dict()) return bundle diff --git a/lib/tool_shed/structured_app.py b/lib/tool_shed/structured_app.py index c3eee0c94299..8fd828f9f5e1 100644 --- a/lib/tool_shed/structured_app.py +++ b/lib/tool_shed/structured_app.py @@ -3,7 +3,7 @@ from galaxy.structured_app import BasicSharedApp if TYPE_CHECKING: - from tool_shed.managers.tool_state_cache import ToolStateCache + from tool_shed.managers.model_cache import ModelCache from tool_shed.repository_registry import Registry as RepositoryRegistry from tool_shed.repository_types.registry import Registry as RepositoryTypesRegistry from tool_shed.util.hgweb_config import HgWebConfigManager @@ -17,4 +17,4 @@ class ToolShedApp(BasicSharedApp): repository_registry: "RepositoryRegistry" hgweb_config_manager: "HgWebConfigManager" security_agent: "CommunityRBACAgent" - tool_state_cache: "ToolStateCache" + model_cache: "ModelCache" diff --git a/lib/tool_shed/webapp/app.py b/lib/tool_shed/webapp/app.py index 4083674241a4..e046301497ad 100644 --- a/lib/tool_shed/webapp/app.py +++ b/lib/tool_shed/webapp/app.py @@ -33,7 +33,7 @@ from galaxy.structured_app import BasicSharedApp from galaxy.web_stack import application_stack_instance from tool_shed.grids.repository_grid_filter_manager import RepositoryGridFilterManager -from tool_shed.managers.tool_state_cache import ToolStateCache +from tool_shed.managers.model_cache import ModelCache from tool_shed.structured_app import ToolShedApp from tool_shed.util.hgweb_config import hgweb_config_manager from tool_shed.webapp.model.migrations import verify_database @@ -84,7 +84,7 @@ def __init__(self, **kwd) -> None: self._register_singleton(SharedModelMapping, model) self._register_singleton(mapping.ToolShedModelMapping, model) self._register_singleton(scoped_session, self.model.context) - self.tool_state_cache = ToolStateCache(self.config.tool_state_cache_dir) + self.model_cache = ModelCache(self.config.model_cache_dir) self.user_manager = self._register_singleton(UserManager, UserManager(self, app_type="tool_shed")) self.api_keys_manager = self._register_singleton(ApiKeyManager) # initialize the Tool Shed tag handler. diff --git a/test/unit/tool_shed/_util.py b/test/unit/tool_shed/_util.py index 3002c6a82fab..755731b018ad 100644 --- a/test/unit/tool_shed/_util.py +++ b/test/unit/tool_shed/_util.py @@ -18,7 +18,7 @@ from galaxy.util import safe_makedirs from tool_shed.context import ProvidesRepositoriesContext from tool_shed.managers.repositories import upload_tar_and_set_metadata -from tool_shed.managers.tool_state_cache import ToolStateCache +from tool_shed.managers.model_cache import ModelCache from tool_shed.managers.users import create_user from tool_shed.repository_types import util as rt_util from tool_shed.repository_types.registry import Registry as RepositoryTypesRegistry @@ -81,7 +81,7 @@ def __init__(self, temp_directory=None): self.config = TestToolShedConfig(temp_directory) self.security = IdEncodingHelper(id_secret=self.config.id_secret) self.repository_registry = tool_shed.repository_registry.Registry(self) - self.tool_state_cache = ToolStateCache(os.path.join(temp_directory, "tool_state_cache")) + self.model_cache = ModelCache(os.path.join(temp_directory, "model_cache")) @property def security_agent(self): diff --git a/test/unit/tool_shed/test_model_cache.py b/test/unit/tool_shed/test_model_cache.py new file mode 100644 index 000000000000..3be2f9cef9f1 --- /dev/null +++ b/test/unit/tool_shed/test_model_cache.py @@ -0,0 +1,36 @@ +from pydantic import BaseModel, ConfigDict + +from tool_shed.managers.model_cache import hash_model + + +class Moo(BaseModel): + foo: int + + +class MooLike(BaseModel): + model_config = ConfigDict(title="Moo") + foo: int + + +class NewMoo(BaseModel): + model_config = ConfigDict(title="Moo") + foo: int + new_prop: str + + +def test_hash(): + hash_moo_1 = hash_model(Moo) + hash_moo_2 = hash_model(Moo) + assert hash_moo_1 == hash_moo_2 + + +def test_hash_by_value(): + hash_moo_1 = hash_model(Moo) + hash_moo_like = hash_model(MooLike) + assert hash_moo_1 == hash_moo_like + + +def test_hash_different_on_updates(): + hash_moo_1 = hash_model(Moo) + hash_moo_new = hash_model(NewMoo) + assert hash_moo_1 != hash_moo_new