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, "<", "<") 0165 return replace(s, ">", ">",) 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