Skip to content

Commit c1516a5

Browse files
committed
debugging task unittests
1 parent dab69db commit c1516a5

11 files changed

+103
-97
lines changed

conftest.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,20 @@
66
from click.testing import CliRunner
77

88

9-
@pytest.fixture(scope="session")
10-
def pkg_dir():
11-
return Path(__file__).parent
9+
PKG_DIR = Path(__file__).parent
10+
EXAMPLE_SPECS_DIR = PKG_DIR / "example-specs"
11+
EXAMPLE_TASKS_DIR = EXAMPLE_SPECS_DIR / "task"
12+
EXAMPLE_WORKFLOWS_DIR = EXAMPLE_SPECS_DIR / "workflow"
1213

1314

14-
@pytest.fixture(scope="session")
15-
def example_specs_dir(pkg_dir):
16-
return pkg_dir / "example-specs"
15+
@pytest.fixture(params=[str(p.stem) for p in (EXAMPLE_TASKS_DIR).glob("*.yaml")])
16+
def task_spec_file(request):
17+
return (EXAMPLE_TASKS_DIR / request.param).with_suffix(".yaml")
18+
19+
20+
@pytest.fixture(params=[str(p.stem) for p in EXAMPLE_WORKFLOWS_DIR.glob("*.yaml")])
21+
def workflow_spec_file(request):
22+
return (EXAMPLE_WORKFLOWS_DIR / request.param).with_suffix(".yaml")
1723

1824

1925
@pytest.fixture

example-specs/ants_segmentation.yml

Lines changed: 0 additions & 14 deletions
This file was deleted.

example-specs/freesurfer_preprocess.yml

Lines changed: 0 additions & 11 deletions
This file was deleted.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
nipype_module: nipype.interfaces.ants.segmentation
2+
task_name: N4BiasFieldCorrection
3+
output_requirements:
4+
output_image: []
5+
bias_image: ["save_bias"]
6+
output_templates:
7+
output_image: ""
8+
doctest:
9+
input_image: test.nii.gz
10+
cmdline:
11+
tests_inputs: []
12+
tests_outputs: [] # - AttributeError
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
nipype_module: nipype.interfaces.freesurfer.preprocess
2+
task_name: ApplyVolTransform
3+
output_requirements: []
4+
output_templates:
5+
transformed_file: "{source_file}_warped"
6+
doctest:
7+
source_file: test.nii.gz
8+
cmdline: mri_vol2vol
9+
tests_inputs: []
10+
tests_outputs: []
File renamed without changes.

nipype2pydra/task.py

Lines changed: 19 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ class TaskConverter:
2424
output_templates: dict = attrs.field(factory=dict)
2525
output_callables: dict = attrs.field(factory=dict)
2626
doctest: dict = attrs.field(factory=dict)
27+
tests_inputs: list = attrs.field(factory=list)
28+
tests_outputs: list = attrs.field(factory=list)
2729
callables_module: ModuleType = attrs.field(
2830
converter=import_module_from_path, default=None
2931
)
@@ -39,11 +41,11 @@ def nipype_interface(self) -> nipype.interfaces.base.BaseInterface:
3941

4042
@property
4143
def nipype_input_spec(self) -> nipype.interfaces.base.BaseInterfaceInputSpec:
42-
return self.nipype_interface.input_spec
44+
return self.nipype_interface.input_spec()
4345

4446
@property
4547
def nipype_output_spec(self) -> nipype.interfaces.base.BaseTraitedSpec:
46-
return self.nipype_interface.input_spec
48+
return self.nipype_interface.output_spec()
4749

4850
def generate(self, output_file: Path):
4951
"""creating pydra input/output spec from nipype specs
@@ -52,18 +54,12 @@ def generate(self, output_file: Path):
5254
input_fields, inp_templates = self.convert_input_fields()
5355
output_fields = self.convert_output_spec(fields_from_template=inp_templates)
5456

55-
input_spec = specs.SpecInfo(
56-
name="Input", fields=input_fields, bases=(specs.ShellSpec,)
57-
)
58-
output_spec = specs.SpecInfo(
59-
name="Output", fields=output_fields, bases=(specs.ShellOutSpec,)
60-
)
61-
6257
testdir = output_file.parent / "tests"
58+
testdir.mkdir()
6359
filename_test = testdir / f"test_spec_{output_file.name}"
6460
filename_test_run = testdir / f"test_run_{output_file.name}"
6561

66-
self.write_task(output_file, input_spec, output_spec)
62+
self.write_task(output_file, input_fields, output_fields)
6763

6864
self.write_test(filename_test=filename_test)
6965
self.write_test(filename_test=filename_test_run, run=True)
@@ -127,9 +123,7 @@ def pydra_fld_input(self, field, nm):
127123
tp_pdr = str
128124
elif getattr(field, "genfile"):
129125
if nm in self.output_templates:
130-
metadata_pdr["output_file_template"] = self.interface_spec[
131-
"output_templates"
132-
][nm]
126+
metadata_pdr["output_file_template"] = self.output_templates[nm]
133127
if tp_pdr in [
134128
specs.File,
135129
specs.Directory,
@@ -306,16 +300,16 @@ def types_to_names(spec_fields):
306300
spec_str += "import typing as ty\n"
307301
spec_str += functions_str
308302
spec_str += f"input_fields = {input_fields_str}\n"
309-
spec_str += f"{self.interface_name}_input_spec = specs.SpecInfo(name='Input', fields=input_fields, bases=(specs.ShellSpec,))\n\n"
303+
spec_str += f"{self.task_name}_input_spec = specs.SpecInfo(name='Input', fields=input_fields, bases=(specs.ShellSpec,))\n\n"
310304
spec_str += f"output_fields = {output_fields_str}\n"
311-
spec_str += f"{self.interface_name}_output_spec = specs.SpecInfo(name='Output', fields=output_fields, bases=(specs.ShellOutSpec,))\n\n"
305+
spec_str += f"{self.task_name}_output_spec = specs.SpecInfo(name='Output', fields=output_fields, bases=(specs.ShellOutSpec,))\n\n"
312306

313-
spec_str += f"class {self.interface_name}(ShellCommandTask):\n"
307+
spec_str += f"class {self.task_name}(ShellCommandTask):\n"
314308
if self.doctest:
315309
spec_str += self.create_doctest()
316-
spec_str += f" input_spec = {self.interface_name}_input_spec\n"
317-
spec_str += f" output_spec = {self.interface_name}_output_spec\n"
318-
spec_str += f" executable='{self.cmd}'\n"
310+
spec_str += f" input_spec = {self.task_name}_input_spec\n"
311+
spec_str += f" output_spec = {self.task_name}_output_spec\n"
312+
spec_str += f" executable='{self.nipype_interface._cmd}'\n"
319313

320314
for tp_repl in self.TYPE_REPLACE:
321315
spec_str = spec_str.replace(*tp_repl)
@@ -352,18 +346,18 @@ def write_test(self, filename_test, run=False):
352346

353347
spec_str = "import os, pytest \nfrom pathlib import Path\n"
354348
spec_str += (
355-
f"from ..{self.interface_name.lower()} import {self.interface_name} \n\n"
349+
f"from ..{self.task_name.lower()} import {self.task_name} \n\n"
356350
)
357351
if run:
358352
pass
359353
spec_str += f"@pytest.mark.parametrize('inputs, outputs', {tests_inp_outp})\n"
360-
spec_str += f"def test_{self.interface_name}(test_data, inputs, outputs):\n"
354+
spec_str += f"def test_{self.task_name}(test_data, inputs, outputs):\n"
361355
spec_str += " in_file = Path(test_data) / 'test.nii.gz'\n"
362356
spec_str += " if inputs is None: inputs = {{}}\n"
363357
spec_str += " for key, val in inputs.items():\n"
364358
spec_str += " try: inputs[key] = eval(val)\n"
365359
spec_str += " except: pass\n"
366-
spec_str += f" task = {self.interface_name}(in_file=in_file, **inputs)\n"
360+
spec_str += f" task = {self.task_name}(in_file=in_file, **inputs)\n"
367361
spec_str += (
368362
" assert set(task.generated_output_names) == "
369363
"set(['return_code', 'stdout', 'stderr'] + outputs)\n"
@@ -392,14 +386,14 @@ def write_test_error(self, input_error):
392386
spec_str = "\n\n"
393387
spec_str += f"@pytest.mark.parametrize('inputs, error', {input_error})\n"
394388
spec_str += (
395-
f"def test_{self.interface_name}_exception(test_data, inputs, error):\n"
389+
f"def test_{self.task_name}_exception(test_data, inputs, error):\n"
396390
)
397391
spec_str += " in_file = Path(test_data) / 'test.nii.gz'\n"
398392
spec_str += " if inputs is None: inputs = {{}}\n"
399393
spec_str += " for key, val in inputs.items():\n"
400394
spec_str += " try: inputs[key] = eval(val)\n"
401395
spec_str += " except: pass\n"
402-
spec_str += f" task = {self.interface_name}(in_file=in_file, **inputs)\n"
396+
spec_str += f" task = {self.task_name}(in_file=in_file, **inputs)\n"
403397
spec_str += " with pytest.raises(eval(error)):\n"
404398
spec_str += " task.generated_output_names\n"
405399

@@ -409,7 +403,7 @@ def create_doctest(self):
409403
"""adding doctests to the interfaces"""
410404
cmdline = self.doctest.pop("cmdline")
411405
doctest = ' """\n Example\n -------\n'
412-
doctest += f" >>> task = {self.interface_name}()\n"
406+
doctest += f" >>> task = {self.task_name}()\n"
413407
for key, val in self.doctest.items():
414408
if type(val) is str:
415409
doctest += f' >>> task.inputs.{key} = "{val}"\n'

tests/test_smriprep.py

Lines changed: 0 additions & 38 deletions
This file was deleted.

tests/test_task.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,30 @@
1+
from importlib import import_module
2+
import yaml
13
from conftest import show_cli_trace
24
from nipype2pydra.cli import task as task_cli
5+
from nipype2pydra.utils import import_module_from_path
36

47

5-
def test_task_conversion(example_specs_dir, cli_runner, work_dir):
8+
def test_task_conversion(task_spec_file, cli_runner, work_dir):
9+
10+
with open(task_spec_file) as f:
11+
task_spec = yaml.safe_load(f)
12+
output_file = work_dir / "pydra_module.py"
613

714
result = cli_runner(
815
task_cli,
916
args=[
10-
str(example_specs_dir / "ants_registration.yml"),
11-
str(work_dir / "ants" / "registration.py"),
17+
str(task_spec_file),
18+
str(output_file),
1219
],
1320
)
1421

1522
assert result.exit_code == 0, show_cli_trace(result)
23+
24+
pydra_module = import_module_from_path(output_file)
25+
pydra_task = getattr(pydra_module, task_spec["task_name"])
26+
nipype_interface = getattr(
27+
import_module(task_spec["nipype_module"]), task_spec["task_name"]
28+
)
29+
30+
assert sorted(f[0] for f in pydra_task.input_spec.fields) == sorted(nipype_interface.input_spec().all_trait_names())

0 commit comments

Comments
 (0)