From 2782966fc46fa6bf3d6b4b950db26da382d5b4f1 Mon Sep 17 00:00:00 2001 From: "Michael R. Crusoe" Date: Thu, 30 Oct 2025 18:02:34 +0100 Subject: [PATCH 1/2] "id" is a required field in for Parameters and WorkflowSteps --- cwl_utils/cwl_v1_2_expression_refactor.py | 4 +- cwl_utils/parser/cwl_v1_2.py | 60 +++++++++++------------ 2 files changed, 33 insertions(+), 31 deletions(-) diff --git a/cwl_utils/cwl_v1_2_expression_refactor.py b/cwl_utils/cwl_v1_2_expression_refactor.py index a56c7950..97f90a5f 100755 --- a/cwl_utils/cwl_v1_2_expression_refactor.py +++ b/cwl_utils/cwl_v1_2_expression_refactor.py @@ -712,7 +712,9 @@ def process_workflow_inputs_and_outputs( source_type_items.append("null") elif source_type_items != "null": source_type_items = ["null", source_type_items] - source_type = cwl.CommandInputParameter(type_=source_type_items) + source_type = cwl.CommandInputParameter( + id=None, type_=source_type_items + ) replace_expr_with_etool( expression, etool_id, diff --git a/cwl_utils/parser/cwl_v1_2.py b/cwl_utils/parser/cwl_v1_2.py index b24ac23c..6bfc4de4 100644 --- a/cwl_utils/parser/cwl_v1_2.py +++ b/cwl_utils/parser/cwl_v1_2.py @@ -13750,12 +13750,12 @@ class CommandInputParameter(InputParameter): def __init__( self, + id: Any, type_: Any, label: Optional[Any] = None, secondaryFiles: Optional[Any] = None, streamable: Optional[Any] = None, doc: Optional[Any] = None, - id: Optional[Any] = None, format: Optional[Any] = None, loadContents: Optional[Any] = None, loadListing: Optional[Any] = None, @@ -13837,7 +13837,7 @@ def fromDoc( try: id = load_field( _doc.get("id"), - uri_union_of_None_type_or_strtype_True_False_None_None, + uri_strtype_True_False_None_None, baseuri, loadingOptions, lc=_doc.get("id") @@ -13885,7 +13885,7 @@ def fromDoc( if docRoot is not None: id = docRoot else: - id = "_:" + str(_uuid__.uuid4()) + _errors__.append(ValidationException("missing id")) if not __original_id_is_none: baseuri = cast(str, id) label = None @@ -14504,12 +14504,12 @@ class CommandOutputParameter(OutputParameter): def __init__( self, + id: Any, type_: Any, label: Optional[Any] = None, secondaryFiles: Optional[Any] = None, streamable: Optional[Any] = None, doc: Optional[Any] = None, - id: Optional[Any] = None, format: Optional[Any] = None, outputBinding: Optional[Any] = None, extension_fields: Optional[dict[str, Any]] = None, @@ -14579,7 +14579,7 @@ def fromDoc( try: id = load_field( _doc.get("id"), - uri_union_of_None_type_or_strtype_True_False_None_None, + uri_strtype_True_False_None_None, baseuri, loadingOptions, lc=_doc.get("id") @@ -14627,7 +14627,7 @@ def fromDoc( if docRoot is not None: id = docRoot else: - id = "_:" + str(_uuid__.uuid4()) + _errors__.append(ValidationException("missing id")) if not __original_id_is_none: baseuri = cast(str, id) label = None @@ -19322,12 +19322,12 @@ class ExpressionToolOutputParameter(OutputParameter): def __init__( self, + id: Any, type_: Any, label: Optional[Any] = None, secondaryFiles: Optional[Any] = None, streamable: Optional[Any] = None, doc: Optional[Any] = None, - id: Optional[Any] = None, format: Optional[Any] = None, extension_fields: Optional[dict[str, Any]] = None, loadingOptions: Optional[LoadingOptions] = None, @@ -19393,7 +19393,7 @@ def fromDoc( try: id = load_field( _doc.get("id"), - uri_union_of_None_type_or_strtype_True_False_None_None, + uri_strtype_True_False_None_None, baseuri, loadingOptions, lc=_doc.get("id") @@ -19441,7 +19441,7 @@ def fromDoc( if docRoot is not None: id = docRoot else: - id = "_:" + str(_uuid__.uuid4()) + _errors__.append(ValidationException("missing id")) if not __original_id_is_none: baseuri = cast(str, id) label = None @@ -19827,12 +19827,12 @@ class WorkflowInputParameter(InputParameter): def __init__( self, + id: Any, type_: Any, label: Optional[Any] = None, secondaryFiles: Optional[Any] = None, streamable: Optional[Any] = None, doc: Optional[Any] = None, - id: Optional[Any] = None, format: Optional[Any] = None, loadContents: Optional[Any] = None, loadListing: Optional[Any] = None, @@ -19914,7 +19914,7 @@ def fromDoc( try: id = load_field( _doc.get("id"), - uri_union_of_None_type_or_strtype_True_False_None_None, + uri_strtype_True_False_None_None, baseuri, loadingOptions, lc=_doc.get("id") @@ -19962,7 +19962,7 @@ def fromDoc( if docRoot is not None: id = docRoot else: - id = "_:" + str(_uuid__.uuid4()) + _errors__.append(ValidationException("missing id")) if not __original_id_is_none: baseuri = cast(str, id) label = None @@ -21312,12 +21312,12 @@ class WorkflowOutputParameter(OutputParameter): def __init__( self, + id: Any, type_: Any, label: Optional[Any] = None, secondaryFiles: Optional[Any] = None, streamable: Optional[Any] = None, doc: Optional[Any] = None, - id: Optional[Any] = None, format: Optional[Any] = None, outputSource: Optional[Any] = None, linkMerge: Optional[Any] = None, @@ -21395,7 +21395,7 @@ def fromDoc( try: id = load_field( _doc.get("id"), - uri_union_of_None_type_or_strtype_True_False_None_None, + uri_strtype_True_False_None_None, baseuri, loadingOptions, lc=_doc.get("id") @@ -21443,7 +21443,7 @@ def fromDoc( if docRoot is not None: id = docRoot else: - id = "_:" + str(_uuid__.uuid4()) + _errors__.append(ValidationException("missing id")) if not __original_id_is_none: baseuri = cast(str, id) label = None @@ -22111,7 +22111,7 @@ class WorkflowStepInput(Identified, Sink, LoadContents, Labeled): def __init__( self, - id: Optional[Any] = None, + id: Any, source: Optional[Any] = None, linkMerge: Optional[Any] = None, pickValue: Optional[Any] = None, @@ -22190,7 +22190,7 @@ def fromDoc( try: id = load_field( _doc.get("id"), - uri_union_of_None_type_or_strtype_True_False_None_None, + uri_strtype_True_False_None_None, baseuri, loadingOptions, lc=_doc.get("id") @@ -22238,7 +22238,7 @@ def fromDoc( if docRoot is not None: id = docRoot else: - id = "_:" + str(_uuid__.uuid4()) + _errors__.append(ValidationException("missing id")) if not __original_id_is_none: baseuri = cast(str, id) source = None @@ -22749,7 +22749,7 @@ class WorkflowStepOutput(Identified): def __init__( self, - id: Optional[Any] = None, + id: Any, extension_fields: Optional[dict[str, Any]] = None, loadingOptions: Optional[LoadingOptions] = None, ) -> None: @@ -22790,7 +22790,7 @@ def fromDoc( try: id = load_field( _doc.get("id"), - uri_union_of_None_type_or_strtype_True_False_None_None, + uri_strtype_True_False_None_None, baseuri, loadingOptions, lc=_doc.get("id") @@ -22838,7 +22838,7 @@ def fromDoc( if docRoot is not None: id = docRoot else: - id = "_:" + str(_uuid__.uuid4()) + _errors__.append(ValidationException("missing id")) if not __original_id_is_none: baseuri = cast(str, id) extension_fields: dict[str, Any] = {} @@ -22985,10 +22985,10 @@ class WorkflowStep(Identified, Labeled, Documented): def __init__( self, + id: Any, in_: Any, out: Any, run: Any, - id: Optional[Any] = None, label: Optional[Any] = None, doc: Optional[Any] = None, requirements: Optional[Any] = None, @@ -23072,7 +23072,7 @@ def fromDoc( try: id = load_field( _doc.get("id"), - uri_union_of_None_type_or_strtype_True_False_None_None, + uri_strtype_True_False_None_None, baseuri, loadingOptions, lc=_doc.get("id") @@ -23120,7 +23120,7 @@ def fromDoc( if docRoot is not None: id = docRoot else: - id = "_:" + str(_uuid__.uuid4()) + _errors__.append(ValidationException("missing id")) if not __original_id_is_none: baseuri = cast(str, id) label = None @@ -24972,12 +24972,12 @@ class OperationInputParameter(InputParameter): def __init__( self, + id: Any, type_: Any, label: Optional[Any] = None, secondaryFiles: Optional[Any] = None, streamable: Optional[Any] = None, doc: Optional[Any] = None, - id: Optional[Any] = None, format: Optional[Any] = None, loadContents: Optional[Any] = None, loadListing: Optional[Any] = None, @@ -25055,7 +25055,7 @@ def fromDoc( try: id = load_field( _doc.get("id"), - uri_union_of_None_type_or_strtype_True_False_None_None, + uri_strtype_True_False_None_None, baseuri, loadingOptions, lc=_doc.get("id") @@ -25103,7 +25103,7 @@ def fromDoc( if docRoot is not None: id = docRoot else: - id = "_:" + str(_uuid__.uuid4()) + _errors__.append(ValidationException("missing id")) if not __original_id_is_none: baseuri = cast(str, id) label = None @@ -25667,12 +25667,12 @@ class OperationOutputParameter(OutputParameter): def __init__( self, + id: Any, type_: Any, label: Optional[Any] = None, secondaryFiles: Optional[Any] = None, streamable: Optional[Any] = None, doc: Optional[Any] = None, - id: Optional[Any] = None, format: Optional[Any] = None, extension_fields: Optional[dict[str, Any]] = None, loadingOptions: Optional[LoadingOptions] = None, @@ -25738,7 +25738,7 @@ def fromDoc( try: id = load_field( _doc.get("id"), - uri_union_of_None_type_or_strtype_True_False_None_None, + uri_strtype_True_False_None_None, baseuri, loadingOptions, lc=_doc.get("id") @@ -25786,7 +25786,7 @@ def fromDoc( if docRoot is not None: id = docRoot else: - id = "_:" + str(_uuid__.uuid4()) + _errors__.append(ValidationException("missing id")) if not __original_id_is_none: baseuri = cast(str, id) label = None From 6f4a2d3d75508130ec1617f8b279da34db5547e7 Mon Sep 17 00:00:00 2001 From: "Michael R. Crusoe" Date: Mon, 8 Dec 2025 10:50:01 +0100 Subject: [PATCH 2/2] now with IdentifierRequired --- cwl_utils/parser/cwl_v1_2.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/cwl_utils/parser/cwl_v1_2.py b/cwl_utils/parser/cwl_v1_2.py index 6bfc4de4..ffbb104d 100644 --- a/cwl_utils/parser/cwl_v1_2.py +++ b/cwl_utils/parser/cwl_v1_2.py @@ -4378,6 +4378,10 @@ class Identified(Saveable): pass +class IdentifierRequired(Identified): + pass + + class LoadContents(Saveable): pass @@ -4394,7 +4398,7 @@ class OutputFormat(Saveable): pass -class Parameter(FieldBase, Documented, Identified): +class Parameter(FieldBase, Documented, IdentifierRequired): """ Define an input or output parameter to a process. @@ -21994,7 +21998,7 @@ class Sink(Saveable): pass -class WorkflowStepInput(Identified, Sink, LoadContents, Labeled): +class WorkflowStepInput(IdentifierRequired, Sink, LoadContents, Labeled): """ The input of a workflow step connects an upstream parameter (from the workflow inputs, or the outputs of other workflows steps) with the input @@ -22732,7 +22736,7 @@ def save( ) -class WorkflowStepOutput(Identified): +class WorkflowStepOutput(IdentifierRequired): """ Associate an output parameter of the underlying process with a workflow parameter. The workflow parameter (given in the `id` field) be may be used @@ -22897,7 +22901,7 @@ def save( attrs = frozenset(["id"]) -class WorkflowStep(Identified, Labeled, Documented): +class WorkflowStep(IdentifierRequired, Labeled, Documented): """ A workflow step is an executable element of a workflow. It specifies the underlying process implementation (such as `CommandLineTool` or another @@ -29219,6 +29223,7 @@ def save( "File": "https://w3id.org/cwl/cwl#File", "IOSchema": "https://w3id.org/cwl/cwl#IOSchema", "Identified": "https://w3id.org/cwl/cwl#Identified", + "IdentifierRequired": "https://w3id.org/cwl/cwl#IdentifierRequired", "InitialWorkDirRequirement": "https://w3id.org/cwl/cwl#InitialWorkDirRequirement", "InlineJavascriptRequirement": "https://w3id.org/cwl/cwl#InlineJavascriptRequirement", "InplaceUpdateRequirement": "https://w3id.org/cwl/cwl#InplaceUpdateRequirement", @@ -29371,6 +29376,7 @@ def save( "https://w3id.org/cwl/cwl#File": "File", "https://w3id.org/cwl/cwl#IOSchema": "IOSchema", "https://w3id.org/cwl/cwl#Identified": "Identified", + "https://w3id.org/cwl/cwl#IdentifierRequired": "IdentifierRequired", "https://w3id.org/cwl/cwl#InitialWorkDirRequirement": "InitialWorkDirRequirement", "https://w3id.org/cwl/cwl#InlineJavascriptRequirement": "InlineJavascriptRequirement", "https://w3id.org/cwl/cwl#InplaceUpdateRequirement": "InplaceUpdateRequirement",