blob: 0cb9ba62c78ec61c7db50d38732f03283168d69f [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
Skip Montanaro9a7c96a2003-01-22 18:17:25 +0000145try:
146 _bool_is_builtin = False.__class__.__name__ == "bool"
147except NameError:
148 _bool_is_builtin = 0
149
Fredrik Lundhb9056332001-07-11 17:42:21 +0000150def _decode(data, encoding, is8bit=re.compile("[\x80-\xff]").search):
151 # decode non-ascii string (if possible)
152 if unicode and encoding and is8bit(data):
153 data = unicode(data, encoding)
154 return data
155
Fredrik Lundh1538c232001-10-01 19:42:03 +0000156def escape(s, replace=string.replace):
157 s = replace(s, "&", "&")
158 s = replace(s, "<", "&lt;")
159 return replace(s, ">", "&gt;",)
160
Fredrik Lundhb9056332001-07-11 17:42:21 +0000161if unicode:
162 def _stringify(string):
163 # convert to 7-bit ascii if possible
164 try:
165 return str(string)
166 except UnicodeError:
167 return string
168else:
169 def _stringify(string):
170 return string
171
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000172__version__ = "1.0.1"
173
174# xmlrpc integer limits
175MAXINT = 2L**31-1
176MININT = -2L**31
177
178# --------------------------------------------------------------------
179# Error constants (from Dan Libby's specification at
180# http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php)
181
182# Ranges of errors
183PARSE_ERROR = -32700
184SERVER_ERROR = -32600
185APPLICATION_ERROR = -32500
186SYSTEM_ERROR = -32400
187TRANSPORT_ERROR = -32300
188
189# Specific errors
190NOT_WELLFORMED_ERROR = -32700
191UNSUPPORTED_ENCODING = -32701
192INVALID_ENCODING_CHAR = -32702
193INVALID_XMLRPC = -32600
194METHOD_NOT_FOUND = -32601
195INVALID_METHOD_PARAMS = -32602
196INTERNAL_ERROR = -32603
Fredrik Lundhb9056332001-07-11 17:42:21 +0000197
198# --------------------------------------------------------------------
199# Exceptions
200
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000201##
202# Base class for all kinds of client-side errors.
203
Fredrik Lundh78eedce2001-08-23 20:04:33 +0000204class Error(Exception):
Fred Drake1b410792001-09-04 18:55:03 +0000205 """Base class for client errors."""
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000206 def __str__(self):
207 return repr(self)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000208
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000209##
210# Indicates an HTTP-level protocol error. This is raised by the HTTP
211# transport layer, if the server returns an error code other than 200
212# (OK).
213#
214# @param url The target URL.
215# @param errcode The HTTP error code.
216# @param errmsg The HTTP error message.
217# @param headers The HTTP header dictionary.
218
Fredrik Lundhb9056332001-07-11 17:42:21 +0000219class ProtocolError(Error):
Fred Drake1b410792001-09-04 18:55:03 +0000220 """Indicates an HTTP protocol error."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000221 def __init__(self, url, errcode, errmsg, headers):
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000222 Error.__init__(self)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000223 self.url = url
224 self.errcode = errcode
225 self.errmsg = errmsg
226 self.headers = headers
227 def __repr__(self):
228 return (
229 "<ProtocolError for %s: %s %s>" %
230 (self.url, self.errcode, self.errmsg)
231 )
232
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000233##
234# Indicates a broken XML-RPC response package. This exception is
235# raised by the unmarshalling layer, if the XML-RPC response is
236# malformed.
237
Fredrik Lundhb9056332001-07-11 17:42:21 +0000238class ResponseError(Error):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000239 """Indicates a broken response package."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000240 pass
241
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000242##
243# Indicates an XML-RPC fault response package. This exception is
244# raised by the unmarshalling layer, if the XML-RPC response contains
245# a fault string. This exception can also used as a class, to
246# generate a fault XML-RPC message.
247#
248# @param faultCode The XML-RPC fault code.
249# @param faultString The XML-RPC fault string.
250
Fredrik Lundhb9056332001-07-11 17:42:21 +0000251class Fault(Error):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000252 """Indicates an XML-RPC fault package."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000253 def __init__(self, faultCode, faultString, **extra):
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000254 Error.__init__(self)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000255 self.faultCode = faultCode
256 self.faultString = faultString
257 def __repr__(self):
258 return (
259 "<Fault %s: %s>" %
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000260 (self.faultCode, repr(self.faultString))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000261 )
262
263# --------------------------------------------------------------------
264# Special values
265
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000266##
267# Wrapper for XML-RPC boolean values. Use the xmlrpclib.True and
268# xmlrpclib.False constants, or the xmlrpclib.boolean() function, to
269# generate boolean XML-RPC values.
270#
271# @param value A boolean value. Any true value is interpreted as True,
272# all other values are interpreted as False.
273
Skip Montanaro9a7c96a2003-01-22 18:17:25 +0000274if _bool_is_builtin:
275 boolean = Boolean = bool
276 # to avoid breaking code which references xmlrpclib.{True,False}
277 True, False = True, False
278else:
279 class Boolean:
280 """Boolean-value wrapper.
Fred Drake1b410792001-09-04 18:55:03 +0000281
Skip Montanaro9a7c96a2003-01-22 18:17:25 +0000282 Use True or False to generate a "boolean" XML-RPC value.
283 """
Fredrik Lundhb9056332001-07-11 17:42:21 +0000284
Skip Montanaro9a7c96a2003-01-22 18:17:25 +0000285 def __init__(self, value = 0):
286 self.value = operator.truth(value)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000287
Skip Montanaro9a7c96a2003-01-22 18:17:25 +0000288 def encode(self, out):
289 out.write("<value><boolean>%d</boolean></value>\n" % self.value)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000290
Skip Montanaro9a7c96a2003-01-22 18:17:25 +0000291 def __cmp__(self, other):
292 if isinstance(other, Boolean):
293 other = other.value
294 return cmp(self.value, other)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000295
Skip Montanaro9a7c96a2003-01-22 18:17:25 +0000296 def __repr__(self):
297 if self.value:
298 return "<Boolean True at %x>" % id(self)
299 else:
300 return "<Boolean False at %x>" % id(self)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000301
Skip Montanaro9a7c96a2003-01-22 18:17:25 +0000302 def __int__(self):
303 return self.value
Fredrik Lundhb9056332001-07-11 17:42:21 +0000304
Skip Montanaro9a7c96a2003-01-22 18:17:25 +0000305 def __nonzero__(self):
306 return self.value
Fredrik Lundhb9056332001-07-11 17:42:21 +0000307
Skip Montanaro9a7c96a2003-01-22 18:17:25 +0000308 True, False = Boolean(1), Boolean(0)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000309
Skip Montanaro9a7c96a2003-01-22 18:17:25 +0000310 ##
311 # Map true or false value to XML-RPC boolean values.
312 #
313 # @def boolean(value)
314 # @param value A boolean value. Any true value is mapped to True,
315 # all other values are mapped to False.
316 # @return xmlrpclib.True or xmlrpclib.False.
317 # @see Boolean
318 # @see True
319 # @see False
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000320
Skip Montanaro9a7c96a2003-01-22 18:17:25 +0000321 def boolean(value, _truefalse=(False, True)):
322 """Convert any Python value to XML-RPC 'boolean'."""
323 return _truefalse[operator.truth(value)]
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000324
325##
326# Wrapper for XML-RPC DateTime values. This converts a time value to
327# the format used by XML-RPC.
328# <p>
329# The value can be given as a string in the format
330# "yyyymmddThh:mm:ss", as a 9-item time tuple (as returned by
331# time.localtime()), or an integer value (as returned by time.time()).
332# The wrapper uses time.localtime() to convert an integer to a time
333# tuple.
334#
335# @param value The time, given as an ISO 8601 string, a time
336# tuple, or a integer time value.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000337
Fredrik Lundhb9056332001-07-11 17:42:21 +0000338class DateTime:
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000339 """DateTime wrapper for an ISO 8601 string or time tuple or
340 localtime integer value to generate 'dateTime.iso8601' XML-RPC
341 value.
342 """
Fredrik Lundhb9056332001-07-11 17:42:21 +0000343
344 def __init__(self, value=0):
Fredrik Lundh78eedce2001-08-23 20:04:33 +0000345 if not isinstance(value, StringType):
346 if not isinstance(value, TupleType):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000347 if value == 0:
348 value = time.time()
349 value = time.localtime(value)
350 value = time.strftime("%Y%m%dT%H:%M:%S", value)
351 self.value = value
352
353 def __cmp__(self, other):
354 if isinstance(other, DateTime):
355 other = other.value
356 return cmp(self.value, other)
357
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000358 ##
359 # Get date/time value.
360 #
361 # @return Date/time value, as an ISO 8601 string.
362
363 def __str__(self):
364 return self.value
365
Fredrik Lundhb9056332001-07-11 17:42:21 +0000366 def __repr__(self):
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000367 return "<DateTime %s at %x>" % (repr(self.value), id(self))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000368
369 def decode(self, data):
370 self.value = string.strip(data)
371
372 def encode(self, out):
373 out.write("<value><dateTime.iso8601>")
374 out.write(self.value)
375 out.write("</dateTime.iso8601></value>\n")
376
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000377def _datetime(data):
378 # decode xml element contents into a DateTime structure.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000379 value = DateTime()
380 value.decode(data)
381 return value
382
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000383##
384# Wrapper for binary data. This can be used to transport any kind
385# of binary data over XML-RPC, using BASE64 encoding.
386#
387# @param data An 8-bit string containing arbitrary data.
388
Fredrik Lundhb9056332001-07-11 17:42:21 +0000389class Binary:
Fred Drake1b410792001-09-04 18:55:03 +0000390 """Wrapper for binary data."""
Fredrik Lundhb9056332001-07-11 17:42:21 +0000391
392 def __init__(self, data=None):
393 self.data = data
394
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000395 ##
396 # Get buffer contents.
397 #
398 # @return Buffer contents, as an 8-bit string.
399
400 def __str__(self):
401 return self.data or ""
402
Fredrik Lundhb9056332001-07-11 17:42:21 +0000403 def __cmp__(self, other):
404 if isinstance(other, Binary):
405 other = other.data
406 return cmp(self.data, other)
407
408 def decode(self, data):
409 import base64
410 self.data = base64.decodestring(data)
411
412 def encode(self, out):
413 import base64, StringIO
414 out.write("<value><base64>\n")
415 base64.encode(StringIO.StringIO(self.data), out)
416 out.write("</base64></value>\n")
417
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000418def _binary(data):
419 # decode xml element contents into a Binary structure
Fredrik Lundhb9056332001-07-11 17:42:21 +0000420 value = Binary()
421 value.decode(data)
422 return value
423
Skip Montanaro9a7c96a2003-01-22 18:17:25 +0000424WRAPPERS = (DateTime, Binary)
425if not _bool_is_builtin:
426 WRAPPERS = WRAPPERS + (Boolean,)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000427
428# --------------------------------------------------------------------
429# XML parsers
430
431try:
432 # optional xmlrpclib accelerator. for more information on this
433 # component, contact info@pythonware.com
434 import _xmlrpclib
435 FastParser = _xmlrpclib.Parser
436 FastUnmarshaller = _xmlrpclib.Unmarshaller
437except (AttributeError, ImportError):
438 FastParser = FastUnmarshaller = None
439
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000440try:
441 import _xmlrpclib
442 FastMarshaller = _xmlrpclib.Marshaller
443except (AttributeError, ImportError):
444 FastMarshaller = None
445
Fredrik Lundhb9056332001-07-11 17:42:21 +0000446#
447# the SGMLOP parser is about 15x faster than Python's builtin
448# XML parser. SGMLOP sources can be downloaded from:
449#
450# http://www.pythonware.com/products/xml/sgmlop.htm
451#
452
453try:
454 import sgmlop
455 if not hasattr(sgmlop, "XMLParser"):
456 raise ImportError
457except ImportError:
458 SgmlopParser = None # sgmlop accelerator not available
459else:
460 class SgmlopParser:
461 def __init__(self, target):
462
463 # setup callbacks
464 self.finish_starttag = target.start
465 self.finish_endtag = target.end
466 self.handle_data = target.data
467 self.handle_xml = target.xml
468
469 # activate parser
470 self.parser = sgmlop.XMLParser()
471 self.parser.register(self)
472 self.feed = self.parser.feed
473 self.entity = {
474 "amp": "&", "gt": ">", "lt": "<",
475 "apos": "'", "quot": '"'
476 }
477
478 def close(self):
479 try:
480 self.parser.close()
481 finally:
482 self.parser = self.feed = None # nuke circular reference
483
484 def handle_proc(self, tag, attr):
485 m = re.search("encoding\s*=\s*['\"]([^\"']+)[\"']", attr)
486 if m:
487 self.handle_xml(m.group(1), 1)
488
489 def handle_entityref(self, entity):
490 # <string> entity
491 try:
492 self.handle_data(self.entity[entity])
493 except KeyError:
494 self.handle_data("&%s;" % entity)
495
496try:
497 from xml.parsers import expat
Guido van Rossumb8551342001-10-02 18:33:11 +0000498 if not hasattr(expat, "ParserCreate"):
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000499 raise ImportError
Fredrik Lundhb9056332001-07-11 17:42:21 +0000500except ImportError:
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000501 ExpatParser = None # expat not available
Fredrik Lundhb9056332001-07-11 17:42:21 +0000502else:
503 class ExpatParser:
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000504 # fast expat parser for Python 2.0 and later. this is about
505 # 50% slower than sgmlop, on roundtrip testing
Fredrik Lundhb9056332001-07-11 17:42:21 +0000506 def __init__(self, target):
507 self._parser = parser = expat.ParserCreate(None, None)
508 self._target = target
509 parser.StartElementHandler = target.start
510 parser.EndElementHandler = target.end
511 parser.CharacterDataHandler = target.data
512 encoding = None
513 if not parser.returns_unicode:
514 encoding = "utf-8"
515 target.xml(encoding, None)
516
517 def feed(self, data):
518 self._parser.Parse(data, 0)
519
520 def close(self):
521 self._parser.Parse("", 1) # end of data
522 del self._target, self._parser # get rid of circular references
523
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000524class SlowParser:
525 """Default XML parser (based on xmllib.XMLParser)."""
526 # this is about 10 times slower than sgmlop, on roundtrip
527 # testing.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000528 def __init__(self, target):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000529 import xmllib # lazy subclassing (!)
530 if xmllib.XMLParser not in SlowParser.__bases__:
531 SlowParser.__bases__ = (xmllib.XMLParser,)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000532 self.handle_xml = target.xml
533 self.unknown_starttag = target.start
534 self.handle_data = target.data
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000535 self.handle_cdata = target.data
Fredrik Lundhb9056332001-07-11 17:42:21 +0000536 self.unknown_endtag = target.end
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000537 try:
538 xmllib.XMLParser.__init__(self, accept_utf8=1)
539 except TypeError:
540 xmllib.XMLParser.__init__(self) # pre-2.0
Fredrik Lundhb9056332001-07-11 17:42:21 +0000541
542# --------------------------------------------------------------------
543# XML-RPC marshalling and unmarshalling code
544
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000545##
546# XML-RPC marshaller.
547#
548# @param encoding Default encoding for 8-bit strings. The default
549# value is None (interpreted as UTF-8).
550# @see dumps
551
Fredrik Lundhb9056332001-07-11 17:42:21 +0000552class Marshaller:
Fred Drake1b410792001-09-04 18:55:03 +0000553 """Generate an XML-RPC params chunk from a Python data structure.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000554
Fredrik Lundhc4c062f2001-09-10 19:45:02 +0000555 Create a Marshaller instance for each set of parameters, and use
556 the "dumps" method to convert your data (represented as a tuple)
557 to an XML-RPC params chunk. To write a fault response, pass a
558 Fault instance instead. You may prefer to use the "dumps" module
559 function for this purpose.
Fred Drake1b410792001-09-04 18:55:03 +0000560 """
Fredrik Lundhb9056332001-07-11 17:42:21 +0000561
562 # by the way, if you don't understand what's going on in here,
563 # that's perfectly ok.
564
565 def __init__(self, encoding=None):
566 self.memo = {}
567 self.data = None
568 self.encoding = encoding
569
570 dispatch = {}
571
572 def dumps(self, values):
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000573 out = []
574 write = out.append
575 dump = self.__dump
Fredrik Lundhb9056332001-07-11 17:42:21 +0000576 if isinstance(values, Fault):
577 # fault instance
578 write("<fault>\n")
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000579 dump(vars(values), write)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000580 write("</fault>\n")
581 else:
582 # parameter block
Fredrik Lundhc266bb02001-08-23 20:13:08 +0000583 # FIXME: the xml-rpc specification allows us to leave out
584 # the entire <params> block if there are no parameters.
585 # however, changing this may break older code (including
586 # old versions of xmlrpclib.py), so this is better left as
587 # is for now. See @XMLRPC3 for more information. /F
Fredrik Lundhb9056332001-07-11 17:42:21 +0000588 write("<params>\n")
589 for v in values:
590 write("<param>\n")
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000591 dump(v, write)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000592 write("</param>\n")
593 write("</params>\n")
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000594 result = string.join(out, "")
Fredrik Lundhb9056332001-07-11 17:42:21 +0000595 return result
596
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000597 def __dump(self, value, write):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000598 try:
599 f = self.dispatch[type(value)]
600 except KeyError:
601 raise TypeError, "cannot marshal %s objects" % type(value)
602 else:
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000603 f(self, value, write)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000604
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000605 def dump_int(self, value, write):
Skip Montanaro5449e082001-10-17 22:53:33 +0000606 # in case ints are > 32 bits
607 if value > MAXINT or value < MININT:
608 raise OverflowError, "int exceeds XML-RPC limits"
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000609 write("<value><int>")
610 write(str(value))
611 write("</int></value>\n")
Fredrik Lundhb9056332001-07-11 17:42:21 +0000612 dispatch[IntType] = dump_int
613
Skip Montanaro9a7c96a2003-01-22 18:17:25 +0000614 if _bool_is_builtin:
615 def dump_bool(self, value, write):
616 write("<value><boolean>")
617 write(value and "1" or "0")
618 write("</boolean></value>\n")
619 dispatch[bool] = dump_bool
620
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000621 def dump_long(self, value, write):
Skip Montanaro5449e082001-10-17 22:53:33 +0000622 if value > MAXINT or value < MININT:
623 raise OverflowError, "long int exceeds XML-RPC limits"
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000624 write("<value><int>")
625 write(str(int(value)))
626 write("</int></value>\n")
Skip Montanaro5e9c71b2001-10-10 15:56:34 +0000627 dispatch[LongType] = dump_long
628
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000629 def dump_double(self, value, write):
630 write("<value><double>")
631 write(repr(value))
632 write("</double></value>\n")
Fredrik Lundhb9056332001-07-11 17:42:21 +0000633 dispatch[FloatType] = dump_double
634
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000635 def dump_string(self, value, write, escape=escape):
636 write("<value><string>")
637 write(escape(value))
638 write("</string></value>\n")
Fredrik Lundhb9056332001-07-11 17:42:21 +0000639 dispatch[StringType] = dump_string
640
641 if unicode:
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000642 def dump_unicode(self, value, write, escape=escape):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000643 value = value.encode(self.encoding)
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000644 write("<value><string>")
645 write(escape(value))
646 write("</string></value>\n")
Fredrik Lundhb9056332001-07-11 17:42:21 +0000647 dispatch[UnicodeType] = dump_unicode
648
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000649 def dump_array(self, value, write):
650 i = id(value)
651 if self.memo.has_key(i):
652 raise TypeError, "cannot marshal recursive sequences"
653 self.memo[i] = None
Fredrik Lundh1538c232001-10-01 19:42:03 +0000654 dump = self.__dump
Fredrik Lundhb9056332001-07-11 17:42:21 +0000655 write("<value><array><data>\n")
656 for v in value:
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000657 dump(v, write)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000658 write("</data></array></value>\n")
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000659 del self.memo[i]
Fredrik Lundhb9056332001-07-11 17:42:21 +0000660 dispatch[TupleType] = dump_array
661 dispatch[ListType] = dump_array
662
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000663 def dump_struct(self, value, write, escape=escape):
664 i = id(value)
665 if self.memo.has_key(i):
666 raise TypeError, "cannot marshal recursive dictionaries"
667 self.memo[i] = None
Fredrik Lundh1538c232001-10-01 19:42:03 +0000668 dump = self.__dump
Fredrik Lundhb9056332001-07-11 17:42:21 +0000669 write("<value><struct>\n")
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000670 for k in value.keys():
Fredrik Lundhb9056332001-07-11 17:42:21 +0000671 write("<member>\n")
672 if type(k) is not StringType:
673 raise TypeError, "dictionary key must be string"
Fredrik Lundh1538c232001-10-01 19:42:03 +0000674 write("<name>%s</name>\n" % escape(k))
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000675 dump(value[k], write)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000676 write("</member>\n")
677 write("</struct></value>\n")
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000678 del self.memo[i]
Fredrik Lundhb9056332001-07-11 17:42:21 +0000679 dispatch[DictType] = dump_struct
680
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000681 def dump_instance(self, value, write):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000682 # check for special wrappers
683 if value.__class__ in WRAPPERS:
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000684 self.write = write
Fredrik Lundhb9056332001-07-11 17:42:21 +0000685 value.encode(self)
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000686 del self.write
Fredrik Lundhb9056332001-07-11 17:42:21 +0000687 else:
688 # store instance attributes as a struct (really?)
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000689 self.dump_struct(value.__dict__, write)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000690 dispatch[InstanceType] = dump_instance
691
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000692##
693# XML-RPC unmarshaller.
694#
695# @see loads
696
Fredrik Lundhb9056332001-07-11 17:42:21 +0000697class Unmarshaller:
Fred Drake1b410792001-09-04 18:55:03 +0000698 """Unmarshal an XML-RPC response, based on incoming XML event
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000699 messages (start, data, end). Call close() to get the resulting
Fred Drake1b410792001-09-04 18:55:03 +0000700 data structure.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000701
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000702 Note that this reader is fairly tolerant, and gladly accepts bogus
703 XML-RPC data without complaining (but not bogus XML).
Fred Drake1b410792001-09-04 18:55:03 +0000704 """
Fredrik Lundhb9056332001-07-11 17:42:21 +0000705
706 # and again, if you don't understand what's going on in here,
707 # that's perfectly ok.
708
709 def __init__(self):
710 self._type = None
711 self._stack = []
712 self._marks = []
713 self._data = []
714 self._methodname = None
715 self._encoding = "utf-8"
716 self.append = self._stack.append
717
718 def close(self):
719 # return response tuple and target method
720 if self._type is None or self._marks:
721 raise ResponseError()
722 if self._type == "fault":
723 raise apply(Fault, (), self._stack[0])
724 return tuple(self._stack)
725
726 def getmethodname(self):
727 return self._methodname
728
729 #
730 # event handlers
731
732 def xml(self, encoding, standalone):
733 self._encoding = encoding
734 # FIXME: assert standalone == 1 ???
735
736 def start(self, tag, attrs):
737 # prepare to handle this element
738 if tag == "array" or tag == "struct":
739 self._marks.append(len(self._stack))
740 self._data = []
741 self._value = (tag == "value")
742
743 def data(self, text):
744 self._data.append(text)
745
Fredrik Lundh1538c232001-10-01 19:42:03 +0000746 def end(self, tag, join=string.join):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000747 # call the appropriate end tag handler
748 try:
749 f = self.dispatch[tag]
750 except KeyError:
751 pass # unknown tag ?
752 else:
Fredrik Lundh1538c232001-10-01 19:42:03 +0000753 return f(self, join(self._data, ""))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000754
755 #
756 # accelerator support
757
758 def end_dispatch(self, tag, data):
759 # dispatch data
760 try:
761 f = self.dispatch[tag]
762 except KeyError:
763 pass # unknown tag ?
764 else:
765 return f(self, data)
766
767 #
768 # element decoders
769
770 dispatch = {}
771
Fredrik Lundh1538c232001-10-01 19:42:03 +0000772 def end_boolean(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000773 if data == "0":
774 self.append(False)
775 elif data == "1":
776 self.append(True)
777 else:
778 raise TypeError, "bad boolean value"
779 self._value = 0
780 dispatch["boolean"] = end_boolean
781
Fredrik Lundh1538c232001-10-01 19:42:03 +0000782 def end_int(self, data):
783 self.append(int(data))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000784 self._value = 0
785 dispatch["i4"] = end_int
786 dispatch["int"] = end_int
787
Fredrik Lundh1538c232001-10-01 19:42:03 +0000788 def end_double(self, data):
789 self.append(float(data))
Fredrik Lundhb9056332001-07-11 17:42:21 +0000790 self._value = 0
791 dispatch["double"] = end_double
792
Fredrik Lundh1538c232001-10-01 19:42:03 +0000793 def end_string(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000794 if self._encoding:
795 data = _decode(data, self._encoding)
796 self.append(_stringify(data))
797 self._value = 0
798 dispatch["string"] = end_string
799 dispatch["name"] = end_string # struct keys are always strings
800
801 def end_array(self, data):
Raymond Hettinger46ac8eb2002-06-30 03:39:14 +0000802 mark = self._marks.pop()
Fredrik Lundhb9056332001-07-11 17:42:21 +0000803 # map arrays to Python lists
804 self._stack[mark:] = [self._stack[mark:]]
805 self._value = 0
806 dispatch["array"] = end_array
807
808 def end_struct(self, data):
Raymond Hettinger46ac8eb2002-06-30 03:39:14 +0000809 mark = self._marks.pop()
Fredrik Lundhb9056332001-07-11 17:42:21 +0000810 # map structs to Python dictionaries
811 dict = {}
812 items = self._stack[mark:]
813 for i in range(0, len(items), 2):
814 dict[_stringify(items[i])] = items[i+1]
815 self._stack[mark:] = [dict]
816 self._value = 0
817 dispatch["struct"] = end_struct
818
Fredrik Lundh1538c232001-10-01 19:42:03 +0000819 def end_base64(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000820 value = Binary()
Fredrik Lundh1538c232001-10-01 19:42:03 +0000821 value.decode(data)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000822 self.append(value)
823 self._value = 0
824 dispatch["base64"] = end_base64
825
Fredrik Lundh1538c232001-10-01 19:42:03 +0000826 def end_dateTime(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000827 value = DateTime()
Fredrik Lundh1538c232001-10-01 19:42:03 +0000828 value.decode(data)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000829 self.append(value)
830 dispatch["dateTime.iso8601"] = end_dateTime
831
832 def end_value(self, data):
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000833 # if we stumble upon a value element with no internal
Fredrik Lundhb9056332001-07-11 17:42:21 +0000834 # elements, treat it as a string element
835 if self._value:
836 self.end_string(data)
837 dispatch["value"] = end_value
838
839 def end_params(self, data):
840 self._type = "params"
841 dispatch["params"] = end_params
842
843 def end_fault(self, data):
844 self._type = "fault"
845 dispatch["fault"] = end_fault
846
Fredrik Lundh1538c232001-10-01 19:42:03 +0000847 def end_methodName(self, data):
Fredrik Lundhb9056332001-07-11 17:42:21 +0000848 if self._encoding:
849 data = _decode(data, self._encoding)
850 self._methodname = data
851 self._type = "methodName" # no params
852 dispatch["methodName"] = end_methodName
853
854
855# --------------------------------------------------------------------
856# convenience functions
857
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000858##
859# Create a parser object, and connect it to an unmarshalling instance.
860# This function picks the fastest available XML parser.
861#
862# return A (parser, unmarshaller) tuple.
863
Fredrik Lundhb9056332001-07-11 17:42:21 +0000864def getparser():
865 """getparser() -> parser, unmarshaller
866
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000867 Create an instance of the fastest available parser, and attach it
868 to an unmarshalling object. Return both objects.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000869 """
870 if FastParser and FastUnmarshaller:
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000871 target = FastUnmarshaller(True, False, _binary, _datetime, Fault)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000872 parser = FastParser(target)
873 else:
874 target = Unmarshaller()
875 if FastParser:
876 parser = FastParser(target)
877 elif SgmlopParser:
878 parser = SgmlopParser(target)
879 elif ExpatParser:
880 parser = ExpatParser(target)
881 else:
882 parser = SlowParser(target)
883 return parser, target
884
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000885##
886# Convert a Python tuple or a Fault instance to an XML-RPC packet.
887#
888# @def dumps(params, **options)
889# @param params A tuple or Fault instance.
890# @keyparam methodname If given, create a methodCall request for
891# this method name.
892# @keyparam methodresponse If given, create a methodResponse packet.
893# If used with a tuple, the tuple must be a singleton (that is,
894# it must contain exactly one element).
895# @keyparam encoding The packet encoding.
896# @return A string containing marshalled data.
897
Fredrik Lundhb9056332001-07-11 17:42:21 +0000898def dumps(params, methodname=None, methodresponse=None, encoding=None):
899 """data [,options] -> marshalled data
900
901 Convert an argument tuple or a Fault instance to an XML-RPC
902 request (or response, if the methodresponse option is used).
903
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000904 In addition to the data object, the following options can be given
905 as keyword arguments:
Fredrik Lundhb9056332001-07-11 17:42:21 +0000906
907 methodname: the method name for a methodCall packet
908
909 methodresponse: true to create a methodResponse packet.
910 If this option is used with a tuple, the tuple must be
911 a singleton (i.e. it can contain only one element).
912
913 encoding: the packet encoding (default is UTF-8)
914
915 All 8-bit strings in the data structure are assumed to use the
916 packet encoding. Unicode strings are automatically converted,
Fredrik Lundhb0e8e9b2001-09-10 21:45:42 +0000917 where necessary.
Fredrik Lundhb9056332001-07-11 17:42:21 +0000918 """
919
920 assert isinstance(params, TupleType) or isinstance(params, Fault),\
921 "argument must be tuple or Fault instance"
922
923 if isinstance(params, Fault):
924 methodresponse = 1
925 elif methodresponse and isinstance(params, TupleType):
926 assert len(params) == 1, "response tuple must be a singleton"
927
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000928 if not encoding:
Fredrik Lundhb9056332001-07-11 17:42:21 +0000929 encoding = "utf-8"
930
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000931 if FastMarshaller:
932 m = FastMarshaller(encoding)
933 else:
934 m = Marshaller(encoding)
935
Fredrik Lundhb9056332001-07-11 17:42:21 +0000936 data = m.dumps(params)
937
938 if encoding != "utf-8":
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000939 xmlheader = "<?xml version='1.0' encoding='%s'?>\n" % str(encoding)
Fredrik Lundhb9056332001-07-11 17:42:21 +0000940 else:
941 xmlheader = "<?xml version='1.0'?>\n" # utf-8 is default
942
943 # standard XML-RPC wrappings
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000944 if methodname:
Fredrik Lundhb9056332001-07-11 17:42:21 +0000945 # a method call
946 if not isinstance(methodname, StringType):
947 methodname = methodname.encode(encoding)
948 data = (
949 xmlheader,
950 "<methodCall>\n"
951 "<methodName>", methodname, "</methodName>\n",
952 data,
953 "</methodCall>\n"
954 )
955 elif methodresponse:
956 # a method response, or a fault structure
957 data = (
958 xmlheader,
959 "<methodResponse>\n",
960 data,
961 "</methodResponse>\n"
962 )
963 else:
964 return data # return as is
965 return string.join(data, "")
966
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000967##
968# Convert an XML-RPC packet to a Python object. If the XML-RPC packet
969# represents a fault condition, this function raises a Fault exception.
970#
971# @param data An XML-RPC packet, given as an 8-bit string.
972# @return A tuple containing the the unpacked data, and the method name
973# (None if not present).
974# @see Fault
975
Fredrik Lundhb9056332001-07-11 17:42:21 +0000976def loads(data):
977 """data -> unmarshalled data, method name
978
979 Convert an XML-RPC packet to unmarshalled data plus a method
980 name (None if not present).
981
982 If the XML-RPC packet represents a fault condition, this function
983 raises a Fault exception.
984 """
Fredrik Lundh3d9addd2002-06-27 21:36:21 +0000985 import sys
Fredrik Lundhb9056332001-07-11 17:42:21 +0000986 p, u = getparser()
987 p.feed(data)
988 p.close()
989 return u.close(), u.getmethodname()
990
991
992# --------------------------------------------------------------------
993# request dispatcher
994
995class _Method:
996 # some magic to bind an XML-RPC method to an RPC server.
997 # supports "nested" methods (e.g. examples.getStateName)
998 def __init__(self, send, name):
999 self.__send = send
1000 self.__name = name
1001 def __getattr__(self, name):
1002 return _Method(self.__send, "%s.%s" % (self.__name, name))
1003 def __call__(self, *args):
1004 return self.__send(self.__name, args)
1005
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001006##
1007# Standard transport class for XML-RPC over HTTP.
1008# <p>
1009# You can create custom transports by subclassing this method, and
1010# overriding selected methods.
Fredrik Lundhb9056332001-07-11 17:42:21 +00001011
1012class Transport:
Fredrik Lundhc4c062f2001-09-10 19:45:02 +00001013 """Handles an HTTP transaction to an XML-RPC server."""
Fredrik Lundhb9056332001-07-11 17:42:21 +00001014
1015 # client identifier (may be overridden)
1016 user_agent = "xmlrpclib.py/%s (by www.pythonware.com)" % __version__
1017
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001018 ##
1019 # Send a complete request, and parse the response.
1020 #
1021 # @param host Target host.
1022 # @param handler Target PRC handler.
1023 # @param request_body XML-RPC request body.
1024 # @param verbose Debugging flag.
1025 # @return Parsed response.
1026
Fredrik Lundhb9056332001-07-11 17:42:21 +00001027 def request(self, host, handler, request_body, verbose=0):
1028 # issue XML-RPC request
1029
1030 h = self.make_connection(host)
1031 if verbose:
1032 h.set_debuglevel(1)
1033
1034 self.send_request(h, handler, request_body)
1035 self.send_host(h, host)
1036 self.send_user_agent(h)
1037 self.send_content(h, request_body)
1038
1039 errcode, errmsg, headers = h.getreply()
1040
1041 if errcode != 200:
1042 raise ProtocolError(
1043 host + handler,
1044 errcode, errmsg,
1045 headers
1046 )
1047
1048 self.verbose = verbose
1049
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001050 try:
1051 sock = h._conn.sock
1052 except AttributeError:
1053 sock = None
1054
1055 return self._parse_response(h.getfile(), sock)
1056
1057 ##
1058 # Create parser.
1059 #
1060 # @return A 2-tuple containing a parser and a unmarshaller.
Fredrik Lundhb9056332001-07-11 17:42:21 +00001061
Fredrik Lundhc4c062f2001-09-10 19:45:02 +00001062 def getparser(self):
1063 # get parser and unmarshaller
1064 return getparser()
1065
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001066 ##
Fredrik Lundh1303c7c2002-10-22 18:23:00 +00001067 # Get authorization info from host parameter
1068 # Host may be a string, or a (host, x509-dict) tuple; if a string,
1069 # it is checked for a "user:pw@host" format, and a "Basic
1070 # Authentication" header is added if appropriate.
1071 #
1072 # @param host Host descriptor (URL or (URL, x509 info) tuple).
1073 # @return A 3-tuple containing (actual host, extra headers,
1074 # x509 info). The header and x509 fields may be None.
1075
1076 def get_host_info(self, host):
1077
1078 x509 = {}
1079 if isinstance(host, TupleType):
1080 host, x509 = host
1081
1082 import urllib
1083 auth, host = urllib.splituser(host)
1084
1085 if auth:
1086 import base64
Fredrik Lundh768c98b2002-11-01 17:14:16 +00001087 auth = base64.encodestring(urllib.unquote(auth))
Fredrik Lundh1303c7c2002-10-22 18:23:00 +00001088 auth = string.join(string.split(auth), "") # get rid of whitespace
1089 extra_headers = [
1090 ("Authorization", "Basic " + auth)
1091 ]
1092 else:
1093 extra_headers = None
1094
1095 return host, extra_headers, x509
1096
1097 ##
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001098 # Connect to server.
1099 #
1100 # @param host Target host.
1101 # @return A connection handle.
1102
Fredrik Lundhb9056332001-07-11 17:42:21 +00001103 def make_connection(self, host):
1104 # create a HTTP connection object from a host descriptor
1105 import httplib
Fredrik Lundh1303c7c2002-10-22 18:23:00 +00001106 host, extra_headers, x509 = self.get_host_info(host)
Fredrik Lundhb9056332001-07-11 17:42:21 +00001107 return httplib.HTTP(host)
1108
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001109 ##
1110 # Send request header.
1111 #
1112 # @param connection Connection handle.
1113 # @param handler Target RPC handler.
1114 # @param request_body XML-RPC body.
1115
Fredrik Lundhb9056332001-07-11 17:42:21 +00001116 def send_request(self, connection, handler, request_body):
1117 connection.putrequest("POST", handler)
1118
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001119 ##
1120 # Send host name.
1121 #
1122 # @param connection Connection handle.
1123 # @param host Host name.
1124
Fredrik Lundhb9056332001-07-11 17:42:21 +00001125 def send_host(self, connection, host):
Fredrik Lundh1303c7c2002-10-22 18:23:00 +00001126 host, extra_headers, x509 = self.get_host_info(host)
Fredrik Lundhb9056332001-07-11 17:42:21 +00001127 connection.putheader("Host", host)
Fredrik Lundh1303c7c2002-10-22 18:23:00 +00001128 if extra_headers:
1129 if isinstance(extra_headers, DictType):
1130 extra_headers = extra_headers.items()
1131 for key, value in extra_headers:
1132 connection.putheader(key, value)
Fredrik Lundhb9056332001-07-11 17:42:21 +00001133
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001134 ##
1135 # Send user-agent identifier.
1136 #
1137 # @param connection Connection handle.
1138
Fredrik Lundhb9056332001-07-11 17:42:21 +00001139 def send_user_agent(self, connection):
1140 connection.putheader("User-Agent", self.user_agent)
1141
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001142 ##
1143 # Send request body.
1144 #
1145 # @param connection Connection handle.
1146 # @param request_body XML-RPC request body.
1147
Fredrik Lundhb9056332001-07-11 17:42:21 +00001148 def send_content(self, connection, request_body):
1149 connection.putheader("Content-Type", "text/xml")
1150 connection.putheader("Content-Length", str(len(request_body)))
1151 connection.endheaders()
1152 if request_body:
1153 connection.send(request_body)
1154
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001155 ##
1156 # Parse response.
1157 #
1158 # @param file Stream.
1159 # @return Response tuple and target method.
1160
1161 def parse_response(self, file):
1162 # compatibility interface
1163 return self._parse_response(file, None)
1164
1165 ##
1166 # Parse response (alternate interface). This is similar to the
1167 # parse_response method, but also provides direct access to the
1168 # underlying socket object (where available).
1169 #
1170 # @param file Stream.
1171 # @param sock Socket handle (or None, if the socket object
1172 # could not be accessed).
1173 # @return Response tuple and target method.
1174
1175 def _parse_response(self, file, sock):
1176 # read response from input file/socket, and parse it
Fredrik Lundhb9056332001-07-11 17:42:21 +00001177
Fredrik Lundhc4c062f2001-09-10 19:45:02 +00001178 p, u = self.getparser()
Fredrik Lundhb9056332001-07-11 17:42:21 +00001179
1180 while 1:
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001181 if sock:
1182 response = sock.recv(1024)
1183 else:
1184 response = file.read(1024)
Fredrik Lundhb9056332001-07-11 17:42:21 +00001185 if not response:
1186 break
1187 if self.verbose:
1188 print "body:", repr(response)
1189 p.feed(response)
1190
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001191 file.close()
Fredrik Lundhb9056332001-07-11 17:42:21 +00001192 p.close()
1193
1194 return u.close()
1195
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001196##
1197# Standard transport class for XML-RPC over HTTPS.
1198
Fredrik Lundhb9056332001-07-11 17:42:21 +00001199class SafeTransport(Transport):
Fredrik Lundhc4c062f2001-09-10 19:45:02 +00001200 """Handles an HTTPS transaction to an XML-RPC server."""
Fredrik Lundhb9056332001-07-11 17:42:21 +00001201
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001202 # FIXME: mostly untested
1203
Fredrik Lundhb9056332001-07-11 17:42:21 +00001204 def make_connection(self, host):
1205 # create a HTTPS connection object from a host descriptor
1206 # host may be a string, or a (host, x509-dict) tuple
1207 import httplib
Fredrik Lundh1303c7c2002-10-22 18:23:00 +00001208 host, extra_headers, x509 = self.get_host_info(host)
Fredrik Lundhb9056332001-07-11 17:42:21 +00001209 try:
1210 HTTPS = httplib.HTTPS
1211 except AttributeError:
Fredrik Lundh1303c7c2002-10-22 18:23:00 +00001212 raise NotImplementedError(
1213 "your version of httplib doesn't support HTTPS"
1214 )
Fredrik Lundhb9056332001-07-11 17:42:21 +00001215 else:
Fredrik Lundh1303c7c2002-10-22 18:23:00 +00001216 return apply(HTTPS, (host, None), x509 or {})
Fredrik Lundhb9056332001-07-11 17:42:21 +00001217
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001218##
1219# Standard server proxy. This class establishes a virtual connection
1220# to an XML-RPC server.
1221# <p>
1222# This class is available as ServerProxy and Server. New code should
1223# use ServerProxy, to avoid confusion.
1224#
1225# @def ServerProxy(uri, **options)
1226# @param uri The connection point on the server.
1227# @keyparam transport A transport factory, compatible with the
1228# standard transport class.
1229# @keyparam encoding The default encoding used for 8-bit strings
1230# (default is UTF-8).
1231# @keyparam verbose Use a true value to enable debugging output.
1232# (printed to standard output).
1233# @see Transport
1234
Fredrik Lundhb9056332001-07-11 17:42:21 +00001235class ServerProxy:
1236 """uri [,options] -> a logical connection to an XML-RPC server
1237
1238 uri is the connection point on the server, given as
1239 scheme://host/target.
1240
1241 The standard implementation always supports the "http" scheme. If
1242 SSL socket support is available (Python 2.0), it also supports
1243 "https".
1244
1245 If the target part and the slash preceding it are both omitted,
1246 "/RPC2" is assumed.
1247
1248 The following options can be given as keyword arguments:
1249
1250 transport: a transport factory
1251 encoding: the request encoding (default is UTF-8)
1252
1253 All 8-bit strings passed to the server proxy are assumed to use
1254 the given encoding.
1255 """
1256
1257 def __init__(self, uri, transport=None, encoding=None, verbose=0):
1258 # establish a "logical" server connection
1259
1260 # get the url
Fredrik Lundhc4c062f2001-09-10 19:45:02 +00001261 import urllib
Fredrik Lundhb9056332001-07-11 17:42:21 +00001262 type, uri = urllib.splittype(uri)
1263 if type not in ("http", "https"):
1264 raise IOError, "unsupported XML-RPC protocol"
1265 self.__host, self.__handler = urllib.splithost(uri)
1266 if not self.__handler:
1267 self.__handler = "/RPC2"
1268
1269 if transport is None:
1270 if type == "https":
1271 transport = SafeTransport()
1272 else:
1273 transport = Transport()
1274 self.__transport = transport
1275
1276 self.__encoding = encoding
1277 self.__verbose = verbose
1278
1279 def __request(self, methodname, params):
1280 # call a method on the remote server
1281
1282 request = dumps(params, methodname, encoding=self.__encoding)
1283
1284 response = self.__transport.request(
1285 self.__host,
1286 self.__handler,
1287 request,
1288 verbose=self.__verbose
1289 )
1290
1291 if len(response) == 1:
1292 response = response[0]
1293
1294 return response
1295
1296 def __repr__(self):
1297 return (
Fredrik Lundh78eedce2001-08-23 20:04:33 +00001298 "<ServerProxy for %s%s>" %
Fredrik Lundhb9056332001-07-11 17:42:21 +00001299 (self.__host, self.__handler)
1300 )
1301
1302 __str__ = __repr__
1303
1304 def __getattr__(self, name):
1305 # magic method dispatcher
1306 return _Method(self.__request, name)
1307
1308 # note: to call a remote object with an non-standard name, use
1309 # result getattr(server, "strange-python-name")(args)
1310
Fredrik Lundh78eedce2001-08-23 20:04:33 +00001311# compatibility
Fredrik Lundh3d9addd2002-06-27 21:36:21 +00001312
Fredrik Lundhb9056332001-07-11 17:42:21 +00001313Server = ServerProxy
1314
1315# --------------------------------------------------------------------
1316# test code
1317
1318if __name__ == "__main__":
1319
1320 # simple test program (from the XML-RPC specification)
1321
Fredrik Lundh78eedce2001-08-23 20:04:33 +00001322 # server = ServerProxy("http://localhost:8000") # local server
1323 server = ServerProxy("http://betty.userland.com")
Fredrik Lundhb9056332001-07-11 17:42:21 +00001324
1325 print server
1326
1327 try:
1328 print server.examples.getStateName(41)
1329 except Error, v:
1330 print "ERROR", v