@@ -841,9 +841,14 @@ def compile_workflow_once(yaml_tree_ast: YamlTree,
841841 steps_list .append (step_i_copy )
842842 yaml_tree .update ({'steps' : steps_list }) # steps_list ?
843843
844+ # --------------------------------------------------------
845+ # Helpers for generating yaml_inputs for workflow execution from inputs_workflow
846+ # --------------------------------------------------------
844847 def type_includes_null (cwl_type : Any ) -> bool :
845848 if isinstance (cwl_type , list ):
846849 return "null" in cwl_type
850+ if isinstance (cwl_type , str ) and cwl_type .endswith ("?" ):
851+ return True
847852 return False
848853
849854 def strip_null_from_union (cwl_type : Any ) -> Any :
@@ -852,99 +857,114 @@ def strip_null_from_union(cwl_type: Any) -> Any:
852857 if len (non_null ) == 1 :
853858 return non_null [0 ]
854859 return non_null
860+ if isinstance (cwl_type , str ) and cwl_type .endswith ("?" ):
861+ return cwl_type [:- 1 ]
855862 return cwl_type
856863
857864 def emit_file_or_dir (type_name : str , value : Any , fmt : Any = None ) -> Dict [str , Any ]:
858- obj = {
865+ obj : Dict [ str , Any ] = {
859866 "class" : type_name ,
860867 "location" : value
861868 }
862869 if type_name == "File" and fmt :
863870 obj ["format" ] = fmt
864871 return obj
865872
866- def populate_scalar_val (in_dict : Dict [str , Any ]) -> Any :
867- raw_type = in_dict ["type" ]
868- value = in_dict .get ("value" )
869- fmt = in_dict .get ("format" )
870-
871- # Optional null handling is done outside this function
872- cwl_type = strip_null_from_union (raw_type )
873-
874- # File
875- if cwl_type == "File" :
876- return emit_file_or_dir ("File" , value , fmt )
877-
878- # Directory
879- if cwl_type == "Directory" :
880- return emit_file_or_dir ("Directory" , value )
881-
882- # Primitive types
883- if cwl_type in ("string" , "string?" ):
884- return str (value )
885-
886- if cwl_type in ("int" , "int?" ):
887- return int (value )
888-
889- if cwl_type in ("float" , "float?" ):
890- return float (value )
891-
892- if cwl_type in ("boolean" , "boolean?" ):
893- return bool (value )
894-
895- # Fallback for unknown types
896- return value
897-
873+ # --------------------------------------------------------
874+ # Scalar emission for each primitive type, as well as File and Directory
875+ # --------------------------------------------------------
876+ def populate_scalar_val (cwl_type : Any , value : Any , fmt : Any = None ) -> Any :
877+ match cwl_type :
878+ case "File" :
879+ obj : Dict [str , Any ] = {
880+ "class" : "File" ,
881+ "location" : value
882+ }
883+ if fmt :
884+ obj ["format" ] = fmt
885+ return obj
886+
887+ case "Directory" :
888+ return {
889+ "class" : "Directory" ,
890+ "location" : value
891+ }
892+
893+ case "string" :
894+ return str (value )
895+
896+ case "int" :
897+ return int (value )
898+
899+ case "float" :
900+ return float (value )
901+
902+ case "boolean" :
903+ return bool (value )
904+
905+ case _:
906+ # Unknown or already structured type
907+ return value
908+
909+ # --------------------------------------------------------
910+ # Main input handling for yaml_inputs
911+ # --------------------------------------------------------
898912 def populate_input_value (in_dict : Dict [str , Any ]) -> Any :
899913 raw_type = in_dict ["type" ]
900914 value = in_dict .get ("value" )
915+ fmt = in_dict .get ("format" )
901916
902- # --- Handle optional null case ---
917+ # ---------- Null handling ------- ---
903918 if value is None :
904919 if type_includes_null (raw_type ):
905- return None # caller will omit key
906- else :
907- raise ValueError (
908- f"Required input of type { raw_type } was not provided."
909- )
920+ return None
921+ raise ValueError (
922+ f"Required input of type { raw_type } was not provided."
923+ )
910924
911- # --- Handle array types ---
912- # Case 1: {"type": "array", "items": ...}
913- if isinstance (raw_type , dict ) and raw_type .get ("type" ) == "array" :
914- item_type = raw_type ["items" ]
925+ # Normalize type (strip null / ?)
926+ cwl_type = strip_null_from_union (raw_type )
927+
928+ # ---------- Array handling ----------
929+ # Case: {"type": "array", "items": ...}
930+ if isinstance (cwl_type , dict ) and cwl_type .get ("type" ) == "array" :
931+ item_type = strip_null_from_union (cwl_type ["items" ])
932+ if not isinstance (value , list ):
933+ raise TypeError (
934+ f"Expected list for array type but got { type (value ).__name__ } "
935+ )
915936 return [
916- populate_scalar_val ({
917- "type" : item_type ,
918- "value" : val ,
919- "format" : in_dict .get ("format" )
920- })
921- for val in value
937+ populate_scalar_val (item_type , v , fmt )
938+ for v in value
922939 ]
923940
924- # Case 2: union including array
925- if isinstance (raw_type , list ):
926- for t in raw_type :
941+ # Case: Union containing array
942+ if isinstance (cwl_type , list ):
943+ for t in cwl_type :
927944 if isinstance (t , dict ) and t .get ("type" ) == "array" :
928- item_type = t ["items" ]
945+ item_type = strip_null_from_union (t ["items" ])
946+
947+ if not isinstance (value , list ):
948+ raise TypeError (
949+ f"Expected list for array type but got { type (value ).__name__ } "
950+ )
951+
929952 return [
930- populate_scalar_val ({
931- "type" : item_type ,
932- "value" : val ,
933- "format" : in_dict .get ("format" )
934- })
935- for val in value
953+ populate_scalar_val (item_type , v , fmt )
954+ for v in value
936955 ]
937956
938- # --- Scalar case ---
939- return populate_scalar_val (in_dict )
957+ # ---------- Scalar case ------- ---
958+ return populate_scalar_val (cwl_type , value , fmt )
940959
941- # ------------------------------
942- # Generate the final workflow inputs by populating the values from the inputs file.
943- # ------------------------------
960+ # --------------------------------------------------------
961+ # Generate yaml_inputs by populating values from inputs_file_workflow
962+ # To be dumped in *_yaml_inputs.yml and passed to CWL workflow execution as --inputs-file
963+ # --------------------------------------------------------
944964 yaml_inputs : WorkflowInputsFile = {}
945965 for key , in_dict in inputs_file_workflow .items ():
946966 val = populate_input_value (in_dict )
947- # Omit optional- null values only
967+ # Omit optional null fields only
948968 if val is None :
949969 continue
950970 yaml_inputs [key ] = val
0 commit comments