From 787525531fe4ad504c25cd544dfbd5f45d940b53 Mon Sep 17 00:00:00 2001 From: Matt Kramer Date: Thu, 9 May 2024 23:49:13 -0500 Subject: [PATCH] feat: Add ability to specify create command and disable environment creation (#69) * Add ability to specify create command and disable environment creation * Add another test * Update README.md and tweak help output * Fix type --- README.md | 3 ++ environment-dev.yml | 2 + .../add_renovate_annotations.py | 21 ++++++++-- tests/test_generate_renovate_annotations.py | 39 +++++++++++++++++-- 4 files changed, 57 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 7467fd6..98681c0 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,9 @@ An example usage is shown below: --internal-pip-index-url=https://pypi.anaconda.org/my-organization/simple, --internal-pip-package=my-private-package, --internal-pip-package=my-other-private-package, + --create-command="make setup", # Default value + --environment-selector="-p ./env", # Default value + --disable-environment-creation # Creation enabled by default ] ``` diff --git a/environment-dev.yml b/environment-dev.yml index 1d00eb6..d403687 100644 --- a/environment-dev.yml +++ b/environment-dev.yml @@ -13,6 +13,8 @@ dependencies: - pytest=7.4.0 # renovate: datasource=conda depName=main/pytest-cov - pytest-cov=4.1.0 +# renovate: datasource=conda depName=main/pytest-mock +- pytest-mock=3.10.0 - pip: # renovate: datasource=pypi - cog==0.9.7 diff --git a/src/anaconda_pre_commit_hooks/add_renovate_annotations.py b/src/anaconda_pre_commit_hooks/add_renovate_annotations.py index 02e0d11..493ec54 100755 --- a/src/anaconda_pre_commit_hooks/add_renovate_annotations.py +++ b/src/anaconda_pre_commit_hooks/add_renovate_annotations.py @@ -16,6 +16,9 @@ import typer +DEFAULT_ENVIRONMENT_SELECTOR = "-p ./env" +DEFAULT_CREATE_COMMAND = "make setup" + CondaOrPip = str PackageName = str PackageVersion = str @@ -66,8 +69,8 @@ def list_packages_in_conda_environment(environment_selector: str) -> list[dict]: def load_dependencies( project_directory: Optional[Path] = None, - create_command: str = "make setup", - environment_selector: str = "-p ./env", + create_command: Optional[str] = DEFAULT_CREATE_COMMAND, + environment_selector: str = DEFAULT_ENVIRONMENT_SELECTOR, ) -> Dependencies: """Load the dependencies from a live conda environment. @@ -80,7 +83,8 @@ def load_dependencies( An object containing all dependencies in the installed environment, split between conda and pip packages. """ - setup_conda_environment(create_command, cwd=project_directory or Path.cwd()) + if create_command is not None: + setup_conda_environment(create_command, cwd=project_directory or Path.cwd()) data = list_packages_in_conda_environment(environment_selector) @@ -213,6 +217,11 @@ def cli( env_files: list[Path], internal_pip_package: Annotated[Optional[list[str]], typer.Option()] = None, internal_pip_index_url: Annotated[str, typer.Option()] = "", + create_command: Annotated[str, typer.Option()] = DEFAULT_CREATE_COMMAND, + environment_selector: Annotated[str, typer.Option()] = DEFAULT_ENVIRONMENT_SELECTOR, + disable_environment_creation: Annotated[ + bool, typer.Option("--disable-environment-creation") + ] = False, ) -> None: # Construct a mapping of package name to index URL based on CLI options pip_index_overrides = parse_pip_index_overrides( @@ -223,7 +232,11 @@ def cli( # `make setup` for each file, and only once per project. project_dirs = sorted({env_file.parent for env_file in env_files}) for project_dir in project_dirs: - deps = load_dependencies(project_dir) + deps = load_dependencies( + project_dir, + create_command=create_command if not disable_environment_creation else None, + environment_selector=environment_selector, + ) project_env_files = (e for e in env_files if e.parent == project_dir) for env_file in project_env_files: add_comments_to_env_file( diff --git a/tests/test_generate_renovate_annotations.py b/tests/test_generate_renovate_annotations.py index 5c2a66d..5091be4 100644 --- a/tests/test_generate_renovate_annotations.py +++ b/tests/test_generate_renovate_annotations.py @@ -6,15 +6,20 @@ import pytest import yaml +from anaconda_pre_commit_hooks import add_renovate_annotations from anaconda_pre_commit_hooks.add_renovate_annotations import ( Dependencies, Dependency, add_comments_to_env_file, + cli, load_dependencies, parse_pip_index_overrides, setup_conda_environment, ) +# Mock out subprocess run for all tests +pytestmark = pytest.mark.usefixtures("mock_subprocess_run") + DEFAULT_FILES_REGEX_STRING = r"environment[\w-]*\.ya?ml" ENVIRONMENT_YAML = dedent("""\ @@ -113,7 +118,7 @@ def mock_subprocess_run(monkeypatch): old_subprocess_run = subprocess.run def f(args, *posargs, **kwargs): - if args == ["make", "setup"]: + if args == ["make", "setup"] or args[:3] == ["conda", "env", "create"]: return subprocess.CompletedProcess( args, 0, @@ -156,13 +161,11 @@ def f(args, *posargs, **kwargs): monkeypatch.setattr(subprocess, "run", f) -@pytest.mark.usefixtures("mock_subprocess_run") def test_setup_conda_environment(): result = setup_conda_environment("make setup") assert result is None -@pytest.mark.usefixtures("mock_subprocess_run") def test_load_dependencies(): dependencies = load_dependencies(Path.cwd()) assert dependencies == Dependencies( @@ -171,7 +174,6 @@ def test_load_dependencies(): ) -@pytest.mark.usefixtures("mock_subprocess_run") def test_add_comments_to_env_file(tmp_path): env_file_path = tmp_path / "environment.yml" with env_file_path.open("w") as fp: @@ -208,3 +210,32 @@ def test_add_comments_to_env_file(tmp_path): - -e . name: some-environment-name """) + + +def test_disable_environment_creation(mocker): + mock = mocker.spy(add_renovate_annotations, "setup_conda_environment") + load_dependencies(create_command=None) + assert mock.call_count == 0 + + +def test_cli_disable_environment_creation(tmp_path, mocker): + env_file_path = tmp_path / "environment.yml" + with env_file_path.open("w") as fp: + fp.write(ENVIRONMENT_YAML) + + mock = mocker.spy(add_renovate_annotations, "setup_conda_environment") + cli(env_files=[env_file_path], disable_environment_creation=True) + assert mock.call_count == 0 + + +def test_cli_override_create_command(tmp_path, mocker): + env_file_path = tmp_path / "environment.yml" + with env_file_path.open("w") as fp: + fp.write(ENVIRONMENT_YAML) + + mock = mocker.spy(add_renovate_annotations, "setup_conda_environment") + create_command = "conda env create -n some-environment-name -f environment.yml" + cli(env_files=[env_file_path], create_command=create_command) + assert mock.call_count == 1 + args, kwargs = mock.call_args + assert args[0] == create_command