Fix up python 3.3 incompatabilities

Make the python 3.3 testing work by selectively
disabling & including eventlet, switch to testtools
and testrepository which has 2.6, 2.7, 3.2+ unified
testing support so that we can correctly run our
tests in all supported python versions.

Closes-Bug: #1251660
Co-authored-by: Alexander Gorodnev <agorodnev@griddynamics.com>
Change-Id: I23b6f04387cfd3bf6b5a044edffa446ca897ce3a
This commit is contained in:
Joshua Harlow 2013-11-16 02:44:14 -08:00 committed by Alexander Gorodnev
parent 359ce523f4
commit db15db8186
32 changed files with 280 additions and 227 deletions

9
.testr.conf Normal file
View File

@ -0,0 +1,9 @@
[DEFAULT]
test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \
OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \
OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-160} \
${PYTHON:-python} -m subunit.run discover -t ./ ./taskflow/tests $LISTOPT $IDOPTION
test_id_option=--load-list $IDFILE
test_list_option=--list

View File

@ -15,7 +15,7 @@ stevedore>=0.10
# Backport for concurrent.futures which exists in 3.2+ # Backport for concurrent.futures which exists in 3.2+
futures>=2.1.3 futures>=2.1.3
# Only needed if the eventlet executor is used. # Only needed if the eventlet executor is used.
eventlet>=0.13.0 # eventlet>=0.13.0
# NOTE(harlowja): if you want to be able to use the graph_utils # NOTE(harlowja): if you want to be able to use the graph_utils
# export_graph_to_dot function you will need to uncomment the following. # export_graph_to_dot function you will need to uncomment the following.
# pydot>=1.0 # pydot>=1.0

View File

@ -17,6 +17,8 @@ classifier =
Programming Language :: Python :: 2 Programming Language :: Python :: 2
Programming Language :: Python :: 2.6 Programming Language :: Python :: 2.6
Programming Language :: Python :: 2.7 Programming Language :: Python :: 2.7
Programming Language :: Python :: 3
Programming Language :: Python :: 3.3
[global] [global]
setup-hooks = setup-hooks =

View File

@ -81,7 +81,7 @@ def get_backend():
class PrintText(task.Task): class PrintText(task.Task):
"""Just inserts some text print outs in a workflow.""" """Just inserts some text print outs in a workflow."""
def __init__(self, print_what, no_slow=False): def __init__(self, print_what, no_slow=False):
content_hash = hashlib.md5(print_what).hexdigest()[0:8] content_hash = hashlib.md5(print_what.encode('utf-8')).hexdigest()[0:8]
super(PrintText, self).__init__(name="Print: %s" % (content_hash)) super(PrintText, self).__init__(name="Print: %s" % (content_hash))
self._text = print_what self._text = print_what
self._no_slow = no_slow self._no_slow = no_slow
@ -257,8 +257,9 @@ except (IndexError, ValueError):
# Set up how we want our engine to run, serial, parallel... # Set up how we want our engine to run, serial, parallel...
engine_conf = { engine_conf = {
'engine': 'parallel', 'engine': 'parallel',
'executor': e_utils.GreenExecutor(5),
} }
if e_utils.EVENTLET_AVAILABLE:
engine_conf['executor'] = e_utils.GreenExecutor(5)
# Create/fetch a logbook that will track the workflows work. # Create/fetch a logbook that will track the workflows work.
book = None book = None

View File

@ -38,7 +38,6 @@ from taskflow import engines
from taskflow import task from taskflow import task
from taskflow.persistence import backends from taskflow.persistence import backends
from taskflow.utils import eventlet_utils as e_utils
from taskflow.utils import persistence_utils as p_utils from taskflow.utils import persistence_utils as p_utils
@ -83,7 +82,7 @@ def get_backend():
class PrintText(task.Task): class PrintText(task.Task):
def __init__(self, print_what, no_slow=False): def __init__(self, print_what, no_slow=False):
content_hash = hashlib.md5(print_what).hexdigest()[0:8] content_hash = hashlib.md5(print_what.encode('utf-8')).hexdigest()[0:8]
super(PrintText, self).__init__(name="Print: %s" % (content_hash)) super(PrintText, self).__init__(name="Print: %s" % (content_hash))
self._text = print_what self._text = print_what
self._no_slow = no_slow self._no_slow = no_slow
@ -156,13 +155,13 @@ else:
flow_detail = find_flow_detail(backend, book_id, flow_id) flow_detail = find_flow_detail(backend, book_id, flow_id)
# Annnnd load and run. # Annnnd load and run.
engine_conf = {
'engine': 'serial',
}
engine = engines.load(flow, engine = engines.load(flow,
flow_detail=flow_detail, flow_detail=flow_detail,
backend=backend, backend=backend,
engine_conf={ engine_conf=engine_conf)
'engine': 'parallel',
'executor': e_utils.GreenExecutor(10),
})
engine.run() engine.run()
# How to use. # How to use.

View File

@ -2,4 +2,4 @@ Calling jim 555.
Calling joe 444. Calling joe 444.
Calling 444 and apologizing. Calling 444 and apologizing.
Calling 555 and apologizing. Calling 555 and apologizing.
Flow failed: IOError('Suzzie not home right now.',) Flow failed: Suzzie not home right now.

View File

@ -98,4 +98,4 @@ except Exception as e:
# You will also note that this is not a problem in this case since no # You will also note that this is not a problem in this case since no
# parallelism is involved; this is ensured by the usage of a linear flow, # parallelism is involved; this is ensured by the usage of a linear flow,
# which runs serially as well as the default engine type which is 'serial'. # which runs serially as well as the default engine type which is 'serial'.
print("Flow failed: %r" % e) print("Flow failed: %s" % e)

View File

@ -1,10 +1,10 @@
Flow => RUNNING Flow => RUNNING
Task __main__.call_jim => RUNNING Task __main__.call_jim => RUNNING
Calling jim. Calling jim.
Context = {'joe_number': 444, 'jim_number': 555} Context = [('jim_number', 555), ('joe_number', 444)]
Task __main__.call_jim => SUCCESS Task __main__.call_jim => SUCCESS
Task __main__.call_joe => RUNNING Task __main__.call_joe => RUNNING
Calling joe. Calling joe.
Context = {'joe_number': 444, 'jim_number': 555} Context = [('jim_number', 555), ('joe_number', 444)]
Task __main__.call_joe => SUCCESS Task __main__.call_joe => SUCCESS
Flow => SUCCESS Flow => SUCCESS

View File

@ -58,12 +58,12 @@ from taskflow import task
def call_jim(context): def call_jim(context):
print("Calling jim.") print("Calling jim.")
print("Context = %s" % (context)) print("Context = %s" % (sorted(context.items(), key=lambda x: x[0])))
def call_joe(context): def call_joe(context):
print("Calling joe.") print("Calling joe.")
print("Context = %s" % (context)) print("Context = %s" % (sorted(context.items(), key=lambda x: x[0])))
def flow_watch(state, details): def flow_watch(state, details):

View File

@ -36,6 +36,7 @@ from taskflow.persistence.backends import base
from taskflow.persistence.backends.sqlalchemy import migration from taskflow.persistence.backends.sqlalchemy import migration
from taskflow.persistence.backends.sqlalchemy import models from taskflow.persistence.backends.sqlalchemy import models
from taskflow.persistence import logbook from taskflow.persistence import logbook
from taskflow.utils import eventlet_utils
from taskflow.utils import misc from taskflow.utils import misc
from taskflow.utils import persistence_utils from taskflow.utils import persistence_utils
@ -224,7 +225,9 @@ class SQLAlchemyBackend(base.Backend):
# or engine arg overrides make sure we merge them in. # or engine arg overrides make sure we merge them in.
engine_args.update(conf.pop('engine_args', {})) engine_args.update(conf.pop('engine_args', {}))
engine = sa.create_engine(sql_connection, **engine_args) engine = sa.create_engine(sql_connection, **engine_args)
if misc.as_bool(conf.pop('checkin_yield', True)): checkin_yield = conf.pop('checkin_yield',
eventlet_utils.EVENTLET_AVAILABLE)
if misc.as_bool(checkin_yield):
sa.event.listen(engine, 'checkin', _thread_yield) sa.event.listen(engine, 'checkin', _thread_yield)
if 'mysql' in e_url.drivername: if 'mysql' in e_url.drivername:
if misc.as_bool(conf.pop('checkout_ping', True)): if misc.as_bool(conf.pop('checkout_ping', True)):

View File

@ -16,17 +16,41 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import unittest2 from testtools import compat
from testtools import matchers
from testtools import testcase
class TestCase(unittest2.TestCase): class TestCase(testcase.TestCase):
"""Test case base class for all unit tests.""" """Test case base class for all taskflow unit tests."""
def setUp(self): def assertRaisesRegexp(self, exc_class, pattern, callable_obj,
super(TestCase, self).setUp() *args, **kwargs):
# TODO(harlowja): submit a pull/review request to testtools to add
# this method to there codebase instead of having it exist in ours
# since it really doesn't belong here.
def tearDown(self): class ReRaiseOtherTypes(object):
super(TestCase, self).tearDown() def match(self, matchee):
if not issubclass(matchee[0], exc_class):
compat.reraise(*matchee)
class CaptureMatchee(object):
def match(self, matchee):
self.matchee = matchee[1]
capture = CaptureMatchee()
matcher = matchers.Raises(matchers.MatchesAll(ReRaiseOtherTypes(),
matchers.MatchesException(exc_class,
pattern),
capture))
our_callable = testcase.Nullary(callable_obj, *args, **kwargs)
self.assertThat(our_callable, matcher)
return capture.matchee
def assertRegexpMatches(self, text, pattern):
matcher = matchers.MatchesRegex(pattern)
self.assertThat(text, matcher)
def assertIsSubset(self, super_set, sub_set, msg=None): def assertIsSubset(self, super_set, sub_set, msg=None):
missing_set = set() missing_set = set()

View File

@ -98,7 +98,7 @@ class ExamplesTestCase(taskflow.test.TestCase):
# replace them with some constant string # replace them with some constant string
output = self.uuid_re.sub('<SOME UUID>', output) output = self.uuid_re.sub('<SOME UUID>', output)
expected_output = self.uuid_re.sub('<SOME UUID>', expected_output) expected_output = self.uuid_re.sub('<SOME UUID>', expected_output)
self.assertMultiLineEqual(output, expected_output) self.assertEqual(output, expected_output)
ExamplesTestCase.update() ExamplesTestCase.update()

View File

@ -23,6 +23,7 @@ from taskflow.tests.unit.persistence import base
class MemoryPersistenceTest(test.TestCase, base.PersistenceTestMixin): class MemoryPersistenceTest(test.TestCase, base.PersistenceTestMixin):
def setUp(self): def setUp(self):
super(MemoryPersistenceTest, self).setUp()
self._backend = impl_memory.MemoryBackend({}) self._backend = impl_memory.MemoryBackend({})
def _get_connection(self): def _get_connection(self):

View File

@ -85,30 +85,34 @@ class EngineTaskTest(utils.EngineTestBase):
'fail reverted(Failure: RuntimeError: Woot!)', 'fail reverted(Failure: RuntimeError: Woot!)',
'fail REVERTED', 'fail REVERTED',
'flow REVERTED'] 'flow REVERTED']
with self.assertRaisesRegexp(RuntimeError, '^Woot'): self.assertRaisesRegexp(RuntimeError, '^Woot', engine.run)
engine.run()
self.assertEqual(self.values, expected) self.assertEqual(self.values, expected)
self.assertEqual(engine.storage.get_flow_state(), states.REVERTED) self.assertEqual(engine.storage.get_flow_state(), states.REVERTED)
with self.assertRaisesRegexp(RuntimeError, '^Woot'): self.assertRaisesRegexp(RuntimeError, '^Woot', engine.run)
engine.run()
now_expected = expected + ['fail PENDING', 'flow PENDING'] + expected now_expected = expected + ['fail PENDING', 'flow PENDING'] + expected
self.assertEqual(self.values, now_expected) self.assertEqual(self.values, now_expected)
self.assertEqual(engine.storage.get_flow_state(), states.REVERTED) self.assertEqual(engine.storage.get_flow_state(), states.REVERTED)
def test_invalid_flow_raises(self): def test_invalid_flow_raises(self):
value = 'i am string, not task/flow, sorry'
with self.assertRaises(TypeError) as err: def compile_bad(value):
engine = self._make_engine(value) engine = self._make_engine(value)
engine.compile() engine.compile()
self.assertIn(value, str(err.exception))
value = 'i am string, not task/flow, sorry'
err = self.assertRaises(TypeError, compile_bad, value)
self.assertIn(value, str(err))
def test_invalid_flow_raises_from_run(self): def test_invalid_flow_raises_from_run(self):
value = 'i am string, not task/flow, sorry'
with self.assertRaises(TypeError) as err: def run_bad(value):
engine = self._make_engine(value) engine = self._make_engine(value)
engine.run() engine.run()
self.assertIn(value, str(err.exception))
value = 'i am string, not task/flow, sorry'
err = self.assertRaises(TypeError, run_bad, value)
self.assertIn(value, str(err))
class EngineLinearFlowTest(utils.EngineTestBase): class EngineLinearFlowTest(utils.EngineTestBase):
@ -136,8 +140,7 @@ class EngineLinearFlowTest(utils.EngineTestBase):
utils.FailingTask(name='fail') utils.FailingTask(name='fail')
) )
engine = self._make_engine(flow) engine = self._make_engine(flow)
with self.assertRaisesRegexp(RuntimeError, '^Woot'): self.assertRaisesRegexp(RuntimeError, '^Woot', engine.run)
engine.run()
self.assertEqual(engine.storage.fetch_all(), {}) self.assertEqual(engine.storage.fetch_all(), {})
def test_sequential_flow_nested_blocks(self): def test_sequential_flow_nested_blocks(self):
@ -156,8 +159,7 @@ class EngineLinearFlowTest(utils.EngineTestBase):
utils.FailingTask(name='fail') utils.FailingTask(name='fail')
) )
engine = self._make_engine(flow) engine = self._make_engine(flow)
with self.assertRaisesRegexp(RuntimeError, '^Gotcha'): self.assertRaisesRegexp(RuntimeError, '^Gotcha', engine.run)
engine.run()
def test_revert_not_run_task_is_not_reverted(self): def test_revert_not_run_task_is_not_reverted(self):
flow = lf.Flow('revert-not-run').add( flow = lf.Flow('revert-not-run').add(
@ -165,8 +167,7 @@ class EngineLinearFlowTest(utils.EngineTestBase):
utils.NeverRunningTask(), utils.NeverRunningTask(),
) )
engine = self._make_engine(flow) engine = self._make_engine(flow)
with self.assertRaisesRegexp(RuntimeError, '^Woot'): self.assertRaisesRegexp(RuntimeError, '^Woot', engine.run)
engine.run()
self.assertEqual( self.assertEqual(
self.values, self.values,
['fail reverted(Failure: RuntimeError: Woot!)']) ['fail reverted(Failure: RuntimeError: Woot!)'])
@ -180,8 +181,7 @@ class EngineLinearFlowTest(utils.EngineTestBase):
) )
) )
engine = self._make_engine(flow) engine = self._make_engine(flow)
with self.assertRaisesRegexp(RuntimeError, '^Woot'): self.assertRaisesRegexp(RuntimeError, '^Woot', engine.run)
engine.run()
self.assertEqual( self.assertEqual(
self.values, self.values,
['task1', 'task2', ['task1', 'task2',
@ -195,7 +195,7 @@ class EngineLinearFlowTest(utils.EngineTestBase):
def revert(m_self, result, flow_failures): def revert(m_self, result, flow_failures):
self.assertEqual(result, 'RESULT') self.assertEqual(result, 'RESULT')
self.assertEqual(flow_failures.keys(), ['fail1']) self.assertEqual(list(flow_failures.keys()), ['fail1'])
fail = flow_failures['fail1'] fail = flow_failures['fail1']
self.assertIsInstance(fail, misc.Failure) self.assertIsInstance(fail, misc.Failure)
self.assertEqual(str(fail), 'Failure: RuntimeError: Woot!') self.assertEqual(str(fail), 'Failure: RuntimeError: Woot!')
@ -205,8 +205,7 @@ class EngineLinearFlowTest(utils.EngineTestBase):
utils.FailingTask(self.values, 'fail1') utils.FailingTask(self.values, 'fail1')
) )
engine = self._make_engine(flow) engine = self._make_engine(flow)
with self.assertRaisesRegexp(RuntimeError, '^Woot'): self.assertRaisesRegexp(RuntimeError, '^Woot', engine.run)
engine.run()
class EngineParallelFlowTest(utils.EngineTestBase): class EngineParallelFlowTest(utils.EngineTestBase):
@ -236,8 +235,7 @@ class EngineParallelFlowTest(utils.EngineTestBase):
utils.TaskNoRequiresNoReturns(name='task2') utils.TaskNoRequiresNoReturns(name='task2')
) )
engine = self._make_engine(flow) engine = self._make_engine(flow)
with self.assertRaisesRegexp(RuntimeError, '^Woot'): self.assertRaisesRegexp(RuntimeError, '^Woot', engine.run)
engine.run()
def test_parallel_revert_exception_is_reraised(self): def test_parallel_revert_exception_is_reraised(self):
# NOTE(imelnikov): if we put NastyTask and FailingTask # NOTE(imelnikov): if we put NastyTask and FailingTask
@ -252,8 +250,7 @@ class EngineParallelFlowTest(utils.EngineTestBase):
utils.FailingTask(self.values, sleep=0.1) utils.FailingTask(self.values, sleep=0.1)
) )
engine = self._make_engine(flow) engine = self._make_engine(flow)
with self.assertRaisesRegexp(RuntimeError, '^Gotcha'): self.assertRaisesRegexp(RuntimeError, '^Gotcha', engine.run)
engine.run()
def test_sequential_flow_two_tasks_with_resumption(self): def test_sequential_flow_two_tasks_with_resumption(self):
flow = lf.Flow('lf-2-r').add( flow = lf.Flow('lf-2-r').add(
@ -285,8 +282,7 @@ class EngineParallelFlowTest(utils.EngineTestBase):
utils.SaveOrderTask(self.values, name='task2', sleep=0.01) utils.SaveOrderTask(self.values, name='task2', sleep=0.01)
) )
engine = self._make_engine(flow) engine = self._make_engine(flow)
with self.assertRaisesRegexp(RuntimeError, '^Woot'): self.assertRaisesRegexp(RuntimeError, '^Woot', engine.run)
engine.run()
result = set(self.values) result = set(self.values)
# NOTE(harlowja): task 1/2 may or may not have executed, even with the # NOTE(harlowja): task 1/2 may or may not have executed, even with the
# sleeps due to the fact that the above is an unordered flow. # sleeps due to the fact that the above is an unordered flow.
@ -303,8 +299,7 @@ class EngineParallelFlowTest(utils.EngineTestBase):
name='task2') # this should not get reverted name='task2') # this should not get reverted
) )
engine = self._make_engine(flow) engine = self._make_engine(flow)
with self.assertRaisesRegexp(RuntimeError, '^Gotcha'): self.assertRaisesRegexp(RuntimeError, '^Gotcha', engine.run)
engine.run()
result = set(self.values) result = set(self.values)
self.assertEqual(result, set(['task1'])) self.assertEqual(result, set(['task1']))
@ -319,8 +314,7 @@ class EngineParallelFlowTest(utils.EngineTestBase):
) )
) )
engine = self._make_engine(flow) engine = self._make_engine(flow)
with self.assertRaisesRegexp(RuntimeError, '^Gotcha'): self.assertRaisesRegexp(RuntimeError, '^Gotcha', engine.run)
engine.run()
result = set(self.values) result = set(self.values)
# Task1, task2 may *not* have executed and also may have *not* reverted # Task1, task2 may *not* have executed and also may have *not* reverted
# since the above is an unordered flow so take that into account by # since the above is an unordered flow so take that into account by
@ -381,8 +375,7 @@ class EngineParallelFlowTest(utils.EngineTestBase):
) )
) )
engine = self._make_engine(flow) engine = self._make_engine(flow)
with self.assertRaisesRegexp(RuntimeError, '^Woot'): self.assertRaisesRegexp(RuntimeError, '^Woot', engine.run)
engine.run()
result = set(self.values) result = set(self.values)
# Task3 may or may not have executed, depending on scheduling and # Task3 may or may not have executed, depending on scheduling and
# task ordering selection, so it may or may not exist in the result set # task ordering selection, so it may or may not exist in the result set
@ -407,8 +400,7 @@ class EngineParallelFlowTest(utils.EngineTestBase):
) )
) )
engine = self._make_engine(flow) engine = self._make_engine(flow)
with self.assertRaisesRegexp(RuntimeError, '^Woot'): self.assertRaisesRegexp(RuntimeError, '^Woot', engine.run)
engine.run()
result = set(self.values) result = set(self.values)
# Since this is an unordered flow we can not guarantee that task1 or # Since this is an unordered flow we can not guarantee that task1 or
# task2 will exist and be reverted, although they may exist depending # task2 will exist and be reverted, although they may exist depending
@ -432,8 +424,7 @@ class EngineParallelFlowTest(utils.EngineTestBase):
) )
) )
engine = self._make_engine(flow) engine = self._make_engine(flow)
with self.assertRaisesRegexp(RuntimeError, '^Gotcha'): self.assertRaisesRegexp(RuntimeError, '^Gotcha', engine.run)
engine.run()
result = set(self.values) result = set(self.values)
possible_result = set(['task1', 'task1 reverted(5)', possible_result = set(['task1', 'task1 reverted(5)',
'task2', 'task2 reverted(5)', 'task2', 'task2 reverted(5)',
@ -492,8 +483,7 @@ class EngineGraphFlowTest(utils.EngineTestBase):
utils.SaveOrderTask(self.values, name='task1', provides='a')) utils.SaveOrderTask(self.values, name='task1', provides='a'))
engine = self._make_engine(flow) engine = self._make_engine(flow)
with self.assertRaisesRegexp(RuntimeError, '^Woot'): self.assertRaisesRegexp(RuntimeError, '^Woot', engine.run)
engine.run()
self.assertEqual( self.assertEqual(
self.values, self.values,
['task1', 'task2', ['task1', 'task2',
@ -508,8 +498,7 @@ class EngineGraphFlowTest(utils.EngineTestBase):
utils.SaveOrderTask(self.values, name='task1', provides='a')) utils.SaveOrderTask(self.values, name='task1', provides='a'))
engine = self._make_engine(flow) engine = self._make_engine(flow)
with self.assertRaisesRegexp(RuntimeError, '^Gotcha'): self.assertRaisesRegexp(RuntimeError, '^Gotcha', engine.run)
engine.run()
self.assertEqual(engine.storage.get_flow_state(), states.FAILURE) self.assertEqual(engine.storage.get_flow_state(), states.FAILURE)
def test_graph_flow_with_multireturn_and_multiargs_tasks(self): def test_graph_flow_with_multireturn_and_multiargs_tasks(self):

View File

@ -61,8 +61,9 @@ class ArgumentsPassingTest(utils.EngineTestBase):
}) })
def test_bad_save_as_value(self): def test_bad_save_as_value(self):
with self.assertRaises(TypeError): self.assertRaises(TypeError,
utils.TaskOneReturn(name='task1', provides=object()) utils.TaskOneReturn,
name='task1', provides=object())
def test_arguments_passing(self): def test_arguments_passing(self):
flow = utils.TaskMultiArgOneReturn(provides='result') flow = utils.TaskMultiArgOneReturn(provides='result')
@ -78,8 +79,7 @@ class ArgumentsPassingTest(utils.EngineTestBase):
flow = utils.TaskMultiArg() flow = utils.TaskMultiArg()
engine = self._make_engine(flow) engine = self._make_engine(flow)
engine.storage.inject({'a': 1, 'b': 4, 'x': 17}) engine.storage.inject({'a': 1, 'b': 4, 'x': 17})
with self.assertRaises(exc.MissingDependencies): self.assertRaises(exc.MissingDependencies, engine.run)
engine.run()
def test_partial_arguments_mapping(self): def test_partial_arguments_mapping(self):
flow = utils.TaskMultiArgOneReturn(provides='result', flow = utils.TaskMultiArgOneReturn(provides='result',
@ -109,19 +109,18 @@ class ArgumentsPassingTest(utils.EngineTestBase):
flow = utils.TaskMultiArg(rebind={'z': 'b'}) flow = utils.TaskMultiArg(rebind={'z': 'b'})
engine = self._make_engine(flow) engine = self._make_engine(flow)
engine.storage.inject({'a': 1, 'y': 4, 'c': 9, 'x': 17}) engine.storage.inject({'a': 1, 'y': 4, 'c': 9, 'x': 17})
with self.assertRaises(exc.MissingDependencies): self.assertRaises(exc.MissingDependencies, engine.run)
engine.run()
def test_invalid_argument_name_list(self): def test_invalid_argument_name_list(self):
flow = utils.TaskMultiArg(rebind=['a', 'z', 'b']) flow = utils.TaskMultiArg(rebind=['a', 'z', 'b'])
engine = self._make_engine(flow) engine = self._make_engine(flow)
engine.storage.inject({'a': 1, 'b': 4, 'c': 9, 'x': 17}) engine.storage.inject({'a': 1, 'b': 4, 'c': 9, 'x': 17})
with self.assertRaises(exc.MissingDependencies): self.assertRaises(exc.MissingDependencies, engine.run)
engine.run()
def test_bad_rebind_args_value(self): def test_bad_rebind_args_value(self):
with self.assertRaises(TypeError): self.assertRaises(TypeError,
utils.TaskOneArg(rebind=object()) utils.TaskOneArg,
rebind=object())
class SingleThreadedEngineTest(ArgumentsPassingTest, class SingleThreadedEngineTest(ArgumentsPassingTest,

View File

@ -40,6 +40,7 @@ class CheckFlowTransitionTest(test.TestCase):
states.check_flow_transition(states.RUNNING, states.RESUMING)) states.check_flow_transition(states.RUNNING, states.RESUMING))
def test_bad_transition_raises(self): def test_bad_transition_raises(self):
with self.assertRaisesRegexp(exc.InvalidStateException, self.assertRaisesRegexp(exc.InvalidStateException,
'^Flow transition.*not allowed'): '^Flow transition.*not allowed',
states.check_flow_transition(states.FAILURE, states.SUCCESS) states.check_flow_transition,
states.FAILURE, states.SUCCESS)

View File

@ -29,25 +29,28 @@ class FlowFromDetailTestCase(test.TestCase):
def test_no_meta(self): def test_no_meta(self):
_lb, flow_detail = p_utils.temporary_flow_detail() _lb, flow_detail = p_utils.temporary_flow_detail()
self.assertIs(flow_detail.meta, None) self.assertIs(flow_detail.meta, None)
expected_msg = '^Cannot .* no factory information saved.$' self.assertRaisesRegexp(ValueError,
with self.assertRaisesRegexp(ValueError, expected_msg): '^Cannot .* no factory information saved.$',
taskflow.engines.flow_from_detail(flow_detail) taskflow.engines.flow_from_detail,
flow_detail)
def test_no_factory_in_meta(self): def test_no_factory_in_meta(self):
_lb, flow_detail = p_utils.temporary_flow_detail() _lb, flow_detail = p_utils.temporary_flow_detail()
flow_detail.meta = {} flow_detail.meta = {}
expected_msg = '^Cannot .* no factory information saved.$' self.assertRaisesRegexp(ValueError,
with self.assertRaisesRegexp(ValueError, expected_msg): '^Cannot .* no factory information saved.$',
taskflow.engines.flow_from_detail(flow_detail) taskflow.engines.flow_from_detail,
flow_detail)
def test_no_importable_function(self): def test_no_importable_function(self):
_lb, flow_detail = p_utils.temporary_flow_detail() _lb, flow_detail = p_utils.temporary_flow_detail()
flow_detail.meta = dict(factory=dict( flow_detail.meta = dict(factory=dict(
name='you can not import me, i contain spaces' name='you can not import me, i contain spaces'
)) ))
expected_msg = '^Could not import factory' self.assertRaisesRegexp(ImportError,
with self.assertRaisesRegexp(ImportError, expected_msg): '^Could not import factory',
taskflow.engines.flow_from_detail(flow_detail) taskflow.engines.flow_from_detail,
flow_detail)
def test_no_arg_factory(self): def test_no_arg_factory(self):
name = 'some.test.factory' name = 'some.test.factory'
@ -79,11 +82,14 @@ def my_flow_factory(task_name):
class LoadFromFactoryTestCase(test.TestCase): class LoadFromFactoryTestCase(test.TestCase):
def test_non_reimportable(self): def test_non_reimportable(self):
def factory(): def factory():
pass pass
with self.assertRaisesRegexp(ValueError,
'Flow factory .* is not reimportable'): self.assertRaisesRegexp(ValueError,
taskflow.engines.load_from_factory(factory) 'Flow factory .* is not reimportable',
taskflow.engines.load_from_factory,
factory)
def test_it_works(self): def test_it_works(self):
engine = taskflow.engines.load_from_factory( engine = taskflow.engines.load_from_factory(

View File

@ -174,14 +174,14 @@ class FlattenTest(test.TestCase):
t_utils.DummyTask(name="a"), t_utils.DummyTask(name="a"),
t_utils.DummyTask(name="a") t_utils.DummyTask(name="a")
) )
with self.assertRaisesRegexp(exc.InvariantViolationException, self.assertRaisesRegexp(exc.InvariantViolationException,
'^Tasks with duplicate names'): '^Tasks with duplicate names',
f_utils.flatten(flo) f_utils.flatten, flo)
def test_flatten_checks_for_dups_globally(self): def test_flatten_checks_for_dups_globally(self):
flo = gf.Flow("test").add( flo = gf.Flow("test").add(
gf.Flow("int1").add(t_utils.DummyTask(name="a")), gf.Flow("int1").add(t_utils.DummyTask(name="a")),
gf.Flow("int2").add(t_utils.DummyTask(name="a"))) gf.Flow("int2").add(t_utils.DummyTask(name="a")))
with self.assertRaisesRegexp(exc.InvariantViolationException, self.assertRaisesRegexp(exc.InvariantViolationException,
'^Tasks with duplicate names'): '^Tasks with duplicate names',
f_utils.flatten(flo) f_utils.flatten, flo)

View File

@ -86,10 +86,11 @@ class FlowDependenciesTest(test.TestCase):
self.assertEqual(flow.provides, set(['x', 'a', 'b', 'c'])) self.assertEqual(flow.provides, set(['x', 'a', 'b', 'c']))
def test_linear_flow_provides_out_of_order(self): def test_linear_flow_provides_out_of_order(self):
with self.assertRaises(exceptions.InvariantViolationException): flow = lf.Flow('lf')
lf.Flow('lf').add( self.assertRaises(exceptions.InvariantViolationException,
utils.TaskOneArg('task2'), flow.add,
utils.TaskOneReturn('task1', provides='x')) utils.TaskOneArg('task2'),
utils.TaskOneReturn('task1', provides='x'))
def test_linear_flow_provides_required_values(self): def test_linear_flow_provides_required_values(self):
flow = lf.Flow('lf').add( flow = lf.Flow('lf').add(
@ -110,8 +111,10 @@ class FlowDependenciesTest(test.TestCase):
def test_linear_flow_self_requires(self): def test_linear_flow_self_requires(self):
flow = lf.Flow('uf') flow = lf.Flow('uf')
with self.assertRaises(exceptions.InvariantViolationException): self.assertRaises(exceptions.InvariantViolationException,
flow.add(utils.TaskNoRequiresNoReturns(rebind=['x'], provides='x')) flow.add,
utils.TaskNoRequiresNoReturns(rebind=['x'],
provides='x'))
def test_unordered_flow_without_dependencies(self): def test_unordered_flow_without_dependencies(self):
flow = uf.Flow('uf').add( flow = uf.Flow('uf').add(
@ -122,8 +125,10 @@ class FlowDependenciesTest(test.TestCase):
def test_unordered_flow_self_requires(self): def test_unordered_flow_self_requires(self):
flow = uf.Flow('uf') flow = uf.Flow('uf')
with self.assertRaises(exceptions.InvariantViolationException): self.assertRaises(exceptions.InvariantViolationException,
flow.add(utils.TaskNoRequiresNoReturns(rebind=['x'], provides='x')) flow.add,
utils.TaskNoRequiresNoReturns(rebind=['x'],
provides='x'))
def test_unordered_flow_reuires_values(self): def test_unordered_flow_reuires_values(self):
flow = uf.Flow('uf').add( flow = uf.Flow('uf').add(
@ -147,22 +152,25 @@ class FlowDependenciesTest(test.TestCase):
self.assertEqual(flow.provides, set(['x', 'a', 'b', 'c'])) self.assertEqual(flow.provides, set(['x', 'a', 'b', 'c']))
def test_unordered_flow_provides_required_values(self): def test_unordered_flow_provides_required_values(self):
with self.assertRaises(exceptions.InvariantViolationException): flow = uf.Flow('uf')
uf.Flow('uf').add( self.assertRaises(exceptions.InvariantViolationException,
utils.TaskOneReturn('task1', provides='x'), flow.add,
utils.TaskOneArg('task2')) utils.TaskOneReturn('task1', provides='x'),
utils.TaskOneArg('task2'))
def test_unordered_flow_requires_provided_value_other_call(self): def test_unordered_flow_requires_provided_value_other_call(self):
flow = uf.Flow('uf') flow = uf.Flow('uf')
flow.add(utils.TaskOneReturn('task1', provides='x')) flow.add(utils.TaskOneReturn('task1', provides='x'))
with self.assertRaises(exceptions.InvariantViolationException): self.assertRaises(exceptions.InvariantViolationException,
flow.add(utils.TaskOneArg('task2')) flow.add,
utils.TaskOneArg('task2'))
def test_unordered_flow_provides_required_value_other_call(self): def test_unordered_flow_provides_required_value_other_call(self):
flow = uf.Flow('uf') flow = uf.Flow('uf')
flow.add(utils.TaskOneArg('task2')) flow.add(utils.TaskOneArg('task2'))
with self.assertRaises(exceptions.InvariantViolationException): self.assertRaises(exceptions.InvariantViolationException,
flow.add(utils.TaskOneReturn('task1', provides='x')) flow.add,
utils.TaskOneReturn('task1', provides='x'))
def test_unordered_flow_multi_provides_and_requires_values(self): def test_unordered_flow_multi_provides_and_requires_values(self):
flow = uf.Flow('uf').add( flow = uf.Flow('uf').add(
@ -196,9 +204,11 @@ class FlowDependenciesTest(test.TestCase):
self.assertEqual(flow.provides, set()) self.assertEqual(flow.provides, set())
def test_graph_flow_self_requires(self): def test_graph_flow_self_requires(self):
with self.assertRaisesRegexp(exceptions.DependencyFailure, '^No path'): flow = gf.Flow('g-1-req-error')
gf.Flow('g-1-req-error').add( self.assertRaisesRegexp(exceptions.DependencyFailure, '^No path',
utils.TaskOneArgOneReturn(requires=['a'], provides='a')) flow.add,
utils.TaskOneArgOneReturn(requires=['a'],
provides='a'))
def test_graph_flow_reuires_values(self): def test_graph_flow_reuires_values(self):
flow = gf.Flow('gf').add( flow = gf.Flow('gf').add(
@ -231,8 +241,9 @@ class FlowDependenciesTest(test.TestCase):
def test_graph_flow_provides_provided_value_other_call(self): def test_graph_flow_provides_provided_value_other_call(self):
flow = gf.Flow('gf') flow = gf.Flow('gf')
flow.add(utils.TaskOneReturn('task1', provides='x')) flow.add(utils.TaskOneReturn('task1', provides='x'))
with self.assertRaises(exceptions.DependencyFailure): self.assertRaises(exceptions.DependencyFailure,
flow.add(utils.TaskOneReturn('task2', provides='x')) flow.add,
utils.TaskOneReturn('task2', provides='x'))
def test_graph_flow_multi_provides_and_requires_values(self): def test_graph_flow_multi_provides_and_requires_values(self):
flow = gf.Flow('gf').add( flow = gf.Flow('gf').add(
@ -245,8 +256,12 @@ class FlowDependenciesTest(test.TestCase):
self.assertEqual(flow.provides, set(['d', 'e', 'f', 'i', 'j', 'k'])) self.assertEqual(flow.provides, set(['d', 'e', 'f', 'i', 'j', 'k']))
def test_graph_cyclic_dependency(self): def test_graph_cyclic_dependency(self):
with self.assertRaisesRegexp(exceptions.DependencyFailure, '^No path'): flow = gf.Flow('g-3-cyclic')
gf.Flow('g-3-cyclic').add( self.assertRaisesRegexp(exceptions.DependencyFailure, '^No path',
utils.TaskOneArgOneReturn(provides='a', requires=['b']), flow.add,
utils.TaskOneArgOneReturn(provides='b', requires=['c']), utils.TaskOneArgOneReturn(provides='a',
utils.TaskOneArgOneReturn(provides='c', requires=['a'])) requires=['b']),
utils.TaskOneArgOneReturn(provides='b',
requires=['c']),
utils.TaskOneArgOneReturn(provides='c',
requires=['a']))

View File

@ -63,6 +63,6 @@ class FunctorTaskTest(test.TestCase):
t(bof.run_one, revert=bof.revert_one), t(bof.run_one, revert=bof.revert_one),
t(bof.run_fail) t(bof.run_fail)
) )
with self.assertRaisesRegexp(RuntimeError, '^Woot'): self.assertRaisesRegexp(RuntimeError, '^Woot',
taskflow.engines.run(flow) taskflow.engines.run, flow)
self.assertEqual(values, ['one', 'fail', 'revert one']) self.assertEqual(values, ['one', 'fail', 'revert one'])

View File

@ -19,11 +19,13 @@
import collections import collections
import functools import functools
from taskflow import test import testtools
from taskflow import test
from taskflow.utils import eventlet_utils as eu from taskflow.utils import eventlet_utils as eu
@testtools.skipIf(not eu.EVENTLET_AVAILABLE, 'eventlet is not available')
class GreenExecutorTest(test.TestCase): class GreenExecutorTest(test.TestCase):
def make_funcs(self, called, amount): def make_funcs(self, called, amount):

View File

@ -124,8 +124,7 @@ class StorageTest(test.TestCase):
def test_get_non_existing_var(self): def test_get_non_existing_var(self):
s = self._get_storage() s = self._get_storage()
s.add_task('42', 'my task') s.add_task('42', 'my task')
with self.assertRaises(exceptions.NotFound): self.assertRaises(exceptions.NotFound, s.get, '42')
s.get('42')
def test_reset(self): def test_reset(self):
s = self._get_storage() s = self._get_storage()
@ -133,8 +132,7 @@ class StorageTest(test.TestCase):
s.save('42', 5) s.save('42', 5)
s.reset('42') s.reset('42')
self.assertEqual(s.get_task_state('42'), states.PENDING) self.assertEqual(s.get_task_state('42'), states.PENDING)
with self.assertRaises(exceptions.NotFound): self.assertRaises(exceptions.NotFound, s.get, '42')
s.get('42')
def test_reset_unknown_task(self): def test_reset_unknown_task(self):
s = self._get_storage() s = self._get_storage()
@ -151,11 +149,9 @@ class StorageTest(test.TestCase):
s.reset_tasks() s.reset_tasks()
self.assertEqual(s.get_task_state('42'), states.PENDING) self.assertEqual(s.get_task_state('42'), states.PENDING)
with self.assertRaises(exceptions.NotFound): self.assertRaises(exceptions.NotFound, s.get, '42')
s.get('42')
self.assertEqual(s.get_task_state('43'), states.PENDING) self.assertEqual(s.get_task_state('43'), states.PENDING)
with self.assertRaises(exceptions.NotFound): self.assertRaises(exceptions.NotFound, s.get, '43')
s.get('43')
def test_reset_tasks_does_not_breaks_inject(self): def test_reset_tasks_does_not_breaks_inject(self):
s = self._get_storage() s = self._get_storage()
@ -182,9 +178,9 @@ class StorageTest(test.TestCase):
def test_fetch_unknown_name(self): def test_fetch_unknown_name(self):
s = self._get_storage() s = self._get_storage()
with self.assertRaisesRegexp(exceptions.NotFound, self.assertRaisesRegexp(exceptions.NotFound,
"^Name 'xxx' is not mapped"): "^Name 'xxx' is not mapped",
s.fetch('xxx') s.fetch, 'xxx')
def test_default_task_progress(self): def test_default_task_progress(self):
s = self._get_storage() s = self._get_storage()
@ -230,8 +226,7 @@ class StorageTest(test.TestCase):
s.add_task('42', 'my task') s.add_task('42', 'my task')
name = 'my result' name = 'my result'
s.set_result_mapping('42', {name: None}) s.set_result_mapping('42', {name: None})
with self.assertRaises(exceptions.NotFound): self.assertRaises(exceptions.NotFound, s.get, name)
s.get(name)
self.assertEqual(s.fetch_all(), {}) self.assertEqual(s.fetch_all(), {})
def test_save_multiple_results(self): def test_save_multiple_results(self):
@ -297,8 +292,8 @@ class StorageTest(test.TestCase):
def test_fetch_not_found_args(self): def test_fetch_not_found_args(self):
s = self._get_storage() s = self._get_storage()
s.inject({'foo': 'bar', 'spam': 'eggs'}) s.inject({'foo': 'bar', 'spam': 'eggs'})
with self.assertRaises(exceptions.NotFound): self.assertRaises(exceptions.NotFound,
s.fetch_mapped_args({'viking': 'helmet'}) s.fetch_mapped_args, {'viking': 'helmet'})
def test_set_and_get_task_state(self): def test_set_and_get_task_state(self):
s = self._get_storage() s = self._get_storage()
@ -309,8 +304,8 @@ class StorageTest(test.TestCase):
def test_get_state_of_unknown_task(self): def test_get_state_of_unknown_task(self):
s = self._get_storage() s = self._get_storage()
with self.assertRaisesRegexp(exceptions.NotFound, '^Unknown'): self.assertRaisesRegexp(exceptions.NotFound, '^Unknown',
s.get_task_state('42') s.get_task_state, '42')
def test_task_by_name(self): def test_task_by_name(self):
s = self._get_storage() s = self._get_storage()
@ -319,9 +314,9 @@ class StorageTest(test.TestCase):
def test_unknown_task_by_name(self): def test_unknown_task_by_name(self):
s = self._get_storage() s = self._get_storage()
with self.assertRaisesRegexp(exceptions.NotFound, self.assertRaisesRegexp(exceptions.NotFound,
'^Unknown task name:'): '^Unknown task name:',
s.get_uuid_by_name('42') s.get_uuid_by_name, '42')
def test_initial_flow_state(self): def test_initial_flow_state(self):
s = self._get_storage() s = self._get_storage()
@ -348,9 +343,8 @@ class StorageTest(test.TestCase):
s.save('42', {}) s.save('42', {})
mocked_warning.assert_called_once_with( mocked_warning.assert_called_once_with(
mock.ANY, 'my task', 'key', 'result') mock.ANY, 'my task', 'key', 'result')
with self.assertRaisesRegexp(exceptions.NotFound, self.assertRaisesRegexp(exceptions.NotFound,
'^Unable to find result'): '^Unable to find result', s.fetch, 'result')
s.fetch('result')
@mock.patch.object(storage.LOG, 'warning') @mock.patch.object(storage.LOG, 'warning')
def test_empty_result_is_checked(self, mocked_warning): def test_empty_result_is_checked(self, mocked_warning):
@ -360,9 +354,8 @@ class StorageTest(test.TestCase):
s.save('42', ()) s.save('42', ())
mocked_warning.assert_called_once_with( mocked_warning.assert_called_once_with(
mock.ANY, 'my task', 0, 'a') mock.ANY, 'my task', 0, 'a')
with self.assertRaisesRegexp(exceptions.NotFound, self.assertRaisesRegexp(exceptions.NotFound,
'^Unable to find result'): '^Unable to find result', s.fetch, 'a')
s.fetch('a')
@mock.patch.object(storage.LOG, 'warning') @mock.patch.object(storage.LOG, 'warning')
def test_short_result_is_checked(self, mocked_warning): def test_short_result_is_checked(self, mocked_warning):
@ -373,9 +366,8 @@ class StorageTest(test.TestCase):
mocked_warning.assert_called_once_with( mocked_warning.assert_called_once_with(
mock.ANY, 'my task', 1, 'b') mock.ANY, 'my task', 1, 'b')
self.assertEqual(s.fetch('a'), 'result') self.assertEqual(s.fetch('a'), 'result')
with self.assertRaisesRegexp(exceptions.NotFound, self.assertRaisesRegexp(exceptions.NotFound,
'^Unable to find result'): '^Unable to find result', s.fetch, 'b')
s.fetch('b')
@mock.patch.object(storage.LOG, 'warning') @mock.patch.object(storage.LOG, 'warning')
def test_multiple_providers_are_checked(self, mocked_warning): def test_multiple_providers_are_checked(self, mocked_warning):

View File

@ -132,8 +132,7 @@ class SuspendFlowTest(utils.EngineTestBase):
['a', 'b', ['a', 'b',
'c reverted(Failure: RuntimeError: Woot!)', 'c reverted(Failure: RuntimeError: Woot!)',
'b reverted(5)']) 'b reverted(5)'])
with self.assertRaisesRegexp(RuntimeError, '^Woot'): self.assertRaisesRegexp(RuntimeError, '^Woot', engine.run)
engine.run()
self.assertEqual(engine.storage.get_flow_state(), states.REVERTED) self.assertEqual(engine.storage.get_flow_state(), states.REVERTED)
self.assertEqual( self.assertEqual(
self.values, self.values,
@ -155,8 +154,7 @@ class SuspendFlowTest(utils.EngineTestBase):
# pretend we are resuming # pretend we are resuming
engine2 = self._make_engine(flow, engine.storage._flowdetail) engine2 = self._make_engine(flow, engine.storage._flowdetail)
with self.assertRaisesRegexp(RuntimeError, '^Woot'): self.assertRaisesRegexp(RuntimeError, '^Woot', engine2.run)
engine2.run()
self.assertEqual(engine2.storage.get_flow_state(), states.REVERTED) self.assertEqual(engine2.storage.get_flow_state(), states.REVERTED)
self.assertEqual( self.assertEqual(
self.values, self.values,
@ -182,8 +180,7 @@ class SuspendFlowTest(utils.EngineTestBase):
AutoSuspendingTaskOnRevert(self.values, 'b') AutoSuspendingTaskOnRevert(self.values, 'b')
) )
engine2 = self._make_engine(flow2, engine.storage._flowdetail) engine2 = self._make_engine(flow2, engine.storage._flowdetail)
with self.assertRaisesRegexp(RuntimeError, '^Woot'): self.assertRaisesRegexp(RuntimeError, '^Woot', engine2.run)
engine2.run()
self.assertEqual(engine2.storage.get_flow_state(), states.REVERTED) self.assertEqual(engine2.storage.get_flow_state(), states.REVERTED)
self.assertEqual( self.assertEqual(
self.values, self.values,
@ -207,8 +204,7 @@ class SuspendFlowTest(utils.EngineTestBase):
engine.storage.get_uuid_by_name(engine.storage.injector_name), engine.storage.get_uuid_by_name(engine.storage.injector_name),
None, None,
states.FAILURE) states.FAILURE)
with self.assertRaises(exc.MissingDependencies): self.assertRaises(exc.MissingDependencies, engine.run)
engine.run()
class SingleThreadedEngineTest(SuspendFlowTest, class SingleThreadedEngineTest(SuspendFlowTest,

View File

@ -71,8 +71,8 @@ class TaskTestCase(test.TestCase):
self.assertEqual(my_task.save_as, {'food': 0}) self.assertEqual(my_task.save_as, {'food': 0})
def test_bad_provides(self): def test_bad_provides(self):
with self.assertRaisesRegexp(TypeError, '^Task provides'): self.assertRaisesRegexp(TypeError, '^Task provides',
MyTask(provides=object()) MyTask, provides=object())
def test_requires_by_default(self): def test_requires_by_default(self):
my_task = MyTask() my_task = MyTask()
@ -100,8 +100,9 @@ class TaskTestCase(test.TestCase):
}) })
def test_requires_explicit_not_enough(self): def test_requires_explicit_not_enough(self):
with self.assertRaisesRegexp(ValueError, '^Missing arguments'): self.assertRaisesRegexp(ValueError, '^Missing arguments',
MyTask(auto_extract=False, requires=('spam', 'eggs')) MyTask,
auto_extract=False, requires=('spam', 'eggs'))
def test_requires_ignores_optional(self): def test_requires_ignores_optional(self):
my_task = DefaultArgTask() my_task = DefaultArgTask()
@ -128,8 +129,8 @@ class TaskTestCase(test.TestCase):
}) })
def test_rebind_unknown(self): def test_rebind_unknown(self):
with self.assertRaisesRegexp(ValueError, '^Extra arguments'): self.assertRaisesRegexp(ValueError, '^Extra arguments',
MyTask(rebind={'foo': 'bar'}) MyTask, rebind={'foo': 'bar'})
def test_rebind_unknown_kwargs(self): def test_rebind_unknown_kwargs(self):
task = KwargsTask(rebind={'foo': 'bar'}) task = KwargsTask(rebind={'foo': 'bar'})
@ -155,8 +156,8 @@ class TaskTestCase(test.TestCase):
}) })
def test_rebind_list_more(self): def test_rebind_list_more(self):
with self.assertRaisesRegexp(ValueError, '^Extra arguments'): self.assertRaisesRegexp(ValueError, '^Extra arguments',
MyTask(rebind=('a', 'b', 'c', 'd')) MyTask, rebind=('a', 'b', 'c', 'd'))
def test_rebind_list_more_kwargs(self): def test_rebind_list_more_kwargs(self):
task = KwargsTask(rebind=('a', 'b', 'c')) task = KwargsTask(rebind=('a', 'b', 'c'))
@ -167,8 +168,8 @@ class TaskTestCase(test.TestCase):
}) })
def test_rebind_list_bad_value(self): def test_rebind_list_bad_value(self):
with self.assertRaisesRegexp(TypeError, '^Invalid rebind value:'): self.assertRaisesRegexp(TypeError, '^Invalid rebind value:',
MyTask(rebind=object()) MyTask, rebind=object())
def test_default_provides(self): def test_default_provides(self):
task = DefaultProvidesTask() task = DefaultProvidesTask()

View File

@ -227,13 +227,16 @@ class AttrDictTest(test.TestCase):
self.assertEqual(attrs, dict(obj)) self.assertEqual(attrs, dict(obj))
def test_runtime_invalid_set(self): def test_runtime_invalid_set(self):
def bad_assign(obj):
obj._123 = 'b'
attrs = { attrs = {
'a': 1, 'a': 1,
} }
obj = misc.AttrDict(**attrs) obj = misc.AttrDict(**attrs)
self.assertEqual(obj.a, 1) self.assertEqual(obj.a, 1)
with self.assertRaises(AttributeError): self.assertRaises(AttributeError, bad_assign, obj)
obj._123 = 'b'
def test_bypass_get(self): def test_bypass_get(self):
attrs = { attrs = {
@ -243,14 +246,17 @@ class AttrDictTest(test.TestCase):
self.assertEqual(1, obj['a']) self.assertEqual(1, obj['a'])
def test_bypass_set_no_get(self): def test_bypass_set_no_get(self):
def bad_assign(obj):
obj._b = 'e'
attrs = { attrs = {
'a': 1, 'a': 1,
} }
obj = misc.AttrDict(**attrs) obj = misc.AttrDict(**attrs)
self.assertEqual(1, obj['a']) self.assertEqual(1, obj['a'])
obj['_b'] = 'c' obj['_b'] = 'c'
with self.assertRaises(AttributeError): self.assertRaises(AttributeError, bad_assign, obj)
obj._b = 'e'
self.assertEqual('c', obj['_b']) self.assertEqual('c', obj['_b'])

View File

@ -75,8 +75,7 @@ class CaptureFailureTestCase(test.TestCase, GeneralFailureObjTestsMixin):
self.assertIs(exc_info[1], self.fail_obj.exception) self.assertIs(exc_info[1], self.fail_obj.exception)
def test_reraises(self): def test_reraises(self):
with self.assertRaisesRegexp(RuntimeError, '^Woot!$'): self.assertRaisesRegexp(RuntimeError, '^Woot!$', self.fail_obj.reraise)
self.fail_obj.reraise()
class ReCreatedFailureTestCase(test.TestCase, GeneralFailureObjTestsMixin): class ReCreatedFailureTestCase(test.TestCase, GeneralFailureObjTestsMixin):
@ -95,9 +94,8 @@ class ReCreatedFailureTestCase(test.TestCase, GeneralFailureObjTestsMixin):
self.assertIs(self.fail_obj.exc_info, None) self.assertIs(self.fail_obj.exc_info, None)
def test_reraises(self): def test_reraises(self):
with self.assertRaises(exceptions.WrappedFailure) as ctx: exc = self.assertRaises(exceptions.WrappedFailure,
self.fail_obj.reraise() self.fail_obj.reraise)
exc = ctx.exception
self.assertIs(exc.check(RuntimeError), RuntimeError) self.assertIs(exc.check(RuntimeError), RuntimeError)
@ -110,31 +108,30 @@ class FailureObjectTestCase(test.TestCase):
self.assertRaises(TypeError, misc.Failure) self.assertRaises(TypeError, misc.Failure)
def test_unknown_argument(self): def test_unknown_argument(self):
with self.assertRaises(TypeError) as ctx: exc = self.assertRaises(TypeError, misc.Failure,
misc.Failure( exception_str='Woot!',
exception_str='Woot!', traceback_str=None,
traceback_str=None, exc_type_names=['Exception'],
exc_type_names=['Exception'], hi='hi there')
hi='hi there')
expected = "Failure.__init__ got unexpected keyword argument(s): hi" expected = "Failure.__init__ got unexpected keyword argument(s): hi"
self.assertEqual(str(ctx.exception), expected) self.assertEqual(str(exc), expected)
def test_empty_does_not_reraise(self): def test_empty_does_not_reraise(self):
self.assertIs(misc.Failure.reraise_if_any([]), None) self.assertIs(misc.Failure.reraise_if_any([]), None)
def test_reraises_one(self): def test_reraises_one(self):
fls = [_captured_failure('Woot!')] fls = [_captured_failure('Woot!')]
with self.assertRaisesRegexp(RuntimeError, '^Woot!$'): self.assertRaisesRegexp(RuntimeError, '^Woot!$',
misc.Failure.reraise_if_any(fls) misc.Failure.reraise_if_any, fls)
def test_reraises_several(self): def test_reraises_several(self):
fls = [ fls = [
_captured_failure('Woot!'), _captured_failure('Woot!'),
_captured_failure('Oh, not again!') _captured_failure('Oh, not again!')
] ]
with self.assertRaises(exceptions.WrappedFailure) as ctx: exc = self.assertRaises(exceptions.WrappedFailure,
misc.Failure.reraise_if_any(fls) misc.Failure.reraise_if_any, fls)
self.assertEqual(list(ctx.exception), fls) self.assertEqual(list(exc), fls)
def test_failure_copy(self): def test_failure_copy(self):
fail_obj = _captured_failure('Woot!') fail_obj = _captured_failure('Woot!')

View File

@ -19,11 +19,14 @@
import logging import logging
import threading import threading
from eventlet.green import threading as gthreading try:
from eventlet.green import threading as gthreading
from eventlet import greenpool from eventlet import greenpool
from eventlet import patcher from eventlet import patcher
from eventlet import queue from eventlet import queue
EVENTLET_AVAILABLE = True
except ImportError:
EVENTLET_AVAILABLE = False
from concurrent import futures from concurrent import futures
@ -93,6 +96,7 @@ class GreenExecutor(futures.Executor):
"""A greenthread backed executor.""" """A greenthread backed executor."""
def __init__(self, max_workers=1000): def __init__(self, max_workers=1000):
assert EVENTLET_AVAILABLE, 'eventlet is needed to use GreenExecutor'
assert int(max_workers) > 0, 'Max workers must be greater than zero' assert int(max_workers) > 0, 'Max workers must be greater than zero'
self._max_workers = int(max_workers) self._max_workers = int(max_workers)
self._pool = greenpool.GreenPool(self._max_workers) self._pool = greenpool.GreenPool(self._max_workers)

View File

@ -32,6 +32,7 @@ from taskflow.utils import reflection
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
NUMERIC_TYPES = tuple(list(six.integer_types) + [float])
def wraps(fn): def wraps(fn):
@ -81,19 +82,29 @@ def is_valid_attribute_name(name, allow_self=False, allow_hidden=False):
return False return False
# Make the name just be a simple string in latin-1 encoding in python3 # Make the name just be a simple string in latin-1 encoding in python3
name = six.b(name) name = six.b(name)
if not allow_self and name.lower().startswith('self'): if not allow_self and name.lower().startswith(six.b('self')):
return False return False
if not allow_hidden and name.startswith("_"): if not allow_hidden and name.startswith(six.b("_")):
return False return False
# See: http://docs.python.org/release/2.5.2/ref/grammar.txt (or newer) # See: http://docs.python.org/release/2.5.2/ref/grammar.txt (or newer)
# #
# Python identifiers should start with a letter. # Python identifiers should start with a letter.
if not name[0].isalpha(): if isinstance(name[0], six.integer_types):
return False if not chr(name[0]).isalpha():
for i in range(1, len(name)):
# The rest of an attribute name follows: (letter | digit | "_")*
if not (name[i].isalpha() or name[i].isdigit() or name[i] == "_"):
return False return False
else:
if not name[0].isalpha():
return False
for i in range(1, len(name)):
symbol = name[i]
# The rest of an attribute name follows: (letter | digit | "_")*
if isinstance(symbol, six.integer_types):
symbol = chr(symbol)
if not (symbol.isalpha() or symbol.isdigit() or symbol == "_"):
return False
else:
if not (symbol.isalpha() or symbol.isdigit() or symbol == "_"):
return False
return True return True
@ -108,7 +119,6 @@ class AttrDict(dict):
if not is_valid_attribute_name(name): if not is_valid_attribute_name(name):
return False return False
# Make the name just be a simple string in latin-1 encoding in python3 # Make the name just be a simple string in latin-1 encoding in python3
name = six.b(name)
if name in cls.NO_ATTRS: if name in cls.NO_ATTRS:
return False return False
return True return True

View File

@ -207,7 +207,7 @@ def _format_meta(metadata, indent):
for (k, v) in metadata.items(): for (k, v) in metadata.items():
# Progress for now is a special snowflake and will be formatted # Progress for now is a special snowflake and will be formatted
# in percent format. # in percent format.
if k == 'progress' and isinstance(v, (float, int, long)): if k == 'progress' and isinstance(v, misc.NUMERIC_TYPES):
v = "%0.2f%%" % (v * 100.0) v = "%0.2f%%" % (v * 100.0)
lines.append("%s+ %s = %s" % (" " * (indent + 2), k, v)) lines.append("%s+ %s = %s" % (" " * (indent + 2), k, v))
return lines return lines

View File

@ -0,0 +1 @@
eventlet>=0.13.0

View File

@ -1,15 +1,6 @@
# Install bounded pep8/pyflakes first, then let flake8 install
pep8==1.4.5
pyflakes>=0.7.2,<0.7.4
flake8==2.0
hacking>=0.5.6,<0.8 hacking>=0.5.6,<0.8
discover
coverage>=3.6 coverage>=3.6
mock>=1.0 mock>=1.0
nose testrepository>=0.0.17
nose-exclude testtools>=0.9.32
openstack.nose_plugin>=0.7
pylint==0.25.2
# Needed for features in 2.7 not in 2.6
unittest2

20
tox.ini
View File

@ -10,15 +10,19 @@ setenv = VIRTUAL_ENV={envdir}
LANG=en_US.UTF-8 LANG=en_US.UTF-8
LANGUAGE=en_US:en LANGUAGE=en_US:en
LC_ALL=C LC_ALL=C
NOSE_WITH_OPENSTACK=1
NOSE_OPENSTACK_COLOR=1
NOSE_OPENSTACK_RED=0.05
NOSE_OPENSTACK_YELLOW=0.025
NOSE_OPENSTACK_SHOW_ELAPSED=1
NOSE_OPENSTACK_STDOUT=1
deps = -r{toxinidir}/requirements.txt deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt -r{toxinidir}/test-requirements.txt
commands = nosetests {posargs} commands = python setup.py testr --slowest --testr-args='{posargs}'
[testenv:py26]
deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
-r{toxinidir}/test-2.x-requirements.txt
[testenv:py27]
deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
-r{toxinidir}/test-2.x-requirements.txt
[tox:jenkins] [tox:jenkins]
downloadcache = ~/cache/pip downloadcache = ~/cache/pip
@ -34,7 +38,7 @@ deps = -r{toxinidir}/requirements.txt
commands = pylint commands = pylint
[testenv:cover] [testenv:cover]
setenv = NOSE_WITH_COVERAGE=1 commands = python setup.py testr --coverage --testr-args='{posargs}'
[testenv:venv] [testenv:venv]
commands = {posargs} commands = {posargs}