Gentoo Websites Logo
Go to: Gentoo Home Documentation Forums Lists Bugs Planet Store Wiki Get Gentoo!
Bug 548814 - sys-apps/portage: suggestion to make --ask behaviour depend on os.isatty()
Summary: sys-apps/portage: suggestion to make --ask behaviour depend on os.isatty()
Status: CONFIRMED
Alias: None
Product: Gentoo Linux
Classification: Unclassified
Component: [OLD] Core system (show other bugs)
Hardware: All Linux
: Normal enhancement (vote)
Assignee: Portage team
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2015-05-06 23:31 UTC by Kerin Millar
Modified: 2016-07-24 20:45 UTC (History)
0 users

See Also:
Package list:
Runtime testing required: ---


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Kerin Millar 2015-05-06 23:31:21 UTC
Currently, --ask always results in a prompt being displayed, regardless of whether stdout is attached to a terminal or not. This leads to clumsy behaviour if inadvertently piped. Also, I have seen some people put --ask in EMERGE_DEFAULT_OPTS, which would break cron jobs. At least one package in the tree provides a cron job that calls upon emerge.

I think it would be kinder to change the behaviour of --ask such that os.iastty(1) is called and, if false, emerge either raises an error or behaves as if the parameter were not specified at all. The latter approach would allow people to have their cake and eat it with respect to EMERGE_DEFAULT_OPTS.
Comment 1 Zac Medico gentoo-dev 2015-05-07 00:01:29 UTC
Currently, it only checks if stdin is a tty:

https://gitweb.gentoo.org/proj/portage.git/tree/pym/_emerge/actions.py?id=fcb77fe28505e98d281240e524d7073769809157#n2902

I guess we could make it check both, though it will break tee usage such this:

   emerge -a foo | tee emerge.out

I doubt many people do that anyway, if any.
Comment 2 Kerin Millar 2015-05-07 00:13:12 UTC
Hmm, yeah, it would break tee. This generated a small discussion on freenode and some other possibilities that emerged from that were:

* have distinct make.conf variables for tty vs non-tty situations
* ensure that packaged scripts that call upon emerge specify --ask n
* filter out options from EMERGE_DEFAULT_OPTS that depend on user interaction
* have a generic parameter to ensure non-interactive usage

My main concern is where you have something like porticron, which is scheduled and calls upon emerge --sync. Currently, it won't actually sync if EMERGE_DEFAULT_OPTS contains "--ask". I think that it would be nice if a user could have it without the porticron author, in this case, having to care and update the script to negate whatever whichever features portage has. I can't think of a perfect solution, though.
Comment 3 Kerin Millar 2015-05-07 00:15:04 UTC
correction: "... whichever interactive features portage has."
Comment 4 Kerin Millar 2015-05-07 00:19:34 UTC
I'm not a Python man but this might serve to implement a compromise:

def hasctty():
    try:
        open("/dev/tty", "r+")
        return True
    except:
        return False

That will yield True when going through a pipe, but shouldn't when run from cron.
Comment 5 Brian Dolbec gentoo-dev 2015-05-07 00:52:26 UTC
(In reply to Kerin Millar from comment #2)
> Hmm, yeah, it would break tee. This generated a small discussion on freenode
> and some other possibilities that emerged from that were:
> 
> * have distinct make.conf variables for tty vs non-tty situations
> * ensure that packaged scripts that call upon emerge specify --ask n
> * filter out options from EMERGE_DEFAULT_OPTS that depend on user interaction
> * have a generic parameter to ensure non-interactive usage
> 
> My main concern is where you have something like porticron, which is
> scheduled and calls upon emerge --sync. Currently, it won't actually sync if
> EMERGE_DEFAULT_OPTS contains "--ask". I think that it would be nice if a
> user could have it without the porticron author, in this case, having to
> care and update the script to negate whatever whichever features portage
> has. I can't think of a perfect solution, though.

To sync from a cron without the need for checking for the --ask parameter, then with >=portage-2.2.16 you can do 

emaint sync --auto

The emaint system does not look at EMERGE_DEFAULT_OPTS.  The sync system is now based in emaint.  Emerge --sync just calls the new sync system in emaint with the --auto parameter so that it syncs only repos with auto-sync=yes set.
Comment 6 Kerin Millar 2015-05-07 01:11:40 UTC
(In reply to Brian Dolbec from comment #5)
> The emaint system does not look at EMERGE_DEFAULT_OPTS.  The sync system is
> now based in emaint.  Emerge --sync just calls the new sync system in emaint
> with the --auto parameter so that it syncs only repos with auto-sync=yes set.

Interesting. Thanks for the tip.
Comment 7 Kerin Millar 2016-07-23 13:33:53 UTC
Bump. Zac, any thoughts as to the method of checking for a controlling tty, per comment #4? It addresses the issue that originally motivated me to file this bug, without affecting pipeline usage. That is, under cron, the sample function will always return False.
Comment 8 Zac Medico gentoo-dev 2016-07-24 00:37:20 UTC
(In reply to Kerin Millar from comment #7)
> Bump. Zac, any thoughts as to the method of checking for a controlling tty,
> per comment #4?

I haven't found a case where it will return True. I just tried this in my shell:

$ python -c 'open("/dev/tty", "r+")'
Traceback (most recent call last):
  File "<string>", line 1, in <module>
io.UnsupportedOperation: File or stream is not seekable.
Comment 9 Kerin Millar 2016-07-24 16:22:34 UTC
Oops, I only tested under 2.7 originally. Fortunately, os.open can be used instead, as shown below. I have tested this method with success in both python 2.7 and 3.5. While not shown here, I also tested by way of a cron job.

Incidentally, I use "state" in my equivalent perl code to enclose the result and avoid further open calls in the event that the function is called repeatedly. I haven't attempted that here, as I don't know how to do it properly in python.

build ~ # cat hasctty
#!/usr/bin/env python

import os

def hasctty():
    try:
        os.open('/dev/tty', os.O_RDWR | os.O_NOCTTY)
        return True
    except:
        return False

if hasctty():
        print("true")
else:
        print("false")
build ~ # ./hasctty
true
build ~ # ./hasctty | cat
true
build ~ # ssh -T localhost '/root/hasctty' # no pseudo-tty
false
build ~ # ssh -t localhost '/root/hasctty' # force psuedo-tty
true
Connection to localhost closed.
Comment 10 Zac Medico gentoo-dev 2016-07-24 20:10:13 UTC
I've (In reply to Kerin Millar from comment #9)
> def hasctty():
>     try:
>         os.open('/dev/tty', os.O_RDWR | os.O_NOCTTY)
>         return True
>     except:
>         return False

That works for me. In addition to that, I suppose we should keep sys.stdout.isatty() check, since hasctty() returns True for a command run via nohup with /dev/null as stdin.
Comment 11 Zac Medico gentoo-dev 2016-07-24 20:10:55 UTC
(In reply to Zac Medico from comment #10)
> That works for me. In addition to that, I suppose we should keep
> sys.stdout.isatty() check, since hasctty() returns True for a command run
> via nohup with /dev/null as stdin.

Err, I mean sys.stdin.isatty().
Comment 12 Kerin Millar 2016-07-24 20:45:50 UTC
(In reply to Zac Medico from comment #11)
> Err, I mean sys.stdin.isatty().

Indeed, checking stdin remains an important metric for determining whether interaction is possible.