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
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
])
22 def _FromObjectStoreValue(raw_value
):
23 return dict((key
[0:key
.rfind('@')], raw_value
[key
]) for key
in raw_value
)
25 class _AsyncUncachedFuture(object):
33 self
._version
= version
35 self
._cached
_value
= cached_value
36 self
._missing
_paths
= missing_paths
37 self
._fetch
_delegate
= fetch_delegate
38 self
._object
_store
= object_store
41 uncached_value
= self
._fetch
_delegate
.Get()
42 self
._object
_store
.SetMulti(_ToObjectStoreValue(uncached_value
,
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.
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
74 value
= self
._version
_object
_store
.Get(key
).Get()
77 if self
._datetime
.now() - time
< _VERSION_CACHE_MAXAGE
:
80 version
= self
._patcher
.GetVersion()
81 self
._version
_object
_store
.Set(key
,
82 (version
, self
._datetime
.now()))
85 def GetPatchedFiles(self
, version
=None):
87 version
= self
.GetVersion()
88 patched_files
= self
._list
_object
_store
.Get(version
).Get()
89 if patched_files
is not None:
92 patched_files
= self
._patcher
.GetPatchedFiles(version
)
93 self
._list
_object
_store
.Set(version
, patched_files
)
96 def Apply(self
, paths
, file_system
, version
=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
,
110 self
._patcher
.Apply(set(added
) |
set(modified
),
113 self
._file
_object
_store
)
115 def GetIdentity(self
):
116 return self
._patcher
.GetIdentity()