diff --git a/IPython/lib/irunner.py b/IPython/lib/irunner.py deleted file mode 100755 index bd352c4..0000000 --- a/IPython/lib/irunner.py +++ /dev/null @@ -1,455 +0,0 @@ -#!/usr/bin/env python -"""Module for interactively running scripts. - -This module implements classes for interactively running scripts written for -any system with a prompt which can be matched by a regexp suitable for -pexpect. It can be used to run as if they had been typed up interactively, an -arbitrary series of commands for the target system. - -The module includes classes ready for IPython (with the default prompts), -plain Python and SAGE, but making a new one is trivial. To see how to use it, -simply run the module as a script: - -./irunner.py --help - - -This is an extension of Ken Schutte 's script -contributed on the ipython-user list: - -http://mail.scipy.org/pipermail/ipython-user/2006-May/003539.html - -Notes ------ - - - This module requires pexpect, available in most linux distros, or which can - be downloaded from http://pexpect.sourceforge.net - - - Because pexpect only works under Unix or Windows-Cygwin, this has the same - limitations. This means that it will NOT work under native windows Python. -""" -from __future__ import print_function - -# Stdlib imports -import optparse -import os -import sys - -# Third-party modules: we carry a copy of pexpect to reduce the need for -# external dependencies, but our import checks for a system version first. -from IPython.external import pexpect -from IPython.utils import py3compat - -# We want to use native strings on both versions of Python, and with two -# different versions of pexpect. -if py3compat.PY3: - try: - spawn = pexpect.spawnu # Pexpect 3.0 + - except AttributeError: - spawn = pexpect.spawn # pexpect-u fork -else: - spawn = pexpect.spawn - -# Global usage strings, to avoid indentation issues when typing it below. -USAGE = """ -Interactive script runner, type: %s - -runner [opts] script_name -""" - -def pexpect_monkeypatch(): - """Patch pexpect to prevent unhandled exceptions at VM teardown. - - Calling this function will monkeypatch the pexpect.spawn class and modify - its __del__ method to make it more robust in the face of failures that can - occur if it is called when the Python VM is shutting down. - - Since Python may fire __del__ methods arbitrarily late, it's possible for - them to execute during the teardown of the Python VM itself. At this - point, various builtin modules have been reset to None. Thus, the call to - self.close() will trigger an exception because it tries to call os.close(), - and os is now None. - """ - - if pexpect.__version__[:3] >= '2.2': - # No need to patch, fix is already the upstream version. - return - - def __del__(self): - """This makes sure that no system resources are left open. - Python only garbage collects Python objects. OS file descriptors - are not Python objects, so they must be handled explicitly. - If the child file descriptor was opened outside of this class - (passed to the constructor) then this does not close it. - """ - if not self.closed: - try: - self.close() - except AttributeError: - pass - - pexpect.spawn.__del__ = __del__ - -pexpect_monkeypatch() - -# The generic runner class -class InteractiveRunner(object): - """Class to run a sequence of commands through an interactive program.""" - - def __init__(self,program,prompts,args=None,out=sys.stdout,echo=True): - """Construct a runner. - - Inputs: - - - program: command to execute the given program. - - - prompts: a list of patterns to match as valid prompts, in the - format used by pexpect. This basically means that it can be either - a string (to be compiled as a regular expression) or a list of such - (it must be a true list, as pexpect does type checks). - - If more than one prompt is given, the first is treated as the main - program prompt and the others as 'continuation' prompts, like - python's. This means that blank lines in the input source are - ommitted when the first prompt is matched, but are NOT ommitted when - the continuation one matches, since this is how python signals the - end of multiline input interactively. - - Optional inputs: - - - args(None): optional list of strings to pass as arguments to the - child program. - - - out(sys.stdout): if given, an output stream to be used when writing - output. The only requirement is that it must have a .write() method. - - Public members not parameterized in the constructor: - - - delaybeforesend(0): Newer versions of pexpect have a delay before - sending each new input. For our purposes here, it's typically best - to just set this to zero, but if you encounter reliability problems - or want an interactive run to pause briefly at each prompt, just - increase this value (it is measured in seconds). Note that this - variable is not honored at all by older versions of pexpect. - """ - - self.program = program - self.prompts = prompts - if args is None: args = [] - self.args = args - self.out = out - self.echo = echo - # Other public members which we don't make as parameters, but which - # users may occasionally want to tweak - self.delaybeforesend = 0 - - # Create child process and hold on to it so we don't have to re-create - # for every single execution call - c = self.child = spawn(self.program,self.args,timeout=None) - c.delaybeforesend = self.delaybeforesend - # pexpect hard-codes the terminal size as (24,80) (rows,columns). - # This causes problems because any line longer than 80 characters gets - # completely overwrapped on the printed outptut (even though - # internally the code runs fine). We reset this to 99 rows X 200 - # columns (arbitrarily chosen), which should avoid problems in all - # reasonable cases. - c.setwinsize(99,200) - - def close(self): - """close child process""" - - self.child.close() - - def run_file(self,fname,interact=False,get_output=False): - """Run the given file interactively. - - Inputs: - - - fname: name of the file to execute. - - See the run_source docstring for the meaning of the optional - arguments.""" - - fobj = open(fname,'r') - try: - out = self.run_source(fobj,interact,get_output) - finally: - fobj.close() - if get_output: - return out - - def run_source(self,source,interact=False,get_output=False): - """Run the given source code interactively. - - Inputs: - - - source: a string of code to be executed, or an open file object we - can iterate over. - - Optional inputs: - - - interact(False): if true, start to interact with the running - program at the end of the script. Otherwise, just exit. - - - get_output(False): if true, capture the output of the child process - (filtering the input commands out) and return it as a string. - - Returns: - A string containing the process output, but only if requested. - """ - - # if the source is a string, chop it up in lines so we can iterate - # over it just as if it were an open file. - if isinstance(source, py3compat.string_types): - source = source.splitlines(True) - - if self.echo: - # normalize all strings we write to use the native OS line - # separators. - linesep = os.linesep - stdwrite = self.out.write - write = lambda s: stdwrite(s.replace('\r\n',linesep)) - else: - # Quiet mode, all writes are no-ops - write = lambda s: None - - c = self.child - prompts = c.compile_pattern_list(self.prompts) - prompt_idx = c.expect_list(prompts) - - # Flag whether the script ends normally or not, to know whether we can - # do anything further with the underlying process. - end_normal = True - - # If the output was requested, store it in a list for return at the end - if get_output: - output = [] - store_output = output.append - - for cmd in source: - # skip blank lines for all matches to the 'main' prompt, while the - # secondary prompts do not - if prompt_idx==0 and \ - (cmd.isspace() or cmd.lstrip().startswith('#')): - write(cmd) - continue - - # write('AFTER: '+c.after) # dbg - write(c.after) - c.send(cmd) - try: - prompt_idx = c.expect_list(prompts) - except pexpect.EOF: - # this will happen if the child dies unexpectedly - write(c.before) - end_normal = False - break - - write(c.before) - - # With an echoing process, the output we get in c.before contains - # the command sent, a newline, and then the actual process output - if get_output: - store_output(c.before[len(cmd+'\n'):]) - #write('CMD: <<%s>>' % cmd) # dbg - #write('OUTPUT: <<%s>>' % output[-1]) # dbg - - self.out.flush() - if end_normal: - if interact: - c.send('\n') - print('<< Starting interactive mode >>', end=' ') - try: - c.interact() - except OSError: - # This is what fires when the child stops. Simply print a - # newline so the system prompt is aligned. The extra - # space is there to make sure it gets printed, otherwise - # OS buffering sometimes just suppresses it. - write(' \n') - self.out.flush() - else: - if interact: - e="Further interaction is not possible: child process is dead." - print(e, file=sys.stderr) - - # Leave the child ready for more input later on, otherwise select just - # hangs on the second invocation. - if c.isalive(): - c.send('\n') - - # Return any requested output - if get_output: - return ''.join(output) - - def main(self,argv=None): - """Run as a command-line script.""" - - parser = optparse.OptionParser(usage=USAGE % self.__class__.__name__) - newopt = parser.add_option - newopt('-i','--interact',action='store_true',default=False, - help='Interact with the program after the script is run.') - - opts,args = parser.parse_args(argv) - - if len(args) != 1: - print("You must supply exactly one file to run.", file=sys.stderr) - sys.exit(1) - - self.run_file(args[0],opts.interact) - - -# Specific runners for particular programs -class IPythonRunner(InteractiveRunner): - """Interactive IPython runner. - - This initalizes IPython in 'nocolor' mode for simplicity. This lets us - avoid having to write a regexp that matches ANSI sequences, though pexpect - does support them. If anyone contributes patches for ANSI color support, - they will be welcome. - - It also sets the prompts manually, since the prompt regexps for - pexpect need to be matched to the actual prompts, so user-customized - prompts would break this. - """ - - def __init__(self, program='', args=None, out=sys.stdout, echo=True): - """New runner, optionally passing the ipython command to use.""" - args0 = ['--colors=NoColor', - '--no-term-title', - '--no-autoindent', - # '--quick' is important, to prevent loading default config: - '--quick'] - args = args0 + (args or []) - - # Special case to launch IPython with current interpreter - if program == '': - program = sys.executable - args = ['-m', 'IPython'] + args - - prompts = [r'In \[\d+\]: ',r' \.*: '] - InteractiveRunner.__init__(self,program,prompts,args,out,echo) - - -class PythonRunner(InteractiveRunner): - """Interactive Python runner.""" - - def __init__(self,program=sys.executable, args=None, out=sys.stdout, echo=True): - """New runner, optionally passing the python command to use.""" - - prompts = [r'>>> ',r'\.\.\. '] - InteractiveRunner.__init__(self,program,prompts,args,out,echo) - - -class SAGERunner(InteractiveRunner): - """Interactive SAGE runner. - - WARNING: this runner only works if you manually adjust your SAGE - configuration so that the 'color' option in the configuration file is set to - 'NoColor', because currently the prompt matching regexp does not identify - color sequences.""" - - def __init__(self,program='sage',args=None,out=sys.stdout,echo=True): - """New runner, optionally passing the sage command to use.""" - - prompts = ['sage: ',r'\s*\.\.\. '] - InteractiveRunner.__init__(self,program,prompts,args,out,echo) - - -class RunnerFactory(object): - """Code runner factory. - - This class provides an IPython code runner, but enforces that only one - runner is ever instantiated. The runner is created based on the extension - of the first file to run, and it raises an exception if a runner is later - requested for a different extension type. - - This ensures that we don't generate example files for doctest with a mix of - python and ipython syntax. - """ - - def __init__(self,out=sys.stdout): - """Instantiate a code runner.""" - - self.out = out - self.runner = None - self.runnerClass = None - - def _makeRunner(self,runnerClass): - self.runnerClass = runnerClass - self.runner = runnerClass(out=self.out) - return self.runner - - def __call__(self,fname): - """Return a runner for the given filename.""" - - if fname.endswith('.py'): - runnerClass = PythonRunner - elif fname.endswith('.ipy'): - runnerClass = IPythonRunner - else: - raise ValueError('Unknown file type for Runner: %r' % fname) - - if self.runner is None: - return self._makeRunner(runnerClass) - else: - if runnerClass==self.runnerClass: - return self.runner - else: - e='A runner of type %r can not run file %r' % \ - (self.runnerClass,fname) - raise ValueError(e) - - -# Global usage string, to avoid indentation issues if typed in a function def. -MAIN_USAGE = """ -%prog [options] file_to_run - -This is an interface to the various interactive runners available in this -module. If you want to pass specific options to one of the runners, you need -to first terminate the main options with a '--', and then provide the runner's -options. For example: - -irunner.py --python -- --help - -will pass --help to the python runner. Similarly, - -irunner.py --ipython -- --interact script.ipy - -will run the script.ipy file under the IPython runner, and then will start to -interact with IPython at the end of the script (instead of exiting). - -The already implemented runners are listed below; adding one for a new program -is a trivial task, see the source for examples. -""" - -def main(): - """Run as a command-line script.""" - - parser = optparse.OptionParser(usage=MAIN_USAGE) - newopt = parser.add_option - newopt('--ipython',action='store_const',dest='mode',const='ipython', - help='IPython interactive runner (default).') - newopt('--python',action='store_const',dest='mode',const='python', - help='Python interactive runner.') - newopt('--sage',action='store_const',dest='mode',const='sage', - help='SAGE interactive runner.') - - opts,args = parser.parse_args() - runners = dict(ipython=IPythonRunner, - python=PythonRunner, - sage=SAGERunner) - - try: - ext = os.path.splitext(args[0])[-1] - except IndexError: - ext = '' - modes = {'.ipy':'ipython', - '.py':'python', - '.sage':'sage'} - mode = modes.get(ext,"ipython") - if opts.mode: - mode = opts.mode - runners[mode]().main(args) - -if __name__ == '__main__': - main() diff --git a/IPython/lib/tests/test_imports.py b/IPython/lib/tests/test_imports.py index 7b2eb29..d2e1b87 100644 --- a/IPython/lib/tests/test_imports.py +++ b/IPython/lib/tests/test_imports.py @@ -9,7 +9,3 @@ def test_import_deepreload(): def test_import_demo(): from IPython.lib import demo - -@dec.skip_win32 -def test_import_irunner(): - from IPython.lib import irunner diff --git a/IPython/lib/tests/test_irunner.py b/IPython/lib/tests/test_irunner.py deleted file mode 100644 index cb8a7aa..0000000 --- a/IPython/lib/tests/test_irunner.py +++ /dev/null @@ -1,184 +0,0 @@ -"""Test suite for the irunner module. - -Not the most elegant or fine-grained, but it does cover at least the bulk -functionality.""" -from __future__ import print_function - -# Global to make tests extra verbose and help debugging -VERBOSE = True - -# stdlib imports -import sys -import unittest - -# IPython imports -from IPython.lib import irunner -from IPython.utils.py3compat import doctest_refactor_print, PY3 - -if PY3: - from io import StringIO -else: - from StringIO import StringIO - -# Testing code begins -class RunnerTestCase(unittest.TestCase): - - def setUp(self): - self.out = StringIO() - #self.out = sys.stdout - - def _test_runner(self,runner,source,output): - """Test that a given runner's input/output match.""" - - runner.run_source(source) - out = self.out.getvalue() - #out = '' - # this output contains nasty \r\n lineends, and the initial ipython - # banner. clean it up for comparison, removing lines of whitespace - output_l = [l for l in output.splitlines() if l and not l.isspace()] - out_l = [l for l in out.splitlines() if l and not l.isspace()] - mismatch = 0 - if len(output_l) != len(out_l): - message = ("Mismatch in number of lines\n\n" - "Expected:\n" - "~~~~~~~~~\n" - "%s\n\n" - "Got:\n" - "~~~~~~~~~\n" - "%s" - ) % ("\n".join(output_l), "\n".join(out_l)) - self.fail(message) - for n in range(len(output_l)): - # Do a line-by-line comparison - ol1 = output_l[n].strip() - ol2 = out_l[n].strip() - if ol1 != ol2: - mismatch += 1 - if VERBOSE: - print('<<< line %s does not match:' % n) - print(repr(ol1)) - print(repr(ol2)) - print('>>>') - self.assertTrue(mismatch==0,'Number of mismatched lines: %s' % - mismatch) - - def testIPython(self): - """Test the IPython runner.""" - source = doctest_refactor_print(""" -print 'hello, this is python' -# some more code -x=1;y=2 -x+y**2 - -# An example of autocall functionality -from math import * -autocall 1 -cos pi -autocall 0 -cos pi -cos(pi) - -for i in range(5): - print i - -print "that's all folks!" - -exit -""") - output = doctest_refactor_print("""\ -In [1]: print 'hello, this is python' -hello, this is python - - -# some more code -In [2]: x=1;y=2 - -In [3]: x+y**2 -Out[3]: 5 - - -# An example of autocall functionality -In [4]: from math import * - -In [5]: autocall 1 -Automatic calling is: Smart - -In [6]: cos pi -------> cos(pi) -Out[6]: -1.0 - -In [7]: autocall 0 -Automatic calling is: OFF - -In [8]: cos pi - File "", line 1 - cos pi - ^ -SyntaxError: invalid syntax - - -In [9]: cos(pi) -Out[9]: -1.0 - - -In [10]: for i in range(5): - ....: print i - ....: -0 -1 -2 -3 -4 - -In [11]: print "that's all folks!" -that's all folks! - - -In [12]: exit -""") - runner = irunner.IPythonRunner(out=self.out) - self._test_runner(runner,source,output) - - def testPython(self): - """Test the Python runner.""" - runner = irunner.PythonRunner(out=self.out, args=['-E']) - source = doctest_refactor_print(""" -print 'hello, this is python' - -# some more code -x=1;y=2 -x+y**2 - -from math import * -cos(pi) - -for i in range(5): - print i - -print "that's all folks!" - """) - output = doctest_refactor_print("""\ ->>> print 'hello, this is python' -hello, this is python - -# some more code ->>> x=1;y=2 ->>> x+y**2 -5 - ->>> from math import * ->>> cos(pi) --1.0 - ->>> for i in range(5): -... print i -... -0 -1 -2 -3 -4 ->>> print "that's all folks!" -that's all folks! -""") - self._test_runner(runner,source,output) diff --git a/IPython/lib/tests/test_irunner_pylab_magic.py b/IPython/lib/tests/test_irunner_pylab_magic.py deleted file mode 100644 index adbeff3..0000000 --- a/IPython/lib/tests/test_irunner_pylab_magic.py +++ /dev/null @@ -1,126 +0,0 @@ -"""Test suite for pylab_import_all magic -Modified from the irunner module but using regex. -""" -from __future__ import print_function - -# Global to make tests extra verbose and help debugging -VERBOSE = True - -# stdlib imports -import sys -import unittest -import re - -# IPython imports -from IPython.lib import irunner -from IPython.testing import decorators -from IPython.utils.py3compat import PY3 - -if PY3: - from io import StringIO -else: - from StringIO import StringIO - -def pylab_not_importable(): - """Test if importing pylab fails. (For example, when having no display)""" - try: - import pylab - return False - except: - return True - -# Testing code begins -class RunnerTestCase(unittest.TestCase): - - def setUp(self): - self.out = StringIO() - #self.out = sys.stdout - - def _test_runner(self,runner,source,output): - """Test that a given runner's input/output match.""" - - runner.run_source(source) - out = self.out.getvalue() - #out = '' - # this output contains nasty \r\n lineends, and the initial ipython - # banner. clean it up for comparison, removing lines of whitespace - output_l = [l for l in output.splitlines() if l and not l.isspace()] - out_l = [l for l in out.splitlines() if l and not l.isspace()] - mismatch = 0 - if len(output_l) != len(out_l): - message = ("Mismatch in number of lines\n\n" - "Expected:\n" - "~~~~~~~~~\n" - "%s\n\n" - "Got:\n" - "~~~~~~~~~\n" - "%s" - ) % ("\n".join(output_l), "\n".join(out_l)) - self.fail(message) - for n in range(len(output_l)): - # Do a line-by-line comparison - ol1 = output_l[n].strip() - ol2 = out_l[n].strip() - if not re.match(ol1,ol2): - mismatch += 1 - if VERBOSE: - print('<<< line %s does not match:' % n) - print(repr(ol1)) - print(repr(ol2)) - print('>>>') - self.assertTrue(mismatch==0,'Number of mismatched lines: %s' % - mismatch) - - @decorators.skip_if_no_x11 - @decorators.skipif_not_matplotlib - @decorators.skipif(pylab_not_importable, "Likely a run without X.") - def test_pylab_import_all_enabled(self): - "Verify that plot is available when pylab_import_all = True" - source = """ -from IPython.config.application import Application -app = Application.instance() -app.pylab_import_all = True -pylab -ip=get_ipython() -'plot' in ip.user_ns - """ - output = """ -In \[1\]: from IPython\.config\.application import Application -In \[2\]: app = Application\.instance\(\) -In \[3\]: app\.pylab_import_all = True -In \[4\]: pylab -^Using matplotlib backend: -Populating the interactive namespace from numpy and matplotlib -In \[5\]: ip=get_ipython\(\) -In \[6\]: \'plot\' in ip\.user_ns -Out\[6\]: True -""" - runner = irunner.IPythonRunner(out=self.out) - self._test_runner(runner,source,output) - - @decorators.skip_if_no_x11 - @decorators.skipif_not_matplotlib - @decorators.skipif(pylab_not_importable, "Likely a run without X.") - def test_pylab_import_all_disabled(self): - "Verify that plot is not available when pylab_import_all = False" - source = """ -from IPython.config.application import Application -app = Application.instance() -app.pylab_import_all = False -pylab -ip=get_ipython() -'plot' in ip.user_ns - """ - output = """ -In \[1\]: from IPython\.config\.application import Application -In \[2\]: app = Application\.instance\(\) -In \[3\]: app\.pylab_import_all = False -In \[4\]: pylab -^Using matplotlib backend: -Populating the interactive namespace from numpy and matplotlib -In \[5\]: ip=get_ipython\(\) -In \[6\]: \'plot\' in ip\.user_ns -Out\[6\]: False -""" - runner = irunner.IPythonRunner(out=self.out) - self._test_runner(runner,source,output) diff --git a/IPython/testing/iptest.py b/IPython/testing/iptest.py index a39912a..3bd7e1b 100644 --- a/IPython/testing/iptest.py +++ b/IPython/testing/iptest.py @@ -201,9 +201,6 @@ if not have['matplotlib']: # lib: sec = test_sections['lib'] -if not have['pexpect']: - sec.exclude('irunner') - sec.exclude('tests.test_irunner') if not have['zmq']: sec.exclude('kernel') # We do this unconditionally, so that the test suite doesn't import @@ -219,8 +216,6 @@ sec.exclude('inputhook') # testing: sec = test_sections['testing'] -# This guy is probably attic material -sec.exclude('mkdoctests') # These have to be skipped on win32 because they use echo, rm, cd, etc. # See ticket https://github.com/ipython/ipython/issues/87 if sys.platform == 'win32': diff --git a/IPython/testing/mkdoctests.py b/IPython/testing/mkdoctests.py deleted file mode 100755 index f06f20f..0000000 --- a/IPython/testing/mkdoctests.py +++ /dev/null @@ -1,244 +0,0 @@ -#!/usr/bin/env python -"""Utility for making a doctest file out of Python or IPython input. - - %prog [options] input_file [output_file] - -This script is a convenient generator of doctest files that uses IPython's -irunner script to execute valid Python or IPython input in a separate process, -capture all of the output, and write it to an output file. - -It can be used in one of two ways: - -1. With a plain Python or IPython input file (denoted by extensions '.py' or - '.ipy'. In this case, the output is an auto-generated reST file with a - basic header, and the captured Python input and output contained in an - indented code block. - - If no output filename is given, the input name is used, with the extension - replaced by '.txt'. - -2. With an input template file. Template files are simply plain text files - with special directives of the form - - %run filename - - to include the named file at that point. - - If no output filename is given and the input filename is of the form - 'base.tpl.txt', the output will be automatically named 'base.txt'. -""" - -# Standard library imports - -import optparse -import os -import re -import sys -import tempfile - -# IPython-specific libraries -from IPython.lib import irunner -from IPython.utils.warn import fatal - -class IndentOut(object): - """A simple output stream that indents all output by a fixed amount. - - Instances of this class trap output to a given stream and first reformat it - to indent every input line.""" - - def __init__(self,out=sys.stdout,indent=4): - """Create an indented writer. - - :Keywords: - - - `out` : stream (sys.stdout) - Output stream to actually write to after indenting. - - - `indent` : int - Number of spaces to indent every input line by. - """ - - self.indent_text = ' '*indent - self.indent = re.compile('^',re.MULTILINE).sub - self.out = out - self._write = out.write - self.buffer = [] - self._closed = False - - def write(self,data): - """Write a string to the output stream.""" - - if self._closed: - raise ValueError('I/O operation on closed file') - self.buffer.append(data) - - def flush(self): - if self.buffer: - data = ''.join(self.buffer) - self.buffer[:] = [] - self._write(self.indent(self.indent_text,data)) - - def close(self): - self.flush() - self._closed = True - -class RunnerFactory(object): - """Code runner factory. - - This class provides an IPython code runner, but enforces that only one - runner is every instantiated. The runner is created based on the extension - of the first file to run, and it raises an exception if a runner is later - requested for a different extension type. - - This ensures that we don't generate example files for doctest with a mix of - python and ipython syntax. - """ - - def __init__(self,out=sys.stdout): - """Instantiate a code runner.""" - - self.out = out - self.runner = None - self.runnerClass = None - - def _makeRunner(self,runnerClass): - self.runnerClass = runnerClass - self.runner = runnerClass(out=self.out) - return self.runner - - def __call__(self,fname): - """Return a runner for the given filename.""" - - if fname.endswith('.py'): - runnerClass = irunner.PythonRunner - elif fname.endswith('.ipy'): - runnerClass = irunner.IPythonRunner - else: - raise ValueError('Unknown file type for Runner: %r' % fname) - - if self.runner is None: - return self._makeRunner(runnerClass) - else: - if runnerClass==self.runnerClass: - return self.runner - else: - e='A runner of type %r can not run file %r' % \ - (self.runnerClass,fname) - raise ValueError(e) - -TPL = """ -========================= - Auto-generated doctests -========================= - -This file was auto-generated by IPython in its entirety. If you need finer -control over the contents, simply make a manual template. See the -mkdoctests.py script for details. - -%%run %s -""" - -def main(): - """Run as a script.""" - - # Parse options and arguments. - parser = optparse.OptionParser(usage=__doc__) - newopt = parser.add_option - newopt('-f','--force',action='store_true',dest='force',default=False, - help='Force overwriting of the output file.') - newopt('-s','--stdout',action='store_true',dest='stdout',default=False, - help='Use stdout instead of a file for output.') - - opts,args = parser.parse_args() - if len(args) < 1: - parser.error("incorrect number of arguments") - - # Input filename - fname = args[0] - - # We auto-generate the output file based on a trivial template to make it - # really easy to create simple doctests. - - auto_gen_output = False - try: - outfname = args[1] - except IndexError: - outfname = None - - if fname.endswith('.tpl.txt') and outfname is None: - outfname = fname.replace('.tpl.txt','.txt') - else: - bname, ext = os.path.splitext(fname) - if ext in ['.py','.ipy']: - auto_gen_output = True - if outfname is None: - outfname = bname+'.txt' - - # Open input file - - # In auto-gen mode, we actually change the name of the input file to be our - # auto-generated template - if auto_gen_output: - infile = tempfile.TemporaryFile() - infile.write(TPL % fname) - infile.flush() - infile.seek(0) - else: - infile = open(fname) - - # Now open the output file. If opts.stdout was given, this overrides any - # explicit choice of output filename and just directs all output to - # stdout. - if opts.stdout: - outfile = sys.stdout - else: - # Argument processing finished, start main code - if os.path.isfile(outfname) and not opts.force: - fatal("Output file %r exists, use --force (-f) to overwrite." - % outfname) - outfile = open(outfname,'w') - - - # all output from included files will be indented - indentOut = IndentOut(outfile,4) - getRunner = RunnerFactory(indentOut) - - # Marker in reST for transition lines - rst_transition = '\n'+'-'*76+'\n\n' - - # local shorthand for loop - write = outfile.write - - # Process input, simply writing back out all normal lines and executing the - # files in lines marked as '%run filename'. - for line in infile: - if line.startswith('%run '): - # We don't support files with spaces in their names. - incfname = line.split()[1] - - # We make the output of the included file appear bracketed between - # clear reST transition marks, and indent it so that if anyone - # makes an HTML or PDF out of the file, all doctest input and - # output appears in proper literal blocks. - write(rst_transition) - write('Begin included file %s::\n\n' % incfname) - - # I deliberately do NOT trap any exceptions here, so that if - # there's any problem, the user running this at the command line - # finds out immediately by the code blowing up, rather than ending - # up silently with an incomplete or incorrect file. - getRunner(incfname).run_file(incfname) - - write('\nEnd included file %s\n' % incfname) - write(rst_transition) - else: - # The rest of the input file is just written out - write(line) - infile.close() - - # Don't close sys.stdout!!! - if outfile is not sys.stdout: - outfile.close() - -if __name__ == '__main__': - main()