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