Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion colcon_parallel_executor/executor/parallel.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,9 @@ async def _execute(self, args, jobs, *, on_error):
f.identifier for f in futures.values())))

# check results of done futures
for done_future in done_futures:
for done_future in [
f for f in futures.keys() if f in done_futures

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

lgtm! If any done_future is not in future keys, this is silently dropped. Which is perhaps what we want? before we got an error in the next line

]:
job = futures[done_future]
del futures[done_future]
# get result without raising an exception
Expand Down
27 changes: 20 additions & 7 deletions test/test_executor_parallel.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ def test_parallel():

args = SimpleNamespace(parallel_workers=2)
jobs = OrderedDict()
jobs['one'] = Job1()
jobs['job1'] = Job1()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Interestingly, it seems that these renames actually has an unexpected effect with the dependency checker that before would just give everything the same priority, namely this one, since one/two/three didn't match the keys job1, job2:

recursive_dependent_counts = {}
for package_name, job in jobs.items():
# ignore "self" dependency
recursive_dependent_counts[package_name] = len([
j for name, j in jobs.items()
if package_name != name and package_name in j.dependencies])

I believe that now that the scheduler actually gives a priority to the jobs that others depend on.

I do wonder if that will interfere with the behavior of having first initialized jobs finish first.

Anyway, seems harmless but just something to keep in mind. Otherwise I'm always for consistency!


# success
rc = extension.execute(args, jobs)
Expand All @@ -119,8 +119,8 @@ def test_parallel():
ran_jobs.clear()

# return error code
jobs['two'] = Job2()
jobs['four'] = Job4()
jobs['job2'] = Job2()
jobs['job4'] = Job4()
rc = extension.execute(args, jobs)
assert rc == 2
assert ran_jobs == ['job1']
Expand All @@ -144,22 +144,22 @@ def test_parallel():
ran_jobs.clear()

# continue after error, keeping first error code
jobs['five'] = Job5()
jobs['job5'] = Job5()
rc = extension.execute(args, jobs, on_error=OnError.continue_)
assert rc == 2
assert ran_jobs == ['job1', 'job4']
ran_jobs.clear()

# continue but skip downstream
jobs['six'] = Job6()
jobs['seven'] = Job7()
jobs['job6'] = Job6()
jobs['job7'] = Job7()
rc = extension.execute(args, jobs, on_error=OnError.skip_downstream)
assert rc == 2
assert ran_jobs == ['job1', 'job7', 'job4']
ran_jobs.clear()

# exception
jobs['two'] = Job3()
jobs['job2'] = Job3()
rc = extension.execute(args, jobs)
assert isinstance(rc, RuntimeError)
assert ran_jobs == ['job1']
Expand Down Expand Up @@ -400,3 +400,16 @@ def test_parallel_workers_zero():
assert rc == 0
assert set(ran_jobs) == {'job1', 'job2'}
ran_jobs.clear()


def test_parallel_exception_skip_pending():
extension = ParallelExecutorExtension()
args = SimpleNamespace(parallel_workers=1)
jobs = OrderedDict()
jobs['job3'] = Job3()
jobs['job4'] = Job4()

rc = extension.execute(args, jobs, on_error=OnError.skip_pending)
assert isinstance(rc, RuntimeError)
assert ran_jobs == []
ran_jobs.clear()
Loading