[arch-commits] Commit in sugar-toolkit-gtk3/repos/community-x86_64 (3 files)

Balló György bgyorgy at archlinux.org
Wed Jun 19 11:00:31 UTC 2019


    Date: Wednesday, June 19, 2019 @ 11:00:30
  Author: bgyorgy
Revision: 482334

archrelease: copy trunk to community-x86_64

Added:
  sugar-toolkit-gtk3/repos/community-x86_64/PKGBUILD
    (from rev 482333, sugar-toolkit-gtk3/trunk/PKGBUILD)
Deleted:
  sugar-toolkit-gtk3/repos/community-x86_64/PKGBUILD
  sugar-toolkit-gtk3/repos/community-x86_64/six.patch

-----------+
 PKGBUILD  |   85 -
 six.patch | 2987 ------------------------------------------------------------
 2 files changed, 39 insertions(+), 3033 deletions(-)

Deleted: PKGBUILD
===================================================================
--- PKGBUILD	2019-06-19 11:00:27 UTC (rev 482333)
+++ PKGBUILD	2019-06-19 11:00:30 UTC (rev 482334)
@@ -1,46 +0,0 @@
-# Maintainer: Balló György <ballogyor+arch at gmail dot com>
-
-pkgname=sugar-toolkit-gtk3
-pkgver=0.113
-pkgrel=2
-pkgdesc="Sugar GTK library"
-arch=('x86_64')
-url="https://sugarlabs.org/"
-license=('LGPL')
-depends=('alsa-lib' 'gtk3' 'libsm' 'python2-cairo' 'python2-dateutil' 'python2-decorator'
-         'python2-telepathy' 'sugar-artwork' 'sugar-datastore' 'telepathy-mission-control' 'unzip')
-makedepends=('intltool' 'gobject-introspection')
-optdepends=('webkit2gtk: run sugar-activity-web')
-source=(https://download.sugarlabs.org/sources/sucrose/glucose/$pkgname/$pkgname-$pkgver.tar.xz
-        six.patch)
-sha256sums=('4062d981d551f1567d073389385ffc28793b75f0ed2485e8f9250a145239cba7'
-            '3adf9d6ddc817c31887f1c22e9976d9865ca17c608a5142ef97ffbd8c0de3424')
-
-prepare() {
-  cd $pkgname-$pkgver
-  # Revert 'Port from Python 2 to six', it's not complete yet
-  patch -RNp1 -i ../six.patch
-  autoreconf -fi
-}
-
-build() {
-  cd $pkgname-$pkgver
-  # Disable hardened build until fixed upstream
-  # https://bugs.sugarlabs.org/ticket/4886
-  export CFLAGS=${CFLAGS/ -fno-plt}
-  export LDFLAGS=${LDFLAGS/,-z,now}
-
-  # Don't use incomplete python3 port
-  sed -i 's/PYTHON=python3/PYTHON=python2/' configure
-  sed -i 's/exec sugar-activity3/exec sugar-activity/' bin/sugar-activity-web
-  sed -i '/dist_bin_SCRIPTS/ s/ sugar-activity3//' bin/Makefile.in
-
-  ./configure --prefix=/usr
-  sed -i 's/ -shared / -Wl,-O1,--as-needed\0/g' libtool
-  make
-}
-
-package() {
-  cd $pkgname-$pkgver
-  make DESTDIR="$pkgdir" install
-}

Copied: sugar-toolkit-gtk3/repos/community-x86_64/PKGBUILD (from rev 482333, sugar-toolkit-gtk3/trunk/PKGBUILD)
===================================================================
--- PKGBUILD	                        (rev 0)
+++ PKGBUILD	2019-06-19 11:00:30 UTC (rev 482334)
@@ -0,0 +1,39 @@
+# Maintainer: Balló György <ballogyor+arch at gmail dot com>
+
+pkgname=sugar-toolkit-gtk3
+pkgver=0.114
+pkgrel=1
+pkgdesc="Sugar GTK library"
+arch=('x86_64')
+url="https://sugarlabs.org/"
+license=('LGPL')
+depends=('alsa-lib' 'gtk3' 'libsm' 'python2-cairo' 'python2-dateutil' 'python2-decorator'
+         'python2-telepathy' 'sugar-artwork' 'sugar-datastore' 'telepathy-mission-control' 'unzip')
+makedepends=('intltool' 'gobject-introspection')
+optdepends=('webkit2gtk: run sugar-activity-web')
+source=(https://download.sugarlabs.org/sources/sucrose/glucose/$pkgname/$pkgname-$pkgver.tar.xz)
+sha256sums=('4b8ca467b85b6273fdb3ae86b1afa5071f4c0c6e11a27e9166a394620c2c77e5')
+
+prepare() {
+  cd $pkgname-$pkgver
+  # Don't use incomplete python3 port
+  sed -i 's/exec sugar-activity3/exec sugar-activity/' bin/sugar-activity-web
+  sed -i '/dist_bin_SCRIPTS/ s/ sugar-activity3//' bin/Makefile.{am,in}
+}
+
+build() {
+  cd $pkgname-$pkgver
+  # Disable hardened build until fixed upstream
+  # https://bugs.sugarlabs.org/ticket/4886
+  export CFLAGS=${CFLAGS/ -fno-plt}
+  export LDFLAGS=${LDFLAGS/,-z,now}
+
+  ./configure --prefix=/usr PYTHON=python2
+  sed -i 's/ -shared / -Wl,-O1,--as-needed\0/g' libtool
+  make -j1
+}
+
+package() {
+  cd $pkgname-$pkgver
+  make DESTDIR="$pkgdir" install
+}

Deleted: six.patch
===================================================================
--- six.patch	2019-06-19 11:00:27 UTC (rev 482333)
+++ six.patch	2019-06-19 11:00:30 UTC (rev 482334)
@@ -1,2987 +0,0 @@
-From aa8a5e70c415e6c2acf4ff373d9b366ac4692bb1 Mon Sep 17 00:00:00 2001
-From: Pro-Panda <f2016015 at pilani.bits-pilani.ac.in>
-Date: Thu, 1 Mar 2018 17:28:56 +0530
-Subject: [PATCH] Port from Python 2 to six
-
-Signed-off-by: James Cameron <quozl at laptop.org>
----
- Makefile.am                                   |   2 +-
- bin/Makefile.am                               |   2 +-
- bin/sugar-activity                            | 218 +----------------
- bin/sugar-activity-web                        |   4 +-
- bin/sugar-activity3                           |   5 +
- configure.ac                                  |   2 +-
- doc/conf.py                                   |  26 +-
- examples/alert.py                             |   6 +-
- examples/animator.py                          |   2 +-
- examples/colorbutton.py                       |   2 +-
- examples/combobox.py                          |   2 +-
- examples/customdestroy.py                     |   6 +-
- examples/gtktreesensitive.py                  |   2 +-
- examples/iconentry.py                         |   4 +-
- examples/radiotoolbutton.py                   |   2 +-
- examples/scrollingdetector.py                 |   6 +-
- examples/tabs.py                              |   2 +-
- examples/toolbutton.py                        |   2 +-
- src/sugar3/activity/Makefile.am               |   1 +
- src/sugar3/activity/activity.py               |  34 +--
- src/sugar3/activity/activityfactory.py        |  13 +-
- src/sugar3/activity/activityhandle.py         |  14 +-
- src/sugar3/activity/activityinstance.py       | 222 ++++++++++++++++++
- src/sugar3/activity/activityservice.py        |   2 +-
- src/sugar3/activity/bundlebuilder.py          |  44 ++--
- src/sugar3/activity/webkit1.py                |   8 +-
- src/sugar3/activity/widgets.py                |   2 +-
- src/sugar3/bundle/__init__.py                 |   6 +-
- src/sugar3/bundle/activitybundle.py           |   4 +-
- src/sugar3/bundle/bundle.py                   |  10 +-
- src/sugar3/bundle/bundleversion.py            |   1 +
- src/sugar3/bundle/contentbundle.py            |   7 +-
- src/sugar3/datastore/datastore.py             |  17 +-
- src/sugar3/dispatch/dispatcher.py             |  20 +-
- src/sugar3/dispatch/saferef.py                |  48 ++--
- src/sugar3/env.py                             |   4 +-
- src/sugar3/graphics/Makefile.am               |   1 -
- src/sugar3/graphics/alert.py                  |  22 +-
- src/sugar3/graphics/colorbutton.py            |  42 ++--
- src/sugar3/graphics/icon.py                   |  21 +-
- src/sugar3/graphics/iconentry.py              |   2 +-
- src/sugar3/graphics/objectchooser.py          |  24 +-
- src/sugar3/graphics/palettegroup.py           |   2 +-
- src/sugar3/graphics/palettemenu.py            |   2 +-
- src/sugar3/graphics/palettewindow.py          |  14 +-
- src/sugar3/graphics/popwindow.py              | 202 ----------------
- src/sugar3/graphics/progressicon.py           |   1 +
- src/sugar3/graphics/scrollingdetector.py      |  12 +-
- src/sugar3/graphics/style.py                  |   5 +-
- src/sugar3/graphics/toolbarbox.py             |   2 +
- src/sugar3/graphics/toolbox.py                |  14 +-
- src/sugar3/graphics/toolbutton.py             |   2 +-
- src/sugar3/graphics/tray.py                   |   4 +
- src/sugar3/graphics/xocolor.py                |  24 +-
- src/sugar3/logger.py                          |  25 +-
- src/sugar3/mime.py                            |   8 +-
- src/sugar3/network.py                         |  24 +-
- src/sugar3/presence/activity.py               |  24 +-
- src/sugar3/presence/buddy.py                  |  16 +-
- src/sugar3/presence/connectionmanager.py      |   6 +-
- src/sugar3/presence/presenceservice.py        |   7 +-
- src/sugar3/presence/tubeconn.py               |   7 +-
- src/sugar3/profile.py                         |   3 +-
- src/sugar3/speech.py                          |   9 +-
- src/sugar3/test/Makefile.am                   |   2 +-
- src/sugar3/test/{unittest.py => _unittest.py} |  10 +-
- src/sugar3/test/discover.py                   |   2 -
- src/sugar3/test/uitree.py                     |   2 +-
- src/sugar3/util.py                            |  20 +-
- .../sample.activity/activity/activity.info    |   2 +-
- tests/data/sample.activity/setup.py           |   2 +-
- tests/graphics/progressicon.py                |   1 +
- tests/test_mime.py                            |   2 +-
- tests/test_uitree.py                          |   3 +-
- 74 files changed, 623 insertions(+), 700 deletions(-)
- create mode 100755 bin/sugar-activity3
- create mode 100644 src/sugar3/activity/activityinstance.py
- delete mode 100644 src/sugar3/graphics/popwindow.py
- rename src/sugar3/test/{unittest.py => _unittest.py} (93%)
-
-diff --git a/Makefile.am b/Makefile.am
-index 70ed1a8ff..d89c4a61b 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -26,4 +26,4 @@ check-po:
- test: check-po
- 	pyflakes $(top_srcdir)
- 	pep8 $(top_srcdir)
--	python -m sugar3.test.discover $(top_srcdir)/tests
-+	python3 -m sugar3.test.discover $(top_srcdir)/tests
-diff --git a/bin/Makefile.am b/bin/Makefile.am
-index 2d214b3cc..3b5f6f8f8 100644
---- a/bin/Makefile.am
-+++ b/bin/Makefile.am
-@@ -1 +1 @@
--dist_bin_SCRIPTS = sugar-activity sugar-activity-web
-+dist_bin_SCRIPTS = sugar-activity sugar-activity-web sugar-activity3
-diff --git a/bin/sugar-activity b/bin/sugar-activity
-index a184655ae..9e12d61ae 100755
---- a/bin/sugar-activity
-+++ b/bin/sugar-activity
-@@ -1,219 +1,5 @@
- #!/usr/bin/env python2
- 
--# Copyright (C) 2006-2008, Red Hat, Inc.
--#
--# 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, write to the Free Software
--# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+from sugar3.activity import activityinstance
- 
--import os
--import sys
--
--# Change the default encoding to avoid UnicodeDecodeError
--# http://lists.sugarlabs.org/archive/sugar-devel/2012-August/038928.html
--reload(sys)
--sys.setdefaultencoding('utf-8')
--
--import gettext
--from optparse import OptionParser
--
--import dbus
--import dbus.service
--from dbus.mainloop.glib import DBusGMainLoop
--DBusGMainLoop(set_as_default=True)
--
--from sugar3.activity import activityhandle
--from sugar3 import config
--from sugar3.bundle.activitybundle import ActivityBundle
--from sugar3 import logger
--
--from sugar3.bundle.bundle import MalformedBundleException
--
--from distutils.dir_util import mkpath
--import time
--import hashlib
--import random
--
--def create_activity_instance(constructor, handle):
--    activity = constructor(handle)
--    activity.show()
--    return activity
--
--
--def get_single_process_name(bundle_id):
--    return bundle_id
--
--
--def get_single_process_path(bundle_id):
--    return '/' + bundle_id.replace('.', '/')
--
--
--class SingleProcess(dbus.service.Object):
--
--    def __init__(self, name_service, constructor):
--        self.constructor = constructor
--
--        bus = dbus.SessionBus()
--        bus_name = dbus.service.BusName(name_service, bus=bus)
--        object_path = get_single_process_path(name_service)
--        dbus.service.Object.__init__(self, bus_name, object_path)
--
--    @dbus.service.method('org.laptop.SingleProcess', in_signature='a{sv}')
--    def create(self, handle_dict):
--        handle = activityhandle.create_from_dict(handle_dict)
--        create_activity_instance(self.constructor, handle)
--
--
--def main():
--    usage = 'usage: %prog [options] [activity dir] [python class]'
--    epilog = 'If you are running from a directory containing an Activity, ' \
--             'the argument may be omitted.  Otherwise please provide either '\
--             'a directory containing a Sugar Activity [activity dir], a '\
--             '[python_class], or both.'
--
--    parser = OptionParser(usage=usage, epilog=epilog)
--    parser.add_option('-b', '--bundle-id', dest='bundle_id',
--                      help='identifier of the activity bundle')
--    parser.add_option('-a', '--activity-id', dest='activity_id',
--                      help='identifier of the activity instance')
--    parser.add_option('-o', '--object-id', dest='object_id',
--                      help='identifier of the associated datastore object')
--    parser.add_option('-u', '--uri', dest='uri',
--                      help='URI to load')
--    parser.add_option('-s', '--single-process', dest='single_process',
--                      action='store_true',
--                      help='start all the instances in the same process')
--    parser.add_option('-i', '--invited', dest='invited',
--                      action='store_true', default=False,
--                      help='the activity is being launched for handling an '
--                           'invite from the network')
--    (options, args) = parser.parse_args()
--
--    logger.start()
--
--    activity_class = None
--    if len(args) == 2:
--        activity_class = args[1]
--        os.chdir(args[0])
--    elif len(args) == 1:
--        if os.path.isdir(args[0]):
--            os.chdir(args[0])
--        else:
--            activity_class = args[0]
--
--    os.environ['SUGAR_BUNDLE_PATH'] = os.path.abspath(os.curdir)
--
--    bundle_path = os.environ['SUGAR_BUNDLE_PATH']
--    sys.path.insert(0, bundle_path)
--
--    try:
--        bundle = ActivityBundle(bundle_path)
--    except MalformedBundleException:
--        parser.print_help()
--        exit(0)
--
--    if not activity_class:
--        if bundle.get_command().startswith('sugar-activity'):
--            activity_class = bundle.get_command().split(" ")[1]
--
--    if 'SUGAR_VERSION' not in os.environ:
--        profile_id = os.environ.get('SUGAR_PROFILE', 'default')
--        home_dir = os.environ.get('SUGAR_HOME', os.path.expanduser('~/.sugar'))
--        base = os.path.join(home_dir, profile_id)
--        activity_root = os.path.join(base, bundle.get_bundle_id())
--
--        instance_dir = os.path.join(activity_root, 'instance')
--        mkpath(instance_dir)
--
--        data_dir = os.path.join(activity_root, 'data')
--        mkpath(data_dir)
--
--        tmp_dir = os.path.join(activity_root, 'tmp')
--        mkpath(tmp_dir)
--
--        os.environ['SUGAR_ACTIVITY_ROOT'] = activity_root
--        os.environ['SUGAR_BUNDLE_PATH'] = bundle.get_path()
--
--    os.environ['SUGAR_BUNDLE_ID'] = bundle.get_bundle_id()
--    os.environ['SUGAR_BUNDLE_NAME'] = bundle.get_name()
--    os.environ['SUGAR_BUNDLE_VERSION'] = str(bundle.get_activity_version())
--
--    # must be done early, some activities set translations globally, SL #3654
--    activity_locale_path = os.environ.get("SUGAR_LOCALEDIR",
--                                          config.locale_path)
--
--    gettext.bindtextdomain(bundle.get_bundle_id(), activity_locale_path)
--    gettext.bindtextdomain('sugar-toolkit-gtk3', config.locale_path)
--    gettext.textdomain(bundle.get_bundle_id())
--
--    splitted_module = activity_class.rsplit('.', 1)
--    module_name = splitted_module[0]
--    class_name = splitted_module[1]
--
--    module = __import__(module_name)
--    for comp in module_name.split('.')[1:]:
--        module = getattr(module, comp)
--
--    activity_constructor = getattr(module, class_name)
--
--    if not options.activity_id:
--        # Generate random hash
--        data = '%s%s' % (time.time(), random.randint(10000, 100000))
--        random_hash = hashlib.sha1(data).hexdigest()
--        options.activity_id = random_hash
--        options.bundle_id = bundle.get_bundle_id()
--
--    activity_handle = activityhandle.ActivityHandle(
--        activity_id=options.activity_id,
--        object_id=options.object_id, uri=options.uri,
--        invited=options.invited)
--
--    if options.single_process is True:
--        sessionbus = dbus.SessionBus()
--
--        service_name = get_single_process_name(options.bundle_id)
--        service_path = get_single_process_path(options.bundle_id)
--
--        bus_object = sessionbus.get_object(
--            'org.freedesktop.DBus', '/org/freedesktop/DBus')
--        try:
--            name = bus_object.GetNameOwner(
--                service_name, dbus_interface='org.freedesktop.DBus')
--        except dbus.DBusException:
--            name = None
--
--        if not name:
--            SingleProcess(service_name, activity_constructor)
--        else:
--            try:
--                single_process = sessionbus.get_object(service_name,
--                                                       service_path)
--                single_process.create(
--                    activity_handle.get_dict(),
--                    dbus_interface='org.laptop.SingleProcess')
--
--                print 'Created %s in a single process.' % service_name
--                sys.exit(0)
--            except (TypeError, dbus.DBusException):
--                print 'Could not communicate with the instance process,' \
--                    'launching a new process'
--
--    if hasattr(module, 'start'):
--        module.start()
--
--    instance = create_activity_instance(activity_constructor, activity_handle)
--
--    if hasattr(instance, 'run_main_loop'):
--        instance.run_main_loop()
--
--main()
-+activityinstance.main()
-diff --git a/bin/sugar-activity-web b/bin/sugar-activity-web
-index b204b3e69..8fd7a59ce 100644
---- a/bin/sugar-activity-web
-+++ b/bin/sugar-activity-web
-@@ -18,7 +18,7 @@
- # Boston, MA 02111-1307, USA.
- 
- if [ "$SUGAR_USE_WEBKIT1" = "yes" ]; then
--    exec sugar-activity sugar3.activity.webkit1.WebActivity $@
-+    exec sugar-activity3 sugar3.activity.webkit1.WebActivity $@
- else
--    exec sugar-activity sugar3.activity.webactivity.WebActivity $@
-+    exec sugar-activity3 sugar3.activity.webactivity.WebActivity $@
- fi
-diff --git a/bin/sugar-activity3 b/bin/sugar-activity3
-new file mode 100755
-index 000000000..6798bc328
---- /dev/null
-+++ b/bin/sugar-activity3
-@@ -0,0 +1,5 @@
-+#!/usr/bin/env python3
-+
-+from sugar3.activity import activityinstance
-+
-+activityinstance.main()
-diff --git a/configure.ac b/configure.ac
-index e0cfdc6d7..e28e4b310 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -15,7 +15,7 @@ GNOME_COMPILE_WARNINGS(maximum)
- 
- AC_PATH_PROG([GLIB_GENMARSHAL], [glib-genmarshal])
- 
--PYTHON=python2
-+PYTHON=python3
- AM_PATH_PYTHON
- 
- PKG_CHECK_MODULES(EXT, gtk+-3.0 gdk-3.0 gdk-pixbuf-2.0 sm ice alsa
-diff --git a/src/sugar3/activity/Makefile.am b/src/sugar3/activity/Makefile.am
-index ace612e96..e3cf05bcd 100644
---- a/src/sugar3/activity/Makefile.am
-+++ b/src/sugar3/activity/Makefile.am
-@@ -2,6 +2,7 @@ sugardir = $(pythondir)/sugar3/activity
- sugar_PYTHON =              \
- 	__init__.py             \
- 	activity.py             \
-+	activityinstance.py     \
- 	activityfactory.py      \
- 	activityhandle.py       \
- 	activityservice.py      \
-diff --git a/src/sugar3/activity/activity.py b/src/sugar3/activity/activity.py
-index fdb7db01a..0fbd1de4f 100644
---- a/src/sugar3/activity/activity.py
-+++ b/src/sugar3/activity/activity.py
-@@ -158,6 +158,7 @@ class MySpecialToolbar(Gtk.Toolbar):
- You may copy it and use it as a template.
- '''
- 
-+import six
- import gettext
- import logging
- import os
-@@ -165,7 +166,6 @@ class MySpecialToolbar(Gtk.Toolbar):
- import time
- from hashlib import sha1
- from functools import partial
--import StringIO
- import cairo
- import json
- 
-@@ -206,7 +206,9 @@ class MySpecialToolbar(Gtk.Toolbar):
- 
- from gi.repository import SugarExt
- 
--_ = lambda msg: gettext.dgettext('sugar-toolkit-gtk3', msg)
-+
-+def _(msg): return gettext.dgettext('sugar-toolkit-gtk3', msg)
-+
- 
- SCOPE_PRIVATE = 'private'
- SCOPE_INVITE_ONLY = 'invite'  # shouldn't be shown in UI, it's implicit
-@@ -881,7 +883,7 @@ def get_preview(self):
-         cr.set_source_surface(screenshot_surface)
-         cr.paint()
- 
--        preview_str = StringIO.StringIO()
-+        preview_str = six.BytesIO()
-         preview_surface.write_to_png(preview_str)
-         return preview_str.getvalue()
- 
-@@ -919,7 +921,7 @@ def save(self):
- 
-         buddies_dict = self._get_buddies()
-         if buddies_dict:
--            self.metadata['buddies_id'] = json.dumps(buddies_dict.keys())
-+            self.metadata['buddies_id'] = json.dumps(list(buddies_dict.keys()))
-             self.metadata['buddies'] = json.dumps(self._get_buddies())
- 
-         # update spent time before saving
-@@ -1103,8 +1105,9 @@ def share(self, private=False):
-             raise RuntimeError('Activity %s already shared.' %
-                                self._activity_id)
-         verb = private and 'private' or 'public'
--        logging.debug('Requesting %s share of activity %s.' % (verb,
--                      self._activity_id))
-+        logging.debug(
-+            'Requesting %s share of activity %s.' %
-+            (verb, self._activity_id))
-         pservice = presenceservice.get_instance()
-         pservice.connect('activity-shared', self.__share_cb)
-         pservice.share_activity(self, private=private)
-@@ -1197,8 +1200,8 @@ def __stop_dialog_response_cb(self, alert, response_id):
-         if response_id == Gtk.ResponseType.OK:
-             title = alert.entry.get_text()
-             if self._is_resumed and \
--                title == self._original_title:
--                    datastore.delete(self._jobject_old.get_object_id())
-+                    title == self._original_title:
-+                datastore.delete(self._jobject_old.get_object_id())
-             self._jobject.metadata['title'] = title
-             self._do_close(False)
- 
-@@ -1222,7 +1225,7 @@ def _get_save_label_tip(self, title):
-         label = _('Save new')
-         tip = _('Save a new journal entry')
-         if self._is_resumed and \
--            title == self._original_title:
-+                title == self._original_title:
-             label = _('Save')
-             tip = _('Save into the old journal entry')
- 
-@@ -1244,7 +1247,7 @@ def _prepare_close(self, skip_save=False):
-         if not skip_save:
-             try:
-                 self.save()
--            except:
-+            except BaseException:
-                 # pylint: disable=W0702
-                 logging.exception('Error saving activity object to datastore')
-                 self._show_keep_failed_dialog()
-@@ -1306,12 +1309,12 @@ def close(self, skip_save=False):
- 
-     def __realize_cb(self, window):
-         display_name = Gdk.Display.get_default().get_name()
--        if ':' in display_name: 
-+        if ':' in display_name:
-             # X11 for sure; this only works in X11
-             xid = window.get_window().get_xid()
-             SugarExt.wm_set_bundle_id(xid, self.get_bundle_id())
-             SugarExt.wm_set_activity_id(xid, str(self._activity_id))
--        elif display_name is 'Broadway': 
-+        elif display_name is 'Broadway':
-             # GTK3's HTML5 backend
-             # This is needed so that the window takes the whole browser window
-             self.maximize()
-@@ -1432,7 +1435,7 @@ def __get_filters_cb(self):
-         }
-         filter_dict = dbus.Dictionary(filters, signature='sv')
-         logging.debug('__get_filters_cb %r' % dbus.Array([filter_dict],
--                      signature='a{sv}'))
-+                                                         signature='a{sv}'))
-         return dbus.Array([filter_dict], signature='a{sv}')
- 
-     @dbus.service.method(dbus_interface=CLIENT_HANDLER,
-@@ -1448,9 +1451,10 @@ def HandleChannels(self, account, connection, channels, requests_satisfied,
-                 handle_type = properties[CHANNEL + '.TargetHandleType']
-                 if channel_type == CHANNEL_TYPE_TEXT:
-                     self._got_channel_cb(connection, object_path, handle_type)
--        except Exception, e:
-+        except Exception as e:
-             logging.exception(e)
- 
-+
- _session = None
- 
- 
-@@ -1503,7 +1507,7 @@ def get_activity_root():
-         activity_root = env.get_profile_path(os.environ['SUGAR_BUNDLE_ID'])
-         try:
-             os.mkdir(activity_root)
--        except OSError, e:
-+        except OSError as e:
-             if e.errno != EEXIST:
-                 raise e
-         return activity_root
-diff --git a/src/sugar3/activity/activityfactory.py b/src/sugar3/activity/activityfactory.py
-index 31895a5d1..b46ecceb0 100644
---- a/src/sugar3/activity/activityfactory.py
-+++ b/src/sugar3/activity/activityfactory.py
-@@ -23,7 +23,6 @@
- """
- 
- import logging
--
- import dbus
- from gi.repository import GObject
- from gi.repository import GLib
-@@ -53,7 +52,7 @@
- 
- 
- def _close_fds():
--    for i in xrange(3, MAXFD):
-+    for i in range(3, MAXFD):
-         try:
-             os.close(i)
-         # pylint: disable=W0704
-@@ -69,7 +68,7 @@ def create_activity_id():
- def _mkdir(path):
-     try:
-         os.mkdir(path)
--    except OSError, e:
-+    except OSError as e:
-         if e.errno != EEXIST:
-             raise e
- 
-@@ -137,10 +136,10 @@ def open_log_file(activity):
-     while True:
-         path = env.get_logs_path('%s-%s.log' % (activity.get_bundle_id(), i))
-         try:
--            fd = os.open(path, os.O_EXCL | os.O_CREAT | os.O_WRONLY, 0644)
--            f = os.fdopen(fd, 'w', 0)
-+            fd = os.open(path, os.O_EXCL | os.O_CREAT | os.O_WRONLY, 0o644)
-+            f = os.fdopen(fd, 'w')
-             return (path, f)
--        except OSError, e:
-+        except OSError as e:
-             if e.errno == EEXIST:
-                 i += 1
-             elif e.errno == ENOSPC:
-@@ -225,7 +224,7 @@ def _create_activity(self):
-                               self._handle.object_id, self._handle.uri,
-                               self._handle.invited)
- 
--        dev_null = file('/dev/null', 'r')
-+        dev_null = open('/dev/null', 'r')
-         child = subprocess.Popen([str(s) for s in command],
-                                  env=environ,
-                                  cwd=str(self._bundle.get_path()),
-diff --git a/src/sugar3/activity/activityhandle.py b/src/sugar3/activity/activityhandle.py
-index d7973a995..d989a8927 100644
---- a/src/sugar3/activity/activityhandle.py
-+++ b/src/sugar3/activity/activityhandle.py
-@@ -24,26 +24,26 @@
- class ActivityHandle(object):
-     '''
-     Data structure storing simple activity metadata
--    
-+
-     Args:
-         activity_id (string): unique id for the activity to be
-         created
--        
-+
-         object_id (string): identity of the journal object
-         associated with the activity.
--        
-+
-         When you resume an activity from the journal
-         the object_id will be passed in. It is optional
-         since new activities does not have an
-         associated object.
--        
-+
-         uri (string): URI associated with the activity. Used when
-         opening an external file or resource in the
-         activity, rather than a journal object
-         (downloads stored on the file system for
-         example or web pages)
--        
--        invited (bool): True if the activity is being 
-+
-+        invited (bool): True if the activity is being
-         launched for handling an invite from the network
-     '''
- 
-@@ -55,7 +55,7 @@ def __init__(self, activity_id=None, object_id=None, uri=None,
-         self.invited = invited
- 
-     def get_dict(self):
--        '''Returns activity settings as a dictionary in format 
-+        '''Returns activity settings as a dictionary in format
-             {activity_id:XXXX, object_id:XXXX, uri:XXXX, invited:BOOL}'''
-         result = {'activity_id': self.activity_id, 'invited': self.invited}
-         if self.object_id:
-diff --git a/src/sugar3/activity/activityinstance.py b/src/sugar3/activity/activityinstance.py
-new file mode 100644
-index 000000000..b756f54f9
---- /dev/null
-+++ b/src/sugar3/activity/activityinstance.py
-@@ -0,0 +1,222 @@
-+# Copyright (C) 2006-2008, Red Hat, Inc.
-+#
-+# 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, write to the Free Software
-+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
-+
-+import os
-+import sys
-+import six
-+import logging
-+
-+# Change the default encoding to avoid UnicodeDecodeError
-+# http://lists.sugarlabs.org/archive/sugar-devel/2012-August/038928.html
-+if six.PY2:
-+    reload(sys)
-+    sys.setdefaultencoding('utf-8')
-+
-+import gettext
-+from optparse import OptionParser
-+
-+import dbus
-+import dbus.service
-+from dbus.mainloop.glib import DBusGMainLoop
-+DBusGMainLoop(set_as_default=True)
-+
-+from sugar3.activity import activityhandle
-+from sugar3 import config
-+from sugar3.bundle.activitybundle import ActivityBundle
-+from sugar3 import logger
-+
-+from sugar3.bundle.bundle import MalformedBundleException
-+
-+from distutils.dir_util import mkpath
-+import time
-+import hashlib
-+import random
-+
-+
-+def create_activity_instance(constructor, handle):
-+    activity = constructor(handle)
-+    activity.show()
-+    return activity
-+
-+
-+def get_single_process_name(bundle_id):
-+    return bundle_id
-+
-+
-+def get_single_process_path(bundle_id):
-+    return '/' + bundle_id.replace('.', '/')
-+
-+
-+class SingleProcess(dbus.service.Object):
-+
-+    def __init__(self, name_service, constructor):
-+        self.constructor = constructor
-+
-+        bus = dbus.SessionBus()
-+        bus_name = dbus.service.BusName(name_service, bus=bus)
-+        object_path = get_single_process_path(name_service)
-+        dbus.service.Object.__init__(self, bus_name, object_path)
-+
-+    @dbus.service.method('org.laptop.SingleProcess', in_signature='a{sv}')
-+    def create(self, handle_dict):
-+        handle = activityhandle.create_from_dict(handle_dict)
-+        create_activity_instance(self.constructor, handle)
-+
-+
-+def main():
-+    usage = 'usage: %prog [options] [activity dir] [python class]'
-+    epilog = 'If you are running from a directory containing an Activity, ' \
-+             'the argument may be omitted.  Otherwise please provide either '\
-+             'a directory containing a Sugar Activity [activity dir], a '\
-+             '[python_class], or both.'
-+
-+    parser = OptionParser(usage=usage, epilog=epilog)
-+    parser.add_option('-b', '--bundle-id', dest='bundle_id',
-+                      help='identifier of the activity bundle')
-+    parser.add_option('-a', '--activity-id', dest='activity_id',
-+                      help='identifier of the activity instance')
-+    parser.add_option('-o', '--object-id', dest='object_id',
-+                      help='identifier of the associated datastore object')
-+    parser.add_option('-u', '--uri', dest='uri',
-+                      help='URI to load')
-+    parser.add_option('-s', '--single-process', dest='single_process',
-+                      action='store_true',
-+                      help='start all the instances in the same process')
-+    parser.add_option('-i', '--invited', dest='invited',
-+                      action='store_true', default=False,
-+                      help='the activity is being launched for handling an '
-+                           'invite from the network')
-+    (options, args) = parser.parse_args()
-+
-+    logger.start()
-+
-+    activity_class = None
-+    if len(args) == 2:
-+        activity_class = args[1]
-+        os.chdir(args[0])
-+    elif len(args) == 1:
-+        if os.path.isdir(args[0]):
-+            os.chdir(args[0])
-+        else:
-+            activity_class = args[0]
-+
-+    os.environ['SUGAR_BUNDLE_PATH'] = os.path.abspath(os.curdir)
-+
-+    bundle_path = os.environ['SUGAR_BUNDLE_PATH']
-+    sys.path.insert(0, bundle_path)
-+
-+    try:
-+        bundle = ActivityBundle(bundle_path)
-+    except MalformedBundleException:
-+        parser.print_help()
-+        exit(0)
-+
-+    if not activity_class:
-+        command = bundle.get_command()
-+        if command.startswith('sugar-activity'):
-+            if not command.startswith('sugar-activity3'):
-+                logging.warning("Activity written for Python 2, consider porting to Python 3.")
-+            activity_class = command.split(" ")[1]
-+
-+    if 'SUGAR_VERSION' not in os.environ:
-+        profile_id = os.environ.get('SUGAR_PROFILE', 'default')
-+        home_dir = os.environ.get('SUGAR_HOME', os.path.expanduser('~/.sugar'))
-+        base = os.path.join(home_dir, profile_id)
-+        activity_root = os.path.join(base, bundle.get_bundle_id())
-+
-+        instance_dir = os.path.join(activity_root, 'instance')
-+        mkpath(instance_dir)
-+
-+        data_dir = os.path.join(activity_root, 'data')
-+        mkpath(data_dir)
-+
-+        tmp_dir = os.path.join(activity_root, 'tmp')
-+        mkpath(tmp_dir)
-+
-+        os.environ['SUGAR_ACTIVITY_ROOT'] = activity_root
-+        os.environ['SUGAR_BUNDLE_PATH'] = bundle.get_path()
-+
-+    os.environ['SUGAR_BUNDLE_ID'] = bundle.get_bundle_id()
-+    os.environ['SUGAR_BUNDLE_NAME'] = bundle.get_name()
-+    os.environ['SUGAR_BUNDLE_VERSION'] = str(bundle.get_activity_version())
-+
-+    # must be done early, some activities set translations globally, SL #3654
-+    activity_locale_path = os.environ.get("SUGAR_LOCALEDIR",
-+                                          config.locale_path)
-+
-+    gettext.bindtextdomain(bundle.get_bundle_id(), activity_locale_path)
-+    gettext.bindtextdomain('sugar-toolkit-gtk3', config.locale_path)
-+    gettext.textdomain(bundle.get_bundle_id())
-+
-+    splitted_module = activity_class.rsplit('.', 1)
-+    module_name = splitted_module[0]
-+    class_name = splitted_module[1]
-+
-+    module = __import__(module_name)
-+    for comp in module_name.split('.')[1:]:
-+        module = getattr(module, comp)
-+
-+    activity_constructor = getattr(module, class_name)
-+
-+    if not options.activity_id:
-+        # Generate random hash
-+        data = '%s%s' % (time.time(), random.randint(10000, 100000))
-+        random_hash = hashlib.sha1(data.encode()).hexdigest()
-+        options.activity_id = random_hash
-+        options.bundle_id = bundle.get_bundle_id()
-+
-+    activity_handle = activityhandle.ActivityHandle(
-+        activity_id=options.activity_id,
-+        object_id=options.object_id, uri=options.uri,
-+        invited=options.invited)
-+
-+    if options.single_process is True:
-+        sessionbus = dbus.SessionBus()
-+
-+        service_name = get_single_process_name(options.bundle_id)
-+        service_path = get_single_process_path(options.bundle_id)
-+
-+        bus_object = sessionbus.get_object(
-+            'org.freedesktop.DBus', '/org/freedesktop/DBus')
-+        try:
-+            name = bus_object.GetNameOwner(
-+                service_name, dbus_interface='org.freedesktop.DBus')
-+        except dbus.DBusException:
-+            name = None
-+
-+        if not name:
-+            SingleProcess(service_name, activity_constructor)
-+        else:
-+            try:
-+                single_process = sessionbus.get_object(service_name,
-+                                                       service_path)
-+                single_process.create(
-+                    activity_handle.get_dict(),
-+                    dbus_interface='org.laptop.SingleProcess')
-+
-+                print('Created %s in a single process.' % service_name)
-+                sys.exit(0)
-+            except (TypeError, dbus.DBusException):
-+                print('Could not communicate with the instance process,'
-+                      'launching a new process')
-+
-+    if hasattr(module, 'start'):
-+        module.start()
-+
-+    instance = create_activity_instance(activity_constructor, activity_handle)
-+
-+    if hasattr(instance, 'run_main_loop'):
-+        instance.run_main_loop()
-diff --git a/src/sugar3/activity/activityservice.py b/src/sugar3/activity/activityservice.py
-index 738e602e0..25329029e 100644
---- a/src/sugar3/activity/activityservice.py
-+++ b/src/sugar3/activity/activityservice.py
-@@ -79,5 +79,5 @@ def HandleViewSource(self):
-     def GetDocumentPath(self, async_cb, async_err_cb):
-         try:
-             self._activity.get_document_path(async_cb, async_err_cb)
--        except Exception, e:
-+        except Exception as e:
-             async_err_cb(e)
-diff --git a/src/sugar3/activity/bundlebuilder.py b/src/sugar3/activity/bundlebuilder.py
-index 403484456..f270346c2 100644
---- a/src/sugar3/activity/bundlebuilder.py
-+++ b/src/sugar3/activity/bundlebuilder.py
-@@ -40,12 +40,13 @@
- import logging
- from glob import glob
- from fnmatch import fnmatch
--from ConfigParser import ConfigParser
-+from six.moves.configparser import ConfigParser
- import xml.etree.cElementTree as ET
--from HTMLParser import HTMLParser
-+from six.moves.html_parser import HTMLParser
- 
- from sugar3 import env
- from sugar3.bundle.activitybundle import ActivityBundle
-+from six.moves import reduce
- 
- 
- IGNORE_DIRS = ['dist', '.git', 'screenshots']
-@@ -150,7 +151,7 @@ def build_locale(self):
-             args = ['msgfmt', '--output-file=%s' % mo_file, file_name]
-             retcode = subprocess.call(args)
-             if retcode:
--                print 'ERROR - msgfmt failed with return code %i.' % retcode
-+                print('ERROR - msgfmt failed with return code %i.' % retcode)
-                 if self._no_fail:
-                     continue
- 
-@@ -301,8 +302,8 @@ def install(self, prefix, install_mime=True, install_desktop_file=True):
- 
-             source_to_dest[source_path] = dest_path
- 
--        for source, dest in source_to_dest.items():
--            print 'Install %s to %s.' % (source, dest)
-+        for source, dest in list(source_to_dest.items()):
-+            print('Install %s to %s.' % (source, dest))
- 
-             path = os.path.dirname(dest)
-             if not os.path.exists(path):
-@@ -429,7 +430,7 @@ def cmd_check(config, options):
-     if options.choice == 'integration':
-         run_unit_test = False
- 
--    print "Running Tests"
-+    print("Running Tests")
- 
-     test_path = os.path.join(config.source_dir, "tests")
- 
-@@ -443,22 +444,22 @@ def cmd_check(config, options):
-             all_tests = unittest.defaultTestLoader.discover(unit_test_path)
-             unittest.TextTestRunner(verbosity=options.verbose).run(all_tests)
-         elif not run_unit_test:
--            print "Not running unit tests"
-+            print("Not running unit tests")
-         else:
--            print 'No "unit" directory found.'
-+            print('No "unit" directory found.')
- 
-         if os.path.isdir(integration_test_path) and run_integration_test:
-             all_tests = unittest.defaultTestLoader.discover(
-                 integration_test_path)
-             unittest.TextTestRunner(verbosity=options.verbose).run(all_tests)
-         elif not run_integration_test:
--            print "Not running integration tests"
-+            print("Not running integration tests")
-         else:
--            print 'No "integration" directory found.'
-+            print('No "integration" directory found.')
- 
--        print "Finished testing"
-+        print("Finished testing")
-     else:
--        print "Error: No tests/ directory"
-+        print("Error: No tests/ directory")
- 
- 
- def cmd_dev(config, options):
-@@ -472,9 +473,9 @@ def cmd_dev(config, options):
-         os.symlink(config.source_dir, bundle_path)
-     except OSError:
-         if os.path.islink(bundle_path):
--            print 'ERROR - The bundle has been already setup for development.'
-+            print('ERROR - The bundle has been already setup for development.')
-         else:
--            print 'ERROR - A bundle with the same name is already installed.'
-+            print('ERROR - A bundle with the same name is already installed.')
- 
- 
- def cmd_dist_xo(config, options):
-@@ -490,9 +491,9 @@ def cmd_dist_xo(config, options):
- def cmd_fix_manifest(config, options):
-     '''Add missing files to the manifest (OBSOLETE)'''
- 
--    print 'WARNING: The fix_manifest command is obsolete.'
--    print '         The MANIFEST file is no longer used in bundles,'
--    print '         please remove it.'
-+    print('WARNING: The fix_manifest command is obsolete.')
-+    print('         The MANIFEST file is no longer used in bundles,')
-+    print('         please remove it.')
- 
- 
- def cmd_dist_source(config, options):
-@@ -506,7 +507,10 @@ def cmd_install(config, options):
-     """Install the activity in the system"""
- 
-     installer = Installer(Builder(config))
--    installer.install(options.prefix, options.install_mime, options.install_desktop_file)
-+    installer.install(
-+        options.prefix,
-+        options.install_mime,
-+        options.install_desktop_file)
- 
- 
- def _po_escape(string):
-@@ -568,7 +572,7 @@ def cmd_genpot(config, options):
-     args += python_files
-     retcode = subprocess.call(args)
-     if retcode:
--        print 'ERROR - xgettext failed with return code %i.' % retcode
-+        print('ERROR - xgettext failed with return code %i.' % retcode)
- 
- 
- def cmd_build(config, options):
-@@ -603,7 +607,7 @@ def start():
-                               choices=['unit', 'integration'],
-                               help="run unit/integration test")
-     check_parser.add_argument("--verbosity", "-v", dest="verbose",
--                              type=int, choices=range(0, 3),
-+                              type=int, choices=list(range(0, 3)),
-                               default=1, nargs='?',
-                               help="verbosity for the unit tests")
- 
-diff --git a/src/sugar3/activity/webkit1.py b/src/sugar3/activity/webkit1.py
-index a2d015480..8ea7dade8 100644
---- a/src/sugar3/activity/webkit1.py
-+++ b/src/sugar3/activity/webkit1.py
-@@ -28,8 +28,8 @@
- from gi.repository import WebKit
- import socket
- from threading import Thread
--from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
--import SocketServer
-+from six.moves.BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
-+from six.moves import socketserver
- import select
- import errno
- import mimetypes
-@@ -80,7 +80,7 @@ def serve_forever(self, poll_interval=0.5):
-             # shutdown request and wastes cpu at all other times.
-             try:
-                 r, w, e = select.select([self], [], [], poll_interval)
--            except select.error, e:
-+            except select.error as e:
-                 if e[0] == errno.EINTR:
-                     logging.debug("got eintr")
-                     continue
-@@ -92,7 +92,7 @@ def serve_forever(self, poll_interval=0.5):
-     def server_bind(self):
-         """Override server_bind in HTTPServer to not use
-         getfqdn to get the server name because is very slow."""
--        SocketServer.TCPServer.server_bind(self)
-+        socketserver.TCPServer.server_bind(self)
-         _host, port = self.socket.getsockname()[:2]
-         self.server_name = 'localhost'
-         self.server_port = port
-diff --git a/src/sugar3/activity/widgets.py b/src/sugar3/activity/widgets.py
-index 06606e807..21e717287 100644
---- a/src/sugar3/activity/widgets.py
-+++ b/src/sugar3/activity/widgets.py
-@@ -34,7 +34,7 @@
- from sugar3 import profile
- 
- 
--_ = lambda msg: gettext.dgettext('sugar-toolkit-gtk3', msg)
-+def _(msg): return gettext.dgettext('sugar-toolkit-gtk3', msg)
- 
- 
- def _create_activity_icon(metadata):
-diff --git a/src/sugar3/bundle/__init__.py b/src/sugar3/bundle/__init__.py
-index 9b8f78f7c..ea06fb45b 100644
---- a/src/sugar3/bundle/__init__.py
-+++ b/src/sugar3/bundle/__init__.py
-@@ -44,7 +44,9 @@
- * `icon` - the icon file for the activity, shown by Sugar in the list
-   of installed activities,
- 
--* `exec` - how to execute the activity, e.g. `sugar-activity module.Class`,
-+* `exec` - how to execute the activity, e.g.
-+  `sugar-activity3 module.Class` (For activities written for Python 3),
-+  `sugar-activity module.Class` (For activities written for Python 2)
- 
- Optional metadata keys are;
- 
-@@ -117,7 +119,7 @@
-     [Activity]
-     name = Browse
-     bundle_id = org.laptop.WebActivity
--    exec = sugar-activity webactivity.WebActivity -s
-+    exec = sugar-activity3 webactivity.WebActivity -s
-     activity_version = 200
-     icon = activity-web
-     max_participants = 100
-diff --git a/src/sugar3/bundle/activitybundle.py b/src/sugar3/bundle/activitybundle.py
-index c0d9d985f..879831e53 100644
---- a/src/sugar3/bundle/activitybundle.py
-+++ b/src/sugar3/bundle/activitybundle.py
-@@ -20,7 +20,7 @@
- UNSTABLE.
- """
- 
--from ConfigParser import ConfigParser, ParsingError
-+from six.moves.configparser import ConfigParser, ParsingError
- from locale import normalize
- import os
- import shutil
-@@ -441,7 +441,7 @@ def uninstall(self, force=False, delete_profile=False):
-         if delete_profile:
-             bundle_profile_path = env.get_profile_path(self._bundle_id)
-             if os.path.exists(bundle_profile_path):
--                os.chmod(bundle_profile_path, 0775)
-+                os.chmod(bundle_profile_path, 0o775)
-                 shutil.rmtree(bundle_profile_path, ignore_errors=True)
- 
-         self._uninstall(install_path)
-diff --git a/src/sugar3/bundle/bundle.py b/src/sugar3/bundle/bundle.py
-index 59cd885ad..c5716107b 100644
---- a/src/sugar3/bundle/bundle.py
-+++ b/src/sugar3/bundle/bundle.py
-@@ -20,10 +20,10 @@
- UNSTABLE.
- """
- 
-+import six
- import os
- import logging
- import shutil
--import StringIO
- import zipfile
- 
- 
-@@ -74,7 +74,7 @@ def __init__(self, path):
-         if not os.path.isdir(self._path):
-             try:
-                 self._zip_file = zipfile.ZipFile(self._path)
--            except zipfile.error, exception:
-+            except zipfile.error as exception:
-                 raise MalformedBundleException('Error accessing zip file %r: '
-                                                '%s' % (self._path, exception))
-             self._check_zip_bundle()
-@@ -115,7 +115,7 @@ def get_file(self, filename):
-         if self._zip_file is None:
-             path = os.path.join(self._path, filename)
-             try:
--                f = open(path, 'rb')
-+                f = open(path, 'r')
-             except IOError:
-                 logging.debug("cannot open path %s" % path)
-                 return None
-@@ -123,7 +123,7 @@ def get_file(self, filename):
-             path = os.path.join(self._zip_root_dir, filename)
-             try:
-                 data = self._zip_file.read(path)
--                f = StringIO.StringIO(data)
-+                f = six.StringIO(data)
-             except KeyError:
-                 logging.debug('%s not found in zip %s.' % (filename, path))
-                 return None
-@@ -171,7 +171,7 @@ def _unzip(self, install_dir):
-             raise AlreadyInstalledException
- 
-         if not os.path.isdir(install_dir):
--            os.mkdir(install_dir, 0775)
-+            os.mkdir(install_dir, 0o775)
- 
-         # zipfile provides API that in theory would let us do this
-         # correctly by hand, but handling all the oddities of
-diff --git a/src/sugar3/bundle/bundleversion.py b/src/sugar3/bundle/bundleversion.py
-index 91df406ae..7ad9e3e09 100644
---- a/src/sugar3/bundle/bundleversion.py
-+++ b/src/sugar3/bundle/bundleversion.py
-@@ -82,6 +82,7 @@ class NormalizedVersion(object):
-     Attributes:
-         parts (list): the numeric parts of the version after normalization.
-     """
-+
-     def __init__(self, activity_version):
-         self._activity_version = activity_version
-         self.parts = []
-diff --git a/src/sugar3/bundle/contentbundle.py b/src/sugar3/bundle/contentbundle.py
-index 146e7f8c6..0e61ade08 100644
---- a/src/sugar3/bundle/contentbundle.py
-+++ b/src/sugar3/bundle/contentbundle.py
-@@ -21,10 +21,11 @@
- UNSTABLE.
- """
- 
--from ConfigParser import ConfigParser
-+from six.moves import urllib
-+from six.moves.configparser import ConfigParser
-+
- import tempfile
- import os
--import urllib
- 
- from sugar3 import env
- from sugar3.bundle.bundle import Bundle, MalformedBundleException
-@@ -142,7 +143,7 @@ def get_icon(self):
- 
-     def get_start_uri(self):
-         path = os.path.join(self.get_path(), self._activity_start)
--        return 'file://' + urllib.pathname2url(path)
-+        return 'file://' + urllib.request.pathname2url(path)
- 
-     def get_bundle_id(self):
-         return self._global_name
-diff --git a/src/sugar3/datastore/datastore.py b/src/sugar3/datastore/datastore.py
-index f132f0c8b..e1b3f8b9a 100644
---- a/src/sugar3/datastore/datastore.py
-+++ b/src/sugar3/datastore/datastore.py
-@@ -20,6 +20,7 @@
- STABLE
- """
- 
-+import six
- import logging
- import time
- from datetime import datetime
-@@ -69,6 +70,7 @@ def __datastore_updated_cb(object_id):
- def __datastore_deleted_cb(object_id):
-     deleted.send(None, object_id=object_id)
- 
-+
- created = dispatch.Signal()
- deleted = dispatch.Signal()
- updated = dispatch.Signal()
-@@ -85,6 +87,12 @@ def __init__(self, properties=None):
-         if not properties:
-             self._properties = {}
-         else:
-+            if six.PY3:
-+                for x, y in properties.items():
-+                    try:
-+                        properties[x] = y.decode()
-+                    except BaseException:
-+                        pass
-             self._properties = properties
- 
-         default_keys = ['activity', 'activity_id',
-@@ -97,6 +105,11 @@ def __getitem__(self, key):
-         return self._properties[key]
- 
-     def __setitem__(self, key, value):
-+        if six.PY3:
-+            try:
-+                value = value.decode()
-+            except BaseException:
-+                pass
-         if key not in self._properties or self._properties[key] != value:
-             self._properties[key] = value
-             self.emit('updated')
-@@ -112,7 +125,7 @@ def has_key(self, key):
-         return key in self._properties
- 
-     def keys(self):
--        return self._properties.keys()
-+        return list(self._properties.keys())
- 
-     def get_dictionary(self):
-         return self._properties
-@@ -128,7 +141,7 @@ def get(self, key, default=None):
- 
-     def update(self, properties):
-         """Update all of the metadata"""
--        for (key, value) in properties.items():
-+        for (key, value) in list(properties.items()):
-             self[key] = value
- 
- 
-diff --git a/src/sugar3/dispatch/dispatcher.py b/src/sugar3/dispatch/dispatcher.py
-index 89d219c12..4b437a3aa 100644
---- a/src/sugar3/dispatch/dispatcher.py
-+++ b/src/sugar3/dispatch/dispatcher.py
-@@ -1,4 +1,6 @@
- import weakref
-+import six
-+
- try:
-     set
- except NameError:
-@@ -11,7 +13,7 @@
- 
- def _make_id(target):
-     if hasattr(target, 'im_func'):
--        return (id(target.im_self), id(target.im_func))
-+        return (id(im_self(target)), id(im_func(target)))
-     return id(target)
- 
- 
-@@ -159,7 +161,7 @@ def send_robust(self, sender, **named):
-         for receiver in self._live_receivers(_make_id(sender)):
-             try:
-                 response = receiver(signal=self, sender=sender, **named)
--            except Exception, err:
-+            except Exception as err:
-                 responses.append((receiver, err))
-             else:
-                 responses.append((receiver, response))
-@@ -195,3 +197,17 @@ def _remove_receiver(self, receiver):
-             for idx, (r_key, _) in enumerate(self.receivers):
-                 if r_key == key:
-                     del self.receivers[idx]
-+
-+
-+def im_self(func):
-+    if six.PY2:
-+        return func.im_self
-+    elif six.PY3:
-+        return func.__self__
-+
-+
-+def im_func(func):
-+    if six.PY2:
-+        return func.im_func
-+    elif six.PY3:
-+        return func.__func__
-diff --git a/src/sugar3/dispatch/saferef.py b/src/sugar3/dispatch/saferef.py
-index df899cddb..581703c21 100644
---- a/src/sugar3/dispatch/saferef.py
-+++ b/src/sugar3/dispatch/saferef.py
-@@ -5,6 +5,7 @@
- aren't handled by the core weakref module).
- """
- 
-+import six
- import weakref
- import traceback
- 
-@@ -21,7 +22,7 @@ def safeRef(target, onDelete=None):
-         weakref or a BoundMethodWeakref) as argument.
-     """
-     if hasattr(target, 'im_self'):
--        if target.im_self is not None:
-+        if im_self(target) is not None:
-             # Turn a bound method into a BoundMethodWeakref instance.
-             # Keep track of these instances for lookup by disconnect().
-             assert hasattr(target, 'im_func'), \
-@@ -123,18 +124,18 @@ def remove(weak, self=self):
-                 try:
-                     if callable(function):
-                         function(self)
--                except Exception, e:
-+                except Exception as e:
-                     try:
-                         traceback.print_exc()
-                     except AttributeError:
--                        print "Exception during saferef %s cleanup "
--                        "function %s: %s" % (self, function, e)
-+                        print("Exception during saferef %s cleanup "
-+                              "function %s: %s" % (self, function, e))
-         self.deletionMethods = [onDelete]
-         self.key = self.calculateKey(target)
--        self.weakSelf = weakref.ref(target.im_self, remove)
--        self.weakFunc = weakref.ref(target.im_func, remove)
--        self.selfName = str(target.im_self)
--        self.funcName = str(target.im_func.__name__)
-+        self.weakSelf = weakref.ref(im_self(target), remove)
-+        self.weakFunc = weakref.ref(im_func(target), remove)
-+        self.selfName = str(im_self(target))
-+        self.funcName = str(im_func(target).__name__)
- 
-     def calculateKey(cls, target):
-         """Calculate the reference key for this reference
-@@ -142,7 +143,7 @@ def calculateKey(cls, target):
-         Currently this is a two-tuple of the id()'s of the
-         target object and the target function respectively.
-         """
--        return (id(target.im_self), id(target.im_func))
-+        return (id(im_self(target)), id(im_func(target)))
-     calculateKey = classmethod(calculateKey)
- 
-     def __str__(self):
-@@ -155,15 +156,19 @@ def __str__(self):
- 
-     __repr__ = __str__
- 
--    def __nonzero__(self):
-+    def __bool__(self):
-         """Whether we are still a valid reference"""
-         return self() is not None
- 
-+    def __nonzero__(self):
-+        """Python2 alternative for __bool__"""
-+        return self() is not None
-+
-     def __cmp__(self, other):
-         """Compare with another reference"""
-         if not isinstance(other, self.__class__):
--            return cmp(self.__class__, type(other))
--        return cmp(self.key, other.key)
-+            return ((self.__class__ > type(other)) - (self.__class__ < type(other)))
-+        return ((self.key > other.key) - (self.key < other.key))
- 
-     def __call__(self):
-         """Return a strong reference to the bound method
-@@ -201,6 +206,7 @@ def foo(self): return "foo"
-     aren't descriptors (such as Jython) this implementation has the advantage
-     of working in the most cases.
-     """
-+
-     def __init__(self, target, onDelete=None):
-         """Return a weak-reference-like instance for a bound method
- 
-@@ -215,9 +221,9 @@ def __init__(self, target, onDelete=None):
-             collected).  Should take a single argument,
-             which will be passed a pointer to this object.
-         """
--        assert getattr(target.im_self, target.__name__) == target, \
-+        assert getattr(im_self(target), target.__name__) == target, \
-             ("method %s isn't available as the attribute %s of %s" %
--                (target, target.__name__, target.im_self))
-+                (target, target.__name__, im_self(target)))
-         super(BoundNonDescriptorMethodWeakref, self).__init__(target, onDelete)
- 
-     def __call__(self):
-@@ -255,3 +261,17 @@ def get_bound_method_weakref(target, onDelete):
-         # no luck, use the alternative implementation:
-         return BoundNonDescriptorMethodWeakref(target=target,
-                                                onDelete=onDelete)
-+
-+
-+def im_self(func):
-+    if six.PY2:
-+        return func.im_self
-+    elif six.PY3:
-+        return func.__self__
-+
-+
-+def im_func(func):
-+    if six.PY2:
-+        return func.im_func
-+    elif six.PY3:
-+        return func.__func__
-diff --git a/src/sugar3/env.py b/src/sugar3/env.py
-index a18d5c93f..bbb1a9a18 100644
---- a/src/sugar3/env.py
-+++ b/src/sugar3/env.py
-@@ -36,9 +36,9 @@ def get_profile_path(path=None):
-     base = os.path.join(home_dir, profile_id)
-     if not os.path.isdir(base):
-         try:
--            os.makedirs(base, 0770)
-+            os.makedirs(base, 0o770)
-         except OSError:
--            print 'Could not create user directory.'
-+            print('Could not create user directory.')
- 
-     if path is not None:
-         return os.path.join(base, path)
-diff --git a/src/sugar3/graphics/Makefile.am b/src/sugar3/graphics/Makefile.am
-index e5fdf09a1..0621750ef 100644
---- a/src/sugar3/graphics/Makefile.am
-+++ b/src/sugar3/graphics/Makefile.am
-@@ -16,7 +16,6 @@ sugar_PYTHON =              \
- 	palettemenu.py          \
- 	palettewindow.py        \
- 	panel.py                \
--	popwindow.py            \
- 	radiopalette.py         \
- 	radiotoolbutton.py      \
- 	scrollingdetector.py    \
-diff --git a/src/sugar3/graphics/alert.py b/src/sugar3/graphics/alert.py
-index d262a209e..66cefd9df 100644
---- a/src/sugar3/graphics/alert.py
-+++ b/src/sugar3/graphics/alert.py
-@@ -61,7 +61,7 @@
- from sugar3.graphics.icon import Icon
- 
- 
--_ = lambda msg: gettext.dgettext('sugar-toolkit-gtk3', msg)
-+def _(msg): return gettext.dgettext('sugar-toolkit-gtk3', msg)
- 
- 
- if not hasattr(GObject.ParamFlags, 'READWRITE'):
-@@ -258,6 +258,8 @@ def _response(self, response_id):
- 
-     def __button_clicked_cb(self, button, response_id):
-         self._response(response_id)
-+
-+
- if hasattr(Alert, 'set_css_name'):
-     Alert.set_css_name('alert')
- 
-@@ -294,9 +296,9 @@ def _alert_response_cb(self, alert, response_id):
- 
-             # Check the response identifier.
-             if response_id is Gtk.ResponseType.OK:
--                print 'Ok Button was clicked.'
-+                print('Ok Button was clicked.')
-             elif response_id is Gtk.ResponseType.CANCEL:
--                print 'Cancel Button was clicked.'
-+                print('Cancel Button was clicked.')
-     """
- 
-     def __init__(self, **kwargs):
-@@ -341,7 +343,7 @@ def _alert_response_cb(self, alert, response_id):
- 
-             # Check the response identifier.
-             if response_id is Gtk.ResponseType.OK:
--                print 'Ok Button was clicked.'
-+                print('Ok Button was clicked.')
-     """
- 
-     def __init__(self, **kwargs):
-@@ -390,6 +392,8 @@ def _draw(self, context):
- 
-     def set_text(self, text):
-         self._text.set_markup('<b>%s</b>' % GLib.markup_escape_text(str(text)))
-+
-+
- if hasattr(_TimeoutIcon, 'set_css_name'):
-     _TimeoutIcon.set_css_name('timeouticon')
- 
-@@ -458,11 +462,11 @@ def __alert_response_cb(self, alert, response_id):
- 
-             # Check the response identifier.
-             if response_id is Gtk.ResponseType.OK:
--                print 'Continue Button was clicked.'
-+                print('Continue Button was clicked.')
-             elif response_id is Gtk.ResponseType.CANCEL:
--                print 'Cancel Button was clicked.'
-+                print('Cancel Button was clicked.')
-             elif response_id == -1:
--                print 'Timeout occurred'
-+                print('Timeout occurred')
-     """
- 
-     def __init__(self, timeout=5, **kwargs):
-@@ -508,9 +512,9 @@ def __alert_response_cb(self, alert, response_id):
- 
-             # Check the response identifier.
-             if response_id is Gtk.ResponseType.OK:
--                print 'Ok Button was clicked.'
-+                print('Ok Button was clicked.')
-             elif response_id == -1:
--                print 'Timeout occurred'
-+                print('Timeout occurred')
-     """
- 
-     def __init__(self, timeout=5, **kwargs):
-diff --git a/src/sugar3/graphics/colorbutton.py b/src/sugar3/graphics/colorbutton.py
-index 2ca6851c2..10c188ef6 100644
---- a/src/sugar3/graphics/colorbutton.py
-+++ b/src/sugar3/graphics/colorbutton.py
-@@ -29,7 +29,7 @@
- from sugar3.graphics.palette import Palette, ToolInvoker, WidgetInvoker
- 
- 
--_ = lambda msg: gettext.dgettext('sugar-toolkit-gtk3', msg)
-+def _(msg): return gettext.dgettext('sugar-toolkit-gtk3', msg)
- 
- 
- if not hasattr(GObject.ParamFlags, 'READWRITE'):
-@@ -38,8 +38,8 @@
- 
- 
- def get_svg_color_string(color):
--    return '#%.2X%.2X%.2X' % (color.red / 257, color.green / 257,
--                              color.blue / 257)
-+    return '#%.2X%.2X%.2X' % (color.red // 257, color.green // 257,
-+                              color.blue // 257)
- 
- 
- class _ColorButton(Gtk.Button):
-@@ -123,8 +123,8 @@ def _get_fg_style_color_str(self):
-         context = self.get_style_context()
-         fg_color = context.get_color(Gtk.StateType.NORMAL)
-         # the color components are stored as float values between 0.0 and 1.0
--        return '#%.2X%.2X%.2X' % (fg_color.red * 255, fg_color.green * 255,
--                                  fg_color.blue * 255)
-+        return '#%.2X%.2X%.2X' % (int(fg_color.red * 255), int(fg_color.green * 255),
-+                                  int(fg_color.blue * 255))
- 
-     def set_color(self, color):
-         assert isinstance(color, Gdk.Color)
-@@ -149,11 +149,11 @@ def set_icon_name(self, icon_name):
-         '''
-         Sets the icon for the tool button from a named themed icon.
-         If it is none then no icon will be shown.
--        
-+
-         Args:
-             icon_name(string): The name for a themed icon.
-             It can be set as 'None' too.
--        
-+
-         Example:
-             set_icon_name('view-radial')
-         '''
-@@ -169,11 +169,11 @@ def get_icon_name(self):
-     icon_name = GObject.Property(type=str,
-                                  getter=get_icon_name, setter=set_icon_name)
- 
--    def set_icon_size(self, icon_size):
--        self._preview.props.icon_size = icon_size
-+    def set_icon_size(self, pixel_size):
-+        self._preview.props.pixel_size = pixel_size
- 
-     def get_icon_size(self):
--        return self._preview.props.icon_size
-+        return self._preview.props.pixel_size
- 
-     icon_size = GObject.Property(type=int,
-                                  getter=get_icon_size, setter=set_icon_size)
-@@ -496,13 +496,13 @@ def __button_can_activate_accel_cb(self, button, signal_id):
-     def set_accelerator(self, accelerator):
-         '''
-         Sets keyboard shortcut that activates this button.
--        
-+
-         Args:
-             accelerator(string): accelerator to be set. Should be in
-             form <modifier>Letter
-             Find about format here :
-             https://developer.gnome.org/gtk3/stable/gtk3-Keyboard-Accelerators.html#gtk-accelerator-parse
--        
-+
-         Example:
-             set_accelerator(self, 'accel')
-         '''
-@@ -523,21 +523,21 @@ def create_palette(self):
-         The create_palette function is called when the palette needs to be
-         invoked.  For example, when the user has right clicked the icon or
-         the user has hovered over the icon for a long time.
--        
-+
-         The create_palette will only be called once or zero times.  The palette
-         returned will be stored and re-used if the user invokes the palette
-         multiple times.
--        
-+
-         Your create_palette implementation does not need to
-         :any:`Gtk.Widget.show` the palette, as this will be done by the
-         invoker.  However, you still need to show
-         the menu items, etc that you place in the palette.
--        
-+
-         Returns:
--            
-+
-             sugar3.graphics.palette.Palette, or None to indicate that you
-             do not want a palette shown
--        
-+
-         The default implementation returns None, to indicate no palette should
-         be shown.
-         '''
-@@ -595,11 +595,11 @@ def set_icon_name(self, icon_name):
-         '''
-         Sets the icon for the tool button from a named themed icon.
-         If it is none then no icon will be shown.
--        
-+
-         Args:
-             icon_name(string): The name for a themed icon.
-             It can be set as 'None' too.
--        
-+
-         Example:
-             set_icon_name('view-radial')
-         '''
-@@ -632,8 +632,8 @@ def get_icon_size(self):
- 
-     def set_title(self, title):
-         '''
--        The set_title() method sets the "title" property to the value of 
--        title. The "title" property contains the string that is used to 
-+        The set_title() method sets the "title" property to the value of
-+        title. The "title" property contains the string that is used to
-         set the colorbutton title.
-         '''
-         self.get_child().props.title = title
-diff --git a/src/sugar3/graphics/icon.py b/src/sugar3/graphics/icon.py
-index 0666dc158..2343aee36 100644
---- a/src/sugar3/graphics/icon.py
-+++ b/src/sugar3/graphics/icon.py
-@@ -87,11 +87,13 @@
- and 85.0% on the Y axis.
- '''
- 
-+import six
- import re
- import math
- import logging
- import os
--from ConfigParser import ConfigParser
-+
-+from six.moves.configparser import ConfigParser
- 
- import gi
- gi.require_version('Rsvg', '2.0')
-@@ -127,8 +129,8 @@ def load(self, file_name, entities, cache):
-             if cache:
-                 self._cache[file_name] = icon
- 
--        for entity, value in entities.items():
--            if isinstance(value, basestring):
-+        for entity, value in list(entities.items()):
-+            if isinstance(value, six.string_types):
-                 xml = '<!ENTITY %s "%s">' % (entity, value)
-                 icon = re.sub('<!ENTITY %s .*>' % entity, xml, icon)
-             else:
-@@ -207,7 +209,7 @@ def _get_attach_points(self, info, size_request):
-             # try read from the .icon file
-             icon_filename = info.get_filename().replace('.svg', '.icon')
-             if icon_filename != info.get_filename() and \
--                os.path.exists(icon_filename):
-+                            os.path.exists(icon_filename):
- 
-                 try:
-                     with open(icon_filename) as config_file:
-@@ -470,7 +472,6 @@ class Icon(Gtk.Image):
- 
-     __gtype_name__ = 'SugarIcon'
- 
--    # FIXME: deprecate icon_size
-     _MENU_SIZES = (Gtk.IconSize.MENU, Gtk.IconSize.DND,
-                    Gtk.IconSize.SMALL_TOOLBAR, Gtk.IconSize.BUTTON)
- 
-@@ -483,7 +484,6 @@ def __init__(self, **kwargs):
-         self._alpha = 1.0
-         self._scale = 1.0
- 
--        # FIXME: deprecate icon_size
-         if 'icon_size' in kwargs:
-             logging.warning("icon_size is deprecated. Use pixel_size instead.")
- 
-@@ -532,7 +532,6 @@ def _sync_image_properties(self):
-         if self._buffer.file_name != self.props.file:
-             self._buffer.file_name = self.props.file
- 
--        # FIXME: deprecate icon_size
-         pixel_size = None
-         if self.props.pixel_size == -1:
-             if self.props.icon_size in self._MENU_SIZES:
-@@ -549,7 +548,7 @@ def _sync_image_properties(self):
-             self._buffer.height = height
- 
-     def _icon_size_changed_cb(self, image, pspec):
--        self._buffer.icon_size = self.props.icon_size
-+        self._buffer.icon_size = self.props.pixel_size
- 
-     def _icon_name_changed_cb(self, image, pspec):
-         self._buffer.icon_name = self.props.icon_name
-@@ -805,7 +804,7 @@ def __init__(self, **kwargs):
-         # for example, after a touch palette invocation
-         self.connect_after('button-release-event',
-                            self.__button_release_event_cb)
--        for key, value in kwargs.iteritems():
-+        for key, value in six.iteritems(kwargs):
-             self.set_property(key, value)
- 
-         from sugar3.graphics.palette import CursorInvoker
-@@ -1152,6 +1151,8 @@ def __palette_popup_cb(self, palette):
- 
-     def __palette_popdown_cb(self, palette):
-         self.unset_state_flags(Gtk.StateFlags.PRELIGHT)
-+
-+
- if hasattr(CanvasIcon, 'set_css_name'):
-     CanvasIcon.set_css_name('canvasicon')
- 
-@@ -1447,6 +1448,6 @@ def get_surface(**kwargs):
-         cairo surface or None if image was not found
-     '''
-     icon = _IconBuffer()
--    for key, value in kwargs.items():
-+    for key, value in list(kwargs.items()):
-         icon.__setattr__(key, value)
-     return icon.get_surface()
-diff --git a/src/sugar3/graphics/iconentry.py b/src/sugar3/graphics/iconentry.py
-index 243807fa5..bc4746168 100644
---- a/src/sugar3/graphics/iconentry.py
-+++ b/src/sugar3/graphics/iconentry.py
-@@ -61,7 +61,7 @@ def set_icon_from_name(self, position, name):
-         self.set_icon(position, pixbuf)
- 
-     def set_icon(self, position, pixbuf):
--        if type(pixbuf) is not GdkPixbuf.Pixbuf:
-+        if not isinstance(pixbuf, GdkPixbuf.Pixbuf):
-             raise ValueError('Argument must be a pixbuf, not %r.' % pixbuf)
-         self.set_icon_from_pixbuf(position, pixbuf)
- 
-diff --git a/src/sugar3/graphics/objectchooser.py b/src/sugar3/graphics/objectchooser.py
-index e7fad8853..7cbe860f2 100644
---- a/src/sugar3/graphics/objectchooser.py
-+++ b/src/sugar3/graphics/objectchooser.py
-@@ -19,8 +19,8 @@
- STABLE.
- """
- 
-+import six
- import logging
--import StringIO
- import cairo
- 
- from gi.repository import GObject
-@@ -59,13 +59,13 @@ def get_preview_pixbuf(preview_data, width=-1, height=-1):
-         None, if it could not be created
- 
-     Example:
--        pixbuf = get_preview_pixbuf(metadata.get('preview', ''))                
--        has_preview = pixbuf is not None                                        
--                                                                               
--        if has_preview:                                                        
--            im = Gtk.Image()                                                    
--            im.set_from_pixbuf(pixbuf)                                          
--            box.add(im)                                                        
-+        pixbuf = get_preview_pixbuf(metadata.get('preview', ''))
-+        has_preview = pixbuf is not None
-+
-+        if has_preview:
-+            im = Gtk.Image()
-+            im.set_from_pixbuf(pixbuf)
-+            box.add(im)
-             im.show()
-     """
-     if width == -1:
-@@ -82,7 +82,7 @@ def get_preview_pixbuf(preview_data, width=-1, height=-1):
-             import base64
-             preview_data = base64.b64decode(preview_data)
- 
--        png_file = StringIO.StringIO(preview_data)
-+        png_file = six.StringIO(preview_data)
-         try:
-             # Load image and scale to dimensions
-             surface = cairo.ImageSurface.create_from_png(png_file)
-@@ -126,7 +126,7 @@ class ObjectChooser(object):
-         what_filter (str): an activity bundle_id or a generic mime type as
-             defined in :mod:`sugar3.mime` used to determine which objects
-             will be presented in the object chooser
--        
-+
-         filter_type (str): should be one of [None, FILTER_TYPE_GENERIC_MIME,
-             FILTER_TYPE_ACTIVITY, FILTER_TYPE_MIME_BY_ACTIVITY]
- 
-@@ -155,7 +155,7 @@ class ObjectChooser(object):
- 
-     Examples:
-         chooser = ObjectChooser(self._activity, what_filter='Image')
--        
-+
-         chooser = ObjectChooser(parent=self,
-                                 what_filter=self.get_bundle_id(),
-                                 filter_type=FILTER_TYPE_ACTIVITY)
-@@ -192,7 +192,7 @@ def __init__(self, parent=None, what_filter=None, filter_type=None,
-     def run(self):
-         """
-         Runs the object chooser and displays it.
--        
-+
-         Returns:
-             Gtk.ResponseType constant, the response received from displaying the
-                 object chooser.
-diff --git a/src/sugar3/graphics/palettegroup.py b/src/sugar3/graphics/palettegroup.py
-index 98539feb2..9940e355b 100644
---- a/src/sugar3/graphics/palettegroup.py
-+++ b/src/sugar3/graphics/palettegroup.py
-@@ -36,7 +36,7 @@ def get_group(group_id):
- 
- 
- def popdown_all():
--    for group in _groups.values():
-+    for group in list(_groups.values()):
-         group.popdown()
- 
- 
-diff --git a/src/sugar3/graphics/palettemenu.py b/src/sugar3/graphics/palettemenu.py
-index 231280716..ddc871164 100644
---- a/src/sugar3/graphics/palettemenu.py
-+++ b/src/sugar3/graphics/palettemenu.py
-@@ -56,7 +56,7 @@ def __init__(self):
-                 menu_item.show()
- 
-             def __edit_cb(self, menu_item):
--                print 'Edit...'
-+                print('Edit...')
- 
-         # Usually the Palette instance is returned in a create_palette function
-         p = ItemPalette()
-diff --git a/src/sugar3/graphics/palettewindow.py b/src/sugar3/graphics/palettewindow.py
-index 3253fd00a..1cadbb677 100644
---- a/src/sugar3/graphics/palettewindow.py
-+++ b/src/sugar3/graphics/palettewindow.py
-@@ -43,6 +43,7 @@
- 
- _pointer = None
- 
-+
- def _get_pointer_position(widget):
-     global _pointer
- 
-@@ -53,6 +54,7 @@ def _get_pointer_position(widget):
-     screen, x, y = _pointer.get_position()
-     return (x, y)
- 
-+
- def _calculate_gap(a, b):
-     """Helper function to find the gap position and size of widget a"""
-     # Test for each side if the palette and invoker are
-@@ -420,6 +422,8 @@ def popdown(self):
-         self.disconnect_by_func(self.__enter_notify_event_cb)
-         self.disconnect_by_func(self.__leave_notify_event_cb)
-         self.hide()
-+
-+
- if hasattr(_PaletteWindowWidget, 'set_css_name'):
-     _PaletteWindowWidget.set_css_name('palette')
- 
-@@ -954,7 +958,7 @@ def get_alignment(self, palette_dim):
-             dright = screen_area.x + screen_area.width - rect.x - rect.width
- 
-             ih = 0
--            
-+
-             if palette_dim.width == 0:
-                 ph = 0
- 
-@@ -1315,7 +1319,7 @@ def attach(self, parent):
-         self._leave_hid = self._item.connect('leave-notify-event',
-                                              self.__leave_notify_event_cb)
-         self._release_hid = self._item.connect('button-release-event',
--                                               self.__button_release_event_cb)
-+                                             self.__button_release_event_cb)
-         self._long_pressed_hid = self._long_pressed_controller.connect(
-             'pressed',
-             self.__long_pressed_event_cb, self._item)
-@@ -1325,9 +1329,9 @@ def attach(self, parent):
- 
-     def detach(self):
-         Invoker.detach(self)
--        self._item.disconnect(self._enter_hid)
--        self._item.disconnect(self._leave_hid)
--        self._item.disconnect(self._release_hid)
-+        self._item.disconnect_by_func(self.__enter_notify_event_cb)
-+        self._item.disconnect_by_func(self.__leave_notify_event_cb)
-+        self._item.disconnect_by_func(self.__button_release_event_cb)
-         self._long_pressed_controller.detach(self._item)
-         self._long_pressed_controller.disconnect(self._long_pressed_hid)
- 
-diff --git a/src/sugar3/graphics/popwindow.py b/src/sugar3/graphics/popwindow.py
-deleted file mode 100644
-index 5a4a4d9ea..000000000
---- a/src/sugar3/graphics/popwindow.py
-+++ /dev/null
-@@ -1,202 +0,0 @@
--# Copyright (C) 2016 Abhijit Patel
--#
--# 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, write to the Free Software
--# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
--
--'''
--Provide a PopWindow class for pop-up windows.
--Making PopWindow containing Gtk.Toolbar which also contains Gtk.Label
--and Toolbutton at the end of the Gtk.Toolbar.
--
--It is possible to change props like size and add more widgets PopWindow
--and also to Gtk.Toolbar.
--
--Example:
--    .. literalinclude: ..sugar/src/jarabe/view/viewsource.py
--    .. literalinclude: ..sugar/src/jarabe/view/viewhelp.py
--'''
--from gettext import gettext as _
--from gi.repository import Gtk
--from gi.repository import Gdk
--from gi.repository import GdkX11
--from gi.repository import GObject
--
--from sugar3.graphics import style
--from sugar3.graphics.toolbutton import ToolButton
--
--from jarabe.model import shell
--
--
--class PopWindow(Gtk.Window):
--    """
--    UI interface for activity Pop-up Windows.
--    PopWindows are the windows that open on the top of the current window.
--    These pop-up windows don't cover the whole screen.
--    They contain canvas content, alerts messages, a tray and a
--    toolbar.
--
--    FULLSCREEN and HALF_WIDTH for setting size of the window.
--
--    Kwargs:
--        size (int,int): size to be set of the window
--        window_xid (xlib.Window): xid of the parent window
--    """
--    FULLSCREEN = (Gdk.Screen.width() - style.GRID_CELL_SIZE * 3,
--                  Gdk.Screen.height() - style.GRID_CELL_SIZE * 2)
--
--    HALF_WIDTH = ((Gdk.Screen.height() - style.GRID_CELL_SIZE * 3)/2,
--                  (Gdk.Screen.height() - style.GRID_CELL_SIZE * 2))
--
--    def __init__(self, window_xid=None, **kwargs):
--        Gtk.Window.__init__(self, **kwargs)
--        self._parent_window_xid = window_xid
--
--        self.set_decorated(False)
--        self.set_position(Gtk.WindowPosition.CENTER_ALWAYS)
--        self.set_border_width(style.LINE_WIDTH)
--        self.set_has_resize_grip(False)
--        self.props.size = self.FULLSCREEN
--
--        self.connect('realize', self.__realize_cb)
--        self.connect('key-press-event', self.__key_press_event_cb)
--        self.connect('hide', self.__hide_cb)
--
--        self._vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
--        self.add(self._vbox)
--        self._vbox.show()
--
--        self._title_box = TitleBox()
--        self._title_box.close_button.connect(
--            'clicked',
--            self.__close_button_clicked_cb)
--        self._title_box.set_size_request(-1, style.GRID_CELL_SIZE)
--
--        self._vbox.pack_start(self._title_box, False, True, 0)
--        self._title_box.show()
--
--        # Note:
--        # Not displaying the pop-up from here instead allowing
--        # the child class to display the window after modifications
--        # like chaninging window size, decorating, changing position.
--
--    def set_size(self, size):
--        width, height = size
--        self.set_size_request(width, height)
--
--    size = GObject.Property(type=None, setter=set_size)
--
--    def get_title_box(self):
--        '''
--        Getter method for title-box
--
--        Returns:
--            self._title_box (): Title or Tool Box
--        '''
--        return self._title_box
--
--    title_box = GObject.Property(type=str, getter=get_title_box)
--
--    def get_vbox(self):
--        '''
--        Getter method for canvas
--
--        Returns:
--            self._vbox (Gtk.Box): canvas
--        '''
--        return self._vbox
--    vbox = GObject.Property(type=str, getter=get_vbox)
--
--    def __close_button_clicked_cb(self, button):
--        self.destroy()
--
--    def __key_press_event_cb(self, window, event):
--        keyname = Gdk.keyval_name(event.keyval)
--        if keyname == 'Escape':
--            self.destroy()
--
--    def __realize_cb(self, widget):
--        self.set_type_hint(Gdk.WindowTypeHint.DIALOG)
--        window = self.get_window()
--        window.set_accept_focus(True)
--
--        if self._parent_window_xid is not None:
--            display = Gdk.Display.get_default()
--            parent = GdkX11.X11Window.foreign_new_for_display(
--                display, self._parent_window_xid)
--            window.set_transient_for(parent)
--            shell.get_model().push_modal()
--
--    def __hide_cb(self, widget):
--        shell.get_model().pop_modal()
--
--    def add_view(self, widget, expand=True, fill=True, padding=0):
--        '''
--        Adds child to the vbox.
--
--        Args:
--            widget (Gtk.Widget): widget to be added
--
--            expand (bool): True if child is to be given extra space allocated
--                to vbox.
--
--            fill (bool): True if space given to child by the expand option is
--                actually allocated to child, rather than just padding it.
--
--            padding (int): extra space in pixels to put between child and its
--                neighbors, over and above the global amount specified
--                by spacing in vbox.
--
--        Returns:
--            None
--        '''
--        self._vbox.pack_start(widget, expand, fill, padding)
--
--
--class TitleBox(Gtk.Toolbar):
--    '''
--    Title box at the top of the pop-up window.
--    Title and close button are added to the box and as needed more widgets
--    can be added using self.add_widget method.
--    This box is optional as the inherited class can remove this block by
--    setting the self._set_title_box to False.
--    '''
--
--    def __init__(self):
--        Gtk.Toolbar.__init__(self)
--
--        self.close_button = ToolButton(icon_name='dialog-cancel')
--        self.close_button.set_tooltip(_('Close'))
--        self.insert(self.close_button, -1)
--        self.close_button.show()
--
--        self._label = Gtk.Label()
--        self._label.set_alignment(0, 0.5)
--
--        tool_item = Gtk.ToolItem()
--        tool_item.set_expand(True)
--        tool_item.add(self._label)
--        self._label.show()
--        self.insert(tool_item, 0)
--        tool_item.show()
--
--    def set_title(self, title):
--        '''
--        setter function for 'title' property.
--        Args:
--           title (str): title for the pop-up window
--        '''
--        self._label.set_markup('<b>%s</b>' % title)
--        self._label.show()
--
--    title = GObject.Property(type=str, setter=set_title)
-diff --git a/src/sugar3/graphics/progressicon.py b/src/sugar3/graphics/progressicon.py
-index 327487102..d001efc7c 100644
---- a/src/sugar3/graphics/progressicon.py
-+++ b/src/sugar3/graphics/progressicon.py
-@@ -41,6 +41,7 @@ class ProgressIcon(Gtk.DrawingArea):
-       fill_color (string): The main (inside) color of progressicon
-          [e.g. fill_color=style.COLOR_BLUE.get_svg()
-     '''
-+
-     def __init__(self, icon_name, pixel_size, stroke_color, fill_color,
-                  direction='vertical'):
-         Gtk.DrawingArea.__init__(self)
-diff --git a/src/sugar3/graphics/scrollingdetector.py b/src/sugar3/graphics/scrollingdetector.py
-index c0b483b27..a533aae04 100644
---- a/src/sugar3/graphics/scrollingdetector.py
-+++ b/src/sugar3/graphics/scrollingdetector.py
-@@ -32,18 +32,18 @@
- 
- class ScrollingDetector(GObject.GObject):
-     '''
--    The scrolling detector sends signals when a scrolled window is scrolled and 
-+    The scrolling detector sends signals when a scrolled window is scrolled and
-     when a scrolled window stops scrolling. Only one `scroll-start` signal will be
--	emitted until scrolling stops.
--    
-+        emitted until scrolling stops.
-+
-     The `scroll-start` signal is emitted when scrolling begins and
-     The `scroll-end` signal is emitted when scrolling ends
-     Neither of these two signals have any arguments
--    
-+
-     Args:
-         scrolled_window (Gtk.ScrolledWindow): A GTK scrolled window object for which
-             scrolling is to be detected
--        
-+
-         timeout (int): time in milliseconds to establish the interval for which
-             scrolling is detected
-     '''
-@@ -65,7 +65,7 @@ def connect_scrolled_window(self):
-         Connects scrolling detector to a scrolled window.
-         Detects scrolling when the vertical scrollbar
-         adjustment value is changed
--        
-+
-         Should be used to link an instance of a scrolling detector
-         to a Scrolled Window, after setting scrolled_window
-         '''
-diff --git a/src/sugar3/graphics/style.py b/src/sugar3/graphics/style.py
-index 1a5f47f66..8dafd5b7d 100644
---- a/src/sugar3/graphics/style.py
-+++ b/src/sugar3/graphics/style.py
-@@ -59,6 +59,7 @@ class Font(object):
-     Args:
-         desc (str): a description of the Font object
-     '''
-+
-     def __init__(self, desc):
-         self._desc = desc
- 
-@@ -84,6 +85,7 @@ class Color(object):
- 
-         alpha (double):  transparency of color
-     '''
-+
-     def __init__(self, color, alpha=1.0):
-         self._r, self._g, self._b = self._html_to_rgb(color)
-         self._a = alpha
-@@ -112,7 +114,8 @@ def get_html(self):
-         '''
-         Returns string in the standard html Color format (#FFFFFF)
-         '''
--        return '#%02x%02x%02x' % (self._r * 255, self._g * 255, self._b * 255)
-+        return '#%02x%02x%02x' % (
-+            int(self._r * 255), int(self._g * 255), int(self._b * 255))
- 
-     def _html_to_rgb(self, html_color):
-         '''
-diff --git a/src/sugar3/graphics/toolbarbox.py b/src/sugar3/graphics/toolbarbox.py
-index c813da065..804e2b24f 100644
---- a/src/sugar3/graphics/toolbarbox.py
-+++ b/src/sugar3/graphics/toolbarbox.py
-@@ -203,6 +203,8 @@ def __remove_cb(self, sender, button):
-         if button == self.expanded_button:
-             self.remove(button.page_widget)
-             self._expanded_button_index = -1
-+
-+
- if hasattr(ToolbarBox, 'set_css_name'):
-     ToolbarBox.set_css_name('toolbarbox')
- 
-diff --git a/src/sugar3/graphics/toolbox.py b/src/sugar3/graphics/toolbox.py
-index 5cd41f11d..23843b11b 100644
---- a/src/sugar3/graphics/toolbox.py
-+++ b/src/sugar3/graphics/toolbox.py
-@@ -32,7 +32,7 @@ class Toolbox(Gtk.VBox):
-     Class to represent the toolbox of an activity. Groups a
-     number of toolbars vertically, which can be accessed using their
-     indices. The current toolbar is the only one displayed.
--    
-+
-     Emits `current-toolbar-changed` signal when the
-     current toolbar is changed. This signal takes the current page index
-     as an argument.
-@@ -71,13 +71,13 @@ def _notify_page_cb(self, notebook, pspec):
-     def add_toolbar(self, name, toolbar):
-         '''
-         Adds a toolbar to this toolbox. Toolbar will be added
--        to the end of this toolbox, and it's index will be 
-+        to the end of this toolbox, and it's index will be
-         1 greater than the previously added index (index will be
-         0 if it is the first toolbar added).
--        
-+
-         Args:
-             name (string): name of toolbar to be added
--            
-+
-             toolbar (.. :class:`Gtk.Toolbar`): Gtk.Toolbar to be appended to this toolbox
-         '''
-         label = Gtk.Label(label=name)
-@@ -106,7 +106,7 @@ def add_toolbar(self, name, toolbar):
-     def remove_toolbar(self, index):
-         '''
-         Removes toolbar at the index specified.
--        
-+
-         Args:
-             index (int): index of the toolbar to be removed
-         '''
-@@ -118,9 +118,9 @@ def remove_toolbar(self, index):
- 
-     def set_current_toolbar(self, index):
-         '''
--        Sets the current toolbar to that of the index specified and 
-+        Sets the current toolbar to that of the index specified and
-         displays it.
--        
-+
-         Args:
-             index (int): index of toolbar to be set as current toolbar
-         '''
-diff --git a/src/sugar3/graphics/toolbutton.py b/src/sugar3/graphics/toolbutton.py
-index e8f4e9ed2..8b0031f77 100644
---- a/src/sugar3/graphics/toolbutton.py
-+++ b/src/sugar3/graphics/toolbutton.py
-@@ -27,7 +27,7 @@
-         from sugar3.graphics.toolbutton import ToolButton
- 
-         def __clicked_cb(button):
--            print "tool button was clicked"
-+            print("tool button was clicked")
- 
-         w = Gtk.Window()
-         w.connect('destroy', Gtk.main_quit)
-diff --git a/src/sugar3/graphics/tray.py b/src/sugar3/graphics/tray.py
-index da8dda99c..55db31afe 100644
---- a/src/sugar3/graphics/tray.py
-+++ b/src/sugar3/graphics/tray.py
-@@ -330,6 +330,8 @@ def get_item_index(self, item):
- 
-     def scroll_to_item(self, item):
-         self._viewport.scroll_to_item(item)
-+
-+
- if hasattr(HTray, 'set_css_name'):
-     HTray.set_css_name('htray')
- 
-@@ -424,6 +426,8 @@ def get_item_index(self, item):
- 
-     def scroll_to_item(self, item):
-         self._viewport.scroll_to_item(item)
-+
-+
- if hasattr(VTray, 'set_css_name'):
-     VTray.set_css_name('VTray')
- 
-diff --git a/src/sugar3/graphics/xocolor.py b/src/sugar3/graphics/xocolor.py
-index 4c6e33a87..e5d7e4e85 100644
---- a/src/sugar3/graphics/xocolor.py
-+++ b/src/sugar3/graphics/xocolor.py
-@@ -20,6 +20,7 @@
- Each pair of colors represents the fill color and the stroke color
- '''
- 
-+import six
- import random
- import logging
- 
-@@ -210,11 +211,11 @@
- def _parse_string(color_string):
-     '''
-     Returns array of length 2 of two colors in standard html form of [stroke color, fill color]
--    
-+
-     Args:
-         color_string (string): two html format strings separated by a comma
-     '''
--    if not isinstance(color_string, (str, unicode)):
-+    if not isinstance(color_string, (six.text_type, six.binary_type)):
-         logging.error('Invalid color string: %r', color_string)
-         return None
- 
-@@ -233,13 +234,14 @@ def _parse_string(color_string):
- class XoColor:
-     '''
-     Defines color for XO
--    
-+
-     Args:
--        color_string (string): two html format strings separated 
--		    by a comma, "white", or "insensitive". If color_string 
--		    is None, the user's color will be created. If parsed_color 
--		    cannot be created, a random color will be used
-+        color_string (string): two html format strings separated
-+                    by a comma, "white", or "insensitive". If color_string
-+                    is None, the user's color will be created. If parsed_color
-+                    cannot be created, a random color will be used
-     '''
-+
-     def __init__(self, color_string=None):
-         parsed_color = None
- 
-@@ -261,7 +263,7 @@ def __cmp__(self, other):
-         '''
-         Compares two XO colors by their stroke and fill color
-         Returns 0 if they are equal and -1 if they are unequal
--        
-+
-         Args:
-             other (object): other XO color to compare
-         '''
-@@ -295,12 +297,12 @@ def to_string(self):
- 
-     f = open(sys.argv[1], 'r')
- 
--    print 'colors = ['
-+    print('colors = [')
- 
-     for line in f.readlines():
-         match = re.match(r'fill: ([A-Z0-9]*) stroke: ([A-Z0-9]*)', line)
--        print "['#%s', '#%s'], \\" % (match.group(2), match.group(1))
-+        print("['#%s', '#%s'], \\" % (match.group(2), match.group(1)))
- 
--    print ']'
-+    print(']')
- 
-     f.close()
-diff --git a/src/sugar3/logger.py b/src/sugar3/logger.py
-index 2e01b1cb4..43ef5b56c 100644
---- a/src/sugar3/logger.py
-+++ b/src/sugar3/logger.py
-@@ -20,16 +20,17 @@
- STABLE.
- """
- 
-+import six
- import array
- import collections
- import errno
- import logging
- import sys
- import os
--import repr as repr_
- import decorator
- import time
- 
-+from six.moves import reprlib as repr_
- from sugar3 import env
- 
- # Let's keep this module self contained so that it can be easily
-@@ -104,8 +105,8 @@ def cleanup():
-             for f in os.listdir(root):
-                 os.remove(os.path.join(root, f))
-             os.rmdir(root)
--        except OSError, e:
--            print "Could not remove old logs files %s" % e
-+        except OSError as e:
-+            print("Could not remove old logs files %s" % e)
- 
-     if len(backup_logs) > 0:
-         name = str(int(time.time()))
-@@ -116,7 +117,7 @@ def cleanup():
-                 source_path = os.path.join(logs_dir, log)
-                 dest_path = os.path.join(backup_dir, log)
-                 os.rename(source_path, dest_path)
--        except OSError, e:
-+        except OSError as e:
-             # gracefully deal w/ disk full
-             if e.errno != errno.ENOSPC:
-                 raise e
-@@ -145,7 +146,7 @@ def __init__(self, stream):
-         def write(self, s):
-             try:
-                 self._stream.write(s)
--            except IOError, e:
-+            except IOError as e:
-                 # gracefully deal w/ disk full
-                 if e.errno != errno.ENOSPC:
-                     raise e
-@@ -153,7 +154,7 @@ def write(self, s):
-         def flush(self):
-             try:
-                 self._stream.flush()
--            except IOError, e:
-+            except IOError as e:
-                 # gracefully deal w/ disk full
-                 if e.errno != errno.ENOSPC:
-                     raise e
-@@ -177,7 +178,7 @@ def flush(self):
- 
-             sys.stdout = SafeLogWrapper(sys.stdout)
-             sys.stderr = SafeLogWrapper(sys.stderr)
--        except OSError, e:
-+        except OSError as e:
-             # if we're out of space, just continue
-             if e.errno != errno.ENOSPC:
-                 raise e
-@@ -188,13 +189,15 @@ def flush(self):
- class TraceRepr(repr_.Repr):
- 
-     # better handling of subclasses of basic types, e.g. for DBus
--    _TYPES = [int, long, bool, tuple, list, array.array, set, frozenset,
-+    _TYPES = [int, bool, tuple, list, array.array, set, frozenset,
-               collections.deque, dict, str]
-+    if six.PY2:
-+        _TYPES.append(long)
- 
-     def repr1(self, x, level):
-         for t in self._TYPES:
-             if isinstance(x, t):
--                return getattr(self, 'repr_'+t.__name__)(x, level)
-+                return getattr(self, 'repr_' + t.__name__)(x, level)
- 
-         return repr_.Repr.repr1(self, x, level)
- 
-@@ -232,14 +235,14 @@ def _trace(f, *args, **kwargs):
-             [trace_repr.repr(a)
-                 for (idx, a) in enumerate(args) if idx not in skip_args] +
-             ['%s=%s' % (k, trace_repr.repr(v))
--                for (k, v) in kwargs.items() if k not in skip_kwargs])
-+                for (k, v) in list(kwargs.items()) if k not in skip_kwargs])
- 
-         trace_logger.log(TRACE, "%s(%s) invoked", f.__name__,
-                          params_formatted)
- 
-         try:
-             res = f(*args, **kwargs)
--        except:
-+        except BaseException:
-             trace_logger.exception("Exception occurred in %s" % f.__name__)
-             raise
- 
-diff --git a/src/sugar3/mime.py b/src/sugar3/mime.py
-index cd30fa70d..a2797d0d0 100644
---- a/src/sugar3/mime.py
-+++ b/src/sugar3/mime.py
-@@ -32,7 +32,9 @@
- from gi.repository import GdkPixbuf
- from gi.repository import Gio
- 
--_ = lambda msg: gettext.dgettext('sugar-toolkit-gtk3', msg)
-+
-+def _(msg): return gettext.dgettext('sugar-toolkit-gtk3', msg)
-+
- 
- GENERIC_TYPE_TEXT = 'Text'
- GENERIC_TYPE_IMAGE = 'Image'
-@@ -185,14 +187,14 @@ def get_mime_parents(mime_type):
-             with open(subclasses_path) as parents_file:
-                 for line in parents_file:
-                     subclass, parent = line.split()
--                    if subclass not in _subclasses.keys():
-+                    if subclass not in list(_subclasses.keys()):
-                         _subclasses[subclass] = [parent]
-                     else:
-                         _subclasses[subclass].append(parent)
- 
-         _subclasses_timestamps = timestamps
- 
--    if mime_type in _subclasses.keys():
-+    if mime_type in list(_subclasses.keys()):
-         return _subclasses[mime_type]
-     else:
-         return []
-diff --git a/src/sugar3/network.py b/src/sugar3/network.py
-index 771a3caed..6afa4f8a8 100644
---- a/src/sugar3/network.py
-+++ b/src/sugar3/network.py
-@@ -21,14 +21,14 @@
- 
- import os
- import threading
--import urllib
-+from six.moves import urllib
- import fcntl
- import tempfile
- 
- from gi.repository import GObject
- from gi.repository import GLib
--import SimpleHTTPServer
--import SocketServer
-+from six.moves import SimpleHTTPServer
-+from six.moves import socketserver
- 
- 
- __authinfos = {}
-@@ -46,7 +46,7 @@ def _del_authinfo():
-     del __authinfos[threading.currentThread()]
- 
- 
--class GlibTCPServer(SocketServer.TCPServer):
-+class GlibTCPServer(socketserver.TCPServer):
-     """GlibTCPServer
- 
-     Integrate socket accept into glib mainloop.
-@@ -56,7 +56,7 @@ class GlibTCPServer(SocketServer.TCPServer):
-     request_queue_size = 20
- 
-     def __init__(self, server_address, RequestHandlerClass):
--        SocketServer.TCPServer.__init__(self, server_address,
-+        socketserver.TCPServer.__init__(self, server_address,
-                                         RequestHandlerClass)
-         self.socket.setblocking(0)  # Set nonblocking
- 
-@@ -212,7 +212,7 @@ def __init__(self, url, destdir=None):
-         GObject.GObject.__init__(self)
- 
-     def start(self, destfile=None, destfd=None):
--        self._info = urllib.urlopen(self._url)
-+        self._info = urllib.request.urlopen(self._url)
-         self._outf = None
-         self._fname = None
-         if destfd and not destfile:
-@@ -226,14 +226,14 @@ def start(self, destfile=None, destfd=None):
-                 self._outf = destfd
-             else:
-                 self._outf = os.open(self._fname, os.O_RDWR |
--                                     os.O_TRUNC | os.O_CREAT, 0644)
-+                                     os.O_TRUNC | os.O_CREAT, 0o644)
-         else:
-             fname = self._get_filename_from_headers(self._info.headers)
-             self._suggested_fname = fname
--            garbage_, path = urllib.splittype(self._url)
--            garbage_, path = urllib.splithost(path or "")
--            path, garbage_ = urllib.splitquery(path or "")
--            path, garbage_ = urllib.splitattr(path or "")
-+            garbage_, path = urllib.parse.splittype(self._url)
-+            garbage_, path = urllib.parse.splithost(path or "")
-+            path, garbage_ = urllib.parse.splitquery(path or "")
-+            path, garbage_ = urllib.parse.splitattr(path or "")
-             suffix = os.path.splitext(path)[1]
-             (self._outf, self._fname) = tempfile.mkstemp(suffix=suffix,
-                                                          dir=self._destdir)
-@@ -291,7 +291,7 @@ def _read_next_chunk(self, source, condition):
-                 self.cleanup()
-                 self.emit('finished', self._fname, self._suggested_fname)
-                 return False
--        except Exception, err:
-+        except Exception as err:
-             self.cleanup(remove=True)
-             self.emit('error', 'Error downloading file: %r' % err)
-             return False
-diff --git a/src/sugar3/presence/activity.py b/src/sugar3/presence/activity.py
-index f07aab827..6097b8379 100644
---- a/src/sugar3/presence/activity.py
-+++ b/src/sugar3/presence/activity.py
-@@ -21,6 +21,7 @@
- STABLE.
- """
- 
-+import six
- import logging
- from functools import partial
- 
-@@ -124,6 +125,11 @@ def __init__(self, account_path, connection, room_handle=None,
- 
-     def _start_tracking_properties(self):
-         bus = dbus.SessionBus()
-+        arg_dict = dict(reply_handler=self.__got_properties_cb,
-+                        error_handler=self.__error_handler_cb)
-+        if six.PY2:
-+            arg_dict = arg_dict.update(utf8_strings=True)
-+
-         self._get_properties_call = bus.call_async(
-             self.telepathy_conn.requested_bus_name,
-             self.telepathy_conn.object_path,
-@@ -131,9 +137,7 @@ def _start_tracking_properties(self):
-             'GetProperties',
-             'u',
-             (self.room_handle,),
--            reply_handler=self.__got_properties_cb,
--            error_handler=self.__error_handler_cb,
--            utf8_strings=True)
-+            arg_dict)
- 
-         # As only one Activity instance is needed per activity process,
-         # we can afford listening to ActivityPropertiesChanged like this.
-@@ -144,7 +148,7 @@ def _start_tracking_properties(self):
- 
-     def __activity_properties_changed_cb(self, room_handle, properties):
-         _logger.debug('%r: Activity properties changed to %r' % (self,
--                      properties))
-+                                                                 properties))
-         self._update_properties(properties)
- 
-     def __got_properties_cb(self, properties):
-@@ -239,7 +243,7 @@ def get_joined_buddies(self):
-         returns list of presence Buddy objects that we can successfully
-         create from the buddy object paths that PS has for this activity.
-         """
--        return self._joined_buddies.values()
-+        return list(self._joined_buddies.values())
- 
-     def get_buddy_by_handle(self, handle):
-         """Retrieve the Buddy object given a telepathy handle.
-@@ -300,8 +304,9 @@ def _start_tracking_channel(self):
-         channel.connect_to_signal('Closed', self.__text_channel_closed_cb)
- 
-     def __get_all_members_cb(self, members, local_pending, remote_pending):
--        _logger.debug('__get_all_members_cb %r %r' % (members,
--                      self._text_channel_group_flags))
-+        _logger.debug(
-+            '__get_all_members_cb %r %r' %
-+            (members, self._text_channel_group_flags))
-         if self._channel_self_handle in members:
-             members.remove(self._channel_self_handle)
- 
-@@ -631,8 +636,9 @@ def _tubes_ready(self):
-         self._add_self_to_channel()
- 
-     def __text_channel_group_flags_changed_cb(self, added, removed):
--        _logger.debug('__text_channel_group_flags_changed_cb %r %r' % (added,
--                      removed))
-+        _logger.debug(
-+            '__text_channel_group_flags_changed_cb %r %r' %
-+            (added, removed))
-         self.text_channel_group_flags |= added
-         self.text_channel_group_flags &= ~removed
- 
-diff --git a/src/sugar3/presence/buddy.py b/src/sugar3/presence/buddy.py
-index 6f85ae894..2e0190c00 100644
---- a/src/sugar3/presence/buddy.py
-+++ b/src/sugar3/presence/buddy.py
-@@ -22,7 +22,7 @@
- """
- 
- import logging
--
-+import six
- from gi.repository import GObject
- import dbus
- from telepathy.interfaces import CONNECTION, \
-@@ -103,7 +103,7 @@ def set_color(self, color):
-     def get_current_activity(self):
-         if self._current_activity is None:
-             return None
--        for activity in self._activities.values():
-+        for activity in list(self._activities.values()):
-             if activity.props.id == self._current_activity:
-                 return activity
-         return None
-@@ -164,6 +164,12 @@ def __init__(self, account_path, contact_id):
-                                      dbus_interface=CONNECTION)
-         self.contact_handle = handles[0]
- 
-+        arg_dict = dict(reply_handler=self.__got_properties_cb,
-+                        error_handler=self.__error_handler_cb,
-+                        byte_arrays = True)
-+        if six.PY2:
-+            arg_dict = arg_dict.update(utf8_strings=True)
-+
-         self._get_properties_call = bus.call_async(
-             connection_name,
-             connection.object_path,
-@@ -171,10 +177,7 @@ def __init__(self, account_path, contact_id):
-             'GetProperties',
-             'u',
-             (self.contact_handle,),
--            reply_handler=self.__got_properties_cb,
--            error_handler=self.__error_handler_cb,
--            utf8_strings=True,
--            byte_arrays=True)
-+            arg_dict)
- 
-         self._get_attributes_call = bus.call_async(
-             connection_name,
-@@ -245,4 +248,3 @@ def __init__(self):
- 
-         self.props.nick = get_nick_name()
-         self.props.color = get_color().to_string()
--
-diff --git a/src/sugar3/presence/connectionmanager.py b/src/sugar3/presence/connectionmanager.py
-index 325c200cc..dbb957caf 100644
---- a/src/sugar3/presence/connectionmanager.py
-+++ b/src/sugar3/presence/connectionmanager.py
-@@ -92,7 +92,8 @@ def __status_changed_cb(self, account_path, status, reason):
- 
-     def get_preferred_connection(self):
-         best_connection = None, None
--        for account_path, connection in self._connections_per_account.items():
-+        for account_path, connection in list(
-+                self._connections_per_account.items()):
-             if 'salut' in account_path and connection.connected:
-                 best_connection = account_path, connection.connection
-             elif 'gabble' in account_path and connection.connected:
-@@ -107,7 +108,8 @@ def get_connections_per_account(self):
-         return self._connections_per_account
- 
-     def get_account_for_connection(self, connection_path):
--        for account_path, connection in self._connections_per_account.items():
-+        for account_path, connection in list(
-+                self._connections_per_account.items()):
-             if connection.connection.object_path == connection_path:
-                 return account_path
-         return None
-diff --git a/src/sugar3/presence/presenceservice.py b/src/sugar3/presence/presenceservice.py
-index 8b66b0e54..2f9f85104 100644
---- a/src/sugar3/presence/presenceservice.py
-+++ b/src/sugar3/presence/presenceservice.py
-@@ -78,7 +78,8 @@ def get_activity(self, activity_id, warn_if_none=True):
-             connection_manager = get_connection_manager()
-             connections_per_account = \
-                 connection_manager.get_connections_per_account()
--            for account_path, connection in connections_per_account.items():
-+            for account_path, connection in list(
-+                    connections_per_account.items()):
-                 if not connection.connected:
-                     continue
-                 logging.debug('Calling GetActivity on %s' % account_path)
-@@ -86,13 +87,13 @@ def get_activity(self, activity_id, warn_if_none=True):
-                     room_handle = connection.connection.GetActivity(
-                         activity_id,
-                         dbus_interface=CONN_INTERFACE_ACTIVITY_PROPERTIES)
--                except dbus.exceptions.DBusException, e:
-+                except dbus.exceptions.DBusException as e:
-                     name = 'org.freedesktop.Telepathy.Error.NotAvailable'
-                     if e.get_dbus_name() == name:
-                         logging.debug("There's no shared activity with the id "
-                                       "%s" % activity_id)
-                     elif e.get_dbus_name() == \
--                         'org.freedesktop.DBus.Error.UnknownMethod':
-+                            'org.freedesktop.DBus.Error.UnknownMethod':
-                         logging.warning(
-                             'Telepathy Account %r does not support '
-                             'Sugar collaboration', account_path)
-diff --git a/src/sugar3/presence/tubeconn.py b/src/sugar3/presence/tubeconn.py
-index 9a496d984..8b1e2ff92 100644
---- a/src/sugar3/presence/tubeconn.py
-+++ b/src/sugar3/presence/tubeconn.py
-@@ -24,6 +24,7 @@
- __docformat__ = 'reStructuredText'
- 
- 
-+import six
- import logging
- 
- from dbus.connection import Connection
-@@ -77,7 +78,9 @@ def _on_get_self_handle_error(self, e):
- 
-     def close(self):
-         self._dbus_names_changed_match.remove()
--        self._on_dbus_names_changed(self.tube_id, (), self.participants.keys())
-+        self._on_dbus_names_changed(
-+            self.tube_id, (), list(
-+                self.participants.keys()))
-         super(TubeConnection, self).close()
- 
-     def _on_get_dbus_names_reply(self, names):
-@@ -111,6 +114,6 @@ def watch_participants(self, callback):
-             # GetDBusNames already returned: fake a participant add event
-             # immediately
-             added = []
--            for k, v in self.participants.iteritems():
-+            for k, v in six.iteritems(self.participants):
-                 added.append((k, v))
-             callback(added, [])
-diff --git a/src/sugar3/profile.py b/src/sugar3/profile.py
-index 818afe05a..e46943fde 100644
---- a/src/sugar3/profile.py
-+++ b/src/sugar3/profile.py
-@@ -21,7 +21,8 @@
- from gi.repository import Gio
- import os
- import logging
--from ConfigParser import ConfigParser
-+
-+from six.moves.configparser import ConfigParser
- 
- from sugar3 import env
- from sugar3 import util
-diff --git a/src/sugar3/speech.py b/src/sugar3/speech.py
-index fd2c5b5f7..19416043e 100644
---- a/src/sugar3/speech.py
-+++ b/src/sugar3/speech.py
-@@ -33,7 +33,7 @@
-     from gi.repository import Gst
-     Gst.init(None)
-     Gst.parse_launch('espeak')
--except:
-+except BaseException:
-     logging.error('Gst or the espeak plugin is not installed in the system.')
-     _HAS_GST = False
- 
-@@ -268,7 +268,12 @@ def say_text(self, text, pitch=None, rate=None, lang_code=None):
-         else:
-             voice_name = self._player.get_all_voices()[lang_code]
-         if text:
--            logging.error('PLAYING %r lang %r pitch %r rate %r', text, voice_name, pitch, rate)
-+            logging.error(
-+                'PLAYING %r lang %r pitch %r rate %r',
-+                text,
-+                voice_name,
-+                pitch,
-+                rate)
-             self._player.speak(pitch, rate, voice_name, text)
- 
-     def say_selected_text(self):
-diff --git a/src/sugar3/test/Makefile.am b/src/sugar3/test/Makefile.am
-index 2ccac0223..6d77c2fc6 100644
---- a/src/sugar3/test/Makefile.am
-+++ b/src/sugar3/test/Makefile.am
-@@ -3,4 +3,4 @@ sugar_PYTHON = \
- 	__init__.py \
-     discover.py \
- 	uitree.py \
--	unittest.py
-+	_unittest.py
-diff --git a/src/sugar3/test/unittest.py b/src/sugar3/test/_unittest.py
-similarity index 93%
-rename from src/sugar3/test/unittest.py
-rename to src/sugar3/test/_unittest.py
-index d3e65ee24..6c51d7ee6 100644
---- a/src/sugar3/test/unittest.py
-+++ b/src/sugar3/test/_unittest.py
-@@ -18,8 +18,6 @@
- UNSTABLE.
- """
- 
--from __future__ import absolute_import
--
- import logging
- import os
- import unittest
-@@ -52,11 +50,11 @@ def tearDown(self):
-     @contextmanager
-     def run_view(self, name):
-         view_path = os.path.join("views", "%s.py" % name)
--        process = subprocess.Popen(["python", view_path])
-+        process = subprocess.Popen(["python3", view_path])
- 
-         try:
-             yield
--        except:
-+        except BaseException:
-             logging.debug(uitree.get_root().dump())
-             raise
-         finally:
-@@ -77,12 +75,12 @@ def _run_activity(self, options=None):
-                 cmd += options
-             process = subprocess.Popen(cmd)
-         else:
--            print "No bundle_id specified."
-+            print("No bundle_id specified.")
-             return
- 
-         try:
-             yield
--        except:
-+        except BaseException:
-             logging.debug(uitree.get_root().dump())
-             raise
-         finally:
-diff --git a/src/sugar3/test/discover.py b/src/sugar3/test/discover.py
-index 8de28f8c7..dbf898d1d 100644
---- a/src/sugar3/test/discover.py
-+++ b/src/sugar3/test/discover.py
-@@ -18,8 +18,6 @@
- UNSTABLE.
- """
- 
--from __future__ import absolute_import
--
- import argparse
- import sys
- import os
-diff --git a/src/sugar3/test/uitree.py b/src/sugar3/test/uitree.py
-index a4e8e7109..3da33c1c2 100644
---- a/src/sugar3/test/uitree.py
-+++ b/src/sugar3/test/uitree.py
-@@ -44,7 +44,7 @@ def wrapped(*args, **kwargs):
- 
-             try:
-                 result = func(*args, **kwargs)
--            except GLib.GError, e:
-+            except GLib.GError as e:
-                 # The application is not responding, try again
-                 if e.code == Atspi.Error.IPC:
-                     continue
-diff --git a/src/sugar3/util.py b/src/sugar3/util.py
-index f44f250cb..45cbd3688 100644
---- a/src/sugar3/util.py
-+++ b/src/sugar3/util.py
-@@ -20,6 +20,7 @@
- UNSTABLE. We have been adding helpers randomly to this module.
- """
- 
-+import six
- import os
- import time
- import hashlib
-@@ -31,20 +32,26 @@
- import atexit
- 
- 
--_ = lambda msg: gettext.dgettext('sugar-toolkit-gtk3', msg)
-+def _(msg): return gettext.dgettext('sugar-toolkit-gtk3', msg)
- 
- 
- def printable_hash(in_hash):
-     """Convert binary hash data into printable characters."""
-     printable = ""
-     for char in in_hash:
--        printable = printable + binascii.b2a_hex(char)
-+        if six.PY3:
-+            char = bytes([char])
-+            printable = printable + binascii.b2a_hex(char).decode()
-+        else:
-+            printable = printable + binascii.b2a_hex(char)
-     return printable
- 
- 
- def sha_data(data):
-     """sha1 hash some bytes."""
-     sha_hash = hashlib.sha1()
-+    if six.PY3:
-+        data = data.encode('utf-8')
-     sha_hash.update(data)
-     return sha_hash.digest()
- 
-@@ -81,7 +88,7 @@ def is_hex(s):
- 
- def validate_activity_id(actid):
-     """Validate an activity ID."""
--    if not isinstance(actid, (str, unicode)):
-+    if not isinstance(actid, (six.binary_type, six.text_type)):
-         return False
-     if len(actid) != ACTIVITY_ID_LEN:
-         return False
-@@ -204,7 +211,7 @@ def itervalues(self):
-             yield j
- 
-     def keys(self):
--        return self.d.keys()
-+        return list(self.d.keys())
- 
- 
- units = [['%d year', '%d years', 356 * 24 * 60 * 60],
-@@ -331,13 +338,14 @@ def __del__(self):
- 
- def _cleanup_temp_files():
-     logging.debug('_cleanup_temp_files')
--    for path in _tracked_paths.keys():
-+    for path in list(_tracked_paths.keys()):
-         try:
-             os.unlink(path)
--        except:
-+        except BaseException:
-             # pylint: disable=W0702
-             logging.exception('Exception occurred in _cleanup_temp_files')
- 
-+
- atexit.register(_cleanup_temp_files)
- 
- 


More information about the arch-commits mailing list