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