Skip to content

Commit afae53c

Browse files
committed
Merge pull request #1156 from oesteban/fix/RedirectX
[FIX] Improve redirections of X
2 parents b77b4bc + 1b6b88b commit afae53c

File tree

2 files changed

+84
-77
lines changed

2 files changed

+84
-77
lines changed

nipype/algorithms/mesh.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ class ComputeMeshWarp(BaseInterface):
7878

7979
input_spec = ComputeMeshWarpInputSpec
8080
output_spec = ComputeMeshWarpOutputSpec
81+
_redirect_x = True
8182

8283
def _triangle_area(self, A, B, C):
8384
A = np.array(A)
@@ -221,6 +222,7 @@ class MeshWarpMaths(BaseInterface):
221222

222223
input_spec = MeshWarpMathsInputSpec
223224
output_spec = MeshWarpMathsOutputSpec
225+
_redirect_x = True
224226

225227
def _run_interface(self, runtime):
226228
try:

nipype/interfaces/base.py

Lines changed: 82 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -53,25 +53,34 @@ def __init__(self, value):
5353
def __str__(self):
5454
return repr(self.value)
5555

56-
def _lock_files():
57-
tmpdir = '/tmp'
58-
pattern = '.X*-lock'
59-
names = fnmatch.filter(os.listdir(tmpdir), pattern)
60-
ls = [os.path.join(tmpdir, child) for child in names]
61-
ls = [p for p in ls if os.path.isfile(p)]
62-
return ls
63-
64-
def _search_for_free_display():
65-
ls = [int(x.split('X')[1].split('-')[0]) for x in _lock_files()]
66-
min_display_num = 1000
67-
if len(ls):
68-
display_num = max(min_display_num, max(ls) + 1)
69-
else:
70-
display_num = min_display_num
71-
random.seed()
72-
display_num += random.randint(0, 100)
73-
return display_num
7456

57+
def _unlock_display(ndisplay):
58+
lockf = os.path.join('/tmp', '.X%d-lock' % ndisplay)
59+
try:
60+
os.remove(lockf)
61+
except:
62+
return False
63+
64+
return True
65+
66+
def _exists_in_path(cmd, environ):
67+
'''
68+
Based on a code snippet from
69+
http://orip.org/2009/08/python-checking-if-executable-exists-in.html
70+
'''
71+
72+
if 'PATH' in environ:
73+
input_environ = environ.get("PATH")
74+
else:
75+
input_environ = os.environ.get("PATH", "")
76+
extensions = os.environ.get("PATHEXT", "").split(os.pathsep)
77+
for directory in input_environ.split(os.pathsep):
78+
base = os.path.join(directory, cmd)
79+
options = [base] + [(base + ext) for ext in extensions]
80+
for filename in options:
81+
if os.path.exists(filename):
82+
return True, filename
83+
return False, None
7584

7685
def load_template(name):
7786
"""Load a template from the script_templates directory
@@ -946,6 +955,36 @@ def _check_version_requirements(self, trait_object, raise_exception=True):
946955
version, max_ver))
947956
return unavailable_traits
948957

958+
def _run_wrapper(self, runtime):
959+
sysdisplay = os.getenv('DISPLAY')
960+
if self._redirect_x:
961+
try:
962+
from xvfbwrapper import Xvfb
963+
except ImportError:
964+
iflogger.error('Xvfb wrapper could not be imported')
965+
raise
966+
967+
vdisp = Xvfb(nolisten='tcp')
968+
vdisp.start()
969+
vdisp_num = vdisp.vdisplay_num
970+
971+
iflogger.info('Redirecting X to :%d' % vdisp_num)
972+
runtime.environ['DISPLAY'] = ':%d' % vdisp_num
973+
974+
runtime = self._run_interface(runtime)
975+
976+
if self._redirect_x:
977+
if sysdisplay is None:
978+
os.unsetenv('DISPLAY')
979+
else:
980+
os.environ['DISPLAY'] = sysdisplay
981+
982+
iflogger.info('Freeing X :%d' % vdisp_num)
983+
vdisp.stop()
984+
_unlock_display(vdisp_num)
985+
986+
return runtime
987+
949988
def _run_interface(self, runtime):
950989
""" Core function that executes interface
951990
"""
@@ -982,34 +1021,7 @@ def run(self, **inputs):
9821021
hostname=getfqdn(),
9831022
version=self.version)
9841023
try:
985-
if self._redirect_x:
986-
exist_val, _ = self._exists_in_path('Xvfb',
987-
runtime.environ)
988-
if not exist_val:
989-
raise IOError("Xvfb could not be found on host %s" %
990-
(runtime.hostname))
991-
else:
992-
vdisplay_num = _search_for_free_display()
993-
xvfb_cmd = ['Xvfb', ':%d' % vdisplay_num]
994-
xvfb_proc = subprocess.Popen(xvfb_cmd,
995-
stdout=open(os.devnull),
996-
stderr=open(os.devnull))
997-
wait_step = 0.2
998-
wait_time = 0
999-
while xvfb_proc.poll() is not None:
1000-
if wait_time > config.get('execution', 'xvfb_max_wait'):
1001-
raise Exception('Error: Xvfb did not start')
1002-
time.sleep(wait_step) # give Xvfb time to start
1003-
wait_time += wait_step
1004-
1005-
runtime.environ['DISPLAY'] = ':%s' % vdisplay_num
1006-
1007-
runtime = self._run_interface(runtime)
1008-
1009-
if self._redirect_x:
1010-
xvfb_proc.kill()
1011-
xvfb_proc.wait()
1012-
1024+
runtime = self._run_wrapper(runtime)
10131025
outputs = self.aggregate_outputs(runtime)
10141026
runtime.endTime = dt.isoformat(dt.utcnow())
10151027
timediff = parseutc(runtime.endTime) - parseutc(runtime.startTime)
@@ -1169,32 +1181,39 @@ def _read(self, drain):
11691181
self._lastidx = len(self._rows)
11701182

11711183

1172-
def run_command(runtime, output=None, timeout=0.01):
1184+
def run_command(runtime, output=None, timeout=0.01, redirect_x=False):
11731185
"""Run a command, read stdout and stderr, prefix with timestamp.
11741186
11751187
The returned runtime contains a merged stdout+stderr log with timestamps
11761188
"""
11771189
PIPE = subprocess.PIPE
11781190

1191+
cmdline = runtime.cmdline
1192+
if redirect_x:
1193+
exist_xvfb, _ = _exists_in_path('xvfb-run', runtime.environ)
1194+
if not exist_xvfb:
1195+
raise RuntimeError('Xvfb was not found, X redirection aborted')
1196+
cmdline = 'xvfb-run -a ' + cmdline
1197+
11791198
if output == 'file':
11801199
errfile = os.path.join(runtime.cwd, 'stderr.nipype')
11811200
outfile = os.path.join(runtime.cwd, 'stdout.nipype')
11821201
stderr = open(errfile, 'wt')
11831202
stdout = open(outfile, 'wt')
11841203

1185-
proc = subprocess.Popen(runtime.cmdline,
1204+
proc = subprocess.Popen(cmdline,
11861205
stdout=stdout,
11871206
stderr=stderr,
11881207
shell=True,
11891208
cwd=runtime.cwd,
11901209
env=runtime.environ)
11911210
else:
1192-
proc = subprocess.Popen(runtime.cmdline,
1193-
stdout=PIPE,
1194-
stderr=PIPE,
1195-
shell=True,
1196-
cwd=runtime.cwd,
1197-
env=runtime.environ)
1211+
proc = subprocess.Popen(cmdline,
1212+
stdout=PIPE,
1213+
stderr=PIPE,
1214+
shell=True,
1215+
cwd=runtime.cwd,
1216+
env=runtime.environ)
11981217
result = {}
11991218
errfile = os.path.join(runtime.cwd, 'stderr.nipype')
12001219
outfile = os.path.join(runtime.cwd, 'stdout.nipype')
@@ -1412,7 +1431,7 @@ def _get_environ(self):
14121431

14131432
def version_from_command(self, flag='-v'):
14141433
cmdname = self.cmd.split()[0]
1415-
if self._exists_in_path(cmdname):
1434+
if _exists_in_path(cmdname):
14161435
env = deepcopy(os.environ.data)
14171436
out_environ = self._get_environ()
14181437
env.update(out_environ)
@@ -1425,6 +1444,10 @@ def version_from_command(self, flag='-v'):
14251444
o, e = proc.communicate()
14261445
return o
14271446

1447+
def _run_wrapper(self, runtime):
1448+
runtime = self._run_interface(runtime)
1449+
return runtime
1450+
14281451
def _run_interface(self, runtime, correct_return_codes=[0]):
14291452
"""Execute command via subprocess
14301453
@@ -1444,40 +1467,22 @@ def _run_interface(self, runtime, correct_return_codes=[0]):
14441467
out_environ = self._get_environ()
14451468
runtime.environ.update(out_environ)
14461469
executable_name = self.cmd.split()[0]
1447-
exist_val, cmd_path = self._exists_in_path(executable_name,
1448-
runtime.environ)
1470+
exist_val, cmd_path = _exists_in_path(executable_name,
1471+
runtime.environ)
14491472
if not exist_val:
14501473
raise IOError("%s could not be found on host %s" %
14511474
(self.cmd.split()[0], runtime.hostname))
14521475
setattr(runtime, 'command_path', cmd_path)
14531476
setattr(runtime, 'dependencies', get_dependencies(executable_name,
14541477
runtime.environ))
1455-
runtime = run_command(runtime, output=self.inputs.terminal_output)
1478+
runtime = run_command(runtime, output=self.inputs.terminal_output,
1479+
redirect_x=self._redirect_x)
14561480
if runtime.returncode is None or \
14571481
runtime.returncode not in correct_return_codes:
14581482
self.raise_exception(runtime)
14591483

14601484
return runtime
14611485

1462-
def _exists_in_path(self, cmd, environ):
1463-
'''
1464-
Based on a code snippet from
1465-
http://orip.org/2009/08/python-checking-if-executable-exists-in.html
1466-
'''
1467-
1468-
if 'PATH' in environ:
1469-
input_environ = environ.get("PATH")
1470-
else:
1471-
input_environ = os.environ.get("PATH", "")
1472-
extensions = os.environ.get("PATHEXT", "").split(os.pathsep)
1473-
for directory in input_environ.split(os.pathsep):
1474-
base = os.path.join(directory, cmd)
1475-
options = [base] + [(base + ext) for ext in extensions]
1476-
for filename in options:
1477-
if os.path.exists(filename):
1478-
return True, filename
1479-
return False, None
1480-
14811486
def _format_arg(self, name, trait_spec, value):
14821487
"""A helper function for _parse_inputs
14831488

0 commit comments

Comments
 (0)