OLD | NEW |
1 # Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2010 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 """A database of OWNERS files.""" | 5 """A database of OWNERS files.""" |
6 | 6 |
7 import collections | 7 import collections |
8 import re | 8 import re |
9 | 9 |
10 | 10 |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
68 self.stop_looking = set(['']) | 68 self.stop_looking = set(['']) |
69 | 69 |
70 def reviewers_for(self, files): | 70 def reviewers_for(self, files): |
71 """Returns a suggested set of reviewers that will cover the files. | 71 """Returns a suggested set of reviewers that will cover the files. |
72 | 72 |
73 files is a sequence of paths relative to (and under) self.root.""" | 73 files is a sequence of paths relative to (and under) self.root.""" |
74 self._check_paths(files) | 74 self._check_paths(files) |
75 self._load_data_needed_for(files) | 75 self._load_data_needed_for(files) |
76 return self._covering_set_of_owners_for(files) | 76 return self._covering_set_of_owners_for(files) |
77 | 77 |
78 def files_are_covered_by(self, files, reviewers): | 78 def directories_not_covered_by(self, files, reviewers): |
79 """Returns whether every file is owned by at least one reviewer.""" | 79 """Returns the set of directories that are not owned by a reviewer. |
80 return not self.files_not_covered_by(files, reviewers) | |
81 | 80 |
82 def files_not_covered_by(self, files, reviewers): | 81 Determines which of the given files are not owned by at least one of the |
83 """Returns the set of files that are not owned by at least one reviewer. | 82 reviewers, then returns a set containing the applicable enclosing |
| 83 directories, i.e. the ones upward from the files that have OWNERS files. |
84 | 84 |
85 Args: | 85 Args: |
86 files is a sequence of paths relative to (and under) self.root. | 86 files is a sequence of paths relative to (and under) self.root. |
87 reviewers is a sequence of strings matching self.email_regexp.""" | 87 reviewers is a sequence of strings matching self.email_regexp. |
| 88 """ |
88 self._check_paths(files) | 89 self._check_paths(files) |
89 self._check_reviewers(reviewers) | 90 self._check_reviewers(reviewers) |
90 if not reviewers: | 91 self._load_data_needed_for(files) |
91 return files | |
92 | 92 |
93 self._load_data_needed_for(files) | 93 dirs = set([self.os_path.dirname(f) for f in files]) |
94 files_by_dir = self._files_by_dir(files) | |
95 covered_dirs = self._dirs_covered_by(reviewers) | 94 covered_dirs = self._dirs_covered_by(reviewers) |
96 uncovered_files = [] | 95 uncovered_dirs = [self._enclosing_dir_with_owners(d) for d in dirs |
97 for d, files_in_d in files_by_dir.iteritems(): | 96 if not self._is_dir_covered_by(d, covered_dirs)] |
98 if not self._is_dir_covered_by(d, covered_dirs): | 97 |
99 uncovered_files.extend(files_in_d) | 98 return set(uncovered_dirs) |
100 return set(uncovered_files) | |
101 | 99 |
102 def _check_paths(self, files): | 100 def _check_paths(self, files): |
103 def _is_under(f, pfx): | 101 def _is_under(f, pfx): |
104 return self.os_path.abspath(self.os_path.join(pfx, f)).startswith(pfx) | 102 return self.os_path.abspath(self.os_path.join(pfx, f)).startswith(pfx) |
105 _assert_is_collection(files) | 103 _assert_is_collection(files) |
106 assert all(_is_under(f, self.os_path.abspath(self.root)) for f in files) | 104 assert all(_is_under(f, self.os_path.abspath(self.root)) for f in files) |
107 | 105 |
108 def _check_reviewers(self, reviewers): | 106 def _check_reviewers(self, reviewers): |
109 _assert_is_collection(reviewers) | 107 _assert_is_collection(reviewers) |
110 assert all(self.email_regexp.match(r) for r in reviewers) | 108 assert all(self.email_regexp.match(r) for r in reviewers) |
111 | 109 |
112 def _files_by_dir(self, files): | |
113 dirs = {} | |
114 for f in files: | |
115 dirs.setdefault(self.os_path.dirname(f), []).append(f) | |
116 return dirs | |
117 | |
118 def _dirs_covered_by(self, reviewers): | 110 def _dirs_covered_by(self, reviewers): |
119 dirs = self.owned_by[EVERYONE] | 111 dirs = self.owned_by[EVERYONE] |
120 for r in reviewers: | 112 for r in reviewers: |
121 dirs = dirs | self.owned_by.get(r, set()) | 113 dirs = dirs | self.owned_by.get(r, set()) |
122 return dirs | 114 return dirs |
123 | 115 |
124 def _stop_looking(self, dirname): | 116 def _stop_looking(self, dirname): |
125 return dirname in self.stop_looking | 117 return dirname in self.stop_looking |
126 | 118 |
127 def _is_dir_covered_by(self, dirname, covered_dirs): | 119 def _is_dir_covered_by(self, dirname, covered_dirs): |
128 while not dirname in covered_dirs and not self._stop_looking(dirname): | 120 while not dirname in covered_dirs and not self._stop_looking(dirname): |
129 dirname = self.os_path.dirname(dirname) | 121 dirname = self.os_path.dirname(dirname) |
130 return dirname in covered_dirs | 122 return dirname in covered_dirs |
131 | 123 |
| 124 def _enclosing_dir_with_owners(self, directory): |
| 125 """Returns the innermost enclosing directory that has an OWNERS file.""" |
| 126 dirpath = directory |
| 127 while not dirpath in self.owners_for: |
| 128 if self._stop_looking(dirpath): |
| 129 break |
| 130 dirpath = self.os_path.dirname(dirpath) |
| 131 return dirpath |
| 132 |
132 def _load_data_needed_for(self, files): | 133 def _load_data_needed_for(self, files): |
133 for f in files: | 134 for f in files: |
134 dirpath = self.os_path.dirname(f) | 135 dirpath = self.os_path.dirname(f) |
135 while not dirpath in self.owners_for: | 136 while not dirpath in self.owners_for: |
136 self._read_owners_in_dir(dirpath) | 137 self._read_owners_in_dir(dirpath) |
137 if self._stop_looking(dirpath): | 138 if self._stop_looking(dirpath): |
138 break | 139 break |
139 dirpath = self.os_path.dirname(dirpath) | 140 dirpath = self.os_path.dirname(dirpath) |
140 | 141 |
141 def _read_owners_in_dir(self, dirpath): | 142 def _read_owners_in_dir(self, dirpath): |
(...skipping 27 matching lines...) Expand all Loading... |
169 # short combinations of owners. | 170 # short combinations of owners. |
170 every_owner = set() | 171 every_owner = set() |
171 for f in files: | 172 for f in files: |
172 dirname = self.os_path.dirname(f) | 173 dirname = self.os_path.dirname(f) |
173 while dirname in self.owners_for: | 174 while dirname in self.owners_for: |
174 every_owner |= self.owners_for[dirname] | 175 every_owner |= self.owners_for[dirname] |
175 if self._stop_looking(dirname): | 176 if self._stop_looking(dirname): |
176 break | 177 break |
177 dirname = self.os_path.dirname(dirname) | 178 dirname = self.os_path.dirname(dirname) |
178 return every_owner | 179 return every_owner |
OLD | NEW |