blob: 4b6fc43cff4c5cf1a6a81b461b20864c699e1700 [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 Lundh3d9addd2002-06-27 21:36:21 +000036# 2001-10-02 fl More dumps microtuning
37# 2001-10-04 fl Make sure import expat gets a parser (from Guido van Rossum)
Skip Montanaro5e9c71b2001-10-10 15:56:34 +000038# 2001-10-10 sm Allow long ints to be passed as ints if they don't overflow
Fredrik Lundh3d9addd2002-06-27 21:36:21 +000039# 2001-10-17 sm Test for int and long overflow (allows use on 64-bit systems)
Fredrik Lundhb6ab93f2001-12-19 21:40:04 +000040# 2001-11-12 fl Use repr() to marshal doubles (from Paul Felix)
Fredrik Lundh3d9addd2002-06-27 21:36:21 +000041# 2002-03-17 fl Avoid buffered read when possible (from James Rucker)
42# 2002-04-07 fl Added pythondoc comments
43# 2002-04-16 fl Added __str__ methods to datetime/binary wrappers
44# 2002-05-15 fl Added error constants (from Andrew Kuchling)
45# 2002-06-27 fl Merged with Python CVS version
Fredrik Lundhb9056332001-07-11 17:42:21 +000046#
Fredrik Lundh3d9addd2002-06-27 21:36:21 +000047# Copyright (c) 1999-2002 by Secret Labs AB.
48# Copyright (c) 1999-2002 by Fredrik Lundh.
Fredrik Lundhb9056332001-07-11 17:42:21 +000049#
50# info@pythonware.com
51# http://www.pythonware.com
52#
53# --------------------------------------------------------------------
54# The XML-RPC client interface is
55#
Fredrik Lundh3d9addd2002-06-27 21:36:21 +000056# Copyright (c) 1999-2002 by Secret Labs AB
57# Copyright (c) 1999-2002 by Fredrik Lundh
Fredrik Lundhb9056332001-07-11 17:42:21 +000058#
59# By obtaining, using, and/or copying this software and/or its
60# associated documentation, you agree that you have read, understood,
61# and will comply with the following terms and conditions:
62#
63# Permission to use, copy, modify, and distribute this software and
64# its associated documentation for any purpose and without fee is
65# hereby granted, provided that the above copyright notice appears in
66# all copies, and that both that copyright notice and this permission
67# notice appear in supporting documentation, and that the name of
68# Secret Labs AB or the author not be used in advertising or publicity
69# pertaining to distribution of the software without specific, written
70# prior permission.
71#
72# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
73# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
74# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR
75# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
76# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
77# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
78# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
79# OF THIS SOFTWARE.
80# --------------------------------------------------------------------
81
82#
Fredrik Lundh3d9addd2002-06-27 21:36:21 +000083# things to look into some day:
Fredrik Lundhb9056332001-07-11 17:42:21 +000084
Fredrik Lundh3d9addd2002-06-27 21:36:21 +000085# TODO: sort out True/False/boolean issues for Python 2.3
Fredrik Lundhb9056332001-07-11 17:42:21 +000086
Fred Drake1b410792001-09-04 18:55:03 +000087"""
88An XML-RPC client interface for Python.
89
90The marshalling and response parser code can also be used to
91implement XML-RPC servers.
92
Fred Drake1b410792001-09-04 18:55:03 +000093Exported exceptions:
94
Fredrik Lundhc4c062f2001-09-10 19:45:02 +000095 Error Base class for client errors
96 ProtocolError Indicates an HTTP protocol error
97 ResponseError Indicates a broken response package
98 Fault Indicates an XML-RPC fault package
Fred Drake1b410792001-09-04 18:55:03 +000099
100Exported classes:
101
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000102 ServerProxy Represents a logical connection to an XML-RPC server
Fred Drake1b410792001-09-04 18:55:03 +0000103
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000104 Boolean boolean wrapper to generate a "boolean" XML-RPC value
105 DateTime dateTime wrapper for an ISO 8601 string or time tuple or
106 localtime integer value to generate a "dateTime.iso8601"
107 XML-RPC value
108 Binary binary data wrapper
Fred Drake1b410792001-09-04 18:55:03 +0000109
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000110 SlowParser Slow but safe standard parser (based on xmllib)
111 Marshaller Generate an XML-RPC params chunk from a Python data structure
112 Unmarshaller Unmarshal an XML-RPC response from incoming XML event message
113 Transport Handles an HTTP transaction to an XML-RPC server
114 SafeTransport Handles an HTTPS transaction to an XML-RPC server
Fred Drake1b410792001-09-04 18:55:03 +0000115
116Exported constants:
117
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000118 True
119 False
Fred Drake1b410792001-09-04 18:55:03 +0000120
121Exported functions:
122
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000123 boolean Convert any Python value to an XML-RPC boolean
124 getparser Create instance of the fastest available parser & attach
125 to an unmarshalling object
126 dumps Convert an argument tuple or a Fault instance to an XML-RPC
127 request (or response, if the methodresponse option is used).
128 loads Convert an XML-RPC packet to unmarshalled data plus a method
129 name (None if not present).
Fred Drake1b410792001-09-04 18:55:03 +0000130"""
131
Fred Drake2a2d9702001-10-17 01:51:04 +0000132import re, string, time, operator
Fredrik Lundh1538c232001-10-01 19:42:03 +0000133
Fredrik Lundhb9056332001-07-11 17:42:21 +0000134from types import *
Fredrik Lundhb9056332001-07-11 17:42:21 +0000135
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000136# --------------------------------------------------------------------
137# Internal stuff
138
Fredrik Lundhb9056332001-07-11 17:42:21 +0000139try:
140 unicode
141except NameError:
142 unicode = None # unicode support not available
143
144def _decode(data, encoding, is8bit=re.compile("[\x80-\xff]").search):
145 # decode non-ascii string (if possible)
146 if unicode and encoding and is8bit(data):
147 data = unicode(data, encoding)
148 return data
149
Fredrik Lundh1538c232001-10-01 19:42:03 +0000150def escape(s, replace=string.replace):
151 s = replace(s, "&", "&")
152 s = replace(s, "<", "&lt;")
153 return replace(s, ">", "&gt;",)
154
Fredrik Lundhb9056332001-07-11 17:42:21 +0000155if unicode:
156 def _stringify(string):
157 # convert to 7-bit ascii if possible
158 try:
159 return str(string)
160 except UnicodeError:
161 return string
162else:
163 def _stringify(string):
164 return string
165
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000166__version__ = "1.0.1"
167
168# xmlrpc integer limits
169MAXINT = 2L**31-1
170MININT = -2L**31
171
172# --------------------------------------------------------------------
173# Error constants (from Dan Libby's specification at
174# http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php)
175
176# Ranges of errors
177PARSE_ERROR = -32700
178SERVER_ERROR = -32600
179APPLICATION_ERROR = -32500
180SYSTEM_ERROR = -32400
181TRANSPORT_ERROR = -32300
182
183# Specific errors
184NOT_WELLFORMED_ERROR = -32700
185UNSUPPORTED_ENCODING = -32701
186INVALID_ENCODING_CHAR = -32702
187INVALID_XMLRPC = -32600
188METHOD_NOT_FOUND = -32601
189INVALID_METHOD_PARAMS = -32602
190INTERNAL_ERROR = -32603
Fredrik Lundhb9056332001-07-11 17:42:21 +0000191
192# --------------------------------------------------------------------
193# Exceptions
194
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000195##
196# Base class for all kinds of client-side errors.
197
Fredrik Lundh78eedce2001-08-23 20:04:33 +0000198class Error(Exception):
Fred Drake1b410792001-09-04 18:55:03 +0000199 """Base class for client errors."""
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000200 def __str__(self):
201 return repr(self)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000202
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000203##
204# Indicates an HTTP-level protocol error. This is raised by the HTTP
205# transport layer, if the server returns an error code other than 200
206# (OK).
207#
208# @param url The target URL.
209# @param errcode The HTTP error code.
210# @param errmsg The HTTP error message.
211# @param headers The HTTP header dictionary.
212
Fredrik Lundhb9056332001-07-11 17:42:21 +0000213class ProtocolError(Error):
Fred Drake1b410792001-09-04 18:55:03 +0000214 """Indicates an HTTP protocol error."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000215 def __init__(self, url, errcode, errmsg, headers):
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000216 Error.__init__(self)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000217 self.url = url
218 self.errcode = errcode
219 self.errmsg = errmsg
220 self.headers = headers
221 def __repr__(self):
222 return (
223 "<ProtocolError for %s: %s %s>" %
224 (self.url, self.errcode, self.errmsg)
225 )
226
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000227##
228# Indicates a broken XML-RPC response package. This exception is
229# raised by the unmarshalling layer, if the XML-RPC response is
230# malformed.
231
Fredrik Lundhb9056332001-07-11 17:42:21 +0000232class ResponseError(Error):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000233 """Indicates a broken response package."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000234 pass
235
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000236##
237# Indicates an XML-RPC fault response package. This exception is
238# raised by the unmarshalling layer, if the XML-RPC response contains
239# a fault string. This exception can also used as a class, to
240# generate a fault XML-RPC message.
241#
242# @param faultCode The XML-RPC fault code.
243# @param faultString The XML-RPC fault string.
244
Fredrik Lundhb9056332001-07-11 17:42:21 +0000245class Fault(Error):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000246 """Indicates an XML-RPC fault package."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000247 def __init__(self, faultCode, faultString, **extra):
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000248 Error.__init__(self)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000249 self.faultCode = faultCode
250 self.faultString = faultString
251 def __repr__(self):
252 return (
253 "<Fault %s: %s>" %
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000254 (self.faultCode, repr(self.faultString))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000255 )
256
257# --------------------------------------------------------------------
258# Special values
259
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000260##
261# Wrapper for XML-RPC boolean values. Use the xmlrpclib.True and
262# xmlrpclib.False constants, or the xmlrpclib.boolean() function, to
263# generate boolean XML-RPC values.
264#
265# @param value A boolean value. Any true value is interpreted as True,
266# all other values are interpreted as False.
267
Fredrik Lundhb9056332001-07-11 17:42:21 +0000268class Boolean:
Fred Drake1b410792001-09-04 18:55:03 +0000269 """Boolean-value wrapper.
270
271 Use True or False to generate a "boolean" XML-RPC value.
272 """
Fredrik Lundhb9056332001-07-11 17:42:21 +0000273
274 def __init__(self, value = 0):
275 self.value = operator.truth(value)
276
277 def encode(self, out):
278 out.write("<value><boolean>%d</boolean></value>\n" % self.value)
279
280 def __cmp__(self, other):
281 if isinstance(other, Boolean):
282 other = other.value
283 return cmp(self.value, other)
284
285 def __repr__(self):
286 if self.value:
287 return "<Boolean True at %x>" % id(self)
288 else:
289 return "<Boolean False at %x>" % id(self)
290
291 def __int__(self):
292 return self.value
293
294 def __nonzero__(self):
295 return self.value
296
297True, False = Boolean(1), Boolean(0)
298
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000299##
300# Map true or false value to XML-RPC boolean values.
301#
302# @def boolean(value)
303# @param value A boolean value. Any true value is mapped to True,
304# all other values are mapped to False.
305# @return xmlrpclib.True or xmlrpclib.False.
306# @see Boolean
307# @see True
308# @see False
309
310def boolean(value, _truefalse=(False, True)):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000311 """Convert any Python value to XML-RPC 'boolean'."""
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000312 return _truefalse[operator.truth(value)]
313
314##
315# Wrapper for XML-RPC DateTime values. This converts a time value to
316# the format used by XML-RPC.
317# <p>
318# The value can be given as a string in the format
319# "yyyymmddThh:mm:ss", as a 9-item time tuple (as returned by
320# time.localtime()), or an integer value (as returned by time.time()).
321# The wrapper uses time.localtime() to convert an integer to a time
322# tuple.
323#
324# @param value The time, given as an ISO 8601 string, a time
325# tuple, or a integer time value.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000326
Fredrik Lundhb9056332001-07-11 17:42:21 +0000327class DateTime:
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000328 """DateTime wrapper for an ISO 8601 string or time tuple or
329 localtime integer value to generate 'dateTime.iso8601' XML-RPC
330 value.
331 """
Fredrik Lundhb9056332001-07-11 17:42:21 +0000332
333 def __init__(self, value=0):
Fredrik Lundh78eedce2001-08-23 20:04:33 +0000334 if not isinstance(value, StringType):
335 if not isinstance(value, TupleType):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000336 if value == 0:
337 value = time.time()
338 value = time.localtime(value)
339 value = time.strftime("%Y%m%dT%H:%M:%S", value)
340 self.value = value
341
342 def __cmp__(self, other):
343 if isinstance(other, DateTime):
344 other = other.value
345 return cmp(self.value, other)
346
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000347 ##
348 # Get date/time value.
349 #
350 # @return Date/time value, as an ISO 8601 string.
351
352 def __str__(self):
353 return self.value
354
Fredrik Lundhb9056332001-07-11 17:42:21 +0000355 def __repr__(self):
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000356 return "<DateTime %s at %x>" % (repr(self.value), id(self))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000357
358 def decode(self, data):
359 self.value = string.strip(data)
360
361 def encode(self, out):
362 out.write("<value><dateTime.iso8601>")
363 out.write(self.value)
364 out.write("</dateTime.iso8601></value>\n")
365
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000366def _datetime(data):
367 # decode xml element contents into a DateTime structure.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000368 value = DateTime()
369 value.decode(data)
370 return value
371
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000372##
373# Wrapper for binary data. This can be used to transport any kind
374# of binary data over XML-RPC, using BASE64 encoding.
375#
376# @param data An 8-bit string containing arbitrary data.
377
Fredrik Lundhb9056332001-07-11 17:42:21 +0000378class Binary:
Fred Drake1b410792001-09-04 18:55:03 +0000379 """Wrapper for binary data."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000380
381 def __init__(self, data=None):
382 self.data = data
383
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000384 ##
385 # Get buffer contents.
386 #
387 # @return Buffer contents, as an 8-bit string.
388
389 def __str__(self):
390 return self.data or ""
391
Fredrik Lundhb9056332001-07-11 17:42:21 +0000392 def __cmp__(self, other):
393 if isinstance(other, Binary):
394 other = other.data
395 return cmp(self.data, other)
396
397 def decode(self, data):
398 import base64
399 self.data = base64.decodestring(data)
400
401 def encode(self, out):
402 import base64, StringIO
403 out.write("<value><base64>\n")
404 base64.encode(StringIO.StringIO(self.data), out)
405 out.write("</base64></value>\n")
406
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000407def _binary(data):
408 # decode xml element contents into a Binary structure
Fredrik Lundhb9056332001-07-11 17:42:21 +0000409 value = Binary()
410 value.decode(data)
411 return value
412
413WRAPPERS = DateTime, Binary, Boolean
414
415# --------------------------------------------------------------------
416# XML parsers
417
418try:
419 # optional xmlrpclib accelerator. for more information on this
420 # component, contact info@pythonware.com
421 import _xmlrpclib
422 FastParser = _xmlrpclib.Parser
423 FastUnmarshaller = _xmlrpclib.Unmarshaller
424except (AttributeError, ImportError):
425 FastParser = FastUnmarshaller = None
426
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000427try:
428 import _xmlrpclib
429 FastMarshaller = _xmlrpclib.Marshaller
430except (AttributeError, ImportError):
431 FastMarshaller = None
432
Fredrik Lundhb9056332001-07-11 17:42:21 +0000433#
434# the SGMLOP parser is about 15x faster than Python's builtin
435# XML parser. SGMLOP sources can be downloaded from:
436#
437# http://www.pythonware.com/products/xml/sgmlop.htm
438#
439
440try:
441 import sgmlop
442 if not hasattr(sgmlop, "XMLParser"):
443 raise ImportError
444except ImportError:
445 SgmlopParser = None # sgmlop accelerator not available
446else:
447 class SgmlopParser:
448 def __init__(self, target):
449
450 # setup callbacks
451 self.finish_starttag = target.start
452 self.finish_endtag = target.end
453 self.handle_data = target.data
454 self.handle_xml = target.xml
455
456 # activate parser
457 self.parser = sgmlop.XMLParser()
458 self.parser.register(self)
459 self.feed = self.parser.feed
460 self.entity = {
461 "amp": "&", "gt": ">", "lt": "<",
462 "apos": "'", "quot": '"'
463 }
464
465 def close(self):
466 try:
467 self.parser.close()
468 finally:
469 self.parser = self.feed = None # nuke circular reference
470
471 def handle_proc(self, tag, attr):
472 m = re.search("encoding\s*=\s*['\"]([^\"']+)[\"']", attr)
473 if m:
474 self.handle_xml(m.group(1), 1)
475
476 def handle_entityref(self, entity):
477 # <string> entity
478 try:
479 self.handle_data(self.entity[entity])
480 except KeyError:
481 self.handle_data("&%s;" % entity)
482
483try:
484 from xml.parsers import expat
Guido van Rossumb8551342001-10-02 18:33:11 +0000485 if not hasattr(expat, "ParserCreate"):
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000486 raise ImportError
Fredrik Lundhb9056332001-07-11 17:42:21 +0000487except ImportError:
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000488 ExpatParser = None # expat not available
Fredrik Lundhb9056332001-07-11 17:42:21 +0000489else:
490 class ExpatParser:
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000491 # fast expat parser for Python 2.0 and later. this is about
492 # 50% slower than sgmlop, on roundtrip testing
Fredrik Lundhb9056332001-07-11 17:42:21 +0000493 def __init__(self, target):
494 self._parser = parser = expat.ParserCreate(None, None)
495 self._target = target
496 parser.StartElementHandler = target.start
497 parser.EndElementHandler = target.end
498 parser.CharacterDataHandler = target.data
499 encoding = None
500 if not parser.returns_unicode:
501 encoding = "utf-8"
502 target.xml(encoding, None)
503
504 def feed(self, data):
505 self._parser.Parse(data, 0)
506
507 def close(self):
508 self._parser.Parse("", 1) # end of data
509 del self._target, self._parser # get rid of circular references
510
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000511class SlowParser:
512 """Default XML parser (based on xmllib.XMLParser)."""
513 # this is about 10 times slower than sgmlop, on roundtrip
514 # testing.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000515 def __init__(self, target):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000516 import xmllib # lazy subclassing (!)
517 if xmllib.XMLParser not in SlowParser.__bases__:
518 SlowParser.__bases__ = (xmllib.XMLParser,)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000519 self.handle_xml = target.xml
520 self.unknown_starttag = target.start
521 self.handle_data = target.data
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000522 self.handle_cdata = target.data
Fredrik Lundhb9056332001-07-11 17:42:21 +0000523 self.unknown_endtag = target.end
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000524 try:
525 xmllib.XMLParser.__init__(self, accept_utf8=1)
526 except TypeError:
527 xmllib.XMLParser.__init__(self) # pre-2.0
Fredrik Lundhb9056332001-07-11 17:42:21 +0000528
529# --------------------------------------------------------------------
530# XML-RPC marshalling and unmarshalling code
531
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000532##
533# XML-RPC marshaller.
534#
535# @param encoding Default encoding for 8-bit strings. The default
536# value is None (interpreted as UTF-8).
537# @see dumps
538
Fredrik Lundhb9056332001-07-11 17:42:21 +0000539class Marshaller:
Fred Drake1b410792001-09-04 18:55:03 +0000540 """Generate an XML-RPC params chunk from a Python data structure.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000541
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000542 Create a Marshaller instance for each set of parameters, and use
543 the "dumps" method to convert your data (represented as a tuple)
544 to an XML-RPC params chunk. To write a fault response, pass a
545 Fault instance instead. You may prefer to use the "dumps" module
546 function for this purpose.
Fred Drake1b410792001-09-04 18:55:03 +0000547 """
Fredrik Lundhb9056332001-07-11 17:42:21 +0000548
549 # by the way, if you don't understand what's going on in here,
550 # that's perfectly ok.
551
552 def __init__(self, encoding=None):
553 self.memo = {}
554 self.data = None
555 self.encoding = encoding
556
557 dispatch = {}
558
559 def dumps(self, values):
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000560 out = []
561 write = out.append
562 dump = self.__dump
Fredrik Lundhb9056332001-07-11 17:42:21 +0000563 if isinstance(values, Fault):
564 # fault instance
565 write("<fault>\n")
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000566 dump(vars(values), write)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000567 write("</fault>\n")
568 else:
569 # parameter block
Fredrik Lundhc266bb02001-08-23 20:13:08 +0000570 # FIXME: the xml-rpc specification allows us to leave out
571 # the entire <params> block if there are no parameters.
572 # however, changing this may break older code (including
573 # old versions of xmlrpclib.py), so this is better left as
574 # is for now. See @XMLRPC3 for more information. /F
Fredrik Lundhb9056332001-07-11 17:42:21 +0000575 write("<params>\n")
576 for v in values:
577 write("<param>\n")
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000578 dump(v, write)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000579 write("</param>\n")
580 write("</params>\n")
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000581 result = string.join(out, "")
Fredrik Lundhb9056332001-07-11 17:42:21 +0000582 return result
583
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000584 def __dump(self, value, write):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000585 try:
586 f = self.dispatch[type(value)]
587 except KeyError:
588 raise TypeError, "cannot marshal %s objects" % type(value)
589 else:
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000590 f(self, value, write)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000591
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000592 def dump_int(self, value, write):
Skip Montanaro5449e082001-10-17 22:53:33 +0000593 # in case ints are > 32 bits
594 if value > MAXINT or value < MININT:
595 raise OverflowError, "int exceeds XML-RPC limits"
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000596 write("<value><int>")
597 write(str(value))
598 write("</int></value>\n")
Fredrik Lundhb9056332001-07-11 17:42:21 +0000599 dispatch[IntType] = dump_int
600
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000601 def dump_long(self, value, write):
Skip Montanaro5449e082001-10-17 22:53:33 +0000602 if value > MAXINT or value < MININT:
603 raise OverflowError, "long int exceeds XML-RPC limits"
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000604 write("<value><int>")
605 write(str(int(value)))
606 write("</int></value>\n")
Skip Montanaro5e9c71b2001-10-10 15:56:34 +0000607 dispatch[LongType] = dump_long
608
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000609 def dump_double(self, value, write):
610 write("<value><double>")
611 write(repr(value))
612 write("</double></value>\n")
Fredrik Lundhb9056332001-07-11 17:42:21 +0000613 dispatch[FloatType] = dump_double
614
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000615 def dump_string(self, value, write, escape=escape):
616 write("<value><string>")
617 write(escape(value))
618 write("</string></value>\n")
Fredrik Lundhb9056332001-07-11 17:42:21 +0000619 dispatch[StringType] = dump_string
620
621 if unicode:
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000622 def dump_unicode(self, value, write, escape=escape):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000623 value = value.encode(self.encoding)
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000624 write("<value><string>")
625 write(escape(value))
626 write("</string></value>\n")
Fredrik Lundhb9056332001-07-11 17:42:21 +0000627 dispatch[UnicodeType] = dump_unicode
628
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000629 def dump_array(self, value, write):
630 i = id(value)
631 if self.memo.has_key(i):
632 raise TypeError, "cannot marshal recursive sequences"
633 self.memo[i] = None
Fredrik Lundh1538c232001-10-01 19:42:03 +0000634 dump = self.__dump
Fredrik Lundhb9056332001-07-11 17:42:21 +0000635 write("<value><array><data>\n")
636 for v in value:
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000637 dump(v, write)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000638 write("</data></array></value>\n")
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000639 del self.memo[i]
Fredrik Lundhb9056332001-07-11 17:42:21 +0000640 dispatch[TupleType] = dump_array
641 dispatch[ListType] = dump_array
642
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000643 def dump_struct(self, value, write, escape=escape):
644 i = id(value)
645 if self.memo.has_key(i):
646 raise TypeError, "cannot marshal recursive dictionaries"
647 self.memo[i] = None
Fredrik Lundh1538c232001-10-01 19:42:03 +0000648 dump = self.__dump
Fredrik Lundhb9056332001-07-11 17:42:21 +0000649 write("<value><struct>\n")
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000650 for k in value.keys():
Fredrik Lundhb9056332001-07-11 17:42:21 +0000651 write("<member>\n")
652 if type(k) is not StringType:
653 raise TypeError, "dictionary key must be string"
Fredrik Lundh1538c232001-10-01 19:42:03 +0000654 write("<name>%s</name>\n" % escape(k))
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000655 dump(value[k], write)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000656 write("</member>\n")
657 write("</struct></value>\n")
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000658 del self.memo[i]
Fredrik Lundhb9056332001-07-11 17:42:21 +0000659 dispatch[DictType] = dump_struct
660
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000661 def dump_instance(self, value, write):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000662 # check for special wrappers
663 if value.__class__ in WRAPPERS:
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000664 self.write = write
Fredrik Lundhb9056332001-07-11 17:42:21 +0000665 value.encode(self)
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000666 del self.write
Fredrik Lundhb9056332001-07-11 17:42:21 +0000667 else:
668 # store instance attributes as a struct (really?)
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000669 self.dump_struct(value.__dict__, write)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000670 dispatch[InstanceType] = dump_instance
671
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000672##
673# XML-RPC unmarshaller.
674#
675# @see loads
676
Fredrik Lundhb9056332001-07-11 17:42:21 +0000677class Unmarshaller:
Fred Drake1b410792001-09-04 18:55:03 +0000678 """Unmarshal an XML-RPC response, based on incoming XML event
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000679 messages (start, data, end). Call close() to get the resulting
Fred Drake1b410792001-09-04 18:55:03 +0000680 data structure.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000681
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000682 Note that this reader is fairly tolerant, and gladly accepts bogus
683 XML-RPC data without complaining (but not bogus XML).
Fred Drake1b410792001-09-04 18:55:03 +0000684 """
Fredrik Lundhb9056332001-07-11 17:42:21 +0000685
686 # and again, if you don't understand what's going on in here,
687 # that's perfectly ok.
688
689 def __init__(self):
690 self._type = None
691 self._stack = []
692 self._marks = []
693 self._data = []
694 self._methodname = None
695 self._encoding = "utf-8"
696 self.append = self._stack.append
697
698 def close(self):
699 # return response tuple and target method
700 if self._type is None or self._marks:
701 raise ResponseError()
702 if self._type == "fault":
703 raise apply(Fault, (), self._stack[0])
704 return tuple(self._stack)
705
706 def getmethodname(self):
707 return self._methodname
708
709 #
710 # event handlers
711
712 def xml(self, encoding, standalone):
713 self._encoding = encoding
714 # FIXME: assert standalone == 1 ???
715
716 def start(self, tag, attrs):
717 # prepare to handle this element
718 if tag == "array" or tag == "struct":
719 self._marks.append(len(self._stack))
720 self._data = []
721 self._value = (tag == "value")
722
723 def data(self, text):
724 self._data.append(text)
725
Fredrik Lundh1538c232001-10-01 19:42:03 +0000726 def end(self, tag, join=string.join):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000727 # call the appropriate end tag handler
728 try:
729 f = self.dispatch[tag]
730 except KeyError:
731 pass # unknown tag ?
732 else:
Fredrik Lundh1538c232001-10-01 19:42:03 +0000733 return f(self, join(self._data, ""))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000734
735 #
736 # accelerator support
737
738 def end_dispatch(self, tag, data):
739 # dispatch data
740 try:
741 f = self.dispatch[tag]
742 except KeyError:
743 pass # unknown tag ?
744 else:
745 return f(self, data)
746
747 #
748 # element decoders
749
750 dispatch = {}
751
Fredrik Lundh1538c232001-10-01 19:42:03 +0000752 def end_boolean(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000753 if data == "0":
754 self.append(False)
755 elif data == "1":
756 self.append(True)
757 else:
758 raise TypeError, "bad boolean value"
759 self._value = 0
760 dispatch["boolean"] = end_boolean
761
Fredrik Lundh1538c232001-10-01 19:42:03 +0000762 def end_int(self, data):
763 self.append(int(data))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000764 self._value = 0
765 dispatch["i4"] = end_int
766 dispatch["int"] = end_int
767
Fredrik Lundh1538c232001-10-01 19:42:03 +0000768 def end_double(self, data):
769 self.append(float(data))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000770 self._value = 0
771 dispatch["double"] = end_double
772
Fredrik Lundh1538c232001-10-01 19:42:03 +0000773 def end_string(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000774 if self._encoding:
775 data = _decode(data, self._encoding)
776 self.append(_stringify(data))
777 self._value = 0
778 dispatch["string"] = end_string
779 dispatch["name"] = end_string # struct keys are always strings
780
781 def end_array(self, data):
Raymond Hettinger46ac8eb2002-06-30 03:39:14 +0000782 mark = self._marks.pop()
Fredrik Lundhb9056332001-07-11 17:42:21 +0000783 # map arrays to Python lists
784 self._stack[mark:] = [self._stack[mark:]]
785 self._value = 0
786 dispatch["array"] = end_array
787
788 def end_struct(self, data):
Raymond Hettinger46ac8eb2002-06-30 03:39:14 +0000789 mark = self._marks.pop()
Fredrik Lundhb9056332001-07-11 17:42:21 +0000790 # map structs to Python dictionaries
791 dict = {}
792 items = self._stack[mark:]
793 for i in range(0, len(items), 2):
794 dict[_stringify(items[i])] = items[i+1]
795 self._stack[mark:] = [dict]
796 self._value = 0
797 dispatch["struct"] = end_struct
798
Fredrik Lundh1538c232001-10-01 19:42:03 +0000799 def end_base64(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000800 value = Binary()
Fredrik Lundh1538c232001-10-01 19:42:03 +0000801 value.decode(data)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000802 self.append(value)
803 self._value = 0
804 dispatch["base64"] = end_base64
805
Fredrik Lundh1538c232001-10-01 19:42:03 +0000806 def end_dateTime(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000807 value = DateTime()
Fredrik Lundh1538c232001-10-01 19:42:03 +0000808 value.decode(data)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000809 self.append(value)
810 dispatch["dateTime.iso8601"] = end_dateTime
811
812 def end_value(self, data):
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000813 # if we stumble upon a value element with no internal
Fredrik Lundhb9056332001-07-11 17:42:21 +0000814 # elements, treat it as a string element
815 if self._value:
816 self.end_string(data)
817 dispatch["value"] = end_value
818
819 def end_params(self, data):
820 self._type = "params"
821 dispatch["params"] = end_params
822
823 def end_fault(self, data):
824 self._type = "fault"
825 dispatch["fault"] = end_fault
826
Fredrik Lundh1538c232001-10-01 19:42:03 +0000827 def end_methodName(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000828 if self._encoding:
829 data = _decode(data, self._encoding)
830 self._methodname = data
831 self._type = "methodName" # no params
832 dispatch["methodName"] = end_methodName
833
834
835# --------------------------------------------------------------------
836# convenience functions
837
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000838##
839# Create a parser object, and connect it to an unmarshalling instance.
840# This function picks the fastest available XML parser.
841#
842# return A (parser, unmarshaller) tuple.
843
Fredrik Lundhb9056332001-07-11 17:42:21 +0000844def getparser():
845 """getparser() -> parser, unmarshaller
846
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000847 Create an instance of the fastest available parser, and attach it
848 to an unmarshalling object. Return both objects.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000849 """
850 if FastParser and FastUnmarshaller:
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000851 target = FastUnmarshaller(True, False, _binary, _datetime, Fault)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000852 parser = FastParser(target)
853 else:
854 target = Unmarshaller()
855 if FastParser:
856 parser = FastParser(target)
857 elif SgmlopParser:
858 parser = SgmlopParser(target)
859 elif ExpatParser:
860 parser = ExpatParser(target)
861 else:
862 parser = SlowParser(target)
863 return parser, target
864
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000865##
866# Convert a Python tuple or a Fault instance to an XML-RPC packet.
867#
868# @def dumps(params, **options)
869# @param params A tuple or Fault instance.
870# @keyparam methodname If given, create a methodCall request for
871# this method name.
872# @keyparam methodresponse If given, create a methodResponse packet.
873# If used with a tuple, the tuple must be a singleton (that is,
874# it must contain exactly one element).
875# @keyparam encoding The packet encoding.
876# @return A string containing marshalled data.
877
Fredrik Lundhb9056332001-07-11 17:42:21 +0000878def dumps(params, methodname=None, methodresponse=None, encoding=None):
879 """data [,options] -> marshalled data
880
881 Convert an argument tuple or a Fault instance to an XML-RPC
882 request (or response, if the methodresponse option is used).
883
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000884 In addition to the data object, the following options can be given
885 as keyword arguments:
Fredrik Lundhb9056332001-07-11 17:42:21 +0000886
887 methodname: the method name for a methodCall packet
888
889 methodresponse: true to create a methodResponse packet.
890 If this option is used with a tuple, the tuple must be
891 a singleton (i.e. it can contain only one element).
892
893 encoding: the packet encoding (default is UTF-8)
894
895 All 8-bit strings in the data structure are assumed to use the
896 packet encoding. Unicode strings are automatically converted,
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000897 where necessary.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000898 """
899
900 assert isinstance(params, TupleType) or isinstance(params, Fault),\
901 "argument must be tuple or Fault instance"
902
903 if isinstance(params, Fault):
904 methodresponse = 1
905 elif methodresponse and isinstance(params, TupleType):
906 assert len(params) == 1, "response tuple must be a singleton"
907
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000908 if not encoding:
Fredrik Lundhb9056332001-07-11 17:42:21 +0000909 encoding = "utf-8"
910
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000911 if FastMarshaller:
912 m = FastMarshaller(encoding)
913 else:
914 m = Marshaller(encoding)
915
Fredrik Lundhb9056332001-07-11 17:42:21 +0000916 data = m.dumps(params)
917
918 if encoding != "utf-8":
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000919 xmlheader = "<?xml version='1.0' encoding='%s'?>\n" % str(encoding)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000920 else:
921 xmlheader = "<?xml version='1.0'?>\n" # utf-8 is default
922
923 # standard XML-RPC wrappings
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000924 if methodname:
Fredrik Lundhb9056332001-07-11 17:42:21 +0000925 # a method call
926 if not isinstance(methodname, StringType):
927 methodname = methodname.encode(encoding)
928 data = (
929 xmlheader,
930 "<methodCall>\n"
931 "<methodName>", methodname, "</methodName>\n",
932 data,
933 "</methodCall>\n"
934 )
935 elif methodresponse:
936 # a method response, or a fault structure
937 data = (
938 xmlheader,
939 "<methodResponse>\n",
940 data,
941 "</methodResponse>\n"
942 )
943 else:
944 return data # return as is
945 return string.join(data, "")
946
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000947##
948# Convert an XML-RPC packet to a Python object. If the XML-RPC packet
949# represents a fault condition, this function raises a Fault exception.
950#
951# @param data An XML-RPC packet, given as an 8-bit string.
952# @return A tuple containing the the unpacked data, and the method name
953# (None if not present).
954# @see Fault
955
Fredrik Lundhb9056332001-07-11 17:42:21 +0000956def loads(data):
957 """data -> unmarshalled data, method name
958
959 Convert an XML-RPC packet to unmarshalled data plus a method
960 name (None if not present).
961
962 If the XML-RPC packet represents a fault condition, this function
963 raises a Fault exception.
964 """
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000965 import sys
Fredrik Lundhb9056332001-07-11 17:42:21 +0000966 p, u = getparser()
967 p.feed(data)
968 p.close()
969 return u.close(), u.getmethodname()
970
971
972# --------------------------------------------------------------------
973# request dispatcher
974
975class _Method:
976 # some magic to bind an XML-RPC method to an RPC server.
977 # supports "nested" methods (e.g. examples.getStateName)
978 def __init__(self, send, name):
979 self.__send = send
980 self.__name = name
981 def __getattr__(self, name):
982 return _Method(self.__send, "%s.%s" % (self.__name, name))
983 def __call__(self, *args):
984 return self.__send(self.__name, args)
985
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000986##
987# Standard transport class for XML-RPC over HTTP.
988# <p>
989# You can create custom transports by subclassing this method, and
990# overriding selected methods.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000991
992class Transport:
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000993 """Handles an HTTP transaction to an XML-RPC server."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000994
995 # client identifier (may be overridden)
996 user_agent = "xmlrpclib.py/%s (by www.pythonware.com)" % __version__
997
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000998 ##
999 # Send a complete request, and parse the response.
1000 #
1001 # @param host Target host.
1002 # @param handler Target PRC handler.
1003 # @param request_body XML-RPC request body.
1004 # @param verbose Debugging flag.
1005 # @return Parsed response.
1006
Fredrik Lundhb9056332001-07-11 17:42:21 +00001007 def request(self, host, handler, request_body, verbose=0):
1008 # issue XML-RPC request
1009
1010 h = self.make_connection(host)
1011 if verbose:
1012 h.set_debuglevel(1)
1013
1014 self.send_request(h, handler, request_body)
1015 self.send_host(h, host)
1016 self.send_user_agent(h)
1017 self.send_content(h, request_body)
1018
1019 errcode, errmsg, headers = h.getreply()
1020
1021 if errcode != 200:
1022 raise ProtocolError(
1023 host + handler,
1024 errcode, errmsg,
1025 headers
1026 )
1027
1028 self.verbose = verbose
1029
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001030 try:
1031 sock = h._conn.sock
1032 except AttributeError:
1033 sock = None
1034
1035 return self._parse_response(h.getfile(), sock)
1036
1037 ##
1038 # Create parser.
1039 #
1040 # @return A 2-tuple containing a parser and a unmarshaller.
Fredrik Lundhb9056332001-07-11 17:42:21 +00001041
Fredrik Lundhc4c062f2001-09-10 19:45:02 +00001042 def getparser(self):
1043 # get parser and unmarshaller
1044 return getparser()
1045
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001046 ##
1047 # Connect to server.
1048 #
1049 # @param host Target host.
1050 # @return A connection handle.
1051
Fredrik Lundhb9056332001-07-11 17:42:21 +00001052 def make_connection(self, host):
1053 # create a HTTP connection object from a host descriptor
1054 import httplib
1055 return httplib.HTTP(host)
1056
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001057 ##
1058 # Send request header.
1059 #
1060 # @param connection Connection handle.
1061 # @param handler Target RPC handler.
1062 # @param request_body XML-RPC body.
1063
Fredrik Lundhb9056332001-07-11 17:42:21 +00001064 def send_request(self, connection, handler, request_body):
1065 connection.putrequest("POST", handler)
1066
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001067 ##
1068 # Send host name.
1069 #
1070 # @param connection Connection handle.
1071 # @param host Host name.
1072
Fredrik Lundhb9056332001-07-11 17:42:21 +00001073 def send_host(self, connection, host):
1074 connection.putheader("Host", host)
1075
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001076 ##
1077 # Send user-agent identifier.
1078 #
1079 # @param connection Connection handle.
1080
Fredrik Lundhb9056332001-07-11 17:42:21 +00001081 def send_user_agent(self, connection):
1082 connection.putheader("User-Agent", self.user_agent)
1083
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001084 ##
1085 # Send request body.
1086 #
1087 # @param connection Connection handle.
1088 # @param request_body XML-RPC request body.
1089
Fredrik Lundhb9056332001-07-11 17:42:21 +00001090 def send_content(self, connection, request_body):
1091 connection.putheader("Content-Type", "text/xml")
1092 connection.putheader("Content-Length", str(len(request_body)))
1093 connection.endheaders()
1094 if request_body:
1095 connection.send(request_body)
1096
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001097 ##
1098 # Parse response.
1099 #
1100 # @param file Stream.
1101 # @return Response tuple and target method.
1102
1103 def parse_response(self, file):
1104 # compatibility interface
1105 return self._parse_response(file, None)
1106
1107 ##
1108 # Parse response (alternate interface). This is similar to the
1109 # parse_response method, but also provides direct access to the
1110 # underlying socket object (where available).
1111 #
1112 # @param file Stream.
1113 # @param sock Socket handle (or None, if the socket object
1114 # could not be accessed).
1115 # @return Response tuple and target method.
1116
1117 def _parse_response(self, file, sock):
1118 # read response from input file/socket, and parse it
Fredrik Lundhb9056332001-07-11 17:42:21 +00001119
Fredrik Lundhc4c062f2001-09-10 19:45:02 +00001120 p, u = self.getparser()
Fredrik Lundhb9056332001-07-11 17:42:21 +00001121
1122 while 1:
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001123 if sock:
1124 response = sock.recv(1024)
1125 else:
1126 response = file.read(1024)
Fredrik Lundhb9056332001-07-11 17:42:21 +00001127 if not response:
1128 break
1129 if self.verbose:
1130 print "body:", repr(response)
1131 p.feed(response)
1132
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001133 file.close()
Fredrik Lundhb9056332001-07-11 17:42:21 +00001134 p.close()
1135
1136 return u.close()
1137
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001138##
1139# Standard transport class for XML-RPC over HTTPS.
1140
Fredrik Lundhb9056332001-07-11 17:42:21 +00001141class SafeTransport(Transport):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +00001142 """Handles an HTTPS transaction to an XML-RPC server."""
Fredrik Lundhb9056332001-07-11 17:42:21 +00001143
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001144 # FIXME: mostly untested
1145
Fredrik Lundhb9056332001-07-11 17:42:21 +00001146 def make_connection(self, host):
1147 # create a HTTPS connection object from a host descriptor
1148 # host may be a string, or a (host, x509-dict) tuple
1149 import httplib
1150 if isinstance(host, TupleType):
1151 host, x509 = host
1152 else:
1153 x509 = {}
1154 try:
1155 HTTPS = httplib.HTTPS
1156 except AttributeError:
1157 raise NotImplementedError,\
1158 "your version of httplib doesn't support HTTPS"
1159 else:
1160 return apply(HTTPS, (host, None), x509)
1161
1162 def send_host(self, connection, host):
1163 if isinstance(host, TupleType):
1164 host, x509 = host
1165 connection.putheader("Host", host)
1166
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001167##
1168# Standard server proxy. This class establishes a virtual connection
1169# to an XML-RPC server.
1170# <p>
1171# This class is available as ServerProxy and Server. New code should
1172# use ServerProxy, to avoid confusion.
1173#
1174# @def ServerProxy(uri, **options)
1175# @param uri The connection point on the server.
1176# @keyparam transport A transport factory, compatible with the
1177# standard transport class.
1178# @keyparam encoding The default encoding used for 8-bit strings
1179# (default is UTF-8).
1180# @keyparam verbose Use a true value to enable debugging output.
1181# (printed to standard output).
1182# @see Transport
1183
Fredrik Lundhb9056332001-07-11 17:42:21 +00001184class ServerProxy:
1185 """uri [,options] -> a logical connection to an XML-RPC server
1186
1187 uri is the connection point on the server, given as
1188 scheme://host/target.
1189
1190 The standard implementation always supports the "http" scheme. If
1191 SSL socket support is available (Python 2.0), it also supports
1192 "https".
1193
1194 If the target part and the slash preceding it are both omitted,
1195 "/RPC2" is assumed.
1196
1197 The following options can be given as keyword arguments:
1198
1199 transport: a transport factory
1200 encoding: the request encoding (default is UTF-8)
1201
1202 All 8-bit strings passed to the server proxy are assumed to use
1203 the given encoding.
1204 """
1205
1206 def __init__(self, uri, transport=None, encoding=None, verbose=0):
1207 # establish a "logical" server connection
1208
1209 # get the url
Fredrik Lundhc4c062f2001-09-10 19:45:02 +00001210 import urllib
Fredrik Lundhb9056332001-07-11 17:42:21 +00001211 type, uri = urllib.splittype(uri)
1212 if type not in ("http", "https"):
1213 raise IOError, "unsupported XML-RPC protocol"
1214 self.__host, self.__handler = urllib.splithost(uri)
1215 if not self.__handler:
1216 self.__handler = "/RPC2"
1217
1218 if transport is None:
1219 if type == "https":
1220 transport = SafeTransport()
1221 else:
1222 transport = Transport()
1223 self.__transport = transport
1224
1225 self.__encoding = encoding
1226 self.__verbose = verbose
1227
1228 def __request(self, methodname, params):
1229 # call a method on the remote server
1230
1231 request = dumps(params, methodname, encoding=self.__encoding)
1232
1233 response = self.__transport.request(
1234 self.__host,
1235 self.__handler,
1236 request,
1237 verbose=self.__verbose
1238 )
1239
1240 if len(response) == 1:
1241 response = response[0]
1242
1243 return response
1244
1245 def __repr__(self):
1246 return (
Fredrik Lundh78eedce2001-08-23 20:04:33 +00001247 "<ServerProxy for %s%s>" %
Fredrik Lundhb9056332001-07-11 17:42:21 +00001248 (self.__host, self.__handler)
1249 )
1250
1251 __str__ = __repr__
1252
1253 def __getattr__(self, name):
1254 # magic method dispatcher
1255 return _Method(self.__request, name)
1256
1257 # note: to call a remote object with an non-standard name, use
1258 # result getattr(server, "strange-python-name")(args)
1259
Fredrik Lundh78eedce2001-08-23 20:04:33 +00001260# compatibility
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001261
Fredrik Lundhb9056332001-07-11 17:42:21 +00001262Server = ServerProxy
1263
1264# --------------------------------------------------------------------
1265# test code
1266
1267if __name__ == "__main__":
1268
1269 # simple test program (from the XML-RPC specification)
1270
Fredrik Lundh78eedce2001-08-23 20:04:33 +00001271 # server = ServerProxy("http://localhost:8000") # local server
1272 server = ServerProxy("http://betty.userland.com")
Fredrik Lundhb9056332001-07-11 17:42:21 +00001273
1274 print server
1275
1276 try:
1277 print server.examples.getStateName(41)
1278 except Error, v:
1279 print "ERROR", v