Skip to content

Commit e8bf3bf

Browse files
authored
Merge pull request #846 from krinsman/step6
Step 6
2 parents fffd462 + cb768ff commit e8bf3bf

File tree

14 files changed

+201
-281
lines changed

14 files changed

+201
-281
lines changed

nbviewer/app.py

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
1919

2020
from tornado import web, httpserver, ioloop, log
21-
from tornado.httpclient import AsyncHTTPClient
2221

2322
import tornado.options
2423
from tornado.options import define, options
@@ -30,15 +29,10 @@
3029

3130
from .handlers import init_handlers
3231
from .cache import DummyAsyncCache, AsyncMultipartMemcache, MockCache, pylibmc
33-
from .index import NoSearch, ElasticSearch
32+
from .index import NoSearch
3433
from .formats import configure_formats
35-
3634
from .providers import default_providers, default_rewrites
37-
38-
try:
39-
from .providers.url.client import NBViewerCurlAsyncHTTPClient as HTTPClientClass
40-
except ImportError:
41-
from .providers.url.client import NBViewerSimpleAsyncHTTPClient as HTTPClientClass
35+
from .providers.url.client import NBViewerAsyncHTTPClient as HTTPClientClass
4236
from .ratelimit import RateLimiter
4337

4438
from .log import log_request
@@ -93,6 +87,13 @@ class NBViewer(Application):
9387
gist_handler = Unicode(default_value="nbviewer.providers.gist.handlers.GistHandler", help="The Tornado handler to use for viewing notebooks stored as GitHub Gists").tag(config=True)
9488
user_gists_handler = Unicode(default_value="nbviewer.providers.gist.handlers.UserGistsHandler", help="The Tornado handler to use for viewing directory containing all of a user's Gists").tag(config=True)
9589

90+
client = Any().tag(config=True)
91+
@default('client')
92+
def _default_client(self):
93+
client = HTTPClientClass()
94+
client.cache = self.cache
95+
return client
96+
9697
index = Any().tag(config=True)
9798
@default('index')
9899
def _load_index(self):
@@ -160,15 +161,6 @@ def cache(self):
160161

161162
return cache
162163

163-
# for some reason this needs to be a computed property,
164-
# and not a traitlets Any(), otherwise nbviewer won't run
165-
@cached_property
166-
def client(self):
167-
AsyncHTTPClient.configure(HTTPClientClass)
168-
client = AsyncHTTPClient()
169-
client.cache = self.cache
170-
return client
171-
172164
@cached_property
173165
def env(self):
174166
env = Environment(loader=FileSystemLoader(self.template_paths), autoescape=True)

nbviewer/cache.py

Lines changed: 38 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,8 @@
99
from time import monotonic
1010

1111
from concurrent.futures import ThreadPoolExecutor
12-
from tornado.concurrent import Future
12+
from asyncio import Future
1313

14-
from tornado import gen
1514
from tornado.log import app_log
1615

1716
try:
@@ -28,25 +27,25 @@ class MockCache(object):
2827
def __init__(self, *args, **kwargs):
2928
pass
3029

31-
def get(self, key):
30+
async def get(self, key):
3231
f = Future()
3332
f.set_result(None)
34-
return f
33+
return await f
3534

36-
def set(self, key, value, *args, **kwargs):
35+
async def set(self, key, value, *args, **kwargs):
3736
f = Future()
3837
f.set_result(None)
39-
return f
38+
return await f
4039

41-
def add(self, key, value, *args, **kwargs):
40+
async def add(self, key, value, *args, **kwargs):
4241
f = Future()
4342
f.set_result(True)
44-
return f
43+
return await f
4544

46-
def incr(self, key):
45+
async def incr(self, key):
4746
f = Future()
4847
f.set_result(None)
49-
return f
48+
return await f
5049

5150
class DummyAsyncCache(object):
5251
"""Dummy Async Cache. Just stores things in a dict of fixed size."""
@@ -55,10 +54,10 @@ def __init__(self, limit=10):
5554
self._cache_order = []
5655
self.limit = limit
5756

58-
def get(self, key):
57+
async def get(self, key):
5958
f = Future()
6059
f.set_result(self._get(key))
61-
return f
60+
return await f
6261

6362
def _get(self, key):
6463
value, deadline = self._cache.get(key, (None, None))
@@ -68,7 +67,7 @@ def _get(self, key):
6867
else:
6968
return value
7069

71-
def set(self, key, value, expires=0):
70+
async def set(self, key, value, expires=0):
7271
if key in self._cache and self._cache_order[-1] != key:
7372
idx = self._cache_order.index(key)
7473
del self._cache_order[idx]
@@ -87,18 +86,18 @@ def set(self, key, value, expires=0):
8786
self._cache[key] = (value, deadline)
8887
f = Future()
8988
f.set_result(True)
90-
return f
89+
return await f
9190

92-
def add(self, key, value, expires=0):
91+
async def add(self, key, value, expires=0):
9392
f = Future()
9493
if self._get(key) is not None:
9594
f.set_result(False)
9695
else:
97-
self.set(key, value, expires)
96+
await self.set(key, value, expires)
9897
f.set_result(True)
99-
return f
98+
return await f
10099

101-
def incr(self, key):
100+
async def incr(self, key):
102101
f = Future()
103102
if self._get(key) is not None:
104103
value, deadline = self._cache[key]
@@ -107,7 +106,7 @@ def incr(self, key):
107106
else:
108107
value = None
109108
f.set_result(value)
110-
return f
109+
return await f
111110

112111
class AsyncMemcache(object):
113112
"""Wrap pylibmc.Client to run in a background thread
@@ -119,8 +118,12 @@ def __init__(self, *args, **kwargs):
119118

120119
self.mc = pylibmc.Client(*args, **kwargs)
121120
self.mc_pool = pylibmc.ThreadMappedPool(self.mc)
122-
123-
def _call_in_thread(self, method_name, *args, **kwargs):
121+
122+
self.loop = asyncio.get_event_loop()
123+
124+
async def _call_in_thread(self, method_name, *args, **kwargs):
125+
# https://stackoverflow.com/questions/34376814/await-future-from-executor-future-cant-be-used-in-await-expression
126+
124127
key = args[0]
125128
if 'multi' in method_name:
126129
key = sorted(key)[0].decode('ascii') + '[%i]' % len(key)
@@ -130,19 +133,19 @@ def f():
130133
with self.mc_pool.reserve() as mc:
131134
meth = getattr(mc, method_name)
132135
return meth(*args, **kwargs)
133-
return self.pool.submit(f)
136+
return await self.loop.run_in_executor(self.pool, f)
134137

135-
def get(self, *args, **kwargs):
136-
return self._call_in_thread('get', *args, **kwargs)
138+
async def get(self, *args, **kwargs):
139+
return await self._call_in_thread('get', *args, **kwargs)
137140

138-
def set(self, *args, **kwargs):
139-
return self._call_in_thread('set', *args, **kwargs)
141+
async def set(self, *args, **kwargs):
142+
return await self._call_in_thread('set', *args, **kwargs)
140143

141-
def add(self, *args, **kwargs):
142-
return self._call_in_thread('add', *args, **kwargs)
144+
async def add(self, *args, **kwargs):
145+
return await self._call_in_thread('add', *args, **kwargs)
143146

144-
def incr(self, *args, **kwargs):
145-
return self._call_in_thread('incr', *args, **kwargs)
147+
async def incr(self, *args, **kwargs):
148+
return await self._call_in_thread('incr', *args, **kwargs)
146149

147150
class AsyncMultipartMemcache(AsyncMemcache):
148151
"""subclass of AsyncMemcache that splits large files into multiple chunks
@@ -154,11 +157,10 @@ def __init__(self, *args, **kwargs):
154157
self.max_chunks = kwargs.pop('max_chunks', 16)
155158
super(AsyncMultipartMemcache, self).__init__(*args, **kwargs)
156159

157-
@gen.coroutine
158-
def get(self, key, *args, **kwargs):
160+
async def get(self, key, *args, **kwargs):
159161
keys = [('%s.%i' % (key, idx)).encode()
160162
for idx in range(self.max_chunks)]
161-
values = yield self._call_in_thread('get_multi', keys, *args, **kwargs)
163+
values = await self._call_in_thread('get_multi', keys, *args, **kwargs)
162164
parts = []
163165
for key in keys:
164166
if key not in values:
@@ -171,10 +173,9 @@ def get(self, key, *args, **kwargs):
171173
except zlib.error as e:
172174
app_log.error("zlib decompression of %s failed: %s", key, e)
173175
else:
174-
raise gen.Return(result)
176+
return result
175177

176-
@gen.coroutine
177-
def set(self, key, value, *args, **kwargs):
178+
async def set(self, key, value, *args, **kwargs):
178179
chunk_size = self.chunk_size
179180
compressed = zlib.compress(value)
180181
offsets = range(0, len(compressed), chunk_size)
@@ -186,5 +187,5 @@ def set(self, key, value, *args, **kwargs):
186187
values[('%s.%i' % (key, idx)).encode()] = compressed[
187188
offset:offset + chunk_size
188189
]
189-
return self._call_in_thread('set_multi', values, *args, **kwargs)
190+
return await self._call_in_thread('set_multi', values, *args, **kwargs)
190191

nbviewer/formats.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
# the file COPYING, distributed as part of this software.
66
#-----------------------------------------------------------------------------
77

8-
import re
98
import os
109

1110
from nbconvert.exporters.export import exporter_map

0 commit comments

Comments
 (0)