Skip to content

Commit 352601c

Browse files
Issue #26885: xmlrpc now supports unmarshalling additional data types used
by Apache XML-RPC implementation for numerics and None.
1 parent 9fab79b commit 352601c

5 files changed

Lines changed: 101 additions & 8 deletions

File tree

Doc/library/xmlrpc.client.rst

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,13 @@ between conformable Python objects and XML on the wire.
8888
+======================+=======================================================+
8989
| ``boolean`` | :class:`bool` |
9090
+----------------------+-------------------------------------------------------+
91-
| ``int`` or ``i4`` | :class:`int` in range from -2147483648 to 2147483647. |
91+
| ``int``, ``i1``, | :class:`int` in range from -2147483648 to 2147483647. |
92+
| ``i2``, ``i4``, | Values get the ``<int>`` tag. |
93+
| ``i8`` or | |
94+
| ``biginteger`` | |
9295
+----------------------+-------------------------------------------------------+
93-
| ``double`` | :class:`float` |
96+
| ``double`` or | :class:`float`. Values get the ``<double>`` tag. |
97+
| ``float`` | |
9498
+----------------------+-------------------------------------------------------+
9599
| ``string`` | :class:`str` |
96100
+----------------------+-------------------------------------------------------+
@@ -114,6 +118,8 @@ between conformable Python objects and XML on the wire.
114118
| ``nil`` | The ``None`` constant. Passing is allowed only if |
115119
| | *allow_none* is true. |
116120
+----------------------+-------------------------------------------------------+
121+
| ``bigdecimal`` | :class:`decimal.Decimal`. Returned type only. |
122+
+----------------------+-------------------------------------------------------+
117123

118124
This is the full set of data types supported by XML-RPC. Method calls may also
119125
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.
137143
.. versionchanged:: 3.5
138144
Added the *context* argument.
139145

146+
.. versionchanged:: 3.6
147+
Added support of type tags with prefixes (e.g.``ex:nil``).
148+
Added support of unmarsalling additional types used by Apache XML-RPC
149+
implementation for numerics: ``i1``, ``i2``, ``i8``, ``biginteger``,
150+
``float`` and ``bigdecimal``.
151+
See http://ws.apache.org/xmlrpc/types.html for a description.
152+
140153

141154
.. seealso::
142155

Doc/whatsnew/3.6.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -934,6 +934,14 @@ Allowed keyword arguments to be passed to :func:`Beep <winsound.Beep>`,
934934
<winsound.PlaySound>` (:issue:`27982`).
935935

936936

937+
xmlrpc.client
938+
-------------
939+
940+
The module now supports unmarshalling additional data types used by
941+
Apache XML-RPC implementation for numerics and ``None``.
942+
(Contributed by Serhiy Storchaka in :issue:`26885`.)
943+
944+
937945
zipfile
938946
-------
939947

Lib/test/test_xmlrpc.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import base64
22
import datetime
3+
import decimal
34
import sys
45
import time
56
import unittest
@@ -237,6 +238,54 @@ def test_loads_unsupported(self):
237238
'</struct></value></param></params>')
238239
self.assertRaises(ResponseError, xmlrpclib.loads, data)
239240

241+
def check_loads(self, s, value, **kwargs):
242+
dump = '<params><param><value>%s</value></param></params>' % s
243+
result, m = xmlrpclib.loads(dump, **kwargs)
244+
(newvalue,) = result
245+
self.assertEqual(newvalue, value)
246+
self.assertIs(type(newvalue), type(value))
247+
self.assertIsNone(m)
248+
249+
def test_load_standard_types(self):
250+
check = self.check_loads
251+
check('string', 'string')
252+
check('<string>string</string>', 'string')
253+
check('<string>𝔘𝔫𝔦𝔠𝔬𝔡𝔢 string</string>', '𝔘𝔫𝔦𝔠𝔬𝔡𝔢 string')
254+
check('<int>2056183947</int>', 2056183947)
255+
check('<int>-2056183947</int>', -2056183947)
256+
check('<i4>2056183947</i4>', 2056183947)
257+
check('<double>46093.78125</double>', 46093.78125)
258+
check('<boolean>0</boolean>', False)
259+
check('<base64>AGJ5dGUgc3RyaW5n/w==</base64>',
260+
xmlrpclib.Binary(b'\x00byte string\xff'))
261+
check('<base64>AGJ5dGUgc3RyaW5n/w==</base64>',
262+
b'\x00byte string\xff', use_builtin_types=True)
263+
check('<dateTime.iso8601>20050210T11:41:23</dateTime.iso8601>',
264+
xmlrpclib.DateTime('20050210T11:41:23'))
265+
check('<dateTime.iso8601>20050210T11:41:23</dateTime.iso8601>',
266+
datetime.datetime(2005, 2, 10, 11, 41, 23),
267+
use_builtin_types=True)
268+
check('<array><data>'
269+
'<value><int>1</int></value><value><int>2</int></value>'
270+
'</data></array>', [1, 2])
271+
check('<struct>'
272+
'<member><name>b</name><value><int>2</int></value></member>'
273+
'<member><name>a</name><value><int>1</int></value></member>'
274+
'</struct>', {'a': 1, 'b': 2})
275+
276+
def test_load_extension_types(self):
277+
check = self.check_loads
278+
check('<nil/>', None)
279+
check('<ex:nil/>', None)
280+
check('<i1>205</i1>', 205)
281+
check('<i2>20561</i2>', 20561)
282+
check('<i8>9876543210</i8>', 9876543210)
283+
check('<biginteger>98765432100123456789</biginteger>',
284+
98765432100123456789)
285+
check('<float>93.78125</float>', 93.78125)
286+
check('<bigdecimal>9876543210.0123456789</bigdecimal>',
287+
decimal.Decimal('9876543210.0123456789'))
288+
240289
def test_get_host_info(self):
241290
# see bug #3613, this raised a TypeError
242291
transp = xmlrpc.client.Transport()

Lib/xmlrpc/client.py

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@
132132
import sys
133133
import time
134134
from datetime import datetime
135+
from decimal import Decimal
135136
import http.client
136137
import urllib.parse
137138
from xml.parsers import expat
@@ -667,6 +668,8 @@ def xml(self, encoding, standalone):
667668

668669
def start(self, tag, attrs):
669670
# prepare to handle this element
671+
if ':' in tag:
672+
tag = tag.split(':')[-1]
670673
if tag == "array" or tag == "struct":
671674
self._marks.append(len(self._stack))
672675
self._data = []
@@ -682,9 +685,13 @@ def end(self, tag):
682685
try:
683686
f = self.dispatch[tag]
684687
except KeyError:
685-
pass # unknown tag ?
686-
else:
687-
return f(self, "".join(self._data))
688+
if ':' not in tag:
689+
return # unknown tag ?
690+
try:
691+
f = self.dispatch[tag.split(':')[-1]]
692+
except KeyError:
693+
return # unknown tag ?
694+
return f(self, "".join(self._data))
688695

689696
#
690697
# accelerator support
@@ -694,9 +701,13 @@ def end_dispatch(self, tag, data):
694701
try:
695702
f = self.dispatch[tag]
696703
except KeyError:
697-
pass # unknown tag ?
698-
else:
699-
return f(self, data)
704+
if ':' not in tag:
705+
return # unknown tag ?
706+
try:
707+
f = self.dispatch[tag.split(':')[-1]]
708+
except KeyError:
709+
return # unknown tag ?
710+
return f(self, data)
700711

701712
#
702713
# element decoders
@@ -721,14 +732,23 @@ def end_boolean(self, data):
721732
def end_int(self, data):
722733
self.append(int(data))
723734
self._value = 0
735+
dispatch["i1"] = end_int
736+
dispatch["i2"] = end_int
724737
dispatch["i4"] = end_int
725738
dispatch["i8"] = end_int
726739
dispatch["int"] = end_int
740+
dispatch["biginteger"] = end_int
727741

728742
def end_double(self, data):
729743
self.append(float(data))
730744
self._value = 0
731745
dispatch["double"] = end_double
746+
dispatch["float"] = end_double
747+
748+
def end_bigdecimal(self, data):
749+
self.append(Decimal(data))
750+
self._value = 0
751+
dispatch["bigdecimal"] = end_bigdecimal
732752

733753
def end_string(self, data):
734754
if self._encoding:

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,9 @@ Core and Builtins
143143
Library
144144
-------
145145

146+
- Issue #26885: xmlrpc now supports unmarshalling additional data types used
147+
by Apache XML-RPC implementation for numerics and None.
148+
146149
- Issue #28070: Fixed parsing inline verbose flag in regular expressions.
147150

148151
- Issue #19500: Add client-side SSL session resumption to the ssl module.

0 commit comments

Comments
 (0)