From d77e2f514e79c7a7508e5bf0122d7630f34aadd8 2013-12-13 20:11:14 From: Thomas Kluyver Date: 2013-12-13 20:11:14 Subject: [PATCH] Option to spew subprocess streams during tests This supersedes PR #4268. Run the tests with '--subproc-streams show' to show output from subprocesses (kernels, IPython.parallel components) in the terminal, or with '--subproc-streams discard' to send it to /dev/null. By default (or with '--subproc-streams capture') the output is piped, captured and displayed only when tests fail. But in some situations, a test fails because of an error which actually occurred earlier, so you have to see all the output. --- diff --git a/IPython/kernel/tests/utils.py b/IPython/kernel/tests/utils.py index c1ce603..0577c6a 100644 --- a/IPython/kernel/tests/utils.py +++ b/IPython/kernel/tests/utils.py @@ -43,11 +43,10 @@ KC = None def start_new_kernel(argv=None): """start a new kernel, and return its Manager and Client""" km = KernelManager() - kwargs = dict(stdout=nose.ipy_stream_capturer.writefd, stderr=STDOUT) + kwargs = dict(stdout=nose.iptest_stdstreams_fileno(), stderr=STDOUT) if argv: kwargs['extra_arguments'] = argv km.start_kernel(**kwargs) - nose.ipy_stream_capturer.ensure_started() kc = km.client() kc.start_channels() diff --git a/IPython/parallel/tests/__init__.py b/IPython/parallel/tests/__init__.py index c264eff..3a7d6c6 100644 --- a/IPython/parallel/tests/__init__.py +++ b/IPython/parallel/tests/__init__.py @@ -37,16 +37,15 @@ class TestProcessLauncher(LocalProcessLauncher): """subclass LocalProcessLauncher, to prevent extra sockets and threads being created on Windows""" def start(self): if self.state == 'before': + # Store stdout & stderr to show with failing tests. + # This is defined in IPython.testing.iptest self.process = Popen(self.args, - stdout=nose.ipy_stream_capturer.writefd, stderr=STDOUT, + stdout=nose.iptest_stdstreams_fileno(), stderr=STDOUT, env=os.environ, cwd=self.work_dir ) self.notify_start(self.process.pid) self.poll = self.process.poll - # Store stdout & stderr to show with failing tests. - # This is defined in IPython.testing.iptest - nose.ipy_stream_capturer.ensure_started() else: s = 'The process was already started and has state: %r' % self.state raise ProcessStateError(s) diff --git a/IPython/testing/iptest.py b/IPython/testing/iptest.py index f12e6c4..a39912a 100644 --- a/IPython/testing/iptest.py +++ b/IPython/testing/iptest.py @@ -407,14 +407,25 @@ class SubprocessStreamCapturePlugin(Plugin): def __init__(self): Plugin.__init__(self) self.stream_capturer = StreamCapturer() + self.destination = os.environ.get('IPTEST_SUBPROC_STREAMS', 'capture') # This is ugly, but distant parts of the test machinery need to be able # to redirect streams, so we make the object globally accessible. - nose.ipy_stream_capturer = self.stream_capturer + nose.iptest_stdstreams_fileno = self.get_write_fileno + + def get_write_fileno(self): + if self.destination == 'capture': + self.stream_capturer.ensure_started() + return self.stream_capturer.writefd + elif self.destination == 'discard': + return os.open(os.devnull, os.O_WRONLY) + else: + return sys.__stdout__.fileno() def configure(self, options, config): Plugin.configure(self, options, config) # Override nose trying to disable plugin. - self.enabled = True + if self.destination == 'capture': + self.enabled = True def startTest(self, test): # Reset log capture diff --git a/IPython/testing/iptestcontroller.py b/IPython/testing/iptestcontroller.py index 73197c8..cea88e8 100644 --- a/IPython/testing/iptestcontroller.py +++ b/IPython/testing/iptestcontroller.py @@ -237,20 +237,23 @@ def prepare_controllers(options): c_py = [PyTestController(name) for name in py_testgroups] configure_py_controllers(c_py, xunit=options.xunit, - coverage=options.coverage, extra_args=options.extra_args) + coverage=options.coverage, subproc_streams=options.subproc_streams, + extra_args=options.extra_args) controllers = c_py + c_js to_run = [c for c in controllers if c.will_run] not_run = [c for c in controllers if not c.will_run] return to_run, not_run -def configure_py_controllers(controllers, xunit=False, coverage=False, extra_args=()): +def configure_py_controllers(controllers, xunit=False, coverage=False, + subproc_streams='capture', extra_args=()): """Apply options for a collection of TestController objects.""" for controller in controllers: if xunit: controller.add_xunit() if coverage: controller.add_coverage() + controller.env['IPTEST_SUBPROC_STREAMS'] = subproc_streams controller.cmd.extend(extra_args) def do_run(controller): @@ -469,6 +472,9 @@ argparser.add_argument('--xunit', action='store_true', argparser.add_argument('--coverage', nargs='?', const=True, default=False, help="Measure test coverage. Specify 'html' or " "'xml' to get reports.") +argparser.add_argument('--subproc-streams', default='capture', + help="What to do with stdout/stderr from subprocesses. " + "'capture' (default), 'show' and 'discard' are the options.") def default_options(): """Get an argparse Namespace object with the default arguments, to pass to