ipython-kernel 0.8.2.0 → 0.8.3.0
raw patch · 6 files changed
+243/−27 lines, 6 filesdep +unordered-containers
Dependencies added: unordered-containers
Files
- ipython-kernel.cabal +2/−1
- src/IHaskell/IPython/Message/Parser.hs +111/−7
- src/IHaskell/IPython/Message/UUID.hs +2/−2
- src/IHaskell/IPython/Message/Writer.hs +32/−6
- src/IHaskell/IPython/Types.hs +93/−8
- src/IHaskell/IPython/ZeroMQ.hs +3/−3
ipython-kernel.cabal view
@@ -1,5 +1,5 @@ name: ipython-kernel-version: 0.8.2.0+version: 0.8.3.0 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.@@ -46,6 +46,7 @@ mtl >=2.1, text >=0.11, transformers >=0.3,+ unordered-containers >= 0.2.5, uuid >=1.3, zeromq4-haskell >=0.1, SHA >=1.6
src/IHaskell/IPython/Message/Parser.hs view
@@ -1,4 +1,5 @@-{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE OverloadedStrings, ScopedTypeVariables #-}+{-# OPTIONS_GHC -fno-warn-unused-binds -fno-warn-name-shadowing #-} -- | Description : Parsing messages received from IPython --@@ -7,13 +8,17 @@ -- the low-level 0MQ interface. module IHaskell.IPython.Message.Parser (parseMessage) where -import Data.Aeson ((.:), (.:?), (.!=), decode, Result(..), Object) import Control.Applicative ((<|>), (<$>), (<*>))-import Data.Aeson.Types (parse)-import Data.ByteString+import Data.Aeson ((.:), (.:?), (.!=), decode, Result(..), Object, Value(..))+import Data.Aeson.Types (parse, parseEither)+import Data.ByteString hiding (unpack)+import qualified Data.ByteString.Lazy as Lazy+import Data.HashMap.Strict as HM import Data.Map (Map)+import Data.Maybe (fromMaybe) import Data.Text (Text)-import qualified Data.ByteString.Lazy as Lazy+import Data.Text (Text, unpack)+import Debug.Trace import IHaskell.IPython.Types type LByteString = Lazy.ByteString@@ -72,7 +77,13 @@ -> LByteString -> Message -- ^ The parser that converts the body into a message. This message -- should have an undefined header. parser KernelInfoRequestMessage = kernelInfoRequestParser+parser ExecuteInputMessage = executeInputParser parser ExecuteRequestMessage = executeRequestParser+parser ExecuteReplyMessage = executeReplyParser+parser ExecuteErrorMessage = executeErrorParser+parser ExecuteResultMessage = executeResultParser+parser DisplayDataMessage = displayDataParser+parser IsCompleteRequestMessage = isCompleteRequestParser parser CompleteRequestMessage = completeRequestParser parser InspectRequestMessage = inspectRequestParser parser ShutdownRequestMessage = shutdownRequestParser@@ -81,6 +92,11 @@ parser CommDataMessage = commDataParser parser CommCloseMessage = commCloseParser parser HistoryRequestMessage = historyRequestParser+parser StatusMessage = statusMessageParser+parser StreamMessage = streamMessageParser+parser InputMessage = inputMessageParser+parser OutputMessage = outputMessageParser+parser ClearOutputMessage = clearOutputMessageParser parser other = error $ "Unknown message type " ++ show other -- | Parse a kernel info request. A kernel info request has no auxiliary information, so ignore the@@ -88,6 +104,13 @@ kernelInfoRequestParser :: LByteString -> Message kernelInfoRequestParser _ = KernelInfoRequest { header = noHeader } +-- | Parse an execute_input response. Fields used are:+executeInputParser :: LByteString -> Message+executeInputParser = requestParser $ \obj -> do+ code <- obj .: "code"+ executionCount <- obj .: "execution_count"+ return $ ExecuteInput noHeader code executionCount+ -- | Parse an execute request. Fields used are: -- 1. "code": the code to execute. -- 2. "silent": whether to execute silently.@@ -114,9 +137,48 @@ , getUserExpressions = [] } -requestParser parser content = parsed+-- | Parse an execute reply+executeReplyParser :: LByteString -> Message+executeReplyParser = requestParser $ \obj -> do+ status <- obj .: "status"+ executionCount <- obj .: "execution_count"+ return $ ExecuteReply noHeader status [] executionCount++-- | Parse an execute reply+executeErrorParser :: LByteString -> Message+executeErrorParser = requestParser $ \obj -> do+ -- executionCount <- obj .: "execution_count"+ traceback <- obj .: "traceback"+ ename <- obj .: "ename"+ evalue <- obj .: "evalue"+ return $ ExecuteError noHeader [] traceback ename evalue++makeDisplayDatas :: Object -> [DisplayData]+makeDisplayDatas dataDict = [DisplayData (read $ unpack mimeType) content | (mimeType, String content) <- HM.toList+ dataDict]++-- | Parse an execute result+executeResultParser :: LByteString -> Message+executeResultParser = requestParser $ \obj -> do+ executionCount <- obj .: "execution_count"+ dataDict :: Object <- obj .: "data"+ let displayDatas = makeDisplayDatas dataDict+ metadataDict <- obj .: "metadata"+ return $ ExecuteResult noHeader displayDatas metadataDict executionCount++-- | Parse a display data message+displayDataParser :: LByteString -> Message+displayDataParser = requestParser $ \obj -> do+ dataDict :: Object <- obj .: "data"+ let displayDatas = makeDisplayDatas dataDict+ maybeSource <- obj .:? "source"+ return $ PublishDisplayData noHeader (fromMaybe "" maybeSource) displayDatas++requestParser parser content =+ case parseEither parser decoded of+ Right parsed -> parsed+ Left err -> trace ("Parse error: " ++ show err) SendNothing where- Success parsed = parse parser decoded Just decoded = decode content historyRequestParser :: LByteString -> Message@@ -132,6 +194,48 @@ "tail" -> HistoryTail "search" -> HistorySearch str -> error $ "Unknown history access type: " ++ str++statusMessageParser :: LByteString -> Message+statusMessageParser = requestParser $ \obj -> do+ execution_state <- obj .: "execution_state"+ return $ PublishStatus noHeader execution_state++streamMessageParser :: LByteString -> Message+streamMessageParser = requestParser $ \obj -> do+ streamType <- obj .: "name"+ streamContent <- obj .: "text"+ return $ PublishStream noHeader streamType streamContent++inputMessageParser :: LByteString -> Message+inputMessageParser = requestParser $ \obj -> do+ code <- obj .: "code"+ executionCount <- obj .: "execution_count"+ return $ Input noHeader code executionCount++getDisplayDatas Nothing = []+getDisplayDatas (Just dataDict) = makeDisplayDatas dataDict++outputMessageParser :: LByteString -> Message+outputMessageParser = requestParser $ \obj -> do+ -- Handle both "data" and "text" keys+ maybeDataDict1 :: Maybe Object <- obj .:? "data"+ let displayDatas1 = getDisplayDatas maybeDataDict1++ maybeDataDict2 :: Maybe Object <- obj .:? "text"+ let displayDatas2 = getDisplayDatas maybeDataDict2++ executionCount <- obj .: "execution_count"+ return $ Output noHeader (displayDatas1 ++ displayDatas2) executionCount++clearOutputMessageParser :: LByteString -> Message+clearOutputMessageParser = requestParser $ \obj -> do+ wait <- obj .: "wait"+ return $ ClearOutput noHeader wait++isCompleteRequestParser :: LByteString -> Message+isCompleteRequestParser = requestParser $ \obj -> do+ code <- obj .: "code"+ return $ IsCompleteRequest noHeader code completeRequestParser :: LByteString -> Message completeRequestParser = requestParser $ \obj -> do
src/IHaskell/IPython/Message/UUID.hs view
@@ -3,10 +3,10 @@ -- Generate, parse, and pretty print UUIDs for use with IPython. module IHaskell.IPython.Message.UUID (UUID, random, randoms, uuidToString) where +import Control.Applicative ((<$>), (<*>)) import Control.Monad (mzero, replicateM)-import Control.Applicative ((<$>))-import Data.Text (pack) import Data.Aeson+import Data.Text (pack) import Data.UUID.V4 (nextRandom) -- | A UUID (universally unique identifier).
src/IHaskell/IPython/Message/Writer.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE OverloadedStrings #-}+{-# OPTIONS_GHC -fno-warn-unused-binds -fno-warn-name-shadowing -fno-warn-unused-matches #-} -- | Description : @ToJSON@ for Messages --@@ -6,14 +7,10 @@ module IHaskell.IPython.Message.Writer (ToJSON(..)) where import Data.Aeson+import Data.Aeson.Types (Pair) import Data.Map (Map)-import Data.Text (Text, pack) import Data.Monoid (mempty)--import qualified Data.ByteString.Lazy as L-import qualified Data.ByteString as B-import Data.Text.Encoding-+import Data.Text (Text, pack) import IHaskell.IPython.Types instance ToJSON LanguageInfo where@@ -34,6 +31,23 @@ , "language_info" .= languageInfo rep ] + toJSON ExecuteRequest+ { getCode = code+ , getSilent = silent+ , getStoreHistory = storeHistory+ , getAllowStdin = allowStdin+ , getUserVariables = userVariables+ , getUserExpressions = userExpressions+ } =+ object+ [ "code" .= code+ , "silent" .= silent+ , "store_history" .= storeHistory+ , "allow_stdin" .= allowStdin+ , "user_variables" .= userVariables+ , "user_expressions" .= userExpressions+ ]+ toJSON ExecuteReply { status = status, executionCounter = counter, pagerOutput = pager } = object [ "status" .= show status@@ -116,6 +130,18 @@ 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
src/IHaskell/IPython/Types.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE OverloadedStrings, DeriveDataTypeable, DeriveGeneric #-}+{-# OPTIONS_GHC -fno-warn-unused-binds -fno-warn-name-shadowing -fno-warn-unused-matches #-} -- | This module contains all types used to create an IPython language kernel. module IHaskell.IPython.Types (@@ -17,6 +18,7 @@ Username(..), Metadata(..), MessageType(..),+ CodeReview(..), Width(..), Height(..), StreamType(..),@@ -34,19 +36,18 @@ extractPlain, ) where -import Data.Aeson import Control.Applicative ((<$>), (<*>))+import Data.Aeson import Data.ByteString (ByteString)+import Data.List (find)+import Data.Map (Map)+import Data.Serialize+import Data.Text (Text) import qualified Data.Text as Text import qualified Data.Text.Encoding as Text-import Data.Text (Text)-import qualified Data.String as S-import Data.Serialize-import IHaskell.IPython.Message.UUID-import GHC.Generics (Generic) import Data.Typeable-import Data.List (find)-import Data.Map (Map)+import GHC.Generics (Generic)+import IHaskell.IPython.Message.UUID ------------------ IPython Kernel Profile Types ---------------------- --@@ -169,13 +170,18 @@ -- | The type of a message, corresponding to IPython message types. data MessageType = KernelInfoReplyMessage | KernelInfoRequestMessage+ | ExecuteInputMessage | ExecuteReplyMessage+ | ExecuteErrorMessage | ExecuteRequestMessage+ | ExecuteResultMessage | StatusMessage | StreamMessage | DisplayDataMessage | OutputMessage | InputMessage+ | IsCompleteRequestMessage+ | IsCompleteReplyMessage | CompleteRequestMessage | CompleteReplyMessage | InspectRequestMessage@@ -195,13 +201,18 @@ showMessageType :: MessageType -> String showMessageType KernelInfoReplyMessage = "kernel_info_reply" showMessageType KernelInfoRequestMessage = "kernel_info_request"+showMessageType ExecuteInputMessage = "execute_input" showMessageType ExecuteReplyMessage = "execute_reply"+showMessageType ExecuteErrorMessage = "error" showMessageType ExecuteRequestMessage = "execute_request"+showMessageType ExecuteResultMessage = "execute_result" showMessageType StatusMessage = "status" showMessageType StreamMessage = "stream" showMessageType DisplayDataMessage = "display_data" showMessageType OutputMessage = "pyout" showMessageType InputMessage = "pyin"+showMessageType IsCompleteRequestMessage = "is_complete_request"+showMessageType IsCompleteReplyMessage = "is_complete_reply" showMessageType CompleteRequestMessage = "complete_request" showMessageType CompleteReplyMessage = "complete_reply" showMessageType InspectRequestMessage = "inspect_request"@@ -222,13 +233,18 @@ case s of "kernel_info_reply" -> return KernelInfoReplyMessage "kernel_info_request" -> return KernelInfoRequestMessage+ "execute_input" -> return ExecuteInputMessage "execute_reply" -> return ExecuteReplyMessage+ "error" -> return ExecuteErrorMessage "execute_request" -> return ExecuteRequestMessage+ "execute_result" -> return ExecuteResultMessage "status" -> return StatusMessage "stream" -> return StreamMessage "display_data" -> return DisplayDataMessage "pyout" -> return OutputMessage "pyin" -> return InputMessage+ "is_complete_request" -> return IsCompleteRequestMessage+ "is_complete_reply" -> return IsCompleteReplyMessage "complete_request" -> return CompleteRequestMessage "complete_reply" -> return CompleteReplyMessage "inspect_request" -> return InspectRequestMessage@@ -243,6 +259,7 @@ "comm_close" -> return CommCloseMessage "history_request" -> return HistoryRequestMessage "history_reply" -> return HistoryReplyMessage+ "status_message" -> return StatusMessage _ -> fail ("Unknown message type: " ++ show s) parseJSON _ = fail "Must be a string."@@ -256,6 +273,12 @@ } deriving (Show, Eq) +data CodeReview = CodeComplete+ | CodeIncomplete String -- ^ String to be used to indent next line of input+ | CodeInvalid+ | CodeUnknown+ deriving Show+ -- | A message used to communicate with the IPython frontend. data Message = -- | A request from a frontend for information about the kernel.@@ -270,6 +293,13 @@ } | -- | A request from a frontend to execute some code.+ ExecuteInput+ { header :: MessageHeader+ , getCode :: Text -- ^ The code string.+ , executionCounter :: Int -- ^ The execution count, i.e. which output this is.+ }+ |+ -- | A request from a frontend to execute some code. ExecuteRequest { header :: MessageHeader , getCode :: Text -- ^ The code string.@@ -288,6 +318,23 @@ , executionCounter :: Int -- ^ The execution count, i.e. which output this is. } |+ -- | A reply to an execute request.+ ExecuteResult+ { header :: MessageHeader+ , dataResult :: [DisplayData] -- ^ Key/value pairs (keys are MIME types)+ , metadataResult :: Map String String -- ^ Any metadata that describes the data+ , executionCounter :: Int -- ^ The execution count, i.e. which output this is.+ }+ |+ -- | 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+ }+ | PublishStatus { header :: MessageHeader , executionState :: ExecutionState -- ^ The execution state of the kernel.@@ -316,7 +363,19 @@ , inCode :: String -- ^ Submitted input code. , executionCount :: Int -- ^ Which input this is. }+ | Input { header :: MessageHeader, getCode :: Text, executionCount :: Int }+ | Output { header :: MessageHeader, getText :: [DisplayData], executionCount :: Int } |+ IsCompleteRequest+ { header :: MessageHeader+ , inputToReview :: String -- ^ The code entered in the repl.+ }+ |+ IsCompleteReply+ { header :: MessageHeader+ , reviewResult :: CodeReview -- ^ The result of reviewing the code.+ }+ | CompleteRequest { header :: MessageHeader , getCode :: Text {- ^@@ -415,6 +474,11 @@ | Err | Abort +instance FromJSON ExecuteReplyStatus where+ parseJSON (String "ok") = return Ok+ parseJSON (String "error") = return Err+ parseJSON (String "abort") = return Abort+ instance Show ExecuteReplyStatus where show Ok = "ok" show Err = "error"@@ -426,15 +490,27 @@ | Starting deriving Show +instance FromJSON ExecutionState where+ parseJSON (String "busy") = return Busy+ parseJSON (String "idle") = return Idle+ parseJSON (String "starting") = return Starting+ -- | Input and output streams. data StreamType = Stdin | Stdout+ | Stderr deriving Show +instance FromJSON StreamType where+ parseJSON (String "stdin") = return Stdin+ parseJSON (String "stdout") = return Stdout+ parseJSON (String "stderr") = return Stderr+ -- | Get the reply message type for a request message type. replyType :: MessageType -> Maybe MessageType replyType KernelInfoRequestMessage = Just KernelInfoReplyMessage replyType ExecuteRequestMessage = Just ExecuteReplyMessage+replyType IsCompleteRequestMessage = Just IsCompleteReplyMessage replyType CompleteRequestMessage = Just CompleteReplyMessage replyType InspectRequestMessage = Just InspectReplyMessage replyType ShutdownRequestMessage = Just ShutdownReplyMessage@@ -491,3 +567,12 @@ show MimeSvg = "image/svg+xml" show MimeLatex = "text/latex" show MimeJavascript = "application/javascript"++instance Read MimeType where+ readsPrec _ "text/plain" = [(PlainText, "")]+ readsPrec _ "text/html" = [(MimeHtml, "")]+ readsPrec _ "image/png" = [(MimePng 50 50, "")]+ readsPrec _ "image/jpg" = [(MimeJpg 50 50, "")]+ readsPrec _ "image/svg+xml" = [(MimeSvg, "")]+ readsPrec _ "text/latex" = [(MimeLatex, "")]+ readsPrec _ "application/javascript" = [(MimeJavascript, "")]
src/IHaskell/IPython/ZeroMQ.hs view
@@ -18,9 +18,9 @@ import Control.Exception import Control.Monad import Data.Aeson-import qualified Data.ByteString.Lazy as LBS import Data.ByteString (ByteString) import qualified Data.ByteString.Char8 as Char+import qualified Data.ByteString.Lazy as LBS import Data.Char import Data.Digest.Pure.SHA as SHA import Data.Monoid ((<>))@@ -28,9 +28,9 @@ import System.ZMQ4 as ZMQ4 hiding (stdin) import Text.Read (readMaybe) -import IHaskell.IPython.Types import IHaskell.IPython.Message.Parser-import IHaskell.IPython.Message.Writer+import IHaskell.IPython.Message.Writer ()+import IHaskell.IPython.Types -- | The channel interface to the ZeroMQ sockets. All communication is done via Messages, which are -- encoded and decoded into a lower level form before being transmitted to IPython. These channels