| OLD | NEW |
| (Empty) |
| 1 # Copyright 2015 The Chromium Authors. All rights reserved. | |
| 2 # Use of this source code is governed by a BSD-style license that can be | |
| 3 # found in the LICENSE file. | |
| 4 | |
| 5 import argparse | |
| 6 import datetime | |
| 7 import logging | |
| 8 import os | |
| 9 import sys | |
| 10 | |
| 11 import psutil | |
| 12 | |
| 13 from infra_libs import logs | |
| 14 from infra_libs import ts_mon | |
| 15 | |
| 16 | |
| 17 class BaseApplication(object): | |
| 18 """Encapsulates common boilerplate for setting up an application. | |
| 19 | |
| 20 Subclasses must implement the main() method, and will usually also implement | |
| 21 add_argparse_options(). | |
| 22 | |
| 23 By default this will initialise logging and timeseries monitoring (ts_mon) | |
| 24 modules. | |
| 25 | |
| 26 Minimal example:: | |
| 27 | |
| 28 import infra_libs | |
| 29 | |
| 30 class MyApplication(infra_libs.BaseApplication): | |
| 31 def main(self, opts): | |
| 32 # Do stuff. | |
| 33 | |
| 34 if __name__ == '__main__': | |
| 35 MyApplication().run() | |
| 36 | |
| 37 Class variables (override these in your class definition): | |
| 38 PROG_NAME: The program name to display in the --help message. Defaults to | |
| 39 sys.argv[0]. Passed to argparse.ArgumentParser. | |
| 40 DESCRIPTION: Text to display in the --help message. Passed to | |
| 41 argparse.ArgumentParser. | |
| 42 USES_STANDARD_LOGGING: Whether to configure the standard logging libraries. | |
| 43 Defaults to True. | |
| 44 USES_TS_MON: Whether to configure timeseries monitoring. Defaults to True. | |
| 45 | |
| 46 Instance variables (use these in your application): | |
| 47 opts: The argparse.Namespace containing parsed commandline arguments. | |
| 48 """ | |
| 49 | |
| 50 PROG_NAME = None | |
| 51 DESCRIPTION = None | |
| 52 USES_STANDARD_LOGGING = True | |
| 53 USES_TS_MON = True | |
| 54 | |
| 55 def __init__(self): | |
| 56 self.opts = None | |
| 57 self.parser = None | |
| 58 | |
| 59 def add_argparse_options(self, parser): | |
| 60 """Register any arguments used by this application. | |
| 61 | |
| 62 Override this method and call parser.add_argument(). | |
| 63 | |
| 64 Args: | |
| 65 parser: An argparse.ArgumentParser object. | |
| 66 """ | |
| 67 | |
| 68 if self.USES_STANDARD_LOGGING: | |
| 69 logs.add_argparse_options(parser) | |
| 70 if self.USES_TS_MON: | |
| 71 ts_mon.add_argparse_options(parser) | |
| 72 | |
| 73 def process_argparse_options(self, options): | |
| 74 """Process any commandline arguments. | |
| 75 | |
| 76 Args: | |
| 77 options: An argparse.Namespace object. | |
| 78 """ | |
| 79 | |
| 80 if self.USES_STANDARD_LOGGING: | |
| 81 logs.process_argparse_options(options) | |
| 82 if self.USES_TS_MON: | |
| 83 ts_mon.process_argparse_options(options) | |
| 84 | |
| 85 def main(self, opts): | |
| 86 """Your application's main method. | |
| 87 | |
| 88 Do the work of your application here. When this method returns the | |
| 89 application will exit. | |
| 90 | |
| 91 Args: | |
| 92 opts: An argparse.Namespace containing parsed commandline options. This | |
| 93 is passed as an argument for convenience but is also accessible as an | |
| 94 instance variable (self.opts). | |
| 95 | |
| 96 Return: | |
| 97 An integer exit status, or None to use an exit status of 0. | |
| 98 """ | |
| 99 raise NotImplementedError | |
| 100 | |
| 101 def run(self, args=None): | |
| 102 """Main application entry point.""" | |
| 103 | |
| 104 if args is None: # pragma: no cover | |
| 105 args = sys.argv | |
| 106 | |
| 107 # Add and parse commandline args. | |
| 108 self.parser = argparse.ArgumentParser( | |
| 109 description=self.DESCRIPTION, | |
| 110 prog=self.PROG_NAME or args[0], | |
| 111 formatter_class=argparse.RawTextHelpFormatter) | |
| 112 | |
| 113 self.add_argparse_options(self.parser) | |
| 114 self.opts = self.parser.parse_args(args[1:]) | |
| 115 self.process_argparse_options(self.opts) | |
| 116 | |
| 117 # Print a startup log message. | |
| 118 logging.info('Process started at %s', datetime.datetime.utcfromtimestamp( | |
| 119 psutil.Process().create_time()).isoformat()) | |
| 120 logging.info('Command line arguments:') | |
| 121 for index, arg in enumerate(sys.argv): | |
| 122 logging.info('argv[%d]: %s', index, arg) | |
| 123 logging.info('Process id %d', os.getpid()) | |
| 124 logging.info('Current working directory %s', os.getcwd()) | |
| 125 | |
| 126 # Run the application's main function. | |
| 127 try: | |
| 128 status = self.main(self.opts) | |
| 129 except Exception: | |
| 130 logging.exception('Uncaught exception, exiting:') | |
| 131 if self.USES_TS_MON: | |
| 132 # Flushing ts_mon to try to report the exception. | |
| 133 ts_mon.flush() | |
| 134 status = 1 | |
| 135 | |
| 136 sys.exit(status) | |
| OLD | NEW |