blob: 0eb9f3f07396cfb75ab62f50188e61820159c28b [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):
782 mark = self._marks[-1]
783 del self._marks[-1]
784 # map arrays to Python lists
785 self._stack[mark:] = [self._stack[mark:]]
786 self._value = 0
787 dispatch["array"] = end_array
788
789 def end_struct(self, data):
790 mark = self._marks[-1]
791 del self._marks[-1]
792 # map structs to Python dictionaries
793 dict = {}
794 items = self._stack[mark:]
795 for i in range(0, len(items), 2):
796 dict[_stringify(items[i])] = items[i+1]
797 self._stack[mark:] = [dict]
798 self._value = 0
799 dispatch["struct"] = end_struct
800
Fredrik Lundh1538c232001-10-01 19:42:03 +0000801 def end_base64(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000802 value = Binary()
Fredrik Lundh1538c232001-10-01 19:42:03 +0000803 value.decode(data)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000804 self.append(value)
805 self._value = 0
806 dispatch["base64"] = end_base64
807
Fredrik Lundh1538c232001-10-01 19:42:03 +0000808 def end_dateTime(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000809 value = DateTime()
Fredrik Lundh1538c232001-10-01 19:42:03 +0000810 value.decode(data)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000811 self.append(value)
812 dispatch["dateTime.iso8601"] = end_dateTime
813
814 def end_value(self, data):
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000815 # if we stumble upon a value element with no internal
Fredrik Lundhb9056332001-07-11 17:42:21 +0000816 # elements, treat it as a string element
817 if self._value:
818 self.end_string(data)
819 dispatch["value"] = end_value
820
821 def end_params(self, data):
822 self._type = "params"
823 dispatch["params"] = end_params
824
825 def end_fault(self, data):
826 self._type = "fault"
827 dispatch["fault"] = end_fault
828
Fredrik Lundh1538c232001-10-01 19:42:03 +0000829 def end_methodName(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000830 if self._encoding:
831 data = _decode(data, self._encoding)
832 self._methodname = data
833 self._type = "methodName" # no params
834 dispatch["methodName"] = end_methodName
835
836
837# --------------------------------------------------------------------
838# convenience functions
839
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000840##
841# Create a parser object, and connect it to an unmarshalling instance.
842# This function picks the fastest available XML parser.
843#
844# return A (parser, unmarshaller) tuple.
845
Fredrik Lundhb9056332001-07-11 17:42:21 +0000846def getparser():
847 """getparser() -> parser, unmarshaller
848
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000849 Create an instance of the fastest available parser, and attach it
850 to an unmarshalling object. Return both objects.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000851 """
852 if FastParser and FastUnmarshaller:
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000853 target = FastUnmarshaller(True, False, _binary, _datetime, Fault)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000854 parser = FastParser(target)
855 else:
856 target = Unmarshaller()
857 if FastParser:
858 parser = FastParser(target)
859 elif SgmlopParser:
860 parser = SgmlopParser(target)
861 elif ExpatParser:
862 parser = ExpatParser(target)
863 else:
864 parser = SlowParser(target)
865 return parser, target
866
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000867##
868# Convert a Python tuple or a Fault instance to an XML-RPC packet.
869#
870# @def dumps(params, **options)
871# @param params A tuple or Fault instance.
872# @keyparam methodname If given, create a methodCall request for
873# this method name.
874# @keyparam methodresponse If given, create a methodResponse packet.
875# If used with a tuple, the tuple must be a singleton (that is,
876# it must contain exactly one element).
877# @keyparam encoding The packet encoding.
878# @return A string containing marshalled data.
879
Fredrik Lundhb9056332001-07-11 17:42:21 +0000880def dumps(params, methodname=None, methodresponse=None, encoding=None):
881 """data [,options] -> marshalled data
882
883 Convert an argument tuple or a Fault instance to an XML-RPC
884 request (or response, if the methodresponse option is used).
885
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000886 In addition to the data object, the following options can be given
887 as keyword arguments:
Fredrik Lundhb9056332001-07-11 17:42:21 +0000888
889 methodname: the method name for a methodCall packet
890
891 methodresponse: true to create a methodResponse packet.
892 If this option is used with a tuple, the tuple must be
893 a singleton (i.e. it can contain only one element).
894
895 encoding: the packet encoding (default is UTF-8)
896
897 All 8-bit strings in the data structure are assumed to use the
898 packet encoding. Unicode strings are automatically converted,
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000899 where necessary.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000900 """
901
902 assert isinstance(params, TupleType) or isinstance(params, Fault),\
903 "argument must be tuple or Fault instance"
904
905 if isinstance(params, Fault):
906 methodresponse = 1
907 elif methodresponse and isinstance(params, TupleType):
908 assert len(params) == 1, "response tuple must be a singleton"
909
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000910 if not encoding:
Fredrik Lundhb9056332001-07-11 17:42:21 +0000911 encoding = "utf-8"
912
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000913 if FastMarshaller:
914 m = FastMarshaller(encoding)
915 else:
916 m = Marshaller(encoding)
917
Fredrik Lundhb9056332001-07-11 17:42:21 +0000918 data = m.dumps(params)
919
920 if encoding != "utf-8":
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000921 xmlheader = "<?xml version='1.0' encoding='%s'?>\n" % str(encoding)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000922 else:
923 xmlheader = "<?xml version='1.0'?>\n" # utf-8 is default
924
925 # standard XML-RPC wrappings
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000926 if methodname:
Fredrik Lundhb9056332001-07-11 17:42:21 +0000927 # a method call
928 if not isinstance(methodname, StringType):
929 methodname = methodname.encode(encoding)
930 data = (
931 xmlheader,
932 "<methodCall>\n"
933 "<methodName>", methodname, "</methodName>\n",
934 data,
935 "</methodCall>\n"
936 )
937 elif methodresponse:
938 # a method response, or a fault structure
939 data = (
940 xmlheader,
941 "<methodResponse>\n",
942 data,
943 "</methodResponse>\n"
944 )
945 else:
946 return data # return as is
947 return string.join(data, "")
948
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000949##
950# Convert an XML-RPC packet to a Python object. If the XML-RPC packet
951# represents a fault condition, this function raises a Fault exception.
952#
953# @param data An XML-RPC packet, given as an 8-bit string.
954# @return A tuple containing the the unpacked data, and the method name
955# (None if not present).
956# @see Fault
957
Fredrik Lundhb9056332001-07-11 17:42:21 +0000958def loads(data):
959 """data -> unmarshalled data, method name
960
961 Convert an XML-RPC packet to unmarshalled data plus a method
962 name (None if not present).
963
964 If the XML-RPC packet represents a fault condition, this function
965 raises a Fault exception.
966 """
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000967 import sys
Fredrik Lundhb9056332001-07-11 17:42:21 +0000968 p, u = getparser()
969 p.feed(data)
970 p.close()
971 return u.close(), u.getmethodname()
972
973
974# --------------------------------------------------------------------
975# request dispatcher
976
977class _Method:
978 # some magic to bind an XML-RPC method to an RPC server.
979 # supports "nested" methods (e.g. examples.getStateName)
980 def __init__(self, send, name):
981 self.__send = send
982 self.__name = name
983 def __getattr__(self, name):
984 return _Method(self.__send, "%s.%s" % (self.__name, name))
985 def __call__(self, *args):
986 return self.__send(self.__name, args)
987
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000988##
989# Standard transport class for XML-RPC over HTTP.
990# <p>
991# You can create custom transports by subclassing this method, and
992# overriding selected methods.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000993
994class Transport:
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000995 """Handles an HTTP transaction to an XML-RPC server."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000996
997 # client identifier (may be overridden)
998 user_agent = "xmlrpclib.py/%s (by www.pythonware.com)" % __version__
999
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001000 ##
1001 # Send a complete request, and parse the response.
1002 #
1003 # @param host Target host.
1004 # @param handler Target PRC handler.
1005 # @param request_body XML-RPC request body.
1006 # @param verbose Debugging flag.
1007 # @return Parsed response.
1008
Fredrik Lundhb9056332001-07-11 17:42:21 +00001009 def request(self, host, handler, request_body, verbose=0):
1010 # issue XML-RPC request
1011
1012 h = self.make_connection(host)
1013 if verbose:
1014 h.set_debuglevel(1)
1015
1016 self.send_request(h, handler, request_body)
1017 self.send_host(h, host)
1018 self.send_user_agent(h)
1019 self.send_content(h, request_body)
1020
1021 errcode, errmsg, headers = h.getreply()
1022
1023 if errcode != 200:
1024 raise ProtocolError(
1025 host + handler,
1026 errcode, errmsg,
1027 headers
1028 )
1029
1030 self.verbose = verbose
1031
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001032 try:
1033 sock = h._conn.sock
1034 except AttributeError:
1035 sock = None
1036
1037 return self._parse_response(h.getfile(), sock)
1038
1039 ##
1040 # Create parser.
1041 #
1042 # @return A 2-tuple containing a parser and a unmarshaller.
Fredrik Lundhb9056332001-07-11 17:42:21 +00001043
Fredrik Lundhc4c062f2001-09-10 19:45:02 +00001044 def getparser(self):
1045 # get parser and unmarshaller
1046 return getparser()
1047
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001048 ##
1049 # Connect to server.
1050 #
1051 # @param host Target host.
1052 # @return A connection handle.
1053
Fredrik Lundhb9056332001-07-11 17:42:21 +00001054 def make_connection(self, host):
1055 # create a HTTP connection object from a host descriptor
1056 import httplib
1057 return httplib.HTTP(host)
1058
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001059 ##
1060 # Send request header.
1061 #
1062 # @param connection Connection handle.
1063 # @param handler Target RPC handler.
1064 # @param request_body XML-RPC body.
1065
Fredrik Lundhb9056332001-07-11 17:42:21 +00001066 def send_request(self, connection, handler, request_body):
1067 connection.putrequest("POST", handler)
1068
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001069 ##
1070 # Send host name.
1071 #
1072 # @param connection Connection handle.
1073 # @param host Host name.
1074
Fredrik Lundhb9056332001-07-11 17:42:21 +00001075 def send_host(self, connection, host):
1076 connection.putheader("Host", host)
1077
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001078 ##
1079 # Send user-agent identifier.
1080 #
1081 # @param connection Connection handle.
1082
Fredrik Lundhb9056332001-07-11 17:42:21 +00001083 def send_user_agent(self, connection):
1084 connection.putheader("User-Agent", self.user_agent)
1085
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001086 ##
1087 # Send request body.
1088 #
1089 # @param connection Connection handle.
1090 # @param request_body XML-RPC request body.
1091
Fredrik Lundhb9056332001-07-11 17:42:21 +00001092 def send_content(self, connection, request_body):
1093 connection.putheader("Content-Type", "text/xml")
1094 connection.putheader("Content-Length", str(len(request_body)))
1095 connection.endheaders()
1096 if request_body:
1097 connection.send(request_body)
1098
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001099 ##
1100 # Parse response.
1101 #
1102 # @param file Stream.
1103 # @return Response tuple and target method.
1104
1105 def parse_response(self, file):
1106 # compatibility interface
1107 return self._parse_response(file, None)
1108
1109 ##
1110 # Parse response (alternate interface). This is similar to the
1111 # parse_response method, but also provides direct access to the
1112 # underlying socket object (where available).
1113 #
1114 # @param file Stream.
1115 # @param sock Socket handle (or None, if the socket object
1116 # could not be accessed).
1117 # @return Response tuple and target method.
1118
1119 def _parse_response(self, file, sock):
1120 # read response from input file/socket, and parse it
Fredrik Lundhb9056332001-07-11 17:42:21 +00001121
Fredrik Lundhc4c062f2001-09-10 19:45:02 +00001122 p, u = self.getparser()
Fredrik Lundhb9056332001-07-11 17:42:21 +00001123
1124 while 1:
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001125 if sock:
1126 response = sock.recv(1024)
1127 else:
1128 response = file.read(1024)
Fredrik Lundhb9056332001-07-11 17:42:21 +00001129 if not response:
1130 break
1131 if self.verbose:
1132 print "body:", repr(response)
1133 p.feed(response)
1134
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001135 file.close()
Fredrik Lundhb9056332001-07-11 17:42:21 +00001136 p.close()
1137
1138 return u.close()
1139
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001140##
1141# Standard transport class for XML-RPC over HTTPS.
1142
Fredrik Lundhb9056332001-07-11 17:42:21 +00001143class SafeTransport(Transport):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +00001144 """Handles an HTTPS transaction to an XML-RPC server."""
Fredrik Lundhb9056332001-07-11 17:42:21 +00001145
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001146 # FIXME: mostly untested
1147
Fredrik Lundhb9056332001-07-11 17:42:21 +00001148 def make_connection(self, host):
1149 # create a HTTPS connection object from a host descriptor
1150 # host may be a string, or a (host, x509-dict) tuple
1151 import httplib
1152 if isinstance(host, TupleType):
1153 host, x509 = host
1154 else:
1155 x509 = {}
1156 try:
1157 HTTPS = httplib.HTTPS
1158 except AttributeError:
1159 raise NotImplementedError,\
1160 "your version of httplib doesn't support HTTPS"
1161 else:
1162 return apply(HTTPS, (host, None), x509)
1163
1164 def send_host(self, connection, host):
1165 if isinstance(host, TupleType):
1166 host, x509 = host
1167 connection.putheader("Host", host)
1168
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001169##
1170# Standard server proxy. This class establishes a virtual connection
1171# to an XML-RPC server.
1172# <p>
1173# This class is available as ServerProxy and Server. New code should
1174# use ServerProxy, to avoid confusion.
1175#
1176# @def ServerProxy(uri, **options)
1177# @param uri The connection point on the server.
1178# @keyparam transport A transport factory, compatible with the
1179# standard transport class.
1180# @keyparam encoding The default encoding used for 8-bit strings
1181# (default is UTF-8).
1182# @keyparam verbose Use a true value to enable debugging output.
1183# (printed to standard output).
1184# @see Transport
1185
Fredrik Lundhb9056332001-07-11 17:42:21 +00001186class ServerProxy:
1187 """uri [,options] -> a logical connection to an XML-RPC server
1188
1189 uri is the connection point on the server, given as
1190 scheme://host/target.
1191
1192 The standard implementation always supports the "http" scheme. If
1193 SSL socket support is available (Python 2.0), it also supports
1194 "https".
1195
1196 If the target part and the slash preceding it are both omitted,
1197 "/RPC2" is assumed.
1198
1199 The following options can be given as keyword arguments:
1200
1201 transport: a transport factory
1202 encoding: the request encoding (default is UTF-8)
1203
1204 All 8-bit strings passed to the server proxy are assumed to use
1205 the given encoding.
1206 """
1207
1208 def __init__(self, uri, transport=None, encoding=None, verbose=0):
1209 # establish a "logical" server connection
1210
1211 # get the url
Fredrik Lundhc4c062f2001-09-10 19:45:02 +00001212 import urllib
Fredrik Lundhb9056332001-07-11 17:42:21 +00001213 type, uri = urllib.splittype(uri)
1214 if type not in ("http", "https"):
1215 raise IOError, "unsupported XML-RPC protocol"
1216 self.__host, self.__handler = urllib.splithost(uri)
1217 if not self.__handler:
1218 self.__handler = "/RPC2"
1219
1220 if transport is None:
1221 if type == "https":
1222 transport = SafeTransport()
1223 else:
1224 transport = Transport()
1225 self.__transport = transport
1226
1227 self.__encoding = encoding
1228 self.__verbose = verbose
1229
1230 def __request(self, methodname, params):
1231 # call a method on the remote server
1232
1233 request = dumps(params, methodname, encoding=self.__encoding)
1234
1235 response = self.__transport.request(
1236 self.__host,
1237 self.__handler,
1238 request,
1239 verbose=self.__verbose
1240 )
1241
1242 if len(response) == 1:
1243 response = response[0]
1244
1245 return response
1246
1247 def __repr__(self):
1248 return (
Fredrik Lundh78eedce2001-08-23 20:04:33 +00001249 "<ServerProxy for %s%s>" %
Fredrik Lundhb9056332001-07-11 17:42:21 +00001250 (self.__host, self.__handler)
1251 )
1252
1253 __str__ = __repr__
1254
1255 def __getattr__(self, name):
1256 # magic method dispatcher
1257 return _Method(self.__request, name)
1258
1259 # note: to call a remote object with an non-standard name, use
1260 # result getattr(server, "strange-python-name")(args)
1261
Fredrik Lundh78eedce2001-08-23 20:04:33 +00001262# compatibility
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001263
Fredrik Lundhb9056332001-07-11 17:42:21 +00001264Server = ServerProxy
1265
1266# --------------------------------------------------------------------
1267# test code
1268
1269if __name__ == "__main__":
1270
1271 # simple test program (from the XML-RPC specification)
1272
Fredrik Lundh78eedce2001-08-23 20:04:33 +00001273 # server = ServerProxy("http://localhost:8000") # local server
1274 server = ServerProxy("http://betty.userland.com")
Fredrik Lundhb9056332001-07-11 17:42:21 +00001275
1276 print server
1277
1278 try:
1279 print server.examples.getStateName(41)
1280 except Error, v:
1281 print "ERROR", v