25
25
26
26
from jinja2 import Environment , FileSystemLoader
27
27
28
- from traitlets import Unicode
28
+ from traitlets import Unicode , Any , Set , default
29
29
from traitlets .config import Application
30
30
31
31
from .handlers import init_handlers
@@ -74,83 +74,96 @@ def nrfoot():
74
74
75
75
class NBViewer (Application ):
76
76
77
- config_file = Unicode ('nbviewer_config.py' , help = "The config file to load" ). tag ( config = True )
77
+ name = Unicode ('nbviewer' )
78
78
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 )
83
80
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' ):
110
92
log .app_log .info ("Indexing notebooks" )
111
93
tcp_index = os .environ .get ('NBINDEX_PORT' )
112
94
index_url = tcp_index .split ('tcp://' )[1 ]
113
95
index_host , index_port = index_url .split (":" )
114
- indexer = ElasticSearch (index_host , index_port )
115
96
else :
116
97
log .app_log .info ("Not indexing notebooks" )
117
98
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 ]
119
131
if options .no_cache :
120
132
log .app_log .info ("Not using cache" )
121
133
cache = MockCache ()
122
134
elif pylibmc and memcache_urls :
135
+ # setup memcache
136
+ mc_pool = ThreadPoolExecutor (options .mc_threads )
123
137
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" , "" )
126
140
if username and password :
127
141
kwargs ['binary' ] = True
128
142
kwargs ['username' ] = username
129
143
kwargs ['password' ] = password
130
144
log .app_log .info ("Using SASL memcache" )
131
145
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 )
135
149
else :
136
150
log .app_log .info ("Using in-memory cache" )
137
151
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 )
154
167
env .filters ['markdown' ] = markdown .markdown
155
168
try :
156
169
git_data = git_info (here )
@@ -159,106 +172,137 @@ def init_tornado_application(self):
159
172
git_data = {}
160
173
else :
161
174
git_data ['msg' ] = escape (git_data ['msg' ])
162
-
163
-
175
+
164
176
if options .no_cache :
165
- # force jinja to recompile template every time
177
+ # force Jinja2 to recompile template every time
166
178
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 ):
193
185
fetch_kwargs = dict (connect_timeout = 10 ,)
194
186
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 )
198
188
log .app_log .info ("Using web proxy {proxy_host}:{proxy_port}."
199
189
"" .format (** fetch_kwargs ))
200
-
190
+
201
191
if options .no_check_certificate :
202
- fetch_kwargs .update (dict (validate_cert = False ))
203
-
192
+ fetch_kwargs .update (validate_cert = False )
204
193
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 )
208
242
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
215
253
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 ,
254
259
)
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
+
256
303
if options .localfiles :
257
304
log .app_log .warning ("Serving local notebooks in %s, this can be a security risk" , options .localfiles )
258
305
259
- # handle handlers
260
- handlers = init_handlers (formats , options .providers , base_url , options .localfiles )
261
-
262
306
# create the app
263
307
self .tornado_application = web .Application (handlers , debug = options .debug , ** settings )
264
308
0 commit comments