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:
Mike C. Fletcher
2007-04-09 22:47:37 -04:00
parent 508a59b99b
commit 3f10890319
15 changed files with 373 additions and 18 deletions
+28
View File
@@ -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.
#
# 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."""
__gtype_name__ = 'SugarActivity'
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)
self.connect('destroy', self._destroy_cb)
@@ -105,6 +130,7 @@ class Activity(Window, gtk.Container):
return False
def _destroy_cb(self, window):
"""Destroys our ActivityService and sharing service"""
if self._bus:
del self._bus
self._bus = None
@@ -112,4 +138,6 @@ class Activity(Window, gtk.Container):
self._pservice.unregister_service(self._service)
def get_bundle_path():
"""Return the bundle path for the current process' bundle
"""
return os.environ['SUGAR_BUNDLE_PATH']
+46 -1
View File
@@ -1,3 +1,4 @@
"""Shell side object which manages request to start activity"""
# Copyright (C) 2006, Red Hat, Inc.
#
# 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"
def create_activity_id():
"""Generate a new, unique ID for this activity"""
pservice = presenceservice.get_instance()
# create a new unique activity ID
@@ -52,6 +54,14 @@ def create_activity_id():
raise RuntimeError("Cannot generate unique activity id.")
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__ = {
'success': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, ([])),
@@ -61,6 +71,29 @@ class ActivityCreationHandler(gobject.GObject):
}
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)
self._service_name = service_name
self._activity_handle = activity_handle
@@ -75,16 +108,28 @@ class ActivityCreationHandler(gobject.GObject):
factory.create(self._activity_handle.get_dict(),
reply_handler=self._reply_handler,
error_handler=self._error_handler)
def get_activity_id(self):
"""Retrieve the unique identity for this activity"""
return self._activity_handle.activity_id
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)." %
(self._activity_handle.activity_id, self._service_name))
self.emit('success')
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" %
(self._activity_handle.activity_id, self._service_name, err))
self.emit('error', err)
+51 -1
View File
@@ -35,9 +35,43 @@ gobject.threads_init()
dbus.glib.threads_init()
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):
"""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 = []
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}")
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 = self._constructor(activity_handle)
activity.present()
@@ -70,6 +110,16 @@ class ActivityFactoryService(dbus.service.Object):
return activity.window.xid
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)
if hasattr(self._module, 'stop'):
+46 -12
View File
@@ -18,13 +18,47 @@
from sugar.presence import presenceservice
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.pservice_id = None
self.object_id = None
self.uri = None
self.pservice_id = pservice_id
self.object_id = object_id
self.uri = uri
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:
pservice = presenceservice.get_instance()
return pservice.get_activity(self.pservice_id)
@@ -32,6 +66,7 @@ class ActivityHandle(object):
return None
def get_dict(self):
"""Retrieve our settings as a dictionary"""
result = { 'activity_id' : self.activity_id }
if self.pservice_id:
result['pservice_id'] = self.pservice_id
@@ -43,12 +78,11 @@ class ActivityHandle(object):
return result
def create_from_dict(handle_dict):
result = ActivityHandle(handle_dict['activity_id'])
if handle_dict.has_key('pservice_id'):
result.pservice_id = handle_dict['pservice_id']
if handle_dict.has_key('object_id'):
result.object_id = handle_dict['object_id']
if handle_dict.has_key('uri'):
result.uri = handle_dict['uri']
"""Create a handle from a dictionary of parameters"""
result = ActivityHandle(
handle_dict['activity_id'],
pservice_id = handle_dict.get( 'pservice_id' ),
object_id = handle_dict.get('object_id'),
uri = handle_dict.get('uri'),
)
return result
+15
View File
@@ -29,6 +29,21 @@ class ActivityService(dbus.service.Object):
tightly control what stuff passes through the dbus python bindings."""
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
service_name = _ACTIVITY_SERVICE_NAME + '%d' % xid
object_path = _ACTIVITY_SERVICE_PATH + "/%s" % xid
+12 -1
View File
@@ -1,3 +1,4 @@
"""Metadata description of a given application/activity"""
import logging
import locale
import os
@@ -8,7 +9,17 @@ from sugar import env
_PYTHON_FACTORY='sugar-activity-factory'
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):
self._name = None
self._icon = None
+18 -1
View File
@@ -15,8 +15,25 @@ def _get_data_dirs():
return [ '/usr/local/share/', '/usr/share/' ]
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):
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):
os.makedirs(service_dir)