3

I'm writing a multithreaded program with an interactive console:

def console()
    import readline
    while True:
        data = input()
        do_whatever(data.split())

However, a library I'm using runs my callbacks from a different thread. The callback needs to print to the console. Thus I want to clear the command line, re-display the prompt, and re-show the command line.

How do I do that, short of re-implementing readline?

4
  • Do you need your callback to print to stdout/stderr? One quick and ugly hack is to print many 'new lines' to refresh the whole screen. or execute system command like "clear" or "cls" based on os. Commented Sep 24, 2018 at 7:58
  • I can't do whole-screen refresh because I need the scrollback buffer. Also, that doesn't tell me how to re-write the readline prompt and whatever the user is in the middle of entering. Commented Sep 24, 2018 at 8:05
  • Have you found a solution for your problem? An out-of-the box solution might be to use rlwrap around your program instead of using readline from Python. Commented Dec 17, 2020 at 14:12
  • This question is similar to: How to implement a python REPL that nicely handles asynchronous output?. If you believe it’s different, please edit the question, make it clear how it’s different and/or how the answers on that question are not helpful for your problem. Commented Jul 5, 2024 at 6:30

1 Answer 1

2

Starting from this answer that solves the issue for C, I arrived at the following Python code, which works well enough for my purposes:

import os
import readline
import signal
import sys
import time
import threading


print_lock = threading.Lock()

def print_safely(text):
    with print_lock:
        sys.stdout.write(f'\r\x1b[K{text}\n')
        os.kill(os.getpid(), signal.SIGWINCH)

def background_thread():
    while True:
        time.sleep(0.5)
        print_safely('x')

threading.Thread(target=background_thread, daemon=True).start()

while True:
    try:
        inp = input(f'> ')
        with print_lock:
            print(repr(inp))
    except (KeyboardInterrupt, EOFError):
        print('')
        break

print_safely

  • uses \r\x1b[K (CR + CSI code EL) to remove the existing Readline prompt
  • prints its payload line
  • sends SIGWINCH to the thread that runs readline (in my case, readline runs in the main thread so os.getpid() gives the right target PID); this causes readline to re-draw its full prompt.

Possible remaining issues:

  • As mentioned in the other answer, if the readline prompt is longer than one line, the line-clearing will most likely fail.
  • You need to acquire the print_lock whenever printing in the main thread (this is true independent of the readline issue)
  • There might be some racy behaviours when the user presses a key at the exact same moment when the background thread prints.
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.