Skip to content

Commit b3964a9

Browse files
committed
Reorganize init_tornado_application by creating class attributes and properties
1 parent f1c8cb1 commit b3964a9

File tree

1 file changed

+185
-141
lines changed

1 file changed

+185
-141
lines changed

nbviewer/app.py

Lines changed: 185 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525

2626
from jinja2 import Environment, FileSystemLoader
2727

28-
from traitlets import Unicode
28+
from traitlets import Unicode, Any, Set, default
2929
from traitlets.config import Application
3030

3131
from .handlers import init_handlers
@@ -74,83 +74,96 @@ def nrfoot():
7474

7575
class NBViewer(Application):
7676

77-
config_file = Unicode('nbviewer_config.py', help="The config file to load").tag(config=True)
77+
name = Unicode('nbviewer')
7878

79-
def init_tornado_application(self):
80-
# NBConvert config
81-
self.config.NbconvertApp.fileext = 'html'
82-
self.config.CSSHTMLHeaderTransformer.enabled = False
79+
config_file = Unicode('nbviewer_config.py', help="The config file to load").tag(config=True)
8380

84-
# DEBUG env implies both autoreload and log-level
85-
if os.environ.get("DEBUG"):
86-
options.debug = True
87-
logging.getLogger().setLevel(logging.DEBUG)
88-
89-
# setup memcache
90-
mc_pool = ThreadPoolExecutor(options.mc_threads)
91-
92-
# setup formats
93-
formats = configure_formats(options, self.config, log.app_log)
94-
95-
if options.processes:
96-
pool = ProcessPoolExecutor(options.processes)
97-
else:
98-
pool = ThreadPoolExecutor(options.threads)
99-
100-
memcache_urls = os.environ.get('MEMCACHIER_SERVERS',
101-
os.environ.get('MEMCACHE_SERVERS')
102-
)
103-
104-
# Handle linked Docker containers
105-
if(os.environ.get('NBCACHE_PORT')):
106-
tcp_memcache = os.environ.get('NBCACHE_PORT')
107-
memcache_urls = tcp_memcache.split('tcp://')[1]
108-
109-
if(os.environ.get('NBINDEX_PORT')):
81+
url_handler = Unicode(default_value="nbviewer.providers.url.handlers.URLHandler", help="The Tornado handler to use for viewing notebooks accessed via URL").tag(config=True)
82+
local_handler = Unicode(default_value="nbviewer.providers.local.handlers.LocalFileHandler", help="The Tornado handler to use for viewing notebooks found on a local filesystem").tag(config=True)
83+
github_blob_handler = Unicode(default_value="nbviewer.providers.github.handlers.GitHubBlobHandler", help="The Tornado handler to use for viewing notebooks stored as blobs on GitHub").tag(config=True)
84+
github_tree_handler = Unicode(default_value="nbviewer.providers.github.handlers.GitHubTreeHandler", help="The Tornado handler to use for viewing directory trees on GitHub").tag(config=True)
85+
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)
86+
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)
87+
88+
index = Any().tag(config=True)
89+
@default('index')
90+
def _load_index(self):
91+
if os.environ.get('NBINDEX_PORT'):
11092
log.app_log.info("Indexing notebooks")
11193
tcp_index = os.environ.get('NBINDEX_PORT')
11294
index_url = tcp_index.split('tcp://')[1]
11395
index_host, index_port = index_url.split(":")
114-
indexer = ElasticSearch(index_host, index_port)
11596
else:
11697
log.app_log.info("Not indexing notebooks")
11798
indexer = NoSearch()
118-
99+
return indexer
100+
101+
# cache frontpage links for the maximum allowed time
102+
max_cache_uris = Set().tag(config=True)
103+
@default('max_cache_uris')
104+
def _load_max_cache_uris(self):
105+
max_cache_uris = {''}
106+
for section in self.frontpage_setup['sections']:
107+
for link in section['links']:
108+
max_cache_uris.add('/' + link['target'])
109+
return max_cache_uris
110+
111+
static_path = Unicode(default_value=pjoin(here, 'static')).tag(config=True)
112+
113+
static_url_prefix = Unicode().tag(config=True)
114+
@default('static_url_prefix')
115+
def _load_static_url_prefix(self):
116+
return url_path_join(self.base_url, '/static/')
117+
118+
@property
119+
def base_url(self):
120+
# prefer the JupyterHub defined service prefix over the CLI
121+
base_url = os.getenv("JUPYTERHUB_SERVICE_PREFIX", options.base_url)
122+
return base_url
123+
124+
@property
125+
def cache(self):
126+
memcache_urls = os.environ.get('MEMCACHIER_SERVERS', os.environ.get('MEMCACHE_SERVERS'))
127+
# Handle linked Docker containers
128+
if os.environ.get('NBCACHE_PORT'):
129+
tcp_memcache = os.environ.get('NBCACHE_PORT')
130+
memcache_urls = tcp_memcache.split('tcp://')[1]
119131
if options.no_cache:
120132
log.app_log.info("Not using cache")
121133
cache = MockCache()
122134
elif pylibmc and memcache_urls:
135+
# setup memcache
136+
mc_pool = ThreadPoolExecutor(options.mc_threads)
123137
kwargs = dict(pool=mc_pool)
124-
username = os.environ.get('MEMCACHIER_USERNAME', '')
125-
password = os.environ.get('MEMCACHIER_PASSWORD', '')
138+
username = os.environ.get("MEMCACHIER_USERNAME", "")
139+
password = os.environ.get("MEMCACHIER_PASSWORD", "")
126140
if username and password:
127141
kwargs['binary'] = True
128142
kwargs['username'] = username
129143
kwargs['password'] = password
130144
log.app_log.info("Using SASL memcache")
131145
else:
132-
log.app_log.info("Using plain memecache")
133-
134-
cache = AsyncMultipartMemcache(memcache_urls.split(','), **kwargs)
146+
log.app_log.info("Using plain memcache")
147+
148+
cache = AsyncMultiPartMemcache(memcache_urls.split(','), **kwargs)
135149
else:
136150
log.app_log.info("Using in-memory cache")
137151
cache = DummyAsyncCache()
138-
139-
# setup tornado handlers and settings
140-
141-
template_paths = pjoin(here, 'templates')
142-
143-
if options.template_path is not None:
144-
log.app_log.info("Using custom template path {}".format(
145-
options.template_path)
146-
)
147-
template_paths = [options.template_path, template_paths]
148-
149-
static_path = pjoin(here, 'static')
150-
env = Environment(
151-
loader=FileSystemLoader(template_paths),
152-
autoescape=True
153-
)
152+
153+
return cache
154+
155+
# for some reason this needs to be a computed property,
156+
# and not a traitlets Any(), otherwise nbviewer won't run
157+
@property
158+
def client(self):
159+
AsyncHTTPClient.configure(HTTPClientClass)
160+
client = AsyncHTTPClient()
161+
client.cache = self.cache
162+
return client
163+
164+
@property
165+
def env(self):
166+
env = Environment(loader=FileSystemLoader(self.template_paths), autoescape=True)
154167
env.filters['markdown'] = markdown.markdown
155168
try:
156169
git_data = git_info(here)
@@ -159,106 +172,137 @@ def init_tornado_application(self):
159172
git_data = {}
160173
else:
161174
git_data['msg'] = escape(git_data['msg'])
162-
163-
175+
164176
if options.no_cache:
165-
# force jinja to recompile template every time
177+
# force Jinja2 to recompile template every time
166178
env.globals.update(cache_size=0)
167-
env.globals.update(nrhead=nrhead, nrfoot=nrfoot, git_data=git_data,
168-
jupyter_info=jupyter_info(), len=len,
169-
)
170-
AsyncHTTPClient.configure(HTTPClientClass)
171-
client = AsyncHTTPClient()
172-
client.cache = cache
173-
174-
# load frontpage sections
175-
with io.open(options.frontpage, 'r') as f:
176-
frontpage_setup = json.load(f)
177-
# check if the json has a 'sections' field, otherwise assume it is
178-
# just a list of sessions, and provide the defaults for the other
179-
# fields
180-
if 'sections' not in frontpage_setup:
181-
frontpage_setup = {'title': 'nbviewer',
182-
'subtitle':
183-
'A simple way to share Jupyter Notebooks',
184-
'show_input': True,
185-
'sections': frontpage_setup}
186-
187-
# cache frontpage links for the maximum allowed time
188-
max_cache_uris = {''}
189-
for section in frontpage_setup['sections']:
190-
for link in section['links']:
191-
max_cache_uris.add('/' + link['target'])
192-
179+
env.globals.update(nrhead=nrhead, nrfoot=nrfoot, git_data=git_data, jupyter_info=jupyter_info(), len=len)
180+
181+
return env
182+
183+
@property
184+
def fetch_kwargs(self):
193185
fetch_kwargs = dict(connect_timeout=10,)
194186
if options.proxy_host:
195-
fetch_kwargs.update(dict(proxy_host=options.proxy_host,
196-
proxy_port=options.proxy_port))
197-
187+
fetch_kwargs.update(proxy_host=options.proxy_host, proxy_port=options.proxy_port)
198188
log.app_log.info("Using web proxy {proxy_host}:{proxy_port}."
199189
"".format(**fetch_kwargs))
200-
190+
201191
if options.no_check_certificate:
202-
fetch_kwargs.update(dict(validate_cert=False))
203-
192+
fetch_kwargs.update(validate_cert=False)
204193
log.app_log.info("Not validating SSL certificates")
205-
206-
# prefer the jhub defined service prefix over the CLI
207-
base_url = os.getenv('JUPYTERHUB_SERVICE_PREFIX', options.base_url)
194+
195+
return fetch_kwargs
196+
197+
@property
198+
def formats(self):
199+
formats = configure_formats(options, self.config, log.app_log)
200+
return formats
201+
202+
# load frontpage sections
203+
@property
204+
def frontpage_setup(self):
205+
with io.open(options.frontpage, 'r') as f:
206+
frontpage_setup = json.load(f)
207+
# check if the JSON has a 'sections' field, otherwise assume it is just a list of sessions,
208+
# and provide the defaults of the other fields
209+
if 'sections' not in frontpage_setup:
210+
frontpage_setup = {
211+
'title':'nbviewer', 'subtitle':'A simple way to share Jupyter notebooks',
212+
'show_input':True, 'sections':frontpage_setup
213+
}
214+
return frontpage_setup
215+
216+
@property
217+
def pool(self):
218+
if options.processes:
219+
pool = ProcessPoolExecutor(options.processes)
220+
else:
221+
pool = ThreadPoolExecutor(options.threads)
222+
return pool
223+
224+
@property
225+
def rate_limiter(self):
226+
rate_limiter = RateLimiter(limit=options.rate_limit, interval=options.rate_limit_interval, cache=self.cache)
227+
return rate_limiter
228+
229+
@property
230+
def template_paths(self):
231+
template_paths = pjoin(here, 'templates')
232+
if options.template_path is not None:
233+
log.app_log.info("Using custom template path {}".format(options.template_path))
234+
template_paths = [options.template_path, template_paths]
235+
236+
return template_paths
237+
238+
239+
def init_tornado_application(self):
240+
# handle handlers
241+
handlers = init_handlers(self.formats, options.providers, self.base_url, options.localfiles)
208242

209-
rate_limiter = RateLimiter(
210-
limit=options.rate_limit,
211-
interval=options.rate_limit_interval,
212-
cache=cache,
213-
)
214-
243+
# NBConvert config
244+
self.config.NbconvertApp.fileext = 'html'
245+
self.config.CSSHTMLHeaderTransformer.enabled = False
246+
247+
# DEBUG env implies both autoreload and log-level
248+
if os.environ.get("DEBUG"):
249+
options.debug = True
250+
logging.getLogger().setLevel(logging.DEBUG)
251+
252+
# input traitlets to settings
215253
settings = dict(
216-
log_function=log_request,
217-
jinja2_env=env,
218-
static_path=static_path,
219-
static_url_prefix=url_path_join(base_url, '/static/'),
220-
client=client,
221-
formats=formats,
222-
default_format=options.default_format,
223-
providers=options.providers,
224-
provider_rewrites=options.provider_rewrites,
225-
config=self.config,
226-
index=indexer,
227-
cache=cache,
228-
cache_expiry_min=options.cache_expiry_min,
229-
cache_expiry_max=options.cache_expiry_max,
230-
max_cache_uris=max_cache_uris,
231-
frontpage_setup=frontpage_setup,
232-
pool=pool,
233-
gzip=True,
234-
render_timeout=options.render_timeout,
235-
localfile_path=os.path.abspath(options.localfiles),
236-
localfile_follow_symlinks=options.localfile_follow_symlinks,
237-
localfile_any_user=options.localfile_any_user,
238-
fetch_kwargs=fetch_kwargs,
239-
mathjax_url=options.mathjax_url,
240-
rate_limiter=rate_limiter,
241-
statsd_host=options.statsd_host,
242-
statsd_port=options.statsd_port,
243-
statsd_prefix=options.statsd_prefix,
244-
base_url=base_url,
245-
google_analytics_id=os.getenv('GOOGLE_ANALYTICS_ID'),
246-
hub_api_token=os.getenv('JUPYTERHUB_API_TOKEN'),
247-
hub_api_url=os.getenv('JUPYTERHUB_API_URL'),
248-
hub_base_url=os.getenv('JUPYTERHUB_BASE_URL'),
249-
ipywidgets_base_url=options.ipywidgets_base_url,
250-
jupyter_widgets_html_manager_version=options.jupyter_widgets_html_manager_version,
251-
jupyter_js_widgets_version=options.jupyter_js_widgets_version,
252-
content_security_policy=options.content_security_policy,
253-
binder_base_url=options.binder_base_url,
254+
config=self.config,
255+
index=self.index,
256+
max_cache_uris=self.max_cache_uris,
257+
static_path=self.static_path,
258+
static_url_prefix=self.static_url_prefix,
254259
)
255-
260+
# input computed properties to settings
261+
settings.update(
262+
base_url=self.base_url,
263+
cache=self.cache,
264+
client=self.client,
265+
fetch_kwargs=self.fetch_kwargs,
266+
formats=self.formats,
267+
frontpage_setup=self.frontpage_setup,
268+
jinja2_env=self.env,
269+
pool=self.pool,
270+
rate_limiter=self.rate_limiter,
271+
)
272+
# input settings from CLI options
273+
settings.update(
274+
binder_base_url=options.binder_base_url,
275+
cache_expiry_max=options.cache_expiry_max,
276+
cache_expiry_min=options.cache_expiry_min,
277+
content_security_policy=options.content_security_policy,
278+
default_format=options.default_format,
279+
ipywidgets_base_url=options.ipywidgets_base_url,
280+
jupyter_js_widgets_version=options.jupyter_js_widgets_version,
281+
jupyter_widgets_html_manager_version=options.jupyter_widgets_html_manager_version,
282+
localfile_any_user=options.localfile_any_user,
283+
localfile_follow_symlinks=options.localfile_follow_symlinks,
284+
localfile_path=os.path.abspath(options.localfiles),
285+
mathjax_url=options.mathjax_url,
286+
provider_rewrites=options.provider_rewrites,
287+
providers=options.providers,
288+
render_timeout=options.render_timeout,
289+
statsd_host=options.statsd_host,
290+
statsd_port=options.statsd_port,
291+
statsd_prefix=options.statsd_prefix,
292+
)
293+
# additional settings
294+
settings.update(
295+
google_analytics_id=os.getenv('GOOGLE_ANALYTICS_ID'),
296+
gzip=True,
297+
hub_api_token=os.getenv('JUPYTERHUB_API_TOKEN'),
298+
hub_api_url=os.getenv('JUPYTERHUB_API_URL'),
299+
hub_base_url=os.getenv('JUPYTERHUB_BASE_URL'),
300+
log_function=log_request,
301+
)
302+
256303
if options.localfiles:
257304
log.app_log.warning("Serving local notebooks in %s, this can be a security risk", options.localfiles)
258305

259-
# handle handlers
260-
handlers = init_handlers(formats, options.providers, base_url, options.localfiles)
261-
262306
# create the app
263307
self.tornado_application = web.Application(handlers, debug=options.debug, **settings)
264308

0 commit comments

Comments
 (0)