[Zope3-checkins] SVN: Zope3/branches/jim-adapter/src/zope/app/component/ Added compatibility for old pickles.

Jim Fulton jim at zope.com
Tue Apr 25 09:22:38 EDT 2006


Log message for revision 67595:
  Added compatibility for old pickles.
  

Changed:
  U   Zope3/branches/jim-adapter/src/zope/app/component/back35.py
  U   Zope3/branches/jim-adapter/src/zope/app/component/site.py
  A   Zope3/branches/jim-adapter/src/zope/app/component/tests/gen3.fs
  U   Zope3/branches/jim-adapter/src/zope/app/component/tests/test_registration.py

-=-
Modified: Zope3/branches/jim-adapter/src/zope/app/component/back35.py
===================================================================
--- Zope3/branches/jim-adapter/src/zope/app/component/back35.py	2006-04-25 13:22:32 UTC (rev 67594)
+++ Zope3/branches/jim-adapter/src/zope/app/component/back35.py	2006-04-25 13:22:36 UTC (rev 67595)
@@ -15,8 +15,17 @@
 
 $Id$
 """
+
+import UserDict
+import warnings
+
+import persistent
+import persistent.list
+import persistent.mapping
+
 from persistent import Persistent
 
+import zope.cachedescriptors.property
 import zope.event
 import zope.schema
 import zope.interface.adapter
@@ -373,7 +382,7 @@
             raise ValueError(value)
 
 
-class SimpleRegistration(Persistent, Contained):
+class SimpleRegistration(persistent.Persistent, Contained):
     """Registration objects that just contain registration data"""
     implements(IRegistration, IRegistrationManagerContained)
 
@@ -770,3 +779,132 @@
 
         self.validate(value)
         return value
+
+
+class LocalSiteGeneration3SupportMixin:
+
+    @zope.cachedescriptors.property.readproperty
+    def _utility_registrations(self):
+        return _OldUtilityRegistrations(
+            self, 'utilities', '_utility_registrations')
+
+    @zope.cachedescriptors.property.readproperty
+    def _adapter_registrations(self):
+        return _OldAdapterRegistrations(
+            self, 'adapters', '_adapter_registrations')
+
+    @zope.cachedescriptors.property.readproperty
+    def _subscription_registrations(self):
+        return _OldSubscriberRegistrations(self, '_subscription_registrations')
+
+    @zope.cachedescriptors.property.readproperty
+    def _handler_registrations(self):
+        return _OldSubscriberRegistrations(self, '_handler_registrations')
+
+    def _evolve_to_generation_4(self):
+        self._utility_registrations.update(())
+        self._adapter_registrations.update(())
+        self._subscription_registrations.extend(())
+        self._handler_registrations.extend(())
+        for sub in self.subs:
+            sub._evolve_to_generation_4()
+        
+
+class _OldUtilityRegistrations(UserDict.DictMixin):
+
+    def __init__(self, site, rname, name):
+        self.site = site
+        self.rname = rname
+        self.__name__ = name
+
+    def _getOldRegistrations(self):
+        return getattr(self.site, self.rname)._registrations
+
+    def __getitem__(self, key):
+        (provided, name) = key
+        for r in self._getOldRegistrations():
+            if r.name == name and r.provided == provided:
+                return r.component, u''
+        raise KeyError, key
+
+    def keys(self):
+        return [
+            (r.provided, r.name)
+            for r in self._getOldRegistrations()
+            ]
+
+    def update(self, other):
+        newregistrations = persistent.mapping.PersistentMapping()
+        for r in self._getOldRegistrations():
+            newregistrations[(r.provided, r.name)] = r.component, u''
+
+        # finish the conversion of the utilities:
+        del getattr(self.site, self.rname)._registrations
+
+        for key, value in dict(other).iteritems():
+            newregistrations[key] = value
+
+        setattr(self.site, self.__name__, newregistrations)
+
+    def __setitem__(self, *args):
+        self.update(args)
+
+class _OldAdapterRegistrations(_OldUtilityRegistrations):
+
+    def _getOldRegistrations(self):
+        if self.site.adapters._registrations:
+            warnings.warn(
+                "Old non-utility registrations are not supported and will not "
+                "be converted",
+                DeprecationWarning)
+        return ()
+
+
+class _OldSubscriberRegistrations(object):
+
+    def __init__(self, site, name):
+        self.site = site
+        self.__name__ = name
+
+    def __iter__(self):
+        return iter(())
+
+    def __setslice__(self, i, j, other):
+        assert i == 0
+        self.extend(other)
+
+    def extend(self, other):
+        assert not other
+        setattr(self.site, self.__name__, persistent.list.PersistentList())
+
+    def append(self, value):
+        setattr(self.site, self.__name__,
+                persistent.list.PersistentList([value]),
+                )
+
+class _LocalAdapterRegistryGeneration3SupportMixin(object):
+
+    def __setstate__(self, state):
+        if '_registrations' in state:
+            # convert data to generation 3 data structure:
+            next = state['next']
+            if next is None:
+                next = state['base']
+            bases = (next, )
+            self.__init__(bases)
+            registrations = []
+            for r in state['_registrations']:
+                if isinstance(r, UtilityRegistration):
+                    self.register((), r.provided, r.name, r.component)
+                    registrations.append(r)
+                else:
+                    warnings.warn(
+                        "Old %s registrations are not supported and will not "
+                        "be converted" % r.__class__.__name__,
+                        DeprecationWarning)
+            
+            self._registrations = tuple(registrations)
+        else:
+            super(_LocalAdapterRegistryGeneration3SupportMixin, self
+                  ).__setstate__(state)
+                    

Modified: Zope3/branches/jim-adapter/src/zope/app/component/site.py
===================================================================
--- Zope3/branches/jim-adapter/src/zope/app/component/site.py	2006-04-25 13:22:32 UTC (rev 67594)
+++ Zope3/branches/jim-adapter/src/zope/app/component/site.py	2006-04-25 13:22:36 UTC (rev 67595)
@@ -138,13 +138,17 @@
 
 
 class _LocalAdapterRegistry(
+    zope.app.component.back35._LocalAdapterRegistryGeneration3SupportMixin,
     zope.component.persistentregistry.PersistentAdapterRegistry,
     zope.location.Location,
     ):
     pass
 
-class LocalSiteManager(BTreeContainer,
-                       zope.component.persistentregistry.PersistentComponents):
+class LocalSiteManager(
+    BTreeContainer,
+    zope.app.component.back35.LocalSiteGeneration3SupportMixin,
+    zope.component.persistentregistry.PersistentComponents,
+    ):
     """Local Site Manager implementation"""
     zope.interface.implements(interfaces.ILocalSiteManager)
 
@@ -359,6 +363,8 @@
     "defined in zope.component, especially IComponentRegistry.",
     LocalAdapterRegistry = 'zope.app.component.site:_LocalAdapterRegistry',
     LocalUtilityRegistry = 'zope.app.component.site:_LocalAdapterRegistry',
+    UtilityRegistration = 'zope.app.component.back35:UtilityRegistration',
+    AdaptersRegistration = 'zope.app.component.back35:AdaptersRegistration',
     )
 
 def threadSiteSubscriber(ob, event):

Added: Zope3/branches/jim-adapter/src/zope/app/component/tests/gen3.fs
===================================================================
(Binary files differ)


Property changes on: Zope3/branches/jim-adapter/src/zope/app/component/tests/gen3.fs
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Modified: Zope3/branches/jim-adapter/src/zope/app/component/tests/test_registration.py
===================================================================
--- Zope3/branches/jim-adapter/src/zope/app/component/tests/test_registration.py	2006-04-25 13:22:32 UTC (rev 67594)
+++ Zope3/branches/jim-adapter/src/zope/app/component/tests/test_registration.py	2006-04-25 13:22:36 UTC (rev 67595)
@@ -17,14 +17,32 @@
 """
 __docformat__ = "reStructuredText"
 
+import os
 import unittest
 import warnings
 
+from ZODB.DB import DB
+import ZODB.FileStorage
+from ZODB.DemoStorage import DemoStorage
+import transaction
+import persistent
+
 import zope.component.testing as placelesssetup
 from zope.testing import doctest
 from zope.app.testing import setup
+import zope.app.container.contained
+from zope import interface
 
-def setUp(test):
+# test class for testing data conversion
+class IFoo(interface.Interface):
+    pass
+class Foo(persistent.Persistent, zope.app.container.contained.Contained):
+    interface.implements(IFoo)
+    name = ''
+    def __init__(self, name=''):
+        self.name = name
+
+def setUpOld(test):
     placelesssetup.setUp(test)
     setup.setUpAnnotations()
     setup.setUpDependable()
@@ -36,14 +54,287 @@
     warnings.showwarning = test.globs['showwarning']
     placelesssetup.tearDown(test)
 
+def setUp(test):
+    placelesssetup.setUp(test)
+    test.globs['showwarning'] = warnings.showwarning
+    warnings.showwarning = lambda *a, **k: None
+
+def oldfs():
+    return FileStorage(
+        os.path.join(os.path.dirname(__file__), 'gen3.fs'),
+        read_only=True,
+        )
+
+# Work around a bug in ZODB
+# XXX fix ZODB
+class FileStorage(ZODB.FileStorage.FileStorage):
+    
+    def new_oid(self):
+        self._lock_acquire()
+        try:
+            last = self._oid
+            d = ord(last[-1])
+            if d < 255:  # fast path for the usual case
+                last = last[:-1] + chr(d+1)
+            else:        # there's a carry out of the last byte
+                last_as_long, = _structunpack(">Q", last)
+                last = _structpack(">Q", last_as_long + 1)
+            self._oid = last
+            return last
+        finally:
+             self._lock_release()
+ 
+
+def test_old_databases_backward_compat():
+    """
+
+Let's open an old database and get the 3 site managers in it:
+
+    >>> fs = oldfs()
+    >>> demo = DemoStorage(base=fs)
+    >>> db = DB(demo)
+    >>> tm = transaction.TransactionManager()
+    >>> root = db.open(transaction_manager=tm).root()
+    >>> _ = tm.begin()
+    
+    >>> sm1 = root['Application'].getSiteManager()
+    >>> [sm2] = sm1.subs
+    >>> [sm3] = sm2.subs
+
+We can look up utilities as we expect:
+
+    >>> sm1.getUtility(IFoo, '1') is sm1['default']['1']
+    True
+
+    >>> sm3.getUtility(IFoo, '3') is sm3['default']['3']
+    True
+
+    >>> sm2.getUtility(IFoo, '2') is sm2['default']['2']
+    True
+
+    >>> sm1.getUtility(IFoo, '2') is sm1['default']['2']
+    True
+
+    >>> sm1.getUtility(IFoo, '3') is sm1['default']['3']
+    True
+
+    >>> sm2.getUtility(IFoo, '3') is sm2['default']['3']
+    True
+
+    >>> sm2.getUtility(IFoo, '4') is sm2['default']['4']
+    True
+
+    >>> sm3.getUtility(IFoo, '4') is sm3['default']['4']
+    True
+
+    >>> sm3.getUtility(IFoo, '5') is sm3['default']['5']
+    True
+
+and we get registration info:
+
+    >>> sorted([r.name for r in sm2.registeredUtilities()])
+    [u'2', u'3', u'4']
+
+We don't have any adapter or subscriber information, because it wasn't
+previously supported to register those:
+
+    >>> len(list(sm2.registeredAdapters()))
+    0
+    >>> len(list(sm2.registeredSubscriptionAdapters()))
+    0
+    >>> len(list(sm2.registeredHandlers()))
+    0
+
+We haven't modified anything yet.  We can see this in a number of
+ways.  If we look at the internal data structured used, we can see
+that they are weird:
+
+    >>> sm2._utility_registrations.__class__.__name__
+    '_OldUtilityRegistrations'
+
+    >>> sm2._adapter_registrations.__class__.__name__
+    '_OldAdapterRegistrations'
+
+    >>> sm2._subscription_registrations.__class__.__name__
+    '_OldSubscriberRegistrations'
+
+    >>> sm2._handler_registrations.__class__.__name__
+    '_OldSubscriberRegistrations'
+
+and the registries have a _registrations attribute, which is a sign
+that they haven't been converted yet:
+
+    >>> hasattr(sm2.utilities, '_registrations')
+    True
+
+    >>> hasattr(sm2.adapters, '_registrations')
+    True
+
+We'll commit the transaction and make sure the database hasn't
+grown. (This relies on a buglet in DemoStorage length computation.)
+
+    >>> tm.commit()
+    >>> len(demo) == len(fs)
+    True
+
+Of course, we can register new utilities:
+
+    >>> _ = tm.begin()
+    >>> sm1.registerUtility(Foo('one'), IFoo, '1')
+    >>> sm2.registerUtility(Foo('two'), IFoo, '2')
+    >>> tm.commit()
+
+We should then be able to look up the newly registered utilities.
+Let's try to do so in a separate connection:
+
+    >>> tm2 = transaction.TransactionManager()
+    >>> root2 = db.open(transaction_manager=tm2).root()
+    >>> _ = tm2.begin()
+    
+    >>> sm1 = root2['Application'].getSiteManager()
+    >>> [sm2] = sm1.subs
+    >>> [sm3] = sm2.subs
+
+    >>> sm1.getUtility(IFoo, '1').name
+    'one'
+
+    >>> sm2.getUtility(IFoo, '2').name
+    'two'
+
+    >>> sm1.getUtility(IFoo, '2') is sm1['default']['2']
+    True
+
+    >>> sm1.getUtility(IFoo, '3') is sm1['default']['3']
+    True
+
+    >>> sm2.getUtility(IFoo, '3') is sm2['default']['3']
+    True
+
+    >>> sm2.getUtility(IFoo, '4') is sm2['default']['4']
+    True
+
+    >>> sm3.getUtility(IFoo, '4') is sm3['default']['4']
+    True
+
+    >>> sm3.getUtility(IFoo, '5') is sm3['default']['5']
+    True
+
+Because we registered utilities, the corresponding data structures
+have been updated:
+
+    >>> sm2._utility_registrations.__class__.__name__
+    'PersistentMapping'
+
+    >>> hasattr(sm2.utilities, '_registrations')
+    False
+
+But other data structures haven't been effected:
+
+    >>> sm2._adapter_registrations.__class__.__name__
+    '_OldAdapterRegistrations'
+
+    >>> hasattr(sm2.adapters, '_registrations')
+    True
+
+Nor, of course, have the data structures for sites that we haven't
+changed:
+
+    >>> sm3._utility_registrations.__class__.__name__
+    '_OldUtilityRegistrations'
+
+    >>> hasattr(sm3.utilities, '_registrations')
+    True
+
+The _evolve_to_generation_4 method actually converts the remaining
+data structures. It also evolves all of it's subinterfaces:
+
+    >>> sm1._evolve_to_generation_4()
+    >>> tm2.commit()
+
+and we see that all of the data structures have been converted:
+
+    >>> sm1._utility_registrations.__class__.__name__
+    'PersistentMapping'
+    >>> sm1._adapter_registrations.__class__.__name__
+    'PersistentMapping'
+    >>> sm1._subscription_registrations.__class__.__name__
+    'PersistentList'
+    >>> sm1._handler_registrations.__class__.__name__
+    'PersistentList'
+    >>> hasattr(sm1.utilities, '_registrations')
+    False
+    >>> hasattr(sm1.adapters, '_registrations')
+    False
+
+    >>> sm2._utility_registrations.__class__.__name__
+    'PersistentMapping'
+    >>> sm2._adapter_registrations.__class__.__name__
+    'PersistentMapping'
+    >>> sm2._subscription_registrations.__class__.__name__
+    'PersistentList'
+    >>> sm2._handler_registrations.__class__.__name__
+    'PersistentList'
+    >>> hasattr(sm2.utilities, '_registrations')
+    False
+    >>> hasattr(sm2.adapters, '_registrations')
+    False
+
+    >>> sm3._utility_registrations.__class__.__name__
+    'PersistentMapping'
+    >>> sm3._adapter_registrations.__class__.__name__
+    'PersistentMapping'
+    >>> sm3._subscription_registrations.__class__.__name__
+    'PersistentList'
+    >>> sm3._handler_registrations.__class__.__name__
+    'PersistentList'
+    >>> hasattr(sm3.utilities, '_registrations')
+    False
+    >>> hasattr(sm3.adapters, '_registrations')
+    False
+
+and that lookups still work as expected:
+
+
+    >>> sm1.getUtility(IFoo, '1').name
+    'one'
+
+    >>> sm2.getUtility(IFoo, '2').name
+    'two'
+
+    >>> sm1.getUtility(IFoo, '2') is sm1['default']['2']
+    True
+
+    >>> sm1.getUtility(IFoo, '3') is sm1['default']['3']
+    True
+
+    >>> sm2.getUtility(IFoo, '3') is sm2['default']['3']
+    True
+
+    >>> sm2.getUtility(IFoo, '4') is sm2['default']['4']
+    True
+
+    >>> sm3.getUtility(IFoo, '4') is sm3['default']['4']
+    True
+
+    >>> sm3.getUtility(IFoo, '5') is sm3['default']['5']
+    True
+
+
+Cleanup:
+
+    >>> db.close()
+
+"""
+
 def test_suite():
     suite = unittest.TestSuite((
         doctest.DocFileSuite('deprecated35_statusproperty.txt',
                              'deprecated35_registration.txt',
-                             setUp=setUp, tearDown=tearDown),
+                             setUp=setUpOld, tearDown=tearDown),
+        doctest.DocTestSuite(setUp=setUp, tearDown=tearDown)
         ))
-    suite.level = 2
     return suite
 
+
 if __name__ == "__main__":
     unittest.main(defaultTest='test_suite')



More information about the Zope3-Checkins mailing list