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


syntax highlighted by Code2HTML, v. 0.9.1