[Dextrose] [PATCH sugar-toolkit] Initial feedback implementation
Aleksey Lim
alsroot at activitycentral.org
Fri Feb 11 21:34:06 EST 2011
---
src/sugar/Makefile.am | 1 +
src/sugar/activity/activityfactory.py | 10 +++-
src/sugar/activity/main.py | 4 +-
src/sugar/feedback.py | 92 +++++++++++++++++++++++++++++++++
4 files changed, 104 insertions(+), 3 deletions(-)
create mode 100644 src/sugar/feedback.py
diff --git a/src/sugar/Makefile.am b/src/sugar/Makefile.am
index e775be1..4e3028a 100644
--- a/src/sugar/Makefile.am
+++ b/src/sugar/Makefile.am
@@ -2,6 +2,7 @@ SUBDIRS = activity bundle graphics presence datastore dispatch
sugardir = $(pythondir)/sugar
sugar_PYTHON = \
+ feedback.py \
env.py \
network.py \
profile.py \
diff --git a/src/sugar/activity/activityfactory.py b/src/sugar/activity/activityfactory.py
index 6b4ba32..3e7a07f 100644
--- a/src/sugar/activity/activityfactory.py
+++ b/src/sugar/activity/activityfactory.py
@@ -23,6 +23,7 @@ the moment there is no reason to stabilize this API.
import logging
+import cjson
import dbus
import gobject
@@ -274,7 +275,7 @@ class ActivityCreationHandler(gobject.GObject):
gobject.child_watch_add(child.pid,
_child_watch_cb,
- (environment_dir, log_file,
+ (environment_dir, log_file, log_path,
self._handle.activity_id))
def _no_reply_handler(self, *args):
@@ -338,7 +339,7 @@ def create_with_object_id(bundle, object_id):
def _child_watch_cb(pid, condition, user_data):
# FIXME we use standalone method here instead of ActivityCreationHandler's
# member to have workaround code, see #1123
- environment_dir, log_file, activity_id = user_data
+ environment_dir, log_file, log_path, activity_id = user_data
if environment_dir is not None:
subprocess.call(['/bin/rm', '-rf', environment_dir])
@@ -381,6 +382,11 @@ def _child_watch_cb(pid, condition, user_data):
def error_handler_cb(error):
logging.error('Cannot send NotifyLaunchFailure to the shell')
+ report = cjson.encode({'failed_exit': 1})
+ shell.Feedback(activity_id, report, log_path,
+ reply_handler=reply_handler_cb,
+ error_handler=error_handler_cb)
+
# TODO send launching failure but activity could already show
# main window, see http://bugs.sugarlabs.org/ticket/1447#comment:19
shell.NotifyLaunchFailure(activity_id,
diff --git a/src/sugar/activity/main.py b/src/sugar/activity/main.py
index 8b20562..10d0031 100644
--- a/src/sugar/activity/main.py
+++ b/src/sugar/activity/main.py
@@ -30,7 +30,7 @@ from sugar.activity import activityhandle
from sugar.activity import i18n
from sugar.bundle.activitybundle import ActivityBundle
from sugar.graphics import style
-from sugar import logger
+from sugar import logger, feedback
def create_activity_instance(constructor, handle):
@@ -109,6 +109,8 @@ def main():
gettext.bindtextdomain(bundle.get_bundle_id(), locale_path)
gettext.textdomain(bundle.get_bundle_id())
+ feedback.start(options.activity_id)
+
splitted_module = args[0].rsplit('.', 1)
module_name = splitted_module[0]
class_name = splitted_module[1]
diff --git a/src/sugar/feedback.py b/src/sugar/feedback.py
new file mode 100644
index 0000000..5833d61
--- /dev/null
+++ b/src/sugar/feedback.py
@@ -0,0 +1,92 @@
+# Copyright (C) 2011, Aleksey Lim
+#
+# 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, either version 2 of the License, or
+# (at your option) any later version.
+#
+# 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, see <http://www.gnu.org/licenses/>.
+
+import os
+import sys
+import atexit
+import logging
+
+import dbus
+import cjson
+
+
+_SHELL_SERVICE = "org.laptop.Shell"
+_SHELL_PATH = "/org/laptop/Shell"
+_SHELL_IFACE = "org.laptop.Shell"
+
+_report = {}
+_need_log = False
+
+
+def start(activity_id, log_path=None):
+ if log_path is None:
+ # TODO more portable solution
+ stdout_file = os.readlink('/proc/self/fd/%s' % sys.stdout.fileno())
+ if os.path.isfile(stdout_file):
+ log_path = stdout_file
+ else:
+ log_path = ''
+
+ atexit.register(_send, activity_id, log_path)
+
+
+def trigger(key, need_log=False):
+ global _need_log
+
+ if key not in _report:
+ _report[key] = 0
+ _report[key] += 1
+ _need_log = _need_log or need_log
+
+ logging.debug('Feedback[%s] == %s', key, _report[key])
+
+
+def flush():
+ global _report
+ global _need_log
+
+ report = _report
+ need_log = _need_log
+ _report = {}
+ _need_log = False
+
+ return report, need_log
+
+
+def _send(activity_id, log_path):
+ if not _report:
+ return
+
+ report, need_log = flush()
+
+ bus = dbus.SessionBus()
+ bus_object = bus.get_object(_SHELL_SERVICE, _SHELL_PATH)
+ shell = dbus.Interface(bus_object, _SHELL_IFACE)
+ shell.Feedback(activity_id, cjson.encode(report),
+ log_path if need_log else '')
+
+
+def _excepthook(exctype, value, tb):
+ try:
+ import traceback
+ message = ''.join(traceback.format_exception(exctype, value, tb))
+ logging.error(message)
+ trigger('unhandled_exception', need_log=True)
+ except Exception, error:
+ print >>sys.stderr, 'Unhandled %r while processing: %r %r' % \
+ (error, exctype, value)
+
+
+sys.excepthook = _excepthook
--
1.7.3.4
More information about the Dextrose
mailing list