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