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