1 """Utility for opening a file using the default application in a cross-platform 2 manner. Modified from http://code.activestate.com/recipes/511443/. 3 """ 4 5 __version__ = '1.1x' 6 __all__ = ['open'] 7 8 import os 9 import sys 10 import webbrowser 11 import subprocess 12 13 _controllers = {} 14 _open = None 15 16 17 class BaseController(object): 18 '''Base class for open program controllers.''' 19 20 def __init__(self, name): 21 self.name = name 22 23 def open(self, filename): 24 raise NotImplementedError 25 26 27 class Controller(BaseController): 28 '''Controller for a generic open program.''' 29 30 def __init__(self, *args): 31 super(Controller, self).__init__(os.path.basename(args[0])) 32 self.args = list(args) 33 34 def _invoke(self, cmdline): 35 if sys.platform[:3] == 'win': 36 closefds = False 37 startupinfo = subprocess.STARTUPINFO() 38 startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW 39 else: 40 closefds = True 41 startupinfo = None 42 43 if (os.environ.get('DISPLAY') or sys.platform[:3] == 'win' or 44 sys.platform == 'darwin'): 45 inout = file(os.devnull, 'r+') 46 else: 47 # for TTY programs, we need stdin/out 48 inout = None 49 50 # if possible, put the child precess in separate process group, 51 # so keyboard interrupts don't affect child precess as well as 52 # Python 53 setsid = getattr(os, 'setsid', None) 54 if not setsid: 55 setsid = getattr(os, 'setpgrp', None) 56 57 pipe = subprocess.Popen(cmdline, stdin=inout, stdout=inout, 58 stderr=inout, close_fds=closefds, 59 preexec_fn=setsid, startupinfo=startupinfo) 60 61 # It is assumed that this kind of tools (gnome-open, kfmclient, 62 # exo-open, xdg-open and open for OSX) immediately exit after lauching 63 # the specific application 64 returncode = pipe.wait() 65 if hasattr(self, 'fixreturncode'): 66 returncode = self.fixreturncode(returncode) 67 return not returncode 68 69 def open(self, filename): 70 if isinstance(filename, basestring): 71 cmdline = self.args + [filename] 72 else: 73 # assume it is a sequence 74 cmdline = self.args + filename 75 try: 76 return self._invoke(cmdline) 77 except OSError: 78 return False 79 80 81 # Platform support for Windows 82 if sys.platform[:3] == 'win': 83 84 class Start(BaseController): 85 '''Controller for the win32 start progam through os.startfile.''' 86 87 def open(self, filename): 88 try: 89 os.startfile(filename) 90 except WindowsError: 91 # [Error 22] No application is associated with the specified 92 # file for this operation: '<URL>' 93 return False 94 else: 95 return True 96 97 _controllers['windows-default'] = Start('start') 98 _open = _controllers['windows-default'].open 99 100 101 # Platform support for MacOS 102 elif sys.platform == 'darwin': 103 _controllers['open']= Controller('open') 104 _open = _controllers['open'].open 105 106 107 # Platform support for Unix 108 else: 109 110 import commands 111 112 # @WARNING: use the private API of the webbrowser module 113 from webbrowser import _iscommand 114 115 class KfmClient(Controller): 116 '''Controller for the KDE kfmclient program.''' 117 118 def __init__(self, kfmclient='kfmclient'): 119 super(KfmClient, self).__init__(kfmclient, 'exec') 120 self.kde_version = self.detect_kde_version() 121 122 def detect_kde_version(self): 123 kde_version = None 124 try: 125 info = commands.getoutput('kde-config --version') 126 127 for line in info.splitlines(): 128 if line.startswith('KDE'): 129 kde_version = line.split(':')[-1].strip() 130 break 131 except (OSError, RuntimeError): 132 pass 133 134 return kde_version 135 136 def fixreturncode(self, returncode): 137 if returncode is not None and self.kde_version > '3.5.4': 138 return returncode 139 else: 140 return os.EX_OK 141 142 def detect_desktop_environment(): 143 '''Checks for known desktop environments 144 145 Return the desktop environments name, lowercase (kde, gnome, xfce) 146 or "generic" 147 148 ''' 149 150 desktop_environment = 'generic' 151 152 if os.environ.get('KDE_FULL_SESSION') == 'true': 153 desktop_environment = 'kde' 154 elif os.environ.get('GNOME_DESKTOP_SESSION_ID'): 155 desktop_environment = 'gnome' 156 else: 157 try: 158 info = commands.getoutput('xprop -root _DT_SAVE_MODE') 159 if ' = "xfce4"' in info: 160 desktop_environment = 'xfce' 161 except (OSError, RuntimeError): 162 pass 163 164 return desktop_environment 165 166 167 def register_X_controllers(): 168 if _iscommand('kfmclient'): 169 _controllers['kde-open'] = KfmClient() 170 171 for command in ('gnome-open', 'exo-open', 'xdg-open'): 172 if _iscommand(command): 173 _controllers[command] = Controller(command) 174 175 def get(): 176 controllers_map = { 177 'gnome': 'gnome-open', 178 'kde': 'kde-open', 179 'xfce': 'exo-open', 180 } 181 182 desktop_environment = detect_desktop_environment() 183 184 try: 185 controller_name = controllers_map[desktop_environment] 186 return _controllers[controller_name].open 187 188 except KeyError: 189 if _controllers.has_key('xdg-open'): 190 return _controllers['xdg-open'].open 191 else: 192 return webbrowser.open 193 194 195 if os.environ.get("DISPLAY"): 196 register_X_controllers() 197 _open = get() 198 199 200 def open(filename): 201 '''Open a file or an URL in the registered default application.''' 202 203 return _open(filename) 204