Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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.`
Comment thread
aisk marked this conversation as resolved.
Outdated

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))
}
Comment thread
aisk marked this conversation as resolved.
line, err := py.InputHook(promptStr)
if err != nil {
if err == io.EOF {
return nil, py.ExceptionNewf(py.EOFError, "EOF when reading a line")
}
Comment thread
aisk marked this conversation as resolved.
Outdated
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)
}
}
Comment thread
sbinet marked this conversation as resolved.

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")
}
Comment thread
aisk marked this conversation as resolved.
Outdated
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