classification
Title: getpass.getpass() performs differently on Windows vs *nix
Type: enhancement Stage: test needed
Components: Library (Lib) Versions: Python 3.2
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: Nosy List: BreamoreBoy, esrever_otua, georg.brandl
Priority: low Keywords:

Created on 2005-07-06 21:36 by esrever_otua, last changed 2010-08-21 18:13 by BreamoreBoy. This issue is now closed.

Messages (9)
msg60766 - (view) Author: Darryl Dixon (esrever_otua) Date: 2005-07-06 21:36
getpass.getpass() on *nix platforms allows users to
input unicode characters and other NLS input. 
getpass.getpass() on Windows only allows ASCII input in
the 0-127 codepage range.  This means that getpass can
not be used cross-platform for complex passwords.
msg60767 - (view) Author: Georg Brandl (georg.brandl) * (Python committer) Date: 2005-07-07 08:46
Logged In: YES 
user_id=1188172

What makes you think that? I tried it on Windows XP, in a
cmd.exe session.

I could enter, for example, an ü (umlauted u), which in the
resulting string came out encoded as \x81, as is correct in
the encoding used by the console window, namely cp850. I
could then convert this to latin1 by using
s.decode(sys.stdin.encoding).encode("latin-1").
msg60768 - (view) Author: Darryl Dixon (esrever_otua) Date: 2005-07-07 09:18
Logged In: YES 
user_id=567623

I think that, because I've read the source, and
getpass.getpass() uses msvcrt.getch() on Windows, which
doesn't support anything at all above ASCII 255.

Also because I have a bug report pending against one of the
projects that I maintain from a user that is experiencing a
problem because of exactly this issue.

See:
https://sourceforge.net/tracker/index.php?func=detail&aid=1224877&group_id=69259&atid=523935

Also, I call shenanigans on your claim of successfully
inputting an umlaut-u into getpass.getpass(); this character
can *theoretically* be input, as it's below ASCII 255, but
in practice I can neither input it directly, nor COPY+PASTE
it from the clipboard on my WinXP SP2 with Python 2.4.1
installed.

Finally, regardless of whether "ü" works or not, other
characters that live solely in unicode, such as "¿" most
certainly do not (and never will) work (not even theoretically).

Regards,
D
msg60769 - (view) Author: Georg Brandl (georg.brandl) * (Python committer) Date: 2005-07-07 10:01
Logged In: YES 
user_id=1188172

First of all, don't accuse me of lying. If I wanted to lie
professionally, I would have become politician, not programmer.
I'm German, and using a German keyboard layout which does
have the ü, so it can be perfectly input.

Your problem is that you cannot input characters which don't
directly create keycodes on the keyboard, but must be copied
to the console in some way.

In python-dev, you said that msvcrt.getch() would have to
call _getch() a second time in the case that it returns 0x00
or 0xe0 the first time and/or return a Unicode string.

First, the documentation for _getch() says that this is a
special case for arrow and function keys (such as F1 or
<Up>). These keys don't have a representation in any
character set, as they are control keys, so how would you
represent them as Unicode?
Second: In my console, pressing F7 yields the return values
"\x00" and "A".
When the 0x00/0xe0 read the first time is secretly
swallowed, the user of msvcrt.getch() can't know whether the
user pressed "A" or F7.
That said, the behaviour of getch() is documented and correct.


But how does that all help you with your original problem,
which is that _getch() doesn't help you with Alt+XXXX
sequences or console window Copy-Paste? In my understanding
_getch() only works with characters directly input to the
console. Sorry, but then this is not a Python problem but a
Windows one.
msg60770 - (view) Author: Darryl Dixon (esrever_otua) Date: 2005-07-07 10:15
Logged In: YES 
user_id=567623

Right.  I've had enough of this nonsense, listen carefully:
as English isn't your first language I'll make this as
simple as possible:

1) I didn't come here to start a flame war, but rather to
point out a genuine deficiency in the Windows getpass,
compared to the *nix version.

2) You didn't lie, and *I didn't accuse you of that*, but
rather pointed out that all was not correct or complete in
your assertion.  *I* was right, as you carefully neglected
to mention that you're using a native keyboard, with an NLS
system that puts ü below ASCII 127 in your local codepage

3) getpass.getpass(), when called on *nix, allows me to
input '¿' and every other character under the sun.  When
called on Windows, *it does not*.  This Means Complex
Passwords With getpass() Aren't Portable.  End Of Story. It
Is Undocumented.  This Is A Bug.

4) Yes the problem isn't that getch() isn't doing what it
should, it is that getch() is the *wrong function to use* in
order to gain parity with the *nix version.

5) Finally, you seem hell-bent on ignoring what I've
written.  I was wrong about getch() behaviour on python-dev,
but That Isn't The Problem, which is why *I* didn't mention
it in this initial bug-report.  My assertion below that
getch() won't get anything above ASCII 255 is *completely
accurate*.  It is the Wrong Function To Use.  This Is A Bug
In Getpass.

6) Finally, Finally, please read this and understand: As per
the initial bug-report, the problem is that getpass on
Windows is limited in ways that getpass on *nix isn't.
msg60771 - (view) Author: Georg Brandl (georg.brandl) * (Python committer) Date: 2005-07-07 10:29
Logged In: YES 
user_id=1188172

Okay. This isn't menat to attempt a flamewar, but rather
find out what can be done to help you (and, thank you very
much for making it simple extra for me, I would have
preferred the simple version even if I was a native speaker).

You're right that this bug report is about getpass's
portability, I had a bit lost track of this. Since you
posted the link to your tracker in your previous comment, I
somehow linked this to here.

I have only two questions left.

1) Do you know another function in the MS API that allows
reading a character from the console without displaying it?

2) How do you input your characters under the sun on Unix?
Can you do this on a text console, too?
msg60772 - (view) Author: Darryl Dixon (esrever_otua) Date: 2005-07-07 10:42
Logged In: YES 
user_id=567623

I am not an expert C coder (no surprise) however this simple
code I wrote will accept a line of input (including the \r\n
on the end) without echoing it back to the user:

#include <windows.h>

#define LINE_BUF 65536

char *getstr(){
    /*
     * OK, basically, we get a handle to STDIN, take a copy
of the
     * flags currently in force, set our own to prevent
screen echo,
     * do a read of the console that returns on '\r\n'
(included in
     * returned string), restore the original flags on
STDIN, and
     * finally returns the input string.
     *
     * This is basically a re-implementation of getch(), but
instead
     * of a single char, you get a whole string (with no
screen echo).
     *
     * For docs on functions called, see:
     *
http://msdn.microsoft.com/library/en-us/dllproc/base/console_functions.asp
     */
    HANDLE hstdin;
    DWORD read_chars, mode;
    char in_chars[LINE_BUF];
    hstdin = GetStdHandle(STD_INPUT_HANDLE);
    GetConsoleMode(hstdin,
                   &mode);
    SetConsoleMode(hstdin,
                   ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT);
    ReadConsole(hstdin,
                in_chars,
                LINE_BUF,
                &read_chars,
                NULL);
    SetConsoleMode(hstdin,
                   mode);
    return in_chars;
}

This code, when SWIG-ed to use with Python 2.4.1 works
perfectly.  The key is setting the flags on the console
handle to remove the ENABLE_ECHO_INPUT flag.  Also, if
ENABLE_LINE_INPUT is removed, theoretically the
ReadConsole() function should return after each typed character.

On *nix you can use unicode_start and loadkeys to input
unicode directly, including ALT+xxxx style input.

Regards,
D
msg60773 - (view) Author: Darryl Dixon (esrever_otua) Date: 2005-07-12 00:56
Logged In: YES 
user_id=567623

Following on from earlier code; the code below implements a
complete C module for Python that provides a 'getpass'
implementation for windows that behaves similarly to
existing getpass (accepts a prompt, returns a string without
newlines).  I'm not a fantastic C coder, so this is really
just to provide ideas.


/*
 * This file is part of 'win32console'
 * Copyright 2005 Darryl A. Dixon
<esrever_otua@pythonhacker.is-a-geek.net>
 */
#include <string.h>
#include <windows.h>
#include "Python.h"

#define LINE_BUF 1024 * sizeof(TCHAR)
#define CHAR_BUF 6 * sizeof(TCHAR)

/* Module globals */
static PyObject *getpass_error = NULL;

static PyObject *getpass(self, args)
    PyObject *self, *args;
{
    char *prompt = "Password: ";
    char *newline = "\r\n";
    if (PyArg_ParseTuple(args, "|s", &prompt)){
        HANDLE hstdin, hstdout;
        DWORD read_chars, mode, written_chars;
        char inchars[LINE_BUF];
        hstdout = GetStdHandle(STD_OUTPUT_HANDLE);
        WriteConsole(hstdout,
                     prompt,
                     strlen(prompt),
                     &written_chars,
                     NULL);
        hstdin = GetStdHandle(STD_INPUT_HANDLE);
        GetConsoleMode(hstdin,
                       &mode);
        SetConsoleMode(hstdin,
                       ENABLE_LINE_INPUT |
ENABLE_PROCESSED_INPUT);
        /* Hmm, is allowing threads to run while we're
playing with the flags on
         * STDIN such a good idea?  ...Who knows... */
        Py_BEGIN_ALLOW_THREADS
        ReadConsole(hstdin,
                    inchars,
                    LINE_BUF,
                    &read_chars,
                    NULL);
        Py_END_ALLOW_THREADS
        SetConsoleMode(hstdin,
                       mode);
        WriteConsole(hstdout,
                     newline,
                     strlen(newline),
                     &written_chars,
                     NULL);
        /* -2 to strip the \015\012 from the end
-------------v */
        return PyString_FromStringAndSize(inchars,
read_chars-2);
    }else{
        return NULL;
    }
}

static PyObject *getch(self, args)
        PyObject *self, *args;
{
    if (PyArg_ParseTuple(args, "")){
        HANDLE hstdin;
        DWORD read_chars, mode;
        char inchars[CHAR_BUF];
        hstdin = GetStdHandle(STD_INPUT_HANDLE);
        GetConsoleMode(hstdin,
                       &mode);
        SetConsoleMode(hstdin,
                       ENABLE_PROCESSED_INPUT);
        Py_BEGIN_ALLOW_THREADS
        ReadConsole(hstdin,
                    inchars,
                    CHAR_BUF,
                    &read_chars,
                    NULL);
        Py_END_ALLOW_THREADS
        SetConsoleMode(hstdin,
                       mode);
        /* this should generally always be 1 ----------v */
        return PyString_FromStringAndSize(inchars, read_chars);
    }else{
        return NULL;
    }
}

static PyMethodDef methods[] = {
  {"getpass", getpass, METH_VARARGS},
  {"getch", getch, METH_VARARGS},
  {NULL, NULL},
};

void init_win32console(){
    PyObject *mod, *dict;

    mod = Py_InitModule("_win32console", methods);
    dict = PyModule_GetDict(mod);

#if PYTHON_API_VERSION >= 1007
    getpass_error = PyErr_NewException("getpass.error",
NULL, NULL);
#else
    getpass_error = Py_BuildValue("s", "getpass.error");
#endif
    PyDict_SetItemString(dict, "error", getpass_error);
}
msg114535 - (view) Author: Mark Lawrence (BreamoreBoy) * Date: 2010-08-21 18:13
Fixed in r59451.
History
Date User Action Args
2010-08-21 18:13:10BreamoreBoysetstatus: open -> closed
versions: + Python 3.2, - Python 2.7
nosy: + BreamoreBoy

messages: + msg114535

resolution: fixed
2009-02-16 02:25:57ajaksu2setpriority: normal -> low
stage: test needed
type: enhancement
components: + Library (Lib), - None
versions: + Python 2.7
2005-07-06 21:36:18esrever_otuacreate