189 lines
5.6 KiB
Python
189 lines
5.6 KiB
Python
|
"""Internal module for Python 2 backwards compatibility."""
|
||
|
# flake8: noqa
|
||
|
import errno
|
||
|
import socket
|
||
|
import sys
|
||
|
|
||
|
|
||
|
def sendall(sock, *args, **kwargs):
|
||
|
return sock.sendall(*args, **kwargs)
|
||
|
|
||
|
|
||
|
def shutdown(sock, *args, **kwargs):
|
||
|
return sock.shutdown(*args, **kwargs)
|
||
|
|
||
|
|
||
|
def ssl_wrap_socket(context, sock, *args, **kwargs):
|
||
|
return context.wrap_socket(sock, *args, **kwargs)
|
||
|
|
||
|
|
||
|
# 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 time
|
||
|
|
||
|
# 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:
|
||
|
# In Python 3, the ssl module raises socket.timeout whereas it raises
|
||
|
# SSLError in Python 2. For compatibility between versions, ensure
|
||
|
# socket.timeout is raised for both.
|
||
|
import functools
|
||
|
|
||
|
try:
|
||
|
from ssl import SSLError as _SSLError
|
||
|
except ImportError:
|
||
|
class _SSLError(Exception):
|
||
|
"""A replacement in case ssl.SSLError is not available."""
|
||
|
pass
|
||
|
|
||
|
_EXPECTED_SSL_TIMEOUT_MESSAGES = (
|
||
|
"The handshake operation timed out",
|
||
|
"The read operation timed out",
|
||
|
"The write operation timed out",
|
||
|
)
|
||
|
|
||
|
def _handle_ssl_timeout(func):
|
||
|
@functools.wraps(func)
|
||
|
def wrapper(*args, **kwargs):
|
||
|
try:
|
||
|
return func(*args, **kwargs)
|
||
|
except _SSLError as e:
|
||
|
message = len(e.args) == 1 and unicode(e.args[0]) or ''
|
||
|
if any(x in message for x in _EXPECTED_SSL_TIMEOUT_MESSAGES):
|
||
|
# Raise socket.timeout for compatibility with Python 3.
|
||
|
raise socket.timeout(*e.args)
|
||
|
raise
|
||
|
return wrapper
|
||
|
|
||
|
recv = _handle_ssl_timeout(recv)
|
||
|
recv_into = _handle_ssl_timeout(recv_into)
|
||
|
sendall = _handle_ssl_timeout(sendall)
|
||
|
shutdown = _handle_ssl_timeout(shutdown)
|
||
|
ssl_wrap_socket = _handle_ssl_timeout(ssl_wrap_socket)
|
||
|
|
||
|
if sys.version_info[0] < 3:
|
||
|
from urllib import unquote
|
||
|
from urlparse import parse_qs, urlparse
|
||
|
from itertools import imap, izip
|
||
|
from string import letters as ascii_letters
|
||
|
from Queue import Queue
|
||
|
|
||
|
# special unicode handling for python2 to avoid UnicodeDecodeError
|
||
|
def safe_unicode(obj, *args):
|
||
|
""" return the unicode representation of obj """
|
||
|
try:
|
||
|
return unicode(obj, *args)
|
||
|
except UnicodeDecodeError:
|
||
|
# obj is byte string
|
||
|
ascii_text = str(obj).encode('string_escape')
|
||
|
return unicode(ascii_text)
|
||
|
|
||
|
def iteritems(x):
|
||
|
return x.iteritems()
|
||
|
|
||
|
def iterkeys(x):
|
||
|
return x.iterkeys()
|
||
|
|
||
|
def itervalues(x):
|
||
|
return x.itervalues()
|
||
|
|
||
|
def nativestr(x):
|
||
|
return x if isinstance(x, str) else x.encode('utf-8', 'replace')
|
||
|
|
||
|
def next(x):
|
||
|
return x.next()
|
||
|
|
||
|
unichr = unichr
|
||
|
xrange = xrange
|
||
|
basestring = basestring
|
||
|
unicode = unicode
|
||
|
long = long
|
||
|
BlockingIOError = socket.error
|
||
|
else:
|
||
|
from urllib.parse import parse_qs, unquote, urlparse
|
||
|
from string import ascii_letters
|
||
|
from queue import Queue
|
||
|
|
||
|
def iteritems(x):
|
||
|
return iter(x.items())
|
||
|
|
||
|
def iterkeys(x):
|
||
|
return iter(x.keys())
|
||
|
|
||
|
def itervalues(x):
|
||
|
return iter(x.values())
|
||
|
|
||
|
def nativestr(x):
|
||
|
return x if isinstance(x, str) else x.decode('utf-8', 'replace')
|
||
|
|
||
|
def safe_unicode(value):
|
||
|
if isinstance(value, bytes):
|
||
|
value = value.decode('utf-8', 'replace')
|
||
|
return str(value)
|
||
|
|
||
|
next = next
|
||
|
unichr = chr
|
||
|
imap = map
|
||
|
izip = zip
|
||
|
xrange = range
|
||
|
basestring = str
|
||
|
unicode = str
|
||
|
long = int
|
||
|
BlockingIOError = BlockingIOError
|
||
|
|
||
|
try: # Python 3
|
||
|
from queue import LifoQueue, Empty, Full
|
||
|
except ImportError: # Python 2
|
||
|
from Queue import LifoQueue, Empty, Full
|