Commit 5475548e authored by Jason Madden's avatar Jason Madden

dnspython+pypy fixups

parent f8e8c3cc
The c-ares and DNSPython resolvers now raise exceptions much more
consistently with the standard resolver. Types and errnos are more
likely to match. (Depending on the system and configuration, it may
not match exactly, at least with DNSPython. There are still some rare
cases where the system resolver can raise ``herror`` bun DNSPython
will raise ``gaierror`` or vice versa. There doesn't seem to be a
deterministic way to account for this.)
consistently with the standard resolver. Types and errnos are
substantially more likely to match what the standard library produces.
Depending on the system and configuration, results may not match
exactly, at least with DNSPython. There are still some rare cases
where the system resolver can raise ``herror`` but DNSPython will
raise ``gaierror`` or vice versa. There doesn't seem to be a
deterministic way to account for this. On PyPy, ``getnameinfo`` can
produce results when CPython raises ``socket.error``, and gevent's
DNSPython resolver also raises ``socket.error``.
In addition, several other small discrepancies were addressed,
including handling of localhost and broadcast host names.
......@@ -19,13 +19,14 @@ from _socket import gethostbyaddr as native_gethostbyaddr
from _socket import gethostbyname as native_gethostbyname
from _socket import gethostbyname_ex as native_gethostbyname_ex
from _socket import getservbyname as native_getservbyname
from _socket import herror
from gevent._compat import string_types
from gevent._compat import text_type
from gevent._compat import hostname_types
from gevent._compat import integer_types
from gevent._compat import PY3
from gevent._compat import PYPY
from gevent._compat import MAC
from gevent.resolver._addresses import is_ipv6_addr
......@@ -245,14 +246,14 @@ class AbstractResolver(object):
address = sockaddr[0]
address = self._hostname_to_bytes(sockaddr[0])
if address in self._LOCAL_HOSTNAMES:
if address in self._LOCAL_AND_BROADCAST_HOSTNAMES:
return native_getnameinfo(sockaddr, flags)
port = sockaddr[1]
if not isinstance(port, integer_types):
raise TypeError('port must be an integer, not %s' % type(port))
if port >= 65536:
if not PYPY and port >= 65536:
# System resolvers do different things with an
# out-of-bound port; macOS CPython 3.8 raises ``gaierror: [Errno 8]
# nodename nor servname provided, or not known``, while
......@@ -260,8 +261,12 @@ class AbstractResolver(object):
# sockaddr resolved to multiple addresses``. TravisCI, at least ot
# one point, successfully resolved www.gevent.org to ``(readthedocs.org, '0')``.
# But c-ares 1.16 would raise ``gaierror(25, 'ARES_ESERVICE: unknown')``.
# Doing this appears to get the expected results.
# Doing this appears to get the expected results on CPython
port = 0
if PYPY and (port < 0 or port >= 65536):
# PyPy seems to always be strict about that and produce the same results
# on all platforms.
raise OverflowError("port must be 0-65535.")
if len(sockaddr) > 2:
# Must be IPv6: (host, port, [flowinfo, [scopeid]])
......
......@@ -351,6 +351,10 @@ class Resolver(AbstractResolver):
symbolic scope IDs in IPv6 addresses passed to ``getaddrinfo`` exhibits
some differences.
On PyPy, ``getnameinfo`` can produce results when CPython raises
``socket.error``, and gevent's DNSPython resolver also
raises ``socket.error``.
.. caution::
This resolver is experimental. It may be removed or modified in
......
......@@ -32,6 +32,7 @@ from gevent.testing.sysinfo import RESOLVER_NOT_SYSTEM
from gevent.testing.sysinfo import RESOLVER_DNSPYTHON
from gevent.testing.sysinfo import RESOLVER_ARES
from gevent.testing.sysinfo import PY2
from gevent.testing.sysinfo import PYPY
import gevent.testing.timing
......@@ -398,6 +399,12 @@ class TestCase(greentest.TestCase):
if isinstance(real_result, TypeError):
return
if PYPY and isinstance(real_result, socket.herror):
# PyPy doesn't do errno or multiple arguments in herror;
# it just puts a string like 'host lookup failed: <thehost>';
# it must be doing that manually.
return
self.assertEqual(real_result.args, gevent_result.args, msg)
if hasattr(real_result, 'errno'):
self.assertEqual(real_result.errno, gevent_result.errno)
......@@ -420,7 +427,6 @@ class TestCase(greentest.TestCase):
# If we're using a different resolver, allow the real resolver to generate an
# error that the gevent resolver actually gets an answer to.
if (
RESOLVER_NOT_SYSTEM
and isinstance(real_result, errors)
......@@ -428,6 +434,19 @@ class TestCase(greentest.TestCase):
):
return
# On PyPy, socket.getnameinfo() can produce results even when the hostname resolves to
# multiple addresses, like www.gevent.org does. DNSPython (and c-ares?) don't do that,
# they refuse to pick a name and raise ``socket.error``
if (
RESOLVER_NOT_SYSTEM
and PYPY
and func_name == 'getnameinfo'
and isinstance(gevent_result, socket.error)
and not isinstance(real_result, socket.error)
):
return
# From 2.7 on, assertEqual does a better job highlighting the results than we would
# because it calls assertSequenceEqual, which highlights the exact
# difference in the tuple
......@@ -559,7 +578,8 @@ class TestBroadcast(TestCase):
switch_expected = False
if RESOLVER_DNSPYTHON:
# ares and dnspython raises errors for broadcasthost/255.255.255.255
# dnspython raises errors for broadcasthost/255.255.255.255, but the system
# can resolve it.
@unittest.skip('ares raises errors for broadcasthost/255.255.255.255')
def test__broadcast__gethostbyaddr(self):
......
......@@ -11,7 +11,7 @@ from gevent.tests.test__socket_dns import TestCase, add
from gevent.testing.sysinfo import OSX
from gevent.testing.sysinfo import RESOLVER_NOT_SYSTEM
from gevent.testing.sysinfo import RESOLVER_DNSPYTHON
from gevent.testing.sysinfo import PYPY
# We can't control the DNS servers on CI (or in general...)
......@@ -73,12 +73,15 @@ class Test6(TestCase):
class Test6_google(Test6):
host = 'ipv6.google.com'
def _normalize_result_getnameinfo(self, result):
if greentest.RUNNING_ON_CI and RESOLVER_NOT_SYSTEM:
# Disabled, there are multiple possibilities
# and we can get different ones, rarely.
if greentest.RUNNING_ON_CI and RESOLVER_NOT_SYSTEM:
# Disabled, there are multiple possibilities
# and we can get different ones, rarely.
def _normalize_result_getnameinfo(self, result):
return ()
return result
if PYPY:
# PyPy tends to be especially problematic in that area.
_normalize_result_getaddrinfo = _normalize_result_getnameinfo
add(Test6, Test6.host)
add(Test6_google, Test6_google.host)
......
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