Source code for gevent._socket2

# Copyright (c) 2009-2014 Denis Bilenko and gevent contributors. See LICENSE for details.
"""
Python 2 socket module.
"""
from __future__ import absolute_import
from __future__ import print_function

# Our import magic sadly makes this warning useless
# pylint: disable=undefined-variable
import sys

from gevent import _socketcommon
from gevent._util import copy_globals
from gevent._compat import PYPY

copy_globals(_socketcommon, globals(),
             names_to_ignore=_socketcommon.__py3_imports__ + _socketcommon.__extensions__,
             dunder_names_to_keep=())

__socket__ = _socketcommon.__socket__
__implements__ = _socketcommon._implements
__extensions__ = _socketcommon.__extensions__
__imports__ = [i for i in _socketcommon.__imports__ if i not in _socketcommon.__py3_imports__]
__dns__ = _socketcommon.__dns__
try:
    _fileobject = __socket__._fileobject
except AttributeError:
    # Allow this module to be imported under Python 3
    # for building the docs
    _fileobject = object
else:
    # Python 2 doesn't natively support with statements on _fileobject;
    # but it substantially eases our test cases if we can do the same with on both Py3
    # and Py2. (For this same reason we make the socket itself a context manager.)
    # Implementation copied from Python 3
    assert not hasattr(_fileobject, '__enter__')
    # we could either patch in place:
    #_fileobject.__enter__ = lambda self: self
    #_fileobject.__exit__ = lambda self, *args: self.close() if not self.closed else None
    # or we could subclass. subclassing has the benefit of not
    # changing the behaviour of the stdlib if we're just imported; OTOH,
    # under Python 2.6/2.7, test_urllib2net.py asserts that the class IS
    # socket._fileobject (sigh), so we have to work around that.

    # We also make it call our custom socket closing method that disposes
    # of IO watchers but not the actual socket itself.

    # Python 2 relies on reference counting to close sockets, so this is all
    # very ugly and fragile.

    class _fileobject(_fileobject): # pylint:disable=function-redefined
        __slots__ = (
            '__weakref__',
        )

        def __enter__(self):
            return self

        def __exit__(self, *args):
            if not self.closed:
                self.close()

        def close(self):
            if self._sock is not None:
                self._sock._drop_events_and_close(closefd=False)
            super(_fileobject, self).close()


class _closedsocket(object):
    __slots__ = ()

    def _dummy(*args, **kwargs): # pylint:disable=no-method-argument,unused-argument
        raise error(EBADF, 'Bad file descriptor')
    # All _delegate_methods must also be initialized here.
    send = recv = recv_into = sendto = recvfrom = recvfrom_into = _dummy

    def __nonzero__(self):
        return False

    __bool__ = __nonzero__

    if PYPY:

        def _drop(self):
            pass

        def _reuse(self):
            pass

    __getattr__ = _dummy


gtype = type

_Base = _socketcommon.SocketMixin

[docs]class socket(_Base): """ gevent `socket.socket <https://docs.python.org/2/library/socket.html#socket-objects>`_ for Python 2. This object should have the same API as the standard library socket linked to above. Not all methods are specifically documented here; when they are they may point out a difference to be aware of or may document a method the standard library does not. .. versionchanged:: 1.5.0 This object is a context manager, returning itself, like in Python 3. """ # pylint:disable=too-many-public-methods __slots__ = ( ) def __init__(self, family=AF_INET, type=SOCK_STREAM, proto=0, _sock=None): _Base.__init__(self) timeout = _socket.getdefaulttimeout() if _sock is None: self._sock = _realsocket(family, type, proto) else: if hasattr(_sock, '_sock'): timeout = getattr(_sock, 'timeout', timeout) while hasattr(_sock, '_sock'): # passed a gevent socket or a native # socket._socketobject. Unwrap this all the way to the # native _socket.socket. _sock = _sock._sock self._sock = _sock if PYPY: self._sock._reuse() self.timeout = timeout self._sock.setblocking(0) fileno = self._sock.fileno() self.hub = get_hub() io = self.hub.loop.io self._read_event = io(fileno, 1) self._write_event = io(fileno, 2) def __enter__(self): return self def __exit__(self, t, v, tb): self.close() def __repr__(self): return '<%s at %s %s>' % (type(self).__name__, hex(id(self)), self._formatinfo()) def __str__(self): return '<%s %s>' % (type(self).__name__, self._formatinfo()) def _formatinfo(self): # pylint:disable=broad-except try: fileno = self.fileno() except Exception as ex: fileno = str(ex) try: sockname = self.getsockname() sockname = '%s:%s' % sockname except Exception: sockname = None try: peername = self.getpeername() peername = '%s:%s' % peername except Exception: peername = None result = 'fileno=%s' % fileno if sockname is not None: result += ' sock=' + str(sockname) if peername is not None: result += ' peer=' + str(peername) if getattr(self, 'timeout', None) is not None: result += ' timeout=' + str(self.timeout) return result def accept(self): while 1: try: client_socket, address = self._sock.accept() break except error as ex: if ex.args[0] != EWOULDBLOCK or self.timeout == 0.0: raise sys.exc_clear() self._wait(self._read_event) sockobj = socket(_sock=client_socket) if PYPY: client_socket._drop() return sockobj, address def _drop_ref_on_close(self, sock): # See the same method in _socket3.py. We just can't be as deterministic # as we can on Python 3. scheduled_new = self.hub.loop.closing_fd(sock.fileno()) if PYPY: meth = sock._drop else: meth = sock.fileno # Still keep it alive if we need to if scheduled_new: self.hub.loop.run_callback(meth) else: meth() def close(self, _closedsocket=_closedsocket): if not self._sock: return # This function should not reference any globals. See Python issue #808164. # First, break any reference to the loop.io objects. Our # fileno, which they were tied to, is about to be free to be # reused, so these objects are no longer functional. self._drop_events_and_close() # Next, change self._sock. On CPython, this drops a # reference, and if it was the last reference, __del__ will # close it. (We cannot close it, makefile() relies on # reference counting like this, and it may be shared among # multiple wrapper objects). Methods *must not* cache # `self._sock` separately from # self._write_event/self._read_event, or they will be out of # sync and we may get inappropriate errors. (See # test__hub:TestCloseSocketWhilePolling for an example). self._sock = _closedsocket() @property def closed(self): return isinstance(self._sock, _closedsocket)
[docs] def dup(self): """dup() -> socket object Return a new socket object connected to the same system resource. Note, that the new socket does not inherit the timeout.""" return socket(_sock=self._sock)
def makefile(self, mode='r', bufsize=-1): # Two things to look out for: # 1) Closing the original socket object should not close the # fileobject (hence creating a new socket instance); # An alternate approach is what _socket3.py does, which is to # keep count of the times makefile objects have been opened (Py3's # SocketIO helps with that). But the newly created socket, which # has its own read/write watchers, does need those to be closed # when the fileobject is; our custom subclass does that. Note that # we can't pass the 'close=True' argument, as that causes reference counts # to get screwed up, and Python2 sockets rely on those. # 2) The resulting fileobject must keep the timeout in order # to be compatible with the stdlib's socket.makefile. # Pass self as _sock to preserve timeout. fobj = _fileobject(type(self)(_sock=self), mode, bufsize) if PYPY: self._sock._drop() return fobj
[docs] def sendall(self, data, flags=0): if isinstance(data, unicode): data = data.encode() return _Base.sendall(self, data, flags)
if PYPY: def _reuse(self): self._sock._reuse() def _drop(self): self._sock._drop()
SocketType = socket if hasattr(_socket, 'socketpair'): # The native, low-level socketpair returns # low-level objects def socketpair(family=getattr(_socket, 'AF_UNIX', _socket.AF_INET), type=_socket.SOCK_STREAM, proto=0): one, two = _socket.socketpair(family, type, proto) result = socket(_sock=one), socket(_sock=two) if PYPY: one._drop() two._drop() return result elif hasattr(__socket__, 'socketpair'): # The high-level backport uses high-level socket APIs. It works # cooperatively automatically if we're monkey-patched, # else we must do it ourself. _orig_socketpair = __socket__.socketpair def socketpair(family=_socket.AF_INET, type=_socket.SOCK_STREAM, proto=0): one, two = _orig_socketpair(family, type, proto) if not isinstance(one, socket): one = socket(_sock=one) two = socket(_sock=two) if PYPY: one._drop() two._drop() return one, two elif 'socketpair' in __implements__: __implements__.remove('socketpair') if hasattr(_socket, 'fromfd'): def fromfd(fd, family, type, proto=0): s = _socket.fromfd(fd, family, type, proto) result = socket(_sock=s) if PYPY: s._drop() return result elif 'fromfd' in __implements__: __implements__.remove('fromfd') if hasattr(__socket__, 'ssl'): def ssl(sock, keyfile=None, certfile=None): # deprecated in 2.7.9 but still present; # sometimes backported by distros. See ssl.py # Note that we import gevent.ssl, not _ssl2, to get the correct # version. from gevent import ssl as _sslmod # wrap_socket is 2.7.9/backport, sslwrap_simple is older. They take # the same arguments. wrap = getattr(_sslmod, 'wrap_socket', None) or getattr(_sslmod, 'sslwrap_simple') return wrap(sock, keyfile, certfile) __implements__.append('ssl') if hasattr(__socket__, 'sethostname'): # This was added in 3.3, but PyPy 2.7-7.3.2 # leaked it back into Python 2. sethostname = __socket__.sethostname __imports__.append('sethostname') __all__ = __implements__ + __extensions__ + __imports__