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