Issue #26885: xmlrpc now supports unmarshalling additional data types used

by Apache XML-RPC implementation for numerics and None.
This commit is contained in:
Serhiy Storchaka 2016-09-11 11:23:38 +03:00
parent 9fab79bcb5
commit 352601ca00
5 changed files with 101 additions and 8 deletions

View File

@ -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 ``<int>`` tag. |
| ``i8`` or | |
| ``biginteger`` | |
+----------------------+-------------------------------------------------------+
| ``double`` | :class:`float` |
| ``double`` or | :class:`float`. Values get the ``<double>`` 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::

View File

@ -934,6 +934,14 @@ Allowed keyword arguments to be passed to :func:`Beep <winsound.Beep>`,
<winsound.PlaySound>` (: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
-------

View File

@ -1,5 +1,6 @@
import base64
import datetime
import decimal
import sys
import time
import unittest
@ -237,6 +238,54 @@ class XMLRPCTestCase(unittest.TestCase):
'</struct></value></param></params>')
self.assertRaises(ResponseError, xmlrpclib.loads, data)
def check_loads(self, s, value, **kwargs):
dump = '<params><param><value>%s</value></param></params>' % 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</string>', 'string')
check('<string>𝔘𝔫𝔦𝔠𝔬𝔡𝔢 string</string>', '𝔘𝔫𝔦𝔠𝔬𝔡𝔢 string')
check('<int>2056183947</int>', 2056183947)
check('<int>-2056183947</int>', -2056183947)
check('<i4>2056183947</i4>', 2056183947)
check('<double>46093.78125</double>', 46093.78125)
check('<boolean>0</boolean>', False)
check('<base64>AGJ5dGUgc3RyaW5n/w==</base64>',
xmlrpclib.Binary(b'\x00byte string\xff'))
check('<base64>AGJ5dGUgc3RyaW5n/w==</base64>',
b'\x00byte string\xff', use_builtin_types=True)
check('<dateTime.iso8601>20050210T11:41:23</dateTime.iso8601>',
xmlrpclib.DateTime('20050210T11:41:23'))
check('<dateTime.iso8601>20050210T11:41:23</dateTime.iso8601>',
datetime.datetime(2005, 2, 10, 11, 41, 23),
use_builtin_types=True)
check('<array><data>'
'<value><int>1</int></value><value><int>2</int></value>'
'</data></array>', [1, 2])
check('<struct>'
'<member><name>b</name><value><int>2</int></value></member>'
'<member><name>a</name><value><int>1</int></value></member>'
'</struct>', {'a': 1, 'b': 2})
def test_load_extension_types(self):
check = self.check_loads
check('<nil/>', None)
check('<ex:nil/>', None)
check('<i1>205</i1>', 205)
check('<i2>20561</i2>', 20561)
check('<i8>9876543210</i8>', 9876543210)
check('<biginteger>98765432100123456789</biginteger>',
98765432100123456789)
check('<float>93.78125</float>', 93.78125)
check('<bigdecimal>9876543210.0123456789</bigdecimal>',
decimal.Decimal('9876543210.0123456789'))
def test_get_host_info(self):
# see bug #3613, this raised a TypeError
transp = xmlrpc.client.Transport()

View File

@ -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,8 +685,12 @@ class Unmarshaller:
try:
f = self.dispatch[tag]
except KeyError:
pass # unknown tag ?
else:
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))
#
@ -694,8 +701,12 @@ class Unmarshaller:
try:
f = self.dispatch[tag]
except KeyError:
pass # unknown tag ?
else:
if ':' not in tag:
return # unknown tag ?
try:
f = self.dispatch[tag.split(':')[-1]]
except KeyError:
return # unknown tag ?
return f(self, data)
#
@ -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:

View File

@ -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.