# ---------------------------------------------------------------------------- # Copyright (c) 2007 Humanized, Inc. All rights reserved. # ---------------------------------------------------------------------------- # # SampleEnsoExtension.py # # Python Version - 2.4 # # ---------------------------------------------------------------------------- """ This sample script sets up an Enso Extension for use with the Enso Developer Prototype Beta Product. """ # ---------------------------------------------------------------------------- # Imports # ---------------------------------------------------------------------------- from SimpleXMLRPCServer import SimpleXMLRPCServer import xmlrpclib import socket import time import threading # ---------------------------------------------------------------------------- # Constants # ---------------------------------------------------------------------------- # The full URL for the XML-RPC endpoint of the Enso Developer # Prototype, which we need to connect to in order to access Enso # services. For the time being, this is hard-coded, but in the future # we may use some sort of name service to look it up. XMLRPC_ENDPOINT_URL = "http://127.0.0.1:11374" # The TCP port that our Enso Extension's XML-RPC endpoint will be # located on. EXTENSION_ENDPOINT_PORT = 8620 # The IP address that our Enso Extension's XML-RPC endpoint be located # on. EXTENSION_ENDPOINT_ADDRESS = "127.0.0.1" # The full URL for our Enso Extension's XML-RPC endpoint. EXTENSION_ENDPOINT_URL = "http://%s:%d" % ( EXTENSION_ENDPOINT_ADDRESS, EXTENSION_ENDPOINT_PORT ) # ---------------------------------------------------------------------------- # Server Thread # ---------------------------------------------------------------------------- class ServerThread(threading.Thread): """ Simple thread that encapsulates the running of the server that hosts our Enso Extension's XML-RPC endpoint. """ def __init__( self, address ): threading.Thread.__init__( self ) self._rpcServer = None self._stop = False self._address = address def run( self ): self._rpcServer = SimpleXMLRPCServer( self._address ) # We want to set the timeout so that we can CTRL-C out of the # server on Windows machines. Windows won't let keyboard # interrupts kick a process out of a socket system call, so we # have to listen for incoming connections in 1-second # "chunks". self._rpcServer.socket.settimeout( 1.0 ) self._rpcServer.register_instance( EnsoExtensionMethods() ) while not self._stop: self._rpcServer.handle_request() def stop( self ): self._stop = True class EnsoExtensionMethods( object ): """ Methods for our Enso Extension's XML-RPC endpoint, which implement the Enso Extension API. Every public method here (i.e., every method that doesn't start with an underscore character) is a required method that must be implemented by all Enso Extensions, which Enso will call into when an Extension's services are required. """ def __init__( self ): self.enso = xmlrpclib.ServerProxy( XMLRPC_ENDPOINT_URL ) def callCommand( self, commandName, postfix ): """ Extension API method that is called whenever an Enso user has executed a command that our Extension implements. This method must complete as soon as possible, because it is currently executed synchronously by Enso. 'commandName' is the full name of the command that is to be run. 'postfix' is the postfix (i.e., argument) that the user has specified for the command. For instance, in the 'order {food}' command, this will be a string representing the type of food the end-user wants to order. If a postfix is not specified or not applicable to a command, this will be an empty string. """ if commandName == "translate to swedish": self.__translateToSwedish() elif commandName == "order {food}": self.__orderFood( postfix ) else: raise AssertionError( "Unknown command name: %s" % \ commandName ) # The Enso Developer Prototype doesn't look at this return # value, but we can't return None because it's not part of the # XML-RPC specification, so we'll return True instead. return True def __translateToSwedish( self ): """ Implementation of the 'translate to swedish' command. """ import urllib # URL of the Swedish translation web service. URL = "http://www.cs.utexas.edu/users/jbc/bork/bork.cgi?" # Call an Enso Developer Prototype API function to get the # user's current unicode selection. text = self.enso.getUnicodeSelection() if len( text ) == 0: # Use the Enso Developer Prototype API to display a # transparent primary message to the end-user. self.enso.displayMessage( "
No text selected!
" ) else: params = { "input": text, "type": "chef" } params = urllib.urlencode( params ) # Note that we're making a network connection here, which # could take a long time to complete. Ideally, this # should be executed in a separate thread, and only if it # takes longer than about 250 ms--the maximum wait time # before end-users start clicking on things--should the # Enso Extension print a primary message telling the # end-user to "please wait". Furthermore, network errors # should be caught and converted into primary messages # informing the end-user. Actually implementing such # behavior is left as an exercise for the reader. translated = urllib.urlopen( URL, params ).read() translated = translated.decode( "iso-8859-1" ) # Use the Enso Developer Prototype API to replace the # user's current unicode selection. The second parameter # is the name of the command that is originating the # change, for display purposes only. If the unicode # selection can't be changed (e.g., because it is static # text on a web page), then the text will automatically be # inserted into Enso's put buffer for insertion at a later # time by the user (via the 'put' command). self.enso.setUnicodeSelection( translated, "translate to swedish" ) def __orderFood( self, foodName ): """ Implementation of the 'order {food}' command. """ # The 'caption' tag can be used to provide a caption for the # main text, displayed in a slightly smaller font and # different color from the main text. self.enso.displayMessage( u"Ordering %s\u2026
" "%s ordered!
" "This command translates the current" \ "text selection to Swedish. Try it on this " \ "sentence.
", # The postfix type of the command; it can either be # 'none', in which case this is an ordinary command that # takes no special postfix argument, or 'bounded', in # which case the end-user can choose a postfix from a list # of possibilities. "none" ) enso.registerCommand( EXTENSION_ENDPOINT_URL, "order {food}", "order some food.", "Allows you to order food.
", "bounded" ) # Set valid postfixes for the 'order {food}' bounded command. # This function can be called at any time, if and when the # list of valid postfixes changes. enso.setCommandValidPostfixes( EXTENSION_ENDPOINT_URL, "order {food}", ["pizza", "lingonberries"] ) try: # Now just block for input while our server thread # listens for incoming connections from Enso. print "press enter to exit." raw_input() finally: # Before we leave, let Enso know that we're not offering # our commands anymore. enso.unregisterCommand( EXTENSION_ENDPOINT_URL, "translate to swedish" ) enso.unregisterCommand( EXTENSION_ENDPOINT_URL, "order {food}" ) finally: print "Shutting down XML-RPC server." serverThread.stop()