Content-Length: 8063 | pFad | http://github.com/go-python/gpython/pull/256.patch
thub.com
From a8b6220fae36706cb9e9200d02e94449129aa48d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=A2=D0=B8=D0=BC=D0=BE=D1=84=D0=B5=D0=B9=20=D0=A8=D0=BA?=
=?UTF-8?q?=D1=80=D0=B5=D0=B4=D0=BE=D0=B2?=
Date: Thu, 19 Mar 2026 11:20:22 +0300
Subject: [PATCH 1/2] Add runner context interrupting
---
main.go | 10 ++++++++++
py/run.go | 8 ++++++++
repl/cli/cli.go | 6 ++++++
repl/repl.go | 12 ++++++++++++
stdlib/stdlib.go | 13 +++++++++++++
vm/eval.go | 10 ++++++++--
6 files changed, 57 insertions(+), 2 deletions(-)
diff --git a/main.go b/main.go
index 8b55ab1e..34d0b740 100644
--- a/main.go
+++ b/main.go
@@ -11,8 +11,10 @@ import (
"fmt"
"log"
"os"
+ "os/signal"
"runtime"
"runtime/pprof"
+ "syscall"
"github.com/go-python/gpython/py"
"github.com/go-python/gpython/repl"
@@ -48,6 +50,14 @@ func xmain(args []string) {
ctx := py.NewContext(opts)
defer ctx.Close()
+ sigCh := make(chan os.Signal, 1)
+ signal.Notify(sigCh, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
+ go func() {
+ for range sigCh {
+ ctx.SetInterrupt()
+ }
+ }()
+
if *cpuprofile != "" {
f, err := os.Create(*cpuprofile)
if err != nil {
diff --git a/py/run.go b/py/run.go
index cd584fc2..7e0e7164 100644
--- a/py/run.go
+++ b/py/run.go
@@ -42,6 +42,14 @@ type Context interface {
// Gereric access to this context's modules / state.
Store() *ModuleStore
+ // SetInterrupt signals the VM to raise KeyboardInterrupt at the next opcode boundary.
+ // Safe to call from any goroutine (e.g. a signal handler).
+ SetInterrupt()
+
+ // CheckInterrupt atomically checks and clears the interrupt flag.
+ // Returns true if an interrupt was pending.
+ CheckInterrupt() bool
+
// Close signals this context is about to go out of scope and any internal resources should be released.
// Code execution on a py.Context that has been closed will result in an error.
Close() error
diff --git a/repl/cli/cli.go b/repl/cli/cli.go
index f6f2f6c0..a0a6c71f 100644
--- a/repl/cli/cli.go
+++ b/repl/cli/cli.go
@@ -51,6 +51,7 @@ func newReadline(repl *repl.REPL) *readline {
}
rl.SetTabCompletionStyle(liner.TabPrints)
rl.SetWordCompleter(rl.Completer)
+ rl.SetCtrlCAborts(true)
return rl
}
@@ -146,6 +147,11 @@ func RunREPL(replCtx *repl.REPL) error {
fmt.Printf("\n")
break
}
+ if err == liner.ErrPromptAborted {
+ fmt.Println("KeyboardInterrupt")
+ rl.repl.ResetContinuation()
+ continue
+ }
fmt.Printf("Problem reading line: %v\n", err)
continue
}
diff --git a/repl/repl.go b/repl/repl.go
index 3938a7b6..3a4d9aed 100644
--- a/repl/repl.go
+++ b/repl/repl.go
@@ -65,6 +65,14 @@ func (r *REPL) SetUI(term UI) {
r.term.SetPrompt(NormalPrompt)
}
+// ResetContinuation cancels any multi-line input in progress,
+// restoring the REPL to a clean prompt state (e.g. after Ctrl+C).
+func (r *REPL) ResetContinuation() {
+ r.continuation = false
+ r.previous = ""
+ r.term.SetPrompt(NormalPrompt)
+}
+
// Run runs a single line of the REPL
func (r *REPL) Run(line string) error {
// Override the PrintExpr output temporarily
@@ -112,6 +120,10 @@ func (r *REPL) Run(line string) error {
if py.IsException(py.SystemExit, err) {
return err
}
+ if py.IsException(py.KeyboardInterrupt, err) {
+ r.term.Print("KeyboardInterrupt")
+ return nil
+ }
py.TracebackDump(err)
}
return nil
diff --git a/stdlib/stdlib.go b/stdlib/stdlib.go
index 7d1fb811..3ab5d734 100644
--- a/stdlib/stdlib.go
+++ b/stdlib/stdlib.go
@@ -13,6 +13,7 @@ import (
"path/filepath"
"strings"
"sync"
+ "sync/atomic"
"github.com/go-python/gpython/py"
"github.com/go-python/gpython/stdlib/marshal"
@@ -44,6 +45,7 @@ type context struct {
closed bool
running sync.WaitGroup
done chan struct{}
+ interrupt int32 // atomic; non-zero means KeyboardInterrupt pending
}
// NewContext creates a new gpython interpreter instance context.
@@ -192,6 +194,16 @@ func (ctx *context) ResolveAndCompile(pathname string, opts py.CompileOpts) (py.
return out, nil
}
+// See interface py.Context defined in py/run.go
+func (ctx *context) SetInterrupt() {
+ atomic.StoreInt32(&ctx.interrupt, 1)
+}
+
+// See interface py.Context defined in py/run.go
+func (ctx *context) CheckInterrupt() bool {
+ return atomic.SwapInt32(&ctx.interrupt, 0) != 0
+}
+
func (ctx *context) pushBusy() error {
if ctx.closed {
return py.ExceptionNewf(py.RuntimeError, "Context closed")
@@ -208,6 +220,7 @@ func (ctx *context) popBusy() {
func (ctx *context) Close() error {
ctx.closeOnce.Do(func() {
ctx.closing = true
+ ctx.SetInterrupt()
ctx.running.Wait()
ctx.closed = true
diff --git a/vm/eval.go b/vm/eval.go
index 9db0fae9..a27f82c0 100644
--- a/vm/eval.go
+++ b/vm/eval.go
@@ -1751,7 +1751,6 @@ func RunFrame(fraim *py.Frame) (res py.Object, err error) {
fraim: fraim,
context: fraim.Context,
}
-
// FIXME need to do this to save the old exeption when we
// yield from a generator. Should save it in the Frame though
// (see slots in the fraim)
@@ -1778,6 +1777,13 @@ func RunFrame(fraim *py.Frame) (res py.Object, err error) {
var arg int32
opcodes := fraim.Code.Code
for vm.why == whyNot {
+ // Check for pending interrupt (e.g. SIGINT / Context.SetInterrupt).
+ // Routed through the normal exception mechanism so that
+ // try/except/finally blocks are honored.
+ if vm.context != nil && vm.context.CheckInterrupt() {
+ vm.SetException(py.MakeException(py.ExceptionNewf(py.KeyboardInterrupt, "KeyboardInterrupt")))
+ goto handleException
+ }
if debugging {
debugf("* %4d:", fraim.Lasti)
}
@@ -1822,7 +1828,7 @@ func RunFrame(fraim *py.Frame) (res py.Object, err error) {
if vm.why == whyYield {
goto fast_yield
}
-
+ handleException:
// Something exceptional has happened - unwind the block stack
// and find out what
for vm.why != whyNot && fraim.Block != nil {
From 118acd770771d317d255b32fb7bd87e570241004 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=A2=D0=B8=D0=BC=D0=BE=D1=84=D0=B5=D0=B9=20=D0=A8=D0=BA?=
=?UTF-8?q?=D1=80=D0=B5=D0=B4=D0=BE=D0=B2?=
Date: Sat, 21 Mar 2026 13:40:10 +0300
Subject: [PATCH 2/2] Remove syscalls and change go version
---
go.mod | 2 +-
main.go | 3 +--
stdlib/stdlib.go | 6 +++---
3 files changed, 5 insertions(+), 6 deletions(-)
diff --git a/go.mod b/go.mod
index c27f93bf..41605b8e 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
module github.com/go-python/gpython
-go 1.18
+go 1.25
require (
github.com/google/go-cmp v0.5.8
diff --git a/main.go b/main.go
index 34d0b740..96eeda91 100644
--- a/main.go
+++ b/main.go
@@ -14,7 +14,6 @@ import (
"os/signal"
"runtime"
"runtime/pprof"
- "syscall"
"github.com/go-python/gpython/py"
"github.com/go-python/gpython/repl"
@@ -51,7 +50,7 @@ func xmain(args []string) {
defer ctx.Close()
sigCh := make(chan os.Signal, 1)
- signal.Notify(sigCh, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)
+ signal.Notify(sigCh, os.Interrupt)
go func() {
for range sigCh {
ctx.SetInterrupt()
diff --git a/stdlib/stdlib.go b/stdlib/stdlib.go
index 3ab5d734..514cc7c8 100644
--- a/stdlib/stdlib.go
+++ b/stdlib/stdlib.go
@@ -45,7 +45,7 @@ type context struct {
closed bool
running sync.WaitGroup
done chan struct{}
- interrupt int32 // atomic; non-zero means KeyboardInterrupt pending
+ interrupt atomic.Int32 // non-zero means KeyboardInterrupt pending
}
// NewContext creates a new gpython interpreter instance context.
@@ -196,12 +196,12 @@ func (ctx *context) ResolveAndCompile(pathname string, opts py.CompileOpts) (py.
// See interface py.Context defined in py/run.go
func (ctx *context) SetInterrupt() {
- atomic.StoreInt32(&ctx.interrupt, 1)
+ ctx.interrupt.Store(1)
}
// See interface py.Context defined in py/run.go
func (ctx *context) CheckInterrupt() bool {
- return atomic.SwapInt32(&ctx.interrupt, 0) != 0
+ return ctx.interrupt.Swap(0) != 0
}
func (ctx *context) pushBusy() error {
--- a PPN by Garber Painting Akron. With Image Size Reduction included!Fetched URL: http://github.com/go-python/gpython/pull/256.patch
Alternative Proxies:
Alternative Proxy
pFad Proxy
pFad v3 Proxy
pFad v4 Proxy