@@ -406,6 +406,7 @@ func (kernel *Kernel) handleExecuteRequest(receipt msgReceipt) error {
406
406
407
407
jupyterStdOut := JupyterStreamWriter {StreamStdout , & receipt }
408
408
jupyterStdErr := JupyterStreamWriter {StreamStderr , & receipt }
409
+ outerr := OutErr {& jupyterStdOut , & jupyterStdErr }
409
410
410
411
// Forward all data written to stdout/stderr to the front-end.
411
412
go func () {
@@ -428,7 +429,7 @@ func (kernel *Kernel) handleExecuteRequest(receipt msgReceipt) error {
428
429
}()
429
430
430
431
// eval
431
- vals , types , executionErr := doEval (ir , jupyterStdOut , jupyterStdErr , code )
432
+ vals , types , executionErr := doEval (ir , outerr , code )
432
433
433
434
// Close and restore the streams.
434
435
wOut .Close ()
@@ -470,7 +471,7 @@ func (kernel *Kernel) handleExecuteRequest(receipt msgReceipt) error {
470
471
471
472
// doEval evaluates the code in the interpreter. This function captures an uncaught panic
472
473
// as well as the values of the last statement/expression.
473
- func doEval (ir * interp.Interp , jupyterStdOut , jupyterStdErr JupyterStreamWriter , code string ) (val []interface {}, typ []xreflect.Type , err error ) {
474
+ func doEval (ir * interp.Interp , outerr OutErr , code string ) (val []interface {}, typ []xreflect.Type , err error ) {
474
475
475
476
// Capture a panic from the evaluation if one occurs and store it in the `err` return parameter.
476
477
defer func () {
@@ -482,7 +483,7 @@ func doEval(ir *interp.Interp, jupyterStdOut, jupyterStdErr JupyterStreamWriter,
482
483
}
483
484
}()
484
485
485
- code = evalSpecialCommands (ir , jupyterStdOut , jupyterStdErr , code )
486
+ code = evalSpecialCommands (ir , outerr , code )
486
487
487
488
// Prepare and perform the multiline evaluation.
488
489
compiler := ir .Comp
@@ -628,34 +629,43 @@ func startHeartbeat(hbSocket Socket, wg *sync.WaitGroup) (shutdown chan struct{}
628
629
}
629
630
630
631
// find and execute special commands in code, remove them from returned string
631
- func evalSpecialCommands (ir * interp.Interp , jupyterStdOut , jupyterStdErr JupyterStreamWriter , code string ) string {
632
+ func evalSpecialCommands (ir * interp.Interp , outerr OutErr , code string ) string {
632
633
lines := strings .Split (code , "\n " )
634
+ stop := false
633
635
for i , line := range lines {
634
636
line = strings .TrimSpace (line )
635
637
if len (line ) != 0 {
636
638
switch line [0 ] {
637
639
case '%' :
638
- evalSpecialCommand (ir , line )
640
+ evalSpecialCommand (ir , outerr , line )
639
641
lines [i ] = ""
640
642
case '$' :
641
- evalShellCommand (ir , jupyterStdOut , jupyterStdErr , line )
643
+ evalShellCommand (ir , outerr , line )
642
644
lines [i ] = ""
645
+ default :
646
+ // if a line is NOT a special command,
647
+ // stop processing special commands
648
+ stop = true
643
649
}
644
650
}
651
+ if stop {
652
+ break
653
+ }
645
654
}
646
655
return strings .Join (lines , "\n " )
647
656
}
648
657
649
- // execute special command
650
- func evalSpecialCommand (ir * interp.Interp , line string ) {
658
+ // execute special command. line must start with '%'
659
+ func evalSpecialCommand (ir * interp.Interp , outerr OutErr , line string ) {
651
660
const help string = `
652
661
available special commands (%):
653
662
%help
654
663
%go111module {on|off}
655
664
656
- execute shell commands ($):
665
+ execute shell commands ($): $command [args...]
666
+ example:
657
667
$ls -l
658
- `
668
+ `
659
669
660
670
args := strings .SplitN (line , " " , 2 )
661
671
cmd := args [0 ]
@@ -674,53 +684,52 @@ $ls -l
674
684
panic (fmt .Errorf ("special command %s: expecting a single argument 'on' or 'off', found: %q" , cmd , arg ))
675
685
}
676
686
case "%help" :
677
- panic ( help )
687
+ fmt . Fprint ( outerr . out , help )
678
688
default :
679
689
panic (fmt .Errorf ("unknown special command: %q\n %s" , line , help ))
680
690
}
681
691
}
682
692
683
- // execute shell command
684
- func evalShellCommand (ir * interp.Interp , jupyterStdOut , jupyterStdErr JupyterStreamWriter , line string ) {
685
- args := strings .Split (line , " " )
693
+ // execute shell command. line must start with '$'
694
+ func evalShellCommand (ir * interp.Interp , outerr OutErr , line string ) {
695
+ args := strings .Fields (line [ 1 :] )
686
696
if len (args ) <= 0 {
687
697
return
688
698
}
689
699
690
700
var writersWG sync.WaitGroup
691
701
writersWG .Add (2 )
692
702
693
- command := strings .Replace (args [0 ], "$" , "" , 1 )
694
- cmd := exec .Command (command , args [1 :]... )
703
+ cmd := exec .Command (args [0 ], args [1 :]... )
695
704
696
705
stdout , err := cmd .StdoutPipe ()
697
706
if err != nil {
698
- panic (err )
707
+ panic (fmt . Errorf ( "Command.StdoutPipe() failed: %v" , err ) )
699
708
}
700
709
701
710
stderr , err := cmd .StderrPipe ()
702
711
if err != nil {
703
- panic (err )
712
+ panic (fmt . Errorf ( "Command.StderrPipe() failed: %v" , err ) )
704
713
}
705
714
706
715
go func () {
707
716
defer writersWG .Done ()
708
- io .Copy (& jupyterStdOut , stdout )
717
+ io .Copy (outerr . out , stdout )
709
718
}()
710
719
711
720
go func () {
712
721
defer writersWG .Done ()
713
- io .Copy (& jupyterStdErr , stderr )
722
+ io .Copy (outerr . err , stderr )
714
723
}()
715
724
716
725
err = cmd .Start ()
717
726
if err != nil {
718
- panic (err )
727
+ panic (fmt . Errorf ( "error starting command '%s': %v" , line [ 1 :], err ) )
719
728
}
720
729
721
730
err = cmd .Wait ()
722
731
if err != nil {
723
- panic (err )
732
+ panic (fmt . Errorf ( "error waiting for command '%s': %v" , line [ 1 :], err ) )
724
733
}
725
734
726
735
writersWG .Wait ()
0 commit comments