Home | History | Annotate | Download | only in share
      1 #!/usr/bin/env python
      2 # -*- coding: utf-8 -*-
      3 
      4 """Utility for opening a file using the default application in a cross-platform
      5 manner. Modified from http://code.activestate.com/recipes/511443/.
      6 """
      7 
      8 __version__ = '1.1x'
      9 __all__ = ['open']
     10 
     11 import os
     12 import sys
     13 import webbrowser
     14 import subprocess
     15 
     16 _controllers = {}
     17 _open = None
     18 
     19 
     20 class 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 
     30 class 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
     85 if 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
    105 elif sys.platform == 'darwin':
    106     _controllers['open']= Controller('open')
    107     _open = _controllers['open'].open
    108 
    109 
    110 # Platform support for Unix
    111 else:
    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 
    203 def open(filename):
    204     '''Open a file or an URL in the registered default application.'''
    205 
    206     return _open(filename)
    207