2

I have to write a code that somehow works for both python versions and I can't understand the behavior of the code:

from __future__ import print_function
import sys
sys.stdout.flush()
print("input: ", end="")
f = sys.stdin.readline()
print(f)

When I run the code with python2 it behaves like I expect:

$ python2 test_input.py 
input: foo bar
foo bar

When I run the code with python3 it behaves strange. It first reads the input and then prints the prompt:

$ python3 test_input.py 
foo bar
input: foo bar

Can you explain this and suggest a fix?

5
  • The I/O subsystem in Python 3 is a complete overhaul (see the io module, available in Python 2 as well), so it is no suprise that flushing behaviour may have changed. Commented Oct 19, 2014 at 10:03
  • @MartijnPieters, actually, the solution by Dietrich Epp works. Commented Oct 19, 2014 at 10:06
  • @MartijnPieters, how would you do it? Commented Oct 19, 2014 at 10:07
  • have you tried from six.moves import input; print("*" + input("input: "))? ` Commented Oct 19, 2014 at 13:26
  • If you are using sys.stdin.readline to avoid the rawinput to input change, put try: input=rawinput\nexcept NameError: pass. at the top of the file. Commented Oct 19, 2014 at 22:22

3 Answers 3

2

You got your flush() and print() backwards. Here is the correct order:

# First, write to stdout
print("input: ", end="")
# Then, flush all data in the stdout buffer
sys.stdout.flush()
Sign up to request clarification or add additional context in comments.

7 Comments

I think that that was the point. The OP shows that stdout has been flushed manually first, after which the difference in behaviour is unexpected.
@MartijnPieters: What would the point be of calling flush() with nothing in the buffer?
To produce a minimal, verifiable test case that demonstrates the behaviour?
@MartijnPieters: For what? For demonstrating that flushing an empty buffer is pointless?
It is already known that the buffer is empty, because the program is reproduced in its entirety, and the buffer is empty at program startup.
|
2

The difference is that CPython 2 uses C stdio to implement standard streams such as sys.stdin, sys.stdout (used by print()) while Python 3 reimplements IO on top of system API e.g., open, read, write on POSIX.

To avoid thinking about how print() is implemented, the same issue occurs if sys.stdout is used directly:

# __main__.py
import sys

sys.stdout.write("input: ")
f = sys.stdin.readline()
sys.stdout.write("*" + f)

On Python 3 "input: " is not printed before the readline() call:

$ python2 .
input: foo bar
*foo bar
$ python3 .
foo bar
input: *foo bar

In C, stdout is flushed before reading any input in the interactive case (it is undefined behavior if an output operation is followed by an input operation on the same update stream without fflush() in between). This C program prints "input: " before asking for input as expected:

#include <stdio.h>

#define MAXLEN 100

int main(void) {
  char buf[MAXLEN] = {0};

  fputs("input: ", stdout);
  if (!fgets(buf, MAXLEN, stdin))
    return 1;

  fputs(buf, stdout);
  return 0;
}

That is why the workaround: calling sys.stdout.flush() before sys.stdin.readline() suggested by @Dietrich Epp works.

It is a deficiency in Python 3 implementation. stdout shall be flushed by default before reading from stdin if both point to the same place (os.path.samefile(sys.stdout.fileno(), sys.stdin.fileno()) e.g., if both tty). You could report the issue at Python bug tracker.

Comments

0

In Python 3.3 the flush keyword was added to the print() function:

Changed in version 3.3: Added the flush keyword argument.

In versions before 3.3, the print() function would always flush, even when using end=''. Your exact behaviour also reproduced using Python 3.2:

$ python3.2 test.py 
foo bar
input: foo bar

To get the same behaviour, flush in Python 3.3:

try:
    print("input: ", end="", flush=True)
except TypeError:
    print("input: ", end="")

or use sys.stdout.flush() after print() calls on Python 3.3 or up.

2 Comments

"Or use sys.stdout.flush() after print() calls"... weren't you complaining about that advice in my answer?
@DietrichEpp: I am complaining about your diagnosis. I'm saying the flush was put first deliberately. I never said it wouldn't solve the problem. The OP is asking for us to explain why there is a difference between Py 2 and Py 3.

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.