blob: 5c23d2c63bdc5d9804dfb1561410713ddfea2b94 [file] [log] [blame]
[email protected]725f1c32011-04-01 20:24:541#!/usr/bin/env python
2# Copyright (c) 2011 The Chromium Authors. All rights reserved.
[email protected]b2ab4942009-06-11 21:39:193# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Watchlists
7
8Watchlists is a mechanism that allow a developer (a "watcher") to watch over
9portions of code that he is interested in. A "watcher" will be cc-ed to
10changes that modify that portion of code, thereby giving him an opportunity
11to make comments on codereview.chromium.org even before the change is
12committed.
13Refer: http://dev.chromium.org/developers/contributing-code/watchlists
14
15When invoked directly from the base of a repository, this script lists out
16the watchers for files given on the command line. This is useful to verify
17changes to WATCHLISTS files.
18"""
19
20import logging
21import os
22import re
23import sys
24
25
26class Watchlists(object):
27 """Manage Watchlists.
28
29 This class provides mechanism to load watchlists for a repo and identify
30 watchers.
31 Usage:
32 wl = Watchlists("/path/to/repo/root")
33 watchers = wl.GetWatchersForPaths(["/path/to/file1",
34 "/path/to/file2",])
35 """
36
37 _RULES = "WATCHLISTS"
38 _RULES_FILENAME = _RULES
39 _repo_root = None
40 _defns = {} # Definitions
Raphael Kubo da Costaae979432017-11-02 18:49:5741 _path_regexps = {} # Name -> Regular expression mapping
[email protected]b2ab4942009-06-11 21:39:1942 _watchlists = {} # name to email mapping
43
44 def __init__(self, repo_root):
45 self._repo_root = repo_root
46 self._LoadWatchlistRules()
47
48 def _GetRulesFilePath(self):
[email protected]b82e4092009-06-18 14:24:0849 """Returns path to WATCHLISTS file."""
[email protected]b2ab4942009-06-11 21:39:1950 return os.path.join(self._repo_root, self._RULES_FILENAME)
51
52 def _HasWatchlistsFile(self):
53 """Determine if watchlists are available for this repo."""
54 return os.path.exists(self._GetRulesFilePath())
55
[email protected]b82e4092009-06-18 14:24:0856 def _ContentsOfWatchlistsFile(self):
57 """Read the WATCHLISTS file and return its contents."""
58 try:
59 watchlists_file = open(self._GetRulesFilePath())
60 contents = watchlists_file.read()
61 watchlists_file.close()
62 return contents
63 except IOError, e:
64 logging.error("Cannot read %s: %s" % (self._GetRulesFilePath(), e))
65 return ''
66
[email protected]b2ab4942009-06-11 21:39:1967 def _LoadWatchlistRules(self):
[email protected]b82e4092009-06-18 14:24:0868 """Load watchlists from WATCHLISTS file. Does nothing if not present."""
[email protected]b2ab4942009-06-11 21:39:1969 if not self._HasWatchlistsFile():
70 return
[email protected]b2ab4942009-06-11 21:39:1971
[email protected]b82e4092009-06-18 14:24:0872 contents = self._ContentsOfWatchlistsFile()
[email protected]b2ab4942009-06-11 21:39:1973 watchlists_data = None
74 try:
75 watchlists_data = eval(contents, {'__builtins__': None}, None)
76 except SyntaxError, e:
77 logging.error("Cannot parse %s. %s" % (self._GetRulesFilePath(), e))
78 return
79
80 defns = watchlists_data.get("WATCHLIST_DEFINITIONS")
81 if not defns:
82 logging.error("WATCHLIST_DEFINITIONS not defined in %s" %
83 self._GetRulesFilePath())
84 return
85 watchlists = watchlists_data.get("WATCHLISTS")
86 if not watchlists:
87 logging.error("WATCHLISTS not defined in %s" % self._GetRulesFilePath())
88 return
89 self._defns = defns
90 self._watchlists = watchlists
91
Raphael Kubo da Costaae979432017-11-02 18:49:5792 # Compile the regular expressions ahead of time to avoid creating them
93 # on-the-fly multiple times per file.
94 self._path_regexps = {}
95 for name, rule in defns.iteritems():
96 filepath = rule.get('filepath')
97 if not filepath:
98 continue
99 self._path_regexps[name] = re.compile(filepath)
100
[email protected]b2ab4942009-06-11 21:39:19101 # Verify that all watchlist names are defined
102 for name in watchlists:
103 if name not in defns:
104 logging.error("%s not defined in %s" % (name, self._GetRulesFilePath()))
105
106 def GetWatchersForPaths(self, paths):
107 """Fetch the list of watchers for |paths|
108
109 Args:
110 paths: [path1, path2, ...]
111
112 Returns:
113 [[email protected], [email protected], ...]
114 """
115 watchers = set() # A set, to avoid duplicates
116 for path in paths:
[email protected]76256af2009-06-18 22:52:12117 path = path.replace(os.sep, '/')
Raphael Kubo da Costaae979432017-11-02 18:49:57118 for name, rule in self._path_regexps.iteritems():
[email protected]cb2985f2010-11-03 14:08:31119 if name not in self._watchlists:
120 continue
Raphael Kubo da Costaae979432017-11-02 18:49:57121 if rule.search(path):
[email protected]b2ab4942009-06-11 21:39:19122 map(watchers.add, self._watchlists[name])
123 return list(watchers)
124
125
126def main(argv):
127 # Confirm that watchlists can be parsed and spew out the watchers
128 if len(argv) < 2:
129 print "Usage (from the base of repo):"
130 print " %s [file-1] [file-2] ...." % argv[0]
131 return 1
132 wl = Watchlists(os.getcwd())
133 watchers = wl.GetWatchersForPaths(argv[1:])
134 print watchers
135
136
137if __name__ == '__main__':
138 main(sys.argv)