2 # XML-RPC CLIENT LIBRARY
5 # an XML-RPC client interface for Python.
7 # the marshalling and response parser code can also be used to
8 # implement XML-RPC servers.
11 # this version is designed to work with Python 2.1 or newer.
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
53 # Copyright (c) 1999-2002 by Secret Labs AB.
54 # Copyright (c) 1999-2002 by Fredrik Lundh.
57 # http://www.pythonware.com
59 # --------------------------------------------------------------------
60 # The XML-RPC client interface is
62 # Copyright (c) 1999-2002 by Secret Labs AB
63 # Copyright (c) 1999-2002 by Fredrik Lundh
65 # By obtaining, using, and/or copying this software and/or its
66 # associated documentation, you agree that you have read, understood,
67 # and will comply with the following terms and conditions:
69 # Permission to use, copy, modify, and distribute this software and
70 # its associated documentation for any purpose and without fee is
71 # hereby granted, provided that the above copyright notice appears in
72 # all copies, and that both that copyright notice and this permission
73 # notice appear in supporting documentation, and that the name of
74 # Secret Labs AB or the author not be used in advertising or publicity
75 # pertaining to distribution of the software without specific, written
78 # SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
79 # TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
80 # ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR
81 # BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
82 # DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
83 # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
84 # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
86 # --------------------------------------------------------------------
89 # things to look into some day:
91 # TODO: sort out True/False/boolean issues for Python 2.3
94 An XML-RPC client interface for Python.
96 The marshalling and response parser code can also be used to
97 implement XML-RPC servers.
101 Error Base class for client errors
102 ProtocolError Indicates an HTTP protocol error
103 ResponseError Indicates a broken response package
104 Fault Indicates an XML-RPC fault package
108 ServerProxy Represents a logical connection to an XML-RPC server
110 MultiCall Executor of boxcared xmlrpc requests
111 Boolean boolean wrapper to generate a "boolean" XML-RPC value
112 DateTime dateTime wrapper for an ISO 8601 string or time tuple or
113 localtime integer value to generate a "dateTime.iso8601"
115 Binary binary data wrapper
117 SlowParser Slow but safe standard parser (based on xmllib)
118 Marshaller Generate an XML-RPC params chunk from a Python data structure
119 Unmarshaller Unmarshal an XML-RPC response from incoming XML event message
120 Transport Handles an HTTP transaction to an XML-RPC server
121 SafeTransport Handles an HTTPS transaction to an XML-RPC server
130 boolean Convert any Python value to an XML-RPC boolean
131 getparser Create instance of the fastest available parser & attach
132 to an unmarshalling object
133 dumps Convert an argument tuple or a Fault instance to an XML-RPC
134 request (or response, if the methodresponse option is used).
135 loads Convert an XML-RPC packet to unmarshalled data plus a method
136 name (None if not present).
139 import re
, string
, time
, operator
148 gzip
= None #python can be built without zlib/gzip support
150 # --------------------------------------------------------------------
156 unicode = None # unicode support not available
164 _bool_is_builtin
= False.__class
__.__name
__ == "bool"
168 def _decode(data
, encoding
, is8bit
=re
.compile("[\x80-\xff]").search
):
169 # decode non-ascii string (if possible)
170 if unicode and encoding
and is8bit(data
):
171 data
= unicode(data
, encoding
)
174 def escape(s
, replace
=string
.replace
):
175 s
= replace(s
, "&", "&")
176 s
= replace(s
, "<", "<")
177 return replace(s
, ">", ">",)
180 def _stringify(string
):
181 # convert to 7-bit ascii if possible
183 return string
.encode("ascii")
187 def _stringify(string
):
190 __version__
= "1.0.1"
192 # xmlrpc integer limits
196 # --------------------------------------------------------------------
197 # Error constants (from Dan Libby's specification at
198 # http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php)
202 SERVER_ERROR
= -32600
203 APPLICATION_ERROR
= -32500
204 SYSTEM_ERROR
= -32400
205 TRANSPORT_ERROR
= -32300
208 NOT_WELLFORMED_ERROR
= -32700
209 UNSUPPORTED_ENCODING
= -32701
210 INVALID_ENCODING_CHAR
= -32702
211 INVALID_XMLRPC
= -32600
212 METHOD_NOT_FOUND
= -32601
213 INVALID_METHOD_PARAMS
= -32602
214 INTERNAL_ERROR
= -32603
216 # --------------------------------------------------------------------
220 # Base class for all kinds of client-side errors.
222 class Error(Exception):
223 """Base class for client errors."""
228 # Indicates an HTTP-level protocol error. This is raised by the HTTP
229 # transport layer, if the server returns an error code other than 200
232 # @param url The target URL.
233 # @param errcode The HTTP error code.
234 # @param errmsg The HTTP error message.
235 # @param headers The HTTP header dictionary.
237 class ProtocolError(Error
):
238 """Indicates an HTTP protocol error."""
239 def __init__(self
, url
, errcode
, errmsg
, headers
):
242 self
.errcode
= errcode
244 self
.headers
= headers
247 "<ProtocolError for %s: %s %s>" %
248 (self
.url
, self
.errcode
, self
.errmsg
)
252 # Indicates a broken XML-RPC response package. This exception is
253 # raised by the unmarshalling layer, if the XML-RPC response is
256 class ResponseError(Error
):
257 """Indicates a broken response package."""
261 # Indicates an XML-RPC fault response package. This exception is
262 # raised by the unmarshalling layer, if the XML-RPC response contains
263 # a fault string. This exception can also used as a class, to
264 # generate a fault XML-RPC message.
266 # @param faultCode The XML-RPC fault code.
267 # @param faultString The XML-RPC fault string.
270 """Indicates an XML-RPC fault package."""
271 def __init__(self
, faultCode
, faultString
, **extra
):
273 self
.faultCode
= faultCode
274 self
.faultString
= faultString
278 (self
.faultCode
, repr(self
.faultString
))
281 # --------------------------------------------------------------------
285 # Wrapper for XML-RPC boolean values. Use the xmlrpclib.True and
286 # xmlrpclib.False constants, or the xmlrpclib.boolean() function, to
287 # generate boolean XML-RPC values.
289 # @param value A boolean value. Any true value is interpreted as True,
290 # all other values are interpreted as False.
292 from sys
import modules
293 mod_dict
= modules
[__name__
].__dict
__
295 boolean
= Boolean
= bool
296 # to avoid breaking code which references xmlrpclib.{True,False}
297 mod_dict
['True'] = True
298 mod_dict
['False'] = False
301 """Boolean-value wrapper.
303 Use True or False to generate a "boolean" XML-RPC value.
306 def __init__(self
, value
= 0):
307 self
.value
= operator
.truth(value
)
309 def encode(self
, out
):
310 out
.write("<value><boolean>%d</boolean></value>\n" % self
.value
)
312 def __cmp__(self
, other
):
313 if isinstance(other
, Boolean
):
315 return cmp(self
.value
, other
)
319 return "<Boolean True at %x>" % id(self
)
321 return "<Boolean False at %x>" % id(self
)
326 def __nonzero__(self
):
329 mod_dict
['True'] = Boolean(1)
330 mod_dict
['False'] = Boolean(0)
333 # Map true or false value to XML-RPC boolean values.
335 # @def boolean(value)
336 # @param value A boolean value. Any true value is mapped to True,
337 # all other values are mapped to False.
338 # @return xmlrpclib.True or xmlrpclib.False.
343 def boolean(value
, _truefalse
=(False, True)):
344 """Convert any Python value to XML-RPC 'boolean'."""
345 return _truefalse
[operator
.truth(value
)]
347 del modules
, mod_dict
350 # Wrapper for XML-RPC DateTime values. This converts a time value to
351 # the format used by XML-RPC.
353 # The value can be given as a string in the format
354 # "yyyymmddThh:mm:ss", as a 9-item time tuple (as returned by
355 # time.localtime()), or an integer value (as returned by time.time()).
356 # The wrapper uses time.localtime() to convert an integer to a time
359 # @param value The time, given as an ISO 8601 string, a time
360 # tuple, or a integer time value.
362 def _strftime(value
):
364 if isinstance(value
, datetime
.datetime
):
365 return "%04d%02d%02dT%02d:%02d:%02d" % (
366 value
.year
, value
.month
, value
.day
,
367 value
.hour
, value
.minute
, value
.second
)
369 if not isinstance(value
, (TupleType
, time
.struct_time
)):
372 value
= time
.localtime(value
)
374 return "%04d%02d%02dT%02d:%02d:%02d" % value
[:6]
377 """DateTime wrapper for an ISO 8601 string or time tuple or
378 localtime integer value to generate 'dateTime.iso8601' XML-RPC
382 def __init__(self
, value
=0):
383 if isinstance(value
, StringType
):
386 self
.value
= _strftime(value
)
388 def make_comparable(self
, other
):
389 if isinstance(other
, DateTime
):
392 elif datetime
and isinstance(other
, datetime
.datetime
):
394 o
= other
.strftime("%Y%m%dT%H:%M:%S")
395 elif isinstance(other
, (str, unicode)):
398 elif hasattr(other
, "timetuple"):
400 o
= other
.timetuple()
402 otype
= (hasattr(other
, "__class__")
403 and other
.__class
__.__name
__
405 raise TypeError("Can't compare %s and %s" %
406 (self
.__class
__.__name
__, otype
))
409 def __lt__(self
, other
):
410 s
, o
= self
.make_comparable(other
)
413 def __le__(self
, other
):
414 s
, o
= self
.make_comparable(other
)
417 def __gt__(self
, other
):
418 s
, o
= self
.make_comparable(other
)
421 def __ge__(self
, other
):
422 s
, o
= self
.make_comparable(other
)
425 def __eq__(self
, other
):
426 s
, o
= self
.make_comparable(other
)
429 def __ne__(self
, other
):
430 s
, o
= self
.make_comparable(other
)
434 return time
.strptime(self
.value
, "%Y%m%dT%H:%M:%S")
436 def __cmp__(self
, other
):
437 s
, o
= self
.make_comparable(other
)
441 # Get date/time value.
443 # @return Date/time value, as an ISO 8601 string.
449 return "<DateTime %s at %x>" % (repr(self
.value
), id(self
))
451 def decode(self
, data
):
453 self
.value
= string
.strip(data
)
455 def encode(self
, out
):
456 out
.write("<value><dateTime.iso8601>")
457 out
.write(self
.value
)
458 out
.write("</dateTime.iso8601></value>\n")
461 # decode xml element contents into a DateTime structure.
466 def _datetime_type(data
):
467 t
= time
.strptime(data
, "%Y%m%dT%H:%M:%S")
468 return datetime
.datetime(*tuple(t
)[:6])
471 # Wrapper for binary data. This can be used to transport any kind
472 # of binary data over XML-RPC, using BASE64 encoding.
474 # @param data An 8-bit string containing arbitrary data.
478 import cStringIO
as StringIO
483 """Wrapper for binary data."""
485 def __init__(self
, data
=None):
489 # Get buffer contents.
491 # @return Buffer contents, as an 8-bit string.
494 return self
.data
or ""
496 def __cmp__(self
, other
):
497 if isinstance(other
, Binary
):
499 return cmp(self
.data
, other
)
501 def decode(self
, data
):
502 self
.data
= base64
.decodestring(data
)
504 def encode(self
, out
):
505 out
.write("<value><base64>\n")
506 base64
.encode(StringIO
.StringIO(self
.data
), out
)
507 out
.write("</base64></value>\n")
510 # decode xml element contents into a Binary structure
515 WRAPPERS
= (DateTime
, Binary
)
516 if not _bool_is_builtin
:
517 WRAPPERS
= WRAPPERS
+ (Boolean
,)
519 # --------------------------------------------------------------------
523 # optional xmlrpclib accelerator
525 FastParser
= _xmlrpclib
.Parser
526 FastUnmarshaller
= _xmlrpclib
.Unmarshaller
527 except (AttributeError, ImportError):
528 FastParser
= FastUnmarshaller
= None
532 FastMarshaller
= _xmlrpclib
.Marshaller
533 except (AttributeError, ImportError):
534 FastMarshaller
= None
537 from xml
.parsers
import expat
538 if not hasattr(expat
, "ParserCreate"):
541 ExpatParser
= None # expat not available
544 # fast expat parser for Python 2.0 and later.
545 def __init__(self
, target
):
546 self
._parser
= parser
= expat
.ParserCreate(None, None)
547 self
._target
= target
548 parser
.StartElementHandler
= target
.start
549 parser
.EndElementHandler
= target
.end
550 parser
.CharacterDataHandler
= target
.data
552 if not parser
.returns_unicode
:
554 target
.xml(encoding
, None)
556 def feed(self
, data
):
557 self
._parser
.Parse(data
, 0)
560 self
._parser
.Parse("", 1) # end of data
561 del self
._target
, self
._parser
# get rid of circular references
564 """Default XML parser (based on xmllib.XMLParser)."""
565 # this is the slowest parser.
566 def __init__(self
, target
):
567 import xmllib
# lazy subclassing (!)
568 if xmllib
.XMLParser
not in SlowParser
.__bases
__:
569 SlowParser
.__bases
__ = (xmllib
.XMLParser
,)
570 self
.handle_xml
= target
.xml
571 self
.unknown_starttag
= target
.start
572 self
.handle_data
= target
.data
573 self
.handle_cdata
= target
.data
574 self
.unknown_endtag
= target
.end
576 xmllib
.XMLParser
.__init
__(self
, accept_utf8
=1)
578 xmllib
.XMLParser
.__init
__(self
) # pre-2.0
580 # --------------------------------------------------------------------
581 # XML-RPC marshalling and unmarshalling code
584 # XML-RPC marshaller.
586 # @param encoding Default encoding for 8-bit strings. The default
587 # value is None (interpreted as UTF-8).
591 """Generate an XML-RPC params chunk from a Python data structure.
593 Create a Marshaller instance for each set of parameters, and use
594 the "dumps" method to convert your data (represented as a tuple)
595 to an XML-RPC params chunk. To write a fault response, pass a
596 Fault instance instead. You may prefer to use the "dumps" module
597 function for this purpose.
600 # by the way, if you don't understand what's going on in here,
601 # that's perfectly ok.
603 def __init__(self
, encoding
=None, allow_none
=0):
606 self
.encoding
= encoding
607 self
.allow_none
= allow_none
611 def dumps(self
, values
):
615 if isinstance(values
, Fault
):
618 dump({'faultCode': values
.faultCode
,
619 'faultString': values
.faultString
},
624 # FIXME: the xml-rpc specification allows us to leave out
625 # the entire <params> block if there are no parameters.
626 # however, changing this may break older code (including
627 # old versions of xmlrpclib.py), so this is better left as
628 # is for now. See @XMLRPC3 for more information. /F
635 result
= string
.join(out
, "")
638 def __dump(self
, value
, write
):
640 f
= self
.dispatch
[type(value
)]
642 # check if this object can be marshalled as a structure
646 raise TypeError, "cannot marshal %s objects" % type(value
)
647 # check if this class is a sub-class of a basic type,
648 # because we don't know how to marshal these types
649 # (e.g. a string sub-class)
650 for type_
in type(value
).__mro
__:
651 if type_
in self
.dispatch
.keys():
652 raise TypeError, "cannot marshal %s objects" % type(value
)
653 f
= self
.dispatch
[InstanceType
]
654 f(self
, value
, write
)
656 def dump_nil (self
, value
, write
):
657 if not self
.allow_none
:
658 raise TypeError, "cannot marshal None unless allow_none is enabled"
659 write("<value><nil/></value>")
660 dispatch
[NoneType
] = dump_nil
662 def dump_int(self
, value
, write
):
663 # in case ints are > 32 bits
664 if value
> MAXINT
or value
< MININT
:
665 raise OverflowError, "int exceeds XML-RPC limits"
666 write("<value><int>")
668 write("</int></value>\n")
669 dispatch
[IntType
] = dump_int
672 def dump_bool(self
, value
, write
):
673 write("<value><boolean>")
674 write(value
and "1" or "0")
675 write("</boolean></value>\n")
676 dispatch
[bool] = dump_bool
678 def dump_long(self
, value
, write
):
679 if value
> MAXINT
or value
< MININT
:
680 raise OverflowError, "long int exceeds XML-RPC limits"
681 write("<value><int>")
682 write(str(int(value
)))
683 write("</int></value>\n")
684 dispatch
[LongType
] = dump_long
686 def dump_double(self
, value
, write
):
687 write("<value><double>")
689 write("</double></value>\n")
690 dispatch
[FloatType
] = dump_double
692 def dump_string(self
, value
, write
, escape
=escape
):
693 write("<value><string>")
695 write("</string></value>\n")
696 dispatch
[StringType
] = dump_string
699 def dump_unicode(self
, value
, write
, escape
=escape
):
700 value
= value
.encode(self
.encoding
)
701 write("<value><string>")
703 write("</string></value>\n")
704 dispatch
[UnicodeType
] = dump_unicode
706 def dump_array(self
, value
, write
):
709 raise TypeError, "cannot marshal recursive sequences"
712 write("<value><array><data>\n")
715 write("</data></array></value>\n")
717 dispatch
[TupleType
] = dump_array
718 dispatch
[ListType
] = dump_array
720 def dump_struct(self
, value
, write
, escape
=escape
):
723 raise TypeError, "cannot marshal recursive dictionaries"
726 write("<value><struct>\n")
727 for k
, v
in value
.items():
729 if type(k
) is not StringType
:
730 if unicode and type(k
) is UnicodeType
:
731 k
= k
.encode(self
.encoding
)
733 raise TypeError, "dictionary key must be string"
734 write("<name>%s</name>\n" % escape(k
))
737 write("</struct></value>\n")
739 dispatch
[DictType
] = dump_struct
742 def dump_datetime(self
, value
, write
):
743 write("<value><dateTime.iso8601>")
744 write(_strftime(value
))
745 write("</dateTime.iso8601></value>\n")
746 dispatch
[datetime
.datetime
] = dump_datetime
748 def dump_instance(self
, value
, write
):
749 # check for special wrappers
750 if value
.__class
__ in WRAPPERS
:
755 # store instance attributes as a struct (really?)
756 self
.dump_struct(value
.__dict
__, write
)
757 dispatch
[InstanceType
] = dump_instance
760 # XML-RPC unmarshaller.
765 """Unmarshal an XML-RPC response, based on incoming XML event
766 messages (start, data, end). Call close() to get the resulting
769 Note that this reader is fairly tolerant, and gladly accepts bogus
770 XML-RPC data without complaining (but not bogus XML).
773 # and again, if you don't understand what's going on in here,
774 # that's perfectly ok.
776 def __init__(self
, use_datetime
=0):
781 self
._methodname
= None
782 self
._encoding
= "utf-8"
783 self
.append
= self
._stack
.append
784 self
._use
_datetime
= use_datetime
785 if use_datetime
and not datetime
:
786 raise ValueError, "the datetime module is not available"
789 # return response tuple and target method
790 if self
._type
is None or self
._marks
:
791 raise ResponseError()
792 if self
._type
== "fault":
793 raise Fault(**self
._stack
[0])
794 return tuple(self
._stack
)
796 def getmethodname(self
):
797 return self
._methodname
802 def xml(self
, encoding
, standalone
):
803 self
._encoding
= encoding
804 # FIXME: assert standalone == 1 ???
806 def start(self
, tag
, attrs
):
807 # prepare to handle this element
808 if tag
== "array" or tag
== "struct":
809 self
._marks
.append(len(self
._stack
))
811 self
._value
= (tag
== "value")
813 def data(self
, text
):
814 self
._data
.append(text
)
816 def end(self
, tag
, join
=string
.join
):
817 # call the appropriate end tag handler
819 f
= self
.dispatch
[tag
]
823 return f(self
, join(self
._data
, ""))
826 # accelerator support
828 def end_dispatch(self
, tag
, data
):
831 f
= self
.dispatch
[tag
]
842 def end_nil (self
, data
):
845 dispatch
["nil"] = end_nil
847 def end_boolean(self
, data
):
853 raise TypeError, "bad boolean value"
855 dispatch
["boolean"] = end_boolean
857 def end_int(self
, data
):
858 self
.append(int(data
))
860 dispatch
["i4"] = end_int
861 dispatch
["i8"] = end_int
862 dispatch
["int"] = end_int
864 def end_double(self
, data
):
865 self
.append(float(data
))
867 dispatch
["double"] = end_double
869 def end_string(self
, data
):
871 data
= _decode(data
, self
._encoding
)
872 self
.append(_stringify(data
))
874 dispatch
["string"] = end_string
875 dispatch
["name"] = end_string
# struct keys are always strings
877 def end_array(self
, data
):
878 mark
= self
._marks
.pop()
879 # map arrays to Python lists
880 self
._stack
[mark
:] = [self
._stack
[mark
:]]
882 dispatch
["array"] = end_array
884 def end_struct(self
, data
):
885 mark
= self
._marks
.pop()
886 # map structs to Python dictionaries
888 items
= self
._stack
[mark
:]
889 for i
in range(0, len(items
), 2):
890 dict[_stringify(items
[i
])] = items
[i
+1]
891 self
._stack
[mark
:] = [dict]
893 dispatch
["struct"] = end_struct
895 def end_base64(self
, data
):
900 dispatch
["base64"] = end_base64
902 def end_dateTime(self
, data
):
905 if self
._use
_datetime
:
906 value
= _datetime_type(data
)
908 dispatch
["dateTime.iso8601"] = end_dateTime
910 def end_value(self
, data
):
911 # if we stumble upon a value element with no internal
912 # elements, treat it as a string element
914 self
.end_string(data
)
915 dispatch
["value"] = end_value
917 def end_params(self
, data
):
918 self
._type
= "params"
919 dispatch
["params"] = end_params
921 def end_fault(self
, data
):
923 dispatch
["fault"] = end_fault
925 def end_methodName(self
, data
):
927 data
= _decode(data
, self
._encoding
)
928 self
._methodname
= data
929 self
._type
= "methodName" # no params
930 dispatch
["methodName"] = end_methodName
935 class _MultiCallMethod
:
936 # some lesser magic to store calls made to a MultiCall object
937 # for batch execution
938 def __init__(self
, call_list
, name
):
939 self
.__call
_list
= call_list
941 def __getattr__(self
, name
):
942 return _MultiCallMethod(self
.__call
_list
, "%s.%s" % (self
.__name
, name
))
943 def __call__(self
, *args
):
944 self
.__call
_list
.append((self
.__name
, args
))
946 class MultiCallIterator
:
947 """Iterates over the results of a multicall. Exceptions are
948 thrown in response to xmlrpc faults."""
950 def __init__(self
, results
):
951 self
.results
= results
953 def __getitem__(self
, i
):
954 item
= self
.results
[i
]
955 if type(item
) == type({}):
956 raise Fault(item
['faultCode'], item
['faultString'])
957 elif type(item
) == type([]):
961 "unexpected type in multicall result"
964 """server -> a object used to boxcar method calls
966 server should be a ServerProxy object.
968 Methods can be added to the MultiCall using normal
969 method call syntax e.g.:
971 multicall = MultiCall(server_proxy)
973 multicall.get_address("Guido")
975 To execute the multicall, call the MultiCall object e.g.:
977 add_result, address = multicall()
980 def __init__(self
, server
):
981 self
.__server
= server
982 self
.__call
_list
= []
985 return "<MultiCall at %x>" % id(self
)
989 def __getattr__(self
, name
):
990 return _MultiCallMethod(self
.__call
_list
, name
)
994 for name
, args
in self
.__call
_list
:
995 marshalled_list
.append({'methodName' : name
, 'params' : args
})
997 return MultiCallIterator(self
.__server
.system
.multicall(marshalled_list
))
999 # --------------------------------------------------------------------
1000 # convenience functions
1003 # Create a parser object, and connect it to an unmarshalling instance.
1004 # This function picks the fastest available XML parser.
1006 # return A (parser, unmarshaller) tuple.
1008 def getparser(use_datetime
=0):
1009 """getparser() -> parser, unmarshaller
1011 Create an instance of the fastest available parser, and attach it
1012 to an unmarshalling object. Return both objects.
1014 if use_datetime
and not datetime
:
1015 raise ValueError, "the datetime module is not available"
1016 if FastParser
and FastUnmarshaller
:
1018 mkdatetime
= _datetime_type
1020 mkdatetime
= _datetime
1021 target
= FastUnmarshaller(True, False, _binary
, mkdatetime
, Fault
)
1022 parser
= FastParser(target
)
1024 target
= Unmarshaller(use_datetime
=use_datetime
)
1026 parser
= FastParser(target
)
1028 parser
= ExpatParser(target
)
1030 parser
= SlowParser(target
)
1031 return parser
, target
1034 # Convert a Python tuple or a Fault instance to an XML-RPC packet.
1036 # @def dumps(params, **options)
1037 # @param params A tuple or Fault instance.
1038 # @keyparam methodname If given, create a methodCall request for
1040 # @keyparam methodresponse If given, create a methodResponse packet.
1041 # If used with a tuple, the tuple must be a singleton (that is,
1042 # it must contain exactly one element).
1043 # @keyparam encoding The packet encoding.
1044 # @return A string containing marshalled data.
1046 def dumps(params
, methodname
=None, methodresponse
=None, encoding
=None,
1048 """data [,options] -> marshalled data
1050 Convert an argument tuple or a Fault instance to an XML-RPC
1051 request (or response, if the methodresponse option is used).
1053 In addition to the data object, the following options can be given
1054 as keyword arguments:
1056 methodname: the method name for a methodCall packet
1058 methodresponse: true to create a methodResponse packet.
1059 If this option is used with a tuple, the tuple must be
1060 a singleton (i.e. it can contain only one element).
1062 encoding: the packet encoding (default is UTF-8)
1064 All 8-bit strings in the data structure are assumed to use the
1065 packet encoding. Unicode strings are automatically converted,
1069 assert isinstance(params
, TupleType
) or isinstance(params
, Fault
),\
1070 "argument must be tuple or Fault instance"
1072 if isinstance(params
, Fault
):
1074 elif methodresponse
and isinstance(params
, TupleType
):
1075 assert len(params
) == 1, "response tuple must be a singleton"
1081 m
= FastMarshaller(encoding
)
1083 m
= Marshaller(encoding
, allow_none
)
1085 data
= m
.dumps(params
)
1087 if encoding
!= "utf-8":
1088 xmlheader
= "<?xml version='1.0' encoding='%s'?>\n" % str(encoding
)
1090 xmlheader
= "<?xml version='1.0'?>\n" # utf-8 is default
1092 # standard XML-RPC wrappings
1095 if not isinstance(methodname
, StringType
):
1096 methodname
= methodname
.encode(encoding
)
1100 "<methodName>", methodname
, "</methodName>\n",
1104 elif methodresponse
:
1105 # a method response, or a fault structure
1108 "<methodResponse>\n",
1110 "</methodResponse>\n"
1113 return data
# return as is
1114 return string
.join(data
, "")
1117 # Convert an XML-RPC packet to a Python object. If the XML-RPC packet
1118 # represents a fault condition, this function raises a Fault exception.
1120 # @param data An XML-RPC packet, given as an 8-bit string.
1121 # @return A tuple containing the unpacked data, and the method name
1122 # (None if not present).
1125 def loads(data
, use_datetime
=0):
1126 """data -> unmarshalled data, method name
1128 Convert an XML-RPC packet to unmarshalled data plus a method
1129 name (None if not present).
1131 If the XML-RPC packet represents a fault condition, this function
1132 raises a Fault exception.
1134 p
, u
= getparser(use_datetime
=use_datetime
)
1137 return u
.close(), u
.getmethodname()
1140 # Encode a string using the gzip content encoding such as specified by the
1141 # Content-Encoding: gzip
1142 # in the HTTP header, as described in RFC 1952
1144 # @param data the unencoded data
1145 # @return the encoded data
1147 def gzip_encode(data
):
1148 """data -> gzip encoded data
1150 Encode data using the gzip content encoding as described in RFC 1952
1153 raise NotImplementedError
1154 f
= StringIO
.StringIO()
1155 gzf
= gzip
.GzipFile(mode
="wb", fileobj
=f
, compresslevel
=1)
1158 encoded
= f
.getvalue()
1163 # Decode a string using the gzip content encoding such as specified by the
1164 # Content-Encoding: gzip
1165 # in the HTTP header, as described in RFC 1952
1167 # @param data The encoded data
1168 # @return the unencoded data
1169 # @raises ValueError if data is not correctly coded.
1171 def gzip_decode(data
):
1172 """gzip encoded data -> unencoded data
1174 Decode data using the gzip content encoding as described in RFC 1952
1177 raise NotImplementedError
1178 f
= StringIO
.StringIO(data
)
1179 gzf
= gzip
.GzipFile(mode
="rb", fileobj
=f
)
1181 decoded
= gzf
.read()
1183 raise ValueError("invalid data")
1189 # Return a decoded file-like object for the gzip encoding
1190 # as described in RFC 1952.
1192 # @param response A stream supporting a read() method
1193 # @return a file-like object that the decoded data can be read() from
1195 class GzipDecodedResponse(gzip
.GzipFile
if gzip
else object):
1196 """a file-like object to decode a response encoded with the gzip
1197 method, as described in RFC 1952.
1199 def __init__(self
, response
):
1200 #response doesn't support tell() and read(), required by
1203 raise NotImplementedError
1204 self
.stringio
= StringIO
.StringIO(response
.read())
1205 gzip
.GzipFile
.__init
__(self
, mode
="rb", fileobj
=self
.stringio
)
1208 gzip
.GzipFile
.close(self
)
1209 self
.stringio
.close()
1212 # --------------------------------------------------------------------
1213 # request dispatcher
1216 # some magic to bind an XML-RPC method to an RPC server.
1217 # supports "nested" methods (e.g. examples.getStateName)
1218 def __init__(self
, send
, name
):
1221 def __getattr__(self
, name
):
1222 return _Method(self
.__send
, "%s.%s" % (self
.__name
, name
))
1223 def __call__(self
, *args
):
1224 return self
.__send
(self
.__name
, args
)
1227 # Standard transport class for XML-RPC over HTTP.
1229 # You can create custom transports by subclassing this method, and
1230 # overriding selected methods.
1233 """Handles an HTTP transaction to an XML-RPC server."""
1235 # client identifier (may be overridden)
1236 user_agent
= "xmlrpclib.py/%s (by www.pythonware.com)" % __version__
1238 #if true, we'll request gzip encoding
1239 accept_gzip_encoding
= True
1241 # if positive, encode request using gzip if it exceeds this threshold
1242 # note that many server will get confused, so only use it if you know
1243 # that they can decode such a request
1244 encode_threshold
= None #None = don't encode
1246 def __init__(self
, use_datetime
=0):
1247 self
._use
_datetime
= use_datetime
1248 self
._connection
= (None, None)
1249 self
._extra
_headers
= []
1251 # Send a complete request, and parse the response.
1252 # Retry request if a cached connection has disconnected.
1254 # @param host Target host.
1255 # @param handler Target PRC handler.
1256 # @param request_body XML-RPC request body.
1257 # @param verbose Debugging flag.
1258 # @return Parsed response.
1260 def request(self
, host
, handler
, request_body
, verbose
=0):
1261 #retry request once if cached connection has gone cold
1264 return self
.single_request(host
, handler
, request_body
, verbose
)
1265 except socket
.error
, e
:
1266 if i
or e
.errno
not in (errno
.ECONNRESET
, errno
.ECONNABORTED
):
1268 except httplib
.BadStatusLine
: #close after we sent request
1273 # Send a complete request, and parse the response.
1275 # @param host Target host.
1276 # @param handler Target PRC handler.
1277 # @param request_body XML-RPC request body.
1278 # @param verbose Debugging flag.
1279 # @return Parsed response.
1281 def single_request(self
, host
, handler
, request_body
, verbose
=0):
1282 # issue XML-RPC request
1284 h
= self
.make_connection(host
)
1289 self
.send_request(h
, handler
, request_body
)
1290 self
.send_host(h
, host
)
1291 self
.send_user_agent(h
)
1292 self
.send_content(h
, request_body
)
1294 response
= h
.getresponse(buffering
=True)
1295 if response
.status
== 200:
1296 self
.verbose
= verbose
1297 return self
.parse_response(response
)
1301 # All unexpected errors leave connection in
1302 # a strange state, so we clear it.
1306 #discard any response data and raise exception
1307 if (response
.getheader("content-length", 0)):
1309 raise ProtocolError(
1311 response
.status
, response
.reason
,
1318 # @return A 2-tuple containing a parser and a unmarshaller.
1320 def getparser(self
):
1321 # get parser and unmarshaller
1322 return getparser(use_datetime
=self
._use
_datetime
)
1325 # Get authorization info from host parameter
1326 # Host may be a string, or a (host, x509-dict) tuple; if a string,
1327 # it is checked for a "user:pw@host" format, and a "Basic
1328 # Authentication" header is added if appropriate.
1330 # @param host Host descriptor (URL or (URL, x509 info) tuple).
1331 # @return A 3-tuple containing (actual host, extra headers,
1332 # x509 info). The header and x509 fields may be None.
1334 def get_host_info(self
, host
):
1337 if isinstance(host
, TupleType
):
1341 auth
, host
= urllib
.splituser(host
)
1345 auth
= base64
.encodestring(urllib
.unquote(auth
))
1346 auth
= string
.join(string
.split(auth
), "") # get rid of whitespace
1348 ("Authorization", "Basic " + auth
)
1351 extra_headers
= None
1353 return host
, extra_headers
, x509
1356 # Connect to server.
1358 # @param host Target host.
1359 # @return A connection handle.
1361 def make_connection(self
, host
):
1362 #return an existing connection if possible. This allows
1363 #HTTP/1.1 keep-alive.
1364 if self
._connection
and host
== self
._connection
[0]:
1365 return self
._connection
[1]
1367 # create a HTTP connection object from a host descriptor
1368 chost
, self
._extra
_headers
, x509
= self
.get_host_info(host
)
1369 #store the host argument along with the connection object
1370 self
._connection
= host
, httplib
.HTTPConnection(chost
)
1371 return self
._connection
[1]
1374 # Clear any cached connection object.
1375 # Used in the event of socket errors.
1378 if self
._connection
[1]:
1379 self
._connection
[1].close()
1380 self
._connection
= (None, None)
1383 # Send request header.
1385 # @param connection Connection handle.
1386 # @param handler Target RPC handler.
1387 # @param request_body XML-RPC body.
1389 def send_request(self
, connection
, handler
, request_body
):
1390 if (self
.accept_gzip_encoding
and gzip
):
1391 connection
.putrequest("POST", handler
, skip_accept_encoding
=True)
1392 connection
.putheader("Accept-Encoding", "gzip")
1394 connection
.putrequest("POST", handler
)
1399 # @param connection Connection handle.
1400 # @param host Host name.
1402 # Note: This function doesn't actually add the "Host"
1403 # header anymore, it is done as part of the connection.putrequest() in
1404 # send_request() above.
1406 def send_host(self
, connection
, host
):
1407 extra_headers
= self
._extra
_headers
1409 if isinstance(extra_headers
, DictType
):
1410 extra_headers
= extra_headers
.items()
1411 for key
, value
in extra_headers
:
1412 connection
.putheader(key
, value
)
1415 # Send user-agent identifier.
1417 # @param connection Connection handle.
1419 def send_user_agent(self
, connection
):
1420 connection
.putheader("User-Agent", self
.user_agent
)
1423 # Send request body.
1425 # @param connection Connection handle.
1426 # @param request_body XML-RPC request body.
1428 def send_content(self
, connection
, request_body
):
1429 connection
.putheader("Content-Type", "text/xml")
1431 #optionally encode the request
1432 if (self
.encode_threshold
is not None and
1433 self
.encode_threshold
< len(request_body
) and
1435 connection
.putheader("Content-Encoding", "gzip")
1436 request_body
= gzip_encode(request_body
)
1438 connection
.putheader("Content-Length", str(len(request_body
)))
1439 connection
.endheaders(request_body
)
1444 # @param file Stream.
1445 # @return Response tuple and target method.
1447 def parse_response(self
, response
):
1448 # read response data from httpresponse, and parse it
1449 if response
.getheader("Content-Encoding", "") == "gzip":
1450 stream
= GzipDecodedResponse(response
)
1454 p
, u
= self
.getparser()
1457 data
= stream
.read(1024)
1461 print "body:", repr(data
)
1464 if stream
is not response
:
1471 # Standard transport class for XML-RPC over HTTPS.
1473 class SafeTransport(Transport
):
1474 """Handles an HTTPS transaction to an XML-RPC server."""
1476 # FIXME: mostly untested
1478 def make_connection(self
, host
):
1479 if self
._connection
and host
== self
._connection
[0]:
1480 return self
._connection
[1]
1481 # create a HTTPS connection object from a host descriptor
1482 # host may be a string, or a (host, x509-dict) tuple
1484 HTTPS
= httplib
.HTTPSConnection
1485 except AttributeError:
1486 raise NotImplementedError(
1487 "your version of httplib doesn't support HTTPS"
1490 chost
, self
._extra
_headers
, x509
= self
.get_host_info(host
)
1491 self
._connection
= host
, HTTPS(chost
, None, **(x509
or {}))
1492 return self
._connection
[1]
1495 # Standard server proxy. This class establishes a virtual connection
1496 # to an XML-RPC server.
1498 # This class is available as ServerProxy and Server. New code should
1499 # use ServerProxy, to avoid confusion.
1501 # @def ServerProxy(uri, **options)
1502 # @param uri The connection point on the server.
1503 # @keyparam transport A transport factory, compatible with the
1504 # standard transport class.
1505 # @keyparam encoding The default encoding used for 8-bit strings
1506 # (default is UTF-8).
1507 # @keyparam verbose Use a true value to enable debugging output.
1508 # (printed to standard output).
1512 """uri [,options] -> a logical connection to an XML-RPC server
1514 uri is the connection point on the server, given as
1515 scheme://host/target.
1517 The standard implementation always supports the "http" scheme. If
1518 SSL socket support is available (Python 2.0), it also supports
1521 If the target part and the slash preceding it are both omitted,
1524 The following options can be given as keyword arguments:
1526 transport: a transport factory
1527 encoding: the request encoding (default is UTF-8)
1529 All 8-bit strings passed to the server proxy are assumed to use
1533 def __init__(self
, uri
, transport
=None, encoding
=None, verbose
=0,
1534 allow_none
=0, use_datetime
=0):
1535 # establish a "logical" server connection
1539 type, uri
= urllib
.splittype(uri
)
1540 if type not in ("http", "https"):
1541 raise IOError, "unsupported XML-RPC protocol"
1542 self
.__host
, self
.__handler
= urllib
.splithost(uri
)
1543 if not self
.__handler
:
1544 self
.__handler
= "/RPC2"
1546 if transport
is None:
1548 transport
= SafeTransport(use_datetime
=use_datetime
)
1550 transport
= Transport(use_datetime
=use_datetime
)
1551 self
.__transport
= transport
1553 self
.__encoding
= encoding
1554 self
.__verbose
= verbose
1555 self
.__allow
_none
= allow_none
1558 self
.__transport
.close()
1560 def __request(self
, methodname
, params
):
1561 # call a method on the remote server
1563 request
= dumps(params
, methodname
, encoding
=self
.__encoding
,
1564 allow_none
=self
.__allow
_none
)
1566 response
= self
.__transport
.request(
1570 verbose
=self
.__verbose
1573 if len(response
) == 1:
1574 response
= response
[0]
1580 "<ServerProxy for %s%s>" %
1581 (self
.__host
, self
.__handler
)
1586 def __getattr__(self
, name
):
1587 # magic method dispatcher
1588 return _Method(self
.__request
, name
)
1590 # note: to call a remote object with an non-standard name, use
1591 # result getattr(server, "strange-python-name")(args)
1593 def __call__(self
, attr
):
1594 """A workaround to get special attributes on the ServerProxy
1595 without interfering with the magic __getattr__
1599 elif attr
== "transport":
1600 return self
.__transport
1601 raise AttributeError("Attribute %r not found" % (attr
,))
1605 Server
= ServerProxy
1607 # --------------------------------------------------------------------
1610 if __name__
== "__main__":
1612 # simple test program (from the XML-RPC specification)
1614 # server = ServerProxy("http://localhost:8000") # local server
1615 server
= ServerProxy("http://time.xmlrpc.com/RPC2")
1620 print server
.currentTime
.getCurrentTime()
1624 multi
= MultiCall(server
)
1625 multi
.currentTime
.getCurrentTime()
1626 multi
.currentTime
.getCurrentTime()
1628 for response
in multi():