Something went wrong on our end
Forked from
FSL / fslpy
829 commits behind the upstream repository.
-
This means that users who have FSL installed in Windows Subsystem for Linux can call the wrappers directly from a Windows python program (after setting the environment variable FSLWSL=1)
This means that users who have FSL installed in Windows Subsystem for Linux can call the wrappers directly from a Windows python program (after setting the environment variable FSLWSL=1)
platform.py 11.75 KiB
#!/usr/bin/env python
#
# platform.py - Platform information
#
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#
"""This module provides the :class:`Platform` class, which is a container
of information about the current platform we are running on. A single
``Platform`` instance is created when this module is first imported, and
is available as a module attribute called :attr:`platform`.
"""
import logging
import os
import os.path as op
import sys
import importlib
import fsl.utils.notifier as notifier
# An annoying consequence of using
# a system-module name for our own
# module is that we can't import
# it directly (as it will attempt
# to import itself, i.e. this module).
#
# This is only necessary in Python 2.x
# (as python 3 disallows relative
# imports).
builtin_platform = importlib.import_module('platform')
log = logging.getLogger(__name__)
WX_UNKNOWN = 0
"""Identifier for the :attr:`Platform.wxFlavour` and
:attr:`Platform.wxPlatform` properties indicating an unknown/undetermined
flavour/platform.
"""
WX_PYTHON = 1
"""Identifier for the :attr:`Platform.wxFlavour` property, indicating that
we are running standard wx Python.
"""
WX_PHOENIX = 2
"""Identifier for the :attr:`Platform.wxFlavour` property, indicating that we
are running wx Python/Phoenix.
"""
WX_MAC_COCOA = 1
"""Identifier for the :attr:`Platform.wxPlatform` property, indicating that we
are running the OSX cocoa wx build.
"""
WX_MAC_CARBON = 2
"""Identifier for the :attr:`Platform.wxPlatform` property, indicating that we
are running the OSX carbon wx build.
"""
WX_GTK = 3
"""Identifier for the :attr:`Platform.wxPlatform` property, indicating that we
are running the Linux/GTK wx build.
"""
class Platform(notifier.Notifier):
"""The ``Platform`` class contains a handful of properties which contain
information about the platform we are running on.
.. note:: The values of the :attr:`glVersion` and :attr:`glRenderer`
properties are not automatically set - they will only contain
a value if one is assigned to them. *FSLeyes* does this during
startup, in the :func:`fsleyes.gl.bootstrap` function.
.. autosummary::
os
frozen
fsldir
fsldevdir
haveGui
canHaveGui
inSSHSession
wxPlatform
wxFlavour
glVersion
glRenderer
glIsSoftwareRenderer
"""
def __init__(self):
"""Create a ``Platform`` instance. """
# For things which 'from fsl.utils.platform import platform',
# these identifiers are available on the platform instance
self.WX_UNKNOWN = WX_UNKNOWN
self.WX_PYTHON = WX_PYTHON
self.WX_PHOENIX = WX_PHOENIX
self.WX_MAC_COCOA = WX_MAC_COCOA
self.WX_MAC_CARBON = WX_MAC_CARBON
self.WX_GTK = WX_GTK
self.__inSSHSession = False
self.__inVNCSession = False
self.__glVersion = None
self.__glRenderer = None
self.__glIsSoftware = None
self.__fslVersion = None
# initialise fsldir - see fsldir.setter
self.fsldir = self.fsldir
# Determine if a display is available. We do
# this once at init (instead of on-demand in
# the canHaveGui method) because calling the
# IsDisplayAvailable function will cause the
# application to steal focus under OSX!
try:
import wx
self.__canHaveGui = wx.App.IsDisplayAvailable()
except ImportError:
self.__canHaveGui = False
# If one of the SSH_/VNC environment
# variables is set, then we're probably
# running over SSH/VNC.
sshVars = ['SSH_CLIENT', 'SSH_TTY']
vncVars = ['VNCDESKTOP', 'X2GO_SESSION', 'NXSESSIONID']
self.__inSSHSession = any(s in os.environ for s in sshVars)
self.__inVNCSession = any(v in os.environ for v in vncVars)
@property
def os(self):
"""The operating system name. Whatever is returned by the built-in
``platform.system`` function.
"""
return builtin_platform.system()
@property
def frozen(self):
"""``True`` if we are running in a compiled/frozen application,
``False`` otherwise.
"""
return getattr(sys, 'frozen', False)
@property
def haveGui(self):
"""``True`` if we are running with a GUI, ``False`` otherwise.
This currently equates to testing whether a display is available
(see :meth:`canHaveGui`) and whether a ``wx.App`` exists. It
previously also tested whether an event loop was running, but this
is not compatible with execution from IPython/Jupyter notebook, where
the event loop is called periodically, and so is not always running.
"""
try:
import wx
app = wx.GetApp()
# TODO Previously this conditional
# also used app.IsMainLoopRunning()
# to check that the wx main loop
# was running. But this doesn't
# suit situations where a non-main
# event loop is running, or where
# the mainloop is periodically
# started and stopped (e.g. when
# the event loop is being run by
# IPython).
#
# In c++ wx, there is the
# wx.App.UsesEventLoop method, but
# this is not presently exposed to
# Python code (and wouldn't help
# to detect the loop start/stop
# scenario).
#
# So this constraint has been
# (hopefully) temporarily relaxed
# until I can think of a better
# solution.
return (self.canHaveGui and
app is not None)
except ImportError:
return False
@property
def canHaveGui(self):
"""``True`` if it is possible to create a GUI, ``False`` otherwise. """
return self.__canHaveGui
@property
def inSSHSession(self):
"""``True`` if this application is running over an SSH session,
``False`` otherwise.
"""
return self.__inSSHSession
@property
def inVNCSession(self):
"""``True`` if this application is running over a VNC (or similar)
session, ``False`` otherwise. Currently, the following remote desktop
environments are detected:
- VNC
- x2go
- NoMachine
"""
return self.__inVNCSession
@property
def wxPlatform(self):
"""One of :data:`WX_UNKNOWN`, :data:`WX_MAC_COCOA`,
:data:`WX_MAC_CARBON`, or :data:`WX_GTK`, indicating the wx platform.
"""
if not self.canHaveGui:
return WX_UNKNOWN
import wx
pi = [t.lower() for t in wx.PlatformInfo]
if any(['cocoa' in p for p in pi]): plat = WX_MAC_COCOA
elif any(['carbon' in p for p in pi]): plat = WX_MAC_CARBON
elif any(['gtk' in p for p in pi]): plat = WX_GTK
else: plat = WX_UNKNOWN
if plat is WX_UNKNOWN:
log.warning('Could not determine wx platform from '
'information: {}'.format(pi))
return plat
@property
def wxFlavour(self):
"""One of :data:`WX_UNKNOWN`, :data:`WX_PYTHON` or :data:`WX_PHOENIX`,
indicating the wx flavour.
"""
if not self.canHaveGui:
return WX_UNKNOWN
import wx
pi = [t.lower() for t in wx.PlatformInfo]
isPhoenix = False
for tag in pi:
if 'phoenix' in tag:
isPhoenix = True
break
if isPhoenix: return WX_PHOENIX
else: return WX_PYTHON
@property
def fsldir(self):
"""The FSL installation location.
.. note:: The ``fsldir`` property can be updated - when it is changed,
any registered listeners are notified via the
:class:`.Notifier` interface.
"""
return os.environ.get('FSLDIR', None)
@property
def fsldevdir(self):
"""The FSL development directory location. """
return os.environ.get('FSLDEVDIR', None)
@property
def fslwsl(self):
"""Boolean flag indicating whether FSL is installed in Windows Subsystem for Linux """
return os.environ.get('FSLWSL', "0") == "1"
@fsldir.setter
def fsldir(self, value):
"""Changes the value of the :attr:`fsldir` property, and notifies any
registered listeners.
"""
if value is not None:
value = value.strip()
if value is None: pass
elif value == '': value = None
elif not op.exists(value): value = None
elif not op.isdir(value): value = None
if value is None:
os.environ.pop('FSLDIR', None)
else:
os.environ['FSLDIR'] = value
# Set the FSL version field if we can
versionFile = op.join(value, 'etc', 'fslversion')
if op.exists(versionFile):
with open(versionFile, 'rt') as f:
# split string at colon for new hash style versions
# first object in list is the non-hashed version string (e.g. 6.0.2)
# if no ":hash:" then standard FSL version string is still returned
self.__fslVersion = f.read().strip().split(":")[0]
self.notify(value=value)
@fsldevdir.setter
def fsldevdir(self, value):
"""Changes the value of the :attr:`fsldevdir` property, and notifies
any registered listeners.
"""
if value is not None:
value = value.strip()
if value is None: pass
elif value == '': value = None
elif not op.exists(value): value = None
elif not op.isdir(value): value = None
if value is None:
os.environ.pop('FSLDEVDIR', None)
else:
os.environ['FSLDEVDIR'] = value
@property
def fslVersion(self):
"""Returns the FSL version as a string, e.g. ``'5.0.9'``. Returns
``None`` if a FSL installation could not be found.
"""
return self.__fslVersion
@property
def glVersion(self):
"""Returns the available OpenGL version, or ``None`` if it has not
been set.
"""
return self.__glVersion
@glVersion.setter
def glVersion(self, value):
"""Set the available OpenGL version. """
self.__glVersion = value
@property
def glRenderer(self):
"""Returns the available OpenGL renderer, or ``None`` if it has not
been set.
"""
return self.__glRenderer
@glRenderer.setter
def glRenderer(self, value):
"""Set the available OpenGL renderer. """
self.__glRenderer = value
value = value.lower()
# There doesn't seem to be any quantitative
# method for determining whether we are using
# software-based rendering, so a hack is
# necessary.
self.__glIsSoftware = any((
'software' in value,
'chromium' in value,
))
@property
def glIsSoftwareRenderer(self):
"""Returns ``True`` if the OpenGL renderer is software based,
``False`` otherwise, or ``None`` if the renderer has not yet been set.
.. note:: This check is based on heuristics, ans is not guaranteed to
be correct.
"""
return self.__glIsSoftware
platform = Platform()
"""An instance of the :class:`Platform` class. Feel free to create your own
instance, but be aware that if you do so you will not be updated of changes
to the :attr:`Platform.fsldir` property.
"""