12
12
from ruamel .yaml import scalarstring
13
13
from ruamel .yaml .main import YAML
14
14
15
- from . import _logger
16
- from .errors import WDLSourceLine
15
+ from wdl2cwl import _logger
16
+ from wdl2cwl .errors import WDLSourceLine
17
17
18
18
valid_js_identifier = regex .compile (
19
19
r"^(?!(?:do|if|in|for|let|new|try|var|case|else|enum|eval|null|this|true|"
@@ -69,6 +69,32 @@ def get_cwl_type(input_type: WDL.Type.Base) -> str:
69
69
return type_of
70
70
71
71
72
+ def get_mem_in_bytes (unit : str ) -> str :
73
+ """Determine the value of a memory unit in bytes."""
74
+ with WDLSourceLine (unit , ConversionException ):
75
+ if unit == "KiB" or unit == "Ki" :
76
+ mem_in_bytes = "1024^1"
77
+ elif unit == "MiB" or unit == "Mi" :
78
+ mem_in_bytes = "1024^2"
79
+ elif unit == "GiB" or unit == "Gi" :
80
+ mem_in_bytes = "1024^3"
81
+ elif unit == "TiB" or unit == "Ti" :
82
+ mem_in_bytes = "1024^4"
83
+ elif unit == "B" :
84
+ mem_in_bytes = "1024^0"
85
+ elif unit == "KB" or unit == "K" :
86
+ mem_in_bytes = "1000^1"
87
+ elif unit == "MB" or unit == "M" :
88
+ mem_in_bytes = "1000^2"
89
+ elif unit == "GB" or unit == "G" :
90
+ mem_in_bytes = "1000^3"
91
+ elif unit == "TB" or unit == "T" :
92
+ mem_in_bytes = "1000^4"
93
+ else :
94
+ raise ConversionException (f"Invalid memory unit: ${ unit } " )
95
+ return mem_in_bytes
96
+
97
+
72
98
def get_outdir_requirement (outdir : Union [WDL .Expr .Get , WDL .Expr .Apply ]) -> int :
73
99
"""Produce the memory requirement for the output directory from WDL runtime disks."""
74
100
# This is yet to be implemented. After Feature Parity.
@@ -125,7 +151,7 @@ def get_literal_name(
125
151
# if the literal expr is used inside WDL.Expr.Apply
126
152
# the literal value is what's needed
127
153
parent = expr .parent # type: ignore[union-attr]
128
- if isinstance (parent , WDL .Expr .Apply ):
154
+ if isinstance (parent , ( WDL .Expr .Apply , WDL . Expr . IfThenElse ) ):
129
155
return expr .literal .value # type: ignore
130
156
raise WDLSourceLine (expr , ConversionException ).makeError (
131
157
f"The parent expression for { expr } is not WDL.Expr.Apply, but { parent } ."
@@ -289,9 +315,6 @@ def load_wdl_task(self, obj: WDL.Tree.Task) -> cwl.CommandLineTool:
289
315
_logger .warning ("Skipping parameter_meta: %s" , obj .parameter_meta )
290
316
if obj .meta :
291
317
_logger .warning ("Skipping meta: %s" , obj .meta )
292
- if len (obj .postinputs ) > 0 :
293
- for a in obj .postinputs :
294
- _logger .warning ("Skipping variable: %s" , a )
295
318
return cwl .CommandLineTool (
296
319
id = obj .name ,
297
320
inputs = cwl_inputs ,
@@ -356,7 +379,7 @@ def get_time_minutes_requirement(
356
379
self , time_minutes : WDL .Expr .Get
357
380
) -> Union [str , int ]:
358
381
"""Produce the time limit expression from WDL runtime time minutes."""
359
- with WDLSourceLine (time_minutes , WDLSourceLine ):
382
+ with WDLSourceLine (time_minutes , ConversionException ):
360
383
if isinstance (time_minutes , (WDL .Expr .Int , WDL .Expr .Float )):
361
384
literal = time_minutes .literal .value # type: ignore
362
385
return literal * 60 # type: ignore
@@ -379,7 +402,10 @@ def get_memory_literal(self, memory_runtime: WDL.Expr.String) -> float:
379
402
if memory_runtime .literal is None :
380
403
_ , placeholder , unit , _ = memory_runtime .parts
381
404
with WDLSourceLine (placeholder , ConversionException ):
382
- value_name = self .get_expr_get (placeholder .expr ) # type: ignore
405
+ if isinstance (placeholder .expr , WDL .Expr .Get ): # type: ignore
406
+ value_name = self .get_expr_get (placeholder .expr ) # type: ignore
407
+ else :
408
+ value_name = self .get_expr_apply (placeholder .expr ) # type: ignore
383
409
return self .get_ram_min_js (value_name , unit .strip ()) # type: ignore
384
410
385
411
ram_min = self .get_expr_string (memory_runtime )[1 :- 1 ]
@@ -388,27 +414,8 @@ def get_memory_literal(self, memory_runtime: WDL.Expr.String) -> float:
388
414
raise ConversionException ("Missing Memory units, yet still a string?" )
389
415
unit = unit_result .group ()
390
416
value = float (ram_min .split (unit )[0 ])
391
-
392
- if unit == "KiB" :
393
- memory = value / 1024
394
- elif unit == "MiB" :
395
- memory = value
396
- elif unit == "GiB" :
397
- memory = value * 1024
398
- elif unit == "TiB" :
399
- memory = value * 1024 * 1024
400
- elif unit == "B" :
401
- memory = value / (1024 * 1024 )
402
- elif unit == "KB" or unit == "K" :
403
- memory = (value * 1000 ) / (1024 * 1024 )
404
- elif unit == "MB" or unit == "M" :
405
- memory = (value * (1000 * 1000 )) / (1024 * 1024 )
406
- elif unit == "GB" or unit == "G" :
407
- memory = (value * (1000 * 1000 * 1000 )) / (1024 * 1024 )
408
- elif unit == "TB" or unit == "T" :
409
- memory = (value * (1000 * 1000 * 1000 * 1000 )) / (1024 * 1024 )
410
- else :
411
- raise ConversionException (f"Invalid memory unit: ${ unit } " )
417
+ byte , power = get_mem_in_bytes (unit ).split ("^" )
418
+ memory : float = value * float (byte ) ** float (power ) / (1024 * 1024 )
412
419
413
420
return memory
414
421
@@ -586,9 +593,25 @@ def get_expr_apply(self, wdl_apply_expr: WDL.Expr.Apply) -> str:
586
593
return f"{ iterable_object_expr } [{ index_expr } ]"
587
594
elif function_name == "_gt" :
588
595
left_operand , right_operand = arguments
589
- left_operand_expr = self .get_expr_apply (left_operand ) # type: ignore
596
+ if isinstance (left_operand , WDL .Expr .Get ):
597
+ left_operand_expr = self .get_expr (left_operand )
598
+ else :
599
+ left_operand_expr = self .get_expr_apply (left_operand ) # type: ignore
590
600
right_operand_expr = self .get_expr (right_operand )
591
601
return f"{ left_operand_expr } > { right_operand_expr } "
602
+ elif function_name == "_lt" :
603
+ left_operand , right_operand = arguments
604
+ if isinstance (left_operand , WDL .Expr .Get ):
605
+ left_operand_expr = self .get_expr (left_operand )
606
+ else :
607
+ left_operand_expr = self .get_expr_apply (left_operand ) # type: ignore
608
+ right_operand_expr = self .get_expr (right_operand )
609
+ return f"{ left_operand_expr } < { right_operand_expr } "
610
+ elif function_name == "_lor" :
611
+ left_operand , right_operand = arguments
612
+ left_operand_expr = self .get_expr_apply (left_operand ) # type: ignore
613
+ right_operand_expr = self .get_expr (right_operand )
614
+ return f"{ left_operand_expr } || { right_operand_expr } "
592
615
elif function_name == "length" :
593
616
only_arg_expr = self .get_expr_get (arguments [0 ]) # type: ignore
594
617
return f"{ only_arg_expr } .length"
@@ -602,27 +625,77 @@ def get_expr_apply(self, wdl_apply_expr: WDL.Expr.Apply) -> str:
602
625
elif function_name == "read_string" :
603
626
only_arg = arguments [0 ]
604
627
return self .get_expr (only_arg )
628
+ elif function_name == "read_float" :
629
+ only_arg = arguments [0 ]
630
+ return self .get_expr (only_arg )
605
631
elif function_name == "glob" :
606
632
only_arg = arguments [0 ]
607
633
return self .get_expr (only_arg )
634
+ elif function_name == "select_first" :
635
+ array_obj = arguments [0 ]
636
+ array_items = [self .get_expr (item ) for item in array_obj .items ] # type: ignore
637
+ items_str = ", " .join (array_items )
638
+ return f"[{ items_str } ].find(element => element !== null) "
639
+ elif function_name == "_mul" :
640
+ left_operand , right_operand = arguments
641
+ left_str = self .get_expr (left_operand )
642
+ right_str = self .get_expr (right_operand )
643
+ return f"{ left_str } *{ right_str } "
644
+ elif function_name == "_eqeq" :
645
+ left_operand , right_operand = arguments
646
+ left_str = self .get_expr (left_operand )
647
+ right_str = self .get_expr (right_operand )
648
+ return f"{ left_str } === { right_str } "
649
+ elif function_name == "ceil" :
650
+ only_arg = self .get_expr (arguments [0 ]) # type: ignore
651
+ return f"Math.ceil({ only_arg } ) "
652
+ elif function_name == "_div" :
653
+ left_operand , right_operand = arguments
654
+ left_str = self .get_expr (left_operand )
655
+ right_str = self .get_expr (right_operand )
656
+ return f"{ left_str } /{ right_str } "
657
+ elif function_name == "_sub" :
658
+ left_operand , right_operand = arguments
659
+ left_str = self .get_expr (left_operand )
660
+ right_str = self .get_expr (right_operand )
661
+ return f"{ left_str } -{ right_str } "
662
+ elif function_name == "size" :
663
+ left_operand , right_operand = arguments
664
+ if isinstance (left_operand , WDL .Expr .Array ):
665
+ array_items = [self .get_expr (item ) for item in left_operand .items ]
666
+ left = ", " .join (array_items )
667
+ left_str = f"[{ left } ]"
668
+ else :
669
+ left_str = self .get_expr (left_operand )
670
+ size_unit = self .get_expr (right_operand )[1 :- 1 ]
671
+ unit_value = get_mem_in_bytes (size_unit )
672
+ return (
673
+ "(function(size_of=0)"
674
+ + "{"
675
+ + f"{ left_str } .forEach(function(element)"
676
+ + "{ if (element) {"
677
+ + "size_of += element.size"
678
+ + "}})}"
679
+ + f") / { unit_value } "
680
+ )
608
681
609
682
raise WDLSourceLine (wdl_apply_expr , ConversionException ).makeError (
610
683
f"Function name '{ function_name } ' not yet handled."
611
684
)
612
685
613
686
def get_expr_get (self , wdl_get_expr : WDL .Expr .Get ) -> str :
614
687
"""Translate WDL Get Expressions."""
615
- with WDLSourceLine ( wdl_get_expr , ConversionException ):
616
- member = wdl_get_expr . member
617
- if (
618
- not member
619
- and isinstance (wdl_get_expr .expr , WDL . Expr . Ident )
620
- and wdl_get_expr . expr
621
- ):
622
- return self . get_expr_ident ( wdl_get_expr . expr )
623
- raise ConversionException (
624
- f"Get expressions with { member } are not yet handled. "
625
- )
688
+ member = wdl_get_expr . member
689
+
690
+ if not member :
691
+ return self . get_expr_ident ( wdl_get_expr . expr ) # type: ignore
692
+ struct_name = self . get_expr (wdl_get_expr .expr )
693
+ member_str = f" { struct_name } . { member } "
694
+ return (
695
+ member_str
696
+ if not isinstance ( wdl_get_expr . type , WDL . Type . File )
697
+ else f" { member_str } .path "
698
+ )
626
699
627
700
def get_expr_ident (self , wdl_ident_expr : WDL .Expr .Ident ) -> str :
628
701
"""Translate WDL Ident Expressions."""
@@ -766,22 +839,37 @@ def get_cwl_task_inputs(
766
839
input_name = wdl_input .name
767
840
self .non_static_values .add (input_name )
768
841
input_value = None
769
- type_of : Union [str , cwl .CommandInputArraySchema ]
842
+ type_of : Union [
843
+ str , cwl .CommandInputArraySchema , cwl .CommandInputRecordSchema
844
+ ]
770
845
771
846
if hasattr (wdl_input , "value" ):
772
847
wdl_input = wdl_input .value # type: ignore
773
848
774
849
if isinstance (wdl_input .type , WDL .Type .Array ):
775
850
input_type = get_cwl_type (wdl_input .type .item_type )
776
851
type_of = cwl .CommandInputArraySchema (items = input_type , type = "array" )
852
+ elif isinstance (wdl_input .type , WDL .Type .StructInstance ):
853
+ type_of = cwl .CommandInputRecordSchema (
854
+ type = "record" ,
855
+ name = wdl_input .type .type_name ,
856
+ fields = self .get_struct_inputs (wdl_input .type .members ),
857
+ )
777
858
else :
778
859
type_of = get_cwl_type (wdl_input .type )
779
860
780
861
if wdl_input .type .optional or isinstance (wdl_input .expr , WDL .Expr .Apply ):
781
862
final_type_of : Union [
782
- List [Union [str , cwl .CommandInputArraySchema ]],
863
+ List [
864
+ Union [
865
+ str ,
866
+ cwl .CommandInputArraySchema ,
867
+ cwl .CommandInputRecordSchema ,
868
+ ]
869
+ ],
783
870
str ,
784
871
cwl .CommandInputArraySchema ,
872
+ cwl .CommandInputRecordSchema ,
785
873
] = [type_of , "null" ]
786
874
if isinstance (wdl_input .expr , WDL .Expr .Apply ):
787
875
self .optional_cwl_null .add (input_name )
@@ -805,6 +893,24 @@ def get_cwl_task_inputs(
805
893
806
894
return inputs
807
895
896
+ def get_struct_inputs (
897
+ self , members : Optional [Dict [str , WDL .Type .Base ]]
898
+ ) -> List [cwl .CommandInputRecordField ]:
899
+ """Get member items of a WDL struct and return a list of cwl.CommandInputRecordField."""
900
+ inputs : List [cwl .CommandInputRecordField ] = []
901
+ if not members :
902
+ return inputs
903
+ for member , value in members .items ():
904
+ input_name = member
905
+ if isinstance (value , WDL .Type .Array ):
906
+ array_items_type = value .item_type
907
+ input_type = get_cwl_type (array_items_type )
908
+ type_of = cwl .CommandInputArraySchema (items = input_type , type = "array" )
909
+ else :
910
+ type_of = get_cwl_type (value ) # type: ignore
911
+ inputs .append (cwl .CommandInputRecordField (name = input_name , type = type_of ))
912
+ return inputs
913
+
808
914
def get_cwl_task_outputs (
809
915
self , wdl_outputs : List [WDL .Tree .Decl ]
810
916
) -> List [cwl .CommandOutputParameter ]:
@@ -856,6 +962,30 @@ def get_cwl_task_outputs(
856
962
),
857
963
)
858
964
)
965
+ elif (
966
+ isinstance (wdl_output .expr , WDL .Expr .Apply )
967
+ and wdl_output .expr .function_name == "read_float"
968
+ ):
969
+ glob_expr = self .get_expr (wdl_output )
970
+ is_literal = wdl_output .expr .arguments [0 ].literal
971
+ if is_literal :
972
+ glob_str = glob_expr [
973
+ 1 :- 1
974
+ ] # remove quotes from the string returned by get_expr_string
975
+ else :
976
+ glob_str = f"$({ glob_expr } )"
977
+
978
+ outputs .append (
979
+ cwl .CommandOutputParameter (
980
+ id = output_name ,
981
+ type = type_of ,
982
+ outputBinding = cwl .CommandOutputBinding (
983
+ glob = glob_str ,
984
+ loadContents = True ,
985
+ outputEval = r"$(parseFloat(self[0].contents))" ,
986
+ ),
987
+ )
988
+ )
859
989
elif (
860
990
isinstance (wdl_output .expr , WDL .Expr .Apply )
861
991
and wdl_output .expr .function_name == "stdout"
0 commit comments