Commit f1058ba9 authored by Jason Madden's avatar Jason Madden

socket: Stop connect_ex from calling a subclass connect method.

This matches the standard library more closely.

Fixes #1932 and fixes #1931.
parent 7484e95d
Fix an edge case connecting a non-blocking ``SSLSocket`` that could result
in an AttributeError. In a change to match the standard library,
calling ``sock.connect_ex()`` on a subclass of ``socket`` no longer
calls the subclass's ``connect`` method.
Initial fix by Priyankar Jain.
......@@ -581,6 +581,49 @@ class SocketMixin(object):
it will be used instead of ignored, if the platform supplies
:func:`socket.inet_pton`.
"""
# In the standard library, ``connect`` and ``connect_ex`` are implemented
# in C, and they both call a C function ``internal_connect`` to do the real
# work. This means that it is a visible behaviour difference to have our
# Python implementation of ``connect_ex`` simply call ``connect``:
# it could be overridden in a subclass or at runtime! Because of our exception handling,
# this can make a difference for known subclasses like SSLSocket.
self._internal_connect(address)
def connect_ex(self, address):
"""
Connect to *address*, returning a result code.
.. versionchanged:: NEXT
No longer uses an overridden ``connect`` method on
this object. Instead, like the standard library, this method always
uses a non-replacable internal connection function.
"""
try:
return self._internal_connect(address) or 0
except __socket__.timeout:
return EAGAIN
except __socket__.gaierror: # pylint:disable=try-except-raise
# gaierror/overflowerror/typerror is not silenced by connect_ex;
# gaierror extends error so catch it first
raise
except _SocketError as ex:
# Python 3: error is now OSError and it has various subclasses.
# Only those that apply to actually connecting are silenced by
# connect_ex.
# On Python 3, we want to check ex.errno; on Python 2
# there is no such attribute, we need to look at the first
# argument.
try:
err = ex.errno
except AttributeError:
err = ex.args[0]
if err:
return err
raise
def _internal_connect(self, address):
# Like the C function ``internal_connect``, not meant to be overridden,
# but exposed for testing.
if self.timeout == 0.0:
return self._sock.connect(address)
address = _resolve_addr(self._sock, address)
......@@ -611,30 +654,6 @@ class SocketMixin(object):
result = ECONNREFUSED
raise _SocketError(result, strerror(result))
def connect_ex(self, address):
try:
return self.connect(address) or 0
except __socket__.timeout:
return EAGAIN
except __socket__.gaierror: # pylint:disable=try-except-raise
# gaierror/overflowerror/typerror is not silenced by connect_ex;
# gaierror extends error so catch it first
raise
except _SocketError as ex:
# Python 3: error is now OSError and it has various subclasses.
# Only those that apply to actually connecting are silenced by
# connect_ex.
# On Python 3, we want to check ex.errno; on Python 2
# there is no such attribute, we need to look at the first
# argument.
try:
err = ex.errno
except AttributeError:
err = ex.args[0]
if err:
return err
raise
def recv(self, *args):
while 1:
try:
......
......@@ -434,6 +434,31 @@ class TestTCP(greentest.TestCase):
finally:
s.close()
@skipWithoutExternalNetwork("Tries to resolve hostname")
def test_connect_ex_not_call_connect(self):
# Issue 1931
def do_it(sock):
try:
with self.assertRaises(socket.gaierror):
sock.connect_ex(('foo.bar.fizzbuzz', support.find_unused_port()))
finally:
sock.close()
# An instance attribute doesn't matter because we can't set it
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
with self.assertRaises(AttributeError):
s.connect = None
s.close()
# A subclass
class S(socket.socket):
def connect(self, *args):
raise AssertionError('Should not be called')
s = S(socket.AF_INET, socket.SOCK_STREAM)
do_it(s)
def test_connect_ex_nonblocking_overflow(self):
# Issue 841
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
......
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