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