Docstrings for modules all over sugar and shell.
These are just the doc strings I created as I was spelunking through to see how Sugar manages launching applications. The resulting auto-documentation is neither polished or finished, but it should help people reading the code somewhat. There are a few minor code cleanups: * activityhandle (replacing C idiom for initialisation with a Python one) * bundle registry (using a parameterised directory name so that it shows up in the documentation) * validate_activity_id function, use isinstance( item, (str,unicode)) for the query, rather than two separate checks with isinstance
This commit is contained in:
parent
508a59b99b
commit
3f10890319
@ -25,6 +25,13 @@ from sugar.presence import presenceservice
|
|||||||
from sugar import profile
|
from sugar import profile
|
||||||
|
|
||||||
class HomeActivity(gobject.GObject):
|
class HomeActivity(gobject.GObject):
|
||||||
|
"""Activity which appears in the "Home View" of the Sugar shell
|
||||||
|
|
||||||
|
This class stores the Sugar Shell's metadata regarding a
|
||||||
|
given activity/application in the system. It interacts with
|
||||||
|
the sugar.activity.* modules extensively in order to
|
||||||
|
accomplish its tasks.
|
||||||
|
"""
|
||||||
__gsignals__ = {
|
__gsignals__ = {
|
||||||
'launch-timeout': (gobject.SIGNAL_RUN_FIRST,
|
'launch-timeout': (gobject.SIGNAL_RUN_FIRST,
|
||||||
gobject.TYPE_NONE,
|
gobject.TYPE_NONE,
|
||||||
@ -32,6 +39,15 @@ class HomeActivity(gobject.GObject):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, bundle, activity_id):
|
def __init__(self, bundle, activity_id):
|
||||||
|
"""Initialise the HomeActivity
|
||||||
|
|
||||||
|
bundle -- sugar.activity.bundle.Bundle instance,
|
||||||
|
provides the information required to actually
|
||||||
|
create the new instance. This is, in effect,
|
||||||
|
the "type" of activity being created.
|
||||||
|
activity_id -- unique identifier for this instance
|
||||||
|
of the activity type
|
||||||
|
"""
|
||||||
gobject.GObject.__init__(self)
|
gobject.GObject.__init__(self)
|
||||||
self._window = None
|
self._window = None
|
||||||
self._xid = None
|
self._xid = None
|
||||||
@ -52,6 +68,8 @@ class HomeActivity(gobject.GObject):
|
|||||||
self._launch_timeout_id = 0
|
self._launch_timeout_id = 0
|
||||||
|
|
||||||
def _launch_timeout_cb(self, user_data=None):
|
def _launch_timeout_cb(self, user_data=None):
|
||||||
|
"""Callback for launch operation timeouts
|
||||||
|
"""
|
||||||
logging.debug("Activity %s (%s) launch timed out" %
|
logging.debug("Activity %s (%s) launch timed out" %
|
||||||
(self._activity_id, self.get_type))
|
(self._activity_id, self.get_type))
|
||||||
self._launch_timeout_id = 0
|
self._launch_timeout_id = 0
|
||||||
@ -78,17 +96,33 @@ class HomeActivity(gobject.GObject):
|
|||||||
self._service = service
|
self._service = service
|
||||||
|
|
||||||
def get_service(self):
|
def get_service(self):
|
||||||
|
"""Retrieve the application's sugar introspection service
|
||||||
|
|
||||||
|
Note that non-native Sugar applications will not have
|
||||||
|
such a service, so the return value will be None in
|
||||||
|
those cases.
|
||||||
|
"""
|
||||||
return self._service
|
return self._service
|
||||||
|
|
||||||
def get_title(self):
|
def get_title(self):
|
||||||
|
"""Retrieve the application's root window's suggested title"""
|
||||||
if not self._launched:
|
if not self._launched:
|
||||||
raise RuntimeError("Activity is still launching.")
|
raise RuntimeError("Activity is still launching.")
|
||||||
return self._window.get_name()
|
return self._window.get_name()
|
||||||
|
|
||||||
def get_icon_name(self):
|
def get_icon_name(self):
|
||||||
|
"""Retrieve the bundle's icon (file) name"""
|
||||||
return self._bundle.get_icon()
|
return self._bundle.get_icon()
|
||||||
|
|
||||||
def get_icon_color(self):
|
def get_icon_color(self):
|
||||||
|
"""Retrieve the appropriate icon colour for this activity
|
||||||
|
|
||||||
|
Uses activity_id to index into the PresenceService's
|
||||||
|
set of activity colours, if the PresenceService does not
|
||||||
|
have an entry (implying that this is not a Sugar-shared application)
|
||||||
|
uses the local user's profile.get_color() to determine the
|
||||||
|
colour for the icon.
|
||||||
|
"""
|
||||||
pservice = presenceservice.get_instance()
|
pservice = presenceservice.get_instance()
|
||||||
activity = pservice.get_activity(self._activity_id)
|
activity = pservice.get_activity(self._activity_id)
|
||||||
if activity != None:
|
if activity != None:
|
||||||
@ -97,28 +131,53 @@ class HomeActivity(gobject.GObject):
|
|||||||
return profile.get_color()
|
return profile.get_color()
|
||||||
|
|
||||||
def get_activity_id(self):
|
def get_activity_id(self):
|
||||||
|
"""Retrieve the "activity_id" passed in to our constructor
|
||||||
|
|
||||||
|
This is a "globally likely unique" identifier generated by
|
||||||
|
sugar.util.unique_id
|
||||||
|
"""
|
||||||
return self._activity_id
|
return self._activity_id
|
||||||
|
|
||||||
def get_xid(self):
|
def get_xid(self):
|
||||||
|
"""Retrieve the X-windows ID of our root window"""
|
||||||
if not self._launched:
|
if not self._launched:
|
||||||
raise RuntimeError("Activity is still launching.")
|
raise RuntimeError("Activity is still launching.")
|
||||||
return self._xid
|
return self._xid
|
||||||
|
|
||||||
def get_window(self):
|
def get_window(self):
|
||||||
|
"""Retrieve the X-windows root window of this application
|
||||||
|
|
||||||
|
This was stored by the set_window method, which was
|
||||||
|
called by HomeModel._add_activity, which was called
|
||||||
|
via a callback that looks for all 'window-opened'
|
||||||
|
events.
|
||||||
|
|
||||||
|
HomeModel currently uses a dbus service query on the
|
||||||
|
activity to determine to which HomeActivity the newly
|
||||||
|
launched window belongs.
|
||||||
|
"""
|
||||||
if not self._launched:
|
if not self._launched:
|
||||||
raise RuntimeError("Activity is still launching.")
|
raise RuntimeError("Activity is still launching.")
|
||||||
return self._window
|
return self._window
|
||||||
|
|
||||||
def get_type(self):
|
def get_type(self):
|
||||||
|
"""Retrieve bundle's "service_name" for future reference"""
|
||||||
return self._bundle.get_service_name()
|
return self._bundle.get_service_name()
|
||||||
|
|
||||||
def get_shared(self):
|
def get_shared(self):
|
||||||
|
"""Return whether this activity is using Presence service sharing"""
|
||||||
if not self._launched:
|
if not self._launched:
|
||||||
raise RuntimeError("Activity is still launching.")
|
raise RuntimeError("Activity is still launching.")
|
||||||
return self._service.get_shared()
|
return self._service.get_shared()
|
||||||
|
|
||||||
def get_launch_time(self):
|
def get_launch_time(self):
|
||||||
|
"""Return the time at which the activity was first launched
|
||||||
|
|
||||||
|
Format is floating-point time.time() value
|
||||||
|
(seconds since the epoch)
|
||||||
|
"""
|
||||||
return self._launch_time
|
return self._launch_time
|
||||||
|
|
||||||
def get_launched(self):
|
def get_launched(self):
|
||||||
|
"""Return whether we have bound our top-level window yet"""
|
||||||
return self._launched
|
return self._launched
|
||||||
|
@ -28,7 +28,19 @@ _SERVICE_NAME = "org.laptop.Activity"
|
|||||||
_SERVICE_PATH = "/org/laptop/Activity"
|
_SERVICE_PATH = "/org/laptop/Activity"
|
||||||
|
|
||||||
class HomeModel(gobject.GObject):
|
class HomeModel(gobject.GObject):
|
||||||
|
"""Model of the "Home" view (activity management)
|
||||||
|
|
||||||
|
The HomeModel is basically the point of registration
|
||||||
|
for all running activities within Sugar. It traps
|
||||||
|
events that tell the system there is a new activity
|
||||||
|
being created (generated by the activity factories),
|
||||||
|
or removed, as well as those which tell us that the
|
||||||
|
currently focussed activity has changed.
|
||||||
|
|
||||||
|
The HomeModel tracks a set of HomeActivity instances,
|
||||||
|
which are tracking the window to activity mappings
|
||||||
|
the activity factories have set up.
|
||||||
|
"""
|
||||||
__gsignals__ = {
|
__gsignals__ = {
|
||||||
'activity-launched': (gobject.SIGNAL_RUN_FIRST,
|
'activity-launched': (gobject.SIGNAL_RUN_FIRST,
|
||||||
gobject.TYPE_NONE,
|
gobject.TYPE_NONE,
|
||||||
@ -124,6 +136,16 @@ class HomeModel(gobject.GObject):
|
|||||||
self.emit('activity-added', home_window)
|
self.emit('activity-added', home_window)
|
||||||
|
|
||||||
def _add_activity(self, window):
|
def _add_activity(self, window):
|
||||||
|
"""Add the window to the set of windows we track
|
||||||
|
|
||||||
|
At the moment this requires that something somewhere
|
||||||
|
have registered a dbus service with the XID of the
|
||||||
|
new window that is used to bind the requested activity
|
||||||
|
to the window.
|
||||||
|
|
||||||
|
window -- gtk.Window instance representing a new
|
||||||
|
normal, top-level window
|
||||||
|
"""
|
||||||
bus = dbus.SessionBus()
|
bus = dbus.SessionBus()
|
||||||
xid = window.get_xid()
|
xid = window.get_xid()
|
||||||
try:
|
try:
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
"""OLPC Sugar User Interface"""
|
||||||
ZOOM_MESH = 0
|
ZOOM_MESH = 0
|
||||||
ZOOM_FRIENDS = 1
|
ZOOM_FRIENDS = 1
|
||||||
ZOOM_HOME = 2
|
ZOOM_HOME = 2
|
||||||
|
@ -1,3 +1,8 @@
|
|||||||
|
"""Base class for Python-coded activities
|
||||||
|
|
||||||
|
This is currently the only reference for what an
|
||||||
|
activity must do to participate in the Sugar desktop.
|
||||||
|
"""
|
||||||
# Copyright (C) 2006, Red Hat, Inc.
|
# Copyright (C) 2006, Red Hat, Inc.
|
||||||
#
|
#
|
||||||
# This library is free software; you can redistribute it and/or
|
# This library is free software; you can redistribute it and/or
|
||||||
@ -29,6 +34,26 @@ class Activity(Window, gtk.Container):
|
|||||||
"""Base Activity class that all other Activities derive from."""
|
"""Base Activity class that all other Activities derive from."""
|
||||||
__gtype_name__ = 'SugarActivity'
|
__gtype_name__ = 'SugarActivity'
|
||||||
def __init__(self, handle):
|
def __init__(self, handle):
|
||||||
|
"""Initialise the Activity
|
||||||
|
|
||||||
|
handle -- sugar.activity.activityhandle.ActivityHandle
|
||||||
|
instance providing the activity id and access to the
|
||||||
|
presence service which *may* provide sharing for this
|
||||||
|
application
|
||||||
|
|
||||||
|
Side effects:
|
||||||
|
|
||||||
|
Sets the gdk screen DPI setting (resolution) to the
|
||||||
|
Sugar screen resolution.
|
||||||
|
|
||||||
|
Connects our "destroy" message to our _destroy_cb
|
||||||
|
method.
|
||||||
|
|
||||||
|
Creates a base gtk.Window within this window.
|
||||||
|
|
||||||
|
Creates an ActivityService (self._bus) servicing
|
||||||
|
this application.
|
||||||
|
"""
|
||||||
Window.__init__(self)
|
Window.__init__(self)
|
||||||
|
|
||||||
self.connect('destroy', self._destroy_cb)
|
self.connect('destroy', self._destroy_cb)
|
||||||
@ -105,6 +130,7 @@ class Activity(Window, gtk.Container):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def _destroy_cb(self, window):
|
def _destroy_cb(self, window):
|
||||||
|
"""Destroys our ActivityService and sharing service"""
|
||||||
if self._bus:
|
if self._bus:
|
||||||
del self._bus
|
del self._bus
|
||||||
self._bus = None
|
self._bus = None
|
||||||
@ -112,4 +138,6 @@ class Activity(Window, gtk.Container):
|
|||||||
self._pservice.unregister_service(self._service)
|
self._pservice.unregister_service(self._service)
|
||||||
|
|
||||||
def get_bundle_path():
|
def get_bundle_path():
|
||||||
|
"""Return the bundle path for the current process' bundle
|
||||||
|
"""
|
||||||
return os.environ['SUGAR_BUNDLE_PATH']
|
return os.environ['SUGAR_BUNDLE_PATH']
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
"""Shell side object which manages request to start activity"""
|
||||||
# Copyright (C) 2006, Red Hat, Inc.
|
# Copyright (C) 2006, Red Hat, Inc.
|
||||||
#
|
#
|
||||||
# This library is free software; you can redistribute it and/or
|
# This library is free software; you can redistribute it and/or
|
||||||
@ -31,6 +32,7 @@ _ACTIVITY_SERVICE_PATH = "/org/laptop/Activity"
|
|||||||
_ACTIVITY_INTERFACE = "org.laptop.Activity"
|
_ACTIVITY_INTERFACE = "org.laptop.Activity"
|
||||||
|
|
||||||
def create_activity_id():
|
def create_activity_id():
|
||||||
|
"""Generate a new, unique ID for this activity"""
|
||||||
pservice = presenceservice.get_instance()
|
pservice = presenceservice.get_instance()
|
||||||
|
|
||||||
# create a new unique activity ID
|
# create a new unique activity ID
|
||||||
@ -52,6 +54,14 @@ def create_activity_id():
|
|||||||
raise RuntimeError("Cannot generate unique activity id.")
|
raise RuntimeError("Cannot generate unique activity id.")
|
||||||
|
|
||||||
class ActivityCreationHandler(gobject.GObject):
|
class ActivityCreationHandler(gobject.GObject):
|
||||||
|
"""Sugar-side activity creation interface
|
||||||
|
|
||||||
|
This object uses a dbus method on the ActivityFactory
|
||||||
|
service to create the new activity. It generates
|
||||||
|
GObject events in response to the success/failure of
|
||||||
|
activity startup using callbacks to the service's
|
||||||
|
create call.
|
||||||
|
"""
|
||||||
__gsignals__ = {
|
__gsignals__ = {
|
||||||
'success': (gobject.SIGNAL_RUN_FIRST,
|
'success': (gobject.SIGNAL_RUN_FIRST,
|
||||||
gobject.TYPE_NONE, ([])),
|
gobject.TYPE_NONE, ([])),
|
||||||
@ -61,6 +71,29 @@ class ActivityCreationHandler(gobject.GObject):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, service_name, activity_handle):
|
def __init__(self, service_name, activity_handle):
|
||||||
|
"""Initialise the handler
|
||||||
|
|
||||||
|
service_name -- used to retrieve the activity bundle
|
||||||
|
from the global BundleRegistry. This is what
|
||||||
|
determines what activity will be run.
|
||||||
|
activity_handle -- stores the values which are to
|
||||||
|
be passed to the service to uniquely identify
|
||||||
|
the activity to be created and the sharing
|
||||||
|
service that may or may not be connected with it
|
||||||
|
|
||||||
|
sugar.activity.activityhandle.ActivityHandle instance
|
||||||
|
|
||||||
|
calls the "create" method on the service for this
|
||||||
|
particular activity type and registers the
|
||||||
|
_reply_handler and _error_handler methods on that
|
||||||
|
call's results.
|
||||||
|
|
||||||
|
The specific service which creates new instances of this
|
||||||
|
particular type of activity is created during the activity
|
||||||
|
registration process in
|
||||||
|
sugar.activity.bundleregistry.BundleRegistry which creates
|
||||||
|
service definition files for each registered bundle type.
|
||||||
|
"""
|
||||||
gobject.GObject.__init__(self)
|
gobject.GObject.__init__(self)
|
||||||
self._service_name = service_name
|
self._service_name = service_name
|
||||||
self._activity_handle = activity_handle
|
self._activity_handle = activity_handle
|
||||||
@ -75,16 +108,28 @@ class ActivityCreationHandler(gobject.GObject):
|
|||||||
factory.create(self._activity_handle.get_dict(),
|
factory.create(self._activity_handle.get_dict(),
|
||||||
reply_handler=self._reply_handler,
|
reply_handler=self._reply_handler,
|
||||||
error_handler=self._error_handler)
|
error_handler=self._error_handler)
|
||||||
|
|
||||||
def get_activity_id(self):
|
def get_activity_id(self):
|
||||||
|
"""Retrieve the unique identity for this activity"""
|
||||||
return self._activity_handle.activity_id
|
return self._activity_handle.activity_id
|
||||||
|
|
||||||
def _reply_handler(self, xid):
|
def _reply_handler(self, xid):
|
||||||
|
"""Reply from service regarding what window was created
|
||||||
|
|
||||||
|
xid -- X windows ID for the window that was just created
|
||||||
|
|
||||||
|
emits the "success" message (on ourselves)
|
||||||
|
"""
|
||||||
logging.debug("Activity created %s (%s)." %
|
logging.debug("Activity created %s (%s)." %
|
||||||
(self._activity_handle.activity_id, self._service_name))
|
(self._activity_handle.activity_id, self._service_name))
|
||||||
self.emit('success')
|
self.emit('success')
|
||||||
|
|
||||||
def _error_handler(self, err):
|
def _error_handler(self, err):
|
||||||
|
"""Reply from service with an error message (exception)
|
||||||
|
|
||||||
|
err -- exception object describing the error
|
||||||
|
|
||||||
|
emits the "error" message (on ourselves)
|
||||||
|
"""
|
||||||
logging.debug("Couldn't create activity %s (%s): %s" %
|
logging.debug("Couldn't create activity %s (%s): %s" %
|
||||||
(self._activity_handle.activity_id, self._service_name, err))
|
(self._activity_handle.activity_id, self._service_name, err))
|
||||||
self.emit('error', err)
|
self.emit('error', err)
|
||||||
|
@ -35,9 +35,43 @@ gobject.threads_init()
|
|||||||
dbus.glib.threads_init()
|
dbus.glib.threads_init()
|
||||||
|
|
||||||
class ActivityFactoryService(dbus.service.Object):
|
class ActivityFactoryService(dbus.service.Object):
|
||||||
"""D-Bus service that creates new instances of an activity"""
|
"""D-Bus service that creates instances of Python activities
|
||||||
|
|
||||||
|
The ActivityFactoryService is a dbus service created for
|
||||||
|
each Python based activity type (that is, each activity
|
||||||
|
bundle which declares a "class" in its activity.info file,
|
||||||
|
rather than an "exec").
|
||||||
|
|
||||||
|
The ActivityFactoryService is the actual process which
|
||||||
|
instantiates the Python classes for Sugar interfaces. That
|
||||||
|
is, your Python code runs in the same process as the
|
||||||
|
ActivityFactoryService itself.
|
||||||
|
|
||||||
|
The "service" process is created at the moment Sugar first
|
||||||
|
attempts to create an instance of the activity type. It
|
||||||
|
then remains in memory until the last instance of the
|
||||||
|
activity type is terminated.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, service_name, activity_class):
|
def __init__(self, service_name, activity_class):
|
||||||
|
"""Initialize the service to create activities of this type
|
||||||
|
|
||||||
|
service_name -- bundle's service name, this is used
|
||||||
|
to construct the dbus service name used to access
|
||||||
|
the created service.
|
||||||
|
activity_class -- dotted Python class name for the
|
||||||
|
Activity class which is to be instantiated by
|
||||||
|
the service. Assumed to be composed of a module
|
||||||
|
followed by a class.
|
||||||
|
|
||||||
|
if the module specified has a "start" attribute this object
|
||||||
|
will be called on service initialisation (before first
|
||||||
|
instance is created).
|
||||||
|
|
||||||
|
if the module specified has a "stop" attribute this object
|
||||||
|
will be called after every instance exits (note: may be
|
||||||
|
called multiple times for each time start is called!)
|
||||||
|
"""
|
||||||
self._activities = []
|
self._activities = []
|
||||||
|
|
||||||
splitted_module = activity_class.rsplit('.', 1)
|
splitted_module = activity_class.rsplit('.', 1)
|
||||||
@ -60,6 +94,12 @@ class ActivityFactoryService(dbus.service.Object):
|
|||||||
|
|
||||||
@dbus.service.method("com.redhat.Sugar.ActivityFactory", in_signature="a{ss}")
|
@dbus.service.method("com.redhat.Sugar.ActivityFactory", in_signature="a{ss}")
|
||||||
def create(self, handle):
|
def create(self, handle):
|
||||||
|
"""Create a new instance of this activity
|
||||||
|
|
||||||
|
handle -- sugar.activity.activityhandle.ActivityHandle
|
||||||
|
compatible dictionary providing the instance-specific
|
||||||
|
values for the new instance
|
||||||
|
"""
|
||||||
activity_handle = activityhandle.create_from_dict(handle)
|
activity_handle = activityhandle.create_from_dict(handle)
|
||||||
activity = self._constructor(activity_handle)
|
activity = self._constructor(activity_handle)
|
||||||
activity.present()
|
activity.present()
|
||||||
@ -70,6 +110,16 @@ class ActivityFactoryService(dbus.service.Object):
|
|||||||
return activity.window.xid
|
return activity.window.xid
|
||||||
|
|
||||||
def _activity_destroy_cb(self, activity):
|
def _activity_destroy_cb(self, activity):
|
||||||
|
"""On close of an instance's root window
|
||||||
|
|
||||||
|
Removes the activity from the tracked activities.
|
||||||
|
|
||||||
|
If our implementation module has a stop, calls
|
||||||
|
that.
|
||||||
|
|
||||||
|
If there are no more tracked activities, closes
|
||||||
|
the activity.
|
||||||
|
"""
|
||||||
self._activities.remove(activity)
|
self._activities.remove(activity)
|
||||||
|
|
||||||
if hasattr(self._module, 'stop'):
|
if hasattr(self._module, 'stop'):
|
||||||
|
@ -18,13 +18,47 @@
|
|||||||
from sugar.presence import presenceservice
|
from sugar.presence import presenceservice
|
||||||
|
|
||||||
class ActivityHandle(object):
|
class ActivityHandle(object):
|
||||||
def __init__(self, activity_id):
|
"""Data structure storing simple activity metadata"""
|
||||||
|
def __init__(
|
||||||
|
self, activity_id, pservice_id=None,
|
||||||
|
object_id=None,uri=None
|
||||||
|
):
|
||||||
|
"""Initialise the handle from activity_id
|
||||||
|
|
||||||
|
activity_id -- unique id for the activity to be
|
||||||
|
created
|
||||||
|
pservice_id -- identity of the sharing service
|
||||||
|
for this activity in the PresenceService
|
||||||
|
object_id -- identity of the journal object
|
||||||
|
associated with the activity. It was used by
|
||||||
|
the journal prototype implementation, might
|
||||||
|
change when we do the real one.
|
||||||
|
|
||||||
|
When you resume an activity from the journal
|
||||||
|
the object_id will be passed in. It's optional
|
||||||
|
since new activities does not have an
|
||||||
|
associated object (yet).
|
||||||
|
|
||||||
|
XXX Not clear how this relates to the activity
|
||||||
|
id yet, i.e. not sure we really need both. TBF
|
||||||
|
uri -- 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)
|
||||||
|
"""
|
||||||
self.activity_id = activity_id
|
self.activity_id = activity_id
|
||||||
self.pservice_id = None
|
self.pservice_id = pservice_id
|
||||||
self.object_id = None
|
self.object_id = object_id
|
||||||
self.uri = None
|
self.uri = uri
|
||||||
|
|
||||||
def get_presence_service(self):
|
def get_presence_service(self):
|
||||||
|
"""Retrieve the "sharing service" for this activity
|
||||||
|
|
||||||
|
Uses the PresenceService to find any existing dbus
|
||||||
|
service which provides sharing mechanisms for this
|
||||||
|
activity.
|
||||||
|
"""
|
||||||
if self.pservice_id:
|
if self.pservice_id:
|
||||||
pservice = presenceservice.get_instance()
|
pservice = presenceservice.get_instance()
|
||||||
return pservice.get_activity(self.pservice_id)
|
return pservice.get_activity(self.pservice_id)
|
||||||
@ -32,6 +66,7 @@ class ActivityHandle(object):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def get_dict(self):
|
def get_dict(self):
|
||||||
|
"""Retrieve our settings as a dictionary"""
|
||||||
result = { 'activity_id' : self.activity_id }
|
result = { 'activity_id' : self.activity_id }
|
||||||
if self.pservice_id:
|
if self.pservice_id:
|
||||||
result['pservice_id'] = self.pservice_id
|
result['pservice_id'] = self.pservice_id
|
||||||
@ -43,12 +78,11 @@ class ActivityHandle(object):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
def create_from_dict(handle_dict):
|
def create_from_dict(handle_dict):
|
||||||
result = ActivityHandle(handle_dict['activity_id'])
|
"""Create a handle from a dictionary of parameters"""
|
||||||
if handle_dict.has_key('pservice_id'):
|
result = ActivityHandle(
|
||||||
result.pservice_id = handle_dict['pservice_id']
|
handle_dict['activity_id'],
|
||||||
if handle_dict.has_key('object_id'):
|
pservice_id = handle_dict.get( 'pservice_id' ),
|
||||||
result.object_id = handle_dict['object_id']
|
object_id = handle_dict.get('object_id'),
|
||||||
if handle_dict.has_key('uri'):
|
uri = handle_dict.get('uri'),
|
||||||
result.uri = handle_dict['uri']
|
)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
@ -29,6 +29,21 @@ class ActivityService(dbus.service.Object):
|
|||||||
tightly control what stuff passes through the dbus python bindings."""
|
tightly control what stuff passes through the dbus python bindings."""
|
||||||
|
|
||||||
def __init__(self, activity):
|
def __init__(self, activity):
|
||||||
|
"""Initialise the service for the given activity
|
||||||
|
|
||||||
|
activity -- sugar.activity.activity.Activity instance,
|
||||||
|
must have already bound it's window (i.e. it must
|
||||||
|
have already initialised to the point of having
|
||||||
|
the X window available).
|
||||||
|
|
||||||
|
Creates dbus services that use the xid of the activity's
|
||||||
|
root window as discriminants among all active services
|
||||||
|
of this type. That is, the services are all available
|
||||||
|
as names/paths derived from the xid for the window.
|
||||||
|
|
||||||
|
The various methods exposed on dbus are just forwarded
|
||||||
|
to the client Activity object's equally-named methods.
|
||||||
|
"""
|
||||||
xid = activity.window.xid
|
xid = activity.window.xid
|
||||||
service_name = _ACTIVITY_SERVICE_NAME + '%d' % xid
|
service_name = _ACTIVITY_SERVICE_NAME + '%d' % xid
|
||||||
object_path = _ACTIVITY_SERVICE_PATH + "/%s" % xid
|
object_path = _ACTIVITY_SERVICE_PATH + "/%s" % xid
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
"""Metadata description of a given application/activity"""
|
||||||
import logging
|
import logging
|
||||||
import locale
|
import locale
|
||||||
import os
|
import os
|
||||||
@ -8,7 +9,17 @@ from sugar import env
|
|||||||
_PYTHON_FACTORY='sugar-activity-factory'
|
_PYTHON_FACTORY='sugar-activity-factory'
|
||||||
|
|
||||||
class Bundle:
|
class Bundle:
|
||||||
"""Info about an activity bundle. Wraps the activity.info file."""
|
"""Metadata description of a given application/activity
|
||||||
|
|
||||||
|
The metadata is normally read from an activity.info file,
|
||||||
|
which is an INI-style configuration file read using the
|
||||||
|
standard Python ConfigParser module.
|
||||||
|
|
||||||
|
The format reference for the Bundle definition files is
|
||||||
|
available for further reference:
|
||||||
|
|
||||||
|
http://wiki.laptop.org/go/Activity_bundles
|
||||||
|
"""
|
||||||
def __init__(self, path):
|
def __init__(self, path):
|
||||||
self._name = None
|
self._name = None
|
||||||
self._icon = None
|
self._icon = None
|
||||||
|
@ -15,8 +15,25 @@ def _get_data_dirs():
|
|||||||
return [ '/usr/local/share/', '/usr/share/' ]
|
return [ '/usr/local/share/', '/usr/share/' ]
|
||||||
|
|
||||||
class _ServiceManager(object):
|
class _ServiceManager(object):
|
||||||
|
"""Internal class responsible for creating dbus service files
|
||||||
|
|
||||||
|
DBUS services are defined in files which bind a service name
|
||||||
|
to the name of an executable which provides the service name.
|
||||||
|
|
||||||
|
In Sugar, the service files are automatically generated from
|
||||||
|
the activity registry (by this class). When an activity's
|
||||||
|
dbus launch service is requested, dbus will launch the
|
||||||
|
specified executable in order to allow it to provide the
|
||||||
|
requested activity-launching service.
|
||||||
|
|
||||||
|
In the case of activities which provide a "class", instead of
|
||||||
|
an "exec" attribute in their activity.info, the
|
||||||
|
sugar-activity-factory script is used with an appropriate
|
||||||
|
argument to service that bundle.
|
||||||
|
"""
|
||||||
|
SERVICE_DIRECTORY = '~/.local/share/dbus-1/services'
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
service_dir = os.path.expanduser('~/.local/share/dbus-1/services')
|
service_dir = os.path.expanduser(self.SERVICE_DIRECTORY)
|
||||||
if not os.path.isdir(service_dir):
|
if not os.path.isdir(service_dir):
|
||||||
os.makedirs(service_dir)
|
os.makedirs(service_dir)
|
||||||
|
|
||||||
|
@ -1,10 +1,25 @@
|
|||||||
|
"""Simple date-representation model"""
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
class Date(object):
|
class Date(object):
|
||||||
|
"""Date-object storing a simple time.time() float
|
||||||
|
|
||||||
|
XXX not sure about the rationale for this class,
|
||||||
|
possibly it makes transfer over dbus easier?
|
||||||
|
"""
|
||||||
def __init__(self, timestamp):
|
def __init__(self, timestamp):
|
||||||
|
"""Initialise via a timestamp (floating point value)"""
|
||||||
self._timestamp = timestamp
|
self._timestamp = timestamp
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
"""Produce a formatted date representation
|
||||||
|
|
||||||
|
Eventually this should produce a localised version
|
||||||
|
of the date. At the moment it always produces English
|
||||||
|
dates in long form with Today and Yesterday
|
||||||
|
special-cased and dates from this year not presenting
|
||||||
|
the year in the date.
|
||||||
|
"""
|
||||||
date = datetime.date.fromtimestamp(self._timestamp)
|
date = datetime.date.fromtimestamp(self._timestamp)
|
||||||
today = datetime.date.today()
|
today = datetime.date.today()
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
"""Calculates file-paths for the Sugar working environment"""
|
||||||
# Copyright (C) 2006, Red Hat, Inc.
|
# Copyright (C) 2006, Red Hat, Inc.
|
||||||
#
|
#
|
||||||
# This library is free software; you can redistribute it and/or
|
# This library is free software; you can redistribute it and/or
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
"""Logging module configuration for Sugar"""
|
||||||
# Copyright (C) 2006, Red Hat, Inc.
|
# Copyright (C) 2006, Red Hat, Inc.
|
||||||
#
|
#
|
||||||
# This library is free software; you can redistribute it and/or
|
# This library is free software; you can redistribute it and/or
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
"""User settings/configuration loading"""
|
||||||
# Copyright (C) 2006, Red Hat, Inc.
|
# Copyright (C) 2006, Red Hat, Inc.
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or modify
|
# This program is free software; you can redistribute it and/or modify
|
||||||
@ -23,6 +24,30 @@ from sugar import util
|
|||||||
from sugar.graphics.xocolor import XoColor
|
from sugar.graphics.xocolor import XoColor
|
||||||
|
|
||||||
class _Profile(object):
|
class _Profile(object):
|
||||||
|
"""Local user's current options/profile information
|
||||||
|
|
||||||
|
User settings are stored in an INI-style configuration
|
||||||
|
file. This object uses the ConfigParser module to load
|
||||||
|
the settings. At the moment the only storage mechanism
|
||||||
|
is in the set_server_registered method, which loads the
|
||||||
|
file directly from disk, then dumps it back out again
|
||||||
|
immediately, rather than using the class.
|
||||||
|
|
||||||
|
The profile is also responsible for loading the user's
|
||||||
|
public and private ssh keys from disk.
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
|
||||||
|
name -- child's name
|
||||||
|
color -- XoColor for the child's icon
|
||||||
|
server -- school server with which the child is
|
||||||
|
associated
|
||||||
|
server_registered -- whether the child has registered
|
||||||
|
with the school server or not
|
||||||
|
|
||||||
|
pubkey -- public ssh key
|
||||||
|
privkey_hash -- SHA has of the child's public key
|
||||||
|
"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.name = None
|
self.name = None
|
||||||
self.color = None
|
self.color = None
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
"""Various utility functions"""
|
||||||
# Copyright (C) 2006, Red Hat, Inc.
|
# Copyright (C) 2006, Red Hat, Inc.
|
||||||
#
|
#
|
||||||
# This library is free software; you can redistribute it and/or
|
# This library is free software; you can redistribute it and/or
|
||||||
@ -38,6 +39,19 @@ def _sha_data(data):
|
|||||||
return sha_hash.digest()
|
return sha_hash.digest()
|
||||||
|
|
||||||
def unique_id(data = ''):
|
def unique_id(data = ''):
|
||||||
|
"""Generate a likely-unique ID for whatever purpose
|
||||||
|
|
||||||
|
data -- suffix appended to working data before hashing
|
||||||
|
|
||||||
|
Returns a 40-character string with hexidecimal digits
|
||||||
|
representing an SHA hash of the time, a random digit
|
||||||
|
within a constrained range and the data passed.
|
||||||
|
|
||||||
|
Note: these are *not* crypotographically secure or
|
||||||
|
globally unique identifiers. While they are likely
|
||||||
|
to be unique-enough, no attempt is made to make
|
||||||
|
perfectly unique values.
|
||||||
|
"""
|
||||||
data_string = "%s%s%s" % (time.time(), random.randint(10000, 100000), data)
|
data_string = "%s%s%s" % (time.time(), random.randint(10000, 100000), data)
|
||||||
return printable_hash(_sha_data(data_string))
|
return printable_hash(_sha_data(data_string))
|
||||||
|
|
||||||
@ -49,7 +63,7 @@ def is_hex(s):
|
|||||||
|
|
||||||
def validate_activity_id(actid):
|
def validate_activity_id(actid):
|
||||||
"""Validate an activity ID."""
|
"""Validate an activity ID."""
|
||||||
if not isinstance(actid, str) and not isinstance(actid, unicode):
|
if not isinstance(actid, (str,unicode)):
|
||||||
return False
|
return False
|
||||||
if len(actid) != ACTIVITY_ID_LEN:
|
if len(actid) != ACTIVITY_ID_LEN:
|
||||||
return False
|
return False
|
||||||
@ -62,6 +76,23 @@ class _ServiceParser(ConfigParser):
|
|||||||
return option
|
return option
|
||||||
|
|
||||||
def write_service(name, bin, path):
|
def write_service(name, bin, path):
|
||||||
|
"""Write a D-BUS service definition file
|
||||||
|
|
||||||
|
These are written by the bundleregistry when
|
||||||
|
a new activity is registered. They bind a
|
||||||
|
D-BUS bus-name with an executable which is
|
||||||
|
to provide the named service.
|
||||||
|
|
||||||
|
name -- D-BUS service name, must be a valid
|
||||||
|
filename/D-BUS name
|
||||||
|
bin -- executable providing named service
|
||||||
|
path -- directory into which to write the
|
||||||
|
name.service file
|
||||||
|
|
||||||
|
The service files themselves are written using
|
||||||
|
the _ServiceParser class, which is a subclass
|
||||||
|
of the standard ConfigParser class.
|
||||||
|
"""
|
||||||
service_cp = _ServiceParser()
|
service_cp = _ServiceParser()
|
||||||
section = 'D-BUS Service'
|
section = 'D-BUS Service'
|
||||||
service_cp.add_section(section)
|
service_cp.add_section(section)
|
||||||
|
Loading…
Reference in New Issue
Block a user