Mercurial > repos > guerler > springsuite
comparison planemo/lib/python3.7/site-packages/galaxy/tool_util/deps/views.py @ 1:56ad4e20f292 draft
"planemo upload commit 6eee67778febed82ddd413c3ca40b3183a3898f1"
author | guerler |
---|---|
date | Fri, 31 Jul 2020 00:32:28 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
0:d30785e31577 | 1:56ad4e20f292 |
---|---|
1 from galaxy import exceptions | |
2 from galaxy.util import asbool, listify | |
3 from .dependencies import ToolInfo | |
4 from .resolvers import ( | |
5 ContainerDependency, | |
6 NullDependency, | |
7 ) | |
8 | |
9 | |
10 class DependencyResolversView(object): | |
11 """ Provide a RESTfulish/JSONy interface to a galaxy.tool_util.deps.DependencyResolver | |
12 object. This can be adapted by the Galaxy web framework or other web apps. | |
13 """ | |
14 | |
15 def __init__(self, app): | |
16 self._app = app | |
17 | |
18 def index(self): | |
19 return [r.to_dict() for r in self._dependency_resolvers] | |
20 | |
21 def show(self, index): | |
22 return self._dependency_resolver(index).to_dict() | |
23 | |
24 def reload(self): | |
25 self.toolbox.reload_dependency_manager() | |
26 | |
27 def manager_requirements(self): | |
28 requirements = [] | |
29 for index, resolver in enumerate(self._dependency_resolvers): | |
30 if not hasattr(resolver, "list_dependencies"): | |
31 continue | |
32 for requirement in resolver.list_dependencies(): | |
33 requirements.append({"index": index, "requirement": requirement.to_dict()}) | |
34 return requirements | |
35 | |
36 def resolver_requirements(self, index): | |
37 requirements = [] | |
38 resolver = self._dependency_resolver(index) | |
39 if not hasattr(resolver, "list_dependencies"): | |
40 raise exceptions.NotImplemented() | |
41 for requirement in resolver.list_dependencies(): | |
42 requirements.append(requirement.to_dict()) | |
43 return requirements | |
44 | |
45 def manager_dependency(self, **kwds): | |
46 return self._dependency(**kwds) | |
47 | |
48 def resolver_dependency(self, index, **kwds): | |
49 return self._dependency(**kwds) | |
50 | |
51 def show_dependencies(self, tool_requirements_d, installed_tool_dependencies=None, **kwd): | |
52 """ | |
53 Resolves dependencies to build a requirements status in the admin panel/API | |
54 """ | |
55 to_deps_kwds = { | |
56 'install': False, | |
57 'return_null': True, | |
58 'installed_tool_dependencies': installed_tool_dependencies | |
59 } | |
60 to_deps_kwds.update(kwd) | |
61 dependencies_per_tool = {tool: self._dependency_manager.requirements_to_dependencies(requirements, | |
62 **to_deps_kwds) | |
63 for tool, requirements in tool_requirements_d.items()} | |
64 return dependencies_per_tool | |
65 | |
66 def uninstall_dependencies(self, index=None, resolver_type=None, container_type=None, **payload): | |
67 """Attempt to uninstall requirements. Returns 0 if successfull, else None.""" | |
68 requirements = payload.get('requirements') | |
69 if not requirements: | |
70 return None | |
71 if index: | |
72 resolver = self._dependency_resolvers[index] | |
73 if resolver.can_uninstall_dependencies: | |
74 return resolver.uninstall(requirements) | |
75 elif resolver_type: | |
76 for resolver in self._dependency_resolvers: | |
77 if resolver.resolver_type == resolver_type and resolver.can_uninstall_dependencies: | |
78 return resolver.uninstall(requirements) | |
79 elif container_type: | |
80 for resolver in self._dependency_resolvers: | |
81 if getattr(resolver, "container_type", None) == container_type and resolver.can_uninstall_dependencies: | |
82 return resolver.uninstall(requirements) | |
83 else: | |
84 for index in self.uninstallable_resolvers: | |
85 return_code = self._dependency_resolvers[index].uninstall(requirements) | |
86 if return_code == 0: | |
87 return return_code | |
88 return None | |
89 | |
90 @property | |
91 def unused_dependency_paths(self): | |
92 """List dependencies that are not currently installed.""" | |
93 unused_dependencies = [] | |
94 toolbox_requirements_status = self.toolbox_requirements_status | |
95 for resolver in self._dependency_resolvers: | |
96 if hasattr(resolver, 'unused_dependency_paths'): | |
97 unused_dependencies.extend(resolver.unused_dependency_paths(toolbox_requirements_status)) | |
98 return set(unused_dependencies) | |
99 | |
100 def remove_unused_dependency_paths(self, envs): | |
101 """ | |
102 Remove dependencies that are not currently used. | |
103 | |
104 Returns a list of all environments that have been successfully removed. | |
105 """ | |
106 envs_to_remove = set(envs) | |
107 toolbox_requirements_status = self.toolbox_requirements_status | |
108 removed_environments = set() | |
109 for resolver in self._dependency_resolvers: | |
110 if hasattr(resolver, 'unused_dependency_paths') and hasattr(resolver, 'uninstall_environments'): | |
111 unused_dependencies = resolver.unused_dependency_paths(toolbox_requirements_status) | |
112 can_remove = envs_to_remove & set(unused_dependencies) | |
113 exit_code = resolver.uninstall_environments(can_remove) | |
114 if exit_code == 0: | |
115 removed_environments = removed_environments.union(can_remove) | |
116 envs_to_remove = envs_to_remove.difference(can_remove) | |
117 return list(removed_environments) | |
118 | |
119 def install_dependencies(self, requirements, **kwds): | |
120 kwds['install'] = True | |
121 return self._dependency_manager._requirements_to_dependencies_dict(requirements, **kwds) | |
122 | |
123 def install_dependency(self, index=None, **payload): | |
124 """ | |
125 Installs dependency using highest priority resolver that supports dependency installation | |
126 (Currently only the conda resolver supports this). If index is given, attempt | |
127 installation directly using the corresponding resolver. | |
128 Returns True on success, False on failure. | |
129 payload is dictionary that must container name, version and type, | |
130 e.g. {'name': 'numpy', version='1.9.1', type='package'} | |
131 """ | |
132 if index: | |
133 return self._install_dependency(index, **payload) | |
134 else: | |
135 for index in self.installable_resolvers: | |
136 success = self._install_dependency(index, **payload) | |
137 if success: | |
138 return success | |
139 return False | |
140 | |
141 def _install_dependency(self, index, **payload): | |
142 """ | |
143 Resolver install dependency should return True when installation succeeds, | |
144 False if not successful | |
145 """ | |
146 resolver = self._dependency_resolver(index) | |
147 if not hasattr(resolver, "install_dependency"): | |
148 raise exceptions.NotImplemented() | |
149 | |
150 name, version, type, extra_kwds = self._parse_dependency_info(payload) | |
151 return resolver.install_dependency( | |
152 name=name, | |
153 version=version, | |
154 type=type, | |
155 **extra_kwds | |
156 ) | |
157 | |
158 def _dependency(self, index=None, **kwds): | |
159 if index is not None: | |
160 index = int(index) | |
161 | |
162 name, version, type, extra_kwds = self._parse_dependency_info(kwds) | |
163 resolve_kwds = dict( | |
164 job_directory=None, | |
165 index=index, | |
166 **extra_kwds | |
167 ) | |
168 dependency = self._dependency_manager.find_dep( | |
169 name, version=version, type=type, **resolve_kwds | |
170 ) | |
171 return dependency.to_dict() | |
172 | |
173 def _parse_dependency_info(self, kwds): | |
174 extra_kwds = kwds.copy() | |
175 name = extra_kwds.pop("name", None) | |
176 if name is None: | |
177 raise exceptions.RequestParameterMissingException("Missing 'name' parameter required for resolution.") | |
178 version = extra_kwds.pop("version", None) | |
179 type = extra_kwds.pop("type", "package") | |
180 return name, version, type, extra_kwds | |
181 | |
182 def _dependency_resolver(self, index): | |
183 index = int(index) | |
184 return self._dependency_resolvers[index] | |
185 | |
186 @property | |
187 def _dependency_manager(self): | |
188 return self._app.toolbox.dependency_manager | |
189 | |
190 @property | |
191 def _dependency_resolvers(self): | |
192 dependency_manager = self._dependency_manager | |
193 dependency_resolvers = dependency_manager.dependency_resolvers | |
194 return dependency_resolvers | |
195 | |
196 @property | |
197 def installable_resolvers(self): | |
198 """ | |
199 List index for all active resolvers that have the 'install_dependency' attribute. | |
200 """ | |
201 return [index for index, resolver in enumerate(self._dependency_resolvers) if hasattr(resolver, "install_dependency") and not resolver.disabled] | |
202 | |
203 @property | |
204 def uninstallable_resolvers(self): | |
205 """ | |
206 List index for all active resolvers that can uninstall dependencies that have been installed through this resolver. | |
207 """ | |
208 return [index for index, resolver in enumerate(self._dependency_resolvers) if resolver.can_uninstall_dependencies and not resolver.disabled] | |
209 | |
210 @property | |
211 def tool_ids_by_requirements(self): | |
212 """Dictionary with requirements as keys, and tool_ids as values.""" | |
213 tool_ids_by_requirements = {} | |
214 if not self._app.toolbox.tools_by_id: | |
215 return {} | |
216 for tid, tool in self._app.toolbox.tools_by_id.items(): | |
217 if tool.tool_requirements not in tool_ids_by_requirements: | |
218 tool_ids_by_requirements[tool.tool_requirements] = [tid] | |
219 else: | |
220 tool_ids_by_requirements[tool.tool_requirements].append(tid) | |
221 return tool_ids_by_requirements | |
222 | |
223 @property | |
224 def toolbox_requirements_status(self): | |
225 return self.summarize_requirements() | |
226 | |
227 def summarize_requirements(self, **kwds): | |
228 summary_kwds = {} | |
229 if 'index' in kwds: | |
230 summary_kwds['index'] = int(kwds['index']) | |
231 if 'include_containers' in kwds: | |
232 summary_kwds['include_containers'] = asbool(kwds['include_containers']) | |
233 if 'container_type' in kwds: | |
234 summary_kwds['container_type'] = kwds['container_type'] | |
235 if 'resolver_type' in kwds: | |
236 summary_kwds['resolver_type'] = kwds['resolver_type'] | |
237 if 'search' in kwds: | |
238 summary_kwds['search'] = asbool(kwds['search']) | |
239 if 'install' in kwds: | |
240 summary_kwds['install'] = asbool(kwds['install']) | |
241 | |
242 tool_ids_by_requirements = self.tool_ids_by_requirements | |
243 statuses = {r: self.get_requirements_status(tool_requirements_d={tids[0]: r}, | |
244 installed_tool_dependencies=self._app.toolbox.tools_by_id[tids[0]].installed_tool_dependencies, **summary_kwds) | |
245 for r, tids in tool_ids_by_requirements.items()} | |
246 if kwds.get("for_json", False): | |
247 # All public attributes of this class should be returning JSON - this is meant to mimic a restful API. | |
248 rval = [] | |
249 for requirements, status in statuses.items(): | |
250 item = {} | |
251 item["requirements"] = requirements.to_dict() | |
252 item["status"] = status | |
253 item["tool_ids"] = tool_ids_by_requirements[requirements] | |
254 rval.append(item) | |
255 statuses = rval | |
256 return statuses | |
257 | |
258 def summarize_tools(self, **kwds): | |
259 summary_kwds = {} | |
260 if 'index' in kwds: | |
261 summary_kwds['index'] = int(kwds['index']) | |
262 if 'include_containers' in kwds: | |
263 summary_kwds['include_containers'] = asbool(kwds['include_containers']) | |
264 if 'container_type' in kwds: | |
265 summary_kwds['container_type'] = kwds['container_type'] | |
266 if 'resolver_type' in kwds: | |
267 summary_kwds['resolver_type'] = kwds['resolver_type'] | |
268 if 'search' in kwds: | |
269 summary_kwds['search'] = asbool(kwds['search']) | |
270 if 'install' in kwds: | |
271 summary_kwds['install'] = asbool(kwds['install']) | |
272 | |
273 tool_ids = pop_tool_ids(kwds) | |
274 | |
275 rval = [] | |
276 for tid, tool in self._app.toolbox.tools_by_id.items(): | |
277 if tool_ids and tid not in tool_ids: | |
278 continue | |
279 | |
280 requirements = tool.tool_requirements | |
281 status = self.get_requirements_status(tool_requirements_d={tid: requirements}, | |
282 installed_tool_dependencies=tool.installed_tool_dependencies, | |
283 tool_instance=tool, **summary_kwds) | |
284 item = {} | |
285 item["requirements"] = requirements.to_dict() | |
286 item["status"] = status | |
287 item["tool_ids"] = [tid] | |
288 rval.append(item) | |
289 return rval | |
290 | |
291 def get_requirements_status(self, tool_requirements_d, installed_tool_dependencies=None, **kwd): | |
292 dependencies = self.show_dependencies(tool_requirements_d, installed_tool_dependencies, **kwd) | |
293 # dependencies is a dict keyed on tool_ids, value is a ToolRequirements object for that tool. | |
294 # We use the union of resolvable ToolRequirements to get resolved dependencies without duplicates. | |
295 requirements = [r.resolvable for r in tool_requirements_d.values()] | |
296 flat_tool_requirements = set().union(*requirements) | |
297 flat_dependencies = [] | |
298 for requirements_odict in dependencies.values(): | |
299 for requirement in requirements_odict: | |
300 if requirement in flat_tool_requirements: | |
301 flat_dependencies.append(requirements_odict[requirement]) | |
302 flat_tool_requirements.remove(requirement) | |
303 return [d.to_dict() for d in flat_dependencies] | |
304 | |
305 def clean(self, index=None, **kwds): | |
306 if index: | |
307 resolver = self._dependency_resolver(index) | |
308 if not hasattr(resolver, "clean"): | |
309 raise exceptions.NotImplemented() | |
310 else: | |
311 resolver.clean() | |
312 return "OK" | |
313 else: | |
314 [resolver.clean(**kwds) for resolver in self._dependency_resolvers if hasattr(resolver, 'clean')] | |
315 return "OK" | |
316 | |
317 | |
318 class ContainerResolutionView(object): | |
319 """ | |
320 """ | |
321 | |
322 def __init__(self, app): | |
323 self._app = app | |
324 | |
325 def index(self): | |
326 return [r.to_dict() for r in self._container_resolvers] | |
327 | |
328 def show(self, index): | |
329 return self._container_resolver(index).to_dict() | |
330 | |
331 def resolve(self, **kwds): | |
332 find_best_kwds = { | |
333 'install': False, | |
334 'enabled_container_types': ['docker', 'singularity'], | |
335 'resolution_cache': kwds.get("resolution_cache"), | |
336 } | |
337 | |
338 if 'index' in kwds: | |
339 find_best_kwds['index'] = int(kwds['index']) | |
340 if 'container_type' in kwds: | |
341 find_best_kwds['enabled_container_types'] = [kwds['container_type']] | |
342 if 'resolver_type' in kwds: | |
343 find_best_kwds['resolver_type'] = kwds['resolver_type'] | |
344 if 'install' in kwds: | |
345 find_best_kwds['install'] = asbool(kwds['install']) | |
346 | |
347 tool_info_kwds = {} | |
348 tool_id = kwds["tool_id"] | |
349 tool = self._app.toolbox.tools_by_id[tool_id] | |
350 | |
351 requirements = tool.tool_requirements | |
352 tool_info_kwds = dict(requirements=requirements) | |
353 requirements_only = asbool(kwds.get("requirements_only", False)) | |
354 # If requirements_only, simply use the tool to load a requirement set from, | |
355 # mimics the default behavior of searching for containers through the dependency resolution | |
356 # component. Not useful for tool execution but perhaps when summarizing mulled containers for requirements. | |
357 if not requirements_only: | |
358 tool_info_kwds['container_descriptions'] = tool.containers | |
359 tool_info_kwds['requires_galaxy_python_environment'] = tool.requires_galaxy_python_environment | |
360 tool_info_kwds['tool_id'] = tool.id | |
361 tool_info_kwds['tool_version'] = tool.version | |
362 | |
363 find_best_kwds["tool_info"] = ToolInfo(**tool_info_kwds) | |
364 | |
365 # Consider implementing 'search' to match dependency resolution API. | |
366 resolved_container_description = self._app.container_finder.resolve( | |
367 **find_best_kwds | |
368 ) | |
369 if resolved_container_description: | |
370 status = ContainerDependency(resolved_container_description.container_description, container_resolver=resolved_container_description.container_resolver).to_dict() | |
371 else: | |
372 status = NullDependency().to_dict() | |
373 return { | |
374 "tool_id": kwds["tool_id"], | |
375 "status": status, | |
376 "requirements": requirements.to_dict() | |
377 } | |
378 | |
379 def resolve_toolbox(self, **kwds): | |
380 rval = [] | |
381 resolve_kwds = kwds.copy() | |
382 tool_ids = pop_tool_ids(resolve_kwds) | |
383 resolve_kwds["resolution_cache"] = self._app.container_finder.resolution_cache() | |
384 if tool_ids is not None: | |
385 tool_ids = listify(tool_ids) | |
386 for tool_id, tool in self._app.toolbox.tools_by_id.items(): | |
387 if tool_ids is not None and tool_id not in tool_ids: | |
388 continue | |
389 | |
390 if tool.tool_action.produces_real_jobs: | |
391 rval.append(self.resolve(tool_id=tool_id, **resolve_kwds)) | |
392 return rval | |
393 | |
394 @property | |
395 def _container_resolvers(self): | |
396 return self._app.container_finder.container_resolvers | |
397 | |
398 def _container_resolver(self, index): | |
399 index = int(index) | |
400 return self._container_resolvers[index] | |
401 | |
402 | |
403 def pop_tool_ids(kwds): | |
404 tool_ids = None | |
405 if "tool_ids" in kwds: | |
406 tool_ids = listify(kwds.pop("tool_ids")) | |
407 if "tool_ids[]" in kwds: | |
408 tool_ids = listify(kwds.pop("tool_ids[]")) | |
409 if "tool_id" in kwds: | |
410 tool_ids = [kwds.pop("tool_id")] | |
411 return tool_ids |