comparison env/lib/python3.9/site-packages/planemo/context.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 """Define the context around Planemo computation.
2
3 Abstractions for cross cutting concerns (logging, workspace management,
4 etc.).
5 """
6 import abc
7 import logging.config
8 import os
9 import shutil
10 import sys
11 import traceback
12 from urllib.request import urlopen
13
14 from planemo.config import read_global_config
15
16
17 class PlanemoContextInterface(metaclass=abc.ABCMeta):
18 """Interface Planemo operations use to access workspace context."""
19
20 @abc.abstractmethod
21 def set_option_source(self, param_name, option_source, force=False):
22 """Specify how an option was set."""
23
24 @abc.abstractmethod
25 def get_option_source(self, param_name):
26 """Return OptionSource value indicating how the option was set."""
27
28 @abc.abstractproperty
29 def global_config(self):
30 """Read Planemo's global configuration."""
31
32 @abc.abstractmethod
33 def log(self, msg, *args):
34 """Log a message."""
35
36 @abc.abstractmethod
37 def vlog(self, msg, *args, **kwds):
38 """Log a message only if verbose is enabled."""
39
40 @abc.abstractproperty
41 def workspace(self):
42 """Create and return Planemo's workspace."""
43
44 @abc.abstractproperty
45 def galaxy_profiles_directory(self):
46 """Create a return a directory for storing Galaxy profiles."""
47
48 @abc.abstractmethod
49 def cache_download(self, url, destination):
50 """Use workspace to cache download of ``url``."""
51
52
53 class PlanemoContext(PlanemoContextInterface):
54 """Implementation of ``PlanemoContextInterface``"""
55
56 def __init__(self):
57 """Construct a Context object using execution environment."""
58 self.home = os.getcwd()
59 self._global_config = None
60 # Will be set by planemo CLI driver
61 self.verbose = False
62 self.planemo_config = None
63 self.planemo_directory = None
64 self.option_source = {}
65
66 def set_option_source(self, param_name, option_source, force=False):
67 """Specify how an option was set."""
68 if not force:
69 assert param_name not in self.option_source, "No option source for [%s]" % param_name
70 self.option_source[param_name] = option_source
71
72 def get_option_source(self, param_name):
73 """Return OptionSource value indicating how the option was set."""
74 assert param_name in self.option_source, "No option source for [%s]" % param_name
75 return self.option_source[param_name]
76
77 @property
78 def global_config(self):
79 """Read Planemo's global configuration.
80
81 As defined most simply by ~/.planemo.yml.
82 """
83 if self._global_config is None:
84 self._global_config = read_global_config(self.planemo_config)
85 return self._global_config or {}
86
87 def log(self, msg, *args):
88 """Log a message."""
89 if args:
90 msg %= args
91 self._log_message(msg)
92
93 def vlog(self, msg, *args, **kwds):
94 """Log a message only if verbose is enabled."""
95 if self.verbose:
96 self.log(msg, *args)
97 if kwds.get("exception", False):
98 traceback.print_exc(file=sys.stderr)
99
100 @property
101 def workspace(self):
102 """Create and return Planemo's workspace.
103
104 By default this will be ``~/.planemo``.
105 """
106 if not self.planemo_directory:
107 raise Exception("No planemo workspace defined.")
108 workspace = self.planemo_directory
109 return self._ensure_directory(workspace, "workspace")
110
111 @property
112 def galaxy_profiles_directory(self):
113 """Create a return a directory for storing Galaxy profiles."""
114 path = os.path.join(self.workspace, "profiles")
115 return self._ensure_directory(path, "Galaxy profiles")
116
117 def cache_download(self, url, destination):
118 """Use workspace to cache download of ``url``."""
119 cache = os.path.join(self.workspace, "cache")
120 if not os.path.exists(cache):
121 os.makedirs(cache)
122 filename = os.path.basename(url)
123 cache_destination = os.path.join(cache, filename)
124 if not os.path.exists(cache_destination):
125 with urlopen(url) as fh:
126 content = fh.read()
127 if len(content) == 0:
128 raise Exception("Failed to download [%s]." % url)
129 with open(cache_destination, "wb") as f:
130 f.write(content)
131
132 shutil.copy(cache_destination, destination)
133
134 def _ensure_directory(self, path, name):
135 if not os.path.exists(path):
136 os.makedirs(path)
137 if not os.path.isdir(path):
138 template = "Planemo %s directory [%s] unavailable."
139 message = template % (name, path)
140 raise Exception(message)
141 return path
142
143 def _log_message(self, message):
144 """Extension point for overriding I/O."""
145 print(message)
146
147
148 def configure_standard_planemo_logging(verbose):
149 """Configure Planemo's default logging rules."""
150 logging_config = {
151 'version': 1,
152 'disable_existing_loggers': False,
153 'formatters': {
154 'verbose': {
155 'format': '%(name)s %(levelname)s %(asctime)s: %(message)s'
156 },
157 'simple': {
158 'format': '%(name)s %(levelname)s: %(message)s'
159 },
160 },
161 'handlers': {
162 'console': {
163 'level': 'DEBUG',
164 'class': 'logging.StreamHandler',
165 'formatter': 'simple' if not verbose else 'verbose'
166 },
167 },
168 'loggers': {
169 # Suppress CWL is beta warning, for Planemo purposes - it is absolutely not.
170 'galaxy.tools.parser.factory': {
171 'handlers': ['console'],
172 'propagate': False,
173 'level': 'ERROR' if not verbose else "DEBUG",
174 },
175 'galaxy.tools.deps.commands': {
176 'handlers': ['console'],
177 'propagate': False,
178 'level': 'ERROR' if not verbose else "DEBUG",
179 },
180 'galaxy': {
181 'handlers': ['console'],
182 'propagate': False,
183 'level': 'INFO' if not verbose else "DEBUG",
184 },
185 # @jmchilton
186 # I'm fixing up Planemo's lint functionality for CWL and I keep seeing this for the
187 # schema metadata stuff (e.g. in the workflows repo). "rdflib.term WARNING:
188 # http://schema.org/docs/!DOCTYPE html does not look like a valid URI, trying to
189 # serialize this will break.". I'm going to suppress this warning I think, or are the
190 # examples wrong and should declare their namespaces differently in some way?
191 # @mr-c
192 # That particular warning is worth suppressing. A PR to silence it permanently would be very welcome!
193 # https://github.com/RDFLib/rdflib/blob/master/rdflib/term.py#L225
194 'rdflib.term': {
195 'handlers': ['console'],
196 'propagate': False,
197 'level': 'ERROR' if not verbose else "DEBUG",
198 }
199 },
200 'root': {
201 'handlers': ['console'],
202 'propagate': False,
203 'level': 'WARNING' if not verbose else "DEBUG",
204 }
205 }
206 logging.config.dictConfig(logging_config)
207
208
209 __all__ = (
210 'configure_standard_planemo_logging',
211 'PlanemoContextInterface',
212 'PlanemoContext',
213 )