Changeset 469:30576260957d
- Timestamp:
- 08/25/08 13:45:02
(5 months ago)
- Author:
- Brian Warner <warner@allmydata.com>
- branch:
- default
- Message:
replace ConnectionDone? and ConnectionClosed? with DeadReferenceError?, so application code only has to check for a single exception type. Closes #89.
-
Files:
-
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
| r468 |
r469 |
|
| | 1 | 2008-08-25 Brian Warner <warner@lothar.com> |
|---|
| | 2 | |
|---|
| | 3 | * foolscap/broker.py (Broker.abandonAllRequests): map both |
|---|
| | 4 | ConnectionLost and ConnectionDone into DeadReferenceError, so that |
|---|
| | 5 | application code only needs to check for one exception type. |
|---|
| | 6 | * foolscap/negotiate.py (Negotiation.evaluateNegotiationVersion1): |
|---|
| | 7 | when an existing connection is dropped in favor of a new one, drop |
|---|
| | 8 | it with DeadReferenceError instead of ConnectionDone. The mapping |
|---|
| | 9 | in Broker.abandonAllRequests doesn't seem to quite catch |
|---|
| | 10 | everything in unit tests. |
|---|
| | 11 | (Negotiation.acceptDecisionVersion1): same, when we're the slave |
|---|
| | 12 | * foolscap/test/test_call.py (TestCall.test_connection_lost_is_deadref): |
|---|
| | 13 | test it |
|---|
| | 14 | (TestCall.test_connection_done_is_deadref): same |
|---|
| | 15 | (TestCall.testChoiceOf): switch to use ShouldFailMixin |
|---|
| | 16 | * foolscap/test/test_gifts.py (Gifts): remove |
|---|
| | 17 | ignoreConnectionDone, just look for DeadReferenceError |
|---|
| | 18 | |
|---|
| 1 | 19 | 2008-08-21 Brian Warner <warner@lothar.com> |
|---|
| 2 | 20 | |
|---|
| r406 |
r469 |
|
| 483 | 483 | |
|---|
| 484 | 484 | def abandonAllRequests(self, why): |
|---|
| | 485 | # map all connection-lost errors to DeadReferenceError, so |
|---|
| | 486 | # application code only needs to check for one exception type |
|---|
| | 487 | if why.check(error.ConnectionLost, error.ConnectionDone): |
|---|
| | 488 | why = failure.Failure(DeadReferenceError("Connection was lost")) |
|---|
| 485 | 489 | for req in self.waitingForAnswers.values(): |
|---|
| 486 | 490 | eventually(req.fail, why) |
|---|
| r379 |
r469 |
|
| 4 | 4 | from twisted.python.failure import Failure |
|---|
| 5 | 5 | from twisted.internet import protocol, reactor |
|---|
| 6 | | from twisted.internet.error import ConnectionDone |
|---|
| 7 | 6 | |
|---|
| 8 | 7 | from foolscap import broker, referenceable, vocab |
|---|
| … | … | |
| 10 | 9 | from foolscap.tokens import SIZE_LIMIT, ERROR, \ |
|---|
| 11 | 10 | BananaError, NegotiationError, RemoteNegotiationError |
|---|
| | 11 | from foolscap.ipb import DeadReferenceError |
|---|
| 12 | 12 | from foolscap.banana import int2b128 |
|---|
| 13 | 13 | from foolscap.logging import log |
|---|
| … | … | |
| 785 | 785 | self.log("accepting new offer, dropping existing connection", |
|---|
| 786 | 786 | parent=lp) |
|---|
| 787 | | err = ConnectionDone("[%s] replaced by a new connection" |
|---|
| 788 | | % theirTubRef.getShortTubID()) |
|---|
| | 787 | err = DeadReferenceError("[%s] replaced by a new connection" |
|---|
| | 788 | % theirTubRef.getShortTubID()) |
|---|
| 789 | 789 | why = Failure(err) |
|---|
| 790 | 790 | existing.shutdown(why) |
|---|
| … | … | |
| 1079 | 1079 | self.log("master told us to use a new connection, " |
|---|
| 1080 | 1080 | "so we must drop the existing one", level=UNUSUAL) |
|---|
| 1081 | | err = ConnectionDone("replaced by a new connection") |
|---|
| | 1081 | err = DeadReferenceError("replaced by a new connection") |
|---|
| 1082 | 1082 | why = Failure(err) |
|---|
| 1083 | 1083 | self.tub.brokers[self.theirTubRef].shutdown(why) |
|---|
| r374 |
r469 |
|
| 11 | 11 | from twisted.python import log |
|---|
| 12 | 12 | from twisted.trial import unittest |
|---|
| 13 | | from twisted.internet.main import CONNECTION_LOST |
|---|
| | 13 | from twisted.internet.main import CONNECTION_LOST, CONNECTION_DONE |
|---|
| 14 | 14 | from twisted.python.failure import Failure |
|---|
| 15 | 15 | |
|---|
| 16 | 16 | from foolscap.tokens import Violation |
|---|
| 17 | 17 | from foolscap.eventual import flushEventualQueue |
|---|
| 18 | | from foolscap.test.common import HelperTarget, TargetMixin |
|---|
| | 18 | from foolscap.test.common import HelperTarget, TargetMixin, ShouldFailMixin |
|---|
| 19 | 19 | from foolscap.test.common import RIMyTarget, Target, TargetWithoutInterfaces, \ |
|---|
| 20 | 20 | BrokenTarget |
|---|
| | 21 | from foolscap import DeadReferenceError |
|---|
| 21 | 22 | |
|---|
| 22 | 23 | class Unsendable: |
|---|
| … | … | |
| 24 | 25 | |
|---|
| 25 | 26 | |
|---|
| 26 | | class TestCall(TargetMixin, unittest.TestCase): |
|---|
| | 27 | class TestCall(TargetMixin, ShouldFailMixin, unittest.TestCase): |
|---|
| 27 | 28 | def setUp(self): |
|---|
| 28 | 29 | TargetMixin.setUp(self) |
|---|
| … | … | |
| 201 | 202 | d.addCallback(lambda res: rr.callRemote("choice1", "a"*2000)) |
|---|
| 202 | 203 | d.addCallback(lambda res: self.failUnlessEqual(res, None)) |
|---|
| 203 | | def _check_false(res): |
|---|
| 204 | | # False does not conform |
|---|
| 205 | | d1 = rr.callRemote("choice1", False) |
|---|
| 206 | | d1.addBoth(self.shouldFail, Violation, "testChoiceOf") |
|---|
| 207 | | return d1 |
|---|
| 208 | | d.addCallback(_check_false) |
|---|
| 209 | | return d |
|---|
| 210 | | |
|---|
| 211 | | def shouldFail(self, res, expected_failure, which, substring=None): |
|---|
| 212 | | if isinstance(res, Failure): |
|---|
| 213 | | res.trap(expected_failure) |
|---|
| 214 | | if substring: |
|---|
| 215 | | self.failUnless(substring in str(res), |
|---|
| 216 | | "substring '%s' not in '%s'" |
|---|
| 217 | | % (substring, str(res))) |
|---|
| 218 | | else: |
|---|
| 219 | | self.fail("%s was supposed to raise %s, not get '%s'" % |
|---|
| 220 | | (which, expected_failure, res)) |
|---|
| | 204 | # False does not conform |
|---|
| | 205 | d.addCallback(lambda res: |
|---|
| | 206 | self.shouldFail(Violation, "testChoiceOf", None, |
|---|
| | 207 | rr.callRemote, "choice1", False)) |
|---|
| | 208 | return d |
|---|
| 221 | 209 | |
|---|
| 222 | 210 | def testMegaSchema(self): |
|---|
| … | … | |
| 410 | 398 | return d |
|---|
| 411 | 399 | |
|---|
| | 400 | def test_connection_lost_is_deadref(self): |
|---|
| | 401 | rr, target = self.setupTarget(HelperTarget()) |
|---|
| | 402 | d1 = rr.callRemote("hang") |
|---|
| | 403 | def get_d(): return d1 |
|---|
| | 404 | rr.tracker.broker.transport.loseConnection(Failure(CONNECTION_LOST)) |
|---|
| | 405 | d = self.shouldFail(DeadReferenceError, "lost_is_deadref.1", |
|---|
| | 406 | "Connection was lost", |
|---|
| | 407 | get_d) |
|---|
| | 408 | # and once the connection is down, we should get a DeadReferenceError |
|---|
| | 409 | # for new messages |
|---|
| | 410 | d.addCallback(lambda res: |
|---|
| | 411 | self.shouldFail(DeadReferenceError, "lost_is_deadref.2", |
|---|
| | 412 | "Calling Stale Broker", |
|---|
| | 413 | rr.callRemote, "hang")) |
|---|
| | 414 | return d |
|---|
| | 415 | |
|---|
| | 416 | def test_connection_done_is_deadref(self): |
|---|
| | 417 | rr, target = self.setupTarget(HelperTarget()) |
|---|
| | 418 | d = rr.callRemote("hang") |
|---|
| | 419 | rr.tracker.broker.transport.loseConnection(Failure(CONNECTION_DONE)) |
|---|
| | 420 | d.addCallbacks(lambda res: self.fail("should have failed"), |
|---|
| | 421 | lambda why: why.trap(DeadReferenceError) and None) |
|---|
| | 422 | return d |
|---|
| | 423 | |
|---|
| 412 | 424 | def disconnected(self, *args, **kwargs): |
|---|
| 413 | 425 | self.lost = 1 |
|---|
| r446 |
r469 |
|
| 3 | 3 | from twisted.trial import unittest |
|---|
| 4 | 4 | from twisted.internet import defer, protocol, reactor |
|---|
| 5 | | from twisted.internet.error import ConnectionDone, ConnectionLost, \ |
|---|
| 6 | | ConnectionRefusedError |
|---|
| | 5 | from twisted.internet.error import ConnectionRefusedError |
|---|
| 7 | 6 | from twisted.python import failure |
|---|
| 8 | 7 | from foolscap import RemoteInterface, Referenceable |
|---|
| … | … | |
| 12 | 11 | from foolscap.eventual import flushEventualQueue |
|---|
| 13 | 12 | from foolscap.tokens import BananaError, NegotiationError |
|---|
| 14 | | |
|---|
| 15 | | def ignoreConnectionDone(f): |
|---|
| 16 | | f.trap(ConnectionDone, ConnectionLost) |
|---|
| 17 | | return None |
|---|
| 18 | 13 | |
|---|
| 19 | 14 | class RIConstrainedHelper(RemoteInterface): |
|---|
| … | … | |
| 161 | 156 | if self.debug: print "Alice introduces Carol to Bob" |
|---|
| 162 | 157 | # send the gift. This might not get acked by the time the test is |
|---|
| 163 | | # done and everything is torn down, so explicitly silence any |
|---|
| 164 | | # ConnectionDone error that might result. When we get |
|---|
| 165 | | # callRemoteOnly(), use that instead. |
|---|
| 166 | | d3 = self.abob.callRemote("set", obj=(self.alice, self.acarol)) |
|---|
| 167 | | d3.addErrback(ignoreConnectionDone) |
|---|
| | 158 | # done and everything is torn down, so we use callRemoteOnly |
|---|
| | 159 | self.abob.callRemoteOnly("set", obj=(self.alice, self.acarol)) |
|---|
| 168 | 160 | return d2 # this fires with the gift that bob got |
|---|
| 169 | 161 | d.addCallback(_introduce) |
|---|
| … | … | |
| 174 | 166 | d2 = self.carol.waitfor() |
|---|
| 175 | 167 | # handle ConnectionDone as described before |
|---|
| 176 | | d3 = self.bcarol.callRemote("set", obj=12) |
|---|
| 177 | | d3.addErrback(ignoreConnectionDone) |
|---|
| | 168 | self.bcarol.callRemoteOnly("set", obj=12) |
|---|
| 178 | 169 | return d2 |
|---|
| 179 | 170 | d.addCallback(_bobGotCarol) |
|---|
| … | … | |
| 212 | 203 | # ConnectionDone error that might result. When we get |
|---|
| 213 | 204 | # callRemoteOnly(), use that instead. |
|---|
| 214 | | d3 = self.abob.callRemote("set", obj=(self.alice, |
|---|
| 215 | | self.acarol, |
|---|
| 216 | | a_cindy)) |
|---|
| 217 | | d3.addErrback(ignoreConnectionDone) |
|---|
| | 205 | self.abob.callRemoteOnly("set", obj=(self.alice, |
|---|
| | 206 | self.acarol, |
|---|
| | 207 | a_cindy)) |
|---|
| 218 | 208 | return d2 # this fires with the gift that bob got |
|---|
| 219 | 209 | d.addCallback(_introduce) |
|---|
| … | … | |
| 230 | 220 | |
|---|
| 231 | 221 | # handle ConnectionDone as described before |
|---|
| 232 | | d4 = b_carol.callRemote("set", obj=4) |
|---|
| 233 | | d4.addErrback(ignoreConnectionDone) |
|---|
| 234 | | d5 = b_cindy.callRemote("set", obj=5) |
|---|
| 235 | | d5.addErrback(ignoreConnectionDone) |
|---|
| | 222 | b_carol.callRemoteOnly("set", obj=4) |
|---|
| | 223 | b_cindy.callRemoteOnly("set", obj=5) |
|---|
| 236 | 224 | return defer.DeferredList([d2,d3]) |
|---|
| 237 | 225 | d.addCallback(_bobGotCarol) |
|---|