217 lines · python
1#!/usr/bin/env python2# -*- coding: utf-8 -*-3 4"""Utility for opening a file using the default application in a cross-platform5manner. Modified from http://code.activestate.com/recipes/511443/.6"""7 8__version__ = "1.1x"9__all__ = ["open"]10 11import os12import sys13import webbrowser14import subprocess15 16_controllers = {}17_open = None18 19 20class BaseController(object):21 """Base class for open program controllers."""22 23 def __init__(self, name):24 self.name = name25 26 def open(self, filename):27 raise NotImplementedError28 29 30class Controller(BaseController):31 """Controller for a generic open program."""32 33 def __init__(self, *args):34 super(Controller, self).__init__(os.path.basename(args[0]))35 self.args = list(args)36 37 def _invoke(self, cmdline):38 if sys.platform[:3] == "win":39 closefds = False40 startupinfo = subprocess.STARTUPINFO()41 startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW42 else:43 closefds = True44 startupinfo = None45 46 if (47 os.environ.get("DISPLAY")48 or sys.platform[:3] == "win"49 or sys.platform == "darwin"50 ):51 inout = subprocess.DEVNULL52 else:53 # for TTY programs, we need stdin/out54 inout = None55 56 # if possible, put the child precess in separate process group,57 # so keyboard interrupts don't affect child precess as well as58 # Python59 setsid = getattr(os, "setsid", None)60 if not setsid:61 setsid = getattr(os, "setpgrp", None)62 63 pipe = subprocess.Popen(64 cmdline,65 stdin=inout,66 stdout=inout,67 stderr=inout,68 close_fds=closefds,69 preexec_fn=setsid,70 startupinfo=startupinfo,71 )72 73 # It is assumed that this kind of tools (gnome-open, kfmclient,74 # exo-open, xdg-open and open for OSX) immediately exit after launching75 # the specific application76 returncode = pipe.wait()77 if hasattr(self, "fixreturncode"):78 returncode = self.fixreturncode(returncode)79 return not returncode80 81 def open(self, filename):82 if isinstance(filename, basestring):83 cmdline = self.args + [filename]84 else:85 # assume it is a sequence86 cmdline = self.args + filename87 try:88 return self._invoke(cmdline)89 except OSError:90 return False91 92 93# Platform support for Windows94if sys.platform[:3] == "win":95 96 class Start(BaseController):97 """Controller for the win32 start program through os.startfile."""98 99 def open(self, filename):100 try:101 os.startfile(filename)102 except WindowsError:103 # [Error 22] No application is associated with the specified104 # file for this operation: '<URL>'105 return False106 else:107 return True108 109 _controllers["windows-default"] = Start("start")110 _open = _controllers["windows-default"].open111 112 113# Platform support for MacOS114elif sys.platform == "darwin":115 _controllers["open"] = Controller("open")116 _open = _controllers["open"].open117 118 119# Platform support for Unix120else:121 122 try:123 from commands import getoutput124 except ImportError:125 from subprocess import getoutput126 127 # @WARNING: use the private API of the webbrowser module128 from webbrowser import _iscommand129 130 class KfmClient(Controller):131 """Controller for the KDE kfmclient program."""132 133 def __init__(self, kfmclient="kfmclient"):134 super(KfmClient, self).__init__(kfmclient, "exec")135 self.kde_version = self.detect_kde_version()136 137 def detect_kde_version(self):138 kde_version = None139 try:140 info = getoutput("kde-config --version")141 142 for line in info.splitlines():143 if line.startswith("KDE"):144 kde_version = line.split(":")[-1].strip()145 break146 except (OSError, RuntimeError):147 pass148 149 return kde_version150 151 def fixreturncode(self, returncode):152 if returncode is not None and self.kde_version > "3.5.4":153 return returncode154 else:155 return os.EX_OK156 157 def detect_desktop_environment():158 """Checks for known desktop environments159 160 Return the desktop environments name, lowercase (kde, gnome, xfce)161 or "generic"162 163 """164 165 desktop_environment = "generic"166 167 if os.environ.get("KDE_FULL_SESSION") == "true":168 desktop_environment = "kde"169 elif os.environ.get("GNOME_DESKTOP_SESSION_ID"):170 desktop_environment = "gnome"171 else:172 try:173 info = getoutput("xprop -root _DT_SAVE_MODE")174 if ' = "xfce4"' in info:175 desktop_environment = "xfce"176 except (OSError, RuntimeError):177 pass178 179 return desktop_environment180 181 def register_X_controllers():182 if _iscommand("kfmclient"):183 _controllers["kde-open"] = KfmClient()184 185 for command in ("gnome-open", "exo-open", "xdg-open"):186 if _iscommand(command):187 _controllers[command] = Controller(command)188 189 def get():190 controllers_map = {191 "gnome": "gnome-open",192 "kde": "kde-open",193 "xfce": "exo-open",194 }195 196 desktop_environment = detect_desktop_environment()197 198 try:199 controller_name = controllers_map[desktop_environment]200 return _controllers[controller_name].open201 202 except KeyError:203 if "xdg-open" in _controllers:204 return _controllers["xdg-open"].open205 else:206 return webbrowser.open207 208 if os.environ.get("DISPLAY"):209 register_X_controllers()210 _open = get()211 212 213def open(filename):214 """Open a file or a URL in the registered default application."""215 216 return _open(filename)217