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