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