1.1 --- a/libxml2dom/xmlrpc.py Sun Sep 30 00:57:06 2007 +0000
1.2 +++ b/libxml2dom/xmlrpc.py Sun Sep 30 23:23:40 2007 +0000
1.3 @@ -32,6 +32,7 @@
1.4 from libxml2dom.macrolib import *
1.5 from libxml2dom.macrolib import \
1.6 createDocument as Node_createDocument
1.7 +import datetime
1.8
1.9 class XMLRPCImplementation(libxml2dom.Implementation):
1.10
1.11 @@ -77,6 +78,8 @@
1.12 return XMLRPCStructElement(_node, self, context_node.ownerDocument)
1.13 elif Node_localName(_node) == "member":
1.14 return XMLRPCMemberElement(_node, self, context_node.ownerDocument)
1.15 + elif Node_localName(_node) == "value":
1.16 + return XMLRPCValueElement(_node, self, context_node.ownerDocument)
1.17
1.18 # Otherwise, make generic XML-RPC elements.
1.19
1.20 @@ -93,6 +96,25 @@
1.21
1.22 return XMLRPCDocument(Node_createDocument(namespaceURI, localName, None), self).documentElement
1.23
1.24 + def createMethodCall(self):
1.25 + return self.createXMLRPCMessage(None, "methodCall")
1.26 +
1.27 + def createMethodResponse(self):
1.28 + return self.createXMLRPCMessage(None, "methodResponse")
1.29 +
1.30 +# Internal utility functions.
1.31 +
1.32 +def boolean(s):
1.33 + if s.lower() == "true":
1.34 + return True
1.35 + elif s.lower() == "false":
1.36 + return False
1.37 + else:
1.38 + raise ValueError, "String value %s not convertable to boolean." % repr(s)
1.39 +
1.40 +def iso8601(s):
1.41 + return s
1.42 +
1.43 # Node classes.
1.44
1.45 class XMLRPCNode(libxml2dom.Node):
1.46 @@ -101,47 +123,213 @@
1.47
1.48 pass
1.49
1.50 +class XMLRPCElement(XMLRPCNode):
1.51 +
1.52 + "An XML-RPC element."
1.53 +
1.54 + pass
1.55 +
1.56 class XMLRPCDocument(libxml2dom._Document, XMLRPCNode):
1.57
1.58 "An XML-RPC document fragment."
1.59
1.60 def _method(self):
1.61 - return self.xpath("./methodCall|./methodResponse")[0]
1.62 + return (self.xpath("./methodCall|./methodResponse") or [None])[0]
1.63
1.64 method = property(_method)
1.65
1.66 -class XMLRPCElement(XMLRPCNode):
1.67 + # Convenience methods and properties.
1.68 +
1.69 + def _methodName(self):
1.70 + if self.method is not None:
1.71 + return self.method.name
1.72 + else:
1.73 + return None
1.74 +
1.75 + def _parameterItems(self):
1.76 + if self.method is not None:
1.77 + return self.method.parameterItems
1.78 + else:
1.79 + return None
1.80 +
1.81 + def _parameterValues(self):
1.82 + if self.method is not None:
1.83 + return self.method.parameterValues
1.84 + else:
1.85 + return None
1.86
1.87 - "An XML-RPC element."
1.88 + def _parameterMap(self):
1.89 + if self.method is not None:
1.90 + return self.method.parameterMap
1.91 + else:
1.92 + return None
1.93 +
1.94 + def _fault(self):
1.95 + if self.method is not None:
1.96 + return self.method.fault
1.97 + else:
1.98 + return None
1.99
1.100 - pass
1.101 + # Node construction methods.
1.102 +
1.103 + def createMethodCall(self):
1.104 + return self.ownerDocument.createElement("methodCall")
1.105 +
1.106 + def createMethodResponse(self):
1.107 + return self.ownerDocument.createElement("methodResponse")
1.108 +
1.109 + methodName = property(_methodName)
1.110 + parameterItems = property(_parameterItems)
1.111 + parameterValues = property(_parameterValues)
1.112 + parameterMap = property(_parameterMap)
1.113 + fault = property(_fault)
1.114
1.115 class XMLRPCMethodElement(XMLRPCNode):
1.116
1.117 "An XML-RPC method element."
1.118
1.119 def _fault(self):
1.120 - return self.xpath("./fault")[0]
1.121 + return (self.xpath("./fault") or [None])[0]
1.122
1.123 def _methodName(self):
1.124 - return self.xpath("./methodName")[0]
1.125 + return (self.xpath("./methodName") or [None])[0]
1.126 +
1.127 + def _name(self):
1.128 + name = self.methodName
1.129 + if name is not None:
1.130 + return name.value
1.131 + else:
1.132 + return None
1.133 +
1.134 + def _setName(self, name):
1.135 + if self.methodName is None:
1.136 + methodName = self.createMethodName()
1.137 + self.appendChild(methodName)
1.138 + self.methodName.value = name
1.139
1.140 def _parameters(self):
1.141 - return self.xpath("./params/param/value//*[not(./*)]")
1.142 + return self.xpath("./params/param")
1.143 +
1.144 + def _parameterItems(self):
1.145 + values = self.xpath("./params/param/value")
1.146 + if values:
1.147 + items = []
1.148 + for value in values:
1.149 + items.append(self._item_contents(value))
1.150 + return items
1.151 + else:
1.152 + return []
1.153
1.154 def _parameterValues(self):
1.155 - values = {}
1.156 - for parameter in self.parameters:
1.157 - values[(parameter.namespaceURI, parameter.localName)] = parameter.textContent.strip()
1.158 + return self._value_contents(self.parameterItems)
1.159 +
1.160 + def _setParameterValues(self, parameters):
1.161 + param_list = self.parameters
1.162 + params = (self.xpath("./params") or [None])[0]
1.163 + if params:
1.164 + if param_list:
1.165 + for param in param_list:
1.166 + params.removeChild(param)
1.167 + else:
1.168 + params = self.createParameters()
1.169 + self.appendChild(params)
1.170 +
1.171 + for parameter in parameters:
1.172 + param = self.ownerDocument.createElement("param")
1.173 + params.appendChild(param)
1.174 + value = self.ownerDocument.createElement("value")
1.175 + param.appendChild(value)
1.176 +
1.177 + # NOTE: Only handles simple types.
1.178 +
1.179 + container = self._make_container(parameter)
1.180 + value.appendChild(container)
1.181 + text = self.ownerDocument.createTextNode(unicode(parameter))
1.182 + container.appendChild(text)
1.183 +
1.184 + def _parameterMap(self):
1.185 + return self._map_contents(self.parameterItems)
1.186 +
1.187 + # Internal data.
1.188 +
1.189 + converters = {
1.190 + "string" : unicode,
1.191 + "int" : int,
1.192 + "i4" : int,
1.193 + "double" : float,
1.194 + "boolean" : boolean, # see the module globals
1.195 + "dateTime.iso8601" : iso8601, # see the module globals
1.196 + "base64" : str
1.197 + }
1.198 +
1.199 + # Internal methods.
1.200 +
1.201 + def _make_container(self, parameter):
1.202 + if isinstance(parameter, (str, unicode)):
1.203 + return self.ownerDocument.createElement("string")
1.204 + elif isinstance(parameter, (int, long)):
1.205 + return self.ownerDocument.createElement("int")
1.206 + elif isinstance(parameter, float):
1.207 + return self.ownerDocument.createElement("double")
1.208 + elif isinstance(parameter, bool):
1.209 + return self.ownerDocument.createElement("boolean")
1.210 + elif isinstance(parameter, datetime.datetime):
1.211 + return self.ownerDocument.createElement("dateTime.iso8601")
1.212 +
1.213 + def _from_string(self, typename, value):
1.214 + return self.converters.get(typename, str)(value)
1.215 +
1.216 + def _item_contents(self, value):
1.217 + if value.type == "struct":
1.218 + items = []
1.219 + for member in value.container.members:
1.220 + if member.value.type == "struct":
1.221 + items.append((member.name, self._item_contents(member.value)))
1.222 + else:
1.223 + items.append((member.name, self._from_string(member.value.type, member.value.container.value)))
1.224 + return None, items
1.225 + else:
1.226 + return None, self._from_string(value.type, value.container.value)
1.227 +
1.228 + def _value_contents(self, items):
1.229 + values = []
1.230 + for name, value in items:
1.231 + if isinstance(value, list):
1.232 + values.append(self._value_contents(value))
1.233 + elif name is None:
1.234 + values.append(value)
1.235 + else:
1.236 + values.append((name, value))
1.237 return values
1.238
1.239 + def _map_contents(self, items):
1.240 + d = {}
1.241 + for n, (name, value) in enumerate(items):
1.242 + key_name = name or str(n)
1.243 + if isinstance(value, list):
1.244 + d[key_name] = self._map_contents(value)
1.245 + else:
1.246 + d[key_name] = value
1.247 + return d
1.248 +
1.249 + # Node construction methods.
1.250 +
1.251 + def createMethodName(self):
1.252 + return self.ownerDocument.createElement("methodName")
1.253 +
1.254 + def createParameters(self):
1.255 + return self.ownerDocument.createElement("params")
1.256 +
1.257 def createFault(self):
1.258 return self.ownerDocument.createElement("fault")
1.259
1.260 fault = property(_fault)
1.261 + name = property(_name, _setName)
1.262 methodName = property(_methodName)
1.263 parameters = property(_parameters)
1.264 - parameterValues = property(_parameterValues)
1.265 + parameterItems = property(_parameterItems)
1.266 + parameterValues = property(_parameterValues, _setParameterValues)
1.267 + parameterMap = property(_parameterMap)
1.268
1.269 class XMLRPCStringElement(XMLRPCNode):
1.270
1.271 @@ -158,6 +346,23 @@
1.272
1.273 value = property(_value, _setValue)
1.274
1.275 +class XMLRPCValueElement(XMLRPCStringElement):
1.276 +
1.277 + "An XML-RPC value element."
1.278 +
1.279 + def _type(self):
1.280 + elements = self.xpath("*")
1.281 + if elements:
1.282 + return elements[0].localName
1.283 + else:
1.284 + return "string"
1.285 +
1.286 + def _container(self):
1.287 + return (self.xpath("*") or [self])[0]
1.288 +
1.289 + type = property(_type)
1.290 + container = property(_container)
1.291 +
1.292 class XMLRPCMethodNameElement(XMLRPCStringElement):
1.293
1.294 "An XML-RPC method element."
1.295 @@ -208,10 +413,14 @@
1.296 "An XML-RPC structure member element."
1.297
1.298 def _name(self):
1.299 - return self.xpath("./name")[0].textContent.strip()
1.300 + value = self.xpath("./name")
1.301 + if value:
1.302 + return value[0].textContent.strip()
1.303 + else:
1.304 + return None
1.305
1.306 def _value(self):
1.307 - return self.xpath("./value")[0]
1.308 + return (self.xpath("./value") or [None])[0]
1.309
1.310 name = property(_name)
1.311 value = property(_value)
1.312 @@ -221,10 +430,18 @@
1.313 "An XML-RPC fault element."
1.314
1.315 def _code(self):
1.316 - return self.xpath("./value/struct/member[./name/text() = 'faultCode']/value/int")[0]
1.317 + code = self.xpath("./value/struct/member[./name/text() = 'faultCode']/value/int")
1.318 + if code:
1.319 + return code[0].value
1.320 + else:
1.321 + return None
1.322
1.323 def _reason(self):
1.324 - return self.xpath("./value/struct/member[./name/text() = 'faultString']/value/string")[0]
1.325 + reason = self.xpath("./value/struct/member[./name/text() = 'faultString']/value/string")
1.326 + if reason:
1.327 + return reason[0].value
1.328 + else:
1.329 + return None
1.330
1.331 code = property(_code)
1.332 reason = property(_reason)
1.333 @@ -235,7 +452,13 @@
1.334 createDocumentType = libxml2dom.createDocumentType
1.335
1.336 def createXMLRPCMessage(namespaceURI, localName):
1.337 - return default_impl.createXMLRPCMessage(namespaceURI, localName)
1.338 + return default_impl.createXMLRPCMessage(None, localName)
1.339 +
1.340 +def createMethodCall():
1.341 + return default_impl.createMethodCall()
1.342 +
1.343 +def createMethodResponse():
1.344 + return default_impl.createMethodResponse()
1.345
1.346 def parse(stream_or_string, html=0, htmlencoding=None, unfinished=0, impl=None):
1.347 return libxml2dom.parse(stream_or_string, html=html, htmlencoding=htmlencoding, unfinished=unfinished, impl=(impl or default_impl))