From 6a60d4dfb4a68f6b9fcdf2e69c3ccf072b774a33 2007-02-20 10:25:51 From: fperez Date: 2007-02-20 10:25:51 Subject: [PATCH] - Improvements to demo classes and some magic fixes. --- diff --git a/IPython/Magic.py b/IPython/Magic.py index 91141bb..b725149 100644 --- a/IPython/Magic.py +++ b/IPython/Magic.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """Magic functions for InteractiveShell. -$Id: Magic.py 2066 2007-01-31 18:56:06Z fperez $""" +$Id: Magic.py 2104 2007-02-20 10:25:51Z fperez $""" #***************************************************************************** # Copyright (C) 2001 Janko Hauser and @@ -454,15 +454,28 @@ Currently the magic system has the following functions:\n""" def magic_automagic(self, parameter_s = ''): """Make magic functions callable without having to type the initial %. - Toggles on/off (when off, you must call it as %automagic, of - course). Note that magic functions have lowest priority, so if there's - a variable whose name collides with that of a magic fn, automagic - won't work for that function (you get the variable instead). However, - if you delete the variable (del var), the previously shadowed magic - function becomes visible to automagic again.""" + Without argumentsl toggles on/off (when off, you must call it as + %automagic, of course). With arguments it sets the value, and you can + use any of (case insensitive): + + - on,1,True: to activate + + - off,0,False: to deactivate. + + Note that magic functions have lowest priority, so if there's a + variable whose name collides with that of a magic fn, automagic won't + work for that function (you get the variable instead). However, if you + delete the variable (del var), the previously shadowed magic function + becomes visible to automagic again.""" rc = self.shell.rc - rc.automagic = not rc.automagic + arg = parameter_s.lower() + if parameter_s in ('on','1','true'): + rc.automagic = True + elif parameter_s in ('off','0','false'): + rc.automagic = False + else: + rc.automagic = not rc.automagic print '\n' + Magic.auto_status[rc.automagic] def magic_autocall(self, parameter_s = ''): diff --git a/IPython/demo.py b/IPython/demo.py index 3055211..0734868 100644 --- a/IPython/demo.py +++ b/IPython/demo.py @@ -23,6 +23,13 @@ The classes are (see their docstrings for further details): - IPythonLineDemo: IPython version of the LineDemo class (the demo is executed a line at a time, but processed via IPython). + - ClearMixin: mixin to make Demo classes with less visual clutter. It + declares an empty marquee and a pre_cmd that clears the screen before each + block (see Subclassing below). + + - ClearDemo, ClearIPDemo: mixin-enabled versions of the Demo and IPythonDemo + classes. + Subclassing =========== @@ -35,7 +42,7 @@ subclassing more convenient. Their docstrings below have some more details: - pre_cmd(): run right before the execution of each block. - - pre_cmd(): run right after the execution of each block. If the block + - post_cmd(): run right after the execution of each block. If the block raises an exception, this is NOT called. @@ -56,11 +63,17 @@ behavior. The supported tags are: -# --- stop --- +# stop Defines block boundaries, the points where IPython stops execution of the file and returns to the interactive prompt. + You can optionally mark the stop tag with extra dashes before and after the + word 'stop', to help visually distinguish the blocks in a text editor: + + # --- stop --- + + # silent Make a block execute silently (and hence automatically). Typically used in @@ -110,21 +123,22 @@ The following is a very simple example of a valid demo file. print 'Hello, welcome to an interactive IPython demo.' # The mark below defines a block boundary, which is a point where IPython will -# stop execution and return to the interactive prompt. -# Note that in actual interactive execution, -# --- stop --- +# stop execution and return to the interactive prompt. The dashes are actually +# optional and used only as a visual aid to clearly separate blocks while +editing the demo code. +# stop x = 1 y = 2 -# --- stop --- +# stop # the mark below makes this block as silent # silent print 'This is a silent block, which gets executed but not printed.' -# --- stop --- +# stop # auto print 'This is an automatic block.' print 'It is executed without asking for confirmation, but printed.' @@ -132,7 +146,7 @@ z = x+y print 'z=',x -# --- stop --- +# stop # This is just another normal block. print 'z is now:', z @@ -164,9 +178,9 @@ class DemoError(exceptions.Exception): pass def re_mark(mark): return re.compile(r'^\s*#\s+\s+%s\s*$' % mark,re.MULTILINE) -class Demo: +class Demo(object): - re_stop = re_mark('---\s?stop\s?---') + re_stop = re_mark('-?\s?stop\s?-?') re_silent = re_mark('silent') re_auto = re_mark('auto') re_auto_all = re_mark('auto_all') @@ -271,7 +285,12 @@ class Demo: return index def seek(self,index): - """Move the current seek pointer to the given block""" + """Move the current seek pointer to the given block. + + You can use negative indices to seek from the end, with identical + semantics to those of Python lists.""" + if index<0: + index = self.nblocks + index self._validate_index(index) self.block_index = index self.finished = False @@ -280,8 +299,10 @@ class Demo: """Move the seek pointer back num blocks (default is 1).""" self.seek(self.block_index-num) - def jump(self,num): - """Jump a given number of blocks relative to the current one.""" + def jump(self,num=1): + """Jump a given number of blocks relative to the current one. + + The offset can be positive or negative, defaults to 1.""" self.seek(self.block_index+num) def again(self): @@ -327,7 +348,7 @@ class Demo: print self.marquee('<%s> block # %s (%s remaining)' % (self.fname,index,self.nblocks-index-1)) - print self.src_blocks_colored[index], + sys.stdout.write(self.src_blocks_colored[index]) sys.stdout.flush() def show_all(self): @@ -375,7 +396,7 @@ class Demo: self.pre_cmd() self.show(index) if self.auto_all or self._auto[index]: - print marquee('output') + print marquee('output:') else: print marquee('Press to quit, to execute...'), ans = raw_input().strip() @@ -396,9 +417,12 @@ class Demo: self.ip_ns.update(self.user_ns) if self.block_index == self.nblocks: - print - print self.marquee(' END OF DEMO ') - print self.marquee('Use reset() if you want to rerun it.') + mq1 = self.marquee('END OF DEMO') + if mq1: + # avoid spurious prints if empty marquees are used + print + print mq1 + print self.marquee('Use reset() if you want to rerun it.') self.finished = True # These methods are meant to be overridden by subclasses who may wish to @@ -462,6 +486,41 @@ class LineDemo(Demo): # ensure clean namespace and seek offset self.reset() + class IPythonLineDemo(IPythonDemo,LineDemo): """Variant of the LineDemo class whose input is processed by IPython.""" pass + + +class ClearMixin(object): + """Use this mixin to make Demo classes with less visual clutter. + + Demos using this mixin will clear the screen before every block and use + blank marquees. + + Note that in order for the methods defined here to actually override those + of the classes it's mixed with, it must go /first/ in the inheritance + tree. For example: + + class ClearIPDemo(ClearMixin,IPythonDemo): pass + + will provide an IPythonDemo class with the mixin's features. + """ + + def marquee(self,txt='',width=78,mark='*'): + """Blank marquee that returns '' no matter what the input.""" + return '' + + def pre_cmd(self): + """Method called before executing each block. + + This one simply clears the screen.""" + os.system('clear') + + +class ClearDemo(ClearMixin,Demo): + pass + + +class ClearIPDemo(ClearMixin,IPythonDemo): + pass diff --git a/doc/ChangeLog b/doc/ChangeLog index 0faa5dc..634b561 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,3 +1,11 @@ +2007-02-19 Fernando Perez + + * IPython/demo.py (Demo.re_stop): make dashes optional in demo + stop marks. + (ClearingMixin): a simple mixin to easily make a Demo class clear + the screen in between blocks and have empty marquees. The + ClearDemo and ClearIPDemo classes that use it are included. + 2007-02-18 Fernando Perez * IPython/irunner.py (pexpect_monkeypatch): patch pexpect to