blob: fda68e7b8777c7cff5304e507c8ff4860dcd1fa4 [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
Fredrik Lundhb9056332001-07-11 17:42:21 +000037#
38# Copyright (c) 1999-2001 by Secret Labs AB.
39# Copyright (c) 1999-2001 by Fredrik Lundh.
40#
41# info@pythonware.com
42# http://www.pythonware.com
43#
44# --------------------------------------------------------------------
45# The XML-RPC client interface is
46#
47# Copyright (c) 1999-2001 by Secret Labs AB
48# Copyright (c) 1999-2001 by Fredrik Lundh
49#
50# By obtaining, using, and/or copying this software and/or its
51# associated documentation, you agree that you have read, understood,
52# and will comply with the following terms and conditions:
53#
54# Permission to use, copy, modify, and distribute this software and
55# its associated documentation for any purpose and without fee is
56# hereby granted, provided that the above copyright notice appears in
57# all copies, and that both that copyright notice and this permission
58# notice appear in supporting documentation, and that the name of
59# Secret Labs AB or the author not be used in advertising or publicity
60# pertaining to distribution of the software without specific, written
61# prior permission.
62#
63# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
64# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
65# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR
66# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
67# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
68# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
69# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
70# OF THIS SOFTWARE.
71# --------------------------------------------------------------------
72
73#
74# things to look into before 1.0 final:
75
Fredrik Lundhb9056332001-07-11 17:42:21 +000076# TODO: support basic authentication (see robin's patch)
77# TODO: fix host tuple handling in the server constructor
78# TODO: let transport verify schemes
79# TODO: update documentation
80# TODO: authentication plugins
Fredrik Lundhb9056332001-07-11 17:42:21 +000081
Fred Drake1b410792001-09-04 18:55:03 +000082"""
83An XML-RPC client interface for Python.
84
85The marshalling and response parser code can also be used to
86implement XML-RPC servers.
87
Fred Drake1b410792001-09-04 18:55:03 +000088Exported exceptions:
89
Fredrik Lundhc4c062f2001-09-10 19:45:02 +000090 Error Base class for client errors
91 ProtocolError Indicates an HTTP protocol error
92 ResponseError Indicates a broken response package
93 Fault Indicates an XML-RPC fault package
Fred Drake1b410792001-09-04 18:55:03 +000094
95Exported classes:
96
Fredrik Lundhc4c062f2001-09-10 19:45:02 +000097 ServerProxy Represents a logical connection to an XML-RPC server
Fred Drake1b410792001-09-04 18:55:03 +000098
Fredrik Lundhc4c062f2001-09-10 19:45:02 +000099 Boolean boolean wrapper to generate a "boolean" XML-RPC value
100 DateTime dateTime wrapper for an ISO 8601 string or time tuple or
101 localtime integer value to generate a "dateTime.iso8601"
102 XML-RPC value
103 Binary binary data wrapper
Fred Drake1b410792001-09-04 18:55:03 +0000104
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000105 SlowParser Slow but safe standard parser (based on xmllib)
106 Marshaller Generate an XML-RPC params chunk from a Python data structure
107 Unmarshaller Unmarshal an XML-RPC response from incoming XML event message
108 Transport Handles an HTTP transaction to an XML-RPC server
109 SafeTransport Handles an HTTPS transaction to an XML-RPC server
Fred Drake1b410792001-09-04 18:55:03 +0000110
111Exported constants:
112
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000113 True
114 False
Fred Drake1b410792001-09-04 18:55:03 +0000115
116Exported functions:
117
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000118 boolean Convert any Python value to an XML-RPC boolean
119 getparser Create instance of the fastest available parser & attach
120 to an unmarshalling object
121 dumps Convert an argument tuple or a Fault instance to an XML-RPC
122 request (or response, if the methodresponse option is used).
123 loads Convert an XML-RPC packet to unmarshalled data plus a method
124 name (None if not present).
Fred Drake1b410792001-09-04 18:55:03 +0000125"""
126
Fred Drake2a2d9702001-10-17 01:51:04 +0000127import re, string, time, operator
Fredrik Lundh1538c232001-10-01 19:42:03 +0000128
Fredrik Lundhb9056332001-07-11 17:42:21 +0000129from types import *
Fredrik Lundhb9056332001-07-11 17:42:21 +0000130
131try:
132 unicode
133except NameError:
134 unicode = None # unicode support not available
135
136def _decode(data, encoding, is8bit=re.compile("[\x80-\xff]").search):
137 # decode non-ascii string (if possible)
138 if unicode and encoding and is8bit(data):
139 data = unicode(data, encoding)
140 return data
141
Fredrik Lundh1538c232001-10-01 19:42:03 +0000142def escape(s, replace=string.replace):
143 s = replace(s, "&", "&")
144 s = replace(s, "<", "&lt;")
145 return replace(s, ">", "&gt;",)
146
Fredrik Lundhb9056332001-07-11 17:42:21 +0000147if unicode:
148 def _stringify(string):
149 # convert to 7-bit ascii if possible
150 try:
151 return str(string)
152 except UnicodeError:
153 return string
154else:
155 def _stringify(string):
156 return string
157
Fredrik Lundh1538c232001-10-01 19:42:03 +0000158__version__ = "1.0b4"
Fredrik Lundhb9056332001-07-11 17:42:21 +0000159
160# --------------------------------------------------------------------
161# Exceptions
162
Fredrik Lundh78eedce2001-08-23 20:04:33 +0000163class Error(Exception):
Fred Drake1b410792001-09-04 18:55:03 +0000164 """Base class for client errors."""
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000165 def __str__(self):
166 return repr(self)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000167
168class ProtocolError(Error):
Fred Drake1b410792001-09-04 18:55:03 +0000169 """Indicates an HTTP protocol error."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000170 def __init__(self, url, errcode, errmsg, headers):
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000171 Error.__init__(self)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000172 self.url = url
173 self.errcode = errcode
174 self.errmsg = errmsg
175 self.headers = headers
176 def __repr__(self):
177 return (
178 "<ProtocolError for %s: %s %s>" %
179 (self.url, self.errcode, self.errmsg)
180 )
181
182class ResponseError(Error):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000183 """Indicates a broken response package."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000184 pass
185
186class Fault(Error):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000187 """Indicates an XML-RPC fault package."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000188 def __init__(self, faultCode, faultString, **extra):
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000189 Error.__init__(self)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000190 self.faultCode = faultCode
191 self.faultString = faultString
192 def __repr__(self):
193 return (
194 "<Fault %s: %s>" %
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000195 (self.faultCode, repr(self.faultString))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000196 )
197
198# --------------------------------------------------------------------
199# Special values
200
Fredrik Lundhb9056332001-07-11 17:42:21 +0000201class Boolean:
Fred Drake1b410792001-09-04 18:55:03 +0000202 """Boolean-value wrapper.
203
204 Use True or False to generate a "boolean" XML-RPC value.
205 """
Fredrik Lundhb9056332001-07-11 17:42:21 +0000206
207 def __init__(self, value = 0):
208 self.value = operator.truth(value)
209
210 def encode(self, out):
211 out.write("<value><boolean>%d</boolean></value>\n" % self.value)
212
213 def __cmp__(self, other):
214 if isinstance(other, Boolean):
215 other = other.value
216 return cmp(self.value, other)
217
218 def __repr__(self):
219 if self.value:
220 return "<Boolean True at %x>" % id(self)
221 else:
222 return "<Boolean False at %x>" % id(self)
223
224 def __int__(self):
225 return self.value
226
227 def __nonzero__(self):
228 return self.value
229
230True, False = Boolean(1), Boolean(0)
231
232def boolean(value, truefalse=(False, True)):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000233 """Convert any Python value to XML-RPC 'boolean'."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000234 return truefalse[operator.truth(value)]
235
Fredrik Lundhb9056332001-07-11 17:42:21 +0000236class DateTime:
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000237 """DateTime wrapper for an ISO 8601 string or time tuple or
238 localtime integer value to generate 'dateTime.iso8601' XML-RPC
239 value.
240 """
Fredrik Lundhb9056332001-07-11 17:42:21 +0000241
242 def __init__(self, value=0):
Fredrik Lundh78eedce2001-08-23 20:04:33 +0000243 if not isinstance(value, StringType):
244 if not isinstance(value, TupleType):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000245 if value == 0:
246 value = time.time()
247 value = time.localtime(value)
248 value = time.strftime("%Y%m%dT%H:%M:%S", value)
249 self.value = value
250
251 def __cmp__(self, other):
252 if isinstance(other, DateTime):
253 other = other.value
254 return cmp(self.value, other)
255
256 def __repr__(self):
257 return "<DateTime %s at %x>" % (self.value, id(self))
258
259 def decode(self, data):
260 self.value = string.strip(data)
261
262 def encode(self, out):
263 out.write("<value><dateTime.iso8601>")
264 out.write(self.value)
265 out.write("</dateTime.iso8601></value>\n")
266
267def datetime(data):
268 value = DateTime()
269 value.decode(data)
270 return value
271
Fredrik Lundhb9056332001-07-11 17:42:21 +0000272class Binary:
Fred Drake1b410792001-09-04 18:55:03 +0000273 """Wrapper for binary data."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000274
275 def __init__(self, data=None):
276 self.data = data
277
278 def __cmp__(self, other):
279 if isinstance(other, Binary):
280 other = other.data
281 return cmp(self.data, other)
282
283 def decode(self, data):
284 import base64
285 self.data = base64.decodestring(data)
286
287 def encode(self, out):
288 import base64, StringIO
289 out.write("<value><base64>\n")
290 base64.encode(StringIO.StringIO(self.data), out)
291 out.write("</base64></value>\n")
292
293def binary(data):
294 value = Binary()
295 value.decode(data)
296 return value
297
298WRAPPERS = DateTime, Binary, Boolean
299
300# --------------------------------------------------------------------
301# XML parsers
302
303try:
304 # optional xmlrpclib accelerator. for more information on this
305 # component, contact info@pythonware.com
306 import _xmlrpclib
307 FastParser = _xmlrpclib.Parser
308 FastUnmarshaller = _xmlrpclib.Unmarshaller
309except (AttributeError, ImportError):
310 FastParser = FastUnmarshaller = None
311
312#
313# the SGMLOP parser is about 15x faster than Python's builtin
314# XML parser. SGMLOP sources can be downloaded from:
315#
316# http://www.pythonware.com/products/xml/sgmlop.htm
317#
318
319try:
320 import sgmlop
321 if not hasattr(sgmlop, "XMLParser"):
322 raise ImportError
323except ImportError:
324 SgmlopParser = None # sgmlop accelerator not available
325else:
326 class SgmlopParser:
327 def __init__(self, target):
328
329 # setup callbacks
330 self.finish_starttag = target.start
331 self.finish_endtag = target.end
332 self.handle_data = target.data
333 self.handle_xml = target.xml
334
335 # activate parser
336 self.parser = sgmlop.XMLParser()
337 self.parser.register(self)
338 self.feed = self.parser.feed
339 self.entity = {
340 "amp": "&", "gt": ">", "lt": "<",
341 "apos": "'", "quot": '"'
342 }
343
344 def close(self):
345 try:
346 self.parser.close()
347 finally:
348 self.parser = self.feed = None # nuke circular reference
349
350 def handle_proc(self, tag, attr):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000351 import re
Fredrik Lundhb9056332001-07-11 17:42:21 +0000352 m = re.search("encoding\s*=\s*['\"]([^\"']+)[\"']", attr)
353 if m:
354 self.handle_xml(m.group(1), 1)
355
356 def handle_entityref(self, entity):
357 # <string> entity
358 try:
359 self.handle_data(self.entity[entity])
360 except KeyError:
361 self.handle_data("&%s;" % entity)
362
363try:
364 from xml.parsers import expat
Guido van Rossumb8551342001-10-02 18:33:11 +0000365 if not hasattr(expat, "ParserCreate"):
366 raise ImportError, "ParserCreate"
Fredrik Lundhb9056332001-07-11 17:42:21 +0000367except ImportError:
368 ExpatParser = None
369else:
370 class ExpatParser:
371 # fast expat parser for Python 2.0. this is about 50%
372 # slower than sgmlop, on roundtrip testing
373 def __init__(self, target):
374 self._parser = parser = expat.ParserCreate(None, None)
375 self._target = target
376 parser.StartElementHandler = target.start
377 parser.EndElementHandler = target.end
378 parser.CharacterDataHandler = target.data
379 encoding = None
380 if not parser.returns_unicode:
381 encoding = "utf-8"
382 target.xml(encoding, None)
383
384 def feed(self, data):
385 self._parser.Parse(data, 0)
386
387 def close(self):
388 self._parser.Parse("", 1) # end of data
389 del self._target, self._parser # get rid of circular references
390
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000391class SlowParser:
392 """Default XML parser (based on xmllib.XMLParser)."""
393 # this is about 10 times slower than sgmlop, on roundtrip
394 # testing.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000395 def __init__(self, target):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000396 import xmllib # lazy subclassing (!)
397 if xmllib.XMLParser not in SlowParser.__bases__:
398 SlowParser.__bases__ = (xmllib.XMLParser,)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000399 self.handle_xml = target.xml
400 self.unknown_starttag = target.start
401 self.handle_data = target.data
402 self.unknown_endtag = target.end
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000403 try:
404 xmllib.XMLParser.__init__(self, accept_utf8=1)
405 except TypeError:
406 xmllib.XMLParser.__init__(self) # pre-2.0
Fredrik Lundhb9056332001-07-11 17:42:21 +0000407
408# --------------------------------------------------------------------
409# XML-RPC marshalling and unmarshalling code
410
411class Marshaller:
Fred Drake1b410792001-09-04 18:55:03 +0000412 """Generate an XML-RPC params chunk from a Python data structure.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000413
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000414 Create a Marshaller instance for each set of parameters, and use
415 the "dumps" method to convert your data (represented as a tuple)
416 to an XML-RPC params chunk. To write a fault response, pass a
417 Fault instance instead. You may prefer to use the "dumps" module
418 function for this purpose.
Fred Drake1b410792001-09-04 18:55:03 +0000419 """
Fredrik Lundhb9056332001-07-11 17:42:21 +0000420
421 # by the way, if you don't understand what's going on in here,
422 # that's perfectly ok.
423
424 def __init__(self, encoding=None):
425 self.memo = {}
426 self.data = None
427 self.encoding = encoding
428
429 dispatch = {}
430
431 def dumps(self, values):
432 self.__out = []
433 self.write = write = self.__out.append
434 if isinstance(values, Fault):
435 # fault instance
436 write("<fault>\n")
437 self.__dump(vars(values))
438 write("</fault>\n")
439 else:
440 # parameter block
Fredrik Lundhc266bb02001-08-23 20:13:08 +0000441 # FIXME: the xml-rpc specification allows us to leave out
442 # the entire <params> block if there are no parameters.
443 # however, changing this may break older code (including
444 # old versions of xmlrpclib.py), so this is better left as
445 # is for now. See @XMLRPC3 for more information. /F
Fredrik Lundhb9056332001-07-11 17:42:21 +0000446 write("<params>\n")
447 for v in values:
448 write("<param>\n")
449 self.__dump(v)
450 write("</param>\n")
451 write("</params>\n")
452 result = string.join(self.__out, "")
453 del self.__out, self.write # don't need this any more
454 return result
455
456 def __dump(self, value):
457 try:
458 f = self.dispatch[type(value)]
459 except KeyError:
460 raise TypeError, "cannot marshal %s objects" % type(value)
461 else:
462 f(self, value)
463
464 def dump_int(self, value):
465 self.write("<value><int>%s</int></value>\n" % value)
466 dispatch[IntType] = dump_int
467
Skip Montanaro5e9c71b2001-10-10 15:56:34 +0000468 def dump_long(self, value):
469 val = int(value)
470 self.write("<value><int>%s</int></value>\n" % val)
471 dispatch[LongType] = dump_long
472
Fredrik Lundhb9056332001-07-11 17:42:21 +0000473 def dump_double(self, value):
474 self.write("<value><double>%s</double></value>\n" % value)
475 dispatch[FloatType] = dump_double
476
Fredrik Lundh1538c232001-10-01 19:42:03 +0000477 def dump_string(self, value, escape=escape):
478 self.write("<value><string>%s</string></value>\n" % escape(value))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000479 dispatch[StringType] = dump_string
480
481 if unicode:
Fredrik Lundh1538c232001-10-01 19:42:03 +0000482 def dump_unicode(self, value, escape=escape):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000483 value = value.encode(self.encoding)
Fredrik Lundh1538c232001-10-01 19:42:03 +0000484 self.write("<value><string>%s</string></value>\n" % escape(value))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000485 dispatch[UnicodeType] = dump_unicode
486
Fredrik Lundh1538c232001-10-01 19:42:03 +0000487 def opencontainer(self, value):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000488 if value:
489 i = id(value)
490 if self.memo.has_key(i):
491 raise TypeError, "cannot marshal recursive data structures"
492 self.memo[i] = None
493
Fredrik Lundh1538c232001-10-01 19:42:03 +0000494 def closecontainer(self, value):
Martin v. Löwis5f12d752001-09-30 20:15:41 +0000495 if value:
496 del self.memo[id(value)]
497
Fredrik Lundhb9056332001-07-11 17:42:21 +0000498 def dump_array(self, value):
Fredrik Lundh1538c232001-10-01 19:42:03 +0000499 self.opencontainer(value)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000500 write = self.write
Fredrik Lundh1538c232001-10-01 19:42:03 +0000501 dump = self.__dump
Fredrik Lundhb9056332001-07-11 17:42:21 +0000502 write("<value><array><data>\n")
503 for v in value:
Fredrik Lundh1538c232001-10-01 19:42:03 +0000504 dump(v)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000505 write("</data></array></value>\n")
Fredrik Lundh1538c232001-10-01 19:42:03 +0000506 self.closecontainer(value)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000507 dispatch[TupleType] = dump_array
508 dispatch[ListType] = dump_array
509
Fredrik Lundh1538c232001-10-01 19:42:03 +0000510 def dump_struct(self, value, escape=escape):
511 self.opencontainer(value)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000512 write = self.write
Fredrik Lundh1538c232001-10-01 19:42:03 +0000513 dump = self.__dump
Fredrik Lundhb9056332001-07-11 17:42:21 +0000514 write("<value><struct>\n")
515 for k, v in value.items():
516 write("<member>\n")
517 if type(k) is not StringType:
518 raise TypeError, "dictionary key must be string"
Fredrik Lundh1538c232001-10-01 19:42:03 +0000519 write("<name>%s</name>\n" % escape(k))
520 dump(v)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000521 write("</member>\n")
522 write("</struct></value>\n")
Fredrik Lundh1538c232001-10-01 19:42:03 +0000523 self.closecontainer(value)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000524 dispatch[DictType] = dump_struct
525
526 def dump_instance(self, value):
527 # check for special wrappers
528 if value.__class__ in WRAPPERS:
529 value.encode(self)
530 else:
531 # store instance attributes as a struct (really?)
532 self.dump_struct(value.__dict__)
533 dispatch[InstanceType] = dump_instance
534
535class Unmarshaller:
Fred Drake1b410792001-09-04 18:55:03 +0000536 """Unmarshal an XML-RPC response, based on incoming XML event
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000537 messages (start, data, end). Call close() to get the resulting
Fred Drake1b410792001-09-04 18:55:03 +0000538 data structure.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000539
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000540 Note that this reader is fairly tolerant, and gladly accepts bogus
541 XML-RPC data without complaining (but not bogus XML).
Fred Drake1b410792001-09-04 18:55:03 +0000542 """
Fredrik Lundhb9056332001-07-11 17:42:21 +0000543
544 # and again, if you don't understand what's going on in here,
545 # that's perfectly ok.
546
547 def __init__(self):
548 self._type = None
549 self._stack = []
550 self._marks = []
551 self._data = []
552 self._methodname = None
553 self._encoding = "utf-8"
554 self.append = self._stack.append
555
556 def close(self):
557 # return response tuple and target method
558 if self._type is None or self._marks:
559 raise ResponseError()
560 if self._type == "fault":
561 raise apply(Fault, (), self._stack[0])
562 return tuple(self._stack)
563
564 def getmethodname(self):
565 return self._methodname
566
567 #
568 # event handlers
569
570 def xml(self, encoding, standalone):
571 self._encoding = encoding
572 # FIXME: assert standalone == 1 ???
573
574 def start(self, tag, attrs):
575 # prepare to handle this element
576 if tag == "array" or tag == "struct":
577 self._marks.append(len(self._stack))
578 self._data = []
579 self._value = (tag == "value")
580
581 def data(self, text):
582 self._data.append(text)
583
Fredrik Lundh1538c232001-10-01 19:42:03 +0000584 def end(self, tag, join=string.join):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000585 # call the appropriate end tag handler
586 try:
587 f = self.dispatch[tag]
588 except KeyError:
589 pass # unknown tag ?
590 else:
Fredrik Lundh1538c232001-10-01 19:42:03 +0000591 return f(self, join(self._data, ""))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000592
593 #
594 # accelerator support
595
596 def end_dispatch(self, tag, data):
597 # dispatch data
598 try:
599 f = self.dispatch[tag]
600 except KeyError:
601 pass # unknown tag ?
602 else:
603 return f(self, data)
604
605 #
606 # element decoders
607
608 dispatch = {}
609
Fredrik Lundh1538c232001-10-01 19:42:03 +0000610 def end_boolean(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000611 if data == "0":
612 self.append(False)
613 elif data == "1":
614 self.append(True)
615 else:
616 raise TypeError, "bad boolean value"
617 self._value = 0
618 dispatch["boolean"] = end_boolean
619
Fredrik Lundh1538c232001-10-01 19:42:03 +0000620 def end_int(self, data):
621 self.append(int(data))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000622 self._value = 0
623 dispatch["i4"] = end_int
624 dispatch["int"] = end_int
625
Fredrik Lundh1538c232001-10-01 19:42:03 +0000626 def end_double(self, data):
627 self.append(float(data))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000628 self._value = 0
629 dispatch["double"] = end_double
630
Fredrik Lundh1538c232001-10-01 19:42:03 +0000631 def end_string(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000632 if self._encoding:
633 data = _decode(data, self._encoding)
634 self.append(_stringify(data))
635 self._value = 0
636 dispatch["string"] = end_string
637 dispatch["name"] = end_string # struct keys are always strings
638
639 def end_array(self, data):
640 mark = self._marks[-1]
641 del self._marks[-1]
642 # map arrays to Python lists
643 self._stack[mark:] = [self._stack[mark:]]
644 self._value = 0
645 dispatch["array"] = end_array
646
647 def end_struct(self, data):
648 mark = self._marks[-1]
649 del self._marks[-1]
650 # map structs to Python dictionaries
651 dict = {}
652 items = self._stack[mark:]
653 for i in range(0, len(items), 2):
654 dict[_stringify(items[i])] = items[i+1]
655 self._stack[mark:] = [dict]
656 self._value = 0
657 dispatch["struct"] = end_struct
658
Fredrik Lundh1538c232001-10-01 19:42:03 +0000659 def end_base64(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000660 value = Binary()
Fredrik Lundh1538c232001-10-01 19:42:03 +0000661 value.decode(data)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000662 self.append(value)
663 self._value = 0
664 dispatch["base64"] = end_base64
665
Fredrik Lundh1538c232001-10-01 19:42:03 +0000666 def end_dateTime(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000667 value = DateTime()
Fredrik Lundh1538c232001-10-01 19:42:03 +0000668 value.decode(data)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000669 self.append(value)
670 dispatch["dateTime.iso8601"] = end_dateTime
671
672 def end_value(self, data):
673 # if we stumble upon an value element with no internal
674 # elements, treat it as a string element
675 if self._value:
676 self.end_string(data)
677 dispatch["value"] = end_value
678
679 def end_params(self, data):
680 self._type = "params"
681 dispatch["params"] = end_params
682
683 def end_fault(self, data):
684 self._type = "fault"
685 dispatch["fault"] = end_fault
686
Fredrik Lundh1538c232001-10-01 19:42:03 +0000687 def end_methodName(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000688 if self._encoding:
689 data = _decode(data, self._encoding)
690 self._methodname = data
691 self._type = "methodName" # no params
692 dispatch["methodName"] = end_methodName
693
694
695# --------------------------------------------------------------------
696# convenience functions
697
698def getparser():
699 """getparser() -> parser, unmarshaller
700
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000701 Create an instance of the fastest available parser, and attach it
702 to an unmarshalling object. Return both objects.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000703 """
704 if FastParser and FastUnmarshaller:
705 target = FastUnmarshaller(True, False, binary, datetime)
706 parser = FastParser(target)
707 else:
708 target = Unmarshaller()
709 if FastParser:
710 parser = FastParser(target)
711 elif SgmlopParser:
712 parser = SgmlopParser(target)
713 elif ExpatParser:
714 parser = ExpatParser(target)
715 else:
716 parser = SlowParser(target)
717 return parser, target
718
719def dumps(params, methodname=None, methodresponse=None, encoding=None):
720 """data [,options] -> marshalled data
721
722 Convert an argument tuple or a Fault instance to an XML-RPC
723 request (or response, if the methodresponse option is used).
724
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000725 In addition to the data object, the following options can be given
726 as keyword arguments:
Fredrik Lundhb9056332001-07-11 17:42:21 +0000727
728 methodname: the method name for a methodCall packet
729
730 methodresponse: true to create a methodResponse packet.
731 If this option is used with a tuple, the tuple must be
732 a singleton (i.e. it can contain only one element).
733
734 encoding: the packet encoding (default is UTF-8)
735
736 All 8-bit strings in the data structure are assumed to use the
737 packet encoding. Unicode strings are automatically converted,
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000738 where necessary.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000739 """
740
741 assert isinstance(params, TupleType) or isinstance(params, Fault),\
742 "argument must be tuple or Fault instance"
743
744 if isinstance(params, Fault):
745 methodresponse = 1
746 elif methodresponse and isinstance(params, TupleType):
747 assert len(params) == 1, "response tuple must be a singleton"
748
749 if not encoding:
750 encoding = "utf-8"
751
752 m = Marshaller(encoding)
753 data = m.dumps(params)
754
755 if encoding != "utf-8":
756 xmlheader = "<?xml version='1.0' encoding=%s?>\n" % repr(encoding)
757 else:
758 xmlheader = "<?xml version='1.0'?>\n" # utf-8 is default
759
760 # standard XML-RPC wrappings
761 if methodname:
762 # a method call
763 if not isinstance(methodname, StringType):
764 methodname = methodname.encode(encoding)
765 data = (
766 xmlheader,
767 "<methodCall>\n"
768 "<methodName>", methodname, "</methodName>\n",
769 data,
770 "</methodCall>\n"
771 )
772 elif methodresponse:
773 # a method response, or a fault structure
774 data = (
775 xmlheader,
776 "<methodResponse>\n",
777 data,
778 "</methodResponse>\n"
779 )
780 else:
781 return data # return as is
782 return string.join(data, "")
783
784def loads(data):
785 """data -> unmarshalled data, method name
786
787 Convert an XML-RPC packet to unmarshalled data plus a method
788 name (None if not present).
789
790 If the XML-RPC packet represents a fault condition, this function
791 raises a Fault exception.
792 """
793 p, u = getparser()
794 p.feed(data)
795 p.close()
796 return u.close(), u.getmethodname()
797
798
799# --------------------------------------------------------------------
800# request dispatcher
801
802class _Method:
803 # some magic to bind an XML-RPC method to an RPC server.
804 # supports "nested" methods (e.g. examples.getStateName)
805 def __init__(self, send, name):
806 self.__send = send
807 self.__name = name
808 def __getattr__(self, name):
809 return _Method(self.__send, "%s.%s" % (self.__name, name))
810 def __call__(self, *args):
811 return self.__send(self.__name, args)
812
813
814class Transport:
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000815 """Handles an HTTP transaction to an XML-RPC server."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000816
817 # client identifier (may be overridden)
818 user_agent = "xmlrpclib.py/%s (by www.pythonware.com)" % __version__
819
820 def request(self, host, handler, request_body, verbose=0):
821 # issue XML-RPC request
822
823 h = self.make_connection(host)
824 if verbose:
825 h.set_debuglevel(1)
826
827 self.send_request(h, handler, request_body)
828 self.send_host(h, host)
829 self.send_user_agent(h)
830 self.send_content(h, request_body)
831
832 errcode, errmsg, headers = h.getreply()
833
834 if errcode != 200:
835 raise ProtocolError(
836 host + handler,
837 errcode, errmsg,
838 headers
839 )
840
841 self.verbose = verbose
842
843 return self.parse_response(h.getfile())
844
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000845 def getparser(self):
846 # get parser and unmarshaller
847 return getparser()
848
Fredrik Lundhb9056332001-07-11 17:42:21 +0000849 def make_connection(self, host):
850 # create a HTTP connection object from a host descriptor
851 import httplib
852 return httplib.HTTP(host)
853
854 def send_request(self, connection, handler, request_body):
855 connection.putrequest("POST", handler)
856
857 def send_host(self, connection, host):
858 connection.putheader("Host", host)
859
860 def send_user_agent(self, connection):
861 connection.putheader("User-Agent", self.user_agent)
862
863 def send_content(self, connection, request_body):
864 connection.putheader("Content-Type", "text/xml")
865 connection.putheader("Content-Length", str(len(request_body)))
866 connection.endheaders()
867 if request_body:
868 connection.send(request_body)
869
870 def parse_response(self, f):
871 # read response from input file, and parse it
872
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000873 p, u = self.getparser()
Fredrik Lundhb9056332001-07-11 17:42:21 +0000874
875 while 1:
876 response = f.read(1024)
877 if not response:
878 break
879 if self.verbose:
880 print "body:", repr(response)
881 p.feed(response)
882
883 f.close()
884 p.close()
885
886 return u.close()
887
888class SafeTransport(Transport):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000889 """Handles an HTTPS transaction to an XML-RPC server."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000890
891 def make_connection(self, host):
892 # create a HTTPS connection object from a host descriptor
893 # host may be a string, or a (host, x509-dict) tuple
894 import httplib
895 if isinstance(host, TupleType):
896 host, x509 = host
897 else:
898 x509 = {}
899 try:
900 HTTPS = httplib.HTTPS
901 except AttributeError:
902 raise NotImplementedError,\
903 "your version of httplib doesn't support HTTPS"
904 else:
905 return apply(HTTPS, (host, None), x509)
906
907 def send_host(self, connection, host):
908 if isinstance(host, TupleType):
909 host, x509 = host
910 connection.putheader("Host", host)
911
912class ServerProxy:
913 """uri [,options] -> a logical connection to an XML-RPC server
914
915 uri is the connection point on the server, given as
916 scheme://host/target.
917
918 The standard implementation always supports the "http" scheme. If
919 SSL socket support is available (Python 2.0), it also supports
920 "https".
921
922 If the target part and the slash preceding it are both omitted,
923 "/RPC2" is assumed.
924
925 The following options can be given as keyword arguments:
926
927 transport: a transport factory
928 encoding: the request encoding (default is UTF-8)
929
930 All 8-bit strings passed to the server proxy are assumed to use
931 the given encoding.
932 """
933
934 def __init__(self, uri, transport=None, encoding=None, verbose=0):
935 # establish a "logical" server connection
936
937 # get the url
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000938 import urllib
Fredrik Lundhb9056332001-07-11 17:42:21 +0000939 type, uri = urllib.splittype(uri)
940 if type not in ("http", "https"):
941 raise IOError, "unsupported XML-RPC protocol"
942 self.__host, self.__handler = urllib.splithost(uri)
943 if not self.__handler:
944 self.__handler = "/RPC2"
945
946 if transport is None:
947 if type == "https":
948 transport = SafeTransport()
949 else:
950 transport = Transport()
951 self.__transport = transport
952
953 self.__encoding = encoding
954 self.__verbose = verbose
955
956 def __request(self, methodname, params):
957 # call a method on the remote server
958
959 request = dumps(params, methodname, encoding=self.__encoding)
960
961 response = self.__transport.request(
962 self.__host,
963 self.__handler,
964 request,
965 verbose=self.__verbose
966 )
967
968 if len(response) == 1:
969 response = response[0]
970
971 return response
972
973 def __repr__(self):
974 return (
Fredrik Lundh78eedce2001-08-23 20:04:33 +0000975 "<ServerProxy for %s%s>" %
Fredrik Lundhb9056332001-07-11 17:42:21 +0000976 (self.__host, self.__handler)
977 )
978
979 __str__ = __repr__
980
981 def __getattr__(self, name):
982 # magic method dispatcher
983 return _Method(self.__request, name)
984
985 # note: to call a remote object with an non-standard name, use
986 # result getattr(server, "strange-python-name")(args)
987
Fredrik Lundh78eedce2001-08-23 20:04:33 +0000988# compatibility
Fredrik Lundhb9056332001-07-11 17:42:21 +0000989Server = ServerProxy
990
991# --------------------------------------------------------------------
992# test code
993
994if __name__ == "__main__":
995
996 # simple test program (from the XML-RPC specification)
997
Fredrik Lundh78eedce2001-08-23 20:04:33 +0000998 # server = ServerProxy("http://localhost:8000") # local server
999 server = ServerProxy("http://betty.userland.com")
Fredrik Lundhb9056332001-07-11 17:42:21 +00001000
1001 print server
1002
1003 try:
1004 print server.examples.getStateName(41)
1005 except Error, v:
1006 print "ERROR", v