Skip to content

Commit ff52338

Browse files
committed
Fix optional dependency installation
1 parent c272cd2 commit ff52338

File tree

3 files changed

+57
-36
lines changed

3 files changed

+57
-36
lines changed

src/ansible_dev_environment/subcommands/installer.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from ansible_dev_environment.utils import (
1717
builder_introspect,
1818
collections_from_requirements,
19+
opt_deps_to_files,
1920
oxford_join,
2021
subprocess_run,
2122
)
@@ -77,6 +78,8 @@ def run(self) -> None:
7778

7879
if self._config.args.requirement or self._config.args.cpi:
7980
self._install_galaxy_requirements()
81+
82+
opt_dep_paths = None
8083
if self._config.args.collection_specifier:
8184
collections = [
8285
parse_collection_request(
@@ -98,7 +101,15 @@ def run(self) -> None:
98101
self._output.critical(msg)
99102
self._install_galaxy_collections(collections=distant_collections)
100103

101-
builder_introspect(config=self._config, output=self._output)
104+
opt_dep_paths = [
105+
path
106+
for collection in collections
107+
for path in opt_deps_to_files(collection, self._output)
108+
]
109+
msg = f"Optional dependencies found: {oxford_join(opt_dep_paths)}"
110+
self._output.info(msg)
111+
112+
builder_introspect(config=self._config, opt_dep_paths=opt_dep_paths, output=self._output)
102113
self._pip_install()
103114
Checker(config=self._config, output=self._output).system_deps()
104115

src/ansible_dev_environment/utils.py

Lines changed: 37 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,15 @@
1818

1919

2020
if TYPE_CHECKING:
21+
from collections.abc import Sequence
2122
from pathlib import Path
2223
from types import TracebackType
2324

25+
from .collection import Collection
2426
from .config import Config
2527
from .output import Output
2628

29+
2730
from typing import Any
2831

2932

@@ -156,50 +159,58 @@ def subprocess_run( # pylint: disable=too-many-positional-arguments
156159
)
157160

158161

159-
def oxford_join(words: list[str]) -> str:
162+
def oxford_join(words: Sequence[str | Path]) -> str:
160163
"""Join a list of words with commas and an oxford comma.
161164
162165
Args:
163166
words: A list of words to join
164167
Returns:
165168
A string of words joined with commas and an oxford comma
166169
"""
167-
words.sort()
168-
if not words:
170+
_words = sorted([str(word) for word in words])
171+
if not _words:
169172
return ""
170-
if len(words) == 1:
171-
return words[0]
172-
if len(words) == 2: # noqa: PLR2004
173-
return " and ".join(words)
174-
return ", ".join(words[:-1]) + ", and " + words[-1]
173+
if len(_words) == 1:
174+
return _words[0]
175+
if len(_words) == 2: # noqa: PLR2004
176+
return " and ".join(_words)
177+
return ", ".join(_words[:-1]) + ", and " + _words[-1]
175178

176179

177-
def opt_deps_to_files(collection_path: Path, dep_str: str) -> list[Path]:
180+
def opt_deps_to_files(collection: Collection, output: Output) -> list[Path]:
178181
"""Convert a string of optional dependencies to a list of files.
179182
180183
Args:
181-
collection_path: The path to the collection
182-
dep_str: A string of optional dependencies
184+
collection: The collection object
185+
output: The output object
183186
Returns:
184-
A list of files
187+
A list of paths
185188
"""
186-
deps = dep_str.split(",")
189+
if not collection.opt_deps:
190+
msg = "No optional dependencies specified."
191+
output.debug(msg)
192+
return []
193+
194+
deps = collection.opt_deps.split(",")
187195
files = []
188196
for dep in deps:
189197
_dep = dep.strip()
190-
variant1 = collection_path / f"{_dep}-requirements.txt"
198+
variant1 = collection.path / f"{_dep}-requirements.txt"
191199
if variant1.exists():
192200
files.append(variant1)
193201
continue
194-
variant2 = collection_path / f"requirements-{_dep}.txt"
202+
variant2 = collection.path / f"requirements-{_dep}.txt"
195203
if variant2.exists():
196204
files.append(variant2)
197205
continue
198206
msg = (
199207
f"Failed to find optional dependency file for '{_dep}'."
200208
f" Checked for '{variant1.name}' and '{variant2.name}'. Skipping."
201209
)
202-
logger.error(msg)
210+
output.error(msg)
211+
count = len(files)
212+
msg = f"Found {count} optional dependency file{'s' * (count > 1)}. {oxford_join(files)}"
213+
output.debug(msg)
203214
return files
204215

205216

@@ -277,7 +288,11 @@ def collect_manifests( # noqa: C901
277288
return sort_dict(collections)
278289

279290

280-
def builder_introspect(config: Config, output: Output) -> None:
291+
def builder_introspect(
292+
config: Config,
293+
output: Output,
294+
opt_dep_paths: list[Path] | None = None,
295+
) -> None:
281296
"""Introspect a collection.
282297
283298
Use the sys executable to run builder, since it is a direct dependency
@@ -286,29 +301,20 @@ def builder_introspect(config: Config, output: Output) -> None:
286301
Args:
287302
config: The configuration object.
288303
output: The output object.
304+
opt_dep_paths: A list of optional dependency paths.
289305
"""
290306
command = (
291307
f"{sys.executable} -m ansible_builder introspect {config.site_pkg_path}"
292308
f" --write-pip {config.discovered_python_reqs}"
293309
f" --write-bindep {config.discovered_bindep_reqs}"
294310
" --sanitize"
295311
)
296-
if (
297-
hasattr(config.args, "collection_specifier")
298-
and hasattr(config, "collection")
299-
and config.collection.opt_deps
300-
and config.collection.path
301-
):
302-
dep_paths = opt_deps_to_files(
303-
collection_path=config.collection.path,
304-
dep_str=config.collection.opt_deps,
305-
)
306-
for dep_path in dep_paths:
307-
command += f" --user-pip {dep_path}"
312+
for opt_dep_path in opt_dep_paths or []:
313+
command += f" --user-pip {opt_dep_path}"
308314
msg = f"Writing discovered python requirements to: {config.discovered_python_reqs}"
309-
logger.debug(msg)
315+
output.debug(msg)
310316
msg = f"Writing discovered system requirements to: {config.discovered_bindep_reqs}"
311-
logger.debug(msg)
317+
output.debug(msg)
312318
work = "Persisting requirements to file system"
313319
try:
314320
subprocess_run(

tests/integration/test_basic.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ def test_venv(
2626
) -> None:
2727
"""Basic smoke test.
2828
29+
Test for a local collection install with optional dependencies
30+
2931
Args:
3032
capsys: Capture stdout and stderr
3133
tmp_path: Temporary directory
@@ -56,17 +58,19 @@ def test_venv(
5658
[
5759
"ade",
5860
"install",
59-
str(tmp_path / "cisco.nxos"),
61+
str(tmp_path / "cisco.nxos[test]"),
6062
"--venv=venv",
6163
"--no-ansi",
64+
"-vvv",
6265
],
6366
)
6467
with pytest.raises(SystemExit):
6568
main()
66-
string = "Installed collections include: ansible.netcommon, ansible.utils,"
67-
captured = capsys.readouterr()
6869

69-
assert string in captured.out
70+
captured = capsys.readouterr()
71+
assert "Installed collections include: ansible.netcommon, ansible.utils," in captured.out
72+
assert "Optional dependencies found" in captured.out
73+
assert "'pytest-xdist # from collection user'" in captured.out
7074

7175
monkeypatch.setattr(
7276
"sys.argv",

0 commit comments

Comments
 (0)