From 88013b2f9f93c3220812cd3b624db1423ac081ed Mon Sep 17 00:00:00 2001 From: Vincent Vinet Date: Mon, 19 Oct 2009 17:13:52 +0000 Subject: apply code review induced fixes, fix a few discovered bugs, enjoy, rinse and repeat --- (limited to 'tutorius/TProbe.py') diff --git a/tutorius/TProbe.py b/tutorius/TProbe.py index 6c0883a..9d19f1b 100644 --- a/tutorius/TProbe.py +++ b/tutorius/TProbe.py @@ -1,4 +1,5 @@ import logging +LOGGER = logging.getLogger("sugar.tutorius.TProbe") import os import gobject @@ -26,25 +27,15 @@ import copy -------------------- ---------- """ +#TODO Add stub error handling for remote calls in the classes so that it will +# be clearer how errors can be handled in the future. + class TProbe(dbus.service.Object): """ Tutorius Probe Defines an entry point for Tutorius into activities that allows performing actions and registering events onto an activity via a DBUS Interface. - - Exposes the following dbus methods: - void registered(string service) - string ping() -> status - string install(string action) -> address - void update(string address, string action_props) - void uninstall(string address) - string subscribe(string pickled_event) -> address - void unsubscribe(string address) - - Exposes the following dbus Events: - eventOccured(event): - """ def __init__(self, activity_name, activity): @@ -54,9 +45,9 @@ class TProbe(dbus.service.Object): @param activity_name unique activity_id @param activity activity reference, must be a gtk container """ - logging.debug("TProbe :: Creating TProbe for %s (%d)", activity_name, os.getpid()) - logging.debug("TProbe :: Current gobject context: %s", str(gobject.main_context_default())) - logging.debug("TProbe :: Current gobject depth: %s", str(gobject.main_depth())) + LOGGER.debug("TProbe :: Creating TProbe for %s (%d)", activity_name, os.getpid()) + LOGGER.debug("TProbe :: Current gobject context: %s", str(gobject.main_context_default())) + LOGGER.debug("TProbe :: Current gobject depth: %s", str(gobject.main_depth())) # Moving the ObjectStore assignment here, in the meantime # the reference to the activity shouldn't be share as a # global variable but passed by the Probe to the objects @@ -163,6 +154,9 @@ class TProbe(dbus.service.Object): @param pickled_event string pickled EventFilter @return string unique name of registered event """ + #TODO Perform event unmapping once Tutorials use abstract events + # instead of concrete EventFilters that are tied to their + # implementation. eventfilter = pickle.loads(str(pickled_event)) # The callback uses the event defined previously and each @@ -200,14 +194,8 @@ class TProbe(dbus.service.Object): # The actual method we will call on the probe to send events def notify(self, event): - logging.debug("TProbe :: notify event %s", str(event)) - #HACK: reinstanciate the event with it's properties, to clear - # any internal state from getting pickled - if isinstance(event, TPropContainer): - newevent = type(event)(**event._props) - else: - newevent = event - self.eventOccured(pickle.dumps(newevent)) + LOGGER.debug("TProbe :: notify event %s", str(event)) + self.eventOccured(pickle.dumps(event)) # Return a unique name for this action def _generate_action_reference(self, action): @@ -225,10 +213,13 @@ class TProbe(dbus.service.Object): def _generate_event_reference(self, event): # TODO elavoie 2009-07-25 Should return a universal address name = event.__class__.__name__ - suffix = 1 + #Keep the counter to avoid looping all the time + suffix = getattr(self, '_event_ref_suffix', 0 ) + 1 while self._subscribedEvents.has_key(name+str(suffix)): suffix += 1 + + #setattr(self, '_event_ref_suffix', suffix) return name + str(suffix) @@ -238,25 +229,15 @@ class ProbeProxy: It provides an object interface to the TProbe, which requires pickled strings, across a DBus communication. - - Public Methods: - ProbeProxy(string activityName) :: Constructor - string install(Action action) - void update(Action action) - void uninstall(Action action) - void uninstall_all() - string subscribe(Event event, callable callback) - void unsubscribe(Event event, callable callback) - void unsubscribe_all() """ def __init__(self, activityName): """ Constructor - @param activityName unique activity id + @param activityName unique activity id. Must be a valid dbus bus name. """ - logging.debug("ProbeProxy :: Creating ProbeProxy for %s (%d)", activityName, os.getpid()) - logging.debug("ProbeProxy :: Current gobject context: %s", str(gobject.main_context_default())) - logging.debug("ProbeProxy :: Current gobject depth: %s", str(gobject.main_depth())) + LOGGER.debug("ProbeProxy :: Creating ProbeProxy for %s (%d)", activityName, os.getpid()) + LOGGER.debug("ProbeProxy :: Current gobject context: %s", str(gobject.main_context_default())) + LOGGER.debug("ProbeProxy :: Current gobject depth: %s", str(gobject.main_depth())) bus = dbus.SessionBus() self._object = bus.get_object(activityName, "/tutorius/Probe") self._probe = dbus.Interface(self._object, "org.tutorius.ProbeInterface") @@ -269,20 +250,21 @@ class ProbeProxy: self._subscribedEvents = {} self._registeredCallbacks = {} + self._object.connect_to_signal("eventOccured", self._handle_signal, dbus_interface="org.tutorius.ProbeInterface") def _handle_signal(self, pickled_event): event = pickle.loads(str(pickled_event)) - logging.debug("ProbeProxy :: Received Event : %s %s", str(event), str(event._props.items())) + LOGGER.debug("ProbeProxy :: Received Event : %s %s", str(event), str(event._props.items())) - logging.debug("ProbeProxy :: Currently %d events registered", len(self._registeredCallbacks)) + LOGGER.debug("ProbeProxy :: Currently %d events registered", len(self._registeredCallbacks)) if self._registeredCallbacks.has_key(event): for callback in self._registeredCallbacks[event].itervalues(): callback(event) else: for event in self._registeredCallbacks.keys(): - logging.debug("==== %s", str(event._props.items())) - logging.debug("ProbeProxy :: Event does not appear to be registered") + LOGGER.debug("==== %s", str(event._props.items())) + LOGGER.debug("ProbeProxy :: Event does not appear to be registered") def isAlive(self): try: @@ -300,41 +282,38 @@ class ProbeProxy: """ Install an action on the TProbe's activity @param action Action to install + @param block Force a synchroneous dbus call if True @return None """ - remote_call(self._probe.install, (pickle.dumps(action),), + return remote_call(self._probe.install, (pickle.dumps(action),), save_args(self.__update_action, action), block=block) - def update(self, action, block=False): + def update(self, action, newaction, block=False): """ Update an already installed action's properties and run it again @param action Action to update + @param newaction Action to update it with + @param block Force a synchroneous dbus call if True @return None """ + #TODO review how to make this work well if not action in self._actions: raise RuntimeWarning("Action not installed") - return - remote_call(self._probe.update, (self._actions[action], pickle.dumps(action._props)), block=block) + #TODO Check error handling + return remote_call(self._probe.update, (self._actions[action], pickle.dumps(newaction._props)), block=block) def uninstall(self, action, block=False): """ Uninstall an installed action @param action Action to uninstall + @param block Force a synchroneous dbus call if True """ if action in self._actions: remote_call(self._probe.uninstall,(self._actions.pop(action),), block=block) - def uninstall_all(self, block=False): - """ - Uninstall all installed actions - @return None - """ - for action in self._actions.keys(): - self.uninstall(action, block) - def __update_event(self, event, callback, address): - logging.debug("ProbeProxy :: Registered event %s with address %s", str(event), str(address)) + LOGGER.debug("ProbeProxy :: Registered event %s with address %s", str(hash(event)), str(address)) # Since multiple callbacks could be associated to the same # event signature, we will store multiple callbacks # in a dictionary indexed by the unique address @@ -365,26 +344,33 @@ class ProbeProxy: return address def __clear_event(self, address): + LOGGER.debug("ProbeProxy :: Unregistering adress %s", str(address)) # Cleanup everything if self._subscribedEvents.has_key(address): event = self._subscribedEvents[address] if self._registeredCallbacks.has_key(event)\ and self._registeredCallbacks[event].has_key(address): + LOGGER.debug("ProbeProxy :: POP ") self._registeredCallbacks[event].pop(address) if self._registeredCallbacks[event] == {}: + LOGGER.debug("ProbeProxy :: POP2 ") self._registeredCallbacks.pop(event) self._subscribedEvents.pop(address) + else: + LOGGER.debug("ProbeProxy :: unsubsribe address %s inconsistency : not registered", address) def subscribe(self, event, callback, block=True): """ Register an event listener @param event Event to listen for @param callback callable that will be called when the event occurs + @param block Force a synchroneous dbus call if True (Not allowed yet) @return address identifier used for unsubscribing """ + LOGGER.debug("ProbeProxy :: Registering event %s", str(hash(event))) if not block: raise RuntimeError("This function does not allow non-blocking mode yet") @@ -395,32 +381,41 @@ class ProbeProxy: save_args(self.__update_event, event, callback), block=block) - def unsubscribe(self, address, block=False): + def unsubscribe(self, address, block=True): """ Unregister an event listener @param address identifier given by subscribe() + @param block Force a synchroneous dbus call if True @return None """ + LOGGER.debug("ProbeProxy :: Unregister adress %s issued", str(address)) + if not block: + raise RuntimeError("This function does not allow non-blocking mode yet") if address in self._subscribedEvents.keys(): remote_call(self._probe.unsubscribe, (address,), return_cb=save_args(self.__clear_event, address), block=block) else: - logging.debug("ProbeProxy :: unsubsribe address %s failed : not registered", address) + LOGGER.debug("ProbeProxy :: unsubsribe address %s failed : not registered", address) - def unsubscribe_all(self, block=False): + def detach(self, block=False): """ - Unregister all event listeners - @return None + Detach the ProbeProxy from it's TProbe. All installed actions and + subscribed events should be removed. """ + for action in self._actions.keys(): + self.uninstall(action, block) + for address in self._subscribedEvents.keys(): self.unsubscribe(address, block) + class ProbeManager(object): """ The ProbeManager provides multiplexing across multiple activity ProbeProxies For now, it only handles one at a time, though. + Actually it doesn't do much at all. But it keeps your encapsulation happy """ def __init__(self): self._probes = {} @@ -438,9 +433,9 @@ class ProbeManager(object): def attach(self, activity_id): if activity_id in self._probes: raise RuntimeWarning("Activity already attached") - return self._probes[activity_id] = ProbeProxy(activity_id) + #TODO what do we do with this? Raise something? if self._probes[activity_id].isAlive(): print "Alive!" else: @@ -449,48 +444,64 @@ class ProbeManager(object): def detach(self, activity_id): if activity_id in self._probes: probe = self._probes.pop(activity_id) - probe.unsubscribe_all() - probe.uninstall_all() + probe.detach() - def install(self, action): - if self.currentActivity: - return self._probes[self.currentActivity].install(action) - else: - raise RuntimeWarning("No activity attached") - - def update(self, action): + def install(self, action, block=False): + """ + Install an action on the current activity + @param action Action to install + @param block Force a synchroneous dbus call if True + @return None + """ if self.currentActivity: - return self._probes[self.currentActivity].update(action) + return self._probes[self.currentActivity].install(action, block) else: raise RuntimeWarning("No activity attached") - def uninstall(self, action): + def update(self, action, newaction, block=False): + """ + Update an already installed action's properties and run it again + @param action Action to update + @param newaction Action to update it with + @param block Force a synchroneous dbus call if True + @return None + """ if self.currentActivity: - return self._probes[self.currentActivity].uninstall(action) + return self._probes[self.currentActivity].update(action, newaction, block) else: raise RuntimeWarning("No activity attached") - def uninstall_all(self): + def uninstall(self, action, block=False): + """ + Uninstall an installed action + @param action Action to uninstall + @param block Force a synchroneous dbus call if True + """ if self.currentActivity: - return self._probes[self.currentActivity].uninstall_all() + return self._probes[self.currentActivity].uninstall(action, block) else: raise RuntimeWarning("No activity attached") def subscribe(self, event, callback): + """ + Register an event listener + @param event Event to listen for + @param callback callable that will be called when the event occurs + @return address identifier used for unsubscribing + """ if self.currentActivity: return self._probes[self.currentActivity].subscribe(event, callback) else: raise RuntimeWarning("No activity attached") def unsubscribe(self, address): + """ + Unregister an event listener + @param address identifier given by subscribe() + @return None + """ if self.currentActivity: return self._probes[self.currentActivity].unsubscribe(address) else: raise RuntimeWarning("No activity attached") - def unsubscribe_all(self): - if self.currentActivity: - return self._probes[self.currentActivity].unsubscribe_all() - else: - raise RuntimeWarning("No activity attached") - -- cgit v0.9.1