[PATCH] Flush password prompts for non-terminal callers

Idan K idankk86 at gmail.com
Sat Jan 15 14:38:18 CST 2011


On Sat, Jan 15, 2011 at 5:40 PM, Steven Streeting
<steve at stevestreeting.com>wrote:

> # HG changeset patch
> # User Steve Streeting <steve at stevestreeting.com>
> # Date 1295105307 0
> # Node ID 407ab604cbb7b923f8e517e5f4858b0005bc14d0
> # Parent  8f72d2f6c26322c235602280ea0a2b3debf37b06
> Ensure that password prompts are flushed so that any non-terminal waiting
> for them on stderr can reliably see them. This is required to do interactive
> login from non-terminal clients, without forcing the user to pre-populate
> their configuration parameters. Without this feature, hg commands which
> require authentication which hasn't been pre-configured will simply randomly
> hang waiting for input without the calling program being any the wiser.
> Flushing the prompt means it always has a chance to know that hg is waiting
> for a response.
> This is discussed in more detail in this tracker item:
> http://mercurial.selenic.com/bts/issue2590
>
> diff --git a/mercurial/ui.py b/mercurial/ui.py
> --- a/mercurial/ui.py
> +++ b/mercurial/ui.py
> @@ -468,7 +468,12 @@
>                 # windows sometimes raises something other than ImportError
>             except Exception:
>                 pass
> -        line = raw_input(prompt)
> +        # prompt using read_line instead of raw_input which ensures prompt
> is flushed
> +        self.write_err(prompt)
> +        line = sys.stdin.readline()
> +        # get rid of \r
> +        if line and line[-1] == '\r':
> +            line = line[:-1]
>         # When stdin is in binary mode on Windows, it can cause
>         # raw_input() to emit an extra trailing carriage return
>         if os.linesep == '\r\n' and line and line[-1] == '\r':
> @@ -508,7 +513,13 @@
>         if not self.interactive():
>             return default
>         try:
> -            return getpass.getpass(prompt or _('password: '))
> +            # Ensure we flush so that we can read prompt in non-terminal
> +            self.write_err('password:')
> +            pwd = sys.stdin.readline()
> +            # get rid of \r
> +            if pwd and pwd[-1] == '\r':
> +                pwd = pwd[:-1]
> +            return pwd
>         except EOFError:
>             raise util.Abort(_('response expected'))
>     def status(self, *msg, **opts):
>
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel at selenic.com
> http://selenic.com/mailman/listinfo/mercurial-devel


By using stdin.readline() you're essentially throwing away all the efforts
getpass.getpass() goes into trying not to echo the actual password (which
might pose a security risk - shoulder attack? ;-).
It seems that getpass.getpass() flushes the output stream since Python 2.6,
which version and platform have you tested this on?

Since Mercruial supports Python versions earlier than 2.6, short of fixing
getpass in its code base, an alternative might be to provide a wrapped
sys.stdout to getpass.getpass() that will flush the output stream after a
write() call has been issued, for the broken getpass. I'm not sure if it's
the best option though.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://selenic.com/pipermail/mercurial-devel/attachments/20110115/56f3becc/attachment.htm>


More information about the Mercurial-devel mailing list