summaryrefslogtreecommitdiff
path: root/eventlet
diff options
context:
space:
mode:
Diffstat (limited to 'eventlet')
-rw-r--r--eventlet/green/http/__init__.py2
-rw-r--r--eventlet/green/http/client.py9
-rw-r--r--eventlet/green/http/cookiejar.py13
-rw-r--r--eventlet/green/http/cookies.py7
-rw-r--r--eventlet/green/http/server.py17
-rw-r--r--eventlet/green/selectors.py11
-rw-r--r--eventlet/green/subprocess.py12
-rw-r--r--eventlet/green/urllib.py38
-rw-r--r--eventlet/green/urllib/__init__.py40
-rw-r--r--eventlet/green/urllib/error.py4
-rw-r--r--eventlet/green/urllib/parse.py3
-rw-r--r--eventlet/green/urllib/request.py44
-rw-r--r--eventlet/green/urllib/response.py3
-rw-r--r--eventlet/greenio/__init__.py8
-rw-r--r--eventlet/greenio/base.py (renamed from eventlet/greenio.py)238
-rw-r--r--eventlet/greenio/py2.py222
-rw-r--r--eventlet/greenio/py3.py191
-rw-r--r--eventlet/wsgi.py30
18 files changed, 603 insertions, 289 deletions
diff --git a/eventlet/green/http/__init__.py b/eventlet/green/http/__init__.py
new file mode 100644
index 0000000..c9e2a23
--- /dev/null
+++ b/eventlet/green/http/__init__.py
@@ -0,0 +1,2 @@
+from eventlet.support import six
+assert six.PY3, 'This is a Python 3 module'
diff --git a/eventlet/green/http/client.py b/eventlet/green/http/client.py
new file mode 100644
index 0000000..480a252
--- /dev/null
+++ b/eventlet/green/http/client.py
@@ -0,0 +1,9 @@
+from eventlet import patcher
+from eventlet.green import os, socket
+from eventlet.green.urllib import parse as urllib_parse
+
+patcher.inject('http.client', globals(),
+ ('os', os), ('socket', socket), ('urllib.parse', urllib_parse))
+
+del patcher
+del urllib_parse
diff --git a/eventlet/green/http/cookiejar.py b/eventlet/green/http/cookiejar.py
new file mode 100644
index 0000000..5e511d2
--- /dev/null
+++ b/eventlet/green/http/cookiejar.py
@@ -0,0 +1,13 @@
+from eventlet.green import threading, time
+from eventlet.green.http import client
+from eventlet.green.urllib import parse as urllib_parse, request as urllib_request
+from eventlet import patcher
+
+patcher.inject('http.cookiejar', globals(),
+ ('http.client', client), ('threading', threading),
+ ('urllib.parse', urllib_parse), ('urllib.request', urllib_request),
+ ('time', time))
+
+del urllib_request
+del urllib_parse
+del patcher
diff --git a/eventlet/green/http/cookies.py b/eventlet/green/http/cookies.py
new file mode 100644
index 0000000..e139069
--- /dev/null
+++ b/eventlet/green/http/cookies.py
@@ -0,0 +1,7 @@
+from eventlet import patcher
+from eventlet.green import time
+
+patcher.inject('http.cookies', globals())
+_getdate = patcher.patch_function(_getdate, ('time', time))
+
+del patcher
diff --git a/eventlet/green/http/server.py b/eventlet/green/http/server.py
new file mode 100644
index 0000000..35c3ab2
--- /dev/null
+++ b/eventlet/green/http/server.py
@@ -0,0 +1,17 @@
+from eventlet import patcher
+from eventlet.green import os, time, select, socket, SocketServer, subprocess
+from eventlet.green.http import client
+from eventlet.green.urllib import parse as urllib_parse
+
+patcher.inject('http.server', globals(),
+ ('http.client', client), ('os', os), ('select', select),
+ ('socket', socket), ('socketserver', SocketServer), ('time', time),
+ ('urllib.parse', urllib_parse))
+
+
+CGIHTTPRequestHandler.run_cgi = patcher.patch_function(
+ CGIHTTPRequestHandler.run_cgi, ('subprocess', subprocess))
+
+del urllib_parse
+del client
+del patcher
diff --git a/eventlet/green/selectors.py b/eventlet/green/selectors.py
new file mode 100644
index 0000000..26427ec
--- /dev/null
+++ b/eventlet/green/selectors.py
@@ -0,0 +1,11 @@
+import sys
+
+from eventlet import patcher
+from eventlet.green import select
+
+patcher.inject('selectors', globals(), ('select', select))
+
+del patcher
+
+if sys.platform != 'win32':
+ SelectSelector._select = staticmethod(select.select)
diff --git a/eventlet/green/subprocess.py b/eventlet/green/subprocess.py
index 1d7c49a..7ce38cf 100644
--- a/eventlet/green/subprocess.py
+++ b/eventlet/green/subprocess.py
@@ -1,15 +1,21 @@
import errno
-import time
+import sys
from types import FunctionType
import eventlet
from eventlet import greenio
from eventlet import patcher
-from eventlet.green import select
+from eventlet.green import select, threading, time
from eventlet.support import six
-patcher.inject('subprocess', globals(), ('select', select))
+to_patch = [('select', select), ('threading', threading), ('time', time)]
+
+if sys.version_info > (3, 4):
+ from eventlet.green import selectors
+ to_patch.append(('selectors', selectors))
+
+patcher.inject('subprocess', globals(), *to_patch)
subprocess_orig = __import__("subprocess")
diff --git a/eventlet/green/urllib.py b/eventlet/green/urllib.py
deleted file mode 100644
index f5c8f13..0000000
--- a/eventlet/green/urllib.py
+++ /dev/null
@@ -1,38 +0,0 @@
-from eventlet import patcher
-from eventlet.green import socket
-from eventlet.green import time
-from eventlet.green import httplib
-from eventlet.green import ftplib
-
-to_patch = [('socket', socket), ('httplib', httplib),
- ('time', time), ('ftplib', ftplib)]
-try:
- from eventlet.green import ssl
- to_patch.append(('ssl', ssl))
-except ImportError:
- pass
-
-patcher.inject('urllib', globals(), *to_patch)
-try:
- URLopener
-except NameError:
- patcher.inject('urllib.request', globals(), *to_patch)
-
-
-# patch a bunch of things that have imports inside the
-# function body; this is lame and hacky but I don't feel
-# too bad because urllib is a hacky pile of junk that no
-# one should be using anyhow
-URLopener.open_http = patcher.patch_function(URLopener.open_http, ('httplib', httplib))
-if hasattr(URLopener, 'open_https'):
- URLopener.open_https = patcher.patch_function(URLopener.open_https, ('httplib', httplib))
-
-URLopener.open_ftp = patcher.patch_function(URLopener.open_ftp, ('ftplib', ftplib))
-ftpwrapper.init = patcher.patch_function(ftpwrapper.init, ('ftplib', ftplib))
-ftpwrapper.retrfile = patcher.patch_function(ftpwrapper.retrfile, ('ftplib', ftplib))
-
-del patcher
-
-# Run test program when run as a script
-if __name__ == '__main__':
- main()
diff --git a/eventlet/green/urllib/__init__.py b/eventlet/green/urllib/__init__.py
new file mode 100644
index 0000000..7cb4ea6
--- /dev/null
+++ b/eventlet/green/urllib/__init__.py
@@ -0,0 +1,40 @@
+from eventlet import patcher
+from eventlet.green import socket
+from eventlet.green import time
+from eventlet.green import httplib
+from eventlet.green import ftplib
+from eventlet.support import six
+
+if six.PY2:
+ to_patch = [('socket', socket), ('httplib', httplib),
+ ('time', time), ('ftplib', ftplib)]
+ try:
+ from eventlet.green import ssl
+ to_patch.append(('ssl', ssl))
+ except ImportError:
+ pass
+
+ patcher.inject('urllib', globals(), *to_patch)
+ try:
+ URLopener
+ except NameError:
+ patcher.inject('urllib.request', globals(), *to_patch)
+
+
+ # patch a bunch of things that have imports inside the
+ # function body; this is lame and hacky but I don't feel
+ # too bad because urllib is a hacky pile of junk that no
+ # one should be using anyhow
+ URLopener.open_http = patcher.patch_function(URLopener.open_http, ('httplib', httplib))
+ if hasattr(URLopener, 'open_https'):
+ URLopener.open_https = patcher.patch_function(URLopener.open_https, ('httplib', httplib))
+
+ URLopener.open_ftp = patcher.patch_function(URLopener.open_ftp, ('ftplib', ftplib))
+ ftpwrapper.init = patcher.patch_function(ftpwrapper.init, ('ftplib', ftplib))
+ ftpwrapper.retrfile = patcher.patch_function(ftpwrapper.retrfile, ('ftplib', ftplib))
+
+ del patcher
+
+ # Run test program when run as a script
+ if __name__ == '__main__':
+ main()
diff --git a/eventlet/green/urllib/error.py b/eventlet/green/urllib/error.py
new file mode 100644
index 0000000..6913813
--- /dev/null
+++ b/eventlet/green/urllib/error.py
@@ -0,0 +1,4 @@
+from eventlet import patcher
+from eventlet.green.urllib import response
+patcher.inject('urllib.error', globals(), ('urllib.response', response))
+del patcher
diff --git a/eventlet/green/urllib/parse.py b/eventlet/green/urllib/parse.py
new file mode 100644
index 0000000..f3a8924
--- /dev/null
+++ b/eventlet/green/urllib/parse.py
@@ -0,0 +1,3 @@
+from eventlet import patcher
+patcher.inject('urllib.parse', globals())
+del patcher
diff --git a/eventlet/green/urllib/request.py b/eventlet/green/urllib/request.py
new file mode 100644
index 0000000..8160bb9
--- /dev/null
+++ b/eventlet/green/urllib/request.py
@@ -0,0 +1,44 @@
+from eventlet import patcher
+from eventlet.green import ftplib, os, socket, time
+from eventlet.green.http import client as http_client
+from eventlet.green.urllib import error, parse, response
+
+# TODO should we also have green email version?
+# import email
+
+
+to_patch = [
+ ('http.client', http_client),
+ ('os', os),
+ ('socket', socket),
+ ('time', time),
+ ('urllib.error', error),
+ ('urllib.parse', parse),
+ ('urllib.response', response),
+]
+
+try:
+ from eventlet.green import ssl
+except ImportError:
+ pass
+else:
+ to_patch.append(('ssl', ssl))
+
+patcher.inject('urllib.request', globals(), *to_patch)
+del to_patch
+
+to_patch_in_functions = [('ftplib', ftplib)]
+del ftplib
+
+FTPHandler.ftp_open = patcher.patch_function(FTPHandler.ftp_open, *to_patch_in_functions)
+URLopener.open_ftp = patcher.patch_function(URLopener.open_ftp, *to_patch_in_functions)
+
+ftperrors = patcher.patch_function(ftperrors, *to_patch_in_functions)
+
+ftpwrapper.init = patcher.patch_function(ftpwrapper.init, *to_patch_in_functions)
+ftpwrapper.retrfile = patcher.patch_function(ftpwrapper.retrfile, *to_patch_in_functions)
+
+del error
+del parse
+del response
+del to_patch_in_functions
diff --git a/eventlet/green/urllib/response.py b/eventlet/green/urllib/response.py
new file mode 100644
index 0000000..f9aaba5
--- /dev/null
+++ b/eventlet/green/urllib/response.py
@@ -0,0 +1,3 @@
+from eventlet import patcher
+patcher.inject('urllib.response', globals())
+del patcher
diff --git a/eventlet/greenio/__init__.py b/eventlet/greenio/__init__.py
new file mode 100644
index 0000000..72cd33e
--- /dev/null
+++ b/eventlet/greenio/__init__.py
@@ -0,0 +1,8 @@
+from eventlet.support import six
+
+from eventlet.greenio.base import * # noqa
+
+if six.PY2:
+ from eventlet.greenio.py2 import * # noqa
+else:
+ from eventlet.greenio.py3 import * # noqa
diff --git a/eventlet/greenio.py b/eventlet/greenio/base.py
index 46e3909..8da51ca 100644
--- a/eventlet/greenio.py
+++ b/eventlet/greenio/base.py
@@ -7,9 +7,13 @@ import time
import warnings
from eventlet.support import get_errno, six
-from eventlet.hubs import trampoline, notify_close, notify_opened, IOClosed
+from eventlet.hubs import trampoline, notify_opened, IOClosed
-__all__ = ['GreenSocket', 'GreenPipe', 'shutdown_safe']
+__all__ = [
+ 'GreenSocket', '_GLOBAL_DEFAULT_TIMEOUT', 'set_nonblocking',
+ 'SOCKET_CLOSED', 'CONNECT_ERR', 'CONNECT_SUCCESS',
+ 'shutdown_safe', 'SSL',
+]
BUFFER_SIZE = 4096
CONNECT_ERR = set((errno.EINPROGRESS, errno.EALREADY, errno.EWOULDBLOCK))
@@ -17,11 +21,8 @@ CONNECT_SUCCESS = set((0, errno.EISCONN))
if sys.platform[:3] == "win":
CONNECT_ERR.add(errno.WSAEINVAL) # Bug 67
-if six.PY3:
- from io import IOBase as file
- _fileobject = socket.SocketIO
-elif six.PY2:
- _fileobject = socket._fileobject
+if six.PY2:
+ _python2_fileobject = socket._fileobject
def socket_connect(descriptor, address):
@@ -293,7 +294,7 @@ class GreenSocket(object):
else:
def makefile(self, *args, **kwargs):
dupped = self.dup()
- res = _fileobject(dupped, *args, **kwargs)
+ res = _python2_fileobject(dupped, *args, **kwargs)
if hasattr(dupped, "_drop"):
dupped._drop()
return res
@@ -418,119 +419,11 @@ class GreenSocket(object):
getattr(self.fd, '_sock', self.fd)._drop()
-class _SocketDuckForFd(object):
- """Class implementing all socket method used by _fileobject
- in cooperative manner using low level os I/O calls.
- """
- _refcount = 0
-
- def __init__(self, fileno):
- self._fileno = fileno
- notify_opened(fileno)
- self._closed = False
-
- def _trampoline(self, fd, read=False, write=False, timeout=None, timeout_exc=None):
- if self._closed:
- # Don't trampoline if we're already closed.
- raise IOClosed()
- try:
- return trampoline(fd, read=read, write=write, timeout=timeout,
- timeout_exc=timeout_exc,
- mark_as_closed=self._mark_as_closed)
- except IOClosed:
- # Our fileno has been obsoleted. Defang ourselves to
- # prevent spurious closes.
- self._mark_as_closed()
- raise
-
- def _mark_as_closed(self):
- self._closed = True
-
- @property
- def _sock(self):
- return self
-
- def fileno(self):
- return self._fileno
-
- def recv(self, buflen):
- while True:
- try:
- data = os.read(self._fileno, buflen)
- return data
- except OSError as e:
- if get_errno(e) not in SOCKET_BLOCKING:
- raise IOError(*e.args)
- self._trampoline(self, read=True)
-
- def recv_into(self, buf, nbytes=0, flags=0):
- if nbytes == 0:
- nbytes = len(buf)
- data = self.recv(nbytes)
- buf[:nbytes] = data
- return len(data)
-
- def send(self, data):
- while True:
- try:
- return os.write(self._fileno, data)
- except OSError as e:
- if get_errno(e) not in SOCKET_BLOCKING:
- raise IOError(*e.args)
- else:
- trampoline(self, write=True)
-
- def sendall(self, data):
- len_data = len(data)
- os_write = os.write
- fileno = self._fileno
- try:
- total_sent = os_write(fileno, data)
- except OSError as e:
- if get_errno(e) != errno.EAGAIN:
- raise IOError(*e.args)
- total_sent = 0
- while total_sent < len_data:
- self._trampoline(self, write=True)
- try:
- total_sent += os_write(fileno, data[total_sent:])
- except OSError as e:
- if get_errno(e) != errno. EAGAIN:
- raise IOError(*e.args)
-
- def __del__(self):
- self._close()
-
- def _close(self):
- notify_close(self._fileno)
- self._mark_as_closed()
- try:
- os.close(self._fileno)
- except:
- # os.close may fail if __init__ didn't complete
- # (i.e file dscriptor passed to popen was invalid
- pass
-
- def __repr__(self):
- return "%s:%d" % (self.__class__.__name__, self._fileno)
-
- def _reuse(self):
- self._refcount += 1
-
- def _drop(self):
- self._refcount -= 1
- if self._refcount == 0:
- self._close()
- # Python3
- _decref_socketios = _drop
-
-
-def _operationOnClosedFile(*args, **kwargs):
+def _operation_on_closed_file(*args, **kwargs):
raise ValueError("I/O operation on closed file")
-class GreenPipe(_fileobject):
- """
+greenpipe_doc = """
GreenPipe is a cooperative replacement for file class.
It will cooperate on pipes. It will block on regular file.
Differneces from file class:
@@ -542,115 +435,6 @@ class GreenPipe(_fileobject):
- file argument can be descriptor, file name or file object.
"""
- def __init__(self, f, mode='r', bufsize=-1):
- if not isinstance(f, six.string_types + (int, file)):
- raise TypeError('f(ile) should be int, str, unicode or file, not %r' % f)
-
- if isinstance(f, six.string_types):
- f = open(f, mode, 0)
-
- if isinstance(f, int):
- fileno = f
- self._name = "<fd:%d>" % fileno
- else:
- fileno = os.dup(f.fileno())
- self._name = f.name
- if not (
- f.mode == mode or
- six.PY3 and f.mode == 'rb+' and mode == 'wb+'
- ):
- raise ValueError('file.mode %r does not match mode parameter %r' % (f.mode, mode))
- self._name = f.name
- f.close()
-
- if six.PY3:
- mode = mode.rstrip('+')
-
- super(GreenPipe, self).__init__(_SocketDuckForFd(fileno), mode)
- set_nonblocking(self)
- self.softspace = 0
-
- if six.PY3:
- def write(self, data):
- while data:
- sent = _fileobject.write(self, data)
- data = data[sent:]
-
- @property
- def name(self):
- return self._name
-
- def __repr__(self):
- return "<%s %s %r, mode %r at 0x%x>" % (
- self.closed and 'closed' or 'open',
- self.__class__.__name__,
- self.name,
- self.mode,
- (id(self) < 0) and (sys.maxint + id(self)) or id(self))
-
- def close(self):
- super(GreenPipe, self).close()
- for method in [
- 'fileno', 'flush', 'isatty', 'next', 'read', 'readinto',
- 'readline', 'readlines', 'seek', 'tell', 'truncate',
- 'write', 'xreadlines', '__iter__', '__next__', 'writelines']:
- setattr(self, method, _operationOnClosedFile)
-
- def __enter__(self):
- return self
-
- def __exit__(self, *args):
- self.close()
-
- def _get_readahead_len(self):
- return len(self._rbuf.getvalue())
-
- def _clear_readahead_buf(self):
- len = self._get_readahead_len()
- if len > 0:
- self.read(len)
-
- def tell(self):
- self.flush()
- try:
- return os.lseek(self.fileno(), 0, 1) - self._get_readahead_len()
- except OSError as e:
- raise IOError(*e.args)
-
- def seek(self, offset, whence=0):
- self.flush()
- if whence == 1 and offset == 0: # tell synonym
- return self.tell()
- if whence == 1: # adjust offset by what is read ahead
- offset -= self._get_readahead_len()
- try:
- rv = os.lseek(self.fileno(), offset, whence)
- except OSError as e:
- raise IOError(*e.args)
- else:
- self._clear_readahead_buf()
- return rv
-
- if getattr(file, "truncate", None): # not all OSes implement truncate
- def truncate(self, size=-1):
- self.flush()
- if size == -1:
- size = self.tell()
- try:
- rv = os.ftruncate(self.fileno(), size)
- except OSError as e:
- raise IOError(*e.args)
- else:
- self.seek(size) # move position&clear buffer
- return rv
-
- def isatty(self):
- try:
- return os.isatty(self.fileno())
- except OSError as e:
- raise IOError(*e.args)
-
-
# import SSL module here so we can refer to greenio.SSL.exceptionclass
try:
from OpenSSL import SSL
diff --git a/eventlet/greenio/py2.py b/eventlet/greenio/py2.py
new file mode 100644
index 0000000..6fa6cb7
--- /dev/null
+++ b/eventlet/greenio/py2.py
@@ -0,0 +1,222 @@
+import errno
+import os
+
+from eventlet.greenio.base import (
+ _operation_on_closed_file,
+ greenpipe_doc,
+ set_nonblocking,
+ socket,
+ SOCKET_BLOCKING,
+)
+from eventlet.hubs import trampoline, notify_close, notify_opened, IOClosed
+from eventlet.support import get_errno, six
+
+__all__ = ['_fileobject', 'GreenPipe']
+
+_fileobject = socket._fileobject
+
+
+class GreenPipe(_fileobject):
+
+ __doc__ = greenpipe_doc
+
+ def __init__(self, f, mode='r', bufsize=-1):
+ if not isinstance(f, six.string_types + (int, file)):
+ raise TypeError('f(ile) should be int, str, unicode or file, not %r' % f)
+
+ if isinstance(f, six.string_types):
+ f = open(f, mode, 0)
+
+ if isinstance(f, int):
+ fileno = f
+ self._name = "<fd:%d>" % fileno
+ else:
+ fileno = os.dup(f.fileno())
+ self._name = f.name
+ if f.mode != mode:
+ raise ValueError('file.mode %r does not match mode parameter %r' % (f.mode, mode))
+ self._name = f.name
+ f.close()
+
+ super(GreenPipe, self).__init__(_SocketDuckForFd(fileno), mode)
+ set_nonblocking(self)
+ self.softspace = 0
+
+ @property
+ def name(self):
+ return self._name
+
+ def __repr__(self):
+ return "<%s %s %r, mode %r at 0x%x>" % (
+ self.closed and 'closed' or 'open',
+ self.__class__.__name__,
+ self.name,
+ self.mode,
+ (id(self) < 0) and (sys.maxint + id(self)) or id(self))
+
+ def close(self):
+ super(GreenPipe, self).close()
+ for method in [
+ 'fileno', 'flush', 'isatty', 'next', 'read', 'readinto',
+ 'readline', 'readlines', 'seek', 'tell', 'truncate',
+ 'write', 'xreadlines', '__iter__', '__next__', 'writelines']:
+ setattr(self, method, _operation_on_closed_file)
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, *args):
+ self.close()
+
+ def _get_readahead_len(self):
+ return len(self._rbuf.getvalue())
+
+ def _clear_readahead_buf(self):
+ len = self._get_readahead_len()
+ if len > 0:
+ self.read(len)
+
+ def tell(self):
+ self.flush()
+ try:
+ return os.lseek(self.fileno(), 0, 1) - self._get_readahead_len()
+ except OSError as e:
+ raise IOError(*e.args)
+
+ def seek(self, offset, whence=0):
+ self.flush()
+ if whence == 1 and offset == 0: # tell synonym
+ return self.tell()
+ if whence == 1: # adjust offset by what is read ahead
+ offset -= self._get_readahead_len()
+ try:
+ rv = os.lseek(self.fileno(), offset, whence)
+ except OSError as e:
+ raise IOError(*e.args)
+ else:
+ self._clear_readahead_buf()
+ return rv
+
+ if getattr(file, "truncate", None): # not all OSes implement truncate
+ def truncate(self, size=-1):
+ self.flush()
+ if size == -1:
+ size = self.tell()
+ try:
+ rv = os.ftruncate(self.fileno(), size)
+ except OSError as e:
+ raise IOError(*e.args)
+ else:
+ self.seek(size) # move position&clear buffer
+ return rv
+
+ def isatty(self):
+ try:
+ return os.isatty(self.fileno())
+ except OSError as e:
+ raise IOError(*e.args)
+
+
+class _SocketDuckForFd(object):
+ """Class implementing all socket method used by _fileobject
+ in cooperative manner using low level os I/O calls.
+ """
+ _refcount = 0
+
+ def __init__(self, fileno):
+ self._fileno = fileno
+ notify_opened(fileno)
+ self._closed = False
+
+ def _trampoline(self, fd, read=False, write=False, timeout=None, timeout_exc=None):
+ if self._closed:
+ # Don't trampoline if we're already closed.
+ raise IOClosed()
+ try:
+ return trampoline(fd, read=read, write=write, timeout=timeout,
+ timeout_exc=timeout_exc,
+ mark_as_closed=self._mark_as_closed)
+ except IOClosed:
+ # Our fileno has been obsoleted. Defang ourselves to
+ # prevent spurious closes.
+ self._mark_as_closed()
+ raise
+
+ def _mark_as_closed(self):
+ self._closed = True
+
+ @property
+ def _sock(self):
+ return self
+
+ def fileno(self):
+ return self._fileno
+
+ def recv(self, buflen):
+ while True:
+ try:
+ data = os.read(self._fileno, buflen)
+ return data
+ except OSError as e:
+ if get_errno(e) not in SOCKET_BLOCKING:
+ raise IOError(*e.args)
+ self._trampoline(self, read=True)
+
+ def recv_into(self, buf, nbytes=0, flags=0):
+ if nbytes == 0:
+ nbytes = len(buf)
+ data = self.recv(nbytes)
+ buf[:nbytes] = data
+ return len(data)
+
+ def send(self, data):
+ while True:
+ try:
+ return os.write(self._fileno, data)
+ except OSError as e:
+ if get_errno(e) not in SOCKET_BLOCKING:
+ raise IOError(*e.args)
+ else:
+ trampoline(self, write=True)
+
+ def sendall(self, data):
+ len_data = len(data)
+ os_write = os.write
+ fileno = self._fileno
+ try:
+ total_sent = os_write(fileno, data)
+ except OSError as e:
+ if get_errno(e) != errno.EAGAIN:
+ raise IOError(*e.args)
+ total_sent = 0
+ while total_sent < len_data:
+ self._trampoline(self, write=True)
+ try:
+ total_sent += os_write(fileno, data[total_sent:])
+ except OSError as e:
+ if get_errno(e) != errno. EAGAIN:
+ raise IOError(*e.args)
+
+ def __del__(self):
+ self._close()
+
+ def _close(self):
+ notify_close(self._fileno)
+ self._mark_as_closed()
+ try:
+ os.close(self._fileno)
+ except:
+ # os.close may fail if __init__ didn't complete
+ # (i.e file dscriptor passed to popen was invalid
+ pass
+
+ def __repr__(self):
+ return "%s:%d" % (self.__class__.__name__, self._fileno)
+
+ def _reuse(self):
+ self._refcount += 1
+
+ def _drop(self):
+ self._refcount -= 1
+ if self._refcount == 0:
+ self._close()
diff --git a/eventlet/greenio/py3.py b/eventlet/greenio/py3.py
new file mode 100644
index 0000000..e41663a
--- /dev/null
+++ b/eventlet/greenio/py3.py
@@ -0,0 +1,191 @@
+import _pyio as _original_pyio
+import errno
+import os as _original_os
+import socket as _original_socket
+from io import (
+ BufferedRandom as _OriginalBufferedRandom,
+ BufferedReader as _OriginalBufferedReader,
+ BufferedWriter as _OriginalBufferedWriter,
+ DEFAULT_BUFFER_SIZE,
+ TextIOWrapper as _OriginalTextIOWrapper,
+ IOBase as _OriginalIOBase,
+)
+from types import FunctionType
+
+from eventlet.greenio.base import (
+ _operation_on_closed_file,
+ greenpipe_doc,
+ set_nonblocking,
+ SOCKET_BLOCKING,
+)
+from eventlet.hubs import notify_close, notify_opened, IOClosed, trampoline
+from eventlet.support import get_errno, six
+
+__all__ = ['_fileobject', 'GreenPipe']
+
+# TODO get rid of this, it only seems like the original _fileobject
+_fileobject = _original_socket.SocketIO
+
+# Large part of the following code is copied from the original
+# eventlet.greenio module
+
+
+class GreenFileIO(_OriginalIOBase):
+ def __init__(self, name, mode='r', closefd=True, opener=None):
+ if isinstance(name, int):
+ fileno = name
+ self._name = "<fd:%d>" % fileno
+ else:
+ assert isinstance(name, six.string_types)
+ with open(name, mode) as fd:
+ self._name = fd.name
+ fileno = _original_os.dup(fd.fileno())
+
+ notify_opened(fileno)
+ self._fileno = fileno
+ self._mode = mode
+ self._closed = False
+ set_nonblocking(self)
+ self._seekable = None
+
+ @property
+ def closed(self):
+ return self._closed
+
+ def seekable(self):
+ if self._seekable is None:
+ try:
+ _original_os.lseek(self._fileno, 0, _original_os.SEEK_CUR)
+ except IOError as e:
+ if get_errno(e) == errno.ESPIPE:
+ self._seekable = False
+ else:
+ raise
+ else:
+ self._seekable = True
+
+ return self._seekable
+
+ def readable(self):
+ return 'r' in self._mode or '+' in self._mode
+
+ def writable(self):
+ return 'w' in self._mode or '+' in self._mode
+
+ def fileno(self):
+ return self._fileno
+
+ def read(self, buflen):
+ while True:
+ try:
+ return _original_os.read(self._fileno, buflen)
+ except OSError as e:
+ if get_errno(e) not in SOCKET_BLOCKING:
+ raise IOError(*e.args)
+ self._trampoline(self, read=True)
+
+ def readinto(self, b):
+ up_to = len(b)
+ data = self.read(up_to)
+ bytes_read = len(data)
+ b[:bytes_read] = data
+ return bytes_read
+
+ def isatty(self):
+ try:
+ return _original_os.isatty(self.fileno())
+ except OSError as e:
+ raise IOError(*e.args)
+
+ def _trampoline(self, fd, read=False, write=False, timeout=None, timeout_exc=None):
+ if self._closed:
+ # Don't trampoline if we're already closed.
+ raise IOClosed()
+ try:
+ return trampoline(fd, read=read, write=write, timeout=timeout,
+ timeout_exc=timeout_exc,
+ mark_as_closed=self._mark_as_closed)
+ except IOClosed:
+ # Our fileno has been obsoleted. Defang ourselves to
+ # prevent spurious closes.
+ self._mark_as_closed()
+ raise
+
+ def _mark_as_closed(self):
+ """ Mark this socket as being closed """
+ self._closed = True
+
+ def write(self, data):
+ while True:
+ try:
+ return _original_os.write(self._fileno, data)
+ except OSError as e:
+ if get_errno(e) not in SOCKET_BLOCKING:
+ raise IOError(*e.args)
+ else:
+ trampoline(self, write=True)
+
+ def close(self):
+ _original_os.close(self._fileno)
+ notify_close(self._fileno)
+ self._closed = True
+ for method in [
+ 'fileno', 'flush', 'isatty', 'next', 'read', 'readinto',
+ 'readline', 'readlines', 'seek', 'tell', 'truncate',
+ 'write', 'xreadlines', '__iter__', '__next__', 'writelines']:
+ setattr(self, method, _operation_on_closed_file)
+
+ def truncate(self, size=-1):
+ if size == -1:
+ size = self.tell()
+ try:
+ rv = _original_os.ftruncate(self._fileno, size)
+ except OSError as e:
+ raise IOError(*e.args)
+ else:
+ self.seek(size) # move position&clear buffer
+ return rv
+
+ def seek(self, offset, whence=_original_os.SEEK_SET):
+ try:
+ return _original_os.lseek(self._fileno, offset, whence)
+ except OSError as e:
+ raise IOError(*e.args)
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, *args):
+ self.close()
+
+
+_open_environment = dict(globals())
+_open_environment.update(dict(
+ BufferedRandom=_OriginalBufferedRandom,
+ BufferedWriter=_OriginalBufferedWriter,
+ BufferedReader=_OriginalBufferedReader,
+ TextIOWrapper=_OriginalTextIOWrapper,
+ FileIO=GreenFileIO,
+ os=_original_os,
+))
+
+_open = FunctionType(
+ six.get_function_code(_original_pyio.open),
+ _open_environment,
+)
+
+
+def GreenPipe(name, mode="r", buffering=-1, encoding=None, errors=None,
+ newline=None, closefd=True, opener=None):
+ try:
+ fileno = name.fileno()
+ except AttributeError:
+ pass
+ else:
+ fileno = _original_os.dup(fileno)
+ name.close()
+ name = fileno
+
+ return _open(name, mode, buffering, encoding, errors, newline, closefd, opener)
+
+GreenPipe.__doc__ = greenpipe_doc
diff --git a/eventlet/wsgi.py b/eventlet/wsgi.py
index e69d107..05b629f 100644
--- a/eventlet/wsgi.py
+++ b/eventlet/wsgi.py
@@ -8,12 +8,13 @@ import warnings
from eventlet.green import BaseHTTPServer
from eventlet.green import socket
-from eventlet.green import urllib
from eventlet import greenio
from eventlet import greenpool
from eventlet import support
from eventlet.support import six
+from eventlet.support.six.moves import urllib
+
DEFAULT_MAX_SIMULTANEOUS_REQUESTS = 1024
DEFAULT_MAX_HTTP_VERSION = 'HTTP/1.1'
@@ -395,24 +396,8 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler):
towrite.append(six.b("%x" % (len(data),)) + b"\r\n" + data + b"\r\n")
else:
towrite.append(data)
- try:
- _writelines(towrite)
- length[0] = length[0] + sum(map(len, towrite))
- except UnicodeEncodeError:
- self.server.log_message(
- "Encountered non-ascii unicode while attempting to write"
- "wsgi response: %r" %
- [x for x in towrite if isinstance(x, six.text_type)])
- self.server.log_message(traceback.format_exc())
- _writelines(
- ["HTTP/1.1 500 Internal Server Error\r\n",
- "Connection: close\r\n",
- "Content-type: text/plain\r\n",
- "Content-length: 98\r\n",
- "Date: %s\r\n" % format_date_time(time.time()),
- "\r\n",
- ("Internal Server Error: wsgi application passed "
- "a unicode object to the server instead of a string.")])
+ _writelines(towrite)
+ length[0] = length[0] + sum(map(len, towrite))
def start_response(status, response_headers, exc_info=None):
status_code[0] = status.split()[0]
@@ -456,6 +441,9 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler):
minimum_write_chunk_size = int(self.environ.get(
'eventlet.minimum_write_chunk_size', self.minimum_chunk_size))
for data in result:
+ if isinstance(data, six.text_type):
+ data = data.encode('ascii')
+
towrite.append(data)
towrite_size += len(data)
if towrite_size >= minimum_write_chunk_size:
@@ -472,7 +460,7 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler):
self.close_connection = 1
tb = traceback.format_exc()
self.server.log_message(tb)
- if not headers_set:
+ if not headers_sent:
err_body = six.b(tb) if self.server.debug else b''
start_response("500 Internal Server Error",
[('Content-type', 'text/plain'),
@@ -522,7 +510,7 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler):
pq = self.path.split('?', 1)
env['RAW_PATH_INFO'] = pq[0]
- env['PATH_INFO'] = urllib.unquote(pq[0])
+ env['PATH_INFO'] = urllib.parse.unquote(pq[0])
if len(pq) > 1:
env['QUERY_STRING'] = pq[1]