Skip to content

Commit 70750d0

Browse files
committed
simplifying all methods related to cmdline for ShellCommandTask: creating command line only for stateless versions; temporarily removing some asserts from tests
1 parent 74957ca commit 70750d0

File tree

4 files changed

+75
-156
lines changed

4 files changed

+75
-156
lines changed

pydra/engine/task.py

Lines changed: 34 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -317,38 +317,13 @@ def __init__(
317317

318318
@property
319319
def command_args(self):
320-
"""Get command line arguments, returns a list if task has a state"""
320+
"""Get command line arguments"""
321321
if is_lazy(self.inputs):
322322
raise Exception("can't return cmdline, self.inputs has LazyFields")
323-
orig_inputs = attr.asdict(self.inputs, recurse=False)
324323
if self.state:
325-
command_args_list = []
326-
self.state.prepare_states(self.inputs)
327-
for ii, el in enumerate(self.state.states_ind):
328-
command_args_list.append(self._command_args_single(el, index=ii))
329-
self.inputs = attr.evolve(self.inputs, **orig_inputs)
330-
return command_args_list
331-
else:
332-
command_args = self._command_args_single()
333-
self.inputs = attr.evolve(self.inputs, **orig_inputs)
334-
return command_args
335-
336-
def _command_args_single(self, state_ind=None, index=None):
337-
"""Get command line arguments for a single state
324+
raise NotImplementedError
338325

339-
Parameters
340-
----------
341-
state_ind : dict[str, int]
342-
Keys are inputs being mapped over, values are indices within that input
343-
index : int
344-
Index in flattened list of states
345-
"""
346-
if index is not None:
347-
modified_inputs = template_update(
348-
self.inputs, output_dir=self.output_dir[index], state_ind=state_ind
349-
)
350-
else:
351-
modified_inputs = template_update(self.inputs, output_dir=self.output_dir)
326+
modified_inputs = template_update(self.inputs, output_dir=self.output_dir)
352327
if modified_inputs is not None:
353328
self.inputs = attr.evolve(self.inputs, **modified_inputs)
354329

@@ -366,15 +341,13 @@ def _command_args_single(self, state_ind=None, index=None):
366341
):
367342
continue
368343
if name == "executable":
369-
pos_args.append(
370-
self._command_shelltask_executable(field, state_ind, index)
371-
)
344+
pos_args.append(self._command_shelltask_executable(field))
372345
elif name == "args":
373-
pos_val = self._command_shelltask_args(field, state_ind, index)
346+
pos_val = self._command_shelltask_args(field)
374347
if pos_val:
375348
pos_args.append(pos_val)
376349
else:
377-
pos_val = self._command_pos_args(field, state_ind, index)
350+
pos_val = self._command_pos_args(field)
378351
if pos_val:
379352
pos_args.append(pos_val)
380353

@@ -383,39 +356,35 @@ def _command_args_single(self, state_ind=None, index=None):
383356
# pos_args values are each a list of arguments, so concatenate lists after sorting
384357
return sum(cmd_args, [])
385358

386-
def _field_value(self, field, state_ind, index, check_file=False):
359+
def _field_value(self, field, check_file=False):
387360
"""
388361
Checking value of the specific field, if value is not set, None is returned.
389-
If state_ind and ind, taking a specific element of the field.
390362
check_file has no effect, but subclasses can use it to validate or modify
391363
filenames.
392364
"""
393-
name = f"{self.name}.{field.name}"
394365
value = getattr(self.inputs, field.name)
395-
if self.state and name in state_ind:
396-
value = value[state_ind[name]]
397366
if value == attr.NOTHING:
398367
value = None
399368
return value
400369

401-
def _command_shelltask_executable(self, field, state_ind, index):
370+
def _command_shelltask_executable(self, field):
402371
"""Returining position and value for executable ShellTask input"""
403372
pos = 0 # executable should be the first el. of the command
404-
value = self._field_value(field, state_ind, index)
373+
value = self._field_value(field)
405374
if value is None:
406375
raise ValueError("executable has to be set")
407376
return pos, ensure_list(value, tuple2list=True)
408377

409-
def _command_shelltask_args(self, field, state_ind, index):
378+
def _command_shelltask_args(self, field):
410379
"""Returining position and value for args ShellTask input"""
411380
pos = -1 # assuming that args is the last el. of the command
412-
value = self._field_value(field, state_ind, index, check_file=True)
381+
value = self._field_value(field, check_file=True)
413382
if value is None:
414383
return None
415384
else:
416385
return pos, ensure_list(value, tuple2list=True)
417386

418-
def _command_pos_args(self, field, state_ind, index):
387+
def _command_pos_args(self, field):
419388
"""
420389
Checking all additional input fields, setting pos to None, if position not set.
421390
Creating a list with additional parts of the command that comes from
@@ -443,7 +412,7 @@ def _command_pos_args(self, field, state_ind, index):
443412
# Shift negatives down to allow args to be -1
444413
pos += 1 if pos >= 0 else -1
445414

446-
value = self._field_value(field, state_ind, index, check_file=True)
415+
value = self._field_value(field, check_file=True)
447416
if field.metadata.get("readonly", False) and value is not None:
448417
raise Exception(f"{field.name} is read only, the value can't be provided")
449418
elif (
@@ -453,12 +422,7 @@ def _command_pos_args(self, field, state_ind, index):
453422
):
454423
return None
455424

456-
# getting stated inputs
457-
inputs_dict_st = attr.asdict(self.inputs, recurse=False)
458-
if state_ind is not None:
459-
for k, v in state_ind.items():
460-
k = k.split(".")[1]
461-
inputs_dict_st[k] = inputs_dict_st[k][v]
425+
inputs_dict = attr.asdict(self.inputs, recurse=False)
462426

463427
cmd_add = []
464428
# formatter that creates a custom command argument
@@ -470,10 +434,10 @@ def _command_pos_args(self, field, state_ind, index):
470434
if argnm == "field":
471435
call_args_val[argnm] = value
472436
elif argnm == "inputs":
473-
call_args_val[argnm] = inputs_dict_st
437+
call_args_val[argnm] = inputs_dict
474438
else:
475-
if argnm in inputs_dict_st:
476-
call_args_val[argnm] = inputs_dict_st[argnm]
439+
if argnm in inputs_dict:
440+
call_args_val[argnm] = inputs_dict[argnm]
477441
else:
478442
raise AttributeError(
479443
f"arguments of the formatter function from {field.name} "
@@ -534,21 +498,12 @@ def cmdline(self):
534498
raise Exception("can't return cmdline, self.inputs has LazyFields")
535499
# checking the inputs fields before returning the command line
536500
self.inputs.check_fields_input_spec()
501+
if self.state:
502+
raise NotImplementedError
537503
if isinstance(self, ContainerTask):
538-
if self.state:
539-
cmdline = []
540-
for con, com in zip(self.container_args, self.command_args):
541-
cmdline.append(" ".join(con + com))
542-
else:
543-
cmdline = " ".join(self.container_args + self.command_args)
504+
cmdline = " ".join(self.container_args + self.command_args)
544505
else:
545-
if self.state:
546-
cmdline = []
547-
for el in self.command_args:
548-
cmdline.append(" ".join(el))
549-
else:
550-
cmdline = " ".join(self.command_args)
551-
506+
cmdline = " ".join(self.command_args)
552507
return cmdline
553508

554509
def _run_task(self):
@@ -628,18 +583,17 @@ def __init__(
628583
**kwargs,
629584
)
630585

631-
def _field_value(self, field, state_ind, index, check_file=False):
586+
def _field_value(self, field, check_file=False):
632587
"""
633588
Checking value of the specific field, if value is not set, None is returned.
634-
If state_ind and ind, taking a specific element of the field.
635589
If check_file is True, checking if field is a a local file
636590
and settings bindings if needed.
637591
"""
638-
value = super()._field_value(field, state_ind, index)
592+
value = super()._field_value(field)
639593
if value and check_file and is_local_file(field):
640594
# changing path to the cpath (the directory should be mounted)
641595
lpath = Path(str(value))
642-
cdir = self.bind_paths(index)[lpath.parent][0]
596+
cdir = self.bind_paths()[lpath.parent][0]
643597
cpath = cdir.joinpath(lpath.name)
644598
value = str(cpath)
645599
return value
@@ -655,7 +609,7 @@ def container_check(self, container_type):
655609
if self.inputs.image is attr.NOTHING:
656610
raise AttributeError("Container image is not specified")
657611

658-
def bind_paths(self, index=None):
612+
def bind_paths(self):
659613
"""Get bound mount points
660614
661615
Returns
@@ -667,10 +621,7 @@ def bind_paths(self, index=None):
667621
output_dir_cpath = None
668622
if self.inputs.bindings is None:
669623
self.inputs.bindings = []
670-
if index is None:
671-
output_dir = self.output_dir
672-
else:
673-
output_dir = self.output_dir[index]
624+
output_dir = self.output_dir
674625
for binding in self.inputs.bindings:
675626
binding = list(binding)
676627
if len(binding) == 3:
@@ -691,15 +642,15 @@ def bind_paths(self, index=None):
691642
bind_paths[output_dir] = (self.output_cpath, "rw")
692643
return bind_paths
693644

694-
def binds(self, opt, index=None):
645+
def binds(self, opt):
695646
"""
696647
Specify mounts to bind from local filesystems to container and working directory.
697648
698649
Uses py:meth:`bind_paths`
699650
700651
"""
701652
bargs = []
702-
for lpath, (cpath, mode) in self.bind_paths(index).items():
653+
for lpath, (cpath, mode) in self.bind_paths().items():
703654
bargs.extend([opt, f"{lpath}:{cpath}:{mode}"])
704655
return bargs
705656

@@ -775,31 +726,15 @@ def container_args(self):
775726
raise Exception("can't return container_args, self.inputs has LazyFields")
776727
self.container_check("docker")
777728
if self.state:
778-
self.state.prepare_states(self.inputs)
779-
cargs_list = []
780-
for ii, el in enumerate(self.state.states_ind):
781-
if f"{self.name}.image" in el:
782-
cargs_list.append(
783-
self._container_args_single(
784-
self.inputs.image[el[f"{self.name}.image"]], index=ii
785-
)
786-
)
787-
else:
788-
cargs_list.append(
789-
self._container_args_single(self.inputs.image, index=ii)
790-
)
791-
return cargs_list
792-
else:
793-
return self._container_args_single(self.inputs.image)
729+
raise NotImplementedError
794730

795-
def _container_args_single(self, image, index=None):
796731
cargs = ["docker", "run"]
797732
if self.inputs.container_xargs is not None:
798733
cargs.extend(self.inputs.container_xargs)
799734

800-
cargs.extend(self.binds("-v", index))
735+
cargs.extend(self.binds("-v"))
801736
cargs.extend(["-w", str(self.output_cpath)])
802-
cargs.append(image)
737+
cargs.append(self.inputs.image)
803738

804739
return cargs
805740

@@ -869,30 +804,14 @@ def container_args(self):
869804
raise Exception("can't return container_args, self.inputs has LazyFields")
870805
self.container_check("singularity")
871806
if self.state:
872-
self.state.prepare_states(self.inputs)
873-
cargs_list = []
874-
for ii, el in enumerate(self.state.states_ind):
875-
if f"{self.name}.image" in el:
876-
cargs_list.append(
877-
self._container_args_single(
878-
self.inputs.image[el[f"{self.name}.image"]], index=ii
879-
)
880-
)
881-
else:
882-
cargs_list.append(
883-
self._container_args_single(self.inputs.image, index=ii)
884-
)
885-
return cargs_list
886-
else:
887-
return self._container_args_single(self.inputs.image)
807+
raise NotImplementedError
888808

889-
def _container_args_single(self, image, index=None):
890809
cargs = ["singularity", "exec"]
891810

892811
if self.inputs.container_xargs is not None:
893812
cargs.extend(self.inputs.container_xargs)
894813

895-
cargs.extend(self.binds("-B", index))
814+
cargs.extend(self.binds("-B"))
896815
cargs.extend(["--pwd", str(self.output_cpath)])
897-
cargs.append(image)
816+
cargs.append(self.inputs.image)
898817
return cargs

pydra/engine/tests/test_dockertask.py

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -343,11 +343,11 @@ def test_docker_st_1(plugin):
343343
)
344344
assert docky.state.splitter == "docky.executable"
345345

346-
for ii, el in enumerate(docky.cmdline):
347-
assert (
348-
el
349-
== f"docker run --rm -v {docky.output_dir[ii]}:/output_pydra:rw -w /output_pydra {docky.inputs.image} {cmd[ii]}"
350-
)
346+
# for ii, el in enumerate(docky.cmdline):
347+
# assert (
348+
# el
349+
# == f"docker run --rm -v {docky.output_dir[ii]}:/output_pydra:rw -w /output_pydra {docky.inputs.image} {cmd[ii]}"
350+
# )
351351

352352
res = docky(plugin=plugin)
353353
assert res[0].output.stdout == "/output_pydra\n"
@@ -367,11 +367,11 @@ def test_docker_st_2(plugin):
367367
)
368368
assert docky.state.splitter == "docky.image"
369369

370-
for ii, el in enumerate(docky.cmdline):
371-
assert (
372-
el
373-
== f"docker run --rm -v {docky.output_dir[ii]}:/output_pydra:rw -w /output_pydra {docky.inputs.image[ii]} {' '.join(cmd)}"
374-
)
370+
# for ii, el in enumerate(docky.cmdline):
371+
# assert (
372+
# el
373+
# == f"docker run --rm -v {docky.output_dir[ii]}:/output_pydra:rw -w /output_pydra {docky.inputs.image[ii]} {' '.join(cmd)}"
374+
# )
375375

376376
res = docky(plugin=plugin)
377377
assert "Debian" in res[0].output.stdout
@@ -410,16 +410,16 @@ def test_docker_st_4(plugin):
410410
assert docky.state.combiner == ["docky.image"]
411411
assert docky.state.splitter_final == "docky.executable"
412412

413-
for ii, el in enumerate(docky.cmdline):
414-
i, j = ii // 2, ii % 2
415-
if j == 0:
416-
cmd_str = "whoami"
417-
else:
418-
cmd_str = " ".join(["cat", "/etc/issue"])
419-
assert (
420-
el
421-
== f"docker run --rm -v {docky.output_dir[ii]}:/output_pydra:rw -w /output_pydra {docky.inputs.image[i]} {cmd_str}"
422-
)
413+
# for ii, el in enumerate(docky.cmdline):
414+
# i, j = ii // 2, ii % 2
415+
# if j == 0:
416+
# cmd_str = "whoami"
417+
# else:
418+
# cmd_str = " ".join(["cat", "/etc/issue"])
419+
# assert (
420+
# el
421+
# == f"docker run --rm -v {docky.output_dir[ii]}:/output_pydra:rw -w /output_pydra {docky.inputs.image[i]} {cmd_str}"
422+
# )
423423

424424
res = docky(plugin=plugin)
425425

0 commit comments

Comments
 (0)