-
Notifications
You must be signed in to change notification settings - Fork 532
ENH: Centralize virtual/physical $DISPLAYs #2203
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 8 commits
94146e7
e8e18f0
486cef3
44b4880
8a16650
0a718c1
2f83d08
7644acf
2c89c58
f634c0b
5151bf6
46dff1c
fc75e0f
82a2c6a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2146,11 +2146,12 @@ class SkullStrip(AFNICommand): | |
|
||
def __init__(self, **inputs): | ||
super(SkullStrip, self).__init__(**inputs) | ||
|
||
if not no_afni(): | ||
v = Info.version() | ||
|
||
# As of AFNI 16.0.00, redirect_x is not needed | ||
if v[0] > 2015: | ||
# Between AFNI 16.0.00 and 16.2.07, redirect_x is not needed | ||
if v >= (2016, 0, 0) and v < (2016, 2, 7): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. woah - is this really true - crazy! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. oh yeah, tell me about it. I'll report this to AFNI, to make sure this is not a mistake linking. |
||
self._redirect_x = False | ||
|
||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,21 +11,19 @@ | |
''' | ||
from __future__ import print_function, division, unicode_literals, absolute_import | ||
import os | ||
import shutil | ||
import errno | ||
from warnings import warn | ||
import atexit | ||
from io import StringIO | ||
from distutils.version import LooseVersion | ||
from simplejson import load, dump | ||
import numpy as np | ||
|
||
from builtins import str, object, open | ||
from future import standard_library | ||
standard_library.install_aliases() | ||
|
||
import configparser | ||
from ..external import portalocker | ||
import configparser | ||
|
||
from future import standard_library | ||
standard_library.install_aliases() | ||
|
||
NUMPY_MMAP = LooseVersion(np.__version__) >= LooseVersion('1.12.0') | ||
|
||
|
@@ -44,7 +42,6 @@ | |
[execution] | ||
create_report = true | ||
crashdump_dir = %s | ||
display_variable = :1 | ||
hash_method = timestamp | ||
job_finished_timeout = 5 | ||
keep_inputs = false | ||
|
@@ -82,7 +79,8 @@ def mkdir_p(path): | |
|
||
|
||
class NipypeConfig(object): | ||
"""Base nipype config class | ||
""" | ||
Base nipype config class | ||
""" | ||
|
||
def __init__(self, *args, **kwargs): | ||
|
@@ -91,6 +89,7 @@ def __init__(self, *args, **kwargs): | |
config_file = os.path.join(config_dir, 'nipype.cfg') | ||
self.data_file = os.path.join(config_dir, 'nipype.json') | ||
self._config.readfp(StringIO(default_cfg)) | ||
self._display = None | ||
if os.path.exists(config_dir): | ||
self._config.read([config_file, 'nipype.cfg']) | ||
|
||
|
@@ -172,3 +171,66 @@ def update_matplotlib(self): | |
def enable_provenance(self): | ||
self._config.set('execution', 'write_provenance', 'true') | ||
self._config.set('execution', 'hash_method', 'content') | ||
|
||
def get_display(self): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If I understand it, if this has not been called before workflow runtime, then each node will get a copy of And I don't see anywhere where it's stopped. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The first time you call it, one display will be locked. As a matter of fact, when I first wrote the tests (https://github.com/oesteban/nipype/blob/fix/VirtualDisplay/nipype/utils/tests/test_config.py#L9-L49) I realized I had to manally reset the config object all the times because once it is called it is fixed to that display. Therefore, all those Does this reply to your question? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okay. I meant to put at the end of the first sentence: "Is this the desired behavior?" Sounds like it is. Similarly, the fact that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, that was actually the problem: nipype would spawn an endless number of displays. Well, it is probably nice to add a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, added a callback when python exists the session :D. |
||
"""Returns the first display available""" | ||
|
||
# Check if an Xorg server is listening | ||
# import subprocess as sp | ||
# if not hasattr(sp, 'DEVNULL'): | ||
# setattr(sp, 'DEVNULL', os.devnull) | ||
# x_listening = bool(sp.call('ps au | grep -v grep | grep -i xorg', | ||
# shell=True, stdout=sp.DEVNULL)) | ||
|
||
if self._display is not None: | ||
return ':%d' % self._display.vdisplay_num | ||
|
||
sysdisplay = None | ||
if self._config.has_option('execution', 'display_variable'): | ||
sysdisplay = self._config.get('execution', 'display_variable') | ||
|
||
sysdisplay = sysdisplay or os.getenv('DISPLAY') | ||
if sysdisplay: | ||
from collections import namedtuple | ||
|
||
def _mock(): | ||
pass | ||
|
||
# Store a fake Xvfb object | ||
ndisp = int(sysdisplay.split(':')[-1]) | ||
Xvfb = namedtuple('Xvfb', ['vdisplay_num', 'stop']) | ||
self._display = Xvfb(ndisp, _mock) | ||
return sysdisplay | ||
else: | ||
# If $DISPLAY is empty, it confuses Xvfb so unset | ||
if sysdisplay == '': | ||
del os.environ['DISPLAY'] | ||
try: | ||
from xvfbwrapper import Xvfb | ||
except ImportError: | ||
raise RuntimeError( | ||
'A display server was required, but $DISPLAY is not defined ' | ||
'and Xvfb could not be imported.') | ||
|
||
self._display = Xvfb(nolisten='tcp') | ||
self._display.start() | ||
|
||
# Older versions of Xvfb used vdisplay_num | ||
if hasattr(self._display, 'vdisplay_num'): | ||
return ':%d' % self._display.vdisplay_num | ||
|
||
if hasattr(self._display, 'new_display'): | ||
return ':%d' % self._display.new_display | ||
|
||
def stop_display(self): | ||
"""Closes the display if started""" | ||
if self._display is not None: | ||
self._display.stop() | ||
|
||
|
||
@atexit.register | ||
def free_display(): | ||
from nipype import config | ||
from nipype import logging | ||
config.stop_display() | ||
logging.getLogger('interface').info('Closing display (if virtual)') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If nipype is going to spawn many I think I must be missing something here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually the opposite. You want nipype to run only one The rationale is precisely stop spawning many There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see. And you're testing to make sure you're not producing multiple by running multiple interfaces? Because my first comment noted that it looks like each Node makes a copy of Again, this is from a very cursory look, so it's possible I'm missing things. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @effigies - config makes copies of sections, not the entire config object. so as long as the config.get_display is called, nipype maintains a global config/state. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a little unclear. If I understand it correctly, we use the input
$DISPLAY
variable unless this is set? Under that assumption, here's a proposed rewrite:There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right.
display_variable
should override$DISPLAY
. I'll change the code and use your proposed text for the documentation.