 Chromium Code Reviews
 Chromium Code Reviews Issue 11114005:
  implement per-file OWNERS support  (Closed) 
  Base URL: https://git.chromium.org/chromium/tools/depot_tools.git@master
    
  
    Issue 11114005:
  implement per-file OWNERS support  (Closed) 
  Base URL: https://git.chromium.org/chromium/tools/depot_tools.git@master| Index: owners.py | 
| diff --git a/owners.py b/owners.py | 
| index 18c42b8f519a62df53e3dcfa14bb6d722921ee70..20e5bf5d8032284fcb074b79257e8108218e5aba 100644 | 
| --- a/owners.py | 
| +++ b/owners.py | 
| @@ -43,16 +43,18 @@ class Database(object): | 
| of changed files, and see if a list of changed files is covered by a | 
| list of reviewers.""" | 
| - def __init__(self, root, fopen, os_path): | 
| + def __init__(self, root, fopen, os_path, glob): | 
| """Args: | 
| root: the path to the root of the Repository | 
| open: function callback to open a text file for reading | 
| os_path: module/object callback with fields for 'abspath', 'dirname', | 
| 'exists', and 'join' | 
| + glob: function callback to list entries in a directory match a glob (i.e., glob.glob) | 
| 
M-A Ruel
2012/10/12 15:19:04
80 cols
 
Dirk Pranke
2012/10/16 22:42:41
Done.
 | 
| """ | 
| self.root = root | 
| self.fopen = fopen | 
| self.os_path = os_path | 
| + self.glob = glob | 
| # Pick a default email regexp to use; callers can override as desired. | 
| self.email_regexp = re.compile(BASIC_EMAIL_REGEXP) | 
| @@ -75,6 +77,7 @@ class Database(object): | 
| self._load_data_needed_for(files) | 
| return self._covering_set_of_owners_for(files) | 
| + # TODO(dpranke): rename to objects_not_covered_by | 
| def directories_not_covered_by(self, files, reviewers): | 
| """Returns the set of directories that are not owned by a reviewer. | 
| @@ -90,12 +93,20 @@ class Database(object): | 
| self._check_reviewers(reviewers) | 
| self._load_data_needed_for(files) | 
| - dirs = set([self.os_path.dirname(f) for f in files]) | 
| - covered_dirs = self._dirs_covered_by(reviewers) | 
| - uncovered_dirs = [self._enclosing_dir_with_owners(d) for d in dirs | 
| - if not self._is_dir_covered_by(d, covered_dirs)] | 
| + objs = set() | 
| + for f in files: | 
| + if f in self.owners_for: | 
| + objs.add(f) | 
| + else: | 
| + objs.add(self.os_path.dirname(f)) | 
| + | 
| + covered_objs = self._objs_covered_by(reviewers) | 
| + uncovered_objs = [self._enclosing_obj_with_owners(o) for o in objs | 
| + if not self._is_obj_covered_by(o, covered_objs)] | 
| - return set(uncovered_dirs) | 
| + return set(uncovered_objs) | 
| + | 
| + objects_not_covered_by = directories_not_covered_by | 
| 
M-A Ruel
2012/10/12 15:19:04
Why not rename right away? I don't see any advanta
 
Dirk Pranke
2012/10/16 22:42:41
I thought it would make the patch easier to review
 | 
| def _check_paths(self, files): | 
| def _is_under(f, pfx): | 
| @@ -107,20 +118,27 @@ class Database(object): | 
| _assert_is_collection(reviewers) | 
| assert all(self.email_regexp.match(r) for r in reviewers) | 
| + # TODO(dpranke): Rename to _objs_covered_by and update_callers | 
| def _dirs_covered_by(self, reviewers): | 
| dirs = self.owned_by[EVERYONE] | 
| for r in reviewers: | 
| dirs = dirs | self.owned_by.get(r, set()) | 
| return dirs | 
| + _objs_covered_by = _dirs_covered_by | 
| + | 
| def _stop_looking(self, dirname): | 
| return dirname in self.stop_looking | 
| + # TODO(dpranke): Rename to _is_dir_covered_by and update callers. | 
| def _is_dir_covered_by(self, dirname, covered_dirs): | 
| while not dirname in covered_dirs and not self._stop_looking(dirname): | 
| dirname = self.os_path.dirname(dirname) | 
| return dirname in covered_dirs | 
| + _is_obj_covered_by = _is_dir_covered_by | 
| + | 
| + # TODO(dpranke): Rename to _enclosing_obj_with_owners and update callers. | 
| def _enclosing_dir_with_owners(self, directory): | 
| """Returns the innermost enclosing directory that has an OWNERS file.""" | 
| dirpath = directory | 
| @@ -130,6 +148,8 @@ class Database(object): | 
| dirpath = self.os_path.dirname(dirpath) | 
| return dirpath | 
| + _enclosing_obj_with_owners = _enclosing_dir_with_owners | 
| + | 
| def _load_data_needed_for(self, files): | 
| for f in files: | 
| dirpath = self.os_path.dirname(f) | 
| @@ -153,16 +173,35 @@ class Database(object): | 
| if line == 'set noparent': | 
| self.stop_looking.add(dirpath) | 
| continue | 
| + | 
| + m = re.match("per-file (.+)=(.+)", line) | 
| + if m: | 
| + glob_string = m.group(1) | 
| + directive = m.group(2) | 
| + full_glob_string = self.os_path.join(self.root, dirpath, glob_string) | 
| 
M-A Ruel
2012/10/12 15:19:04
You should assert not self.os_path.isabs(glob_stri
 
Dirk Pranke
2012/10/16 22:42:41
Good point. Actually it's probably better to ensur
 | 
| + baselines = self.glob(full_glob_string) | 
| + for baseline in [self.os_path.relpath(self.root, b) for b in baselines]: | 
| 
M-A Ruel
2012/10/12 15:19:04
for baseline in (self.os_path.relpath(self.root, b
 
Dirk Pranke
2012/10/16 22:42:41
I tend to prefer the comprehension notation to the
 | 
| + self._add_entry(baseline, directive, "per-file line", | 
| + owners_path, lineno) | 
| + continue | 
| + | 
| if line.startswith('set '): | 
| raise SyntaxErrorInOwnersFile(owners_path, lineno, | 
| 'unknown option: "%s"' % line[4:].strip()) | 
| - if self.email_regexp.match(line) or line == EVERYONE: | 
| - self.owned_by.setdefault(line, set()).add(dirpath) | 
| - self.owners_for.setdefault(dirpath, set()).add(line) | 
| - continue | 
| + | 
| + self._add_entry(dirpath, line, "line", owners_path, lineno) | 
| + | 
| + def _add_entry(self, path, directive, line_type, owners_path, lineno): | 
| + if directive == "set noparent": | 
| + self.stop_looking.add(path) | 
| + elif self.email_regexp.match(directive) or directive == EVERYONE: | 
| + self.owned_by.setdefault(directive, set()).add(path) | 
| + self.owners_for.setdefault(path, set()).add(directive) | 
| + else: | 
| raise SyntaxErrorInOwnersFile(owners_path, lineno, | 
| - ('line is not a comment, a "set" directive, ' | 
| - 'or an email address: "%s"' % line)) | 
| + ('%s is not a "set" directive, ' | 
| 
M-A Ruel
2012/10/12 15:19:04
Technically, you'd also want to say it's not an EV
 
Dirk Pranke
2012/10/16 22:42:41
Done.
 | 
| + 'or an email address: "%s"' % (line_type, directive))) | 
| + | 
| def _covering_set_of_owners_for(self, files): | 
| # Get the set of directories from the files. |