Unverified Commit 726999ae authored by Vaibhav Sagar's avatar Vaibhav Sagar Committed by GitHub

Merge pull request #928 from erikd/topic/warnings-other

Turn on and fix more warnings
parents 50ff0ff7 196ccac9
...@@ -31,15 +31,14 @@ import Bag ...@@ -31,15 +31,14 @@ import Bag
import ErrUtils hiding (ErrMsg) import ErrUtils hiding (ErrMsg)
import FastString import FastString
#if MIN_VERSION_ghc(8,4,0) #if MIN_VERSION_ghc(8,4,0)
import GHC hiding (Located, Parsed) import GHC hiding (Located, Parsed, parser)
#else #else
import GHC hiding (Located) import GHC hiding (Located, parser)
#endif #endif
import Lexer import Lexer hiding (buffer)
import OrdList import OrdList
import Outputable hiding ((<>)) import qualified SrcLoc as SrcLoc
import SrcLoc hiding (Located) import StringBuffer hiding (len)
import StringBuffer
import qualified Language.Haskell.GHC.HappyParser as Parse import qualified Language.Haskell.GHC.HappyParser as Parse
...@@ -74,12 +73,48 @@ data Located a = Located { ...@@ -74,12 +73,48 @@ data Located a = Located {
data Parser a = Parser (P a) data Parser a = Parser (P a)
-- Our parsers. -- Our parsers.
parserStatement = Parser Parse.fullStatement #if MIN_VERSION_ghc(8,4,0)
parserImport = Parser Parse.fullImport parserStatement :: Parser (Maybe (LStmt GhcPs (LHsExpr GhcPs)))
parserDeclaration = Parser Parse.fullDeclaration #else
parserExpression = Parser Parse.fullExpression parserStatement :: Parser (Maybe (LStmt RdrName (LHsExpr RdrName)))
parserTypeSignature = Parser Parse.fullTypeSignature #endif
parserModule = Parser Parse.fullModule parserStatement = Parser Parse.fullStatement
#if MIN_VERSION_ghc(8,4,0)
parserImport :: Parser (LImportDecl GhcPs)
#else
parserImport :: Parser (LImportDecl RdrName)
#endif
parserImport = Parser Parse.fullImport
#if MIN_VERSION_ghc(8,4,0)
parserDeclaration :: Parser (OrdList (LHsDecl GhcPs))
#else
parserDeclaration :: Parser (OrdList (LHsDecl RdrName))
#endif
parserDeclaration = Parser Parse.fullDeclaration
#if MIN_VERSION_ghc(8,4,0)
parserExpression :: Parser (LHsExpr GhcPs)
#else
parserExpression :: Parser (LHsExpr RdrName)
#endif
parserExpression = Parser Parse.fullExpression
#if MIN_VERSION_ghc(8,4,0)
parserTypeSignature :: Parser (SrcLoc.Located (OrdList (LHsDecl GhcPs)))
#else
parserTypeSignature :: Parser (SrcLoc.Located (OrdList (LHsDecl RdrName)))
#endif
parserTypeSignature = Parser Parse.fullTypeSignature
#if MIN_VERSION_ghc(8,4,0)
parserModule :: Parser (SrcLoc.Located (HsModule GhcPs))
#else
parserModule :: Parser (SrcLoc.Located (HsModule RdrName))
#endif
parserModule = Parser Parse.fullModule
-- | Run a GHC parser on a string. Return success or failure with -- | Run a GHC parser on a string. Return success or failure with
-- associated information for both. -- associated information for both.
...@@ -87,7 +122,7 @@ runParser :: DynFlags -> Parser a -> String -> ParseOutput a ...@@ -87,7 +122,7 @@ runParser :: DynFlags -> Parser a -> String -> ParseOutput a
runParser flags (Parser parser) str = runParser flags (Parser parser) str =
-- Create an initial parser state. -- Create an initial parser state.
let filename = "<interactive>" let filename = "<interactive>"
location = mkRealSrcLoc (mkFastString filename) 1 1 location = SrcLoc.mkRealSrcLoc (mkFastString filename) 1 1
buffer = stringToStringBuffer str buffer = stringToStringBuffer str
parseState = mkPState flags buffer location in parseState = mkPState flags buffer location in
-- Convert a GHC parser output into our own. -- Convert a GHC parser output into our own.
...@@ -95,48 +130,29 @@ runParser flags (Parser parser) str = ...@@ -95,48 +130,29 @@ runParser flags (Parser parser) str =
where where
toParseOut :: ParseResult a -> ParseOutput a toParseOut :: ParseResult a -> ParseOutput a
#if MIN_VERSION_ghc(8,4,0) #if MIN_VERSION_ghc(8,4,0)
toParseOut (PFailed _ span@(RealSrcSpan realSpan) err) = toParseOut (PFailed _ spn@(RealSrcSpan realSpan) err) =
#else #else
toParseOut (PFailed span@(RealSrcSpan realSpan) err) = toParseOut (PFailed spn@(RealSrcSpan realSpan) err) =
#endif #endif
let errMsg = printErrorBag $ unitBag $ mkPlainErrMsg flags span err let errMsg = printErrorBag $ unitBag $ mkPlainErrMsg flags spn err
line = srcLocLine $ realSrcSpanStart realSpan ln = srcLocLine $ SrcLoc.realSrcSpanStart realSpan
col = srcLocCol $ realSrcSpanStart realSpan col = srcLocCol $ SrcLoc.realSrcSpanStart realSpan
in Failure errMsg $ Loc line col in Failure errMsg $ Loc ln col
#if MIN_VERSION_ghc(8,4,0) #if MIN_VERSION_ghc(8,4,0)
toParseOut (PFailed _ span err) = toParseOut (PFailed _ spn err) =
#else #else
toParseOut (PFailed span err) = toParseOut (PFailed spn err) =
#endif #endif
let errMsg = printErrorBag $ unitBag $ mkPlainErrMsg flags span err let errMsg = printErrorBag $ unitBag $ mkPlainErrMsg flags spn err
in Failure errMsg $ Loc 0 0 in Failure errMsg $ Loc 0 0
toParseOut (POk parseState result) = toParseOut (POk _parseState result) =
let parseEnd = realSrcSpanStart $ last_loc parseState Parsed result
endLine = srcLocLine parseEnd
endCol = srcLocCol parseEnd
(before, after) = splitAtLoc endLine endCol str
in Parsed result
-- Convert the bag of errors into an error string. -- Convert the bag of errors into an error string.
printErrorBag bag = joinLines . map show $ bagToList bag printErrorBag bag = joinLines . map show $ bagToList bag
-- | Split a string at a given line and column. The column is included in
-- the second part of the split.
splitAtLoc :: LineNumber -> ColumnNumber -> String -> (String, String)
splitAtLoc line col string =
if line > length (lines string)
then (string, "")
else (before, after)
where
(beforeLines, afterLines) = splitAt line $ lines string
theLine = last beforeLines
(beforeChars, afterChars) = splitAt (col - 1) theLine
before = joinLines (init beforeLines) ++ '\n' : beforeChars
after = joinLines $ afterChars : afterLines
-- Not the same as 'unlines', due to trailing \n -- Not the same as 'unlines', due to trailing \n
joinLines :: [String] -> String joinLines :: [String] -> String
joinLines = intercalate "\n" joinLines = intercalate "\n"
...@@ -151,7 +167,7 @@ layoutChunks :: String -> [Located String] ...@@ -151,7 +167,7 @@ layoutChunks :: String -> [Located String]
layoutChunks = joinQuasiquotes . go 1 layoutChunks = joinQuasiquotes . go 1
where where
go :: LineNumber -> String -> [Located String] go :: LineNumber -> String -> [Located String]
go line = filter (not . null . unloc) . map (fmap strip) . layoutLines line . lines go ln = filter (not . null . unloc) . map (fmap strip) . layoutLines ln . lines
-- drop spaces on left and right -- drop spaces on left and right
strip = dropRight . dropLeft strip = dropRight . dropLeft
...@@ -165,13 +181,13 @@ layoutChunks = joinQuasiquotes . go 1 ...@@ -165,13 +181,13 @@ layoutChunks = joinQuasiquotes . go 1
layoutLines _ [] = [] layoutLines _ [] = []
-- Use the indent of the first line to find the end of the first block. -- Use the indent of the first line to find the end of the first block.
layoutLines lineIdx all@(firstLine:rest) = layoutLines lineIdx xs@(firstLine:rest) =
let firstIndent = indentLevel firstLine let firstIndent = indentLevel firstLine
blockEnded line = indentLevel line <= firstIndent in blockEnded ln = indentLevel ln <= firstIndent in
case findIndex blockEnded rest of case findIndex blockEnded rest of
-- If the first block doesn't end, return the whole string, since -- If the first block doesn't end, return the whole string, since
-- that just means the block takes up the entire string. -- that just means the block takes up the entire string.
Nothing -> [Located lineIdx $ intercalate "\n" all] Nothing -> [Located lineIdx $ intercalate "\n" xs]
-- We found the end of the block. Split this bit out and recurse. -- We found the end of the block. Split this bit out and recurse.
Just idx -> Just idx ->
...@@ -213,6 +229,7 @@ removeComments = removeOneLineComments . removeMultilineComments 0 0 ...@@ -213,6 +229,7 @@ removeComments = removeOneLineComments . removeMultilineComments 0 0
where where
dropLine = removeOneLineComments . dropWhile (/= '\n') dropLine = removeOneLineComments . dropWhile (/= '\n')
removeMultilineComments :: Int -> Int -> String -> String
removeMultilineComments nesting pragmaNesting str = removeMultilineComments nesting pragmaNesting str =
case str of case str of
-- Don't remove comments after cmd directives -- Don't remove comments after cmd directives
...@@ -253,8 +270,8 @@ removeComments = removeOneLineComments . removeMultilineComments 0 0 ...@@ -253,8 +270,8 @@ removeComments = removeOneLineComments . removeMultilineComments 0 0
-- Take a part of a string that ends in an unescaped quote. -- Take a part of a string that ends in an unescaped quote.
takeString str = case str of takeString str = case str of
escaped@('\\':'"':rest) -> escaped escaped@('\\':'"':_) -> escaped
'"':rest -> "\"" '"':_ -> "\""
x:xs -> x:takeString xs x:xs -> x:takeString xs
[] -> [] [] -> []
......
...@@ -18,6 +18,7 @@ cabal-version: >=1.16 ...@@ -18,6 +18,7 @@ cabal-version: >=1.16
library library
build-tools: happy, cpphs build-tools: happy, cpphs
ghc-options: -Wall
exposed-modules: Language.Haskell.GHC.Parser, exposed-modules: Language.Haskell.GHC.Parser,
Language.Haskell.GHC.HappyParser Language.Haskell.GHC.HappyParser
-- other-modules: -- other-modules:
......
...@@ -17,7 +17,6 @@ import HsSyn ...@@ -17,7 +17,6 @@ import HsSyn
import OrdList import OrdList
-- compiler/parser -- compiler/parser
import RdrHsSyn
import Lexer import Lexer
-- compiler/basicTypes -- compiler/basicTypes
......
...@@ -17,12 +17,8 @@ import HsSyn ...@@ -17,12 +17,8 @@ import HsSyn
import OrdList import OrdList
-- compiler/parser -- compiler/parser
import RdrHsSyn
import Lexer import Lexer
-- compiler/basicTypes
import RdrName
fullStatement :: P (Maybe (LStmt GhcPs (LHsExpr GhcPs))) fullStatement :: P (Maybe (LStmt GhcPs (LHsExpr GhcPs)))
fullStatement = parseStmt fullStatement = parseStmt
......
name: ipython-kernel name: ipython-kernel
version: 0.9.1.0 version: 0.10.0.0
synopsis: A library for creating kernels for IPython frontends synopsis: A library for creating kernels for IPython frontends
description: ipython-kernel is a library for communicating with frontends for the interactive IPython framework. It is used extensively in IHaskell, the interactive Haskell environment. description: ipython-kernel is a library for communicating with frontends for the interactive IPython framework. It is used extensively in IHaskell, the interactive Haskell environment.
...@@ -24,10 +24,11 @@ flag examples ...@@ -24,10 +24,11 @@ flag examples
library library
ghc-options: -Wall
exposed-modules: IHaskell.IPython.Kernel exposed-modules: IHaskell.IPython.Kernel
IHaskell.IPython.Types IHaskell.IPython.Types
IHaskell.IPython.ZeroMQ IHaskell.IPython.ZeroMQ
IHaskell.IPython.Message.Writer
IHaskell.IPython.Message.Parser IHaskell.IPython.Message.Parser
IHaskell.IPython.Message.UUID IHaskell.IPython.Message.UUID
IHaskell.IPython.EasyKernel IHaskell.IPython.EasyKernel
...@@ -38,6 +39,7 @@ library ...@@ -38,6 +39,7 @@ library
aeson , aeson ,
bytestring , bytestring ,
cereal , cereal ,
cereal-text ,
containers , containers ,
cryptonite , cryptonite ,
directory , directory ,
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
-- logos, help text, and so forth. -- logos, help text, and so forth.
module IHaskell.IPython.EasyKernel (easyKernel, installKernelspec, KernelConfig(..)) where module IHaskell.IPython.EasyKernel (easyKernel, installKernelspec, KernelConfig(..)) where
import Data.Aeson (decode, encode) import Data.Aeson (decode, encode, toJSON)
import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString.Lazy as BL
...@@ -32,7 +32,7 @@ import System.Process (rawSystem) ...@@ -32,7 +32,7 @@ import System.Process (rawSystem)
import Control.Concurrent (MVar, readChan, writeChan, newMVar, readMVar, modifyMVar_) import Control.Concurrent (MVar, readChan, writeChan, newMVar, readMVar, modifyMVar_)
import Control.Monad.IO.Class (MonadIO(..)) import Control.Monad.IO.Class (MonadIO(..))
import Control.Monad (forever, when, unless, void) import Control.Monad (forever, when, void)
import qualified Data.Map as Map import qualified Data.Map as Map
import Data.Maybe (fromMaybe) import Data.Maybe (fromMaybe)
...@@ -40,10 +40,8 @@ import qualified Data.Text as T ...@@ -40,10 +40,8 @@ import qualified Data.Text as T
import IHaskell.IPython.Kernel import IHaskell.IPython.Kernel
import IHaskell.IPython.Message.UUID as UUID import IHaskell.IPython.Message.UUID as UUID
import IHaskell.IPython.Types
import System.Directory (createDirectoryIfMissing, doesDirectoryExist, doesFileExist, import System.Directory (createDirectoryIfMissing, getTemporaryDirectory)
getHomeDirectory, getTemporaryDirectory)
import System.FilePath ((</>)) import System.FilePath ((</>))
import System.Exit (exitSuccess) import System.Exit (exitSuccess)
import System.IO (openFile, IOMode(ReadMode)) import System.IO (openFile, IOMode(ReadMode))
...@@ -53,7 +51,7 @@ import System.IO (openFile, IOMode(ReadMode)) ...@@ -53,7 +51,7 @@ import System.IO (openFile, IOMode(ReadMode))
-- running cells, and the type of final results of cells, respectively. -- running cells, and the type of final results of cells, respectively.
data KernelConfig m output result = data KernelConfig m output result =
KernelConfig KernelConfig
{ {
-- | Info on the language of the kernel. -- | Info on the language of the kernel.
kernelLanguageInfo :: LanguageInfo kernelLanguageInfo :: LanguageInfo
-- | Write all the files into the kernel directory, including `kernel.js`, `logo-64x64.svg`, and any -- | Write all the files into the kernel directory, including `kernel.js`, `logo-64x64.svg`, and any
...@@ -122,19 +120,12 @@ createReplyHeader :: MonadIO m => MessageHeader -> m MessageHeader ...@@ -122,19 +120,12 @@ createReplyHeader :: MonadIO m => MessageHeader -> m MessageHeader
createReplyHeader parent = do createReplyHeader parent = do
-- Generate a new message UUID. -- Generate a new message UUID.
newMessageId <- liftIO UUID.random newMessageId <- liftIO UUID.random
let repType = fromMaybe err (replyType $ msgType parent) let repType = fromMaybe err (replyType $ mhMsgType parent)
err = error $ "No reply for message " ++ show (msgType parent) err = error $ "No reply for message " ++ show (mhMsgType parent)
return $ MessageHeader (mhIdentifiers parent) (Just parent) (Map.fromList [])
newMessageId (mhSessionId parent) (mhUsername parent) repType
return
MessageHeader
{ identifiers = identifiers parent
, parentHeader = Just parent
, metadata = Map.fromList []
, messageId = newMessageId
, sessionId = sessionId parent
, username = username parent
, msgType = repType
}
-- | Execute an IPython kernel for a config. Your 'main' action should call this as the last thing -- | Execute an IPython kernel for a config. Your 'main' action should call this as the last thing
-- it does. -- it does.
...@@ -145,16 +136,14 @@ easyKernel :: MonadIO m ...@@ -145,16 +136,14 @@ easyKernel :: MonadIO m
-> m () -> m ()
easyKernel profileFile config = do easyKernel profileFile config = do
prof <- liftIO $ getProfile profileFile prof <- liftIO $ getProfile profileFile
zmq@(Channels shellReqChan shellRepChan ctrlReqChan ctrlRepChan iopubChan _) <- liftIO $ serveProfile zmq <- liftIO $ serveProfile prof False
prof
False
execCount <- liftIO $ newMVar 0 execCount <- liftIO $ newMVar 0
forever $ do forever $ do
req <- liftIO $ readChan shellReqChan req <- liftIO $ readChan (shellRequestChannel zmq)
repHeader <- createReplyHeader (header req) repHeader <- createReplyHeader (header req)
when (debug config) . liftIO $ print req when (debug config) . liftIO $ print req
reply <- replyTo config execCount zmq req repHeader reply <- replyTo config execCount zmq req repHeader
liftIO $ writeChan shellRepChan reply liftIO $ writeChan (shellRequestChannel zmq) reply
replyTo :: MonadIO m replyTo :: MonadIO m
=> KernelConfig m output result => KernelConfig m output result
...@@ -180,17 +169,17 @@ replyTo config _ interface KernelInfoRequest{} replyHeader = do ...@@ -180,17 +169,17 @@ replyTo config _ interface KernelInfoRequest{} replyHeader = do
, status = Ok , status = Ok
} }
replyTo config _ _ CommInfoRequest{} replyHeader = replyTo _ _ _ CommInfoRequest{} replyHeader =
return return
CommInfoReply CommInfoReply
{ header = replyHeader { header = replyHeader
, commInfo = Map.empty } , commInfo = Map.empty }
replyTo config _ interface ShutdownRequest { restartPending = pending } replyHeader = do replyTo _ _ interface ShutdownRequest { restartPending = pending } replyHeader = do
liftIO $ writeChan (shellReplyChannel interface) $ ShutdownReply replyHeader pending liftIO $ writeChan (shellReplyChannel interface) $ ShutdownReply replyHeader pending
liftIO exitSuccess liftIO exitSuccess
replyTo config execCount interface req@ExecuteRequest { getCode = code } replyHeader = do replyTo config execCount interface req@ExecuteRequest{} replyHeader = do
let send = writeChan (iopubChannel interface) let send = writeChan (iopubChannel interface)
busyHeader <- dupHeader replyHeader StatusMessage busyHeader <- dupHeader replyHeader StatusMessage
...@@ -205,7 +194,7 @@ replyTo config execCount interface req@ExecuteRequest { getCode = code } replyHe ...@@ -205,7 +194,7 @@ replyTo config execCount interface req@ExecuteRequest { getCode = code } replyHe
send $ PublishDisplayData send $ PublishDisplayData
outputHeader outputHeader
(displayOutput config x) (displayOutput config x)
in run config code clearOutput sendOutput in run config (getCode req) clearOutput sendOutput
liftIO . send $ PublishDisplayData outputHeader (displayResult config res) liftIO . send $ PublishDisplayData outputHeader (displayResult config res)
...@@ -254,4 +243,4 @@ dupHeader :: MonadIO m => MessageHeader -> MessageType -> m MessageHeader ...@@ -254,4 +243,4 @@ dupHeader :: MonadIO m => MessageHeader -> MessageType -> m MessageHeader
dupHeader hdr mtype = dupHeader hdr mtype =
do do
uuid <- liftIO UUID.random uuid <- liftIO UUID.random
return hdr { messageId = uuid, msgType = mtype } return hdr { mhMessageId = uuid, mhMsgType = mtype }
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
module IHaskell.IPython.Kernel (module X) where module IHaskell.IPython.Kernel (module X) where
import IHaskell.IPython.Types as X import IHaskell.IPython.Types as X
import IHaskell.IPython.Message.Writer as X
import IHaskell.IPython.Message.Parser as X import IHaskell.IPython.Message.Parser as X
import IHaskell.IPython.Message.UUID as X import IHaskell.IPython.Message.UUID as X
import IHaskell.IPython.ZeroMQ as X import IHaskell.IPython.ZeroMQ as X
...@@ -8,15 +8,14 @@ ...@@ -8,15 +8,14 @@
-- the low-level 0MQ interface. -- the low-level 0MQ interface.
module IHaskell.IPython.Message.Parser (parseMessage) where module IHaskell.IPython.Message.Parser (parseMessage) where
import Control.Applicative ((<|>), (<$>), (<*>)) import Control.Applicative ((<$>), (<*>))
import Data.Aeson ((.:), (.:?), (.!=), decode, Result(..), Object, Value(..)) import Data.Aeson ((.:), (.:?), (.!=), decode, FromJSON, Result(..), Object, Value(..))
import Data.Aeson.Types (parse, parseEither) import Data.Aeson.Types (Parser, parse, parseEither)
import Data.ByteString hiding (unpack) import Data.ByteString hiding (unpack)
import qualified Data.ByteString.Lazy as Lazy import qualified Data.ByteString.Lazy as Lazy
import Data.HashMap.Strict as HM import Data.HashMap.Strict as HM
import Data.Map (Map) import Data.Map (Map)
import Data.Maybe (fromMaybe) import Data.Maybe (fromMaybe)
import Data.Text (Text)
import Data.Text (Text, unpack) import Data.Text (Text, unpack)
import Debug.Trace import Debug.Trace
import IHaskell.IPython.Types import IHaskell.IPython.Types
...@@ -32,7 +31,7 @@ parseMessage :: [ByteString] -- ^ The list of identifiers sent with the message. ...@@ -32,7 +31,7 @@ parseMessage :: [ByteString] -- ^ The list of identifiers sent with the message.
-> Message -- ^ A parsed message. -> Message -- ^ A parsed message.
parseMessage idents headerData parentHeader metadata content = parseMessage idents headerData parentHeader metadata content =
let header = parseHeader idents headerData parentHeader metadata let header = parseHeader idents headerData parentHeader metadata
messageType = msgType header messageType = mhMsgType header
messageWithoutHeader = parser messageType $ Lazy.fromStrict content messageWithoutHeader = parser messageType $ Lazy.fromStrict content
in messageWithoutHeader { header = header } in messageWithoutHeader { header = header }
...@@ -43,15 +42,7 @@ parseHeader :: [ByteString] -- ^ The list of identifiers. ...@@ -43,15 +42,7 @@ parseHeader :: [ByteString] -- ^ The list of identifiers.
-> ByteString -- ^ The metadata, or "{}" for an empty map. -> ByteString -- ^ The metadata, or "{}" for an empty map.
-> MessageHeader -- The resulting message header. -> MessageHeader -- The resulting message header.
parseHeader idents headerData parentHeader metadata = parseHeader idents headerData parentHeader metadata =
MessageHeader MessageHeader idents parentResult metadataMap messageUUID sessionUUID username messageType
{ identifiers = idents
, parentHeader = parentResult
, metadata = metadataMap
, messageId = messageUUID
, sessionId = sessionUUID
, username = username
, msgType = messageType
}
where where
-- Decode the header data and the parent header data into JSON objects. If the parent header data is -- Decode the header data and the parent header data into JSON objects. If the parent header data is
-- absent, just have Nothing instead. -- absent, just have Nothing instead.
...@@ -180,6 +171,7 @@ displayDataParser = requestParser $ \obj -> do ...@@ -180,6 +171,7 @@ displayDataParser = requestParser $ \obj -> do
let displayDatas = makeDisplayDatas dataDict let displayDatas = makeDisplayDatas dataDict
return $ PublishDisplayData noHeader displayDatas return $ PublishDisplayData noHeader displayDatas
requestParser :: FromJSON a => (a -> Parser Message) -> LByteString -> Message
requestParser parser content = requestParser parser content =
case parseEither parser decoded of case parseEither parser decoded of
Right parsed -> parsed Right parsed -> parsed
...@@ -218,6 +210,7 @@ inputMessageParser = requestParser $ \obj -> do ...@@ -218,6 +210,7 @@ inputMessageParser = requestParser $ \obj -> do
executionCount <- obj .: "execution_count" executionCount <- obj .: "execution_count"
return $ Input noHeader code executionCount return $ Input noHeader code executionCount
getDisplayDatas :: Maybe Object -> [DisplayData]
getDisplayDatas Nothing = [] getDisplayDatas Nothing = []
getDisplayDatas (Just dataDict) = makeDisplayDatas dataDict getDisplayDatas (Just dataDict) = makeDisplayDatas dataDict
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
-- Generate, parse, and pretty print UUIDs for use with IPython. -- Generate, parse, and pretty print UUIDs for use with IPython.
module IHaskell.IPython.Message.UUID (UUID, random, randoms, uuidToString) where module IHaskell.IPython.Message.UUID (UUID, random, randoms, uuidToString) where
import Control.Applicative ((<$>), (<*>)) import Control.Applicative ((<$>))
import Control.Monad (mzero, replicateM) import Control.Monad (mzero, replicateM)
import Data.Aeson import Data.Aeson
import Data.Text (pack) import Data.Text (pack)
......
{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_GHC -fno-warn-unused-binds -fno-warn-name-shadowing -fno-warn-unused-matches #-}
-- | Description : @ToJSON@ for Messages
--
-- This module contains the @ToJSON@ instance for @Message@.
module IHaskell.IPython.Message.Writer (ToJSON(..)) where
import Data.Aeson
import Data.Aeson.Types (Pair)
import Data.Aeson.Parser (json)
import Data.Map (Map)
import Data.Monoid (mempty)
import Data.Text (Text, pack)
import Data.Text.Encoding (encodeUtf8)
import qualified Data.Map as Map
import IHaskell.IPython.Types
import Data.Maybe (fromMaybe)
instance ToJSON LanguageInfo where
toJSON info = object
[ "name" .= languageName info
, "version" .= languageVersion info
, "file_extension" .= languageFileExtension info
, "codemirror_mode" .= languageCodeMirrorMode info
, "pygments_lexer" .= languagePygmentsLexer info
]
-- Convert message bodies into JSON.
instance ToJSON Message where
toJSON rep@KernelInfoReply{} =
object
[ "protocol_version" .= protocolVersion rep
, "banner" .= banner rep
, "implementation" .= implementation rep
, "implementation_version" .= implementationVersion rep
, "language_info" .= languageInfo rep
, "status" .= show (status rep)
]
toJSON CommInfoReply
{ header = header
, commInfo = commInfo
} =
object
[ "comms" .= Map.map (\comm -> object ["target_name" .= comm]) commInfo
, "status" .= string "ok"
]
toJSON ExecuteRequest
{ getCode = code
, getSilent = silent
, getStoreHistory = storeHistory
, getAllowStdin = allowStdin
, getUserExpressions = userExpressions
} =
object
[ "code" .= code
, "silent" .= silent
, "store_history" .= storeHistory
, "allow_stdin" .= allowStdin
, "user_expressions" .= userExpressions
]
toJSON ExecuteReply { status = status, executionCounter = counter, pagerOutput = pager } =
object
[ "status" .= show status
, "execution_count" .= counter
, "payload" .=
if null pager
then []
else mkPayload pager
, "user_expressions" .= emptyMap
]
where
mkPayload o = [ object
[ "source" .= string "page"
, "start" .= Number 0
, "data" .= object (map displayDataToJson o)
]
]
toJSON PublishStatus { executionState = executionState } =
object ["execution_state" .= executionState]
toJSON PublishStream { streamType = streamType, streamContent = content } =
object ["data" .= content, "name" .= streamType]
toJSON PublishDisplayData { displayData = datas } =
object
["metadata" .= object [], "data" .= object (map displayDataToJson datas)]
toJSON PublishOutput { executionCount = execCount, reprText = reprText } =
object
[ "data" .= object ["text/plain" .= reprText]
, "execution_count" .= execCount
, "metadata" .= object []
]
toJSON PublishInput { executionCount = execCount, inCode = code } =
object ["execution_count" .= execCount, "code" .= code]
toJSON (CompleteReply _ matches start end metadata status) =
object
[ "matches" .= matches
, "cursor_start" .= start
, "cursor_end" .= end
, "metadata" .= metadata
, "status" .= if status
then string "ok"
else "error"
]
toJSON i@InspectReply{} =
object
[ "status" .= if inspectStatus i
then string "ok"
else "error"
, "data" .= object (map displayDataToJson . inspectData $ i)
, "metadata" .= object []
, "found" .= inspectStatus i
]
toJSON ShutdownReply { restartPending = restart } =
object ["restart" .= restart
, "status" .= string "ok"
]
toJSON ClearOutput { wait = wait } =
object ["wait" .= wait]
toJSON RequestInput { inputPrompt = prompt } =
object ["prompt" .= prompt]
toJSON req@CommOpen{} =
object
[ "comm_id" .= commUuid req
, "target_name" .= commTargetName req
, "target_module" .= commTargetModule req
, "data" .= commData req
]
toJSON req@CommData{} =
object ["comm_id" .= commUuid req, "data" .= commData req]
toJSON req@CommClose{} =
object ["comm_id" .= commUuid req, "data" .= commData req]
toJSON req@HistoryReply{} =
object ["history" .= map tuplify (historyReply req)
, "status" .= string "ok"
]
where
tuplify (HistoryReplyElement sess linum res) = (sess, linum, case res of
Left inp -> toJSON inp
Right (inp, out) -> toJSON out)
toJSON req@IsCompleteReply{} =
object pairs
where
pairs =
case reviewResult req of
CodeComplete -> status "complete"
CodeIncomplete ind -> status "incomplete" ++ indent ind
CodeInvalid -> status "invalid"
CodeUnknown -> status "unknown"
status x = ["status" .= pack x]
indent x = ["indent" .= pack x]
toJSON body = error $ "Do not know how to convert to JSON for message " ++ show body
-- | Print an execution state as "busy", "idle", or "starting".
instance ToJSON ExecutionState where
toJSON Busy = String "busy"
toJSON Idle = String "idle"
toJSON Starting = String "starting"
-- | Print a stream as "stdin" or "stdout" strings.
instance ToJSON StreamType where
toJSON Stdin = String "stdin"
toJSON Stdout = String "stdout"
-- | Convert a MIME type and value into a JSON dictionary pair.
displayDataToJson :: DisplayData -> (Text, Value)
displayDataToJson (DisplayData MimeJson dataStr) =
pack (show MimeJson) .= fromMaybe (String "") (decodeStrict (encodeUtf8 dataStr) :: Maybe Value)
displayDataToJson (DisplayData MimeVegalite dataStr) =
pack (show MimeVegalite) .= fromMaybe (String "") (decodeStrict (encodeUtf8 dataStr) :: Maybe Value)
displayDataToJson (DisplayData MimeVega dataStr) =
pack (show MimeVega) .= fromMaybe (String "") (decodeStrict (encodeUtf8 dataStr) :: Maybe Value)
displayDataToJson (DisplayData mimeType dataStr) =
pack (show mimeType) .= String dataStr
----- Constants -----
emptyMap :: Map String String
emptyMap = mempty
emptyList :: [Int]
emptyList = []
ints :: [Int] -> [Int]
ints = id
string :: String -> String
string = id
...@@ -6,8 +6,8 @@ module IHaskell.IPython.Types ( ...@@ -6,8 +6,8 @@ module IHaskell.IPython.Types (
-- * IPython kernel profile -- * IPython kernel profile
Profile(..), Profile(..),
Transport(..), Transport(..),
Port(..), Port,
IP(..), IP,
-- * IPython kernelspecs -- * IPython kernelspecs
KernelSpec(..), KernelSpec(..),
...@@ -15,12 +15,12 @@ module IHaskell.IPython.Types ( ...@@ -15,12 +15,12 @@ module IHaskell.IPython.Types (
-- * IPython messaging protocol -- * IPython messaging protocol
Message(..), Message(..),
MessageHeader(..), MessageHeader(..),
Username(..), Username,
Metadata(..), Metadata,
MessageType(..), MessageType(..),
CodeReview(..), CodeReview(..),
Width(..), Width,
Height(..), Height,
StreamType(..), StreamType(..),
ExecutionState(..), ExecutionState(..),
ExecuteReplyStatus(..), ExecuteReplyStatus(..),
...@@ -38,11 +38,15 @@ module IHaskell.IPython.Types ( ...@@ -38,11 +38,15 @@ module IHaskell.IPython.Types (
import Control.Applicative ((<$>), (<*>)) import Control.Applicative ((<$>), (<*>))
import Data.Aeson import Data.Aeson
import Data.Aeson.Types (typeMismatch)
import Data.ByteString (ByteString) import Data.ByteString (ByteString)
import Data.List (find) import Data.List (find)
import Data.Map (Map) import Data.Map (Map)
import qualified Data.Map as Map
import Data.Maybe (fromMaybe)
import Data.Serialize import Data.Serialize
import Data.Text (Text) import Data.Serialize.Text ()
import Data.Text (Text, pack)
import qualified Data.Text as Text import qualified Data.Text as Text
import qualified Data.Text.Encoding as Text import qualified Data.Text.Encoding as Text
import Data.Typeable import Data.Typeable
...@@ -117,7 +121,7 @@ instance ToJSON Transport where ...@@ -117,7 +121,7 @@ instance ToJSON Transport where
-------------------- IPython Kernelspec Types ---------------------- -------------------- IPython Kernelspec Types ----------------------
data KernelSpec = data KernelSpec =
KernelSpec KernelSpec
{ {
-- | Name shown to users to describe this kernel (e.g. "Haskell") -- | Name shown to users to describe this kernel (e.g. "Haskell")
kernelDisplayName :: String kernelDisplayName :: String
-- | Name for the kernel; unique kernel identifier (e.g. "haskell") -- | Name for the kernel; unique kernel identifier (e.g. "haskell")
...@@ -140,13 +144,13 @@ instance ToJSON KernelSpec where ...@@ -140,13 +144,13 @@ instance ToJSON KernelSpec where
-- | A message header with some metadata. -- | A message header with some metadata.
data MessageHeader = data MessageHeader =
MessageHeader MessageHeader
{ identifiers :: [ByteString] -- ^ The identifiers sent with the message. { mhIdentifiers :: [ByteString] -- ^ The identifiers sent with the message.
, parentHeader :: Maybe MessageHeader -- ^ The parent header, if present. , mhParentHeader :: Maybe MessageHeader -- ^ The parent header, if present.
, metadata :: Metadata -- ^ A dict of metadata. , mhMetadata :: Metadata -- ^ A dict of metadata.
, messageId :: UUID -- ^ A unique message UUID. , mhMessageId :: UUID -- ^ A unique message UUID.
, sessionId :: UUID -- ^ A unique session UUID. , mhSessionId :: UUID -- ^ A unique session UUID.
, username :: Username -- ^ The user who sent this message. , mhUsername :: Username -- ^ The user who sent this message.
, msgType :: MessageType -- ^ The message type. , mhMsgType :: MessageType -- ^ The message type.
} }
deriving (Show, Read) deriving (Show, Read)
...@@ -154,11 +158,11 @@ data MessageHeader = ...@@ -154,11 +158,11 @@ data MessageHeader =
-- all the record fields. -- all the record fields.
instance ToJSON MessageHeader where instance ToJSON MessageHeader where
toJSON header = object toJSON header = object
[ "msg_id" .= messageId header [ "msg_id" .= mhMessageId header
, "session" .= sessionId header , "session" .= mhSessionId header
, "username" .= username header , "username" .= mhUsername header
, "version" .= ("5.0" :: String) , "version" .= ("5.0" :: String)
, "msg_type" .= showMessageType (msgType header) , "msg_type" .= showMessageType (mhMsgType header)
] ]
-- | A username for the source of a message. -- | A username for the source of a message.
...@@ -280,6 +284,15 @@ data LanguageInfo = ...@@ -280,6 +284,15 @@ data LanguageInfo =
} }
deriving (Show, Eq) deriving (Show, Eq)
instance ToJSON LanguageInfo where
toJSON info = object
[ "name" .= languageName info
, "version" .= languageVersion info
, "file_extension" .= languageFileExtension info
, "codemirror_mode" .= languageCodeMirrorMode info
, "pygments_lexer" .= languagePygmentsLexer info
]
data CodeReview = CodeComplete data CodeReview = CodeComplete
| CodeIncomplete String -- ^ String to be used to indent next line of input | CodeIncomplete String -- ^ String to be used to indent next line of input
| CodeInvalid | CodeInvalid
...@@ -472,6 +485,146 @@ data Message = ...@@ -472,6 +485,146 @@ data Message =
| SendNothing -- Dummy message; nothing is sent. | SendNothing -- Dummy message; nothing is sent.
deriving Show deriving Show
-- Convert message bodies into JSON.
instance ToJSON Message where
toJSON rep@KernelInfoReply{} =
object
[ "protocol_version" .= protocolVersion rep
, "banner" .= banner rep
, "implementation" .= implementation rep
, "implementation_version" .= implementationVersion rep
, "language_info" .= languageInfo rep
, "status" .= show (status rep)
]
toJSON CommInfoReply
{ header = header
, commInfo = commInfo
} =
object
[ "comms" .= Map.map (\comm -> object ["target_name" .= comm]) commInfo
, "status" .= string "ok"
]
toJSON ExecuteRequest
{ getCode = code
, getSilent = silent
, getStoreHistory = storeHistory
, getAllowStdin = allowStdin
, getUserExpressions = userExpressions
} =
object
[ "code" .= code
, "silent" .= silent
, "store_history" .= storeHistory
, "allow_stdin" .= allowStdin
, "user_expressions" .= userExpressions
]
toJSON ExecuteReply { status = status, executionCounter = counter, pagerOutput = pager } =
object
[ "status" .= show status
, "execution_count" .= counter
, "payload" .=
if null pager
then []
else mkPayload pager
, "user_expressions" .= emptyMap
]
where
mkPayload o = [ object
[ "source" .= string "page"
, "start" .= Number 0
, "data" .= object (map displayDataToJson o)
]
]
toJSON PublishStatus { executionState = executionState } =
object ["execution_state" .= executionState]
toJSON PublishStream { streamType = streamType, streamContent = content } =
object ["data" .= content, "name" .= streamType]
toJSON PublishDisplayData { displayData = datas } =
object
["metadata" .= object [], "data" .= object (map displayDataToJson datas)]
toJSON PublishOutput { executionCount = execCount, reprText = reprText } =
object
[ "data" .= object ["text/plain" .= reprText]
, "execution_count" .= execCount
, "metadata" .= object []
]
toJSON PublishInput { executionCount = execCount, inCode = code } =
object ["execution_count" .= execCount, "code" .= code]
toJSON (CompleteReply _ matches start end metadata status) =
object
[ "matches" .= matches
, "cursor_start" .= start
, "cursor_end" .= end
, "metadata" .= metadata
, "status" .= if status
then string "ok"
else "error"
]
toJSON i@InspectReply{} =
object
[ "status" .= if inspectStatus i
then string "ok"
else "error"
, "data" .= object (map displayDataToJson . inspectData $ i)
, "metadata" .= object []
, "found" .= inspectStatus i
]
toJSON ShutdownReply { restartPending = restart } =
object ["restart" .= restart
, "status" .= string "ok"
]
toJSON ClearOutput { wait = wait } =
object ["wait" .= wait]
toJSON RequestInput { inputPrompt = prompt } =
object ["prompt" .= prompt]
toJSON req@CommOpen{} =
object
[ "comm_id" .= commUuid req
, "target_name" .= commTargetName req
, "target_module" .= commTargetModule req
, "data" .= commData req
]
toJSON req@CommData{} =
object ["comm_id" .= commUuid req, "data" .= commData req]
toJSON req@CommClose{} =
object ["comm_id" .= commUuid req, "data" .= commData req]
toJSON req@HistoryReply{} =
object ["history" .= map tuplify (historyReply req)
, "status" .= string "ok"
]
where
tuplify (HistoryReplyElement sess linum res) = (sess, linum, case res of
Left inp -> toJSON inp
Right (inp, out) -> toJSON out)
toJSON req@IsCompleteReply{} =
object pairs
where
pairs =
case reviewResult req of
CodeComplete -> status "complete"
CodeIncomplete ind -> status "incomplete" ++ indent ind
CodeInvalid -> status "invalid"
CodeUnknown -> status "unknown"
status x = ["status" .= pack x]
indent x = ["indent" .= pack x]
toJSON body = error $ "Do not know how to convert to JSON for message " ++ show body
-- | Ways in which the frontend can request history. TODO: Implement fields as described in -- | Ways in which the frontend can request history. TODO: Implement fields as described in
-- messaging spec. -- messaging spec.
data HistoryAccessType = HistoryRange data HistoryAccessType = HistoryRange
...@@ -497,6 +650,7 @@ instance FromJSON ExecuteReplyStatus where ...@@ -497,6 +650,7 @@ instance FromJSON ExecuteReplyStatus where
parseJSON (String "ok") = return Ok parseJSON (String "ok") = return Ok
parseJSON (String "error") = return Err parseJSON (String "error") = return Err
parseJSON (String "abort") = return Abort parseJSON (String "abort") = return Abort
parseJSON invalid = typeMismatch "ExecuteReplyStatus" invalid
instance Show ExecuteReplyStatus where instance Show ExecuteReplyStatus where
show Ok = "ok" show Ok = "ok"
...@@ -513,6 +667,13 @@ instance FromJSON ExecutionState where ...@@ -513,6 +667,13 @@ instance FromJSON ExecutionState where
parseJSON (String "busy") = return Busy parseJSON (String "busy") = return Busy
parseJSON (String "idle") = return Idle parseJSON (String "idle") = return Idle
parseJSON (String "starting") = return Starting parseJSON (String "starting") = return Starting
parseJSON invalid = typeMismatch "ExecutionState" invalid
-- | Print an execution state as "busy", "idle", or "starting".
instance ToJSON ExecutionState where
toJSON Busy = String "busy"
toJSON Idle = String "idle"
toJSON Starting = String "starting"
-- | Input and output streams. -- | Input and output streams.
data StreamType = Stdin data StreamType = Stdin
...@@ -524,6 +685,13 @@ instance FromJSON StreamType where ...@@ -524,6 +685,13 @@ instance FromJSON StreamType where
parseJSON (String "stdin") = return Stdin parseJSON (String "stdin") = return Stdin
parseJSON (String "stdout") = return Stdout parseJSON (String "stdout") = return Stdout
parseJSON (String "stderr") = return Stderr parseJSON (String "stderr") = return Stderr
parseJSON invalid = typeMismatch "StreamType" invalid
-- | Print a stream as "stdin" or "stdout" strings.
instance ToJSON StreamType where
toJSON Stdin = String "stdin"
toJSON Stdout = String "stdout"
toJSON Stderr = String "stderr"
-- | Get the reply message type for a request message type. -- | Get the reply message type for a request message type.
replyType :: MessageType -> Maybe MessageType replyType :: MessageType -> Maybe MessageType
...@@ -547,11 +715,6 @@ data DisplayData = DisplayData MimeType Text ...@@ -547,11 +715,6 @@ data DisplayData = DisplayData MimeType Text
instance Show DisplayData where instance Show DisplayData where
show _ = "DisplayData" show _ = "DisplayData"
-- Allow DisplayData serialization
instance Serialize Text where
put str = put (Text.encodeUtf8 str)
get = Text.decodeUtf8 <$> get
instance Serialize DisplayData instance Serialize DisplayData
instance Serialize MimeType instance Serialize MimeType
...@@ -583,6 +746,7 @@ extractPlain disps = ...@@ -583,6 +746,7 @@ extractPlain disps =
case find isPlain disps of case find isPlain disps of
Nothing -> "" Nothing -> ""
Just (DisplayData PlainText bytestr) -> Text.unpack bytestr Just (DisplayData PlainText bytestr) -> Text.unpack bytestr
Just _ -> ""
where where
isPlain (DisplayData mime _) = mime == PlainText isPlain (DisplayData mime _) = mime == PlainText
...@@ -617,3 +781,21 @@ instance Read MimeType where ...@@ -617,3 +781,21 @@ instance Read MimeType where
readsPrec _ "application/vnd.vega.v2+json" = [(MimeVega, "")] readsPrec _ "application/vnd.vega.v2+json" = [(MimeVega, "")]
readsPrec _ "application/vnd.vegalite.v1+json" = [(MimeVegalite, "")] readsPrec _ "application/vnd.vegalite.v1+json" = [(MimeVegalite, "")]
readsPrec _ "application/vdom.v1+json" = [(MimeVdom, "")] readsPrec _ "application/vdom.v1+json" = [(MimeVdom, "")]
readsPrec _ _ = []
-- | Convert a MIME type and value into a JSON dictionary pair.
displayDataToJson :: DisplayData -> (Text, Value)
displayDataToJson (DisplayData MimeJson dataStr) =
pack (show MimeJson) .= fromMaybe (String "") (decodeStrict (Text.encodeUtf8 dataStr) :: Maybe Value)
displayDataToJson (DisplayData MimeVegalite dataStr) =
pack (show MimeVegalite) .= fromMaybe (String "") (decodeStrict (Text.encodeUtf8 dataStr) :: Maybe Value)
displayDataToJson (DisplayData MimeVega dataStr) =
pack (show MimeVega) .= fromMaybe (String "") (decodeStrict (Text.encodeUtf8 dataStr) :: Maybe Value)
displayDataToJson (DisplayData mimeType dataStr) =
pack (show mimeType) .= String dataStr
string :: String -> String
string = id
emptyMap :: Map String String
emptyMap = mempty
...@@ -27,11 +27,10 @@ import qualified Data.ByteString.Lazy as LBS ...@@ -27,11 +27,10 @@ import qualified Data.ByteString.Lazy as LBS
import Data.Char import Data.Char
import Data.Monoid ((<>)) import Data.Monoid ((<>))
import qualified Data.Text.Encoding as Text import qualified Data.Text.Encoding as Text
import System.ZMQ4 as ZMQ4 hiding (stdin) import System.ZMQ4 as ZMQ4
import Text.Read (readMaybe) import Text.Read (readMaybe)
import IHaskell.IPython.Message.Parser import IHaskell.IPython.Message.Parser
import IHaskell.IPython.Message.Writer ()
import IHaskell.IPython.Types import IHaskell.IPython.Types
-- | The channel interface to the ZeroMQ sockets. All communication is done via Messages, which are -- | The channel interface to the ZeroMQ sockets. All communication is done via Messages, which are
...@@ -39,7 +38,7 @@ import IHaskell.IPython.Types ...@@ -39,7 +38,7 @@ import IHaskell.IPython.Types
-- should functionally serve as high-level sockets which speak Messages instead of ByteStrings. -- should functionally serve as high-level sockets which speak Messages instead of ByteStrings.
data ZeroMQInterface = data ZeroMQInterface =
Channels Channels
{ {
-- | A channel populated with requests from the frontend. -- | A channel populated with requests from the frontend.
shellRequestChannel :: Chan Message shellRequestChannel :: Chan Message
-- | Writing to this channel causes a reply to be sent to the frontend. -- | Writing to this channel causes a reply to be sent to the frontend.
...@@ -90,16 +89,16 @@ serveProfile profile debug = do ...@@ -90,16 +89,16 @@ serveProfile profile debug = do
-- Create the context in a separate thread that never finishes. If withContext or withSocket -- Create the context in a separate thread that never finishes. If withContext or withSocket
-- complete, the context or socket become invalid. -- complete, the context or socket become invalid.
forkIO $ withContext $ \context -> do _ <- forkIO $ withContext $ \ctxt -> do
-- Serve on all sockets. -- Serve on all sockets.
forkIO $ serveSocket context Rep (hbPort profile) $ heartbeat channels _ <- forkIO $ serveSocket ctxt Rep (hbPort profile) $ heartbeat channels
forkIO $ serveSocket context Router (controlPort profile) $ control debug channels _ <- forkIO $ serveSocket ctxt Router (controlPort profile) $ control debug channels
forkIO $ serveSocket context Router (shellPort profile) $ shell debug channels _ <- forkIO $ serveSocket ctxt Router (shellPort profile) $ shell debug channels
-- The context is reference counted in this thread only. Thus, the last serveSocket cannot be -- The ctxt is reference counted in this thread only. Thus, the last serveSocket cannot be
-- asynchronous, because otherwise context would be garbage collectable - since it would only be -- asynchronous, because otherwise ctxt would be garbage collectable - since it would only be
-- used in other threads. Thus, keep the last serveSocket in this thread. -- used in other threads. Thus, keep the last serveSocket in this thread.
serveSocket context Pub (iopubPort profile) $ iopub debug channels serveSocket ctxt Pub (iopubPort profile) $ iopub debug channels
return channels return channels
...@@ -132,9 +131,9 @@ parsePort s = readMaybe num ...@@ -132,9 +131,9 @@ parsePort s = readMaybe num
num = reverse (takeWhile isNumber (reverse s)) num = reverse (takeWhile isNumber (reverse s))
bindLocalEphemeralPort :: Socket a -> IO Int bindLocalEphemeralPort :: Socket a -> IO Int
bindLocalEphemeralPort socket = do bindLocalEphemeralPort sock = do
bind socket $ "tcp://127.0.0.1:*" bind sock $ "tcp://127.0.0.1:*"
endpointString <- lastEndpoint socket endpointString <- lastEndpoint sock
case parsePort endpointString of case parsePort endpointString of
Nothing -> Nothing ->
fail $ "internalError: IHaskell.IPython.ZeroMQ.bindLocalEphemeralPort encountered a port index that could not be interpreted as an int." fail $ "internalError: IHaskell.IPython.ZeroMQ.bindLocalEphemeralPort encountered a port index that could not be interpreted as an int."
...@@ -152,19 +151,19 @@ withEphemeralPorts :: ByteString -- ^ HMAC encryption key ...@@ -152,19 +151,19 @@ withEphemeralPorts :: ByteString -- ^ HMAC encryption key
withEphemeralPorts key debug callback = do withEphemeralPorts key debug callback = do
channels <- newZeroMQInterface key channels <- newZeroMQInterface key
-- Create the ZMQ4 context -- Create the ZMQ4 context
withContext $ \context -> do withContext $ \ctxt -> do
-- Create the sockets to communicate with. -- Create the sockets to communicate with.
withSocket context Rep $ \heartbeatSocket -> do withSocket ctxt Rep $ \heartbeatSocket -> do
withSocket context Router $ \controlportSocket -> do withSocket ctxt Router $ \controlportSocket -> do
withSocket context Router $ \shellportSocket -> do withSocket ctxt Router $ \shellportSocket -> do
withSocket context Pub $ \iopubSocket -> do withSocket ctxt Pub $ \iopubSocket -> do
-- Bind each socket to a local port, getting the port chosen. -- Bind each socket to a local port, getting the port chosen.
hbPort <- bindLocalEphemeralPort heartbeatSocket hbPt <- bindLocalEphemeralPort heartbeatSocket
controlPort <- bindLocalEphemeralPort controlportSocket controlPt <- bindLocalEphemeralPort controlportSocket
shellPort <- bindLocalEphemeralPort shellportSocket shellPt <- bindLocalEphemeralPort shellportSocket
iopubPort <- bindLocalEphemeralPort iopubSocket iopubPt <- bindLocalEphemeralPort iopubSocket
-- Create object to store ephemeral ports -- Create object to store ephemeral ports
let ports = ZeroMQEphemeralPorts { ephHbPort = hbPort, ephControlPort = controlPort, ephShellPort = shellPort, ephIOPubPort = iopubPort, ephSignatureKey = key } let ports = ZeroMQEphemeralPorts hbPt controlPt shellPt iopubPt key
-- Launch actions to listen to communicate between channels and cockets. -- Launch actions to listen to communicate between channels and cockets.
_ <- forkIO $ forever $ heartbeat channels heartbeatSocket _ <- forkIO $ forever $ heartbeat channels heartbeatSocket
_ <- forkIO $ forever $ control debug channels controlportSocket _ <- forkIO $ forever $ control debug channels controlportSocket
...@@ -180,44 +179,44 @@ serveStdin profile = do ...@@ -180,44 +179,44 @@ serveStdin profile = do
-- Create the context in a separate thread that never finishes. If withContext or withSocket -- Create the context in a separate thread that never finishes. If withContext or withSocket
-- complete, the context or socket become invalid. -- complete, the context or socket become invalid.
forkIO $ withContext $ \context -> _ <- forkIO $ withContext $ \ctxt ->
-- Serve on all sockets. -- Serve on all sockets.
serveSocket context Router (stdinPort profile) $ \socket -> do serveSocket ctxt Router (stdinPort profile) $ \sock -> do
-- Read the request from the interface channel and send it. -- Read the request from the interface channel and send it.
readChan reqChannel >>= sendMessage False (signatureKey profile) socket readChan reqChannel >>= sendMessage False (signatureKey profile) sock
-- Receive a response and write it to the interface channel. -- Receive a response and write it to the interface channel.
receiveMessage False socket >>= writeChan repChannel receiveMessage False sock >>= writeChan repChannel
return $ StdinChannel reqChannel repChannel return $ StdinChannel reqChannel repChannel
-- | Serve on a given socket in a separate thread. Bind the socket in the | given context and then -- | Serve on a given sock in a separate thread. Bind the sock in the | given context and then
-- loop the provided action, which should listen | on the socket and respond to any events. -- loop the provided action, which should listen | on the sock and respond to any events.
serveSocket :: SocketType a => Context -> a -> Port -> (Socket a -> IO b) -> IO () serveSocket :: SocketType a => Context -> a -> Port -> (Socket a -> IO b) -> IO ()
serveSocket context socketType port action = void $ serveSocket ctxt socketType port action = void $
withSocket context socketType $ \socket -> do withSocket ctxt socketType $ \sock -> do
bind socket $ "tcp://127.0.0.1:" ++ show port bind sock $ "tcp://127.0.0.1:" ++ show port
forever $ action socket forever $ action sock
-- | Listener on the heartbeat port. Echoes back any data it was sent. -- | Listener on the heartbeat port. Echoes back any data it was sent.
heartbeat :: ZeroMQInterface -> Socket Rep -> IO () heartbeat :: ZeroMQInterface -> Socket Rep -> IO ()
heartbeat _ socket = do heartbeat _ sock = do
-- Read some data. -- Read some data.
request <- receive socket request <- receive sock
-- Send it back. -- Send it back.
send socket [] request send sock [] request
-- | Listener on the shell port. Reads messages and writes them to | the shell request channel. For -- | Listener on the shell port. Reads messages and writes them to | the shell request channel. For
-- each message, reads a response from the | shell reply channel of the interface and sends it back -- each message, reads a response from the | shell reply channel of the interface and sends it back
-- to the frontend. -- to the frontend.
shell :: Bool -> ZeroMQInterface -> Socket Router -> IO () shell :: Bool -> ZeroMQInterface -> Socket Router -> IO ()
shell debug channels socket = do shell debug channels sock = do
-- Receive a message and write it to the interface channel. -- Receive a message and write it to the interface channel.
receiveMessage debug socket >>= writeChan requestChannel receiveMessage debug sock >>= writeChan requestChannel
-- Read the reply from the interface channel and send it. -- Read the reply from the interface channel and send it.
readChan replyChannel >>= sendMessage debug (hmacKey channels) socket readChan replyChannel >>= sendMessage debug (hmacKey channels) sock
where where
requestChannel = shellRequestChannel channels requestChannel = shellRequestChannel channels
...@@ -227,12 +226,12 @@ shell debug channels socket = do ...@@ -227,12 +226,12 @@ shell debug channels socket = do
-- each message, reads a response from the | shell reply channel of the interface and sends it back -- each message, reads a response from the | shell reply channel of the interface and sends it back
-- to the frontend. -- to the frontend.
control :: Bool -> ZeroMQInterface -> Socket Router -> IO () control :: Bool -> ZeroMQInterface -> Socket Router -> IO ()
control debug channels socket = do control debug channels sock = do
-- Receive a message and write it to the interface channel. -- Receive a message and write it to the interface channel.
receiveMessage debug socket >>= writeChan requestChannel receiveMessage debug sock >>= writeChan requestChannel
-- Read the reply from the interface channel and send it. -- Read the reply from the interface channel and send it.
readChan replyChannel >>= sendMessage debug (hmacKey channels) socket readChan replyChannel >>= sendMessage debug (hmacKey channels) sock
where where
requestChannel = controlRequestChannel channels requestChannel = controlRequestChannel channels
...@@ -241,33 +240,33 @@ control debug channels socket = do ...@@ -241,33 +240,33 @@ control debug channels socket = do
-- | Send messages via the iopub channel. | This reads messages from the ZeroMQ iopub interface -- | Send messages via the iopub channel. | This reads messages from the ZeroMQ iopub interface
-- channel | and then writes the messages to the socket. -- channel | and then writes the messages to the socket.
iopub :: Bool -> ZeroMQInterface -> Socket Pub -> IO () iopub :: Bool -> ZeroMQInterface -> Socket Pub -> IO ()
iopub debug channels socket = iopub debug channels sock =
readChan (iopubChannel channels) >>= sendMessage debug (hmacKey channels) socket readChan (iopubChannel channels) >>= sendMessage debug (hmacKey channels) sock
-- | Attempt to send a message along the socket, returning true if successful. -- | Attempt to send a message along the socket, returning true if successful.
trySendMessage :: Sender a => String -> Bool -> ByteString -> Socket a -> Message -> IO Bool trySendMessage :: Sender a => String -> Bool -> ByteString -> Socket a -> Message -> IO Bool
trySendMessage nm debug hmacKey socket message = do trySendMessage _ debug hmackey sock msg = do
let zmqErrorHandler :: ZMQError -> IO Bool let zmqErrorHandler :: ZMQError -> IO Bool
zmqErrorHandler e zmqErrorHandler e
-- Ignore errors if we cannot send. We may want to forward this to the thread that tried put the -- Ignore errors if we cannot send. We may want to forward this to the thread that tried put the
-- message in the Chan initially. -- message in the Chan initially.
| errno e == 38 = return False | errno e == 38 = return False
| otherwise = throwIO e | otherwise = throwIO e
(sendMessage debug hmacKey socket message >> return True) `catch` zmqErrorHandler (sendMessage debug hmackey sock msg >> return True) `catch` zmqErrorHandler
-- | Send messages via the iopub channel. This reads messages from the ZeroMQ iopub interface -- | Send messages via the iopub channel. This reads messages from the ZeroMQ iopub interface
-- channel and then writes the messages to the socket. This is a checked implementation which will -- channel and then writes the messages to the socket. This is a checked implementation which will
-- stop if the socket is closed. -- stop if the socket is closed.
checkedIOpub :: Bool -> ZeroMQInterface -> Socket Pub -> IO () checkedIOpub :: Bool -> ZeroMQInterface -> Socket Pub -> IO ()
checkedIOpub debug channels socket = do checkedIOpub debug channels sock = do
msg <- readChan (iopubChannel channels) msg <- readChan (iopubChannel channels)
cont <- trySendMessage "io" debug (hmacKey channels) socket msg cont <- trySendMessage "io" debug (hmacKey channels) sock msg
when cont $ when cont $
checkedIOpub debug channels socket checkedIOpub debug channels sock
-- | Receive and parse a message from a socket. -- | Receive and parse a message from a socket.
receiveMessage :: Receiver a => Bool -> Socket a -> IO Message receiveMessage :: Receiver a => Bool -> Socket a -> IO Message
receiveMessage debug socket = do receiveMessage debug sock = do
-- Read all identifiers until the identifier/message delimiter. -- Read all identifiers until the identifier/message delimiter.
idents <- readUntil "<IDS|MSG>" idents <- readUntil "<IDS|MSG>"
...@@ -285,12 +284,11 @@ receiveMessage debug socket = do ...@@ -285,12 +284,11 @@ receiveMessage debug socket = do
putStr "Content: " putStr "Content: "
Char.putStrLn content Char.putStrLn content
let message = parseMessage idents headerData parentHeader metadata content return $ parseMessage idents headerData parentHeader metadata content
return message
where where
-- Receive the next piece of data from the socket. -- Receive the next piece of data from the socket.
next = receive socket next = receive sock
-- Read data from the socket until we hit an ending string. Return all data as a list, which does -- Read data from the socket until we hit an ending string. Return all data as a list, which does
-- not include the ending string. -- not include the ending string.
...@@ -306,10 +304,10 @@ receiveMessage debug socket = do ...@@ -306,10 +304,10 @@ receiveMessage debug socket = do
-- socket. Sign it using HMAC with SHA-256 using the provided key. -- socket. Sign it using HMAC with SHA-256 using the provided key.
sendMessage :: Sender a => Bool -> ByteString -> Socket a -> Message -> IO () sendMessage :: Sender a => Bool -> ByteString -> Socket a -> Message -> IO ()
sendMessage _ _ _ SendNothing = return () sendMessage _ _ _ SendNothing = return ()
sendMessage debug hmacKey socket message = do sendMessage debug hmackey sock msg = do
when debug $ do when debug $ do
putStr "Message: " putStr "Message: "
print message print msg
putStr "Sent: " putStr "Sent: "
print content print content
...@@ -325,8 +323,8 @@ sendMessage debug hmacKey socket message = do ...@@ -325,8 +323,8 @@ sendMessage debug hmacKey socket message = do
sendLast content sendLast content
where where
sendPiece = send socket [SendMore] sendPiece = send sock [SendMore]
sendLast = send socket [] sendLast = send sock []
-- Encode to a strict bytestring. -- Encode to a strict bytestring.
encodeStrict :: ToJSON a => a -> ByteString encodeStrict :: ToJSON a => a -> ByteString
...@@ -338,12 +336,12 @@ sendMessage debug hmacKey socket message = do ...@@ -338,12 +336,12 @@ sendMessage debug hmacKey socket message = do
-- Compute the HMAC SHA-256 signature of a bytestring message. -- Compute the HMAC SHA-256 signature of a bytestring message.
hmac :: ByteString -> ByteString hmac :: ByteString -> ByteString
hmac = Char.pack . show . (HMAC.hmacGetDigest :: HMAC.HMAC SHA256 -> Hash.Digest SHA256) . HMAC.hmac hmacKey hmac = Char.pack . show . (HMAC.hmacGetDigest :: HMAC.HMAC SHA256 -> Hash.Digest SHA256) . HMAC.hmac hmackey
-- Pieces of the message. -- Pieces of the message.
head = header message hdr = header msg
parentHeaderStr = maybe "{}" encodeStrict $ parentHeader head parentHeaderStr = maybe "{}" encodeStrict $ mhParentHeader hdr
idents = identifiers head idents = mhIdentifiers hdr
metadata = "{}" metadata = "{}"
content = encodeStrict message content = encodeStrict msg
headStr = encodeStrict head headStr = encodeStrict hdr
...@@ -195,7 +195,7 @@ runKernel kOpts profileSrc = do ...@@ -195,7 +195,7 @@ runKernel kOpts profileSrc = do
installHandler keyboardSignal (CatchOnce $ putStrLn "Press Ctrl-C again to quit kernel.") installHandler keyboardSignal (CatchOnce $ putStrLn "Press Ctrl-C again to quit kernel.")
Nothing Nothing
isCommMessage req = msgType (header req) `elem` [CommDataMessage, CommCloseMessage] isCommMessage req = mhMsgType (header req) `elem` [CommDataMessage, CommCloseMessage]
-- Initial kernel state. -- Initial kernel state.
initialKernelState :: IO (MVar KernelState) initialKernelState :: IO (MVar KernelState)
...@@ -206,19 +206,11 @@ createReplyHeader :: MessageHeader -> Interpreter MessageHeader ...@@ -206,19 +206,11 @@ createReplyHeader :: MessageHeader -> Interpreter MessageHeader
createReplyHeader parent = do createReplyHeader parent = do
-- Generate a new message UUID. -- Generate a new message UUID.
newMessageId <- liftIO UUID.random newMessageId <- liftIO UUID.random
let repType = fromMaybe err (replyType $ msgType parent) let repType = fromMaybe err (replyType $ mhMsgType parent)
err = error $ "No reply for message " ++ show (msgType parent) err = error $ "No reply for message " ++ show (mhMsgType parent)
return return $ MessageHeader (mhIdentifiers parent) (Just parent) mempty
MessageHeader newMessageId (mhSessionId parent) (mhUsername parent) repType
{ identifiers = identifiers parent
, parentHeader = Just parent
, metadata = Map.fromList []
, messageId = newMessageId
, sessionId = sessionId parent
, username = username parent
, msgType = repType
}
-- | Compute a reply to a message. -- | Compute a reply to a message.
replyTo :: ZeroMQInterface -> Message -> MessageHeader -> KernelState -> Interpreter (KernelState, Message) replyTo :: ZeroMQInterface -> Message -> MessageHeader -> KernelState -> Interpreter (KernelState, Message)
...@@ -432,7 +424,7 @@ handleComm send kernelState req replyHeader = do ...@@ -432,7 +424,7 @@ handleComm send kernelState req replyHeader = do
newState <- case Map.lookup uuid widgets of newState <- case Map.lookup uuid widgets of
Nothing -> return kernelState Nothing -> return kernelState
Just (Widget widget) -> Just (Widget widget) ->
case msgType $ header req of case mhMsgType $ header req of
CommDataMessage -> do CommDataMessage -> do
disp <- run $ comm widget dat communicate disp <- run $ comm widget dat communicate
pgrOut <- liftIO $ readMVar pOut pgrOut <- liftIO $ readMVar pOut
......
...@@ -33,7 +33,6 @@ import GHC.IO.Handle ...@@ -33,7 +33,6 @@ import GHC.IO.Handle
import GHC.IO.Handle.Types import GHC.IO.Handle.Types
import System.Posix.IO import System.Posix.IO
import System.IO.Unsafe import System.IO.Unsafe
import qualified Data.Map as Map
import IHaskell.IPython.Types import IHaskell.IPython.Types
import IHaskell.IPython.ZeroMQ import IHaskell.IPython.ZeroMQ
...@@ -88,15 +87,8 @@ getInputLine dir = do ...@@ -88,15 +87,8 @@ getInputLine dir = do
-- Send a request for input. -- Send a request for input.
uuid <- UUID.random uuid <- UUID.random
parentHdr <- fromJust . readMay <$> readFile (dir ++ "/.last-req-header") parentHdr <- fromJust . readMay <$> readFile (dir ++ "/.last-req-header")
let hdr = MessageHeader let hdr = MessageHeader (mhIdentifiers parentHdr) (Just parentHdr) mempty
{ username = username parentHdr uuid (mhSessionId parentHdr) (mhUsername parentHdr) InputRequestMessage
, identifiers = identifiers parentHdr
, parentHeader = Just parentHdr
, messageId = uuid
, sessionId = sessionId parentHdr
, metadata = Map.fromList []
, msgType = InputRequestMessage
}
let msg = RequestInput hdr "" let msg = RequestInput hdr ""
writeChan req msg writeChan req msg
......
...@@ -39,7 +39,7 @@ module IHaskell.Types ( ...@@ -39,7 +39,7 @@ module IHaskell.Types (
import IHaskellPrelude import IHaskellPrelude
import Data.Aeson (ToJSON, Value, (.=), object) import Data.Aeson (ToJSON (..), Value, (.=), object)
import Data.Function (on) import Data.Function (on)
import Data.Serialize import Data.Serialize
import GHC.Generics import GHC.Generics
...@@ -268,4 +268,4 @@ dupHeader :: MessageHeader -> MessageType -> IO MessageHeader ...@@ -268,4 +268,4 @@ dupHeader :: MessageHeader -> MessageType -> IO MessageHeader
dupHeader hdr messageType = do dupHeader hdr messageType = do
uuid <- liftIO random uuid <- liftIO random
return hdr { messageId = uuid, msgType = messageType } return hdr { mhMessageId = uuid, mhMsgType = messageType }
...@@ -21,6 +21,7 @@ extra-deps: [] ...@@ -21,6 +21,7 @@ extra-deps: []
ghc-options: ghc-options:
# Eventually we want "$locals": -Wall -Werror # Eventually we want "$locals": -Wall -Werror
ghc-parser: -Wall -Werror
ihaskell: -Wall -Werror ihaskell: -Wall -Werror
nix: nix:
......
...@@ -25,6 +25,7 @@ extra-deps: ...@@ -25,6 +25,7 @@ extra-deps:
ghc-options: ghc-options:
# Eventually we want "$locals": -Wall -Werror # Eventually we want "$locals": -Wall -Werror
ghc-parser: -Wall -Werror
ihaskell: -Wall -Werror ihaskell: -Wall -Werror
nix: nix:
......
...@@ -19,6 +19,7 @@ packages: ...@@ -19,6 +19,7 @@ packages:
ghc-options: ghc-options:
# Eventually we want "$locals": -Wall -Werror # Eventually we want "$locals": -Wall -Werror
ghc-parser: -Wall -Werror
ihaskell: -Wall -Werror ihaskell: -Wall -Werror
allow-newer: true allow-newer: true
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment