1.1 --- a/libxml2dom/xmlrpc.py Fri Sep 12 01:03:56 2008 +0200
1.2 +++ b/libxml2dom/xmlrpc.py Sun Sep 14 02:30:58 2008 +0200
1.3 @@ -25,7 +25,13 @@
1.4 The sending and receiving of XML-RPC messages can be done using traditional HTTP
1.5 libraries.
1.6
1.7 -See tests/xmlrpc_test.py for more details.
1.8 +See tests/test_xmlrpc_parse.py for more details.
1.9 +
1.10 +Useful properties:
1.11 +
1.12 + * container - returns the actual value element containing transmitted data
1.13 + * contents - returns the converted data from within a container or a
1.14 + container object
1.15 """
1.16
1.17 import libxml2dom
1.18 @@ -33,6 +39,7 @@
1.19 from libxml2dom.macrolib import \
1.20 createDocument as Node_createDocument
1.21 import datetime
1.22 +import xml.dom
1.23
1.24 # Node classes.
1.25
1.26 @@ -41,12 +48,78 @@
1.27 "Convenience modifications to nodes specific to libxml2dom.xmlrpc."
1.28
1.29 def add_or_replace_element(self, new_element):
1.30 +
1.31 + """
1.32 + Add or replace the given 'new_element', using its localName to find any
1.33 + element to be replaced.
1.34 + """
1.35 +
1.36 elements = self.xpath(new_element.localName)
1.37 if elements:
1.38 self.replaceChild(new_element, elements[0])
1.39 else:
1.40 self.appendChild(new_element)
1.41
1.42 + def serialise_value(self, parent, value):
1.43 +
1.44 + "Serialise, under the 'parent', the given 'value' object."
1.45 +
1.46 + if isinstance(value, (str, int, float, bool)):
1.47 + valueElement = parent.createValue(typenames[value.__class__.__name__], 1)
1.48 + valueElement.container.value = str(value)
1.49 +
1.50 + elif isinstance(value, datetime.datetime):
1.51 + valueElement = parent.createValue("dateTime.iso8601", 1)
1.52 + valueElement.container.value = value.strftime("%Y%m%dT%H:%M:%S")
1.53 +
1.54 + elif isinstance(value, (tuple, list)):
1.55 + array = parent.createValue("array", 1).container
1.56 + dataElement = array.createData(1)
1.57 + for v in value:
1.58 + self.serialise_value(dataElement, v)
1.59 +
1.60 + elif isinstance(value, dict):
1.61 + struct = parent.createValue("struct", 1).container
1.62 + for k, v in value.items():
1.63 + member = struct.createMember(1)
1.64 + member.memberName = str(k)
1.65 + self.serialise_value(member, v)
1.66 +
1.67 + else:
1.68 + raise ValueError, "Value %r cannot be serialised." % value
1.69 +
1.70 +class ContentEquality:
1.71 +
1.72 + "Equality testing."
1.73 +
1.74 + def __eq__(self, other):
1.75 + if hasattr(other, "contents"):
1.76 + return self.contents == other.contents
1.77 + else:
1.78 + return self.contents == other
1.79 +
1.80 + def __ne__(self, other):
1.81 + return not self.__eq__(other)
1.82 +
1.83 +class SequenceEquality:
1.84 +
1.85 + "Sequence equality testing."
1.86 +
1.87 + def __eq__(self, other):
1.88 + for i, j in map(None, self, other):
1.89 + if i != j:
1.90 + return False
1.91 + return True
1.92 +
1.93 + def __ne__(self, other):
1.94 + return not self.__eq__(other)
1.95 +
1.96 + def __len__(self):
1.97 + return len(self.values())
1.98 +
1.99 + def __getitem__(self, i):
1.100 + return self.values()[i]
1.101 +
1.102 class XMLRPCElement(XMLRPCNode):
1.103
1.104 "An XML-RPC element."
1.105 @@ -110,9 +183,21 @@
1.106 self.methodNameElement.value = name
1.107
1.108 def _parameterValues(self):
1.109 - return [value.container.contents for value in self.xpath("./params/param/value")]
1.110 + if self.params:
1.111 + return self.params.values()
1.112 + else:
1.113 + return None
1.114 +
1.115 + def _setParameterValues(self, values):
1.116 + params = self.createParameters(1)
1.117 + for value in values:
1.118 + param = params.createParameter(1)
1.119 + self.serialise_value(param, value)
1.120
1.121 def _parameters(self):
1.122 +
1.123 + "Return a list of the individual param elements."
1.124 +
1.125 return self.xpath("./params/param")
1.126
1.127 # Node construction methods.
1.128 @@ -139,16 +224,36 @@
1.129 params = property(_params)
1.130 methodNameElement = property(_methodNameElement)
1.131 methodName = property(_methodName, _setMethodName)
1.132 - parameterValues = property(_parameterValues)
1.133 + parameterValues = property(_parameterValues, _setParameterValues)
1.134 parameters = property(_parameters)
1.135
1.136 -class XMLRPCParametersElement(XMLRPCNode):
1.137 +class XMLRPCParametersElement(SequenceEquality, XMLRPCNode):
1.138
1.139 - "An XML-RPC parameters/params element."
1.140 + """
1.141 + An XML-RPC parameters/params element.
1.142 +
1.143 + This element behaves like a list in that values can be appended to it and
1.144 + these will be added as new parameters.
1.145 + """
1.146
1.147 def _parameters(self):
1.148 +
1.149 + "Return a list of the individual param elements."
1.150 +
1.151 return self.xpath("./param")
1.152
1.153 + # Sequence emulation.
1.154 +
1.155 + def append(self, value):
1.156 + param = self.createParameter(1)
1.157 + self.serialise_value(param, value)
1.158 +
1.159 + def values(self):
1.160 + return [value.contents for value in self.xpath("./param/value")]
1.161 +
1.162 + def __repr__(self):
1.163 + return "<XMLRPCParametersElement: %r>" % self.parameters
1.164 +
1.165 # Node construction methods.
1.166
1.167 def createParameter(self, insert=0):
1.168 @@ -159,13 +264,50 @@
1.169
1.170 parameters = property(_parameters)
1.171
1.172 -class XMLRPCParameterElement(XMLRPCNode):
1.173 +class XMLRPCParameterElement(ContentEquality, XMLRPCNode):
1.174 +
1.175 + """
1.176 + An XML-RPC parameter/param element.
1.177
1.178 - "An XML-RPC parameter/param element."
1.179 + This element behaves like a list in that values can be appended to it and
1.180 + these will be added either as new elements in an existing array or struct,
1.181 + or as new elements in a new array.
1.182 + """
1.183
1.184 def _valueElement(self):
1.185 return (self.xpath("./value") or [None])[0]
1.186
1.187 + def _contents(self):
1.188 + if self.valueElement is not None:
1.189 + return self.valueElement.contents
1.190 + else:
1.191 + return self
1.192 +
1.193 + # Sequence emulation.
1.194 +
1.195 + def __len__(self):
1.196 + return len(self.values())
1.197 +
1.198 + def __getitem__(self, i):
1.199 + return self.values()[i]
1.200 +
1.201 + def append(self, value):
1.202 + if self.valueElement is None:
1.203 + self.createValue("array", 1)
1.204 + self.contents.append(value)
1.205 +
1.206 + def values(self):
1.207 + if self.contents is not self:
1.208 + return self.contents.values()
1.209 + else:
1.210 + return []
1.211 +
1.212 + def __repr__(self):
1.213 + if self.contents is self:
1.214 + return "<XMLRPCParameterElement>"
1.215 + else:
1.216 + return "<XMLRPCParameterElement: %r>" % self.contents
1.217 +
1.218 # Node construction methods.
1.219
1.220 def createValue(self, typename=None, insert=0):
1.221 @@ -178,10 +320,16 @@
1.222 return value
1.223
1.224 valueElement = property(_valueElement)
1.225 + contents = property(_contents)
1.226
1.227 -class XMLRPCArrayElement(XMLRPCNode):
1.228 +class XMLRPCArrayElement(SequenceEquality, XMLRPCNode):
1.229
1.230 - "An XML-RPC array element."
1.231 + """
1.232 + An XML-RPC array element.
1.233 +
1.234 + This element behaves like a list in that values can be appended to it and
1.235 + these will be added as new elements in the array.
1.236 + """
1.237
1.238 def _dataElement(self):
1.239 return (self.xpath("./data") or [None])[0]
1.240 @@ -191,23 +339,16 @@
1.241
1.242 # Sequence emulation.
1.243
1.244 - def __len__(self):
1.245 - if self.data:
1.246 - return len(self.data)
1.247 - else:
1.248 - return 0
1.249 + def append(self, value):
1.250 + if self.dataElement is None:
1.251 + self.createData(1)
1.252 + self.serialise_value(self.dataElement, value)
1.253
1.254 - def __getitem__(self, i):
1.255 - if self.data:
1.256 - return self.data[i]
1.257 - else:
1.258 - raise IndexError, i
1.259 + def values(self):
1.260 + return [v.contents for v in self.xpath("./data/value")]
1.261
1.262 - def __eq__(self, other):
1.263 - for i, j in map(None, self, other):
1.264 - if i != j:
1.265 - return False
1.266 - return True
1.267 + def __repr__(self):
1.268 + return "<XMLRPCArrayElement: %r>" % self.values()
1.269
1.270 # Node construction methods.
1.271
1.272 @@ -220,9 +361,14 @@
1.273 dataElement = property(_dataElement)
1.274 contents = property(_contents)
1.275
1.276 -class XMLRPCStructElement(XMLRPCNode):
1.277 +class XMLRPCStructElement(SequenceEquality, XMLRPCNode):
1.278
1.279 - "An XML-RPC structure element."
1.280 + """
1.281 + An XML-RPC structure element.
1.282 +
1.283 + This element behaves like a list in that values can be appended to it and
1.284 + these will be added as new (name, value) members in the structure.
1.285 + """
1.286
1.287 def _members(self):
1.288 return self.xpath("./member")
1.289 @@ -238,51 +384,77 @@
1.290 def __getitem__(self, i):
1.291 return self.members[i]
1.292
1.293 - def __eq__(self, other):
1.294 - for i, j in map(None, self, other):
1.295 - if i != j:
1.296 - return False
1.297 - return True
1.298 + def append(self, item):
1.299 + name, value = item
1.300 + member = self.createMember(1)
1.301 + member.memberName = name
1.302 + self.serialise_value(member, value)
1.303 +
1.304 + def __repr__(self):
1.305 + return "<XMLRPCStructElement: %r>" % self.items()
1.306 +
1.307 + # Dictionary emulation.
1.308 +
1.309 + def keys(self):
1.310 + return [member.memberName for member in self.members]
1.311 +
1.312 + def values(self):
1.313 + return [member.memberValue for member in self.members]
1.314 +
1.315 + def items(self):
1.316 + return [(member.memberName, member.memberValue) for member in self.members]
1.317
1.318 # Node construction methods.
1.319
1.320 def createMember(self, insert=0):
1.321 e = self.ownerDocument.createElement("member")
1.322 if insert:
1.323 - self.add_or_replace_element(e)
1.324 + self.appendChild(e)
1.325 return e
1.326
1.327 members = property(_members)
1.328 contents = property(_contents)
1.329
1.330 -class XMLRPCDataElement(XMLRPCNode):
1.331 +class XMLRPCDataElement(SequenceEquality, XMLRPCNode):
1.332 +
1.333 + """
1.334 + An XML-RPC array data element.
1.335
1.336 - "An XML-RPC array data element."
1.337 + This element behaves like a list in that values can be appended to it and
1.338 + these will be added as new elements in the array.
1.339 + """
1.340
1.341 - def _values(self):
1.342 - return self.xpath("./value")
1.343 + def _contents(self):
1.344 + return self
1.345
1.346 # Sequence emulation.
1.347
1.348 - def __len__(self):
1.349 - return len(self.values)
1.350 + def append(self, value):
1.351 + self.serialise_value(self, value)
1.352
1.353 - def __getitem__(self, i):
1.354 - return self.values[i].container.contents
1.355 + def values(self):
1.356 + return [v.contents for v in self.xpath("./value")]
1.357
1.358 # Node construction methods.
1.359
1.360 - def createValue(self, insert=0):
1.361 - e = self.ownerDocument.createElement("value")
1.362 + def createValue(self, typename=None, insert=0):
1.363 + value = self.ownerDocument.createElement("value")
1.364 + if typename is not None:
1.365 + contents = self.ownerDocument.createElement(typename)
1.366 + value.appendChild(contents)
1.367 if insert:
1.368 - self.add_or_replace_element(e)
1.369 - return e
1.370 + self.appendChild(value)
1.371 + return value
1.372
1.373 - values = property(_values)
1.374 + contents = property(_contents)
1.375
1.376 class XMLRPCMemberElement(XMLRPCNode):
1.377
1.378 - "An XML-RPC structure member element."
1.379 + """
1.380 + An XML-RPC structure member element.
1.381 +
1.382 + This element behaves like a tuple of the form (name, value).
1.383 + """
1.384
1.385 def _valueElement(self):
1.386 return (self.xpath("./value") or [None])[0]
1.387 @@ -302,6 +474,12 @@
1.388 self.appendChild(nameElement)
1.389 self.nameElement.value = name
1.390
1.391 + def _memberValue(self):
1.392 + if self.valueElement is None:
1.393 + return None
1.394 + else:
1.395 + return self.valueElement.contents
1.396 +
1.397 def _contents(self):
1.398 return self
1.399
1.400 @@ -311,11 +489,16 @@
1.401 return 2
1.402
1.403 def __getitem__(self, i):
1.404 - return (self.memberName, self.value.container.contents)[i]
1.405 + return (self.memberName, self.valueElement.contents)[i]
1.406 +
1.407 + # Equality testing.
1.408
1.409 def __eq__(self, other):
1.410 return self[0] == other[0] and self[1] == other[1]
1.411
1.412 + def __ne__(self, other):
1.413 + return not self.__eq__(other)
1.414 +
1.415 # Node construction methods.
1.416
1.417 def createName(self, insert=0):
1.418 @@ -324,18 +507,22 @@
1.419 self.add_or_replace_element(e)
1.420 return e
1.421
1.422 - def createValue(self, insert=0):
1.423 - e = self.ownerDocument.createElement("value")
1.424 + def createValue(self, typename=None, insert=0):
1.425 + value = self.ownerDocument.createElement("value")
1.426 + if typename is not None:
1.427 + contents = self.ownerDocument.createElement(typename)
1.428 + value.appendChild(contents)
1.429 if insert:
1.430 - self.add_or_replace_element(e)
1.431 - return e
1.432 + self.add_or_replace_element(value)
1.433 + return value
1.434
1.435 nameElement = property(_nameElement)
1.436 memberName = property(_memberName, _setMemberName)
1.437 + memberValue = property(_memberValue)
1.438 valueElement = property(_valueElement)
1.439 contents = property(_contents)
1.440
1.441 -class XMLRPCStringElement(XMLRPCNode):
1.442 +class XMLRPCStringElement(ContentEquality, XMLRPCNode):
1.443
1.444 "An XML-RPC string element."
1.445
1.446 @@ -353,11 +540,8 @@
1.447 def _contents(self):
1.448 return convert(self.typename, self.value)
1.449
1.450 - def __eq__(self, other):
1.451 - if hasattr(other, "contents"):
1.452 - return self.contents == other.contents
1.453 - else:
1.454 - return self.contents == other
1.455 + def __repr__(self):
1.456 + return "<%s: %r>" % (self.__class__.__name__, self.contents)
1.457
1.458 value = property(_value, _setValue)
1.459 contents = property(_contents)
1.460 @@ -368,16 +552,39 @@
1.461
1.462 pass
1.463
1.464 +class XMLRPCMethodNameElement(XMLRPCNameElement):
1.465 +
1.466 + "An XML-RPC method element."
1.467 +
1.468 + pass
1.469 +
1.470 class XMLRPCValueElement(XMLRPCStringElement):
1.471
1.472 "An XML-RPC value element."
1.473
1.474 + def _value(self):
1.475 + if self.container is self:
1.476 + return XMLRPCStringElement._value(self)
1.477 + else:
1.478 + return None
1.479 +
1.480 + def _setValue(self, value):
1.481 + if self.container is self:
1.482 + return XMLRPCStringElement._setValue(self, value)
1.483 + else:
1.484 + raise xml.dom.DOMException(xml.dom.NOT_SUPPORTED_ERR)
1.485 +
1.486 + def _contents(self):
1.487 + if self.container is self:
1.488 + return XMLRPCStringElement._contents(self)
1.489 + else:
1.490 + return self.container.contents
1.491 +
1.492 def _type(self):
1.493 - elements = self.xpath("*")
1.494 - if elements:
1.495 - return elements[0].localName
1.496 + if self.container is self:
1.497 + return "string"
1.498 else:
1.499 - return "string"
1.500 + return self.container.localName
1.501
1.502 def _setType(self, typename):
1.503 new_contents = self.ownerDocument.createElement(typename)
1.504 @@ -386,14 +593,10 @@
1.505 def _container(self):
1.506 return (self.xpath("*") or [self])[0]
1.507
1.508 + value = property(_value, _setValue)
1.509 type = property(_type, _setType)
1.510 container = property(_container)
1.511 -
1.512 -class XMLRPCMethodNameElement(XMLRPCStringElement):
1.513 -
1.514 - "An XML-RPC method element."
1.515 -
1.516 - pass
1.517 + contents = property(_contents)
1.518
1.519 class XMLRPCIntegerElement(XMLRPCStringElement):
1.520
1.521 @@ -457,7 +660,7 @@
1.522 elif s.lower() == "false":
1.523 return False
1.524 else:
1.525 - raise ValueError, "String value %s not convertable to boolean." % repr(s)
1.526 + raise ValueError, "String value %r not convertable to boolean." % s
1.527
1.528 def iso8601(s):
1.529 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]))
1.530 @@ -473,6 +676,13 @@
1.531 "base64" : str
1.532 }
1.533
1.534 +typenames = {
1.535 + "str" : "string",
1.536 + "int" : "int",
1.537 + "bool" : "boolean",
1.538 + "float" : "double"
1.539 + }
1.540 +
1.541 # Implementation-related functionality.
1.542
1.543 class XMLRPCImplementation(libxml2dom.Implementation):