[Zope-Checkins] SVN: Zope/branches/2.9/ Made DateTime.DateTime marshallable via XML-RPC.

Stefan H. Holek cvs-admin at zope.org
Fri Jun 16 11:45:47 EDT 2006


Log message for revision 68690:
  Made DateTime.DateTime marshallable via XML-RPC.
  Fixes http://www.zope.org/Collectors/Zope/2109
  

Changed:
  U   Zope/branches/2.9/doc/CHANGES.txt
  U   Zope/branches/2.9/lib/python/DateTime/DateTime.py
  U   Zope/branches/2.9/lib/python/ZPublisher/tests/test_xmlrpc.py
  U   Zope/branches/2.9/lib/python/ZPublisher/xmlrpc.py

-=-
Modified: Zope/branches/2.9/doc/CHANGES.txt
===================================================================
--- Zope/branches/2.9/doc/CHANGES.txt	2006-06-16 12:02:13 UTC (rev 68689)
+++ Zope/branches/2.9/doc/CHANGES.txt	2006-06-16 15:45:43 UTC (rev 68690)
@@ -18,6 +18,8 @@
 
    Bugs fixed
 
+      - Collector #2109: XML-RPC did not handle DateTime.DateTime objects.
+
       - Collector #2016: DemoStorage couldn't wrap base storages without
                          an '_oid' attribute.
 

Modified: Zope/branches/2.9/lib/python/DateTime/DateTime.py
===================================================================
--- Zope/branches/2.9/lib/python/DateTime/DateTime.py	2006-06-16 12:02:13 UTC (rev 68689)
+++ Zope/branches/2.9/lib/python/DateTime/DateTime.py	2006-06-16 15:45:43 UTC (rev 68690)
@@ -1805,7 +1805,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.9/lib/python/ZPublisher/tests/test_xmlrpc.py
===================================================================
--- Zope/branches/2.9/lib/python/ZPublisher/tests/test_xmlrpc.py	2006-06-16 12:02:13 UTC (rev 68689)
+++ Zope/branches/2.9/lib/python/ZPublisher/tests/test_xmlrpc.py	2006-06-16 15:45:43 UTC (rev 68690)
@@ -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.9/lib/python/ZPublisher/xmlrpc.py
===================================================================
--- Zope/branches/2.9/lib/python/ZPublisher/xmlrpc.py	2006-06-16 12:02:13 UTC (rev 68689)
+++ Zope/branches/2.9/lib/python/ZPublisher/xmlrpc.py	2006-06-16 15:45:43 UTC (rev 68690)
@@ -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")
 



More information about the Zope-Checkins mailing list