From d570175cc96b8a1b581990676ff6de6ededf381e Mon Sep 17 00:00:00 2001 From: raiden00pl Date: Sat, 22 Aug 2026 07:42:22 +0200 Subject: [PATCH] interpreters/python: use NuttX readline for TTY input CPython's default TTY reader bypasses NuttX editing and control-key handling. Signed-off-by: raiden00pl Assisted-by: Claude Code --- interpreters/python/python_wrapper.c | 116 +++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/interpreters/python/python_wrapper.c b/interpreters/python/python_wrapper.c index be7454514..5bd80929e 100644 --- a/interpreters/python/python_wrapper.c +++ b/interpreters/python/python_wrapper.c @@ -45,6 +45,10 @@ #include +#ifdef CONFIG_SYSTEM_READLINE +# include +#endif + #include "romfs_cpython_modules.h" #include "Python.h" @@ -80,6 +84,17 @@ #define MKMOUNT_DEVNAME(m) "/dev/ram" STR_RAMDEVNO(m) #define MOUNT_DEVNAME MKMOUNT_DEVNAME(CONFIG_CPYTHON_ROMFS_RAMDEVNO) +#ifdef CONFIG_SYSTEM_READLINE +# if CONFIG_LINE_MAX > 2 +# define PYTHON_READLINE_BUFSIZE CONFIG_LINE_MAX +# else +# define PYTHON_READLINE_BUFSIZE 2 +# endif + +# define PYTHON_READLINE_OPTIONS \ + (READLINE_CTRL_D_EOF | READLINE_RETURN_ON_EINTR) +#endif + /**************************************************************************** * Private Types ****************************************************************************/ @@ -92,6 +107,103 @@ * Private Functions ****************************************************************************/ +#ifdef CONFIG_SYSTEM_READLINE +/**************************************************************************** + * Name: python_readline + * + * Description: + * Read an interactive line with the NuttX line editor. CPython calls + * this hook only for a TTY attached to the main interpreter, leaving its + * standard fgets-based reader in place for redirected input and + * subinterpreters. + * + ****************************************************************************/ + +static char *python_readline(FILE *sys_stdin, FILE *sys_stdout, + const char *prompt) +{ +#if defined(CONFIG_READLINE_TABCOMPLETION) || defined(CONFIG_READLINE_EDIT) + FAR const char *oldprompt; +#endif + FAR char *line; + FAR char *tmp; + size_t linesize; + size_t used; + ssize_t nread; + + fflush(sys_stdout); + + if (prompt != NULL) + { + fputs(prompt, stderr); + } + + fflush(stderr); + + linesize = PYTHON_READLINE_BUFSIZE; + line = PyMem_RawMalloc(linesize); + if (line == NULL) + { + return NULL; + } + + used = 0; + +#if defined(CONFIG_READLINE_TABCOMPLETION) || defined(CONFIG_READLINE_EDIT) + oldprompt = readline_prompt(prompt); +#endif + + for (; ; ) + { + nread = readline_fd_ex(line + used, PYTHON_READLINE_BUFSIZE, + fileno(sys_stdin), fileno(sys_stdout), + PYTHON_READLINE_OPTIONS); + if (nread == -EINTR) + { + PyMem_RawFree(line); + line = NULL; + break; + } + + if (nread == EOF) + { + line[used] = '\0'; + break; + } + + used += nread; + if (used > 0 && line[used - 1] == '\n') + { + break; + } + + if (used > SIZE_MAX - PYTHON_READLINE_BUFSIZE) + { + PyMem_RawFree(line); + line = NULL; + break; + } + + linesize = used + PYTHON_READLINE_BUFSIZE; + tmp = PyMem_RawRealloc(line, linesize); + if (tmp == NULL) + { + PyMem_RawFree(line); + line = NULL; + break; + } + + line = tmp; + } + +#if defined(CONFIG_READLINE_TABCOMPLETION) || defined(CONFIG_READLINE_EDIT) + readline_prompt(oldprompt); +#endif + + return line; +} +#endif + /**************************************************************************** * Name: check_and_mount_romfs * @@ -194,6 +306,10 @@ int main(int argc, FAR char *argv[]) _pyruntime_early_init(); +#ifdef CONFIG_SYSTEM_READLINE + PyOS_ReadlineFunctionPointer = python_readline; +#endif + setenv("PYTHONHOME", "/usr/local", 1); setenv("PYTHON_BASIC_REPL", "1", 1);