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