Skip to content

Commit

Permalink
Merge pull request #5 from PMCC-BioinformaticsCore/cleanup-commandbui…
Browse files Browse the repository at this point in the history
…lder

Cleanup command line builder
  • Loading branch information
illusional authored Jul 13, 2020
2 parents 90c228d + 2a5d446 commit a9b029f
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 75 deletions.
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from setuptools import setup

__version__ = "v0.2.10"
__version__ = "v0.3.0"

DESCRIPTION = (
"Contains classes and helpers to generate WDL without worrying about the syntax. "
Expand Down
46 changes: 34 additions & 12 deletions tests/test_task_generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def test_readme_task(self):

command = Task.Command("echo")
command.inputs.append(
Task.Command.CommandInput(
Task.Command.CommandInput.from_fields(
"taskGreeting",
optional=False,
position=None,
Expand All @@ -72,7 +72,7 @@ def test_readme_task(self):
)
)
command.inputs.append(
Task.Command.CommandInput(
Task.Command.CommandInput.from_fields(
"otherInput",
optional=True,
position=2,
Expand All @@ -83,7 +83,7 @@ def test_readme_task(self):
)
command = Task.Command("echo")
command.inputs.append(
Task.Command.CommandInput(
Task.Command.CommandInput.from_fields(
"taskGreeting",
optional=False,
position=None,
Expand All @@ -93,7 +93,7 @@ def test_readme_task(self):
)
)
command.inputs.append(
Task.Command.CommandInput(
Task.Command.CommandInput.from_fields(
"otherInput",
optional=True,
position=2,
Expand Down Expand Up @@ -132,8 +132,8 @@ def test_hello_tasks():
class TestCommandGeneration(unittest.TestCase):
def test_simple_command(self):
command = Task.Command("egrep")
command.inputs.append(Task.Command.CommandInput("pattern"))
command.inputs.append(Task.Command.CommandInput("in"))
command.inputs.append(Task.Command.CommandInput("~{pattern}"))
command.inputs.append(Task.Command.CommandInput("~{in}"))

expected = """\
egrep \\
Expand All @@ -145,7 +145,7 @@ def test_simple_command(self):
def test_readme_example(self):
command = Task.Command("echo")
command.inputs.append(
Task.Command.CommandInput(
Task.Command.CommandInput.from_fields(
"taskGreeting",
optional=False,
position=None,
Expand All @@ -155,7 +155,7 @@ def test_readme_example(self):
)
)
command.inputs.append(
Task.Command.CommandInput(
Task.Command.CommandInput.from_fields(
"otherInput",
optional=True,
position=2,
Expand All @@ -172,7 +172,7 @@ def test_readme_example(self):
self.assertEqual(expected, command.get_string())

def test_commandinput_space(self):
t = Task.Command.CommandInput(
t = Task.Command.CommandInput.from_fields(
"taskGreeting",
optional=False,
position=None,
Expand All @@ -183,7 +183,7 @@ def test_commandinput_space(self):
self.assertEqual("-a ~{taskGreeting}", t.get_string())

def test_commandinput_nospace(self):
t = Task.Command.CommandInput(
t = Task.Command.CommandInput.from_fields(
"taskGreeting",
optional=False,
position=None,
Expand All @@ -194,20 +194,42 @@ def test_commandinput_nospace(self):
self.assertEqual("val=~{taskGreeting}", t.get_string())

def test_commandarg_space(self):
t = Task.Command.CommandInput(
t = Task.Command.CommandInput.from_fields(
"argVal", position=None, prefix="-p", separate_value_from_prefix=True
)
self.assertEqual("-p ~{argVal}", t.get_string())

def test_commandarg_nospace(self):
t = Task.Command.CommandArgument(
t = Task.Command.CommandArgument.from_fields(
prefix="arg=",
value="argVal",
position=None,
separate_value_from_prefix=False,
)
self.assertEqual("arg=argVal", t.get_string())

def test_commandarg_flag(self):
t = Task.Command.CommandInput.from_fields(
name="my_value",
true="--arg"
)
self.assertEqual("~{if (my_value) then \"--arg\" else \"\"}", t.get_string())

def test_commandarg_flag_false(self):
t = Task.Command.CommandInput.from_fields(
name="my_value",
false="--arg"
)
self.assertEqual("~{if (my_value) then \"\" else \"--arg\"}", t.get_string())

def test_commandinp_array_inp(self):
t = Task.Command.CommandInput.from_fields(
name="my_array",
separator=" ",
default=[]
)
self.assertEqual("~{sep(" ", if defined(my_array) then my_array else [])}", t.get_string())


class TestWorkflowGeneration(unittest.TestCase):
def test_hello_workflow(self):
Expand Down
118 changes: 56 additions & 62 deletions wdlgen/task.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,114 +58,108 @@ class Command(WdlBase):
class CommandArgument(WdlBase):
def __init__(
self,
prefix: str = None,
value: str = None,
position: int = None,
separate_value_from_prefix: bool = True,
value,
position=None
):
self.prefix: Optional[str] = prefix
self.position: Optional[int] = position
self.value = value
self.separate = separate_value_from_prefix
self.position = position

@staticmethod
def from_fields(prefix: str = None, value: str = None, position: int = None, separate_value_from_prefix: bool = True,):
pre = prefix if prefix else ""
sp = " " if separate_value_from_prefix else ""
val = value if value else ""
return Task.Command.CommandArgument((pre + sp + val).strip(), position=position)

def get_string(self):
pre = self.prefix if self.prefix else ""
sp = " " if self.separate else ""
val = self.value if self.value else ""
return (pre + sp + val).strip()
return self.value

class CommandInput(CommandArgument):
def __init__(
self,
name: str,
optional: bool = False,
prefix: str = None,
position: int = None,
separate_value_from_prefix: bool = True,
default=None,
separator=None,
true=None,
false=None,
separate_arrays=None,
value,
position=None,
):
super().__init__(
prefix=prefix,
value=None,
value=value,
position=position,
separate_value_from_prefix=separate_value_from_prefix,
)
self.name = name
self.optional = optional
self.default = default
self.separator = separator
self.true = true
self.false = false
self.separate_arrays = separate_arrays

@staticmethod
def from_input(inp: Input, prefix: str = None, position: int = None):
return Task.Command.CommandInput(
return Task.Command.CommandInput.from_fields(
inp.name, inp.type.optional, prefix, position
)

def get_string(self):
@staticmethod
def from_fields(name: str,
optional: bool = False,
prefix: str = None,
position: int = None,
separate_value_from_prefix: bool = True,
default=None,
separator=None,
true=None,
false=None,
separate_arrays=None):

name, array_sep, default, true, false = (
self.name,
self.separator,
self.default,
self.true,
self.false,
name,
separator,
default,
true,
false,
)

pr = self.prefix if self.prefix else ""
bc = pr + (" " if self.separate and self.prefix else "")
pr = prefix if prefix else ""
bc = pr + (" " if separate_value_from_prefix and prefix else "")

if self.separate_arrays:
if array_sep or default or true or false:
if separate_arrays:
if separate_arrays or default or true or false:
print(
"separate_array take preferences over: separator, default, true, false"
)
if self.optional:
if optional:
# Ugly optional workaround: https://github.com/openwdl/wdl/issues/25#issuecomment-315424063
# Additional workaround for 'length(select_first({name}, [])' as length requires a non-optional array
internal_pref = f'if defined({name}) && length(select_first([{name}, []])) > 0 then "{bc}" else ""'
return f'~{{{internal_pref}}}~{{sep=" {bc}" {name}}}'
return f'~{{sep=" " prefix("{bc}", {name})}}'
return Task.Command.CommandInput(f'~{{{internal_pref}}}~{{sep(" {bc}", {name})}}', position=position)
return Task.Command.CommandInput(f'~{{sep(" ", prefix("{bc}", {name}))}}', position=position)

if array_sep and self.optional:
elif array_sep and optional:
# optional array with separator
# ifdefname = f'(if defined({name}) then {name} else [])'
return f'~{{true="{bc}" false="" defined({name})}}~{{sep="{array_sep}" {name}}}'
return Task.Command.CommandInput(f'~{{true="{bc}" false="" defined({name})}}~{{sep("{array_sep}", {name})}}', position=position)

options = []
if default:
# build up new value from previous options
value = name

if default is not None:
val = default
if isinstance(default, str):
val = f'"{default}"'
if isinstance(default, bool):

val = "true" if default else "false"
options.append(f"default={val}")

value = f"if defined({value}) then {value} else {val}"

if array_sep:
options.append(f'sep="{array_sep}"')
value = f'sep("{array_sep}", {value})'
is_flag = true or false
if is_flag:
options.append(f'true="{true if true else ""}"')
options.append(f'false="{false if false else ""}"')

stroptions = "".join(o + " " for o in options)
value = f'if ({value}) then "{true if true else ""}" else "{false if false else ""}"'

prewithquotes = f'"{bc}" + ' if bc.strip() else ""
if self.optional and not default and not is_flag and prewithquotes:
if optional and not default and not is_flag and prewithquotes:
# Option 1: We apply quotes are value, Option 2: We quote whole "prefix + name" combo
full_token = (
f"{prewithquotes} '\"' + {name} + '\"'"
if (self.separate and self.prefix and prewithquotes)
else f"'\"' + {prewithquotes}{name} + '\"'"
f"{prewithquotes} '\"' + {value} + '\"'"
if (separate_value_from_prefix and prefix and prewithquotes)
else f"'\"' + {prewithquotes}{value} + '\"'"
)
return f'~{{{stroptions}if defined({name}) then ({full_token}) else ""}}'
return Task.Command.CommandInput(f'~{{if defined({value}) then ({full_token}) else ""}}', position=position)
else:
return bc + f"~{{{stroptions}{name}}}"
return Task.Command.CommandInput(bc + f"~{{{value}}}", position=position)

def __init__(
self,
Expand Down

0 comments on commit a9b029f

Please sign in to comment.