Skip to content

Commit ae60b07

Browse files
committed
Got python and shell interface conversions to work for new syntax
1 parent 78690fc commit ae60b07

File tree

6 files changed

+93
-28
lines changed

6 files changed

+93
-28
lines changed

example-specs/pkg-gen/qsmxt.yaml

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
qsmxt:
2+
interfaces:
3+
- qsmxt.interfaces.nipype_interface_addtojson.AddToJsonInterface
4+
- qsmxt.interfaces.nipype_interface_analyse.AnalyseInterface
5+
- qsmxt.interfaces.nipype_interface_axialsampling.AxialSamplingInterface
6+
- qsmxt.interfaces.nipype_interface_axialsampling.ResampleLikeInterface
7+
- qsmxt.interfaces.nipype_interface_bet2.Bet2Interface
8+
- qsmxt.interfaces.nipype_interface_clearswi.ClearSwiInterface
9+
- qsmxt.interfaces.nipype_interface_combinemagnitude.CombineMagnitudeInterface
10+
- qsmxt.interfaces.nipype_interface_copyfile.DynamicCopyFiles
11+
- qsmxt.interfaces.nipype_interface_erode.ErosionInterface
12+
- qsmxt.interfaces.nipype_interface_fastsurfer.FastSurferInterface
13+
- qsmxt.interfaces.nipype_interface_hdbet.HDBETInterface
14+
- qsmxt.interfaces.nipype_interface_json.JsonInterface
15+
- qsmxt.interfaces.nipype_interface_laplacian_unwrapping.LaplacianInterface
16+
- qsmxt.interfaces.nipype_interface_makehomogeneous.MakeHomogeneousInterface
17+
- qsmxt.interfaces.nipype_interface_masking.MaskingInterface
18+
- qsmxt.interfaces.nipype_interface_mgz2nii.Mgz2NiiInterface
19+
- qsmxt.interfaces.nipype_interface_nextqsm.NextqsmInterface
20+
- qsmxt.interfaces.nipype_interface_nextqsm.NormalizeB0Interface
21+
- qsmxt.interfaces.nipype_interface_nii2dcm.Nii2DcmInterface
22+
- qsmxt.interfaces.nipype_interface_nonzeroaverage.NonzeroAverageInterface
23+
- qsmxt.interfaces.nipype_interface_phaseweights.RomeoMaskingInterface
24+
- qsmxt.interfaces.nipype_interface_phaseweights.PbMaskingInterface
25+
- qsmxt.interfaces.nipype_interface_processphase.ScalePhaseInterface
26+
- qsmxt.interfaces.nipype_interface_processphase.PhaseToNormalizedInterface
27+
- qsmxt.interfaces.nipype_interface_processphase.FreqToPhaseInterface
28+
- qsmxt.interfaces.nipype_interface_processphase.FreqToNormalizedInterface
29+
- qsmxt.interfaces.nipype_interface_qsm_referencing.ReferenceQSMInterface
30+
- qsmxt.interfaces.nipype_interface_qsmjl.LaplacianUnwrappingInterface
31+
- qsmxt.interfaces.nipype_interface_qsmjl.PhaseToFreqInterface
32+
- qsmxt.interfaces.nipype_interface_qsmjl.VsharpInterface
33+
- qsmxt.interfaces.nipype_interface_qsmjl.PdfInterface
34+
- qsmxt.interfaces.nipype_interface_qsmjl.RtsQsmInterface
35+
- qsmxt.interfaces.nipype_interface_qsmjl.TvQsmInterface
36+
- qsmxt.interfaces.nipype_interface_resample_like.ResampleLikeInterface
37+
- qsmxt.interfaces.nipype_interface_romeo.RomeoB0Interface
38+
- qsmxt.interfaces.nipype_interface_t2star_r2star.T2sR2sInterface
39+
- qsmxt.interfaces.nipype_interface_tgv_qsm_jl.TGVQSMJlInterface
40+
- qsmxt.interfaces.nipype_interface_tgv_qsm.TGVQSMInterface
41+
- qsmxt.interfaces.nipype_interface_twopass.TwopassNiftiInterface
42+
workflows:
43+
- qsmxt.workflows.masking.masking_workflow
44+
- qsmxt.workflows.qsm.get_preceding_node_and_attribute
45+
- qsmxt.workflows.qsm.get_matching_files
46+
- qsmxt.workflows.qsm.init_qsm_workflow
47+
- qsmxt.workflows.qsm.qsm_workflow
48+
- qsmxt.workflows.template.init_template_workflow

nipype2pydra/cli/convert.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ def convert(
7474
if fspath.parent == package_dir and fspath.name in (
7575
"_version.py",
7676
"__init__.py",
77+
"ported",
7778
):
7879
continue
7980
if fspath.is_dir():

nipype2pydra/interface/python.py

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,20 @@
1212
logger = logging.getLogger("nipype2pydra")
1313

1414

15+
def type2str(type_):
16+
"""Convert a type to a string representation."""
17+
if isinstance(type_, str):
18+
return type_
19+
if type_ is ty.Any:
20+
return "ty.Any"
21+
elif hasattr(type_, "__name__"):
22+
return type_.__name__
23+
elif hasattr(type_, "__qualname__"):
24+
return type_.__qualname__
25+
else:
26+
return str(type_).replace("typing.", "ty.")
27+
28+
1529
@attrs.define(slots=False)
1630
class PythonInterfaceConverter(BaseInterfaceConverter):
1731

@@ -136,8 +150,8 @@ def generate_code(self, input_fields, nonstd_types, output_fields) -> ty.Tuple[
136150

137151
assert method_body, "Neither `run_interface` and `list_outputs` are defined"
138152

139-
spec_str = "@python.define"
140-
spec_str += f"class {self.task_name}(python.Task['{self.task_name}.Outputs']):"
153+
spec_str = "@python.define\n"
154+
spec_str += f"class {self.task_name}(python.Task['{self.task_name}.Outputs']):\n"
141155
spec_str += ' """\n'
142156
spec_str += self.create_doctests(
143157
input_fields=input_fields, nonstd_types=nonstd_types
@@ -147,32 +161,33 @@ def generate_code(self, input_fields, nonstd_types, output_fields) -> ty.Tuple[
147161
for inpt in input_fields:
148162
if len(inpt) == 4:
149163
name, type_, default, _ = inpt
150-
spec_str += f" {name}: {type_} = {default}\n"
164+
spec_str += f" {name}: {type2str(type_)} = {default}\n"
151165
else:
152166
name, type_, _ = inpt
153-
spec_str += f" {name}: {type_}\n"
167+
spec_str += f" {name}: {type2str(type_)}\n"
154168

155-
spec_str += " @staticmethod\n"
169+
spec_str += "\n\n class Outputs(python.Outputs):\n"
170+
for outpt in output_fields:
171+
name, type_, _ = outpt
172+
spec_str += f" {name}: {type2str(type_)}\n"
173+
174+
spec_str += "\n @staticmethod\n"
156175
spec_str += (
157176
" def function("
158-
+ ", ".join(f"{n}: {t}" for n, t, _ in input_fields)
177+
+ ", ".join(f"{i[0]}: {type2str(i[1])}" for i in input_fields)
159178
+ ")"
160179
)
161-
output_types = [o[1] for o in output_fields]
180+
output_types = [type2str(o[1]) for o in output_fields]
162181
if any(t is not ty.Any for t in output_types):
163-
spec_str += "-> "
182+
spec_str += " -> "
164183
if len(output_types) > 1:
165184
spec_str += "tuples[" + ", ".join(output_types) + "]"
166185
else:
167186
spec_str += output_types[0]
168187
spec_str += ":\n"
169-
spec_str += method_body + "\n"
170-
spec_str += "\n return {}".format(", ".join(output_names))
188+
spec_str += " " + method_body.replace("\n", "\n ") + "\n"
189+
spec_str += "\n return {}".format(", ".join(output_names))
171190

172-
spec_str += " class Outputs(python.Outputs):"
173-
for outpt in output_fields:
174-
name, type_, _ = outpt
175-
spec_str += f" {name}: {type_}\n"
176191

177192
for m in sorted(self.used.methods, key=attrgetter("__name__")):
178193
if m.__name__ not in self.included_methods:

nipype2pydra/interface/shell.py

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -86,14 +86,6 @@ def generate_code(self, input_fields, nonstd_types, output_fields) -> str:
8686
"try the FunctionInterfaceConverter class instead"
8787
)
8888

89-
def unwrap_field_type(t):
90-
if issubclass(t, WithClassifiers) and t.is_classified:
91-
unwraped_classifiers = ", ".join(
92-
unwrap_field_type(c) for c in t.classifiers
93-
)
94-
return f"{t.unclassified.__name__}[{unwraped_classifiers}]"
95-
return t.__name__
96-
9789
nonstd_types = copy(nonstd_types)
9890

9991
input_names = [i[0] for i in input_fields]
@@ -485,7 +477,7 @@ def callables_code(self):
485477
)
486478
lo_body = self._process_inputs(lo_body)
487479
lo_body = re.sub(
488-
r"(\w+) = self\.output_spec\(\).get\(\)", r"\1 = {}", lo_body
480+
r"(\w+) = self\.output_spec\(\).(?:trait_)get\(\)", r"\1 = {}", lo_body
489481
)
490482

491483
if not lo_body.strip():

nipype2pydra/package.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -660,7 +660,9 @@ def package_dir(self, package_root: Path) -> Path:
660660

661661
def write_post_release_file(self, fspath: Path):
662662

663-
if ".dev" in self.nipype_package.__version__:
663+
pkg_version = getattr(self.nipype_package, "__version__", "0.1.0")
664+
665+
if ".dev" in pkg_version:
664666
logger.warning(
665667
(
666668
"using development version of nipype2pydra (%s), "
@@ -680,7 +682,7 @@ def write_post_release_file(self, fspath: Path):
680682
self.name,
681683
)
682684

683-
src_pkg_version = self.nipype_package.__version__.split(".dev")[0]
685+
src_pkg_version = pkg_version.split(".dev")[0]
684686
nipype2pydra_version = nipype2pydra.__version__.split(".dev")[0]
685687
post_release = (src_pkg_version + nipype2pydra_version).replace(".", "")
686688

@@ -874,7 +876,10 @@ def write_to_module(
874876
converter_imports = []
875877

876878
for klass in used.classes:
877-
if f"\nclass {klass.__name__}(" not in code_str:
879+
if (
880+
klass.__module__ == module_name
881+
and f"\nclass {klass.__name__}(" not in code_str
882+
):
878883
try:
879884
class_converter = self.classes[full_address(klass)]
880885
converter_imports.extend(class_converter.used.import_stmts)
@@ -906,7 +911,10 @@ def write_to_module(
906911
code_str += "\n" + converted_code + "\n"
907912

908913
for func in sorted(used.functions, key=attrgetter("__name__")):
909-
if f"\ndef {func.__name__}(" not in code_str:
914+
if (
915+
func.__module__ == module_name
916+
and f"\ndef {func.__name__}(" not in code_str
917+
):
910918
if func.__name__ in self.functions:
911919
function_converter = self.functions[full_address(func)]
912920
converter_imports.extend(function_converter.used.import_stmts)

nipype2pydra/symbols.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,8 @@ def _find_referenced(
403403
class_def = (obj.__name__, obj)
404404
if (
405405
class_def
406-
not in self.imported_classes + omit_classes
406+
not in self.imported_classes
407+
+ self.package.omit_classes
407408
):
408409
self.imported_classes.append(class_def)
409410
intra_pkg_objs[imported.object.__name__].add(obj)

0 commit comments

Comments
 (0)