Skip to content

Sort out terminal_output #2209

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 31 commits into from
Oct 6, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
ba97ef1
[ENH] Added new fsl.WarpPointsFromStd interface
oesteban Mar 1, 2016
4bdd241
Merge branch 'master' into enh/FSLstd2imgcoords
oesteban Mar 4, 2016
82e1ce7
do not inherit from WarpPoints interfaces
oesteban Mar 4, 2016
91a7ac4
update specs
oesteban Mar 7, 2016
f5bae9f
updated CHANGES, fixed docstring
oesteban Mar 7, 2016
4540fd2
Merge branch 'master' into enh/FSLstd2imgcoords
oesteban Mar 7, 2016
4d9a080
use terminal_output
oesteban Mar 21, 2016
57d3a3b
Merge branch 'enh/FSLstd2imgcoords' of github.com:oesteban/nipype int…
oesteban Mar 21, 2016
e5a73b7
Merge remote-tracking branch 'upstream/master' into enh/FSLstd2imgcoords
oesteban Sep 8, 2016
e7e4dcc
add +IGNORE_UNICODE to doctest, update specs
oesteban Sep 8, 2016
51a7e72
Merge branch 'master' into enh/FSLstd2imgcoords
oesteban Sep 8, 2016
9e3ee31
[WIP] Revise terminal_output
oesteban Oct 4, 2017
77bcb60
fix multiproc access to terminal_output
oesteban Oct 4, 2017
e269e74
update failing spec
oesteban Oct 4, 2017
484da5d
fix terminal_output tests
oesteban Oct 4, 2017
037e932
Merge remote-tracking branch 'upstream/master' into fix/1407
oesteban Oct 5, 2017
91a0194
Merge branch 'enh/FSLstd2imgcoords' into fix/1407
oesteban Oct 5, 2017
617bba0
fix xor in AFNI OutlierCount. fixes #1406
oesteban Oct 5, 2017
19a356b
update specs of the two interfaces affected so far
oesteban Oct 5, 2017
1d22038
make ants.OutlierCount use the terminal_output. Close #1406
oesteban Oct 5, 2017
8b08f90
update documentation
oesteban Oct 5, 2017
6279fb2
fix tests
oesteban Oct 5, 2017
0abc8ac
move documentation to the interface_specs file
oesteban Oct 5, 2017
5372a79
fix CHANGES
oesteban Oct 5, 2017
82263dc
remove unnecessary dependency
oesteban Oct 5, 2017
ec4d612
Merge branch 'master' into fix/1407
effigies Oct 5, 2017
d951ab4
fix tests
oesteban Oct 5, 2017
4d4d3f0
Merge remote-tracking branch 'upstream/master' into fix/1407
oesteban Oct 6, 2017
799dfab
@effigies' and @satra's comments
oesteban Oct 6, 2017
8ed12f6
fix overcorrection
oesteban Oct 6, 2017
a66678c
update specs
oesteban Oct 6, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
3 changes: 2 additions & 1 deletion CHANGES
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
Upcoming release
================

* ENH: Improve terminal_output feature (https://github.com/nipy/nipype/pull/2209)
* ENH: Simple interface to FSL std2imgcoords (https://github.com/nipy/nipype/pull/2209, prev #1398)
* ENH: Centralize virtual/physical $DISPLAYs (https://github.com/nipy/nipype/pull/#2203)
* ENH: New ResourceMonitor - replaces resource profiler (https://github.com/nipy/nipype/pull/#2200)


0.13.1 (May 20, 2017)
=====================

Expand Down
64 changes: 64 additions & 0 deletions doc/devel/interface_specs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,70 @@ generated depending on inputs, by the tool. OutputSpecs inherit from
``interfaces.base.TraitedSpec`` directly.


Controlling outputs to terminal
-------------------------------

It is very likely that the software wrapped within the interface writes
to the standard output or the standard error of the terminal.
Interfaces provide a means to access and retrieve these outputs, by
using the ``terminal_output`` attribute: ::

import nipype.interfaces.fsl as fsl
mybet = fsl.BET(from_file='bet-settings.json')
mybet.terminal_output = 'file_split'

In the example, the ``terminal_output = 'file_split'`` will redirect the
standard output and the standard error to split files (called
``stdout.nipype`` and ``stderr.nipype`` respectively).
The possible values for ``terminal_output`` are:

*file*
Redirects both standard output and standard error to the same file
called ``output.nipype``.
Messages from both streams will be overlapped as they arrive to
the file.

*file_split*
Redirects the output streams separately, to ``stdout.nipype``
and ``stderr.nipype`` respectively, as described in the example.

*file_stdout*
Only the standard output will be redirected to ``stdout.nipype``
and the standard error will be discarded.

*file_stderr*
Only the standard error will be redirected to ``stderr.nipype``
and the standard output will be discarded.

*stream*
Both output streams are redirected to the current logger printing
their messages interleaved and immediately to the terminal.

*allatonce*
Both output streams will be forwarded to a buffer and stored
separately in the `runtime` object that the `run()` method returns.
No files are written nor streams printed out to terminal.

*none*
Both outputs are discarded

In all cases, except for the ``'none'`` setting of ``terminal_output``,
the ``run()`` method will return a "runtime" object that will contain
the streams in the corresponding properties (``runtime.stdout``
for the standard output, ``runtime.stderr`` for the standard error, and
``runtime.merged`` for both when streams are mixed, eg. when using the
*file* option). ::

import nipype.interfaces.fsl as fsl
mybet = fsl.BET(from_file='bet-settings.json')
mybet.terminal_output = 'file_split'
Copy link
Member

Choose a reason for hiding this comment

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

does this suggest that we should consider two types of fields in the json files? things that set node/interface attributes, like mem, proc, terminal_output, etc.,. and then a section that's exclusively the inputs?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

good point. We could think of a JSON representation of the interface state for nipype 2.0, don't you think?

...
result = mybet.run()
result.runtime.stdout
' ... captured standard output ...'



Traited Attributes
------------------

Expand Down
2 changes: 1 addition & 1 deletion doc/users/interface_tutorial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Specifying input settings
The nipype interface modules provide a Python interface to external
packages like FSL_ and SPM_. Within the module are a series of Python
classes which wrap specific package functionality. For example, in
the fsl module, the class :class:`nipype.interfaces.fsl.Bet` wraps the
the fsl module, the class :class:`nipype.interfaces.fsl.BET` wraps the
``bet`` command-line tool. Using the command-line tool, one would
specify input settings using flags like ``-o``, ``-m``, ``-f <f>``, etc...
However, in nipype, options are assigned to Python attributes and can
Expand Down
6 changes: 3 additions & 3 deletions doc/users/saving_workflows.rst
Original file line number Diff line number Diff line change
Expand Up @@ -82,20 +82,20 @@ This will create a file "outputtestsave.py" with the following content:
bet2.inputs.environ = {'FSLOUTPUTTYPE': 'NIFTI_GZ'}
bet2.inputs.ignore_exception = False
bet2.inputs.output_type = 'NIFTI_GZ'
bet2.inputs.terminal_output = 'stream'
bet2.terminal_output = 'stream'
# Node: testsave.bet
bet = Node(BET(), name="bet")
bet.iterables = ('frac', [0.3, 0.4])
bet.inputs.environ = {'FSLOUTPUTTYPE': 'NIFTI_GZ'}
bet.inputs.ignore_exception = False
bet.inputs.output_type = 'NIFTI_GZ'
bet.inputs.terminal_output = 'stream'
bet.terminal_output = 'stream'
# Node: testsave.maths
maths = Node(ImageMaths(), name="maths")
maths.inputs.environ = {'FSLOUTPUTTYPE': 'NIFTI_GZ'}
maths.inputs.ignore_exception = False
maths.inputs.output_type = 'NIFTI_GZ'
maths.inputs.terminal_output = 'stream'
maths.terminal_output = 'stream'
testsave.connect(bet2, ('mask_file', func), maths, "in_file2")
testsave.connect(bet, "mask_file", maths, "in_file")
testsave.connect(testfunc, "output", maths, "op_string")
Expand Down
8 changes: 4 additions & 4 deletions examples/fmri_ants_openfmri.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ def create_reg_workflow(name='registration'):
warpmean.inputs.input_image_type = 0
warpmean.inputs.interpolation = 'Linear'
warpmean.inputs.invert_transform_flags = [False, False]
warpmean.inputs.terminal_output = 'file'
warpmean.terminal_output = 'file'

register.connect(inputnode, 'target_image_brain', warpmean, 'reference_image')
register.connect(inputnode, 'mean_image', warpmean, 'input_image')
Expand All @@ -234,7 +234,7 @@ def create_reg_workflow(name='registration'):
warpall.inputs.input_image_type = 0
warpall.inputs.interpolation = 'Linear'
warpall.inputs.invert_transform_flags = [False, False]
warpall.inputs.terminal_output = 'file'
warpall.terminal_output = 'file'

register.connect(inputnode, 'target_image_brain', warpall, 'reference_image')
register.connect(inputnode, 'source_files', warpall, 'input_image')
Expand Down Expand Up @@ -428,7 +428,7 @@ def create_fs_reg_workflow(name='registration'):
warpmean.inputs.input_image_type = 0
warpmean.inputs.interpolation = 'Linear'
warpmean.inputs.invert_transform_flags = [False, False]
warpmean.inputs.terminal_output = 'file'
warpmean.terminal_output = 'file'
warpmean.inputs.args = '--float'
# warpmean.inputs.num_threads = 4
# warpmean.plugin_args = {'sbatch_args': '--mem=4G -c 4'}
Expand All @@ -443,7 +443,7 @@ def create_fs_reg_workflow(name='registration'):
warpall.inputs.input_image_type = 0
warpall.inputs.interpolation = 'Linear'
warpall.inputs.invert_transform_flags = [False, False]
warpall.inputs.terminal_output = 'file'
warpall.terminal_output = 'file'
warpall.inputs.args = '--float'
warpall.inputs.num_threads = 2
warpall.plugin_args = {'sbatch_args': '--mem=6G -c 2'}
Expand Down
4 changes: 2 additions & 2 deletions examples/rsfmri_vol_surface_preprocessing.py
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,7 @@ def create_reg_workflow(name='registration'):
warpmean.inputs.input_image_type = 3
warpmean.inputs.interpolation = 'Linear'
warpmean.inputs.invert_transform_flags = [False, False]
warpmean.inputs.terminal_output = 'file'
warpmean.terminal_output = 'file'
warpmean.inputs.args = '--float'
warpmean.inputs.num_threads = 4

Expand Down Expand Up @@ -767,7 +767,7 @@ def merge_files(in1, in2):
warpall.inputs.input_image_type = 3
warpall.inputs.interpolation = 'Linear'
warpall.inputs.invert_transform_flags = [False, False]
warpall.inputs.terminal_output = 'file'
warpall.terminal_output = 'file'
warpall.inputs.reference_image = target_file
warpall.inputs.args = '--float'
warpall.inputs.num_threads = 1
Expand Down
4 changes: 2 additions & 2 deletions examples/rsfmri_vol_surface_preprocessing_nipy.py
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,7 @@ def create_reg_workflow(name='registration'):
warpmean.inputs.input_image_type = 3
warpmean.inputs.interpolation = 'Linear'
warpmean.inputs.invert_transform_flags = [False, False]
warpmean.inputs.terminal_output = 'file'
warpmean.terminal_output = 'file'
warpmean.inputs.args = '--float'
warpmean.inputs.num_threads = 4
warpmean.plugin_args = {'sbatch_args': '-c%d' % 4}
Expand Down Expand Up @@ -704,7 +704,7 @@ def merge_files(in1, in2):
warpall.inputs.input_image_type = 3
warpall.inputs.interpolation = 'Linear'
warpall.inputs.invert_transform_flags = [False, False]
warpall.inputs.terminal_output = 'file'
warpall.terminal_output = 'file'
warpall.inputs.reference_image = target_file
warpall.inputs.args = '--float'
warpall.inputs.num_threads = 2
Expand Down
49 changes: 26 additions & 23 deletions nipype/interfaces/afni/preprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -1676,13 +1676,13 @@ class OutlierCountInputSpec(CommandLineInputSpec):
False,
usedefault=True,
argstr='-autoclip',
xor=['in_file'],
xor=['mask'],
desc='clip off small voxels')
automask = traits.Bool(
False,
usedefault=True,
argstr='-automask',
xor=['in_file'],
xor=['mask'],
desc='clip off small voxels')
fraction = traits.Bool(
False,
Expand Down Expand Up @@ -1718,28 +1718,19 @@ class OutlierCountInputSpec(CommandLineInputSpec):
out_file = File(
name_template='%s_outliers',
name_source=['in_file'],
argstr='> %s',
keep_extension=False,
position=-1,
desc='capture standard output')


class OutlierCountOutputSpec(TraitedSpec):
out_outliers = File(
exists=True,
desc='output image file name')
out_file = File(
name_template='%s_tqual',
name_source=['in_file'],
argstr='> %s',
keep_extension=False,
position=-1,
desc='capture standard output')
out_outliers = File(exists=True,
desc='output image file name')
out_file = File(desc='capture standard output')


class OutlierCount(CommandLine):
"""Calculates number of 'outliers' a 3D+time dataset, at each
time point, and writes the results to stdout.
"""Calculates number of 'outliers' at each time point of a
a 3D+time dataset.

For complete details, see the `3dToutcount Documentation
<https://afni.nimh.nih.gov/pub/dist/doc/program_help/3dToutcount.html>`_
Expand All @@ -1751,28 +1742,42 @@ class OutlierCount(CommandLine):
>>> toutcount = afni.OutlierCount()
>>> toutcount.inputs.in_file = 'functional.nii'
>>> toutcount.cmdline # doctest: +ELLIPSIS +ALLOW_UNICODE
'3dToutcount functional.nii > functional_outliers'
'3dToutcount functional.nii'
>>> res = toutcount.run() # doctest: +SKIP

"""

_cmd = '3dToutcount'
input_spec = OutlierCountInputSpec
output_spec = OutlierCountOutputSpec
_terminal_output = 'file_split'

def _parse_inputs(self, skip=None):
if skip is None:
skip = []

# This is not strictly an input, but needs be
# set before run() is called.
if self.terminal_output == 'none':
self.terminal_output = 'file_split'

if not self.inputs.save_outliers:
skip += ['outliers_file']
return super(OutlierCount, self)._parse_inputs(skip)

def _run_interface(self, runtime):
runtime = super(OutlierCount, self)._run_interface(runtime)

# Read from runtime.stdout or runtime.merged
with open(op.abspath(self.inputs.out_file), 'w') as outfh:
outfh.write(runtime.stdout or runtime.merged)
return runtime

def _list_outputs(self):
outputs = self.output_spec().get()
outputs['out_file'] = op.abspath(self.inputs.out_file)
if self.inputs.save_outliers:
outputs['out_outliers'] = op.abspath(self.inputs.outliers_file)
outputs['out_file'] = op.abspath(self.inputs.out_file)
return outputs


Expand Down Expand Up @@ -1880,13 +1885,10 @@ class ROIStatsInputSpec(CommandLineInputSpec):
desc='execute quietly',
argstr='-quiet',
position=1)
terminal_output = traits.Enum(
'allatonce',
terminal_output = traits.Enum('allatonce', deprecated='1.0.0',
desc='Control terminal output:`allatonce` - waits till command is '
'finished to display output',
nohash=True,
mandatory=True,
usedefault=True)
nohash=True)


class ROIStatsOutputSpec(TraitedSpec):
Expand Down Expand Up @@ -1915,6 +1917,7 @@ class ROIStats(AFNICommandBase):

"""
_cmd = '3dROIstats'
_terminal_output = 'allatonce'
input_spec = ROIStatsInputSpec
output_spec = ROIStatsOutputSpec

Expand Down
6 changes: 5 additions & 1 deletion nipype/interfaces/afni/tests/test_auto_ABoverlap.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,17 @@ def test_ABoverlap_inputs():
),
no_automask=dict(argstr='-no_automask',
),
num_threads=dict(nohash=True,
usedefault=True,
),
out_file=dict(argstr=' |& tee %s',
position=-1,
),
outputtype=dict(),
quiet=dict(argstr='-quiet',
),
terminal_output=dict(nohash=True,
terminal_output=dict(deprecated='1.0.0',
nohash=True,
),
verb=dict(argstr='-verb',
),
Expand Down
6 changes: 5 additions & 1 deletion nipype/interfaces/afni/tests/test_auto_AFNICommand.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,16 @@ def test_AFNICommand_inputs():
ignore_exception=dict(nohash=True,
usedefault=True,
),
num_threads=dict(nohash=True,
usedefault=True,
),
out_file=dict(argstr='-prefix %s',
name_source=['in_file'],
name_template='%s_afni',
),
outputtype=dict(),
terminal_output=dict(nohash=True,
terminal_output=dict(deprecated='1.0.0',
nohash=True,
),
)
inputs = AFNICommand.input_spec()
Expand Down
3 changes: 2 additions & 1 deletion nipype/interfaces/afni/tests/test_auto_AFNICommandBase.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ def test_AFNICommandBase_inputs():
ignore_exception=dict(nohash=True,
usedefault=True,
),
terminal_output=dict(nohash=True,
terminal_output=dict(deprecated='1.0.0',
nohash=True,
),
)
inputs = AFNICommandBase.input_spec()
Expand Down
6 changes: 5 additions & 1 deletion nipype/interfaces/afni/tests/test_auto_AFNIPythonCommand.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,16 @@ def test_AFNIPythonCommand_inputs():
ignore_exception=dict(nohash=True,
usedefault=True,
),
num_threads=dict(nohash=True,
usedefault=True,
),
out_file=dict(argstr='-prefix %s',
name_source=['in_file'],
name_template='%s_afni',
),
outputtype=dict(),
terminal_output=dict(nohash=True,
terminal_output=dict(deprecated='1.0.0',
nohash=True,
),
)
inputs = AFNIPythonCommand.input_spec()
Expand Down
6 changes: 5 additions & 1 deletion nipype/interfaces/afni/tests/test_auto_AFNItoNIFTI.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ def test_AFNItoNIFTI_inputs():
newid=dict(argstr='-newid',
xor=['oldid'],
),
num_threads=dict(nohash=True,
usedefault=True,
),
oldid=dict(argstr='-oldid',
xor=['newid'],
),
Expand All @@ -33,7 +36,8 @@ def test_AFNItoNIFTI_inputs():
outputtype=dict(),
pure=dict(argstr='-pure',
),
terminal_output=dict(nohash=True,
terminal_output=dict(deprecated='1.0.0',
nohash=True,
),
)
inputs = AFNItoNIFTI.input_spec()
Expand Down
Loading