[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