Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / common / extensions / docs / server2 / caching_rietveld_patcher.py
blob8e7aab6c5d5a1ecd9b5a280ef6199dfd57d574f9
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
3 # found in the LICENSE file.
5 from datetime import datetime, timedelta
6 from file_system import FileNotFoundError
7 from future import Future
8 from patcher import Patcher
10 _VERSION_CACHE_MAXAGE = timedelta(seconds=5)
12 ''' Append @version for keys to distinguish between different patchsets of
13 an issue.
14 '''
15 def _MakeKey(path, version):
16 return '%s@%s' % (path, version)
18 def _ToObjectStoreValue(raw_value, version):
19 return dict((_MakeKey(key, version), raw_value[key])
20 for key in raw_value)
22 def _FromObjectStoreValue(raw_value):
23 return dict((key[0:key.rfind('@')], raw_value[key]) for key in raw_value)
25 class _AsyncUncachedFuture(object):
26 def __init__(self,
27 version,
28 paths,
29 cached_value,
30 missing_paths,
31 fetch_delegate,
32 object_store):
33 self._version = version
34 self._paths = paths
35 self._cached_value = cached_value
36 self._missing_paths = missing_paths
37 self._fetch_delegate = fetch_delegate
38 self._object_store = object_store
40 def Get(self):
41 uncached_value = self._fetch_delegate.Get()
42 self._object_store.SetMulti(_ToObjectStoreValue(uncached_value,
43 self._version))
45 for path in self._missing_paths:
46 if uncached_value.get(path) is None:
47 raise FileNotFoundError('File %s was not found in the patch.' % path)
48 self._cached_value[path] = uncached_value[path]
50 return self._cached_value
52 class CachingRietveldPatcher(Patcher):
53 ''' CachingRietveldPatcher implements a caching layer on top of |patcher|.
54 In theory, it can be used with any class that implements Patcher. But this
55 class assumes that applying to all patched files at once is more efficient
56 than applying to individual files.
57 '''
58 def __init__(self,
59 rietveld_patcher,
60 object_store_creator,
61 test_datetime=datetime):
62 self._patcher = rietveld_patcher
63 def create_object_store(category):
64 return object_store_creator.Create(
65 CachingRietveldPatcher,
66 category='%s/%s' % (rietveld_patcher.GetIdentity(), category))
67 self._version_object_store = create_object_store('version')
68 self._list_object_store = create_object_store('list')
69 self._file_object_store = create_object_store('file')
70 self._datetime = test_datetime
72 def GetVersion(self):
73 key = 'version'
74 value = self._version_object_store.Get(key).Get()
75 if value is not None:
76 version, time = value
77 if self._datetime.now() - time < _VERSION_CACHE_MAXAGE:
78 return version
80 version = self._patcher.GetVersion()
81 self._version_object_store.Set(key,
82 (version, self._datetime.now()))
83 return version
85 def GetPatchedFiles(self, version=None):
86 if version is None:
87 version = self.GetVersion()
88 patched_files = self._list_object_store.Get(version).Get()
89 if patched_files is not None:
90 return patched_files
92 patched_files = self._patcher.GetPatchedFiles(version)
93 self._list_object_store.Set(version, patched_files)
94 return patched_files
96 def Apply(self, paths, file_system, version=None):
97 if version is None:
98 version = self.GetVersion()
99 added, deleted, modified = self.GetPatchedFiles(version)
100 cached_value = _FromObjectStoreValue(self._file_object_store.
101 GetMulti([_MakeKey(path, version) for path in paths]).Get())
102 missing_paths = list(set(paths) - set(cached_value.keys()))
103 if len(missing_paths) == 0:
104 return Future(value=cached_value)
106 return _AsyncUncachedFuture(version,
107 paths,
108 cached_value,
109 missing_paths,
110 self._patcher.Apply(set(added) | set(modified),
111 None,
112 version),
113 self._file_object_store)
115 def GetIdentity(self):
116 return self._patcher.GetIdentity()