/
errors.py
142 lines (102 loc) · 3.82 KB
/
errors.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
"""Exceptions raised by Apps."""
from functools import wraps
from typing import Callable, List, Optional, TypeVar, Union
from typing_extensions import ParamSpec
from types import TracebackType
import logging
from tblib import Traceback
from parsl.data_provider.files import File
from parsl.errors import ParslError
logger = logging.getLogger(__name__)
class AppException(ParslError):
"""An error raised during execution of an app.
What this exception contains depends entirely on context
"""
class AppBadFormatting(ParslError):
"""An error raised during formatting of a bash function.
"""
class BashExitFailure(AppException):
"""A non-zero exit code returned from a @bash_app
Contains:
app name (str)
exitcode (int)
"""
def __init__(self, app_name: str, exitcode: int) -> None:
self.app_name = app_name
self.exitcode = exitcode
def __str__(self) -> str:
return f"bash_app {self.app_name} failed with unix exit code {self.exitcode}"
class AppTimeout(AppException):
"""An error raised during execution of an app when it exceeds its allotted walltime.
"""
class BashAppNoReturn(AppException):
"""Bash app returned no string.
Contains:
reason(string)
"""
def __init__(self, reason: str) -> None:
super().__init__(reason)
self.reason = reason
class MissingOutputs(ParslError):
"""Error raised at the end of app execution due to missing output files.
Contains:
reason(string)
outputs(List of files)
"""
def __init__(self, reason: str, outputs: List[File]) -> None:
super().__init__(reason, outputs)
self.reason = reason
self.outputs = outputs
def __repr__(self) -> str:
return "Missing Outputs: {0}, Reason:{1}".format(self.outputs, self.reason)
class BadStdStreamFile(ParslError):
"""Error raised due to bad filepaths specified for STDOUT/ STDERR.
Contains:
reason(string)
"""
def __init__(self, reason: str) -> None:
super().__init__(reason)
self._reason = reason
def __repr__(self) -> str:
return "Bad Stream File: {}".format(self._reason)
def __str__(self) -> str:
return self.__repr__()
class RemoteExceptionWrapper:
def __init__(self, e_type: type, e_value: BaseException, traceback: Optional[TracebackType]) -> None:
self.e_type = e_type
self.e_value = e_value
self.e_traceback = None if traceback is None else Traceback(traceback)
if e_value.__cause__ is None:
self.cause = None
else:
cause = e_value.__cause__
self.cause = self.__class__(type(cause), cause, cause.__traceback__)
def reraise(self) -> None:
# the type is logged here before deserialising v and tb
# because occasionally there are problems deserialising the
# value (see #785, #548) and the fix is related to the
# specific exception type.
logger.debug("Reraising exception of type {}".format(self.e_type))
v = self.get_exception()
raise v
def get_exception(self) -> BaseException:
v = self.e_value
if self.cause is not None:
v.__cause__ = self.cause.get_exception()
if self.e_traceback is not None:
tb = self.e_traceback.as_traceback()
return v.with_traceback(tb)
else:
return v
P = ParamSpec('P')
R = TypeVar('R')
def wrap_error(func: Callable[P, R]) -> Callable[P, Union[R, RemoteExceptionWrapper]]:
@wraps(func)
def wrapper(*args: P.args, **kwargs: P.kwargs) -> Union[R, RemoteExceptionWrapper]:
import sys
from parsl.app.errors import RemoteExceptionWrapper
try:
return func(*args, **kwargs)
except Exception:
return RemoteExceptionWrapper(*sys.exc_info())
return wrapper