Log message for revision 68691: Made DateTime.DateTime marshallable via XML-RPC. Fixes http://www.zope.org/Collectors/Zope/2109
Changed: U Zope/branches/2.10/lib/python/DateTime/DateTime.py U Zope/branches/2.10/lib/python/ZPublisher/tests/test_xmlrpc.py U Zope/branches/2.10/lib/python/ZPublisher/xmlrpc.py -=- Modified: Zope/branches/2.10/lib/python/DateTime/DateTime.py =================================================================== --- Zope/branches/2.10/lib/python/DateTime/DateTime.py 2006-06-16 15:45:43 UTC (rev 68690) +++ Zope/branches/2.10/lib/python/DateTime/DateTime.py 2006-06-16 15:54:47 UTC (rev 68691) @@ -1797,7 +1797,15 @@ d1 = (( d4 - L) % 365) + L 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: def __init__(self, dt, format): Modified: Zope/branches/2.10/lib/python/ZPublisher/tests/test_xmlrpc.py =================================================================== --- Zope/branches/2.10/lib/python/ZPublisher/tests/test_xmlrpc.py 2006-06-16 15:45:43 UTC (rev 68690) +++ Zope/branches/2.10/lib/python/ZPublisher/tests/test_xmlrpc.py 2006-06-16 15:54:47 UTC (rev 68691) @@ -1,4 +1,5 @@ import unittest +from DateTime import DateTime class FauxResponse: @@ -12,6 +13,9 @@ def setHeader(self, name, value): self._headers[name] = value + def setStatus(self, status): + self._status = status + class FauxInstance: def __init__(self, **kw): self.__dict__.update(kw) @@ -55,7 +59,139 @@ data, method = xmlrpclib.loads(faux._body) 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(): return unittest.TestSuite((unittest.makeSuite(XMLRPCResponseTests),)) Modified: Zope/branches/2.10/lib/python/ZPublisher/xmlrpc.py =================================================================== --- Zope/branches/2.10/lib/python/ZPublisher/xmlrpc.py 2006-06-16 15:45:43 UTC (rev 68690) +++ Zope/branches/2.10/lib/python/ZPublisher/xmlrpc.py 2006-06-16 15:54:47 UTC (rev 68691) @@ -25,7 +25,29 @@ import xmlrpclib 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): """Parse input data and return a method path and argument tuple @@ -100,16 +122,6 @@ # Convert Fault object to XML-RPC response. body=xmlrpclib.dumps(body, methodresponse=1, allow_none=True) 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 # strings, integers as integers, etc. We do *not* convert # everything to a string first. @@ -119,6 +131,8 @@ try: body = xmlrpclib.dumps( (body,), methodresponse=1, allow_none=True) + except ConflictError: + raise except: self.exception() return @@ -165,6 +179,8 @@ f=Fault(-1, 'Unexpected Zope exception: %s' % value) else: f=Fault(-2, 'Unexpected Zope error value: %s' % value) + except ConflictError: + raise except: f=Fault(-3, "Unknown Zope fault type") _______________________________________________ Zope-Checkins maillist - Zope-Checkins@zope.org http://mail.zope.org/mailman/listinfo/zope-checkins