xref: /aosp_15_r20/prebuilts/build-tools/common/py3-stdlib/xmlrpc/client.py (revision cda5da8d549138a6648c5ee6d7a49cf8f4a657be)
1*cda5da8dSAndroid Build Coastguard Worker#
2*cda5da8dSAndroid Build Coastguard Worker# XML-RPC CLIENT LIBRARY
3*cda5da8dSAndroid Build Coastguard Worker# $Id$
4*cda5da8dSAndroid Build Coastguard Worker#
5*cda5da8dSAndroid Build Coastguard Worker# an XML-RPC client interface for Python.
6*cda5da8dSAndroid Build Coastguard Worker#
7*cda5da8dSAndroid Build Coastguard Worker# the marshalling and response parser code can also be used to
8*cda5da8dSAndroid Build Coastguard Worker# implement XML-RPC servers.
9*cda5da8dSAndroid Build Coastguard Worker#
10*cda5da8dSAndroid Build Coastguard Worker# Notes:
11*cda5da8dSAndroid Build Coastguard Worker# this version is designed to work with Python 2.1 or newer.
12*cda5da8dSAndroid Build Coastguard Worker#
13*cda5da8dSAndroid Build Coastguard Worker# History:
14*cda5da8dSAndroid Build Coastguard Worker# 1999-01-14 fl  Created
15*cda5da8dSAndroid Build Coastguard Worker# 1999-01-15 fl  Changed dateTime to use localtime
16*cda5da8dSAndroid Build Coastguard Worker# 1999-01-16 fl  Added Binary/base64 element, default to RPC2 service
17*cda5da8dSAndroid Build Coastguard Worker# 1999-01-19 fl  Fixed array data element (from Skip Montanaro)
18*cda5da8dSAndroid Build Coastguard Worker# 1999-01-21 fl  Fixed dateTime constructor, etc.
19*cda5da8dSAndroid Build Coastguard Worker# 1999-02-02 fl  Added fault handling, handle empty sequences, etc.
20*cda5da8dSAndroid Build Coastguard Worker# 1999-02-10 fl  Fixed problem with empty responses (from Skip Montanaro)
21*cda5da8dSAndroid Build Coastguard Worker# 1999-06-20 fl  Speed improvements, pluggable parsers/transports (0.9.8)
22*cda5da8dSAndroid Build Coastguard Worker# 2000-11-28 fl  Changed boolean to check the truth value of its argument
23*cda5da8dSAndroid Build Coastguard Worker# 2001-02-24 fl  Added encoding/Unicode/SafeTransport patches
24*cda5da8dSAndroid Build Coastguard Worker# 2001-02-26 fl  Added compare support to wrappers (0.9.9/1.0b1)
25*cda5da8dSAndroid Build Coastguard Worker# 2001-03-28 fl  Make sure response tuple is a singleton
26*cda5da8dSAndroid Build Coastguard Worker# 2001-03-29 fl  Don't require empty params element (from Nicholas Riley)
27*cda5da8dSAndroid Build Coastguard Worker# 2001-06-10 fl  Folded in _xmlrpclib accelerator support (1.0b2)
28*cda5da8dSAndroid Build Coastguard Worker# 2001-08-20 fl  Base xmlrpclib.Error on built-in Exception (from Paul Prescod)
29*cda5da8dSAndroid Build Coastguard Worker# 2001-09-03 fl  Allow Transport subclass to override getparser
30*cda5da8dSAndroid Build Coastguard Worker# 2001-09-10 fl  Lazy import of urllib, cgi, xmllib (20x import speedup)
31*cda5da8dSAndroid Build Coastguard Worker# 2001-10-01 fl  Remove containers from memo cache when done with them
32*cda5da8dSAndroid Build Coastguard Worker# 2001-10-01 fl  Use faster escape method (80% dumps speedup)
33*cda5da8dSAndroid Build Coastguard Worker# 2001-10-02 fl  More dumps microtuning
34*cda5da8dSAndroid Build Coastguard Worker# 2001-10-04 fl  Make sure import expat gets a parser (from Guido van Rossum)
35*cda5da8dSAndroid Build Coastguard Worker# 2001-10-10 sm  Allow long ints to be passed as ints if they don't overflow
36*cda5da8dSAndroid Build Coastguard Worker# 2001-10-17 sm  Test for int and long overflow (allows use on 64-bit systems)
37*cda5da8dSAndroid Build Coastguard Worker# 2001-11-12 fl  Use repr() to marshal doubles (from Paul Felix)
38*cda5da8dSAndroid Build Coastguard Worker# 2002-03-17 fl  Avoid buffered read when possible (from James Rucker)
39*cda5da8dSAndroid Build Coastguard Worker# 2002-04-07 fl  Added pythondoc comments
40*cda5da8dSAndroid Build Coastguard Worker# 2002-04-16 fl  Added __str__ methods to datetime/binary wrappers
41*cda5da8dSAndroid Build Coastguard Worker# 2002-05-15 fl  Added error constants (from Andrew Kuchling)
42*cda5da8dSAndroid Build Coastguard Worker# 2002-06-27 fl  Merged with Python CVS version
43*cda5da8dSAndroid Build Coastguard Worker# 2002-10-22 fl  Added basic authentication (based on code from Phillip Eby)
44*cda5da8dSAndroid Build Coastguard Worker# 2003-01-22 sm  Add support for the bool type
45*cda5da8dSAndroid Build Coastguard Worker# 2003-02-27 gvr Remove apply calls
46*cda5da8dSAndroid Build Coastguard Worker# 2003-04-24 sm  Use cStringIO if available
47*cda5da8dSAndroid Build Coastguard Worker# 2003-04-25 ak  Add support for nil
48*cda5da8dSAndroid Build Coastguard Worker# 2003-06-15 gn  Add support for time.struct_time
49*cda5da8dSAndroid Build Coastguard Worker# 2003-07-12 gp  Correct marshalling of Faults
50*cda5da8dSAndroid Build Coastguard Worker# 2003-10-31 mvl Add multicall support
51*cda5da8dSAndroid Build Coastguard Worker# 2004-08-20 mvl Bump minimum supported Python version to 2.1
52*cda5da8dSAndroid Build Coastguard Worker# 2014-12-02 ch/doko  Add workaround for gzip bomb vulnerability
53*cda5da8dSAndroid Build Coastguard Worker#
54*cda5da8dSAndroid Build Coastguard Worker# Copyright (c) 1999-2002 by Secret Labs AB.
55*cda5da8dSAndroid Build Coastguard Worker# Copyright (c) 1999-2002 by Fredrik Lundh.
56*cda5da8dSAndroid Build Coastguard Worker#
57*cda5da8dSAndroid Build Coastguard Worker# [email protected]
58*cda5da8dSAndroid Build Coastguard Worker# http://www.pythonware.com
59*cda5da8dSAndroid Build Coastguard Worker#
60*cda5da8dSAndroid Build Coastguard Worker# --------------------------------------------------------------------
61*cda5da8dSAndroid Build Coastguard Worker# The XML-RPC client interface is
62*cda5da8dSAndroid Build Coastguard Worker#
63*cda5da8dSAndroid Build Coastguard Worker# Copyright (c) 1999-2002 by Secret Labs AB
64*cda5da8dSAndroid Build Coastguard Worker# Copyright (c) 1999-2002 by Fredrik Lundh
65*cda5da8dSAndroid Build Coastguard Worker#
66*cda5da8dSAndroid Build Coastguard Worker# By obtaining, using, and/or copying this software and/or its
67*cda5da8dSAndroid Build Coastguard Worker# associated documentation, you agree that you have read, understood,
68*cda5da8dSAndroid Build Coastguard Worker# and will comply with the following terms and conditions:
69*cda5da8dSAndroid Build Coastguard Worker#
70*cda5da8dSAndroid Build Coastguard Worker# Permission to use, copy, modify, and distribute this software and
71*cda5da8dSAndroid Build Coastguard Worker# its associated documentation for any purpose and without fee is
72*cda5da8dSAndroid Build Coastguard Worker# hereby granted, provided that the above copyright notice appears in
73*cda5da8dSAndroid Build Coastguard Worker# all copies, and that both that copyright notice and this permission
74*cda5da8dSAndroid Build Coastguard Worker# notice appear in supporting documentation, and that the name of
75*cda5da8dSAndroid Build Coastguard Worker# Secret Labs AB or the author not be used in advertising or publicity
76*cda5da8dSAndroid Build Coastguard Worker# pertaining to distribution of the software without specific, written
77*cda5da8dSAndroid Build Coastguard Worker# prior permission.
78*cda5da8dSAndroid Build Coastguard Worker#
79*cda5da8dSAndroid Build Coastguard Worker# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
80*cda5da8dSAndroid Build Coastguard Worker# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
81*cda5da8dSAndroid Build Coastguard Worker# ABILITY AND FITNESS.  IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR
82*cda5da8dSAndroid Build Coastguard Worker# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
83*cda5da8dSAndroid Build Coastguard Worker# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
84*cda5da8dSAndroid Build Coastguard Worker# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
85*cda5da8dSAndroid Build Coastguard Worker# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
86*cda5da8dSAndroid Build Coastguard Worker# OF THIS SOFTWARE.
87*cda5da8dSAndroid Build Coastguard Worker# --------------------------------------------------------------------
88*cda5da8dSAndroid Build Coastguard Worker
89*cda5da8dSAndroid Build Coastguard Worker"""
90*cda5da8dSAndroid Build Coastguard WorkerAn XML-RPC client interface for Python.
91*cda5da8dSAndroid Build Coastguard Worker
92*cda5da8dSAndroid Build Coastguard WorkerThe marshalling and response parser code can also be used to
93*cda5da8dSAndroid Build Coastguard Workerimplement XML-RPC servers.
94*cda5da8dSAndroid Build Coastguard Worker
95*cda5da8dSAndroid Build Coastguard WorkerExported exceptions:
96*cda5da8dSAndroid Build Coastguard Worker
97*cda5da8dSAndroid Build Coastguard Worker  Error          Base class for client errors
98*cda5da8dSAndroid Build Coastguard Worker  ProtocolError  Indicates an HTTP protocol error
99*cda5da8dSAndroid Build Coastguard Worker  ResponseError  Indicates a broken response package
100*cda5da8dSAndroid Build Coastguard Worker  Fault          Indicates an XML-RPC fault package
101*cda5da8dSAndroid Build Coastguard Worker
102*cda5da8dSAndroid Build Coastguard WorkerExported classes:
103*cda5da8dSAndroid Build Coastguard Worker
104*cda5da8dSAndroid Build Coastguard Worker  ServerProxy    Represents a logical connection to an XML-RPC server
105*cda5da8dSAndroid Build Coastguard Worker
106*cda5da8dSAndroid Build Coastguard Worker  MultiCall      Executor of boxcared xmlrpc requests
107*cda5da8dSAndroid Build Coastguard Worker  DateTime       dateTime wrapper for an ISO 8601 string or time tuple or
108*cda5da8dSAndroid Build Coastguard Worker                 localtime integer value to generate a "dateTime.iso8601"
109*cda5da8dSAndroid Build Coastguard Worker                 XML-RPC value
110*cda5da8dSAndroid Build Coastguard Worker  Binary         binary data wrapper
111*cda5da8dSAndroid Build Coastguard Worker
112*cda5da8dSAndroid Build Coastguard Worker  Marshaller     Generate an XML-RPC params chunk from a Python data structure
113*cda5da8dSAndroid Build Coastguard Worker  Unmarshaller   Unmarshal an XML-RPC response from incoming XML event message
114*cda5da8dSAndroid Build Coastguard Worker  Transport      Handles an HTTP transaction to an XML-RPC server
115*cda5da8dSAndroid Build Coastguard Worker  SafeTransport  Handles an HTTPS transaction to an XML-RPC server
116*cda5da8dSAndroid Build Coastguard Worker
117*cda5da8dSAndroid Build Coastguard WorkerExported constants:
118*cda5da8dSAndroid Build Coastguard Worker
119*cda5da8dSAndroid Build Coastguard Worker  (none)
120*cda5da8dSAndroid Build Coastguard Worker
121*cda5da8dSAndroid Build Coastguard WorkerExported functions:
122*cda5da8dSAndroid Build Coastguard Worker
123*cda5da8dSAndroid Build Coastguard Worker  getparser      Create instance of the fastest available parser & attach
124*cda5da8dSAndroid Build Coastguard Worker                 to an unmarshalling object
125*cda5da8dSAndroid Build Coastguard Worker  dumps          Convert an argument tuple or a Fault instance to an XML-RPC
126*cda5da8dSAndroid Build Coastguard Worker                 request (or response, if the methodresponse option is used).
127*cda5da8dSAndroid Build Coastguard Worker  loads          Convert an XML-RPC packet to unmarshalled data plus a method
128*cda5da8dSAndroid Build Coastguard Worker                 name (None if not present).
129*cda5da8dSAndroid Build Coastguard Worker"""
130*cda5da8dSAndroid Build Coastguard Worker
131*cda5da8dSAndroid Build Coastguard Workerimport base64
132*cda5da8dSAndroid Build Coastguard Workerimport sys
133*cda5da8dSAndroid Build Coastguard Workerimport time
134*cda5da8dSAndroid Build Coastguard Workerfrom datetime import datetime
135*cda5da8dSAndroid Build Coastguard Workerfrom decimal import Decimal
136*cda5da8dSAndroid Build Coastguard Workerimport http.client
137*cda5da8dSAndroid Build Coastguard Workerimport urllib.parse
138*cda5da8dSAndroid Build Coastguard Workerfrom xml.parsers import expat
139*cda5da8dSAndroid Build Coastguard Workerimport errno
140*cda5da8dSAndroid Build Coastguard Workerfrom io import BytesIO
141*cda5da8dSAndroid Build Coastguard Workertry:
142*cda5da8dSAndroid Build Coastguard Worker    import gzip
143*cda5da8dSAndroid Build Coastguard Workerexcept ImportError:
144*cda5da8dSAndroid Build Coastguard Worker    gzip = None #python can be built without zlib/gzip support
145*cda5da8dSAndroid Build Coastguard Worker
146*cda5da8dSAndroid Build Coastguard Worker# --------------------------------------------------------------------
147*cda5da8dSAndroid Build Coastguard Worker# Internal stuff
148*cda5da8dSAndroid Build Coastguard Worker
149*cda5da8dSAndroid Build Coastguard Workerdef escape(s):
150*cda5da8dSAndroid Build Coastguard Worker    s = s.replace("&", "&")
151*cda5da8dSAndroid Build Coastguard Worker    s = s.replace("<", "&lt;")
152*cda5da8dSAndroid Build Coastguard Worker    return s.replace(">", "&gt;",)
153*cda5da8dSAndroid Build Coastguard Worker
154*cda5da8dSAndroid Build Coastguard Worker# used in User-Agent header sent
155*cda5da8dSAndroid Build Coastguard Worker__version__ = '%d.%d' % sys.version_info[:2]
156*cda5da8dSAndroid Build Coastguard Worker
157*cda5da8dSAndroid Build Coastguard Worker# xmlrpc integer limits
158*cda5da8dSAndroid Build Coastguard WorkerMAXINT =  2**31-1
159*cda5da8dSAndroid Build Coastguard WorkerMININT = -2**31
160*cda5da8dSAndroid Build Coastguard Worker
161*cda5da8dSAndroid Build Coastguard Worker# --------------------------------------------------------------------
162*cda5da8dSAndroid Build Coastguard Worker# Error constants (from Dan Libby's specification at
163*cda5da8dSAndroid Build Coastguard Worker# http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php)
164*cda5da8dSAndroid Build Coastguard Worker
165*cda5da8dSAndroid Build Coastguard Worker# Ranges of errors
166*cda5da8dSAndroid Build Coastguard WorkerPARSE_ERROR       = -32700
167*cda5da8dSAndroid Build Coastguard WorkerSERVER_ERROR      = -32600
168*cda5da8dSAndroid Build Coastguard WorkerAPPLICATION_ERROR = -32500
169*cda5da8dSAndroid Build Coastguard WorkerSYSTEM_ERROR      = -32400
170*cda5da8dSAndroid Build Coastguard WorkerTRANSPORT_ERROR   = -32300
171*cda5da8dSAndroid Build Coastguard Worker
172*cda5da8dSAndroid Build Coastguard Worker# Specific errors
173*cda5da8dSAndroid Build Coastguard WorkerNOT_WELLFORMED_ERROR  = -32700
174*cda5da8dSAndroid Build Coastguard WorkerUNSUPPORTED_ENCODING  = -32701
175*cda5da8dSAndroid Build Coastguard WorkerINVALID_ENCODING_CHAR = -32702
176*cda5da8dSAndroid Build Coastguard WorkerINVALID_XMLRPC        = -32600
177*cda5da8dSAndroid Build Coastguard WorkerMETHOD_NOT_FOUND      = -32601
178*cda5da8dSAndroid Build Coastguard WorkerINVALID_METHOD_PARAMS = -32602
179*cda5da8dSAndroid Build Coastguard WorkerINTERNAL_ERROR        = -32603
180*cda5da8dSAndroid Build Coastguard Worker
181*cda5da8dSAndroid Build Coastguard Worker# --------------------------------------------------------------------
182*cda5da8dSAndroid Build Coastguard Worker# Exceptions
183*cda5da8dSAndroid Build Coastguard Worker
184*cda5da8dSAndroid Build Coastguard Worker##
185*cda5da8dSAndroid Build Coastguard Worker# Base class for all kinds of client-side errors.
186*cda5da8dSAndroid Build Coastguard Worker
187*cda5da8dSAndroid Build Coastguard Workerclass Error(Exception):
188*cda5da8dSAndroid Build Coastguard Worker    """Base class for client errors."""
189*cda5da8dSAndroid Build Coastguard Worker    __str__ = object.__str__
190*cda5da8dSAndroid Build Coastguard Worker
191*cda5da8dSAndroid Build Coastguard Worker##
192*cda5da8dSAndroid Build Coastguard Worker# Indicates an HTTP-level protocol error.  This is raised by the HTTP
193*cda5da8dSAndroid Build Coastguard Worker# transport layer, if the server returns an error code other than 200
194*cda5da8dSAndroid Build Coastguard Worker# (OK).
195*cda5da8dSAndroid Build Coastguard Worker#
196*cda5da8dSAndroid Build Coastguard Worker# @param url The target URL.
197*cda5da8dSAndroid Build Coastguard Worker# @param errcode The HTTP error code.
198*cda5da8dSAndroid Build Coastguard Worker# @param errmsg The HTTP error message.
199*cda5da8dSAndroid Build Coastguard Worker# @param headers The HTTP header dictionary.
200*cda5da8dSAndroid Build Coastguard Worker
201*cda5da8dSAndroid Build Coastguard Workerclass ProtocolError(Error):
202*cda5da8dSAndroid Build Coastguard Worker    """Indicates an HTTP protocol error."""
203*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, url, errcode, errmsg, headers):
204*cda5da8dSAndroid Build Coastguard Worker        Error.__init__(self)
205*cda5da8dSAndroid Build Coastguard Worker        self.url = url
206*cda5da8dSAndroid Build Coastguard Worker        self.errcode = errcode
207*cda5da8dSAndroid Build Coastguard Worker        self.errmsg = errmsg
208*cda5da8dSAndroid Build Coastguard Worker        self.headers = headers
209*cda5da8dSAndroid Build Coastguard Worker    def __repr__(self):
210*cda5da8dSAndroid Build Coastguard Worker        return (
211*cda5da8dSAndroid Build Coastguard Worker            "<%s for %s: %s %s>" %
212*cda5da8dSAndroid Build Coastguard Worker            (self.__class__.__name__, self.url, self.errcode, self.errmsg)
213*cda5da8dSAndroid Build Coastguard Worker            )
214*cda5da8dSAndroid Build Coastguard Worker
215*cda5da8dSAndroid Build Coastguard Worker##
216*cda5da8dSAndroid Build Coastguard Worker# Indicates a broken XML-RPC response package.  This exception is
217*cda5da8dSAndroid Build Coastguard Worker# raised by the unmarshalling layer, if the XML-RPC response is
218*cda5da8dSAndroid Build Coastguard Worker# malformed.
219*cda5da8dSAndroid Build Coastguard Worker
220*cda5da8dSAndroid Build Coastguard Workerclass ResponseError(Error):
221*cda5da8dSAndroid Build Coastguard Worker    """Indicates a broken response package."""
222*cda5da8dSAndroid Build Coastguard Worker    pass
223*cda5da8dSAndroid Build Coastguard Worker
224*cda5da8dSAndroid Build Coastguard Worker##
225*cda5da8dSAndroid Build Coastguard Worker# Indicates an XML-RPC fault response package.  This exception is
226*cda5da8dSAndroid Build Coastguard Worker# raised by the unmarshalling layer, if the XML-RPC response contains
227*cda5da8dSAndroid Build Coastguard Worker# a fault string.  This exception can also be used as a class, to
228*cda5da8dSAndroid Build Coastguard Worker# generate a fault XML-RPC message.
229*cda5da8dSAndroid Build Coastguard Worker#
230*cda5da8dSAndroid Build Coastguard Worker# @param faultCode The XML-RPC fault code.
231*cda5da8dSAndroid Build Coastguard Worker# @param faultString The XML-RPC fault string.
232*cda5da8dSAndroid Build Coastguard Worker
233*cda5da8dSAndroid Build Coastguard Workerclass Fault(Error):
234*cda5da8dSAndroid Build Coastguard Worker    """Indicates an XML-RPC fault package."""
235*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, faultCode, faultString, **extra):
236*cda5da8dSAndroid Build Coastguard Worker        Error.__init__(self)
237*cda5da8dSAndroid Build Coastguard Worker        self.faultCode = faultCode
238*cda5da8dSAndroid Build Coastguard Worker        self.faultString = faultString
239*cda5da8dSAndroid Build Coastguard Worker    def __repr__(self):
240*cda5da8dSAndroid Build Coastguard Worker        return "<%s %s: %r>" % (self.__class__.__name__,
241*cda5da8dSAndroid Build Coastguard Worker                                self.faultCode, self.faultString)
242*cda5da8dSAndroid Build Coastguard Worker
243*cda5da8dSAndroid Build Coastguard Worker# --------------------------------------------------------------------
244*cda5da8dSAndroid Build Coastguard Worker# Special values
245*cda5da8dSAndroid Build Coastguard Worker
246*cda5da8dSAndroid Build Coastguard Worker##
247*cda5da8dSAndroid Build Coastguard Worker# Backwards compatibility
248*cda5da8dSAndroid Build Coastguard Worker
249*cda5da8dSAndroid Build Coastguard Workerboolean = Boolean = bool
250*cda5da8dSAndroid Build Coastguard Worker
251*cda5da8dSAndroid Build Coastguard Worker##
252*cda5da8dSAndroid Build Coastguard Worker# Wrapper for XML-RPC DateTime values.  This converts a time value to
253*cda5da8dSAndroid Build Coastguard Worker# the format used by XML-RPC.
254*cda5da8dSAndroid Build Coastguard Worker# <p>
255*cda5da8dSAndroid Build Coastguard Worker# The value can be given as a datetime object, as a string in the
256*cda5da8dSAndroid Build Coastguard Worker# format "yyyymmddThh:mm:ss", as a 9-item time tuple (as returned by
257*cda5da8dSAndroid Build Coastguard Worker# time.localtime()), or an integer value (as returned by time.time()).
258*cda5da8dSAndroid Build Coastguard Worker# The wrapper uses time.localtime() to convert an integer to a time
259*cda5da8dSAndroid Build Coastguard Worker# tuple.
260*cda5da8dSAndroid Build Coastguard Worker#
261*cda5da8dSAndroid Build Coastguard Worker# @param value The time, given as a datetime object, an ISO 8601 string,
262*cda5da8dSAndroid Build Coastguard Worker#              a time tuple, or an integer time value.
263*cda5da8dSAndroid Build Coastguard Worker
264*cda5da8dSAndroid Build Coastguard Worker
265*cda5da8dSAndroid Build Coastguard Worker# Issue #13305: different format codes across platforms
266*cda5da8dSAndroid Build Coastguard Worker_day0 = datetime(1, 1, 1)
267*cda5da8dSAndroid Build Coastguard Workerdef _try(fmt):
268*cda5da8dSAndroid Build Coastguard Worker    try:
269*cda5da8dSAndroid Build Coastguard Worker        return _day0.strftime(fmt) == '0001'
270*cda5da8dSAndroid Build Coastguard Worker    except ValueError:
271*cda5da8dSAndroid Build Coastguard Worker        return False
272*cda5da8dSAndroid Build Coastguard Workerif _try('%Y'):      # Mac OS X
273*cda5da8dSAndroid Build Coastguard Worker    def _iso8601_format(value):
274*cda5da8dSAndroid Build Coastguard Worker        return value.strftime("%Y%m%dT%H:%M:%S")
275*cda5da8dSAndroid Build Coastguard Workerelif _try('%4Y'):   # Linux
276*cda5da8dSAndroid Build Coastguard Worker    def _iso8601_format(value):
277*cda5da8dSAndroid Build Coastguard Worker        return value.strftime("%4Y%m%dT%H:%M:%S")
278*cda5da8dSAndroid Build Coastguard Workerelse:
279*cda5da8dSAndroid Build Coastguard Worker    def _iso8601_format(value):
280*cda5da8dSAndroid Build Coastguard Worker        return value.strftime("%Y%m%dT%H:%M:%S").zfill(17)
281*cda5da8dSAndroid Build Coastguard Workerdel _day0
282*cda5da8dSAndroid Build Coastguard Workerdel _try
283*cda5da8dSAndroid Build Coastguard Worker
284*cda5da8dSAndroid Build Coastguard Worker
285*cda5da8dSAndroid Build Coastguard Workerdef _strftime(value):
286*cda5da8dSAndroid Build Coastguard Worker    if isinstance(value, datetime):
287*cda5da8dSAndroid Build Coastguard Worker        return _iso8601_format(value)
288*cda5da8dSAndroid Build Coastguard Worker
289*cda5da8dSAndroid Build Coastguard Worker    if not isinstance(value, (tuple, time.struct_time)):
290*cda5da8dSAndroid Build Coastguard Worker        if value == 0:
291*cda5da8dSAndroid Build Coastguard Worker            value = time.time()
292*cda5da8dSAndroid Build Coastguard Worker        value = time.localtime(value)
293*cda5da8dSAndroid Build Coastguard Worker
294*cda5da8dSAndroid Build Coastguard Worker    return "%04d%02d%02dT%02d:%02d:%02d" % value[:6]
295*cda5da8dSAndroid Build Coastguard Worker
296*cda5da8dSAndroid Build Coastguard Workerclass DateTime:
297*cda5da8dSAndroid Build Coastguard Worker    """DateTime wrapper for an ISO 8601 string or time tuple or
298*cda5da8dSAndroid Build Coastguard Worker    localtime integer value to generate 'dateTime.iso8601' XML-RPC
299*cda5da8dSAndroid Build Coastguard Worker    value.
300*cda5da8dSAndroid Build Coastguard Worker    """
301*cda5da8dSAndroid Build Coastguard Worker
302*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, value=0):
303*cda5da8dSAndroid Build Coastguard Worker        if isinstance(value, str):
304*cda5da8dSAndroid Build Coastguard Worker            self.value = value
305*cda5da8dSAndroid Build Coastguard Worker        else:
306*cda5da8dSAndroid Build Coastguard Worker            self.value = _strftime(value)
307*cda5da8dSAndroid Build Coastguard Worker
308*cda5da8dSAndroid Build Coastguard Worker    def make_comparable(self, other):
309*cda5da8dSAndroid Build Coastguard Worker        if isinstance(other, DateTime):
310*cda5da8dSAndroid Build Coastguard Worker            s = self.value
311*cda5da8dSAndroid Build Coastguard Worker            o = other.value
312*cda5da8dSAndroid Build Coastguard Worker        elif isinstance(other, datetime):
313*cda5da8dSAndroid Build Coastguard Worker            s = self.value
314*cda5da8dSAndroid Build Coastguard Worker            o = _iso8601_format(other)
315*cda5da8dSAndroid Build Coastguard Worker        elif isinstance(other, str):
316*cda5da8dSAndroid Build Coastguard Worker            s = self.value
317*cda5da8dSAndroid Build Coastguard Worker            o = other
318*cda5da8dSAndroid Build Coastguard Worker        elif hasattr(other, "timetuple"):
319*cda5da8dSAndroid Build Coastguard Worker            s = self.timetuple()
320*cda5da8dSAndroid Build Coastguard Worker            o = other.timetuple()
321*cda5da8dSAndroid Build Coastguard Worker        else:
322*cda5da8dSAndroid Build Coastguard Worker            s = self
323*cda5da8dSAndroid Build Coastguard Worker            o = NotImplemented
324*cda5da8dSAndroid Build Coastguard Worker        return s, o
325*cda5da8dSAndroid Build Coastguard Worker
326*cda5da8dSAndroid Build Coastguard Worker    def __lt__(self, other):
327*cda5da8dSAndroid Build Coastguard Worker        s, o = self.make_comparable(other)
328*cda5da8dSAndroid Build Coastguard Worker        if o is NotImplemented:
329*cda5da8dSAndroid Build Coastguard Worker            return NotImplemented
330*cda5da8dSAndroid Build Coastguard Worker        return s < o
331*cda5da8dSAndroid Build Coastguard Worker
332*cda5da8dSAndroid Build Coastguard Worker    def __le__(self, other):
333*cda5da8dSAndroid Build Coastguard Worker        s, o = self.make_comparable(other)
334*cda5da8dSAndroid Build Coastguard Worker        if o is NotImplemented:
335*cda5da8dSAndroid Build Coastguard Worker            return NotImplemented
336*cda5da8dSAndroid Build Coastguard Worker        return s <= o
337*cda5da8dSAndroid Build Coastguard Worker
338*cda5da8dSAndroid Build Coastguard Worker    def __gt__(self, other):
339*cda5da8dSAndroid Build Coastguard Worker        s, o = self.make_comparable(other)
340*cda5da8dSAndroid Build Coastguard Worker        if o is NotImplemented:
341*cda5da8dSAndroid Build Coastguard Worker            return NotImplemented
342*cda5da8dSAndroid Build Coastguard Worker        return s > o
343*cda5da8dSAndroid Build Coastguard Worker
344*cda5da8dSAndroid Build Coastguard Worker    def __ge__(self, other):
345*cda5da8dSAndroid Build Coastguard Worker        s, o = self.make_comparable(other)
346*cda5da8dSAndroid Build Coastguard Worker        if o is NotImplemented:
347*cda5da8dSAndroid Build Coastguard Worker            return NotImplemented
348*cda5da8dSAndroid Build Coastguard Worker        return s >= o
349*cda5da8dSAndroid Build Coastguard Worker
350*cda5da8dSAndroid Build Coastguard Worker    def __eq__(self, other):
351*cda5da8dSAndroid Build Coastguard Worker        s, o = self.make_comparable(other)
352*cda5da8dSAndroid Build Coastguard Worker        if o is NotImplemented:
353*cda5da8dSAndroid Build Coastguard Worker            return NotImplemented
354*cda5da8dSAndroid Build Coastguard Worker        return s == o
355*cda5da8dSAndroid Build Coastguard Worker
356*cda5da8dSAndroid Build Coastguard Worker    def timetuple(self):
357*cda5da8dSAndroid Build Coastguard Worker        return time.strptime(self.value, "%Y%m%dT%H:%M:%S")
358*cda5da8dSAndroid Build Coastguard Worker
359*cda5da8dSAndroid Build Coastguard Worker    ##
360*cda5da8dSAndroid Build Coastguard Worker    # Get date/time value.
361*cda5da8dSAndroid Build Coastguard Worker    #
362*cda5da8dSAndroid Build Coastguard Worker    # @return Date/time value, as an ISO 8601 string.
363*cda5da8dSAndroid Build Coastguard Worker
364*cda5da8dSAndroid Build Coastguard Worker    def __str__(self):
365*cda5da8dSAndroid Build Coastguard Worker        return self.value
366*cda5da8dSAndroid Build Coastguard Worker
367*cda5da8dSAndroid Build Coastguard Worker    def __repr__(self):
368*cda5da8dSAndroid Build Coastguard Worker        return "<%s %r at %#x>" % (self.__class__.__name__, self.value, id(self))
369*cda5da8dSAndroid Build Coastguard Worker
370*cda5da8dSAndroid Build Coastguard Worker    def decode(self, data):
371*cda5da8dSAndroid Build Coastguard Worker        self.value = str(data).strip()
372*cda5da8dSAndroid Build Coastguard Worker
373*cda5da8dSAndroid Build Coastguard Worker    def encode(self, out):
374*cda5da8dSAndroid Build Coastguard Worker        out.write("<value><dateTime.iso8601>")
375*cda5da8dSAndroid Build Coastguard Worker        out.write(self.value)
376*cda5da8dSAndroid Build Coastguard Worker        out.write("</dateTime.iso8601></value>\n")
377*cda5da8dSAndroid Build Coastguard Worker
378*cda5da8dSAndroid Build Coastguard Workerdef _datetime(data):
379*cda5da8dSAndroid Build Coastguard Worker    # decode xml element contents into a DateTime structure.
380*cda5da8dSAndroid Build Coastguard Worker    value = DateTime()
381*cda5da8dSAndroid Build Coastguard Worker    value.decode(data)
382*cda5da8dSAndroid Build Coastguard Worker    return value
383*cda5da8dSAndroid Build Coastguard Worker
384*cda5da8dSAndroid Build Coastguard Workerdef _datetime_type(data):
385*cda5da8dSAndroid Build Coastguard Worker    return datetime.strptime(data, "%Y%m%dT%H:%M:%S")
386*cda5da8dSAndroid Build Coastguard Worker
387*cda5da8dSAndroid Build Coastguard Worker##
388*cda5da8dSAndroid Build Coastguard Worker# Wrapper for binary data.  This can be used to transport any kind
389*cda5da8dSAndroid Build Coastguard Worker# of binary data over XML-RPC, using BASE64 encoding.
390*cda5da8dSAndroid Build Coastguard Worker#
391*cda5da8dSAndroid Build Coastguard Worker# @param data An 8-bit string containing arbitrary data.
392*cda5da8dSAndroid Build Coastguard Worker
393*cda5da8dSAndroid Build Coastguard Workerclass Binary:
394*cda5da8dSAndroid Build Coastguard Worker    """Wrapper for binary data."""
395*cda5da8dSAndroid Build Coastguard Worker
396*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, data=None):
397*cda5da8dSAndroid Build Coastguard Worker        if data is None:
398*cda5da8dSAndroid Build Coastguard Worker            data = b""
399*cda5da8dSAndroid Build Coastguard Worker        else:
400*cda5da8dSAndroid Build Coastguard Worker            if not isinstance(data, (bytes, bytearray)):
401*cda5da8dSAndroid Build Coastguard Worker                raise TypeError("expected bytes or bytearray, not %s" %
402*cda5da8dSAndroid Build Coastguard Worker                                data.__class__.__name__)
403*cda5da8dSAndroid Build Coastguard Worker            data = bytes(data)  # Make a copy of the bytes!
404*cda5da8dSAndroid Build Coastguard Worker        self.data = data
405*cda5da8dSAndroid Build Coastguard Worker
406*cda5da8dSAndroid Build Coastguard Worker    ##
407*cda5da8dSAndroid Build Coastguard Worker    # Get buffer contents.
408*cda5da8dSAndroid Build Coastguard Worker    #
409*cda5da8dSAndroid Build Coastguard Worker    # @return Buffer contents, as an 8-bit string.
410*cda5da8dSAndroid Build Coastguard Worker
411*cda5da8dSAndroid Build Coastguard Worker    def __str__(self):
412*cda5da8dSAndroid Build Coastguard Worker        return str(self.data, "latin-1")  # XXX encoding?!
413*cda5da8dSAndroid Build Coastguard Worker
414*cda5da8dSAndroid Build Coastguard Worker    def __eq__(self, other):
415*cda5da8dSAndroid Build Coastguard Worker        if isinstance(other, Binary):
416*cda5da8dSAndroid Build Coastguard Worker            other = other.data
417*cda5da8dSAndroid Build Coastguard Worker        return self.data == other
418*cda5da8dSAndroid Build Coastguard Worker
419*cda5da8dSAndroid Build Coastguard Worker    def decode(self, data):
420*cda5da8dSAndroid Build Coastguard Worker        self.data = base64.decodebytes(data)
421*cda5da8dSAndroid Build Coastguard Worker
422*cda5da8dSAndroid Build Coastguard Worker    def encode(self, out):
423*cda5da8dSAndroid Build Coastguard Worker        out.write("<value><base64>\n")
424*cda5da8dSAndroid Build Coastguard Worker        encoded = base64.encodebytes(self.data)
425*cda5da8dSAndroid Build Coastguard Worker        out.write(encoded.decode('ascii'))
426*cda5da8dSAndroid Build Coastguard Worker        out.write("</base64></value>\n")
427*cda5da8dSAndroid Build Coastguard Worker
428*cda5da8dSAndroid Build Coastguard Workerdef _binary(data):
429*cda5da8dSAndroid Build Coastguard Worker    # decode xml element contents into a Binary structure
430*cda5da8dSAndroid Build Coastguard Worker    value = Binary()
431*cda5da8dSAndroid Build Coastguard Worker    value.decode(data)
432*cda5da8dSAndroid Build Coastguard Worker    return value
433*cda5da8dSAndroid Build Coastguard Worker
434*cda5da8dSAndroid Build Coastguard WorkerWRAPPERS = (DateTime, Binary)
435*cda5da8dSAndroid Build Coastguard Worker
436*cda5da8dSAndroid Build Coastguard Worker# --------------------------------------------------------------------
437*cda5da8dSAndroid Build Coastguard Worker# XML parsers
438*cda5da8dSAndroid Build Coastguard Worker
439*cda5da8dSAndroid Build Coastguard Workerclass ExpatParser:
440*cda5da8dSAndroid Build Coastguard Worker    # fast expat parser for Python 2.0 and later.
441*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, target):
442*cda5da8dSAndroid Build Coastguard Worker        self._parser = parser = expat.ParserCreate(None, None)
443*cda5da8dSAndroid Build Coastguard Worker        self._target = target
444*cda5da8dSAndroid Build Coastguard Worker        parser.StartElementHandler = target.start
445*cda5da8dSAndroid Build Coastguard Worker        parser.EndElementHandler = target.end
446*cda5da8dSAndroid Build Coastguard Worker        parser.CharacterDataHandler = target.data
447*cda5da8dSAndroid Build Coastguard Worker        encoding = None
448*cda5da8dSAndroid Build Coastguard Worker        target.xml(encoding, None)
449*cda5da8dSAndroid Build Coastguard Worker
450*cda5da8dSAndroid Build Coastguard Worker    def feed(self, data):
451*cda5da8dSAndroid Build Coastguard Worker        self._parser.Parse(data, False)
452*cda5da8dSAndroid Build Coastguard Worker
453*cda5da8dSAndroid Build Coastguard Worker    def close(self):
454*cda5da8dSAndroid Build Coastguard Worker        try:
455*cda5da8dSAndroid Build Coastguard Worker            parser = self._parser
456*cda5da8dSAndroid Build Coastguard Worker        except AttributeError:
457*cda5da8dSAndroid Build Coastguard Worker            pass
458*cda5da8dSAndroid Build Coastguard Worker        else:
459*cda5da8dSAndroid Build Coastguard Worker            del self._target, self._parser # get rid of circular references
460*cda5da8dSAndroid Build Coastguard Worker            parser.Parse(b"", True) # end of data
461*cda5da8dSAndroid Build Coastguard Worker
462*cda5da8dSAndroid Build Coastguard Worker# --------------------------------------------------------------------
463*cda5da8dSAndroid Build Coastguard Worker# XML-RPC marshalling and unmarshalling code
464*cda5da8dSAndroid Build Coastguard Worker
465*cda5da8dSAndroid Build Coastguard Worker##
466*cda5da8dSAndroid Build Coastguard Worker# XML-RPC marshaller.
467*cda5da8dSAndroid Build Coastguard Worker#
468*cda5da8dSAndroid Build Coastguard Worker# @param encoding Default encoding for 8-bit strings.  The default
469*cda5da8dSAndroid Build Coastguard Worker#     value is None (interpreted as UTF-8).
470*cda5da8dSAndroid Build Coastguard Worker# @see dumps
471*cda5da8dSAndroid Build Coastguard Worker
472*cda5da8dSAndroid Build Coastguard Workerclass Marshaller:
473*cda5da8dSAndroid Build Coastguard Worker    """Generate an XML-RPC params chunk from a Python data structure.
474*cda5da8dSAndroid Build Coastguard Worker
475*cda5da8dSAndroid Build Coastguard Worker    Create a Marshaller instance for each set of parameters, and use
476*cda5da8dSAndroid Build Coastguard Worker    the "dumps" method to convert your data (represented as a tuple)
477*cda5da8dSAndroid Build Coastguard Worker    to an XML-RPC params chunk.  To write a fault response, pass a
478*cda5da8dSAndroid Build Coastguard Worker    Fault instance instead.  You may prefer to use the "dumps" module
479*cda5da8dSAndroid Build Coastguard Worker    function for this purpose.
480*cda5da8dSAndroid Build Coastguard Worker    """
481*cda5da8dSAndroid Build Coastguard Worker
482*cda5da8dSAndroid Build Coastguard Worker    # by the way, if you don't understand what's going on in here,
483*cda5da8dSAndroid Build Coastguard Worker    # that's perfectly ok.
484*cda5da8dSAndroid Build Coastguard Worker
485*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, encoding=None, allow_none=False):
486*cda5da8dSAndroid Build Coastguard Worker        self.memo = {}
487*cda5da8dSAndroid Build Coastguard Worker        self.data = None
488*cda5da8dSAndroid Build Coastguard Worker        self.encoding = encoding
489*cda5da8dSAndroid Build Coastguard Worker        self.allow_none = allow_none
490*cda5da8dSAndroid Build Coastguard Worker
491*cda5da8dSAndroid Build Coastguard Worker    dispatch = {}
492*cda5da8dSAndroid Build Coastguard Worker
493*cda5da8dSAndroid Build Coastguard Worker    def dumps(self, values):
494*cda5da8dSAndroid Build Coastguard Worker        out = []
495*cda5da8dSAndroid Build Coastguard Worker        write = out.append
496*cda5da8dSAndroid Build Coastguard Worker        dump = self.__dump
497*cda5da8dSAndroid Build Coastguard Worker        if isinstance(values, Fault):
498*cda5da8dSAndroid Build Coastguard Worker            # fault instance
499*cda5da8dSAndroid Build Coastguard Worker            write("<fault>\n")
500*cda5da8dSAndroid Build Coastguard Worker            dump({'faultCode': values.faultCode,
501*cda5da8dSAndroid Build Coastguard Worker                  'faultString': values.faultString},
502*cda5da8dSAndroid Build Coastguard Worker                 write)
503*cda5da8dSAndroid Build Coastguard Worker            write("</fault>\n")
504*cda5da8dSAndroid Build Coastguard Worker        else:
505*cda5da8dSAndroid Build Coastguard Worker            # parameter block
506*cda5da8dSAndroid Build Coastguard Worker            # FIXME: the xml-rpc specification allows us to leave out
507*cda5da8dSAndroid Build Coastguard Worker            # the entire <params> block if there are no parameters.
508*cda5da8dSAndroid Build Coastguard Worker            # however, changing this may break older code (including
509*cda5da8dSAndroid Build Coastguard Worker            # old versions of xmlrpclib.py), so this is better left as
510*cda5da8dSAndroid Build Coastguard Worker            # is for now.  See @XMLRPC3 for more information. /F
511*cda5da8dSAndroid Build Coastguard Worker            write("<params>\n")
512*cda5da8dSAndroid Build Coastguard Worker            for v in values:
513*cda5da8dSAndroid Build Coastguard Worker                write("<param>\n")
514*cda5da8dSAndroid Build Coastguard Worker                dump(v, write)
515*cda5da8dSAndroid Build Coastguard Worker                write("</param>\n")
516*cda5da8dSAndroid Build Coastguard Worker            write("</params>\n")
517*cda5da8dSAndroid Build Coastguard Worker        result = "".join(out)
518*cda5da8dSAndroid Build Coastguard Worker        return result
519*cda5da8dSAndroid Build Coastguard Worker
520*cda5da8dSAndroid Build Coastguard Worker    def __dump(self, value, write):
521*cda5da8dSAndroid Build Coastguard Worker        try:
522*cda5da8dSAndroid Build Coastguard Worker            f = self.dispatch[type(value)]
523*cda5da8dSAndroid Build Coastguard Worker        except KeyError:
524*cda5da8dSAndroid Build Coastguard Worker            # check if this object can be marshalled as a structure
525*cda5da8dSAndroid Build Coastguard Worker            if not hasattr(value, '__dict__'):
526*cda5da8dSAndroid Build Coastguard Worker                raise TypeError("cannot marshal %s objects" % type(value))
527*cda5da8dSAndroid Build Coastguard Worker            # check if this class is a sub-class of a basic type,
528*cda5da8dSAndroid Build Coastguard Worker            # because we don't know how to marshal these types
529*cda5da8dSAndroid Build Coastguard Worker            # (e.g. a string sub-class)
530*cda5da8dSAndroid Build Coastguard Worker            for type_ in type(value).__mro__:
531*cda5da8dSAndroid Build Coastguard Worker                if type_ in self.dispatch.keys():
532*cda5da8dSAndroid Build Coastguard Worker                    raise TypeError("cannot marshal %s objects" % type(value))
533*cda5da8dSAndroid Build Coastguard Worker            # XXX(twouters): using "_arbitrary_instance" as key as a quick-fix
534*cda5da8dSAndroid Build Coastguard Worker            # for the p3yk merge, this should probably be fixed more neatly.
535*cda5da8dSAndroid Build Coastguard Worker            f = self.dispatch["_arbitrary_instance"]
536*cda5da8dSAndroid Build Coastguard Worker        f(self, value, write)
537*cda5da8dSAndroid Build Coastguard Worker
538*cda5da8dSAndroid Build Coastguard Worker    def dump_nil (self, value, write):
539*cda5da8dSAndroid Build Coastguard Worker        if not self.allow_none:
540*cda5da8dSAndroid Build Coastguard Worker            raise TypeError("cannot marshal None unless allow_none is enabled")
541*cda5da8dSAndroid Build Coastguard Worker        write("<value><nil/></value>")
542*cda5da8dSAndroid Build Coastguard Worker    dispatch[type(None)] = dump_nil
543*cda5da8dSAndroid Build Coastguard Worker
544*cda5da8dSAndroid Build Coastguard Worker    def dump_bool(self, value, write):
545*cda5da8dSAndroid Build Coastguard Worker        write("<value><boolean>")
546*cda5da8dSAndroid Build Coastguard Worker        write(value and "1" or "0")
547*cda5da8dSAndroid Build Coastguard Worker        write("</boolean></value>\n")
548*cda5da8dSAndroid Build Coastguard Worker    dispatch[bool] = dump_bool
549*cda5da8dSAndroid Build Coastguard Worker
550*cda5da8dSAndroid Build Coastguard Worker    def dump_long(self, value, write):
551*cda5da8dSAndroid Build Coastguard Worker        if value > MAXINT or value < MININT:
552*cda5da8dSAndroid Build Coastguard Worker            raise OverflowError("int exceeds XML-RPC limits")
553*cda5da8dSAndroid Build Coastguard Worker        write("<value><int>")
554*cda5da8dSAndroid Build Coastguard Worker        write(str(int(value)))
555*cda5da8dSAndroid Build Coastguard Worker        write("</int></value>\n")
556*cda5da8dSAndroid Build Coastguard Worker    dispatch[int] = dump_long
557*cda5da8dSAndroid Build Coastguard Worker
558*cda5da8dSAndroid Build Coastguard Worker    # backward compatible
559*cda5da8dSAndroid Build Coastguard Worker    dump_int = dump_long
560*cda5da8dSAndroid Build Coastguard Worker
561*cda5da8dSAndroid Build Coastguard Worker    def dump_double(self, value, write):
562*cda5da8dSAndroid Build Coastguard Worker        write("<value><double>")
563*cda5da8dSAndroid Build Coastguard Worker        write(repr(value))
564*cda5da8dSAndroid Build Coastguard Worker        write("</double></value>\n")
565*cda5da8dSAndroid Build Coastguard Worker    dispatch[float] = dump_double
566*cda5da8dSAndroid Build Coastguard Worker
567*cda5da8dSAndroid Build Coastguard Worker    def dump_unicode(self, value, write, escape=escape):
568*cda5da8dSAndroid Build Coastguard Worker        write("<value><string>")
569*cda5da8dSAndroid Build Coastguard Worker        write(escape(value))
570*cda5da8dSAndroid Build Coastguard Worker        write("</string></value>\n")
571*cda5da8dSAndroid Build Coastguard Worker    dispatch[str] = dump_unicode
572*cda5da8dSAndroid Build Coastguard Worker
573*cda5da8dSAndroid Build Coastguard Worker    def dump_bytes(self, value, write):
574*cda5da8dSAndroid Build Coastguard Worker        write("<value><base64>\n")
575*cda5da8dSAndroid Build Coastguard Worker        encoded = base64.encodebytes(value)
576*cda5da8dSAndroid Build Coastguard Worker        write(encoded.decode('ascii'))
577*cda5da8dSAndroid Build Coastguard Worker        write("</base64></value>\n")
578*cda5da8dSAndroid Build Coastguard Worker    dispatch[bytes] = dump_bytes
579*cda5da8dSAndroid Build Coastguard Worker    dispatch[bytearray] = dump_bytes
580*cda5da8dSAndroid Build Coastguard Worker
581*cda5da8dSAndroid Build Coastguard Worker    def dump_array(self, value, write):
582*cda5da8dSAndroid Build Coastguard Worker        i = id(value)
583*cda5da8dSAndroid Build Coastguard Worker        if i in self.memo:
584*cda5da8dSAndroid Build Coastguard Worker            raise TypeError("cannot marshal recursive sequences")
585*cda5da8dSAndroid Build Coastguard Worker        self.memo[i] = None
586*cda5da8dSAndroid Build Coastguard Worker        dump = self.__dump
587*cda5da8dSAndroid Build Coastguard Worker        write("<value><array><data>\n")
588*cda5da8dSAndroid Build Coastguard Worker        for v in value:
589*cda5da8dSAndroid Build Coastguard Worker            dump(v, write)
590*cda5da8dSAndroid Build Coastguard Worker        write("</data></array></value>\n")
591*cda5da8dSAndroid Build Coastguard Worker        del self.memo[i]
592*cda5da8dSAndroid Build Coastguard Worker    dispatch[tuple] = dump_array
593*cda5da8dSAndroid Build Coastguard Worker    dispatch[list] = dump_array
594*cda5da8dSAndroid Build Coastguard Worker
595*cda5da8dSAndroid Build Coastguard Worker    def dump_struct(self, value, write, escape=escape):
596*cda5da8dSAndroid Build Coastguard Worker        i = id(value)
597*cda5da8dSAndroid Build Coastguard Worker        if i in self.memo:
598*cda5da8dSAndroid Build Coastguard Worker            raise TypeError("cannot marshal recursive dictionaries")
599*cda5da8dSAndroid Build Coastguard Worker        self.memo[i] = None
600*cda5da8dSAndroid Build Coastguard Worker        dump = self.__dump
601*cda5da8dSAndroid Build Coastguard Worker        write("<value><struct>\n")
602*cda5da8dSAndroid Build Coastguard Worker        for k, v in value.items():
603*cda5da8dSAndroid Build Coastguard Worker            write("<member>\n")
604*cda5da8dSAndroid Build Coastguard Worker            if not isinstance(k, str):
605*cda5da8dSAndroid Build Coastguard Worker                raise TypeError("dictionary key must be string")
606*cda5da8dSAndroid Build Coastguard Worker            write("<name>%s</name>\n" % escape(k))
607*cda5da8dSAndroid Build Coastguard Worker            dump(v, write)
608*cda5da8dSAndroid Build Coastguard Worker            write("</member>\n")
609*cda5da8dSAndroid Build Coastguard Worker        write("</struct></value>\n")
610*cda5da8dSAndroid Build Coastguard Worker        del self.memo[i]
611*cda5da8dSAndroid Build Coastguard Worker    dispatch[dict] = dump_struct
612*cda5da8dSAndroid Build Coastguard Worker
613*cda5da8dSAndroid Build Coastguard Worker    def dump_datetime(self, value, write):
614*cda5da8dSAndroid Build Coastguard Worker        write("<value><dateTime.iso8601>")
615*cda5da8dSAndroid Build Coastguard Worker        write(_strftime(value))
616*cda5da8dSAndroid Build Coastguard Worker        write("</dateTime.iso8601></value>\n")
617*cda5da8dSAndroid Build Coastguard Worker    dispatch[datetime] = dump_datetime
618*cda5da8dSAndroid Build Coastguard Worker
619*cda5da8dSAndroid Build Coastguard Worker    def dump_instance(self, value, write):
620*cda5da8dSAndroid Build Coastguard Worker        # check for special wrappers
621*cda5da8dSAndroid Build Coastguard Worker        if value.__class__ in WRAPPERS:
622*cda5da8dSAndroid Build Coastguard Worker            self.write = write
623*cda5da8dSAndroid Build Coastguard Worker            value.encode(self)
624*cda5da8dSAndroid Build Coastguard Worker            del self.write
625*cda5da8dSAndroid Build Coastguard Worker        else:
626*cda5da8dSAndroid Build Coastguard Worker            # store instance attributes as a struct (really?)
627*cda5da8dSAndroid Build Coastguard Worker            self.dump_struct(value.__dict__, write)
628*cda5da8dSAndroid Build Coastguard Worker    dispatch[DateTime] = dump_instance
629*cda5da8dSAndroid Build Coastguard Worker    dispatch[Binary] = dump_instance
630*cda5da8dSAndroid Build Coastguard Worker    # XXX(twouters): using "_arbitrary_instance" as key as a quick-fix
631*cda5da8dSAndroid Build Coastguard Worker    # for the p3yk merge, this should probably be fixed more neatly.
632*cda5da8dSAndroid Build Coastguard Worker    dispatch["_arbitrary_instance"] = dump_instance
633*cda5da8dSAndroid Build Coastguard Worker
634*cda5da8dSAndroid Build Coastguard Worker##
635*cda5da8dSAndroid Build Coastguard Worker# XML-RPC unmarshaller.
636*cda5da8dSAndroid Build Coastguard Worker#
637*cda5da8dSAndroid Build Coastguard Worker# @see loads
638*cda5da8dSAndroid Build Coastguard Worker
639*cda5da8dSAndroid Build Coastguard Workerclass Unmarshaller:
640*cda5da8dSAndroid Build Coastguard Worker    """Unmarshal an XML-RPC response, based on incoming XML event
641*cda5da8dSAndroid Build Coastguard Worker    messages (start, data, end).  Call close() to get the resulting
642*cda5da8dSAndroid Build Coastguard Worker    data structure.
643*cda5da8dSAndroid Build Coastguard Worker
644*cda5da8dSAndroid Build Coastguard Worker    Note that this reader is fairly tolerant, and gladly accepts bogus
645*cda5da8dSAndroid Build Coastguard Worker    XML-RPC data without complaining (but not bogus XML).
646*cda5da8dSAndroid Build Coastguard Worker    """
647*cda5da8dSAndroid Build Coastguard Worker
648*cda5da8dSAndroid Build Coastguard Worker    # and again, if you don't understand what's going on in here,
649*cda5da8dSAndroid Build Coastguard Worker    # that's perfectly ok.
650*cda5da8dSAndroid Build Coastguard Worker
651*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, use_datetime=False, use_builtin_types=False):
652*cda5da8dSAndroid Build Coastguard Worker        self._type = None
653*cda5da8dSAndroid Build Coastguard Worker        self._stack = []
654*cda5da8dSAndroid Build Coastguard Worker        self._marks = []
655*cda5da8dSAndroid Build Coastguard Worker        self._data = []
656*cda5da8dSAndroid Build Coastguard Worker        self._value = False
657*cda5da8dSAndroid Build Coastguard Worker        self._methodname = None
658*cda5da8dSAndroid Build Coastguard Worker        self._encoding = "utf-8"
659*cda5da8dSAndroid Build Coastguard Worker        self.append = self._stack.append
660*cda5da8dSAndroid Build Coastguard Worker        self._use_datetime = use_builtin_types or use_datetime
661*cda5da8dSAndroid Build Coastguard Worker        self._use_bytes = use_builtin_types
662*cda5da8dSAndroid Build Coastguard Worker
663*cda5da8dSAndroid Build Coastguard Worker    def close(self):
664*cda5da8dSAndroid Build Coastguard Worker        # return response tuple and target method
665*cda5da8dSAndroid Build Coastguard Worker        if self._type is None or self._marks:
666*cda5da8dSAndroid Build Coastguard Worker            raise ResponseError()
667*cda5da8dSAndroid Build Coastguard Worker        if self._type == "fault":
668*cda5da8dSAndroid Build Coastguard Worker            raise Fault(**self._stack[0])
669*cda5da8dSAndroid Build Coastguard Worker        return tuple(self._stack)
670*cda5da8dSAndroid Build Coastguard Worker
671*cda5da8dSAndroid Build Coastguard Worker    def getmethodname(self):
672*cda5da8dSAndroid Build Coastguard Worker        return self._methodname
673*cda5da8dSAndroid Build Coastguard Worker
674*cda5da8dSAndroid Build Coastguard Worker    #
675*cda5da8dSAndroid Build Coastguard Worker    # event handlers
676*cda5da8dSAndroid Build Coastguard Worker
677*cda5da8dSAndroid Build Coastguard Worker    def xml(self, encoding, standalone):
678*cda5da8dSAndroid Build Coastguard Worker        self._encoding = encoding
679*cda5da8dSAndroid Build Coastguard Worker        # FIXME: assert standalone == 1 ???
680*cda5da8dSAndroid Build Coastguard Worker
681*cda5da8dSAndroid Build Coastguard Worker    def start(self, tag, attrs):
682*cda5da8dSAndroid Build Coastguard Worker        # prepare to handle this element
683*cda5da8dSAndroid Build Coastguard Worker        if ':' in tag:
684*cda5da8dSAndroid Build Coastguard Worker            tag = tag.split(':')[-1]
685*cda5da8dSAndroid Build Coastguard Worker        if tag == "array" or tag == "struct":
686*cda5da8dSAndroid Build Coastguard Worker            self._marks.append(len(self._stack))
687*cda5da8dSAndroid Build Coastguard Worker        self._data = []
688*cda5da8dSAndroid Build Coastguard Worker        if self._value and tag not in self.dispatch:
689*cda5da8dSAndroid Build Coastguard Worker            raise ResponseError("unknown tag %r" % tag)
690*cda5da8dSAndroid Build Coastguard Worker        self._value = (tag == "value")
691*cda5da8dSAndroid Build Coastguard Worker
692*cda5da8dSAndroid Build Coastguard Worker    def data(self, text):
693*cda5da8dSAndroid Build Coastguard Worker        self._data.append(text)
694*cda5da8dSAndroid Build Coastguard Worker
695*cda5da8dSAndroid Build Coastguard Worker    def end(self, tag):
696*cda5da8dSAndroid Build Coastguard Worker        # call the appropriate end tag handler
697*cda5da8dSAndroid Build Coastguard Worker        try:
698*cda5da8dSAndroid Build Coastguard Worker            f = self.dispatch[tag]
699*cda5da8dSAndroid Build Coastguard Worker        except KeyError:
700*cda5da8dSAndroid Build Coastguard Worker            if ':' not in tag:
701*cda5da8dSAndroid Build Coastguard Worker                return # unknown tag ?
702*cda5da8dSAndroid Build Coastguard Worker            try:
703*cda5da8dSAndroid Build Coastguard Worker                f = self.dispatch[tag.split(':')[-1]]
704*cda5da8dSAndroid Build Coastguard Worker            except KeyError:
705*cda5da8dSAndroid Build Coastguard Worker                return # unknown tag ?
706*cda5da8dSAndroid Build Coastguard Worker        return f(self, "".join(self._data))
707*cda5da8dSAndroid Build Coastguard Worker
708*cda5da8dSAndroid Build Coastguard Worker    #
709*cda5da8dSAndroid Build Coastguard Worker    # accelerator support
710*cda5da8dSAndroid Build Coastguard Worker
711*cda5da8dSAndroid Build Coastguard Worker    def end_dispatch(self, tag, data):
712*cda5da8dSAndroid Build Coastguard Worker        # dispatch data
713*cda5da8dSAndroid Build Coastguard Worker        try:
714*cda5da8dSAndroid Build Coastguard Worker            f = self.dispatch[tag]
715*cda5da8dSAndroid Build Coastguard Worker        except KeyError:
716*cda5da8dSAndroid Build Coastguard Worker            if ':' not in tag:
717*cda5da8dSAndroid Build Coastguard Worker                return # unknown tag ?
718*cda5da8dSAndroid Build Coastguard Worker            try:
719*cda5da8dSAndroid Build Coastguard Worker                f = self.dispatch[tag.split(':')[-1]]
720*cda5da8dSAndroid Build Coastguard Worker            except KeyError:
721*cda5da8dSAndroid Build Coastguard Worker                return # unknown tag ?
722*cda5da8dSAndroid Build Coastguard Worker        return f(self, data)
723*cda5da8dSAndroid Build Coastguard Worker
724*cda5da8dSAndroid Build Coastguard Worker    #
725*cda5da8dSAndroid Build Coastguard Worker    # element decoders
726*cda5da8dSAndroid Build Coastguard Worker
727*cda5da8dSAndroid Build Coastguard Worker    dispatch = {}
728*cda5da8dSAndroid Build Coastguard Worker
729*cda5da8dSAndroid Build Coastguard Worker    def end_nil (self, data):
730*cda5da8dSAndroid Build Coastguard Worker        self.append(None)
731*cda5da8dSAndroid Build Coastguard Worker        self._value = 0
732*cda5da8dSAndroid Build Coastguard Worker    dispatch["nil"] = end_nil
733*cda5da8dSAndroid Build Coastguard Worker
734*cda5da8dSAndroid Build Coastguard Worker    def end_boolean(self, data):
735*cda5da8dSAndroid Build Coastguard Worker        if data == "0":
736*cda5da8dSAndroid Build Coastguard Worker            self.append(False)
737*cda5da8dSAndroid Build Coastguard Worker        elif data == "1":
738*cda5da8dSAndroid Build Coastguard Worker            self.append(True)
739*cda5da8dSAndroid Build Coastguard Worker        else:
740*cda5da8dSAndroid Build Coastguard Worker            raise TypeError("bad boolean value")
741*cda5da8dSAndroid Build Coastguard Worker        self._value = 0
742*cda5da8dSAndroid Build Coastguard Worker    dispatch["boolean"] = end_boolean
743*cda5da8dSAndroid Build Coastguard Worker
744*cda5da8dSAndroid Build Coastguard Worker    def end_int(self, data):
745*cda5da8dSAndroid Build Coastguard Worker        self.append(int(data))
746*cda5da8dSAndroid Build Coastguard Worker        self._value = 0
747*cda5da8dSAndroid Build Coastguard Worker    dispatch["i1"] = end_int
748*cda5da8dSAndroid Build Coastguard Worker    dispatch["i2"] = end_int
749*cda5da8dSAndroid Build Coastguard Worker    dispatch["i4"] = end_int
750*cda5da8dSAndroid Build Coastguard Worker    dispatch["i8"] = end_int
751*cda5da8dSAndroid Build Coastguard Worker    dispatch["int"] = end_int
752*cda5da8dSAndroid Build Coastguard Worker    dispatch["biginteger"] = end_int
753*cda5da8dSAndroid Build Coastguard Worker
754*cda5da8dSAndroid Build Coastguard Worker    def end_double(self, data):
755*cda5da8dSAndroid Build Coastguard Worker        self.append(float(data))
756*cda5da8dSAndroid Build Coastguard Worker        self._value = 0
757*cda5da8dSAndroid Build Coastguard Worker    dispatch["double"] = end_double
758*cda5da8dSAndroid Build Coastguard Worker    dispatch["float"] = end_double
759*cda5da8dSAndroid Build Coastguard Worker
760*cda5da8dSAndroid Build Coastguard Worker    def end_bigdecimal(self, data):
761*cda5da8dSAndroid Build Coastguard Worker        self.append(Decimal(data))
762*cda5da8dSAndroid Build Coastguard Worker        self._value = 0
763*cda5da8dSAndroid Build Coastguard Worker    dispatch["bigdecimal"] = end_bigdecimal
764*cda5da8dSAndroid Build Coastguard Worker
765*cda5da8dSAndroid Build Coastguard Worker    def end_string(self, data):
766*cda5da8dSAndroid Build Coastguard Worker        if self._encoding:
767*cda5da8dSAndroid Build Coastguard Worker            data = data.decode(self._encoding)
768*cda5da8dSAndroid Build Coastguard Worker        self.append(data)
769*cda5da8dSAndroid Build Coastguard Worker        self._value = 0
770*cda5da8dSAndroid Build Coastguard Worker    dispatch["string"] = end_string
771*cda5da8dSAndroid Build Coastguard Worker    dispatch["name"] = end_string # struct keys are always strings
772*cda5da8dSAndroid Build Coastguard Worker
773*cda5da8dSAndroid Build Coastguard Worker    def end_array(self, data):
774*cda5da8dSAndroid Build Coastguard Worker        mark = self._marks.pop()
775*cda5da8dSAndroid Build Coastguard Worker        # map arrays to Python lists
776*cda5da8dSAndroid Build Coastguard Worker        self._stack[mark:] = [self._stack[mark:]]
777*cda5da8dSAndroid Build Coastguard Worker        self._value = 0
778*cda5da8dSAndroid Build Coastguard Worker    dispatch["array"] = end_array
779*cda5da8dSAndroid Build Coastguard Worker
780*cda5da8dSAndroid Build Coastguard Worker    def end_struct(self, data):
781*cda5da8dSAndroid Build Coastguard Worker        mark = self._marks.pop()
782*cda5da8dSAndroid Build Coastguard Worker        # map structs to Python dictionaries
783*cda5da8dSAndroid Build Coastguard Worker        dict = {}
784*cda5da8dSAndroid Build Coastguard Worker        items = self._stack[mark:]
785*cda5da8dSAndroid Build Coastguard Worker        for i in range(0, len(items), 2):
786*cda5da8dSAndroid Build Coastguard Worker            dict[items[i]] = items[i+1]
787*cda5da8dSAndroid Build Coastguard Worker        self._stack[mark:] = [dict]
788*cda5da8dSAndroid Build Coastguard Worker        self._value = 0
789*cda5da8dSAndroid Build Coastguard Worker    dispatch["struct"] = end_struct
790*cda5da8dSAndroid Build Coastguard Worker
791*cda5da8dSAndroid Build Coastguard Worker    def end_base64(self, data):
792*cda5da8dSAndroid Build Coastguard Worker        value = Binary()
793*cda5da8dSAndroid Build Coastguard Worker        value.decode(data.encode("ascii"))
794*cda5da8dSAndroid Build Coastguard Worker        if self._use_bytes:
795*cda5da8dSAndroid Build Coastguard Worker            value = value.data
796*cda5da8dSAndroid Build Coastguard Worker        self.append(value)
797*cda5da8dSAndroid Build Coastguard Worker        self._value = 0
798*cda5da8dSAndroid Build Coastguard Worker    dispatch["base64"] = end_base64
799*cda5da8dSAndroid Build Coastguard Worker
800*cda5da8dSAndroid Build Coastguard Worker    def end_dateTime(self, data):
801*cda5da8dSAndroid Build Coastguard Worker        value = DateTime()
802*cda5da8dSAndroid Build Coastguard Worker        value.decode(data)
803*cda5da8dSAndroid Build Coastguard Worker        if self._use_datetime:
804*cda5da8dSAndroid Build Coastguard Worker            value = _datetime_type(data)
805*cda5da8dSAndroid Build Coastguard Worker        self.append(value)
806*cda5da8dSAndroid Build Coastguard Worker    dispatch["dateTime.iso8601"] = end_dateTime
807*cda5da8dSAndroid Build Coastguard Worker
808*cda5da8dSAndroid Build Coastguard Worker    def end_value(self, data):
809*cda5da8dSAndroid Build Coastguard Worker        # if we stumble upon a value element with no internal
810*cda5da8dSAndroid Build Coastguard Worker        # elements, treat it as a string element
811*cda5da8dSAndroid Build Coastguard Worker        if self._value:
812*cda5da8dSAndroid Build Coastguard Worker            self.end_string(data)
813*cda5da8dSAndroid Build Coastguard Worker    dispatch["value"] = end_value
814*cda5da8dSAndroid Build Coastguard Worker
815*cda5da8dSAndroid Build Coastguard Worker    def end_params(self, data):
816*cda5da8dSAndroid Build Coastguard Worker        self._type = "params"
817*cda5da8dSAndroid Build Coastguard Worker    dispatch["params"] = end_params
818*cda5da8dSAndroid Build Coastguard Worker
819*cda5da8dSAndroid Build Coastguard Worker    def end_fault(self, data):
820*cda5da8dSAndroid Build Coastguard Worker        self._type = "fault"
821*cda5da8dSAndroid Build Coastguard Worker    dispatch["fault"] = end_fault
822*cda5da8dSAndroid Build Coastguard Worker
823*cda5da8dSAndroid Build Coastguard Worker    def end_methodName(self, data):
824*cda5da8dSAndroid Build Coastguard Worker        if self._encoding:
825*cda5da8dSAndroid Build Coastguard Worker            data = data.decode(self._encoding)
826*cda5da8dSAndroid Build Coastguard Worker        self._methodname = data
827*cda5da8dSAndroid Build Coastguard Worker        self._type = "methodName" # no params
828*cda5da8dSAndroid Build Coastguard Worker    dispatch["methodName"] = end_methodName
829*cda5da8dSAndroid Build Coastguard Worker
830*cda5da8dSAndroid Build Coastguard Worker## Multicall support
831*cda5da8dSAndroid Build Coastguard Worker#
832*cda5da8dSAndroid Build Coastguard Worker
833*cda5da8dSAndroid Build Coastguard Workerclass _MultiCallMethod:
834*cda5da8dSAndroid Build Coastguard Worker    # some lesser magic to store calls made to a MultiCall object
835*cda5da8dSAndroid Build Coastguard Worker    # for batch execution
836*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, call_list, name):
837*cda5da8dSAndroid Build Coastguard Worker        self.__call_list = call_list
838*cda5da8dSAndroid Build Coastguard Worker        self.__name = name
839*cda5da8dSAndroid Build Coastguard Worker    def __getattr__(self, name):
840*cda5da8dSAndroid Build Coastguard Worker        return _MultiCallMethod(self.__call_list, "%s.%s" % (self.__name, name))
841*cda5da8dSAndroid Build Coastguard Worker    def __call__(self, *args):
842*cda5da8dSAndroid Build Coastguard Worker        self.__call_list.append((self.__name, args))
843*cda5da8dSAndroid Build Coastguard Worker
844*cda5da8dSAndroid Build Coastguard Workerclass MultiCallIterator:
845*cda5da8dSAndroid Build Coastguard Worker    """Iterates over the results of a multicall. Exceptions are
846*cda5da8dSAndroid Build Coastguard Worker    raised in response to xmlrpc faults."""
847*cda5da8dSAndroid Build Coastguard Worker
848*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, results):
849*cda5da8dSAndroid Build Coastguard Worker        self.results = results
850*cda5da8dSAndroid Build Coastguard Worker
851*cda5da8dSAndroid Build Coastguard Worker    def __getitem__(self, i):
852*cda5da8dSAndroid Build Coastguard Worker        item = self.results[i]
853*cda5da8dSAndroid Build Coastguard Worker        if type(item) == type({}):
854*cda5da8dSAndroid Build Coastguard Worker            raise Fault(item['faultCode'], item['faultString'])
855*cda5da8dSAndroid Build Coastguard Worker        elif type(item) == type([]):
856*cda5da8dSAndroid Build Coastguard Worker            return item[0]
857*cda5da8dSAndroid Build Coastguard Worker        else:
858*cda5da8dSAndroid Build Coastguard Worker            raise ValueError("unexpected type in multicall result")
859*cda5da8dSAndroid Build Coastguard Worker
860*cda5da8dSAndroid Build Coastguard Workerclass MultiCall:
861*cda5da8dSAndroid Build Coastguard Worker    """server -> an object used to boxcar method calls
862*cda5da8dSAndroid Build Coastguard Worker
863*cda5da8dSAndroid Build Coastguard Worker    server should be a ServerProxy object.
864*cda5da8dSAndroid Build Coastguard Worker
865*cda5da8dSAndroid Build Coastguard Worker    Methods can be added to the MultiCall using normal
866*cda5da8dSAndroid Build Coastguard Worker    method call syntax e.g.:
867*cda5da8dSAndroid Build Coastguard Worker
868*cda5da8dSAndroid Build Coastguard Worker    multicall = MultiCall(server_proxy)
869*cda5da8dSAndroid Build Coastguard Worker    multicall.add(2,3)
870*cda5da8dSAndroid Build Coastguard Worker    multicall.get_address("Guido")
871*cda5da8dSAndroid Build Coastguard Worker
872*cda5da8dSAndroid Build Coastguard Worker    To execute the multicall, call the MultiCall object e.g.:
873*cda5da8dSAndroid Build Coastguard Worker
874*cda5da8dSAndroid Build Coastguard Worker    add_result, address = multicall()
875*cda5da8dSAndroid Build Coastguard Worker    """
876*cda5da8dSAndroid Build Coastguard Worker
877*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, server):
878*cda5da8dSAndroid Build Coastguard Worker        self.__server = server
879*cda5da8dSAndroid Build Coastguard Worker        self.__call_list = []
880*cda5da8dSAndroid Build Coastguard Worker
881*cda5da8dSAndroid Build Coastguard Worker    def __repr__(self):
882*cda5da8dSAndroid Build Coastguard Worker        return "<%s at %#x>" % (self.__class__.__name__, id(self))
883*cda5da8dSAndroid Build Coastguard Worker
884*cda5da8dSAndroid Build Coastguard Worker    def __getattr__(self, name):
885*cda5da8dSAndroid Build Coastguard Worker        return _MultiCallMethod(self.__call_list, name)
886*cda5da8dSAndroid Build Coastguard Worker
887*cda5da8dSAndroid Build Coastguard Worker    def __call__(self):
888*cda5da8dSAndroid Build Coastguard Worker        marshalled_list = []
889*cda5da8dSAndroid Build Coastguard Worker        for name, args in self.__call_list:
890*cda5da8dSAndroid Build Coastguard Worker            marshalled_list.append({'methodName' : name, 'params' : args})
891*cda5da8dSAndroid Build Coastguard Worker
892*cda5da8dSAndroid Build Coastguard Worker        return MultiCallIterator(self.__server.system.multicall(marshalled_list))
893*cda5da8dSAndroid Build Coastguard Worker
894*cda5da8dSAndroid Build Coastguard Worker# --------------------------------------------------------------------
895*cda5da8dSAndroid Build Coastguard Worker# convenience functions
896*cda5da8dSAndroid Build Coastguard Worker
897*cda5da8dSAndroid Build Coastguard WorkerFastMarshaller = FastParser = FastUnmarshaller = None
898*cda5da8dSAndroid Build Coastguard Worker
899*cda5da8dSAndroid Build Coastguard Worker##
900*cda5da8dSAndroid Build Coastguard Worker# Create a parser object, and connect it to an unmarshalling instance.
901*cda5da8dSAndroid Build Coastguard Worker# This function picks the fastest available XML parser.
902*cda5da8dSAndroid Build Coastguard Worker#
903*cda5da8dSAndroid Build Coastguard Worker# return A (parser, unmarshaller) tuple.
904*cda5da8dSAndroid Build Coastguard Worker
905*cda5da8dSAndroid Build Coastguard Workerdef getparser(use_datetime=False, use_builtin_types=False):
906*cda5da8dSAndroid Build Coastguard Worker    """getparser() -> parser, unmarshaller
907*cda5da8dSAndroid Build Coastguard Worker
908*cda5da8dSAndroid Build Coastguard Worker    Create an instance of the fastest available parser, and attach it
909*cda5da8dSAndroid Build Coastguard Worker    to an unmarshalling object.  Return both objects.
910*cda5da8dSAndroid Build Coastguard Worker    """
911*cda5da8dSAndroid Build Coastguard Worker    if FastParser and FastUnmarshaller:
912*cda5da8dSAndroid Build Coastguard Worker        if use_builtin_types:
913*cda5da8dSAndroid Build Coastguard Worker            mkdatetime = _datetime_type
914*cda5da8dSAndroid Build Coastguard Worker            mkbytes = base64.decodebytes
915*cda5da8dSAndroid Build Coastguard Worker        elif use_datetime:
916*cda5da8dSAndroid Build Coastguard Worker            mkdatetime = _datetime_type
917*cda5da8dSAndroid Build Coastguard Worker            mkbytes = _binary
918*cda5da8dSAndroid Build Coastguard Worker        else:
919*cda5da8dSAndroid Build Coastguard Worker            mkdatetime = _datetime
920*cda5da8dSAndroid Build Coastguard Worker            mkbytes = _binary
921*cda5da8dSAndroid Build Coastguard Worker        target = FastUnmarshaller(True, False, mkbytes, mkdatetime, Fault)
922*cda5da8dSAndroid Build Coastguard Worker        parser = FastParser(target)
923*cda5da8dSAndroid Build Coastguard Worker    else:
924*cda5da8dSAndroid Build Coastguard Worker        target = Unmarshaller(use_datetime=use_datetime, use_builtin_types=use_builtin_types)
925*cda5da8dSAndroid Build Coastguard Worker        if FastParser:
926*cda5da8dSAndroid Build Coastguard Worker            parser = FastParser(target)
927*cda5da8dSAndroid Build Coastguard Worker        else:
928*cda5da8dSAndroid Build Coastguard Worker            parser = ExpatParser(target)
929*cda5da8dSAndroid Build Coastguard Worker    return parser, target
930*cda5da8dSAndroid Build Coastguard Worker
931*cda5da8dSAndroid Build Coastguard Worker##
932*cda5da8dSAndroid Build Coastguard Worker# Convert a Python tuple or a Fault instance to an XML-RPC packet.
933*cda5da8dSAndroid Build Coastguard Worker#
934*cda5da8dSAndroid Build Coastguard Worker# @def dumps(params, **options)
935*cda5da8dSAndroid Build Coastguard Worker# @param params A tuple or Fault instance.
936*cda5da8dSAndroid Build Coastguard Worker# @keyparam methodname If given, create a methodCall request for
937*cda5da8dSAndroid Build Coastguard Worker#     this method name.
938*cda5da8dSAndroid Build Coastguard Worker# @keyparam methodresponse If given, create a methodResponse packet.
939*cda5da8dSAndroid Build Coastguard Worker#     If used with a tuple, the tuple must be a singleton (that is,
940*cda5da8dSAndroid Build Coastguard Worker#     it must contain exactly one element).
941*cda5da8dSAndroid Build Coastguard Worker# @keyparam encoding The packet encoding.
942*cda5da8dSAndroid Build Coastguard Worker# @return A string containing marshalled data.
943*cda5da8dSAndroid Build Coastguard Worker
944*cda5da8dSAndroid Build Coastguard Workerdef dumps(params, methodname=None, methodresponse=None, encoding=None,
945*cda5da8dSAndroid Build Coastguard Worker          allow_none=False):
946*cda5da8dSAndroid Build Coastguard Worker    """data [,options] -> marshalled data
947*cda5da8dSAndroid Build Coastguard Worker
948*cda5da8dSAndroid Build Coastguard Worker    Convert an argument tuple or a Fault instance to an XML-RPC
949*cda5da8dSAndroid Build Coastguard Worker    request (or response, if the methodresponse option is used).
950*cda5da8dSAndroid Build Coastguard Worker
951*cda5da8dSAndroid Build Coastguard Worker    In addition to the data object, the following options can be given
952*cda5da8dSAndroid Build Coastguard Worker    as keyword arguments:
953*cda5da8dSAndroid Build Coastguard Worker
954*cda5da8dSAndroid Build Coastguard Worker        methodname: the method name for a methodCall packet
955*cda5da8dSAndroid Build Coastguard Worker
956*cda5da8dSAndroid Build Coastguard Worker        methodresponse: true to create a methodResponse packet.
957*cda5da8dSAndroid Build Coastguard Worker        If this option is used with a tuple, the tuple must be
958*cda5da8dSAndroid Build Coastguard Worker        a singleton (i.e. it can contain only one element).
959*cda5da8dSAndroid Build Coastguard Worker
960*cda5da8dSAndroid Build Coastguard Worker        encoding: the packet encoding (default is UTF-8)
961*cda5da8dSAndroid Build Coastguard Worker
962*cda5da8dSAndroid Build Coastguard Worker    All byte strings in the data structure are assumed to use the
963*cda5da8dSAndroid Build Coastguard Worker    packet encoding.  Unicode strings are automatically converted,
964*cda5da8dSAndroid Build Coastguard Worker    where necessary.
965*cda5da8dSAndroid Build Coastguard Worker    """
966*cda5da8dSAndroid Build Coastguard Worker
967*cda5da8dSAndroid Build Coastguard Worker    assert isinstance(params, (tuple, Fault)), "argument must be tuple or Fault instance"
968*cda5da8dSAndroid Build Coastguard Worker    if isinstance(params, Fault):
969*cda5da8dSAndroid Build Coastguard Worker        methodresponse = 1
970*cda5da8dSAndroid Build Coastguard Worker    elif methodresponse and isinstance(params, tuple):
971*cda5da8dSAndroid Build Coastguard Worker        assert len(params) == 1, "response tuple must be a singleton"
972*cda5da8dSAndroid Build Coastguard Worker
973*cda5da8dSAndroid Build Coastguard Worker    if not encoding:
974*cda5da8dSAndroid Build Coastguard Worker        encoding = "utf-8"
975*cda5da8dSAndroid Build Coastguard Worker
976*cda5da8dSAndroid Build Coastguard Worker    if FastMarshaller:
977*cda5da8dSAndroid Build Coastguard Worker        m = FastMarshaller(encoding)
978*cda5da8dSAndroid Build Coastguard Worker    else:
979*cda5da8dSAndroid Build Coastguard Worker        m = Marshaller(encoding, allow_none)
980*cda5da8dSAndroid Build Coastguard Worker
981*cda5da8dSAndroid Build Coastguard Worker    data = m.dumps(params)
982*cda5da8dSAndroid Build Coastguard Worker
983*cda5da8dSAndroid Build Coastguard Worker    if encoding != "utf-8":
984*cda5da8dSAndroid Build Coastguard Worker        xmlheader = "<?xml version='1.0' encoding='%s'?>\n" % str(encoding)
985*cda5da8dSAndroid Build Coastguard Worker    else:
986*cda5da8dSAndroid Build Coastguard Worker        xmlheader = "<?xml version='1.0'?>\n" # utf-8 is default
987*cda5da8dSAndroid Build Coastguard Worker
988*cda5da8dSAndroid Build Coastguard Worker    # standard XML-RPC wrappings
989*cda5da8dSAndroid Build Coastguard Worker    if methodname:
990*cda5da8dSAndroid Build Coastguard Worker        # a method call
991*cda5da8dSAndroid Build Coastguard Worker        data = (
992*cda5da8dSAndroid Build Coastguard Worker            xmlheader,
993*cda5da8dSAndroid Build Coastguard Worker            "<methodCall>\n"
994*cda5da8dSAndroid Build Coastguard Worker            "<methodName>", methodname, "</methodName>\n",
995*cda5da8dSAndroid Build Coastguard Worker            data,
996*cda5da8dSAndroid Build Coastguard Worker            "</methodCall>\n"
997*cda5da8dSAndroid Build Coastguard Worker            )
998*cda5da8dSAndroid Build Coastguard Worker    elif methodresponse:
999*cda5da8dSAndroid Build Coastguard Worker        # a method response, or a fault structure
1000*cda5da8dSAndroid Build Coastguard Worker        data = (
1001*cda5da8dSAndroid Build Coastguard Worker            xmlheader,
1002*cda5da8dSAndroid Build Coastguard Worker            "<methodResponse>\n",
1003*cda5da8dSAndroid Build Coastguard Worker            data,
1004*cda5da8dSAndroid Build Coastguard Worker            "</methodResponse>\n"
1005*cda5da8dSAndroid Build Coastguard Worker            )
1006*cda5da8dSAndroid Build Coastguard Worker    else:
1007*cda5da8dSAndroid Build Coastguard Worker        return data # return as is
1008*cda5da8dSAndroid Build Coastguard Worker    return "".join(data)
1009*cda5da8dSAndroid Build Coastguard Worker
1010*cda5da8dSAndroid Build Coastguard Worker##
1011*cda5da8dSAndroid Build Coastguard Worker# Convert an XML-RPC packet to a Python object.  If the XML-RPC packet
1012*cda5da8dSAndroid Build Coastguard Worker# represents a fault condition, this function raises a Fault exception.
1013*cda5da8dSAndroid Build Coastguard Worker#
1014*cda5da8dSAndroid Build Coastguard Worker# @param data An XML-RPC packet, given as an 8-bit string.
1015*cda5da8dSAndroid Build Coastguard Worker# @return A tuple containing the unpacked data, and the method name
1016*cda5da8dSAndroid Build Coastguard Worker#     (None if not present).
1017*cda5da8dSAndroid Build Coastguard Worker# @see Fault
1018*cda5da8dSAndroid Build Coastguard Worker
1019*cda5da8dSAndroid Build Coastguard Workerdef loads(data, use_datetime=False, use_builtin_types=False):
1020*cda5da8dSAndroid Build Coastguard Worker    """data -> unmarshalled data, method name
1021*cda5da8dSAndroid Build Coastguard Worker
1022*cda5da8dSAndroid Build Coastguard Worker    Convert an XML-RPC packet to unmarshalled data plus a method
1023*cda5da8dSAndroid Build Coastguard Worker    name (None if not present).
1024*cda5da8dSAndroid Build Coastguard Worker
1025*cda5da8dSAndroid Build Coastguard Worker    If the XML-RPC packet represents a fault condition, this function
1026*cda5da8dSAndroid Build Coastguard Worker    raises a Fault exception.
1027*cda5da8dSAndroid Build Coastguard Worker    """
1028*cda5da8dSAndroid Build Coastguard Worker    p, u = getparser(use_datetime=use_datetime, use_builtin_types=use_builtin_types)
1029*cda5da8dSAndroid Build Coastguard Worker    p.feed(data)
1030*cda5da8dSAndroid Build Coastguard Worker    p.close()
1031*cda5da8dSAndroid Build Coastguard Worker    return u.close(), u.getmethodname()
1032*cda5da8dSAndroid Build Coastguard Worker
1033*cda5da8dSAndroid Build Coastguard Worker##
1034*cda5da8dSAndroid Build Coastguard Worker# Encode a string using the gzip content encoding such as specified by the
1035*cda5da8dSAndroid Build Coastguard Worker# Content-Encoding: gzip
1036*cda5da8dSAndroid Build Coastguard Worker# in the HTTP header, as described in RFC 1952
1037*cda5da8dSAndroid Build Coastguard Worker#
1038*cda5da8dSAndroid Build Coastguard Worker# @param data the unencoded data
1039*cda5da8dSAndroid Build Coastguard Worker# @return the encoded data
1040*cda5da8dSAndroid Build Coastguard Worker
1041*cda5da8dSAndroid Build Coastguard Workerdef gzip_encode(data):
1042*cda5da8dSAndroid Build Coastguard Worker    """data -> gzip encoded data
1043*cda5da8dSAndroid Build Coastguard Worker
1044*cda5da8dSAndroid Build Coastguard Worker    Encode data using the gzip content encoding as described in RFC 1952
1045*cda5da8dSAndroid Build Coastguard Worker    """
1046*cda5da8dSAndroid Build Coastguard Worker    if not gzip:
1047*cda5da8dSAndroid Build Coastguard Worker        raise NotImplementedError
1048*cda5da8dSAndroid Build Coastguard Worker    f = BytesIO()
1049*cda5da8dSAndroid Build Coastguard Worker    with gzip.GzipFile(mode="wb", fileobj=f, compresslevel=1) as gzf:
1050*cda5da8dSAndroid Build Coastguard Worker        gzf.write(data)
1051*cda5da8dSAndroid Build Coastguard Worker    return f.getvalue()
1052*cda5da8dSAndroid Build Coastguard Worker
1053*cda5da8dSAndroid Build Coastguard Worker##
1054*cda5da8dSAndroid Build Coastguard Worker# Decode a string using the gzip content encoding such as specified by the
1055*cda5da8dSAndroid Build Coastguard Worker# Content-Encoding: gzip
1056*cda5da8dSAndroid Build Coastguard Worker# in the HTTP header, as described in RFC 1952
1057*cda5da8dSAndroid Build Coastguard Worker#
1058*cda5da8dSAndroid Build Coastguard Worker# @param data The encoded data
1059*cda5da8dSAndroid Build Coastguard Worker# @keyparam max_decode Maximum bytes to decode (20 MiB default), use negative
1060*cda5da8dSAndroid Build Coastguard Worker#    values for unlimited decoding
1061*cda5da8dSAndroid Build Coastguard Worker# @return the unencoded data
1062*cda5da8dSAndroid Build Coastguard Worker# @raises ValueError if data is not correctly coded.
1063*cda5da8dSAndroid Build Coastguard Worker# @raises ValueError if max gzipped payload length exceeded
1064*cda5da8dSAndroid Build Coastguard Worker
1065*cda5da8dSAndroid Build Coastguard Workerdef gzip_decode(data, max_decode=20971520):
1066*cda5da8dSAndroid Build Coastguard Worker    """gzip encoded data -> unencoded data
1067*cda5da8dSAndroid Build Coastguard Worker
1068*cda5da8dSAndroid Build Coastguard Worker    Decode data using the gzip content encoding as described in RFC 1952
1069*cda5da8dSAndroid Build Coastguard Worker    """
1070*cda5da8dSAndroid Build Coastguard Worker    if not gzip:
1071*cda5da8dSAndroid Build Coastguard Worker        raise NotImplementedError
1072*cda5da8dSAndroid Build Coastguard Worker    with gzip.GzipFile(mode="rb", fileobj=BytesIO(data)) as gzf:
1073*cda5da8dSAndroid Build Coastguard Worker        try:
1074*cda5da8dSAndroid Build Coastguard Worker            if max_decode < 0: # no limit
1075*cda5da8dSAndroid Build Coastguard Worker                decoded = gzf.read()
1076*cda5da8dSAndroid Build Coastguard Worker            else:
1077*cda5da8dSAndroid Build Coastguard Worker                decoded = gzf.read(max_decode + 1)
1078*cda5da8dSAndroid Build Coastguard Worker        except OSError:
1079*cda5da8dSAndroid Build Coastguard Worker            raise ValueError("invalid data")
1080*cda5da8dSAndroid Build Coastguard Worker    if max_decode >= 0 and len(decoded) > max_decode:
1081*cda5da8dSAndroid Build Coastguard Worker        raise ValueError("max gzipped payload length exceeded")
1082*cda5da8dSAndroid Build Coastguard Worker    return decoded
1083*cda5da8dSAndroid Build Coastguard Worker
1084*cda5da8dSAndroid Build Coastguard Worker##
1085*cda5da8dSAndroid Build Coastguard Worker# Return a decoded file-like object for the gzip encoding
1086*cda5da8dSAndroid Build Coastguard Worker# as described in RFC 1952.
1087*cda5da8dSAndroid Build Coastguard Worker#
1088*cda5da8dSAndroid Build Coastguard Worker# @param response A stream supporting a read() method
1089*cda5da8dSAndroid Build Coastguard Worker# @return a file-like object that the decoded data can be read() from
1090*cda5da8dSAndroid Build Coastguard Worker
1091*cda5da8dSAndroid Build Coastguard Workerclass GzipDecodedResponse(gzip.GzipFile if gzip else object):
1092*cda5da8dSAndroid Build Coastguard Worker    """a file-like object to decode a response encoded with the gzip
1093*cda5da8dSAndroid Build Coastguard Worker    method, as described in RFC 1952.
1094*cda5da8dSAndroid Build Coastguard Worker    """
1095*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, response):
1096*cda5da8dSAndroid Build Coastguard Worker        #response doesn't support tell() and read(), required by
1097*cda5da8dSAndroid Build Coastguard Worker        #GzipFile
1098*cda5da8dSAndroid Build Coastguard Worker        if not gzip:
1099*cda5da8dSAndroid Build Coastguard Worker            raise NotImplementedError
1100*cda5da8dSAndroid Build Coastguard Worker        self.io = BytesIO(response.read())
1101*cda5da8dSAndroid Build Coastguard Worker        gzip.GzipFile.__init__(self, mode="rb", fileobj=self.io)
1102*cda5da8dSAndroid Build Coastguard Worker
1103*cda5da8dSAndroid Build Coastguard Worker    def close(self):
1104*cda5da8dSAndroid Build Coastguard Worker        try:
1105*cda5da8dSAndroid Build Coastguard Worker            gzip.GzipFile.close(self)
1106*cda5da8dSAndroid Build Coastguard Worker        finally:
1107*cda5da8dSAndroid Build Coastguard Worker            self.io.close()
1108*cda5da8dSAndroid Build Coastguard Worker
1109*cda5da8dSAndroid Build Coastguard Worker
1110*cda5da8dSAndroid Build Coastguard Worker# --------------------------------------------------------------------
1111*cda5da8dSAndroid Build Coastguard Worker# request dispatcher
1112*cda5da8dSAndroid Build Coastguard Worker
1113*cda5da8dSAndroid Build Coastguard Workerclass _Method:
1114*cda5da8dSAndroid Build Coastguard Worker    # some magic to bind an XML-RPC method to an RPC server.
1115*cda5da8dSAndroid Build Coastguard Worker    # supports "nested" methods (e.g. examples.getStateName)
1116*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, send, name):
1117*cda5da8dSAndroid Build Coastguard Worker        self.__send = send
1118*cda5da8dSAndroid Build Coastguard Worker        self.__name = name
1119*cda5da8dSAndroid Build Coastguard Worker    def __getattr__(self, name):
1120*cda5da8dSAndroid Build Coastguard Worker        return _Method(self.__send, "%s.%s" % (self.__name, name))
1121*cda5da8dSAndroid Build Coastguard Worker    def __call__(self, *args):
1122*cda5da8dSAndroid Build Coastguard Worker        return self.__send(self.__name, args)
1123*cda5da8dSAndroid Build Coastguard Worker
1124*cda5da8dSAndroid Build Coastguard Worker##
1125*cda5da8dSAndroid Build Coastguard Worker# Standard transport class for XML-RPC over HTTP.
1126*cda5da8dSAndroid Build Coastguard Worker# <p>
1127*cda5da8dSAndroid Build Coastguard Worker# You can create custom transports by subclassing this method, and
1128*cda5da8dSAndroid Build Coastguard Worker# overriding selected methods.
1129*cda5da8dSAndroid Build Coastguard Worker
1130*cda5da8dSAndroid Build Coastguard Workerclass Transport:
1131*cda5da8dSAndroid Build Coastguard Worker    """Handles an HTTP transaction to an XML-RPC server."""
1132*cda5da8dSAndroid Build Coastguard Worker
1133*cda5da8dSAndroid Build Coastguard Worker    # client identifier (may be overridden)
1134*cda5da8dSAndroid Build Coastguard Worker    user_agent = "Python-xmlrpc/%s" % __version__
1135*cda5da8dSAndroid Build Coastguard Worker
1136*cda5da8dSAndroid Build Coastguard Worker    #if true, we'll request gzip encoding
1137*cda5da8dSAndroid Build Coastguard Worker    accept_gzip_encoding = True
1138*cda5da8dSAndroid Build Coastguard Worker
1139*cda5da8dSAndroid Build Coastguard Worker    # if positive, encode request using gzip if it exceeds this threshold
1140*cda5da8dSAndroid Build Coastguard Worker    # note that many servers will get confused, so only use it if you know
1141*cda5da8dSAndroid Build Coastguard Worker    # that they can decode such a request
1142*cda5da8dSAndroid Build Coastguard Worker    encode_threshold = None #None = don't encode
1143*cda5da8dSAndroid Build Coastguard Worker
1144*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, use_datetime=False, use_builtin_types=False,
1145*cda5da8dSAndroid Build Coastguard Worker                 *, headers=()):
1146*cda5da8dSAndroid Build Coastguard Worker        self._use_datetime = use_datetime
1147*cda5da8dSAndroid Build Coastguard Worker        self._use_builtin_types = use_builtin_types
1148*cda5da8dSAndroid Build Coastguard Worker        self._connection = (None, None)
1149*cda5da8dSAndroid Build Coastguard Worker        self._headers = list(headers)
1150*cda5da8dSAndroid Build Coastguard Worker        self._extra_headers = []
1151*cda5da8dSAndroid Build Coastguard Worker
1152*cda5da8dSAndroid Build Coastguard Worker    ##
1153*cda5da8dSAndroid Build Coastguard Worker    # Send a complete request, and parse the response.
1154*cda5da8dSAndroid Build Coastguard Worker    # Retry request if a cached connection has disconnected.
1155*cda5da8dSAndroid Build Coastguard Worker    #
1156*cda5da8dSAndroid Build Coastguard Worker    # @param host Target host.
1157*cda5da8dSAndroid Build Coastguard Worker    # @param handler Target PRC handler.
1158*cda5da8dSAndroid Build Coastguard Worker    # @param request_body XML-RPC request body.
1159*cda5da8dSAndroid Build Coastguard Worker    # @param verbose Debugging flag.
1160*cda5da8dSAndroid Build Coastguard Worker    # @return Parsed response.
1161*cda5da8dSAndroid Build Coastguard Worker
1162*cda5da8dSAndroid Build Coastguard Worker    def request(self, host, handler, request_body, verbose=False):
1163*cda5da8dSAndroid Build Coastguard Worker        #retry request once if cached connection has gone cold
1164*cda5da8dSAndroid Build Coastguard Worker        for i in (0, 1):
1165*cda5da8dSAndroid Build Coastguard Worker            try:
1166*cda5da8dSAndroid Build Coastguard Worker                return self.single_request(host, handler, request_body, verbose)
1167*cda5da8dSAndroid Build Coastguard Worker            except http.client.RemoteDisconnected:
1168*cda5da8dSAndroid Build Coastguard Worker                if i:
1169*cda5da8dSAndroid Build Coastguard Worker                    raise
1170*cda5da8dSAndroid Build Coastguard Worker            except OSError as e:
1171*cda5da8dSAndroid Build Coastguard Worker                if i or e.errno not in (errno.ECONNRESET, errno.ECONNABORTED,
1172*cda5da8dSAndroid Build Coastguard Worker                                        errno.EPIPE):
1173*cda5da8dSAndroid Build Coastguard Worker                    raise
1174*cda5da8dSAndroid Build Coastguard Worker
1175*cda5da8dSAndroid Build Coastguard Worker    def single_request(self, host, handler, request_body, verbose=False):
1176*cda5da8dSAndroid Build Coastguard Worker        # issue XML-RPC request
1177*cda5da8dSAndroid Build Coastguard Worker        try:
1178*cda5da8dSAndroid Build Coastguard Worker            http_conn = self.send_request(host, handler, request_body, verbose)
1179*cda5da8dSAndroid Build Coastguard Worker            resp = http_conn.getresponse()
1180*cda5da8dSAndroid Build Coastguard Worker            if resp.status == 200:
1181*cda5da8dSAndroid Build Coastguard Worker                self.verbose = verbose
1182*cda5da8dSAndroid Build Coastguard Worker                return self.parse_response(resp)
1183*cda5da8dSAndroid Build Coastguard Worker
1184*cda5da8dSAndroid Build Coastguard Worker        except Fault:
1185*cda5da8dSAndroid Build Coastguard Worker            raise
1186*cda5da8dSAndroid Build Coastguard Worker        except Exception:
1187*cda5da8dSAndroid Build Coastguard Worker            #All unexpected errors leave connection in
1188*cda5da8dSAndroid Build Coastguard Worker            # a strange state, so we clear it.
1189*cda5da8dSAndroid Build Coastguard Worker            self.close()
1190*cda5da8dSAndroid Build Coastguard Worker            raise
1191*cda5da8dSAndroid Build Coastguard Worker
1192*cda5da8dSAndroid Build Coastguard Worker        #We got an error response.
1193*cda5da8dSAndroid Build Coastguard Worker        #Discard any response data and raise exception
1194*cda5da8dSAndroid Build Coastguard Worker        if resp.getheader("content-length", ""):
1195*cda5da8dSAndroid Build Coastguard Worker            resp.read()
1196*cda5da8dSAndroid Build Coastguard Worker        raise ProtocolError(
1197*cda5da8dSAndroid Build Coastguard Worker            host + handler,
1198*cda5da8dSAndroid Build Coastguard Worker            resp.status, resp.reason,
1199*cda5da8dSAndroid Build Coastguard Worker            dict(resp.getheaders())
1200*cda5da8dSAndroid Build Coastguard Worker            )
1201*cda5da8dSAndroid Build Coastguard Worker
1202*cda5da8dSAndroid Build Coastguard Worker
1203*cda5da8dSAndroid Build Coastguard Worker    ##
1204*cda5da8dSAndroid Build Coastguard Worker    # Create parser.
1205*cda5da8dSAndroid Build Coastguard Worker    #
1206*cda5da8dSAndroid Build Coastguard Worker    # @return A 2-tuple containing a parser and an unmarshaller.
1207*cda5da8dSAndroid Build Coastguard Worker
1208*cda5da8dSAndroid Build Coastguard Worker    def getparser(self):
1209*cda5da8dSAndroid Build Coastguard Worker        # get parser and unmarshaller
1210*cda5da8dSAndroid Build Coastguard Worker        return getparser(use_datetime=self._use_datetime,
1211*cda5da8dSAndroid Build Coastguard Worker                         use_builtin_types=self._use_builtin_types)
1212*cda5da8dSAndroid Build Coastguard Worker
1213*cda5da8dSAndroid Build Coastguard Worker    ##
1214*cda5da8dSAndroid Build Coastguard Worker    # Get authorization info from host parameter
1215*cda5da8dSAndroid Build Coastguard Worker    # Host may be a string, or a (host, x509-dict) tuple; if a string,
1216*cda5da8dSAndroid Build Coastguard Worker    # it is checked for a "user:pw@host" format, and a "Basic
1217*cda5da8dSAndroid Build Coastguard Worker    # Authentication" header is added if appropriate.
1218*cda5da8dSAndroid Build Coastguard Worker    #
1219*cda5da8dSAndroid Build Coastguard Worker    # @param host Host descriptor (URL or (URL, x509 info) tuple).
1220*cda5da8dSAndroid Build Coastguard Worker    # @return A 3-tuple containing (actual host, extra headers,
1221*cda5da8dSAndroid Build Coastguard Worker    #     x509 info).  The header and x509 fields may be None.
1222*cda5da8dSAndroid Build Coastguard Worker
1223*cda5da8dSAndroid Build Coastguard Worker    def get_host_info(self, host):
1224*cda5da8dSAndroid Build Coastguard Worker
1225*cda5da8dSAndroid Build Coastguard Worker        x509 = {}
1226*cda5da8dSAndroid Build Coastguard Worker        if isinstance(host, tuple):
1227*cda5da8dSAndroid Build Coastguard Worker            host, x509 = host
1228*cda5da8dSAndroid Build Coastguard Worker
1229*cda5da8dSAndroid Build Coastguard Worker        auth, host = urllib.parse._splituser(host)
1230*cda5da8dSAndroid Build Coastguard Worker
1231*cda5da8dSAndroid Build Coastguard Worker        if auth:
1232*cda5da8dSAndroid Build Coastguard Worker            auth = urllib.parse.unquote_to_bytes(auth)
1233*cda5da8dSAndroid Build Coastguard Worker            auth = base64.encodebytes(auth).decode("utf-8")
1234*cda5da8dSAndroid Build Coastguard Worker            auth = "".join(auth.split()) # get rid of whitespace
1235*cda5da8dSAndroid Build Coastguard Worker            extra_headers = [
1236*cda5da8dSAndroid Build Coastguard Worker                ("Authorization", "Basic " + auth)
1237*cda5da8dSAndroid Build Coastguard Worker                ]
1238*cda5da8dSAndroid Build Coastguard Worker        else:
1239*cda5da8dSAndroid Build Coastguard Worker            extra_headers = []
1240*cda5da8dSAndroid Build Coastguard Worker
1241*cda5da8dSAndroid Build Coastguard Worker        return host, extra_headers, x509
1242*cda5da8dSAndroid Build Coastguard Worker
1243*cda5da8dSAndroid Build Coastguard Worker    ##
1244*cda5da8dSAndroid Build Coastguard Worker    # Connect to server.
1245*cda5da8dSAndroid Build Coastguard Worker    #
1246*cda5da8dSAndroid Build Coastguard Worker    # @param host Target host.
1247*cda5da8dSAndroid Build Coastguard Worker    # @return An HTTPConnection object
1248*cda5da8dSAndroid Build Coastguard Worker
1249*cda5da8dSAndroid Build Coastguard Worker    def make_connection(self, host):
1250*cda5da8dSAndroid Build Coastguard Worker        #return an existing connection if possible.  This allows
1251*cda5da8dSAndroid Build Coastguard Worker        #HTTP/1.1 keep-alive.
1252*cda5da8dSAndroid Build Coastguard Worker        if self._connection and host == self._connection[0]:
1253*cda5da8dSAndroid Build Coastguard Worker            return self._connection[1]
1254*cda5da8dSAndroid Build Coastguard Worker        # create a HTTP connection object from a host descriptor
1255*cda5da8dSAndroid Build Coastguard Worker        chost, self._extra_headers, x509 = self.get_host_info(host)
1256*cda5da8dSAndroid Build Coastguard Worker        self._connection = host, http.client.HTTPConnection(chost)
1257*cda5da8dSAndroid Build Coastguard Worker        return self._connection[1]
1258*cda5da8dSAndroid Build Coastguard Worker
1259*cda5da8dSAndroid Build Coastguard Worker    ##
1260*cda5da8dSAndroid Build Coastguard Worker    # Clear any cached connection object.
1261*cda5da8dSAndroid Build Coastguard Worker    # Used in the event of socket errors.
1262*cda5da8dSAndroid Build Coastguard Worker    #
1263*cda5da8dSAndroid Build Coastguard Worker    def close(self):
1264*cda5da8dSAndroid Build Coastguard Worker        host, connection = self._connection
1265*cda5da8dSAndroid Build Coastguard Worker        if connection:
1266*cda5da8dSAndroid Build Coastguard Worker            self._connection = (None, None)
1267*cda5da8dSAndroid Build Coastguard Worker            connection.close()
1268*cda5da8dSAndroid Build Coastguard Worker
1269*cda5da8dSAndroid Build Coastguard Worker    ##
1270*cda5da8dSAndroid Build Coastguard Worker    # Send HTTP request.
1271*cda5da8dSAndroid Build Coastguard Worker    #
1272*cda5da8dSAndroid Build Coastguard Worker    # @param host Host descriptor (URL or (URL, x509 info) tuple).
1273*cda5da8dSAndroid Build Coastguard Worker    # @param handler Target RPC handler (a path relative to host)
1274*cda5da8dSAndroid Build Coastguard Worker    # @param request_body The XML-RPC request body
1275*cda5da8dSAndroid Build Coastguard Worker    # @param debug Enable debugging if debug is true.
1276*cda5da8dSAndroid Build Coastguard Worker    # @return An HTTPConnection.
1277*cda5da8dSAndroid Build Coastguard Worker
1278*cda5da8dSAndroid Build Coastguard Worker    def send_request(self, host, handler, request_body, debug):
1279*cda5da8dSAndroid Build Coastguard Worker        connection = self.make_connection(host)
1280*cda5da8dSAndroid Build Coastguard Worker        headers = self._headers + self._extra_headers
1281*cda5da8dSAndroid Build Coastguard Worker        if debug:
1282*cda5da8dSAndroid Build Coastguard Worker            connection.set_debuglevel(1)
1283*cda5da8dSAndroid Build Coastguard Worker        if self.accept_gzip_encoding and gzip:
1284*cda5da8dSAndroid Build Coastguard Worker            connection.putrequest("POST", handler, skip_accept_encoding=True)
1285*cda5da8dSAndroid Build Coastguard Worker            headers.append(("Accept-Encoding", "gzip"))
1286*cda5da8dSAndroid Build Coastguard Worker        else:
1287*cda5da8dSAndroid Build Coastguard Worker            connection.putrequest("POST", handler)
1288*cda5da8dSAndroid Build Coastguard Worker        headers.append(("Content-Type", "text/xml"))
1289*cda5da8dSAndroid Build Coastguard Worker        headers.append(("User-Agent", self.user_agent))
1290*cda5da8dSAndroid Build Coastguard Worker        self.send_headers(connection, headers)
1291*cda5da8dSAndroid Build Coastguard Worker        self.send_content(connection, request_body)
1292*cda5da8dSAndroid Build Coastguard Worker        return connection
1293*cda5da8dSAndroid Build Coastguard Worker
1294*cda5da8dSAndroid Build Coastguard Worker    ##
1295*cda5da8dSAndroid Build Coastguard Worker    # Send request headers.
1296*cda5da8dSAndroid Build Coastguard Worker    # This function provides a useful hook for subclassing
1297*cda5da8dSAndroid Build Coastguard Worker    #
1298*cda5da8dSAndroid Build Coastguard Worker    # @param connection httpConnection.
1299*cda5da8dSAndroid Build Coastguard Worker    # @param headers list of key,value pairs for HTTP headers
1300*cda5da8dSAndroid Build Coastguard Worker
1301*cda5da8dSAndroid Build Coastguard Worker    def send_headers(self, connection, headers):
1302*cda5da8dSAndroid Build Coastguard Worker        for key, val in headers:
1303*cda5da8dSAndroid Build Coastguard Worker            connection.putheader(key, val)
1304*cda5da8dSAndroid Build Coastguard Worker
1305*cda5da8dSAndroid Build Coastguard Worker    ##
1306*cda5da8dSAndroid Build Coastguard Worker    # Send request body.
1307*cda5da8dSAndroid Build Coastguard Worker    # This function provides a useful hook for subclassing
1308*cda5da8dSAndroid Build Coastguard Worker    #
1309*cda5da8dSAndroid Build Coastguard Worker    # @param connection httpConnection.
1310*cda5da8dSAndroid Build Coastguard Worker    # @param request_body XML-RPC request body.
1311*cda5da8dSAndroid Build Coastguard Worker
1312*cda5da8dSAndroid Build Coastguard Worker    def send_content(self, connection, request_body):
1313*cda5da8dSAndroid Build Coastguard Worker        #optionally encode the request
1314*cda5da8dSAndroid Build Coastguard Worker        if (self.encode_threshold is not None and
1315*cda5da8dSAndroid Build Coastguard Worker            self.encode_threshold < len(request_body) and
1316*cda5da8dSAndroid Build Coastguard Worker            gzip):
1317*cda5da8dSAndroid Build Coastguard Worker            connection.putheader("Content-Encoding", "gzip")
1318*cda5da8dSAndroid Build Coastguard Worker            request_body = gzip_encode(request_body)
1319*cda5da8dSAndroid Build Coastguard Worker
1320*cda5da8dSAndroid Build Coastguard Worker        connection.putheader("Content-Length", str(len(request_body)))
1321*cda5da8dSAndroid Build Coastguard Worker        connection.endheaders(request_body)
1322*cda5da8dSAndroid Build Coastguard Worker
1323*cda5da8dSAndroid Build Coastguard Worker    ##
1324*cda5da8dSAndroid Build Coastguard Worker    # Parse response.
1325*cda5da8dSAndroid Build Coastguard Worker    #
1326*cda5da8dSAndroid Build Coastguard Worker    # @param file Stream.
1327*cda5da8dSAndroid Build Coastguard Worker    # @return Response tuple and target method.
1328*cda5da8dSAndroid Build Coastguard Worker
1329*cda5da8dSAndroid Build Coastguard Worker    def parse_response(self, response):
1330*cda5da8dSAndroid Build Coastguard Worker        # read response data from httpresponse, and parse it
1331*cda5da8dSAndroid Build Coastguard Worker        # Check for new http response object, otherwise it is a file object.
1332*cda5da8dSAndroid Build Coastguard Worker        if hasattr(response, 'getheader'):
1333*cda5da8dSAndroid Build Coastguard Worker            if response.getheader("Content-Encoding", "") == "gzip":
1334*cda5da8dSAndroid Build Coastguard Worker                stream = GzipDecodedResponse(response)
1335*cda5da8dSAndroid Build Coastguard Worker            else:
1336*cda5da8dSAndroid Build Coastguard Worker                stream = response
1337*cda5da8dSAndroid Build Coastguard Worker        else:
1338*cda5da8dSAndroid Build Coastguard Worker            stream = response
1339*cda5da8dSAndroid Build Coastguard Worker
1340*cda5da8dSAndroid Build Coastguard Worker        p, u = self.getparser()
1341*cda5da8dSAndroid Build Coastguard Worker
1342*cda5da8dSAndroid Build Coastguard Worker        while 1:
1343*cda5da8dSAndroid Build Coastguard Worker            data = stream.read(1024)
1344*cda5da8dSAndroid Build Coastguard Worker            if not data:
1345*cda5da8dSAndroid Build Coastguard Worker                break
1346*cda5da8dSAndroid Build Coastguard Worker            if self.verbose:
1347*cda5da8dSAndroid Build Coastguard Worker                print("body:", repr(data))
1348*cda5da8dSAndroid Build Coastguard Worker            p.feed(data)
1349*cda5da8dSAndroid Build Coastguard Worker
1350*cda5da8dSAndroid Build Coastguard Worker        if stream is not response:
1351*cda5da8dSAndroid Build Coastguard Worker            stream.close()
1352*cda5da8dSAndroid Build Coastguard Worker        p.close()
1353*cda5da8dSAndroid Build Coastguard Worker
1354*cda5da8dSAndroid Build Coastguard Worker        return u.close()
1355*cda5da8dSAndroid Build Coastguard Worker
1356*cda5da8dSAndroid Build Coastguard Worker##
1357*cda5da8dSAndroid Build Coastguard Worker# Standard transport class for XML-RPC over HTTPS.
1358*cda5da8dSAndroid Build Coastguard Worker
1359*cda5da8dSAndroid Build Coastguard Workerclass SafeTransport(Transport):
1360*cda5da8dSAndroid Build Coastguard Worker    """Handles an HTTPS transaction to an XML-RPC server."""
1361*cda5da8dSAndroid Build Coastguard Worker
1362*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, use_datetime=False, use_builtin_types=False,
1363*cda5da8dSAndroid Build Coastguard Worker                 *, headers=(), context=None):
1364*cda5da8dSAndroid Build Coastguard Worker        super().__init__(use_datetime=use_datetime,
1365*cda5da8dSAndroid Build Coastguard Worker                         use_builtin_types=use_builtin_types,
1366*cda5da8dSAndroid Build Coastguard Worker                         headers=headers)
1367*cda5da8dSAndroid Build Coastguard Worker        self.context = context
1368*cda5da8dSAndroid Build Coastguard Worker
1369*cda5da8dSAndroid Build Coastguard Worker    # FIXME: mostly untested
1370*cda5da8dSAndroid Build Coastguard Worker
1371*cda5da8dSAndroid Build Coastguard Worker    def make_connection(self, host):
1372*cda5da8dSAndroid Build Coastguard Worker        if self._connection and host == self._connection[0]:
1373*cda5da8dSAndroid Build Coastguard Worker            return self._connection[1]
1374*cda5da8dSAndroid Build Coastguard Worker
1375*cda5da8dSAndroid Build Coastguard Worker        if not hasattr(http.client, "HTTPSConnection"):
1376*cda5da8dSAndroid Build Coastguard Worker            raise NotImplementedError(
1377*cda5da8dSAndroid Build Coastguard Worker            "your version of http.client doesn't support HTTPS")
1378*cda5da8dSAndroid Build Coastguard Worker        # create a HTTPS connection object from a host descriptor
1379*cda5da8dSAndroid Build Coastguard Worker        # host may be a string, or a (host, x509-dict) tuple
1380*cda5da8dSAndroid Build Coastguard Worker        chost, self._extra_headers, x509 = self.get_host_info(host)
1381*cda5da8dSAndroid Build Coastguard Worker        self._connection = host, http.client.HTTPSConnection(chost,
1382*cda5da8dSAndroid Build Coastguard Worker            None, context=self.context, **(x509 or {}))
1383*cda5da8dSAndroid Build Coastguard Worker        return self._connection[1]
1384*cda5da8dSAndroid Build Coastguard Worker
1385*cda5da8dSAndroid Build Coastguard Worker##
1386*cda5da8dSAndroid Build Coastguard Worker# Standard server proxy.  This class establishes a virtual connection
1387*cda5da8dSAndroid Build Coastguard Worker# to an XML-RPC server.
1388*cda5da8dSAndroid Build Coastguard Worker# <p>
1389*cda5da8dSAndroid Build Coastguard Worker# This class is available as ServerProxy and Server.  New code should
1390*cda5da8dSAndroid Build Coastguard Worker# use ServerProxy, to avoid confusion.
1391*cda5da8dSAndroid Build Coastguard Worker#
1392*cda5da8dSAndroid Build Coastguard Worker# @def ServerProxy(uri, **options)
1393*cda5da8dSAndroid Build Coastguard Worker# @param uri The connection point on the server.
1394*cda5da8dSAndroid Build Coastguard Worker# @keyparam transport A transport factory, compatible with the
1395*cda5da8dSAndroid Build Coastguard Worker#    standard transport class.
1396*cda5da8dSAndroid Build Coastguard Worker# @keyparam encoding The default encoding used for 8-bit strings
1397*cda5da8dSAndroid Build Coastguard Worker#    (default is UTF-8).
1398*cda5da8dSAndroid Build Coastguard Worker# @keyparam verbose Use a true value to enable debugging output.
1399*cda5da8dSAndroid Build Coastguard Worker#    (printed to standard output).
1400*cda5da8dSAndroid Build Coastguard Worker# @see Transport
1401*cda5da8dSAndroid Build Coastguard Worker
1402*cda5da8dSAndroid Build Coastguard Workerclass ServerProxy:
1403*cda5da8dSAndroid Build Coastguard Worker    """uri [,options] -> a logical connection to an XML-RPC server
1404*cda5da8dSAndroid Build Coastguard Worker
1405*cda5da8dSAndroid Build Coastguard Worker    uri is the connection point on the server, given as
1406*cda5da8dSAndroid Build Coastguard Worker    scheme://host/target.
1407*cda5da8dSAndroid Build Coastguard Worker
1408*cda5da8dSAndroid Build Coastguard Worker    The standard implementation always supports the "http" scheme.  If
1409*cda5da8dSAndroid Build Coastguard Worker    SSL socket support is available (Python 2.0), it also supports
1410*cda5da8dSAndroid Build Coastguard Worker    "https".
1411*cda5da8dSAndroid Build Coastguard Worker
1412*cda5da8dSAndroid Build Coastguard Worker    If the target part and the slash preceding it are both omitted,
1413*cda5da8dSAndroid Build Coastguard Worker    "/RPC2" is assumed.
1414*cda5da8dSAndroid Build Coastguard Worker
1415*cda5da8dSAndroid Build Coastguard Worker    The following options can be given as keyword arguments:
1416*cda5da8dSAndroid Build Coastguard Worker
1417*cda5da8dSAndroid Build Coastguard Worker        transport: a transport factory
1418*cda5da8dSAndroid Build Coastguard Worker        encoding: the request encoding (default is UTF-8)
1419*cda5da8dSAndroid Build Coastguard Worker
1420*cda5da8dSAndroid Build Coastguard Worker    All 8-bit strings passed to the server proxy are assumed to use
1421*cda5da8dSAndroid Build Coastguard Worker    the given encoding.
1422*cda5da8dSAndroid Build Coastguard Worker    """
1423*cda5da8dSAndroid Build Coastguard Worker
1424*cda5da8dSAndroid Build Coastguard Worker    def __init__(self, uri, transport=None, encoding=None, verbose=False,
1425*cda5da8dSAndroid Build Coastguard Worker                 allow_none=False, use_datetime=False, use_builtin_types=False,
1426*cda5da8dSAndroid Build Coastguard Worker                 *, headers=(), context=None):
1427*cda5da8dSAndroid Build Coastguard Worker        # establish a "logical" server connection
1428*cda5da8dSAndroid Build Coastguard Worker
1429*cda5da8dSAndroid Build Coastguard Worker        # get the url
1430*cda5da8dSAndroid Build Coastguard Worker        p = urllib.parse.urlsplit(uri)
1431*cda5da8dSAndroid Build Coastguard Worker        if p.scheme not in ("http", "https"):
1432*cda5da8dSAndroid Build Coastguard Worker            raise OSError("unsupported XML-RPC protocol")
1433*cda5da8dSAndroid Build Coastguard Worker        self.__host = p.netloc
1434*cda5da8dSAndroid Build Coastguard Worker        self.__handler = urllib.parse.urlunsplit(["", "", *p[2:]])
1435*cda5da8dSAndroid Build Coastguard Worker        if not self.__handler:
1436*cda5da8dSAndroid Build Coastguard Worker            self.__handler = "/RPC2"
1437*cda5da8dSAndroid Build Coastguard Worker
1438*cda5da8dSAndroid Build Coastguard Worker        if transport is None:
1439*cda5da8dSAndroid Build Coastguard Worker            if p.scheme == "https":
1440*cda5da8dSAndroid Build Coastguard Worker                handler = SafeTransport
1441*cda5da8dSAndroid Build Coastguard Worker                extra_kwargs = {"context": context}
1442*cda5da8dSAndroid Build Coastguard Worker            else:
1443*cda5da8dSAndroid Build Coastguard Worker                handler = Transport
1444*cda5da8dSAndroid Build Coastguard Worker                extra_kwargs = {}
1445*cda5da8dSAndroid Build Coastguard Worker            transport = handler(use_datetime=use_datetime,
1446*cda5da8dSAndroid Build Coastguard Worker                                use_builtin_types=use_builtin_types,
1447*cda5da8dSAndroid Build Coastguard Worker                                headers=headers,
1448*cda5da8dSAndroid Build Coastguard Worker                                **extra_kwargs)
1449*cda5da8dSAndroid Build Coastguard Worker        self.__transport = transport
1450*cda5da8dSAndroid Build Coastguard Worker
1451*cda5da8dSAndroid Build Coastguard Worker        self.__encoding = encoding or 'utf-8'
1452*cda5da8dSAndroid Build Coastguard Worker        self.__verbose = verbose
1453*cda5da8dSAndroid Build Coastguard Worker        self.__allow_none = allow_none
1454*cda5da8dSAndroid Build Coastguard Worker
1455*cda5da8dSAndroid Build Coastguard Worker    def __close(self):
1456*cda5da8dSAndroid Build Coastguard Worker        self.__transport.close()
1457*cda5da8dSAndroid Build Coastguard Worker
1458*cda5da8dSAndroid Build Coastguard Worker    def __request(self, methodname, params):
1459*cda5da8dSAndroid Build Coastguard Worker        # call a method on the remote server
1460*cda5da8dSAndroid Build Coastguard Worker
1461*cda5da8dSAndroid Build Coastguard Worker        request = dumps(params, methodname, encoding=self.__encoding,
1462*cda5da8dSAndroid Build Coastguard Worker                        allow_none=self.__allow_none).encode(self.__encoding, 'xmlcharrefreplace')
1463*cda5da8dSAndroid Build Coastguard Worker
1464*cda5da8dSAndroid Build Coastguard Worker        response = self.__transport.request(
1465*cda5da8dSAndroid Build Coastguard Worker            self.__host,
1466*cda5da8dSAndroid Build Coastguard Worker            self.__handler,
1467*cda5da8dSAndroid Build Coastguard Worker            request,
1468*cda5da8dSAndroid Build Coastguard Worker            verbose=self.__verbose
1469*cda5da8dSAndroid Build Coastguard Worker            )
1470*cda5da8dSAndroid Build Coastguard Worker
1471*cda5da8dSAndroid Build Coastguard Worker        if len(response) == 1:
1472*cda5da8dSAndroid Build Coastguard Worker            response = response[0]
1473*cda5da8dSAndroid Build Coastguard Worker
1474*cda5da8dSAndroid Build Coastguard Worker        return response
1475*cda5da8dSAndroid Build Coastguard Worker
1476*cda5da8dSAndroid Build Coastguard Worker    def __repr__(self):
1477*cda5da8dSAndroid Build Coastguard Worker        return (
1478*cda5da8dSAndroid Build Coastguard Worker            "<%s for %s%s>" %
1479*cda5da8dSAndroid Build Coastguard Worker            (self.__class__.__name__, self.__host, self.__handler)
1480*cda5da8dSAndroid Build Coastguard Worker            )
1481*cda5da8dSAndroid Build Coastguard Worker
1482*cda5da8dSAndroid Build Coastguard Worker    def __getattr__(self, name):
1483*cda5da8dSAndroid Build Coastguard Worker        # magic method dispatcher
1484*cda5da8dSAndroid Build Coastguard Worker        return _Method(self.__request, name)
1485*cda5da8dSAndroid Build Coastguard Worker
1486*cda5da8dSAndroid Build Coastguard Worker    # note: to call a remote object with a non-standard name, use
1487*cda5da8dSAndroid Build Coastguard Worker    # result getattr(server, "strange-python-name")(args)
1488*cda5da8dSAndroid Build Coastguard Worker
1489*cda5da8dSAndroid Build Coastguard Worker    def __call__(self, attr):
1490*cda5da8dSAndroid Build Coastguard Worker        """A workaround to get special attributes on the ServerProxy
1491*cda5da8dSAndroid Build Coastguard Worker           without interfering with the magic __getattr__
1492*cda5da8dSAndroid Build Coastguard Worker        """
1493*cda5da8dSAndroid Build Coastguard Worker        if attr == "close":
1494*cda5da8dSAndroid Build Coastguard Worker            return self.__close
1495*cda5da8dSAndroid Build Coastguard Worker        elif attr == "transport":
1496*cda5da8dSAndroid Build Coastguard Worker            return self.__transport
1497*cda5da8dSAndroid Build Coastguard Worker        raise AttributeError("Attribute %r not found" % (attr,))
1498*cda5da8dSAndroid Build Coastguard Worker
1499*cda5da8dSAndroid Build Coastguard Worker    def __enter__(self):
1500*cda5da8dSAndroid Build Coastguard Worker        return self
1501*cda5da8dSAndroid Build Coastguard Worker
1502*cda5da8dSAndroid Build Coastguard Worker    def __exit__(self, *args):
1503*cda5da8dSAndroid Build Coastguard Worker        self.__close()
1504*cda5da8dSAndroid Build Coastguard Worker
1505*cda5da8dSAndroid Build Coastguard Worker# compatibility
1506*cda5da8dSAndroid Build Coastguard Worker
1507*cda5da8dSAndroid Build Coastguard WorkerServer = ServerProxy
1508*cda5da8dSAndroid Build Coastguard Worker
1509*cda5da8dSAndroid Build Coastguard Worker# --------------------------------------------------------------------
1510*cda5da8dSAndroid Build Coastguard Worker# test code
1511*cda5da8dSAndroid Build Coastguard Worker
1512*cda5da8dSAndroid Build Coastguard Workerif __name__ == "__main__":
1513*cda5da8dSAndroid Build Coastguard Worker
1514*cda5da8dSAndroid Build Coastguard Worker    # simple test program (from the XML-RPC specification)
1515*cda5da8dSAndroid Build Coastguard Worker
1516*cda5da8dSAndroid Build Coastguard Worker    # local server, available from Lib/xmlrpc/server.py
1517*cda5da8dSAndroid Build Coastguard Worker    server = ServerProxy("http://localhost:8000")
1518*cda5da8dSAndroid Build Coastguard Worker
1519*cda5da8dSAndroid Build Coastguard Worker    try:
1520*cda5da8dSAndroid Build Coastguard Worker        print(server.currentTime.getCurrentTime())
1521*cda5da8dSAndroid Build Coastguard Worker    except Error as v:
1522*cda5da8dSAndroid Build Coastguard Worker        print("ERROR", v)
1523*cda5da8dSAndroid Build Coastguard Worker
1524*cda5da8dSAndroid Build Coastguard Worker    multi = MultiCall(server)
1525*cda5da8dSAndroid Build Coastguard Worker    multi.getData()
1526*cda5da8dSAndroid Build Coastguard Worker    multi.pow(2,9)
1527*cda5da8dSAndroid Build Coastguard Worker    multi.add(1,2)
1528*cda5da8dSAndroid Build Coastguard Worker    try:
1529*cda5da8dSAndroid Build Coastguard Worker        for response in multi():
1530*cda5da8dSAndroid Build Coastguard Worker            print(response)
1531*cda5da8dSAndroid Build Coastguard Worker    except Error as v:
1532*cda5da8dSAndroid Build Coastguard Worker        print("ERROR", v)
1533