summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorahernandez.miralles@gmail.com <ahernandez.miralles@gmail.com@0039d316-1c4b-4281-b951-d872f2087c98>2014-08-05 07:46:03 +0000
committerahernandez.miralles@gmail.com <ahernandez.miralles@gmail.com@0039d316-1c4b-4281-b951-d872f2087c98>2014-08-05 07:46:03 +0000
commitcc0d4be2389a0cc9eb37e50b4fd868916655ba21 (patch)
tree5327e211e95711a6e70531bbfb320ca6bfe75969
parentf0938d1e15514d060fde1acdc96124aa58f9f296 (diff)
downloadchromium_src-cc0d4be2389a0cc9eb37e50b4fd868916655ba21.zip
chromium_src-cc0d4be2389a0cc9eb37e50b4fd868916655ba21.tar.gz
chromium_src-cc0d4be2389a0cc9eb37e50b4fd868916655ba21.tar.bz2
Docserver: Update Future.Then() to be more Promise-like
BUG=306341 NOTRY=True Review URL: https://codereview.chromium.org/417163004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@287482 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/common/extensions/docs/server2/caching_file_system.py28
-rw-r--r--chrome/common/extensions/docs/server2/chroot_file_system.py10
-rw-r--r--chrome/common/extensions/docs/server2/fake_url_fetcher.py7
-rw-r--r--chrome/common/extensions/docs/server2/file_system.py11
-rw-r--r--chrome/common/extensions/docs/server2/future.py15
-rwxr-xr-xchrome/common/extensions/docs/server2/future_test.py57
-rw-r--r--chrome/common/extensions/docs/server2/mock_file_system.py7
-rwxr-xr-xchrome/common/extensions/docs/server2/mock_file_system_test.py2
8 files changed, 96 insertions, 41 deletions
diff --git a/chrome/common/extensions/docs/server2/caching_file_system.py b/chrome/common/extensions/docs/server2/caching_file_system.py
index 59d8030..c0ff5d0 100644
--- a/chrome/common/extensions/docs/server2/caching_file_system.py
+++ b/chrome/common/extensions/docs/server2/caching_file_system.py
@@ -63,14 +63,12 @@ class CachingFileSystem(FileSystem):
if dir_stat is not None:
return Future(value=make_stat_info(dir_stat))
- dir_stat_future = self._MemoizedStatAsyncFromFileSystem(dir_path)
- def resolve():
- dir_stat = dir_stat_future.Get()
+ def next(dir_stat):
assert dir_stat is not None # should have raised a FileNotFoundError
# We only ever need to cache the dir stat.
self._stat_object_store.Set(dir_path, dir_stat)
return make_stat_info(dir_stat)
- return Future(callback=resolve)
+ return self._MemoizedStatAsyncFromFileSystem(dir_path).Then(next)
@memoize
def _MemoizedStatAsyncFromFileSystem(self, dir_path):
@@ -93,18 +91,17 @@ class CachingFileSystem(FileSystem):
# with a value.
stat_futures = {}
- def swallow_file_not_found_error(future):
- def resolve():
- try: return future.Get()
- except FileNotFoundError: return Nnone
- return Future(callback=resolve)
+ def handle(error):
+ if isinstance(error, FileNotFoundError):
+ return None
+ raise error
for path in paths:
stat_value = cached_stat_values.get(path)
if stat_value is None:
stat_future = self.StatAsync(path)
if skip_not_found:
- stat_future = swallow_file_not_found_error(stat_future)
+ stat_future = stat_future.Then(lambda x: x, handle)
else:
stat_future = Future(value=stat_value)
stat_futures[path] = stat_future
@@ -120,19 +117,16 @@ class CachingFileSystem(FileSystem):
# Everything was cached and up-to-date.
return Future(value=fresh_data)
- # Read in the values that were uncached or old.
- read_futures = self._file_system.Read(
- set(paths) - set(fresh_data.iterkeys()),
- skip_not_found=skip_not_found)
- def resolve():
- new_results = read_futures.Get()
+ def next(new_results):
# Update the cache. This is a path -> (data, version) mapping.
self._read_object_store.SetMulti(
dict((path, (new_result, stat_futures[path].Get().version))
for path, new_result in new_results.iteritems()))
new_results.update(fresh_data)
return new_results
- return Future(callback=resolve)
+ # Read in the values that were uncached or old.
+ return self._file_system.Read(set(paths) - set(fresh_data.iterkeys()),
+ skip_not_found=skip_not_found).Then(next)
def GetIdentity(self):
return self._file_system.GetIdentity()
diff --git a/chrome/common/extensions/docs/server2/chroot_file_system.py b/chrome/common/extensions/docs/server2/chroot_file_system.py
index af521d8..2ed612f 100644
--- a/chrome/common/extensions/docs/server2/chroot_file_system.py
+++ b/chrome/common/extensions/docs/server2/chroot_file_system.py
@@ -32,13 +32,11 @@ class ChrootFileSystem(FileSystem):
prefixed = posixpath.join(self._root, path)
prefixed_paths[prefixed] = path
return prefixed
- future_result = self._file_system.Read(
- tuple(prefix(path) for path in paths),
- skip_not_found=skip_not_found)
- def resolve():
+ def next(results):
return dict((prefixed_paths[path], content)
- for path, content in future_result.Get().iteritems())
- return Future(callback=resolve)
+ for path, content in results.iteritems())
+ return self._file_system.Read(tuple(prefix(path) for path in paths),
+ skip_not_found-skip_not_found).Then(next)
def Refresh(self):
return self._file_system.Refresh()
diff --git a/chrome/common/extensions/docs/server2/fake_url_fetcher.py b/chrome/common/extensions/docs/server2/fake_url_fetcher.py
index dbfaa55..21b6cf6 100644
--- a/chrome/common/extensions/docs/server2/fake_url_fetcher.py
+++ b/chrome/common/extensions/docs/server2/fake_url_fetcher.py
@@ -129,11 +129,10 @@ class MockURLFetcher(object):
def FetchAsync(self, url, **kwargs):
self._fetch_async_count += 1
- future = self._fetcher.FetchAsync(url, **kwargs)
- def resolve():
+ def next(result):
self._fetch_resolve_count += 1
- return future.Get()
- return Future(callback=resolve)
+ return result
+ return self._fetcher.FetchAsync(url, **kwargs).Then(next)
def CheckAndReset(self,
fetch_count=0,
diff --git a/chrome/common/extensions/docs/server2/file_system.py b/chrome/common/extensions/docs/server2/file_system.py
index 99e33c5..1e5b567 100644
--- a/chrome/common/extensions/docs/server2/file_system.py
+++ b/chrome/common/extensions/docs/server2/file_system.py
@@ -106,13 +106,12 @@ class FileSystem(object):
return Future(value=True)
parent, base = SplitParent(path)
- list_future = self.ReadSingle(ToDirectory(parent))
- def resolve():
- try:
- return base in list_future.Get()
- except FileNotFoundError:
+ def handle(error):
+ if isinstance(error, FileNotFoundError):
return False
- return Future(callback=resolve)
+ raise error
+ return self.ReadSingle(ToDirectory(parent)).Then(lambda l: base in l,
+ handle)
def Refresh(self):
'''Asynchronously refreshes the content of the FileSystem, returning a
diff --git a/chrome/common/extensions/docs/server2/future.py b/chrome/common/extensions/docs/server2/future.py
index 4b09073..51c2842 100644
--- a/chrome/common/extensions/docs/server2/future.py
+++ b/chrome/common/extensions/docs/server2/future.py
@@ -7,6 +7,10 @@ import sys
_no_value = object()
+def _DefaultErrorHandler(error):
+ raise error
+
+
def All(futures, except_pass=None):
'''Creates a Future which returns a list of results from each Future in
|futures|.
@@ -61,12 +65,17 @@ class Future(object):
self._exc_info is None):
raise ValueError('Must have either a value, error, or callback.')
- def Then(self, callback):
+ def Then(self, callback, error_handler=_DefaultErrorHandler):
'''Creates and returns a future that runs |callback| on the value of this
- future.
+ future, or runs optional |error_handler| if resolving this future results in
+ an exception.
'''
def then():
- return callback(self.Get())
+ try:
+ val = self.Get()
+ except Exception as e:
+ return error_handler(e)
+ return callback(val)
return Future(callback=then)
def Get(self):
diff --git a/chrome/common/extensions/docs/server2/future_test.py b/chrome/common/extensions/docs/server2/future_test.py
index 440994b..1c188d1 100755
--- a/chrome/common/extensions/docs/server2/future_test.py
+++ b/chrome/common/extensions/docs/server2/future_test.py
@@ -159,6 +159,63 @@ class FutureTest(unittest.TestCase):
except_pass=(ValueError,))
self.assertRaises(ValueError, race.Get)
+ def testThen(self):
+ def assertIs42(val):
+ self.assertEquals(val, 42)
+
+ then = Future(value=42).Then(assertIs42)
+ # Shouldn't raise an error.
+ then.Get()
+
+ # Test raising an error.
+ then = Future(value=41).Then(assertIs42)
+ self.assertRaises(AssertionError, then.Get)
+
+ # Test setting up an error handler.
+ def handle(error):
+ if isinstance(error, ValueError):
+ return 'Caught'
+ raise error
+
+ def raiseValueError():
+ raise ValueError
+
+ def raiseException():
+ raise Exception
+
+ then = Future(callback=raiseValueError).Then(assertIs42, handle)
+ self.assertEquals(then.Get(), 'Caught')
+ then = Future(callback=raiseException).Then(assertIs42, handle)
+ self.assertRaises(Exception, then.Get)
+
+ # Test chains of thens.
+ addOne = lambda val: val + 1
+ then = Future(value=40).Then(addOne).Then(addOne).Then(assertIs42)
+ # Shouldn't raise an error.
+ then.Get()
+
+ # Test error in chain.
+ then = Future(value=40).Then(addOne).Then(assertIs42).Then(addOne)
+ self.assertRaises(AssertionError, then.Get)
+
+ # Test handle error in chain.
+ def raiseValueErrorWithVal(val):
+ raise ValueError
+
+ then = Future(value=40).Then(addOne).Then(raiseValueErrorWithVal).Then(
+ addOne, handle).Then(lambda val: val + ' me')
+ self.assertEquals(then.Get(), 'Caught me')
+
+ # Test multiple handlers.
+ def myHandle(error):
+ if isinstance(error, AssertionError):
+ return 10
+ raise error
+
+ then = Future(value=40).Then(assertIs42).Then(addOne, handle).Then(addOne,
+ myHandle)
+ self.assertEquals(then.Get(), 10)
+
if __name__ == '__main__':
unittest.main()
diff --git a/chrome/common/extensions/docs/server2/mock_file_system.py b/chrome/common/extensions/docs/server2/mock_file_system.py
index e15f58c..52a84bd 100644
--- a/chrome/common/extensions/docs/server2/mock_file_system.py
+++ b/chrome/common/extensions/docs/server2/mock_file_system.py
@@ -42,16 +42,15 @@ class MockFileSystem(FileSystem):
from |_updates|, if any.
'''
self._read_count += 1
- future_result = self._file_system.Read(paths, skip_not_found=skip_not_found)
- def resolve():
+ def next(result):
self._read_resolve_count += 1
- result = future_result.Get()
for path in result.iterkeys():
update = self._GetMostRecentUpdate(path)
if update is not None:
result[path] = update
return result
- return Future(callback=resolve)
+ return self._file_system.Read(paths,
+ skip_not_found=skip_not_found).Then(next)
def Refresh(self):
return self._file_system.Refresh()
diff --git a/chrome/common/extensions/docs/server2/mock_file_system_test.py b/chrome/common/extensions/docs/server2/mock_file_system_test.py
index f2fe7d4..813751f 100755
--- a/chrome/common/extensions/docs/server2/mock_file_system_test.py
+++ b/chrome/common/extensions/docs/server2/mock_file_system_test.py
@@ -60,7 +60,7 @@ class MockFileSystemTest(unittest.TestCase):
future = fs.Read(['notfound.html', 'apps/'])
self.assertTrue(*fs.CheckAndReset(read_count=1))
self.assertRaises(FileNotFoundError, future.Get)
- self.assertTrue(*fs.CheckAndReset(read_resolve_count=1))
+ self.assertTrue(*fs.CheckAndReset(read_resolve_count=0))
fs.Stat('404.html')
fs.Stat('404.html')