blob: 5ef1cf9300ccc2ff80b1530d284f995a4a420a7e [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 Lundh1303c7c2002-10-22 18:23:00 +000046# 2002-10-22 fl Added basic authentication (based on code from Phillip Eby)
Fredrik Lundhb9056332001-07-11 17:42:21 +000047#
Fredrik Lundh3d9addd2002-06-27 21:36:21 +000048# Copyright (c) 1999-2002 by Secret Labs AB.
49# Copyright (c) 1999-2002 by Fredrik Lundh.
Fredrik Lundhb9056332001-07-11 17:42:21 +000050#
51# info@pythonware.com
52# http://www.pythonware.com
53#
54# --------------------------------------------------------------------
55# The XML-RPC client interface is
56#
Fredrik Lundh3d9addd2002-06-27 21:36:21 +000057# Copyright (c) 1999-2002 by Secret Labs AB
58# Copyright (c) 1999-2002 by Fredrik Lundh
Fredrik Lundhb9056332001-07-11 17:42:21 +000059#
60# By obtaining, using, and/or copying this software and/or its
61# associated documentation, you agree that you have read, understood,
62# and will comply with the following terms and conditions:
63#
64# Permission to use, copy, modify, and distribute this software and
65# its associated documentation for any purpose and without fee is
66# hereby granted, provided that the above copyright notice appears in
67# all copies, and that both that copyright notice and this permission
68# notice appear in supporting documentation, and that the name of
69# Secret Labs AB or the author not be used in advertising or publicity
70# pertaining to distribution of the software without specific, written
71# prior permission.
72#
73# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
74# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
75# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR
76# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
77# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
78# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
79# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
80# OF THIS SOFTWARE.
81# --------------------------------------------------------------------
82
83#
Fredrik Lundh3d9addd2002-06-27 21:36:21 +000084# things to look into some day:
Fredrik Lundhb9056332001-07-11 17:42:21 +000085
Fredrik Lundh3d9addd2002-06-27 21:36:21 +000086# TODO: sort out True/False/boolean issues for Python 2.3
Fredrik Lundhb9056332001-07-11 17:42:21 +000087
Fred Drake1b410792001-09-04 18:55:03 +000088"""
89An XML-RPC client interface for Python.
90
91The marshalling and response parser code can also be used to
92implement XML-RPC servers.
93
Fred Drake1b410792001-09-04 18:55:03 +000094Exported exceptions:
95
Fredrik Lundhc4c062f2001-09-10 19:45:02 +000096 Error Base class for client errors
97 ProtocolError Indicates an HTTP protocol error
98 ResponseError Indicates a broken response package
99 Fault Indicates an XML-RPC fault package
Fred Drake1b410792001-09-04 18:55:03 +0000100
101Exported classes:
102
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000103 ServerProxy Represents a logical connection to an XML-RPC server
Fred Drake1b410792001-09-04 18:55:03 +0000104
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000105 Boolean boolean wrapper to generate a "boolean" XML-RPC value
106 DateTime dateTime wrapper for an ISO 8601 string or time tuple or
107 localtime integer value to generate a "dateTime.iso8601"
108 XML-RPC value
109 Binary binary data wrapper
Fred Drake1b410792001-09-04 18:55:03 +0000110
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000111 SlowParser Slow but safe standard parser (based on xmllib)
112 Marshaller Generate an XML-RPC params chunk from a Python data structure
113 Unmarshaller Unmarshal an XML-RPC response from incoming XML event message
114 Transport Handles an HTTP transaction to an XML-RPC server
115 SafeTransport Handles an HTTPS transaction to an XML-RPC server
Fred Drake1b410792001-09-04 18:55:03 +0000116
117Exported constants:
118
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000119 True
120 False
Fred Drake1b410792001-09-04 18:55:03 +0000121
122Exported functions:
123
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000124 boolean Convert any Python value to an XML-RPC boolean
125 getparser Create instance of the fastest available parser & attach
126 to an unmarshalling object
127 dumps Convert an argument tuple or a Fault instance to an XML-RPC
128 request (or response, if the methodresponse option is used).
129 loads Convert an XML-RPC packet to unmarshalled data plus a method
130 name (None if not present).
Fred Drake1b410792001-09-04 18:55:03 +0000131"""
132
Fred Drake2a2d9702001-10-17 01:51:04 +0000133import re, string, time, operator
Fredrik Lundh1538c232001-10-01 19:42:03 +0000134
Fredrik Lundhb9056332001-07-11 17:42:21 +0000135from types import *
Fredrik Lundhb9056332001-07-11 17:42:21 +0000136
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000137# --------------------------------------------------------------------
138# Internal stuff
139
Fredrik Lundhb9056332001-07-11 17:42:21 +0000140try:
141 unicode
142except NameError:
143 unicode = None # unicode support not available
144
145def _decode(data, encoding, is8bit=re.compile("[\x80-\xff]").search):
146 # decode non-ascii string (if possible)
147 if unicode and encoding and is8bit(data):
148 data = unicode(data, encoding)
149 return data
150
Fredrik Lundh1538c232001-10-01 19:42:03 +0000151def escape(s, replace=string.replace):
152 s = replace(s, "&", "&")
153 s = replace(s, "<", "&lt;")
154 return replace(s, ">", "&gt;",)
155
Fredrik Lundhb9056332001-07-11 17:42:21 +0000156if unicode:
157 def _stringify(string):
158 # convert to 7-bit ascii if possible
159 try:
160 return str(string)
161 except UnicodeError:
162 return string
163else:
164 def _stringify(string):
165 return string
166
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000167__version__ = "1.0.1"
168
169# xmlrpc integer limits
170MAXINT = 2L**31-1
171MININT = -2L**31
172
173# --------------------------------------------------------------------
174# Error constants (from Dan Libby's specification at
175# http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php)
176
177# Ranges of errors
178PARSE_ERROR = -32700
179SERVER_ERROR = -32600
180APPLICATION_ERROR = -32500
181SYSTEM_ERROR = -32400
182TRANSPORT_ERROR = -32300
183
184# Specific errors
185NOT_WELLFORMED_ERROR = -32700
186UNSUPPORTED_ENCODING = -32701
187INVALID_ENCODING_CHAR = -32702
188INVALID_XMLRPC = -32600
189METHOD_NOT_FOUND = -32601
190INVALID_METHOD_PARAMS = -32602
191INTERNAL_ERROR = -32603
Fredrik Lundhb9056332001-07-11 17:42:21 +0000192
193# --------------------------------------------------------------------
194# Exceptions
195
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000196##
197# Base class for all kinds of client-side errors.
198
Fredrik Lundh78eedce2001-08-23 20:04:33 +0000199class Error(Exception):
Fred Drake1b410792001-09-04 18:55:03 +0000200 """Base class for client errors."""
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000201 def __str__(self):
202 return repr(self)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000203
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000204##
205# Indicates an HTTP-level protocol error. This is raised by the HTTP
206# transport layer, if the server returns an error code other than 200
207# (OK).
208#
209# @param url The target URL.
210# @param errcode The HTTP error code.
211# @param errmsg The HTTP error message.
212# @param headers The HTTP header dictionary.
213
Fredrik Lundhb9056332001-07-11 17:42:21 +0000214class ProtocolError(Error):
Fred Drake1b410792001-09-04 18:55:03 +0000215 """Indicates an HTTP protocol error."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000216 def __init__(self, url, errcode, errmsg, headers):
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000217 Error.__init__(self)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000218 self.url = url
219 self.errcode = errcode
220 self.errmsg = errmsg
221 self.headers = headers
222 def __repr__(self):
223 return (
224 "<ProtocolError for %s: %s %s>" %
225 (self.url, self.errcode, self.errmsg)
226 )
227
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000228##
229# Indicates a broken XML-RPC response package. This exception is
230# raised by the unmarshalling layer, if the XML-RPC response is
231# malformed.
232
Fredrik Lundhb9056332001-07-11 17:42:21 +0000233class ResponseError(Error):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000234 """Indicates a broken response package."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000235 pass
236
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000237##
238# Indicates an XML-RPC fault response package. This exception is
239# raised by the unmarshalling layer, if the XML-RPC response contains
240# a fault string. This exception can also used as a class, to
241# generate a fault XML-RPC message.
242#
243# @param faultCode The XML-RPC fault code.
244# @param faultString The XML-RPC fault string.
245
Fredrik Lundhb9056332001-07-11 17:42:21 +0000246class Fault(Error):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000247 """Indicates an XML-RPC fault package."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000248 def __init__(self, faultCode, faultString, **extra):
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000249 Error.__init__(self)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000250 self.faultCode = faultCode
251 self.faultString = faultString
252 def __repr__(self):
253 return (
254 "<Fault %s: %s>" %
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000255 (self.faultCode, repr(self.faultString))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000256 )
257
258# --------------------------------------------------------------------
259# Special values
260
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000261##
262# Wrapper for XML-RPC boolean values. Use the xmlrpclib.True and
263# xmlrpclib.False constants, or the xmlrpclib.boolean() function, to
264# generate boolean XML-RPC values.
265#
266# @param value A boolean value. Any true value is interpreted as True,
267# all other values are interpreted as False.
268
Fredrik Lundhb9056332001-07-11 17:42:21 +0000269class Boolean:
Fred Drake1b410792001-09-04 18:55:03 +0000270 """Boolean-value wrapper.
271
272 Use True or False to generate a "boolean" XML-RPC value.
273 """
Fredrik Lundhb9056332001-07-11 17:42:21 +0000274
275 def __init__(self, value = 0):
276 self.value = operator.truth(value)
277
278 def encode(self, out):
279 out.write("<value><boolean>%d</boolean></value>\n" % self.value)
280
281 def __cmp__(self, other):
282 if isinstance(other, Boolean):
283 other = other.value
284 return cmp(self.value, other)
285
286 def __repr__(self):
287 if self.value:
288 return "<Boolean True at %x>" % id(self)
289 else:
290 return "<Boolean False at %x>" % id(self)
291
292 def __int__(self):
293 return self.value
294
295 def __nonzero__(self):
296 return self.value
297
298True, False = Boolean(1), Boolean(0)
299
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000300##
301# Map true or false value to XML-RPC boolean values.
302#
303# @def boolean(value)
304# @param value A boolean value. Any true value is mapped to True,
305# all other values are mapped to False.
306# @return xmlrpclib.True or xmlrpclib.False.
307# @see Boolean
308# @see True
309# @see False
310
311def boolean(value, _truefalse=(False, True)):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000312 """Convert any Python value to XML-RPC 'boolean'."""
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000313 return _truefalse[operator.truth(value)]
314
315##
316# Wrapper for XML-RPC DateTime values. This converts a time value to
317# the format used by XML-RPC.
318# <p>
319# The value can be given as a string in the format
320# "yyyymmddThh:mm:ss", as a 9-item time tuple (as returned by
321# time.localtime()), or an integer value (as returned by time.time()).
322# The wrapper uses time.localtime() to convert an integer to a time
323# tuple.
324#
325# @param value The time, given as an ISO 8601 string, a time
326# tuple, or a integer time value.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000327
Fredrik Lundhb9056332001-07-11 17:42:21 +0000328class DateTime:
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000329 """DateTime wrapper for an ISO 8601 string or time tuple or
330 localtime integer value to generate 'dateTime.iso8601' XML-RPC
331 value.
332 """
Fredrik Lundhb9056332001-07-11 17:42:21 +0000333
334 def __init__(self, value=0):
Fredrik Lundh78eedce2001-08-23 20:04:33 +0000335 if not isinstance(value, StringType):
336 if not isinstance(value, TupleType):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000337 if value == 0:
338 value = time.time()
339 value = time.localtime(value)
340 value = time.strftime("%Y%m%dT%H:%M:%S", value)
341 self.value = value
342
343 def __cmp__(self, other):
344 if isinstance(other, DateTime):
345 other = other.value
346 return cmp(self.value, other)
347
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000348 ##
349 # Get date/time value.
350 #
351 # @return Date/time value, as an ISO 8601 string.
352
353 def __str__(self):
354 return self.value
355
Fredrik Lundhb9056332001-07-11 17:42:21 +0000356 def __repr__(self):
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000357 return "<DateTime %s at %x>" % (repr(self.value), id(self))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000358
359 def decode(self, data):
360 self.value = string.strip(data)
361
362 def encode(self, out):
363 out.write("<value><dateTime.iso8601>")
364 out.write(self.value)
365 out.write("</dateTime.iso8601></value>\n")
366
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000367def _datetime(data):
368 # decode xml element contents into a DateTime structure.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000369 value = DateTime()
370 value.decode(data)
371 return value
372
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000373##
374# Wrapper for binary data. This can be used to transport any kind
375# of binary data over XML-RPC, using BASE64 encoding.
376#
377# @param data An 8-bit string containing arbitrary data.
378
Fredrik Lundhb9056332001-07-11 17:42:21 +0000379class Binary:
Fred Drake1b410792001-09-04 18:55:03 +0000380 """Wrapper for binary data."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000381
382 def __init__(self, data=None):
383 self.data = data
384
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000385 ##
386 # Get buffer contents.
387 #
388 # @return Buffer contents, as an 8-bit string.
389
390 def __str__(self):
391 return self.data or ""
392
Fredrik Lundhb9056332001-07-11 17:42:21 +0000393 def __cmp__(self, other):
394 if isinstance(other, Binary):
395 other = other.data
396 return cmp(self.data, other)
397
398 def decode(self, data):
399 import base64
400 self.data = base64.decodestring(data)
401
402 def encode(self, out):
403 import base64, StringIO
404 out.write("<value><base64>\n")
405 base64.encode(StringIO.StringIO(self.data), out)
406 out.write("</base64></value>\n")
407
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000408def _binary(data):
409 # decode xml element contents into a Binary structure
Fredrik Lundhb9056332001-07-11 17:42:21 +0000410 value = Binary()
411 value.decode(data)
412 return value
413
414WRAPPERS = DateTime, Binary, Boolean
415
416# --------------------------------------------------------------------
417# XML parsers
418
419try:
420 # optional xmlrpclib accelerator. for more information on this
421 # component, contact info@pythonware.com
422 import _xmlrpclib
423 FastParser = _xmlrpclib.Parser
424 FastUnmarshaller = _xmlrpclib.Unmarshaller
425except (AttributeError, ImportError):
426 FastParser = FastUnmarshaller = None
427
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000428try:
429 import _xmlrpclib
430 FastMarshaller = _xmlrpclib.Marshaller
431except (AttributeError, ImportError):
432 FastMarshaller = None
433
Fredrik Lundhb9056332001-07-11 17:42:21 +0000434#
435# the SGMLOP parser is about 15x faster than Python's builtin
436# XML parser. SGMLOP sources can be downloaded from:
437#
438# http://www.pythonware.com/products/xml/sgmlop.htm
439#
440
441try:
442 import sgmlop
443 if not hasattr(sgmlop, "XMLParser"):
444 raise ImportError
445except ImportError:
446 SgmlopParser = None # sgmlop accelerator not available
447else:
448 class SgmlopParser:
449 def __init__(self, target):
450
451 # setup callbacks
452 self.finish_starttag = target.start
453 self.finish_endtag = target.end
454 self.handle_data = target.data
455 self.handle_xml = target.xml
456
457 # activate parser
458 self.parser = sgmlop.XMLParser()
459 self.parser.register(self)
460 self.feed = self.parser.feed
461 self.entity = {
462 "amp": "&", "gt": ">", "lt": "<",
463 "apos": "'", "quot": '"'
464 }
465
466 def close(self):
467 try:
468 self.parser.close()
469 finally:
470 self.parser = self.feed = None # nuke circular reference
471
472 def handle_proc(self, tag, attr):
473 m = re.search("encoding\s*=\s*['\"]([^\"']+)[\"']", attr)
474 if m:
475 self.handle_xml(m.group(1), 1)
476
477 def handle_entityref(self, entity):
478 # <string> entity
479 try:
480 self.handle_data(self.entity[entity])
481 except KeyError:
482 self.handle_data("&%s;" % entity)
483
484try:
485 from xml.parsers import expat
Guido van Rossumb8551342001-10-02 18:33:11 +0000486 if not hasattr(expat, "ParserCreate"):
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000487 raise ImportError
Fredrik Lundhb9056332001-07-11 17:42:21 +0000488except ImportError:
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000489 ExpatParser = None # expat not available
Fredrik Lundhb9056332001-07-11 17:42:21 +0000490else:
491 class ExpatParser:
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000492 # fast expat parser for Python 2.0 and later. this is about
493 # 50% slower than sgmlop, on roundtrip testing
Fredrik Lundhb9056332001-07-11 17:42:21 +0000494 def __init__(self, target):
495 self._parser = parser = expat.ParserCreate(None, None)
496 self._target = target
497 parser.StartElementHandler = target.start
498 parser.EndElementHandler = target.end
499 parser.CharacterDataHandler = target.data
500 encoding = None
501 if not parser.returns_unicode:
502 encoding = "utf-8"
503 target.xml(encoding, None)
504
505 def feed(self, data):
506 self._parser.Parse(data, 0)
507
508 def close(self):
509 self._parser.Parse("", 1) # end of data
510 del self._target, self._parser # get rid of circular references
511
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000512class SlowParser:
513 """Default XML parser (based on xmllib.XMLParser)."""
514 # this is about 10 times slower than sgmlop, on roundtrip
515 # testing.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000516 def __init__(self, target):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000517 import xmllib # lazy subclassing (!)
518 if xmllib.XMLParser not in SlowParser.__bases__:
519 SlowParser.__bases__ = (xmllib.XMLParser,)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000520 self.handle_xml = target.xml
521 self.unknown_starttag = target.start
522 self.handle_data = target.data
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000523 self.handle_cdata = target.data
Fredrik Lundhb9056332001-07-11 17:42:21 +0000524 self.unknown_endtag = target.end
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000525 try:
526 xmllib.XMLParser.__init__(self, accept_utf8=1)
527 except TypeError:
528 xmllib.XMLParser.__init__(self) # pre-2.0
Fredrik Lundhb9056332001-07-11 17:42:21 +0000529
530# --------------------------------------------------------------------
531# XML-RPC marshalling and unmarshalling code
532
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000533##
534# XML-RPC marshaller.
535#
536# @param encoding Default encoding for 8-bit strings. The default
537# value is None (interpreted as UTF-8).
538# @see dumps
539
Fredrik Lundhb9056332001-07-11 17:42:21 +0000540class Marshaller:
Fred Drake1b410792001-09-04 18:55:03 +0000541 """Generate an XML-RPC params chunk from a Python data structure.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000542
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000543 Create a Marshaller instance for each set of parameters, and use
544 the "dumps" method to convert your data (represented as a tuple)
545 to an XML-RPC params chunk. To write a fault response, pass a
546 Fault instance instead. You may prefer to use the "dumps" module
547 function for this purpose.
Fred Drake1b410792001-09-04 18:55:03 +0000548 """
Fredrik Lundhb9056332001-07-11 17:42:21 +0000549
550 # by the way, if you don't understand what's going on in here,
551 # that's perfectly ok.
552
553 def __init__(self, encoding=None):
554 self.memo = {}
555 self.data = None
556 self.encoding = encoding
557
558 dispatch = {}
559
560 def dumps(self, values):
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000561 out = []
562 write = out.append
563 dump = self.__dump
Fredrik Lundhb9056332001-07-11 17:42:21 +0000564 if isinstance(values, Fault):
565 # fault instance
566 write("<fault>\n")
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000567 dump(vars(values), write)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000568 write("</fault>\n")
569 else:
570 # parameter block
Fredrik Lundhc266bb02001-08-23 20:13:08 +0000571 # FIXME: the xml-rpc specification allows us to leave out
572 # the entire <params> block if there are no parameters.
573 # however, changing this may break older code (including
574 # old versions of xmlrpclib.py), so this is better left as
575 # is for now. See @XMLRPC3 for more information. /F
Fredrik Lundhb9056332001-07-11 17:42:21 +0000576 write("<params>\n")
577 for v in values:
578 write("<param>\n")
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000579 dump(v, write)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000580 write("</param>\n")
581 write("</params>\n")
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000582 result = string.join(out, "")
Fredrik Lundhb9056332001-07-11 17:42:21 +0000583 return result
584
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000585 def __dump(self, value, write):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000586 try:
587 f = self.dispatch[type(value)]
588 except KeyError:
589 raise TypeError, "cannot marshal %s objects" % type(value)
590 else:
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000591 f(self, value, write)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000592
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000593 def dump_int(self, value, write):
Skip Montanaro5449e082001-10-17 22:53:33 +0000594 # in case ints are > 32 bits
595 if value > MAXINT or value < MININT:
596 raise OverflowError, "int exceeds XML-RPC limits"
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000597 write("<value><int>")
598 write(str(value))
599 write("</int></value>\n")
Fredrik Lundhb9056332001-07-11 17:42:21 +0000600 dispatch[IntType] = dump_int
601
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000602 def dump_long(self, value, write):
Skip Montanaro5449e082001-10-17 22:53:33 +0000603 if value > MAXINT or value < MININT:
604 raise OverflowError, "long int exceeds XML-RPC limits"
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000605 write("<value><int>")
606 write(str(int(value)))
607 write("</int></value>\n")
Skip Montanaro5e9c71b2001-10-10 15:56:34 +0000608 dispatch[LongType] = dump_long
609
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000610 def dump_double(self, value, write):
611 write("<value><double>")
612 write(repr(value))
613 write("</double></value>\n")
Fredrik Lundhb9056332001-07-11 17:42:21 +0000614 dispatch[FloatType] = dump_double
615
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000616 def dump_string(self, value, write, escape=escape):
617 write("<value><string>")
618 write(escape(value))
619 write("</string></value>\n")
Fredrik Lundhb9056332001-07-11 17:42:21 +0000620 dispatch[StringType] = dump_string
621
622 if unicode:
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000623 def dump_unicode(self, value, write, escape=escape):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000624 value = value.encode(self.encoding)
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000625 write("<value><string>")
626 write(escape(value))
627 write("</string></value>\n")
Fredrik Lundhb9056332001-07-11 17:42:21 +0000628 dispatch[UnicodeType] = dump_unicode
629
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000630 def dump_array(self, value, write):
631 i = id(value)
632 if self.memo.has_key(i):
633 raise TypeError, "cannot marshal recursive sequences"
634 self.memo[i] = None
Fredrik Lundh1538c232001-10-01 19:42:03 +0000635 dump = self.__dump
Fredrik Lundhb9056332001-07-11 17:42:21 +0000636 write("<value><array><data>\n")
637 for v in value:
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000638 dump(v, write)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000639 write("</data></array></value>\n")
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000640 del self.memo[i]
Fredrik Lundhb9056332001-07-11 17:42:21 +0000641 dispatch[TupleType] = dump_array
642 dispatch[ListType] = dump_array
643
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000644 def dump_struct(self, value, write, escape=escape):
645 i = id(value)
646 if self.memo.has_key(i):
647 raise TypeError, "cannot marshal recursive dictionaries"
648 self.memo[i] = None
Fredrik Lundh1538c232001-10-01 19:42:03 +0000649 dump = self.__dump
Fredrik Lundhb9056332001-07-11 17:42:21 +0000650 write("<value><struct>\n")
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000651 for k in value.keys():
Fredrik Lundhb9056332001-07-11 17:42:21 +0000652 write("<member>\n")
653 if type(k) is not StringType:
654 raise TypeError, "dictionary key must be string"
Fredrik Lundh1538c232001-10-01 19:42:03 +0000655 write("<name>%s</name>\n" % escape(k))
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000656 dump(value[k], write)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000657 write("</member>\n")
658 write("</struct></value>\n")
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000659 del self.memo[i]
Fredrik Lundhb9056332001-07-11 17:42:21 +0000660 dispatch[DictType] = dump_struct
661
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000662 def dump_instance(self, value, write):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000663 # check for special wrappers
664 if value.__class__ in WRAPPERS:
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000665 self.write = write
Fredrik Lundhb9056332001-07-11 17:42:21 +0000666 value.encode(self)
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000667 del self.write
Fredrik Lundhb9056332001-07-11 17:42:21 +0000668 else:
669 # store instance attributes as a struct (really?)
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000670 self.dump_struct(value.__dict__, write)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000671 dispatch[InstanceType] = dump_instance
672
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000673##
674# XML-RPC unmarshaller.
675#
676# @see loads
677
Fredrik Lundhb9056332001-07-11 17:42:21 +0000678class Unmarshaller:
Fred Drake1b410792001-09-04 18:55:03 +0000679 """Unmarshal an XML-RPC response, based on incoming XML event
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000680 messages (start, data, end). Call close() to get the resulting
Fred Drake1b410792001-09-04 18:55:03 +0000681 data structure.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000682
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000683 Note that this reader is fairly tolerant, and gladly accepts bogus
684 XML-RPC data without complaining (but not bogus XML).
Fred Drake1b410792001-09-04 18:55:03 +0000685 """
Fredrik Lundhb9056332001-07-11 17:42:21 +0000686
687 # and again, if you don't understand what's going on in here,
688 # that's perfectly ok.
689
690 def __init__(self):
691 self._type = None
692 self._stack = []
693 self._marks = []
694 self._data = []
695 self._methodname = None
696 self._encoding = "utf-8"
697 self.append = self._stack.append
698
699 def close(self):
700 # return response tuple and target method
701 if self._type is None or self._marks:
702 raise ResponseError()
703 if self._type == "fault":
704 raise apply(Fault, (), self._stack[0])
705 return tuple(self._stack)
706
707 def getmethodname(self):
708 return self._methodname
709
710 #
711 # event handlers
712
713 def xml(self, encoding, standalone):
714 self._encoding = encoding
715 # FIXME: assert standalone == 1 ???
716
717 def start(self, tag, attrs):
718 # prepare to handle this element
719 if tag == "array" or tag == "struct":
720 self._marks.append(len(self._stack))
721 self._data = []
722 self._value = (tag == "value")
723
724 def data(self, text):
725 self._data.append(text)
726
Fredrik Lundh1538c232001-10-01 19:42:03 +0000727 def end(self, tag, join=string.join):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000728 # call the appropriate end tag handler
729 try:
730 f = self.dispatch[tag]
731 except KeyError:
732 pass # unknown tag ?
733 else:
Fredrik Lundh1538c232001-10-01 19:42:03 +0000734 return f(self, join(self._data, ""))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000735
736 #
737 # accelerator support
738
739 def end_dispatch(self, tag, data):
740 # dispatch data
741 try:
742 f = self.dispatch[tag]
743 except KeyError:
744 pass # unknown tag ?
745 else:
746 return f(self, data)
747
748 #
749 # element decoders
750
751 dispatch = {}
752
Fredrik Lundh1538c232001-10-01 19:42:03 +0000753 def end_boolean(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000754 if data == "0":
755 self.append(False)
756 elif data == "1":
757 self.append(True)
758 else:
759 raise TypeError, "bad boolean value"
760 self._value = 0
761 dispatch["boolean"] = end_boolean
762
Fredrik Lundh1538c232001-10-01 19:42:03 +0000763 def end_int(self, data):
764 self.append(int(data))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000765 self._value = 0
766 dispatch["i4"] = end_int
767 dispatch["int"] = end_int
768
Fredrik Lundh1538c232001-10-01 19:42:03 +0000769 def end_double(self, data):
770 self.append(float(data))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000771 self._value = 0
772 dispatch["double"] = end_double
773
Fredrik Lundh1538c232001-10-01 19:42:03 +0000774 def end_string(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000775 if self._encoding:
776 data = _decode(data, self._encoding)
777 self.append(_stringify(data))
778 self._value = 0
779 dispatch["string"] = end_string
780 dispatch["name"] = end_string # struct keys are always strings
781
782 def end_array(self, data):
Raymond Hettinger46ac8eb2002-06-30 03:39:14 +0000783 mark = self._marks.pop()
Fredrik Lundhb9056332001-07-11 17:42:21 +0000784 # 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):
Raymond Hettinger46ac8eb2002-06-30 03:39:14 +0000790 mark = self._marks.pop()
Fredrik Lundhb9056332001-07-11 17:42:21 +0000791 # map structs to Python dictionaries
792 dict = {}
793 items = self._stack[mark:]
794 for i in range(0, len(items), 2):
795 dict[_stringify(items[i])] = items[i+1]
796 self._stack[mark:] = [dict]
797 self._value = 0
798 dispatch["struct"] = end_struct
799
Fredrik Lundh1538c232001-10-01 19:42:03 +0000800 def end_base64(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000801 value = Binary()
Fredrik Lundh1538c232001-10-01 19:42:03 +0000802 value.decode(data)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000803 self.append(value)
804 self._value = 0
805 dispatch["base64"] = end_base64
806
Fredrik Lundh1538c232001-10-01 19:42:03 +0000807 def end_dateTime(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000808 value = DateTime()
Fredrik Lundh1538c232001-10-01 19:42:03 +0000809 value.decode(data)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000810 self.append(value)
811 dispatch["dateTime.iso8601"] = end_dateTime
812
813 def end_value(self, data):
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000814 # if we stumble upon a value element with no internal
Fredrik Lundhb9056332001-07-11 17:42:21 +0000815 # elements, treat it as a string element
816 if self._value:
817 self.end_string(data)
818 dispatch["value"] = end_value
819
820 def end_params(self, data):
821 self._type = "params"
822 dispatch["params"] = end_params
823
824 def end_fault(self, data):
825 self._type = "fault"
826 dispatch["fault"] = end_fault
827
Fredrik Lundh1538c232001-10-01 19:42:03 +0000828 def end_methodName(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000829 if self._encoding:
830 data = _decode(data, self._encoding)
831 self._methodname = data
832 self._type = "methodName" # no params
833 dispatch["methodName"] = end_methodName
834
835
836# --------------------------------------------------------------------
837# convenience functions
838
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000839##
840# Create a parser object, and connect it to an unmarshalling instance.
841# This function picks the fastest available XML parser.
842#
843# return A (parser, unmarshaller) tuple.
844
Fredrik Lundhb9056332001-07-11 17:42:21 +0000845def getparser():
846 """getparser() -> parser, unmarshaller
847
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000848 Create an instance of the fastest available parser, and attach it
849 to an unmarshalling object. Return both objects.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000850 """
851 if FastParser and FastUnmarshaller:
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000852 target = FastUnmarshaller(True, False, _binary, _datetime, Fault)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000853 parser = FastParser(target)
854 else:
855 target = Unmarshaller()
856 if FastParser:
857 parser = FastParser(target)
858 elif SgmlopParser:
859 parser = SgmlopParser(target)
860 elif ExpatParser:
861 parser = ExpatParser(target)
862 else:
863 parser = SlowParser(target)
864 return parser, target
865
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000866##
867# Convert a Python tuple or a Fault instance to an XML-RPC packet.
868#
869# @def dumps(params, **options)
870# @param params A tuple or Fault instance.
871# @keyparam methodname If given, create a methodCall request for
872# this method name.
873# @keyparam methodresponse If given, create a methodResponse packet.
874# If used with a tuple, the tuple must be a singleton (that is,
875# it must contain exactly one element).
876# @keyparam encoding The packet encoding.
877# @return A string containing marshalled data.
878
Fredrik Lundhb9056332001-07-11 17:42:21 +0000879def dumps(params, methodname=None, methodresponse=None, encoding=None):
880 """data [,options] -> marshalled data
881
882 Convert an argument tuple or a Fault instance to an XML-RPC
883 request (or response, if the methodresponse option is used).
884
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000885 In addition to the data object, the following options can be given
886 as keyword arguments:
Fredrik Lundhb9056332001-07-11 17:42:21 +0000887
888 methodname: the method name for a methodCall packet
889
890 methodresponse: true to create a methodResponse packet.
891 If this option is used with a tuple, the tuple must be
892 a singleton (i.e. it can contain only one element).
893
894 encoding: the packet encoding (default is UTF-8)
895
896 All 8-bit strings in the data structure are assumed to use the
897 packet encoding. Unicode strings are automatically converted,
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000898 where necessary.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000899 """
900
901 assert isinstance(params, TupleType) or isinstance(params, Fault),\
902 "argument must be tuple or Fault instance"
903
904 if isinstance(params, Fault):
905 methodresponse = 1
906 elif methodresponse and isinstance(params, TupleType):
907 assert len(params) == 1, "response tuple must be a singleton"
908
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000909 if not encoding:
Fredrik Lundhb9056332001-07-11 17:42:21 +0000910 encoding = "utf-8"
911
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000912 if FastMarshaller:
913 m = FastMarshaller(encoding)
914 else:
915 m = Marshaller(encoding)
916
Fredrik Lundhb9056332001-07-11 17:42:21 +0000917 data = m.dumps(params)
918
919 if encoding != "utf-8":
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000920 xmlheader = "<?xml version='1.0' encoding='%s'?>\n" % str(encoding)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000921 else:
922 xmlheader = "<?xml version='1.0'?>\n" # utf-8 is default
923
924 # standard XML-RPC wrappings
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000925 if methodname:
Fredrik Lundhb9056332001-07-11 17:42:21 +0000926 # a method call
927 if not isinstance(methodname, StringType):
928 methodname = methodname.encode(encoding)
929 data = (
930 xmlheader,
931 "<methodCall>\n"
932 "<methodName>", methodname, "</methodName>\n",
933 data,
934 "</methodCall>\n"
935 )
936 elif methodresponse:
937 # a method response, or a fault structure
938 data = (
939 xmlheader,
940 "<methodResponse>\n",
941 data,
942 "</methodResponse>\n"
943 )
944 else:
945 return data # return as is
946 return string.join(data, "")
947
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000948##
949# Convert an XML-RPC packet to a Python object. If the XML-RPC packet
950# represents a fault condition, this function raises a Fault exception.
951#
952# @param data An XML-RPC packet, given as an 8-bit string.
953# @return A tuple containing the the unpacked data, and the method name
954# (None if not present).
955# @see Fault
956
Fredrik Lundhb9056332001-07-11 17:42:21 +0000957def loads(data):
958 """data -> unmarshalled data, method name
959
960 Convert an XML-RPC packet to unmarshalled data plus a method
961 name (None if not present).
962
963 If the XML-RPC packet represents a fault condition, this function
964 raises a Fault exception.
965 """
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000966 import sys
Fredrik Lundhb9056332001-07-11 17:42:21 +0000967 p, u = getparser()
968 p.feed(data)
969 p.close()
970 return u.close(), u.getmethodname()
971
972
973# --------------------------------------------------------------------
974# request dispatcher
975
976class _Method:
977 # some magic to bind an XML-RPC method to an RPC server.
978 # supports "nested" methods (e.g. examples.getStateName)
979 def __init__(self, send, name):
980 self.__send = send
981 self.__name = name
982 def __getattr__(self, name):
983 return _Method(self.__send, "%s.%s" % (self.__name, name))
984 def __call__(self, *args):
985 return self.__send(self.__name, args)
986
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000987##
988# Standard transport class for XML-RPC over HTTP.
989# <p>
990# You can create custom transports by subclassing this method, and
991# overriding selected methods.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000992
993class Transport:
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000994 """Handles an HTTP transaction to an XML-RPC server."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000995
996 # client identifier (may be overridden)
997 user_agent = "xmlrpclib.py/%s (by www.pythonware.com)" % __version__
998
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000999 ##
1000 # Send a complete request, and parse the response.
1001 #
1002 # @param host Target host.
1003 # @param handler Target PRC handler.
1004 # @param request_body XML-RPC request body.
1005 # @param verbose Debugging flag.
1006 # @return Parsed response.
1007
Fredrik Lundhb9056332001-07-11 17:42:21 +00001008 def request(self, host, handler, request_body, verbose=0):
1009 # issue XML-RPC request
1010
1011 h = self.make_connection(host)
1012 if verbose:
1013 h.set_debuglevel(1)
1014
1015 self.send_request(h, handler, request_body)
1016 self.send_host(h, host)
1017 self.send_user_agent(h)
1018 self.send_content(h, request_body)
1019
1020 errcode, errmsg, headers = h.getreply()
1021
1022 if errcode != 200:
1023 raise ProtocolError(
1024 host + handler,
1025 errcode, errmsg,
1026 headers
1027 )
1028
1029 self.verbose = verbose
1030
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001031 try:
1032 sock = h._conn.sock
1033 except AttributeError:
1034 sock = None
1035
1036 return self._parse_response(h.getfile(), sock)
1037
1038 ##
1039 # Create parser.
1040 #
1041 # @return A 2-tuple containing a parser and a unmarshaller.
Fredrik Lundhb9056332001-07-11 17:42:21 +00001042
Fredrik Lundhc4c062f2001-09-10 19:45:02 +00001043 def getparser(self):
1044 # get parser and unmarshaller
1045 return getparser()
1046
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001047 ##
Fredrik Lundh1303c7c2002-10-22 18:23:00 +00001048 # Get authorization info from host parameter
1049 # Host may be a string, or a (host, x509-dict) tuple; if a string,
1050 # it is checked for a "user:pw@host" format, and a "Basic
1051 # Authentication" header is added if appropriate.
1052 #
1053 # @param host Host descriptor (URL or (URL, x509 info) tuple).
1054 # @return A 3-tuple containing (actual host, extra headers,
1055 # x509 info). The header and x509 fields may be None.
1056
1057 def get_host_info(self, host):
1058
1059 x509 = {}
1060 if isinstance(host, TupleType):
1061 host, x509 = host
1062
1063 import urllib
1064 auth, host = urllib.splituser(host)
1065
1066 if auth:
1067 import base64
Fredrik Lundh768c98b2002-11-01 17:14:16 +00001068 auth = base64.encodestring(urllib.unquote(auth))
Fredrik Lundh1303c7c2002-10-22 18:23:00 +00001069 auth = string.join(string.split(auth), "") # get rid of whitespace
1070 extra_headers = [
1071 ("Authorization", "Basic " + auth)
1072 ]
1073 else:
1074 extra_headers = None
1075
1076 return host, extra_headers, x509
1077
1078 ##
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001079 # Connect to server.
1080 #
1081 # @param host Target host.
1082 # @return A connection handle.
1083
Fredrik Lundhb9056332001-07-11 17:42:21 +00001084 def make_connection(self, host):
1085 # create a HTTP connection object from a host descriptor
1086 import httplib
Fredrik Lundh1303c7c2002-10-22 18:23:00 +00001087 host, extra_headers, x509 = self.get_host_info(host)
Fredrik Lundhb9056332001-07-11 17:42:21 +00001088 return httplib.HTTP(host)
1089
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001090 ##
1091 # Send request header.
1092 #
1093 # @param connection Connection handle.
1094 # @param handler Target RPC handler.
1095 # @param request_body XML-RPC body.
1096
Fredrik Lundhb9056332001-07-11 17:42:21 +00001097 def send_request(self, connection, handler, request_body):
1098 connection.putrequest("POST", handler)
1099
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001100 ##
1101 # Send host name.
1102 #
1103 # @param connection Connection handle.
1104 # @param host Host name.
1105
Fredrik Lundhb9056332001-07-11 17:42:21 +00001106 def send_host(self, connection, host):
Fredrik Lundh1303c7c2002-10-22 18:23:00 +00001107 host, extra_headers, x509 = self.get_host_info(host)
Fredrik Lundhb9056332001-07-11 17:42:21 +00001108 connection.putheader("Host", host)
Fredrik Lundh1303c7c2002-10-22 18:23:00 +00001109 if extra_headers:
1110 if isinstance(extra_headers, DictType):
1111 extra_headers = extra_headers.items()
1112 for key, value in extra_headers:
1113 connection.putheader(key, value)
Fredrik Lundhb9056332001-07-11 17:42:21 +00001114
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001115 ##
1116 # Send user-agent identifier.
1117 #
1118 # @param connection Connection handle.
1119
Fredrik Lundhb9056332001-07-11 17:42:21 +00001120 def send_user_agent(self, connection):
1121 connection.putheader("User-Agent", self.user_agent)
1122
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001123 ##
1124 # Send request body.
1125 #
1126 # @param connection Connection handle.
1127 # @param request_body XML-RPC request body.
1128
Fredrik Lundhb9056332001-07-11 17:42:21 +00001129 def send_content(self, connection, request_body):
1130 connection.putheader("Content-Type", "text/xml")
1131 connection.putheader("Content-Length", str(len(request_body)))
1132 connection.endheaders()
1133 if request_body:
1134 connection.send(request_body)
1135
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001136 ##
1137 # Parse response.
1138 #
1139 # @param file Stream.
1140 # @return Response tuple and target method.
1141
1142 def parse_response(self, file):
1143 # compatibility interface
1144 return self._parse_response(file, None)
1145
1146 ##
1147 # Parse response (alternate interface). This is similar to the
1148 # parse_response method, but also provides direct access to the
1149 # underlying socket object (where available).
1150 #
1151 # @param file Stream.
1152 # @param sock Socket handle (or None, if the socket object
1153 # could not be accessed).
1154 # @return Response tuple and target method.
1155
1156 def _parse_response(self, file, sock):
1157 # read response from input file/socket, and parse it
Fredrik Lundhb9056332001-07-11 17:42:21 +00001158
Fredrik Lundhc4c062f2001-09-10 19:45:02 +00001159 p, u = self.getparser()
Fredrik Lundhb9056332001-07-11 17:42:21 +00001160
1161 while 1:
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001162 if sock:
1163 response = sock.recv(1024)
1164 else:
1165 response = file.read(1024)
Fredrik Lundhb9056332001-07-11 17:42:21 +00001166 if not response:
1167 break
1168 if self.verbose:
1169 print "body:", repr(response)
1170 p.feed(response)
1171
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001172 file.close()
Fredrik Lundhb9056332001-07-11 17:42:21 +00001173 p.close()
1174
1175 return u.close()
1176
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001177##
1178# Standard transport class for XML-RPC over HTTPS.
1179
Fredrik Lundhb9056332001-07-11 17:42:21 +00001180class SafeTransport(Transport):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +00001181 """Handles an HTTPS transaction to an XML-RPC server."""
Fredrik Lundhb9056332001-07-11 17:42:21 +00001182
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001183 # FIXME: mostly untested
1184
Fredrik Lundhb9056332001-07-11 17:42:21 +00001185 def make_connection(self, host):
1186 # create a HTTPS connection object from a host descriptor
1187 # host may be a string, or a (host, x509-dict) tuple
1188 import httplib
Fredrik Lundh1303c7c2002-10-22 18:23:00 +00001189 host, extra_headers, x509 = self.get_host_info(host)
Fredrik Lundhb9056332001-07-11 17:42:21 +00001190 try:
1191 HTTPS = httplib.HTTPS
1192 except AttributeError:
Fredrik Lundh1303c7c2002-10-22 18:23:00 +00001193 raise NotImplementedError(
1194 "your version of httplib doesn't support HTTPS"
1195 )
Fredrik Lundhb9056332001-07-11 17:42:21 +00001196 else:
Fredrik Lundh1303c7c2002-10-22 18:23:00 +00001197 return apply(HTTPS, (host, None), x509 or {})
Fredrik Lundhb9056332001-07-11 17:42:21 +00001198
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001199##
1200# Standard server proxy. This class establishes a virtual connection
1201# to an XML-RPC server.
1202# <p>
1203# This class is available as ServerProxy and Server. New code should
1204# use ServerProxy, to avoid confusion.
1205#
1206# @def ServerProxy(uri, **options)
1207# @param uri The connection point on the server.
1208# @keyparam transport A transport factory, compatible with the
1209# standard transport class.
1210# @keyparam encoding The default encoding used for 8-bit strings
1211# (default is UTF-8).
1212# @keyparam verbose Use a true value to enable debugging output.
1213# (printed to standard output).
1214# @see Transport
1215
Fredrik Lundhb9056332001-07-11 17:42:21 +00001216class ServerProxy:
1217 """uri [,options] -> a logical connection to an XML-RPC server
1218
1219 uri is the connection point on the server, given as
1220 scheme://host/target.
1221
1222 The standard implementation always supports the "http" scheme. If
1223 SSL socket support is available (Python 2.0), it also supports
1224 "https".
1225
1226 If the target part and the slash preceding it are both omitted,
1227 "/RPC2" is assumed.
1228
1229 The following options can be given as keyword arguments:
1230
1231 transport: a transport factory
1232 encoding: the request encoding (default is UTF-8)
1233
1234 All 8-bit strings passed to the server proxy are assumed to use
1235 the given encoding.
1236 """
1237
1238 def __init__(self, uri, transport=None, encoding=None, verbose=0):
1239 # establish a "logical" server connection
1240
1241 # get the url
Fredrik Lundhc4c062f2001-09-10 19:45:02 +00001242 import urllib
Fredrik Lundhb9056332001-07-11 17:42:21 +00001243 type, uri = urllib.splittype(uri)
1244 if type not in ("http", "https"):
1245 raise IOError, "unsupported XML-RPC protocol"
1246 self.__host, self.__handler = urllib.splithost(uri)
1247 if not self.__handler:
1248 self.__handler = "/RPC2"
1249
1250 if transport is None:
1251 if type == "https":
1252 transport = SafeTransport()
1253 else:
1254 transport = Transport()
1255 self.__transport = transport
1256
1257 self.__encoding = encoding
1258 self.__verbose = verbose
1259
1260 def __request(self, methodname, params):
1261 # call a method on the remote server
1262
1263 request = dumps(params, methodname, encoding=self.__encoding)
1264
1265 response = self.__transport.request(
1266 self.__host,
1267 self.__handler,
1268 request,
1269 verbose=self.__verbose
1270 )
1271
1272 if len(response) == 1:
1273 response = response[0]
1274
1275 return response
1276
1277 def __repr__(self):
1278 return (
Fredrik Lundh78eedce2001-08-23 20:04:33 +00001279 "<ServerProxy for %s%s>" %
Fredrik Lundhb9056332001-07-11 17:42:21 +00001280 (self.__host, self.__handler)
1281 )
1282
1283 __str__ = __repr__
1284
1285 def __getattr__(self, name):
1286 # magic method dispatcher
1287 return _Method(self.__request, name)
1288
1289 # note: to call a remote object with an non-standard name, use
1290 # result getattr(server, "strange-python-name")(args)
1291
Fredrik Lundh78eedce2001-08-23 20:04:33 +00001292# compatibility
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001293
Fredrik Lundhb9056332001-07-11 17:42:21 +00001294Server = ServerProxy
1295
1296# --------------------------------------------------------------------
1297# test code
1298
1299if __name__ == "__main__":
1300
1301 # simple test program (from the XML-RPC specification)
1302
Fredrik Lundh78eedce2001-08-23 20:04:33 +00001303 # server = ServerProxy("http://localhost:8000") # local server
1304 server = ServerProxy("http://betty.userland.com")
Fredrik Lundhb9056332001-07-11 17:42:21 +00001305
1306 print server
1307
1308 try:
1309 print server.examples.getStateName(41)
1310 except Error, v:
1311 print "ERROR", v