Skip to content

Commit ee16f7c

Browse files
committed
Add as_packages parameter to find_shortest_chain
The implementation of the pathfinding already supports this, so let's add this to the public API.
1 parent 6076f49 commit ee16f7c

6 files changed

Lines changed: 44 additions & 5 deletions

File tree

CHANGELOG.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
Changelog
33
=========
44

5+
Unreleased
6+
----------
7+
8+
* Added `as_packages` option to the `find_shortest_chain` method.
9+
510
3.6 (2025-02-07)
611
----------------
712

docs/usage.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,10 @@ Methods for analysing import chains
221221
:param str importer: The module at the start of a potential chain of imports between ``importer`` and ``imported``
222222
(i.e. the module that potentially imports ``imported``, even indirectly).
223223
:param str imported: The module at the end of the potential chain of imports.
224+
:param bool as_packages: Whether to treat the supplied modules as individual modules,
225+
or as packages (including any descendants, if there are any). If
226+
treating them as packages, all descendants of ``importer`` and
227+
``imported`` will be checked too.
224228
:return: The shortest chain of imports between the supplied modules, or None if no chain exists.
225229
:rtype: A tuple of strings, ordered from importer to imported modules, or None.
226230

rust/src/lib.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -296,17 +296,18 @@ impl GraphWrapper {
296296
.collect())
297297
}
298298

299-
// TODO(peter) Add `as_packages` argument here? The implementation already supports it!
299+
#[pyo3(signature = (importer, imported, as_packages=false))]
300300
pub fn find_shortest_chain(
301301
&self,
302302
importer: &str,
303303
imported: &str,
304+
as_packages: bool,
304305
) -> PyResult<Option<Vec<String>>> {
305306
let importer = self.get_visible_module_by_name(importer)?.token();
306307
let imported = self.get_visible_module_by_name(imported)?.token();
307308
Ok(self
308309
._graph
309-
.find_shortest_chain(importer, imported, false)?
310+
.find_shortest_chain(importer, imported, as_packages)?
310311
.map(|chain| {
311312
chain
312313
.iter()

src/grimp/adaptors/graph.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,12 +108,14 @@ def find_downstream_modules(self, module: str, as_package: bool = False) -> Set[
108108
def find_upstream_modules(self, module: str, as_package: bool = False) -> Set[str]:
109109
return self._rustgraph.find_upstream_modules(module, as_package)
110110

111-
def find_shortest_chain(self, importer: str, imported: str) -> tuple[str, ...] | None:
111+
def find_shortest_chain(
112+
self, importer: str, imported: str, as_packages: bool = False
113+
) -> tuple[str, ...] | None:
112114
for module in (importer, imported):
113115
if not self._rustgraph.contains_module(module):
114116
raise ValueError(f"Module {module} is not present in the graph.")
115117

116-
chain = self._rustgraph.find_shortest_chain(importer, imported)
118+
chain = self._rustgraph.find_shortest_chain(importer, imported, as_packages)
117119
return tuple(chain) if chain else None
118120

119121
def find_shortest_chains(

src/grimp/application/ports/graph.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,11 +220,19 @@ def find_upstream_modules(self, module: str, as_package: bool = False) -> Set[st
220220
raise NotImplementedError
221221

222222
@abc.abstractmethod
223-
def find_shortest_chain(self, importer: str, imported: str) -> tuple[str, ...] | None:
223+
def find_shortest_chain(
224+
self, importer: str, imported: str, as_packages: bool = False
225+
) -> tuple[str, ...] | None:
224226
"""
225227
Attempt to find the shortest chain of imports between two modules, in the direction
226228
of importer to imported.
227229
230+
Optional args:
231+
as_packages: Whether to treat the supplied modules as individual modules,
232+
or as packages (including any descendants, if there are any). If
233+
treating them as subpackages, all descendants of the supplied modules
234+
will be checked too.
235+
228236
Returns:
229237
Tuple of module names, from importer to imported, or None if no chain exists.
230238
"""

tests/unit/adaptors/graph/test_chains.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,25 @@ def test_demonstrate_nondeterminism_of_equal_chains(self):
168168
other_chain = (source, d, e, f, destination)
169169
assert (result == one_chain) or (result == other_chain)
170170

171+
@pytest.mark.parametrize(
172+
"as_packages, expected_result",
173+
(
174+
(False, None),
175+
(True, ("green.foo", "blue.bar")),
176+
),
177+
)
178+
def test_as_packages(self, as_packages: bool, expected_result: Set[Tuple]):
179+
graph = ImportGraph()
180+
graph.add_module("green")
181+
graph.add_module("blue")
182+
graph.add_import(importer="green.foo", imported="blue.bar")
183+
184+
result = graph.find_shortest_chain(
185+
importer="green", imported="blue", as_packages=as_packages
186+
)
187+
188+
assert result == expected_result
189+
171190

172191
class TestFindShortestChains:
173192
@pytest.mark.parametrize(

0 commit comments

Comments
 (0)