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