Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
authorMike's Primary Account <mcfletch@caramon.fletchers>2007-04-20 20:25:37 (GMT)
committer Mike's Primary Account <mcfletch@caramon.fletchers>2007-04-20 20:25:37 (GMT)
commitc0c64809a06348765521e55913acd29f5c93fc16 (patch)
treef056d61f47dc39f69bc26104002b723d15a8b9eb /services
parent79d17c14f4e1321c2311c4800d36b1da19653eda (diff)
Initial documentation pass for buddy objects
Diffstat (limited to 'services')
-rw-r--r--services/presence/buddy.py180
1 files changed, 171 insertions, 9 deletions
diff --git a/services/presence/buddy.py b/services/presence/buddy.py
index 8b2eec4..8ad4b48 100644
--- a/services/presence/buddy.py
+++ b/services/presence/buddy.py
@@ -1,3 +1,4 @@
+"""An "actor" on the network, whether remote or local"""
# Copyright (C) 2007, Red Hat, Inc.
# Copyright (C) 2007, Collabora Ltd.
#
@@ -29,6 +30,7 @@ _BUDDY_INTERFACE = "org.laptop.Sugar.Presence.Buddy"
_OWNER_INTERFACE = "org.laptop.Sugar.Presence.Buddy.Owner"
class NotFoundError(dbus.DBusException):
+ """Raised when a given actor is not found on the network"""
def __init__(self):
dbus.DBusException.__init__(self)
self._dbus_error_name = _PRESENCE_INTERFACE + '.NotFound'
@@ -38,8 +40,26 @@ class DBusGObject(dbus.service.Object, gobject.GObject): __metaclass__ = DBusGOb
class Buddy(DBusGObject):
- """Represents another person on the network and keeps track of the
- activities and resources they make available for sharing."""
+ """Person on the network (tracks properties and shared activites)
+
+ The Buddy is a collection of metadata describing a particular
+ actor/person on the network. The Buddy object tracks a set of
+ activities which the actor has shared with the presence service.
+
+ Buddies have a "valid" property which is used to flag Buddies
+ which are no longer reachable. That is, a Buddy may represent
+ a no-longer reachable target on the network.
+
+ The Buddy emits GObject events that the PresenceService uses
+ to track changes in its status.
+
+ Attributes:
+
+ _activities -- dictionary mapping activity ID to
+ activity.Activity objects
+ handles -- dictionary mapping telepresence client to
+ "handle" (XXX what's that)
+ """
__gsignals__ = {
'validity-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
@@ -62,6 +82,15 @@ class Buddy(DBusGObject):
}
def __init__(self, bus_name, object_id, **kwargs):
+ """Initialize the Buddy object
+
+ bus_name -- DBUS object bus name (identifier)
+ object_id -- the activity's unique identifier
+ kwargs -- used to initialize the object's properties
+
+ constructs a DBUS "object path" from the _BUDDY_PATH
+ and object_id
+ """
if not bus_name:
raise ValueError("DBus bus name must be valid")
if not object_id or not isinstance(object_id, int):
@@ -89,6 +118,10 @@ class Buddy(DBusGObject):
gobject.GObject.__init__(self, **kwargs)
def do_get_property(self, pspec):
+ """Retrieve current value for the given property specifier
+
+ pspec -- property specifier with a "name" attribute
+ """
if pspec.name == "key":
return self._key
elif pspec.name == "icon":
@@ -109,6 +142,14 @@ class Buddy(DBusGObject):
return self._owner
def do_set_property(self, pspec, value):
+ """Set given property
+
+ pspec -- property specifier with a "name" attribute
+ value -- value to set
+
+ emits 'icon-changed' signal on icon setting
+ calls _update_validity on all calls
+ """
if pspec.name == "icon":
if str(value) != self._icon:
self._icon = str(value)
@@ -129,27 +170,42 @@ class Buddy(DBusGObject):
@dbus.service.signal(_BUDDY_INTERFACE,
signature="ay")
def IconChanged(self, icon_data):
- pass
+ """Generates DBUS signal with icon_data"""
@dbus.service.signal(_BUDDY_INTERFACE,
signature="o")
def JoinedActivity(self, activity_path):
- pass
+ """Generates DBUS signal when buddy joins activity
+
+ activity_path -- DBUS path to the activity object
+ """
@dbus.service.signal(_BUDDY_INTERFACE,
signature="o")
def LeftActivity(self, activity_path):
- pass
+ """Generates DBUS signal when buddy leaves activity
+
+ activity_path -- DBUS path to the activity object
+ """
@dbus.service.signal(_BUDDY_INTERFACE,
signature="a{sv}")
def PropertyChanged(self, updated):
- pass
+ """Generates DBUS signal when buddy's property changes
+
+ updated -- updated property-set (dictionary) with the
+ Buddy's property (changed) values. Note: not the
+ full set of properties, just the changes.
+ """
# dbus methods
@dbus.service.method(_BUDDY_INTERFACE,
in_signature="", out_signature="ay")
def GetIcon(self):
+ """Retrieve Buddy's icon data
+
+ returns empty string or dbus.ByteArray
+ """
if not self.props.icon:
return ""
return dbus.ByteArray(self.props.icon)
@@ -157,6 +213,11 @@ class Buddy(DBusGObject):
@dbus.service.method(_BUDDY_INTERFACE,
in_signature="", out_signature="ao")
def GetJoinedActivities(self):
+ """Retrieve set of Buddy's joined activities (paths)
+
+ returns list of dbus service paths for the Buddy's joined
+ activities
+ """
acts = []
for act in self.get_joined_activities():
acts.append(act.object_path())
@@ -165,6 +226,18 @@ class Buddy(DBusGObject):
@dbus.service.method(_BUDDY_INTERFACE,
in_signature="", out_signature="a{sv}")
def GetProperties(self):
+ """Retrieve set of Buddy's properties
+
+ returns dictionary of
+ nick : str(nickname)
+ owner : bool( whether this Buddy is an owner??? )
+ XXX what is the owner flag for?
+ key : str(public-key)
+ color: Buddy's icon colour
+ XXX what type?
+ current-activity: Buddy's current activity_id, or
+ "" if no current activity
+ """
props = {}
props['nick'] = self.props.nick
props['owner'] = self.props.owner
@@ -178,9 +251,16 @@ class Buddy(DBusGObject):
# methods
def object_path(self):
+ """Retrieve our dbus.ObjectPath object"""
return dbus.ObjectPath(self._object_path)
def add_activity(self, activity):
+ """Add an activity to the Buddy's set of activities
+
+ activity -- activity.Activity instance
+
+ calls JoinedActivity
+ """
actid = activity.props.id
if self._activities.has_key(actid):
return
@@ -189,6 +269,12 @@ class Buddy(DBusGObject):
self.JoinedActivity(activity.object_path())
def remove_activity(self, activity):
+ """Remove the activity from the Buddy's set of activities
+
+ activity -- activity.Activity instance
+
+ calls LeftActivity
+ """
actid = activity.props.id
if not self._activities.has_key(actid):
return
@@ -197,6 +283,7 @@ class Buddy(DBusGObject):
self.LeftActivity(activity.object_path())
def get_joined_activities(self):
+ """Retrieves list of still-valid activity objects"""
acts = []
for act in self._activities.values():
if act.props.valid:
@@ -204,6 +291,14 @@ class Buddy(DBusGObject):
return acts
def set_properties(self, properties):
+ """Set the given set of properties on the object
+
+ properties -- set of property values to set
+
+ if no change, no events generated
+ if change, generates property-changed and
+ calls _update_validity
+ """
changed = False
if "nick" in properties.keys():
nick = properties["nick"]
@@ -234,6 +329,12 @@ class Buddy(DBusGObject):
self._update_validity()
def _update_validity(self):
+ """Check whether we are now valid
+
+ validity is True if color, nick and key are non-null
+
+ emits validity-changed if we have changed validity
+ """
try:
old_valid = self._valid
if self._color and self._nick and self._key:
@@ -247,6 +348,13 @@ class Buddy(DBusGObject):
self._valid = False
class GenericOwner(Buddy):
+ """Common functionality for Local User-like objects
+
+ The TestOwner wants to produce something *like* a
+ ShellOwner, but with randomised changes and the like.
+ This class provides the common features for a real
+ local owner and a testing one.
+ """
__gtype_name__ = "GenericOwner"
__gproperties__ = {
@@ -256,6 +364,15 @@ class GenericOwner(Buddy):
}
def __init__(self, ps, bus_name, object_id, **kwargs):
+ """Initialize the GenericOwner instance
+
+ ps -- presenceservice.PresenceService object
+ bus_name -- DBUS object bus name (identifier)
+ object_id -- the activity's unique identifier
+ kwargs -- used to initialize the object's properties
+
+ calls Buddy.__init__
+ """
self._ps = ps
self._server = 'olpc.collabora.co.uk'
self._key_hash = None
@@ -274,21 +391,27 @@ class GenericOwner(Buddy):
self._owner = True
def get_registered(self):
+ """Retrieve whether owner has registered with presence server"""
return self._registered
def get_server(self):
+ """Retrieve presence server (XXX url??)"""
return self._server
def get_key_hash(self):
+ """Retrieve the user's private-key hash"""
return self._key_hash
def set_registered(self, registered):
+ """Customisation point: handle the registration of the owner"""
raise RuntimeError("Subclasses must implement")
class ShellOwner(GenericOwner):
- """Class representing the owner of the machine. This is the client
- portion of the Owner, paired with the server portion in Owner.py."""
-
+ """Representation of the local-machine owner using Sugar's Shell
+
+ The ShellOwner uses the Sugar Shell's dbus services to
+ register for updates about the user's profile description.
+ """
__gtype_name__ = "ShellOwner"
_SHELL_SERVICE = "org.laptop.Shell"
@@ -296,6 +419,19 @@ class ShellOwner(GenericOwner):
_SHELL_PATH = "/org/laptop/Shell"
def __init__(self, ps, bus_name, object_id, test=False):
+ """Initialize the ShellOwner instance
+
+ ps -- presenceservice.PresenceService object
+ bus_name -- DBUS object bus name (identifier)
+ object_id -- the activity's unique identifier
+ test -- ignored
+
+ Retrieves initial property values from the profile
+ module. Loads the buddy icon from file as well.
+ XXX note: no error handling on that
+
+ calls GenericOwner.__init__
+ """
server = profile.get_server()
key_hash = profile.get_private_key_hash()
registered = profile.get_server_registered()
@@ -325,10 +461,17 @@ class ShellOwner(GenericOwner):
pass
def set_registered(self, value):
+ """Handle notification that we have been registered"""
if value:
profile.set_server_registered()
def _name_owner_changed_handler(self, name, old, new):
+ """Handle DBUS notification of a new / renamed service
+
+ Watches for the _SHELL_SERVICE, i.e. the Sugar Shell,
+ and registers with it if we have not yet registered
+ with it (using _connect_to_shell).
+ """
if name != self._SHELL_SERVICE:
return
if (old and len(old)) and (not new and not len(new)):
@@ -339,6 +482,11 @@ class ShellOwner(GenericOwner):
self._connect_to_shell()
def _connect_to_shell(self):
+ """Connect to the Sugar Shell service to watch for events
+
+ Connects the various XChanged events on the Sugar Shell
+ service to our _x_changed_cb methods.
+ """
obj = self._bus.get_object(self._SHELL_SERVICE, self._SHELL_PATH)
self._shell_owner = dbus.Interface(obj, self._SHELL_OWNER_INTERFACE)
self._shell_owner.connect_to_signal('IconChanged', self._icon_changed_cb)
@@ -348,17 +496,26 @@ class ShellOwner(GenericOwner):
self._cur_activity_changed_cb)
def _icon_changed_cb(self, icon):
+ """Handle icon change, set property to generate event"""
self.props.icon = icon
def _color_changed_cb(self, color):
+ """Handle color change, set property to generate event"""
props = {'color': color}
self.set_properties(props)
def _nick_changed_cb(self, nick):
+ """Handle nickname change, set property to generate event"""
props = {'nick': nick}
self.set_properties(props)
def _cur_activity_changed_cb(self, activity_id):
+ """Handle current-activity change, set property to generate event
+
+ Filters out local activities (those not in self.activites)
+ because the network users can't join those activities, so
+ the activity_id shared will be None in those cases...
+ """
if not self._activities.has_key(activity_id):
# This activity is local-only
activity_id = None
@@ -495,6 +652,7 @@ class TestOwner(GenericOwner):
def _hash_private_key(self):
+ """Unused method to has a private key, see profile"""
self.privkey_hash = None
key_path = os.path.join(env.get_profile_path(), 'owner.key')
@@ -545,6 +703,7 @@ def _extract_public_key(keyfile):
return key
def _extract_private_key(keyfile):
+ """Get a private key from a private key file"""
# Extract the private key
try:
f = open(keyfile, "r")
@@ -568,6 +727,7 @@ def _extract_private_key(keyfile):
return key
def _get_new_keypair(num):
+ """Retrieve a public/private key pair for testing"""
# Generate keypair
privkeyfile = os.path.join("/tmp", "test%d.key" % num)
pubkeyfile = os.path.join("/tmp", 'test%d.key.pub' % num)
@@ -600,10 +760,12 @@ def _get_new_keypair(num):
return (pubkey, privkey)
def _get_random_name():
+ """Produce random names for testing"""
names = ["Liam", "Noel", "Guigsy", "Whitey", "Bonehead"]
return names[random.randint(0, len(names) - 1)]
def _get_random_image():
+ """Produce a random image for display"""
import cairo, math, random, gtk
def rand():