| #!/usr/bin/env python |
| # -*- coding: utf-8 -*- |
| |
| """Utility for opening a file using the default application in a cross-platform |
| manner. Modified from http://code.activestate.com/recipes/511443/. |
| """ |
| |
| __version__ = '1.1x' |
| __all__ = ['open'] |
| |
| import os |
| import sys |
| import webbrowser |
| import subprocess |
| |
| _controllers = {} |
| _open = None |
| |
| |
| class BaseController(object): |
| '''Base class for open program controllers.''' |
| |
| def __init__(self, name): |
| self.name = name |
| |
| def open(self, filename): |
| raise NotImplementedError |
| |
| |
| class Controller(BaseController): |
| '''Controller for a generic open program.''' |
| |
| def __init__(self, *args): |
| super(Controller, self).__init__(os.path.basename(args[0])) |
| self.args = list(args) |
| |
| def _invoke(self, cmdline): |
| if sys.platform[:3] == 'win': |
| closefds = False |
| startupinfo = subprocess.STARTUPINFO() |
| startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW |
| else: |
| closefds = True |
| startupinfo = None |
| |
| if (os.environ.get('DISPLAY') or sys.platform[:3] == 'win' or |
| sys.platform == 'darwin'): |
| inout = file(os.devnull, 'r+') |
| else: |
| # for TTY programs, we need stdin/out |
| inout = None |
| |
| # if possible, put the child precess in separate process group, |
| # so keyboard interrupts don't affect child precess as well as |
| # Python |
| setsid = getattr(os, 'setsid', None) |
| if not setsid: |
| setsid = getattr(os, 'setpgrp', None) |
| |
| pipe = subprocess.Popen(cmdline, stdin=inout, stdout=inout, |
| stderr=inout, close_fds=closefds, |
| preexec_fn=setsid, startupinfo=startupinfo) |
| |
| # It is assumed that this kind of tools (gnome-open, kfmclient, |
| # exo-open, xdg-open and open for OSX) immediately exit after launching |
| # the specific application |
| returncode = pipe.wait() |
| if hasattr(self, 'fixreturncode'): |
| returncode = self.fixreturncode(returncode) |
| return not returncode |
| |
| def open(self, filename): |
| if isinstance(filename, basestring): |
| cmdline = self.args + [filename] |
| else: |
| # assume it is a sequence |
| cmdline = self.args + filename |
| try: |
| return self._invoke(cmdline) |
| except OSError: |
| return False |
| |
| |
| # Platform support for Windows |
| if sys.platform[:3] == 'win': |
| |
| class Start(BaseController): |
| '''Controller for the win32 start program through os.startfile.''' |
| |
| def open(self, filename): |
| try: |
| os.startfile(filename) |
| except WindowsError: |
| # [Error 22] No application is associated with the specified |
| # file for this operation: '<URL>' |
| return False |
| else: |
| return True |
| |
| _controllers['windows-default'] = Start('start') |
| _open = _controllers['windows-default'].open |
| |
| |
| # Platform support for MacOS |
| elif sys.platform == 'darwin': |
| _controllers['open']= Controller('open') |
| _open = _controllers['open'].open |
| |
| |
| # Platform support for Unix |
| else: |
| |
| try: |
| from commands import getoutput |
| except ImportError: |
| from subprocess import getoutput |
| |
| # @WARNING: use the private API of the webbrowser module |
| from webbrowser import _iscommand |
| |
| class KfmClient(Controller): |
| '''Controller for the KDE kfmclient program.''' |
| |
| def __init__(self, kfmclient='kfmclient'): |
| super(KfmClient, self).__init__(kfmclient, 'exec') |
| self.kde_version = self.detect_kde_version() |
| |
| def detect_kde_version(self): |
| kde_version = None |
| try: |
| info = getoutput('kde-config --version') |
| |
| for line in info.splitlines(): |
| if line.startswith('KDE'): |
| kde_version = line.split(':')[-1].strip() |
| break |
| except (OSError, RuntimeError): |
| pass |
| |
| return kde_version |
| |
| def fixreturncode(self, returncode): |
| if returncode is not None and self.kde_version > '3.5.4': |
| return returncode |
| else: |
| return os.EX_OK |
| |
| def detect_desktop_environment(): |
| '''Checks for known desktop environments |
| |
| Return the desktop environments name, lowercase (kde, gnome, xfce) |
| or "generic" |
| |
| ''' |
| |
| desktop_environment = 'generic' |
| |
| if os.environ.get('KDE_FULL_SESSION') == 'true': |
| desktop_environment = 'kde' |
| elif os.environ.get('GNOME_DESKTOP_SESSION_ID'): |
| desktop_environment = 'gnome' |
| else: |
| try: |
| info = getoutput('xprop -root _DT_SAVE_MODE') |
| if ' = "xfce4"' in info: |
| desktop_environment = 'xfce' |
| except (OSError, RuntimeError): |
| pass |
| |
| return desktop_environment |
| |
| |
| def register_X_controllers(): |
| if _iscommand('kfmclient'): |
| _controllers['kde-open'] = KfmClient() |
| |
| for command in ('gnome-open', 'exo-open', 'xdg-open'): |
| if _iscommand(command): |
| _controllers[command] = Controller(command) |
| |
| def get(): |
| controllers_map = { |
| 'gnome': 'gnome-open', |
| 'kde': 'kde-open', |
| 'xfce': 'exo-open', |
| } |
| |
| desktop_environment = detect_desktop_environment() |
| |
| try: |
| controller_name = controllers_map[desktop_environment] |
| return _controllers[controller_name].open |
| |
| except KeyError: |
| if 'xdg-open' in _controllers: |
| return _controllers['xdg-open'].open |
| else: |
| return webbrowser.open |
| |
| |
| if os.environ.get("DISPLAY"): |
| register_X_controllers() |
| _open = get() |
| |
| |
| def open(filename): |
| '''Open a file or a URL in the registered default application.''' |
| |
| return _open(filename) |