diff --git a/cwltool/process.py b/cwltool/process.py index 44cacec57..56dedea56 100644 --- a/cwltool/process.py +++ b/cwltool/process.py @@ -467,6 +467,26 @@ def avroize_type( return field_type +def _type_contains_file( + field_type: CWLObjectType | MutableSequence[Any] | CWLOutputType | None, +) -> bool: + """Return True if a (possibly compound/nullable) parameter type includes a ``File``.""" + if isinstance(field_type, str): + # handle plain ("File"), namespaced ("...#File") and avro ("...cwl.File") forms + return field_type.replace("#", ".").split(".")[-1] == "File" + if isinstance(field_type, MutableSequence): + return any(_type_contains_file(entry) for entry in field_type) + if isinstance(field_type, MutableMapping): + if field_type.get("type") == "array": + return _type_contains_file(cast(CWLOutputType, field_type.get("items"))) + if field_type.get("type") == "record": + return any( + _type_contains_file(cast(CWLObjectType, fld).get("type")) + for fld in cast(MutableSequence[Any], field_type.get("fields", [])) + ) + return False + + def get_overrides(overrides: MutableSequence[CWLObjectType], toolid: str) -> CWLObjectType: """Combine overrides for the target tool ID.""" req: CWLObjectType = {} @@ -655,6 +675,14 @@ def __init__(self, toolpath_object: CommentedMap, loadingContext: LoadingContext else: c["type"] = c["type"] + if "format" in c and not _type_contains_file(c["type"]): + _logger.warning( + SourceLine(i, "format", str).makeError( + "'format' is only valid for 'File' type parameters, but " + "'%s' is not a File; the 'format' field will be ignored." % c["name"] + ) + ) + c["type"] = avroize_type(c["type"], c["name"]) if key == "inputs": cast(list[CWLObjectType], self.inputs_record_schema["fields"]).append(c) diff --git a/tests/test_validate.py b/tests/test_validate.py index 8e664d9aa..4508bd973 100644 --- a/tests/test_validate.py +++ b/tests/test_validate.py @@ -57,6 +57,34 @@ def test_validate_with_invalid_input_object() -> None: ) +def test_validate_warns_on_format_for_non_file() -> None: + """`format` on a non-File parameter (e.g. Directory) warns but still validates (#1616, #607).""" + custom_log = io.StringIO() + handler = logging.StreamHandler(custom_log) + handler.setLevel(logging.DEBUG) + exit_code, _, _ = get_main_output( + ["--validate", get_data("tests/wf/format-on-directory.cwl")], + logger_handler=handler, + ) + log_text = re.sub(r"\s\s+", " ", custom_log.getvalue()) + assert exit_code == 0 + assert "'format' is only valid for 'File' type parameters" in log_text + assert "'indir' is not a File" in log_text + + +def test_validate_no_format_warning_for_file() -> None: + """`format` on a File parameter is valid and must not warn (#1616, #607).""" + custom_log = io.StringIO() + handler = logging.StreamHandler(custom_log) + handler.setLevel(logging.DEBUG) + exit_code, _, _ = get_main_output( + ["--validate", get_data("tests/wf/format-on-file.cwl")], + logger_handler=handler, + ) + assert exit_code == 0 + assert "'format' is only valid" not in custom_log.getvalue() + + def test_validate_quiet() -> None: """Ensure that --validate --quiet prints the correct amount of information.""" exit_code, stdout, stderr = get_main_output( diff --git a/tests/wf/format-on-directory.cwl b/tests/wf/format-on-directory.cwl new file mode 100644 index 000000000..184df95b3 --- /dev/null +++ b/tests/wf/format-on-directory.cwl @@ -0,0 +1,13 @@ +#!/usr/bin/env cwl-runner +# Regression fixture for https://github.com/common-workflow-language/cwltool/issues/1616 +# (and #607): `format` is only meaningful for File parameters. Specifying it on a +# Directory input should produce a validation warning, not pass silently. +cwlVersion: v1.2 +class: CommandLineTool +inputs: + indir: + type: Directory + format: http://edamontology.org/format_1915 + inputBinding: {} +outputs: [] +baseCommand: echo diff --git a/tests/wf/format-on-file.cwl b/tests/wf/format-on-file.cwl new file mode 100644 index 000000000..6417f7561 --- /dev/null +++ b/tests/wf/format-on-file.cwl @@ -0,0 +1,11 @@ +#!/usr/bin/env cwl-runner +# Companion fixture: `format` on a File input is valid and must NOT warn. +cwlVersion: v1.2 +class: CommandLineTool +inputs: + inf: + type: File + format: http://edamontology.org/format_1915 + inputBinding: {} +outputs: [] +baseCommand: echo