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