4

I have a program that uses curses, and then returns to the main script for further processing. After it returns, my subsequent output to stdout does not appear until there's a large amount of it (e.g. thousands of bytes).

I've reduced the problem to a very simple program that fails reliably:

import curses
import time

curses.initscr()
curses.endwin()

print "Hello world!"
time.sleep(5)

If I comment out the two curses calls, the 'Hello world!' prints before the delay. If I put them in, it prints after the delay (when the script exits).

Neil Baylis
  • 652
  • 4
  • 14
  • possible duplicate of [How to flush output of Python print?](http://stackoverflow.com/questions/230751/how-to-flush-output-of-python-print) – Jack Sep 14 '10 at 04:08
  • @jack. nope. this relates to why the output buffer needs to be flushed in the first place – aaronasterling Sep 14 '10 at 04:35
  • Ok, sorry. Just tried to help pointing the OP to something that could help! – Jack Sep 14 '10 at 23:58

4 Answers4

3

The curses.endwin() call "sets standard I/O back to normal"... which unfortunately means "buffered" (you could consider that a bug in curses and file the bug on Python's tracker).

To ensure standard-output is unbuffered,

import os

sys.stdout = os.fdopen(0, 'w', 0)

(You can put the import os at the start, or course; the reassignment of sys.stdout is meant to go right after the endwin call).

Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
  • So does stdout begin life as unbuffered? I thought that's what the -u flag was for. But yes, this fix works, thanks. – Neil Baylis Sep 14 '10 at 05:10
  • What exactly "sets standard I/O back to normal"? I'm not seeing that in the Python docs, curses module source, or endwin man page (curs_initscr(3X)). – jwelsh Mar 01 '17 at 18:45
1

more clear way:

import sys
import curses

correct_stdout = sys.stdout    # remember correct

curses.initscr()
curses.endwin()

sys.stdout = correct_stdout    # restore correct
Vladimir
  • 11
  • 1
1

Alex beat me to it (he knew, I had to figure it out) but I wrote this nifty context manager that you might be able to use.

import curses
import time
import os
import sys


class CursesStdout(object):
    def __enter__(self):
        pass

    def __exit__(self, *args):
        sys.stdout = sys.__stdout__ = os.fdopen(sys.__stdout__.fileno(), 'w', 0)

with CursesStdout():
    curses.initscr()
    curses.endwin()


print "Hello world!"
time.sleep(2)

I modify sys.__stdout__ directly because when you look at the source in curses/__init__.py you see that the initscr function passes that file descriptor to the C code and so I store that to be reset to normal.

aaronasterling
  • 68,820
  • 20
  • 127
  • 125
0

I have found that tis is system/library dependent. On my Windows machine (curses from http://www.lfd.uci.edu/~gohlke/pythonlibs/) it flushes and then sleeps, but on Linux it flushes after sleep. I think you can simply use: sys.stdout.flush() like:

print "Hello world!"
sys.stdout.flush()
time.sleep(5)

(it may be better to make some kind of "print & flush" function)

Michał Niklas
  • 53,067
  • 18
  • 70
  • 114