added completly new version for haslach 2025
This commit is contained in:
30
.venv/lib/python3.7/site-packages/asynctest/__init__.py
Normal file
30
.venv/lib/python3.7/site-packages/asynctest/__init__.py
Normal file
@@ -0,0 +1,30 @@
|
||||
# coding: utf-8
|
||||
# flake8: noqa
|
||||
"""
|
||||
The package asynctest is built on top of the standard :mod:`unittest` module
|
||||
and cuts down boilerplate code when testing libraries for :mod:`asyncio`.
|
||||
|
||||
asynctest imports the standard unittest package, overrides some of its features
|
||||
and adds new ones. A test author can import asynctest in place of
|
||||
:mod:`unittest` safely.
|
||||
|
||||
It is divided in submodules, but they are all imported at the top level,
|
||||
so :class:`asynctest.case.TestCase` is equivalent to :class:`asynctest.TestCase`.
|
||||
|
||||
Currently, asynctest targets the "selector" model. Hence, some features will
|
||||
not (yet) work with Windows' proactor.
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from unittest import *
|
||||
|
||||
# Shadows unittest with our enhanced classes
|
||||
from .case import *
|
||||
from .mock import *
|
||||
|
||||
# And load or own tools
|
||||
from ._fail_on import *
|
||||
from .helpers import *
|
||||
from .selector import *
|
||||
|
||||
__all__ = unittest.__all__
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
146
.venv/lib/python3.7/site-packages/asynctest/_fail_on.py
Normal file
146
.venv/lib/python3.7/site-packages/asynctest/_fail_on.py
Normal file
@@ -0,0 +1,146 @@
|
||||
# coding: utf-8
|
||||
"""
|
||||
:class:`asynctest.TestCase` decorator which controls checks performed after
|
||||
tests.
|
||||
|
||||
This module is separated from :mod:`asynctest.case` to avoid circular imports
|
||||
in modules registering new checks.
|
||||
|
||||
To implement new checks:
|
||||
|
||||
* its name must be added in the ``DEFAULTS`` dict,
|
||||
|
||||
* a static method of the same name must be added to the :class:`_fail_on`
|
||||
class,
|
||||
|
||||
* an optional static method named ``before_[name of the check]`` can be
|
||||
added to :class:`_fail_on` to implement some set-up before the test runs.
|
||||
|
||||
A check may be only available on some platforms, activated by a conditional
|
||||
import. In this case, ``DEFAULT`` and :class:`_fail_on` can be updated in the
|
||||
module. There is an example in the :mod:`asynctest.selector` module.
|
||||
"""
|
||||
from asyncio import TimerHandle
|
||||
|
||||
|
||||
_FAIL_ON_ATTR = "_asynctest_fail_on"
|
||||
|
||||
|
||||
#: Default value of the arguments of @fail_on, the name of the argument matches
|
||||
#: the name of the static method performing the check in the :class:`_fail_on`.
|
||||
#: The value is True when the check is enabled by default, False otherwise.
|
||||
DEFAULTS = {
|
||||
"unused_loop": False,
|
||||
"active_handles": False,
|
||||
}
|
||||
|
||||
|
||||
class _fail_on:
|
||||
def __init__(self, checks=None):
|
||||
self.checks = checks or {}
|
||||
self._computed_checks = None
|
||||
|
||||
def __call__(self, func):
|
||||
checker = getattr(func, _FAIL_ON_ATTR, None)
|
||||
if checker:
|
||||
checker = checker.copy()
|
||||
checker.update(self.checks)
|
||||
else:
|
||||
checker = self.copy()
|
||||
|
||||
setattr(func, _FAIL_ON_ATTR, checker)
|
||||
return func
|
||||
|
||||
def update(self, checks, override=True):
|
||||
if override:
|
||||
self.checks.update(checks)
|
||||
else:
|
||||
for check, value in checks.items():
|
||||
self.checks.setdefault(check, value)
|
||||
|
||||
def copy(self):
|
||||
return _fail_on(self.checks.copy())
|
||||
|
||||
def get_checks(self, case):
|
||||
# cache the result so it's consistent across calls to get_checks()
|
||||
if self._computed_checks is None:
|
||||
checks = DEFAULTS.copy()
|
||||
|
||||
try:
|
||||
checks.update(getattr(case, _FAIL_ON_ATTR, None).checks)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
checks.update(self.checks)
|
||||
self._computed_checks = checks
|
||||
|
||||
return self._computed_checks
|
||||
|
||||
def before_test(self, case):
|
||||
checks = self.get_checks(case)
|
||||
for check in filter(checks.get, checks):
|
||||
try:
|
||||
getattr(self, "before_test_" + check)(case)
|
||||
except (AttributeError, TypeError):
|
||||
pass
|
||||
|
||||
def check_test(self, case):
|
||||
checks = self.get_checks(case)
|
||||
for check in filter(checks.get, checks):
|
||||
getattr(self, check)(case)
|
||||
|
||||
# checks
|
||||
|
||||
@staticmethod
|
||||
def unused_loop(case):
|
||||
if not case.loop._asynctest_ran:
|
||||
case.fail("Loop did not run during the test")
|
||||
|
||||
@staticmethod
|
||||
def _is_live_timer_handle(handle):
|
||||
return isinstance(handle, TimerHandle) and not handle._cancelled
|
||||
|
||||
@classmethod
|
||||
def _live_timer_handles(cls, loop):
|
||||
return filter(cls._is_live_timer_handle, loop._scheduled)
|
||||
|
||||
@classmethod
|
||||
def active_handles(cls, case):
|
||||
handles = tuple(cls._live_timer_handles(case.loop))
|
||||
if handles:
|
||||
case.fail("Loop contained unfinished work {!r}".format(handles))
|
||||
|
||||
|
||||
def fail_on(**kwargs):
|
||||
"""
|
||||
Enable checks on the loop state after a test ran to help testers to
|
||||
identify common mistakes.
|
||||
"""
|
||||
# documented in asynctest.case.rst
|
||||
for kwarg in kwargs:
|
||||
if kwarg not in DEFAULTS:
|
||||
raise TypeError("fail_on() got an unexpected keyword argument "
|
||||
"'{}'".format(kwarg))
|
||||
|
||||
return _fail_on(kwargs)
|
||||
|
||||
|
||||
def _fail_on_all(flag, func):
|
||||
checker = _fail_on(dict((arg, flag) for arg in DEFAULTS))
|
||||
return checker if func is None else checker(func)
|
||||
|
||||
|
||||
def strict(func=None):
|
||||
"""
|
||||
Activate strict checking of the state of the loop after a test ran.
|
||||
"""
|
||||
# documented in asynctest.case.rst
|
||||
return _fail_on_all(True, func)
|
||||
|
||||
|
||||
def lenient(func=None):
|
||||
"""
|
||||
Deactivate all checks after a test ran.
|
||||
"""
|
||||
# documented in asynctest.case.rst
|
||||
return _fail_on_all(False, func)
|
505
.venv/lib/python3.7/site-packages/asynctest/case.py
Normal file
505
.venv/lib/python3.7/site-packages/asynctest/case.py
Normal file
@@ -0,0 +1,505 @@
|
||||
# coding: utf-8
|
||||
"""
|
||||
Module ``case``
|
||||
---------------
|
||||
|
||||
Enhance :class:`unittest.TestCase`:
|
||||
|
||||
* a new loop is issued and set as the default loop before each test, and
|
||||
closed and disposed after,
|
||||
|
||||
* if the loop uses a selector, it will be wrapped with
|
||||
:class:`asynctest.TestSelector`,
|
||||
|
||||
* a test method in a TestCase identified as a coroutine function or returning
|
||||
a coroutine will run on the loop,
|
||||
|
||||
* :meth:`~TestCase.setUp()` and :meth:`~TestCase.tearDown()` methods can be
|
||||
coroutine functions,
|
||||
|
||||
* cleanup functions registered with :meth:`~TestCase.addCleanup()` can be
|
||||
coroutine functions,
|
||||
|
||||
* a test fails if the loop did not run during the test.
|
||||
|
||||
class-level set-up
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Since each test runs in its own loop, it is not possible to run
|
||||
:meth:`~TestCase.setUpClass()` and :meth:`~TestCase.tearDownClass()` as
|
||||
coroutines.
|
||||
|
||||
If one needs to perform set-up actions at the class level (meaning
|
||||
once for all tests in the class), it should be done using a loop created for
|
||||
this sole purpose and that is not shared with the tests. Ideally, the loop
|
||||
shall be closed in the method which creates it.
|
||||
|
||||
If one really needs to share a loop between tests,
|
||||
:attr:`TestCase.use_default_loop` can be set to ``True`` (as a class
|
||||
attribute). The test case will use the loop returned by
|
||||
:meth:`asyncio.get_event_loop()` instead of creating a new loop for each test.
|
||||
This way, the event loop or event loop policy can be set during class-level
|
||||
set-up and tear down.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import functools
|
||||
import types
|
||||
import unittest
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
from unittest.case import * # NOQA
|
||||
|
||||
import asynctest.selector
|
||||
import asynctest._fail_on
|
||||
|
||||
|
||||
class _Policy(asyncio.AbstractEventLoopPolicy):
|
||||
def __init__(self, original_policy, loop, forbid_get_event_loop):
|
||||
self.original_policy = original_policy
|
||||
self.forbid_get_event_loop = forbid_get_event_loop
|
||||
self.loop = loop
|
||||
self.watcher = None
|
||||
|
||||
# we override the loop from the original policy because we don't want to
|
||||
# instantiate a "default loop" that may be never closed (usually, we only
|
||||
# run the test, so the "original default loop" is not even created by the
|
||||
# policy).
|
||||
def get_event_loop(self):
|
||||
if self.forbid_get_event_loop:
|
||||
raise AssertionError("TestCase.forbid_get_event_loop is True, "
|
||||
"asyncio.get_event_loop() must not be called")
|
||||
elif self.loop:
|
||||
return self.loop
|
||||
else:
|
||||
return self.original_policy.get_event_loop()
|
||||
|
||||
def new_event_loop(self):
|
||||
return self.original_policy.new_event_loop()
|
||||
|
||||
def set_event_loop(self, loop):
|
||||
result = self.original_policy.set_event_loop(loop)
|
||||
self.loop = loop
|
||||
return result
|
||||
|
||||
def _check_unix(self):
|
||||
if not hasattr(asyncio, "SafeChildWatcher"):
|
||||
raise NotImplementedError
|
||||
|
||||
def get_child_watcher(self):
|
||||
self._check_unix()
|
||||
if self.loop:
|
||||
if self.watcher is None:
|
||||
self.watcher = asyncio.SafeChildWatcher()
|
||||
self.watcher.attach_loop(self.loop)
|
||||
|
||||
return self.watcher
|
||||
else:
|
||||
return self.original_policy.get_child_watcher()
|
||||
|
||||
def set_child_watcher(self, watcher):
|
||||
self._check_unix()
|
||||
if self.loop:
|
||||
result = self.original_policy.set_child_watcher(watcher)
|
||||
self.watcher = watcher
|
||||
return result
|
||||
else:
|
||||
return self.original_policy.set_child_watcher(watcher)
|
||||
|
||||
def reset_watcher(self):
|
||||
if self.watcher:
|
||||
self.watcher.close()
|
||||
# force the original policy to reissue a child watcher next time
|
||||
# get_child_watcher() is called, which effectively attach the loop
|
||||
# to the new watcher. That's the best we can do so far
|
||||
self.original_policy.set_child_watcher(None)
|
||||
|
||||
|
||||
class TestCase(unittest.TestCase):
|
||||
"""
|
||||
A test which is a coroutine function or which returns a coroutine will run
|
||||
on the loop.
|
||||
|
||||
Once the test returned, one or more assertions are checked. For instance,
|
||||
a test fails if the loop didn't run. These checks can be enabled or
|
||||
disabled using the :func:`~asynctest.fail_on` decorator.
|
||||
|
||||
By default, a new loop is created and is set as the default loop before
|
||||
each test. Test authors can retrieve this loop with
|
||||
:attr:`~asynctest.TestCase.loop`.
|
||||
|
||||
If :attr:`~asynctest.TestCase.use_default_loop` is set to ``True``, the
|
||||
current default event loop is used instead. In this case, it is up to the
|
||||
test author to deal with the state of the loop in each test: the loop might
|
||||
be closed, callbacks and tasks may be scheduled by previous tests. It is
|
||||
also up to the test author to close the loop and dispose the related
|
||||
resources.
|
||||
|
||||
If :attr:`~asynctest.TestCase.forbid_get_event_loop` is set to ``True``,
|
||||
a call to :func:`asyncio.get_event_loop()` will raise an
|
||||
:exc:`AssertionError`. Since Python 3.6, calling
|
||||
:func:`asyncio.get_event_loop()` from a callback or a coroutine will return
|
||||
the running loop (instead of raising an exception).
|
||||
|
||||
These behaviors should be configured when defining the test case class::
|
||||
|
||||
class With_Reusable_Loop_TestCase(asynctest.TestCase):
|
||||
use_default_loop = True
|
||||
|
||||
forbid_get_event_loop = False
|
||||
|
||||
def test_something(self):
|
||||
pass
|
||||
|
||||
If :meth:`setUp()` and :meth:`tearDown()` are coroutine functions, they
|
||||
will run on the loop. Note that :meth:`setUpClass()` and
|
||||
:meth:`tearDownClass()` can not be coroutines.
|
||||
|
||||
.. versionadded:: 0.5
|
||||
|
||||
attribute :attr:`~asynctest.TestCase.use_default_loop`.
|
||||
|
||||
.. versionadded:: 0.7
|
||||
|
||||
attribute :attr:`~asynctest.TestCase.forbid_get_event_loop`.
|
||||
In any case, the default loop is now reset to its original state
|
||||
outside a test function.
|
||||
|
||||
.. versionadded:: 0.8
|
||||
|
||||
``ignore_loop`` has been deprecated in favor of the extensible
|
||||
:func:`~asynctest.fail_on` decorator.
|
||||
"""
|
||||
#: If true, the loop used by the test case is the current default event
|
||||
#: loop returned by :func:`asyncio.get_event_loop()`. The loop will not be
|
||||
#: closed and recreated between tests.
|
||||
use_default_loop = False
|
||||
|
||||
#: If true, the value returned by :func:`asyncio.get_event_loop()` will be
|
||||
#: set to ``None`` during the test. It allows to ensure that tested code
|
||||
#: use a loop object explicitly passed around.
|
||||
forbid_get_event_loop = False
|
||||
|
||||
#: Event loop created and set as default event loop during the test.
|
||||
loop = None
|
||||
|
||||
def _init_loop(self):
|
||||
if self.use_default_loop:
|
||||
self.loop = asyncio.get_event_loop()
|
||||
loop = None
|
||||
else:
|
||||
loop = self.loop = asyncio.new_event_loop()
|
||||
|
||||
policy = _Policy(asyncio.get_event_loop_policy(),
|
||||
loop, self.forbid_get_event_loop)
|
||||
|
||||
asyncio.set_event_loop_policy(policy)
|
||||
|
||||
self.loop = self._patch_loop(self.loop)
|
||||
|
||||
def _unset_loop(self):
|
||||
policy = asyncio.get_event_loop_policy()
|
||||
|
||||
if not self.use_default_loop:
|
||||
if sys.version_info >= (3, 6):
|
||||
self.loop.run_until_complete(self.loop.shutdown_asyncgens())
|
||||
self.loop.close()
|
||||
policy.reset_watcher()
|
||||
|
||||
asyncio.set_event_loop_policy(policy.original_policy)
|
||||
self.loop = None
|
||||
|
||||
def _patch_loop(self, loop):
|
||||
if hasattr(loop, '_asynctest_ran'):
|
||||
# The loop is already patched
|
||||
return loop
|
||||
|
||||
loop._asynctest_ran = False
|
||||
|
||||
def wraps(method):
|
||||
@functools.wraps(method)
|
||||
def wrapper(self, *args, **kwargs):
|
||||
try:
|
||||
return method(*args, **kwargs)
|
||||
finally:
|
||||
loop._asynctest_ran = True
|
||||
|
||||
return types.MethodType(wrapper, loop)
|
||||
|
||||
for method in ('run_forever', 'run_until_complete', ):
|
||||
setattr(loop, method, wraps(getattr(loop, method)))
|
||||
|
||||
if isinstance(loop, asyncio.selector_events.BaseSelectorEventLoop):
|
||||
loop._selector = asynctest.selector.TestSelector(loop._selector)
|
||||
|
||||
return loop
|
||||
|
||||
def _setUp(self):
|
||||
self._init_loop()
|
||||
|
||||
# initialize post-test checks
|
||||
test = getattr(self, self._testMethodName)
|
||||
checker = getattr(test, asynctest._fail_on._FAIL_ON_ATTR, None)
|
||||
self._checker = checker or asynctest._fail_on._fail_on()
|
||||
self._checker.before_test(self)
|
||||
|
||||
if asyncio.iscoroutinefunction(self.setUp):
|
||||
self.loop.run_until_complete(self.setUp())
|
||||
else:
|
||||
self.setUp()
|
||||
|
||||
# don't take into account if the loop ran during setUp
|
||||
self.loop._asynctest_ran = False
|
||||
|
||||
def _tearDown(self):
|
||||
if asyncio.iscoroutinefunction(self.tearDown):
|
||||
self.loop.run_until_complete(self.tearDown())
|
||||
else:
|
||||
self.tearDown()
|
||||
|
||||
# post-test checks
|
||||
self._checker.check_test(self)
|
||||
|
||||
# Override unittest.TestCase methods which call setUp() and tearDown()
|
||||
def run(self, result=None):
|
||||
orig_result = result
|
||||
if result is None:
|
||||
result = self.defaultTestResult()
|
||||
startTestRun = getattr(result, 'startTestRun', None)
|
||||
if startTestRun is not None:
|
||||
startTestRun()
|
||||
|
||||
result.startTest(self)
|
||||
|
||||
testMethod = getattr(self, self._testMethodName)
|
||||
if (getattr(self.__class__, "__unittest_skip__", False) or
|
||||
getattr(testMethod, "__unittest_skip__", False)):
|
||||
# If the class or method was skipped.
|
||||
try:
|
||||
skip_why = (getattr(self.__class__, '__unittest_skip_why__', '') or
|
||||
getattr(testMethod, '__unittest_skip_why__', ''))
|
||||
self._addSkip(result, self, skip_why)
|
||||
finally:
|
||||
result.stopTest(self)
|
||||
return
|
||||
expecting_failure = getattr(testMethod,
|
||||
"__unittest_expecting_failure__", False)
|
||||
outcome = unittest.case._Outcome(result)
|
||||
try:
|
||||
self._outcome = outcome
|
||||
|
||||
with outcome.testPartExecutor(self):
|
||||
self._setUp()
|
||||
if outcome.success:
|
||||
outcome.expecting_failure = expecting_failure
|
||||
with outcome.testPartExecutor(self, isTest=True):
|
||||
self._run_test_method(testMethod)
|
||||
outcome.expecting_failure = False
|
||||
with outcome.testPartExecutor(self):
|
||||
self._tearDown()
|
||||
|
||||
self.loop.run_until_complete(self.doCleanups())
|
||||
self._unset_loop()
|
||||
for test, reason in outcome.skipped:
|
||||
self._addSkip(result, test, reason)
|
||||
self._feedErrorsToResult(result, outcome.errors)
|
||||
if outcome.success:
|
||||
if expecting_failure:
|
||||
if outcome.expectedFailure:
|
||||
self._addExpectedFailure(result, outcome.expectedFailure)
|
||||
else:
|
||||
self._addUnexpectedSuccess(result)
|
||||
else:
|
||||
result.addSuccess(self)
|
||||
return result
|
||||
finally:
|
||||
result.stopTest(self)
|
||||
if orig_result is None:
|
||||
stopTestRun = getattr(result, 'stopTestRun', None)
|
||||
if stopTestRun is not None:
|
||||
stopTestRun()
|
||||
|
||||
# explicitly break reference cycles:
|
||||
# outcome.errors -> frame -> outcome -> outcome.errors
|
||||
# outcome.expectedFailure -> frame -> outcome -> outcome.expectedFailure
|
||||
outcome.errors.clear()
|
||||
outcome.expectedFailure = None
|
||||
|
||||
# clear the outcome, no more needed
|
||||
self._outcome = None
|
||||
|
||||
def debug(self):
|
||||
self._setUp()
|
||||
try:
|
||||
self._run_test_method(getattr(self, self._testMethodName))
|
||||
self._tearDown()
|
||||
|
||||
while self._cleanups:
|
||||
function, args, kwargs = self._cleanups.pop(-1)
|
||||
if asyncio.iscoroutinefunction(function):
|
||||
self.loop.run_until_complete(function(*args, **kwargs))
|
||||
else:
|
||||
function(*args, **kwargs)
|
||||
except Exception:
|
||||
raise
|
||||
finally:
|
||||
self._unset_loop()
|
||||
|
||||
def _run_test_method(self, method):
|
||||
# If the method is a coroutine or returns a coroutine, run it on the
|
||||
# loop
|
||||
result = method()
|
||||
if asyncio.iscoroutine(result):
|
||||
self.loop.run_until_complete(result)
|
||||
|
||||
@asyncio.coroutine
|
||||
def doCleanups(self):
|
||||
"""
|
||||
Execute all cleanup functions. Normally called for you after tearDown.
|
||||
"""
|
||||
outcome = self._outcome or unittest.mock._Outcome()
|
||||
while self._cleanups:
|
||||
function, args, kwargs = self._cleanups.pop()
|
||||
with outcome.testPartExecutor(self):
|
||||
if asyncio.iscoroutinefunction(function):
|
||||
yield from function(*args, **kwargs)
|
||||
else:
|
||||
function(*args, **kwargs)
|
||||
|
||||
return outcome.success
|
||||
|
||||
def addCleanup(self, function, *args, **kwargs):
|
||||
"""
|
||||
Add a function, with arguments, to be called when the test is
|
||||
completed. If function is a coroutine function, it will run on the loop
|
||||
before it's cleaned.
|
||||
"""
|
||||
return super().addCleanup(function, *args, **kwargs)
|
||||
|
||||
@asyncio.coroutine
|
||||
def assertAsyncRaises(self, exception, awaitable):
|
||||
"""
|
||||
Test that an exception of type ``exception`` is raised when an
|
||||
exception is raised when awaiting ``awaitable``, a future or coroutine.
|
||||
|
||||
:see: :meth:`unittest.TestCase.assertRaises()`
|
||||
"""
|
||||
with self.assertRaises(exception):
|
||||
return (yield from awaitable)
|
||||
|
||||
@asyncio.coroutine
|
||||
def assertAsyncRaisesRegex(self, exception, regex, awaitable):
|
||||
"""
|
||||
Like :meth:`assertAsyncRaises()` but also tests that ``regex`` matches
|
||||
on the string representation of the raised exception.
|
||||
|
||||
:see: :meth:`unittest.TestCase.assertRaisesRegex()`
|
||||
"""
|
||||
with self.assertRaisesRegex(exception, regex):
|
||||
return (yield from awaitable)
|
||||
|
||||
@asyncio.coroutine
|
||||
def assertAsyncWarns(self, warning, awaitable):
|
||||
"""
|
||||
Test that a warning is triggered when awaiting ``awaitable``, a future
|
||||
or a coroutine.
|
||||
|
||||
:see: :meth:`unittest.TestCase.assertWarns()`
|
||||
"""
|
||||
with self.assertWarns(warning):
|
||||
return (yield from awaitable)
|
||||
|
||||
@asyncio.coroutine
|
||||
def assertAsyncWarnsRegex(self, warning, regex, awaitable):
|
||||
"""
|
||||
Like :meth:`assertAsyncWarns()` but also tests that ``regex`` matches
|
||||
on the message of the triggered warning.
|
||||
|
||||
:see: :meth:`unittest.TestCase.assertWarnsRegex()`
|
||||
"""
|
||||
with self.assertWarnsRegex(warning, regex):
|
||||
return (yield from awaitable)
|
||||
|
||||
|
||||
class FunctionTestCase(TestCase, unittest.FunctionTestCase):
|
||||
"""
|
||||
Enables the same features as :class:`~asynctest.TestCase`, but for
|
||||
:class:`~asynctest.FunctionTestCase`.
|
||||
"""
|
||||
|
||||
|
||||
class ClockedTestCase(TestCase):
|
||||
"""
|
||||
Subclass of :class:`~asynctest.TestCase` with a controlled loop clock,
|
||||
useful for testing timer based behaviour without slowing test run time.
|
||||
|
||||
The clock will only advance when :meth:`advance()` is called.
|
||||
"""
|
||||
def _init_loop(self):
|
||||
super()._init_loop()
|
||||
self.loop.time = functools.wraps(self.loop.time)(lambda: self._time)
|
||||
self._time = 0
|
||||
|
||||
@asyncio.coroutine
|
||||
def advance(self, seconds):
|
||||
"""
|
||||
Fast forward time by a number of ``seconds``.
|
||||
|
||||
Callbacks scheduled to run up to the destination clock time will be
|
||||
executed on time:
|
||||
|
||||
>>> self.loop.call_later(1, print_time)
|
||||
>>> self.loop.call_later(2, self.loop.call_later, 1, print_time)
|
||||
>>> await self.advance(3)
|
||||
1
|
||||
3
|
||||
|
||||
In this example, the third callback is scheduled at ``t = 2`` to be
|
||||
executed at ``t + 1``. Hence, it will run at ``t = 3``. The callback as
|
||||
been called on time.
|
||||
"""
|
||||
if seconds < 0:
|
||||
raise ValueError(
|
||||
'Cannot go back in time ({} seconds)'.format(seconds))
|
||||
|
||||
yield from self._drain_loop()
|
||||
|
||||
target_time = self._time + seconds
|
||||
while True:
|
||||
next_time = self._next_scheduled()
|
||||
if next_time is None or next_time > target_time:
|
||||
break
|
||||
|
||||
self._time = next_time
|
||||
yield from self._drain_loop()
|
||||
|
||||
self._time = target_time
|
||||
yield from self._drain_loop()
|
||||
|
||||
def _next_scheduled(self):
|
||||
try:
|
||||
return self.loop._scheduled[0]._when
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
@asyncio.coroutine
|
||||
def _drain_loop(self):
|
||||
while True:
|
||||
next_time = self._next_scheduled()
|
||||
if not self.loop._ready and (next_time is None or
|
||||
next_time > self._time):
|
||||
break
|
||||
|
||||
yield from asyncio.sleep(0)
|
||||
self.loop._TestCase_asynctest_ran = True
|
||||
|
||||
|
||||
def ignore_loop(func=None):
|
||||
"""
|
||||
Ignore the error case where the loop did not run during the test.
|
||||
"""
|
||||
warnings.warn("ignore_loop() is deprecated in favor of "
|
||||
"fail_on(unused_loop=False)", DeprecationWarning)
|
||||
checker = asynctest._fail_on._fail_on({"unused_loop": False})
|
||||
return checker if func is None else checker(func)
|
24
.venv/lib/python3.7/site-packages/asynctest/helpers.py
Normal file
24
.venv/lib/python3.7/site-packages/asynctest/helpers.py
Normal file
@@ -0,0 +1,24 @@
|
||||
# coding: utf-8
|
||||
"""
|
||||
Module ``helpers``
|
||||
------------------
|
||||
|
||||
Helper functions and coroutines for :mod:`asynctest`.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
|
||||
|
||||
@asyncio.coroutine
|
||||
def exhaust_callbacks(loop):
|
||||
"""
|
||||
Run the loop until all ready callbacks are executed.
|
||||
|
||||
The coroutine doesn't wait for callbacks scheduled in the future with
|
||||
:meth:`~asyncio.BaseEventLoop.call_at()` or
|
||||
:meth:`~asyncio.BaseEventLoop.call_later()`.
|
||||
|
||||
:param loop: event loop
|
||||
"""
|
||||
while loop._ready:
|
||||
yield from asyncio.sleep(0, loop=loop)
|
1382
.venv/lib/python3.7/site-packages/asynctest/mock.py
Normal file
1382
.venv/lib/python3.7/site-packages/asynctest/mock.py
Normal file
File diff suppressed because it is too large
Load Diff
378
.venv/lib/python3.7/site-packages/asynctest/selector.py
Normal file
378
.venv/lib/python3.7/site-packages/asynctest/selector.py
Normal file
@@ -0,0 +1,378 @@
|
||||
# coding: utf-8
|
||||
"""
|
||||
Module ``selector``
|
||||
-------------------
|
||||
|
||||
Mock of :mod:`selectors` and compatible objects performing asynchronous IO.
|
||||
|
||||
This module provides classes to mock objects performing IO (files, sockets,
|
||||
etc). These mocks are compatible with :class:`~asynctest.TestSelector`, which
|
||||
can simulate the behavior of a selector on the mock objects, or forward actual
|
||||
work to a real selector.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
try:
|
||||
import selectors
|
||||
except ImportError:
|
||||
# In the case of Python 3.3, attempt to use the selectors
|
||||
# modules from within the asyncio package
|
||||
import asyncio.selectors as selectors
|
||||
import socket
|
||||
|
||||
try:
|
||||
import ssl
|
||||
except ImportError:
|
||||
# allow python to be compiled without ssl
|
||||
ssl = None
|
||||
|
||||
from . import mock
|
||||
from . import _fail_on
|
||||
|
||||
|
||||
class FileDescriptor(int):
|
||||
"""
|
||||
A subclass of int which allows to identify the virtual file-descriptor of a
|
||||
:class:`~asynctest.FileMock`.
|
||||
|
||||
If :class:`~asynctest.FileDescriptor()` without argument, its value will be
|
||||
the value of :data:`~FileDescriptor.next_fd`.
|
||||
|
||||
When an object is created, :data:`~FileDescriptor.next_fd` is set to the
|
||||
highest value for a :class:`~asynctest.FileDescriptor` object + 1.
|
||||
"""
|
||||
next_fd = 0
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
if not args and not kwargs:
|
||||
s = super().__new__(cls, FileDescriptor.next_fd)
|
||||
else:
|
||||
s = super().__new__(cls, *args, **kwargs)
|
||||
|
||||
FileDescriptor.next_fd = max(FileDescriptor.next_fd + 1, s + 1)
|
||||
|
||||
return s
|
||||
|
||||
def __hash__(self):
|
||||
# Return a different hash than the int so we can register both a
|
||||
# FileDescriptor object and an int of the same value
|
||||
return hash('__FileDescriptor_{}'.format(self))
|
||||
|
||||
|
||||
def fd(fileobj):
|
||||
"""
|
||||
Return the :class:`~asynctest.FileDescriptor` value of ``fileobj``.
|
||||
|
||||
If ``fileobj`` is a :class:`~asynctest.FileDescriptor`, ``fileobj`` is
|
||||
returned, else ``fileobj.fileno()`` is returned instead.
|
||||
|
||||
Note that if fileobj is an int, :exc:`ValueError` is raised.
|
||||
|
||||
:raise ValueError: if ``fileobj`` is not a :class:`~asynctest.FileMock`,
|
||||
a file-like object or
|
||||
a :class:`~asynctest.FileDescriptor`.
|
||||
"""
|
||||
try:
|
||||
return fileobj if isinstance(fileobj, FileDescriptor) else fileobj.fileno()
|
||||
except Exception:
|
||||
raise ValueError
|
||||
|
||||
|
||||
def isfilemock(obj):
|
||||
"""
|
||||
Return ``True`` if the ``obj`` or ``obj.fileno()`` is
|
||||
a :class:`asynctest.FileDescriptor`.
|
||||
"""
|
||||
try:
|
||||
return (isinstance(obj, FileDescriptor) or
|
||||
isinstance(obj.fileno(), FileDescriptor))
|
||||
except AttributeError:
|
||||
# obj has no attribute fileno()
|
||||
return False
|
||||
|
||||
|
||||
class FileMock(mock.Mock):
|
||||
"""
|
||||
Mock a file-like object.
|
||||
|
||||
A FileMock is an intelligent mock which can work with TestSelector to
|
||||
simulate IO events during tests.
|
||||
|
||||
.. method:: fileno()
|
||||
|
||||
Return a :class:`~asynctest.FileDescriptor` object.
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.fileno.return_value = FileDescriptor()
|
||||
|
||||
def _get_child_mock(self, *args, **kwargs):
|
||||
# A FileMock returns a Mock by default, not a FileMock
|
||||
return mock.Mock(**kwargs)
|
||||
|
||||
|
||||
class SocketMock(FileMock):
|
||||
"""
|
||||
Mock a socket.
|
||||
|
||||
See :class:`~asynctest.FileMock`.
|
||||
"""
|
||||
def __init__(self, side_effect=None, return_value=mock.DEFAULT,
|
||||
wraps=None, name=None, spec_set=None, parent=None,
|
||||
**kwargs):
|
||||
super().__init__(socket.socket, side_effect=side_effect,
|
||||
return_value=return_value, wraps=wraps, name=name,
|
||||
spec_set=spec_set, parent=parent, **kwargs)
|
||||
|
||||
|
||||
if ssl:
|
||||
class SSLSocketMock(SocketMock):
|
||||
"""
|
||||
Mock a socket wrapped by the :mod:`ssl` module.
|
||||
|
||||
See :class:`~asynctest.FileMock`.
|
||||
|
||||
.. versionadded:: 0.5
|
||||
"""
|
||||
def __init__(self, side_effect=None, return_value=mock.DEFAULT,
|
||||
wraps=None, name=None, spec_set=None, parent=None,
|
||||
**kwargs):
|
||||
FileMock.__init__(self, ssl.SSLSocket, side_effect=side_effect,
|
||||
return_value=return_value, wraps=wraps, name=name,
|
||||
spec_set=spec_set, parent=parent, **kwargs)
|
||||
|
||||
|
||||
def _set_event_ready(fileobj, loop, event):
|
||||
selector = loop._selector
|
||||
fd = selector._fileobj_lookup(fileobj)
|
||||
|
||||
if fd in selector._fd_to_key:
|
||||
loop._process_events([(selector._fd_to_key[fd], event)])
|
||||
|
||||
|
||||
def set_read_ready(fileobj, loop):
|
||||
"""
|
||||
Schedule callbacks registered on ``loop`` as if the selector notified that
|
||||
data is ready to be read on ``fileobj``.
|
||||
|
||||
:param fileobj: file object or :class:`~asynctest.FileMock` on which the
|
||||
event is mocked.
|
||||
|
||||
:param loop: :class:`asyncio.SelectorEventLoop` watching for events on
|
||||
``fileobj``.
|
||||
|
||||
::
|
||||
|
||||
mock = asynctest.SocketMock()
|
||||
mock.recv.return_value = b"Data"
|
||||
|
||||
def read_ready(sock):
|
||||
print("received:", sock.recv(1024))
|
||||
|
||||
loop.add_reader(mock, read_ready, mock)
|
||||
|
||||
set_read_ready(mock, loop)
|
||||
|
||||
loop.run_forever() # prints received: b"Data"
|
||||
|
||||
.. versionadded:: 0.4
|
||||
"""
|
||||
# since the selector would notify of events at the beginning of the next
|
||||
# iteration, we let this iteration finish before actually scheduling the
|
||||
# reader (hence the call_soon)
|
||||
loop.call_soon_threadsafe(_set_event_ready, fileobj, loop, selectors.EVENT_READ)
|
||||
|
||||
|
||||
def set_write_ready(fileobj, loop):
|
||||
"""
|
||||
Schedule callbacks registered on ``loop`` as if the selector notified that
|
||||
data can be written to ``fileobj``.
|
||||
|
||||
:param fileobj: file object or :class:`~asynctest.FileMock` on which th
|
||||
event is mocked.
|
||||
:param loop: :class:`asyncio.SelectorEventLoop` watching for events on
|
||||
``fileobj``.
|
||||
|
||||
.. versionadded:: 0.4
|
||||
"""
|
||||
loop.call_soon_threadsafe(_set_event_ready, fileobj, loop, selectors.EVENT_WRITE)
|
||||
|
||||
|
||||
class TestSelector(selectors._BaseSelectorImpl):
|
||||
"""
|
||||
A selector which supports IOMock objects.
|
||||
|
||||
It can wrap an actual implementation of a selector, so the selector will
|
||||
work both with mocks and real file-like objects.
|
||||
|
||||
A common use case is to patch the selector loop::
|
||||
|
||||
loop._selector = asynctest.TestSelector(loop._selector)
|
||||
|
||||
:param selector: optional, if provided, this selector will be used to work
|
||||
with real file-like objects.
|
||||
"""
|
||||
def __init__(self, selector=None):
|
||||
super().__init__()
|
||||
self._selector = selector
|
||||
|
||||
def _fileobj_lookup(self, fileobj):
|
||||
if isfilemock(fileobj):
|
||||
return fd(fileobj)
|
||||
|
||||
return super()._fileobj_lookup(fileobj)
|
||||
|
||||
def register(self, fileobj, events, data=None):
|
||||
"""
|
||||
Register a file object or a :class:`~asynctest.FileMock`.
|
||||
|
||||
If a real selector object has been supplied to the
|
||||
:class:`~asynctest.TestSelector` object and ``fileobj`` is not
|
||||
a :class:`~asynctest.FileMock` or a :class:`~asynctest.FileDescriptor`
|
||||
returned by :meth:`FileMock.fileno()`, the object will be registered to
|
||||
the real selector.
|
||||
|
||||
See :meth:`selectors.BaseSelector.register`.
|
||||
"""
|
||||
if isfilemock(fileobj) or self._selector is None:
|
||||
key = super().register(fileobj, events, data)
|
||||
else:
|
||||
key = self._selector.register(fileobj, events, data)
|
||||
|
||||
if key:
|
||||
self._fd_to_key[key.fd] = key
|
||||
|
||||
return key
|
||||
|
||||
def unregister(self, fileobj):
|
||||
"""
|
||||
Unregister a file object or a :class:`~asynctest.FileMock`.
|
||||
|
||||
See :meth:`selectors.BaseSelector.unregister`.
|
||||
"""
|
||||
if isfilemock(fileobj) or self._selector is None:
|
||||
key = super().unregister(fileobj)
|
||||
else:
|
||||
key = self._selector.unregister(fileobj)
|
||||
|
||||
if key and key.fd in self._fd_to_key:
|
||||
del self._fd_to_key[key.fd]
|
||||
|
||||
return key
|
||||
|
||||
def modify(self, fileobj, events, data=None):
|
||||
"""
|
||||
Shortcut when calling :meth:`TestSelector.unregister` then
|
||||
:meth:`TestSelector.register` to update the registration of a an object
|
||||
to the selector.
|
||||
|
||||
See :meth:`selectors.BaseSelector.modify`.
|
||||
"""
|
||||
if isfilemock(fileobj) or self._selector is None:
|
||||
key = super().modify(fileobj, events, data)
|
||||
else:
|
||||
# del the key first because modify() fails if events is incorrect
|
||||
fd = self._fileobj_lookup(fileobj)
|
||||
|
||||
if fd in self._fd_to_key:
|
||||
del self._fd_to_key[fd]
|
||||
|
||||
key = self._selector.modify(fileobj, events, data)
|
||||
|
||||
if key:
|
||||
self._fd_to_key[key.fd] = key
|
||||
|
||||
return key
|
||||
|
||||
def select(self, timeout=None):
|
||||
"""
|
||||
Perform the selection.
|
||||
|
||||
This method is a no-op if no actual selector has been supplied.
|
||||
|
||||
See :meth:`selectors.BaseSelector.select`.
|
||||
"""
|
||||
if self._selector is None:
|
||||
return []
|
||||
|
||||
return self._selector.select(timeout)
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
Close the selector.
|
||||
|
||||
Close the actual selector if supplied, unregister all mocks.
|
||||
|
||||
See :meth:`selectors.BaseSelector.close`.
|
||||
"""
|
||||
if self._selector is not None:
|
||||
self._selector.close()
|
||||
|
||||
super().close()
|
||||
|
||||
|
||||
def get_registered_events(selector):
|
||||
watched_events = []
|
||||
for event in selector.get_map().values():
|
||||
watched_events.append(event)
|
||||
|
||||
if selector._selector is not None:
|
||||
# this is our TestSelector, wrapping a true selector object
|
||||
for event in selector._selector.get_map().values():
|
||||
watched_events.append(event)
|
||||
|
||||
return set(watched_events)
|
||||
|
||||
|
||||
if hasattr(asyncio, "format_helpers"):
|
||||
# Python 3.7+
|
||||
def _format_callback(handle):
|
||||
return asyncio.format_helpers._format_callback(handle._callback,
|
||||
handle._args, None)
|
||||
elif hasattr(asyncio.events, "_format_args_and_kwargs"):
|
||||
# Python 3.5, 3.6
|
||||
def _format_callback(handle):
|
||||
return asyncio.events._format_callback(handle._callback, handle._args,
|
||||
None)
|
||||
else:
|
||||
# Python 3.4
|
||||
def _format_callback(handle):
|
||||
return asyncio.events._format_callback(handle._callback, handle._args)
|
||||
|
||||
|
||||
def _format_event(event):
|
||||
callbacks = []
|
||||
|
||||
if event.events & selectors.EVENT_READ:
|
||||
callbacks.append("add_reader({}, {})".format(
|
||||
event.fileobj, _format_callback(event.data[0])))
|
||||
|
||||
if event.events & selectors.EVENT_WRITE:
|
||||
callbacks.append("add_writer({}, {})".format(
|
||||
event.fileobj, _format_callback(event.data[1])))
|
||||
|
||||
return callbacks
|
||||
|
||||
|
||||
def fail_on_before_test_active_selector_callbacks(case):
|
||||
case._active_selector_callbacks = get_registered_events(
|
||||
case.loop._selector)
|
||||
|
||||
|
||||
def fail_on_active_selector_callbacks(case):
|
||||
ignored_events = case._active_selector_callbacks
|
||||
active_events = get_registered_events(case.loop._selector)
|
||||
|
||||
output = ["some events watched during the tests were not removed:"]
|
||||
for c in map(_format_event, active_events - ignored_events):
|
||||
output.extend(c)
|
||||
|
||||
if len(output) > 1:
|
||||
case.fail("\n - ".join(output))
|
||||
|
||||
|
||||
_fail_on.DEFAULTS["active_selector_callbacks"] = False
|
||||
_fail_on._fail_on.active_selector_callbacks = staticmethod(fail_on_active_selector_callbacks)
|
||||
_fail_on._fail_on.before_test_active_selector_callbacks = \
|
||||
staticmethod(fail_on_before_test_active_selector_callbacks)
|
Reference in New Issue
Block a user