summaryrefslogtreecommitdiffstats
path: root/contrib/python/pytest-asyncio
diff options
context:
space:
mode:
authorrobot-piglet <[email protected]>2024-10-06 13:42:43 +0300
committerrobot-piglet <[email protected]>2024-10-06 13:52:30 +0300
commit52aed29f744afda4549ef5d64acd0fa8c2092789 (patch)
treee40c9abd25653990d13b68936aee518454df424e /contrib/python/pytest-asyncio
parent813943fcad905eee1235d764be4268dddd07ce64 (diff)
Intermediate changes
commit_hash:cc4365f5a0e443b92d87079a9c91e77fea2ddcaf
Diffstat (limited to 'contrib/python/pytest-asyncio')
-rw-r--r--contrib/python/pytest-asyncio/.dist-info/METADATA91
-rw-r--r--contrib/python/pytest-asyncio/.dist-info/entry_points.txt2
-rw-r--r--contrib/python/pytest-asyncio/.dist-info/top_level.txt1
-rw-r--r--contrib/python/pytest-asyncio/LICENSE201
-rw-r--r--contrib/python/pytest-asyncio/README.rst52
-rw-r--r--contrib/python/pytest-asyncio/pytest_asyncio/__init__.py5
-rw-r--r--contrib/python/pytest-asyncio/pytest_asyncio/_version.py4
-rw-r--r--contrib/python/pytest-asyncio/pytest_asyncio/plugin.py624
-rw-r--r--contrib/python/pytest-asyncio/pytest_asyncio/py.typed0
-rw-r--r--contrib/python/pytest-asyncio/ya.make30
10 files changed, 1010 insertions, 0 deletions
diff --git a/contrib/python/pytest-asyncio/.dist-info/METADATA b/contrib/python/pytest-asyncio/.dist-info/METADATA
new file mode 100644
index 00000000000..c73b027ba51
--- /dev/null
+++ b/contrib/python/pytest-asyncio/.dist-info/METADATA
@@ -0,0 +1,91 @@
+Metadata-Version: 2.1
+Name: pytest-asyncio
+Version: 0.21.1
+Summary: Pytest support for asyncio
+Home-page: https://github.com/pytest-dev/pytest-asyncio
+Author: Tin Tvrtković <[email protected]>
+Author-email: [email protected]
+License: Apache 2.0
+Project-URL: Documentation, https://pytest-asyncio.readthedocs.io
+Project-URL: Changelog, https://pytest-asyncio.readthedocs.io/en/latest/reference/changelog.html
+Project-URL: Source Code, https://github.com/pytest-dev/pytest-asyncio
+Project-URL: Bug Tracker, https://github.com/pytest-dev/pytest-asyncio/issues
+Classifier: Development Status :: 4 - Beta
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: Apache Software License
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: 3.11
+Classifier: Topic :: Software Development :: Testing
+Classifier: Framework :: AsyncIO
+Classifier: Framework :: Pytest
+Classifier: Typing :: Typed
+Requires-Python: >=3.7
+Description-Content-Type: text/x-rst
+License-File: LICENSE
+Requires-Dist: pytest (>=7.0.0)
+Requires-Dist: typing-extensions (>=3.7.2) ; python_version < "3.8"
+Provides-Extra: docs
+Requires-Dist: sphinx (>=5.3) ; extra == 'docs'
+Requires-Dist: sphinx-rtd-theme (>=1.0) ; extra == 'docs'
+Provides-Extra: testing
+Requires-Dist: coverage (>=6.2) ; extra == 'testing'
+Requires-Dist: hypothesis (>=5.7.1) ; extra == 'testing'
+Requires-Dist: flaky (>=3.5.0) ; extra == 'testing'
+Requires-Dist: mypy (>=0.931) ; extra == 'testing'
+Requires-Dist: pytest-trio (>=0.7.0) ; extra == 'testing'
+
+pytest-asyncio
+==============
+
+.. image:: https://img.shields.io/pypi/v/pytest-asyncio.svg
+ :target: https://pypi.python.org/pypi/pytest-asyncio
+.. image:: https://github.com/pytest-dev/pytest-asyncio/workflows/CI/badge.svg
+ :target: https://github.com/pytest-dev/pytest-asyncio/actions?workflow=CI
+.. image:: https://codecov.io/gh/pytest-dev/pytest-asyncio/branch/main/graph/badge.svg
+ :target: https://codecov.io/gh/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
+.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
+ :target: https://github.com/ambv/black
+
+`pytest-asyncio <https://pytest-asyncio.readthedocs.io/en/latest/>`_ is a `pytest <https://docs.pytest.org/en/latest/contents.html>`_ plugin. It facilitates testing of code that uses the `asyncio <https://docs.python.org/3/library/asyncio.html>`_ library.
+
+Specifically, pytest-asyncio provides support for coroutines as test functions. This allows users to *await* code inside their tests. For example, the following code is executed as a test item by pytest:
+
+.. code-block:: python
+
+ @pytest.mark.asyncio
+ async def test_some_asyncio_code():
+ res = await library.do_something()
+ assert b"expected result" == res
+
+More details can be found in the `documentation <https://pytest-asyncio.readthedocs.io/en/latest/>`_.
+
+Note that test classes subclassing the standard `unittest <https://docs.python.org/3/library/unittest.html>`__ library are not supported. Users
+are advised to use `unittest.IsolatedAsyncioTestCase <https://docs.python.org/3/library/unittest.html#unittest.IsolatedAsyncioTestCase>`__
+or an async framework such as `asynctest <https://asynctest.readthedocs.io/en/latest>`__.
+
+
+pytest-asyncio is available under the `Apache License 2.0 <https://github.com/pytest-dev/pytest-asyncio/blob/main/LICENSE>`_.
+
+
+Installation
+------------
+
+To install pytest-asyncio, simply:
+
+.. code-block:: bash
+
+ $ pip install pytest-asyncio
+
+This is enough for pytest to pick up pytest-asyncio.
+
+
+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.
diff --git a/contrib/python/pytest-asyncio/.dist-info/entry_points.txt b/contrib/python/pytest-asyncio/.dist-info/entry_points.txt
new file mode 100644
index 00000000000..88db714dad5
--- /dev/null
+++ b/contrib/python/pytest-asyncio/.dist-info/entry_points.txt
@@ -0,0 +1,2 @@
+[pytest11]
+asyncio = pytest_asyncio.plugin
diff --git a/contrib/python/pytest-asyncio/.dist-info/top_level.txt b/contrib/python/pytest-asyncio/.dist-info/top_level.txt
new file mode 100644
index 00000000000..08d05d1ecf4
--- /dev/null
+++ b/contrib/python/pytest-asyncio/.dist-info/top_level.txt
@@ -0,0 +1 @@
+pytest_asyncio
diff --git a/contrib/python/pytest-asyncio/LICENSE b/contrib/python/pytest-asyncio/LICENSE
new file mode 100644
index 00000000000..5c304d1a4a7
--- /dev/null
+++ b/contrib/python/pytest-asyncio/LICENSE
@@ -0,0 +1,201 @@
+Apache 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.
diff --git a/contrib/python/pytest-asyncio/README.rst b/contrib/python/pytest-asyncio/README.rst
new file mode 100644
index 00000000000..0682b744302
--- /dev/null
+++ b/contrib/python/pytest-asyncio/README.rst
@@ -0,0 +1,52 @@
+pytest-asyncio
+==============
+
+.. image:: https://img.shields.io/pypi/v/pytest-asyncio.svg
+ :target: https://pypi.python.org/pypi/pytest-asyncio
+.. image:: https://github.com/pytest-dev/pytest-asyncio/workflows/CI/badge.svg
+ :target: https://github.com/pytest-dev/pytest-asyncio/actions?workflow=CI
+.. image:: https://codecov.io/gh/pytest-dev/pytest-asyncio/branch/main/graph/badge.svg
+ :target: https://codecov.io/gh/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
+.. image:: https://img.shields.io/badge/code%20style-black-000000.svg
+ :target: https://github.com/ambv/black
+
+`pytest-asyncio <https://pytest-asyncio.readthedocs.io/en/latest/>`_ is a `pytest <https://docs.pytest.org/en/latest/contents.html>`_ plugin. It facilitates testing of code that uses the `asyncio <https://docs.python.org/3/library/asyncio.html>`_ library.
+
+Specifically, pytest-asyncio provides support for coroutines as test functions. This allows users to *await* code inside their tests. For example, the following code is executed as a test item by pytest:
+
+.. code-block:: python
+
+ @pytest.mark.asyncio
+ async def test_some_asyncio_code():
+ res = await library.do_something()
+ assert b"expected result" == res
+
+More details can be found in the `documentation <https://pytest-asyncio.readthedocs.io/en/latest/>`_.
+
+Note that test classes subclassing the standard `unittest <https://docs.python.org/3/library/unittest.html>`__ library are not supported. Users
+are advised to use `unittest.IsolatedAsyncioTestCase <https://docs.python.org/3/library/unittest.html#unittest.IsolatedAsyncioTestCase>`__
+or an async framework such as `asynctest <https://asynctest.readthedocs.io/en/latest>`__.
+
+
+pytest-asyncio is available under the `Apache License 2.0 <https://github.com/pytest-dev/pytest-asyncio/blob/main/LICENSE>`_.
+
+
+Installation
+------------
+
+To install pytest-asyncio, simply:
+
+.. code-block:: bash
+
+ $ pip install pytest-asyncio
+
+This is enough for pytest to pick up pytest-asyncio.
+
+
+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.
diff --git a/contrib/python/pytest-asyncio/pytest_asyncio/__init__.py b/contrib/python/pytest-asyncio/pytest_asyncio/__init__.py
new file mode 100644
index 00000000000..1bc2811d93f
--- /dev/null
+++ b/contrib/python/pytest-asyncio/pytest_asyncio/__init__.py
@@ -0,0 +1,5 @@
+"""The main point for importing pytest-asyncio items."""
+from ._version import version as __version__ # noqa
+from .plugin import fixture
+
+__all__ = ("fixture",)
diff --git a/contrib/python/pytest-asyncio/pytest_asyncio/_version.py b/contrib/python/pytest-asyncio/pytest_asyncio/_version.py
new file mode 100644
index 00000000000..11f23015fb4
--- /dev/null
+++ b/contrib/python/pytest-asyncio/pytest_asyncio/_version.py
@@ -0,0 +1,4 @@
+# file generated by setuptools_scm
+# don't change, don't track in version control
+__version__ = version = '0.21.1'
+__version_tuple__ = version_tuple = (0, 21, 1)
diff --git a/contrib/python/pytest-asyncio/pytest_asyncio/plugin.py b/contrib/python/pytest-asyncio/pytest_asyncio/plugin.py
new file mode 100644
index 00000000000..db93b851de8
--- /dev/null
+++ b/contrib/python/pytest-asyncio/pytest_asyncio/plugin.py
@@ -0,0 +1,624 @@
+"""pytest-asyncio implementation."""
+import asyncio
+import contextlib
+import enum
+import functools
+import inspect
+import socket
+import sys
+import warnings
+from textwrap import dedent
+from typing import (
+ Any,
+ AsyncIterator,
+ Awaitable,
+ Callable,
+ Dict,
+ Iterable,
+ Iterator,
+ List,
+ Optional,
+ Set,
+ TypeVar,
+ Union,
+ cast,
+ overload,
+)
+
+import pytest
+from pytest import (
+ Config,
+ FixtureRequest,
+ Function,
+ Item,
+ Parser,
+ PytestPluginManager,
+ Session,
+)
+
+if sys.version_info >= (3, 8):
+ from typing import Literal
+else:
+ from typing_extensions import Literal
+
+_R = TypeVar("_R")
+
+_ScopeName = Literal["session", "package", "module", "class", "function"]
+_T = TypeVar("_T")
+
+SimpleFixtureFunction = TypeVar(
+ "SimpleFixtureFunction", bound=Callable[..., Awaitable[_R]]
+)
+FactoryFixtureFunction = TypeVar(
+ "FactoryFixtureFunction", bound=Callable[..., AsyncIterator[_R]]
+)
+FixtureFunction = Union[SimpleFixtureFunction, FactoryFixtureFunction]
+FixtureFunctionMarker = Callable[[FixtureFunction], FixtureFunction]
+
+# https://github.com/pytest-dev/pytest/pull/9510
+FixtureDef = Any
+SubRequest = Any
+
+
+class Mode(str, enum.Enum):
+ AUTO = "auto"
+ STRICT = "strict"
+
+
+ASYNCIO_MODE_HELP = """\
+'auto' - for automatically handling all async functions by the plugin
+'strict' - for autoprocessing disabling (useful if different async frameworks \
+should be tested together, e.g. \
+both pytest-asyncio and pytest-trio are used in the same project)
+"""
+
+
+def pytest_addoption(parser: Parser, pluginmanager: PytestPluginManager) -> None:
+ group = parser.getgroup("asyncio")
+ group.addoption(
+ "--asyncio-mode",
+ dest="asyncio_mode",
+ default=None,
+ metavar="MODE",
+ help=ASYNCIO_MODE_HELP,
+ )
+ parser.addini(
+ "asyncio_mode",
+ help="default value for --asyncio-mode",
+ default="auto",
+ )
+
+
+@overload
+def fixture(
+ fixture_function: FixtureFunction,
+ *,
+ scope: "Union[_ScopeName, Callable[[str, Config], _ScopeName]]" = ...,
+ params: Optional[Iterable[object]] = ...,
+ autouse: bool = ...,
+ ids: Union[
+ Iterable[Union[str, float, int, bool, None]],
+ Callable[[Any], Optional[object]],
+ None,
+ ] = ...,
+ name: Optional[str] = ...,
+) -> FixtureFunction:
+ ...
+
+
+@overload
+def fixture(
+ fixture_function: None = ...,
+ *,
+ scope: "Union[_ScopeName, Callable[[str, Config], _ScopeName]]" = ...,
+ params: Optional[Iterable[object]] = ...,
+ autouse: bool = ...,
+ ids: Union[
+ Iterable[Union[str, float, int, bool, None]],
+ Callable[[Any], Optional[object]],
+ None,
+ ] = ...,
+ name: Optional[str] = None,
+) -> FixtureFunctionMarker:
+ ...
+
+
+def fixture(
+ fixture_function: Optional[FixtureFunction] = None, **kwargs: Any
+) -> Union[FixtureFunction, FixtureFunctionMarker]:
+ if fixture_function is not None:
+ _make_asyncio_fixture_function(fixture_function)
+ return pytest.fixture(fixture_function, **kwargs)
+
+ else:
+
+ @functools.wraps(fixture)
+ def inner(fixture_function: FixtureFunction) -> FixtureFunction:
+ return fixture(fixture_function, **kwargs)
+
+ return inner
+
+
+def _is_asyncio_fixture_function(obj: Any) -> bool:
+ obj = getattr(obj, "__func__", obj) # instance method maybe?
+ return getattr(obj, "_force_asyncio_fixture", False)
+
+
+def _make_asyncio_fixture_function(obj: Any) -> None:
+ if hasattr(obj, "__func__"):
+ # instance method, check the function object
+ obj = obj.__func__
+ obj._force_asyncio_fixture = True
+
+
+def _is_coroutine(obj: Any) -> bool:
+ """Check to see if an object is really an asyncio coroutine."""
+ return asyncio.iscoroutinefunction(obj)
+
+
+def _is_coroutine_or_asyncgen(obj: Any) -> bool:
+ return _is_coroutine(obj) or inspect.isasyncgenfunction(obj)
+
+
+def _get_asyncio_mode(config: Config) -> Mode:
+ val = config.getoption("asyncio_mode")
+ if val is None:
+ val = config.getini("asyncio_mode")
+ try:
+ return Mode(val)
+ except ValueError:
+ modes = ", ".join(m.value for m in Mode)
+ raise pytest.UsageError(
+ f"{val!r} is not a valid asyncio_mode. Valid modes: {modes}."
+ )
+
+
+def pytest_configure(config: Config) -> None:
+ """Inject documentation."""
+ config.addinivalue_line(
+ "markers",
+ "asyncio: "
+ "mark the test as a coroutine, it will be "
+ "run using an asyncio event loop",
+ )
+
+
[email protected](tryfirst=True)
+def pytest_report_header(config: Config) -> List[str]:
+ """Add asyncio config to pytest header."""
+ mode = _get_asyncio_mode(config)
+ return [f"asyncio: mode={mode}"]
+
+
+def _preprocess_async_fixtures(
+ config: Config,
+ processed_fixturedefs: Set[FixtureDef],
+) -> None:
+ asyncio_mode = _get_asyncio_mode(config)
+ fixturemanager = config.pluginmanager.get_plugin("funcmanage")
+ for fixtures in fixturemanager._arg2fixturedefs.values():
+ for fixturedef in fixtures:
+ func = fixturedef.func
+ if fixturedef in processed_fixturedefs or not _is_coroutine_or_asyncgen(
+ func
+ ):
+ continue
+ if not _is_asyncio_fixture_function(func) and asyncio_mode == Mode.STRICT:
+ # Ignore async fixtures without explicit asyncio mark in strict mode
+ # This applies to pytest_trio fixtures, for example
+ continue
+ _make_asyncio_fixture_function(func)
+ _inject_fixture_argnames(fixturedef)
+ _synchronize_async_fixture(fixturedef)
+ assert _is_asyncio_fixture_function(fixturedef.func)
+ processed_fixturedefs.add(fixturedef)
+
+
+def _inject_fixture_argnames(fixturedef: FixtureDef) -> None:
+ """
+ Ensures that `request` and `event_loop` are arguments of the specified fixture.
+ """
+ to_add = []
+ for name in ("request", "event_loop"):
+ if name not in fixturedef.argnames:
+ to_add.append(name)
+ if to_add:
+ fixturedef.argnames += tuple(to_add)
+
+
+def _synchronize_async_fixture(fixturedef: FixtureDef) -> None:
+ """
+ Wraps the fixture function of an async fixture in a synchronous function.
+ """
+ if inspect.isasyncgenfunction(fixturedef.func):
+ _wrap_asyncgen_fixture(fixturedef)
+ elif inspect.iscoroutinefunction(fixturedef.func):
+ _wrap_async_fixture(fixturedef)
+
+
+def _add_kwargs(
+ func: Callable[..., Any],
+ kwargs: Dict[str, Any],
+ event_loop: asyncio.AbstractEventLoop,
+ request: SubRequest,
+) -> Dict[str, Any]:
+ sig = inspect.signature(func)
+ ret = kwargs.copy()
+ if "request" in sig.parameters:
+ ret["request"] = request
+ if "event_loop" in sig.parameters:
+ ret["event_loop"] = event_loop
+ return ret
+
+
+def _perhaps_rebind_fixture_func(
+ func: _T, instance: Optional[Any], unittest: bool
+) -> _T:
+ if instance is not None:
+ # The fixture needs to be bound to the actual request.instance
+ # so it is bound to the same object as the test method.
+ unbound, cls = func, None
+ try:
+ unbound, cls = func.__func__, type(func.__self__) # type: ignore
+ except AttributeError:
+ pass
+ # If unittest is true, the fixture is bound unconditionally.
+ # otherwise, only if the fixture was bound before to an instance of
+ # the same type.
+ if unittest or (cls is not None and isinstance(instance, cls)):
+ func = unbound.__get__(instance) # type: ignore
+ return func
+
+
+def _wrap_asyncgen_fixture(fixturedef: FixtureDef) -> None:
+ fixture = fixturedef.func
+
+ @functools.wraps(fixture)
+ def _asyncgen_fixture_wrapper(
+ event_loop: asyncio.AbstractEventLoop, request: SubRequest, **kwargs: Any
+ ):
+ func = _perhaps_rebind_fixture_func(
+ fixture, request.instance, fixturedef.unittest
+ )
+ gen_obj = func(**_add_kwargs(func, kwargs, event_loop, request))
+
+ async def setup():
+ res = await gen_obj.__anext__()
+ return res
+
+ def finalizer() -> None:
+ """Yield again, to finalize."""
+
+ async def async_finalizer() -> None:
+ try:
+ await gen_obj.__anext__()
+ except StopAsyncIteration:
+ pass
+ else:
+ msg = "Async generator fixture didn't stop."
+ msg += "Yield only once."
+ raise ValueError(msg)
+
+ event_loop.run_until_complete(async_finalizer())
+
+ result = event_loop.run_until_complete(setup())
+ request.addfinalizer(finalizer)
+ return result
+
+ fixturedef.func = _asyncgen_fixture_wrapper
+
+
+def _wrap_async_fixture(fixturedef: FixtureDef) -> None:
+ fixture = fixturedef.func
+
+ @functools.wraps(fixture)
+ def _async_fixture_wrapper(
+ event_loop: asyncio.AbstractEventLoop, request: SubRequest, **kwargs: Any
+ ):
+ func = _perhaps_rebind_fixture_func(
+ fixture, request.instance, fixturedef.unittest
+ )
+
+ async def setup():
+ res = await func(**_add_kwargs(func, kwargs, event_loop, request))
+ return res
+
+ return event_loop.run_until_complete(setup())
+
+ fixturedef.func = _async_fixture_wrapper
+
+
+_HOLDER: Set[FixtureDef] = set()
+
+
[email protected](tryfirst=True)
+def pytest_pycollect_makeitem(
+ collector: Union[pytest.Module, pytest.Class], name: str, obj: object
+) -> Union[
+ pytest.Item, pytest.Collector, List[Union[pytest.Item, pytest.Collector]], None
+]:
+ """A pytest hook to collect asyncio coroutines."""
+ if not collector.funcnamefilter(name):
+ return None
+ _preprocess_async_fixtures(collector.config, _HOLDER)
+ return None
+
+
+def pytest_collection_modifyitems(
+ session: Session, config: Config, items: List[Item]
+) -> None:
+ """
+ Marks collected async test items as `asyncio` tests.
+
+ The mark is only applied in `AUTO` mode. It is applied to:
+
+ - coroutines
+ - staticmethods wrapping coroutines
+ - Hypothesis tests wrapping coroutines
+
+ """
+ if _get_asyncio_mode(config) != Mode.AUTO:
+ return
+ function_items = (item for item in items if isinstance(item, Function))
+ for function_item in function_items:
+ function = function_item.obj
+ if isinstance(function, staticmethod):
+ # staticmethods need to be unwrapped.
+ function = function.__func__
+ if (
+ _is_coroutine(function)
+ or _is_hypothesis_test(function)
+ and _hypothesis_test_wraps_coroutine(function)
+ ):
+ function_item.add_marker("asyncio")
+
+
+def _hypothesis_test_wraps_coroutine(function: Any) -> bool:
+ return _is_coroutine(function.hypothesis.inner_test)
+
+
[email protected](hookwrapper=True)
+def pytest_fixture_setup(
+ fixturedef: FixtureDef, request: SubRequest
+) -> Optional[object]:
+ """Adjust the event loop policy when an event loop is produced."""
+ if fixturedef.argname == "event_loop":
+ # The use of a fixture finalizer is preferred over the
+ # pytest_fixture_post_finalizer hook. The fixture finalizer is invoked once
+ # for each fixture, whereas the hook may be invoked multiple times for
+ # any specific fixture.
+ # see https://github.com/pytest-dev/pytest/issues/5848
+ _add_finalizers(
+ fixturedef,
+ _close_event_loop,
+ _provide_clean_event_loop,
+ )
+ outcome = yield
+ loop = outcome.get_result()
+ policy = asyncio.get_event_loop_policy()
+ try:
+ with warnings.catch_warnings():
+ warnings.simplefilter("ignore", DeprecationWarning)
+ old_loop = policy.get_event_loop()
+ if old_loop is not loop:
+ old_loop.close()
+ except RuntimeError:
+ # Either the current event loop has been set to None
+ # or the loop policy doesn't specify to create new loops
+ # or we're not in the main thread
+ pass
+ policy.set_event_loop(loop)
+ return
+
+ yield
+
+
+def _add_finalizers(fixturedef: FixtureDef, *finalizers: Callable[[], object]) -> None:
+ """
+ Regsiters the specified fixture finalizers in the fixture.
+
+ Finalizers need to specified in the exact order in which they should be invoked.
+
+ :param fixturedef: Fixture definition which finalizers should be added to
+ :param finalizers: Finalizers to be added
+ """
+ for finalizer in reversed(finalizers):
+ fixturedef.addfinalizer(finalizer)
+
+
+_UNCLOSED_EVENT_LOOP_WARNING = dedent(
+ """\
+ pytest-asyncio detected an unclosed event loop when tearing down the event_loop
+ fixture: %r
+ pytest-asyncio will close the event loop for you, but future versions of the
+ library will no longer do so. In order to ensure compatibility with future
+ versions, please make sure that:
+ 1. Any custom "event_loop" fixture properly closes the loop after yielding it
+ 2. The scopes of your custom "event_loop" fixtures do not overlap
+ 3. Your code does not modify the event loop in async fixtures or tests
+ """
+)
+
+
+def _close_event_loop() -> None:
+ policy = asyncio.get_event_loop_policy()
+ try:
+ loop = policy.get_event_loop()
+ except RuntimeError:
+ loop = None
+ if loop is not None:
+ if not loop.is_closed():
+ warnings.warn(
+ _UNCLOSED_EVENT_LOOP_WARNING % loop,
+ DeprecationWarning,
+ )
+ loop.close()
+
+
+def _provide_clean_event_loop() -> None:
+ # At this point, the event loop for the current thread is closed.
+ # When a user calls asyncio.get_event_loop(), they will get a closed loop.
+ # In order to avoid this side effect from pytest-asyncio, we need to replace
+ # the current loop with a fresh one.
+ # Note that we cannot set the loop to None, because get_event_loop only creates
+ # a new loop, when set_event_loop has not been called.
+ policy = asyncio.get_event_loop_policy()
+ new_loop = policy.new_event_loop()
+ policy.set_event_loop(new_loop)
+
+
[email protected](tryfirst=True, hookwrapper=True)
+def pytest_pyfunc_call(pyfuncitem: pytest.Function) -> Optional[object]:
+ """
+ Pytest hook called before a test case is run.
+
+ Wraps marked tests in a synchronous function
+ where the wrapped test coroutine is executed in an event loop.
+ """
+ marker = pyfuncitem.get_closest_marker("asyncio")
+ if marker is not None:
+ funcargs: Dict[str, object] = pyfuncitem.funcargs # type: ignore[name-defined]
+ loop = cast(asyncio.AbstractEventLoop, funcargs["event_loop"])
+ if _is_hypothesis_test(pyfuncitem.obj):
+ pyfuncitem.obj.hypothesis.inner_test = wrap_in_sync(
+ pyfuncitem,
+ pyfuncitem.obj.hypothesis.inner_test,
+ _loop=loop,
+ )
+ else:
+ pyfuncitem.obj = wrap_in_sync(
+ pyfuncitem,
+ pyfuncitem.obj,
+ _loop=loop,
+ )
+ yield
+
+
+def _is_hypothesis_test(function: Any) -> bool:
+ return getattr(function, "is_hypothesis_test", False)
+
+
+def wrap_in_sync(
+ pyfuncitem: pytest.Function,
+ func: Callable[..., Awaitable[Any]],
+ _loop: asyncio.AbstractEventLoop,
+):
+ """Return a sync wrapper around an async function executing it in the
+ current event loop."""
+
+ # if the function is already wrapped, we rewrap using the original one
+ # not using __wrapped__ because the original function may already be
+ # a wrapped one
+ raw_func = getattr(func, "_raw_test_func", None)
+ if raw_func is not None:
+ func = raw_func
+
+ @functools.wraps(func)
+ def inner(*args, **kwargs):
+ coro = func(*args, **kwargs)
+ if not inspect.isawaitable(coro):
+ pyfuncitem.warn(
+ pytest.PytestWarning(
+ f"The test {pyfuncitem} is marked with '@pytest.mark.asyncio' "
+ "but it is not an async function. "
+ "Please remove asyncio marker. "
+ "If the test is not marked explicitly, "
+ "check for global markers applied via 'pytestmark'."
+ )
+ )
+ return
+ task = asyncio.ensure_future(coro, loop=_loop)
+ try:
+ return _loop.run_until_complete(task)
+ except BaseException:
+ # run_until_complete doesn't get the result from exceptions
+ # that are not subclasses of `Exception`. Consume all
+ # exceptions to prevent asyncio's warning from logging.
+ if task.done() and not task.cancelled():
+ task.exception()
+ raise
+
+ inner._raw_test_func = func # type: ignore[attr-defined]
+ return inner
+
+
+def pytest_runtest_setup(item: pytest.Item) -> None:
+ marker = item.get_closest_marker("asyncio")
+ if marker is None:
+ return
+ fixturenames = item.fixturenames # type: ignore[attr-defined]
+ # inject an event loop fixture for all async tests
+ if "event_loop" in fixturenames:
+ fixturenames.remove("event_loop")
+ fixturenames.insert(0, "event_loop")
+ obj = getattr(item, "obj", None)
+ if not getattr(obj, "hypothesis", False) and getattr(
+ 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
+ )
+
+
+def event_loop(request: FixtureRequest) -> Iterator[asyncio.AbstractEventLoop]:
+ """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_port(socket_type: int) -> int:
+ """Find an unused localhost port from 1024-65535 and return it."""
+ with contextlib.closing(socket.socket(type=socket_type)) as sock:
+ sock.bind(("127.0.0.1", 0))
+ return sock.getsockname()[1]
+
+
+def unused_tcp_port() -> int:
+ return _unused_port(socket.SOCK_STREAM)
+
+
+def unused_udp_port() -> int:
+ return _unused_port(socket.SOCK_DGRAM)
+
+
[email protected](scope="session")
+def unused_tcp_port_factory() -> Callable[[], int]:
+ """A factory function, producing different unused TCP ports."""
+ produced = set()
+
+ def factory():
+ """Return an unused port."""
+ port = _unused_port(socket.SOCK_STREAM)
+
+ while port in produced:
+ port = _unused_port(socket.SOCK_STREAM)
+
+ produced.add(port)
+
+ return port
+
+ return factory
+
+
[email protected](scope="session")
+def unused_udp_port_factory() -> Callable[[], int]:
+ """A factory function, producing different unused UDP ports."""
+ produced = set()
+
+ def factory():
+ """Return an unused port."""
+ port = _unused_port(socket.SOCK_DGRAM)
+
+ while port in produced:
+ port = _unused_port(socket.SOCK_DGRAM)
+
+ produced.add(port)
+
+ return port
+
+ return factory
diff --git a/contrib/python/pytest-asyncio/pytest_asyncio/py.typed b/contrib/python/pytest-asyncio/pytest_asyncio/py.typed
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/contrib/python/pytest-asyncio/pytest_asyncio/py.typed
diff --git a/contrib/python/pytest-asyncio/ya.make b/contrib/python/pytest-asyncio/ya.make
new file mode 100644
index 00000000000..e3918ea4b02
--- /dev/null
+++ b/contrib/python/pytest-asyncio/ya.make
@@ -0,0 +1,30 @@
+# Generated by devtools/yamaker (pypi).
+
+PY3_LIBRARY()
+
+VERSION(0.21.1)
+
+LICENSE(Apache-2.0)
+
+PEERDIR(
+ contrib/python/pytest
+)
+
+NO_LINT()
+
+PY_SRCS(
+ TOP_LEVEL
+ pytest_asyncio/__init__.py
+ pytest_asyncio/_version.py
+ pytest_asyncio/plugin.py
+)
+
+RESOURCE_FILES(
+ PREFIX contrib/python/pytest-asyncio/
+ .dist-info/METADATA
+ .dist-info/entry_points.txt
+ .dist-info/top_level.txt
+ pytest_asyncio/py.typed
+)
+
+END()