183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh#
283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# XML-RPC CLIENT LIBRARY
383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# $Id$
483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh#
583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# an XML-RPC client interface for Python.
683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh#
783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# the marshalling and response parser code can also be used to
883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# implement XML-RPC servers.
983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh#
1083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Notes:
1183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# this version is designed to work with Python 2.1 or newer.
1283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh#
1383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# History:
1483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# 1999-01-14 fl  Created
1583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# 1999-01-15 fl  Changed dateTime to use localtime
1683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# 1999-01-16 fl  Added Binary/base64 element, default to RPC2 service
1783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# 1999-01-19 fl  Fixed array data element (from Skip Montanaro)
1883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# 1999-01-21 fl  Fixed dateTime constructor, etc.
1983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# 1999-02-02 fl  Added fault handling, handle empty sequences, etc.
2083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# 1999-02-10 fl  Fixed problem with empty responses (from Skip Montanaro)
2183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# 1999-06-20 fl  Speed improvements, pluggable parsers/transports (0.9.8)
2283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# 2000-11-28 fl  Changed boolean to check the truth value of its argument
2383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# 2001-02-24 fl  Added encoding/Unicode/SafeTransport patches
2483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# 2001-02-26 fl  Added compare support to wrappers (0.9.9/1.0b1)
2583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# 2001-03-28 fl  Make sure response tuple is a singleton
2683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# 2001-03-29 fl  Don't require empty params element (from Nicholas Riley)
2783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# 2001-06-10 fl  Folded in _xmlrpclib accelerator support (1.0b2)
2883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# 2001-08-20 fl  Base xmlrpclib.Error on built-in Exception (from Paul Prescod)
2983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# 2001-09-03 fl  Allow Transport subclass to override getparser
3083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# 2001-09-10 fl  Lazy import of urllib, cgi, xmllib (20x import speedup)
3183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# 2001-10-01 fl  Remove containers from memo cache when done with them
3283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# 2001-10-01 fl  Use faster escape method (80% dumps speedup)
3383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# 2001-10-02 fl  More dumps microtuning
3483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# 2001-10-04 fl  Make sure import expat gets a parser (from Guido van Rossum)
3583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# 2001-10-10 sm  Allow long ints to be passed as ints if they don't overflow
3683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# 2001-10-17 sm  Test for int and long overflow (allows use on 64-bit systems)
3783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# 2001-11-12 fl  Use repr() to marshal doubles (from Paul Felix)
3883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# 2002-03-17 fl  Avoid buffered read when possible (from James Rucker)
3983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# 2002-04-07 fl  Added pythondoc comments
4083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# 2002-04-16 fl  Added __str__ methods to datetime/binary wrappers
4183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# 2002-05-15 fl  Added error constants (from Andrew Kuchling)
4283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# 2002-06-27 fl  Merged with Python CVS version
4383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# 2002-10-22 fl  Added basic authentication (based on code from Phillip Eby)
4483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# 2003-01-22 sm  Add support for the bool type
4583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# 2003-02-27 gvr Remove apply calls
4683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# 2003-04-24 sm  Use cStringIO if available
4783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# 2003-04-25 ak  Add support for nil
4883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# 2003-06-15 gn  Add support for time.struct_time
4983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# 2003-07-12 gp  Correct marshalling of Faults
5083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# 2003-10-31 mvl Add multicall support
5183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# 2004-08-20 mvl Bump minimum supported Python version to 2.1
5283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh#
5383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Copyright (c) 1999-2002 by Secret Labs AB.
5483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Copyright (c) 1999-2002 by Fredrik Lundh.
5583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh#
5683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# info@pythonware.com
5783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# http://www.pythonware.com
5883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh#
5983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# --------------------------------------------------------------------
6083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# The XML-RPC client interface is
6183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh#
6283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Copyright (c) 1999-2002 by Secret Labs AB
6383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Copyright (c) 1999-2002 by Fredrik Lundh
6483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh#
6583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# By obtaining, using, and/or copying this software and/or its
6683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# associated documentation, you agree that you have read, understood,
6783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# and will comply with the following terms and conditions:
6883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh#
6983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Permission to use, copy, modify, and distribute this software and
7083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# its associated documentation for any purpose and without fee is
7183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# hereby granted, provided that the above copyright notice appears in
7283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# all copies, and that both that copyright notice and this permission
7383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# notice appear in supporting documentation, and that the name of
7483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Secret Labs AB or the author not be used in advertising or publicity
7583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# pertaining to distribution of the software without specific, written
7683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# prior permission.
7783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh#
7883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
7983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
8083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# ABILITY AND FITNESS.  IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR
8183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
8283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
8383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
8483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
8583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# OF THIS SOFTWARE.
8683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# --------------------------------------------------------------------
8783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
8883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh#
8983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# things to look into some day:
9083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
9183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# TODO: sort out True/False/boolean issues for Python 2.3
9283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
9383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh"""
9483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehAn XML-RPC client interface for Python.
9583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
9683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehThe marshalling and response parser code can also be used to
9783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehimplement XML-RPC servers.
9883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
9983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehExported exceptions:
10083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
10183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  Error          Base class for client errors
10283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  ProtocolError  Indicates an HTTP protocol error
10383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  ResponseError  Indicates a broken response package
10483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  Fault          Indicates an XML-RPC fault package
10583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
10683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehExported classes:
10783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
10883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  ServerProxy    Represents a logical connection to an XML-RPC server
10983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
11083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  MultiCall      Executor of boxcared xmlrpc requests
11183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  Boolean        boolean wrapper to generate a "boolean" XML-RPC value
11283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  DateTime       dateTime wrapper for an ISO 8601 string or time tuple or
11383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                 localtime integer value to generate a "dateTime.iso8601"
11483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                 XML-RPC value
11583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  Binary         binary data wrapper
11683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
11783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  SlowParser     Slow but safe standard parser (based on xmllib)
11883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  Marshaller     Generate an XML-RPC params chunk from a Python data structure
11983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  Unmarshaller   Unmarshal an XML-RPC response from incoming XML event message
12083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  Transport      Handles an HTTP transaction to an XML-RPC server
12183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  SafeTransport  Handles an HTTPS transaction to an XML-RPC server
12283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
12383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehExported constants:
12483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
12583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  True
12683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  False
12783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
12883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehExported functions:
12983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
13083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  boolean        Convert any Python value to an XML-RPC boolean
13183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  getparser      Create instance of the fastest available parser & attach
13283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                 to an unmarshalling object
13383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  dumps          Convert an argument tuple or a Fault instance to an XML-RPC
13483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                 request (or response, if the methodresponse option is used).
13583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh  loads          Convert an XML-RPC packet to unmarshalled data plus a method
13683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                 name (None if not present).
13783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh"""
13883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
13983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehimport re, string, time, operator
14083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
14183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehfrom types import *
14283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehimport socket
14383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehimport errno
14483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehimport httplib
14583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehtry:
14683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    import gzip
14783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehexcept ImportError:
14883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    gzip = None #python can be built without zlib/gzip support
14983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
15083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# --------------------------------------------------------------------
15183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Internal stuff
15283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
15383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehtry:
15483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    unicode
15583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehexcept NameError:
15683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    unicode = None # unicode support not available
15783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
15883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehtry:
15983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    import datetime
16083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehexcept ImportError:
16183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    datetime = None
16283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
16383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehtry:
16483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    _bool_is_builtin = False.__class__.__name__ == "bool"
16583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehexcept NameError:
16683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    _bool_is_builtin = 0
16783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
16883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef _decode(data, encoding, is8bit=re.compile("[\x80-\xff]").search):
16983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # decode non-ascii string (if possible)
17083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if unicode and encoding and is8bit(data):
17183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        data = unicode(data, encoding)
17283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    return data
17383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
17483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef escape(s, replace=string.replace):
17583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    s = replace(s, "&", "&")
17683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    s = replace(s, "<", "&lt;")
17783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    return replace(s, ">", "&gt;",)
17883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
17983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehif unicode:
18083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def _stringify(string):
18183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # convert to 7-bit ascii if possible
18283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        try:
18383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            return string.encode("ascii")
18483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        except UnicodeError:
18583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            return string
18683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehelse:
18783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def _stringify(string):
18883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return string
18983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
19083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh__version__ = "1.0.1"
19183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
19283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# xmlrpc integer limits
19383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehMAXINT =  2L**31-1
19483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehMININT = -2L**31
19583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
19683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# --------------------------------------------------------------------
19783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Error constants (from Dan Libby's specification at
19883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php)
19983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
20083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Ranges of errors
20183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehPARSE_ERROR       = -32700
20283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehSERVER_ERROR      = -32600
20383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehAPPLICATION_ERROR = -32500
20483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehSYSTEM_ERROR      = -32400
20583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehTRANSPORT_ERROR   = -32300
20683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
20783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Specific errors
20883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehNOT_WELLFORMED_ERROR  = -32700
20983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehUNSUPPORTED_ENCODING  = -32701
21083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehINVALID_ENCODING_CHAR = -32702
21183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehINVALID_XMLRPC        = -32600
21283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehMETHOD_NOT_FOUND      = -32601
21383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehINVALID_METHOD_PARAMS = -32602
21483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehINTERNAL_ERROR        = -32603
21583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
21683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# --------------------------------------------------------------------
21783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Exceptions
21883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
21983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh##
22083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Base class for all kinds of client-side errors.
22183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
22283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehclass Error(Exception):
22383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """Base class for client errors."""
22483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __str__(self):
22583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return repr(self)
22683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
22783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh##
22883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Indicates an HTTP-level protocol error.  This is raised by the HTTP
22983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# transport layer, if the server returns an error code other than 200
23083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# (OK).
23183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh#
23283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# @param url The target URL.
23383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# @param errcode The HTTP error code.
23483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# @param errmsg The HTTP error message.
23583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# @param headers The HTTP header dictionary.
23683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
23783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehclass ProtocolError(Error):
23883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """Indicates an HTTP protocol error."""
23983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __init__(self, url, errcode, errmsg, headers):
24083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        Error.__init__(self)
24183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.url = url
24283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.errcode = errcode
24383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.errmsg = errmsg
24483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.headers = headers
24583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __repr__(self):
24683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return (
24783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            "<ProtocolError for %s: %s %s>" %
24883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            (self.url, self.errcode, self.errmsg)
24983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            )
25083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
25183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh##
25283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Indicates a broken XML-RPC response package.  This exception is
25383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# raised by the unmarshalling layer, if the XML-RPC response is
25483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# malformed.
25583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
25683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehclass ResponseError(Error):
25783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """Indicates a broken response package."""
25883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    pass
25983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
26083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh##
26183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Indicates an XML-RPC fault response package.  This exception is
26283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# raised by the unmarshalling layer, if the XML-RPC response contains
26383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# a fault string.  This exception can also used as a class, to
26483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# generate a fault XML-RPC message.
26583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh#
26683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# @param faultCode The XML-RPC fault code.
26783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# @param faultString The XML-RPC fault string.
26883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
26983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehclass Fault(Error):
27083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """Indicates an XML-RPC fault package."""
27183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __init__(self, faultCode, faultString, **extra):
27283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        Error.__init__(self)
27383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.faultCode = faultCode
27483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.faultString = faultString
27583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __repr__(self):
27683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return (
27783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            "<Fault %s: %s>" %
27883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            (self.faultCode, repr(self.faultString))
27983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            )
28083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
28183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# --------------------------------------------------------------------
28283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Special values
28383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
28483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh##
28583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Wrapper for XML-RPC boolean values.  Use the xmlrpclib.True and
28683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# xmlrpclib.False constants, or the xmlrpclib.boolean() function, to
28783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# generate boolean XML-RPC values.
28883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh#
28983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# @param value A boolean value.  Any true value is interpreted as True,
29083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh#              all other values are interpreted as False.
29183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
29283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehfrom sys import modules
29383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehmod_dict = modules[__name__].__dict__
29483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehif _bool_is_builtin:
29583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    boolean = Boolean = bool
29683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # to avoid breaking code which references xmlrpclib.{True,False}
29783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    mod_dict['True'] = True
29883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    mod_dict['False'] = False
29983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehelse:
30083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    class Boolean:
30183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """Boolean-value wrapper.
30283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
30383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        Use True or False to generate a "boolean" XML-RPC value.
30483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
30583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
30683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        def __init__(self, value = 0):
30783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self.value = operator.truth(value)
30883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
30983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        def encode(self, out):
31083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            out.write("<value><boolean>%d</boolean></value>\n" % self.value)
31183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
31283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        def __cmp__(self, other):
31383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if isinstance(other, Boolean):
31483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                other = other.value
31583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            return cmp(self.value, other)
31683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
31783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        def __repr__(self):
31883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if self.value:
31983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                return "<Boolean True at %x>" % id(self)
32083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            else:
32183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                return "<Boolean False at %x>" % id(self)
32283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
32383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        def __int__(self):
32483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            return self.value
32583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
32683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        def __nonzero__(self):
32783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            return self.value
32883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
32983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    mod_dict['True'] = Boolean(1)
33083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    mod_dict['False'] = Boolean(0)
33183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
33283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    ##
33383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # Map true or false value to XML-RPC boolean values.
33483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #
33583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # @def boolean(value)
33683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # @param value A boolean value.  Any true value is mapped to True,
33783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #              all other values are mapped to False.
33883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # @return xmlrpclib.True or xmlrpclib.False.
33983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # @see Boolean
34083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # @see True
34183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # @see False
34283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
34383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def boolean(value, _truefalse=(False, True)):
34483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """Convert any Python value to XML-RPC 'boolean'."""
34583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return _truefalse[operator.truth(value)]
34683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
34783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdel modules, mod_dict
34883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
34983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh##
35083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Wrapper for XML-RPC DateTime values.  This converts a time value to
35183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# the format used by XML-RPC.
35283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# <p>
35383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# The value can be given as a string in the format
35483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# "yyyymmddThh:mm:ss", as a 9-item time tuple (as returned by
35583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# time.localtime()), or an integer value (as returned by time.time()).
35683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# The wrapper uses time.localtime() to convert an integer to a time
35783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# tuple.
35883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh#
35983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# @param value The time, given as an ISO 8601 string, a time
36083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh#              tuple, or a integer time value.
36183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
36283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef _strftime(value):
36383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if datetime:
36483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if isinstance(value, datetime.datetime):
36583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            return "%04d%02d%02dT%02d:%02d:%02d" % (
36683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                value.year, value.month, value.day,
36783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                value.hour, value.minute, value.second)
36883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
36983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if not isinstance(value, (TupleType, time.struct_time)):
37083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if value == 0:
37183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            value = time.time()
37283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        value = time.localtime(value)
37383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
37483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    return "%04d%02d%02dT%02d:%02d:%02d" % value[:6]
37583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
37683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehclass DateTime:
37783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """DateTime wrapper for an ISO 8601 string or time tuple or
37883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    localtime integer value to generate 'dateTime.iso8601' XML-RPC
37983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    value.
38083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """
38183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
38283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __init__(self, value=0):
38383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if isinstance(value, StringType):
38483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self.value = value
38583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
38683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self.value = _strftime(value)
38783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
38883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def make_comparable(self, other):
38983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if isinstance(other, DateTime):
39083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            s = self.value
39183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            o = other.value
39283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        elif datetime and isinstance(other, datetime.datetime):
39383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            s = self.value
39483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            o = other.strftime("%Y%m%dT%H:%M:%S")
39583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        elif isinstance(other, (str, unicode)):
39683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            s = self.value
39783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            o = other
39883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        elif hasattr(other, "timetuple"):
39983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            s = self.timetuple()
40083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            o = other.timetuple()
40183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
40283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            otype = (hasattr(other, "__class__")
40383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                     and other.__class__.__name__
40483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                     or type(other))
40583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            raise TypeError("Can't compare %s and %s" %
40683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                            (self.__class__.__name__, otype))
40783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return s, o
40883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
40983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __lt__(self, other):
41083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        s, o = self.make_comparable(other)
41183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return s < o
41283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
41383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __le__(self, other):
41483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        s, o = self.make_comparable(other)
41583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return s <= o
41683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
41783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __gt__(self, other):
41883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        s, o = self.make_comparable(other)
41983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return s > o
42083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
42183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __ge__(self, other):
42283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        s, o = self.make_comparable(other)
42383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return s >= o
42483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
42583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __eq__(self, other):
42683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        s, o = self.make_comparable(other)
42783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return s == o
42883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
42983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __ne__(self, other):
43083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        s, o = self.make_comparable(other)
43183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return s != o
43283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
43383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def timetuple(self):
43483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return time.strptime(self.value, "%Y%m%dT%H:%M:%S")
43583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
43683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __cmp__(self, other):
43783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        s, o = self.make_comparable(other)
43883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return cmp(s, o)
43983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
44083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    ##
44183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # Get date/time value.
44283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #
44383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # @return Date/time value, as an ISO 8601 string.
44483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
44583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __str__(self):
44683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return self.value
44783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
44883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __repr__(self):
44983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return "<DateTime %s at %x>" % (repr(self.value), id(self))
45083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
45183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def decode(self, data):
45283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        data = str(data)
45383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.value = string.strip(data)
45483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
45583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def encode(self, out):
45683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        out.write("<value><dateTime.iso8601>")
45783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        out.write(self.value)
45883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        out.write("</dateTime.iso8601></value>\n")
45983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
46083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef _datetime(data):
46183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # decode xml element contents into a DateTime structure.
46283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    value = DateTime()
46383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    value.decode(data)
46483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    return value
46583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
46683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef _datetime_type(data):
46783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    t = time.strptime(data, "%Y%m%dT%H:%M:%S")
46883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    return datetime.datetime(*tuple(t)[:6])
46983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
47083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh##
47183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Wrapper for binary data.  This can be used to transport any kind
47283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# of binary data over XML-RPC, using BASE64 encoding.
47383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh#
47483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# @param data An 8-bit string containing arbitrary data.
47583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
47683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehimport base64
47783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehtry:
47883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    import cStringIO as StringIO
47983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehexcept ImportError:
48083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    import StringIO
48183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
48283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehclass Binary:
48383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """Wrapper for binary data."""
48483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
48583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __init__(self, data=None):
48683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.data = data
48783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
48883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    ##
48983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # Get buffer contents.
49083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #
49183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # @return Buffer contents, as an 8-bit string.
49283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
49383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __str__(self):
49483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return self.data or ""
49583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
49683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __cmp__(self, other):
49783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if isinstance(other, Binary):
49883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            other = other.data
49983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return cmp(self.data, other)
50083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
50183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def decode(self, data):
50283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.data = base64.decodestring(data)
50383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
50483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def encode(self, out):
50583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        out.write("<value><base64>\n")
50683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        base64.encode(StringIO.StringIO(self.data), out)
50783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        out.write("</base64></value>\n")
50883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
50983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef _binary(data):
51083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # decode xml element contents into a Binary structure
51183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    value = Binary()
51283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    value.decode(data)
51383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    return value
51483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
51583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehWRAPPERS = (DateTime, Binary)
51683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehif not _bool_is_builtin:
51783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    WRAPPERS = WRAPPERS + (Boolean,)
51883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
51983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# --------------------------------------------------------------------
52083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# XML parsers
52183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
52283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehtry:
52383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # optional xmlrpclib accelerator
52483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    import _xmlrpclib
52583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    FastParser = _xmlrpclib.Parser
52683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    FastUnmarshaller = _xmlrpclib.Unmarshaller
52783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehexcept (AttributeError, ImportError):
52883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    FastParser = FastUnmarshaller = None
52983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
53083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehtry:
53183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    import _xmlrpclib
53283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    FastMarshaller = _xmlrpclib.Marshaller
53383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehexcept (AttributeError, ImportError):
53483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    FastMarshaller = None
53583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
53683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehtry:
53783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    from xml.parsers import expat
53883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if not hasattr(expat, "ParserCreate"):
53983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        raise ImportError
54083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehexcept ImportError:
54183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    ExpatParser = None # expat not available
54283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehelse:
54383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    class ExpatParser:
54483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # fast expat parser for Python 2.0 and later.
54583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        def __init__(self, target):
54683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self._parser = parser = expat.ParserCreate(None, None)
54783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self._target = target
54883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            parser.StartElementHandler = target.start
54983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            parser.EndElementHandler = target.end
55083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            parser.CharacterDataHandler = target.data
55183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            encoding = None
55283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if not parser.returns_unicode:
55383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                encoding = "utf-8"
55483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            target.xml(encoding, None)
55583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
55683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        def feed(self, data):
55783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self._parser.Parse(data, 0)
55883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
55983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        def close(self):
56083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self._parser.Parse("", 1) # end of data
56183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            del self._target, self._parser # get rid of circular references
56283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
56383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehclass SlowParser:
56483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """Default XML parser (based on xmllib.XMLParser)."""
56583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # this is the slowest parser.
56683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __init__(self, target):
56783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        import xmllib # lazy subclassing (!)
56883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if xmllib.XMLParser not in SlowParser.__bases__:
56983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            SlowParser.__bases__ = (xmllib.XMLParser,)
57083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.handle_xml = target.xml
57183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.unknown_starttag = target.start
57283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.handle_data = target.data
57383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.handle_cdata = target.data
57483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.unknown_endtag = target.end
57583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        try:
57683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            xmllib.XMLParser.__init__(self, accept_utf8=1)
57783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        except TypeError:
57883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            xmllib.XMLParser.__init__(self) # pre-2.0
57983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
58083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# --------------------------------------------------------------------
58183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# XML-RPC marshalling and unmarshalling code
58283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
58383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh##
58483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# XML-RPC marshaller.
58583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh#
58683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# @param encoding Default encoding for 8-bit strings.  The default
58783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh#     value is None (interpreted as UTF-8).
58883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# @see dumps
58983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
59083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehclass Marshaller:
59183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """Generate an XML-RPC params chunk from a Python data structure.
59283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
59383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    Create a Marshaller instance for each set of parameters, and use
59483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    the "dumps" method to convert your data (represented as a tuple)
59583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    to an XML-RPC params chunk.  To write a fault response, pass a
59683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    Fault instance instead.  You may prefer to use the "dumps" module
59783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    function for this purpose.
59883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """
59983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
60083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # by the way, if you don't understand what's going on in here,
60183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # that's perfectly ok.
60283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
60383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __init__(self, encoding=None, allow_none=0):
60483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.memo = {}
60583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.data = None
60683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.encoding = encoding
60783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.allow_none = allow_none
60883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
60983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    dispatch = {}
61083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
61183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def dumps(self, values):
61283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        out = []
61383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        write = out.append
61483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        dump = self.__dump
61583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if isinstance(values, Fault):
61683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            # fault instance
61783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            write("<fault>\n")
61883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            dump({'faultCode': values.faultCode,
61983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                  'faultString': values.faultString},
62083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                 write)
62183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            write("</fault>\n")
62283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
62383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            # parameter block
62483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            # FIXME: the xml-rpc specification allows us to leave out
62583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            # the entire <params> block if there are no parameters.
62683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            # however, changing this may break older code (including
62783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            # old versions of xmlrpclib.py), so this is better left as
62883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            # is for now.  See @XMLRPC3 for more information. /F
62983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            write("<params>\n")
63083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            for v in values:
63183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                write("<param>\n")
63283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                dump(v, write)
63383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                write("</param>\n")
63483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            write("</params>\n")
63583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        result = string.join(out, "")
63683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return result
63783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
63883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __dump(self, value, write):
63983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        try:
64083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            f = self.dispatch[type(value)]
64183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        except KeyError:
64283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            # check if this object can be marshalled as a structure
64383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            try:
64483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                value.__dict__
64583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            except:
64683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                raise TypeError, "cannot marshal %s objects" % type(value)
64783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            # check if this class is a sub-class of a basic type,
64883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            # because we don't know how to marshal these types
64983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            # (e.g. a string sub-class)
65083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            for type_ in type(value).__mro__:
65183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                if type_ in self.dispatch.keys():
65283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                    raise TypeError, "cannot marshal %s objects" % type(value)
65383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            f = self.dispatch[InstanceType]
65483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        f(self, value, write)
65583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
65683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def dump_nil (self, value, write):
65783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if not self.allow_none:
65883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            raise TypeError, "cannot marshal None unless allow_none is enabled"
65983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        write("<value><nil/></value>")
66083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    dispatch[NoneType] = dump_nil
66183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
66283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def dump_int(self, value, write):
66383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # in case ints are > 32 bits
66483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if value > MAXINT or value < MININT:
66583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            raise OverflowError, "int exceeds XML-RPC limits"
66683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        write("<value><int>")
66783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        write(str(value))
66883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        write("</int></value>\n")
66983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    dispatch[IntType] = dump_int
67083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
67183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if _bool_is_builtin:
67283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        def dump_bool(self, value, write):
67383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            write("<value><boolean>")
67483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            write(value and "1" or "0")
67583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            write("</boolean></value>\n")
67683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        dispatch[bool] = dump_bool
67783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
67883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def dump_long(self, value, write):
67983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if value > MAXINT or value < MININT:
68083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            raise OverflowError, "long int exceeds XML-RPC limits"
68183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        write("<value><int>")
68283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        write(str(int(value)))
68383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        write("</int></value>\n")
68483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    dispatch[LongType] = dump_long
68583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
68683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def dump_double(self, value, write):
68783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        write("<value><double>")
68883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        write(repr(value))
68983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        write("</double></value>\n")
69083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    dispatch[FloatType] = dump_double
69183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
69283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def dump_string(self, value, write, escape=escape):
69383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        write("<value><string>")
69483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        write(escape(value))
69583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        write("</string></value>\n")
69683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    dispatch[StringType] = dump_string
69783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
69883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if unicode:
69983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        def dump_unicode(self, value, write, escape=escape):
70083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            value = value.encode(self.encoding)
70183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            write("<value><string>")
70283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            write(escape(value))
70383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            write("</string></value>\n")
70483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        dispatch[UnicodeType] = dump_unicode
70583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
70683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def dump_array(self, value, write):
70783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        i = id(value)
70883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if i in self.memo:
70983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            raise TypeError, "cannot marshal recursive sequences"
71083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.memo[i] = None
71183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        dump = self.__dump
71283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        write("<value><array><data>\n")
71383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        for v in value:
71483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            dump(v, write)
71583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        write("</data></array></value>\n")
71683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        del self.memo[i]
71783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    dispatch[TupleType] = dump_array
71883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    dispatch[ListType] = dump_array
71983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
72083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def dump_struct(self, value, write, escape=escape):
72183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        i = id(value)
72283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if i in self.memo:
72383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            raise TypeError, "cannot marshal recursive dictionaries"
72483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.memo[i] = None
72583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        dump = self.__dump
72683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        write("<value><struct>\n")
72783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        for k, v in value.items():
72883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            write("<member>\n")
72983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if type(k) is not StringType:
73083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                if unicode and type(k) is UnicodeType:
73183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                    k = k.encode(self.encoding)
73283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                else:
73383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                    raise TypeError, "dictionary key must be string"
73483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            write("<name>%s</name>\n" % escape(k))
73583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            dump(v, write)
73683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            write("</member>\n")
73783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        write("</struct></value>\n")
73883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        del self.memo[i]
73983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    dispatch[DictType] = dump_struct
74083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
74183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if datetime:
74283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        def dump_datetime(self, value, write):
74383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            write("<value><dateTime.iso8601>")
74483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            write(_strftime(value))
74583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            write("</dateTime.iso8601></value>\n")
74683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        dispatch[datetime.datetime] = dump_datetime
74783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
74883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def dump_instance(self, value, write):
74983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # check for special wrappers
75083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if value.__class__ in WRAPPERS:
75183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self.write = write
75283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            value.encode(self)
75383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            del self.write
75483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
75583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            # store instance attributes as a struct (really?)
75683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self.dump_struct(value.__dict__, write)
75783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    dispatch[InstanceType] = dump_instance
75883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
75983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh##
76083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# XML-RPC unmarshaller.
76183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh#
76283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# @see loads
76383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
76483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehclass Unmarshaller:
76583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """Unmarshal an XML-RPC response, based on incoming XML event
76683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    messages (start, data, end).  Call close() to get the resulting
76783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    data structure.
76883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
76983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    Note that this reader is fairly tolerant, and gladly accepts bogus
77083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    XML-RPC data without complaining (but not bogus XML).
77183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """
77283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
77383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # and again, if you don't understand what's going on in here,
77483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # that's perfectly ok.
77583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
77683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __init__(self, use_datetime=0):
77783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._type = None
77883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._stack = []
77983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._marks = []
78083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._data = []
78183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._methodname = None
78283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._encoding = "utf-8"
78383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.append = self._stack.append
78483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._use_datetime = use_datetime
78583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if use_datetime and not datetime:
78683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            raise ValueError, "the datetime module is not available"
78783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
78883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def close(self):
78983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # return response tuple and target method
79083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if self._type is None or self._marks:
79183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            raise ResponseError()
79283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if self._type == "fault":
79383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            raise Fault(**self._stack[0])
79483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return tuple(self._stack)
79583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
79683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def getmethodname(self):
79783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return self._methodname
79883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
79983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #
80083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # event handlers
80183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
80283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def xml(self, encoding, standalone):
80383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._encoding = encoding
80483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # FIXME: assert standalone == 1 ???
80583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
80683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def start(self, tag, attrs):
80783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # prepare to handle this element
80883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if tag == "array" or tag == "struct":
80983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self._marks.append(len(self._stack))
81083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._data = []
81183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._value = (tag == "value")
81283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
81383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def data(self, text):
81483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._data.append(text)
81583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
81683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def end(self, tag, join=string.join):
81783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # call the appropriate end tag handler
81883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        try:
81983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            f = self.dispatch[tag]
82083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        except KeyError:
82183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            pass # unknown tag ?
82283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
82383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            return f(self, join(self._data, ""))
82483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
82583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #
82683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # accelerator support
82783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
82883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def end_dispatch(self, tag, data):
82983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # dispatch data
83083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        try:
83183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            f = self.dispatch[tag]
83283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        except KeyError:
83383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            pass # unknown tag ?
83483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
83583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            return f(self, data)
83683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
83783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #
83883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # element decoders
83983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
84083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    dispatch = {}
84183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
84283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def end_nil (self, data):
84383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.append(None)
84483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._value = 0
84583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    dispatch["nil"] = end_nil
84683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
84783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def end_boolean(self, data):
84883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if data == "0":
84983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self.append(False)
85083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        elif data == "1":
85183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self.append(True)
85283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
85383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            raise TypeError, "bad boolean value"
85483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._value = 0
85583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    dispatch["boolean"] = end_boolean
85683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
85783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def end_int(self, data):
85883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.append(int(data))
85983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._value = 0
86083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    dispatch["i4"] = end_int
86183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    dispatch["i8"] = end_int
86283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    dispatch["int"] = end_int
86383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
86483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def end_double(self, data):
86583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.append(float(data))
86683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._value = 0
86783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    dispatch["double"] = end_double
86883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
86983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def end_string(self, data):
87083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if self._encoding:
87183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            data = _decode(data, self._encoding)
87283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.append(_stringify(data))
87383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._value = 0
87483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    dispatch["string"] = end_string
87583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    dispatch["name"] = end_string # struct keys are always strings
87683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
87783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def end_array(self, data):
87883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        mark = self._marks.pop()
87983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # map arrays to Python lists
88083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._stack[mark:] = [self._stack[mark:]]
88183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._value = 0
88283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    dispatch["array"] = end_array
88383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
88483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def end_struct(self, data):
88583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        mark = self._marks.pop()
88683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # map structs to Python dictionaries
88783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        dict = {}
88883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        items = self._stack[mark:]
88983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        for i in range(0, len(items), 2):
89083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            dict[_stringify(items[i])] = items[i+1]
89183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._stack[mark:] = [dict]
89283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._value = 0
89383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    dispatch["struct"] = end_struct
89483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
89583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def end_base64(self, data):
89683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        value = Binary()
89783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        value.decode(data)
89883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.append(value)
89983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._value = 0
90083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    dispatch["base64"] = end_base64
90183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
90283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def end_dateTime(self, data):
90383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        value = DateTime()
90483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        value.decode(data)
90583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if self._use_datetime:
90683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            value = _datetime_type(data)
90783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.append(value)
90883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    dispatch["dateTime.iso8601"] = end_dateTime
90983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
91083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def end_value(self, data):
91183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # if we stumble upon a value element with no internal
91283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # elements, treat it as a string element
91383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if self._value:
91483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self.end_string(data)
91583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    dispatch["value"] = end_value
91683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
91783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def end_params(self, data):
91883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._type = "params"
91983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    dispatch["params"] = end_params
92083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
92183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def end_fault(self, data):
92283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._type = "fault"
92383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    dispatch["fault"] = end_fault
92483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
92583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def end_methodName(self, data):
92683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if self._encoding:
92783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            data = _decode(data, self._encoding)
92883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._methodname = data
92983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._type = "methodName" # no params
93083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    dispatch["methodName"] = end_methodName
93183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
93283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh## Multicall support
93383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh#
93483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
93583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehclass _MultiCallMethod:
93683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # some lesser magic to store calls made to a MultiCall object
93783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # for batch execution
93883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __init__(self, call_list, name):
93983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.__call_list = call_list
94083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.__name = name
94183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __getattr__(self, name):
94283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return _MultiCallMethod(self.__call_list, "%s.%s" % (self.__name, name))
94383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __call__(self, *args):
94483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.__call_list.append((self.__name, args))
94583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
94683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehclass MultiCallIterator:
94783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """Iterates over the results of a multicall. Exceptions are
94883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    raised in response to xmlrpc faults."""
94983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
95083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __init__(self, results):
95183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.results = results
95283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
95383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __getitem__(self, i):
95483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        item = self.results[i]
95583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if type(item) == type({}):
95683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            raise Fault(item['faultCode'], item['faultString'])
95783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        elif type(item) == type([]):
95883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            return item[0]
95983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
96083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            raise ValueError,\
96183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                  "unexpected type in multicall result"
96283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
96383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehclass MultiCall:
96483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """server -> a object used to boxcar method calls
96583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
96683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    server should be a ServerProxy object.
96783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
96883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    Methods can be added to the MultiCall using normal
96983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    method call syntax e.g.:
97083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
97183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    multicall = MultiCall(server_proxy)
97283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    multicall.add(2,3)
97383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    multicall.get_address("Guido")
97483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
97583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    To execute the multicall, call the MultiCall object e.g.:
97683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
97783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    add_result, address = multicall()
97883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """
97983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
98083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __init__(self, server):
98183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.__server = server
98283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.__call_list = []
98383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
98483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __repr__(self):
98583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return "<MultiCall at %x>" % id(self)
98683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
98783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    __str__ = __repr__
98883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
98983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __getattr__(self, name):
99083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return _MultiCallMethod(self.__call_list, name)
99183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
99283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __call__(self):
99383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        marshalled_list = []
99483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        for name, args in self.__call_list:
99583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            marshalled_list.append({'methodName' : name, 'params' : args})
99683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
99783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return MultiCallIterator(self.__server.system.multicall(marshalled_list))
99883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
99983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# --------------------------------------------------------------------
100083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# convenience functions
100183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
100283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh##
100383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Create a parser object, and connect it to an unmarshalling instance.
100483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# This function picks the fastest available XML parser.
100583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh#
100683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# return A (parser, unmarshaller) tuple.
100783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
100883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef getparser(use_datetime=0):
100983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """getparser() -> parser, unmarshaller
101083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
101183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    Create an instance of the fastest available parser, and attach it
101283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    to an unmarshalling object.  Return both objects.
101383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """
101483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if use_datetime and not datetime:
101583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        raise ValueError, "the datetime module is not available"
101683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if FastParser and FastUnmarshaller:
101783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if use_datetime:
101883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            mkdatetime = _datetime_type
101983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
102083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            mkdatetime = _datetime
102183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        target = FastUnmarshaller(True, False, _binary, mkdatetime, Fault)
102283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        parser = FastParser(target)
102383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    else:
102483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        target = Unmarshaller(use_datetime=use_datetime)
102583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if FastParser:
102683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            parser = FastParser(target)
102783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        elif ExpatParser:
102883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            parser = ExpatParser(target)
102983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
103083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            parser = SlowParser(target)
103183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    return parser, target
103283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
103383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh##
103483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Convert a Python tuple or a Fault instance to an XML-RPC packet.
103583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh#
103683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# @def dumps(params, **options)
103783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# @param params A tuple or Fault instance.
103883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# @keyparam methodname If given, create a methodCall request for
103983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh#     this method name.
104083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# @keyparam methodresponse If given, create a methodResponse packet.
104183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh#     If used with a tuple, the tuple must be a singleton (that is,
104283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh#     it must contain exactly one element).
104383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# @keyparam encoding The packet encoding.
104483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# @return A string containing marshalled data.
104583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
104683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef dumps(params, methodname=None, methodresponse=None, encoding=None,
104783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh          allow_none=0):
104883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """data [,options] -> marshalled data
104983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
105083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    Convert an argument tuple or a Fault instance to an XML-RPC
105183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    request (or response, if the methodresponse option is used).
105283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
105383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    In addition to the data object, the following options can be given
105483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    as keyword arguments:
105583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
105683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        methodname: the method name for a methodCall packet
105783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
105883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        methodresponse: true to create a methodResponse packet.
105983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        If this option is used with a tuple, the tuple must be
106083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        a singleton (i.e. it can contain only one element).
106183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
106283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        encoding: the packet encoding (default is UTF-8)
106383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
106483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    All 8-bit strings in the data structure are assumed to use the
106583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    packet encoding.  Unicode strings are automatically converted,
106683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    where necessary.
106783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """
106883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
106983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    assert isinstance(params, TupleType) or isinstance(params, Fault),\
107083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh           "argument must be tuple or Fault instance"
107183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
107283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if isinstance(params, Fault):
107383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        methodresponse = 1
107483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    elif methodresponse and isinstance(params, TupleType):
107583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        assert len(params) == 1, "response tuple must be a singleton"
107683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
107783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if not encoding:
107883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        encoding = "utf-8"
107983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
108083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if FastMarshaller:
108183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        m = FastMarshaller(encoding)
108283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    else:
108383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        m = Marshaller(encoding, allow_none)
108483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
108583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    data = m.dumps(params)
108683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
108783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if encoding != "utf-8":
108883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        xmlheader = "<?xml version='1.0' encoding='%s'?>\n" % str(encoding)
108983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    else:
109083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        xmlheader = "<?xml version='1.0'?>\n" # utf-8 is default
109183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
109283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # standard XML-RPC wrappings
109383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if methodname:
109483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # a method call
109583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if not isinstance(methodname, StringType):
109683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            methodname = methodname.encode(encoding)
109783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        data = (
109883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            xmlheader,
109983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            "<methodCall>\n"
110083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            "<methodName>", methodname, "</methodName>\n",
110183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            data,
110283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            "</methodCall>\n"
110383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            )
110483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    elif methodresponse:
110583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # a method response, or a fault structure
110683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        data = (
110783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            xmlheader,
110883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            "<methodResponse>\n",
110983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            data,
111083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            "</methodResponse>\n"
111183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            )
111283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    else:
111383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return data # return as is
111483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    return string.join(data, "")
111583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
111683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh##
111783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Convert an XML-RPC packet to a Python object.  If the XML-RPC packet
111883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# represents a fault condition, this function raises a Fault exception.
111983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh#
112083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# @param data An XML-RPC packet, given as an 8-bit string.
112183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# @return A tuple containing the unpacked data, and the method name
112283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh#     (None if not present).
112383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# @see Fault
112483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
112583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef loads(data, use_datetime=0):
112683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """data -> unmarshalled data, method name
112783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
112883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    Convert an XML-RPC packet to unmarshalled data plus a method
112983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    name (None if not present).
113083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
113183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    If the XML-RPC packet represents a fault condition, this function
113283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    raises a Fault exception.
113383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """
113483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    p, u = getparser(use_datetime=use_datetime)
113583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    p.feed(data)
113683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    p.close()
113783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    return u.close(), u.getmethodname()
113883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
113983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh##
114083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Encode a string using the gzip content encoding such as specified by the
114183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Content-Encoding: gzip
114283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# in the HTTP header, as described in RFC 1952
114383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh#
114483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# @param data the unencoded data
114583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# @return the encoded data
114683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
114783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef gzip_encode(data):
114883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """data -> gzip encoded data
114983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
115083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    Encode data using the gzip content encoding as described in RFC 1952
115183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """
115283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if not gzip:
115383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        raise NotImplementedError
115483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    f = StringIO.StringIO()
115583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    gzf = gzip.GzipFile(mode="wb", fileobj=f, compresslevel=1)
115683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    gzf.write(data)
115783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    gzf.close()
115883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    encoded = f.getvalue()
115983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    f.close()
116083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    return encoded
116183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
116283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh##
116383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Decode a string using the gzip content encoding such as specified by the
116483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Content-Encoding: gzip
116583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# in the HTTP header, as described in RFC 1952
116683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh#
116783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# @param data The encoded data
116883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# @return the unencoded data
116983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# @raises ValueError if data is not correctly coded.
117083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
117183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehdef gzip_decode(data):
117283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """gzip encoded data -> unencoded data
117383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
117483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    Decode data using the gzip content encoding as described in RFC 1952
117583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """
117683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    if not gzip:
117783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        raise NotImplementedError
117883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    f = StringIO.StringIO(data)
117983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    gzf = gzip.GzipFile(mode="rb", fileobj=f)
118083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    try:
118183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        decoded = gzf.read()
118283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    except IOError:
118383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        raise ValueError("invalid data")
118483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    f.close()
118583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    gzf.close()
118683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    return decoded
118783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
118883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh##
118983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Return a decoded file-like object for the gzip encoding
119083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# as described in RFC 1952.
119183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh#
119283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# @param response A stream supporting a read() method
119383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# @return a file-like object that the decoded data can be read() from
119483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
119583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehclass GzipDecodedResponse(gzip.GzipFile if gzip else object):
119683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """a file-like object to decode a response encoded with the gzip
119783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    method, as described in RFC 1952.
119883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """
119983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __init__(self, response):
120083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        #response doesn't support tell() and read(), required by
120183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        #GzipFile
120283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if not gzip:
120383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            raise NotImplementedError
120483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.stringio = StringIO.StringIO(response.read())
120583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        gzip.GzipFile.__init__(self, mode="rb", fileobj=self.stringio)
120683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
120783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def close(self):
120883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        gzip.GzipFile.close(self)
120983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.stringio.close()
121083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
121183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
121283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# --------------------------------------------------------------------
121383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# request dispatcher
121483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
121583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehclass _Method:
121683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # some magic to bind an XML-RPC method to an RPC server.
121783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # supports "nested" methods (e.g. examples.getStateName)
121883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __init__(self, send, name):
121983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.__send = send
122083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.__name = name
122183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __getattr__(self, name):
122283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return _Method(self.__send, "%s.%s" % (self.__name, name))
122383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __call__(self, *args):
122483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return self.__send(self.__name, args)
122583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
122683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh##
122783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Standard transport class for XML-RPC over HTTP.
122883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# <p>
122983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# You can create custom transports by subclassing this method, and
123083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# overriding selected methods.
123183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
123283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehclass Transport:
123383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """Handles an HTTP transaction to an XML-RPC server."""
123483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
123583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # client identifier (may be overridden)
123683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    user_agent = "xmlrpclib.py/%s (by www.pythonware.com)" % __version__
123783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
123883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #if true, we'll request gzip encoding
123983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    accept_gzip_encoding = True
124083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
124183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # if positive, encode request using gzip if it exceeds this threshold
124283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # note that many server will get confused, so only use it if you know
124383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # that they can decode such a request
124483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    encode_threshold = None #None = don't encode
124583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
124683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __init__(self, use_datetime=0):
124783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._use_datetime = use_datetime
124883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._connection = (None, None)
124983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._extra_headers = []
125083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    ##
125183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # Send a complete request, and parse the response.
125283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # Retry request if a cached connection has disconnected.
125383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #
125483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # @param host Target host.
125583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # @param handler Target PRC handler.
125683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # @param request_body XML-RPC request body.
125783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # @param verbose Debugging flag.
125883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # @return Parsed response.
125983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
126083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def request(self, host, handler, request_body, verbose=0):
126183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        #retry request once if cached connection has gone cold
126283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        for i in (0, 1):
126383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            try:
126483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                return self.single_request(host, handler, request_body, verbose)
126583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            except socket.error, e:
126683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                if i or e.errno not in (errno.ECONNRESET, errno.ECONNABORTED, errno.EPIPE):
126783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                    raise
126883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            except httplib.BadStatusLine: #close after we sent request
126983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                if i:
127083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                    raise
127183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
127283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    ##
127383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # Send a complete request, and parse the response.
127483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #
127583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # @param host Target host.
127683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # @param handler Target PRC handler.
127783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # @param request_body XML-RPC request body.
127883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # @param verbose Debugging flag.
127983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # @return Parsed response.
128083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
128183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def single_request(self, host, handler, request_body, verbose=0):
128283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # issue XML-RPC request
128383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
128483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        h = self.make_connection(host)
128583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if verbose:
128683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            h.set_debuglevel(1)
128783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
128883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        try:
128983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self.send_request(h, handler, request_body)
129083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self.send_host(h, host)
129183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self.send_user_agent(h)
129283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self.send_content(h, request_body)
129383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
129483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            response = h.getresponse(buffering=True)
129583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if response.status == 200:
129683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                self.verbose = verbose
129783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                return self.parse_response(response)
129883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        except Fault:
129983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            raise
130083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        except Exception:
130183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            # All unexpected errors leave connection in
130283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            # a strange state, so we clear it.
130383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self.close()
130483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            raise
130583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
130683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        #discard any response data and raise exception
130783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if (response.getheader("content-length", 0)):
130883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            response.read()
130983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        raise ProtocolError(
131083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            host + handler,
131183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            response.status, response.reason,
131283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            response.msg,
131383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            )
131483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
131583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    ##
131683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # Create parser.
131783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #
131883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # @return A 2-tuple containing a parser and a unmarshaller.
131983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
132083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def getparser(self):
132183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # get parser and unmarshaller
132283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return getparser(use_datetime=self._use_datetime)
132383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
132483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    ##
132583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # Get authorization info from host parameter
132683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # Host may be a string, or a (host, x509-dict) tuple; if a string,
132783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # it is checked for a "user:pw@host" format, and a "Basic
132883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # Authentication" header is added if appropriate.
132983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #
133083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # @param host Host descriptor (URL or (URL, x509 info) tuple).
133183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # @return A 3-tuple containing (actual host, extra headers,
133283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #     x509 info).  The header and x509 fields may be None.
133383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
133483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def get_host_info(self, host):
133583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
133683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        x509 = {}
133783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if isinstance(host, TupleType):
133883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            host, x509 = host
133983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
134083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        import urllib
134183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        auth, host = urllib.splituser(host)
134283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
134383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if auth:
134483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            import base64
134583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            auth = base64.encodestring(urllib.unquote(auth))
134683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            auth = string.join(string.split(auth), "") # get rid of whitespace
134783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            extra_headers = [
134883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                ("Authorization", "Basic " + auth)
134983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                ]
135083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
135183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            extra_headers = None
135283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
135383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return host, extra_headers, x509
135483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
135583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    ##
135683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # Connect to server.
135783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #
135883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # @param host Target host.
135983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # @return A connection handle.
136083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
136183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def make_connection(self, host):
136283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        #return an existing connection if possible.  This allows
136383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        #HTTP/1.1 keep-alive.
136483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if self._connection and host == self._connection[0]:
136583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            return self._connection[1]
136683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
136783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # create a HTTP connection object from a host descriptor
136883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        chost, self._extra_headers, x509 = self.get_host_info(host)
136983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        #store the host argument along with the connection object
137083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self._connection = host, httplib.HTTPConnection(chost)
137183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return self._connection[1]
137283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
137383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    ##
137483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # Clear any cached connection object.
137583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # Used in the event of socket errors.
137683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #
137783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def close(self):
137883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if self._connection[1]:
137983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self._connection[1].close()
138083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self._connection = (None, None)
138183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
138283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    ##
138383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # Send request header.
138483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #
138583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # @param connection Connection handle.
138683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # @param handler Target RPC handler.
138783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # @param request_body XML-RPC body.
138883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
138983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def send_request(self, connection, handler, request_body):
139083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if (self.accept_gzip_encoding and gzip):
139183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            connection.putrequest("POST", handler, skip_accept_encoding=True)
139283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            connection.putheader("Accept-Encoding", "gzip")
139383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
139483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            connection.putrequest("POST", handler)
139583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
139683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    ##
139783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # Send host name.
139883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #
139983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # @param connection Connection handle.
140083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # @param host Host name.
140183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #
140283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # Note: This function doesn't actually add the "Host"
140383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # header anymore, it is done as part of the connection.putrequest() in
140483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # send_request() above.
140583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
140683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def send_host(self, connection, host):
140783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        extra_headers = self._extra_headers
140883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if extra_headers:
140983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if isinstance(extra_headers, DictType):
141083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                extra_headers = extra_headers.items()
141183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            for key, value in extra_headers:
141283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                connection.putheader(key, value)
141383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
141483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    ##
141583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # Send user-agent identifier.
141683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #
141783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # @param connection Connection handle.
141883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
141983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def send_user_agent(self, connection):
142083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        connection.putheader("User-Agent", self.user_agent)
142183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
142283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    ##
142383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # Send request body.
142483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #
142583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # @param connection Connection handle.
142683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # @param request_body XML-RPC request body.
142783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
142883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def send_content(self, connection, request_body):
142983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        connection.putheader("Content-Type", "text/xml")
143083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
143183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        #optionally encode the request
143283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if (self.encode_threshold is not None and
143383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self.encode_threshold < len(request_body) and
143483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            gzip):
143583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            connection.putheader("Content-Encoding", "gzip")
143683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            request_body = gzip_encode(request_body)
143783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
143883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        connection.putheader("Content-Length", str(len(request_body)))
143983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        connection.endheaders(request_body)
144083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
144183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    ##
144283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # Parse response.
144383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    #
144483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # @param file Stream.
144583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # @return Response tuple and target method.
144683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
144783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def parse_response(self, response):
144883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # read response data from httpresponse, and parse it
144983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
145083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # Check for new http response object, else it is a file object
145183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if hasattr(response,'getheader'):
145283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if response.getheader("Content-Encoding", "") == "gzip":
145383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                stream = GzipDecodedResponse(response)
145483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            else:
145583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                stream = response
145683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
145783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            stream = response
145883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
145983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        p, u = self.getparser()
146083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
146183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        while 1:
146283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            data = stream.read(1024)
146383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if not data:
146483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                break
146583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if self.verbose:
146683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                print "body:", repr(data)
146783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            p.feed(data)
146883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
146983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if stream is not response:
147083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            stream.close()
147183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        p.close()
147283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
147383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return u.close()
147483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
147583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh##
147683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Standard transport class for XML-RPC over HTTPS.
147783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
147883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehclass SafeTransport(Transport):
147983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """Handles an HTTPS transaction to an XML-RPC server."""
148083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
148183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # FIXME: mostly untested
148283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
148383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def make_connection(self, host):
148483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if self._connection and host == self._connection[0]:
148583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            return self._connection[1]
148683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # create a HTTPS connection object from a host descriptor
148783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # host may be a string, or a (host, x509-dict) tuple
148883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        try:
148983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            HTTPS = httplib.HTTPSConnection
149083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        except AttributeError:
149183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            raise NotImplementedError(
149283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                "your version of httplib doesn't support HTTPS"
149383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                )
149483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        else:
149583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            chost, self._extra_headers, x509 = self.get_host_info(host)
149683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self._connection = host, HTTPS(chost, None, **(x509 or {}))
149783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            return self._connection[1]
149883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
149983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh##
150083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# Standard server proxy.  This class establishes a virtual connection
150183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# to an XML-RPC server.
150283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# <p>
150383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# This class is available as ServerProxy and Server.  New code should
150483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# use ServerProxy, to avoid confusion.
150583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh#
150683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# @def ServerProxy(uri, **options)
150783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# @param uri The connection point on the server.
150883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# @keyparam transport A transport factory, compatible with the
150983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh#    standard transport class.
151083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# @keyparam encoding The default encoding used for 8-bit strings
151183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh#    (default is UTF-8).
151283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# @keyparam verbose Use a true value to enable debugging output.
151383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh#    (printed to standard output).
151483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# @see Transport
151583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
151683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehclass ServerProxy:
151783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """uri [,options] -> a logical connection to an XML-RPC server
151883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
151983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    uri is the connection point on the server, given as
152083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    scheme://host/target.
152183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
152283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    The standard implementation always supports the "http" scheme.  If
152383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    SSL socket support is available (Python 2.0), it also supports
152483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    "https".
152583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
152683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    If the target part and the slash preceding it are both omitted,
152783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    "/RPC2" is assumed.
152883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
152983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    The following options can be given as keyword arguments:
153083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
153183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        transport: a transport factory
153283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        encoding: the request encoding (default is UTF-8)
153383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
153483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    All 8-bit strings passed to the server proxy are assumed to use
153583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    the given encoding.
153683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    """
153783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
153883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __init__(self, uri, transport=None, encoding=None, verbose=0,
153983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                 allow_none=0, use_datetime=0):
154083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # establish a "logical" server connection
154183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
154283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if isinstance(uri, unicode):
154383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            uri = uri.encode('ISO-8859-1')
154483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
154583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # get the url
154683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        import urllib
154783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        type, uri = urllib.splittype(uri)
154883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if type not in ("http", "https"):
154983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            raise IOError, "unsupported XML-RPC protocol"
155083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.__host, self.__handler = urllib.splithost(uri)
155183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if not self.__handler:
155283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self.__handler = "/RPC2"
155383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
155483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if transport is None:
155583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            if type == "https":
155683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                transport = SafeTransport(use_datetime=use_datetime)
155783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            else:
155883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                transport = Transport(use_datetime=use_datetime)
155983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.__transport = transport
156083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
156183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.__encoding = encoding
156283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.__verbose = verbose
156383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.__allow_none = allow_none
156483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
156583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __close(self):
156683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        self.__transport.close()
156783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
156883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __request(self, methodname, params):
156983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # call a method on the remote server
157083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
157183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        request = dumps(params, methodname, encoding=self.__encoding,
157283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh                        allow_none=self.__allow_none)
157383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
157483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        response = self.__transport.request(
157583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self.__host,
157683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            self.__handler,
157783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            request,
157883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            verbose=self.__verbose
157983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            )
158083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
158183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if len(response) == 1:
158283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            response = response[0]
158383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
158483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return response
158583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
158683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __repr__(self):
158783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return (
158883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            "<ServerProxy for %s%s>" %
158983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            (self.__host, self.__handler)
159083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            )
159183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
159283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    __str__ = __repr__
159383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
159483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __getattr__(self, name):
159583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        # magic method dispatcher
159683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        return _Method(self.__request, name)
159783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
159883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # note: to call a remote object with an non-standard name, use
159983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # result getattr(server, "strange-python-name")(args)
160083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
160183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    def __call__(self, attr):
160283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """A workaround to get special attributes on the ServerProxy
160383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh           without interfering with the magic __getattr__
160483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        """
160583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        if attr == "close":
160683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            return self.__close
160783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        elif attr == "transport":
160883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            return self.__transport
160983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        raise AttributeError("Attribute %r not found" % (attr,))
161083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
161183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# compatibility
161283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
161383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew HsiehServer = ServerProxy
161483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
161583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# --------------------------------------------------------------------
161683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh# test code
161783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
161883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsiehif __name__ == "__main__":
161983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
162083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # simple test program (from the XML-RPC specification)
162183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
162283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    # server = ServerProxy("http://localhost:8000") # local server
162383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    server = ServerProxy("http://time.xmlrpc.com/RPC2")
162483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
162583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    print server
162683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
162783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    try:
162883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        print server.currentTime.getCurrentTime()
162983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    except Error, v:
163083760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        print "ERROR", v
163183760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh
163283760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    multi = MultiCall(server)
163383760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    multi.currentTime.getCurrentTime()
163483760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    multi.currentTime.getCurrentTime()
163583760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    try:
163683760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        for response in multi():
163783760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh            print response
163883760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh    except Error, v:
163983760d213fb3bec7b4117d266fcfbf6fe2ba14abAndrew Hsieh        print "ERROR", v
1640