diff -r ed6345cb08ab Doc/library/xmlrpc.client.rst
--- a/Doc/library/xmlrpc.client.rst Fri Apr 29 11:31:52 2016 +0300
+++ b/Doc/library/xmlrpc.client.rst Fri Apr 29 18:29:19 2016 +0300
@@ -132,6 +132,11 @@ between conformable Python objects and X
.. versionchanged:: 3.5
Added the *context* argument.
+ .. versionchanged:: 3.6
+ Added support of parsing additional types used by Apache XML-RPC
+ implementation for numerics and ``None``.
+ See http://ws.apache.org/xmlrpc/types.html for a description.
+
.. seealso::
diff -r ed6345cb08ab Doc/whatsnew/3.6.rst
--- a/Doc/whatsnew/3.6.rst Fri Apr 29 11:31:52 2016 +0300
+++ b/Doc/whatsnew/3.6.rst Fri Apr 29 18:29:19 2016 +0300
@@ -341,6 +341,14 @@ The "Object allocated at" traceback is n
:mod:`warnings` was already imported.
+xmlrpc.client
+-------------
+
+The module now supports parsing additional data types used by Apache XML-RPC
+implementation for numerics and ``None``.
+(Contributed by Serhiy Storchaka in :issue:`XXXXXX`.)
+
+
zipfile
-------
diff -r ed6345cb08ab Lib/test/test_xmlrpc.py
--- a/Lib/test/test_xmlrpc.py Fri Apr 29 11:31:52 2016 +0300
+++ b/Lib/test/test_xmlrpc.py Fri Apr 29 18:29:19 2016 +0300
@@ -1,5 +1,6 @@
import base64
import datetime
+import decimal
import sys
import time
import unittest
@@ -223,6 +224,54 @@ class XMLRPCTestCase(unittest.TestCase):
self.assertIs(type(newvalue), xmlrpclib.Binary)
self.assertIsNone(m)
+ 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 -r ed6345cb08ab Lib/xmlrpc/client.py
--- a/Lib/xmlrpc/client.py Fri Apr 29 11:31:52 2016 +0300
+++ b/Lib/xmlrpc/client.py Fri Apr 29 18:29:19 2016 +0300
@@ -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
@@ -666,6 +667,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 = []
@@ -679,9 +682,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
@@ -691,9 +698,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
@@ -718,14 +729,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: