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