diff --git a/src/binarylane/console/parser/help_formatter.py b/src/binarylane/console/parser/help_formatter.py index 39efae2..53d5a0c 100644 --- a/src/binarylane/console/parser/help_formatter.py +++ b/src/binarylane/console/parser/help_formatter.py @@ -27,6 +27,14 @@ def _split_lines(self, text: str, width: int) -> typing.List[str]: return [text for text in text.splitlines() for text in textwrap.wrap(text, width)] + def _get_help_string(self, action: argparse.Action) -> str: + """Escape help text for argparse. + + Argparse uses %-formatting for help strings, so literal % must be escaped as %% + while %(...)s needs to be left as-is. + """ + return (super()._get_help_string(action) or "").replace("%", "%%").replace("%%(", "%(") + def add_usage( self, usage: Optional[str], diff --git a/tests/integration/test_help.py b/tests/integration/test_help.py index 7e740d2..15a7a27 100644 --- a/tests/integration/test_help.py +++ b/tests/integration/test_help.py @@ -37,3 +37,14 @@ def test_required_argument_help(app: App, capsys: CaptureFixture[str]) -> None: captured = capsys.readouterr() assert "\nArguments:\n" in captured.out assert "\nParameters:\n" in captured.out + + +def test_help_with_percent_in_description(app: App, capsys: CaptureFixture[str]) -> None: + # Help text containing "100%" should not cause argparse format string errors + # The % character must be escaped as %% to prevent interpretation as format specifier + with pytest.raises(SystemExit): + app.run(["server", "alert", "get", "--help"]) + + captured = capsys.readouterr() + assert "usage: bl server alert get" in captured.out + assert "100%" in captured.out