home *** CD-ROM | disk | FTP | other *** search
/ Chip 2004 December / CHIP_CD_2004-12.iso / bonus / oo / OOo_1.1.3_ru_RU_infra_WinIntel_install.exe / $PLUGINSDIR / f_0372 / python-core-2.2.2 / lib / xmlrpclib.py < prev    next >
Text File  |  2004-10-09  |  32KB  |  1,017 lines

  1. #
  2. # XML-RPC CLIENT LIBRARY
  3. # $Id: xmlrpclib.py,v 1.15 2001/12/19 21:40:04 effbot Exp $
  4. #
  5. # an XML-RPC client interface for Python.
  6. #
  7. # the marshalling and response parser code can also be used to
  8. # implement XML-RPC servers.
  9. #
  10. # Notes:
  11. # this version is designed to work with Python 1.5.2 or newer.
  12. # unicode encoding support requires at least Python 1.6.
  13. # experimental HTTPS requires Python 2.0 built with SSL sockets.
  14. # expat parser support requires Python 2.0 with pyexpat support.
  15. #
  16. # History:
  17. # 1999-01-14 fl  Created
  18. # 1999-01-15 fl  Changed dateTime to use localtime
  19. # 1999-01-16 fl  Added Binary/base64 element, default to RPC2 service
  20. # 1999-01-19 fl  Fixed array data element (from Skip Montanaro)
  21. # 1999-01-21 fl  Fixed dateTime constructor, etc.
  22. # 1999-02-02 fl  Added fault handling, handle empty sequences, etc.
  23. # 1999-02-10 fl  Fixed problem with empty responses (from Skip Montanaro)
  24. # 1999-06-20 fl  Speed improvements, pluggable parsers/transports (0.9.8)
  25. # 2000-11-28 fl  Changed boolean to check the truth value of its argument
  26. # 2001-02-24 fl  Added encoding/Unicode/SafeTransport patches
  27. # 2001-02-26 fl  Added compare support to wrappers (0.9.9/1.0b1)
  28. # 2001-03-28 fl  Make sure response tuple is a singleton
  29. # 2001-03-29 fl  Don't require empty params element (from Nicholas Riley)
  30. # 2001-06-10 fl  Folded in _xmlrpclib accelerator support (1.0b2)
  31. # 2001-08-20 fl  Base xmlrpclib.Error on built-in Exception (from Paul Prescod)
  32. # 2001-09-03 fl  Allow Transport subclass to override getparser
  33. # 2001-09-10 fl  Lazy import of urllib, cgi, xmllib (20x import speedup)
  34. # 2001-10-01 fl  Remove containers from memo cache when done with them
  35. # 2001-10-01 fl  Use faster escape method (80% dumps speedup)
  36. # 2001-10-10 sm  Allow long ints to be passed as ints if they don't overflow
  37. # 2001-10-17 sm  test for int and long overflow (allows use on 64-bit systems)
  38. # 2001-11-12 fl  Use repr() to marshal doubles (from Paul Felix)
  39. #
  40. # Copyright (c) 1999-2001 by Secret Labs AB.
  41. # Copyright (c) 1999-2001 by Fredrik Lundh.
  42. #
  43. # info@pythonware.com
  44. # http://www.pythonware.com
  45. #
  46. # --------------------------------------------------------------------
  47. # The XML-RPC client interface is
  48. #
  49. # Copyright (c) 1999-2001 by Secret Labs AB
  50. # Copyright (c) 1999-2001 by Fredrik Lundh
  51. #
  52. # By obtaining, using, and/or copying this software and/or its
  53. # associated documentation, you agree that you have read, understood,
  54. # and will comply with the following terms and conditions:
  55. #
  56. # Permission to use, copy, modify, and distribute this software and
  57. # its associated documentation for any purpose and without fee is
  58. # hereby granted, provided that the above copyright notice appears in
  59. # all copies, and that both that copyright notice and this permission
  60. # notice appear in supporting documentation, and that the name of
  61. # Secret Labs AB or the author not be used in advertising or publicity
  62. # pertaining to distribution of the software without specific, written
  63. # prior permission.
  64. #
  65. # SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
  66. # TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
  67. # ABILITY AND FITNESS.  IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR
  68. # BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
  69. # DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
  70. # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
  71. # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
  72. # OF THIS SOFTWARE.
  73. # --------------------------------------------------------------------
  74.  
  75. #
  76. # things to look into:
  77.  
  78. # TODO: support basic authentication (see robin's patch)
  79. # TODO: fix host tuple handling in the server constructor
  80. # TODO: let transport verify schemes
  81. # TODO: update documentation
  82. # TODO: authentication plugins
  83.  
  84. """
  85. An XML-RPC client interface for Python.
  86.  
  87. The marshalling and response parser code can also be used to
  88. implement XML-RPC servers.
  89.  
  90. Exported exceptions:
  91.  
  92.   Error          Base class for client errors
  93.   ProtocolError  Indicates an HTTP protocol error
  94.   ResponseError  Indicates a broken response package
  95.   Fault          Indicates an XML-RPC fault package
  96.  
  97. Exported classes:
  98.  
  99.   ServerProxy    Represents a logical connection to an XML-RPC server
  100.  
  101.   Boolean        boolean wrapper to generate a "boolean" XML-RPC value
  102.   DateTime       dateTime wrapper for an ISO 8601 string or time tuple or
  103.                  localtime integer value to generate a "dateTime.iso8601"
  104.                  XML-RPC value
  105.   Binary         binary data wrapper
  106.  
  107.   SlowParser     Slow but safe standard parser (based on xmllib)
  108.   Marshaller     Generate an XML-RPC params chunk from a Python data structure
  109.   Unmarshaller   Unmarshal an XML-RPC response from incoming XML event message
  110.   Transport      Handles an HTTP transaction to an XML-RPC server
  111.   SafeTransport  Handles an HTTPS transaction to an XML-RPC server
  112.  
  113. Exported constants:
  114.  
  115.   True
  116.   False
  117.  
  118. Exported functions:
  119.  
  120.   boolean        Convert any Python value to an XML-RPC boolean
  121.   getparser      Create instance of the fastest available parser & attach
  122.                  to an unmarshalling object
  123.   dumps          Convert an argument tuple or a Fault instance to an XML-RPC
  124.                  request (or response, if the methodresponse option is used).
  125.   loads          Convert an XML-RPC packet to unmarshalled data plus a method
  126.                  name (None if not present).
  127. """
  128.  
  129. import re, string, time, operator
  130.  
  131. from types import *
  132.  
  133. try:
  134.     unicode
  135. except NameError:
  136.     unicode = None # unicode support not available
  137.  
  138. def _decode(data, encoding, is8bit=re.compile("[\x80-\xff]").search):
  139.     # decode non-ascii string (if possible)
  140.     if unicode and encoding and is8bit(data):
  141.         data = unicode(data, encoding)
  142.     return data
  143.  
  144. def escape(s, replace=string.replace):
  145.     s = replace(s, "&", "&")
  146.     s = replace(s, "<", "<")
  147.     return replace(s, ">", ">",)
  148.  
  149. MAXINT =  2L**31-1
  150. MININT = -2L**31
  151.  
  152. if unicode:
  153.     def _stringify(string):
  154.         # convert to 7-bit ascii if possible
  155.         try:
  156.             return str(string)
  157.         except UnicodeError:
  158.             return string
  159. else:
  160.     def _stringify(string):
  161.         return string
  162.  
  163. __version__ = "1.0.0"
  164.  
  165. # --------------------------------------------------------------------
  166. # Exceptions
  167.  
  168. class Error(Exception):
  169.     """Base class for client errors."""
  170.     def __str__(self):
  171.         return repr(self)
  172.  
  173. class ProtocolError(Error):
  174.     """Indicates an HTTP protocol error."""
  175.     def __init__(self, url, errcode, errmsg, headers):
  176.         Error.__init__(self)
  177.         self.url = url
  178.         self.errcode = errcode
  179.         self.errmsg = errmsg
  180.         self.headers = headers
  181.     def __repr__(self):
  182.         return (
  183.             "<ProtocolError for %s: %s %s>" %
  184.             (self.url, self.errcode, self.errmsg)
  185.             )
  186.  
  187. class ResponseError(Error):
  188.     """Indicates a broken response package."""
  189.     pass
  190.  
  191. class Fault(Error):
  192.     """Indicates an XML-RPC fault package."""
  193.     def __init__(self, faultCode, faultString, **extra):
  194.         Error.__init__(self)
  195.         self.faultCode = faultCode
  196.         self.faultString = faultString
  197.     def __repr__(self):
  198.         return (
  199.             "<Fault %s: %s>" %
  200.             (self.faultCode, repr(self.faultString))
  201.             )
  202.  
  203. # --------------------------------------------------------------------
  204. # Special values
  205.  
  206. class Boolean:
  207.     """Boolean-value wrapper.
  208.  
  209.     Use True or False to generate a "boolean" XML-RPC value.
  210.     """
  211.  
  212.     def __init__(self, value = 0):
  213.         self.value = operator.truth(value)
  214.  
  215.     def encode(self, out):
  216.         out.write("<value><boolean>%d</boolean></value>\n" % self.value)
  217.  
  218.     def __cmp__(self, other):
  219.         if isinstance(other, Boolean):
  220.             other = other.value
  221.         return cmp(self.value, other)
  222.  
  223.     def __repr__(self):
  224.         if self.value:
  225.             return "<Boolean True at %x>" % id(self)
  226.         else:
  227.             return "<Boolean False at %x>" % id(self)
  228.  
  229.     def __int__(self):
  230.         return self.value
  231.  
  232.     def __nonzero__(self):
  233.         return self.value
  234.  
  235. True, False = Boolean(1), Boolean(0)
  236.  
  237. def boolean(value, truefalse=(False, True)):
  238.     """Convert any Python value to XML-RPC 'boolean'."""
  239.     return truefalse[operator.truth(value)]
  240.  
  241. class DateTime:
  242.     """DateTime wrapper for an ISO 8601 string or time tuple or
  243.     localtime integer value to generate 'dateTime.iso8601' XML-RPC
  244.     value.
  245.     """
  246.  
  247.     def __init__(self, value=0):
  248.         if not isinstance(value, StringType):
  249.             if not isinstance(value, TupleType):
  250.                 if value == 0:
  251.                     value = time.time()
  252.                 value = time.localtime(value)
  253.             value = time.strftime("%Y%m%dT%H:%M:%S", value)
  254.         self.value = value
  255.  
  256.     def __cmp__(self, other):
  257.         if isinstance(other, DateTime):
  258.             other = other.value
  259.         return cmp(self.value, other)
  260.  
  261.     def __repr__(self):
  262.         return "<DateTime %s at %x>" % (self.value, id(self))
  263.  
  264.     def decode(self, data):
  265.         self.value = string.strip(data)
  266.  
  267.     def encode(self, out):
  268.         out.write("<value><dateTime.iso8601>")
  269.         out.write(self.value)
  270.         out.write("</dateTime.iso8601></value>\n")
  271.  
  272. def datetime(data):
  273.     value = DateTime()
  274.     value.decode(data)
  275.     return value
  276.  
  277. class Binary:
  278.     """Wrapper for binary data."""
  279.  
  280.     def __init__(self, data=None):
  281.         self.data = data
  282.  
  283.     def __cmp__(self, other):
  284.         if isinstance(other, Binary):
  285.             other = other.data
  286.         return cmp(self.data, other)
  287.  
  288.     def decode(self, data):
  289.         import base64
  290.         self.data = base64.decodestring(data)
  291.  
  292.     def encode(self, out):
  293.         import base64, StringIO
  294.         out.write("<value><base64>\n")
  295.         base64.encode(StringIO.StringIO(self.data), out)
  296.         out.write("</base64></value>\n")
  297.  
  298. def binary(data):
  299.     value = Binary()
  300.     value.decode(data)
  301.     return value
  302.  
  303. WRAPPERS = DateTime, Binary, Boolean
  304.  
  305. # --------------------------------------------------------------------
  306. # XML parsers
  307.  
  308. try:
  309.     # optional xmlrpclib accelerator.  for more information on this
  310.     # component, contact info@pythonware.com
  311.     import _xmlrpclib
  312.     FastParser = _xmlrpclib.Parser
  313.     FastUnmarshaller = _xmlrpclib.Unmarshaller
  314. except (AttributeError, ImportError):
  315.     FastParser = FastUnmarshaller = None
  316.  
  317. #
  318. # the SGMLOP parser is about 15x faster than Python's builtin
  319. # XML parser.  SGMLOP sources can be downloaded from:
  320. #
  321. #     http://www.pythonware.com/products/xml/sgmlop.htm
  322. #
  323.  
  324. try:
  325.     import sgmlop
  326.     if not hasattr(sgmlop, "XMLParser"):
  327.         raise ImportError
  328. except ImportError:
  329.     SgmlopParser = None # sgmlop accelerator not available
  330. else:
  331.     class SgmlopParser:
  332.         def __init__(self, target):
  333.  
  334.             # setup callbacks
  335.             self.finish_starttag = target.start
  336.             self.finish_endtag = target.end
  337.             self.handle_data = target.data
  338.             self.handle_xml = target.xml
  339.  
  340.             # activate parser
  341.             self.parser = sgmlop.XMLParser()
  342.             self.parser.register(self)
  343.             self.feed = self.parser.feed
  344.             self.entity = {
  345.                 "amp": "&", "gt": ">", "lt": "<",
  346.                 "apos": "'", "quot": '"'
  347.                 }
  348.  
  349.         def close(self):
  350.             try:
  351.                 self.parser.close()
  352.             finally:
  353.                 self.parser = self.feed = None # nuke circular reference
  354.  
  355.         def handle_proc(self, tag, attr):
  356.             import re
  357.             m = re.search("encoding\s*=\s*['\"]([^\"']+)[\"']", attr)
  358.             if m:
  359.                 self.handle_xml(m.group(1), 1)
  360.  
  361.         def handle_entityref(self, entity):
  362.             # <string> entity
  363.             try:
  364.                 self.handle_data(self.entity[entity])
  365.             except KeyError:
  366.                 self.handle_data("&%s;" % entity)
  367.  
  368. try:
  369.     from xml.parsers import expat
  370.     if not hasattr(expat, "ParserCreate"):
  371.         raise ImportError, "ParserCreate"
  372. except ImportError:
  373.     ExpatParser = None
  374. else:
  375.     class ExpatParser:
  376.         # fast expat parser for Python 2.0.  this is about 50%
  377.         # slower than sgmlop, on roundtrip testing
  378.         def __init__(self, target):
  379.             self._parser = parser = expat.ParserCreate(None, None)
  380.             self._target = target
  381.             parser.StartElementHandler = target.start
  382.             parser.EndElementHandler = target.end
  383.             parser.CharacterDataHandler = target.data
  384.             encoding = None
  385.             if not parser.returns_unicode:
  386.                 encoding = "utf-8"
  387.             target.xml(encoding, None)
  388.  
  389.         def feed(self, data):
  390.             self._parser.Parse(data, 0)
  391.  
  392.         def close(self):
  393.             self._parser.Parse("", 1) # end of data
  394.             del self._target, self._parser # get rid of circular references
  395.  
  396. class SlowParser:
  397.     """Default XML parser (based on xmllib.XMLParser)."""
  398.     # this is about 10 times slower than sgmlop, on roundtrip
  399.     # testing.
  400.     def __init__(self, target):
  401.         import xmllib # lazy subclassing (!)
  402.         if xmllib.XMLParser not in SlowParser.__bases__:
  403.             SlowParser.__bases__ = (xmllib.XMLParser,)
  404.         self.handle_xml = target.xml
  405.         self.unknown_starttag = target.start
  406.         self.handle_data = target.data
  407.         self.unknown_endtag = target.end
  408.         try:
  409.             xmllib.XMLParser.__init__(self, accept_utf8=1)
  410.         except TypeError:
  411.             xmllib.XMLParser.__init__(self) # pre-2.0
  412.  
  413. # --------------------------------------------------------------------
  414. # XML-RPC marshalling and unmarshalling code
  415.  
  416. class Marshaller:
  417.     """Generate an XML-RPC params chunk from a Python data structure.
  418.  
  419.     Create a Marshaller instance for each set of parameters, and use
  420.     the "dumps" method to convert your data (represented as a tuple)
  421.     to an XML-RPC params chunk.  To write a fault response, pass a
  422.     Fault instance instead.  You may prefer to use the "dumps" module
  423.     function for this purpose.
  424.     """
  425.  
  426.     # by the way, if you don't understand what's going on in here,
  427.     # that's perfectly ok.
  428.  
  429.     def __init__(self, encoding=None):
  430.         self.memo = {}
  431.         self.data = None
  432.         self.encoding = encoding
  433.  
  434.     dispatch = {}
  435.  
  436.     def dumps(self, values):
  437.         self.__out = []
  438.         self.write = write = self.__out.append
  439.         if isinstance(values, Fault):
  440.             # fault instance
  441.             write("<fault>\n")
  442.             self.__dump(vars(values))
  443.             write("</fault>\n")
  444.         else:
  445.             # parameter block
  446.             # FIXME: the xml-rpc specification allows us to leave out
  447.             # the entire <params> block if there are no parameters.
  448.             # however, changing this may break older code (including
  449.             # old versions of xmlrpclib.py), so this is better left as
  450.             # is for now.  See @XMLRPC3 for more information. /F
  451.             write("<params>\n")
  452.             for v in values:
  453.                 write("<param>\n")
  454.                 self.__dump(v)
  455.                 write("</param>\n")
  456.             write("</params>\n")
  457.         result = string.join(self.__out, "")
  458.         del self.__out, self.write # don't need this any more
  459.         return result
  460.  
  461.     def __dump(self, value):
  462.         try:
  463.             f = self.dispatch[type(value)]
  464.         except KeyError:
  465.             raise TypeError, "cannot marshal %s objects" % type(value)
  466.         else:
  467.             f(self, value)
  468.  
  469.     def dump_int(self, value):
  470.         # in case ints are > 32 bits
  471.         if value > MAXINT or value < MININT:
  472.             raise OverflowError, "int exceeds XML-RPC limits"
  473.         self.write("<value><int>%s</int></value>\n" % value)
  474.     dispatch[IntType] = dump_int
  475.  
  476.     def dump_long(self, value):
  477.         # in case ints are > 32 bits
  478.         if value > MAXINT or value < MININT:
  479.             raise OverflowError, "long int exceeds XML-RPC limits"
  480.         self.write("<value><int>%s</int></value>\n" % int(value))
  481.     dispatch[LongType] = dump_long
  482.  
  483.     def dump_double(self, value):
  484.         self.write("<value><double>%s</double></value>\n" % repr(value))
  485.     dispatch[FloatType] = dump_double
  486.  
  487.     def dump_string(self, value, escape=escape):
  488.         self.write("<value><string>%s</string></value>\n" % escape(value))
  489.     dispatch[StringType] = dump_string
  490.  
  491.     if unicode:
  492.         def dump_unicode(self, value, escape=escape):
  493.             value = value.encode(self.encoding)
  494.             self.write("<value><string>%s</string></value>\n" % escape(value))
  495.         dispatch[UnicodeType] = dump_unicode
  496.  
  497.     def opencontainer(self, value):
  498.         if value:
  499.             i = id(value)
  500.             if self.memo.has_key(i):
  501.                 raise TypeError, "cannot marshal recursive data structures"
  502.             self.memo[i] = None
  503.  
  504.     def closecontainer(self, value):
  505.         if value:
  506.             del self.memo[id(value)]
  507.  
  508.     def dump_array(self, value):
  509.         self.opencontainer(value)
  510.         write = self.write
  511.         dump = self.__dump
  512.         write("<value><array><data>\n")
  513.         for v in value:
  514.             dump(v)
  515.         write("</data></array></value>\n")
  516.         self.closecontainer(value)
  517.     dispatch[TupleType] = dump_array
  518.     dispatch[ListType] = dump_array
  519.  
  520.     def dump_struct(self, value, escape=escape):
  521.         self.opencontainer(value)
  522.         write = self.write
  523.         dump = self.__dump
  524.         write("<value><struct>\n")
  525.         for k, v in value.items():
  526.             write("<member>\n")
  527.             if type(k) is not StringType:
  528.                 raise TypeError, "dictionary key must be string"
  529.             write("<name>%s</name>\n" % escape(k))
  530.             dump(v)
  531.             write("</member>\n")
  532.         write("</struct></value>\n")
  533.         self.closecontainer(value)
  534.     dispatch[DictType] = dump_struct
  535.  
  536.     def dump_instance(self, value):
  537.         # check for special wrappers
  538.         if value.__class__ in WRAPPERS:
  539.             value.encode(self)
  540.         else:
  541.             # store instance attributes as a struct (really?)
  542.             self.dump_struct(value.__dict__)
  543.     dispatch[InstanceType] = dump_instance
  544.  
  545. class Unmarshaller:
  546.     """Unmarshal an XML-RPC response, based on incoming XML event
  547.     messages (start, data, end).  Call close() to get the resulting
  548.     data structure.
  549.  
  550.     Note that this reader is fairly tolerant, and gladly accepts bogus
  551.     XML-RPC data without complaining (but not bogus XML).
  552.     """
  553.  
  554.     # and again, if you don't understand what's going on in here,
  555.     # that's perfectly ok.
  556.  
  557.     def __init__(self):
  558.         self._type = None
  559.         self._stack = []
  560.         self._marks = []
  561.         self._data = []
  562.         self._methodname = None
  563.         self._encoding = "utf-8"
  564.         self.append = self._stack.append
  565.  
  566.     def close(self):
  567.         # return response tuple and target method
  568.         if self._type is None or self._marks:
  569.             raise ResponseError()
  570.         if self._type == "fault":
  571.             raise apply(Fault, (), self._stack[0])
  572.         return tuple(self._stack)
  573.  
  574.     def getmethodname(self):
  575.         return self._methodname
  576.  
  577.     #
  578.     # event handlers
  579.  
  580.     def xml(self, encoding, standalone):
  581.         self._encoding = encoding
  582.         # FIXME: assert standalone == 1 ???
  583.  
  584.     def start(self, tag, attrs):
  585.         # prepare to handle this element
  586.         if tag == "array" or tag == "struct":
  587.             self._marks.append(len(self._stack))
  588.         self._data = []
  589.         self._value = (tag == "value")
  590.  
  591.     def data(self, text):
  592.         self._data.append(text)
  593.  
  594.     def end(self, tag, join=string.join):
  595.         # call the appropriate end tag handler
  596.         try:
  597.             f = self.dispatch[tag]
  598.         except KeyError:
  599.             pass # unknown tag ?
  600.         else:
  601.             return f(self, join(self._data, ""))
  602.  
  603.     #
  604.     # accelerator support
  605.  
  606.     def end_dispatch(self, tag, data):
  607.         # dispatch data
  608.         try:
  609.             f = self.dispatch[tag]
  610.         except KeyError:
  611.             pass # unknown tag ?
  612.         else:
  613.             return f(self, data)
  614.  
  615.     #
  616.     # element decoders
  617.  
  618.     dispatch = {}
  619.  
  620.     def end_boolean(self, data):
  621.         if data == "0":
  622.             self.append(False)
  623.         elif data == "1":
  624.             self.append(True)
  625.         else:
  626.             raise TypeError, "bad boolean value"
  627.         self._value = 0
  628.     dispatch["boolean"] = end_boolean
  629.  
  630.     def end_int(self, data):
  631.         self.append(int(data))
  632.         self._value = 0
  633.     dispatch["i4"] = end_int
  634.     dispatch["int"] = end_int
  635.  
  636.     def end_double(self, data):
  637.         self.append(float(data))
  638.         self._value = 0
  639.     dispatch["double"] = end_double
  640.  
  641.     def end_string(self, data):
  642.         if self._encoding:
  643.             data = _decode(data, self._encoding)
  644.         self.append(_stringify(data))
  645.         self._value = 0
  646.     dispatch["string"] = end_string
  647.     dispatch["name"] = end_string # struct keys are always strings
  648.  
  649.     def end_array(self, data):
  650.         mark = self._marks[-1]
  651.         del self._marks[-1]
  652.         # map arrays to Python lists
  653.         self._stack[mark:] = [self._stack[mark:]]
  654.         self._value = 0
  655.     dispatch["array"] = end_array
  656.  
  657.     def end_struct(self, data):
  658.         mark = self._marks[-1]
  659.         del self._marks[-1]
  660.         # map structs to Python dictionaries
  661.         dict = {}
  662.         items = self._stack[mark:]
  663.         for i in range(0, len(items), 2):
  664.             dict[_stringify(items[i])] = items[i+1]
  665.         self._stack[mark:] = [dict]
  666.         self._value = 0
  667.     dispatch["struct"] = end_struct
  668.  
  669.     def end_base64(self, data):
  670.         value = Binary()
  671.         value.decode(data)
  672.         self.append(value)
  673.         self._value = 0
  674.     dispatch["base64"] = end_base64
  675.  
  676.     def end_dateTime(self, data):
  677.         value = DateTime()
  678.         value.decode(data)
  679.         self.append(value)
  680.     dispatch["dateTime.iso8601"] = end_dateTime
  681.  
  682.     def end_value(self, data):
  683.         # if we stumble upon an value element with no internal
  684.         # elements, treat it as a string element
  685.         if self._value:
  686.             self.end_string(data)
  687.     dispatch["value"] = end_value
  688.  
  689.     def end_params(self, data):
  690.         self._type = "params"
  691.     dispatch["params"] = end_params
  692.  
  693.     def end_fault(self, data):
  694.         self._type = "fault"
  695.     dispatch["fault"] = end_fault
  696.  
  697.     def end_methodName(self, data):
  698.         if self._encoding:
  699.             data = _decode(data, self._encoding)
  700.         self._methodname = data
  701.         self._type = "methodName" # no params
  702.     dispatch["methodName"] = end_methodName
  703.  
  704.  
  705. # --------------------------------------------------------------------
  706. # convenience functions
  707.  
  708. def getparser():
  709.     """getparser() -> parser, unmarshaller
  710.  
  711.     Create an instance of the fastest available parser, and attach it
  712.     to an unmarshalling object.  Return both objects.
  713.     """
  714.     if FastParser and FastUnmarshaller:
  715.         target = FastUnmarshaller(True, False, binary, datetime)
  716.         parser = FastParser(target)
  717.     else:
  718.         target = Unmarshaller()
  719.         if FastParser:
  720.             parser = FastParser(target)
  721.         elif SgmlopParser:
  722.             parser = SgmlopParser(target)
  723.         elif ExpatParser:
  724.             parser = ExpatParser(target)
  725.         else:
  726.             parser = SlowParser(target)
  727.     return parser, target
  728.  
  729. def dumps(params, methodname=None, methodresponse=None, encoding=None):
  730.     """data [,options] -> marshalled data
  731.  
  732.     Convert an argument tuple or a Fault instance to an XML-RPC
  733.     request (or response, if the methodresponse option is used).
  734.  
  735.     In addition to the data object, the following options can be given
  736.     as keyword arguments:
  737.  
  738.         methodname: the method name for a methodCall packet
  739.  
  740.         methodresponse: true to create a methodResponse packet.
  741.         If this option is used with a tuple, the tuple must be
  742.         a singleton (i.e. it can contain only one element).
  743.  
  744.         encoding: the packet encoding (default is UTF-8)
  745.  
  746.     All 8-bit strings in the data structure are assumed to use the
  747.     packet encoding.  Unicode strings are automatically converted,
  748.     where necessary.
  749.     """
  750.  
  751.     assert isinstance(params, TupleType) or isinstance(params, Fault),\
  752.            "argument must be tuple or Fault instance"
  753.  
  754.     if isinstance(params, Fault):
  755.         methodresponse = 1
  756.     elif methodresponse and isinstance(params, TupleType):
  757.         assert len(params) == 1, "response tuple must be a singleton"
  758.  
  759.     if not encoding:
  760.         encoding = "utf-8"
  761.  
  762.     m = Marshaller(encoding)
  763.     data = m.dumps(params)
  764.  
  765.     if encoding != "utf-8":
  766.         xmlheader = "<?xml version='1.0' encoding=%s?>\n" % repr(encoding)
  767.     else:
  768.         xmlheader = "<?xml version='1.0'?>\n" # utf-8 is default
  769.  
  770.     # standard XML-RPC wrappings
  771.     if methodname:
  772.         # a method call
  773.         if not isinstance(methodname, StringType):
  774.             methodname = methodname.encode(encoding)
  775.         data = (
  776.             xmlheader,
  777.             "<methodCall>\n"
  778.             "<methodName>", methodname, "</methodName>\n",
  779.             data,
  780.             "</methodCall>\n"
  781.             )
  782.     elif methodresponse:
  783.         # a method response, or a fault structure
  784.         data = (
  785.             xmlheader,
  786.             "<methodResponse>\n",
  787.             data,
  788.             "</methodResponse>\n"
  789.             )
  790.     else:
  791.         return data # return as is
  792.     return string.join(data, "")
  793.  
  794. def loads(data):
  795.     """data -> unmarshalled data, method name
  796.  
  797.     Convert an XML-RPC packet to unmarshalled data plus a method
  798.     name (None if not present).
  799.  
  800.     If the XML-RPC packet represents a fault condition, this function
  801.     raises a Fault exception.
  802.     """
  803.     p, u = getparser()
  804.     p.feed(data)
  805.     p.close()
  806.     return u.close(), u.getmethodname()
  807.  
  808.  
  809. # --------------------------------------------------------------------
  810. # request dispatcher
  811.  
  812. class _Method:
  813.     # some magic to bind an XML-RPC method to an RPC server.
  814.     # supports "nested" methods (e.g. examples.getStateName)
  815.     def __init__(self, send, name):
  816.         self.__send = send
  817.         self.__name = name
  818.     def __getattr__(self, name):
  819.         return _Method(self.__send, "%s.%s" % (self.__name, name))
  820.     def __call__(self, *args):
  821.         return self.__send(self.__name, args)
  822.  
  823.  
  824. class Transport:
  825.     """Handles an HTTP transaction to an XML-RPC server."""
  826.  
  827.     # client identifier (may be overridden)
  828.     user_agent = "xmlrpclib.py/%s (by www.pythonware.com)" % __version__
  829.  
  830.     def request(self, host, handler, request_body, verbose=0):
  831.         # issue XML-RPC request
  832.  
  833.         h = self.make_connection(host)
  834.         if verbose:
  835.             h.set_debuglevel(1)
  836.  
  837.         self.send_request(h, handler, request_body)
  838.         self.send_host(h, host)
  839.         self.send_user_agent(h)
  840.         self.send_content(h, request_body)
  841.  
  842.         errcode, errmsg, headers = h.getreply()
  843.  
  844.         if errcode != 200:
  845.             raise ProtocolError(
  846.                 host + handler,
  847.                 errcode, errmsg,
  848.                 headers
  849.                 )
  850.  
  851.         self.verbose = verbose
  852.  
  853.         return self.parse_response(h.getfile())
  854.  
  855.     def getparser(self):
  856.         # get parser and unmarshaller
  857.         return getparser()
  858.  
  859.     def make_connection(self, host):
  860.         # create a HTTP connection object from a host descriptor
  861.         import httplib
  862.         return httplib.HTTP(host)
  863.  
  864.     def send_request(self, connection, handler, request_body):
  865.         connection.putrequest("POST", handler)
  866.  
  867.     def send_host(self, connection, host):
  868.         connection.putheader("Host", host)
  869.  
  870.     def send_user_agent(self, connection):
  871.         connection.putheader("User-Agent", self.user_agent)
  872.  
  873.     def send_content(self, connection, request_body):
  874.         connection.putheader("Content-Type", "text/xml")
  875.         connection.putheader("Content-Length", str(len(request_body)))
  876.         connection.endheaders()
  877.         if request_body:
  878.             connection.send(request_body)
  879.  
  880.     def parse_response(self, f):
  881.         # read response from input file, and parse it
  882.  
  883.         p, u = self.getparser()
  884.  
  885.         while 1:
  886.             response = f.read(1024)
  887.             if not response:
  888.                 break
  889.             if self.verbose:
  890.                 print "body:", repr(response)
  891.             p.feed(response)
  892.  
  893.         f.close()
  894.         p.close()
  895.  
  896.         return u.close()
  897.  
  898. class SafeTransport(Transport):
  899.     """Handles an HTTPS transaction to an XML-RPC server."""
  900.  
  901.     def make_connection(self, host):
  902.         # create a HTTPS connection object from a host descriptor
  903.         # host may be a string, or a (host, x509-dict) tuple
  904.         import httplib
  905.         if isinstance(host, TupleType):
  906.             host, x509 = host
  907.         else:
  908.             x509 = {}
  909.         try:
  910.             HTTPS = httplib.HTTPS
  911.         except AttributeError:
  912.             raise NotImplementedError,\
  913.                   "your version of httplib doesn't support HTTPS"
  914.         else:
  915.             return apply(HTTPS, (host, None), x509)
  916.  
  917.     def send_host(self, connection, host):
  918.         if isinstance(host, TupleType):
  919.             host, x509 = host
  920.         connection.putheader("Host", host)
  921.  
  922. class ServerProxy:
  923.     """uri [,options] -> a logical connection to an XML-RPC server
  924.  
  925.     uri is the connection point on the server, given as
  926.     scheme://host/target.
  927.  
  928.     The standard implementation always supports the "http" scheme.  If
  929.     SSL socket support is available (Python 2.0), it also supports
  930.     "https".
  931.  
  932.     If the target part and the slash preceding it are both omitted,
  933.     "/RPC2" is assumed.
  934.  
  935.     The following options can be given as keyword arguments:
  936.  
  937.         transport: a transport factory
  938.         encoding: the request encoding (default is UTF-8)
  939.  
  940.     All 8-bit strings passed to the server proxy are assumed to use
  941.     the given encoding.
  942.     """
  943.  
  944.     def __init__(self, uri, transport=None, encoding=None, verbose=0):
  945.         # establish a "logical" server connection
  946.  
  947.         # get the url
  948.         import urllib
  949.         type, uri = urllib.splittype(uri)
  950.         if type not in ("http", "https"):
  951.             raise IOError, "unsupported XML-RPC protocol"
  952.         self.__host, self.__handler = urllib.splithost(uri)
  953.         if not self.__handler:
  954.             self.__handler = "/RPC2"
  955.  
  956.         if transport is None:
  957.             if type == "https":
  958.                 transport = SafeTransport()
  959.             else:
  960.                 transport = Transport()
  961.         self.__transport = transport
  962.  
  963.         self.__encoding = encoding
  964.         self.__verbose = verbose
  965.  
  966.     def __request(self, methodname, params):
  967.         # call a method on the remote server
  968.  
  969.         request = dumps(params, methodname, encoding=self.__encoding)
  970.  
  971.         response = self.__transport.request(
  972.             self.__host,
  973.             self.__handler,
  974.             request,
  975.             verbose=self.__verbose
  976.             )
  977.  
  978.         if len(response) == 1:
  979.             response = response[0]
  980.  
  981.         return response
  982.  
  983.     def __repr__(self):
  984.         return (
  985.             "<ServerProxy for %s%s>" %
  986.             (self.__host, self.__handler)
  987.             )
  988.  
  989.     __str__ = __repr__
  990.  
  991.     def __getattr__(self, name):
  992.         # magic method dispatcher
  993.         return _Method(self.__request, name)
  994.  
  995.     # note: to call a remote object with an non-standard name, use
  996.     # result getattr(server, "strange-python-name")(args)
  997.  
  998. # compatibility
  999. Server = ServerProxy
  1000.  
  1001. # --------------------------------------------------------------------
  1002. # test code
  1003.  
  1004. if __name__ == "__main__":
  1005.  
  1006.     # simple test program (from the XML-RPC specification)
  1007.  
  1008.     # server = ServerProxy("http://localhost:8000") # local server
  1009.     server = ServerProxy("http://betty.userland.com")
  1010.  
  1011.     print server
  1012.  
  1013.     try:
  1014.         print server.examples.getStateName(41)
  1015.     except Error, v:
  1016.         print "ERROR", v
  1017.