Skip to content
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

Merged
merged 4 commits into from Apr 17, 2017
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
29 changes: 25 additions & 4 deletions src/nti/transactions/tests/test_transaction.py
Expand Up @@ -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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this change?

Copy link
Author

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?

Copy link
Member

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.

from nti.transactions.interfaces import AbortFailedError

from nti.transactions.transactions import do
from nti.transactions.transactions import do_near_end
from nti.transactions.transactions import _do_commit
from nti.transactions.transactions import TransactionLoop

import transaction
from transaction.interfaces import TransientError
Expand Down Expand Up @@ -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_near_end(call=test_call, args=(10,))
manager3 = do(call=test_call, args=(2,))
transaction.commit()
assert_that(results, contains(0,1,2,10))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spaces between commas, please.

28 changes: 27 additions & 1 deletion src/nti/transactions/transactions.py
Expand Up @@ -196,6 +196,22 @@ def rollback(self):
# anyway.
pass

class OrderedNearEndObjectDataManager(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(OrderedNearEndObjectDataManager, 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.
Expand Down Expand Up @@ -233,10 +249,20 @@ def do(*args, **kwargs):
Establishes a IDataManager in the current transaction.
See :class:`ObjectDataManager` for the possible arguments.
"""
result = ObjectDataManager(*args, **kwargs)
klass = kwargs.pop('datamanager_class', ObjectDataManager)
result = klass(*args, **kwargs)
transaction.get().join(result)
return result

def do_near_end(*args, **kwargs):
"""
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.
"""
kwargs['datamanager_class'] = OrderedNearEndObjectDataManager
return do(*args, **kwargs)

def _do_commit(tx, description, long_commit_duration):
exc_info = sys.exc_info()
try:
Expand Down