Skip to content

Commit dea0721

Browse files
authored
Merge branch 'master' into fix/2300
2 parents 71b1787 + 10a0a41 commit dea0721

File tree

7 files changed

+90
-61
lines changed

7 files changed

+90
-61
lines changed

CHANGES

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ Upcoming release
88
###### [Full changelog](https://github.com/nipy/nipype/milestone/13)
99

1010
* FIX: MultiProc mishandling crashes (https://github.com/nipy/nipype/pull/2301)
11+
* MAINT: Revise use of `subprocess.Popen` (https://github.com/nipy/nipype/pull/2289)
12+
* ENH: Memorize version checks (https://github.com/nipy/nipype/pull/2274, https://github.com/nipy/nipype/pull/2295)
1113

1214

1315
0.14.0rc1 (November 21, 2017)

nipype/interfaces/afni/tests/test_auto_Unifize.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ def test_Unifize_inputs():
3232
name_source='in_file',
3333
),
3434
outputtype=dict(),
35+
rbt=dict(argstr='-rbt %f %f %f',
36+
),
3537
scale_file=dict(argstr='-ssave %s',
3638
),
3739
t2=dict(argstr='-T2',

nipype/interfaces/afni/utils.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2329,6 +2329,14 @@ class UnifizeInputSpec(AFNICommandInputSpec):
23292329
argstr='-EPI',
23302330
requires=['no_duplo', 't2'],
23312331
xor=['gm'])
2332+
rbt = traits.Tuple(
2333+
traits.Float(), traits.Float(), traits.Float(),
2334+
desc='Option for AFNI experts only.'
2335+
'Specify the 3 parameters for the algorithm:\n'
2336+
'R = radius; same as given by option \'-Urad\', [default=18.3]\n'
2337+
'b = bottom percentile of normalizing data range, [default=70.0]\n'
2338+
'r = top percentile of normalizing data range, [default=80.0]\n',
2339+
argstr='-rbt %f %f %f')
23322340

23332341

23342342
class UnifizeOutputSpec(TraitedSpec):

nipype/interfaces/base.py

Lines changed: 52 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,10 @@
99
Requires Packages to be installed
1010
"""
1111
from __future__ import print_function, division, unicode_literals, absolute_import
12-
from future import standard_library
13-
standard_library.install_aliases()
12+
import gc
13+
1414
from builtins import range, object, open, str, bytes
1515

16-
from configparser import NoOptionError
1716
from copy import deepcopy
1817
import datetime
1918
from datetime import datetime as dt
@@ -26,7 +25,6 @@
2625
import select
2726
import subprocess as sp
2827
import sys
29-
import time
3028
from textwrap import wrap
3129
from warnings import warn
3230
import simplejson as json
@@ -43,6 +41,8 @@
4341
traits, Undefined, TraitDictObject, TraitListObject, TraitError, isdefined,
4442
File, Directory, DictStrStr, has_metadata, ImageFile)
4543
from ..external.due import due
44+
from future import standard_library
45+
standard_library.install_aliases()
4646

4747
nipype_version = Version(__version__)
4848
iflogger = logging.getLogger('interface')
@@ -58,6 +58,7 @@
5858
class Str(traits.Unicode):
5959
"""Replacement for the default traits.Str based in bytes"""
6060

61+
6162
traits.Str = Str
6263

6364

@@ -1260,6 +1261,7 @@ class SimpleInterface(BaseInterface):
12601261
>>> os.chdir(old.strpath)
12611262
12621263
"""
1264+
12631265
def __init__(self, from_file=None, resource_monitor=None, **inputs):
12641266
super(SimpleInterface, self).__init__(
12651267
from_file=from_file, resource_monitor=resource_monitor, **inputs)
@@ -1387,8 +1389,7 @@ def run_command(runtime, output=None, timeout=0.01):
13871389
shell=True,
13881390
cwd=runtime.cwd,
13891391
env=env,
1390-
close_fds=True,
1391-
)
1392+
close_fds=True)
13921393
result = {
13931394
'stdout': [],
13941395
'stderr': [],
@@ -1427,37 +1428,50 @@ def _process(drain=0):
14271428
temp.sort()
14281429
result['merged'] = [r[1] for r in temp]
14291430

1430-
if output == 'allatonce':
1431-
stdout, stderr = proc.communicate()
1432-
result['stdout'] = read_stream(stdout, logger=iflogger)
1433-
result['stderr'] = read_stream(stderr, logger=iflogger)
1434-
1435-
elif output.startswith('file'):
1431+
if output.startswith('file'):
14361432
proc.wait()
14371433
if outfile is not None:
14381434
stdout.flush()
14391435
stdout.close()
14401436
with open(outfile, 'rb') as ofh:
14411437
stdoutstr = ofh.read()
14421438
result['stdout'] = read_stream(stdoutstr, logger=iflogger)
1439+
del stdoutstr
14431440

14441441
if errfile is not None:
14451442
stderr.flush()
14461443
stderr.close()
14471444
with open(errfile, 'rb') as efh:
14481445
stderrstr = efh.read()
14491446
result['stderr'] = read_stream(stderrstr, logger=iflogger)
1447+
del stderrstr
14501448

14511449
if output == 'file':
14521450
result['merged'] = result['stdout']
14531451
result['stdout'] = []
14541452
else:
1455-
proc.communicate() # Discard stdout and stderr
1453+
stdout, stderr = proc.communicate()
1454+
if output == 'allatonce': # Discard stdout and stderr otherwise
1455+
result['stdout'] = read_stream(stdout, logger=iflogger)
1456+
result['stderr'] = read_stream(stderr, logger=iflogger)
1457+
1458+
runtime.returncode = proc.returncode
1459+
try:
1460+
proc.terminate() # Ensure we are done
1461+
except OSError as error:
1462+
# Python 2 raises when the process is already gone
1463+
if error.errno != errno.ESRCH:
1464+
raise
1465+
1466+
# Dereference & force GC for a cleanup
1467+
del proc
1468+
del stdout
1469+
del stderr
1470+
gc.collect()
14561471

14571472
runtime.stderr = '\n'.join(result['stderr'])
14581473
runtime.stdout = '\n'.join(result['stdout'])
14591474
runtime.merged = '\n'.join(result['merged'])
1460-
runtime.returncode = proc.returncode
14611475
return runtime
14621476

14631477

@@ -1467,21 +1481,26 @@ def get_dependencies(name, environ):
14671481
Uses otool on darwin, ldd on linux. Currently doesn't support windows.
14681482
14691483
"""
1484+
cmd = None
14701485
if sys.platform == 'darwin':
1471-
proc = sp.Popen('otool -L `which %s`' % name,
1472-
stdout=sp.PIPE,
1473-
stderr=sp.PIPE,
1474-
shell=True,
1475-
env=environ)
1486+
cmd = 'otool -L `which {}`'.format
14761487
elif 'linux' in sys.platform:
1477-
proc = sp.Popen('ldd `which %s`' % name,
1478-
stdout=sp.PIPE,
1479-
stderr=sp.PIPE,
1480-
shell=True,
1481-
env=environ)
1482-
else:
1488+
cmd = 'ldd -L `which {}`'.format
1489+
1490+
if cmd is None:
14831491
return 'Platform %s not supported' % sys.platform
1484-
o, e = proc.communicate()
1492+
1493+
try:
1494+
proc = sp.Popen(
1495+
cmd(name), stdout=sp.PIPE, stderr=sp.PIPE, shell=True,
1496+
env=environ, close_fds=True)
1497+
o, _ = proc.communicate()
1498+
proc.terminate()
1499+
gc.collect()
1500+
except:
1501+
iflogger.warning(
1502+
'Could not get linked libraries for "%s".', name)
1503+
return 'Failed collecting dependencies'
14851504
return o.rstrip()
14861505

14871506

@@ -1572,6 +1591,9 @@ def __init__(self, command=None, terminal_output=None, **inputs):
15721591
# Set command. Input argument takes precedence
15731592
self._cmd = command or getattr(self, '_cmd', None)
15741593

1594+
# Store dependencies in runtime object
1595+
self._ldd = str2bool(config.get('execution', 'get_linked_libs', 'true'))
1596+
15751597
if self._cmd is None:
15761598
raise Exception("Missing command")
15771599

@@ -1620,6 +1642,8 @@ def _get_environ(self):
16201642
return getattr(self.inputs, 'environ', {})
16211643

16221644
def version_from_command(self, flag='-v'):
1645+
iflogger.warning('version_from_command member of CommandLine was '
1646+
'Deprecated in nipype-1.0.0 and deleted in 1.1.0')
16231647
cmdname = self.cmd.split()[0]
16241648
env = dict(os.environ)
16251649
if _exists_in_path(cmdname, env):
@@ -1664,7 +1688,8 @@ def _run_interface(self, runtime, correct_return_codes=(0,)):
16641688
(self.cmd.split()[0], runtime.hostname))
16651689

16661690
runtime.command_path = cmd_path
1667-
runtime.dependencies = get_dependencies(executable_name, runtime.environ)
1691+
runtime.dependencies = (get_dependencies(executable_name, runtime.environ)
1692+
if self._ldd else '<skipped>')
16681693
runtime = run_command(runtime, output=self.terminal_output)
16691694
if runtime.returncode is None or \
16701695
runtime.returncode not in correct_return_codes:

nipype/interfaces/fsl/base.py

Lines changed: 18 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -26,56 +26,47 @@
2626
2727
"""
2828
from __future__ import print_function, division, unicode_literals, absolute_import
29-
from builtins import open, object
3029

3130
from glob import glob
3231
import os
3332

3433
from ... import logging
3534
from ...utils.filemanip import fname_presuffix
36-
from ..base import traits, isdefined, CommandLine, CommandLineInputSpec
35+
from ..base import traits, isdefined, CommandLine, CommandLineInputSpec, PackageInfo
3736
from ...external.due import BibTeX
3837

3938
IFLOGGER = logging.getLogger('interface')
4039

4140

42-
class Info(object):
43-
"""Handle fsl output type and version information.
44-
45-
version refers to the version of fsl on the system
41+
class Info(PackageInfo):
42+
"""
43+
Handle FSL ``output_type`` and version information.
4644
4745
output type refers to the type of file fsl defaults to writing
4846
eg, NIFTI, NIFTI_GZ
4947
48+
Examples
49+
--------
50+
51+
>>> from nipype.interfaces.fsl import Info
52+
>>> Info.version() # doctest: +SKIP
53+
>>> Info.output_type() # doctest: +SKIP
54+
55+
5056
"""
5157

5258
ftypes = {'NIFTI': '.nii',
5359
'NIFTI_PAIR': '.img',
5460
'NIFTI_GZ': '.nii.gz',
5561
'NIFTI_PAIR_GZ': '.img.gz'}
5662

57-
@staticmethod
58-
def version():
59-
"""Check for fsl version on system
60-
61-
Parameters
62-
----------
63-
None
64-
65-
Returns
66-
-------
67-
version : str
68-
Version number as string or None if FSL not found
63+
if os.getenv('FSLDIR'):
64+
version_file = os.path.join(
65+
os.getenv('FSLDIR'), 'etc', 'fslversion')
6966

70-
"""
71-
# find which fsl being used....and get version from
72-
# /path/to/fsl/etc/fslversion
73-
try:
74-
basedir = os.environ['FSLDIR']
75-
except KeyError:
76-
return None
77-
out = open('%s/etc/fslversion' % (basedir)).read()
78-
return out.strip('\n')
67+
@staticmethod
68+
def parse_version(raw_info):
69+
return raw_info.splitlines()[0]
7970

8071
@classmethod
8172
def output_type_to_ext(cls, output_type):

nipype/pipeline/engine/workflows.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262

6363
logger = logging.getLogger('workflow')
6464

65+
6566
class Workflow(EngineBase):
6667
"""Controls the setup and execution of a pipeline of processes."""
6768

@@ -196,7 +197,7 @@ def connect(self, *args, **kwargs):
196197
# determine their inputs/outputs depending on
197198
# connection settings. Skip these modules in the check
198199
if dest in connected_ports[destnode]:
199-
raise Exception("""
200+
raise Exception("""\
200201
Trying to connect %s:%s to %s:%s but input '%s' of node '%s' is already
201202
connected.
202203
""" % (srcnode, source, destnode, dest, dest, destnode))
@@ -297,7 +298,7 @@ def disconnect(self, *args):
297298
remove = []
298299
for edge in conn:
299300
if edge in ed_conns:
300-
idx = ed_conns.index(edge)
301+
# idx = ed_conns.index(edge)
301302
remove.append((edge[0], edge[1]))
302303

303304
logger.debug('disconnect(): remove list %s', to_str(remove))
@@ -426,7 +427,7 @@ def write_graph(self, dotfilename='graph.dot', graph2use='hierarchical',
426427
base_dir = os.getcwd()
427428
base_dir = make_output_dir(base_dir)
428429
if graph2use in ['hierarchical', 'colored']:
429-
if self.name[:1].isdigit(): # these graphs break if int
430+
if self.name[:1].isdigit(): # these graphs break if int
430431
raise ValueError('{} graph failed, workflow name cannot begin '
431432
'with a number'.format(graph2use))
432433
dotfilename = op.join(base_dir, dotfilename)
@@ -646,7 +647,7 @@ def _write_report_info(self, workingdir, name, graph):
646647
# Avoid RuntimeWarning: divide by zero encountered in log10
647648
num_nodes = len(nodes)
648649
if num_nodes > 0:
649-
index_name = np.ceil(np.log10(num_nodes)).astype(int)
650+
index_name = np.ceil(np.log10(num_nodes)).astype(int)
650651
else:
651652
index_name = 0
652653
template = '%%0%dd_' % index_name
@@ -794,10 +795,10 @@ def _get_outputs(self):
794795
setattr(outputdict, node.name, outputs)
795796
return outputdict
796797

797-
def _set_input(self, object, name, newvalue):
798+
def _set_input(self, objekt, name, newvalue):
798799
"""Trait callback function to update a node input
799800
"""
800-
object.traits()[name].node.set_input(name, newvalue)
801+
objekt.traits()[name].node.set_input(name, newvalue)
801802

802803
def _set_node_input(self, node, param, source, sourceinfo):
803804
"""Set inputs of a node given the edge connection"""

nipype/pipeline/plugins/multiproc.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ def __init__(self, plugin_args=None):
127127

128128
# Instantiate different thread pools for non-daemon processes
129129
logger.debug('MultiProcPlugin starting in "%sdaemon" mode (n_procs=%d, mem_gb=%0.2f)',
130-
'non' if non_daemon else '', self.processors, self.memory_gb)
130+
'non' * int(non_daemon), self.processors, self.memory_gb)
131131

132132
NipypePool = NonDaemonPool if non_daemon else Pool
133133
try:

0 commit comments

Comments
 (0)