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