##// END OF EJS Templates
Add `ipython_execlines` option to IPython directive.
chebee7i -
Show More
@@ -1,839 +1,845 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Sphinx directive to support embedded IPython code.
2 """Sphinx directive to support embedded IPython code.
3
3
4 This directive allows pasting of entire interactive IPython sessions, prompts
4 This directive allows pasting of entire interactive IPython sessions, prompts
5 and all, and their code will actually get re-executed at doc build time, with
5 and all, and their code will actually get re-executed at doc build time, with
6 all prompts renumbered sequentially. It also allows you to input code as a pure
6 all prompts renumbered sequentially. It also allows you to input code as a pure
7 python input by giving the argument python to the directive. The output looks
7 python input by giving the argument python to the directive. The output looks
8 like an interactive ipython section.
8 like an interactive ipython section.
9
9
10 To enable this directive, simply list it in your Sphinx ``conf.py`` file
10 To enable this directive, simply list it in your Sphinx ``conf.py`` file
11 (making sure the directory where you placed it is visible to sphinx, as is
11 (making sure the directory where you placed it is visible to sphinx, as is
12 needed for all Sphinx directives). For example, to enable syntax highlighting
12 needed for all Sphinx directives). For example, to enable syntax highlighting
13 and the IPython directive::
13 and the IPython directive::
14
14
15 extensions = ['IPython.sphinxext.ipython_console_highlighting',
15 extensions = ['IPython.sphinxext.ipython_console_highlighting',
16 'IPython.sphinxext.ipython_directive']
16 'IPython.sphinxext.ipython_directive']
17
17
18 By default this directive assumes that your prompts are unchanged IPython ones,
18 By default this directive assumes that your prompts are unchanged IPython ones,
19 but this can be customized. The configurable options that can be placed in
19 but this can be customized. The configurable options that can be placed in
20 conf.py are:
20 conf.py are:
21
21
22 ipython_savefig_dir:
22 ipython_savefig_dir:
23 The directory in which to save the figures. This is relative to the
23 The directory in which to save the figures. This is relative to the
24 Sphinx source directory. The default is `html_static_path`.
24 Sphinx source directory. The default is `html_static_path`.
25 ipython_rgxin:
25 ipython_rgxin:
26 The compiled regular expression to denote the start of IPython input
26 The compiled regular expression to denote the start of IPython input
27 lines. The default is re.compile('In \[(\d+)\]:\s?(.*)\s*'). You
27 lines. The default is re.compile('In \[(\d+)\]:\s?(.*)\s*'). You
28 shouldn't need to change this.
28 shouldn't need to change this.
29 ipython_rgxout:
29 ipython_rgxout:
30 The compiled regular expression to denote the start of IPython output
30 The compiled regular expression to denote the start of IPython output
31 lines. The default is re.compile('Out\[(\d+)\]:\s?(.*)\s*'). You
31 lines. The default is re.compile('Out\[(\d+)\]:\s?(.*)\s*'). You
32 shouldn't need to change this.
32 shouldn't need to change this.
33 ipython_promptin:
33 ipython_promptin:
34 The string to represent the IPython input prompt in the generated ReST.
34 The string to represent the IPython input prompt in the generated ReST.
35 The default is 'In [%d]:'. This expects that the line numbers are used
35 The default is 'In [%d]:'. This expects that the line numbers are used
36 in the prompt.
36 in the prompt.
37 ipython_promptout:
37 ipython_promptout:
38 The string to represent the IPython prompt in the generated ReST. The
38 The string to represent the IPython prompt in the generated ReST. The
39 default is 'Out [%d]:'. This expects that the line numbers are used
39 default is 'Out [%d]:'. This expects that the line numbers are used
40 in the prompt.
40 in the prompt.
41 ipython_mplbackend:
41 ipython_mplbackend:
42 The string which specifies if the embedded Sphinx shell should import
42 The string which specifies if the embedded Sphinx shell should import
43 Matplotlib and set the backend for each code-block. If `None`, or equal
43 Matplotlib and set the backend for each code-block. If `None`, or equal
44 to '' or 'None', then `matplotlib` will not be automatically imported. If
44 to '' or 'None', then `matplotlib` will not be automatically imported. If
45 not `None`, then the value should specify a backend that is passed to
45 not `None`, then the value should specify a backend that is passed to
46 `matplotlib.use()`. The default value is 'agg'.
46 `matplotlib.use()`. The default value is 'agg'.
47 ipython_execlines:
48 A list of strings to be exec'd for each embedded Sphinx shell. Typical
49 usage is to make certain packages always available. If None, then
50 `['import numpy as np', 'from pylab import *']` is used. Set this to an
51 empty list if you wish to have no imports always available.
47
52
48 As an example, to use the IPython directive when `matplotlib` is not available,
53 As an example, to use the IPython directive when `matplotlib` is not available,
49 one sets the backend to `None`::
54 one sets the backend to `None`::
50
55
51 ipython_mplbacked = None
56 ipython_mplbacked = None
52
57
53
58
54 ToDo
59 ToDo
55 ----
60 ----
56
61
57 - Turn the ad-hoc test() function into a real test suite.
62 - Turn the ad-hoc test() function into a real test suite.
58 - Break up ipython-specific functionality from matplotlib stuff into better
63 - Break up ipython-specific functionality from matplotlib stuff into better
59 separated code.
64 separated code.
60
65
61 Authors
66 Authors
62 -------
67 -------
63
68
64 - John D Hunter: orignal author.
69 - John D Hunter: orignal author.
65 - Fernando Perez: refactoring, documentation, cleanups, port to 0.11.
70 - Fernando Perez: refactoring, documentation, cleanups, port to 0.11.
66 - VΓ‘clavΕ milauer <eudoxos-AT-arcig.cz>: Prompt generalizations.
71 - VΓ‘clavΕ milauer <eudoxos-AT-arcig.cz>: Prompt generalizations.
67 - Skipper Seabold, refactoring, cleanups, pure python addition
72 - Skipper Seabold, refactoring, cleanups, pure python addition
68 """
73 """
69 from __future__ import print_function
74 from __future__ import print_function
70
75
71 #-----------------------------------------------------------------------------
76 #-----------------------------------------------------------------------------
72 # Imports
77 # Imports
73 #-----------------------------------------------------------------------------
78 #-----------------------------------------------------------------------------
74
79
75 # Stdlib
80 # Stdlib
76 import os
81 import os
77 import re
82 import re
78 import sys
83 import sys
79 import tempfile
84 import tempfile
80 import ast
85 import ast
81
86
82 # To keep compatibility with various python versions
87 # To keep compatibility with various python versions
83 try:
88 try:
84 from hashlib import md5
89 from hashlib import md5
85 except ImportError:
90 except ImportError:
86 from md5 import md5
91 from md5 import md5
87
92
88 # Third-party
93 # Third-party
89 import sphinx
94 import sphinx
90 from docutils.parsers.rst import directives
95 from docutils.parsers.rst import directives
91 from docutils import nodes
96 from docutils import nodes
92 from sphinx.util.compat import Directive
97 from sphinx.util.compat import Directive
93
98
94 # Our own
99 # Our own
95 from IPython import Config, InteractiveShell
100 from IPython import Config, InteractiveShell
96 from IPython.core.profiledir import ProfileDir
101 from IPython.core.profiledir import ProfileDir
97 from IPython.utils import io
102 from IPython.utils import io
98 from IPython.utils.py3compat import PY3
103 from IPython.utils.py3compat import PY3
99
104
100 if PY3:
105 if PY3:
101 from io import StringIO
106 from io import StringIO
102 else:
107 else:
103 from StringIO import StringIO
108 from StringIO import StringIO
104
109
105 #-----------------------------------------------------------------------------
110 #-----------------------------------------------------------------------------
106 # Globals
111 # Globals
107 #-----------------------------------------------------------------------------
112 #-----------------------------------------------------------------------------
108 # for tokenizing blocks
113 # for tokenizing blocks
109 COMMENT, INPUT, OUTPUT = range(3)
114 COMMENT, INPUT, OUTPUT = range(3)
110
115
111 #-----------------------------------------------------------------------------
116 #-----------------------------------------------------------------------------
112 # Functions and class declarations
117 # Functions and class declarations
113 #-----------------------------------------------------------------------------
118 #-----------------------------------------------------------------------------
114 def block_parser(part, rgxin, rgxout, fmtin, fmtout):
119 def block_parser(part, rgxin, rgxout, fmtin, fmtout):
115 """
120 """
116 part is a string of ipython text, comprised of at most one
121 part is a string of ipython text, comprised of at most one
117 input, one ouput, comments, and blank lines. The block parser
122 input, one ouput, comments, and blank lines. The block parser
118 parses the text into a list of::
123 parses the text into a list of::
119
124
120 blocks = [ (TOKEN0, data0), (TOKEN1, data1), ...]
125 blocks = [ (TOKEN0, data0), (TOKEN1, data1), ...]
121
126
122 where TOKEN is one of [COMMENT | INPUT | OUTPUT ] and
127 where TOKEN is one of [COMMENT | INPUT | OUTPUT ] and
123 data is, depending on the type of token::
128 data is, depending on the type of token::
124
129
125 COMMENT : the comment string
130 COMMENT : the comment string
126
131
127 INPUT: the (DECORATOR, INPUT_LINE, REST) where
132 INPUT: the (DECORATOR, INPUT_LINE, REST) where
128 DECORATOR: the input decorator (or None)
133 DECORATOR: the input decorator (or None)
129 INPUT_LINE: the input as string (possibly multi-line)
134 INPUT_LINE: the input as string (possibly multi-line)
130 REST : any stdout generated by the input line (not OUTPUT)
135 REST : any stdout generated by the input line (not OUTPUT)
131
136
132
137
133 OUTPUT: the output string, possibly multi-line
138 OUTPUT: the output string, possibly multi-line
134
139
135 """
140 """
136 block = []
141 block = []
137 lines = part.split('\n')
142 lines = part.split('\n')
138 N = len(lines)
143 N = len(lines)
139 i = 0
144 i = 0
140 decorator = None
145 decorator = None
141 while 1:
146 while 1:
142
147
143 if i==N:
148 if i==N:
144 # nothing left to parse -- the last line
149 # nothing left to parse -- the last line
145 break
150 break
146
151
147 line = lines[i]
152 line = lines[i]
148 i += 1
153 i += 1
149 line_stripped = line.strip()
154 line_stripped = line.strip()
150 if line_stripped.startswith('#'):
155 if line_stripped.startswith('#'):
151 block.append((COMMENT, line))
156 block.append((COMMENT, line))
152 continue
157 continue
153
158
154 if line_stripped.startswith('@'):
159 if line_stripped.startswith('@'):
155 # we're assuming at most one decorator -- may need to
160 # we're assuming at most one decorator -- may need to
156 # rethink
161 # rethink
157 decorator = line_stripped
162 decorator = line_stripped
158 continue
163 continue
159
164
160 # does this look like an input line?
165 # does this look like an input line?
161 matchin = rgxin.match(line)
166 matchin = rgxin.match(line)
162 if matchin:
167 if matchin:
163 lineno, inputline = int(matchin.group(1)), matchin.group(2)
168 lineno, inputline = int(matchin.group(1)), matchin.group(2)
164
169
165 # the ....: continuation string
170 # the ....: continuation string
166 continuation = ' %s:'%''.join(['.']*(len(str(lineno))+2))
171 continuation = ' %s:'%''.join(['.']*(len(str(lineno))+2))
167 Nc = len(continuation)
172 Nc = len(continuation)
168 # input lines can continue on for more than one line, if
173 # input lines can continue on for more than one line, if
169 # we have a '\' line continuation char or a function call
174 # we have a '\' line continuation char or a function call
170 # echo line 'print'. The input line can only be
175 # echo line 'print'. The input line can only be
171 # terminated by the end of the block or an output line, so
176 # terminated by the end of the block or an output line, so
172 # we parse out the rest of the input line if it is
177 # we parse out the rest of the input line if it is
173 # multiline as well as any echo text
178 # multiline as well as any echo text
174
179
175 rest = []
180 rest = []
176 while i<N:
181 while i<N:
177
182
178 # look ahead; if the next line is blank, or a comment, or
183 # look ahead; if the next line is blank, or a comment, or
179 # an output line, we're done
184 # an output line, we're done
180
185
181 nextline = lines[i]
186 nextline = lines[i]
182 matchout = rgxout.match(nextline)
187 matchout = rgxout.match(nextline)
183 #print "nextline=%s, continuation=%s, starts=%s"%(nextline, continuation, nextline.startswith(continuation))
188 #print "nextline=%s, continuation=%s, starts=%s"%(nextline, continuation, nextline.startswith(continuation))
184 if matchout or nextline.startswith('#'):
189 if matchout or nextline.startswith('#'):
185 break
190 break
186 elif nextline.startswith(continuation):
191 elif nextline.startswith(continuation):
187 inputline += '\n' + nextline[Nc:]
192 inputline += '\n' + nextline[Nc:]
188 else:
193 else:
189 rest.append(nextline)
194 rest.append(nextline)
190 i+= 1
195 i+= 1
191
196
192 block.append((INPUT, (decorator, inputline, '\n'.join(rest))))
197 block.append((INPUT, (decorator, inputline, '\n'.join(rest))))
193 continue
198 continue
194
199
195 # if it looks like an output line grab all the text to the end
200 # if it looks like an output line grab all the text to the end
196 # of the block
201 # of the block
197 matchout = rgxout.match(line)
202 matchout = rgxout.match(line)
198 if matchout:
203 if matchout:
199 lineno, output = int(matchout.group(1)), matchout.group(2)
204 lineno, output = int(matchout.group(1)), matchout.group(2)
200 if i<N-1:
205 if i<N-1:
201 output = '\n'.join([output] + lines[i:])
206 output = '\n'.join([output] + lines[i:])
202
207
203 block.append((OUTPUT, output))
208 block.append((OUTPUT, output))
204 break
209 break
205
210
206 return block
211 return block
207
212
208 class EmbeddedSphinxShell(object):
213 class EmbeddedSphinxShell(object):
209 """An embedded IPython instance to run inside Sphinx"""
214 """An embedded IPython instance to run inside Sphinx"""
210
215
211 def __init__(self):
216 def __init__(self, exec_lines=None):
212
217
213 self.cout = StringIO()
218 self.cout = StringIO()
214
219
220 if exec_lines is None:
221 exec_lines = ['import numpy as np', 'from pylab import *']
215
222
216 # Create config object for IPython
223 # Create config object for IPython
217 config = Config()
224 config = Config()
218 config.Global.display_banner = False
225 config.Global.display_banner = False
219 config.Global.exec_lines = ['import numpy as np',
226 config.Global.exec_lines = exec_lines
220 'from pylab import *'
221 ]
222 config.InteractiveShell.autocall = False
227 config.InteractiveShell.autocall = False
223 config.InteractiveShell.autoindent = False
228 config.InteractiveShell.autoindent = False
224 config.InteractiveShell.colors = 'NoColor'
229 config.InteractiveShell.colors = 'NoColor'
225
230
226 # create a profile so instance history isn't saved
231 # create a profile so instance history isn't saved
227 tmp_profile_dir = tempfile.mkdtemp(prefix='profile_')
232 tmp_profile_dir = tempfile.mkdtemp(prefix='profile_')
228 profname = 'auto_profile_sphinx_build'
233 profname = 'auto_profile_sphinx_build'
229 pdir = os.path.join(tmp_profile_dir,profname)
234 pdir = os.path.join(tmp_profile_dir,profname)
230 profile = ProfileDir.create_profile_dir(pdir)
235 profile = ProfileDir.create_profile_dir(pdir)
231
236
232 # Create and initialize ipython, but don't start its mainloop
237 # Create and initialize ipython, but don't start its mainloop
233 IP = InteractiveShell.instance(config=config, profile_dir=profile)
238 IP = InteractiveShell.instance(config=config, profile_dir=profile)
234 # io.stdout redirect must be done *after* instantiating InteractiveShell
239 # io.stdout redirect must be done *after* instantiating InteractiveShell
235 io.stdout = self.cout
240 io.stdout = self.cout
236 io.stderr = self.cout
241 io.stderr = self.cout
237
242
238 # For debugging, so we can see normal output, use this:
243 # For debugging, so we can see normal output, use this:
239 #from IPython.utils.io import Tee
244 #from IPython.utils.io import Tee
240 #io.stdout = Tee(self.cout, channel='stdout') # dbg
245 #io.stdout = Tee(self.cout, channel='stdout') # dbg
241 #io.stderr = Tee(self.cout, channel='stderr') # dbg
246 #io.stderr = Tee(self.cout, channel='stderr') # dbg
242
247
243 # Store a few parts of IPython we'll need.
248 # Store a few parts of IPython we'll need.
244 self.IP = IP
249 self.IP = IP
245 self.user_ns = self.IP.user_ns
250 self.user_ns = self.IP.user_ns
246 self.user_global_ns = self.IP.user_global_ns
251 self.user_global_ns = self.IP.user_global_ns
247
252
248 self.input = ''
253 self.input = ''
249 self.output = ''
254 self.output = ''
250
255
251 self.is_verbatim = False
256 self.is_verbatim = False
252 self.is_doctest = False
257 self.is_doctest = False
253 self.is_suppress = False
258 self.is_suppress = False
254
259
255 # on the first call to the savefig decorator, we'll import
260 # on the first call to the savefig decorator, we'll import
256 # pyplot as plt so we can make a call to the plt.gcf().savefig
261 # pyplot as plt so we can make a call to the plt.gcf().savefig
257 self._pyplot_imported = False
262 self._pyplot_imported = False
258
263
259 def clear_cout(self):
264 def clear_cout(self):
260 self.cout.seek(0)
265 self.cout.seek(0)
261 self.cout.truncate(0)
266 self.cout.truncate(0)
262
267
263 def process_input_line(self, line, store_history=True):
268 def process_input_line(self, line, store_history=True):
264 """process the input, capturing stdout"""
269 """process the input, capturing stdout"""
265 #print "input='%s'"%self.input
270 #print "input='%s'"%self.input
266 stdout = sys.stdout
271 stdout = sys.stdout
267 splitter = self.IP.input_splitter
272 splitter = self.IP.input_splitter
268 try:
273 try:
269 sys.stdout = self.cout
274 sys.stdout = self.cout
270 splitter.push(line)
275 splitter.push(line)
271 more = splitter.push_accepts_more()
276 more = splitter.push_accepts_more()
272 if not more:
277 if not more:
273 source_raw = splitter.source_raw_reset()[1]
278 source_raw = splitter.source_raw_reset()[1]
274 self.IP.run_cell(source_raw, store_history=store_history)
279 self.IP.run_cell(source_raw, store_history=store_history)
275 finally:
280 finally:
276 sys.stdout = stdout
281 sys.stdout = stdout
277
282
278 def process_image(self, decorator):
283 def process_image(self, decorator):
279 """
284 """
280 # build out an image directive like
285 # build out an image directive like
281 # .. image:: somefile.png
286 # .. image:: somefile.png
282 # :width 4in
287 # :width 4in
283 #
288 #
284 # from an input like
289 # from an input like
285 # savefig somefile.png width=4in
290 # savefig somefile.png width=4in
286 """
291 """
287 savefig_dir = self.savefig_dir
292 savefig_dir = self.savefig_dir
288 source_dir = self.source_dir
293 source_dir = self.source_dir
289 saveargs = decorator.split(' ')
294 saveargs = decorator.split(' ')
290 filename = saveargs[1]
295 filename = saveargs[1]
291 # insert relative path to image file in source
296 # insert relative path to image file in source
292 outfile = os.path.relpath(os.path.join(savefig_dir,filename),
297 outfile = os.path.relpath(os.path.join(savefig_dir,filename),
293 source_dir)
298 source_dir)
294
299
295 imagerows = ['.. image:: %s'%outfile]
300 imagerows = ['.. image:: %s'%outfile]
296
301
297 for kwarg in saveargs[2:]:
302 for kwarg in saveargs[2:]:
298 arg, val = kwarg.split('=')
303 arg, val = kwarg.split('=')
299 arg = arg.strip()
304 arg = arg.strip()
300 val = val.strip()
305 val = val.strip()
301 imagerows.append(' :%s: %s'%(arg, val))
306 imagerows.append(' :%s: %s'%(arg, val))
302
307
303 image_file = os.path.basename(outfile) # only return file name
308 image_file = os.path.basename(outfile) # only return file name
304 image_directive = '\n'.join(imagerows)
309 image_directive = '\n'.join(imagerows)
305 return image_file, image_directive
310 return image_file, image_directive
306
311
307
312
308 # Callbacks for each type of token
313 # Callbacks for each type of token
309 def process_input(self, data, input_prompt, lineno):
314 def process_input(self, data, input_prompt, lineno):
310 """Process data block for INPUT token."""
315 """Process data block for INPUT token."""
311 decorator, input, rest = data
316 decorator, input, rest = data
312 image_file = None
317 image_file = None
313 image_directive = None
318 image_directive = None
314 #print 'INPUT:', data # dbg
319 #print 'INPUT:', data # dbg
315 is_verbatim = decorator=='@verbatim' or self.is_verbatim
320 is_verbatim = decorator=='@verbatim' or self.is_verbatim
316 is_doctest = decorator=='@doctest' or self.is_doctest
321 is_doctest = decorator=='@doctest' or self.is_doctest
317 is_suppress = decorator=='@suppress' or self.is_suppress
322 is_suppress = decorator=='@suppress' or self.is_suppress
318 is_savefig = decorator is not None and \
323 is_savefig = decorator is not None and \
319 decorator.startswith('@savefig')
324 decorator.startswith('@savefig')
320
325
321 input_lines = input.split('\n')
326 input_lines = input.split('\n')
322 if len(input_lines) > 1:
327 if len(input_lines) > 1:
323 if input_lines[-1] != "":
328 if input_lines[-1] != "":
324 input_lines.append('') # make sure there's a blank line
329 input_lines.append('') # make sure there's a blank line
325 # so splitter buffer gets reset
330 # so splitter buffer gets reset
326
331
327 continuation = ' %s:'%''.join(['.']*(len(str(lineno))+2))
332 continuation = ' %s:'%''.join(['.']*(len(str(lineno))+2))
328 Nc = len(continuation)
333 Nc = len(continuation)
329
334
330 if is_savefig:
335 if is_savefig:
331 image_file, image_directive = self.process_image(decorator)
336 image_file, image_directive = self.process_image(decorator)
332
337
333 ret = []
338 ret = []
334 is_semicolon = False
339 is_semicolon = False
335
340
336 for i, line in enumerate(input_lines):
341 for i, line in enumerate(input_lines):
337 if line.endswith(';'):
342 if line.endswith(';'):
338 is_semicolon = True
343 is_semicolon = True
339
344
340 if i==0:
345 if i==0:
341 # process the first input line
346 # process the first input line
342 if is_verbatim:
347 if is_verbatim:
343 self.process_input_line('')
348 self.process_input_line('')
344 self.IP.execution_count += 1 # increment it anyway
349 self.IP.execution_count += 1 # increment it anyway
345 else:
350 else:
346 # only submit the line in non-verbatim mode
351 # only submit the line in non-verbatim mode
347 self.process_input_line(line, store_history=True)
352 self.process_input_line(line, store_history=True)
348 formatted_line = '%s %s'%(input_prompt, line)
353 formatted_line = '%s %s'%(input_prompt, line)
349 else:
354 else:
350 # process a continuation line
355 # process a continuation line
351 if not is_verbatim:
356 if not is_verbatim:
352 self.process_input_line(line, store_history=True)
357 self.process_input_line(line, store_history=True)
353
358
354 formatted_line = '%s %s'%(continuation, line)
359 formatted_line = '%s %s'%(continuation, line)
355
360
356 if not is_suppress:
361 if not is_suppress:
357 ret.append(formatted_line)
362 ret.append(formatted_line)
358
363
359 if not is_suppress and len(rest.strip()) and is_verbatim:
364 if not is_suppress and len(rest.strip()) and is_verbatim:
360 # the "rest" is the standard output of the
365 # the "rest" is the standard output of the
361 # input, which needs to be added in
366 # input, which needs to be added in
362 # verbatim mode
367 # verbatim mode
363 ret.append(rest)
368 ret.append(rest)
364
369
365 self.cout.seek(0)
370 self.cout.seek(0)
366 output = self.cout.read()
371 output = self.cout.read()
367 if not is_suppress and not is_semicolon:
372 if not is_suppress and not is_semicolon:
368 ret.append(output)
373 ret.append(output)
369 elif is_semicolon: # get spacing right
374 elif is_semicolon: # get spacing right
370 ret.append('')
375 ret.append('')
371
376
372 self.cout.truncate(0)
377 self.cout.truncate(0)
373 return (ret, input_lines, output, is_doctest, image_file,
378 return (ret, input_lines, output, is_doctest, image_file,
374 image_directive)
379 image_directive)
375 #print 'OUTPUT', output # dbg
380 #print 'OUTPUT', output # dbg
376
381
377 def process_output(self, data, output_prompt,
382 def process_output(self, data, output_prompt,
378 input_lines, output, is_doctest, image_file):
383 input_lines, output, is_doctest, image_file):
379 """Process data block for OUTPUT token."""
384 """Process data block for OUTPUT token."""
380 if is_doctest:
385 if is_doctest:
381 submitted = data.strip()
386 submitted = data.strip()
382 found = output
387 found = output
383 if found is not None:
388 if found is not None:
384 found = found.strip()
389 found = found.strip()
385
390
386 # XXX - fperez: in 0.11, 'output' never comes with the prompt
391 # XXX - fperez: in 0.11, 'output' never comes with the prompt
387 # in it, just the actual output text. So I think all this code
392 # in it, just the actual output text. So I think all this code
388 # can be nuked...
393 # can be nuked...
389
394
390 # the above comment does not appear to be accurate... (minrk)
395 # the above comment does not appear to be accurate... (minrk)
391
396
392 ind = found.find(output_prompt)
397 ind = found.find(output_prompt)
393 if ind<0:
398 if ind<0:
394 e='output prompt="%s" does not match out line=%s' % \
399 e='output prompt="%s" does not match out line=%s' % \
395 (output_prompt, found)
400 (output_prompt, found)
396 raise RuntimeError(e)
401 raise RuntimeError(e)
397 found = found[len(output_prompt):].strip()
402 found = found[len(output_prompt):].strip()
398
403
399 if found!=submitted:
404 if found!=submitted:
400 e = ('doctest failure for input_lines="%s" with '
405 e = ('doctest failure for input_lines="%s" with '
401 'found_output="%s" and submitted output="%s"' %
406 'found_output="%s" and submitted output="%s"' %
402 (input_lines, found, submitted) )
407 (input_lines, found, submitted) )
403 raise RuntimeError(e)
408 raise RuntimeError(e)
404 #print 'doctest PASSED for input_lines="%s" with found_output="%s" and submitted output="%s"'%(input_lines, found, submitted)
409 #print 'doctest PASSED for input_lines="%s" with found_output="%s" and submitted output="%s"'%(input_lines, found, submitted)
405
410
406 def process_comment(self, data):
411 def process_comment(self, data):
407 """Process data fPblock for COMMENT token."""
412 """Process data fPblock for COMMENT token."""
408 if not self.is_suppress:
413 if not self.is_suppress:
409 return [data]
414 return [data]
410
415
411 def save_image(self, image_file):
416 def save_image(self, image_file):
412 """
417 """
413 Saves the image file to disk.
418 Saves the image file to disk.
414 """
419 """
415 self.ensure_pyplot()
420 self.ensure_pyplot()
416 command = 'plt.gcf().savefig("%s")'%image_file
421 command = 'plt.gcf().savefig("%s")'%image_file
417 #print 'SAVEFIG', command # dbg
422 #print 'SAVEFIG', command # dbg
418 self.process_input_line('bookmark ipy_thisdir', store_history=False)
423 self.process_input_line('bookmark ipy_thisdir', store_history=False)
419 self.process_input_line('cd -b ipy_savedir', store_history=False)
424 self.process_input_line('cd -b ipy_savedir', store_history=False)
420 self.process_input_line(command, store_history=False)
425 self.process_input_line(command, store_history=False)
421 self.process_input_line('cd -b ipy_thisdir', store_history=False)
426 self.process_input_line('cd -b ipy_thisdir', store_history=False)
422 self.process_input_line('bookmark -d ipy_thisdir', store_history=False)
427 self.process_input_line('bookmark -d ipy_thisdir', store_history=False)
423 self.clear_cout()
428 self.clear_cout()
424
429
425
430
426 def process_block(self, block):
431 def process_block(self, block):
427 """
432 """
428 process block from the block_parser and return a list of processed lines
433 process block from the block_parser and return a list of processed lines
429 """
434 """
430 ret = []
435 ret = []
431 output = None
436 output = None
432 input_lines = None
437 input_lines = None
433 lineno = self.IP.execution_count
438 lineno = self.IP.execution_count
434
439
435 input_prompt = self.promptin%lineno
440 input_prompt = self.promptin%lineno
436 output_prompt = self.promptout%lineno
441 output_prompt = self.promptout%lineno
437 image_file = None
442 image_file = None
438 image_directive = None
443 image_directive = None
439
444
440 for token, data in block:
445 for token, data in block:
441 if token==COMMENT:
446 if token==COMMENT:
442 out_data = self.process_comment(data)
447 out_data = self.process_comment(data)
443 elif token==INPUT:
448 elif token==INPUT:
444 (out_data, input_lines, output, is_doctest, image_file,
449 (out_data, input_lines, output, is_doctest, image_file,
445 image_directive) = \
450 image_directive) = \
446 self.process_input(data, input_prompt, lineno)
451 self.process_input(data, input_prompt, lineno)
447 elif token==OUTPUT:
452 elif token==OUTPUT:
448 out_data = \
453 out_data = \
449 self.process_output(data, output_prompt,
454 self.process_output(data, output_prompt,
450 input_lines, output, is_doctest,
455 input_lines, output, is_doctest,
451 image_file)
456 image_file)
452 if out_data:
457 if out_data:
453 ret.extend(out_data)
458 ret.extend(out_data)
454
459
455 # save the image files
460 # save the image files
456 if image_file is not None:
461 if image_file is not None:
457 self.save_image(image_file)
462 self.save_image(image_file)
458
463
459 return ret, image_directive
464 return ret, image_directive
460
465
461 def ensure_pyplot(self):
466 def ensure_pyplot(self):
462 if self._pyplot_imported:
467 if self._pyplot_imported:
463 return
468 return
464 self.process_input_line('import matplotlib.pyplot as plt',
469 self.process_input_line('import matplotlib.pyplot as plt',
465 store_history=False)
470 store_history=False)
466
471
467 def process_pure_python(self, content):
472 def process_pure_python(self, content):
468 """
473 """
469 content is a list of strings. it is unedited directive conent
474 content is a list of strings. it is unedited directive conent
470
475
471 This runs it line by line in the InteractiveShell, prepends
476 This runs it line by line in the InteractiveShell, prepends
472 prompts as needed capturing stderr and stdout, then returns
477 prompts as needed capturing stderr and stdout, then returns
473 the content as a list as if it were ipython code
478 the content as a list as if it were ipython code
474 """
479 """
475 output = []
480 output = []
476 savefig = False # keep up with this to clear figure
481 savefig = False # keep up with this to clear figure
477 multiline = False # to handle line continuation
482 multiline = False # to handle line continuation
478 multiline_start = None
483 multiline_start = None
479 fmtin = self.promptin
484 fmtin = self.promptin
480
485
481 ct = 0
486 ct = 0
482
487
483 for lineno, line in enumerate(content):
488 for lineno, line in enumerate(content):
484
489
485 line_stripped = line.strip()
490 line_stripped = line.strip()
486 if not len(line):
491 if not len(line):
487 output.append(line)
492 output.append(line)
488 continue
493 continue
489
494
490 # handle decorators
495 # handle decorators
491 if line_stripped.startswith('@'):
496 if line_stripped.startswith('@'):
492 output.extend([line])
497 output.extend([line])
493 if 'savefig' in line:
498 if 'savefig' in line:
494 savefig = True # and need to clear figure
499 savefig = True # and need to clear figure
495 continue
500 continue
496
501
497 # handle comments
502 # handle comments
498 if line_stripped.startswith('#'):
503 if line_stripped.startswith('#'):
499 output.extend([line])
504 output.extend([line])
500 continue
505 continue
501
506
502 # deal with lines checking for multiline
507 # deal with lines checking for multiline
503 continuation = u' %s:'% ''.join(['.']*(len(str(ct))+2))
508 continuation = u' %s:'% ''.join(['.']*(len(str(ct))+2))
504 if not multiline:
509 if not multiline:
505 modified = u"%s %s" % (fmtin % ct, line_stripped)
510 modified = u"%s %s" % (fmtin % ct, line_stripped)
506 output.append(modified)
511 output.append(modified)
507 ct += 1
512 ct += 1
508 try:
513 try:
509 ast.parse(line_stripped)
514 ast.parse(line_stripped)
510 output.append(u'')
515 output.append(u'')
511 except Exception: # on a multiline
516 except Exception: # on a multiline
512 multiline = True
517 multiline = True
513 multiline_start = lineno
518 multiline_start = lineno
514 else: # still on a multiline
519 else: # still on a multiline
515 modified = u'%s %s' % (continuation, line)
520 modified = u'%s %s' % (continuation, line)
516 output.append(modified)
521 output.append(modified)
517
522
518 # if the next line is indented, it should be part of multiline
523 # if the next line is indented, it should be part of multiline
519 if len(content) > lineno + 1:
524 if len(content) > lineno + 1:
520 nextline = content[lineno + 1]
525 nextline = content[lineno + 1]
521 if len(nextline) - len(nextline.lstrip()) > 3:
526 if len(nextline) - len(nextline.lstrip()) > 3:
522 continue
527 continue
523 try:
528 try:
524 mod = ast.parse(
529 mod = ast.parse(
525 '\n'.join(content[multiline_start:lineno+1]))
530 '\n'.join(content[multiline_start:lineno+1]))
526 if isinstance(mod.body[0], ast.FunctionDef):
531 if isinstance(mod.body[0], ast.FunctionDef):
527 # check to see if we have the whole function
532 # check to see if we have the whole function
528 for element in mod.body[0].body:
533 for element in mod.body[0].body:
529 if isinstance(element, ast.Return):
534 if isinstance(element, ast.Return):
530 multiline = False
535 multiline = False
531 else:
536 else:
532 output.append(u'')
537 output.append(u'')
533 multiline = False
538 multiline = False
534 except Exception:
539 except Exception:
535 pass
540 pass
536
541
537 if savefig: # clear figure if plotted
542 if savefig: # clear figure if plotted
538 self.ensure_pyplot()
543 self.ensure_pyplot()
539 self.process_input_line('plt.clf()', store_history=False)
544 self.process_input_line('plt.clf()', store_history=False)
540 self.clear_cout()
545 self.clear_cout()
541 savefig = False
546 savefig = False
542
547
543 return output
548 return output
544
549
545 class IPythonDirective(Directive):
550 class IPythonDirective(Directive):
546
551
547 has_content = True
552 has_content = True
548 required_arguments = 0
553 required_arguments = 0
549 optional_arguments = 4 # python, suppress, verbatim, doctest
554 optional_arguments = 4 # python, suppress, verbatim, doctest
550 final_argumuent_whitespace = True
555 final_argumuent_whitespace = True
551 option_spec = { 'python': directives.unchanged,
556 option_spec = { 'python': directives.unchanged,
552 'suppress' : directives.flag,
557 'suppress' : directives.flag,
553 'verbatim' : directives.flag,
558 'verbatim' : directives.flag,
554 'doctest' : directives.flag,
559 'doctest' : directives.flag,
555 }
560 }
556
561
557 shell = None
562 shell = None
558
563
559 seen_docs = set()
564 seen_docs = set()
560
565
561 def get_config_options(self):
566 def get_config_options(self):
562 # contains sphinx configuration variables
567 # contains sphinx configuration variables
563 config = self.state.document.settings.env.config
568 config = self.state.document.settings.env.config
564
569
565 # get config variables to set figure output directory
570 # get config variables to set figure output directory
566 confdir = self.state.document.settings.env.app.confdir
571 confdir = self.state.document.settings.env.app.confdir
567 savefig_dir = config.ipython_savefig_dir
572 savefig_dir = config.ipython_savefig_dir
568 source_dir = os.path.dirname(self.state.document.current_source)
573 source_dir = os.path.dirname(self.state.document.current_source)
569 if savefig_dir is None:
574 if savefig_dir is None:
570 savefig_dir = config.html_static_path
575 savefig_dir = config.html_static_path
571 if isinstance(savefig_dir, list):
576 if isinstance(savefig_dir, list):
572 savefig_dir = savefig_dir[0] # safe to assume only one path?
577 savefig_dir = savefig_dir[0] # safe to assume only one path?
573 savefig_dir = os.path.join(confdir, savefig_dir)
578 savefig_dir = os.path.join(confdir, savefig_dir)
574
579
575 # get regex and prompt stuff
580 # get regex and prompt stuff
576 rgxin = config.ipython_rgxin
581 rgxin = config.ipython_rgxin
577 rgxout = config.ipython_rgxout
582 rgxout = config.ipython_rgxout
578 promptin = config.ipython_promptin
583 promptin = config.ipython_promptin
579 promptout = config.ipython_promptout
584 promptout = config.ipython_promptout
580 mplbackend = config.ipython_mplbackend
585 mplbackend = config.ipython_mplbackend
586 exec_lines = config.ipython_execlines
581
587
582 return (savefig_dir, source_dir, rgxin, rgxout,
588 return (savefig_dir, source_dir, rgxin, rgxout,
583 promptin, promptout, mplbackend)
589 promptin, promptout, mplbackend, exec_lines)
584
590
585 def setup(self):
591 def setup(self):
586 # Get configuration values.
592 # Get configuration values.
587 (savefig_dir, source_dir, rgxin, rgxout,
593 (savefig_dir, source_dir, rgxin, rgxout, promptin,
588 promptin, promptout, mplbackend) = self.get_config_options()
594 promptout, mplbackend, exec_lines) = self.get_config_options()
589
595
590 if self.shell is None:
596 if self.shell is None:
591 self.shell = EmbeddedSphinxShell()
597 self.shell = EmbeddedSphinxShell(exec_lines)
592 if mplbackend:
598 if mplbackend:
593 # Each ipython code-block is run in a separate process.
599 # Each ipython code-block is run in a separate process.
594 import matplotlib
600 import matplotlib
595 matplotlib.use(mplbackend)
601 matplotlib.use(mplbackend)
596
602
597 # reset the execution count if we haven't processed this doc
603 # reset the execution count if we haven't processed this doc
598 #NOTE: this may be borked if there are multiple seen_doc tmp files
604 #NOTE: this may be borked if there are multiple seen_doc tmp files
599 #check time stamp?
605 #check time stamp?
600 if not self.state.document.current_source in self.seen_docs:
606 if not self.state.document.current_source in self.seen_docs:
601 self.shell.IP.history_manager.reset()
607 self.shell.IP.history_manager.reset()
602 self.shell.IP.execution_count = 1
608 self.shell.IP.execution_count = 1
603 self.seen_docs.add(self.state.document.current_source)
609 self.seen_docs.add(self.state.document.current_source)
604
610
605 # and attach to shell so we don't have to pass them around
611 # and attach to shell so we don't have to pass them around
606 self.shell.rgxin = rgxin
612 self.shell.rgxin = rgxin
607 self.shell.rgxout = rgxout
613 self.shell.rgxout = rgxout
608 self.shell.promptin = promptin
614 self.shell.promptin = promptin
609 self.shell.promptout = promptout
615 self.shell.promptout = promptout
610 self.shell.savefig_dir = savefig_dir
616 self.shell.savefig_dir = savefig_dir
611 self.shell.source_dir = source_dir
617 self.shell.source_dir = source_dir
612
618
613 # setup bookmark for saving figures directory
619 # setup bookmark for saving figures directory
614 self.shell.process_input_line('bookmark ipy_savedir %s'%savefig_dir,
620 self.shell.process_input_line('bookmark ipy_savedir %s'%savefig_dir,
615 store_history=False)
621 store_history=False)
616 self.shell.clear_cout()
622 self.shell.clear_cout()
617
623
618 return rgxin, rgxout, promptin, promptout
624 return rgxin, rgxout, promptin, promptout
619
625
620
626
621 def teardown(self):
627 def teardown(self):
622 # delete last bookmark
628 # delete last bookmark
623 self.shell.process_input_line('bookmark -d ipy_savedir',
629 self.shell.process_input_line('bookmark -d ipy_savedir',
624 store_history=False)
630 store_history=False)
625 self.shell.clear_cout()
631 self.shell.clear_cout()
626
632
627 def run(self):
633 def run(self):
628 debug = False
634 debug = False
629
635
630 #TODO, any reason block_parser can't be a method of embeddable shell
636 #TODO, any reason block_parser can't be a method of embeddable shell
631 # then we wouldn't have to carry these around
637 # then we wouldn't have to carry these around
632 rgxin, rgxout, promptin, promptout = self.setup()
638 rgxin, rgxout, promptin, promptout = self.setup()
633
639
634 options = self.options
640 options = self.options
635 self.shell.is_suppress = 'suppress' in options
641 self.shell.is_suppress = 'suppress' in options
636 self.shell.is_doctest = 'doctest' in options
642 self.shell.is_doctest = 'doctest' in options
637 self.shell.is_verbatim = 'verbatim' in options
643 self.shell.is_verbatim = 'verbatim' in options
638
644
639 # handle pure python code
645 # handle pure python code
640 if 'python' in self.arguments:
646 if 'python' in self.arguments:
641 content = self.content
647 content = self.content
642 self.content = self.shell.process_pure_python(content)
648 self.content = self.shell.process_pure_python(content)
643
649
644 parts = '\n'.join(self.content).split('\n\n')
650 parts = '\n'.join(self.content).split('\n\n')
645
651
646 lines = ['.. code-block:: ipython','']
652 lines = ['.. code-block:: ipython','']
647 figures = []
653 figures = []
648
654
649 for part in parts:
655 for part in parts:
650 block = block_parser(part, rgxin, rgxout, promptin, promptout)
656 block = block_parser(part, rgxin, rgxout, promptin, promptout)
651 if len(block):
657 if len(block):
652 rows, figure = self.shell.process_block(block)
658 rows, figure = self.shell.process_block(block)
653 for row in rows:
659 for row in rows:
654 lines.extend([' %s'%line for line in row.split('\n')])
660 lines.extend([' %s'%line for line in row.split('\n')])
655
661
656 if figure is not None:
662 if figure is not None:
657 figures.append(figure)
663 figures.append(figure)
658
664
659 for figure in figures:
665 for figure in figures:
660 lines.append('')
666 lines.append('')
661 lines.extend(figure.split('\n'))
667 lines.extend(figure.split('\n'))
662 lines.append('')
668 lines.append('')
663
669
664 if len(lines)>2:
670 if len(lines)>2:
665 if debug:
671 if debug:
666 print('\n'.join(lines))
672 print('\n'.join(lines))
667 else:
673 else:
668 # This is what makes the lines appear in the final output.
674 # This is what makes the lines appear in the final output.
669 self.state_machine.insert_input(
675 self.state_machine.insert_input(
670 lines, self.state_machine.input_lines.source(0))
676 lines, self.state_machine.input_lines.source(0))
671
677
672 # cleanup
678 # cleanup
673 self.teardown()
679 self.teardown()
674
680
675 return []#, imgnode]
681 return []#, imgnode]
676
682
677 # Enable as a proper Sphinx directive
683 # Enable as a proper Sphinx directive
678 def setup(app):
684 def setup(app):
679 setup.app = app
685 setup.app = app
680
686
681 app.add_directive('ipython', IPythonDirective)
687 app.add_directive('ipython', IPythonDirective)
682 app.add_config_value('ipython_savefig_dir', None, 'env')
688 app.add_config_value('ipython_savefig_dir', None, 'env')
683 app.add_config_value('ipython_rgxin',
689 app.add_config_value('ipython_rgxin',
684 re.compile('In \[(\d+)\]:\s?(.*)\s*'), 'env')
690 re.compile('In \[(\d+)\]:\s?(.*)\s*'), 'env')
685 app.add_config_value('ipython_rgxout',
691 app.add_config_value('ipython_rgxout',
686 re.compile('Out\[(\d+)\]:\s?(.*)\s*'), 'env')
692 re.compile('Out\[(\d+)\]:\s?(.*)\s*'), 'env')
687 app.add_config_value('ipython_promptin', 'In [%d]:', 'env')
693 app.add_config_value('ipython_promptin', 'In [%d]:', 'env')
688 app.add_config_value('ipython_promptout', 'Out[%d]:', 'env')
694 app.add_config_value('ipython_promptout', 'Out[%d]:', 'env')
689 app.add_config_value('ipython_mplbackend', 'agg', 'env')
695 app.add_config_value('ipython_mplbackend', 'agg', 'env')
690
696 app.add_config_value('ipython_execlines', None, 'env')
691
697
692 # Simple smoke test, needs to be converted to a proper automatic test.
698 # Simple smoke test, needs to be converted to a proper automatic test.
693 def test():
699 def test():
694
700
695 examples = [
701 examples = [
696 r"""
702 r"""
697 In [9]: pwd
703 In [9]: pwd
698 Out[9]: '/home/jdhunter/py4science/book'
704 Out[9]: '/home/jdhunter/py4science/book'
699
705
700 In [10]: cd bookdata/
706 In [10]: cd bookdata/
701 /home/jdhunter/py4science/book/bookdata
707 /home/jdhunter/py4science/book/bookdata
702
708
703 In [2]: from pylab import *
709 In [2]: from pylab import *
704
710
705 In [2]: ion()
711 In [2]: ion()
706
712
707 In [3]: im = imread('stinkbug.png')
713 In [3]: im = imread('stinkbug.png')
708
714
709 @savefig mystinkbug.png width=4in
715 @savefig mystinkbug.png width=4in
710 In [4]: imshow(im)
716 In [4]: imshow(im)
711 Out[4]: <matplotlib.image.AxesImage object at 0x39ea850>
717 Out[4]: <matplotlib.image.AxesImage object at 0x39ea850>
712
718
713 """,
719 """,
714 r"""
720 r"""
715
721
716 In [1]: x = 'hello world'
722 In [1]: x = 'hello world'
717
723
718 # string methods can be
724 # string methods can be
719 # used to alter the string
725 # used to alter the string
720 @doctest
726 @doctest
721 In [2]: x.upper()
727 In [2]: x.upper()
722 Out[2]: 'HELLO WORLD'
728 Out[2]: 'HELLO WORLD'
723
729
724 @verbatim
730 @verbatim
725 In [3]: x.st<TAB>
731 In [3]: x.st<TAB>
726 x.startswith x.strip
732 x.startswith x.strip
727 """,
733 """,
728 r"""
734 r"""
729
735
730 In [130]: url = 'http://ichart.finance.yahoo.com/table.csv?s=CROX\
736 In [130]: url = 'http://ichart.finance.yahoo.com/table.csv?s=CROX\
731 .....: &d=9&e=22&f=2009&g=d&a=1&br=8&c=2006&ignore=.csv'
737 .....: &d=9&e=22&f=2009&g=d&a=1&br=8&c=2006&ignore=.csv'
732
738
733 In [131]: print url.split('&')
739 In [131]: print url.split('&')
734 ['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']
740 ['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']
735
741
736 In [60]: import urllib
742 In [60]: import urllib
737
743
738 """,
744 """,
739 r"""\
745 r"""\
740
746
741 In [133]: import numpy.random
747 In [133]: import numpy.random
742
748
743 @suppress
749 @suppress
744 In [134]: numpy.random.seed(2358)
750 In [134]: numpy.random.seed(2358)
745
751
746 @doctest
752 @doctest
747 In [135]: numpy.random.rand(10,2)
753 In [135]: numpy.random.rand(10,2)
748 Out[135]:
754 Out[135]:
749 array([[ 0.64524308, 0.59943846],
755 array([[ 0.64524308, 0.59943846],
750 [ 0.47102322, 0.8715456 ],
756 [ 0.47102322, 0.8715456 ],
751 [ 0.29370834, 0.74776844],
757 [ 0.29370834, 0.74776844],
752 [ 0.99539577, 0.1313423 ],
758 [ 0.99539577, 0.1313423 ],
753 [ 0.16250302, 0.21103583],
759 [ 0.16250302, 0.21103583],
754 [ 0.81626524, 0.1312433 ],
760 [ 0.81626524, 0.1312433 ],
755 [ 0.67338089, 0.72302393],
761 [ 0.67338089, 0.72302393],
756 [ 0.7566368 , 0.07033696],
762 [ 0.7566368 , 0.07033696],
757 [ 0.22591016, 0.77731835],
763 [ 0.22591016, 0.77731835],
758 [ 0.0072729 , 0.34273127]])
764 [ 0.0072729 , 0.34273127]])
759
765
760 """,
766 """,
761
767
762 r"""
768 r"""
763 In [106]: print x
769 In [106]: print x
764 jdh
770 jdh
765
771
766 In [109]: for i in range(10):
772 In [109]: for i in range(10):
767 .....: print i
773 .....: print i
768 .....:
774 .....:
769 .....:
775 .....:
770 0
776 0
771 1
777 1
772 2
778 2
773 3
779 3
774 4
780 4
775 5
781 5
776 6
782 6
777 7
783 7
778 8
784 8
779 9
785 9
780 """,
786 """,
781
787
782 r"""
788 r"""
783
789
784 In [144]: from pylab import *
790 In [144]: from pylab import *
785
791
786 In [145]: ion()
792 In [145]: ion()
787
793
788 # use a semicolon to suppress the output
794 # use a semicolon to suppress the output
789 @savefig test_hist.png width=4in
795 @savefig test_hist.png width=4in
790 In [151]: hist(np.random.randn(10000), 100);
796 In [151]: hist(np.random.randn(10000), 100);
791
797
792
798
793 @savefig test_plot.png width=4in
799 @savefig test_plot.png width=4in
794 In [151]: plot(np.random.randn(10000), 'o');
800 In [151]: plot(np.random.randn(10000), 'o');
795 """,
801 """,
796
802
797 r"""
803 r"""
798 # use a semicolon to suppress the output
804 # use a semicolon to suppress the output
799 In [151]: plt.clf()
805 In [151]: plt.clf()
800
806
801 @savefig plot_simple.png width=4in
807 @savefig plot_simple.png width=4in
802 In [151]: plot([1,2,3])
808 In [151]: plot([1,2,3])
803
809
804 @savefig hist_simple.png width=4in
810 @savefig hist_simple.png width=4in
805 In [151]: hist(np.random.randn(10000), 100);
811 In [151]: hist(np.random.randn(10000), 100);
806
812
807 """,
813 """,
808 r"""
814 r"""
809 # update the current fig
815 # update the current fig
810 In [151]: ylabel('number')
816 In [151]: ylabel('number')
811
817
812 In [152]: title('normal distribution')
818 In [152]: title('normal distribution')
813
819
814
820
815 @savefig hist_with_text.png
821 @savefig hist_with_text.png
816 In [153]: grid(True)
822 In [153]: grid(True)
817
823
818 """,
824 """,
819 ]
825 ]
820 # skip local-file depending first example:
826 # skip local-file depending first example:
821 examples = examples[1:]
827 examples = examples[1:]
822
828
823 #ipython_directive.DEBUG = True # dbg
829 #ipython_directive.DEBUG = True # dbg
824 #options = dict(suppress=True) # dbg
830 #options = dict(suppress=True) # dbg
825 options = dict()
831 options = dict()
826 for example in examples:
832 for example in examples:
827 content = example.split('\n')
833 content = example.split('\n')
828 IPythonDirective('debug', arguments=None, options=options,
834 IPythonDirective('debug', arguments=None, options=options,
829 content=content, lineno=0,
835 content=content, lineno=0,
830 content_offset=None, block_text=None,
836 content_offset=None, block_text=None,
831 state=None, state_machine=None,
837 state=None, state_machine=None,
832 )
838 )
833
839
834 # Run test suite as a script
840 # Run test suite as a script
835 if __name__=='__main__':
841 if __name__=='__main__':
836 if not os.path.isdir('_static'):
842 if not os.path.isdir('_static'):
837 os.mkdir('_static')
843 os.mkdir('_static')
838 test()
844 test()
839 print('All OK? Check figures in _static/')
845 print('All OK? Check figures in _static/')
General Comments 0
You need to be logged in to leave comments. Login now