Skip to content

Commit 28ff6f9

Browse files
committed
Enable ability to write default config file (in provisional manner before converting to traitlets from tornado.options)wq
1 parent 7124c89 commit 28ff6f9

File tree

2 files changed

+80
-3
lines changed

2 files changed

+80
-3
lines changed

nbviewer/app.py

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,6 @@ class NBViewer(Application):
7777

7878
name = Unicode('nbviewer')
7979

80-
config_file = Unicode('nbviewer_config.py', help="The config file to load").tag(config=True)
81-
8280
# Use this to insert custom configuration of handlers for NBViewer extensions
8381
handler_settings = Dict().tag(config=True)
8482

@@ -335,9 +333,47 @@ def init_tornado_application(self):
335333
# create the app
336334
self.tornado_application = web.Application(handlers, debug=options.debug, **settings)
337335

336+
# Mostly copied from JupyterHub because if it isn't broken then don't fix it
337+
def write_config(self):
338+
"""Write our default config to a .py config file"""
339+
config_file_dir = os.path.dirname(os.path.abspath(options.config_file))
340+
if not os.path.isdir(config_file_dir):
341+
self.exit("{} does not exist. The destination directory must exist before generating config file.".format(config_file_dir))
342+
if os.path.exists(options.config_file) and not options.answer_yes:
343+
answer = ''
344+
345+
def ask():
346+
prompt = "Overwrite %s with default config? [y/N]" % options.config_file
347+
try:
348+
return input(prompt).lower() or 'n'
349+
except KeyboardInterrupt:
350+
print('') # empty line
351+
return 'n'
352+
353+
answer = ask()
354+
while not answer.startswith(('y', 'n')):
355+
print("Please answer 'yes' or 'no'")
356+
answer = ask()
357+
if answer.startswith('n'):
358+
self.exit("Not overwriting config file with default.")
359+
360+
# Inherited method from traitlets.Application
361+
config_text = self.generate_config_file()
362+
if isinstance(config_text, bytes):
363+
config_text = config_text.decode('utf8')
364+
print("Writing default config to: %s" % options.config_file)
365+
with open(options.config_file, mode='w') as f:
366+
f.write(config_text)
367+
self.exit("Wrote default config file.")
368+
338369
def __init__(self, *args, **kwargs):
339370
super().__init__(*args, **kwargs)
340-
self.load_config_file(self.config_file)
371+
372+
if options.generate_config:
373+
self.write_config_file()
374+
375+
# Inherited method from traitlets.Application
376+
self.load_config_file(options.config_file)
341377
self.init_tornado_application()
342378

343379
def init_options():
@@ -353,14 +389,17 @@ def init_options():
353389
else:
354390
default_host, default_port = '0.0.0.0', 5000
355391

392+
define("answer_yes", default=False, help="Answer yes to any questions (e.g. confirm overwrite).", type=bool)
356393
define("base_url", default='/', help='URL base for the server')
357394
define("binder_base_url", default="https://mybinder.org/v2", help="URL base for binder notebook execution service", type=str)
358395
define("cache_expiry_max", default=2*60*60, help="maximum cache expiry (seconds)", type=int)
359396
define("cache_expiry_min", default=10*60, help="minimum cache expiry (seconds)", type=int)
397+
define("config_file", default='nbviewer_config.py', help="The config file to load", type=str)
360398
define("content_security_policy", default="connect-src 'none';", help="Content-Security-Policy header setting", type=str)
361399
define("debug", default=False, help="run in debug mode", type=bool)
362400
define("default_format", default="html", help="format to use for legacy / URLs", type=str)
363401
define("frontpage", default=FRONTPAGE_JSON, help="path to json file containing frontpage content", type=str)
402+
define("generate_config", default=False, help="Generate default config file and then stop.", type=bool)
364403
define("host", default=default_host, help="run on the given interface", type=str)
365404
define("ipywidgets_base_url", default="https://unpkg.com/", help="URL base for ipywidgets JS package", type=str)
366405
define("jupyter_js_widgets_version", default="*", help="Version specifier for jupyter-js-widgets JS package", type=str)

nbviewer/tests/test_app.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import os
2+
import sys
3+
4+
from tempfile import NamedTemporaryFile
5+
from subprocess import Popen
6+
7+
# Also copied mostly from JupyterHub since again -- if not broken, don't fix.
8+
def test_generate_config():
9+
with NamedTemporaryFile(prefix='nbviewer_config', suffix='.py') as tf:
10+
cfg_file = tf.name
11+
with open(cfg_file, 'w') as f:
12+
f.write("c.A = 5")
13+
p = Popen(
14+
[sys.executable, '-m', 'nbviewer', '--generate-config', '--config-file={}'.format(cfg_file)],
15+
stdout=PIPE,
16+
stdin=PIPE,
17+
)
18+
out, _ = p.communicate(b'n')
19+
out = out.decode('utf8', 'replace')
20+
assert os.path.exists(cfg_file)
21+
with open(cfg_file) as f:
22+
cfg_text = f.read()
23+
assert cfg_text == 'c.A = 5'
24+
25+
p = Popen(
26+
[sys.executable, '-m', 'nbviewer', '--generate-config', '--config-file={}'.format(cfg_file)],
27+
stdout=PIPE,
28+
stdin=PIPE,
29+
)
30+
out, _ = p.communicate(b'x\ny')
31+
out = out.decode('utf8', 'replace')
32+
assert os.path.exists(cfg_file)
33+
with open(cfg_file) as f:
34+
cfg_text = f.read()
35+
os.remove(cfg_file)
36+
assert cfg_file in out
37+
assert 'NBViewer.name' in cfg_text
38+
assert 'NBViewer.static_path' in cfg_text

0 commit comments

Comments
 (0)