Skip to content
Open
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
5 changes: 5 additions & 0 deletions py/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ var (
// Compiles a python buffer into a py.Code object.
// Returns a py.Code object or otherwise an error.
Compile func(src, srcDesc string, mode CompileMode, flags int, dont_inherit bool) (*Code, error)

// InputHook is an optional function that can be set to provide a custom input
// mechanism for the input() builtin. If nil, input() reads from sys.stdin.
// This is used by the REPL to integrate with the liner library.
InputHook func(prompt string) (string, error)
)

// RunFile resolves the given pathname, compiles as needed, executes the code in the given module, and returns the Module to indicate success.
Expand Down
8 changes: 8 additions & 0 deletions repl/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"os/user"
"path/filepath"

"github.com/go-python/gpython/py"
"github.com/go-python/gpython/repl"
"github.com/peterh/liner"
)
Expand Down Expand Up @@ -124,6 +125,13 @@ func RunREPL(replCtx *repl.REPL) error {
rl := newReadline(replCtx)
replCtx.SetUI(rl)
defer rl.Close()

// Set up InputHook for the input() builtin function
py.InputHook = func(prompt string) (string, error) {
return rl.Prompt(prompt)
}
defer func() { py.InputHook = nil }()

err := rl.ReadHistory()
if err != nil {
if !os.IsNotExist(err) {
Expand Down
80 changes: 79 additions & 1 deletion stdlib/builtin/builtin.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
package builtin

import (
"bufio"
"fmt"
"io"
"math/big"
"strconv"
"unicode/utf8"
Expand Down Expand Up @@ -44,7 +46,7 @@ func init() {
// py.MustNewMethod("hash", builtin_hash, 0, hash_doc),
py.MustNewMethod("hex", builtin_hex, 0, hex_doc),
// py.MustNewMethod("id", builtin_id, 0, id_doc),
// py.MustNewMethod("input", builtin_input, 0, input_doc),
py.MustNewMethod("input", builtin_input, 0, input_doc),
py.MustNewMethod("isinstance", builtin_isinstance, 0, isinstance_doc),
// py.MustNewMethod("issubclass", builtin_issubclass, 0, issubclass_doc),
py.MustNewMethod("iter", builtin_iter, 0, iter_doc),
Expand Down Expand Up @@ -1181,6 +1183,82 @@ func builtin_chr(self py.Object, args py.Tuple) (py.Object, error) {
return py.String(buf[:n]), nil
}

const input_doc = `input([prompt]) -> string

Read a string from standard input. The trailing newline is stripped.
The prompt string, if given, is printed to standard output without a
trailing newline before reading input.
If the user hits EOF (*nix: Ctrl-D, Windows: Ctrl-Z+Return), raise EOFError.
On *nix systems, GNU readline is used if enabled.`

func builtin_input(self py.Object, args py.Tuple) (py.Object, error) {
var prompt py.Object = py.None

err := py.UnpackTuple(args, nil, "input", 0, 1, &prompt)
if err != nil {
return nil, err
}

// Use InputHook if available (e.g., in REPL mode)
if py.InputHook != nil {
promptStr := ""
if prompt != py.None {
promptStr = string(prompt.(py.String))
}
line, err := py.InputHook(promptStr)
if err != nil {
if err == io.EOF {
return nil, py.ExceptionNewf(py.EOFError, "EOF when reading a line")
}
return nil, err
}
return py.String(line), nil
}

sysModule, err := self.(*py.Module).Context.GetModule("sys")
if err != nil {
return nil, err
}

stdin := sysModule.Globals["stdin"]
stdout := sysModule.Globals["stdout"]

if prompt != py.None {
write, err := py.GetAttrString(stdout, "write")
if err != nil {
return nil, err
}
_, err = py.Call(write, py.Tuple{prompt}, nil)
if err != nil {
return nil, err
}

flush, err := py.GetAttrString(stdout, "flush")
if err == nil {
py.Call(flush, nil, nil)
}
}

file := stdin.(*py.File)
reader := bufio.NewReader(file.File)
line, err := reader.ReadString('\n')
if err != nil {
if err.Error() == "EOF" {
return nil, py.ExceptionNewf(py.EOFError, "EOF when reading a line")
}
return nil, err
}

if len(line) > 0 && line[len(line)-1] == '\n' {
line = line[:len(line)-1]
if len(line) > 0 && line[len(line)-1] == '\r' {
line = line[:len(line)-1]
}
}

return py.String(line), nil
}

const locals_doc = `locals() -> dictionary

Update and return a dictionary containing the current scope's local variables.`
Expand Down
Loading