| #!/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) |