blob: a6b5582aae94a4f74d1070ca2fd87947c9d29653 [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
41 _watchlists = {} # name to email mapping
42
43 def __init__(self, repo_root):
44 self._repo_root = repo_root
45 self._LoadWatchlistRules()
46
47 def _GetRulesFilePath(self):
nirnimesh@chromium.orgb82e4092009-06-18 14:24:08 +000048 """Returns path to WATCHLISTS file."""
nirnimesh@chromium.orgb2ab4942009-06-11 21:39:19 +000049 return os.path.join(self._repo_root, self._RULES_FILENAME)
50
51 def _HasWatchlistsFile(self):
52 """Determine if watchlists are available for this repo."""
53 return os.path.exists(self._GetRulesFilePath())
54
nirnimesh@chromium.orgb82e4092009-06-18 14:24:08 +000055 def _ContentsOfWatchlistsFile(self):
56 """Read the WATCHLISTS file and return its contents."""
57 try:
58 watchlists_file = open(self._GetRulesFilePath())
59 contents = watchlists_file.read()
60 watchlists_file.close()
61 return contents
62 except IOError, e:
63 logging.error("Cannot read %s: %s" % (self._GetRulesFilePath(), e))
64 return ''
65
nirnimesh@chromium.orgb2ab4942009-06-11 21:39:19 +000066 def _LoadWatchlistRules(self):
nirnimesh@chromium.orgb82e4092009-06-18 14:24:08 +000067 """Load watchlists from WATCHLISTS file. Does nothing if not present."""
nirnimesh@chromium.orgb2ab4942009-06-11 21:39:19 +000068 if not self._HasWatchlistsFile():
69 return
nirnimesh@chromium.orgb2ab4942009-06-11 21:39:19 +000070
nirnimesh@chromium.orgb82e4092009-06-18 14:24:08 +000071 contents = self._ContentsOfWatchlistsFile()
nirnimesh@chromium.orgb2ab4942009-06-11 21:39:19 +000072 watchlists_data = None
73 try:
74 watchlists_data = eval(contents, {'__builtins__': None}, None)
75 except SyntaxError, e:
76 logging.error("Cannot parse %s. %s" % (self._GetRulesFilePath(), e))
77 return
78
79 defns = watchlists_data.get("WATCHLIST_DEFINITIONS")
80 if not defns:
81 logging.error("WATCHLIST_DEFINITIONS not defined in %s" %
82 self._GetRulesFilePath())
83 return
84 watchlists = watchlists_data.get("WATCHLISTS")
85 if not watchlists:
86 logging.error("WATCHLISTS not defined in %s" % self._GetRulesFilePath())
87 return
88 self._defns = defns
89 self._watchlists = watchlists
90
91 # Verify that all watchlist names are defined
92 for name in watchlists:
93 if name not in defns:
94 logging.error("%s not defined in %s" % (name, self._GetRulesFilePath()))
95
96 def GetWatchersForPaths(self, paths):
97 """Fetch the list of watchers for |paths|
98
99 Args:
100 paths: [path1, path2, ...]
101
102 Returns:
103 [u1@chromium.org, u2@gmail.com, ...]
104 """
105 watchers = set() # A set, to avoid duplicates
106 for path in paths:
nirnimesh@chromium.org76256af2009-06-18 22:52:12 +0000107 path = path.replace(os.sep, '/')
nirnimesh@chromium.orgb2ab4942009-06-11 21:39:19 +0000108 for name, rule in self._defns.iteritems():
maruel@chromium.orgcb2985f2010-11-03 14:08:31 +0000109 if name not in self._watchlists:
110 continue
nirnimesh@chromium.orgb2ab4942009-06-11 21:39:19 +0000111 rex_str = rule.get('filepath')
maruel@chromium.orgcb2985f2010-11-03 14:08:31 +0000112 if not rex_str:
113 continue
nirnimesh@chromium.orgb2ab4942009-06-11 21:39:19 +0000114 if re.search(rex_str, path):
115 map(watchers.add, self._watchlists[name])
116 return list(watchers)
117
118
119def main(argv):
120 # Confirm that watchlists can be parsed and spew out the watchers
121 if len(argv) < 2:
122 print "Usage (from the base of repo):"
123 print " %s [file-1] [file-2] ...." % argv[0]
124 return 1
125 wl = Watchlists(os.getcwd())
126 watchers = wl.GetWatchersForPaths(argv[1:])
127 print watchers
128
129
130if __name__ == '__main__':
131 main(sys.argv)