Status: | Merged |
---|---|
Approved by: | Eric Day |
Approved revision: | 37 |
Merged at revision: | 37 |
Proposed branch: | lp:~eday/burrow/doc-updates |
Merge into: | lp:burrow |
Diff against target: |
523 lines (+183/-13) 4 files modified
burrow/backend/__init__.py (+145/-13) burrow/backend/http.py (+2/-0) burrow/backend/memory.py (+15/-0) burrow/backend/sqlite.py (+21/-0) |
To merge this branch: | bzr merge lp:~eday/burrow/doc-updates |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Burrow Core Team | Pending | ||
Review via email: mp+72754@code.launchpad.net |
Commit message
Description of the change
More docs for backend modules.
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'burrow/backend/__init__.py' |
2 | --- burrow/backend/__init__.py 2011-08-19 00:07:39 +0000 |
3 | +++ burrow/backend/__init__.py 2011-08-24 17:01:25 +0000 |
4 | @@ -20,6 +20,9 @@ |
5 | |
6 | import burrow.common |
7 | |
8 | +# Since this is an interface, arguments are unused. Ignore warnings in pylint. |
9 | +# pylint: disable=W0613 |
10 | + |
11 | |
12 | class Backend(burrow.common.Module): |
13 | '''Interface that backend modules must implement.''' |
14 | @@ -34,48 +37,165 @@ |
15 | thread_pool.spawn_n(self._clean) |
16 | |
17 | def delete_accounts(self, filters=None): |
18 | - '''Delete accounts, which includes all queues and messages |
19 | - for the accounts. With no filters, this will delete all data |
20 | - for the entire server, so it should be used with caution. |
21 | + '''Delete accounts, which includes all queues and messages within |
22 | + the accounts. With no filters, this will delete all data for the |
23 | + entire server, so it should be used with caution. |
24 | |
25 | :param filters: Optional dict of filters for the request. Valid |
26 | - filters are 'marker', 'limit', and 'detail'. Valid values |
27 | - for 'detail' are 'none', 'id', and 'all'. Default value for |
28 | - 'detail' is 'none'. |
29 | - :returns: Generator which will loop through all messages if |
30 | - 'detail' is not 'none'. |
31 | + filters are 'marker', 'limit', and 'detail'. The 'marker' |
32 | + value is the last seen account ID for use in pagination, |
33 | + and only accounts after this ID will be affected. If the |
34 | + 'marker' value is not given or not found, it will start from |
35 | + the beginning. The 'limit' value is the number of accounts |
36 | + to delete for the request. If 'limit' is not given, it will |
37 | + delete them all. Valid values for 'detail' are 'none', 'id', |
38 | + and 'all'. Default value for 'detail' is 'none'. |
39 | + |
40 | + :returns: Generator which will loop through all accounts if |
41 | + 'detail' is not 'none'. If 'detail' is 'none', the generator |
42 | + will stop on the first iteration, but it still must be called |
43 | + to finish the request. |
44 | ''' |
45 | return [] |
46 | |
47 | def get_accounts(self, filters=None): |
48 | + '''Get a list of accounts, possibly filtered. This uses the same |
49 | + parameters and return type as :func:`delete_accounts()`, except |
50 | + the default value for 'detail' in 'filters' is 'id'.''' |
51 | return [] |
52 | |
53 | def delete_queues(self, account, filters=None): |
54 | + '''Delete queues within an account, which includes all messages |
55 | + within the queues. With no filters, this will delete all data |
56 | + for the entire account, so it should be used with caution. |
57 | + |
58 | + :param account: Account to delete the queues from. |
59 | + |
60 | + :param filters: Optional dict of filters for the request. Valid |
61 | + filters are 'marker', 'limit', and 'detail'. The 'marker' |
62 | + value is the last seen queue ID for use in pagination, and |
63 | + only queues after this ID will be affected. If the 'marker' |
64 | + value is not given or not found, it will start from the |
65 | + beginning. The 'limit' value is the number of queues to |
66 | + delete for the request. If 'limit' is not given, it will |
67 | + delete them all. Valid values for 'detail' are 'none', 'id', |
68 | + and 'all'. Default value for 'detail' is 'none'. |
69 | + |
70 | + :returns: Generator which will loop through all queues if 'detail' |
71 | + is not 'none'. If 'detail' is 'none', the generator will stop |
72 | + on the first iteration, but it still must be called to finish |
73 | + the request. |
74 | + ''' |
75 | return [] |
76 | |
77 | def get_queues(self, account, filters=None): |
78 | + '''Get a list of queues, possibly filtered. This uses the same |
79 | + parameters and return type as :func:`delete_queues()`, except |
80 | + the default value for 'detail' in 'filters' is 'id'.''' |
81 | return [] |
82 | |
83 | def delete_messages(self, account, queue, filters=None): |
84 | + '''Delete messages within a queue. With no filters, this will |
85 | + delete all messages in the queue, so it should be used with |
86 | + caution. |
87 | + |
88 | + :param account: Account to delete the messages from. |
89 | + |
90 | + :param queue: Queue within the given account to delete the |
91 | + messages from. |
92 | + |
93 | + :param filters: Optional dict of filters for the request. Valid |
94 | + filters are 'marker', 'limit', 'match_hidden', 'wait', and |
95 | + 'detail'. The 'marker' value is the last seen message ID for |
96 | + use in pagination, and only messages after this ID will be |
97 | + affected. If the 'marker' value is not given or not found, |
98 | + it will start from the beginning. The 'limit' value is the |
99 | + number of messages to delete for the request. If 'limit' |
100 | + is not given, it will delete them all. If 'match_hidden' |
101 | + is True, the request will match all messages, even if their |
102 | + 'hide' value is non-zero, otherwise messages with a 'hide' |
103 | + value of non-zero are skipped. If 'wait' is given, this is the |
104 | + number of seconds for the request to wait for a message if no |
105 | + messages can be found. Valid values for 'detail' are 'none', |
106 | + 'id', 'attributes', 'body', and 'all'. Default value for |
107 | + 'detail' is 'none'. |
108 | + |
109 | + :returns: Generator which will loop through all messages if 'detail' |
110 | + is not 'none'. If 'detail' is 'none', the generator will stop |
111 | + on the first iteration, but it still must be called to finish |
112 | + the request. |
113 | + ''' |
114 | return [] |
115 | |
116 | def get_messages(self, account, queue, filters=None): |
117 | + '''Get a list of messages, possibly filtered. This uses the same |
118 | + parameters and return type as :func:`delete_messages()`, except |
119 | + the default value for 'detail' in 'filters' is 'all'.''' |
120 | return [] |
121 | |
122 | def update_messages(self, account, queue, attributes, filters=None): |
123 | + '''Update a list of messages, possibly filtered. In addition to |
124 | + the parameters and return type used in :func:`delete_messages()`, |
125 | + this also requires: |
126 | + |
127 | + :param attributes: Attributes to set as described in |
128 | + :func:`create_message()` |
129 | + ''' |
130 | return [] |
131 | |
132 | def create_message(self, account, queue, message, body, attributes=None): |
133 | + '''Create a new message in the given account and queue. |
134 | + |
135 | + :param account: Account to create the messages in. |
136 | + |
137 | + :param queue: Queue within the given account to create the |
138 | + messages in. |
139 | + |
140 | + :param message: Message ID to use within the queue, which is |
141 | + always unique. When a message of the same ID already exists, |
142 | + the old message is overwritten with this new one. |
143 | + |
144 | + :param body: Body of the message. |
145 | + |
146 | + :param attributes: A dict of initial attributes to set. Valid |
147 | + attributes are 'ttl' and 'hide'. The value of 'ttl' is the |
148 | + number of seconds from when the request is made for the message |
149 | + to be removed automatically by the server. The value of 'hide' |
150 | + is the number of seconds from when the request is made for |
151 | + the message to stay hidden from requests that do not have the |
152 | + 'match_hidden' filter given as True. |
153 | + |
154 | + :returns: True if the message was created, False if a message |
155 | + with the same ID existed and was replaced. |
156 | + ''' |
157 | return True |
158 | |
159 | def delete_message(self, account, queue, message, filters=None): |
160 | + '''Same as :func:`delete_messages()`, except only delete the |
161 | + given message ID. |
162 | + |
163 | + :returns: The message detail according to 'detail' in 'filters, |
164 | + or None if 'detail' is 'none'. |
165 | + ''' |
166 | return None |
167 | |
168 | def get_message(self, account, queue, message, filters=None): |
169 | + '''Same as :func:`get_messages()`, except only get the given |
170 | + message ID. |
171 | + |
172 | + :returns: The message detail according to 'detail' in 'filters, |
173 | + or None if 'detail' is 'none'. |
174 | + ''' |
175 | return None |
176 | |
177 | def update_message(self, account, queue, message, attributes, |
178 | filters=None): |
179 | + '''Same as :func:`update_messages()`, except only update the |
180 | + given message ID. |
181 | + |
182 | + :returns: The message detail according to 'detail' in 'filters, |
183 | + or None if 'detail' is 'none'. |
184 | + ''' |
185 | return None |
186 | |
187 | def clean(self): |
188 | @@ -91,6 +211,7 @@ |
189 | eventlet.sleep(1) |
190 | |
191 | def _get_attributes(self, attributes, ttl=None, hide=None): |
192 | + '''Helper method to parse attributes for implementations to use.''' |
193 | if attributes is not None: |
194 | ttl = attributes.get('ttl', ttl) |
195 | hide = attributes.get('hide', hide) |
196 | @@ -101,6 +222,8 @@ |
197 | return ttl, hide |
198 | |
199 | def _get_detail(self, filters, default=None): |
200 | + '''Helper method to parse account and queue detail for |
201 | + implementations to use.''' |
202 | detail = default if filters is None else filters.get('detail', default) |
203 | if detail == 'none': |
204 | detail = None |
205 | @@ -109,6 +232,8 @@ |
206 | return detail |
207 | |
208 | def _get_message_detail(self, filters, default=None): |
209 | + '''Helper method to parse message detail for implementations |
210 | + to use.''' |
211 | detail = default if filters is None else filters.get('detail', default) |
212 | options = ['id', 'attributes', 'body', 'all'] |
213 | if detail == 'none': |
214 | @@ -122,7 +247,7 @@ |
215 | a visible message.''' |
216 | queue = '%s/%s' % (account, queue) |
217 | if queue in self.queues: |
218 | - for count in xrange(0, self.queues[queue].getting()): |
219 | + for _count in xrange(0, self.queues[queue].getting()): |
220 | self.queues[queue].put(0) |
221 | |
222 | def wait(self, account, queue, seconds): |
223 | @@ -139,17 +264,21 @@ |
224 | |
225 | |
226 | def wait_without_attributes(method): |
227 | - def wrapper(self, account, queue, filters=None): |
228 | + '''Decorator that will wait for messages with the method does not |
229 | + take attributes.''' |
230 | + def __wrapper__(self, account, queue, filters=None): |
231 | original = lambda: method(self, account, queue, filters) |
232 | return wait(self, account, queue, filters, original) |
233 | - return wrapper |
234 | + return __wrapper__ |
235 | |
236 | |
237 | def wait_with_attributes(method): |
238 | - def wrapper(self, account, queue, attributes, filters=None): |
239 | + '''Decorator that will wait for messages with the method takes |
240 | + attributes.''' |
241 | + def __wrapper__(self, account, queue, attributes, filters=None): |
242 | original = lambda: method(self, account, queue, attributes, filters) |
243 | return wait(self, account, queue, filters, original) |
244 | - return wrapper |
245 | + return __wrapper__ |
246 | |
247 | |
248 | def wait(self, account, queue, filters, method): |
249 | @@ -173,8 +302,11 @@ |
250 | |
251 | |
252 | class NotFound(Exception): |
253 | + '''Raised when an account, queue, or message can not be found.''' |
254 | pass |
255 | |
256 | |
257 | class InvalidArguments(Exception): |
258 | + '''Raised when the given arguments are invalid, usually from attributes |
259 | + or filters.''' |
260 | pass |
261 | |
262 | === modified file 'burrow/backend/http.py' |
263 | --- burrow/backend/http.py 2011-08-19 00:07:39 +0000 |
264 | +++ burrow/backend/http.py 2011-08-24 17:01:25 +0000 |
265 | @@ -109,6 +109,7 @@ |
266 | pass |
267 | |
268 | def _add_parameters(self, url, attributes=None, filters=None): |
269 | + '''Add attributes and filters on to the URL as query parameters.''' |
270 | separator = '?' |
271 | if attributes is not None: |
272 | parameters = ['ttl', 'hide'] |
273 | @@ -127,6 +128,7 @@ |
274 | return url |
275 | |
276 | def _request(self, method, url, *args, **kwargs): |
277 | + '''Perform the request and handle the response.''' |
278 | connection = httplib.HTTPConnection(*self.server) |
279 | connection.request(method, '/v1.0' + url, *args, **kwargs) |
280 | response = connection.getresponse() |
281 | |
282 | === modified file 'burrow/backend/memory.py' |
283 | --- burrow/backend/memory.py 2011-08-19 00:07:39 +0000 |
284 | +++ burrow/backend/memory.py 2011-08-24 17:01:25 +0000 |
285 | @@ -175,6 +175,7 @@ |
286 | self.prev = None |
287 | |
288 | def detail(self, detail): |
289 | + '''Format detail response for this item.''' |
290 | if detail == 'id': |
291 | return self.id |
292 | elif detail == 'all': |
293 | @@ -193,6 +194,7 @@ |
294 | self.index = {} |
295 | |
296 | def add(self, item): |
297 | + '''Add a new item to the list.''' |
298 | if self.first is None: |
299 | self.first = item |
300 | if self.last is not None: |
301 | @@ -203,9 +205,11 @@ |
302 | return item |
303 | |
304 | def count(self): |
305 | + '''Return a count of the number of items in the list.''' |
306 | return len(self.index) |
307 | |
308 | def delete(self, id): |
309 | + '''Delete an item from the list by id.''' |
310 | item = self.index.pop(id) |
311 | if item.next is not None: |
312 | item.next.prev = item.prev |
313 | @@ -217,6 +221,7 @@ |
314 | self.last = item.prev |
315 | |
316 | def get(self, id, create=False): |
317 | + '''Get an item from the list by id.''' |
318 | if id in self.index: |
319 | return self.index[id] |
320 | elif create: |
321 | @@ -224,6 +229,7 @@ |
322 | raise burrow.backend.NotFound() |
323 | |
324 | def iter(self, filters=None): |
325 | + '''Iterate through all items in the list, possibly filtered.''' |
326 | if filters is None: |
327 | marker = None |
328 | limit = None |
329 | @@ -245,6 +251,7 @@ |
330 | item = item.next |
331 | |
332 | def reset(self): |
333 | + '''Remove all items in the list.''' |
334 | if self.count() == 0: |
335 | raise burrow.backend.NotFound() |
336 | self.first = None |
337 | @@ -253,6 +260,7 @@ |
338 | |
339 | |
340 | class Account(Item): |
341 | + '''A type of item representing an account.''' |
342 | |
343 | def __init__(self, id=None): |
344 | super(Account, self).__init__(id) |
345 | @@ -260,10 +268,12 @@ |
346 | |
347 | |
348 | class Accounts(IndexedList): |
349 | + '''A type of list representing an account list.''' |
350 | |
351 | item_class = Account |
352 | |
353 | def delete_queue(self, account, queue): |
354 | + '''Delete a queue within the given account.''' |
355 | account = self.get(account) |
356 | if account is not None: |
357 | account.queues.delete(queue) |
358 | @@ -271,6 +281,7 @@ |
359 | self.delete(account.id) |
360 | |
361 | def get_queue(self, account, queue, create=False): |
362 | + '''Get a queue within the given the account.''' |
363 | if account in self.index: |
364 | account = self.index[account] |
365 | elif create: |
366 | @@ -281,6 +292,7 @@ |
367 | |
368 | |
369 | class Queue(Item): |
370 | + '''A type of item representing a queue.''' |
371 | |
372 | def __init__(self, id=None): |
373 | super(Queue, self).__init__(id) |
374 | @@ -288,11 +300,13 @@ |
375 | |
376 | |
377 | class Queues(IndexedList): |
378 | + '''A type of list representing a queue list.''' |
379 | |
380 | item_class = Queue |
381 | |
382 | |
383 | class Message(Item): |
384 | + '''A type of item representing a message.''' |
385 | |
386 | def __init__(self, id=None): |
387 | super(Message, self).__init__(id) |
388 | @@ -319,6 +333,7 @@ |
389 | |
390 | |
391 | class Messages(IndexedList): |
392 | + '''A type of list representing a message list.''' |
393 | |
394 | item_class = Message |
395 | |
396 | |
397 | === modified file 'burrow/backend/sqlite.py' |
398 | --- burrow/backend/sqlite.py 2011-08-19 00:07:39 +0000 |
399 | +++ burrow/backend/sqlite.py 2011-08-24 17:01:25 +0000 |
400 | @@ -30,6 +30,8 @@ |
401 | |
402 | |
403 | class Backend(burrow.backend.Backend): |
404 | + '''Backend implemention that uses SQLite to store the account, queue, |
405 | + and message data.''' |
406 | |
407 | def __init__(self, config): |
408 | super(Backend, self).__init__(config) |
409 | @@ -83,6 +85,8 @@ |
410 | self._delete_accounts(ids) |
411 | |
412 | def _delete_accounts(self, ids): |
413 | + '''Delete all accounts with the given row IDs, which includes |
414 | + cascading deletes for all queues and messages as well.''' |
415 | ids = tuple(ids) |
416 | query_values = '(?' + (',?' * (len(ids) - 1)) + ')' |
417 | queue_ids = [] |
418 | @@ -103,6 +107,7 @@ |
419 | self.db.execute(query + query_values, ids) |
420 | |
421 | def _detail(self, row, detail): |
422 | + '''Format the account or queue detail from the given row.''' |
423 | if detail == 'id': |
424 | return row[0] |
425 | elif detail == 'all': |
426 | @@ -117,6 +122,8 @@ |
427 | yield self._detail(row, detail) |
428 | |
429 | def _get_accounts(self, query, filters): |
430 | + '''Build the SQL query to get accounts and check for empty |
431 | + responses.''' |
432 | values = tuple() |
433 | if filters is None: |
434 | marker = None |
435 | @@ -142,6 +149,7 @@ |
436 | raise burrow.backend.NotFound() |
437 | |
438 | def _get_account(self, account): |
439 | + '''Get the rowid for a given account ID.''' |
440 | query = 'SELECT rowid FROM accounts WHERE account=?' |
441 | rows = self.db.execute(query, (account,)).fetchall() |
442 | if len(rows) == 0: |
443 | @@ -165,6 +173,8 @@ |
444 | self._check_empty_account(account_rowid) |
445 | |
446 | def _delete_queues(self, ids): |
447 | + '''Delete all queues with the given row IDs, which includes |
448 | + cascading deletes for all messages as well.''' |
449 | ids = tuple(ids) |
450 | query_values = '(?' + (',?' * (len(ids) - 1)) + ')' |
451 | query = 'DELETE FROM messages WHERE queue IN ' |
452 | @@ -173,6 +183,7 @@ |
453 | self.db.execute(query + query_values, ids) |
454 | |
455 | def _check_empty_account(self, account_rowid): |
456 | + '''Check to see if an account is empty, and if so, remove it.''' |
457 | query = 'SELECT rowid FROM queues WHERE account=? LIMIT 1' |
458 | if len(self.db.execute(query, (account_rowid,)).fetchall()) == 0: |
459 | query = 'DELETE FROM accounts WHERE rowid=?' |
460 | @@ -187,6 +198,8 @@ |
461 | yield self._detail(row, detail) |
462 | |
463 | def _get_queues(self, query, account_rowid, filters): |
464 | + '''Build the SQL query to get queues and check for empty |
465 | + responses.''' |
466 | query += ' WHERE account=?' |
467 | values = (account_rowid,) |
468 | if filters is None: |
469 | @@ -213,6 +226,7 @@ |
470 | raise burrow.backend.NotFound() |
471 | |
472 | def _get_queue(self, account_rowid, queue): |
473 | + '''Get the rowid for a given queue ID.''' |
474 | query = 'SELECT rowid FROM queues WHERE account=? AND queue=?' |
475 | rows = self.db.execute(query, (account_rowid, queue)).fetchall() |
476 | if len(rows) == 0: |
477 | @@ -238,18 +252,21 @@ |
478 | self._check_empty_queue(account_rowid, queue_rowid) |
479 | |
480 | def _delete_messages(self, ids): |
481 | + '''Delete all messages with the given row IDs.''' |
482 | ids = tuple(ids) |
483 | query_values = '(?' + (',?' * (len(ids) - 1)) + ')' |
484 | query = 'DELETE FROM messages WHERE rowid IN ' |
485 | self.db.execute(query + query_values, ids) |
486 | |
487 | def _check_empty_queue(self, account_rowid, queue_rowid): |
488 | + '''Check to see if a queue is empty, and if so, remove it.''' |
489 | query = 'SELECT rowid FROM messages WHERE queue=? LIMIT 1' |
490 | if len(self.db.execute(query, (queue_rowid,)).fetchall()) == 0: |
491 | self.db.execute('DELETE FROM queues WHERE rowid=?', (queue_rowid,)) |
492 | self._check_empty_account(account_rowid) |
493 | |
494 | def _message_detail(self, row, detail): |
495 | + '''Format the message detail from the given row.''' |
496 | if detail == 'id': |
497 | return row[0] |
498 | elif detail == 'body': |
499 | @@ -277,6 +294,8 @@ |
500 | yield self._message_detail(row, detail) |
501 | |
502 | def _get_messages(self, query, queue_rowid, filters): |
503 | + '''Build the SQL query to get messages and check for empty |
504 | + responses.''' |
505 | query += ' WHERE queue=?' |
506 | values = (queue_rowid,) |
507 | if filters is None: |
508 | @@ -307,6 +326,7 @@ |
509 | raise burrow.backend.NotFound() |
510 | |
511 | def _get_message(self, queue_rowid, message, full=False): |
512 | + '''Get the rowid for a given message ID.''' |
513 | if full: |
514 | query = 'SELECT rowid,message,ttl,hide,body' |
515 | else: |
516 | @@ -348,6 +368,7 @@ |
517 | self.notify(account, queue) |
518 | |
519 | def _update_messages(self, ttl, hide, ids): |
520 | + '''Build the SQL query to update messages.''' |
521 | query = 'UPDATE messages SET ' |
522 | query_values = ' WHERE rowid IN (?' + (',?' * (len(ids) - 1)) + ')' |
523 | values = [] |