"""Module containing a preprocessor that removes the outputs from code cells"""

# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.

import os

try:
    from queue import Empty  # Py 3
except ImportError:
    from Queue import Empty  # Py 2

from IPython.utils.traitlets import List, Unicode

from IPython.nbformat.v4 import output_from_msg
from .base import Preprocessor
from IPython.utils.traitlets import Integer


class ExecutePreprocessor(Preprocessor):
    """
    Executes all the cells in a notebook
    """
    
    timeout = Integer(30, config=True,
        help="The time to wait (in seconds) for output from executions."
    )
    
    extra_arguments = List(Unicode)

    def preprocess(self, nb, resources):
        path = resources.get('metadata', {}).get('path', '')
        if path == '':
            path = None

        from IPython.kernel import run_kernel
        kernel_name = nb.metadata.get('kernelspec', {}).get('name', 'python')
        self.log.info("Executing notebook with kernel: %s" % kernel_name)
        with run_kernel(kernel_name=kernel_name,
                        extra_arguments=self.extra_arguments,
                        stderr=open(os.devnull, 'w'),
                        cwd=path) as kc:
            self.kc = kc
            self.kc.allow_stdin = False
            nb, resources = super(ExecutePreprocessor, self).preprocess(nb, resources)
        return nb, resources

    def preprocess_cell(self, cell, resources, cell_index):
        """
        Apply a transformation on each code cell. See base.py for details.
        """
        if cell.cell_type != 'code':
            return cell, resources
        try:
            outputs = self.run_cell(cell)
        except Exception as e:
            self.log.error("failed to run cell: " + repr(e))
            self.log.error(str(cell.source))
            raise
        cell.outputs = outputs
        return cell, resources

    def run_cell(self, cell):
        msg_id = self.kc.execute(cell.source)
        self.log.debug("Executing cell:\n%s", cell.source)
        # wait for finish, with timeout
        while True:
            try:
                msg = self.kc.shell_channel.get_msg(timeout=self.timeout)
            except Empty:
                self.log.error("Timeout waiting for execute reply")
                raise
            if msg['parent_header'].get('msg_id') == msg_id:
                break
            else:
                # not our reply
                continue
        
        outs = []

        while True:
            try:
                msg = self.kc.iopub_channel.get_msg(timeout=self.timeout)
            except Empty:
                self.log.warn("Timeout waiting for IOPub output")
                break
            if msg['parent_header'].get('msg_id') != msg_id:
                # not an output from our execution
                continue

            msg_type = msg['msg_type']
            self.log.debug("output: %s", msg_type)
            content = msg['content']

            # set the prompt number for the input and the output
            if 'execution_count' in content:
                cell['execution_count'] = content['execution_count']

            if msg_type == 'status':
                if content['execution_state'] == 'idle':
                    break
                else:
                    continue
            elif msg_type == 'execute_input':
                continue
            elif msg_type == 'clear_output':
                outs = []
                continue

            try:
                out = output_from_msg(msg)
            except ValueError:
                self.log.error("unhandled iopub msg: " + msg_type)
            else:
                outs.append(out)

        return outs

@@ -185,16 +185,44 @@
 mport os
+%0Afrom textwrap import dedent
 %0A%0Atry:%0A 
@@ -362,16 +362,22 @@
  Unicode
+, Bool
 %0A%0Afrom I
@@ -715,16 +715,319 @@
 .%22%0A    )
+%0A%0A    interrupt_on_timeout = Bool(%0A        False, config=True,%0A        help=dedent(%0A            %22%22%22%0A            If execution of a cell times out, interrupt the kernel and %0A            continue executing other cells rather than throwing an error and %0A            stopping.%0A            %22%22%22%0A        )%0A    )
 %0A    %0A  
@@ -1242,19 +1242,33 @@
 rnel
- import run
+.manager import start_new
 _ker
@@ -1435,24 +1435,57 @@
     
-with run_kernel(
+self.km, self.kc = start_new_kernel(%0A            
 kern
@@ -1505,28 +1505,16 @@
 l_name,%0A
-            
         
@@ -1567,28 +1567,16 @@
         
-   
-         
 stderr=o
@@ -1613,66 +1613,18 @@
     
-            cwd=path) as kc:%0A            self.kc = kc%0A    
+cwd=path)%0A
     
@@ -1654,16 +1654,30 @@
  = False
+%0A%0A        try:
 %0A       
@@ -1756,16 +1756,116 @@
 ources)%0A
+        finally:%0A            self.kc.stop_channels()%0A            self.km.shutdown_kernel(now=True)%0A%0A
         
@@ -2770,16 +2770,219 @@
 reply%22)%0A
+                if self.interrupt_on_timeout:%0A                    self.log.error(%22Interrupting kernel%22)%0A                    self.km.interrupt_kernel()%0A                    break%0A                else:%0A    
         
@@ -2987,32 +2987,33 @@
           raise%0A
+%0A
             if m

Add option to interrupt kernel after execution timeout