# HG changeset patch # User paulb # Date 1191699202 0 # Node ID 1872e9e7e1b13e884dacaaeb3b0af429b87c59fb # Parent 62677a2566f0b49abe309638bdf11259b636f53f [project @ 2007-10-06 19:33:22 by paulb] Renamed conversions to rpc. Introduced a ParameterName class in order to attempt to encapsulate naming differences between XML-RPC and SOAP. Added XML-RPC array support. Renamed parameterValues property to rawParameterValues, with a new parameterValues property performing conversion on the retrieved values. diff -r 62677a2566f0 -r 1872e9e7e1b1 libxml2dom/conversions.py --- a/libxml2dom/conversions.py Mon Oct 01 23:27:18 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ -#!/usr/bin/env python - -""" -Conversion functions and data used by XML-RPC and SOAP. - -Copyright (C) 2007 Paul Boddie - -This program is free software; you can redistribute it and/or modify it under -the terms of the GNU Lesser General Public License as published by the Free -Software Foundation; either version 3 of the License, or (at your option) any -later version. - -This program is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. - -You should have received a copy of the GNU Lesser General Public License along -with this program. If not, see . -""" - -converters = { - "string" : unicode, - "int" : int, - "i4" : int, - "double" : float, - "boolean" : boolean, # see the module globals - "dateTime.iso8601" : iso8601, # see the module globals - "base64" : str - } - -def from_string(typename, value): - return converters.get(typename, str)(value) - -# Utility functions. - -def boolean(s): - if s.lower() == "true": - return True - elif s.lower() == "false": - return False - else: - raise ValueError, "String value %s not convertable to boolean." % repr(s) - -def iso8601(s): - # NOTE: To be written. - return s - -# vim: tabstop=4 expandtab shiftwidth=4 diff -r 62677a2566f0 -r 1872e9e7e1b1 libxml2dom/rpc.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libxml2dom/rpc.py Sat Oct 06 19:33:22 2007 +0000 @@ -0,0 +1,102 @@ +#!/usr/bin/env python + +""" +Conversion functions and data used by XML-RPC and SOAP. + +Copyright (C) 2007 Paul Boddie + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the Free +Software Foundation; either version 3 of the License, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. + +You should have received a copy of the GNU Lesser General Public License along +with this program. If not, see . +""" + +import datetime + +# Utility classes. + +class ParameterName(object): + + "A method parameter name." + + def __init__(self, ns, name): + self.ns = ns + self.name = name + + def __eq__(self, other): + other_ns, other_name = other + return self.ns, to_localName(self.name) == other_ns, to_localName(other_name) + + def __hash__(self): + return hash(self.ns + to_localName(self.name)) + + def __repr__(self): + return "ParameterName(%s, %s)" % (repr(self.ns), repr(self.name or None)) + + # Sequence emulation. + + def __len__(self): + return 2 + + def __getitem__(self, i): + return (self.ns, self.name)[i] + +def to_localName(name): + return (name or "").split(":")[-1] or None + +def convert(parameters, converters=None): + + """ + Convert the 'parameters', returning a list of name, value items where the + names are the plain names for each parameter, and the values may be + converted from strings to other data types. + """ + + conv = default_converters + conv.update(converters or {}) + results = [] + for parameter_name, parameter_value in parameters: + typename, name = parameter_name + localName = to_localName(name) + if isinstance(parameter_value, list): + value = convert(parameter_value, converters) + else: + functions = conv.get(typename, {}) + function = functions.get(localName) or functions.get(None, unicode) + value = function(parameter_value) + results.append((localName, value)) + return results + +# Utility functions. + +def boolean(s): + if s.lower() == "true": + return True + elif s.lower() == "false": + return False + else: + raise ValueError, "String value %s not convertable to boolean." % repr(s) + +def iso8601(s): + # NOTE: To be written. + return s + +default_converters = { + "string" : {None : unicode}, + "int" : {None : int}, + "i4" : {None : int}, + "double" : {None : float}, + "boolean" : {None : boolean}, + "dateTime.iso8601" : {None : iso8601}, + "base64" : {None : str} + } + +# vim: tabstop=4 expandtab shiftwidth=4 diff -r 62677a2566f0 -r 1872e9e7e1b1 libxml2dom/soap.py --- a/libxml2dom/soap.py Mon Oct 01 23:27:18 2007 +0000 +++ b/libxml2dom/soap.py Sat Oct 06 19:33:22 2007 +0000 @@ -32,6 +32,7 @@ from libxml2dom.macrolib import * from libxml2dom.macrolib import \ createDocument as Node_createDocument +from libxml2dom.rpc import ParameterName # SOAP-related namespaces. @@ -224,13 +225,13 @@ def _parameters(self): return self.xpath("*") - def _parameterValues(self): + def _rawParameterValues(self): values = [] for parameter in self.parameters: values.append(self._get_value(parameter)) return values - def _setParameterValues(self, parameters): + def _setRawParameterValues(self, parameters): for node in self.parameters: self.removeChild(node) @@ -239,38 +240,48 @@ for parameter in parameters: self._add_value(self, parameter) + def _parameterValues(self): + return libxml2dom.rpc.convert(self.rawParameterValues, getattr(self.ownerDocument, "converters", None)) + # Internal methods. def _add_value(self, value, parameter): - (ns, name), parameter = parameter - container = self.ownerDocument.createElementNS(ns, name) + + "Add to the 'value' element the given 'parameter'." + + parameter_name, parameter_value = parameter + container = self.ownerDocument.createElementNS(*parameter_name) value.appendChild(container) - if isinstance(parameter, (list, dict)): - if isinstance(parameter, dict): - items = parameter.items() + if isinstance(parameter_value, (list, dict)): + if isinstance(parameter_value, dict): + items = parameter_value.items() else: - items = parameter + items = parameter_value for item in items: self._add_value(container, item) else: - text = self.ownerDocument.createTextNode(unicode(parameter)) + text = self.ownerDocument.createTextNode(unicode(parameter_value)) container.appendChild(text) def _get_value(self, parameter): + + "Return the parameter name and value from within the given 'parameter'." + elements = parameter.xpath("*") if elements: items = [] for element in elements: items.append(self._get_value(element)) - return (parameter.namespaceURI, parameter.name), items + return ParameterName(parameter.namespaceURI, parameter.name), items else: - return (parameter.namespaceURI, parameter.name), parameter.textContent.strip() + return ParameterName(parameter.namespaceURI, parameter.name), parameter.textContent.strip() methodName = property(_methodName) resultParameter = property(_resultParameter) resultParameterValue = property(_resultParameterValue) parameters = property(_parameters) - parameterValues = property(_parameterValues, _setParameterValues) + rawParameterValues = property(_rawParameterValues, _setRawParameterValues) + parameterValues = property(_parameterValues) class SOAPFaultElement(SOAPNode): diff -r 62677a2566f0 -r 1872e9e7e1b1 libxml2dom/xmlrpc.py --- a/libxml2dom/xmlrpc.py Mon Oct 01 23:27:18 2007 +0000 +++ b/libxml2dom/xmlrpc.py Sat Oct 06 19:33:22 2007 +0000 @@ -32,7 +32,7 @@ from libxml2dom.macrolib import * from libxml2dom.macrolib import \ createDocument as Node_createDocument -import datetime +from libxml2dom.rpc import ParameterName class XMLRPCImplementation(libxml2dom.Implementation): @@ -82,6 +82,10 @@ return XMLRPCValueElement(_node, self, context_node.ownerDocument) elif Node_localName(_node) == "name": return XMLRPCNameElement(_node, self, context_node.ownerDocument) + elif Node_localName(_node) == "array": + return XMLRPCArrayElement(_node, self, context_node.ownerDocument) + elif Node_localName(_node) == "data": + return XMLRPCDataElement(_node, self, context_node.ownerDocument) # Otherwise, make generic XML-RPC elements. @@ -168,7 +172,7 @@ def _parameters(self): return self.xpath("./params/param") - def _parameterValues(self): + def _rawParameterValues(self): values = self.xpath("./params/param/value") if values: items = [] @@ -178,7 +182,7 @@ else: return [] - def _setParameterValues(self, parameters): + def _setRawParameterValues(self, parameters): param_list = self.parameters params = (self.xpath("./params") or [None])[0] @@ -201,37 +205,90 @@ param.appendChild(value) self._add_value(value, parameter) + def _parameterValues(self): + return libxml2dom.rpc.convert(self.rawParameterValues) + # Internal methods. def _add_value(self, value, parameter): - typename, parameter = parameter + + "Add to the 'value' element the given 'parameter'." + + (typename, parameter_name), parameter_value = parameter + if typename == "struct": - if isinstance(parameter, dict): - items = parameter.items() + if isinstance(parameter_value, dict): + items = parameter_value.items() else: - items = parameter + items = parameter_value + + # Create a struct element and add the members. + struct = self.ownerDocument.createElement("struct") - for item_name, item_value in items: + value.appendChild(struct) + + for item in items: + (item_typename, item_name), item_value = item member = struct.createMember() struct.appendChild(member) + + # Peek into the item to set up the name. + member.memberName = item_name + + # Add the item inside a new value element. + memberValue = member.createValue() member.appendChild(memberValue) - self._add_value(memberValue, item_value) - value.appendChild(struct) + self._add_value(memberValue, item) + + elif typename == "array": + + # Create an array element and add the members. + + array = self.ownerDocument.createElement("array") + value.appendChild(array) + data = array.createData() + array.appendChild(data) + + for item in parameter_value: + + # Add the item inside a new value element. + + data_value = data.createValue() + data.appendChild(data_value) + self._add_value(data_value, item) + else: container = self.ownerDocument.createElement(typename) value.appendChild(container) - container.value = unicode(parameter) + container.value = unicode(parameter_value) + + def _get_value(self, value, name=None): - def _get_value(self, value): + """ + Return the parameter name and value from within the given 'value' + element, using the optional 'name' as part of the returned name if + specified. + """ + if value.type == "struct": items = [] + + # Peek inside member values to get member names. + for member in value.container.members: - items.append((member.memberName, self._get_value(member.value))) - return (value.type, items) + items.append(self._get_value(member.value, member.memberName)) + return (ParameterName(value.type, name), items) + + elif value.type == "array": + items = [] + for data_value in value.container.data.values: + items.append(self._get_value(data_value)) + return (ParameterName(value.type, name), items) + else: - return (value.type, value.container.value) + return (ParameterName(value.type, name), value.container.value) # Node construction methods. @@ -248,7 +305,22 @@ methodNameElement = property(_methodNameElement) methodName = property(_methodName, _setMethodName) parameters = property(_parameters) - parameterValues = property(_parameterValues, _setParameterValues) + rawParameterValues = property(_rawParameterValues, _setRawParameterValues) + parameterValues = property(_parameterValues) + +class XMLRPCArrayElement(XMLRPCNode): + + "An XML-RPC array element." + + def _data(self): + return (self.xpath("./data") or [None])[0] + + # Node construction methods. + + def createData(self): + return self.ownerDocument.createElement("data") + + data = property(_data) class XMLRPCStructElement(XMLRPCNode): @@ -264,10 +336,27 @@ members = property(_members) +class XMLRPCDataElement(XMLRPCNode): + + "An XML-RPC array data element." + + def _values(self): + return self.xpath("./value") + + values = property(_values) + + # Node construction methods. + + def createValue(self): + return self.ownerDocument.createElement("value") + class XMLRPCMemberElement(XMLRPCNode): "An XML-RPC structure member element." + def _value(self): + return (self.xpath("./value") or [None])[0] + def _nameElement(self): return (self.xpath("./name") or [None])[0] @@ -283,9 +372,6 @@ self.appendChild(nameElement) self.nameElement.value = name - def _value(self): - return (self.xpath("./value") or [None])[0] - # Node construction methods. def createName(self): @@ -294,9 +380,9 @@ def createValue(self): return self.ownerDocument.createElement("value") + value = property(_value) nameElement = property(_nameElement) memberName = property(_memberName, _setMemberName) - value = property(_value) class XMLRPCStringElement(XMLRPCNode):