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