Skip to content

Commit 2018456

Browse files
author
Peter Amstutz
authored
Use exception chaining & str(exc) (#1136)
* Use exception chaining & str(exc) Use exception chaining (via raise_from which is a stub on python2) so that the entire traceback is captured by the exception. Explicitly coerce exceptions to str() in error messages so we only get the exception message and not the entire representation (stacktrace etc). This should mostly bring py2/py3 error logging behavior back into sync. * Add future to dependencies. * Fix raise_from type annotation to NoReturn * Bump schema salad dependency
1 parent 4c905b8 commit 2018456

File tree

13 files changed

+106
-35
lines changed

13 files changed

+106
-35
lines changed

cwltool/builder.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from schema_salad.sourceline import SourceLine
1919
from schema_salad.ref_resolver import uri_file_path
2020
from six import iteritems, string_types
21+
from future.utils import raise_from
2122
from typing import IO
2223
from typing_extensions import (TYPE_CHECKING, # pylint: disable=unused-import
2324
Text, Type)
@@ -348,9 +349,9 @@ def _capture_files(f):
348349
check_format(datum, self.do_eval(schema["format"]),
349350
self.formatgraph)
350351
except validate.ValidationException as ve:
351-
raise WorkflowException(
352+
raise_from(WorkflowException(
352353
"Expected value of '%s' to have format %s but\n "
353-
" %s" % (schema["name"], schema["format"], ve))
354+
" %s" % (schema["name"], schema["format"], ve)), ve)
354355

355356
visit_class(datum.get("secondaryFiles", []), ("File", "Directory"), _capture_files)
356357

cwltool/command_line_tool.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
from schema_salad.ref_resolver import file_uri, uri_file_path
2525
from schema_salad.sourceline import SourceLine
2626
from six import string_types
27+
from future.utils import raise_from
2728

2829
from six.moves import map, urllib
2930
from typing_extensions import (TYPE_CHECKING, # pylint: disable=unused-import
@@ -108,7 +109,7 @@ def run(self,
108109
self.output_callback(ev, "success")
109110
except Exception as err:
110111
_logger.warning(u"Failed to evaluate expression:\n%s",
111-
err, exc_info=runtimeContext.debug)
112+
Text(err), exc_info=runtimeContext.debug)
112113
self.output_callback({}, "permanentFail")
113114

114115
def job(self,
@@ -649,9 +650,9 @@ def makeWorkflowException(msg):
649650
adjustFileObjs(ret, builder.mutation_manager.set_generation)
650651
return ret if ret is not None else {}
651652
except validate.ValidationException as e:
652-
raise WorkflowException(
653+
raise_from(WorkflowException(
653654
"Error validating output record. " + Text(e) + "\n in "
654-
+ json_dumps(ret, indent=4))
655+
+ json_dumps(ret, indent=4)), e)
655656
finally:
656657
if builder.mutation_manager and readers:
657658
for r in readers.values():

cwltool/executors.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import psutil
1212
from six import string_types, with_metaclass
1313
from typing_extensions import Text # pylint: disable=unused-import
14+
from future.utils import raise_from
1415
from schema_salad.validate import ValidationException
1516

1617
from .builder import Builder # pylint: disable=unused-import
@@ -189,7 +190,7 @@ def run_jobs(self,
189190
raise
190191
except Exception as err:
191192
logger.exception("Got workflow error")
192-
raise WorkflowException(Text(err))
193+
raise_from(WorkflowException(Text(err)), err)
193194

194195

195196
class MultithreadedJobExecutor(JobExecutor):

cwltool/expression.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import six
1010
from six import string_types, u
11+
from future.utils import raise_from
1112
from typing_extensions import Text # pylint: disable=unused-import
1213
# move to a regular typing import when Python 3.3-3.6 is no longer supported
1314

@@ -143,16 +144,16 @@ def next_seg(parsed_string, remaining_string, current_value): # type: (Text, Te
143144
try:
144145
key = int(next_segment_str[1:-1])
145146
except ValueError as v:
146-
raise WorkflowException(u(str(v)))
147+
raise_from(WorkflowException(u(str(v))), v)
147148
if not isinstance(current_value, MutableSequence):
148149
raise WorkflowException("%s is a %s, cannot index on int '%s'" % (parsed_string, type(current_value).__name__, key))
149150
if key >= len(current_value):
150151
raise WorkflowException("%s list index %i out of range" % (parsed_string, key))
151152

152153
try:
153154
return next_seg(parsed_string + remaining_string, remaining_string[m.end(0):], current_value[key])
154-
except KeyError:
155-
raise WorkflowException("%s doesn't have property %s" % (parsed_string, key))
155+
except KeyError as e:
156+
raise_from(WorkflowException("%s doesn't have property %s" % (parsed_string, key)), e)
156157
else:
157158
return current_value
158159

@@ -295,6 +296,6 @@ def do_eval(ex, # type: Union[Text, Dict]
295296
strip_whitespace=strip_whitespace)
296297

297298
except Exception as e:
298-
raise WorkflowException("Expression evaluation error:\n%s" % e)
299+
raise_from(WorkflowException("Expression evaluation error:\n%s" % Text(e)), e)
299300
else:
300301
return ex

cwltool/job.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
from prov.model import PROV
2525
from schema_salad.sourceline import SourceLine
2626
from six import PY2, with_metaclass
27+
from future.utils import raise_from
2728
from typing_extensions import (TYPE_CHECKING, # pylint: disable=unused-import
2829
Text)
2930

@@ -344,14 +345,14 @@ def _execute(self,
344345
except OSError as e:
345346
if e.errno == 2:
346347
if runtime:
347-
_logger.error(u"'%s' not found: %s", runtime[0], e)
348+
_logger.error(u"'%s' not found: %s", runtime[0], Text(e))
348349
else:
349-
_logger.error(u"'%s' not found: %s", self.command_line[0], e)
350+
_logger.error(u"'%s' not found: %s", self.command_line[0], Text(e))
350351
else:
351352
_logger.exception(u"Exception while running job")
352353
processStatus = "permanentFail"
353354
except WorkflowException as err:
354-
_logger.error(u"[job %s] Job error:\n%s", self.name, err)
355+
_logger.error(u"[job %s] Job error:\n%s", self.name, Text(err))
355356
processStatus = "permanentFail"
356357
except Exception as e:
357358
_logger.exception(u"Exception while running job")
@@ -659,8 +660,8 @@ def run(self,
659660
container = "Singularity" if runtimeContext.singularity else "Docker"
660661
_logger.debug(u"%s error", container, exc_info=True)
661662
if docker_is_req:
662-
raise UnsupportedRequirement(
663-
"%s is required to run this tool: %s" % (container, err))
663+
raise_from(UnsupportedRequirement(
664+
"%s is required to run this tool: %s" % (container, Text(err))), err)
664665
else:
665666
raise WorkflowException(
666667
"{0} is not available for this tool, try "

cwltool/main.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -733,18 +733,18 @@ def my_represent_none(self, data): # pylint: disable=unused-argument
733733
return 0
734734

735735
except (validate.ValidationException) as exc:
736-
_logger.error(u"Tool definition failed validation:\n%s", exc,
736+
_logger.error(u"Tool definition failed validation:\n%s", Text(exc),
737737
exc_info=args.debug)
738738
return 1
739739
except (RuntimeError, WorkflowException) as exc:
740-
_logger.error(u"Tool definition failed initialization:\n%s", exc,
740+
_logger.error(u"Tool definition failed initialization:\n%s", Text(exc),
741741
exc_info=args.debug)
742742
return 1
743743
except Exception as exc:
744744
_logger.error(
745745
u"I'm sorry, I couldn't load this CWL file%s.\nThe error was: %s",
746746
try_again_msg,
747-
exc if not args.debug else "",
747+
Text(exc) if not args.debug else "",
748748
exc_info=args.debug)
749749
return 1
750750

@@ -770,7 +770,7 @@ def my_represent_none(self, data): # pylint: disable=unused-argument
770770
try:
771771
os.makedirs(os.path.dirname(getattr(runtimeContext, dirprefix)))
772772
except Exception as e:
773-
_logger.error("Failed to create directory: %s", e)
773+
_logger.error("Failed to create directory: %s", Text(e))
774774
return 1
775775

776776
if args.cachedir:
@@ -852,12 +852,12 @@ def loc_to_path(obj):
852852
return 0
853853

854854
except (validate.ValidationException) as exc:
855-
_logger.error(u"Input object failed validation:\n%s", exc,
855+
_logger.error(u"Input object failed validation:\n%s", Text(exc),
856856
exc_info=args.debug)
857857
return 1
858858
except UnsupportedRequirement as exc:
859859
_logger.error(
860-
u"Workflow or tool uses unsupported feature:\n%s", exc,
860+
u"Workflow or tool uses unsupported feature:\n%s", Text(exc),
861861
exc_info=args.debug)
862862
return 33
863863
except WorkflowException as exc:
@@ -867,7 +867,7 @@ def loc_to_path(obj):
867867
return 1
868868
except Exception as exc: # pylint: disable=broad-except
869869
_logger.error(
870-
u"Unhandled error%s:\n %s", try_again_msg, exc, exc_info=args.debug)
870+
u"Unhandled error%s:\n %s", try_again_msg, Text(exc), exc_info=args.debug)
871871
return 1
872872

873873
finally:

cwltool/process.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
from ruamel.yaml.comments import CommentedMap, CommentedSeq
2525
from six import PY3, iteritems, itervalues, string_types, with_metaclass
2626
from six.moves import urllib
27+
from future.utils import raise_from
2728
from typing_extensions import (TYPE_CHECKING, # pylint: disable=unused-import
2829
Text)
2930
from schema_salad import schema, validate
@@ -556,7 +557,7 @@ def __init__(self,
556557
_logger.error(
557558
"Failed to read options file %s",
558559
loadingContext.js_hint_options_file)
559-
raise err
560+
raise
560561
else:
561562
validate_js_options = None
562563
if self.doc_schema is not None:
@@ -654,7 +655,7 @@ def inc(d): # type: (List[int]) -> None
654655
""" % (filecount[0], k))))
655656

656657
except (validate.ValidationException, WorkflowException) as err:
657-
raise WorkflowException("Invalid job input record:\n" + Text(err))
658+
raise_from(WorkflowException("Invalid job input record:\n" + Text(err)), err)
658659

659660
files = [] # type: List[Dict[Text, Text]]
660661
bindings = CommentedSeq()

cwltool/sandboxjs.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from typing import Any, Dict, List, Tuple, Union
1313

1414
import six
15+
from future.utils import raise_from
1516
from pkg_resources import resource_stream
1617
from typing_extensions import Text # pylint: disable=unused-import
1718
# move to a regular typing import when Python 3.3-3.6 is no longer supported
@@ -374,6 +375,6 @@ def fn_linenum(): # type: () -> Text
374375
try:
375376
return json.loads(stdout)
376377
except ValueError as err:
377-
raise JavascriptException(
378+
raise_from(JavascriptException(
378379
u"{}\nscript was:\n{}\nstdout was: '{}'\nstderr was: '{}'\n".format(
379-
err, fn_linenum(), stdout, stderr))
380+
err, fn_linenum(), stdout, stderr)), err)

cwltool/workflow.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from schema_salad.sourceline import SourceLine
1818
from six import string_types, iteritems
1919
from six.moves import range
20+
from future.utils import raise_from
2021
from typing_extensions import Text # pylint: disable=unused-import
2122
# move to a regular typing import when Python 3.3-3.6 is no longer supported
2223

@@ -249,7 +250,7 @@ def do_output_callback(self, final_output_callback):
249250
"outputSource", incomplete=True)
250251
except WorkflowException as err:
251252
_logger.error(
252-
u"[%s] Cannot collect workflow output: %s", self.name, err)
253+
u"[%s] Cannot collect workflow output: %s", self.name, Text(err))
253254
self.processStatus = "permanentFail"
254255
if self.prov_obj and self.parent_wf \
255256
and self.prov_obj.workflow_run_uri != self.parent_wf.workflow_run_uri:
@@ -464,7 +465,7 @@ def job(self,
464465
step.iterable = self.try_make_job(
465466
step, output_callback, runtimeContext)
466467
except WorkflowException as exc:
467-
_logger.error(u"[%s] Cannot make job: %s", step.name, exc)
468+
_logger.error(u"[%s] Cannot make job: %s", step.name, Text(exc))
468469
_logger.debug("", exc_info=True)
469470
self.processStatus = "permanentFail"
470471

@@ -480,7 +481,7 @@ def job(self,
480481
else:
481482
break
482483
except WorkflowException as exc:
483-
_logger.error(u"[%s] Cannot make job: %s", step.name, exc)
484+
_logger.error(u"[%s] Cannot make job: %s", step.name, Text(exc))
484485
_logger.debug("", exc_info=True)
485486
self.processStatus = "permanentFail"
486487

@@ -633,9 +634,9 @@ def __init__(self,
633634
except validate.ValidationException as vexc:
634635
if loadingContext.debug:
635636
_logger.exception("Validation exception")
636-
raise WorkflowException(
637+
raise_from(WorkflowException(
637638
u"Tool definition %s failed validation:\n%s" %
638-
(toolpath_object["run"], validate.indent(str(vexc))))
639+
(toolpath_object["run"], validate.indent(str(vexc)))), vexc)
639640

640641
validation_errors = []
641642
self.tool = toolpath_object = copy.deepcopy(toolpath_object)
@@ -804,7 +805,7 @@ def job(self,
804805
raise
805806
except Exception as exc:
806807
_logger.exception("Unexpected exception")
807-
raise WorkflowException(Text(exc))
808+
raise_from(WorkflowException(Text(exc)), exc)
808809

809810
def visit(self, op):
810811
self.embedded_tool.visit(op)
@@ -871,7 +872,7 @@ def parallel_steps(steps, rc, runtimeContext):
871872
if made_progress:
872873
break
873874
except WorkflowException as exc:
874-
_logger.error(u"Cannot make scatter job: %s", exc)
875+
_logger.error(u"Cannot make scatter job: %s", Text(exc))
875876
_logger.debug("", exc_info=True)
876877
rc.receive_scatter_output(index, {}, "permanentFail")
877878
if not made_progress and rc.completed < rc.total:

requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ requests>=2.4.3
22
ruamel.yaml>=0.12.4,<=0.15.77
33
rdflib>=4.2.2,<4.3
44
shellescape>=3.4.1,<3.5
5-
schema-salad>=4.0,<5
5+
schema-salad>=4.4,<5
66
typing>=3.5.3; python_version<"3.6"
77
pathlib2==2.3.2; python_version<"3"
88
prov==1.5.1
@@ -13,3 +13,4 @@ scandir
1313
subprocess32 >= 3.5.0; os.name=="posix" and python_version<"3.5"
1414
typing-extensions
1515
coloredlogs
16+
future

0 commit comments

Comments
 (0)