symbian-qemu-0.9.1-12/python-win32-2.6.1/lib/xmlrpclib.py
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     1 #
       
     2 # XML-RPC CLIENT LIBRARY
       
     3 # $Id: xmlrpclib.py 65467 2008-08-04 00:50:11Z brett.cannon $
       
     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 2.1 or newer.
       
    12 #
       
    13 # History:
       
    14 # 1999-01-14 fl  Created
       
    15 # 1999-01-15 fl  Changed dateTime to use localtime
       
    16 # 1999-01-16 fl  Added Binary/base64 element, default to RPC2 service
       
    17 # 1999-01-19 fl  Fixed array data element (from Skip Montanaro)
       
    18 # 1999-01-21 fl  Fixed dateTime constructor, etc.
       
    19 # 1999-02-02 fl  Added fault handling, handle empty sequences, etc.
       
    20 # 1999-02-10 fl  Fixed problem with empty responses (from Skip Montanaro)
       
    21 # 1999-06-20 fl  Speed improvements, pluggable parsers/transports (0.9.8)
       
    22 # 2000-11-28 fl  Changed boolean to check the truth value of its argument
       
    23 # 2001-02-24 fl  Added encoding/Unicode/SafeTransport patches
       
    24 # 2001-02-26 fl  Added compare support to wrappers (0.9.9/1.0b1)
       
    25 # 2001-03-28 fl  Make sure response tuple is a singleton
       
    26 # 2001-03-29 fl  Don't require empty params element (from Nicholas Riley)
       
    27 # 2001-06-10 fl  Folded in _xmlrpclib accelerator support (1.0b2)
       
    28 # 2001-08-20 fl  Base xmlrpclib.Error on built-in Exception (from Paul Prescod)
       
    29 # 2001-09-03 fl  Allow Transport subclass to override getparser
       
    30 # 2001-09-10 fl  Lazy import of urllib, cgi, xmllib (20x import speedup)
       
    31 # 2001-10-01 fl  Remove containers from memo cache when done with them
       
    32 # 2001-10-01 fl  Use faster escape method (80% dumps speedup)
       
    33 # 2001-10-02 fl  More dumps microtuning
       
    34 # 2001-10-04 fl  Make sure import expat gets a parser (from Guido van Rossum)
       
    35 # 2001-10-10 sm  Allow long ints to be passed as ints if they don't overflow
       
    36 # 2001-10-17 sm  Test for int and long overflow (allows use on 64-bit systems)
       
    37 # 2001-11-12 fl  Use repr() to marshal doubles (from Paul Felix)
       
    38 # 2002-03-17 fl  Avoid buffered read when possible (from James Rucker)
       
    39 # 2002-04-07 fl  Added pythondoc comments
       
    40 # 2002-04-16 fl  Added __str__ methods to datetime/binary wrappers
       
    41 # 2002-05-15 fl  Added error constants (from Andrew Kuchling)
       
    42 # 2002-06-27 fl  Merged with Python CVS version
       
    43 # 2002-10-22 fl  Added basic authentication (based on code from Phillip Eby)
       
    44 # 2003-01-22 sm  Add support for the bool type
       
    45 # 2003-02-27 gvr Remove apply calls
       
    46 # 2003-04-24 sm  Use cStringIO if available
       
    47 # 2003-04-25 ak  Add support for nil
       
    48 # 2003-06-15 gn  Add support for time.struct_time
       
    49 # 2003-07-12 gp  Correct marshalling of Faults
       
    50 # 2003-10-31 mvl Add multicall support
       
    51 # 2004-08-20 mvl Bump minimum supported Python version to 2.1
       
    52 #
       
    53 # Copyright (c) 1999-2002 by Secret Labs AB.
       
    54 # Copyright (c) 1999-2002 by Fredrik Lundh.
       
    55 #
       
    56 # info@pythonware.com
       
    57 # http://www.pythonware.com
       
    58 #
       
    59 # --------------------------------------------------------------------
       
    60 # The XML-RPC client interface is
       
    61 #
       
    62 # Copyright (c) 1999-2002 by Secret Labs AB
       
    63 # Copyright (c) 1999-2002 by Fredrik Lundh
       
    64 #
       
    65 # By obtaining, using, and/or copying this software and/or its
       
    66 # associated documentation, you agree that you have read, understood,
       
    67 # and will comply with the following terms and conditions:
       
    68 #
       
    69 # Permission to use, copy, modify, and distribute this software and
       
    70 # its associated documentation for any purpose and without fee is
       
    71 # hereby granted, provided that the above copyright notice appears in
       
    72 # all copies, and that both that copyright notice and this permission
       
    73 # notice appear in supporting documentation, and that the name of
       
    74 # Secret Labs AB or the author not be used in advertising or publicity
       
    75 # pertaining to distribution of the software without specific, written
       
    76 # prior permission.
       
    77 #
       
    78 # SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
       
    79 # TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
       
    80 # ABILITY AND FITNESS.  IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR
       
    81 # BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
       
    82 # DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
       
    83 # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
       
    84 # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
       
    85 # OF THIS SOFTWARE.
       
    86 # --------------------------------------------------------------------
       
    87 
       
    88 #
       
    89 # things to look into some day:
       
    90 
       
    91 # TODO: sort out True/False/boolean issues for Python 2.3
       
    92 
       
    93 """
       
    94 An XML-RPC client interface for Python.
       
    95 
       
    96 The marshalling and response parser code can also be used to
       
    97 implement XML-RPC servers.
       
    98 
       
    99 Exported exceptions:
       
   100 
       
   101   Error          Base class for client errors
       
   102   ProtocolError  Indicates an HTTP protocol error
       
   103   ResponseError  Indicates a broken response package
       
   104   Fault          Indicates an XML-RPC fault package
       
   105 
       
   106 Exported classes:
       
   107 
       
   108   ServerProxy    Represents a logical connection to an XML-RPC server
       
   109 
       
   110   MultiCall      Executor of boxcared xmlrpc requests
       
   111   Boolean        boolean wrapper to generate a "boolean" XML-RPC value
       
   112   DateTime       dateTime wrapper for an ISO 8601 string or time tuple or
       
   113                  localtime integer value to generate a "dateTime.iso8601"
       
   114                  XML-RPC value
       
   115   Binary         binary data wrapper
       
   116 
       
   117   SlowParser     Slow but safe standard parser (based on xmllib)
       
   118   Marshaller     Generate an XML-RPC params chunk from a Python data structure
       
   119   Unmarshaller   Unmarshal an XML-RPC response from incoming XML event message
       
   120   Transport      Handles an HTTP transaction to an XML-RPC server
       
   121   SafeTransport  Handles an HTTPS transaction to an XML-RPC server
       
   122 
       
   123 Exported constants:
       
   124 
       
   125   True
       
   126   False
       
   127 
       
   128 Exported functions:
       
   129 
       
   130   boolean        Convert any Python value to an XML-RPC boolean
       
   131   getparser      Create instance of the fastest available parser & attach
       
   132                  to an unmarshalling object
       
   133   dumps          Convert an argument tuple or a Fault instance to an XML-RPC
       
   134                  request (or response, if the methodresponse option is used).
       
   135   loads          Convert an XML-RPC packet to unmarshalled data plus a method
       
   136                  name (None if not present).
       
   137 """
       
   138 
       
   139 import re, string, time, operator
       
   140 
       
   141 from types import *
       
   142 
       
   143 # --------------------------------------------------------------------
       
   144 # Internal stuff
       
   145 
       
   146 try:
       
   147     unicode
       
   148 except NameError:
       
   149     unicode = None # unicode support not available
       
   150 
       
   151 try:
       
   152     import datetime
       
   153 except ImportError:
       
   154     datetime = None
       
   155 
       
   156 try:
       
   157     _bool_is_builtin = False.__class__.__name__ == "bool"
       
   158 except NameError:
       
   159     _bool_is_builtin = 0
       
   160 
       
   161 def _decode(data, encoding, is8bit=re.compile("[\x80-\xff]").search):
       
   162     # decode non-ascii string (if possible)
       
   163     if unicode and encoding and is8bit(data):
       
   164         data = unicode(data, encoding)
       
   165     return data
       
   166 
       
   167 def escape(s, replace=string.replace):
       
   168     s = replace(s, "&", "&")
       
   169     s = replace(s, "<", "&lt;")
       
   170     return replace(s, ">", "&gt;",)
       
   171 
       
   172 if unicode:
       
   173     def _stringify(string):
       
   174         # convert to 7-bit ascii if possible
       
   175         try:
       
   176             return string.encode("ascii")
       
   177         except UnicodeError:
       
   178             return string
       
   179 else:
       
   180     def _stringify(string):
       
   181         return string
       
   182 
       
   183 __version__ = "1.0.1"
       
   184 
       
   185 # xmlrpc integer limits
       
   186 MAXINT =  2L**31-1
       
   187 MININT = -2L**31
       
   188 
       
   189 # --------------------------------------------------------------------
       
   190 # Error constants (from Dan Libby's specification at
       
   191 # http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php)
       
   192 
       
   193 # Ranges of errors
       
   194 PARSE_ERROR       = -32700
       
   195 SERVER_ERROR      = -32600
       
   196 APPLICATION_ERROR = -32500
       
   197 SYSTEM_ERROR      = -32400
       
   198 TRANSPORT_ERROR   = -32300
       
   199 
       
   200 # Specific errors
       
   201 NOT_WELLFORMED_ERROR  = -32700
       
   202 UNSUPPORTED_ENCODING  = -32701
       
   203 INVALID_ENCODING_CHAR = -32702
       
   204 INVALID_XMLRPC        = -32600
       
   205 METHOD_NOT_FOUND      = -32601
       
   206 INVALID_METHOD_PARAMS = -32602
       
   207 INTERNAL_ERROR        = -32603
       
   208 
       
   209 # --------------------------------------------------------------------
       
   210 # Exceptions
       
   211 
       
   212 ##
       
   213 # Base class for all kinds of client-side errors.
       
   214 
       
   215 class Error(Exception):
       
   216     """Base class for client errors."""
       
   217     def __str__(self):
       
   218         return repr(self)
       
   219 
       
   220 ##
       
   221 # Indicates an HTTP-level protocol error.  This is raised by the HTTP
       
   222 # transport layer, if the server returns an error code other than 200
       
   223 # (OK).
       
   224 #
       
   225 # @param url The target URL.
       
   226 # @param errcode The HTTP error code.
       
   227 # @param errmsg The HTTP error message.
       
   228 # @param headers The HTTP header dictionary.
       
   229 
       
   230 class ProtocolError(Error):
       
   231     """Indicates an HTTP protocol error."""
       
   232     def __init__(self, url, errcode, errmsg, headers):
       
   233         Error.__init__(self)
       
   234         self.url = url
       
   235         self.errcode = errcode
       
   236         self.errmsg = errmsg
       
   237         self.headers = headers
       
   238     def __repr__(self):
       
   239         return (
       
   240             "<ProtocolError for %s: %s %s>" %
       
   241             (self.url, self.errcode, self.errmsg)
       
   242             )
       
   243 
       
   244 ##
       
   245 # Indicates a broken XML-RPC response package.  This exception is
       
   246 # raised by the unmarshalling layer, if the XML-RPC response is
       
   247 # malformed.
       
   248 
       
   249 class ResponseError(Error):
       
   250     """Indicates a broken response package."""
       
   251     pass
       
   252 
       
   253 ##
       
   254 # Indicates an XML-RPC fault response package.  This exception is
       
   255 # raised by the unmarshalling layer, if the XML-RPC response contains
       
   256 # a fault string.  This exception can also used as a class, to
       
   257 # generate a fault XML-RPC message.
       
   258 #
       
   259 # @param faultCode The XML-RPC fault code.
       
   260 # @param faultString The XML-RPC fault string.
       
   261 
       
   262 class Fault(Error):
       
   263     """Indicates an XML-RPC fault package."""
       
   264     def __init__(self, faultCode, faultString, **extra):
       
   265         Error.__init__(self)
       
   266         self.faultCode = faultCode
       
   267         self.faultString = faultString
       
   268     def __repr__(self):
       
   269         return (
       
   270             "<Fault %s: %s>" %
       
   271             (self.faultCode, repr(self.faultString))
       
   272             )
       
   273 
       
   274 # --------------------------------------------------------------------
       
   275 # Special values
       
   276 
       
   277 ##
       
   278 # Wrapper for XML-RPC boolean values.  Use the xmlrpclib.True and
       
   279 # xmlrpclib.False constants, or the xmlrpclib.boolean() function, to
       
   280 # generate boolean XML-RPC values.
       
   281 #
       
   282 # @param value A boolean value.  Any true value is interpreted as True,
       
   283 #              all other values are interpreted as False.
       
   284 
       
   285 from sys import modules
       
   286 mod_dict = modules[__name__].__dict__
       
   287 if _bool_is_builtin:
       
   288     boolean = Boolean = bool
       
   289     # to avoid breaking code which references xmlrpclib.{True,False}
       
   290     mod_dict['True'] = True
       
   291     mod_dict['False'] = False
       
   292 else:
       
   293     class Boolean:
       
   294         """Boolean-value wrapper.
       
   295 
       
   296         Use True or False to generate a "boolean" XML-RPC value.
       
   297         """
       
   298 
       
   299         def __init__(self, value = 0):
       
   300             self.value = operator.truth(value)
       
   301 
       
   302         def encode(self, out):
       
   303             out.write("<value><boolean>%d</boolean></value>\n" % self.value)
       
   304 
       
   305         def __cmp__(self, other):
       
   306             if isinstance(other, Boolean):
       
   307                 other = other.value
       
   308             return cmp(self.value, other)
       
   309 
       
   310         def __repr__(self):
       
   311             if self.value:
       
   312                 return "<Boolean True at %x>" % id(self)
       
   313             else:
       
   314                 return "<Boolean False at %x>" % id(self)
       
   315 
       
   316         def __int__(self):
       
   317             return self.value
       
   318 
       
   319         def __nonzero__(self):
       
   320             return self.value
       
   321 
       
   322     mod_dict['True'] = Boolean(1)
       
   323     mod_dict['False'] = Boolean(0)
       
   324 
       
   325     ##
       
   326     # Map true or false value to XML-RPC boolean values.
       
   327     #
       
   328     # @def boolean(value)
       
   329     # @param value A boolean value.  Any true value is mapped to True,
       
   330     #              all other values are mapped to False.
       
   331     # @return xmlrpclib.True or xmlrpclib.False.
       
   332     # @see Boolean
       
   333     # @see True
       
   334     # @see False
       
   335 
       
   336     def boolean(value, _truefalse=(False, True)):
       
   337         """Convert any Python value to XML-RPC 'boolean'."""
       
   338         return _truefalse[operator.truth(value)]
       
   339 
       
   340 del modules, mod_dict
       
   341 
       
   342 ##
       
   343 # Wrapper for XML-RPC DateTime values.  This converts a time value to
       
   344 # the format used by XML-RPC.
       
   345 # <p>
       
   346 # The value can be given as a string in the format
       
   347 # "yyyymmddThh:mm:ss", as a 9-item time tuple (as returned by
       
   348 # time.localtime()), or an integer value (as returned by time.time()).
       
   349 # The wrapper uses time.localtime() to convert an integer to a time
       
   350 # tuple.
       
   351 #
       
   352 # @param value The time, given as an ISO 8601 string, a time
       
   353 #              tuple, or a integer time value.
       
   354 
       
   355 def _strftime(value):
       
   356     if datetime:
       
   357         if isinstance(value, datetime.datetime):
       
   358             return "%04d%02d%02dT%02d:%02d:%02d" % (
       
   359                 value.year, value.month, value.day,
       
   360                 value.hour, value.minute, value.second)
       
   361 
       
   362     if not isinstance(value, (TupleType, time.struct_time)):
       
   363         if value == 0:
       
   364             value = time.time()
       
   365         value = time.localtime(value)
       
   366 
       
   367     return "%04d%02d%02dT%02d:%02d:%02d" % value[:6]
       
   368 
       
   369 class DateTime:
       
   370     """DateTime wrapper for an ISO 8601 string or time tuple or
       
   371     localtime integer value to generate 'dateTime.iso8601' XML-RPC
       
   372     value.
       
   373     """
       
   374 
       
   375     def __init__(self, value=0):
       
   376         if isinstance(value, StringType):
       
   377             self.value = value
       
   378         else:
       
   379             self.value = _strftime(value)
       
   380 
       
   381     def make_comparable(self, other):
       
   382         if isinstance(other, DateTime):
       
   383             s = self.value
       
   384             o = other.value
       
   385         elif datetime and isinstance(other, datetime.datetime):
       
   386             s = self.value
       
   387             o = other.strftime("%Y%m%dT%H:%M:%S")
       
   388         elif isinstance(other, (str, unicode)):
       
   389             s = self.value
       
   390             o = other
       
   391         elif hasattr(other, "timetuple"):
       
   392             s = self.timetuple()
       
   393             o = other.timetuple()
       
   394         else:
       
   395             otype = (hasattr(other, "__class__")
       
   396                      and other.__class__.__name__
       
   397                      or type(other))
       
   398             raise TypeError("Can't compare %s and %s" %
       
   399                             (self.__class__.__name__, otype))
       
   400         return s, o
       
   401 
       
   402     def __lt__(self, other):
       
   403         s, o = self.make_comparable(other)
       
   404         return s < o
       
   405 
       
   406     def __le__(self, other):
       
   407         s, o = self.make_comparable(other)
       
   408         return s <= o
       
   409 
       
   410     def __gt__(self, other):
       
   411         s, o = self.make_comparable(other)
       
   412         return s > o
       
   413 
       
   414     def __ge__(self, other):
       
   415         s, o = self.make_comparable(other)
       
   416         return s >= o
       
   417 
       
   418     def __eq__(self, other):
       
   419         s, o = self.make_comparable(other)
       
   420         return s == o
       
   421 
       
   422     def __ne__(self, other):
       
   423         s, o = self.make_comparable(other)
       
   424         return s != o
       
   425 
       
   426     def timetuple(self):
       
   427         return time.strptime(self.value, "%Y%m%dT%H:%M:%S")
       
   428 
       
   429     def __cmp__(self, other):
       
   430         s, o = self.make_comparable(other)
       
   431         return cmp(s, o)
       
   432 
       
   433     ##
       
   434     # Get date/time value.
       
   435     #
       
   436     # @return Date/time value, as an ISO 8601 string.
       
   437 
       
   438     def __str__(self):
       
   439         return self.value
       
   440 
       
   441     def __repr__(self):
       
   442         return "<DateTime %s at %x>" % (repr(self.value), id(self))
       
   443 
       
   444     def decode(self, data):
       
   445         data = str(data)
       
   446         self.value = string.strip(data)
       
   447 
       
   448     def encode(self, out):
       
   449         out.write("<value><dateTime.iso8601>")
       
   450         out.write(self.value)
       
   451         out.write("</dateTime.iso8601></value>\n")
       
   452 
       
   453 def _datetime(data):
       
   454     # decode xml element contents into a DateTime structure.
       
   455     value = DateTime()
       
   456     value.decode(data)
       
   457     return value
       
   458 
       
   459 def _datetime_type(data):
       
   460     t = time.strptime(data, "%Y%m%dT%H:%M:%S")
       
   461     return datetime.datetime(*tuple(t)[:6])
       
   462 
       
   463 ##
       
   464 # Wrapper for binary data.  This can be used to transport any kind
       
   465 # of binary data over XML-RPC, using BASE64 encoding.
       
   466 #
       
   467 # @param data An 8-bit string containing arbitrary data.
       
   468 
       
   469 import base64
       
   470 try:
       
   471     import cStringIO as StringIO
       
   472 except ImportError:
       
   473     import StringIO
       
   474 
       
   475 class Binary:
       
   476     """Wrapper for binary data."""
       
   477 
       
   478     def __init__(self, data=None):
       
   479         self.data = data
       
   480 
       
   481     ##
       
   482     # Get buffer contents.
       
   483     #
       
   484     # @return Buffer contents, as an 8-bit string.
       
   485 
       
   486     def __str__(self):
       
   487         return self.data or ""
       
   488 
       
   489     def __cmp__(self, other):
       
   490         if isinstance(other, Binary):
       
   491             other = other.data
       
   492         return cmp(self.data, other)
       
   493 
       
   494     def decode(self, data):
       
   495         self.data = base64.decodestring(data)
       
   496 
       
   497     def encode(self, out):
       
   498         out.write("<value><base64>\n")
       
   499         base64.encode(StringIO.StringIO(self.data), out)
       
   500         out.write("</base64></value>\n")
       
   501 
       
   502 def _binary(data):
       
   503     # decode xml element contents into a Binary structure
       
   504     value = Binary()
       
   505     value.decode(data)
       
   506     return value
       
   507 
       
   508 WRAPPERS = (DateTime, Binary)
       
   509 if not _bool_is_builtin:
       
   510     WRAPPERS = WRAPPERS + (Boolean,)
       
   511 
       
   512 # --------------------------------------------------------------------
       
   513 # XML parsers
       
   514 
       
   515 try:
       
   516     # optional xmlrpclib accelerator
       
   517     import _xmlrpclib
       
   518     FastParser = _xmlrpclib.Parser
       
   519     FastUnmarshaller = _xmlrpclib.Unmarshaller
       
   520 except (AttributeError, ImportError):
       
   521     FastParser = FastUnmarshaller = None
       
   522 
       
   523 try:
       
   524     import _xmlrpclib
       
   525     FastMarshaller = _xmlrpclib.Marshaller
       
   526 except (AttributeError, ImportError):
       
   527     FastMarshaller = None
       
   528 
       
   529 #
       
   530 # the SGMLOP parser is about 15x faster than Python's builtin
       
   531 # XML parser.  SGMLOP sources can be downloaded from:
       
   532 #
       
   533 #     http://www.pythonware.com/products/xml/sgmlop.htm
       
   534 #
       
   535 
       
   536 try:
       
   537     import sgmlop
       
   538     if not hasattr(sgmlop, "XMLParser"):
       
   539         raise ImportError
       
   540 except ImportError:
       
   541     SgmlopParser = None # sgmlop accelerator not available
       
   542 else:
       
   543     class SgmlopParser:
       
   544         def __init__(self, target):
       
   545 
       
   546             # setup callbacks
       
   547             self.finish_starttag = target.start
       
   548             self.finish_endtag = target.end
       
   549             self.handle_data = target.data
       
   550             self.handle_xml = target.xml
       
   551 
       
   552             # activate parser
       
   553             self.parser = sgmlop.XMLParser()
       
   554             self.parser.register(self)
       
   555             self.feed = self.parser.feed
       
   556             self.entity = {
       
   557                 "amp": "&", "gt": ">", "lt": "<",
       
   558                 "apos": "'", "quot": '"'
       
   559                 }
       
   560 
       
   561         def close(self):
       
   562             try:
       
   563                 self.parser.close()
       
   564             finally:
       
   565                 self.parser = self.feed = None # nuke circular reference
       
   566 
       
   567         def handle_proc(self, tag, attr):
       
   568             m = re.search("encoding\s*=\s*['\"]([^\"']+)[\"']", attr)
       
   569             if m:
       
   570                 self.handle_xml(m.group(1), 1)
       
   571 
       
   572         def handle_entityref(self, entity):
       
   573             # <string> entity
       
   574             try:
       
   575                 self.handle_data(self.entity[entity])
       
   576             except KeyError:
       
   577                 self.handle_data("&%s;" % entity)
       
   578 
       
   579 try:
       
   580     from xml.parsers import expat
       
   581     if not hasattr(expat, "ParserCreate"):
       
   582         raise ImportError
       
   583 except ImportError:
       
   584     ExpatParser = None # expat not available
       
   585 else:
       
   586     class ExpatParser:
       
   587         # fast expat parser for Python 2.0 and later.  this is about
       
   588         # 50% slower than sgmlop, on roundtrip testing
       
   589         def __init__(self, target):
       
   590             self._parser = parser = expat.ParserCreate(None, None)
       
   591             self._target = target
       
   592             parser.StartElementHandler = target.start
       
   593             parser.EndElementHandler = target.end
       
   594             parser.CharacterDataHandler = target.data
       
   595             encoding = None
       
   596             if not parser.returns_unicode:
       
   597                 encoding = "utf-8"
       
   598             target.xml(encoding, None)
       
   599 
       
   600         def feed(self, data):
       
   601             self._parser.Parse(data, 0)
       
   602 
       
   603         def close(self):
       
   604             self._parser.Parse("", 1) # end of data
       
   605             del self._target, self._parser # get rid of circular references
       
   606 
       
   607 class SlowParser:
       
   608     """Default XML parser (based on xmllib.XMLParser)."""
       
   609     # this is about 10 times slower than sgmlop, on roundtrip
       
   610     # testing.
       
   611     def __init__(self, target):
       
   612         import xmllib # lazy subclassing (!)
       
   613         if xmllib.XMLParser not in SlowParser.__bases__:
       
   614             SlowParser.__bases__ = (xmllib.XMLParser,)
       
   615         self.handle_xml = target.xml
       
   616         self.unknown_starttag = target.start
       
   617         self.handle_data = target.data
       
   618         self.handle_cdata = target.data
       
   619         self.unknown_endtag = target.end
       
   620         try:
       
   621             xmllib.XMLParser.__init__(self, accept_utf8=1)
       
   622         except TypeError:
       
   623             xmllib.XMLParser.__init__(self) # pre-2.0
       
   624 
       
   625 # --------------------------------------------------------------------
       
   626 # XML-RPC marshalling and unmarshalling code
       
   627 
       
   628 ##
       
   629 # XML-RPC marshaller.
       
   630 #
       
   631 # @param encoding Default encoding for 8-bit strings.  The default
       
   632 #     value is None (interpreted as UTF-8).
       
   633 # @see dumps
       
   634 
       
   635 class Marshaller:
       
   636     """Generate an XML-RPC params chunk from a Python data structure.
       
   637 
       
   638     Create a Marshaller instance for each set of parameters, and use
       
   639     the "dumps" method to convert your data (represented as a tuple)
       
   640     to an XML-RPC params chunk.  To write a fault response, pass a
       
   641     Fault instance instead.  You may prefer to use the "dumps" module
       
   642     function for this purpose.
       
   643     """
       
   644 
       
   645     # by the way, if you don't understand what's going on in here,
       
   646     # that's perfectly ok.
       
   647 
       
   648     def __init__(self, encoding=None, allow_none=0):
       
   649         self.memo = {}
       
   650         self.data = None
       
   651         self.encoding = encoding
       
   652         self.allow_none = allow_none
       
   653 
       
   654     dispatch = {}
       
   655 
       
   656     def dumps(self, values):
       
   657         out = []
       
   658         write = out.append
       
   659         dump = self.__dump
       
   660         if isinstance(values, Fault):
       
   661             # fault instance
       
   662             write("<fault>\n")
       
   663             dump({'faultCode': values.faultCode,
       
   664                   'faultString': values.faultString},
       
   665                  write)
       
   666             write("</fault>\n")
       
   667         else:
       
   668             # parameter block
       
   669             # FIXME: the xml-rpc specification allows us to leave out
       
   670             # the entire <params> block if there are no parameters.
       
   671             # however, changing this may break older code (including
       
   672             # old versions of xmlrpclib.py), so this is better left as
       
   673             # is for now.  See @XMLRPC3 for more information. /F
       
   674             write("<params>\n")
       
   675             for v in values:
       
   676                 write("<param>\n")
       
   677                 dump(v, write)
       
   678                 write("</param>\n")
       
   679             write("</params>\n")
       
   680         result = string.join(out, "")
       
   681         return result
       
   682 
       
   683     def __dump(self, value, write):
       
   684         try:
       
   685             f = self.dispatch[type(value)]
       
   686         except KeyError:
       
   687             # check if this object can be marshalled as a structure
       
   688             try:
       
   689                 value.__dict__
       
   690             except:
       
   691                 raise TypeError, "cannot marshal %s objects" % type(value)
       
   692             # check if this class is a sub-class of a basic type,
       
   693             # because we don't know how to marshal these types
       
   694             # (e.g. a string sub-class)
       
   695             for type_ in type(value).__mro__:
       
   696                 if type_ in self.dispatch.keys():
       
   697                     raise TypeError, "cannot marshal %s objects" % type(value)
       
   698             f = self.dispatch[InstanceType]
       
   699         f(self, value, write)
       
   700 
       
   701     def dump_nil (self, value, write):
       
   702         if not self.allow_none:
       
   703             raise TypeError, "cannot marshal None unless allow_none is enabled"
       
   704         write("<value><nil/></value>")
       
   705     dispatch[NoneType] = dump_nil
       
   706 
       
   707     def dump_int(self, value, write):
       
   708         # in case ints are > 32 bits
       
   709         if value > MAXINT or value < MININT:
       
   710             raise OverflowError, "int exceeds XML-RPC limits"
       
   711         write("<value><int>")
       
   712         write(str(value))
       
   713         write("</int></value>\n")
       
   714     dispatch[IntType] = dump_int
       
   715 
       
   716     if _bool_is_builtin:
       
   717         def dump_bool(self, value, write):
       
   718             write("<value><boolean>")
       
   719             write(value and "1" or "0")
       
   720             write("</boolean></value>\n")
       
   721         dispatch[bool] = dump_bool
       
   722 
       
   723     def dump_long(self, value, write):
       
   724         if value > MAXINT or value < MININT:
       
   725             raise OverflowError, "long int exceeds XML-RPC limits"
       
   726         write("<value><int>")
       
   727         write(str(int(value)))
       
   728         write("</int></value>\n")
       
   729     dispatch[LongType] = dump_long
       
   730 
       
   731     def dump_double(self, value, write):
       
   732         write("<value><double>")
       
   733         write(repr(value))
       
   734         write("</double></value>\n")
       
   735     dispatch[FloatType] = dump_double
       
   736 
       
   737     def dump_string(self, value, write, escape=escape):
       
   738         write("<value><string>")
       
   739         write(escape(value))
       
   740         write("</string></value>\n")
       
   741     dispatch[StringType] = dump_string
       
   742 
       
   743     if unicode:
       
   744         def dump_unicode(self, value, write, escape=escape):
       
   745             value = value.encode(self.encoding)
       
   746             write("<value><string>")
       
   747             write(escape(value))
       
   748             write("</string></value>\n")
       
   749         dispatch[UnicodeType] = dump_unicode
       
   750 
       
   751     def dump_array(self, value, write):
       
   752         i = id(value)
       
   753         if i in self.memo:
       
   754             raise TypeError, "cannot marshal recursive sequences"
       
   755         self.memo[i] = None
       
   756         dump = self.__dump
       
   757         write("<value><array><data>\n")
       
   758         for v in value:
       
   759             dump(v, write)
       
   760         write("</data></array></value>\n")
       
   761         del self.memo[i]
       
   762     dispatch[TupleType] = dump_array
       
   763     dispatch[ListType] = dump_array
       
   764 
       
   765     def dump_struct(self, value, write, escape=escape):
       
   766         i = id(value)
       
   767         if i in self.memo:
       
   768             raise TypeError, "cannot marshal recursive dictionaries"
       
   769         self.memo[i] = None
       
   770         dump = self.__dump
       
   771         write("<value><struct>\n")
       
   772         for k, v in value.items():
       
   773             write("<member>\n")
       
   774             if type(k) is not StringType:
       
   775                 if unicode and type(k) is UnicodeType:
       
   776                     k = k.encode(self.encoding)
       
   777                 else:
       
   778                     raise TypeError, "dictionary key must be string"
       
   779             write("<name>%s</name>\n" % escape(k))
       
   780             dump(v, write)
       
   781             write("</member>\n")
       
   782         write("</struct></value>\n")
       
   783         del self.memo[i]
       
   784     dispatch[DictType] = dump_struct
       
   785 
       
   786     if datetime:
       
   787         def dump_datetime(self, value, write):
       
   788             write("<value><dateTime.iso8601>")
       
   789             write(_strftime(value))
       
   790             write("</dateTime.iso8601></value>\n")
       
   791         dispatch[datetime.datetime] = dump_datetime
       
   792 
       
   793     def dump_instance(self, value, write):
       
   794         # check for special wrappers
       
   795         if value.__class__ in WRAPPERS:
       
   796             self.write = write
       
   797             value.encode(self)
       
   798             del self.write
       
   799         else:
       
   800             # store instance attributes as a struct (really?)
       
   801             self.dump_struct(value.__dict__, write)
       
   802     dispatch[InstanceType] = dump_instance
       
   803 
       
   804 ##
       
   805 # XML-RPC unmarshaller.
       
   806 #
       
   807 # @see loads
       
   808 
       
   809 class Unmarshaller:
       
   810     """Unmarshal an XML-RPC response, based on incoming XML event
       
   811     messages (start, data, end).  Call close() to get the resulting
       
   812     data structure.
       
   813 
       
   814     Note that this reader is fairly tolerant, and gladly accepts bogus
       
   815     XML-RPC data without complaining (but not bogus XML).
       
   816     """
       
   817 
       
   818     # and again, if you don't understand what's going on in here,
       
   819     # that's perfectly ok.
       
   820 
       
   821     def __init__(self, use_datetime=0):
       
   822         self._type = None
       
   823         self._stack = []
       
   824         self._marks = []
       
   825         self._data = []
       
   826         self._methodname = None
       
   827         self._encoding = "utf-8"
       
   828         self.append = self._stack.append
       
   829         self._use_datetime = use_datetime
       
   830         if use_datetime and not datetime:
       
   831             raise ValueError, "the datetime module is not available"
       
   832 
       
   833     def close(self):
       
   834         # return response tuple and target method
       
   835         if self._type is None or self._marks:
       
   836             raise ResponseError()
       
   837         if self._type == "fault":
       
   838             raise Fault(**self._stack[0])
       
   839         return tuple(self._stack)
       
   840 
       
   841     def getmethodname(self):
       
   842         return self._methodname
       
   843 
       
   844     #
       
   845     # event handlers
       
   846 
       
   847     def xml(self, encoding, standalone):
       
   848         self._encoding = encoding
       
   849         # FIXME: assert standalone == 1 ???
       
   850 
       
   851     def start(self, tag, attrs):
       
   852         # prepare to handle this element
       
   853         if tag == "array" or tag == "struct":
       
   854             self._marks.append(len(self._stack))
       
   855         self._data = []
       
   856         self._value = (tag == "value")
       
   857 
       
   858     def data(self, text):
       
   859         self._data.append(text)
       
   860 
       
   861     def end(self, tag, join=string.join):
       
   862         # call the appropriate end tag handler
       
   863         try:
       
   864             f = self.dispatch[tag]
       
   865         except KeyError:
       
   866             pass # unknown tag ?
       
   867         else:
       
   868             return f(self, join(self._data, ""))
       
   869 
       
   870     #
       
   871     # accelerator support
       
   872 
       
   873     def end_dispatch(self, tag, data):
       
   874         # dispatch data
       
   875         try:
       
   876             f = self.dispatch[tag]
       
   877         except KeyError:
       
   878             pass # unknown tag ?
       
   879         else:
       
   880             return f(self, data)
       
   881 
       
   882     #
       
   883     # element decoders
       
   884 
       
   885     dispatch = {}
       
   886 
       
   887     def end_nil (self, data):
       
   888         self.append(None)
       
   889         self._value = 0
       
   890     dispatch["nil"] = end_nil
       
   891 
       
   892     def end_boolean(self, data):
       
   893         if data == "0":
       
   894             self.append(False)
       
   895         elif data == "1":
       
   896             self.append(True)
       
   897         else:
       
   898             raise TypeError, "bad boolean value"
       
   899         self._value = 0
       
   900     dispatch["boolean"] = end_boolean
       
   901 
       
   902     def end_int(self, data):
       
   903         self.append(int(data))
       
   904         self._value = 0
       
   905     dispatch["i4"] = end_int
       
   906     dispatch["i8"] = end_int
       
   907     dispatch["int"] = end_int
       
   908 
       
   909     def end_double(self, data):
       
   910         self.append(float(data))
       
   911         self._value = 0
       
   912     dispatch["double"] = end_double
       
   913 
       
   914     def end_string(self, data):
       
   915         if self._encoding:
       
   916             data = _decode(data, self._encoding)
       
   917         self.append(_stringify(data))
       
   918         self._value = 0
       
   919     dispatch["string"] = end_string
       
   920     dispatch["name"] = end_string # struct keys are always strings
       
   921 
       
   922     def end_array(self, data):
       
   923         mark = self._marks.pop()
       
   924         # map arrays to Python lists
       
   925         self._stack[mark:] = [self._stack[mark:]]
       
   926         self._value = 0
       
   927     dispatch["array"] = end_array
       
   928 
       
   929     def end_struct(self, data):
       
   930         mark = self._marks.pop()
       
   931         # map structs to Python dictionaries
       
   932         dict = {}
       
   933         items = self._stack[mark:]
       
   934         for i in range(0, len(items), 2):
       
   935             dict[_stringify(items[i])] = items[i+1]
       
   936         self._stack[mark:] = [dict]
       
   937         self._value = 0
       
   938     dispatch["struct"] = end_struct
       
   939 
       
   940     def end_base64(self, data):
       
   941         value = Binary()
       
   942         value.decode(data)
       
   943         self.append(value)
       
   944         self._value = 0
       
   945     dispatch["base64"] = end_base64
       
   946 
       
   947     def end_dateTime(self, data):
       
   948         value = DateTime()
       
   949         value.decode(data)
       
   950         if self._use_datetime:
       
   951             value = _datetime_type(data)
       
   952         self.append(value)
       
   953     dispatch["dateTime.iso8601"] = end_dateTime
       
   954 
       
   955     def end_value(self, data):
       
   956         # if we stumble upon a value element with no internal
       
   957         # elements, treat it as a string element
       
   958         if self._value:
       
   959             self.end_string(data)
       
   960     dispatch["value"] = end_value
       
   961 
       
   962     def end_params(self, data):
       
   963         self._type = "params"
       
   964     dispatch["params"] = end_params
       
   965 
       
   966     def end_fault(self, data):
       
   967         self._type = "fault"
       
   968     dispatch["fault"] = end_fault
       
   969 
       
   970     def end_methodName(self, data):
       
   971         if self._encoding:
       
   972             data = _decode(data, self._encoding)
       
   973         self._methodname = data
       
   974         self._type = "methodName" # no params
       
   975     dispatch["methodName"] = end_methodName
       
   976 
       
   977 ## Multicall support
       
   978 #
       
   979 
       
   980 class _MultiCallMethod:
       
   981     # some lesser magic to store calls made to a MultiCall object
       
   982     # for batch execution
       
   983     def __init__(self, call_list, name):
       
   984         self.__call_list = call_list
       
   985         self.__name = name
       
   986     def __getattr__(self, name):
       
   987         return _MultiCallMethod(self.__call_list, "%s.%s" % (self.__name, name))
       
   988     def __call__(self, *args):
       
   989         self.__call_list.append((self.__name, args))
       
   990 
       
   991 class MultiCallIterator:
       
   992     """Iterates over the results of a multicall. Exceptions are
       
   993     thrown in response to xmlrpc faults."""
       
   994 
       
   995     def __init__(self, results):
       
   996         self.results = results
       
   997 
       
   998     def __getitem__(self, i):
       
   999         item = self.results[i]
       
  1000         if type(item) == type({}):
       
  1001             raise Fault(item['faultCode'], item['faultString'])
       
  1002         elif type(item) == type([]):
       
  1003             return item[0]
       
  1004         else:
       
  1005             raise ValueError,\
       
  1006                   "unexpected type in multicall result"
       
  1007 
       
  1008 class MultiCall:
       
  1009     """server -> a object used to boxcar method calls
       
  1010 
       
  1011     server should be a ServerProxy object.
       
  1012 
       
  1013     Methods can be added to the MultiCall using normal
       
  1014     method call syntax e.g.:
       
  1015 
       
  1016     multicall = MultiCall(server_proxy)
       
  1017     multicall.add(2,3)
       
  1018     multicall.get_address("Guido")
       
  1019 
       
  1020     To execute the multicall, call the MultiCall object e.g.:
       
  1021 
       
  1022     add_result, address = multicall()
       
  1023     """
       
  1024 
       
  1025     def __init__(self, server):
       
  1026         self.__server = server
       
  1027         self.__call_list = []
       
  1028 
       
  1029     def __repr__(self):
       
  1030         return "<MultiCall at %x>" % id(self)
       
  1031 
       
  1032     __str__ = __repr__
       
  1033 
       
  1034     def __getattr__(self, name):
       
  1035         return _MultiCallMethod(self.__call_list, name)
       
  1036 
       
  1037     def __call__(self):
       
  1038         marshalled_list = []
       
  1039         for name, args in self.__call_list:
       
  1040             marshalled_list.append({'methodName' : name, 'params' : args})
       
  1041 
       
  1042         return MultiCallIterator(self.__server.system.multicall(marshalled_list))
       
  1043 
       
  1044 # --------------------------------------------------------------------
       
  1045 # convenience functions
       
  1046 
       
  1047 ##
       
  1048 # Create a parser object, and connect it to an unmarshalling instance.
       
  1049 # This function picks the fastest available XML parser.
       
  1050 #
       
  1051 # return A (parser, unmarshaller) tuple.
       
  1052 
       
  1053 def getparser(use_datetime=0):
       
  1054     """getparser() -> parser, unmarshaller
       
  1055 
       
  1056     Create an instance of the fastest available parser, and attach it
       
  1057     to an unmarshalling object.  Return both objects.
       
  1058     """
       
  1059     if use_datetime and not datetime:
       
  1060         raise ValueError, "the datetime module is not available"
       
  1061     if FastParser and FastUnmarshaller:
       
  1062         if use_datetime:
       
  1063             mkdatetime = _datetime_type
       
  1064         else:
       
  1065             mkdatetime = _datetime
       
  1066         target = FastUnmarshaller(True, False, _binary, mkdatetime, Fault)
       
  1067         parser = FastParser(target)
       
  1068     else:
       
  1069         target = Unmarshaller(use_datetime=use_datetime)
       
  1070         if FastParser:
       
  1071             parser = FastParser(target)
       
  1072         elif SgmlopParser:
       
  1073             parser = SgmlopParser(target)
       
  1074         elif ExpatParser:
       
  1075             parser = ExpatParser(target)
       
  1076         else:
       
  1077             parser = SlowParser(target)
       
  1078     return parser, target
       
  1079 
       
  1080 ##
       
  1081 # Convert a Python tuple or a Fault instance to an XML-RPC packet.
       
  1082 #
       
  1083 # @def dumps(params, **options)
       
  1084 # @param params A tuple or Fault instance.
       
  1085 # @keyparam methodname If given, create a methodCall request for
       
  1086 #     this method name.
       
  1087 # @keyparam methodresponse If given, create a methodResponse packet.
       
  1088 #     If used with a tuple, the tuple must be a singleton (that is,
       
  1089 #     it must contain exactly one element).
       
  1090 # @keyparam encoding The packet encoding.
       
  1091 # @return A string containing marshalled data.
       
  1092 
       
  1093 def dumps(params, methodname=None, methodresponse=None, encoding=None,
       
  1094           allow_none=0):
       
  1095     """data [,options] -> marshalled data
       
  1096 
       
  1097     Convert an argument tuple or a Fault instance to an XML-RPC
       
  1098     request (or response, if the methodresponse option is used).
       
  1099 
       
  1100     In addition to the data object, the following options can be given
       
  1101     as keyword arguments:
       
  1102 
       
  1103         methodname: the method name for a methodCall packet
       
  1104 
       
  1105         methodresponse: true to create a methodResponse packet.
       
  1106         If this option is used with a tuple, the tuple must be
       
  1107         a singleton (i.e. it can contain only one element).
       
  1108 
       
  1109         encoding: the packet encoding (default is UTF-8)
       
  1110 
       
  1111     All 8-bit strings in the data structure are assumed to use the
       
  1112     packet encoding.  Unicode strings are automatically converted,
       
  1113     where necessary.
       
  1114     """
       
  1115 
       
  1116     assert isinstance(params, TupleType) or isinstance(params, Fault),\
       
  1117            "argument must be tuple or Fault instance"
       
  1118 
       
  1119     if isinstance(params, Fault):
       
  1120         methodresponse = 1
       
  1121     elif methodresponse and isinstance(params, TupleType):
       
  1122         assert len(params) == 1, "response tuple must be a singleton"
       
  1123 
       
  1124     if not encoding:
       
  1125         encoding = "utf-8"
       
  1126 
       
  1127     if FastMarshaller:
       
  1128         m = FastMarshaller(encoding)
       
  1129     else:
       
  1130         m = Marshaller(encoding, allow_none)
       
  1131 
       
  1132     data = m.dumps(params)
       
  1133 
       
  1134     if encoding != "utf-8":
       
  1135         xmlheader = "<?xml version='1.0' encoding='%s'?>\n" % str(encoding)
       
  1136     else:
       
  1137         xmlheader = "<?xml version='1.0'?>\n" # utf-8 is default
       
  1138 
       
  1139     # standard XML-RPC wrappings
       
  1140     if methodname:
       
  1141         # a method call
       
  1142         if not isinstance(methodname, StringType):
       
  1143             methodname = methodname.encode(encoding)
       
  1144         data = (
       
  1145             xmlheader,
       
  1146             "<methodCall>\n"
       
  1147             "<methodName>", methodname, "</methodName>\n",
       
  1148             data,
       
  1149             "</methodCall>\n"
       
  1150             )
       
  1151     elif methodresponse:
       
  1152         # a method response, or a fault structure
       
  1153         data = (
       
  1154             xmlheader,
       
  1155             "<methodResponse>\n",
       
  1156             data,
       
  1157             "</methodResponse>\n"
       
  1158             )
       
  1159     else:
       
  1160         return data # return as is
       
  1161     return string.join(data, "")
       
  1162 
       
  1163 ##
       
  1164 # Convert an XML-RPC packet to a Python object.  If the XML-RPC packet
       
  1165 # represents a fault condition, this function raises a Fault exception.
       
  1166 #
       
  1167 # @param data An XML-RPC packet, given as an 8-bit string.
       
  1168 # @return A tuple containing the unpacked data, and the method name
       
  1169 #     (None if not present).
       
  1170 # @see Fault
       
  1171 
       
  1172 def loads(data, use_datetime=0):
       
  1173     """data -> unmarshalled data, method name
       
  1174 
       
  1175     Convert an XML-RPC packet to unmarshalled data plus a method
       
  1176     name (None if not present).
       
  1177 
       
  1178     If the XML-RPC packet represents a fault condition, this function
       
  1179     raises a Fault exception.
       
  1180     """
       
  1181     p, u = getparser(use_datetime=use_datetime)
       
  1182     p.feed(data)
       
  1183     p.close()
       
  1184     return u.close(), u.getmethodname()
       
  1185 
       
  1186 
       
  1187 # --------------------------------------------------------------------
       
  1188 # request dispatcher
       
  1189 
       
  1190 class _Method:
       
  1191     # some magic to bind an XML-RPC method to an RPC server.
       
  1192     # supports "nested" methods (e.g. examples.getStateName)
       
  1193     def __init__(self, send, name):
       
  1194         self.__send = send
       
  1195         self.__name = name
       
  1196     def __getattr__(self, name):
       
  1197         return _Method(self.__send, "%s.%s" % (self.__name, name))
       
  1198     def __call__(self, *args):
       
  1199         return self.__send(self.__name, args)
       
  1200 
       
  1201 ##
       
  1202 # Standard transport class for XML-RPC over HTTP.
       
  1203 # <p>
       
  1204 # You can create custom transports by subclassing this method, and
       
  1205 # overriding selected methods.
       
  1206 
       
  1207 class Transport:
       
  1208     """Handles an HTTP transaction to an XML-RPC server."""
       
  1209 
       
  1210     # client identifier (may be overridden)
       
  1211     user_agent = "xmlrpclib.py/%s (by www.pythonware.com)" % __version__
       
  1212 
       
  1213     def __init__(self, use_datetime=0):
       
  1214         self._use_datetime = use_datetime
       
  1215 
       
  1216     ##
       
  1217     # Send a complete request, and parse the response.
       
  1218     #
       
  1219     # @param host Target host.
       
  1220     # @param handler Target PRC handler.
       
  1221     # @param request_body XML-RPC request body.
       
  1222     # @param verbose Debugging flag.
       
  1223     # @return Parsed response.
       
  1224 
       
  1225     def request(self, host, handler, request_body, verbose=0):
       
  1226         # issue XML-RPC request
       
  1227 
       
  1228         h = self.make_connection(host)
       
  1229         if verbose:
       
  1230             h.set_debuglevel(1)
       
  1231 
       
  1232         self.send_request(h, handler, request_body)
       
  1233         self.send_host(h, host)
       
  1234         self.send_user_agent(h)
       
  1235         self.send_content(h, request_body)
       
  1236 
       
  1237         errcode, errmsg, headers = h.getreply()
       
  1238 
       
  1239         if errcode != 200:
       
  1240             raise ProtocolError(
       
  1241                 host + handler,
       
  1242                 errcode, errmsg,
       
  1243                 headers
       
  1244                 )
       
  1245 
       
  1246         self.verbose = verbose
       
  1247 
       
  1248         try:
       
  1249             sock = h._conn.sock
       
  1250         except AttributeError:
       
  1251             sock = None
       
  1252 
       
  1253         return self._parse_response(h.getfile(), sock)
       
  1254 
       
  1255     ##
       
  1256     # Create parser.
       
  1257     #
       
  1258     # @return A 2-tuple containing a parser and a unmarshaller.
       
  1259 
       
  1260     def getparser(self):
       
  1261         # get parser and unmarshaller
       
  1262         return getparser(use_datetime=self._use_datetime)
       
  1263 
       
  1264     ##
       
  1265     # Get authorization info from host parameter
       
  1266     # Host may be a string, or a (host, x509-dict) tuple; if a string,
       
  1267     # it is checked for a "user:pw@host" format, and a "Basic
       
  1268     # Authentication" header is added if appropriate.
       
  1269     #
       
  1270     # @param host Host descriptor (URL or (URL, x509 info) tuple).
       
  1271     # @return A 3-tuple containing (actual host, extra headers,
       
  1272     #     x509 info).  The header and x509 fields may be None.
       
  1273 
       
  1274     def get_host_info(self, host):
       
  1275 
       
  1276         x509 = {}
       
  1277         if isinstance(host, TupleType):
       
  1278             host, x509 = host
       
  1279 
       
  1280         import urllib
       
  1281         auth, host = urllib.splituser(host)
       
  1282 
       
  1283         if auth:
       
  1284             import base64
       
  1285             auth = base64.encodestring(urllib.unquote(auth))
       
  1286             auth = string.join(string.split(auth), "") # get rid of whitespace
       
  1287             extra_headers = [
       
  1288                 ("Authorization", "Basic " + auth)
       
  1289                 ]
       
  1290         else:
       
  1291             extra_headers = None
       
  1292 
       
  1293         return host, extra_headers, x509
       
  1294 
       
  1295     ##
       
  1296     # Connect to server.
       
  1297     #
       
  1298     # @param host Target host.
       
  1299     # @return A connection handle.
       
  1300 
       
  1301     def make_connection(self, host):
       
  1302         # create a HTTP connection object from a host descriptor
       
  1303         import httplib
       
  1304         host, extra_headers, x509 = self.get_host_info(host)
       
  1305         return httplib.HTTP(host)
       
  1306 
       
  1307     ##
       
  1308     # Send request header.
       
  1309     #
       
  1310     # @param connection Connection handle.
       
  1311     # @param handler Target RPC handler.
       
  1312     # @param request_body XML-RPC body.
       
  1313 
       
  1314     def send_request(self, connection, handler, request_body):
       
  1315         connection.putrequest("POST", handler)
       
  1316 
       
  1317     ##
       
  1318     # Send host name.
       
  1319     #
       
  1320     # @param connection Connection handle.
       
  1321     # @param host Host name.
       
  1322 
       
  1323     def send_host(self, connection, host):
       
  1324         host, extra_headers, x509 = self.get_host_info(host)
       
  1325         connection.putheader("Host", host)
       
  1326         if extra_headers:
       
  1327             if isinstance(extra_headers, DictType):
       
  1328                 extra_headers = extra_headers.items()
       
  1329             for key, value in extra_headers:
       
  1330                 connection.putheader(key, value)
       
  1331 
       
  1332     ##
       
  1333     # Send user-agent identifier.
       
  1334     #
       
  1335     # @param connection Connection handle.
       
  1336 
       
  1337     def send_user_agent(self, connection):
       
  1338         connection.putheader("User-Agent", self.user_agent)
       
  1339 
       
  1340     ##
       
  1341     # Send request body.
       
  1342     #
       
  1343     # @param connection Connection handle.
       
  1344     # @param request_body XML-RPC request body.
       
  1345 
       
  1346     def send_content(self, connection, request_body):
       
  1347         connection.putheader("Content-Type", "text/xml")
       
  1348         connection.putheader("Content-Length", str(len(request_body)))
       
  1349         connection.endheaders()
       
  1350         if request_body:
       
  1351             connection.send(request_body)
       
  1352 
       
  1353     ##
       
  1354     # Parse response.
       
  1355     #
       
  1356     # @param file Stream.
       
  1357     # @return Response tuple and target method.
       
  1358 
       
  1359     def parse_response(self, file):
       
  1360         # compatibility interface
       
  1361         return self._parse_response(file, None)
       
  1362 
       
  1363     ##
       
  1364     # Parse response (alternate interface).  This is similar to the
       
  1365     # parse_response method, but also provides direct access to the
       
  1366     # underlying socket object (where available).
       
  1367     #
       
  1368     # @param file Stream.
       
  1369     # @param sock Socket handle (or None, if the socket object
       
  1370     #    could not be accessed).
       
  1371     # @return Response tuple and target method.
       
  1372 
       
  1373     def _parse_response(self, file, sock):
       
  1374         # read response from input file/socket, and parse it
       
  1375 
       
  1376         p, u = self.getparser()
       
  1377 
       
  1378         while 1:
       
  1379             if sock:
       
  1380                 response = sock.recv(1024)
       
  1381             else:
       
  1382                 response = file.read(1024)
       
  1383             if not response:
       
  1384                 break
       
  1385             if self.verbose:
       
  1386                 print "body:", repr(response)
       
  1387             p.feed(response)
       
  1388 
       
  1389         file.close()
       
  1390         p.close()
       
  1391 
       
  1392         return u.close()
       
  1393 
       
  1394 ##
       
  1395 # Standard transport class for XML-RPC over HTTPS.
       
  1396 
       
  1397 class SafeTransport(Transport):
       
  1398     """Handles an HTTPS transaction to an XML-RPC server."""
       
  1399 
       
  1400     # FIXME: mostly untested
       
  1401 
       
  1402     def make_connection(self, host):
       
  1403         # create a HTTPS connection object from a host descriptor
       
  1404         # host may be a string, or a (host, x509-dict) tuple
       
  1405         import httplib
       
  1406         host, extra_headers, x509 = self.get_host_info(host)
       
  1407         try:
       
  1408             HTTPS = httplib.HTTPS
       
  1409         except AttributeError:
       
  1410             raise NotImplementedError(
       
  1411                 "your version of httplib doesn't support HTTPS"
       
  1412                 )
       
  1413         else:
       
  1414             return HTTPS(host, None, **(x509 or {}))
       
  1415 
       
  1416 ##
       
  1417 # Standard server proxy.  This class establishes a virtual connection
       
  1418 # to an XML-RPC server.
       
  1419 # <p>
       
  1420 # This class is available as ServerProxy and Server.  New code should
       
  1421 # use ServerProxy, to avoid confusion.
       
  1422 #
       
  1423 # @def ServerProxy(uri, **options)
       
  1424 # @param uri The connection point on the server.
       
  1425 # @keyparam transport A transport factory, compatible with the
       
  1426 #    standard transport class.
       
  1427 # @keyparam encoding The default encoding used for 8-bit strings
       
  1428 #    (default is UTF-8).
       
  1429 # @keyparam verbose Use a true value to enable debugging output.
       
  1430 #    (printed to standard output).
       
  1431 # @see Transport
       
  1432 
       
  1433 class ServerProxy:
       
  1434     """uri [,options] -> a logical connection to an XML-RPC server
       
  1435 
       
  1436     uri is the connection point on the server, given as
       
  1437     scheme://host/target.
       
  1438 
       
  1439     The standard implementation always supports the "http" scheme.  If
       
  1440     SSL socket support is available (Python 2.0), it also supports
       
  1441     "https".
       
  1442 
       
  1443     If the target part and the slash preceding it are both omitted,
       
  1444     "/RPC2" is assumed.
       
  1445 
       
  1446     The following options can be given as keyword arguments:
       
  1447 
       
  1448         transport: a transport factory
       
  1449         encoding: the request encoding (default is UTF-8)
       
  1450 
       
  1451     All 8-bit strings passed to the server proxy are assumed to use
       
  1452     the given encoding.
       
  1453     """
       
  1454 
       
  1455     def __init__(self, uri, transport=None, encoding=None, verbose=0,
       
  1456                  allow_none=0, use_datetime=0):
       
  1457         # establish a "logical" server connection
       
  1458 
       
  1459         # get the url
       
  1460         import urllib
       
  1461         type, uri = urllib.splittype(uri)
       
  1462         if type not in ("http", "https"):
       
  1463             raise IOError, "unsupported XML-RPC protocol"
       
  1464         self.__host, self.__handler = urllib.splithost(uri)
       
  1465         if not self.__handler:
       
  1466             self.__handler = "/RPC2"
       
  1467 
       
  1468         if transport is None:
       
  1469             if type == "https":
       
  1470                 transport = SafeTransport(use_datetime=use_datetime)
       
  1471             else:
       
  1472                 transport = Transport(use_datetime=use_datetime)
       
  1473         self.__transport = transport
       
  1474 
       
  1475         self.__encoding = encoding
       
  1476         self.__verbose = verbose
       
  1477         self.__allow_none = allow_none
       
  1478 
       
  1479     def __request(self, methodname, params):
       
  1480         # call a method on the remote server
       
  1481 
       
  1482         request = dumps(params, methodname, encoding=self.__encoding,
       
  1483                         allow_none=self.__allow_none)
       
  1484 
       
  1485         response = self.__transport.request(
       
  1486             self.__host,
       
  1487             self.__handler,
       
  1488             request,
       
  1489             verbose=self.__verbose
       
  1490             )
       
  1491 
       
  1492         if len(response) == 1:
       
  1493             response = response[0]
       
  1494 
       
  1495         return response
       
  1496 
       
  1497     def __repr__(self):
       
  1498         return (
       
  1499             "<ServerProxy for %s%s>" %
       
  1500             (self.__host, self.__handler)
       
  1501             )
       
  1502 
       
  1503     __str__ = __repr__
       
  1504 
       
  1505     def __getattr__(self, name):
       
  1506         # magic method dispatcher
       
  1507         return _Method(self.__request, name)
       
  1508 
       
  1509     # note: to call a remote object with an non-standard name, use
       
  1510     # result getattr(server, "strange-python-name")(args)
       
  1511 
       
  1512 # compatibility
       
  1513 
       
  1514 Server = ServerProxy
       
  1515 
       
  1516 # --------------------------------------------------------------------
       
  1517 # test code
       
  1518 
       
  1519 if __name__ == "__main__":
       
  1520 
       
  1521     # simple test program (from the XML-RPC specification)
       
  1522 
       
  1523     # server = ServerProxy("http://localhost:8000") # local server
       
  1524     server = ServerProxy("http://time.xmlrpc.com/RPC2")
       
  1525 
       
  1526     print server
       
  1527 
       
  1528     try:
       
  1529         print server.currentTime.getCurrentTime()
       
  1530     except Error, v:
       
  1531         print "ERROR", v
       
  1532 
       
  1533     multi = MultiCall(server)
       
  1534     multi.currentTime.getCurrentTime()
       
  1535     multi.currentTime.getCurrentTime()
       
  1536     try:
       
  1537         for response in multi():
       
  1538             print response
       
  1539     except Error, v:
       
  1540         print "ERROR", v