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