Log message for revision 97155: OFS.ObjectManager now fully implements the zope.container.IContainer interface and no longer just claims to do so. See the changelog for details and how backwards compatibility is handled.
Changed: U Zope/trunk/doc/CHANGES.rst U Zope/trunk/src/OFS/ObjectManager.py U Zope/trunk/src/OFS/interfaces.py U Zope/trunk/src/OFS/tests/testObjectManager.py -=- Modified: Zope/trunk/doc/CHANGES.rst =================================================================== --- Zope/trunk/doc/CHANGES.rst 2009-02-23 12:24:48 UTC (rev 97154) +++ Zope/trunk/doc/CHANGES.rst 2009-02-23 12:49:08 UTC (rev 97155) @@ -114,7 +114,24 @@ Features added ++++++++++++++ -- Acquisition has been made aware of __parent__ pointers. This allows +- OFS.ObjectManager now fully implements the zope.container.IContainer + interface. For the last Zope2 releases it already claimed to implement the + interface, but didn't actually full-fill the interface contract. This means + you can start using more commonly used Python idioms to access objects inside + object managers. Complete dictionary-like access and container methods + including iteration are now supported. For each class derived from + ObjectManager you can use for any instance om: `om.keys()` instead of + `om.objectIds()`, `om.values()` instead of `om.objectValues()`, but also + `om.items()`, `ob.get('id')`, `ob['id']`, `'id' in om`, `iter(om)`, + `len(om)`, `om['id'] = object()` instead of `om._setObject('id', object())` + and `del ob['id']`. Should contained items of the object manager have ids + equal to any of the new method names, the objects will override the method, + as expected in Acquisition enabled types. Adding new objects into object + managers by those new names will no longer work, though. The added methods + call the already existing methods internally, so if a derived type overwrote + those, the new interface will provide the same functionality. + +- Acquisition has been made aware of `__parent__` pointers. This allows direct access to many Zope 3 classes without the need to mixin Acquisition base classes for the security to work. Modified: Zope/trunk/src/OFS/ObjectManager.py =================================================================== --- Zope/trunk/src/OFS/ObjectManager.py 2009-02-23 12:24:48 UTC (rev 97154) +++ Zope/trunk/src/OFS/ObjectManager.py 2009-02-23 12:49:08 UTC (rev 97155) @@ -54,7 +54,6 @@ from zope.container.contained import ObjectAddedEvent from zope.container.contained import ObjectRemovedEvent from zope.container.contained import notifyContainerModified -from zope.container.interfaces import IContainer from OFS.CopySupport import CopyContainer from OFS.interfaces import IObjectManager @@ -156,7 +155,7 @@ # The claim to implement IContainer has been made during the Zope3 # integration project called Five but hasn't been completed in full. - implements(IObjectManager, IContainer) + implements(IObjectManager) security = ClassSecurityInfo() security.declareObjectProtected(access_contents_information) @@ -765,6 +764,9 @@ break return marshal.dumps((mode,0,0,1,owner,group,0,mtime,mtime,mtime)) + def __delitem__(self, name): + return self.manage_delObjects(ids=[name]) + def __getitem__(self, key): v=self._getOb(key, None) if v is not None: return v @@ -775,6 +777,34 @@ return NullResource(self, key, request).__of__(self) raise KeyError, key + def __setitem__(self, key, value): + return self._setObject(key, value) + + def __contains__(self, name): + return name in self.objectIds() + + def __iter__(self): + return iter(self.objectIds()) + + def __len__(self): + return len(self.objectIds()) + + security.declareProtected(access_contents_information, 'get') + def get(self, key, default=None): + return self._getOb(key, default) + + security.declareProtected(access_contents_information, 'keys') + def keys(self): + return self.objectIds() + + security.declareProtected(access_contents_information, 'get') + def items(self): + return self.objectItems() + + security.declareProtected(access_contents_information, 'values') + def values(self): + return self.objectValues() + # Don't InitializeClass, there is a specific __class_init__ on ObjectManager # InitializeClass(ObjectManager) Modified: Zope/trunk/src/OFS/interfaces.py =================================================================== --- Zope/trunk/src/OFS/interfaces.py 2009-02-23 12:24:48 UTC (rev 97154) +++ Zope/trunk/src/OFS/interfaces.py 2009-02-23 12:49:08 UTC (rev 97155) @@ -14,6 +14,7 @@ $Id$ """ +from zope.container.interfaces import IContainer from zope.interface import Attribute from zope.interface import Interface from zope.schema import Bool, BytesLine, Tuple @@ -474,7 +475,7 @@ # based on OFS.ObjectManager.ObjectManager class IObjectManager(IZopeObject, ICopyContainer, INavigation, IManageable, IAcquirer, IPersistent, IDAVCollection, ITraversable, - IPossibleSite): + IPossibleSite, IContainer): """Generic object manager This interface provides core behavior for collections of heterogeneous @@ -582,11 +583,7 @@ """ """ - def __getitem__(key): - """ - """ - # XXX: might contain non-API methods and outdated comments; # not synced with ZopeBook API Reference; # based on OFS.FindSupport.FindSupport Modified: Zope/trunk/src/OFS/tests/testObjectManager.py =================================================================== --- Zope/trunk/src/OFS/tests/testObjectManager.py 2009-02-23 12:24:48 UTC (rev 97154) +++ Zope/trunk/src/OFS/tests/testObjectManager.py 2009-02-23 12:49:08 UTC (rev 97155) @@ -1,19 +1,22 @@ import unittest +from zope.app.testing.placelesssetup import PlacelessSetup + from AccessControl.Owned import EmergencyUserCannotOwn from AccessControl.SecurityManagement import newSecurityManager from AccessControl.SecurityManagement import noSecurityManager from AccessControl.User import User # before SpecialUsers from AccessControl.SpecialUsers import emergency_user, nobody, system +from Acquisition import aq_base from Acquisition import Implicit from App.config import getConfiguration from logging import getLogger from OFS.ObjectManager import ObjectManager from OFS.SimpleItem import SimpleItem -from zope.app.testing.placelesssetup import PlacelessSetup import Products.Five from Products.Five import zcml from Products.Five.eventconfigure import setDeprecatedManageAddDelete +from zExceptions import BadRequest logger = getLogger('OFS.subscribers') @@ -87,7 +90,6 @@ return ObjectManagerWithIItem def _makeOne( self, *args, **kw ): - return self._getTargetClass()( *args, **kw ).__of__( FauxRoot() ) def test_z3interfaces(self): @@ -98,175 +100,102 @@ verifyClass(IObjectManager, ObjectManager) def test_setObject_set_owner_with_no_user( self ): - om = self._makeOne() - newSecurityManager( None, None ) - si = SimpleItem( 'no_user' ) - om._setObject( 'no_user', si ) - self.assertEqual( si.__ac_local_roles__, None ) def test_setObject_set_owner_with_emergency_user( self ): om = self._makeOne() - newSecurityManager( None, emergency_user ) - si = SimpleItem( 'should_fail' ) - self.assertEqual( si.__ac_local_roles__, None ) - self.assertRaises( EmergencyUserCannotOwn , om._setObject, 'should_fail', si ) def test_setObject_set_owner_with_system_user( self ): - om = self._makeOne() - newSecurityManager( None, system ) - si = SimpleItem( 'system' ) - self.assertEqual( si.__ac_local_roles__, None ) - om._setObject( 'system', si ) - self.assertEqual( si.__ac_local_roles__, None ) def test_setObject_set_owner_with_anonymous_user( self ): - om = self._makeOne() - newSecurityManager( None, nobody ) - si = SimpleItem( 'anon' ) - self.assertEqual( si.__ac_local_roles__, None ) - om._setObject( 'anon', si ) - self.assertEqual( si.__ac_local_roles__, None ) def test_setObject_set_owner_with_user( self ): - om = self._makeOne() - user = User( 'user', '123', (), () ).__of__( FauxRoot() ) - newSecurityManager( None, user ) - si = SimpleItem( 'user_creation' ) - self.assertEqual( si.__ac_local_roles__, None ) - om._setObject( 'user_creation', si ) - self.assertEqual( si.__ac_local_roles__, { 'user': ['Owner'] } ) def test_setObject_set_owner_with_faux_user( self ): - om = self._makeOne() - user = FauxUser( 'user_id', 'user_login' ).__of__( FauxRoot() ) - newSecurityManager( None, user ) - si = SimpleItem( 'faux_creation' ) - self.assertEqual( si.__ac_local_roles__, None ) - om._setObject( 'faux_creation', si ) - self.assertEqual( si.__ac_local_roles__, { 'user_id': ['Owner'] } ) def test_setObject_no_set_owner_with_no_user( self ): - om = self._makeOne() - newSecurityManager( None, None ) - si = SimpleItem( 'should_be_okay' ) - self.assertEqual( si.__ac_local_roles__, None ) - om._setObject( 'should_be_okay', si, set_owner=0 ) - self.assertEqual( si.__ac_local_roles__, None ) def test_setObject_no_set_owner_with_emergency_user( self ): - om = self._makeOne() - newSecurityManager( None, emergency_user ) - si = SimpleItem( 'should_be_okay' ) - self.assertEqual( si.__ac_local_roles__, None ) - om._setObject( 'should_be_okay', si, set_owner=0 ) - self.assertEqual( si.__ac_local_roles__, None ) def test_setObject_no_set_owner_with_system_user( self ): - om = self._makeOne() - newSecurityManager( None, system ) - si = SimpleItem( 'should_be_okay' ) - self.assertEqual( si.__ac_local_roles__, None ) - om._setObject( 'should_be_okay', si, set_owner=0 ) - self.assertEqual( si.__ac_local_roles__, None ) def test_setObject_no_set_owner_with_anonymous_user( self ): - om = self._makeOne() - newSecurityManager( None, nobody ) - si = SimpleItem( 'should_be_okay' ) - self.assertEqual( si.__ac_local_roles__, None ) - om._setObject( 'should_be_okay', si, set_owner=0 ) - self.assertEqual( si.__ac_local_roles__, None ) def test_setObject_no_set_owner_with_user( self ): - om = self._makeOne() - user = User( 'user', '123', (), () ).__of__( FauxRoot() ) - newSecurityManager( None, user ) - si = SimpleItem( 'should_be_okay' ) - self.assertEqual( si.__ac_local_roles__, None ) - om._setObject( 'should_be_okay', si, set_owner=0 ) - self.assertEqual( si.__ac_local_roles__, None ) def test_setObject_no_set_owner_with_faux_user( self ): - om = self._makeOne() - user = FauxUser( 'user_id', 'user_login' ).__of__( FauxRoot() ) - newSecurityManager( None, user ) - si = SimpleItem( 'should_be_okay' ) - self.assertEqual( si.__ac_local_roles__, None ) - om._setObject( 'should_be_okay', si, set_owner=0 ) - self.assertEqual( si.__ac_local_roles__, None ) def test_delObject_before_delete(self): @@ -382,7 +311,6 @@ om._setObject('.bashrc', si) def test_setObject_checkId_bad(self): - from zExceptions import BadRequest om = self._makeOne() si = SimpleItem('111') om._setObject('111', si) @@ -401,6 +329,116 @@ self.assertRaises(BadRequest, om._setObject, 'REQUEST', si) self.assertRaises(BadRequest, om._setObject, '/', si) + def test_getsetitem(self): + om = self._makeOne() + si1 = SimpleItem('1') + si2 = SimpleItem('2') + om['1'] = si1 + self.failUnless('1' in om) + self.failUnless(si1 in om.objectValues()) + self.failUnless('1' in om.objectIds()) + om['2'] = si2 + self.failUnless('2' in om) + self.failUnless(si2 in om.objectValues()) + self.failUnless('2' in om.objectIds()) + self.assertRaises(BadRequest, om._setObject, '1', si2) + self.assertRaises(BadRequest, om.__setitem__, '1', si2) + + def test_delitem(self): + om = self._makeOne() + si1 = SimpleItem('1') + si2 = SimpleItem('2') + om['1'] = si1 + om['2'] = si2 + self.failUnless('1' in om) + self.failUnless('2' in om) + del om['1'] + self.failIf('1' in om) + self.failUnless('2' in om) + om._delObject('2') + self.failIf('2' in om) + + def test_iterator(self): + om = self._makeOne() + si1 = SimpleItem('1') + si2 = SimpleItem('2') + om['1'] = si1 + om['2'] = si2 + iterator = iter(om) + self.failUnless(hasattr(iterator, '__iter__')) + self.failUnless(hasattr(iterator, 'next')) + result = [i for i in iterator] + self.failUnless('1' in result) + self.failUnless('2' in result) + + def test_len(self): + om = self._makeOne() + si1 = SimpleItem('1') + si2 = SimpleItem('2') + om['1'] = si1 + om['2'] = si2 + self.failUnless(len(om) == 2) + + def test_get(self): + om = self._makeOne() + si1 = SimpleItem('1') + self.assertRaises(BadRequest, om.__setitem__, 'get', si1) + om['1'] = si1 + self.failUnless(om.get('1') == si1) + # A contained item overwrites the method + self.failUnless(hasattr(om.get, 'im_func')) + om.__dict__['get'] = si1 + self.failUnless(aq_base(om.get) is si1) + self.failUnless(aq_base(om['get']) is si1) + # Once the object is gone, the method is back + del om['get'] + self.failUnless(hasattr(om.get, 'im_func')) + + def test_items(self): + om = self._makeOne() + si1 = SimpleItem('1') + self.assertRaises(BadRequest, om.__setitem__, 'items', si1) + om['1'] = si1 + self.failUnless(('1', si1) in om.items()) + # A contained item overwrites the method + self.failUnless(hasattr(om.items, 'im_func')) + om.__dict__['items'] = si1 + self.failUnless(aq_base(om.items) is si1) + self.failUnless(aq_base(om['items']) is si1) + # Once the object is gone, the method is back + del om['items'] + self.failUnless(hasattr(om.items, 'im_func')) + + def test_keys(self): + om = self._makeOne() + si1 = SimpleItem('1') + self.assertRaises(BadRequest, om.__setitem__, 'keys', si1) + om['1'] = si1 + self.failUnless('1' in om.keys()) + # A contained item overwrites the method + self.failUnless(hasattr(om.keys, 'im_func')) + om.__dict__['keys'] = si1 + self.failUnless(aq_base(om.keys) is si1) + self.failUnless(aq_base(om['keys']) is si1) + # Once the object is gone, the method is back + del om['keys'] + self.failUnless(hasattr(om.keys, 'im_func')) + + def test_values(self): + om = self._makeOne() + si1 = SimpleItem('1') + self.assertRaises(BadRequest, om.__setitem__, 'values', si1) + om['1'] = si1 + self.failUnless(si1 in om.values()) + # A contained item overwrites the method + self.failUnless(hasattr(om.values, 'im_func')) + om.__dict__['values'] = si1 + self.failUnless(aq_base(om.values) is si1) + self.failUnless(aq_base(om['values']) is si1) + # Once the object is gone, the method is back + del om['values'] + self.failUnless(hasattr(om.values, 'im_func')) + def test_list_imports(self): om = self._makeOne() # This must work whether we've done "make instance" or not. _______________________________________________ Zope-Checkins maillist - Zope-Checkins@zope.org http://mail.zope.org/mailman/listinfo/zope-checkins