PyXR

c:\python24\lib \ xmlrpclib.py



0001 #
0002 # XML-RPC CLIENT LIBRARY
0003 # $Id: xmlrpclib.py,v 1.36 2004/10/13 06:48:37 effbot Exp $
0004 #
0005 # an XML-RPC client interface for Python.
0006 #
0007 # the marshalling and response parser code can also be used to
0008 # implement XML-RPC servers.
0009 #
0010 # Notes:
0011 # this version is designed to work with Python 2.1 or newer.
0012 #
0013 # History:
0014 # 1999-01-14 fl  Created
0015 # 1999-01-15 fl  Changed dateTime to use localtime
0016 # 1999-01-16 fl  Added Binary/base64 element, default to RPC2 service
0017 # 1999-01-19 fl  Fixed array data element (from Skip Montanaro)
0018 # 1999-01-21 fl  Fixed dateTime constructor, etc.
0019 # 1999-02-02 fl  Added fault handling, handle empty sequences, etc.
0020 # 1999-02-10 fl  Fixed problem with empty responses (from Skip Montanaro)
0021 # 1999-06-20 fl  Speed improvements, pluggable parsers/transports (0.9.8)
0022 # 2000-11-28 fl  Changed boolean to check the truth value of its argument
0023 # 2001-02-24 fl  Added encoding/Unicode/SafeTransport patches
0024 # 2001-02-26 fl  Added compare support to wrappers (0.9.9/1.0b1)
0025 # 2001-03-28 fl  Make sure response tuple is a singleton
0026 # 2001-03-29 fl  Don't require empty params element (from Nicholas Riley)
0027 # 2001-06-10 fl  Folded in _xmlrpclib accelerator support (1.0b2)
0028 # 2001-08-20 fl  Base xmlrpclib.Error on built-in Exception (from Paul Prescod)
0029 # 2001-09-03 fl  Allow Transport subclass to override getparser
0030 # 2001-09-10 fl  Lazy import of urllib, cgi, xmllib (20x import speedup)
0031 # 2001-10-01 fl  Remove containers from memo cache when done with them
0032 # 2001-10-01 fl  Use faster escape method (80% dumps speedup)
0033 # 2001-10-02 fl  More dumps microtuning
0034 # 2001-10-04 fl  Make sure import expat gets a parser (from Guido van Rossum)
0035 # 2001-10-10 sm  Allow long ints to be passed as ints if they don't overflow
0036 # 2001-10-17 sm  Test for int and long overflow (allows use on 64-bit systems)
0037 # 2001-11-12 fl  Use repr() to marshal doubles (from Paul Felix)
0038 # 2002-03-17 fl  Avoid buffered read when possible (from James Rucker)
0039 # 2002-04-07 fl  Added pythondoc comments
0040 # 2002-04-16 fl  Added __str__ methods to datetime/binary wrappers
0041 # 2002-05-15 fl  Added error constants (from Andrew Kuchling)
0042 # 2002-06-27 fl  Merged with Python CVS version
0043 # 2002-10-22 fl  Added basic authentication (based on code from Phillip Eby)
0044 # 2003-01-22 sm  Add support for the bool type
0045 # 2003-02-27 gvr Remove apply calls
0046 # 2003-04-24 sm  Use cStringIO if available
0047 # 2003-04-25 ak  Add support for nil
0048 # 2003-06-15 gn  Add support for time.struct_time
0049 # 2003-07-12 gp  Correct marshalling of Faults
0050 # 2003-10-31 mvl Add multicall support
0051 # 2004-08-20 mvl Bump minimum supported Python version to 2.1
0052 #
0053 # Copyright (c) 1999-2002 by Secret Labs AB.
0054 # Copyright (c) 1999-2002 by Fredrik Lundh.
0055 #
0056 # info@pythonware.com
0057 # http://www.pythonware.com
0058 #
0059 # --------------------------------------------------------------------
0060 # The XML-RPC client interface is
0061 #
0062 # Copyright (c) 1999-2002 by Secret Labs AB
0063 # Copyright (c) 1999-2002 by Fredrik Lundh
0064 #
0065 # By obtaining, using, and/or copying this software and/or its
0066 # associated documentation, you agree that you have read, understood,
0067 # and will comply with the following terms and conditions:
0068 #
0069 # Permission to use, copy, modify, and distribute this software and
0070 # its associated documentation for any purpose and without fee is
0071 # hereby granted, provided that the above copyright notice appears in
0072 # all copies, and that both that copyright notice and this permission
0073 # notice appear in supporting documentation, and that the name of
0074 # Secret Labs AB or the author not be used in advertising or publicity
0075 # pertaining to distribution of the software without specific, written
0076 # prior permission.
0077 #
0078 # SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
0079 # TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
0080 # ABILITY AND FITNESS.  IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR
0081 # BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
0082 # DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
0083 # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
0084 # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
0085 # OF THIS SOFTWARE.
0086 # --------------------------------------------------------------------
0087 
0088 #
0089 # things to look into some day:
0090 
0091 # TODO: sort out True/False/boolean issues for Python 2.3
0092 
0093 """
0094 An XML-RPC client interface for Python.
0095 
0096 The marshalling and response parser code can also be used to
0097 implement XML-RPC servers.
0098 
0099 Exported exceptions:
0100 
0101   Error          Base class for client errors
0102   ProtocolError  Indicates an HTTP protocol error
0103   ResponseError  Indicates a broken response package
0104   Fault          Indicates an XML-RPC fault package
0105 
0106 Exported classes:
0107 
0108   ServerProxy    Represents a logical connection to an XML-RPC server
0109 
0110   MultiCall      Executor of boxcared xmlrpc requests
0111   Boolean        boolean wrapper to generate a "boolean" XML-RPC value
0112   DateTime       dateTime wrapper for an ISO 8601 string or time tuple or
0113                  localtime integer value to generate a "dateTime.iso8601"
0114                  XML-RPC value
0115   Binary         binary data wrapper
0116 
0117   SlowParser     Slow but safe standard parser (based on xmllib)
0118   Marshaller     Generate an XML-RPC params chunk from a Python data structure
0119   Unmarshaller   Unmarshal an XML-RPC response from incoming XML event message
0120   Transport      Handles an HTTP transaction to an XML-RPC server
0121   SafeTransport  Handles an HTTPS transaction to an XML-RPC server
0122 
0123 Exported constants:
0124 
0125   True
0126   False
0127 
0128 Exported functions:
0129 
0130   boolean        Convert any Python value to an XML-RPC boolean
0131   getparser      Create instance of the fastest available parser & attach
0132                  to an unmarshalling object
0133   dumps          Convert an argument tuple or a Fault instance to an XML-RPC
0134                  request (or response, if the methodresponse option is used).
0135   loads          Convert an XML-RPC packet to unmarshalled data plus a method
0136                  name (None if not present).
0137 """
0138 
0139 import re, string, time, operator
0140 
0141 from types import *
0142 
0143 # --------------------------------------------------------------------
0144 # Internal stuff
0145 
0146 try:
0147     unicode
0148 except NameError:
0149     unicode = None # unicode support not available
0150 
0151 try:
0152     _bool_is_builtin = False.__class__.__name__ == "bool"
0153 except NameError:
0154     _bool_is_builtin = 0
0155 
0156 def _decode(data, encoding, is8bit=re.compile("[\x80-\xff]").search):
0157     # decode non-ascii string (if possible)
0158     if unicode and encoding and is8bit(data):
0159         data = unicode(data, encoding)
0160     return data
0161 
0162 def escape(s, replace=string.replace):
0163     s = replace(s, "&", "&")
0164     s = replace(s, "<", "&lt;")
0165     return replace(s, ">", "&gt;",)
0166 
0167 if unicode:
0168     def _stringify(string):
0169         # convert to 7-bit ascii if possible
0170         try:
0171             return str(string)
0172         except UnicodeError:
0173             return string
0174 else:
0175     def _stringify(string):
0176         return string
0177 
0178 __version__ = "1.0.1"
0179 
0180 # xmlrpc integer limits
0181 MAXINT =  2L**31-1
0182 MININT = -2L**31
0183 
0184 # --------------------------------------------------------------------
0185 # Error constants (from Dan Libby's specification at
0186 # http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php)
0187 
0188 # Ranges of errors
0189 PARSE_ERROR       = -32700
0190 SERVER_ERROR      = -32600
0191 APPLICATION_ERROR = -32500
0192 SYSTEM_ERROR      = -32400
0193 TRANSPORT_ERROR   = -32300
0194 
0195 # Specific errors
0196 NOT_WELLFORMED_ERROR  = -32700
0197 UNSUPPORTED_ENCODING  = -32701
0198 INVALID_ENCODING_CHAR = -32702
0199 INVALID_XMLRPC        = -32600
0200 METHOD_NOT_FOUND      = -32601
0201 INVALID_METHOD_PARAMS = -32602
0202 INTERNAL_ERROR        = -32603
0203 
0204 # --------------------------------------------------------------------
0205 # Exceptions
0206 
0207 ##
0208 # Base class for all kinds of client-side errors.
0209 
0210 class Error(Exception):
0211     """Base class for client errors."""
0212     def __str__(self):
0213         return repr(self)
0214 
0215 ##
0216 # Indicates an HTTP-level protocol error.  This is raised by the HTTP
0217 # transport layer, if the server returns an error code other than 200
0218 # (OK).
0219 #
0220 # @param url The target URL.
0221 # @param errcode The HTTP error code.
0222 # @param errmsg The HTTP error message.
0223 # @param headers The HTTP header dictionary.
0224 
0225 class ProtocolError(Error):
0226     """Indicates an HTTP protocol error."""
0227     def __init__(self, url, errcode, errmsg, headers):
0228         Error.__init__(self)
0229         self.url = url
0230         self.errcode = errcode
0231         self.errmsg = errmsg
0232         self.headers = headers
0233     def __repr__(self):
0234         return (
0235             "<ProtocolError for %s: %s %s>" %
0236             (self.url, self.errcode, self.errmsg)
0237             )
0238 
0239 ##
0240 # Indicates a broken XML-RPC response package.  This exception is
0241 # raised by the unmarshalling layer, if the XML-RPC response is
0242 # malformed.
0243 
0244 class ResponseError(Error):
0245     """Indicates a broken response package."""
0246     pass
0247 
0248 ##
0249 # Indicates an XML-RPC fault response package.  This exception is
0250 # raised by the unmarshalling layer, if the XML-RPC response contains
0251 # a fault string.  This exception can also used as a class, to
0252 # generate a fault XML-RPC message.
0253 #
0254 # @param faultCode The XML-RPC fault code.
0255 # @param faultString The XML-RPC fault string.
0256 
0257 class Fault(Error):
0258     """Indicates an XML-RPC fault package."""
0259     def __init__(self, faultCode, faultString, **extra):
0260         Error.__init__(self)
0261         self.faultCode = faultCode
0262         self.faultString = faultString
0263     def __repr__(self):
0264         return (
0265             "<Fault %s: %s>" %
0266             (self.faultCode, repr(self.faultString))
0267             )
0268 
0269 # --------------------------------------------------------------------
0270 # Special values
0271 
0272 ##
0273 # Wrapper for XML-RPC boolean values.  Use the xmlrpclib.True and
0274 # xmlrpclib.False constants, or the xmlrpclib.boolean() function, to
0275 # generate boolean XML-RPC values.
0276 #
0277 # @param value A boolean value.  Any true value is interpreted as True,
0278 #              all other values are interpreted as False.
0279 
0280 if _bool_is_builtin:
0281     boolean = Boolean = bool
0282     # to avoid breaking code which references xmlrpclib.{True,False}
0283     True, False = True, False
0284 else:
0285     class Boolean:
0286         """Boolean-value wrapper.
0287 
0288         Use True or False to generate a "boolean" XML-RPC value.
0289         """
0290 
0291         def __init__(self, value = 0):
0292             self.value = operator.truth(value)
0293 
0294         def encode(self, out):
0295             out.write("<value><boolean>%d</boolean></value>\n" % self.value)
0296 
0297         def __cmp__(self, other):
0298             if isinstance(other, Boolean):
0299                 other = other.value
0300             return cmp(self.value, other)
0301 
0302         def __repr__(self):
0303             if self.value:
0304                 return "<Boolean True at %x>" % id(self)
0305             else:
0306                 return "<Boolean False at %x>" % id(self)
0307 
0308         def __int__(self):
0309             return self.value
0310 
0311         def __nonzero__(self):
0312             return self.value
0313 
0314     True, False = Boolean(1), Boolean(0)
0315 
0316     ##
0317     # Map true or false value to XML-RPC boolean values.
0318     #
0319     # @def boolean(value)
0320     # @param value A boolean value.  Any true value is mapped to True,
0321     #              all other values are mapped to False.
0322     # @return xmlrpclib.True or xmlrpclib.False.
0323     # @see Boolean
0324     # @see True
0325     # @see False
0326 
0327     def boolean(value, _truefalse=(False, True)):
0328         """Convert any Python value to XML-RPC 'boolean'."""
0329         return _truefalse[operator.truth(value)]
0330 
0331 ##
0332 # Wrapper for XML-RPC DateTime values.  This converts a time value to
0333 # the format used by XML-RPC.
0334 # <p>
0335 # The value can be given as a string in the format
0336 # "yyyymmddThh:mm:ss", as a 9-item time tuple (as returned by
0337 # time.localtime()), or an integer value (as returned by time.time()).
0338 # The wrapper uses time.localtime() to convert an integer to a time
0339 # tuple.
0340 #
0341 # @param value The time, given as an ISO 8601 string, a time
0342 #              tuple, or a integer time value.
0343 
0344 class DateTime:
0345     """DateTime wrapper for an ISO 8601 string or time tuple or
0346     localtime integer value to generate 'dateTime.iso8601' XML-RPC
0347     value.
0348     """
0349 
0350     def __init__(self, value=0):
0351         if not isinstance(value, StringType):
0352             if not isinstance(value, (TupleType, time.struct_time)):
0353                 if value == 0:
0354                     value = time.time()
0355                 value = time.localtime(value)
0356             value = time.strftime("%Y%m%dT%H:%M:%S", value)
0357         self.value = value
0358 
0359     def __cmp__(self, other):
0360         if isinstance(other, DateTime):
0361             other = other.value
0362         return cmp(self.value, other)
0363 
0364     ##
0365     # Get date/time value.
0366     #
0367     # @return Date/time value, as an ISO 8601 string.
0368 
0369     def __str__(self):
0370         return self.value
0371 
0372     def __repr__(self):
0373         return "<DateTime %s at %x>" % (repr(self.value), id(self))
0374 
0375     def decode(self, data):
0376         self.value = string.strip(data)
0377 
0378     def encode(self, out):
0379         out.write("<value><dateTime.iso8601>")
0380         out.write(self.value)
0381         out.write("</dateTime.iso8601></value>\n")
0382 
0383 def _datetime(data):
0384     # decode xml element contents into a DateTime structure.
0385     value = DateTime()
0386     value.decode(data)
0387     return value
0388 
0389 ##
0390 # Wrapper for binary data.  This can be used to transport any kind
0391 # of binary data over XML-RPC, using BASE64 encoding.
0392 #
0393 # @param data An 8-bit string containing arbitrary data.
0394 
0395 import base64
0396 try:
0397     import cStringIO as StringIO
0398 except ImportError:
0399     import StringIO
0400 
0401 class Binary:
0402     """Wrapper for binary data."""
0403 
0404     def __init__(self, data=None):
0405         self.data = data
0406 
0407     ##
0408     # Get buffer contents.
0409     #
0410     # @return Buffer contents, as an 8-bit string.
0411 
0412     def __str__(self):
0413         return self.data or ""
0414 
0415     def __cmp__(self, other):
0416         if isinstance(other, Binary):
0417             other = other.data
0418         return cmp(self.data, other)
0419 
0420     def decode(self, data):
0421         self.data = base64.decodestring(data)
0422 
0423     def encode(self, out):
0424         out.write("<value><base64>\n")
0425         base64.encode(StringIO.StringIO(self.data), out)
0426         out.write("</base64></value>\n")
0427 
0428 def _binary(data):
0429     # decode xml element contents into a Binary structure
0430     value = Binary()
0431     value.decode(data)
0432     return value
0433 
0434 WRAPPERS = (DateTime, Binary)
0435 if not _bool_is_builtin:
0436     WRAPPERS = WRAPPERS + (Boolean,)
0437 
0438 # --------------------------------------------------------------------
0439 # XML parsers
0440 
0441 try:
0442     # optional xmlrpclib accelerator
0443     import _xmlrpclib
0444     FastParser = _xmlrpclib.Parser
0445     FastUnmarshaller = _xmlrpclib.Unmarshaller
0446 except (AttributeError, ImportError):
0447     FastParser = FastUnmarshaller = None
0448 
0449 try:
0450     import _xmlrpclib
0451     FastMarshaller = _xmlrpclib.Marshaller
0452 except (AttributeError, ImportError):
0453     FastMarshaller = None
0454 
0455 #
0456 # the SGMLOP parser is about 15x faster than Python's builtin
0457 # XML parser.  SGMLOP sources can be downloaded from:
0458 #
0459 #     http://www.pythonware.com/products/xml/sgmlop.htm
0460 #
0461 
0462 try:
0463     import sgmlop
0464     if not hasattr(sgmlop, "XMLParser"):
0465         raise ImportError
0466 except ImportError:
0467     SgmlopParser = None # sgmlop accelerator not available
0468 else:
0469     class SgmlopParser:
0470         def __init__(self, target):
0471 
0472             # setup callbacks
0473             self.finish_starttag = target.start
0474             self.finish_endtag = target.end
0475             self.handle_data = target.data
0476             self.handle_xml = target.xml
0477 
0478             # activate parser
0479             self.parser = sgmlop.XMLParser()
0480             self.parser.register(self)
0481             self.feed = self.parser.feed
0482             self.entity = {
0483                 "amp": "&", "gt": ">", "lt": "<",
0484                 "apos": "'", "quot": '"'
0485                 }
0486 
0487         def close(self):
0488             try:
0489                 self.parser.close()
0490             finally:
0491                 self.parser = self.feed = None # nuke circular reference
0492 
0493         def handle_proc(self, tag, attr):
0494             m = re.search("encoding\s*=\s*['\"]([^\"']+)[\"']", attr)
0495             if m:
0496                 self.handle_xml(m.group(1), 1)
0497 
0498         def handle_entityref(self, entity):
0499             # <string> entity
0500             try:
0501                 self.handle_data(self.entity[entity])
0502             except KeyError:
0503                 self.handle_data("&%s;" % entity)
0504 
0505 try:
0506     from xml.parsers import expat
0507     if not hasattr(expat, "ParserCreate"):
0508         raise ImportError
0509 except ImportError:
0510     ExpatParser = None # expat not available
0511 else:
0512     class ExpatParser:
0513         # fast expat parser for Python 2.0 and later.  this is about
0514         # 50% slower than sgmlop, on roundtrip testing
0515         def __init__(self, target):
0516             self._parser = parser = expat.ParserCreate(None, None)
0517             self._target = target
0518             parser.StartElementHandler = target.start
0519             parser.EndElementHandler = target.end
0520             parser.CharacterDataHandler = target.data
0521             encoding = None
0522             if not parser.returns_unicode:
0523                 encoding = "utf-8"
0524             target.xml(encoding, None)
0525 
0526         def feed(self, data):
0527             self._parser.Parse(data, 0)
0528 
0529         def close(self):
0530             self._parser.Parse("", 1) # end of data
0531             del self._target, self._parser # get rid of circular references
0532 
0533 class SlowParser:
0534     """Default XML parser (based on xmllib.XMLParser)."""
0535     # this is about 10 times slower than sgmlop, on roundtrip
0536     # testing.
0537     def __init__(self, target):
0538         import xmllib # lazy subclassing (!)
0539         if xmllib.XMLParser not in SlowParser.__bases__:
0540             SlowParser.__bases__ = (xmllib.XMLParser,)
0541         self.handle_xml = target.xml
0542         self.unknown_starttag = target.start
0543         self.handle_data = target.data
0544         self.handle_cdata = target.data
0545         self.unknown_endtag = target.end
0546         try:
0547             xmllib.XMLParser.__init__(self, accept_utf8=1)
0548         except TypeError:
0549             xmllib.XMLParser.__init__(self) # pre-2.0
0550 
0551 # --------------------------------------------------------------------
0552 # XML-RPC marshalling and unmarshalling code
0553 
0554 ##
0555 # XML-RPC marshaller.
0556 #
0557 # @param encoding Default encoding for 8-bit strings.  The default
0558 #     value is None (interpreted as UTF-8).
0559 # @see dumps
0560 
0561 class Marshaller:
0562     """Generate an XML-RPC params chunk from a Python data structure.
0563 
0564     Create a Marshaller instance for each set of parameters, and use
0565     the "dumps" method to convert your data (represented as a tuple)
0566     to an XML-RPC params chunk.  To write a fault response, pass a
0567     Fault instance instead.  You may prefer to use the "dumps" module
0568     function for this purpose.
0569     """
0570 
0571     # by the way, if you don't understand what's going on in here,
0572     # that's perfectly ok.
0573 
0574     def __init__(self, encoding=None, allow_none=0):
0575         self.memo = {}
0576         self.data = None
0577         self.encoding = encoding
0578         self.allow_none = allow_none
0579 
0580     dispatch = {}
0581 
0582     def dumps(self, values):
0583         out = []
0584         write = out.append
0585         dump = self.__dump
0586         if isinstance(values, Fault):
0587             # fault instance
0588             write("<fault>\n")
0589             dump({'faultCode': values.faultCode,
0590                   'faultString': values.faultString},
0591                  write)
0592             write("</fault>\n")
0593         else:
0594             # parameter block
0595             # FIXME: the xml-rpc specification allows us to leave out
0596             # the entire <params> block if there are no parameters.
0597             # however, changing this may break older code (including
0598             # old versions of xmlrpclib.py), so this is better left as
0599             # is for now.  See @XMLRPC3 for more information. /F
0600             write("<params>\n")
0601             for v in values:
0602                 write("<param>\n")
0603                 dump(v, write)
0604                 write("</param>\n")
0605             write("</params>\n")
0606         result = string.join(out, "")
0607         return result
0608 
0609     def __dump(self, value, write):
0610         try:
0611             f = self.dispatch[type(value)]
0612         except KeyError:
0613             raise TypeError, "cannot marshal %s objects" % type(value)
0614         else:
0615             f(self, value, write)
0616 
0617     def dump_nil (self, value, write):
0618         if not self.allow_none:
0619             raise TypeError, "cannot marshal None unless allow_none is enabled"
0620         write("<value><nil/></value>")
0621     dispatch[NoneType] = dump_nil
0622 
0623     def dump_int(self, value, write):
0624         # in case ints are > 32 bits
0625         if value > MAXINT or value < MININT:
0626             raise OverflowError, "int exceeds XML-RPC limits"
0627         write("<value><int>")
0628         write(str(value))
0629         write("</int></value>\n")
0630     dispatch[IntType] = dump_int
0631 
0632     if _bool_is_builtin:
0633         def dump_bool(self, value, write):
0634             write("<value><boolean>")
0635             write(value and "1" or "0")
0636             write("</boolean></value>\n")
0637         dispatch[bool] = dump_bool
0638 
0639     def dump_long(self, value, write):
0640         if value > MAXINT or value < MININT:
0641             raise OverflowError, "long int exceeds XML-RPC limits"
0642         write("<value><int>")
0643         write(str(int(value)))
0644         write("</int></value>\n")
0645     dispatch[LongType] = dump_long
0646 
0647     def dump_double(self, value, write):
0648         write("<value><double>")
0649         write(repr(value))
0650         write("</double></value>\n")
0651     dispatch[FloatType] = dump_double
0652 
0653     def dump_string(self, value, write, escape=escape):
0654         write("<value><string>")
0655         write(escape(value))
0656         write("</string></value>\n")
0657     dispatch[StringType] = dump_string
0658 
0659     if unicode:
0660         def dump_unicode(self, value, write, escape=escape):
0661             value = value.encode(self.encoding)
0662             write("<value><string>")
0663             write(escape(value))
0664             write("</string></value>\n")
0665         dispatch[UnicodeType] = dump_unicode
0666 
0667     def dump_array(self, value, write):
0668         i = id(value)
0669         if self.memo.has_key(i):
0670             raise TypeError, "cannot marshal recursive sequences"
0671         self.memo[i] = None
0672         dump = self.__dump
0673         write("<value><array><data>\n")
0674         for v in value:
0675             dump(v, write)
0676         write("</data></array></value>\n")
0677         del self.memo[i]
0678     dispatch[TupleType] = dump_array
0679     dispatch[ListType] = dump_array
0680 
0681     def dump_struct(self, value, write, escape=escape):
0682         i = id(value)
0683         if self.memo.has_key(i):
0684             raise TypeError, "cannot marshal recursive dictionaries"
0685         self.memo[i] = None
0686         dump = self.__dump
0687         write("<value><struct>\n")
0688         for k, v in value.items():
0689             write("<member>\n")
0690             if type(k) is not StringType:
0691                 if unicode and type(k) is UnicodeType:
0692                     k = k.encode(self.encoding)
0693                 else:
0694                     raise TypeError, "dictionary key must be string"
0695             write("<name>%s</name>\n" % escape(k))
0696             dump(v, write)
0697             write("</member>\n")
0698         write("</struct></value>\n")
0699         del self.memo[i]
0700     dispatch[DictType] = dump_struct
0701 
0702     def dump_instance(self, value, write):
0703         # check for special wrappers
0704         if value.__class__ in WRAPPERS:
0705             self.write = write
0706             value.encode(self)
0707             del self.write
0708         else:
0709             # store instance attributes as a struct (really?)
0710             self.dump_struct(value.__dict__, write)
0711     dispatch[InstanceType] = dump_instance
0712 
0713 ##
0714 # XML-RPC unmarshaller.
0715 #
0716 # @see loads
0717 
0718 class Unmarshaller:
0719     """Unmarshal an XML-RPC response, based on incoming XML event
0720     messages (start, data, end).  Call close() to get the resulting
0721     data structure.
0722 
0723     Note that this reader is fairly tolerant, and gladly accepts bogus
0724     XML-RPC data without complaining (but not bogus XML).
0725     """
0726 
0727     # and again, if you don't understand what's going on in here,
0728     # that's perfectly ok.
0729 
0730     def __init__(self):
0731         self._type = None
0732         self._stack = []
0733         self._marks = []
0734         self._data = []
0735         self._methodname = None
0736         self._encoding = "utf-8"
0737         self.append = self._stack.append
0738 
0739     def close(self):
0740         # return response tuple and target method
0741         if self._type is None or self._marks:
0742             raise ResponseError()
0743         if self._type == "fault":
0744             raise Fault(**self._stack[0])
0745         return tuple(self._stack)
0746 
0747     def getmethodname(self):
0748         return self._methodname
0749 
0750     #
0751     # event handlers
0752 
0753     def xml(self, encoding, standalone):
0754         self._encoding = encoding
0755         # FIXME: assert standalone == 1 ???
0756 
0757     def start(self, tag, attrs):
0758         # prepare to handle this element
0759         if tag == "array" or tag == "struct":
0760             self._marks.append(len(self._stack))
0761         self._data = []
0762         self._value = (tag == "value")
0763 
0764     def data(self, text):
0765         self._data.append(text)
0766 
0767     def end(self, tag, join=string.join):
0768         # call the appropriate end tag handler
0769         try:
0770             f = self.dispatch[tag]
0771         except KeyError:
0772             pass # unknown tag ?
0773         else:
0774             return f(self, join(self._data, ""))
0775 
0776     #
0777     # accelerator support
0778 
0779     def end_dispatch(self, tag, data):
0780         # dispatch data
0781         try:
0782             f = self.dispatch[tag]
0783         except KeyError:
0784             pass # unknown tag ?
0785         else:
0786             return f(self, data)
0787 
0788     #
0789     # element decoders
0790 
0791     dispatch = {}
0792 
0793     def end_nil (self, data):
0794         self.append(None)
0795         self._value = 0
0796     dispatch["nil"] = end_nil
0797 
0798     def end_boolean(self, data):
0799         if data == "0":
0800             self.append(False)
0801         elif data == "1":
0802             self.append(True)
0803         else:
0804             raise TypeError, "bad boolean value"
0805         self._value = 0
0806     dispatch["boolean"] = end_boolean
0807 
0808     def end_int(self, data):
0809         self.append(int(data))
0810         self._value = 0
0811     dispatch["i4"] = end_int
0812     dispatch["int"] = end_int
0813 
0814     def end_double(self, data):
0815         self.append(float(data))
0816         self._value = 0
0817     dispatch["double"] = end_double
0818 
0819     def end_string(self, data):
0820         if self._encoding:
0821             data = _decode(data, self._encoding)
0822         self.append(_stringify(data))
0823         self._value = 0
0824     dispatch["string"] = end_string
0825     dispatch["name"] = end_string # struct keys are always strings
0826 
0827     def end_array(self, data):
0828         mark = self._marks.pop()
0829         # map arrays to Python lists
0830         self._stack[mark:] = [self._stack[mark:]]
0831         self._value = 0
0832     dispatch["array"] = end_array
0833 
0834     def end_struct(self, data):
0835         mark = self._marks.pop()
0836         # map structs to Python dictionaries
0837         dict = {}
0838         items = self._stack[mark:]
0839         for i in range(0, len(items), 2):
0840             dict[_stringify(items[i])] = items[i+1]
0841         self._stack[mark:] = [dict]
0842         self._value = 0
0843     dispatch["struct"] = end_struct
0844 
0845     def end_base64(self, data):
0846         value = Binary()
0847         value.decode(data)
0848         self.append(value)
0849         self._value = 0
0850     dispatch["base64"] = end_base64
0851 
0852     def end_dateTime(self, data):
0853         value = DateTime()
0854         value.decode(data)
0855         self.append(value)
0856     dispatch["dateTime.iso8601"] = end_dateTime
0857 
0858     def end_value(self, data):
0859         # if we stumble upon a value element with no internal
0860         # elements, treat it as a string element
0861         if self._value:
0862             self.end_string(data)
0863     dispatch["value"] = end_value
0864 
0865     def end_params(self, data):
0866         self._type = "params"
0867     dispatch["params"] = end_params
0868 
0869     def end_fault(self, data):
0870         self._type = "fault"
0871     dispatch["fault"] = end_fault
0872 
0873     def end_methodName(self, data):
0874         if self._encoding:
0875             data = _decode(data, self._encoding)
0876         self._methodname = data
0877         self._type = "methodName" # no params
0878     dispatch["methodName"] = end_methodName
0879 
0880 ## Multicall support
0881 #
0882 
0883 class _MultiCallMethod:
0884     # some lesser magic to store calls made to a MultiCall object
0885     # for batch execution
0886     def __init__(self, call_list, name):
0887         self.__call_list = call_list
0888         self.__name = name
0889     def __getattr__(self, name):
0890         return _MultiCallMethod(self.__call_list, "%s.%s" % (self.__name, name))
0891     def __call__(self, *args):
0892         self.__call_list.append((self.__name, args))
0893 
0894 class MultiCallIterator:
0895     """Iterates over the results of a multicall. Exceptions are
0896     thrown in response to xmlrpc faults."""
0897 
0898     def __init__(self, results):
0899         self.results = results
0900 
0901     def __getitem__(self, i):
0902         item = self.results[i]
0903         if type(item) == type({}):
0904             raise Fault(item['faultCode'], item['faultString'])
0905         elif type(item) == type([]):
0906             return item[0]
0907         else:
0908             raise ValueError,\
0909                   "unexpected type in multicall result"
0910 
0911 class MultiCall:
0912     """server -> a object used to boxcar method calls
0913 
0914     server should be a ServerProxy object.
0915 
0916     Methods can be added to the MultiCall using normal
0917     method call syntax e.g.:
0918 
0919     multicall = MultiCall(server_proxy)
0920     multicall.add(2,3)
0921     multicall.get_address("Guido")
0922 
0923     To execute the multicall, call the MultiCall object e.g.:
0924 
0925     add_result, address = multicall()
0926     """
0927 
0928     def __init__(self, server):
0929         self.__server = server
0930         self.__call_list = []
0931 
0932     def __repr__(self):
0933         return "<MultiCall at %x>" % id(self)
0934 
0935     __str__ = __repr__
0936 
0937     def __getattr__(self, name):
0938         return _MultiCallMethod(self.__call_list, name)
0939 
0940     def __call__(self):
0941         marshalled_list = []
0942         for name, args in self.__call_list:
0943             marshalled_list.append({'methodName' : name, 'params' : args})
0944 
0945         return MultiCallIterator(self.__server.system.multicall(marshalled_list))
0946 
0947 # --------------------------------------------------------------------
0948 # convenience functions
0949 
0950 ##
0951 # Create a parser object, and connect it to an unmarshalling instance.
0952 # This function picks the fastest available XML parser.
0953 #
0954 # return A (parser, unmarshaller) tuple.
0955 
0956 def getparser():
0957     """getparser() -> parser, unmarshaller
0958 
0959     Create an instance of the fastest available parser, and attach it
0960     to an unmarshalling object.  Return both objects.
0961     """
0962     if FastParser and FastUnmarshaller:
0963         target = FastUnmarshaller(True, False, _binary, _datetime, Fault)
0964         parser = FastParser(target)
0965     else:
0966         target = Unmarshaller()
0967         if FastParser:
0968             parser = FastParser(target)
0969         elif SgmlopParser:
0970             parser = SgmlopParser(target)
0971         elif ExpatParser:
0972             parser = ExpatParser(target)
0973         else:
0974             parser = SlowParser(target)
0975     return parser, target
0976 
0977 ##
0978 # Convert a Python tuple or a Fault instance to an XML-RPC packet.
0979 #
0980 # @def dumps(params, **options)
0981 # @param params A tuple or Fault instance.
0982 # @keyparam methodname If given, create a methodCall request for
0983 #     this method name.
0984 # @keyparam methodresponse If given, create a methodResponse packet.
0985 #     If used with a tuple, the tuple must be a singleton (that is,
0986 #     it must contain exactly one element).
0987 # @keyparam encoding The packet encoding.
0988 # @return A string containing marshalled data.
0989 
0990 def dumps(params, methodname=None, methodresponse=None, encoding=None,
0991           allow_none=0):
0992     """data [,options] -> marshalled data
0993 
0994     Convert an argument tuple or a Fault instance to an XML-RPC
0995     request (or response, if the methodresponse option is used).
0996 
0997     In addition to the data object, the following options can be given
0998     as keyword arguments:
0999 
1000         methodname: the method name for a methodCall packet
1001 
1002         methodresponse: true to create a methodResponse packet.
1003         If this option is used with a tuple, the tuple must be
1004         a singleton (i.e. it can contain only one element).
1005 
1006         encoding: the packet encoding (default is UTF-8)
1007 
1008     All 8-bit strings in the data structure are assumed to use the
1009     packet encoding.  Unicode strings are automatically converted,
1010     where necessary.
1011     """
1012 
1013     assert isinstance(params, TupleType) or isinstance(params, Fault),\
1014            "argument must be tuple or Fault instance"
1015 
1016     if isinstance(params, Fault):
1017         methodresponse = 1
1018     elif methodresponse and isinstance(params, TupleType):
1019         assert len(params) == 1, "response tuple must be a singleton"
1020 
1021     if not encoding:
1022         encoding = "utf-8"
1023 
1024     if FastMarshaller:
1025         m = FastMarshaller(encoding)
1026     else:
1027         m = Marshaller(encoding, allow_none)
1028 
1029     data = m.dumps(params)
1030 
1031     if encoding != "utf-8":
1032         xmlheader = "<?xml version='1.0' encoding='%s'?>\n" % str(encoding)
1033     else:
1034         xmlheader = "<?xml version='1.0'?>\n" # utf-8 is default
1035 
1036     # standard XML-RPC wrappings
1037     if methodname:
1038         # a method call
1039         if not isinstance(methodname, StringType):
1040             methodname = methodname.encode(encoding)
1041         data = (
1042             xmlheader,
1043             "<methodCall>\n"
1044             "<methodName>", methodname, "</methodName>\n",
1045             data,
1046             "</methodCall>\n"
1047             )
1048     elif methodresponse:
1049         # a method response, or a fault structure
1050         data = (
1051             xmlheader,
1052             "<methodResponse>\n",
1053             data,
1054             "</methodResponse>\n"
1055             )
1056     else:
1057         return data # return as is
1058     return string.join(data, "")
1059 
1060 ##
1061 # Convert an XML-RPC packet to a Python object.  If the XML-RPC packet
1062 # represents a fault condition, this function raises a Fault exception.
1063 #
1064 # @param data An XML-RPC packet, given as an 8-bit string.
1065 # @return A tuple containing the unpacked data, and the method name
1066 #     (None if not present).
1067 # @see Fault
1068 
1069 def loads(data):
1070     """data -> unmarshalled data, method name
1071 
1072     Convert an XML-RPC packet to unmarshalled data plus a method
1073     name (None if not present).
1074 
1075     If the XML-RPC packet represents a fault condition, this function
1076     raises a Fault exception.
1077     """
1078     p, u = getparser()
1079     p.feed(data)
1080     p.close()
1081     return u.close(), u.getmethodname()
1082 
1083 
1084 # --------------------------------------------------------------------
1085 # request dispatcher
1086 
1087 class _Method:
1088     # some magic to bind an XML-RPC method to an RPC server.
1089     # supports "nested" methods (e.g. examples.getStateName)
1090     def __init__(self, send, name):
1091         self.__send = send
1092         self.__name = name
1093     def __getattr__(self, name):
1094         return _Method(self.__send, "%s.%s" % (self.__name, name))
1095     def __call__(self, *args):
1096         return self.__send(self.__name, args)
1097 
1098 ##
1099 # Standard transport class for XML-RPC over HTTP.
1100 # <p>
1101 # You can create custom transports by subclassing this method, and
1102 # overriding selected methods.
1103 
1104 class Transport:
1105     """Handles an HTTP transaction to an XML-RPC server."""
1106 
1107     # client identifier (may be overridden)
1108     user_agent = "xmlrpclib.py/%s (by www.pythonware.com)" % __version__
1109 
1110     ##
1111     # Send a complete request, and parse the response.
1112     #
1113     # @param host Target host.
1114     # @param handler Target PRC handler.
1115     # @param request_body XML-RPC request body.
1116     # @param verbose Debugging flag.
1117     # @return Parsed response.
1118 
1119     def request(self, host, handler, request_body, verbose=0):
1120         # issue XML-RPC request
1121 
1122         h = self.make_connection(host)
1123         if verbose:
1124             h.set_debuglevel(1)
1125 
1126         self.send_request(h, handler, request_body)
1127         self.send_host(h, host)
1128         self.send_user_agent(h)
1129         self.send_content(h, request_body)
1130 
1131         errcode, errmsg, headers = h.getreply()
1132 
1133         if errcode != 200:
1134             raise ProtocolError(
1135                 host + handler,
1136                 errcode, errmsg,
1137                 headers
1138                 )
1139 
1140         self.verbose = verbose
1141 
1142         try:
1143             sock = h._conn.sock
1144         except AttributeError:
1145             sock = None
1146 
1147         return self._parse_response(h.getfile(), sock)
1148 
1149     ##
1150     # Create parser.
1151     #
1152     # @return A 2-tuple containing a parser and a unmarshaller.
1153 
1154     def getparser(self):
1155         # get parser and unmarshaller
1156         return getparser()
1157 
1158     ##
1159     # Get authorization info from host parameter
1160     # Host may be a string, or a (host, x509-dict) tuple; if a string,
1161     # it is checked for a "user:pw@host" format, and a "Basic
1162     # Authentication" header is added if appropriate.
1163     #
1164     # @param host Host descriptor (URL or (URL, x509 info) tuple).
1165     # @return A 3-tuple containing (actual host, extra headers,
1166     #     x509 info).  The header and x509 fields may be None.
1167 
1168     def get_host_info(self, host):
1169 
1170         x509 = {}
1171         if isinstance(host, TupleType):
1172             host, x509 = host
1173 
1174         import urllib
1175         auth, host = urllib.splituser(host)
1176 
1177         if auth:
1178             import base64
1179             auth = base64.encodestring(urllib.unquote(auth))
1180             auth = string.join(string.split(auth), "") # get rid of whitespace
1181             extra_headers = [
1182                 ("Authorization", "Basic " + auth)
1183                 ]
1184         else:
1185             extra_headers = None
1186 
1187         return host, extra_headers, x509
1188 
1189     ##
1190     # Connect to server.
1191     #
1192     # @param host Target host.
1193     # @return A connection handle.
1194 
1195     def make_connection(self, host):
1196         # create a HTTP connection object from a host descriptor
1197         import httplib
1198         host, extra_headers, x509 = self.get_host_info(host)
1199         return httplib.HTTP(host)
1200 
1201     ##
1202     # Send request header.
1203     #
1204     # @param connection Connection handle.
1205     # @param handler Target RPC handler.
1206     # @param request_body XML-RPC body.
1207 
1208     def send_request(self, connection, handler, request_body):
1209         connection.putrequest("POST", handler)
1210 
1211     ##
1212     # Send host name.
1213     #
1214     # @param connection Connection handle.
1215     # @param host Host name.
1216 
1217     def send_host(self, connection, host):
1218         host, extra_headers, x509 = self.get_host_info(host)
1219         connection.putheader("Host", host)
1220         if extra_headers:
1221             if isinstance(extra_headers, DictType):
1222                 extra_headers = extra_headers.items()
1223             for key, value in extra_headers:
1224                 connection.putheader(key, value)
1225 
1226     ##
1227     # Send user-agent identifier.
1228     #
1229     # @param connection Connection handle.
1230 
1231     def send_user_agent(self, connection):
1232         connection.putheader("User-Agent", self.user_agent)
1233 
1234     ##
1235     # Send request body.
1236     #
1237     # @param connection Connection handle.
1238     # @param request_body XML-RPC request body.
1239 
1240     def send_content(self, connection, request_body):
1241         connection.putheader("Content-Type", "text/xml")
1242         connection.putheader("Content-Length", str(len(request_body)))
1243         connection.endheaders()
1244         if request_body:
1245             connection.send(request_body)
1246 
1247     ##
1248     # Parse response.
1249     #
1250     # @param file Stream.
1251     # @return Response tuple and target method.
1252 
1253     def parse_response(self, file):
1254         # compatibility interface
1255         return self._parse_response(file, None)
1256 
1257     ##
1258     # Parse response (alternate interface).  This is similar to the
1259     # parse_response method, but also provides direct access to the
1260     # underlying socket object (where available).
1261     #
1262     # @param file Stream.
1263     # @param sock Socket handle (or None, if the socket object
1264     #    could not be accessed).
1265     # @return Response tuple and target method.
1266 
1267     def _parse_response(self, file, sock):
1268         # read response from input file/socket, and parse it
1269 
1270         p, u = self.getparser()
1271 
1272         while 1:
1273             if sock:
1274                 response = sock.recv(1024)
1275             else:
1276                 response = file.read(1024)
1277             if not response:
1278                 break
1279             if self.verbose:
1280                 print "body:", repr(response)
1281             p.feed(response)
1282 
1283         file.close()
1284         p.close()
1285 
1286         return u.close()
1287 
1288 ##
1289 # Standard transport class for XML-RPC over HTTPS.
1290 
1291 class SafeTransport(Transport):
1292     """Handles an HTTPS transaction to an XML-RPC server."""
1293 
1294     # FIXME: mostly untested
1295 
1296     def make_connection(self, host):
1297         # create a HTTPS connection object from a host descriptor
1298         # host may be a string, or a (host, x509-dict) tuple
1299         import httplib
1300         host, extra_headers, x509 = self.get_host_info(host)
1301         try:
1302             HTTPS = httplib.HTTPS
1303         except AttributeError:
1304             raise NotImplementedError(
1305                 "your version of httplib doesn't support HTTPS"
1306                 )
1307         else:
1308             return HTTPS(host, None, **(x509 or {}))
1309 
1310 ##
1311 # Standard server proxy.  This class establishes a virtual connection
1312 # to an XML-RPC server.
1313 # <p>
1314 # This class is available as ServerProxy and Server.  New code should
1315 # use ServerProxy, to avoid confusion.
1316 #
1317 # @def ServerProxy(uri, **options)
1318 # @param uri The connection point on the server.
1319 # @keyparam transport A transport factory, compatible with the
1320 #    standard transport class.
1321 # @keyparam encoding The default encoding used for 8-bit strings
1322 #    (default is UTF-8).
1323 # @keyparam verbose Use a true value to enable debugging output.
1324 #    (printed to standard output).
1325 # @see Transport
1326 
1327 class ServerProxy:
1328     """uri [,options] -> a logical connection to an XML-RPC server
1329 
1330     uri is the connection point on the server, given as
1331     scheme://host/target.
1332 
1333     The standard implementation always supports the "http" scheme.  If
1334     SSL socket support is available (Python 2.0), it also supports
1335     "https".
1336 
1337     If the target part and the slash preceding it are both omitted,
1338     "/RPC2" is assumed.
1339 
1340     The following options can be given as keyword arguments:
1341 
1342         transport: a transport factory
1343         encoding: the request encoding (default is UTF-8)
1344 
1345     All 8-bit strings passed to the server proxy are assumed to use
1346     the given encoding.
1347     """
1348 
1349     def __init__(self, uri, transport=None, encoding=None, verbose=0,
1350                  allow_none=0):
1351         # establish a "logical" server connection
1352 
1353         # get the url
1354         import urllib
1355         type, uri = urllib.splittype(uri)
1356         if type not in ("http", "https"):
1357             raise IOError, "unsupported XML-RPC protocol"
1358         self.__host, self.__handler = urllib.splithost(uri)
1359         if not self.__handler:
1360             self.__handler = "/RPC2"
1361 
1362         if transport is None:
1363             if type == "https":
1364                 transport = SafeTransport()
1365             else:
1366                 transport = Transport()
1367         self.__transport = transport
1368 
1369         self.__encoding = encoding
1370         self.__verbose = verbose
1371         self.__allow_none = allow_none
1372 
1373     def __request(self, methodname, params):
1374         # call a method on the remote server
1375 
1376         request = dumps(params, methodname, encoding=self.__encoding,
1377                         allow_none=self.__allow_none)
1378 
1379         response = self.__transport.request(
1380             self.__host,
1381             self.__handler,
1382             request,
1383             verbose=self.__verbose
1384             )
1385 
1386         if len(response) == 1:
1387             response = response[0]
1388 
1389         return response
1390 
1391     def __repr__(self):
1392         return (
1393             "<ServerProxy for %s%s>" %
1394             (self.__host, self.__handler)
1395             )
1396 
1397     __str__ = __repr__
1398 
1399     def __getattr__(self, name):
1400         # magic method dispatcher
1401         return _Method(self.__request, name)
1402 
1403     # note: to call a remote object with an non-standard name, use
1404     # result getattr(server, "strange-python-name")(args)
1405 
1406 # compatibility
1407 
1408 Server = ServerProxy
1409 
1410 # --------------------------------------------------------------------
1411 # test code
1412 
1413 if __name__ == "__main__":
1414 
1415     # simple test program (from the XML-RPC specification)
1416 
1417     # server = ServerProxy("http://localhost:8000") # local server
1418     server = ServerProxy("http://time.xmlrpc.com/RPC2")
1419 
1420     print server
1421 
1422     try:
1423         print server.currentTime.getCurrentTime()
1424     except Error, v:
1425         print "ERROR", v
1426 
1427     multi = MultiCall(server)
1428     multi.currentTime.getCurrentTime()
1429     multi.currentTime.getCurrentTime()
1430     try:
1431         for response in multi():
1432             print response
1433     except Error, v:
1434         print "ERROR", v
1435 

Generated by PyXR 0.9.4
SourceForge.net Logo