Skip to content

Commit 3637201

Browse files
committed
Fixes #1155
1 parent 27b3284 commit 3637201

File tree

1 file changed

+82
-36
lines changed

1 file changed

+82
-36
lines changed

nipype/interfaces/base.py

Lines changed: 82 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,17 @@ def _lock_files():
6161
ls = [p for p in ls if os.path.isfile(p)]
6262
return ls
6363

64+
def _unlock_display(ndisplay):
65+
lockf = os.path.join('/tmp', '.X%d-lock' % ndisplay)
66+
try:
67+
os.remove(lockf)
68+
except:
69+
return False
70+
71+
return True
72+
73+
74+
6475
def _search_for_free_display():
6576
ls = [int(x.split('X')[1].split('-')[0]) for x in _lock_files()]
6677
min_display_num = 1000
@@ -946,6 +957,36 @@ def _check_version_requirements(self, trait_object, raise_exception=True):
946957
version, max_ver))
947958
return unavailable_traits
948959

960+
def _run_wrapper(self, runtime):
961+
sysdisplay = os.getenv('DISPLAY')
962+
if self._redirect_x:
963+
try:
964+
from xvfbwrapper import Xvfb
965+
except ImportError:
966+
iflogger.error('Xvfb wrapper could not be imported')
967+
raise
968+
969+
vdisp = Xvfb(nolisten='tcp')
970+
vdisp.start()
971+
vdisp_num = vdisp.vdisplay_num
972+
973+
iflogger.info('Redirecting X to :%d' % vdisp_num)
974+
runtime.environ['DISPLAY'] = ':%d' % vdisp_num
975+
976+
runtime = self._run_interface(runtime)
977+
978+
if self._redirect_x:
979+
if sysdisplay is None:
980+
os.unsetenv('DISPLAY')
981+
else:
982+
os.environ['DISPLAY'] = sysdisplay
983+
984+
iflogger.info('Freeing X :%d' % vdisp_num)
985+
vdisp.stop()
986+
_unlock_display(vdisp_num)
987+
988+
return runtime
989+
949990
def _run_interface(self, runtime):
950991
""" Core function that executes interface
951992
"""
@@ -982,14 +1023,7 @@ def run(self, **inputs):
9821023
hostname=getfqdn(),
9831024
version=self.version)
9841025
try:
985-
if self._redirect_x:
986-
from xvfbwrapper import Xvfb
987-
with Xvfb() as xvfb:
988-
runtime = self._run_interface(runtime)
989-
else:
990-
iflogger.warn('Error redirecting X, trying without xvfb...')
991-
runtime = self._run_interface(runtime)
992-
1026+
runtime = self._run_wrapper(runtime)
9931027
outputs = self.aggregate_outputs(runtime)
9941028
runtime.endTime = dt.isoformat(dt.utcnow())
9951029
timediff = parseutc(runtime.endTime) - parseutc(runtime.startTime)
@@ -1097,6 +1131,25 @@ def version(self):
10971131
self.__class__.__name__)
10981132
return self._version
10991133

1134+
def _exists_in_path(self, cmd, environ):
1135+
'''
1136+
Based on a code snippet from
1137+
http://orip.org/2009/08/python-checking-if-executable-exists-in.html
1138+
'''
1139+
1140+
if 'PATH' in environ:
1141+
input_environ = environ.get("PATH")
1142+
else:
1143+
input_environ = os.environ.get("PATH", "")
1144+
extensions = os.environ.get("PATHEXT", "").split(os.pathsep)
1145+
for directory in input_environ.split(os.pathsep):
1146+
base = os.path.join(directory, cmd)
1147+
options = [base] + [(base + ext) for ext in extensions]
1148+
for filename in options:
1149+
if os.path.exists(filename):
1150+
return True, filename
1151+
return False, None
1152+
11001153

11011154
class Stream(object):
11021155
"""Function to capture stdout and stderr streams with timestamps
@@ -1149,32 +1202,39 @@ def _read(self, drain):
11491202
self._lastidx = len(self._rows)
11501203

11511204

1152-
def run_command(runtime, output=None, timeout=0.01):
1205+
def run_command(runtime, output=None, timeout=0.01, redirect_x=False):
11531206
"""Run a command, read stdout and stderr, prefix with timestamp.
11541207
11551208
The returned runtime contains a merged stdout+stderr log with timestamps
11561209
"""
11571210
PIPE = subprocess.PIPE
11581211

1212+
cmdline = runtime.cmdline
1213+
if redirect_x:
1214+
exist_xvfb, _ = self._exists_in_path('xvfb-run', runtime.environ)
1215+
if not exist_val:
1216+
raise RuntimeError('Xvfb was not found, X redirection aborted')
1217+
cmdline = 'xvfb-run -a ' + cmdline
1218+
11591219
if output == 'file':
11601220
errfile = os.path.join(runtime.cwd, 'stderr.nipype')
11611221
outfile = os.path.join(runtime.cwd, 'stdout.nipype')
11621222
stderr = open(errfile, 'wt')
11631223
stdout = open(outfile, 'wt')
11641224

1165-
proc = subprocess.Popen(runtime.cmdline,
1225+
proc = subprocess.Popen(cmdline,
11661226
stdout=stdout,
11671227
stderr=stderr,
11681228
shell=True,
11691229
cwd=runtime.cwd,
11701230
env=runtime.environ)
11711231
else:
1172-
proc = subprocess.Popen(runtime.cmdline,
1173-
stdout=PIPE,
1174-
stderr=PIPE,
1175-
shell=True,
1176-
cwd=runtime.cwd,
1177-
env=runtime.environ)
1232+
proc = subprocess.Popen(cmdline,
1233+
stdout=PIPE,
1234+
stderr=PIPE,
1235+
shell=True,
1236+
cwd=runtime.cwd,
1237+
env=runtime.environ)
11781238
result = {}
11791239
errfile = os.path.join(runtime.cwd, 'stderr.nipype')
11801240
outfile = os.path.join(runtime.cwd, 'stdout.nipype')
@@ -1405,6 +1465,10 @@ def version_from_command(self, flag='-v'):
14051465
o, e = proc.communicate()
14061466
return o
14071467

1468+
def _run_wrapper(self, runtime):
1469+
runtime = self._run_interface(runtime)
1470+
return runtime
1471+
14081472
def _run_interface(self, runtime, correct_return_codes=[0]):
14091473
"""Execute command via subprocess
14101474
@@ -1432,32 +1496,14 @@ def _run_interface(self, runtime, correct_return_codes=[0]):
14321496
setattr(runtime, 'command_path', cmd_path)
14331497
setattr(runtime, 'dependencies', get_dependencies(executable_name,
14341498
runtime.environ))
1435-
runtime = run_command(runtime, output=self.inputs.terminal_output)
1499+
runtime = run_command(runtime, output=self.inputs.terminal_output,
1500+
redirect_x=self._redirect_x)
14361501
if runtime.returncode is None or \
14371502
runtime.returncode not in correct_return_codes:
14381503
self.raise_exception(runtime)
14391504

14401505
return runtime
14411506

1442-
def _exists_in_path(self, cmd, environ):
1443-
'''
1444-
Based on a code snippet from
1445-
http://orip.org/2009/08/python-checking-if-executable-exists-in.html
1446-
'''
1447-
1448-
if 'PATH' in environ:
1449-
input_environ = environ.get("PATH")
1450-
else:
1451-
input_environ = os.environ.get("PATH", "")
1452-
extensions = os.environ.get("PATHEXT", "").split(os.pathsep)
1453-
for directory in input_environ.split(os.pathsep):
1454-
base = os.path.join(directory, cmd)
1455-
options = [base] + [(base + ext) for ext in extensions]
1456-
for filename in options:
1457-
if os.path.exists(filename):
1458-
return True, filename
1459-
return False, None
1460-
14611507
def _format_arg(self, name, trait_spec, value):
14621508
"""A helper function for _parse_inputs
14631509

0 commit comments

Comments
 (0)