def outputs(self) -> List[j.TOutput]: return [ j.TOutput("out_file", j.File, doc="Single file to compare"), j.TOutput("line_count", j.Int, doc="Number of lines in the file"), j.TOutput("misc_files", j.Array(j.File), doc="array of output file, to show array index"), ]
def ingest_cwl_type(self, cwl_type, secondary_files): inp_type = self.from_cwl_inner_type(cwl_type) if secondary_files: array_optional_layers = [] while isinstance(inp_type, j.Array): array_optional_layers.append(inp_type.optional) inp_type = inp_type.subtype() inp_type = self.get_data_type_from_secondaries( secondaries=self.process_secondary_files(secondary_files), optional=inp_type.optional, ) for is_optional in array_optional_layers[::-1]: inp_type = j.Array(inp_type, optional=is_optional) return inp_type
def parse_wdl_type(self, t: WDL.Type.Base): optional = t.optional if isinstance(t, WDL.Type.Int): return j.Int(optional=optional) elif isinstance(t, WDL.Type.String): return j.String(optional=optional) elif isinstance(t, WDL.Type.Float): return j.Float(optional=optional) elif isinstance(t, WDL.Type.Boolean): return j.Boolean(optional=optional) elif isinstance(t, WDL.Type.File): return j.File(optional=optional) elif isinstance(t, WDL.Type.Directory): return j.Directory(optional=optional) elif isinstance(t, WDL.Type.Array): return j.Array(self.parse_wdl_type(t.item_type), optional=optional) raise Exception( f"Didn't handle WDL type conversion for '{t}' ({type(t)})")
# This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import janis_core as j # from ... import ToolWithTwoInputs wf = j.WorkflowBuilder("scatter_execution") # Two inputs that can have different lengths wf.input("input1", j.Array(j.String)) wf.input("input2", j.Array(j.String)) wf.step( "run_toolwithtwoinputs", # Connect the inputs as normal ToolWithTwoInputs(toolInput1=wf.input1, toolInput2=wf.input2), # Scatter of the 'inp' input of Echo. scatter=j.ScatterDescription( fields=["toolInput1", "toolInput2"], method=j.ScatterMethod.cross ), ) # This will automatically be an array of files # because wf.run_toolwithtwoinputs has a scatter block. wf.output("out", source=wf.run_toolwithtwoinputs.out)
inputs=[j.ToolInput("inp", MyDataTypeWithSecondaries, position=0)], outputs=[ j.ToolOutput( "out", MyDataTypeWithSecondaries, selector="file.txt", ) ], ) WorkflowWithSecondaries = j.WorkflowBuilder("workflow_with_secondaries") inner_dt = MyDataTypeWithSecondaries scatter = None # helper method to show how scatters / not scatters work should_scatter = False if should_scatter: inner_dt = j.Array(MyDataTypeWithSecondaries) scatter = "inp" WorkflowWithSecondaries.input("inp", inner_dt) WorkflowWithSecondaries.step( "stp", ToolWithSecondaryFiles(inp=WorkflowWithSecondaries.inp), scatter=scatter ) WorkflowWithSecondaries.output("out", source=WorkflowWithSecondaries.stp.out) if __name__ == "__main__": ToolWithSecondaryFiles().translate("wdl") # WorkflowWithSecondaries().translate("wdl")
def from_cwl_inner_type(self, cwl_type) -> j.DataType: if isinstance(cwl_type, str): optional = "?" in cwl_type cwl_type = cwl_type.replace("?", "") array_count = 0 while cwl_type.endswith("[]"): array_count += 1 cwl_type = cwl_type[:-2] if cwl_type == "File": inner = j.File elif cwl_type == "Directory": inner = j.Directory elif cwl_type == "string": inner = j.String elif cwl_type == "int": inner = j.Int elif cwl_type == "float": inner = j.Float elif cwl_type == "boolean": inner = j.Boolean elif cwl_type == "stdout": inner = j.Stdout elif cwl_type == "stderr": inner = j.Stderr elif cwl_type == "Any": inner = j.String elif cwl_type == "long": inner = j.Int else: raise Exception(f"Can't detect type {cwl_type}") return inner(optional=optional) elif isinstance(cwl_type, list): optional = None types = [] for c in cwl_type: if c == "null": optional = True else: types.append(self.ingest_cwl_type(c, [])) if len(types) == 1: if optional is not None: types[0].optional = optional return types[0] else: from janis_core.types.common_data_types import UnionType if optional is not None: for inner in types: inner.optional = optional return UnionType(*types) elif isinstance(cwl_type, self.cwlgen.CommandInputArraySchema): return j.Array(self.from_cwl_inner_type(cwl_type.items)) elif isinstance(cwl_type, self.cwlgen.InputArraySchema): return j.Array(self.from_cwl_inner_type(cwl_type.items)) elif isinstance(cwl_type, self.cwlgen.CommandOutputArraySchema): return j.Array(self.from_cwl_inner_type(cwl_type.items)) elif isinstance(cwl_type, self.cwlgen.OutputArraySchema): return j.Array(self.from_cwl_inner_type(cwl_type.items)) elif isinstance(cwl_type, self.cwlgen.InputEnumSchema): return j.String() else: raise Exception(f"Can't parse type {type(cwl_type).__name__}")
def outputs(self) -> List[j.TOutput]: return [j.TOutput("out", j.Array(j.Int))]
tool = Cat() # Declare which input (ids) to scatter on fields_to_scatter = {"file"} # State how to scatter those inputs scatter_method = j.ScatterMethod.dot # Declare a workflow called {tool.id() + "_scattered"} w = j.WorkflowBuilder(tool.id() + "_scattered") # Pass through the inputs, but make any "fields_to_scatter" a j.Array, store these values in a map of inputs inps = { inp.id(): w.input( inp.id(), j.Array(inp.intype) if inp.id() in fields_to_scatter else inp.intype, ) for inp in tool.tool_inputs() } # Create a step: stp = w.step( # with step_id same as the tool name tool.id().lower(), # by spreading those inputs (we just declared) onto the tool. tool(**inps), # declaring the scatter information scatter=j.ScatterDescription(fields=list(fields_to_scatter), method=scatter_method), )
def outputs(self): return [j.TOutput("out", j.Array(j.String()))]
NB: Although WDL supports optional files, some backends of Cromwell do not. """ import janis_core as j ToolWithOptionalWildcardOutput = j.CommandToolBuilder( tool="optional_wildcard_output_tool", version="v0.1.0", container="ubuntu:latest", base_command=None, # write '1' to a file called 'out.csv' arguments=[j.ToolArgument("echo 1 > out.csv", shell_quote=False)], inputs=[j.ToolInput("inp", j.File, localise_file=True)], outputs=[ j.ToolOutput("out_csv_files", j.Array(j.File), selector=j.WildcardSelector("*.csv")), # the next two are functionally equivalent j.ToolOutput("out_single_csv_file_1", j.Array(j.File), selector=j.WildcardSelector("*.csv", select_first=True)), j.ToolOutput("out_single_csv_file_2", j.Array(j.File), selector=j.WildcardSelector("*.csv")[0]), # OPTIONAL glob outputs # capture all files with *.txt pattern (but select the first if possible) j.ToolOutput("out_optional_glob", j.File(optional=True), selector=j.WildcardSelector("*.txt", select_first=True)) ],
# GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import janis_core as j CommandLineThatBindsArrays = j.CommandToolBuilder( tool="CommandLineThatBindsArrays", container="ubuntu:latest", version="development", base_command="echo", inputs=[ # NO prefix, joined by space (' ') [DEFAULT] # need a position to ensure it binds onto the command line j.ToolInput("array1", j.Array(j.String), position=0), # # No prefix, joined by commans (',') j.ToolInput("array2", j.Array(j.String), separator=",", position=1), # # Single prefix, then joined by space (' ') j.ToolInput("array3", j.Array(j.String), prefix="--prefix"), # # Single prefix, then joined by commas (',') j.ToolInput("array4", j.Array(j.String), prefix="--prefix", separator=","), # # Prefix applies to each element j.ToolInput(
# it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. import janis_core as j from janis_unix.tools import Echo wf = j.WorkflowBuilder("scatter_execution") wf.input("inputs", j.Array(j.String)) wf.step( "print", # Connect the regular array inputs Echo(inp=wf.inputs), # Scatter of the 'inp' input of Echo. scatter="inp", ) # This will automatically be an array of files # because wf.print has a scatter block. wf.output("out", source=wf.print.out)
# # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. """ Create a step whose value is dependent on multiplying an input number, and the length of an list input. We construct the combination of operators using fairly natural Python syntax. """ import janis_core as j from janis_unix.tools import Echo w = j.WorkflowBuilder("multiply") w.input("number_input", int) w.input("list_input", j.Array(j.String())) # The input expression: # (w.number_input * w.list_input.length()).as_str() # is equivalent to the following manually constructed: # j.AsStringOperator(j.MultiplyOperator(w.number_inputs, j.LengthOperator(w.list_input))) w.step("multiply", Echo(inp=(w.number_input * w.list_input.length()).as_str())) w.output("out", source=w.number_input.as_str() + w.multiply.out.contents() + "my-output") if __name__ == "__main__": w.translate("wdl")
def outputs(self) -> List[j.TOutput]: return [j.TOutput("out_regions", j.Array(Bed))]
# Although not preferred, you can use Python primitives, and typing # annotations in place of the Janis types: # str - j.String() # int - j.Int() # float - j.Float() # bool - j.Boolean() # # typing.Optional[str] - j.String(optional=True) # typing.List[str] - j.Array(j.String()) workflow = j.WorkflowBuilder("typing_tests") workflow.input("my_string_input_1", j.String()) workflow.input("my_string_input_2", str) workflow.input("my_optional_str_input_1", j.String(optional=True)) workflow.input("my_optional_str_input_2", Optional[str]) workflow.input("my_list_of_strings_input_1", j.Array(j.String())) workflow.input("my_list_of_strings_input_2", List[str]) workflow.input("my_optional_list_of_strings_1", j.Array(j.String(), optional=True)) workflow.input("my_optional_list_of_strings_2", Optional[List[str]]) workflow.input("my_list_of_optional_strings_1", j.Array(j.String(optional=True))) workflow.input("my_list_of_optional_strings_2", List[Optional[str]])
# # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. """ We'll use Janis to _try_ and collect a SINGLE file with the extension '.txt', but one won't exist. CWL will automatically coerce the empty File[0] to File? (null). However, in WDL, we have do this slightly differently: File? out = if length(glob("*.txt")) > 0 then glob("*.txt")[0] else None NB: Although WDL supports optional files, some backends of Cromwell do not. """ import janis_core as j ToolWithDynamicGlob = j.CommandToolBuilder( tool="tool_with_dynamic_glob", version="v0.1.0", container="ubuntu:latest", base_command=None, # write '1' to a file called 'out.csv' arguments=[j.ToolArgument("echo 1 > out.csv", shell_quote=False)], inputs=[j.ToolInput("extension", str)], outputs=[ j.ToolOutput("out_csv_files", j.Array(j.File), selector=j.WildcardSelector("*" + j.InputSelector("extension"))), ], ) if __name__ == "__main__": ToolWithDynamicGlob().translate("cwl")