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