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