Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
## 0.32

### 0.32.0
- feat: Add `epilog=` field on commands for text which should go after the argument help.
- refactor: All root objects (Command/Arg/Subcommand) to have "Final" variants which represent their post-normalization shape.
- feat: Add support for fixed size optional-value num_args arguments (e.g. --foo / --foo 4).
- feat: Add `aliases=` support for subcommands. Each entry is either a string (visible alias) or a `cappa.Alias(name, hidden=..., deprecated=...)` for finer control. Hidden aliases dispatch but are omitted from help and completion; deprecated aliases emit a runtime warning when used. Collisions are detected at construction time.
- fix: Display Args with the same group identity as alternatives in helptext on the same line.
Expand Down
6 changes: 6 additions & 0 deletions src/cappa/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,7 @@ def command(
aliases: list[str | Alias] | None = None,
help: str | None = None,
description: str | None = None,
epilog: str | None = None,
invoke: InvokeCallableSpec[Any] | None = None,
hidden: bool = False,
default_short: bool = False,
Expand All @@ -515,6 +516,7 @@ def command(
aliases: list[str | Alias] | None = None,
help: str | None = None,
description: str | None = None,
epilog: str | None = None,
invoke: InvokeCallableSpec[Any] | None = None,
hidden: bool = False,
default_short: bool = False,
Expand All @@ -530,6 +532,7 @@ def command(
aliases: list[str | Alias] | None = None,
help: str | None = None,
description: str | None = None,
epilog: str | None = None,
invoke: InvokeCallableSpec[Any] | None = None,
hidden: bool = False,
default_short: bool = False,
Expand All @@ -547,6 +550,7 @@ def command(
aliases: list[str | Alias] | None = None,
help: str | None = None,
description: str | None = None,
epilog: str | None = None,
invoke: InvokeCallableSpec[Any] | None = None,
hidden: bool = False,
default_short: bool = False,
Expand All @@ -570,6 +574,7 @@ def command(
any params/options.
description: Optional extended help text. If omitted, the `cls` docstring will
be parsed, and the extended long description section will be used.
epilog: Optional text displayed after the argument list in the help output.
invoke: Optional command to be called in the event parsing is successful.
In the case of subcommands, it will only call the parsed/selected
function to invoke.
Expand Down Expand Up @@ -597,6 +602,7 @@ def wrapper(_decorated_cls: U) -> U:
aliases=list(aliases) if aliases is not None else command.aliases,
help=help,
description=description,
epilog=epilog,
hidden=hidden,
default_short=default_short,
default_long=default_long,
Expand Down
3 changes: 3 additions & 0 deletions src/cappa/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ class Command(Generic[T]):
any params/options.
description: Optional extended help text. If omitted, the `cls` docstring will
be parsed, and the extended long description section will be used.
epilog: Optional text displayed after the argument list in the help output.
invoke: Optional command to be called in the event parsing is successful.
In the case of subcommands, it will only call the parsed/selected
function to invoke. The value can **either** be a callable object or
Expand Down Expand Up @@ -125,6 +126,7 @@ class Command(Generic[T]):
aliases: list[str | Alias] = dataclasses.field(default_factory=lambda: [])
help: str | None = None
description: str | None = None
epilog: str | None = None
invoke: Callable[..., Any] | str | None = None

hidden: bool = False
Expand Down Expand Up @@ -270,6 +272,7 @@ def collect(
aliases=self.aliases,
help=kwargs.get("help", self.help),
description=kwargs.get("description", self.description),
epilog=self.epilog,
invoke=self.invoke,
hidden=self.hidden,
default_short=self.default_short,
Expand Down
5 changes: 5 additions & 0 deletions src/cappa/help.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,11 @@ def long(self, command: FinalCommand[Any], prog: str) -> list[Displayable]:

console = Console()
lines.extend(add_long_args(console, self, arg_groups))

if command.epilog:
lines.append(NewLine())
lines.append(Padding(Markdown(command.epilog), self.left_padding))

return lines

def short(self, command: FinalCommand[Any], prog: str) -> Displayable:
Expand Down
32 changes: 31 additions & 1 deletion tests/command/test_help.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,20 @@

import re
from dataclasses import dataclass
from textwrap import dedent
from typing import Any

import pytest

import cappa
from cappa.output import Exit
from tests.utils import Backend, backends, parse
from tests.utils import (
Backend,
backends,
parse,
strip_trailing_whitespace,
terminal_width,
)


@pytest.mark.help
Expand Down Expand Up @@ -73,3 +80,26 @@ class Command:

stdout = capsys.readouterr().out
assert "All the help." in stdout


@pytest.mark.help
@backends
def test_epilog(backend: Backend, capsys: Any):
@cappa.command(epilog="See also: https://example.com")
@dataclass
class Command:
pass

with terminal_width(), pytest.raises(Exit):
parse(Command, "--help", backend=backend, completion=False)

result = strip_trailing_whitespace(capsys.readouterr().out)
tail = result[result.index("[-h, --help]") :]
assert tail == dedent(
"""\
[-h, --help] Show this message and exit.


See also: https://example.com
"""
)
Loading