d73ff9b0fb
Deploy Site / deploy-vercel (push) Has been cancelled
Deploy Site / deploy-docs (push) Has been cancelled
Docker / shell lint / Lint Dockerfile (hadolint) (push) Has been cancelled
Docker / shell lint / Lint docker/ shell scripts (shellcheck) (push) Has been cancelled
Docker Build and Publish / build-amd64 (push) Has been cancelled
Docker Build and Publish / build-arm64 (push) Has been cancelled
Lint (ruff + ty) / ruff + ty diff (push) Has been cancelled
Lint (ruff + ty) / ruff enforcement (blocking) (push) Has been cancelled
Lint (ruff + ty) / Windows footguns (blocking) (push) Has been cancelled
Nix Lockfile Fix / auto-fix-main (push) Has been cancelled
Nix Lockfile Fix / fix (push) Has been cancelled
Nix / nix (macos-latest) (push) Has been cancelled
Nix / nix (ubuntu-latest) (push) Has been cancelled
OSV-Scanner / Scan lockfiles (push) Has been cancelled
Build Skills Index / build-index (push) Has been cancelled
Tests / test (1) (push) Has been cancelled
Tests / test (2) (push) Has been cancelled
Tests / test (3) (push) Has been cancelled
Tests / test (4) (push) Has been cancelled
Tests / test (5) (push) Has been cancelled
Tests / test (6) (push) Has been cancelled
Tests / e2e (push) Has been cancelled
uv.lock check / uv lock --check (push) Has been cancelled
Docker Build and Publish / merge (push) Has been cancelled
Build Skills Index / trigger-deploy (push) Has been cancelled
Tests / save-durations (push) Has been cancelled
66 lines
2.4 KiB
Python
66 lines
2.4 KiB
Python
"""Tests for the defensive subparser routing workaround (bpo-9338).
|
|
|
|
The main() function in hermes_cli/main.py sets subparsers.required=True
|
|
when argv contains a known subcommand name. This forces deterministic
|
|
routing on Python versions where argparse fails to match subcommand tokens
|
|
when the parent parser has nargs='?' optional arguments (--continue).
|
|
|
|
If the subcommand token is consumed as a flag value (e.g. `hermes -c model`
|
|
to resume a session named 'model'), the required=True parse raises
|
|
SystemExit and the code falls back to the default required=False behaviour.
|
|
"""
|
|
import argparse
|
|
import io
|
|
import sys
|
|
|
|
|
|
|
|
def _build_parser():
|
|
"""Build a minimal replica of the hermes top-level parser."""
|
|
parser = argparse.ArgumentParser(prog="hermes")
|
|
parser.add_argument("--version", "-V", action="store_true")
|
|
parser.add_argument("--resume", "-r", metavar="SESSION", default=None)
|
|
parser.add_argument(
|
|
"--continue", "-c",
|
|
dest="continue_last",
|
|
nargs="?",
|
|
const=True,
|
|
default=None,
|
|
metavar="SESSION_NAME",
|
|
)
|
|
parser.add_argument("--worktree", "-w", action="store_true", default=False)
|
|
parser.add_argument("--skills", "-s", action="append", default=None)
|
|
parser.add_argument("--yolo", action="store_true", default=False)
|
|
parser.add_argument("--pass-session-id", action="store_true", default=False)
|
|
|
|
subparsers = parser.add_subparsers(dest="command", help="Command to run")
|
|
chat_p = subparsers.add_parser("chat")
|
|
chat_p.add_argument("-q", "--query", default=None)
|
|
subparsers.add_parser("model")
|
|
subparsers.add_parser("gateway")
|
|
subparsers.add_parser("setup")
|
|
return parser, subparsers
|
|
|
|
|
|
def _safe_parse(parser, subparsers, argv):
|
|
"""Replica of the defensive parsing logic from main()."""
|
|
known_cmds = set(subparsers.choices.keys()) if hasattr(subparsers, "choices") else set()
|
|
has_cmd_token = any(t in known_cmds for t in argv if not t.startswith("-"))
|
|
|
|
if has_cmd_token:
|
|
subparsers.required = True
|
|
saved_stderr = sys.stderr
|
|
try:
|
|
sys.stderr = io.StringIO()
|
|
args = parser.parse_args(argv)
|
|
sys.stderr = saved_stderr
|
|
return args
|
|
except SystemExit:
|
|
sys.stderr = saved_stderr
|
|
subparsers.required = False
|
|
return parser.parse_args(argv)
|
|
else:
|
|
subparsers.required = False
|
|
return parser.parse_args(argv)
|
|
|