diff options
Diffstat (limited to 'Experior.Activity/sugarbot.py')
-rwxr-xr-x | Experior.Activity/sugarbot.py | 207 |
1 files changed, 207 insertions, 0 deletions
diff --git a/Experior.Activity/sugarbot.py b/Experior.Activity/sugarbot.py new file mode 100755 index 0000000..717ffed --- /dev/null +++ b/Experior.Activity/sugarbot.py @@ -0,0 +1,207 @@ +#!/usr/bin/env python +# encoding: utf-8 +""" +sugarbot.py + +This file is part of sugarbot. + +sugarbot 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 3 of the License, or +(at your option) any later version. + +sugarbot 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 sugarbot. If not, see <http://www.gnu.org/licenses/>. +""" +import gobject +import gtk +import logging +import os +import sblog +import socket +import sys + +from xmlrpclib import ServerProxy + +from sugar.activity import activity +#from sugar.activity import registry +from jarabe.model.bundleregistry import get_registry + +from sbrpcserver import sbRpcServer, proxyString + +# For reading GDK.Event's +from sbgui import sbGUI + +class sugarbot(activity.Activity): + def __dynamicImport(self, fullname, path=None): + """ + Handles emulation of the target Activity. + + Dynamically modifies the module-search path, and imports an object from + a module. Some fancy work is done to get that to work properly. + @param fullname: The module to import, example calculate.Calculate. + @param path: If necessary, the path to the imported module. + """ + module = None + module_name = "" + class_name = "" + + if fullname is None: + return self.__parentClass + + try: + # <hack>========================================================= + # This could probably be cleaned up or refactored into separate + # functions. However, the code is short enough that it is not + # likely worth it. + if path != None: + sys.path.append(path) + + splitted_module = fullname.rsplit('.', 1) + + if len(splitted_module) >= 2: + module_name = splitted_module[0] + class_name = splitted_module[1] + + if module_name is not None and class_name is not None and \ + len(module_name) > 0 and len(class_name) > 0: + module = __import__(module_name) + + for comp in module_name.split('.')[1:]: + module = getattr(module, comp) + # </hack>======================================================== + except ImportError: + raise ImportError, \ + "Error in sugarbot._dynamicImport(%s,%s)" % (fullname,path) + + finally: + if hasattr(module, class_name): + self.__parentClass = getattr(module, class_name) + else: # Failsafe + self.__parentClass = activity.Activity + + return self.__parentClass + + def __cloneActivity(self,sugarHandle): + """ + Starts the cloned activity, where self.__parentClass is the activity + class. This is performed by [1] inheriting from the parentClass, + and [2] calling parentClass.__init__. + """ + if self.__parentClass is None: + raise AttributeError, "self.__parentClass not defined properly." + + else: + sugarbot.__bases__ = (self.__parentClass,) + self.__parentClass.__init__(self,sugarHandle) + + def __getActivityList(self): + """ + Gets a list of activities from the registry. Stores this list in + self.__activityList. + """ + # Prevent looking up all of the activities multiple times. + if not hasattr(self,"__activityList"): + #self.__activityList = registry.get_registry().get_activities() + self.__activityList = get_registry()._bundles + return self.__activityList + + # If we didn't get any activities, something went wrong. + if len(self.__activityList) < 1: + raise "Activity list is empty. Cannot get activity info!" + + return None + + def __selectActivity(self,name): + """ + Selects an activity from the activityList by name. This allows + simpler access to the list of activities. + (For example, one can specify only 'Calculate' instead of + 'org.laptop.Calculate' or 'calculate.Calculate'). + @param name: The name of the activity (e.g. 'Calculate') + """ + # Get the list of activities if it does not already exist + activityList = self.__getActivityList() + + # Initialization of some variables... + self.__path = None + self.__importClass = None + self.__className = None + + # Iterate through the activity list + for activity in activityList: + if activity.get_name() == name: + self.__path = activity.get_path() + self.__importClass = activity.get_command().split()[-1] + self.__className = self.__importClass.split(".")[-1] + self.log.debug("Importing class %s" % name) + return + + # If we ever get here, that means we didn't find anything... + if (self.__path is None) and (self.__importClass is None) and \ + (self.__className is None): + raise NameError, "Could not find '%s' in activity list." % name + + def __initializeScript(self): + """ + Initialize the script on the XML-RPC server. + Returns the name of the Activity that should be instantiated + """ + rpc = self.__xmlRPC + + try: + self.sessID = os.environ['SUGARBOT_ID'] + except KeyError: + self.sessID = 0 + + # Start the script + if not rpc.startScript(self.sessID): + rpc.fail("Could not start the script!", self.sessID) + exit() + + # Get our activity name + activityName = rpc.getActivityName(self.sessID) + if activityName is None: + rpc.fail('Bad activity name provided', self.sessID) + exit() + + self.log.info("Activity is %s" % activityName) + return activityName + + + def __init__(self, handle): + """ + Performs setup, and runs the specified activity. + """ + self.log = logging.getLogger('sugarbot') + + # Set up threading... + gtk.gdk.threads_init() + + # Handle is set to 'None' for testing purposes via Nose. Obviously, + # if Sugar sets the handle to None, there are other problems... + if handle is None: + return + + try: + # Create the RPC connection object + self.__xmlRPC = ServerProxy(proxyString()) + + # Set up the sbGUI object for automation + self.__sbgui = sbGUI(self, self.__xmlRPC) + + # Get our activity name + activityName = self.__initializeScript() + + # Actually clone the activity + self.__selectActivity(activityName) + self.__dynamicImport(self.__importClass,self.__path) + self.__cloneActivity(handle) + except socket.error: + sys.log.error("====== COULD NOT CONNECT TO XML-RPC SERVER ======") + sys.exit(-1) |