| Index: chrome/common/extensions/docs/server2/caching_rietveld_patcher.py
|
| diff --git a/chrome/common/extensions/docs/server2/caching_rietveld_patcher.py b/chrome/common/extensions/docs/server2/caching_rietveld_patcher.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..98c1924345760e8108d1ee10ea6aa816cc25a2cc
|
| --- /dev/null
|
| +++ b/chrome/common/extensions/docs/server2/caching_rietveld_patcher.py
|
| @@ -0,0 +1,125 @@
|
| +# Copyright 2013 The Chromium Authors. All rights reserved.
|
| +# Use of this source code is governed by a BSD-style license that can be
|
| +# found in the LICENSE file.
|
| +
|
| +from datetime import datetime, timedelta
|
| +from file_system import FileNotFoundError, ToUnicode
|
| +from future import Future
|
| +from patcher import Patcher
|
| +
|
| +_VERSION_CACHE_MAXAGE = timedelta(seconds=5)
|
| +
|
| +''' Append @version for keys to distinguish between different patchsets of
|
| +an issue.
|
| +'''
|
| +def _MakeKey(path, version):
|
| + return '%s@%s' % (path, version)
|
| +
|
| +def _ToObjectStoreValue(raw_value, version):
|
| + return {_MakeKey(key, version): raw_value[key] for key in raw_value}
|
| +
|
| +def _FromObjectStoreValue(raw_value, binary):
|
| + return {key[0:key.rfind('@')]: _HandleBinary(raw_value[key], binary)
|
| + for key in raw_value}
|
| +
|
| +def _HandleBinary(data, binary):
|
| + return data if binary else ToUnicode(data)
|
| +
|
| +class _AsyncUncachedFuture(object):
|
| + def __init__(self,
|
| + version,
|
| + paths,
|
| + binary,
|
| + cached_value,
|
| + missing_paths,
|
| + fetch_delegate,
|
| + object_store):
|
| + self._version = version
|
| + self._paths = paths
|
| + self._binary = binary
|
| + self._cached_value = cached_value
|
| + self._missing_paths = missing_paths
|
| + self._fetch_delegate = fetch_delegate
|
| + self._object_store = object_store
|
| +
|
| + def Get(self):
|
| + uncached_raw_value = self._fetch_delegate.Get()
|
| + self._object_store.SetMulti(_ToObjectStoreValue(uncached_raw_value,
|
| + self._version))
|
| +
|
| + for path in self._missing_paths:
|
| + if uncached_raw_value.get(path) is None:
|
| + raise FileNotFoundError('File %s was not found in the patch.' % path)
|
| + self._cached_value[path] = _HandleBinary(uncached_raw_value[path],
|
| + self._binary)
|
| +
|
| + return self._cached_value
|
| +
|
| +class CachingRietveldPatcher(Patcher):
|
| + ''' CachingRietveldPatcher implements a caching layer on top of |patcher|.
|
| + In theory, it can be used with any class that implements Patcher. But this
|
| + class assumes that applying to all patched files at once is more efficient
|
| + than applying to individual files.
|
| + '''
|
| + def __init__(self,
|
| + rietveld_patcher,
|
| + object_store_creator,
|
| + test_datetime=datetime):
|
| + self._patcher = rietveld_patcher
|
| + self._version_object_store = object_store_creator.Create(
|
| + CachingRietveldPatcher, category='version')
|
| + self._list_object_store = object_store_creator.Create(
|
| + CachingRietveldPatcher, category='list')
|
| + self._file_object_store = object_store_creator.Create(
|
| + CachingRietveldPatcher, category='file')
|
| + self._datetime = test_datetime
|
| +
|
| + def GetVersion(self):
|
| + key = 'version'
|
| + value = self._version_object_store.Get(key).Get()
|
| + if value is not None:
|
| + version, time = value
|
| + if self._datetime.now() - time < _VERSION_CACHE_MAXAGE:
|
| + return version
|
| +
|
| + version = self._patcher.GetVersion()
|
| + self._version_object_store.Set(key,
|
| + (version, self._datetime.now()))
|
| + return version
|
| +
|
| + def GetPatchedFiles(self, version=None):
|
| + if version is None:
|
| + version = self.GetVersion()
|
| + patched_files = self._list_object_store.Get(version).Get()
|
| + if patched_files is not None:
|
| + return patched_files
|
| +
|
| + patched_files = self._patcher.GetPatchedFiles(version)
|
| + self._list_object_store.Set(version, patched_files)
|
| + return patched_files
|
| +
|
| + def Apply(self, paths, file_system, binary=False, version=None):
|
| + if version is None:
|
| + version = self.GetVersion()
|
| + added, deleted, modified = self.GetPatchedFiles(version)
|
| + cached_value = _FromObjectStoreValue(self._file_object_store.
|
| + GetMulti([_MakeKey(path, version) for path in paths]).Get(), binary)
|
| + missing_paths = list(set(paths) - set(cached_value.keys()))
|
| + if len(missing_paths) == 0:
|
| + return Future(value=cached_value)
|
| +
|
| + # binary is explicitly set to True. Here we are applying the patch to
|
| + # ALL patched files without a way to know whether individual files are
|
| + # binary or not. Therefore all data cached must be binary. When reading
|
| + # from the cache with binary=False, it will be converted to Unicode by
|
| + # _HandleBinary.
|
| + return _AsyncUncachedFuture(version,
|
| + paths,
|
| + binary,
|
| + cached_value,
|
| + missing_paths,
|
| + self._patcher.Apply(set(added) | set(modified),
|
| + None,
|
| + True,
|
| + version),
|
| + self._file_object_store)
|
|
|