##// END OF EJS Templates
Add GET /nbconvert to list available formats
Thomas Kluyver -
Show More
@@ -1,85 +1,100 b''
1 import json
1 2 import os
2 3
3 4 from tornado import web
4 5
5 from ..base.handlers import IPythonHandler
6 from ..base.handlers import IPythonHandler, json_errors
6 7 from IPython.nbformat.current import to_notebook_json
7 8 from IPython.nbconvert.exporters.export import exporter_map
8 9 from IPython.utils import tz
9 10
10 11
11 12 def has_resource_files(resources):
12 13 output_files_dir = resources.get('output_files_dir', "")
13 14 return bool(os.path.isdir(output_files_dir) and \
14 15 os.listdir(output_files_dir))
15 16
16 17 class NbconvertFileHandler(IPythonHandler):
17 18
18 19 SUPPORTED_METHODS = ('GET',)
19 20
20 21 @web.authenticated
21 22 def get(self, format, path='', name=None):
22 23 exporter = exporter_map[format]()
23 24
24 25 path = path.strip('/')
25 26 os_path = self.notebook_manager.get_os_path(name, path)
26 27 if not os.path.isfile(os_path):
27 28 raise web.HTTPError(404, u'Notebook does not exist: %s' % name)
28 29
29 30 info = os.stat(os_path)
30 31 self.set_header('Last-Modified', tz.utcfromtimestamp(info.st_mtime))
31 32
32 33 # Force download if requested
33 34 if self.get_argument('download', 'false').lower() == 'true':
34 35 filename = os.path.splitext(name)[0] + '.' + exporter.file_extension
35 36 self.set_header('Content-Disposition',
36 37 'attachment; filename="%s"' % filename)
37 38
38 39 # MIME type
39 40 if exporter.output_mimetype:
40 41 self.set_header('Content-Type',
41 42 '%s; charset=utf-8' % exporter.output_mimetype)
42 43
43 44 output, resources = exporter.from_filename(os_path)
44 45
45 46 # TODO: If there are resources, combine them into a zip file
46 47 assert not has_resource_files(resources)
47 48
48 49 self.finish(output)
49 50
50 51 class NbconvertPostHandler(IPythonHandler):
51 52 SUPPORTED_METHODS = ('POST',)
52 53
53 54 @web.authenticated
54 55 def post(self, format):
55 56 exporter = exporter_map[format]()
56 57
57 58 model = self.get_json_body()
58 59 nbnode = to_notebook_json(model['content'])
59 60
60 61 # MIME type
61 62 if exporter.output_mimetype:
62 63 self.set_header('Content-Type',
63 64 '%s; charset=utf-8' % exporter.output_mimetype)
64 65
65 66 output, resources = exporter.from_notebook_node(nbnode)
66 67
67 68 # TODO: If there are resources, combine them into a zip file
68 69 assert not has_resource_files(resources)
69 70
70 71 self.finish(output)
71 72
73 class NbconvertRootHandler(IPythonHandler):
74 SUPPORTED_METHODS = ('GET',)
75
76 @web.authenticated
77 @json_errors
78 def get(self):
79 res = {}
80 for format, exporter in exporter_map.items():
81 res[format] = info = {}
82 info['output_mimetype'] = exporter.output_mimetype
83
84 self.finish(json.dumps(res))
85
72 86 #-----------------------------------------------------------------------------
73 87 # URL to handler mappings
74 88 #-----------------------------------------------------------------------------
75 89
76 90 _format_regex = r"(?P<format>\w+)"
77 91 _path_regex = r"(?P<path>(?:/.*)*)"
78 92 _notebook_name_regex = r"(?P<name>[^/]+\.ipynb)"
79 93 _notebook_path_regex = "%s/%s" % (_path_regex, _notebook_name_regex)
80 94
81 95 default_handlers = [
82 96 (r"/nbconvert/%s%s" % (_format_regex, _notebook_path_regex),
83 97 NbconvertFileHandler),
84 98 (r"/nbconvert/%s" % _format_regex, NbconvertPostHandler),
99 (r"/nbconvert", NbconvertRootHandler),
85 100 ] No newline at end of file
@@ -1,94 +1,104 b''
1 1 import io
2 2 import json
3 3 import os
4 4 from os.path import join as pjoin
5 5 import shutil
6 6
7 7 import requests
8 8
9 9 from IPython.html.utils import url_path_join
10 10 from IPython.html.tests.launchnotebook import NotebookTestBase, assert_http_error
11 11 from IPython.nbformat.current import (new_notebook, write, new_worksheet,
12 12 new_heading_cell, new_code_cell)
13 13
14 14 class NbconvertAPI(object):
15 15 """Wrapper for nbconvert API calls."""
16 16 def __init__(self, base_url):
17 17 self.base_url = base_url
18 18
19 19 def _req(self, verb, path, body=None, params=None):
20 20 response = requests.request(verb,
21 21 url_path_join(self.base_url, 'nbconvert', path),
22 22 data=body, params=params,
23 23 )
24 24 response.raise_for_status()
25 25 return response
26 26
27 27 def from_file(self, format, path, name, download=False):
28 28 return self._req('GET', url_path_join(format, path, name),
29 29 params={'download':download})
30 30
31 31 def from_post(self, format, nbmodel):
32 32 body = json.dumps(nbmodel)
33 33 return self._req('POST', format, body)
34 34
35 def list_formats(self):
36 return self._req('GET', '')
37
35 38 class APITest(NotebookTestBase):
36 39 def setUp(self):
37 40 nbdir = self.notebook_dir.name
38 41
39 42 if not os.path.isdir(pjoin(nbdir, 'foo')):
40 43 os.mkdir(pjoin(nbdir, 'foo'))
41 44
42 45 nb = new_notebook(name='testnb')
43 46
44 47 ws = new_worksheet()
45 48 nb.worksheets = [ws]
46 49 ws.cells.append(new_heading_cell(u'Created by test Β³'))
47 50 ws.cells.append(new_code_cell(input=u'print(2*6)'))
48 51
49 52 with io.open(pjoin(nbdir, 'foo', 'testnb.ipynb'), 'w',
50 53 encoding='utf-8') as f:
51 54 write(nb, f, format='ipynb')
52 55
53 56 self.nbconvert_api = NbconvertAPI(self.base_url())
54 57
55 58 def tearDown(self):
56 59 nbdir = self.notebook_dir.name
57 60
58 61 for dname in ['foo']:
59 62 shutil.rmtree(pjoin(nbdir, dname), ignore_errors=True)
60 63
61 64 def test_from_file(self):
62 65 r = self.nbconvert_api.from_file('html', 'foo', 'testnb.ipynb')
63 66 self.assertEqual(r.status_code, 200)
64 67 self.assertIn(u'text/html', r.headers['Content-Type'])
65 68 self.assertIn(u'Created by test', r.text)
66 69 self.assertIn(u'print', r.text)
67 70
68 71 r = self.nbconvert_api.from_file('python', 'foo', 'testnb.ipynb')
69 72 self.assertIn(u'text/x-python', r.headers['Content-Type'])
70 73 self.assertIn(u'print(2*6)', r.text)
71 74
72 75 def test_from_file_404(self):
73 76 with assert_http_error(404):
74 77 self.nbconvert_api.from_file('html', 'foo', 'thisdoesntexist.ipynb')
75 78
76 79 def test_from_file_download(self):
77 80 r = self.nbconvert_api.from_file('python', 'foo', 'testnb.ipynb', download=True)
78 81 content_disposition = r.headers['Content-Disposition']
79 82 self.assertIn('attachment', content_disposition)
80 83 self.assertIn('testnb.py', content_disposition)
81 84
82 85 def test_from_post(self):
83 86 nbmodel_url = url_path_join(self.base_url(), 'api/notebooks/foo/testnb.ipynb')
84 87 nbmodel = requests.get(nbmodel_url).json()
85 88
86 89 r = self.nbconvert_api.from_post(format='html', nbmodel=nbmodel)
87 90 self.assertEqual(r.status_code, 200)
88 91 self.assertIn(u'text/html', r.headers['Content-Type'])
89 92 self.assertIn(u'Created by test', r.text)
90 93 self.assertIn(u'print', r.text)
91 94
92 95 r = self.nbconvert_api.from_post(format='python', nbmodel=nbmodel)
93 96 self.assertIn(u'text/x-python', r.headers['Content-Type'])
94 self.assertIn(u'print(2*6)', r.text) No newline at end of file
97 self.assertIn(u'print(2*6)', r.text)
98
99 def test_list_formats(self):
100 formats = self.nbconvert_api.list_formats().json()
101 self.assertIsInstance(formats, dict)
102 self.assertIn('python', formats)
103 self.assertIn('html', formats)
104 self.assertEqual(formats['python']['output_mimetype'], 'text/x-python') No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now