Skip to content

Commit 5a00d2c

Browse files
authored
normalize script names: no .py suffix, always cwl- (#154)
Improve README and docs
1 parent 8ad7b09 commit 5a00d2c

11 files changed

+154
-55
lines changed

README.rst

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@
1111
cwl-utils
1212
---------
1313

14-
A collection of scripts to demonstrate the use of the new Python classes
15-
for loading and parsing `CWL
14+
Python Utilities and Autogenerated Classes for loading and parsing `CWL
1615
v1.0 <https://github.com/common-workflow-language/cwl-utils/blob/main/cwl_utils/parser/v1_0.py>`__,
1716
`CWL
1817
v1.1 <https://github.com/common-workflow-language/cwl-utils/blob/main/cwl_utils/parser/v1_1.py>`__,
@@ -41,7 +40,7 @@ Usage
4140
Pull the all referenced software container images
4241
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4342

44-
``docker_extract.py`` is useful to cache or pre-pull all software
43+
``cwl-docker-extract`` is useful to cache or pre-pull all software
4544
container images referenced in a CWL CommandLineTool or CWL Workflow
4645
(including all referenced CommandLineTools and sub-Workflows and so on).
4746

@@ -50,15 +49,71 @@ the software container images in Docker format.
5049

5150
.. code:: bash
5251
53-
docker_extract.py DIRECTORY path_to_my_workflow.cwl
52+
cwl-docker-extract DIRECTORY path_to_my_workflow.cwl
5453
5554
Or you can use the Singularity software container engine to download and
5655
save the software container images and convert them to the Singularity
5756
format at the same time.
5857

5958
.. code:: bash
6059
61-
docker_extract.py --singularity DIRECTORY path_to_my_workflow.cwl
60+
cwl-docker-extract --singularity DIRECTORY path_to_my_workflow.cwl
61+
62+
63+
Print all referenced software packages
64+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
65+
66+
``cwl-cite-extract`` prints all software packages found (recursively) in the
67+
specified CWL document.
68+
69+
Currently the package name and any listed specs and version field are printed
70+
for all ``SoftwareRequirement`` s found.
71+
72+
.. code:: bash
73+
74+
cwl-cite-extract path_to_my_workflow.cwl
75+
76+
77+
Replace CWL Expressions with concrete steps
78+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
79+
80+
``cwl-expression-refactor`` refactors CWL documents so that any CWL Expression
81+
evaluations are separate steps (either CWL ExpressionTools or CWL CommandLineTools.)
82+
This allows execution by CWL engines that do not want to support inline expression
83+
evaluation outside of concrete steps, or do not want to directly support CWL's
84+
optional ``InlineJavascriptRequirement`` at all.
85+
86+
87+
.. code:: bash
88+
89+
cwl-expression-refactor directory/path/to/save/outputs path_to_my_workflow.cwl [more_workflows.cwl]
90+
91+
Split a packed CWL document
92+
~~~~~~~~~~~~~~~~~~~~~~~~~~~
93+
94+
``cwl-graph-split`` splits a packed CWL document file into multiple files.
95+
96+
Packed CWL documents use the $graph construct to contain multiple CWL Process
97+
objects (Workflow, CommandLineTool, ExpressionTool, Operation). Typically
98+
packed CWL documents contain a CWL Workflow under the name "main" and the
99+
workflow steps (including any sub-workflows).
100+
101+
.. code:: bash
102+
103+
cwl-graph-split --outdir optional/directory/path/to/save/outputs path_to_my_workflow.cwl
104+
105+
Normalize a CWL document
106+
~~~~~~~~~~~~~~~~~~~~~~~~
107+
108+
``cwl-normalizer`` normalizes one or more CWL document so that for each document,
109+
a JSON format CWL document is produces with it and all of its dependencies packed
110+
together, upgrading to CWL v1.2, as needed. Can optionally refactor CWL
111+
Expressions into separate steps in the manner of cwl-expression-refactor.
112+
113+
.. code:: bash
114+
115+
cwl-normalizer directory/path/to/save/outputs path_to_my_workflow.cwl [more_workflows.cwl]
116+
62117
63118
Using the CWL Parsers
64119
~~~~~~~~~~~~~~~~~~~~~

cwl_utils/cite_extract.py

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,43 @@
11
#!/usr/bin/env python3
22
# SPDX-License-Identifier: GPL-3.0-only
3+
import argparse
34
import sys
4-
from typing import Iterator, Union, cast
5+
from typing import Iterator, List, Union, cast
56

67
import cwl_utils.parser.cwl_v1_0 as cwl
78

89
ProcessType = Union[cwl.Workflow, cwl.CommandLineTool, cwl.ExpressionTool]
910

1011

11-
def main() -> int:
12-
"""Load the first argument and extract the software requirements."""
13-
top = cwl.load_document(sys.argv[1])
12+
def arg_parser() -> argparse.ArgumentParser:
13+
"""Argument parser."""
14+
parser = argparse.ArgumentParser(
15+
description="Print information about software used in a CWL document (Workflow or CommandLineTool). "
16+
"For CWL Workflows, all steps will also be searched (recursively)."
17+
)
18+
parser.add_argument(
19+
"input", help="Input CWL document (CWL Workflow or CWL CommandLineTool)"
20+
)
21+
return parser
22+
23+
24+
def parse_args(args: List[str]) -> argparse.Namespace:
25+
"""Parse the command line arguments."""
26+
return arg_parser().parse_args(args)
27+
28+
29+
def run(args: argparse.Namespace) -> int:
30+
"""Extract the software requirements."""
31+
top = cwl.load_document(args.input)
1432
traverse(top)
1533
return 0
1634

1735

36+
def main() -> None:
37+
"""Console entry point."""
38+
sys.exit(run(parse_args(sys.argv[1:])))
39+
40+
1841
def extract_software_packages(process: ProcessType) -> None:
1942
"""Print software packages found in the given process."""
2043
for req in extract_software_reqs(process):
@@ -73,4 +96,4 @@ def traverse_workflow(workflow: cwl.Workflow) -> None:
7396

7497

7598
if __name__ == "__main__":
76-
sys.exit(main())
99+
main()

cwl_utils/docker_extract.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import os
55
import sys
66
from pathlib import Path
7-
from typing import Iterator, Union, cast
7+
from typing import Iterator, List, Union, cast
88

99
import cwl_utils.parser.cwl_v1_0 as cwl
1010
from cwl_utils.image_puller import (
@@ -19,10 +19,13 @@
1919
def arg_parser() -> argparse.ArgumentParser:
2020
"""Argument parser."""
2121
parser = argparse.ArgumentParser(
22-
description="Tool to save docker images from a cwl workflow."
22+
description="Save container images specified in a CWL document (Workflow or CommandLineTool). "
23+
"For CWL Workflows, all steps will also be searched (recursively)."
2324
)
2425
parser.add_argument("dir", help="Directory in which to save images")
25-
parser.add_argument("input", help="Input CWL workflow")
26+
parser.add_argument(
27+
"input", help="Input CWL document (CWL Workflow or CWL CommandLineTool)"
28+
)
2629
parser.add_argument(
2730
"-s",
2831
"--singularity",
@@ -32,12 +35,12 @@ def arg_parser() -> argparse.ArgumentParser:
3235
return parser
3336

3437

35-
def parse_args() -> argparse.Namespace:
38+
def parse_args(args: List[str]) -> argparse.Namespace:
3639
"""Parse the command line arguments."""
37-
return arg_parser().parse_args()
40+
return arg_parser().parse_args(args)
3841

3942

40-
def main(args: argparse.Namespace) -> None:
43+
def run(args: argparse.Namespace) -> int:
4144
"""Extract the docker reqs and download them using Singularity or docker."""
4245
os.makedirs(args.dir, exist_ok=True)
4346

@@ -52,6 +55,7 @@ def main(args: argparse.Namespace) -> None:
5255
else:
5356
image_puller = DockerImagePuller(req.dockerPull, args.dir)
5457
image_puller.save_docker_image()
58+
return 0
5559

5660

5761
def extract_docker_requirements(
@@ -102,6 +106,10 @@ def traverse_workflow(workflow: cwl.Workflow) -> Iterator[cwl.DockerRequirement]
102106
yield from traverse(get_process_from_step(step))
103107

104108

109+
def main() -> None:
110+
"""Command line entry point."""
111+
sys.exit(run(parse_args(sys.argv[1:])))
112+
113+
105114
if __name__ == "__main__":
106-
main(parse_args())
107-
sys.exit(0)
115+
main()

cwl_utils/cwl_expression_refactor.py renamed to cwl_utils/expression_refactor.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -96,15 +96,19 @@ def parse_args(args: List[str]) -> argparse.Namespace:
9696
return arg_parser().parse_args(args)
9797

9898

99-
def main(args: Optional[List[str]] = None) -> int:
99+
def main() -> None:
100+
"""Console entry point."""
101+
sys.exit(run(sys.argv[1:]))
102+
103+
104+
def run(args: List[str]) -> int:
100105
"""Collect the arguments and run."""
101-
if not args:
102-
args = sys.argv[1:]
103-
return run(parse_args(args))
106+
return refactor(parse_args(args))
104107

105108

106-
def run(args: argparse.Namespace) -> int:
109+
def refactor(args: argparse.Namespace) -> int:
107110
"""Primary processing loop."""
111+
108112
return_code = 0
109113
yaml = YAML(typ="rt")
110114
yaml.preserve_quotes = True # type: ignore[assignment]
@@ -175,4 +179,3 @@ def run(args: argparse.Namespace) -> int:
175179

176180
if __name__ == "__main__":
177181
main()
178-
sys.exit(0)

cwl_utils/graph_split.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
import argparse
1212
import json
1313
import os
14-
from typing import IO, Any, MutableMapping, Set, Union, cast
14+
import sys
15+
from typing import IO, Any, List, MutableMapping, Set, Union, cast
1516

1617
from cwlformat.formatter import stringify_dict
1718
from ruamel.yaml.dumper import RoundTripDumper
@@ -22,7 +23,7 @@
2223

2324
def arg_parser() -> argparse.ArgumentParser:
2425
"""Build the argument parser."""
25-
parser = argparse.ArgumentParser(description="Split the packed CWL.")
26+
parser = argparse.ArgumentParser(description="Split a packed CWL document.")
2627
parser.add_argument("cwlfile")
2728
parser.add_argument(
2829
"-m",
@@ -57,20 +58,26 @@ def arg_parser() -> argparse.ArgumentParser:
5758

5859

5960
def main() -> None:
61+
"""Console entry point."""
62+
sys.exit(run(sys.argv[1:]))
63+
64+
65+
def run(args: List[str]) -> int:
6066
"""Split the packed CWL at the path of the first argument."""
61-
options = arg_parser().parse_args()
67+
options = arg_parser().parse_args(args)
6268

6369
with open(options.cwlfile) as source_handle:
64-
run(
70+
graph_split(
6571
source_handle,
6672
options.outdir,
6773
options.output_format,
6874
options.mainfile,
6975
options.pretty,
7076
)
77+
return 0
7178

7279

73-
def run(
80+
def graph_split(
7481
sourceIO: IO[str], output_dir: str, output_format: str, mainfile: str, pretty: bool
7582
) -> None:
7683
"""Loop over the provided packed CWL document and split it up."""

cwl_utils/cwl_normalizer.py renamed to cwl_utils/normalizer.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import sys
88
import tempfile
99
from pathlib import Path
10-
from typing import List, MutableSequence, Optional, Set
10+
from typing import List, MutableSequence, Set
1111

1212
from cwlupgrader import main as cwlupgrader
1313
from ruamel import yaml
@@ -29,7 +29,7 @@
2929
def arg_parser() -> argparse.ArgumentParser:
3030
"""Build the argument parser."""
3131
parser = argparse.ArgumentParser(
32-
description="Tool to normalize CWL documents. Will upgrade to CWL v1.2, "
32+
description="Normalizes CWL documents. Will upgrade to CWL v1.2, "
3333
"and pack the result. Can optionally refactor out CWL expressions."
3434
)
3535
parser.add_argument(
@@ -66,11 +66,9 @@ def parse_args(args: List[str]) -> argparse.Namespace:
6666
return arg_parser().parse_args(args)
6767

6868

69-
def main(args: Optional[List[str]] = None) -> int:
70-
"""Collect the arguments and run."""
71-
if not args:
72-
args = sys.argv[1:]
73-
return run(parse_args(args))
69+
def main() -> None:
70+
"""Console entry point."""
71+
sys.exit(run(parse_args(sys.argv[1:])))
7472

7573

7674
def run(args: argparse.Namespace) -> int:
@@ -128,4 +126,3 @@ def run(args: argparse.Namespace) -> int:
128126

129127
if __name__ == "__main__":
130128
main()
131-
sys.exit(0)

docs/index.rst

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,23 @@ Modules
1414
:caption: Contents:
1515

1616

17-
Example scripts
18-
===============
17+
Included Utility Programs
18+
=========================
19+
20+
.. autoprogram:: cwl_utils.cite_extract:arg_parser()
21+
:prog: cwl-cite-extract
1922

2023
.. autoprogram:: cwl_utils.docker_extract:arg_parser()
21-
:prog: docker_extract.py
24+
:prog: cwl-docker-extract
2225

23-
.. autoprogram:: cwl_utils.cwl_expression_refactor:arg_parser()
24-
:prog: cwl_expression_refactor.py
26+
.. autoprogram:: cwl_utils.expression_refactor:arg_parser()
27+
:prog: cwl-expression-refactor
2528

2629
.. autoprogram:: cwl_utils.graph_split:arg_parser()
27-
:prog: graph_split.py
30+
:prog: cwl-graph-split
2831

29-
.. autoprogram:: cwl_utils.cwl_normalizer:arg_parser()
30-
:prog: cwl_normalizer.py
32+
.. autoprogram:: cwl_utils.normalizer:arg_parser()
33+
:prog: cwl-normalizer
3134

3235

3336
Indices and tables

setup.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,16 @@
3939
.splitlines(),
4040
tests_require=["pytest<8"],
4141
test_suite="tests",
42-
scripts=[
43-
"cwl_utils/docker_extract.py",
44-
"cwl_utils/cwl_expression_refactor.py",
45-
"cwl_utils/cite_extract.py",
46-
"cwl_utils/graph_split.py",
47-
"cwl_utils/cwl_normalizer.py",
48-
],
4942
extras_require={"pretty": ["cwlformat"]},
43+
entry_points={
44+
"console_scripts": [
45+
"cwl-cite-extract=cwl_utils.cite_extract:main",
46+
"cwl-docker-extract=cwl_utils.docker_extract:main",
47+
"cwl-expression-refactor=cwl_utils.expression_refactor:main",
48+
"cwl-graph-split=cwl_utils.graph_split:main",
49+
"cwl-normalizer=cwl_utils.normalizer:main",
50+
]
51+
},
5052
classifiers=[
5153
"Environment :: Console",
5254
"Intended Audience :: Science/Research",

test-requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
pytest < 8
22
pytest-cov
33
pytest-xdist
4+
cwlformat

tests/test_etools_to_clt.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@
1313
import cwl_utils.parser.cwl_v1_0 as parser
1414
import cwl_utils.parser.cwl_v1_1 as parser1
1515
import cwl_utils.parser.cwl_v1_2 as parser2
16-
from cwl_utils.cwl_expression_refactor import main as expression_refactor
1716
from cwl_utils.cwl_v1_0_expression_refactor import traverse as traverse0
1817
from cwl_utils.cwl_v1_1_expression_refactor import traverse as traverse1
1918
from cwl_utils.cwl_v1_2_expression_refactor import traverse as traverse2
2019
from cwl_utils.errors import WorkflowException
20+
from cwl_utils.expression_refactor import run as expression_refactor
2121

2222
HERE = Path(__file__).resolve().parent
2323

0 commit comments

Comments
 (0)