Commit 324be297 authored by Hanno Schlichting's avatar Hanno Schlichting

OFS.ObjectManager now fully implements the zope.container.IContainer interface...

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.
parent 52e91401
...@@ -114,7 +114,24 @@ Restructuring ...@@ -114,7 +114,24 @@ Restructuring
Features added 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 direct access to many Zope 3 classes without the need to mixin
Acquisition base classes for the security to work. Acquisition base classes for the security to work.
......
...@@ -54,7 +54,6 @@ from zope.event import notify ...@@ -54,7 +54,6 @@ from zope.event import notify
from zope.container.contained import ObjectAddedEvent from zope.container.contained import ObjectAddedEvent
from zope.container.contained import ObjectRemovedEvent from zope.container.contained import ObjectRemovedEvent
from zope.container.contained import notifyContainerModified from zope.container.contained import notifyContainerModified
from zope.container.interfaces import IContainer
from OFS.CopySupport import CopyContainer from OFS.CopySupport import CopyContainer
from OFS.interfaces import IObjectManager from OFS.interfaces import IObjectManager
...@@ -156,7 +155,7 @@ class ObjectManager(CopyContainer, ...@@ -156,7 +155,7 @@ class ObjectManager(CopyContainer,
# The claim to implement IContainer has been made during the Zope3 # The claim to implement IContainer has been made during the Zope3
# integration project called Five but hasn't been completed in full. # integration project called Five but hasn't been completed in full.
implements(IObjectManager, IContainer) implements(IObjectManager)
security = ClassSecurityInfo() security = ClassSecurityInfo()
security.declareObjectProtected(access_contents_information) security.declareObjectProtected(access_contents_information)
...@@ -765,6 +764,9 @@ class ObjectManager(CopyContainer, ...@@ -765,6 +764,9 @@ class ObjectManager(CopyContainer,
break break
return marshal.dumps((mode,0,0,1,owner,group,0,mtime,mtime,mtime)) 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): def __getitem__(self, key):
v=self._getOb(key, None) v=self._getOb(key, None)
if v is not None: return v if v is not None: return v
...@@ -775,6 +777,34 @@ class ObjectManager(CopyContainer, ...@@ -775,6 +777,34 @@ class ObjectManager(CopyContainer,
return NullResource(self, key, request).__of__(self) return NullResource(self, key, request).__of__(self)
raise KeyError, key 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 # Don't InitializeClass, there is a specific __class_init__ on ObjectManager
# InitializeClass(ObjectManager) # InitializeClass(ObjectManager)
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
$Id$ $Id$
""" """
from zope.container.interfaces import IContainer
from zope.interface import Attribute from zope.interface import Attribute
from zope.interface import Interface from zope.interface import Interface
from zope.schema import Bool, BytesLine, Tuple from zope.schema import Bool, BytesLine, Tuple
...@@ -474,7 +475,7 @@ class ICopyContainer(Interface): ...@@ -474,7 +475,7 @@ class ICopyContainer(Interface):
# based on OFS.ObjectManager.ObjectManager # based on OFS.ObjectManager.ObjectManager
class IObjectManager(IZopeObject, ICopyContainer, INavigation, IManageable, class IObjectManager(IZopeObject, ICopyContainer, INavigation, IManageable,
IAcquirer, IPersistent, IDAVCollection, ITraversable, IAcquirer, IPersistent, IDAVCollection, ITraversable,
IPossibleSite): IPossibleSite, IContainer):
"""Generic object manager """Generic object manager
This interface provides core behavior for collections of heterogeneous This interface provides core behavior for collections of heterogeneous
...@@ -582,10 +583,6 @@ class IObjectManager(IZopeObject, ICopyContainer, INavigation, IManageable, ...@@ -582,10 +583,6 @@ class IObjectManager(IZopeObject, ICopyContainer, INavigation, IManageable,
""" """
""" """
def __getitem__(key):
"""
"""
# XXX: might contain non-API methods and outdated comments; # XXX: might contain non-API methods and outdated comments;
# not synced with ZopeBook API Reference; # not synced with ZopeBook API Reference;
......
import unittest import unittest
from zope.app.testing.placelesssetup import PlacelessSetup
from AccessControl.Owned import EmergencyUserCannotOwn from AccessControl.Owned import EmergencyUserCannotOwn
from AccessControl.SecurityManagement import newSecurityManager from AccessControl.SecurityManagement import newSecurityManager
from AccessControl.SecurityManagement import noSecurityManager from AccessControl.SecurityManagement import noSecurityManager
from AccessControl.User import User # before SpecialUsers from AccessControl.User import User # before SpecialUsers
from AccessControl.SpecialUsers import emergency_user, nobody, system from AccessControl.SpecialUsers import emergency_user, nobody, system
from Acquisition import aq_base
from Acquisition import Implicit from Acquisition import Implicit
from App.config import getConfiguration from App.config import getConfiguration
from logging import getLogger from logging import getLogger
from OFS.ObjectManager import ObjectManager from OFS.ObjectManager import ObjectManager
from OFS.SimpleItem import SimpleItem from OFS.SimpleItem import SimpleItem
from zope.app.testing.placelesssetup import PlacelessSetup
import Products.Five import Products.Five
from Products.Five import zcml from Products.Five import zcml
from Products.Five.eventconfigure import setDeprecatedManageAddDelete from Products.Five.eventconfigure import setDeprecatedManageAddDelete
from zExceptions import BadRequest
logger = getLogger('OFS.subscribers') logger = getLogger('OFS.subscribers')
...@@ -87,7 +90,6 @@ class ObjectManagerTests(PlacelessSetup, unittest.TestCase): ...@@ -87,7 +90,6 @@ class ObjectManagerTests(PlacelessSetup, unittest.TestCase):
return ObjectManagerWithIItem return ObjectManagerWithIItem
def _makeOne( self, *args, **kw ): def _makeOne( self, *args, **kw ):
return self._getTargetClass()( *args, **kw ).__of__( FauxRoot() ) return self._getTargetClass()( *args, **kw ).__of__( FauxRoot() )
def test_z3interfaces(self): def test_z3interfaces(self):
...@@ -98,175 +100,102 @@ class ObjectManagerTests(PlacelessSetup, unittest.TestCase): ...@@ -98,175 +100,102 @@ class ObjectManagerTests(PlacelessSetup, unittest.TestCase):
verifyClass(IObjectManager, ObjectManager) verifyClass(IObjectManager, ObjectManager)
def test_setObject_set_owner_with_no_user( self ): def test_setObject_set_owner_with_no_user( self ):
om = self._makeOne() om = self._makeOne()
newSecurityManager( None, None ) newSecurityManager( None, None )
si = SimpleItem( 'no_user' ) si = SimpleItem( 'no_user' )
om._setObject( 'no_user', si ) om._setObject( 'no_user', si )
self.assertEqual( si.__ac_local_roles__, None ) self.assertEqual( si.__ac_local_roles__, None )
def test_setObject_set_owner_with_emergency_user( self ): def test_setObject_set_owner_with_emergency_user( self ):
om = self._makeOne() om = self._makeOne()
newSecurityManager( None, emergency_user ) newSecurityManager( None, emergency_user )
si = SimpleItem( 'should_fail' ) si = SimpleItem( 'should_fail' )
self.assertEqual( si.__ac_local_roles__, None ) self.assertEqual( si.__ac_local_roles__, None )
self.assertRaises( EmergencyUserCannotOwn self.assertRaises( EmergencyUserCannotOwn
, om._setObject, 'should_fail', si ) , om._setObject, 'should_fail', si )
def test_setObject_set_owner_with_system_user( self ): def test_setObject_set_owner_with_system_user( self ):
om = self._makeOne() om = self._makeOne()
newSecurityManager( None, system ) newSecurityManager( None, system )
si = SimpleItem( 'system' ) si = SimpleItem( 'system' )
self.assertEqual( si.__ac_local_roles__, None ) self.assertEqual( si.__ac_local_roles__, None )
om._setObject( 'system', si ) om._setObject( 'system', si )
self.assertEqual( si.__ac_local_roles__, None ) self.assertEqual( si.__ac_local_roles__, None )
def test_setObject_set_owner_with_anonymous_user( self ): def test_setObject_set_owner_with_anonymous_user( self ):
om = self._makeOne() om = self._makeOne()
newSecurityManager( None, nobody ) newSecurityManager( None, nobody )
si = SimpleItem( 'anon' ) si = SimpleItem( 'anon' )
self.assertEqual( si.__ac_local_roles__, None ) self.assertEqual( si.__ac_local_roles__, None )
om._setObject( 'anon', si ) om._setObject( 'anon', si )
self.assertEqual( si.__ac_local_roles__, None ) self.assertEqual( si.__ac_local_roles__, None )
def test_setObject_set_owner_with_user( self ): def test_setObject_set_owner_with_user( self ):
om = self._makeOne() om = self._makeOne()
user = User( 'user', '123', (), () ).__of__( FauxRoot() ) user = User( 'user', '123', (), () ).__of__( FauxRoot() )
newSecurityManager( None, user ) newSecurityManager( None, user )
si = SimpleItem( 'user_creation' ) si = SimpleItem( 'user_creation' )
self.assertEqual( si.__ac_local_roles__, None ) self.assertEqual( si.__ac_local_roles__, None )
om._setObject( 'user_creation', si ) om._setObject( 'user_creation', si )
self.assertEqual( si.__ac_local_roles__, { 'user': ['Owner'] } ) self.assertEqual( si.__ac_local_roles__, { 'user': ['Owner'] } )
def test_setObject_set_owner_with_faux_user( self ): def test_setObject_set_owner_with_faux_user( self ):
om = self._makeOne() om = self._makeOne()
user = FauxUser( 'user_id', 'user_login' ).__of__( FauxRoot() ) user = FauxUser( 'user_id', 'user_login' ).__of__( FauxRoot() )
newSecurityManager( None, user ) newSecurityManager( None, user )
si = SimpleItem( 'faux_creation' ) si = SimpleItem( 'faux_creation' )
self.assertEqual( si.__ac_local_roles__, None ) self.assertEqual( si.__ac_local_roles__, None )
om._setObject( 'faux_creation', si ) om._setObject( 'faux_creation', si )
self.assertEqual( si.__ac_local_roles__, { 'user_id': ['Owner'] } ) self.assertEqual( si.__ac_local_roles__, { 'user_id': ['Owner'] } )
def test_setObject_no_set_owner_with_no_user( self ): def test_setObject_no_set_owner_with_no_user( self ):
om = self._makeOne() om = self._makeOne()
newSecurityManager( None, None ) newSecurityManager( None, None )
si = SimpleItem( 'should_be_okay' ) si = SimpleItem( 'should_be_okay' )
self.assertEqual( si.__ac_local_roles__, None ) self.assertEqual( si.__ac_local_roles__, None )
om._setObject( 'should_be_okay', si, set_owner=0 ) om._setObject( 'should_be_okay', si, set_owner=0 )
self.assertEqual( si.__ac_local_roles__, None ) self.assertEqual( si.__ac_local_roles__, None )
def test_setObject_no_set_owner_with_emergency_user( self ): def test_setObject_no_set_owner_with_emergency_user( self ):
om = self._makeOne() om = self._makeOne()
newSecurityManager( None, emergency_user ) newSecurityManager( None, emergency_user )
si = SimpleItem( 'should_be_okay' ) si = SimpleItem( 'should_be_okay' )
self.assertEqual( si.__ac_local_roles__, None ) self.assertEqual( si.__ac_local_roles__, None )
om._setObject( 'should_be_okay', si, set_owner=0 ) om._setObject( 'should_be_okay', si, set_owner=0 )
self.assertEqual( si.__ac_local_roles__, None ) self.assertEqual( si.__ac_local_roles__, None )
def test_setObject_no_set_owner_with_system_user( self ): def test_setObject_no_set_owner_with_system_user( self ):
om = self._makeOne() om = self._makeOne()
newSecurityManager( None, system ) newSecurityManager( None, system )
si = SimpleItem( 'should_be_okay' ) si = SimpleItem( 'should_be_okay' )
self.assertEqual( si.__ac_local_roles__, None ) self.assertEqual( si.__ac_local_roles__, None )
om._setObject( 'should_be_okay', si, set_owner=0 ) om._setObject( 'should_be_okay', si, set_owner=0 )
self.assertEqual( si.__ac_local_roles__, None ) self.assertEqual( si.__ac_local_roles__, None )
def test_setObject_no_set_owner_with_anonymous_user( self ): def test_setObject_no_set_owner_with_anonymous_user( self ):
om = self._makeOne() om = self._makeOne()
newSecurityManager( None, nobody ) newSecurityManager( None, nobody )
si = SimpleItem( 'should_be_okay' ) si = SimpleItem( 'should_be_okay' )
self.assertEqual( si.__ac_local_roles__, None ) self.assertEqual( si.__ac_local_roles__, None )
om._setObject( 'should_be_okay', si, set_owner=0 ) om._setObject( 'should_be_okay', si, set_owner=0 )
self.assertEqual( si.__ac_local_roles__, None ) self.assertEqual( si.__ac_local_roles__, None )
def test_setObject_no_set_owner_with_user( self ): def test_setObject_no_set_owner_with_user( self ):
om = self._makeOne() om = self._makeOne()
user = User( 'user', '123', (), () ).__of__( FauxRoot() ) user = User( 'user', '123', (), () ).__of__( FauxRoot() )
newSecurityManager( None, user ) newSecurityManager( None, user )
si = SimpleItem( 'should_be_okay' ) si = SimpleItem( 'should_be_okay' )
self.assertEqual( si.__ac_local_roles__, None ) self.assertEqual( si.__ac_local_roles__, None )
om._setObject( 'should_be_okay', si, set_owner=0 ) om._setObject( 'should_be_okay', si, set_owner=0 )
self.assertEqual( si.__ac_local_roles__, None ) self.assertEqual( si.__ac_local_roles__, None )
def test_setObject_no_set_owner_with_faux_user( self ): def test_setObject_no_set_owner_with_faux_user( self ):
om = self._makeOne() om = self._makeOne()
user = FauxUser( 'user_id', 'user_login' ).__of__( FauxRoot() ) user = FauxUser( 'user_id', 'user_login' ).__of__( FauxRoot() )
newSecurityManager( None, user ) newSecurityManager( None, user )
si = SimpleItem( 'should_be_okay' ) si = SimpleItem( 'should_be_okay' )
self.assertEqual( si.__ac_local_roles__, None ) self.assertEqual( si.__ac_local_roles__, None )
om._setObject( 'should_be_okay', si, set_owner=0 ) om._setObject( 'should_be_okay', si, set_owner=0 )
self.assertEqual( si.__ac_local_roles__, None ) self.assertEqual( si.__ac_local_roles__, None )
def test_delObject_before_delete(self): def test_delObject_before_delete(self):
...@@ -382,7 +311,6 @@ class ObjectManagerTests(PlacelessSetup, unittest.TestCase): ...@@ -382,7 +311,6 @@ class ObjectManagerTests(PlacelessSetup, unittest.TestCase):
om._setObject('.bashrc', si) om._setObject('.bashrc', si)
def test_setObject_checkId_bad(self): def test_setObject_checkId_bad(self):
from zExceptions import BadRequest
om = self._makeOne() om = self._makeOne()
si = SimpleItem('111') si = SimpleItem('111')
om._setObject('111', si) om._setObject('111', si)
...@@ -401,6 +329,116 @@ class ObjectManagerTests(PlacelessSetup, unittest.TestCase): ...@@ -401,6 +329,116 @@ class ObjectManagerTests(PlacelessSetup, unittest.TestCase):
self.assertRaises(BadRequest, om._setObject, 'REQUEST', si) self.assertRaises(BadRequest, om._setObject, 'REQUEST', si)
self.assertRaises(BadRequest, om._setObject, '/', 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): def test_list_imports(self):
om = self._makeOne() om = self._makeOne()
# This must work whether we've done "make instance" or not. # This must work whether we've done "make instance" or not.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment