Skip to content

Commit 18a2355

Browse files
pkg._version should be added to exhaustive_ignores in Import Linter inference (#1427)
Fixes #1423
1 parent fabae30 commit 18a2355

3 files changed

Lines changed: 135 additions & 0 deletions

File tree

src/usethis/_integrations/project/imports.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,17 @@ def _get_module_layered_architecture(
8888
if m not in layered:
8989
excluded.add(m)
9090

91+
# _version is typically auto-generated by VCS plugins (e.g. setuptools-scm,
92+
# hatch-vcs) and should not participate in the layered architecture.
93+
# Instead, it is excluded so it appears in exhaustive_ignores.
94+
# Ref: https://github.com/usethis-python/usethis-python/issues/1423
95+
_VERSION_MODULE = "_version"
96+
for layer in layers:
97+
if _VERSION_MODULE in layer:
98+
layer.discard(_VERSION_MODULE)
99+
excluded.add(_VERSION_MODULE)
100+
layers = [layer for layer in layers if layer]
101+
91102
return LayeredArchitecture(
92103
layers=list(reversed(layers)),
93104
excluded=excluded,

tests/usethis/_core/test_core_tool.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1188,6 +1188,49 @@ def test_cyclic_excluded(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch):
11881188
"""
11891189
)
11901190

1191+
@pytest.mark.usefixtures("_vary_network_conn")
1192+
def test_version_excluded(
1193+
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
1194+
):
1195+
"""_version.py is auto-excluded into exhaustive_ignores.
1196+
1197+
Ref: https://github.com/usethis-python/usethis-python/issues/1423
1198+
"""
1199+
# Arrange
1200+
(tmp_path / ".importlinter").touch()
1201+
(tmp_path / "a").mkdir()
1202+
(tmp_path / "a" / "__init__.py").touch()
1203+
(tmp_path / "a" / "b.py").touch()
1204+
(tmp_path / "a" / "c.py").touch()
1205+
(tmp_path / "a" / "_version.py").touch()
1206+
1207+
monkeypatch.syspath_prepend(str(tmp_path))
1208+
1209+
# Act
1210+
with change_cwd(tmp_path), files_manager():
1211+
use_import_linter()
1212+
1213+
# Assert
1214+
contents = (tmp_path / ".importlinter").read_text()
1215+
assert contents == (
1216+
"""\
1217+
[importlinter]
1218+
root_packages =
1219+
a
1220+
1221+
[importlinter:contract:0]
1222+
name = a
1223+
type = layers
1224+
layers =
1225+
b | c
1226+
containers =
1227+
a
1228+
exhaustive = True
1229+
exhaustive_ignores =
1230+
_version
1231+
"""
1232+
)
1233+
11911234
@pytest.mark.usefixtures("_vary_network_conn")
11921235
def test_existing_ini_match(self, tmp_path: Path):
11931236
# Arrange

tests/usethis/_integrations/project/test_imports.py

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,87 @@ def test_bottom_heavy(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch):
204204
assert arch.layers == [{"f", "g"}, {"d", "e"}, {"a", "b", "c"}]
205205
assert arch.excluded == set()
206206

207+
def test_version_excluded_with_others(
208+
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
209+
):
210+
"""_version is excluded even when sharing a layer with other modules.
211+
212+
Ref: https://github.com/usethis-python/usethis-python/issues/1423
213+
"""
214+
# Arrange
215+
(tmp_path / "salut").mkdir()
216+
(tmp_path / "salut" / "__init__.py").touch()
217+
(tmp_path / "salut" / "a.py").touch()
218+
(tmp_path / "salut" / "b.py").touch()
219+
(tmp_path / "salut" / "_version.py").touch()
220+
221+
monkeypatch.syspath_prepend(str(tmp_path))
222+
223+
# Act
224+
with change_cwd(tmp_path):
225+
graph = _get_graph("salut")
226+
arch = _get_module_layered_architecture("salut", graph=graph)
227+
228+
# Assert
229+
assert arch.layers == [{"a", "b"}]
230+
assert arch.excluded == {"_version"}
231+
232+
def test_version_excluded_sole_in_layer(
233+
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
234+
):
235+
"""_version is excluded even when it is the sole module in its layer.
236+
237+
The empty layer should be cleaned up.
238+
239+
Ref: https://github.com/usethis-python/usethis-python/issues/1423
240+
"""
241+
# Arrange
242+
(tmp_path / "salut").mkdir()
243+
(tmp_path / "salut" / "__init__.py").touch()
244+
(tmp_path / "salut" / "a.py").touch()
245+
(tmp_path / "salut" / "b.py").write_text("""\
246+
import salut.a
247+
""")
248+
(tmp_path / "salut" / "_version.py").touch()
249+
250+
monkeypatch.syspath_prepend(str(tmp_path))
251+
252+
# Act
253+
with change_cwd(tmp_path):
254+
graph = _get_graph("salut")
255+
arch = _get_module_layered_architecture("salut", graph=graph)
256+
257+
# Assert
258+
assert arch.layers == [{"b"}, {"a"}]
259+
assert arch.excluded == {"_version"}
260+
261+
def test_version_excluded_with_deps(
262+
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
263+
):
264+
"""_version is excluded even when other modules import it.
265+
266+
Ref: https://github.com/usethis-python/usethis-python/issues/1423
267+
"""
268+
# Arrange
269+
(tmp_path / "salut").mkdir()
270+
(tmp_path / "salut" / "__init__.py").touch()
271+
(tmp_path / "salut" / "a.py").write_text("""\
272+
import salut._version
273+
""")
274+
(tmp_path / "salut" / "b.py").touch()
275+
(tmp_path / "salut" / "_version.py").touch()
276+
277+
monkeypatch.syspath_prepend(str(tmp_path))
278+
279+
# Act
280+
with change_cwd(tmp_path):
281+
graph = _get_graph("salut")
282+
arch = _get_module_layered_architecture("salut", graph=graph)
283+
284+
# Assert
285+
assert arch.layers == [{"a"}, {"b"}]
286+
assert arch.excluded == {"_version"}
287+
207288

208289
class TestGetChildDependencies:
209290
def test_three(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch):

0 commit comments

Comments
 (0)