10

Under Ubuntu Desktop (Unity) when a script marked as executable and then I click on the file I get pop-up window like the one here in the image:

enter image description here

pyscript.py is an executable Python script file with a shebang: #!/usr/bin/python where /usr/bin/python is the path to the Python interpreter. Since I'm not running this process in a terminal window, because I just clicked "Run", I thought initially there would be no standard streams for the process; As I experimented more, I realized all the standard streams are available:

pyscript.py

#!/usr/bin/python3
import sys, os
f = file=open("output.txt", "w")
print(sys.stdout, sys.stdin, sys.stderr, sep='\n', file=f)

output.txt

<_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'>
<_io.TextIOWrapper name='<stdin>' mode='r' encoding='UTF-8'>
<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>

Which raises the question, there's no terminal window running in the background, what are sys.stdout, stdin and stderr connected to? Technically, running Python without console window under Windows with .pyw I get the same output.

Take a look at this question: pythonw.exe or python.exe?

One answer states the following:

Standard streams sys.stdin, sys.stdout and sys.stderr are NOT available.

The emphasis that standard streams are not available doesn't seem to be true when I test this with Python 2.7 in Windows 10 by clicking the .pyw file, though, in the Windows registry .pyw files are associated with Python 2.X which runs pythonw.exe in my machine.

This question is a Unix/Windows jumble!

GIZ
  • 4,409
  • 1
  • 24
  • 43
  • 1
    The answer probably isn't going to be anything Python-specific. – user2357112 Jun 05 '17 at 22:10
  • But it *will* be specific to the operating environment. Are you asking about Windows or about Unity? – Robᵩ Jun 05 '17 at 22:12
  • Have you tried reading to or writing from these streams? What happens? – merlin2011 Jun 05 '17 at 22:14
  • @Robᵩ I'm asking about both. – GIZ Jun 05 '17 at 22:22
  • @merlin2011 Yes, I can write to the streams, but since there's no terminal window open, I get nothing and I get no exception as well. – GIZ Jun 05 '17 at 22:24
  • If possible, let's omit the Windows part of the question and focus on the Unix side, because Windows is tedious by nature, just to narrow the scope of our quest. – GIZ Jun 05 '17 at 22:27
  • The answers may well be different depending on the desktop environment. On your system, if you run a python program that just does nothing but run for a minute or two, if you get its pid and do `ls -l /proc/pid/fd`, what are the targets for the symlinks named 0, 1, and 2? – Mark Plotnick Jun 05 '17 at 23:35
  • 1
    In Ubuntu 16.04 started from lightdm, for a program run via the window manager, stdin is `/dev/null` and stdout/stderr are a pseudoterminal client (`/dev/pts/[N]`). It looks like upstart (still used in addition to systemd) has the pseudoterminal server fd (for `/dev/ptmx`). Also, upstart's stdout is `/dev/null`, and I don't see output written to the client pts being logged anywhere. It think it's simply discarded in the default configuration. – Eryk Sun Jun 06 '17 at 00:15
  • For pythonw.exe on Windows, it doesn't inherit or allocate a console (conhost.exe), so any console buffer handles it inherits in the standard handles (`StandardInput`, `StandardOutput`, and `StandardError`) get closed at startup. However it can inherit non-console handles when the handles are inheritable, and either set as the parent's standard handles or explicitly passed as the `STARTUPINFO` handles (e.g. `hStdInput`), and `CreateProcess` is called either with just `bInheritHandles` as `TRUE` or additionally with an explicit list of handles to inherit in `STARTUPINFOEX`. – Eryk Sun Jun 06 '17 at 00:22
  • @eryksun I'm using Unity desktop in Ubuntu 16.04 and yes like you said, `stdin` is actually `/dev/null`. `stdout` and `stderr` are sockets with the same inode. This is the result of `ls -l` in `/proc/[PID]/fd`: `lr-x------ 1 user user 64 Jun 6 11:48 0 -> /dev/null` `lrwx------ 1 user user 64 Jun 6 11:48 1 -> socket:[17977]`, `lrwx------ 1 user user 64 Jun 6 11:48 2 -> socket:[17977]` *plus* the FD 3 for the file that the Python script opens. – GIZ Jun 06 '17 at 09:06
  • @direprobs, I can't explain the difference. I'm running the script via Files and clicking on "Run" in the dialog, but I get stdout (1) and stderr (2) connected to a pts. So, for example, in IDLE3 `os.get_terminal_size()`, returns `os.terminal_size(columns=0, lines=0)`. For you, given stdout is a socket, I would expect the call to fail. – Eryk Sun Jun 06 '17 at 09:43
  • @eryksun Yes, `os.get_terminal_size()` raises this exception: `OSError: [Errno 25] Inappropriate ioctl for device` when run by clicking "Run", but returns `os.terminal_size(...)` when "Run in Terminal". – GIZ Jun 06 '17 at 10:09
  • The process tree in my case is `lightdm` -> `lightdm --session-child [N] [M]` -> `upstart --user` -> `python3 pyscript.py`. Is that like yours? – Eryk Sun Jun 06 '17 at 10:40
  • @eryksun mine looks like this: `systemd---lightdm---lightdm---upstart---pyscript.py`, so yes. – GIZ Jun 06 '17 at 17:52

1 Answers1

2

As others mentioned, the question is not Python-specific. When a process is spawned, it's the parent's duty to set the child's file descriptors (or to allow them to be inherited).

So, this is not even OS-specific, but actually application specific. For example, you might have a different anser for Gnome and KDE. Or when executing the file from within Windows Explorer or 7-Zip (I think; I know how this works on Unix, not so sure on Windows). Different applications spawning the process might be making different arrangements.

On Linux, you can find out by running lsof on the python process, which will list the opened files and tell you where exactly your stdout is going. Here's your code, changed to do just that:

#!/usr/bin/env python

# vi: ai sts=4 sw=4 et

import sys, os, pprint
import subprocess

f = open("/tmp/output.txt", "w")

for stream in (sys.stdout, sys.stdin, sys.stderr):
    print (stream, file=f)
    print ("STTY?", stream.isatty(), file=f)
    print ("fileno:", stream.fileno(), file=f)
    print ("name:", stream.name, file=f)

#    print (pprint.pprint(dir(stream)), file=f)
    print (file=f)

subprocess.call ("lsof -p %s" % os.getpid(), stdout = f, shell = True)

Only the last line is actually important. See the output for a normal run:

(...)
test.py 29722 xxx    0u   CHR  136,4      0t0      7 /dev/pts/4
test.py 29722 xxx    1u   CHR  136,4      0t0      7 /dev/pts/4
test.py 29722 xxx    2u   CHR  136,4      0t0      7 /dev/pts/4

And when redirecting the output to a file:

(...)
test.py 29728 xxx    0u   CHR  136,4      0t0       7 /dev/pts/4
test.py 29728 xxx    1w   REG   0,38        0 2070222 /tmp/asdf.txt
test.py 29728 xxx    2u   CHR  136,4      0t0       7 /dev/pts/4

See that the file #1 has changed? Same will happen when you run it from Unity, telling you where that is going.

I didn't exactly reproduce your problem, as my Gnome 3 file manager won't run scripts like that and I'd not look into it, but I'm curious to know where yours goes.

caxcaxcoatl
  • 8,472
  • 1
  • 13
  • 21