Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create DisplayManager abstract class #13769

Draft
wants to merge 22 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
2695426
add json flag to notices
ForgottenProgramme Mar 5, 2024
56a7463
Merge branch 'conda:main' into main
ForgottenProgramme Mar 5, 2024
a02c88c
write test
ForgottenProgramme Mar 5, 2024
585504d
Merge branch 'main' of https://github.com/forgottenprogramme/conda
ForgottenProgramme Mar 5, 2024
a65cba9
fix test
ForgottenProgramme Mar 5, 2024
7c0c1f4
add a helper func in ChannelNotice class
ForgottenProgramme Mar 5, 2024
0bd28a4
revert change made to distro.py file
ForgottenProgramme Mar 5, 2024
5b2b7bc
Merge branch 'main' into main
kenodegard Mar 5, 2024
fc1a3d8
fix failing test on conda-forge
ForgottenProgramme Mar 6, 2024
b18b696
Merge branch 'main' of https://github.com/forgottenprogramme/conda
ForgottenProgramme Mar 6, 2024
819c60a
remove commented code
ForgottenProgramme Mar 6, 2024
6bec2e0
changes based on review
ForgottenProgramme Mar 6, 2024
2b5b8d6
Merge branch 'main' of https://github.com/forgottenprogramme/conda
ForgottenProgramme Mar 12, 2024
565d838
add rich to context
ForgottenProgramme Mar 25, 2024
c2fe9a4
set no_wrap to false
ForgottenProgramme Mar 25, 2024
7297398
add funcs for different displays
ForgottenProgramme Apr 2, 2024
39079ed
change func signature
ForgottenProgramme Apr 2, 2024
58590e2
Merge branch 'main' of https://github.com/forgottenprogramme/conda in…
ForgottenProgramme Apr 2, 2024
2437538
create DisplayManager abstract class
ForgottenProgramme Apr 5, 2024
3279137
add new func
ForgottenProgramme Apr 9, 2024
085508e
use the new dict_based_on_args function
ForgottenProgramme Apr 10, 2024
8d0130e
Merge branch 'main' of https://github.com/forgottenprogramme/conda in…
ForgottenProgramme Apr 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions conda/base/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,7 @@ class Context(Configuration):
error_upload_url = ParameterLoader(PrimitiveParameter(ERROR_UPLOAD_URL))
force = ParameterLoader(PrimitiveParameter(False))
json = ParameterLoader(PrimitiveParameter(False))
rich = ParameterLoader(PrimitiveParameter(False))
offline = ParameterLoader(PrimitiveParameter(False))
quiet = ParameterLoader(PrimitiveParameter(False))
ignore_pinned = ParameterLoader(PrimitiveParameter(False))
Expand Down
253 changes: 146 additions & 107 deletions conda/cli/main_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,22 @@

from __future__ import annotations

import json
import os
import re
import sys
from abc import ABC, abstractmethod
from argparse import SUPPRESS, _StoreTrueAction
from logging import getLogger
from os.path import exists, expanduser, isfile, join
from textwrap import wrap
from typing import TYPE_CHECKING

from rich.console import Console
from rich.table import Table

from ..base.context import context
from ..deprecations import deprecated
from .common import stdout_json

if TYPE_CHECKING:
from argparse import ArgumentParser, Namespace, _SubParsersAction
Expand All @@ -43,6 +48,13 @@ def configure_parser(sub_parsers: _SubParsersAction, **kwargs) -> ArgumentParser
epilog=epilog,
**kwargs,
)

p.add_argument(
"--rich",
action="store_true",
default=NULL,
help="print rich info.",
)
add_parser_json(p)
p.add_argument(
"--offline",
Expand Down Expand Up @@ -204,7 +216,6 @@ def get_info_dict() -> dict[str, Any]:
from .. import __version__ as conda_version
from ..base.context import (
DEFAULT_SOLVER,
context,
env_name,
sys_rc_path,
user_rc_path,
Expand Down Expand Up @@ -342,61 +353,144 @@ def get_env_vars_str(info_dict: dict[str, Any]) -> str:
return "\n".join(builder)


def get_main_info_str(info_dict: dict[str, Any]) -> str:
"""
Returns a printable string of the contents of ``info_dict``.
def flatten(lines: Iterable[str]) -> str:
return ("\n" + 26 * " ").join(map(str, lines))

:param info_dict: The output of ``get_info_dict()``.
:returns: String to print.

from ..common.compat import on_win


def builder(info_dict: dict[str, Any]):
if info_dict["active_prefix_name"]:
yield ("active environment", info_dict["active_prefix_name"])
yield ("active env location", info_dict["active_prefix"])
else:
yield ("active environment", info_dict["active_prefix"])

if info_dict["conda_shlvl"] >= 0:
yield ("shell level", info_dict["conda_shlvl"])

yield ("user config file", info_dict["user_rc_path"])
yield ("populated config files", flatten(info_dict["config_files"]))
yield ("conda version", info_dict["conda_version"])
yield ("conda-build version", info_dict["conda_build_version"])
yield ("python version", info_dict["python_version"])
yield (
"solver",
f"{info_dict['solver']['name']}{' (default)' if info_dict['solver']['default'] else ''}",
)
yield (
"virtual packages",
flatten("=".join(pkg) for pkg in info_dict["virtual_pkgs"]),
)
writable = "writable" if info_dict["root_writable"] else "read only"
yield ("base environment", f"{info_dict['root_prefix']} ({writable})")
yield ("conda av data dir", info_dict["av_data_dir"])
yield ("conda av metadata url", info_dict["av_metadata_url_base"])
yield ("channel URLs", flatten(info_dict["channels"]))
yield ("package cache", flatten(info_dict["pkgs_dirs"]))
yield ("envs directories", flatten(info_dict["envs_dirs"]))
yield ("platform", info_dict["platform"])
yield ("user-agent", info_dict["user_agent"])

if on_win:
yield ("administrator", info_dict["is_windows_admin"])
else:
yield ("UID:GID", f"{info_dict['UID']}:{info_dict['GID']}")

yield ("netrc file", info_dict["netrc_file"])
yield ("offline mode", info_dict["offline"])


def dict_based_on_args(info_dict: dict, args, context) -> dict:
"""
Based on which flags are used with `conda info`, the output changes.
This functions takes the "info_dict" dictionary as a parameter and depending on which args are used,
returns a trimmed version of info_dict dictionary.
"""
if args.base:
return {"root_prefix": context.root_prefix}
elif args.unsafe_channels:
return {"channels": context.channels}
if args.envs:
from ..core.envs_manager import list_all_known_prefixes

from ..common.compat import on_win
content_dict = {"envs": list_all_known_prefixes()}
if args.system and not context.json:
from .find_commands import find_commands, find_executable

content_dict = {}
content_dict["sys.version"] = sys.version
content_dict["sys.prefix"] = sys.prefix
content_dict["sys.executable"] = sys.executable
content_dict["conda location"] = info_dict["conda_location"]
for cmd in sorted(set(find_commands() + ("build",))):
content_dict["conda-{}"] = find_executable("conda-" + cmd)
content_dict["user site dirs"] = info_dict["site_dirs"]
for name, value in sorted(info_dict["env_vars"].items()):
content_dict[name] = value

def flatten(lines: Iterable[str]) -> str:
return ("\n" + 26 * " ").join(map(str, lines))
if (
context.verbose
or all(not getattr(args, opt) for opt in ("envs", "system"))
and not context.json
):
content_dict = dict(builder(info_dict))

def builder():
if info_dict["active_prefix_name"]:
yield ("active environment", info_dict["active_prefix_name"])
yield ("active env location", info_dict["active_prefix"])
else:
yield ("active environment", info_dict["active_prefix"])

if info_dict["conda_shlvl"] >= 0:
yield ("shell level", info_dict["conda_shlvl"])

yield ("user config file", info_dict["user_rc_path"])
yield ("populated config files", flatten(info_dict["config_files"]))
yield ("conda version", info_dict["conda_version"])
yield ("conda-build version", info_dict["conda_build_version"])
yield ("python version", info_dict["python_version"])
yield (
"solver",
f"{info_dict['solver']['name']}{' (default)' if info_dict['solver']['default'] else ''}",
if context.json:
content_dict = info_dict

return content_dict


class DisplayManager(ABC):
@abstractmethod
def display(self): ...

@abstractmethod
def std(self): ...

@abstractmethod
def json(self): ...

@abstractmethod
def rich(self): ...


class Tabular(DisplayManager):
"""If data needs to be printed in a table like form"""

def std(self, args, context, content_dict: dict):
conda_info_string = "\n".join(
("", *(f"{key:>23} : {value}" for key, value in content_dict.items()), "")
)
yield (
"virtual packages",
flatten("=".join(pkg) for pkg in info_dict["virtual_pkgs"]),
print(conda_info_string)

def json(self, args, context, content_dict: dict):
stdout_json(content_dict)

def rich(self, args, context, content_dict: dict):
table = Table(
# find a way to extracting and inserting the command name instead of hard coding "conda info".
title="conda info",
show_header=False,
show_lines=True,
style="black",
)
writable = "writable" if info_dict["root_writable"] else "read only"
yield ("base environment", f"{info_dict['root_prefix']} ({writable})")
yield ("conda av data dir", info_dict["av_data_dir"])
yield ("conda av metadata url", info_dict["av_metadata_url_base"])
yield ("channel URLs", flatten(info_dict["channels"]))
yield ("package cache", flatten(info_dict["pkgs_dirs"]))
yield ("envs directories", flatten(info_dict["envs_dirs"]))
yield ("platform", info_dict["platform"])
yield ("user-agent", info_dict["user_agent"])

if on_win:
yield ("administrator", info_dict["is_windows_admin"])
table.add_column("", no_wrap=False)
table.add_column("", no_wrap=False)
for k, v in content_dict.items():
table.add_row(str(k), str(v))
console = Console()
console.print(table)

def display(self, args, context, content_dict: dict):
if context.json:
self.json(args, context, content_dict)
elif context.rich:
self.rich(args, context, content_dict)
else:
yield ("UID:GID", f"{info_dict['UID']}:{info_dict['GID']}")

yield ("netrc file", info_dict["netrc_file"])
yield ("offline mode", info_dict["offline"])

return "\n".join(("", *(f"{key:>23} : {value}" for key, value in builder()), ""))
self.std(args, context, content_dict)


def execute(args: Namespace, parser: ArgumentParser) -> int:
Expand All @@ -411,65 +505,10 @@ def execute(args: Namespace, parser: ArgumentParser) -> int:
* ``conda info --system`` (deprecated) (no ``--json``)
"""

from ..base.context import context
from .common import print_envs_list, stdout_json

if args.base:
if context.json:
stdout_json({"root_prefix": context.root_prefix})
else:
print(f"{context.root_prefix}")
return 0

if args.unsafe_channels:
if not context.json:
print("\n".join(context.channels))
else:
print(json.dumps({"channels": context.channels}))
return 0

options = "envs", "system"

if context.verbose or context.json:
for option in options:
setattr(args, option, True)
info_dict = get_info_dict()
final_info_dict = dict_based_on_args(info_dict, args, context)

if (
context.verbose or all(not getattr(args, opt) for opt in options)
) and not context.json:
print(get_main_info_str(info_dict) + "\n")

if args.envs:
from ..core.envs_manager import list_all_known_prefixes

info_dict["envs"] = list_all_known_prefixes()
print_envs_list(info_dict["envs"], not context.json)

if args.system:
if not context.json:
from .find_commands import find_commands, find_executable

print("sys.version: %s..." % (sys.version[:40]))
print("sys.prefix: %s" % sys.prefix)
print("sys.executable: %s" % sys.executable)
print("conda location: %s" % info_dict["conda_location"])
for cmd in sorted(set(find_commands() + ("build",))):
print("conda-{}: {}".format(cmd, find_executable("conda-" + cmd)))
print("user site dirs: ", end="")
site_dirs = info_dict["site_dirs"]
if site_dirs:
print(site_dirs[0])
else:
print()
for site_dir in site_dirs[1:]:
print(" %s" % site_dir)
print()

for name, value in sorted(info_dict["env_vars"].items()):
print(f"{name}: {value}")
print()
conda_info_table = Tabular()
conda_info_table.display(args, context, final_info_dict)

if context.json:
stdout_json(info_dict)
return 0