##// END OF EJS Templates
Add output to IPython directive when in verbatim mode....
chebee7i -
Show More
@@ -1,1071 +1,1081 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 Sphinx directive to support embedded IPython code.
3 Sphinx directive to support embedded IPython code.
4
4
5 This directive allows pasting of entire interactive IPython sessions, prompts
5 This directive allows pasting of entire interactive IPython sessions, prompts
6 and all, and their code will actually get re-executed at doc build time, with
6 and all, and their code will actually get re-executed at doc build time, with
7 all prompts renumbered sequentially. It also allows you to input code as a pure
7 all prompts renumbered sequentially. It also allows you to input code as a pure
8 python input by giving the argument python to the directive. The output looks
8 python input by giving the argument python to the directive. The output looks
9 like an interactive ipython section.
9 like an interactive ipython section.
10
10
11 To enable this directive, simply list it in your Sphinx ``conf.py`` file
11 To enable this directive, simply list it in your Sphinx ``conf.py`` file
12 (making sure the directory where you placed it is visible to sphinx, as is
12 (making sure the directory where you placed it is visible to sphinx, as is
13 needed for all Sphinx directives). For example, to enable syntax highlighting
13 needed for all Sphinx directives). For example, to enable syntax highlighting
14 and the IPython directive::
14 and the IPython directive::
15
15
16 extensions = ['IPython.sphinxext.ipython_console_highlighting',
16 extensions = ['IPython.sphinxext.ipython_console_highlighting',
17 'IPython.sphinxext.ipython_directive']
17 'IPython.sphinxext.ipython_directive']
18
18
19 The IPython directive outputs code-blocks with the language 'ipython'. So
19 The IPython directive outputs code-blocks with the language 'ipython'. So
20 if you do not have the syntax highlighting extension enabled as well, then
20 if you do not have the syntax highlighting extension enabled as well, then
21 all rendered code-blocks will be uncolored. By default this directive assumes
21 all rendered code-blocks will be uncolored. By default this directive assumes
22 that your prompts are unchanged IPython ones, but this can be customized.
22 that your prompts are unchanged IPython ones, but this can be customized.
23 The configurable options that can be placed in conf.py are:
23 The configurable options that can be placed in conf.py are:
24
24
25 ipython_savefig_dir:
25 ipython_savefig_dir:
26 The directory in which to save the figures. This is relative to the
26 The directory in which to save the figures. This is relative to the
27 Sphinx source directory. The default is `html_static_path`.
27 Sphinx source directory. The default is `html_static_path`.
28 ipython_rgxin:
28 ipython_rgxin:
29 The compiled regular expression to denote the start of IPython input
29 The compiled regular expression to denote the start of IPython input
30 lines. The default is re.compile('In \[(\d+)\]:\s?(.*)\s*'). You
30 lines. The default is re.compile('In \[(\d+)\]:\s?(.*)\s*'). You
31 shouldn't need to change this.
31 shouldn't need to change this.
32 ipython_rgxout:
32 ipython_rgxout:
33 The compiled regular expression to denote the start of IPython output
33 The compiled regular expression to denote the start of IPython output
34 lines. The default is re.compile('Out\[(\d+)\]:\s?(.*)\s*'). You
34 lines. The default is re.compile('Out\[(\d+)\]:\s?(.*)\s*'). You
35 shouldn't need to change this.
35 shouldn't need to change this.
36 ipython_promptin:
36 ipython_promptin:
37 The string to represent the IPython input prompt in the generated ReST.
37 The string to represent the IPython input prompt in the generated ReST.
38 The default is 'In [%d]:'. This expects that the line numbers are used
38 The default is 'In [%d]:'. This expects that the line numbers are used
39 in the prompt.
39 in the prompt.
40 ipython_promptout:
40 ipython_promptout:
41 The string to represent the IPython prompt in the generated ReST. The
41 The string to represent the IPython prompt in the generated ReST. The
42 default is 'Out [%d]:'. This expects that the line numbers are used
42 default is 'Out [%d]:'. This expects that the line numbers are used
43 in the prompt.
43 in the prompt.
44 ipython_mplbackend:
44 ipython_mplbackend:
45 The string which specifies if the embedded Sphinx shell should import
45 The string which specifies if the embedded Sphinx shell should import
46 Matplotlib and set the backend. The value specifies a backend that is
46 Matplotlib and set the backend. The value specifies a backend that is
47 passed to `matplotlib.use()` before any lines in `ipython_execlines` are
47 passed to `matplotlib.use()` before any lines in `ipython_execlines` are
48 executed. If not specified in conf.py, then the default value of 'agg' is
48 executed. If not specified in conf.py, then the default value of 'agg' is
49 used. To use the IPython directive without matplotlib as a dependency, set
49 used. To use the IPython directive without matplotlib as a dependency, set
50 the value to `None`. It may end up that matplotlib is still imported
50 the value to `None`. It may end up that matplotlib is still imported
51 if the user specifies so in `ipython_execlines` or makes use of the
51 if the user specifies so in `ipython_execlines` or makes use of the
52 @savefig pseudo decorator.
52 @savefig pseudo decorator.
53 ipython_execlines:
53 ipython_execlines:
54 A list of strings to be exec'd in the embedded Sphinx shell. Typical
54 A list of strings to be exec'd in the embedded Sphinx shell. Typical
55 usage is to make certain packages always available. Set this to an empty
55 usage is to make certain packages always available. Set this to an empty
56 list if you wish to have no imports always available. If specified in
56 list if you wish to have no imports always available. If specified in
57 conf.py as `None`, then it has the effect of making no imports available.
57 conf.py as `None`, then it has the effect of making no imports available.
58 If omitted from conf.py altogether, then the default value of
58 If omitted from conf.py altogether, then the default value of
59 ['import numpy as np', 'import matplotlib.pyplot as plt'] is used.
59 ['import numpy as np', 'import matplotlib.pyplot as plt'] is used.
60 ipython_holdcount
60 ipython_holdcount
61 When the @suppress pseudo-decorator is used, the execution count can be
61 When the @suppress pseudo-decorator is used, the execution count can be
62 incremented or not. The default behavior is to hold the execution count,
62 incremented or not. The default behavior is to hold the execution count,
63 corresponding to a value of `True`. Set this to `False` to increment
63 corresponding to a value of `True`. Set this to `False` to increment
64 the execution count after each suppressed command.
64 the execution count after each suppressed command.
65
65
66 As an example, to use the IPython directive when `matplotlib` is not available,
66 As an example, to use the IPython directive when `matplotlib` is not available,
67 one sets the backend to `None`::
67 one sets the backend to `None`::
68
68
69 ipython_mplbackend = None
69 ipython_mplbackend = None
70
70
71 An example usage of the directive is:
71 An example usage of the directive is:
72
72
73 .. code-block:: rst
73 .. code-block:: rst
74
74
75 .. ipython::
75 .. ipython::
76
76
77 In [1]: x = 1
77 In [1]: x = 1
78
78
79 In [2]: y = x**2
79 In [2]: y = x**2
80
80
81 In [3]: print(y)
81 In [3]: print(y)
82
82
83 See http://matplotlib.org/sampledoc/ipython_directive.html for additional
83 See http://matplotlib.org/sampledoc/ipython_directive.html for additional
84 documentation.
84 documentation.
85
85
86 Pseudo-Decorators
86 Pseudo-Decorators
87 =================
87 =================
88
88
89 In addition to the Pseudo-Decorators/options described at the above link,
89 In addition to the Pseudo-Decorators/options described at the above link,
90 several enhancements have been made.
90 several enhancements have been made.
91 The directive will emit a message to the console at build-time if
91 The directive will emit a message to the console at build-time if
92 code-execution resulted in an exception or warning. You can suppress these on
92 code-execution resulted in an exception or warning. You can suppress these on
93 a per-block basis by specifying the :okexcept: or :okwarning: options:
93 a per-block basis by specifying the :okexcept: or :okwarning: options:
94
94
95 .. code-block:: rst
95 .. code-block:: rst
96
96
97 .. ipython::
97 .. ipython::
98 :okexcept:
98 :okexcept:
99 :okwarning:
99 :okwarning:
100
100
101 In [1]: 1/0
101 In [1]: 1/0
102 In [2]: # raise warning.
102 In [2]: # raise warning.
103
103
104 ToDo
104 ToDo
105 ----
105 ----
106
106
107 - Turn the ad-hoc test() function into a real test suite.
107 - Turn the ad-hoc test() function into a real test suite.
108 - Break up ipython-specific functionality from matplotlib stuff into better
108 - Break up ipython-specific functionality from matplotlib stuff into better
109 separated code.
109 separated code.
110
110
111 Authors
111 Authors
112 -------
112 -------
113
113
114 - John D Hunter: orignal author.
114 - John D Hunter: orignal author.
115 - Fernando Perez: refactoring, documentation, cleanups, port to 0.11.
115 - Fernando Perez: refactoring, documentation, cleanups, port to 0.11.
116 - VΓ‘clavΕ milauer <eudoxos-AT-arcig.cz>: Prompt generalizations.
116 - VΓ‘clavΕ milauer <eudoxos-AT-arcig.cz>: Prompt generalizations.
117 - Skipper Seabold, refactoring, cleanups, pure python addition
117 - Skipper Seabold, refactoring, cleanups, pure python addition
118 """
118 """
119 from __future__ import print_function
119 from __future__ import print_function
120
120
121 #-----------------------------------------------------------------------------
121 #-----------------------------------------------------------------------------
122 # Imports
122 # Imports
123 #-----------------------------------------------------------------------------
123 #-----------------------------------------------------------------------------
124
124
125 # Stdlib
125 # Stdlib
126 import os
126 import os
127 import re
127 import re
128 import sys
128 import sys
129 import tempfile
129 import tempfile
130 import ast
130 import ast
131 import warnings
131 import warnings
132
132
133 # To keep compatibility with various python versions
133 # To keep compatibility with various python versions
134 try:
134 try:
135 from hashlib import md5
135 from hashlib import md5
136 except ImportError:
136 except ImportError:
137 from md5 import md5
137 from md5 import md5
138
138
139 # Third-party
139 # Third-party
140 import sphinx
140 import sphinx
141 from docutils.parsers.rst import directives
141 from docutils.parsers.rst import directives
142 from docutils import nodes
142 from docutils import nodes
143 from sphinx.util.compat import Directive
143 from sphinx.util.compat import Directive
144
144
145 # Our own
145 # Our own
146 from IPython import Config, InteractiveShell
146 from IPython import Config, InteractiveShell
147 from IPython.core.profiledir import ProfileDir
147 from IPython.core.profiledir import ProfileDir
148 from IPython.utils import io
148 from IPython.utils import io
149 from IPython.utils.py3compat import PY3
149 from IPython.utils.py3compat import PY3
150
150
151 if PY3:
151 if PY3:
152 from io import StringIO
152 from io import StringIO
153 else:
153 else:
154 from StringIO import StringIO
154 from StringIO import StringIO
155
155
156 #-----------------------------------------------------------------------------
156 #-----------------------------------------------------------------------------
157 # Globals
157 # Globals
158 #-----------------------------------------------------------------------------
158 #-----------------------------------------------------------------------------
159 # for tokenizing blocks
159 # for tokenizing blocks
160 COMMENT, INPUT, OUTPUT = range(3)
160 COMMENT, INPUT, OUTPUT = range(3)
161
161
162 #-----------------------------------------------------------------------------
162 #-----------------------------------------------------------------------------
163 # Functions and class declarations
163 # Functions and class declarations
164 #-----------------------------------------------------------------------------
164 #-----------------------------------------------------------------------------
165
165
166 def block_parser(part, rgxin, rgxout, fmtin, fmtout):
166 def block_parser(part, rgxin, rgxout, fmtin, fmtout):
167 """
167 """
168 part is a string of ipython text, comprised of at most one
168 part is a string of ipython text, comprised of at most one
169 input, one ouput, comments, and blank lines. The block parser
169 input, one ouput, comments, and blank lines. The block parser
170 parses the text into a list of::
170 parses the text into a list of::
171
171
172 blocks = [ (TOKEN0, data0), (TOKEN1, data1), ...]
172 blocks = [ (TOKEN0, data0), (TOKEN1, data1), ...]
173
173
174 where TOKEN is one of [COMMENT | INPUT | OUTPUT ] and
174 where TOKEN is one of [COMMENT | INPUT | OUTPUT ] and
175 data is, depending on the type of token::
175 data is, depending on the type of token::
176
176
177 COMMENT : the comment string
177 COMMENT : the comment string
178
178
179 INPUT: the (DECORATOR, INPUT_LINE, REST) where
179 INPUT: the (DECORATOR, INPUT_LINE, REST) where
180 DECORATOR: the input decorator (or None)
180 DECORATOR: the input decorator (or None)
181 INPUT_LINE: the input as string (possibly multi-line)
181 INPUT_LINE: the input as string (possibly multi-line)
182 REST : any stdout generated by the input line (not OUTPUT)
182 REST : any stdout generated by the input line (not OUTPUT)
183
183
184 OUTPUT: the output string, possibly multi-line
184 OUTPUT: the output string, possibly multi-line
185
185
186 """
186 """
187 block = []
187 block = []
188 lines = part.split('\n')
188 lines = part.split('\n')
189 N = len(lines)
189 N = len(lines)
190 i = 0
190 i = 0
191 decorator = None
191 decorator = None
192 while 1:
192 while 1:
193
193
194 if i==N:
194 if i==N:
195 # nothing left to parse -- the last line
195 # nothing left to parse -- the last line
196 break
196 break
197
197
198 line = lines[i]
198 line = lines[i]
199 i += 1
199 i += 1
200 line_stripped = line.strip()
200 line_stripped = line.strip()
201 if line_stripped.startswith('#'):
201 if line_stripped.startswith('#'):
202 block.append((COMMENT, line))
202 block.append((COMMENT, line))
203 continue
203 continue
204
204
205 if line_stripped.startswith('@'):
205 if line_stripped.startswith('@'):
206 # we're assuming at most one decorator -- may need to
206 # we're assuming at most one decorator -- may need to
207 # rethink
207 # rethink
208 decorator = line_stripped
208 decorator = line_stripped
209 continue
209 continue
210
210
211 # does this look like an input line?
211 # does this look like an input line?
212 matchin = rgxin.match(line)
212 matchin = rgxin.match(line)
213 if matchin:
213 if matchin:
214 lineno, inputline = int(matchin.group(1)), matchin.group(2)
214 lineno, inputline = int(matchin.group(1)), matchin.group(2)
215
215
216 # the ....: continuation string
216 # the ....: continuation string
217 continuation = ' %s:'%''.join(['.']*(len(str(lineno))+2))
217 continuation = ' %s:'%''.join(['.']*(len(str(lineno))+2))
218 Nc = len(continuation)
218 Nc = len(continuation)
219 # input lines can continue on for more than one line, if
219 # input lines can continue on for more than one line, if
220 # we have a '\' line continuation char or a function call
220 # we have a '\' line continuation char or a function call
221 # echo line 'print'. The input line can only be
221 # echo line 'print'. The input line can only be
222 # terminated by the end of the block or an output line, so
222 # terminated by the end of the block or an output line, so
223 # we parse out the rest of the input line if it is
223 # we parse out the rest of the input line if it is
224 # multiline as well as any echo text
224 # multiline as well as any echo text
225
225
226 rest = []
226 rest = []
227 while i<N:
227 while i<N:
228
228
229 # look ahead; if the next line is blank, or a comment, or
229 # look ahead; if the next line is blank, or a comment, or
230 # an output line, we're done
230 # an output line, we're done
231
231
232 nextline = lines[i]
232 nextline = lines[i]
233 matchout = rgxout.match(nextline)
233 matchout = rgxout.match(nextline)
234 #print "nextline=%s, continuation=%s, starts=%s"%(nextline, continuation, nextline.startswith(continuation))
234 #print "nextline=%s, continuation=%s, starts=%s"%(nextline, continuation, nextline.startswith(continuation))
235 if matchout or nextline.startswith('#'):
235 if matchout or nextline.startswith('#'):
236 break
236 break
237 elif nextline.startswith(continuation):
237 elif nextline.startswith(continuation):
238 # The default ipython_rgx* treat the space following the colon as optional.
238 # The default ipython_rgx* treat the space following the colon as optional.
239 # However, If the space is there we must consume it or code
239 # However, If the space is there we must consume it or code
240 # employing the cython_magic extension will fail to execute.
240 # employing the cython_magic extension will fail to execute.
241 #
241 #
242 # This works with the default ipython_rgx* patterns,
242 # This works with the default ipython_rgx* patterns,
243 # If you modify them, YMMV.
243 # If you modify them, YMMV.
244 nextline = nextline[Nc:]
244 nextline = nextline[Nc:]
245 if nextline and nextline[0] == ' ':
245 if nextline and nextline[0] == ' ':
246 nextline = nextline[1:]
246 nextline = nextline[1:]
247
247
248 inputline += '\n' + nextline
248 inputline += '\n' + nextline
249 else:
249 else:
250 rest.append(nextline)
250 rest.append(nextline)
251 i+= 1
251 i+= 1
252
252
253 block.append((INPUT, (decorator, inputline, '\n'.join(rest))))
253 block.append((INPUT, (decorator, inputline, '\n'.join(rest))))
254 continue
254 continue
255
255
256 # if it looks like an output line grab all the text to the end
256 # if it looks like an output line grab all the text to the end
257 # of the block
257 # of the block
258 matchout = rgxout.match(line)
258 matchout = rgxout.match(line)
259 if matchout:
259 if matchout:
260 lineno, output = int(matchout.group(1)), matchout.group(2)
260 lineno, output = int(matchout.group(1)), matchout.group(2)
261 if i<N-1:
261 if i<N-1:
262 output = '\n'.join([output] + lines[i:])
262 output = '\n'.join([output] + lines[i:])
263
263
264 block.append((OUTPUT, output))
264 block.append((OUTPUT, output))
265 break
265 break
266
266
267 return block
267 return block
268
268
269
269
270 class EmbeddedSphinxShell(object):
270 class EmbeddedSphinxShell(object):
271 """An embedded IPython instance to run inside Sphinx"""
271 """An embedded IPython instance to run inside Sphinx"""
272
272
273 def __init__(self, exec_lines=None):
273 def __init__(self, exec_lines=None):
274
274
275 self.cout = StringIO()
275 self.cout = StringIO()
276
276
277 if exec_lines is None:
277 if exec_lines is None:
278 exec_lines = []
278 exec_lines = []
279
279
280 # Create config object for IPython
280 # Create config object for IPython
281 config = Config()
281 config = Config()
282 config.InteractiveShell.autocall = False
282 config.InteractiveShell.autocall = False
283 config.InteractiveShell.autoindent = False
283 config.InteractiveShell.autoindent = False
284 config.InteractiveShell.colors = 'NoColor'
284 config.InteractiveShell.colors = 'NoColor'
285
285
286 # create a profile so instance history isn't saved
286 # create a profile so instance history isn't saved
287 tmp_profile_dir = tempfile.mkdtemp(prefix='profile_')
287 tmp_profile_dir = tempfile.mkdtemp(prefix='profile_')
288 profname = 'auto_profile_sphinx_build'
288 profname = 'auto_profile_sphinx_build'
289 pdir = os.path.join(tmp_profile_dir,profname)
289 pdir = os.path.join(tmp_profile_dir,profname)
290 profile = ProfileDir.create_profile_dir(pdir)
290 profile = ProfileDir.create_profile_dir(pdir)
291
291
292 # Create and initialize global ipython, but don't start its mainloop.
292 # Create and initialize global ipython, but don't start its mainloop.
293 # This will persist across different EmbededSphinxShell instances.
293 # This will persist across different EmbededSphinxShell instances.
294 IP = InteractiveShell.instance(config=config, profile_dir=profile)
294 IP = InteractiveShell.instance(config=config, profile_dir=profile)
295
295
296 # io.stdout redirect must be done after instantiating InteractiveShell
296 # io.stdout redirect must be done after instantiating InteractiveShell
297 io.stdout = self.cout
297 io.stdout = self.cout
298 io.stderr = self.cout
298 io.stderr = self.cout
299
299
300 # For debugging, so we can see normal output, use this:
300 # For debugging, so we can see normal output, use this:
301 #from IPython.utils.io import Tee
301 #from IPython.utils.io import Tee
302 #io.stdout = Tee(self.cout, channel='stdout') # dbg
302 #io.stdout = Tee(self.cout, channel='stdout') # dbg
303 #io.stderr = Tee(self.cout, channel='stderr') # dbg
303 #io.stderr = Tee(self.cout, channel='stderr') # dbg
304
304
305 # Store a few parts of IPython we'll need.
305 # Store a few parts of IPython we'll need.
306 self.IP = IP
306 self.IP = IP
307 self.user_ns = self.IP.user_ns
307 self.user_ns = self.IP.user_ns
308 self.user_global_ns = self.IP.user_global_ns
308 self.user_global_ns = self.IP.user_global_ns
309
309
310 self.input = ''
310 self.input = ''
311 self.output = ''
311 self.output = ''
312
312
313 self.is_verbatim = False
313 self.is_verbatim = False
314 self.is_doctest = False
314 self.is_doctest = False
315 self.is_suppress = False
315 self.is_suppress = False
316
316
317 # Optionally, provide more detailed information to shell.
317 # Optionally, provide more detailed information to shell.
318 # this is assigned by the SetUp method of IPythonDirective
318 # this is assigned by the SetUp method of IPythonDirective
319 # to point at itself.
319 # to point at itself.
320 #
320 #
321 # So, you can access handy things at self.directive.state
321 # So, you can access handy things at self.directive.state
322 self.directive = None
322 self.directive = None
323
323
324 # on the first call to the savefig decorator, we'll import
324 # on the first call to the savefig decorator, we'll import
325 # pyplot as plt so we can make a call to the plt.gcf().savefig
325 # pyplot as plt so we can make a call to the plt.gcf().savefig
326 self._pyplot_imported = False
326 self._pyplot_imported = False
327
327
328 # Prepopulate the namespace.
328 # Prepopulate the namespace.
329 for line in exec_lines:
329 for line in exec_lines:
330 self.process_input_line(line, store_history=False)
330 self.process_input_line(line, store_history=False)
331
331
332 def clear_cout(self):
332 def clear_cout(self):
333 self.cout.seek(0)
333 self.cout.seek(0)
334 self.cout.truncate(0)
334 self.cout.truncate(0)
335
335
336 def process_input_line(self, line, store_history=True):
336 def process_input_line(self, line, store_history=True):
337 """process the input, capturing stdout"""
337 """process the input, capturing stdout"""
338
338
339 stdout = sys.stdout
339 stdout = sys.stdout
340 splitter = self.IP.input_splitter
340 splitter = self.IP.input_splitter
341 try:
341 try:
342 sys.stdout = self.cout
342 sys.stdout = self.cout
343 splitter.push(line)
343 splitter.push(line)
344 more = splitter.push_accepts_more()
344 more = splitter.push_accepts_more()
345 if not more:
345 if not more:
346 source_raw = splitter.raw_reset()
346 source_raw = splitter.raw_reset()
347 self.IP.run_cell(source_raw, store_history=store_history)
347 self.IP.run_cell(source_raw, store_history=store_history)
348 finally:
348 finally:
349 sys.stdout = stdout
349 sys.stdout = stdout
350
350
351 def process_image(self, decorator):
351 def process_image(self, decorator):
352 """
352 """
353 # build out an image directive like
353 # build out an image directive like
354 # .. image:: somefile.png
354 # .. image:: somefile.png
355 # :width 4in
355 # :width 4in
356 #
356 #
357 # from an input like
357 # from an input like
358 # savefig somefile.png width=4in
358 # savefig somefile.png width=4in
359 """
359 """
360 savefig_dir = self.savefig_dir
360 savefig_dir = self.savefig_dir
361 source_dir = self.source_dir
361 source_dir = self.source_dir
362 saveargs = decorator.split(' ')
362 saveargs = decorator.split(' ')
363 filename = saveargs[1]
363 filename = saveargs[1]
364 # insert relative path to image file in source
364 # insert relative path to image file in source
365 outfile = os.path.relpath(os.path.join(savefig_dir,filename),
365 outfile = os.path.relpath(os.path.join(savefig_dir,filename),
366 source_dir)
366 source_dir)
367
367
368 imagerows = ['.. image:: %s'%outfile]
368 imagerows = ['.. image:: %s'%outfile]
369
369
370 for kwarg in saveargs[2:]:
370 for kwarg in saveargs[2:]:
371 arg, val = kwarg.split('=')
371 arg, val = kwarg.split('=')
372 arg = arg.strip()
372 arg = arg.strip()
373 val = val.strip()
373 val = val.strip()
374 imagerows.append(' :%s: %s'%(arg, val))
374 imagerows.append(' :%s: %s'%(arg, val))
375
375
376 image_file = os.path.basename(outfile) # only return file name
376 image_file = os.path.basename(outfile) # only return file name
377 image_directive = '\n'.join(imagerows)
377 image_directive = '\n'.join(imagerows)
378 return image_file, image_directive
378 return image_file, image_directive
379
379
380 # Callbacks for each type of token
380 # Callbacks for each type of token
381 def process_input(self, data, input_prompt, lineno):
381 def process_input(self, data, input_prompt, lineno):
382 """
382 """
383 Process data block for INPUT token.
383 Process data block for INPUT token.
384
384
385 """
385 """
386 decorator, input, rest = data
386 decorator, input, rest = data
387 image_file = None
387 image_file = None
388 image_directive = None
388 image_directive = None
389
389
390 is_verbatim = decorator=='@verbatim' or self.is_verbatim
390 is_verbatim = decorator=='@verbatim' or self.is_verbatim
391 is_doctest = (decorator is not None and \
391 is_doctest = (decorator is not None and \
392 decorator.startswith('@doctest')) or self.is_doctest
392 decorator.startswith('@doctest')) or self.is_doctest
393 is_suppress = decorator=='@suppress' or self.is_suppress
393 is_suppress = decorator=='@suppress' or self.is_suppress
394 is_okexcept = decorator=='@okexcept' or self.is_okexcept
394 is_okexcept = decorator=='@okexcept' or self.is_okexcept
395 is_okwarning = decorator=='@okwarning' or self.is_okwarning
395 is_okwarning = decorator=='@okwarning' or self.is_okwarning
396 is_savefig = decorator is not None and \
396 is_savefig = decorator is not None and \
397 decorator.startswith('@savefig')
397 decorator.startswith('@savefig')
398
398
399 input_lines = input.split('\n')
399 input_lines = input.split('\n')
400 if len(input_lines) > 1:
400 if len(input_lines) > 1:
401 if input_lines[-1] != "":
401 if input_lines[-1] != "":
402 input_lines.append('') # make sure there's a blank line
402 input_lines.append('') # make sure there's a blank line
403 # so splitter buffer gets reset
403 # so splitter buffer gets reset
404
404
405 continuation = ' %s:'%''.join(['.']*(len(str(lineno))+2))
405 continuation = ' %s:'%''.join(['.']*(len(str(lineno))+2))
406
406
407 if is_savefig:
407 if is_savefig:
408 image_file, image_directive = self.process_image(decorator)
408 image_file, image_directive = self.process_image(decorator)
409
409
410 ret = []
410 ret = []
411 is_semicolon = False
411 is_semicolon = False
412
412
413 # Hold the execution count, if requested to do so.
413 # Hold the execution count, if requested to do so.
414 if is_suppress and self.hold_count:
414 if is_suppress and self.hold_count:
415 store_history = False
415 store_history = False
416 else:
416 else:
417 store_history = True
417 store_history = True
418
418
419 # Note: catch_warnings is not thread safe
419 # Note: catch_warnings is not thread safe
420 with warnings.catch_warnings(record=True) as ws:
420 with warnings.catch_warnings(record=True) as ws:
421 for i, line in enumerate(input_lines):
421 for i, line in enumerate(input_lines):
422 if line.endswith(';'):
422 if line.endswith(';'):
423 is_semicolon = True
423 is_semicolon = True
424
424
425 if i == 0:
425 if i == 0:
426 # process the first input line
426 # process the first input line
427 if is_verbatim:
427 if is_verbatim:
428 self.process_input_line('')
428 self.process_input_line('')
429 self.IP.execution_count += 1 # increment it anyway
429 self.IP.execution_count += 1 # increment it anyway
430 else:
430 else:
431 # only submit the line in non-verbatim mode
431 # only submit the line in non-verbatim mode
432 self.process_input_line(line, store_history=store_history)
432 self.process_input_line(line, store_history=store_history)
433 formatted_line = '%s %s'%(input_prompt, line)
433 formatted_line = '%s %s'%(input_prompt, line)
434 else:
434 else:
435 # process a continuation line
435 # process a continuation line
436 if not is_verbatim:
436 if not is_verbatim:
437 self.process_input_line(line, store_history=store_history)
437 self.process_input_line(line, store_history=store_history)
438
438
439 formatted_line = '%s %s'%(continuation, line)
439 formatted_line = '%s %s'%(continuation, line)
440
440
441 if not is_suppress:
441 if not is_suppress:
442 ret.append(formatted_line)
442 ret.append(formatted_line)
443
443
444 if not is_suppress and len(rest.strip()) and is_verbatim:
444 if not is_suppress and len(rest.strip()) and is_verbatim:
445 # the "rest" is the standard output of the
445 # the "rest" is the standard output of the
446 # input, which needs to be added in
446 # input, which needs to be added in
447 # verbatim mode
447 # verbatim mode
448 ret.append(rest)
448 ret.append(rest)
449
449
450 self.cout.seek(0)
450 self.cout.seek(0)
451 output = self.cout.read()
451 output = self.cout.read()
452 if not is_suppress and not is_semicolon:
452 if not is_suppress and not is_semicolon:
453 ret.append(output)
453 ret.append(output)
454 elif is_semicolon: # get spacing right
454 elif is_semicolon: # get spacing right
455 ret.append('')
455 ret.append('')
456
456
457 # context information
457 # context information
458 filename = "Unknown"
458 filename = "Unknown"
459 lineno = 0
459 lineno = 0
460 if self.directive.state:
460 if self.directive.state:
461 filename = self.directive.state.document.current_source
461 filename = self.directive.state.document.current_source
462 lineno = self.directive.state.document.current_line
462 lineno = self.directive.state.document.current_line
463
463
464 # output any exceptions raised during execution to stdout
464 # output any exceptions raised during execution to stdout
465 # unless :okexcept: has been specified.
465 # unless :okexcept: has been specified.
466 if not is_okexcept and "Traceback" in output:
466 if not is_okexcept and "Traceback" in output:
467 s = "\nException in %s at block ending on line %s\n" % (filename, lineno)
467 s = "\nException in %s at block ending on line %s\n" % (filename, lineno)
468 s += "Specify :okexcept: as an option in the ipython:: block to suppress this message\n"
468 s += "Specify :okexcept: as an option in the ipython:: block to suppress this message\n"
469 sys.stdout.write('\n\n>>>' + ('-' * 73))
469 sys.stdout.write('\n\n>>>' + ('-' * 73))
470 sys.stdout.write(s)
470 sys.stdout.write(s)
471 sys.stdout.write(output)
471 sys.stdout.write(output)
472 sys.stdout.write('<<<' + ('-' * 73) + '\n\n')
472 sys.stdout.write('<<<' + ('-' * 73) + '\n\n')
473
473
474 # output any warning raised during execution to stdout
474 # output any warning raised during execution to stdout
475 # unless :okwarning: has been specified.
475 # unless :okwarning: has been specified.
476 if not is_okwarning:
476 if not is_okwarning:
477 for w in ws:
477 for w in ws:
478 s = "\nWarning in %s at block ending on line %s\n" % (filename, lineno)
478 s = "\nWarning in %s at block ending on line %s\n" % (filename, lineno)
479 s += "Specify :okwarning: as an option in the ipython:: block to suppress this message\n"
479 s += "Specify :okwarning: as an option in the ipython:: block to suppress this message\n"
480 sys.stdout.write('\n\n>>>' + ('-' * 73))
480 sys.stdout.write('\n\n>>>' + ('-' * 73))
481 sys.stdout.write(s)
481 sys.stdout.write(s)
482 sys.stdout.write(('-' * 76) + '\n')
482 sys.stdout.write(('-' * 76) + '\n')
483 s=warnings.formatwarning(w.message, w.category,
483 s=warnings.formatwarning(w.message, w.category,
484 w.filename, w.lineno, w.line)
484 w.filename, w.lineno, w.line)
485 sys.stdout.write(s)
485 sys.stdout.write(s)
486 sys.stdout.write('<<<' + ('-' * 73) + '\n')
486 sys.stdout.write('<<<' + ('-' * 73) + '\n')
487
487
488 self.cout.truncate(0)
488 self.cout.truncate(0)
489 return (ret, input_lines, output, is_doctest, decorator, image_file,
489 return (ret, input_lines, output, is_doctest, decorator, image_file,
490 image_directive)
490 image_directive)
491
491
492
492
493 def process_output(self, data, output_prompt,
493 def process_output(self, data, output_prompt,
494 input_lines, output, is_doctest, decorator, image_file):
494 input_lines, output, is_doctest, decorator, image_file):
495 """
495 """
496 Process data block for OUTPUT token.
496 Process data block for OUTPUT token.
497
497
498 """
498 """
499 TAB = ' ' * 4
499 TAB = ' ' * 4
500
500
501 if is_doctest and output is not None:
501 if is_doctest and output is not None:
502
502
503 found = output
503 found = output
504 found = found.strip()
504 found = found.strip()
505 submitted = data.strip()
505 submitted = data.strip()
506
506
507 if self.directive is None:
507 if self.directive is None:
508 source = 'Unavailable'
508 source = 'Unavailable'
509 content = 'Unavailable'
509 content = 'Unavailable'
510 else:
510 else:
511 source = self.directive.state.document.current_source
511 source = self.directive.state.document.current_source
512 content = self.directive.content
512 content = self.directive.content
513 # Add tabs and join into a single string.
513 # Add tabs and join into a single string.
514 content = '\n'.join([TAB + line for line in content])
514 content = '\n'.join([TAB + line for line in content])
515
515
516 # Make sure the output contains the output prompt.
516 # Make sure the output contains the output prompt.
517 ind = found.find(output_prompt)
517 ind = found.find(output_prompt)
518 if ind < 0:
518 if ind < 0:
519 e = ('output does not contain output prompt\n\n'
519 e = ('output does not contain output prompt\n\n'
520 'Document source: {0}\n\n'
520 'Document source: {0}\n\n'
521 'Raw content: \n{1}\n\n'
521 'Raw content: \n{1}\n\n'
522 'Input line(s):\n{TAB}{2}\n\n'
522 'Input line(s):\n{TAB}{2}\n\n'
523 'Output line(s):\n{TAB}{3}\n\n')
523 'Output line(s):\n{TAB}{3}\n\n')
524 e = e.format(source, content, '\n'.join(input_lines),
524 e = e.format(source, content, '\n'.join(input_lines),
525 repr(found), TAB=TAB)
525 repr(found), TAB=TAB)
526 raise RuntimeError(e)
526 raise RuntimeError(e)
527 found = found[len(output_prompt):].strip()
527 found = found[len(output_prompt):].strip()
528
528
529 # Handle the actual doctest comparison.
529 # Handle the actual doctest comparison.
530 if decorator.strip() == '@doctest':
530 if decorator.strip() == '@doctest':
531 # Standard doctest
531 # Standard doctest
532 if found != submitted:
532 if found != submitted:
533 e = ('doctest failure\n\n'
533 e = ('doctest failure\n\n'
534 'Document source: {0}\n\n'
534 'Document source: {0}\n\n'
535 'Raw content: \n{1}\n\n'
535 'Raw content: \n{1}\n\n'
536 'On input line(s):\n{TAB}{2}\n\n'
536 'On input line(s):\n{TAB}{2}\n\n'
537 'we found output:\n{TAB}{3}\n\n'
537 'we found output:\n{TAB}{3}\n\n'
538 'instead of the expected:\n{TAB}{4}\n\n')
538 'instead of the expected:\n{TAB}{4}\n\n')
539 e = e.format(source, content, '\n'.join(input_lines),
539 e = e.format(source, content, '\n'.join(input_lines),
540 repr(found), repr(submitted), TAB=TAB)
540 repr(found), repr(submitted), TAB=TAB)
541 raise RuntimeError(e)
541 raise RuntimeError(e)
542 else:
542 else:
543 self.custom_doctest(decorator, input_lines, found, submitted)
543 self.custom_doctest(decorator, input_lines, found, submitted)
544
544
545 # Make sure output is processed when in verbatim mode.
546 # https://github.com/ipython/ipython/issues/5776
547 is_verbatim = decorator=='@verbatim' or self.is_verbatim
548 if is_verbatim:
549 out_data = data
550 else:
551 out_data = None
552
553 return out_data
554
545 def process_comment(self, data):
555 def process_comment(self, data):
546 """Process data fPblock for COMMENT token."""
556 """Process data fPblock for COMMENT token."""
547 if not self.is_suppress:
557 if not self.is_suppress:
548 return [data]
558 return [data]
549
559
550 def save_image(self, image_file):
560 def save_image(self, image_file):
551 """
561 """
552 Saves the image file to disk.
562 Saves the image file to disk.
553 """
563 """
554 self.ensure_pyplot()
564 self.ensure_pyplot()
555 command = 'plt.gcf().savefig("%s")'%image_file
565 command = 'plt.gcf().savefig("%s")'%image_file
556 #print 'SAVEFIG', command # dbg
566 #print 'SAVEFIG', command # dbg
557 self.process_input_line('bookmark ipy_thisdir', store_history=False)
567 self.process_input_line('bookmark ipy_thisdir', store_history=False)
558 self.process_input_line('cd -b ipy_savedir', store_history=False)
568 self.process_input_line('cd -b ipy_savedir', store_history=False)
559 self.process_input_line(command, store_history=False)
569 self.process_input_line(command, store_history=False)
560 self.process_input_line('cd -b ipy_thisdir', store_history=False)
570 self.process_input_line('cd -b ipy_thisdir', store_history=False)
561 self.process_input_line('bookmark -d ipy_thisdir', store_history=False)
571 self.process_input_line('bookmark -d ipy_thisdir', store_history=False)
562 self.clear_cout()
572 self.clear_cout()
563
573
564 def process_block(self, block):
574 def process_block(self, block):
565 """
575 """
566 process block from the block_parser and return a list of processed lines
576 process block from the block_parser and return a list of processed lines
567 """
577 """
568 ret = []
578 ret = []
569 output = None
579 output = None
570 input_lines = None
580 input_lines = None
571 lineno = self.IP.execution_count
581 lineno = self.IP.execution_count
572
582
573 input_prompt = self.promptin % lineno
583 input_prompt = self.promptin % lineno
574 output_prompt = self.promptout % lineno
584 output_prompt = self.promptout % lineno
575 image_file = None
585 image_file = None
576 image_directive = None
586 image_directive = None
577
587
578 for token, data in block:
588 for token, data in block:
579 if token == COMMENT:
589 if token == COMMENT:
580 out_data = self.process_comment(data)
590 out_data = self.process_comment(data)
581 elif token == INPUT:
591 elif token == INPUT:
582 (out_data, input_lines, output, is_doctest, decorator,
592 (out_data, input_lines, output, is_doctest, decorator,
583 image_file, image_directive) = \
593 image_file, image_directive) = \
584 self.process_input(data, input_prompt, lineno)
594 self.process_input(data, input_prompt, lineno)
585 elif token == OUTPUT:
595 elif token == OUTPUT:
586 out_data = \
596 out_data = \
587 self.process_output(data, output_prompt,
597 self.process_output(data, output_prompt,
588 input_lines, output, is_doctest,
598 input_lines, output, is_doctest,
589 decorator, image_file)
599 decorator, image_file)
590 if out_data:
600 if out_data:
591 ret.extend(out_data)
601 ret.extend(out_data)
592
602
593 # save the image files
603 # save the image files
594 if image_file is not None:
604 if image_file is not None:
595 self.save_image(image_file)
605 self.save_image(image_file)
596
606
597 return ret, image_directive
607 return ret, image_directive
598
608
599 def ensure_pyplot(self):
609 def ensure_pyplot(self):
600 """
610 """
601 Ensures that pyplot has been imported into the embedded IPython shell.
611 Ensures that pyplot has been imported into the embedded IPython shell.
602
612
603 Also, makes sure to set the backend appropriately if not set already.
613 Also, makes sure to set the backend appropriately if not set already.
604
614
605 """
615 """
606 # We are here if the @figure pseudo decorator was used. Thus, it's
616 # We are here if the @figure pseudo decorator was used. Thus, it's
607 # possible that we could be here even if python_mplbackend were set to
617 # possible that we could be here even if python_mplbackend were set to
608 # `None`. That's also strange and perhaps worthy of raising an
618 # `None`. That's also strange and perhaps worthy of raising an
609 # exception, but for now, we just set the backend to 'agg'.
619 # exception, but for now, we just set the backend to 'agg'.
610
620
611 if not self._pyplot_imported:
621 if not self._pyplot_imported:
612 if 'matplotlib.backends' not in sys.modules:
622 if 'matplotlib.backends' not in sys.modules:
613 # Then ipython_matplotlib was set to None but there was a
623 # Then ipython_matplotlib was set to None but there was a
614 # call to the @figure decorator (and ipython_execlines did
624 # call to the @figure decorator (and ipython_execlines did
615 # not set a backend).
625 # not set a backend).
616 #raise Exception("No backend was set, but @figure was used!")
626 #raise Exception("No backend was set, but @figure was used!")
617 import matplotlib
627 import matplotlib
618 matplotlib.use('agg')
628 matplotlib.use('agg')
619
629
620 # Always import pyplot into embedded shell.
630 # Always import pyplot into embedded shell.
621 self.process_input_line('import matplotlib.pyplot as plt',
631 self.process_input_line('import matplotlib.pyplot as plt',
622 store_history=False)
632 store_history=False)
623 self._pyplot_imported = True
633 self._pyplot_imported = True
624
634
625 def process_pure_python(self, content):
635 def process_pure_python(self, content):
626 """
636 """
627 content is a list of strings. it is unedited directive content
637 content is a list of strings. it is unedited directive content
628
638
629 This runs it line by line in the InteractiveShell, prepends
639 This runs it line by line in the InteractiveShell, prepends
630 prompts as needed capturing stderr and stdout, then returns
640 prompts as needed capturing stderr and stdout, then returns
631 the content as a list as if it were ipython code
641 the content as a list as if it were ipython code
632 """
642 """
633 output = []
643 output = []
634 savefig = False # keep up with this to clear figure
644 savefig = False # keep up with this to clear figure
635 multiline = False # to handle line continuation
645 multiline = False # to handle line continuation
636 multiline_start = None
646 multiline_start = None
637 fmtin = self.promptin
647 fmtin = self.promptin
638
648
639 ct = 0
649 ct = 0
640
650
641 for lineno, line in enumerate(content):
651 for lineno, line in enumerate(content):
642
652
643 line_stripped = line.strip()
653 line_stripped = line.strip()
644 if not len(line):
654 if not len(line):
645 output.append(line)
655 output.append(line)
646 continue
656 continue
647
657
648 # handle decorators
658 # handle decorators
649 if line_stripped.startswith('@'):
659 if line_stripped.startswith('@'):
650 output.extend([line])
660 output.extend([line])
651 if 'savefig' in line:
661 if 'savefig' in line:
652 savefig = True # and need to clear figure
662 savefig = True # and need to clear figure
653 continue
663 continue
654
664
655 # handle comments
665 # handle comments
656 if line_stripped.startswith('#'):
666 if line_stripped.startswith('#'):
657 output.extend([line])
667 output.extend([line])
658 continue
668 continue
659
669
660 # deal with lines checking for multiline
670 # deal with lines checking for multiline
661 continuation = u' %s:'% ''.join(['.']*(len(str(ct))+2))
671 continuation = u' %s:'% ''.join(['.']*(len(str(ct))+2))
662 if not multiline:
672 if not multiline:
663 modified = u"%s %s" % (fmtin % ct, line_stripped)
673 modified = u"%s %s" % (fmtin % ct, line_stripped)
664 output.append(modified)
674 output.append(modified)
665 ct += 1
675 ct += 1
666 try:
676 try:
667 ast.parse(line_stripped)
677 ast.parse(line_stripped)
668 output.append(u'')
678 output.append(u'')
669 except Exception: # on a multiline
679 except Exception: # on a multiline
670 multiline = True
680 multiline = True
671 multiline_start = lineno
681 multiline_start = lineno
672 else: # still on a multiline
682 else: # still on a multiline
673 modified = u'%s %s' % (continuation, line)
683 modified = u'%s %s' % (continuation, line)
674 output.append(modified)
684 output.append(modified)
675
685
676 # if the next line is indented, it should be part of multiline
686 # if the next line is indented, it should be part of multiline
677 if len(content) > lineno + 1:
687 if len(content) > lineno + 1:
678 nextline = content[lineno + 1]
688 nextline = content[lineno + 1]
679 if len(nextline) - len(nextline.lstrip()) > 3:
689 if len(nextline) - len(nextline.lstrip()) > 3:
680 continue
690 continue
681 try:
691 try:
682 mod = ast.parse(
692 mod = ast.parse(
683 '\n'.join(content[multiline_start:lineno+1]))
693 '\n'.join(content[multiline_start:lineno+1]))
684 if isinstance(mod.body[0], ast.FunctionDef):
694 if isinstance(mod.body[0], ast.FunctionDef):
685 # check to see if we have the whole function
695 # check to see if we have the whole function
686 for element in mod.body[0].body:
696 for element in mod.body[0].body:
687 if isinstance(element, ast.Return):
697 if isinstance(element, ast.Return):
688 multiline = False
698 multiline = False
689 else:
699 else:
690 output.append(u'')
700 output.append(u'')
691 multiline = False
701 multiline = False
692 except Exception:
702 except Exception:
693 pass
703 pass
694
704
695 if savefig: # clear figure if plotted
705 if savefig: # clear figure if plotted
696 self.ensure_pyplot()
706 self.ensure_pyplot()
697 self.process_input_line('plt.clf()', store_history=False)
707 self.process_input_line('plt.clf()', store_history=False)
698 self.clear_cout()
708 self.clear_cout()
699 savefig = False
709 savefig = False
700
710
701 return output
711 return output
702
712
703 def custom_doctest(self, decorator, input_lines, found, submitted):
713 def custom_doctest(self, decorator, input_lines, found, submitted):
704 """
714 """
705 Perform a specialized doctest.
715 Perform a specialized doctest.
706
716
707 """
717 """
708 from .custom_doctests import doctests
718 from .custom_doctests import doctests
709
719
710 args = decorator.split()
720 args = decorator.split()
711 doctest_type = args[1]
721 doctest_type = args[1]
712 if doctest_type in doctests:
722 if doctest_type in doctests:
713 doctests[doctest_type](self, args, input_lines, found, submitted)
723 doctests[doctest_type](self, args, input_lines, found, submitted)
714 else:
724 else:
715 e = "Invalid option to @doctest: {0}".format(doctest_type)
725 e = "Invalid option to @doctest: {0}".format(doctest_type)
716 raise Exception(e)
726 raise Exception(e)
717
727
718
728
719 class IPythonDirective(Directive):
729 class IPythonDirective(Directive):
720
730
721 has_content = True
731 has_content = True
722 required_arguments = 0
732 required_arguments = 0
723 optional_arguments = 4 # python, suppress, verbatim, doctest
733 optional_arguments = 4 # python, suppress, verbatim, doctest
724 final_argumuent_whitespace = True
734 final_argumuent_whitespace = True
725 option_spec = { 'python': directives.unchanged,
735 option_spec = { 'python': directives.unchanged,
726 'suppress' : directives.flag,
736 'suppress' : directives.flag,
727 'verbatim' : directives.flag,
737 'verbatim' : directives.flag,
728 'doctest' : directives.flag,
738 'doctest' : directives.flag,
729 'okexcept': directives.flag,
739 'okexcept': directives.flag,
730 'okwarning': directives.flag
740 'okwarning': directives.flag
731 }
741 }
732
742
733 shell = None
743 shell = None
734
744
735 seen_docs = set()
745 seen_docs = set()
736
746
737 def get_config_options(self):
747 def get_config_options(self):
738 # contains sphinx configuration variables
748 # contains sphinx configuration variables
739 config = self.state.document.settings.env.config
749 config = self.state.document.settings.env.config
740
750
741 # get config variables to set figure output directory
751 # get config variables to set figure output directory
742 confdir = self.state.document.settings.env.app.confdir
752 confdir = self.state.document.settings.env.app.confdir
743 savefig_dir = config.ipython_savefig_dir
753 savefig_dir = config.ipython_savefig_dir
744 source_dir = os.path.dirname(self.state.document.current_source)
754 source_dir = os.path.dirname(self.state.document.current_source)
745 if savefig_dir is None:
755 if savefig_dir is None:
746 savefig_dir = config.html_static_path
756 savefig_dir = config.html_static_path
747 if isinstance(savefig_dir, list):
757 if isinstance(savefig_dir, list):
748 savefig_dir = savefig_dir[0] # safe to assume only one path?
758 savefig_dir = savefig_dir[0] # safe to assume only one path?
749 savefig_dir = os.path.join(confdir, savefig_dir)
759 savefig_dir = os.path.join(confdir, savefig_dir)
750
760
751 # get regex and prompt stuff
761 # get regex and prompt stuff
752 rgxin = config.ipython_rgxin
762 rgxin = config.ipython_rgxin
753 rgxout = config.ipython_rgxout
763 rgxout = config.ipython_rgxout
754 promptin = config.ipython_promptin
764 promptin = config.ipython_promptin
755 promptout = config.ipython_promptout
765 promptout = config.ipython_promptout
756 mplbackend = config.ipython_mplbackend
766 mplbackend = config.ipython_mplbackend
757 exec_lines = config.ipython_execlines
767 exec_lines = config.ipython_execlines
758 hold_count = config.ipython_holdcount
768 hold_count = config.ipython_holdcount
759
769
760 return (savefig_dir, source_dir, rgxin, rgxout,
770 return (savefig_dir, source_dir, rgxin, rgxout,
761 promptin, promptout, mplbackend, exec_lines, hold_count)
771 promptin, promptout, mplbackend, exec_lines, hold_count)
762
772
763 def setup(self):
773 def setup(self):
764 # Get configuration values.
774 # Get configuration values.
765 (savefig_dir, source_dir, rgxin, rgxout, promptin, promptout,
775 (savefig_dir, source_dir, rgxin, rgxout, promptin, promptout,
766 mplbackend, exec_lines, hold_count) = self.get_config_options()
776 mplbackend, exec_lines, hold_count) = self.get_config_options()
767
777
768 if self.shell is None:
778 if self.shell is None:
769 # We will be here many times. However, when the
779 # We will be here many times. However, when the
770 # EmbeddedSphinxShell is created, its interactive shell member
780 # EmbeddedSphinxShell is created, its interactive shell member
771 # is the same for each instance.
781 # is the same for each instance.
772
782
773 if mplbackend:
783 if mplbackend:
774 import matplotlib
784 import matplotlib
775 # Repeated calls to use() will not hurt us since `mplbackend`
785 # Repeated calls to use() will not hurt us since `mplbackend`
776 # is the same each time.
786 # is the same each time.
777 matplotlib.use(mplbackend)
787 matplotlib.use(mplbackend)
778
788
779 # Must be called after (potentially) importing matplotlib and
789 # Must be called after (potentially) importing matplotlib and
780 # setting its backend since exec_lines might import pylab.
790 # setting its backend since exec_lines might import pylab.
781 self.shell = EmbeddedSphinxShell(exec_lines)
791 self.shell = EmbeddedSphinxShell(exec_lines)
782
792
783 # Store IPython directive to enable better error messages
793 # Store IPython directive to enable better error messages
784 self.shell.directive = self
794 self.shell.directive = self
785
795
786 # reset the execution count if we haven't processed this doc
796 # reset the execution count if we haven't processed this doc
787 #NOTE: this may be borked if there are multiple seen_doc tmp files
797 #NOTE: this may be borked if there are multiple seen_doc tmp files
788 #check time stamp?
798 #check time stamp?
789 if not self.state.document.current_source in self.seen_docs:
799 if not self.state.document.current_source in self.seen_docs:
790 self.shell.IP.history_manager.reset()
800 self.shell.IP.history_manager.reset()
791 self.shell.IP.execution_count = 1
801 self.shell.IP.execution_count = 1
792 self.shell.IP.prompt_manager.width = 0
802 self.shell.IP.prompt_manager.width = 0
793 self.seen_docs.add(self.state.document.current_source)
803 self.seen_docs.add(self.state.document.current_source)
794
804
795 # and attach to shell so we don't have to pass them around
805 # and attach to shell so we don't have to pass them around
796 self.shell.rgxin = rgxin
806 self.shell.rgxin = rgxin
797 self.shell.rgxout = rgxout
807 self.shell.rgxout = rgxout
798 self.shell.promptin = promptin
808 self.shell.promptin = promptin
799 self.shell.promptout = promptout
809 self.shell.promptout = promptout
800 self.shell.savefig_dir = savefig_dir
810 self.shell.savefig_dir = savefig_dir
801 self.shell.source_dir = source_dir
811 self.shell.source_dir = source_dir
802 self.shell.hold_count = hold_count
812 self.shell.hold_count = hold_count
803
813
804 # setup bookmark for saving figures directory
814 # setup bookmark for saving figures directory
805 self.shell.process_input_line('bookmark ipy_savedir %s'%savefig_dir,
815 self.shell.process_input_line('bookmark ipy_savedir %s'%savefig_dir,
806 store_history=False)
816 store_history=False)
807 self.shell.clear_cout()
817 self.shell.clear_cout()
808
818
809 return rgxin, rgxout, promptin, promptout
819 return rgxin, rgxout, promptin, promptout
810
820
811 def teardown(self):
821 def teardown(self):
812 # delete last bookmark
822 # delete last bookmark
813 self.shell.process_input_line('bookmark -d ipy_savedir',
823 self.shell.process_input_line('bookmark -d ipy_savedir',
814 store_history=False)
824 store_history=False)
815 self.shell.clear_cout()
825 self.shell.clear_cout()
816
826
817 def run(self):
827 def run(self):
818 debug = False
828 debug = False
819
829
820 #TODO, any reason block_parser can't be a method of embeddable shell
830 #TODO, any reason block_parser can't be a method of embeddable shell
821 # then we wouldn't have to carry these around
831 # then we wouldn't have to carry these around
822 rgxin, rgxout, promptin, promptout = self.setup()
832 rgxin, rgxout, promptin, promptout = self.setup()
823
833
824 options = self.options
834 options = self.options
825 self.shell.is_suppress = 'suppress' in options
835 self.shell.is_suppress = 'suppress' in options
826 self.shell.is_doctest = 'doctest' in options
836 self.shell.is_doctest = 'doctest' in options
827 self.shell.is_verbatim = 'verbatim' in options
837 self.shell.is_verbatim = 'verbatim' in options
828 self.shell.is_okexcept = 'okexcept' in options
838 self.shell.is_okexcept = 'okexcept' in options
829 self.shell.is_okwarning = 'okwarning' in options
839 self.shell.is_okwarning = 'okwarning' in options
830
840
831 # handle pure python code
841 # handle pure python code
832 if 'python' in self.arguments:
842 if 'python' in self.arguments:
833 content = self.content
843 content = self.content
834 self.content = self.shell.process_pure_python(content)
844 self.content = self.shell.process_pure_python(content)
835
845
836 parts = '\n'.join(self.content).split('\n\n')
846 parts = '\n'.join(self.content).split('\n\n')
837
847
838 lines = ['.. code-block:: ipython', '']
848 lines = ['.. code-block:: ipython', '']
839 figures = []
849 figures = []
840
850
841 for part in parts:
851 for part in parts:
842 block = block_parser(part, rgxin, rgxout, promptin, promptout)
852 block = block_parser(part, rgxin, rgxout, promptin, promptout)
843 if len(block):
853 if len(block):
844 rows, figure = self.shell.process_block(block)
854 rows, figure = self.shell.process_block(block)
845 for row in rows:
855 for row in rows:
846 lines.extend([' %s'%line for line in row.split('\n')])
856 lines.extend([' %s'%line for line in row.split('\n')])
847
857
848 if figure is not None:
858 if figure is not None:
849 figures.append(figure)
859 figures.append(figure)
850
860
851 for figure in figures:
861 for figure in figures:
852 lines.append('')
862 lines.append('')
853 lines.extend(figure.split('\n'))
863 lines.extend(figure.split('\n'))
854 lines.append('')
864 lines.append('')
855
865
856 if len(lines)>2:
866 if len(lines)>2:
857 if debug:
867 if debug:
858 print('\n'.join(lines))
868 print('\n'.join(lines))
859 else:
869 else:
860 # This has to do with input, not output. But if we comment
870 # This has to do with input, not output. But if we comment
861 # these lines out, then no IPython code will appear in the
871 # these lines out, then no IPython code will appear in the
862 # final output.
872 # final output.
863 self.state_machine.insert_input(
873 self.state_machine.insert_input(
864 lines, self.state_machine.input_lines.source(0))
874 lines, self.state_machine.input_lines.source(0))
865
875
866 # cleanup
876 # cleanup
867 self.teardown()
877 self.teardown()
868
878
869 return []
879 return []
870
880
871 # Enable as a proper Sphinx directive
881 # Enable as a proper Sphinx directive
872 def setup(app):
882 def setup(app):
873 setup.app = app
883 setup.app = app
874
884
875 app.add_directive('ipython', IPythonDirective)
885 app.add_directive('ipython', IPythonDirective)
876 app.add_config_value('ipython_savefig_dir', None, 'env')
886 app.add_config_value('ipython_savefig_dir', None, 'env')
877 app.add_config_value('ipython_rgxin',
887 app.add_config_value('ipython_rgxin',
878 re.compile('In \[(\d+)\]:\s?(.*)\s*'), 'env')
888 re.compile('In \[(\d+)\]:\s?(.*)\s*'), 'env')
879 app.add_config_value('ipython_rgxout',
889 app.add_config_value('ipython_rgxout',
880 re.compile('Out\[(\d+)\]:\s?(.*)\s*'), 'env')
890 re.compile('Out\[(\d+)\]:\s?(.*)\s*'), 'env')
881 app.add_config_value('ipython_promptin', 'In [%d]:', 'env')
891 app.add_config_value('ipython_promptin', 'In [%d]:', 'env')
882 app.add_config_value('ipython_promptout', 'Out[%d]:', 'env')
892 app.add_config_value('ipython_promptout', 'Out[%d]:', 'env')
883
893
884 # We could just let matplotlib pick whatever is specified as the default
894 # We could just let matplotlib pick whatever is specified as the default
885 # backend in the matplotlibrc file, but this would cause issues if the
895 # backend in the matplotlibrc file, but this would cause issues if the
886 # backend didn't work in headless environments. For this reason, 'agg'
896 # backend didn't work in headless environments. For this reason, 'agg'
887 # is a good default backend choice.
897 # is a good default backend choice.
888 app.add_config_value('ipython_mplbackend', 'agg', 'env')
898 app.add_config_value('ipython_mplbackend', 'agg', 'env')
889
899
890 # If the user sets this config value to `None`, then EmbeddedSphinxShell's
900 # If the user sets this config value to `None`, then EmbeddedSphinxShell's
891 # __init__ method will treat it as [].
901 # __init__ method will treat it as [].
892 execlines = ['import numpy as np', 'import matplotlib.pyplot as plt']
902 execlines = ['import numpy as np', 'import matplotlib.pyplot as plt']
893 app.add_config_value('ipython_execlines', execlines, 'env')
903 app.add_config_value('ipython_execlines', execlines, 'env')
894
904
895 app.add_config_value('ipython_holdcount', True, 'env')
905 app.add_config_value('ipython_holdcount', True, 'env')
896
906
897 # Simple smoke test, needs to be converted to a proper automatic test.
907 # Simple smoke test, needs to be converted to a proper automatic test.
898 def test():
908 def test():
899
909
900 examples = [
910 examples = [
901 r"""
911 r"""
902 In [9]: pwd
912 In [9]: pwd
903 Out[9]: '/home/jdhunter/py4science/book'
913 Out[9]: '/home/jdhunter/py4science/book'
904
914
905 In [10]: cd bookdata/
915 In [10]: cd bookdata/
906 /home/jdhunter/py4science/book/bookdata
916 /home/jdhunter/py4science/book/bookdata
907
917
908 In [2]: from pylab import *
918 In [2]: from pylab import *
909
919
910 In [2]: ion()
920 In [2]: ion()
911
921
912 In [3]: im = imread('stinkbug.png')
922 In [3]: im = imread('stinkbug.png')
913
923
914 @savefig mystinkbug.png width=4in
924 @savefig mystinkbug.png width=4in
915 In [4]: imshow(im)
925 In [4]: imshow(im)
916 Out[4]: <matplotlib.image.AxesImage object at 0x39ea850>
926 Out[4]: <matplotlib.image.AxesImage object at 0x39ea850>
917
927
918 """,
928 """,
919 r"""
929 r"""
920
930
921 In [1]: x = 'hello world'
931 In [1]: x = 'hello world'
922
932
923 # string methods can be
933 # string methods can be
924 # used to alter the string
934 # used to alter the string
925 @doctest
935 @doctest
926 In [2]: x.upper()
936 In [2]: x.upper()
927 Out[2]: 'HELLO WORLD'
937 Out[2]: 'HELLO WORLD'
928
938
929 @verbatim
939 @verbatim
930 In [3]: x.st<TAB>
940 In [3]: x.st<TAB>
931 x.startswith x.strip
941 x.startswith x.strip
932 """,
942 """,
933 r"""
943 r"""
934
944
935 In [130]: url = 'http://ichart.finance.yahoo.com/table.csv?s=CROX\
945 In [130]: url = 'http://ichart.finance.yahoo.com/table.csv?s=CROX\
936 .....: &d=9&e=22&f=2009&g=d&a=1&br=8&c=2006&ignore=.csv'
946 .....: &d=9&e=22&f=2009&g=d&a=1&br=8&c=2006&ignore=.csv'
937
947
938 In [131]: print url.split('&')
948 In [131]: print url.split('&')
939 ['http://ichart.finance.yahoo.com/table.csv?s=CROX', 'd=9', 'e=22', 'f=2009', 'g=d', 'a=1', 'b=8', 'c=2006', 'ignore=.csv']
949 ['http://ichart.finance.yahoo.com/table.csv?s=CROX', 'd=9', 'e=22', 'f=2009', 'g=d', 'a=1', 'b=8', 'c=2006', 'ignore=.csv']
940
950
941 In [60]: import urllib
951 In [60]: import urllib
942
952
943 """,
953 """,
944 r"""\
954 r"""\
945
955
946 In [133]: import numpy.random
956 In [133]: import numpy.random
947
957
948 @suppress
958 @suppress
949 In [134]: numpy.random.seed(2358)
959 In [134]: numpy.random.seed(2358)
950
960
951 @doctest
961 @doctest
952 In [135]: numpy.random.rand(10,2)
962 In [135]: numpy.random.rand(10,2)
953 Out[135]:
963 Out[135]:
954 array([[ 0.64524308, 0.59943846],
964 array([[ 0.64524308, 0.59943846],
955 [ 0.47102322, 0.8715456 ],
965 [ 0.47102322, 0.8715456 ],
956 [ 0.29370834, 0.74776844],
966 [ 0.29370834, 0.74776844],
957 [ 0.99539577, 0.1313423 ],
967 [ 0.99539577, 0.1313423 ],
958 [ 0.16250302, 0.21103583],
968 [ 0.16250302, 0.21103583],
959 [ 0.81626524, 0.1312433 ],
969 [ 0.81626524, 0.1312433 ],
960 [ 0.67338089, 0.72302393],
970 [ 0.67338089, 0.72302393],
961 [ 0.7566368 , 0.07033696],
971 [ 0.7566368 , 0.07033696],
962 [ 0.22591016, 0.77731835],
972 [ 0.22591016, 0.77731835],
963 [ 0.0072729 , 0.34273127]])
973 [ 0.0072729 , 0.34273127]])
964
974
965 """,
975 """,
966
976
967 r"""
977 r"""
968 In [106]: print x
978 In [106]: print x
969 jdh
979 jdh
970
980
971 In [109]: for i in range(10):
981 In [109]: for i in range(10):
972 .....: print i
982 .....: print i
973 .....:
983 .....:
974 .....:
984 .....:
975 0
985 0
976 1
986 1
977 2
987 2
978 3
988 3
979 4
989 4
980 5
990 5
981 6
991 6
982 7
992 7
983 8
993 8
984 9
994 9
985 """,
995 """,
986
996
987 r"""
997 r"""
988
998
989 In [144]: from pylab import *
999 In [144]: from pylab import *
990
1000
991 In [145]: ion()
1001 In [145]: ion()
992
1002
993 # use a semicolon to suppress the output
1003 # use a semicolon to suppress the output
994 @savefig test_hist.png width=4in
1004 @savefig test_hist.png width=4in
995 In [151]: hist(np.random.randn(10000), 100);
1005 In [151]: hist(np.random.randn(10000), 100);
996
1006
997
1007
998 @savefig test_plot.png width=4in
1008 @savefig test_plot.png width=4in
999 In [151]: plot(np.random.randn(10000), 'o');
1009 In [151]: plot(np.random.randn(10000), 'o');
1000 """,
1010 """,
1001
1011
1002 r"""
1012 r"""
1003 # use a semicolon to suppress the output
1013 # use a semicolon to suppress the output
1004 In [151]: plt.clf()
1014 In [151]: plt.clf()
1005
1015
1006 @savefig plot_simple.png width=4in
1016 @savefig plot_simple.png width=4in
1007 In [151]: plot([1,2,3])
1017 In [151]: plot([1,2,3])
1008
1018
1009 @savefig hist_simple.png width=4in
1019 @savefig hist_simple.png width=4in
1010 In [151]: hist(np.random.randn(10000), 100);
1020 In [151]: hist(np.random.randn(10000), 100);
1011
1021
1012 """,
1022 """,
1013 r"""
1023 r"""
1014 # update the current fig
1024 # update the current fig
1015 In [151]: ylabel('number')
1025 In [151]: ylabel('number')
1016
1026
1017 In [152]: title('normal distribution')
1027 In [152]: title('normal distribution')
1018
1028
1019
1029
1020 @savefig hist_with_text.png
1030 @savefig hist_with_text.png
1021 In [153]: grid(True)
1031 In [153]: grid(True)
1022
1032
1023 @doctest float
1033 @doctest float
1024 In [154]: 0.1 + 0.2
1034 In [154]: 0.1 + 0.2
1025 Out[154]: 0.3
1035 Out[154]: 0.3
1026
1036
1027 @doctest float
1037 @doctest float
1028 In [155]: np.arange(16).reshape(4,4)
1038 In [155]: np.arange(16).reshape(4,4)
1029 Out[155]:
1039 Out[155]:
1030 array([[ 0, 1, 2, 3],
1040 array([[ 0, 1, 2, 3],
1031 [ 4, 5, 6, 7],
1041 [ 4, 5, 6, 7],
1032 [ 8, 9, 10, 11],
1042 [ 8, 9, 10, 11],
1033 [12, 13, 14, 15]])
1043 [12, 13, 14, 15]])
1034
1044
1035 In [1]: x = np.arange(16, dtype=float).reshape(4,4)
1045 In [1]: x = np.arange(16, dtype=float).reshape(4,4)
1036
1046
1037 In [2]: x[0,0] = np.inf
1047 In [2]: x[0,0] = np.inf
1038
1048
1039 In [3]: x[0,1] = np.nan
1049 In [3]: x[0,1] = np.nan
1040
1050
1041 @doctest float
1051 @doctest float
1042 In [4]: x
1052 In [4]: x
1043 Out[4]:
1053 Out[4]:
1044 array([[ inf, nan, 2., 3.],
1054 array([[ inf, nan, 2., 3.],
1045 [ 4., 5., 6., 7.],
1055 [ 4., 5., 6., 7.],
1046 [ 8., 9., 10., 11.],
1056 [ 8., 9., 10., 11.],
1047 [ 12., 13., 14., 15.]])
1057 [ 12., 13., 14., 15.]])
1048
1058
1049
1059
1050 """,
1060 """,
1051 ]
1061 ]
1052 # skip local-file depending first example:
1062 # skip local-file depending first example:
1053 examples = examples[1:]
1063 examples = examples[1:]
1054
1064
1055 #ipython_directive.DEBUG = True # dbg
1065 #ipython_directive.DEBUG = True # dbg
1056 #options = dict(suppress=True) # dbg
1066 #options = dict(suppress=True) # dbg
1057 options = dict()
1067 options = dict()
1058 for example in examples:
1068 for example in examples:
1059 content = example.split('\n')
1069 content = example.split('\n')
1060 IPythonDirective('debug', arguments=None, options=options,
1070 IPythonDirective('debug', arguments=None, options=options,
1061 content=content, lineno=0,
1071 content=content, lineno=0,
1062 content_offset=None, block_text=None,
1072 content_offset=None, block_text=None,
1063 state=None, state_machine=None,
1073 state=None, state_machine=None,
1064 )
1074 )
1065
1075
1066 # Run test suite as a script
1076 # Run test suite as a script
1067 if __name__=='__main__':
1077 if __name__=='__main__':
1068 if not os.path.isdir('_static'):
1078 if not os.path.isdir('_static'):
1069 os.mkdir('_static')
1079 os.mkdir('_static')
1070 test()
1080 test()
1071 print('All OK? Check figures in _static/')
1081 print('All OK? Check figures in _static/')
General Comments 0
You need to be logged in to leave comments. Login now