Skip to content

Commit

Permalink
Move to rich-click for prettier CLI help.
Browse files Browse the repository at this point in the history
Should work now that ewels/rich-click#164 is addressed!
  • Loading branch information
Julian committed Apr 14, 2024
1 parent f02ccb1 commit 0fa9c17
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 41 deletions.
68 changes: 53 additions & 15 deletions bowtie/_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from importlib.resources import files
from pathlib import Path
from pprint import pformat
from textwrap import dedent
from typing import TYPE_CHECKING, Literal, ParamSpec, Protocol
import asyncio
import json
Expand All @@ -28,8 +29,8 @@
from rich.table import Column, Table
from rich.text import Text
from url import URL, RelativeURLWithoutBase
import click
import referencing_loaders
import rich_click as click
import structlog
import structlog.typing

Expand Down Expand Up @@ -61,6 +62,7 @@
from bowtie._commands import AnyTestResult, ImplementationId
from bowtie._core import DialectRunner, ImplementationInfo, MakeValidator


# Windows fallbacks...
_EX_CONFIG = getattr(os, "EX_CONFIG", 1)
_EX_DATAERR = getattr(os, "EX_DATAERR", 1)
Expand All @@ -83,16 +85,45 @@
_F = Literal["json", "pretty", "markdown"]


@click.rich_config(
help_config=click.RichHelpConfiguration(
command_groups=dict(
bowtie=[
dict(
name="Basic Commands",
commands=["validate", "suite", "summary", "info"],
),
dict(
name="Advanced Usage",
commands=[
"filter-dialects",
"filter-implementations",
"run",
],
),
dict(
name="Plumbing Commands",
commands=["badges", "smoke"],
),
],
),
style_commands_table_column_width_ratio=(1, 3),
# Otherwise there's an uncomfortable amount of internal whitespace.
max_width=120,
),
)
@click.group(
context_settings=dict(help_option_names=["--help", "-h"]),
epilog="""
If you don't know where to begin, `bowtie validate` (for checking
what any given implementations think of your schema) or `bowtie suite`
(for running the official test suite against implementations) are likely
good places to start.
Full documentation can also be found at https://docs.bowtie.report
""",
# needing to explicitly dedent here, as well as the extra newline
# before "Full documentation" both seem like rich-click bugs.
epilog=dedent(
"""
If you don't know where to begin, `bowtie validate --help` or
`bowtie suite --help` are likely good places to start.
Full documentation can also be found at https://docs.bowtie.report
""",
),
)
@click.version_option(prog_name="bowtie", package_name="bowtie-json-schema")
@click.option(
Expand Down Expand Up @@ -245,7 +276,7 @@ def cmd(image_names: Iterable[str], **kwargs: Any) -> int:
)
def badges(site: Path):
"""
Generate Bowtie badges from previous runs.
Generate Bowtie badges for implementations using a previous Bowtie run.
Will generate badges for any existing dialects, and ignore any for which a
report was not generated.
Expand Down Expand Up @@ -942,7 +973,11 @@ def run(
**kwargs: Any,
):
"""
Run test cases written in Bowtie's test format.
Run test cases written directly in Bowtie's testing format.
This is generally useful if you wish to hand-author which schemas to
include in the schema registry, or otherwise exactly control the contents
of a test case.
"""
cases = filter(
TestCase.from_dict(dialect=dialect, **json.loads(line))
Expand Down Expand Up @@ -988,7 +1023,7 @@ def validate(
**kwargs: Any,
):
"""
Validate instances across any implementation.
Validate instances under a schema across any supported implementation.
"""
if not instances:
return _EX_NOINPUT
Expand Down Expand Up @@ -1058,7 +1093,10 @@ async def filter_implementations(
languages: Set[str],
):
"""
Output implementations matching a given criteria.
Output implementations which match the given criteria.
Useful for piping or otherwise using the resulting output for further
Bowtie commands.
"""
if not dialects and languages == KNOWN_LANGUAGES:
for implementation in ctx.params.get("image_names", ()):
Expand Down Expand Up @@ -1198,7 +1236,7 @@ async def smoke(
echo: Callable[..., None],
) -> int:
"""
Smoke test implementations for basic correctness.
Smoke test implementations for basic correctness against Bowtie's protocol.
"""
exit_code = 0

Expand Down Expand Up @@ -1266,7 +1304,7 @@ def suite(
**kwargs: Any,
):
"""
Run tests from the official JSON Schema suite.
Run the official JSON Schema test suite against any implementation.
Supports a number of possible inputs:
Expand Down
9 changes: 7 additions & 2 deletions docs/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ charset-normalizer==3.3.2
# via requests
click==8.1.7
# via
# bowtie-json-schema
# rich-click
# sphinx-click
cryptography==42.0.5
# via pyjwt
Expand Down Expand Up @@ -119,6 +119,9 @@ rich==13.7.1
# via
# bowtie-json-schema
# diagnostic
# rich-click
rich-click==1.8.0.dev4
# via bowtie-json-schema
rpds-py==0.18.0
# via
# bowtie-json-schema
Expand Down Expand Up @@ -176,7 +179,9 @@ sphinxext-opengraph==0.9.1
structlog==24.1.0
# via bowtie-json-schema
typing-extensions==4.11.0
# via aiodocker
# via
# aiodocker
# rich-click
uritemplate==4.1.1
# via github3-py
url-py==0.10.0
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,14 @@ dynamic = ["version"]
dependencies = [
"aiodocker",
"attrs>=22.2.0",
"click",
"diagnostic",
"github3.py",
"jsonschema>=4.19.0",
"jsonschema_lexer",
"referencing>=0.31.0",
"referencing-loaders>=0.4.2",
"rich",
"rich-click>=1.8.0dev4",
"rpds.py>=0.18.0",
"structlog",
"typing-extensions; python_version<'3.11'",
Expand Down
9 changes: 7 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ cffi==1.16.0
charset-normalizer==3.3.2
# via requests
click==8.1.7
# via bowtie-json-schema (pyproject.toml)
# via rich-click
cryptography==42.0.5
# via pyjwt
diagnostic==2.1.0
Expand Down Expand Up @@ -80,6 +80,9 @@ rich==13.7.1
# via
# bowtie-json-schema (pyproject.toml)
# diagnostic
# rich-click
rich-click==1.8.0.dev4
# via bowtie-json-schema (pyproject.toml)
rpds-py==0.18.0
# via
# bowtie-json-schema (pyproject.toml)
Expand All @@ -90,7 +93,9 @@ six==1.16.0
structlog==24.1.0
# via bowtie-json-schema (pyproject.toml)
typing-extensions==4.11.0
# via aiodocker
# via
# aiodocker
# rich-click
uritemplate==4.1.1
# via github3-py
url-py==0.10.0
Expand Down
8 changes: 7 additions & 1 deletion test-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ charset-normalizer==3.3.2
click==8.1.7
# via
# -r requirements.txt
# bowtie-json-schema
# rich-click
cryptography==42.0.5
# via
# -r requirements.txt
Expand Down Expand Up @@ -158,6 +158,11 @@ rich==13.7.1
# -r requirements.txt
# bowtie-json-schema
# diagnostic
# rich-click
rich-click==1.8.0.dev4
# via
# -r requirements.txt
# bowtie-json-schema
rpds-py==0.18.0
# via
# -r requirements.txt
Expand All @@ -178,6 +183,7 @@ typing-extensions==4.11.0
# via
# -r requirements.txt
# aiodocker
# rich-click
uritemplate==4.1.1
# via
# -r requirements.txt
Expand Down
11 changes: 5 additions & 6 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,9 @@ def test_help_is_not_truncated():
capture_output=True,
check=True,
)
first, *lines = result.stdout.decode().splitlines()

# It's fine for the first line to, that's our Usage line, so ignore it.
assert first.casefold().startswith("usage")

truncated = [line for line in lines if line.strip().endswith("...")]
truncated = [
line # [1:]: ignore the Usage: line
for line in result.stdout.decode().strip().splitlines()[1:]
if "..." in line
]
assert not truncated
Loading

0 comments on commit 0fa9c17

Please sign in to comment.