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