1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
|
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (C) 2011 Canonical
#
# Authors:
# Michael Vogt
#
# This program is free software; you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation; version 3.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
# py3 compat
try:
import cPickle as pickle
pickle # pyflakes
except ImportError:
import pickle
import logging
import os
import json
from gi.repository import GObject
LOG = logging.getLogger(__name__)
class SpawnHelper(GObject.GObject):
__gsignals__ = {
"data-available" : (GObject.SIGNAL_RUN_LAST,
GObject.TYPE_NONE,
(GObject.TYPE_PYOBJECT,),
),
"exited" : (GObject.SIGNAL_RUN_LAST,
GObject.TYPE_NONE,
(int,),
),
"error" : (GObject.SIGNAL_RUN_LAST,
GObject.TYPE_NONE,
(str,),
),
}
def __init__(self, format="pickle"):
super(SpawnHelper, self).__init__()
self._expect_format = format
self._stdout = None
self._stderr = None
self._io_watch = None
self._child_watch = None
self._cmd = None
def run(self, cmd):
self._cmd = cmd
(pid, stdin, stdout, stderr) = GObject.spawn_async(
cmd, flags = GObject.SPAWN_DO_NOT_REAP_CHILD,
standard_output=True, standard_error=True)
LOG.debug("running: '%s' as pid: '%s'" % (cmd, pid))
self._child_watch = GObject.child_watch_add(
pid, self._helper_finished, data=(stdout, stderr))
self._io_watch = GObject.io_add_watch(
stdout, GObject.IO_IN, self._helper_io_ready, (stdout, ))
def _helper_finished(self, pid, status, (stdout, stderr)):
LOG.debug("helper_finished: '%s' '%s'" % (pid, status))
# get status code
res = os.WEXITSTATUS(status)
if res == 0:
self.emit("exited", res)
else:
LOG.warn("exit code %s from helper" % res)
# check stderr
err = os.read(stderr, 4*1024)
self._stderr = err
if err:
LOG.warn("got error from helper: '%s'" % err)
self.emit("error", err)
os.close(stderr)
if self._io_watch:
# remove with a delay timeout delay to ensure that any
# pending data is still flused
GObject.timeout_add(100, GObject.source_remove, self._io_watch)
if self._child_watch:
GObject.source_remove(self._child_watch)
def _helper_io_ready(self, source, condition, (stdout,)):
# read the raw data
data = ""
while True:
s = os.read(stdout, 1024)
if not s: break
data += s
os.close(stdout)
self._stdout = data
if self._expect_format == "pickle":
# unpickle it, we should *always* get valid data here, so if
# we don't this should raise a error
try:
data = pickle.loads(data)
except:
LOG.exception("can not load pickle data: '%s'" % data)
elif self._expect_format == "json":
try:
data = json.loads(data)
except:
LOG.exception("can not load json: '%s'" % data)
elif self._expect_format == "none":
pass
else:
LOG.error("unknown format: '%s'", self._expect_format)
LOG.debug("got data for cmd: '%s'='%s'" % (self._cmd, data))
self.emit("data-available", data)
return False
|