Skip to content

Commit

Permalink
Add buffers field of the Wire Protocol
Browse files Browse the repository at this point in the history
https://jupyter-client.readthedocs.io/en/stable/messaging.html#the-wire-protocol

Resolves issue

IHaskell#1144

This field has been in "the Wire Protocol" since before the Jupyter Message
specification version 5.0.

jupyter/jupyter_client#520

I've tested this feature with a proprietary JupyterLab extension and
I've verified that it works. It's difficult to provide a public
reproducible test.

TODO test sending
  • Loading branch information
jamesdbrock committed Feb 27, 2020
1 parent 9dd237e commit f051725
Show file tree
Hide file tree
Showing 4 changed files with 22 additions and 9 deletions.
2 changes: 1 addition & 1 deletion ipython-kernel/src/IHaskell/IPython/EasyKernel.hs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ createReplyHeader parent = do
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
Expand Down
13 changes: 8 additions & 5 deletions ipython-kernel/src/IHaskell/IPython/Message/Parser.hs
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,16 @@ import IHaskell.IPython.Types
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 }
Expand All @@ -39,16 +41,17 @@ parseHeader :: [ByteString] -- ^ The list of identifiers.
-> 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"
Expand Down
1 change: 1 addition & 0 deletions ipython-kernel/src/IHaskell/IPython/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ data MessageHeader =
, 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)

Expand Down
15 changes: 12 additions & 3 deletions ipython-kernel/src/IHaskell/IPython/ZeroMQ.hs
Original file line number Diff line number Diff line change
Expand Up @@ -278,14 +278,15 @@ receiveMessage debug sock = do
parentHeader <- next
metadata <- next
content <- next
buffers <- receiveMulti sock

when debug $ do
putStr "Header: "
Char.putStrLn headerData
putStr "Content: "
Char.putStrLn content

return $ parseMessage idents headerData parentHeader metadata content
return $ parseMessage idents headerData parentHeader metadata content buffers

where
-- Receive the next piece of data from the socket.
Expand Down Expand Up @@ -320,10 +321,18 @@ sendMessage debug hmackey sock msg = do
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 []

Expand Down

0 comments on commit f051725

Please sign in to comment.