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