Skip to content

Jupyter v5 protocol #70

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Sep 12, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 85 additions & 78 deletions kernel.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"io/ioutil"
"log"
"os"
"runtime"
"strings"

"github.com/cosmos72/gomacro/base"
Expand Down Expand Up @@ -43,23 +44,44 @@ type SocketGroup struct {
Key []byte
}

// kernelInfo holds information about the igo kernel, for
// kernel_info_reply messages.
type kernelInfo struct {
ProtocolVersion []int `json:"protocol_version"`
Language string `json:"language"`
// KernelLanguageInfo holds information about the language that this kernel executes code in.
type kernelLanguageInfo struct {
Name string `json:"name"`
Version string `json:"version"`
MIMEType string `json:"mimetype"`
FileExtension string `json:"file_extension"`
PygmentsLexer string `json:"pygments_lexer"`
CodeMirrorMode string `json:"codemirror_mode"`
NBConvertExporter string `json:"nbconvert_exporter"`
}

// HelpLink stores data to be displayed in the help menu of the notebook.
type helpLink struct {
Text string `json:"text"`
URL string `json:"url"`
}

// kernelStatus holds a kernel state, for status broadcast messages.
type kernelStatus struct {
ExecutionState string `json:"execution_state"`
// KernelInfo holds information about the igo kernel, for kernel_info_reply messages.
type kernelInfo struct {
ProtocolVersion string `json:"protocol_version"`
Implementation string `json:"implementation"`
ImplementationVersion string `json:"implementation_version"`
LanguageInfo kernelLanguageInfo `json:"language_info"`
Banner string `json:"banner"`
HelpLinks []helpLink `json:"help_links"`
}

// shutdownReply encodes a boolean indication of stutdown/restart
// shutdownReply encodes a boolean indication of stutdown/restart.
type shutdownReply struct {
Restart bool `json:"restart"`
}

const (
kernelStarting = "starting"
kernelBusy = "busy"
kernelIdle = "idle"
)

// runKernel is the main entry point to start the kernel.
func runKernel(connectionFile string) {

Expand Down Expand Up @@ -120,11 +142,11 @@ func runKernel(connectionFile string) {

handleShellMsg(ir, msgReceipt{msg, ids, sockets})

// TODO Handle stdin socket.
// TODO Handle stdin socket.
case sockets.StdinSocket:
sockets.StdinSocket.RecvMessageBytes(0)

// Handle control messages.
// Handle control messages.
case sockets.ControlSocket:
msgParts, err = sockets.ControlSocket.RecvMessageBytes(0)
if err != nil {
Expand Down Expand Up @@ -210,31 +232,29 @@ func handleShellMsg(ir *classic.Interp, receipt msgReceipt) {

// sendKernelInfo sends a kernel_info_reply message.
func sendKernelInfo(receipt msgReceipt) error {
reply, err := NewMsg("kernel_info_reply", receipt.Msg)
if err != nil {
return err
}

reply.Content = kernelInfo{[]int{4, 0}, "go"}
if err := receipt.SendResponse(receipt.Sockets.ShellSocket, reply); err != nil {
return err
}

return nil
return receipt.Reply("kernel_info_reply",
kernelInfo{
ProtocolVersion: ProtocolVersion,
Implementation: "gophernotes",
ImplementationVersion: Version,
Banner: fmt.Sprintf("Go kernel: gophernotes - v%s", Version),
LanguageInfo: kernelLanguageInfo{
Name: "go",
Version: runtime.Version(),
FileExtension: ".go",
},
HelpLinks: []helpLink{
{Text: "Go", URL: "https://golang.org/"},
{Text: "gophernotes", URL: "https://github.com/gopherdata/gophernotes"},
},
},
)
}

// handleExecuteRequest runs code from an execute_request method,
// and sends the various reply messages.
func handleExecuteRequest(ir *classic.Interp, receipt msgReceipt) error {

// Prepare the reply message.
reply, err := NewMsg("execute_reply", receipt.Msg)
if err != nil {
return err
}

content := make(map[string]interface{})

// Extract the data from the request
reqcontent := receipt.Msg.Content.(map[string]interface{})
code := reqcontent["code"].(string)
in := bufio.NewReader(strings.NewReader(code))
Expand All @@ -244,8 +264,26 @@ func handleExecuteRequest(ir *classic.Interp, receipt msgReceipt) error {
ExecCounter++
}

// Prepare the map that will hold the reply content.
content := make(map[string]interface{})
content["execution_count"] = ExecCounter

// Tell the front-end that the kernel is working and when finished notify the
// front-end that the kernel is idle again.
if err := receipt.PublishKernelStatus(kernelBusy); err != nil {
log.Printf("Error publishing kernel status 'busy': %v\n", err)
}
defer func() {
if err := receipt.PublishKernelStatus(kernelIdle); err != nil {
log.Printf("Error publishing kernel status 'idle': %v\n", err)
}
}()

// Tell the front-end what the kernel is about to execute.
if err := receipt.PublishExecutionInput(ExecCounter, code); err != nil {
log.Printf("Error publishing execution input: %v\n", err)
}

// Redirect the standard out from the REPL.
oldStdout := os.Stdout
rOut, wOut, err := os.Pipe()
Expand All @@ -266,7 +304,7 @@ func handleExecuteRequest(ir *classic.Interp, receipt msgReceipt) error {
env.Options &^= base.OptShowPrompt
env.Line = 0

// Perform the first iteration manually, to collect comments
// Perform the first iteration manually, to collect comments.
var comments string
str, firstToken := env.ReadMultiline(in, base.ReadOptCollectAllComments)
if firstToken >= 0 {
Expand Down Expand Up @@ -306,25 +344,17 @@ func handleExecuteRequest(ir *classic.Interp, receipt msgReceipt) error {
wErr.Close()
stdErr := <-outStderr

// TODO write stdout and stderr to streams rather than publishing as results

if len(val) > 0 {
content["status"] = "ok"
content["payload"] = make([]map[string]interface{}, 0)
content["user_variables"] = make(map[string]string)
content["user_expressions"] = make(map[string]string)
if !silent {
var outContent OutputMsg

out, err := NewMsg("execute_result", receipt.Msg)
if err != nil {
return err
if !silent {
// Publish the result of the execution.
if err := receipt.PublishExecutionResult(ExecCounter, val); err != nil {
log.Printf("Error publishing execution result: %v\n", err)
}

outContent.Execcount = ExecCounter
outContent.Data = make(map[string]string)
outContent.Data["text/plain"] = val
outContent.Metadata = make(map[string]interface{})
out.Content = outContent
receipt.SendResponse(receipt.Sockets.IOPubSocket, out)
}
}

Expand All @@ -334,48 +364,25 @@ func handleExecuteRequest(ir *classic.Interp, receipt msgReceipt) error {
content["evalue"] = stdErr
content["traceback"] = nil

errormsg, err := NewMsg("pyerr", receipt.Msg)
if err != nil {
return err
if err := receipt.PublishExecutionError(stdErr, []string{stdErr}); err != nil {
log.Printf("Error publishing execution error: %v\n", err)
}

errormsg.Content = ErrMsg{"Error", stdErr, []string{stdErr}}
receipt.SendResponse(receipt.Sockets.IOPubSocket, errormsg)
}

// Send the output back to the notebook.
reply.Content = content

if err := receipt.SendResponse(receipt.Sockets.ShellSocket, reply); err != nil {
return err
}

idle, err := NewMsg("status", receipt.Msg)
if err != nil {
return err
}

idle.Content = kernelStatus{"idle"}

if err := receipt.SendResponse(receipt.Sockets.IOPubSocket, idle); err != nil {
return err
}

return nil
return receipt.Reply("execute_reply", content)
}

// handleShutdownRequest sends a "shutdown" message
// handleShutdownRequest sends a "shutdown" message.
func handleShutdownRequest(receipt msgReceipt) {
reply, err := NewMsg("shutdown_reply", receipt.Msg)
if err != nil {
log.Fatal(err)
}

content := receipt.Msg.Content.(map[string]interface{})
restart := content["restart"].(bool)
reply.Content = shutdownReply{restart}

if err := receipt.SendResponse(receipt.Sockets.ShellSocket, reply); err != nil {
reply := shutdownReply{
Restart: restart,
}

if err := receipt.Reply("shutdown_reply", reply); err != nil {
log.Fatal(err)
}

Expand Down
3 changes: 3 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import (
"log"
)

const Version string = "1.0.0"
const ProtocolVersion string = "5.0"

func main() {

// Parse the connection file.
Expand Down
Loading