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
pyflakes $(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
# Copyright (C) 2006-2008, Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
from sugar3.activity import activityinstance
import os
import sys
# Change the default encoding to avoid UnicodeDecodeError
# http://lists.sugarlabs.org/archive/sugar-devel/2012-August/038928.html
reload(sys)
sys.setdefaultencoding('utf-8')
import gettext
from optparse import OptionParser
import dbus
import dbus.service
from dbus.mainloop.glib import DBusGMainLoop
DBusGMainLoop(set_as_default=True)
from sugar3.activity import activityhandle
from sugar3 import config
from sugar3.bundle.activitybundle import ActivityBundle
from sugar3 import logger
from sugar3.bundle.bundle import MalformedBundleException
from distutils.dir_util import mkpath
import time
import hashlib
import random
def create_activity_instance(constructor, handle):
activity = constructor(handle)
activity.show()
return activity
def get_single_process_name(bundle_id):
return bundle_id
def get_single_process_path(bundle_id):
return '/' + bundle_id.replace('.', '/')
class SingleProcess(dbus.service.Object):
def __init__(self, name_service, constructor):
self.constructor = constructor
bus = dbus.SessionBus()
bus_name = dbus.service.BusName(name_service, bus=bus)
object_path = get_single_process_path(name_service)
dbus.service.Object.__init__(self, bus_name, object_path)
@dbus.service.method('org.laptop.SingleProcess', in_signature='a{sv}')
def create(self, handle_dict):
handle = activityhandle.create_from_dict(handle_dict)
create_activity_instance(self.constructor, handle)
def main():
usage = 'usage: %prog [options] [activity dir] [python class]'
epilog = 'If you are running from a directory containing an Activity, ' \
'the argument may be omitted. Otherwise please provide either '\
'a directory containing a Sugar Activity [activity dir], a '\
'[python_class], or both.'
parser = OptionParser(usage=usage, epilog=epilog)
parser.add_option('-b', '--bundle-id', dest='bundle_id',
help='identifier of the activity bundle')
parser.add_option('-a', '--activity-id', dest='activity_id',
help='identifier of the activity instance')
parser.add_option('-o', '--object-id', dest='object_id',
help='identifier of the associated datastore object')
parser.add_option('-u', '--uri', dest='uri',
help='URI to load')
parser.add_option('-s', '--single-process', dest='single_process',
action='store_true',
help='start all the instances in the same process')
parser.add_option('-i', '--invited', dest='invited',
action='store_true', default=False,
help='the activity is being launched for handling an '
'invite from the network')
(options, args) = parser.parse_args()
logger.start()
activity_class = None
if len(args) == 2:
activity_class = args[1]
os.chdir(args[0])
elif len(args) == 1:
if os.path.isdir(args[0]):
os.chdir(args[0])
else:
activity_class = args[0]
os.environ['SUGAR_BUNDLE_PATH'] = os.path.abspath(os.curdir)
bundle_path = os.environ['SUGAR_BUNDLE_PATH']
sys.path.insert(0, bundle_path)
try:
bundle = ActivityBundle(bundle_path)
except MalformedBundleException:
parser.print_help()
exit(0)
if not activity_class:
if bundle.get_command().startswith('sugar-activity'):
activity_class = bundle.get_command().split(" ")[1]
if 'SUGAR_VERSION' not in os.environ:
profile_id = os.environ.get('SUGAR_PROFILE', 'default')
home_dir = os.environ.get('SUGAR_HOME', os.path.expanduser('~/.sugar'))
base = os.path.join(home_dir, profile_id)
activity_root = os.path.join(base, bundle.get_bundle_id())
instance_dir = os.path.join(activity_root, 'instance')
mkpath(instance_dir)
data_dir = os.path.join(activity_root, 'data')
mkpath(data_dir)
tmp_dir = os.path.join(activity_root, 'tmp')
mkpath(tmp_dir)
os.environ['SUGAR_ACTIVITY_ROOT'] = activity_root
os.environ['SUGAR_BUNDLE_PATH'] = bundle.get_path()
os.environ['SUGAR_BUNDLE_ID'] = bundle.get_bundle_id()
os.environ['SUGAR_BUNDLE_NAME'] = bundle.get_name()
os.environ['SUGAR_BUNDLE_VERSION'] = str(bundle.get_activity_version())
# must be done early, some activities set translations globally, SL #3654
activity_locale_path = os.environ.get("SUGAR_LOCALEDIR",
config.locale_path)
gettext.bindtextdomain(bundle.get_bundle_id(), activity_locale_path)
gettext.bindtextdomain('sugar-toolkit-gtk3', config.locale_path)
gettext.textdomain(bundle.get_bundle_id())
splitted_module = activity_class.rsplit('.', 1)
module_name = splitted_module[0]
class_name = splitted_module[1]
module = __import__(module_name)
for comp in module_name.split('.')[1:]:
module = getattr(module, comp)
activity_constructor = getattr(module, class_name)
if not options.activity_id:
# Generate random hash
data = '%s%s' % (time.time(), random.randint(10000, 100000))
random_hash = hashlib.sha1(data).hexdigest()
options.activity_id = random_hash
options.bundle_id = bundle.get_bundle_id()
activity_handle = activityhandle.ActivityHandle(
activity_id=options.activity_id,
object_id=options.object_id, uri=options.uri,
invited=options.invited)
if options.single_process is True:
sessionbus = dbus.SessionBus()
service_name = get_single_process_name(options.bundle_id)
service_path = get_single_process_path(options.bundle_id)
bus_object = sessionbus.get_object(
'org.freedesktop.DBus', '/org/freedesktop/DBus')
try:
name = bus_object.GetNameOwner(
service_name, dbus_interface='org.freedesktop.DBus')
except dbus.DBusException:
name = None
if not name:
SingleProcess(service_name, activity_constructor)
else:
try:
single_process = sessionbus.get_object(service_name,
service_path)
single_process.create(
activity_handle.get_dict(),
dbus_interface='org.laptop.SingleProcess')
print 'Created %s in a single process.' % service_name
sys.exit(0)
except (TypeError, dbus.DBusException):
print 'Could not communicate with the instance process,' \
'launching a new process'
if hasattr(module, 'start'):
module.start()
instance = create_activity_instance(activity_constructor, activity_handle)
if hasattr(instance, 'run_main_loop'):
instance.run_main_loop()
main()
activityinstance.main()

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

@ -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])
PYTHON=python2
PYTHON=python3
AM_PATH_PYTHON
PKG_CHECK_MODULES(EXT, gtk+-3.0 gdk-3.0 gdk-pixbuf-2.0 sm ice alsa

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

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

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

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

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

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

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

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

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

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

@ -21,7 +21,7 @@ toolbar_box.toolbar.insert(separator, -1)
def __clicked_cb(button):
n = int(button.get_tooltip())
button.set_tooltip(str(n + 1))
print "tool button click count %d" % n
print("tool button click count %d" % n)
tool_button = ToolButton(icon_name='view-radial', tooltip='0')

@ -2,6 +2,7 @@ sugardir = $(pythondir)/sugar3/activity
sugar_PYTHON = \
__init__.py \
activity.py \
activityinstance.py \
activityfactory.py \
activityhandle.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.
'''
import six
import gettext
import logging
import os
@ -165,7 +166,6 @@ import signal
import time
from hashlib import sha1
from functools import partial
import StringIO
import cairo
import json
@ -206,7 +206,9 @@ from errno import EEXIST
from gi.repository import SugarExt
_ = lambda msg: gettext.dgettext('sugar-toolkit-gtk3', msg)
def _(msg): return gettext.dgettext('sugar-toolkit-gtk3', msg)
SCOPE_PRIVATE = 'private'
SCOPE_INVITE_ONLY = 'invite' # shouldn't be shown in UI, it's implicit
@ -881,7 +883,7 @@ class Activity(Window, Gtk.Container):
cr.set_source_surface(screenshot_surface)
cr.paint()
preview_str = StringIO.StringIO()
preview_str = six.BytesIO()
preview_surface.write_to_png(preview_str)
return preview_str.getvalue()
@ -919,7 +921,7 @@ class Activity(Window, Gtk.Container):
buddies_dict = self._get_buddies()
if buddies_dict:
self.metadata['buddies_id'] = json.dumps(buddies_dict.keys())
self.metadata['buddies_id'] = json.dumps(list(buddies_dict.keys()))
self.metadata['buddies'] = json.dumps(self._get_buddies())
# update spent time before saving
@ -1103,8 +1105,9 @@ class Activity(Window, Gtk.Container):
raise RuntimeError('Activity %s already shared.' %
self._activity_id)
verb = private and 'private' or 'public'
logging.debug('Requesting %s share of activity %s.' % (verb,
self._activity_id))
logging.debug(
'Requesting %s share of activity %s.' %
(verb, self._activity_id))
pservice = presenceservice.get_instance()
pservice.connect('activity-shared', self.__share_cb)
pservice.share_activity(self, private=private)
@ -1244,7 +1247,7 @@ class Activity(Window, Gtk.Container):
if not skip_save:
try:
self.save()
except:
except BaseException:
# pylint: disable=W0702
logging.exception('Error saving activity object to datastore')
self._show_keep_failed_dialog()
@ -1448,9 +1451,10 @@ class _ClientHandler(dbus.service.Object, DBusProperties):
handle_type = properties[CHANNEL + '.TargetHandleType']
if channel_type == CHANNEL_TYPE_TEXT:
self._got_channel_cb(connection, object_path, handle_type)
except Exception, e:
except Exception as e:
logging.exception(e)
_session = None
@ -1503,7 +1507,7 @@ def get_activity_root():
activity_root = env.get_profile_path(os.environ['SUGAR_BUNDLE_ID'])
try:
os.mkdir(activity_root)
except OSError, e:
except OSError as e:
if e.errno != EEXIST:
raise e
return activity_root

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

@ -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):
try:
self._activity.get_document_path(async_cb, async_err_cb)
except Exception, e:
except Exception as e:
async_err_cb(e)

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

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

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

@ -44,7 +44,9 @@ an `[Activity]` header on the first line:
* `icon` - the icon file for the activity, shown by Sugar in the list
of installed activities,
* `exec` - how to execute the activity, e.g. `sugar-activity module.Class`,
* `exec` - how to execute the activity, e.g.
`sugar-activity3 module.Class` (For activities written for Python 3),
`sugar-activity module.Class` (For activities written for Python 2)
Optional metadata keys are;
@ -117,7 +119,7 @@ Example `activity.info`
[Activity]
name = Browse
bundle_id = org.laptop.WebActivity
exec = sugar-activity webactivity.WebActivity -s
exec = sugar-activity3 webactivity.WebActivity -s
activity_version = 200
icon = activity-web
max_participants = 100

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

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

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

@ -21,10 +21,11 @@
UNSTABLE.
"""
from ConfigParser import ConfigParser
from six.moves import urllib
from six.moves.configparser import ConfigParser
import tempfile
import os
import urllib
from sugar3 import env
from sugar3.bundle.bundle import Bundle, MalformedBundleException
@ -142,7 +143,7 @@ class ContentBundle(Bundle):
def get_start_uri(self):
path = os.path.join(self.get_path(), self._activity_start)
return 'file://' + urllib.pathname2url(path)
return 'file://' + urllib.request.pathname2url(path)
def get_bundle_id(self):
return self._global_name

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

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

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

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

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

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

@ -29,7 +29,7 @@ from sugar3.graphics.icon import Icon
from sugar3.graphics.palette import Palette, ToolInvoker, WidgetInvoker
_ = lambda msg: gettext.dgettext('sugar-toolkit-gtk3', msg)
def _(msg): return gettext.dgettext('sugar-toolkit-gtk3', msg)
if not hasattr(GObject.ParamFlags, 'READWRITE'):
@ -38,8 +38,8 @@ if not hasattr(GObject.ParamFlags, 'READWRITE'):
def get_svg_color_string(color):
return '#%.2X%.2X%.2X' % (color.red / 257, color.green / 257,
color.blue / 257)
return '#%.2X%.2X%.2X' % (color.red // 257, color.green // 257,
color.blue // 257)
class _ColorButton(Gtk.Button):
@ -123,8 +123,8 @@ class _ColorButton(Gtk.Button):
context = self.get_style_context()
fg_color = context.get_color(Gtk.StateType.NORMAL)
# the color components are stored as float values between 0.0 and 1.0
return '#%.2X%.2X%.2X' % (fg_color.red * 255, fg_color.green * 255,
fg_color.blue * 255)
return '#%.2X%.2X%.2X' % (int(fg_color.red * 255), int(fg_color.green * 255),
int(fg_color.blue * 255))
def set_color(self, color):
assert isinstance(color, Gdk.Color)
@ -169,11 +169,11 @@ class _ColorButton(Gtk.Button):
icon_name = GObject.Property(type=str,
getter=get_icon_name, setter=set_icon_name)
def set_icon_size(self, icon_size):
self._preview.props.icon_size = icon_size
def set_icon_size(self, pixel_size):
self._preview.props.pixel_size = pixel_size
def get_icon_size(self):
return self._preview.props.icon_size
return self._preview.props.pixel_size
icon_size = GObject.Property(type=int,
getter=get_icon_size, setter=set_icon_size)

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

@ -61,7 +61,7 @@ class IconEntry(Gtk.Entry):
self.set_icon(position, pixbuf)
def set_icon(self, position, pixbuf):
if type(pixbuf) is not GdkPixbuf.Pixbuf:
if not isinstance(pixbuf, GdkPixbuf.Pixbuf):
raise ValueError('Argument must be a pixbuf, not %r.' % pixbuf)
self.set_icon_from_pixbuf(position, pixbuf)

@ -19,8 +19,8 @@
STABLE.
"""
import six
import logging
import StringIO
import cairo
from gi.repository import GObject
@ -82,7 +82,7 @@ def get_preview_pixbuf(preview_data, width=-1, height=-1):
import base64
preview_data = base64.b64decode(preview_data)
png_file = StringIO.StringIO(preview_data)
png_file = six.StringIO(preview_data)
try:
# Load image and scale to dimensions
surface = cairo.ImageSurface.create_from_png(png_file)

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

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

@ -43,6 +43,7 @@ from sugar3.graphics.icon import CellRendererIcon
_pointer = None
def _get_pointer_position(widget):
global _pointer
@ -53,6 +54,7 @@ def _get_pointer_position(widget):
screen, x, y = _pointer.get_position()
return (x, y)
def _calculate_gap(a, b):
"""Helper function to find the gap position and size of widget a"""
# Test for each side if the palette and invoker are
@ -420,6 +422,8 @@ class _PaletteWindowWidget(Gtk.Window):
self.disconnect_by_func(self.__enter_notify_event_cb)
self.disconnect_by_func(self.__leave_notify_event_cb)
self.hide()
if hasattr(_PaletteWindowWidget, 'set_css_name'):
_PaletteWindowWidget.set_css_name('palette')
@ -1325,9 +1329,9 @@ class CursorInvoker(Invoker):
def detach(self):
Invoker.detach(self)
self._item.disconnect(self._enter_hid)
self._item.disconnect(self._leave_hid)
self._item.disconnect(self._release_hid)
self._item.disconnect_by_func(self.__enter_notify_event_cb)
self._item.disconnect_by_func(self.__leave_notify_event_cb)
self._item.disconnect_by_func(self.__button_release_event_cb)
self._long_pressed_controller.detach(self._item)
self._long_pressed_controller.disconnect(self._long_pressed_hid)

@ -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
[e.g. fill_color=style.COLOR_BLUE.get_svg()
'''
def __init__(self, icon_name, pixel_size, stroke_color, fill_color,
direction='vertical'):
Gtk.DrawingArea.__init__(self)

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

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

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

@ -330,6 +330,8 @@ class HTray(Gtk.EventBox):
def scroll_to_item(self, item):
self._viewport.scroll_to_item(item)
if hasattr(HTray, 'set_css_name'):
HTray.set_css_name('htray')
@ -424,6 +426,8 @@ class VTray(Gtk.EventBox):
def scroll_to_item(self, item):
self._viewport.scroll_to_item(item)
if hasattr(VTray, 'set_css_name'):
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
'''
import six
import random
import logging
@ -214,7 +215,7 @@ def _parse_string(color_string):
Args:
color_string (string): two html format strings separated by a comma
'''
if not isinstance(color_string, (str, unicode)):
if not isinstance(color_string, (six.text_type, six.binary_type)):
logging.error('Invalid color string: %r', color_string)
return None
@ -240,6 +241,7 @@ class XoColor:
is None, the user's color will be created. If parsed_color
cannot be created, a random color will be used
'''
def __init__(self, color_string=None):
parsed_color = None
@ -295,12 +297,12 @@ if __name__ == '__main__':
f = open(sys.argv[1], 'r')
print 'colors = ['
print('colors = [')
for line in f.readlines():
match = re.match(r'fill: ([A-Z0-9]*) stroke: ([A-Z0-9]*)', line)
print "['#%s', '#%s'], \\" % (match.group(2), match.group(1))
print("['#%s', '#%s'], \\" % (match.group(2), match.group(1)))
print ']'
print(']')
f.close()

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

@ -32,7 +32,9 @@ from gi.repository import GLib
from gi.repository import GdkPixbuf
from gi.repository import Gio
_ = lambda msg: gettext.dgettext('sugar-toolkit-gtk3', msg)
def _(msg): return gettext.dgettext('sugar-toolkit-gtk3', msg)
GENERIC_TYPE_TEXT = 'Text'
GENERIC_TYPE_IMAGE = 'Image'
@ -185,14 +187,14 @@ def get_mime_parents(mime_type):
with open(subclasses_path) as parents_file:
for line in parents_file:
subclass, parent = line.split()
if subclass not in _subclasses.keys():
if subclass not in list(_subclasses.keys()):
_subclasses[subclass] = [parent]
else:
_subclasses[subclass].append(parent)
_subclasses_timestamps = timestamps
if mime_type in _subclasses.keys():
if mime_type in list(_subclasses.keys()):
return _subclasses[mime_type]
else:
return []

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

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

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

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

@ -78,7 +78,8 @@ class PresenceService(GObject.GObject):
connection_manager = get_connection_manager()
connections_per_account = \
connection_manager.get_connections_per_account()
for account_path, connection in connections_per_account.items():
for account_path, connection in list(
connections_per_account.items()):
if not connection.connected:
continue
logging.debug('Calling GetActivity on %s' % account_path)
@ -86,7 +87,7 @@ class PresenceService(GObject.GObject):
room_handle = connection.connection.GetActivity(
activity_id,
dbus_interface=CONN_INTERFACE_ACTIVITY_PROPERTIES)
except dbus.exceptions.DBusException, e:
except dbus.exceptions.DBusException as e:
name = 'org.freedesktop.Telepathy.Error.NotAvailable'
if e.get_dbus_name() == name:
logging.debug("There's no shared activity with the id "

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

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

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

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

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

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

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

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

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

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

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

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

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

Loading…
Cancel
Save