@@ -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
7685def 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