blob: 4e98c4c8026f62a217a91dd036d0cd0d568ae933 [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)
Fredrik Lundhb9056332001-07-11 17:42:21 +000036#
37# Copyright (c) 1999-2001 by Secret Labs AB.
38# Copyright (c) 1999-2001 by Fredrik Lundh.
39#
40# info@pythonware.com
41# http://www.pythonware.com
42#
43# --------------------------------------------------------------------
44# The XML-RPC client interface is
45#
46# Copyright (c) 1999-2001 by Secret Labs AB
47# Copyright (c) 1999-2001 by Fredrik Lundh
48#
49# By obtaining, using, and/or copying this software and/or its
50# associated documentation, you agree that you have read, understood,
51# and will comply with the following terms and conditions:
52#
53# Permission to use, copy, modify, and distribute this software and
54# its associated documentation for any purpose and without fee is
55# hereby granted, provided that the above copyright notice appears in
56# all copies, and that both that copyright notice and this permission
57# notice appear in supporting documentation, and that the name of
58# Secret Labs AB or the author not be used in advertising or publicity
59# pertaining to distribution of the software without specific, written
60# prior permission.
61#
62# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
63# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
64# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR
65# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
66# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
67# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
68# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
69# OF THIS SOFTWARE.
70# --------------------------------------------------------------------
71
72#
73# things to look into before 1.0 final:
74
Fredrik Lundhb9056332001-07-11 17:42:21 +000075# TODO: support basic authentication (see robin's patch)
76# TODO: fix host tuple handling in the server constructor
77# TODO: let transport verify schemes
78# TODO: update documentation
79# TODO: authentication plugins
Fredrik Lundhb9056332001-07-11 17:42:21 +000080
Fred Drake1b410792001-09-04 18:55:03 +000081"""
82An XML-RPC client interface for Python.
83
84The marshalling and response parser code can also be used to
85implement XML-RPC servers.
86
Fred Drake1b410792001-09-04 18:55:03 +000087Exported exceptions:
88
Fredrik Lundhc4c062f2001-09-10 19:45:02 +000089 Error Base class for client errors
90 ProtocolError Indicates an HTTP protocol error
91 ResponseError Indicates a broken response package
92 Fault Indicates an XML-RPC fault package
Fred Drake1b410792001-09-04 18:55:03 +000093
94Exported classes:
95
Fredrik Lundhc4c062f2001-09-10 19:45:02 +000096 ServerProxy Represents a logical connection to an XML-RPC server
Fred Drake1b410792001-09-04 18:55:03 +000097
Fredrik Lundhc4c062f2001-09-10 19:45:02 +000098 Boolean boolean wrapper to generate a "boolean" XML-RPC value
99 DateTime dateTime wrapper for an ISO 8601 string or time tuple or
100 localtime integer value to generate a "dateTime.iso8601"
101 XML-RPC value
102 Binary binary data wrapper
Fred Drake1b410792001-09-04 18:55:03 +0000103
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000104 SlowParser Slow but safe standard parser (based on xmllib)
105 Marshaller Generate an XML-RPC params chunk from a Python data structure
106 Unmarshaller Unmarshal an XML-RPC response from incoming XML event message
107 Transport Handles an HTTP transaction to an XML-RPC server
108 SafeTransport Handles an HTTPS transaction to an XML-RPC server
Fred Drake1b410792001-09-04 18:55:03 +0000109
110Exported constants:
111
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000112 True
113 False
Fred Drake1b410792001-09-04 18:55:03 +0000114
115Exported functions:
116
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000117 boolean Convert any Python value to an XML-RPC boolean
118 getparser Create instance of the fastest available parser & attach
119 to an unmarshalling object
120 dumps Convert an argument tuple or a Fault instance to an XML-RPC
121 request (or response, if the methodresponse option is used).
122 loads Convert an XML-RPC packet to unmarshalled data plus a method
123 name (None if not present).
Fred Drake1b410792001-09-04 18:55:03 +0000124"""
125
Fredrik Lundh1538c232001-10-01 19:42:03 +0000126import re, string, sys, time, operator
127
Fredrik Lundhb9056332001-07-11 17:42:21 +0000128from types import *
Fredrik Lundhb9056332001-07-11 17:42:21 +0000129
130try:
131 unicode
132except NameError:
133 unicode = None # unicode support not available
134
135def _decode(data, encoding, is8bit=re.compile("[\x80-\xff]").search):
136 # decode non-ascii string (if possible)
137 if unicode and encoding and is8bit(data):
138 data = unicode(data, encoding)
139 return data
140
Fredrik Lundh1538c232001-10-01 19:42:03 +0000141def escape(s, replace=string.replace):
142 s = replace(s, "&", "&")
143 s = replace(s, "<", "&lt;")
144 return replace(s, ">", "&gt;",)
145
Fredrik Lundhb9056332001-07-11 17:42:21 +0000146if unicode:
147 def _stringify(string):
148 # convert to 7-bit ascii if possible
149 try:
150 return str(string)
151 except UnicodeError:
152 return string
153else:
154 def _stringify(string):
155 return string
156
Fredrik Lundh1538c232001-10-01 19:42:03 +0000157__version__ = "1.0b4"
Fredrik Lundhb9056332001-07-11 17:42:21 +0000158
159# --------------------------------------------------------------------
160# Exceptions
161
Fredrik Lundh78eedce2001-08-23 20:04:33 +0000162class Error(Exception):
Fred Drake1b410792001-09-04 18:55:03 +0000163 """Base class for client errors."""
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000164 def __str__(self):
165 return repr(self)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000166
167class ProtocolError(Error):
Fred Drake1b410792001-09-04 18:55:03 +0000168 """Indicates an HTTP protocol error."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000169 def __init__(self, url, errcode, errmsg, headers):
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000170 Error.__init__(self)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000171 self.url = url
172 self.errcode = errcode
173 self.errmsg = errmsg
174 self.headers = headers
175 def __repr__(self):
176 return (
177 "<ProtocolError for %s: %s %s>" %
178 (self.url, self.errcode, self.errmsg)
179 )
180
181class ResponseError(Error):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000182 """Indicates a broken response package."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000183 pass
184
185class Fault(Error):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000186 """Indicates an XML-RPC fault package."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000187 def __init__(self, faultCode, faultString, **extra):
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000188 Error.__init__(self)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000189 self.faultCode = faultCode
190 self.faultString = faultString
191 def __repr__(self):
192 return (
193 "<Fault %s: %s>" %
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000194 (self.faultCode, repr(self.faultString))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000195 )
196
197# --------------------------------------------------------------------
198# Special values
199
Fredrik Lundhb9056332001-07-11 17:42:21 +0000200class Boolean:
Fred Drake1b410792001-09-04 18:55:03 +0000201 """Boolean-value wrapper.
202
203 Use True or False to generate a "boolean" XML-RPC value.
204 """
Fredrik Lundhb9056332001-07-11 17:42:21 +0000205
206 def __init__(self, value = 0):
207 self.value = operator.truth(value)
208
209 def encode(self, out):
210 out.write("<value><boolean>%d</boolean></value>\n" % self.value)
211
212 def __cmp__(self, other):
213 if isinstance(other, Boolean):
214 other = other.value
215 return cmp(self.value, other)
216
217 def __repr__(self):
218 if self.value:
219 return "<Boolean True at %x>" % id(self)
220 else:
221 return "<Boolean False at %x>" % id(self)
222
223 def __int__(self):
224 return self.value
225
226 def __nonzero__(self):
227 return self.value
228
229True, False = Boolean(1), Boolean(0)
230
231def boolean(value, truefalse=(False, True)):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000232 """Convert any Python value to XML-RPC 'boolean'."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000233 return truefalse[operator.truth(value)]
234
Fredrik Lundhb9056332001-07-11 17:42:21 +0000235class DateTime:
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000236 """DateTime wrapper for an ISO 8601 string or time tuple or
237 localtime integer value to generate 'dateTime.iso8601' XML-RPC
238 value.
239 """
Fredrik Lundhb9056332001-07-11 17:42:21 +0000240
241 def __init__(self, value=0):
Fredrik Lundh78eedce2001-08-23 20:04:33 +0000242 if not isinstance(value, StringType):
243 if not isinstance(value, TupleType):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000244 if value == 0:
245 value = time.time()
246 value = time.localtime(value)
247 value = time.strftime("%Y%m%dT%H:%M:%S", value)
248 self.value = value
249
250 def __cmp__(self, other):
251 if isinstance(other, DateTime):
252 other = other.value
253 return cmp(self.value, other)
254
255 def __repr__(self):
256 return "<DateTime %s at %x>" % (self.value, id(self))
257
258 def decode(self, data):
259 self.value = string.strip(data)
260
261 def encode(self, out):
262 out.write("<value><dateTime.iso8601>")
263 out.write(self.value)
264 out.write("</dateTime.iso8601></value>\n")
265
266def datetime(data):
267 value = DateTime()
268 value.decode(data)
269 return value
270
Fredrik Lundhb9056332001-07-11 17:42:21 +0000271class Binary:
Fred Drake1b410792001-09-04 18:55:03 +0000272 """Wrapper for binary data."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000273
274 def __init__(self, data=None):
275 self.data = data
276
277 def __cmp__(self, other):
278 if isinstance(other, Binary):
279 other = other.data
280 return cmp(self.data, other)
281
282 def decode(self, data):
283 import base64
284 self.data = base64.decodestring(data)
285
286 def encode(self, out):
287 import base64, StringIO
288 out.write("<value><base64>\n")
289 base64.encode(StringIO.StringIO(self.data), out)
290 out.write("</base64></value>\n")
291
292def binary(data):
293 value = Binary()
294 value.decode(data)
295 return value
296
297WRAPPERS = DateTime, Binary, Boolean
298
299# --------------------------------------------------------------------
300# XML parsers
301
302try:
303 # optional xmlrpclib accelerator. for more information on this
304 # component, contact info@pythonware.com
305 import _xmlrpclib
306 FastParser = _xmlrpclib.Parser
307 FastUnmarshaller = _xmlrpclib.Unmarshaller
308except (AttributeError, ImportError):
309 FastParser = FastUnmarshaller = None
310
311#
312# the SGMLOP parser is about 15x faster than Python's builtin
313# XML parser. SGMLOP sources can be downloaded from:
314#
315# http://www.pythonware.com/products/xml/sgmlop.htm
316#
317
318try:
319 import sgmlop
320 if not hasattr(sgmlop, "XMLParser"):
321 raise ImportError
322except ImportError:
323 SgmlopParser = None # sgmlop accelerator not available
324else:
325 class SgmlopParser:
326 def __init__(self, target):
327
328 # setup callbacks
329 self.finish_starttag = target.start
330 self.finish_endtag = target.end
331 self.handle_data = target.data
332 self.handle_xml = target.xml
333
334 # activate parser
335 self.parser = sgmlop.XMLParser()
336 self.parser.register(self)
337 self.feed = self.parser.feed
338 self.entity = {
339 "amp": "&", "gt": ">", "lt": "<",
340 "apos": "'", "quot": '"'
341 }
342
343 def close(self):
344 try:
345 self.parser.close()
346 finally:
347 self.parser = self.feed = None # nuke circular reference
348
349 def handle_proc(self, tag, attr):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000350 import re
Fredrik Lundhb9056332001-07-11 17:42:21 +0000351 m = re.search("encoding\s*=\s*['\"]([^\"']+)[\"']", attr)
352 if m:
353 self.handle_xml(m.group(1), 1)
354
355 def handle_entityref(self, entity):
356 # <string> entity
357 try:
358 self.handle_data(self.entity[entity])
359 except KeyError:
360 self.handle_data("&%s;" % entity)
361
362try:
363 from xml.parsers import expat
Guido van Rossumb8551342001-10-02 18:33:11 +0000364 if not hasattr(expat, "ParserCreate"):
365 raise ImportError, "ParserCreate"
Fredrik Lundhb9056332001-07-11 17:42:21 +0000366except ImportError:
367 ExpatParser = None
368else:
369 class ExpatParser:
370 # fast expat parser for Python 2.0. this is about 50%
371 # slower than sgmlop, on roundtrip testing
372 def __init__(self, target):
373 self._parser = parser = expat.ParserCreate(None, None)
374 self._target = target
375 parser.StartElementHandler = target.start
376 parser.EndElementHandler = target.end
377 parser.CharacterDataHandler = target.data
378 encoding = None
379 if not parser.returns_unicode:
380 encoding = "utf-8"
381 target.xml(encoding, None)
382
383 def feed(self, data):
384 self._parser.Parse(data, 0)
385
386 def close(self):
387 self._parser.Parse("", 1) # end of data
388 del self._target, self._parser # get rid of circular references
389
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000390class SlowParser:
391 """Default XML parser (based on xmllib.XMLParser)."""
392 # this is about 10 times slower than sgmlop, on roundtrip
393 # testing.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000394 def __init__(self, target):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000395 import xmllib # lazy subclassing (!)
396 if xmllib.XMLParser not in SlowParser.__bases__:
397 SlowParser.__bases__ = (xmllib.XMLParser,)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000398 self.handle_xml = target.xml
399 self.unknown_starttag = target.start
400 self.handle_data = target.data
401 self.unknown_endtag = target.end
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000402 try:
403 xmllib.XMLParser.__init__(self, accept_utf8=1)
404 except TypeError:
405 xmllib.XMLParser.__init__(self) # pre-2.0
Fredrik Lundhb9056332001-07-11 17:42:21 +0000406
407# --------------------------------------------------------------------
408# XML-RPC marshalling and unmarshalling code
409
410class Marshaller:
Fred Drake1b410792001-09-04 18:55:03 +0000411 """Generate an XML-RPC params chunk from a Python data structure.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000412
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000413 Create a Marshaller instance for each set of parameters, and use
414 the "dumps" method to convert your data (represented as a tuple)
415 to an XML-RPC params chunk. To write a fault response, pass a
416 Fault instance instead. You may prefer to use the "dumps" module
417 function for this purpose.
Fred Drake1b410792001-09-04 18:55:03 +0000418 """
Fredrik Lundhb9056332001-07-11 17:42:21 +0000419
420 # by the way, if you don't understand what's going on in here,
421 # that's perfectly ok.
422
423 def __init__(self, encoding=None):
424 self.memo = {}
425 self.data = None
426 self.encoding = encoding
427
428 dispatch = {}
429
430 def dumps(self, values):
431 self.__out = []
432 self.write = write = self.__out.append
433 if isinstance(values, Fault):
434 # fault instance
435 write("<fault>\n")
436 self.__dump(vars(values))
437 write("</fault>\n")
438 else:
439 # parameter block
Fredrik Lundhc266bb02001-08-23 20:13:08 +0000440 # FIXME: the xml-rpc specification allows us to leave out
441 # the entire <params> block if there are no parameters.
442 # however, changing this may break older code (including
443 # old versions of xmlrpclib.py), so this is better left as
444 # is for now. See @XMLRPC3 for more information. /F
Fredrik Lundhb9056332001-07-11 17:42:21 +0000445 write("<params>\n")
446 for v in values:
447 write("<param>\n")
448 self.__dump(v)
449 write("</param>\n")
450 write("</params>\n")
451 result = string.join(self.__out, "")
452 del self.__out, self.write # don't need this any more
453 return result
454
455 def __dump(self, value):
456 try:
457 f = self.dispatch[type(value)]
458 except KeyError:
459 raise TypeError, "cannot marshal %s objects" % type(value)
460 else:
461 f(self, value)
462
463 def dump_int(self, value):
464 self.write("<value><int>%s</int></value>\n" % value)
465 dispatch[IntType] = dump_int
466
467 def dump_double(self, value):
468 self.write("<value><double>%s</double></value>\n" % value)
469 dispatch[FloatType] = dump_double
470
Fredrik Lundh1538c232001-10-01 19:42:03 +0000471 def dump_string(self, value, escape=escape):
472 self.write("<value><string>%s</string></value>\n" % escape(value))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000473 dispatch[StringType] = dump_string
474
475 if unicode:
Fredrik Lundh1538c232001-10-01 19:42:03 +0000476 def dump_unicode(self, value, escape=escape):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000477 value = value.encode(self.encoding)
Fredrik Lundh1538c232001-10-01 19:42:03 +0000478 self.write("<value><string>%s</string></value>\n" % escape(value))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000479 dispatch[UnicodeType] = dump_unicode
480
Fredrik Lundh1538c232001-10-01 19:42:03 +0000481 def opencontainer(self, value):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000482 if value:
483 i = id(value)
484 if self.memo.has_key(i):
485 raise TypeError, "cannot marshal recursive data structures"
486 self.memo[i] = None
487
Fredrik Lundh1538c232001-10-01 19:42:03 +0000488 def closecontainer(self, value):
Martin v. Löwis5f12d752001-09-30 20:15:41 +0000489 if value:
490 del self.memo[id(value)]
491
Fredrik Lundhb9056332001-07-11 17:42:21 +0000492 def dump_array(self, value):
Fredrik Lundh1538c232001-10-01 19:42:03 +0000493 self.opencontainer(value)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000494 write = self.write
Fredrik Lundh1538c232001-10-01 19:42:03 +0000495 dump = self.__dump
Fredrik Lundhb9056332001-07-11 17:42:21 +0000496 write("<value><array><data>\n")
497 for v in value:
Fredrik Lundh1538c232001-10-01 19:42:03 +0000498 dump(v)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000499 write("</data></array></value>\n")
Fredrik Lundh1538c232001-10-01 19:42:03 +0000500 self.closecontainer(value)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000501 dispatch[TupleType] = dump_array
502 dispatch[ListType] = dump_array
503
Fredrik Lundh1538c232001-10-01 19:42:03 +0000504 def dump_struct(self, value, escape=escape):
505 self.opencontainer(value)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000506 write = self.write
Fredrik Lundh1538c232001-10-01 19:42:03 +0000507 dump = self.__dump
Fredrik Lundhb9056332001-07-11 17:42:21 +0000508 write("<value><struct>\n")
509 for k, v in value.items():
510 write("<member>\n")
511 if type(k) is not StringType:
512 raise TypeError, "dictionary key must be string"
Fredrik Lundh1538c232001-10-01 19:42:03 +0000513 write("<name>%s</name>\n" % escape(k))
514 dump(v)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000515 write("</member>\n")
516 write("</struct></value>\n")
Fredrik Lundh1538c232001-10-01 19:42:03 +0000517 self.closecontainer(value)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000518 dispatch[DictType] = dump_struct
519
520 def dump_instance(self, value):
521 # check for special wrappers
522 if value.__class__ in WRAPPERS:
523 value.encode(self)
524 else:
525 # store instance attributes as a struct (really?)
526 self.dump_struct(value.__dict__)
527 dispatch[InstanceType] = dump_instance
528
529class Unmarshaller:
Fred Drake1b410792001-09-04 18:55:03 +0000530 """Unmarshal an XML-RPC response, based on incoming XML event
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000531 messages (start, data, end). Call close() to get the resulting
Fred Drake1b410792001-09-04 18:55:03 +0000532 data structure.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000533
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000534 Note that this reader is fairly tolerant, and gladly accepts bogus
535 XML-RPC data without complaining (but not bogus XML).
Fred Drake1b410792001-09-04 18:55:03 +0000536 """
Fredrik Lundhb9056332001-07-11 17:42:21 +0000537
538 # and again, if you don't understand what's going on in here,
539 # that's perfectly ok.
540
541 def __init__(self):
542 self._type = None
543 self._stack = []
544 self._marks = []
545 self._data = []
546 self._methodname = None
547 self._encoding = "utf-8"
548 self.append = self._stack.append
549
550 def close(self):
551 # return response tuple and target method
552 if self._type is None or self._marks:
553 raise ResponseError()
554 if self._type == "fault":
555 raise apply(Fault, (), self._stack[0])
556 return tuple(self._stack)
557
558 def getmethodname(self):
559 return self._methodname
560
561 #
562 # event handlers
563
564 def xml(self, encoding, standalone):
565 self._encoding = encoding
566 # FIXME: assert standalone == 1 ???
567
568 def start(self, tag, attrs):
569 # prepare to handle this element
570 if tag == "array" or tag == "struct":
571 self._marks.append(len(self._stack))
572 self._data = []
573 self._value = (tag == "value")
574
575 def data(self, text):
576 self._data.append(text)
577
Fredrik Lundh1538c232001-10-01 19:42:03 +0000578 def end(self, tag, join=string.join):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000579 # call the appropriate end tag handler
580 try:
581 f = self.dispatch[tag]
582 except KeyError:
583 pass # unknown tag ?
584 else:
Fredrik Lundh1538c232001-10-01 19:42:03 +0000585 return f(self, join(self._data, ""))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000586
587 #
588 # accelerator support
589
590 def end_dispatch(self, tag, data):
591 # dispatch data
592 try:
593 f = self.dispatch[tag]
594 except KeyError:
595 pass # unknown tag ?
596 else:
597 return f(self, data)
598
599 #
600 # element decoders
601
602 dispatch = {}
603
Fredrik Lundh1538c232001-10-01 19:42:03 +0000604 def end_boolean(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000605 if data == "0":
606 self.append(False)
607 elif data == "1":
608 self.append(True)
609 else:
610 raise TypeError, "bad boolean value"
611 self._value = 0
612 dispatch["boolean"] = end_boolean
613
Fredrik Lundh1538c232001-10-01 19:42:03 +0000614 def end_int(self, data):
615 self.append(int(data))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000616 self._value = 0
617 dispatch["i4"] = end_int
618 dispatch["int"] = end_int
619
Fredrik Lundh1538c232001-10-01 19:42:03 +0000620 def end_double(self, data):
621 self.append(float(data))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000622 self._value = 0
623 dispatch["double"] = end_double
624
Fredrik Lundh1538c232001-10-01 19:42:03 +0000625 def end_string(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000626 if self._encoding:
627 data = _decode(data, self._encoding)
628 self.append(_stringify(data))
629 self._value = 0
630 dispatch["string"] = end_string
631 dispatch["name"] = end_string # struct keys are always strings
632
633 def end_array(self, data):
634 mark = self._marks[-1]
635 del self._marks[-1]
636 # map arrays to Python lists
637 self._stack[mark:] = [self._stack[mark:]]
638 self._value = 0
639 dispatch["array"] = end_array
640
641 def end_struct(self, data):
642 mark = self._marks[-1]
643 del self._marks[-1]
644 # map structs to Python dictionaries
645 dict = {}
646 items = self._stack[mark:]
647 for i in range(0, len(items), 2):
648 dict[_stringify(items[i])] = items[i+1]
649 self._stack[mark:] = [dict]
650 self._value = 0
651 dispatch["struct"] = end_struct
652
Fredrik Lundh1538c232001-10-01 19:42:03 +0000653 def end_base64(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000654 value = Binary()
Fredrik Lundh1538c232001-10-01 19:42:03 +0000655 value.decode(data)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000656 self.append(value)
657 self._value = 0
658 dispatch["base64"] = end_base64
659
Fredrik Lundh1538c232001-10-01 19:42:03 +0000660 def end_dateTime(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000661 value = DateTime()
Fredrik Lundh1538c232001-10-01 19:42:03 +0000662 value.decode(data)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000663 self.append(value)
664 dispatch["dateTime.iso8601"] = end_dateTime
665
666 def end_value(self, data):
667 # if we stumble upon an value element with no internal
668 # elements, treat it as a string element
669 if self._value:
670 self.end_string(data)
671 dispatch["value"] = end_value
672
673 def end_params(self, data):
674 self._type = "params"
675 dispatch["params"] = end_params
676
677 def end_fault(self, data):
678 self._type = "fault"
679 dispatch["fault"] = end_fault
680
Fredrik Lundh1538c232001-10-01 19:42:03 +0000681 def end_methodName(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000682 if self._encoding:
683 data = _decode(data, self._encoding)
684 self._methodname = data
685 self._type = "methodName" # no params
686 dispatch["methodName"] = end_methodName
687
688
689# --------------------------------------------------------------------
690# convenience functions
691
692def getparser():
693 """getparser() -> parser, unmarshaller
694
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000695 Create an instance of the fastest available parser, and attach it
696 to an unmarshalling object. Return both objects.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000697 """
698 if FastParser and FastUnmarshaller:
699 target = FastUnmarshaller(True, False, binary, datetime)
700 parser = FastParser(target)
701 else:
702 target = Unmarshaller()
703 if FastParser:
704 parser = FastParser(target)
705 elif SgmlopParser:
706 parser = SgmlopParser(target)
707 elif ExpatParser:
708 parser = ExpatParser(target)
709 else:
710 parser = SlowParser(target)
711 return parser, target
712
713def dumps(params, methodname=None, methodresponse=None, encoding=None):
714 """data [,options] -> marshalled data
715
716 Convert an argument tuple or a Fault instance to an XML-RPC
717 request (or response, if the methodresponse option is used).
718
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000719 In addition to the data object, the following options can be given
720 as keyword arguments:
Fredrik Lundhb9056332001-07-11 17:42:21 +0000721
722 methodname: the method name for a methodCall packet
723
724 methodresponse: true to create a methodResponse packet.
725 If this option is used with a tuple, the tuple must be
726 a singleton (i.e. it can contain only one element).
727
728 encoding: the packet encoding (default is UTF-8)
729
730 All 8-bit strings in the data structure are assumed to use the
731 packet encoding. Unicode strings are automatically converted,
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000732 where necessary.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000733 """
734
735 assert isinstance(params, TupleType) or isinstance(params, Fault),\
736 "argument must be tuple or Fault instance"
737
738 if isinstance(params, Fault):
739 methodresponse = 1
740 elif methodresponse and isinstance(params, TupleType):
741 assert len(params) == 1, "response tuple must be a singleton"
742
743 if not encoding:
744 encoding = "utf-8"
745
746 m = Marshaller(encoding)
747 data = m.dumps(params)
748
749 if encoding != "utf-8":
750 xmlheader = "<?xml version='1.0' encoding=%s?>\n" % repr(encoding)
751 else:
752 xmlheader = "<?xml version='1.0'?>\n" # utf-8 is default
753
754 # standard XML-RPC wrappings
755 if methodname:
756 # a method call
757 if not isinstance(methodname, StringType):
758 methodname = methodname.encode(encoding)
759 data = (
760 xmlheader,
761 "<methodCall>\n"
762 "<methodName>", methodname, "</methodName>\n",
763 data,
764 "</methodCall>\n"
765 )
766 elif methodresponse:
767 # a method response, or a fault structure
768 data = (
769 xmlheader,
770 "<methodResponse>\n",
771 data,
772 "</methodResponse>\n"
773 )
774 else:
775 return data # return as is
776 return string.join(data, "")
777
778def loads(data):
779 """data -> unmarshalled data, method name
780
781 Convert an XML-RPC packet to unmarshalled data plus a method
782 name (None if not present).
783
784 If the XML-RPC packet represents a fault condition, this function
785 raises a Fault exception.
786 """
787 p, u = getparser()
788 p.feed(data)
789 p.close()
790 return u.close(), u.getmethodname()
791
792
793# --------------------------------------------------------------------
794# request dispatcher
795
796class _Method:
797 # some magic to bind an XML-RPC method to an RPC server.
798 # supports "nested" methods (e.g. examples.getStateName)
799 def __init__(self, send, name):
800 self.__send = send
801 self.__name = name
802 def __getattr__(self, name):
803 return _Method(self.__send, "%s.%s" % (self.__name, name))
804 def __call__(self, *args):
805 return self.__send(self.__name, args)
806
807
808class Transport:
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000809 """Handles an HTTP transaction to an XML-RPC server."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000810
811 # client identifier (may be overridden)
812 user_agent = "xmlrpclib.py/%s (by www.pythonware.com)" % __version__
813
814 def request(self, host, handler, request_body, verbose=0):
815 # issue XML-RPC request
816
817 h = self.make_connection(host)
818 if verbose:
819 h.set_debuglevel(1)
820
821 self.send_request(h, handler, request_body)
822 self.send_host(h, host)
823 self.send_user_agent(h)
824 self.send_content(h, request_body)
825
826 errcode, errmsg, headers = h.getreply()
827
828 if errcode != 200:
829 raise ProtocolError(
830 host + handler,
831 errcode, errmsg,
832 headers
833 )
834
835 self.verbose = verbose
836
837 return self.parse_response(h.getfile())
838
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000839 def getparser(self):
840 # get parser and unmarshaller
841 return getparser()
842
Fredrik Lundhb9056332001-07-11 17:42:21 +0000843 def make_connection(self, host):
844 # create a HTTP connection object from a host descriptor
845 import httplib
846 return httplib.HTTP(host)
847
848 def send_request(self, connection, handler, request_body):
849 connection.putrequest("POST", handler)
850
851 def send_host(self, connection, host):
852 connection.putheader("Host", host)
853
854 def send_user_agent(self, connection):
855 connection.putheader("User-Agent", self.user_agent)
856
857 def send_content(self, connection, request_body):
858 connection.putheader("Content-Type", "text/xml")
859 connection.putheader("Content-Length", str(len(request_body)))
860 connection.endheaders()
861 if request_body:
862 connection.send(request_body)
863
864 def parse_response(self, f):
865 # read response from input file, and parse it
866
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000867 p, u = self.getparser()
Fredrik Lundhb9056332001-07-11 17:42:21 +0000868
869 while 1:
870 response = f.read(1024)
871 if not response:
872 break
873 if self.verbose:
874 print "body:", repr(response)
875 p.feed(response)
876
877 f.close()
878 p.close()
879
880 return u.close()
881
882class SafeTransport(Transport):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000883 """Handles an HTTPS transaction to an XML-RPC server."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000884
885 def make_connection(self, host):
886 # create a HTTPS connection object from a host descriptor
887 # host may be a string, or a (host, x509-dict) tuple
888 import httplib
889 if isinstance(host, TupleType):
890 host, x509 = host
891 else:
892 x509 = {}
893 try:
894 HTTPS = httplib.HTTPS
895 except AttributeError:
896 raise NotImplementedError,\
897 "your version of httplib doesn't support HTTPS"
898 else:
899 return apply(HTTPS, (host, None), x509)
900
901 def send_host(self, connection, host):
902 if isinstance(host, TupleType):
903 host, x509 = host
904 connection.putheader("Host", host)
905
906class ServerProxy:
907 """uri [,options] -> a logical connection to an XML-RPC server
908
909 uri is the connection point on the server, given as
910 scheme://host/target.
911
912 The standard implementation always supports the "http" scheme. If
913 SSL socket support is available (Python 2.0), it also supports
914 "https".
915
916 If the target part and the slash preceding it are both omitted,
917 "/RPC2" is assumed.
918
919 The following options can be given as keyword arguments:
920
921 transport: a transport factory
922 encoding: the request encoding (default is UTF-8)
923
924 All 8-bit strings passed to the server proxy are assumed to use
925 the given encoding.
926 """
927
928 def __init__(self, uri, transport=None, encoding=None, verbose=0):
929 # establish a "logical" server connection
930
931 # get the url
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000932 import urllib
Fredrik Lundhb9056332001-07-11 17:42:21 +0000933 type, uri = urllib.splittype(uri)
934 if type not in ("http", "https"):
935 raise IOError, "unsupported XML-RPC protocol"
936 self.__host, self.__handler = urllib.splithost(uri)
937 if not self.__handler:
938 self.__handler = "/RPC2"
939
940 if transport is None:
941 if type == "https":
942 transport = SafeTransport()
943 else:
944 transport = Transport()
945 self.__transport = transport
946
947 self.__encoding = encoding
948 self.__verbose = verbose
949
950 def __request(self, methodname, params):
951 # call a method on the remote server
952
953 request = dumps(params, methodname, encoding=self.__encoding)
954
955 response = self.__transport.request(
956 self.__host,
957 self.__handler,
958 request,
959 verbose=self.__verbose
960 )
961
962 if len(response) == 1:
963 response = response[0]
964
965 return response
966
967 def __repr__(self):
968 return (
Fredrik Lundh78eedce2001-08-23 20:04:33 +0000969 "<ServerProxy for %s%s>" %
Fredrik Lundhb9056332001-07-11 17:42:21 +0000970 (self.__host, self.__handler)
971 )
972
973 __str__ = __repr__
974
975 def __getattr__(self, name):
976 # magic method dispatcher
977 return _Method(self.__request, name)
978
979 # note: to call a remote object with an non-standard name, use
980 # result getattr(server, "strange-python-name")(args)
981
Fredrik Lundh78eedce2001-08-23 20:04:33 +0000982# compatibility
Fredrik Lundhb9056332001-07-11 17:42:21 +0000983Server = ServerProxy
984
985# --------------------------------------------------------------------
986# test code
987
988if __name__ == "__main__":
989
990 # simple test program (from the XML-RPC specification)
991
Fredrik Lundh78eedce2001-08-23 20:04:33 +0000992 # server = ServerProxy("http://localhost:8000") # local server
993 server = ServerProxy("http://betty.userland.com")
Fredrik Lundhb9056332001-07-11 17:42:21 +0000994
995 print server
996
997 try:
998 print server.examples.getStateName(41)
999 except Error, v:
1000 print "ERROR", v