blob: b1f5a949930150ae569bbd3655e8acd2541c5888 [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
Fred Drake1b410792001-09-04 18:55:03 +000088"""
89An XML-RPC client interface for Python.
90
91The marshalling and response parser code can also be used to
92implement XML-RPC servers.
93
Fred Drake1b410792001-09-04 18:55:03 +000094Exported exceptions:
95
Fredrik Lundhc4c062f2001-09-10 19:45:02 +000096 Error Base class for client errors
97 ProtocolError Indicates an HTTP protocol error
98 ResponseError Indicates a broken response package
99 Fault Indicates an XML-RPC fault package
Fred Drake1b410792001-09-04 18:55:03 +0000100
101Exported classes:
102
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000103 ServerProxy Represents a logical connection to an XML-RPC server
Fred Drake1b410792001-09-04 18:55:03 +0000104
Raymond Hettingercc523fc2003-11-02 09:47:05 +0000105 MultiCall Executor of boxcared xmlrpc requests
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000106 DateTime dateTime wrapper for an ISO 8601 string or time tuple or
107 localtime integer value to generate a "dateTime.iso8601"
108 XML-RPC value
109 Binary binary data wrapper
Fred Drake1b410792001-09-04 18:55:03 +0000110
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000111 Marshaller Generate an XML-RPC params chunk from a Python data structure
112 Unmarshaller Unmarshal an XML-RPC response from incoming XML event message
113 Transport Handles an HTTP transaction to an XML-RPC server
114 SafeTransport Handles an HTTPS transaction to an XML-RPC server
Fred Drake1b410792001-09-04 18:55:03 +0000115
116Exported constants:
117
Florent Xiclunac4fec932011-10-30 20:19:32 +0100118 (none)
Fred Drake1b410792001-09-04 18:55:03 +0000119
120Exported functions:
121
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000122 getparser Create instance of the fastest available parser & attach
123 to an unmarshalling object
124 dumps Convert an argument tuple or a Fault instance to an XML-RPC
125 request (or response, if the methodresponse option is used).
126 loads Convert an XML-RPC packet to unmarshalled data plus a method
127 name (None if not present).
Fred Drake1b410792001-09-04 18:55:03 +0000128"""
129
Florent Xiclunac4fec932011-10-30 20:19:32 +0100130import base64
Florent Xicluna75861df2011-10-30 20:39:24 +0100131import sys
Florent Xiclunac4fec932011-10-30 20:19:32 +0100132import time
Florent Xiclunab6f019a2011-10-30 23:54:17 +0100133from datetime import datetime
Georg Brandl24420152008-05-26 16:32:26 +0000134import http.client
Georg Brandlcef803f2009-06-04 09:04:53 +0000135from xml.parsers import expat
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000136import socket
137import errno
138from io import BytesIO
Kristján Valur Jónssonaefde242009-07-19 22:29:24 +0000139try:
140 import gzip
141except ImportError:
142 gzip = None #python can be built without zlib/gzip support
Fredrik Lundh1538c232001-10-01 19:42:03 +0000143
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000144# --------------------------------------------------------------------
145# Internal stuff
146
Neal Norwitzff113342007-04-17 08:42:15 +0000147def escape(s):
148 s = s.replace("&", "&")
149 s = s.replace("<", "&lt;")
150 return s.replace(">", "&gt;",)
Fredrik Lundh1538c232001-10-01 19:42:03 +0000151
Florent Xicluna75861df2011-10-30 20:39:24 +0100152# used in User-Agent header sent
153__version__ = sys.version[:3]
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000154
155# xmlrpc integer limits
Guido van Rossume2a383d2007-01-15 16:59:06 +0000156MAXINT = 2**31-1
157MININT = -2**31
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000158
159# --------------------------------------------------------------------
160# Error constants (from Dan Libby's specification at
161# http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php)
162
163# Ranges of errors
164PARSE_ERROR = -32700
165SERVER_ERROR = -32600
166APPLICATION_ERROR = -32500
167SYSTEM_ERROR = -32400
168TRANSPORT_ERROR = -32300
169
170# Specific errors
171NOT_WELLFORMED_ERROR = -32700
172UNSUPPORTED_ENCODING = -32701
173INVALID_ENCODING_CHAR = -32702
174INVALID_XMLRPC = -32600
175METHOD_NOT_FOUND = -32601
176INVALID_METHOD_PARAMS = -32602
177INTERNAL_ERROR = -32603
Fredrik Lundhb9056332001-07-11 17:42:21 +0000178
179# --------------------------------------------------------------------
180# Exceptions
181
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000182##
183# Base class for all kinds of client-side errors.
184
Fredrik Lundh78eedce2001-08-23 20:04:33 +0000185class Error(Exception):
Fred Drake1b410792001-09-04 18:55:03 +0000186 """Base class for client errors."""
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000187 def __str__(self):
188 return repr(self)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000189
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000190##
191# Indicates an HTTP-level protocol error. This is raised by the HTTP
192# transport layer, if the server returns an error code other than 200
193# (OK).
194#
195# @param url The target URL.
196# @param errcode The HTTP error code.
197# @param errmsg The HTTP error message.
198# @param headers The HTTP header dictionary.
199
Fredrik Lundhb9056332001-07-11 17:42:21 +0000200class ProtocolError(Error):
Fred Drake1b410792001-09-04 18:55:03 +0000201 """Indicates an HTTP protocol error."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000202 def __init__(self, url, errcode, errmsg, headers):
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000203 Error.__init__(self)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000204 self.url = url
205 self.errcode = errcode
206 self.errmsg = errmsg
207 self.headers = headers
208 def __repr__(self):
209 return (
210 "<ProtocolError for %s: %s %s>" %
211 (self.url, self.errcode, self.errmsg)
212 )
213
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000214##
215# Indicates a broken XML-RPC response package. This exception is
216# raised by the unmarshalling layer, if the XML-RPC response is
217# malformed.
218
Fredrik Lundhb9056332001-07-11 17:42:21 +0000219class ResponseError(Error):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000220 """Indicates a broken response package."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000221 pass
222
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000223##
224# Indicates an XML-RPC fault response package. This exception is
225# raised by the unmarshalling layer, if the XML-RPC response contains
Florent Xiclunac4fec932011-10-30 20:19:32 +0100226# a fault string. This exception can also be used as a class, to
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000227# generate a fault XML-RPC message.
228#
229# @param faultCode The XML-RPC fault code.
230# @param faultString The XML-RPC fault string.
231
Fredrik Lundhb9056332001-07-11 17:42:21 +0000232class Fault(Error):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000233 """Indicates an XML-RPC fault package."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000234 def __init__(self, faultCode, faultString, **extra):
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000235 Error.__init__(self)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000236 self.faultCode = faultCode
237 self.faultString = faultString
238 def __repr__(self):
Florent Xiclunac4fec932011-10-30 20:19:32 +0100239 return "<Fault %s: %r>" % (self.faultCode, self.faultString)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000240
241# --------------------------------------------------------------------
242# Special values
243
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000244##
Georg Brandl38eceaa2008-05-26 11:14:17 +0000245# Backwards compatibility
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000246
Guido van Rossum47b9ff62006-08-24 00:41:19 +0000247boolean = Boolean = bool
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000248
249##
250# Wrapper for XML-RPC DateTime values. This converts a time value to
251# the format used by XML-RPC.
252# <p>
253# The value can be given as a string in the format
254# "yyyymmddThh:mm:ss", as a 9-item time tuple (as returned by
255# time.localtime()), or an integer value (as returned by time.time()).
256# The wrapper uses time.localtime() to convert an integer to a time
257# tuple.
258#
259# @param value The time, given as an ISO 8601 string, a time
260# tuple, or a integer time value.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000261
Christian Heimesdae2a892008-04-19 00:55:37 +0000262def _strftime(value):
Florent Xiclunab6f019a2011-10-30 23:54:17 +0100263 if isinstance(value, datetime):
264 return value.strftime("%Y%m%dT%H:%M:%S")
Christian Heimesdae2a892008-04-19 00:55:37 +0000265
266 if not isinstance(value, (tuple, time.struct_time)):
267 if value == 0:
268 value = time.time()
269 value = time.localtime(value)
270
271 return "%04d%02d%02dT%02d:%02d:%02d" % value[:6]
272
Fredrik Lundhb9056332001-07-11 17:42:21 +0000273class DateTime:
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000274 """DateTime wrapper for an ISO 8601 string or time tuple or
275 localtime integer value to generate 'dateTime.iso8601' XML-RPC
276 value.
277 """
Fredrik Lundhb9056332001-07-11 17:42:21 +0000278
279 def __init__(self, value=0):
Christian Heimesdae2a892008-04-19 00:55:37 +0000280 if isinstance(value, str):
281 self.value = value
282 else:
283 self.value = _strftime(value)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000284
Christian Heimes05e8be12008-02-23 18:30:17 +0000285 def make_comparable(self, other):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000286 if isinstance(other, DateTime):
Christian Heimes05e8be12008-02-23 18:30:17 +0000287 s = self.value
288 o = other.value
Florent Xiclunab6f019a2011-10-30 23:54:17 +0100289 elif isinstance(other, datetime):
Christian Heimes05e8be12008-02-23 18:30:17 +0000290 s = self.value
291 o = other.strftime("%Y%m%dT%H:%M:%S")
Florent Xicluna3fa29f72011-10-30 20:18:50 +0100292 elif isinstance(other, str):
Christian Heimes05e8be12008-02-23 18:30:17 +0000293 s = self.value
294 o = other
295 elif hasattr(other, "timetuple"):
296 s = self.timetuple()
297 o = other.timetuple()
298 else:
299 otype = (hasattr(other, "__class__")
300 and other.__class__.__name__
301 or type(other))
302 raise TypeError("Can't compare %s and %s" %
303 (self.__class__.__name__, otype))
304 return s, o
305
306 def __lt__(self, other):
307 s, o = self.make_comparable(other)
308 return s < o
309
310 def __le__(self, other):
311 s, o = self.make_comparable(other)
312 return s <= o
313
314 def __gt__(self, other):
315 s, o = self.make_comparable(other)
316 return s > o
317
318 def __ge__(self, other):
319 s, o = self.make_comparable(other)
320 return s >= o
321
322 def __eq__(self, other):
323 s, o = self.make_comparable(other)
324 return s == o
Guido van Rossum47b9ff62006-08-24 00:41:19 +0000325
326 def __ne__(self, other):
Christian Heimes05e8be12008-02-23 18:30:17 +0000327 s, o = self.make_comparable(other)
328 return s != o
329
330 def timetuple(self):
331 return time.strptime(self.value, "%Y%m%dT%H:%M:%S")
332
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000333 ##
334 # Get date/time value.
335 #
336 # @return Date/time value, as an ISO 8601 string.
337
338 def __str__(self):
339 return self.value
340
Fredrik Lundhb9056332001-07-11 17:42:21 +0000341 def __repr__(self):
Florent Xiclunac4fec932011-10-30 20:19:32 +0100342 return "<DateTime %r at %x>" % (self.value, id(self))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000343
344 def decode(self, data):
Neal Norwitzff113342007-04-17 08:42:15 +0000345 self.value = str(data).strip()
Fredrik Lundhb9056332001-07-11 17:42:21 +0000346
347 def encode(self, out):
348 out.write("<value><dateTime.iso8601>")
349 out.write(self.value)
350 out.write("</dateTime.iso8601></value>\n")
351
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000352def _datetime(data):
353 # decode xml element contents into a DateTime structure.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000354 value = DateTime()
355 value.decode(data)
356 return value
357
Skip Montanaro174dd222005-05-14 20:54:16 +0000358def _datetime_type(data):
Florent Xiclunab6f019a2011-10-30 23:54:17 +0100359 return datetime.strptime(data, "%Y%m%dT%H:%M:%S")
Skip Montanaro174dd222005-05-14 20:54:16 +0000360
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000361##
362# Wrapper for binary data. This can be used to transport any kind
363# of binary data over XML-RPC, using BASE64 encoding.
364#
365# @param data An 8-bit string containing arbitrary data.
366
Fredrik Lundhb9056332001-07-11 17:42:21 +0000367class Binary:
Fred Drake1b410792001-09-04 18:55:03 +0000368 """Wrapper for binary data."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000369
370 def __init__(self, data=None):
Guido van Rossum54a40cb2007-08-27 22:27:41 +0000371 if data is None:
372 data = b""
373 else:
374 if not isinstance(data, bytes):
375 raise TypeError("expected bytes, not %s" %
376 data.__class__.__name__)
377 data = bytes(data) # Make a copy of the bytes!
Fredrik Lundhb9056332001-07-11 17:42:21 +0000378 self.data = data
379
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000380 ##
381 # Get buffer contents.
382 #
383 # @return Buffer contents, as an 8-bit string.
384
385 def __str__(self):
Guido van Rossum54a40cb2007-08-27 22:27:41 +0000386 return str(self.data, "latin-1") # XXX encoding?!
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000387
Guido van Rossum47b9ff62006-08-24 00:41:19 +0000388 def __eq__(self, other):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000389 if isinstance(other, Binary):
390 other = other.data
Guido van Rossum47b9ff62006-08-24 00:41:19 +0000391 return self.data == other
392
393 def __ne__(self, other):
394 if isinstance(other, Binary):
395 other = other.data
396 return self.data != other
Fredrik Lundhb9056332001-07-11 17:42:21 +0000397
398 def decode(self, data):
Georg Brandlb54d8012009-06-04 09:11:51 +0000399 self.data = base64.decodebytes(data)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000400
401 def encode(self, out):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000402 out.write("<value><base64>\n")
Georg Brandlb54d8012009-06-04 09:11:51 +0000403 encoded = base64.encodebytes(self.data)
Brett Cannon2dbde5e2007-07-30 03:50:35 +0000404 out.write(encoded.decode('ascii'))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000405 out.write("</base64></value>\n")
406
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000407def _binary(data):
408 # decode xml element contents into a Binary structure
Fredrik Lundhb9056332001-07-11 17:42:21 +0000409 value = Binary()
410 value.decode(data)
411 return value
412
Skip Montanaro9a7c96a2003-01-22 18:17:25 +0000413WRAPPERS = (DateTime, Binary)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000414
415# --------------------------------------------------------------------
416# XML parsers
417
Georg Brandlcef803f2009-06-04 09:04:53 +0000418class ExpatParser:
419 # fast expat parser for Python 2.0 and later.
420 def __init__(self, target):
421 self._parser = parser = expat.ParserCreate(None, None)
422 self._target = target
423 parser.StartElementHandler = target.start
424 parser.EndElementHandler = target.end
425 parser.CharacterDataHandler = target.data
426 encoding = None
427 target.xml(encoding, None)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000428
Georg Brandlcef803f2009-06-04 09:04:53 +0000429 def feed(self, data):
430 self._parser.Parse(data, 0)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000431
Georg Brandlcef803f2009-06-04 09:04:53 +0000432 def close(self):
433 self._parser.Parse("", 1) # end of data
434 del self._target, self._parser # get rid of circular references
Fredrik Lundhb9056332001-07-11 17:42:21 +0000435
Fredrik Lundhb9056332001-07-11 17:42:21 +0000436# --------------------------------------------------------------------
437# XML-RPC marshalling and unmarshalling code
438
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000439##
440# XML-RPC marshaller.
441#
442# @param encoding Default encoding for 8-bit strings. The default
443# value is None (interpreted as UTF-8).
444# @see dumps
445
Fredrik Lundhb9056332001-07-11 17:42:21 +0000446class Marshaller:
Fred Drake1b410792001-09-04 18:55:03 +0000447 """Generate an XML-RPC params chunk from a Python data structure.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000448
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000449 Create a Marshaller instance for each set of parameters, and use
450 the "dumps" method to convert your data (represented as a tuple)
451 to an XML-RPC params chunk. To write a fault response, pass a
452 Fault instance instead. You may prefer to use the "dumps" module
453 function for this purpose.
Fred Drake1b410792001-09-04 18:55:03 +0000454 """
Fredrik Lundhb9056332001-07-11 17:42:21 +0000455
456 # by the way, if you don't understand what's going on in here,
457 # that's perfectly ok.
458
Georg Brandlfe991052009-09-16 15:54:04 +0000459 def __init__(self, encoding=None, allow_none=False):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000460 self.memo = {}
461 self.data = None
462 self.encoding = encoding
Andrew M. Kuchlinga4c2b742003-04-25 00:26:51 +0000463 self.allow_none = allow_none
Tim Petersc2659cf2003-05-12 20:19:37 +0000464
Fredrik Lundhb9056332001-07-11 17:42:21 +0000465 dispatch = {}
466
467 def dumps(self, values):
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000468 out = []
469 write = out.append
470 dump = self.__dump
Fredrik Lundhb9056332001-07-11 17:42:21 +0000471 if isinstance(values, Fault):
472 # fault instance
473 write("<fault>\n")
Martin v. Löwis541342f2003-07-12 07:53:04 +0000474 dump({'faultCode': values.faultCode,
475 'faultString': values.faultString},
476 write)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000477 write("</fault>\n")
478 else:
479 # parameter block
Fredrik Lundhc266bb02001-08-23 20:13:08 +0000480 # FIXME: the xml-rpc specification allows us to leave out
481 # the entire <params> block if there are no parameters.
482 # however, changing this may break older code (including
483 # old versions of xmlrpclib.py), so this is better left as
484 # is for now. See @XMLRPC3 for more information. /F
Fredrik Lundhb9056332001-07-11 17:42:21 +0000485 write("<params>\n")
486 for v in values:
487 write("<param>\n")
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000488 dump(v, write)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000489 write("</param>\n")
490 write("</params>\n")
Neal Norwitzff113342007-04-17 08:42:15 +0000491 result = "".join(out)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000492 return result
493
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000494 def __dump(self, value, write):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000495 try:
496 f = self.dispatch[type(value)]
497 except KeyError:
Thomas Wouters89f507f2006-12-13 04:49:30 +0000498 # check if this object can be marshalled as a structure
Florent Xicluna93dfee12011-10-30 20:22:25 +0100499 if not hasattr(value, '__dict__'):
Collin Winterce36ad82007-08-30 01:19:48 +0000500 raise TypeError("cannot marshal %s objects" % type(value))
Thomas Wouters89f507f2006-12-13 04:49:30 +0000501 # check if this class is a sub-class of a basic type,
502 # because we don't know how to marshal these types
503 # (e.g. a string sub-class)
504 for type_ in type(value).__mro__:
505 if type_ in self.dispatch.keys():
Collin Winterce36ad82007-08-30 01:19:48 +0000506 raise TypeError("cannot marshal %s objects" % type(value))
Thomas Wouters89f507f2006-12-13 04:49:30 +0000507 # XXX(twouters): using "_arbitrary_instance" as key as a quick-fix
508 # for the p3yk merge, this should probably be fixed more neatly.
509 f = self.dispatch["_arbitrary_instance"]
510 f(self, value, write)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000511
Andrew M. Kuchlinga4c2b742003-04-25 00:26:51 +0000512 def dump_nil (self, value, write):
513 if not self.allow_none:
Collin Winterce36ad82007-08-30 01:19:48 +0000514 raise TypeError("cannot marshal None unless allow_none is enabled")
Andrew M. Kuchlinga4c2b742003-04-25 00:26:51 +0000515 write("<value><nil/></value>")
Guido van Rossum13257902007-06-07 23:15:56 +0000516 dispatch[type(None)] = dump_nil
Tim Petersc2659cf2003-05-12 20:19:37 +0000517
Georg Brandl38eceaa2008-05-26 11:14:17 +0000518 def dump_bool(self, value, write):
519 write("<value><boolean>")
520 write(value and "1" or "0")
521 write("</boolean></value>\n")
522 dispatch[bool] = dump_bool
Skip Montanaro9a7c96a2003-01-22 18:17:25 +0000523
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000524 def dump_long(self, value, write):
Skip Montanaro5449e082001-10-17 22:53:33 +0000525 if value > MAXINT or value < MININT:
Collin Winterce36ad82007-08-30 01:19:48 +0000526 raise OverflowError("long int exceeds XML-RPC limits")
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000527 write("<value><int>")
528 write(str(int(value)))
529 write("</int></value>\n")
Guido van Rossum13257902007-06-07 23:15:56 +0000530 dispatch[int] = dump_long
Skip Montanaro5e9c71b2001-10-10 15:56:34 +0000531
Florent Xicluna2bb96f52011-10-23 22:11:00 +0200532 # backward compatible
533 dump_int = dump_long
534
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000535 def dump_double(self, value, write):
536 write("<value><double>")
537 write(repr(value))
538 write("</double></value>\n")
Guido van Rossum13257902007-06-07 23:15:56 +0000539 dispatch[float] = dump_double
Fredrik Lundhb9056332001-07-11 17:42:21 +0000540
Guido van Rossum54ad5232007-05-27 09:17:48 +0000541 def dump_unicode(self, value, write, escape=escape):
Guido van Rossum54ad5232007-05-27 09:17:48 +0000542 write("<value><string>")
543 write(escape(value))
544 write("</string></value>\n")
545 dispatch[str] = dump_unicode
Fredrik Lundhb9056332001-07-11 17:42:21 +0000546
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000547 def dump_array(self, value, write):
548 i = id(value)
Guido van Rossume2b70bc2006-08-18 22:13:04 +0000549 if i in self.memo:
Collin Winterce36ad82007-08-30 01:19:48 +0000550 raise TypeError("cannot marshal recursive sequences")
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000551 self.memo[i] = None
Fredrik Lundh1538c232001-10-01 19:42:03 +0000552 dump = self.__dump
Fredrik Lundhb9056332001-07-11 17:42:21 +0000553 write("<value><array><data>\n")
554 for v in value:
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000555 dump(v, write)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000556 write("</data></array></value>\n")
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000557 del self.memo[i]
Guido van Rossum13257902007-06-07 23:15:56 +0000558 dispatch[tuple] = dump_array
559 dispatch[list] = dump_array
Fredrik Lundhb9056332001-07-11 17:42:21 +0000560
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000561 def dump_struct(self, value, write, escape=escape):
562 i = id(value)
Guido van Rossume2b70bc2006-08-18 22:13:04 +0000563 if i in self.memo:
Collin Winterce36ad82007-08-30 01:19:48 +0000564 raise TypeError("cannot marshal recursive dictionaries")
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000565 self.memo[i] = None
Fredrik Lundh1538c232001-10-01 19:42:03 +0000566 dump = self.__dump
Fredrik Lundhb9056332001-07-11 17:42:21 +0000567 write("<value><struct>\n")
Andrew M. Kuchling5962f452004-06-05 12:35:58 +0000568 for k, v in value.items():
Fredrik Lundhb9056332001-07-11 17:42:21 +0000569 write("<member>\n")
Guido van Rossum3172c5d2007-10-16 18:12:55 +0000570 if not isinstance(k, str):
Collin Winterce36ad82007-08-30 01:19:48 +0000571 raise TypeError("dictionary key must be string")
Fredrik Lundh1538c232001-10-01 19:42:03 +0000572 write("<name>%s</name>\n" % escape(k))
Andrew M. Kuchling5962f452004-06-05 12:35:58 +0000573 dump(v, write)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000574 write("</member>\n")
575 write("</struct></value>\n")
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000576 del self.memo[i]
Guido van Rossum13257902007-06-07 23:15:56 +0000577 dispatch[dict] = dump_struct
Fredrik Lundhb9056332001-07-11 17:42:21 +0000578
Florent Xiclunab6f019a2011-10-30 23:54:17 +0100579 def dump_datetime(self, value, write):
580 write("<value><dateTime.iso8601>")
581 write(_strftime(value))
582 write("</dateTime.iso8601></value>\n")
583 dispatch[datetime] = dump_datetime
Fred Drakeba613c32005-02-10 18:33:30 +0000584
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000585 def dump_instance(self, value, write):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000586 # check for special wrappers
587 if value.__class__ in WRAPPERS:
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000588 self.write = write
Fredrik Lundhb9056332001-07-11 17:42:21 +0000589 value.encode(self)
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000590 del self.write
Fredrik Lundhb9056332001-07-11 17:42:21 +0000591 else:
592 # store instance attributes as a struct (really?)
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000593 self.dump_struct(value.__dict__, write)
Guido van Rossume4dea982006-04-21 13:45:00 +0000594 dispatch[DateTime] = dump_instance
595 dispatch[Binary] = dump_instance
Thomas Wouters89f507f2006-12-13 04:49:30 +0000596 # XXX(twouters): using "_arbitrary_instance" as key as a quick-fix
597 # for the p3yk merge, this should probably be fixed more neatly.
598 dispatch["_arbitrary_instance"] = dump_instance
Fredrik Lundhb9056332001-07-11 17:42:21 +0000599
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000600##
601# XML-RPC unmarshaller.
602#
603# @see loads
604
Fredrik Lundhb9056332001-07-11 17:42:21 +0000605class Unmarshaller:
Fred Drake1b410792001-09-04 18:55:03 +0000606 """Unmarshal an XML-RPC response, based on incoming XML event
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000607 messages (start, data, end). Call close() to get the resulting
Fred Drake1b410792001-09-04 18:55:03 +0000608 data structure.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000609
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000610 Note that this reader is fairly tolerant, and gladly accepts bogus
611 XML-RPC data without complaining (but not bogus XML).
Fred Drake1b410792001-09-04 18:55:03 +0000612 """
Fredrik Lundhb9056332001-07-11 17:42:21 +0000613
614 # and again, if you don't understand what's going on in here,
615 # that's perfectly ok.
616
Georg Brandlfe991052009-09-16 15:54:04 +0000617 def __init__(self, use_datetime=False):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000618 self._type = None
619 self._stack = []
620 self._marks = []
621 self._data = []
622 self._methodname = None
623 self._encoding = "utf-8"
624 self.append = self._stack.append
Skip Montanaro174dd222005-05-14 20:54:16 +0000625 self._use_datetime = use_datetime
Fredrik Lundhb9056332001-07-11 17:42:21 +0000626
627 def close(self):
628 # return response tuple and target method
629 if self._type is None or self._marks:
630 raise ResponseError()
631 if self._type == "fault":
Guido van Rossum68468eb2003-02-27 20:14:51 +0000632 raise Fault(**self._stack[0])
Fredrik Lundhb9056332001-07-11 17:42:21 +0000633 return tuple(self._stack)
634
635 def getmethodname(self):
636 return self._methodname
637
638 #
639 # event handlers
640
641 def xml(self, encoding, standalone):
642 self._encoding = encoding
643 # FIXME: assert standalone == 1 ???
644
645 def start(self, tag, attrs):
646 # prepare to handle this element
647 if tag == "array" or tag == "struct":
648 self._marks.append(len(self._stack))
649 self._data = []
650 self._value = (tag == "value")
651
652 def data(self, text):
653 self._data.append(text)
654
Neal Norwitzff113342007-04-17 08:42:15 +0000655 def end(self, tag):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000656 # call the appropriate end tag handler
657 try:
658 f = self.dispatch[tag]
659 except KeyError:
660 pass # unknown tag ?
661 else:
Neal Norwitzff113342007-04-17 08:42:15 +0000662 return f(self, "".join(self._data))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000663
664 #
665 # accelerator support
666
667 def end_dispatch(self, tag, data):
668 # dispatch data
669 try:
670 f = self.dispatch[tag]
671 except KeyError:
672 pass # unknown tag ?
673 else:
674 return f(self, data)
675
676 #
677 # element decoders
678
679 dispatch = {}
680
Andrew M. Kuchlinga4c2b742003-04-25 00:26:51 +0000681 def end_nil (self, data):
682 self.append(None)
683 self._value = 0
684 dispatch["nil"] = end_nil
Tim Petersc2659cf2003-05-12 20:19:37 +0000685
Fredrik Lundh1538c232001-10-01 19:42:03 +0000686 def end_boolean(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000687 if data == "0":
688 self.append(False)
689 elif data == "1":
690 self.append(True)
691 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000692 raise TypeError("bad boolean value")
Fredrik Lundhb9056332001-07-11 17:42:21 +0000693 self._value = 0
694 dispatch["boolean"] = end_boolean
695
Fredrik Lundh1538c232001-10-01 19:42:03 +0000696 def end_int(self, data):
697 self.append(int(data))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000698 self._value = 0
699 dispatch["i4"] = end_int
Georg Brandlf08a9dd2008-06-10 16:57:31 +0000700 dispatch["i8"] = end_int
Fredrik Lundhb9056332001-07-11 17:42:21 +0000701 dispatch["int"] = end_int
702
Fredrik Lundh1538c232001-10-01 19:42:03 +0000703 def end_double(self, data):
704 self.append(float(data))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000705 self._value = 0
706 dispatch["double"] = end_double
707
Fredrik Lundh1538c232001-10-01 19:42:03 +0000708 def end_string(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000709 if self._encoding:
Alexandre Vassalottie671fd22009-07-22 02:32:34 +0000710 data = data.decode(self._encoding)
711 self.append(data)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000712 self._value = 0
713 dispatch["string"] = end_string
714 dispatch["name"] = end_string # struct keys are always strings
715
716 def end_array(self, data):
Raymond Hettinger46ac8eb2002-06-30 03:39:14 +0000717 mark = self._marks.pop()
Fredrik Lundhb9056332001-07-11 17:42:21 +0000718 # map arrays to Python lists
719 self._stack[mark:] = [self._stack[mark:]]
720 self._value = 0
721 dispatch["array"] = end_array
722
723 def end_struct(self, data):
Raymond Hettinger46ac8eb2002-06-30 03:39:14 +0000724 mark = self._marks.pop()
Fredrik Lundhb9056332001-07-11 17:42:21 +0000725 # map structs to Python dictionaries
726 dict = {}
727 items = self._stack[mark:]
728 for i in range(0, len(items), 2):
Alexandre Vassalottie671fd22009-07-22 02:32:34 +0000729 dict[items[i]] = items[i+1]
Fredrik Lundhb9056332001-07-11 17:42:21 +0000730 self._stack[mark:] = [dict]
731 self._value = 0
732 dispatch["struct"] = end_struct
733
Fredrik Lundh1538c232001-10-01 19:42:03 +0000734 def end_base64(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000735 value = Binary()
Guido van Rossum54a40cb2007-08-27 22:27:41 +0000736 value.decode(data.encode("ascii"))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000737 self.append(value)
738 self._value = 0
739 dispatch["base64"] = end_base64
740
Fredrik Lundh1538c232001-10-01 19:42:03 +0000741 def end_dateTime(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000742 value = DateTime()
Fredrik Lundh1538c232001-10-01 19:42:03 +0000743 value.decode(data)
Skip Montanaro174dd222005-05-14 20:54:16 +0000744 if self._use_datetime:
745 value = _datetime_type(data)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000746 self.append(value)
747 dispatch["dateTime.iso8601"] = end_dateTime
748
749 def end_value(self, data):
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000750 # if we stumble upon a value element with no internal
Fredrik Lundhb9056332001-07-11 17:42:21 +0000751 # elements, treat it as a string element
752 if self._value:
753 self.end_string(data)
754 dispatch["value"] = end_value
755
756 def end_params(self, data):
757 self._type = "params"
758 dispatch["params"] = end_params
759
760 def end_fault(self, data):
761 self._type = "fault"
762 dispatch["fault"] = end_fault
763
Fredrik Lundh1538c232001-10-01 19:42:03 +0000764 def end_methodName(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000765 if self._encoding:
Alexandre Vassalottie671fd22009-07-22 02:32:34 +0000766 data = data.decode(self._encoding)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000767 self._methodname = data
768 self._type = "methodName" # no params
769 dispatch["methodName"] = end_methodName
770
Martin v. Löwis45394c22003-10-31 13:49:36 +0000771## Multicall support
772#
Fredrik Lundhb9056332001-07-11 17:42:21 +0000773
Martin v. Löwis45394c22003-10-31 13:49:36 +0000774class _MultiCallMethod:
775 # some lesser magic to store calls made to a MultiCall object
776 # for batch execution
777 def __init__(self, call_list, name):
778 self.__call_list = call_list
779 self.__name = name
780 def __getattr__(self, name):
781 return _MultiCallMethod(self.__call_list, "%s.%s" % (self.__name, name))
782 def __call__(self, *args):
783 self.__call_list.append((self.__name, args))
784
Martin v. Löwis12237b32004-08-22 16:04:50 +0000785class MultiCallIterator:
Martin v. Löwis45394c22003-10-31 13:49:36 +0000786 """Iterates over the results of a multicall. Exceptions are
787 thrown in response to xmlrpc faults."""
Raymond Hettingercc523fc2003-11-02 09:47:05 +0000788
Martin v. Löwis12237b32004-08-22 16:04:50 +0000789 def __init__(self, results):
790 self.results = results
791
792 def __getitem__(self, i):
793 item = self.results[i]
794 if type(item) == type({}):
795 raise Fault(item['faultCode'], item['faultString'])
796 elif type(item) == type([]):
797 return item[0]
Martin v. Löwis45394c22003-10-31 13:49:36 +0000798 else:
Collin Winterce36ad82007-08-30 01:19:48 +0000799 raise ValueError("unexpected type in multicall result")
Raymond Hettingercc523fc2003-11-02 09:47:05 +0000800
Martin v. Löwis45394c22003-10-31 13:49:36 +0000801class MultiCall:
802 """server -> a object used to boxcar method calls
803
804 server should be a ServerProxy object.
805
806 Methods can be added to the MultiCall using normal
807 method call syntax e.g.:
808
809 multicall = MultiCall(server_proxy)
810 multicall.add(2,3)
811 multicall.get_address("Guido")
812
813 To execute the multicall, call the MultiCall object e.g.:
814
815 add_result, address = multicall()
816 """
Raymond Hettingercc523fc2003-11-02 09:47:05 +0000817
Martin v. Löwis45394c22003-10-31 13:49:36 +0000818 def __init__(self, server):
819 self.__server = server
820 self.__call_list = []
Raymond Hettingercc523fc2003-11-02 09:47:05 +0000821
Martin v. Löwis45394c22003-10-31 13:49:36 +0000822 def __repr__(self):
823 return "<MultiCall at %x>" % id(self)
Raymond Hettingercc523fc2003-11-02 09:47:05 +0000824
Martin v. Löwis45394c22003-10-31 13:49:36 +0000825 __str__ = __repr__
826
827 def __getattr__(self, name):
828 return _MultiCallMethod(self.__call_list, name)
829
830 def __call__(self):
831 marshalled_list = []
832 for name, args in self.__call_list:
833 marshalled_list.append({'methodName' : name, 'params' : args})
834
835 return MultiCallIterator(self.__server.system.multicall(marshalled_list))
Raymond Hettingercc523fc2003-11-02 09:47:05 +0000836
Fredrik Lundhb9056332001-07-11 17:42:21 +0000837# --------------------------------------------------------------------
838# convenience functions
839
Georg Brandl38eceaa2008-05-26 11:14:17 +0000840FastMarshaller = FastParser = FastUnmarshaller = None
841
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000842##
843# Create a parser object, and connect it to an unmarshalling instance.
844# This function picks the fastest available XML parser.
845#
846# return A (parser, unmarshaller) tuple.
847
Georg Brandlfe991052009-09-16 15:54:04 +0000848def getparser(use_datetime=False):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000849 """getparser() -> parser, unmarshaller
850
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000851 Create an instance of the fastest available parser, and attach it
852 to an unmarshalling object. Return both objects.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000853 """
854 if FastParser and FastUnmarshaller:
Skip Montanaro174dd222005-05-14 20:54:16 +0000855 if use_datetime:
856 mkdatetime = _datetime_type
857 else:
858 mkdatetime = _datetime
859 target = FastUnmarshaller(True, False, _binary, mkdatetime, Fault)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000860 parser = FastParser(target)
861 else:
Skip Montanaro174dd222005-05-14 20:54:16 +0000862 target = Unmarshaller(use_datetime=use_datetime)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000863 if FastParser:
864 parser = FastParser(target)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000865 else:
Georg Brandlcef803f2009-06-04 09:04:53 +0000866 parser = ExpatParser(target)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000867 return parser, target
868
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000869##
870# Convert a Python tuple or a Fault instance to an XML-RPC packet.
871#
872# @def dumps(params, **options)
873# @param params A tuple or Fault instance.
874# @keyparam methodname If given, create a methodCall request for
875# this method name.
876# @keyparam methodresponse If given, create a methodResponse packet.
877# If used with a tuple, the tuple must be a singleton (that is,
878# it must contain exactly one element).
879# @keyparam encoding The packet encoding.
880# @return A string containing marshalled data.
881
Andrew M. Kuchlinga4c2b742003-04-25 00:26:51 +0000882def dumps(params, methodname=None, methodresponse=None, encoding=None,
Georg Brandlfe991052009-09-16 15:54:04 +0000883 allow_none=False):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000884 """data [,options] -> marshalled data
885
886 Convert an argument tuple or a Fault instance to an XML-RPC
887 request (or response, if the methodresponse option is used).
888
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000889 In addition to the data object, the following options can be given
890 as keyword arguments:
Fredrik Lundhb9056332001-07-11 17:42:21 +0000891
892 methodname: the method name for a methodCall packet
893
894 methodresponse: true to create a methodResponse packet.
895 If this option is used with a tuple, the tuple must be
896 a singleton (i.e. it can contain only one element).
897
898 encoding: the packet encoding (default is UTF-8)
899
900 All 8-bit strings in the data structure are assumed to use the
901 packet encoding. Unicode strings are automatically converted,
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000902 where necessary.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000903 """
904
Guido van Rossum13257902007-06-07 23:15:56 +0000905 assert isinstance(params, (tuple, Fault)), "argument must be tuple or Fault instance"
Fredrik Lundhb9056332001-07-11 17:42:21 +0000906 if isinstance(params, Fault):
907 methodresponse = 1
Guido van Rossum13257902007-06-07 23:15:56 +0000908 elif methodresponse and isinstance(params, tuple):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000909 assert len(params) == 1, "response tuple must be a singleton"
910
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000911 if not encoding:
Fredrik Lundhb9056332001-07-11 17:42:21 +0000912 encoding = "utf-8"
913
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000914 if FastMarshaller:
915 m = FastMarshaller(encoding)
916 else:
Andrew M. Kuchlinga4c2b742003-04-25 00:26:51 +0000917 m = Marshaller(encoding, allow_none)
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000918
Fredrik Lundhb9056332001-07-11 17:42:21 +0000919 data = m.dumps(params)
920
921 if encoding != "utf-8":
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000922 xmlheader = "<?xml version='1.0' encoding='%s'?>\n" % str(encoding)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000923 else:
924 xmlheader = "<?xml version='1.0'?>\n" # utf-8 is default
925
926 # standard XML-RPC wrappings
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000927 if methodname:
Fredrik Lundhb9056332001-07-11 17:42:21 +0000928 # a method call
Guido van Rossum3172c5d2007-10-16 18:12:55 +0000929 if not isinstance(methodname, str):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000930 methodname = methodname.encode(encoding)
931 data = (
932 xmlheader,
933 "<methodCall>\n"
934 "<methodName>", methodname, "</methodName>\n",
935 data,
936 "</methodCall>\n"
937 )
938 elif methodresponse:
939 # a method response, or a fault structure
940 data = (
941 xmlheader,
942 "<methodResponse>\n",
943 data,
944 "</methodResponse>\n"
945 )
946 else:
947 return data # return as is
Neal Norwitzff113342007-04-17 08:42:15 +0000948 return "".join(data)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000949
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000950##
951# Convert an XML-RPC packet to a Python object. If the XML-RPC packet
952# represents a fault condition, this function raises a Fault exception.
953#
954# @param data An XML-RPC packet, given as an 8-bit string.
Walter Dörwaldf0dfc7a2003-10-20 14:01:56 +0000955# @return A tuple containing the unpacked data, and the method name
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000956# (None if not present).
957# @see Fault
958
Georg Brandlfe991052009-09-16 15:54:04 +0000959def loads(data, use_datetime=False):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000960 """data -> unmarshalled data, method name
961
962 Convert an XML-RPC packet to unmarshalled data plus a method
963 name (None if not present).
964
965 If the XML-RPC packet represents a fault condition, this function
966 raises a Fault exception.
967 """
Skip Montanaro174dd222005-05-14 20:54:16 +0000968 p, u = getparser(use_datetime=use_datetime)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000969 p.feed(data)
970 p.close()
971 return u.close(), u.getmethodname()
972
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000973##
974# Encode a string using the gzip content encoding such as specified by the
975# Content-Encoding: gzip
976# in the HTTP header, as described in RFC 1952
977#
978# @param data the unencoded data
979# @return the encoded data
980
981def gzip_encode(data):
982 """data -> gzip encoded data
983
984 Encode data using the gzip content encoding as described in RFC 1952
985 """
Kristján Valur Jónssonaefde242009-07-19 22:29:24 +0000986 if not gzip:
987 raise NotImplementedError
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +0000988 f = BytesIO()
989 gzf = gzip.GzipFile(mode="wb", fileobj=f, compresslevel=1)
990 gzf.write(data)
991 gzf.close()
992 encoded = f.getvalue()
993 f.close()
994 return encoded
995
996##
997# Decode a string using the gzip content encoding such as specified by the
998# Content-Encoding: gzip
999# in the HTTP header, as described in RFC 1952
1000#
1001# @param data The encoded data
1002# @return the unencoded data
1003# @raises ValueError if data is not correctly coded.
1004
1005def gzip_decode(data):
1006 """gzip encoded data -> unencoded data
1007
1008 Decode data using the gzip content encoding as described in RFC 1952
1009 """
Kristján Valur Jónssonaefde242009-07-19 22:29:24 +00001010 if not gzip:
1011 raise NotImplementedError
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +00001012 f = BytesIO(data)
1013 gzf = gzip.GzipFile(mode="rb", fileobj=f)
1014 try:
1015 decoded = gzf.read()
1016 except IOError:
1017 raise ValueError("invalid data")
1018 f.close()
1019 gzf.close()
1020 return decoded
1021
1022##
1023# Return a decoded file-like object for the gzip encoding
1024# as described in RFC 1952.
1025#
1026# @param response A stream supporting a read() method
1027# @return a file-like object that the decoded data can be read() from
1028
Kristján Valur Jónsson9ab07312009-07-19 22:38:38 +00001029class GzipDecodedResponse(gzip.GzipFile if gzip else object):
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +00001030 """a file-like object to decode a response encoded with the gzip
1031 method, as described in RFC 1952.
1032 """
1033 def __init__(self, response):
1034 #response doesn't support tell() and read(), required by
1035 #GzipFile
Kristján Valur Jónssonaefde242009-07-19 22:29:24 +00001036 if not gzip:
1037 raise NotImplementedError
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +00001038 self.io = BytesIO(response.read())
1039 gzip.GzipFile.__init__(self, mode="rb", fileobj=self.io)
1040
1041 def close(self):
1042 gzip.GzipFile.close(self)
1043 self.io.close()
1044
Fredrik Lundhb9056332001-07-11 17:42:21 +00001045
1046# --------------------------------------------------------------------
1047# request dispatcher
1048
1049class _Method:
1050 # some magic to bind an XML-RPC method to an RPC server.
1051 # supports "nested" methods (e.g. examples.getStateName)
1052 def __init__(self, send, name):
1053 self.__send = send
1054 self.__name = name
1055 def __getattr__(self, name):
1056 return _Method(self.__send, "%s.%s" % (self.__name, name))
1057 def __call__(self, *args):
1058 return self.__send(self.__name, args)
1059
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001060##
1061# Standard transport class for XML-RPC over HTTP.
1062# <p>
1063# You can create custom transports by subclassing this method, and
1064# overriding selected methods.
Fredrik Lundhb9056332001-07-11 17:42:21 +00001065
1066class Transport:
Fredrik Lundhc4c062f2001-09-10 19:45:02 +00001067 """Handles an HTTP transaction to an XML-RPC server."""
Fredrik Lundhb9056332001-07-11 17:42:21 +00001068
1069 # client identifier (may be overridden)
Florent Xicluna75861df2011-10-30 20:39:24 +01001070 user_agent = "Python-xmlrpc/%s" % __version__
Fredrik Lundhb9056332001-07-11 17:42:21 +00001071
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +00001072 #if true, we'll request gzip encoding
1073 accept_gzip_encoding = True
1074
1075 # if positive, encode request using gzip if it exceeds this threshold
1076 # note that many server will get confused, so only use it if you know
1077 # that they can decode such a request
1078 encode_threshold = None #None = don't encode
1079
Georg Brandlfe991052009-09-16 15:54:04 +00001080 def __init__(self, use_datetime=False):
Skip Montanaro174dd222005-05-14 20:54:16 +00001081 self._use_datetime = use_datetime
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +00001082 self._connection = (None, None)
1083 self._extra_headers = []
Skip Montanaro174dd222005-05-14 20:54:16 +00001084
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001085 ##
1086 # Send a complete request, and parse the response.
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +00001087 # Retry request if a cached connection has disconnected.
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001088 #
1089 # @param host Target host.
1090 # @param handler Target PRC handler.
1091 # @param request_body XML-RPC request body.
1092 # @param verbose Debugging flag.
1093 # @return Parsed response.
1094
Georg Brandlfe991052009-09-16 15:54:04 +00001095 def request(self, host, handler, request_body, verbose=False):
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +00001096 #retry request once if cached connection has gone cold
1097 for i in (0, 1):
1098 try:
1099 return self.single_request(host, handler, request_body, verbose)
Kristján Valur Jónsson43535d92009-07-03 23:23:50 +00001100 except socket.error as e:
Victor Stinner756f5472010-07-24 02:24:55 +00001101 if i or e.errno not in (errno.ECONNRESET, errno.ECONNABORTED, errno.EPIPE):
Kristján Valur Jónsson43535d92009-07-03 23:23:50 +00001102 raise
1103 except http.client.BadStatusLine: #close after we sent request
1104 if i:
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +00001105 raise
1106
Georg Brandlfe991052009-09-16 15:54:04 +00001107 def single_request(self, host, handler, request_body, verbose=False):
Fredrik Lundhb9056332001-07-11 17:42:21 +00001108 # issue XML-RPC request
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +00001109 try:
1110 http_conn = self.send_request(host, handler, request_body, verbose)
1111 resp = http_conn.getresponse()
1112 if resp.status == 200:
1113 self.verbose = verbose
1114 return self.parse_response(resp)
Fredrik Lundhb9056332001-07-11 17:42:21 +00001115
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +00001116 except Fault:
1117 raise
1118 except Exception:
1119 #All unexpected errors leave connection in
1120 # a strange state, so we clear it.
1121 self.close()
1122 raise
Fredrik Lundhb9056332001-07-11 17:42:21 +00001123
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +00001124 #We got an error response.
1125 #Discard any response data and raise exception
1126 if resp.getheader("content-length", ""):
1127 resp.read()
1128 raise ProtocolError(
1129 host + handler,
1130 resp.status, resp.reason,
1131 dict(resp.getheaders())
1132 )
Fredrik Lundhb9056332001-07-11 17:42:21 +00001133
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001134
1135 ##
1136 # Create parser.
1137 #
1138 # @return A 2-tuple containing a parser and a unmarshaller.
Fredrik Lundhb9056332001-07-11 17:42:21 +00001139
Fredrik Lundhc4c062f2001-09-10 19:45:02 +00001140 def getparser(self):
1141 # get parser and unmarshaller
Skip Montanaro174dd222005-05-14 20:54:16 +00001142 return getparser(use_datetime=self._use_datetime)
Fredrik Lundhc4c062f2001-09-10 19:45:02 +00001143
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001144 ##
Fredrik Lundh1303c7c2002-10-22 18:23:00 +00001145 # Get authorization info from host parameter
1146 # Host may be a string, or a (host, x509-dict) tuple; if a string,
1147 # it is checked for a "user:pw@host" format, and a "Basic
1148 # Authentication" header is added if appropriate.
1149 #
1150 # @param host Host descriptor (URL or (URL, x509 info) tuple).
1151 # @return A 3-tuple containing (actual host, extra headers,
1152 # x509 info). The header and x509 fields may be None.
1153
1154 def get_host_info(self, host):
1155
1156 x509 = {}
Guido van Rossum13257902007-06-07 23:15:56 +00001157 if isinstance(host, tuple):
Fredrik Lundh1303c7c2002-10-22 18:23:00 +00001158 host, x509 = host
1159
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001160 import urllib.parse
1161 auth, host = urllib.parse.splituser(host)
Fredrik Lundh1303c7c2002-10-22 18:23:00 +00001162
1163 if auth:
Georg Brandlc8dcfb62009-02-13 10:50:01 +00001164 auth = urllib.parse.unquote_to_bytes(auth)
Georg Brandlb54d8012009-06-04 09:11:51 +00001165 auth = base64.encodebytes(auth).decode("utf-8")
Neal Norwitzff113342007-04-17 08:42:15 +00001166 auth = "".join(auth.split()) # get rid of whitespace
Fredrik Lundh1303c7c2002-10-22 18:23:00 +00001167 extra_headers = [
1168 ("Authorization", "Basic " + auth)
1169 ]
1170 else:
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +00001171 extra_headers = []
Fredrik Lundh1303c7c2002-10-22 18:23:00 +00001172
1173 return host, extra_headers, x509
1174
1175 ##
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001176 # Connect to server.
1177 #
1178 # @param host Target host.
Jeremy Hylton5d8a88a2007-08-14 16:47:39 +00001179 # @return An HTTPConnection object
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001180
Fredrik Lundhb9056332001-07-11 17:42:21 +00001181 def make_connection(self, host):
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +00001182 #return an existing connection if possible. This allows
1183 #HTTP/1.1 keep-alive.
1184 if self._connection and host == self._connection[0]:
1185 return self._connection[1]
Fredrik Lundhb9056332001-07-11 17:42:21 +00001186 # create a HTTP connection object from a host descriptor
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +00001187 chost, self._extra_headers, x509 = self.get_host_info(host)
1188 self._connection = host, http.client.HTTPConnection(chost)
1189 return self._connection[1]
Jeremy Hylton5d8a88a2007-08-14 16:47:39 +00001190
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +00001191 ##
1192 # Clear any cached connection object.
1193 # Used in the event of socket errors.
1194 #
1195 def close(self):
1196 if self._connection[1]:
1197 self._connection[1].close()
1198 self._connection = (None, None)
Fredrik Lundhb9056332001-07-11 17:42:21 +00001199
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001200 ##
Jeremy Hylton5d8a88a2007-08-14 16:47:39 +00001201 # Send HTTP request.
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001202 #
Jeremy Hylton5d8a88a2007-08-14 16:47:39 +00001203 # @param host Host descriptor (URL or (URL, x509 info) tuple).
1204 # @param handler Targer RPC handler (a path relative to host)
1205 # @param request_body The XML-RPC request body
1206 # @param debug Enable debugging if debug is true.
1207 # @return An HTTPConnection.
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001208
Jeremy Hylton5d8a88a2007-08-14 16:47:39 +00001209 def send_request(self, host, handler, request_body, debug):
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +00001210 connection = self.make_connection(host)
1211 headers = self._extra_headers[:]
Jeremy Hylton5d8a88a2007-08-14 16:47:39 +00001212 if debug:
1213 connection.set_debuglevel(1)
Kristján Valur Jónssonaefde242009-07-19 22:29:24 +00001214 if self.accept_gzip_encoding and gzip:
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +00001215 connection.putrequest("POST", handler, skip_accept_encoding=True)
1216 headers.append(("Accept-Encoding", "gzip"))
1217 else:
1218 connection.putrequest("POST", handler)
1219 headers.append(("Content-Type", "text/xml"))
1220 headers.append(("User-Agent", self.user_agent))
1221 self.send_headers(connection, headers)
1222 self.send_content(connection, request_body)
Jeremy Hylton5d8a88a2007-08-14 16:47:39 +00001223 return connection
Fredrik Lundhb9056332001-07-11 17:42:21 +00001224
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001225 ##
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +00001226 # Send request headers.
1227 # This function provides a useful hook for subclassing
1228 #
1229 # @param connection httpConnection.
1230 # @param headers list of key,value pairs for HTTP headers
1231
1232 def send_headers(self, connection, headers):
1233 for key, val in headers:
1234 connection.putheader(key, val)
1235
1236 ##
1237 # Send request body.
1238 # This function provides a useful hook for subclassing
1239 #
1240 # @param connection httpConnection.
1241 # @param request_body XML-RPC request body.
1242
1243 def send_content(self, connection, request_body):
1244 #optionally encode the request
1245 if (self.encode_threshold is not None and
Kristján Valur Jónssonaefde242009-07-19 22:29:24 +00001246 self.encode_threshold < len(request_body) and
1247 gzip):
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +00001248 connection.putheader("Content-Encoding", "gzip")
1249 request_body = gzip_encode(request_body)
1250
1251 connection.putheader("Content-Length", str(len(request_body)))
1252 connection.endheaders(request_body)
1253
1254 ##
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001255 # Parse response.
1256 #
1257 # @param file Stream.
1258 # @return Response tuple and target method.
1259
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +00001260 def parse_response(self, response):
1261 # read response data from httpresponse, and parse it
Senthil Kumaranf34445f2010-12-08 08:04:49 +00001262 # Check for new http response object, otherwise it is a file object.
1263 if hasattr(response, 'getheader'):
1264 if response.getheader("Content-Encoding", "") == "gzip":
1265 stream = GzipDecodedResponse(response)
1266 else:
1267 stream = response
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +00001268 else:
1269 stream = response
Fredrik Lundhb9056332001-07-11 17:42:21 +00001270
Fredrik Lundhc4c062f2001-09-10 19:45:02 +00001271 p, u = self.getparser()
Fredrik Lundhb9056332001-07-11 17:42:21 +00001272
1273 while 1:
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +00001274 data = stream.read(1024)
1275 if not data:
Fredrik Lundhb9056332001-07-11 17:42:21 +00001276 break
1277 if self.verbose:
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +00001278 print("body:", repr(data))
1279 p.feed(data)
Fredrik Lundhb9056332001-07-11 17:42:21 +00001280
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +00001281 if stream is not response:
1282 stream.close()
Fredrik Lundhb9056332001-07-11 17:42:21 +00001283 p.close()
1284
1285 return u.close()
1286
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001287##
1288# Standard transport class for XML-RPC over HTTPS.
1289
Fredrik Lundhb9056332001-07-11 17:42:21 +00001290class SafeTransport(Transport):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +00001291 """Handles an HTTPS transaction to an XML-RPC server."""
Fredrik Lundhb9056332001-07-11 17:42:21 +00001292
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001293 # FIXME: mostly untested
1294
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +00001295 def make_connection(self, host):
1296 if self._connection and host == self._connection[0]:
1297 return self._connection[1]
1298
Senthil Kumaran6a0b5c42010-11-18 17:08:48 +00001299 if not hasattr(http.client, "HTTPSConnection"):
Fredrik Lundh1303c7c2002-10-22 18:23:00 +00001300 raise NotImplementedError(
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +00001301 "your version of http.client doesn't support HTTPS")
1302 # create a HTTPS connection object from a host descriptor
1303 # host may be a string, or a (host, x509-dict) tuple
1304 chost, self._extra_headers, x509 = self.get_host_info(host)
1305 self._connection = host, http.client.HTTPSConnection(chost,
1306 None, **(x509 or {}))
1307 return self._connection[1]
Fredrik Lundhb9056332001-07-11 17:42:21 +00001308
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001309##
1310# Standard server proxy. This class establishes a virtual connection
1311# to an XML-RPC server.
1312# <p>
1313# This class is available as ServerProxy and Server. New code should
1314# use ServerProxy, to avoid confusion.
1315#
1316# @def ServerProxy(uri, **options)
1317# @param uri The connection point on the server.
1318# @keyparam transport A transport factory, compatible with the
1319# standard transport class.
1320# @keyparam encoding The default encoding used for 8-bit strings
1321# (default is UTF-8).
1322# @keyparam verbose Use a true value to enable debugging output.
1323# (printed to standard output).
1324# @see Transport
1325
Fredrik Lundhb9056332001-07-11 17:42:21 +00001326class ServerProxy:
1327 """uri [,options] -> a logical connection to an XML-RPC server
1328
1329 uri is the connection point on the server, given as
1330 scheme://host/target.
1331
1332 The standard implementation always supports the "http" scheme. If
1333 SSL socket support is available (Python 2.0), it also supports
1334 "https".
1335
1336 If the target part and the slash preceding it are both omitted,
1337 "/RPC2" is assumed.
1338
1339 The following options can be given as keyword arguments:
1340
1341 transport: a transport factory
1342 encoding: the request encoding (default is UTF-8)
1343
1344 All 8-bit strings passed to the server proxy are assumed to use
1345 the given encoding.
1346 """
1347
Georg Brandlfe991052009-09-16 15:54:04 +00001348 def __init__(self, uri, transport=None, encoding=None, verbose=False,
1349 allow_none=False, use_datetime=False):
Fredrik Lundhb9056332001-07-11 17:42:21 +00001350 # establish a "logical" server connection
1351
1352 # get the url
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001353 import urllib.parse
1354 type, uri = urllib.parse.splittype(uri)
Fredrik Lundhb9056332001-07-11 17:42:21 +00001355 if type not in ("http", "https"):
Collin Winterce36ad82007-08-30 01:19:48 +00001356 raise IOError("unsupported XML-RPC protocol")
Jeremy Hylton1afc1692008-06-18 20:49:58 +00001357 self.__host, self.__handler = urllib.parse.splithost(uri)
Fredrik Lundhb9056332001-07-11 17:42:21 +00001358 if not self.__handler:
1359 self.__handler = "/RPC2"
1360
1361 if transport is None:
1362 if type == "https":
Skip Montanaro174dd222005-05-14 20:54:16 +00001363 transport = SafeTransport(use_datetime=use_datetime)
Fredrik Lundhb9056332001-07-11 17:42:21 +00001364 else:
Skip Montanaro174dd222005-05-14 20:54:16 +00001365 transport = Transport(use_datetime=use_datetime)
Fredrik Lundhb9056332001-07-11 17:42:21 +00001366 self.__transport = transport
1367
Senthil Kumaranb3af08f2009-04-01 20:20:43 +00001368 self.__encoding = encoding or 'utf-8'
Fredrik Lundhb9056332001-07-11 17:42:21 +00001369 self.__verbose = verbose
Andrew M. Kuchlinga4c2b742003-04-25 00:26:51 +00001370 self.__allow_none = allow_none
Tim Petersc2659cf2003-05-12 20:19:37 +00001371
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +00001372 def __close(self):
1373 self.__transport.close()
1374
Fredrik Lundhb9056332001-07-11 17:42:21 +00001375 def __request(self, methodname, params):
1376 # call a method on the remote server
1377
Andrew M. Kuchlinga4c2b742003-04-25 00:26:51 +00001378 request = dumps(params, methodname, encoding=self.__encoding,
Senthil Kumaranb3af08f2009-04-01 20:20:43 +00001379 allow_none=self.__allow_none).encode(self.__encoding)
Fredrik Lundhb9056332001-07-11 17:42:21 +00001380
1381 response = self.__transport.request(
1382 self.__host,
1383 self.__handler,
1384 request,
1385 verbose=self.__verbose
1386 )
1387
1388 if len(response) == 1:
1389 response = response[0]
1390
1391 return response
1392
1393 def __repr__(self):
1394 return (
Fredrik Lundh78eedce2001-08-23 20:04:33 +00001395 "<ServerProxy for %s%s>" %
Fredrik Lundhb9056332001-07-11 17:42:21 +00001396 (self.__host, self.__handler)
1397 )
1398
1399 __str__ = __repr__
Raymond Hettingercc523fc2003-11-02 09:47:05 +00001400
Fredrik Lundhb9056332001-07-11 17:42:21 +00001401 def __getattr__(self, name):
1402 # magic method dispatcher
1403 return _Method(self.__request, name)
1404
1405 # note: to call a remote object with an non-standard name, use
1406 # result getattr(server, "strange-python-name")(args)
1407
Kristján Valur Jónsson985fc6a2009-07-01 10:01:31 +00001408 def __call__(self, attr):
1409 """A workaround to get special attributes on the ServerProxy
1410 without interfering with the magic __getattr__
1411 """
1412 if attr == "close":
1413 return self.__close
1414 elif attr == "transport":
1415 return self.__transport
1416 raise AttributeError("Attribute %r not found" % (attr,))
1417
Fredrik Lundh78eedce2001-08-23 20:04:33 +00001418# compatibility
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001419
Fredrik Lundhb9056332001-07-11 17:42:21 +00001420Server = ServerProxy
1421
1422# --------------------------------------------------------------------
1423# test code
1424
1425if __name__ == "__main__":
1426
1427 # simple test program (from the XML-RPC specification)
1428
Fredrik Lundh78eedce2001-08-23 20:04:33 +00001429 # server = ServerProxy("http://localhost:8000") # local server
Martin v. Löwis12237b32004-08-22 16:04:50 +00001430 server = ServerProxy("http://time.xmlrpc.com/RPC2")
Fredrik Lundhb9056332001-07-11 17:42:21 +00001431
Fredrik Lundhb9056332001-07-11 17:42:21 +00001432 try:
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001433 print(server.currentTime.getCurrentTime())
Guido van Rossumb940e112007-01-10 16:19:56 +00001434 except Error as v:
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001435 print("ERROR", v)
Martin v. Löwis12237b32004-08-22 16:04:50 +00001436
Jeremy Hylton5d8a88a2007-08-14 16:47:39 +00001437 # The server at xmlrpc.com doesn't seem to support multicall anymore.
Martin v. Löwis12237b32004-08-22 16:04:50 +00001438 multi = MultiCall(server)
1439 multi.currentTime.getCurrentTime()
1440 multi.currentTime.getCurrentTime()
1441 try:
1442 for response in multi():
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001443 print(response)
Guido van Rossumb940e112007-01-10 16:19:56 +00001444 except Error as v:
Guido van Rossumbe19ed72007-02-09 05:37:30 +00001445 print("ERROR", v)