Skip to content

Commit 7314d90

Browse files
committed
Making use of service resource over the low level client for SQS.
References jegesh#39. Signed-off-by: hjpotter92 <[email protected]>
1 parent 295dfc2 commit 7314d90

File tree

3 files changed

+100
-135
lines changed

3 files changed

+100
-135
lines changed

README.rst

+6-6
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ Here is a basic code sample:
3131

3232
**Standard Listener**
3333

34-
::
34+
.. code:: python
3535
3636
from sqs_listener import SqsListener
3737
@@ -44,7 +44,7 @@ Here is a basic code sample:
4444
4545
**Error Listener**
4646

47-
::
47+
.. code:: python
4848
4949
from sqs_listener import SqsListener
5050
class MyErrorListener(SqsListener):
@@ -75,7 +75,7 @@ Running as a Daemon
7575
7676
| Typically, in a production environment, you'll want to listen to an SQS queue with a daemonized process.
7777
The simplest way to do this is by running the listener in a detached process. On a typical Linux distribution it might look like this:
78-
|
78+
|
7979
``nohup python my_listener.py > listener.log &``
8080
| And saving the resulting process id for later (for stopping the listener via the ``kill`` command).
8181
|
@@ -94,7 +94,7 @@ Logging
9494
|
9595
| For instance:
9696
97-
::
97+
.. code:: python
9898
9999
logger = logging.getLogger('sqs_listener')
100100
logger.setLevel(logging.INFO)
@@ -111,7 +111,7 @@ Logging
111111
|
112112
| Or to a log file:
113113
114-
::
114+
.. code:: python
115115
116116
logger = logging.getLogger('sqs_listener')
117117
logger.setLevel(logging.INFO)
@@ -140,7 +140,7 @@ Sending messages
140140
141141
**Launcher Example**
142142
143-
::
143+
.. code:: python
144144
145145
from sqs_launcher import SqsLauncher
146146

sqs_launcher/__init__.py

+14-11
Original file line numberDiff line numberDiff line change
@@ -45,32 +45,36 @@ def __init__(self, queue=None, queue_url=None, create_queue=False, visibility_ti
4545
raise EnvironmentError('Environment variable `AWS_ACCOUNT_ID` not set and no role found.')
4646
# new session for each instantiation
4747
self._session = boto3.session.Session()
48+
self._resource = self._session.resource('sqs')
4849
self._client = self._session.client('sqs')
4950

5051
self._queue_name = queue
5152
self._queue_url = queue_url
53+
self._queue = None
5254
if not queue_url:
53-
queues = self._client.list_queues(QueueNamePrefix=self._queue_name)
55+
queues = self._resource.queues.filter(QueueNamePrefix=self._queue_name)
5456
exists = False
55-
for q in queues.get('QueueUrls', []):
56-
qname = q.split('/')[-1]
57+
for q in queues:
58+
qname = q.url.split('/')[-1]
5759
if qname == self._queue_name:
5860
exists = True
59-
self._queue_url = q
60-
61+
self._queue_url = q.url
62+
self._queue = q
6163
if not exists:
6264
if create_queue:
63-
q = self._client.create_queue(
65+
q = self._resource.create_queue(
6466
QueueName=self._queue_name,
6567
Attributes={
66-
'VisibilityTimeout': visibility_timeout # 10 minutes
67-
}
68+
'VisibilityTimeout': visibility_timeout, # 10 minutes
69+
},
6870
)
69-
self._queue_url = q['QueueUrl']
71+
self._queue_url = q.url
72+
self._queue = q
7073
else:
7174
raise ValueError('No queue found with name ' + self._queue_name)
7275
else:
7376
self._queue_name = self._get_queue_name_from_url(queue_url)
77+
self._queue = self._resource.Queue(queue_url)
7478

7579
def launch_message(self, message, **kwargs):
7680
"""
@@ -81,8 +85,7 @@ def launch_message(self, message, **kwargs):
8185
:return: (dict) the message response from SQS
8286
"""
8387
sqs_logger.info("Sending message to queue " + self._queue_name)
84-
return self._client.send_message(
85-
QueueUrl=self._queue_url,
88+
return self._queue.send_message(
8689
MessageBody=json.dumps(message),
8790
**kwargs,
8891
)

sqs_listener/__init__.py

+80-118
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
sqs_logger = logging.getLogger('sqs_listener')
2929

30+
3031
class SqsListener(object):
3132
__metaclass__ = ABCMeta
3233

@@ -38,16 +39,16 @@ def __init__(self, queue, **kwargs):
3839
aws_access_key = kwargs.get('aws_access_key', '')
3940
aws_secret_key = kwargs.get('aws_secret_key', '')
4041

41-
if len(aws_access_key) != 0 and len(aws_secret_key) != 0:
42+
if all(aws_access_key, aws_secret_key):
4243
boto3_session = boto3.Session(
4344
aws_access_key_id=aws_access_key,
44-
aws_secret_access_key=aws_secret_key
45+
aws_secret_access_key=aws_secret_key,
4546
)
4647
else:
4748
if (not os.environ.get('AWS_ACCOUNT_ID', None) and
4849
not ('iam-role' == boto3.Session().get_credentials().method)):
4950
raise EnvironmentError('Environment variable `AWS_ACCOUNT_ID` not set and no role found.')
50-
51+
self._queue = None # The SQS Queue resource
5152
self._queue_name = queue
5253
self._poll_interval = kwargs.get("interval", 60)
5354
self._queue_visibility_timeout = kwargs.get('visibility_timeout', '600')
@@ -67,135 +68,109 @@ def __init__(self, queue, **kwargs):
6768
else:
6869
self._session = boto3.session.Session()
6970
self._region_name = kwargs.get('region_name', self._session.region_name)
70-
self._client = self._initialize_client()
71-
71+
self._resource = self._initialize_resource()
7272

73-
def _initialize_client(self):
73+
def _initialize_resource(self):
7474
# new session for each instantiation
7575
ssl = True
7676
if self._region_name == 'elasticmq':
7777
ssl = False
7878

79-
sqs = self._session.client('sqs', region_name=self._region_name, endpoint_url=self._endpoint_name, use_ssl=ssl)
80-
queues = sqs.list_queues(QueueNamePrefix=self._queue_name)
81-
mainQueueExists = False
82-
errorQueueExists = False
83-
if 'QueueUrls' in queues:
84-
for q in queues['QueueUrls']:
85-
qname = q.split('/')[-1]
86-
if qname == self._queue_name:
87-
mainQueueExists = True
88-
if self._error_queue_name and qname == self._error_queue_name:
89-
errorQueueExists = True
90-
91-
92-
# create queue if necessary.
79+
sqs = self._session.resource('sqs', region_name=self._region_name, endpoint_url=self._endpoint_name, use_ssl=ssl)
80+
queues = sqs.queues.filter(QueueNamePrefix=self._queue_name)
81+
main_queue_exists = False
82+
error_queue_exists = False
83+
for q in queues:
84+
qname = q.url.split('/')[-1]
85+
if qname == self._queue_name:
86+
main_queue_exists = True
87+
if self._error_queue_name and qname == self._error_queue_name:
88+
error_queue_exists = True
89+
90+
# create queue if necessary.
9391
# creation is idempotent, no harm in calling on a queue if it already exists.
9492
if self._queue_url is None:
95-
if not mainQueueExists:
93+
if not main_queue_exists:
9694
sqs_logger.warning("main queue not found, creating now")
97-
95+
queue_attributes = {
96+
'VisibilityTimeout': self._queue_visibility_timeout, # 10 minutes
97+
}
9898
# is this a fifo queue?
9999
if self._queue_name.endswith(".fifo"):
100-
fifoQueue="true"
101-
q = sqs.create_queue(
102-
QueueName=self._queue_name,
103-
Attributes={
104-
'VisibilityTimeout': self._queue_visibility_timeout, # 10 minutes
105-
'FifoQueue':fifoQueue
106-
}
107-
)
108-
else:
109-
# need to avoid FifoQueue property for normal non-fifo queues
110-
q = sqs.create_queue(
111-
QueueName=self._queue_name,
112-
Attributes={
113-
'VisibilityTimeout': self._queue_visibility_timeout, # 10 minutes
114-
}
115-
)
116-
self._queue_url = q['QueueUrl']
117-
118-
if self._error_queue_name and not errorQueueExists:
100+
queue_attributes["FifoQueue"] = "true"
101+
q = sqs.create_queue(
102+
QueueName=self._queue_name,
103+
Attributes=queue_attributes,
104+
)
105+
self._queue_url = q.url
106+
107+
if self._error_queue_name and not error_queue_exists:
119108
sqs_logger.warning("error queue not found, creating now")
120109
q = sqs.create_queue(
121110
QueueName=self._error_queue_name,
122111
Attributes={
123-
'VisibilityTimeout': self._queue_visibility_timeout # 10 minutes
124-
}
112+
'VisibilityTimeout': self._queue_visibility_timeout, # 10 minutes
113+
},
125114
)
126115

127116
if self._queue_url is None:
128-
if os.environ.get('AWS_ACCOUNT_ID', None):
129-
qs = sqs.get_queue_url(QueueName=self._queue_name,
130-
QueueOwnerAWSAccountId=os.environ.get('AWS_ACCOUNT_ID', None))
131-
else:
132-
qs = sqs.get_queue_url(QueueName=self._queue_name)
133-
self._queue_url = qs['QueueUrl']
117+
qs = sqs.get_queue_by_name(
118+
QueueName=self._queue_name,
119+
QueueOwnerAWSAccountId=os.environ.get('AWS_ACCOUNT_ID', None),
120+
)
121+
self._queue_url = qs.url
122+
self._queue = sqs.Queue(self._queue_url)
134123
return sqs
135124

136125
def _start_listening(self):
137-
# TODO consider incorporating output processing from here: https://github.com/debrouwere/sqs-antenna/blob/master/antenna/__init__.py
126+
# TODO consider incorporating output processing from here:
127+
# https://github.com/debrouwere/sqs-antenna/blob/master/antenna/__init__.py
138128
while True:
139-
# calling with WaitTimeSecconds of zero show the same behavior as
129+
# calling with `WaitTimeSecconds` of zero show the same behavior as
140130
# not specifiying a wait time, ie: short polling
141-
messages = self._client.receive_message(
142-
QueueUrl=self._queue_url,
143-
MessageAttributeNames=self._message_attribute_names,
131+
messages = self._queue.receive_message(
144132
AttributeNames=self._attribute_names,
133+
MessageAttributeNames=self._message_attribute_names,
145134
WaitTimeSeconds=self._wait_time,
146-
MaxNumberOfMessages=self._max_number_of_messages
135+
MaxNumberOfMessages=self._max_number_of_messages,
147136
)
148-
if 'Messages' in messages:
149-
150-
sqs_logger.debug(messages)
151-
continue
152-
sqs_logger.info("{} messages received".format(len(messages['Messages'])))
153-
for m in messages['Messages']:
154-
receipt_handle = m['ReceiptHandle']
155-
m_body = m['Body']
156-
message_attribs = None
157-
attribs = None
158-
159-
# catch problems with malformed JSON, usually a result of someone writing poor JSON directly in the AWS console
160-
try:
161-
params_dict = json.loads(m_body)
162-
except:
163-
sqs_logger.warning("Unable to parse message - JSON is not formatted properly")
164-
continue
165-
if 'MessageAttributes' in m:
166-
message_attribs = m['MessageAttributes']
167-
if 'Attributes' in m:
168-
attribs = m['Attributes']
169-
try:
170-
if self._force_delete:
171-
self._client.delete_message(
172-
QueueUrl=self._queue_url,
173-
ReceiptHandle=receipt_handle
174-
)
175-
self.handle_message(params_dict, message_attribs, attribs)
176-
else:
177-
self.handle_message(params_dict, message_attribs, attribs)
178-
self._client.delete_message(
179-
QueueUrl=self._queue_url,
180-
ReceiptHandle=receipt_handle
181-
)
182-
except Exception as ex:
183-
# need exception logtype to log stack trace
184-
sqs_logger.exception(ex)
185-
if self._error_queue_name:
186-
exc_type, exc_obj, exc_tb = sys.exc_info()
187-
188-
sqs_logger.info( "Pushing exception to error queue")
189-
error_launcher = SqsLauncher(queue=self._error_queue_name, create_queue=True)
190-
error_launcher.launch_message(
191-
{
192-
'exception_type': str(exc_type),
193-
'error_message': str(ex.args)
194-
}
195-
)
196-
197-
else:
137+
if not messages:
198138
time.sleep(self._poll_interval)
139+
continue
140+
sqs_logger.debug(messages)
141+
sqs_logger.info("{} messages received".format(len(messages['Messages'])))
142+
for m in messages:
143+
receipt_handle = m.receipt_handle
144+
m_body = m.body
145+
message_attribs = m.message_attributes
146+
attribs = m.attributes
147+
# catch problems with malformed JSON, usually a result of someone writing poor JSON directly in the AWS console
148+
try:
149+
params_dict = json.loads(m_body)
150+
except:
151+
sqs_logger.warning("Unable to parse message - JSON is not formatted properly")
152+
continue
153+
try:
154+
if self._force_delete:
155+
m.delete()
156+
self.handle_message(params_dict, message_attribs, attribs)
157+
else:
158+
self.handle_message(params_dict, message_attribs, attribs)
159+
m.delete()
160+
except Exception as ex:
161+
# need exception logtype to log stack trace
162+
sqs_logger.exception(ex)
163+
if self._error_queue_name:
164+
exc_type, exc_obj, exc_tb = sys.exc_info()
165+
166+
sqs_logger.info( "Pushing exception to error queue")
167+
error_launcher = SqsLauncher(queue=self._error_queue_name, create_queue=True)
168+
error_launcher.launch_message(
169+
{
170+
'exception_type': str(exc_type),
171+
'error_message': str(ex.args)
172+
}
173+
)
199174

200175
def listen(self):
201176
sqs_logger.info( "Listening to queue " + self._queue_name)
@@ -204,19 +179,6 @@ def listen(self):
204179

205180
self._start_listening()
206181

207-
def _prepare_logger(self):
208-
logger = logging.getLogger('eg_daemon')
209-
logger.setLevel(logging.INFO)
210-
211-
sh = logging.StreamHandler(sys.stdout)
212-
sh.setLevel(logging.INFO)
213-
214-
formatstr = '[%(asctime)s - %(name)s - %(levelname)s] %(message)s'
215-
formatter = logging.Formatter(formatstr)
216-
217-
sh.setFormatter(formatter)
218-
logger.addHandler(sh)
219-
220182
@abstractmethod
221183
def handle_message(self, body, attributes, messages_attributes):
222184
"""

0 commit comments

Comments
 (0)