Skip to main content

1645. gopy myreadline

What we are porting

Parser/myreadline.c (~700 lines) is CPython's host-readline shim. It owns:

  • The signal-safe PyOS_Readline entry point that the REPL calls one line at a time.
  • Integration with GNU readline / libedit when present.
  • The fallback for when no readline library is linked (raw stdin with Ctrl-C handling).
  • Atomicity around PyOS_FinalizeReadline.

This file is the smallest of the parser block and the most divergent from CPython, because gopy does not link libreadline. We substitute a Go-native line editor.

Strategy

Two layers:

  1. parser/readline/: the public entry point Readline(prompt) (string, error). The REPL and the FromReadline tokenizer driver (1641) call it.
  2. parser/readline/<backend>.go: backend selected at build time. goreadline.go is the default; we link golang.org/x/term for raw mode and pull in chzyer/readline (or a hand-rolled editor) for history and line editing.

Both layers stay free of CPython header dependencies. They just expose the same (prompt) -> (line, err) shape.

What stays compatible

User-observable behaviour to preserve:

  1. Ctrl-C at the prompt raises KeyboardInterrupt without exiting the REPL.
  2. EOF (Ctrl-D on Unix, Ctrl-Z+Enter on Windows) returns io.EOF from Readline.
  3. The history file path (PYTHONSTARTUP, ~/.python_history) is read and updated in the same locations CPython uses.
  4. Continuation prompt is sys.ps2 ("... " by default), same as primary sys.ps1 (">>> ").
  5. Tab completion hooks into the same rlcompleter-shaped Python-side callback when readline.set_completer is set.

What diverges

  • No GNU readline link. We use a Go-native editor.
  • No libedit fallback path. Goes through the same Go editor.
  • PyOS_InputHook (used by IDLE for Tk integration) becomes a Go callback registry; users targeting IDLE would still need a Go-native shim. Not in scope for v0.9.

Go shape

// Readline reads one line from the user, displaying prompt.
// Mirrors PyOS_Readline. Returns io.EOF on EOF.
func Readline(prompt string) (string, error)

// SetCompleter registers a tab-completion callback.
// Mirrors readline.set_completer.
func SetCompleter(fn func(text string, state int) (string, bool))

// LoadHistory reads the history file. Returns nil if the file
// does not exist.
func LoadHistory(path string) error

// SaveHistory appends the in-memory history to path.
func SaveHistory(path string) error

File mapping

C sourceGo target
Parser/myreadline.cparser/readline/readline.go
(no CPython equivalent)parser/readline/editor.go (backend)
(no CPython equivalent)parser/readline/history.go

Checklist

Status legend: [x] shipped, [ ] pending, [~] partial / scaffold, [n] deferred / not in scope this phase.

Files

  • parser/readline/readline.go: Readline, signal handling, prompt rendering.
  • parser/readline/editor.go: terminal raw-mode driver, arrow-key navigation, line editing primitives.
  • parser/readline/history.go: LoadHistory, SaveHistory, in-memory ring buffer.
  • parser/readline/completer.go: SetCompleter, tab expansion plumbing.
  • parser/readline/readline_test.go: scripted-input panel (no real terminal needed).

Surface guarantees

  • Ctrl-C at the prompt raises KeyboardInterrupt.
  • Ctrl-D on an empty line returns io.EOF.
  • History file path defaults to ~/.python_history, overridable via PYTHONSTARTUP.
  • sys.ps1 / sys.ps2 thread through unchanged.
  • Tab completion calls the registered completer with the same (text, state) protocol as CPython.

Out of scope

  • IDLE / Tkinter PyOS_InputHook. Tracked separately if/when IDLE support is in scope.
  • GNU readline binary compatibility. We never link libreadline.
  • Bracketed paste mode beyond what the Go editor library provides.

Cross-references

  • REPL entry point: separate spec series (sysconfig / interpreter loop, not yet numbered).
  • Tokenizer driver that consumes Readline: 1641.