diff --git a/IPython/Extensions/InterpreterPasteInput.py b/IPython/Extensions/InterpreterPasteInput.py index 4c2f77d..c984d36 100644 --- a/IPython/Extensions/InterpreterPasteInput.py +++ b/IPython/Extensions/InterpreterPasteInput.py @@ -31,7 +31,25 @@ The >>> and ... are stripped from the input so that the python interpreter only sees the real part of the code. All other input is processed normally. + +Notes +===== + +* You can even paste code that has extra initial spaces, such as is common in +doctests: + +In [3]: >>> a = ['Mary', 'had', 'a', 'little', 'lamb'] + +In [4]: >>> for i in range(len(a)): + ...: ... print i, a[i] + ...: ... +0 Mary +1 had +2 a +3 little +4 lamb """ + #***************************************************************************** # Copyright (C) 2001-2006 Fernando Perez # @@ -60,23 +78,32 @@ __license__ = Release.license # The prototype of any alternate prefilter must be like this one (the name # doesn't matter): # - line is a string containing the user input line. -# - continuation is a parameter which tells us if we are processing a first line of -# user input or the second or higher of a multi-line statement. +# - continuation is a parameter which tells us if we are processing a first +# line of user input or the second or higher of a multi-line statement. + +import re + +PROMPT_RE = re.compile(r'(^[ \t]*>>> |^[ \t]*\.\.\. )') def prefilter_paste(self,line,continuation): """Alternate prefilter for input of pasted code from an interpreter. """ - - from re import match - - if match(r'^>>> |^\.\.\. ',line): + if not line: + return '' + m = PROMPT_RE.match(line) + if m: # In the end, always call the default IPython _prefilter() function. # Note that self must be passed explicitly, b/c we're calling the # unbound class method (since this method will overwrite the instance # prefilter()) - return self._prefilter(line[4:],continuation) + return self._prefilter(line[len(m.group(0)):],continuation) elif line.strip() == '...': return self._prefilter('',continuation) + elif line.isspace(): + # This allows us to recognize multiple input prompts separated by blank + # lines and pasted in a single chunk, very common when pasting doctests + # or long tutorial passages. + return '' else: return self._prefilter(line,continuation) diff --git a/IPython/Extensions/ipy_profile_doctest.py b/IPython/Extensions/ipy_profile_doctest.py new file mode 100644 index 0000000..0e160b1 --- /dev/null +++ b/IPython/Extensions/ipy_profile_doctest.py @@ -0,0 +1,40 @@ +"""Config file for 'doctest' profile. + +This profile modifies the prompts to be the standard Python ones, so that you +can generate easily doctests from an IPython session. + +But more importantly, it enables pasting of code with '>>>' prompts and +arbitrary initial whitespace, as is typical of doctests in reST files and +docstrings. This allows you to easily re-run existing doctests and iteratively +work on them as part of your development workflow. + +The exception mode is also set to 'plain' so the generated exceptions are as +similar as possible to the default Python ones, for inclusion in doctests.""" + +# get various stuff that are there for historical / familiarity reasons +import ipy_legacy + +from IPython import ipapi + +from IPython.Extensions import InterpreterPasteInput + +def main(): + ip = ipapi.get() + o = ip.options + + # Set the prompts similar to the defaults + o.prompt_in1 = '>>> ' + o.prompt_in2 = '... ' + o.prompt_out = '' + + # No separation between successive inputs + o.separate_in = '' + + # Disable pprint, so that outputs are printed as similarly to standard + # python as possible + o.pprint = 0 + + # Use plain exceptions, to also resemble normal pyhton. + o.xmode = 'plain' + +main() diff --git a/IPython/iplib.py b/IPython/iplib.py index 6824ab3..0cd52a4 100644 --- a/IPython/iplib.py +++ b/IPython/iplib.py @@ -6,7 +6,7 @@ Requires Python 2.3 or newer. This file contains all the classes and helper functions specific to IPython. -$Id: iplib.py 2577 2007-08-02 23:50:02Z fperez $ +$Id: iplib.py 2581 2007-08-04 20:52:05Z fperez $ """ #***************************************************************************** @@ -2059,6 +2059,17 @@ want to merge them back into the new files.""" % locals() #print '***line: <%s>' % line # dbg + if not line: + # Return immediately on purely empty lines, so that if the user + # previously typed some whitespace that started a continuation + # prompt, he can break out of that loop with just an empty line. + # This is how the default python prompt works. + + # Only return if the accumulated input buffer was just whitespace! + if ''.join(self.buffer).isspace(): + self.buffer[:] = [] + return '' + line_info = prefilter.LineInfo(line, continue_prompt) # the input history needs to track even empty lines diff --git a/doc/ChangeLog b/doc/ChangeLog index ae45862..2cfd201 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,3 +1,24 @@ +2007-08-04 Fernando Perez + + * IPython/Extensions/ipy_profile_doctest.py: New profile for + doctest support. It sets prompts/exceptions as similar to + standard Python as possible, so that ipython sessions in this + profile can be easily pasted as doctests with minimal + modifications. It also enables pasting of doctests from external + sources (even if they have leading whitespace), so that you can + rerun doctests from existing sources. + + * IPython/iplib.py (_prefilter): fix a buglet where after entering + some whitespace, the prompt would become a continuation prompt + with no way of exiting it other than Ctrl-C. This fix brings us + into conformity with how the default python prompt works. + + * IPython/Extensions/InterpreterPasteInput.py (prefilter_paste): + Add support for pasting not only lines that start with '>>>', but + also with ' >>>'. That is, arbitrary whitespace can now precede + the prompts. This makes the system useful for pasting doctests + from docstrings back into a normal session. + 2007-08-02 Fernando Perez * IPython/Shell.py (IPShellEmbed.__call__): fix bug introduced in