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 launching 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 program 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 try: 114 from commands import getoutput 115 except ImportError: 116 from subprocess import getoutput 117 118 # @WARNING: use the private API of the webbrowser module 119 from webbrowser import _iscommand 120 121 class KfmClient(Controller): 122 '''Controller for the KDE kfmclient program.''' 123 124 def __init__(self, kfmclient='kfmclient'): 125 super(KfmClient, self).__init__(kfmclient, 'exec') 126 self.kde_version = self.detect_kde_version() 127 128 def detect_kde_version(self): 129 kde_version = None 130 try: 131 info = getoutput('kde-config --version') 132 133 for line in info.splitlines(): 134 if line.startswith('KDE'): 135 kde_version = line.split(':')[-1].strip() 136 break 137 except (OSError, RuntimeError): 138 pass 139 140 return kde_version 141 142 def fixreturncode(self, returncode): 143 if returncode is not None and self.kde_version > '3.5.4': 144 return returncode 145 else: 146 return os.EX_OK 147 148 def detect_desktop_environment(): 149 '''Checks for known desktop environments 150 151 Return the desktop environments name, lowercase (kde, gnome, xfce) 152 or "generic" 153 154 ''' 155 156 desktop_environment = 'generic' 157 158 if os.environ.get('KDE_FULL_SESSION') == 'true': 159 desktop_environment = 'kde' 160 elif os.environ.get('GNOME_DESKTOP_SESSION_ID'): 161 desktop_environment = 'gnome' 162 else: 163 try: 164 info = getoutput('xprop -root _DT_SAVE_MODE') 165 if ' = "xfce4"' in info: 166 desktop_environment = 'xfce' 167 except (OSError, RuntimeError): 168 pass 169 170 return desktop_environment 171 172 173 def register_X_controllers(): 174 if _iscommand('kfmclient'): 175 _controllers['kde-open'] = KfmClient() 176 177 for command in ('gnome-open', 'exo-open', 'xdg-open'): 178 if _iscommand(command): 179 _controllers[command] = Controller(command) 180 181 def get(): 182 controllers_map = { 183 'gnome': 'gnome-open', 184 'kde': 'kde-open', 185 'xfce': 'exo-open', 186 } 187 188 desktop_environment = detect_desktop_environment() 189 190 try: 191 controller_name = controllers_map[desktop_environment] 192 return _controllers[controller_name].open 193 194 except KeyError: 195 if 'xdg-open' in _controllers: 196 return _controllers['xdg-open'].open 197 else: 198 return webbrowser.open 199 200 201 if os.environ.get("DISPLAY"): 202 register_X_controllers() 203 _open = get() 204 205 206def open(filename): 207 '''Open a file or a URL in the registered default application.''' 208 209 return _open(filename) 210