blob: 294136a126ec649dac19a639132506082989f93f [file] [log] [blame]
#!/usr/bin/env python3
# Copyright 2023 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""
Provides a framework for command line interfaces based on argh.
It automatically adds common arguments, such as -v, -vv and --color to provide consistent
behavior.
"""
import argparse
import sys
import traceback
from typing import (
Any,
Callable,
Optional,
)
from .util import (
add_common_args,
parse_common_args,
print_timing_info,
record_time,
verbose,
ensure_packages_exist,
)
ensure_packages_exist("argh")
import argh # type: ignore
# Hack: argh does not support type annotations. This prevents type errors.
argh: Any # type: ignore
def run_main(main_fn: Callable[..., Any], usage: Optional[str] = None):
run_commands(default_fn=main_fn, usage=usage)
def run_commands(
*functions: Callable[..., Any],
default_fn: Optional[Callable[..., Any]] = None,
usage: Optional[str] = None,
):
"""
Allow the user to call the provided functions with command line arguments translated to
function arguments via argh: https://pythonhosted.org/argh
"""
exit_code = 0
try:
parser = argparse.ArgumentParser(
description=usage,
# Docstrings are used as the description in argparse, preserve their formatting.
formatter_class=argparse.RawDescriptionHelpFormatter,
# Do not allow implied abbreviations. Abbreviations should be manually specified.
allow_abbrev=False,
)
add_common_args(parser)
# Add provided commands to parser. Do not use sub-commands if we just got one function.
if functions:
argh.add_commands(parser, functions) # type: ignore
if default_fn:
argh.set_default_command(parser, default_fn) # type: ignore
with record_time("Total Time"):
# Call main method
argh.dispatch(parser) # type: ignore
except Exception as e:
if verbose():
traceback.print_exc()
else:
print(e)
exit_code = 1
if parse_common_args().timing_info:
print_timing_info()
sys.exit(exit_code)
if __name__ == "__main__":
import doctest
(failures, num_tests) = doctest.testmod(optionflags=doctest.ELLIPSIS)
sys.exit(1 if failures > 0 else 0)