From 062814e7e5bd006b3f0e4bd4227e70ccc63e6757 Mon Sep 17 00:00:00 2001 From: Mike Lin Date: Tue, 16 Mar 2021 19:39:11 -1000 Subject: [PATCH] miniwdl run: -o directs output/error JSON to a file --- WDL/CLI.py | 31 ++++++++++++++++++++++++------- WDL/Error.py | 3 ++- tests/runner.t | 2 +- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/WDL/CLI.py b/WDL/CLI.py index 62ea9d19..a7b0603a 100644 --- a/WDL/CLI.py +++ b/WDL/CLI.py @@ -473,6 +473,12 @@ def fill_run_subparser(subparsers): action="store_true", help="upon failure, print error information JSON to standard output (in addition to standard error logging)", ) + group.add_argument( + "-o", + metavar="OUT.json", + dest="stdout_file", + help="write JSON output/error to specified file instead of standard output (implies --error-json)", + ) group = run_parser.add_argument_group("logging") group.add_argument( "-v", @@ -551,6 +557,7 @@ def runner( no_cache=False, error_json=False, log_json=False, + stdout_file=None, **kwargs, ): # set up logging @@ -669,12 +676,10 @@ def runner( root=eff_root, # if copy_input_files is set, then input files need not reside under the configured root ) except Error.InputError as exn: - if error_json: - print(json.dumps(runtime.error_json(exn), indent=(None if log_json else 2))) + runner_standard_output(runtime.error_json(exn), stdout_file, error_json, log_json) die(exn.args[0]) except Exception as exn: - if error_json: - print(json.dumps(runtime.error_json(exn), indent=(None if log_json else 2))) + runner_standard_output(runtime.error_json(exn), stdout_file, error_json, log_json) raise if json_only: @@ -722,8 +727,7 @@ def runner( try: rundir, output_env = runtime.run(cfg, target, input_env, run_dir=run_dir, _cache=cache) except Exception as exn: - if error_json: - print(json.dumps(runtime.error_json(exn), indent=(None if log_json else 2))) + runner_standard_output(runtime.error_json(exn), stdout_file, error_json, log_json) exit_status = 2 from_rundir = None while isinstance(exn, runtime.RunFailed): @@ -762,7 +766,7 @@ def runner( # report outputs_json = {"outputs": values_to_json(output_env, namespace=target.name), "dir": rundir} - print(json.dumps(outputs_json, indent=(None if log_json else 2))) + runner_standard_output(outputs_json, stdout_file, error_json, log_json) return outputs_json @@ -1158,6 +1162,19 @@ def raiser(exc: OSError): return path +def runner_standard_output(content, stdout_file, error_json, log_json): + """ + Write the runner output/error JSON in the way requested by the user + """ + if error_json or stdout_file or "error" not in content: + content_json = json.dumps(content, indent=(None if log_json else 2)) + if stdout_file: + with open(stdout_file, "w") as outfile: + print(content_json, file=outfile) + else: + print(content_json) + + def fill_run_self_test_subparser(subparsers): run_parser = subparsers.add_parser( "run_self_test", help="Run a short built-in workflow to test system configuration" diff --git a/WDL/Error.py b/WDL/Error.py index 2bc21633..d9b730a1 100644 --- a/WDL/Error.py +++ b/WDL/Error.py @@ -173,7 +173,8 @@ def __str__(self) -> str: elif str(self.actual).replace("?", "") == str(self.expected): msg += ( " -- to coerce T? X into T, try select_first([X,defaultValue])" - " or select_first([X]) (which might fail at runtime)" + " or select_first([X]) (which might fail at runtime);" + " to coerce Array[T?] X into Array[T], try select_all(X)" ) return msg diff --git a/tests/runner.t b/tests/runner.t index 99f2269e..fe078a5c 100644 --- a/tests/runner.t +++ b/tests/runner.t @@ -188,7 +188,7 @@ task failer { } } EOF -$miniwdl run --dir failer2000/. --verbose --error-json failer2000.wdl > failer2000.stdout 2> failer2000.log.txt +$miniwdl run --dir failer2000/. --verbose --error-json failer2000.wdl -o failer2000.stdout 2> failer2000.log.txt is "$?" "42" "failer2000" is "$(jq '.cause.exit_status' failer2000.stdout)" "42" "workflow error stdout" is "$(jq '.cause.exit_status' failer2000/error.json)" "42" "workflow error.json"