blob: 3524a45bdaa77cf43164f4a9714a61248c3cffcb [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
364except ImportError:
365 ExpatParser = None
366else:
367 class ExpatParser:
368 # fast expat parser for Python 2.0. this is about 50%
369 # slower than sgmlop, on roundtrip testing
370 def __init__(self, target):
371 self._parser = parser = expat.ParserCreate(None, None)
372 self._target = target
373 parser.StartElementHandler = target.start
374 parser.EndElementHandler = target.end
375 parser.CharacterDataHandler = target.data
376 encoding = None
377 if not parser.returns_unicode:
378 encoding = "utf-8"
379 target.xml(encoding, None)
380
381 def feed(self, data):
382 self._parser.Parse(data, 0)
383
384 def close(self):
385 self._parser.Parse("", 1) # end of data
386 del self._target, self._parser # get rid of circular references
387
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000388class SlowParser:
389 """Default XML parser (based on xmllib.XMLParser)."""
390 # this is about 10 times slower than sgmlop, on roundtrip
391 # testing.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000392 def __init__(self, target):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000393 import xmllib # lazy subclassing (!)
394 if xmllib.XMLParser not in SlowParser.__bases__:
395 SlowParser.__bases__ = (xmllib.XMLParser,)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000396 self.handle_xml = target.xml
397 self.unknown_starttag = target.start
398 self.handle_data = target.data
399 self.unknown_endtag = target.end
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000400 try:
401 xmllib.XMLParser.__init__(self, accept_utf8=1)
402 except TypeError:
403 xmllib.XMLParser.__init__(self) # pre-2.0
Fredrik Lundhb9056332001-07-11 17:42:21 +0000404
405# --------------------------------------------------------------------
406# XML-RPC marshalling and unmarshalling code
407
408class Marshaller:
Fred Drake1b410792001-09-04 18:55:03 +0000409 """Generate an XML-RPC params chunk from a Python data structure.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000410
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000411 Create a Marshaller instance for each set of parameters, and use
412 the "dumps" method to convert your data (represented as a tuple)
413 to an XML-RPC params chunk. To write a fault response, pass a
414 Fault instance instead. You may prefer to use the "dumps" module
415 function for this purpose.
Fred Drake1b410792001-09-04 18:55:03 +0000416 """
Fredrik Lundhb9056332001-07-11 17:42:21 +0000417
418 # by the way, if you don't understand what's going on in here,
419 # that's perfectly ok.
420
421 def __init__(self, encoding=None):
422 self.memo = {}
423 self.data = None
424 self.encoding = encoding
425
426 dispatch = {}
427
428 def dumps(self, values):
429 self.__out = []
430 self.write = write = self.__out.append
431 if isinstance(values, Fault):
432 # fault instance
433 write("<fault>\n")
434 self.__dump(vars(values))
435 write("</fault>\n")
436 else:
437 # parameter block
Fredrik Lundhc266bb02001-08-23 20:13:08 +0000438 # FIXME: the xml-rpc specification allows us to leave out
439 # the entire <params> block if there are no parameters.
440 # however, changing this may break older code (including
441 # old versions of xmlrpclib.py), so this is better left as
442 # is for now. See @XMLRPC3 for more information. /F
Fredrik Lundhb9056332001-07-11 17:42:21 +0000443 write("<params>\n")
444 for v in values:
445 write("<param>\n")
446 self.__dump(v)
447 write("</param>\n")
448 write("</params>\n")
449 result = string.join(self.__out, "")
450 del self.__out, self.write # don't need this any more
451 return result
452
453 def __dump(self, value):
454 try:
455 f = self.dispatch[type(value)]
456 except KeyError:
457 raise TypeError, "cannot marshal %s objects" % type(value)
458 else:
459 f(self, value)
460
461 def dump_int(self, value):
462 self.write("<value><int>%s</int></value>\n" % value)
463 dispatch[IntType] = dump_int
464
465 def dump_double(self, value):
466 self.write("<value><double>%s</double></value>\n" % value)
467 dispatch[FloatType] = dump_double
468
Fredrik Lundh1538c232001-10-01 19:42:03 +0000469 def dump_string(self, value, escape=escape):
470 self.write("<value><string>%s</string></value>\n" % escape(value))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000471 dispatch[StringType] = dump_string
472
473 if unicode:
Fredrik Lundh1538c232001-10-01 19:42:03 +0000474 def dump_unicode(self, value, escape=escape):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000475 value = value.encode(self.encoding)
Fredrik Lundh1538c232001-10-01 19:42:03 +0000476 self.write("<value><string>%s</string></value>\n" % escape(value))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000477 dispatch[UnicodeType] = dump_unicode
478
Fredrik Lundh1538c232001-10-01 19:42:03 +0000479 def opencontainer(self, value):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000480 if value:
481 i = id(value)
482 if self.memo.has_key(i):
483 raise TypeError, "cannot marshal recursive data structures"
484 self.memo[i] = None
485
Fredrik Lundh1538c232001-10-01 19:42:03 +0000486 def closecontainer(self, value):
Martin v. Löwis5f12d752001-09-30 20:15:41 +0000487 if value:
488 del self.memo[id(value)]
489
Fredrik Lundhb9056332001-07-11 17:42:21 +0000490 def dump_array(self, value):
Fredrik Lundh1538c232001-10-01 19:42:03 +0000491 self.opencontainer(value)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000492 write = self.write
Fredrik Lundh1538c232001-10-01 19:42:03 +0000493 dump = self.__dump
Fredrik Lundhb9056332001-07-11 17:42:21 +0000494 write("<value><array><data>\n")
495 for v in value:
Fredrik Lundh1538c232001-10-01 19:42:03 +0000496 dump(v)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000497 write("</data></array></value>\n")
Fredrik Lundh1538c232001-10-01 19:42:03 +0000498 self.closecontainer(value)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000499 dispatch[TupleType] = dump_array
500 dispatch[ListType] = dump_array
501
Fredrik Lundh1538c232001-10-01 19:42:03 +0000502 def dump_struct(self, value, escape=escape):
503 self.opencontainer(value)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000504 write = self.write
Fredrik Lundh1538c232001-10-01 19:42:03 +0000505 dump = self.__dump
Fredrik Lundhb9056332001-07-11 17:42:21 +0000506 write("<value><struct>\n")
507 for k, v in value.items():
508 write("<member>\n")
509 if type(k) is not StringType:
510 raise TypeError, "dictionary key must be string"
Fredrik Lundh1538c232001-10-01 19:42:03 +0000511 write("<name>%s</name>\n" % escape(k))
512 dump(v)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000513 write("</member>\n")
514 write("</struct></value>\n")
Fredrik Lundh1538c232001-10-01 19:42:03 +0000515 self.closecontainer(value)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000516 dispatch[DictType] = dump_struct
517
518 def dump_instance(self, value):
519 # check for special wrappers
520 if value.__class__ in WRAPPERS:
521 value.encode(self)
522 else:
523 # store instance attributes as a struct (really?)
524 self.dump_struct(value.__dict__)
525 dispatch[InstanceType] = dump_instance
526
527class Unmarshaller:
Fred Drake1b410792001-09-04 18:55:03 +0000528 """Unmarshal an XML-RPC response, based on incoming XML event
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000529 messages (start, data, end). Call close() to get the resulting
Fred Drake1b410792001-09-04 18:55:03 +0000530 data structure.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000531
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000532 Note that this reader is fairly tolerant, and gladly accepts bogus
533 XML-RPC data without complaining (but not bogus XML).
Fred Drake1b410792001-09-04 18:55:03 +0000534 """
Fredrik Lundhb9056332001-07-11 17:42:21 +0000535
536 # and again, if you don't understand what's going on in here,
537 # that's perfectly ok.
538
539 def __init__(self):
540 self._type = None
541 self._stack = []
542 self._marks = []
543 self._data = []
544 self._methodname = None
545 self._encoding = "utf-8"
546 self.append = self._stack.append
547
548 def close(self):
549 # return response tuple and target method
550 if self._type is None or self._marks:
551 raise ResponseError()
552 if self._type == "fault":
553 raise apply(Fault, (), self._stack[0])
554 return tuple(self._stack)
555
556 def getmethodname(self):
557 return self._methodname
558
559 #
560 # event handlers
561
562 def xml(self, encoding, standalone):
563 self._encoding = encoding
564 # FIXME: assert standalone == 1 ???
565
566 def start(self, tag, attrs):
567 # prepare to handle this element
568 if tag == "array" or tag == "struct":
569 self._marks.append(len(self._stack))
570 self._data = []
571 self._value = (tag == "value")
572
573 def data(self, text):
574 self._data.append(text)
575
Fredrik Lundh1538c232001-10-01 19:42:03 +0000576 def end(self, tag, join=string.join):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000577 # call the appropriate end tag handler
578 try:
579 f = self.dispatch[tag]
580 except KeyError:
581 pass # unknown tag ?
582 else:
Fredrik Lundh1538c232001-10-01 19:42:03 +0000583 return f(self, join(self._data, ""))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000584
585 #
586 # accelerator support
587
588 def end_dispatch(self, tag, data):
589 # dispatch data
590 try:
591 f = self.dispatch[tag]
592 except KeyError:
593 pass # unknown tag ?
594 else:
595 return f(self, data)
596
597 #
598 # element decoders
599
600 dispatch = {}
601
Fredrik Lundh1538c232001-10-01 19:42:03 +0000602 def end_boolean(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000603 if data == "0":
604 self.append(False)
605 elif data == "1":
606 self.append(True)
607 else:
608 raise TypeError, "bad boolean value"
609 self._value = 0
610 dispatch["boolean"] = end_boolean
611
Fredrik Lundh1538c232001-10-01 19:42:03 +0000612 def end_int(self, data):
613 self.append(int(data))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000614 self._value = 0
615 dispatch["i4"] = end_int
616 dispatch["int"] = end_int
617
Fredrik Lundh1538c232001-10-01 19:42:03 +0000618 def end_double(self, data):
619 self.append(float(data))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000620 self._value = 0
621 dispatch["double"] = end_double
622
Fredrik Lundh1538c232001-10-01 19:42:03 +0000623 def end_string(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000624 if self._encoding:
625 data = _decode(data, self._encoding)
626 self.append(_stringify(data))
627 self._value = 0
628 dispatch["string"] = end_string
629 dispatch["name"] = end_string # struct keys are always strings
630
631 def end_array(self, data):
632 mark = self._marks[-1]
633 del self._marks[-1]
634 # map arrays to Python lists
635 self._stack[mark:] = [self._stack[mark:]]
636 self._value = 0
637 dispatch["array"] = end_array
638
639 def end_struct(self, data):
640 mark = self._marks[-1]
641 del self._marks[-1]
642 # map structs to Python dictionaries
643 dict = {}
644 items = self._stack[mark:]
645 for i in range(0, len(items), 2):
646 dict[_stringify(items[i])] = items[i+1]
647 self._stack[mark:] = [dict]
648 self._value = 0
649 dispatch["struct"] = end_struct
650
Fredrik Lundh1538c232001-10-01 19:42:03 +0000651 def end_base64(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000652 value = Binary()
Fredrik Lundh1538c232001-10-01 19:42:03 +0000653 value.decode(data)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000654 self.append(value)
655 self._value = 0
656 dispatch["base64"] = end_base64
657
Fredrik Lundh1538c232001-10-01 19:42:03 +0000658 def end_dateTime(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000659 value = DateTime()
Fredrik Lundh1538c232001-10-01 19:42:03 +0000660 value.decode(data)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000661 self.append(value)
662 dispatch["dateTime.iso8601"] = end_dateTime
663
664 def end_value(self, data):
665 # if we stumble upon an value element with no internal
666 # elements, treat it as a string element
667 if self._value:
668 self.end_string(data)
669 dispatch["value"] = end_value
670
671 def end_params(self, data):
672 self._type = "params"
673 dispatch["params"] = end_params
674
675 def end_fault(self, data):
676 self._type = "fault"
677 dispatch["fault"] = end_fault
678
Fredrik Lundh1538c232001-10-01 19:42:03 +0000679 def end_methodName(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000680 if self._encoding:
681 data = _decode(data, self._encoding)
682 self._methodname = data
683 self._type = "methodName" # no params
684 dispatch["methodName"] = end_methodName
685
686
687# --------------------------------------------------------------------
688# convenience functions
689
690def getparser():
691 """getparser() -> parser, unmarshaller
692
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000693 Create an instance of the fastest available parser, and attach it
694 to an unmarshalling object. Return both objects.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000695 """
696 if FastParser and FastUnmarshaller:
697 target = FastUnmarshaller(True, False, binary, datetime)
698 parser = FastParser(target)
699 else:
700 target = Unmarshaller()
701 if FastParser:
702 parser = FastParser(target)
703 elif SgmlopParser:
704 parser = SgmlopParser(target)
705 elif ExpatParser:
706 parser = ExpatParser(target)
707 else:
708 parser = SlowParser(target)
709 return parser, target
710
711def dumps(params, methodname=None, methodresponse=None, encoding=None):
712 """data [,options] -> marshalled data
713
714 Convert an argument tuple or a Fault instance to an XML-RPC
715 request (or response, if the methodresponse option is used).
716
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000717 In addition to the data object, the following options can be given
718 as keyword arguments:
Fredrik Lundhb9056332001-07-11 17:42:21 +0000719
720 methodname: the method name for a methodCall packet
721
722 methodresponse: true to create a methodResponse packet.
723 If this option is used with a tuple, the tuple must be
724 a singleton (i.e. it can contain only one element).
725
726 encoding: the packet encoding (default is UTF-8)
727
728 All 8-bit strings in the data structure are assumed to use the
729 packet encoding. Unicode strings are automatically converted,
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000730 where necessary.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000731 """
732
733 assert isinstance(params, TupleType) or isinstance(params, Fault),\
734 "argument must be tuple or Fault instance"
735
736 if isinstance(params, Fault):
737 methodresponse = 1
738 elif methodresponse and isinstance(params, TupleType):
739 assert len(params) == 1, "response tuple must be a singleton"
740
741 if not encoding:
742 encoding = "utf-8"
743
744 m = Marshaller(encoding)
745 data = m.dumps(params)
746
747 if encoding != "utf-8":
748 xmlheader = "<?xml version='1.0' encoding=%s?>\n" % repr(encoding)
749 else:
750 xmlheader = "<?xml version='1.0'?>\n" # utf-8 is default
751
752 # standard XML-RPC wrappings
753 if methodname:
754 # a method call
755 if not isinstance(methodname, StringType):
756 methodname = methodname.encode(encoding)
757 data = (
758 xmlheader,
759 "<methodCall>\n"
760 "<methodName>", methodname, "</methodName>\n",
761 data,
762 "</methodCall>\n"
763 )
764 elif methodresponse:
765 # a method response, or a fault structure
766 data = (
767 xmlheader,
768 "<methodResponse>\n",
769 data,
770 "</methodResponse>\n"
771 )
772 else:
773 return data # return as is
774 return string.join(data, "")
775
776def loads(data):
777 """data -> unmarshalled data, method name
778
779 Convert an XML-RPC packet to unmarshalled data plus a method
780 name (None if not present).
781
782 If the XML-RPC packet represents a fault condition, this function
783 raises a Fault exception.
784 """
785 p, u = getparser()
786 p.feed(data)
787 p.close()
788 return u.close(), u.getmethodname()
789
790
791# --------------------------------------------------------------------
792# request dispatcher
793
794class _Method:
795 # some magic to bind an XML-RPC method to an RPC server.
796 # supports "nested" methods (e.g. examples.getStateName)
797 def __init__(self, send, name):
798 self.__send = send
799 self.__name = name
800 def __getattr__(self, name):
801 return _Method(self.__send, "%s.%s" % (self.__name, name))
802 def __call__(self, *args):
803 return self.__send(self.__name, args)
804
805
806class Transport:
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000807 """Handles an HTTP transaction to an XML-RPC server."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000808
809 # client identifier (may be overridden)
810 user_agent = "xmlrpclib.py/%s (by www.pythonware.com)" % __version__
811
812 def request(self, host, handler, request_body, verbose=0):
813 # issue XML-RPC request
814
815 h = self.make_connection(host)
816 if verbose:
817 h.set_debuglevel(1)
818
819 self.send_request(h, handler, request_body)
820 self.send_host(h, host)
821 self.send_user_agent(h)
822 self.send_content(h, request_body)
823
824 errcode, errmsg, headers = h.getreply()
825
826 if errcode != 200:
827 raise ProtocolError(
828 host + handler,
829 errcode, errmsg,
830 headers
831 )
832
833 self.verbose = verbose
834
835 return self.parse_response(h.getfile())
836
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000837 def getparser(self):
838 # get parser and unmarshaller
839 return getparser()
840
Fredrik Lundhb9056332001-07-11 17:42:21 +0000841 def make_connection(self, host):
842 # create a HTTP connection object from a host descriptor
843 import httplib
844 return httplib.HTTP(host)
845
846 def send_request(self, connection, handler, request_body):
847 connection.putrequest("POST", handler)
848
849 def send_host(self, connection, host):
850 connection.putheader("Host", host)
851
852 def send_user_agent(self, connection):
853 connection.putheader("User-Agent", self.user_agent)
854
855 def send_content(self, connection, request_body):
856 connection.putheader("Content-Type", "text/xml")
857 connection.putheader("Content-Length", str(len(request_body)))
858 connection.endheaders()
859 if request_body:
860 connection.send(request_body)
861
862 def parse_response(self, f):
863 # read response from input file, and parse it
864
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000865 p, u = self.getparser()
Fredrik Lundhb9056332001-07-11 17:42:21 +0000866
867 while 1:
868 response = f.read(1024)
869 if not response:
870 break
871 if self.verbose:
872 print "body:", repr(response)
873 p.feed(response)
874
875 f.close()
876 p.close()
877
878 return u.close()
879
880class SafeTransport(Transport):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000881 """Handles an HTTPS transaction to an XML-RPC server."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000882
883 def make_connection(self, host):
884 # create a HTTPS connection object from a host descriptor
885 # host may be a string, or a (host, x509-dict) tuple
886 import httplib
887 if isinstance(host, TupleType):
888 host, x509 = host
889 else:
890 x509 = {}
891 try:
892 HTTPS = httplib.HTTPS
893 except AttributeError:
894 raise NotImplementedError,\
895 "your version of httplib doesn't support HTTPS"
896 else:
897 return apply(HTTPS, (host, None), x509)
898
899 def send_host(self, connection, host):
900 if isinstance(host, TupleType):
901 host, x509 = host
902 connection.putheader("Host", host)
903
904class ServerProxy:
905 """uri [,options] -> a logical connection to an XML-RPC server
906
907 uri is the connection point on the server, given as
908 scheme://host/target.
909
910 The standard implementation always supports the "http" scheme. If
911 SSL socket support is available (Python 2.0), it also supports
912 "https".
913
914 If the target part and the slash preceding it are both omitted,
915 "/RPC2" is assumed.
916
917 The following options can be given as keyword arguments:
918
919 transport: a transport factory
920 encoding: the request encoding (default is UTF-8)
921
922 All 8-bit strings passed to the server proxy are assumed to use
923 the given encoding.
924 """
925
926 def __init__(self, uri, transport=None, encoding=None, verbose=0):
927 # establish a "logical" server connection
928
929 # get the url
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000930 import urllib
Fredrik Lundhb9056332001-07-11 17:42:21 +0000931 type, uri = urllib.splittype(uri)
932 if type not in ("http", "https"):
933 raise IOError, "unsupported XML-RPC protocol"
934 self.__host, self.__handler = urllib.splithost(uri)
935 if not self.__handler:
936 self.__handler = "/RPC2"
937
938 if transport is None:
939 if type == "https":
940 transport = SafeTransport()
941 else:
942 transport = Transport()
943 self.__transport = transport
944
945 self.__encoding = encoding
946 self.__verbose = verbose
947
948 def __request(self, methodname, params):
949 # call a method on the remote server
950
951 request = dumps(params, methodname, encoding=self.__encoding)
952
953 response = self.__transport.request(
954 self.__host,
955 self.__handler,
956 request,
957 verbose=self.__verbose
958 )
959
960 if len(response) == 1:
961 response = response[0]
962
963 return response
964
965 def __repr__(self):
966 return (
Fredrik Lundh78eedce2001-08-23 20:04:33 +0000967 "<ServerProxy for %s%s>" %
Fredrik Lundhb9056332001-07-11 17:42:21 +0000968 (self.__host, self.__handler)
969 )
970
971 __str__ = __repr__
972
973 def __getattr__(self, name):
974 # magic method dispatcher
975 return _Method(self.__request, name)
976
977 # note: to call a remote object with an non-standard name, use
978 # result getattr(server, "strange-python-name")(args)
979
Fredrik Lundh78eedce2001-08-23 20:04:33 +0000980# compatibility
Fredrik Lundhb9056332001-07-11 17:42:21 +0000981Server = ServerProxy
982
983# --------------------------------------------------------------------
984# test code
985
986if __name__ == "__main__":
987
988 # simple test program (from the XML-RPC specification)
989
Fredrik Lundh78eedce2001-08-23 20:04:33 +0000990 # server = ServerProxy("http://localhost:8000") # local server
991 server = ServerProxy("http://betty.userland.com")
Fredrik Lundhb9056332001-07-11 17:42:21 +0000992
993 print server
994
995 try:
996 print server.examples.getStateName(41)
997 except Error, v:
998 print "ERROR", v