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