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