Skip to content

Commit

Permalink
Made console rule listing rich (#978)
Browse files Browse the repository at this point in the history
Adds 'rich' output format for listing rules (-L) and makes it default.

This format is more verbose and includes all known rule metadata in
a way that makes it easier to read.
  • Loading branch information
ssbarnea authored Aug 27, 2020
1 parent 4af982f commit b9c345f
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 8 deletions.
16 changes: 11 additions & 5 deletions lib/ansiblelint/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@
import sys
from typing import TYPE_CHECKING, List, Set, Type

from rich.console import Console
from rich.markdown import Markdown

from ansiblelint import cli, formatters
from ansiblelint.generate_docs import rules_as_rst
from ansiblelint.color import console
from ansiblelint.generate_docs import rules_as_rich, rules_as_rst
from ansiblelint.rules import RulesCollection
from ansiblelint.runner import Runner
from ansiblelint.utils import get_playbooks_and_roles, get_rules_dirs
Expand All @@ -42,7 +42,12 @@
from ansiblelint.errors import MatchError

_logger = logging.getLogger(__name__)
console = Console()

_rule_format_map = {
'plain': str,
'rich': rules_as_rich,
'rst': rules_as_rst
}


def initialize_logger(level: int = 0) -> None:
Expand Down Expand Up @@ -123,8 +128,9 @@ def main() -> int:
rules = RulesCollection(rulesdirs)

if options.listrules:
formatted_rules = rules if options.format == 'plain' else rules_as_rst(rules)
print(formatted_rules)
console.print(
_rule_format_map[options.format](rules),
highlight=False)
return 0

if options.listtags:
Expand Down
4 changes: 2 additions & 2 deletions lib/ansiblelint/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@ def get_cli_parser() -> argparse.ArgumentParser:

parser.add_argument('-L', dest='listrules', default=False,
action='store_true', help="list all the rules")
parser.add_argument('-f', dest='format', default='plain',
choices=['plain', 'rst'],
parser.add_argument('-f', dest='format', default='rich',
choices=['rich', 'plain', 'rst'],
help="Format used rules output, (default: %(default)s)")
parser.add_argument('-q', dest='quiet',
default=False,
Expand Down
13 changes: 12 additions & 1 deletion lib/ansiblelint/color.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
"""Console coloring support."""
"""Console coloring and terminal support."""
from enum import Enum

from rich.console import Console
from rich.theme import Theme

_theme = Theme({
"info": "cyan",
"warning": "dim yellow",
"danger": "bold red",
"title": "yellow"
})
console = Console(theme=_theme)


class Color(Enum):
"""Color styles."""
Expand Down
23 changes: 23 additions & 0 deletions lib/ansiblelint/generate_docs.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
"""Utils to generate rule table .rst documentation."""
import logging
from typing import Iterable

from rich import box
from rich.console import render_group
from rich.markdown import Markdown
from rich.table import Table

from ansiblelint.rules import RulesCollection

Expand Down Expand Up @@ -41,3 +47,20 @@ def rules_as_rst(rules: RulesCollection) -> str:
r += f"\n\n.. _{d.id}:\n\n{title}\n{'*' * len(title)}\n\n{d.description}"

return r


@render_group()
def rules_as_rich(rules: RulesCollection) -> Iterable[Table]:
"""Print documentation for a list of rules, returns empty string."""
for rule in rules:
table = Table(show_header=True, header_style="title", box=box.MINIMAL)
table.add_column(rule.id, style="dim", width=16)
table.add_column(Markdown(rule.shortdesc))
table.add_row("description", Markdown(rule.description))
if rule.version_added:
table.add_row("version_added", rule.version_added)
if rule.tags:
table.add_row("tags", ", ".join(rule.tags))
if rule.severity:
table.add_row("severity", rule.severity)
yield table
2 changes: 2 additions & 0 deletions lib/ansiblelint/rules/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ def verbose(self) -> str:
tags: List[str] = []
shortdesc: str = ""
description: str = ""
version_added: str = ""
severity: str = ""
match = None
matchtask = None
matchplay = None
Expand Down
19 changes: 19 additions & 0 deletions test/TestRulesCollection.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@

from ansiblelint.rules import RulesCollection

from . import run_ansible_lint


@pytest.fixture
def test_rules_collection():
Expand Down Expand Up @@ -92,3 +94,20 @@ def test_no_duplicate_rule_ids(test_rules_collection):
real_rules = RulesCollection([os.path.abspath('./lib/ansiblelint/rules')])
rule_ids = [rule.id for rule in real_rules]
assert not any(y > 1 for y in collections.Counter(rule_ids).values())


def test_rich_rule_listing():
"""Test that rich list format output is rendered as a table.
This check also offers the contract of having rule id, short and long
descriptions in the console output.
"""
rules_path = os.path.abspath('./test/rules')
result = run_ansible_lint("-r", rules_path, "-f", "rich", "-L")
assert result.returncode == 0

for rule in RulesCollection([rules_path]):
assert rule.id in result.stdout
assert rule.shortdesc in result.stdout
# description could wrap inside table, so we do not check full length
assert rule.description[:30] in result.stdout

0 comments on commit b9c345f

Please sign in to comment.