Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 # Copyright (c) 2010 The Chromium Authors. All rights reserved. | |
|
M-A Ruel
2011/02/25 20:41:54
2011
| |
| 2 # Use of this source code is governed by a BSD-style license that can be | |
| 3 # found in the LICENSE file. | |
| 4 | |
| 5 """A database of OWNERS files.""" | |
| 6 | |
| 7 class Assertion(AssertionError): | |
| 8 pass | |
| 9 | |
| 10 class Database(object): | |
| 11 def __init__(self, root, fopen, os_path): | |
| 12 """Initializes the database of owners for a repository. | |
|
M-A Ruel
2011/02/25 20:41:54
Personal nit: constructor docstring are usually us
| |
| 13 | |
| 14 Args: | |
| 15 root: the path to the root of the Repository | |
| 16 all_owners: the list of every owner in the system | |
| 17 open: function callback to open a text file for reading | |
| 18 os_path: module/object callback with fields for 'exists', | |
| 19 'dirname', and 'join' | |
| 20 """ | |
| 21 self.root = root | |
| 22 self.fopen = fopen | |
| 23 self.os_path = os_path | |
| 24 | |
| 25 # Mapping of files to authorized owners. | |
| 26 self.files_owned_by = {} | |
| 27 | |
| 28 # Mapping of owners to the files they own. | |
| 29 self.owners_for = {} | |
| 30 | |
| 31 # In-memory cached map of files to their OWNERS files. | |
| 32 self.owners_file_for = {} | |
| 33 | |
| 34 # In-memory cache of OWNERS files and their contents | |
| 35 self.owners_files = {} | |
| 36 | |
| 37 def OwnersFor(self, files): | |
| 38 """Returns a sets of reviewers that will cover the set of files. | |
| 39 | |
| 40 The set of files are paths relative to (and under) self.root.""" | |
| 41 self._LoadDataNeededFor(files) | |
| 42 return self._CoveringSetOfOwnersFor(files) | |
| 43 | |
| 44 def FilesAreCoveredBy(self, files, reviewers): | |
| 45 return not self.FilesNotCoveredBy(files, reviewers) | |
| 46 | |
| 47 def FilesNotCoveredBy(self, files, reviewers): | |
| 48 covered_files = set() | |
| 49 for reviewer in reviewers: | |
| 50 covered_files = covered_files.union(self.files_owned_by[reviewer]) | |
| 51 return files.difference(covered_files) | |
| 52 | |
| 53 def _LoadDataNeededFor(self, files): | |
| 54 for f in files: | |
| 55 self._LoadOwnersFor(f) | |
| 56 | |
| 57 def _LoadOwnersFor(self, f): | |
|
M-A Ruel
2011/02/25 20:41:54
Is this function really needed? It's called only o
| |
| 58 if f not in self.owners_for: | |
| 59 owner_file = self._FindOwnersFileFor(f) | |
| 60 self.owners_file_for[f] = owner_file | |
| 61 self._ReadOwnersFile(owner_file, f) | |
| 62 | |
| 63 def _FindOwnersFileFor(self, f): | |
| 64 # This is really a "do ... until dirname = ''" | |
| 65 dirname = self.os_path.dirname(f) | |
| 66 while dirname: | |
| 67 owner_path = self.os_path.join(dirname, 'OWNERS') | |
| 68 if self.os_path.exists(owner_path): | |
| 69 return owner_path | |
| 70 dirname = self.os_path.dirname(dirname) | |
| 71 owner_path = self.os_path.join(dirname, 'OWNERS') | |
| 72 if self.os_path.exists(owner_path): | |
| 73 return owner_path | |
| 74 raise Assertion('No OWNERS file found for %s' % f) | |
| 75 | |
| 76 def _ReadOwnersFile(self, owner_file, affected_file): | |
| 77 owners_for = self.owners_for.setdefault(affected_file, set()) | |
| 78 for owner in self.fopen(owner_file): | |
| 79 owner = owner.strip() | |
| 80 self.files_owned_by.setdefault(owner, set()).add(affected_file) | |
| 81 owners_for.add(owner) | |
| 82 | |
| 83 def _CoveringSetOfOwnersFor(self, files): | |
| 84 # TODO(dpranke): implement the greedy algorithm for covering sets, and | |
| 85 # consider returning multiple options in case there are several equally | |
| 86 # short combinations of owners. | |
| 87 every_owner = set() | |
| 88 for f in files: | |
| 89 every_owner = every_owner.union(self.owners_for[f]) | |
| 90 return every_owner | |
| OLD | NEW |