Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 from base64 import b64decode | 5 from base64 import b64decode |
| 6 from itertools import izip | 6 from itertools import izip |
| 7 import json | 7 import json |
| 8 import posixpath | 8 import posixpath |
| 9 import traceback | 9 import traceback |
| 10 | 10 |
| 11 from appengine_url_fetcher import AppEngineUrlFetcher | 11 from appengine_url_fetcher import AppEngineUrlFetcher |
| 12 from appengine_wrappers import IsDownloadError | 12 from appengine_wrappers import IsDownloadError |
| 13 from docs_server_utils import StringIdentity | 13 from docs_server_utils import StringIdentity |
| 14 from file_system import (FileNotFoundError, | 14 from file_system import (FileNotFoundError, |
| 15 FileSystem, | 15 FileSystem, |
| 16 FileSystemError, | 16 FileSystemError, |
| 17 StatInfo) | 17 StatInfo) |
| 18 from future import All, Future | 18 from future import All, Future |
| 19 from path_util import AssertIsValid, IsDirectory, SplitParent, ToDirectory | 19 from path_util import AssertIsValid, IsDirectory, ToDirectory |
| 20 from third_party.json_schema_compiler.memoize import memoize | 20 from third_party.json_schema_compiler.memoize import memoize |
| 21 from url_constants import GITILES_BASE, GITILES_BRANCH_BASE | 21 from url_constants import GITILES_BASE, GITILES_BRANCH_BASE |
| 22 | 22 |
| 23 | 23 |
| 24 _JSON_FORMAT = '?format=JSON' | 24 _JSON_FORMAT = '?format=JSON' |
| 25 _TEXT_FORMAT = '?format=TEXT' | 25 _TEXT_FORMAT = '?format=TEXT' |
| 26 | 26 |
| 27 | 27 |
| 28 def _ParseGitilesJson(json_data): | 28 def _ParseGitilesJson(json_data): |
| 29 '''json.loads with fix-up for non-executable JSON. Use this to parse any JSON | 29 '''json.loads with fix-up for non-executable JSON. Use this to parse any JSON |
| (...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 97 # { | 97 # { |
| 98 # "id": "12a5464de48d2c46bc0b2dc78fafed75aab554fa", # The tree ID. | 98 # "id": "12a5464de48d2c46bc0b2dc78fafed75aab554fa", # The tree ID. |
| 99 # "entries": [ | 99 # "entries": [ |
| 100 # { | 100 # { |
| 101 # "mode": 33188, | 101 # "mode": 33188, |
| 102 # "type": "blob", | 102 # "type": "blob", |
| 103 # "id": "ab971ca447bc4bce415ed4498369e00164d91cb6", # File ID. | 103 # "id": "ab971ca447bc4bce415ed4498369e00164d91cb6", # File ID. |
| 104 # "name": ".gitignore" | 104 # "name": ".gitignore" |
| 105 # }, | 105 # }, |
| 106 # ... | 106 # ... |
| 107 # ] | |
| 107 # } | 108 # } |
| 108 def list_dir(json_data): | 109 def list_dir(json_data): |
| 109 return [e['name'] for e in _ParseGitilesJson(json_data)['entries']] | 110 entries = _ParseGitilesJson(json_data)['entries'] |
|
not at google - send to devlin
2014/08/21 20:58:29
Maybe instead of ['entries'] this should be .get('
| |
| 111 return [e['name'] + ('/' if e['type'] == 'tree' else '') for e in entries] | |
| 110 | 112 |
| 111 def fixup_url_format(path): | 113 def fixup_url_format(path): |
| 112 # By default, Gitiles URLs display resources in HTML. To get resources | 114 # By default, Gitiles URLs display resources in HTML. To get resources |
| 113 # suitable for our consumption, a '?format=' string must be appended to | 115 # suitable for our consumption, a '?format=' string must be appended to |
| 114 # the URL. The format may be one of 'JSON' or 'TEXT' for directory or | 116 # the URL. The format may be one of 'JSON' or 'TEXT' for directory or |
| 115 # text resources, respectively. | 117 # text resources, respectively. |
| 116 return path + (_JSON_FORMAT if IsDirectory(path) else _TEXT_FORMAT) | 118 return path + (_JSON_FORMAT if IsDirectory(path) else _TEXT_FORMAT) |
| 117 | 119 |
| 118 # A list of tuples of the form (path, Future). | 120 # A list of tuples of the form (path, Future). |
| 119 fetches = ((path, self._FetchAsync(fixup_url_format(path))) | 121 fetches = ((path, self._FetchAsync(fixup_url_format(path))) |
| 120 for path in paths) | 122 for path in paths) |
| 121 | 123 |
| 122 def parse_contents(results): | 124 def parse_contents(results): |
| 123 value = {} | 125 value = {} |
| 124 for path, content in izip(paths, results): | 126 for path, content in izip(paths, results): |
| 125 if content is None: | 127 if content is None: |
| 126 continue | 128 continue |
| 127 # Gitiles encodes text content in base64 (see | 129 # Gitiles encodes text content in base64 (see |
| 128 # http://tools.ietf.org/html/rfc4648 for info about base64). | 130 # http://tools.ietf.org/html/rfc4648 for info about base64). |
| 129 value[path] = (list_dir if IsDirectory(path) else b64decode)(content) | 131 value[path] = (list_dir if IsDirectory(path) else b64decode)(content) |
| 130 return value | 132 return value |
| 131 return All(self._ResolveFetchContent(path, future, skip_not_found) | 133 return All(self._ResolveFetchContent(path, future, skip_not_found) |
| 132 for path, future in fetches).Then(parse_contents) | 134 for path, future in fetches).Then(parse_contents) |
| 133 | 135 |
| 134 def Refresh(self): | 136 def Refresh(self): |
| 135 return Future(value=()) | 137 return Future(value=()) |
| 136 | 138 |
| 137 @memoize | 139 @memoize |
| 138 def GetCommitID(self): | 140 def _GetCommitInfo(self, key): |
| 139 '''Returns a future that resolves to the commit ID for this branch. | 141 '''Gets the commit information specified by |key|. |
| 142 | |
| 143 The JSON view for commit info looks like: | |
| 144 { | |
| 145 "commit": "8fd578e1a7b142cd10a4387861f05fb9459b69e2", # Commit ID. | |
| 146 "tree": "3ade65d8a91eadd009a6c9feea8f87db2c528a53", # Tree ID. | |
| 147 "parents": [ | |
| 148 "a477c787fe847ae0482329f69b39ce0fde047359" # Previous commit ID. | |
| 149 ], | |
| 150 "author": { | |
| 151 "name": "...", | |
| 152 "email": "...", | |
| 153 "time": "Tue Aug 12 17:17:21 2014" | |
| 154 }, | |
| 155 "committer": { | |
| 156 "name": "...", | |
| 157 "email": "...", | |
| 158 "time": "Tue Aug 12 17:18:28 2014" | |
| 159 }, | |
| 160 "message": "...", | |
| 161 "tree_diff": [...] | |
| 162 } | |
| 140 ''' | 163 ''' |
| 141 # Commit information for a branch is obtained by appending '?format=JSON' | 164 # Commit information for a branch is obtained by appending '?format=JSON' |
| 142 # to the branch URL. Note that '<gitiles_url>/<branch>?format=JSON' is | 165 # to the branch URL. Note that '<gitiles_url>/<branch>?format=JSON' is |
| 143 # different from '<gitiles_url>/<branch>/?format=JSON': the latter serves | 166 # different from '<gitiles_url>/<branch>/?format=JSON': the latter serves |
| 144 # the root directory JSON content, whereas the former serves the branch | 167 # the root directory JSON content, whereas the former serves the branch |
| 145 # commit info JSON content. | 168 # commit info JSON content. |
| 146 fetch_future = self._fetcher.FetchAsync(self._base_url + _JSON_FORMAT) | 169 fetch_future = self._fetcher.FetchAsync(self._base_url + _JSON_FORMAT) |
| 147 content_future = self._ResolveFetchContent(self._base_url, fetch_future) | 170 content_future = self._ResolveFetchContent(self._base_url, fetch_future) |
| 148 # The commit info JSON looks like: | 171 return content_future.Then(lambda json: _ParseGitilesJson(json)[key]) |
| 149 # | 172 |
| 150 # { | 173 def GetCommitID(self): |
| 151 # "commit": "8fd578e1a7b142cd10a4387861f05fb9459b69e2", # Commit ID. | 174 '''Returns a future that resolves to the commit ID for this branch. |
| 152 # "tree": "3ade65d8a91eadd009a6c9feea8f87db2c528a53", # Tree ID. | 175 ''' |
| 153 # "author": {...}, | 176 return self._GetCommitInfo('commit') |
| 154 # "committer": {...}, | |
| 155 # "message": <codereview message>, | |
| 156 # ... | |
| 157 # } | |
| 158 return content_future.Then(lambda json: _ParseGitilesJson(json)['commit']) | |
| 159 | 177 |
| 160 def Stat(self, path): | 178 def Stat(self, path): |
| 161 return self.StatAsync(path).Get() | 179 return self.StatAsync(path).Get() |
| 162 | 180 |
| 163 def StatAsync(self, path): | 181 def StatAsync(self, path): |
| 164 dir_, filename = posixpath.split(path) | 182 dir_, filename = posixpath.split(path) |
| 165 def stat(content): | 183 def stat(content): |
| 166 stat_info = _CreateStatInfo(content) | 184 stat_info = _CreateStatInfo(content) |
| 167 if stat_info.version is None: | 185 if stat_info.version is None: |
| 168 raise FileSystemError('Failed to find version of dir %s' % dir_) | 186 raise FileSystemError('Failed to find version of dir %s' % dir_) |
| 169 if IsDirectory(path): | 187 if IsDirectory(path): |
| 170 return stat_info | 188 return stat_info |
| 171 if filename not in stat_info.child_versions: | 189 if filename not in stat_info.child_versions: |
| 172 raise FileNotFoundError( | 190 raise FileNotFoundError( |
| 173 '%s from %s was not in child versions for Stat' % (filename, path)) | 191 '%s from %s was not in child versions for Stat' % (filename, path)) |
| 174 return StatInfo(stat_info.child_versions[filename]) | 192 return StatInfo(stat_info.child_versions[filename]) |
| 175 fetch_future = self._FetchAsync(ToDirectory(dir_) + _JSON_FORMAT) | 193 fetch_future = self._FetchAsync(ToDirectory(dir_) + _JSON_FORMAT) |
| 176 return self._ResolveFetchContent(path, fetch_future).Then(stat) | 194 return self._ResolveFetchContent(path, fetch_future).Then(stat) |
| 177 | 195 |
| 178 def GetIdentity(self): | 196 def GetIdentity(self): |
| 179 return '@'.join((self.__class__.__name__, | 197 # NOTE: Do not use commit information to create the string identity. |
| 180 StringIdentity(self._commit or self._branch))) | 198 # Doing so will mess up caching. |
| 199 if self._commit is None and self._branch != 'master': | |
| 200 str_id = GITILES_BRANCH_BASE | |
| 201 else: | |
| 202 str_id = GITILES_BASE | |
| 203 return '@'.join((self.__class__.__name__, StringIdentity(str_id))) | |
| OLD | NEW |