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 os | 5 from object_store_creator import ObjectStoreCreator |
6 | |
7 import object_store | |
8 | |
9 APPS = 'Apps' | |
10 APPS_FS = 'AppsFileSystem' | |
11 CRON = 'Cron' | |
12 EXTENSIONS = 'Extensions' | |
13 EXTENSIONS_FS = 'ExtensionsFileSystem' | |
14 CRON_FILE_LISTING = 'Cron.FileListing' | |
15 CRON_GITHUB_INVALIDATION = 'Cron.GithubInvalidation' | |
16 CRON_INVALIDATION = 'Cron.Invalidation' | |
17 HANDLEBAR = 'Handlebar' | |
18 IDL = 'IDL' | |
19 IDL_NO_REFS = 'IDLNoRefs' | |
20 IDL_NAMES = 'IDLNames' | |
21 INTRO = 'Intro' | |
22 JSON = 'JSON' | |
23 JSON_NO_REFS = 'JSONNoRefs' | |
24 LIST = 'List' | |
25 NAMES = 'Names' | |
26 PERMS = 'Perms' | |
27 SIDENAV = 'Sidenav' | |
28 STATIC = 'Static' | |
29 ZIP = 'Zip' | |
30 | 6 |
31 class _CacheEntry(object): | 7 class _CacheEntry(object): |
32 def __init__(self, cache_data, version): | 8 def __init__(self, cache_data, version): |
33 self._cache_data = cache_data | 9 self._cache_data = cache_data |
34 self.version = version | 10 self.version = version |
35 | 11 |
36 class CompiledFileSystem(object): | 12 class CompiledFileSystem(object): |
37 """This class caches FileSystem data that has been processed. | 13 """This class caches FileSystem data that has been processed. |
38 """ | 14 """ |
39 class Factory(object): | 15 class Factory(object): |
40 """A class to build a CompiledFileSystem. | 16 """A class to build a CompiledFileSystem backed by |file_system|. |
| 17 Set an explicit |store_type| for tests. |
41 """ | 18 """ |
42 def __init__(self, file_system, object_store): | 19 def __init__(self, file_system, store_type=None): |
43 self._file_system = file_system | 20 self._file_system = file_system |
44 self._object_store = object_store | 21 self._store_type = store_type |
| 22 self._identity_instance = None |
45 | 23 |
46 def Create(self, populate_function, namespace, version=None): | 24 def Create(self, populate_function, cls, category=None, version=None): |
47 """Create a CompiledFileSystem that populates the cache by calling | 25 """Create a CompiledFileSystem that populates the cache by calling |
48 |populate_function| with (path, data), where |data| is the data that was | 26 |populate_function| with (path, data), where |data| is the data that was |
49 fetched from |path|. The keys to the cache are put in the namespace | 27 fetched from |path|. |
50 specified by |namespace|, and optionally adding |version|. """ | 28 The namespace for the file system is derived like ObjectStoreCreator: from |
51 return CompiledFileSystem(self._file_system, | 29 |cls| along with an optional |category| and |version|. |
52 populate_function, | 30 """ |
53 self._object_store, | 31 assert isinstance(cls, type) |
54 namespace, | 32 assert not cls.__name__[0].islower() # guard against non-class types |
55 version=version) | 33 full_name = cls.__name__ |
| 34 if category is not None: |
| 35 full_name = '%s/%s' % (full_name, category) |
| 36 return self._Create(populate_function, full_name, version=version) |
| 37 |
| 38 def GetOrCreateIdentity(self): |
| 39 '''Handy helper to get or create the identity compiled file system. |
| 40 GetFromFile will return the file's contents. |
| 41 GetFromFileListing will return the directory list. |
| 42 ''' |
| 43 if self._identity_instance is None: |
| 44 self._identity_instance = self._Create(lambda _, x: x, 'id') |
| 45 return self._identity_instance |
| 46 |
| 47 def _Create(self, populate_function, full_name, version=None): |
| 48 object_store_creator = ObjectStoreCreator(CompiledFileSystem, |
| 49 store_type=self._store_type) |
| 50 return CompiledFileSystem( |
| 51 self._file_system, |
| 52 populate_function, |
| 53 object_store_creator.Create(category='%s/file' % full_name, |
| 54 version=version), |
| 55 object_store_creator.Create(category='%s/list' % full_name, |
| 56 version=version)) |
56 | 57 |
57 def __init__(self, | 58 def __init__(self, |
58 file_system, | 59 file_system, |
59 populate_function, | 60 populate_function, |
60 object_store, | 61 file_object_store, |
61 namespace, | 62 list_object_store): |
62 version=None): | |
63 self._file_system = file_system | 63 self._file_system = file_system |
64 self._populate_function = populate_function | 64 self._populate_function = populate_function |
65 self._object_store = object_store | 65 self._file_object_store = file_object_store |
66 self._namespace = 'CompiledFileSystem.' + namespace | 66 self._list_object_store = list_object_store |
67 if version is not None: | |
68 self._namespace = '%s.%s' % (self._namespace, version) | |
69 | 67 |
70 def _MakeKey(self, key): | 68 def _RecursiveList(self, path): |
71 return self._namespace + '.' + key | 69 files = [] |
72 | 70 for filename in self._file_system.ReadSingle(path): |
73 def _RecursiveList(self, files): | |
74 all_files = files[:] | |
75 dirs = {} | |
76 for filename in files: | |
77 if filename.endswith('/'): | 71 if filename.endswith('/'): |
78 all_files.remove(filename) | 72 files.extend(['%s%s' % (filename, f) |
79 dirs.update(self._file_system.Read([filename]).Get()) | 73 for f in self._RecursiveList('%s%s' % (path, filename))]) |
80 for dir_, files in dirs.iteritems(): | 74 else: |
81 all_files.extend(self._RecursiveList([dir_ + f for f in files])) | 75 files.append(filename) |
82 return all_files | 76 return files |
83 | 77 |
84 def GetFromFile(self, path, binary=False): | 78 def GetFromFile(self, path, binary=False): |
85 """Calls |populate_function| on the contents of the file at |path|. If | 79 """Calls |populate_function| on the contents of the file at |path|. If |
86 |binary| is True then the file will be read as binary - but this will only | 80 |binary| is True then the file will be read as binary - but this will only |
87 apply for the first time the file is fetched; if already cached, |binary| | 81 apply for the first time the file is fetched; if already cached, |binary| |
88 will be ignored. | 82 will be ignored. |
89 """ | 83 """ |
90 version = self._file_system.Stat(path).version | 84 version = self._file_system.Stat(path).version |
91 cache_entry = self._object_store.Get(self._MakeKey(path), | 85 cache_entry = self._file_object_store.Get(path, time=0).Get() |
92 object_store.FILE_SYSTEM_CACHE, | |
93 time=0).Get() | |
94 if (cache_entry is not None) and (version == cache_entry.version): | 86 if (cache_entry is not None) and (version == cache_entry.version): |
95 return cache_entry._cache_data | 87 return cache_entry._cache_data |
96 cache_data = self._populate_function( | 88 cache_data = self._populate_function( |
97 path, | 89 path, |
98 self._file_system.ReadSingle(path, binary=binary)) | 90 self._file_system.ReadSingle(path, binary=binary)) |
99 self._object_store.Set(self._MakeKey(path), | 91 self._file_object_store.Set(path, _CacheEntry(cache_data, version), time=0) |
100 _CacheEntry(cache_data, version), | |
101 object_store.FILE_SYSTEM_CACHE, | |
102 time=0) | |
103 return cache_data | 92 return cache_data |
104 | 93 |
105 def GetFromFileListing(self, path): | 94 def GetFromFileListing(self, path): |
106 """Calls |populate_function| on the listing of the files at |path|. | 95 """Calls |populate_function| on the listing of the files at |path|. |
107 Assumes that the path given is to a directory. | 96 Assumes that the path given is to a directory. |
108 """ | 97 """ |
109 if not path.endswith('/'): | 98 if not path.endswith('/'): |
110 path += '/' | 99 path += '/' |
111 version = self._file_system.Stat(path).version | 100 version = self._file_system.Stat(path).version |
112 cache_entry = self._object_store.Get( | 101 cache_entry = self._list_object_store.Get(path, time=0).Get() |
113 self._MakeKey(path), | |
114 object_store.FILE_SYSTEM_CACHE_LISTING, | |
115 time=0).Get() | |
116 if (cache_entry is not None) and (version == cache_entry.version): | 102 if (cache_entry is not None) and (version == cache_entry.version): |
117 return cache_entry._cache_data | 103 return cache_entry._cache_data |
118 cache_data = self._populate_function( | 104 cache_data = self._populate_function(path, self._RecursiveList(path)) |
119 path, | 105 self._list_object_store.Set(path, _CacheEntry(cache_data, version), time=0) |
120 self._RecursiveList( | |
121 [path + f for f in self._file_system.ReadSingle(path)])) | |
122 self._object_store.Set(self._MakeKey(path), | |
123 _CacheEntry(cache_data, version), | |
124 object_store.FILE_SYSTEM_CACHE_LISTING, | |
125 time=0) | |
126 return cache_data | 106 return cache_data |
OLD | NEW |