Commit 8510adec authored by Stefan H. Holek's avatar Stefan H. Holek

Made DateTime.DateTime marshallable via XML-RPC.

Fixes http://www.zope.org/Collectors/Zope/2109
parent a0bae348
...@@ -18,6 +18,8 @@ Zope Changes ...@@ -18,6 +18,8 @@ Zope Changes
Bugs fixed Bugs fixed
- Collector #2109: XML-RPC did not handle DateTime.DateTime objects.
- Collector #2016: DemoStorage couldn't wrap base storages without - Collector #2016: DemoStorage couldn't wrap base storages without
an '_oid' attribute. an '_oid' attribute.
......
...@@ -1805,6 +1805,14 @@ class DateTime: ...@@ -1805,6 +1805,14 @@ class DateTime:
d1 = (( d4 - L) % 365) + L d1 = (( d4 - L) % 365) + L
return d1/7 + 1 return d1/7 + 1
def encode(self, out):
"""
Encode value for XML-RPC
"""
out.write('<value><dateTime.iso8601>')
out.write(self.ISO8601())
out.write('</dateTime.iso8601></value>\n')
class strftimeFormatter: class strftimeFormatter:
......
import unittest import unittest
from DateTime import DateTime
class FauxResponse: class FauxResponse:
...@@ -12,6 +13,9 @@ class FauxResponse: ...@@ -12,6 +13,9 @@ class FauxResponse:
def setHeader(self, name, value): def setHeader(self, name, value):
self._headers[name] = value self._headers[name] = value
def setStatus(self, status):
self._status = status
class FauxInstance: class FauxInstance:
def __init__(self, **kw): def __init__(self, **kw):
self.__dict__.update(kw) self.__dict__.update(kw)
...@@ -55,6 +59,138 @@ class XMLRPCResponseTests(unittest.TestCase): ...@@ -55,6 +59,138 @@ class XMLRPCResponseTests(unittest.TestCase):
data, method = xmlrpclib.loads(faux._body) data, method = xmlrpclib.loads(faux._body)
self.assert_(data[0]['public'] is None) self.assert_(data[0]['public'] is None)
def test_instance(self):
# Instances are turned into dicts with their private
# attributes removed.
import xmlrpclib
body = FauxInstance(_secret='abc', public='def')
faux = FauxResponse()
response = self._makeOne(faux)
response.setBody(body)
data, method = xmlrpclib.loads(faux._body)
data = data[0]
self.assertEqual(data, {'public': 'def'})
def test_instanceattribute(self):
# While the removal of private ('_') attributes works fine for the
# top-level instance, how about attributes that are themselves
# instances?
import xmlrpclib
body = FauxInstance(public=FauxInstance(_secret='abc', public='def'))
faux = FauxResponse()
response = self._makeOne(faux)
response.setBody(body)
data, method = xmlrpclib.loads(faux._body)
data = data[0]['public']
self.assertEqual(data, {'public': 'def'})
def test_instanceattribute_recursive(self):
# Instance "flattening" should work recursively, ad infinitum
import xmlrpclib
body = FauxInstance(public=FauxInstance(public=FauxInstance(_secret='abc', public='def')))
faux = FauxResponse()
response = self._makeOne(faux)
response.setBody(body)
data, method = xmlrpclib.loads(faux._body)
data = data[0]['public']['public']
self.assertEqual(data, {'public': 'def'})
def test_instance_in_list(self):
# Instances are turned into dicts with their private
# attributes removed, even when embedded in another
# data structure.
import xmlrpclib
body = [FauxInstance(_secret='abc', public='def')]
faux = FauxResponse()
response = self._makeOne(faux)
response.setBody(body)
data, method = xmlrpclib.loads(faux._body)
data = data[0][0]
self.assertEqual(data, {'public': 'def'})
def test_instance_in_dict(self):
# Instances are turned into dicts with their private
# attributes removed, even when embedded in another
# data structure.
import xmlrpclib
body = {'faux': FauxInstance(_secret='abc', public='def')}
faux = FauxResponse()
response = self._makeOne(faux)
response.setBody(body)
data, method = xmlrpclib.loads(faux._body)
data = data[0]['faux']
self.assertEqual(data, {'public': 'def'})
def test_zopedatetimeinstance(self):
# DateTime instance at top-level
import xmlrpclib
body = DateTime('2006-05-24 07:00:00 GMT+0')
faux = FauxResponse()
response = self._makeOne(faux)
response.setBody(body)
data, method = xmlrpclib.loads(faux._body)
data = data[0]
self.failUnless(isinstance(data, xmlrpclib.DateTime))
self.assertEqual(data.value, u'2006-05-24T07:00:00+00:00')
def test_zopedatetimeattribute(self):
# DateTime instance as attribute
import xmlrpclib
body = FauxInstance(public=DateTime('2006-05-24 07:00:00 GMT+0'))
faux = FauxResponse()
response = self._makeOne(faux)
response.setBody(body)
data, method = xmlrpclib.loads(faux._body)
data = data[0]['public']
self.failUnless(isinstance(data, xmlrpclib.DateTime))
self.assertEqual(data.value, u'2006-05-24T07:00:00+00:00')
def test_zopedatetimeattribute_recursive(self):
# DateTime encoding should work recursively
import xmlrpclib
body = FauxInstance(public=FauxInstance(public=DateTime('2006-05-24 07:00:00 GMT+0')))
faux = FauxResponse()
response = self._makeOne(faux)
response.setBody(body)
data, method = xmlrpclib.loads(faux._body)
data = data[0]['public']['public']
self.failUnless(isinstance(data, xmlrpclib.DateTime))
self.assertEqual(data.value, u'2006-05-24T07:00:00+00:00')
def test_zopedatetimeinstance_in_list(self):
# DateTime instance embedded in a list
import xmlrpclib
body = [DateTime('2006-05-24 07:00:00 GMT+0')]
faux = FauxResponse()
response = self._makeOne(faux)
response.setBody(body)
data, method = xmlrpclib.loads(faux._body)
data = data[0][0]
self.failUnless(isinstance(data, xmlrpclib.DateTime))
self.assertEqual(data.value, u'2006-05-24T07:00:00+00:00')
def test_zopedatetimeinstance_in_dict(self):
# DateTime instance embedded in a dict
import xmlrpclib
body = {'date': DateTime('2006-05-24 07:00:00 GMT+0')}
faux = FauxResponse()
response = self._makeOne(faux)
response.setBody(body)
data, method = xmlrpclib.loads(faux._body)
data = data[0]['date']
self.failUnless(isinstance(data, xmlrpclib.DateTime))
self.assertEqual(data.value, u'2006-05-24T07:00:00+00:00')
def test_functionattribute(self):
# Cannot marshal functions or methods, obviously
import xmlrpclib
def foo(): pass
body = FauxInstance(public=foo)
faux = FauxResponse()
response = self._makeOne(faux)
response.setBody(body)
self.assertRaises(xmlrpclib.Fault, xmlrpclib.loads, faux._body)
def test_suite(): def test_suite():
return unittest.TestSuite((unittest.makeSuite(XMLRPCResponseTests),)) return unittest.TestSuite((unittest.makeSuite(XMLRPCResponseTests),))
......
...@@ -25,6 +25,28 @@ from HTTPResponse import HTTPResponse ...@@ -25,6 +25,28 @@ from HTTPResponse import HTTPResponse
import xmlrpclib import xmlrpclib
from zExceptions import Unauthorized from zExceptions import Unauthorized
from ZODB.POSException import ConflictError
# Make DateTime.DateTime marshallable via XML-RPC
# http://www.zope.org/Collectors/Zope/2109
from DateTime import DateTime
WRAPPERS = xmlrpclib.WRAPPERS + (DateTime,)
def dump_instance(self, value, write):
# Check for special wrappers
if value.__class__ in WRAPPERS:
self.write = write
value.encode(self)
del self.write
else:
# Store instance attributes as a struct (really?).
# We want to avoid disclosing private attributes.
# Private attributes are by convention named with
# a leading underscore character.
value = dict([(k, v) for (k, v) in value.__dict__.items() if k[0] != '_'])
self.dump_struct(value, write)
xmlrpclib.Marshaller.dispatch[types.InstanceType] = dump_instance
def parse_input(data): def parse_input(data):
"""Parse input data and return a method path and argument tuple """Parse input data and return a method path and argument tuple
...@@ -100,16 +122,6 @@ class Response: ...@@ -100,16 +122,6 @@ class Response:
# Convert Fault object to XML-RPC response. # Convert Fault object to XML-RPC response.
body=xmlrpclib.dumps(body, methodresponse=1, allow_none=True) body=xmlrpclib.dumps(body, methodresponse=1, allow_none=True)
else: else:
if type(body) == types.InstanceType:
# Avoid disclosing private members. Private members are
# by convention named with a leading underscore char.
orig = body.__dict__
dict = {}
for key in orig.keys():
if key[:1] != '_':
dict[key] = orig[key]
body = dict
# Marshall our body as an XML-RPC response. Strings will be sent # Marshall our body as an XML-RPC response. Strings will be sent
# strings, integers as integers, etc. We do *not* convert # strings, integers as integers, etc. We do *not* convert
# everything to a string first. # everything to a string first.
...@@ -119,6 +131,8 @@ class Response: ...@@ -119,6 +131,8 @@ class Response:
try: try:
body = xmlrpclib.dumps( body = xmlrpclib.dumps(
(body,), methodresponse=1, allow_none=True) (body,), methodresponse=1, allow_none=True)
except ConflictError:
raise
except: except:
self.exception() self.exception()
return return
...@@ -165,6 +179,8 @@ class Response: ...@@ -165,6 +179,8 @@ class Response:
f=Fault(-1, 'Unexpected Zope exception: %s' % value) f=Fault(-1, 'Unexpected Zope exception: %s' % value)
else: else:
f=Fault(-2, 'Unexpected Zope error value: %s' % value) f=Fault(-2, 'Unexpected Zope error value: %s' % value)
except ConflictError:
raise
except: except:
f=Fault(-3, "Unknown Zope fault type") f=Fault(-3, "Unknown Zope fault type")
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment