Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

raw_input() and sys.stdin misbehaves on CTRL-C

I am trying to detect a KeyboardInterrupt exception when CTRL-C is pressed during a raw_input() prompt. Normally the following code works just fine to detect the command:

try:
    input = raw_input("Input something: ")
except KeyboardInterrupt:
    do_something()

The problem comes when trying to intercept the input from sys.stdin. After adding some code in between raw_input() and sys.stdin, the CTRL-C command now results in two exceptions: EOFError followed by KeyboardInterrupt a line or two later. This is the code used to test:

import sys
import traceback

class StdinReplacement(object):
    def __init__(self):
        self.stdin = sys.stdin
        sys.stdin = self
    def readline(self):
        input = self.stdin.readline()
        # here something could be done with input before returning it
        return input

if __name__ == '__main__':

    rep = StdinReplacement()
    while True:
        info = None
        try:
            try:
                input = raw_input("Input something: ")
                print input
            except:
                info = sys.exc_info()
                print info
        except:
            print '\n'
            print "0:", traceback.print_traceback(*info)
            print "1:", traceback.print_exception(*sys.exc_info())

Which results in the following being printed out:

0:Traceback (most recent call last):
  File "stdin_issues.py", line 19, in <module>
    input = raw_input("Input something: ")
EOFError: EOF when reading a line
 None
1:Traceback (most recent call last):
  File "stdin_issues.py", line 23, in <module>
    print info
KeyboardInterrupt

Am I missing something obvious? Maybe intercepting the input in a bad way?

Found this fairly old page which seems like the same issue. No solution though: https://mail.python.org/pipermail/python-list/2009-October/555375.html

Some environment details: Python 2.7.3 (64-bit), Windows 7 SP1 (64-bit)

------------------------------------------------------------------------

EDIT: An update to the readline method of StdinReplacement fixed the issue.

def readline(self):
    input = self.stdin.readline()
    # here something could be done with input before returning it
    if len(input) > 0:
        return input
    else:
        return '\n'
like image 220
crdavis Avatar asked Jul 14 '14 12:07

crdavis


1 Answers

It seems like the problem is that your readline method returns an empty line, thus signaling the end of file:

import sys

class Test(object):
    def readline(self):
        return ''

sys.stdin = Test()

raw_input('')   # EOFError!

However modifying it such that it doesn't return an empty line makes the code work:

import sys

class Test(object):
    def readline(self):
        return '\n'  # or 'a', or whatever

sys.stdin = Test()

raw_input('')   # no error

The readline method should return an empty string only when the file finished. The EOFError is used to mean exactly this: raw_input expected the file to contain a line, but the file ended.


This is due to the call to PyFile_GetLine found at the end of the implementation of raw_input:

return PyFile_GetLine(fin, -1);

According to the documentation of PyFile_GetLine(PyObject *p, int n):

If n is less than 0, however, one line is read regardless of length, but EOFError is raised if the end of the file is reached immediately.

Since it passes -1 as n the EOFError is raised when the EOF is found (i.e. you return an empty string from readline).


As far as I can tell the behaviour you see is only possible if you insert the input and also create an interrupt. Pressing only Ctrl+C doesn't generate any EOFError (at least on linux).

like image 74
Bakuriu Avatar answered Sep 19 '22 04:09

Bakuriu