[Zope-CVS] CVS: Packages/zasync - manager.py:1.3 manager.txt:1.2
Gary Poster
gary at zope.com
Tue Oct 19 12:42:22 EDT 2004
Update of /cvs-repository/Packages/zasync
In directory cvs.zope.org:/tmp/cvs-serv24463
Modified Files:
manager.py manager.txt
Log Message:
Don't raise an error when a deferred is called twice--this will abort the transaction, which means that the deferred will never be resolved if it was supposed to be. Therefore, return a failure and log the problem. Also add a 'here' to the context dict for the callbacks because some code wants it.
=== Packages/zasync/manager.py 1.2 => 1.3 ===
--- Packages/zasync/manager.py:1.2 Thu Oct 14 12:35:04 2004
+++ Packages/zasync/manager.py Tue Oct 19 12:42:21 2004
@@ -18,7 +18,7 @@
"""
from types import NoneType
-import datetime, random, sys, os
+import datetime, random, sys, os, logging
from twisted.internet import defer
from twisted.python import failure
@@ -173,10 +173,12 @@
'tool': tool,
'root': root,
'deferred': self,
+ 'here': self,
'modules': SecureModuleImporter,
'request': root.REQUEST,
'result': self.result,
'failure': self.failure,
+ 'original': res,
'results': results,
}
try:
@@ -225,18 +227,32 @@
def callback(self, result):
self.aq_inner.aq_parent.resolve(self)
if self.raw_state != UNCALLED:
- raise RuntimeError("Deferred has already been called")
+ # we don't want to abort the transaction because there's a good
+ # chance that we *need* to have the manager resolve this deferred.
+ # Therefore, we return a failure rather than raise a transaction.
+ # We do not call the callbacks.
+ logging.getLogger('zasync').error(
+ "Zope deferred %r has already been called" % (self.key,))
+ return failure.Failure(
+ RuntimeError("Deferred has already been called"))
self.raw_state = CALLED
self.original_result = self.result = result
return self.__call_all()
security.declarePrivate('errback')
- def errback(self, failure):
+ def errback(self, fail):
self.aq_inner.aq_parent.resolve(self)
if self.raw_state != UNCALLED:
- raise RuntimeError("Deferred has already been called")
+ # we don't want to abort the transaction because there's a good
+ # chance that we *need* to have the manager resolve this deferred.
+ # Therefore, we return a failure rather than raise a transaction.
+ # We do not call the errbacks.
+ logging.getLogger('zasync').error(
+ "Zope deferred %r has already been called" % (self.key,))
+ return failure.Failure(
+ RuntimeError("Deferred has already been called"))
self.raw_state = FAILURE
- self.original_failure = self.failure = failure
+ self.original_failure = self.failure = fail
return self.__call_all()
security.declareProtected(permissions.ModifyDeferred,
=== Packages/zasync/manager.txt 1.1.1.1 => 1.2 ===
--- Packages/zasync/manager.txt:1.1.1.1 Sun Oct 10 19:37:07 2004
+++ Packages/zasync/manager.txt Tue Oct 19 12:42:21 2004
@@ -358,16 +358,40 @@
>>> res is out
True
-Note that you may only call errback or callback once.
+Note that you may only call errback or callback once. This actually tries
+to send a message to the zasync logger, because it should only be called from
+within the zasync client. So first we'll set up a handler for the zasync log.
->>> acm.getDeferred(appDeferred.key).callback(42)
-Traceback (most recent call last):
-...
-RuntimeError: Deferred has already been called
->>> acm.getDeferred(appDeferred.key).errback(res)
-Traceback (most recent call last):
-...
-RuntimeError: Deferred has already been called
+>>> import logging, StringIO
+>>> logger = logging.getLogger('zasync')
+>>> log_out = StringIO.StringIO()
+>>> hdlr = logging.StreamHandler(log_out)
+>>> formatter = logging.Formatter('%(levelname)s %(message)s')
+>>> hdlr.setFormatter(formatter)
+>>> logger.addHandler(hdlr)
+>>> logger.setLevel(logging.DEBUG)
+
+Now we'll actually test the re-calls. First a callback...
+
+>>> out = acm.getDeferred(appDeferred.key).callback(42)
+>>> isinstance(out, failure.Failure)
+True
+>>> out.getErrorMessage()
+'Deferred has already been called'
+>>> log_out.getvalue() == (
+... 'ERROR Zope deferred %r has already been called\n' % (appDeferred.key,))
+True
+
+...and now an errback.
+
+>>> log_out.seek(0) # clear the error log
+>>> log_out.truncate()
+>>> out = acm.getDeferred(appDeferred.key).errback(res)
+>>> isinstance(out, failure.Failure)
+True
+>>> log_out.getvalue() == (
+... 'ERROR Zope deferred %r has already been called\n' % (appDeferred.key,))
+True
Now that a deferred has been resolved, there should only be one accepted call
remaining, and the next cache rotation should be set.
More information about the Zope-CVS
mailing list