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: 1 addition & 1 deletion packages/volo-cli/src/volo_cli/commands/certify.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

certify_app = typer.Typer(
name="certify",
help="Volo Certified reliability + safety a signed agent certificate + badge.",
help="Volo Certified - reliability + safety -> a signed agent certificate + badge.",
no_args_is_help=True,
)

Expand Down
22 changes: 22 additions & 0 deletions packages/volo-cli/src/volo_cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

from __future__ import annotations

import contextlib
import sys

import typer

from volo_cli.commands.certify import certify_app
Expand All @@ -28,6 +31,25 @@

__version__ = "0.1.0.dev0"


def _force_utf8_io() -> None:
"""Make stdout/stderr UTF-8 so the CLI never crashes on non-ASCII output under a legacy
codepage (Windows cp1252 when piped/redirected/in CI). Runs at import — before Click renders
``--help`` — so even help text with a stray non-ASCII char is safe. ``errors='replace'`` keeps
a bad byte from ever raising. No-op where the stream can't be reconfigured (e.g. pytest capture).
"""
for stream in (sys.stdout, sys.stderr):
reconfigure = getattr(stream, "reconfigure", None)
if reconfigure is not None:
# stream already detached/closed -> nothing we can do, and never worth crashing over
with contextlib.suppress(ValueError, OSError):
reconfigure(encoding="utf-8", errors="replace")


# Runs at import (the console-script imports this module before invoking `app`), so it takes
# effect before Click renders any output — including `--help`.
_force_utf8_io()

app = typer.Typer(
name="volo",
help="Volo — a flight simulator for AI agents. See https://github.com/volo.",
Expand Down
28 changes: 28 additions & 0 deletions packages/volo-cli/tests/test_cli_encoding.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""The CLI must not crash on non-ASCII output under a legacy codepage (Windows cp1252)."""

from __future__ import annotations

from typer.testing import CliRunner

from volo_cli.main import _force_utf8_io, app

runner = CliRunner()


def test_force_utf8_io_is_safe_and_idempotent() -> None:
_force_utf8_io()
_force_utf8_io() # calling twice must not raise


def test_top_level_help_renders() -> None:
res = runner.invoke(app, ["--help"])
assert res.exit_code == 0
assert "certify" in res.output


def test_command_help_strings_are_cp1252_safe() -> None:
# every registered command's help text must survive a legacy codepage (the --help crash was a
# stray non-ASCII char in a help string)
for group in app.registered_groups:
help_text = getattr(group.typer_instance.info, "help", "") or ""
help_text.encode("cp1252") # raises UnicodeEncodeError if a stray char sneaks back in
Loading