Search code examples
pythonpython-3.xstdin

Conflict between sys.stdin and input() - EOFError: EOF when reading a line


I can't get the following script to work without throwing an EOFError exception:

#!/usr/bin/env python3

import json
import sys

# usage:
# echo '[{"testname": "testval"}]' | python3 test.py

myjson = json.load(sys.stdin)
print(json.dumps(myjson))

answer = input("> ")  # BUG: EOFError: EOF when reading a line
print(answer)

I've read this question which seems to be related: Python STDIN User Input Issue

I think that tells me I need to clear the stdin buffer ? But I'm not sure how because print(sys.stdin.readline()) just outputs a newline and the EOFError is still there.

I also tried using the sys.stdin.flush() method (found in this question: Usage of sys.stdout.flush() method) although I still don't understand what it does because I couldn't find it in the official documentation (3.6), the closest I found was this but it doesn't mention flush: https://docs.python.org/3/library/sys.html

Please bear in mind that I'm not a programmer nor do I have a CS education or background. I just write scripts to automate parts of my, otherwise non-technical, work. So if you know any good beginner ressource on how stdin/stdout works in the shell with Python please do tell.


Solution

  • By piping input, Python is opening sys.stdin as a FIFO. Otherwise, Python will open sys.stdin to /dev/tty or equivalent.

    You can verify this with:

    import os,sys,stat
    print("isatty():", sys.stdin.isatty())
    print("isfifo():", stat.S_ISFIFO(os.fstat(0).st_mode))
    

    Run this twice, once piping in data, once not.

    I get:

    $ echo "Test" | ./test2.py
    isatty(): False
    isfifo(): True
    
    $ ./test2.py
    isatty(): True
    isfifo(): False
    

    So your EOF occurs because the FIFO sys.stdin is opened to is empty.

    You can reopen sys.stdin to /dev/tty, however.

    j = json.load(sys.stdin)
    print(j)
    
    sys.stdin = open("/dev/tty")
    
    answer = input("> ")
    print(answer)
    

    Which would work fine:

    $ echo '{"key":"val"}' | python3 ./testjson.py
    {'key': 'val'}
    > testing
    testing