Coverage for src / competitive_verifier / app.py: 100%
41 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-03-05 16:00 +0000
« prev ^ index » next coverage.py v7.13.1, created at 2026-03-05 16:00 +0000
1import importlib.metadata
2from argparse import ArgumentParser as BaseParser
3from logging import getLogger
4from typing import Annotated, Any, Literal, get_args
6from pydantic import Field, TypeAdapter
8from competitive_verifier.arg import BaseArguments
9from competitive_verifier.documents import Docs
10from competitive_verifier.download import Download
11from competitive_verifier.inout import Check, MergeInput, MergeResult
12from competitive_verifier.migrate import Migration
13from competitive_verifier.oj import OjResolve
14from competitive_verifier.verify import Verify
16logger = getLogger(__name__)
19class HelpError(Exception):
20 pass
23class NoSubcommand(BaseArguments):
24 subcommand: Literal[None] = None # noqa: PYI061
26 def run(self) -> bool:
27 raise HelpError
29 @classmethod
30 def add_parser(cls, parser: BaseParser):
31 super().add_parser(parser)
32 parser.add_argument(
33 "--version",
34 action="version",
35 version=importlib.metadata.version("competitive-verifier"),
36 help="print the competitive-verifier version number",
37 )
40Arguments = Annotated[
41 NoSubcommand
42 | Verify
43 | Docs
44 | Download
45 | MergeInput
46 | MergeResult
47 | Check
48 | OjResolve
49 | Migration,
50 Field(discriminator="subcommand"),
51]
52ARG_TYPES: tuple[type[BaseArguments], ...] = get_args(Arguments.__origin__) # pyright: ignore[reportUnknownMemberType, reportAttributeAccessIssue]
55class ArgumentParser(BaseParser):
56 def __init__(self, **kwargs: dict[str, Any]) -> None:
57 super().__init__(**kwargs) # pyright: ignore[reportArgumentType]
58 subparsers = self.add_subparsers(dest="subcommand", parser_class=BaseParser)
60 for s in ARG_TYPES:
61 s.add_parser(
62 self
63 if (sub := s.get_subcommand_info()) is None
64 else subparsers.add_parser(**sub)
65 )
67 def parse(self, args: list[str] | None = None) -> Arguments:
68 type_adapter = TypeAdapter[Arguments](Arguments)
69 return type_adapter.validate_python(self.parse_args(args).__dict__)
72def main(args: list[str] | None = None) -> int | None:
73 parser = ArgumentParser()
75 try:
76 return not parser.parse(args).run()
77 except HelpError:
78 parser.print_help()
79 return 2
82if __name__ == "__main__": # pragma: no cover
83 main()