Skip to content

Commit e0b0170

Browse files
committed
debugging task unittests
1 parent f521a61 commit e0b0170

File tree

4 files changed

+45
-21
lines changed

4 files changed

+45
-21
lines changed

nipype2pydra/cli.py

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,32 +20,45 @@ def cli():
2020
manually specified aspects of the conversion see
2121
https://github.com/nipype/nipype2pydra/tree/main/example-specs for examples
2222
23-
OUTPUT_BASE_DIR is the path of generated module file
23+
PACKAGE_ROOT is the path to the root directory of the package in which to generate the
24+
converted module file
2425
"""
2526
)
2627
@click.argument("yaml-spec", type=click.File())
27-
@click.argument("output-file", type=Path)
28+
@click.argument("package-root", type=Path)
2829
@click.option(
2930
"-c",
3031
"--callables",
3132
type=click.File(),
3233
default=None,
3334
help="a Python file containing callable functions required in the command interface",
3435
)
35-
def task(yaml_spec, output_file, callables):
36+
@click.option(
37+
"--output-module" "-m",
38+
type=str,
39+
default=None,
40+
help=(
41+
"the output module to store the converted task into relative to the `pydra.tasks` "
42+
"package. If not provided, then the path relative to `nipype.interfaces` in the "
43+
"source interface will be used instead"
44+
),
45+
)
46+
def task(yaml_spec, package_root, callables, output_module):
3647

3748
spec = yaml.safe_load(yaml_spec)
3849

39-
converter = TaskConverter(callables_module=callables, **spec)
40-
converter.generate(output_file)
50+
converter = TaskConverter(
51+
output_module=output_module, callables_module=callables, **spec
52+
)
53+
converter.generate(package_root)
4154

4255

4356
@cli.command(help="Port Nipype workflow creation functions to Pydra")
4457
@click.argument("yaml-spec", type=click.File())
45-
@click.argument("output-module-file", type=click.File(mode="w"))
46-
def workflow(yaml_spec, output_module_file):
58+
@click.argument("package-root", type=click.File(mode="w"))
59+
def workflow(yaml_spec, package_root):
4760

4861
spec = yaml.safe_load(yaml_spec)
4962

5063
converter = WorkflowConverter(spec)
51-
converter.generate(output_module_file)
64+
converter.generate(package_root)

nipype2pydra/task.py

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class TaskConverter:
2626
doctest: dict = attrs.field(factory=dict)
2727
tests_inputs: list = attrs.field(factory=list)
2828
tests_outputs: list = attrs.field(factory=list)
29+
output_module: str = attrs.field(default=None)
2930
callables_module: ModuleType = attrs.field(
3031
converter=import_module_from_path, default=None
3132
)
@@ -35,6 +36,18 @@ def output_callables_validator(self, _, output_callables: dict):
3536
if not output_callables.keys().isdisjoint(self.output_templates.keys()):
3637
raise Exception("output_callables and output_templates have the same keys")
3738

39+
def __attrs_post_init__(self):
40+
if self.output_module is None:
41+
if self.nipype_module.__name__.startswith("nipype.interfaces."):
42+
self.output_module = self.nipype_module.__name__[len("nipype.interfaces."):]
43+
else:
44+
raise RuntimeError(
45+
"Output-module needs to be explicitly provided to task converter "
46+
"when converting Nipype interefaces in non standard locations such "
47+
f"as {self.nipype_module.__name__}.{self.task_name} (i.e. not in "
48+
"nipype.interfaces)"
49+
)
50+
3851
@property
3952
def nipype_interface(self) -> nipype.interfaces.base.BaseInterface:
4053
return getattr(self.nipype_module, self.task_name)
@@ -47,20 +60,21 @@ def nipype_input_spec(self) -> nipype.interfaces.base.BaseInterfaceInputSpec:
4760
def nipype_output_spec(self) -> nipype.interfaces.base.BaseTraitedSpec:
4861
return self.nipype_interface.output_spec()
4962

50-
def generate(self, output_file: Path):
63+
def generate(self, package_root: Path):
5164
"""creating pydra input/output spec from nipype specs
5265
if write is True, a pydra Task class will be written to the file together with tests
5366
"""
5467
input_fields, inp_templates = self.convert_input_fields()
5568
output_fields = self.convert_output_spec(fields_from_template=inp_templates)
5669

57-
testdir = output_file.parent / "tests"
58-
testdir.mkdir()
59-
filename_test = testdir / f"test_spec_{output_file.name}"
60-
filename_test_run = testdir / f"test_run_{output_file.name}"
61-
70+
module_path = (Path(package_root) / "pydra" / "tasks").joinpath(self.output_module.split("."))
71+
output_file = (module_path / self.task_name.lower()).with_suffix(".py")
6272
self.write_task(output_file, input_fields, output_fields)
6373

74+
testdir = module_path / "tests"
75+
testdir.mkdir()
76+
filename_test = testdir / f"test_spec_{self.task_name.lower()}"
77+
filename_test_run = testdir / f"test_run_{self.task_name.lower()}"
6478
self.write_test(filename_test=filename_test)
6579
self.write_test(filename_test=filename_test_run, run=True)
6680

@@ -131,7 +145,7 @@ def pydra_fld_input(self, field, nm):
131145
tp_pdr = str
132146
elif nm not in self.output_callables:
133147
raise Exception(
134-
f"the filed {nm} has genfile=True, but no output template or callables provided"
148+
f"the filed {nm} has genfile=True, but no output template or callables_module provided"
135149
)
136150

137151
metadata_pdr.update(metadata_extra_spec)
@@ -346,12 +360,12 @@ def write_test(self, filename_test, run=False):
346360

347361
spec_str = "import os, pytest \nfrom pathlib import Path\n"
348362
spec_str += (
349-
f"from ..{self.task_name.lower()} import {self.task_name} \n\n"
363+
f"from pydra.tasks.{self.output_module}.{self.task_name.lower()} import {self.task_name} \n\n"
350364
)
351365
if run:
352366
pass
353367
spec_str += f"@pytest.mark.parametrize('inputs, outputs', {tests_inp_outp})\n"
354-
spec_str += f"def test_{self.task_name}(test_data, inputs, outputs):\n"
368+
spec_str += f"def test_{self.task_name.lower()}(test_data, inputs, outputs):\n"
355369
spec_str += " in_file = Path(test_data) / 'test.nii.gz'\n"
356370
spec_str += " if inputs is None: inputs = {{}}\n"
357371
spec_str += " for key, val in inputs.items():\n"

nipype2pydra/workflow.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import black
88
from nipype.interfaces.base import isdefined
99
from .utils import load_class_or_func
10-
from nipype.pipeline.engine.workflows import Workflow
1110

1211

1312
class WorkflowConverter:
@@ -41,8 +40,7 @@ def node_connections(self, workflow, functions: dict[str, dict], wf_inputs: dict
4140
connections.update(self.node_connections(nested_wf, functions=functions))
4241
return connections
4342

44-
45-
def generate(self, format_with_black=False):
43+
def generate(self, package_root: str, format_with_black: bool = False):
4644

4745
functions = defaultdict(dict)
4846
connections = self.node_connections(self.wf, functions=functions)

tests/test_task.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
"trait_added",
1212
"trait_modified",
1313
"environ",
14-
"environ_items",
1514
]
1615

1716

0 commit comments

Comments
 (0)