Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(34)

Side by Side Diff: appengine/findit/lib/gitiles/gitiles_repository.py

Issue 2538373003: [Culprit-Finder] Merge lib/ to libs/. (Closed)
Patch Set: . Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 # Copyright 2014 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 import base64
6 from datetime import datetime
7 from datetime import timedelta
8 import json
9 import re
10
11 from lib.gitiles import commit_util
12 from lib.gitiles import diff
13 from lib.gitiles.blame import Blame
14 from lib.gitiles.blame import Region
15 from lib.gitiles.change_log import ChangeLog
16 from lib.gitiles.change_log import FileChangeInfo
17 from lib.gitiles.git_repository import GitRepository
18 from lib.time_util import TimeZoneInfo
19
20 COMMIT_POSITION_PATTERN = re.compile(
21 '^Cr-Commit-Position: refs/heads/master@{#(\d+)}$', re.IGNORECASE)
22 CODE_REVIEW_URL_PATTERN = re.compile(
23 '^(?:Review URL|Review-Url): (.*\d+).*$', re.IGNORECASE)
24 REVERTED_REVISION_PATTERN = re.compile(
25 '^> Committed: https://.+/([0-9a-fA-F]{40})$', re.IGNORECASE)
26 TIMEZONE_PATTERN = re.compile('[-+]\d{4}$')
27 CACHE_EXPIRE_TIME_SECONDS = 24 * 60 * 60
28
29
30 class GitilesRepository(GitRepository):
31 """Use Gitiles to access a repository on https://chromium.googlesource.com."""
32
33 def __init__(self, http_client, repo_url=None):
34 super(GitilesRepository, self).__init__()
35 if repo_url and repo_url.endswith('/'):
36 self._repo_url = repo_url[:-1]
37 else:
38 self._repo_url = repo_url
39
40 self._http_client = http_client
41
42 @property
43 def repo_url(self):
44 return self._repo_url
45
46 @repo_url.setter
47 def repo_url(self, repo_url):
48 self._repo_url = repo_url
49
50 @property
51 def http_client(self):
52 return self._http_client
53
54 @property
55 def identifier(self):
56 return self.repo_url
57
58 def _SendRequestForJsonResponse(self, url, params=None):
59 if params is None: # pragma: no cover
60 params = {}
61 params['format'] = 'json'
62
63 # Gerrit prepends )]}' to json-formatted response.
64 prefix = ')]}\'\n'
65
66 status_code, content = self.http_client.Get(url, params)
67 if status_code != 200:
68 return None
69 elif not content or not content.startswith(prefix):
70 raise Exception('Response does not begin with %s' % prefix)
71
72 return json.loads(content[len(prefix):])
73
74 def _SendRequestForTextResponse(self, url):
75 status_code, content = self.http_client.Get(url, {'format': 'text'})
76 if status_code != 200:
77 return None
78 return base64.b64decode(content)
79
80 def _GetDateTimeFromString(self, datetime_string,
81 date_format='%a %b %d %H:%M:%S %Y'):
82 if TIMEZONE_PATTERN.findall(datetime_string):
83 # Need to handle timezone conversion.
84 naive_datetime_str, _, offset_str = datetime_string.rpartition(' ')
85 naive_datetime = datetime.strptime(naive_datetime_str, date_format)
86 return TimeZoneInfo(offset_str).LocalToUTC(naive_datetime)
87
88 return datetime.strptime(datetime_string, date_format)
89
90 def _DownloadChangeLogData(self, revision):
91 url = '%s/+/%s' % (self.repo_url, revision)
92 return url, self._SendRequestForJsonResponse(url)
93
94 def _ParseChangeLogFromLogData(self, data):
95 commit_position, code_review_url = (
96 commit_util.ExtractCommitPositionAndCodeReviewUrl(data['message']))
97
98 touched_files = []
99 for file_diff in data['tree_diff']:
100 change_type = file_diff['type'].lower()
101 if not diff.IsKnownChangeType(change_type):
102 raise Exception('Unknown change type "%s"' % change_type)
103 touched_files.append(
104 FileChangeInfo(
105 change_type, file_diff['old_path'], file_diff['new_path']))
106
107 author_time = self._GetDateTimeFromString(data['author']['time'])
108 committer_time = self._GetDateTimeFromString(data['committer']['time'])
109 reverted_revision = commit_util.GetRevertedRevision(data['message'])
110 url = '%s/+/%s' % (self.repo_url, data['commit'])
111
112 return ChangeLog(
113 data['author']['name'],
114 commit_util.NormalizeEmail(data['author']['email']),
115 author_time,
116 data['committer']['name'],
117 commit_util.NormalizeEmail(data['committer']['email']),
118 committer_time, data['commit'], commit_position,
119 data['message'], touched_files, url, code_review_url,
120 reverted_revision)
121
122 def GetChangeLog(self, revision):
123 """Returns the change log of the given revision."""
124 _, data = self._DownloadChangeLogData(revision)
125 if not data:
126 return None
127
128 return self._ParseChangeLogFromLogData(data)
129
130 def GetCommitsBetweenRevisions(self, start_revision, end_revision, n=1000):
131 """Gets a list of commit hashes between start_revision and end_revision.
132
133 Args:
134 start_revision: The oldest revision in the range.
135 end_revision: The latest revision in the range.
136 n: The maximum number of revisions to request at a time.
137
138 Returns:
139 A list of commit hashes made since start_revision through and including
140 end_revision in order from most-recent to least-recent. This includes
141 end_revision, but not start_revision.
142 """
143 params = {'n': n}
144 next_end_revision = end_revision
145 commits = []
146
147 while next_end_revision:
148 url = '%s/+log/%s..%s' % (
149 self.repo_url, start_revision, next_end_revision)
150 data = self._SendRequestForJsonResponse(url, params)
151
152 if not data:
153 break
154
155 for log in data.get('log', []):
156 commit = log.get('commit')
157 if commit:
158 commits.append(commit)
159
160 next_end_revision = data.get('next')
161
162 return commits
163
164 def GetChangeDiff(self, revision):
165 """Returns the raw diff of the given revision."""
166 url = '%s/+/%s%%5E%%21/' % (self.repo_url, revision)
167 return self._SendRequestForTextResponse(url)
168
169 def GetBlame(self, path, revision):
170 """Returns blame of the file at ``path`` of the given revision."""
171 url = '%s/+blame/%s/%s' % (self.repo_url, revision, path)
172
173 data = self._SendRequestForJsonResponse(url)
174 if not data:
175 return None
176
177 blame = Blame(revision, path)
178 for region in data['regions']:
179 author_time = self._GetDateTimeFromString(
180 region['author']['time'], '%Y-%m-%d %H:%M:%S')
181
182 blame.AddRegion(
183 Region(region['start'], region['count'], region['commit'],
184 region['author']['name'],
185 commit_util.NormalizeEmail(region['author']['email']),
186 author_time))
187
188 return blame
189
190 def GetSource(self, path, revision):
191 """Returns source code of the file at ``path`` of the given revision."""
192 url = '%s/+/%s/%s' % (self.repo_url, revision, path)
193 return self._SendRequestForTextResponse(url)
194
195 def GetChangeLogs(self, start_revision, end_revision, n=1000):
196 """Gets a list of ChangeLogs in revision range by batch.
197
198 Args:
199 start_revision (str): The oldest revision in the range.
200 end_revision (str): The latest revision in the range.
201 n (int): The maximum number of revisions to request at a time (default
202 to 1000).
203
204 Returns:
205 A list of changelogs in (start_revision, end_revision].
206 """
207 next_end_revision = end_revision
208 changelogs = []
209
210 while next_end_revision:
211 url = '%s/+log/%s..%s' % (self.repo_url,
212 start_revision, next_end_revision)
213 data = self._SendRequestForJsonResponse(url, params={'n': str(n),
214 'name-status': '1'})
215
216 for log in data['log']:
217 changelogs.append(self._ParseChangeLogFromLogData(log))
218
219 if 'next' in data:
220 next_end_revision = data['next']
221 else:
222 next_end_revision = None
223
224 return changelogs
OLDNEW
« no previous file with comments | « appengine/findit/lib/gitiles/git_repository.py ('k') | appengine/findit/lib/gitiles/test/__init__.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698