Skip to content
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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
gophernotes
.ipynb_checkpoints
Untitled.ipynb
Untitled*.ipynb
81 changes: 71 additions & 10 deletions kernel.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"io/ioutil"
"log"
"os"
"os/exec"
"reflect"
"runtime"
"strings"
Expand Down Expand Up @@ -403,16 +404,17 @@ func (kernel *Kernel) handleExecuteRequest(receipt msgReceipt) error {
var writersWG sync.WaitGroup
writersWG.Add(2)

jupyterStdOut := JupyterStreamWriter{StreamStdout, &receipt}
jupyterStdErr := JupyterStreamWriter{StreamStderr, &receipt}

// Forward all data written to stdout/stderr to the front-end.
go func() {
defer writersWG.Done()
jupyterStdOut := JupyterStreamWriter{StreamStdout, &receipt}
io.Copy(&jupyterStdOut, rOut)
}()

go func() {
defer writersWG.Done()
jupyterStdErr := JupyterStreamWriter{StreamStderr, &receipt}
io.Copy(&jupyterStdErr, rErr)
}()

Expand All @@ -426,7 +428,7 @@ func (kernel *Kernel) handleExecuteRequest(receipt msgReceipt) error {
}()

// eval
vals, types, executionErr := doEval(ir, code)
vals, types, executionErr := doEval(ir, jupyterStdOut, jupyterStdErr, code)

// Close and restore the streams.
wOut.Close()
Expand Down Expand Up @@ -468,7 +470,7 @@ func (kernel *Kernel) handleExecuteRequest(receipt msgReceipt) error {

// doEval evaluates the code in the interpreter. This function captures an uncaught panic
// as well as the values of the last statement/expression.
func doEval(ir *interp.Interp, code string) (val []interface{}, typ []xreflect.Type, err error) {
func doEval(ir *interp.Interp, jupyterStdOut, jupyterStdErr JupyterStreamWriter, code string) (val []interface{}, typ []xreflect.Type, err error) {

// Capture a panic from the evaluation if one occurs and store it in the `err` return parameter.
defer func() {
Expand All @@ -480,7 +482,7 @@ func doEval(ir *interp.Interp, code string) (val []interface{}, typ []xreflect.T
}
}()

code = evalSpecialCommands(ir, code)
code = evalSpecialCommands(ir, jupyterStdOut, jupyterStdErr, code)

// Prepare and perform the multiline evaluation.
compiler := ir.Comp
Expand Down Expand Up @@ -626,21 +628,34 @@ func startHeartbeat(hbSocket Socket, wg *sync.WaitGroup) (shutdown chan struct{}
}

// find and execute special commands in code, remove them from returned string
func evalSpecialCommands(ir *interp.Interp, code string) string {
func evalSpecialCommands(ir *interp.Interp, jupyterStdOut, jupyterStdErr JupyterStreamWriter, code string) string {
lines := strings.Split(code, "\n")
for i, line := range lines {
line = strings.TrimSpace(line)
if len(line) != 0 && line[0] == '%' {
evalSpecialCommand(ir, line)
lines[i] = ""
if len(line) != 0 {
switch line[0] {
case '%':
evalSpecialCommand(ir, line)
lines[i] = ""
case '$':
evalShellCommand(ir, jupyterStdOut, jupyterStdErr, line)
lines[i] = ""
}
}
}
return strings.Join(lines, "\n")
}

// execute special command
func evalSpecialCommand(ir *interp.Interp, line string) {
const help string = "available special commands:\n %go111module {on|off}\n %help"
const help string = `
available special commands (%):
%help
%go111module {on|off}

execute shell commands ($):
$ls -l
`

args := strings.SplitN(line, " ", 2)
cmd := args[0]
Expand All @@ -664,3 +679,49 @@ func evalSpecialCommand(ir *interp.Interp, line string) {
panic(fmt.Errorf("unknown special command: %q\n%s", line, help))
}
}

// execute shell command
func evalShellCommand(ir *interp.Interp, jupyterStdOut, jupyterStdErr JupyterStreamWriter, line string) {
args := strings.Split(line, " ")
if len(args) <= 0 {
return
}

var writersWG sync.WaitGroup
writersWG.Add(2)

command := strings.Replace(args[0], "$", "", 1)
cmd := exec.Command(command, args[1:]...)

stdout, err := cmd.StdoutPipe()
if err != nil {
panic(err)
}

stderr, err := cmd.StderrPipe()
if err != nil {
panic(err)
}

go func() {
defer writersWG.Done()
io.Copy(&jupyterStdOut, stdout)
}()

go func() {
defer writersWG.Done()
io.Copy(&jupyterStdErr, stderr)
}()

err = cmd.Start()
if err != nil {
panic(err)
}

err = cmd.Wait()
if err != nil {
panic(err)
}

writersWG.Wait()
}