diff --git a/IPython/demo.py b/IPython/demo.py new file mode 100644 index 0000000..e4569da --- /dev/null +++ b/IPython/demo.py @@ -0,0 +1,115 @@ +"""Module for interactive demos using IPython. +""" +#***************************************************************************** +# Copyright (C) 2005 Fernando Perez. +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +# +#***************************************************************************** + +import exceptions + +from IPython.PyColorize import Parser +from IPython.genutils import marquee + +class DemoError(exceptions.Exception): pass + +class Demo: + def __init__(self,fname,pause_mark='# pause',auto=False): + + self.fname = fname + self.pause_mark = pause_mark + self.auto = auto + + # get a few things from ipython. While it's a bit ugly design-wise, + # it ensures that things like color scheme and the like are always in + # sync with the ipython mode being used. This class is only meant to + # be used inside ipython anyways, so it's OK. + self.ip_showtraceback = __IPYTHON__.showtraceback + self.ip_ns = __IPYTHON__.user_ns + self.ip_colors = __IPYTHON__.rc['colors'] + + # read data and parse into blocks + fobj = file(fname,'r') + self.src = fobj.read() + fobj.close() + self.src_blocks = [b.strip() for b in self.src.split(pause_mark) if b] + self.nblocks = len(self.src_blocks) + + # try to colorize blocks + colorize = Parser().format + col_scheme = self.ip_colors + self.src_blocks_colored = [colorize(s_blk,'str',col_scheme) + for s_blk in self.src_blocks] + + # finish initialization + self.reset() + + def reset(self): + self.user_ns = {} + self.finished = False + self.block_index = 0 + + def again(self): + self.block_index -= 1 + self() + + + def _validate_index(self,index): + if index<0 or index>=self.nblocks: + raise ValueError('invalid block index %s' % index) + + def seek(self,index): + self._validate_index(index) + self.block_index = index-1 + self.finished = False + + def show(self,index=None): + if index is None: + index = self.block_index + else: + self._validate_index(index) + print marquee('<%s> block # %s (%s/%s)' % + (self.fname,index,index+1,self.nblocks)) + print self.src_blocks_colored[index], + + def __call__(self,index=None): + """run a block of the demo. + + If index is given, it should be an integer >=1 and <= nblocks. This + means that the calling convention is one off from typical Python + lists. The reason for the inconsistency is that the demo always + prints 'Block n/N, and N is the total, so it would be very odd to use + zero-indexing here.""" + + if index is None and self.finished: + print 'Demo finished. Use reset() if you want to rerun it.' + return + if index is None: + index = self.block_index + self._validate_index(index) + try: + next_block = self.src_blocks[index] + self.block_index += 1 + self.show(index) + if not self.auto: + print marquee('Press to quit, to execute...'), + ans = raw_input().strip() + if ans: + print marquee('Block NOT executed') + return + + exec next_block in self.user_ns + + except: + self.ip_showtraceback(filename=self.fname) + else: + self.ip_ns.update(self.user_ns) + + if self.block_index == self.nblocks: + print + print marquee(' END OF DEMO ') + print marquee('Use reset() if you want to rerun it.') + self.finished = True + diff --git a/IPython/genutils.py b/IPython/genutils.py index a66ed39..15a4c4f 100644 --- a/IPython/genutils.py +++ b/IPython/genutils.py @@ -5,7 +5,7 @@ General purpose utilities. This is a grab-bag of stuff I find useful in most programs I write. Some of these things are also convenient when working at the command line. -$Id: genutils.py 874 2005-09-20 20:13:04Z fperez $""" +$Id: genutils.py 894 2005-09-22 07:16:18Z fperez $""" #***************************************************************************** # Copyright (C) 2001-2004 Fernando Perez. @@ -899,6 +899,16 @@ def ask_yes_no(prompt,default=None): return answers[ans] #---------------------------------------------------------------------------- +def marquee(txt='',width=80,mark='*'): + """Return the input string centered in a 'marquee'.""" + if not txt: + return (mark*width)[:width] + nmark = (width-len(txt)-2)/len(mark)/2 + if nmark < 0: nmark =0 + marks = mark*nmark + return '%s %s %s' % (marks,txt,marks) + +#---------------------------------------------------------------------------- class EvalDict: """ Emulate a dict which evaluates its contents in the caller's frame. diff --git a/IPython/iplib.py b/IPython/iplib.py index d378340..18f6840 100644 --- a/IPython/iplib.py +++ b/IPython/iplib.py @@ -6,7 +6,7 @@ Requires Python 2.1 or newer. This file contains all the classes and helper functions specific to IPython. -$Id: iplib.py 874 2005-09-20 20:13:04Z fperez $ +$Id: iplib.py 894 2005-09-22 07:16:18Z fperez $ """ #***************************************************************************** @@ -1272,7 +1272,7 @@ want to merge them back into the new files.""" % locals() return pdb.pm() - def showtraceback(self,exc_tuple = None): + def showtraceback(self,exc_tuple = None,filename=None): """Display the exception that just occurred.""" # Though this won't be called by syntax errors in the input line, @@ -1282,7 +1282,7 @@ want to merge them back into the new files.""" % locals() else: type, value, tb = exc_tuple if type is SyntaxError: - self.showsyntaxerror() + self.showsyntaxerror(filename) else: sys.last_type = type sys.last_value = value diff --git a/doc/ChangeLog b/doc/ChangeLog index 21d9ee8..a08aac6 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,9 +1,20 @@ +2005-09-22 Fernando Perez + + * IPython/genutils.py (marquee): little utility used by the demo + code, handy in general. + + * IPython/demo.py (Demo.__init__): new class for interactive + demos. Not documented yet, I just wrote it in a hurry for + scipy'05. Will docstring later. + 2005-09-20 Fernando Perez * IPython/Shell.py (sigint_handler): Drastic simplification which also seems to make Ctrl-C work correctly across threads! This is so simple, that I can't beleive I'd missed it before. Needs more testing, though. + (KBINT): Never mind, revert changes. I'm sure I'd tried something + like this before... * IPython/genutils.py (get_home_dir): add protection against non-dirs in win32 registry.