Mercurial > repos > shellac > sam_consensus_v3
comparison env/lib/python3.9/site-packages/planemo/shed2tap/base.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 from __future__ import print_function | |
| 2 | |
| 3 import os | |
| 4 import subprocess | |
| 5 import sys | |
| 6 import tarfile | |
| 7 import zipfile | |
| 8 from ftplib import all_errors as FTPErrors # tuple of exceptions | |
| 9 from typing import List | |
| 10 from xml.etree import ElementTree | |
| 11 | |
| 12 from galaxy.util import unicodify | |
| 13 from six import iteritems | |
| 14 from six import string_types | |
| 15 from six.moves import map as imap | |
| 16 from six.moves.urllib.error import URLError | |
| 17 from six.moves.urllib.request import urlretrieve | |
| 18 | |
| 19 | |
| 20 TOOLSHED_MAP = { | |
| 21 "toolshed": "https://toolshed.g2.bx.psu.edu", | |
| 22 "testtoolshed": "https://testtoolshed.g2.bx.psu.edu", | |
| 23 } | |
| 24 | |
| 25 | |
| 26 class Dependencies(object): | |
| 27 """ Base class for parsing Tool Shed dependency files. | |
| 28 """ | |
| 29 | |
| 30 def __init__( | |
| 31 self, | |
| 32 dependencies_file, | |
| 33 repo=None, | |
| 34 package_factory=None, | |
| 35 ): | |
| 36 if package_factory is None: | |
| 37 package_factory = BasePackage | |
| 38 self.repo = repo | |
| 39 self.root = ElementTree.parse(dependencies_file).getroot() | |
| 40 packages = [] | |
| 41 dependencies = [] | |
| 42 package_els = self.root.findall("package") | |
| 43 assert package_els is not None | |
| 44 for package_el in package_els: | |
| 45 install_els = package_el.findall("install") | |
| 46 readme_els = package_el.findall("readme") | |
| 47 if len(readme_els) > 0: | |
| 48 readme = readme_els[0].text | |
| 49 else: | |
| 50 readme = None | |
| 51 assert len(install_els) in (0, 1) | |
| 52 if len(install_els) == 1: | |
| 53 install_el = install_els[0] | |
| 54 package = package_factory( | |
| 55 self, | |
| 56 package_el, | |
| 57 install_el, | |
| 58 readme=readme | |
| 59 ) | |
| 60 packages.append(package) | |
| 61 else: | |
| 62 repository_el = package_el.find("repository") | |
| 63 if repository_el is None: | |
| 64 message = "no repository in package el for %s" % repo | |
| 65 raise AssertionError(message) | |
| 66 dependency = Dependency(self, package_el, repository_el) | |
| 67 dependencies.append(dependency) | |
| 68 | |
| 69 self.packages = packages | |
| 70 self.dependencies = dependencies | |
| 71 | |
| 72 def single_package(self): | |
| 73 return len(self.packages) == 1 | |
| 74 | |
| 75 def __repr__(self): | |
| 76 return "Dependencies[for_repo=%s]" % self.repo | |
| 77 | |
| 78 | |
| 79 class Repo(object): | |
| 80 | |
| 81 def __init__(self, **kwds): | |
| 82 for key, value in iteritems(kwds): | |
| 83 setattr(self, key, value) | |
| 84 | |
| 85 def recipe_base_name(self): | |
| 86 owner = self.owner.replace("-", "") | |
| 87 name = self.name | |
| 88 name = name.replace("_", "").replace("-", "") | |
| 89 base = "%s_%s" % (owner, name) | |
| 90 return base | |
| 91 | |
| 92 @staticmethod | |
| 93 def from_xml(elem): | |
| 94 tool_shed_url = elem.attrib.get("toolshed", None) | |
| 95 if tool_shed_url and ("testtoolshed" in tool_shed_url): | |
| 96 prefix = "testtoolshed" | |
| 97 else: | |
| 98 prefix = "toolshed" | |
| 99 prior = elem.attrib.get("prior_installation_required", False) | |
| 100 return Repo( | |
| 101 prefix=prefix, | |
| 102 name=elem.attrib["name"], | |
| 103 owner=elem.attrib["owner"], | |
| 104 tool_shed_url=tool_shed_url, | |
| 105 changeset_revision=elem.attrib.get("changeset_revision", None), | |
| 106 prior_installation_required=prior, | |
| 107 ) | |
| 108 | |
| 109 @staticmethod | |
| 110 def from_api(prefix, repo_json): | |
| 111 return Repo( | |
| 112 prefix=prefix, | |
| 113 name=repo_json["name"], | |
| 114 owner=repo_json["owner"], | |
| 115 tool_shed_url=TOOLSHED_MAP[prefix], | |
| 116 ) | |
| 117 | |
| 118 def get_file(self, path): | |
| 119 try: | |
| 120 url_template = "%s/repos/%s/%s/raw-file/tip/%s" | |
| 121 url = url_template % ( | |
| 122 self.tool_shed_url, | |
| 123 self.owner, | |
| 124 self.name, | |
| 125 path | |
| 126 ) | |
| 127 path, headers = urlretrieve(url) | |
| 128 return path | |
| 129 except Exception as e: | |
| 130 print(e) | |
| 131 return None | |
| 132 | |
| 133 def __repr__(self): | |
| 134 return "Repository[name=%s,owner=%s]" % (self.name, self.owner) | |
| 135 | |
| 136 | |
| 137 class Dependency(object): | |
| 138 | |
| 139 def __init__(self, dependencies, package_el, repository_el): | |
| 140 self.dependencies = dependencies | |
| 141 self.package_el = package_el | |
| 142 self.repository_el = repository_el | |
| 143 self.repo = Repo.from_xml(repository_el) | |
| 144 | |
| 145 def __repr__(self): | |
| 146 temp = "Dependency[package_name=%s,version=%s,dependent_package=%s]" | |
| 147 return temp % ( | |
| 148 self.package_el.attrib["name"], | |
| 149 self.package_el.attrib["version"], | |
| 150 self.repository_el.attrib["name"] | |
| 151 ) | |
| 152 | |
| 153 | |
| 154 class BasePackage(object): | |
| 155 | |
| 156 def __init__(self, dependencies, package_el, install_el, readme): | |
| 157 self.dependencies = dependencies | |
| 158 self.package_el = package_el | |
| 159 self.install_el = install_el | |
| 160 self.readme = readme | |
| 161 self.all_actions = self.get_all_actions() | |
| 162 self.no_arch_option = self.has_no_achitecture_install() | |
| 163 | |
| 164 def get_all_actions(self): | |
| 165 action_or_group = self.install_el[0] | |
| 166 parsed_actions = [] | |
| 167 if action_or_group.tag == "actions": | |
| 168 parsed_actions.append(self.parse_actions(action_or_group)) | |
| 169 elif action_or_group.tag == "actions_group": | |
| 170 actions_els = action_or_group.findall("actions") | |
| 171 assert actions_els is not None | |
| 172 for actions in actions_els: | |
| 173 parsed_actions.append(self.parse_actions(actions)) | |
| 174 action_els = action_or_group.findall("action") | |
| 175 assert action_els is not None | |
| 176 for action in action_els: | |
| 177 for parsed_a in parsed_actions: | |
| 178 parsed_a.actions.append(self.parse_action(action)) | |
| 179 return parsed_actions | |
| 180 | |
| 181 def has_no_achitecture_install(self): | |
| 182 all_actions = self.all_actions | |
| 183 if len(all_actions) < 2: | |
| 184 return False | |
| 185 else: | |
| 186 last_action = all_actions[-1] | |
| 187 return (not last_action.architecture) and (not last_action.os) | |
| 188 | |
| 189 def has_explicit_set_environments(self): | |
| 190 all_actions = self.all_actions | |
| 191 for actions in all_actions: | |
| 192 for action in actions.actions: | |
| 193 if action.explicit_variables: | |
| 194 return True | |
| 195 return False | |
| 196 | |
| 197 def has_multiple_set_environments(self): | |
| 198 all_actions = self.all_actions | |
| 199 for actions in all_actions: | |
| 200 count = 0 | |
| 201 for action in actions.actions: | |
| 202 if action.explicit_variables: | |
| 203 count += 1 | |
| 204 if count > 1: | |
| 205 return True | |
| 206 return False | |
| 207 | |
| 208 def parse_actions(self, actions): | |
| 209 os = actions.attrib.get("os", None) | |
| 210 architecture = actions.get("architecture", None) | |
| 211 action_els = actions.findall("action") | |
| 212 assert action_els is not None | |
| 213 parsed_actions = list(imap(self.parse_action, action_els)) | |
| 214 action_packages = [] | |
| 215 for package in actions.findall("package"): | |
| 216 action_packages.append(self.parse_action_package(package)) | |
| 217 return Actions(parsed_actions, os, architecture, action_packages) | |
| 218 | |
| 219 def parse_action_package(self, elem): | |
| 220 name = elem.attrib["name"] | |
| 221 version = elem.attrib["version"] | |
| 222 repo = Repo.from_xml(elem.find("repository")) | |
| 223 return ActionPackage(name, version, repo) | |
| 224 | |
| 225 def parse_action(self, action): | |
| 226 return BaseAction.from_elem(action, package=self) | |
| 227 | |
| 228 def __repr__(self): | |
| 229 actions = self.all_actions | |
| 230 parts = ( | |
| 231 self.package_el.attrib["name"], | |
| 232 self.package_el.attrib["version"], | |
| 233 self.dependencies, | |
| 234 actions | |
| 235 ) | |
| 236 template = "Install[name=%s,version=%s,dependencies=%s,actions=%s]" | |
| 237 return template % parts | |
| 238 | |
| 239 | |
| 240 class Actions(object): | |
| 241 | |
| 242 def __init__( | |
| 243 self, | |
| 244 actions, | |
| 245 os=None, | |
| 246 architecture=None, | |
| 247 action_packages=[] | |
| 248 ): | |
| 249 self.os = os | |
| 250 self.architecture = architecture | |
| 251 self.actions = actions or [] | |
| 252 self.action_packages = action_packages | |
| 253 | |
| 254 def first_download(self): | |
| 255 for action in self.actions: | |
| 256 if action.action_type in ["download_by_url", "download_file"]: | |
| 257 return action | |
| 258 return None | |
| 259 | |
| 260 def downloads(self): | |
| 261 actions = [] | |
| 262 for action in self.actions: | |
| 263 if action.action_type in ["download_by_url", "download_file"]: | |
| 264 actions.append(action) | |
| 265 return actions | |
| 266 | |
| 267 def __repr__(self): | |
| 268 platform = "" | |
| 269 if self.os or self.architecture: | |
| 270 platform = "os=%s,arch=%s," % (self.os, self.architecture) | |
| 271 return "Actions[%s%s]" % (platform, map(str, self.actions)) | |
| 272 | |
| 273 def _indent_extend(self, target, new_entries, indent=" "): | |
| 274 for line in new_entries: | |
| 275 target.append(indent + line) | |
| 276 | |
| 277 def to_bash(self): | |
| 278 # Use self.os.title() to match "Linux" or "Darwin" in bash where case matters: | |
| 279 if self.os and self.architecture: | |
| 280 condition = '("%s" == `uname`) && ("%s" == `arch`)' % (self.os.title(), self.architecture) | |
| 281 elif self.os: | |
| 282 condition = '"%s" == `uname`' % self.os.title() | |
| 283 elif self.architecture: | |
| 284 condition = '"%s" == `arch`' % self.architecture | |
| 285 else: | |
| 286 condition = None | |
| 287 | |
| 288 install_cmds = [] | |
| 289 env_cmds = [] | |
| 290 | |
| 291 if condition: | |
| 292 # Conditional actions block | |
| 293 install_cmds = [ | |
| 294 '#' + '-' * 60, | |
| 295 'if [[ $specifc_action_done == 0 && %s ]]' % condition, | |
| 296 'then', | |
| 297 ' echo "Platform-specific action for os=%s, arch=%s"' % (self.os, self.architecture)] | |
| 298 env_cmds = install_cmds[:] | |
| 299 # TODO - Refactor block indentation? | |
| 300 for action in self.actions: | |
| 301 i_cmds, e_cmds = action.to_bash() | |
| 302 self._indent_extend(install_cmds, i_cmds) | |
| 303 self._indent_extend(env_cmds, e_cmds) | |
| 304 # If we run the action, do not want to run any later actions! | |
| 305 install_cmds.extend([' specifc_action_done=1', 'fi']) | |
| 306 env_cmds.extend([' specifc_action_done=1', 'fi']) | |
| 307 else: | |
| 308 # Non-specific default action... | |
| 309 install_cmds = [ | |
| 310 '#' + '-' * 60, | |
| 311 'if [[ $specifc_action_done == 0 ]]', | |
| 312 'then', | |
| 313 ' echo "Non-platform-specific actions"'] | |
| 314 env_cmds = install_cmds[:] | |
| 315 for action in self.actions: | |
| 316 i_cmds, e_cmds = action.to_bash() | |
| 317 self._indent_extend(install_cmds, i_cmds) | |
| 318 self._indent_extend(env_cmds, e_cmds) | |
| 319 install_cmds.append('fi') | |
| 320 env_cmds.append('fi') | |
| 321 return install_cmds, env_cmds | |
| 322 | |
| 323 | |
| 324 class ActionPackage(object): | |
| 325 | |
| 326 def __init__(self, name, version, repo): | |
| 327 self.name = name | |
| 328 self.version = version | |
| 329 self.repo = repo | |
| 330 | |
| 331 | |
| 332 class BaseAction(object): | |
| 333 _keys = [] # type: List[str] | |
| 334 action_type = None # type: str | |
| 335 | |
| 336 def __repr__(self): | |
| 337 return "Action[type=%s]" % self.action_type | |
| 338 | |
| 339 def same_as(self, other): | |
| 340 if self._keys != other._keys: | |
| 341 return False | |
| 342 else: | |
| 343 for key in self._keys: | |
| 344 if getattr(self, key) != getattr(other, key): | |
| 345 return False | |
| 346 | |
| 347 return True | |
| 348 | |
| 349 def parse_action_repo(self, elem): | |
| 350 repo_elem = elem.find("repository") | |
| 351 repo = Repo.from_xml(repo_elem) | |
| 352 self.repo = repo | |
| 353 | |
| 354 def parse_package_elems(self, elem): | |
| 355 package_els = elem.findall("package") | |
| 356 packages = [] | |
| 357 assert package_els is not None | |
| 358 for package_el in package_els: | |
| 359 packages.append(package_el.text) | |
| 360 self.packages = packages | |
| 361 | |
| 362 @classmethod | |
| 363 def from_elem(cls, elem, package): | |
| 364 type = elem.attrib["type"] | |
| 365 action_class = actions_by_type[type] | |
| 366 return action_class(elem) | |
| 367 | |
| 368 def to_bash(self): | |
| 369 """Return lists of bash shell commands to execute this action. | |
| 370 | |
| 371 This method is be implemented by each sub-class, and will | |
| 372 return two list of strings (for ``dep_install.sh`` and | |
| 373 ``env.sh`` respectively). | |
| 374 """ | |
| 375 raise NotImplementedError("No to_bash defined for %r" % self) | |
| 376 | |
| 377 | |
| 378 def _tar_folders(filename): | |
| 379 with tarfile.open(filename, "r", errorlevel=0) as archive: | |
| 380 folders = set() | |
| 381 for i in archive.getmembers(): | |
| 382 if i.isdir(): | |
| 383 folders.add(i.name.rstrip("/")) | |
| 384 else: | |
| 385 folders.add(os.path.split(i.name)[0]) | |
| 386 return list(folders) | |
| 387 | |
| 388 | |
| 389 def _zip_folders(filename): | |
| 390 archive = zipfile.ZipFile(filename, "r") | |
| 391 return list(set(i.filename.rstrip("/") for i in archive.infolist() if i.filename.endswith("/"))) | |
| 392 | |
| 393 | |
| 394 def _common_prefix(folders): | |
| 395 common_prefix = "" | |
| 396 if len(folders) == 1: | |
| 397 common_prefix = list(folders)[0] | |
| 398 else: | |
| 399 common_prefix = os.path.commonprefix(folders) | |
| 400 assert not os.path.isabs(common_prefix), folders | |
| 401 return common_prefix | |
| 402 | |
| 403 | |
| 404 def _cache_download(url, filename, sha256sum=None): | |
| 405 """Returns local path to cached copy of URL using given filename.""" | |
| 406 cache = os.environ.get("DOWNLOAD_CACHE", "./download_cache/") | |
| 407 # TODO - expose this as a command line option | |
| 408 | |
| 409 if not os.path.isdir(cache): | |
| 410 os.mkdir(cache) | |
| 411 | |
| 412 local = os.path.join(cache, filename) | |
| 413 | |
| 414 if not os.path.isfile(local): | |
| 415 # Must download it... | |
| 416 try: | |
| 417 # TODO - log this nicely... | |
| 418 sys.stderr.write("Downloading %s to %r\n" % (url, local)) | |
| 419 urlretrieve(url, local) | |
| 420 except URLError: | |
| 421 # Most likely server is down, could be bad URL in XML action: | |
| 422 raise RuntimeError("Unable to download %s" % url) | |
| 423 except FTPErrors: | |
| 424 # Most likely server is down, could be bad URL in XML action: | |
| 425 raise RuntimeError("Unable to download %s" % url) | |
| 426 | |
| 427 # Verifying the checksum is slow, only do this on a fresh | |
| 428 # download. Assume locally cached files are already OK. | |
| 429 if sha256sum: | |
| 430 # TODO - log this nicely... | |
| 431 sys.stderr.write("Verifying checksum for %s\n" % filename) | |
| 432 filehash = subprocess.check_output(['shasum', '-a', '256', local])[0:64].strip() | |
| 433 filehash = unicodify(filehash) | |
| 434 if filehash != sha256sum: | |
| 435 raise RuntimeError("Checksum failure for %s, got %r but wanted %r" % (local, filehash, sha256sum)) | |
| 436 | |
| 437 return local | |
| 438 | |
| 439 | |
| 440 def _determine_compressed_file_folder(url, downloaded_filename, target_filename=None, sha256sum=None): | |
| 441 """Determine how to decompress the file & its directory structure. | |
| 442 | |
| 443 Returns a list of shell commands. Consider this example where the | |
| 444 folder to change to cannot be guessed from the tar-ball filename: | |
| 445 | |
| 446 $ curl -o "ncbi-blast-2.2.30+-ia32-linux.tar.gz" \ | |
| 447 "ftp://ftp.ncbi.nlm.nih.gov/blast/executables/blast+/2.2.30/ncbi-blast-2.2.30+-ia32-linux.tar.gz" | |
| 448 $ tar -zxvf ncbi-blast-2.2.30+-ia32-linux.tar.gz | |
| 449 $ cd ncbi-blast-2.2.30+ | |
| 450 | |
| 451 Here it would return: | |
| 452 | |
| 453 ['tar -zxvf ncbi-blast-2.2.30+-ia32-linux.tar.gz', 'cd ncbi-blast-2.2.30+'] | |
| 454 | |
| 455 If not cached, this function will download the file to the | |
| 456 $DOWNLOAD_CACHE folder, and then open it / decompress it in | |
| 457 order to find common folder prefix used. This will also verify | |
| 458 how to decompress the file, and the checksum if given. | |
| 459 """ | |
| 460 answer = [] | |
| 461 | |
| 462 local = _cache_download(url, downloaded_filename, sha256sum) | |
| 463 | |
| 464 if not target_filename: | |
| 465 target_filename = downloaded_filename | |
| 466 | |
| 467 if tarfile.is_tarfile(local): | |
| 468 folders = _tar_folders(local) | |
| 469 if target_filename.endswith((".tar.gz", ".tgz")): | |
| 470 answer.append('tar -zxvf %s' % target_filename) | |
| 471 elif target_filename.endswith(".tar.bz2"): | |
| 472 answer.append('tar -jxvf %s' % target_filename) | |
| 473 elif target_filename.endswith(".tar"): | |
| 474 answer.extend('tar -xvf %s' % target_filename) | |
| 475 else: | |
| 476 # Quite possibly this file doesn't need decompressing, | |
| 477 # but until we've tested lots of real world tool_dependencies.xml | |
| 478 # files I'd like to check these cases to confirm this. | |
| 479 raise NotImplementedError("How to decompress tar file %s?" % target_filename) | |
| 480 elif zipfile.is_zipfile(local): | |
| 481 if target_filename.endswith(".jar"): | |
| 482 # Do not decompress! | |
| 483 return answer | |
| 484 folders = _zip_folders(local) | |
| 485 answer.append('unzip %s' % target_filename) | |
| 486 elif target_filename.endswith(".dmg"): | |
| 487 # Do not decompress! | |
| 488 return answer | |
| 489 else: | |
| 490 # No compression? Leave as it is? | |
| 491 raise NotImplementedError("What kind of compression is %s using?" % local) | |
| 492 | |
| 493 common_prefix = _common_prefix(folders) | |
| 494 if common_prefix: | |
| 495 answer.append('cd "%s"' % common_prefix) | |
| 496 | |
| 497 return answer | |
| 498 | |
| 499 | |
| 500 def _commands_and_downloaded_file(url, target_filename=None, sha256sum=None): | |
| 501 # We preserve the filename from the URL in the cache. | |
| 502 # i.e. We do NOT use the target_filename in the cache. | |
| 503 # This because some Galaxy recipes normalise platform specific downloads | |
| 504 # to use a single target filename, which would therefore break checksums etc | |
| 505 # e.g. tests/data/repos/package_1/tool_dependencies.xml | |
| 506 downloaded_filename = os.path.split(url)[-1] | |
| 507 if "?" in downloaded_filename: | |
| 508 downloaded_filename = downloaded_filename[:downloaded_filename.index("?")] | |
| 509 if "#" in downloaded_filename: | |
| 510 downloaded_filename = downloaded_filename[:downloaded_filename.index("#")] | |
| 511 | |
| 512 if not target_filename: | |
| 513 target_filename = downloaded_filename | |
| 514 | |
| 515 # Curl is present on Mac OS X, can we assume it will be on Linux? | |
| 516 # Cannot assume that wget will be on Mac OS X. | |
| 517 answer = [ | |
| 518 'if [[ -f "%s" ]]' % target_filename, | |
| 519 'then', | |
| 520 ' echo "Reusing existing %s"' % target_filename, | |
| 521 'elif [[ -f "$DOWNLOAD_CACHE/%s" ]]' % downloaded_filename, | |
| 522 'then', | |
| 523 ' echo "Reusing cached %s"' % downloaded_filename, | |
| 524 ' cp "$DOWNLOAD_CACHE/%s" "%s"' % (downloaded_filename, target_filename), | |
| 525 'else', | |
| 526 ' echo "Downloading %s"' % downloaded_filename, | |
| 527 ' curl -L -o "$DOWNLOAD_CACHE/%s" "%s"' % (downloaded_filename, url), | |
| 528 ' cp "$DOWNLOAD_CACHE/%s" "%s"' % (downloaded_filename, target_filename), | |
| 529 ] | |
| 530 if sha256sum: | |
| 531 # This is inserted into the if-else for a fresh download only. | |
| 532 # Note double space between checksum and filename: | |
| 533 answer.append(' echo "%s %s" | shasum -a 256 -c -' % (sha256sum, target_filename)) | |
| 534 answer.append('fi') | |
| 535 | |
| 536 return answer, downloaded_filename | |
| 537 | |
| 538 | |
| 539 def _commands_to_download_and_extract(url, target_filename=None, sha256sum=None): | |
| 540 answer, downloaded_filename = _commands_and_downloaded_file(url, target_filename, sha256sum) | |
| 541 # Now should we unpack the tar-ball etc? | |
| 542 answer.extend(_determine_compressed_file_folder(url, downloaded_filename, target_filename, sha256sum)) | |
| 543 return answer, [] | |
| 544 | |
| 545 | |
| 546 class DownloadByUrlAction(BaseAction): | |
| 547 action_type = "download_by_url" | |
| 548 _keys = ["url"] | |
| 549 | |
| 550 def __init__(self, elem): | |
| 551 self.url = elem.text.strip() | |
| 552 assert self.url | |
| 553 self.sha256sum = elem.attrib.get("sha256sum", None) | |
| 554 self.target_filename = elem.attrib.get("target_filename", None) | |
| 555 | |
| 556 def to_bash(self): | |
| 557 # See class DownloadByUrl in Galaxy, | |
| 558 # lib/tool_shed/galaxy_install/tool_dependencies/recipe/step_handler.py | |
| 559 return _commands_to_download_and_extract(self.url, self.target_filename, self.sha256sum) | |
| 560 | |
| 561 | |
| 562 class DownloadFileAction(BaseAction): | |
| 563 action_type = "download_file" | |
| 564 _keys = ["url", "extract"] | |
| 565 | |
| 566 def __init__(self, elem): | |
| 567 self.url = elem.text.strip() | |
| 568 self.extract = asbool(elem.attrib.get("extract", False)) | |
| 569 self.sha256sum = elem.attrib.get("sha256sum", None) | |
| 570 self.target_filename = elem.attrib.get("target_filename", None) | |
| 571 | |
| 572 def to_bash(self): | |
| 573 if self.extract: | |
| 574 return _commands_to_download_and_extract(self.url, self.target_filename, self.sha256sum) | |
| 575 else: | |
| 576 commands, downloaded_file = _commands_and_downloaded_file(self.url, self.target_filename, self.sha256sum) | |
| 577 return commands, [] | |
| 578 | |
| 579 | |
| 580 class DownloadBinary(BaseAction): | |
| 581 action_type = "download_binary" | |
| 582 _keys = ["url_template", "target_directory"] | |
| 583 | |
| 584 def __init__(self, elem): | |
| 585 self.url_template = elem.text | |
| 586 assert self.url_template | |
| 587 self.target_directory = elem.get('target_directory', None) | |
| 588 | |
| 589 | |
| 590 class ShellCommandAction(BaseAction): | |
| 591 action_type = "shell_command" | |
| 592 _keys = ["command"] | |
| 593 | |
| 594 def __init__(self, elem): | |
| 595 self.command = elem.text | |
| 596 | |
| 597 def to_bash(self): | |
| 598 # Galaxy would run each action from the same temp | |
| 599 # working directory - possible that tool_dependencies.xml | |
| 600 # shell_command could change $PWD so reset this: | |
| 601 return ["pushd . > /dev/null", self.command, "popd > /dev/null"], [] | |
| 602 | |
| 603 | |
| 604 class TemplateShellCommandAction(BaseAction): | |
| 605 action_type = "template_command" | |
| 606 _keys = ["language", "command"] | |
| 607 | |
| 608 def __init__(self, elem): | |
| 609 self.command = elem.text | |
| 610 self.language = elem.get('language', 'cheetah').lower() | |
| 611 assert self.command | |
| 612 assert self.language == "cheetah" | |
| 613 | |
| 614 | |
| 615 class MoveFileAction(BaseAction): | |
| 616 action_type = "move_file" | |
| 617 _keys = ["move_file"] | |
| 618 | |
| 619 def __init__(self, elem): | |
| 620 self.source = elem.find("source").text | |
| 621 self.destination = elem.find("destination").text | |
| 622 | |
| 623 def to_bash(self): | |
| 624 return ["mv %s %s" % (self.source, self.destination)], [] | |
| 625 | |
| 626 | |
| 627 class MoveDirectoryFilesAction(BaseAction): | |
| 628 action_type = "move_directory_files" | |
| 629 _keys = ["source_directory", "destination_directory"] | |
| 630 | |
| 631 def __init__(self, elem): | |
| 632 source_directory = elem.find("source_directory").text | |
| 633 destination_directory = elem.find("destination_directory").text | |
| 634 self.source_directory = source_directory | |
| 635 self.destination_directory = destination_directory | |
| 636 | |
| 637 def to_bash(self): | |
| 638 return ["mv %s/* %s/" % (self.source_directory, self.destination_directory)], [] | |
| 639 | |
| 640 | |
| 641 class SetEnvironmentAction(BaseAction): | |
| 642 action_type = "set_environment" | |
| 643 _keys = ["variables"] | |
| 644 | |
| 645 def __init__(self, elem): | |
| 646 variables = [] | |
| 647 var_els = elem.findall("environment_variable") | |
| 648 assert var_els is not None | |
| 649 for ev_elem in var_els: | |
| 650 var = SetVariable(ev_elem) | |
| 651 variables.append(var) | |
| 652 self.variables = variables | |
| 653 assert self.variables | |
| 654 | |
| 655 def to_bash(self): | |
| 656 answer = [] | |
| 657 for var in self.variables: | |
| 658 # Expand $INSTALL_DIR here? | |
| 659 if var.action == "set_to": | |
| 660 answer.append('export %s=%s' % (var.name, var.raw_value)) | |
| 661 elif var.action == "prepend_to": | |
| 662 answer.append('export %s=%s:$%s' % (var.name, var.raw_value, var.name)) | |
| 663 elif var.action == "append_to": | |
| 664 answer.append('export %s=$%s:%s' % (var.name, var.name, var.raw_value)) | |
| 665 else: | |
| 666 raise ValueError("Undefined environment variable action %r" % var.action) | |
| 667 return answer, answer # Actions needed in env.sh here! | |
| 668 | |
| 669 | |
| 670 class ChmodAction(BaseAction): | |
| 671 action_type = "chmod" | |
| 672 _keys = ["mods"] | |
| 673 | |
| 674 def __init__(self, elem): | |
| 675 mods = [] | |
| 676 file_els = elem.findall("file") | |
| 677 assert file_els is not None | |
| 678 for mod_elem in file_els: | |
| 679 mod = {} | |
| 680 mod["mode"] = mod_elem.attrib["mode"] | |
| 681 mod["target"] = mod_elem.text | |
| 682 mods.append(mod) | |
| 683 self.mods = mods | |
| 684 assert self.mods | |
| 685 | |
| 686 def to_bash(self): | |
| 687 return ["chmod %s %s" % (m["mode"], m["target"]) for m in self.mods], [] | |
| 688 | |
| 689 | |
| 690 class MakeInstallAction(BaseAction): | |
| 691 action_type = "make_install" | |
| 692 _keys = [] # type: List[str] | |
| 693 | |
| 694 def __init__(self, elem): | |
| 695 pass | |
| 696 | |
| 697 def to_bash(self): | |
| 698 return ["make install"], [] | |
| 699 | |
| 700 | |
| 701 class AutoconfAction(BaseAction): | |
| 702 action_type = "autoconf" | |
| 703 _keys = ["options"] | |
| 704 | |
| 705 def __init__(self, elem): | |
| 706 self.options = elem.text | |
| 707 | |
| 708 def to_bash(self): | |
| 709 if self.options: | |
| 710 raise NotImplementedError("Options with action autoconf not implemented yet.") | |
| 711 return ['./configure', 'make', 'make install'], [] | |
| 712 | |
| 713 | |
| 714 class ChangeDirectoryAction(BaseAction): | |
| 715 action_type = "change_directory" | |
| 716 _keys = ["directory"] | |
| 717 | |
| 718 def __init__(self, elem): | |
| 719 self.directory = elem.text | |
| 720 assert self.directory | |
| 721 | |
| 722 def to_bash(self): | |
| 723 return ["cd %s" % self.directory], [] | |
| 724 | |
| 725 | |
| 726 class MakeDirectoryAction(BaseAction): | |
| 727 action_type = "make_directory" | |
| 728 _keys = ["directory"] | |
| 729 | |
| 730 def __init__(self, elem): | |
| 731 self.directory = elem.text | |
| 732 | |
| 733 def to_bash(self): | |
| 734 return ["mkdir -p %s" % self.directory], [] | |
| 735 | |
| 736 | |
| 737 class SetupPerlEnvironmentAction(BaseAction): | |
| 738 action_type = "setup_perl_environment" | |
| 739 _keys = ["repo", "packages"] | |
| 740 | |
| 741 def __init__(self, elem): | |
| 742 self.parse_action_repo(elem) | |
| 743 self.parse_package_elems(elem) | |
| 744 | |
| 745 | |
| 746 class SetupRubyEnvironmentAction(BaseAction): | |
| 747 action_type = "setup_ruby_environment" | |
| 748 _keys = ["repo", "packages"] | |
| 749 | |
| 750 def __init__(self, elem): | |
| 751 self.parse_action_repo(elem) | |
| 752 self.parse_package_elems(elem) | |
| 753 | |
| 754 | |
| 755 class SetupPythonEnvironmentAction(BaseAction): | |
| 756 action_type = "setup_python_environment" | |
| 757 _keys = ["repo", "packages"] | |
| 758 | |
| 759 def __init__(self, elem): | |
| 760 self.parse_action_repo(elem) | |
| 761 self.parse_package_elems(elem) | |
| 762 | |
| 763 | |
| 764 class SetupVirtualenvAction(BaseAction): | |
| 765 action_type = "setup_virtualenv" | |
| 766 _keys = ["use_requirements_file", "python", "requirements"] | |
| 767 | |
| 768 def __init__(self, elem): | |
| 769 use_reqs = elem.attrib.get("use_requirements_file", "True") | |
| 770 self.use_requirements_file = asbool(use_reqs) | |
| 771 self.python = elem.get('python', 'python') | |
| 772 self.requirements = elem.text or 'requirements.txt' | |
| 773 | |
| 774 | |
| 775 class SetupREnvironmentAction(BaseAction): | |
| 776 action_type = "setup_r_environment" | |
| 777 _keys = ["repo", "packages"] | |
| 778 | |
| 779 def __init__(self, elem): | |
| 780 self.parse_action_repo(elem) | |
| 781 self.parse_package_elems(elem) | |
| 782 | |
| 783 | |
| 784 class SetEnvironmentForInstallAction(BaseAction): | |
| 785 action_type = "set_environment_for_install" | |
| 786 | |
| 787 def __init__(self, elem): | |
| 788 pass | |
| 789 | |
| 790 def to_bash(self): | |
| 791 # TODO - How could we resolve/check the dependencies? | |
| 792 return ['echo "WARNING: Assuming packages already installed!"'], [] | |
| 793 | |
| 794 | |
| 795 class SetVariable(object): | |
| 796 | |
| 797 def __init__(self, elem): | |
| 798 self.action = elem.attrib["action"] | |
| 799 self.name = elem.attrib["name"] | |
| 800 self.raw_value = elem.text | |
| 801 | |
| 802 | |
| 803 truthy = frozenset(['true', 'yes', 'on', 'y', 't', '1']) | |
| 804 falsy = frozenset(['false', 'no', 'off', 'n', 'f', '0']) | |
| 805 | |
| 806 | |
| 807 def asbool(obj): | |
| 808 if isinstance(obj, string_types): | |
| 809 obj = obj.strip().lower() | |
| 810 if obj in truthy: | |
| 811 return True | |
| 812 elif obj in falsy: | |
| 813 return False | |
| 814 else: | |
| 815 raise ValueError("String is not true/false: %r" % obj) | |
| 816 return bool(obj) | |
| 817 | |
| 818 | |
| 819 action_classes = [ | |
| 820 DownloadByUrlAction, | |
| 821 DownloadFileAction, | |
| 822 DownloadBinary, | |
| 823 ShellCommandAction, | |
| 824 TemplateShellCommandAction, | |
| 825 MoveFileAction, | |
| 826 MoveDirectoryFilesAction, | |
| 827 SetEnvironmentAction, | |
| 828 ChmodAction, | |
| 829 MakeInstallAction, | |
| 830 AutoconfAction, | |
| 831 ChangeDirectoryAction, | |
| 832 MakeDirectoryAction, | |
| 833 SetupPerlEnvironmentAction, | |
| 834 SetupRubyEnvironmentAction, | |
| 835 SetupPythonEnvironmentAction, | |
| 836 SetupVirtualenvAction, | |
| 837 SetupREnvironmentAction, | |
| 838 SetEnvironmentForInstallAction, | |
| 839 ] | |
| 840 | |
| 841 actions_by_type = dict(map(lambda c: (c.action_type, c), action_classes)) |
