| 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 |
| 11 # If this is present by itself on a line, this means that everyone can review. | 11 # If this is present by itself on a line, this means that everyone can review. |
| 12 EVERYONE = '*' | 12 EVERYONE = '*' |
| 13 | 13 |
| 14 | 14 |
| 15 # Recognizes 'X@Y' email addresses. Very simplistic. | 15 # Recognizes 'X@Y' email addresses. Very simplistic. |
| 16 BASIC_EMAIL_REGEXP = r'^[\w\-\+\%\.]+\@[\w\-\+\%\.]+$' | 16 BASIC_EMAIL_REGEXP = r'^[\w\-\+\%\.]+\@[\w\-\+\%\.]+$' |
| 17 | 17 |
| 18 | 18 |
| 19 def _assert_is_collection(obj): |
| 20 assert (isinstance(obj, collections.Iterable) and |
| 21 isinstance(obj, collections.Sized) and |
| 22 not isinstance(obj, basestring)) |
| 23 |
| 24 |
| 19 class SyntaxErrorInOwnersFile(Exception): | 25 class SyntaxErrorInOwnersFile(Exception): |
| 20 def __init__(self, path, lineno, msg): | 26 def __init__(self, path, lineno, msg): |
| 21 super(SyntaxErrorInOwnersFile, self).__init__((path, lineno, msg)) | 27 super(SyntaxErrorInOwnersFile, self).__init__((path, lineno, msg)) |
| 22 self.path = path | 28 self.path = path |
| 23 self.lineno = lineno | 29 self.lineno = lineno |
| 24 self.msg = msg | 30 self.msg = msg |
| 25 | 31 |
| 26 def __str__(self): | 32 def __str__(self): |
| 27 return "%s:%d syntax error: %s" % (self.path, self.lineno, self.msg) | 33 return "%s:%d syntax error: %s" % (self.path, self.lineno, self.msg) |
| 28 | 34 |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 83 | 89 |
| 84 self._load_data_needed_for(files) | 90 self._load_data_needed_for(files) |
| 85 files_by_dir = self._files_by_dir(files) | 91 files_by_dir = self._files_by_dir(files) |
| 86 covered_dirs = self._dirs_covered_by(reviewers) | 92 covered_dirs = self._dirs_covered_by(reviewers) |
| 87 uncovered_files = [] | 93 uncovered_files = [] |
| 88 for d, files_in_d in files_by_dir.iteritems(): | 94 for d, files_in_d in files_by_dir.iteritems(): |
| 89 if not self._is_dir_covered_by(d, covered_dirs): | 95 if not self._is_dir_covered_by(d, covered_dirs): |
| 90 uncovered_files.extend(files_in_d) | 96 uncovered_files.extend(files_in_d) |
| 91 return set(uncovered_files) | 97 return set(uncovered_files) |
| 92 | 98 |
| 93 def _check_collection(self, obj): | |
| 94 assert (isinstance(obj, collections.Iterable) and | |
| 95 isinstance(obj, collections.Sized) and | |
| 96 not isinstance(obj, basestring)) | |
| 97 | |
| 98 def _check_paths(self, files): | 99 def _check_paths(self, files): |
| 99 def _is_under(f, pfx): | 100 def _is_under(f, pfx): |
| 100 return self.os_path.abspath(self.os_path.join(pfx, f)).startswith(pfx) | 101 return self.os_path.abspath(self.os_path.join(pfx, f)).startswith(pfx) |
| 101 self._check_collection(files) | 102 _assert_is_collection(files) |
| 102 assert all(_is_under(f, self.os_path.abspath(self.root)) for f in files) | 103 assert all(_is_under(f, self.os_path.abspath(self.root)) for f in files) |
| 103 | 104 |
| 104 def _check_reviewers(self, reviewers): | 105 def _check_reviewers(self, reviewers): |
| 105 self._check_collection(reviewers) | 106 _assert_is_collection(reviewers) |
| 106 assert all(self.email_regexp.match(r) for r in reviewers) | 107 assert all(self.email_regexp.match(r) for r in reviewers) |
| 107 | 108 |
| 108 def _files_by_dir(self, files): | 109 def _files_by_dir(self, files): |
| 109 dirs = {} | 110 dirs = {} |
| 110 for f in files: | 111 for f in files: |
| 111 dirs.setdefault(self.os_path.dirname(f), []).append(f) | 112 dirs.setdefault(self.os_path.dirname(f), []).append(f) |
| 112 return dirs | 113 return dirs |
| 113 | 114 |
| 114 def _dirs_covered_by(self, reviewers): | 115 def _dirs_covered_by(self, reviewers): |
| 115 dirs = self.owned_by[EVERYONE] | 116 dirs = self.owned_by[EVERYONE] |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 165 # short combinations of owners. | 166 # short combinations of owners. |
| 166 every_owner = set() | 167 every_owner = set() |
| 167 for f in files: | 168 for f in files: |
| 168 dirname = self.os_path.dirname(f) | 169 dirname = self.os_path.dirname(f) |
| 169 while dirname in self.owners_for: | 170 while dirname in self.owners_for: |
| 170 every_owner |= self.owners_for[dirname] | 171 every_owner |= self.owners_for[dirname] |
| 171 if self._stop_looking(dirname): | 172 if self._stop_looking(dirname): |
| 172 break | 173 break |
| 173 dirname = self.os_path.dirname(dirname) | 174 dirname = self.os_path.dirname(dirname) |
| 174 return every_owner | 175 return every_owner |
| OLD | NEW |