diff --git a/IPython/Magic.py b/IPython/Magic.py index 8dd0426..cc6834a 100644 --- a/IPython/Magic.py +++ b/IPython/Magic.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """Magic functions for InteractiveShell. -$Id: Magic.py 907 2005-09-24 00:59:56Z fperez $""" +$Id: Magic.py 908 2005-09-26 16:05:48Z fperez $""" #***************************************************************************** # Copyright (C) 2001 Janko Hauser and @@ -69,20 +69,6 @@ def on_off(tag): """Return an ON/OFF string for a 1/0 input. Simple utility function.""" return ['OFF','ON'][tag] -def get_py_filename(name): - """Return a valid python filename in the current directory. - - If the given name is not a file, it adds '.py' and searches again. - Raises IOError with an informative message if the file isn't found.""" - - name = os.path.expanduser(name) - if not os.path.isfile(name) and not name.endswith('.py'): - name += '.py' - if os.path.isfile(name): - return name - else: - raise IOError,'File `%s` not found.' % name - #**************************************************************************** # Utility classes @@ -2459,16 +2445,8 @@ Defaulting color scheme to 'NoColor'""" This magic is similar to the cat utility, but it will assume the file to be Python source and will show it with syntax highlighting. """ - try: - filename = get_py_filename(parameter_s) - except IndexError: - warn('you must provide at least a filename.') - return - fobj=open(filename,'r') - source = fobj.read() - fobj.close() - colorize = Parser().format - colorized_src = colorize(source,'str',self.shell.rc['colors']) - page(colorized_src,screen_lines=self.shell.rc.screen_length) + filename = get_py_filename(parameter_s) + page(self.shell.colorize(file_read(filename)), + screen_lines=self.shell.rc.screen_length) # end Magic diff --git a/IPython/demo.py b/IPython/demo.py index be57f65..af39a85 100644 --- a/IPython/demo.py +++ b/IPython/demo.py @@ -1,6 +1,103 @@ """Module for interactive demos using IPython. -Sorry, but this uses Python 2.3 features, so it won't work in 2.2 environments. +This module implements a single class, Demo, for running Python scripts +interactively in IPython for demonstrations. With very simple markup (a few +tags in comments), you can control points where the script stops executing and +returns control to IPython. + +The file is run in its own empty namespace (though you can pass it a string of +arguments as if in a command line environment, and it will see those as +sys.argv). But at each stop, the global IPython namespace is updated with the +current internal demo namespace, so you can work interactively with the data +accumulated so far. + +By default, each block of code is printed (with syntax highlighting) before +executing it and you have to confirm execution. This is intended to show the +code to an audience first so you can discuss it, and only proceed with +execution once you agree. There are a few tags which allow you to modify this +behavior. + +The supported tags are: + +# --- stop --- + + Defines block boundaries, the points where IPython stops execution of the + file and returns to the interactive prompt. + +# silent + + Make a block execute silently (and hence automatically). Typically used in + cases where you have some boilerplate or initialization code which you need + executed but do not want to be seen in the demo. + +# auto + + Make a block execute automatically, but still being printed. Useful for + simple code which does not warrant discussion, since it avoids the extra + manual confirmation. + +# auto_all + + This tag can _only_ be in the first block, and if given it overrides the + individual auto tags to make the whole demo fully automatic (no block asks + for confirmation). It can also be given at creation time (or the attribute + set later) to override what's in the file. + +While _any_ python file can be run as a Demo instance, if there are no stop +tags the whole file will run in a single block (no different that calling +first %pycat and then %run). The minimal markup to make this useful is to +place a set of stop tags; the other tags are only there to let you fine-tune +the execution. + +This is probably best explained with the simple example file below. You can +copy this into a file named ex_demo.py, and try running it via: + +from IPython.demo import Demo +d = Demo('ex_demo.py') +d() <--- Call the d object (omit the parens if you have autocall on). + +Each time you call the demo object, it runs the next block. The demo object +has a few useful methods for navigation, like again(), jump(), seek() and +back(). It can be reset for a new run via reset() or reloaded from disk (in +case you've edited the source) via reload(). See their docstrings below. + +#################### EXAMPLE DEMO ############################### +'''A simple interactive demo to illustrate the use of IPython's Demo class.''' + +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 --- + +x = 1 +y = 2 + +# --- stop --- + +# the mark below makes this block as silent +# silent + +print 'This is a silent block, which gets executed but not printed.' + +# --- stop --- +# auto +print 'This is an automatic block.' +print 'It is executed without asking for confirmation, but printed.' +z = x+y + +print 'z=',x + +# --- stop --- +# This is just another normal block. +print 'z is now:', z + +print 'bye!' +################### END EXAMPLE DEMO ############################ + +WARNING: this module uses Python 2.3 features, so it won't work in 2.2 +environments. """ #***************************************************************************** # Copyright (C) 2005 Fernando Perez. @@ -15,15 +112,28 @@ import exceptions import re from IPython.PyColorize import Parser -from IPython.genutils import marquee, shlex_split +from IPython.genutils import marquee, shlex_split, file_read + +__all__ = ['Demo','DemoError'] class DemoError(exceptions.Exception): pass +def re_mark(mark): + return re.compile(r'^\s*#\s+\s+%s\s*$' % mark,re.MULTILINE) + class Demo: - def __init__(self,fname,arg_str='',mark_pause='# pause', - mark_silent='# silent',mark_auto='# auto',auto=False): + + re_stop = re_mark('---\s?stop\s?---') + re_silent = re_mark('silent') + re_auto = re_mark('auto') + re_auto_all = re_mark('auto_all') + + def __init__(self,fname,arg_str='',auto_all=None): """Make a new demo object. To run the demo, simply call the object. + See the module docstring for full details and an example (you can use + IPython.Demo? in IPython to see it). + Inputs: - fname = filename. @@ -34,41 +144,24 @@ class Demo: just like sys.argv, so the demo script can see a similar environment. - - mark_pause ('# pause'): marks for pausing (block boundaries). The - marks are turned into regexps which match them as standalone in a - line, with all leading/trailing whitespace ignored. - - - mark_silent('# silent'): mark blocks as silent, which means that - they are executed without printing their content to screen. Silent - blocks are always automatically executed. - - - mark_auto ('# auto'): mark individual blocks as automatically - executed (without asking for confirmation). - - - auto(False): global flag to run all blocks automatically without + - auto_all(None): global flag to run all blocks automatically without confirmation. This attribute overrides the block-level tags and applies to the whole demo. It is an attribute of the object, and can be changed at runtime simply by reassigning it to a boolean value. """ - self.fname = fname - self.sys_argv = [fname] + shlex_split(arg_str) - self.mark_pause = mark_pause - self.mark_silent = mark_silent - self.re_pause = re.compile(r'^\s*%s\s*$' % mark_pause,re.MULTILINE) - self.re_silent = re.compile(r'^\s*%s\s*$' % mark_silent,re.MULTILINE) - self.re_auto = re.compile(r'^\s*%s\s*$' % mark_auto,re.MULTILINE) - self.auto = auto - + self.fname = fname + self.sys_argv = [fname] + shlex_split(arg_str) + self.auto_all = auto_all + # 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_showtb = __IPYTHON__.showtraceback - self.ip_ns = __IPYTHON__.user_ns - self.ip_colors = __IPYTHON__.rc['colors'] - self.colorize = Parser().format + self.ip_showtb = __IPYTHON__.showtraceback + self.ip_ns = __IPYTHON__.user_ns + self.ip_colorize = __IPYTHON__.pycolorize # load user data and initialize data structures self.reload() @@ -76,42 +169,44 @@ class Demo: def reload(self): """Reload source from disk and initialize state.""" # read data and parse into blocks - fobj = file(self.fname,'r') - self.src = fobj.read() - fobj.close() - src_blocks = [b.strip() for b in self.re_pause.split(self.src) if b] - self._silent = [bool(self.re_silent.findall(b)) for b in src_blocks] - self._auto = [bool(self.re_auto.findall(b)) for b in src_blocks] - # strip out the 'auto' markers - src_b = [] + self.src = file_read(self.fname) + src_b = [b.strip() for b in self.re_stop.split(self.src) if b] + self._silent = [bool(self.re_silent.findall(b)) for b in src_b] + self._auto = [bool(self.re_auto.findall(b)) for b in src_b] + + # if auto_all is not given (def. None), we read it from the file + if self.auto_all is None: + self.auto_all = bool(self.re_auto_all.findall(src_b[0])) + else: + self.auto_all = bool(self.auto_all) + + # Clean the sources from all markup so it doesn't get displayed when + # running the demo + src_blocks = [] auto_strip = lambda s: self.re_auto.sub('',s) - for i,b in enumerate(src_blocks): + for i,b in enumerate(src_b): if self._auto[i]: - src_b.append(auto_strip(b)) + src_blocks.append(auto_strip(b)) else: - src_b.append(b) - self.nblocks = len(src_b) - self.src_blocks = src_b - - # try to colorize blocks - col_scheme = self.ip_colors - self.src_blocks_colored = [self.colorize(s_blk,'str',col_scheme) - for s_blk in self.src_blocks] + src_blocks.append(b) + # remove the auto_all marker + src_blocks[0] = self.re_auto_all.sub('',src_blocks[0]) + + self.nblocks = len(src_blocks) + self.src_blocks = src_blocks + + # also build syntax-highlighted source + self.src_blocks_colored = map(self.ip_colorize,self.src_blocks) + # ensure clean namespace and seek offset self.reset() def reset(self): """Reset the namespace and seek pointer to restart the demo""" - self.user_ns = {} - self.finished = False + self.user_ns = {} + self.finished = False self.block_index = 0 - def again(self): - """Repeat the last block""" - self.block_index -= 1 - self.finished = False - self() - def _validate_index(self,index): if index<0 or index>=self.nblocks: raise ValueError('invalid block index %s' % index) @@ -122,6 +217,19 @@ class Demo: self.block_index = index self.finished = False + def back(self,num=1): + """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.""" + self.seek(self.block_index+num) + + def again(self): + """Move the seek pointer back one block and re-execute.""" + self.back(1) + self() + def show(self,index=None): """Show a single block on screen""" if index is None: @@ -173,7 +281,7 @@ class Demo: (index,self.nblocks-index-1)) else: self.show(index) - if self.auto or self._auto[index]: + if self.auto_all or self._auto[index]: print marquee('output') else: print marquee('Press to quit, to execute...'), diff --git a/IPython/genutils.py b/IPython/genutils.py index 73151d4..64be144 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 897 2005-09-22 09:32:46Z fperez $""" +$Id: genutils.py 908 2005-09-26 16:05:48Z fperez $""" #***************************************************************************** # Copyright (C) 2001-2004 Fernando Perez. @@ -439,6 +439,21 @@ def mutex_opts(dict,ex_op): 'Options '+op1+' and '+op2+' are mutually exclusive.' #----------------------------------------------------------------------------- +def get_py_filename(name): + """Return a valid python filename in the current directory. + + If the given name is not a file, it adds '.py' and searches again. + Raises IOError with an informative message if the file isn't found.""" + + name = os.path.expanduser(name) + if not os.path.isfile(name) and not name.endswith('.py'): + name += '.py' + if os.path.isfile(name): + return name + else: + raise IOError,'File `%s` not found.' % name + +#----------------------------------------------------------------------------- def filefind(fname,alt_dirs = None): """Return the given filename either in the current directory, if it exists, or in a specified list of directories. @@ -467,6 +482,14 @@ def filefind(fname,alt_dirs = None): ' not found in current or supplied directories:' + `alt_dirs` #---------------------------------------------------------------------------- +def file_read(filename): + """Read a file and close it. Returns the file source.""" + fobj=open(filename,'r'); + source = fobj.read(); + fobj.close() + return source + +#---------------------------------------------------------------------------- def target_outdated(target,deps): """Determine whether a target is out of date. @@ -937,7 +960,7 @@ def ask_yes_no(prompt,default=None): return answers[ans] #---------------------------------------------------------------------------- -def marquee(txt='',width=80,mark='*'): +def marquee(txt='',width=78,mark='*'): """Return the input string centered in a 'marquee'.""" if not txt: return (mark*width)[:width] diff --git a/IPython/iplib.py b/IPython/iplib.py index 18f6840..411a661 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 894 2005-09-22 07:16:18Z fperez $ +$Id: iplib.py 908 2005-09-26 16:05:48Z fperez $ """ #***************************************************************************** @@ -61,6 +61,7 @@ from IPython.Struct import Struct from IPython.Itpl import Itpl,itpl,printpl,ItplNS,itplns from IPython.FakeModule import FakeModule from IPython.background_jobs import BackgroundJobManager +from IPython.PyColorize import Parser from IPython.genutils import * # Global pointer to the running @@ -661,6 +662,10 @@ class InteractiveShell(code.InteractiveConsole, Logger, Magic): # magic code via this pointer instead of the current mixin salad. Magic.set_shell(self,self) + # Python source parser/formatter for syntax highlighting + pyformat = Parser().format + self.pycolorize = lambda src: pyformat(src,'str',self.rc['colors']) + # hooks holds pointers used for user-side customizations self.hooks = Struct() diff --git a/doc/ChangeLog b/doc/ChangeLog index 44e6353..4d87598 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,3 +1,10 @@ +2005-09-24 Fernando Perez + + * IPython/demo.py: finish demo module, fully documented now. + + * IPython/genutils.py (file_read): simple little utility to read a + file and ensure it's closed afterwards. + 2005-09-23 Fernando Perez * IPython/demo.py (Demo.__init__): added support for individually diff --git a/doc/manual_base.lyx b/doc/manual_base.lyx index 9115569..7eeef74 100644 --- a/doc/manual_base.lyx +++ b/doc/manual_base.lyx @@ -2,15 +2,45 @@ \lyxformat 221 \textclass article \begin_preamble -\usepackage{ae,aecompl} -\usepackage{hyperref} +%\usepackage{ae,aecompl} +\usepackage{color} + +% A few colors to replace the defaults for certain link types +\definecolor{orange}{cmyk}{0,0.4,0.8,0.2} +\definecolor{darkorange}{rgb}{.71,0.21,0.01} +\definecolor{darkred}{rgb}{.52,0.08,0.01} +\definecolor{darkgreen}{rgb}{.12,.54,.11} + +% Use and configure listings package for nicely formatted code +\usepackage{listings} +\lstset{ + language=Python, + basicstyle=\small\ttfamily, + commentstyle=\ttfamily\color{blue}, + stringstyle=\ttfamily\color{darkorange}, + showstringspaces=false, + breaklines=true, + postbreak = \space\dots +} + +\usepackage[%pdftex, % needed for pdflatex + breaklinks=true, % so long urls are correctly broken across lines + colorlinks=true, + urlcolor=blue, + linkcolor=darkred, + citecolor=darkgreen, + ]{hyperref} + \usepackage{html} + +% This helps prevent overly long lines that stretch beyond the margins +\sloppy \end_preamble \language english \inputencoding latin1 -\fontscheme default +\fontscheme palatino \graphics default -\paperfontsize default +\paperfontsize 10 \spacing single \papersize Default \paperpackage a4 @@ -19,9 +49,9 @@ \use_natbib 0 \use_numerical_citations 0 \paperorientation portrait -\leftmargin 1.25in +\leftmargin 1.1in \topmargin 1in -\rightmargin 1.25in +\rightmargin 1.1in \bottommargin 1in \secnumdepth 3 \tocdepth 3 @@ -433,8 +463,8 @@ IPython is generously hosted at \end_inset by the SciPy project. - This site offers downloads, CVS access, mailing lists and a bug tracking - system. + This site offers downloads, subversion access, mailing lists and a bug + tracking system. I am very grateful to Enthought ( \begin_inset LatexCommand \htmlurl{http://www.enthought.com} @@ -2271,6 +2301,19 @@ Use While IPython doesn't support true multiline editing, this command allows you to call an editor on the spot, and IPython will execute the code you type in there as if it were typed interactively. +\layout Itemize + +Use the IPython.demo.Demo class to load any Python script as an interactive + demo. + With a minimal amount of simple markup, you can control the execution of + the script, stopping as needed. + See sec.\SpecialChar ~ + +\begin_inset LatexCommand \ref{sec:interactive-demos} + +\end_inset + + for more. \layout Standard If you have your own favorite tip on using IPython efficiently for a certain @@ -3577,9 +3620,13 @@ example-magic.py \layout Standard -\begin_inset Include \verbatiminput{examples/example-magic.py} -preview false +\begin_inset ERT +status Open + +\layout Standard +\backslash +lstinputlisting{examples/example-magic.py} \end_inset @@ -5323,9 +5370,13 @@ directory contains lots of comments on all of these options. \layout Standard -\begin_inset Include \verbatiminput{../IPython/UserConfig/ipythonrc} -preview false +\begin_inset ERT +status Open +\layout Standard + +\backslash +lstinputlisting{../IPython/UserConfig/ipythonrc} \end_inset @@ -6021,9 +6072,13 @@ example-embed.py \layout Standard -\begin_inset Include \verbatiminput{examples/example-embed.py} -preview false +\begin_inset ERT +status Open +\layout Standard + +\backslash +lstinputlisting{examples/example-embed.py} \end_inset @@ -6034,9 +6089,13 @@ Once you understand how the system functions, you can use the following \layout Standard -\begin_inset Include \verbatiminput{examples/example-embed-short.py} -preview false +\begin_inset ERT +status Open +\layout Standard + +\backslash +lstinputlisting{examples/example-embed-short.py} \end_inset @@ -7648,6 +7707,30 @@ This is obviously a brute force way of avoiding race conditions with the \layout Section +\begin_inset LatexCommand \label{sec:interactive-demos} + +\end_inset + +Interactive demos with IPython +\layout Standard + +IPython ships with +\layout Standard + + +\begin_inset ERT +status Open + +\layout Standard + +\backslash +lstinputlisting{examples/example-demo.py} +\end_inset + + +\layout Section + + \begin_inset LatexCommand \label{sec:matplotlib-support} \end_inset @@ -7960,9 +8043,13 @@ example-gnuplot.py \layout Standard -\begin_inset Include \verbatiminput{examples/example-gnuplot.py} -preview false +\begin_inset ERT +status Open +\layout Standard + +\backslash +lstinputlisting{examples/example-gnuplot.py} \end_inset @@ -8311,7 +8398,39 @@ IPython is mainly developed by Fernando P \family default . For all IPython-related requests, please contact Fernando. - User or development help should be requested via the IPython mailing lists: + +\layout Standard + +As of late 2005, the following developers have joined the core team: +\layout List +\labelwidthstring 00.00.0000 + +Robert\SpecialChar ~ +Kern +\family typewriter + +\family default +: co-mentored the 2005 Google Summer of Code project to develop python interacti +ve notebooks (XML documents) and graphical interface. + This project was awarded to the students Tzanko Matev +\family typewriter + +\family default + and Toni Alatalo +\family typewriter + +\layout List +\labelwidthstring 00.00.0000 + +Brian\SpecialChar ~ +Granger +\family typewriter + +\family default +: extending IPython to allow support for interactive parallel computing. +\layout Standard + +User or development help should be requested via the IPython mailing lists: \layout Description User\SpecialChar ~ @@ -8800,16 +8919,6 @@ Tretkowski \layout List \labelwidthstring 00.00.0000 -Robert\SpecialChar ~ -Kern -\family typewriter - -\family default - help with OSX issues, much help in general with various Python topics, - especially related to scientific computing. -\layout List -\labelwidthstring 00.00.0000 - George\SpecialChar ~ Sakkis < \family typewriter