From d51c84c6620727afad86f042800abd63b9833a52 Mon Sep 17 00:00:00 2001 From: "Bradley A. Thornton" Date: Fri, 4 Apr 2025 04:52:28 -0700 Subject: [PATCH 1/3] Find builder adjacent to python --- src/ansible_dev_environment/utils.py | 17 ++++++- tests/unit/test_utils.py | 72 +++++++++++++++++++++++++++- 2 files changed, 86 insertions(+), 3 deletions(-) diff --git a/src/ansible_dev_environment/utils.py b/src/ansible_dev_environment/utils.py index 9cc2d62..60c9f82 100644 --- a/src/ansible_dev_environment/utils.py +++ b/src/ansible_dev_environment/utils.py @@ -11,6 +11,7 @@ import time from dataclasses import dataclass +from pathlib import Path from typing import TYPE_CHECKING import subprocess_tee @@ -18,7 +19,6 @@ if TYPE_CHECKING: - from pathlib import Path from types import TracebackType from .config import Config @@ -280,12 +280,25 @@ def collect_manifests( # noqa: C901 def builder_introspect(config: Config, output: Output) -> None: """Introspect a collection. + Default to the path of ansible-builder adjacent to the python executable. + Since ansible-builder is a dependency of ade, it should be in the same bin directory + as the python interpreter that spawned the ade process. + If not found, we cannot introspect. + Args: config: The configuration object. output: The output object. """ + builder_path = Path(sys.executable).parent / "ansible-builder" + if not builder_path.exists(): + output.critical( + "Failed to find ansible-builder. Please check the installation" + " of ade as it should have been installed by default.", + ) + return # pragma: no cover + command = ( - f"ansible-builder introspect {config.site_pkg_path}" + f"{builder_path} introspect {config.site_pkg_path}" f" --write-pip {config.discovered_python_reqs}" f" --write-bindep {config.discovered_bindep_reqs}" " --sanitize" diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py index a51e8d2..b2cd23a 100644 --- a/tests/unit/test_utils.py +++ b/tests/unit/test_utils.py @@ -13,7 +13,7 @@ ) from ansible_dev_environment.config import Config from ansible_dev_environment.output import Output -from ansible_dev_environment.utils import TermFeatures +from ansible_dev_environment.utils import TermFeatures, builder_introspect term_features = TermFeatures(color=False, links=False) @@ -123,3 +123,73 @@ def test_parse_collection_request(scenario: tuple[str, Collection | None]) -> No parse_collection_request(string=string, config=config, output=output) else: assert parse_collection_request(string=string, config=config, output=output) == spec + + +def test_builder_found(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: + """Test that builder is found. + + Args: + tmp_path: A temporary path + monkeypatch: The pytest Monkeypatch fixture + + Raises: + AssertionError: if either file is not found + """ + + @property # type: ignore[misc] + def cache_dir(_self: Config) -> Path: + """Return a temporary cache directory. + + Args: + _self: The Config object + + Returns: + A temporary cache directory. + """ + return tmp_path + + monkeypatch.setattr(Config, "cache_dir", cache_dir) + + args = Namespace(venv=str(tmp_path / ".venv"), system_site_packages=False, verbose=0) + + cfg = Config( + args=args, + term_features=term_features, + output=output, + ) + cfg.init() + + builder_introspect(cfg, output) + + assert config.discovered_bindep_reqs.exists() is True + assert config.discovered_python_reqs.exists() is True + + +def test_builder_not_found(monkeypatch: pytest.MonkeyPatch) -> None: + """Test builder not found raises a system exit. + + Args: + monkeypatch: The pytest Monkeypatch fixture + + Raises: + AssertionError: When the exit code is not 1 + """ + + def exists(_self: Path) -> bool: + """Mock path exists. + + Args: + _self: The path object + + Returns: + False indicating the path does not exist + + """ + return False + + monkeypatch.setattr(Path, "exists", exists) + + with pytest.raises(SystemExit) as exc_info: + builder_introspect(config, output) + + assert exc_info.value.code == 1 From b8aa4bdcd64007d58ddb3dc74d9d7d6d04dee602 Mon Sep 17 00:00:00 2001 From: "Bradley A. Thornton" Date: Fri, 4 Apr 2025 05:00:22 -0700 Subject: [PATCH 2/3] Correct config object --- tests/unit/test_utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py index b2cd23a..5bfb485 100644 --- a/tests/unit/test_utils.py +++ b/tests/unit/test_utils.py @@ -161,8 +161,8 @@ def cache_dir(_self: Config) -> Path: builder_introspect(cfg, output) - assert config.discovered_bindep_reqs.exists() is True - assert config.discovered_python_reqs.exists() is True + assert cfg.discovered_bindep_reqs.exists() is True + assert cfg.discovered_python_reqs.exists() is True def test_builder_not_found(monkeypatch: pytest.MonkeyPatch) -> None: From bc3347b55a919e417a5307cb5cbeca2fb4512755 Mon Sep 17 00:00:00 2001 From: "Bradley A. Thornton" Date: Fri, 4 Apr 2025 12:35:08 -0700 Subject: [PATCH 3/3] Switch to python -m --- src/ansible_dev_environment/utils.py | 20 +++++-------------- tests/unit/test_utils.py | 30 ---------------------------- 2 files changed, 5 insertions(+), 45 deletions(-) diff --git a/src/ansible_dev_environment/utils.py b/src/ansible_dev_environment/utils.py index 60c9f82..66616ce 100644 --- a/src/ansible_dev_environment/utils.py +++ b/src/ansible_dev_environment/utils.py @@ -11,7 +11,6 @@ import time from dataclasses import dataclass -from pathlib import Path from typing import TYPE_CHECKING import subprocess_tee @@ -19,6 +18,7 @@ if TYPE_CHECKING: + from pathlib import Path from types import TracebackType from .config import Config @@ -280,25 +280,15 @@ def collect_manifests( # noqa: C901 def builder_introspect(config: Config, output: Output) -> None: """Introspect a collection. - Default to the path of ansible-builder adjacent to the python executable. - Since ansible-builder is a dependency of ade, it should be in the same bin directory - as the python interpreter that spawned the ade process. - If not found, we cannot introspect. + Use the sys executable to run builder, since it is a direct dependency + it should be accessible to the current interpreter. Args: config: The configuration object. output: The output object. """ - builder_path = Path(sys.executable).parent / "ansible-builder" - if not builder_path.exists(): - output.critical( - "Failed to find ansible-builder. Please check the installation" - " of ade as it should have been installed by default.", - ) - return # pragma: no cover - command = ( - f"{builder_path} introspect {config.site_pkg_path}" + f"{sys.executable} -m ansible_builder introspect {config.site_pkg_path}" f" --write-pip {config.discovered_python_reqs}" f" --write-bindep {config.discovered_bindep_reqs}" " --sanitize" @@ -329,7 +319,7 @@ def builder_introspect(config: Config, output: Output) -> None: ) except subprocess.CalledProcessError as exc: err = f"Failed to discover requirements: {exc} {exc.stderr}" - logger.critical(err) + output.critical(err) if not config.discovered_python_reqs.exists(): config.discovered_python_reqs.touch() diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py index 5bfb485..0494e07 100644 --- a/tests/unit/test_utils.py +++ b/tests/unit/test_utils.py @@ -163,33 +163,3 @@ def cache_dir(_self: Config) -> Path: assert cfg.discovered_bindep_reqs.exists() is True assert cfg.discovered_python_reqs.exists() is True - - -def test_builder_not_found(monkeypatch: pytest.MonkeyPatch) -> None: - """Test builder not found raises a system exit. - - Args: - monkeypatch: The pytest Monkeypatch fixture - - Raises: - AssertionError: When the exit code is not 1 - """ - - def exists(_self: Path) -> bool: - """Mock path exists. - - Args: - _self: The path object - - Returns: - False indicating the path does not exist - - """ - return False - - monkeypatch.setattr(Path, "exists", exists) - - with pytest.raises(SystemExit) as exc_info: - builder_introspect(config, output) - - assert exc_info.value.code == 1