github.com/yunabe/lgo@v0.0.0-20190709125917-42c42d410fdf/cmd/lgo-internal/kernel.go (about) 1 package main 2 3 import ( 4 "context" 5 "encoding/hex" 6 "encoding/json" 7 "fmt" 8 "io" 9 "log" 10 "math/rand" 11 "os" 12 "runtime/debug" 13 "strings" 14 "time" 15 "unicode/utf8" 16 17 "github.com/golang/glog" 18 "github.com/yunabe/lgo/cmd/lgo-internal/liner" 19 "github.com/yunabe/lgo/cmd/runner" 20 "github.com/yunabe/lgo/converter" 21 "github.com/yunabe/lgo/core" 22 scaffold "github.com/yunabe/lgo/jupyter/gojupyterscaffold" 23 ) 24 25 type handlers struct { 26 runner *runner.LgoRunner 27 execCount int 28 } 29 30 func (*handlers) HandleKernelInfo() scaffold.KernelInfo { 31 return scaffold.KernelInfo{ 32 ProtocolVersion: "5.2", 33 Implementation: "lgo", 34 ImplementationVersion: "0.0.1", 35 LanguageInfo: scaffold.KernelLanguageInfo{ 36 Name: "go", 37 }, 38 Banner: "lgo", 39 } 40 } 41 42 func pipeOutput(send func(string), file **os.File, done chan<- struct{}) (close func() error, err error) { 43 r, w, err := os.Pipe() 44 if err != nil { 45 return nil, err 46 } 47 orig := *file 48 *file = w 49 go func() { 50 utf8R := newUTF8AwareReader(r) 51 var buf [4096]byte 52 for { 53 n, err := utf8R.Read(buf[:]) 54 if n > 0 { 55 send(string(buf[:n])) 56 } 57 if err != nil { 58 if err != io.EOF { 59 glog.Error(err) 60 } 61 break 62 } 63 } 64 if err := r.Close(); err != nil { 65 glog.Errorf("Failed to close a reader pipe: %v", err) 66 } 67 done <- struct{}{} 68 }() 69 close = func() error { 70 *file = orig 71 return w.Close() 72 } 73 return close, nil 74 } 75 76 type jupyterDisplayer func(data *scaffold.DisplayData, update bool) 77 78 func init() { 79 // Initialize the seed to use it from display. 80 rand.Seed(time.Now().UnixNano()) 81 } 82 83 func (d jupyterDisplayer) display(data *scaffold.DisplayData, id *string) { 84 update := false 85 if id != nil { 86 if *id == "" { 87 var buf [16]byte 88 rand.Read(buf[:]) 89 var enc [32]byte 90 hex.Encode(enc[:], buf[:]) 91 *id = "displayid_" + string(enc[:]) 92 } else { 93 update = true 94 } 95 if data.Transient == nil { 96 data.Transient = make(map[string]interface{}) 97 } 98 data.Transient["display_id"] = *id 99 } 100 d(data, update) 101 } 102 103 func (d jupyterDisplayer) Raw(contentType string, v interface{}, id *string) error { 104 _, err := json.Marshal(v) 105 if err != nil { 106 return err 107 } 108 d.display(&scaffold.DisplayData{ 109 Data: map[string]interface{}{ 110 contentType: v, 111 }, 112 }, id) 113 return nil 114 } 115 116 func (d jupyterDisplayer) displayString(contentType, content string, id *string) { 117 d.display(&scaffold.DisplayData{ 118 Data: map[string]interface{}{ 119 contentType: content, 120 }, 121 }, id) 122 } 123 124 func (d jupyterDisplayer) displayBytes(contentType string, content []byte, id *string) { 125 d.display(&scaffold.DisplayData{ 126 Data: map[string]interface{}{ 127 contentType: content, 128 }, 129 }, id) 130 } 131 132 func (d jupyterDisplayer) JavaScript(s string, id *string) { 133 d.displayString("application/javascript", s, id) 134 } 135 func (d jupyterDisplayer) HTML(s string, id *string) { d.displayString("text/html", s, id) } 136 func (d jupyterDisplayer) Markdown(s string, id *string) { d.displayString("text/markdown", s, id) } 137 func (d jupyterDisplayer) Latex(s string, id *string) { d.displayString("text/latex", s, id) } 138 func (d jupyterDisplayer) SVG(s string, id *string) { d.displayString("image/svg+xml", s, id) } 139 func (d jupyterDisplayer) PNG(b []byte, id *string) { d.displayBytes("image/png", b, id) } 140 func (d jupyterDisplayer) JPEG(b []byte, id *string) { d.displayBytes("image/jpeg", b, id) } 141 func (d jupyterDisplayer) GIF(b []byte, id *string) { d.displayBytes("image/gif", b, id) } 142 func (d jupyterDisplayer) PDF(b []byte, id *string) { d.displayBytes("application/pdf", b, id) } 143 func (d jupyterDisplayer) Text(s string, id *string) { d.displayString("text/plain", s, id) } 144 145 func (h *handlers) HandleExecuteRequest(ctx context.Context, r *scaffold.ExecuteRequest, stream func(string, string), displayData func(data *scaffold.DisplayData, update bool)) *scaffold.ExecuteResult { 146 h.execCount++ 147 rDone := make(chan struct{}) 148 soClose, err := pipeOutput(func(msg string) { 149 stream("stdout", msg) 150 }, &os.Stdout, rDone) 151 if err != nil { 152 glog.Errorf("Failed to open stdout pipe: %v", err) 153 return &scaffold.ExecuteResult{ 154 Status: "error", 155 ExecutionCount: h.execCount, 156 } 157 } 158 seClose, err := pipeOutput(func(msg string) { 159 stream("stderr", msg) 160 }, &os.Stderr, rDone) 161 if err != nil { 162 glog.Errorf("Failed to open stderr pipe: %v", err) 163 return &scaffold.ExecuteResult{ 164 Status: "error", 165 ExecutionCount: h.execCount, 166 } 167 } 168 lgoCtx := core.LgoContext{ 169 Context: ctx, Display: jupyterDisplayer(displayData), 170 } 171 func() { 172 defer func() { 173 p := recover() 174 if p != nil { 175 // The return value of debug.Stack() ends with \n. 176 fmt.Fprintf(os.Stderr, "panic: %v\n\n%s", p, debug.Stack()) 177 } 178 }() 179 // Print the err in the notebook 180 if err = h.runner.Run(lgoCtx, r.Code); err != nil { 181 runner.PrintError(os.Stderr, err) 182 } 183 }() 184 soClose() 185 seClose() 186 <-rDone 187 <-rDone 188 if err != nil { 189 return &scaffold.ExecuteResult{ 190 Status: "error", 191 ExecutionCount: h.execCount, 192 } 193 } 194 return &scaffold.ExecuteResult{ 195 Status: "ok", 196 ExecutionCount: h.execCount, 197 } 198 } 199 200 func runeOffsetToByteOffset(s string, roff int) int { 201 var runes int 202 for boff := range s { 203 runes++ 204 if runes > roff { 205 return boff 206 } 207 } 208 return len(s) 209 } 210 211 func (h *handlers) HandleComplete(req *scaffold.CompleteRequest) *scaffold.CompleteReply { 212 // Not implemented 213 offset := runeOffsetToByteOffset(req.Code, req.CursorPos) 214 matches, start, end := h.runner.Complete(context.Background(), req.Code, offset) 215 if len(matches) == 0 { 216 return nil 217 } 218 runeStart := utf8.RuneCountInString(req.Code[:start]) 219 runeEnd := runeStart + utf8.RuneCountInString(req.Code[start:end]) 220 return &scaffold.CompleteReply{ 221 Matches: matches, 222 Status: "ok", 223 CursorStart: runeStart, 224 CursorEnd: runeEnd, 225 } 226 } 227 228 func (h *handlers) HandleInspect(r *scaffold.InspectRequest) *scaffold.InspectReply { 229 doc, err := h.runner.Inspect(context.Background(), r.Code, runeOffsetToByteOffset(r.Code, r.CursorPos)) 230 if err != nil { 231 glog.Errorf("Failed to inspect: %v", err) 232 return nil 233 } 234 if doc == "" { 235 return nil 236 } 237 return &scaffold.InspectReply{ 238 Status: "ok", 239 Found: true, 240 Data: map[string]interface{}{ 241 "text/plain": doc, 242 }, 243 } 244 } 245 246 func (*handlers) HandleIsComplete(req *scaffold.IsCompleteRequest) *scaffold.IsCompleteReply { 247 cont, indent := liner.ContinueLineString(req.Code) 248 if cont { 249 return &scaffold.IsCompleteReply{ 250 Status: "incomplete", 251 // Use 4-spaces instead of "\t" because jupyter console prints "^I" for "\t". 252 Indent: strings.Repeat(" ", indent), 253 } 254 } 255 return &scaffold.IsCompleteReply{ 256 Status: "complete", 257 } 258 } 259 260 func (*handlers) HandleGoFmt(req *scaffold.GoFmtRequest) (*scaffold.GoFmtReply, error) { 261 formatted, err := converter.Format(req.Code) 262 if err != nil { 263 return nil, err 264 } 265 return &scaffold.GoFmtReply{ 266 Status: "ok", 267 Code: formatted, 268 }, nil 269 } 270 271 // kernelLogWriter forwards messages to the current os.Stderr, which is change on every execution. 272 type kernelLogWriter struct{} 273 274 func (kernelLogWriter) Write(p []byte) (n int, err error) { 275 return os.Stderr.Write(p) 276 } 277 278 type glogLogger struct{} 279 280 func (*glogLogger) Info(msg string) { 281 // 0: here, 1: loggerWrapper methods, 2: caller 282 glog.InfoDepth(2, msg) 283 } 284 func (*glogLogger) Warning(msg string) { 285 glog.WarningDepth(2, msg) 286 } 287 func (*glogLogger) Error(msg string) { 288 glog.ErrorDepth(2, msg) 289 } 290 func (*glogLogger) Fatal(msg string) { 291 glog.FatalDepth(2, msg) 292 } 293 294 func kernelMain(lgopath string, sessID *runner.SessionID) { 295 log.SetOutput(kernelLogWriter{}) 296 scaffold.SetLogger(&glogLogger{}) 297 server, err := scaffold.NewServer(context.Background(), *connectionFile, &handlers{ 298 runner: runner.NewLgoRunner(lgopath, sessID), 299 }) 300 if err != nil { 301 glog.Fatalf("Failed to create a server: %v", err) 302 } 303 304 // Start the server loop 305 server.Loop() 306 // clean-up 307 glog.Infof("Clean the session: %s", sessID.Marshal()) 308 runner.CleanSession(lgopath, sessID) 309 }