[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