ipython-kernel 0.10.1.0 → 0.10.2.0
raw patch · 5 files changed
+50/−44 lines, 5 files
Files
- ipython-kernel.cabal +3/−2
- src/IHaskell/IPython/EasyKernel.hs +2/−2
- src/IHaskell/IPython/Message/Parser.hs +8/−5
- src/IHaskell/IPython/Types.hs +4/−1
- src/IHaskell/IPython/ZeroMQ.hs +33/−34
ipython-kernel.cabal view
@@ -1,5 +1,5 @@ name: ipython-kernel-version: 0.10.1.0+version: 0.10.2.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.@@ -52,7 +52,8 @@ transformers , unordered-containers, uuid ,- zeromq4-haskell+ zeromq4-haskell ,+ parsec -- Example program executable simple-calc-example
src/IHaskell/IPython/EasyKernel.hs view
@@ -125,7 +125,7 @@ err = error $ "No reply for message " ++ show (mhMsgType parent) return $ MessageHeader (mhIdentifiers parent) (Just parent) (Metadata (HashMap.fromList []))- newMessageId (mhSessionId parent) (mhUsername parent) repType+ newMessageId (mhSessionId parent) (mhUsername parent) repType [] -- | Execute an IPython kernel for a config. Your 'main' action should call this as the last thing@@ -144,7 +144,7 @@ repHeader <- createReplyHeader (header req) when (debug config) . liftIO $ print req reply <- replyTo config execCount zmq req repHeader- liftIO $ writeChan (shellRequestChannel zmq) reply+ liftIO $ writeChan (shellReplyChannel zmq) reply replyTo :: MonadIO m => KernelConfig m output result
src/IHaskell/IPython/Message/Parser.hs view
@@ -22,14 +22,16 @@ type LByteString = Lazy.ByteString -- --- External interface ----- | Parse a message from its ByteString components into a Message.+-- See https://jupyter-client.readthedocs.io/en/stable/messaging.html#the-wire-protocol parseMessage :: [ByteString] -- ^ The list of identifiers sent with the message. -> ByteString -- ^ The header data. -> ByteString -- ^ The parent header, which is just "{}" if there is no header. -> ByteString -- ^ The metadata map, also "{}" for an empty map. -> ByteString -- ^ The message content.+ -> [ByteString] -- ^ Extra raw data buffer(s) -> Message -- ^ A parsed message.-parseMessage idents headerData parentHeader metadata content =- let header = parseHeader idents headerData parentHeader metadata+parseMessage idents headerData parentHeader metadata content buffers =+ let header = parseHeader idents headerData parentHeader metadata buffers messageType = mhMsgType header messageWithoutHeader = parser messageType $ Lazy.fromStrict content in messageWithoutHeader { header = header }@@ -39,16 +41,17 @@ -> ByteString -- ^ The header data. -> ByteString -- ^ The parent header, or "{}" for Nothing. -> ByteString -- ^ The metadata, or "{}" for an empty map.+ -> [ByteString] -- ^ Extra raw data buffer(s) -> MessageHeader -- The resulting message header.-parseHeader idents headerData parentHeader metadata =- MessageHeader idents parentResult metadataMap messageUUID sessionUUID username messageType+parseHeader idents headerData parentHeader metadata buffers =+ MessageHeader idents parentResult metadataMap messageUUID sessionUUID username messageType buffers where -- Decode the header data and the parent header data into JSON objects. If the parent header data is -- absent, just have Nothing instead. Just result = decode $ Lazy.fromStrict headerData :: Maybe Object parentResult = if parentHeader == "{}" then Nothing- else Just $ parseHeader idents parentHeader "{}" metadata+ else Just $ parseHeader idents parentHeader "{}" metadata [] Success (messageType, username, messageUUID, sessionUUID) = flip parse result $ \obj -> do messType <- obj .: "msg_type"
src/IHaskell/IPython/Types.hs view
@@ -153,6 +153,7 @@ , mhSessionId :: UUID -- ^ A unique session UUID. , mhUsername :: Username -- ^ The user who sent this message. , mhMsgType :: MessageType -- ^ The message type.+ , mhBuffers :: [ByteString] -- ^ Extra raw data buffer(s) } deriving (Show, Read) @@ -779,6 +780,7 @@ | MimeVega | MimeVegalite | MimeVdom+ | MimeCustom Text deriving (Eq, Typeable, Generic) -- Extract the plain text from a list of displays.@@ -806,6 +808,7 @@ show MimeVega = "application/vnd.vega.v2+json" show MimeVegalite = "application/vnd.vegalite.v2+json" show MimeVdom = "application/vdom.v1+json"+ show (MimeCustom custom) = Text.unpack custom instance Read MimeType where readsPrec _ "text/plain" = [(PlainText, "")]@@ -822,7 +825,7 @@ readsPrec _ "application/vnd.vega.v2+json" = [(MimeVega, "")] readsPrec _ "application/vnd.vegalite.v1+json" = [(MimeVegalite, "")] readsPrec _ "application/vdom.v1+json" = [(MimeVdom, "")]- readsPrec _ _ = []+ readsPrec _ t = [(MimeCustom (Text.pack t), "")] -- | Convert a MIME type and value into a JSON dictionary pair. displayDataToJson :: DisplayData -> (Text, Value)
src/IHaskell/IPython/ZeroMQ.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE OverloadedStrings, DoAndIfThenElse #-}+{-# LANGUAGE OverloadedStrings, DoAndIfThenElse, FlexibleContexts #-} -- | Description : Low-level ZeroMQ communication wrapper. --@@ -30,6 +30,7 @@ import qualified Data.Text.Encoding as Text import System.ZMQ4 as ZMQ4 import Text.Read (readMaybe)+import Text.Parsec (runParserT, manyTill, anyToken, (<|>), eof, tokenPrim, incSourceColumn) import IHaskell.IPython.Message.Parser import IHaskell.IPython.Types@@ -268,38 +269,28 @@ -- | Receive and parse a message from a socket. receiveMessage :: Receiver a => Bool -> Socket a -> IO Message receiveMessage debug sock = do- -- Read all identifiers until the identifier/message delimiter.- idents <- readUntil "<IDS|MSG>"-- -- Ignore the signature for now.- void next-- headerData <- next- parentHeader <- next- metadata <- next- content <- next-- when debug $ do- putStr "Header: "- Char.putStrLn headerData- putStr "Content: "- Char.putStrLn content-- return $ parseMessage idents headerData parentHeader metadata content-+ blobs <- receiveMulti sock+ runParserT parseBlobs () "" blobs >>= \r -> case r of+ Left parseerr -> fail $ "Malformed Wire Protocol message: " <> show parseerr+ Right (idents, headerData, parentHeader, metaData, content, buffers) -> do+ when debug $ do+ putStr "Header: "+ Char.putStrLn headerData+ putStr "Content: "+ Char.putStrLn content+ return $ parseMessage idents headerData parentHeader metaData content buffers where- -- Receive the next piece of data from the socket.- next = receive sock-- -- Read data from the socket until we hit an ending string. Return all data as a list, which does- -- not include the ending string.- readUntil str = do- line <- next- if line /= str- then do- remaining <- readUntil str- return $ line : remaining- else return []+ parseBlobs = do+ idents <- manyTill anyToken (satisfy (=="<IDS|MSG>"))+ _ <- anyToken <|> fail "No signature"+ headerData <- anyToken <|> fail "No headerData"+ parentHeader <- anyToken <|> fail "No parentHeader"+ metaData <- anyToken <|> fail "No metaData"+ content <- anyToken <|> fail "No contents"+ buffers <- manyTill anyToken eof+ pure (idents, headerData, parentHeader, metaData, content, buffers)+ satisfy f = tokenPrim Char.unpack (\pos _ _ -> incSourceColumn pos 1)+ (\c -> if f c then Just c else Nothing) -- | Encode a message in the IPython ZeroMQ communication protocol and send it through the provided -- socket. Sign it using HMAC with SHA-256 using the provided key.@@ -320,10 +311,18 @@ sendPiece parentHeaderStr sendPiece metadata - -- Conclude transmission with content.- sendLast content+ -- If there are no mhBuffers, then conclude transmission with content.+ case mhBuffers hdr of+ [] -> sendLast content+ _ -> sendPiece content + sendBuffers $ mhBuffers hdr+ where+ sendBuffers [] = pure ()+ sendBuffers [b] = sendLast b+ sendBuffers (b:bs) = sendPiece b >> sendBuffers bs+ sendPiece = send sock [SendMore] sendLast = send sock []