Skip to content

Commit

Permalink
For Python < 3.5, automatically retry EINTR
Browse files Browse the repository at this point in the history
  • Loading branch information
thomasst committed May 24, 2016
1 parent 5ef18fd commit 2b59b25
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 4 deletions.
57 changes: 57 additions & 0 deletions redis/_compat.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,63 @@
"""Internal module for Python 2 backwards compatibility."""
import sys

# For Python older than 3.5, retry EINTR.
if sys.version_info[0] < 3 or sys.version_info[0] == 3 and sys.version_info[1] < 5:
# Adapted from https://bugs.python.org/review/23863/patch/14532/54418
import socket
import time
import errno

# Wrapper for handling interruptable system calls.
def _retryable_call(s, func, *args, **kwargs):
# Some modules (SSL) use the _fileobject wrapper directly and
# implement a smaller portion of the socket interface, thus we
# need to let them continue to do so.
timeout, deadline = None, 0.0
attempted = False
try:
timeout = s.gettimeout()
except AttributeError:
pass

if timeout:
deadline = time.time() + timeout

try:
while True:
if attempted and timeout:
now = time.time()
if now >= deadline:
raise socket.error(errno.EWOULDBLOCK, "timed out")
else:
# Overwrite the timeout on the socket object
# to take into account elapsed time.
s.settimeout(deadline - now)
try:
attempted = True
return func(*args, **kwargs)
except socket.error as e:
if e.args[0] == errno.EINTR:
continue
raise
finally:
# Set the existing timeout back for future
# calls.
if timeout:
s.settimeout(timeout)

def recv(sock, *args, **kwargs):
return _retryable_call(sock, sock.recv, *args, **kwargs)

def recv_into(sock, *args, **kwargs):
return _retryable_call(sock, sock.recv_into, *args, **kwargs)

else: # Python 3.5 and above automatically retry EINTR
def recv(sock, *args, **kwargs):
return sock.recv(*args, **kwargs)

def recv_into(sock, *args, **kwargs):
return sock.recv_into(*args, **kwargs)

if sys.version_info[0] < 3:
from urllib import unquote
Expand Down
8 changes: 4 additions & 4 deletions redis/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from redis._compat import (b, xrange, imap, byte_to_chr, unicode, bytes, long,
BytesIO, nativestr, basestring, iteritems,
LifoQueue, Empty, Full, urlparse, parse_qs,
unquote)
recv, recv_into, unquote)
from redis.exceptions import (
RedisError,
ConnectionError,
Expand Down Expand Up @@ -123,7 +123,7 @@ def _read_from_socket(self, length=None):

try:
while True:
data = self._sock.recv(socket_read_size)
data = recv(self._sock, socket_read_size)
# an empty string indicates the server shutdown the socket
if isinstance(data, bytes) and len(data) == 0:
raise socket.error(SERVER_CLOSED_CONNECTION_ERROR)
Expand Down Expand Up @@ -341,11 +341,11 @@ def read_response(self):
while response is False:
try:
if HIREDIS_USE_BYTE_BUFFER:
bufflen = self._sock.recv_into(self._buffer)
bufflen = recv_into(self._sock, self._buffer)
if bufflen == 0:
raise socket.error(SERVER_CLOSED_CONNECTION_ERROR)
else:
buffer = self._sock.recv(socket_read_size)
buffer = recv(self._sock, socket_read_size)
# an empty string indicates the server shutdown the socket
if not isinstance(buffer, bytes) or len(buffer) == 0:
raise socket.error(SERVER_CLOSED_CONNECTION_ERROR)
Expand Down

0 comments on commit 2b59b25

Please sign in to comment.