##// END OF EJS Templates
Merge pull request #6053 from minrk/execute-preprocessor...
Min RK -
r17118:f64a9ca8 merge
parent child Browse files
Show More
@@ -0,0 +1,165 b''
1 """Module containing a preprocessor that removes the outputs from code cells"""
2
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
5
6 import os
7 import sys
8
9 try:
10 from queue import Empty # Py 3
11 except ImportError:
12 from Queue import Empty # Py 2
13
14 from IPython.utils.traitlets import List, Unicode
15
16 from IPython.nbformat.current import reads, NotebookNode, writes
17 from .base import Preprocessor
18 from IPython.utils.traitlets import Integer
19
20 class ExecutePreprocessor(Preprocessor):
21 """
22 Executes all the cells in a notebook
23 """
24
25 timeout = Integer(30, config=True,
26 help="The time to wait (in seconds) for output from executions."
27 )
28 # FIXME: to be removed with nbformat v4
29 # map msg_type to v3 output_type
30 msg_type_map = {
31 "error" : "pyerr",
32 "execute_result" : "pyout",
33 }
34
35 # FIXME: to be removed with nbformat v4
36 # map mime-type to v3 mime-type keys
37 mime_map = {
38 "text/plain" : "text",
39 "text/html" : "html",
40 "image/svg+xml" : "svg",
41 "image/png" : "png",
42 "image/jpeg" : "jpeg",
43 "text/latex" : "latex",
44 "application/json" : "json",
45 "application/javascript" : "javascript",
46 }
47
48 extra_arguments = List(Unicode)
49
50 def _create_client(self):
51 from IPython.kernel import KernelManager
52 self.km = KernelManager()
53 self.km.write_connection_file()
54 self.kc = self.km.client()
55 self.kc.start_channels()
56 self.km.start_kernel(extra_arguments=self.extra_arguments, stderr=open(os.devnull, 'w'))
57 self.iopub = self.kc.iopub_channel
58 self.shell = self.kc.shell_channel
59 self.shell.kernel_info()
60 try:
61 self.shell.get_msg(timeout=self.timeout)
62 except Empty:
63 self.log.error("Timeout waiting for kernel_info reply")
64 raise
65 # flush IOPub
66 while True:
67 try:
68 self.iopub.get_msg(block=True, timeout=0.25)
69 except Empty:
70 break
71
72 def _shutdown_client(self):
73 self.kc.stop_channels()
74 self.km.shutdown_kernel()
75 del self.km
76
77 def preprocess(self, nb, resources):
78 self._create_client()
79 nb, resources = super(ExecutePreprocessor, self).preprocess(nb, resources)
80 self._shutdown_client()
81 return nb, resources
82
83 def preprocess_cell(self, cell, resources, cell_index):
84 """
85 Apply a transformation on each code cell. See base.py for details.
86 """
87 if cell.cell_type != 'code':
88 return cell, resources
89 try:
90 outputs = self.run_cell(self.shell, self.iopub, cell)
91 except Exception as e:
92 self.log.error("failed to run cell: " + repr(e))
93 self.log.error(str(cell.input))
94 raise
95 cell.outputs = outputs
96 return cell, resources
97
98 def run_cell(self, shell, iopub, cell):
99 msg_id = shell.execute(cell.input)
100 self.log.debug("Executing cell:\n%s", cell.input)
101 # wait for finish, with timeout
102 while True:
103 try:
104 msg = shell.get_msg(timeout=self.timeout)
105 except Empty:
106 self.log.error("Timeout waiting for execute reply")
107 raise
108 if msg['parent_header'].get('msg_id') == msg_id:
109 break
110 else:
111 # not our reply
112 continue
113
114 outs = []
115
116 while True:
117 try:
118 msg = iopub.get_msg(timeout=self.timeout)
119 except Empty:
120 self.log.warn("Timeout waiting for IOPub output")
121 break
122 if msg['parent_header'].get('msg_id') != msg_id:
123 # not an output from our execution
124 continue
125
126 msg_type = msg['msg_type']
127 self.log.debug("output: %s", msg_type)
128 content = msg['content']
129 if msg_type == 'status':
130 if content['execution_state'] == 'idle':
131 break
132 else:
133 continue
134 elif msg_type in {'execute_input', 'pyin'}:
135 continue
136 elif msg_type == 'clear_output':
137 outs = []
138 continue
139
140 out = NotebookNode(output_type=self.msg_type_map.get(msg_type, msg_type))
141
142 # set the prompt number for the input and the output
143 if 'execution_count' in content:
144 cell['prompt_number'] = content['execution_count']
145 out.prompt_number = content['execution_count']
146
147 if msg_type == 'stream':
148 out.stream = content['name']
149 out.text = content['data']
150 elif msg_type in ('display_data', 'execute_result'):
151 out['metadata'] = content['metadata']
152 for mime, data in content['data'].items():
153 # map mime-type keys to nbformat v3 keys
154 # this will be unnecessary in nbformat v4
155 key = self.mime_map.get(mime, mime)
156 out[key] = data
157 elif msg_type == 'error':
158 out.ename = content['ename']
159 out.evalue = content['evalue']
160 out.traceback = content['traceback']
161 else:
162 self.log.error("unhandled iopub msg: " + msg_type)
163
164 outs.append(out)
165 return outs
@@ -0,0 +1,46 b''
1 {
2 "metadata": {
3 "name": ""
4 },
5 "nbformat": 3,
6 "nbformat_minor": 0,
7 "worksheets": [
8 {
9 "cells": [
10 {
11 "cell_type": "code",
12 "collapsed": false,
13 "input": [
14 "from IPython.display import clear_output"
15 ],
16 "language": "python",
17 "metadata": {},
18 "outputs": [],
19 "prompt_number": 1
20 },
21 {
22 "cell_type": "code",
23 "collapsed": false,
24 "input": [
25 "for i in range(10):\n",
26 " clear_output()\n",
27 " print(i)"
28 ],
29 "language": "python",
30 "metadata": {},
31 "outputs": [
32 {
33 "output_type": "stream",
34 "stream": "stdout",
35 "text": [
36 "9\n"
37 ]
38 }
39 ],
40 "prompt_number": 2
41 }
42 ],
43 "metadata": {}
44 }
45 ]
46 } No newline at end of file
@@ -0,0 +1,55 b''
1 {
2 "metadata": {
3 "name": ""
4 },
5 "nbformat": 3,
6 "nbformat_minor": 0,
7 "worksheets": [
8 {
9 "cells": [
10 {
11 "cell_type": "code",
12 "collapsed": false,
13 "input": [
14 "i, j = 1, 1"
15 ],
16 "language": "python",
17 "metadata": {},
18 "outputs": [],
19 "prompt_number": 1
20 },
21 {
22 "cell_type": "code",
23 "collapsed": false,
24 "input": [
25 "for m in range(10):\n",
26 " i, j = j, i + j\n",
27 " print(j)"
28 ],
29 "language": "python",
30 "metadata": {},
31 "outputs": [
32 {
33 "output_type": "stream",
34 "stream": "stdout",
35 "text": [
36 "2\n",
37 "3\n",
38 "5\n",
39 "8\n",
40 "13\n",
41 "21\n",
42 "34\n",
43 "55\n",
44 "89\n",
45 "144\n"
46 ]
47 }
48 ],
49 "prompt_number": 2
50 }
51 ],
52 "metadata": {}
53 }
54 ]
55 } No newline at end of file
@@ -0,0 +1,33 b''
1 {
2 "metadata": {
3 "name": ""
4 },
5 "nbformat": 3,
6 "nbformat_minor": 0,
7 "worksheets": [
8 {
9 "cells": [
10 {
11 "cell_type": "code",
12 "collapsed": false,
13 "input": [
14 "print(\"Hello World\")"
15 ],
16 "language": "python",
17 "metadata": {},
18 "outputs": [
19 {
20 "output_type": "stream",
21 "stream": "stdout",
22 "text": [
23 "Hello World\n"
24 ]
25 }
26 ],
27 "prompt_number": 1
28 }
29 ],
30 "metadata": {}
31 }
32 ]
33 } No newline at end of file
@@ -0,0 +1,36 b''
1 {
2 "metadata": {
3 "name": ""
4 },
5 "nbformat": 3,
6 "nbformat_minor": 0,
7 "worksheets": [
8 {
9 "cells": [
10 {
11 "cell_type": "code",
12 "collapsed": false,
13 "input": [
14 "from IPython.display import Image"
15 ],
16 "language": "python",
17 "metadata": {},
18 "outputs": [],
19 "prompt_number": 1
20 },
21 {
22 "cell_type": "code",
23 "collapsed": false,
24 "input": [
25 "Image('../input/python.png');"
26 ],
27 "language": "python",
28 "metadata": {},
29 "outputs": [],
30 "prompt_number": 2
31 }
32 ],
33 "metadata": {}
34 }
35 ]
36 } No newline at end of file
@@ -0,0 +1,53 b''
1 {
2 "metadata": {
3 "name": ""
4 },
5 "nbformat": 3,
6 "nbformat_minor": 0,
7 "worksheets": [
8 {
9 "cells": [
10 {
11 "cell_type": "code",
12 "collapsed": false,
13 "input": [
14 "from IPython.display import SVG"
15 ],
16 "language": "python",
17 "metadata": {},
18 "outputs": [],
19 "prompt_number": 1
20 },
21 {
22 "cell_type": "code",
23 "collapsed": false,
24 "input": [
25 "SVG(data='''\n",
26 "<svg height=\"100\" width=\"100\">\n",
27 " <circle cx=\"50\" cy=\"50\" r=\"40\" stroke=\"black\" stroke-width=\"2\" fill=\"red\" />\n",
28 "</svg>''')"
29 ],
30 "language": "python",
31 "metadata": {},
32 "outputs": [
33 {
34 "metadata": {},
35 "output_type": "pyout",
36 "prompt_number": 2,
37 "svg": [
38 "<svg height=\"100\" width=\"100\">\n",
39 " <circle cx=\"50\" cy=\"50\" fill=\"red\" r=\"40\" stroke=\"black\" stroke-width=\"2\"/>\n",
40 "</svg>"
41 ],
42 "text": [
43 "<IPython.core.display.SVG at 0x10428e150>"
44 ]
45 }
46 ],
47 "prompt_number": 2
48 }
49 ],
50 "metadata": {}
51 }
52 ]
53 } No newline at end of file
@@ -0,0 +1,55 b''
1 {
2 "metadata": {
3 "name": ""
4 },
5 "nbformat": 3,
6 "nbformat_minor": 0,
7 "worksheets": [
8 {
9 "cells": [
10 {
11 "cell_type": "code",
12 "collapsed": false,
13 "input": [
14 "raise Exception(\"message\")"
15 ],
16 "language": "python",
17 "metadata": {},
18 "outputs": [
19 {
20 "ename": "Exception",
21 "evalue": "message",
22 "output_type": "pyerr",
23 "traceback": [
24 "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[1;31mException\u001b[0m Traceback (most recent call last)",
25 "\u001b[1;32m<ipython-input-1-335814d14fc1>\u001b[0m in \u001b[0;36m<module>\u001b[1;34m()\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[1;32mraise\u001b[0m \u001b[0mException\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"message\"\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
26 "\u001b[1;31mException\u001b[0m: message"
27 ]
28 }
29 ],
30 "prompt_number": 1
31 },
32 {
33 "cell_type": "code",
34 "collapsed": false,
35 "input": [
36 "print('ok')"
37 ],
38 "language": "python",
39 "metadata": {},
40 "outputs": [
41 {
42 "output_type": "stream",
43 "stream": "stdout",
44 "text": [
45 "ok\n"
46 ]
47 }
48 ],
49 "prompt_number": 2
50 }
51 ],
52 "metadata": {}
53 }
54 ]
55 } No newline at end of file
@@ -0,0 +1,33 b''
1 {
2 "metadata": {
3 "name": ""
4 },
5 "nbformat": 3,
6 "nbformat_minor": 0,
7 "worksheets": [
8 {
9 "cells": [
10 {
11 "cell_type": "code",
12 "collapsed": false,
13 "input": [
14 "print('\u2603')"
15 ],
16 "language": "python",
17 "metadata": {},
18 "outputs": [
19 {
20 "output_type": "stream",
21 "stream": "stdout",
22 "text": [
23 "\u2603\n"
24 ]
25 }
26 ],
27 "prompt_number": 1
28 }
29 ],
30 "metadata": {}
31 }
32 ]
33 } No newline at end of file
1 NO CONTENT: new file 100644, binary diff hidden
@@ -0,0 +1,82 b''
1 """
2 Module with tests for the execute preprocessor.
3 """
4
5 # Copyright (c) IPython Development Team.
6 # Distributed under the terms of the Modified BSD License.
7
8 import copy
9 import glob
10 import os
11 import re
12
13 from IPython.nbformat import current as nbformat
14
15 from .base import PreprocessorTestsBase
16 from ..execute import ExecutePreprocessor
17
18 from IPython.nbconvert.filters import strip_ansi
19
20 addr_pat = re.compile(r'0x[0-9a-f]{7,9}')
21
22 class TestExecute(PreprocessorTestsBase):
23 """Contains test functions for execute.py"""
24
25 @staticmethod
26 def normalize_output(output):
27 """
28 Normalizes outputs for comparison.
29 """
30 output = dict(output)
31 if 'metadata' in output:
32 del output['metadata']
33 if 'text' in output:
34 output['text'] = re.sub(addr_pat, '<HEXADDR>', output['text'])
35 if 'svg' in output:
36 del output['text']
37 if 'traceback' in output:
38 tb = []
39 for line in output['traceback']:
40 tb.append(strip_ansi(line))
41 output['traceback'] = tb
42
43 return output
44
45
46 def assert_notebooks_equal(self, expected, actual):
47 expected_cells = expected['worksheets'][0]['cells']
48 actual_cells = actual['worksheets'][0]['cells']
49 assert len(expected_cells) == len(actual_cells)
50
51 for expected_cell, actual_cell in zip(expected_cells, actual_cells):
52 expected_outputs = expected_cell.get('outputs', [])
53 actual_outputs = actual_cell.get('outputs', [])
54 normalized_expected_outputs = list(map(self.normalize_output, expected_outputs))
55 normalized_actual_outputs = list(map(self.normalize_output, actual_outputs))
56 assert normalized_expected_outputs == normalized_actual_outputs
57
58
59 def build_preprocessor(self):
60 """Make an instance of a preprocessor"""
61 preprocessor = ExecutePreprocessor()
62 preprocessor.enabled = True
63 return preprocessor
64
65
66 def test_constructor(self):
67 """Can a ExecutePreprocessor be constructed?"""
68 self.build_preprocessor()
69
70
71 def test_run_notebooks(self):
72 """Runs a series of test notebooks and compares them to their actual output"""
73 current_dir = os.path.dirname(__file__)
74 input_files = glob.glob(os.path.join(current_dir, 'files', '*.ipynb'))
75 for filename in input_files:
76 with open(os.path.join(current_dir, 'files', filename)) as f:
77 input_nb = nbformat.read(f, 'ipynb')
78 res = self.build_resources()
79 preprocessor = self.build_preprocessor()
80 output_nb, _ = preprocessor(copy.deepcopy(input_nb), res)
81 self.assert_notebooks_equal(output_nb, input_nb)
82
@@ -0,0 +1,4 b''
1 New preprocessor for nbconvert that executes all the code cells in a notebook.
2 To run a notebook and save its output in a new notebook::
3
4 ipython nbconvert InputNotebook --ExecutePreprocessor.enabled=True --to notebook --output Executed
@@ -71,6 +71,7 b' class Exporter(LoggingConfigurable):'
71 71 'IPython.nbconvert.preprocessors.RevealHelpPreprocessor',
72 72 'IPython.nbconvert.preprocessors.LatexPreprocessor',
73 73 'IPython.nbconvert.preprocessors.ClearOutputPreprocessor',
74 'IPython.nbconvert.preprocessors.ExecutePreprocessor',
74 75 'IPython.nbconvert.preprocessors.HighlightMagicsPreprocessor'],
75 76 config=True,
76 77 help="""List of preprocessors available by default, by name, namespace,
@@ -8,6 +8,7 b' from .latex import LatexPreprocessor'
8 8 from .csshtmlheader import CSSHTMLHeaderPreprocessor
9 9 from .highlightmagics import HighlightMagicsPreprocessor
10 10 from .clearoutput import ClearOutputPreprocessor
11 from .execute import ExecutePreprocessor
11 12
12 13 # decorated function Preprocessors
13 14 from .coalescestreams import coalesce_streams
@@ -185,7 +185,11 b' def find_package_data():'
185 185 'IPython.html.tests' : js_tests,
186 186 'IPython.qt.console' : ['resources/icon/*.svg'],
187 187 'IPython.nbconvert' : nbconvert_templates +
188 ['tests/files/*.*', 'exporters/tests/files/*.*'],
188 [
189 'tests/files/*.*',
190 'exporters/tests/files/*.*',
191 'preprocessors/tests/files/*.*',
192 ],
189 193 'IPython.nbconvert.filters' : ['marked.js'],
190 194 'IPython.nbformat' : ['tests/*.ipynb','v3/v3.withref.json']
191 195 }
General Comments 0
You need to be logged in to leave comments. Login now