Skip to content

Commit 0811b02

Browse files
committed
Added a mechanism for running interfaces that require X without having
one through Xvfb.
1 parent 84513a2 commit 0811b02

File tree

4 files changed

+54
-10
lines changed

4 files changed

+54
-10
lines changed

nipype/interfaces/base.py

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
from ..utils.provenance import write_provenance
3939
from .. import config, logging, LooseVersion
4040
from .. import __version__
41+
import random, time, fnmatch
4142

4243
nipype_version = LooseVersion(__version__)
4344

@@ -46,6 +47,25 @@
4647

4748
__docformat__ = 'restructuredtext'
4849

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

5070
def load_template(name):
5171
"""Load a template from the script_templates directory
@@ -701,6 +721,7 @@ class BaseInterface(Interface):
701721
input_spec = BaseInterfaceInputSpec
702722
_version = None
703723
_additional_metadata = []
724+
_redirect_x = False
704725

705726
def __init__(self, **inputs):
706727
if not self.input_spec:
@@ -956,7 +977,25 @@ def run(self, **inputs):
956977
hostname=getfqdn(),
957978
version=self.version)
958979
try:
980+
if self._redirect_x:
981+
vdisplay_num = _search_for_free_display()
982+
xvfb_cmd = ['Xvfb', ':%d' % vdisplay_num]
983+
xvfb_proc = subprocess.Popen(xvfb_cmd,
984+
stdout=open(os.devnull),
985+
stderr=open(os.devnull))
986+
time.sleep(0.2) # give Xvfb time to start
987+
if xvfb_proc.poll() is not None:
988+
raise Exception('Error: Xvfb did not start')
989+
old_displaynum = os.environ['DISPLAY']
990+
os.environ['DISPLAY'] = ':%s' % vdisplay_num
991+
959992
runtime = self._run_interface(runtime)
993+
994+
if self._redirect_x:
995+
xvfb_proc.kill()
996+
xvfb_proc.wait()
997+
os.environ['DISPLAY'] = old_displaynum
998+
960999
outputs = self.aggregate_outputs(runtime)
9611000
runtime.endTime = dt.isoformat(dt.utcnow())
9621001
timediff = parseutc(runtime.endTime) - parseutc(runtime.startTime)
@@ -1344,11 +1383,12 @@ def help(cls, returnhelp=False):
13441383

13451384
def _get_environ(self):
13461385
out_environ = {}
1347-
try:
1348-
display_var = config.get('execution', 'display_variable')
1349-
out_environ = {'DISPLAY': display_var}
1350-
except NoOptionError:
1351-
pass
1386+
if not self._redirect_x:
1387+
try:
1388+
display_var = config.get('execution', 'display_variable')
1389+
out_environ = {'DISPLAY': display_var}
1390+
except NoOptionError:
1391+
pass
13521392
iflogger.debug(out_environ)
13531393
if isdefined(self.inputs.environ):
13541394
out_environ.update(self.inputs.environ)

nipype/interfaces/mipav/developer.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88

99
class MedicAlgorithmSPECTRE2010InputSpec(CommandLineInputSpec):
10-
maxMemoryUsage = traits.Int(desc="Maximum Memory Allowed (in MegaBytes). Increase or decrease this depending on java virtual machine heap size requirements.", argstr="-xDefaultMem %d")
10+
maxMemoryUsage = traits.Int(desc="Maximum Memory Allowed (in MegaBytes). Increase or decrease this depending on java virtual machine heap size requirements.", argstr="--maxMemoryUsage %d")
1111
inInput = File(desc="Input volume to be skullstripped.", exists=True, argstr="--inInput %s")
1212
inAtlas = File(desc="SPECTRE atlas description file. A text file enumerating atlas files and landmarks.", exists=True, argstr="--inAtlas %s")
1313
inInitial = traits.Int(desc="Erosion of the inital mask, which is based on the probability mask and the classification., The initial mask is ouput as the d0 volume at the conclusion of SPECTRE.", argstr="--inInitial %d")
@@ -93,6 +93,7 @@ class MedicAlgorithmSPECTRE2010(SEMLikeCommandLine):
9393
output_spec = MedicAlgorithmSPECTRE2010OutputSpec
9494
_cmd = "java edu.jhu.ece.iacl.jist.cli.run edu.jhu.ece.iacl.plugins.segmentation.skull_strip.MedicAlgorithmSPECTRE2010 "
9595
_outputs_filenames = {'outd0':'outd0.nii','outOriginal':'outOriginal.nii','outMask':'outMask.nii','outSplitHalves':'outSplitHalves.nii','outMidsagittal':'outMidsagittal.nii','outPrior':'outPrior.nii','outFANTASM':'outFANTASM.nii','outSegmentation':'outSegmentation.nii','outStripped':'outStripped.nii'}
96+
_redirect_x = True
9697

9798

9899
class JistIntensityMp2rageMaskingInputSpec(CommandLineInputSpec):
@@ -133,3 +134,4 @@ class JistIntensityMp2rageMasking(SEMLikeCommandLine):
133134
output_spec = JistIntensityMp2rageMaskingOutputSpec
134135
_cmd = "java edu.jhu.ece.iacl.jist.cli.run de.mpg.cbs.jist.intensity.JistIntensityMp2rageMasking "
135136
_outputs_filenames = {'outSignal2':'outSignal2.nii','outSignal':'outSignal.nii','outMasked2':'outMasked2.nii','outMasked':'outMasked.nii'}
137+
_redirect_x = True

nipype/interfaces/mipav/generate_classes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@
127127
modules_list = modules_from_julia
128128

129129
## SlicerExecutionModel compliant tools that are usually statically built, and don't need the Slicer3 --launcher
130-
generate_all_classes(modules_list=modules_list,launcher=["java edu.jhu.ece.iacl.jist.cli.run" ])
130+
generate_all_classes(modules_list=modules_list,launcher=["java edu.jhu.ece.iacl.jist.cli.run" ], redirect_x = True)
131131
## Tools compliant with SlicerExecutionModel called from the Slicer environment (for shared lib compatibility)
132132
#launcher = ['/home/raid3/gorgolewski/software/slicer/Slicer', '--launch']
133133
#generate_all_classes(modules_list=modules_list, launcher=launcher)

nipype/interfaces/slicer/generate_classes.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ def configuration(parent_package='',top_path=None):
101101
f.close()
102102

103103

104-
def generate_all_classes(modules_list=[], launcher=[]):
104+
def generate_all_classes(modules_list=[], launcher=[], redirect_x = False):
105105
""" modules_list contains all the SEM compliant tools that should have wrappers created for them.
106106
launcher containtains the command line prefix wrapper arugments needed to prepare
107107
a proper environment for each of the modules.
@@ -111,7 +111,7 @@ def generate_all_classes(modules_list=[], launcher=[]):
111111
print("=" * 80)
112112
print("Generating Definition for module {0}".format(module))
113113
print("^" * 80)
114-
package, code, module = generate_class(module, launcher)
114+
package, code, module = generate_class(module, launcher, redirect_x = redirect_x)
115115
cur_package = all_code
116116
module_name = package.strip().split(" ")[0].split(".")[-1]
117117
for package in package.strip().split(" ")[0].split(".")[:-1]:
@@ -126,7 +126,7 @@ def generate_all_classes(modules_list=[], launcher=[]):
126126
crawl_code_struct(all_code, os.getcwd())
127127

128128

129-
def generate_class(module, launcher, strip_module_name_prefix=True):
129+
def generate_class(module, launcher, strip_module_name_prefix=True, redirect_x = False):
130130
dom = grab_xml(module, launcher)
131131
if strip_module_name_prefix:
132132
module_name = module.split(".")[-1]
@@ -293,6 +293,8 @@ def generate_class(module, launcher, strip_module_name_prefix=True):
293293
output_spec = %module_name%OutputSpec
294294
_cmd = "%launcher% %name% "
295295
%output_filenames_code%\n"""
296+
template += " _redirect_x = True\n"
297+
296298

297299
main_class = template.replace('%class_str%', class_string).replace("%module_name%", module_name).replace("%name%", module).replace("%output_filenames_code%", output_filenames_code).replace("%launcher%", " ".join(launcher))
298300

0 commit comments

Comments
 (0)