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