# HG changeset patch # User paulb # Date 1191714525 0 # Node ID ae3c38d8e2beb749e25f8b8cd543669c6e887054 # Parent c2860e2547ab396da4011ac9363347d77ab68f4d [project @ 2007-10-06 23:48:42 by paulb] Removed the rpc module along with ParameterName and ParameterValue, focusing again on the document nodes themselves. diff -r c2860e2547ab -r ae3c38d8e2be libxml2dom/rpc.py --- a/libxml2dom/rpc.py Sat Oct 06 20:46:13 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,112 +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 . -""" - -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): - 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)) - -class ParameterValue(object): - - "A method parameter value." - - def __init__(self, name, value): - self.name = name - self.value = value - - def convert(self, converters=None): - conv = default_converters - conv.update(converters or {}) - typename = self.name.ns - localName = to_localName(self.name.name) - if isinstance(self.value, list): - return [item.convert(converters) for item in self.value] - else: - functions = conv.get(typename, {}) - function = functions.get(localName) or functions.get(None, unicode) - return function(self.value) - - def _data(self): - return self.convert() - - data = property(_data) - - def __eq__(self, other): - return self.name == other.name and self.value == other.value - - def __hash__(self): - return hash(self.value) - - def __repr__(self): - return "ParameterValue(%s, %s)" % (repr(self.name), repr(self.value)) - - # Sequence emulation. - - def __len__(self): - return len(self.value) - - def __getitem__(self, i): - return self.value[i] - -def to_localName(name): - return (name or "").split(":")[-1] or None - -# 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 c2860e2547ab -r ae3c38d8e2be libxml2dom/soap.py --- a/libxml2dom/soap.py Sat Oct 06 20:46:13 2007 +0000 +++ b/libxml2dom/soap.py Sat Oct 06 23:48:45 2007 +0000 @@ -32,7 +32,6 @@ from libxml2dom.macrolib import * from libxml2dom.macrolib import \ createDocument as Node_createDocument -from libxml2dom.rpc import ParameterName, ParameterValue # SOAP-related namespaces. @@ -133,6 +132,45 @@ ns.update(namespaces or {}) return libxml2dom.Node.xpath(self, expr, variables, ns) + # All nodes support convenience methods. + + def convert(self, node): + return node.textContent.strip() + + def _contents(self): + if not self.xpath("*"): + return (self.localName, getattr(self.ownerDocument, "convert", self.convert)(self)) + else: + return self + + def __len__(self): + children = self.xpath("*") + if not children: + return 2 + else: + return len(children) + + def __getitem__(self, i): + children = self.xpath("*") + if not children: + return self.contents[i] + else: + return self.xpath("*")[i] + + def __eq__(self, other): + children = self.xpath("*") + if children: + for i, j in map(None, self, other): + if i != j: + return False + return True + elif hasattr(other, "contents"): + return self.contents == other.contents + else: + return self.contents == other + + contents = property(_contents) + class SOAPDocument(libxml2dom._Document, SOAPNode): "A SOAP document fragment." @@ -214,7 +252,7 @@ def _resultParameterValue(self): if self.resultParameter: name = self.resultParameter.textContent.strip() - result = self.xpath(".//%s" % name, namespaces={self.prefix : self.namespaceURI}) + result = self.xpath(".//" + name, namespaces={self.prefix : self.namespaceURI}) if result: return result[0].textContent.strip() else: @@ -222,61 +260,13 @@ else: return None - def _parameters(self): - return self.xpath("*") - def _parameterValues(self): - values = [] - for parameter in self.parameters: - values.append(self._get_value(parameter)) - return values - - def _setParameterValues(self, parameters): - for node in self.parameters: - self.removeChild(node) - - # Add the parameter values. - - for parameter in parameters: - self._add_value(self, parameter) - - # Internal methods. - - def _add_value(self, value, parameter): - - "Add to the 'value' element the given 'parameter'." - - container = self.ownerDocument.createElementNS(parameter.name.ns, parameter.name.name) - value.appendChild(container) - if isinstance(parameter.value, (list, dict)): - if isinstance(parameter.value, dict): - items = parameter.value.items() - else: - items = parameter.value - for item in items: - self._add_value(container, item) - else: - 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 ParameterValue(ParameterName(parameter.namespaceURI, parameter.name), items) - else: - return ParameterValue(ParameterName(parameter.namespaceURI, parameter.name), parameter.textContent.strip()) + return [value.contents for value in self.xpath("*")] methodName = property(_methodName) resultParameter = property(_resultParameter) resultParameterValue = property(_resultParameterValue) - parameters = property(_parameters) - parameterValues = property(_parameterValues, _setParameterValues) + parameterValues = property(_parameterValues) class SOAPFaultElement(SOAPNode): diff -r c2860e2547ab -r ae3c38d8e2be libxml2dom/xmlrpc.py --- a/libxml2dom/xmlrpc.py Sat Oct 06 20:46:13 2007 +0000 +++ b/libxml2dom/xmlrpc.py Sat Oct 06 23:48:45 2007 +0000 @@ -32,7 +32,7 @@ from libxml2dom.macrolib import * from libxml2dom.macrolib import \ createDocument as Node_createDocument -from libxml2dom.rpc import ParameterName, ParameterValue +import datetime class XMLRPCImplementation(libxml2dom.Implementation): @@ -169,120 +169,8 @@ self.appendChild(methodName) self.methodNameElement.value = name - def _parameters(self): - return self.xpath("./params/param") - def _parameterValues(self): - values = self.xpath("./params/param/value") - if values: - items = [] - for value in values: - items.append(self._get_value(value)) - return items - else: - return [] - - def _setParameterValues(self, parameters): - param_list = self.parameters - params = (self.xpath("./params") or [None])[0] - - # Remove any previous parameters. - - if params: - if param_list: - for param in param_list: - params.removeChild(param) - else: - params = self.createParameters() - self.appendChild(params) - - # Add parameter values. - - for parameter in parameters: - param = self.ownerDocument.createElement("param") - params.appendChild(param) - value = self.ownerDocument.createElement("value") - param.appendChild(value) - self._add_value(value, parameter) - - # Internal methods. - - def _add_value(self, value, parameter): - - "Add to the 'value' element the given 'parameter'." - - if parameter.name.ns == "struct": - if isinstance(parameter.value, dict): - items = parameter.value.items() - else: - items = parameter.value - - # Create a struct element and add the members. - - struct = self.ownerDocument.createElement("struct") - value.appendChild(struct) - - for item in items: - 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) - - elif parameter.name.ns == "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(parameter.name.ns) - value.appendChild(container) - container.value = unicode(parameter.value) - - def _get_value(self, value, name=None): - - """ - 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(self._get_value(member.value, member.memberName)) - return ParameterValue(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 ParameterValue(ParameterName(value.type, name), items) - - else: - return ParameterValue(ParameterName(value.type, name), value.container.value) + return [value.container.contents for value in self.xpath("./params/param/value")] # Node construction methods. @@ -298,8 +186,7 @@ fault = property(_fault) methodNameElement = property(_methodNameElement) methodName = property(_methodName, _setMethodName) - parameters = property(_parameters) - parameterValues = property(_parameterValues, _setParameterValues) + parameterValues = property(_parameterValues) class XMLRPCArrayElement(XMLRPCNode): @@ -308,12 +195,36 @@ def _data(self): return (self.xpath("./data") or [None])[0] + def _contents(self): + return self + + # Sequence emulation. + + def __len__(self): + if self.data: + return len(self.data) + else: + return 0 + + def __getitem__(self, i): + if self.data: + return self.data[i] + else: + raise IndexError, i + + def __eq__(self, other): + for i, j in map(None, self, other): + if i != j: + return False + return True + # Node construction methods. def createData(self): return self.ownerDocument.createElement("data") data = property(_data) + contents = property(_contents) class XMLRPCStructElement(XMLRPCNode): @@ -322,12 +233,30 @@ def _members(self): return self.xpath("./member") + def _contents(self): + return self + + # Sequence emulation. + + def __len__(self): + return len(self.members) + + def __getitem__(self, i): + return self.members[i] + + def __eq__(self, other): + for i, j in map(None, self, other): + if i != j: + return False + return True + # Node construction methods. def createMember(self): return self.ownerDocument.createElement("member") members = property(_members) + contents = property(_contents) class XMLRPCDataElement(XMLRPCNode): @@ -336,13 +265,21 @@ def _values(self): return self.xpath("./value") - values = property(_values) + # Sequence emulation. + + def __len__(self): + return len(self.values) + + def __getitem__(self, i): + return self.values[i].container.contents # Node construction methods. def createValue(self): return self.ownerDocument.createElement("value") + values = property(_values) + class XMLRPCMemberElement(XMLRPCNode): "An XML-RPC structure member element." @@ -365,6 +302,20 @@ self.appendChild(nameElement) self.nameElement.value = name + def _contents(self): + return self + + # Item (name, value) emulation. + + def __len__(self): + return 2 + + def __getitem__(self, i): + return (self.memberName, self.value.container.contents)[i] + + def __eq__(self, other): + return self[0] == other[0] and self[1] == other[1] + # Node construction methods. def createName(self): @@ -376,11 +327,14 @@ value = property(_value) nameElement = property(_nameElement) memberName = property(_memberName, _setMemberName) + contents = property(_contents) class XMLRPCStringElement(XMLRPCNode): "An XML-RPC string element." + typename = "string" + def _value(self): return self.textContent.strip() @@ -390,7 +344,17 @@ text = self.ownerDocument.createTextNode(value) self.appendChild(text) + def _contents(self): + return convert(self.typename, self.value) + + def __eq__(self, other): + if hasattr(other, "contents"): + return self.contents == other.contents + else: + return self.contents == other + value = property(_value, _setValue) + contents = property(_contents) class XMLRPCNameElement(XMLRPCStringElement): @@ -425,31 +389,31 @@ "An XML-RPC integer element." - pass + typename = "int" class XMLRPCBooleanElement(XMLRPCStringElement): "An XML-RPC boolean element." - pass + typename = "boolean" class XMLRPCDoubleElement(XMLRPCStringElement): "An XML-RPC double floating point number element." - pass + typename = "double" class XMLRPCDateTimeElement(XMLRPCStringElement): "An XML-RPC date/time element." - pass + typename = "datetime" class XMLRPCBase64Element(XMLRPCStringElement): "An XML-RPC integer element." - pass + typename = "base64" class XMLRPCFaultElement(XMLRPCNode): @@ -472,6 +436,33 @@ code = property(_code) reason = property(_reason) +# Conversion functions. + +def convert(typename, value): + return default_converters[typename](value) + +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): + year, month, day, hour, minute, second = map(int, (s[:4], s[4:6], s[6:8], s[9:11], s[12:14], s[15:17])) + return datetime.datetime(year, month, day, hour, minute, second) + +default_converters = { + "string" : unicode, + "int" : int, + "i4" : int, + "double" : float, + "boolean" : boolean, + "dateTime.iso8601" : iso8601, + "base64" : str + } + # Utility functions. createDocument = libxml2dom.createDocument diff -r c2860e2547ab -r ae3c38d8e2be tests/soap_test.py --- a/tests/soap_test.py Sat Oct 06 20:46:13 2007 +0000 +++ b/tests/soap_test.py Sat Oct 06 23:48:45 2007 +0000 @@ -2,7 +2,6 @@ # -*- coding: iso-8859-15 -*- import libxml2dom.soap -from libxml2dom.rpc import ParameterName, ParameterValue request = """ @@ -33,14 +32,11 @@ req = libxml2dom.soap.parseString(request) assert req.method.methodName == "chargeReservation" assert req.method.parameterValues == [ - ParameterValue(ParameterName("http://travelcompany.example.org/reservation", "reservation"), [ - ParameterValue(ParameterName("http://travelcompany.example.org/reservation", "code"), "FT35ZBQ") - ]), - ParameterValue(ParameterName("http://mycompany.example.com/financial", "creditCard"), [ - ParameterValue(ParameterName("http://mycompany.example.com/employees", "name"), u"Åke Jógvan Øyvind"), - ParameterValue(ParameterName("http://mycompany.example.com/financial", "number"), "123456789099999"), - ParameterValue(ParameterName("http://mycompany.example.com/financial", "expiration"), "2005-02") - ]) + [("code", "FT35ZBQ")], + [("name", u"Åke Jógvan Øyvind"), + ("number", "123456789099999"), + ("expiration", "2005-02") + ] ] assert req.fault is None print "Method name:", req.method.methodName @@ -70,9 +66,8 @@ resp = libxml2dom.soap.parseString(response) assert resp.method.methodName == "chargeReservationResponse" assert resp.method.parameterValues == [ - ParameterValue(ParameterName("http://travelcompany.example.org/", "code"), "FT35ZBQ"), - ParameterValue(ParameterName("http://travelcompany.example.org/", "viewAt"), - "http://travelcompany.example.org/reservations?code=FT35ZBQ") + ("code", "FT35ZBQ"), + ("viewAt", "http://travelcompany.example.org/reservations?code=FT35ZBQ") ] assert resp.fault is None print "Method name:", resp.method.methodName @@ -105,11 +100,10 @@ resp2 = libxml2dom.soap.parseString(response2) assert resp2.method.methodName == "chargeReservationResponse" assert resp2.method.parameterValues == [ - ParameterValue(ParameterName("http://www.w3.org/2003/05/soap-rpc", "result"), "m:status"), - ParameterValue(ParameterName("http://travelcompany.example.org/", "status"), "confirmed"), - ParameterValue(ParameterName("http://travelcompany.example.org/", "code"), "FT35ZBQ"), - ParameterValue(ParameterName("http://travelcompany.example.org/", "viewAt"), - "http://travelcompany.example.org/reservations?code=FT35ZBQ") + ("result", "m:status"), + ("status", "confirmed"), + ("code", "FT35ZBQ"), + ("viewAt", "http://travelcompany.example.org/reservations?code=FT35ZBQ") ] assert resp2.fault is None print "Method name:", resp2.method.methodName diff -r c2860e2547ab -r ae3c38d8e2be tests/xmlrpc_test.py --- a/tests/xmlrpc_test.py Sat Oct 06 20:46:13 2007 +0000 +++ b/tests/xmlrpc_test.py Sat Oct 06 23:48:45 2007 +0000 @@ -1,7 +1,6 @@ #!/usr/bin/env python import libxml2dom.xmlrpc -from libxml2dom.rpc import ParameterName, ParameterValue # Some examples from the specification. @@ -17,7 +16,7 @@ req = libxml2dom.xmlrpc.parseString(request) assert req.method.methodName == "examples.getStateName" -assert req.method.parameterValues == [ParameterValue(ParameterName("i4", ""), "41")] +assert req.method.parameterValues == [41] assert req.fault is None print "Method name:", req.method.methodName print "Parameter values:", req.method.parameterValues @@ -34,7 +33,7 @@ resp = libxml2dom.xmlrpc.parseString(response) assert resp.method.methodName is None -assert resp.method.parameterValues == [ParameterValue(ParameterName("string", ""), "South Dakota")] +assert resp.method.parameterValues == ["South Dakota"] assert resp.fault is None print "Method name:", resp.method.methodName print "Parameter values:", resp.method.parameterValues @@ -98,11 +97,11 @@ s = libxml2dom.xmlrpc.parseString(search) assert s.method.methodName == "search" assert s.method.parameterValues == [ - ParameterValue(ParameterName("struct", None), [ - ParameterValue(ParameterName("string", "name"), "libxml2dom"), - ParameterValue(ParameterName("string", "description"), "XML") - ]), - ParameterValue(ParameterName("string", None), "and") + [ + ("name", "libxml2dom"), + ("description", "XML") + ], + "and" ] assert s.fault is None print "Method name:", s.method.methodName @@ -147,13 +146,13 @@ s2 = libxml2dom.xmlrpc.parseString(search2) assert s2.method.methodName == "search" assert s2.method.parameterValues == [ - ParameterValue(ParameterName("struct", None), [ - ParameterValue(ParameterName("struct", "names"), [ - ParameterValue(ParameterName("string", "name"), "libxml2dom"), - ParameterValue(ParameterName("string", "description"), "XML") + [ + ("names", [ + ("name", "libxml2dom"), + ("description", "XML") ]) - ]), - ParameterValue(ParameterName("string", None), "and") + ], + "and" ] assert s2.fault is None print "Method name:", s2.method.methodName @@ -182,12 +181,7 @@ a = libxml2dom.xmlrpc.parseString(arrays) assert a.method.methodName is None -assert a.method.parameterValues == [ - ParameterValue(ParameterName("array", None), [ - ParameterValue(ParameterName("string", None), "libxml2dom"), - ParameterValue(ParameterName("string", None), "XSLTools") - ]) - ] +assert a.method.parameterValues == [["libxml2dom", "XSLTools"]] assert a.fault is None print "Method name:", a.method.methodName print "Parameter values:", a.method.parameterValues