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

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

Issue 521693003: Docserver: Add @Cache annotation to CompiledFileSystem (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Small clarification + rebase 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 (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
8 from docs_server_utils import ToUnicode 7 from docs_server_utils import ToUnicode
9 from file_system import FileNotFoundError 8 from file_system import FileNotFoundError
10 from future import Future 9 from future import Future
11 from path_util import AssertIsDirectory, AssertIsFile, ToDirectory 10 from path_util import AssertIsDirectory, AssertIsFile, ToDirectory
12 from third_party.json_schema_compiler import json_parse 11 from third_party.json_schema_compiler import json_parse
13 from third_party.json_schema_compiler.memoize import memoize 12 from third_party.json_schema_compiler.memoize import memoize
14 from third_party.motemplate import Motemplate 13 from third_party.motemplate import Motemplate
15 14
16 15
16 _CACHEABLE_FUNCTIONS = set()
17 _SINGLE_FILE_FUNCTIONS = set() 17 _SINGLE_FILE_FUNCTIONS = set()
18 18
19 19
20 def _GetUnboundFunction(fn):
21 '''Functions bound to an object are separate from the unbound
22 defintion. This causes issues when checking for cache membership,
23 so always get the unbound function, if possible.
24 '''
25 return getattr(fn, 'im_func', fn)
26
27
28 def Cache(fn):
29 '''A decorator which can be applied to the compilation function
30 passed to CompiledFileSystem.Create, indicating that file/list data
31 should be cached.
32
33 This decorator should be listed first in any list of decorators, along
34 with the SingleFile decorator below.
35 '''
36 _CACHEABLE_FUNCTIONS.add(_GetUnboundFunction(fn))
37 return fn
38
39
20 def SingleFile(fn): 40 def SingleFile(fn):
21 '''A decorator which can be optionally applied to the compilation function 41 '''A decorator which can be optionally applied to the compilation function
22 passed to CompiledFileSystem.Create, indicating that the function only 42 passed to CompiledFileSystem.Create, indicating that the function only
23 needs access to the file which is given in the function's callback. When 43 needs access to the file which is given in the function's callback. When
24 this is the case some optimisations can be done. 44 this is the case some optimisations can be done.
25 45
26 Note that this decorator must be listed first in any list of decorators to 46 Note that this decorator must be listed first in any list of decorators to
27 have any effect. 47 have any effect.
28 ''' 48 '''
29 _SINGLE_FILE_FUNCTIONS.add(fn) 49 _SINGLE_FILE_FUNCTIONS.add(_GetUnboundFunction(fn))
30 return fn 50 return fn
31 51
32 52
33 def Unicode(fn): 53 def Unicode(fn):
34 '''A decorator which can be optionally applied to the compilation function 54 '''A decorator which can be optionally applied to the compilation function
35 passed to CompiledFileSystem.Create, indicating that the function processes 55 passed to CompiledFileSystem.Create, indicating that the function processes
36 the file's data as Unicode text. 56 the file's data as Unicode text.
37 ''' 57 '''
38 58
39 # The arguments passed to fn can be (self, path, data) or (path, data). In 59 # The arguments passed to fn can be (self, path, data) or (path, data). In
40 # either case the last argument is |data|, which should be converted to 60 # either case the last argument is |data|, which should be converted to
41 # Unicode. 61 # Unicode.
42 def convert_args(args): 62 def convert_args(args):
43 args = list(args) 63 args = list(args)
44 args[-1] = ToUnicode(args[-1]) 64 args[-1] = ToUnicode(args[-1])
45 return args 65 return args
46 66
47 return lambda *args: fn(*convert_args(args)) 67 return lambda *args: fn(*convert_args(args))
48 68
49 69
50 class _CacheEntry(object): 70 class _CacheEntry(object):
51 def __init__(self, cache_data, version): 71 def __init__(self, cache_data, version):
52 72
53 self._cache_data = cache_data 73 self.cache_data = cache_data
54 self.version = version 74 self.version = version
55 75
56 76
57 class CompiledFileSystem(object): 77 class CompiledFileSystem(object):
58 '''This class caches FileSystem data that has been processed. 78 '''This class caches FileSystem data that has been processed.
59 ''' 79 '''
60 80
61 class Factory(object): 81 class Factory(object):
62 '''A class to build a CompiledFileSystem backed by |file_system|. 82 '''A class to build a CompiledFileSystem backed by |file_system|.
63 ''' 83 '''
(...skipping 29 matching lines...) Expand all
93 compilation_function, 113 compilation_function,
94 create_object_store('file'), 114 create_object_store('file'),
95 create_object_store('list')) 115 create_object_store('list'))
96 116
97 @memoize 117 @memoize
98 def ForJson(self, file_system): 118 def ForJson(self, file_system):
99 '''A CompiledFileSystem specifically for parsing JSON configuration data. 119 '''A CompiledFileSystem specifically for parsing JSON configuration data.
100 These are memoized over file systems tied to different branches. 120 These are memoized over file systems tied to different branches.
101 ''' 121 '''
102 return self.Create(file_system, 122 return self.Create(file_system,
103 SingleFile(lambda _, data: 123 Cache(SingleFile(lambda _, data:
104 json_parse.Parse(ToUnicode(data))), 124 json_parse.Parse(ToUnicode(data)))),
105 CompiledFileSystem, 125 CompiledFileSystem,
106 category='json') 126 category='json')
107 127
108 @memoize 128 @memoize
109 def ForTemplates(self, file_system): 129 def ForTemplates(self, file_system):
110 '''Creates a CompiledFileSystem for parsing templates. 130 '''Creates a CompiledFileSystem for parsing templates.
111 ''' 131 '''
112 return self.Create( 132 return self.Create(
113 file_system, 133 file_system,
114 SingleFile(lambda path, text: Motemplate(ToUnicode(text), name=path)), 134 SingleFile(lambda path, text: Motemplate(ToUnicode(text), name=path)),
(...skipping 12 matching lines...) Expand all
127 def __init__(self, 147 def __init__(self,
128 file_system, 148 file_system,
129 compilation_function, 149 compilation_function,
130 file_object_store, 150 file_object_store,
131 list_object_store): 151 list_object_store):
132 self._file_system = file_system 152 self._file_system = file_system
133 self._compilation_function = compilation_function 153 self._compilation_function = compilation_function
134 self._file_object_store = file_object_store 154 self._file_object_store = file_object_store
135 self._list_object_store = list_object_store 155 self._list_object_store = list_object_store
136 156
157 def _Get(self, store, key):
158 if _GetUnboundFunction(self._compilation_function) in _CACHEABLE_FUNCTIONS:
159 return store.Get(key)
160 return Future(value=None)
161
162 def _Set(self, store, key, value):
163 if _GetUnboundFunction(self._compilation_function) in _CACHEABLE_FUNCTIONS:
164 store.Set(key, value)
165
137 def _RecursiveList(self, path): 166 def _RecursiveList(self, path):
138 '''Returns a Future containing the recursive directory listing of |path| as 167 '''Returns a Future containing the recursive directory listing of |path| as
139 a flat list of paths. 168 a flat list of paths.
140 ''' 169 '''
141 def split_dirs_from_files(paths): 170 def split_dirs_from_files(paths):
142 '''Returns a tuple (dirs, files) where |dirs| contains the directory 171 '''Returns a tuple (dirs, files) where |dirs| contains the directory
143 names in |paths| and |files| contains the files. 172 names in |paths| and |files| contains the files.
144 ''' 173 '''
145 result = [], [] 174 result = [], []
146 for path in paths: 175 for path in paths:
(...skipping 25 matching lines...) Expand all
172 # |path| so that |file_system| can find the files. 201 # |path| so that |file_system| can find the files.
173 dirs += add_prefix(dir_name, new_dirs) 202 dirs += add_prefix(dir_name, new_dirs)
174 # |files| are not for reading, they are for returning to the caller. 203 # |files| are not for reading, they are for returning to the caller.
175 # This entire function set (i.e. GetFromFileListing) is defined to 204 # This entire function set (i.e. GetFromFileListing) is defined to
176 # not include the fetched-path in the result, however, |dir_name| 205 # not include the fetched-path in the result, however, |dir_name|
177 # will be prefixed with |path|. Strip it. 206 # will be prefixed with |path|. Strip it.
178 assert dir_name.startswith(path) 207 assert dir_name.startswith(path)
179 files += add_prefix(dir_name[len(path):], new_files) 208 files += add_prefix(dir_name[len(path):], new_files)
180 if dirs: 209 if dirs:
181 files += self._file_system.Read(dirs).Then( 210 files += self._file_system.Read(dirs).Then(
182 lambda results: get_from_future_listing(results)).Get() 211 get_from_future_listing).Get()
183 return files 212 return files
184 213
185 return self._file_system.Read(add_prefix(path, first_layer_dirs)).Then( 214 return self._file_system.Read(add_prefix(path, first_layer_dirs)).Then(
186 lambda results: first_layer_files + get_from_future_listing(results)) 215 lambda results: first_layer_files + get_from_future_listing(results))
187 216
188 def GetFromFile(self, path, skip_not_found=False): 217 def GetFromFile(self, path, skip_not_found=False):
189 '''Calls |compilation_function| on the contents of the file at |path|. 218 '''Calls |compilation_function| on the contents of the file at |path|.
190 If |skip_not_found| is True, then None is passed to |compilation_function|. 219 If |skip_not_found| is True, then None is passed to |compilation_function|.
191 ''' 220 '''
192 AssertIsFile(path) 221 AssertIsFile(path)
193 222
194 try: 223 try:
195 version = self._file_system.Stat(path).version 224 version = self._file_system.Stat(path).version
196 except FileNotFoundError: 225 except FileNotFoundError:
197 if skip_not_found: 226 if skip_not_found:
198 version = None 227 version = None
199 else: 228 else:
200 return Future(exc_info=sys.exc_info()) 229 return Future(exc_info=sys.exc_info())
201 230
202 cache_entry = self._file_object_store.Get(path).Get() 231 cache_entry = self._Get(self._file_object_store, path).Get()
203 if (cache_entry is not None) and (version == cache_entry.version): 232 if (cache_entry is not None) and (version == cache_entry.version):
204 return Future(value=cache_entry._cache_data) 233 return Future(value=cache_entry.cache_data)
205 234
206 def compile_(files): 235 def compile_(files):
207 cache_data = self._compilation_function(path, files) 236 cache_data = self._compilation_function(path, files)
208 self._file_object_store.Set(path, _CacheEntry(cache_data, version)) 237 self._Set(self._file_object_store, path, _CacheEntry(cache_data, version))
209 return cache_data 238 return cache_data
210 239
211 return self._file_system.ReadSingle( 240 return self._file_system.ReadSingle(
212 path, skip_not_found=skip_not_found).Then(compile_) 241 path, skip_not_found=skip_not_found).Then(compile_)
213 242
214 def GetFromFileListing(self, path): 243 def GetFromFileListing(self, path):
215 '''Calls |compilation_function| on the listing of the files at |path|. 244 '''Calls |compilation_function| on the listing of the files at |path|.
216 Assumes that the path given is to a directory. 245 Assumes that the path given is to a directory.
217 ''' 246 '''
218 AssertIsDirectory(path) 247 AssertIsDirectory(path)
219 248
220 try: 249 try:
221 version = self._file_system.Stat(path).version 250 version = self._file_system.Stat(path).version
222 except FileNotFoundError: 251 except FileNotFoundError:
223 return Future(exc_info=sys.exc_info()) 252 return Future(exc_info=sys.exc_info())
224 253
225 cache_entry = self._list_object_store.Get(path).Get() 254 cache_entry = self._Get(self._list_object_store, path).Get()
226 if (cache_entry is not None) and (version == cache_entry.version): 255 if (cache_entry is not None) and (version == cache_entry.version):
227 return Future(value=cache_entry._cache_data) 256 return Future(value=cache_entry.cache_data)
228 257
229 def next(files): 258 def compile_(files):
230 cache_data = self._compilation_function(path, files) 259 cache_data = self._compilation_function(path, files)
231 self._list_object_store.Set(path, _CacheEntry(cache_data, version)) 260 self._Set(self._list_object_store, path, _CacheEntry(cache_data, version))
232 return cache_data 261 return cache_data
233 return self._RecursiveList(path).Then(next) 262 return self._RecursiveList(path).Then(compile_)
234 263
235 # _GetFileVersionFromCache and _GetFileListingVersionFromCache are exposed 264 # _GetFileVersionFromCache and _GetFileListingVersionFromCache are exposed
236 # *only* so that ChainedCompiledFileSystem can optimise its caches. *Do not* 265 # *only* so that ChainedCompiledFileSystem can optimise its caches. *Do not*
237 # use these methods otherwise, they don't do what you want. Use 266 # use these methods otherwise, they don't do what you want. Use
238 # FileSystem.Stat on the FileSystem that this CompiledFileSystem uses. 267 # FileSystem.Stat on the FileSystem that this CompiledFileSystem uses.
239 268
240 def _GetFileVersionFromCache(self, path): 269 def _GetFileVersionFromCache(self, path):
241 cache_entry = self._file_object_store.Get(path).Get() 270 cache_entry = self._Get(self._file_object_store, path).Get()
242 if cache_entry is not None: 271 if cache_entry is not None:
243 return Future(value=cache_entry.version) 272 return Future(value=cache_entry.version)
244 stat_future = self._file_system.StatAsync(path) 273 stat_future = self._file_system.StatAsync(path)
245 return Future(callback=lambda: stat_future.Get().version) 274 return Future(callback=lambda: stat_future.Get().version)
246 275
247 def _GetFileListingVersionFromCache(self, path): 276 def _GetFileListingVersionFromCache(self, path):
248 path = ToDirectory(path) 277 path = ToDirectory(path)
249 cache_entry = self._list_object_store.Get(path).Get() 278 cache_entry = self._Get(self._list_object_store, path).Get()
250 if cache_entry is not None: 279 if cache_entry is not None:
251 return Future(value=cache_entry.version) 280 return Future(value=cache_entry.version)
252 stat_future = self._file_system.StatAsync(path) 281 stat_future = self._file_system.StatAsync(path)
253 return Future(callback=lambda: stat_future.Get().version) 282 return Future(callback=lambda: stat_future.Get().version)
254 283
255 def GetIdentity(self): 284 def GetIdentity(self):
256 return self._file_system.GetIdentity() 285 return self._file_system.GetIdentity()
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698