Skip to content

Commit 6a10aa5

Browse files
jfennickmr-c
andauthored
--write-summary to redirect final JSON output (#1773)
Co-authored-by: Jake Fennick <[email protected]> Co-authored-by: Michael R. Crusoe <[email protected]>
1 parent 38f42e6 commit 6a10aa5

File tree

4 files changed

+82
-44
lines changed

4 files changed

+82
-44
lines changed

cwltool/argparser.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,15 @@ def arg_parser() -> argparse.ArgumentParser:
390390
"--debug", action="store_true", help="Print even more logging"
391391
)
392392

393+
parser.add_argument(
394+
"--write-summary",
395+
"-w",
396+
type=str,
397+
help="Path to write the final output JSON object to. Default is stdout.",
398+
default="",
399+
dest="write_summary",
400+
)
401+
393402
parser.add_argument(
394403
"--strict-memory-limit",
395404
action="store_true",

cwltool/cwlrdf.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import urllib
22
from codecs import StreamWriter
3-
from typing import Any, Dict, Iterator, Optional, TextIO, Union, cast
3+
from typing import IO, Any, Dict, Iterator, Optional, TextIO, Union, cast
44

55
from rdflib import Graph
66
from rdflib.query import ResultRow
@@ -217,7 +217,7 @@ def dot_without_parameters(g: Graph, stdout: Union[TextIO, StreamWriter]) -> Non
217217
def printdot(
218218
wf: Process,
219219
ctx: ContextType,
220-
stdout: Union[TextIO, StreamWriter],
220+
stdout: IO[str],
221221
) -> None:
222222
cwl_viewer = CWLViewer(printrdf(wf, ctx, "n3")) # type: CWLViewer
223223
stdout.write(cwl_viewer.dot().replace(f"{wf.metadata['id']}#", ""))

cwltool/main.py

Lines changed: 46 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,13 @@
3939
from schema_salad.exceptions import ValidationException
4040
from schema_salad.ref_resolver import Loader, file_uri, uri_file_path
4141
from schema_salad.sourceline import cmap, strip_dup_lineno
42-
from schema_salad.utils import ContextType, FetcherCallableType, json_dumps, yaml_no_ts
42+
from schema_salad.utils import (
43+
ContextType,
44+
FetcherCallableType,
45+
json_dump,
46+
json_dumps,
47+
yaml_no_ts,
48+
)
4349

4450
import ruamel.yaml
4551
from ruamel.yaml.comments import CommentedMap, CommentedSeq
@@ -415,7 +421,7 @@ def init_job_order(
415421
args: argparse.Namespace,
416422
process: Process,
417423
loader: Loader,
418-
stdout: Union[TextIO, StreamWriter],
424+
stdout: IO[str],
419425
print_input_deps: bool = False,
420426
relative_deps: str = "primary",
421427
make_fs_access: Callable[[str], StdFsAccess] = StdFsAccess,
@@ -438,7 +444,7 @@ def init_job_order(
438444
file_uri(os.getcwd()) + "/",
439445
)
440446
if args.tool_help:
441-
toolparser.print_help(cast(IO[str], stdout))
447+
toolparser.print_help(stdout)
442448
exit(0)
443449
cmd_line = vars(toolparser.parse_args(args.job_order))
444450
for record_name in records:
@@ -564,7 +570,7 @@ def make_relative(base: str, obj: CWLObjectType) -> None:
564570
def printdeps(
565571
obj: CWLObjectType,
566572
document_loader: Loader,
567-
stdout: Union[TextIO, StreamWriter],
573+
stdout: IO[str],
568574
relative_deps: str,
569575
uri: str,
570576
basedir: Optional[str] = None,
@@ -577,7 +583,7 @@ def printdeps(
577583
elif relative_deps == "cwd":
578584
base = os.getcwd()
579585
visit_class(deps, ("File", "Directory"), functools.partial(make_relative, base))
580-
print(json_dumps(deps, indent=4, default=str), file=stdout)
586+
json_dump(deps, stdout, indent=4, default=str)
581587

582588

583589
def prov_deps(
@@ -641,10 +647,10 @@ def print_pack(
641647
"""Return a CWL serialization of the CWL document in JSON."""
642648
packed = pack(loadingContext, uri)
643649
if len(cast(Sized, packed["$graph"])) > 1:
644-
return json_dumps(packed, indent=4, default=str)
645-
return json_dumps(
646-
cast(MutableSequence[CWLObjectType], packed["$graph"])[0], indent=4, default=str
647-
)
650+
target = packed
651+
else:
652+
target = cast(MutableSequence[CWLObjectType], packed["$graph"])[0]
653+
return json_dumps(target, indent=4, default=str)
648654

649655

650656
def supported_cwl_versions(enable_dev: bool) -> List[str]:
@@ -763,9 +769,7 @@ def setup_loadingContext(
763769
return loadingContext
764770

765771

766-
def make_template(
767-
tool: Process,
768-
) -> None:
772+
def make_template(tool: Process, target: IO[str]) -> None:
769773
"""Make a template CWL input object for the give Process."""
770774

771775
def my_represent_none(
@@ -783,7 +787,7 @@ def my_represent_none(
783787
yaml.block_seq_indent = 2
784788
yaml.dump(
785789
generate_input_template(tool),
786-
sys.stdout,
790+
target,
787791
)
788792

789793

@@ -945,7 +949,7 @@ def check_working_directories(
945949

946950
def print_targets(
947951
tool: Process,
948-
stdout: Union[TextIO, StreamWriter],
952+
stdout: IO[str],
949953
loading_context: LoadingContext,
950954
prefix: str = "",
951955
) -> None:
@@ -982,7 +986,7 @@ def main(
982986
args: Optional[argparse.Namespace] = None,
983987
job_order_object: Optional[CWLObjectType] = None,
984988
stdin: IO[Any] = sys.stdin,
985-
stdout: Optional[Union[TextIO, StreamWriter]] = None,
989+
stdout: Optional[IO[str]] = None,
986990
stderr: IO[Any] = sys.stderr,
987991
versionfunc: Callable[[], str] = versionstring,
988992
logger_handler: Optional[logging.Handler] = None,
@@ -992,17 +996,18 @@ def main(
992996
runtimeContext: Optional[RuntimeContext] = None,
993997
input_required: bool = True,
994998
) -> int:
995-
if not stdout: # force UTF-8 even if the console is configured differently
999+
if stdout is None: # force UTF-8 even if the console is configured differently
9961000
if hasattr(sys.stdout, "encoding") and sys.stdout.encoding.upper() not in (
9971001
"UTF-8",
9981002
"UTF8",
9991003
):
10001004
if hasattr(sys.stdout, "detach"):
10011005
stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8")
10021006
else:
1003-
stdout = getwriter("utf-8")(sys.stdout) # type: ignore
1007+
stdout = getwriter("utf-8")(sys.stdout) # type: ignore[assignment,arg-type]
10041008
else:
10051009
stdout = sys.stdout
1010+
stdout = cast(IO[str], stdout)
10061011

10071012
_logger.removeHandler(defaultStreamHandler)
10081013
stderr_handler = logger_handler
@@ -1151,15 +1156,13 @@ def main(
11511156
)
11521157

11531158
if args.print_pre:
1154-
print(
1155-
json_dumps(
1156-
processobj,
1157-
indent=4,
1158-
sort_keys=True,
1159-
separators=(",", ": "),
1160-
default=str,
1161-
),
1162-
file=stdout,
1159+
json_dump(
1160+
processobj,
1161+
stdout,
1162+
indent=4,
1163+
sort_keys=True,
1164+
separators=(",", ": "),
1165+
default=str,
11631166
)
11641167
return 0
11651168

@@ -1179,7 +1182,7 @@ def main(
11791182
raise main_missing_exc
11801183

11811184
if args.make_template:
1182-
make_template(tool)
1185+
make_template(tool, stdout)
11831186
return 0
11841187

11851188
if args.validate:
@@ -1225,15 +1228,13 @@ def main(
12251228
if args.print_subgraph:
12261229
if "name" in tool.tool:
12271230
del tool.tool["name"]
1228-
print(
1229-
json_dumps(
1230-
tool.tool,
1231-
indent=4,
1232-
sort_keys=True,
1233-
separators=(",", ": "),
1234-
default=str,
1235-
),
1236-
file=stdout,
1231+
json_dump(
1232+
tool.tool,
1233+
stdout,
1234+
indent=4,
1235+
sort_keys=True,
1236+
separators=(",", ": "),
1237+
default=str,
12371238
)
12381239
return 0
12391240

@@ -1398,12 +1399,15 @@ def loc_to_path(obj: CWLObjectType) -> None:
13981399
# Unsetting the Generation from final output object
13991400
visit_class(out, ("File",), MutationManager().unset_generation)
14001401

1401-
print(
1402-
json_dumps(out, indent=4, ensure_ascii=False, default=str),
1403-
file=stdout,
1404-
)
1405-
if hasattr(stdout, "flush"):
1406-
stdout.flush()
1402+
if args.write_summary:
1403+
with open(args.write_summary, "w") as output_file:
1404+
json_dump(
1405+
out, output_file, indent=4, ensure_ascii=False, default=str
1406+
)
1407+
else:
1408+
json_dump(out, stdout, indent=4, ensure_ascii=False, default=str)
1409+
if hasattr(stdout, "flush"):
1410+
stdout.flush()
14071411

14081412
if status != "success":
14091413
_logger.warning("Final process status is %s", status)

tests/test_examples.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1348,6 +1348,31 @@ def test_cache_relative_paths(tmp_path: Path, factor: str) -> None:
13481348
assert (tmp_path / "cwltool_cache" / "27903451fc1ee10c148a0bdeb845b2cf").exists()
13491349

13501350

1351+
def test_write_summary(tmp_path: Path) -> None:
1352+
"""Test --write-summary."""
1353+
commands = [
1354+
get_data("tests/wf/no-parameters-echo.cwl"),
1355+
]
1356+
error_code, stdout, stderr = get_main_output(commands)
1357+
stderr = re.sub(r"\s\s+", " ", stderr)
1358+
assert error_code == 0, stderr
1359+
1360+
final_output_path = str(tmp_path / "final-output.json")
1361+
commands_no = [
1362+
"--write-summary",
1363+
final_output_path,
1364+
get_data("tests/wf/no-parameters-echo.cwl"),
1365+
]
1366+
error_code, stdout_no, stderr = get_main_output(commands_no)
1367+
stderr = re.sub(r"\s\s+", " ", stderr)
1368+
assert error_code == 0, stderr
1369+
1370+
with open(final_output_path) as f:
1371+
final_output_str = f.read()
1372+
1373+
assert len(stdout_no) + len(final_output_str) == len(stdout)
1374+
1375+
13511376
@needs_docker
13521377
def test_compute_checksum() -> None:
13531378
runtime_context = RuntimeContext()

0 commit comments

Comments
 (0)