Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # Copyright 2013 The Chromium Authors. All rights reserved. | 1 # Copyright 2013 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 import logging | 5 import logging |
| 6 import mimetypes | 6 import mimetypes |
| 7 import posixpath | 7 import posixpath |
| 8 import traceback | 8 import traceback |
| 9 | 9 |
| 10 from compiled_file_system import SingleFile | 10 from compiled_file_system import SingleFile |
| 11 from directory_zipper import DirectoryZipper | 11 from directory_zipper import DirectoryZipper |
| 12 from docs_server_utils import ToUnicode | 12 from docs_server_utils import ToUnicode |
| 13 from file_system import FileNotFoundError | 13 from file_system import FileNotFoundError |
| 14 from future import Future | 14 from future import All, Future |
| 15 from path_canonicalizer import PathCanonicalizer | 15 from path_canonicalizer import PathCanonicalizer |
| 16 from path_util import AssertIsValid, IsDirectory, Join, ToDirectory | 16 from path_util import AssertIsValid, IsDirectory, Join, ToDirectory |
| 17 from special_paths import SITE_VERIFICATION_FILE | 17 from special_paths import SITE_VERIFICATION_FILE |
| 18 from third_party.handlebar import Handlebar | 18 from third_party.handlebar import Handlebar |
| 19 from third_party.markdown import markdown | 19 from third_party.markdown import markdown |
| 20 | 20 |
| 21 | 21 |
| 22 _MIMETYPE_OVERRIDES = { | 22 _MIMETYPE_OVERRIDES = { |
| 23 # SVG is not supported by mimetypes.guess_type on AppEngine. | 23 # SVG is not supported by mimetypes.guess_type on AppEngine. |
| 24 '.svg': 'image/svg+xml', | 24 '.svg': 'image/svg+xml', |
| (...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 122 ''' | 122 ''' |
| 123 AssertIsValid(path) | 123 AssertIsValid(path) |
| 124 base, ext = posixpath.splitext(path) | 124 base, ext = posixpath.splitext(path) |
| 125 if self._directory_zipper and ext == '.zip': | 125 if self._directory_zipper and ext == '.zip': |
| 126 # The canonical location of zip files is the canonical location of the | 126 # The canonical location of zip files is the canonical location of the |
| 127 # directory to zip + '.zip'. | 127 # directory to zip + '.zip'. |
| 128 return self._path_canonicalizer.Canonicalize(base + '/').rstrip('/') + ext | 128 return self._path_canonicalizer.Canonicalize(base + '/').rstrip('/') + ext |
| 129 return self._path_canonicalizer.Canonicalize(path) | 129 return self._path_canonicalizer.Canonicalize(path) |
| 130 | 130 |
| 131 def GetContentAndType(self, path): | 131 def GetContentAndType(self, path): |
| 132 '''Returns the ContentAndType of the file at |path|. | 132 '''Returns a Future to the ContentAndType of the file at |path|. |
| 133 ''' | 133 ''' |
| 134 AssertIsValid(path) | 134 AssertIsValid(path) |
| 135 base, ext = posixpath.splitext(path) | 135 base, ext = posixpath.splitext(path) |
| 136 if self._directory_zipper and ext == '.zip': | |
| 137 return (self._directory_zipper.Zip(ToDirectory(base)) | |
| 138 .Then(lambda zipped: ContentAndType(zipped, | |
| 139 'application/zip', | |
| 140 None))) | |
| 141 return self._FindFileForPath(path).Then(self._content_cache.GetFromFile) | |
| 136 | 142 |
| 137 # Check for a zip file first, if zip is enabled. | 143 def GetVersion(self, path): |
| 144 '''Returns a Future to the version of the file at |path|. | |
| 145 ''' | |
| 146 AssertIsValid(path) | |
| 147 base, ext = posixpath.splitext(path) | |
| 138 if self._directory_zipper and ext == '.zip': | 148 if self._directory_zipper and ext == '.zip': |
| 139 zip_future = self._directory_zipper.Zip(ToDirectory(base)) | 149 stat_future = self.file_system.StatAsync(ToDirectory(base)) |
| 140 return Future(callback= | 150 else: |
| 141 lambda: ContentAndType(zip_future.Get(), 'application/zip', None)) | 151 stat_future = self._FindFileForPath(path).Then(self.file_system.StatAsync) |
| 152 return stat_future.Then(lambda stat: stat.version) | |
| 142 | 153 |
| 143 # If there is no file extension, look for a file with one of the default | 154 def _FindFileForPath(self, path): |
| 144 # extensions. If one cannot be found, check if the path is a directory. | 155 '''Finds the real file backing |path|. This may require looking for the |
| 145 # If it is, then check for an index file with one of the default | 156 correct file extension, or looking for an 'index' file if it's a directory. |
| 146 # extensions. | 157 Returns None if no path is found. |
| 147 if not ext: | 158 ''' |
| 148 new_path = self._AddExt(path) | 159 AssertIsValid(path) |
| 149 # Add a trailing / to check if it is a directory and not a file with | 160 _, ext = posixpath.splitext(path) |
| 150 # no extension. | |
| 151 if new_path is None and self.file_system.Exists(ToDirectory(path)).Get(): | |
| 152 new_path = self._AddExt(Join(path, 'index')) | |
| 153 # If an index file wasn't found in this directly then we're never going | |
| 154 # to find a file. | |
| 155 if new_path is None: | |
| 156 return FileNotFoundError.RaiseInFuture('"%s" is a directory' % path) | |
| 157 if new_path is not None: | |
| 158 path = new_path | |
| 159 | 161 |
| 160 return self._content_cache.GetFromFile(path) | 162 if ext: |
| 163 # There was already an extension, trust that it's a path. Elsewhere | |
| 164 # up the stack this will be caught if it's not. | |
| 165 return Future(value=path) | |
| 161 | 166 |
| 162 def _AddExt(self, path): | 167 def find_file_with_name(name): |
| 163 '''Tries to append each of the default file extensions to path and returns | 168 '''Tries to find a file in the file system called |name| with one of the |
| 164 the first one that is an existing file. | 169 default extensions of this content provider. |
| 165 ''' | 170 If none is found, returns None. |
| 166 for default_ext in self._default_extensions: | 171 ''' |
| 167 if self.file_system.Exists(path + default_ext).Get(): | 172 paths = [name + ext for ext in self._default_extensions] |
| 168 return path + default_ext | 173 def get_first_path_which_exists(existence): |
|
Yoyo Zhou
2014/08/13 18:30:50
Seems like this wants to be a Race. But maybe it s
not at google - send to devlin
2014/08/13 19:38:22
Yeah I wish it could be a Race, but Race returns t
Yoyo Zhou
2014/08/13 20:34:15
Well, you should be able to do this like
Race((se
| |
| 169 return None | 174 for exists, path in zip(existence, paths): |
| 175 if exists: | |
| 176 return path | |
| 177 return None | |
| 178 return (All(self.file_system.Exists(path) for path in paths) | |
| 179 .Then(get_first_path_which_exists)) | |
| 180 | |
| 181 def find_index_file(): | |
| 182 '''Tries to find an index file in |path|, if |path| is a directory. | |
| 183 If not, or if there is no index file, returns None. | |
| 184 ''' | |
| 185 def get_index_if_directory_exists(directory_exists): | |
| 186 if not directory_exists: | |
| 187 return None | |
| 188 return find_file_with_name(Join(path, 'index')) | |
| 189 return (self.file_system.Exists(ToDirectory(path)) | |
| 190 .Then(get_index_if_directory_exists)) | |
| 191 | |
| 192 # Try to find a file with the right name. If not, and it's a directory, | |
| 193 # look for an index file in that directory. If nothing at all is found, | |
| 194 # return the original |path| - its nonexistence will be caught up the stack. | |
| 195 return (find_file_with_name(path) | |
| 196 .Then(lambda found: found or find_index_file()) | |
| 197 .Then(lambda found: found or path)) | |
| 170 | 198 |
| 171 def Cron(self): | 199 def Cron(self): |
| 172 futures = [('<path_canonicalizer>', # semi-arbitrary string since there is | 200 futures = [self._path_canonicalizer.Cron()] |
| 173 # no path associated with this Future. | |
| 174 self._path_canonicalizer.Cron())] | |
| 175 for root, _, files in self.file_system.Walk(''): | 201 for root, _, files in self.file_system.Walk(''): |
| 176 for f in files: | 202 for f in files: |
| 177 futures.append((Join(root, f), | 203 futures.append(self.GetContentAndType(Join(root, f))) |
| 178 self.GetContentAndType(Join(root, f)))) | |
| 179 # Also cache the extension-less version of the file if needed. | 204 # Also cache the extension-less version of the file if needed. |
| 180 base, ext = posixpath.splitext(f) | 205 base, ext = posixpath.splitext(f) |
| 181 if f != SITE_VERIFICATION_FILE and ext in self._default_extensions: | 206 if f != SITE_VERIFICATION_FILE and ext in self._default_extensions: |
| 182 futures.append((Join(root, base), | 207 futures.append(self.GetContentAndType(Join(root, base))) |
| 183 self.GetContentAndType(Join(root, base)))) | |
| 184 # TODO(kalman): Cache .zip files for each directory (if supported). | 208 # TODO(kalman): Cache .zip files for each directory (if supported). |
| 185 def resolve(): | 209 return All(futures, except_pass=Exception, except_pass_log=True) |
| 186 for label, future in futures: | |
| 187 try: future.Get() | |
| 188 except: logging.error('%s: %s' % (label, traceback.format_exc())) | |
| 189 return Future(callback=resolve) | |
| 190 | 210 |
| 191 def __repr__(self): | 211 def __repr__(self): |
| 192 return 'ContentProvider of <%s>' % repr(self.file_system) | 212 return 'ContentProvider of <%s>' % repr(self.file_system) |
| OLD | NEW |