blob: 8c6f799aa98e46c72c5ee94e618b941d52b80c06 [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):
356 m = re.search("encoding\s*=\s*['\"]([^\"']+)[\"']", attr)
357 if m:
358 self.handle_xml(m.group(1), 1)
359
360 def handle_entityref(self, entity):
361 # <string> entity
362 try:
363 self.handle_data(self.entity[entity])
364 except KeyError:
365 self.handle_data("&%s;" % entity)
366
367try:
368 from xml.parsers import expat
Guido van Rossumb8551342001-10-02 18:33:11 +0000369 if not hasattr(expat, "ParserCreate"):
370 raise ImportError, "ParserCreate"
Fredrik Lundhb9056332001-07-11 17:42:21 +0000371except ImportError:
372 ExpatParser = None
373else:
374 class ExpatParser:
375 # fast expat parser for Python 2.0. this is about 50%
376 # slower than sgmlop, on roundtrip testing
377 def __init__(self, target):
378 self._parser = parser = expat.ParserCreate(None, None)
379 self._target = target
380 parser.StartElementHandler = target.start
381 parser.EndElementHandler = target.end
382 parser.CharacterDataHandler = target.data
383 encoding = None
384 if not parser.returns_unicode:
385 encoding = "utf-8"
386 target.xml(encoding, None)
387
388 def feed(self, data):
389 self._parser.Parse(data, 0)
390
391 def close(self):
392 self._parser.Parse("", 1) # end of data
393 del self._target, self._parser # get rid of circular references
394
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000395class SlowParser:
396 """Default XML parser (based on xmllib.XMLParser)."""
397 # this is about 10 times slower than sgmlop, on roundtrip
398 # testing.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000399 def __init__(self, target):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000400 import xmllib # lazy subclassing (!)
401 if xmllib.XMLParser not in SlowParser.__bases__:
402 SlowParser.__bases__ = (xmllib.XMLParser,)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000403 self.handle_xml = target.xml
404 self.unknown_starttag = target.start
405 self.handle_data = target.data
406 self.unknown_endtag = target.end
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000407 try:
408 xmllib.XMLParser.__init__(self, accept_utf8=1)
409 except TypeError:
410 xmllib.XMLParser.__init__(self) # pre-2.0
Fredrik Lundhb9056332001-07-11 17:42:21 +0000411
412# --------------------------------------------------------------------
413# XML-RPC marshalling and unmarshalling code
414
415class Marshaller:
Fred Drake1b410792001-09-04 18:55:03 +0000416 """Generate an XML-RPC params chunk from a Python data structure.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000417
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000418 Create a Marshaller instance for each set of parameters, and use
419 the "dumps" method to convert your data (represented as a tuple)
420 to an XML-RPC params chunk. To write a fault response, pass a
421 Fault instance instead. You may prefer to use the "dumps" module
422 function for this purpose.
Fred Drake1b410792001-09-04 18:55:03 +0000423 """
Fredrik Lundhb9056332001-07-11 17:42:21 +0000424
425 # by the way, if you don't understand what's going on in here,
426 # that's perfectly ok.
427
428 def __init__(self, encoding=None):
429 self.memo = {}
430 self.data = None
431 self.encoding = encoding
432
433 dispatch = {}
434
435 def dumps(self, values):
436 self.__out = []
437 self.write = write = self.__out.append
438 if isinstance(values, Fault):
439 # fault instance
440 write("<fault>\n")
441 self.__dump(vars(values))
442 write("</fault>\n")
443 else:
444 # parameter block
Fredrik Lundhc266bb02001-08-23 20:13:08 +0000445 # FIXME: the xml-rpc specification allows us to leave out
446 # the entire <params> block if there are no parameters.
447 # however, changing this may break older code (including
448 # old versions of xmlrpclib.py), so this is better left as
449 # is for now. See @XMLRPC3 for more information. /F
Fredrik Lundhb9056332001-07-11 17:42:21 +0000450 write("<params>\n")
451 for v in values:
452 write("<param>\n")
453 self.__dump(v)
454 write("</param>\n")
455 write("</params>\n")
456 result = string.join(self.__out, "")
457 del self.__out, self.write # don't need this any more
458 return result
459
460 def __dump(self, value):
461 try:
462 f = self.dispatch[type(value)]
463 except KeyError:
464 raise TypeError, "cannot marshal %s objects" % type(value)
465 else:
466 f(self, value)
467
468 def dump_int(self, value):
Skip Montanaro5449e082001-10-17 22:53:33 +0000469 # in case ints are > 32 bits
470 if value > MAXINT or value < MININT:
471 raise OverflowError, "int exceeds XML-RPC limits"
Fredrik Lundhb9056332001-07-11 17:42:21 +0000472 self.write("<value><int>%s</int></value>\n" % value)
473 dispatch[IntType] = dump_int
474
Skip Montanaro5e9c71b2001-10-10 15:56:34 +0000475 def dump_long(self, value):
Skip Montanaro5449e082001-10-17 22:53:33 +0000476 # in case ints are > 32 bits
477 if value > MAXINT or value < MININT:
478 raise OverflowError, "long int exceeds XML-RPC limits"
479 self.write("<value><int>%s</int></value>\n" % int(value))
Skip Montanaro5e9c71b2001-10-10 15:56:34 +0000480 dispatch[LongType] = dump_long
481
Fredrik Lundhb9056332001-07-11 17:42:21 +0000482 def dump_double(self, value):
Fredrik Lundhb6ab93f2001-12-19 21:40:04 +0000483 self.write("<value><double>%s</double></value>\n" % repr(value))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000484 dispatch[FloatType] = dump_double
485
Fredrik Lundh1538c232001-10-01 19:42:03 +0000486 def dump_string(self, value, escape=escape):
487 self.write("<value><string>%s</string></value>\n" % escape(value))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000488 dispatch[StringType] = dump_string
489
490 if unicode:
Fredrik Lundh1538c232001-10-01 19:42:03 +0000491 def dump_unicode(self, value, escape=escape):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000492 value = value.encode(self.encoding)
Fredrik Lundh1538c232001-10-01 19:42:03 +0000493 self.write("<value><string>%s</string></value>\n" % escape(value))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000494 dispatch[UnicodeType] = dump_unicode
495
Fredrik Lundh1538c232001-10-01 19:42:03 +0000496 def opencontainer(self, value):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000497 if value:
498 i = id(value)
499 if self.memo.has_key(i):
500 raise TypeError, "cannot marshal recursive data structures"
501 self.memo[i] = None
502
Fredrik Lundh1538c232001-10-01 19:42:03 +0000503 def closecontainer(self, value):
Martin v. Löwis5f12d752001-09-30 20:15:41 +0000504 if value:
505 del self.memo[id(value)]
506
Fredrik Lundhb9056332001-07-11 17:42:21 +0000507 def dump_array(self, value):
Fredrik Lundh1538c232001-10-01 19:42:03 +0000508 self.opencontainer(value)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000509 write = self.write
Fredrik Lundh1538c232001-10-01 19:42:03 +0000510 dump = self.__dump
Fredrik Lundhb9056332001-07-11 17:42:21 +0000511 write("<value><array><data>\n")
512 for v in value:
Fredrik Lundh1538c232001-10-01 19:42:03 +0000513 dump(v)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000514 write("</data></array></value>\n")
Fredrik Lundh1538c232001-10-01 19:42:03 +0000515 self.closecontainer(value)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000516 dispatch[TupleType] = dump_array
517 dispatch[ListType] = dump_array
518
Fredrik Lundh1538c232001-10-01 19:42:03 +0000519 def dump_struct(self, value, escape=escape):
520 self.opencontainer(value)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000521 write = self.write
Fredrik Lundh1538c232001-10-01 19:42:03 +0000522 dump = self.__dump
Fredrik Lundhb9056332001-07-11 17:42:21 +0000523 write("<value><struct>\n")
524 for k, v in value.items():
525 write("<member>\n")
526 if type(k) is not StringType:
527 raise TypeError, "dictionary key must be string"
Fredrik Lundh1538c232001-10-01 19:42:03 +0000528 write("<name>%s</name>\n" % escape(k))
529 dump(v)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000530 write("</member>\n")
531 write("</struct></value>\n")
Fredrik Lundh1538c232001-10-01 19:42:03 +0000532 self.closecontainer(value)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000533 dispatch[DictType] = dump_struct
534
535 def dump_instance(self, value):
536 # check for special wrappers
537 if value.__class__ in WRAPPERS:
538 value.encode(self)
539 else:
540 # store instance attributes as a struct (really?)
541 self.dump_struct(value.__dict__)
542 dispatch[InstanceType] = dump_instance
543
544class Unmarshaller:
Fred Drake1b410792001-09-04 18:55:03 +0000545 """Unmarshal an XML-RPC response, based on incoming XML event
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000546 messages (start, data, end). Call close() to get the resulting
Fred Drake1b410792001-09-04 18:55:03 +0000547 data structure.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000548
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000549 Note that this reader is fairly tolerant, and gladly accepts bogus
550 XML-RPC data without complaining (but not bogus XML).
Fred Drake1b410792001-09-04 18:55:03 +0000551 """
Fredrik Lundhb9056332001-07-11 17:42:21 +0000552
553 # and again, if you don't understand what's going on in here,
554 # that's perfectly ok.
555
556 def __init__(self):
557 self._type = None
558 self._stack = []
559 self._marks = []
560 self._data = []
561 self._methodname = None
562 self._encoding = "utf-8"
563 self.append = self._stack.append
564
565 def close(self):
566 # return response tuple and target method
567 if self._type is None or self._marks:
568 raise ResponseError()
569 if self._type == "fault":
570 raise apply(Fault, (), self._stack[0])
571 return tuple(self._stack)
572
573 def getmethodname(self):
574 return self._methodname
575
576 #
577 # event handlers
578
579 def xml(self, encoding, standalone):
580 self._encoding = encoding
581 # FIXME: assert standalone == 1 ???
582
583 def start(self, tag, attrs):
584 # prepare to handle this element
585 if tag == "array" or tag == "struct":
586 self._marks.append(len(self._stack))
587 self._data = []
588 self._value = (tag == "value")
589
590 def data(self, text):
591 self._data.append(text)
592
Fredrik Lundh1538c232001-10-01 19:42:03 +0000593 def end(self, tag, join=string.join):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000594 # call the appropriate end tag handler
595 try:
596 f = self.dispatch[tag]
597 except KeyError:
598 pass # unknown tag ?
599 else:
Fredrik Lundh1538c232001-10-01 19:42:03 +0000600 return f(self, join(self._data, ""))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000601
602 #
603 # accelerator support
604
605 def end_dispatch(self, tag, data):
606 # dispatch data
607 try:
608 f = self.dispatch[tag]
609 except KeyError:
610 pass # unknown tag ?
611 else:
612 return f(self, data)
613
614 #
615 # element decoders
616
617 dispatch = {}
618
Fredrik Lundh1538c232001-10-01 19:42:03 +0000619 def end_boolean(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000620 if data == "0":
621 self.append(False)
622 elif data == "1":
623 self.append(True)
624 else:
625 raise TypeError, "bad boolean value"
626 self._value = 0
627 dispatch["boolean"] = end_boolean
628
Fredrik Lundh1538c232001-10-01 19:42:03 +0000629 def end_int(self, data):
630 self.append(int(data))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000631 self._value = 0
632 dispatch["i4"] = end_int
633 dispatch["int"] = end_int
634
Fredrik Lundh1538c232001-10-01 19:42:03 +0000635 def end_double(self, data):
636 self.append(float(data))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000637 self._value = 0
638 dispatch["double"] = end_double
639
Fredrik Lundh1538c232001-10-01 19:42:03 +0000640 def end_string(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000641 if self._encoding:
642 data = _decode(data, self._encoding)
643 self.append(_stringify(data))
644 self._value = 0
645 dispatch["string"] = end_string
646 dispatch["name"] = end_string # struct keys are always strings
647
648 def end_array(self, data):
649 mark = self._marks[-1]
650 del self._marks[-1]
651 # map arrays to Python lists
652 self._stack[mark:] = [self._stack[mark:]]
653 self._value = 0
654 dispatch["array"] = end_array
655
656 def end_struct(self, data):
657 mark = self._marks[-1]
658 del self._marks[-1]
659 # map structs to Python dictionaries
660 dict = {}
661 items = self._stack[mark:]
662 for i in range(0, len(items), 2):
663 dict[_stringify(items[i])] = items[i+1]
664 self._stack[mark:] = [dict]
665 self._value = 0
666 dispatch["struct"] = end_struct
667
Fredrik Lundh1538c232001-10-01 19:42:03 +0000668 def end_base64(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000669 value = Binary()
Fredrik Lundh1538c232001-10-01 19:42:03 +0000670 value.decode(data)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000671 self.append(value)
672 self._value = 0
673 dispatch["base64"] = end_base64
674
Fredrik Lundh1538c232001-10-01 19:42:03 +0000675 def end_dateTime(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000676 value = DateTime()
Fredrik Lundh1538c232001-10-01 19:42:03 +0000677 value.decode(data)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000678 self.append(value)
679 dispatch["dateTime.iso8601"] = end_dateTime
680
681 def end_value(self, data):
682 # if we stumble upon an value element with no internal
683 # elements, treat it as a string element
684 if self._value:
685 self.end_string(data)
686 dispatch["value"] = end_value
687
688 def end_params(self, data):
689 self._type = "params"
690 dispatch["params"] = end_params
691
692 def end_fault(self, data):
693 self._type = "fault"
694 dispatch["fault"] = end_fault
695
Fredrik Lundh1538c232001-10-01 19:42:03 +0000696 def end_methodName(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000697 if self._encoding:
698 data = _decode(data, self._encoding)
699 self._methodname = data
700 self._type = "methodName" # no params
701 dispatch["methodName"] = end_methodName
702
703
704# --------------------------------------------------------------------
705# convenience functions
706
707def getparser():
708 """getparser() -> parser, unmarshaller
709
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000710 Create an instance of the fastest available parser, and attach it
711 to an unmarshalling object. Return both objects.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000712 """
713 if FastParser and FastUnmarshaller:
714 target = FastUnmarshaller(True, False, binary, datetime)
715 parser = FastParser(target)
716 else:
717 target = Unmarshaller()
718 if FastParser:
719 parser = FastParser(target)
720 elif SgmlopParser:
721 parser = SgmlopParser(target)
722 elif ExpatParser:
723 parser = ExpatParser(target)
724 else:
725 parser = SlowParser(target)
726 return parser, target
727
728def dumps(params, methodname=None, methodresponse=None, encoding=None):
729 """data [,options] -> marshalled data
730
731 Convert an argument tuple or a Fault instance to an XML-RPC
732 request (or response, if the methodresponse option is used).
733
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000734 In addition to the data object, the following options can be given
735 as keyword arguments:
Fredrik Lundhb9056332001-07-11 17:42:21 +0000736
737 methodname: the method name for a methodCall packet
738
739 methodresponse: true to create a methodResponse packet.
740 If this option is used with a tuple, the tuple must be
741 a singleton (i.e. it can contain only one element).
742
743 encoding: the packet encoding (default is UTF-8)
744
745 All 8-bit strings in the data structure are assumed to use the
746 packet encoding. Unicode strings are automatically converted,
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000747 where necessary.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000748 """
749
750 assert isinstance(params, TupleType) or isinstance(params, Fault),\
751 "argument must be tuple or Fault instance"
752
753 if isinstance(params, Fault):
754 methodresponse = 1
755 elif methodresponse and isinstance(params, TupleType):
756 assert len(params) == 1, "response tuple must be a singleton"
757
758 if not encoding:
759 encoding = "utf-8"
760
761 m = Marshaller(encoding)
762 data = m.dumps(params)
763
764 if encoding != "utf-8":
765 xmlheader = "<?xml version='1.0' encoding=%s?>\n" % repr(encoding)
766 else:
767 xmlheader = "<?xml version='1.0'?>\n" # utf-8 is default
768
769 # standard XML-RPC wrappings
770 if methodname:
771 # a method call
772 if not isinstance(methodname, StringType):
773 methodname = methodname.encode(encoding)
774 data = (
775 xmlheader,
776 "<methodCall>\n"
777 "<methodName>", methodname, "</methodName>\n",
778 data,
779 "</methodCall>\n"
780 )
781 elif methodresponse:
782 # a method response, or a fault structure
783 data = (
784 xmlheader,
785 "<methodResponse>\n",
786 data,
787 "</methodResponse>\n"
788 )
789 else:
790 return data # return as is
791 return string.join(data, "")
792
793def loads(data):
794 """data -> unmarshalled data, method name
795
796 Convert an XML-RPC packet to unmarshalled data plus a method
797 name (None if not present).
798
799 If the XML-RPC packet represents a fault condition, this function
800 raises a Fault exception.
801 """
802 p, u = getparser()
803 p.feed(data)
804 p.close()
805 return u.close(), u.getmethodname()
806
807
808# --------------------------------------------------------------------
809# request dispatcher
810
811class _Method:
812 # some magic to bind an XML-RPC method to an RPC server.
813 # supports "nested" methods (e.g. examples.getStateName)
814 def __init__(self, send, name):
815 self.__send = send
816 self.__name = name
817 def __getattr__(self, name):
818 return _Method(self.__send, "%s.%s" % (self.__name, name))
819 def __call__(self, *args):
820 return self.__send(self.__name, args)
821
822
823class Transport:
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000824 """Handles an HTTP transaction to an XML-RPC server."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000825
826 # client identifier (may be overridden)
827 user_agent = "xmlrpclib.py/%s (by www.pythonware.com)" % __version__
828
829 def request(self, host, handler, request_body, verbose=0):
830 # issue XML-RPC request
831
832 h = self.make_connection(host)
833 if verbose:
834 h.set_debuglevel(1)
835
836 self.send_request(h, handler, request_body)
837 self.send_host(h, host)
838 self.send_user_agent(h)
839 self.send_content(h, request_body)
840
841 errcode, errmsg, headers = h.getreply()
842
843 if errcode != 200:
844 raise ProtocolError(
845 host + handler,
846 errcode, errmsg,
847 headers
848 )
849
850 self.verbose = verbose
851
852 return self.parse_response(h.getfile())
853
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000854 def getparser(self):
855 # get parser and unmarshaller
856 return getparser()
857
Fredrik Lundhb9056332001-07-11 17:42:21 +0000858 def make_connection(self, host):
859 # create a HTTP connection object from a host descriptor
860 import httplib
861 return httplib.HTTP(host)
862
863 def send_request(self, connection, handler, request_body):
864 connection.putrequest("POST", handler)
865
866 def send_host(self, connection, host):
867 connection.putheader("Host", host)
868
869 def send_user_agent(self, connection):
870 connection.putheader("User-Agent", self.user_agent)
871
872 def send_content(self, connection, request_body):
873 connection.putheader("Content-Type", "text/xml")
874 connection.putheader("Content-Length", str(len(request_body)))
875 connection.endheaders()
876 if request_body:
877 connection.send(request_body)
878
879 def parse_response(self, f):
880 # read response from input file, and parse it
881
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000882 p, u = self.getparser()
Fredrik Lundhb9056332001-07-11 17:42:21 +0000883
884 while 1:
885 response = f.read(1024)
886 if not response:
887 break
888 if self.verbose:
889 print "body:", repr(response)
890 p.feed(response)
891
892 f.close()
893 p.close()
894
895 return u.close()
896
897class SafeTransport(Transport):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000898 """Handles an HTTPS transaction to an XML-RPC server."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000899
900 def make_connection(self, host):
901 # create a HTTPS connection object from a host descriptor
902 # host may be a string, or a (host, x509-dict) tuple
903 import httplib
904 if isinstance(host, TupleType):
905 host, x509 = host
906 else:
907 x509 = {}
908 try:
909 HTTPS = httplib.HTTPS
910 except AttributeError:
911 raise NotImplementedError,\
912 "your version of httplib doesn't support HTTPS"
913 else:
914 return apply(HTTPS, (host, None), x509)
915
916 def send_host(self, connection, host):
917 if isinstance(host, TupleType):
918 host, x509 = host
919 connection.putheader("Host", host)
920
921class ServerProxy:
922 """uri [,options] -> a logical connection to an XML-RPC server
923
924 uri is the connection point on the server, given as
925 scheme://host/target.
926
927 The standard implementation always supports the "http" scheme. If
928 SSL socket support is available (Python 2.0), it also supports
929 "https".
930
931 If the target part and the slash preceding it are both omitted,
932 "/RPC2" is assumed.
933
934 The following options can be given as keyword arguments:
935
936 transport: a transport factory
937 encoding: the request encoding (default is UTF-8)
938
939 All 8-bit strings passed to the server proxy are assumed to use
940 the given encoding.
941 """
942
943 def __init__(self, uri, transport=None, encoding=None, verbose=0):
944 # establish a "logical" server connection
945
946 # get the url
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000947 import urllib
Fredrik Lundhb9056332001-07-11 17:42:21 +0000948 type, uri = urllib.splittype(uri)
949 if type not in ("http", "https"):
950 raise IOError, "unsupported XML-RPC protocol"
951 self.__host, self.__handler = urllib.splithost(uri)
952 if not self.__handler:
953 self.__handler = "/RPC2"
954
955 if transport is None:
956 if type == "https":
957 transport = SafeTransport()
958 else:
959 transport = Transport()
960 self.__transport = transport
961
962 self.__encoding = encoding
963 self.__verbose = verbose
964
965 def __request(self, methodname, params):
966 # call a method on the remote server
967
968 request = dumps(params, methodname, encoding=self.__encoding)
969
970 response = self.__transport.request(
971 self.__host,
972 self.__handler,
973 request,
974 verbose=self.__verbose
975 )
976
977 if len(response) == 1:
978 response = response[0]
979
980 return response
981
982 def __repr__(self):
983 return (
Fredrik Lundh78eedce2001-08-23 20:04:33 +0000984 "<ServerProxy for %s%s>" %
Fredrik Lundhb9056332001-07-11 17:42:21 +0000985 (self.__host, self.__handler)
986 )
987
988 __str__ = __repr__
989
990 def __getattr__(self, name):
991 # magic method dispatcher
992 return _Method(self.__request, name)
993
994 # note: to call a remote object with an non-standard name, use
995 # result getattr(server, "strange-python-name")(args)
996
Fredrik Lundh78eedce2001-08-23 20:04:33 +0000997# compatibility
Fredrik Lundhb9056332001-07-11 17:42:21 +0000998Server = ServerProxy
999
1000# --------------------------------------------------------------------
1001# test code
1002
1003if __name__ == "__main__":
1004
1005 # simple test program (from the XML-RPC specification)
1006
Fredrik Lundh78eedce2001-08-23 20:04:33 +00001007 # server = ServerProxy("http://localhost:8000") # local server
1008 server = ServerProxy("http://betty.userland.com")
Fredrik Lundhb9056332001-07-11 17:42:21 +00001009
1010 print server
1011
1012 try:
1013 print server.examples.getStateName(41)
1014 except Error, v:
1015 print "ERROR", v