Skip to content

Commit fb5774b

Browse files
authored
Expressiontool timer (#1681)
* increase default eval_timeout value Increasing eval_timeout from 20 to 60 seconds. The longer default time allows for smooth running on systems with increased I/O timings (such as HPC systems with network based file systems). * Add log information when eval-timeout is exceeded Added logger output when timer has been triggered. This also stops the truncation of stdout and stderr. * Add eval_timeout control to validate_js_expressions For consistency with other expression tools, eval_timeout is now used for controlling the timeout of the validate_js_expression steps. This has involved adding eval_timeout to LoadingContext (mirroring RuntimeContext).
1 parent b21b0c1 commit fb5774b

File tree

6 files changed

+36
-12
lines changed

6 files changed

+36
-12
lines changed

cwltool/argparser.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -226,9 +226,9 @@ def arg_parser() -> argparse.ArgumentParser:
226226
parser.add_argument(
227227
"--eval-timeout",
228228
help="Time to wait for a Javascript expression to evaluate before giving "
229-
"an error, default 20s.",
229+
"an error, default 60s.",
230230
type=float,
231-
default=20,
231+
default=60,
232232
)
233233

234234
provgroup = parser.add_argument_group(

cwltool/context.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ def __init__(self, kwargs: Optional[Dict[str, Any]] = None) -> None:
102102
self.relax_path_checks = False # type: bool
103103
self.singularity = False # type: bool
104104
self.podman = False # type: bool
105+
self.eval_timeout = 60 # type: float
105106

106107
super().__init__(kwargs)
107108

@@ -164,7 +165,7 @@ def __init__(self, kwargs: Optional[Dict[str, Any]] = None) -> None:
164165
self.js_console = False # type: bool
165166
self.job_script_provider = None # type: Optional[DependenciesConfiguration]
166167
self.select_resources = None # type: Optional[select_resources_callable]
167-
self.eval_timeout = 20 # type: float
168+
self.eval_timeout = 60 # type: float
168169
self.postScatterEval = (
169170
None
170171
) # type: Optional[Callable[[CWLObjectType], Optional[CWLObjectType]]]

cwltool/process.py

+1
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,7 @@ def __init__(
731731
self.doc_schema.names[avroname],
732732
validate_js_options,
733733
self.container_engine,
734+
loadingContext.eval_timeout,
734735
)
735736

736737
dockerReq, is_req = self.get_requirement("DockerRequirement")

cwltool/sandboxjs.py

+15-3
Original file line numberDiff line numberDiff line change
@@ -285,11 +285,23 @@ def process_finished() -> bool:
285285
pipes[1].write(buf)
286286
except OSError:
287287
break
288-
timer.cancel()
289288

290289
stdin_buf.close()
291-
stdoutdata = stdout_buf.getvalue()[: -len(PROCESS_FINISHED_STR) - 1]
292-
stderrdata = stderr_buf.getvalue()[: -len(PROCESS_FINISHED_STR) - 1]
290+
291+
if not timer.is_alive():
292+
_logger.info("Expression Tool stopped because time limit has been exceeded.")
293+
_logger.info(
294+
"Time limit is {} seconds. This can be increased using the --eval-timeout flag.\n".format(
295+
timeout
296+
)
297+
)
298+
stdoutdata = stdout_buf.getvalue()
299+
stderrdata = stderr_buf.getvalue()
300+
else:
301+
stdoutdata = stdout_buf.getvalue()[: -len(PROCESS_FINISHED_STR) - 1]
302+
stderrdata = stderr_buf.getvalue()[: -len(PROCESS_FINISHED_STR) - 1]
303+
304+
timer.cancel()
293305

294306
nodejs.poll()
295307

cwltool/validate_js.py

+13-3
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ def jshint_js(
134134
globals: Optional[List[str]] = None,
135135
options: Optional[Dict[str, Union[List[str], str, int]]] = None,
136136
container_engine: str = "docker",
137+
eval_timeout: float = 60,
137138
) -> JSHintJSReturn:
138139
if globals is None:
139140
globals = []
@@ -164,7 +165,7 @@ def jshint_js(
164165
returncode, stdout, stderr = exec_js_process(
165166
"validateJS(%s)"
166167
% json_dumps({"code": js_text, "options": options, "globals": globals}),
167-
timeout=30,
168+
timeout=eval_timeout,
168169
context=jshint_functions_text,
169170
container_engine=container_engine,
170171
)
@@ -216,6 +217,7 @@ def validate_js_expressions(
216217
schema: Schema,
217218
jshint_options: Optional[Dict[str, Union[List[str], str, int]]] = None,
218219
container_engine: str = "docker",
220+
eval_timeout: float = 60,
219221
) -> None:
220222

221223
if tool.get("requirements") is None:
@@ -236,7 +238,11 @@ def validate_js_expressions(
236238

237239
for i, expression_lib_line in enumerate(expression_lib):
238240
expression_lib_line_errors, expression_lib_line_globals = jshint_js(
239-
expression_lib_line, js_globals, jshint_options, container_engine
241+
expression_lib_line,
242+
js_globals,
243+
jshint_options,
244+
container_engine,
245+
eval_timeout,
240246
)
241247
js_globals.extend(expression_lib_line_globals)
242248
print_js_hint_messages(
@@ -262,7 +268,11 @@ def validate_js_expressions(
262268
code_fragment = unscanned_str[scan_slice[0] + 1 : scan_slice[1]]
263269
code_fragment_js = code_fragment_to_js(code_fragment, "")
264270
expression_errors, _ = jshint_js(
265-
code_fragment_js, js_globals, jshint_options, container_engine
271+
code_fragment_js,
272+
js_globals,
273+
jshint_options,
274+
container_engine,
275+
eval_timeout,
266276
)
267277
print_js_hint_messages(expression_errors, source_line)
268278

tests/test_singularity_versions.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
"""Test singularity{,-ce} & apptainer versions."""
2+
from subprocess import check_output # nosec
3+
24
import cwltool.singularity
35
from cwltool.singularity import (
46
get_version,
57
is_apptainer_1_or_newer,
68
is_version_2_6,
7-
is_version_3_or_newer,
89
is_version_3_1_or_newer,
910
is_version_3_4_or_newer,
11+
is_version_3_or_newer,
1012
)
1113

12-
from subprocess import check_output # nosec
13-
1414

1515
def reset_singularity_version_cache() -> None:
1616
"""Reset the cache for testing."""

0 commit comments

Comments
 (0)