def test_independent(self): """Test that waiters are independent.""" result = [] def bad(res): result.append(res) raise Exception() def fired(res): result.append(res) e = TwistedEvent() d = e.wait().addCallback(bad) e.wait().addCallback(fired) d.addErrback(lambda _ : None) e.fire() self.assertEqual(result, [True, True])
def test_fail(self): """Test event failure.""" result = [] def failed(res): result.append(res) e = TwistedEvent() e.wait().addErrback(failed) self.assertEqual(result, []) f = Failure(Exception()) e.fail(f) self.assertEqual(result, [f]) self.assertRaises(AlreadyFiredError, e.fire) self.assertRaises(AlreadyFiredError, e.fail, f) e.wait().addErrback(failed) self.assertEqual(result, [f, f]) e.fail_if_not_fired(None) e = TwistedEvent() e.wait().addErrback(failed) e.fail_if_not_fired(f) self.assertEqual(result, [f, f, f])
def test_fire(self): """Test event success.""" result = [] def fired(res): result.append(res) e = TwistedEvent() e.wait().addCallback(fired) self.assertEqual(result, []) e.fire() self.assertEqual(result, [True]) self.assertRaises(AlreadyFiredError, e.fire) self.assertRaises(AlreadyFiredError, e.fail, None) e.wait().addCallback(fired) self.assertEqual(result, [True, True]) e.fail_if_not_fired(None)
class AMQClient(FrameReceiver): channelClass = AMQChannel # Max unreceived heartbeat frames. The AMQP standard says it's 3. MAX_UNSEEN_HEARTBEAT = 3 def __init__(self, delegate, vhost, spec, heartbeat=0, clock=None, insist=False): FrameReceiver.__init__(self, spec) self.delegate = delegate # XXX Cyclic dependency self.delegate.client = self self.vhost = vhost self.channelFactory = type("Channel%s" % self.spec.klass.__name__, (self.channelClass, self.spec.klass), {}) self.channels = {} self.outgoing = defer.DeferredQueue() self.work = defer.DeferredQueue() self.started = TwistedEvent() self.queueLock = defer.DeferredLock() self.basic_return_queue = TimeoutDeferredQueue() self.queues = {} self.outgoing.get().addCallback(self.writer) self.work.get().addCallback(self.worker) self.heartbeatInterval = heartbeat self.insist = insist if self.heartbeatInterval > 0: if clock is None: from twisted.internet import reactor as clock self.clock = clock self.checkHB = self.clock.callLater(self.heartbeatInterval * self.MAX_UNSEEN_HEARTBEAT, self.checkHeartbeat) self.sendHB = LoopingCall(self.sendHeartbeat) d = self.started.wait() d.addCallback(lambda _: self.reschedule_sendHB()) d.addCallback(lambda _: self.reschedule_checkHB()) def reschedule_sendHB(self): if self.heartbeatInterval > 0: if self.sendHB.running: self.sendHB.stop() self.sendHB.start(self.heartbeatInterval, now=False) def reschedule_checkHB(self): if self.checkHB.active(): self.checkHB.cancel() self.checkHB = self.clock.callLater(self.heartbeatInterval * self.MAX_UNSEEN_HEARTBEAT, self.checkHeartbeat) def check_0_8(self): return (self.spec.minor, self.spec.major) == (0, 8) def channel(self, id): if id not in self.channels: self.channels[id] = self.channelFactory(id, self.outgoing, self) return defer.succeed(self.channels[id]) @defer.inlineCallbacks def queue(self, key): yield self.queueLock.acquire() try: try: q = self.queues[key] except KeyError: q = TimeoutDeferredQueue() self.queues[key] = q finally: self.queueLock.release() defer.returnValue(q) def close(self, reason): for ch in self.channels.values(): ch.close(reason) for q in self.queues.values(): q.close() self.delegate.close(reason) def writer(self, frame): self.sendFrame(frame) self.outgoing.get().addCallback(self.writer) def worker(self, queue): d = self.dispatch(queue) def cb(ign): self.work.get().addCallback(self.worker) d.addCallback(cb) d.addErrback(self.close) @defer.inlineCallbacks def dispatch(self, queue): frame = yield queue.get() channel = yield self.channel(frame.channel) payload = frame.payload if payload.method.content: content = yield readContent(queue) else: content = None # Let the caller deal with exceptions thrown here. message = Message(payload.method, payload.args, content) self.delegate.dispatch(channel, message) # As soon as we connect to the target AMQP broker, send the init string def connectionMade(self): self.sendInitString() self.setFrameMode() def frameReceived(self, frame): self.processFrame(frame) def sendFrame(self, frame): if frame.payload.type != Frame.HEARTBEAT: self.reschedule_sendHB() FrameReceiver.sendFrame(self, frame) @defer.inlineCallbacks def processFrame(self, frame): ch = yield self.channel(frame.channel) if frame.payload.type == Frame.HEARTBEAT: self.lastHBReceived = time() else: ch.dispatch(frame, self.work) if self.heartbeatInterval > 0: self.reschedule_checkHB() @defer.inlineCallbacks def authenticate(self, username, password, mechanism='AMQPLAIN', locale='en_US'): if mechanism == 'AMQPLAIN': response = {"LOGIN": username, "PASSWORD": password} elif mechanism == 'PLAIN': response = "\0" + username + "\0" + password else: raise ValueError('Unknown mechanism:'+mechanism) yield self.start(response, mechanism, locale) @defer.inlineCallbacks def start(self, response, mechanism='AMQPLAIN', locale='en_US'): self.response = response self.mechanism = mechanism self.locale = locale yield self.started.wait() channel0 = yield self.channel(0) if self.check_0_8(): result = yield channel0.connection_open(self.vhost, insist=self.insist) else: result = yield channel0.connection_open(self.vhost) defer.returnValue(result) def sendHeartbeat(self): self.sendFrame(Frame(0, Heartbeat())) self.lastHBSent = time() def checkHeartbeat(self): if self.checkHB.active(): self.checkHB.cancel() self.transport.loseConnection() def connectionLost(self, reason): if self.heartbeatInterval > 0: if self.sendHB.running: self.sendHB.stop() if self.checkHB.active(): self.checkHB.cancel() self.close(reason) def channelFailed(self, channel, reason): """Unexpected channel close""" pass
class AMQClient(FrameReceiver): channelClass = AMQChannel # Max unreceived heartbeat frames. The AMQP standard says it's 3. MAX_UNSEEN_HEARTBEAT = 3 def __init__(self, delegate, vhost, spec, heartbeat=0, clock=None, insist=False): FrameReceiver.__init__(self, spec) self.delegate = delegate # XXX Cyclic dependency self.delegate.client = self self.vhost = vhost self.channelFactory = type("Channel%s" % self.spec.klass.__name__, (self.channelClass, self.spec.klass), {}) self.channels = {} self.channelLock = defer.DeferredLock() self.outgoing = defer.DeferredQueue() self.work = defer.DeferredQueue() self.started = TwistedEvent() self.disconnected = TwistedEvent() # Fired upon connection shutdown self.closed = False self.queueLock = defer.DeferredLock() self.basic_return_queue = TimeoutDeferredQueue() self.queues = {} self.outgoing.get().addCallback(self.writer) self.work.get().addCallback(self.worker) self.heartbeatInterval = heartbeat self.insist = insist if clock is None: from twisted.internet import reactor clock = reactor self.clock = clock if self.heartbeatInterval > 0: self.checkHB = self.clock.callLater( self.heartbeatInterval * self.MAX_UNSEEN_HEARTBEAT, self.check_heartbeat) self.sendHB = LoopingCall(self.send_heartbeat) self.sendHB.clock = self.clock d = self.started.wait() d.addCallback(lambda _: self.reschedule_send_heartbeat()) d.addCallback(lambda _: self.reschedule_check_heartbeat()) # If self.started fails, don't start the heartbeat. d.addErrback(lambda _: None) def reschedule_send_heartbeat(self): if self.heartbeatInterval > 0: if self.sendHB.running: self.sendHB.stop() self.sendHB.start(self.heartbeatInterval, now=False) def reschedule_check_heartbeat(self): if self.checkHB.active(): self.checkHB.cancel() self.checkHB = self.clock.callLater( self.heartbeatInterval * self.MAX_UNSEEN_HEARTBEAT, self.check_heartbeat) def check_0_8(self): return (self.spec.minor, self.spec.major) == (0, 8) @defer.inlineCallbacks def channel(self, id): yield self.channelLock.acquire() try: try: ch = self.channels[id] except KeyError: ch = self.channelFactory(id, self.outgoing, self) self.channels[id] = ch finally: self.channelLock.release() defer.returnValue(ch) @defer.inlineCallbacks def queue(self, key): yield self.queueLock.acquire() try: try: q = self.queues[key] except KeyError: q = TimeoutDeferredQueue(clock=self.clock) self.queues[key] = q finally: self.queueLock.release() defer.returnValue(q) @defer.inlineCallbacks def close(self, reason=None, within=0): """Explicitely close the connection. @param reason: Optional closing reason. If not given, ConnectionDone will be used. @param within: Shutdown the client within this amount of seconds. If zero (the default), all channels and queues will be closed immediately. If greater than 0, try to close the AMQP connection cleanly, by sending a "close" method and waiting for "close-ok". If no reply is received within the given amount of seconds, the transport will be forcely shutdown. """ if self.closed: return if reason is None: reason = ConnectionDone() if within > 0: channel0 = yield self.channel(0) deferred = channel0.connection_close() call = self.clock.callLater(within, deferred.cancel) try: yield deferred except defer.CancelledError: pass else: call.cancel() self.do_close(reason) def do_close(self, reason): """Called when connection_close() is received""" # Let's close all channels and queues, since we don't want to write # any more data and no further read will happen. for ch in self.channels.values(): ch.close(reason) for q in self.queues.values(): q.close() self.delegate.close(reason) def writer(self, frame): self.send_frame(frame) self.outgoing.get().addCallback(self.writer) def worker(self, queue): d = self.dispatch(queue) def cb(ign): self.work.get().addCallback(self.worker) d.addCallback(cb) d.addErrback(self.close) @defer.inlineCallbacks def dispatch(self, queue): frame = yield queue.get() channel = yield self.channel(frame.channel) payload = frame.payload if payload.method.content: content = yield read_content(queue) else: content = None # Let the caller deal with exceptions thrown here. message = Message(payload.method, payload.args, content) self.delegate.dispatch(channel, message) # As soon as we connect to the target AMQP broker, send the init string def connectionMade(self): self.send_init_string() self.set_frame_mode() def frame_received(self, frame): self.process_frame(frame) def send_frame(self, frame): if frame.payload.type != Frame.HEARTBEAT: self.reschedule_send_heartbeat() FrameReceiver.send_frame(self, frame) @defer.inlineCallbacks def process_frame(self, frame): ch = yield self.channel(frame.channel) if frame.payload.type == Frame.HEARTBEAT: self.last_heartbeat_received = time() else: ch.dispatch(frame, self.work) if self.heartbeatInterval > 0 and not self.closed: self.reschedule_check_heartbeat() @defer.inlineCallbacks def authenticate(self, username, password, mechanism='AMQPLAIN', locale='en_US'): if mechanism == 'AMQPLAIN': response = {"LOGIN": username, "PASSWORD": password} elif mechanism == 'PLAIN': response = "\0" + username + "\0" + password else: raise ValueError('Unknown mechanism:' + mechanism) yield self.start(response, mechanism, locale) @defer.inlineCallbacks def start(self, response, mechanism='AMQPLAIN', locale='en_US'): self.response = response self.mechanism = mechanism self.locale = locale yield self.started.wait() channel0 = yield self.channel(0) if self.check_0_8(): result = yield channel0.connection_open(self.vhost, insist=self.insist) else: result = yield channel0.connection_open(self.vhost) defer.returnValue(result) def send_heartbeat(self): self.send_frame(Frame(0, Heartbeat())) self.last_heartbeat_sent = time() def check_heartbeat(self): if self.checkHB.active(): self.checkHB.cancel() # Abort the connection, since the other pear is unresponsive and # there's no point in shutting down cleanly and trying to flush # pending data. self.transport.abortConnection() def connectionLost(self, reason): if self.heartbeatInterval > 0: if self.sendHB.running: self.sendHB.stop() if self.checkHB.active(): self.checkHB.cancel() self.close(reason) self.disconnected.fire() def channel_failed(self, channel, reason): """Unexpected channel close""" pass
class AMQClient(FrameReceiver): channelClass = AMQChannel # Max unreceived heartbeat frames. The AMQP standard says it's 3. MAX_UNSEEN_HEARTBEAT = 3 def __init__(self, delegate, vhost, spec, heartbeat=0, clock=None, insist=False): FrameReceiver.__init__(self, spec) self.delegate = delegate # XXX Cyclic dependency self.delegate.client = self self.vhost = vhost self.channelFactory = type("Channel%s" % self.spec.klass.__name__, (self.channelClass, self.spec.klass), {}) self.channels = {} self.channelLock = defer.DeferredLock() self.outgoing = defer.DeferredQueue() self.work = defer.DeferredQueue() self.started = TwistedEvent() self.queueLock = defer.DeferredLock() self.basic_return_queue = TimeoutDeferredQueue() self.queues = {} self.outgoing.get().addCallback(self.writer) self.work.get().addCallback(self.worker) self.heartbeatInterval = heartbeat self.insist = insist if self.heartbeatInterval > 0: if clock is None: from twisted.internet import reactor as clock self.clock = clock self.checkHB = self.clock.callLater( self.heartbeatInterval * self.MAX_UNSEEN_HEARTBEAT, self.checkHeartbeat) self.sendHB = LoopingCall(self.sendHeartbeat) d = self.started.wait() d.addCallback(lambda _: self.reschedule_sendHB()) d.addCallback(lambda _: self.reschedule_checkHB()) def reschedule_sendHB(self): if self.heartbeatInterval > 0: if self.sendHB.running: self.sendHB.stop() self.sendHB.start(self.heartbeatInterval, now=False) def reschedule_checkHB(self): if self.checkHB.active(): self.checkHB.cancel() self.checkHB = self.clock.callLater( self.heartbeatInterval * self.MAX_UNSEEN_HEARTBEAT, self.checkHeartbeat) def check_0_8(self): return (self.spec.minor, self.spec.major) == (0, 8) @defer.inlineCallbacks def channel(self, id): yield self.channelLock.acquire() try: try: ch = self.channels[id] except KeyError: ch = self.channelFactory(id, self.outgoing) self.channels[id] = ch finally: self.channelLock.release() defer.returnValue(ch) @defer.inlineCallbacks def queue(self, key): yield self.queueLock.acquire() try: try: q = self.queues[key] except KeyError: q = TimeoutDeferredQueue() self.queues[key] = q finally: self.queueLock.release() defer.returnValue(q) def close(self, reason): for ch in self.channels.values(): ch.close(reason) for q in self.queues.values(): q.close() self.delegate.close(reason) def writer(self, frame): self.sendFrame(frame) self.outgoing.get().addCallback(self.writer) def worker(self, queue): d = self.dispatch(queue) def cb(ign): self.work.get().addCallback(self.worker) d.addCallback(cb) d.addErrback(self.close) @defer.inlineCallbacks def dispatch(self, queue): frame = yield queue.get() channel = yield self.channel(frame.channel) payload = frame.payload if payload.method.content: content = yield readContent(queue) else: content = None # Let the caller deal with exceptions thrown here. message = Message(payload.method, payload.args, content) self.delegate.dispatch(channel, message) # As soon as we connect to the target AMQP broker, send the init string def connectionMade(self): self.sendInitString() self.setFrameMode() def frameReceived(self, frame): self.processFrame(frame) def sendFrame(self, frame): if frame.payload.type != Frame.HEARTBEAT: self.reschedule_sendHB() FrameReceiver.sendFrame(self, frame) @defer.inlineCallbacks def processFrame(self, frame): ch = yield self.channel(frame.channel) if frame.payload.type == Frame.HEARTBEAT: self.lastHBReceived = time() else: ch.dispatch(frame, self.work) if self.heartbeatInterval > 0: self.reschedule_checkHB() @defer.inlineCallbacks def authenticate(self, username, password, mechanism='AMQPLAIN', locale='en_US'): if self.check_0_8(): response = {"LOGIN": username, "PASSWORD": password} else: response = "\0" + username + "\0" + password yield self.start(response, mechanism, locale) @defer.inlineCallbacks def start(self, response, mechanism='AMQPLAIN', locale='en_US'): self.response = response self.mechanism = mechanism self.locale = locale yield self.started.wait() channel0 = yield self.channel(0) if self.check_0_8(): result = yield channel0.connection_open(self.vhost, insist=self.insist) else: result = yield channel0.connection_open(self.vhost) defer.returnValue(result) def sendHeartbeat(self): self.sendFrame(Frame(0, Heartbeat())) self.lastHBSent = time() def checkHeartbeat(self): if self.checkHB.active(): self.checkHB.cancel() self.transport.loseConnection() def connectionLost(self, reason): if self.heartbeatInterval > 0: if self.sendHB.running: self.sendHB.stop() if self.checkHB.active(): self.checkHB.cancel() self.close(reason)
class AMQClient(FrameReceiver): channelClass = AMQChannel # Max unreceived heartbeat frames. The AMQP standard says it's 3. MAX_UNSEEN_HEARTBEAT = 3 def __init__(self, delegate, vhost, spec, heartbeat=0, clock=None, insist=None): FrameReceiver.__init__(self, spec) self.delegate = delegate # XXX Cyclic dependency self.delegate.client = self self.vhost = vhost self.channelFactory = type("Channel%s" % self.spec.klass.__name__, (self.channelClass, self.spec.klass), {}) self.channels = {} self.channelLock = defer.DeferredLock() self.outgoing = defer.DeferredQueue() self.work = defer.DeferredQueue() self.started = TwistedEvent() self.queueLock = defer.DeferredLock() self.basic_return_queue = TimeoutDeferredQueue() self.queues = {} self.outgoing.get().addCallback(self.writer) self.work.get().addCallback(self.worker) self.heartbeatInterval = heartbeat self.insist = insist self.checkHB = None if self.heartbeatInterval > 0: if clock is None: from twisted.internet import reactor as clock self.clock = clock self.sendHB = LoopingCall(self.sendHeartbeat) d = self.started.wait() d.addCallback(lambda _: self.reschedule_sendHB()) d.addCallback(lambda _: self.reschedule_checkHB()) def reschedule_sendHB(self): if self.heartbeatInterval > 0: if self.sendHB.running: self.sendHB.stop() self.sendHB.start(self.heartbeatInterval, now=False) def reschedule_checkHB(self): if self.checkHB.active(): self.checkHB.cancel() self.checkHB = self.clock.callLater(self.heartbeatInterval * self.MAX_UNSEEN_HEARTBEAT, self.checkHeartbeat) def _locked(self, lock, f, *args, **kwargs): def release(_): lock.release() return _ try: return lock.acquire().addCallback(lambda _: defer.maybeDeferred(f, *args, **kwargs)).addBoth(release) except: return defer.fail() def channel(self, id): return self._locked(self.channelLock, self._channel, id) def _channel(self, id): try: ch = self.channels[id] except KeyError: ch = self.channelFactory(id, self.outgoing) self.channels[id] = ch return ch def queue(self, key): return self._locked(self.queueLock, self._queue, key) def _queue(self, key): try: q = self.queues[key] except KeyError: q = TimeoutDeferredQueue() self.queues[key] = q return q def closeChannel(self, channel): return self._locked(self.channelLock, self._closeChannel, channel) def _closeChannel(self, channel): channel.close(None) del self.channels[channel.id] def closeQueue(self, key, queue): return self._locked(self.queueLock, self._closeQueue, key, queue) def _closeQueue(self, key, queue): queue.close() del self.queues[key] def close(self, reason): for ch in self.channels.values(): ch.close(reason) for q in self.queues.values(): q.close() self.delegate.close(reason) def writer(self, frame): self.sendFrame(frame) self.outgoing.get().addCallback(self.writer) def worker(self, queue): d = self.dispatch(queue) def cb(ign): self.work.get().addCallback(self.worker) d.addCallback(cb) d.addErrback(self.close) def dispatch(self, queue): def gotChannel(channel, frame): payload = frame.payload def decodeContent(): if payload.method.content: return readContent(queue) else: return None def dispatch(content): # Let the caller deal with exceptions thrown here. message = Message(payload.method, payload.args, content) self.delegate.dispatch(channel, message) return defer.maybeDeferred(decodeContent).addCallback(dispatch) return queue.get().addCallback(lambda frame: self.channel(frame.channel).addCallback(gotChannel, frame)) # As soon as we connect to the target AMQP broker, send the init string def connectionMade(self): self.checkHB = self.clock.callLater(self.heartbeatInterval * self.MAX_UNSEEN_HEARTBEAT, self.checkHeartbeat) self.sendInitString() self.setFrameMode() def frameReceived(self, frame): self.processFrame(frame) def sendFrame(self, frame): if frame.payload.type != Frame.HEARTBEAT: self.reschedule_sendHB() FrameReceiver.sendFrame(self, frame) def processFrame(self, frame): def gotChannel(ch): if frame.payload.type == Frame.HEARTBEAT: self.lastHBReceived = time() else: ch.dispatch(frame, self.work) if self.heartbeatInterval > 0: self.reschedule_checkHB() return self.channel(frame.channel).addCallback(gotChannel) def authenticate(self, username, password, mechanism='AMQPLAIN', locale='en_US'): response = {"LOGIN": username, "PASSWORD": password} return self.start(response, mechanism, locale) def start(self, response, mechanism='AMQPLAIN', locale='en_US'): self.response = response self.mechanism = mechanism self.locale = locale args = {} if self.insist is not None: args['insist'] = self.insist return self.started.wait().addCallback(lambda _: self.channel(0).addCallback(lambda channel0: channel0.connection_open(self.vhost, **args))) def sendHeartbeat(self): self.sendFrame(Frame(0, Heartbeat())) self.lastHBSent = time() def checkHeartbeat(self): if self.checkHB.active(): self.checkHB.cancel() if self.transport: self.transport.loseConnection() def connectionLost(self, reason): if self.heartbeatInterval > 0: if self.sendHB.running: self.sendHB.stop() if self.checkHB.active(): self.checkHB.cancel() self.close(reason)