Skip to content

Commit 1e92a40

Browse files
committed
debugged changes so that all packages build successfully again
1 parent 28659bc commit 1e92a40

File tree

4 files changed

+69
-35
lines changed

4 files changed

+69
-35
lines changed

nipype2pydra/interface/base.py

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
)
4343
from fileformats.generic import File
4444
import nipype2pydra.package
45+
from nipype2pydra.exceptions import UnmatchedParensException
4546

4647
logger = logging.getLogger("nipype2pydra")
4748

@@ -498,7 +499,10 @@ def _referenced_funcs_and_methods(self):
498499
method_returns[method_name] = []
499500
method_stacks[method_name] = ()
500501
for method_name in self.INCLUDED_METHODS:
501-
if method_name not in self.nipype_interface.__dict__:
502+
base = find_super_method(
503+
self.nipype_interface, method_name, include_class=True
504+
)[1]
505+
if self.package.is_omitted(base):
502506
continue # Don't include base methods
503507
method = getattr(self.nipype_interface, method_name)
504508
referenced_methods.add(method)
@@ -1103,9 +1107,7 @@ def _get_referenced(
11031107
)
11041108
for match in re.findall(r"super\([^\)]*\)\.(\w+)\(", method_body):
11051109
super_method, base = find_super_method(super_base, match)
1106-
if any(
1107-
base.__module__.startswith(m) for m in UsedSymbols.ALWAYS_OMIT_MODULES
1108-
):
1110+
if self.package.is_omitted(super_method):
11091111
continue
11101112
func_name = self._common_parent_pkg_prefix(base) + match
11111113
if func_name not in referenced_supers:
@@ -1296,28 +1298,28 @@ def replace_supers(self, method_body, super_base=None):
12961298
if super_base is None:
12971299
super_base = self.nipype_interface
12981300
name_map = self.method_supers[super_base]
1299-
1300-
def replace_super(match):
1301-
super_method, base = find_super_method(super_base, match.group(1))
1301+
splits = re.split(r"super\([^\)]*\)\.(\w+)(?=\()", method_body)
1302+
new_body = splits[0]
1303+
for name, block in zip(splits[1::2], splits[2::2]):
1304+
super_method, base = find_super_method(super_base, name)
1305+
_, args, post = extract_args(block)
1306+
arg_str = ", ".join(args)
13021307
try:
1303-
return self.SPECIAL_SUPER_MAPPINGS[super_method].format(
1304-
args=match.group(2)
1308+
new_body += self.SPECIAL_SUPER_MAPPINGS[super_method].format(
1309+
args=arg_str
13051310
)
13061311
except KeyError:
13071312
try:
1308-
return name_map[match.group(1)] + "(" + match.group(2) + ")"
1313+
new_body += name_map[name] + "(" + arg_str + ")"
13091314
except KeyError:
1310-
if any(
1311-
base.__module__.startswith(m)
1312-
for m in UsedSymbols.ALWAYS_OMIT_MODULES
1313-
):
1315+
if self.package.is_omitted(base):
13141316
raise KeyError(
1315-
f"Require special mapping for '{match.group(1)}' in {base} class "
1317+
f"Require special mapping for '{name}' in {base} class "
13161318
"as methods in that module are being omitted from the conversion"
13171319
) from None
13181320
raise
1319-
1320-
return re.sub(r"super\([^\)]*\)\.(\w+)\(([^\)]*)\)", replace_super, method_body)
1321+
new_body += post[1:]
1322+
return new_body
13211323

13221324
def unwrap_nested_methods(
13231325
self, method_body, additional_args=(), inputs_as_dict: bool = False
@@ -1375,13 +1377,22 @@ def unwrap_nested_methods(
13751377
)
13761378
# Insert additional arguments to the method call (which were previously
13771379
# accessed via member attributes)
1378-
new_body += name + insert_args_in_signature(
1379-
args,
1380-
[
1381-
f"{a}=inputs['{a}']" if inputs_as_dict else f"{a}={a}"
1382-
for a in (list(self.method_args[name]) + list(additional_args))
1383-
],
1384-
)
1380+
args_to_be_inserted = list(self.method_args[name]) + list(additional_args)
1381+
try:
1382+
new_body += name + insert_args_in_signature(
1383+
args,
1384+
[
1385+
f"{a}=inputs['{a}']" if inputs_as_dict else f"{a}={a}"
1386+
for a in args_to_be_inserted
1387+
],
1388+
)
1389+
except UnmatchedParensException:
1390+
logger.warning(
1391+
f"Nested method call inside '{name}' in {self.full_address}, "
1392+
"the following args will need to be manually inserted up after the "
1393+
f"conversion: {args_to_be_inserted}"
1394+
)
1395+
new_body += name + args
13851396
method_body = new_body
13861397
# Convert assignment to self attributes into method-scoped variables (hopefully
13871398
# there aren't any name clashes)
@@ -1395,6 +1406,7 @@ def unwrap_nested_methods(
13951406
CommandLine._format_arg: "argstr.format(**inputs)",
13961407
CommandLine._filename_from_source: "{args} + '_generated'",
13971408
BaseInterface._check_version_requirements: "[]",
1409+
CommandLine._parse_inputs: "{{}}",
13981410
}
13991411

14001412
INPUT_KEYS = [

nipype2pydra/interface/shell_command.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,8 @@ def types_to_names(spec_fields):
174174
self.parse_inputs_code,
175175
self.callables_code,
176176
self.defaults_code,
177-
],
177+
]
178+
+ list(self.referenced_methods),
178179
omit_classes=self.package.omit_classes + [BaseInterface, TraitedSpec],
179180
omit_modules=self.package.omit_modules,
180181
omit_functions=self.package.omit_functions,
@@ -215,7 +216,7 @@ def input_fields(self):
215216
for field in input_fields:
216217
if field[0] in self.formatted_input_field_names:
217218
field[-1]["formatter"] = f"{field[0]}_formatter"
218-
self._format_argstrs[field[0]] = field[-1].pop("argstr")
219+
self._format_argstrs[field[0]] = field[-1].pop("argstr", "")
219220
return input_fields
220221

221222
@cached_property
@@ -284,12 +285,13 @@ def format_arg_code(self):
284285
# Strip out return value
285286
body = re.sub(
286287
(
287-
r"\s*return super\((\w+,\s*self)?\)\._format_arg\("
288+
r"^ return super\((\w+,\s*self)?\)\._format_arg\("
288289
+ ", ".join(existing_args)
289290
+ r"\)\n"
290291
),
291-
"",
292+
"return argstr.format(**inputs)",
292293
body,
294+
flags=re.MULTILINE,
293295
)
294296
if not body:
295297
return ""
@@ -299,10 +301,13 @@ def format_arg_code(self):
299301
code_str = f"""def _format_arg({name_arg}, {val_arg}, inputs, argstr):{self.parse_inputs_call}
300302
if {val_arg} is None:
301303
return ""
302-
{body}
303-
return argstr.format(**inputs)
304+
{body}"""
305+
306+
if not code_str.rstrip().endswith("return argstr.format(**inputs)"):
307+
code_str += "\n return argstr.format(**inputs)"
308+
309+
code_str += "\n\n"
304310

305-
"""
306311
for field_name in self.formatted_input_field_names:
307312
code_str += (
308313
f"def {field_name}_formatter(field, inputs):\n"
@@ -328,6 +333,8 @@ def parse_inputs_code(self) -> str:
328333

329334
# Strip out return value
330335
body = re.sub(r"\s*return .*\n", "", body)
336+
if not body:
337+
return ""
331338
body = self.unwrap_nested_methods(body, inputs_as_dict=True)
332339
body = self.replace_supers(body)
333340

nipype2pydra/package.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ def all_import_translations(self) -> ty.List[ty.Tuple[str, str]]:
310310

311311
@property
312312
def all_omit_modules(self) -> ty.List[str]:
313-
return self.omit_modules + ["nipype.interfaces.utility"]
313+
return self.omit_modules + UsedSymbols.ALWAYS_OMIT_MODULES
314314

315315
@property
316316
def all_explicit(self):
@@ -344,6 +344,17 @@ def config_defaults(self) -> ty.Dict[str, ty.Dict[str, str]]:
344344
all_defaults[name] = defaults
345345
return all_defaults
346346

347+
def is_omitted(self, obj: ty.Any) -> bool:
348+
if full_address(obj) in self.omit_classes + self.omit_functions:
349+
return True
350+
if inspect.ismodule(obj):
351+
mod_name = obj.__name__
352+
else:
353+
mod_name = obj.__module__
354+
if any(re.match(m + r"\b", mod_name) for m in self.all_omit_modules):
355+
return True
356+
return False
357+
347358
def write(self, package_root: Path, to_include: ty.List[str] = None):
348359
"""Writes the package to the specified package root"""
349360

@@ -886,6 +897,7 @@ def write_to_module(
886897
# Write to file for debugging
887898
debug_file = "~/unparsable-nipype2pydra-output.py"
888899
with open(Path(debug_file).expanduser(), "w") as f:
900+
f.write(f"# Attemping to convert {self.nipype_name}\n")
889901
f.write(converted_code)
890902
raise RuntimeError(
891903
f"Black could not parse generated code (written to {debug_file}): "

nipype2pydra/utils/misc.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -542,9 +542,12 @@ def get_return_line(func: ty.Union[str, ty.Callable]) -> str:
542542

543543

544544
def find_super_method(
545-
super_base: type, method_name: str
545+
super_base: type, method_name: str, include_class: bool = False
546546
) -> ty.Tuple[ty.Callable, type]:
547-
for base in super_base.__mro__[1:]:
547+
mro = super_base.__mro__
548+
if not include_class:
549+
mro = mro[1:]
550+
for base in mro:
548551
if method_name in base.__dict__: # Found the match
549552
return getattr(base, method_name), base
550553
raise RuntimeError(
@@ -554,4 +557,4 @@ def find_super_method(
554557

555558

556559
def strip_comments(src: str) -> str:
557-
return re.sub(r"\s+#.*", "", src)
560+
return re.sub(r"^\s+#.*", "", src, flags=re.MULTILINE)

0 commit comments

Comments
 (0)