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 |