PYTHON   22

Implementing Web Services with Quixote

Guest on 27th June 2022 05:37:17 AM

  1. Implementing Web Services with Quixote
  2. ======================================
  3.  
  4. This document will show you how to implement Web services using
  5. Quixote.
  6.  
  7.  
  8. An XML-RPC Service
  9. ------------------
  10.  
  11. XML-RPC is the simplest protocol commonly used to expose a Web
  12. service.  In XML-RPC, there are a few basic data types such as
  13. integers, floats, strings, and dates, and a few aggregate types such
  14. as arrays and structs.  The xmlrpclib module, part of the Python 2.2
  15. standard library and available separately from
  16. http://www.pythonware.com/products/xmlrpc/, converts between Python's
  17. standard data types and the XML-RPC data types.
  18.  
  19. ==============  =====================
  20. XML-RPC Type    Python Type or Class
  21. --------------  ---------------------
  22. <int>           int
  23. <double>        float
  24. <string>        string
  25. <array>         list
  26. <struct>        dict
  27. <boolean>       xmlrpclib.Boolean
  28. <base64>        xmlrpclib.Binary
  29. <dateTime>      xmlrpclib.DateTime
  30. ==============  =====================
  31.  
  32.  
  33. Making XML-RPC Calls
  34. --------------------
  35.  
  36. Making an XML-RPC call using xmlrpclib is easy.  An XML-RPC server
  37. lives at a particular URL, so the first step is to create an
  38. xmlrpclib.ServerProxy object pointing at that URL. ::
  39.  
  40.    >>> import xmlrpclib
  41.    >>> s = xmlrpclib.ServerProxy(
  42.                 'http://www.stuffeddog.com/speller/speller-rpc.cgi')
  43.  
  44. Now you can simply make a call to the spell-checking service offered
  45. by this server::
  46.  
  47.    >>> s.speller.spellCheck('my speling isnt gud', {})
  48.    [{'word': 'speling', 'suggestions': ['apeling', 'spelding',
  49.      'spelling', 'sperling', 'spewing', 'spiling'], 'location': 4},
  50.    {'word': 'isnt', 'suggestions': [``isn't'', 'ist'], 'location': 12}]
  51.     >>>
  52.  
  53. This call results in the following XML being sent::
  54.  
  55.     <?xml version='1.0'?>
  56.     <methodCall>
  57.          <methodName>speller.spellCheck</methodName>
  58.          <params>
  59.              <param>
  60.                     <value><string>my speling isnt gud</string></value>
  61.              </param>
  62.              <param>
  63.                      <value><struct></struct></value>
  64.              </param>
  65.          </params>
  66.     </methodCall>
  67.  
  68.  
  69. Writing a Quixote Service
  70. -------------------------
  71.  
  72. In the quixote.util module, Quixote provides a function,
  73. ``xmlrpc(request, func)``, that processes the body of an XML-RPC
  74. request.  ``request`` is the HTTPRequest object that Quixote passes to
  75. every function it invokes.  ``func`` is a user-supplied function that
  76. receives the name of the XML-RPC method being called and a tuple
  77. containing the method's parameters.  If there's a bug in the function
  78. you supply and it raises an exception, the ``xmlrpc()`` function will
  79. catch the exception and return a ``Fault`` to the remote caller.
  80.  
  81. Here's an example of implementing a simple XML-RPC handler with a
  82. single method, ``get_time()``, that simply returns the current
  83. time.  The first task is to expose a URL for accessing the service. ::
  84.  
  85.    from quixote.directory import Directory
  86.    from quixote.util import xmlrpc
  87.    from quixote import get_request
  88.  
  89.    class RPCDirectory(Directory):
  90.  
  91.        _q_exports = ['rpc']
  92.  
  93.        def rpc (self):
  94.            return xmlrpc(get_request(), rpc_process)
  95.  
  96.    def rpc_process (meth, params):
  97.        ...
  98.  
  99. When the above code is placed in the __init__.py file for the Python
  100. package corresponding to your Quixote application, it exposes the URL
  101. ``http://<hostname>/rpc`` as the access point for the XML-RPC service.
  102.  
  103. Next, we need to fill in the contents of the ``rpc_process()``
  104. function::
  105.  
  106.    import time
  107.  
  108.    def rpc_process (meth, params):
  109.        if meth == 'get_time':
  110.            # params is ignored
  111.            now = time.gmtime(time.time())
  112.            return xmlrpclib.DateTime(now)
  113.        else:
  114.            raise RuntimeError, "Unknown XML-RPC method: %r" % meth
  115.  
  116. ``rpc_process()`` receives the method name and the parameters, and its
  117. job is to run the right code for the method, returning a result that
  118. will be marshalled into XML-RPC.  The body of ``rpc_process()`` will
  119. therefore usually be an ``if`` statement that checks the name of the
  120. method, and calls another function to do the actual work.  In this case,
  121. ``get_time()`` is very simple so the two lines of code it requires are
  122. simply included in the body of ``rpc_process()``.
  123.  
  124. If the method name doesn't belong to a supported method, execution
  125. will fall through to the ``else`` clause, which will raise a
  126. RuntimeError exception.  Quixote's ``xmlrpc()`` will catch this
  127. exception and report it to the caller as an XML-RPC fault, with the
  128. error code set to 1.
  129.  
  130. As you add additional XML-RPC services, the ``if`` statement in
  131. ``rpc_process()`` will grow more branches.  You might be tempted to pass
  132. the method name to ``getattr()`` to select a method from a module or
  133. class.  That would work, too, and avoids having a continually growing
  134. set of branches, but you should be careful with this and be sure that
  135. there are no private methods that a remote caller could access.  I
  136. generally prefer to have the ``if... elif... elif... else`` blocks, for
  137. three reasons: 1) adding another branch isn't much work, 2) it's
  138. explicit about the supported method names, and 3) there won't be any
  139. security holes in doing so.
  140.  
  141. An alternative approach is to have a dictionary mapping method names
  142. to the corresponding functions and restrict the legal method names
  143. to the keys of this dictionary::
  144.  
  145.     def echo (*params):
  146.         # Just returns the parameters it's passed
  147.         return params
  148.  
  149.     def get_time ():
  150.         now = time.gmtime(time.time())
  151.         return xmlrpclib.DateTime(now)
  152.  
  153.     methods = {'echo' : echo,
  154.                'get_time' : get_time}
  155.  
  156.     def rpc_process (meth, params):
  157.         func = methods.get[meth]
  158.         if methods.has_key(meth):
  159.             # params is ignored
  160.             now = time.gmtime(time.time())
  161.             return xmlrpclib.DateTime(now)
  162.         else:
  163.             raise RuntimeError, "Unknown XML-RPC method: %r" % meth
  164.  
  165. This approach works nicely when there are many methods and the
  166. ``if...elif...else`` statement would be unworkably long.

Raw Paste


Login or Register to edit or fork this paste. It's free.