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:
@@ -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']
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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'):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user