Fix lookup scoping multi-match ordering

When look-up is occurring the possible provider
ordering is used instead of the scope returned provider
ordering. This causes incorrect matches when look-up
argument names that are produced by multiple providers (at
the same scope level) all providing the same requirement
name.

The scope order should be enforced as the de-facto order
and not the order that the storage unit finds (which is
hash based and varies depending on hash ordering).

Closes-Bug: #1425326

Change-Id: I15f1ee5515758bdc470c0f7dd7a2f616923e5628
This commit is contained in:
Joshua Harlow
2015-02-24 16:19:04 -08:00
committed by Joshua Harlow
parent 7f9e44d501
commit 0a97fb96b5
6 changed files with 101 additions and 9 deletions

View File

@@ -657,15 +657,17 @@ class Storage(object):
scope_iter = iter(scope_walker)
else:
scope_iter = iter([])
for atom_names in scope_iter:
if not atom_names:
continue
providers = []
for p in possible_providers:
if p.name in atom_names:
providers.append((p, _get_results(looking_for, p)))
extractor = lambda p: p.name
for names in scope_iter:
# *Always* retain the scope ordering (if any matches
# happen); instead of retaining the possible provider match
# order (which isn't that important and may be different from
# the scope requested ordering).
providers = misc.look_for(names, possible_providers,
extractor=extractor)
if providers:
return providers
return [(p, _get_results(looking_for, p))
for p in providers]
return []
with self._lock.read_lock():

View File

@@ -156,6 +156,40 @@ class EngineLinearFlowTest(utils.EngineTestBase):
engine = self._make_engine(flow)
self.assertRaises(exc.Empty, engine.run)
def test_overlap_parent_sibling_expected_result(self):
flow = lf.Flow('flow-1')
flow.add(utils.ProgressingTask(provides='source'))
flow.add(utils.TaskOneReturn(provides='source'))
subflow = lf.Flow('flow-2')
subflow.add(utils.AddOne())
flow.add(subflow)
engine = self._make_engine(flow)
engine.run()
results = engine.storage.fetch_all()
self.assertEqual(2, results['result'])
def test_overlap_parent_expected_result(self):
flow = lf.Flow('flow-1')
flow.add(utils.ProgressingTask(provides='source'))
subflow = lf.Flow('flow-2')
subflow.add(utils.TaskOneReturn(provides='source'))
subflow.add(utils.AddOne())
flow.add(subflow)
engine = self._make_engine(flow)
engine.run()
results = engine.storage.fetch_all()
self.assertEqual(2, results['result'])
def test_overlap_sibling_expected_result(self):
flow = lf.Flow('flow-1')
flow.add(utils.ProgressingTask(provides='source'))
flow.add(utils.TaskOneReturn(provides='source'))
flow.add(utils.AddOne())
engine = self._make_engine(flow)
engine.run()
results = engine.storage.fetch_all()
self.assertEqual(2, results['result'])
def test_sequential_flow_one_task(self):
flow = lf.Flow('flow-1').add(
utils.ProgressingTask(name='task1')

View File

@@ -244,6 +244,24 @@ class TestCountdownIter(test.TestCase):
self.assertRaises(ValueError, six.next, it)
class TestLookFor(test.TestCase):
def test_no_matches(self):
hay = [9, 10, 11]
self.assertEqual([], misc.look_for(hay, [1, 2, 3]))
def test_match_order(self):
hay = [6, 5, 4, 3, 2, 1]
priors = []
for i in range(0, 6):
priors.append(i + 1)
matches = misc.look_for(hay, priors)
self.assertGreater(0, len(matches))
self.assertIsSuperAndSubsequence(hay, matches)
hay = [10, 1, 15, 3, 5, 8, 44]
self.assertEqual([1, 15], misc.look_for(hay, [15, 1]))
self.assertEqual([10, 44], misc.look_for(hay, [44, 10]))
class TestClamping(test.TestCase):
def test_simple_clamp(self):
result = misc.clamp(1.0, 2.0, 3.0)

View File

@@ -34,7 +34,7 @@ class TestWorker(test.MockTestCase):
self.exchange = 'test-exchange'
self.topic = 'test-topic'
self.threads_count = 5
self.endpoint_count = 23
self.endpoint_count = 24
# patch classes
self.executor_mock, self.executor_inst_mock = self.patchClass(

View File

@@ -89,6 +89,13 @@ class DummyTask(task.Task):
pass
class AddOne(task.Task):
default_provides = 'result'
def execute(self, source):
return source + 1
class FakeTask(object):
def execute(self, **kwargs):

View File

@@ -196,6 +196,37 @@ def parse_uri(uri):
return netutils.urlsplit(uri)
def look_for(haystack, needles, extractor=None):
"""Find items in haystack and returns matches found (in haystack order).
Given a list of items (the haystack) and a list of items to look for (the
needles) this will look for the needles in the haystack and returns
the found needles (if any). The ordering of the returned needles is in the
order they are located in the haystack.
Example input and output:
>>> from taskflow.utils import misc
>>> hay = [3, 2, 1]
>>> misc.look_for(hay, [1, 2])
[2, 1]
"""
if not haystack:
return []
if extractor is None:
extractor = lambda v: v
matches = []
for i, v in enumerate(needles):
try:
matches.append((haystack.index(extractor(v)), i))
except ValueError:
pass
if not matches:
return []
else:
return [needles[i] for (_hay_i, i) in sorted(matches)]
def clamp(value, minimum, maximum, on_clamped=None):
"""Clamps a value to ensure its >= minimum and <= maximum."""
if minimum > maximum: