eventlet / eventlet

Concurrent networking library for Python
https://eventlet.net
Other
1.24k stars 322 forks source link

Monkey-patching os module breaks getpass on OSX #340

Open augurar opened 7 years ago

augurar commented 7 years ago

Monkey-patching the os module causes getpass.getpass() to fail:

>>> import eventlet
>>> eventlet.monkey_patch(os=True)
>>> import getpass
>>> getpass.getpass()
Password: Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/getpass.py", line 71, in unix_getpass
    passwd = _raw_input(prompt, stream, input=input)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/getpass.py", line 133, in _raw_input
    line = input.readline()
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/socket.py", line 447, in readline
    data = self._sock.recv(self._rbufsize)
  File "/.../lib/python2.7/site-packages/eventlet/greenio/py2.py", line 164, in recv
    raise IOError(*e.args)
IOError: [Errno 22] Invalid argument

It appears that getpass() is trying to read from /dev/tty, but the kevent() syscall being used does not support terminal device files.

augurar commented 7 years ago

Here is some C code to reproduce the underlying error:

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/event.h>
#include <sys/time.h>
#include <sys/types.h>

int main(int argc, char **argv) {
    int fd = open("/dev/tty", O_RDWR | O_NOCTTY);
    if (fd < 0) {
        perror("open");
        exit(1);
    }

    int kqfd = kqueue();
    if (kqfd < 0) {
        perror("kqueue");
        exit(1);
    }

    struct kevent ke;
    EV_SET(&ke, fd, EVFILT_READ, EV_ADD, 0, 0, 0);

    struct timespec timeout;
    timeout.tv_sec = 0;
    timeout.tv_nsec = 0;

    int result = kevent(kqfd, &ke, 1, NULL, 0, &timeout);
    if (result < 0) {
        perror("kevent");
        exit(1);
    }
}
temoto commented 7 years ago

Thanks for report. You can try monkey_patch(os=False) for a temporary workaround.

augurar commented 7 years ago

Yes; another workaround is to supply getpass with the unpatched os module:

import getpass
getpass.os = eventlet.patcher.original('os')
temoto commented 7 years ago

Yes, that one is much better.