[Zope3-checkins] SVN: Zope3/trunk/src/zope/app/interface/ More elegant fix for various persistent interface issues

Ross Patterson me at rpatterson.net
Wed Feb 28 16:04:55 EST 2007


Log message for revision 72918:
  More elegant fix for various persistent interface issues
  
  By adding recognition for the persistent interface type to the
  zodbcode.patch.NameFinder, we avoid the need to create persistent
  versions of declarations and other objects.
  
  Also added a test for provideInterface declarations inside persistent
  modules.
  

Changed:
  U   Zope3/trunk/src/zope/app/interface/__init__.py
  U   Zope3/trunk/src/zope/app/interface/tests/test_interface.py
  A   Zope3/trunk/src/zope/app/interface/wref.py

-=-
Modified: Zope3/trunk/src/zope/app/interface/__init__.py
===================================================================
--- Zope3/trunk/src/zope/app/interface/__init__.py	2007-02-28 20:12:00 UTC (rev 72917)
+++ Zope3/trunk/src/zope/app/interface/__init__.py	2007-02-28 21:04:54 UTC (rev 72918)
@@ -21,88 +21,21 @@
 
 
 from persistent import Persistent
-from persistent.wref import PersistentWeakKeyDictionary
-from zodbcode.patch import registerWrapper, Wrapper
+from zodbcode.patch import registerWrapper, Wrapper, NameFinder
 
 from zope.interface.interface import InterfaceClass
 from zope.interface import Interface
 from zope.security.proxy import removeSecurityProxy
 
-persistentFactories = {}
-def getPersistentKey(v_key):
-    if not hasattr(v_key, '__reduce__'):
-        return
-    reduce = v_key.__reduce__()
-    lookups = reduce[0], type(v_key), getattr(v_key, '__class__')
-    for lookup in lookups:
-        p_factory = persistentFactories.get(lookup, None)
-        if p_factory is not None:
-            return p_factory(v_key)
+from wref import FlexibleWeakKeyDictionary
 
-class DependentsDict(PersistentWeakKeyDictionary):
-    """Intercept non-persistent keys and swap in persistent
-    equivalents."""
-
-    def __setstate__(self, state):
-        data = state['data']
-        for v_key, value in data:
-            p_key = getPersistentKey(v_key)
-            if p_key is not None:
-                data[p_key] = data[v_key]
-        state['data'] = data
-        return super(DependentsDict, self).__setstate__(state)
-                
-    def __setitem__(self, key, value):
-        p_key = getPersistentKey(key)
-        if p_key is not None:
-            key = p_key
-        return super(DependentsDict, self).__setitem__(key, value)
-
-    def __len__(self): return len(self.data)
-
-    def get(self, key, default=None):
-        if not hasattr(key, '_p_oid') or not hasattr(key, '_p_jar'):
-            return default
-        return super(DependentsDict, self).get(key, default)
-
-    def update(self, adict):
-        for v_key in adict.keys():
-            p_key = getPersistentKey(v_key)
-            if p_key is not None:
-                adict[p_key] = adict[v_key]
-        return super(DependentsDict, self).update(adict)
-
-    def keys(self): return [k() for k in self.data.keys()]
-
-from zope.interface.declarations import ProvidesClass, Provides
-class PersistentProvidesClass(Persistent, ProvidesClass):
-    """A persistent Provides class."""
-    def __init__(self, *args, **kw):
-        Persistent.__init__(self)
-        ProvidesClass.__init__(self, *args, **kw)
-        self.dependents = DependentsDict()
-def persistentProvides(obj):
-    return PersistentProvidesClass(*obj.__reduce__()[1])
-persistentFactories[Provides] = persistentProvides
-
-from zope.interface.declarations import Implements
-class PersistentImplements(Persistent, Implements):
-    """A persistent Implements class."""
-    def __init__(self, *args, **kw):
-        Persistent.__init__(self)
-        Implements.__init__(self, *args, **kw)
-        self.dependents = DependentsDict()
-def persistentImplements(obj):
-    return PersistentImplements(*obj.__bases__)
-persistentFactories[Implements] = persistentImplements
-
 class PersistentInterfaceClass(Persistent, InterfaceClass):
 
     def __init__(self, *args, **kw):
         Persistent.__init__(self)
         InterfaceClass.__init__(self, *args, **kw)
         
-        self.dependents = DependentsDict()
+        self.dependents = FlexibleWeakKeyDictionary()
 
 # PersistentInterface is equivalent to the zope.interface.Interface object
 # except that it is also persistent.  It is used in conjunction with
@@ -111,10 +44,6 @@
                                                (Interface, ))
 
 
-def persistentInterface(iface):
-    return PersistentInterfaceClass(iface.__name__)
-persistentFactories[InterfaceClass] = persistentInterface
-
 class PersistentInterfaceWrapper(Wrapper):
 
     def unwrap(self):
@@ -124,7 +53,7 @@
 def getInterfaceStateForPersistentInterfaceCreation(iface):
     # Need to convert the dependents weakref dict to a persistent dict
     dict = iface.__dict__.copy()
-    dependents = DependentsDict()
+    dependents = FlexibleWeakKeyDictionary()
     for k, v in dict['dependents'].iteritems():
         dependents[k] = v
     dict['dependents'] = dependents
@@ -135,6 +64,11 @@
                 getInterfaceStateForPersistentInterfaceCreation,
                 )
 
+NameFinder.classTypes[InterfaceClass] = True
+NameFinder.types[InterfaceClass] = True
+NameFinder.classTypes[PersistentInterfaceClass] = True
+NameFinder.types[PersistentInterfaceClass] = True
+
 from zope.interface.declarations import providedBy
 
 def queryType(object, interface):

Modified: Zope3/trunk/src/zope/app/interface/tests/test_interface.py
===================================================================
--- Zope3/trunk/src/zope/app/interface/tests/test_interface.py	2007-02-28 20:12:00 UTC (rev 72917)
+++ Zope3/trunk/src/zope/app/interface/tests/test_interface.py	2007-02-28 21:04:54 UTC (rev 72918)
@@ -29,7 +29,8 @@
 from zodbcode.module import ManagedRegistry
 
 from zope.interface import Interface, implements, directlyProvides
-from zope.app.interface import PersistentInterface, PersistentProvidesClass
+from zope.interface.interfaces import IInterface
+from zope.app.interface import PersistentInterface
 
 # TODO: for some reason changing this code to use implements() does not
 # work. This is due to a bug that is supposed to be fixed after X3.0.
@@ -56,8 +57,21 @@
 class IBah(IQuux): pass
 class IBaz(Interface): pass
 class IBlah(IBaz): pass
+
 """
 
+provide_iface_code = """\
+from zope.interface import Interface
+from zope.component.interface import provideInterface
+from zope.app.interface.tests.test_interface import IBarInterface
+
+class IBar(Interface): pass
+provideInterface('', IBar, iface_type=IBarInterface)
+
+"""
+
+class IBarInterface(IInterface): pass
+
 class Bar(Persistent): pass
 class Baz(Persistent): pass
 
@@ -120,9 +134,14 @@
         self.root['blah'] = blah
         self.assertTrue(barmodule.IBlah.providedBy(blah))
 
+        # Update the code to make sure everything works on update
+        self.registry.updateModule('barmodule',
+                                   bar_code + '\nfoo = 1')
+
         transaction.commit()
         self.db.close()
         root = self.db.open().root()
+
         barmodule = root['registry'].findModule("barmodule")
 
         bar = root['bar']
@@ -158,38 +177,18 @@
         barmodule = root['registry'].findModule("barmodule")
         self.assertEqual(barmodule.IBar.dependents.keys(), [])
 
-    def test_persistentDeclarations(self):
-        """Verify equivalency of persistent declarations
+    def test_persistentProvides(self):
+        """Verify that provideInterface works."""
 
-        Make sure that the persistent declaration instances are
-        equivalent to the non-persistent instances from which they
-        originate."""
-
-        self.registry.newModule("barmodule", bar_code)
+        self.registry.newModule("barmodule", provide_iface_code)
         barmodule = self.registry.findModule("barmodule")
+        self.assertTrue(IBarInterface.providedBy(barmodule.IBar))
 
-        class Baz(object):
-            implements(barmodule.IBar)
-
-        bar = Bar()
-        directlyProvides(bar, barmodule.IBar)
-
-        dep_keys = barmodule.IBar.dependents.keys()
-         # keys are not in a reliable order, esp. across platforms, so have
-         # fun sniffing...
-        if isinstance(dep_keys[0], PersistentProvidesClass):
-            dep_provides, dep_implements = dep_keys
-        else:
-            dep_implements, dep_provides = dep_keys
-
-        self.assertEqual(
-            bar.__provides__._Provides__args,
-            dep_provides._Provides__args
-            )
-        self.assertEqual(
-            Baz.__implemented__.__bases__,
-            dep_implements.__bases__
-            )
+        self.registry.updateModule('barmodule',
+                                   provide_iface_code + '\nfoo = 1')
+        transaction.commit()
+        barmodule = self.registry.findModule("barmodule")
+        self.assertTrue(IBarInterface.providedBy(barmodule.IBar))
         
 def test_suite():
     return unittest.makeSuite(PersistentInterfaceTest)

Added: Zope3/trunk/src/zope/app/interface/wref.py
===================================================================
--- Zope3/trunk/src/zope/app/interface/wref.py	2007-02-28 20:12:00 UTC (rev 72917)
+++ Zope3/trunk/src/zope/app/interface/wref.py	2007-02-28 21:04:54 UTC (rev 72918)
@@ -0,0 +1,52 @@
+from weakref import ref
+
+from persistent.interfaces import IPersistent
+from persistent.wref import (WeakRef, WeakRefMarker,
+                             PersistentWeakKeyDictionary)
+
+class wref(ref):
+    def __reduce_ex__(self, proto):
+        return _wref_reconstructor, ()
+
+class Dummy(object): pass
+
+def _wref_reconstructor():
+    """Return a dead reference on reconstruction"""
+    return wref(Dummy())
+
+def getWeakRef(ob):
+    """Get either a persistent or non-presistent weakref"""
+    if IPersistent.providedBy(ob):
+        return WeakRef(ob)
+    else:
+        return wref(ob)
+
+class FlexibleWeakKeyDictionary(PersistentWeakKeyDictionary):
+
+    def __setitem__(self, key, value):
+        self.data[getWeakRef(key)] = value
+
+    def __getitem__(self, key):
+        return self.data[getWeakRef(key)]
+
+    def __delitem__(self, key):
+        del self.data[getWeakRef(key)]
+
+    def get(self, key, default=None):
+        return self.data.get(getWeakRef(key), default)
+
+    def __contains__(self, key):
+        return getWeakRef(key) in self.data
+
+    def update(self, adict):
+        if isinstance(adict, PersistentWeakKeyDictionary):
+            self.data.update(adict.update)
+        else:
+            for k, v in adict.items():
+                self.data[getWeakRef(k)] = v
+
+    def keys(self):
+        return [k() for k in self.data.keys()]
+
+    def __len__(self):
+        return len(self.data)



More information about the Zope3-Checkins mailing list