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 """Main implementation of metric_tool.""" | |
5 | |
6 import ast | |
7 import os | |
8 import logging | |
9 | |
10 | |
11 # https://chromium.googlesource.com/infra/infra/+/master/infra_libs/logs/README. md | |
12 LOGGER = logging.getLogger(__name__) | |
13 | |
14 METRICS_NAMES = set(('StringMetric', 'BooleanMetric', | |
15 'CounterMetric', 'GaugeMetric', 'CumulativeMetric', | |
16 'FloatMetric', 'DistributionMetric', | |
Sergey Berezin
2015/09/29 00:01:18
nit: ideally, DistributionMetric shouldn't be used
pgervais
2015/09/30 00:09:37
Removed
| |
17 'CumulativeDistributionMetric', | |
18 'NonCumulativeDistributionMetric')) | |
19 | |
20 | |
21 def extract_metrics_descriptions(filepath): | |
22 """Parse a python file and return all metrics descriptions it can find. | |
23 | |
24 A metric description is the value of the 'description' keyword passed to a | |
25 metric definition (classes deriving from ts_mon.Metric) | |
26 | |
27 Args: | |
28 filepath (str): path to a Python file. | |
29 | |
30 Returns: | |
31 description (list of tuples): each tuple being | |
32 (metric name, description string). Metric name is 'DYNAMIC' it something | |
33 different than a static string is used in the metric creation. | |
34 If a metric is instanciated without a description, return None. | |
35 """ | |
36 descriptions = [] | |
37 try: | |
38 with open(filepath, 'r') as f: | |
39 content = f.read() # pragma: no branch | |
40 except IOError: | |
41 return descriptions | |
42 | |
43 try: | |
44 root = ast.parse(content) | |
45 except SyntaxError: # just ignore invalid / python3 files. | |
46 return descriptions | |
47 | |
48 for node in ast.walk(root): | |
49 if not isinstance(node, ast.Call): | |
50 continue | |
51 | |
52 # Look for metrics definitions | |
53 calls = [] | |
54 if isinstance(node.func, ast.Name): | |
55 if node.func.id in METRICS_NAMES: | |
56 LOGGER.debug('Method %s found line %d', node.func.id, node.func.lineno) | |
57 calls.append(node) | |
58 elif isinstance(node.func, ast.Attribute): # pragma: no branch | |
59 if node.func.attr in METRICS_NAMES: | |
60 LOGGER.debug('Method %s found line %d', | |
61 node.func.attr, node.func.lineno) | |
62 calls.append(node) | |
63 | |
64 # Extract parameters from function call | |
65 for fcall in calls: | |
66 # Metric name | |
67 metric_name = 'DYNAMIC' | |
68 if fcall.args and isinstance(fcall.args[0], ast.Str): | |
69 metric_name = fcall.args[0].s | |
70 | |
71 # Getting descriptions | |
72 description = None | |
73 for keyword in fcall.keywords: | |
74 if keyword.arg == 'description' and isinstance(keyword.value, ast.Str): | |
75 description = keyword.value.s | |
76 | |
77 descriptions.append((filepath, fcall.lineno, metric_name, description)) | |
78 | |
79 return descriptions | |
80 | |
81 | |
82 def main(path): | |
83 """Recursively walk a directory structure and print metrics documentation. | |
84 | |
85 For all instanciated metrics in any Python file found under the directory | |
86 passed as argument, if a 'description' keyword is provided print it | |
87 alongside the metric name. | |
88 | |
89 Args: | |
90 path (str): directory. | |
91 """ | |
92 documented = [] | |
93 non_documented = [] | |
94 | |
95 for (dirpath, _, filenames) in os.walk(path): | |
96 for filename in filenames: | |
97 | |
98 if (not filename.endswith('.py') # pragma: no branch | |
99 or filename.endswith('_test.py')): | |
100 continue # pragma: no cover | |
101 | |
102 full_filename = os.path.join(dirpath, filename) | |
103 LOGGER.debug('Scanning file %s', full_filename) | |
104 descriptions = extract_metrics_descriptions(full_filename) | |
105 | |
106 for description in descriptions: | |
107 # TODO(pgervais): use namedtuple here instead | |
108 if description[3]: | |
109 documented.append(description) | |
110 else: | |
111 non_documented.append(description) | |
112 | |
113 if documented: # pragma: no branch | |
114 print('\nDocumented metrics found:') | |
115 for description in documented: | |
116 print('/chrome/infra/{2} \t"{3}" at {0}:{1}'.format(*description)) | |
117 | |
118 if non_documented: # pragma: no branch | |
119 print('\nUndocumented metrics found:') | |
120 for description in non_documented: | |
121 print('/chrome/infra/{2} \t at {0}:{1}'.format(*description)) | |
OLD | NEW |