blob: 2e68a1b33a1faab75fa2a925e44a1adafb6316b0 [file] [log] [blame]
Fredrik Lundhb9056332001-07-11 17:42:21 +00001#
2# XML-RPC CLIENT LIBRARY
3# $Id$
4#
Fredrik Lundhc4c062f2001-09-10 19:45:02 +00005# 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:
Martin v. Löwis37af9862004-08-20 07:31:37 +000011# this version is designed to work with Python 2.1 or newer.
Fredrik Lundhc4c062f2001-09-10 19:45:02 +000012#
Fredrik Lundhb9056332001-07-11 17:42:21 +000013# 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)
Fredrik Lundh78eedce2001-08-23 20:04:33 +000027# 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)
Fredrik Lundhc4c062f2001-09-10 19:45:02 +000029# 2001-09-03 fl Allow Transport subclass to override getparser
30# 2001-09-10 fl Lazy import of urllib, cgi, xmllib (20x import speedup)
Fredrik Lundh1538c232001-10-01 19:42:03 +000031# 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)
Fredrik Lundh3d9addd2002-06-27 21:36:21 +000033# 2001-10-02 fl More dumps microtuning
34# 2001-10-04 fl Make sure import expat gets a parser (from Guido van Rossum)
Skip Montanaro5e9c71b2001-10-10 15:56:34 +000035# 2001-10-10 sm Allow long ints to be passed as ints if they don't overflow
Fredrik Lundh3d9addd2002-06-27 21:36:21 +000036# 2001-10-17 sm Test for int and long overflow (allows use on 64-bit systems)
Fredrik Lundhb6ab93f2001-12-19 21:40:04 +000037# 2001-11-12 fl Use repr() to marshal doubles (from Paul Felix)
Fredrik Lundh3d9addd2002-06-27 21:36:21 +000038# 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
Fredrik Lundh1303c7c2002-10-22 18:23:00 +000043# 2002-10-22 fl Added basic authentication (based on code from Phillip Eby)
Martin v. Löwis541342f2003-07-12 07:53:04 +000044# 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
Martin v. Löwis45394c22003-10-31 13:49:36 +000050# 2003-10-31 mvl Add multicall support
Martin v. Löwis37af9862004-08-20 07:31:37 +000051# 2004-08-20 mvl Bump minimum supported Python version to 2.1
Fredrik Lundhb9056332001-07-11 17:42:21 +000052#
Fredrik Lundh3d9addd2002-06-27 21:36:21 +000053# Copyright (c) 1999-2002 by Secret Labs AB.
54# Copyright (c) 1999-2002 by Fredrik Lundh.
Fredrik Lundhb9056332001-07-11 17:42:21 +000055#
56# info@pythonware.com
57# http://www.pythonware.com
58#
59# --------------------------------------------------------------------
60# The XML-RPC client interface is
61#
Fredrik Lundh3d9addd2002-06-27 21:36:21 +000062# Copyright (c) 1999-2002 by Secret Labs AB
63# Copyright (c) 1999-2002 by Fredrik Lundh
Fredrik Lundhb9056332001-07-11 17:42:21 +000064#
65# By obtaining, using, and/or copying this software and/or its
66# associated documentation, you agree that you have read, understood,
67# and will comply with the following terms and conditions:
68#
69# Permission to use, copy, modify, and distribute this software and
70# its associated documentation for any purpose and without fee is
71# hereby granted, provided that the above copyright notice appears in
72# all copies, and that both that copyright notice and this permission
73# notice appear in supporting documentation, and that the name of
74# Secret Labs AB or the author not be used in advertising or publicity
75# pertaining to distribution of the software without specific, written
76# prior permission.
77#
78# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
79# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
80# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR
81# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
82# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
83# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
84# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
85# OF THIS SOFTWARE.
86# --------------------------------------------------------------------
87
88#
Fredrik Lundh3d9addd2002-06-27 21:36:21 +000089# things to look into some day:
Fredrik Lundhb9056332001-07-11 17:42:21 +000090
Fredrik Lundh3d9addd2002-06-27 21:36:21 +000091# TODO: sort out True/False/boolean issues for Python 2.3
Fredrik Lundhb9056332001-07-11 17:42:21 +000092
Fred Drake1b410792001-09-04 18:55:03 +000093"""
94An XML-RPC client interface for Python.
95
96The marshalling and response parser code can also be used to
97implement XML-RPC servers.
98
Fred Drake1b410792001-09-04 18:55:03 +000099Exported exceptions:
100
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000101 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
Fred Drake1b410792001-09-04 18:55:03 +0000105
106Exported classes:
107
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000108 ServerProxy Represents a logical connection to an XML-RPC server
Fred Drake1b410792001-09-04 18:55:03 +0000109
Raymond Hettingercc523fc2003-11-02 09:47:05 +0000110 MultiCall Executor of boxcared xmlrpc requests
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000111 Boolean boolean wrapper to generate a "boolean" XML-RPC value
112 DateTime dateTime wrapper for an ISO 8601 string or time tuple or
113 localtime integer value to generate a "dateTime.iso8601"
114 XML-RPC value
115 Binary binary data wrapper
Fred Drake1b410792001-09-04 18:55:03 +0000116
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000117 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
Fred Drake1b410792001-09-04 18:55:03 +0000122
123Exported constants:
124
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000125 True
126 False
Fred Drake1b410792001-09-04 18:55:03 +0000127
128Exported functions:
129
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000130 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).
Fred Drake1b410792001-09-04 18:55:03 +0000137"""
138
Neal Norwitzff113342007-04-17 08:42:15 +0000139import re, time, operator
Jeremy Hylton5d8a88a2007-08-14 16:47:39 +0000140import httplib
Fredrik Lundh1538c232001-10-01 19:42:03 +0000141
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000142# --------------------------------------------------------------------
143# Internal stuff
144
Fredrik Lundhb9056332001-07-11 17:42:21 +0000145try:
Fred Drakeba613c32005-02-10 18:33:30 +0000146 import datetime
147except ImportError:
148 datetime = None
149
150try:
Skip Montanaro9a7c96a2003-01-22 18:17:25 +0000151 _bool_is_builtin = False.__class__.__name__ == "bool"
152except NameError:
153 _bool_is_builtin = 0
154
Fredrik Lundhb9056332001-07-11 17:42:21 +0000155def _decode(data, encoding, is8bit=re.compile("[\x80-\xff]").search):
156 # decode non-ascii string (if possible)
Guido van Rossum54ad5232007-05-27 09:17:48 +0000157 if encoding and is8bit(data):
Guido van Rossumef87d6e2007-05-02 19:09:54 +0000158 data = str(data, encoding)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000159 return data
160
Neal Norwitzff113342007-04-17 08:42:15 +0000161def escape(s):
162 s = s.replace("&", "&")
163 s = s.replace("<", "&lt;")
164 return s.replace(">", "&gt;",)
Fredrik Lundh1538c232001-10-01 19:42:03 +0000165
Guido van Rossum54ad5232007-05-27 09:17:48 +0000166def _stringify(string):
167 # convert to 7-bit ascii if possible
168 try:
Brett Cannon2dbde5e2007-07-30 03:50:35 +0000169 return string.decode("ascii")
Guido van Rossum261f9df2007-08-31 14:07:27 +0000170 except (UnicodeError, TypeError, AttributeError):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000171 return string
172
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000173__version__ = "1.0.1"
174
175# xmlrpc integer limits
Guido van Rossume2a383d2007-01-15 16:59:06 +0000176MAXINT = 2**31-1
177MININT = -2**31
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000178
179# --------------------------------------------------------------------
180# Error constants (from Dan Libby's specification at
181# http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php)
182
183# Ranges of errors
184PARSE_ERROR = -32700
185SERVER_ERROR = -32600
186APPLICATION_ERROR = -32500
187SYSTEM_ERROR = -32400
188TRANSPORT_ERROR = -32300
189
190# Specific errors
191NOT_WELLFORMED_ERROR = -32700
192UNSUPPORTED_ENCODING = -32701
193INVALID_ENCODING_CHAR = -32702
194INVALID_XMLRPC = -32600
195METHOD_NOT_FOUND = -32601
196INVALID_METHOD_PARAMS = -32602
197INTERNAL_ERROR = -32603
Fredrik Lundhb9056332001-07-11 17:42:21 +0000198
199# --------------------------------------------------------------------
200# Exceptions
201
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000202##
203# Base class for all kinds of client-side errors.
204
Fredrik Lundh78eedce2001-08-23 20:04:33 +0000205class Error(Exception):
Fred Drake1b410792001-09-04 18:55:03 +0000206 """Base class for client errors."""
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000207 def __str__(self):
208 return repr(self)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000209
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000210##
211# Indicates an HTTP-level protocol error. This is raised by the HTTP
212# transport layer, if the server returns an error code other than 200
213# (OK).
214#
215# @param url The target URL.
216# @param errcode The HTTP error code.
217# @param errmsg The HTTP error message.
218# @param headers The HTTP header dictionary.
219
Fredrik Lundhb9056332001-07-11 17:42:21 +0000220class ProtocolError(Error):
Fred Drake1b410792001-09-04 18:55:03 +0000221 """Indicates an HTTP protocol error."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000222 def __init__(self, url, errcode, errmsg, headers):
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000223 Error.__init__(self)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000224 self.url = url
225 self.errcode = errcode
226 self.errmsg = errmsg
227 self.headers = headers
228 def __repr__(self):
229 return (
230 "<ProtocolError for %s: %s %s>" %
231 (self.url, self.errcode, self.errmsg)
232 )
233
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000234##
235# Indicates a broken XML-RPC response package. This exception is
236# raised by the unmarshalling layer, if the XML-RPC response is
237# malformed.
238
Fredrik Lundhb9056332001-07-11 17:42:21 +0000239class ResponseError(Error):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000240 """Indicates a broken response package."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000241 pass
242
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000243##
244# Indicates an XML-RPC fault response package. This exception is
245# raised by the unmarshalling layer, if the XML-RPC response contains
246# a fault string. This exception can also used as a class, to
247# generate a fault XML-RPC message.
248#
249# @param faultCode The XML-RPC fault code.
250# @param faultString The XML-RPC fault string.
251
Fredrik Lundhb9056332001-07-11 17:42:21 +0000252class Fault(Error):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000253 """Indicates an XML-RPC fault package."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000254 def __init__(self, faultCode, faultString, **extra):
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000255 Error.__init__(self)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000256 self.faultCode = faultCode
257 self.faultString = faultString
258 def __repr__(self):
259 return (
260 "<Fault %s: %s>" %
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000261 (self.faultCode, repr(self.faultString))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000262 )
263
264# --------------------------------------------------------------------
265# Special values
266
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000267##
268# Wrapper for XML-RPC boolean values. Use the xmlrpclib.True and
269# xmlrpclib.False constants, or the xmlrpclib.boolean() function, to
270# generate boolean XML-RPC values.
271#
272# @param value A boolean value. Any true value is interpreted as True,
273# all other values are interpreted as False.
274
Guido van Rossum47b9ff62006-08-24 00:41:19 +0000275boolean = Boolean = bool
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000276
277##
278# Wrapper for XML-RPC DateTime values. This converts a time value to
279# the format used by XML-RPC.
280# <p>
281# The value can be given as a string in the format
282# "yyyymmddThh:mm:ss", as a 9-item time tuple (as returned by
283# time.localtime()), or an integer value (as returned by time.time()).
284# The wrapper uses time.localtime() to convert an integer to a time
285# tuple.
286#
287# @param value The time, given as an ISO 8601 string, a time
288# tuple, or a integer time value.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000289
Fredrik Lundhb9056332001-07-11 17:42:21 +0000290class DateTime:
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000291 """DateTime wrapper for an ISO 8601 string or time tuple or
292 localtime integer value to generate 'dateTime.iso8601' XML-RPC
293 value.
294 """
Fredrik Lundhb9056332001-07-11 17:42:21 +0000295
296 def __init__(self, value=0):
Guido van Rossum3172c5d2007-10-16 18:12:55 +0000297 if not isinstance(value, str):
Fred Drakeba613c32005-02-10 18:33:30 +0000298 if datetime and isinstance(value, datetime.datetime):
299 self.value = value.strftime("%Y%m%dT%H:%M:%S")
300 return
Guido van Rossum13257902007-06-07 23:15:56 +0000301 if not isinstance(value, (tuple, time.struct_time)):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000302 if value == 0:
303 value = time.time()
304 value = time.localtime(value)
305 value = time.strftime("%Y%m%dT%H:%M:%S", value)
306 self.value = value
307
Christian Heimes05e8be12008-02-23 18:30:17 +0000308 def make_comparable(self, other):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000309 if isinstance(other, DateTime):
Christian Heimes05e8be12008-02-23 18:30:17 +0000310 s = self.value
311 o = other.value
312 elif datetime and isinstance(other, datetime.datetime):
313 s = self.value
314 o = other.strftime("%Y%m%dT%H:%M:%S")
315 elif isinstance(other, (str, unicode)):
316 s = self.value
317 o = other
318 elif hasattr(other, "timetuple"):
319 s = self.timetuple()
320 o = other.timetuple()
321 else:
322 otype = (hasattr(other, "__class__")
323 and other.__class__.__name__
324 or type(other))
325 raise TypeError("Can't compare %s and %s" %
326 (self.__class__.__name__, otype))
327 return s, o
328
329 def __lt__(self, other):
330 s, o = self.make_comparable(other)
331 return s < o
332
333 def __le__(self, other):
334 s, o = self.make_comparable(other)
335 return s <= o
336
337 def __gt__(self, other):
338 s, o = self.make_comparable(other)
339 return s > o
340
341 def __ge__(self, other):
342 s, o = self.make_comparable(other)
343 return s >= o
344
345 def __eq__(self, other):
346 s, o = self.make_comparable(other)
347 return s == o
Guido van Rossum47b9ff62006-08-24 00:41:19 +0000348
349 def __ne__(self, other):
Christian Heimes05e8be12008-02-23 18:30:17 +0000350 s, o = self.make_comparable(other)
351 return s != o
352
353 def timetuple(self):
354 return time.strptime(self.value, "%Y%m%dT%H:%M:%S")
355
356 def __cmp__(self, other):
357 s, o = self.make_comparable(other)
358 return cmp(s, o)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000359
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000360 ##
361 # Get date/time value.
362 #
363 # @return Date/time value, as an ISO 8601 string.
364
365 def __str__(self):
366 return self.value
367
Fredrik Lundhb9056332001-07-11 17:42:21 +0000368 def __repr__(self):
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000369 return "<DateTime %s at %x>" % (repr(self.value), id(self))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000370
371 def decode(self, data):
Neal Norwitzff113342007-04-17 08:42:15 +0000372 self.value = str(data).strip()
Fredrik Lundhb9056332001-07-11 17:42:21 +0000373
374 def encode(self, out):
375 out.write("<value><dateTime.iso8601>")
376 out.write(self.value)
377 out.write("</dateTime.iso8601></value>\n")
378
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000379def _datetime(data):
380 # decode xml element contents into a DateTime structure.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000381 value = DateTime()
382 value.decode(data)
383 return value
384
Skip Montanaro174dd222005-05-14 20:54:16 +0000385def _datetime_type(data):
386 t = time.strptime(data, "%Y%m%dT%H:%M:%S")
387 return datetime.datetime(*tuple(t)[:6])
388
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000389##
390# Wrapper for binary data. This can be used to transport any kind
391# of binary data over XML-RPC, using BASE64 encoding.
392#
393# @param data An 8-bit string containing arbitrary data.
394
Skip Montanarobfcbfa72003-04-24 19:51:31 +0000395import base64
Guido van Rossum68937b42007-05-18 00:51:22 +0000396import io
Skip Montanarobfcbfa72003-04-24 19:51:31 +0000397
Fredrik Lundhb9056332001-07-11 17:42:21 +0000398class Binary:
Fred Drake1b410792001-09-04 18:55:03 +0000399 """Wrapper for binary data."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000400
401 def __init__(self, data=None):
Guido van Rossum54a40cb2007-08-27 22:27:41 +0000402 if data is None:
403 data = b""
404 else:
405 if not isinstance(data, bytes):
406 raise TypeError("expected bytes, not %s" %
407 data.__class__.__name__)
408 data = bytes(data) # Make a copy of the bytes!
Fredrik Lundhb9056332001-07-11 17:42:21 +0000409 self.data = data
410
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000411 ##
412 # Get buffer contents.
413 #
414 # @return Buffer contents, as an 8-bit string.
415
416 def __str__(self):
Guido van Rossum54a40cb2007-08-27 22:27:41 +0000417 return str(self.data, "latin-1") # XXX encoding?!
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000418
Guido van Rossum47b9ff62006-08-24 00:41:19 +0000419 def __eq__(self, other):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000420 if isinstance(other, Binary):
421 other = other.data
Guido van Rossum47b9ff62006-08-24 00:41:19 +0000422 return self.data == other
423
424 def __ne__(self, other):
425 if isinstance(other, Binary):
426 other = other.data
427 return self.data != other
Fredrik Lundhb9056332001-07-11 17:42:21 +0000428
429 def decode(self, data):
Guido van Rossum54a40cb2007-08-27 22:27:41 +0000430 self.data = base64.decodestring(data)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000431
432 def encode(self, out):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000433 out.write("<value><base64>\n")
Brett Cannon2dbde5e2007-07-30 03:50:35 +0000434 encoded = base64.encodestring(self.data)
435 out.write(encoded.decode('ascii'))
436 out.write('\n')
Fredrik Lundhb9056332001-07-11 17:42:21 +0000437 out.write("</base64></value>\n")
438
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000439def _binary(data):
440 # decode xml element contents into a Binary structure
Fredrik Lundhb9056332001-07-11 17:42:21 +0000441 value = Binary()
442 value.decode(data)
443 return value
444
Skip Montanaro9a7c96a2003-01-22 18:17:25 +0000445WRAPPERS = (DateTime, Binary)
446if not _bool_is_builtin:
447 WRAPPERS = WRAPPERS + (Boolean,)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000448
449# --------------------------------------------------------------------
450# XML parsers
451
452try:
Fredrik Lundh7069c312004-10-13 06:48:37 +0000453 # optional xmlrpclib accelerator
Fredrik Lundhb9056332001-07-11 17:42:21 +0000454 import _xmlrpclib
455 FastParser = _xmlrpclib.Parser
456 FastUnmarshaller = _xmlrpclib.Unmarshaller
457except (AttributeError, ImportError):
458 FastParser = FastUnmarshaller = None
459
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000460try:
461 import _xmlrpclib
462 FastMarshaller = _xmlrpclib.Marshaller
463except (AttributeError, ImportError):
464 FastMarshaller = None
465
Fredrik Lundhb9056332001-07-11 17:42:21 +0000466#
467# the SGMLOP parser is about 15x faster than Python's builtin
468# XML parser. SGMLOP sources can be downloaded from:
469#
470# http://www.pythonware.com/products/xml/sgmlop.htm
471#
472
473try:
474 import sgmlop
475 if not hasattr(sgmlop, "XMLParser"):
476 raise ImportError
477except ImportError:
478 SgmlopParser = None # sgmlop accelerator not available
479else:
480 class SgmlopParser:
481 def __init__(self, target):
482
483 # setup callbacks
484 self.finish_starttag = target.start
485 self.finish_endtag = target.end
486 self.handle_data = target.data
487 self.handle_xml = target.xml
488
489 # activate parser
490 self.parser = sgmlop.XMLParser()
491 self.parser.register(self)
492 self.feed = self.parser.feed
493 self.entity = {
494 "amp": "&", "gt": ">", "lt": "<",
495 "apos": "'", "quot": '"'
496 }
497
498 def close(self):
499 try:
500 self.parser.close()
501 finally:
502 self.parser = self.feed = None # nuke circular reference
503
504 def handle_proc(self, tag, attr):
505 m = re.search("encoding\s*=\s*['\"]([^\"']+)[\"']", attr)
506 if m:
507 self.handle_xml(m.group(1), 1)
508
509 def handle_entityref(self, entity):
510 # <string> entity
511 try:
512 self.handle_data(self.entity[entity])
513 except KeyError:
514 self.handle_data("&%s;" % entity)
515
516try:
517 from xml.parsers import expat
Guido van Rossumb8551342001-10-02 18:33:11 +0000518 if not hasattr(expat, "ParserCreate"):
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000519 raise ImportError
Fredrik Lundhb9056332001-07-11 17:42:21 +0000520except ImportError:
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000521 ExpatParser = None # expat not available
Fredrik Lundhb9056332001-07-11 17:42:21 +0000522else:
523 class ExpatParser:
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000524 # fast expat parser for Python 2.0 and later. this is about
525 # 50% slower than sgmlop, on roundtrip testing
Fredrik Lundhb9056332001-07-11 17:42:21 +0000526 def __init__(self, target):
527 self._parser = parser = expat.ParserCreate(None, None)
528 self._target = target
529 parser.StartElementHandler = target.start
530 parser.EndElementHandler = target.end
531 parser.CharacterDataHandler = target.data
532 encoding = None
Fredrik Lundhb9056332001-07-11 17:42:21 +0000533 target.xml(encoding, None)
534
535 def feed(self, data):
536 self._parser.Parse(data, 0)
537
538 def close(self):
539 self._parser.Parse("", 1) # end of data
540 del self._target, self._parser # get rid of circular references
541
Fredrik Lundhb9056332001-07-11 17:42:21 +0000542# --------------------------------------------------------------------
543# XML-RPC marshalling and unmarshalling code
544
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000545##
546# XML-RPC marshaller.
547#
548# @param encoding Default encoding for 8-bit strings. The default
549# value is None (interpreted as UTF-8).
550# @see dumps
551
Fredrik Lundhb9056332001-07-11 17:42:21 +0000552class Marshaller:
Fred Drake1b410792001-09-04 18:55:03 +0000553 """Generate an XML-RPC params chunk from a Python data structure.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000554
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000555 Create a Marshaller instance for each set of parameters, and use
556 the "dumps" method to convert your data (represented as a tuple)
557 to an XML-RPC params chunk. To write a fault response, pass a
558 Fault instance instead. You may prefer to use the "dumps" module
559 function for this purpose.
Fred Drake1b410792001-09-04 18:55:03 +0000560 """
Fredrik Lundhb9056332001-07-11 17:42:21 +0000561
562 # by the way, if you don't understand what's going on in here,
563 # that's perfectly ok.
564
Andrew M. Kuchlinga4c2b742003-04-25 00:26:51 +0000565 def __init__(self, encoding=None, allow_none=0):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000566 self.memo = {}
567 self.data = None
568 self.encoding = encoding
Andrew M. Kuchlinga4c2b742003-04-25 00:26:51 +0000569 self.allow_none = allow_none
Tim Petersc2659cf2003-05-12 20:19:37 +0000570
Fredrik Lundhb9056332001-07-11 17:42:21 +0000571 dispatch = {}
572
573 def dumps(self, values):
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000574 out = []
575 write = out.append
576 dump = self.__dump
Fredrik Lundhb9056332001-07-11 17:42:21 +0000577 if isinstance(values, Fault):
578 # fault instance
579 write("<fault>\n")
Martin v. Löwis541342f2003-07-12 07:53:04 +0000580 dump({'faultCode': values.faultCode,
581 'faultString': values.faultString},
582 write)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000583 write("</fault>\n")
584 else:
585 # parameter block
Fredrik Lundhc266bb02001-08-23 20:13:08 +0000586 # FIXME: the xml-rpc specification allows us to leave out
587 # the entire <params> block if there are no parameters.
588 # however, changing this may break older code (including
589 # old versions of xmlrpclib.py), so this is better left as
590 # is for now. See @XMLRPC3 for more information. /F
Fredrik Lundhb9056332001-07-11 17:42:21 +0000591 write("<params>\n")
592 for v in values:
593 write("<param>\n")
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000594 dump(v, write)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000595 write("</param>\n")
596 write("</params>\n")
Neal Norwitzff113342007-04-17 08:42:15 +0000597 result = "".join(out)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000598 return result
599
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000600 def __dump(self, value, write):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000601 try:
602 f = self.dispatch[type(value)]
603 except KeyError:
Thomas Wouters89f507f2006-12-13 04:49:30 +0000604 # check if this object can be marshalled as a structure
605 try:
606 value.__dict__
607 except:
Collin Winterce36ad82007-08-30 01:19:48 +0000608 raise TypeError("cannot marshal %s objects" % type(value))
Thomas Wouters89f507f2006-12-13 04:49:30 +0000609 # check if this class is a sub-class of a basic type,
610 # because we don't know how to marshal these types
611 # (e.g. a string sub-class)
612 for type_ in type(value).__mro__:
613 if type_ in self.dispatch.keys():
Collin Winterce36ad82007-08-30 01:19:48 +0000614 raise TypeError("cannot marshal %s objects" % type(value))
Thomas Wouters89f507f2006-12-13 04:49:30 +0000615 # XXX(twouters): using "_arbitrary_instance" as key as a quick-fix
616 # for the p3yk merge, this should probably be fixed more neatly.
617 f = self.dispatch["_arbitrary_instance"]
618 f(self, value, write)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000619
Andrew M. Kuchlinga4c2b742003-04-25 00:26:51 +0000620 def dump_nil (self, value, write):
621 if not self.allow_none:
Collin Winterce36ad82007-08-30 01:19:48 +0000622 raise TypeError("cannot marshal None unless allow_none is enabled")
Andrew M. Kuchlinga4c2b742003-04-25 00:26:51 +0000623 write("<value><nil/></value>")
Guido van Rossum13257902007-06-07 23:15:56 +0000624 dispatch[type(None)] = dump_nil
Tim Petersc2659cf2003-05-12 20:19:37 +0000625
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000626 def dump_int(self, value, write):
Skip Montanaro5449e082001-10-17 22:53:33 +0000627 # in case ints are > 32 bits
628 if value > MAXINT or value < MININT:
Collin Winterce36ad82007-08-30 01:19:48 +0000629 raise OverflowError("int exceeds XML-RPC limits")
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000630 write("<value><int>")
631 write(str(value))
632 write("</int></value>\n")
Guido van Rossum13257902007-06-07 23:15:56 +0000633 #dispatch[int] = dump_int
Fredrik Lundhb9056332001-07-11 17:42:21 +0000634
Skip Montanaro9a7c96a2003-01-22 18:17:25 +0000635 if _bool_is_builtin:
636 def dump_bool(self, value, write):
637 write("<value><boolean>")
638 write(value and "1" or "0")
639 write("</boolean></value>\n")
640 dispatch[bool] = dump_bool
641
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000642 def dump_long(self, value, write):
Skip Montanaro5449e082001-10-17 22:53:33 +0000643 if value > MAXINT or value < MININT:
Collin Winterce36ad82007-08-30 01:19:48 +0000644 raise OverflowError("long int exceeds XML-RPC limits")
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000645 write("<value><int>")
646 write(str(int(value)))
647 write("</int></value>\n")
Guido van Rossum13257902007-06-07 23:15:56 +0000648 dispatch[int] = dump_long
Skip Montanaro5e9c71b2001-10-10 15:56:34 +0000649
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000650 def dump_double(self, value, write):
651 write("<value><double>")
652 write(repr(value))
653 write("</double></value>\n")
Guido van Rossum13257902007-06-07 23:15:56 +0000654 dispatch[float] = dump_double
Fredrik Lundhb9056332001-07-11 17:42:21 +0000655
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000656 def dump_string(self, value, write, escape=escape):
657 write("<value><string>")
658 write(escape(value))
659 write("</string></value>\n")
Guido van Rossum98297ee2007-11-06 21:34:58 +0000660 dispatch[bytes] = dump_string
Fredrik Lundhb9056332001-07-11 17:42:21 +0000661
Guido van Rossum54ad5232007-05-27 09:17:48 +0000662 def dump_unicode(self, value, write, escape=escape):
Guido van Rossum54ad5232007-05-27 09:17:48 +0000663 write("<value><string>")
664 write(escape(value))
665 write("</string></value>\n")
666 dispatch[str] = dump_unicode
Fredrik Lundhb9056332001-07-11 17:42:21 +0000667
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000668 def dump_array(self, value, write):
669 i = id(value)
Guido van Rossume2b70bc2006-08-18 22:13:04 +0000670 if i in self.memo:
Collin Winterce36ad82007-08-30 01:19:48 +0000671 raise TypeError("cannot marshal recursive sequences")
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000672 self.memo[i] = None
Fredrik Lundh1538c232001-10-01 19:42:03 +0000673 dump = self.__dump
Fredrik Lundhb9056332001-07-11 17:42:21 +0000674 write("<value><array><data>\n")
675 for v in value:
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000676 dump(v, write)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000677 write("</data></array></value>\n")
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000678 del self.memo[i]
Guido van Rossum13257902007-06-07 23:15:56 +0000679 dispatch[tuple] = dump_array
680 dispatch[list] = dump_array
Fredrik Lundhb9056332001-07-11 17:42:21 +0000681
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000682 def dump_struct(self, value, write, escape=escape):
683 i = id(value)
Guido van Rossume2b70bc2006-08-18 22:13:04 +0000684 if i in self.memo:
Collin Winterce36ad82007-08-30 01:19:48 +0000685 raise TypeError("cannot marshal recursive dictionaries")
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000686 self.memo[i] = None
Fredrik Lundh1538c232001-10-01 19:42:03 +0000687 dump = self.__dump
Fredrik Lundhb9056332001-07-11 17:42:21 +0000688 write("<value><struct>\n")
Andrew M. Kuchling5962f452004-06-05 12:35:58 +0000689 for k, v in value.items():
Fredrik Lundhb9056332001-07-11 17:42:21 +0000690 write("<member>\n")
Guido van Rossum3172c5d2007-10-16 18:12:55 +0000691 if not isinstance(k, str):
Collin Winterce36ad82007-08-30 01:19:48 +0000692 raise TypeError("dictionary key must be string")
Fredrik Lundh1538c232001-10-01 19:42:03 +0000693 write("<name>%s</name>\n" % escape(k))
Andrew M. Kuchling5962f452004-06-05 12:35:58 +0000694 dump(v, write)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000695 write("</member>\n")
696 write("</struct></value>\n")
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000697 del self.memo[i]
Guido van Rossum13257902007-06-07 23:15:56 +0000698 dispatch[dict] = dump_struct
Fredrik Lundhb9056332001-07-11 17:42:21 +0000699
Fred Drakeba613c32005-02-10 18:33:30 +0000700 if datetime:
701 def dump_datetime(self, value, write):
702 write("<value><dateTime.iso8601>")
703 write(value.strftime("%Y%m%dT%H:%M:%S"))
704 write("</dateTime.iso8601></value>\n")
705 dispatch[datetime.datetime] = dump_datetime
706
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000707 def dump_instance(self, value, write):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000708 # check for special wrappers
709 if value.__class__ in WRAPPERS:
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000710 self.write = write
Fredrik Lundhb9056332001-07-11 17:42:21 +0000711 value.encode(self)
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000712 del self.write
Fredrik Lundhb9056332001-07-11 17:42:21 +0000713 else:
714 # store instance attributes as a struct (really?)
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000715 self.dump_struct(value.__dict__, write)
Guido van Rossume4dea982006-04-21 13:45:00 +0000716 dispatch[DateTime] = dump_instance
717 dispatch[Binary] = dump_instance
Thomas Wouters89f507f2006-12-13 04:49:30 +0000718 # XXX(twouters): using "_arbitrary_instance" as key as a quick-fix
719 # for the p3yk merge, this should probably be fixed more neatly.
720 dispatch["_arbitrary_instance"] = dump_instance
Fredrik Lundhb9056332001-07-11 17:42:21 +0000721
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000722##
723# XML-RPC unmarshaller.
724#
725# @see loads
726
Fredrik Lundhb9056332001-07-11 17:42:21 +0000727class Unmarshaller:
Fred Drake1b410792001-09-04 18:55:03 +0000728 """Unmarshal an XML-RPC response, based on incoming XML event
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000729 messages (start, data, end). Call close() to get the resulting
Fred Drake1b410792001-09-04 18:55:03 +0000730 data structure.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000731
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000732 Note that this reader is fairly tolerant, and gladly accepts bogus
733 XML-RPC data without complaining (but not bogus XML).
Fred Drake1b410792001-09-04 18:55:03 +0000734 """
Fredrik Lundhb9056332001-07-11 17:42:21 +0000735
736 # and again, if you don't understand what's going on in here,
737 # that's perfectly ok.
738
Skip Montanaro174dd222005-05-14 20:54:16 +0000739 def __init__(self, use_datetime=0):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000740 self._type = None
741 self._stack = []
742 self._marks = []
743 self._data = []
744 self._methodname = None
745 self._encoding = "utf-8"
746 self.append = self._stack.append
Skip Montanaro174dd222005-05-14 20:54:16 +0000747 self._use_datetime = use_datetime
748 if use_datetime and not datetime:
Collin Winterce36ad82007-08-30 01:19:48 +0000749 raise ValueError("the datetime module is not available")
Fredrik Lundhb9056332001-07-11 17:42:21 +0000750
751 def close(self):
752 # return response tuple and target method
753 if self._type is None or self._marks:
754 raise ResponseError()
755 if self._type == "fault":
Guido van Rossum68468eb2003-02-27 20:14:51 +0000756 raise Fault(**self._stack[0])
Fredrik Lundhb9056332001-07-11 17:42:21 +0000757 return tuple(self._stack)
758
759 def getmethodname(self):
760 return self._methodname
761
762 #
763 # event handlers
764
765 def xml(self, encoding, standalone):
766 self._encoding = encoding
767 # FIXME: assert standalone == 1 ???
768
769 def start(self, tag, attrs):
770 # prepare to handle this element
771 if tag == "array" or tag == "struct":
772 self._marks.append(len(self._stack))
773 self._data = []
774 self._value = (tag == "value")
775
776 def data(self, text):
777 self._data.append(text)
778
Neal Norwitzff113342007-04-17 08:42:15 +0000779 def end(self, tag):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000780 # call the appropriate end tag handler
781 try:
782 f = self.dispatch[tag]
783 except KeyError:
784 pass # unknown tag ?
785 else:
Neal Norwitzff113342007-04-17 08:42:15 +0000786 return f(self, "".join(self._data))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000787
788 #
789 # accelerator support
790
791 def end_dispatch(self, tag, data):
792 # dispatch data
793 try:
794 f = self.dispatch[tag]
795 except KeyError:
796 pass # unknown tag ?
797 else:
798 return f(self, data)
799
800 #
801 # element decoders
802
803 dispatch = {}
804
Andrew M. Kuchlinga4c2b742003-04-25 00:26:51 +0000805 def end_nil (self, data):
806 self.append(None)
807 self._value = 0
808 dispatch["nil"] = end_nil
Tim Petersc2659cf2003-05-12 20:19:37 +0000809
Fredrik Lundh1538c232001-10-01 19:42:03 +0000810 def end_boolean(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000811 if data == "0":
812 self.append(False)
813 elif data == "1":
814 self.append(True)
815 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000816 raise TypeError("bad boolean value")
Fredrik Lundhb9056332001-07-11 17:42:21 +0000817 self._value = 0
818 dispatch["boolean"] = end_boolean
819
Fredrik Lundh1538c232001-10-01 19:42:03 +0000820 def end_int(self, data):
821 self.append(int(data))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000822 self._value = 0
823 dispatch["i4"] = end_int
824 dispatch["int"] = end_int
825
Fredrik Lundh1538c232001-10-01 19:42:03 +0000826 def end_double(self, data):
827 self.append(float(data))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000828 self._value = 0
829 dispatch["double"] = end_double
830
Fredrik Lundh1538c232001-10-01 19:42:03 +0000831 def end_string(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000832 if self._encoding:
833 data = _decode(data, self._encoding)
834 self.append(_stringify(data))
835 self._value = 0
836 dispatch["string"] = end_string
837 dispatch["name"] = end_string # struct keys are always strings
838
839 def end_array(self, data):
Raymond Hettinger46ac8eb2002-06-30 03:39:14 +0000840 mark = self._marks.pop()
Fredrik Lundhb9056332001-07-11 17:42:21 +0000841 # map arrays to Python lists
842 self._stack[mark:] = [self._stack[mark:]]
843 self._value = 0
844 dispatch["array"] = end_array
845
846 def end_struct(self, data):
Raymond Hettinger46ac8eb2002-06-30 03:39:14 +0000847 mark = self._marks.pop()
Fredrik Lundhb9056332001-07-11 17:42:21 +0000848 # map structs to Python dictionaries
849 dict = {}
850 items = self._stack[mark:]
851 for i in range(0, len(items), 2):
852 dict[_stringify(items[i])] = items[i+1]
853 self._stack[mark:] = [dict]
854 self._value = 0
855 dispatch["struct"] = end_struct
856
Fredrik Lundh1538c232001-10-01 19:42:03 +0000857 def end_base64(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000858 value = Binary()
Guido van Rossum54a40cb2007-08-27 22:27:41 +0000859 value.decode(data.encode("ascii"))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000860 self.append(value)
861 self._value = 0
862 dispatch["base64"] = end_base64
863
Fredrik Lundh1538c232001-10-01 19:42:03 +0000864 def end_dateTime(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000865 value = DateTime()
Fredrik Lundh1538c232001-10-01 19:42:03 +0000866 value.decode(data)
Skip Montanaro174dd222005-05-14 20:54:16 +0000867 if self._use_datetime:
868 value = _datetime_type(data)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000869 self.append(value)
870 dispatch["dateTime.iso8601"] = end_dateTime
871
872 def end_value(self, data):
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000873 # if we stumble upon a value element with no internal
Fredrik Lundhb9056332001-07-11 17:42:21 +0000874 # elements, treat it as a string element
875 if self._value:
876 self.end_string(data)
877 dispatch["value"] = end_value
878
879 def end_params(self, data):
880 self._type = "params"
881 dispatch["params"] = end_params
882
883 def end_fault(self, data):
884 self._type = "fault"
885 dispatch["fault"] = end_fault
886
Fredrik Lundh1538c232001-10-01 19:42:03 +0000887 def end_methodName(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000888 if self._encoding:
889 data = _decode(data, self._encoding)
890 self._methodname = data
891 self._type = "methodName" # no params
892 dispatch["methodName"] = end_methodName
893
Martin v. Löwis45394c22003-10-31 13:49:36 +0000894## Multicall support
895#
Fredrik Lundhb9056332001-07-11 17:42:21 +0000896
Martin v. Löwis45394c22003-10-31 13:49:36 +0000897class _MultiCallMethod:
898 # some lesser magic to store calls made to a MultiCall object
899 # for batch execution
900 def __init__(self, call_list, name):
901 self.__call_list = call_list
902 self.__name = name
903 def __getattr__(self, name):
904 return _MultiCallMethod(self.__call_list, "%s.%s" % (self.__name, name))
905 def __call__(self, *args):
906 self.__call_list.append((self.__name, args))
907
Martin v. Löwis12237b32004-08-22 16:04:50 +0000908class MultiCallIterator:
Martin v. Löwis45394c22003-10-31 13:49:36 +0000909 """Iterates over the results of a multicall. Exceptions are
910 thrown in response to xmlrpc faults."""
Raymond Hettingercc523fc2003-11-02 09:47:05 +0000911
Martin v. Löwis12237b32004-08-22 16:04:50 +0000912 def __init__(self, results):
913 self.results = results
914
915 def __getitem__(self, i):
916 item = self.results[i]
917 if type(item) == type({}):
918 raise Fault(item['faultCode'], item['faultString'])
919 elif type(item) == type([]):
920 return item[0]
Martin v. Löwis45394c22003-10-31 13:49:36 +0000921 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000922 raise ValueError("unexpected type in multicall result")
Raymond Hettingercc523fc2003-11-02 09:47:05 +0000923
Martin v. Löwis45394c22003-10-31 13:49:36 +0000924class MultiCall:
925 """server -> a object used to boxcar method calls
926
927 server should be a ServerProxy object.
928
929 Methods can be added to the MultiCall using normal
930 method call syntax e.g.:
931
932 multicall = MultiCall(server_proxy)
933 multicall.add(2,3)
934 multicall.get_address("Guido")
935
936 To execute the multicall, call the MultiCall object e.g.:
937
938 add_result, address = multicall()
939 """
Raymond Hettingercc523fc2003-11-02 09:47:05 +0000940
Martin v. Löwis45394c22003-10-31 13:49:36 +0000941 def __init__(self, server):
942 self.__server = server
943 self.__call_list = []
Raymond Hettingercc523fc2003-11-02 09:47:05 +0000944
Martin v. Löwis45394c22003-10-31 13:49:36 +0000945 def __repr__(self):
946 return "<MultiCall at %x>" % id(self)
Raymond Hettingercc523fc2003-11-02 09:47:05 +0000947
Martin v. Löwis45394c22003-10-31 13:49:36 +0000948 __str__ = __repr__
949
950 def __getattr__(self, name):
951 return _MultiCallMethod(self.__call_list, name)
952
953 def __call__(self):
954 marshalled_list = []
955 for name, args in self.__call_list:
956 marshalled_list.append({'methodName' : name, 'params' : args})
957
958 return MultiCallIterator(self.__server.system.multicall(marshalled_list))
Raymond Hettingercc523fc2003-11-02 09:47:05 +0000959
Fredrik Lundhb9056332001-07-11 17:42:21 +0000960# --------------------------------------------------------------------
961# convenience functions
962
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000963##
964# Create a parser object, and connect it to an unmarshalling instance.
965# This function picks the fastest available XML parser.
966#
967# return A (parser, unmarshaller) tuple.
968
Skip Montanaro174dd222005-05-14 20:54:16 +0000969def getparser(use_datetime=0):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000970 """getparser() -> parser, unmarshaller
971
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000972 Create an instance of the fastest available parser, and attach it
973 to an unmarshalling object. Return both objects.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000974 """
Skip Montanaro174dd222005-05-14 20:54:16 +0000975 if use_datetime and not datetime:
Collin Winterce36ad82007-08-30 01:19:48 +0000976 raise ValueError("the datetime module is not available")
Fredrik Lundhb9056332001-07-11 17:42:21 +0000977 if FastParser and FastUnmarshaller:
Skip Montanaro174dd222005-05-14 20:54:16 +0000978 if use_datetime:
979 mkdatetime = _datetime_type
980 else:
981 mkdatetime = _datetime
982 target = FastUnmarshaller(True, False, _binary, mkdatetime, Fault)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000983 parser = FastParser(target)
984 else:
Skip Montanaro174dd222005-05-14 20:54:16 +0000985 target = Unmarshaller(use_datetime=use_datetime)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000986 if FastParser:
987 parser = FastParser(target)
988 elif SgmlopParser:
989 parser = SgmlopParser(target)
990 elif ExpatParser:
991 parser = ExpatParser(target)
992 else:
993 parser = SlowParser(target)
994 return parser, target
995
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000996##
997# Convert a Python tuple or a Fault instance to an XML-RPC packet.
998#
999# @def dumps(params, **options)
1000# @param params A tuple or Fault instance.
1001# @keyparam methodname If given, create a methodCall request for
1002# this method name.
1003# @keyparam methodresponse If given, create a methodResponse packet.
1004# If used with a tuple, the tuple must be a singleton (that is,
1005# it must contain exactly one element).
1006# @keyparam encoding The packet encoding.
1007# @return A string containing marshalled data.
1008
Andrew M. Kuchlinga4c2b742003-04-25 00:26:51 +00001009def dumps(params, methodname=None, methodresponse=None, encoding=None,
1010 allow_none=0):
Fredrik Lundhb9056332001-07-11 17:42:21 +00001011 """data [,options] -> marshalled data
1012
1013 Convert an argument tuple or a Fault instance to an XML-RPC
1014 request (or response, if the methodresponse option is used).
1015
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +00001016 In addition to the data object, the following options can be given
1017 as keyword arguments:
Fredrik Lundhb9056332001-07-11 17:42:21 +00001018
1019 methodname: the method name for a methodCall packet
1020
1021 methodresponse: true to create a methodResponse packet.
1022 If this option is used with a tuple, the tuple must be
1023 a singleton (i.e. it can contain only one element).
1024
1025 encoding: the packet encoding (default is UTF-8)
1026
1027 All 8-bit strings in the data structure are assumed to use the
1028 packet encoding. Unicode strings are automatically converted,
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +00001029 where necessary.
Fredrik Lundhb9056332001-07-11 17:42:21 +00001030 """
1031
Guido van Rossum13257902007-06-07 23:15:56 +00001032 assert isinstance(params, (tuple, Fault)), "argument must be tuple or Fault instance"
Fredrik Lundhb9056332001-07-11 17:42:21 +00001033 if isinstance(params, Fault):
1034 methodresponse = 1
Guido van Rossum13257902007-06-07 23:15:56 +00001035 elif methodresponse and isinstance(params, tuple):
Fredrik Lundhb9056332001-07-11 17:42:21 +00001036 assert len(params) == 1, "response tuple must be a singleton"
1037
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001038 if not encoding:
Fredrik Lundhb9056332001-07-11 17:42:21 +00001039 encoding = "utf-8"
1040
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001041 if FastMarshaller:
1042 m = FastMarshaller(encoding)
1043 else:
Andrew M. Kuchlinga4c2b742003-04-25 00:26:51 +00001044 m = Marshaller(encoding, allow_none)
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001045
Fredrik Lundhb9056332001-07-11 17:42:21 +00001046 data = m.dumps(params)
1047
1048 if encoding != "utf-8":
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001049 xmlheader = "<?xml version='1.0' encoding='%s'?>\n" % str(encoding)
Fredrik Lundhb9056332001-07-11 17:42:21 +00001050 else:
1051 xmlheader = "<?xml version='1.0'?>\n" # utf-8 is default
1052
1053 # standard XML-RPC wrappings
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001054 if methodname:
Fredrik Lundhb9056332001-07-11 17:42:21 +00001055 # a method call
Guido van Rossum3172c5d2007-10-16 18:12:55 +00001056 if not isinstance(methodname, str):
Fredrik Lundhb9056332001-07-11 17:42:21 +00001057 methodname = methodname.encode(encoding)
1058 data = (
1059 xmlheader,
1060 "<methodCall>\n"
1061 "<methodName>", methodname, "</methodName>\n",
1062 data,
1063 "</methodCall>\n"
1064 )
1065 elif methodresponse:
1066 # a method response, or a fault structure
1067 data = (
1068 xmlheader,
1069 "<methodResponse>\n",
1070 data,
1071 "</methodResponse>\n"
1072 )
1073 else:
1074 return data # return as is
Neal Norwitzff113342007-04-17 08:42:15 +00001075 return "".join(data)
Fredrik Lundhb9056332001-07-11 17:42:21 +00001076
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001077##
1078# Convert an XML-RPC packet to a Python object. If the XML-RPC packet
1079# represents a fault condition, this function raises a Fault exception.
1080#
1081# @param data An XML-RPC packet, given as an 8-bit string.
Walter Dörwaldf0dfc7a2003-10-20 14:01:56 +00001082# @return A tuple containing the unpacked data, and the method name
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001083# (None if not present).
1084# @see Fault
1085
Skip Montanaro174dd222005-05-14 20:54:16 +00001086def loads(data, use_datetime=0):
Fredrik Lundhb9056332001-07-11 17:42:21 +00001087 """data -> unmarshalled data, method name
1088
1089 Convert an XML-RPC packet to unmarshalled data plus a method
1090 name (None if not present).
1091
1092 If the XML-RPC packet represents a fault condition, this function
1093 raises a Fault exception.
1094 """
Skip Montanaro174dd222005-05-14 20:54:16 +00001095 p, u = getparser(use_datetime=use_datetime)
Fredrik Lundhb9056332001-07-11 17:42:21 +00001096 p.feed(data)
1097 p.close()
1098 return u.close(), u.getmethodname()
1099
1100
1101# --------------------------------------------------------------------
1102# request dispatcher
1103
1104class _Method:
1105 # some magic to bind an XML-RPC method to an RPC server.
1106 # supports "nested" methods (e.g. examples.getStateName)
1107 def __init__(self, send, name):
1108 self.__send = send
1109 self.__name = name
1110 def __getattr__(self, name):
1111 return _Method(self.__send, "%s.%s" % (self.__name, name))
1112 def __call__(self, *args):
1113 return self.__send(self.__name, args)
1114
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001115##
1116# Standard transport class for XML-RPC over HTTP.
1117# <p>
1118# You can create custom transports by subclassing this method, and
1119# overriding selected methods.
Fredrik Lundhb9056332001-07-11 17:42:21 +00001120
1121class Transport:
Fredrik Lundhc4c062f2001-09-10 19:45:02 +00001122 """Handles an HTTP transaction to an XML-RPC server."""
Fredrik Lundhb9056332001-07-11 17:42:21 +00001123
1124 # client identifier (may be overridden)
1125 user_agent = "xmlrpclib.py/%s (by www.pythonware.com)" % __version__
1126
Skip Montanaro174dd222005-05-14 20:54:16 +00001127 def __init__(self, use_datetime=0):
1128 self._use_datetime = use_datetime
1129
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001130 ##
1131 # Send a complete request, and parse the response.
1132 #
1133 # @param host Target host.
1134 # @param handler Target PRC handler.
1135 # @param request_body XML-RPC request body.
1136 # @param verbose Debugging flag.
1137 # @return Parsed response.
1138
Fredrik Lundhb9056332001-07-11 17:42:21 +00001139 def request(self, host, handler, request_body, verbose=0):
1140 # issue XML-RPC request
1141
Jeremy Hylton5d8a88a2007-08-14 16:47:39 +00001142 http_conn = self.send_request(host, handler, request_body, verbose)
1143 resp = http_conn.getresponse()
Fredrik Lundhb9056332001-07-11 17:42:21 +00001144
Jeremy Hylton5d8a88a2007-08-14 16:47:39 +00001145 if resp.status != 200:
Fredrik Lundhb9056332001-07-11 17:42:21 +00001146 raise ProtocolError(
1147 host + handler,
Hye-Shik Chang96042862007-08-19 10:49:11 +00001148 resp.status, resp.reason,
Guido van Rossumcfe02a42007-08-22 23:45:42 +00001149 dict(resp.getheaders())
Fredrik Lundhb9056332001-07-11 17:42:21 +00001150 )
1151
1152 self.verbose = verbose
1153
Jeremy Hylton5d8a88a2007-08-14 16:47:39 +00001154 return self._parse_response(resp, None)
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001155
1156 ##
1157 # Create parser.
1158 #
1159 # @return A 2-tuple containing a parser and a unmarshaller.
Fredrik Lundhb9056332001-07-11 17:42:21 +00001160
Fredrik Lundhc4c062f2001-09-10 19:45:02 +00001161 def getparser(self):
1162 # get parser and unmarshaller
Skip Montanaro174dd222005-05-14 20:54:16 +00001163 return getparser(use_datetime=self._use_datetime)
Fredrik Lundhc4c062f2001-09-10 19:45:02 +00001164
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001165 ##
Fredrik Lundh1303c7c2002-10-22 18:23:00 +00001166 # Get authorization info from host parameter
1167 # Host may be a string, or a (host, x509-dict) tuple; if a string,
1168 # it is checked for a "user:pw@host" format, and a "Basic
1169 # Authentication" header is added if appropriate.
1170 #
1171 # @param host Host descriptor (URL or (URL, x509 info) tuple).
1172 # @return A 3-tuple containing (actual host, extra headers,
1173 # x509 info). The header and x509 fields may be None.
1174
1175 def get_host_info(self, host):
1176
1177 x509 = {}
Guido van Rossum13257902007-06-07 23:15:56 +00001178 if isinstance(host, tuple):
Fredrik Lundh1303c7c2002-10-22 18:23:00 +00001179 host, x509 = host
1180
1181 import urllib
1182 auth, host = urllib.splituser(host)
1183
1184 if auth:
1185 import base64
Fredrik Lundh768c98b2002-11-01 17:14:16 +00001186 auth = base64.encodestring(urllib.unquote(auth))
Neal Norwitzff113342007-04-17 08:42:15 +00001187 auth = "".join(auth.split()) # get rid of whitespace
Fredrik Lundh1303c7c2002-10-22 18:23:00 +00001188 extra_headers = [
1189 ("Authorization", "Basic " + auth)
1190 ]
1191 else:
1192 extra_headers = None
1193
1194 return host, extra_headers, x509
1195
1196 ##
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001197 # Connect to server.
1198 #
1199 # @param host Target host.
Jeremy Hylton5d8a88a2007-08-14 16:47:39 +00001200 # @return An HTTPConnection object
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001201
Fredrik Lundhb9056332001-07-11 17:42:21 +00001202 def make_connection(self, host):
1203 # create a HTTP connection object from a host descriptor
Fredrik Lundh1303c7c2002-10-22 18:23:00 +00001204 host, extra_headers, x509 = self.get_host_info(host)
Jeremy Hylton5d8a88a2007-08-14 16:47:39 +00001205
Fredrik Lundhb9056332001-07-11 17:42:21 +00001206
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001207 ##
Jeremy Hylton5d8a88a2007-08-14 16:47:39 +00001208 # Send HTTP request.
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001209 #
Jeremy Hylton5d8a88a2007-08-14 16:47:39 +00001210 # @param host Host descriptor (URL or (URL, x509 info) tuple).
1211 # @param handler Targer RPC handler (a path relative to host)
1212 # @param request_body The XML-RPC request body
1213 # @param debug Enable debugging if debug is true.
1214 # @return An HTTPConnection.
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001215
Jeremy Hylton5d8a88a2007-08-14 16:47:39 +00001216 def send_request(self, host, handler, request_body, debug):
Fredrik Lundh1303c7c2002-10-22 18:23:00 +00001217 host, extra_headers, x509 = self.get_host_info(host)
Jeremy Hylton5d8a88a2007-08-14 16:47:39 +00001218 connection = httplib.HTTPConnection(host)
1219 if debug:
1220 connection.set_debuglevel(1)
1221 headers = {}
Fredrik Lundh1303c7c2002-10-22 18:23:00 +00001222 if extra_headers:
Jeremy Hylton5d8a88a2007-08-14 16:47:39 +00001223 for key, val in extra_headers:
1224 header[key] = val
1225 headers["Content-Type"] = "text/xml"
1226 headers["User-Agent"] = self.user_agent
1227 connection.request("POST", handler, request_body, headers)
1228 return connection
Fredrik Lundhb9056332001-07-11 17:42:21 +00001229
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001230 ##
1231 # Parse response.
1232 #
1233 # @param file Stream.
1234 # @return Response tuple and target method.
1235
1236 def parse_response(self, file):
1237 # compatibility interface
1238 return self._parse_response(file, None)
1239
1240 ##
1241 # Parse response (alternate interface). This is similar to the
1242 # parse_response method, but also provides direct access to the
1243 # underlying socket object (where available).
1244 #
1245 # @param file Stream.
1246 # @param sock Socket handle (or None, if the socket object
1247 # could not be accessed).
1248 # @return Response tuple and target method.
1249
1250 def _parse_response(self, file, sock):
1251 # read response from input file/socket, and parse it
Fredrik Lundhb9056332001-07-11 17:42:21 +00001252
Fredrik Lundhc4c062f2001-09-10 19:45:02 +00001253 p, u = self.getparser()
Fredrik Lundhb9056332001-07-11 17:42:21 +00001254
1255 while 1:
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001256 if sock:
1257 response = sock.recv(1024)
1258 else:
1259 response = file.read(1024)
Fredrik Lundhb9056332001-07-11 17:42:21 +00001260 if not response:
1261 break
1262 if self.verbose:
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001263 print("body:", repr(response))
Fredrik Lundhb9056332001-07-11 17:42:21 +00001264 p.feed(response)
1265
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001266 file.close()
Fredrik Lundhb9056332001-07-11 17:42:21 +00001267 p.close()
1268
1269 return u.close()
1270
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001271##
1272# Standard transport class for XML-RPC over HTTPS.
1273
Fredrik Lundhb9056332001-07-11 17:42:21 +00001274class SafeTransport(Transport):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +00001275 """Handles an HTTPS transaction to an XML-RPC server."""
Fredrik Lundhb9056332001-07-11 17:42:21 +00001276
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001277 # FIXME: mostly untested
1278
Jeremy Hylton5d8a88a2007-08-14 16:47:39 +00001279 def send_request(self, host, handler, request_body, debug):
1280 import socket
1281 if not hasattr(socket, "ssl"):
Fredrik Lundh1303c7c2002-10-22 18:23:00 +00001282 raise NotImplementedError(
Jeremy Hylton5d8a88a2007-08-14 16:47:39 +00001283 "your version of httplib doesn't support HTTPS")
1284
1285 host, extra_headers, x509 = self.get_host_info(host)
1286 connection = httplib.HTTPSConnection(host, None, **(x509 or {}))
1287 if debug:
1288 connection.set_debuglevel(1)
1289 headers = {}
1290 if extra_headers:
1291 for key, val in extra_headers:
1292 header[key] = val
1293 headers["Content-Type"] = "text/xml"
1294 headers["User-Agent"] = self.user_agent
1295 connection.request("POST", handler, request_body, headers)
1296 return connection
Fredrik Lundhb9056332001-07-11 17:42:21 +00001297
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001298##
1299# Standard server proxy. This class establishes a virtual connection
1300# to an XML-RPC server.
1301# <p>
1302# This class is available as ServerProxy and Server. New code should
1303# use ServerProxy, to avoid confusion.
1304#
1305# @def ServerProxy(uri, **options)
1306# @param uri The connection point on the server.
1307# @keyparam transport A transport factory, compatible with the
1308# standard transport class.
1309# @keyparam encoding The default encoding used for 8-bit strings
1310# (default is UTF-8).
1311# @keyparam verbose Use a true value to enable debugging output.
1312# (printed to standard output).
1313# @see Transport
1314
Fredrik Lundhb9056332001-07-11 17:42:21 +00001315class ServerProxy:
1316 """uri [,options] -> a logical connection to an XML-RPC server
1317
1318 uri is the connection point on the server, given as
1319 scheme://host/target.
1320
1321 The standard implementation always supports the "http" scheme. If
1322 SSL socket support is available (Python 2.0), it also supports
1323 "https".
1324
1325 If the target part and the slash preceding it are both omitted,
1326 "/RPC2" is assumed.
1327
1328 The following options can be given as keyword arguments:
1329
1330 transport: a transport factory
1331 encoding: the request encoding (default is UTF-8)
1332
1333 All 8-bit strings passed to the server proxy are assumed to use
1334 the given encoding.
1335 """
1336
Andrew M. Kuchlinga4c2b742003-04-25 00:26:51 +00001337 def __init__(self, uri, transport=None, encoding=None, verbose=0,
Skip Montanaro174dd222005-05-14 20:54:16 +00001338 allow_none=0, use_datetime=0):
Fredrik Lundhb9056332001-07-11 17:42:21 +00001339 # establish a "logical" server connection
1340
1341 # get the url
Fredrik Lundhc4c062f2001-09-10 19:45:02 +00001342 import urllib
Fredrik Lundhb9056332001-07-11 17:42:21 +00001343 type, uri = urllib.splittype(uri)
1344 if type not in ("http", "https"):
Collin Winterce36ad82007-08-30 01:19:48 +00001345 raise IOError("unsupported XML-RPC protocol")
Fredrik Lundhb9056332001-07-11 17:42:21 +00001346 self.__host, self.__handler = urllib.splithost(uri)
1347 if not self.__handler:
1348 self.__handler = "/RPC2"
1349
1350 if transport is None:
1351 if type == "https":
Skip Montanaro174dd222005-05-14 20:54:16 +00001352 transport = SafeTransport(use_datetime=use_datetime)
Fredrik Lundhb9056332001-07-11 17:42:21 +00001353 else:
Skip Montanaro174dd222005-05-14 20:54:16 +00001354 transport = Transport(use_datetime=use_datetime)
Fredrik Lundhb9056332001-07-11 17:42:21 +00001355 self.__transport = transport
1356
1357 self.__encoding = encoding
1358 self.__verbose = verbose
Andrew M. Kuchlinga4c2b742003-04-25 00:26:51 +00001359 self.__allow_none = allow_none
Tim Petersc2659cf2003-05-12 20:19:37 +00001360
Fredrik Lundhb9056332001-07-11 17:42:21 +00001361 def __request(self, methodname, params):
1362 # call a method on the remote server
1363
Andrew M. Kuchlinga4c2b742003-04-25 00:26:51 +00001364 request = dumps(params, methodname, encoding=self.__encoding,
1365 allow_none=self.__allow_none)
Fredrik Lundhb9056332001-07-11 17:42:21 +00001366
1367 response = self.__transport.request(
1368 self.__host,
1369 self.__handler,
1370 request,
1371 verbose=self.__verbose
1372 )
1373
1374 if len(response) == 1:
1375 response = response[0]
1376
1377 return response
1378
1379 def __repr__(self):
1380 return (
Fredrik Lundh78eedce2001-08-23 20:04:33 +00001381 "<ServerProxy for %s%s>" %
Fredrik Lundhb9056332001-07-11 17:42:21 +00001382 (self.__host, self.__handler)
1383 )
1384
1385 __str__ = __repr__
Raymond Hettingercc523fc2003-11-02 09:47:05 +00001386
Fredrik Lundhb9056332001-07-11 17:42:21 +00001387 def __getattr__(self, name):
1388 # magic method dispatcher
1389 return _Method(self.__request, name)
1390
1391 # note: to call a remote object with an non-standard name, use
1392 # result getattr(server, "strange-python-name")(args)
1393
Fredrik Lundh78eedce2001-08-23 20:04:33 +00001394# compatibility
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001395
Fredrik Lundhb9056332001-07-11 17:42:21 +00001396Server = ServerProxy
1397
1398# --------------------------------------------------------------------
1399# test code
1400
1401if __name__ == "__main__":
1402
1403 # simple test program (from the XML-RPC specification)
1404
Fredrik Lundh78eedce2001-08-23 20:04:33 +00001405 # server = ServerProxy("http://localhost:8000") # local server
Martin v. Löwis12237b32004-08-22 16:04:50 +00001406 server = ServerProxy("http://time.xmlrpc.com/RPC2")
Fredrik Lundhb9056332001-07-11 17:42:21 +00001407
Fredrik Lundhb9056332001-07-11 17:42:21 +00001408 try:
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001409 print(server.currentTime.getCurrentTime())
Guido van Rossumb940e112007-01-10 16:19:56 +00001410 except Error as v:
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001411 print("ERROR", v)
Martin v. Löwis12237b32004-08-22 16:04:50 +00001412
Jeremy Hylton5d8a88a2007-08-14 16:47:39 +00001413 # The server at xmlrpc.com doesn't seem to support multicall anymore.
Martin v. Löwis12237b32004-08-22 16:04:50 +00001414 multi = MultiCall(server)
1415 multi.currentTime.getCurrentTime()
1416 multi.currentTime.getCurrentTime()
1417 try:
1418 for response in multi():
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001419 print(response)
Guido van Rossumb940e112007-01-10 16:19:56 +00001420 except Error as v:
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001421 print("ERROR", v)