Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to clear user input before Python exits

If a program exits before all of its input has been consumed, then the remaining input will be sent to the shell.

Here's an example:

import sys
for line in sys.stdin:
    sys.exit()

Try running the example and copy-paste this multi-line input:

foo
bar
baz

The result will look like this:

❯ python example.py
foo
bar
baz%
❯ bar
zsh: command not found: bar
❯ baz

In this case, the program exited after consuming foo, so bar was automatically sent to the shell. How can I clear the remaining input before Python exits?

The for loop in the example represents complicated logic, so I'm looking for a solution that doesn't modify the for loop.

I tried registering an atexit handler to clear the input:

import sys, atexit

def clear_stdin():
    sys.stdin.read()
atexit.register(clear_stdin)

for line in sys.stdin:
    sys.exit()

This solution does clear the input and prevent it from being sent to the shell, but it unfortunately causes the program to hang until the user enters a blank line. I'd like to clear the remaining input without pausing the program.

like image 370
David Nickerson Avatar asked Oct 15 '25 09:10

David Nickerson


1 Answers

On Unix systems you can use os.set_blocking before reading the input.

If the user pastes a large amount of text, then the terminal may be holding on to additional input that hasn't been sent to stdin yet. It may not be possible for the program to see this, so the following code includes a sleep to allow for the terminal to provide more input. This code also includes several guards to prevent errors:

import sys, atexit, os
from time import sleep

def clear_stdin():
    if sys.__stdin__ and sys.__stdin__.isatty() and hasattr(os, 'set_blocking'):
        try:
            os.set_blocking(sys.__stdin__.fileno(), False)
        except OSError:
            return
        try:
            while sys.__stdin__.read():
                sleep(0.1)
        except TypeError:
            return
atexit.register(clear_stdin)

for line in sys.stdin:
    sys.exit()

This will prevent the remaining input from being sent to the shell while still allowing the program to exit immediately:

❯ python example.py
foo
bar
baz%
❯ baz

If the last line does not end with a newline character then it won't be consumed, but it won't be executed by the shell either.

like image 130
David Nickerson Avatar answered Oct 16 '25 23:10

David Nickerson