-
Notifications
You must be signed in to change notification settings - Fork 41
Commit
…ynchronously Adds ability of channels to act synchronously on top of an asynchronous transport Adds ability to enforce a synchronous connection handshake on an asynchronous transport Catch `EINTR` in a read loop, raise all other read exceptions to fix #44 Immediately closes a connection and raises ConnectionClosed if there is a frame error when reading from the broker Immediately closes a connection and raises ConnectionClosed if user tries to send a frame that is larger than negotiated frame max Adds Connection.closed property
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
-r requirements.txt | ||
|
||
chai>=0.2.0 | ||
chai>=0.4.7 | ||
unittest2 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
#!/usr/bin/env python | ||
#-*- coding:utf-8 -*- | ||
|
||
import sys, os | ||
sys.path.append(os.path.abspath(".")) | ||
sys.path.append(os.path.abspath("..")) | ||
|
||
import logging | ||
import random | ||
import socket | ||
from optparse import OptionParser | ||
|
||
from haigha.connection import Connection | ||
from haigha.message import Message | ||
|
||
parser = OptionParser( | ||
usage='Usage: synchronous_test [options]' | ||
) | ||
parser.add_option('--user', default='guest', type='string') | ||
parser.add_option('--pass', default='guest', dest='password', type='string') | ||
parser.add_option('--vhost', default='/', type='string') | ||
parser.add_option('--host', default='localhost', type='string') | ||
parser.add_option('--debug', default=0, action='count') | ||
|
||
(options,args) = parser.parse_args() | ||
|
||
debug = options.debug | ||
level = logging.DEBUG if debug else logging.INFO | ||
|
||
# Setup logging | ||
logging.basicConfig(level=level, format="[%(levelname)s %(asctime)s] %(message)s" ) | ||
logger = logging.getLogger('haigha') | ||
|
||
sock_opts = { | ||
(socket.IPPROTO_TCP, socket.TCP_NODELAY) : 1, | ||
} | ||
connection = Connection(logger=logger, debug=debug, | ||
user=options.user, password=options.password, | ||
vhost=options.vhost, host=options.host, | ||
heartbeat=None, | ||
sock_opts=sock_opts, | ||
transport='gevent', | ||
synchronous=True) | ||
|
||
ch = connection.channel() | ||
ch.exchange.declare('foo', 'direct') | ||
ch.queue.declare('bar') | ||
ch.queue.bind('bar', 'foo', 'route') | ||
ch.basic.publish(Message('hello world'), 'foo', 'route') | ||
print 'GET:', ch.basic.get('bar') | ||
|
||
ch.basic.publish(Message('hello world'), 'foo', 'route') | ||
ch.basic.publish(Message('hello world'), 'foo', 'route') | ||
print 'PURGE:', ch.queue.purge('bar') | ||
|
||
ch.basic.publish(Message('hello world'), 'foo', 'route') | ||
ch.basic.publish(Message('hello world'), 'foo', 'route') | ||
print 'DELETED:', ch.queue.delete('bar') | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,7 +32,7 @@ class InvalidClass(ChannelError): '''The method frame referenced an invalid clas | |
class InvalidMethod(ChannelError): '''The method frame referenced an invalid method. Non-fatal.''' | ||
class Inactive(ChannelError): '''Tried to send a content frame while the channel was inactive. Non-fatal.''' | ||
|
||
def __init__(self, connection, channel_id, class_map): | ||
def __init__(self, connection, channel_id, class_map, **kwargs): | ||
''' | ||
Initialize with a handle to the connection and an id. Caller must | ||
supply a mapping of {class_id:ProtocolClass} which defines what | ||
|
@@ -72,6 +72,8 @@ def __init__(self, connection, channel_id, class_map): | |
} | ||
self._active = True | ||
|
||
self._synchronous = kwargs.get('synchronous', False) | ||
|
||
@property | ||
def connection(self): | ||
return self._connection | ||
|
@@ -103,6 +105,14 @@ def active(self): | |
''' | ||
return self._active | ||
|
||
@property | ||
def synchronous(self): | ||
''' | ||
Return if this channel is acting synchronous, of its own accord or because | ||
the connection is synchronous. | ||
''' | ||
return self._synchronous or self._connection.synchronous | ||
|
||
def add_open_listener(self, listener): | ||
''' | ||
Add a listener for open events on this channel. The listener should be | ||
|
@@ -206,7 +216,10 @@ def process_frames(self): | |
self.dispatch( frame ) | ||
except ProtocolClass.FrameUnderflow: | ||
return | ||
except Exception: | ||
except (ConnectionClosed,ChannelClosed): | ||
# Immediately raise if connection or channel is closed | ||
raise | ||
except Exception as e: | ||
# Spec says that channel should be closed if there's a framing error. | ||
# Unsure if we can send close if the current exception is transport | ||
# level (e.g. gevent.GreenletExit) | ||
|
@@ -263,7 +276,7 @@ def add_synchronous_cb(self, cb): | |
''' | ||
Add an expectation of a callback to release a synchronous transaction. | ||
''' | ||
if self.connection.synchronous: | ||
if self.connection.synchronous or self._synchronous: | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
awestendorf
Author
Member
|
||
wrapper = SyncWrapper(cb) | ||
self._pending_events.append( wrapper ) | ||
while wrapper._read: | ||
|
@awestendorf
Can the new
synchronous(self)
property-getter be used here instead of a copy of the logic?