##// END OF EJS Templates
Backport PR #5535: fix n^2 performance issue in coalesce_streams preprocessor...
MinRK -
Show More
@@ -1,129 +1,129 b''
1 # coding: utf-8
1 # coding: utf-8
2 import base64
2 import base64
3 import io
3 import io
4 import json
4 import json
5 import os
5 import os
6 from os.path import join as pjoin
6 from os.path import join as pjoin
7 import shutil
7 import shutil
8
8
9 import requests
9 import requests
10
10
11 from IPython.html.utils import url_path_join
11 from IPython.html.utils import url_path_join
12 from IPython.html.tests.launchnotebook import NotebookTestBase, assert_http_error
12 from IPython.html.tests.launchnotebook import NotebookTestBase, assert_http_error
13 from IPython.nbformat.current import (new_notebook, write, new_worksheet,
13 from IPython.nbformat.current import (new_notebook, write, new_worksheet,
14 new_heading_cell, new_code_cell,
14 new_heading_cell, new_code_cell,
15 new_output)
15 new_output)
16
16
17 from IPython.testing.decorators import onlyif_cmds_exist
17 from IPython.testing.decorators import onlyif_cmds_exist
18
18
19
19
20 class NbconvertAPI(object):
20 class NbconvertAPI(object):
21 """Wrapper for nbconvert API calls."""
21 """Wrapper for nbconvert API calls."""
22 def __init__(self, base_url):
22 def __init__(self, base_url):
23 self.base_url = base_url
23 self.base_url = base_url
24
24
25 def _req(self, verb, path, body=None, params=None):
25 def _req(self, verb, path, body=None, params=None):
26 response = requests.request(verb,
26 response = requests.request(verb,
27 url_path_join(self.base_url, 'nbconvert', path),
27 url_path_join(self.base_url, 'nbconvert', path),
28 data=body, params=params,
28 data=body, params=params,
29 )
29 )
30 response.raise_for_status()
30 response.raise_for_status()
31 return response
31 return response
32
32
33 def from_file(self, format, path, name, download=False):
33 def from_file(self, format, path, name, download=False):
34 return self._req('GET', url_path_join(format, path, name),
34 return self._req('GET', url_path_join(format, path, name),
35 params={'download':download})
35 params={'download':download})
36
36
37 def from_post(self, format, nbmodel):
37 def from_post(self, format, nbmodel):
38 body = json.dumps(nbmodel)
38 body = json.dumps(nbmodel)
39 return self._req('POST', format, body)
39 return self._req('POST', format, body)
40
40
41 def list_formats(self):
41 def list_formats(self):
42 return self._req('GET', '')
42 return self._req('GET', '')
43
43
44 png_green_pixel = base64.encodestring(b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00'
44 png_green_pixel = base64.encodestring(b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00'
45 b'\x00\x00\x01\x00\x00x00\x01\x08\x02\x00\x00\x00\x90wS\xde\x00\x00\x00\x0cIDAT'
45 b'\x00\x00\x01\x00\x00x00\x01\x08\x02\x00\x00\x00\x90wS\xde\x00\x00\x00\x0cIDAT'
46 b'\x08\xd7c\x90\xfb\xcf\x00\x00\x02\\\x01\x1e.~d\x87\x00\x00\x00\x00IEND\xaeB`\x82')
46 b'\x08\xd7c\x90\xfb\xcf\x00\x00\x02\\\x01\x1e.~d\x87\x00\x00\x00\x00IEND\xaeB`\x82')
47
47
48 class APITest(NotebookTestBase):
48 class APITest(NotebookTestBase):
49 def setUp(self):
49 def setUp(self):
50 nbdir = self.notebook_dir.name
50 nbdir = self.notebook_dir.name
51
51
52 if not os.path.isdir(pjoin(nbdir, 'foo')):
52 if not os.path.isdir(pjoin(nbdir, 'foo')):
53 os.mkdir(pjoin(nbdir, 'foo'))
53 os.mkdir(pjoin(nbdir, 'foo'))
54
54
55 nb = new_notebook(name='testnb')
55 nb = new_notebook(name='testnb')
56
56
57 ws = new_worksheet()
57 ws = new_worksheet()
58 nb.worksheets = [ws]
58 nb.worksheets = [ws]
59 ws.cells.append(new_heading_cell(u'Created by test Β³'))
59 ws.cells.append(new_heading_cell(u'Created by test Β³'))
60 cc1 = new_code_cell(input=u'print(2*6)')
60 cc1 = new_code_cell(input=u'print(2*6)')
61 cc1.outputs.append(new_output(output_text=u'12'))
61 cc1.outputs.append(new_output(output_text=u'12', output_type='stream'))
62 cc1.outputs.append(new_output(output_png=png_green_pixel, output_type='pyout'))
62 cc1.outputs.append(new_output(output_png=png_green_pixel, output_type='pyout'))
63 ws.cells.append(cc1)
63 ws.cells.append(cc1)
64
64
65 with io.open(pjoin(nbdir, 'foo', 'testnb.ipynb'), 'w',
65 with io.open(pjoin(nbdir, 'foo', 'testnb.ipynb'), 'w',
66 encoding='utf-8') as f:
66 encoding='utf-8') as f:
67 write(nb, f, format='ipynb')
67 write(nb, f, format='ipynb')
68
68
69 self.nbconvert_api = NbconvertAPI(self.base_url())
69 self.nbconvert_api = NbconvertAPI(self.base_url())
70
70
71 def tearDown(self):
71 def tearDown(self):
72 nbdir = self.notebook_dir.name
72 nbdir = self.notebook_dir.name
73
73
74 for dname in ['foo']:
74 for dname in ['foo']:
75 shutil.rmtree(pjoin(nbdir, dname), ignore_errors=True)
75 shutil.rmtree(pjoin(nbdir, dname), ignore_errors=True)
76
76
77 @onlyif_cmds_exist('pandoc')
77 @onlyif_cmds_exist('pandoc')
78 def test_from_file(self):
78 def test_from_file(self):
79 r = self.nbconvert_api.from_file('html', 'foo', 'testnb.ipynb')
79 r = self.nbconvert_api.from_file('html', 'foo', 'testnb.ipynb')
80 self.assertEqual(r.status_code, 200)
80 self.assertEqual(r.status_code, 200)
81 self.assertIn(u'text/html', r.headers['Content-Type'])
81 self.assertIn(u'text/html', r.headers['Content-Type'])
82 self.assertIn(u'Created by test', r.text)
82 self.assertIn(u'Created by test', r.text)
83 self.assertIn(u'print', r.text)
83 self.assertIn(u'print', r.text)
84
84
85 r = self.nbconvert_api.from_file('python', 'foo', 'testnb.ipynb')
85 r = self.nbconvert_api.from_file('python', 'foo', 'testnb.ipynb')
86 self.assertIn(u'text/x-python', r.headers['Content-Type'])
86 self.assertIn(u'text/x-python', r.headers['Content-Type'])
87 self.assertIn(u'print(2*6)', r.text)
87 self.assertIn(u'print(2*6)', r.text)
88
88
89 @onlyif_cmds_exist('pandoc')
89 @onlyif_cmds_exist('pandoc')
90 def test_from_file_404(self):
90 def test_from_file_404(self):
91 with assert_http_error(404):
91 with assert_http_error(404):
92 self.nbconvert_api.from_file('html', 'foo', 'thisdoesntexist.ipynb')
92 self.nbconvert_api.from_file('html', 'foo', 'thisdoesntexist.ipynb')
93
93
94 @onlyif_cmds_exist('pandoc')
94 @onlyif_cmds_exist('pandoc')
95 def test_from_file_download(self):
95 def test_from_file_download(self):
96 r = self.nbconvert_api.from_file('python', 'foo', 'testnb.ipynb', download=True)
96 r = self.nbconvert_api.from_file('python', 'foo', 'testnb.ipynb', download=True)
97 content_disposition = r.headers['Content-Disposition']
97 content_disposition = r.headers['Content-Disposition']
98 self.assertIn('attachment', content_disposition)
98 self.assertIn('attachment', content_disposition)
99 self.assertIn('testnb.py', content_disposition)
99 self.assertIn('testnb.py', content_disposition)
100
100
101 @onlyif_cmds_exist('pandoc')
101 @onlyif_cmds_exist('pandoc')
102 def test_from_file_zip(self):
102 def test_from_file_zip(self):
103 r = self.nbconvert_api.from_file('latex', 'foo', 'testnb.ipynb', download=True)
103 r = self.nbconvert_api.from_file('latex', 'foo', 'testnb.ipynb', download=True)
104 self.assertIn(u'application/zip', r.headers['Content-Type'])
104 self.assertIn(u'application/zip', r.headers['Content-Type'])
105 self.assertIn(u'.zip', r.headers['Content-Disposition'])
105 self.assertIn(u'.zip', r.headers['Content-Disposition'])
106
106
107 @onlyif_cmds_exist('pandoc')
107 @onlyif_cmds_exist('pandoc')
108 def test_from_post(self):
108 def test_from_post(self):
109 nbmodel_url = url_path_join(self.base_url(), 'api/notebooks/foo/testnb.ipynb')
109 nbmodel_url = url_path_join(self.base_url(), 'api/notebooks/foo/testnb.ipynb')
110 nbmodel = requests.get(nbmodel_url).json()
110 nbmodel = requests.get(nbmodel_url).json()
111
111
112 r = self.nbconvert_api.from_post(format='html', nbmodel=nbmodel)
112 r = self.nbconvert_api.from_post(format='html', nbmodel=nbmodel)
113 self.assertEqual(r.status_code, 200)
113 self.assertEqual(r.status_code, 200)
114 self.assertIn(u'text/html', r.headers['Content-Type'])
114 self.assertIn(u'text/html', r.headers['Content-Type'])
115 self.assertIn(u'Created by test', r.text)
115 self.assertIn(u'Created by test', r.text)
116 self.assertIn(u'print', r.text)
116 self.assertIn(u'print', r.text)
117
117
118 r = self.nbconvert_api.from_post(format='python', nbmodel=nbmodel)
118 r = self.nbconvert_api.from_post(format='python', nbmodel=nbmodel)
119 self.assertIn(u'text/x-python', r.headers['Content-Type'])
119 self.assertIn(u'text/x-python', r.headers['Content-Type'])
120 self.assertIn(u'print(2*6)', r.text)
120 self.assertIn(u'print(2*6)', r.text)
121
121
122 @onlyif_cmds_exist('pandoc')
122 @onlyif_cmds_exist('pandoc')
123 def test_from_post_zip(self):
123 def test_from_post_zip(self):
124 nbmodel_url = url_path_join(self.base_url(), 'api/notebooks/foo/testnb.ipynb')
124 nbmodel_url = url_path_join(self.base_url(), 'api/notebooks/foo/testnb.ipynb')
125 nbmodel = requests.get(nbmodel_url).json()
125 nbmodel = requests.get(nbmodel_url).json()
126
126
127 r = self.nbconvert_api.from_post(format='latex', nbmodel=nbmodel)
127 r = self.nbconvert_api.from_post(format='latex', nbmodel=nbmodel)
128 self.assertIn(u'application/zip', r.headers['Content-Type'])
128 self.assertIn(u'application/zip', r.headers['Content-Type'])
129 self.assertIn(u'.zip', r.headers['Content-Disposition'])
129 self.assertIn(u'.zip', r.headers['Content-Disposition'])
@@ -1,111 +1,93 b''
1 """
1 """Base class for preprocessors"""
2 Module that re-groups preprocessor that would be applied to ipynb files
3 before going through the templating machinery.
4
5 It exposes a convenient class to inherit from to access configurability.
6 """
7 #-----------------------------------------------------------------------------
8 # Copyright (c) 2013, the IPython Development Team.
9 #
10 # Distributed under the terms of the Modified BSD License.
11 #
12 # The full license is in the file COPYING.txt, distributed with this software.
13 #-----------------------------------------------------------------------------
14
2
15 #-----------------------------------------------------------------------------
3 # Copyright (c) IPython Development Team.
16 # Imports
4 # Distributed under the terms of the Modified BSD License.
17 #-----------------------------------------------------------------------------
18
5
19 from ..utils.base import NbConvertBase
6 from ..utils.base import NbConvertBase
20 from IPython.utils.traitlets import Bool
7 from IPython.utils.traitlets import Bool
21
8
22 #-----------------------------------------------------------------------------
23 # Classes and Functions
24 #-----------------------------------------------------------------------------
25
9
26 class Preprocessor(NbConvertBase):
10 class Preprocessor(NbConvertBase):
27 """ A configurable preprocessor
11 """ A configurable preprocessor
28
12
29 Inherit from this class if you wish to have configurability for your
13 Inherit from this class if you wish to have configurability for your
30 preprocessor.
14 preprocessor.
31
15
32 Any configurable traitlets this class exposed will be configurable in
16 Any configurable traitlets this class exposed will be configurable in
33 profiles using c.SubClassName.atribute=value
17 profiles using c.SubClassName.attribute = value
34
18
35 you can overwrite :meth:`preprocess_cell` to apply a transformation
19 you can overwrite :meth:`preprocess_cell` to apply a transformation
36 independently on each cell or :meth:`preprocess` if you prefer your own
20 independently on each cell or :meth:`preprocess` if you prefer your own
37 logic. See corresponding docstring for informations.
21 logic. See corresponding docstring for informations.
38
22
39 Disabled by default and can be enabled via the config by
23 Disabled by default and can be enabled via the config by
40 'c.YourPreprocessorName.enabled = True'
24 'c.YourPreprocessorName.enabled = True'
41 """
25 """
42
26
43 enabled = Bool(False, config=True)
27 enabled = Bool(False, config=True)
44
28
45 def __init__(self, **kw):
29 def __init__(self, **kw):
46 """
30 """
47 Public constructor
31 Public constructor
48
32
49 Parameters
33 Parameters
50 ----------
34 ----------
51 config : Config
35 config : Config
52 Configuration file structure
36 Configuration file structure
53 **kw : misc
37 **kw : misc
54 Additional arguments
38 Additional arguments
55 """
39 """
56
40
57 super(Preprocessor, self).__init__(**kw)
41 super(Preprocessor, self).__init__(**kw)
58
42
59
43
60 def __call__(self, nb, resources):
44 def __call__(self, nb, resources):
61 if self.enabled:
45 if self.enabled:
46 self.log.debug("Applying preprocessor: %s", self.__class__.__name__)
62 return self.preprocess(nb,resources)
47 return self.preprocess(nb,resources)
63 else:
48 else:
64 return nb, resources
49 return nb, resources
65
50
66
51
67 def preprocess(self, nb, resources):
52 def preprocess(self, nb, resources):
68 """
53 """
69 Preprocessing to apply on each notebook.
54 Preprocessing to apply on each notebook.
70
55
71 You should return modified nb, resources.
56 Must return modified nb, resources.
57
72 If you wish to apply your preprocessing to each cell, you might want
58 If you wish to apply your preprocessing to each cell, you might want
73 to overwrite preprocess_cell method instead.
59 to override preprocess_cell method instead.
74
60
75 Parameters
61 Parameters
76 ----------
62 ----------
77 nb : NotebookNode
63 nb : NotebookNode
78 Notebook being converted
64 Notebook being converted
79 resources : dictionary
65 resources : dictionary
80 Additional resources used in the conversion process. Allows
66 Additional resources used in the conversion process. Allows
81 preprocessors to pass variables into the Jinja engine.
67 preprocessors to pass variables into the Jinja engine.
82 """
68 """
83 self.log.debug("Applying preprocess: %s", self.__class__.__name__)
69 for worksheet in nb.worksheets:
84 try :
70 for index, cell in enumerate(worksheet.cells):
85 for worksheet in nb.worksheets:
71 worksheet.cells[index], resources = self.preprocess_cell(cell, resources, index)
86 for index, cell in enumerate(worksheet.cells):
72 return nb, resources
87 worksheet.cells[index], resources = self.preprocess_cell(cell, resources, index)
88 return nb, resources
89 except NotImplementedError:
90 raise NotImplementedError('should be implemented by subclass')
91
73
92
74
93 def preprocess_cell(self, cell, resources, index):
75 def preprocess_cell(self, cell, resources, index):
94 """
76 """
95 Overwrite if you want to apply some preprocessing to each cell. You
77 Override if you want to apply some preprocessing to each cell.
96 should return modified cell and resource dictionary.
78 Must return modified cell and resource dictionary.
97
79
98 Parameters
80 Parameters
99 ----------
81 ----------
100 cell : NotebookNode cell
82 cell : NotebookNode cell
101 Notebook cell being processed
83 Notebook cell being processed
102 resources : dictionary
84 resources : dictionary
103 Additional resources used in the conversion process. Allows
85 Additional resources used in the conversion process. Allows
104 preprocessors to pass variables into the Jinja engine.
86 preprocessors to pass variables into the Jinja engine.
105 index : int
87 index : int
106 Index of the cell being processed
88 Index of the cell being processed
107 """
89 """
108
90
109 raise NotImplementedError('should be implemented by subclass')
91 raise NotImplementedError('should be implemented by subclass')
110 return cell, resources
92 return cell, resources
111
93
@@ -1,83 +1,77 b''
1 """Module that allows latex output notebooks to be conditioned before
1 """Preprocessor for merging consecutive stream outputs for easier handling."""
2 they are converted. Exposes a decorator (@cell_preprocessor) in
2
3 addition to the coalesce_streams pre-proccessor.
3 # Copyright (c) IPython Development Team.
4 """
5 #-----------------------------------------------------------------------------
6 # Copyright (c) 2013, the IPython Development Team.
7 #
8 # Distributed under the terms of the Modified BSD License.
4 # Distributed under the terms of the Modified BSD License.
9 #
10 # The full license is in the file COPYING.txt, distributed with this software.
11 #-----------------------------------------------------------------------------
12
5
13 #-----------------------------------------------------------------------------
14 # Imports
15 #-----------------------------------------------------------------------------
16 import re
6 import re
17
7
18 #-----------------------------------------------------------------------------
19 # Functions
20 #-----------------------------------------------------------------------------
21 def cell_preprocessor(function):
8 def cell_preprocessor(function):
22 """
9 """
23 Wrap a function to be executed on all cells of a notebook
10 Wrap a function to be executed on all cells of a notebook
24
11
25 The wrapped function should have these parameters:
12 The wrapped function should have these parameters:
26
13
27 cell : NotebookNode cell
14 cell : NotebookNode cell
28 Notebook cell being processed
15 Notebook cell being processed
29 resources : dictionary
16 resources : dictionary
30 Additional resources used in the conversion process. Allows
17 Additional resources used in the conversion process. Allows
31 preprocessors to pass variables into the Jinja engine.
18 preprocessors to pass variables into the Jinja engine.
32 index : int
19 index : int
33 Index of the cell being processed
20 Index of the cell being processed
34 """
21 """
35
22
36 def wrappedfunc(nb, resources):
23 def wrappedfunc(nb, resources):
37 for worksheet in nb.worksheets :
24 from IPython.config import Application
25 if Application.initialized():
26 Application.instance().log.debug(
27 "Applying preprocessor: %s", function.__name__
28 )
29 for worksheet in nb.worksheets:
38 for index, cell in enumerate(worksheet.cells):
30 for index, cell in enumerate(worksheet.cells):
39 worksheet.cells[index], resources = function(cell, resources, index)
31 worksheet.cells[index], resources = function(cell, resources, index)
40 return nb, resources
32 return nb, resources
41 return wrappedfunc
33 return wrappedfunc
42
34
35 cr_pat = re.compile(r'.*\r(?=[^\n])')
43
36
44 @cell_preprocessor
37 @cell_preprocessor
45 def coalesce_streams(cell, resources, index):
38 def coalesce_streams(cell, resources, index):
46 """
39 """
47 Merge consecutive sequences of stream output into single stream
40 Merge consecutive sequences of stream output into single stream
48 to prevent extra newlines inserted at flush calls
41 to prevent extra newlines inserted at flush calls
49
42
50 Parameters
43 Parameters
51 ----------
44 ----------
52 cell : NotebookNode cell
45 cell : NotebookNode cell
53 Notebook cell being processed
46 Notebook cell being processed
54 resources : dictionary
47 resources : dictionary
55 Additional resources used in the conversion process. Allows
48 Additional resources used in the conversion process. Allows
56 transformers to pass variables into the Jinja engine.
49 transformers to pass variables into the Jinja engine.
57 index : int
50 index : int
58 Index of the cell being processed
51 Index of the cell being processed
59 """
52 """
60
53
61 outputs = cell.get('outputs', [])
54 outputs = cell.get('outputs', [])
62 if not outputs:
55 if not outputs:
63 return cell, resources
56 return cell, resources
64
57
65 last = outputs[0]
58 last = outputs[0]
66 new_outputs = [last]
59 new_outputs = [last]
67
68 for output in outputs[1:]:
60 for output in outputs[1:]:
69 if (output.output_type == 'stream' and
61 if (output.output_type == 'stream' and
70 last.output_type == 'stream' and
62 last.output_type == 'stream' and
71 last.stream == output.stream
63 last.stream == output.stream
72 ):
64 ):
73 last.text += output.text
65 last.text += output.text
74
66
75 # Respect \r characters.
76 cr_pat = re.compile(r'.*\r(?=[^\n])')
77 last.text = cr_pat.sub('', last.text)
78 else:
67 else:
79 new_outputs.append(output)
68 new_outputs.append(output)
80 last = output
69 last = output
81
70
71 # process \r characters
72 for output in new_outputs:
73 if output.output_type == 'stream':
74 output.text = cr_pat.sub('', output.text)
75
82 cell.outputs = new_outputs
76 cell.outputs = new_outputs
83 return cell, resources
77 return cell, resources
@@ -1,217 +1,204 b''
1 """The basic dict based notebook format.
1 """The basic dict based notebook format.
2
2
3 The Python representation of a notebook is a nested structure of
3 The Python representation of a notebook is a nested structure of
4 dictionary subclasses that support attribute access
4 dictionary subclasses that support attribute access
5 (IPython.utils.ipstruct.Struct). The functions in this module are merely
5 (IPython.utils.ipstruct.Struct). The functions in this module are merely
6 helpers to build the structs in the right form.
6 helpers to build the structs in the right form.
7
8 Authors:
9
10 * Brian Granger
11 """
7 """
12
8
13 #-----------------------------------------------------------------------------
9 # Copyright (c) IPython Development Team.
14 # Copyright (C) 2008-2011 The IPython Development Team
10 # Distributed under the terms of the Modified BSD License.
15 #
16 # Distributed under the terms of the BSD License. The full license is in
17 # the file COPYING, distributed as part of this software.
18 #-----------------------------------------------------------------------------
19
20 #-----------------------------------------------------------------------------
21 # Imports
22 #-----------------------------------------------------------------------------
23
11
24 import pprint
12 import pprint
25 import uuid
13 import uuid
26
14
27 from IPython.utils.ipstruct import Struct
15 from IPython.utils.ipstruct import Struct
28 from IPython.utils.py3compat import cast_unicode, unicode_type
16 from IPython.utils.py3compat import cast_unicode, unicode_type
29
17
30 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
31 # Code
19 # Code
32 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
33
21
34 # Change this when incrementing the nbformat version
22 # Change this when incrementing the nbformat version
35 nbformat = 3
23 nbformat = 3
36 nbformat_minor = 0
24 nbformat_minor = 0
37
25
38 class NotebookNode(Struct):
26 class NotebookNode(Struct):
39 pass
27 pass
40
28
41
29
42 def from_dict(d):
30 def from_dict(d):
43 if isinstance(d, dict):
31 if isinstance(d, dict):
44 newd = NotebookNode()
32 newd = NotebookNode()
45 for k,v in d.items():
33 for k,v in d.items():
46 newd[k] = from_dict(v)
34 newd[k] = from_dict(v)
47 return newd
35 return newd
48 elif isinstance(d, (tuple, list)):
36 elif isinstance(d, (tuple, list)):
49 return [from_dict(i) for i in d]
37 return [from_dict(i) for i in d]
50 else:
38 else:
51 return d
39 return d
52
40
53
41
54 def new_output(output_type=None, output_text=None, output_png=None,
42 def new_output(output_type, output_text=None, output_png=None,
55 output_html=None, output_svg=None, output_latex=None, output_json=None,
43 output_html=None, output_svg=None, output_latex=None, output_json=None,
56 output_javascript=None, output_jpeg=None, prompt_number=None,
44 output_javascript=None, output_jpeg=None, prompt_number=None,
57 ename=None, evalue=None, traceback=None, stream=None, metadata=None):
45 ename=None, evalue=None, traceback=None, stream=None, metadata=None):
58 """Create a new output, to go in the ``cell.outputs`` list of a code cell.
46 """Create a new output, to go in the ``cell.outputs`` list of a code cell.
59 """
47 """
60 output = NotebookNode()
48 output = NotebookNode()
61 if output_type is not None:
49 output.output_type = unicode_type(output_type)
62 output.output_type = unicode_type(output_type)
63
50
64 if metadata is None:
51 if metadata is None:
65 metadata = {}
52 metadata = {}
66 if not isinstance(metadata, dict):
53 if not isinstance(metadata, dict):
67 raise TypeError("metadata must be dict")
54 raise TypeError("metadata must be dict")
68 output.metadata = metadata
55 output.metadata = metadata
69
56
70 if output_type != 'pyerr':
57 if output_type != 'pyerr':
71 if output_text is not None:
58 if output_text is not None:
72 output.text = cast_unicode(output_text)
59 output.text = cast_unicode(output_text)
73 if output_png is not None:
60 if output_png is not None:
74 output.png = cast_unicode(output_png)
61 output.png = cast_unicode(output_png)
75 if output_jpeg is not None:
62 if output_jpeg is not None:
76 output.jpeg = cast_unicode(output_jpeg)
63 output.jpeg = cast_unicode(output_jpeg)
77 if output_html is not None:
64 if output_html is not None:
78 output.html = cast_unicode(output_html)
65 output.html = cast_unicode(output_html)
79 if output_svg is not None:
66 if output_svg is not None:
80 output.svg = cast_unicode(output_svg)
67 output.svg = cast_unicode(output_svg)
81 if output_latex is not None:
68 if output_latex is not None:
82 output.latex = cast_unicode(output_latex)
69 output.latex = cast_unicode(output_latex)
83 if output_json is not None:
70 if output_json is not None:
84 output.json = cast_unicode(output_json)
71 output.json = cast_unicode(output_json)
85 if output_javascript is not None:
72 if output_javascript is not None:
86 output.javascript = cast_unicode(output_javascript)
73 output.javascript = cast_unicode(output_javascript)
87
74
88 if output_type == u'pyout':
75 if output_type == u'pyout':
89 if prompt_number is not None:
76 if prompt_number is not None:
90 output.prompt_number = int(prompt_number)
77 output.prompt_number = int(prompt_number)
91
78
92 if output_type == u'pyerr':
79 if output_type == u'pyerr':
93 if ename is not None:
80 if ename is not None:
94 output.ename = cast_unicode(ename)
81 output.ename = cast_unicode(ename)
95 if evalue is not None:
82 if evalue is not None:
96 output.evalue = cast_unicode(evalue)
83 output.evalue = cast_unicode(evalue)
97 if traceback is not None:
84 if traceback is not None:
98 output.traceback = [cast_unicode(frame) for frame in list(traceback)]
85 output.traceback = [cast_unicode(frame) for frame in list(traceback)]
99
86
100 if output_type == u'stream':
87 if output_type == u'stream':
101 output.stream = 'stdout' if stream is None else cast_unicode(stream)
88 output.stream = 'stdout' if stream is None else cast_unicode(stream)
102
89
103 return output
90 return output
104
91
105
92
106 def new_code_cell(input=None, prompt_number=None, outputs=None,
93 def new_code_cell(input=None, prompt_number=None, outputs=None,
107 language=u'python', collapsed=False, metadata=None):
94 language=u'python', collapsed=False, metadata=None):
108 """Create a new code cell with input and output"""
95 """Create a new code cell with input and output"""
109 cell = NotebookNode()
96 cell = NotebookNode()
110 cell.cell_type = u'code'
97 cell.cell_type = u'code'
111 if language is not None:
98 if language is not None:
112 cell.language = cast_unicode(language)
99 cell.language = cast_unicode(language)
113 if input is not None:
100 if input is not None:
114 cell.input = cast_unicode(input)
101 cell.input = cast_unicode(input)
115 if prompt_number is not None:
102 if prompt_number is not None:
116 cell.prompt_number = int(prompt_number)
103 cell.prompt_number = int(prompt_number)
117 if outputs is None:
104 if outputs is None:
118 cell.outputs = []
105 cell.outputs = []
119 else:
106 else:
120 cell.outputs = outputs
107 cell.outputs = outputs
121 if collapsed is not None:
108 if collapsed is not None:
122 cell.collapsed = bool(collapsed)
109 cell.collapsed = bool(collapsed)
123 cell.metadata = NotebookNode(metadata or {})
110 cell.metadata = NotebookNode(metadata or {})
124
111
125 return cell
112 return cell
126
113
127 def new_text_cell(cell_type, source=None, rendered=None, metadata=None):
114 def new_text_cell(cell_type, source=None, rendered=None, metadata=None):
128 """Create a new text cell."""
115 """Create a new text cell."""
129 cell = NotebookNode()
116 cell = NotebookNode()
130 # VERSIONHACK: plaintext -> raw
117 # VERSIONHACK: plaintext -> raw
131 # handle never-released plaintext name for raw cells
118 # handle never-released plaintext name for raw cells
132 if cell_type == 'plaintext':
119 if cell_type == 'plaintext':
133 cell_type = 'raw'
120 cell_type = 'raw'
134 if source is not None:
121 if source is not None:
135 cell.source = cast_unicode(source)
122 cell.source = cast_unicode(source)
136 if rendered is not None:
123 if rendered is not None:
137 cell.rendered = cast_unicode(rendered)
124 cell.rendered = cast_unicode(rendered)
138 cell.metadata = NotebookNode(metadata or {})
125 cell.metadata = NotebookNode(metadata or {})
139 cell.cell_type = cell_type
126 cell.cell_type = cell_type
140 return cell
127 return cell
141
128
142
129
143 def new_heading_cell(source=None, rendered=None, level=1, metadata=None):
130 def new_heading_cell(source=None, rendered=None, level=1, metadata=None):
144 """Create a new section cell with a given integer level."""
131 """Create a new section cell with a given integer level."""
145 cell = NotebookNode()
132 cell = NotebookNode()
146 cell.cell_type = u'heading'
133 cell.cell_type = u'heading'
147 if source is not None:
134 if source is not None:
148 cell.source = cast_unicode(source)
135 cell.source = cast_unicode(source)
149 if rendered is not None:
136 if rendered is not None:
150 cell.rendered = cast_unicode(rendered)
137 cell.rendered = cast_unicode(rendered)
151 cell.level = int(level)
138 cell.level = int(level)
152 cell.metadata = NotebookNode(metadata or {})
139 cell.metadata = NotebookNode(metadata or {})
153 return cell
140 return cell
154
141
155
142
156 def new_worksheet(name=None, cells=None, metadata=None):
143 def new_worksheet(name=None, cells=None, metadata=None):
157 """Create a worksheet by name with with a list of cells."""
144 """Create a worksheet by name with with a list of cells."""
158 ws = NotebookNode()
145 ws = NotebookNode()
159 if name is not None:
146 if name is not None:
160 ws.name = cast_unicode(name)
147 ws.name = cast_unicode(name)
161 if cells is None:
148 if cells is None:
162 ws.cells = []
149 ws.cells = []
163 else:
150 else:
164 ws.cells = list(cells)
151 ws.cells = list(cells)
165 ws.metadata = NotebookNode(metadata or {})
152 ws.metadata = NotebookNode(metadata or {})
166 return ws
153 return ws
167
154
168
155
169 def new_notebook(name=None, metadata=None, worksheets=None):
156 def new_notebook(name=None, metadata=None, worksheets=None):
170 """Create a notebook by name, id and a list of worksheets."""
157 """Create a notebook by name, id and a list of worksheets."""
171 nb = NotebookNode()
158 nb = NotebookNode()
172 nb.nbformat = nbformat
159 nb.nbformat = nbformat
173 nb.nbformat_minor = nbformat_minor
160 nb.nbformat_minor = nbformat_minor
174 if worksheets is None:
161 if worksheets is None:
175 nb.worksheets = []
162 nb.worksheets = []
176 else:
163 else:
177 nb.worksheets = list(worksheets)
164 nb.worksheets = list(worksheets)
178 if metadata is None:
165 if metadata is None:
179 nb.metadata = new_metadata()
166 nb.metadata = new_metadata()
180 else:
167 else:
181 nb.metadata = NotebookNode(metadata)
168 nb.metadata = NotebookNode(metadata)
182 if name is not None:
169 if name is not None:
183 nb.metadata.name = cast_unicode(name)
170 nb.metadata.name = cast_unicode(name)
184 return nb
171 return nb
185
172
186
173
187 def new_metadata(name=None, authors=None, license=None, created=None,
174 def new_metadata(name=None, authors=None, license=None, created=None,
188 modified=None, gistid=None):
175 modified=None, gistid=None):
189 """Create a new metadata node."""
176 """Create a new metadata node."""
190 metadata = NotebookNode()
177 metadata = NotebookNode()
191 if name is not None:
178 if name is not None:
192 metadata.name = cast_unicode(name)
179 metadata.name = cast_unicode(name)
193 if authors is not None:
180 if authors is not None:
194 metadata.authors = list(authors)
181 metadata.authors = list(authors)
195 if created is not None:
182 if created is not None:
196 metadata.created = cast_unicode(created)
183 metadata.created = cast_unicode(created)
197 if modified is not None:
184 if modified is not None:
198 metadata.modified = cast_unicode(modified)
185 metadata.modified = cast_unicode(modified)
199 if license is not None:
186 if license is not None:
200 metadata.license = cast_unicode(license)
187 metadata.license = cast_unicode(license)
201 if gistid is not None:
188 if gistid is not None:
202 metadata.gistid = cast_unicode(gistid)
189 metadata.gistid = cast_unicode(gistid)
203 return metadata
190 return metadata
204
191
205 def new_author(name=None, email=None, affiliation=None, url=None):
192 def new_author(name=None, email=None, affiliation=None, url=None):
206 """Create a new author."""
193 """Create a new author."""
207 author = NotebookNode()
194 author = NotebookNode()
208 if name is not None:
195 if name is not None:
209 author.name = cast_unicode(name)
196 author.name = cast_unicode(name)
210 if email is not None:
197 if email is not None:
211 author.email = cast_unicode(email)
198 author.email = cast_unicode(email)
212 if affiliation is not None:
199 if affiliation is not None:
213 author.affiliation = cast_unicode(affiliation)
200 author.affiliation = cast_unicode(affiliation)
214 if url is not None:
201 if url is not None:
215 author.url = cast_unicode(url)
202 author.url = cast_unicode(url)
216 return author
203 return author
217
204
@@ -1,157 +1,157 b''
1 from unittest import TestCase
1 from unittest import TestCase
2
2
3 from ..nbbase import (
3 from ..nbbase import (
4 NotebookNode,
4 NotebookNode,
5 new_code_cell, new_text_cell, new_worksheet, new_notebook, new_output,
5 new_code_cell, new_text_cell, new_worksheet, new_notebook, new_output,
6 new_author, new_metadata, new_heading_cell, nbformat
6 new_author, new_metadata, new_heading_cell, nbformat
7 )
7 )
8
8
9 class TestCell(TestCase):
9 class TestCell(TestCase):
10
10
11 def test_empty_code_cell(self):
11 def test_empty_code_cell(self):
12 cc = new_code_cell()
12 cc = new_code_cell()
13 self.assertEqual(cc.cell_type,u'code')
13 self.assertEqual(cc.cell_type,u'code')
14 self.assertEqual(u'input' not in cc, True)
14 self.assertEqual(u'input' not in cc, True)
15 self.assertEqual(u'prompt_number' not in cc, True)
15 self.assertEqual(u'prompt_number' not in cc, True)
16 self.assertEqual(cc.outputs, [])
16 self.assertEqual(cc.outputs, [])
17 self.assertEqual(cc.collapsed, False)
17 self.assertEqual(cc.collapsed, False)
18
18
19 def test_code_cell(self):
19 def test_code_cell(self):
20 cc = new_code_cell(input='a=10', prompt_number=0, collapsed=True)
20 cc = new_code_cell(input='a=10', prompt_number=0, collapsed=True)
21 cc.outputs = [new_output(output_type=u'pyout',
21 cc.outputs = [new_output(output_type=u'pyout',
22 output_svg=u'foo',output_text=u'10',prompt_number=0)]
22 output_svg=u'foo',output_text=u'10',prompt_number=0)]
23 self.assertEqual(cc.input, u'a=10')
23 self.assertEqual(cc.input, u'a=10')
24 self.assertEqual(cc.prompt_number, 0)
24 self.assertEqual(cc.prompt_number, 0)
25 self.assertEqual(cc.language, u'python')
25 self.assertEqual(cc.language, u'python')
26 self.assertEqual(cc.outputs[0].svg, u'foo')
26 self.assertEqual(cc.outputs[0].svg, u'foo')
27 self.assertEqual(cc.outputs[0].text, u'10')
27 self.assertEqual(cc.outputs[0].text, u'10')
28 self.assertEqual(cc.outputs[0].prompt_number, 0)
28 self.assertEqual(cc.outputs[0].prompt_number, 0)
29 self.assertEqual(cc.collapsed, True)
29 self.assertEqual(cc.collapsed, True)
30
30
31 def test_pyerr(self):
31 def test_pyerr(self):
32 o = new_output(output_type=u'pyerr', ename=u'NameError',
32 o = new_output(output_type=u'pyerr', ename=u'NameError',
33 evalue=u'Name not found', traceback=[u'frame 0', u'frame 1', u'frame 2']
33 evalue=u'Name not found', traceback=[u'frame 0', u'frame 1', u'frame 2']
34 )
34 )
35 self.assertEqual(o.output_type, u'pyerr')
35 self.assertEqual(o.output_type, u'pyerr')
36 self.assertEqual(o.ename, u'NameError')
36 self.assertEqual(o.ename, u'NameError')
37 self.assertEqual(o.evalue, u'Name not found')
37 self.assertEqual(o.evalue, u'Name not found')
38 self.assertEqual(o.traceback, [u'frame 0', u'frame 1', u'frame 2'])
38 self.assertEqual(o.traceback, [u'frame 0', u'frame 1', u'frame 2'])
39
39
40 def test_empty_html_cell(self):
40 def test_empty_html_cell(self):
41 tc = new_text_cell(u'html')
41 tc = new_text_cell(u'html')
42 self.assertEqual(tc.cell_type, u'html')
42 self.assertEqual(tc.cell_type, u'html')
43 self.assertEqual(u'source' not in tc, True)
43 self.assertEqual(u'source' not in tc, True)
44 self.assertEqual(u'rendered' not in tc, True)
44 self.assertEqual(u'rendered' not in tc, True)
45
45
46 def test_html_cell(self):
46 def test_html_cell(self):
47 tc = new_text_cell(u'html', 'hi', 'hi')
47 tc = new_text_cell(u'html', 'hi', 'hi')
48 self.assertEqual(tc.source, u'hi')
48 self.assertEqual(tc.source, u'hi')
49 self.assertEqual(tc.rendered, u'hi')
49 self.assertEqual(tc.rendered, u'hi')
50
50
51 def test_empty_markdown_cell(self):
51 def test_empty_markdown_cell(self):
52 tc = new_text_cell(u'markdown')
52 tc = new_text_cell(u'markdown')
53 self.assertEqual(tc.cell_type, u'markdown')
53 self.assertEqual(tc.cell_type, u'markdown')
54 self.assertEqual(u'source' not in tc, True)
54 self.assertEqual(u'source' not in tc, True)
55 self.assertEqual(u'rendered' not in tc, True)
55 self.assertEqual(u'rendered' not in tc, True)
56
56
57 def test_markdown_cell(self):
57 def test_markdown_cell(self):
58 tc = new_text_cell(u'markdown', 'hi', 'hi')
58 tc = new_text_cell(u'markdown', 'hi', 'hi')
59 self.assertEqual(tc.source, u'hi')
59 self.assertEqual(tc.source, u'hi')
60 self.assertEqual(tc.rendered, u'hi')
60 self.assertEqual(tc.rendered, u'hi')
61
61
62 def test_empty_raw_cell(self):
62 def test_empty_raw_cell(self):
63 tc = new_text_cell(u'raw')
63 tc = new_text_cell(u'raw')
64 self.assertEqual(tc.cell_type, u'raw')
64 self.assertEqual(tc.cell_type, u'raw')
65 self.assertEqual(u'source' not in tc, True)
65 self.assertEqual(u'source' not in tc, True)
66 self.assertEqual(u'rendered' not in tc, True)
66 self.assertEqual(u'rendered' not in tc, True)
67
67
68 def test_raw_cell(self):
68 def test_raw_cell(self):
69 tc = new_text_cell(u'raw', 'hi', 'hi')
69 tc = new_text_cell(u'raw', 'hi', 'hi')
70 self.assertEqual(tc.source, u'hi')
70 self.assertEqual(tc.source, u'hi')
71 self.assertEqual(tc.rendered, u'hi')
71 self.assertEqual(tc.rendered, u'hi')
72
72
73 def test_empty_heading_cell(self):
73 def test_empty_heading_cell(self):
74 tc = new_heading_cell()
74 tc = new_heading_cell()
75 self.assertEqual(tc.cell_type, u'heading')
75 self.assertEqual(tc.cell_type, u'heading')
76 self.assertEqual(u'source' not in tc, True)
76 self.assertEqual(u'source' not in tc, True)
77 self.assertEqual(u'rendered' not in tc, True)
77 self.assertEqual(u'rendered' not in tc, True)
78
78
79 def test_heading_cell(self):
79 def test_heading_cell(self):
80 tc = new_heading_cell(u'hi', u'hi', level=2)
80 tc = new_heading_cell(u'hi', u'hi', level=2)
81 self.assertEqual(tc.source, u'hi')
81 self.assertEqual(tc.source, u'hi')
82 self.assertEqual(tc.rendered, u'hi')
82 self.assertEqual(tc.rendered, u'hi')
83 self.assertEqual(tc.level, 2)
83 self.assertEqual(tc.level, 2)
84
84
85
85
86 class TestWorksheet(TestCase):
86 class TestWorksheet(TestCase):
87
87
88 def test_empty_worksheet(self):
88 def test_empty_worksheet(self):
89 ws = new_worksheet()
89 ws = new_worksheet()
90 self.assertEqual(ws.cells,[])
90 self.assertEqual(ws.cells,[])
91 self.assertEqual(u'name' not in ws, True)
91 self.assertEqual(u'name' not in ws, True)
92
92
93 def test_worksheet(self):
93 def test_worksheet(self):
94 cells = [new_code_cell(), new_text_cell(u'html')]
94 cells = [new_code_cell(), new_text_cell(u'html')]
95 ws = new_worksheet(cells=cells,name=u'foo')
95 ws = new_worksheet(cells=cells,name=u'foo')
96 self.assertEqual(ws.cells,cells)
96 self.assertEqual(ws.cells,cells)
97 self.assertEqual(ws.name,u'foo')
97 self.assertEqual(ws.name,u'foo')
98
98
99 class TestNotebook(TestCase):
99 class TestNotebook(TestCase):
100
100
101 def test_empty_notebook(self):
101 def test_empty_notebook(self):
102 nb = new_notebook()
102 nb = new_notebook()
103 self.assertEqual(nb.worksheets, [])
103 self.assertEqual(nb.worksheets, [])
104 self.assertEqual(nb.metadata, NotebookNode())
104 self.assertEqual(nb.metadata, NotebookNode())
105 self.assertEqual(nb.nbformat,nbformat)
105 self.assertEqual(nb.nbformat,nbformat)
106
106
107 def test_notebook(self):
107 def test_notebook(self):
108 worksheets = [new_worksheet(),new_worksheet()]
108 worksheets = [new_worksheet(),new_worksheet()]
109 metadata = new_metadata(name=u'foo')
109 metadata = new_metadata(name=u'foo')
110 nb = new_notebook(metadata=metadata,worksheets=worksheets)
110 nb = new_notebook(metadata=metadata,worksheets=worksheets)
111 self.assertEqual(nb.metadata.name,u'foo')
111 self.assertEqual(nb.metadata.name,u'foo')
112 self.assertEqual(nb.worksheets,worksheets)
112 self.assertEqual(nb.worksheets,worksheets)
113 self.assertEqual(nb.nbformat,nbformat)
113 self.assertEqual(nb.nbformat,nbformat)
114
114
115 def test_notebook_name(self):
115 def test_notebook_name(self):
116 worksheets = [new_worksheet(),new_worksheet()]
116 worksheets = [new_worksheet(),new_worksheet()]
117 nb = new_notebook(name='foo',worksheets=worksheets)
117 nb = new_notebook(name='foo',worksheets=worksheets)
118 self.assertEqual(nb.metadata.name,u'foo')
118 self.assertEqual(nb.metadata.name,u'foo')
119 self.assertEqual(nb.worksheets,worksheets)
119 self.assertEqual(nb.worksheets,worksheets)
120 self.assertEqual(nb.nbformat,nbformat)
120 self.assertEqual(nb.nbformat,nbformat)
121
121
122 class TestMetadata(TestCase):
122 class TestMetadata(TestCase):
123
123
124 def test_empty_metadata(self):
124 def test_empty_metadata(self):
125 md = new_metadata()
125 md = new_metadata()
126 self.assertEqual(u'name' not in md, True)
126 self.assertEqual(u'name' not in md, True)
127 self.assertEqual(u'authors' not in md, True)
127 self.assertEqual(u'authors' not in md, True)
128 self.assertEqual(u'license' not in md, True)
128 self.assertEqual(u'license' not in md, True)
129 self.assertEqual(u'saved' not in md, True)
129 self.assertEqual(u'saved' not in md, True)
130 self.assertEqual(u'modified' not in md, True)
130 self.assertEqual(u'modified' not in md, True)
131 self.assertEqual(u'gistid' not in md, True)
131 self.assertEqual(u'gistid' not in md, True)
132
132
133 def test_metadata(self):
133 def test_metadata(self):
134 authors = [new_author(name='Bart Simpson',email='bsimpson@fox.com')]
134 authors = [new_author(name='Bart Simpson',email='bsimpson@fox.com')]
135 md = new_metadata(name=u'foo',license=u'BSD',created=u'today',
135 md = new_metadata(name=u'foo',license=u'BSD',created=u'today',
136 modified=u'now',gistid=u'21341231',authors=authors)
136 modified=u'now',gistid=u'21341231',authors=authors)
137 self.assertEqual(md.name, u'foo')
137 self.assertEqual(md.name, u'foo')
138 self.assertEqual(md.license, u'BSD')
138 self.assertEqual(md.license, u'BSD')
139 self.assertEqual(md.created, u'today')
139 self.assertEqual(md.created, u'today')
140 self.assertEqual(md.modified, u'now')
140 self.assertEqual(md.modified, u'now')
141 self.assertEqual(md.gistid, u'21341231')
141 self.assertEqual(md.gistid, u'21341231')
142 self.assertEqual(md.authors, authors)
142 self.assertEqual(md.authors, authors)
143
143
144 class TestOutputs(TestCase):
144 class TestOutputs(TestCase):
145 def test_binary_png(self):
145 def test_binary_png(self):
146 out = new_output(output_png=b'\x89PNG\r\n\x1a\n')
146 out = new_output(output_png=b'\x89PNG\r\n\x1a\n', output_type='display_data')
147
147
148 def test_b64b6tes_png(self):
148 def test_b64b6tes_png(self):
149 out = new_output(output_png=b'iVBORw0KG')
149 out = new_output(output_png=b'iVBORw0KG', output_type='display_data')
150
150
151 def test_binary_jpeg(self):
151 def test_binary_jpeg(self):
152 out = new_output(output_jpeg=b'\xff\xd8')
152 out = new_output(output_jpeg=b'\xff\xd8', output_type='display_data')
153
153
154 def test_b64b6tes_jpeg(self):
154 def test_b64b6tes_jpeg(self):
155 out = new_output(output_jpeg=b'/9')
155 out = new_output(output_jpeg=b'/9', output_type='display_data')
156
156
157
157
General Comments 0
You need to be logged in to leave comments. Login now