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