Skip to content

cwl v1.3.0-dev1 native loop support #1779

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 27 commits into from
Jul 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
13d65c6
Added loop support
GlassOfWhiskey Dec 25, 2022
8d63c97
include v1.3.0-dev1 schema; adjust updater
mr-c Dec 29, 2022
b4fe0df
impossible to have non-dict hints, reqs, or steps here
mr-c Dec 29, 2022
944d348
Migrate from `loopSource` to `outputSource`
GlassOfWhiskey Jan 3, 2023
f0e8614
Updated cwl-v1.3 files
GlassOfWhiskey Jan 3, 2023
c0f5c79
Removed walrus operator for compatibility
GlassOfWhiskey Jan 3, 2023
32681f4
Solving mypy complaints
GlassOfWhiskey Jan 3, 2023
bf87c06
Fix update
tetron May 16, 2024
ed5af6d
Merged cwl-v1.3 into cwltool
May 16, 2024
fb4cd48
pytest: list errored/failed tests
mr-c Jul 10, 2024
aaad653
increase minimum version of schema-salad
mr-c Jul 10, 2024
6c2c18a
Added additional loop tests
tetron Jul 16, 2024
7b46ac0
Fix format
tetron Jul 16, 2024
07799f8
Fix more lint issues
tetron Jul 16, 2024
2a0ff16
Add loop tests using 1.3.0-dev1 syntax
tetron Jul 16, 2024
56a4300
Try excluding 3.0.1 pydot release
tetron Jul 16, 2024
df14e8a
Not compatible with pydot 3
tetron Jul 16, 2024
b4bd123
Re-add tests for Loop extension
tetron Jul 16, 2024
971ddec
Need to include loop-ext in manifest
tetron Jul 16, 2024
40d3b40
Add another test to improve coverage
tetron Jul 16, 2024
be01ba9
Run format again
tetron Jul 16, 2024
174594a
Should import errors from cwl_util so they are caught properly
tetron Jul 16, 2024
92ab633
Explicitly reexport WorkflowException
tetron Jul 16, 2024
f5a1fce
Disable flake8 error about unused symbol in errors.py
tetron Jul 16, 2024
c450ede
Lint fix
tetron Jul 16, 2024
d041ec0
ignore some untested lines
mr-c Jul 17, 2024
3ed2518
Merge branch 'main' into cwl-v1.3
tetron Jul 17, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ recursive-include mypy-stubs *.pyi *.py
include tests/*
include tests/cwl-conformance/cwltool-conftest.py
include tests/loop/*
include tests/loop-ext/*
include tests/tmp1/tmp2/tmp3/.gitkeep
include tests/tmp4/alpha/*
include tests/wf/*
Expand Down Expand Up @@ -54,6 +55,10 @@ include cwltool/schemas/v1.2/*.yml
include cwltool/schemas/v1.2/*.md
include cwltool/schemas/v1.2/salad/schema_salad/metaschema/*.yml
include cwltool/schemas/v1.2/salad/schema_salad/metaschema/*.md
include cwltool/schemas/v1.3.0-dev1/*.yml
include cwltool/schemas/v1.3.0-dev1/*.md
include cwltool/schemas/v1.3.0-dev1/salad/schema_salad/metaschema/*.yml
include cwltool/schemas/v1.3.0-dev1/salad/schema_salad/metaschema/*.md
include cwltool/extensions.yml
include cwltool/extensions-v1.1.yml
include cwltool/extensions-v1.2.yml
Expand Down
6 changes: 3 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -160,11 +160,11 @@ diff-cover.html: coverage.xml

## test : run the cwltool test suite
test: $(PYSOURCES)
python3 -m pytest -rs ${PYTEST_EXTRA}
python3 -m pytest -rsfE ${PYTEST_EXTRA}

## testcov : run the cwltool test suite and collect coverage
testcov: $(PYSOURCES)
python3 -m pytest -rs --cov --cov-config=.coveragerc --cov-report= ${PYTEST_EXTRA}
python3 -m pytest -rsfE --cov --cov-config=.coveragerc --cov-report= ${PYTEST_EXTRA}

sloccount.sc: $(PYSOURCES) Makefile
sloccount --duplicates --wide --details $^ > $@
Expand All @@ -183,7 +183,7 @@ mypy: $(PYSOURCES)

mypyc: $(PYSOURCES)
MYPYPATH=mypy-stubs CWLTOOL_USE_MYPYC=1 pip install --verbose -e . \
&& pytest -rs -vv ${PYTEST_EXTRA}
&& pytest -rsfE -vv ${PYTEST_EXTRA}

shellcheck: FORCE
shellcheck build-cwltool-docker.sh cwl-docker.sh release-test.sh conformance-test.sh \
Expand Down
27 changes: 9 additions & 18 deletions cwltool/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -517,42 +517,33 @@ def is_conditional_step(param_to_step: Dict[str, CWLObjectType], parm_id: str) -


def is_all_output_method_loop_step(param_to_step: Dict[str, CWLObjectType], parm_id: str) -> bool:
"""Check if a step contains a http://commonwl.org/cwltool#Loop requirement with `all` outputMethod."""
"""Check if a step contains a `loop` directive with `all` outputMethod."""
source_step: Optional[MutableMapping[str, Any]] = param_to_step.get(parm_id)
if source_step is not None:
for requirement in source_step.get("requirements", []):
if (
requirement["class"] == "http://commonwl.org/cwltool#Loop"
and requirement.get("outputMethod") == "all"
):
return True
if source_step.get("loop") is not None and source_step.get("outputMethod") == "all":
return True
return False


def loop_checker(steps: Iterator[MutableMapping[str, Any]]) -> None:
"""
Check http://commonwl.org/cwltool#Loop requirement compatibility with other directives.
Check `loop` compatibility with other directives.

:raises ValidationException: If there is an incompatible combination between
cwltool:loop and 'scatter' or 'when'.
:raises ValidationException: If there is an incompatible combination between `loop` and `scatter`.
"""
exceptions = []
for step in steps:
requirements = {
**{h["class"]: h for h in step.get("hints", [])},
**{r["class"]: r for r in step.get("requirements", [])},
}
if "http://commonwl.org/cwltool#Loop" in requirements:
if "when" in step:
if "loop" in step:
if "when" not in step:
exceptions.append(
SourceLine(step, "id").makeError(
"The `cwltool:Loop` clause is not compatible with the `when` directive."
"The `when` clause is mandatory when the `loop` directive is defined."
)
)
if "scatter" in step:
exceptions.append(
SourceLine(step, "id").makeError(
"The `cwltool:Loop` clause is not compatible with the `scatter` directive."
"The `loop` clause is not compatible with the `scatter` directive."
)
)
if exceptions:
Expand Down
20 changes: 14 additions & 6 deletions cwltool/errors.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
class WorkflowException(Exception):
pass
"""Common errors.

WorkflowException and GraphTargetMissingException are aliased to
equivalent errors from cwl_utils.errors and re-exported by this module
to avoid breaking the interface for other code.

"""

# flake8: noqa: F401

from cwl_utils.errors import WorkflowException as WorkflowException


from cwl_utils.errors import GraphTargetMissingException as GraphTargetMissingException


class UnsupportedRequirement(WorkflowException):
Expand All @@ -8,7 +20,3 @@ class UnsupportedRequirement(WorkflowException):

class ArgumentException(Exception):
"""Mismatched command line arguments provided."""


class GraphTargetMissingException(WorkflowException):
"""When a ``$graph`` is encountered and there is no target and no ``main``/``#main``."""
121 changes: 121 additions & 0 deletions cwltool/extensions-v1.3.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
$base: http://commonwl.org/cwltool#
$namespaces:
cwl: "https://w3id.org/cwl/cwl#"
$graph:
- $import: https://w3id.org/cwl/CommonWorkflowLanguage.yml

- name: Secrets
type: record
inVocab: false
extends: cwl:ProcessRequirement
fields:
class:
type: string
doc: "Always 'Secrets'"
jsonldPredicate:
"_id": "@type"
"_type": "@vocab"
secrets:
type: string[]
doc: |
List one or more input parameters that are sensitive (such as passwords)
which will be deliberately obscured from logging.
jsonldPredicate:
"_type": "@id"
refScope: 0


- name: ProcessGenerator
type: record
inVocab: true
extends: cwl:Process
documentRoot: true
fields:
- name: class
jsonldPredicate:
"_id": "@type"
"_type": "@vocab"
type: string
- name: run
type: [string, cwl:Process]
jsonldPredicate:
_id: "cwl:run"
_type: "@id"
subscope: run
doc: |
Specifies the process to run.

- name: MPIRequirement
type: record
inVocab: false
extends: cwl:ProcessRequirement
doc: |
Indicates that a process requires an MPI runtime.
fields:
- name: class
type: string
doc: "Always 'MPIRequirement'"
jsonldPredicate:
"_id": "@type"
"_type": "@vocab"
- name: processes
type: [int, cwl:Expression]
doc: |
The number of MPI processes to start. If you give a string,
this will be evaluated as a CWL Expression and it must
evaluate to an integer.

- name: CUDARequirement
type: record
extends: cwl:ProcessRequirement
inVocab: false
doc: |
Require support for NVIDA CUDA (GPU hardware acceleration).
fields:
class:
type: string
doc: 'cwltool:CUDARequirement'
jsonldPredicate:
_id: "@type"
_type: "@vocab"
cudaVersionMin:
type: string
doc: |
Minimum CUDA version to run the software, in X.Y format. This
corresponds to a CUDA SDK release. When running directly on
the host (not in a container) the host must have a compatible
CUDA SDK (matching the exact version, or, starting with CUDA
11.3, matching major version). When run in a container, the
container image should provide the CUDA runtime, and the host
driver is injected into the container. In this case, because
CUDA drivers are backwards compatible, it is possible to
use an older SDK with a newer driver across major versions.

See https://docs.nvidia.com/deploy/cuda-compatibility/ for
details.
cudaComputeCapability:
type:
- 'string'
- 'string[]'
doc: |
CUDA hardware capability required to run the software, in X.Y
format.

* If this is a single value, it defines only the minimum
compute capability. GPUs with higher capability are also
accepted.

* If it is an array value, then only select GPUs with compute
capabilities that explicitly appear in the array.
cudaDeviceCountMin:
type: ['null', int, cwl:Expression]
default: 1
doc: |
Minimum number of GPU devices to request. If not specified,
same as `cudaDeviceCountMax`. If neither are specified,
default 1.
cudaDeviceCountMax:
type: ['null', int, cwl:Expression]
doc: |
Maximum number of GPU devices to request. If not specified,
same as `cudaDeviceCountMin`.
5 changes: 1 addition & 4 deletions cwltool/process.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ def filter(self, record: logging.LogRecord) -> bool:
]

cwl_files = (
"Base.yml",
"Workflow.yml",
"CommandLineTool.yml",
"CommonWorkflowLanguage.yml",
Expand Down Expand Up @@ -1046,10 +1047,6 @@ def validate_hints(self, avsc_names: Names, hints: List[CWLObjectType], strict:
sl = SourceLine(hints, i, ValidationException, debug)
with sl:
classname = cast(str, r["class"])
if classname == "http://commonwl.org/cwltool#Loop":
raise ValidationException(
"http://commonwl.org/cwltool#Loop is valid only under requirements."
)
avroname = classname
if classname in self.doc_loader.vocab:
avroname = avro_type_name(self.doc_loader.vocab[classname])
Expand Down
Loading