Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2012 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 sys | 5 import sys |
| 6 | 6 |
| 7 import schema_util | 7 import schema_util |
| 8 from docs_server_utils import ToUnicode | 8 from docs_server_utils import ToUnicode |
| 9 from file_system import FileNotFoundError | 9 from file_system import FileNotFoundError |
| 10 from future import Future | 10 from future import Future |
| 11 from path_util import AssertIsDirectory, AssertIsFile, ToDirectory | 11 from path_util import AssertIsDirectory, AssertIsFile, ToDirectory |
| 12 from third_party.json_schema_compiler import json_parse | 12 from third_party.json_schema_compiler import json_parse |
| 13 from third_party.json_schema_compiler.memoize import memoize | 13 from third_party.json_schema_compiler.memoize import memoize |
| 14 from third_party.motemplate import Motemplate | 14 from third_party.motemplate import Motemplate |
| 15 | 15 |
| 16 | 16 |
| 17 _CACHEABLE_FUNCTIONS = set() | |
| 17 _SINGLE_FILE_FUNCTIONS = set() | 18 _SINGLE_FILE_FUNCTIONS = set() |
| 18 | 19 |
| 19 | 20 |
| 21 def _GetUnboundFunction(fn): | |
| 22 '''Functions bound to an object are separate from the unbound | |
|
ahernandez
2014/08/29 01:49:42
I don't know how to explain this concisely, but th
not at google - send to devlin
2014/08/29 05:00:14
Neat, nice catch. This nested decorator stuff in P
| |
| 23 defintion. This causes issues when checking for membership, | |
| 24 so always get the unbound function, if possible. | |
| 25 ''' | |
| 26 return getattr(fn, 'im_func', fn) | |
| 27 | |
| 28 | |
| 29 def Cache(fn): | |
| 30 '''A decorator which can be applied to the compilation function | |
| 31 passed to CompiledFileSystem.Create, indicating that file/list data | |
| 32 should be cached. | |
| 33 | |
| 34 This decorator should be listed first in any list of decorators, along | |
| 35 with the SingleFile decorator below. | |
| 36 ''' | |
| 37 _CACHEABLE_FUNCTIONS.add(_GetUnboundFunction(fn)) | |
| 38 return fn | |
| 39 | |
| 40 | |
| 20 def SingleFile(fn): | 41 def SingleFile(fn): |
| 21 '''A decorator which can be optionally applied to the compilation function | 42 '''A decorator which can be optionally applied to the compilation function |
| 22 passed to CompiledFileSystem.Create, indicating that the function only | 43 passed to CompiledFileSystem.Create, indicating that the function only |
| 23 needs access to the file which is given in the function's callback. When | 44 needs access to the file which is given in the function's callback. When |
| 24 this is the case some optimisations can be done. | 45 this is the case some optimisations can be done. |
| 25 | 46 |
| 26 Note that this decorator must be listed first in any list of decorators to | 47 Note that this decorator must be listed first in any list of decorators to |
| 27 have any effect. | 48 have any effect. |
| 28 ''' | 49 ''' |
| 29 _SINGLE_FILE_FUNCTIONS.add(fn) | 50 _SINGLE_FILE_FUNCTIONS.add(_GetUnboundFunction(fn)) |
| 30 return fn | 51 return fn |
| 31 | 52 |
| 32 | 53 |
| 33 def Unicode(fn): | 54 def Unicode(fn): |
| 34 '''A decorator which can be optionally applied to the compilation function | 55 '''A decorator which can be optionally applied to the compilation function |
| 35 passed to CompiledFileSystem.Create, indicating that the function processes | 56 passed to CompiledFileSystem.Create, indicating that the function processes |
| 36 the file's data as Unicode text. | 57 the file's data as Unicode text. |
| 37 ''' | 58 ''' |
| 38 | 59 |
| 39 # The arguments passed to fn can be (self, path, data) or (path, data). In | 60 # The arguments passed to fn can be (self, path, data) or (path, data). In |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 93 compilation_function, | 114 compilation_function, |
| 94 create_object_store('file'), | 115 create_object_store('file'), |
| 95 create_object_store('list')) | 116 create_object_store('list')) |
| 96 | 117 |
| 97 @memoize | 118 @memoize |
| 98 def ForJson(self, file_system): | 119 def ForJson(self, file_system): |
| 99 '''A CompiledFileSystem specifically for parsing JSON configuration data. | 120 '''A CompiledFileSystem specifically for parsing JSON configuration data. |
| 100 These are memoized over file systems tied to different branches. | 121 These are memoized over file systems tied to different branches. |
| 101 ''' | 122 ''' |
| 102 return self.Create(file_system, | 123 return self.Create(file_system, |
| 103 SingleFile(lambda _, data: | 124 Cache(SingleFile(lambda _, data: |
|
not at google - send to devlin
2014/08/29 05:00:14
JSON should be ... fairly cheap? Very commonly use
| |
| 104 json_parse.Parse(ToUnicode(data))), | 125 json_parse.Parse(ToUnicode(data)))), |
| 105 CompiledFileSystem, | 126 CompiledFileSystem, |
| 106 category='json') | 127 category='json') |
| 107 | 128 |
| 108 @memoize | 129 @memoize |
| 109 def ForTemplates(self, file_system): | 130 def ForTemplates(self, file_system): |
| 110 '''Creates a CompiledFileSystem for parsing templates. | 131 '''Creates a CompiledFileSystem for parsing templates. |
| 111 ''' | 132 ''' |
| 112 return self.Create( | 133 return self.Create( |
| 113 file_system, | 134 file_system, |
| 114 SingleFile(lambda path, text: Motemplate(ToUnicode(text), name=path)), | 135 SingleFile(lambda path, text: Motemplate(ToUnicode(text), name=path)), |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 127 def __init__(self, | 148 def __init__(self, |
| 128 file_system, | 149 file_system, |
| 129 compilation_function, | 150 compilation_function, |
| 130 file_object_store, | 151 file_object_store, |
| 131 list_object_store): | 152 list_object_store): |
| 132 self._file_system = file_system | 153 self._file_system = file_system |
| 133 self._compilation_function = compilation_function | 154 self._compilation_function = compilation_function |
| 134 self._file_object_store = file_object_store | 155 self._file_object_store = file_object_store |
| 135 self._list_object_store = list_object_store | 156 self._list_object_store = list_object_store |
| 136 | 157 |
| 158 def _GetFromStore(self, key, store): | |
| 159 if _GetUnboundFunction(self._compilation_function) in _CACHEABLE_FUNCTIONS: | |
| 160 return store.Get(key) | |
| 161 return Future(value=None) | |
| 162 | |
| 163 def _SetToStore(self, key, value, store): | |
| 164 if _GetUnboundFunction(self._compilation_function) in _CACHEABLE_FUNCTIONS: | |
| 165 store.Set(key, value) | |
| 166 | |
| 167 def _GetFromFileStore(self, key): | |
| 168 return self._GetFromStore(key, self._file_object_store) | |
| 169 | |
| 170 def _GetFromListStore(self, key): | |
| 171 return self._GetFromStore(key, self._list_object_store) | |
| 172 | |
| 173 def _SetToFileStore(self, key, value): | |
| 174 self._SetToStore(key, value, self._file_object_store) | |
| 175 | |
| 176 def _SetToListStore(self, key, value): | |
| 177 self._SetToStore(key, value, self._list_object_store) | |
| 178 | |
| 137 def _RecursiveList(self, path): | 179 def _RecursiveList(self, path): |
| 138 '''Returns a Future containing the recursive directory listing of |path| as | 180 '''Returns a Future containing the recursive directory listing of |path| as |
| 139 a flat list of paths. | 181 a flat list of paths. |
| 140 ''' | 182 ''' |
| 141 def split_dirs_from_files(paths): | 183 def split_dirs_from_files(paths): |
| 142 '''Returns a tuple (dirs, files) where |dirs| contains the directory | 184 '''Returns a tuple (dirs, files) where |dirs| contains the directory |
| 143 names in |paths| and |files| contains the files. | 185 names in |paths| and |files| contains the files. |
| 144 ''' | 186 ''' |
| 145 result = [], [] | 187 result = [], [] |
| 146 for path in paths: | 188 for path in paths: |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 192 AssertIsFile(path) | 234 AssertIsFile(path) |
| 193 | 235 |
| 194 try: | 236 try: |
| 195 version = self._file_system.Stat(path).version | 237 version = self._file_system.Stat(path).version |
| 196 except FileNotFoundError: | 238 except FileNotFoundError: |
| 197 if skip_not_found: | 239 if skip_not_found: |
| 198 version = None | 240 version = None |
| 199 else: | 241 else: |
| 200 return Future(exc_info=sys.exc_info()) | 242 return Future(exc_info=sys.exc_info()) |
| 201 | 243 |
| 202 cache_entry = self._file_object_store.Get(path).Get() | 244 cache_entry = self._GetFromFileStore(path).Get() |
|
not at google - send to devlin
2014/08/29 05:00:14
These wrapper functions are nice, but if you had s
| |
| 203 if (cache_entry is not None) and (version == cache_entry.version): | 245 if (cache_entry is not None) and (version == cache_entry.version): |
| 204 return Future(value=cache_entry._cache_data) | 246 return Future(value=cache_entry._cache_data) |
| 205 | 247 |
| 206 def compile_(files): | 248 def compile_(files): |
| 207 cache_data = self._compilation_function(path, files) | 249 cache_data = self._compilation_function(path, files) |
| 208 self._file_object_store.Set(path, _CacheEntry(cache_data, version)) | 250 self._SetToFileStore(path, _CacheEntry(cache_data, version)) |
|
not at google - send to devlin
2014/08/29 05:00:14
(FWIW I think that "set in" reads slightly better
| |
| 209 return cache_data | 251 return cache_data |
| 210 | 252 |
| 211 return self._file_system.ReadSingle( | 253 return self._file_system.ReadSingle( |
| 212 path, skip_not_found=skip_not_found).Then(compile_) | 254 path, skip_not_found=skip_not_found).Then(compile_) |
| 213 | 255 |
| 214 def GetFromFileListing(self, path): | 256 def GetFromFileListing(self, path): |
| 215 '''Calls |compilation_function| on the listing of the files at |path|. | 257 '''Calls |compilation_function| on the listing of the files at |path|. |
| 216 Assumes that the path given is to a directory. | 258 Assumes that the path given is to a directory. |
| 217 ''' | 259 ''' |
| 218 AssertIsDirectory(path) | 260 AssertIsDirectory(path) |
| 219 | 261 |
| 220 try: | 262 try: |
| 221 version = self._file_system.Stat(path).version | 263 version = self._file_system.Stat(path).version |
| 222 except FileNotFoundError: | 264 except FileNotFoundError: |
| 223 return Future(exc_info=sys.exc_info()) | 265 return Future(exc_info=sys.exc_info()) |
| 224 | 266 |
| 225 cache_entry = self._list_object_store.Get(path).Get() | 267 cache_entry = self._GetFromListStore(path).Get() |
| 226 if (cache_entry is not None) and (version == cache_entry.version): | 268 if (cache_entry is not None) and (version == cache_entry.version): |
| 227 return Future(value=cache_entry._cache_data) | 269 return Future(value=cache_entry._cache_data) |
| 228 | 270 |
| 229 def next(files): | 271 def next(files): |
| 230 cache_data = self._compilation_function(path, files) | 272 cache_data = self._compilation_function(path, files) |
| 231 self._list_object_store.Set(path, _CacheEntry(cache_data, version)) | 273 self._SetToListStore(path, _CacheEntry(cache_data, version)) |
| 232 return cache_data | 274 return cache_data |
| 233 return self._RecursiveList(path).Then(next) | 275 return self._RecursiveList(path).Then(next) |
| 234 | 276 |
| 235 def GetFileVersion(self, path): | 277 def GetFileVersion(self, path): |
| 236 cache_entry = self._file_object_store.Get(path).Get() | 278 cache_entry = self._GetFromFileStore(path).Get() |
| 237 if cache_entry is not None: | 279 if cache_entry is not None: |
| 238 return cache_entry.version | 280 return cache_entry.version |
| 239 return self._file_system.Stat(path).version | 281 return self._file_system.Stat(path).version |
| 240 | 282 |
| 241 def GetFileListingVersion(self, path): | 283 def GetFileListingVersion(self, path): |
| 242 path = ToDirectory(path) | 284 path = ToDirectory(path) |
| 243 cache_entry = self._list_object_store.Get(path).Get() | 285 cache_entry = self._GetFromListStore(path).Get() |
| 244 if cache_entry is not None: | 286 if cache_entry is not None: |
| 245 return cache_entry.version | 287 return cache_entry.version |
| 246 return self._file_system.Stat(path).version | 288 return self._file_system.Stat(path).version |
| 247 | 289 |
| 248 def FileExists(self, path): | 290 def FileExists(self, path): |
| 249 return self._file_system.Exists(path) | 291 return self._file_system.Exists(path) |
| 250 | 292 |
| 251 def GetIdentity(self): | 293 def GetIdentity(self): |
| 252 return self._file_system.GetIdentity() | 294 return self._file_system.GetIdentity() |
| OLD | NEW |