blob: 5c23d2c63bdc5d9804dfb1561410713ddfea2b94 [file] [log] [blame]
maruel@chromium.org725f1c32011-04-01 20:24:54 +00001#!/usr/bin/env python
2# Copyright (c) 2011 The Chromium Authors. All rights reserved.
nirnimesh@chromium.orgb2ab4942009-06-11 21:39:19 +00003# 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 19:49:57 +010041 _path_regexps = {} # Name -> Regular expression mapping
nirnimesh@chromium.orgb2ab4942009-06-11 21:39:19 +000042 _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):
nirnimesh@chromium.orgb82e4092009-06-18 14:24:08 +000049 """Returns path to WATCHLISTS file."""
nirnimesh@chromium.orgb2ab4942009-06-11 21:39:19 +000050 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
nirnimesh@chromium.orgb82e4092009-06-18 14:24:08 +000056 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
nirnimesh@chromium.orgb2ab4942009-06-11 21:39:19 +000067 def _LoadWatchlistRules(self):
nirnimesh@chromium.orgb82e4092009-06-18 14:24:08 +000068 """Load watchlists from WATCHLISTS file. Does nothing if not present."""
nirnimesh@chromium.orgb2ab4942009-06-11 21:39:19 +000069 if not self._HasWatchlistsFile():
70 return
nirnimesh@chromium.orgb2ab4942009-06-11 21:39:19 +000071
nirnimesh@chromium.orgb82e4092009-06-18 14:24:08 +000072 contents = self._ContentsOfWatchlistsFile()
nirnimesh@chromium.orgb2ab4942009-06-11 21:39:19 +000073 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 19:49:57 +010092 # 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
nirnimesh@chromium.orgb2ab4942009-06-11 21:39:19 +0000101 # 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 [u1@chromium.org, u2@gmail.com, ...]
114 """
115 watchers = set() # A set, to avoid duplicates
116 for path in paths:
nirnimesh@chromium.org76256af2009-06-18 22:52:12 +0000117 path = path.replace(os.sep, '/')
Raphael Kubo da Costaae979432017-11-02 19:49:57 +0100118 for name, rule in self._path_regexps.iteritems():
maruel@chromium.orgcb2985f2010-11-03 14:08:31 +0000119 if name not in self._watchlists:
120 continue
Raphael Kubo da Costaae979432017-11-02 19:49:57 +0100121 if rule.search(path):
nirnimesh@chromium.orgb2ab4942009-06-11 21:39:19 +0000122 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)