From 00c4fe9e5f2373fcefe6fb3f46e641a3c416423e Mon Sep 17 00:00:00 2001 From: mike Date: Sun, 22 Nov 2009 04:24:14 +0000 Subject: LP 448319 : Moving action installation to asynchronous version with confirmation --- diff --git a/tests/enginetests.py b/tests/enginetests.py index 30d68de..f1efe45 100644 --- a/tests/enginetests.py +++ b/tests/enginetests.py @@ -43,13 +43,14 @@ class MockProbeMgr(object): currentActivity = property(fget=lambda s:s, fset=lambda s, v: v) - def install(self, action, block=False): + def install(self, action, action_installed_cb, error_cb): self.action = action + action_installed_cb(action, 'Action1') - def update(self, action, newaction, block=False): + def update(self, action_address, newaction): self.action = newaction - def uninstall(self, action, block=False): + def uninstall(self, action_address): self.action = None def subscribe(self, event, callback): @@ -73,7 +74,6 @@ class TutorialRunnerTest(unittest.TestCase): def setUp(self): self.pM = MockProbeMgr() - def tearDown(self): self.pM = None diff --git a/tutorius/TProbe.py b/tutorius/TProbe.py index 3a57161..37a5e0f 100644 --- a/tutorius/TProbe.py +++ b/tutorius/TProbe.py @@ -1,3 +1,19 @@ +# Copyright (C) 2009, Tutorius.org +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 1 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + import logging LOGGER = logging.getLogger("sugar.tutorius.TProbe") import os @@ -13,7 +29,7 @@ from . import addon from .services import ObjectStore from .properties import TPropContainer -from .dbustools import remote_call, save_args +from .dbustools import remote_call, save_args, ignore, logError import copy """ @@ -305,18 +321,19 @@ class ProbeProxy: def __clear_action(self, action): self._actions.pop(action, None) - def install(self, action, callback, block=False): + def install(self, action, action_installed_cb, error_cb): """ Install an action on the TProbe's activity @param action Action to install - @param block Force a synchroneous dbus call if True + @param action_installed_cb The callback function to call once the action is installed + @param error_cb The callback function to call when an error happens @return None """ - return remote_call(self._probe.install, (pickle.dumps(action),), - save_args(self.__update_action, action, callback), - block=block) + self._probe.install(pickle.dumps(action), + reply_handler=save_args(self.__update_action, action, action_installed_cb), + error_handler=save_args(error_cb, action)) - def update(self, action_address, newaction, block=False): + def update(self, action_address, newaction): """ Update an already installed action's properties and run it again @param action_address The address of the action to update. This is @@ -329,9 +346,9 @@ class ProbeProxy: if not action_address in self._actions.values(): raise RuntimeWarning("Action not installed") #TODO Check error handling - return remote_call(self._probe.update, (action_address, pickle.dumps(newaction._props)), block=block) + return remote_call(self._probe.update, (action_address, pickle.dumps(newaction._props)), block=False) - def uninstall(self, action_address, block=False): + def uninstall(self, action_address): """ Uninstall an installed action @param action_address The address of the action to uninstall. This address was given @@ -340,7 +357,7 @@ class ProbeProxy: """ for (this_action, this_address) in self._actions.items(): if this_address == action_address: - remote_call(self._probe.uninstall,(action_address,), block=block) + remote_call(self._probe.uninstall,(action_address,)) del self._actions[this_action] break @@ -392,11 +409,13 @@ class ProbeProxy: else: LOGGER.debug("ProbeProxy :: unsubsribe address %s inconsistency : not registered", address) - def subscribe(self, event, callback, block=True): + def subscribe(self, event, notification_cb, event_installed_cb=ignore, error_cb=logError, block=True): """ Register an event listener @param event Event to listen for - @param callback callable that will be called when the event occurs + @param notification_cb callable that will be called when the event occurs + @param event_installed_cb callable that will be called once the event is subscribed to + @param error_cb callable that will be called if the subscription fails @param block Force a synchroneous dbus call if True (Not allowed yet) @return address identifier used for unsubscribing """ @@ -408,7 +427,8 @@ class ProbeProxy: # for event types and sources, we will need to revise the lookup # mecanism for which callback function to call return remote_call(self._probe.subscribe, (pickle.dumps(event),), - save_args(self.__update_event, event, callback), + return_cb=save_args(self.__update_event, event, notification_cb), + error_cb=save_args(error_cb, event), block=block) def unsubscribe(self, address, block=True): @@ -432,10 +452,10 @@ class ProbeProxy: subscribed events should be removed. """ for action_addr in self._actions.keys(): - self.uninstall(action_addr, block) + self.uninstall(action_addr) for address in self._subscribedEvents.keys(): - self.unsubscribe(address, block) + self.unsubscribe(address) class ProbeManager(object): @@ -471,19 +491,21 @@ class ProbeManager(object): currentActivity = property(fget=getCurrentActivity, fset=setCurrentActivity) - def install(self, action, callback, block=False): + def install(self, action, action_installed_cb, error_cb): """ Install an action on the current activity @param action Action to install + @param action_installed_cb The callback to call once the action is installed + @param error_cb The callback that will be called if there is an error during installation @param block Force a synchroneous dbus call if True @return None """ if self.currentActivity: - return self._first_proxy(self.currentActivity).install(action, callback, block) + return self._first_proxy(self.currentActivity).install(action, action_installed_cb, error_cb) else: raise RuntimeWarning("No activity attached") - def update(self, action_address, newaction, block=False): + def update(self, action_address, newaction): """ Update an already installed action's properties and run it again @param action_address Action to update @@ -492,18 +514,18 @@ class ProbeManager(object): @return None """ if self.currentActivity: - return self._first_proxy(self.currentActivity).update(action_address, newaction, block) + return self._first_proxy(self.currentActivity).update(action_address, newaction) else: raise RuntimeWarning("No activity attached") - def uninstall(self, action_address, block=False): + def uninstall(self, action_address): """ Uninstall an installed action @param action Action to uninstall @param block Force a synchroneous dbus call if True """ if self.currentActivity: - return self._first_proxy(self.currentActivity).uninstall(action_address, block) + return self._first_proxy(self.currentActivity).uninstall(action_address) else: raise RuntimeWarning("No activity attached") diff --git a/tutorius/dbustools.py b/tutorius/dbustools.py index 02acd3d..0973164 100644 --- a/tutorius/dbustools.py +++ b/tutorius/dbustools.py @@ -1,3 +1,19 @@ +# Copyright (C) 2009, Tutorius.org +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 1 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + import logging import gobject diff --git a/tutorius/engine.py b/tutorius/engine.py index 10c8d14..bb2453a 100644 --- a/tutorius/engine.py +++ b/tutorius/engine.py @@ -30,6 +30,7 @@ class TutorialRunner(object): #Cached objects self._actions = {} self._installed_actions = {} + self._install_errors = {} #Temp FIX until event/actions have an activity id self._activity_id = None @@ -62,17 +63,45 @@ class TutorialRunner(object): LOGGER.debug("TutorialRunner :: Uninstalling action %s with address %s"%(action_name, action_address)) self._pM.uninstall(action_address) self._actions = {} - self._installed_actions = {} + self._installed_actions.clear() + self._install_errors.clear() #Clear the EventFilters for event in self._sEvents: self._pM.unsubscribe(event) self._sEvents.clear() - def __action_installed(self, action_name, address): + def __action_installed(self, action_name, action, address): LOGGER.debug("TutorialRunner :: Action %s received address %s"%(action_name, address)) self._installed_actions[action_name] = address + # Do the check to see if we have finished installing all the actions by either having + # received a address for it or an error message + install_complete = True + for (this_action_name, this_action) in self._actions.items(): + if not this_action_name in self._installed_actions.keys() and not this_action in self._install_errors.keys(): + # There's at least one uninstalled action, so we still wait + install_complete = False + break + + if install_complete: + LOGGER.debug("TutorialRunner :: All actions installed!") + # Raise the All Actions Installed event for the TutorialRunner state + self._all_actions_installed() + + def __install_error(self, action_name, action, exception): + # TODO : Fix this as it doesn't warn the user about the problem or anything + LOGGER.debug("TutorialRunner :: Action could not be installed %s, exception was : %s"%(str(action) + str(exception))) + self._install_errors[action_name] = exception + + def _all_actions_installed(self): + transitions = self._tutorial.get_transition_dict(self._state) + + if len(transitions) == 0: + return + for (event, next_state) in transitions.values(): + self._sEvents.add(self._pM.subscribe(event, save_args(self._handleEvent, next_state))) + def _setupState(self): if self._state is None: raise RuntimeError("Attempting to setupState without a state") @@ -88,13 +117,15 @@ class TutorialRunner(object): state_name = next_state return state_name + if len(self._actions) == 0: + self._all_actions_installed() + return state_name + for (action_name, action) in self._actions.items(): - self._pM.install(action, save_args(self.__action_installed, action_name), True) + self._pM.install(action, save_args(self.__action_installed, action_name), + save_args(self.__install_error, action_name)) LOGGER.debug("TutorialRunner :: Installed action %s"%(action_name)) - - for (event, next_state) in transitions.values(): - self._sEvents.add(self._pM.subscribe(event, save_args(self._handleEvent, next_state))) - + return state_name def enterState(self, state_name): -- cgit v0.9.1