New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add an ExecuteLastObjectDataManager that attempts to execute after ot… #11
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,17 +12,22 @@ | |
#pylint: disable=W0212,R0904 | ||
|
||
import unittest | ||
|
||
from hamcrest import assert_that | ||
from hamcrest import is_ | ||
from hamcrest import calling | ||
from hamcrest import raises | ||
from hamcrest import contains | ||
|
||
import fudge | ||
|
||
from ..transactions import _do_commit | ||
from ..transactions import TransactionLoop | ||
from ..interfaces import CommitFailedError | ||
from ..interfaces import AbortFailedError | ||
from nti.transactions.interfaces import CommitFailedError | ||
from nti.transactions.interfaces import AbortFailedError | ||
|
||
from nti.transactions.transactions import do | ||
from nti.transactions.transactions import do_last | ||
from nti.transactions.transactions import _do_commit | ||
from nti.transactions.transactions import TransactionLoop | ||
|
||
import transaction | ||
from transaction.interfaces import TransientError | ||
|
@@ -218,3 +223,19 @@ def handler(): | |
|
||
loop = Loop(handler) | ||
assert_that(calling(loop), raises(AbortFailedError)) | ||
|
||
class TestDataManagers(unittest.TestCase): | ||
|
||
def test_data_manager_sorting(self): | ||
results = [] | ||
def test_call(x): | ||
results.append(x) | ||
|
||
# The managers will execute in order added (since identical), | ||
# except for the one that requests to go last. | ||
manager1 = do(call=test_call, args=(0,)) | ||
manager2 = do(call=test_call, args=(1,)) | ||
manager_post = do_last(call=test_call, args=(10,)) | ||
manager3 = do(call=test_call, args=(2,)) | ||
transaction.commit() | ||
assert_that(results, contains(0,1,2,10)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Spaces between commas, please. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -196,6 +196,22 @@ def rollback(self): | |
# anyway. | ||
pass | ||
|
||
class ExecuteLastObjectDataManager(ObjectDataManager): | ||
""" | ||
A special extension of :class:`ObjectDataManager` that attempts to execute | ||
after all other data managers have executed. This is useful when an | ||
operation relies on the execution of other data managers. | ||
""" | ||
|
||
def sortKey(self): | ||
""" | ||
Sort prepended with z's in an attempt to execute after other data | ||
managers. | ||
""" | ||
parent_key = super(ExecuteLastObjectDataManager, self).sortKey() | ||
sort_str = str(self.target) if self.target is not None else str(self.callable) | ||
return 'zzz%s:%s' % (sort_str, parent_key) | ||
|
||
class _QueuePutDataManager(ObjectDataManager): | ||
""" | ||
A data manager that checks if the queue is full before putting. | ||
|
@@ -237,6 +253,16 @@ def do(*args, **kwargs): | |
transaction.get().join(result) | ||
return result | ||
|
||
def do_last(*args, **kwargs): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we not use the word "last", here or in the class? That implies promises we can't keep, and it makes no sense if this function/class is used more than once. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Recommendations? I'm stumped :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm fine with either of those. I was hoping the comments would provide the caveat. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Only if you read the docstrings. That doesn't help someone just reading the code where this is used. |
||
""" | ||
Establishes a IDataManager in the current transaction that will attempt to | ||
execute *after* all other DataManagers have had their say. | ||
See :class:`ObjectDataManager` for the possible arguments. | ||
""" | ||
result = ExecuteLastObjectDataManager(*args, **kwargs) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rather than reimplementing this, what about something like this: def do(*args, **kwargs):
klass = kwargs.pop('datamanager_class', ObjectDataManager) # new argument
result = klass(*args, **kwargs)
... # as before
def do_XXX(*args, **kwargs):
kwargs['datamanager_class'] = XXXDataManager
return do(*args, **kwargs) |
||
transaction.get().join(result) | ||
return result | ||
|
||
def _do_commit(tx, description, long_commit_duration): | ||
exc_info = sys.exc_info() | ||
try: | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why this change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought we preferred absolute imports?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't, they make refactoring harder.