Skip to content

Commit

Permalink
Show the filename when dumping uris or versions from the cli
Browse files Browse the repository at this point in the history
When using the forest, uris or versions types with `hab dump --type` setting
verbosity to 1 or higher will show the path to non-placeholder items.
This changes the expansion of truncated versions from verbosity 1 to 2.
  • Loading branch information
MHendricks committed Nov 1, 2024
1 parent c2ae21d commit f0cb3ba
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 16 deletions.
21 changes: 11 additions & 10 deletions hab/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -645,29 +645,30 @@ def dump(settings, uri, env, env_config, report_type, flat, verbosity, format_ty
resolver = settings.resolver

if report_type in ("uris", "versions", "forest"):
# Allow the user to disable truncation of versions with verbosity flag
truncate = None if verbosity else 3
from .parsers.format_parser import FormatParser

def echo_line(line):
if line.strip() == line:
click.echo(f"{Fore.GREEN}{line}{Fore.RESET}")
else:
click.echo(line)
formatter = FormatParser(verbosity, color=True)
# Allow the user to disable truncation of versions with verbosity flag
truncate = None if verbosity > 1 else 3

if report_type in ("uris", "forest"):
click.echo(f'{Fore.YELLOW}{" URIs ".center(50, "-")}{Fore.RESET}')
# Filter out any URI's hidden by the requested verbosity level
with utils.verbosity_filter(resolver, verbosity):
for line in resolver.dump_forest(resolver.configs):
echo_line(line)
for line in resolver.dump_forest(
resolver.configs, fmt=formatter.format
):
click.echo(line)
if report_type in ("versions", "forest"):
click.echo(f'{Fore.YELLOW}{" Versions ".center(50, "-")}{Fore.RESET}')

for line in resolver.dump_forest(
resolver.distros,
attr="name",
fmt=formatter.format,
truncate=truncate,
):
echo_line(line)
click.echo(line)
elif report_type == "all-uris":
# Combines all non-placeholder URI's into a single json document and display.
# This can be used to compare changes to configs when editing them in bulk.
Expand Down
44 changes: 44 additions & 0 deletions hab/parsers/format_parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from colorama import Fore


class FormatParser:
"""Used to format HabBase objects into a readable string.
Parameters:
top_level (str): Formatting applied to name if this is a top level item.
This is applied to the name value before passed to simple/verbose.
simple (str): A str.format string used to generate the non-verbose output.
verbose (str): A str.format string used to generate the verbose output.
All of the format strings are provided the kwargs `pre`, `name` and `filename`
Args:
verbosity (int): Controls the complexity of the output. If zero then
`self.simple` is used, otherwise `self.verbose` is uses.
color (bool, optional): Enables adding color control characters for readability.
"""

def __init__(self, verbosity, color=True):
self.color = color
self.verbosity = verbosity

# Configure the format strings
self.simple = "{pre}{name}"
if self.color:
self.verbose = f'{{pre}}{{name}}: {Fore.YELLOW}"{{filename}}"{Fore.RESET}'
self.top_level = f"{Fore.GREEN}{{name}}{Fore.RESET}"
else:
self.verbose = '{pre}{name}: "{filename}"'
self.top_level = "{name}"

def format(self, parser, attr, pre):
"""Format the output of Config or Distro for printing based on verbosity."""
name = getattr(parser, attr)
if not pre:
name = self.top_level.format(pre=pre, name=name, filename=parser.filename)

if not self.verbosity or not parser.filename:
fmt = self.simple
else:
fmt = self.verbose
return fmt.format(pre=pre, name=name, filename=parser.filename)
17 changes: 11 additions & 6 deletions hab/resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,9 @@ def dump_forest(
attr (str, optional): The name of the attribute to display for each node.
If None is passed, the anytree object is returned un-modified.
fmt (str, optional): str.format string to control the display of
each node in the forest. Accepts (pre, attr) keys.
each node in the forest. Accepts (pre, attr) keys. If a callable
is passed then it will be called passing (parser, attr=attr, pre=pre)
and should return the text for that hab.parsers instance.
style (anytree.render.AbstractStyle, optional): Controls how anytree
renders the branch information. If not set, defaults to a custom
style that intents all children(recursively) to the same depth.
Expand Down Expand Up @@ -235,13 +237,13 @@ def sort_forest(items):
for row in anytree.RenderTree(
forest[tree_name], style=style, childiter=sort_forest
):
cfg = row.node
parser = row.node
# Process inheritance for this config to ensure the correct value
cfg._collect_values(cfg, ["min_verbosity"])
parser._collect_values(parser, ["min_verbosity"])

# Check if this row should be shown based on verbosity settings.
min_verbosity = {"min_verbosity": cfg.min_verbosity}
if not cfg.check_min_verbosity(min_verbosity):
min_verbosity = {"min_verbosity": parser.min_verbosity}
if not parser.check_min_verbosity(min_verbosity):
# TODO: If a parent is hidden but not a child, fix rendering
# so the child's indent is reduced correctly.
continue
Expand All @@ -254,7 +256,10 @@ def sort_forest(items):
pre = row.pre
if limit_pre:
pre = row.pre[: len(indent)]
yield fmt.format(pre=pre, attr=getattr(cfg, attr))
if isinstance(fmt, str):
yield fmt.format(pre=pre, attr=getattr(parser, attr))
else:
yield fmt(parser, attr=attr, pre=pre)

def find_distro(self, requirement):
"""Returns the DistroVersion matching the requirement or None"""
Expand Down
98 changes: 98 additions & 0 deletions tests/test_format_parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import pytest
from colorama import Fore

from hab.parsers.format_parser import FormatParser

# Short access to the required colorama color codes for formatting
cg = Fore.GREEN
cr = Fore.RESET
cy = Fore.YELLOW


@pytest.mark.parametrize(
"uri,pre,color,zero,one",
(
# Top level URI placeholder is not indented
("app", "", False, "app", "app"),
("app", "", True, f"{cg}app{cr}", f"{cg}app{cr}"),
# Child URI is defined, The URI is not turned green and is indented
("app/aliased", " ", False, " app/aliased", ' app/aliased: "{filename}"'),
(
"app/aliased",
" ",
True,
" app/aliased",
f' app/aliased: {cy}"{{filename}}"{cr}',
),
# Grand-child URI is defined, The URI is not turned green and is indented
(
"app/aliased/mod",
" ",
False,
" app/aliased/mod",
' app/aliased/mod: "{filename}"',
),
(
"app/aliased/mod",
" ",
True,
" app/aliased/mod",
f' app/aliased/mod: {cy}"{{filename}}"{cr}',
),
# Top level URI is defined so not a placeholder (not indented)
("project_a", "", False, "project_a", 'project_a: "{filename}"'),
(
"project_a",
"",
True,
f"{cg}project_a{cr}",
f'{cg}project_a{cr}: {cy}"{{filename}}"{cr}',
),
# Parent and child URI is not defined, The URI is not turned green and no filename
("app/houdini", " ", False, " app/houdini", " app/houdini"),
(
"app/houdini",
" ",
True,
" app/houdini",
" app/houdini",
),
),
)
def test_format_parser_uri(config_root, uncached_resolver, uri, pre, color, zero, one):
"""Test various uses of `hab.parsers.format_parser.FormatParser`."""
cfg = uncached_resolver.closest_config(uri)

# Test verbosity set to zero
formatter = FormatParser(0, color=color)
result = formatter.format(cfg, "uri", pre)
assert result == zero

# Test verbosity of one
formatter = FormatParser(1, color=color)
result = formatter.format(cfg, "uri", pre)
assert result == one.format(filename=cfg.filename)


def test_dump_forest_callable(uncached_resolver, config_root):
"""Check that Resolver.dump_forest handles passing a callable to fmt."""
formatter = FormatParser(1, color=False)
result = []
for line in uncached_resolver.dump_forest(
uncached_resolver.distros, attr="name", fmt=formatter.format, truncate=3
):
result.append(line)

result = "\n".join(result)

check = [
"aliased",
f''' aliased==2.0: "{config_root / 'distros/aliased/2.0/.hab.json'}"''',
"aliased_mod",
f''' aliased_mod==1.0: "{config_root / 'distros/aliased_mod/1.0/.hab.json'}"''',
"aliased_verbosity",
f" aliased_verbosity==1.0: "
f'''"{config_root / 'distros/aliased_verbosity/1.0/.hab.json'}"''',
]
check = "\n".join(check)
assert result.startswith(check)

0 comments on commit f0cb3ba

Please sign in to comment.