##// END OF EJS Templates
support running outside jupyter/ipython...
Moez Bouhlel -
Show More
@@ -1,582 +1,629 b''
1 """Module for interactive demos using IPython.
1 """Module for interactive demos using IPython.
2
2
3 This module implements a few classes for running Python scripts interactively
3 This module implements a few classes for running Python scripts interactively
4 in IPython for demonstrations. With very simple markup (a few tags in
4 in IPython for demonstrations. With very simple markup (a few tags in
5 comments), you can control points where the script stops executing and returns
5 comments), you can control points where the script stops executing and returns
6 control to IPython.
6 control to IPython.
7
7
8
8
9 Provided classes
9 Provided classes
10 ----------------
10 ----------------
11
11
12 The classes are (see their docstrings for further details):
12 The classes are (see their docstrings for further details):
13
13
14 - Demo: pure python demos
14 - Demo: pure python demos
15
15
16 - IPythonDemo: demos with input to be processed by IPython as if it had been
16 - IPythonDemo: demos with input to be processed by IPython as if it had been
17 typed interactively (so magics work, as well as any other special syntax you
17 typed interactively (so magics work, as well as any other special syntax you
18 may have added via input prefilters).
18 may have added via input prefilters).
19
19
20 - LineDemo: single-line version of the Demo class. These demos are executed
20 - LineDemo: single-line version of the Demo class. These demos are executed
21 one line at a time, and require no markup.
21 one line at a time, and require no markup.
22
22
23 - IPythonLineDemo: IPython version of the LineDemo class (the demo is
23 - IPythonLineDemo: IPython version of the LineDemo class (the demo is
24 executed a line at a time, but processed via IPython).
24 executed a line at a time, but processed via IPython).
25
25
26 - ClearMixin: mixin to make Demo classes with less visual clutter. It
26 - ClearMixin: mixin to make Demo classes with less visual clutter. It
27 declares an empty marquee and a pre_cmd that clears the screen before each
27 declares an empty marquee and a pre_cmd that clears the screen before each
28 block (see Subclassing below).
28 block (see Subclassing below).
29
29
30 - ClearDemo, ClearIPDemo: mixin-enabled versions of the Demo and IPythonDemo
30 - ClearDemo, ClearIPDemo: mixin-enabled versions of the Demo and IPythonDemo
31 classes.
31 classes.
32
32
33 Inheritance diagram:
33 Inheritance diagram:
34
34
35 .. inheritance-diagram:: IPython.lib.demo
35 .. inheritance-diagram:: IPython.lib.demo
36 :parts: 3
36 :parts: 3
37
37
38 Subclassing
38 Subclassing
39 -----------
39 -----------
40
40
41 The classes here all include a few methods meant to make customization by
41 The classes here all include a few methods meant to make customization by
42 subclassing more convenient. Their docstrings below have some more details:
42 subclassing more convenient. Their docstrings below have some more details:
43
43
44 - highlight(): format every block and optionally highlight comments and
45 docstring content.
46
44 - marquee(): generates a marquee to provide visible on-screen markers at each
47 - marquee(): generates a marquee to provide visible on-screen markers at each
45 block start and end.
48 block start and end.
46
49
47 - pre_cmd(): run right before the execution of each block.
50 - pre_cmd(): run right before the execution of each block.
48
51
49 - post_cmd(): run right after the execution of each block. If the block
52 - post_cmd(): run right after the execution of each block. If the block
50 raises an exception, this is NOT called.
53 raises an exception, this is NOT called.
51
54
52
55
53 Operation
56 Operation
54 ---------
57 ---------
55
58
56 The file is run in its own empty namespace (though you can pass it a string of
59 The file is run in its own empty namespace (though you can pass it a string of
57 arguments as if in a command line environment, and it will see those as
60 arguments as if in a command line environment, and it will see those as
58 sys.argv). But at each stop, the global IPython namespace is updated with the
61 sys.argv). But at each stop, the global IPython namespace is updated with the
59 current internal demo namespace, so you can work interactively with the data
62 current internal demo namespace, so you can work interactively with the data
60 accumulated so far.
63 accumulated so far.
61
64
62 By default, each block of code is printed (with syntax highlighting) before
65 By default, each block of code is printed (with syntax highlighting) before
63 executing it and you have to confirm execution. This is intended to show the
66 executing it and you have to confirm execution. This is intended to show the
64 code to an audience first so you can discuss it, and only proceed with
67 code to an audience first so you can discuss it, and only proceed with
65 execution once you agree. There are a few tags which allow you to modify this
68 execution once you agree. There are a few tags which allow you to modify this
66 behavior.
69 behavior.
67
70
68 The supported tags are:
71 The supported tags are:
69
72
70 # <demo> stop
73 # <demo> stop
71
74
72 Defines block boundaries, the points where IPython stops execution of the
75 Defines block boundaries, the points where IPython stops execution of the
73 file and returns to the interactive prompt.
76 file and returns to the interactive prompt.
74
77
75 You can optionally mark the stop tag with extra dashes before and after the
78 You can optionally mark the stop tag with extra dashes before and after the
76 word 'stop', to help visually distinguish the blocks in a text editor:
79 word 'stop', to help visually distinguish the blocks in a text editor:
77
80
78 # <demo> --- stop ---
81 # <demo> --- stop ---
79
82
80
83
81 # <demo> silent
84 # <demo> silent
82
85
83 Make a block execute silently (and hence automatically). Typically used in
86 Make a block execute silently (and hence automatically). Typically used in
84 cases where you have some boilerplate or initialization code which you need
87 cases where you have some boilerplate or initialization code which you need
85 executed but do not want to be seen in the demo.
88 executed but do not want to be seen in the demo.
86
89
87 # <demo> auto
90 # <demo> auto
88
91
89 Make a block execute automatically, but still being printed. Useful for
92 Make a block execute automatically, but still being printed. Useful for
90 simple code which does not warrant discussion, since it avoids the extra
93 simple code which does not warrant discussion, since it avoids the extra
91 manual confirmation.
94 manual confirmation.
92
95
93 # <demo> auto_all
96 # <demo> auto_all
94
97
95 This tag can _only_ be in the first block, and if given it overrides the
98 This tag can _only_ be in the first block, and if given it overrides the
96 individual auto tags to make the whole demo fully automatic (no block asks
99 individual auto tags to make the whole demo fully automatic (no block asks
97 for confirmation). It can also be given at creation time (or the attribute
100 for confirmation). It can also be given at creation time (or the attribute
98 set later) to override what's in the file.
101 set later) to override what's in the file.
99
102
100 While _any_ python file can be run as a Demo instance, if there are no stop
103 While _any_ python file can be run as a Demo instance, if there are no stop
101 tags the whole file will run in a single block (no different that calling
104 tags the whole file will run in a single block (no different that calling
102 first %pycat and then %run). The minimal markup to make this useful is to
105 first %pycat and then %run). The minimal markup to make this useful is to
103 place a set of stop tags; the other tags are only there to let you fine-tune
106 place a set of stop tags; the other tags are only there to let you fine-tune
104 the execution.
107 the execution.
105
108
106 This is probably best explained with the simple example file below. You can
109 This is probably best explained with the simple example file below. You can
107 copy this into a file named ex_demo.py, and try running it via::
110 copy this into a file named ex_demo.py, and try running it via::
108
111
109 from IPython.demo import Demo
112 from IPython.demo import Demo
110 d = Demo('ex_demo.py')
113 d = Demo('ex_demo.py')
111 d()
114 d()
112
115
113 Each time you call the demo object, it runs the next block. The demo object
116 Each time you call the demo object, it runs the next block. The demo object
114 has a few useful methods for navigation, like again(), edit(), jump(), seek()
117 has a few useful methods for navigation, like again(), edit(), jump(), seek()
115 and back(). It can be reset for a new run via reset() or reloaded from disk
118 and back(). It can be reset for a new run via reset() or reloaded from disk
116 (in case you've edited the source) via reload(). See their docstrings below.
119 (in case you've edited the source) via reload(). See their docstrings below.
117
120
118 Note: To make this simpler to explore, a file called "demo-exercizer.py" has
121 Note: To make this simpler to explore, a file called "demo-exercizer.py" has
119 been added to the "docs/examples/core" directory. Just cd to this directory in
122 been added to the "docs/examples/core" directory. Just cd to this directory in
120 an IPython session, and type::
123 an IPython session, and type::
121
124
122 %run demo-exercizer.py
125 %run demo-exercizer.py
123
126
124 and then follow the directions.
127 and then follow the directions.
125
128
126 Example
129 Example
127 -------
130 -------
128
131
129 The following is a very simple example of a valid demo file.
132 The following is a very simple example of a valid demo file.
130
133
131 ::
134 ::
132
135
133 #################### EXAMPLE DEMO <ex_demo.py> ###############################
136 #################### EXAMPLE DEMO <ex_demo.py> ###############################
134 '''A simple interactive demo to illustrate the use of IPython's Demo class.'''
137 '''A simple interactive demo to illustrate the use of IPython's Demo class.'''
135
138
136 print 'Hello, welcome to an interactive IPython demo.'
139 print 'Hello, welcome to an interactive IPython demo.'
137
140
138 # The mark below defines a block boundary, which is a point where IPython will
141 # The mark below defines a block boundary, which is a point where IPython will
139 # stop execution and return to the interactive prompt. The dashes are actually
142 # stop execution and return to the interactive prompt. The dashes are actually
140 # optional and used only as a visual aid to clearly separate blocks while
143 # optional and used only as a visual aid to clearly separate blocks while
141 # editing the demo code.
144 # editing the demo code.
142 # <demo> stop
145 # <demo> stop
143
146
144 x = 1
147 x = 1
145 y = 2
148 y = 2
146
149
147 # <demo> stop
150 # <demo> stop
148
151
149 # the mark below makes this block as silent
152 # the mark below makes this block as silent
150 # <demo> silent
153 # <demo> silent
151
154
152 print 'This is a silent block, which gets executed but not printed.'
155 print 'This is a silent block, which gets executed but not printed.'
153
156
154 # <demo> stop
157 # <demo> stop
155 # <demo> auto
158 # <demo> auto
156 print 'This is an automatic block.'
159 print 'This is an automatic block.'
157 print 'It is executed without asking for confirmation, but printed.'
160 print 'It is executed without asking for confirmation, but printed.'
158 z = x+y
161 z = x+y
159
162
160 print 'z=',x
163 print 'z=',x
161
164
162 # <demo> stop
165 # <demo> stop
163 # This is just another normal block.
166 # This is just another normal block.
164 print 'z is now:', z
167 print 'z is now:', z
165
168
166 print 'bye!'
169 print 'bye!'
167 ################### END EXAMPLE DEMO <ex_demo.py> ############################
170 ################### END EXAMPLE DEMO <ex_demo.py> ############################
168 """
171 """
169
172
170 from __future__ import unicode_literals
173 from __future__ import unicode_literals
171
174
172 #*****************************************************************************
175 #*****************************************************************************
173 # Copyright (C) 2005-2006 Fernando Perez. <Fernando.Perez@colorado.edu>
176 # Copyright (C) 2005-2006 Fernando Perez. <Fernando.Perez@colorado.edu>
174 #
177 #
175 # Distributed under the terms of the BSD License. The full license is in
178 # Distributed under the terms of the BSD License. The full license is in
176 # the file COPYING, distributed as part of this software.
179 # the file COPYING, distributed as part of this software.
177 #
180 #
178 #*****************************************************************************
181 #*****************************************************************************
179 from __future__ import print_function
182 from __future__ import print_function
180
183
181 import os
184 import os
182 import re
185 import re
183 import shlex
186 import shlex
184 import sys
187 import sys
188 import pygments
185
189
186 from IPython.utils.text import marquee
190 from IPython.utils.text import marquee
187 from IPython.utils import openpy
191 from IPython.utils import openpy
188 from IPython.utils import py3compat
192 from IPython.utils import py3compat
189 __all__ = ['Demo','IPythonDemo','LineDemo','IPythonLineDemo','DemoError']
193 __all__ = ['Demo','IPythonDemo','LineDemo','IPythonLineDemo','DemoError']
190
194
191 class DemoError(Exception): pass
195 class DemoError(Exception): pass
192
196
193 def re_mark(mark):
197 def re_mark(mark):
194 return re.compile(r'^\s*#\s+<demo>\s+%s\s*$' % mark,re.MULTILINE)
198 return re.compile(r'^\s*#\s+<demo>\s+%s\s*$' % mark,re.MULTILINE)
195
199
196 class Demo(object):
200 class Demo(object):
197
201
198 re_stop = re_mark('-*\s?stop\s?-*')
202 re_stop = re_mark('-*\s?stop\s?-*')
199 re_silent = re_mark('silent')
203 re_silent = re_mark('silent')
200 re_auto = re_mark('auto')
204 re_auto = re_mark('auto')
201 re_auto_all = re_mark('auto_all')
205 re_auto_all = re_mark('auto_all')
202
206
203 def __init__(self,src,title='',arg_str='',auto_all=None):
207 def __init__(self,src,title='',arg_str='',auto_all=None, format_rst=False,
208 formatter='terminal', style='default'):
204 """Make a new demo object. To run the demo, simply call the object.
209 """Make a new demo object. To run the demo, simply call the object.
205
210
206 See the module docstring for full details and an example (you can use
211 See the module docstring for full details and an example (you can use
207 IPython.Demo? in IPython to see it).
212 IPython.Demo? in IPython to see it).
208
213
209 Inputs:
214 Inputs:
210
215
211 - src is either a file, or file-like object, or a
216 - src is either a file, or file-like object, or a
212 string that can be resolved to a filename.
217 string that can be resolved to a filename.
213
218
214 Optional inputs:
219 Optional inputs:
215
220
216 - title: a string to use as the demo name. Of most use when the demo
221 - title: a string to use as the demo name. Of most use when the demo
217 you are making comes from an object that has no filename, or if you
222 you are making comes from an object that has no filename, or if you
218 want an alternate denotation distinct from the filename.
223 want an alternate denotation distinct from the filename.
219
224
220 - arg_str(''): a string of arguments, internally converted to a list
225 - arg_str(''): a string of arguments, internally converted to a list
221 just like sys.argv, so the demo script can see a similar
226 just like sys.argv, so the demo script can see a similar
222 environment.
227 environment.
223
228
224 - auto_all(None): global flag to run all blocks automatically without
229 - auto_all(None): global flag to run all blocks automatically without
225 confirmation. This attribute overrides the block-level tags and
230 confirmation. This attribute overrides the block-level tags and
226 applies to the whole demo. It is an attribute of the object, and
231 applies to the whole demo. It is an attribute of the object, and
227 can be changed at runtime simply by reassigning it to a boolean
232 can be changed at runtime simply by reassigning it to a boolean
228 value.
233 value.
234
235 - format_rst(False): a bool to enable comments and doc strings
236 formating with pygments rst lexer
237
238 - formatter('terminal'): a string of pygments formatter name to be
239 used. Useful values for terminals: terminal, terminal256,
240 terminal16m
241
242 - style('default'): a string of pygments style name to be used.
229 """
243 """
230 if hasattr(src, "read"):
244 if hasattr(src, "read"):
231 # It seems to be a file or a file-like object
245 # It seems to be a file or a file-like object
232 self.fname = "from a file-like object"
246 self.fname = "from a file-like object"
233 if title == '':
247 if title == '':
234 self.title = "from a file-like object"
248 self.title = "from a file-like object"
235 else:
249 else:
236 self.title = title
250 self.title = title
237 else:
251 else:
238 # Assume it's a string or something that can be converted to one
252 # Assume it's a string or something that can be converted to one
239 self.fname = src
253 self.fname = src
240 if title == '':
254 if title == '':
241 (filepath, filename) = os.path.split(src)
255 (filepath, filename) = os.path.split(src)
242 self.title = filename
256 self.title = filename
243 else:
257 else:
244 self.title = title
258 self.title = title
245 self.sys_argv = [src] + shlex.split(arg_str)
259 self.sys_argv = [src] + shlex.split(arg_str)
246 self.auto_all = auto_all
260 self.auto_all = auto_all
247 self.src = src
261 self.src = src
248
262
249 # get a few things from ipython. While it's a bit ugly design-wise,
263 self.inside_ipython = "get_ipython" in globals()
250 # it ensures that things like color scheme and the like are always in
264 if self.inside_ipython:
251 # sync with the ipython mode being used. This class is only meant to
265 # get a few things from ipython. While it's a bit ugly design-wise,
252 # be used inside ipython anyways, so it's OK.
266 # it ensures that things like color scheme and the like are always in
253 ip = get_ipython() # this is in builtins whenever IPython is running
267 # sync with the ipython mode being used. This class is only meant to
254 self.ip_ns = ip.user_ns
268 # be used inside ipython anyways, so it's OK.
255 self.ip_colorize = ip.pycolorize
269 ip = get_ipython() # this is in builtins whenever IPython is running
256 self.ip_showtb = ip.showtraceback
270 self.ip_ns = ip.user_ns
257 self.ip_run_cell = ip.run_cell
271 self.ip_colorize = ip.pycolorize
258 self.shell = ip
272 self.ip_showtb = ip.showtraceback
273 self.ip_run_cell = ip.run_cell
274 self.shell = ip
275
276 self.formatter = pygments.formatters.get_formatter_by_name(formatter,
277 style=style)
278 self.python_lexer = pygments.lexers.get_lexer_by_name("py3")
279 self.format_rst = format_rst
280 if format_rst:
281 self.rst_lexer = pygments.lexers.get_lexer_by_name("rst")
259
282
260 # load user data and initialize data structures
283 # load user data and initialize data structures
261 self.reload()
284 self.reload()
262
285
263 def fload(self):
286 def fload(self):
264 """Load file object."""
287 """Load file object."""
265 # read data and parse into blocks
288 # read data and parse into blocks
266 if hasattr(self, 'fobj') and self.fobj is not None:
289 if hasattr(self, 'fobj') and self.fobj is not None:
267 self.fobj.close()
290 self.fobj.close()
268 if hasattr(self.src, "read"):
291 if hasattr(self.src, "read"):
269 # It seems to be a file or a file-like object
292 # It seems to be a file or a file-like object
270 self.fobj = self.src
293 self.fobj = self.src
271 else:
294 else:
272 # Assume it's a string or something that can be converted to one
295 # Assume it's a string or something that can be converted to one
273 self.fobj = openpy.open(self.fname)
296 self.fobj = openpy.open(self.fname)
274
297
275 def reload(self):
298 def reload(self):
276 """Reload source from disk and initialize state."""
299 """Reload source from disk and initialize state."""
277 self.fload()
300 self.fload()
278
301
279 self.src = "".join(openpy.strip_encoding_cookie(self.fobj))
302 self.src = "".join(openpy.strip_encoding_cookie(self.fobj))
280 src_b = [b.strip() for b in self.re_stop.split(self.src) if b]
303 src_b = [b.strip() for b in self.re_stop.split(self.src) if b]
281 self._silent = [bool(self.re_silent.findall(b)) for b in src_b]
304 self._silent = [bool(self.re_silent.findall(b)) for b in src_b]
282 self._auto = [bool(self.re_auto.findall(b)) for b in src_b]
305 self._auto = [bool(self.re_auto.findall(b)) for b in src_b]
283
306
284 # if auto_all is not given (def. None), we read it from the file
307 # if auto_all is not given (def. None), we read it from the file
285 if self.auto_all is None:
308 if self.auto_all is None:
286 self.auto_all = bool(self.re_auto_all.findall(src_b[0]))
309 self.auto_all = bool(self.re_auto_all.findall(src_b[0]))
287 else:
310 else:
288 self.auto_all = bool(self.auto_all)
311 self.auto_all = bool(self.auto_all)
289
312
290 # Clean the sources from all markup so it doesn't get displayed when
313 # Clean the sources from all markup so it doesn't get displayed when
291 # running the demo
314 # running the demo
292 src_blocks = []
315 src_blocks = []
293 auto_strip = lambda s: self.re_auto.sub('',s)
316 auto_strip = lambda s: self.re_auto.sub('',s)
294 for i,b in enumerate(src_b):
317 for i,b in enumerate(src_b):
295 if self._auto[i]:
318 if self._auto[i]:
296 src_blocks.append(auto_strip(b))
319 src_blocks.append(auto_strip(b))
297 else:
320 else:
298 src_blocks.append(b)
321 src_blocks.append(b)
299 # remove the auto_all marker
322 # remove the auto_all marker
300 src_blocks[0] = self.re_auto_all.sub('',src_blocks[0])
323 src_blocks[0] = self.re_auto_all.sub('',src_blocks[0])
301
324
302 self.nblocks = len(src_blocks)
325 self.nblocks = len(src_blocks)
303 self.src_blocks = src_blocks
326 self.src_blocks = src_blocks
304
327
305 # also build syntax-highlighted source
328 # also build syntax-highlighted source
306 self.src_blocks_colored = list(map(self.ip_colorize,self.src_blocks))
329 self.src_blocks_colored = list(map(self.highlight,self.src_blocks))
307
330
308 # ensure clean namespace and seek offset
331 # ensure clean namespace and seek offset
309 self.reset()
332 self.reset()
310
333
311 def reset(self):
334 def reset(self):
312 """Reset the namespace and seek pointer to restart the demo"""
335 """Reset the namespace and seek pointer to restart the demo"""
313 self.user_ns = {}
336 self.user_ns = {}
314 self.finished = False
337 self.finished = False
315 self.block_index = 0
338 self.block_index = 0
316
339
317 def _validate_index(self,index):
340 def _validate_index(self,index):
318 if index<0 or index>=self.nblocks:
341 if index<0 or index>=self.nblocks:
319 raise ValueError('invalid block index %s' % index)
342 raise ValueError('invalid block index %s' % index)
320
343
321 def _get_index(self,index):
344 def _get_index(self,index):
322 """Get the current block index, validating and checking status.
345 """Get the current block index, validating and checking status.
323
346
324 Returns None if the demo is finished"""
347 Returns None if the demo is finished"""
325
348
326 if index is None:
349 if index is None:
327 if self.finished:
350 if self.finished:
328 print('Demo finished. Use <demo_name>.reset() if you want to rerun it.')
351 print('Demo finished. Use <demo_name>.reset() if you want to rerun it.')
329 return None
352 return None
330 index = self.block_index
353 index = self.block_index
331 else:
354 else:
332 self._validate_index(index)
355 self._validate_index(index)
333 return index
356 return index
334
357
335 def seek(self,index):
358 def seek(self,index):
336 """Move the current seek pointer to the given block.
359 """Move the current seek pointer to the given block.
337
360
338 You can use negative indices to seek from the end, with identical
361 You can use negative indices to seek from the end, with identical
339 semantics to those of Python lists."""
362 semantics to those of Python lists."""
340 if index<0:
363 if index<0:
341 index = self.nblocks + index
364 index = self.nblocks + index
342 self._validate_index(index)
365 self._validate_index(index)
343 self.block_index = index
366 self.block_index = index
344 self.finished = False
367 self.finished = False
345
368
346 def back(self,num=1):
369 def back(self,num=1):
347 """Move the seek pointer back num blocks (default is 1)."""
370 """Move the seek pointer back num blocks (default is 1)."""
348 self.seek(self.block_index-num)
371 self.seek(self.block_index-num)
349
372
350 def jump(self,num=1):
373 def jump(self,num=1):
351 """Jump a given number of blocks relative to the current one.
374 """Jump a given number of blocks relative to the current one.
352
375
353 The offset can be positive or negative, defaults to 1."""
376 The offset can be positive or negative, defaults to 1."""
354 self.seek(self.block_index+num)
377 self.seek(self.block_index+num)
355
378
356 def again(self):
379 def again(self):
357 """Move the seek pointer back one block and re-execute."""
380 """Move the seek pointer back one block and re-execute."""
358 self.back(1)
381 self.back(1)
359 self()
382 self()
360
383
361 def edit(self,index=None):
384 def edit(self,index=None):
362 """Edit a block.
385 """Edit a block.
363
386
364 If no number is given, use the last block executed.
387 If no number is given, use the last block executed.
365
388
366 This edits the in-memory copy of the demo, it does NOT modify the
389 This edits the in-memory copy of the demo, it does NOT modify the
367 original source file. If you want to do that, simply open the file in
390 original source file. If you want to do that, simply open the file in
368 an editor and use reload() when you make changes to the file. This
391 an editor and use reload() when you make changes to the file. This
369 method is meant to let you change a block during a demonstration for
392 method is meant to let you change a block during a demonstration for
370 explanatory purposes, without damaging your original script."""
393 explanatory purposes, without damaging your original script."""
371
394
372 index = self._get_index(index)
395 index = self._get_index(index)
373 if index is None:
396 if index is None:
374 return
397 return
375 # decrease the index by one (unless we're at the very beginning), so
398 # decrease the index by one (unless we're at the very beginning), so
376 # that the default demo.edit() call opens up the sblock we've last run
399 # that the default demo.edit() call opens up the sblock we've last run
377 if index>0:
400 if index>0:
378 index -= 1
401 index -= 1
379
402
380 filename = self.shell.mktempfile(self.src_blocks[index])
403 filename = self.shell.mktempfile(self.src_blocks[index])
381 self.shell.hooks.editor(filename,1)
404 self.shell.hooks.editor(filename,1)
382 with open(filename, 'r') as f:
405 with open(filename, 'r') as f:
383 new_block = f.read()
406 new_block = f.read()
384 # update the source and colored block
407 # update the source and colored block
385 self.src_blocks[index] = new_block
408 self.src_blocks[index] = new_block
386 self.src_blocks_colored[index] = self.ip_colorize(new_block)
409 self.src_blocks_colored[index] = self.highlight(new_block)
387 self.block_index = index
410 self.block_index = index
388 # call to run with the newly edited index
411 # call to run with the newly edited index
389 self()
412 self()
390
413
391 def show(self,index=None):
414 def show(self,index=None):
392 """Show a single block on screen"""
415 """Show a single block on screen"""
393
416
394 index = self._get_index(index)
417 index = self._get_index(index)
395 if index is None:
418 if index is None:
396 return
419 return
397
420
398 print(self.marquee('<%s> block # %s (%s remaining)' %
421 print(self.marquee('<%s> block # %s (%s remaining)' %
399 (self.title,index,self.nblocks-index-1)))
422 (self.title,index,self.nblocks-index-1)))
400 print(self.src_blocks_colored[index])
423 print(self.src_blocks_colored[index])
401 sys.stdout.flush()
424 sys.stdout.flush()
402
425
403 def show_all(self):
426 def show_all(self):
404 """Show entire demo on screen, block by block"""
427 """Show entire demo on screen, block by block"""
405
428
406 fname = self.title
429 fname = self.title
407 title = self.title
430 title = self.title
408 nblocks = self.nblocks
431 nblocks = self.nblocks
409 silent = self._silent
432 silent = self._silent
410 marquee = self.marquee
433 marquee = self.marquee
411 for index,block in enumerate(self.src_blocks_colored):
434 for index,block in enumerate(self.src_blocks_colored):
412 if silent[index]:
435 if silent[index]:
413 print(marquee('<%s> SILENT block # %s (%s remaining)' %
436 print(marquee('<%s> SILENT block # %s (%s remaining)' %
414 (title,index,nblocks-index-1)))
437 (title,index,nblocks-index-1)))
415 else:
438 else:
416 print(marquee('<%s> block # %s (%s remaining)' %
439 print(marquee('<%s> block # %s (%s remaining)' %
417 (title,index,nblocks-index-1)))
440 (title,index,nblocks-index-1)))
418 print(block, end=' ')
441 print(block, end=' ')
419 sys.stdout.flush()
442 sys.stdout.flush()
420
443
421 def run_cell(self,source):
444 def run_cell(self,source):
422 """Execute a string with one or more lines of code"""
445 """Execute a string with one or more lines of code"""
423
446
424 exec(source, self.user_ns)
447 exec(source, self.user_ns)
425
448
426 def __call__(self,index=None):
449 def __call__(self,index=None):
427 """run a block of the demo.
450 """run a block of the demo.
428
451
429 If index is given, it should be an integer >=1 and <= nblocks. This
452 If index is given, it should be an integer >=1 and <= nblocks. This
430 means that the calling convention is one off from typical Python
453 means that the calling convention is one off from typical Python
431 lists. The reason for the inconsistency is that the demo always
454 lists. The reason for the inconsistency is that the demo always
432 prints 'Block n/N, and N is the total, so it would be very odd to use
455 prints 'Block n/N, and N is the total, so it would be very odd to use
433 zero-indexing here."""
456 zero-indexing here."""
434
457
435 index = self._get_index(index)
458 index = self._get_index(index)
436 if index is None:
459 if index is None:
437 return
460 return
438 try:
461 try:
439 marquee = self.marquee
462 marquee = self.marquee
440 next_block = self.src_blocks[index]
463 next_block = self.src_blocks[index]
441 self.block_index += 1
464 self.block_index += 1
442 if self._silent[index]:
465 if self._silent[index]:
443 print(marquee('Executing silent block # %s (%s remaining)' %
466 print(marquee('Executing silent block # %s (%s remaining)' %
444 (index,self.nblocks-index-1)))
467 (index,self.nblocks-index-1)))
445 else:
468 else:
446 self.pre_cmd()
469 self.pre_cmd()
447 self.show(index)
470 self.show(index)
448 if self.auto_all or self._auto[index]:
471 if self.auto_all or self._auto[index]:
449 print(marquee('output:'))
472 print(marquee('output:'))
450 else:
473 else:
451 print(marquee('Press <q> to quit, <Enter> to execute...'), end=' ')
474 print(marquee('Press <q> to quit, <Enter> to execute...'), end=' ')
452 ans = py3compat.input().strip()
475 ans = py3compat.input().strip()
453 if ans:
476 if ans:
454 print(marquee('Block NOT executed'))
477 print(marquee('Block NOT executed'))
455 return
478 return
456 try:
479 try:
457 save_argv = sys.argv
480 save_argv = sys.argv
458 sys.argv = self.sys_argv
481 sys.argv = self.sys_argv
459 self.run_cell(next_block)
482 self.run_cell(next_block)
460 self.post_cmd()
483 self.post_cmd()
461 finally:
484 finally:
462 sys.argv = save_argv
485 sys.argv = save_argv
463
486
464 except:
487 except:
465 self.ip_showtb(filename=self.fname)
488 if self.inside_ipython:
489 self.ip_showtb(filename=self.fname)
466 else:
490 else:
467 self.ip_ns.update(self.user_ns)
491 if self.inside_ipython:
492 self.ip_ns.update(self.user_ns)
468
493
469 if self.block_index == self.nblocks:
494 if self.block_index == self.nblocks:
470 mq1 = self.marquee('END OF DEMO')
495 mq1 = self.marquee('END OF DEMO')
471 if mq1:
496 if mq1:
472 # avoid spurious print if empty marquees are used
497 # avoid spurious print if empty marquees are used
473 print()
498 print()
474 print(mq1)
499 print(mq1)
475 print(self.marquee('Use <demo_name>.reset() if you want to rerun it.'))
500 print(self.marquee('Use <demo_name>.reset() if you want to rerun it.'))
476 self.finished = True
501 self.finished = True
477
502
478 # These methods are meant to be overridden by subclasses who may wish to
503 # These methods are meant to be overridden by subclasses who may wish to
479 # customize the behavior of of their demos.
504 # customize the behavior of of their demos.
480 def marquee(self,txt='',width=78,mark='*'):
505 def marquee(self,txt='',width=78,mark='*'):
481 """Return the input string centered in a 'marquee'."""
506 """Return the input string centered in a 'marquee'."""
482 return marquee(txt,width,mark)
507 return marquee(txt,width,mark)
483
508
484 def pre_cmd(self):
509 def pre_cmd(self):
485 """Method called before executing each block."""
510 """Method called before executing each block."""
486 pass
511 pass
487
512
488 def post_cmd(self):
513 def post_cmd(self):
489 """Method called after executing each block."""
514 """Method called after executing each block."""
490 pass
515 pass
491
516
517 def highlight(self, block):
518 """Method called on each block to highlight it content"""
519 tokens = pygments.lex(block, self.python_lexer)
520 if self.format_rst:
521 from pygments.token import Token
522 toks = []
523 for token in tokens:
524 if token[0] == Token.String.Doc and len(token[1]) > 6:
525 toks += pygments.lex(token[1][:3], self.python_lexer)
526 # parse doc string content by rst lexer
527 toks += pygments.lex(token[1][3:-3], self.rst_lexer)
528 toks += pygments.lex(token[1][-3:], self.python_lexer)
529 elif token[0] == Token.Comment.Single:
530 toks.append((Token.Comment.Single, token[1][0]))
531 # parse comment content by rst lexer
532 # remove the extrat newline added by rst lexer
533 toks += list(pygments.lex(token[1][1:], self.rst_lexer))[:-1]
534 else:
535 toks.append(token)
536 tokens = toks
537 return pygments.format(tokens, self.formatter)
538
492
539
493 class IPythonDemo(Demo):
540 class IPythonDemo(Demo):
494 """Class for interactive demos with IPython's input processing applied.
541 """Class for interactive demos with IPython's input processing applied.
495
542
496 This subclasses Demo, but instead of executing each block by the Python
543 This subclasses Demo, but instead of executing each block by the Python
497 interpreter (via exec), it actually calls IPython on it, so that any input
544 interpreter (via exec), it actually calls IPython on it, so that any input
498 filters which may be in place are applied to the input block.
545 filters which may be in place are applied to the input block.
499
546
500 If you have an interactive environment which exposes special input
547 If you have an interactive environment which exposes special input
501 processing, you can use this class instead to write demo scripts which
548 processing, you can use this class instead to write demo scripts which
502 operate exactly as if you had typed them interactively. The default Demo
549 operate exactly as if you had typed them interactively. The default Demo
503 class requires the input to be valid, pure Python code.
550 class requires the input to be valid, pure Python code.
504 """
551 """
505
552
506 def run_cell(self,source):
553 def run_cell(self,source):
507 """Execute a string with one or more lines of code"""
554 """Execute a string with one or more lines of code"""
508
555
509 self.shell.run_cell(source)
556 self.shell.run_cell(source)
510
557
511 class LineDemo(Demo):
558 class LineDemo(Demo):
512 """Demo where each line is executed as a separate block.
559 """Demo where each line is executed as a separate block.
513
560
514 The input script should be valid Python code.
561 The input script should be valid Python code.
515
562
516 This class doesn't require any markup at all, and it's meant for simple
563 This class doesn't require any markup at all, and it's meant for simple
517 scripts (with no nesting or any kind of indentation) which consist of
564 scripts (with no nesting or any kind of indentation) which consist of
518 multiple lines of input to be executed, one at a time, as if they had been
565 multiple lines of input to be executed, one at a time, as if they had been
519 typed in the interactive prompt.
566 typed in the interactive prompt.
520
567
521 Note: the input can not have *any* indentation, which means that only
568 Note: the input can not have *any* indentation, which means that only
522 single-lines of input are accepted, not even function definitions are
569 single-lines of input are accepted, not even function definitions are
523 valid."""
570 valid."""
524
571
525 def reload(self):
572 def reload(self):
526 """Reload source from disk and initialize state."""
573 """Reload source from disk and initialize state."""
527 # read data and parse into blocks
574 # read data and parse into blocks
528 self.fload()
575 self.fload()
529 lines = self.fobj.readlines()
576 lines = self.fobj.readlines()
530 src_b = [l for l in lines if l.strip()]
577 src_b = [l for l in lines if l.strip()]
531 nblocks = len(src_b)
578 nblocks = len(src_b)
532 self.src = ''.join(lines)
579 self.src = ''.join(lines)
533 self._silent = [False]*nblocks
580 self._silent = [False]*nblocks
534 self._auto = [True]*nblocks
581 self._auto = [True]*nblocks
535 self.auto_all = True
582 self.auto_all = True
536 self.nblocks = nblocks
583 self.nblocks = nblocks
537 self.src_blocks = src_b
584 self.src_blocks = src_b
538
585
539 # also build syntax-highlighted source
586 # also build syntax-highlighted source
540 self.src_blocks_colored = list(map(self.ip_colorize,self.src_blocks))
587 self.src_blocks_colored = list(map(self.highlight,self.src_blocks))
541
588
542 # ensure clean namespace and seek offset
589 # ensure clean namespace and seek offset
543 self.reset()
590 self.reset()
544
591
545
592
546 class IPythonLineDemo(IPythonDemo,LineDemo):
593 class IPythonLineDemo(IPythonDemo,LineDemo):
547 """Variant of the LineDemo class whose input is processed by IPython."""
594 """Variant of the LineDemo class whose input is processed by IPython."""
548 pass
595 pass
549
596
550
597
551 class ClearMixin(object):
598 class ClearMixin(object):
552 """Use this mixin to make Demo classes with less visual clutter.
599 """Use this mixin to make Demo classes with less visual clutter.
553
600
554 Demos using this mixin will clear the screen before every block and use
601 Demos using this mixin will clear the screen before every block and use
555 blank marquees.
602 blank marquees.
556
603
557 Note that in order for the methods defined here to actually override those
604 Note that in order for the methods defined here to actually override those
558 of the classes it's mixed with, it must go /first/ in the inheritance
605 of the classes it's mixed with, it must go /first/ in the inheritance
559 tree. For example:
606 tree. For example:
560
607
561 class ClearIPDemo(ClearMixin,IPythonDemo): pass
608 class ClearIPDemo(ClearMixin,IPythonDemo): pass
562
609
563 will provide an IPythonDemo class with the mixin's features.
610 will provide an IPythonDemo class with the mixin's features.
564 """
611 """
565
612
566 def marquee(self,txt='',width=78,mark='*'):
613 def marquee(self,txt='',width=78,mark='*'):
567 """Blank marquee that returns '' no matter what the input."""
614 """Blank marquee that returns '' no matter what the input."""
568 return ''
615 return ''
569
616
570 def pre_cmd(self):
617 def pre_cmd(self):
571 """Method called before executing each block.
618 """Method called before executing each block.
572
619
573 This one simply clears the screen."""
620 This one simply clears the screen."""
574 from IPython.utils.terminal import _term_clear
621 from IPython.utils.terminal import _term_clear
575 _term_clear()
622 _term_clear()
576
623
577 class ClearDemo(ClearMixin,Demo):
624 class ClearDemo(ClearMixin,Demo):
578 pass
625 pass
579
626
580
627
581 class ClearIPDemo(ClearMixin,IPythonDemo):
628 class ClearIPDemo(ClearMixin,IPythonDemo):
582 pass
629 pass
General Comments 0
You need to be logged in to leave comments. Login now