Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(38)

Side by Side Diff: owners.py

Issue 12314044: return a list of uncovered files, not uncovered dirs (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: fix formatting Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | presubmit_canned_checks.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 # Copyright (c) 2012 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 OWNERS files indicate who is allowed to approve changes in a specific directory 7 OWNERS files indicate who is allowed to approve changes in a specific directory
8 (or who is allowed to make changes without needing approval of another OWNER). 8 (or who is allowed to make changes without needing approval of another OWNER).
9 Note that all changes must still be reviewed by someone familiar with the code, 9 Note that all changes must still be reviewed by someone familiar with the code,
10 so you may need approval from both an OWNER and a reviewer in many cases. 10 so you may need approval from both an OWNER and a reviewer in many cases.
(...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after
122 self._check_paths(files) 122 self._check_paths(files)
123 self._load_data_needed_for(files) 123 self._load_data_needed_for(files)
124 suggested_owners = self._covering_set_of_owners_for(files) 124 suggested_owners = self._covering_set_of_owners_for(files)
125 if EVERYONE in suggested_owners: 125 if EVERYONE in suggested_owners:
126 if len(suggested_owners) > 1: 126 if len(suggested_owners) > 1:
127 suggested_owners.remove(EVERYONE) 127 suggested_owners.remove(EVERYONE)
128 else: 128 else:
129 suggested_owners = set(['<anyone>']) 129 suggested_owners = set(['<anyone>'])
130 return suggested_owners 130 return suggested_owners
131 131
132 # TODO(dpranke): rename to objects_not_covered_by 132 def files_not_covered_by(self, files, reviewers):
133 def directories_not_covered_by(self, files, reviewers): 133 """Returns the files not owned by one of the reviewers.
134 """Returns the set of directories that are not owned by a reviewer.
135
136 Determines which of the given files are not owned by at least one of the
137 reviewers, then returns a set containing the applicable enclosing
138 directories, i.e. the ones upward from the files that have OWNERS files.
139 134
140 Args: 135 Args:
141 files is a sequence of paths relative to (and under) self.root. 136 files is a sequence of paths relative to (and under) self.root.
142 reviewers is a sequence of strings matching self.email_regexp. 137 reviewers is a sequence of strings matching self.email_regexp.
143 """ 138 """
144 self._check_paths(files) 139 self._check_paths(files)
145 self._check_reviewers(reviewers) 140 self._check_reviewers(reviewers)
146 self._load_data_needed_for(files) 141 self._load_data_needed_for(files)
147 142
148 objs = set() 143 covered_objs = self._objs_covered_by(reviewers)
149 for f in files: 144 uncovered_files = [f for f in files
150 if f in self.owners_for: 145 if not self._is_obj_covered_by(f, covered_objs)]
151 objs.add(f)
152 else:
153 objs.add(self.os_path.dirname(f))
154 146
155 covered_objs = self._objs_covered_by(reviewers) 147 return set(uncovered_files)
156 uncovered_objs = [self._enclosing_obj_with_owners(o) for o in objs
157 if not self._is_obj_covered_by(o, covered_objs)]
158
159 return set(uncovered_objs)
160
161 objects_not_covered_by = directories_not_covered_by
162 148
163 def _check_paths(self, files): 149 def _check_paths(self, files):
164 def _is_under(f, pfx): 150 def _is_under(f, pfx):
165 return self.os_path.abspath(self.os_path.join(pfx, f)).startswith(pfx) 151 return self.os_path.abspath(self.os_path.join(pfx, f)).startswith(pfx)
166 _assert_is_collection(files) 152 _assert_is_collection(files)
167 assert all(not self.os_path.isabs(f) and 153 assert all(not self.os_path.isabs(f) and
168 _is_under(f, self.os_path.abspath(self.root)) for f in files) 154 _is_under(f, self.os_path.abspath(self.root)) for f in files)
169 155
170 def _check_reviewers(self, reviewers): 156 def _check_reviewers(self, reviewers):
171 _assert_is_collection(reviewers) 157 _assert_is_collection(reviewers)
172 assert all(self.email_regexp.match(r) for r in reviewers) 158 assert all(self.email_regexp.match(r) for r in reviewers)
173 159
174 # TODO(dpranke): Rename to _objs_covered_by and update_callers 160 def _objs_covered_by(self, reviewers):
175 def _dirs_covered_by(self, reviewers): 161 objs = self.owned_by[EVERYONE]
176 dirs = self.owned_by[EVERYONE]
177 for r in reviewers: 162 for r in reviewers:
178 dirs = dirs | self.owned_by.get(r, set()) 163 objs = objs | self.owned_by.get(r, set())
179 return dirs 164 return objs
180 165
181 _objs_covered_by = _dirs_covered_by 166 def _stop_looking(self, objname):
167 return objname in self.stop_looking
182 168
183 def _stop_looking(self, dirname): 169 def _is_obj_covered_by(self, objname, covered_objs):
184 return dirname in self.stop_looking 170 while not objname in covered_objs and not self._stop_looking(objname):
171 objname = self.os_path.dirname(objname)
172 return objname in covered_objs
185 173
186 # TODO(dpranke): Rename to _is_dir_covered_by and update callers. 174 def _enclosing_dir_with_owners(self, objname):
187 def _is_dir_covered_by(self, dirname, covered_dirs):
188 while not dirname in covered_dirs and not self._stop_looking(dirname):
189 dirname = self.os_path.dirname(dirname)
190 return dirname in covered_dirs
191
192 _is_obj_covered_by = _is_dir_covered_by
193
194 # TODO(dpranke): Rename to _enclosing_obj_with_owners and update callers.
195 def _enclosing_dir_with_owners(self, directory):
196 """Returns the innermost enclosing directory that has an OWNERS file.""" 175 """Returns the innermost enclosing directory that has an OWNERS file."""
197 dirpath = directory 176 dirpath = objname
198 while not dirpath in self.owners_for: 177 while not dirpath in self.owners_for:
199 if self._stop_looking(dirpath): 178 if self._stop_looking(dirpath):
200 break 179 break
201 dirpath = self.os_path.dirname(dirpath) 180 dirpath = self.os_path.dirname(dirpath)
202 return dirpath 181 return dirpath
203 182
204 _enclosing_obj_with_owners = _enclosing_dir_with_owners
205
206 def _load_data_needed_for(self, files): 183 def _load_data_needed_for(self, files):
207 for f in files: 184 for f in files:
208 dirpath = self.os_path.dirname(f) 185 dirpath = self.os_path.dirname(f)
209 while not dirpath in self.owners_for: 186 while not dirpath in self.owners_for:
210 self._read_owners_in_dir(dirpath) 187 self._read_owners_in_dir(dirpath)
211 if self._stop_looking(dirpath): 188 if self._stop_looking(dirpath):
212 break 189 break
213 dirpath = self.os_path.dirname(dirpath) 190 dirpath = self.os_path.dirname(dirpath)
214 191
215 def _read_owners_in_dir(self, dirpath): 192 def _read_owners_in_dir(self, dirpath):
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after
309 if num_directories_owned: 286 if num_directories_owned:
310 total_costs_by_owner[owner] = (total_distance / 287 total_costs_by_owner[owner] = (total_distance /
311 pow(num_directories_owned, 1.75)) 288 pow(num_directories_owned, 1.75))
312 289
313 # Return the lowest cost owner. In the case of a tie, pick one randomly. 290 # Return the lowest cost owner. In the case of a tie, pick one randomly.
314 lowest_cost = min(total_costs_by_owner.itervalues()) 291 lowest_cost = min(total_costs_by_owner.itervalues())
315 lowest_cost_owners = filter( 292 lowest_cost_owners = filter(
316 lambda owner: total_costs_by_owner[owner] == lowest_cost, 293 lambda owner: total_costs_by_owner[owner] == lowest_cost,
317 total_costs_by_owner) 294 total_costs_by_owner)
318 return random.Random().choice(lowest_cost_owners) 295 return random.Random().choice(lowest_cost_owners)
OLDNEW
« no previous file with comments | « no previous file | presubmit_canned_checks.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698