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