blob: d18efce4c101aa856c6b3cdec23891c9d52b6b41 [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 DateTime dateTime wrapper for an ISO 8601 string or time tuple or
112 localtime integer value to generate a "dateTime.iso8601"
113 XML-RPC value
114 Binary binary data wrapper
Fred Drake1b410792001-09-04 18:55:03 +0000115
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000116 SlowParser Slow but safe standard parser (based on xmllib)
117 Marshaller Generate an XML-RPC params chunk from a Python data structure
118 Unmarshaller Unmarshal an XML-RPC response from incoming XML event message
119 Transport Handles an HTTP transaction to an XML-RPC server
120 SafeTransport Handles an HTTPS transaction to an XML-RPC server
Fred Drake1b410792001-09-04 18:55:03 +0000121
122Exported constants:
123
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000124 True
125 False
Fred Drake1b410792001-09-04 18:55:03 +0000126
127Exported functions:
128
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000129 getparser Create instance of the fastest available parser & attach
130 to an unmarshalling object
131 dumps Convert an argument tuple or a Fault instance to an XML-RPC
132 request (or response, if the methodresponse option is used).
133 loads Convert an XML-RPC packet to unmarshalled data plus a method
134 name (None if not present).
Fred Drake1b410792001-09-04 18:55:03 +0000135"""
136
Neal Norwitzff113342007-04-17 08:42:15 +0000137import re, time, operator
Georg Brandl24420152008-05-26 16:32:26 +0000138import http.client
Fredrik Lundh1538c232001-10-01 19:42:03 +0000139
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000140# --------------------------------------------------------------------
141# Internal stuff
142
Fredrik Lundhb9056332001-07-11 17:42:21 +0000143try:
Fred Drakeba613c32005-02-10 18:33:30 +0000144 import datetime
145except ImportError:
146 datetime = None
147
Fredrik Lundhb9056332001-07-11 17:42:21 +0000148def _decode(data, encoding, is8bit=re.compile("[\x80-\xff]").search):
149 # decode non-ascii string (if possible)
Guido van Rossum54ad5232007-05-27 09:17:48 +0000150 if encoding and is8bit(data):
Guido van Rossumef87d6e2007-05-02 19:09:54 +0000151 data = str(data, encoding)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000152 return data
153
Neal Norwitzff113342007-04-17 08:42:15 +0000154def escape(s):
155 s = s.replace("&", "&")
156 s = s.replace("<", "&lt;")
157 return s.replace(">", "&gt;",)
Fredrik Lundh1538c232001-10-01 19:42:03 +0000158
Guido van Rossum54ad5232007-05-27 09:17:48 +0000159def _stringify(string):
160 # convert to 7-bit ascii if possible
161 try:
Brett Cannon2dbde5e2007-07-30 03:50:35 +0000162 return string.decode("ascii")
Guido van Rossum261f9df2007-08-31 14:07:27 +0000163 except (UnicodeError, TypeError, AttributeError):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000164 return string
165
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000166__version__ = "1.0.1"
167
168# xmlrpc integer limits
Guido van Rossume2a383d2007-01-15 16:59:06 +0000169MAXINT = 2**31-1
170MININT = -2**31
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000171
172# --------------------------------------------------------------------
173# Error constants (from Dan Libby's specification at
174# http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php)
175
176# Ranges of errors
177PARSE_ERROR = -32700
178SERVER_ERROR = -32600
179APPLICATION_ERROR = -32500
180SYSTEM_ERROR = -32400
181TRANSPORT_ERROR = -32300
182
183# Specific errors
184NOT_WELLFORMED_ERROR = -32700
185UNSUPPORTED_ENCODING = -32701
186INVALID_ENCODING_CHAR = -32702
187INVALID_XMLRPC = -32600
188METHOD_NOT_FOUND = -32601
189INVALID_METHOD_PARAMS = -32602
190INTERNAL_ERROR = -32603
Fredrik Lundhb9056332001-07-11 17:42:21 +0000191
192# --------------------------------------------------------------------
193# Exceptions
194
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000195##
196# Base class for all kinds of client-side errors.
197
Fredrik Lundh78eedce2001-08-23 20:04:33 +0000198class Error(Exception):
Fred Drake1b410792001-09-04 18:55:03 +0000199 """Base class for client errors."""
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000200 def __str__(self):
201 return repr(self)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000202
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000203##
204# Indicates an HTTP-level protocol error. This is raised by the HTTP
205# transport layer, if the server returns an error code other than 200
206# (OK).
207#
208# @param url The target URL.
209# @param errcode The HTTP error code.
210# @param errmsg The HTTP error message.
211# @param headers The HTTP header dictionary.
212
Fredrik Lundhb9056332001-07-11 17:42:21 +0000213class ProtocolError(Error):
Fred Drake1b410792001-09-04 18:55:03 +0000214 """Indicates an HTTP protocol error."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000215 def __init__(self, url, errcode, errmsg, headers):
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000216 Error.__init__(self)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000217 self.url = url
218 self.errcode = errcode
219 self.errmsg = errmsg
220 self.headers = headers
221 def __repr__(self):
222 return (
223 "<ProtocolError for %s: %s %s>" %
224 (self.url, self.errcode, self.errmsg)
225 )
226
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000227##
228# Indicates a broken XML-RPC response package. This exception is
229# raised by the unmarshalling layer, if the XML-RPC response is
230# malformed.
231
Fredrik Lundhb9056332001-07-11 17:42:21 +0000232class ResponseError(Error):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000233 """Indicates a broken response package."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000234 pass
235
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000236##
237# Indicates an XML-RPC fault response package. This exception is
238# raised by the unmarshalling layer, if the XML-RPC response contains
239# a fault string. This exception can also used as a class, to
240# generate a fault XML-RPC message.
241#
242# @param faultCode The XML-RPC fault code.
243# @param faultString The XML-RPC fault string.
244
Fredrik Lundhb9056332001-07-11 17:42:21 +0000245class Fault(Error):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000246 """Indicates an XML-RPC fault package."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000247 def __init__(self, faultCode, faultString, **extra):
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000248 Error.__init__(self)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000249 self.faultCode = faultCode
250 self.faultString = faultString
251 def __repr__(self):
252 return (
253 "<Fault %s: %s>" %
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000254 (self.faultCode, repr(self.faultString))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000255 )
256
257# --------------------------------------------------------------------
258# Special values
259
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000260##
Georg Brandl38eceaa2008-05-26 11:14:17 +0000261# Backwards compatibility
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000262
Guido van Rossum47b9ff62006-08-24 00:41:19 +0000263boolean = Boolean = bool
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000264
265##
266# Wrapper for XML-RPC DateTime values. This converts a time value to
267# the format used by XML-RPC.
268# <p>
269# The value can be given as a string in the format
270# "yyyymmddThh:mm:ss", as a 9-item time tuple (as returned by
271# time.localtime()), or an integer value (as returned by time.time()).
272# The wrapper uses time.localtime() to convert an integer to a time
273# tuple.
274#
275# @param value The time, given as an ISO 8601 string, a time
276# tuple, or a integer time value.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000277
Christian Heimesdae2a892008-04-19 00:55:37 +0000278def _strftime(value):
279 if datetime:
280 if isinstance(value, datetime.datetime):
281 return "%04d%02d%02dT%02d:%02d:%02d" % (
282 value.year, value.month, value.day,
283 value.hour, value.minute, value.second)
284
285 if not isinstance(value, (tuple, time.struct_time)):
286 if value == 0:
287 value = time.time()
288 value = time.localtime(value)
289
290 return "%04d%02d%02dT%02d:%02d:%02d" % value[:6]
291
Fredrik Lundhb9056332001-07-11 17:42:21 +0000292class DateTime:
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000293 """DateTime wrapper for an ISO 8601 string or time tuple or
294 localtime integer value to generate 'dateTime.iso8601' XML-RPC
295 value.
296 """
Fredrik Lundhb9056332001-07-11 17:42:21 +0000297
298 def __init__(self, value=0):
Christian Heimesdae2a892008-04-19 00:55:37 +0000299 if isinstance(value, str):
300 self.value = value
301 else:
302 self.value = _strftime(value)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000303
Christian Heimes05e8be12008-02-23 18:30:17 +0000304 def make_comparable(self, other):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000305 if isinstance(other, DateTime):
Christian Heimes05e8be12008-02-23 18:30:17 +0000306 s = self.value
307 o = other.value
308 elif datetime and isinstance(other, datetime.datetime):
309 s = self.value
310 o = other.strftime("%Y%m%dT%H:%M:%S")
311 elif isinstance(other, (str, unicode)):
312 s = self.value
313 o = other
314 elif hasattr(other, "timetuple"):
315 s = self.timetuple()
316 o = other.timetuple()
317 else:
318 otype = (hasattr(other, "__class__")
319 and other.__class__.__name__
320 or type(other))
321 raise TypeError("Can't compare %s and %s" %
322 (self.__class__.__name__, otype))
323 return s, o
324
325 def __lt__(self, other):
326 s, o = self.make_comparable(other)
327 return s < o
328
329 def __le__(self, other):
330 s, o = self.make_comparable(other)
331 return s <= o
332
333 def __gt__(self, other):
334 s, o = self.make_comparable(other)
335 return s > o
336
337 def __ge__(self, other):
338 s, o = self.make_comparable(other)
339 return s >= o
340
341 def __eq__(self, other):
342 s, o = self.make_comparable(other)
343 return s == o
Guido van Rossum47b9ff62006-08-24 00:41:19 +0000344
345 def __ne__(self, other):
Christian Heimes05e8be12008-02-23 18:30:17 +0000346 s, o = self.make_comparable(other)
347 return s != o
348
349 def timetuple(self):
350 return time.strptime(self.value, "%Y%m%dT%H:%M:%S")
351
352 def __cmp__(self, other):
353 s, o = self.make_comparable(other)
354 return cmp(s, o)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000355
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000356 ##
357 # Get date/time value.
358 #
359 # @return Date/time value, as an ISO 8601 string.
360
361 def __str__(self):
362 return self.value
363
Fredrik Lundhb9056332001-07-11 17:42:21 +0000364 def __repr__(self):
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000365 return "<DateTime %s at %x>" % (repr(self.value), id(self))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000366
367 def decode(self, data):
Neal Norwitzff113342007-04-17 08:42:15 +0000368 self.value = str(data).strip()
Fredrik Lundhb9056332001-07-11 17:42:21 +0000369
370 def encode(self, out):
371 out.write("<value><dateTime.iso8601>")
372 out.write(self.value)
373 out.write("</dateTime.iso8601></value>\n")
374
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000375def _datetime(data):
376 # decode xml element contents into a DateTime structure.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000377 value = DateTime()
378 value.decode(data)
379 return value
380
Skip Montanaro174dd222005-05-14 20:54:16 +0000381def _datetime_type(data):
382 t = time.strptime(data, "%Y%m%dT%H:%M:%S")
383 return datetime.datetime(*tuple(t)[:6])
384
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000385##
386# Wrapper for binary data. This can be used to transport any kind
387# of binary data over XML-RPC, using BASE64 encoding.
388#
389# @param data An 8-bit string containing arbitrary data.
390
Skip Montanarobfcbfa72003-04-24 19:51:31 +0000391import base64
Guido van Rossum68937b42007-05-18 00:51:22 +0000392import io
Skip Montanarobfcbfa72003-04-24 19:51:31 +0000393
Fredrik Lundhb9056332001-07-11 17:42:21 +0000394class Binary:
Fred Drake1b410792001-09-04 18:55:03 +0000395 """Wrapper for binary data."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000396
397 def __init__(self, data=None):
Guido van Rossum54a40cb2007-08-27 22:27:41 +0000398 if data is None:
399 data = b""
400 else:
401 if not isinstance(data, bytes):
402 raise TypeError("expected bytes, not %s" %
403 data.__class__.__name__)
404 data = bytes(data) # Make a copy of the bytes!
Fredrik Lundhb9056332001-07-11 17:42:21 +0000405 self.data = data
406
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000407 ##
408 # Get buffer contents.
409 #
410 # @return Buffer contents, as an 8-bit string.
411
412 def __str__(self):
Guido van Rossum54a40cb2007-08-27 22:27:41 +0000413 return str(self.data, "latin-1") # XXX encoding?!
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000414
Guido van Rossum47b9ff62006-08-24 00:41:19 +0000415 def __eq__(self, other):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000416 if isinstance(other, Binary):
417 other = other.data
Guido van Rossum47b9ff62006-08-24 00:41:19 +0000418 return self.data == other
419
420 def __ne__(self, other):
421 if isinstance(other, Binary):
422 other = other.data
423 return self.data != other
Fredrik Lundhb9056332001-07-11 17:42:21 +0000424
425 def decode(self, data):
Guido van Rossum54a40cb2007-08-27 22:27:41 +0000426 self.data = base64.decodestring(data)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000427
428 def encode(self, out):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000429 out.write("<value><base64>\n")
Brett Cannon2dbde5e2007-07-30 03:50:35 +0000430 encoded = base64.encodestring(self.data)
431 out.write(encoded.decode('ascii'))
432 out.write('\n')
Fredrik Lundhb9056332001-07-11 17:42:21 +0000433 out.write("</base64></value>\n")
434
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000435def _binary(data):
436 # decode xml element contents into a Binary structure
Fredrik Lundhb9056332001-07-11 17:42:21 +0000437 value = Binary()
438 value.decode(data)
439 return value
440
Skip Montanaro9a7c96a2003-01-22 18:17:25 +0000441WRAPPERS = (DateTime, Binary)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000442
443# --------------------------------------------------------------------
444# XML parsers
445
Fredrik Lundhb9056332001-07-11 17:42:21 +0000446#
447# the SGMLOP parser is about 15x faster than Python's builtin
448# XML parser. SGMLOP sources can be downloaded from:
449#
450# http://www.pythonware.com/products/xml/sgmlop.htm
451#
452
453try:
454 import sgmlop
455 if not hasattr(sgmlop, "XMLParser"):
456 raise ImportError
457except ImportError:
458 SgmlopParser = None # sgmlop accelerator not available
459else:
460 class SgmlopParser:
461 def __init__(self, target):
462
463 # setup callbacks
464 self.finish_starttag = target.start
465 self.finish_endtag = target.end
466 self.handle_data = target.data
467 self.handle_xml = target.xml
468
469 # activate parser
470 self.parser = sgmlop.XMLParser()
471 self.parser.register(self)
472 self.feed = self.parser.feed
473 self.entity = {
474 "amp": "&", "gt": ">", "lt": "<",
475 "apos": "'", "quot": '"'
476 }
477
478 def close(self):
479 try:
480 self.parser.close()
481 finally:
482 self.parser = self.feed = None # nuke circular reference
483
484 def handle_proc(self, tag, attr):
485 m = re.search("encoding\s*=\s*['\"]([^\"']+)[\"']", attr)
486 if m:
487 self.handle_xml(m.group(1), 1)
488
489 def handle_entityref(self, entity):
490 # <string> entity
491 try:
492 self.handle_data(self.entity[entity])
493 except KeyError:
494 self.handle_data("&%s;" % entity)
495
496try:
497 from xml.parsers import expat
Guido van Rossumb8551342001-10-02 18:33:11 +0000498 if not hasattr(expat, "ParserCreate"):
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000499 raise ImportError
Fredrik Lundhb9056332001-07-11 17:42:21 +0000500except ImportError:
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000501 ExpatParser = None # expat not available
Fredrik Lundhb9056332001-07-11 17:42:21 +0000502else:
503 class ExpatParser:
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000504 # fast expat parser for Python 2.0 and later. this is about
505 # 50% slower than sgmlop, on roundtrip testing
Fredrik Lundhb9056332001-07-11 17:42:21 +0000506 def __init__(self, target):
507 self._parser = parser = expat.ParserCreate(None, None)
508 self._target = target
509 parser.StartElementHandler = target.start
510 parser.EndElementHandler = target.end
511 parser.CharacterDataHandler = target.data
512 encoding = None
Fredrik Lundhb9056332001-07-11 17:42:21 +0000513 target.xml(encoding, None)
514
515 def feed(self, data):
516 self._parser.Parse(data, 0)
517
518 def close(self):
519 self._parser.Parse("", 1) # end of data
520 del self._target, self._parser # get rid of circular references
521
Fredrik Lundhb9056332001-07-11 17:42:21 +0000522# --------------------------------------------------------------------
523# XML-RPC marshalling and unmarshalling code
524
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000525##
526# XML-RPC marshaller.
527#
528# @param encoding Default encoding for 8-bit strings. The default
529# value is None (interpreted as UTF-8).
530# @see dumps
531
Fredrik Lundhb9056332001-07-11 17:42:21 +0000532class Marshaller:
Fred Drake1b410792001-09-04 18:55:03 +0000533 """Generate an XML-RPC params chunk from a Python data structure.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000534
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000535 Create a Marshaller instance for each set of parameters, and use
536 the "dumps" method to convert your data (represented as a tuple)
537 to an XML-RPC params chunk. To write a fault response, pass a
538 Fault instance instead. You may prefer to use the "dumps" module
539 function for this purpose.
Fred Drake1b410792001-09-04 18:55:03 +0000540 """
Fredrik Lundhb9056332001-07-11 17:42:21 +0000541
542 # by the way, if you don't understand what's going on in here,
543 # that's perfectly ok.
544
Andrew M. Kuchlinga4c2b742003-04-25 00:26:51 +0000545 def __init__(self, encoding=None, allow_none=0):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000546 self.memo = {}
547 self.data = None
548 self.encoding = encoding
Andrew M. Kuchlinga4c2b742003-04-25 00:26:51 +0000549 self.allow_none = allow_none
Tim Petersc2659cf2003-05-12 20:19:37 +0000550
Fredrik Lundhb9056332001-07-11 17:42:21 +0000551 dispatch = {}
552
553 def dumps(self, values):
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000554 out = []
555 write = out.append
556 dump = self.__dump
Fredrik Lundhb9056332001-07-11 17:42:21 +0000557 if isinstance(values, Fault):
558 # fault instance
559 write("<fault>\n")
Martin v. Löwis541342f2003-07-12 07:53:04 +0000560 dump({'faultCode': values.faultCode,
561 'faultString': values.faultString},
562 write)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000563 write("</fault>\n")
564 else:
565 # parameter block
Fredrik Lundhc266bb02001-08-23 20:13:08 +0000566 # FIXME: the xml-rpc specification allows us to leave out
567 # the entire <params> block if there are no parameters.
568 # however, changing this may break older code (including
569 # old versions of xmlrpclib.py), so this is better left as
570 # is for now. See @XMLRPC3 for more information. /F
Fredrik Lundhb9056332001-07-11 17:42:21 +0000571 write("<params>\n")
572 for v in values:
573 write("<param>\n")
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000574 dump(v, write)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000575 write("</param>\n")
576 write("</params>\n")
Neal Norwitzff113342007-04-17 08:42:15 +0000577 result = "".join(out)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000578 return result
579
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000580 def __dump(self, value, write):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000581 try:
582 f = self.dispatch[type(value)]
583 except KeyError:
Thomas Wouters89f507f2006-12-13 04:49:30 +0000584 # check if this object can be marshalled as a structure
585 try:
586 value.__dict__
587 except:
Collin Winterce36ad82007-08-30 01:19:48 +0000588 raise TypeError("cannot marshal %s objects" % type(value))
Thomas Wouters89f507f2006-12-13 04:49:30 +0000589 # check if this class is a sub-class of a basic type,
590 # because we don't know how to marshal these types
591 # (e.g. a string sub-class)
592 for type_ in type(value).__mro__:
593 if type_ in self.dispatch.keys():
Collin Winterce36ad82007-08-30 01:19:48 +0000594 raise TypeError("cannot marshal %s objects" % type(value))
Thomas Wouters89f507f2006-12-13 04:49:30 +0000595 # XXX(twouters): using "_arbitrary_instance" as key as a quick-fix
596 # for the p3yk merge, this should probably be fixed more neatly.
597 f = self.dispatch["_arbitrary_instance"]
598 f(self, value, write)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000599
Andrew M. Kuchlinga4c2b742003-04-25 00:26:51 +0000600 def dump_nil (self, value, write):
601 if not self.allow_none:
Collin Winterce36ad82007-08-30 01:19:48 +0000602 raise TypeError("cannot marshal None unless allow_none is enabled")
Andrew M. Kuchlinga4c2b742003-04-25 00:26:51 +0000603 write("<value><nil/></value>")
Guido van Rossum13257902007-06-07 23:15:56 +0000604 dispatch[type(None)] = dump_nil
Tim Petersc2659cf2003-05-12 20:19:37 +0000605
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000606 def dump_int(self, value, write):
Skip Montanaro5449e082001-10-17 22:53:33 +0000607 # in case ints are > 32 bits
608 if value > MAXINT or value < MININT:
Collin Winterce36ad82007-08-30 01:19:48 +0000609 raise OverflowError("int exceeds XML-RPC limits")
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000610 write("<value><int>")
611 write(str(value))
612 write("</int></value>\n")
Guido van Rossum13257902007-06-07 23:15:56 +0000613 #dispatch[int] = dump_int
Fredrik Lundhb9056332001-07-11 17:42:21 +0000614
Georg Brandl38eceaa2008-05-26 11:14:17 +0000615 def dump_bool(self, value, write):
616 write("<value><boolean>")
617 write(value and "1" or "0")
618 write("</boolean></value>\n")
619 dispatch[bool] = dump_bool
Skip Montanaro9a7c96a2003-01-22 18:17:25 +0000620
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000621 def dump_long(self, value, write):
Skip Montanaro5449e082001-10-17 22:53:33 +0000622 if value > MAXINT or value < MININT:
Collin Winterce36ad82007-08-30 01:19:48 +0000623 raise OverflowError("long int exceeds XML-RPC limits")
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000624 write("<value><int>")
625 write(str(int(value)))
626 write("</int></value>\n")
Guido van Rossum13257902007-06-07 23:15:56 +0000627 dispatch[int] = dump_long
Skip Montanaro5e9c71b2001-10-10 15:56:34 +0000628
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000629 def dump_double(self, value, write):
630 write("<value><double>")
631 write(repr(value))
632 write("</double></value>\n")
Guido van Rossum13257902007-06-07 23:15:56 +0000633 dispatch[float] = dump_double
Fredrik Lundhb9056332001-07-11 17:42:21 +0000634
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000635 def dump_string(self, value, write, escape=escape):
636 write("<value><string>")
637 write(escape(value))
638 write("</string></value>\n")
Guido van Rossum98297ee2007-11-06 21:34:58 +0000639 dispatch[bytes] = dump_string
Fredrik Lundhb9056332001-07-11 17:42:21 +0000640
Guido van Rossum54ad5232007-05-27 09:17:48 +0000641 def dump_unicode(self, value, write, escape=escape):
Guido van Rossum54ad5232007-05-27 09:17:48 +0000642 write("<value><string>")
643 write(escape(value))
644 write("</string></value>\n")
645 dispatch[str] = dump_unicode
Fredrik Lundhb9056332001-07-11 17:42:21 +0000646
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000647 def dump_array(self, value, write):
648 i = id(value)
Guido van Rossume2b70bc2006-08-18 22:13:04 +0000649 if i in self.memo:
Collin Winterce36ad82007-08-30 01:19:48 +0000650 raise TypeError("cannot marshal recursive sequences")
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000651 self.memo[i] = None
Fredrik Lundh1538c232001-10-01 19:42:03 +0000652 dump = self.__dump
Fredrik Lundhb9056332001-07-11 17:42:21 +0000653 write("<value><array><data>\n")
654 for v in value:
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000655 dump(v, write)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000656 write("</data></array></value>\n")
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000657 del self.memo[i]
Guido van Rossum13257902007-06-07 23:15:56 +0000658 dispatch[tuple] = dump_array
659 dispatch[list] = dump_array
Fredrik Lundhb9056332001-07-11 17:42:21 +0000660
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000661 def dump_struct(self, value, write, escape=escape):
662 i = id(value)
Guido van Rossume2b70bc2006-08-18 22:13:04 +0000663 if i in self.memo:
Collin Winterce36ad82007-08-30 01:19:48 +0000664 raise TypeError("cannot marshal recursive dictionaries")
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000665 self.memo[i] = None
Fredrik Lundh1538c232001-10-01 19:42:03 +0000666 dump = self.__dump
Fredrik Lundhb9056332001-07-11 17:42:21 +0000667 write("<value><struct>\n")
Andrew M. Kuchling5962f452004-06-05 12:35:58 +0000668 for k, v in value.items():
Fredrik Lundhb9056332001-07-11 17:42:21 +0000669 write("<member>\n")
Guido van Rossum3172c5d2007-10-16 18:12:55 +0000670 if not isinstance(k, str):
Collin Winterce36ad82007-08-30 01:19:48 +0000671 raise TypeError("dictionary key must be string")
Fredrik Lundh1538c232001-10-01 19:42:03 +0000672 write("<name>%s</name>\n" % escape(k))
Andrew M. Kuchling5962f452004-06-05 12:35:58 +0000673 dump(v, write)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000674 write("</member>\n")
675 write("</struct></value>\n")
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000676 del self.memo[i]
Guido van Rossum13257902007-06-07 23:15:56 +0000677 dispatch[dict] = dump_struct
Fredrik Lundhb9056332001-07-11 17:42:21 +0000678
Fred Drakeba613c32005-02-10 18:33:30 +0000679 if datetime:
680 def dump_datetime(self, value, write):
681 write("<value><dateTime.iso8601>")
Christian Heimesdae2a892008-04-19 00:55:37 +0000682 write(_strftime(value))
Fred Drakeba613c32005-02-10 18:33:30 +0000683 write("</dateTime.iso8601></value>\n")
684 dispatch[datetime.datetime] = dump_datetime
685
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000686 def dump_instance(self, value, write):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000687 # check for special wrappers
688 if value.__class__ in WRAPPERS:
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000689 self.write = write
Fredrik Lundhb9056332001-07-11 17:42:21 +0000690 value.encode(self)
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000691 del self.write
Fredrik Lundhb9056332001-07-11 17:42:21 +0000692 else:
693 # store instance attributes as a struct (really?)
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000694 self.dump_struct(value.__dict__, write)
Guido van Rossume4dea982006-04-21 13:45:00 +0000695 dispatch[DateTime] = dump_instance
696 dispatch[Binary] = dump_instance
Thomas Wouters89f507f2006-12-13 04:49:30 +0000697 # XXX(twouters): using "_arbitrary_instance" as key as a quick-fix
698 # for the p3yk merge, this should probably be fixed more neatly.
699 dispatch["_arbitrary_instance"] = dump_instance
Fredrik Lundhb9056332001-07-11 17:42:21 +0000700
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000701##
702# XML-RPC unmarshaller.
703#
704# @see loads
705
Fredrik Lundhb9056332001-07-11 17:42:21 +0000706class Unmarshaller:
Fred Drake1b410792001-09-04 18:55:03 +0000707 """Unmarshal an XML-RPC response, based on incoming XML event
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000708 messages (start, data, end). Call close() to get the resulting
Fred Drake1b410792001-09-04 18:55:03 +0000709 data structure.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000710
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000711 Note that this reader is fairly tolerant, and gladly accepts bogus
712 XML-RPC data without complaining (but not bogus XML).
Fred Drake1b410792001-09-04 18:55:03 +0000713 """
Fredrik Lundhb9056332001-07-11 17:42:21 +0000714
715 # and again, if you don't understand what's going on in here,
716 # that's perfectly ok.
717
Skip Montanaro174dd222005-05-14 20:54:16 +0000718 def __init__(self, use_datetime=0):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000719 self._type = None
720 self._stack = []
721 self._marks = []
722 self._data = []
723 self._methodname = None
724 self._encoding = "utf-8"
725 self.append = self._stack.append
Skip Montanaro174dd222005-05-14 20:54:16 +0000726 self._use_datetime = use_datetime
727 if use_datetime and not datetime:
Collin Winterce36ad82007-08-30 01:19:48 +0000728 raise ValueError("the datetime module is not available")
Fredrik Lundhb9056332001-07-11 17:42:21 +0000729
730 def close(self):
731 # return response tuple and target method
732 if self._type is None or self._marks:
733 raise ResponseError()
734 if self._type == "fault":
Guido van Rossum68468eb2003-02-27 20:14:51 +0000735 raise Fault(**self._stack[0])
Fredrik Lundhb9056332001-07-11 17:42:21 +0000736 return tuple(self._stack)
737
738 def getmethodname(self):
739 return self._methodname
740
741 #
742 # event handlers
743
744 def xml(self, encoding, standalone):
745 self._encoding = encoding
746 # FIXME: assert standalone == 1 ???
747
748 def start(self, tag, attrs):
749 # prepare to handle this element
750 if tag == "array" or tag == "struct":
751 self._marks.append(len(self._stack))
752 self._data = []
753 self._value = (tag == "value")
754
755 def data(self, text):
756 self._data.append(text)
757
Neal Norwitzff113342007-04-17 08:42:15 +0000758 def end(self, tag):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000759 # call the appropriate end tag handler
760 try:
761 f = self.dispatch[tag]
762 except KeyError:
763 pass # unknown tag ?
764 else:
Neal Norwitzff113342007-04-17 08:42:15 +0000765 return f(self, "".join(self._data))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000766
767 #
768 # accelerator support
769
770 def end_dispatch(self, tag, data):
771 # dispatch data
772 try:
773 f = self.dispatch[tag]
774 except KeyError:
775 pass # unknown tag ?
776 else:
777 return f(self, data)
778
779 #
780 # element decoders
781
782 dispatch = {}
783
Andrew M. Kuchlinga4c2b742003-04-25 00:26:51 +0000784 def end_nil (self, data):
785 self.append(None)
786 self._value = 0
787 dispatch["nil"] = end_nil
Tim Petersc2659cf2003-05-12 20:19:37 +0000788
Fredrik Lundh1538c232001-10-01 19:42:03 +0000789 def end_boolean(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000790 if data == "0":
791 self.append(False)
792 elif data == "1":
793 self.append(True)
794 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000795 raise TypeError("bad boolean value")
Fredrik Lundhb9056332001-07-11 17:42:21 +0000796 self._value = 0
797 dispatch["boolean"] = end_boolean
798
Fredrik Lundh1538c232001-10-01 19:42:03 +0000799 def end_int(self, data):
800 self.append(int(data))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000801 self._value = 0
802 dispatch["i4"] = end_int
Georg Brandlf08a9dd2008-06-10 16:57:31 +0000803 dispatch["i8"] = end_int
Fredrik Lundhb9056332001-07-11 17:42:21 +0000804 dispatch["int"] = end_int
805
Fredrik Lundh1538c232001-10-01 19:42:03 +0000806 def end_double(self, data):
807 self.append(float(data))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000808 self._value = 0
809 dispatch["double"] = end_double
810
Fredrik Lundh1538c232001-10-01 19:42:03 +0000811 def end_string(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000812 if self._encoding:
813 data = _decode(data, self._encoding)
814 self.append(_stringify(data))
815 self._value = 0
816 dispatch["string"] = end_string
817 dispatch["name"] = end_string # struct keys are always strings
818
819 def end_array(self, data):
Raymond Hettinger46ac8eb2002-06-30 03:39:14 +0000820 mark = self._marks.pop()
Fredrik Lundhb9056332001-07-11 17:42:21 +0000821 # map arrays to Python lists
822 self._stack[mark:] = [self._stack[mark:]]
823 self._value = 0
824 dispatch["array"] = end_array
825
826 def end_struct(self, data):
Raymond Hettinger46ac8eb2002-06-30 03:39:14 +0000827 mark = self._marks.pop()
Fredrik Lundhb9056332001-07-11 17:42:21 +0000828 # map structs to Python dictionaries
829 dict = {}
830 items = self._stack[mark:]
831 for i in range(0, len(items), 2):
832 dict[_stringify(items[i])] = items[i+1]
833 self._stack[mark:] = [dict]
834 self._value = 0
835 dispatch["struct"] = end_struct
836
Fredrik Lundh1538c232001-10-01 19:42:03 +0000837 def end_base64(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000838 value = Binary()
Guido van Rossum54a40cb2007-08-27 22:27:41 +0000839 value.decode(data.encode("ascii"))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000840 self.append(value)
841 self._value = 0
842 dispatch["base64"] = end_base64
843
Fredrik Lundh1538c232001-10-01 19:42:03 +0000844 def end_dateTime(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000845 value = DateTime()
Fredrik Lundh1538c232001-10-01 19:42:03 +0000846 value.decode(data)
Skip Montanaro174dd222005-05-14 20:54:16 +0000847 if self._use_datetime:
848 value = _datetime_type(data)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000849 self.append(value)
850 dispatch["dateTime.iso8601"] = end_dateTime
851
852 def end_value(self, data):
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000853 # if we stumble upon a value element with no internal
Fredrik Lundhb9056332001-07-11 17:42:21 +0000854 # elements, treat it as a string element
855 if self._value:
856 self.end_string(data)
857 dispatch["value"] = end_value
858
859 def end_params(self, data):
860 self._type = "params"
861 dispatch["params"] = end_params
862
863 def end_fault(self, data):
864 self._type = "fault"
865 dispatch["fault"] = end_fault
866
Fredrik Lundh1538c232001-10-01 19:42:03 +0000867 def end_methodName(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000868 if self._encoding:
869 data = _decode(data, self._encoding)
870 self._methodname = data
871 self._type = "methodName" # no params
872 dispatch["methodName"] = end_methodName
873
Martin v. Löwis45394c22003-10-31 13:49:36 +0000874## Multicall support
875#
Fredrik Lundhb9056332001-07-11 17:42:21 +0000876
Martin v. Löwis45394c22003-10-31 13:49:36 +0000877class _MultiCallMethod:
878 # some lesser magic to store calls made to a MultiCall object
879 # for batch execution
880 def __init__(self, call_list, name):
881 self.__call_list = call_list
882 self.__name = name
883 def __getattr__(self, name):
884 return _MultiCallMethod(self.__call_list, "%s.%s" % (self.__name, name))
885 def __call__(self, *args):
886 self.__call_list.append((self.__name, args))
887
Martin v. Löwis12237b32004-08-22 16:04:50 +0000888class MultiCallIterator:
Martin v. Löwis45394c22003-10-31 13:49:36 +0000889 """Iterates over the results of a multicall. Exceptions are
890 thrown in response to xmlrpc faults."""
Raymond Hettingercc523fc2003-11-02 09:47:05 +0000891
Martin v. Löwis12237b32004-08-22 16:04:50 +0000892 def __init__(self, results):
893 self.results = results
894
895 def __getitem__(self, i):
896 item = self.results[i]
897 if type(item) == type({}):
898 raise Fault(item['faultCode'], item['faultString'])
899 elif type(item) == type([]):
900 return item[0]
Martin v. Löwis45394c22003-10-31 13:49:36 +0000901 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000902 raise ValueError("unexpected type in multicall result")
Raymond Hettingercc523fc2003-11-02 09:47:05 +0000903
Martin v. Löwis45394c22003-10-31 13:49:36 +0000904class MultiCall:
905 """server -> a object used to boxcar method calls
906
907 server should be a ServerProxy object.
908
909 Methods can be added to the MultiCall using normal
910 method call syntax e.g.:
911
912 multicall = MultiCall(server_proxy)
913 multicall.add(2,3)
914 multicall.get_address("Guido")
915
916 To execute the multicall, call the MultiCall object e.g.:
917
918 add_result, address = multicall()
919 """
Raymond Hettingercc523fc2003-11-02 09:47:05 +0000920
Martin v. Löwis45394c22003-10-31 13:49:36 +0000921 def __init__(self, server):
922 self.__server = server
923 self.__call_list = []
Raymond Hettingercc523fc2003-11-02 09:47:05 +0000924
Martin v. Löwis45394c22003-10-31 13:49:36 +0000925 def __repr__(self):
926 return "<MultiCall at %x>" % id(self)
Raymond Hettingercc523fc2003-11-02 09:47:05 +0000927
Martin v. Löwis45394c22003-10-31 13:49:36 +0000928 __str__ = __repr__
929
930 def __getattr__(self, name):
931 return _MultiCallMethod(self.__call_list, name)
932
933 def __call__(self):
934 marshalled_list = []
935 for name, args in self.__call_list:
936 marshalled_list.append({'methodName' : name, 'params' : args})
937
938 return MultiCallIterator(self.__server.system.multicall(marshalled_list))
Raymond Hettingercc523fc2003-11-02 09:47:05 +0000939
Fredrik Lundhb9056332001-07-11 17:42:21 +0000940# --------------------------------------------------------------------
941# convenience functions
942
Georg Brandl38eceaa2008-05-26 11:14:17 +0000943FastMarshaller = FastParser = FastUnmarshaller = None
944
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000945##
946# Create a parser object, and connect it to an unmarshalling instance.
947# This function picks the fastest available XML parser.
948#
949# return A (parser, unmarshaller) tuple.
950
Skip Montanaro174dd222005-05-14 20:54:16 +0000951def getparser(use_datetime=0):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000952 """getparser() -> parser, unmarshaller
953
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000954 Create an instance of the fastest available parser, and attach it
955 to an unmarshalling object. Return both objects.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000956 """
Skip Montanaro174dd222005-05-14 20:54:16 +0000957 if use_datetime and not datetime:
Collin Winterce36ad82007-08-30 01:19:48 +0000958 raise ValueError("the datetime module is not available")
Fredrik Lundhb9056332001-07-11 17:42:21 +0000959 if FastParser and FastUnmarshaller:
Skip Montanaro174dd222005-05-14 20:54:16 +0000960 if use_datetime:
961 mkdatetime = _datetime_type
962 else:
963 mkdatetime = _datetime
964 target = FastUnmarshaller(True, False, _binary, mkdatetime, Fault)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000965 parser = FastParser(target)
966 else:
Skip Montanaro174dd222005-05-14 20:54:16 +0000967 target = Unmarshaller(use_datetime=use_datetime)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000968 if FastParser:
969 parser = FastParser(target)
970 elif SgmlopParser:
971 parser = SgmlopParser(target)
972 elif ExpatParser:
973 parser = ExpatParser(target)
974 else:
975 parser = SlowParser(target)
976 return parser, target
977
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000978##
979# Convert a Python tuple or a Fault instance to an XML-RPC packet.
980#
981# @def dumps(params, **options)
982# @param params A tuple or Fault instance.
983# @keyparam methodname If given, create a methodCall request for
984# this method name.
985# @keyparam methodresponse If given, create a methodResponse packet.
986# If used with a tuple, the tuple must be a singleton (that is,
987# it must contain exactly one element).
988# @keyparam encoding The packet encoding.
989# @return A string containing marshalled data.
990
Andrew M. Kuchlinga4c2b742003-04-25 00:26:51 +0000991def dumps(params, methodname=None, methodresponse=None, encoding=None,
992 allow_none=0):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000993 """data [,options] -> marshalled data
994
995 Convert an argument tuple or a Fault instance to an XML-RPC
996 request (or response, if the methodresponse option is used).
997
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000998 In addition to the data object, the following options can be given
999 as keyword arguments:
Fredrik Lundhb9056332001-07-11 17:42:21 +00001000
1001 methodname: the method name for a methodCall packet
1002
1003 methodresponse: true to create a methodResponse packet.
1004 If this option is used with a tuple, the tuple must be
1005 a singleton (i.e. it can contain only one element).
1006
1007 encoding: the packet encoding (default is UTF-8)
1008
1009 All 8-bit strings in the data structure are assumed to use the
1010 packet encoding. Unicode strings are automatically converted,
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +00001011 where necessary.
Fredrik Lundhb9056332001-07-11 17:42:21 +00001012 """
1013
Guido van Rossum13257902007-06-07 23:15:56 +00001014 assert isinstance(params, (tuple, Fault)), "argument must be tuple or Fault instance"
Fredrik Lundhb9056332001-07-11 17:42:21 +00001015 if isinstance(params, Fault):
1016 methodresponse = 1
Guido van Rossum13257902007-06-07 23:15:56 +00001017 elif methodresponse and isinstance(params, tuple):
Fredrik Lundhb9056332001-07-11 17:42:21 +00001018 assert len(params) == 1, "response tuple must be a singleton"
1019
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001020 if not encoding:
Fredrik Lundhb9056332001-07-11 17:42:21 +00001021 encoding = "utf-8"
1022
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001023 if FastMarshaller:
1024 m = FastMarshaller(encoding)
1025 else:
Andrew M. Kuchlinga4c2b742003-04-25 00:26:51 +00001026 m = Marshaller(encoding, allow_none)
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001027
Fredrik Lundhb9056332001-07-11 17:42:21 +00001028 data = m.dumps(params)
1029
1030 if encoding != "utf-8":
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001031 xmlheader = "<?xml version='1.0' encoding='%s'?>\n" % str(encoding)
Fredrik Lundhb9056332001-07-11 17:42:21 +00001032 else:
1033 xmlheader = "<?xml version='1.0'?>\n" # utf-8 is default
1034
1035 # standard XML-RPC wrappings
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001036 if methodname:
Fredrik Lundhb9056332001-07-11 17:42:21 +00001037 # a method call
Guido van Rossum3172c5d2007-10-16 18:12:55 +00001038 if not isinstance(methodname, str):
Fredrik Lundhb9056332001-07-11 17:42:21 +00001039 methodname = methodname.encode(encoding)
1040 data = (
1041 xmlheader,
1042 "<methodCall>\n"
1043 "<methodName>", methodname, "</methodName>\n",
1044 data,
1045 "</methodCall>\n"
1046 )
1047 elif methodresponse:
1048 # a method response, or a fault structure
1049 data = (
1050 xmlheader,
1051 "<methodResponse>\n",
1052 data,
1053 "</methodResponse>\n"
1054 )
1055 else:
1056 return data # return as is
Neal Norwitzff113342007-04-17 08:42:15 +00001057 return "".join(data)
Fredrik Lundhb9056332001-07-11 17:42:21 +00001058
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001059##
1060# Convert an XML-RPC packet to a Python object. If the XML-RPC packet
1061# represents a fault condition, this function raises a Fault exception.
1062#
1063# @param data An XML-RPC packet, given as an 8-bit string.
Walter Dörwaldf0dfc7a2003-10-20 14:01:56 +00001064# @return A tuple containing the unpacked data, and the method name
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001065# (None if not present).
1066# @see Fault
1067
Skip Montanaro174dd222005-05-14 20:54:16 +00001068def loads(data, use_datetime=0):
Fredrik Lundhb9056332001-07-11 17:42:21 +00001069 """data -> unmarshalled data, method name
1070
1071 Convert an XML-RPC packet to unmarshalled data plus a method
1072 name (None if not present).
1073
1074 If the XML-RPC packet represents a fault condition, this function
1075 raises a Fault exception.
1076 """
Skip Montanaro174dd222005-05-14 20:54:16 +00001077 p, u = getparser(use_datetime=use_datetime)
Fredrik Lundhb9056332001-07-11 17:42:21 +00001078 p.feed(data)
1079 p.close()
1080 return u.close(), u.getmethodname()
1081
1082
1083# --------------------------------------------------------------------
1084# request dispatcher
1085
1086class _Method:
1087 # some magic to bind an XML-RPC method to an RPC server.
1088 # supports "nested" methods (e.g. examples.getStateName)
1089 def __init__(self, send, name):
1090 self.__send = send
1091 self.__name = name
1092 def __getattr__(self, name):
1093 return _Method(self.__send, "%s.%s" % (self.__name, name))
1094 def __call__(self, *args):
1095 return self.__send(self.__name, args)
1096
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001097##
1098# Standard transport class for XML-RPC over HTTP.
1099# <p>
1100# You can create custom transports by subclassing this method, and
1101# overriding selected methods.
Fredrik Lundhb9056332001-07-11 17:42:21 +00001102
1103class Transport:
Fredrik Lundhc4c062f2001-09-10 19:45:02 +00001104 """Handles an HTTP transaction to an XML-RPC server."""
Fredrik Lundhb9056332001-07-11 17:42:21 +00001105
1106 # client identifier (may be overridden)
1107 user_agent = "xmlrpclib.py/%s (by www.pythonware.com)" % __version__
1108
Skip Montanaro174dd222005-05-14 20:54:16 +00001109 def __init__(self, use_datetime=0):
1110 self._use_datetime = use_datetime
1111
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001112 ##
1113 # Send a complete request, and parse the response.
1114 #
1115 # @param host Target host.
1116 # @param handler Target PRC handler.
1117 # @param request_body XML-RPC request body.
1118 # @param verbose Debugging flag.
1119 # @return Parsed response.
1120
Fredrik Lundhb9056332001-07-11 17:42:21 +00001121 def request(self, host, handler, request_body, verbose=0):
1122 # issue XML-RPC request
1123
Jeremy Hylton5d8a88a2007-08-14 16:47:39 +00001124 http_conn = self.send_request(host, handler, request_body, verbose)
1125 resp = http_conn.getresponse()
Fredrik Lundhb9056332001-07-11 17:42:21 +00001126
Jeremy Hylton5d8a88a2007-08-14 16:47:39 +00001127 if resp.status != 200:
Fredrik Lundhb9056332001-07-11 17:42:21 +00001128 raise ProtocolError(
1129 host + handler,
Hye-Shik Chang96042862007-08-19 10:49:11 +00001130 resp.status, resp.reason,
Guido van Rossumcfe02a42007-08-22 23:45:42 +00001131 dict(resp.getheaders())
Fredrik Lundhb9056332001-07-11 17:42:21 +00001132 )
1133
1134 self.verbose = verbose
1135
Jeremy Hylton5d8a88a2007-08-14 16:47:39 +00001136 return self._parse_response(resp, None)
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001137
1138 ##
1139 # Create parser.
1140 #
1141 # @return A 2-tuple containing a parser and a unmarshaller.
Fredrik Lundhb9056332001-07-11 17:42:21 +00001142
Fredrik Lundhc4c062f2001-09-10 19:45:02 +00001143 def getparser(self):
1144 # get parser and unmarshaller
Skip Montanaro174dd222005-05-14 20:54:16 +00001145 return getparser(use_datetime=self._use_datetime)
Fredrik Lundhc4c062f2001-09-10 19:45:02 +00001146
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001147 ##
Fredrik Lundh1303c7c2002-10-22 18:23:00 +00001148 # Get authorization info from host parameter
1149 # Host may be a string, or a (host, x509-dict) tuple; if a string,
1150 # it is checked for a "user:pw@host" format, and a "Basic
1151 # Authentication" header is added if appropriate.
1152 #
1153 # @param host Host descriptor (URL or (URL, x509 info) tuple).
1154 # @return A 3-tuple containing (actual host, extra headers,
1155 # x509 info). The header and x509 fields may be None.
1156
1157 def get_host_info(self, host):
1158
1159 x509 = {}
Guido van Rossum13257902007-06-07 23:15:56 +00001160 if isinstance(host, tuple):
Fredrik Lundh1303c7c2002-10-22 18:23:00 +00001161 host, x509 = host
1162
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001163 import urllib.parse
1164 auth, host = urllib.parse.splituser(host)
Fredrik Lundh1303c7c2002-10-22 18:23:00 +00001165
1166 if auth:
1167 import base64
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001168 auth = base64.encodestring(urllib.parse.unquote(auth))
Neal Norwitzff113342007-04-17 08:42:15 +00001169 auth = "".join(auth.split()) # get rid of whitespace
Fredrik Lundh1303c7c2002-10-22 18:23:00 +00001170 extra_headers = [
1171 ("Authorization", "Basic " + auth)
1172 ]
1173 else:
1174 extra_headers = None
1175
1176 return host, extra_headers, x509
1177
1178 ##
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001179 # Connect to server.
1180 #
1181 # @param host Target host.
Jeremy Hylton5d8a88a2007-08-14 16:47:39 +00001182 # @return An HTTPConnection object
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001183
Fredrik Lundhb9056332001-07-11 17:42:21 +00001184 def make_connection(self, host):
1185 # create a HTTP connection object from a host descriptor
Fredrik Lundh1303c7c2002-10-22 18:23:00 +00001186 host, extra_headers, x509 = self.get_host_info(host)
Jeremy Hylton5d8a88a2007-08-14 16:47:39 +00001187
Fredrik Lundhb9056332001-07-11 17:42:21 +00001188
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001189 ##
Jeremy Hylton5d8a88a2007-08-14 16:47:39 +00001190 # Send HTTP request.
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001191 #
Jeremy Hylton5d8a88a2007-08-14 16:47:39 +00001192 # @param host Host descriptor (URL or (URL, x509 info) tuple).
1193 # @param handler Targer RPC handler (a path relative to host)
1194 # @param request_body The XML-RPC request body
1195 # @param debug Enable debugging if debug is true.
1196 # @return An HTTPConnection.
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001197
Jeremy Hylton5d8a88a2007-08-14 16:47:39 +00001198 def send_request(self, host, handler, request_body, debug):
Fredrik Lundh1303c7c2002-10-22 18:23:00 +00001199 host, extra_headers, x509 = self.get_host_info(host)
Georg Brandl24420152008-05-26 16:32:26 +00001200 connection = http.client.HTTPConnection(host)
Jeremy Hylton5d8a88a2007-08-14 16:47:39 +00001201 if debug:
1202 connection.set_debuglevel(1)
1203 headers = {}
Fredrik Lundh1303c7c2002-10-22 18:23:00 +00001204 if extra_headers:
Jeremy Hylton5d8a88a2007-08-14 16:47:39 +00001205 for key, val in extra_headers:
Amaury Forgeot d'Arc41e36672008-08-20 21:35:50 +00001206 headers[key] = val
Jeremy Hylton5d8a88a2007-08-14 16:47:39 +00001207 headers["Content-Type"] = "text/xml"
1208 headers["User-Agent"] = self.user_agent
1209 connection.request("POST", handler, request_body, headers)
1210 return connection
Fredrik Lundhb9056332001-07-11 17:42:21 +00001211
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001212 ##
1213 # Parse response.
1214 #
1215 # @param file Stream.
1216 # @return Response tuple and target method.
1217
1218 def parse_response(self, file):
1219 # compatibility interface
1220 return self._parse_response(file, None)
1221
1222 ##
1223 # Parse response (alternate interface). This is similar to the
1224 # parse_response method, but also provides direct access to the
1225 # underlying socket object (where available).
1226 #
1227 # @param file Stream.
1228 # @param sock Socket handle (or None, if the socket object
1229 # could not be accessed).
1230 # @return Response tuple and target method.
1231
1232 def _parse_response(self, file, sock):
1233 # read response from input file/socket, and parse it
Fredrik Lundhb9056332001-07-11 17:42:21 +00001234
Fredrik Lundhc4c062f2001-09-10 19:45:02 +00001235 p, u = self.getparser()
Fredrik Lundhb9056332001-07-11 17:42:21 +00001236
1237 while 1:
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001238 if sock:
1239 response = sock.recv(1024)
1240 else:
1241 response = file.read(1024)
Fredrik Lundhb9056332001-07-11 17:42:21 +00001242 if not response:
1243 break
1244 if self.verbose:
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001245 print("body:", repr(response))
Fredrik Lundhb9056332001-07-11 17:42:21 +00001246 p.feed(response)
1247
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001248 file.close()
Fredrik Lundhb9056332001-07-11 17:42:21 +00001249 p.close()
1250
1251 return u.close()
1252
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001253##
1254# Standard transport class for XML-RPC over HTTPS.
1255
Fredrik Lundhb9056332001-07-11 17:42:21 +00001256class SafeTransport(Transport):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +00001257 """Handles an HTTPS transaction to an XML-RPC server."""
Fredrik Lundhb9056332001-07-11 17:42:21 +00001258
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001259 # FIXME: mostly untested
1260
Jeremy Hylton5d8a88a2007-08-14 16:47:39 +00001261 def send_request(self, host, handler, request_body, debug):
1262 import socket
1263 if not hasattr(socket, "ssl"):
Fredrik Lundh1303c7c2002-10-22 18:23:00 +00001264 raise NotImplementedError(
Georg Brandl24420152008-05-26 16:32:26 +00001265 "your version of http.client doesn't support HTTPS")
Jeremy Hylton5d8a88a2007-08-14 16:47:39 +00001266
1267 host, extra_headers, x509 = self.get_host_info(host)
Georg Brandl24420152008-05-26 16:32:26 +00001268 connection = http.client.HTTPSConnection(host, None, **(x509 or {}))
Jeremy Hylton5d8a88a2007-08-14 16:47:39 +00001269 if debug:
1270 connection.set_debuglevel(1)
1271 headers = {}
1272 if extra_headers:
1273 for key, val in extra_headers:
Amaury Forgeot d'Arc41e36672008-08-20 21:35:50 +00001274 headers[key] = val
Jeremy Hylton5d8a88a2007-08-14 16:47:39 +00001275 headers["Content-Type"] = "text/xml"
1276 headers["User-Agent"] = self.user_agent
1277 connection.request("POST", handler, request_body, headers)
1278 return connection
Fredrik Lundhb9056332001-07-11 17:42:21 +00001279
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001280##
1281# Standard server proxy. This class establishes a virtual connection
1282# to an XML-RPC server.
1283# <p>
1284# This class is available as ServerProxy and Server. New code should
1285# use ServerProxy, to avoid confusion.
1286#
1287# @def ServerProxy(uri, **options)
1288# @param uri The connection point on the server.
1289# @keyparam transport A transport factory, compatible with the
1290# standard transport class.
1291# @keyparam encoding The default encoding used for 8-bit strings
1292# (default is UTF-8).
1293# @keyparam verbose Use a true value to enable debugging output.
1294# (printed to standard output).
1295# @see Transport
1296
Fredrik Lundhb9056332001-07-11 17:42:21 +00001297class ServerProxy:
1298 """uri [,options] -> a logical connection to an XML-RPC server
1299
1300 uri is the connection point on the server, given as
1301 scheme://host/target.
1302
1303 The standard implementation always supports the "http" scheme. If
1304 SSL socket support is available (Python 2.0), it also supports
1305 "https".
1306
1307 If the target part and the slash preceding it are both omitted,
1308 "/RPC2" is assumed.
1309
1310 The following options can be given as keyword arguments:
1311
1312 transport: a transport factory
1313 encoding: the request encoding (default is UTF-8)
1314
1315 All 8-bit strings passed to the server proxy are assumed to use
1316 the given encoding.
1317 """
1318
Andrew M. Kuchlinga4c2b742003-04-25 00:26:51 +00001319 def __init__(self, uri, transport=None, encoding=None, verbose=0,
Skip Montanaro174dd222005-05-14 20:54:16 +00001320 allow_none=0, use_datetime=0):
Fredrik Lundhb9056332001-07-11 17:42:21 +00001321 # establish a "logical" server connection
1322
1323 # get the url
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001324 import urllib.parse
1325 type, uri = urllib.parse.splittype(uri)
Fredrik Lundhb9056332001-07-11 17:42:21 +00001326 if type not in ("http", "https"):
Collin Winterce36ad82007-08-30 01:19:48 +00001327 raise IOError("unsupported XML-RPC protocol")
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001328 self.__host, self.__handler = urllib.parse.splithost(uri)
Fredrik Lundhb9056332001-07-11 17:42:21 +00001329 if not self.__handler:
1330 self.__handler = "/RPC2"
1331
1332 if transport is None:
1333 if type == "https":
Skip Montanaro174dd222005-05-14 20:54:16 +00001334 transport = SafeTransport(use_datetime=use_datetime)
Fredrik Lundhb9056332001-07-11 17:42:21 +00001335 else:
Skip Montanaro174dd222005-05-14 20:54:16 +00001336 transport = Transport(use_datetime=use_datetime)
Fredrik Lundhb9056332001-07-11 17:42:21 +00001337 self.__transport = transport
1338
1339 self.__encoding = encoding
1340 self.__verbose = verbose
Andrew M. Kuchlinga4c2b742003-04-25 00:26:51 +00001341 self.__allow_none = allow_none
Tim Petersc2659cf2003-05-12 20:19:37 +00001342
Fredrik Lundhb9056332001-07-11 17:42:21 +00001343 def __request(self, methodname, params):
1344 # call a method on the remote server
1345
Andrew M. Kuchlinga4c2b742003-04-25 00:26:51 +00001346 request = dumps(params, methodname, encoding=self.__encoding,
1347 allow_none=self.__allow_none)
Fredrik Lundhb9056332001-07-11 17:42:21 +00001348
1349 response = self.__transport.request(
1350 self.__host,
1351 self.__handler,
1352 request,
1353 verbose=self.__verbose
1354 )
1355
1356 if len(response) == 1:
1357 response = response[0]
1358
1359 return response
1360
1361 def __repr__(self):
1362 return (
Fredrik Lundh78eedce2001-08-23 20:04:33 +00001363 "<ServerProxy for %s%s>" %
Fredrik Lundhb9056332001-07-11 17:42:21 +00001364 (self.__host, self.__handler)
1365 )
1366
1367 __str__ = __repr__
Raymond Hettingercc523fc2003-11-02 09:47:05 +00001368
Fredrik Lundhb9056332001-07-11 17:42:21 +00001369 def __getattr__(self, name):
1370 # magic method dispatcher
1371 return _Method(self.__request, name)
1372
1373 # note: to call a remote object with an non-standard name, use
1374 # result getattr(server, "strange-python-name")(args)
1375
Fredrik Lundh78eedce2001-08-23 20:04:33 +00001376# compatibility
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001377
Fredrik Lundhb9056332001-07-11 17:42:21 +00001378Server = ServerProxy
1379
1380# --------------------------------------------------------------------
1381# test code
1382
1383if __name__ == "__main__":
1384
1385 # simple test program (from the XML-RPC specification)
1386
Fredrik Lundh78eedce2001-08-23 20:04:33 +00001387 # server = ServerProxy("http://localhost:8000") # local server
Martin v. Löwis12237b32004-08-22 16:04:50 +00001388 server = ServerProxy("http://time.xmlrpc.com/RPC2")
Fredrik Lundhb9056332001-07-11 17:42:21 +00001389
Fredrik Lundhb9056332001-07-11 17:42:21 +00001390 try:
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001391 print(server.currentTime.getCurrentTime())
Guido van Rossumb940e112007-01-10 16:19:56 +00001392 except Error as v:
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001393 print("ERROR", v)
Martin v. Löwis12237b32004-08-22 16:04:50 +00001394
Jeremy Hylton5d8a88a2007-08-14 16:47:39 +00001395 # The server at xmlrpc.com doesn't seem to support multicall anymore.
Martin v. Löwis12237b32004-08-22 16:04:50 +00001396 multi = MultiCall(server)
1397 multi.currentTime.getCurrentTime()
1398 multi.currentTime.getCurrentTime()
1399 try:
1400 for response in multi():
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001401 print(response)
Guido van Rossumb940e112007-01-10 16:19:56 +00001402 except Error as v:
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001403 print("ERROR", v)