##// END OF EJS Templates
- Small fix to demos to flush stdout on each printed block.
fperez -
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,309 +1,311 b''
1 """Module for interactive demos using IPython.
1 """Module for interactive demos using IPython.
2
2
3 This module implements a single class, Demo, for running Python scripts
3 This module implements a single class, Demo, for running Python scripts
4 interactively in IPython for demonstrations. With very simple markup (a few
4 interactively in IPython for demonstrations. With very simple markup (a few
5 tags in comments), you can control points where the script stops executing and
5 tags in comments), you can control points where the script stops executing and
6 returns control to IPython.
6 returns control to IPython.
7
7
8 The file is run in its own empty namespace (though you can pass it a string of
8 The file is run in its own empty namespace (though you can pass it a string of
9 arguments as if in a command line environment, and it will see those as
9 arguments as if in a command line environment, and it will see those as
10 sys.argv). But at each stop, the global IPython namespace is updated with the
10 sys.argv). But at each stop, the global IPython namespace is updated with the
11 current internal demo namespace, so you can work interactively with the data
11 current internal demo namespace, so you can work interactively with the data
12 accumulated so far.
12 accumulated so far.
13
13
14 By default, each block of code is printed (with syntax highlighting) before
14 By default, each block of code is printed (with syntax highlighting) before
15 executing it and you have to confirm execution. This is intended to show the
15 executing it and you have to confirm execution. This is intended to show the
16 code to an audience first so you can discuss it, and only proceed with
16 code to an audience first so you can discuss it, and only proceed with
17 execution once you agree. There are a few tags which allow you to modify this
17 execution once you agree. There are a few tags which allow you to modify this
18 behavior.
18 behavior.
19
19
20 The supported tags are:
20 The supported tags are:
21
21
22 # <demo> --- stop ---
22 # <demo> --- stop ---
23
23
24 Defines block boundaries, the points where IPython stops execution of the
24 Defines block boundaries, the points where IPython stops execution of the
25 file and returns to the interactive prompt.
25 file and returns to the interactive prompt.
26
26
27 # <demo> silent
27 # <demo> silent
28
28
29 Make a block execute silently (and hence automatically). Typically used in
29 Make a block execute silently (and hence automatically). Typically used in
30 cases where you have some boilerplate or initialization code which you need
30 cases where you have some boilerplate or initialization code which you need
31 executed but do not want to be seen in the demo.
31 executed but do not want to be seen in the demo.
32
32
33 # <demo> auto
33 # <demo> auto
34
34
35 Make a block execute automatically, but still being printed. Useful for
35 Make a block execute automatically, but still being printed. Useful for
36 simple code which does not warrant discussion, since it avoids the extra
36 simple code which does not warrant discussion, since it avoids the extra
37 manual confirmation.
37 manual confirmation.
38
38
39 # <demo> auto_all
39 # <demo> auto_all
40
40
41 This tag can _only_ be in the first block, and if given it overrides the
41 This tag can _only_ be in the first block, and if given it overrides the
42 individual auto tags to make the whole demo fully automatic (no block asks
42 individual auto tags to make the whole demo fully automatic (no block asks
43 for confirmation). It can also be given at creation time (or the attribute
43 for confirmation). It can also be given at creation time (or the attribute
44 set later) to override what's in the file.
44 set later) to override what's in the file.
45
45
46 While _any_ python file can be run as a Demo instance, if there are no stop
46 While _any_ python file can be run as a Demo instance, if there are no stop
47 tags the whole file will run in a single block (no different that calling
47 tags the whole file will run in a single block (no different that calling
48 first %pycat and then %run). The minimal markup to make this useful is to
48 first %pycat and then %run). The minimal markup to make this useful is to
49 place a set of stop tags; the other tags are only there to let you fine-tune
49 place a set of stop tags; the other tags are only there to let you fine-tune
50 the execution.
50 the execution.
51
51
52 This is probably best explained with the simple example file below. You can
52 This is probably best explained with the simple example file below. You can
53 copy this into a file named ex_demo.py, and try running it via:
53 copy this into a file named ex_demo.py, and try running it via:
54
54
55 from IPython.demo import Demo
55 from IPython.demo import Demo
56 d = Demo('ex_demo.py')
56 d = Demo('ex_demo.py')
57 d() <--- Call the d object (omit the parens if you have autocall on).
57 d() <--- Call the d object (omit the parens if you have autocall on).
58
58
59 Each time you call the demo object, it runs the next block. The demo object
59 Each time you call the demo object, it runs the next block. The demo object
60 has a few useful methods for navigation, like again(), jump(), seek() and
60 has a few useful methods for navigation, like again(), jump(), seek() and
61 back(). It can be reset for a new run via reset() or reloaded from disk (in
61 back(). It can be reset for a new run via reset() or reloaded from disk (in
62 case you've edited the source) via reload(). See their docstrings below.
62 case you've edited the source) via reload(). See their docstrings below.
63
63
64 #################### EXAMPLE DEMO <ex_demo.py> ###############################
64 #################### EXAMPLE DEMO <ex_demo.py> ###############################
65 '''A simple interactive demo to illustrate the use of IPython's Demo class.'''
65 '''A simple interactive demo to illustrate the use of IPython's Demo class.'''
66
66
67 print 'Hello, welcome to an interactive IPython demo.'
67 print 'Hello, welcome to an interactive IPython demo.'
68
68
69 # The mark below defines a block boundary, which is a point where IPython will
69 # The mark below defines a block boundary, which is a point where IPython will
70 # stop execution and return to the interactive prompt.
70 # stop execution and return to the interactive prompt.
71 # Note that in actual interactive execution,
71 # Note that in actual interactive execution,
72 # <demo> --- stop ---
72 # <demo> --- stop ---
73
73
74 x = 1
74 x = 1
75 y = 2
75 y = 2
76
76
77 # <demo> --- stop ---
77 # <demo> --- stop ---
78
78
79 # the mark below makes this block as silent
79 # the mark below makes this block as silent
80 # <demo> silent
80 # <demo> silent
81
81
82 print 'This is a silent block, which gets executed but not printed.'
82 print 'This is a silent block, which gets executed but not printed.'
83
83
84 # <demo> --- stop ---
84 # <demo> --- stop ---
85 # <demo> auto
85 # <demo> auto
86 print 'This is an automatic block.'
86 print 'This is an automatic block.'
87 print 'It is executed without asking for confirmation, but printed.'
87 print 'It is executed without asking for confirmation, but printed.'
88 z = x+y
88 z = x+y
89
89
90 print 'z=',x
90 print 'z=',x
91
91
92 # <demo> --- stop ---
92 # <demo> --- stop ---
93 # This is just another normal block.
93 # This is just another normal block.
94 print 'z is now:', z
94 print 'z is now:', z
95
95
96 print 'bye!'
96 print 'bye!'
97 ################### END EXAMPLE DEMO <ex_demo.py> ############################
97 ################### END EXAMPLE DEMO <ex_demo.py> ############################
98
98
99 WARNING: this module uses Python 2.3 features, so it won't work in 2.2
99 WARNING: this module uses Python 2.3 features, so it won't work in 2.2
100 environments.
100 environments.
101 """
101 """
102 #*****************************************************************************
102 #*****************************************************************************
103 # Copyright (C) 2005-2006 Fernando Perez. <Fernando.Perez@colorado.edu>
103 # Copyright (C) 2005-2006 Fernando Perez. <Fernando.Perez@colorado.edu>
104 #
104 #
105 # Distributed under the terms of the BSD License. The full license is in
105 # Distributed under the terms of the BSD License. The full license is in
106 # the file COPYING, distributed as part of this software.
106 # the file COPYING, distributed as part of this software.
107 #
107 #
108 #*****************************************************************************
108 #*****************************************************************************
109
109
110 import exceptions
110 import exceptions
111 import re
111 import re
112 import sys
112 import sys
113
113
114 from IPython.PyColorize import Parser
114 from IPython.PyColorize import Parser
115 from IPython.genutils import marquee, shlex_split, file_read
115 from IPython.genutils import marquee, shlex_split, file_read
116
116
117 __all__ = ['Demo','DemoError']
117 __all__ = ['Demo','DemoError']
118
118
119 class DemoError(exceptions.Exception): pass
119 class DemoError(exceptions.Exception): pass
120
120
121 def re_mark(mark):
121 def re_mark(mark):
122 return re.compile(r'^\s*#\s+<demo>\s+%s\s*$' % mark,re.MULTILINE)
122 return re.compile(r'^\s*#\s+<demo>\s+%s\s*$' % mark,re.MULTILINE)
123
123
124 class Demo:
124 class Demo:
125
125
126 re_stop = re_mark('---\s?stop\s?---')
126 re_stop = re_mark('---\s?stop\s?---')
127 re_silent = re_mark('silent')
127 re_silent = re_mark('silent')
128 re_auto = re_mark('auto')
128 re_auto = re_mark('auto')
129 re_auto_all = re_mark('auto_all')
129 re_auto_all = re_mark('auto_all')
130
130
131 def __init__(self,fname,arg_str='',auto_all=None):
131 def __init__(self,fname,arg_str='',auto_all=None):
132 """Make a new demo object. To run the demo, simply call the object.
132 """Make a new demo object. To run the demo, simply call the object.
133
133
134 See the module docstring for full details and an example (you can use
134 See the module docstring for full details and an example (you can use
135 IPython.Demo? in IPython to see it).
135 IPython.Demo? in IPython to see it).
136
136
137 Inputs:
137 Inputs:
138
138
139 - fname = filename.
139 - fname = filename.
140
140
141 Optional inputs:
141 Optional inputs:
142
142
143 - arg_str(''): a string of arguments, internally converted to a list
143 - arg_str(''): a string of arguments, internally converted to a list
144 just like sys.argv, so the demo script can see a similar
144 just like sys.argv, so the demo script can see a similar
145 environment.
145 environment.
146
146
147 - auto_all(None): global flag to run all blocks automatically without
147 - auto_all(None): global flag to run all blocks automatically without
148 confirmation. This attribute overrides the block-level tags and
148 confirmation. This attribute overrides the block-level tags and
149 applies to the whole demo. It is an attribute of the object, and
149 applies to the whole demo. It is an attribute of the object, and
150 can be changed at runtime simply by reassigning it to a boolean
150 can be changed at runtime simply by reassigning it to a boolean
151 value.
151 value.
152 """
152 """
153
153
154 self.fname = fname
154 self.fname = fname
155 self.sys_argv = [fname] + shlex_split(arg_str)
155 self.sys_argv = [fname] + shlex_split(arg_str)
156 self.auto_all = auto_all
156 self.auto_all = auto_all
157
157
158 # get a few things from ipython. While it's a bit ugly design-wise,
158 # get a few things from ipython. While it's a bit ugly design-wise,
159 # it ensures that things like color scheme and the like are always in
159 # it ensures that things like color scheme and the like are always in
160 # sync with the ipython mode being used. This class is only meant to
160 # sync with the ipython mode being used. This class is only meant to
161 # be used inside ipython anyways, so it's OK.
161 # be used inside ipython anyways, so it's OK.
162 self.ip_showtb = __IPYTHON__.showtraceback
162 self.ip_showtb = __IPYTHON__.showtraceback
163 self.ip_ns = __IPYTHON__.user_ns
163 self.ip_ns = __IPYTHON__.user_ns
164 self.ip_colorize = __IPYTHON__.pycolorize
164 self.ip_colorize = __IPYTHON__.pycolorize
165
165
166 # load user data and initialize data structures
166 # load user data and initialize data structures
167 self.reload()
167 self.reload()
168
168
169 def reload(self):
169 def reload(self):
170 """Reload source from disk and initialize state."""
170 """Reload source from disk and initialize state."""
171 # read data and parse into blocks
171 # read data and parse into blocks
172 self.src = file_read(self.fname)
172 self.src = file_read(self.fname)
173 src_b = [b.strip() for b in self.re_stop.split(self.src) if b]
173 src_b = [b.strip() for b in self.re_stop.split(self.src) if b]
174 self._silent = [bool(self.re_silent.findall(b)) for b in src_b]
174 self._silent = [bool(self.re_silent.findall(b)) for b in src_b]
175 self._auto = [bool(self.re_auto.findall(b)) for b in src_b]
175 self._auto = [bool(self.re_auto.findall(b)) for b in src_b]
176
176
177 # if auto_all is not given (def. None), we read it from the file
177 # if auto_all is not given (def. None), we read it from the file
178 if self.auto_all is None:
178 if self.auto_all is None:
179 self.auto_all = bool(self.re_auto_all.findall(src_b[0]))
179 self.auto_all = bool(self.re_auto_all.findall(src_b[0]))
180 else:
180 else:
181 self.auto_all = bool(self.auto_all)
181 self.auto_all = bool(self.auto_all)
182
182
183 # Clean the sources from all markup so it doesn't get displayed when
183 # Clean the sources from all markup so it doesn't get displayed when
184 # running the demo
184 # running the demo
185 src_blocks = []
185 src_blocks = []
186 auto_strip = lambda s: self.re_auto.sub('',s)
186 auto_strip = lambda s: self.re_auto.sub('',s)
187 for i,b in enumerate(src_b):
187 for i,b in enumerate(src_b):
188 if self._auto[i]:
188 if self._auto[i]:
189 src_blocks.append(auto_strip(b))
189 src_blocks.append(auto_strip(b))
190 else:
190 else:
191 src_blocks.append(b)
191 src_blocks.append(b)
192 # remove the auto_all marker
192 # remove the auto_all marker
193 src_blocks[0] = self.re_auto_all.sub('',src_blocks[0])
193 src_blocks[0] = self.re_auto_all.sub('',src_blocks[0])
194
194
195 self.nblocks = len(src_blocks)
195 self.nblocks = len(src_blocks)
196 self.src_blocks = src_blocks
196 self.src_blocks = src_blocks
197
197
198 # also build syntax-highlighted source
198 # also build syntax-highlighted source
199 self.src_blocks_colored = map(self.ip_colorize,self.src_blocks)
199 self.src_blocks_colored = map(self.ip_colorize,self.src_blocks)
200
200
201 # ensure clean namespace and seek offset
201 # ensure clean namespace and seek offset
202 self.reset()
202 self.reset()
203
203
204 def reset(self):
204 def reset(self):
205 """Reset the namespace and seek pointer to restart the demo"""
205 """Reset the namespace and seek pointer to restart the demo"""
206 self.user_ns = {}
206 self.user_ns = {}
207 self.finished = False
207 self.finished = False
208 self.block_index = 0
208 self.block_index = 0
209
209
210 def _validate_index(self,index):
210 def _validate_index(self,index):
211 if index<0 or index>=self.nblocks:
211 if index<0 or index>=self.nblocks:
212 raise ValueError('invalid block index %s' % index)
212 raise ValueError('invalid block index %s' % index)
213
213
214 def seek(self,index):
214 def seek(self,index):
215 """Move the current seek pointer to the given block"""
215 """Move the current seek pointer to the given block"""
216 self._validate_index(index)
216 self._validate_index(index)
217 self.block_index = index
217 self.block_index = index
218 self.finished = False
218 self.finished = False
219
219
220 def back(self,num=1):
220 def back(self,num=1):
221 """Move the seek pointer back num blocks (default is 1)."""
221 """Move the seek pointer back num blocks (default is 1)."""
222 self.seek(self.block_index-num)
222 self.seek(self.block_index-num)
223
223
224 def jump(self,num):
224 def jump(self,num):
225 """Jump a given number of blocks relative to the current one."""
225 """Jump a given number of blocks relative to the current one."""
226 self.seek(self.block_index+num)
226 self.seek(self.block_index+num)
227
227
228 def again(self):
228 def again(self):
229 """Move the seek pointer back one block and re-execute."""
229 """Move the seek pointer back one block and re-execute."""
230 self.back(1)
230 self.back(1)
231 self()
231 self()
232
232
233 def show(self,index=None):
233 def show(self,index=None):
234 """Show a single block on screen"""
234 """Show a single block on screen"""
235 if index is None:
235 if index is None:
236 if self.finished:
236 if self.finished:
237 print 'Demo finished. Use reset() if you want to rerun it.'
237 print 'Demo finished. Use reset() if you want to rerun it.'
238 return
238 return
239 index = self.block_index
239 index = self.block_index
240 else:
240 else:
241 self._validate_index(index)
241 self._validate_index(index)
242 print marquee('<%s> block # %s (%s remaining)' %
242 print marquee('<%s> block # %s (%s remaining)' %
243 (self.fname,index,self.nblocks-index-1))
243 (self.fname,index,self.nblocks-index-1))
244 print self.src_blocks_colored[index],
244 print self.src_blocks_colored[index],
245 sys.stdout.flush()
245
246
246 def show_all(self):
247 def show_all(self):
247 """Show entire demo on screen, block by block"""
248 """Show entire demo on screen, block by block"""
248
249
249 fname = self.fname
250 fname = self.fname
250 nblocks = self.nblocks
251 nblocks = self.nblocks
251 silent = self._silent
252 silent = self._silent
252 for index,block in enumerate(self.src_blocks_colored):
253 for index,block in enumerate(self.src_blocks_colored):
253 if silent[index]:
254 if silent[index]:
254 print marquee('<%s> SILENT block # %s (%s remaining)' %
255 print marquee('<%s> SILENT block # %s (%s remaining)' %
255 (fname,index,nblocks-index-1))
256 (fname,index,nblocks-index-1))
256 else:
257 else:
257 print marquee('<%s> block # %s (%s remaining)' %
258 print marquee('<%s> block # %s (%s remaining)' %
258 (fname,index,nblocks-index-1))
259 (fname,index,nblocks-index-1))
259 print block,
260 print block,
261 sys.stdout.flush()
260
262
261 def __call__(self,index=None):
263 def __call__(self,index=None):
262 """run a block of the demo.
264 """run a block of the demo.
263
265
264 If index is given, it should be an integer >=1 and <= nblocks. This
266 If index is given, it should be an integer >=1 and <= nblocks. This
265 means that the calling convention is one off from typical Python
267 means that the calling convention is one off from typical Python
266 lists. The reason for the inconsistency is that the demo always
268 lists. The reason for the inconsistency is that the demo always
267 prints 'Block n/N, and N is the total, so it would be very odd to use
269 prints 'Block n/N, and N is the total, so it would be very odd to use
268 zero-indexing here."""
270 zero-indexing here."""
269
271
270 if index is None and self.finished:
272 if index is None and self.finished:
271 print 'Demo finished. Use reset() if you want to rerun it.'
273 print 'Demo finished. Use reset() if you want to rerun it.'
272 return
274 return
273 if index is None:
275 if index is None:
274 index = self.block_index
276 index = self.block_index
275 self._validate_index(index)
277 self._validate_index(index)
276 try:
278 try:
277 next_block = self.src_blocks[index]
279 next_block = self.src_blocks[index]
278 self.block_index += 1
280 self.block_index += 1
279 if self._silent[index]:
281 if self._silent[index]:
280 print marquee('Executing silent block # %s (%s remaining)' %
282 print marquee('Executing silent block # %s (%s remaining)' %
281 (index,self.nblocks-index-1))
283 (index,self.nblocks-index-1))
282 else:
284 else:
283 self.show(index)
285 self.show(index)
284 if self.auto_all or self._auto[index]:
286 if self.auto_all or self._auto[index]:
285 print marquee('output')
287 print marquee('output')
286 else:
288 else:
287 print marquee('Press <q> to quit, <Enter> to execute...'),
289 print marquee('Press <q> to quit, <Enter> to execute...'),
288 ans = raw_input().strip()
290 ans = raw_input().strip()
289 if ans:
291 if ans:
290 print marquee('Block NOT executed')
292 print marquee('Block NOT executed')
291 return
293 return
292 try:
294 try:
293 save_argv = sys.argv
295 save_argv = sys.argv
294 sys.argv = self.sys_argv
296 sys.argv = self.sys_argv
295 exec next_block in self.user_ns
297 exec next_block in self.user_ns
296 finally:
298 finally:
297 sys.argv = save_argv
299 sys.argv = save_argv
298
300
299 except:
301 except:
300 self.ip_showtb(filename=self.fname)
302 self.ip_showtb(filename=self.fname)
301 else:
303 else:
302 self.ip_ns.update(self.user_ns)
304 self.ip_ns.update(self.user_ns)
303
305
304 if self.block_index == self.nblocks:
306 if self.block_index == self.nblocks:
305 print
307 print
306 print marquee(' END OF DEMO ')
308 print marquee(' END OF DEMO ')
307 print marquee('Use reset() if you want to rerun it.')
309 print marquee('Use reset() if you want to rerun it.')
308 self.finished = True
310 self.finished = True
309
311
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now