From e7dcdff81dcefb0a9a0096c4a2cc857d67697f2e Mon Sep 17 00:00:00 2001 From: AN Long Date: Mon, 30 Jun 2025 00:57:55 +0900 Subject: [PATCH 1/2] all: handle SystemExit --- main.go | 30 ++++++++++++++++++++++++++++++ repl/repl.go | 31 +++++++++++++++++++++++++++++++ stdlib/sys/sys.go | 6 +++++- 3 files changed, 66 insertions(+), 1 deletion(-) diff --git a/main.go b/main.go index 8be7ea2e..99580354 100644 --- a/main.go +++ b/main.go @@ -74,6 +74,36 @@ func xmain(args []string) { } else { _, err := py.RunFile(ctx, args[0], py.CompileOpts{}, nil) if err != nil { + if py.IsException(py.SystemExit, err) { + args := err.(py.ExceptionInfo).Value.(*py.Exception).Args.(py.Tuple) + if len(args) == 0 { + os.Exit(0) + } else if len(args) == 1 { + if code, ok := args[0].(py.Int); ok { + c, err := code.GoInt() + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + os.Exit(c) + } + msg, err := py.ReprAsString(args[0]) + if err != nil { + fmt.Fprintln(os.Stderr, err) + } else { + fmt.Fprintln(os.Stderr, msg) + } + os.Exit(1) + } else { + msg, err := py.ReprAsString(args) + if err != nil { + fmt.Fprintln(os.Stderr, err) + } else { + fmt.Fprintln(os.Stderr, msg) + } + os.Exit(1) + } + } py.TracebackDump(err) os.Exit(1) } diff --git a/repl/repl.go b/repl/repl.go index f6639b25..faf9859d 100644 --- a/repl/repl.go +++ b/repl/repl.go @@ -7,6 +7,7 @@ package repl import ( "fmt" + "os" "sort" "strings" @@ -109,6 +110,36 @@ func (r *REPL) Run(line string) { } _, err = r.Context.RunCode(code, r.Module.Globals, r.Module.Globals, nil) if err != nil { + if py.IsException(py.SystemExit, err) { + args := err.(py.ExceptionInfo).Value.(*py.Exception).Args.(py.Tuple) + if len(args) == 0 { + os.Exit(0) + } else if len(args) == 1 { + if code, ok := args[0].(py.Int); ok { + c, err := code.GoInt() + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + os.Exit(c) + } + msg, err := py.ReprAsString(args[0]) + if err != nil { + fmt.Fprintln(os.Stderr, err) + } else { + fmt.Fprintln(os.Stderr, msg) + } + os.Exit(1) + } else { + msg, err := py.ReprAsString(args) + if err != nil { + fmt.Fprintln(os.Stderr, err) + } else { + fmt.Fprintln(os.Stderr, msg) + } + os.Exit(1) + } + } py.TracebackDump(err) } } diff --git a/stdlib/sys/sys.go b/stdlib/sys/sys.go index 3a2318eb..fc6efc5a 100644 --- a/stdlib/sys/sys.go +++ b/stdlib/sys/sys.go @@ -133,7 +133,11 @@ func sys_exit(self py.Object, args py.Tuple) (py.Object, error) { return nil, err } // Raise SystemExit so callers may catch it or clean up. - return py.ExceptionNew(py.SystemExit, args, nil) + exc, err := py.ExceptionNew(py.SystemExit, args, nil) + if err != nil { + return nil, err + } + return nil, exc.(*py.Exception) } const getdefaultencoding_doc = `getdefaultencoding() -> string From ecdd85c5c8a70e999fbfce4eab385c4d603c6ab3 Mon Sep 17 00:00:00 2001 From: AN Long Date: Thu, 3 Jul 2025 21:52:48 +0900 Subject: [PATCH 2/2] Reduce code duplication --- main.go | 72 ++++++++++++++++++++++++++----------------------- repl/cli/cli.go | 8 ++++-- repl/repl.go | 41 +++++----------------------- 3 files changed, 51 insertions(+), 70 deletions(-) diff --git a/main.go b/main.go index 99580354..8b55ab1e 100644 --- a/main.go +++ b/main.go @@ -60,6 +60,7 @@ func xmain(args []string) { defer pprof.StopCPUProfile() } + var err error // IF no args, enter REPL mode if len(args) == 0 { @@ -69,43 +70,46 @@ func xmain(args []string) { fmt.Printf("- go version: %s\n", runtime.Version()) replCtx := repl.New(ctx) - cli.RunREPL(replCtx) + err = cli.RunREPL(replCtx) + } else { + _, err = py.RunFile(ctx, args[0], py.CompileOpts{}, nil) + } + if err != nil { + if py.IsException(py.SystemExit, err) { + handleSystemExit(err.(py.ExceptionInfo).Value.(*py.Exception)) + } + py.TracebackDump(err) + os.Exit(1) + } +} +func handleSystemExit(exc *py.Exception) { + args := exc.Args.(py.Tuple) + if len(args) == 0 { + os.Exit(0) + } else if len(args) == 1 { + if code, ok := args[0].(py.Int); ok { + c, err := code.GoInt() + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } + os.Exit(c) + } + msg, err := py.ReprAsString(args[0]) + if err != nil { + fmt.Fprintln(os.Stderr, err) + } else { + fmt.Fprintln(os.Stderr, msg) + } + os.Exit(1) } else { - _, err := py.RunFile(ctx, args[0], py.CompileOpts{}, nil) + msg, err := py.ReprAsString(args) if err != nil { - if py.IsException(py.SystemExit, err) { - args := err.(py.ExceptionInfo).Value.(*py.Exception).Args.(py.Tuple) - if len(args) == 0 { - os.Exit(0) - } else if len(args) == 1 { - if code, ok := args[0].(py.Int); ok { - c, err := code.GoInt() - if err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) - } - os.Exit(c) - } - msg, err := py.ReprAsString(args[0]) - if err != nil { - fmt.Fprintln(os.Stderr, err) - } else { - fmt.Fprintln(os.Stderr, msg) - } - os.Exit(1) - } else { - msg, err := py.ReprAsString(args) - if err != nil { - fmt.Fprintln(os.Stderr, err) - } else { - fmt.Fprintln(os.Stderr, msg) - } - os.Exit(1) - } - } - py.TracebackDump(err) - os.Exit(1) + fmt.Fprintln(os.Stderr, err) + } else { + fmt.Fprintln(os.Stderr, msg) } + os.Exit(1) } } diff --git a/repl/cli/cli.go b/repl/cli/cli.go index 6648094a..6f7e3966 100644 --- a/repl/cli/cli.go +++ b/repl/cli/cli.go @@ -117,7 +117,7 @@ func (rl *readline) Print(out string) { } // RunREPL starts the REPL loop -func RunREPL(replCtx *repl.REPL) { +func RunREPL(replCtx *repl.REPL) error { if replCtx == nil { replCtx = repl.New(nil) } @@ -144,6 +144,10 @@ func RunREPL(replCtx *repl.REPL) { if line != "" { rl.AppendHistory(line) } - rl.repl.Run(line) + err = rl.repl.Run(line) + if err != nil { + return err + } } + return nil } diff --git a/repl/repl.go b/repl/repl.go index faf9859d..3938a7b6 100644 --- a/repl/repl.go +++ b/repl/repl.go @@ -7,7 +7,6 @@ package repl import ( "fmt" - "os" "sort" "strings" @@ -67,7 +66,7 @@ func (r *REPL) SetUI(term UI) { } // Run runs a single line of the REPL -func (r *REPL) Run(line string) { +func (r *REPL) Run(line string) error { // Override the PrintExpr output temporarily oldPrintExpr := vm.PrintExpr vm.PrintExpr = r.term.Print @@ -77,13 +76,13 @@ func (r *REPL) Run(line string) { if r.continuation { if line != "" { r.previous += string(line) + "\n" - return + return nil } } // need +"\n" because "single" expects \n terminated input toCompile := r.previous + string(line) if toCompile == "" { - return + return nil } code, err := py.Compile(toCompile+"\n", r.prog, py.SingleMode, 0, true) if err != nil { @@ -98,7 +97,7 @@ func (r *REPL) Run(line string) { r.previous += string(line) + "\n" r.term.SetPrompt(ContinuationPrompt) } - return + return nil } } r.continuation = false @@ -106,42 +105,16 @@ func (r *REPL) Run(line string) { r.previous = "" if err != nil { r.term.Print(fmt.Sprintf("Compile error: %v", err)) - return + return nil } _, err = r.Context.RunCode(code, r.Module.Globals, r.Module.Globals, nil) if err != nil { if py.IsException(py.SystemExit, err) { - args := err.(py.ExceptionInfo).Value.(*py.Exception).Args.(py.Tuple) - if len(args) == 0 { - os.Exit(0) - } else if len(args) == 1 { - if code, ok := args[0].(py.Int); ok { - c, err := code.GoInt() - if err != nil { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) - } - os.Exit(c) - } - msg, err := py.ReprAsString(args[0]) - if err != nil { - fmt.Fprintln(os.Stderr, err) - } else { - fmt.Fprintln(os.Stderr, msg) - } - os.Exit(1) - } else { - msg, err := py.ReprAsString(args) - if err != nil { - fmt.Fprintln(os.Stderr, err) - } else { - fmt.Fprintln(os.Stderr, msg) - } - os.Exit(1) - } + return err } py.TracebackDump(err) } + return nil } // WordCompleter takes the currently edited line with the cursor