pax_global_header00006660000000000000000000000064134147667210014525gustar00rootroot0000000000000052 comment=813423de99ddfa16796ea7f37f60baa65afa6759 pytest-asyncio-0.10.0/000077500000000000000000000000001341476672100145765ustar00rootroot00000000000000pytest-asyncio-0.10.0/.gitignore000066400000000000000000000013301341476672100165630ustar00rootroot00000000000000# Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] # C extensions *.so # Distribution / packaging .Python env/ build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ *.egg-info/ .installed.cfg *.egg .hypothesis/ # PyInstaller # Usually these files are written by a python script from a template # before PyInstaller builds the exe, so as to inject date/other infos into it. *.manifest *.spec # Installer logs pip-log.txt pip-delete-this-directory.txt # Unit test / coverage reports htmlcov/ .tox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *,cover # Translations *.mo *.pot # Django stuff: *.log # Sphinx documentation docs/_build/ # PyBuilder target/ .venv* .ideapytest-asyncio-0.10.0/.travis.yml000066400000000000000000000007411341476672100167110ustar00rootroot00000000000000language: python matrix: include: - python: 3.5 env: TOX_ENV=py35 - python: 3.6 env: TOX_ENV=py36 - python: 3.7 env: TOX_ENV=py37 # TODO: the dist and sudo keys are currently needed to use Python 3.7. # They should be removed once Travis-CI supports 3.7 on the default image. dist: xenial sudo: true install: pip install tox-travis coveralls script: tox -e $TOX_ENV after_success: - tox -e coverage-report - coveralls pytest-asyncio-0.10.0/LICENSE000066400000000000000000000260751341476672100156150ustar00rootroot00000000000000Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. pytest-asyncio-0.10.0/README.rst000066400000000000000000000221011341476672100162610ustar00rootroot00000000000000pytest-asyncio: pytest support for asyncio ========================================== .. image:: https://img.shields.io/pypi/v/pytest-asyncio.svg :target: https://pypi.python.org/pypi/pytest-asyncio .. image:: https://travis-ci.org/pytest-dev/pytest-asyncio.svg?branch=master :target: https://travis-ci.org/pytest-dev/pytest-asyncio .. image:: https://coveralls.io/repos/pytest-dev/pytest-asyncio/badge.svg :target: https://coveralls.io/r/pytest-dev/pytest-asyncio .. image:: https://img.shields.io/pypi/pyversions/pytest-asyncio.svg :target: https://github.com/pytest-dev/pytest-asyncio :alt: Supported Python versions pytest-asyncio is an Apache2 licensed library, written in Python, for testing asyncio code with pytest. asyncio code is usually written in the form of coroutines, which makes it slightly more difficult to test using normal testing tools. pytest-asyncio provides useful fixtures and markers to make testing easier. .. code-block:: python @pytest.mark.asyncio async def test_some_asyncio_code(): res = await library.do_something() assert b'expected result' == res pytest-asyncio has been strongly influenced by pytest-tornado_. .. _pytest-tornado: https://github.com/eugeniy/pytest-tornado Features -------- - fixtures for creating and injecting versions of the asyncio event loop - fixtures for injecting unused tcp ports - pytest markers for treating tests as asyncio coroutines - easy testing with non-default event loops - support for `async def` fixtures and async generator fixtures Installation ------------ To install pytest-asyncio, simply: .. code-block:: bash $ pip install pytest-asyncio This is enough for pytest to pick up pytest-asyncio. Fixtures -------- ``event_loop`` ~~~~~~~~~~~~~~ Creates and injects a new instance of the default asyncio event loop. By default, the loop will be closed at the end of the test (i.e. the default fixture scope is ``function``). Note that just using the ``event_loop`` fixture won't make your test function a coroutine. You'll need to interact with the event loop directly, using methods like ``event_loop.run_until_complete``. See the ``pytest.mark.asyncio`` marker for treating test functions like coroutines. Simply using this fixture will not set the generated event loop as the default asyncio event loop, or change the asyncio event loop policy in any way. Use ``pytest.mark.asyncio`` for this purpose. .. code-block:: python def test_http_client(event_loop): url = 'http://httpbin.org/get' resp = event_loop.run_until_complete(http_client(url)) assert b'HTTP/1.1 200 OK' in resp This fixture can be easily overridden in any of the standard pytest locations (e.g. directly in the test file, or in ``conftest.py``) to use a non-default event loop. This will take effect even if you're using the ``pytest.mark.asyncio`` marker and not the ``event_loop`` fixture directly. .. code-block:: python @pytest.yield_fixture() def event_loop(): loop = MyCustomLoop() yield loop loop.close() If the ``pytest.mark.asyncio`` marker is applied, a pytest hook will ensure the produced loop is set as the default global loop. Fixtures depending on the ``event_loop`` fixture can expect the policy to be properly modified when they run. ``unused_tcp_port`` ~~~~~~~~~~~~~~~~~~~ Finds and yields a single unused TCP port on the localhost interface. Useful for binding temporary test servers. ``unused_tcp_port_factory`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~ A callable which returns a different unused TCP port each invocation. Useful when several unused TCP ports are required in a test. .. code-block:: python def a_test(unused_tcp_port_factory): port1, port2 = unused_tcp_port_factory(), unused_tcp_port_factory() ... Async fixtures ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Asynchronous fixtures are defined just like ordinary pytest fixtures, except they should be coroutines or asynchronous generators. .. code-block:: python3 @pytest.fixture async def async_gen_fixture(): await asyncio.sleep(0.1) yield 'a value' @pytest.fixture(scope='module') async def async_fixture(): return await asyncio.sleep(0.1) All scopes are supported, but if you use a non-function scope you will need to redefine the ``event_loop`` fixture to have the same or broader scope. Async fixtures need the event loop, and so must have the same or narrower scope than the ``event_loop`` fixture. If you want to do this with Python 3.5, the ``yield`` statement must be replaced with ``await yield_()`` and the coroutine function must be decorated with ``@async_generator``, like so: .. code-block:: python3 from async_generator import yield_, async_generator @pytest.fixture @async_generator async def async_gen_fixture(): await asyncio.sleep(0.1) await yield_('a value') Markers ------- ``pytest.mark.asyncio`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Mark your test coroutine with this marker and pytest will execute it as an asyncio task using the event loop provided by the ``event_loop`` fixture. See the introductory section for an example. The event loop used can be overriden by overriding the ``event_loop`` fixture (see above). In order to make your test code a little more concise, the pytest |pytestmark|_ feature can be used to mark entire modules or classes with this marker. Only test coroutines will be affected (by default, coroutines prefixed by ``test_``), so, for example, fixtures are safe to define. .. code-block:: python import asyncio import pytest # All test coroutines will be treated as marked. pytestmark = pytest.mark.asyncio async def test_example(event_loop): """No marker!""" await asyncio.sleep(0, loop=event_loop) .. |pytestmark| replace:: ``pytestmark`` .. _pytestmark: http://doc.pytest.org/en/latest/example/markers.html#marking-whole-classes-or-modules Changelog --------- 0.10.0. (UNRELEASED) ~~~~~~~~~~~~~~~~~~~~ - ``pytest-asyncio`` integrates with `Hypothesis `_ to support ``@given`` on async test functions using ``asyncio``. `#102` - Pytest 4.1 support. `#105` 0.9.0 (2018-07-28) ~~~~~~~~~~~~~~~~~~ - Python 3.7 support. - Remove ``event_loop_process_pool`` fixture and ``pytest.mark.asyncio_process_pool`` marker (see https://bugs.python.org/issue34075 for deprecation and removal details) 0.8.0 (2017-09-23) ~~~~~~~~~~~~~~~~~~ - Improve integration with other packages (like aiohttp) with more careful event loop handling. `#64` 0.7.0 (2017-09-08) ~~~~~~~~~~~~~~~~~~ - Python versions pre-3.6 can use the async_generator library for async fixtures. `#62 ` 0.6.0 (2017-05-28) ~~~~~~~~~~~~~~~~~~ - Support for Python versions pre-3.5 has been dropped. - ``pytestmark`` now works on both module and class level. - The ``forbid_global_loop`` parameter has been removed. - Support for async and async gen fixtures has been added. `#45 `_ - The deprecation warning regarding ``asyncio.async()`` has been fixed. `#51 `_ 0.5.0 (2016-09-07) ~~~~~~~~~~~~~~~~~~ - Introduced a changelog. `#31 `_ - The ``event_loop`` fixture is again responsible for closing itself. This makes the fixture slightly harder to correctly override, but enables other fixtures to depend on it correctly. `#30 `_ - Deal with the event loop policy by wrapping a special pytest hook, ``pytest_fixture_setup``. This allows setting the policy before fixtures dependent on the ``event_loop`` fixture run, thus allowing them to take advantage of the ``forbid_global_loop`` parameter. As a consequence of this, we now depend on pytest 3.0. `#29 `_ 0.4.1 (2016-06-01) ~~~~~~~~~~~~~~~~~~ - Fix a bug preventing the propagation of exceptions from the plugin. `#25 `_ 0.4.0 (2016-05-30) ~~~~~~~~~~~~~~~~~~ - Make ``event_loop`` fixtures simpler to override by closing them in the plugin, instead of directly in the fixture. `#21 `_ - Introduce the ``forbid_global_loop`` parameter. `#21 `_ 0.3.0 (2015-12-19) ~~~~~~~~~~~~~~~~~~ - Support for Python 3.5 ``async``/``await`` syntax. `#17 `_ 0.2.0 (2015-08-01) ~~~~~~~~~~~~~~~~~~ - ``unused_tcp_port_factory`` fixture. `#10 `_ 0.1.1 (2015-04-23) ~~~~~~~~~~~~~~~~~~ Initial release. Contributing ------------ Contributions are very welcome. Tests can be run with ``tox``, please ensure the coverage at least stays the same before you submit a pull request. pytest-asyncio-0.10.0/pytest_asyncio/000077500000000000000000000000001341476672100176535ustar00rootroot00000000000000pytest-asyncio-0.10.0/pytest_asyncio/__init__.py000066400000000000000000000001201341476672100217550ustar00rootroot00000000000000"""The main point for importing pytest-asyncio items.""" __version__ = "0.10.0" pytest-asyncio-0.10.0/pytest_asyncio/plugin.py000066400000000000000000000167641341476672100215410ustar00rootroot00000000000000"""pytest-asyncio implementation.""" import asyncio import contextlib import functools import inspect import socket import pytest try: from _pytest.python import transfer_markers except ImportError: # Pytest 4.1.0 removes the transfer_marker api (#104) def transfer_markers(*args, **kwargs): # noqa """Noop when over pytest 4.1.0""" pass try: from async_generator import isasyncgenfunction except ImportError: from inspect import isasyncgenfunction def _is_coroutine(obj): """Check to see if an object is really an asyncio coroutine.""" return asyncio.iscoroutinefunction(obj) or inspect.isgeneratorfunction(obj) def pytest_configure(config): """Inject documentation.""" config.addinivalue_line("markers", "asyncio: " "mark the test as a coroutine, it will be " "run using an asyncio event loop") @pytest.mark.tryfirst def pytest_pycollect_makeitem(collector, name, obj): """A pytest hook to collect asyncio coroutines.""" if collector.funcnamefilter(name) and _is_coroutine(obj): item = pytest.Function(name, parent=collector) # Due to how pytest test collection works, module-level pytestmarks # are applied after the collection step. Since this is the collection # step, we look ourselves. transfer_markers(obj, item.cls, item.module) item = pytest.Function(name, parent=collector) # To reload keywords. if 'asyncio' in item.keywords: return list(collector._genfunctions(name, obj)) @pytest.hookimpl(hookwrapper=True) def pytest_fixture_setup(fixturedef, request): """Adjust the event loop policy when an event loop is produced.""" if isasyncgenfunction(fixturedef.func): # This is an async generator function. Wrap it accordingly. f = fixturedef.func strip_event_loop = False if 'event_loop' not in fixturedef.argnames: fixturedef.argnames += ('event_loop', ) strip_event_loop = True strip_request = False if 'request' not in fixturedef.argnames: fixturedef.argnames += ('request', ) strip_request = True def wrapper(*args, **kwargs): loop = kwargs['event_loop'] request = kwargs['request'] if strip_event_loop: del kwargs['event_loop'] if strip_request: del kwargs['request'] gen_obj = f(*args, **kwargs) async def setup(): res = await gen_obj.__anext__() return res def finalizer(): """Yield again, to finalize.""" async def async_finalizer(): try: await gen_obj.__anext__() except StopAsyncIteration: pass else: msg = "Async generator fixture didn't stop." msg += "Yield only once." raise ValueError(msg) loop.run_until_complete(async_finalizer()) request.addfinalizer(finalizer) return loop.run_until_complete(setup()) fixturedef.func = wrapper elif inspect.iscoroutinefunction(fixturedef.func): # Just a coroutine, not an async generator. f = fixturedef.func strip_event_loop = False if 'event_loop' not in fixturedef.argnames: fixturedef.argnames += ('event_loop', ) strip_event_loop = True def wrapper(*args, **kwargs): loop = kwargs['event_loop'] if strip_event_loop: del kwargs['event_loop'] async def setup(): res = await f(*args, **kwargs) return res return loop.run_until_complete(setup()) fixturedef.func = wrapper outcome = yield if fixturedef.argname == "event_loop" and 'asyncio' in request.keywords: loop = outcome.get_result() for kw in _markers_2_fixtures.keys(): if kw not in request.keywords: continue policy = asyncio.get_event_loop_policy() try: old_loop = policy.get_event_loop() except RuntimeError as exc: if 'no current event loop' not in str(exc): raise old_loop = None policy.set_event_loop(loop) fixturedef.addfinalizer(lambda: policy.set_event_loop(old_loop)) @pytest.mark.tryfirst def pytest_pyfunc_call(pyfuncitem): """ Run asyncio marked test functions in an event loop instead of a normal function call. """ for marker_name, fixture_name in _markers_2_fixtures.items(): if marker_name in pyfuncitem.keywords \ and not getattr(pyfuncitem.obj, 'is_hypothesis_test', False): event_loop = pyfuncitem.funcargs[fixture_name] funcargs = pyfuncitem.funcargs testargs = {arg: funcargs[arg] for arg in pyfuncitem._fixtureinfo.argnames} event_loop.run_until_complete( asyncio.ensure_future( pyfuncitem.obj(**testargs), loop=event_loop)) return True def wrap_in_sync(func): """Return a sync wrapper around an async function.""" @functools.wraps(func) def inner(**kwargs): loop = asyncio.get_event_loop_policy().new_event_loop() try: coro = func(**kwargs) if coro is not None: future = asyncio.ensure_future(coro, loop=loop) loop.run_until_complete(future) finally: loop.close() return inner def pytest_runtest_setup(item): for marker, fixture in _markers_2_fixtures.items(): if marker in item.keywords and fixture not in item.fixturenames: # inject an event loop fixture for all async tests item.fixturenames.append(fixture) if item.get_closest_marker("asyncio") is not None: if hasattr(item.obj, 'hypothesis'): # If it's a Hypothesis test, we insert the wrap_in_sync decorator item.obj.hypothesis.inner_test = wrap_in_sync( item.obj.hypothesis.inner_test ) elif getattr(item.obj, 'is_hypothesis_test', False): pytest.fail( 'test function `%r` is using Hypothesis, but pytest-asyncio ' 'only works with Hypothesis 3.64.0 or later.' % item ) # maps marker to the name of the event loop fixture that will be available # to marked test functions _markers_2_fixtures = { 'asyncio': 'event_loop', } @pytest.yield_fixture def event_loop(request): """Create an instance of the default event loop for each test case.""" loop = asyncio.get_event_loop_policy().new_event_loop() yield loop loop.close() def _unused_tcp_port(): """Find an unused localhost TCP port from 1024-65535 and return it.""" with contextlib.closing(socket.socket()) as sock: sock.bind(('127.0.0.1', 0)) return sock.getsockname()[1] @pytest.fixture def unused_tcp_port(): return _unused_tcp_port() @pytest.fixture def unused_tcp_port_factory(): """A factory function, producing different unused TCP ports.""" produced = set() def factory(): """Return an unused port.""" port = _unused_tcp_port() while port in produced: port = _unused_tcp_port() produced.add(port) return port return factory pytest-asyncio-0.10.0/setup.cfg000066400000000000000000000003621341476672100164200ustar00rootroot00000000000000[coverage:run] source = pytest_asyncio [coverage:report] show_missing = true [tool:pytest] addopts = -rsx --tb=short testpaths = tests filterwarnings = error [metadata] # ensure LICENSE is included in wheel metadata license_file = LICENSE pytest-asyncio-0.10.0/setup.py000066400000000000000000000030621341476672100163110ustar00rootroot00000000000000import re from pathlib import Path from setuptools import setup, find_packages def find_version(): version_file = ( Path(__file__) .parent.joinpath("pytest_asyncio", "__init__.py") .read_text() ) version_match = re.search( r"^__version__ = ['\"]([^'\"]*)['\"]", version_file, re.M ) if version_match: return version_match.group(1) raise RuntimeError("Unable to find version string.") setup( name="pytest-asyncio", version=find_version(), packages=find_packages(), url="https://github.com/pytest-dev/pytest-asyncio", license="Apache 2.0", author="Tin Tvrtković", author_email="tinchester@gmail.com", description="Pytest support for asyncio.", long_description=Path(__file__).parent.joinpath("README.rst").read_text(), classifiers=[ "Development Status :: 4 - Beta", "Intended Audience :: Developers", "License :: OSI Approved :: Apache Software License", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Topic :: Software Development :: Testing", "Framework :: Pytest", ], python_requires=">= 3.5", install_requires=["pytest >= 3.0.6"], extras_require={ ':python_version == "3.5"': "async_generator >= 1.3", "testing": [ "coverage", "async_generator >= 1.3", "hypothesis >= 3.64", ], }, entry_points={"pytest11": ["asyncio = pytest_asyncio.plugin"]}, ) pytest-asyncio-0.10.0/tests/000077500000000000000000000000001341476672100157405ustar00rootroot00000000000000pytest-asyncio-0.10.0/tests/async_fixtures/000077500000000000000000000000001341476672100210065ustar00rootroot00000000000000pytest-asyncio-0.10.0/tests/async_fixtures/__init__.py000066400000000000000000000000001341476672100231050ustar00rootroot00000000000000pytest-asyncio-0.10.0/tests/async_fixtures/test_async_fixtures_35.py000066400000000000000000000007551341476672100260030ustar00rootroot00000000000000import asyncio import unittest.mock import pytest START = object() END = object() RETVAL = object() @pytest.fixture def mock(): return unittest.mock.Mock(return_value=RETVAL) @pytest.fixture async def async_fixture(mock): return await asyncio.sleep(0.1, result=mock(START)) @pytest.mark.asyncio async def test_async_fixture(async_fixture, mock): assert mock.call_count == 1 assert mock.call_args_list[-1] == unittest.mock.call(START) assert async_fixture is RETVAL pytest-asyncio-0.10.0/tests/async_fixtures/test_async_fixtures_scope.py000066400000000000000000000007511341476672100266610ustar00rootroot00000000000000""" We support module-scoped async fixtures, but only if the event loop is module-scoped too. """ import asyncio import pytest @pytest.fixture(scope='module') def event_loop(): """A module-scoped event loop.""" return asyncio.new_event_loop() @pytest.fixture(scope='module') async def async_fixture(): await asyncio.sleep(0.1) return 1 @pytest.mark.asyncio async def test_async_fixture_scope(async_fixture): assert async_fixture == 1 await asyncio.sleep(0.1) pytest-asyncio-0.10.0/tests/async_fixtures/test_async_gen_fixtures_35.py000066400000000000000000000015121341476672100266240ustar00rootroot00000000000000import unittest.mock import pytest from async_generator import yield_, async_generator START = object() END = object() RETVAL = object() @pytest.fixture(scope='module') def mock(): return unittest.mock.Mock(return_value=RETVAL) @pytest.fixture @async_generator async def async_gen_fixture(mock): try: await yield_(mock(START)) except Exception as e: mock(e) else: mock(END) @pytest.mark.asyncio async def test_async_gen_fixture(async_gen_fixture, mock): assert mock.called assert mock.call_args_list[-1] == unittest.mock.call(START) assert async_gen_fixture is RETVAL @pytest.mark.asyncio async def test_async_gen_fixture_finalized(mock): try: assert mock.called assert mock.call_args_list[-1] == unittest.mock.call(END) finally: mock.reset_mock() pytest-asyncio-0.10.0/tests/async_fixtures/test_async_gen_fixtures_36.py000066400000000000000000000014141341476672100266260ustar00rootroot00000000000000import asyncio import unittest.mock import pytest START = object() END = object() RETVAL = object() @pytest.fixture(scope='module') def mock(): return unittest.mock.Mock(return_value=RETVAL) @pytest.fixture async def async_gen_fixture(mock): try: yield mock(START) except Exception as e: mock(e) else: mock(END) @pytest.mark.asyncio async def test_async_gen_fixture(async_gen_fixture, mock): assert mock.called assert mock.call_args_list[-1] == unittest.mock.call(START) assert async_gen_fixture is RETVAL @pytest.mark.asyncio async def test_async_gen_fixture_finalized(mock): try: assert mock.called assert mock.call_args_list[-1] == unittest.mock.call(END) finally: mock.reset_mock() pytest-asyncio-0.10.0/tests/async_fixtures/test_coroutine_fixtures.py000066400000000000000000000011211341476672100263520ustar00rootroot00000000000000import asyncio import unittest.mock import pytest START = object() END = object() RETVAL = object() pytestmark = pytest.mark.skip(reason='@asyncio.coroutine fixtures are not supported yet') @pytest.fixture def mock(): return unittest.mock.Mock(return_value=RETVAL) @pytest.fixture async def coroutine_fixture(mock): await asyncio.sleep(0.1, result=mock(START)) @pytest.mark.asyncio async def test_coroutine_fixture(coroutine_fixture, mock): assert mock.call_count == 1 assert mock.call_args_list[-1] == unittest.mock.call(START) assert coroutine_fixture is RETVAL pytest-asyncio-0.10.0/tests/async_fixtures/test_nested_36.py000066400000000000000000000010321341476672100242050ustar00rootroot00000000000000import asyncio import pytest @pytest.fixture() async def async_inner_fixture(): await asyncio.sleep(0.01) print('inner start') yield True print('inner stop') @pytest.fixture() async def async_fixture_outer(async_inner_fixture, event_loop): await asyncio.sleep(0.01) print('outer start') assert async_inner_fixture is True yield True print('outer stop') @pytest.mark.asyncio async def test_async_fixture(async_fixture_outer): assert async_fixture_outer is True print('test_async_fixture') pytest-asyncio-0.10.0/tests/conftest.py000066400000000000000000000013161341476672100201400ustar00rootroot00000000000000import asyncio import sys import pytest collect_ignore = [] if sys.version_info[:2] < (3, 6): collect_ignore.append("async_fixtures/test_async_gen_fixtures_36.py") collect_ignore.append("async_fixtures/test_nested_36.py") @pytest.yield_fixture() def dependent_fixture(event_loop): """A fixture dependent on the event_loop fixture, doing some cleanup.""" counter = 0 async def just_a_sleep(): """Just sleep a little while.""" nonlocal event_loop await asyncio.sleep(0.1, loop=event_loop) nonlocal counter counter += 1 event_loop.run_until_complete(just_a_sleep()) yield event_loop.run_until_complete(just_a_sleep()) assert counter == 2 pytest-asyncio-0.10.0/tests/markers/000077500000000000000000000000001341476672100174045ustar00rootroot00000000000000pytest-asyncio-0.10.0/tests/markers/test_class_marker_35.py000066400000000000000000000010021341476672100237630ustar00rootroot00000000000000"""Test if pytestmark works when defined on a class.""" import asyncio import pytest class TestPyTestMark: pytestmark = pytest.mark.asyncio async def test_is_asyncio(self, event_loop, sample_fixture): assert asyncio.get_event_loop() counter = 1 async def inc(): nonlocal counter counter += 1 await asyncio.sleep(0) await asyncio.ensure_future(inc()) assert counter == 2 @pytest.fixture def sample_fixture(): return None pytest-asyncio-0.10.0/tests/markers/test_module_marker_35.py000066400000000000000000000014201341476672100241470ustar00rootroot00000000000000"""Test if pytestmark works when defined in a module.""" import asyncio import pytest pytestmark = pytest.mark.asyncio class TestPyTestMark: async def test_is_asyncio(self, event_loop, sample_fixture): assert asyncio.get_event_loop() counter = 1 async def inc(): nonlocal counter counter += 1 await asyncio.sleep(0) await asyncio.ensure_future(inc()) assert counter == 2 async def test_is_asyncio(event_loop, sample_fixture): assert asyncio.get_event_loop() counter = 1 async def inc(): nonlocal counter counter += 1 await asyncio.sleep(0) await asyncio.ensure_future(inc()) assert counter == 2 @pytest.fixture def sample_fixture(): return None pytest-asyncio-0.10.0/tests/multiloop/000077500000000000000000000000001341476672100177645ustar00rootroot00000000000000pytest-asyncio-0.10.0/tests/multiloop/conftest.py000066400000000000000000000005301341476672100221610ustar00rootroot00000000000000import asyncio import pytest class CustomSelectorLoop(asyncio.SelectorEventLoop): """A subclass with no overrides, just to test for presence.""" pass @pytest.yield_fixture() def event_loop(): """Create an instance of the default event loop for each test case.""" loop = CustomSelectorLoop() yield loop loop.close() pytest-asyncio-0.10.0/tests/multiloop/test_alternative_loops.py000066400000000000000000000006271341476672100251340ustar00rootroot00000000000000"""Unit tests for overriding the event loop.""" import asyncio import pytest @pytest.mark.asyncio async def test_for_custom_loop(): """This test should be executed using the custom loop.""" await asyncio.sleep(0.01) assert type(asyncio.get_event_loop()).__name__ == "CustomSelectorLoop" @pytest.mark.asyncio async def test_dependent_fixture(dependent_fixture): await asyncio.sleep(0.1) pytest-asyncio-0.10.0/tests/test_dependent_fixtures.py000066400000000000000000000002521341476672100232470ustar00rootroot00000000000000import asyncio import pytest @pytest.mark.asyncio async def test_dependent_fixture(dependent_fixture): """Test a dependent fixture.""" await asyncio.sleep(0.1) pytest-asyncio-0.10.0/tests/test_event_loop_scope_35.py000066400000000000000000000005331341476672100232240ustar00rootroot00000000000000"""Test the event loop fixture is properly disposed of. These tests need to be run together. """ import asyncio import pytest def test_1(): loop = asyncio.get_event_loop() assert not loop.is_closed() @pytest.mark.asyncio async def test_2(): pass def test_3(): loop = asyncio.get_event_loop() assert not loop.is_closed() pytest-asyncio-0.10.0/tests/test_hypothesis_integration.py000066400000000000000000000010411341476672100241470ustar00rootroot00000000000000"""Tests for the Hypothesis integration, which wraps async functions in a sync shim for Hypothesis. """ import pytest from hypothesis import given, strategies as st @given(st.integers()) @pytest.mark.asyncio async def test_mark_inner(n): assert isinstance(n, int) @pytest.mark.asyncio @given(st.integers()) async def test_mark_outer(n): assert isinstance(n, int) @pytest.mark.parametrize("y", [1, 2]) @given(x=st.none()) @pytest.mark.asyncio async def test_mark_and_parametrize(x, y): assert x is None assert y in (1, 2) pytest-asyncio-0.10.0/tests/test_simple.py000066400000000000000000000077541341476672100206570ustar00rootroot00000000000000"""Quick'n'dirty unit tests for provided fixtures and markers.""" import asyncio import os import pytest import pytest_asyncio.plugin async def async_coro(loop=None): """A very simple coroutine.""" await asyncio.sleep(0, loop=loop) return 'ok' def test_event_loop_fixture(event_loop): """Test the injection of the event_loop fixture.""" assert event_loop ret = event_loop.run_until_complete(async_coro(event_loop)) assert ret == 'ok' @pytest.mark.asyncio def test_asyncio_marker(): """Test the asyncio pytest marker.""" yield # sleep(0) @pytest.mark.xfail(reason='need a failure', strict=True) @pytest.mark.asyncio def test_asyncio_marker_fail(): assert False @pytest.mark.asyncio def test_asyncio_marker_with_default_param(a_param=None): """Test the asyncio pytest marker.""" yield # sleep(0) @pytest.mark.asyncio async def test_unused_port_fixture(unused_tcp_port, event_loop): """Test the unused TCP port fixture.""" async def closer(_, writer): writer.close() server1 = await asyncio.start_server(closer, host='localhost', port=unused_tcp_port, loop=event_loop) with pytest.raises(IOError): await asyncio.start_server(closer, host='localhost', port=unused_tcp_port, loop=event_loop) server1.close() await server1.wait_closed() @pytest.mark.asyncio async def test_unused_port_factory_fixture(unused_tcp_port_factory, event_loop): """Test the unused TCP port factory fixture.""" async def closer(_, writer): writer.close() port1, port2, port3 = (unused_tcp_port_factory(), unused_tcp_port_factory(), unused_tcp_port_factory()) server1 = await asyncio.start_server(closer, host='localhost', port=port1, loop=event_loop) server2 = await asyncio.start_server(closer, host='localhost', port=port2, loop=event_loop) server3 = await asyncio.start_server(closer, host='localhost', port=port3, loop=event_loop) for port in port1, port2, port3: with pytest.raises(IOError): await asyncio.start_server(closer, host='localhost', port=port, loop=event_loop) server1.close() await server1.wait_closed() server2.close() await server2.wait_closed() server3.close() await server3.wait_closed() def test_unused_port_factory_duplicate(unused_tcp_port_factory, monkeypatch): """Test correct avoidance of duplicate ports.""" counter = 0 def mock_unused_tcp_port(): """Force some duplicate ports.""" nonlocal counter counter += 1 if counter < 5: return 10000 else: return 10000 + counter monkeypatch.setattr(pytest_asyncio.plugin, '_unused_tcp_port', mock_unused_tcp_port) assert unused_tcp_port_factory() == 10000 assert unused_tcp_port_factory() > 10000 class Test: """Test that asyncio marked functions work in test methods.""" @pytest.mark.asyncio async def test_asyncio_marker_method(self, event_loop): """Test the asyncio pytest marker in a Test class.""" ret = await async_coro(event_loop) assert ret == 'ok' class TestUnexistingLoop: @pytest.fixture def remove_loop(self): old_loop = asyncio.get_event_loop() asyncio.set_event_loop(None) yield asyncio.set_event_loop(old_loop) @pytest.mark.asyncio async def test_asyncio_marker_without_loop(self, remove_loop): """Test the asyncio pytest marker in a Test class.""" ret = await async_coro() assert ret == 'ok' pytest-asyncio-0.10.0/tests/test_simple_35.py000066400000000000000000000052031341476672100211510ustar00rootroot00000000000000"""Quick'n'dirty unit tests using async and await syntax.""" import asyncio import pytest @pytest.mark.asyncio async def async_coro(loop): await asyncio.sleep(0, loop=loop) return 'ok' @pytest.mark.asyncio async def test_asyncio_marker(): """Test the asyncio pytest marker.""" @pytest.mark.asyncio async def test_asyncio_marker_with_default_param(a_param=None): """Test the asyncio pytest marker.""" @pytest.mark.asyncio async def test_unused_port_fixture(unused_tcp_port, event_loop): """Test the unused TCP port fixture.""" async def closer(_, writer): writer.close() server1 = await asyncio.start_server(closer, host='localhost', port=unused_tcp_port, loop=event_loop) server1.close() await server1.wait_closed() def test_unused_port_factory_fixture(unused_tcp_port_factory, event_loop): """Test the unused TCP port factory fixture.""" async def closer(_, writer): writer.close() port1, port2, port3 = (unused_tcp_port_factory(), unused_tcp_port_factory(), unused_tcp_port_factory()) async def run_test(): server1 = await asyncio.start_server(closer, host='localhost', port=port1, loop=event_loop) server2 = await asyncio.start_server(closer, host='localhost', port=port2, loop=event_loop) server3 = await asyncio.start_server(closer, host='localhost', port=port3, loop=event_loop) for port in port1, port2, port3: with pytest.raises(IOError): await asyncio.start_server(closer, host='localhost', port=port, loop=event_loop) server1.close() await server1.wait_closed() server2.close() await server2.wait_closed() server3.close() await server3.wait_closed() event_loop.run_until_complete(run_test()) event_loop.stop() event_loop.close() class Test: """Test that asyncio marked functions work in test methods.""" @pytest.mark.asyncio async def test_asyncio_marker_method(self, event_loop): """Test the asyncio pytest marker in a Test class.""" ret = await async_coro(event_loop) assert ret == 'ok' def test_async_close_loop(event_loop): event_loop.close() return 'ok' pytest-asyncio-0.10.0/tests/test_subprocess.py000066400000000000000000000013741341476672100215460ustar00rootroot00000000000000"""Tests for using subprocesses in tests.""" import sys import asyncio import asyncio.subprocess import pytest @pytest.mark.asyncio(forbid_global_loop=False) async def test_subprocess(event_loop): """Starting a subprocess should be possible.""" proc = await asyncio.subprocess.create_subprocess_exec( sys.executable, '--version', stdout=asyncio.subprocess.PIPE, loop=event_loop) await proc.communicate() @pytest.mark.asyncio(forbid_global_loop=True) async def test_subprocess_forbid(event_loop): """Starting a subprocess should be possible.""" proc = await asyncio.subprocess.create_subprocess_exec( sys.executable, '--version', stdout=asyncio.subprocess.PIPE, loop=event_loop) await proc.communicate() pytest-asyncio-0.10.0/tox.ini000066400000000000000000000003571341476672100161160ustar00rootroot00000000000000[tox] envlist = py35, py36, py37 minversion = 2.5.0 [testenv] extras = testing commands = coverage run -m pytest {posargs} [testenv:coverage-report] deps = coverage skip_install = true commands = coverage combine coverage report