Skip to content

REF: Update nipype2boutiques script #2894

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 32 commits into from
Aug 13, 2019
Merged

Conversation

erinb90
Copy link
Contributor

@erinb90 erinb90 commented Feb 28, 2019

Summary

boutiques/boutiques#135

Refactored and improved the nipype2boutiques script that converts Nipype interfaces into Boutiques descriptors. It's still not perfect, and doesn't cover every edge case (might even crash for some interfaces), but it does a lot more than it did before. May update this or make another PR in the future if I find ways to improve it more.

Examples of script output

@glatard @gkiar

Acknowledgment

  • (Mandatory) I acknowledge that this contribution will be available under the Apache 2 license.

…value for tool-version when interface version is null
…ices extraction, added method to get input type from handler type
… added check for integer types, added method to get description from spec
…flag to take argstr from input spec, added requires-inputs and disables-inputs
…puts don't disable themselves, made default output path template the output id
…with various different inputs to try and generate as many outputs as possible
@codecov-io
Copy link

codecov-io commented Feb 28, 2019

Codecov Report

❗ No coverage uploaded for pull request base (master@460c1bb). Click here to learn what that means.
The diff coverage is 50%.

Impacted file tree graph

@@            Coverage Diff            @@
##             master    #2894   +/-   ##
=========================================
  Coverage          ?   64.06%           
=========================================
  Files             ?      342           
  Lines             ?    43835           
  Branches          ?     5507           
=========================================
  Hits              ?    28084           
  Misses            ?    14639           
  Partials          ?     1112
Flag Coverage Δ
#unittests 64.06% <50%> (?)
Impacted Files Coverage Δ
nipype/utils/nipype2boutiques.py 52.23% <49.83%> (ø)
nipype/scripts/cli.py 42.85% <66.66%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 460c1bb...01879f2. Read the comment docs.

@glatard
Copy link
Contributor

glatard commented Feb 28, 2019

Thanks for doing this @erinb90, it looks great to me!

erinb90 added 5 commits March 12, 2019 16:20
…nd line, added logic to deal with inputs containing name_source and name_template metadata
…to 1.0.0, added logic to deal with 0/1 booleans and tuples, added list separator and flag separator checks
Copy link
Member

@mgxd mgxd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @erinb90, thanks for the patch - it's a big jump from the previous version supported! I left a few comments (mostly style) - you'll also have to update the nipypecli call

def boutiques(interface, module, output, ignored_template_inputs, docker_image,
docker_index, ignore_template_numbers, verbose):
"""Nipype to Boutiques exporter.
See Boutiques specification at https://github.com/boutiques/schema.
"""
from nipype.utils.nipype2boutiques import generate_boutiques_descriptor
# Generates JSON string
json_string = generate_boutiques_descriptor(
module, interface, ignored_template_inputs, docker_image, docker_index,
verbose, ignore_template_numbers)

tool_desc['tags'] = desc_tags

# Check for positional arguments and reorder command line args if necessary
tool_desc['command-line'] = reorder_cmd_line_args(tool_desc['command-line'], interface, ignore_inputs)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rf: line length

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mgxd What should be the max line length?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should follow pep8 conventions, so 79 characters

if verbose:
print("-> Descriptor saved to file " + outfile.name)

print("NOTE: Descriptors produced by this script may not entirely conform to the Nipype interface "
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rf: line length


inp = {}

if input_number is not None and input_number != 0: # No need to append a number to the first of a list of compound inputs
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if input_number is not None and input_number != 0: # No need to append a number to the first of a list of compound inputs
if input_number: # No need to append a number to the first of a list of compound inputs

elif handler_type == "Float":
inp['type'] = "Number"
elif handler_type == "Bool":
if spec.argstr and len(spec.argstr.split("=")) > 1 and (spec.argstr.split("=")[1] == '0' or spec.argstr.split("=")[1] == '1'):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rf: line length

source = inp_spec.name_source[0]
else:
source = inp_spec.name_source
output['path-template'] = inp_spec.name_template.replace("%s", "[" + source.upper() + "]")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rf: line length

def get_type_from_spec_info(spec_info):
def get_boutiques_groups(input_traits):
"""
Returns a list of dictionaries containing Boutiques groups for the mutually exclusive and all-or-none Nipype inputs.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rf: line length

continue
positional_args.append(item[1])

return interface_name + " " + " ".join(positional_args) + " " + ((last_arg + " ") if last_arg else "") + " ".join(non_positional_args)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rf: line length

tool_desc['docker-index'] = docker_index
tool_desc['output-files'] = []
tool_desc['groups'] = []
tool_desc['tool-version'] = interface.version if interface.version is not None else "1.0.0"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't it be better to state no version was provided rather than assume this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what I thought at first, but then I found it looked weird on Zenodo to not have a version number here:
image


# Save descriptor to a file
if save:
path = save_path if save_path is not None else os.path.join(os.getcwd(), interface_name + '.json')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
path = save_path if save_path is not None else os.path.join(os.getcwd(), interface_name + '.json')
path = save_path or os.path.join(os.getcwd(), interface_name + '.json')

erinb90 added 2 commits March 27, 2019 17:02
…output from a Nipype input spec, code style fixes, some refactoring and improvements
Copy link
Member

@mgxd mgxd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great - thanks! To get the tests passing, you'll need to merge with master.

Only other thing I would suggest before merging this in is supplementing / adding a test that checks the boutique output, if you're up to it.

author=("Oxford Centre for Functional"
" MRI of the Brain (FMRIB)"))

with open('utils/nipype2boutiques_example.json', 'r',
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @mgxd, I want to use this example output file in my test case but am not sure how to reference it (what I have above doesn't work). What should the path be?

Copy link
Member

@mgxd mgxd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some suggestions to get stuff working again

@@ -0,0 +1,549 @@
{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this file should go in nipype/testing/data. You can then import the data with

from nipype.testing import example_data
example_data('relative/path/from/testing/directory')

" MRI of the Brain (FMRIB)"))

with open('utils/nipype2boutiques_example.json', 'r',
encoding='utf-8') as desc_file:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

encoding will not work on Py2 - though if replace open with from io import open, it should fix it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll just remove encoding, it's not necessary here.

@mgxd mgxd added this to the 1.2.0 milestone Mar 29, 2019
@erinb90
Copy link
Contributor Author

erinb90 commented Mar 29, 2019

So my test is failing because on CircleCI the descriptor produced has interface version 5.0.9, while the example file has version 1.0.0 (placeholder when version is not found). When I run nipype2boutiques locally I always see interface.version as None, so how did CircleCI get that version number?

@mgxd
Copy link
Member

mgxd commented Apr 1, 2019

@erinb90 the Circle build has FSL 5.0.9 installed, so the good thing is it's outputting what's expected. To avoid having to constantly reconfigure this test though, I would recommend checking only a subset of fields that will be consistent across environments.

@erinb90 erinb90 force-pushed the update-nipype2boutiques branch from 8976e35 to 0eae216 Compare April 4, 2019 18:53
@mgxd
Copy link
Member

mgxd commented Apr 9, 2019

@erinb90 if you sync with current master it'll fix the travis tests

inp['type'] = "String"
if trait_handler.minlen != 0:
inp['min-list-entries'] = trait_handler.minlen
if trait_handler.maxlen != six.MAXSIZE:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if trait_handler.maxlen != six.MAXSIZE:
if trait_handler.maxlen != sys.maxsize:

import simplejson as json
import six
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we don't use six as a dependency of nipype. this bit should use the relevant pieces from future

Suggested change
import six

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I seem to have broken something in the build in removing this import. It fails with:

UnsatisfiableError: The following specifications were found to be in conflict:
  - matplotlib
Use "conda search <package> --info" to see the dependencies for each package.

Do you know where this could come from?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@erinb90 the error is unrelated, and should be fixed if you fetch current master

Sorry for all these CI build inconveniences - it seems everything waited on your PR to break

@effigies effigies modified the milestones: 1.2.0, 1.2.1 May 8, 2019
@satra satra merged commit c14f24e into nipy:master Aug 13, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants