##// END OF EJS Templates
Fernando Perez -
Show More
@@ -1,564 +1,574 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 225 self.fname = "from a file-like object"
226 226 if title == '':
227 227 self.title = "from a file-like object"
228 228 else:
229 229 self.title = title
230 230 else:
231 231 # Assume it's a string or something that can be converted to one
232 232 self.fname = src
233 233 if title == '':
234 234 (filepath, filename) = os.path.split(src)
235 235 self.title = filename
236 236 else:
237 237 self.title = title
238 238 self.sys_argv = [src] + shlex.split(arg_str)
239 239 self.auto_all = auto_all
240 240 self.src = src
241 241
242 242 # get a few things from ipython. While it's a bit ugly design-wise,
243 243 # it ensures that things like color scheme and the like are always in
244 244 # sync with the ipython mode being used. This class is only meant to
245 245 # be used inside ipython anyways, so it's OK.
246 246 ip = get_ipython() # this is in builtins whenever IPython is running
247 247 self.ip_ns = ip.user_ns
248 248 self.ip_colorize = ip.pycolorize
249 249 self.ip_showtb = ip.showtraceback
250 250 self.ip_runlines = ip.runlines
251 251 self.shell = ip
252 252
253 253 # load user data and initialize data structures
254 254 self.reload()
255 255
256 def reload(self):
257 """Reload source from disk and initialize state."""
256 def fload(self):
257 """Load file object."""
258 258 # read data and parse into blocks
259 259 if hasattr(self, 'fobj') and self.fobj is not None:
260 260 self.fobj.close()
261 261 if hasattr(self.src, "read"):
262 262 # It seems to be a file or a file-like object
263 263 self.fobj = self.src
264 264 else:
265 265 # Assume it's a string or something that can be converted to one
266 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 272 self.src = self.fobj.read()
269 273 src_b = [b.strip() for b in self.re_stop.split(self.src) if b]
270 274 self._silent = [bool(self.re_silent.findall(b)) for b in src_b]
271 275 self._auto = [bool(self.re_auto.findall(b)) for b in src_b]
272 276
273 277 # if auto_all is not given (def. None), we read it from the file
274 278 if self.auto_all is None:
275 279 self.auto_all = bool(self.re_auto_all.findall(src_b[0]))
276 280 else:
277 281 self.auto_all = bool(self.auto_all)
278 282
279 283 # Clean the sources from all markup so it doesn't get displayed when
280 284 # running the demo
281 285 src_blocks = []
282 286 auto_strip = lambda s: self.re_auto.sub('',s)
283 287 for i,b in enumerate(src_b):
284 288 if self._auto[i]:
285 289 src_blocks.append(auto_strip(b))
286 290 else:
287 291 src_blocks.append(b)
288 292 # remove the auto_all marker
289 293 src_blocks[0] = self.re_auto_all.sub('',src_blocks[0])
290 294
291 295 self.nblocks = len(src_blocks)
292 296 self.src_blocks = src_blocks
293 297
294 298 # also build syntax-highlighted source
295 299 self.src_blocks_colored = map(self.ip_colorize,self.src_blocks)
296 300
297 301 # ensure clean namespace and seek offset
298 302 self.reset()
299 303
300 304 def reset(self):
301 305 """Reset the namespace and seek pointer to restart the demo"""
302 306 self.user_ns = {}
303 307 self.finished = False
304 308 self.block_index = 0
305 309
306 310 def _validate_index(self,index):
307 311 if index<0 or index>=self.nblocks:
308 312 raise ValueError('invalid block index %s' % index)
309 313
310 314 def _get_index(self,index):
311 315 """Get the current block index, validating and checking status.
312 316
313 317 Returns None if the demo is finished"""
314 318
315 319 if index is None:
316 320 if self.finished:
317 321 print >>Term.cout, 'Demo finished. Use <demo_name>.reset() if you want to rerun it.'
318 322 return None
319 323 index = self.block_index
320 324 else:
321 325 self._validate_index(index)
322 326 return index
323 327
324 328 def seek(self,index):
325 329 """Move the current seek pointer to the given block.
326 330
327 331 You can use negative indices to seek from the end, with identical
328 332 semantics to those of Python lists."""
329 333 if index<0:
330 334 index = self.nblocks + index
331 335 self._validate_index(index)
332 336 self.block_index = index
333 337 self.finished = False
334 338
335 339 def back(self,num=1):
336 340 """Move the seek pointer back num blocks (default is 1)."""
337 341 self.seek(self.block_index-num)
338 342
339 343 def jump(self,num=1):
340 344 """Jump a given number of blocks relative to the current one.
341 345
342 346 The offset can be positive or negative, defaults to 1."""
343 347 self.seek(self.block_index+num)
344 348
345 349 def again(self):
346 350 """Move the seek pointer back one block and re-execute."""
347 351 self.back(1)
348 352 self()
349 353
350 354 def edit(self,index=None):
351 355 """Edit a block.
352 356
353 357 If no number is given, use the last block executed.
354 358
355 359 This edits the in-memory copy of the demo, it does NOT modify the
356 360 original source file. If you want to do that, simply open the file in
357 361 an editor and use reload() when you make changes to the file. This
358 362 method is meant to let you change a block during a demonstration for
359 363 explanatory purposes, without damaging your original script."""
360 364
361 365 index = self._get_index(index)
362 366 if index is None:
363 367 return
364 368 # decrease the index by one (unless we're at the very beginning), so
365 369 # that the default demo.edit() call opens up the sblock we've last run
366 370 if index>0:
367 371 index -= 1
368 372
369 373 filename = self.shell.mktempfile(self.src_blocks[index])
370 374 self.shell.hooks.editor(filename,1)
371 375 new_block = file_read(filename)
372 376 # update the source and colored block
373 377 self.src_blocks[index] = new_block
374 378 self.src_blocks_colored[index] = self.ip_colorize(new_block)
375 379 self.block_index = index
376 380 # call to run with the newly edited index
377 381 self()
378 382
379 383 def show(self,index=None):
380 384 """Show a single block on screen"""
381 385
382 386 index = self._get_index(index)
383 387 if index is None:
384 388 return
385 389
386 390 print >>Term.cout, self.marquee('<%s> block # %s (%s remaining)' %
387 391 (self.title,index,self.nblocks-index-1))
388 392 print >>Term.cout,(self.src_blocks_colored[index])
389 393 sys.stdout.flush()
390 394
391 395 def show_all(self):
392 396 """Show entire demo on screen, block by block"""
393 397
394 398 fname = self.title
395 399 title = self.title
396 400 nblocks = self.nblocks
397 401 silent = self._silent
398 402 marquee = self.marquee
399 403 for index,block in enumerate(self.src_blocks_colored):
400 404 if silent[index]:
401 405 print >>Term.cout, marquee('<%s> SILENT block # %s (%s remaining)' %
402 406 (title,index,nblocks-index-1))
403 407 else:
404 408 print >>Term.cout, marquee('<%s> block # %s (%s remaining)' %
405 409 (title,index,nblocks-index-1))
406 410 print >>Term.cout, block,
407 411 sys.stdout.flush()
408 412
409 413 def runlines(self,source):
410 414 """Execute a string with one or more lines of code"""
411 415
412 416 exec source in self.user_ns
413 417
414 418 def __call__(self,index=None):
415 419 """run a block of the demo.
416 420
417 421 If index is given, it should be an integer >=1 and <= nblocks. This
418 422 means that the calling convention is one off from typical Python
419 423 lists. The reason for the inconsistency is that the demo always
420 424 prints 'Block n/N, and N is the total, so it would be very odd to use
421 425 zero-indexing here."""
422 426
423 427 index = self._get_index(index)
424 428 if index is None:
425 429 return
426 430 try:
427 431 marquee = self.marquee
428 432 next_block = self.src_blocks[index]
429 433 self.block_index += 1
430 434 if self._silent[index]:
431 435 print >>Term.cout, marquee('Executing silent block # %s (%s remaining)' %
432 436 (index,self.nblocks-index-1))
433 437 else:
434 438 self.pre_cmd()
435 439 self.show(index)
436 440 if self.auto_all or self._auto[index]:
437 441 print >>Term.cout, marquee('output:')
438 442 else:
439 443 print >>Term.cout, marquee('Press <q> to quit, <Enter> to execute...'),
440 444 ans = raw_input().strip()
441 445 if ans:
442 446 print >>Term.cout, marquee('Block NOT executed')
443 447 return
444 448 try:
445 449 save_argv = sys.argv
446 450 sys.argv = self.sys_argv
447 451 self.runlines(next_block)
448 452 self.post_cmd()
449 453 finally:
450 454 sys.argv = save_argv
451 455
452 456 except:
453 457 self.ip_showtb(filename=self.fname)
454 458 else:
455 459 self.ip_ns.update(self.user_ns)
456 460
457 461 if self.block_index == self.nblocks:
458 462 mq1 = self.marquee('END OF DEMO')
459 463 if mq1:
460 464 # avoid spurious print >>Term.cout,s if empty marquees are used
461 465 print >>Term.cout
462 466 print >>Term.cout, mq1
463 467 print >>Term.cout, self.marquee('Use <demo_name>.reset() if you want to rerun it.')
464 468 self.finished = True
465 469
466 470 # These methods are meant to be overridden by subclasses who may wish to
467 471 # customize the behavior of of their demos.
468 472 def marquee(self,txt='',width=78,mark='*'):
469 473 """Return the input string centered in a 'marquee'."""
470 474 return marquee(txt,width,mark)
471 475
472 476 def pre_cmd(self):
473 477 """Method called before executing each block."""
474 478 pass
475 479
476 480 def post_cmd(self):
477 481 """Method called after executing each block."""
478 482 pass
479 483
480 484
481 485 class IPythonDemo(Demo):
482 486 """Class for interactive demos with IPython's input processing applied.
483 487
484 488 This subclasses Demo, but instead of executing each block by the Python
485 489 interpreter (via exec), it actually calls IPython on it, so that any input
486 490 filters which may be in place are applied to the input block.
487 491
488 492 If you have an interactive environment which exposes special input
489 493 processing, you can use this class instead to write demo scripts which
490 494 operate exactly as if you had typed them interactively. The default Demo
491 495 class requires the input to be valid, pure Python code.
492 496 """
493 497
494 498 def runlines(self,source):
495 499 """Execute a string with one or more lines of code"""
496 500
497 501 self.shell.runlines(source)
498 502
499 503 class LineDemo(Demo):
500 504 """Demo where each line is executed as a separate block.
501 505
502 506 The input script should be valid Python code.
503 507
504 508 This class doesn't require any markup at all, and it's meant for simple
505 509 scripts (with no nesting or any kind of indentation) which consist of
506 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 517 def reload(self):
510 518 """Reload source from disk and initialize state."""
511 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 523 nblocks = len(src_b)
514 self.src = os.linesep.join(self.fobj.readlines())
524 self.src = ''.join(lines)
515 525 self._silent = [False]*nblocks
516 526 self._auto = [True]*nblocks
517 527 self.auto_all = True
518 528 self.nblocks = nblocks
519 529 self.src_blocks = src_b
520 530
521 531 # also build syntax-highlighted source
522 532 self.src_blocks_colored = map(self.ip_colorize,self.src_blocks)
523 533
524 534 # ensure clean namespace and seek offset
525 535 self.reset()
526 536
527 537
528 538 class IPythonLineDemo(IPythonDemo,LineDemo):
529 539 """Variant of the LineDemo class whose input is processed by IPython."""
530 540 pass
531 541
532 542
533 543 class ClearMixin(object):
534 544 """Use this mixin to make Demo classes with less visual clutter.
535 545
536 546 Demos using this mixin will clear the screen before every block and use
537 547 blank marquees.
538 548
539 549 Note that in order for the methods defined here to actually override those
540 550 of the classes it's mixed with, it must go /first/ in the inheritance
541 551 tree. For example:
542 552
543 553 class ClearIPDemo(ClearMixin,IPythonDemo): pass
544 554
545 555 will provide an IPythonDemo class with the mixin's features.
546 556 """
547 557
548 558 def marquee(self,txt='',width=78,mark='*'):
549 559 """Blank marquee that returns '' no matter what the input."""
550 560 return ''
551 561
552 562 def pre_cmd(self):
553 563 """Method called before executing each block.
554 564
555 565 This one simply clears the screen."""
556 566 from IPython.utils.terminal import term_clear
557 567 term_clear()
558 568
559 569 class ClearDemo(ClearMixin,Demo):
560 570 pass
561 571
562 572
563 573 class ClearIPDemo(ClearMixin,IPythonDemo):
564 574 pass
General Comments 0
You need to be logged in to leave comments. Login now