Skip to content

Commit 805711c

Browse files
committed
Improved tool API.
1 parent e4aff2c commit 805711c

File tree

11 files changed

+345
-113
lines changed

11 files changed

+345
-113
lines changed

lib/galaxy/config/schemas/tool_shed_config_schema.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,12 +102,12 @@ mapping:
102102
the repositories and tools within the Tool Shed given that you specify
103103
the following two config options.
104104
105-
tool_state_cache_dir:
105+
model_cache_dir:
106106
type: str
107-
default: database/tool_state_cache
107+
default: database/model_cache
108108
required: false
109109
desc: |
110-
Cache directory for tool state.
110+
Cache directory for Pydantic model objects.
111111
112112
repo_name_boost:
113113
type: float
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import json
2+
import os
3+
from typing import (
4+
Any,
5+
Dict,
6+
Optional,
7+
Type,
8+
TypeVar,
9+
)
10+
11+
from pydantic import BaseModel
12+
13+
from galaxy.util.hash_util import md5_hash_str
14+
15+
RAW_CACHED_JSON = Dict[str, Any]
16+
17+
18+
def hash_model(model_class: Type[BaseModel]) -> str:
19+
return md5_hash_str(json.dumps(model_class.model_json_schema()))
20+
21+
22+
MODEL_HASHES: Dict[Type[BaseModel], str] = {}
23+
24+
25+
M = TypeVar("M", bound=BaseModel)
26+
27+
28+
def ensure_model_has_hash(model_class: Type[BaseModel]) -> None:
29+
if model_class not in MODEL_HASHES:
30+
MODEL_HASHES[model_class] = hash_model(model_class)
31+
32+
33+
class ModelCache:
34+
_cache_directory: str
35+
36+
def __init__(self, cache_directory: str):
37+
if not os.path.exists(cache_directory):
38+
os.makedirs(cache_directory)
39+
self._cache_directory = cache_directory
40+
41+
def _cache_target(self, model_class: Type[M], tool_id: str, tool_version: str) -> str:
42+
ensure_model_has_hash(model_class)
43+
# consider breaking this into multiple directories...
44+
cache_target = os.path.join(self._cache_directory, MODEL_HASHES[model_class], tool_id, tool_version)
45+
return cache_target
46+
47+
def get_cache_entry_for(self, model_class: Type[M], tool_id: str, tool_version: str) -> Optional[M]:
48+
cache_target = self._cache_target(model_class, tool_id, tool_version)
49+
if not os.path.exists(cache_target):
50+
return None
51+
with open(cache_target) as f:
52+
return model_class.model_validate(json.load(f))
53+
54+
def has_cached_entry_for(self, model_class: Type[M], tool_id: str, tool_version: str) -> bool:
55+
cache_target = self._cache_target(model_class, tool_id, tool_version)
56+
return os.path.exists(cache_target)
57+
58+
def insert_cache_entry_for(self, model_object: M, tool_id: str, tool_version: str) -> None:
59+
cache_target = self._cache_target(model_object.__class__, tool_id, tool_version)
60+
parent_directory = os.path.dirname(cache_target)
61+
if not os.path.exists(parent_directory):
62+
os.makedirs(parent_directory)
63+
with open(cache_target, "w") as f:
64+
json.dump(model_object.dict(), f)

lib/tool_shed/managers/tool_state_cache.py

Lines changed: 0 additions & 42 deletions
This file was deleted.

lib/tool_shed/managers/tools.py

Lines changed: 66 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
Tuple,
99
)
1010

11+
from pydantic import BaseModel
12+
1113
from galaxy import exceptions
1214
from galaxy.exceptions import (
1315
InternalServerError,
@@ -21,13 +23,16 @@
2123
)
2224
from galaxy.tool_util.parameters import (
2325
input_models_for_tool_source,
24-
tool_parameter_bundle_from_json,
25-
ToolParameterBundleModel,
26+
ToolParameterT,
2627
)
2728
from galaxy.tool_util.parser import (
2829
get_tool_source,
2930
ToolSource,
3031
)
32+
from galaxy.tool_util.parser.interface import (
33+
Citation,
34+
XrefDict,
35+
)
3136
from galaxy.tools.stock import stock_tool_sources
3237
from tool_shed.context import (
3338
ProvidesRepositoriesContext,
@@ -41,6 +46,53 @@
4146
STOCK_TOOL_SOURCES: Optional[Dict[str, Dict[str, ToolSource]]] = None
4247

4348

49+
# parse the tool source with galaxy.util abstractions to provide a bit richer
50+
# information about the tool than older tool shed abstractions.
51+
class ParsedTool(BaseModel):
52+
id: str
53+
version: Optional[str]
54+
name: str
55+
description: Optional[str]
56+
inputs: List[ToolParameterT]
57+
citations: List[Citation]
58+
license: Optional[str]
59+
profile: Optional[str]
60+
edam_operations: List[str]
61+
edam_topics: List[str]
62+
xrefs: List[XrefDict]
63+
help: Optional[str]
64+
65+
66+
def _parse_tool(tool_source: ToolSource) -> ParsedTool:
67+
id = tool_source.parse_id()
68+
version = tool_source.parse_version()
69+
name = tool_source.parse_name()
70+
description = tool_source.parse_description()
71+
inputs = input_models_for_tool_source(tool_source).input_models
72+
citations = tool_source.parse_citations()
73+
license = tool_source.parse_license()
74+
profile = tool_source.parse_profile()
75+
edam_operations = tool_source.parse_edam_operations()
76+
edam_topics = tool_source.parse_edam_topics()
77+
xrefs = tool_source.parse_xrefs()
78+
help = tool_source.parse_help()
79+
80+
return ParsedTool(
81+
id=id,
82+
version=version,
83+
name=name,
84+
description=description,
85+
profile=profile,
86+
inputs=inputs,
87+
license=license,
88+
citations=citations,
89+
edam_operations=edam_operations,
90+
edam_topics=edam_topics,
91+
xrefs=xrefs,
92+
help=help,
93+
)
94+
95+
4496
def search(trans: SessionRequestContext, q: str, page: int = 1, page_size: int = 10) -> dict:
4597
"""
4698
Perform the search over TS tools index.
@@ -97,23 +149,23 @@ def get_repository_metadata_tool_dict(
97149
raise ObjectNotFound()
98150

99151

100-
def tool_input_models_cached_for(
152+
def parsed_tool_model_cached_for(
101153
trans: ProvidesRepositoriesContext, trs_tool_id: str, tool_version: str, repository_clone_url: Optional[str] = None
102-
) -> ToolParameterBundleModel:
103-
tool_state_cache = trans.app.tool_state_cache
104-
raw_json = tool_state_cache.get_cache_entry_for(trs_tool_id, tool_version)
105-
if raw_json is not None:
106-
return tool_parameter_bundle_from_json(raw_json)
107-
bundle = tool_input_models_for(trans, trs_tool_id, tool_version, repository_clone_url=repository_clone_url)
108-
tool_state_cache.insert_cache_entry_for(trs_tool_id, tool_version, bundle.dict())
109-
return bundle
154+
) -> ParsedTool:
155+
model_cache = trans.app.model_cache
156+
parsed_tool = model_cache.get_cache_entry_for(ParsedTool, trs_tool_id, tool_version)
157+
if parsed_tool is not None:
158+
return parsed_tool
159+
parsed_tool = parsed_tool_model_for(trans, trs_tool_id, tool_version, repository_clone_url=repository_clone_url)
160+
model_cache.insert_cache_entry_for(parsed_tool, trs_tool_id, tool_version)
161+
return parsed_tool
110162

111163

112-
def tool_input_models_for(
164+
def parsed_tool_model_for(
113165
trans: ProvidesRepositoriesContext, trs_tool_id: str, tool_version: str, repository_clone_url: Optional[str] = None
114-
) -> ToolParameterBundleModel:
166+
) -> ParsedTool:
115167
tool_source = tool_source_for(trans, trs_tool_id, tool_version, repository_clone_url=repository_clone_url)
116-
return input_models_for_tool_source(tool_source)
168+
return _parse_tool(tool_source)
117169

118170

119171
def tool_source_for(

lib/tool_shed/structured_app.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from galaxy.structured_app import BasicSharedApp
44

55
if TYPE_CHECKING:
6-
from tool_shed.managers.tool_state_cache import ToolStateCache
6+
from tool_shed.managers.model_cache import ModelCache
77
from tool_shed.repository_registry import Registry as RepositoryRegistry
88
from tool_shed.repository_types.registry import Registry as RepositoryTypesRegistry
99
from tool_shed.util.hgweb_config import HgWebConfigManager
@@ -17,4 +17,4 @@ class ToolShedApp(BasicSharedApp):
1717
repository_registry: "RepositoryRegistry"
1818
hgweb_config_manager: "HgWebConfigManager"
1919
security_agent: "CommunityRBACAgent"
20-
tool_state_cache: "ToolStateCache"
20+
model_cache: "ModelCache"

lib/tool_shed/webapp/api2/tools.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@
1010
from galaxy.tool_util.parameters import (
1111
RequestToolState,
1212
to_json_schema_string,
13-
ToolParameterBundleModel,
1413
)
1514
from tool_shed.context import SessionRequestContext
1615
from tool_shed.managers.tools import (
16+
parsed_tool_model_cached_for,
17+
ParsedTool,
1718
search,
18-
tool_input_models_cached_for,
1919
)
2020
from tool_shed.managers.trs import (
2121
get_tool,
@@ -144,17 +144,17 @@ def trs_get_versions(
144144
return get_tool(trans, tool_id).versions
145145

146146
@router.get(
147-
"/api/tools/{tool_id}/versions/{tool_version}/parameter_model",
147+
"/api/tools/{tool_id}/versions/{tool_version}",
148148
operation_id="tools__parameter_model",
149149
summary="Return Galaxy's meta model description of the tool's inputs",
150150
)
151-
def tool_parameters_meta_model(
151+
def show_tool(
152152
self,
153153
trans: SessionRequestContext = DependsOnTrans,
154154
tool_id: str = TOOL_ID_PATH_PARAM,
155155
tool_version: str = TOOL_VERSION_PATH_PARAM,
156-
) -> ToolParameterBundleModel:
157-
return tool_input_models_cached_for(trans, tool_id, tool_version)
156+
) -> ParsedTool:
157+
return parsed_tool_model_cached_for(trans, tool_id, tool_version)
158158

159159
@router.get(
160160
"/api/tools/{tool_id}/versions/{tool_version}/parameter_request_schema",
@@ -168,6 +168,5 @@ def tool_state(
168168
tool_id: str = TOOL_ID_PATH_PARAM,
169169
tool_version: str = TOOL_VERSION_PATH_PARAM,
170170
) -> Response:
171-
return json_schema_response(
172-
RequestToolState.parameter_model_for(tool_input_models_cached_for(trans, tool_id, tool_version))
173-
)
171+
parsed_tool = parsed_tool_model_cached_for(trans, tool_id, tool_version)
172+
return json_schema_response(RequestToolState.parameter_model_for(parsed_tool.inputs))

lib/tool_shed/webapp/app.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
from galaxy.structured_app import BasicSharedApp
3434
from galaxy.web_stack import application_stack_instance
3535
from tool_shed.grids.repository_grid_filter_manager import RepositoryGridFilterManager
36-
from tool_shed.managers.tool_state_cache import ToolStateCache
36+
from tool_shed.managers.model_cache import ModelCache
3737
from tool_shed.structured_app import ToolShedApp
3838
from tool_shed.util.hgweb_config import hgweb_config_manager
3939
from tool_shed.webapp.model.migrations import verify_database
@@ -84,7 +84,7 @@ def __init__(self, **kwd) -> None:
8484
self._register_singleton(SharedModelMapping, model)
8585
self._register_singleton(mapping.ToolShedModelMapping, model)
8686
self._register_singleton(scoped_session, self.model.context)
87-
self.tool_state_cache = ToolStateCache(self.config.tool_state_cache_dir)
87+
self.model_cache = ModelCache(self.config.model_cache_dir)
8888
self.user_manager = self._register_singleton(UserManager, UserManager(self, app_type="tool_shed"))
8989
self.api_keys_manager = self._register_singleton(ApiKeyManager)
9090
# initialize the Tool Shed tag handler.

0 commit comments

Comments
 (0)