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