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

Merge pull request #1093 from gevent/dnspython-nonmonkey

Let dnspython resolver be used without monkey-patching
parents 381b9eec 8778aa2f
......@@ -42,6 +42,10 @@
``AttributeError``, now it once again raises the correct
``socket.error``. Reported in :issue:`1089` by André Cimander.
- Add the module :mod:`gevent.time` that can be imported instead of
:mod:`time`, much like :mod:`gevent.socket` can be imported instead
of :mod:`socket`.
1.3a1 (2018-01-27)
==================
......
......@@ -67,7 +67,7 @@ alltest: basictest
cd src/greentest && GEVENT_RESOLVER=ares GEVENTARES_SERVERS=8.8.8.8 ${PYTHON} testrunner.py --config known_failures.py --ignore tests_that_dont_use_resolver.txt --quiet
${PYTHON} scripts/travis.py fold_end ares
${PYTHON} scripts/travis.py fold_start dnspython "Running dnspython tests"
cd src/greentest && GEVENT_RESOLVER=dnspython ${PYTHON} testrunner.py --config known_failures.py --ignore tests_that_dont_use_resolver.txt,tests_that_dont_monkeypatch.txt --quiet
cd src/greentest && GEVENT_RESOLVER=dnspython ${PYTHON} testrunner.py --config known_failures.py --ignore tests_that_dont_use_resolver.txt --quiet
${PYTHON} scripts/travis.py fold_end dnspython
# In the past, we included all test files that had a reference to 'subprocess'' somewhere in their
# text. The monkey-patched stdlib tests were specifically included here.
......
......@@ -40,6 +40,7 @@ extlinks = {'issue': ('https://github.com/gevent/gevent/issues/%s',
'pull request #')}
autodoc_default_flags = ['members', 'show-inheritance']
autodoc_member_order = 'bysource'
autoclass_content = 'both'
# Add any paths that contain templates here, relative to this directory.
......@@ -226,25 +227,3 @@ del gevent.Greenlet.throw
for item in gevent.socket.__all__[:]:
if getattr(gevent.socket, item) is getattr(socket, item, None):
gevent.socket.__all__.remove(item)
# order the methods in the class documentation the same way they are ordered in the source code
from sphinx.ext import autodoc
from sphinx.ext.autodoc import ClassDocumenter
class MyClassDocumenter(ClassDocumenter):
def get_object_members(self, want_all):
members_check_module, members = super(MyClassDocumenter, self).get_object_members(want_all)
def key((name, obj)):
try:
return obj.im_func.func_code.co_firstlineno
except AttributeError:
return 0
members.sort(key=key)
return members_check_module, members
autodoc.ClassDocumenter = MyClassDocumenter
......@@ -188,5 +188,6 @@ Timeouts
.. autoclass:: Timeout
:members:
:undoc-members:
:special-members: __enter__, __exit__
.. autofunction:: with_timeout
......@@ -21,5 +21,6 @@ API reference
gevent.thread
gevent.threading
gevent.threadpool
gevent.time
gevent.util
lowlevel
# Copyright 2018 gevent. See LICENSE for details.
# Portions of the following are inspired by code from eventlet. I
# believe they are distinct enough that no eventlet copyright would
# apply (they are not a copy or substantial portion of the eventlot
# code).
# Added in gevent 1.3a2. Not public in that release.
from __future__ import absolute_import, print_function
import imp
import importlib
import sys
from gevent._compat import PY3
from gevent._compat import iteritems
from gevent.builtins import __import__ as _import
MAPPING = {
'gevent.local': '_threading_local',
'gevent.socket': 'socket',
'gevent.select': 'select',
'gevent.ssl': 'ssl',
'gevent.thread': '_thread' if PY3 else 'thread',
'gevent.subprocess': 'subprocess',
'gevent.os': 'os',
'gevent.threading': 'threading',
'gevent.builtins': 'builtins' if PY3 else '__builtin__',
'gevent.signal': 'signal',
'gevent.time': 'time',
'gevent.queue': 'queue' if PY3 else 'Queue',
}
_PATCH_PREFIX = '__g_patched_module_'
class _SysModulesPatcher(object):
def __init__(self, importing):
self._saved = {}
self.importing = importing
self.green_modules = {
stdlib_name: importlib.import_module(gevent_name)
for gevent_name, stdlib_name
in iteritems(MAPPING)
}
self.orig_imported = frozenset(sys.modules)
def _save(self):
for modname in self.green_modules:
self._saved[modname] = sys.modules.get(modname, None)
self._saved[self.importing] = sys.modules.get(self.importing, None)
# Anything we've already patched regains its original name during this
# process
for mod_name, mod in iteritems(sys.modules):
if mod_name.startswith(_PATCH_PREFIX):
orig_mod_name = mod_name[len(_PATCH_PREFIX):]
self._saved[mod_name] = sys.modules.get(orig_mod_name, None)
self.green_modules[orig_mod_name] = mod
def _replace(self):
# Cover the target modules so that when you import the module it
# sees only the patched versions
for name, mod in iteritems(self.green_modules):
sys.modules[name] = mod
def _restore(self):
for modname, mod in iteritems(self._saved):
if mod is not None:
sys.modules[modname] = mod
else:
try:
del sys.modules[modname]
except KeyError:
pass
# Anything from the same package tree we imported this time
# needs to be saved so we can restore it later, and so it doesn't
# leak into the namespace.
pkg_prefix = self.importing.split('.', 1)[0]
for modname, mod in list(iteritems(sys.modules)):
if (modname not in self.orig_imported
and modname != self.importing
and not modname.startswith(_PATCH_PREFIX)
and modname.startswith(pkg_prefix)):
sys.modules[_PATCH_PREFIX + modname] = mod
del sys.modules[modname]
def __exit__(self, t, v, tb):
try:
self._restore()
finally:
imp.release_lock()
def __enter__(self):
imp.acquire_lock()
self._save()
self._replace()
def import_patched(module_name):
"""
Import *module_name* with gevent monkey-patches active,
and return the greened module.
Any sub-modules that were imported by the package are also
saved.
"""
patched_name = _PATCH_PREFIX + module_name
if patched_name in sys.modules:
return sys.modules[patched_name]
# Save the current module state, and restore on exit,
# capturing desirable changes in the modules package.
with _SysModulesPatcher(module_name):
sys.modules.pop(module_name, None)
module = _import(module_name, {}, {}, module_name.split('.')[:-1])
sys.modules[patched_name] = module
return module
......@@ -2,6 +2,8 @@
"""
Python 2 socket module.
"""
from __future__ import absolute_import
# Our import magic sadly makes this warning useless
# pylint: disable=undefined-variable
......
......@@ -29,21 +29,22 @@ def copy_globals(source,
dunder_names_to_keep=('__implements__', '__all__', '__imports__'),
cleanup_globs=True):
"""
Copy attributes defined in `source.__dict__` to the dictionary in globs
(which should be the caller's globals()).
Copy attributes defined in ``source.__dict__`` to the dictionary
in globs (which should be the caller's :func:`globals`).
Names that start with `__` are ignored (unless they are in
Names that start with ``__`` are ignored (unless they are in
*dunder_names_to_keep*). Anything found in *names_to_ignore* is
also ignored.
If *only_names* is given, only those attributes will be considered.
In this case, *ignore_missing_names* says whether or not to raise an AttributeError
if one of those names can't be found.
If *only_names* is given, only those attributes will be
considered. In this case, *ignore_missing_names* says whether or
not to raise an :exc:`AttributeError` if one of those names can't
be found.
If cleanup_globs has a true value, then common things imported but not used
at runtime are removed, including this function.
If *cleanup_globs* has a true value, then common things imported but
not used at runtime are removed, including this function.
Returns a list of the names copied
Returns a list of the names copied; this should be assigned to ``__imports__``.
"""
if only_names:
if ignore_missing_names:
......
......@@ -14,15 +14,15 @@ from gevent.lock import RLock
# So we test for the old, deprecated version first
try: # Py2
import __builtin__ as builtins
import __builtin__ as __gbuiltins__
_allowed_module_name_types = (basestring,) # pylint:disable=undefined-variable
__target__ = '__builtin__'
except ImportError:
import builtins # pylint: disable=import-error
import builtins as __gbuiltins__ # pylint: disable=import-error
_allowed_module_name_types = (str,)
__target__ = 'builtins'
_import = builtins.__import__
_import = __gbuiltins__.__import__
# We need to protect imports both across threads and across greenlets.
# And the order matters. Note that under 3.4, the global import lock
......@@ -120,6 +120,13 @@ def _lock_imports():
if sys.version_info[:2] >= (3, 3):
__implements__ = []
__import__ = _import
else:
__implements__ = ['__import__']
__all__ = __implements__
from gevent._util import copy_globals
__imports__ = copy_globals(__gbuiltins__, globals(),
names_to_ignore=__implements__)
......@@ -250,9 +250,7 @@ def patch_os():
def patch_time():
"""Replace :func:`time.sleep` with :func:`gevent.sleep`."""
from gevent.hub import sleep
import time
patch_item(time, 'sleep', sleep)
patch_module('time')
def _patch_existing_locks(threading):
......
# Copyright (c) 2005-2009, eventlet contributors
# Copyright (c) 2009-2015, gevent contributors
# Copyright (c) 2009-2018, gevent contributors
"""
A pure-Python, gevent-friendly WSGI server.
......@@ -9,6 +9,8 @@ created for each request. The server can be customized to use
different subclasses of :class:`WSGIHandler`.
"""
from __future__ import absolute_import
# FIXME: Can we refactor to make smallor?
# pylint:disable=too-many-lines
......
......@@ -42,7 +42,10 @@ from gevent.hub import get_hub, Waiter, getcurrent
from gevent.hub import InvalidSwitchError
__all__ = ['Empty', 'Full', 'Queue', 'PriorityQueue', 'LifoQueue', 'JoinableQueue', 'Channel']
__implements__ = ['Queue', 'PriorityQueue', 'LifoQueue']
__extensions__ = ['JoinableQueue', 'Channel']
__imports__ = ['Empty', 'Full']
__all__ = __implements__ + __extensions__ + __imports__
def _safe_remove(deq, item):
......
# Copyright (c) 2018 gevent contributors. See LICENSE for details.
import _socket
from _socket import AI_NUMERICHOST
from _socket import error
......@@ -9,14 +10,43 @@ import socket
from . import AbstractResolver
from dns import resolver
import dns
from gevent._patcher import import_patched
__all__ = [
'Resolver',
]
# Import the DNS packages to use the gevent modules,
# even if the system is not monkey-patched.
def _patch_dns():
top = import_patched('dns')
for pkg in ('dns',
'dns.rdtypes',
'dns.rdtypes.IN',
'dns.rdtypes.ANY'):
mod = import_patched(pkg)
for name in mod.__all__:
setattr(mod, name, import_patched(pkg + '.' + name))
return top
dns = _patch_dns()
def _dns_import_patched(name):
assert name.startswith('dns')
import_patched(name)
return dns
# This module tries to dynamically import classes
# using __import__, and it's important that they match
# the ones we just created, otherwise exceptions won't be caught
# as expected. It uses a one-arg __import__ statement and then
# tries to walk down the sub-modules using getattr, so we can't
# directly use import_patched as-is.
dns.rdata.__import__ = _dns_import_patched
resolver = dns.resolver
# This is a copy of resolver._getaddrinfo with the crucial change that it
# doesn't have a bare except:, because that breaks Timeout and KeyboardInterrupt
# See https://github.com/rthalley/dnspython/pull/300
......@@ -123,6 +153,8 @@ def _getaddrinfo(host=None, service=None, family=socket.AF_UNSPEC, socktype=0,
return tuples
resolver._getaddrinfo = _getaddrinfo
class Resolver(AbstractResolver):
"""
An *experimental* resolver that uses `dnspython`_.
......@@ -136,17 +168,15 @@ class Resolver(AbstractResolver):
resolver can resolve Unicode host names that the system resolver
cannot.
This uses thread locks and sockets, so it only functions if the
system has been monkey-patched. Otherwise it will raise a
``ValueError``.
.. note::
This uses dnspython's default resolver object. This object has
several useful attributes that can be used to adjust the behaviour
of the DNS system; in particular, the ``cache`` attribute could be
set to an instance of :class:`dns.resolver.Cache` or
:class:`dns.resolver.LRUCache` (by default a ``LRUCache`` is
used), and ``nameservers`` controls which nameservers to talk to,
and ``lifetime`` configures a timeout for each individual query.
This **does not** use dnspython's default resolver object, or share any
classes with ``import dns``. A separate copy of the objects is imported to
be able to function in a non monkey-patched process. The documentation for the resolver
object still applies.
The resolver that we use is available as the :attr:`resolver` attribute
of this object (typically ``gevent.get_hub().resolver.resolver``).
.. caution::
......@@ -169,15 +199,25 @@ class Resolver(AbstractResolver):
"""
def __init__(self, hub=None): # pylint: disable=unused-argument
from gevent import monkey
if not all(monkey.is_module_patched(m) for m in ['threading', 'socket', 'select']):
raise ValueError("Can only be used when monkey-patched")
if resolver._resolver is None:
resolver._resolver = resolver.get_default_resolver()
# Add a default cache
resolver._resolver.cache = resolver.LRUCache()
if resolver._getaddrinfo is not _getaddrinfo:
resolver._getaddrinfo = _getaddrinfo
@property
def resolver(self):
"""
The dnspython resolver object we use.
This object has several useful attributes that can be used to
adjust the behaviour of the DNS system:
* ``cache`` is a :class:`dns.resolver.LRUCache`. Its maximum size
can be configured by calling :meth:`resolver.cache.set_max_size`
* ``nameservers`` controls which nameservers to talk to
* ``lifetime`` configures a timeout for each individual query.
"""
return resolver._resolver
def close(self):
pass
......
......@@ -33,7 +33,7 @@ _signal_getsignal = _signal.getsignal
def getsignal(signalnum):
"""
Exactly the same as :func:`signal.signal` except where
Exactly the same as :func:`signal.getsignal` except where
:const:`signal.SIGCHLD` is concerned.
For :const:`signal.SIGCHLD`, this cooperates with :func:`signal`
......
......@@ -360,12 +360,17 @@ if 'TimeoutExpired' not in globals():
.. versionadded:: 1.2a1
"""
def __init__(self, cmd, timeout, output=None):
_Timeout.__init__(self, timeout, _use_timer=False)
_Timeout.__init__(self, None)
self.cmd = cmd
self.timeout = timeout
self.seconds = timeout
self.output = output
@property
def timeout(self):
return self.seconds
def __str__(self):
return ("Command '%s' timed out after %s seconds" %
(self.cmd, self.timeout))
......
# Copyright (c) 2018 gevent. See LICENSE for details.
"""
The standard library :mod:`time` module, but :func:`sleep` is
gevent-aware.
.. versionadded:: 1.3a2
"""
from __future__ import absolute_import
__implements__ = [
'sleep',
]
__all__ = __implements__
import time as __time__
from gevent._util import copy_globals
__imports__ = copy_globals(__time__, globals(),
names_to_ignore=__implements__)
from gevent.hub import sleep
sleep = sleep # pylint
......@@ -17,8 +17,10 @@ from __future__ import absolute_import, print_function, division
from gevent._compat import string_types
from gevent.hub import getcurrent, _NONE, get_hub
__all__ = ['Timeout',
'with_timeout']
__all__ = [
'Timeout',
'with_timeout',
]
class _FakeTimer(object):
......@@ -26,8 +28,22 @@ class _FakeTimer(object):
# without allocating any native resources. This is useful for timeouts
# that will never expire.
# Also partially mimics the API of Timeout itself for use in _start_new_or_dummy
pending = False
active = False
# This object is used as a singleton, so it should be
# immutable.
__slots__ = ()
@property
def pending(self):
return False
active = pending
@property
def seconds(self):
return None
timer = exception = seconds
def start(self, *args, **kwargs):
# pylint:disable=unused-argument
......@@ -51,23 +67,30 @@ _FakeTimer = _FakeTimer()
class Timeout(BaseException):
"""
Raise *exception* in the current greenlet after given time period::
Timeout(seconds=None, exception=None, ref=True, priority=-1)
Raise *exception* in the current greenlet after *seconds*
have elapsed::
timeout = Timeout(seconds, exception)
timeout.start()
try:
... # exception will be raised here, after *seconds* passed since start() call
finally:
timeout.cancel()
timeout.close()
.. note:: If the code that the timeout was protecting finishes
executing before the timeout elapses, be sure to ``cancel`` the
timeout so it is not unexpectedly raised in the future. Even if
it is raised, it is a best practice to cancel it. This
``try/finally`` construct or a ``with`` statement is a
recommended pattern.
.. note::
When *exception* is omitted or ``None``, the :class:`Timeout` instance itself is raised:
If the code that the timeout was protecting finishes
executing before the timeout elapses, be sure to ``close`` the
timeout so it is not unexpectedly raised in the future. Even if it
is raised, it is a best practice to close it. This ``try/finally``
construct or a ``with`` statement is a recommended pattern. (If
the timeout object will be started again, use ``cancel`` instead
of ``close``; this is rare.)
When *exception* is omitted or ``None``, the ``Timeout`` instance
itself is raised::
>>> import gevent
>>> gevent.Timeout(0.1).start()
......@@ -76,14 +99,41 @@ class Timeout(BaseException):
...
Timeout: 0.1 seconds
To simplify starting and canceling timeouts, the ``with`` statement can be used::
If the *seconds* argument is not given or is ``None`` (e.g.,
``Timeout()``), then the timeout will never expire and never raise
*exception*. This is convenient for creating functions which take
an optional timeout parameter of their own. (Note that this is **not**
the same thing as a *seconds* value of ``0``.)
::
def function(args, timeout=None):
"A function with an optional timeout."
timer = Timeout(timeout)
with timer:
...
.. caution::
A *seconds* value less than ``0.0`` (e.g., ``-1``) is poorly defined. In the future,
support for negative values is likely to do the same thing as a value
of ``None`` or ``0``
A *seconds* value of ``0`` requests that the event loop spin and poll for I/O;
it will immediately expire as soon as control returns to the event loop.
.. rubric:: Use As A Context Manager
To simplify starting and canceling timeouts, the ``with``
statement can be used::
with gevent.Timeout(seconds, exception) as timeout:
pass # ... code block ...
This is equivalent to the try/finally block above with one additional feature:
if *exception* is the literal ``False``, the timeout is still raised, but the context manager
suppresses it, so the code outside the with-block won't see it.
This is equivalent to the try/finally block above with one
additional feature: if *exception* is the literal ``False``, the
timeout is still raised, but the context manager suppresses it, so
the code outside the with-block won't see it.
This is handy for adding a timeout to the functions that don't
support a *timeout* parameter themselves::
......@@ -96,9 +146,14 @@ class Timeout(BaseException):
else:
... # a line was read within 5 seconds
.. caution:: If ``readline()`` above catches and doesn't re-raise :class:`BaseException`
(for example, with a bare ``except:``), then your timeout will fail to function and control
won't be returned to you when you expect.
.. caution::
If ``readline()`` above catches and doesn't re-raise
:exc:`BaseException` (for example, with a bare ``except:``), then
your timeout will fail to function and control won't be returned
to you when you expect.
.. rubric:: Catching Timeouts
When catching timeouts, keep in mind that the one you catch may
not be the one you have set (a calling function may have set its
......@@ -112,41 +167,49 @@ class Timeout(BaseException):
except Timeout as t:
if t is not timeout:
raise # not my timeout
finally:
timeout.close()
If the *seconds* argument is not given or is ``None`` (e.g.,
``Timeout()``), then the timeout will never expire and never raise
*exception*. This is convenient for creating functions which take
an optional timeout parameter of their own. (Note that this is not the same thing
as a *seconds* value of 0.)
.. caution::
A *seconds* value less than 0.0 (e.g., -1) is poorly defined. In the future,
support for negative values is likely to do the same thing as a value
of ``None``.
.. versionchanged:: 1.1b2
If *seconds* is not given or is ``None``, no longer allocate a libev
timer that will never be started.
If *seconds* is not given or is ``None``, no longer allocate a
native timer object that will never be started.
.. versionchanged:: 1.1
Add warning about negative *seconds* values.
Add warning about negative *seconds* values.
.. versionchanged:: 1.3a1
Timeout objects now have a :meth:`close`
method that must be called when the timeout will no longer be
used to properly clean up native resources.
The ``with`` statement does this automatically.
"""
# We inherit a __dict__ from BaseException, so __slots__ actually
# makes us larger.
def __init__(self, seconds=None, exception=None, ref=True, priority=-1,
_use_timer=True, _one_shot=False):
_one_shot=False):
BaseException.__init__(self)
self.seconds = seconds
self.exception = exception
self._one_shot = _one_shot
if seconds is None or not _use_timer:
if seconds is None:
# Avoid going through the timer codepath if no timeout is
# desired; this avoids some CFFI interactions on PyPy that can lead to a
# RuntimeError if this implementation is used during an `import` statement. See
# https://bitbucket.org/pypy/pypy/issues/2089/crash-in-pypy-260-linux64-with-gevent-11b1
# and https://github.com/gevent/gevent/issues/618.
# Plus, in general, it should be more efficient
self.timer = _FakeTimer
else:
# XXX: A zero second timer could cause libuv to block the loop.
# XXX: A timer <= 0 could cause libuv to block the loop; we catch
# that case in libuv/loop.py
self.timer = get_hub().loop.timer(seconds or 0.0, ref=ref, priority=priority)
def start(self):
......@@ -207,18 +270,29 @@ class Timeout(BaseException):
@property
def pending(self):
"""Return True if the timeout is scheduled to be raised."""
"""True if the timeout is scheduled to be raised."""
return self.timer.pending or self.timer.active
def cancel(self):
"""If the timeout is pending, cancel it. Otherwise, do nothing."""
"""
If the timeout is pending, cancel it. Otherwise, do nothing.
The timeout object can be :meth:`started <start>` again. If
you will not start the timeout again, you should use
:meth:`close` instead.
"""
self.timer.stop()
if self._one_shot:
self.close()
def close(self):
"""
Close the timeout and free resources. The timer cannot be started again
after this method has been used.
"""
self.timer.stop()
self.timer.close()
self.timer = _FakeTimer
def __repr__(self):
classname = type(self).__name__
......@@ -251,6 +325,9 @@ class Timeout(BaseException):
return '%s second%s: %s' % (self.seconds, suffix, self.exception)
def __enter__(self):
"""
Start and return the timer. If the timer is already started, just return it.
"""
if not self.pending:
self.start()
return self
......@@ -258,6 +335,7 @@ class Timeout(BaseException):
def __exit__(self, typ, value, tb):
"""
Stop the timer.
.. versionchanged:: 1.3a1
The underlying native timer is also stopped. This object cannot be
used again.
......
......@@ -21,6 +21,9 @@ omit =
# local.so sometimes gets included, and it can't be parsed
# as source, so it fails the whole process.
*.so
src/gevent/libev/*.so
src/gevent/libuv/*.so
src/gevent/resolver/*.so
[report]
......
......@@ -157,6 +157,13 @@ if PYPY:
'test__socket_dns.py',
]
if TRAVIS:
FAILING_TESTS += [
# This fails to get the correct results, sometimes. I can't reproduce locally
'FLAKY test__example_udp_server.py',
'FLAKY test__example_udp_client.py',
]
if PY3 and TRAVIS:
FAILING_TESTS += [
## ---
......
......@@ -7,20 +7,7 @@ import types
from greentest.modules import walk_modules
from greentest.sysinfo import PLATFORM_SPECIFIC_SUFFIXES
MAPPING = {
'gevent.local': '_threading_local',
'gevent.socket': 'socket',
'gevent.select': 'select',
'gevent.ssl': 'ssl',
'gevent.thread': '_thread' if six.PY3 else 'thread',
'gevent.subprocess': 'subprocess',
'gevent.os': 'os',
'gevent.threading': 'threading',
'gevent.builtins': 'builtins' if six.PY3 else '__builtin__',
'gevent.signal': 'signal',
}
from gevent._patcher import MAPPING
class ANY(object):
def __contains__(self, item):
......@@ -43,13 +30,17 @@ COULD_BE_MISSING = {
'subprocess': ['_posixsubprocess'],
}
NO_ALL = ['gevent.threading',
'gevent._util',
'gevent._compat',
'gevent._socketcommon',
'gevent._fileobjectcommon', 'gevent._fileobjectposix',
'gevent._tblib',
'gevent._corecffi']
NO_ALL = [
'gevent.threading',
'gevent._util',
'gevent._compat',
'gevent._socketcommon',
'gevent._fileobjectcommon',
'gevent._fileobjectposix',
'gevent._tblib',
'gevent._corecffi',
'gevent._patcher',
]
# A list of modules that may contain things that aren't actually, technically,
# extensions, but that need to be in __extensions__ anyway due to the way,
......@@ -71,7 +62,7 @@ class Test(unittest.TestCase):
def check_all(self):
"Check that __all__ is present and does not contain invalid entries"
if not hasattr(self.module, '__all__'):
assert self.modname in NO_ALL
self.assertIn(self.modname, NO_ALL)
return
names = {}
six.exec_("from %s import *" % self.modname, names)
......@@ -86,10 +77,11 @@ class Test(unittest.TestCase):
def check_implements_presence_justified(self):
"Check that __implements__ is present only if the module is modeled after a module from stdlib (like gevent.socket)."
if self.__implements__ is not None and self.stdlib_module is None:
raise AssertionError('%r has __implements__ but no stdlib counterpart' % self.modname)
raise AssertionError('%r has __implements__ but no stdlib counterpart (%s)'
% (self.modname, self.stdlib_name))
def set_stdlib_all(self):
assert self.stdlib_module is not None
self.assertIsNotNone(self.stdlib_module)
self.stdlib_has_all = True
self.stdlib_all = getattr(self.stdlib_module, '__all__', None)
if self.stdlib_all is None:
......@@ -115,7 +107,7 @@ class Test(unittest.TestCase):
item = getattr(self.module, name)
try:
stdlib_item = getattr(self.stdlib_module, name)
assert item is not stdlib_item, (name, item, stdlib_item)
self.assertIsNot(item, stdlib_item)
except AttributeError:
if name not in COULD_BE_MISSING.get(self.stdlib_name, []):
raise
......@@ -125,7 +117,7 @@ class Test(unittest.TestCase):
for name in self.__imports__:
item = getattr(self.module, name)
stdlib_item = getattr(self.stdlib_module, name)
assert item is stdlib_item, (name, item, stdlib_item)
self.assertIs(item, stdlib_item)
def check_extensions_actually_extend(self):
"""Check that the module actually defines new entries in __extensions__"""
......
......@@ -17,7 +17,7 @@ class Test_udp_client(TestCase):
server = DatagramServer('127.0.0.1:9000', handle)
server.start()
try:
run([sys.executable, '-W', 'ignore' '-u', 'udp_client.py', 'Test_udp_client'],
run([sys.executable, '-W', 'ignore', '-u', 'udp_client.py', 'Test_udp_client'],
timeout=10, cwd='../../examples/')
finally:
server.close()
......
......@@ -4,10 +4,6 @@
import gevent
from gevent import monkey
if ['gevent.resolver.dnspython.Resolver'] == gevent.get_hub().resolver_class:
# dnspython requires monkey-patching
monkey.patch_all()
import os
import re
import greentest
......
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