diff --git a/cwltool/extensions-v1.1.yml b/cwltool/extensions-v1.1.yml index 2f5daf809..5496cd7c0 100644 --- a/cwltool/extensions-v1.1.yml +++ b/cwltool/extensions-v1.1.yml @@ -64,3 +64,22 @@ $graph: 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: JobName + type: record + inVocab: false + extends: cwl:ProcessRequirement + doc: | + Provide a string or expression for naming the runtime workflow step in logs or user interface. + fields: + - name: class + type: string + doc: "Always 'JobName'" + jsonldPredicate: + "_id": "@type" + "_type": "@vocab" + - name: jobname + type: [string, cwl:Expression] + doc: | + A string or expression returning a string with the preferred name for the job. + If it is an expression, it is evaluated after the input object has been completely determined. diff --git a/cwltool/main.py b/cwltool/main.py index 235344dd8..0c60a9b68 100755 --- a/cwltool/main.py +++ b/cwltool/main.py @@ -664,12 +664,14 @@ def setup_schema( use_custom_schema("v1.2.0-dev1", "http://commonwl.org/cwltool", ext11) use_custom_schema("v1.2.0-dev2", "http://commonwl.org/cwltool", ext11) use_custom_schema("v1.2.0-dev3", "http://commonwl.org/cwltool", ext11) + use_custom_schema("v1.2", "http://commonwl.org/cwltool", ext11) else: use_standard_schema("v1.0") use_standard_schema("v1.1") use_standard_schema("v1.2.0-dev1") use_standard_schema("v1.2.0-dev2") use_standard_schema("v1.2.0-dev3") + use_standard_schema("v1.2") class ProvLogFormatter(logging.Formatter): diff --git a/cwltool/process.py b/cwltool/process.py index 88c23c08c..92f10ae13 100644 --- a/cwltool/process.py +++ b/cwltool/process.py @@ -119,6 +119,7 @@ def filter(self, record: logging.LogRecord) -> bool: "http://commonwl.org/cwltool#NetworkAccess", "http://commonwl.org/cwltool#LoadListingRequirement", "http://commonwl.org/cwltool#InplaceUpdateRequirement", + "http://commonwl.org/cwltool#StepName", ] cwl_files = ( diff --git a/cwltool/workflow_job.py b/cwltool/workflow_job.py index e67747cae..55b4d8504 100644 --- a/cwltool/workflow_job.py +++ b/cwltool/workflow_job.py @@ -69,7 +69,17 @@ def job( ) -> JobsGeneratorType: runtimeContext = runtimeContext.copy() runtimeContext.part_of = self.name - runtimeContext.name = shortname(self.id) + + jobnameReq, is_required = self.step.get_requirement( + "http://commonwl.org/cwltool#JobName" + ) + if jobnameReq is not None: + vfinputs = {shortname(k): v for k, v in joborder.items()} + runtimeContext.name = expression.do_eval( + jobnameReq["jobname"], vfinputs, self.step.requirements, None, None, {} + ) + else: + runtimeContext.name = shortname(self.id) _logger.info("[%s] start", self.name) diff --git a/tests/echo_no_output.cwl b/tests/echo_no_output.cwl new file mode 100644 index 000000000..2ed2614bf --- /dev/null +++ b/tests/echo_no_output.cwl @@ -0,0 +1,9 @@ +#!/usr/bin/env cwl-runner +cwlVersion: v1.1 +class: CommandLineTool +inputs: + text: + type: string + inputBinding: {} +outputs: [] +baseCommand: echo diff --git a/tests/scatter-echo-wf.cwl b/tests/scatter-echo-wf.cwl new file mode 100644 index 000000000..b108d0b94 --- /dev/null +++ b/tests/scatter-echo-wf.cwl @@ -0,0 +1,26 @@ +#!/usr/bin/env cwl-runner +$namespaces: + cwltool: "http://commonwl.org/cwltool#" +cwlVersion: v1.1 +class: Workflow +requirements: + ScatterFeatureRequirement: {} + InlineJavascriptRequirement: {} + StepInputExpressionRequirement: {} + +inputs: + texts: + type: string[] + +outputs: [] + +steps: + echo: + run: echo_no_output.cwl + scatter: text + hints: + cwltool:JobName: + jobname: $("test_" + inputs.text.split('.')[0]) + in: + text: texts + out: [] diff --git a/tests/scatter-echo-wf.yml b/tests/scatter-echo-wf.yml new file mode 100644 index 000000000..2a67a665e --- /dev/null +++ b/tests/scatter-echo-wf.yml @@ -0,0 +1 @@ +texts: ["a.vcf", "b.vcf", "c.vcf"] diff --git a/tests/test_ext.py b/tests/test_ext.py index 1eb4091e5..d738a02de 100644 --- a/tests/test_ext.py +++ b/tests/test_ext.py @@ -295,3 +295,23 @@ def test_warn_large_inputs() -> None: ) finally: cwltool.process.FILE_COUNT_WARNING = was + + +def test_jobname() -> None: + """Test JobName requirement.""" + stream = StringIO() + + main( + [ + "--enable-ext", + get_data("tests/scatter-echo-wf.cwl"), + get_data("tests/scatter-echo-wf.yml"), + ], + stderr=stream, + ) + + assert ( + "test_a" in stream.getvalue() + and "test_b" in stream.getvalue() + and "test_c" in stream.getvalue() + )