Commit 00821040 authored by Jason Madden's avatar Jason Madden Committed by GitHub

Merge pull request #1726 from gevent/issue1724

Move socket objects fully to __slots__.
parents 5a603ade 5cf57b16
Remove the ``__dict__`` attribute from `gevent.socket.socket` objects. The
standard library socket do not have a ``__dict__``.
Noticed by Carson Ip.
......@@ -105,7 +105,9 @@ gtype = type
from gevent._hub_primitives import wait_on_socket as _wait_on_socket
class socket(_socketcommon.SocketMixin):
_Base = _socketcommon.SocketMixin
class socket(_Base):
"""
gevent `socket.socket <https://docs.python.org/2/library/socket.html#socket-objects>`_
for Python 2.
......@@ -120,9 +122,12 @@ class socket(_socketcommon.SocketMixin):
# pylint:disable=too-many-public-methods
# TODO: Define __slots__.
__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)
......@@ -403,20 +408,6 @@ class socket(_socketcommon.SocketMixin):
else:
self.timeout = 0.0
def settimeout(self, howlong):
if howlong is not None:
try:
f = howlong.__float__
except AttributeError:
raise TypeError('a float is required')
howlong = f()
if howlong < 0.0:
raise ValueError('Timeout value out of range')
self.__dict__['timeout'] = howlong # avoid recursion with any property on self.timeout
def gettimeout(self):
return self.__dict__['timeout'] # avoid recursion with any property on self.timeout
def shutdown(self, how):
if how == 0: # SHUT_RD
self.hub.cancel_wait(self._read_event, cancel_wait_ex)
......@@ -444,7 +435,7 @@ class socket(_socketcommon.SocketMixin):
_s = "def %s(self, *args): return self._sock.%s(*args)\n\n"
_m = None
for _m in set(_socketmethods) - set(locals()):
for _m in set(_socketmethods) - set(locals()) - {'settimeout', 'gettimeout'}:
exec(_s % (_m, _m,))
del _m, _s
......
......@@ -113,11 +113,10 @@ class socket(_socketcommon.SocketMixin):
# of _wrefsocket. (gevent internal usage only)
_gevent_sock_class = _wrefsocket
_io_refs = 0
_closed = False
_read_event = None
_write_event = None
__slots__ = (
'_io_refs',
'_closed',
)
# Take the same approach as socket2: wrap a real socket object,
# don't subclass it. This lets code that needs the raw _sock (not tied to the hub)
......@@ -125,6 +124,8 @@ class socket(_socketcommon.SocketMixin):
if sys.version_info[:2] < (3, 7):
def __init__(self, family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None):
super().__init__()
self._closed = False
self._sock = self._gevent_sock_class(family, type, proto, fileno)
self.timeout = None
self.__init_common()
......@@ -132,6 +133,8 @@ class socket(_socketcommon.SocketMixin):
# In 3.7, socket changed to auto-detecting family, type, and proto
# when given a fileno.
def __init__(self, family=-1, type=-1, proto=-1, fileno=None):
super().__init__()
self._closed = False
if fileno is None:
if family == -1:
family = AF_INET
......@@ -144,6 +147,7 @@ class socket(_socketcommon.SocketMixin):
self.__init_common()
def __init_common(self):
self._io_refs = 0
_socket.socket.setblocking(self._sock, False)
fileno = _socket.socket.fileno(self._sock)
self.hub = get_hub()
......@@ -579,20 +583,6 @@ class socket(_socketcommon.SocketMixin):
else:
self.timeout = 0.0
def settimeout(self, howlong):
if howlong is not None:
try:
f = howlong.__float__
except AttributeError:
raise TypeError('a float is required')
howlong = f()
if howlong < 0.0:
raise ValueError('Timeout value out of range')
self.__dict__['timeout'] = howlong
def gettimeout(self):
return self.__dict__['timeout']
def shutdown(self, how):
if how == 0: # SHUT_RD
self.hub.cancel_wait(self._read_event, cancel_wait_ex)
......
......@@ -434,11 +434,27 @@ def _resolve_addr(sock, address):
class SocketMixin(object):
__slots__ = (
'hub',
'timeout',
'_read_event',
'_write_event',
'_sock',
'__weakref__',
)
def __init__(self):
# Writing:
# (self.a, self.b) = (None,) * 2
# generates the fastest bytecode. But At least on PyPy,
# where the SSLSocket subclass has a timeout property,
# it results in the settimeout() method getting the tuple
# as the value, not the unpacked None.
self._read_event = None
self._write_event = None
self._sock = None
self.hub = None
self.timeout = None
def _drop_events_and_close(self, closefd=True, _cancel_wait_ex=cancel_wait_ex):
hub = self.hub
read_event = self._read_event
......@@ -455,3 +471,19 @@ class SocketMixin(object):
def _drop_ref_on_close(self, sock):
raise NotImplementedError
def settimeout(self, howlong):
if howlong is not None:
try:
f = howlong.__float__
except AttributeError:
raise TypeError('a float is required', howlong, type(howlong))
howlong = f()
if howlong < 0.0:
raise ValueError('Timeout value out of range')
# avoid recursion with any property on self.timeout
SocketMixin.timeout.__set__(self, howlong)
def gettimeout(self):
# avoid recursion with any property on self.timeout
return SocketMixin.timeout.__get__(self, type(self))
# This line can be commented out so that most tests run with the
# system socket for comparison.
from __future__ import print_function
from __future__ import absolute_import
from gevent import monkey
# This line can be commented out so that most tests run with the
# system socket for comparison.
monkey.patch_all()
import sys
......@@ -599,5 +599,25 @@ class TestSocket(greentest.TestCase):
with self.assertRaises(socket.error):
s.shutdown(socket.SHUT_RDWR)
def test_can_be_weak_ref(self):
# stdlib socket can be weak reffed.
import weakref
s = socket.socket()
try:
w = weakref.ref(s)
self.assertIsNotNone(w)
finally:
s.close()
def test_has_no_dict(self):
# stdlib socket has no dict
s = socket.socket()
try:
with self.assertRaises(AttributeError):
getattr(s, '__dict__')
finally:
s.close()
if __name__ == '__main__':
greentest.main()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment