[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