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