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