1645. gopy myreadline
What we are porting
Parser/myreadline.c (~700 lines) is CPython's host-readline
shim. It owns:
- The signal-safe
PyOS_Readlineentry 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:
parser/readline/: the public entry pointReadline(prompt) (string, error). The REPL and theFromReadlinetokenizer driver (1641) call it.parser/readline/<backend>.go: backend selected at build time.goreadline.gois the default; we linkgolang.org/x/termfor raw mode and pull inchzyer/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:
- Ctrl-C at the prompt raises KeyboardInterrupt without exiting the REPL.
- EOF (Ctrl-D on Unix, Ctrl-Z+Enter on Windows) returns
io.EOFfromReadline. - The history file path (
PYTHONSTARTUP,~/.python_history) is read and updated in the same locations CPython uses. - Continuation prompt is
sys.ps2("... "by default), same as primarysys.ps1(">>> "). - Tab completion hooks into the same
rlcompleter-shaped Python-side callback whenreadline.set_completeris 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 source | Go target |
|---|---|
Parser/myreadline.c | parser/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 viaPYTHONSTARTUP. -
sys.ps1/sys.ps2thread 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.