Skip to content

Commit

Permalink
Add healthcheck for checking whether the environment is listed on env…
Browse files Browse the repository at this point in the history
…ironments.txt (conda#13000)

Co-authored-by: Ken Odegard <[email protected]>
  • Loading branch information
ForgottenProgramme and kenodegard authored Sep 14, 2023
1 parent c76f5aa commit e658798
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 51 deletions.
2 changes: 1 addition & 1 deletion conda/plugins/subcommands/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Copyright (C) 2012 Anaconda, Inc
# SPDX-License-Identifier: BSD-3-Clause
from .doctor import cli as doctor
from . import doctor

plugins = [doctor]
51 changes: 49 additions & 2 deletions conda/plugins/subcommands/doctor/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,51 @@
# Copyright (C) 2012 Anaconda, Inc
# SPDX-License-Identifier: BSD-3-Clause
"""Provide 'conda doctor' environment check feature."""
# This file is here for packaging, not for importing submodules.
"""Implementation for `conda doctor` subcommand.
Adds various environment and package checks to detect issues or possible environment
corruption.
"""

from __future__ import annotations

import argparse

from ....base.context import context
from ....cli.conda_argparse import (
ArgumentParser,
add_parser_help,
add_parser_prefix,
add_parser_verbose,
)
from ....deprecations import deprecated
from ... import CondaSubcommand, hookimpl


@deprecated(
"24.3", "24.9", addendum="Use `conda.base.context.context.target_prefix` instead."
)
def get_prefix(args: argparse.Namespace) -> str:
context.__init__(argparse_args=args)
return context.target_prefix


def configure_parser(parser: ArgumentParser):
add_parser_verbose(parser)
add_parser_help(parser)
add_parser_prefix(parser)


def execute(args: argparse.Namespace) -> None:
"""Run conda doctor subcommand."""
from .health_checks import display_health_checks

display_health_checks(context.target_prefix, verbose=context.verbose)


@hookimpl
def conda_subcommands():
yield CondaSubcommand(
name="doctor",
summary="Display a health report for your environment.",
action=execute,
configure_parser=configure_parser,
)
47 changes: 0 additions & 47 deletions conda/plugins/subcommands/doctor/cli.py

This file was deleted.

25 changes: 24 additions & 1 deletion conda/plugins/subcommands/doctor/health_checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,39 @@
from logging import getLogger
from pathlib import Path

from conda.core.envs_manager import get_user_environments_txt_file
from conda.exceptions import CondaError
from conda.gateways.disk.read import compute_sum

logger = getLogger(__name__)

OK_MARK = "✅"
X_MARK = "❌"


def display_report_heading(prefix: str) -> None:
"""Displays our report heading."""
print(f"Environment Health Report for: {Path(prefix)}\n")


def check_envs_txt_file(prefix: str | Path) -> bool:
"""Checks whether the environment is listed in the environments.txt file"""
prefix = Path(prefix)
envs_txt_file = Path(get_user_environments_txt_file())
try:
with envs_txt_file.open() as f:
for line in f.readlines():
if prefix.samefile(line.strip()):
return True
return False

except (IsADirectoryError, FileNotFoundError, PermissionError) as err:
logger.error(
f"{envs_txt_file} could not be "
f"accessed because of the following error: {err}"
)


def find_packages_with_missing_files(prefix: str | Path) -> dict[str, list[str]]:
"""Finds packages listed in conda-meta which have missing files."""
packages_with_missing_files = {}
Expand Down Expand Up @@ -104,6 +124,9 @@ def display_health_checks(prefix: str, verbose: bool = False) -> None:
delimiter = "\n "
print(f"{package_name}:{delimiter}{delimiter.join(altered_files)}\n")
else:
print(f"{package_name}: {len(altered_files)}")
print(f"{package_name}: {len(altered_files)}\n")
else:
print(f"{OK_MARK} There are no packages with altered files.\n")

present = OK_MARK if check_envs_txt_file(prefix) else X_MARK
print(f"3. Environment listed in environments.txt file: {present}\n")
32 changes: 32 additions & 0 deletions tests/plugins/subcommands/doctor/test_health_checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@

import pytest
from pytest import MonkeyPatch
from pytest_mock import MockerFixture

from conda.base.context import reset_context
from conda.common.io import env_vars
from conda.plugins.subcommands.doctor.health_checks import (
check_envs_txt_file,
display_health_checks,
find_altered_packages,
find_packages_with_missing_files,
Expand Down Expand Up @@ -83,6 +85,36 @@ def env_altered_files(env_ok: tuple[Path, str, str, str]) -> tuple[Path, str, st
return env_ok


def test_listed_on_envs_txt_file(
tmp_path: Path, mocker: MockerFixture, env_ok: tuple[Path, str, str, str]
):
"""Test that runs for the case when the env is listed on the environments.txt file"""
prefix, _, _, _ = env_ok
tmp_envs_txt_file = tmp_path / "envs.txt"
tmp_envs_txt_file.write_text(f"{prefix}")

mocker.patch(
"conda.plugins.subcommands.doctor.health_checks.get_user_environments_txt_file",
return_value=tmp_envs_txt_file,
)
assert check_envs_txt_file(prefix)


def test_not_listed_on_envs_txt_file(
tmp_path: Path, mocker: MockerFixture, env_ok: tuple[Path, str, str, str]
):
"""Test that runs for the case when the env is not listed on the environments.txt file"""
prefix, _, _, _ = env_ok
tmp_envs_txt_file = tmp_path / "envs.txt"
tmp_envs_txt_file.write_text("Not environment name")

mocker.patch(
"conda.plugins.subcommands.doctor.health_checks.get_user_environments_txt_file",
return_value=tmp_envs_txt_file,
)
assert not check_envs_txt_file(prefix)


def test_no_missing_files(env_ok: tuple[Path, str, str, str]):
"""Test that runs for the case with no missing files"""
prefix, _, _, _ = env_ok
Expand Down

0 comments on commit e658798

Please sign in to comment.