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