Logo Search packages:      
Sourcecode: zim version File versions  Download package

applications.py

# -*- coding: utf-8 -*-

# Copyright 2009 Jaap Karssenberg <pardus@cpan.org>

'''This module contains helper classes for running external applications.

Also see the module zim.gui.applications, which contains classes for
working with applications defined in desktop entry files.
'''

import os
import logging
import subprocess

import gobject


logger = logging.getLogger('zim.applications')


00021 class Application(object):
      '''FIXME'''

      STATUS_OK = 0

      def __init__(self, cmd, tryexeccmd=None):
            assert isinstance(cmd, (tuple, list))
            assert tryexeccmd is None or isinstance(tryexeccmd, basestring)
            self.cmd = tuple(cmd)
            self.tryexeccmd = tryexeccmd

      @property
      def name(self):
            return self.cmd[0]

      @staticmethod
00037       def _lookup(cmd):
            '''Lookup cmd in PATH'''
            if os.name == 'nt' and not '.' in cmd:
                  cmd = cmd + '.exe'
                  # Automagically convert command names on windows

            for dir in os.environ['PATH'].split(os.pathsep):
                  file = os.sep.join((dir, cmd))
                  if os.path.isfile(file):
                        return file
            else:
                  return None

      def _cmd(self, args):
            # substitute args in the command - to be overloaded by child classes
            if args:
                  return self.cmd + tuple(map(unicode, args))
            else:
                  return self.cmd

00057       def tryexec(self):
            '''Check if the executable exists without calling it. If no 'tryexec'
            parameter is defined for this application, the first parameter of
            the command list is used.
            '''
            cmd = self.tryexeccmd or self.cmd[0]
            return not self._lookup(cmd) is None

      def _checkargs(self, cwd, args):
            assert args is None or isinstance(args, (tuple, list))
            argv = [a.encode('utf-8') for a in self._cmd(args)]
            if cwd:
                  cwd = unicode(cwd).encode('utf-8')
            return cwd, argv

00072       def run(self, args=None, cwd=None):
            '''Run application in the foreground and wait for it to return.
            An exception will be thrown if the application returns non-zero.
            '''
            cwd, argv = self._checkargs(cwd, args)
            logger.info('Running: %s (cwd: %s)', argv, cwd)
            subprocess.check_call(argv, cwd=cwd, stdout=open(os.devnull, 'w'))

00080       def pipe(self, args=None, cwd=None, input=None):
            '''Run application in the foreground and wait for it to return.
            This method returns stdout while logging stderr as warning.
            Output is returned as a list of lines.
            '''
            cwd, argv = self._checkargs(cwd, args)
            logger.info('Running: %s (cwd: %s)', argv, cwd)
            stdout, stderr = subprocess.Popen(argv, cwd=cwd, stdout=subprocess.PIPE).communicate(input)

            if stderr:
                  logger.warn(stderr)

            return [line + '\n' for line in stdout.splitlines()]
                  # Explicit newline conversion, e.g. on windows \r\n -> \n

00095       def spawn(self, args=None, callback=None, data=None, cwd=None):
            '''Run application in the background and return immediatly.

            The optional callback can be used to trigger when the application
            exits. The signature is:

                  callback(status)

            where 'status' is the exit status of the process. The application
            object provides a constant 'STATUS_OK' which can be used to test if
            the application was successful or not.
            '''
            # TODO: can we build this based on os.spawn ? - seems this method fails on win32
            cwd, argv = self._checkargs(cwd, args)
            opts = {}
            if cwd:
                  opts['cwd'] = unicode(cwd).encode('utf-8')

            flags = gobject.SPAWN_SEARCH_PATH
            if callback:
                  flags |= gobject.SPAWN_DO_NOT_REAP_CHILD
                  # without this flag child is reaped autmatically -> no zombies

            logger.info('Spawning: %s (cwd: %s)', argv, cwd)
            try:
                  pid, stdin, stdout, stderr = \
                        gobject.spawn_async(argv, flags=flags, **opts)
            except gobject.GError:
                  logger.exception('Failed running: %s', argv)
                  name = self.name
                  #~ ErrorDialog(None, _('Could not run application: %s') % name).run()
                        #~ # T: error when application failed to start
                  return None
            else:
                  logger.debug('Process started with PID: %i', pid)
                  if callback:
                        # child watch does implicite reaping -> no zombies
                        if data is None:
                              gobject.child_watch_add(pid,
                                    lambda pid, status: callback(status))
                        else:
                              gobject.child_watch_add(pid,
                                    lambda pid, status, data: callback(status, data), data)
                  return pid


00141 class WebBrowser(Application):
      '''Wrapper for the webbrowser module with the Application API. Can be
      used as fallback if no webbrowser is configured.
      '''

      name = _('Default') + ' (webbrowser)' # T: label for default webbrowser
      key = 'webbrowser' # Used by zim.gui.applications

      def __init__(self):
            import webbrowser
            self.controller = webbrowser.get()

00153       def tryexec(self):
            return not self.controller is None

      def run(self, args):
            raise NotImplementedError, 'WebBrowser can not run in foreground'

      def spawn(self, args, callback=None):
            if callback:
                  raise NotImplementedError, 'WebBrowser can not handle callback'

            for url in args:
                  logger.info('Opening in webbrowser: %s', url)
                  self.controller.open(url)


00168 class StartFile(Application):
      '''Wrapper for os.startfile(). Usefull mainly on windows to open
      files with the default application.
      '''

      name = _('Default') + ' (os)' # T: label for default application
      key = 'startfile' # Used by zim.gui.applications

      def __init__(self):
            pass

00179       def tryexec(self):
            return hasattr(os, 'startfile')

      def run(self, args):
            raise NotImplementedError, 'StartFile can not run in foreground'

      def spawn(self, args, callback=None):
            if callback:
                  raise NotImplementedError, 'StartFile can not handle callback'

            for file in args:
                  path = os.path.normpath(unicode(file))
                  logger.info('Opening with os.startfile: %s', path)
                  os.startfile(path)

Generated by  Doxygen 1.6.0   Back to index