[Zope-dev] zope.intid and zope.keyreference.interfaces.NotYet exception (patch)
Jan-Wijbrand Kolman
janwijbrand at gmail.com
Tue Jul 3 11:56:39 UTC 2012
On 7/3/12 13:26 , Cykooz wrote:
> 2012/7/3 Jan-Wijbrand Kolman <janwijbrand at gmail.com
> <mailto:janwijbrand at gmail.com>>
>
> Are you basically using a forked zope.keyreference for the time being?
>
> No, I do not use a forked zope.keyreference. I used my fork of the
> zope.intid.
Ah, yes of course, that's what I meant: zope.intid.
I would not consider myself in a position currently to vouch for you to
have comitter rights. I do however wonder if anyone else would like to
comment on your proposed change.
If the comments a favorable I could try to help apply the patch to
zope.intid. This would probably help me and you as you then would not
have to use a forked package anymore.
At the end of this post, I pasted the diff from the current zope.intid
trunk against your "fork" on bitbucket. Maybe this would make it easier
for others to comment on it?
regards, jw
Proposed patch:
===============
diff -u zope.intid/trunk/src/zope/intid//__init__.py
zope.intid-cykooz/src/zope/intid//__init__.py
--- zope.intid/trunk/src/zope/intid//__init__.py 2012-07-03
11:56:11.576511518 +0200
+++ zope.intid-cykooz/src/zope/intid//__init__.py 2012-07-03
11:55:17.261865415 +0200
@@ -19,12 +19,14 @@
This functionality can be used in cataloging.
"""
import random
+import threading
+from weakref import WeakKeyDictionary, WeakSet
import BTrees
from persistent import Persistent
from zope.component import adapter, getAllUtilitiesRegisteredFor,
subscribers
from zope.event import notify
-from zope.interface import implementer
+from zope.interface import implements
from zope.keyreference.interfaces import IKeyReference, NotYet
from zope.lifecycleevent.interfaces import IObjectAddedEvent
from zope.lifecycleevent.interfaces import IObjectRemovedEvent
@@ -32,16 +34,16 @@
from zope.location.interfaces import IContained
from zope.security.proxy import removeSecurityProxy
-from zope.intid.interfaces import IIntIds, IIntIdEvent
+from zope.intid.interfaces import IIntIds, IIntIdEvent, IIntIdsDisabled
from zope.intid.interfaces import IntIdAddedEvent, IntIdRemovedEvent
- at implementer(IIntIds, IContained)
class IntIds(Persistent):
"""This utility provides a two way mapping between objects and
integer ids.
IKeyReferences to objects are stored in the indexes.
"""
+ implements(IIntIds, IContained)
__parent__ = __name__ = None
@@ -136,6 +138,10 @@
del self.ids[key]
+thread_data = threading.local()
+thread_data.deferred_objects = WeakKeyDictionary()
+
+
@adapter(ILocation, IObjectRemovedEvent)
def removeIntIdSubscriber(ob, event):
"""A subscriber to ObjectRemovedEvent
@@ -143,9 +149,22 @@
Removes the unique ids registered for the object in all the unique
id utilities.
"""
+
utilities = tuple(getAllUtilitiesRegisteredFor(IIntIds))
if utilities:
- key = IKeyReference(ob, None)
+ try:
+ key = IKeyReference(ob, None)
+ except NotYet:
+ deferred_objects = thread_data.deferred_objects
+ if ob in deferred_objects:
+ del deferred_objects[ob]
+ parent = getattr(ob, '__parent__', None)
+ if parent in deferred_objects and ob in
deferred_objects[parent]:
+ deferred_objects[parent].remove(ob)
+ if len(deferred_objects[parent]) == 0:
+ del deferred_objects[parent]
+ return
+
# Register only objects that adapt to key reference
if key is not None:
# Notify the catalogs that this object is about to be removed.
@@ -156,6 +175,7 @@
except KeyError:
pass
+
@adapter(ILocation, IObjectAddedEvent)
def addIntIdSubscriber(ob, event):
"""A subscriber to ObjectAddedEvent
@@ -163,16 +183,46 @@
Registers the object added in all unique id utilities and fires
an event for the catalogs.
"""
+
utilities = tuple(getAllUtilitiesRegisteredFor(IIntIds))
if utilities: # assert that there are any utilites
+ register_object(ob, utilities, event)
+
+
+def register_object(ob, utilities, event):
+ deferred_objects = thread_data.deferred_objects
+ intids_enabled = not IIntIdsDisabled.providedBy(ob)
+ try:
key = IKeyReference(ob, None)
- # Register only objects that adapt to key reference
- if key is not None:
- idmap = {}
- for utility in utilities:
- idmap[utility] = utility.register(key)
- # Notify the catalogs that this object was added.
- notify(IntIdAddedEvent(ob, event, idmap))
+ except NotYet:
+ if intids_enabled:
+ parent = getattr(ob, '__parent__', None)
+ if parent is None:
+ raise
+ if parent not in deferred_objects:
+ deferred_objects[parent] = WeakSet()
+ deferred_objects[parent].add(ob)
+ return
+
+ # Register only objects that adapt to key reference
+ if key is None:
+ return
+
+ # Register the current object if it is enabled
+ if intids_enabled:
+ idmap = {}
+ for utility in utilities:
+ idmap[utility] = utility.register(key)
+ # Notify the catalogs that this object was added.
+ notify(IntIdAddedEvent(ob, event, idmap))
+
+ # Register the deferred children of the current object
+ if ob in deferred_objects:
+ children = deferred_objects.pop(ob)
+ for child in children:
+ if child.__parent__ is ob:
+ register_object(child, utilities, event)
+
@adapter(IIntIdEvent)
def intIdEventNotify(event):
diff -u zope.intid/trunk/src/zope/intid//interfaces.py
zope.intid-cykooz/src/zope/intid//interfaces.py
--- zope.intid/trunk/src/zope/intid//interfaces.py 2012-07-03
11:56:11.576511518 +0200
+++ zope.intid-cykooz/src/zope/intid//interfaces.py 2012-07-03
11:55:17.261865415 +0200
@@ -1,6 +1,6 @@
"""Interfaces for the unique id utility.
"""
-from zope.interface import Interface, Attribute, implementer
+from zope.interface import Interface, Attribute, implements
class IIntIdsQuery(Interface):
@@ -61,6 +61,10 @@
"""
+class IIntIdsDisabled(Interface):
+ """ Marker for objects that should not be indexed. """
+
+
class IIntIdEvent(Interface):
"""Generic base interface for IntId-related events"""
@@ -77,12 +81,13 @@
"""
- at implementer(IIntIdRemovedEvent)
class IntIdRemovedEvent(object):
"""The event which is published before the unique id is removed
from the utility so that the catalogs can unindex the object.
"""
+ implements(IIntIdRemovedEvent)
+
def __init__(self, object, event):
self.object = object
self.original_event = event
@@ -98,12 +103,13 @@
idmap = Attribute("The dictionary that holds an (utility -> id)
mapping of created ids")
- at implementer(IIntIdAddedEvent)
class IntIdAddedEvent(object):
"""The event which gets sent when an object is registered in a
unique id utility.
"""
+ implements(IIntIdAddedEvent)
+
def __init__(self, object, event, idmap=None):
self.object = object
self.original_event = event
Only in zope.intid/trunk/src/zope/intid/: .svn
diff -u zope.intid/trunk/src/zope/intid//tests.py
zope.intid-cykooz/src/zope/intid//tests.py
--- zope.intid/trunk/src/zope/intid//tests.py 2012-07-03
11:56:11.576511518 +0200
+++ zope.intid-cykooz/src/zope/intid//tests.py 2012-07-03
11:55:17.261865415 +0200
@@ -13,6 +13,7 @@
##############################################################################
"""Tests for the unique id utility.
"""
+import gc
import random
import unittest
@@ -25,23 +26,25 @@
from zope.component import provideHandler
from zope.component import testing, eventtesting
from zope.component.interfaces import ISite, IComponentLookup
-from zope.interface import implementer, Interface
+from zope.container.interfaces import ISimpleReadContainer
+from zope.container.traversal import ContainerTraversable
+from zope.interface import implements, Interface
+from zope.interface.declarations import directlyProvides
from zope.interface.verify import verifyObject
from zope.keyreference.persistent import KeyReferenceToPersistent
from zope.keyreference.persistent import connectionOfPersistent
from zope.keyreference.interfaces import IKeyReference
+from zope.lifecycleevent import ObjectAddedEvent
from zope.location.interfaces import ILocation
from zope.site.hooks import setSite, setHooks, resetHooks
-from zope.site.folder import rootFolder
+from zope.site.folder import rootFolder, Folder
from zope.site.site import SiteManagerAdapter, LocalSiteManager
from zope.traversing import api
from zope.traversing.testing import setUp as traversingSetUp
from zope.traversing.interfaces import ITraversable
-from zope.container.traversal import ContainerTraversable
-from zope.container.interfaces import ISimpleReadContainer
-from zope.intid import IntIds, intIdEventNotify
-from zope.intid.interfaces import IIntIds
+from zope.intid import IntIds, intIdEventNotify, addIntIdSubscriber
+from zope.intid.interfaces import IIntIds, IIntIdsDisabled
# Local Utility Addition
@@ -67,9 +70,8 @@
return api.traverse(folder, "++etc++site")
- at implementer(ILocation)
class P(Persistent):
- pass
+ implements(ILocation)
class ConnectionStub(object):
@@ -101,9 +103,9 @@
self.root = rootFolder()
createSiteManager(self.root, setsite=True)
- provideAdapter(connectionOfPersistent, (IPersistent, ),
IConnection)
+ provideAdapter(connectionOfPersistent, (IPersistent,), IConnection)
provideAdapter(
- KeyReferenceToPersistent, (IPersistent, ), IKeyReference)
+ KeyReferenceToPersistent, (IPersistent,), IKeyReference)
def tearDown(self):
resetHooks()
@@ -159,14 +161,14 @@
# behaviour of the _generateId method
u = self.createIntIds()
maxint = u.family.maxint
- u._randrange = lambda x,y: maxint-1
+ u._randrange = lambda x, y: maxint - 1
conn = ConnectionStub()
obj = P()
conn.add(obj)
uid = u.register(obj)
- self.assertEquals(maxint-1, uid)
+ self.assertEquals(maxint - 1, uid)
self.assertEquals(maxint, u._v_nextid)
# The next chosen int is exactly the largest number possible
that is
@@ -243,7 +245,6 @@
class TestSubscribers(ReferenceSetupMixin, unittest.TestCase):
def setUp(self):
- from zope.site.folder import Folder
ReferenceSetupMixin.setUp(self)
@@ -298,8 +299,6 @@
self.assertEquals(objevents[0][1].original_event.object,
parent_folder)
def test_addIntIdSubscriber(self):
- from zope.lifecycleevent import ObjectAddedEvent
- from zope.intid import addIntIdSubscriber
from zope.intid.interfaces import IIntIdAddedEvent
from zope.site.interfaces import IFolder
parent_folder = self.root['folder1']['folder1_1']
@@ -344,11 +343,63 @@
return IntIds(family=BTrees.family64)
+class TestNotYetFix(ReferenceSetupMixin, unittest.TestCase):
+
+ def setUp(self):
+ ReferenceSetupMixin.setUp(self)
+ sm = getSiteManager(self.root)
+ self.utility = addUtility(sm, '1', IIntIds, IntIds())
+ provideHandler(intIdEventNotify)
+ self.root._p_jar = ConnectionStub()
+ setSite(self.root)
+
+ def test_deferred_indexes(self):
+ folder1 = Folder()
+ folder2 = Folder()
+
+ folder1['folder2'] = folder2
+ addIntIdSubscriber(folder2, ObjectAddedEvent(folder1))
+ self.assertRaises(KeyError, self.utility.getId, folder2)
+
+ self.root['folder1'] = folder1
+ addIntIdSubscriber(folder1, ObjectAddedEvent(self.root))
+ self.assertIsNotNone(self.utility.getId(folder1))
+ self.assertIsNotNone(self.utility.getId(folder2))
+
+ def test_clear_deferred_objects(self):
+ from zope.intid import thread_data
+
+ folder1 = Folder()
+ folder2 = Folder()
+ folder1['folder2'] = folder2
+
+ addIntIdSubscriber(folder2, ObjectAddedEvent(folder1))
+ eventtesting.clearEvents()
+
+ self.assertRaises(KeyError, self.utility.getId, folder2)
+ self.assertTrue(len(thread_data.deferred_objects) > 0)
+
+ del folder2
+ del folder1
+
+ gc.collect()
+
+ self.assertTrue(len(thread_data.deferred_objects) == 0)
+
+ def test_IIntIdsDisabled(self):
+ folder = Folder()
+ directlyProvides(folder, IIntIdsDisabled)
+ self.root['folder'] = folder
+ addIntIdSubscriber(folder, ObjectAddedEvent(self.root))
+ self.assertRaises(KeyError, self.utility.getId, folder)
+
+
def test_suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(TestIntIds))
suite.addTest(unittest.makeSuite(TestIntIds64))
suite.addTest(unittest.makeSuite(TestSubscribers))
+ suite.addTest(unittest.makeSuite(TestNotYetFix))
return suite
if __name__ == '__main__':
More information about the Zope-Dev
mailing list