1*bb4ee6a4SAndroid Build Coastguard Worker#!/usr/bin/env python3 2*bb4ee6a4SAndroid Build Coastguard Worker# Copyright 2023 The ChromiumOS Authors 3*bb4ee6a4SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be 4*bb4ee6a4SAndroid Build Coastguard Worker# found in the LICENSE file. 5*bb4ee6a4SAndroid Build Coastguard Worker 6*bb4ee6a4SAndroid Build Coastguard Worker""" 7*bb4ee6a4SAndroid Build Coastguard WorkerProvides a framework for command line interfaces based on argh. 8*bb4ee6a4SAndroid Build Coastguard Worker 9*bb4ee6a4SAndroid Build Coastguard WorkerIt automatically adds common arguments, such as -v, -vv and --color to provide consistent 10*bb4ee6a4SAndroid Build Coastguard Workerbehavior. 11*bb4ee6a4SAndroid Build Coastguard Worker""" 12*bb4ee6a4SAndroid Build Coastguard Worker 13*bb4ee6a4SAndroid Build Coastguard Workerimport argparse 14*bb4ee6a4SAndroid Build Coastguard Workerimport sys 15*bb4ee6a4SAndroid Build Coastguard Workerimport traceback 16*bb4ee6a4SAndroid Build Coastguard Workerfrom typing import ( 17*bb4ee6a4SAndroid Build Coastguard Worker Any, 18*bb4ee6a4SAndroid Build Coastguard Worker Callable, 19*bb4ee6a4SAndroid Build Coastguard Worker Optional, 20*bb4ee6a4SAndroid Build Coastguard Worker) 21*bb4ee6a4SAndroid Build Coastguard Worker 22*bb4ee6a4SAndroid Build Coastguard Workerfrom .util import ( 23*bb4ee6a4SAndroid Build Coastguard Worker add_common_args, 24*bb4ee6a4SAndroid Build Coastguard Worker parse_common_args, 25*bb4ee6a4SAndroid Build Coastguard Worker print_timing_info, 26*bb4ee6a4SAndroid Build Coastguard Worker record_time, 27*bb4ee6a4SAndroid Build Coastguard Worker verbose, 28*bb4ee6a4SAndroid Build Coastguard Worker ensure_packages_exist, 29*bb4ee6a4SAndroid Build Coastguard Worker) 30*bb4ee6a4SAndroid Build Coastguard Worker 31*bb4ee6a4SAndroid Build Coastguard Workerensure_packages_exist("argh") 32*bb4ee6a4SAndroid Build Coastguard Workerimport argh # type: ignore 33*bb4ee6a4SAndroid Build Coastguard Worker 34*bb4ee6a4SAndroid Build Coastguard Worker# Hack: argh does not support type annotations. This prevents type errors. 35*bb4ee6a4SAndroid Build Coastguard Workerargh: Any # type: ignore 36*bb4ee6a4SAndroid Build Coastguard Worker 37*bb4ee6a4SAndroid Build Coastguard Worker 38*bb4ee6a4SAndroid Build Coastguard Workerdef run_main(main_fn: Callable[..., Any], usage: Optional[str] = None): 39*bb4ee6a4SAndroid Build Coastguard Worker run_commands(default_fn=main_fn, usage=usage) 40*bb4ee6a4SAndroid Build Coastguard Worker 41*bb4ee6a4SAndroid Build Coastguard Worker 42*bb4ee6a4SAndroid Build Coastguard Workerdef run_commands( 43*bb4ee6a4SAndroid Build Coastguard Worker *functions: Callable[..., Any], 44*bb4ee6a4SAndroid Build Coastguard Worker default_fn: Optional[Callable[..., Any]] = None, 45*bb4ee6a4SAndroid Build Coastguard Worker usage: Optional[str] = None, 46*bb4ee6a4SAndroid Build Coastguard Worker): 47*bb4ee6a4SAndroid Build Coastguard Worker """ 48*bb4ee6a4SAndroid Build Coastguard Worker Allow the user to call the provided functions with command line arguments translated to 49*bb4ee6a4SAndroid Build Coastguard Worker function arguments via argh: https://pythonhosted.org/argh 50*bb4ee6a4SAndroid Build Coastguard Worker """ 51*bb4ee6a4SAndroid Build Coastguard Worker exit_code = 0 52*bb4ee6a4SAndroid Build Coastguard Worker try: 53*bb4ee6a4SAndroid Build Coastguard Worker parser = argparse.ArgumentParser( 54*bb4ee6a4SAndroid Build Coastguard Worker description=usage, 55*bb4ee6a4SAndroid Build Coastguard Worker # Docstrings are used as the description in argparse, preserve their formatting. 56*bb4ee6a4SAndroid Build Coastguard Worker formatter_class=argparse.RawDescriptionHelpFormatter, 57*bb4ee6a4SAndroid Build Coastguard Worker # Do not allow implied abbreviations. Abbreviations should be manually specified. 58*bb4ee6a4SAndroid Build Coastguard Worker allow_abbrev=False, 59*bb4ee6a4SAndroid Build Coastguard Worker ) 60*bb4ee6a4SAndroid Build Coastguard Worker add_common_args(parser) 61*bb4ee6a4SAndroid Build Coastguard Worker 62*bb4ee6a4SAndroid Build Coastguard Worker # Add provided commands to parser. Do not use sub-commands if we just got one function. 63*bb4ee6a4SAndroid Build Coastguard Worker if functions: 64*bb4ee6a4SAndroid Build Coastguard Worker argh.add_commands(parser, functions) # type: ignore 65*bb4ee6a4SAndroid Build Coastguard Worker if default_fn: 66*bb4ee6a4SAndroid Build Coastguard Worker argh.set_default_command(parser, default_fn) # type: ignore 67*bb4ee6a4SAndroid Build Coastguard Worker 68*bb4ee6a4SAndroid Build Coastguard Worker with record_time("Total Time"): 69*bb4ee6a4SAndroid Build Coastguard Worker # Call main method 70*bb4ee6a4SAndroid Build Coastguard Worker argh.dispatch(parser) # type: ignore 71*bb4ee6a4SAndroid Build Coastguard Worker 72*bb4ee6a4SAndroid Build Coastguard Worker except Exception as e: 73*bb4ee6a4SAndroid Build Coastguard Worker if verbose(): 74*bb4ee6a4SAndroid Build Coastguard Worker traceback.print_exc() 75*bb4ee6a4SAndroid Build Coastguard Worker else: 76*bb4ee6a4SAndroid Build Coastguard Worker print(e) 77*bb4ee6a4SAndroid Build Coastguard Worker exit_code = 1 78*bb4ee6a4SAndroid Build Coastguard Worker 79*bb4ee6a4SAndroid Build Coastguard Worker if parse_common_args().timing_info: 80*bb4ee6a4SAndroid Build Coastguard Worker print_timing_info() 81*bb4ee6a4SAndroid Build Coastguard Worker 82*bb4ee6a4SAndroid Build Coastguard Worker sys.exit(exit_code) 83*bb4ee6a4SAndroid Build Coastguard Worker 84*bb4ee6a4SAndroid Build Coastguard Worker 85*bb4ee6a4SAndroid Build Coastguard Workerif __name__ == "__main__": 86*bb4ee6a4SAndroid Build Coastguard Worker import doctest 87*bb4ee6a4SAndroid Build Coastguard Worker 88*bb4ee6a4SAndroid Build Coastguard Worker (failures, num_tests) = doctest.testmod(optionflags=doctest.ELLIPSIS) 89*bb4ee6a4SAndroid Build Coastguard Worker sys.exit(1 if failures > 0 else 0) 90