blob: 78bdd6ff157eaf831a379e43b07418f5f04b9fde [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:
11# this version is designed to work with Python 1.5.2 or newer.
12# unicode encoding support requires at least Python 1.6.
13# experimental HTTPS requires Python 2.0 built with SSL sockets.
14# expat parser support requires Python 2.0 with pyexpat support.
15#
Fredrik Lundhb9056332001-07-11 17:42:21 +000016# History:
17# 1999-01-14 fl Created
18# 1999-01-15 fl Changed dateTime to use localtime
19# 1999-01-16 fl Added Binary/base64 element, default to RPC2 service
20# 1999-01-19 fl Fixed array data element (from Skip Montanaro)
21# 1999-01-21 fl Fixed dateTime constructor, etc.
22# 1999-02-02 fl Added fault handling, handle empty sequences, etc.
23# 1999-02-10 fl Fixed problem with empty responses (from Skip Montanaro)
24# 1999-06-20 fl Speed improvements, pluggable parsers/transports (0.9.8)
25# 2000-11-28 fl Changed boolean to check the truth value of its argument
26# 2001-02-24 fl Added encoding/Unicode/SafeTransport patches
27# 2001-02-26 fl Added compare support to wrappers (0.9.9/1.0b1)
28# 2001-03-28 fl Make sure response tuple is a singleton
29# 2001-03-29 fl Don't require empty params element (from Nicholas Riley)
Fredrik Lundh78eedce2001-08-23 20:04:33 +000030# 2001-06-10 fl Folded in _xmlrpclib accelerator support (1.0b2)
31# 2001-08-20 fl Base xmlrpclib.Error on built-in Exception (from Paul Prescod)
Fredrik Lundhc4c062f2001-09-10 19:45:02 +000032# 2001-09-03 fl Allow Transport subclass to override getparser
33# 2001-09-10 fl Lazy import of urllib, cgi, xmllib (20x import speedup)
Fredrik Lundh1538c232001-10-01 19:42:03 +000034# 2001-10-01 fl Remove containers from memo cache when done with them
35# 2001-10-01 fl Use faster escape method (80% dumps speedup)
Skip Montanaro5e9c71b2001-10-10 15:56:34 +000036# 2001-10-10 sm Allow long ints to be passed as ints if they don't overflow
Skip Montanaro5449e082001-10-17 22:53:33 +000037# 2001-10-17 sm test for int and long overflow (allows use on 64-bit systems)
Fredrik Lundhb6ab93f2001-12-19 21:40:04 +000038# 2001-11-12 fl Use repr() to marshal doubles (from Paul Felix)
Fredrik Lundhb9056332001-07-11 17:42:21 +000039#
40# Copyright (c) 1999-2001 by Secret Labs AB.
41# Copyright (c) 1999-2001 by Fredrik Lundh.
42#
43# info@pythonware.com
44# http://www.pythonware.com
45#
46# --------------------------------------------------------------------
47# The XML-RPC client interface is
48#
49# Copyright (c) 1999-2001 by Secret Labs AB
50# Copyright (c) 1999-2001 by Fredrik Lundh
51#
52# By obtaining, using, and/or copying this software and/or its
53# associated documentation, you agree that you have read, understood,
54# and will comply with the following terms and conditions:
55#
56# Permission to use, copy, modify, and distribute this software and
57# its associated documentation for any purpose and without fee is
58# hereby granted, provided that the above copyright notice appears in
59# all copies, and that both that copyright notice and this permission
60# notice appear in supporting documentation, and that the name of
61# Secret Labs AB or the author not be used in advertising or publicity
62# pertaining to distribution of the software without specific, written
63# prior permission.
64#
65# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
66# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
67# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR
68# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
69# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
70# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
71# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
72# OF THIS SOFTWARE.
73# --------------------------------------------------------------------
74
75#
Fredrik Lundhb6ab93f2001-12-19 21:40:04 +000076# things to look into:
Fredrik Lundhb9056332001-07-11 17:42:21 +000077
Fredrik Lundhb9056332001-07-11 17:42:21 +000078# TODO: support basic authentication (see robin's patch)
79# TODO: fix host tuple handling in the server constructor
80# TODO: let transport verify schemes
81# TODO: update documentation
82# TODO: authentication plugins
Fredrik Lundhb9056332001-07-11 17:42:21 +000083
Fred Drake1b410792001-09-04 18:55:03 +000084"""
85An XML-RPC client interface for Python.
86
87The marshalling and response parser code can also be used to
88implement XML-RPC servers.
89
Fred Drake1b410792001-09-04 18:55:03 +000090Exported exceptions:
91
Fredrik Lundhc4c062f2001-09-10 19:45:02 +000092 Error Base class for client errors
93 ProtocolError Indicates an HTTP protocol error
94 ResponseError Indicates a broken response package
95 Fault Indicates an XML-RPC fault package
Fred Drake1b410792001-09-04 18:55:03 +000096
97Exported classes:
98
Fredrik Lundhc4c062f2001-09-10 19:45:02 +000099 ServerProxy Represents a logical connection to an XML-RPC server
Fred Drake1b410792001-09-04 18:55:03 +0000100
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000101 Boolean boolean wrapper to generate a "boolean" XML-RPC value
102 DateTime dateTime wrapper for an ISO 8601 string or time tuple or
103 localtime integer value to generate a "dateTime.iso8601"
104 XML-RPC value
105 Binary binary data wrapper
Fred Drake1b410792001-09-04 18:55:03 +0000106
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000107 SlowParser Slow but safe standard parser (based on xmllib)
108 Marshaller Generate an XML-RPC params chunk from a Python data structure
109 Unmarshaller Unmarshal an XML-RPC response from incoming XML event message
110 Transport Handles an HTTP transaction to an XML-RPC server
111 SafeTransport Handles an HTTPS transaction to an XML-RPC server
Fred Drake1b410792001-09-04 18:55:03 +0000112
113Exported constants:
114
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000115 True
116 False
Fred Drake1b410792001-09-04 18:55:03 +0000117
118Exported functions:
119
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000120 boolean Convert any Python value to an XML-RPC boolean
121 getparser Create instance of the fastest available parser & attach
122 to an unmarshalling object
123 dumps Convert an argument tuple or a Fault instance to an XML-RPC
124 request (or response, if the methodresponse option is used).
125 loads Convert an XML-RPC packet to unmarshalled data plus a method
126 name (None if not present).
Fred Drake1b410792001-09-04 18:55:03 +0000127"""
128
Fred Drake2a2d9702001-10-17 01:51:04 +0000129import re, string, time, operator
Fredrik Lundh1538c232001-10-01 19:42:03 +0000130
Fredrik Lundhb9056332001-07-11 17:42:21 +0000131from types import *
Fredrik Lundhb9056332001-07-11 17:42:21 +0000132
133try:
134 unicode
135except NameError:
136 unicode = None # unicode support not available
137
138def _decode(data, encoding, is8bit=re.compile("[\x80-\xff]").search):
139 # decode non-ascii string (if possible)
140 if unicode and encoding and is8bit(data):
141 data = unicode(data, encoding)
142 return data
143
Fredrik Lundh1538c232001-10-01 19:42:03 +0000144def escape(s, replace=string.replace):
145 s = replace(s, "&", "&")
146 s = replace(s, "<", "&lt;")
147 return replace(s, ">", "&gt;",)
148
Skip Montanaro5449e082001-10-17 22:53:33 +0000149MAXINT = 2L**31-1
150MININT = -2L**31
151
Fredrik Lundhb9056332001-07-11 17:42:21 +0000152if unicode:
153 def _stringify(string):
154 # convert to 7-bit ascii if possible
155 try:
156 return str(string)
157 except UnicodeError:
158 return string
159else:
160 def _stringify(string):
161 return string
162
Fredrik Lundhb6ab93f2001-12-19 21:40:04 +0000163__version__ = "1.0.0"
Fredrik Lundhb9056332001-07-11 17:42:21 +0000164
165# --------------------------------------------------------------------
166# Exceptions
167
Fredrik Lundh78eedce2001-08-23 20:04:33 +0000168class Error(Exception):
Fred Drake1b410792001-09-04 18:55:03 +0000169 """Base class for client errors."""
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000170 def __str__(self):
171 return repr(self)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000172
173class ProtocolError(Error):
Fred Drake1b410792001-09-04 18:55:03 +0000174 """Indicates an HTTP protocol error."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000175 def __init__(self, url, errcode, errmsg, headers):
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000176 Error.__init__(self)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000177 self.url = url
178 self.errcode = errcode
179 self.errmsg = errmsg
180 self.headers = headers
181 def __repr__(self):
182 return (
183 "<ProtocolError for %s: %s %s>" %
184 (self.url, self.errcode, self.errmsg)
185 )
186
187class ResponseError(Error):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000188 """Indicates a broken response package."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000189 pass
190
191class Fault(Error):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000192 """Indicates an XML-RPC fault package."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000193 def __init__(self, faultCode, faultString, **extra):
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000194 Error.__init__(self)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000195 self.faultCode = faultCode
196 self.faultString = faultString
197 def __repr__(self):
198 return (
199 "<Fault %s: %s>" %
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000200 (self.faultCode, repr(self.faultString))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000201 )
202
203# --------------------------------------------------------------------
204# Special values
205
Fredrik Lundhb9056332001-07-11 17:42:21 +0000206class Boolean:
Fred Drake1b410792001-09-04 18:55:03 +0000207 """Boolean-value wrapper.
208
209 Use True or False to generate a "boolean" XML-RPC value.
210 """
Fredrik Lundhb9056332001-07-11 17:42:21 +0000211
212 def __init__(self, value = 0):
213 self.value = operator.truth(value)
214
215 def encode(self, out):
216 out.write("<value><boolean>%d</boolean></value>\n" % self.value)
217
218 def __cmp__(self, other):
219 if isinstance(other, Boolean):
220 other = other.value
221 return cmp(self.value, other)
222
223 def __repr__(self):
224 if self.value:
225 return "<Boolean True at %x>" % id(self)
226 else:
227 return "<Boolean False at %x>" % id(self)
228
229 def __int__(self):
230 return self.value
231
232 def __nonzero__(self):
233 return self.value
234
235True, False = Boolean(1), Boolean(0)
236
237def boolean(value, truefalse=(False, True)):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000238 """Convert any Python value to XML-RPC 'boolean'."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000239 return truefalse[operator.truth(value)]
240
Fredrik Lundhb9056332001-07-11 17:42:21 +0000241class DateTime:
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000242 """DateTime wrapper for an ISO 8601 string or time tuple or
243 localtime integer value to generate 'dateTime.iso8601' XML-RPC
244 value.
245 """
Fredrik Lundhb9056332001-07-11 17:42:21 +0000246
247 def __init__(self, value=0):
Fredrik Lundh78eedce2001-08-23 20:04:33 +0000248 if not isinstance(value, StringType):
249 if not isinstance(value, TupleType):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000250 if value == 0:
251 value = time.time()
252 value = time.localtime(value)
253 value = time.strftime("%Y%m%dT%H:%M:%S", value)
254 self.value = value
255
256 def __cmp__(self, other):
257 if isinstance(other, DateTime):
258 other = other.value
259 return cmp(self.value, other)
260
261 def __repr__(self):
262 return "<DateTime %s at %x>" % (self.value, id(self))
263
264 def decode(self, data):
265 self.value = string.strip(data)
266
267 def encode(self, out):
268 out.write("<value><dateTime.iso8601>")
269 out.write(self.value)
270 out.write("</dateTime.iso8601></value>\n")
271
272def datetime(data):
273 value = DateTime()
274 value.decode(data)
275 return value
276
Fredrik Lundhb9056332001-07-11 17:42:21 +0000277class Binary:
Fred Drake1b410792001-09-04 18:55:03 +0000278 """Wrapper for binary data."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000279
280 def __init__(self, data=None):
281 self.data = data
282
283 def __cmp__(self, other):
284 if isinstance(other, Binary):
285 other = other.data
286 return cmp(self.data, other)
287
288 def decode(self, data):
289 import base64
290 self.data = base64.decodestring(data)
291
292 def encode(self, out):
293 import base64, StringIO
294 out.write("<value><base64>\n")
295 base64.encode(StringIO.StringIO(self.data), out)
296 out.write("</base64></value>\n")
297
298def binary(data):
299 value = Binary()
300 value.decode(data)
301 return value
302
303WRAPPERS = DateTime, Binary, Boolean
304
305# --------------------------------------------------------------------
306# XML parsers
307
308try:
309 # optional xmlrpclib accelerator. for more information on this
310 # component, contact info@pythonware.com
311 import _xmlrpclib
312 FastParser = _xmlrpclib.Parser
313 FastUnmarshaller = _xmlrpclib.Unmarshaller
314except (AttributeError, ImportError):
315 FastParser = FastUnmarshaller = None
316
317#
318# the SGMLOP parser is about 15x faster than Python's builtin
319# XML parser. SGMLOP sources can be downloaded from:
320#
321# http://www.pythonware.com/products/xml/sgmlop.htm
322#
323
324try:
325 import sgmlop
326 if not hasattr(sgmlop, "XMLParser"):
327 raise ImportError
328except ImportError:
329 SgmlopParser = None # sgmlop accelerator not available
330else:
331 class SgmlopParser:
332 def __init__(self, target):
333
334 # setup callbacks
335 self.finish_starttag = target.start
336 self.finish_endtag = target.end
337 self.handle_data = target.data
338 self.handle_xml = target.xml
339
340 # activate parser
341 self.parser = sgmlop.XMLParser()
342 self.parser.register(self)
343 self.feed = self.parser.feed
344 self.entity = {
345 "amp": "&", "gt": ">", "lt": "<",
346 "apos": "'", "quot": '"'
347 }
348
349 def close(self):
350 try:
351 self.parser.close()
352 finally:
353 self.parser = self.feed = None # nuke circular reference
354
355 def handle_proc(self, tag, attr):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000356 import re
Fredrik Lundhb9056332001-07-11 17:42:21 +0000357 m = re.search("encoding\s*=\s*['\"]([^\"']+)[\"']", attr)
358 if m:
359 self.handle_xml(m.group(1), 1)
360
361 def handle_entityref(self, entity):
362 # <string> entity
363 try:
364 self.handle_data(self.entity[entity])
365 except KeyError:
366 self.handle_data("&%s;" % entity)
367
368try:
369 from xml.parsers import expat
Guido van Rossumb8551342001-10-02 18:33:11 +0000370 if not hasattr(expat, "ParserCreate"):
371 raise ImportError, "ParserCreate"
Fredrik Lundhb9056332001-07-11 17:42:21 +0000372except ImportError:
373 ExpatParser = None
374else:
375 class ExpatParser:
376 # fast expat parser for Python 2.0. this is about 50%
377 # slower than sgmlop, on roundtrip testing
378 def __init__(self, target):
379 self._parser = parser = expat.ParserCreate(None, None)
380 self._target = target
381 parser.StartElementHandler = target.start
382 parser.EndElementHandler = target.end
383 parser.CharacterDataHandler = target.data
384 encoding = None
385 if not parser.returns_unicode:
386 encoding = "utf-8"
387 target.xml(encoding, None)
388
389 def feed(self, data):
390 self._parser.Parse(data, 0)
391
392 def close(self):
393 self._parser.Parse("", 1) # end of data
394 del self._target, self._parser # get rid of circular references
395
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000396class SlowParser:
397 """Default XML parser (based on xmllib.XMLParser)."""
398 # this is about 10 times slower than sgmlop, on roundtrip
399 # testing.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000400 def __init__(self, target):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000401 import xmllib # lazy subclassing (!)
402 if xmllib.XMLParser not in SlowParser.__bases__:
403 SlowParser.__bases__ = (xmllib.XMLParser,)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000404 self.handle_xml = target.xml
405 self.unknown_starttag = target.start
406 self.handle_data = target.data
407 self.unknown_endtag = target.end
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000408 try:
409 xmllib.XMLParser.__init__(self, accept_utf8=1)
410 except TypeError:
411 xmllib.XMLParser.__init__(self) # pre-2.0
Fredrik Lundhb9056332001-07-11 17:42:21 +0000412
413# --------------------------------------------------------------------
414# XML-RPC marshalling and unmarshalling code
415
416class Marshaller:
Fred Drake1b410792001-09-04 18:55:03 +0000417 """Generate an XML-RPC params chunk from a Python data structure.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000418
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000419 Create a Marshaller instance for each set of parameters, and use
420 the "dumps" method to convert your data (represented as a tuple)
421 to an XML-RPC params chunk. To write a fault response, pass a
422 Fault instance instead. You may prefer to use the "dumps" module
423 function for this purpose.
Fred Drake1b410792001-09-04 18:55:03 +0000424 """
Fredrik Lundhb9056332001-07-11 17:42:21 +0000425
426 # by the way, if you don't understand what's going on in here,
427 # that's perfectly ok.
428
429 def __init__(self, encoding=None):
430 self.memo = {}
431 self.data = None
432 self.encoding = encoding
433
434 dispatch = {}
435
436 def dumps(self, values):
437 self.__out = []
438 self.write = write = self.__out.append
439 if isinstance(values, Fault):
440 # fault instance
441 write("<fault>\n")
442 self.__dump(vars(values))
443 write("</fault>\n")
444 else:
445 # parameter block
Fredrik Lundhc266bb02001-08-23 20:13:08 +0000446 # FIXME: the xml-rpc specification allows us to leave out
447 # the entire <params> block if there are no parameters.
448 # however, changing this may break older code (including
449 # old versions of xmlrpclib.py), so this is better left as
450 # is for now. See @XMLRPC3 for more information. /F
Fredrik Lundhb9056332001-07-11 17:42:21 +0000451 write("<params>\n")
452 for v in values:
453 write("<param>\n")
454 self.__dump(v)
455 write("</param>\n")
456 write("</params>\n")
457 result = string.join(self.__out, "")
458 del self.__out, self.write # don't need this any more
459 return result
460
461 def __dump(self, value):
462 try:
463 f = self.dispatch[type(value)]
464 except KeyError:
465 raise TypeError, "cannot marshal %s objects" % type(value)
466 else:
467 f(self, value)
468
469 def dump_int(self, value):
Skip Montanaro5449e082001-10-17 22:53:33 +0000470 # in case ints are > 32 bits
471 if value > MAXINT or value < MININT:
472 raise OverflowError, "int exceeds XML-RPC limits"
Fredrik Lundhb9056332001-07-11 17:42:21 +0000473 self.write("<value><int>%s</int></value>\n" % value)
474 dispatch[IntType] = dump_int
475
Skip Montanaro5e9c71b2001-10-10 15:56:34 +0000476 def dump_long(self, value):
Skip Montanaro5449e082001-10-17 22:53:33 +0000477 # in case ints are > 32 bits
478 if value > MAXINT or value < MININT:
479 raise OverflowError, "long int exceeds XML-RPC limits"
480 self.write("<value><int>%s</int></value>\n" % int(value))
Skip Montanaro5e9c71b2001-10-10 15:56:34 +0000481 dispatch[LongType] = dump_long
482
Fredrik Lundhb9056332001-07-11 17:42:21 +0000483 def dump_double(self, value):
Fredrik Lundhb6ab93f2001-12-19 21:40:04 +0000484 self.write("<value><double>%s</double></value>\n" % repr(value))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000485 dispatch[FloatType] = dump_double
486
Fredrik Lundh1538c232001-10-01 19:42:03 +0000487 def dump_string(self, value, escape=escape):
488 self.write("<value><string>%s</string></value>\n" % escape(value))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000489 dispatch[StringType] = dump_string
490
491 if unicode:
Fredrik Lundh1538c232001-10-01 19:42:03 +0000492 def dump_unicode(self, value, escape=escape):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000493 value = value.encode(self.encoding)
Fredrik Lundh1538c232001-10-01 19:42:03 +0000494 self.write("<value><string>%s</string></value>\n" % escape(value))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000495 dispatch[UnicodeType] = dump_unicode
496
Fredrik Lundh1538c232001-10-01 19:42:03 +0000497 def opencontainer(self, value):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000498 if value:
499 i = id(value)
500 if self.memo.has_key(i):
501 raise TypeError, "cannot marshal recursive data structures"
502 self.memo[i] = None
503
Fredrik Lundh1538c232001-10-01 19:42:03 +0000504 def closecontainer(self, value):
Martin v. Löwis5f12d752001-09-30 20:15:41 +0000505 if value:
506 del self.memo[id(value)]
507
Fredrik Lundhb9056332001-07-11 17:42:21 +0000508 def dump_array(self, value):
Fredrik Lundh1538c232001-10-01 19:42:03 +0000509 self.opencontainer(value)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000510 write = self.write
Fredrik Lundh1538c232001-10-01 19:42:03 +0000511 dump = self.__dump
Fredrik Lundhb9056332001-07-11 17:42:21 +0000512 write("<value><array><data>\n")
513 for v in value:
Fredrik Lundh1538c232001-10-01 19:42:03 +0000514 dump(v)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000515 write("</data></array></value>\n")
Fredrik Lundh1538c232001-10-01 19:42:03 +0000516 self.closecontainer(value)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000517 dispatch[TupleType] = dump_array
518 dispatch[ListType] = dump_array
519
Fredrik Lundh1538c232001-10-01 19:42:03 +0000520 def dump_struct(self, value, escape=escape):
521 self.opencontainer(value)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000522 write = self.write
Fredrik Lundh1538c232001-10-01 19:42:03 +0000523 dump = self.__dump
Fredrik Lundhb9056332001-07-11 17:42:21 +0000524 write("<value><struct>\n")
525 for k, v in value.items():
526 write("<member>\n")
527 if type(k) is not StringType:
528 raise TypeError, "dictionary key must be string"
Fredrik Lundh1538c232001-10-01 19:42:03 +0000529 write("<name>%s</name>\n" % escape(k))
530 dump(v)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000531 write("</member>\n")
532 write("</struct></value>\n")
Fredrik Lundh1538c232001-10-01 19:42:03 +0000533 self.closecontainer(value)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000534 dispatch[DictType] = dump_struct
535
536 def dump_instance(self, value):
537 # check for special wrappers
538 if value.__class__ in WRAPPERS:
539 value.encode(self)
540 else:
541 # store instance attributes as a struct (really?)
542 self.dump_struct(value.__dict__)
543 dispatch[InstanceType] = dump_instance
544
545class Unmarshaller:
Fred Drake1b410792001-09-04 18:55:03 +0000546 """Unmarshal an XML-RPC response, based on incoming XML event
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000547 messages (start, data, end). Call close() to get the resulting
Fred Drake1b410792001-09-04 18:55:03 +0000548 data structure.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000549
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000550 Note that this reader is fairly tolerant, and gladly accepts bogus
551 XML-RPC data without complaining (but not bogus XML).
Fred Drake1b410792001-09-04 18:55:03 +0000552 """
Fredrik Lundhb9056332001-07-11 17:42:21 +0000553
554 # and again, if you don't understand what's going on in here,
555 # that's perfectly ok.
556
557 def __init__(self):
558 self._type = None
559 self._stack = []
560 self._marks = []
561 self._data = []
562 self._methodname = None
563 self._encoding = "utf-8"
564 self.append = self._stack.append
565
566 def close(self):
567 # return response tuple and target method
568 if self._type is None or self._marks:
569 raise ResponseError()
570 if self._type == "fault":
571 raise apply(Fault, (), self._stack[0])
572 return tuple(self._stack)
573
574 def getmethodname(self):
575 return self._methodname
576
577 #
578 # event handlers
579
580 def xml(self, encoding, standalone):
581 self._encoding = encoding
582 # FIXME: assert standalone == 1 ???
583
584 def start(self, tag, attrs):
585 # prepare to handle this element
586 if tag == "array" or tag == "struct":
587 self._marks.append(len(self._stack))
588 self._data = []
589 self._value = (tag == "value")
590
591 def data(self, text):
592 self._data.append(text)
593
Fredrik Lundh1538c232001-10-01 19:42:03 +0000594 def end(self, tag, join=string.join):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000595 # call the appropriate end tag handler
596 try:
597 f = self.dispatch[tag]
598 except KeyError:
599 pass # unknown tag ?
600 else:
Fredrik Lundh1538c232001-10-01 19:42:03 +0000601 return f(self, join(self._data, ""))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000602
603 #
604 # accelerator support
605
606 def end_dispatch(self, tag, data):
607 # dispatch data
608 try:
609 f = self.dispatch[tag]
610 except KeyError:
611 pass # unknown tag ?
612 else:
613 return f(self, data)
614
615 #
616 # element decoders
617
618 dispatch = {}
619
Fredrik Lundh1538c232001-10-01 19:42:03 +0000620 def end_boolean(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000621 if data == "0":
622 self.append(False)
623 elif data == "1":
624 self.append(True)
625 else:
626 raise TypeError, "bad boolean value"
627 self._value = 0
628 dispatch["boolean"] = end_boolean
629
Fredrik Lundh1538c232001-10-01 19:42:03 +0000630 def end_int(self, data):
631 self.append(int(data))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000632 self._value = 0
633 dispatch["i4"] = end_int
634 dispatch["int"] = end_int
635
Fredrik Lundh1538c232001-10-01 19:42:03 +0000636 def end_double(self, data):
637 self.append(float(data))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000638 self._value = 0
639 dispatch["double"] = end_double
640
Fredrik Lundh1538c232001-10-01 19:42:03 +0000641 def end_string(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000642 if self._encoding:
643 data = _decode(data, self._encoding)
644 self.append(_stringify(data))
645 self._value = 0
646 dispatch["string"] = end_string
647 dispatch["name"] = end_string # struct keys are always strings
648
649 def end_array(self, data):
650 mark = self._marks[-1]
651 del self._marks[-1]
652 # map arrays to Python lists
653 self._stack[mark:] = [self._stack[mark:]]
654 self._value = 0
655 dispatch["array"] = end_array
656
657 def end_struct(self, data):
658 mark = self._marks[-1]
659 del self._marks[-1]
660 # map structs to Python dictionaries
661 dict = {}
662 items = self._stack[mark:]
663 for i in range(0, len(items), 2):
664 dict[_stringify(items[i])] = items[i+1]
665 self._stack[mark:] = [dict]
666 self._value = 0
667 dispatch["struct"] = end_struct
668
Fredrik Lundh1538c232001-10-01 19:42:03 +0000669 def end_base64(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000670 value = Binary()
Fredrik Lundh1538c232001-10-01 19:42:03 +0000671 value.decode(data)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000672 self.append(value)
673 self._value = 0
674 dispatch["base64"] = end_base64
675
Fredrik Lundh1538c232001-10-01 19:42:03 +0000676 def end_dateTime(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000677 value = DateTime()
Fredrik Lundh1538c232001-10-01 19:42:03 +0000678 value.decode(data)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000679 self.append(value)
680 dispatch["dateTime.iso8601"] = end_dateTime
681
682 def end_value(self, data):
683 # if we stumble upon an value element with no internal
684 # elements, treat it as a string element
685 if self._value:
686 self.end_string(data)
687 dispatch["value"] = end_value
688
689 def end_params(self, data):
690 self._type = "params"
691 dispatch["params"] = end_params
692
693 def end_fault(self, data):
694 self._type = "fault"
695 dispatch["fault"] = end_fault
696
Fredrik Lundh1538c232001-10-01 19:42:03 +0000697 def end_methodName(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000698 if self._encoding:
699 data = _decode(data, self._encoding)
700 self._methodname = data
701 self._type = "methodName" # no params
702 dispatch["methodName"] = end_methodName
703
704
705# --------------------------------------------------------------------
706# convenience functions
707
708def getparser():
709 """getparser() -> parser, unmarshaller
710
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000711 Create an instance of the fastest available parser, and attach it
712 to an unmarshalling object. Return both objects.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000713 """
714 if FastParser and FastUnmarshaller:
715 target = FastUnmarshaller(True, False, binary, datetime)
716 parser = FastParser(target)
717 else:
718 target = Unmarshaller()
719 if FastParser:
720 parser = FastParser(target)
721 elif SgmlopParser:
722 parser = SgmlopParser(target)
723 elif ExpatParser:
724 parser = ExpatParser(target)
725 else:
726 parser = SlowParser(target)
727 return parser, target
728
729def dumps(params, methodname=None, methodresponse=None, encoding=None):
730 """data [,options] -> marshalled data
731
732 Convert an argument tuple or a Fault instance to an XML-RPC
733 request (or response, if the methodresponse option is used).
734
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000735 In addition to the data object, the following options can be given
736 as keyword arguments:
Fredrik Lundhb9056332001-07-11 17:42:21 +0000737
738 methodname: the method name for a methodCall packet
739
740 methodresponse: true to create a methodResponse packet.
741 If this option is used with a tuple, the tuple must be
742 a singleton (i.e. it can contain only one element).
743
744 encoding: the packet encoding (default is UTF-8)
745
746 All 8-bit strings in the data structure are assumed to use the
747 packet encoding. Unicode strings are automatically converted,
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000748 where necessary.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000749 """
750
751 assert isinstance(params, TupleType) or isinstance(params, Fault),\
752 "argument must be tuple or Fault instance"
753
754 if isinstance(params, Fault):
755 methodresponse = 1
756 elif methodresponse and isinstance(params, TupleType):
757 assert len(params) == 1, "response tuple must be a singleton"
758
759 if not encoding:
760 encoding = "utf-8"
761
762 m = Marshaller(encoding)
763 data = m.dumps(params)
764
765 if encoding != "utf-8":
766 xmlheader = "<?xml version='1.0' encoding=%s?>\n" % repr(encoding)
767 else:
768 xmlheader = "<?xml version='1.0'?>\n" # utf-8 is default
769
770 # standard XML-RPC wrappings
771 if methodname:
772 # a method call
773 if not isinstance(methodname, StringType):
774 methodname = methodname.encode(encoding)
775 data = (
776 xmlheader,
777 "<methodCall>\n"
778 "<methodName>", methodname, "</methodName>\n",
779 data,
780 "</methodCall>\n"
781 )
782 elif methodresponse:
783 # a method response, or a fault structure
784 data = (
785 xmlheader,
786 "<methodResponse>\n",
787 data,
788 "</methodResponse>\n"
789 )
790 else:
791 return data # return as is
792 return string.join(data, "")
793
794def loads(data):
795 """data -> unmarshalled data, method name
796
797 Convert an XML-RPC packet to unmarshalled data plus a method
798 name (None if not present).
799
800 If the XML-RPC packet represents a fault condition, this function
801 raises a Fault exception.
802 """
803 p, u = getparser()
804 p.feed(data)
805 p.close()
806 return u.close(), u.getmethodname()
807
808
809# --------------------------------------------------------------------
810# request dispatcher
811
812class _Method:
813 # some magic to bind an XML-RPC method to an RPC server.
814 # supports "nested" methods (e.g. examples.getStateName)
815 def __init__(self, send, name):
816 self.__send = send
817 self.__name = name
818 def __getattr__(self, name):
819 return _Method(self.__send, "%s.%s" % (self.__name, name))
820 def __call__(self, *args):
821 return self.__send(self.__name, args)
822
823
824class Transport:
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000825 """Handles an HTTP transaction to an XML-RPC server."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000826
827 # client identifier (may be overridden)
828 user_agent = "xmlrpclib.py/%s (by www.pythonware.com)" % __version__
829
830 def request(self, host, handler, request_body, verbose=0):
831 # issue XML-RPC request
832
833 h = self.make_connection(host)
834 if verbose:
835 h.set_debuglevel(1)
836
837 self.send_request(h, handler, request_body)
838 self.send_host(h, host)
839 self.send_user_agent(h)
840 self.send_content(h, request_body)
841
842 errcode, errmsg, headers = h.getreply()
843
844 if errcode != 200:
845 raise ProtocolError(
846 host + handler,
847 errcode, errmsg,
848 headers
849 )
850
851 self.verbose = verbose
852
853 return self.parse_response(h.getfile())
854
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000855 def getparser(self):
856 # get parser and unmarshaller
857 return getparser()
858
Fredrik Lundhb9056332001-07-11 17:42:21 +0000859 def make_connection(self, host):
860 # create a HTTP connection object from a host descriptor
861 import httplib
862 return httplib.HTTP(host)
863
864 def send_request(self, connection, handler, request_body):
865 connection.putrequest("POST", handler)
866
867 def send_host(self, connection, host):
868 connection.putheader("Host", host)
869
870 def send_user_agent(self, connection):
871 connection.putheader("User-Agent", self.user_agent)
872
873 def send_content(self, connection, request_body):
874 connection.putheader("Content-Type", "text/xml")
875 connection.putheader("Content-Length", str(len(request_body)))
876 connection.endheaders()
877 if request_body:
878 connection.send(request_body)
879
880 def parse_response(self, f):
881 # read response from input file, and parse it
882
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000883 p, u = self.getparser()
Fredrik Lundhb9056332001-07-11 17:42:21 +0000884
885 while 1:
886 response = f.read(1024)
887 if not response:
888 break
889 if self.verbose:
890 print "body:", repr(response)
891 p.feed(response)
892
893 f.close()
894 p.close()
895
896 return u.close()
897
898class SafeTransport(Transport):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000899 """Handles an HTTPS transaction to an XML-RPC server."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000900
901 def make_connection(self, host):
902 # create a HTTPS connection object from a host descriptor
903 # host may be a string, or a (host, x509-dict) tuple
904 import httplib
905 if isinstance(host, TupleType):
906 host, x509 = host
907 else:
908 x509 = {}
909 try:
910 HTTPS = httplib.HTTPS
911 except AttributeError:
912 raise NotImplementedError,\
913 "your version of httplib doesn't support HTTPS"
914 else:
915 return apply(HTTPS, (host, None), x509)
916
917 def send_host(self, connection, host):
918 if isinstance(host, TupleType):
919 host, x509 = host
920 connection.putheader("Host", host)
921
922class ServerProxy:
923 """uri [,options] -> a logical connection to an XML-RPC server
924
925 uri is the connection point on the server, given as
926 scheme://host/target.
927
928 The standard implementation always supports the "http" scheme. If
929 SSL socket support is available (Python 2.0), it also supports
930 "https".
931
932 If the target part and the slash preceding it are both omitted,
933 "/RPC2" is assumed.
934
935 The following options can be given as keyword arguments:
936
937 transport: a transport factory
938 encoding: the request encoding (default is UTF-8)
939
940 All 8-bit strings passed to the server proxy are assumed to use
941 the given encoding.
942 """
943
944 def __init__(self, uri, transport=None, encoding=None, verbose=0):
945 # establish a "logical" server connection
946
947 # get the url
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000948 import urllib
Fredrik Lundhb9056332001-07-11 17:42:21 +0000949 type, uri = urllib.splittype(uri)
950 if type not in ("http", "https"):
951 raise IOError, "unsupported XML-RPC protocol"
952 self.__host, self.__handler = urllib.splithost(uri)
953 if not self.__handler:
954 self.__handler = "/RPC2"
955
956 if transport is None:
957 if type == "https":
958 transport = SafeTransport()
959 else:
960 transport = Transport()
961 self.__transport = transport
962
963 self.__encoding = encoding
964 self.__verbose = verbose
965
966 def __request(self, methodname, params):
967 # call a method on the remote server
968
969 request = dumps(params, methodname, encoding=self.__encoding)
970
971 response = self.__transport.request(
972 self.__host,
973 self.__handler,
974 request,
975 verbose=self.__verbose
976 )
977
978 if len(response) == 1:
979 response = response[0]
980
981 return response
982
983 def __repr__(self):
984 return (
Fredrik Lundh78eedce2001-08-23 20:04:33 +0000985 "<ServerProxy for %s%s>" %
Fredrik Lundhb9056332001-07-11 17:42:21 +0000986 (self.__host, self.__handler)
987 )
988
989 __str__ = __repr__
990
991 def __getattr__(self, name):
992 # magic method dispatcher
993 return _Method(self.__request, name)
994
995 # note: to call a remote object with an non-standard name, use
996 # result getattr(server, "strange-python-name")(args)
997
Fredrik Lundh78eedce2001-08-23 20:04:33 +0000998# compatibility
Fredrik Lundhb9056332001-07-11 17:42:21 +0000999Server = ServerProxy
1000
1001# --------------------------------------------------------------------
1002# test code
1003
1004if __name__ == "__main__":
1005
1006 # simple test program (from the XML-RPC specification)
1007
Fredrik Lundh78eedce2001-08-23 20:04:33 +00001008 # server = ServerProxy("http://localhost:8000") # local server
1009 server = ServerProxy("http://betty.userland.com")
Fredrik Lundhb9056332001-07-11 17:42:21 +00001010
1011 print server
1012
1013 try:
1014 print server.examples.getStateName(41)
1015 except Error, v:
1016 print "ERROR", v