diff options
| author | Jakub Stasiak <jakub@stasiak.at> | 2014-11-02 12:29:13 +0000 |
|---|---|---|
| committer | Jakub Stasiak <jakub@stasiak.at> | 2014-11-04 23:56:59 +0000 |
| commit | 01093df590b12cc0ee6aa46c2aaf87458cf3ce78 (patch) | |
| tree | 1fac4642a37bc85e338fc10c184421476e455be7 /eventlet | |
| parent | 67cde41d03c0bccb12fd7d5f6d7e155d6da95e40 (diff) | |
| download | eventlet-python3-clean.tar.gz | |
Python 3 compat; Improve WSGI, WS, threading and testspython3-clean
This includes:
* patching more tests to pass
* removing few unit tests which I think are redundant
* repeating SSL socket reads in a loop to read all data (I suspect this
is related to the fact that writelines is used in the server code
there and Python 3 writelines calls write/send repeatedly while on
Python 2 it calls it once; on one hand there's no guarantee that
single recv/read will return all data sent by the server, on the other
hand it's quite suspicious that the number of required reads seems to
be connected to the number of sends on the other side of the
connection)
* working through Python 2/Python 3 threading and thread differences;
the lock code I used is the simplest way I could make the tests
pass but will likely need to be modified in order to match the
original
This commit includes 6bcb1dc3686f72b0f1af803b46883ce4f5e4b8df and closes
GH #153
Diffstat (limited to 'eventlet')
| -rw-r--r-- | eventlet/green/thread.py | 12 | ||||
| -rw-r--r-- | eventlet/green/threading.py | 13 | ||||
| -rw-r--r-- | eventlet/websocket.py | 51 | ||||
| -rw-r--r-- | eventlet/wsgi.py | 30 |
4 files changed, 54 insertions, 52 deletions
diff --git a/eventlet/green/thread.py b/eventlet/green/thread.py index 66bb581..d2d492a 100644 --- a/eventlet/green/thread.py +++ b/eventlet/green/thread.py @@ -1,6 +1,6 @@ """Implements the standard thread module, using greenthreads.""" from eventlet.support.six.moves import _thread as __thread -from eventlet.support import greenlets as greenlet +from eventlet.support import greenlets as greenlet, six from eventlet import greenthread from eventlet.semaphore import Semaphore as LockType @@ -13,6 +13,16 @@ error = __thread.error __threadcount = 0 +if six.PY3: + def _set_sentinel(): + # HACK this is dummy code + # TODO possibly reimplement this: + # https://hg.python.org/cpython/file/b5e9bc4352e1/Modules/_threadmodule.c#l1203 + return allocate_lock() + + TIMEOUT_MAX = __thread.TIMEOUT_MAX + + def _count(): return __threadcount diff --git a/eventlet/green/threading.py b/eventlet/green/threading.py index 5c56ba1..3176261 100644 --- a/eventlet/green/threading.py +++ b/eventlet/green/threading.py @@ -2,12 +2,17 @@ from eventlet import patcher from eventlet.green import thread from eventlet.green import time -from eventlet.support import greenlets as greenlet +from eventlet.support import greenlets as greenlet, six -__patched__ = ['_start_new_thread', '_allocate_lock', '_get_ident', '_sleep', - 'local', 'stack_size', 'Lock', 'currentThread', +__patched__ = ['_start_new_thread', '_allocate_lock', + '_sleep', 'local', 'stack_size', 'Lock', 'currentThread', 'current_thread', '_after_fork', '_shutdown'] +if six.PY2: + __patched__ += ['_get_ident'] +else: + __patched__ += ['get_ident', '_set_sentinel'] + __orig_threading = patcher.original('threading') __threadlocal = __orig_threading.local() @@ -15,7 +20,7 @@ __threadlocal = __orig_threading.local() patcher.inject( 'threading', globals(), - ('thread', thread), + ('thread' if six.PY2 else '_thread', thread), ('time', time)) del patcher diff --git a/eventlet/websocket.py b/eventlet/websocket.py index 10c868d..1fb33bb 100644 --- a/eventlet/websocket.py +++ b/eventlet/websocket.py @@ -168,25 +168,24 @@ class WebSocketWSGI(object): if qs is not None: location += '?' + qs if self.protocol_version == 75: - handshake_reply = ("HTTP/1.1 101 Web Socket Protocol Handshake\r\n" - "Upgrade: WebSocket\r\n" - "Connection: Upgrade\r\n" - "WebSocket-Origin: %s\r\n" - "WebSocket-Location: %s\r\n\r\n" % ( - environ.get('HTTP_ORIGIN'), - location)) + handshake_reply = ( + b"HTTP/1.1 101 Web Socket Protocol Handshake\r\n" + b"Upgrade: WebSocket\r\n" + b"Connection: Upgrade\r\n" + b"WebSocket-Origin: " + environ.get('HTTP_ORIGIN') + b"\r\n" + b"WebSocket-Location: " + six.b(location) + b"\r\n\r\n" + ) elif self.protocol_version == 76: - handshake_reply = ("HTTP/1.1 101 WebSocket Protocol Handshake\r\n" - "Upgrade: WebSocket\r\n" - "Connection: Upgrade\r\n" - "Sec-WebSocket-Origin: %s\r\n" - "Sec-WebSocket-Protocol: %s\r\n" - "Sec-WebSocket-Location: %s\r\n" - "\r\n%s" % ( - environ.get('HTTP_ORIGIN'), - environ.get('HTTP_SEC_WEBSOCKET_PROTOCOL', 'default'), - location, - response)) + handshake_reply = ( + b"HTTP/1.1 101 WebSocket Protocol Handshake\r\n" + b"Upgrade: WebSocket\r\n" + b"Connection: Upgrade\r\n" + b"Sec-WebSocket-Origin: " + six.b(environ.get('HTTP_ORIGIN')) + b"\r\n" + b"Sec-WebSocket-Protocol: " + + six.b(environ.get('HTTP_SEC_WEBSOCKET_PROTOCOL', 'default')) + b"\r\n" + b"Sec-WebSocket-Location: " + six.b(location) + b"\r\n" + b"\r\n" + response + ) else: # pragma NO COVER raise ValueError("Unknown WebSocket protocol version.") sock.sendall(handshake_reply) @@ -244,7 +243,7 @@ class WebSocketWSGI(object): out += char elif char == " ": spaces += 1 - return int(out) / spaces + return int(out) // spaces class WebSocket(object): @@ -281,7 +280,7 @@ class WebSocket(object): self.environ = environ self.version = version self.websocket_closed = False - self._buf = "" + self._buf = b"" self._msgs = collections.deque() self._sendlock = semaphore.Semaphore() @@ -294,8 +293,8 @@ class WebSocket(object): if isinstance(message, six.text_type): message = message.encode('utf-8') elif not isinstance(message, six.binary_type): - message = b'%s' % (message,) - packed = b"\x00%s\xFF" % message + message = six.b(str(message)) + packed = b"\x00" + message + b"\xFF" return packed def _parse_messages(self): @@ -309,17 +308,17 @@ class WebSocket(object): end_idx = 0 buf = self._buf while buf: - frame_type = ord(buf[0]) + frame_type = six.indexbytes(buf, 0) if frame_type == 0: # Normal message. - end_idx = buf.find("\xFF") + end_idx = buf.find(b"\xFF") if end_idx == -1: # pragma NO COVER break msgs.append(buf[1:end_idx].decode('utf-8', 'replace')) buf = buf[end_idx + 1:] elif frame_type == 255: # Closing handshake. - assert ord(buf[1]) == 0, "Unexpected closing handshake: %r" % buf + assert six.indexbytes(buf, 1) == 0, "Unexpected closing handshake: %r" % buf self.websocket_closed = True break else: @@ -355,7 +354,7 @@ class WebSocket(object): return None # no parsed messages, must mean buf needs more data delta = self.socket.recv(8096) - if delta == '': + if delta == b'': return None self._buf += delta msgs = self._parse_messages() diff --git a/eventlet/wsgi.py b/eventlet/wsgi.py index a17de17..807b413 100644 --- a/eventlet/wsgi.py +++ b/eventlet/wsgi.py @@ -69,11 +69,13 @@ class Input(object): def __init__(self, rfile, content_length, + sock, wfile=None, wfile_line=None, chunked_input=False): self.rfile = rfile + self._sock = sock if content_length is not None: content_length = int(content_length) self.content_length = content_length @@ -193,7 +195,7 @@ class Input(object): return iter(self.read, b'') def get_socket(self): - return self.rfile._sock + return self._sock def set_hundred_continue_response_headers(self, headers, capitalize_response_headers=True): @@ -387,24 +389,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] @@ -448,6 +434,8 @@ 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 not isinstance(data, six.binary_type): + raise Exception('The result iterable has to return bytestrings') towrite.append(data) towrite_size += len(data) if towrite_size >= minimum_write_chunk_size: @@ -464,7 +452,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'), @@ -564,7 +552,7 @@ class HttpProtocol(BaseHTTPServer.BaseHTTPRequestHandler): wfile_line = None chunked = env.get('HTTP_TRANSFER_ENCODING', '').lower() == 'chunked' env['wsgi.input'] = env['eventlet.input'] = Input( - self.rfile, length, wfile=wfile, wfile_line=wfile_line, + self.rfile, length, self.connection, wfile=wfile, wfile_line=wfile_line, chunked_input=chunked) env['eventlet.posthooks'] = [] |
