Interactive scripts (back on topic for once) [was Re: The "loop and a half"]

Chris Angelico rosuav at gmail.com
Fri Oct 6 14:25:05 EDT 2017


On Sat, Oct 7, 2017 at 4:54 AM, Grant Edwards <grant.b.edwards at gmail.com> wrote:
> On 2017-10-06, Chris Angelico <rosuav at gmail.com> wrote:
>> On Sat, Oct 7, 2017 at 4:05 AM, Grant Edwards <grant.b.edwards at gmail.com> wrote:
>>> On 2017-10-06, Thomas Jollans <tjol at tjol.eu> wrote:
>>>
>>>> Seriously? sys.stdin can be None? That's terrifying.
>>>
>>> Why?
>>>
>>> Unix daemons usually run with no stdin, stderr, or stdout.
>>>
>>> And yes, people do write Unix daemons in Python.
>>
>> Hmm, but usually I would expect them still to HAVE those streams,
>> they're just connected to /dev/null or something. I don't think they
>> would actually fail to exist, would they?
>
> That's a good point.  The basic approach is to fork and then just
> close all file descriptors.  Since what is normally the
> std{in,out,err} descriptors can be re-used, you could end up with some
> naive code (e.g. something in a library) writing to a file/socket that
> it shouldn't be writing to.
>
> The defensive approach is to open /dev/null and use dup2() to make fd
> 0 1 2 refer to that.  In that case, they do exist, but stdin always
> reads empty and stdout stderr write data is discarded.
>
> That's probably the more common approach.

Yeah. I usually see daemonized processes that still have *some*
handles in 0/1/2, either /dev/null or to some sort of logging facility
(the latter being extremely helpful where it's available). But I just
tested it, and sure enough, Python does actually stash None into
sys.stdin/out/err if the corresponding file handles were closed prior
to exec.

#include <unistd.h>
#define IGNORE1 \
""" ";
int main()
{
    close(0);
    close(1);
    close(2);
    execlp("python3", "python3", "nostreams.c", NULL);
}
#define IGNORE2 \
" """; #\
import sys #\
with open("nostreams.log", "w") as f: #\
    print("stdin: ", sys.stdin,  file=f) #\
    print("stdout:", sys.stdout, file=f) #\
    print("stderr:", sys.stderr, file=f)

So I suppose you'd have to do something like:

if sys.stdout and sys.stdout.isatty():
    # be interactive
else:
    # perform in batch mode

just to be on the safe side.

ChrisA



More information about the Python-list mailing list