Skip to content

Commit

Permalink
socket: Actually provide non-blocking DNS methods
Browse files Browse the repository at this point in the history
The green socket module seemed to have only blocking DNS resolution
methods even with dnspython installed which is inconsistent with the
documentation.

This patch has a few consequences:

* an import cycle is eliminated
* if an import cycle reappears here it'll be visible

Note: eliminating the import cycle revealed an issue related to monkey
patching and the way we perform greendns tests (the test failures were
already present on Python 3.5[1] as that version has some import cycle
handling changes). The failures look like this:

======================================================================
FAIL: test_query_ans_types (tests.greendns_test.TestHostsResolver)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/kuba/projects/eventlet/tests/greendns_test.py", line 97, in test_query_ans_types
    assert isinstance(ans, greendns.dns.resolver.Answer)
AssertionError

======================================================================
FAIL: test_query_unknown_no_raise (tests.greendns_test.TestHostsResolver)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/kuba/projects/eventlet/tests/greendns_test.py", line 129, in test_query_unknown_no_raise
    assert isinstance(ans, greendns.dns.resolver.Answer)
AssertionError

This issue will be addressed in a separate commit.

This patch is contributed by Smarkets Limited.

[1] eventlet#267
  • Loading branch information
Jakub Stasiak committed May 18, 2016
1 parent 7e1dc8a commit 861d684
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 5 deletions.
11 changes: 10 additions & 1 deletion eventlet/green/socket.py
Expand Up @@ -16,7 +16,16 @@
try:
from eventlet.support import greendns
except ImportError as ex:
pass
try:
import dns
except ImportError:
# greendns import failed because we don't have dnspython - all is well,
# that's why we have the conditional import
pass
else:
# If, however, dnspython is importable yet greendns can't be imported
# this suggests there's another issue (like an import cycle)
raise

if greendns:
gethostbyname = greendns.gethostbyname
Expand Down
12 changes: 8 additions & 4 deletions eventlet/support/greendns.py
Expand Up @@ -43,6 +43,10 @@


def import_patched(module_name):
# Import cycle note: it's crucial to use _socket_nodns here because
# regular evenlet.green.socket imports *this* module and if we imported
# it back we'd end with an import cycle (socket -> greendns -> socket).
# We break this import cycle by providing a restricted socket module.
return patcher.import_patched(module_name,
select=select,
time=time,
Expand All @@ -55,15 +59,15 @@ def import_patched(module_name):

for pkg in ('dns.entropy', 'dns.inet', 'dns.query'):
setattr(dns, pkg.split('.')[1], import_patched(pkg))
del import_patched

import dns.rdtypes
for pkg in ['dns.rdtypes.IN', 'dns.rdtypes.ANY']:
setattr(dns.rdtypes, pkg.split('.')[-1], patcher.import_patched(pkg))
setattr(dns.rdtypes, pkg.split('.')[-1], import_patched(pkg))
for pkg in ['dns.rdtypes.IN.A', 'dns.rdtypes.IN.AAAA']:
setattr(dns.rdtypes.IN, pkg.split('.')[-1], patcher.import_patched(pkg))
setattr(dns.rdtypes.IN, pkg.split('.')[-1], import_patched(pkg))
for pkg in ['dns.rdtypes.ANY.CNAME']:
setattr(dns.rdtypes.ANY, pkg.split('.')[-1], patcher.import_patched(pkg))
setattr(dns.rdtypes.ANY, pkg.split('.')[-1], import_patched(pkg))
del import_patched


socket = _socket_nodns
Expand Down
14 changes: 14 additions & 0 deletions tests/socket_test.py
@@ -1,5 +1,11 @@
import eventlet
from eventlet.green import socket
try:
from eventlet.support import greendns
has_greendns = True
except ImportError:
has_greendns = False
from tests import skip_if


def test_create_connection_error():
Expand Down Expand Up @@ -29,3 +35,11 @@ def server():
sock = eventlet.connect(tuple(addr))
s = sock.recv(1)
assert isinstance(s, bytes)


@skip_if(not has_greendns)
def test_dns_methods_are_green():
assert socket.gethostbyname is greendns.gethostbyname
assert socket.gethostbyname_ex is greendns.gethostbyname_ex
assert socket.getaddrinfo is greendns.getaddrinfo
assert socket.getnameinfo is greendns.getnameinfo

0 comments on commit 861d684

Please sign in to comment.