From 352601ca00376aaf07d4399096f985d3d8ecb96f Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sun, 11 Sep 2016 11:23:38 +0300 Subject: Issue #26885: xmlrpc now supports unmarshalling additional data types used by Apache XML-RPC implementation for numerics and None. --- Doc/library/xmlrpc.client.rst | 17 +++++++++++++-- Doc/whatsnew/3.6.rst | 8 +++++++ Lib/test/test_xmlrpc.py | 49 +++++++++++++++++++++++++++++++++++++++++++ Lib/xmlrpc/client.py | 32 ++++++++++++++++++++++------ Misc/NEWS | 3 +++ 5 files changed, 101 insertions(+), 8 deletions(-) diff --git a/Doc/library/xmlrpc.client.rst b/Doc/library/xmlrpc.client.rst index e7916d2..feafef8 100644 --- a/Doc/library/xmlrpc.client.rst +++ b/Doc/library/xmlrpc.client.rst @@ -88,9 +88,13 @@ between conformable Python objects and XML on the wire. +======================+=======================================================+ | ``boolean`` | :class:`bool` | +----------------------+-------------------------------------------------------+ - | ``int`` or ``i4`` | :class:`int` in range from -2147483648 to 2147483647. | + | ``int``, ``i1``, | :class:`int` in range from -2147483648 to 2147483647. | + | ``i2``, ``i4``, | Values get the ```` tag. | + | ``i8`` or | | + | ``biginteger`` | | +----------------------+-------------------------------------------------------+ - | ``double`` | :class:`float` | + | ``double`` or | :class:`float`. Values get the ```` tag. | + | ``float`` | | +----------------------+-------------------------------------------------------+ | ``string`` | :class:`str` | +----------------------+-------------------------------------------------------+ @@ -114,6 +118,8 @@ between conformable Python objects and XML on the wire. | ``nil`` | The ``None`` constant. Passing is allowed only if | | | *allow_none* is true. | +----------------------+-------------------------------------------------------+ + | ``bigdecimal`` | :class:`decimal.Decimal`. Returned type only. | + +----------------------+-------------------------------------------------------+ This is the full set of data types supported by XML-RPC. Method calls may also raise a special :exc:`Fault` instance, used to signal XML-RPC server errors, or @@ -137,6 +143,13 @@ between conformable Python objects and XML on the wire. .. versionchanged:: 3.5 Added the *context* argument. + .. versionchanged:: 3.6 + Added support of type tags with prefixes (e.g.``ex:nil``). + Added support of unmarsalling additional types used by Apache XML-RPC + implementation for numerics: ``i1``, ``i2``, ``i8``, ``biginteger``, + ``float`` and ``bigdecimal``. + See http://ws.apache.org/xmlrpc/types.html for a description. + .. seealso:: diff --git a/Doc/whatsnew/3.6.rst b/Doc/whatsnew/3.6.rst index e6d3973..6bb3469 100644 --- a/Doc/whatsnew/3.6.rst +++ b/Doc/whatsnew/3.6.rst @@ -934,6 +934,14 @@ Allowed keyword arguments to be passed to :func:`Beep `, ` (:issue:`27982`). +xmlrpc.client +------------- + +The module now supports unmarshalling additional data types used by +Apache XML-RPC implementation for numerics and ``None``. +(Contributed by Serhiy Storchaka in :issue:`26885`.) + + zipfile ------- diff --git a/Lib/test/test_xmlrpc.py b/Lib/test/test_xmlrpc.py index 29a9878..df9c79e 100644 --- a/Lib/test/test_xmlrpc.py +++ b/Lib/test/test_xmlrpc.py @@ -1,5 +1,6 @@ import base64 import datetime +import decimal import sys import time import unittest @@ -237,6 +238,54 @@ class XMLRPCTestCase(unittest.TestCase): '') self.assertRaises(ResponseError, xmlrpclib.loads, data) + def check_loads(self, s, value, **kwargs): + dump = '%s' % s + result, m = xmlrpclib.loads(dump, **kwargs) + (newvalue,) = result + self.assertEqual(newvalue, value) + self.assertIs(type(newvalue), type(value)) + self.assertIsNone(m) + + def test_load_standard_types(self): + check = self.check_loads + check('string', 'string') + check('string', 'string') + check('𝔘𝔫𝔦𝔠𝔬𝔡𝔢 string', '𝔘𝔫𝔦𝔠𝔬𝔡𝔢 string') + check('2056183947', 2056183947) + check('-2056183947', -2056183947) + check('2056183947', 2056183947) + check('46093.78125', 46093.78125) + check('0', False) + check('AGJ5dGUgc3RyaW5n/w==', + xmlrpclib.Binary(b'\x00byte string\xff')) + check('AGJ5dGUgc3RyaW5n/w==', + b'\x00byte string\xff', use_builtin_types=True) + check('20050210T11:41:23', + xmlrpclib.DateTime('20050210T11:41:23')) + check('20050210T11:41:23', + datetime.datetime(2005, 2, 10, 11, 41, 23), + use_builtin_types=True) + check('' + '12' + '', [1, 2]) + check('' + 'b2' + 'a1' + '', {'a': 1, 'b': 2}) + + def test_load_extension_types(self): + check = self.check_loads + check('', None) + check('', None) + check('205', 205) + check('20561', 20561) + check('9876543210', 9876543210) + check('98765432100123456789', + 98765432100123456789) + check('93.78125', 93.78125) + check('9876543210.0123456789', + decimal.Decimal('9876543210.0123456789')) + def test_get_host_info(self): # see bug #3613, this raised a TypeError transp = xmlrpc.client.Transport() diff --git a/Lib/xmlrpc/client.py b/Lib/xmlrpc/client.py index 581a3b9..bd3278e 100644 --- a/Lib/xmlrpc/client.py +++ b/Lib/xmlrpc/client.py @@ -132,6 +132,7 @@ import base64 import sys import time from datetime import datetime +from decimal import Decimal import http.client import urllib.parse from xml.parsers import expat @@ -667,6 +668,8 @@ class Unmarshaller: def start(self, tag, attrs): # prepare to handle this element + if ':' in tag: + tag = tag.split(':')[-1] if tag == "array" or tag == "struct": self._marks.append(len(self._stack)) self._data = [] @@ -682,9 +685,13 @@ class Unmarshaller: try: f = self.dispatch[tag] except KeyError: - pass # unknown tag ? - else: - return f(self, "".join(self._data)) + if ':' not in tag: + return # unknown tag ? + try: + f = self.dispatch[tag.split(':')[-1]] + except KeyError: + return # unknown tag ? + return f(self, "".join(self._data)) # # accelerator support @@ -694,9 +701,13 @@ class Unmarshaller: try: f = self.dispatch[tag] except KeyError: - pass # unknown tag ? - else: - return f(self, data) + if ':' not in tag: + return # unknown tag ? + try: + f = self.dispatch[tag.split(':')[-1]] + except KeyError: + return # unknown tag ? + return f(self, data) # # element decoders @@ -721,14 +732,23 @@ class Unmarshaller: def end_int(self, data): self.append(int(data)) self._value = 0 + dispatch["i1"] = end_int + dispatch["i2"] = end_int dispatch["i4"] = end_int dispatch["i8"] = end_int dispatch["int"] = end_int + dispatch["biginteger"] = end_int def end_double(self, data): self.append(float(data)) self._value = 0 dispatch["double"] = end_double + dispatch["float"] = end_double + + def end_bigdecimal(self, data): + self.append(Decimal(data)) + self._value = 0 + dispatch["bigdecimal"] = end_bigdecimal def end_string(self, data): if self._encoding: diff --git a/Misc/NEWS b/Misc/NEWS index fc41d88..6b30a10 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -143,6 +143,9 @@ Core and Builtins Library ------- +- Issue #26885: xmlrpc now supports unmarshalling additional data types used + by Apache XML-RPC implementation for numerics and None. + - Issue #28070: Fixed parsing inline verbose flag in regular expressions. - Issue #19500: Add client-side SSL session resumption to the ssl module. -- cgit v0.12