blob: 096eb10e709606517cd4532b525d6103a9bfc3a6 [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
Raul Tambre80ee78e2019-05-06 22:41:05 +000020from __future__ import print_function
21
nirnimesh@chromium.orgb2ab4942009-06-11 21:39:19 +000022import logging
23import os
24import re
25import sys
26
27
28class Watchlists(object):
29 """Manage Watchlists.
30
31 This class provides mechanism to load watchlists for a repo and identify
32 watchers.
33 Usage:
34 wl = Watchlists("/path/to/repo/root")
35 watchers = wl.GetWatchersForPaths(["/path/to/file1",
36 "/path/to/file2",])
37 """
38
39 _RULES = "WATCHLISTS"
40 _RULES_FILENAME = _RULES
41 _repo_root = None
42 _defns = {} # Definitions
Raphael Kubo da Costaae979432017-11-02 19:49:57 +010043 _path_regexps = {} # Name -> Regular expression mapping
nirnimesh@chromium.orgb2ab4942009-06-11 21:39:19 +000044 _watchlists = {} # name to email mapping
45
46 def __init__(self, repo_root):
47 self._repo_root = repo_root
48 self._LoadWatchlistRules()
49
50 def _GetRulesFilePath(self):
nirnimesh@chromium.orgb82e4092009-06-18 14:24:08 +000051 """Returns path to WATCHLISTS file."""
nirnimesh@chromium.orgb2ab4942009-06-11 21:39:19 +000052 return os.path.join(self._repo_root, self._RULES_FILENAME)
53
54 def _HasWatchlistsFile(self):
55 """Determine if watchlists are available for this repo."""
56 return os.path.exists(self._GetRulesFilePath())
57
nirnimesh@chromium.orgb82e4092009-06-18 14:24:08 +000058 def _ContentsOfWatchlistsFile(self):
59 """Read the WATCHLISTS file and return its contents."""
60 try:
61 watchlists_file = open(self._GetRulesFilePath())
62 contents = watchlists_file.read()
63 watchlists_file.close()
64 return contents
Raul Tambre7c938462019-05-24 16:35:35 +000065 except IOError as e:
nirnimesh@chromium.orgb82e4092009-06-18 14:24:08 +000066 logging.error("Cannot read %s: %s" % (self._GetRulesFilePath(), e))
67 return ''
68
nirnimesh@chromium.orgb2ab4942009-06-11 21:39:19 +000069 def _LoadWatchlistRules(self):
nirnimesh@chromium.orgb82e4092009-06-18 14:24:08 +000070 """Load watchlists from WATCHLISTS file. Does nothing if not present."""
nirnimesh@chromium.orgb2ab4942009-06-11 21:39:19 +000071 if not self._HasWatchlistsFile():
72 return
nirnimesh@chromium.orgb2ab4942009-06-11 21:39:19 +000073
nirnimesh@chromium.orgb82e4092009-06-18 14:24:08 +000074 contents = self._ContentsOfWatchlistsFile()
nirnimesh@chromium.orgb2ab4942009-06-11 21:39:19 +000075 watchlists_data = None
76 try:
77 watchlists_data = eval(contents, {'__builtins__': None}, None)
Raul Tambre7c938462019-05-24 16:35:35 +000078 except SyntaxError as e:
nirnimesh@chromium.orgb2ab4942009-06-11 21:39:19 +000079 logging.error("Cannot parse %s. %s" % (self._GetRulesFilePath(), e))
80 return
81
82 defns = watchlists_data.get("WATCHLIST_DEFINITIONS")
83 if not defns:
84 logging.error("WATCHLIST_DEFINITIONS not defined in %s" %
85 self._GetRulesFilePath())
86 return
87 watchlists = watchlists_data.get("WATCHLISTS")
88 if not watchlists:
89 logging.error("WATCHLISTS not defined in %s" % self._GetRulesFilePath())
90 return
91 self._defns = defns
92 self._watchlists = watchlists
93
Raphael Kubo da Costaae979432017-11-02 19:49:57 +010094 # Compile the regular expressions ahead of time to avoid creating them
95 # on-the-fly multiple times per file.
96 self._path_regexps = {}
97 for name, rule in defns.iteritems():
98 filepath = rule.get('filepath')
99 if not filepath:
100 continue
101 self._path_regexps[name] = re.compile(filepath)
102
nirnimesh@chromium.orgb2ab4942009-06-11 21:39:19 +0000103 # Verify that all watchlist names are defined
104 for name in watchlists:
105 if name not in defns:
106 logging.error("%s not defined in %s" % (name, self._GetRulesFilePath()))
107
108 def GetWatchersForPaths(self, paths):
109 """Fetch the list of watchers for |paths|
110
111 Args:
112 paths: [path1, path2, ...]
113
114 Returns:
115 [u1@chromium.org, u2@gmail.com, ...]
116 """
117 watchers = set() # A set, to avoid duplicates
118 for path in paths:
nirnimesh@chromium.org76256af2009-06-18 22:52:12 +0000119 path = path.replace(os.sep, '/')
Raphael Kubo da Costaae979432017-11-02 19:49:57 +0100120 for name, rule in self._path_regexps.iteritems():
maruel@chromium.orgcb2985f2010-11-03 14:08:31 +0000121 if name not in self._watchlists:
122 continue
Raphael Kubo da Costaae979432017-11-02 19:49:57 +0100123 if rule.search(path):
nirnimesh@chromium.orgb2ab4942009-06-11 21:39:19 +0000124 map(watchers.add, self._watchlists[name])
125 return list(watchers)
126
127
128def main(argv):
129 # Confirm that watchlists can be parsed and spew out the watchers
130 if len(argv) < 2:
Raul Tambre80ee78e2019-05-06 22:41:05 +0000131 print("Usage (from the base of repo):")
132 print(" %s [file-1] [file-2] ...." % argv[0])
nirnimesh@chromium.orgb2ab4942009-06-11 21:39:19 +0000133 return 1
134 wl = Watchlists(os.getcwd())
135 watchers = wl.GetWatchersForPaths(argv[1:])
Raul Tambre80ee78e2019-05-06 22:41:05 +0000136 print(watchers)
nirnimesh@chromium.orgb2ab4942009-06-11 21:39:19 +0000137
138
139if __name__ == '__main__':
140 main(sys.argv)