comparison planemo/lib/python3.7/site-packages/future/backports/xmlrpc/client.py @ 0:d30785e31577 draft

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