migrate sphinxcontrib.h5p.utils
This commit is contained in:
parent
cc4b567181
commit
5bf4a7eee4
8 changed files with 742 additions and 0 deletions
122
src/byteb4rb1e/utils/argparse/dispatcher.py
Normal file
122
src/byteb4rb1e/utils/argparse/dispatcher.py
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
"""CLI dispatcher — builds parser trees from command dataclasses."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from argparse import ArgumentDefaultsHelpFormatter, ArgumentParser
|
||||
from typing import Any, Dict, List, Optional, Type
|
||||
|
||||
from byteb4rb1e.utils.argparse.command import CLICommand
|
||||
|
||||
|
||||
class CLI:
|
||||
"""Composable CLI built from a tree of Command dataclasses.
|
||||
|
||||
Recursively bootstraps an argparse parser hierarchy and tracks
|
||||
dest names so ``run()`` can dispatch to the correct leaf command
|
||||
without dest chaining in the caller.
|
||||
|
||||
Usage::
|
||||
|
||||
cli = CLI(prog="repository", description="...")
|
||||
cli.bootstrap([MirrorCommand, IndexCommand])
|
||||
cli.run()
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
prog: Optional[str] = None,
|
||||
description: str = "",
|
||||
) -> None:
|
||||
kwargs = {} # type: Dict[str, Any]
|
||||
if prog:
|
||||
kwargs["prog"] = prog
|
||||
if description:
|
||||
kwargs["description"] = description
|
||||
kwargs.setdefault(
|
||||
"formatter_class", ArgumentDefaultsHelpFormatter,
|
||||
)
|
||||
self.parser = ArgumentParser(**kwargs)
|
||||
self._dests = [] # type: List[str]
|
||||
self._commands = {} # type: Dict[str, Command]
|
||||
|
||||
def add_arguments(self, parser: ArgumentParser) -> None:
|
||||
"""Add global arguments to the root parser."""
|
||||
parser.add_argument(
|
||||
"-v", "--verbose", action="count", default=0,
|
||||
help="Increase verbosity (-v for INFO, -vv for DEBUG)",
|
||||
)
|
||||
|
||||
def bootstrap(
|
||||
self,
|
||||
commands: List[Type[Command]],
|
||||
) -> None:
|
||||
"""Build the parser tree from a list of top-level commands."""
|
||||
self.add_arguments(self.parser)
|
||||
dest = "command"
|
||||
self._dests.append(dest)
|
||||
sub = self.parser.add_subparsers(dest=dest)
|
||||
for cmd_cls in commands:
|
||||
self._add(sub, cmd_cls, prefix="")
|
||||
|
||||
def _add(
|
||||
self,
|
||||
subparsers: Any,
|
||||
cmd_cls: Type[Command],
|
||||
prefix: str,
|
||||
) -> None:
|
||||
"""Recursively add a command and its subcommands."""
|
||||
cmd = cmd_cls()
|
||||
parser = subparsers.add_parser(
|
||||
cmd.name,
|
||||
formatter_class=ArgumentDefaultsHelpFormatter,
|
||||
**cmd.parser_kwargs(),
|
||||
)
|
||||
cmd.add_arguments(parser)
|
||||
|
||||
key = "%s.%s" % (prefix, cmd.name) if prefix else cmd.name
|
||||
self._commands[key] = cmd
|
||||
|
||||
if cmd._subcommands:
|
||||
dest = "%s_command" % cmd.name
|
||||
self._dests.append(dest)
|
||||
child_sub = parser.add_subparsers(dest=dest)
|
||||
for sc_cls in cmd._subcommands:
|
||||
self._add(child_sub, sc_cls, prefix=key)
|
||||
|
||||
def _resolve(self, args: Any) -> Optional[Command]:
|
||||
"""Walk dest chain to find the leaf command."""
|
||||
parts = [] # type: List[str]
|
||||
for dest in self._dests:
|
||||
val = getattr(args, dest, None)
|
||||
if val is None:
|
||||
break
|
||||
parts.append(val)
|
||||
if not parts:
|
||||
return None
|
||||
key = ".".join(parts)
|
||||
return self._commands.get(key)
|
||||
|
||||
@staticmethod
|
||||
def _setup_logging(verbosity: int) -> None:
|
||||
if verbosity >= 2:
|
||||
level = logging.DEBUG
|
||||
elif verbosity >= 1:
|
||||
level = logging.INFO
|
||||
else:
|
||||
level = logging.WARNING
|
||||
logging.basicConfig(
|
||||
level=level,
|
||||
format="%(asctime)s [%(levelname)s] %(message)s",
|
||||
handlers=[logging.StreamHandler()],
|
||||
)
|
||||
|
||||
def run(self) -> None:
|
||||
"""Parse args and dispatch to the leaf command."""
|
||||
args = self.parser.parse_args()
|
||||
self._setup_logging(getattr(args, "verbose", 0))
|
||||
cmd = self._resolve(args)
|
||||
if cmd is None:
|
||||
self.parser.print_help()
|
||||
raise SystemExit(1)
|
||||
raise SystemExit(cmd.execute(args))
|
||||
Loading…
Add table
Add a link
Reference in a new issue