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/doc/conf.py b/doc/conf.py
index 7faa8b40b..7891c2d28 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -226,25 +226,25 @@
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
-# The paper size ('letterpaper' or 'a4paper').
-#'papersize': 'letterpaper',
+ # The paper size ('letterpaper' or 'a4paper').
+ #'papersize': 'letterpaper',
-# The font size ('10pt', '11pt' or '12pt').
-#'pointsize': '10pt',
+ # The font size ('10pt', '11pt' or '12pt').
+ #'pointsize': '10pt',
-# Additional stuff for the LaTeX preamble.
-#'preamble': '',
+ # Additional stuff for the LaTeX preamble.
+ #'preamble': '',
-# Latex figure (float) alignment
-#'figure_align': 'htbp',
+ # Latex figure (float) alignment
+ #'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
- (master_doc, 'SugarToolkitGTK3.tex', u'Sugar Toolkit GTK3 Documentation',
- u'Sugar Labs', 'manual'),
+ (master_doc, 'SugarToolkitGTK3.tex', u'Sugar Toolkit GTK3 Documentation',
+ u'Sugar Labs', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
@@ -287,9 +287,9 @@
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
- (master_doc, 'SugarToolkitGTK3', u'Sugar Toolkit GTK3 Documentation',
- author, 'SugarToolkitGTK3', 'One line description of project.',
- 'Miscellaneous'),
+ (master_doc, 'SugarToolkitGTK3', u'Sugar Toolkit GTK3 Documentation',
+ author, 'SugarToolkitGTK3', 'One line description of project.',
+ 'Miscellaneous'),
]
# Documents to append as an appendix to all manuals.
diff --git a/examples/alert.py b/examples/alert.py
index f629a2ff3..f0425cb56 100644
--- a/examples/alert.py
+++ b/examples/alert.py
@@ -21,11 +21,11 @@
# Called when an alert object throws a response event.
def __alert_response_cb(alert, response_id):
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')
alert.connect('response', __alert_response_cb)
diff --git a/examples/animator.py b/examples/animator.py
index dcdad0e53..c6ede6fa8 100644
--- a/examples/animator.py
+++ b/examples/animator.py
@@ -23,7 +23,7 @@ def next_frame(self, current):
def __animation_completed_cb(anim):
- print 'Animation completed'
+ print('Animation completed')
w = Gtk.Window()
diff --git a/examples/colorbutton.py b/examples/colorbutton.py
index 2e4fa4b88..09db06838 100644
--- a/examples/colorbutton.py
+++ b/examples/colorbutton.py
@@ -24,7 +24,7 @@
def color_changed_cb(button, pspec):
- print button.get_color()
+ print(button.get_color())
color_button = ColorToolButton()
diff --git a/examples/combobox.py b/examples/combobox.py
index 61bf11c9e..6d1c1a066 100644
--- a/examples/combobox.py
+++ b/examples/combobox.py
@@ -6,7 +6,7 @@
def __combo_changed_cb(combo):
- print 'Combo changed to %r' % combo.get_value()
+ print('Combo changed to %r' % combo.get_value())
w = Gtk.Window()
diff --git a/examples/customdestroy.py b/examples/customdestroy.py
index 723e28c63..fc33eb9e8 100644
--- a/examples/customdestroy.py
+++ b/examples/customdestroy.py
@@ -13,14 +13,14 @@ def __init__(self):
Gtk.CellRenderer.__init__(self)
def __del__(self):
- print "cellrenderer destroy"
+ print("cellrenderer destroy")
def do_render(self, cairo_t, widget, background_area, cell_area, flags):
pass
def window_destroy_cb(*kwargs):
- print "window destroy"
+ print("window destroy")
Gtk.main_quit()
@@ -30,7 +30,7 @@ def window_destroy_cb(*kwargs):
def treeview_destroy_cb(*kwargs):
- print "treeview destroy"
+ print("treeview destroy")
treeview = Gtk.TreeView()
diff --git a/examples/gtktreesensitive.py b/examples/gtktreesensitive.py
index 15b8856ad..58fea9973 100644
--- a/examples/gtktreesensitive.py
+++ b/examples/gtktreesensitive.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/python3
from gi.repository import Gtk
import common
diff --git a/examples/iconentry.py b/examples/iconentry.py
index f5e4ea518..e35c60122 100644
--- a/examples/iconentry.py
+++ b/examples/iconentry.py
@@ -6,11 +6,11 @@
def __go_next_cb(entry, icon_pos, data=None):
- print 'Go next'
+ print('Go next')
def __entry_activate_cb(widget, data=None):
- print 'Entry activate'
+ print('Entry activate')
w = Gtk.Window()
diff --git a/examples/radiotoolbutton.py b/examples/radiotoolbutton.py
index 08a92a47d..5d0b5472f 100644
--- a/examples/radiotoolbutton.py
+++ b/examples/radiotoolbutton.py
@@ -19,7 +19,7 @@
def echo(button, label):
if not button.props.active:
return
- print label
+ print(label)
palette = RadioPalette()
diff --git a/examples/scrollingdetector.py b/examples/scrollingdetector.py
index e3ba9f5be..df0c70a0c 100644
--- a/examples/scrollingdetector.py
+++ b/examples/scrollingdetector.py
@@ -12,12 +12,12 @@
def _scroll_start_cb(event, treeview, invoker):
- print "Scroll starts"
+ print("Scroll starts")
invoker.detach()
def _scroll_end_cb(event, treeview, invoker):
- print "Scroll ends"
+ print("Scroll ends")
invoker.attach_treeview(treeview)
@@ -30,7 +30,7 @@ def _scroll_end_cb(event, treeview, invoker):
iconlist = os.listdir(os.path.join(data_dir,
'share/icons/sugar/scalable/actions/'))
-print "Displaying %s icons" % len(iconlist)
+print("Displaying %s icons" % len(iconlist))
for icon in iconlist:
icon = os.path.basename(icon)
icon = icon[:icon.find('.')]
diff --git a/examples/tabs.py b/examples/tabs.py
index 47b1b45d8..7a8b52fcd 100644
--- a/examples/tabs.py
+++ b/examples/tabs.py
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/python3
from gi.repository import Gtk
from sugar3.graphics.icon import Icon
diff --git a/examples/toolbutton.py b/examples/toolbutton.py
index aa4a85fe7..c4a26491d 100644
--- a/examples/toolbutton.py
+++ b/examples/toolbutton.py
@@ -21,7 +21,7 @@
def __clicked_cb(button):
n = int(button.get_tooltip())
button.set_tooltip(str(n + 1))
- print "tool button click count %d" % n
+ print("tool button click count %d" % n)
tool_button = ToolButton(icon_name='view-radial', tooltip='0')
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('%s' % 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 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, value)
icon = re.sub('' % 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('%s' % 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)
diff --git a/tests/data/sample.activity/activity/activity.info b/tests/data/sample.activity/activity/activity.info
index 4cf7f755c..820c07a3c 100644
--- a/tests/data/sample.activity/activity/activity.info
+++ b/tests/data/sample.activity/activity/activity.info
@@ -2,6 +2,6 @@
name = Sample
activity_version = 1
bundle_id = org.sugarlabs.Sample
-exec = sugar-activity activity.SampleActivity
+exec = sugar-activity3 activity.SampleActivity
icon = activity-sample
license = GPLv2+
diff --git a/tests/data/sample.activity/setup.py b/tests/data/sample.activity/setup.py
index 9483050ba..191204bda 100755
--- a/tests/data/sample.activity/setup.py
+++ b/tests/data/sample.activity/setup.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python2
+#!/usr/bin/env python3
from sugar3.activity import bundlebuilder
diff --git a/tests/graphics/progressicon.py b/tests/graphics/progressicon.py
index a922749c1..b756d2fa9 100644
--- a/tests/graphics/progressicon.py
+++ b/tests/graphics/progressicon.py
@@ -59,6 +59,7 @@ def timeout_cb():
return False
return True
+
GLib.timeout_add(50, timeout_cb)
if __name__ == '__main__':
diff --git a/tests/test_mime.py b/tests/test_mime.py
index 01896e3c9..9e478de89 100644
--- a/tests/test_mime.py
+++ b/tests/test_mime.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python2
+#!/usr/bin/env python3
# Copyright (C) 2006, Red Hat, Inc.
# Copyright (C) 2007, One Laptop Per Child
diff --git a/tests/test_uitree.py b/tests/test_uitree.py
index 9a3f63b19..b7c7da49c 100644
--- a/tests/test_uitree.py
+++ b/tests/test_uitree.py
@@ -23,7 +23,7 @@
class TestUITree(unittest.TestCase):
def test_tree(self):
- process = subprocess.Popen(["python", __file__, "show_window1"])
+ process = subprocess.Popen(["python3", __file__, "show_window1"])
try:
root = uitree.get_root()
@@ -49,5 +49,6 @@ def show_window1():
Gtk.main()
+
if __name__ == '__main__':
globals()[sys.argv[1]]()