[Python-checkins] cpython: Issue #26885: xmlrpc now supports unmarshalling additional data types used

serhiy.storchaka python-checkins at python.org
Sun Sep 11 04:24:19 EDT 2016


https://hg.python.org/cpython/rev/ef63e1c999bd
changeset:   103611:ef63e1c999bd
user:        Serhiy Storchaka <storchaka at gmail.com>
date:        Sun Sep 11 11:23:38 2016 +0300
summary:
  Issue #26885: xmlrpc now supports unmarshalling additional data types used
by Apache XML-RPC implementation for numerics and None.

files:
  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
--- a/Doc/library/xmlrpc.client.rst
+++ b/Doc/library/xmlrpc.client.rst
@@ -88,9 +88,13 @@
    +======================+=======================================================+
    | ``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 @@
    | ``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 @@
    .. 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
--- a/Doc/whatsnew/3.6.rst
+++ b/Doc/whatsnew/3.6.rst
@@ -934,6 +934,14 @@
 <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
 -------
 
diff --git a/Lib/test/test_xmlrpc.py b/Lib/test/test_xmlrpc.py
--- 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 @@
                 '</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()
diff --git a/Lib/xmlrpc/client.py b/Lib/xmlrpc/client.py
--- a/Lib/xmlrpc/client.py
+++ b/Lib/xmlrpc/client.py
@@ -132,6 +132,7 @@
 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 @@
 
     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 @@
         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 @@
         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 @@
     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
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -143,6 +143,9 @@
 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.

-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list