diff --git a/.travis.yml b/.travis.yml
index 0d7a20c1..519f34b2 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -78,7 +78,7 @@ matrix:
# Run the notebook to regenerate the outputs, then compare the new notebook to the old one.
stack install --fast
stack exec -- ihaskell install --stack
- stack exec -- jupyter nbconvert --to=notebook --execute --stdout notebooks/IHaskell.ipynb > ~/ihaskell-out.ipynb
+ stack exec -- jupyter nbconvert --to=notebook --execute --allow-errors --stdout notebooks/IHaskell.ipynb > ~/ihaskell-out.ipynb
# Images are rendered differently on different systems, so filter them out in the comparison
diff <(egrep -v 'image/png|version|pygments' ~/ihaskell-out.ipynb) <(egrep -v 'image/png|version|pygments' notebooks/IHaskell.ipynb)
@@ -158,7 +158,7 @@ matrix:
# Run the notebook to regenerate the outputs, then compare the new notebook to the old one.
stack install --fast
stack exec -- ihaskell install --stack
- stack exec -- jupyter nbconvert --to=notebook --execute --stdout notebooks/IHaskell.ipynb > ~/ihaskell-out.ipynb
+ stack exec -- jupyter nbconvert --to=notebook --execute --allow-errors --stdout notebooks/IHaskell.ipynb > ~/ihaskell-out.ipynb
# Images are rendered differently on different systems, so filter them out in the comparison
diff <(egrep -v 'image/png|version|pygments' ~/ihaskell-out.ipynb) <(egrep -v 'image/png|version|pygments' notebooks/IHaskell.ipynb)
@@ -238,7 +238,7 @@ matrix:
# Run the notebook to regenerate the outputs, then compare the new notebook to the old one.
stack install --fast
stack exec -- ihaskell install --stack
- stack exec -- jupyter nbconvert --to=notebook --execute --stdout notebooks/IHaskell.ipynb > ~/ihaskell-out.ipynb
+ stack exec -- jupyter nbconvert --to=notebook --execute --allow-errors --stdout notebooks/IHaskell.ipynb > ~/ihaskell-out.ipynb
# Images are rendered differently on different systems, so filter them out in the comparison
diff <(egrep -v 'image/png|version|pygments' ~/ihaskell-out.ipynb) <(egrep -v 'image/png|version|pygments' notebooks/IHaskell.ipynb)
@@ -318,7 +318,7 @@ matrix:
# Run the notebook to regenerate the outputs, then compare the new notebook to the old one.
stack install --fast
stack exec -- ihaskell install --stack
- stack exec -- jupyter nbconvert --to=notebook --execute --stdout notebooks/IHaskell.ipynb > ~/ihaskell-out.ipynb
+ stack exec -- jupyter nbconvert --to=notebook --execute --allow-errors --stdout notebooks/IHaskell.ipynb > ~/ihaskell-out.ipynb
# Images are rendered differently on different systems, so filter them out in the comparison
diff <(egrep -v 'image/png|version|pygments' ~/ihaskell-out.ipynb) <(egrep -v 'image/png|version|pygments' notebooks/IHaskell.ipynb)
@@ -352,7 +352,7 @@ matrix:
ihaskell-plot
ihaskell-static-canvas
])'
- - result/bin/ihaskell-nbconvert --to=notebook --execute --stdout notebooks/IHaskell.ipynb > ~/ihaskell-out.ipynb
+ - result/bin/ihaskell-nbconvert --to=notebook --execute --allow-errors --stdout notebooks/IHaskell.ipynb > ~/ihaskell-out.ipynb
- diff <(egrep -v 'image/png|version|pygments' ~/ihaskell-out.ipynb) <(egrep -v 'image/png|version|pygments' notebooks/IHaskell.ipynb)
- language: nix
dist: xenial
@@ -383,5 +383,5 @@ matrix:
ihaskell-plot
ihaskell-static-canvas
])'
- - result/bin/ihaskell-nbconvert --to=notebook --execute --stdout notebooks/IHaskell.ipynb > ~/ihaskell-out.ipynb
+ - result/bin/ihaskell-nbconvert --to=notebook --execute --allow-errors --stdout notebooks/IHaskell.ipynb > ~/ihaskell-out.ipynb
- diff <(egrep -v 'image/png|version|pygments' ~/ihaskell-out.ipynb) <(egrep -v 'image/png|version|pygments' notebooks/IHaskell.ipynb)
diff --git a/ipython-kernel/src/IHaskell/IPython/Message/Parser.hs b/ipython-kernel/src/IHaskell/IPython/Message/Parser.hs
index 0d40f88b..072b0d7f 100644
--- a/ipython-kernel/src/IHaskell/IPython/Message/Parser.hs
+++ b/ipython-kernel/src/IHaskell/IPython/Message/Parser.hs
@@ -148,7 +148,7 @@ executeErrorParser = requestParser $ \obj -> do
traceback <- obj .: "traceback"
ename <- obj .: "ename"
evalue <- obj .: "evalue"
- return $ ExecuteError noHeader [] traceback ename evalue
+ return $ ExecuteError noHeader traceback ename evalue
makeDisplayDatas :: Object -> [DisplayData]
makeDisplayDatas dataDict = [DisplayData (read $ unpack mimeType) content | (mimeType, String content) <- HM.toList
diff --git a/ipython-kernel/src/IHaskell/IPython/Types.hs b/ipython-kernel/src/IHaskell/IPython/Types.hs
index 8d80df08..d3cad68a 100644
--- a/ipython-kernel/src/IHaskell/IPython/Types.hs
+++ b/ipython-kernel/src/IHaskell/IPython/Types.hs
@@ -378,7 +378,6 @@ data Message =
-- | An error reply to an execute request
ExecuteError
{ header :: MessageHeader
- , pagerOutput :: [DisplayData] -- ^ The mimebundles to display in the pager.
, traceback :: [Text]
, ename :: Text
, evalue :: Text
@@ -561,6 +560,13 @@ instance ToJSON Message where
, "data" .= object (map displayDataToJson o)
]
]
+ toJSON ExecuteError { header = header, traceback = traceback, ename = ename, evalue = evalue } =
+ object
+ [ "header" .= show header
+ , "traceback" .= map toJSON traceback
+ , "ename" .= ename
+ , "evalue" .= evalue
+ ]
toJSON PublishStatus { executionState = executionState } =
object ["execution_state" .= executionState]
toJSON PublishStream { streamType = streamType, streamContent = content } =
diff --git a/main/Main.hs b/main/Main.hs
index d353c646..f96d695b 100644
--- a/main/Main.hs
+++ b/main/Main.hs
@@ -12,7 +12,7 @@ import qualified Data.ByteString.Lazy as LBS
-- Standard library imports.
import Control.Concurrent.Chan
import Control.Arrow (second)
-import Data.Aeson
+import Data.Aeson hiding (Success)
import System.Process (readProcess, readProcessWithExitCode)
import System.Exit (exitSuccess, ExitCode(ExitSuccess))
import Control.Exception (try, SomeException)
@@ -151,7 +151,7 @@ runKernel kOpts profileSrc = do
kernelState { supportLibrariesAvailable = hasSupportLibraries }
-- Initialize the context by evaluating everything we got from the command line flags.
- let noPublish _ = return ()
+ let noPublish _ _ = return ()
noWidget s _ = return s
evaluator line = void $ do
-- Create a new state each time.
@@ -429,12 +429,12 @@ handleComm send kernelState req replyHeader = do
CommDataMessage -> do
disp <- run $ comm widget dat communicate
pgrOut <- liftIO $ readMVar pOut
- liftIO $ publish $ FinalResult disp (if toUsePager then pgrOut else []) []
+ liftIO $ publish (FinalResult disp (if toUsePager then pgrOut else []) []) Success
return kernelState
CommCloseMessage -> do
disp <- run $ close widget dat
pgrOut <- liftIO $ readMVar pOut
- liftIO $ publish $ FinalResult disp (if toUsePager then pgrOut else []) []
+ liftIO $ publish (FinalResult disp (if toUsePager then pgrOut else []) []) Success
return kernelState { openComms = Map.delete uuid widgets }
_ ->
-- Only sensible thing to do.
diff --git a/notebooks/IHaskell.ipynb b/notebooks/IHaskell.ipynb
index 44b45ae4..0e3f35aa 100644
--- a/notebooks/IHaskell.ipynb
+++ b/notebooks/IHaskell.ipynb
@@ -183,99 +183,12 @@
"metadata": {},
"outputs": [
{
- "data": {
- "text/html": [
- "<interactive>:1:1: error:
• ‘Thing’ has no constructors (EmptyDataDecls permits this)
• In the data declaration for ‘Thing’"
- ],
- "text/plain": [
- ":1:1: error:\n",
- " • ‘Thing’ has no constructors (EmptyDataDecls permits this)\n",
- " • In the data declaration for ‘Thing’"
- ]
- },
- "metadata": {},
- "output_type": "display_data"
+ "ename": "",
+ "evalue": "",
+ "output_type": "error",
+ "traceback": [
+ ":1:1: error:\n • ‘Thing’ has no constructors (EmptyDataDecls permits this)\n • In the data declaration for ‘Thing’"
+ ]
}
],
"source": [
@@ -3275,97 +3188,12 @@
"metadata": {},
"outputs": [
{
- "data": {
- "text/html": [
- "<interactive>:1:1: error: Variable not in scope: f :: Integer -> t"
- ],
- "text/plain": [
- ":1:1: error: Variable not in scope: f :: Integer -> t"
- ]
- },
- "metadata": {},
- "output_type": "display_data"
+ "ename": "",
+ "evalue": "",
+ "output_type": "error",
+ "traceback": [
+ ":1:1: error: Variable not in scope: f :: Integer -> t"
+ ]
}
],
"source": [
diff --git a/src/IHaskell/Eval/Evaluate.hs b/src/IHaskell/Eval/Evaluate.hs
index 7b2a6897..ce1e587d 100644
--- a/src/IHaskell/Eval/Evaluate.hs
+++ b/src/IHaskell/Eval/Evaluate.hs
@@ -70,9 +70,6 @@ import Data.Version (versionBranch)
#endif
-data ErrorOccurred = Success
- | Failure
- deriving (Show, Eq)
-- | Set GHC's verbosity for debugging
ghcVerbosity :: Maybe Int
@@ -126,7 +123,7 @@ testInterpret v = interpret GHC.Paths.libdir False (const v)
-- | Evaluation function for testing.
testEvaluate :: String -> IO ()
testEvaluate str = void $ testInterpret $
- evaluate defaultKernelState str (const $ return ()) (\state _ -> return state)
+ evaluate defaultKernelState str (\_ _ -> return ()) (\state _ -> return state)
-- | Run an interpreting action. This is effectively runGhc with initialization and importing. First
-- argument indicates whether `stdin` is handled specially, which cannot be done in a testing
@@ -249,8 +246,9 @@ initializeItVariable =
void $ execStmt "let it = ()" execOptions
-- | Publisher for IHaskell outputs. The first argument indicates whether this output is final
--- (true) or intermediate (false).
-type Publisher = (EvaluationResult -> IO ())
+-- (true) or intermediate (false). The second argument indicates whether the evaluation
+-- completed successfully (Success) or an error occurred (Failure).
+type Publisher = (EvaluationResult -> ErrorOccurred -> IO ())
-- | Output of a command evaluation.
data EvalOut =
@@ -298,14 +296,16 @@ evaluate kernelState code output widgetHandler = do
when (getLintStatus kernelState /= LintOff) $ liftIO $ do
lintSuggestions <- lint cmds
unless (noResults lintSuggestions) $
- output $ FinalResult lintSuggestions [] []
+ output (FinalResult lintSuggestions [] []) Success
runUntilFailure kernelState (map unloc cmds ++ [storeItCommand execCount])
-- Print all parse errors.
_ -> do
forM_ errs $ \err -> do
out <- evalCommand output err kernelState
- liftIO $ output $ FinalResult (evalResult out) [] []
+ liftIO $ output
+ (FinalResult (evalResult out) [] [])
+ (evalStatus out)
return kernelState
return updated { getExecutionCounter = execCount + 1 }
@@ -338,7 +338,9 @@ evaluate kernelState code output widgetHandler = do
-- Output things only if they are non-empty.
unless (noResults result && null (evalPager evalOut)) $
- liftIO $ output $ FinalResult result (evalPager evalOut) []
+ liftIO $ output
+ (FinalResult result (evalPager evalOut) [])
+ (evalStatus evalOut)
let tempMsgs = evalMsgs evalOut
tempState = evalState evalOut { evalMsgs = [] }
@@ -693,7 +695,7 @@ evalCommand publish (Directive ShellCmd cmd) state = wrapExecution state $
case mExitCode of
Nothing -> do
-- Write to frontend and repeat.
- readMVar outputAccum >>= output
+ readMVar outputAccum >>= flip output Success
loop
Just exitCode -> do
next <- readChars pipe "" maxSize
@@ -1222,7 +1224,7 @@ evalStatementOrIO publish state cmd = do
CapturedIO _ ->
write state "Evaluating Action"
- (printed, result) <- capturedEval output cmd
+ (printed, result) <- capturedEval (flip output Success) cmd
case result of
ExecComplete (Right names) _ -> do
dflags <- getSessionDynFlags
diff --git a/src/IHaskell/Publish.hs b/src/IHaskell/Publish.hs
index 53271ae1..968d8e1c 100644
--- a/src/IHaskell/Publish.hs
+++ b/src/IHaskell/Publish.hs
@@ -26,8 +26,9 @@ publishResult :: (Message -> IO ()) -- ^ A function to send messages
-> MVar [DisplayData] -- ^ A MVar to use for storing pager output
-> Bool -- ^ Whether to use the pager
-> EvaluationResult -- ^ The evaluation result
+ -> ErrorOccurred -- ^ Whether evaluation completed successfully
-> IO ()
-publishResult send replyHeader displayed updateNeeded poutput upager result = do
+publishResult send replyHeader displayed updateNeeded poutput upager result success = do
let final =
case result of
IntermediateResult{} -> False
@@ -69,9 +70,13 @@ publishResult send replyHeader displayed updateNeeded poutput upager result = do
sendOutput uniqueLabel (ManyDisplay manyOuts) =
mapM_ (sendOutput uniqueLabel) manyOuts
- sendOutput uniqueLabel (Display outs) = do
- hdr <- dupHeader replyHeader DisplayDataMessage
- send $ PublishDisplayData hdr (map (makeUnique uniqueLabel . prependCss) outs) Nothing
+ sendOutput uniqueLabel (Display outs) = case success of
+ Success -> do
+ hdr <- dupHeader replyHeader DisplayDataMessage
+ send $ PublishDisplayData hdr (map (makeUnique uniqueLabel . prependCss) outs) Nothing
+ Failure -> do
+ hdr <- dupHeader replyHeader ExecuteErrorMessage
+ send $ ExecuteError hdr [T.pack (extractPlain outs)] "" ""
prependCss (DisplayData MimeHtml h) =
DisplayData MimeHtml $ mconcat ["", h]
diff --git a/src/IHaskell/Types.hs b/src/IHaskell/Types.hs
index aac4d7f8..03a20a96 100644
--- a/src/IHaskell/Types.hs
+++ b/src/IHaskell/Types.hs
@@ -18,6 +18,7 @@ module IHaskell.Types (
StreamType(..),
MimeType(..),
DisplayData(..),
+ ErrorOccurred(..),
EvaluationResult(..),
evaluationOutputs,
ExecuteReplyStatus(..),
@@ -274,5 +275,9 @@ evaluationOutputs er =
dupHeader :: MessageHeader -> MessageType -> IO MessageHeader
dupHeader hdr messageType = do
uuid <- liftIO random
-
return hdr { mhMessageId = uuid, mhMsgType = messageType }
+
+-- | Whether or not an error occurred.
+data ErrorOccurred = Success
+ | Failure
+ deriving (Show, Eq)
diff --git a/src/tests/IHaskell/Test/Eval.hs b/src/tests/IHaskell/Test/Eval.hs
index c301c83d..197fb772 100644
--- a/src/tests/IHaskell/Test/Eval.hs
+++ b/src/tests/IHaskell/Test/Eval.hs
@@ -24,7 +24,7 @@ eval :: String -> IO ([Display], String)
eval string = do
outputAccum <- newIORef []
pagerAccum <- newIORef []
- let publish evalResult =
+ let publish evalResult _ =
case evalResult of
IntermediateResult{} -> return ()
FinalResult outs page _ -> do