Port from Python 2 to six

Signed-off-by: James Cameron <quozl@laptop.org>
master
Pro-Panda 5 years ago committed by James Cameron
parent 6345da8e07
commit aa8a5e70c4
  1. 2
      Makefile.am
  2. 2
      bin/Makefile.am
  3. 218
      bin/sugar-activity
  4. 4
      bin/sugar-activity-web
  5. 5
      bin/sugar-activity3
  6. 2
      configure.ac
  7. 26
      doc/conf.py
  8. 6
      examples/alert.py
  9. 2
      examples/animator.py
  10. 2
      examples/colorbutton.py
  11. 2
      examples/combobox.py
  12. 6
      examples/customdestroy.py
  13. 2
      examples/gtktreesensitive.py
  14. 4
      examples/iconentry.py
  15. 2
      examples/radiotoolbutton.py
  16. 6
      examples/scrollingdetector.py
  17. 2
      examples/tabs.py
  18. 2
      examples/toolbutton.py
  19. 1
      src/sugar3/activity/Makefile.am
  20. 34
      src/sugar3/activity/activity.py
  21. 13
      src/sugar3/activity/activityfactory.py
  22. 14
      src/sugar3/activity/activityhandle.py
  23. 222
      src/sugar3/activity/activityinstance.py
  24. 2
      src/sugar3/activity/activityservice.py
  25. 44
      src/sugar3/activity/bundlebuilder.py
  26. 8
      src/sugar3/activity/webkit1.py
  27. 2
      src/sugar3/activity/widgets.py
  28. 6
      src/sugar3/bundle/__init__.py
  29. 4
      src/sugar3/bundle/activitybundle.py
  30. 10
      src/sugar3/bundle/bundle.py
  31. 1
      src/sugar3/bundle/bundleversion.py
  32. 7
      src/sugar3/bundle/contentbundle.py
  33. 17
      src/sugar3/datastore/datastore.py
  34. 20
      src/sugar3/dispatch/dispatcher.py
  35. 48
      src/sugar3/dispatch/saferef.py
  36. 4
      src/sugar3/env.py
  37. 1
      src/sugar3/graphics/Makefile.am
  38. 22
      src/sugar3/graphics/alert.py
  39. 42
      src/sugar3/graphics/colorbutton.py
  40. 21
      src/sugar3/graphics/icon.py
  41. 2
      src/sugar3/graphics/iconentry.py
  42. 24
      src/sugar3/graphics/objectchooser.py
  43. 2
      src/sugar3/graphics/palettegroup.py
  44. 2
      src/sugar3/graphics/palettemenu.py
  45. 14
      src/sugar3/graphics/palettewindow.py
  46. 202
      src/sugar3/graphics/popwindow.py
  47. 1
      src/sugar3/graphics/progressicon.py
  48. 12
      src/sugar3/graphics/scrollingdetector.py
  49. 5
      src/sugar3/graphics/style.py
  50. 2
      src/sugar3/graphics/toolbarbox.py
  51. 14
      src/sugar3/graphics/toolbox.py
  52. 2
      src/sugar3/graphics/toolbutton.py
  53. 4
      src/sugar3/graphics/tray.py
  54. 24
      src/sugar3/graphics/xocolor.py
  55. 25
      src/sugar3/logger.py
  56. 8
      src/sugar3/mime.py
  57. 24
      src/sugar3/network.py
  58. 24
      src/sugar3/presence/activity.py
  59. 16
      src/sugar3/presence/buddy.py
  60. 6
      src/sugar3/presence/connectionmanager.py
  61. 7
      src/sugar3/presence/presenceservice.py
  62. 7
      src/sugar3/presence/tubeconn.py
  63. 3
      src/sugar3/profile.py
  64. 9
      src/sugar3/speech.py
  65. 2
      src/sugar3/test/Makefile.am
  66. 10
      src/sugar3/test/_unittest.py
  67. 2
      src/sugar3/test/discover.py
  68. 2
      src/sugar3/test/uitree.py
  69. 20
      src/sugar3/util.py
  70. 2
      tests/data/sample.activity/activity/activity.info
  71. 2
      tests/data/sample.activity/setup.py
  72. 1
      tests/graphics/progressicon.py
  73. 2
      tests/test_mime.py
  74. 3
      tests/test_uitree.py

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

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

@ -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)
@ -1197,8 +1200,8 @@ class Activity(Window, Gtk.Container):
if response_id == Gtk.ResponseType.OK:
title = alert.entry.get_text()
if self._is_resumed and \
title == self._original_title:
datastore.delete(self._jobject_old.get_object_id())
title == self._original_title:
datastore.delete(self._jobject_old.get_object_id())
self._jobject.metadata['title'] = title
self._do_close(False)
@ -1222,7 +1225,7 @@ class Activity(Window, Gtk.Container):
label = _('Save new')
tip = _('Save a new journal entry')
if self._is_resumed and \
title == self._original_title:
title == self._original_title:
label = _('Save')
tip = _('Save into the old journal entry')
@ -1244,7 +1247,7 @@ 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()
@ -1306,12 +1309,12 @@ class Activity(Window, Gtk.Container):
def __realize_cb(self, window):
display_name = Gdk.Display.get_default().get_name()
if ':' in display_name:
if ':' in display_name:
# X11 for sure; this only works in X11
xid = window.get_window().get_xid()
SugarExt.wm_set_bundle_id(xid, self.get_bundle_id())
SugarExt.wm_set_activity_id(xid, str(self._activity_id))
elif display_name is 'Broadway':
elif display_name is 'Broadway':
# GTK3's HTML5 backend
# This is needed so that the window takes the whole browser window
self.maximize()
@ -1432,7 +1435,7 @@ class _ClientHandler(dbus.service.Object, DBusProperties):
}
filter_dict = dbus.Dictionary(filters, signature='sv')
logging.debug('__get_filters_cb %r' % dbus.Array([filter_dict],
signature='a{sv}'))
signature='a{sv}'))
return dbus.Array([filter_dict], signature='a{sv}')
@dbus.service.method(dbus_interface=CLIENT_HANDLER,
@ -1448,9 +1451,10 @@ 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()),

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

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