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