source: patches/xmlrpclib.py @ 4607

Last change on this file since 4607 was 634, checked in by hectorgh, 4 years ago

adding xmlrpc library

File size: 50.8 KB
Line 
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, "<", "&lt;")
178    return replace(s, ">", "&gt;",)
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 a 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, (str, unicode)):
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            value = value.encode(self.encoding)
707            write("<value><string>")
708            write(escape(value))
709            write("</string></value>\n")
710        dispatch[UnicodeType] = dump_unicode
711
712    def dump_array(self, value, write):
713        i = id(value)
714        if i in self.memo:
715            raise TypeError, "cannot marshal recursive sequences"
716        self.memo[i] = None
717        dump = self.__dump
718        write("<value><array><data>\n")
719        for v in value:
720            dump(v, write)
721        write("</data></array></value>\n")
722        del self.memo[i]
723    dispatch[TupleType] = dump_array
724    dispatch[ListType] = dump_array
725
726    def dump_struct(self, value, write, escape=escape):
727        i = id(value)
728        if i in self.memo:
729            raise TypeError, "cannot marshal recursive dictionaries"
730        self.memo[i] = None
731        dump = self.__dump
732        write("<value><struct>\n")
733        for k, v in value.items():
734            write("<member>\n")
735            if type(k) is not StringType:
736                if unicode and type(k) is UnicodeType:
737                    k = k.encode(self.encoding)
738                else:
739                    raise TypeError, "dictionary key must be string"
740            write("<name>%s</name>\n" % escape(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._methodname = None
788        self._encoding = "utf-8"
789        self.append = self._stack.append
790        self._use_datetime = use_datetime
791        if use_datetime and not datetime:
792            raise ValueError, "the datetime module is not available"
793
794    def close(self):
795        # return response tuple and target method
796        if self._type is None or self._marks:
797            raise ResponseError()
798        if self._type == "fault":
799            raise Fault(**self._stack[0])
800        return tuple(self._stack)
801
802    def getmethodname(self):
803        return self._methodname
804
805    #
806    # event handlers
807
808    def xml(self, encoding, standalone):
809        self._encoding = encoding
810        # FIXME: assert standalone == 1 ???
811
812    def start(self, tag, attrs):
813        # prepare to handle this element
814        if tag == "array" or tag == "struct":
815            self._marks.append(len(self._stack))
816        self._data = []
817        self._value = (tag == "value")
818
819    def data(self, text):
820        self._data.append(text)
821
822    def end(self, tag, join=string.join):
823        # call the appropriate end tag handler
824        try:
825            f = self.dispatch[tag]
826        except KeyError:
827            pass # unknown tag ?
828        else:
829            return f(self, join(self._data, ""))
830
831    #
832    # accelerator support
833
834    def end_dispatch(self, tag, data):
835        # dispatch data
836        try:
837            f = self.dispatch[tag]
838        except KeyError:
839            pass # unknown tag ?
840        else:
841            return f(self, data)
842
843    #
844    # element decoders
845
846    dispatch = {}
847
848    def end_nil (self, data):
849        self.append(None)
850        self._value = 0
851    dispatch["nil"] = end_nil
852
853    def end_boolean(self, data):
854        if data == "0":
855            self.append(False)
856        elif data == "1":
857            self.append(True)
858        else:
859            raise TypeError, "bad boolean value"
860        self._value = 0
861    dispatch["boolean"] = end_boolean
862
863    def end_int(self, data):
864        self.append(int(data))
865        self._value = 0
866    dispatch["i4"] = end_int
867    dispatch["i8"] = end_int
868    dispatch["int"] = end_int
869
870    def end_double(self, data):
871        self.append(float(data))
872        self._value = 0
873    dispatch["double"] = end_double
874
875    def end_string(self, data):
876        if self._encoding:
877            data = _decode(data, self._encoding)
878        self.append(_stringify(data))
879        self._value = 0
880    dispatch["string"] = end_string
881    dispatch["name"] = end_string # struct keys are always strings
882
883    def end_array(self, data):
884        mark = self._marks.pop()
885        # map arrays to Python lists
886        self._stack[mark:] = [self._stack[mark:]]
887        self._value = 0
888    dispatch["array"] = end_array
889
890    def end_struct(self, data):
891        mark = self._marks.pop()
892        # map structs to Python dictionaries
893        dict = {}
894        items = self._stack[mark:]
895        for i in range(0, len(items), 2):
896            dict[_stringify(items[i])] = items[i+1]
897        self._stack[mark:] = [dict]
898        self._value = 0
899    dispatch["struct"] = end_struct
900
901    def end_base64(self, data):
902        value = Binary()
903        value.decode(data)
904        self.append(value)
905        self._value = 0
906    dispatch["base64"] = end_base64
907
908    def end_dateTime(self, data):
909        value = DateTime()
910        value.decode(data)
911        if self._use_datetime:
912            value = _datetime_type(data)
913        self.append(value)
914    dispatch["dateTime.iso8601"] = end_dateTime
915
916    def end_value(self, data):
917        # if we stumble upon a value element with no internal
918        # elements, treat it as a string element
919        if self._value:
920            self.end_string(data)
921    dispatch["value"] = end_value
922
923    def end_params(self, data):
924        self._type = "params"
925    dispatch["params"] = end_params
926
927    def end_fault(self, data):
928        self._type = "fault"
929    dispatch["fault"] = end_fault
930
931    def end_methodName(self, data):
932        if self._encoding:
933            data = _decode(data, self._encoding)
934        self._methodname = data
935        self._type = "methodName" # no params
936    dispatch["methodName"] = end_methodName
937
938## Multicall support
939#
940
941class _MultiCallMethod:
942    # some lesser magic to store calls made to a MultiCall object
943    # for batch execution
944    def __init__(self, call_list, name):
945        self.__call_list = call_list
946        self.__name = name
947    def __getattr__(self, name):
948        return _MultiCallMethod(self.__call_list, "%s.%s" % (self.__name, name))
949    def __call__(self, *args):
950        self.__call_list.append((self.__name, args))
951
952class MultiCallIterator:
953    """Iterates over the results of a multicall. Exceptions are
954    raised in response to xmlrpc faults."""
955
956    def __init__(self, results):
957        self.results = results
958
959    def __getitem__(self, i):
960        item = self.results[i]
961        if type(item) == type({}):
962            raise Fault(item['faultCode'], item['faultString'])
963        elif type(item) == type([]):
964            return item[0]
965        else:
966            raise ValueError,\
967                  "unexpected type in multicall result"
968
969class MultiCall:
970    """server -> a object used to boxcar method calls
971
972    server should be a ServerProxy object.
973
974    Methods can be added to the MultiCall using normal
975    method call syntax e.g.:
976
977    multicall = MultiCall(server_proxy)
978    multicall.add(2,3)
979    multicall.get_address("Guido")
980
981    To execute the multicall, call the MultiCall object e.g.:
982
983    add_result, address = multicall()
984    """
985
986    def __init__(self, server):
987        self.__server = server
988        self.__call_list = []
989
990    def __repr__(self):
991        return "<MultiCall at %x>" % id(self)
992
993    __str__ = __repr__
994
995    def __getattr__(self, name):
996        return _MultiCallMethod(self.__call_list, name)
997
998    def __call__(self):
999        marshalled_list = []
1000        for name, args in self.__call_list:
1001            marshalled_list.append({'methodName' : name, 'params' : args})
1002
1003        return MultiCallIterator(self.__server.system.multicall(marshalled_list))
1004
1005# --------------------------------------------------------------------
1006# convenience functions
1007
1008##
1009# Create a parser object, and connect it to an unmarshalling instance.
1010# This function picks the fastest available XML parser.
1011#
1012# return A (parser, unmarshaller) tuple.
1013
1014def getparser(use_datetime=0):
1015    """getparser() -> parser, unmarshaller
1016
1017    Create an instance of the fastest available parser, and attach it
1018    to an unmarshalling object.  Return both objects.
1019    """
1020    if use_datetime and not datetime:
1021        raise ValueError, "the datetime module is not available"
1022    if FastParser and FastUnmarshaller:
1023        if use_datetime:
1024            mkdatetime = _datetime_type
1025        else:
1026            mkdatetime = _datetime
1027        target = FastUnmarshaller(True, False, _binary, mkdatetime, Fault)
1028        parser = FastParser(target)
1029    else:
1030        target = Unmarshaller(use_datetime=use_datetime)
1031        if FastParser:
1032            parser = FastParser(target)
1033        elif ExpatParser:
1034            parser = ExpatParser(target)
1035        else:
1036            parser = SlowParser(target)
1037    return parser, target
1038
1039##
1040# Convert a Python tuple or a Fault instance to an XML-RPC packet.
1041#
1042# @def dumps(params, **options)
1043# @param params A tuple or Fault instance.
1044# @keyparam methodname If given, create a methodCall request for
1045#     this method name.
1046# @keyparam methodresponse If given, create a methodResponse packet.
1047#     If used with a tuple, the tuple must be a singleton (that is,
1048#     it must contain exactly one element).
1049# @keyparam encoding The packet encoding.
1050# @return A string containing marshalled data.
1051
1052def dumps(params, methodname=None, methodresponse=None, encoding=None,
1053          allow_none=0):
1054    """data [,options] -> marshalled data
1055
1056    Convert an argument tuple or a Fault instance to an XML-RPC
1057    request (or response, if the methodresponse option is used).
1058
1059    In addition to the data object, the following options can be given
1060    as keyword arguments:
1061
1062        methodname: the method name for a methodCall packet
1063
1064        methodresponse: true to create a methodResponse packet.
1065        If this option is used with a tuple, the tuple must be
1066        a singleton (i.e. it can contain only one element).
1067
1068        encoding: the packet encoding (default is UTF-8)
1069
1070    All 8-bit strings in the data structure are assumed to use the
1071    packet encoding.  Unicode strings are automatically converted,
1072    where necessary.
1073    """
1074
1075    assert isinstance(params, TupleType) or isinstance(params, Fault),\
1076           "argument must be tuple or Fault instance"
1077
1078    if isinstance(params, Fault):
1079        methodresponse = 1
1080    elif methodresponse and isinstance(params, TupleType):
1081        assert len(params) == 1, "response tuple must be a singleton"
1082
1083    if not encoding:
1084        encoding = "utf-8"
1085
1086    if FastMarshaller:
1087        m = FastMarshaller(encoding)
1088    else:
1089        m = Marshaller(encoding, allow_none)
1090
1091    data = m.dumps(params)
1092
1093    if encoding != "utf-8":
1094        xmlheader = "<?xml version='1.0' encoding='%s'?>\n" % str(encoding)
1095    else:
1096        xmlheader = "<?xml version='1.0'?>\n" # utf-8 is default
1097
1098    # standard XML-RPC wrappings
1099    if methodname:
1100        # a method call
1101        if not isinstance(methodname, StringType):
1102            methodname = methodname.encode(encoding)
1103        data = (
1104            xmlheader,
1105            "<methodCall>\n"
1106            "<methodName>", methodname, "</methodName>\n",
1107            data,
1108            "</methodCall>\n"
1109            )
1110    elif methodresponse:
1111        # a method response, or a fault structure
1112        data = (
1113            xmlheader,
1114            "<methodResponse>\n",
1115            data,
1116            "</methodResponse>\n"
1117            )
1118    else:
1119        return data # return as is
1120    return string.join(data, "")
1121
1122##
1123# Convert an XML-RPC packet to a Python object.  If the XML-RPC packet
1124# represents a fault condition, this function raises a Fault exception.
1125#
1126# @param data An XML-RPC packet, given as an 8-bit string.
1127# @return A tuple containing the unpacked data, and the method name
1128#     (None if not present).
1129# @see Fault
1130
1131def loads(data, use_datetime=0):
1132    """data -> unmarshalled data, method name
1133
1134    Convert an XML-RPC packet to unmarshalled data plus a method
1135    name (None if not present).
1136
1137    If the XML-RPC packet represents a fault condition, this function
1138    raises a Fault exception.
1139    """
1140    p, u = getparser(use_datetime=use_datetime)
1141    p.feed(data)
1142    p.close()
1143    return u.close(), u.getmethodname()
1144
1145##
1146# Encode a string using the gzip content encoding such as specified by the
1147# Content-Encoding: gzip
1148# in the HTTP header, as described in RFC 1952
1149#
1150# @param data the unencoded data
1151# @return the encoded data
1152
1153def gzip_encode(data):
1154    """data -> gzip encoded data
1155
1156    Encode data using the gzip content encoding as described in RFC 1952
1157    """
1158    if not gzip:
1159        raise NotImplementedError
1160    f = StringIO.StringIO()
1161    gzf = gzip.GzipFile(mode="wb", fileobj=f, compresslevel=1)
1162    gzf.write(data)
1163    gzf.close()
1164    encoded = f.getvalue()
1165    f.close()
1166    return encoded
1167
1168##
1169# Decode a string using the gzip content encoding such as specified by the
1170# Content-Encoding: gzip
1171# in the HTTP header, as described in RFC 1952
1172#
1173# @param data The encoded data
1174# @keyparam max_decode Maximum bytes to decode (20MB default), use negative
1175#    values for unlimited decoding
1176# @return the unencoded data
1177# @raises ValueError if data is not correctly coded.
1178# @raises ValueError if max gzipped payload length exceeded
1179
1180def gzip_decode(data, max_decode=20971520):
1181    """gzip encoded data -> unencoded data
1182
1183    Decode data using the gzip content encoding as described in RFC 1952
1184    """
1185    if not gzip:
1186        raise NotImplementedError
1187    f = StringIO.StringIO(data)
1188    gzf = gzip.GzipFile(mode="rb", fileobj=f)
1189    try:
1190        if max_decode < 0: # no limit
1191            decoded = gzf.read()
1192        else:
1193            decoded = gzf.read(max_decode + 1)
1194    except IOError:
1195        raise ValueError("invalid data")
1196    f.close()
1197    gzf.close()
1198    if max_decode >= 0 and len(decoded) > max_decode:
1199        raise ValueError("max gzipped payload length exceeded")
1200    return decoded
1201
1202##
1203# Return a decoded file-like object for the gzip encoding
1204# as described in RFC 1952.
1205#
1206# @param response A stream supporting a read() method
1207# @return a file-like object that the decoded data can be read() from
1208
1209class GzipDecodedResponse(gzip.GzipFile if gzip else object):
1210    """a file-like object to decode a response encoded with the gzip
1211    method, as described in RFC 1952.
1212    """
1213    def __init__(self, response):
1214        #response doesn't support tell() and read(), required by
1215        #GzipFile
1216        if not gzip:
1217            raise NotImplementedError
1218        self.stringio = StringIO.StringIO(response.read())
1219        gzip.GzipFile.__init__(self, mode="rb", fileobj=self.stringio)
1220
1221    def close(self):
1222        try:
1223            gzip.GzipFile.close(self)
1224        finally:
1225            self.stringio.close()
1226
1227
1228# --------------------------------------------------------------------
1229# request dispatcher
1230
1231class _Method:
1232    # some magic to bind an XML-RPC method to an RPC server.
1233    # supports "nested" methods (e.g. examples.getStateName)
1234    def __init__(self, send, name):
1235        self.__send = send
1236        self.__name = name
1237    def __getattr__(self, name):
1238        return _Method(self.__send, "%s.%s" % (self.__name, name))
1239    def __call__(self, *args):
1240        return self.__send(self.__name, args)
1241
1242##
1243# Standard transport class for XML-RPC over HTTP.
1244# <p>
1245# You can create custom transports by subclassing this method, and
1246# overriding selected methods.
1247
1248class Transport:
1249    """Handles an HTTP transaction to an XML-RPC server."""
1250
1251    # client identifier (may be overridden)
1252    user_agent = "xmlrpclib.py/%s (by www.pythonware.com)" % __version__
1253
1254    #if true, we'll request gzip encoding
1255    accept_gzip_encoding = True
1256
1257    # if positive, encode request using gzip if it exceeds this threshold
1258    # note that many server will get confused, so only use it if you know
1259    # that they can decode such a request
1260    encode_threshold = None #None = don't encode
1261
1262    def __init__(self, use_datetime=0):
1263        self._use_datetime = use_datetime
1264        self._connection = (None, None)
1265        self._extra_headers = []
1266    ##
1267    # Send a complete request, and parse the response.
1268    # Retry request if a cached connection has disconnected.
1269    #
1270    # @param host Target host.
1271    # @param handler Target PRC handler.
1272    # @param request_body XML-RPC request body.
1273    # @param verbose Debugging flag.
1274    # @return Parsed response.
1275
1276    def request(self, host, handler, request_body, verbose=0):
1277        #retry request once if cached connection has gone cold
1278        for i in (0, 1):
1279            try:
1280                return self.single_request(host, handler, request_body, verbose)
1281            except socket.error, e:
1282                if i or e.errno not in (errno.ECONNRESET, errno.ECONNABORTED, errno.EPIPE):
1283                    raise
1284            except httplib.BadStatusLine: #close after we sent request
1285                if i:
1286                    raise
1287
1288    ##
1289    # Send a complete request, and parse the response.
1290    #
1291    # @param host Target host.
1292    # @param handler Target PRC handler.
1293    # @param request_body XML-RPC request body.
1294    # @param verbose Debugging flag.
1295    # @return Parsed response.
1296
1297    def single_request(self, host, handler, request_body, verbose=0):
1298        # issue XML-RPC request
1299
1300        h = self.make_connection(host)
1301        if verbose:
1302            h.set_debuglevel(1)
1303
1304        try:
1305            self.send_request(h, handler, request_body)
1306            self.send_host(h, host)
1307            self.send_user_agent(h)
1308            self.send_content(h, request_body)
1309
1310            response = h.getresponse(buffering=True)
1311            if response.status == 200:
1312                self.verbose = verbose
1313                return self.parse_response(response)
1314        except Fault:
1315            raise
1316        except Exception:
1317            # All unexpected errors leave connection in
1318            # a strange state, so we clear it.
1319            self.close()
1320            raise
1321
1322        #discard any response data and raise exception
1323        if (response.getheader("content-length", 0)):
1324            response.read()
1325        raise ProtocolError(
1326            host + handler,
1327            response.status, response.reason,
1328            response.msg,
1329            )
1330
1331    ##
1332    # Create parser.
1333    #
1334    # @return A 2-tuple containing a parser and a unmarshaller.
1335
1336    def getparser(self):
1337        # get parser and unmarshaller
1338        return getparser(use_datetime=self._use_datetime)
1339
1340    ##
1341    # Get authorization info from host parameter
1342    # Host may be a string, or a (host, x509-dict) tuple; if a string,
1343    # it is checked for a "user:pw@host" format, and a "Basic
1344    # Authentication" header is added if appropriate.
1345    #
1346    # @param host Host descriptor (URL or (URL, x509 info) tuple).
1347    # @return A 3-tuple containing (actual host, extra headers,
1348    #     x509 info).  The header and x509 fields may be None.
1349
1350    def get_host_info(self, host):
1351
1352        x509 = {}
1353        if isinstance(host, TupleType):
1354            host, x509 = host
1355
1356        import urllib
1357        auth, host = urllib.splituser(host)
1358
1359        if auth:
1360            import base64
1361            auth = base64.encodestring(urllib.unquote(auth))
1362            auth = string.join(string.split(auth), "") # get rid of whitespace
1363            extra_headers = [
1364                ("Authorization", "Basic " + auth)
1365                ]
1366        else:
1367            extra_headers = None
1368
1369        return host, extra_headers, x509
1370
1371    ##
1372    # Connect to server.
1373    #
1374    # @param host Target host.
1375    # @return A connection handle.
1376
1377    def make_connection(self, host):
1378        #return an existing connection if possible.  This allows
1379        #HTTP/1.1 keep-alive.
1380        if self._connection and host == self._connection[0]:
1381            return self._connection[1]
1382
1383        # create a HTTP connection object from a host descriptor
1384        chost, self._extra_headers, x509 = self.get_host_info(host)
1385        #store the host argument along with the connection object
1386        self._connection = host, httplib.HTTPConnection(chost)
1387        return self._connection[1]
1388
1389    ##
1390    # Clear any cached connection object.
1391    # Used in the event of socket errors.
1392    #
1393    def close(self):
1394        host, connection = self._connection
1395        if connection:
1396            self._connection = (None, None)
1397            connection.close()
1398
1399    ##
1400    # Send request header.
1401    #
1402    # @param connection Connection handle.
1403    # @param handler Target RPC handler.
1404    # @param request_body XML-RPC body.
1405
1406    def send_request(self, connection, handler, request_body):
1407        if (self.accept_gzip_encoding and gzip):
1408            connection.putrequest("POST", handler, skip_accept_encoding=True)
1409            connection.putheader("Accept-Encoding", "gzip")
1410        else:
1411            connection.putrequest("POST", handler)
1412
1413    ##
1414    # Send host name.
1415    #
1416    # @param connection Connection handle.
1417    # @param host Host name.
1418    #
1419    # Note: This function doesn't actually add the "Host"
1420    # header anymore, it is done as part of the connection.putrequest() in
1421    # send_request() above.
1422
1423    def send_host(self, connection, host):
1424        extra_headers = self._extra_headers
1425        if extra_headers:
1426            if isinstance(extra_headers, DictType):
1427                extra_headers = extra_headers.items()
1428            for key, value in extra_headers:
1429                connection.putheader(key, value)
1430
1431    ##
1432    # Send user-agent identifier.
1433    #
1434    # @param connection Connection handle.
1435
1436    def send_user_agent(self, connection):
1437        connection.putheader("User-Agent", self.user_agent)
1438
1439    ##
1440    # Send request body.
1441    #
1442    # @param connection Connection handle.
1443    # @param request_body XML-RPC request body.
1444
1445    def send_content(self, connection, request_body):
1446        connection.putheader("Content-Type", "text/xml")
1447
1448        #optionally encode the request
1449        if (self.encode_threshold is not None and
1450            self.encode_threshold < len(request_body) and
1451            gzip):
1452            connection.putheader("Content-Encoding", "gzip")
1453            request_body = gzip_encode(request_body)
1454
1455        connection.putheader("Content-Length", str(len(request_body)))
1456        connection.endheaders(request_body)
1457
1458    ##
1459    # Parse response.
1460    #
1461    # @param file Stream.
1462    # @return Response tuple and target method.
1463
1464    def parse_response(self, response):
1465        # read response data from httpresponse, and parse it
1466
1467        # Check for new http response object, else it is a file object
1468        if hasattr(response,'getheader'):
1469            if response.getheader("Content-Encoding", "") == "gzip":
1470                stream = GzipDecodedResponse(response)
1471            else:
1472                stream = response
1473        else:
1474            stream = response
1475
1476        p, u = self.getparser()
1477
1478        while 1:
1479            data = stream.read(1024)
1480            if not data:
1481                break
1482            if self.verbose:
1483                print "body:", repr(data)
1484            p.feed(data)
1485
1486        if stream is not response:
1487            stream.close()
1488        p.close()
1489
1490        return u.close()
1491
1492##
1493# Standard transport class for XML-RPC over HTTPS.
1494
1495class SafeTransport(Transport):
1496    """Handles an HTTPS transaction to an XML-RPC server."""
1497
1498    def __init__(self, use_datetime=0, context=None):
1499        Transport.__init__(self, use_datetime=use_datetime)
1500        self.context = context
1501
1502    # FIXME: mostly untested
1503
1504    def make_connection(self, host):
1505        if self._connection and host == self._connection[0]:
1506            return self._connection[1]
1507        # create a HTTPS connection object from a host descriptor
1508        # host may be a string, or a (host, x509-dict) tuple
1509        try:
1510            HTTPS = httplib.HTTPSConnection
1511        except AttributeError:
1512            raise NotImplementedError(
1513                "your version of httplib doesn't support HTTPS"
1514                )
1515        else:
1516            chost, self._extra_headers, x509 = self.get_host_info(host)
1517            self._connection = host, HTTPS(chost, None, context=self.context, **(x509 or {}))
1518            return self._connection[1]
1519
1520##
1521# Standard server proxy.  This class establishes a virtual connection
1522# to an XML-RPC server.
1523# <p>
1524# This class is available as ServerProxy and Server.  New code should
1525# use ServerProxy, to avoid confusion.
1526#
1527# @def ServerProxy(uri, **options)
1528# @param uri The connection point on the server.
1529# @keyparam transport A transport factory, compatible with the
1530#    standard transport class.
1531# @keyparam encoding The default encoding used for 8-bit strings
1532#    (default is UTF-8).
1533# @keyparam verbose Use a true value to enable debugging output.
1534#    (printed to standard output).
1535# @see Transport
1536
1537class ServerProxy:
1538    """uri [,options] -> a logical connection to an XML-RPC server
1539
1540    uri is the connection point on the server, given as
1541    scheme://host/target.
1542
1543    The standard implementation always supports the "http" scheme.  If
1544    SSL socket support is available (Python 2.0), it also supports
1545    "https".
1546
1547    If the target part and the slash preceding it are both omitted,
1548    "/RPC2" is assumed.
1549
1550    The following options can be given as keyword arguments:
1551
1552        transport: a transport factory
1553        encoding: the request encoding (default is UTF-8)
1554
1555    All 8-bit strings passed to the server proxy are assumed to use
1556    the given encoding.
1557    """
1558
1559    def __init__(self, uri, transport=None, encoding=None, verbose=0,
1560                 allow_none=0, use_datetime=0, context=None):
1561        # establish a "logical" server connection
1562
1563        if isinstance(uri, unicode):
1564            uri = uri.encode('ISO-8859-1')
1565
1566        # get the url
1567        import urllib
1568        type, uri = urllib.splittype(uri)
1569        if type not in ("http", "https"):
1570            raise IOError, "unsupported XML-RPC protocol"
1571        self.__host, self.__handler = urllib.splithost(uri)
1572        if not self.__handler:
1573            self.__handler = "/RPC2"
1574        if context==None:
1575            import ssl
1576            context=ssl._create_unverified_context()
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 an 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
Note: See TracBrowser for help on using the repository browser.