Index: owners.py |
diff --git a/owners.py b/owners.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..921b21a7ba7996c07623294583c0cfbf0d7ef1bb |
--- /dev/null |
+++ b/owners.py |
@@ -0,0 +1,98 @@ |
+# Copyright (c) 2010 The Chromium Authors. All rights reserved. |
M-A Ruel
2011/02/24 16:25:57
2011
|
+# Use of this source code is governed by a BSD-style license that can be |
+# found in the LICENSE file. |
+ |
+"""A database of OWNERS files.""" |
+ |
+import os |
+import pdb |
M-A Ruel
2011/02/24 16:25:57
remove
|
+ |
+ |
+class Assertion(AssertionError): |
+ pass |
+ |
+class Database(object): |
+ def __init__(self, root, all_owners=None, |
M-A Ruel
2011/02/24 16:25:57
Either all align one per line or all packed at 80
|
+ open=file, |
+ os_path=os.path): |
+ """Initialize the database of owners for a repository. |
M-A Ruel
2011/02/24 16:25:57
Initializes
|
+ |
+ Args: |
+ root: the path to the root of the Repository |
+ all_owners: the list of every owner in the system |
+ open: function callback to open a text file for reading |
+ os_path: module/object callback with fields for 'exists', |
+ 'dirname', and 'join' |
+ """ |
+ self.root = root |
+ self.all_owners = all_owners or set() |
+ self.open = open |
+ self.os_path = os_path |
+ |
+ # Mapping of files to authorized owners. |
+ self.files_owned_by = {} |
+ |
+ # Mapping of owners to the files they own. |
+ self.owners_for = {} |
+ |
+ # In-memory cached map of files to their OWNERS files. |
+ self.owners_file_for = {} |
+ |
+ # In-memory cache of OWNERS files and their contents |
+ self.owners_files = {} |
+ |
+ def OwnersFor(self, files): |
+ """Returns a list of sets of reviewers that will cover the set of files. |
+ |
+ The set of files are paths relative to (and under) self.root.""" |
+ self._LoadDataNeededFor(files) |
+ return self._FindCoveringSets(files) |
+ |
+ def AreCovered(self, files, reviewers): |
+ return len(self.not_covered(files, reviewers)) == 0 |
M-A Ruel
2011/02/24 16:25:57
return not self.not_covered(files, reviewers)
|
+ |
+ def FilesNotCoveredBy(self, files, reviewers): |
+ covered_files = set() |
+ for reviewer in reviewers: |
+ covered_files = covered_files.union(self.files_owned_by(reviewer)) |
+ return files.difference(covered_files) |
+ |
+ def _LoadDataNeededFor(self, files): |
+ for f in files: |
+ self._LoadOwnersFor(f) |
+ |
+ def _LoadOwnersFor(self, f): |
+ if f not in self.owners_for: |
+ owner_file = self._FindOwnersFileFor(f) |
+ self.owners_file_for[f] = owner_file |
+ self._ReadOwnersFile(owner_file, f) |
+ |
+ def _FindOwnersFileFor(self, f): |
+ # This is really a "do ... until dirname = ''" |
+ dirname = self.os_path.dirname(f) |
+ while dirname: |
+ owner_path = self.os_path.join(dirname, 'OWNERS') |
+ if self.os_path.exists(owner_path): |
+ return owner_path |
+ dirname = self.os_path.dirname(dirname) |
+ owner_path = self.os_path.join(dirname, 'OWNERS') |
+ if self.os_path.exists(owner_path): |
+ return owner_path |
+ |
+ raise Assertion('No OWNERS file found for %s' % f) |
M-A Ruel
2011/02/24 16:25:57
Is this really an exceptional event? I think it sh
|
+ |
+ def _ReadOwnersFile(self, owner_file, affected_file): |
+ owners_for = self.owners_for.setdefault(affected_file, set()) |
+ for owner in self.open(owner_file).readlines(): |
M-A Ruel
2011/02/24 16:25:57
.readlines() is implied.
|
+ owner = owner.strip() |
+ self.files_owned_by.setdefault(owner, set()).add(affected_file) |
+ owners_for.add(owner) |
+ |
+ def _FindCoveringSets(self, files): |
+ # TODO(dpranke): implement the greedy algorithm for covering sets. |
+ # For now we return the list of every owner that touches at least |
+ # one file in the set. |
+ all_owners = set() |
+ for f in files: |
+ all_owners = all_owners.union(self.owners_for[f]) |
+ return [all_owners] |
M-A Ruel
2011/02/24 16:25:57
Did you mean:
return list(all_owners)
?
|