debian-koji/koji/xmlrpcplus.py
Ken Dreyer aa35b84c8c xmlrpcplus: use parent's int marshalling if possible
If we have an int value between xmlrpc.MININT and xmlrpc.MAXINT, we can
rely on stdlib's xmlrpc.Marshaller. This allows us to drop this custom
code.
2020-02-21 09:44:46 +01:00

119 lines
3.4 KiB
Python

"""
Custom xmlrpc handling for Koji
"""
from __future__ import absolute_import
import types
import six
import six.moves.xmlrpc_client as xmlrpc_client
# duplicate a few values that we need
getparser = xmlrpc_client.getparser
loads = xmlrpc_client.loads
Fault = xmlrpc_client.Fault
DateTime = xmlrpc_client.DateTime
class ExtendedMarshaller(xmlrpc_client.Marshaller):
dispatch = xmlrpc_client.Marshaller.dispatch.copy()
def _dump(self, value, write):
# Parent class is unfriendly to subclasses :-/
f = self.dispatch[type(value)]
f(self, value, write)
def dump_generator(self, value, write):
dump = self._dump
write("<value><array><data>\n")
for v in value:
dump(v, write)
write("</data></array></value>\n")
dispatch[types.GeneratorType] = dump_generator
MAXI8 = 2 ** 63 - 1
MINI8 = -2 ** 63
def dump_int(self, value, write):
# python2's xmlrpclib doesn't support i8 extension for marshalling,
# but can unmarshall it correctly.
if (value > self.MAXI8 or value < self.MINI8):
raise OverflowError("long int exceeds XML-RPC limits")
elif (value > xmlrpc_client.MAXINT or
value < xmlrpc_client.MININT):
write("<value><i8>")
write(str(int(value)))
write("</i8></value>\n")
else:
return xmlrpc_client.Marshaller.dump_int(self, value, write)
dispatch[int] = dump_int
# we always want to allow None
def dump_nil(self, value, write):
write("<value><nil/></value>")
dispatch[type(None)] = dump_nil
if six.PY2:
ExtendedMarshaller.dispatch[long] = ExtendedMarshaller.dump_int
def dumps(params, methodname=None, methodresponse=None, encoding=None,
allow_none=1, marshaller=None):
"""encode an xmlrpc request or response
Differences from the xmlrpclib version:
- allow_none is on by default
- uses our ExtendedMarshaller by default
- option to specify marshaller
"""
if isinstance(params, Fault):
methodresponse = 1
elif not isinstance(params, tuple):
raise TypeError('params must be a tuple or Fault instance')
elif methodresponse and len(params) != 1:
raise ValueError('response tuple must be a singleton')
if not encoding:
encoding = "utf-8"
if marshaller is not None:
m = marshaller(encoding)
else:
m = ExtendedMarshaller(encoding)
data = m.dumps(params)
if encoding != "utf-8":
xmlheader = "<?xml version='1.0' encoding='%s'?>\n" % str(encoding)
else:
xmlheader = "<?xml version='1.0'?>\n" # utf-8 is default
# standard XML-RPC wrappings
if methodname:
# a method call
if six.PY2 and isinstance(methodname, six.text_type):
# Do we need this?
methodname = methodname.encode(encoding, 'xmlcharrefreplace')
parts = (
xmlheader,
"<methodCall>\n"
"<methodName>", methodname, "</methodName>\n",
data,
"</methodCall>\n"
)
elif methodresponse:
# a method response, or a fault structure
parts = (
xmlheader,
"<methodResponse>\n",
data,
"</methodResponse>\n"
)
else:
return data # return as is
return ''.join(parts)