|
9 | 9 | from copy import deepcopy
|
10 | 10 | from glob import glob
|
11 | 11 | import os
|
12 |
| -import getpass |
13 | 12 | import shutil
|
14 |
| -from socket import gethostname |
15 | 13 | import sys
|
16 |
| -import uuid |
17 |
| -from time import strftime, sleep, time |
18 |
| -from traceback import format_exception, format_exc |
| 14 | +from time import sleep, time |
| 15 | +from traceback import format_exc |
19 | 16 |
|
20 | 17 | import numpy as np
|
21 | 18 | import scipy.sparse as ssp
|
22 | 19 |
|
23 |
| - |
24 | 20 | from ... import logging
|
25 |
| -from ...utils.filemanip import savepkl, loadpkl, crash2txt |
| 21 | +from ...utils.filemanip import loadpkl |
26 | 22 | from ...utils.misc import str2bool
|
27 | 23 | from ..engine.utils import (nx, dfs_preorder, topological_sort)
|
28 | 24 | from ..engine import MapNode
|
29 |
| - |
| 25 | +from .tools import report_crash, report_nodes_not_run, create_pyscript |
30 | 26 |
|
31 | 27 | logger = logging.getLogger('workflow')
|
32 |
| -iflogger = logging.getLogger('interface') |
33 | 28 |
|
34 | 29 |
|
35 |
| -def report_crash(node, traceback=None, hostname=None): |
36 |
| - """Writes crash related information to a file |
37 |
| - """ |
38 |
| - name = node._id |
39 |
| - if node.result and hasattr(node.result, 'runtime') and \ |
40 |
| - node.result.runtime: |
41 |
| - if isinstance(node.result.runtime, list): |
42 |
| - host = node.result.runtime[0].hostname |
43 |
| - else: |
44 |
| - host = node.result.runtime.hostname |
45 |
| - else: |
46 |
| - if hostname: |
47 |
| - host = hostname |
48 |
| - else: |
49 |
| - host = gethostname() |
50 |
| - message = ['Node %s failed to run on host %s.' % (name, |
51 |
| - host)] |
52 |
| - logger.error(message) |
53 |
| - if not traceback: |
54 |
| - exc_type, exc_value, exc_traceback = sys.exc_info() |
55 |
| - traceback = format_exception(exc_type, |
56 |
| - exc_value, |
57 |
| - exc_traceback) |
58 |
| - timeofcrash = strftime('%Y%m%d-%H%M%S') |
59 |
| - login_name = getpass.getuser() |
60 |
| - crashfile = 'crash-%s-%s-%s-%s' % (timeofcrash, |
61 |
| - login_name, |
62 |
| - name, |
63 |
| - str(uuid.uuid4())) |
64 |
| - crashdir = node.config['execution']['crashdump_dir'] |
65 |
| - if crashdir is None: |
66 |
| - crashdir = os.getcwd() |
67 |
| - if not os.path.exists(crashdir): |
68 |
| - os.makedirs(crashdir) |
69 |
| - crashfile = os.path.join(crashdir, crashfile) |
70 |
| - if node.config['execution']['crashfile_format'].lower() in ['text', 'txt']: |
71 |
| - crashfile += '.txt' |
72 |
| - else: |
73 |
| - crashfile += '.pklz' |
74 |
| - logger.info('Saving crash info to %s' % crashfile) |
75 |
| - logger.info(''.join(traceback)) |
76 |
| - if node.config['execution']['crashfile_format'].lower() in ['text', 'txt']: |
77 |
| - crash2txt(crashfile, dict(node=node, traceback=traceback)) |
78 |
| - else: |
79 |
| - savepkl(crashfile, dict(node=node, traceback=traceback)) |
80 |
| - return crashfile |
81 |
| - |
82 |
| - |
83 |
| -def report_nodes_not_run(notrun): |
84 |
| - """List nodes that crashed with crashfile info |
85 |
| -
|
86 |
| - Optionally displays dependent nodes that weren't executed as a result of |
87 |
| - the crash. |
| 30 | +class PluginBase(object): |
88 | 31 | """
|
89 |
| - if notrun: |
90 |
| - logger.info("***********************************") |
91 |
| - for info in notrun: |
92 |
| - logger.error("could not run node: %s" % |
93 |
| - '.'.join((info['node']._hierarchy, |
94 |
| - info['node']._id))) |
95 |
| - logger.info("crashfile: %s" % info['crashfile']) |
96 |
| - logger.debug("The following dependent nodes were not run") |
97 |
| - for subnode in info['dependents']: |
98 |
| - logger.debug(subnode._id) |
99 |
| - logger.info("***********************************") |
100 |
| - raise RuntimeError(('Workflow did not execute cleanly. ' |
101 |
| - 'Check log for details')) |
102 |
| - |
103 |
| - |
104 |
| -def create_pyscript(node, updatehash=False, store_exception=True): |
105 |
| - # pickle node |
106 |
| - timestamp = strftime('%Y%m%d_%H%M%S') |
107 |
| - if node._hierarchy: |
108 |
| - suffix = '%s_%s_%s' % (timestamp, node._hierarchy, node._id) |
109 |
| - batch_dir = os.path.join(node.base_dir, |
110 |
| - node._hierarchy.split('.')[0], |
111 |
| - 'batch') |
112 |
| - else: |
113 |
| - suffix = '%s_%s' % (timestamp, node._id) |
114 |
| - batch_dir = os.path.join(node.base_dir, 'batch') |
115 |
| - if not os.path.exists(batch_dir): |
116 |
| - os.makedirs(batch_dir) |
117 |
| - pkl_file = os.path.join(batch_dir, 'node_%s.pklz' % suffix) |
118 |
| - savepkl(pkl_file, dict(node=node, updatehash=updatehash)) |
119 |
| - mpl_backend = node.config["execution"]["matplotlib_backend"] |
120 |
| - # create python script to load and trap exception |
121 |
| - cmdstr = """import os |
122 |
| -import sys |
| 32 | + Base class for plugins |
123 | 33 |
|
124 |
| -can_import_matplotlib = True #Silently allow matplotlib to be ignored |
125 |
| -try: |
126 |
| - import matplotlib |
127 |
| - matplotlib.use('%s') |
128 |
| -except ImportError: |
129 |
| - can_import_matplotlib = False |
130 |
| - pass |
131 |
| -
|
132 |
| -from nipype import config, logging |
133 |
| -from nipype.utils.filemanip import loadpkl, savepkl |
134 |
| -from socket import gethostname |
135 |
| -from traceback import format_exception |
136 |
| -info = None |
137 |
| -pklfile = '%s' |
138 |
| -batchdir = '%s' |
139 |
| -from nipype.utils.filemanip import loadpkl, savepkl |
140 |
| -try: |
141 |
| - if not sys.version_info < (2, 7): |
142 |
| - from collections import OrderedDict |
143 |
| - config_dict=%s |
144 |
| - config.update_config(config_dict) |
145 |
| - ## Only configure matplotlib if it was successfully imported, |
146 |
| - ## matplotlib is an optional component to nipype |
147 |
| - if can_import_matplotlib: |
148 |
| - config.update_matplotlib() |
149 |
| - logging.update_logging(config) |
150 |
| - traceback=None |
151 |
| - cwd = os.getcwd() |
152 |
| - info = loadpkl(pklfile) |
153 |
| - result = info['node'].run(updatehash=info['updatehash']) |
154 |
| -except Exception as e: |
155 |
| - etype, eval, etr = sys.exc_info() |
156 |
| - traceback = format_exception(etype,eval,etr) |
157 |
| - if info is None or not os.path.exists(info['node'].output_dir()): |
158 |
| - result = None |
159 |
| - resultsfile = os.path.join(batchdir, 'crashdump_%s.pklz') |
160 |
| - else: |
161 |
| - result = info['node'].result |
162 |
| - resultsfile = os.path.join(info['node'].output_dir(), |
163 |
| - 'result_%%s.pklz'%%info['node'].name) |
164 |
| -""" |
165 |
| - if store_exception: |
166 |
| - cmdstr += """ |
167 |
| - savepkl(resultsfile, dict(result=result, hostname=gethostname(), |
168 |
| - traceback=traceback)) |
169 |
| -""" |
170 |
| - else: |
171 |
| - cmdstr += """ |
172 |
| - if info is None: |
173 |
| - savepkl(resultsfile, dict(result=result, hostname=gethostname(), |
174 |
| - traceback=traceback)) |
175 |
| - else: |
176 |
| - from nipype.pipeline.plugins.base import report_crash |
177 |
| - report_crash(info['node'], traceback, gethostname()) |
178 |
| - raise Exception(e) |
179 |
| -""" |
180 |
| - cmdstr = cmdstr % (mpl_backend, pkl_file, batch_dir, node.config, suffix) |
181 |
| - pyscript = os.path.join(batch_dir, 'pyscript_%s.py' % suffix) |
182 |
| - with open(pyscript, 'wt') as fp: |
183 |
| - fp.writelines(cmdstr) |
184 |
| - return pyscript |
| 34 | + Execution plugin API |
| 35 | + ==================== |
185 | 36 |
|
| 37 | + Current status:: |
186 | 38 |
|
187 |
| -class PluginBase(object): |
188 |
| - """Base class for plugins""" |
| 39 | + class plugin_runner(PluginBase): |
| 40 | +
|
| 41 | + def run(graph, config, updatehash) |
| 42 | +
|
| 43 | + """ |
189 | 44 |
|
190 | 45 | def __init__(self, plugin_args=None):
|
191 | 46 | if plugin_args is None:
|
192 | 47 | plugin_args = {}
|
193 | 48 | self.plugin_args = plugin_args
|
| 49 | + self._config = None |
194 | 50 |
|
195 | 51 | self._status_callback = plugin_args.get('status_callback')
|
196 | 52 | return
|
@@ -226,11 +82,17 @@ def __init__(self, plugin_args=None):
|
226 | 82 | self.proc_pending = None
|
227 | 83 | self.max_jobs = self.plugin_args.get('max_jobs', np.inf)
|
228 | 84 |
|
| 85 | + def _prerun_check(self, graph): |
| 86 | + """Stub.""" |
| 87 | + |
229 | 88 | def run(self, graph, config, updatehash=False):
|
230 |
| - """Executes a pre-defined pipeline using distributed approaches |
| 89 | + """ |
| 90 | + Executes a pre-defined pipeline using distributed approaches |
231 | 91 | """
|
232 | 92 | logger.info("Running in parallel.")
|
233 | 93 | self._config = config
|
| 94 | + |
| 95 | + self._prerun_check(graph) |
234 | 96 | # Generate appropriate structures for worker-manager model
|
235 | 97 | self._generate_dependency_list(graph)
|
236 | 98 | self.pending_tasks = []
|
@@ -297,7 +159,12 @@ def _submit_job(self, node, updatehash=False):
|
297 | 159 | raise NotImplementedError
|
298 | 160 |
|
299 | 161 | def _report_crash(self, node, result=None):
|
300 |
| - raise NotImplementedError |
| 162 | + tb = None |
| 163 | + if result is not None: |
| 164 | + node._result = getattr(result, 'result') |
| 165 | + tb = getattr(result, 'traceback') |
| 166 | + node._traceback = tb |
| 167 | + return report_crash(node, traceback=tb) |
301 | 168 |
|
302 | 169 | def _clear_task(self, taskid):
|
303 | 170 | raise NotImplementedError
|
@@ -584,15 +451,6 @@ def _submit_job(self, node, updatehash=False):
|
584 | 451 | fp.writelines(batchscript)
|
585 | 452 | return self._submit_batchtask(batchscriptfile, node)
|
586 | 453 |
|
587 |
| - def _report_crash(self, node, result=None): |
588 |
| - if result and result['traceback']: |
589 |
| - node._result = result['result'] |
590 |
| - node._traceback = result['traceback'] |
591 |
| - return report_crash(node, |
592 |
| - traceback=result['traceback']) |
593 |
| - else: |
594 |
| - return report_crash(node) |
595 |
| - |
596 | 454 | def _clear_task(self, taskid):
|
597 | 455 | del self._pending[taskid]
|
598 | 456 |
|
|
0 commit comments