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 **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.
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.
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.
.. 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',
NO_ALL = [
'gevent.threading',
'gevent._util',
'gevent._compat',
'gevent._socketcommon',
'gevent._fileobjectcommon', 'gevent._fileobjectposix',
'gevent._fileobjectcommon',
'gevent._fileobjectposix',
'gevent._tblib',
'gevent._corecffi']
'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