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