Skip to content

Commit 5e1f16d

Browse files
SpencerParkBuildTools
authored and
BuildTools
committed
Update messages to the 5.0 spec and encapsulate.
1 parent 20ea78c commit 5e1f16d

File tree

3 files changed

+204
-77
lines changed

3 files changed

+204
-77
lines changed

kernel.go

+73-76
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"io/ioutil"
1010
"log"
1111
"os"
12+
"runtime"
1213
"strings"
1314

1415
"github.com/cosmos72/gomacro/base"
@@ -43,18 +44,38 @@ type SocketGroup struct {
4344
Key []byte
4445
}
4546

46-
// kernelInfo holds information about the igo kernel, for
47-
// kernel_info_reply messages.
48-
type kernelInfo struct {
49-
ProtocolVersion []int `json:"protocol_version"`
50-
Language string `json:"language"`
51-
}
52-
5347
// kernelStatus holds a kernel state, for status broadcast messages.
5448
type kernelStatus struct {
5549
ExecutionState string `json:"execution_state"`
5650
}
5751

52+
// KernelLanguageInfo holds information about the language that this kernel executes code in
53+
type kernelLanguageInfo struct {
54+
Name string `json:"name"`
55+
Version string `json:"version"`
56+
MIMEType string `json:"mimetype"`
57+
FileExtension string `json:"file_extension"`
58+
PygmentsLexer string `json:"pygments_lexer"`
59+
CodeMirrorMode string `json:"codemirror_mode"`
60+
NBConvertExporter string `json:"nbconvert_exporter"`
61+
}
62+
63+
// HelpLink stores data to be displayed in the help menu of the notebook
64+
type helpLink struct {
65+
Text string `json:"text"`
66+
URL string `json:"url"`
67+
}
68+
69+
// KernelInfo holds information about the igo kernel, for kernel_info_reply messages.
70+
type kernelInfo struct {
71+
ProtocolVersion string `json:"protocol_version"`
72+
Implementation string `json:"implementation"`
73+
ImplementationVersion string `json:"implementation_version"`
74+
LanguageInfo kernelLanguageInfo `json:"language_info"`
75+
Banner string `json:"banner"`
76+
HelpLinks []helpLink `json:"help_links"`
77+
}
78+
5879
// shutdownReply encodes a boolean indication of stutdown/restart
5980
type shutdownReply struct {
6081
Restart bool `json:"restart"`
@@ -120,11 +141,11 @@ func runKernel(connectionFile string) {
120141

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

123-
// TODO Handle stdin socket.
144+
// TODO Handle stdin socket.
124145
case sockets.StdinSocket:
125146
sockets.StdinSocket.RecvMessageBytes(0)
126147

127-
// Handle control messages.
148+
// Handle control messages.
128149
case sockets.ControlSocket:
129150
msgParts, err = sockets.ControlSocket.RecvMessageBytes(0)
130151
if err != nil {
@@ -210,31 +231,29 @@ func handleShellMsg(ir *classic.Interp, receipt msgReceipt) {
210231

211232
// sendKernelInfo sends a kernel_info_reply message.
212233
func sendKernelInfo(receipt msgReceipt) error {
213-
reply, err := NewMsg("kernel_info_reply", receipt.Msg)
214-
if err != nil {
215-
return err
216-
}
217-
218-
reply.Content = kernelInfo{[]int{4, 0}, "go"}
219-
if err := receipt.SendResponse(receipt.Sockets.ShellSocket, reply); err != nil {
220-
return err
221-
}
222-
223-
return nil
234+
return receipt.Reply("kernel_info_reply",
235+
kernelInfo{
236+
ProtocolVersion: "5.0",
237+
Implementation: "gophernotes",
238+
ImplementationVersion: Version,
239+
Banner: fmt.Sprintf("Go kernel: gophernotes - v%s", Version),
240+
LanguageInfo: kernelLanguageInfo{
241+
Name: "go",
242+
Version: runtime.Version(),
243+
FileExtension: ".go",
244+
},
245+
HelpLinks: []helpLink{
246+
{Text: "Go", URL: "https://golang.org/"},
247+
{Text: "gophernotes", URL: "https://github.com/gopherdata/gophernotes"},
248+
},
249+
},
250+
)
224251
}
225252

226253
// handleExecuteRequest runs code from an execute_request method,
227254
// and sends the various reply messages.
228255
func handleExecuteRequest(ir *classic.Interp, receipt msgReceipt) error {
229-
230-
// Prepare the reply message.
231-
reply, err := NewMsg("execute_reply", receipt.Msg)
232-
if err != nil {
233-
return err
234-
}
235-
236-
content := make(map[string]interface{})
237-
256+
// Extract the data from the request
238257
reqcontent := receipt.Msg.Content.(map[string]interface{})
239258
code := reqcontent["code"].(string)
240259
in := bufio.NewReader(strings.NewReader(code))
@@ -244,8 +263,18 @@ func handleExecuteRequest(ir *classic.Interp, receipt msgReceipt) error {
244263
ExecCounter++
245264
}
246265

266+
// Prepare the map that will hold the reply content
267+
content := make(map[string]interface{})
247268
content["execution_count"] = ExecCounter
248269

270+
// Tell the front-end that the kernel is working and when finished notify the
271+
// front-end that the kernel is idle again
272+
receipt.PublishKernelBusy()
273+
defer receipt.PublishKernelIdle()
274+
275+
// Tell the front-end what the kernel is about to execute
276+
receipt.PublishExecutionInput(ExecCounter, code)
277+
249278
// Redirect the standard out from the REPL.
250279
oldStdout := os.Stdout
251280
rOut, wOut, err := os.Pipe()
@@ -306,25 +335,15 @@ func handleExecuteRequest(ir *classic.Interp, receipt msgReceipt) error {
306335
wErr.Close()
307336
stdErr := <-outStderr
308337

338+
// TODO write stdout and stderr to streams rather than publishing as results
339+
309340
if len(val) > 0 {
310341
content["status"] = "ok"
311-
content["payload"] = make([]map[string]interface{}, 0)
312-
content["user_variables"] = make(map[string]string)
313342
content["user_expressions"] = make(map[string]string)
314-
if !silent {
315-
var outContent OutputMsg
316-
317-
out, err := NewMsg("execute_result", receipt.Msg)
318-
if err != nil {
319-
return err
320-
}
321343

322-
outContent.Execcount = ExecCounter
323-
outContent.Data = make(map[string]string)
324-
outContent.Data["text/plain"] = val
325-
outContent.Metadata = make(map[string]interface{})
326-
out.Content = outContent
327-
receipt.SendResponse(receipt.Sockets.IOPubSocket, out)
344+
if !silent {
345+
// Publish the result of the execution
346+
receipt.PublishExecutionResult(ExecCounter, val)
328347
}
329348
}
330349

@@ -334,51 +353,29 @@ func handleExecuteRequest(ir *classic.Interp, receipt msgReceipt) error {
334353
content["evalue"] = stdErr
335354
content["traceback"] = nil
336355

337-
errormsg, err := NewMsg("pyerr", receipt.Msg)
338-
if err != nil {
339-
return err
340-
}
341-
342-
errormsg.Content = ErrMsg{"Error", stdErr, []string{stdErr}}
343-
receipt.SendResponse(receipt.Sockets.IOPubSocket, errormsg)
356+
receipt.PublishExecutionError(stdErr, stdErr)
344357
}
345358

346359
// Send the output back to the notebook.
347-
reply.Content = content
348-
349-
if err := receipt.SendResponse(receipt.Sockets.ShellSocket, reply); err != nil {
350-
return err
351-
}
352-
353-
idle, err := NewMsg("status", receipt.Msg)
354-
if err != nil {
355-
return err
356-
}
357-
358-
idle.Content = kernelStatus{"idle"}
359-
360-
if err := receipt.SendResponse(receipt.Sockets.IOPubSocket, idle); err != nil {
361-
return err
362-
}
363-
364-
return nil
360+
return receipt.Reply("execute_reply", content)
365361
}
366362

367363
// handleShutdownRequest sends a "shutdown" message
368364
func handleShutdownRequest(receipt msgReceipt) {
369-
reply, err := NewMsg("shutdown_reply", receipt.Msg)
370-
if err != nil {
371-
log.Fatal(err)
372-
}
373-
374365
content := receipt.Msg.Content.(map[string]interface{})
375366
restart := content["restart"].(bool)
376-
reply.Content = shutdownReply{restart}
377367

378-
if err := receipt.SendResponse(receipt.Sockets.ShellSocket, reply); err != nil {
368+
err := receipt.Reply("shutdown_reply",
369+
shutdownReply{
370+
Restart: restart,
371+
},
372+
)
373+
374+
if err != nil {
379375
log.Fatal(err)
380376
}
381377

378+
382379
log.Println("Shutting down in response to shutdown_request")
383380
os.Exit(0)
384381
}

main.go

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import (
55
"log"
66
)
77

8+
const Version string = "1.0.0"
9+
810
func main() {
911

1012
// Parse the connection file.

messages.go

+129-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ func WireMsgToComposedMsg(msgparts [][]byte, signkey []byte) (ComposedMsg, [][]b
7171
var msg ComposedMsg
7272
if len(signkey) != 0 {
7373
mac := hmac.New(sha256.New, signkey)
74-
for _, msgpart := range msgparts[i+2 : i+6] {
74+
for _, msgpart := range msgparts[i+2: i+6] {
7575
mac.Write(msgpart)
7676
}
7777
signature := make([]byte, hex.DecodedLen(len(msgparts[i+1])))
@@ -208,3 +208,131 @@ func (receipt *msgReceipt) Reply(msgType string, content interface{}) error {
208208
msg.Content = content
209209
return receipt.SendResponse(receipt.Sockets.ShellSocket, msg)
210210
}
211+
212+
// MIMEDataBundle holds data that can be presented in multiple formats. The keys are MIME types
213+
// and the values are the data formatted with respect to it's MIME type. All bundle should contain
214+
// at least a "text/plain" representation with a string value.
215+
type MIMEDataBundle map[string]interface{}
216+
217+
// NewTextMIMEDataBundle creates a MIMEDataBundle that only contains a text representation described
218+
// the the parameter 'value'
219+
func NewTextMIMEDataBundle(value string) MIMEDataBundle {
220+
return MIMEDataBundle{
221+
"text/plain": value,
222+
}
223+
}
224+
225+
// KernelStatus holds a kernel execution state, for status broadcast messages.
226+
type KernelStatus struct {
227+
ExecutionState string `json:"execution_state"`
228+
}
229+
230+
// PublishKernelStarting publishes a status message notifying front-ends that the kernel is
231+
// starting up.
232+
func (receipt *msgReceipt) PublishKernelStarting() {
233+
receipt.Publish("status",
234+
KernelStatus{
235+
ExecutionState: "starting",
236+
},
237+
)
238+
}
239+
240+
// PublishKernelBusy publishes a status message notifying front-ends that the kernel is
241+
// doing work.
242+
func (receipt *msgReceipt) PublishKernelBusy() {
243+
receipt.Publish("status",
244+
KernelStatus{
245+
ExecutionState: "busy",
246+
},
247+
)
248+
}
249+
250+
// PublishKernelIdle publishes a status message notifying front-ends that the kernel is
251+
// free.
252+
func (receipt *msgReceipt) PublishKernelIdle() {
253+
receipt.Publish("status",
254+
KernelStatus{
255+
ExecutionState: "idle",
256+
},
257+
)
258+
}
259+
260+
// ExecuteInput holds the source code being executed and the execution counter value
261+
// associated with source being run.
262+
type ExecuteInput struct {
263+
ExecCount int `json:"execution_count"`
264+
Code string `json:"code"`
265+
}
266+
267+
// PublishExecutionInput publishes a status message notifying front-ends of what code is
268+
// currently being executed.
269+
func (receipt *msgReceipt) PublishExecutionInput(execCount int, code string) {
270+
receipt.Publish("execute_input",
271+
ExecuteInput{
272+
ExecCount: execCount,
273+
Code: code,
274+
},
275+
)
276+
}
277+
278+
// ExecuteResult holds the output to the 'ExecCount'th code execution.
279+
type ExecuteResult struct {
280+
ExecCount int `json:"execution_count"`
281+
Data MIMEDataBundle `json:"data"`
282+
Metadata MIMEDataBundle `json:"metadata"`
283+
}
284+
285+
// PublishExecuteResult publishes the result of the 'execCount'th execution as a string.
286+
func (receipt *msgReceipt) PublishExecutionResult(execCount int, output string) {
287+
receipt.Publish("execute_result",
288+
ExecuteResult{
289+
ExecCount: execCount,
290+
Data: NewTextMIMEDataBundle(output),
291+
Metadata: make(MIMEDataBundle),
292+
},
293+
)
294+
}
295+
296+
// ExecuteError holds data describing an error encountered during execution.
297+
type ExecuteError struct {
298+
Name string `json:"ename"`
299+
Value string `json:"evalue"`
300+
Trace []string `json:"traceback"`
301+
}
302+
303+
// PublishExecuteResult publishes a serialized error that was encountered during execution.
304+
func (receipt *msgReceipt) PublishExecutionError(err string, trace string) {
305+
receipt.Publish("error",
306+
ExecuteError{
307+
Name: "ERROR",
308+
Value: err,
309+
Trace: []string{trace},
310+
},
311+
)
312+
}
313+
314+
// WriteStreamData holds data to be written to a stream (stdout, stderr)
315+
type WriteStreamData struct {
316+
Stream string `json:"name"`
317+
Data string `json:"text"`
318+
}
319+
320+
// PublishWriteStdOut publishes the data string to the front-end's stdout
321+
func (receipt *msgReceipt) PublishWriteStdOut(data string) {
322+
receipt.Publish("stream",
323+
WriteStreamData{
324+
Stream: "stdout",
325+
Data: data,
326+
},
327+
)
328+
}
329+
330+
// PublishWriteStdErr publishes the data string to the front-end's stderr
331+
func (receipt *msgReceipt) PublishWriteStdErr(data string) {
332+
receipt.Publish("stream",
333+
WriteStreamData{
334+
Stream: "stderr",
335+
Data: data,
336+
},
337+
)
338+
}

0 commit comments

Comments
 (0)