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

Side by Side Diff: chrome/common/extensions/docs/server2/gitiles_file_system.py

Issue 491653002: Docserver: Use GitilesFileSystem instead of SubversionFileSystem (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 3 months 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
1 # Copyright 2014 The Chromium Authors. All rights reserved. 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 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5
5 from base64 import b64decode 6 from base64 import b64decode
6 from itertools import izip 7 from itertools import izip
8 import logging
7 import json 9 import json
8 import posixpath 10 import posixpath
11 import time
9 import traceback 12 import traceback
10 13
11 from appengine_url_fetcher import AppEngineUrlFetcher 14 from appengine_url_fetcher import AppEngineUrlFetcher
12 from appengine_wrappers import IsDownloadError 15 from appengine_wrappers import IsDownloadError, app_identity
13 from docs_server_utils import StringIdentity 16 from docs_server_utils import StringIdentity
14 from file_system import (FileNotFoundError, 17 from file_system import (FileNotFoundError,
15 FileSystem, 18 FileSystem,
16 FileSystemError, 19 FileSystemError,
17 StatInfo) 20 StatInfo)
18 from future import All, Future 21 from future import All, Future
19 from path_util import AssertIsValid, IsDirectory, ToDirectory 22 from path_util import AssertIsValid, IsDirectory, ToDirectory
20 from third_party.json_schema_compiler.memoize import memoize 23 from third_party.json_schema_compiler.memoize import memoize
21 from url_constants import GITILES_BASE, GITILES_BRANCH_BASE 24 from url_constants import (GITILES_BASE,
22 25 GITILES_BRANCH_BASE,
26 GITILES_OAUTH2_SCOPE)
23 27
24 _JSON_FORMAT = '?format=JSON' 28 _JSON_FORMAT = '?format=JSON'
25 _TEXT_FORMAT = '?format=TEXT' 29 _TEXT_FORMAT = '?format=TEXT'
26 30
27 31
28 def _ParseGitilesJson(json_data): 32 def _ParseGitilesJson(json_data):
29 '''json.loads with fix-up for non-executable JSON. Use this to parse any JSON 33 '''json.loads with fix-up for non-executable JSON. Use this to parse any JSON
30 data coming from Gitiles views. 34 data coming from Gitiles views.
31 ''' 35 '''
32 return json.loads(json_data[json_data.find('{'):]) 36 return json.loads(json_data[json_data.find('{'):])
(...skipping 26 matching lines...) Expand all
59 self._fetcher = fetcher 63 self._fetcher = fetcher
60 self._base_url = base_url 64 self._base_url = base_url
61 self._branch = branch 65 self._branch = branch
62 self._commit = commit 66 self._commit = commit
63 67
64 def _FetchAsync(self, url): 68 def _FetchAsync(self, url):
65 '''Convenience wrapper for fetcher.FetchAsync, so callers don't 69 '''Convenience wrapper for fetcher.FetchAsync, so callers don't
66 need to use posixpath.join. 70 need to use posixpath.join.
67 ''' 71 '''
68 AssertIsValid(url) 72 AssertIsValid(url)
69 return self._fetcher.FetchAsync('%s/%s' % (self._base_url, url)) 73 access_token, _ = app_identity.get_access_token(GITILES_OAUTH2_SCOPE)
74 return self._fetcher.FetchAsync('%s/%s' % (self._base_url, url),
75 access_token=access_token)
70 76
71 def _ResolveFetchContent(self, path, fetch_future, skip_not_found=False): 77 def _ResolveFetchContent(self, path, fetch_future, retry,
78 skip_not_found=False):
72 '''Returns a future to cleanly resolve |fetch_future|. 79 '''Returns a future to cleanly resolve |fetch_future|.
73 ''' 80 '''
74 def handle(e): 81 def handle(e):
75 if skip_not_found and IsDownloadError(e): 82 if skip_not_found and IsDownloadError(e):
76 return None 83 return None
77 exc_type = FileNotFoundError if IsDownloadError(e) else FileSystemError 84 exc_type = FileNotFoundError if IsDownloadError(e) else FileSystemError
78 raise exc_type('%s fetching %s for Get from %s: %s' % 85 raise exc_type('%s fetching %s for Get from %s: %s' %
79 (type(e).__name__, path, self._base_url, traceback.format_exc())) 86 (type(e).__name__, path, self._base_url, traceback.format_exc()))
80 87
81 def get_content(result): 88 def get_content(result):
82 if result.status_code == 404: 89 if result.status_code == 404:
83 if skip_not_found: 90 if skip_not_found:
84 return None 91 return None
85 raise FileNotFoundError('Got 404 when fetching %s for Get from %s' % 92 raise FileNotFoundError('Got 404 when fetching %s for Get from %s' %
86 (path, self._base_url)) 93 (path, self._base_url))
94 if result.status_code == 429:
95 logging.warning('Access throttled when fetching %s for Get from %s' %
96 (path, self._base_url))
97 time.sleep(30)
98 return retry().Then(get_content, handle)
87 if result.status_code != 200: 99 if result.status_code != 200:
88 raise FileSystemError( 100 raise FileSystemError(
89 'Got %s when fetching %s for Get from %s, content %s' % 101 'Got %s when fetching %s for Get from %s, content %s' %
90 (result.status_code, path, self._base_url, result.content)) 102 (result.status_code, path, self._base_url, result.content))
91 return result.content 103 return result.content
104
92 return fetch_future.Then(get_content, handle) 105 return fetch_future.Then(get_content, handle)
93 106
94 def Read(self, paths, skip_not_found=False): 107 def Read(self, paths, skip_not_found=False):
95 # Directory content is formatted in JSON in Gitiles as follows: 108 # Directory content is formatted in JSON in Gitiles as follows:
96 # 109 #
97 # { 110 # {
98 # "id": "12a5464de48d2c46bc0b2dc78fafed75aab554fa", # The tree ID. 111 # "id": "12a5464de48d2c46bc0b2dc78fafed75aab554fa", # The tree ID.
99 # "entries": [ 112 # "entries": [
100 # { 113 # {
101 # "mode": 33188, 114 # "mode": 33188,
102 # "type": "blob", 115 # "type": "blob",
103 # "id": "ab971ca447bc4bce415ed4498369e00164d91cb6", # File ID. 116 # "id": "ab971ca447bc4bce415ed4498369e00164d91cb6", # File ID.
104 # "name": ".gitignore" 117 # "name": ".gitignore"
105 # }, 118 # },
106 # ... 119 # ...
107 # ] 120 # ]
108 # } 121 # }
109 def list_dir(json_data): 122 def list_dir(json_data):
110 entries = _ParseGitilesJson(json_data).get('entries', []) 123 entries = _ParseGitilesJson(json_data).get('entries', [])
111 return [e['name'] + ('/' if e['type'] == 'tree' else '') for e in entries] 124 return [e['name'] + ('/' if e['type'] == 'tree' else '') for e in entries]
112 125
113 def fixup_url_format(path): 126 def fixup_url_format(path):
114 # By default, Gitiles URLs display resources in HTML. To get resources 127 # By default, Gitiles URLs display resources in HTML. To get resources
115 # suitable for our consumption, a '?format=' string must be appended to 128 # suitable for our consumption, a '?format=' string must be appended to
116 # the URL. The format may be one of 'JSON' or 'TEXT' for directory or 129 # the URL. The format may be one of 'JSON' or 'TEXT' for directory or
117 # text resources, respectively. 130 # text resources, respectively.
118 return path + (_JSON_FORMAT if IsDirectory(path) else _TEXT_FORMAT) 131 return path + (_JSON_FORMAT if IsDirectory(path) else _TEXT_FORMAT)
119 132
120 # A list of tuples of the form (path, Future). 133 # A list of tuples of the form (path, Future).
121 fetches = ((path, self._FetchAsync(fixup_url_format(path))) 134 fetches = []
122 for path in paths) 135 for path in paths:
136 def make_fetch_future():
137 return self._FetchAsync(fixup_url_format(path))
138 fetches.append((path, make_fetch_future(), make_fetch_future))
123 139
124 def parse_contents(results): 140 def parse_contents(results):
125 value = {} 141 value = {}
126 for path, content in izip(paths, results): 142 for path, content in izip(paths, results):
127 if content is None: 143 if content is None:
128 continue 144 continue
129 # Gitiles encodes text content in base64 (see 145 # Gitiles encodes text content in base64 (see
130 # http://tools.ietf.org/html/rfc4648 for info about base64). 146 # http://tools.ietf.org/html/rfc4648 for info about base64).
131 value[path] = (list_dir if IsDirectory(path) else b64decode)(content) 147 value[path] = (list_dir if IsDirectory(path) else b64decode)(content)
132 return value 148 return value
133 return All(self._ResolveFetchContent(path, future, skip_not_found) 149
134 for path, future in fetches).Then(parse_contents) 150 return All(self._ResolveFetchContent(path, future, factory, skip_not_found)
151 for path, future, factory in fetches).Then(parse_contents)
135 152
136 def Refresh(self): 153 def Refresh(self):
137 return Future(value=()) 154 return Future(value=())
138 155
139 @memoize 156 @memoize
140 def _GetCommitInfo(self, key): 157 def _GetCommitInfo(self, key):
141 '''Gets the commit information specified by |key|. 158 '''Gets the commit information specified by |key|.
142 159
143 The JSON view for commit info looks like: 160 The JSON view for commit info looks like:
144 { 161 {
(...skipping 14 matching lines...) Expand all
159 }, 176 },
160 "message": "...", 177 "message": "...",
161 "tree_diff": [...] 178 "tree_diff": [...]
162 } 179 }
163 ''' 180 '''
164 # Commit information for a branch is obtained by appending '?format=JSON' 181 # Commit information for a branch is obtained by appending '?format=JSON'
165 # to the branch URL. Note that '<gitiles_url>/<branch>?format=JSON' is 182 # to the branch URL. Note that '<gitiles_url>/<branch>?format=JSON' is
166 # different from '<gitiles_url>/<branch>/?format=JSON': the latter serves 183 # different from '<gitiles_url>/<branch>/?format=JSON': the latter serves
167 # the root directory JSON content, whereas the former serves the branch 184 # the root directory JSON content, whereas the former serves the branch
168 # commit info JSON content. 185 # commit info JSON content.
169 fetch_future = self._fetcher.FetchAsync(self._base_url + _JSON_FORMAT) 186
170 content_future = self._ResolveFetchContent(self._base_url, fetch_future) 187 def make_fetch_future():
188 access_token, _ = app_identity.get_access_token(GITILES_OAUTH2_SCOPE)
189 return self._fetcher.FetchAsync(self._base_url + _JSON_FORMAT,
190 access_token = access_token)
191
192 fetch_future = make_fetch_future()
193 content_future = self._ResolveFetchContent(self._base_url, fetch_future,
194 make_fetch_future)
171 return content_future.Then(lambda json: _ParseGitilesJson(json)[key]) 195 return content_future.Then(lambda json: _ParseGitilesJson(json)[key])
172 196
173 def GetCommitID(self): 197 def GetCommitID(self):
174 '''Returns a future that resolves to the commit ID for this branch. 198 '''Returns a future that resolves to the commit ID for this branch.
175 ''' 199 '''
176 return self._GetCommitInfo('commit') 200 return self._GetCommitInfo('commit')
177 201
202 def GetPreviousCommitID(self):
203 '''Returns a future that resolves to the previous commit ID for this branch.
204 '''
205 return self._GetCommitInfo('parents').Then(lambda parents: parents[0])
206
178 def StatAsync(self, path): 207 def StatAsync(self, path):
179 dir_, filename = posixpath.split(path) 208 dir_, filename = posixpath.split(path)
180 def stat(content): 209 def stat(content):
181 stat_info = _CreateStatInfo(content) 210 stat_info = _CreateStatInfo(content)
182 if stat_info.version is None: 211 if stat_info.version is None:
183 raise FileSystemError('Failed to find version of dir %s' % dir_) 212 raise FileSystemError('Failed to find version of dir %s' % dir_)
184 if IsDirectory(path): 213 if IsDirectory(path):
185 return stat_info 214 return stat_info
186 if filename not in stat_info.child_versions: 215 if filename not in stat_info.child_versions:
187 raise FileNotFoundError( 216 raise FileNotFoundError(
188 '%s from %s was not in child versions for Stat' % (filename, path)) 217 '%s from %s was not in child versions for Stat' % (filename, path))
189 return StatInfo(stat_info.child_versions[filename]) 218 return StatInfo(stat_info.child_versions[filename])
190 fetch_future = self._FetchAsync(ToDirectory(dir_) + _JSON_FORMAT) 219
191 return self._ResolveFetchContent(path, fetch_future).Then(stat) 220 def make_fetch_future():
221 return self._FetchAsync(ToDirectory(dir_) + _JSON_FORMAT)
222
223 fetch_future = make_fetch_future()
224 return self._ResolveFetchContent(path, fetch_future,
225 make_fetch_future).Then(stat)
192 226
193 def GetIdentity(self): 227 def GetIdentity(self):
194 # NOTE: Do not use commit information to create the string identity. 228 # NOTE: Do not use commit information to create the string identity.
195 # Doing so will mess up caching. 229 # Doing so will mess up caching.
196 if self._commit is None and self._branch != 'master': 230 if self._commit is None and self._branch != 'master':
197 str_id = GITILES_BRANCH_BASE 231 str_id = GITILES_BRANCH_BASE
198 else: 232 else:
199 str_id = GITILES_BASE 233 str_id = GITILES_BASE
200 return '@'.join((self.__class__.__name__, StringIdentity(str_id))) 234 return '@'.join((self.__class__.__name__, StringIdentity(str_id)))
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698