Skip to content

Commit 4801a4c

Browse files
authored
Merge pull request #2203 from oesteban/fix/VirtualDisplay
ENH: Centralize virtual/physical $DISPLAYs
2 parents 2e5f5fc + 82a2c6a commit 4801a4c

File tree

7 files changed

+281
-74
lines changed

7 files changed

+281
-74
lines changed

CHANGES

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
Upcoming release
22
================
33

4+
* ENH: Centralize virtual/physical $DISPLAYs (https://github.com/nipy/nipype/pull/#2203)
5+
* ENH: New ResourceMonitor - replaces resource profiler (https://github.com/nipy/nipype/pull/#2200)
6+
47

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

doc/users/config_file.rst

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,16 @@ Execution
7373
``false``; default value: ``true``)
7474

7575
*display_variable*
76-
What ``DISPLAY`` variable should all command line interfaces be
77-
run with. This is useful if you are using `xnest
78-
<http://www.x.org/archive/X11R7.5/doc/man/man1/Xnest.1.html>`_
79-
or `Xvfb <http://www.x.org/archive/X11R6.8.1/doc/Xvfb.1.html>`_
80-
and you would like to redirect all spawned windows to
81-
it. (possible values: any X server address; default value: not
82-
set)
76+
Override the ``$DISPLAY`` environment variable for interfaces that require
77+
an X server. This option is useful if there is a running X server, but
78+
``$DISPLAY`` was not defined in nipype's environment. For example, if an X
79+
server is listening on the default port of 6000, set ``display_variable = :0``
80+
to enable nipype interfaces to use it. It may also point to displays provided
81+
by VNC, `xnest <http://www.x.org/archive/X11R7.5/doc/man/man1/Xnest.1.html>`_
82+
or `Xvfb <http://www.x.org/archive/X11R6.8.1/doc/Xvfb.1.html>`_.
83+
If neither ``display_variable`` nor the ``$DISPLAY`` environment variable are
84+
set, nipype will try to configure a new virtual server using Xvfb.
85+
(possible values: any X server address; default value: not set)
8386

8487
*remove_unnecessary_outputs*
8588
This will remove any interface outputs not needed by the workflow. If the

nipype/interfaces/afni/preprocess.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2154,11 +2154,12 @@ class SkullStrip(AFNICommand):
21542154

21552155
def __init__(self, **inputs):
21562156
super(SkullStrip, self).__init__(**inputs)
2157+
21572158
if not no_afni():
21582159
v = Info.version()
21592160

2160-
# As of AFNI 16.0.00, redirect_x is not needed
2161-
if v[0] > 2015:
2161+
# Between AFNI 16.0.00 and 16.2.07, redirect_x is not needed
2162+
if v >= (2016, 0, 0) and v < (2016, 2, 7):
21622163
self._redirect_x = False
21632164

21642165

nipype/interfaces/base.py

Lines changed: 11 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,14 @@
5454

5555

5656
class Str(traits.Unicode):
57-
pass
58-
57+
"""Replacement for the default traits.Str based in bytes"""
5958

6059
traits.Str = Str
6160

6261

6362
class NipypeInterfaceError(Exception):
63+
"""Custom error for interfaces"""
64+
6465
def __init__(self, value):
6566
self.value = value
6667

@@ -1014,31 +1015,6 @@ def _check_version_requirements(self, trait_object, raise_exception=True):
10141015
version, max_ver))
10151016
return unavailable_traits
10161017

1017-
def _run_wrapper(self, runtime):
1018-
if self._redirect_x:
1019-
try:
1020-
from xvfbwrapper import Xvfb
1021-
except ImportError:
1022-
iflogger.error('Xvfb wrapper could not be imported')
1023-
raise
1024-
1025-
vdisp = Xvfb(nolisten='tcp')
1026-
vdisp.start()
1027-
try:
1028-
vdisp_num = vdisp.new_display
1029-
except AttributeError: # outdated version of xvfbwrapper
1030-
vdisp_num = vdisp.vdisplay_num
1031-
1032-
iflogger.info('Redirecting X to :%d' % vdisp_num)
1033-
runtime.environ['DISPLAY'] = ':%d' % vdisp_num
1034-
1035-
runtime = self._run_interface(runtime)
1036-
1037-
if self._redirect_x:
1038-
vdisp.stop()
1039-
1040-
return runtime
1041-
10421018
def _run_interface(self, runtime):
10431019
""" Core function that executes interface
10441020
"""
@@ -1080,6 +1056,9 @@ def run(self, **inputs):
10801056
store_provenance = str2bool(config.get(
10811057
'execution', 'write_provenance', 'false'))
10821058
env = deepcopy(dict(os.environ))
1059+
if self._redirect_x:
1060+
env['DISPLAY'] = config.get_display()
1061+
10831062
runtime = Bunch(cwd=os.getcwd(),
10841063
returncode=None,
10851064
duration=None,
@@ -1102,8 +1081,9 @@ def run(self, **inputs):
11021081
# Grab inputs now, as they should not change during execution
11031082
inputs = self.inputs.get_traitsfree()
11041083
outputs = None
1084+
11051085
try:
1106-
runtime = self._run_wrapper(runtime)
1086+
runtime = self._run_interface(runtime)
11071087
outputs = self.aggregate_outputs(runtime)
11081088
except Exception as e:
11091089
import traceback
@@ -1313,21 +1293,14 @@ def _canonicalize_env(env):
13131293
return out_env
13141294

13151295

1316-
def run_command(runtime, output=None, timeout=0.01, redirect_x=False):
1296+
def run_command(runtime, output=None, timeout=0.01):
13171297
"""Run a command, read stdout and stderr, prefix with timestamp.
13181298
13191299
The returned runtime contains a merged stdout+stderr log with timestamps
13201300
"""
13211301

13221302
# Init variables
13231303
cmdline = runtime.cmdline
1324-
1325-
if redirect_x:
1326-
exist_xvfb, _ = _exists_in_path('xvfb-run', runtime.environ)
1327-
if not exist_xvfb:
1328-
raise RuntimeError('Xvfb was not found, X redirection aborted')
1329-
cmdline = 'xvfb-run -a ' + cmdline
1330-
13311304
env = _canonicalize_env(runtime.environ)
13321305

13331306
default_encoding = locale.getdefaultlocale()[1]
@@ -1564,17 +1537,7 @@ def help(cls, returnhelp=False):
15641537
print(allhelp)
15651538

15661539
def _get_environ(self):
1567-
out_environ = {}
1568-
if not self._redirect_x:
1569-
try:
1570-
display_var = config.get('execution', 'display_variable')
1571-
out_environ = {'DISPLAY': display_var}
1572-
except NoOptionError:
1573-
pass
1574-
iflogger.debug(out_environ)
1575-
if isdefined(self.inputs.environ):
1576-
out_environ.update(self.inputs.environ)
1577-
return out_environ
1540+
return getattr(self.inputs, 'environ', {})
15781541

15791542
def version_from_command(self, flag='-v'):
15801543
cmdname = self.cmd.split()[0]
@@ -1591,10 +1554,6 @@ def version_from_command(self, flag='-v'):
15911554
o, e = proc.communicate()
15921555
return o
15931556

1594-
def _run_wrapper(self, runtime):
1595-
runtime = self._run_interface(runtime)
1596-
return runtime
1597-
15981557
def _run_interface(self, runtime, correct_return_codes=(0,)):
15991558
"""Execute command via subprocess
16001559
@@ -1622,8 +1581,7 @@ def _run_interface(self, runtime, correct_return_codes=(0,)):
16221581
setattr(runtime, 'command_path', cmd_path)
16231582
setattr(runtime, 'dependencies', get_dependencies(executable_name,
16241583
runtime.environ))
1625-
runtime = run_command(runtime, output=self.inputs.terminal_output,
1626-
redirect_x=self._redirect_x)
1584+
runtime = run_command(runtime, output=self.inputs.terminal_output)
16271585
if runtime.returncode is None or \
16281586
runtime.returncode not in correct_return_codes:
16291587
self.raise_exception(runtime)

nipype/interfaces/tests/test_base.py

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -639,25 +639,43 @@ def _gen_filename(self, name):
639639
nib.CommandLine.input_spec = nib.CommandLineInputSpec
640640

641641

642-
def test_Commandline_environ():
642+
def test_Commandline_environ(monkeypatch, tmpdir):
643643
from nipype import config
644644
config.set_default_config()
645+
646+
tmpdir.chdir()
647+
monkeypatch.setitem(os.environ, 'DISPLAY', ':1')
648+
# Test environment
645649
ci3 = nib.CommandLine(command='echo')
646650
res = ci3.run()
647651
assert res.runtime.environ['DISPLAY'] == ':1'
652+
653+
# Test display_variable option
654+
monkeypatch.delitem(os.environ, 'DISPLAY', raising=False)
648655
config.set('execution', 'display_variable', ':3')
649656
res = ci3.run()
650657
assert not 'DISPLAY' in ci3.inputs.environ
658+
assert not 'DISPLAY' in res.runtime.environ
659+
660+
# If the interface has _redirect_x then yes, it should be set
661+
ci3._redirect_x = True
662+
res = ci3.run()
651663
assert res.runtime.environ['DISPLAY'] == ':3'
664+
665+
# Test overwrite
666+
monkeypatch.setitem(os.environ, 'DISPLAY', ':1')
652667
ci3.inputs.environ = {'DISPLAY': ':2'}
653668
res = ci3.run()
654669
assert res.runtime.environ['DISPLAY'] == ':2'
655670

656671

657-
def test_CommandLine_output(setup_file):
658-
tmp_infile = setup_file
659-
tmpd, name = os.path.split(tmp_infile)
660-
assert os.path.exists(tmp_infile)
672+
def test_CommandLine_output(tmpdir):
673+
# Create one file
674+
tmpdir.chdir()
675+
file = tmpdir.join('foo.txt')
676+
file.write('123456\n')
677+
name = os.path.basename(file.strpath)
678+
661679
ci = nib.CommandLine(command='ls -l')
662680
ci.inputs.terminal_output = 'allatonce'
663681
res = ci.run()

0 commit comments

Comments
 (0)