comparison env/lib/python3.9/site-packages/galaxy/tool_util/deps/resolvers/modules.py @ 0:4f3585e2f14b draft default tip

"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author shellac
date Mon, 22 Mar 2021 18:12:50 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:4f3585e2f14b
1 """
2 This file contains the outline of an implementation to load environment modules
3 (http://modules.sourceforge.net/).
4
5 This is a community contributed feature and the core Galaxy team does utilize
6 it, hence support for it will be minimal. The Galaxy team eagerly welcomes
7 community contribution and maintenance however.
8 """
9 import logging
10 from io import StringIO
11 from os import (
12 environ,
13 pathsep
14 )
15 from os.path import (
16 exists,
17 isdir,
18 join
19 )
20 from subprocess import (
21 PIPE,
22 Popen
23 )
24
25 from . import (
26 Dependency,
27 DependencyResolver,
28 MappableDependencyResolver,
29 NullDependency,
30 )
31
32 log = logging.getLogger(__name__)
33
34 DEFAULT_MODULECMD_PATH = "modulecmd" # Just check path
35 DEFAULT_MODULE_PATH = '/usr/share/modules/modulefiles'
36 DEFAULT_INDICATOR = '(default)'
37 DEFAULT_MODULE_PREFETCH = "true"
38 DEFAULT_MAPPING_FILE = 'config/environment_modules_mapping.yml'
39 UNKNOWN_FIND_BY_MESSAGE = "ModuleDependencyResolver does not know how to find modules by [%s], find_by should be one of %s"
40
41
42 class ModuleDependencyResolver(DependencyResolver, MappableDependencyResolver):
43 dict_collection_visible_keys = DependencyResolver.dict_collection_visible_keys + ['base_path', 'modulepath', 'modulecmd', 'prefetch', 'default_indicator', 'find_by']
44 resolver_type = "modules"
45
46 def __init__(self, dependency_manager, **kwds):
47 # Mapping file management
48 self._set_default_mapping_file(kwds)
49 self._setup_mapping(dependency_manager, **kwds)
50 self.versionless = _string_as_bool(kwds.get('versionless', 'false'))
51 find_by = kwds.get('find_by', 'avail')
52 self.find_by = find_by
53 prefetch = _string_as_bool(kwds.get('prefetch', DEFAULT_MODULE_PREFETCH))
54 self.modulecmd = kwds.get('modulecmd', DEFAULT_MODULECMD_PATH)
55 self.modulepath = kwds.get('modulepath', self.__default_modulespath())
56 self.default_indicator = kwds.get('default_indicator', DEFAULT_INDICATOR)
57 if find_by == 'directory':
58 self.module_checker = DirectoryModuleChecker(self, self.modulepath, prefetch)
59 elif find_by == 'avail':
60 self.module_checker = AvailModuleChecker(self, self.modulepath, prefetch, self.default_indicator)
61 else:
62 raise Exception(UNKNOWN_FIND_BY_MESSAGE % (find_by, ["avail", "directory"]))
63
64 def __default_modulespath(self):
65 if 'MODULEPATH' in environ:
66 module_path = environ['MODULEPATH']
67 elif 'MODULESHOME' in environ:
68 module_path = join(environ['MODULESHOME'], 'modulefiles')
69 else:
70 module_path = DEFAULT_MODULE_PATH
71 return module_path
72
73 def _set_default_mapping_file(self, resolver_attributes):
74 if 'mapping_files' not in resolver_attributes:
75 if exists(DEFAULT_MAPPING_FILE):
76 resolver_attributes['mapping_files'] = DEFAULT_MAPPING_FILE
77
78 def resolve(self, requirement, **kwds):
79 requirement = self._expand_mappings(requirement)
80 name, version, type = requirement.name, requirement.version, requirement.type
81
82 if type != "package":
83 return NullDependency(version=version, name=name)
84
85 if self.__has_module(name, version):
86 return ModuleDependency(self, name, version, exact=True, dependency_resolver=self)
87 elif self.versionless and self.__has_module(name, None):
88 return ModuleDependency(self, name, None, exact=False, dependency_resolver=self)
89
90 return NullDependency(version=version, name=name)
91
92 def __has_module(self, name, version):
93 return self.module_checker.has_module(name, version)
94
95
96 class DirectoryModuleChecker:
97 """Finds module by path.
98
99 Searches the paths listed in modulepath to for a file or directory matching the module name.
100 If the version=True, searches for files named module/version."""
101
102 def __init__(self, module_dependency_resolver, modulepath, prefetch):
103 self.module_dependency_resolver = module_dependency_resolver
104 self.directories = modulepath.split(pathsep)
105 if prefetch:
106 log.warning("Created module dependency resolver with prefetch enabled, but directory module checker does not support this.")
107
108 def has_module(self, module, version):
109 has_module = False
110 for directory in self.directories:
111 module_directory = join(directory, module)
112 has_module_directory = isdir(module_directory)
113 if not version:
114 has_module = has_module_directory or exists(module_directory) # could be a bare modulefile
115 else:
116 modulefile = join(module_directory, version)
117 has_modulefile = exists(modulefile)
118 has_module = has_module_directory and has_modulefile
119 if has_module:
120 break
121 return has_module
122
123
124 class AvailModuleChecker:
125 """Finds modules by searching output of 'module avail'.
126
127 Parses the Environment Modules 'module avail' output, splitting
128 module names into module and version on '/' and discarding a postfix matching default_indicator
129 (by default '(default)'. Matching is done using the module and
130 (if version=True) the module version."""
131
132 def __init__(self, module_dependency_resolver, modulepath, prefetch, default_indicator=DEFAULT_INDICATOR):
133 self.module_dependency_resolver = module_dependency_resolver
134 self.modulepath = modulepath
135 self.default_indicator = default_indicator
136 if prefetch:
137 prefetched_modules = []
138 for module in self.__modules():
139 prefetched_modules.append(module)
140 else:
141 prefetched_modules = None
142 self.prefetched_modules = prefetched_modules
143
144 def has_module(self, module, version):
145 module_generator = self.prefetched_modules
146 if module_generator is None:
147 module_generator = self.__modules()
148
149 for module_name, module_version in module_generator:
150 names_match = module == module_name
151 module_match = names_match and (version is None or module_version == version)
152 if module_match:
153 return True
154 return False
155
156 def __modules(self):
157 raw_output = self.__module_avail_output().decode("utf-8")
158 for line in StringIO(raw_output):
159 line = line and line.strip()
160 if not line or line.startswith("-"):
161 continue
162
163 line_modules = line.split()
164 for module in line_modules:
165 if module.endswith(self.default_indicator):
166 module = module[0:-len(self.default_indicator)].strip()
167 module_parts = module.split('/')
168 module_version = None
169 if len(module_parts) == 2:
170 module_version = module_parts[1]
171 module_name = module_parts[0]
172 yield module_name, module_version
173
174 def __module_avail_output(self):
175 avail_command = [self.module_dependency_resolver.modulecmd, 'sh', 'avail']
176 return Popen(avail_command, stderr=PIPE, env={'MODULEPATH': self.modulepath}).communicate()[1]
177
178
179 class ModuleDependency(Dependency):
180 """Converts module dependencies into shell expressions using modulecmd.
181
182 Using Environment Modules' 'modulecmd' (specifically 'modulecmd sh load') to
183 convert module specifications into shell expressions for inclusion in
184 the script used to run a tool in Galaxy."""
185 dict_collection_visible_keys = Dependency.dict_collection_visible_keys + ['module_name', 'module_version', 'dependency_resolver']
186 dependency_type = 'module'
187
188 def __init__(self, module_dependency_resolver, module_name, module_version=None, exact=True, dependency_resolver=None):
189 self.module_dependency_resolver = module_dependency_resolver
190 self.module_name = module_name
191 self.module_version = module_version
192 self._exact = exact
193 self.dependency_resolver = dependency_resolver
194
195 @property
196 def name(self):
197 return self.module_name
198
199 @property
200 def version(self):
201 return self.module_version
202
203 @property
204 def exact(self):
205 return self._exact
206
207 def shell_commands(self):
208 module_to_load = self.module_name
209 if self.module_version:
210 module_to_load = f'{self.module_name}/{self.module_version}'
211 command = 'MODULEPATH={}; export MODULEPATH; eval `{} sh load {}`'.format(self.module_dependency_resolver.modulepath,
212 self.module_dependency_resolver.modulecmd,
213 module_to_load)
214 return command
215
216
217 def _string_as_bool(value):
218 return str(value).lower() == "true"
219
220
221 __all__ = ('ModuleDependencyResolver', )