diff --git a/gerrit_util.py b/gerrit_util.py index fea3862b3..666875604 100644 --- a/gerrit_util.py +++ b/gerrit_util.py @@ -874,14 +874,23 @@ def IsCodeOwnersEnabledOnRepo(host, repo): return not config['status'].get('disabled', False) -def GetOwnersForFile(host, project, branch, path, limit=100, - resolve_all_users=True, seed=None, o_params=('DETAILS',)): +def GetOwnersForFile(host, + project, + branch, + path, + limit=100, + resolve_all_users=True, + highest_score_only=False, + seed=None, + o_params=('DETAILS',)): """Gets information about owners attached to a file.""" path = 'projects/%s/branches/%s/code_owners/%s' % ( urllib.parse.quote(project, ''), urllib.parse.quote(branch, ''), urllib.parse.quote(path, '')) q = ['resolve-all-users=%s' % json.dumps(resolve_all_users)] + if highest_score_only: + q.append('highest-score-only=%s' % json.dumps(highest_score_only)) if seed: q.append('seed=%d' % seed) if limit: diff --git a/owners_client.py b/owners_client.py index 27fa37a25..87801acf9 100644 --- a/owners_client.py +++ b/owners_client.py @@ -167,6 +167,7 @@ class GerritClient(OwnersClient): self._project = project self._branch = branch self._owners_cache = {} + self._best_owners_cache = {} # Seed used by Gerrit to shuffle code owners that have the same score. Can # be used to make the sort order stable across several requests, e.g. to get @@ -174,26 +175,46 @@ class GerritClient(OwnersClient): # same code owners. self._seed = random.getrandbits(30) - def ListOwners(self, path): + def _FetchOwners(self, path, cache, highest_score_only=False): # Always use slashes as separators. path = path.replace(os.sep, '/') - if path not in self._owners_cache: + if path not in cache: # GetOwnersForFile returns a list of account details sorted by order of # best reviewer for path. If owners have the same score, the order is # random, seeded by `self._seed`. - data = gerrit_util.GetOwnersForFile( - self._host, self._project, self._branch, path, - resolve_all_users=False, seed=self._seed) - self._owners_cache[path] = [ - d['account']['email'] - for d in data['code_owners'] - if 'account' in d and 'email' in d['account'] + data = gerrit_util.GetOwnersForFile(self._host, + self._project, + self._branch, + path, + resolve_all_users=False, + highest_score_only=highest_score_only, + seed=self._seed) + cache[path] = [ + d['account']['email'] for d in data['code_owners'] + if 'account' in d and 'email' in d['account'] ] # If owned_by_all_users is true, add everyone as an owner at the end of # the owners list. if data.get('owned_by_all_users', False): - self._owners_cache[path].append(self.EVERYONE) - return self._owners_cache[path] + cache[path].append(self.EVERYONE) + return cache[path] + + def ListOwners(self, path): + return self._FetchOwners(path, self._owners_cache) + + def ListBestOwners(self, path): + return self._FetchOwners(path, + self._best_owners_cache, + highest_score_only=True) + + def BatchListBestOwners(self, paths): + """List only the higest-scoring owners for a group of files. + + Returns a dictionary {path: [owners]}. + """ + with git_common.ScopedPool(kind='threads') as pool: + return dict( + pool.imap_unordered(lambda p: (p, self.ListBestOwners(p)), paths)) def GetCodeOwnersClient(root, upstream, host, project, branch): diff --git a/tests/owners_client_test.py b/tests/owners_client_test.py index 435be7d9f..7c026c3e1 100644 --- a/tests/owners_client_test.py +++ b/tests/owners_client_test.py @@ -102,7 +102,7 @@ class GerritClientTest(unittest.TestCase): # Always use slashes as separators. gerrit_util.GetOwnersForFile.assert_called_once_with( 'host', 'project', 'branch', 'bar/everyone/foo.txt', - resolve_all_users=False, seed=mock.ANY) + resolve_all_users=False, highest_score_only=False, seed=mock.ANY) def testListOwnersOwnedByAll(self): mock.patch(