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