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