Port from Python 2 to six

Signed-off-by: James Cameron <quozl@laptop.org>
master
Pro-Panda 6 years ago committed by James Cameron
parent 6345da8e07
commit aa8a5e70c4

@ -26,4 +26,4 @@ check-po:
test: check-po test: check-po
pyflakes $(top_srcdir) pyflakes $(top_srcdir)
pep8 $(top_srcdir) pep8 $(top_srcdir)
python -m sugar3.test.discover $(top_srcdir)/tests python3 -m sugar3.test.discover $(top_srcdir)/tests

@ -1 +1 @@
dist_bin_SCRIPTS = sugar-activity sugar-activity-web dist_bin_SCRIPTS = sugar-activity sugar-activity-web sugar-activity3

@ -1,219 +1,5 @@
#!/usr/bin/env python2 #!/usr/bin/env python2
# Copyright (C) 2006-2008, Red Hat, Inc. from sugar3.activity import activityinstance
#
# 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 activityinstance.main()
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()

@ -18,7 +18,7 @@
# Boston, MA 02111-1307, USA. # Boston, MA 02111-1307, USA.
if [ "$SUGAR_USE_WEBKIT1" = "yes" ]; then if [ "$SUGAR_USE_WEBKIT1" = "yes" ]; then
exec sugar-activity sugar3.activity.webkit1.WebActivity $@ exec sugar-activity3 sugar3.activity.webkit1.WebActivity $@
else else
exec sugar-activity sugar3.activity.webactivity.WebActivity $@ exec sugar-activity3 sugar3.activity.webactivity.WebActivity $@
fi fi

@ -0,0 +1,5 @@
#!/usr/bin/env python3
from sugar3.activity import activityinstance
activityinstance.main()

@ -15,7 +15,7 @@ GNOME_COMPILE_WARNINGS(maximum)
AC_PATH_PROG([GLIB_GENMARSHAL], [glib-genmarshal]) AC_PATH_PROG([GLIB_GENMARSHAL], [glib-genmarshal])
PYTHON=python2 PYTHON=python3
AM_PATH_PYTHON AM_PATH_PYTHON
PKG_CHECK_MODULES(EXT, gtk+-3.0 gdk-3.0 gdk-pixbuf-2.0 sm ice alsa PKG_CHECK_MODULES(EXT, gtk+-3.0 gdk-3.0 gdk-pixbuf-2.0 sm ice alsa

@ -226,25 +226,25 @@ htmlhelp_basename = 'SugarToolkitGTK3doc'
# -- Options for LaTeX output --------------------------------------------- # -- Options for LaTeX output ---------------------------------------------
latex_elements = { latex_elements = {
# The paper size ('letterpaper' or 'a4paper'). # The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper', #'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt'). # The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt', #'pointsize': '10pt',
# Additional stuff for the LaTeX preamble. # Additional stuff for the LaTeX preamble.
#'preamble': '', #'preamble': '',
# Latex figure (float) alignment # Latex figure (float) alignment
#'figure_align': 'htbp', #'figure_align': 'htbp',
} }
# Grouping the document tree into LaTeX files. List of tuples # Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, # (source start file, target name, title,
# author, documentclass [howto, manual, or own class]). # author, documentclass [howto, manual, or own class]).
latex_documents = [ latex_documents = [
(master_doc, 'SugarToolkitGTK3.tex', u'Sugar Toolkit GTK3 Documentation', (master_doc, 'SugarToolkitGTK3.tex', u'Sugar Toolkit GTK3 Documentation',
u'Sugar Labs', 'manual'), u'Sugar Labs', 'manual'),
] ]
# The name of an image file (relative to this directory) to place at the top of # The name of an image file (relative to this directory) to place at the top of
@ -287,9 +287,9 @@ man_pages = [
# (source start file, target name, title, author, # (source start file, target name, title, author,
# dir menu entry, description, category) # dir menu entry, description, category)
texinfo_documents = [ texinfo_documents = [
(master_doc, 'SugarToolkitGTK3', u'Sugar Toolkit GTK3 Documentation', (master_doc, 'SugarToolkitGTK3', u'Sugar Toolkit GTK3 Documentation',
author, 'SugarToolkitGTK3', 'One line description of project.', author, 'SugarToolkitGTK3', 'One line description of project.',
'Miscellaneous'), 'Miscellaneous'),
] ]
# Documents to append as an appendix to all manuals. # Documents to append as an appendix to all manuals.

@ -21,11 +21,11 @@ box.pack_start(alert, False, False, 0)
# Called when an alert object throws a response event. # Called when an alert object throws a response event.
def __alert_response_cb(alert, response_id): def __alert_response_cb(alert, response_id):
if response_id is Gtk.ResponseType.OK: if response_id is Gtk.ResponseType.OK:
print 'Continue Button was clicked.' print('Continue Button was clicked.')
elif response_id is Gtk.ResponseType.CANCEL: elif response_id is Gtk.ResponseType.CANCEL:
print 'Cancel Button was clicked.' print('Cancel Button was clicked.')
elif response_id == -1: elif response_id == -1:
print 'Timeout occurred' print('Timeout occurred')
alert.connect('response', __alert_response_cb) alert.connect('response', __alert_response_cb)

@ -23,7 +23,7 @@ class _Animation(animator.Animation):
def __animation_completed_cb(anim): def __animation_completed_cb(anim):
print 'Animation completed' print('Animation completed')
w = Gtk.Window() w = Gtk.Window()

@ -24,7 +24,7 @@ separator.show()
def color_changed_cb(button, pspec): def color_changed_cb(button, pspec):
print button.get_color() print(button.get_color())
color_button = ColorToolButton() color_button = ColorToolButton()

@ -6,7 +6,7 @@ set_theme()
def __combo_changed_cb(combo): def __combo_changed_cb(combo):
print 'Combo changed to %r' % combo.get_value() print('Combo changed to %r' % combo.get_value())
w = Gtk.Window() w = Gtk.Window()

@ -13,14 +13,14 @@ class MyCellRenderer(Gtk.CellRenderer):
Gtk.CellRenderer.__init__(self) Gtk.CellRenderer.__init__(self)
def __del__(self): def __del__(self):
print "cellrenderer destroy" print("cellrenderer destroy")
def do_render(self, cairo_t, widget, background_area, cell_area, flags): def do_render(self, cairo_t, widget, background_area, cell_area, flags):
pass pass
def window_destroy_cb(*kwargs): def window_destroy_cb(*kwargs):
print "window destroy" print("window destroy")
Gtk.main_quit() Gtk.main_quit()
@ -30,7 +30,7 @@ window.show()
def treeview_destroy_cb(*kwargs): def treeview_destroy_cb(*kwargs):
print "treeview destroy" print("treeview destroy")
treeview = Gtk.TreeView() treeview = Gtk.TreeView()

@ -1,4 +1,4 @@
#!/usr/bin/python #!/usr/bin/python3
from gi.repository import Gtk from gi.repository import Gtk
import common import common

@ -6,11 +6,11 @@ set_theme()
def __go_next_cb(entry, icon_pos, data=None): def __go_next_cb(entry, icon_pos, data=None):
print 'Go next' print('Go next')
def __entry_activate_cb(widget, data=None): def __entry_activate_cb(widget, data=None):
print 'Entry activate' print('Entry activate')
w = Gtk.Window() w = Gtk.Window()

@ -19,7 +19,7 @@ box.show()
def echo(button, label): def echo(button, label):
if not button.props.active: if not button.props.active:
return return
print label print(label)
palette = RadioPalette() palette = RadioPalette()

@ -12,12 +12,12 @@ import common
def _scroll_start_cb(event, treeview, invoker): def _scroll_start_cb(event, treeview, invoker):
print "Scroll starts" print("Scroll starts")
invoker.detach() invoker.detach()
def _scroll_end_cb(event, treeview, invoker): def _scroll_end_cb(event, treeview, invoker):
print "Scroll ends" print("Scroll ends")
invoker.attach_treeview(treeview) invoker.attach_treeview(treeview)
@ -30,7 +30,7 @@ data_dir = os.getenv('GTK_DATA_PREFIX', '/usr/')
iconlist = os.listdir(os.path.join(data_dir, iconlist = os.listdir(os.path.join(data_dir,
'share/icons/sugar/scalable/actions/')) 'share/icons/sugar/scalable/actions/'))
print "Displaying %s icons" % len(iconlist) print("Displaying %s icons" % len(iconlist))
for icon in iconlist: for icon in iconlist:
icon = os.path.basename(icon) icon = os.path.basename(icon)
icon = icon[:icon.find('.')] icon = icon[:icon.find('.')]

@ -1,4 +1,4 @@
#!/usr/bin/python #!/usr/bin/python3
from gi.repository import Gtk from gi.repository import Gtk
from sugar3.graphics.icon import Icon from sugar3.graphics.icon import Icon

@ -21,7 +21,7 @@ toolbar_box.toolbar.insert(separator, -1)
def __clicked_cb(button): def __clicked_cb(button):
n = int(button.get_tooltip()) n = int(button.get_tooltip())
button.set_tooltip(str(n + 1)) 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') tool_button = ToolButton(icon_name='view-radial', tooltip='0')

@ -2,6 +2,7 @@ sugardir = $(pythondir)/sugar3/activity
sugar_PYTHON = \ sugar_PYTHON = \
__init__.py \ __init__.py \
activity.py \ activity.py \
activityinstance.py \
activityfactory.py \ activityfactory.py \
activityhandle.py \ activityhandle.py \
activityservice.py \ activityservice.py \

@ -158,6 +158,7 @@ Hint: A good and simple activity to learn from is the Read activity.
You may copy it and use it as a template. You may copy it and use it as a template.
''' '''
import six
import gettext import gettext
import logging import logging
import os import os
@ -165,7 +166,6 @@ import signal
import time import time
from hashlib import sha1 from hashlib import sha1
from functools import partial from functools import partial
import StringIO
import cairo import cairo
import json import json
@ -206,7 +206,9 @@ from errno import EEXIST
from gi.repository import SugarExt 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_PRIVATE = 'private'
SCOPE_INVITE_ONLY = 'invite' # shouldn't be shown in UI, it's implicit SCOPE_INVITE_ONLY = 'invite' # shouldn't be shown in UI, it's implicit
@ -881,7 +883,7 @@ class Activity(Window, Gtk.Container):
cr.set_source_surface(screenshot_surface) cr.set_source_surface(screenshot_surface)
cr.paint() cr.paint()
preview_str = StringIO.StringIO() preview_str = six.BytesIO()
preview_surface.write_to_png(preview_str) preview_surface.write_to_png(preview_str)
return preview_str.getvalue() return preview_str.getvalue()
@ -919,7 +921,7 @@ class Activity(Window, Gtk.Container):
buddies_dict = self._get_buddies() buddies_dict = self._get_buddies()
if buddies_dict: 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()) self.metadata['buddies'] = json.dumps(self._get_buddies())
# update spent time before saving # update spent time before saving
@ -1103,8 +1105,9 @@ class Activity(Window, Gtk.Container):
raise RuntimeError('Activity %s already shared.' % raise RuntimeError('Activity %s already shared.' %
self._activity_id) self._activity_id)
verb = private and 'private' or 'public' verb = private and 'private' or 'public'
logging.debug('Requesting %s share of activity %s.' % (verb, logging.debug(
self._activity_id)) 'Requesting %s share of activity %s.' %
(verb, self._activity_id))
pservice = presenceservice.get_instance() pservice = presenceservice.get_instance()
pservice.connect('activity-shared', self.__share_cb) pservice.connect('activity-shared', self.__share_cb)
pservice.share_activity(self, private=private) pservice.share_activity(self, private=private)
@ -1197,8 +1200,8 @@ class Activity(Window, Gtk.Container):
if response_id == Gtk.ResponseType.OK: if response_id == Gtk.ResponseType.OK:
title = alert.entry.get_text() title = alert.entry.get_text()
if self._is_resumed and \ if self._is_resumed and \
title == self._original_title: title == self._original_title:
datastore.delete(self._jobject_old.get_object_id()) datastore.delete(self._jobject_old.get_object_id())
self._jobject.metadata['title'] = title self._jobject.metadata['title'] = title
self._do_close(False) self._do_close(False)
@ -1222,7 +1225,7 @@ class Activity(Window, Gtk.Container):
label = _('Save new') label = _('Save new')
tip = _('Save a new journal entry') tip = _('Save a new journal entry')
if self._is_resumed and \ if self._is_resumed and \
title == self._original_title: title == self._original_title:
label = _('Save') label = _('Save')
tip = _('Save into the old journal entry') tip = _('Save into the old journal entry')
@ -1244,7 +1247,7 @@ class Activity(Window, Gtk.Container):
if not skip_save: if not skip_save:
try: try:
self.save() self.save()
except: except BaseException:
# pylint: disable=W0702 # pylint: disable=W0702
logging.exception('Error saving activity object to datastore') logging.exception('Error saving activity object to datastore')
self._show_keep_failed_dialog() self._show_keep_failed_dialog()
@ -1306,12 +1309,12 @@ class Activity(Window, Gtk.Container):
def __realize_cb(self, window): def __realize_cb(self, window):
display_name = Gdk.Display.get_default().get_name() display_name = Gdk.Display.get_default().get_name()
if ':' in display_name: if ':' in display_name:
# X11 for sure; this only works in X11 # X11 for sure; this only works in X11
xid = window.get_window().get_xid() xid = window.get_window().get_xid()
SugarExt.wm_set_bundle_id(xid, self.get_bundle_id()) SugarExt.wm_set_bundle_id(xid, self.get_bundle_id())
SugarExt.wm_set_activity_id(xid, str(self._activity_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 # GTK3's HTML5 backend
# This is needed so that the window takes the whole browser window # This is needed so that the window takes the whole browser window
self.maximize() self.maximize()
@ -1432,7 +1435,7 @@ class _ClientHandler(dbus.service.Object, DBusProperties):
} }
filter_dict = dbus.Dictionary(filters, signature='sv') filter_dict = dbus.Dictionary(filters, signature='sv')
logging.debug('__get_filters_cb %r' % dbus.Array([filter_dict], logging.debug('__get_filters_cb %r' % dbus.Array([filter_dict],
signature='a{sv}')) signature='a{sv}'))
return dbus.Array([filter_dict], signature='a{sv}') return dbus.Array([filter_dict], signature='a{sv}')
@dbus.service.method(dbus_interface=CLIENT_HANDLER, @dbus.service.method(dbus_interface=CLIENT_HANDLER,
@ -1448,9 +1451,10 @@ class _ClientHandler(dbus.service.Object, DBusProperties):
handle_type = properties[CHANNEL + '.TargetHandleType'] handle_type = properties[CHANNEL + '.TargetHandleType']
if channel_type == CHANNEL_TYPE_TEXT: if channel_type == CHANNEL_TYPE_TEXT:
self._got_channel_cb(connection, object_path, handle_type) self._got_channel_cb(connection, object_path, handle_type)
except Exception, e: except Exception as e:
logging.exception(e) logging.exception(e)
_session = None _session = None
@ -1503,7 +1507,7 @@ def get_activity_root():
activity_root = env.get_profile_path(os.environ['SUGAR_BUNDLE_ID']) activity_root = env.get_profile_path(os.environ['SUGAR_BUNDLE_ID'])
try: try:
os.mkdir(activity_root) os.mkdir(activity_root)
except OSError, e: except OSError as e:
if e.errno != EEXIST: if e.errno != EEXIST:
raise e raise e
return activity_root return activity_root

@ -23,7 +23,6 @@ the moment there is no reason to stabilize this API.
""" """
import logging import logging
import dbus import dbus
from gi.repository import GObject from gi.repository import GObject
from gi.repository import GLib from gi.repository import GLib
@ -53,7 +52,7 @@ except ValueError:
def _close_fds(): def _close_fds():
for i in xrange(3, MAXFD): for i in range(3, MAXFD):
try: try:
os.close(i) os.close(i)
# pylint: disable=W0704 # pylint: disable=W0704
@ -69,7 +68,7 @@ def create_activity_id():
def _mkdir(path): def _mkdir(path):
try: try:
os.mkdir(path) os.mkdir(path)
except OSError, e: except OSError as e:
if e.errno != EEXIST: if e.errno != EEXIST:
raise e raise e
@ -137,10 +136,10 @@ def open_log_file(activity):
while True: while True:
path = env.get_logs_path('%s-%s.log' % (activity.get_bundle_id(), i)) path = env.get_logs_path('%s-%s.log' % (activity.get_bundle_id(), i))
try: try:
fd = os.open(path, os.O_EXCL | os.O_CREAT | os.O_WRONLY, 0644) fd = os.open(path, os.O_EXCL | os.O_CREAT | os.O_WRONLY, 0o644)
f = os.fdopen(fd, 'w', 0) f = os.fdopen(fd, 'w')
return (path, f) return (path, f)
except OSError, e: except OSError as e:
if e.errno == EEXIST: if e.errno == EEXIST:
i += 1 i += 1
elif e.errno == ENOSPC: elif e.errno == ENOSPC:
@ -225,7 +224,7 @@ class ActivityCreationHandler(GObject.GObject):
self._handle.object_id, self._handle.uri, self._handle.object_id, self._handle.uri,
self._handle.invited) self._handle.invited)
dev_null = file('/dev/null', 'r') dev_null = open('/dev/null', 'r')
child = subprocess.Popen([str(s) for s in command], child = subprocess.Popen([str(s) for s in command],
env=environ, env=environ,
cwd=str(self._bundle.get_path()), cwd=str(self._bundle.get_path()),

@ -24,26 +24,26 @@ journal object id's,
class ActivityHandle(object): class ActivityHandle(object):
''' '''
Data structure storing simple activity metadata Data structure storing simple activity metadata
Args: Args:
activity_id (string): unique id for the activity to be activity_id (string): unique id for the activity to be
created created
object_id (string): identity of the journal object object_id (string): identity of the journal object
associated with the activity. associated with the activity.
When you resume an activity from the journal When you resume an activity from the journal
the object_id will be passed in. It is optional the object_id will be passed in. It is optional
since new activities does not have an since new activities does not have an
associated object. associated object.
uri (string): URI associated with the activity. Used when uri (string): URI associated with the activity. Used when
opening an external file or resource in the opening an external file or resource in the
activity, rather than a journal object activity, rather than a journal object
(downloads stored on the file system for (downloads stored on the file system for
example or web pages) 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 launched for handling an invite from the network
''' '''
@ -55,7 +55,7 @@ class ActivityHandle(object):
self.invited = invited self.invited = invited
def get_dict(self): 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}''' {activity_id:XXXX, object_id:XXXX, uri:XXXX, invited:BOOL}'''
result = {'activity_id': self.activity_id, 'invited': self.invited} result = {'activity_id': self.activity_id, 'invited': self.invited}
if self.object_id: if self.object_id:

@ -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()

@ -79,5 +79,5 @@ class ActivityService(dbus.service.Object):
def GetDocumentPath(self, async_cb, async_err_cb): def GetDocumentPath(self, async_cb, async_err_cb):
try: try:
self._activity.get_document_path(async_cb, async_err_cb) self._activity.get_document_path(async_cb, async_err_cb)
except Exception, e: except Exception as e:
async_err_cb(e) async_err_cb(e)

@ -40,12 +40,13 @@ import gettext
import logging import logging
from glob import glob from glob import glob
from fnmatch import fnmatch from fnmatch import fnmatch
from ConfigParser import ConfigParser from six.moves.configparser import ConfigParser
import xml.etree.cElementTree as ET import xml.etree.cElementTree as ET
from HTMLParser import HTMLParser from six.moves.html_parser import HTMLParser
from sugar3 import env from sugar3 import env
from sugar3.bundle.activitybundle import ActivityBundle from sugar3.bundle.activitybundle import ActivityBundle
from six.moves import reduce
IGNORE_DIRS = ['dist', '.git', 'screenshots'] IGNORE_DIRS = ['dist', '.git', 'screenshots']
@ -150,7 +151,7 @@ class Builder(object):
args = ['msgfmt', '--output-file=%s' % mo_file, file_name] args = ['msgfmt', '--output-file=%s' % mo_file, file_name]
retcode = subprocess.call(args) retcode = subprocess.call(args)
if retcode: if retcode:
print 'ERROR - msgfmt failed with return code %i.' % retcode print('ERROR - msgfmt failed with return code %i.' % retcode)
if self._no_fail: if self._no_fail:
continue continue
@ -301,8 +302,8 @@ class Installer(Packager):
source_to_dest[source_path] = dest_path source_to_dest[source_path] = dest_path
for source, dest in source_to_dest.items(): for source, dest in list(source_to_dest.items()):
print 'Install %s to %s.' % (source, dest) print('Install %s to %s.' % (source, dest))
path = os.path.dirname(dest) path = os.path.dirname(dest)
if not os.path.exists(path): if not os.path.exists(path):
@ -429,7 +430,7 @@ def cmd_check(config, options):
if options.choice == 'integration': if options.choice == 'integration':
run_unit_test = False run_unit_test = False
print "Running Tests" print("Running Tests")
test_path = os.path.join(config.source_dir, "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) all_tests = unittest.defaultTestLoader.discover(unit_test_path)
unittest.TextTestRunner(verbosity=options.verbose).run(all_tests) unittest.TextTestRunner(verbosity=options.verbose).run(all_tests)
elif not run_unit_test: elif not run_unit_test:
print "Not running unit tests" print("Not running unit tests")
else: else:
print 'No "unit" directory found.' print('No "unit" directory found.')
if os.path.isdir(integration_test_path) and run_integration_test: if os.path.isdir(integration_test_path) and run_integration_test:
all_tests = unittest.defaultTestLoader.discover( all_tests = unittest.defaultTestLoader.discover(
integration_test_path) integration_test_path)
unittest.TextTestRunner(verbosity=options.verbose).run(all_tests) unittest.TextTestRunner(verbosity=options.verbose).run(all_tests)
elif not run_integration_test: elif not run_integration_test:
print "Not running integration tests" print("Not running integration tests")
else: else:
print 'No "integration" directory found.' print('No "integration" directory found.')
print "Finished testing" print("Finished testing")
else: else:
print "Error: No tests/ directory" print("Error: No tests/ directory")
def cmd_dev(config, options): def cmd_dev(config, options):
@ -472,9 +473,9 @@ def cmd_dev(config, options):
os.symlink(config.source_dir, bundle_path) os.symlink(config.source_dir, bundle_path)
except OSError: except OSError:
if os.path.islink(bundle_path): 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: 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): def cmd_dist_xo(config, options):
@ -490,9 +491,9 @@ def cmd_dist_xo(config, options):
def cmd_fix_manifest(config, options): def cmd_fix_manifest(config, options):
'''Add missing files to the manifest (OBSOLETE)''' '''Add missing files to the manifest (OBSOLETE)'''
print 'WARNING: The fix_manifest command is obsolete.' print('WARNING: The fix_manifest command is obsolete.')
print ' The MANIFEST file is no longer used in bundles,' print(' The MANIFEST file is no longer used in bundles,')
print ' please remove it.' print(' please remove it.')
def cmd_dist_source(config, options): def cmd_dist_source(config, options):
@ -506,7 +507,10 @@ def cmd_install(config, options):
"""Install the activity in the system""" """Install the activity in the system"""
installer = Installer(Builder(config)) 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): def _po_escape(string):
@ -568,7 +572,7 @@ def cmd_genpot(config, options):
args += python_files args += python_files
retcode = subprocess.call(args) retcode = subprocess.call(args)
if retcode: 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): def cmd_build(config, options):
@ -603,7 +607,7 @@ def start():
choices=['unit', 'integration'], choices=['unit', 'integration'],
help="run unit/integration test") help="run unit/integration test")
check_parser.add_argument("--verbosity", "-v", dest="verbose", check_parser.add_argument("--verbosity", "-v", dest="verbose",
type=int, choices=range(0, 3), type=int, choices=list(range(0, 3)),
default=1, nargs='?', default=1, nargs='?',
help="verbosity for the unit tests") help="verbosity for the unit tests")

@ -28,8 +28,8 @@ GObject.threads_init()
from gi.repository import WebKit from gi.repository import WebKit
import socket import socket
from threading import Thread from threading import Thread
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer from six.moves.BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
import SocketServer from six.moves import socketserver
import select import select
import errno import errno
import mimetypes import mimetypes
@ -80,7 +80,7 @@ class LocalHTTPServer(HTTPServer):
# shutdown request and wastes cpu at all other times. # shutdown request and wastes cpu at all other times.
try: try:
r, w, e = select.select([self], [], [], poll_interval) r, w, e = select.select([self], [], [], poll_interval)
except select.error, e: except select.error as e:
if e[0] == errno.EINTR: if e[0] == errno.EINTR:
logging.debug("got eintr") logging.debug("got eintr")
continue continue
@ -92,7 +92,7 @@ class LocalHTTPServer(HTTPServer):
def server_bind(self): def server_bind(self):
"""Override server_bind in HTTPServer to not use """Override server_bind in HTTPServer to not use
getfqdn to get the server name because is very slow.""" 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] _host, port = self.socket.getsockname()[:2]
self.server_name = 'localhost' self.server_name = 'localhost'
self.server_port = port self.server_port = port

@ -34,7 +34,7 @@ from sugar3.graphics.palettemenu import PaletteMenuBox
from sugar3 import profile 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): def _create_activity_icon(metadata):

@ -44,7 +44,9 @@ an `[Activity]` header on the first line:
* `icon` - the icon file for the activity, shown by Sugar in the list * `icon` - the icon file for the activity, shown by Sugar in the list
of installed activities, 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; Optional metadata keys are;
@ -117,7 +119,7 @@ Example `activity.info`
[Activity] [Activity]
name = Browse name = Browse
bundle_id = org.laptop.WebActivity bundle_id = org.laptop.WebActivity
exec = sugar-activity webactivity.WebActivity -s exec = sugar-activity3 webactivity.WebActivity -s
activity_version = 200 activity_version = 200
icon = activity-web icon = activity-web
max_participants = 100 max_participants = 100

@ -20,7 +20,7 @@
UNSTABLE. UNSTABLE.
""" """
from ConfigParser import ConfigParser, ParsingError from six.moves.configparser import ConfigParser, ParsingError
from locale import normalize from locale import normalize
import os import os
import shutil import shutil
@ -441,7 +441,7 @@ class ActivityBundle(Bundle):
if delete_profile: if delete_profile:
bundle_profile_path = env.get_profile_path(self._bundle_id) bundle_profile_path = env.get_profile_path(self._bundle_id)
if os.path.exists(bundle_profile_path): 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) shutil.rmtree(bundle_profile_path, ignore_errors=True)
self._uninstall(install_path) self._uninstall(install_path)

@ -20,10 +20,10 @@
UNSTABLE. UNSTABLE.
""" """
import six
import os import os
import logging import logging
import shutil import shutil
import StringIO
import zipfile import zipfile
@ -74,7 +74,7 @@ class Bundle(object):
if not os.path.isdir(self._path): if not os.path.isdir(self._path):
try: try:
self._zip_file = zipfile.ZipFile(self._path) self._zip_file = zipfile.ZipFile(self._path)
except zipfile.error, exception: except zipfile.error as exception:
raise MalformedBundleException('Error accessing zip file %r: ' raise MalformedBundleException('Error accessing zip file %r: '
'%s' % (self._path, exception)) '%s' % (self._path, exception))
self._check_zip_bundle() self._check_zip_bundle()
@ -115,7 +115,7 @@ class Bundle(object):
if self._zip_file is None: if self._zip_file is None:
path = os.path.join(self._path, filename) path = os.path.join(self._path, filename)
try: try:
f = open(path, 'rb') f = open(path, 'r')
except IOError: except IOError:
logging.debug("cannot open path %s" % path) logging.debug("cannot open path %s" % path)
return None return None
@ -123,7 +123,7 @@ class Bundle(object):
path = os.path.join(self._zip_root_dir, filename) path = os.path.join(self._zip_root_dir, filename)
try: try:
data = self._zip_file.read(path) data = self._zip_file.read(path)
f = StringIO.StringIO(data) f = six.StringIO(data)
except KeyError: except KeyError:
logging.debug('%s not found in zip %s.' % (filename, path)) logging.debug('%s not found in zip %s.' % (filename, path))
return None return None
@ -171,7 +171,7 @@ class Bundle(object):
raise AlreadyInstalledException raise AlreadyInstalledException
if not os.path.isdir(install_dir): 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 # zipfile provides API that in theory would let us do this
# correctly by hand, but handling all the oddities of # correctly by hand, but handling all the oddities of

@ -82,6 +82,7 @@ class NormalizedVersion(object):
Attributes: Attributes:
parts (list): the numeric parts of the version after normalization. parts (list): the numeric parts of the version after normalization.
""" """
def __init__(self, activity_version): def __init__(self, activity_version):
self._activity_version = activity_version self._activity_version = activity_version
self.parts = [] self.parts = []

@ -21,10 +21,11 @@
UNSTABLE. UNSTABLE.
""" """
from ConfigParser import ConfigParser from six.moves import urllib
from six.moves.configparser import ConfigParser
import tempfile import tempfile
import os import os
import urllib
from sugar3 import env from sugar3 import env
from sugar3.bundle.bundle import Bundle, MalformedBundleException from sugar3.bundle.bundle import Bundle, MalformedBundleException
@ -142,7 +143,7 @@ class ContentBundle(Bundle):
def get_start_uri(self): def get_start_uri(self):
path = os.path.join(self.get_path(), self._activity_start) 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): def get_bundle_id(self):
return self._global_name return self._global_name

@ -20,6 +20,7 @@
STABLE STABLE
""" """
import six
import logging import logging
import time import time
from datetime import datetime from datetime import datetime
@ -69,6 +70,7 @@ def __datastore_updated_cb(object_id):
def __datastore_deleted_cb(object_id): def __datastore_deleted_cb(object_id):
deleted.send(None, object_id=object_id) deleted.send(None, object_id=object_id)
created = dispatch.Signal() created = dispatch.Signal()
deleted = dispatch.Signal() deleted = dispatch.Signal()
updated = dispatch.Signal() updated = dispatch.Signal()
@ -85,6 +87,12 @@ class DSMetadata(GObject.GObject):
if not properties: if not properties:
self._properties = {} self._properties = {}
else: else:
if six.PY3:
for x, y in properties.items():
try:
properties[x] = y.decode()
except BaseException:
pass
self._properties = properties self._properties = properties
default_keys = ['activity', 'activity_id', default_keys = ['activity', 'activity_id',
@ -97,6 +105,11 @@ class DSMetadata(GObject.GObject):
return self._properties[key] return self._properties[key]
def __setitem__(self, key, value): 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: if key not in self._properties or self._properties[key] != value:
self._properties[key] = value self._properties[key] = value
self.emit('updated') self.emit('updated')
@ -112,7 +125,7 @@ class DSMetadata(GObject.GObject):
return key in self._properties return key in self._properties
def keys(self): def keys(self):
return self._properties.keys() return list(self._properties.keys())
def get_dictionary(self): def get_dictionary(self):
return self._properties return self._properties
@ -128,7 +141,7 @@ class DSMetadata(GObject.GObject):
def update(self, properties): def update(self, properties):
"""Update all of the metadata""" """Update all of the metadata"""
for (key, value) in properties.items(): for (key, value) in list(properties.items()):
self[key] = value self[key] = value

@ -1,4 +1,6 @@
import weakref import weakref
import six
try: try:
set set
except NameError: except NameError:
@ -11,7 +13,7 @@ WEAKREF_TYPES = (weakref.ReferenceType, saferef.BoundMethodWeakref)
def _make_id(target): def _make_id(target):
if hasattr(target, 'im_func'): 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) return id(target)
@ -159,7 +161,7 @@ class Signal(object):
for receiver in self._live_receivers(_make_id(sender)): for receiver in self._live_receivers(_make_id(sender)):
try: try:
response = receiver(signal=self, sender=sender, **named) response = receiver(signal=self, sender=sender, **named)
except Exception, err: except Exception as err:
responses.append((receiver, err)) responses.append((receiver, err))
else: else:
responses.append((receiver, response)) responses.append((receiver, response))
@ -195,3 +197,17 @@ class Signal(object):
for idx, (r_key, _) in enumerate(self.receivers): for idx, (r_key, _) in enumerate(self.receivers):
if r_key == key: if r_key == key:
del self.receivers[idx] 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__

@ -5,6 +5,7 @@ Provides a way to safely weakref any function, including bound methods (which
aren't handled by the core weakref module). aren't handled by the core weakref module).
""" """
import six
import weakref import weakref
import traceback import traceback
@ -21,7 +22,7 @@ def safeRef(target, onDelete=None):
weakref or a BoundMethodWeakref) as argument. weakref or a BoundMethodWeakref) as argument.
""" """
if hasattr(target, 'im_self'): 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. # Turn a bound method into a BoundMethodWeakref instance.
# Keep track of these instances for lookup by disconnect(). # Keep track of these instances for lookup by disconnect().
assert hasattr(target, 'im_func'), \ assert hasattr(target, 'im_func'), \
@ -123,18 +124,18 @@ class BoundMethodWeakref(object):
try: try:
if callable(function): if callable(function):
function(self) function(self)
except Exception, e: except Exception as e:
try: try:
traceback.print_exc() traceback.print_exc()
except AttributeError: except AttributeError:
print "Exception during saferef %s cleanup " print("Exception during saferef %s cleanup "
"function %s: %s" % (self, function, e) "function %s: %s" % (self, function, e))
self.deletionMethods = [onDelete] self.deletionMethods = [onDelete]
self.key = self.calculateKey(target) self.key = self.calculateKey(target)
self.weakSelf = weakref.ref(target.im_self, remove) self.weakSelf = weakref.ref(im_self(target), remove)
self.weakFunc = weakref.ref(target.im_func, remove) self.weakFunc = weakref.ref(im_func(target), remove)
self.selfName = str(target.im_self) self.selfName = str(im_self(target))
self.funcName = str(target.im_func.__name__) self.funcName = str(im_func(target).__name__)
def calculateKey(cls, target): def calculateKey(cls, target):
"""Calculate the reference key for this reference """Calculate the reference key for this reference
@ -142,7 +143,7 @@ class BoundMethodWeakref(object):
Currently this is a two-tuple of the id()'s of the Currently this is a two-tuple of the id()'s of the
target object and the target function respectively. 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) calculateKey = classmethod(calculateKey)
def __str__(self): def __str__(self):
@ -155,15 +156,19 @@ class BoundMethodWeakref(object):
__repr__ = __str__ __repr__ = __str__
def __nonzero__(self): def __bool__(self):
"""Whether we are still a valid reference""" """Whether we are still a valid reference"""
return self() is not None return self() is not None
def __nonzero__(self):
"""Python2 alternative for __bool__"""
return self() is not None
def __cmp__(self, other): def __cmp__(self, other):
"""Compare with another reference""" """Compare with another reference"""
if not isinstance(other, self.__class__): if not isinstance(other, self.__class__):
return cmp(self.__class__, type(other)) return ((self.__class__ > type(other)) - (self.__class__ < type(other)))
return cmp(self.key, other.key) return ((self.key > other.key) - (self.key < other.key))
def __call__(self): def __call__(self):
"""Return a strong reference to the bound method """Return a strong reference to the bound method
@ -201,6 +206,7 @@ class BoundNonDescriptorMethodWeakref(BoundMethodWeakref):
aren't descriptors (such as Jython) this implementation has the advantage aren't descriptors (such as Jython) this implementation has the advantage
of working in the most cases. of working in the most cases.
""" """
def __init__(self, target, onDelete=None): def __init__(self, target, onDelete=None):
"""Return a weak-reference-like instance for a bound method """Return a weak-reference-like instance for a bound method
@ -215,9 +221,9 @@ class BoundNonDescriptorMethodWeakref(BoundMethodWeakref):
collected). Should take a single argument, collected). Should take a single argument,
which will be passed a pointer to this object. 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" % ("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) super(BoundNonDescriptorMethodWeakref, self).__init__(target, onDelete)
def __call__(self): def __call__(self):
@ -255,3 +261,17 @@ def get_bound_method_weakref(target, onDelete):
# no luck, use the alternative implementation: # no luck, use the alternative implementation:
return BoundNonDescriptorMethodWeakref(target=target, return BoundNonDescriptorMethodWeakref(target=target,
onDelete=onDelete) 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__

@ -36,9 +36,9 @@ def get_profile_path(path=None):
base = os.path.join(home_dir, profile_id) base = os.path.join(home_dir, profile_id)
if not os.path.isdir(base): if not os.path.isdir(base):
try: try:
os.makedirs(base, 0770) os.makedirs(base, 0o770)
except OSError: except OSError:
print 'Could not create user directory.' print('Could not create user directory.')
if path is not None: if path is not None:
return os.path.join(base, path) return os.path.join(base, path)

@ -16,7 +16,6 @@ sugar_PYTHON = \
palettemenu.py \ palettemenu.py \
palettewindow.py \ palettewindow.py \
panel.py \ panel.py \
popwindow.py \
radiopalette.py \ radiopalette.py \
radiotoolbutton.py \ radiotoolbutton.py \
scrollingdetector.py \ scrollingdetector.py \

@ -61,7 +61,7 @@ from sugar3.graphics import style
from sugar3.graphics.icon import Icon 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'): if not hasattr(GObject.ParamFlags, 'READWRITE'):
@ -258,6 +258,8 @@ class Alert(Gtk.EventBox):
def __button_clicked_cb(self, button, response_id): def __button_clicked_cb(self, button, response_id):
self._response(response_id) self._response(response_id)
if hasattr(Alert, 'set_css_name'): if hasattr(Alert, 'set_css_name'):
Alert.set_css_name('alert') Alert.set_css_name('alert')
@ -294,9 +296,9 @@ class ConfirmationAlert(Alert):
# Check the response identifier. # Check the response identifier.
if response_id is Gtk.ResponseType.OK: if response_id is Gtk.ResponseType.OK:
print 'Ok Button was clicked.' print('Ok Button was clicked.')
elif response_id is Gtk.ResponseType.CANCEL: elif response_id is Gtk.ResponseType.CANCEL:
print 'Cancel Button was clicked.' print('Cancel Button was clicked.')
""" """
def __init__(self, **kwargs): def __init__(self, **kwargs):
@ -341,7 +343,7 @@ class ErrorAlert(Alert):
# Check the response identifier. # Check the response identifier.
if response_id is Gtk.ResponseType.OK: if response_id is Gtk.ResponseType.OK:
print 'Ok Button was clicked.' print('Ok Button was clicked.')
""" """
def __init__(self, **kwargs): def __init__(self, **kwargs):
@ -390,6 +392,8 @@ class _TimeoutIcon(Gtk.Alignment):
def set_text(self, text): def set_text(self, text):
self._text.set_markup('<b>%s</b>' % GLib.markup_escape_text(str(text))) self._text.set_markup('<b>%s</b>' % GLib.markup_escape_text(str(text)))
if hasattr(_TimeoutIcon, 'set_css_name'): if hasattr(_TimeoutIcon, 'set_css_name'):
_TimeoutIcon.set_css_name('timeouticon') _TimeoutIcon.set_css_name('timeouticon')
@ -458,11 +462,11 @@ class TimeoutAlert(_TimeoutAlert):
# Check the response identifier. # Check the response identifier.
if response_id is Gtk.ResponseType.OK: if response_id is Gtk.ResponseType.OK:
print 'Continue Button was clicked.' print('Continue Button was clicked.')
elif response_id is Gtk.ResponseType.CANCEL: elif response_id is Gtk.ResponseType.CANCEL:
print 'Cancel Button was clicked.' print('Cancel Button was clicked.')
elif response_id == -1: elif response_id == -1:
print 'Timeout occurred' print('Timeout occurred')
""" """
def __init__(self, timeout=5, **kwargs): def __init__(self, timeout=5, **kwargs):
@ -508,9 +512,9 @@ class NotifyAlert(_TimeoutAlert):
# Check the response identifier. # Check the response identifier.
if response_id is Gtk.ResponseType.OK: if response_id is Gtk.ResponseType.OK:
print 'Ok Button was clicked.' print('Ok Button was clicked.')
elif response_id == -1: elif response_id == -1:
print 'Timeout occurred' print('Timeout occurred')
""" """
def __init__(self, timeout=5, **kwargs): def __init__(self, timeout=5, **kwargs):

@ -29,7 +29,7 @@ from sugar3.graphics.icon import Icon
from sugar3.graphics.palette import Palette, ToolInvoker, WidgetInvoker 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'): if not hasattr(GObject.ParamFlags, 'READWRITE'):
@ -38,8 +38,8 @@ if not hasattr(GObject.ParamFlags, 'READWRITE'):
def get_svg_color_string(color): def get_svg_color_string(color):
return '#%.2X%.2X%.2X' % (color.red / 257, color.green / 257, return '#%.2X%.2X%.2X' % (color.red // 257, color.green // 257,
color.blue / 257) color.blue // 257)
class _ColorButton(Gtk.Button): class _ColorButton(Gtk.Button):
@ -123,8 +123,8 @@ class _ColorButton(Gtk.Button):
context = self.get_style_context() context = self.get_style_context()
fg_color = context.get_color(Gtk.StateType.NORMAL) fg_color = context.get_color(Gtk.StateType.NORMAL)
# the color components are stored as float values between 0.0 and 1.0 # 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, return '#%.2X%.2X%.2X' % (int(fg_color.red * 255), int(fg_color.green * 255),
fg_color.blue * 255) int(fg_color.blue * 255))
def set_color(self, color): def set_color(self, color):
assert isinstance(color, Gdk.Color) assert isinstance(color, Gdk.Color)
@ -149,11 +149,11 @@ class _ColorButton(Gtk.Button):
''' '''
Sets the icon for the tool button from a named themed icon. Sets the icon for the tool button from a named themed icon.
If it is none then no icon will be shown. If it is none then no icon will be shown.
Args: Args:
icon_name(string): The name for a themed icon. icon_name(string): The name for a themed icon.
It can be set as 'None' too. It can be set as 'None' too.
Example: Example:
set_icon_name('view-radial') set_icon_name('view-radial')
''' '''
@ -169,11 +169,11 @@ class _ColorButton(Gtk.Button):
icon_name = GObject.Property(type=str, icon_name = GObject.Property(type=str,
getter=get_icon_name, setter=set_icon_name) getter=get_icon_name, setter=set_icon_name)
def set_icon_size(self, icon_size): def set_icon_size(self, pixel_size):
self._preview.props.icon_size = icon_size self._preview.props.pixel_size = pixel_size
def get_icon_size(self): def get_icon_size(self):
return self._preview.props.icon_size return self._preview.props.pixel_size
icon_size = GObject.Property(type=int, icon_size = GObject.Property(type=int,
getter=get_icon_size, setter=set_icon_size) getter=get_icon_size, setter=set_icon_size)
@ -496,13 +496,13 @@ class ColorToolButton(Gtk.ToolItem):
def set_accelerator(self, accelerator): def set_accelerator(self, accelerator):
''' '''
Sets keyboard shortcut that activates this button. Sets keyboard shortcut that activates this button.
Args: Args:
accelerator(string): accelerator to be set. Should be in accelerator(string): accelerator to be set. Should be in
form <modifier>Letter form <modifier>Letter
Find about format here : Find about format here :
https://developer.gnome.org/gtk3/stable/gtk3-Keyboard-Accelerators.html#gtk-accelerator-parse https://developer.gnome.org/gtk3/stable/gtk3-Keyboard-Accelerators.html#gtk-accelerator-parse
Example: Example:
set_accelerator(self, 'accel') set_accelerator(self, 'accel')
''' '''
@ -523,21 +523,21 @@ class ColorToolButton(Gtk.ToolItem):
The create_palette function is called when the palette needs to be The create_palette function is called when the palette needs to be
invoked. For example, when the user has right clicked the icon or invoked. For example, when the user has right clicked the icon or
the user has hovered over the icon for a long time. the user has hovered over the icon for a long time.
The create_palette will only be called once or zero times. The palette 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 returned will be stored and re-used if the user invokes the palette
multiple times. multiple times.
Your create_palette implementation does not need to Your create_palette implementation does not need to
:any:`Gtk.Widget.show` the palette, as this will be done by the :any:`Gtk.Widget.show` the palette, as this will be done by the
invoker. However, you still need to show invoker. However, you still need to show
the menu items, etc that you place in the palette. the menu items, etc that you place in the palette.
Returns: Returns:
sugar3.graphics.palette.Palette, or None to indicate that you sugar3.graphics.palette.Palette, or None to indicate that you
do not want a palette shown do not want a palette shown
The default implementation returns None, to indicate no palette should The default implementation returns None, to indicate no palette should
be shown. be shown.
''' '''
@ -595,11 +595,11 @@ class ColorToolButton(Gtk.ToolItem):
''' '''
Sets the icon for the tool button from a named themed icon. Sets the icon for the tool button from a named themed icon.
If it is none then no icon will be shown. If it is none then no icon will be shown.
Args: Args:
icon_name(string): The name for a themed icon. icon_name(string): The name for a themed icon.
It can be set as 'None' too. It can be set as 'None' too.
Example: Example:
set_icon_name('view-radial') set_icon_name('view-radial')
''' '''
@ -632,8 +632,8 @@ class ColorToolButton(Gtk.ToolItem):
def set_title(self, title): def set_title(self, title):
''' '''
The set_title() method sets the "title" property to the value of The set_title() method sets the "title" property to the value of
title. The "title" property contains the string that is used to title. The "title" property contains the string that is used to
set the colorbutton title. set the colorbutton title.
''' '''
self.get_child().props.title = title self.get_child().props.title = title

@ -87,11 +87,13 @@ In this example, the badge will be centered at 97.0% on the X axis,
and 85.0% on the Y axis. and 85.0% on the Y axis.
''' '''
import six
import re import re
import math import math
import logging import logging
import os import os
from ConfigParser import ConfigParser
from six.moves.configparser import ConfigParser
import gi import gi
gi.require_version('Rsvg', '2.0') gi.require_version('Rsvg', '2.0')
@ -127,8 +129,8 @@ class _SVGLoader(object):
if cache: if cache:
self._cache[file_name] = icon self._cache[file_name] = icon
for entity, value in entities.items(): for entity, value in list(entities.items()):
if isinstance(value, basestring): if isinstance(value, six.string_types):
xml = '<!ENTITY %s "%s">' % (entity, value) xml = '<!ENTITY %s "%s">' % (entity, value)
icon = re.sub('<!ENTITY %s .*>' % entity, xml, icon) icon = re.sub('<!ENTITY %s .*>' % entity, xml, icon)
else: else:
@ -207,7 +209,7 @@ class _IconBuffer(object):
# try read from the .icon file # try read from the .icon file
icon_filename = info.get_filename().replace('.svg', '.icon') icon_filename = info.get_filename().replace('.svg', '.icon')
if icon_filename != info.get_filename() and \ if icon_filename != info.get_filename() and \
os.path.exists(icon_filename): os.path.exists(icon_filename):
try: try:
with open(icon_filename) as config_file: with open(icon_filename) as config_file:
@ -470,7 +472,6 @@ class Icon(Gtk.Image):
__gtype_name__ = 'SugarIcon' __gtype_name__ = 'SugarIcon'
# FIXME: deprecate icon_size
_MENU_SIZES = (Gtk.IconSize.MENU, Gtk.IconSize.DND, _MENU_SIZES = (Gtk.IconSize.MENU, Gtk.IconSize.DND,
Gtk.IconSize.SMALL_TOOLBAR, Gtk.IconSize.BUTTON) Gtk.IconSize.SMALL_TOOLBAR, Gtk.IconSize.BUTTON)
@ -483,7 +484,6 @@ class Icon(Gtk.Image):
self._alpha = 1.0 self._alpha = 1.0
self._scale = 1.0 self._scale = 1.0
# FIXME: deprecate icon_size
if 'icon_size' in kwargs: if 'icon_size' in kwargs:
logging.warning("icon_size is deprecated. Use pixel_size instead.") logging.warning("icon_size is deprecated. Use pixel_size instead.")
@ -532,7 +532,6 @@ class Icon(Gtk.Image):
if self._buffer.file_name != self.props.file: if self._buffer.file_name != self.props.file:
self._buffer.file_name = self.props.file self._buffer.file_name = self.props.file
# FIXME: deprecate icon_size
pixel_size = None pixel_size = None
if self.props.pixel_size == -1: if self.props.pixel_size == -1:
if self.props.icon_size in self._MENU_SIZES: if self.props.icon_size in self._MENU_SIZES:
@ -549,7 +548,7 @@ class Icon(Gtk.Image):
self._buffer.height = height self._buffer.height = height
def _icon_size_changed_cb(self, image, pspec): 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): def _icon_name_changed_cb(self, image, pspec):
self._buffer.icon_name = self.props.icon_name self._buffer.icon_name = self.props.icon_name
@ -805,7 +804,7 @@ class EventIcon(Gtk.EventBox):
# for example, after a touch palette invocation # for example, after a touch palette invocation
self.connect_after('button-release-event', self.connect_after('button-release-event',
self.__button_release_event_cb) self.__button_release_event_cb)
for key, value in kwargs.iteritems(): for key, value in six.iteritems(kwargs):
self.set_property(key, value) self.set_property(key, value)
from sugar3.graphics.palette import CursorInvoker from sugar3.graphics.palette import CursorInvoker
@ -1152,6 +1151,8 @@ class CanvasIcon(EventIcon):
def __palette_popdown_cb(self, palette): def __palette_popdown_cb(self, palette):
self.unset_state_flags(Gtk.StateFlags.PRELIGHT) self.unset_state_flags(Gtk.StateFlags.PRELIGHT)
if hasattr(CanvasIcon, 'set_css_name'): if hasattr(CanvasIcon, 'set_css_name'):
CanvasIcon.set_css_name('canvasicon') CanvasIcon.set_css_name('canvasicon')
@ -1447,6 +1448,6 @@ def get_surface(**kwargs):
cairo surface or None if image was not found cairo surface or None if image was not found
''' '''
icon = _IconBuffer() icon = _IconBuffer()
for key, value in kwargs.items(): for key, value in list(kwargs.items()):
icon.__setattr__(key, value) icon.__setattr__(key, value)
return icon.get_surface() return icon.get_surface()

@ -61,7 +61,7 @@ class IconEntry(Gtk.Entry):
self.set_icon(position, pixbuf) self.set_icon(position, pixbuf)
def set_icon(self, 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) raise ValueError('Argument must be a pixbuf, not %r.' % pixbuf)
self.set_icon_from_pixbuf(position, pixbuf) self.set_icon_from_pixbuf(position, pixbuf)

@ -19,8 +19,8 @@
STABLE. STABLE.
""" """
import six
import logging import logging
import StringIO
import cairo import cairo
from gi.repository import GObject 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 None, if it could not be created
Example: Example:
pixbuf = get_preview_pixbuf(metadata.get('preview', '')) pixbuf = get_preview_pixbuf(metadata.get('preview', ''))
has_preview = pixbuf is not None has_preview = pixbuf is not None
if has_preview: if has_preview:
im = Gtk.Image() im = Gtk.Image()
im.set_from_pixbuf(pixbuf) im.set_from_pixbuf(pixbuf)
box.add(im) box.add(im)
im.show() im.show()
""" """
if width == -1: if width == -1:
@ -82,7 +82,7 @@ def get_preview_pixbuf(preview_data, width=-1, height=-1):
import base64 import base64
preview_data = base64.b64decode(preview_data) preview_data = base64.b64decode(preview_data)
png_file = StringIO.StringIO(preview_data) png_file = six.StringIO(preview_data)
try: try:
# Load image and scale to dimensions # Load image and scale to dimensions
surface = cairo.ImageSurface.create_from_png(png_file) 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 what_filter (str): an activity bundle_id or a generic mime type as
defined in :mod:`sugar3.mime` used to determine which objects defined in :mod:`sugar3.mime` used to determine which objects
will be presented in the object chooser will be presented in the object chooser
filter_type (str): should be one of [None, FILTER_TYPE_GENERIC_MIME, filter_type (str): should be one of [None, FILTER_TYPE_GENERIC_MIME,
FILTER_TYPE_ACTIVITY, FILTER_TYPE_MIME_BY_ACTIVITY] FILTER_TYPE_ACTIVITY, FILTER_TYPE_MIME_BY_ACTIVITY]
@ -155,7 +155,7 @@ class ObjectChooser(object):
Examples: Examples:
chooser = ObjectChooser(self._activity, what_filter='Image') chooser = ObjectChooser(self._activity, what_filter='Image')
chooser = ObjectChooser(parent=self, chooser = ObjectChooser(parent=self,
what_filter=self.get_bundle_id(), what_filter=self.get_bundle_id(),
filter_type=FILTER_TYPE_ACTIVITY) filter_type=FILTER_TYPE_ACTIVITY)
@ -192,7 +192,7 @@ class ObjectChooser(object):
def run(self): def run(self):
""" """
Runs the object chooser and displays it. Runs the object chooser and displays it.
Returns: Returns:
Gtk.ResponseType constant, the response received from displaying the Gtk.ResponseType constant, the response received from displaying the
object chooser. object chooser.

@ -36,7 +36,7 @@ def get_group(group_id):
def popdown_all(): def popdown_all():
for group in _groups.values(): for group in list(_groups.values()):
group.popdown() group.popdown()

@ -56,7 +56,7 @@ Example:
menu_item.show() menu_item.show()
def __edit_cb(self, menu_item): def __edit_cb(self, menu_item):
print 'Edit...' print('Edit...')
# Usually the Palette instance is returned in a create_palette function # Usually the Palette instance is returned in a create_palette function
p = ItemPalette() p = ItemPalette()

@ -43,6 +43,7 @@ from sugar3.graphics.icon import CellRendererIcon
_pointer = None _pointer = None
def _get_pointer_position(widget): def _get_pointer_position(widget):
global _pointer global _pointer
@ -53,6 +54,7 @@ def _get_pointer_position(widget):
screen, x, y = _pointer.get_position() screen, x, y = _pointer.get_position()
return (x, y) return (x, y)
def _calculate_gap(a, b): def _calculate_gap(a, b):
"""Helper function to find the gap position and size of widget a""" """Helper function to find the gap position and size of widget a"""
# Test for each side if the palette and invoker are # Test for each side if the palette and invoker are
@ -420,6 +422,8 @@ class _PaletteWindowWidget(Gtk.Window):
self.disconnect_by_func(self.__enter_notify_event_cb) self.disconnect_by_func(self.__enter_notify_event_cb)
self.disconnect_by_func(self.__leave_notify_event_cb) self.disconnect_by_func(self.__leave_notify_event_cb)
self.hide() self.hide()
if hasattr(_PaletteWindowWidget, 'set_css_name'): if hasattr(_PaletteWindowWidget, 'set_css_name'):
_PaletteWindowWidget.set_css_name('palette') _PaletteWindowWidget.set_css_name('palette')
@ -954,7 +958,7 @@ class Invoker(GObject.GObject):
dright = screen_area.x + screen_area.width - rect.x - rect.width dright = screen_area.x + screen_area.width - rect.x - rect.width
ih = 0 ih = 0
if palette_dim.width == 0: if palette_dim.width == 0:
ph = 0 ph = 0
@ -1315,7 +1319,7 @@ class CursorInvoker(Invoker):
self._leave_hid = self._item.connect('leave-notify-event', self._leave_hid = self._item.connect('leave-notify-event',
self.__leave_notify_event_cb) self.__leave_notify_event_cb)
self._release_hid = self._item.connect('button-release-event', 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( self._long_pressed_hid = self._long_pressed_controller.connect(
'pressed', 'pressed',
self.__long_pressed_event_cb, self._item) self.__long_pressed_event_cb, self._item)
@ -1325,9 +1329,9 @@ class CursorInvoker(Invoker):
def detach(self): def detach(self):
Invoker.detach(self) Invoker.detach(self)
self._item.disconnect(self._enter_hid) self._item.disconnect_by_func(self.__enter_notify_event_cb)
self._item.disconnect(self._leave_hid) self._item.disconnect_by_func(self.__leave_notify_event_cb)
self._item.disconnect(self._release_hid) self._item.disconnect_by_func(self.__button_release_event_cb)
self._long_pressed_controller.detach(self._item) self._long_pressed_controller.detach(self._item)
self._long_pressed_controller.disconnect(self._long_pressed_hid) self._long_pressed_controller.disconnect(self._long_pressed_hid)

@ -1,202 +0,0 @@
# Copyright (C) 2016 Abhijit Patel
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
'''
Provide a PopWindow class for pop-up windows.
Making PopWindow containing Gtk.Toolbar which also contains Gtk.Label
and Toolbutton at the end of the Gtk.Toolbar.
It is possible to change props like size and add more widgets PopWindow
and also to Gtk.Toolbar.
Example:
.. literalinclude: ..sugar/src/jarabe/view/viewsource.py
.. literalinclude: ..sugar/src/jarabe/view/viewhelp.py
'''
from gettext import gettext as _
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import GdkX11
from gi.repository import GObject
from sugar3.graphics import style
from sugar3.graphics.toolbutton import ToolButton
from jarabe.model import shell
class PopWindow(Gtk.Window):
"""
UI interface for activity Pop-up Windows.
PopWindows are the windows that open on the top of the current window.
These pop-up windows don't cover the whole screen.
They contain canvas content, alerts messages, a tray and a
toolbar.
FULLSCREEN and HALF_WIDTH for setting size of the window.
Kwargs:
size (int,int): size to be set of the window
window_xid (xlib.Window): xid of the parent window
"""
FULLSCREEN = (Gdk.Screen.width() - style.GRID_CELL_SIZE * 3,
Gdk.Screen.height() - style.GRID_CELL_SIZE * 2)
HALF_WIDTH = ((Gdk.Screen.height() - style.GRID_CELL_SIZE * 3)/2,
(Gdk.Screen.height() - style.GRID_CELL_SIZE * 2))
def __init__(self, window_xid=None, **kwargs):
Gtk.Window.__init__(self, **kwargs)
self._parent_window_xid = window_xid
self.set_decorated(False)
self.set_position(Gtk.WindowPosition.CENTER_ALWAYS)
self.set_border_width(style.LINE_WIDTH)
self.set_has_resize_grip(False)
self.props.size = self.FULLSCREEN
self.connect('realize', self.__realize_cb)
self.connect('key-press-event', self.__key_press_event_cb)
self.connect('hide', self.__hide_cb)
self._vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
self.add(self._vbox)
self._vbox.show()
self._title_box = TitleBox()
self._title_box.close_button.connect(
'clicked',
self.__close_button_clicked_cb)
self._title_box.set_size_request(-1, style.GRID_CELL_SIZE)
self._vbox.pack_start(self._title_box, False, True, 0)
self._title_box.show()
# Note:
# Not displaying the pop-up from here instead allowing
# the child class to display the window after modifications
# like chaninging window size, decorating, changing position.
def set_size(self, size):
width, height = size
self.set_size_request(width, height)
size = GObject.Property(type=None, setter=set_size)
def get_title_box(self):
'''
Getter method for title-box
Returns:
self._title_box (): Title or Tool Box
'''
return self._title_box
title_box = GObject.Property(type=str, getter=get_title_box)
def get_vbox(self):
'''
Getter method for canvas
Returns:
self._vbox (Gtk.Box): canvas
'''
return self._vbox
vbox = GObject.Property(type=str, getter=get_vbox)
def __close_button_clicked_cb(self, button):
self.destroy()
def __key_press_event_cb(self, window, event):
keyname = Gdk.keyval_name(event.keyval)
if keyname == 'Escape':
self.destroy()
def __realize_cb(self, widget):
self.set_type_hint(Gdk.WindowTypeHint.DIALOG)
window = self.get_window()
window.set_accept_focus(True)
if self._parent_window_xid is not None:
display = Gdk.Display.get_default()
parent = GdkX11.X11Window.foreign_new_for_display(
display, self._parent_window_xid)
window.set_transient_for(parent)
shell.get_model().push_modal()
def __hide_cb(self, widget):
shell.get_model().pop_modal()
def add_view(self, widget, expand=True, fill=True, padding=0):
'''
Adds child to the vbox.
Args:
widget (Gtk.Widget): widget to be added
expand (bool): True if child is to be given extra space allocated
to vbox.
fill (bool): True if space given to child by the expand option is
actually allocated to child, rather than just padding it.
padding (int): extra space in pixels to put between child and its
neighbors, over and above the global amount specified
by spacing in vbox.
Returns:
None
'''
self._vbox.pack_start(widget, expand, fill, padding)
class TitleBox(Gtk.Toolbar):
'''
Title box at the top of the pop-up window.
Title and close button are added to the box and as needed more widgets
can be added using self.add_widget method.
This box is optional as the inherited class can remove this block by
setting the self._set_title_box to False.
'''
def __init__(self):
Gtk.Toolbar.__init__(self)
self.close_button = ToolButton(icon_name='dialog-cancel')
self.close_button.set_tooltip(_('Close'))
self.insert(self.close_button, -1)
self.close_button.show()
self._label = Gtk.Label()
self._label.set_alignment(0, 0.5)
tool_item = Gtk.ToolItem()
tool_item.set_expand(True)
tool_item.add(self._label)
self._label.show()
self.insert(tool_item, 0)
tool_item.show()
def set_title(self, title):
'''
setter function for 'title' property.
Args:
title (str): title for the pop-up window
'''
self._label.set_markup('<b>%s</b>' % title)
self._label.show()
title = GObject.Property(type=str, setter=set_title)

@ -41,6 +41,7 @@ class ProgressIcon(Gtk.DrawingArea):
fill_color (string): The main (inside) color of progressicon fill_color (string): The main (inside) color of progressicon
[e.g. fill_color=style.COLOR_BLUE.get_svg() [e.g. fill_color=style.COLOR_BLUE.get_svg()
''' '''
def __init__(self, icon_name, pixel_size, stroke_color, fill_color, def __init__(self, icon_name, pixel_size, stroke_color, fill_color,
direction='vertical'): direction='vertical'):
Gtk.DrawingArea.__init__(self) Gtk.DrawingArea.__init__(self)

@ -32,18 +32,18 @@ from gi.repository import GLib
class ScrollingDetector(GObject.GObject): 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 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-start` signal is emitted when scrolling begins and
The `scroll-end` signal is emitted when scrolling ends The `scroll-end` signal is emitted when scrolling ends
Neither of these two signals have any arguments Neither of these two signals have any arguments
Args: Args:
scrolled_window (Gtk.ScrolledWindow): A GTK scrolled window object for which scrolled_window (Gtk.ScrolledWindow): A GTK scrolled window object for which
scrolling is to be detected scrolling is to be detected
timeout (int): time in milliseconds to establish the interval for which timeout (int): time in milliseconds to establish the interval for which
scrolling is detected scrolling is detected
''' '''
@ -65,7 +65,7 @@ class ScrollingDetector(GObject.GObject):
Connects scrolling detector to a scrolled window. Connects scrolling detector to a scrolled window.
Detects scrolling when the vertical scrollbar Detects scrolling when the vertical scrollbar
adjustment value is changed adjustment value is changed
Should be used to link an instance of a scrolling detector Should be used to link an instance of a scrolling detector
to a Scrolled Window, after setting scrolled_window to a Scrolled Window, after setting scrolled_window
''' '''

@ -59,6 +59,7 @@ class Font(object):
Args: Args:
desc (str): a description of the Font object desc (str): a description of the Font object
''' '''
def __init__(self, desc): def __init__(self, desc):
self._desc = desc self._desc = desc
@ -84,6 +85,7 @@ class Color(object):
alpha (double): transparency of color alpha (double): transparency of color
''' '''
def __init__(self, color, alpha=1.0): def __init__(self, color, alpha=1.0):
self._r, self._g, self._b = self._html_to_rgb(color) self._r, self._g, self._b = self._html_to_rgb(color)
self._a = alpha self._a = alpha
@ -112,7 +114,8 @@ class Color(object):
''' '''
Returns string in the standard html Color format (#FFFFFF) 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): def _html_to_rgb(self, html_color):
''' '''

@ -203,6 +203,8 @@ class ToolbarBox(Gtk.VBox):
if button == self.expanded_button: if button == self.expanded_button:
self.remove(button.page_widget) self.remove(button.page_widget)
self._expanded_button_index = -1 self._expanded_button_index = -1
if hasattr(ToolbarBox, 'set_css_name'): if hasattr(ToolbarBox, 'set_css_name'):
ToolbarBox.set_css_name('toolbarbox') ToolbarBox.set_css_name('toolbarbox')

@ -32,7 +32,7 @@ class Toolbox(Gtk.VBox):
Class to represent the toolbox of an activity. Groups a Class to represent the toolbox of an activity. Groups a
number of toolbars vertically, which can be accessed using their number of toolbars vertically, which can be accessed using their
indices. The current toolbar is the only one displayed. indices. The current toolbar is the only one displayed.
Emits `current-toolbar-changed` signal when the Emits `current-toolbar-changed` signal when the
current toolbar is changed. This signal takes the current page index current toolbar is changed. This signal takes the current page index
as an argument. as an argument.
@ -71,13 +71,13 @@ class Toolbox(Gtk.VBox):
def add_toolbar(self, name, toolbar): def add_toolbar(self, name, toolbar):
''' '''
Adds a toolbar to this toolbox. Toolbar will be added 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 1 greater than the previously added index (index will be
0 if it is the first toolbar added). 0 if it is the first toolbar added).
Args: Args:
name (string): name of toolbar to be added name (string): name of toolbar to be added
toolbar (.. :class:`Gtk.Toolbar`): Gtk.Toolbar to be appended to this toolbox toolbar (.. :class:`Gtk.Toolbar`): Gtk.Toolbar to be appended to this toolbox
''' '''
label = Gtk.Label(label=name) label = Gtk.Label(label=name)
@ -106,7 +106,7 @@ class Toolbox(Gtk.VBox):
def remove_toolbar(self, index): def remove_toolbar(self, index):
''' '''
Removes toolbar at the index specified. Removes toolbar at the index specified.
Args: Args:
index (int): index of the toolbar to be removed index (int): index of the toolbar to be removed
''' '''
@ -118,9 +118,9 @@ class Toolbox(Gtk.VBox):
def set_current_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. displays it.
Args: Args:
index (int): index of toolbar to be set as current toolbar index (int): index of toolbar to be set as current toolbar
''' '''

@ -27,7 +27,7 @@ Example:
from sugar3.graphics.toolbutton import ToolButton from sugar3.graphics.toolbutton import ToolButton
def __clicked_cb(button): def __clicked_cb(button):
print "tool button was clicked" print("tool button was clicked")
w = Gtk.Window() w = Gtk.Window()
w.connect('destroy', Gtk.main_quit) w.connect('destroy', Gtk.main_quit)

@ -330,6 +330,8 @@ class HTray(Gtk.EventBox):
def scroll_to_item(self, item): def scroll_to_item(self, item):
self._viewport.scroll_to_item(item) self._viewport.scroll_to_item(item)
if hasattr(HTray, 'set_css_name'): if hasattr(HTray, 'set_css_name'):
HTray.set_css_name('htray') HTray.set_css_name('htray')
@ -424,6 +426,8 @@ class VTray(Gtk.EventBox):
def scroll_to_item(self, item): def scroll_to_item(self, item):
self._viewport.scroll_to_item(item) self._viewport.scroll_to_item(item)
if hasattr(VTray, 'set_css_name'): if hasattr(VTray, 'set_css_name'):
VTray.set_css_name('VTray') VTray.set_css_name('VTray')

@ -20,6 +20,7 @@ This class represents all of the colors that the XO can take on.
Each pair of colors represents the fill color and the stroke color Each pair of colors represents the fill color and the stroke color
''' '''
import six
import random import random
import logging import logging
@ -210,11 +211,11 @@ colors = [['#B20008', '#FF2B34'],
def _parse_string(color_string): def _parse_string(color_string):
''' '''
Returns array of length 2 of two colors in standard html form of [stroke color, fill color] Returns array of length 2 of two colors in standard html form of [stroke color, fill color]
Args: Args:
color_string (string): two html format strings separated by a comma 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) logging.error('Invalid color string: %r', color_string)
return None return None
@ -233,13 +234,14 @@ def _parse_string(color_string):
class XoColor: class XoColor:
''' '''
Defines color for XO Defines color for XO
Args: Args:
color_string (string): two html format strings separated color_string (string): two html format strings separated
by a comma, "white", or "insensitive". If color_string by a comma, "white", or "insensitive". If color_string
is None, the user's color will be created. If parsed_color is None, the user's color will be created. If parsed_color
cannot be created, a random color will be used cannot be created, a random color will be used
''' '''
def __init__(self, color_string=None): def __init__(self, color_string=None):
parsed_color = None parsed_color = None
@ -261,7 +263,7 @@ class XoColor:
''' '''
Compares two XO colors by their stroke and fill color Compares two XO colors by their stroke and fill color
Returns 0 if they are equal and -1 if they are unequal Returns 0 if they are equal and -1 if they are unequal
Args: Args:
other (object): other XO color to compare other (object): other XO color to compare
''' '''
@ -295,12 +297,12 @@ if __name__ == '__main__':
f = open(sys.argv[1], 'r') f = open(sys.argv[1], 'r')
print 'colors = [' print('colors = [')
for line in f.readlines(): for line in f.readlines():
match = re.match(r'fill: ([A-Z0-9]*) stroke: ([A-Z0-9]*)', line) 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() f.close()

@ -20,16 +20,17 @@
STABLE. STABLE.
""" """
import six
import array import array
import collections import collections
import errno import errno
import logging import logging
import sys import sys
import os import os
import repr as repr_
import decorator import decorator
import time import time
from six.moves import reprlib as repr_
from sugar3 import env from sugar3 import env
# Let's keep this module self contained so that it can be easily # 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): for f in os.listdir(root):
os.remove(os.path.join(root, f)) os.remove(os.path.join(root, f))
os.rmdir(root) os.rmdir(root)
except OSError, e: except OSError as e:
print "Could not remove old logs files %s" % e print("Could not remove old logs files %s" % e)
if len(backup_logs) > 0: if len(backup_logs) > 0:
name = str(int(time.time())) name = str(int(time.time()))
@ -116,7 +117,7 @@ def cleanup():
source_path = os.path.join(logs_dir, log) source_path = os.path.join(logs_dir, log)
dest_path = os.path.join(backup_dir, log) dest_path = os.path.join(backup_dir, log)
os.rename(source_path, dest_path) os.rename(source_path, dest_path)
except OSError, e: except OSError as e:
# gracefully deal w/ disk full # gracefully deal w/ disk full
if e.errno != errno.ENOSPC: if e.errno != errno.ENOSPC:
raise e raise e
@ -145,7 +146,7 @@ def start(log_filename=None):
def write(self, s): def write(self, s):
try: try:
self._stream.write(s) self._stream.write(s)
except IOError, e: except IOError as e:
# gracefully deal w/ disk full # gracefully deal w/ disk full
if e.errno != errno.ENOSPC: if e.errno != errno.ENOSPC:
raise e raise e
@ -153,7 +154,7 @@ def start(log_filename=None):
def flush(self): def flush(self):
try: try:
self._stream.flush() self._stream.flush()
except IOError, e: except IOError as e:
# gracefully deal w/ disk full # gracefully deal w/ disk full
if e.errno != errno.ENOSPC: if e.errno != errno.ENOSPC:
raise e raise e
@ -177,7 +178,7 @@ def start(log_filename=None):
sys.stdout = SafeLogWrapper(sys.stdout) sys.stdout = SafeLogWrapper(sys.stdout)
sys.stderr = SafeLogWrapper(sys.stderr) sys.stderr = SafeLogWrapper(sys.stderr)
except OSError, e: except OSError as e:
# if we're out of space, just continue # if we're out of space, just continue
if e.errno != errno.ENOSPC: if e.errno != errno.ENOSPC:
raise e raise e
@ -188,13 +189,15 @@ def start(log_filename=None):
class TraceRepr(repr_.Repr): class TraceRepr(repr_.Repr):
# better handling of subclasses of basic types, e.g. for DBus # 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] collections.deque, dict, str]
if six.PY2:
_TYPES.append(long)
def repr1(self, x, level): def repr1(self, x, level):
for t in self._TYPES: for t in self._TYPES:
if isinstance(x, t): 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) return repr_.Repr.repr1(self, x, level)
@ -232,14 +235,14 @@ def trace(logger=None, logger_name=None, skip_args=None, skip_kwargs=None,
[trace_repr.repr(a) [trace_repr.repr(a)
for (idx, a) in enumerate(args) if idx not in skip_args] + for (idx, a) in enumerate(args) if idx not in skip_args] +
['%s=%s' % (k, trace_repr.repr(v)) ['%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__, trace_logger.log(TRACE, "%s(%s) invoked", f.__name__,
params_formatted) params_formatted)
try: try:
res = f(*args, **kwargs) res = f(*args, **kwargs)
except: except BaseException:
trace_logger.exception("Exception occurred in %s" % f.__name__) trace_logger.exception("Exception occurred in %s" % f.__name__)
raise raise

@ -32,7 +32,9 @@ from gi.repository import GLib
from gi.repository import GdkPixbuf from gi.repository import GdkPixbuf
from gi.repository import Gio 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_TEXT = 'Text'
GENERIC_TYPE_IMAGE = 'Image' GENERIC_TYPE_IMAGE = 'Image'
@ -185,14 +187,14 @@ def get_mime_parents(mime_type):
with open(subclasses_path) as parents_file: with open(subclasses_path) as parents_file:
for line in parents_file: for line in parents_file:
subclass, parent = line.split() subclass, parent = line.split()
if subclass not in _subclasses.keys(): if subclass not in list(_subclasses.keys()):
_subclasses[subclass] = [parent] _subclasses[subclass] = [parent]
else: else:
_subclasses[subclass].append(parent) _subclasses[subclass].append(parent)
_subclasses_timestamps = timestamps _subclasses_timestamps = timestamps
if mime_type in _subclasses.keys(): if mime_type in list(_subclasses.keys()):
return _subclasses[mime_type] return _subclasses[mime_type]
else: else:
return [] return []

@ -21,14 +21,14 @@ STABLE.
import os import os
import threading import threading
import urllib from six.moves import urllib
import fcntl import fcntl
import tempfile import tempfile
from gi.repository import GObject from gi.repository import GObject
from gi.repository import GLib from gi.repository import GLib
import SimpleHTTPServer from six.moves import SimpleHTTPServer
import SocketServer from six.moves import socketserver
__authinfos = {} __authinfos = {}
@ -46,7 +46,7 @@ def _del_authinfo():
del __authinfos[threading.currentThread()] del __authinfos[threading.currentThread()]
class GlibTCPServer(SocketServer.TCPServer): class GlibTCPServer(socketserver.TCPServer):
"""GlibTCPServer """GlibTCPServer
Integrate socket accept into glib mainloop. Integrate socket accept into glib mainloop.
@ -56,7 +56,7 @@ class GlibTCPServer(SocketServer.TCPServer):
request_queue_size = 20 request_queue_size = 20
def __init__(self, server_address, RequestHandlerClass): def __init__(self, server_address, RequestHandlerClass):
SocketServer.TCPServer.__init__(self, server_address, socketserver.TCPServer.__init__(self, server_address,
RequestHandlerClass) RequestHandlerClass)
self.socket.setblocking(0) # Set nonblocking self.socket.setblocking(0) # Set nonblocking
@ -212,7 +212,7 @@ class GlibURLDownloader(GObject.GObject):
GObject.GObject.__init__(self) GObject.GObject.__init__(self)
def start(self, destfile=None, destfd=None): def start(self, destfile=None, destfd=None):
self._info = urllib.urlopen(self._url) self._info = urllib.request.urlopen(self._url)
self._outf = None self._outf = None
self._fname = None self._fname = None
if destfd and not destfile: if destfd and not destfile:
@ -226,14 +226,14 @@ class GlibURLDownloader(GObject.GObject):
self._outf = destfd self._outf = destfd
else: else:
self._outf = os.open(self._fname, os.O_RDWR | self._outf = os.open(self._fname, os.O_RDWR |
os.O_TRUNC | os.O_CREAT, 0644) os.O_TRUNC | os.O_CREAT, 0o644)
else: else:
fname = self._get_filename_from_headers(self._info.headers) fname = self._get_filename_from_headers(self._info.headers)
self._suggested_fname = fname self._suggested_fname = fname
garbage_, path = urllib.splittype(self._url) garbage_, path = urllib.parse.splittype(self._url)
garbage_, path = urllib.splithost(path or "") garbage_, path = urllib.parse.splithost(path or "")
path, garbage_ = urllib.splitquery(path or "") path, garbage_ = urllib.parse.splitquery(path or "")
path, garbage_ = urllib.splitattr(path or "") path, garbage_ = urllib.parse.splitattr(path or "")
suffix = os.path.splitext(path)[1] suffix = os.path.splitext(path)[1]
(self._outf, self._fname) = tempfile.mkstemp(suffix=suffix, (self._outf, self._fname) = tempfile.mkstemp(suffix=suffix,
dir=self._destdir) dir=self._destdir)
@ -291,7 +291,7 @@ class GlibURLDownloader(GObject.GObject):
self.cleanup() self.cleanup()
self.emit('finished', self._fname, self._suggested_fname) self.emit('finished', self._fname, self._suggested_fname)
return False return False
except Exception, err: except Exception as err:
self.cleanup(remove=True) self.cleanup(remove=True)
self.emit('error', 'Error downloading file: %r' % err) self.emit('error', 'Error downloading file: %r' % err)
return False return False

@ -21,6 +21,7 @@
STABLE. STABLE.
""" """
import six
import logging import logging
from functools import partial from functools import partial
@ -124,6 +125,11 @@ class Activity(GObject.GObject):
def _start_tracking_properties(self): def _start_tracking_properties(self):
bus = dbus.SessionBus() 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._get_properties_call = bus.call_async(
self.telepathy_conn.requested_bus_name, self.telepathy_conn.requested_bus_name,
self.telepathy_conn.object_path, self.telepathy_conn.object_path,
@ -131,9 +137,7 @@ class Activity(GObject.GObject):
'GetProperties', 'GetProperties',
'u', 'u',
(self.room_handle,), (self.room_handle,),
reply_handler=self.__got_properties_cb, arg_dict)
error_handler=self.__error_handler_cb,
utf8_strings=True)
# As only one Activity instance is needed per activity process, # As only one Activity instance is needed per activity process,
# we can afford listening to ActivityPropertiesChanged like this. # we can afford listening to ActivityPropertiesChanged like this.
@ -144,7 +148,7 @@ class Activity(GObject.GObject):
def __activity_properties_changed_cb(self, room_handle, properties): def __activity_properties_changed_cb(self, room_handle, properties):
_logger.debug('%r: Activity properties changed to %r' % (self, _logger.debug('%r: Activity properties changed to %r' % (self,
properties)) properties))
self._update_properties(properties) self._update_properties(properties)
def __got_properties_cb(self, properties): def __got_properties_cb(self, properties):
@ -239,7 +243,7 @@ class Activity(GObject.GObject):
returns list of presence Buddy objects that we can successfully returns list of presence Buddy objects that we can successfully
create from the buddy object paths that PS has for this activity. 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): def get_buddy_by_handle(self, handle):
"""Retrieve the Buddy object given a telepathy handle. """Retrieve the Buddy object given a telepathy handle.
@ -300,8 +304,9 @@ class Activity(GObject.GObject):
channel.connect_to_signal('Closed', self.__text_channel_closed_cb) channel.connect_to_signal('Closed', self.__text_channel_closed_cb)
def __get_all_members_cb(self, members, local_pending, remote_pending): def __get_all_members_cb(self, members, local_pending, remote_pending):
_logger.debug('__get_all_members_cb %r %r' % (members, _logger.debug(
self._text_channel_group_flags)) '__get_all_members_cb %r %r' %
(members, self._text_channel_group_flags))
if self._channel_self_handle in members: if self._channel_self_handle in members:
members.remove(self._channel_self_handle) members.remove(self._channel_self_handle)
@ -631,8 +636,9 @@ class _JoinCommand(_BaseCommand):
self._add_self_to_channel() self._add_self_to_channel()
def __text_channel_group_flags_changed_cb(self, added, removed): def __text_channel_group_flags_changed_cb(self, added, removed):
_logger.debug('__text_channel_group_flags_changed_cb %r %r' % (added, _logger.debug(
removed)) '__text_channel_group_flags_changed_cb %r %r' %
(added, removed))
self.text_channel_group_flags |= added self.text_channel_group_flags |= added
self.text_channel_group_flags &= ~removed self.text_channel_group_flags &= ~removed

@ -22,7 +22,7 @@ STABLE.
""" """
import logging import logging
import six
from gi.repository import GObject from gi.repository import GObject
import dbus import dbus
from telepathy.interfaces import CONNECTION, \ from telepathy.interfaces import CONNECTION, \
@ -103,7 +103,7 @@ class BaseBuddy(GObject.GObject):
def get_current_activity(self): def get_current_activity(self):
if self._current_activity is None: if self._current_activity is None:
return None return None
for activity in self._activities.values(): for activity in list(self._activities.values()):
if activity.props.id == self._current_activity: if activity.props.id == self._current_activity:
return activity return activity
return None return None
@ -164,6 +164,12 @@ class Buddy(BaseBuddy):
dbus_interface=CONNECTION) dbus_interface=CONNECTION)
self.contact_handle = handles[0] 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( self._get_properties_call = bus.call_async(
connection_name, connection_name,
connection.object_path, connection.object_path,
@ -171,10 +177,7 @@ class Buddy(BaseBuddy):
'GetProperties', 'GetProperties',
'u', 'u',
(self.contact_handle,), (self.contact_handle,),
reply_handler=self.__got_properties_cb, arg_dict)
error_handler=self.__error_handler_cb,
utf8_strings=True,
byte_arrays=True)
self._get_attributes_call = bus.call_async( self._get_attributes_call = bus.call_async(
connection_name, connection_name,
@ -245,4 +248,3 @@ class Owner(BaseBuddy):
self.props.nick = get_nick_name() self.props.nick = get_nick_name()
self.props.color = get_color().to_string() self.props.color = get_color().to_string()

@ -92,7 +92,8 @@ class ConnectionManager(object):
def get_preferred_connection(self): def get_preferred_connection(self):
best_connection = None, None 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: if 'salut' in account_path and connection.connected:
best_connection = account_path, connection.connection best_connection = account_path, connection.connection
elif 'gabble' in account_path and connection.connected: elif 'gabble' in account_path and connection.connected:
@ -107,7 +108,8 @@ class ConnectionManager(object):
return self._connections_per_account return self._connections_per_account
def get_account_for_connection(self, connection_path): 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: if connection.connection.object_path == connection_path:
return account_path return account_path
return None return None

@ -78,7 +78,8 @@ class PresenceService(GObject.GObject):
connection_manager = get_connection_manager() connection_manager = get_connection_manager()
connections_per_account = \ connections_per_account = \
connection_manager.get_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: if not connection.connected:
continue continue
logging.debug('Calling GetActivity on %s' % account_path) logging.debug('Calling GetActivity on %s' % account_path)
@ -86,13 +87,13 @@ class PresenceService(GObject.GObject):
room_handle = connection.connection.GetActivity( room_handle = connection.connection.GetActivity(
activity_id, activity_id,
dbus_interface=CONN_INTERFACE_ACTIVITY_PROPERTIES) dbus_interface=CONN_INTERFACE_ACTIVITY_PROPERTIES)
except dbus.exceptions.DBusException, e: except dbus.exceptions.DBusException as e:
name = 'org.freedesktop.Telepathy.Error.NotAvailable' name = 'org.freedesktop.Telepathy.Error.NotAvailable'
if e.get_dbus_name() == name: if e.get_dbus_name() == name:
logging.debug("There's no shared activity with the id " logging.debug("There's no shared activity with the id "
"%s" % activity_id) "%s" % activity_id)
elif e.get_dbus_name() == \ elif e.get_dbus_name() == \
'org.freedesktop.DBus.Error.UnknownMethod': 'org.freedesktop.DBus.Error.UnknownMethod':
logging.warning( logging.warning(
'Telepathy Account %r does not support ' 'Telepathy Account %r does not support '
'Sugar collaboration', account_path) 'Sugar collaboration', account_path)

@ -24,6 +24,7 @@ __all__ = ('TubeConnection', )
__docformat__ = 'reStructuredText' __docformat__ = 'reStructuredText'
import six
import logging import logging
from dbus.connection import Connection from dbus.connection import Connection
@ -77,7 +78,9 @@ class TubeConnection(Connection):
def close(self): def close(self):
self._dbus_names_changed_match.remove() 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() super(TubeConnection, self).close()
def _on_get_dbus_names_reply(self, names): def _on_get_dbus_names_reply(self, names):
@ -111,6 +114,6 @@ class TubeConnection(Connection):
# GetDBusNames already returned: fake a participant add event # GetDBusNames already returned: fake a participant add event
# immediately # immediately
added = [] added = []
for k, v in self.participants.iteritems(): for k, v in six.iteritems(self.participants):
added.append((k, v)) added.append((k, v))
callback(added, []) callback(added, [])

@ -21,7 +21,8 @@
from gi.repository import Gio from gi.repository import Gio
import os import os
import logging import logging
from ConfigParser import ConfigParser
from six.moves.configparser import ConfigParser
from sugar3 import env from sugar3 import env
from sugar3 import util from sugar3 import util

@ -33,7 +33,7 @@ try:
from gi.repository import Gst from gi.repository import Gst
Gst.init(None) Gst.init(None)
Gst.parse_launch('espeak') Gst.parse_launch('espeak')
except: except BaseException:
logging.error('Gst or the espeak plugin is not installed in the system.') logging.error('Gst or the espeak plugin is not installed in the system.')
_HAS_GST = False _HAS_GST = False
@ -268,7 +268,12 @@ class SpeechManager(GObject.GObject):
else: else:
voice_name = self._player.get_all_voices()[lang_code] voice_name = self._player.get_all_voices()[lang_code]
if text: 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) self._player.speak(pitch, rate, voice_name, text)
def say_selected_text(self): def say_selected_text(self):

@ -3,4 +3,4 @@ sugar_PYTHON = \
__init__.py \ __init__.py \
discover.py \ discover.py \
uitree.py \ uitree.py \
unittest.py _unittest.py

@ -18,8 +18,6 @@
UNSTABLE. UNSTABLE.
""" """
from __future__ import absolute_import
import logging import logging
import os import os
import unittest import unittest
@ -52,11 +50,11 @@ class UITestCase(unittest.TestCase):
@contextmanager @contextmanager
def run_view(self, name): def run_view(self, name):
view_path = os.path.join("views", "%s.py" % name) view_path = os.path.join("views", "%s.py" % name)
process = subprocess.Popen(["python", view_path]) process = subprocess.Popen(["python3", view_path])
try: try:
yield yield
except: except BaseException:
logging.debug(uitree.get_root().dump()) logging.debug(uitree.get_root().dump())
raise raise
finally: finally:
@ -77,12 +75,12 @@ class UITestCase(unittest.TestCase):
cmd += options cmd += options
process = subprocess.Popen(cmd) process = subprocess.Popen(cmd)
else: else:
print "No bundle_id specified." print("No bundle_id specified.")
return return
try: try:
yield yield
except: except BaseException:
logging.debug(uitree.get_root().dump()) logging.debug(uitree.get_root().dump())
raise raise
finally: finally:

@ -18,8 +18,6 @@
UNSTABLE. UNSTABLE.
""" """
from __future__ import absolute_import
import argparse import argparse
import sys import sys
import os import os

@ -44,7 +44,7 @@ def _retry_find(func):
try: try:
result = func(*args, **kwargs) result = func(*args, **kwargs)
except GLib.GError, e: except GLib.GError as e:
# The application is not responding, try again # The application is not responding, try again
if e.code == Atspi.Error.IPC: if e.code == Atspi.Error.IPC:
continue continue

@ -20,6 +20,7 @@
UNSTABLE. We have been adding helpers randomly to this module. UNSTABLE. We have been adding helpers randomly to this module.
""" """
import six
import os import os
import time import time
import hashlib import hashlib
@ -31,20 +32,26 @@ import logging
import atexit import atexit
_ = lambda msg: gettext.dgettext('sugar-toolkit-gtk3', msg) def _(msg): return gettext.dgettext('sugar-toolkit-gtk3', msg)
def printable_hash(in_hash): def printable_hash(in_hash):
"""Convert binary hash data into printable characters.""" """Convert binary hash data into printable characters."""
printable = "" printable = ""
for char in in_hash: 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 return printable
def sha_data(data): def sha_data(data):
"""sha1 hash some bytes.""" """sha1 hash some bytes."""
sha_hash = hashlib.sha1() sha_hash = hashlib.sha1()
if six.PY3:
data = data.encode('utf-8')
sha_hash.update(data) sha_hash.update(data)
return sha_hash.digest() return sha_hash.digest()
@ -81,7 +88,7 @@ def is_hex(s):
def validate_activity_id(actid): def validate_activity_id(actid):
"""Validate an activity ID.""" """Validate an activity ID."""
if not isinstance(actid, (str, unicode)): if not isinstance(actid, (six.binary_type, six.text_type)):
return False return False
if len(actid) != ACTIVITY_ID_LEN: if len(actid) != ACTIVITY_ID_LEN:
return False return False
@ -204,7 +211,7 @@ class LRU:
yield j yield j
def keys(self): def keys(self):
return self.d.keys() return list(self.d.keys())
units = [['%d year', '%d years', 356 * 24 * 60 * 60], units = [['%d year', '%d years', 356 * 24 * 60 * 60],
@ -331,13 +338,14 @@ class TempFilePath(str):
def _cleanup_temp_files(): def _cleanup_temp_files():
logging.debug('_cleanup_temp_files') logging.debug('_cleanup_temp_files')
for path in _tracked_paths.keys(): for path in list(_tracked_paths.keys()):
try: try:
os.unlink(path) os.unlink(path)
except: except BaseException:
# pylint: disable=W0702 # pylint: disable=W0702
logging.exception('Exception occurred in _cleanup_temp_files') logging.exception('Exception occurred in _cleanup_temp_files')
atexit.register(_cleanup_temp_files) atexit.register(_cleanup_temp_files)

@ -2,6 +2,6 @@
name = Sample name = Sample
activity_version = 1 activity_version = 1
bundle_id = org.sugarlabs.Sample bundle_id = org.sugarlabs.Sample
exec = sugar-activity activity.SampleActivity exec = sugar-activity3 activity.SampleActivity
icon = activity-sample icon = activity-sample
license = GPLv2+ license = GPLv2+

@ -1,4 +1,4 @@
#!/usr/bin/env python2 #!/usr/bin/env python3
from sugar3.activity import bundlebuilder from sugar3.activity import bundlebuilder

@ -59,6 +59,7 @@ def timeout_cb():
return False return False
return True return True
GLib.timeout_add(50, timeout_cb) GLib.timeout_add(50, timeout_cb)
if __name__ == '__main__': if __name__ == '__main__':

@ -1,4 +1,4 @@
#!/usr/bin/env python2 #!/usr/bin/env python3
# Copyright (C) 2006, Red Hat, Inc. # Copyright (C) 2006, Red Hat, Inc.
# Copyright (C) 2007, One Laptop Per Child # Copyright (C) 2007, One Laptop Per Child

@ -23,7 +23,7 @@ from sugar3.test import uitree
class TestUITree(unittest.TestCase): class TestUITree(unittest.TestCase):
def test_tree(self): def test_tree(self):
process = subprocess.Popen(["python", __file__, "show_window1"]) process = subprocess.Popen(["python3", __file__, "show_window1"])
try: try:
root = uitree.get_root() root = uitree.get_root()
@ -49,5 +49,6 @@ def show_window1():
Gtk.main() Gtk.main()
if __name__ == '__main__': if __name__ == '__main__':
globals()[sys.argv[1]]() globals()[sys.argv[1]]()

Loading…
Cancel
Save