Skip to content

Commit 520052d

Browse files
authored
Support complex recursive dependency category specification (#646)
When computing the dependency graph, the existing API exposes a parameter for indicating which direct dependency categories to collect as well as what indirect (recursive) dependency categories to collect. This change allows the caller to specify different recursive dependency categories depending on which category included the dependency in the graph to begin with.
1 parent 1aa845d commit 520052d

File tree

4 files changed

+51
-11
lines changed

4 files changed

+51
-11
lines changed

colcon_core/package_decorator.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,9 @@ def add_recursive_dependencies(
4646
4747
:param set decorators: The known packages to consider
4848
:param Iterable[str] direct_categories: The names of the direct categories
49-
:param Iterable[str] recursive_categories: The names of the recursive
50-
categories
49+
:param Iterable[str]|Mapping[str, Iterable[str]] recursive_categories:
50+
The names of the recursive categories, optionally mapped from the
51+
immediate upstream category which included the dependency
5152
"""
5253
descriptors = [decorator.descriptor for decorator in decorators]
5354
for decorator in decorators:

colcon_core/package_descriptor.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# Licensed under the Apache License, Version 2.0
33

44
from collections import defaultdict
5+
from collections.abc import Mapping
56
from copy import deepcopy
67
import os
78
from pathlib import Path
@@ -105,12 +106,15 @@ def get_recursive_dependencies(
105106
consider
106107
:param Iterable[str] direct_categories: The names of the direct
107108
categories
108-
:param Iterable[str] recursive_categories: The names of the recursive
109-
categories
109+
:param Iterable[str]|Mapping[str, Iterable[str]] recursive_categories:
110+
The names of the recursive categories, optionally mapped from the
111+
immediate upstream category which included the dependency
110112
:returns: The dependencies
111113
:rtype: set[DependencyDescriptor]
112114
:raises AssertionError: if a package lists itself as a dependency
113115
"""
116+
if not isinstance(recursive_categories, Mapping):
117+
recursive_categories = defaultdict(lambda: recursive_categories)
114118
# the following variable only exists for faster access within the loop
115119
descriptors_by_name = defaultdict(set)
116120
for d in descriptors:
@@ -132,11 +136,17 @@ def get_recursive_dependencies(
132136
descs = descriptors_by_name[dep]
133137
if not descs:
134138
continue
139+
categories = set()
140+
for category in dep.metadata['categories']:
141+
cats = recursive_categories.get(category)
142+
if cats is None:
143+
categories = None
144+
break
145+
categories.update(cats)
135146
# recursing into the same function of the dependency descriptor
136147
# queue recursive dependencies
137148
for d in descs:
138-
queue |= d.get_dependencies(
139-
categories=recursive_categories)
149+
queue |= d.get_dependencies(categories=categories)
140150
# add the depth
141151
dep.metadata['depth'] = depth
142152
# add dependency to result set

colcon_core/package_selection/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,9 @@ def get_packages(
138138
:param additional_argument_names: A list of additional arguments to
139139
consider
140140
:param Iterable[str] direct_categories: The names of the direct categories
141-
:param Iterable[str] recursive_categories: The names of the recursive
142-
categories
141+
:param Iterable[str]|Mapping[str, Iterable[str]] recursive_categories:
142+
The names of the recursive categories, optionally mapped from the
143+
immediate upstream category which included the dependency
143144
:rtype: list
144145
:raises RuntimeError: if the returned set of packages contains duplicates
145146
package names

test/test_package_descriptor.py

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Copyright 2016-2018 Dirk Thomas
22
# Licensed under the Apache License, Version 2.0
33

4+
from collections import defaultdict
45
import os
56
from pathlib import Path
67

@@ -49,7 +50,8 @@ def test_get_dependencies():
4950
assert "'self'" in str(e.value)
5051

5152

52-
def test_get_recursive_dependencies():
53+
@pytest.fixture
54+
def recursive_dependencies():
5355
d = PackageDescriptor('/some/path')
5456
d.name = 'A'
5557
d.dependencies['build'].add('B')
@@ -70,6 +72,7 @@ def test_get_recursive_dependencies():
7072
d3.dependencies['build'].add('h')
7173
d3.dependencies['test'].add('G')
7274
d3.dependencies['test'].add('I')
75+
d3.dependencies['test'].add('J')
7376

7477
d4 = PackageDescriptor('/more/path')
7578
d4.name = 'G'
@@ -80,10 +83,35 @@ def test_get_recursive_dependencies():
8083
# circular dependencies should be ignored
8184
d5.dependencies['run'].add('A')
8285

83-
rec_deps = d.get_recursive_dependencies(
84-
{d, d1, d2, d3, d4, d5},
86+
d6 = PackageDescriptor('/paths/galore')
87+
d6.name = 'J'
88+
89+
return d, {d, d1, d2, d3, d4, d5, d6}
90+
91+
92+
def test_get_recursive_dependencies(recursive_dependencies):
93+
desc, all_descs = recursive_dependencies
94+
rec_deps = desc.get_recursive_dependencies(
95+
all_descs,
8596
direct_categories=('build', 'run'),
8697
recursive_categories=('run', 'test'))
98+
assert rec_deps == {
99+
# direct dependencies
100+
'B',
101+
# recursive dependencies
102+
'F', 'G', 'I', 'J',
103+
}
104+
105+
106+
def test_get_recursive_dependencies_map(recursive_dependencies):
107+
recursive_categories = defaultdict(lambda: ('run', 'test'))
108+
recursive_categories['run'] = ('run',)
109+
110+
desc, all_descs = recursive_dependencies
111+
rec_deps = desc.get_recursive_dependencies(
112+
all_descs,
113+
direct_categories=('build', 'run'),
114+
recursive_categories=recursive_categories)
87115
assert rec_deps == {
88116
# direct dependencies
89117
'B',

0 commit comments

Comments
 (0)