10

I've run into a somewhat unusual situation. I'm trying to script the interactive console (for teaching/testing purposes), and I tried the following:

$ python > /dev/null
Python 2.7.3 (v2.7.3:70274d53c1dd, Apr  9 2012, 20:52:43) 
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> print 3
>>> 

3 isn't printed, so clearly everything else was on stderr. So far so good. But then we redirect stderr:

$ python 2> /dev/null
>>> print 3
3
>>> 

How can the prompt be printed in both cases?

EDIT: Redirecting both stdout and stderr causes absolutely nothing to be printed. So Python's clearly "choosing" one of stdout or stderr. Is that documented to happen? I couldn't figure out how this is actually done in the Python source code.

nneonneo
  • 171,345
  • 36
  • 312
  • 383
  • 2
    Huh, that's strange. Perhaps it checks `isatty`? –  Aug 24 '13 at 14:50
  • That'd be my guess, but I can't seem to figure out where Python actually does this. The weirdest thing is that `PyOS_StdioReadline`, the function which appears to be responsible for reading input, is hardcoded to print the prompt to `stderr`. If `stderr` were redirected to `stdout`, as the second example suggests, we should see the banner. – nneonneo Aug 24 '13 at 15:02
  • 1
    It might be easier to use a tool designed for teaching Python, like the [notebook feature of ipython](http://ipython.org/notebook.html). – Burhan Khalid Aug 24 '13 at 15:43

1 Answers1

4

It seems like python checks whether stdout is a tty:

/* This is needed to handle the unlikely case that the
 * interpreter is in interactive mode *and* stdin/out are not
 * a tty.  This can happen, for example if python is run like
 * this: python -i < test1.py
 */
if (!isatty (fileno (sys_stdin)) || !isatty (fileno (sys_stdout)))
    rv = PyOS_StdioReadline (sys_stdin, sys_stdout, prompt);
else
    rv = (*PyOS_ReadlineFunctionPointer)(sys_stdin, sys_stdout,
                                         prompt);

Sourcecode from Parser/myreadline.c around line 194.

It's possible that the interpreter imports the readline module at startup, in which case PyOS_ReadlineFunctionPointer will be set to call_readline, which uses the readline library. In particular it calls rl_callback_handler_install. The documentation of this function doesn't state where the prompt is printed, but it's possible that it checks whether stdout/stderr are ttys.

Bakuriu
  • 98,325
  • 22
  • 197
  • 231
  • Right above that, though, it seems to set `PyOS_ReadlineFunctionPointer` to `PyOS_StdioReadline` (except on VMS, which I'm definitely not on). – nneonneo Aug 24 '13 at 15:19
  • @nneonneo Yes, I'm I bit puzzled, but this is the only reference I can find that could somehow explain the behaviour you see. Do you have the readline library available? Because at the beginning of the file it mentions that it uses it when it is available. – Bakuriu Aug 24 '13 at 15:21
  • @nneonneo It sets it only if it isn't `NULL`. If the interpreter imports the `readline` during startup then the `readline` module will set its function(see the init function at `Modules/readline.c`), and thus the `PyOS_Readline` will not use `PyOS_StdioReadline`. – Bakuriu Aug 24 '13 at 15:29
  • Aha! I missed that detail. I guess this means that Python outputs the prompt to `stderr` only as a fallback...neat! Thanks for your answer. – nneonneo Aug 24 '13 at 15:39