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