From 7f571f8b452c8c310f6004b429f050f3d84495ad Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Fri, 3 Jul 2026 15:23:14 +0100 Subject: [PATCH] py: fix RunSrc to compile in ExecMode so multi-statement source works RunSrc compiled source in SingleMode, whose interactive grammar only accepts a single top-level statement. Source with more than one statement (e.g. two class definitions) failed with SyntaxError, while the equivalent RunFile path (which uses ExecMode) worked fine. Compile in ExecMode instead, matching RunFile and the semantics of Python's exec() for a source buffer, and add a regression test. Fixes #257 --- py/run.go | 2 +- pytest/runsrc_test.go | 64 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 pytest/runsrc_test.go diff --git a/py/run.go b/py/run.go index cd584fc2..9ec87819 100644 --- a/py/run.go +++ b/py/run.go @@ -131,7 +131,7 @@ func RunSrc(ctx Context, pySrc string, pySrcDesc string, inModule interface{}) ( if pySrcDesc == "" { pySrcDesc = "" } - code, err := Compile(pySrc+"\n", pySrcDesc, SingleMode, 0, true) + code, err := Compile(pySrc+"\n", pySrcDesc, ExecMode, 0, true) if err != nil { return nil, err } diff --git a/pytest/runsrc_test.go b/pytest/runsrc_test.go new file mode 100644 index 00000000..c20b6cf0 --- /dev/null +++ b/pytest/runsrc_test.go @@ -0,0 +1,64 @@ +// Copyright 2026 The go-python Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package pytest + +import ( + "testing" + + "github.com/go-python/gpython/py" + + _ "github.com/go-python/gpython/stdlib" +) + +// Regression test for https://github.com/go-python/gpython/issues/257 +// +// RunSrc used to compile in SingleMode which only accepts a single +// interactive statement, so source containing more than one top-level +// statement (e.g. two class definitions) failed with a SyntaxError. +func TestRunSrcMultipleStatements(t *testing.T) { + for _, test := range []struct { + name string + src string + }{ + { + name: "two classes", + src: ` +class A: + pass + + +class B: + pass +`, + }, + { + name: "two functions", + src: ` +def a(): + return 1 + +def b(): + return 2 +`, + }, + { + name: "assignments and expression", + src: ` +x = 1 +y = 2 +z = x + y +`, + }, + } { + t.Run(test.name, func(t *testing.T) { + ctx := py.NewContext(py.DefaultContextOpts()) + defer ctx.Close() + _, err := py.RunSrc(ctx, test.src, "run", nil) + if err != nil { + t.Fatalf("RunSrc failed: %v", err) + } + }) + } +}