OLD | NEW |
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 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
92 | 92 |
93 This class allows you to find a suggested set of reviewers for a list | 93 This class allows you to find a suggested set of reviewers for a list |
94 of changed files, and see if a list of changed files is covered by a | 94 of changed files, and see if a list of changed files is covered by a |
95 list of reviewers.""" | 95 list of reviewers.""" |
96 | 96 |
97 def __init__(self, root, fopen, os_path, glob): | 97 def __init__(self, root, fopen, os_path, glob): |
98 """Args: | 98 """Args: |
99 root: the path to the root of the Repository | 99 root: the path to the root of the Repository |
100 open: function callback to open a text file for reading | 100 open: function callback to open a text file for reading |
101 os_path: module/object callback with fields for 'abspath', 'dirname', | 101 os_path: module/object callback with fields for 'abspath', 'dirname', |
102 'exists', and 'join' | 102 'exists', 'join', and 'relpath' |
103 glob: function callback to list entries in a directory match a glob | 103 glob: function callback to list entries in a directory match a glob |
104 (i.e., glob.glob) | 104 (i.e., glob.glob) |
105 """ | 105 """ |
106 self.root = root | 106 self.root = root |
107 self.fopen = fopen | 107 self.fopen = fopen |
108 self.os_path = os_path | 108 self.os_path = os_path |
109 self.glob = glob | 109 self.glob = glob |
110 | 110 |
111 # Pick a default email regexp to use; callers can override as desired. | 111 # Pick a default email regexp to use; callers can override as desired. |
112 self.email_regexp = re.compile(BASIC_EMAIL_REGEXP) | 112 self.email_regexp = re.compile(BASIC_EMAIL_REGEXP) |
(...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
285 else: | 285 else: |
286 raise SyntaxErrorInOwnersFile(owners_path, lineno, | 286 raise SyntaxErrorInOwnersFile(owners_path, lineno, |
287 ('%s is not a "set" directive, file include, "*", ' | 287 ('%s is not a "set" directive, file include, "*", ' |
288 'or an email address: "%s"' % (line_type, directive))) | 288 'or an email address: "%s"' % (line_type, directive))) |
289 | 289 |
290 def _resolve_include(self, path, start): | 290 def _resolve_include(self, path, start): |
291 if path.startswith('//'): | 291 if path.startswith('//'): |
292 include_path = path[2:] | 292 include_path = path[2:] |
293 else: | 293 else: |
294 assert start.startswith(self.root) | 294 assert start.startswith(self.root) |
295 start = self.os_path.dirname(start[len(self.root):]) | 295 start = self.os_path.dirname(self.os_path.relpath(start, self.root)) |
296 include_path = self.os_path.join(start, path) | 296 include_path = self.os_path.join(start, path) |
297 | 297 |
298 owners_path = self.os_path.join(self.root, include_path) | 298 owners_path = self.os_path.join(self.root, include_path) |
299 if not self.os_path.exists(owners_path): | 299 if not self.os_path.exists(owners_path): |
300 return None | 300 return None |
301 | 301 |
302 return include_path | 302 return include_path |
303 | 303 |
304 def _covering_set_of_owners_for(self, files, author): | 304 def _covering_set_of_owners_for(self, files, author): |
305 dirs_remaining = set(self._enclosing_dir_with_owners(f) for f in files) | 305 dirs_remaining = set(self._enclosing_dir_with_owners(f) for f in files) |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
358 @staticmethod | 358 @staticmethod |
359 def lowest_cost_owner(all_possible_owners, dirs): | 359 def lowest_cost_owner(all_possible_owners, dirs): |
360 total_costs_by_owner = Database.total_costs_by_owner(all_possible_owners, | 360 total_costs_by_owner = Database.total_costs_by_owner(all_possible_owners, |
361 dirs) | 361 dirs) |
362 # Return the lowest cost owner. In the case of a tie, pick one randomly. | 362 # Return the lowest cost owner. In the case of a tie, pick one randomly. |
363 lowest_cost = min(total_costs_by_owner.itervalues()) | 363 lowest_cost = min(total_costs_by_owner.itervalues()) |
364 lowest_cost_owners = filter( | 364 lowest_cost_owners = filter( |
365 lambda owner: total_costs_by_owner[owner] == lowest_cost, | 365 lambda owner: total_costs_by_owner[owner] == lowest_cost, |
366 total_costs_by_owner) | 366 total_costs_by_owner) |
367 return random.Random().choice(lowest_cost_owners) | 367 return random.Random().choice(lowest_cost_owners) |
OLD | NEW |