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))