commit
						0f11a7a73d
					
				|  | @ -1,6 +1,6 @@ | ||||||
| exclude: doc/en/example/py2py3/test_py2.py | exclude: doc/en/example/py2py3/test_py2.py | ||||||
| repos: | repos: | ||||||
| -   repo: https://github.com/python/black | -   repo: https://github.com/psf/black | ||||||
|     rev: 19.3b0 |     rev: 19.3b0 | ||||||
|     hooks: |     hooks: | ||||||
|     -   id: black |     -   id: black | ||||||
|  |  | ||||||
|  | @ -167,7 +167,7 @@ Short version | ||||||
| #. Enable and install `pre-commit <https://pre-commit.com>`_ to ensure style-guides and code checks are followed. | #. Enable and install `pre-commit <https://pre-commit.com>`_ to ensure style-guides and code checks are followed. | ||||||
| #. Target ``master`` for bugfixes and doc changes. | #. Target ``master`` for bugfixes and doc changes. | ||||||
| #. Target ``features`` for new features or functionality changes. | #. Target ``features`` for new features or functionality changes. | ||||||
| #. Follow **PEP-8** for naming and `black <https://github.com/python/black>`_ for formatting. | #. Follow **PEP-8** for naming and `black <https://github.com/psf/black>`_ for formatting. | ||||||
| #. Tests are run using ``tox``:: | #. Tests are run using ``tox``:: | ||||||
| 
 | 
 | ||||||
|     tox -e linting,py37 |     tox -e linting,py37 | ||||||
|  |  | ||||||
|  | @ -26,7 +26,7 @@ | ||||||
|     :target: https://dev.azure.com/pytest-dev/pytest |     :target: https://dev.azure.com/pytest-dev/pytest | ||||||
| 
 | 
 | ||||||
| .. image:: https://img.shields.io/badge/code%20style-black-000000.svg | .. image:: https://img.shields.io/badge/code%20style-black-000000.svg | ||||||
|     :target: https://github.com/python/black |     :target: https://github.com/psf/black | ||||||
| 
 | 
 | ||||||
| .. image:: https://www.codetriage.com/pytest-dev/pytest/badges/users.svg | .. image:: https://www.codetriage.com/pytest-dev/pytest/badges/users.svg | ||||||
|     :target: https://www.codetriage.com/pytest-dev/pytest |     :target: https://www.codetriage.com/pytest-dev/pytest | ||||||
|  |  | ||||||
|  | @ -0,0 +1 @@ | ||||||
|  | Warnings issued during ``pytest_configure`` are explicitly not treated as errors, even if configured as such, because it otherwise completely breaks pytest. | ||||||
|  | @ -0,0 +1 @@ | ||||||
|  | Add docstring for ``Testdir.copy_example``. | ||||||
|  | @ -0,0 +1 @@ | ||||||
|  | Fix collection of ``staticmethod`` objects defined with ``functools.partial``. | ||||||
|  | @ -0,0 +1 @@ | ||||||
|  | Skip async generator test functions, and update the warning message to refer to ``async def`` functions. | ||||||
|  | @ -57,7 +57,7 @@ is that you can use print statements for debugging: | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     def setup_function(function): |     def setup_function(function): | ||||||
|         print("setting up %s" % function) |         print("setting up", function) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     def test_func1(): |     def test_func1(): | ||||||
|  |  | ||||||
|  | @ -177,7 +177,7 @@ class TestRaises: | ||||||
| 
 | 
 | ||||||
|     def test_reinterpret_fails_with_print_for_the_fun_of_it(self): |     def test_reinterpret_fails_with_print_for_the_fun_of_it(self): | ||||||
|         items = [1, 2, 3] |         items = [1, 2, 3] | ||||||
|         print("items is %r" % items) |         print("items is {!r}".format(items)) | ||||||
|         a, b = items.pop() |         a, b = items.pop() | ||||||
| 
 | 
 | ||||||
|     def test_some_error(self): |     def test_some_error(self): | ||||||
|  |  | ||||||
|  | @ -384,7 +384,7 @@ specifies via named environments: | ||||||
|         envnames = [mark.args[0] for mark in item.iter_markers(name="env")] |         envnames = [mark.args[0] for mark in item.iter_markers(name="env")] | ||||||
|         if envnames: |         if envnames: | ||||||
|             if item.config.getoption("-E") not in envnames: |             if item.config.getoption("-E") not in envnames: | ||||||
|                 pytest.skip("test requires env in %r" % envnames) |                 pytest.skip("test requires env in {!r}".format(envnames)) | ||||||
| 
 | 
 | ||||||
| A test file using this local plugin: | A test file using this local plugin: | ||||||
| 
 | 
 | ||||||
|  | @ -578,7 +578,7 @@ for your particular platform, you could use the following plugin: | ||||||
|         supported_platforms = ALL.intersection(mark.name for mark in item.iter_markers()) |         supported_platforms = ALL.intersection(mark.name for mark in item.iter_markers()) | ||||||
|         plat = sys.platform |         plat = sys.platform | ||||||
|         if supported_platforms and plat not in supported_platforms: |         if supported_platforms and plat not in supported_platforms: | ||||||
|             pytest.skip("cannot run on platform %s" % (plat)) |             pytest.skip("cannot run on platform {}".format(plat)) | ||||||
| 
 | 
 | ||||||
| then tests will be skipped if they were specified for a different platform. | then tests will be skipped if they were specified for a different platform. | ||||||
| Let's do a little test file to show how this looks like: | Let's do a little test file to show how this looks like: | ||||||
|  |  | ||||||
|  | @ -69,4 +69,4 @@ class Python: | ||||||
| @pytest.mark.parametrize("obj", [42, {}, {1: 3}]) | @pytest.mark.parametrize("obj", [42, {}, {1: 3}]) | ||||||
| def test_basic_objects(python1, python2, obj): | def test_basic_objects(python1, python2, obj): | ||||||
|     python1.dumps(obj) |     python1.dumps(obj) | ||||||
|     python2.load_and_is_true("obj == %s" % obj) |     python2.load_and_is_true("obj == {}".format(obj)) | ||||||
|  |  | ||||||
|  | @ -33,13 +33,13 @@ class YamlItem(pytest.Item): | ||||||
|             return "\n".join( |             return "\n".join( | ||||||
|                 [ |                 [ | ||||||
|                     "usecase execution failed", |                     "usecase execution failed", | ||||||
|                     "   spec failed: %r: %r" % excinfo.value.args[1:3], |                     "   spec failed: {1!r}: {2!r}".format(*excinfo.value.args), | ||||||
|                     "   no further details known at this point.", |                     "   no further details known at this point.", | ||||||
|                 ] |                 ] | ||||||
|             ) |             ) | ||||||
| 
 | 
 | ||||||
|     def reportinfo(self): |     def reportinfo(self): | ||||||
|         return self.fspath, 0, "usecase: %s" % self.name |         return self.fspath, 0, "usecase: {}".format(self.name) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class YamlException(Exception): | class YamlException(Exception): | ||||||
|  |  | ||||||
|  | @ -434,7 +434,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: | ||||||
| 
 | 
 | ||||||
|         def test_reinterpret_fails_with_print_for_the_fun_of_it(self): |         def test_reinterpret_fails_with_print_for_the_fun_of_it(self): | ||||||
|             items = [1, 2, 3] |             items = [1, 2, 3] | ||||||
|             print("items is %r" % items) |             print("items is {!r}".format(items)) | ||||||
|     >       a, b = items.pop() |     >       a, b = items.pop() | ||||||
|     E       TypeError: 'int' object is not iterable |     E       TypeError: 'int' object is not iterable | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -478,7 +478,7 @@ an ``incremental`` marker which is to be used on classes: | ||||||
|         if "incremental" in item.keywords: |         if "incremental" in item.keywords: | ||||||
|             previousfailed = getattr(item.parent, "_previousfailed", None) |             previousfailed = getattr(item.parent, "_previousfailed", None) | ||||||
|             if previousfailed is not None: |             if previousfailed is not None: | ||||||
|                 pytest.xfail("previous test failed (%s)" % previousfailed.name) |                 pytest.xfail("previous test failed ({})".format(previousfailed.name)) | ||||||
| 
 | 
 | ||||||
| These two hook implementations work together to abort incremental-marked | These two hook implementations work together to abort incremental-marked | ||||||
| tests in a class.  Here is a test module example: | tests in a class.  Here is a test module example: | ||||||
|  | @ -684,7 +684,7 @@ case we just write some information out to a ``failures`` file: | ||||||
|             with open("failures", mode) as f: |             with open("failures", mode) as f: | ||||||
|                 # let's also access a fixture for the fun of it |                 # let's also access a fixture for the fun of it | ||||||
|                 if "tmpdir" in item.fixturenames: |                 if "tmpdir" in item.fixturenames: | ||||||
|                     extra = " (%s)" % item.funcargs["tmpdir"] |                     extra = " ({})".format(item.funcargs["tmpdir"]) | ||||||
|                 else: |                 else: | ||||||
|                     extra = "" |                     extra = "" | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -629,7 +629,7 @@ through the special :py:class:`request <FixtureRequest>` object: | ||||||
|     def smtp_connection(request): |     def smtp_connection(request): | ||||||
|         smtp_connection = smtplib.SMTP(request.param, 587, timeout=5) |         smtp_connection = smtplib.SMTP(request.param, 587, timeout=5) | ||||||
|         yield smtp_connection |         yield smtp_connection | ||||||
|         print("finalizing %s" % smtp_connection) |         print("finalizing {}".format(smtp_connection)) | ||||||
|         smtp_connection.close() |         smtp_connection.close() | ||||||
| 
 | 
 | ||||||
| The main change is the declaration of ``params`` with | The main change is the declaration of ``params`` with | ||||||
|  | @ -902,25 +902,25 @@ to show the setup/teardown flow: | ||||||
|     @pytest.fixture(scope="module", params=["mod1", "mod2"]) |     @pytest.fixture(scope="module", params=["mod1", "mod2"]) | ||||||
|     def modarg(request): |     def modarg(request): | ||||||
|         param = request.param |         param = request.param | ||||||
|         print("  SETUP modarg %s" % param) |         print("  SETUP modarg", param) | ||||||
|         yield param |         yield param | ||||||
|         print("  TEARDOWN modarg %s" % param) |         print("  TEARDOWN modarg", param) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     @pytest.fixture(scope="function", params=[1, 2]) |     @pytest.fixture(scope="function", params=[1, 2]) | ||||||
|     def otherarg(request): |     def otherarg(request): | ||||||
|         param = request.param |         param = request.param | ||||||
|         print("  SETUP otherarg %s" % param) |         print("  SETUP otherarg", param) | ||||||
|         yield param |         yield param | ||||||
|         print("  TEARDOWN otherarg %s" % param) |         print("  TEARDOWN otherarg", param) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     def test_0(otherarg): |     def test_0(otherarg): | ||||||
|         print("  RUN test0 with otherarg %s" % otherarg) |         print("  RUN test0 with otherarg", otherarg) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     def test_1(modarg): |     def test_1(modarg): | ||||||
|         print("  RUN test1 with modarg %s" % modarg) |         print("  RUN test1 with modarg", modarg) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|     def test_2(otherarg, modarg): |     def test_2(otherarg, modarg): | ||||||
|  |  | ||||||
|  | @ -28,7 +28,7 @@ Install ``pytest`` | ||||||
| .. code-block:: bash | .. code-block:: bash | ||||||
| 
 | 
 | ||||||
|     $ pytest --version |     $ pytest --version | ||||||
|     This is pytest version 5.x.y, imported from $PYTHON_PREFIX/lib/python3.6/site-packages/pytest.py |     This is pytest version 5.x.y, imported from $PYTHON_PREFIX/lib/python3.x/site-packages/pytest.py | ||||||
| 
 | 
 | ||||||
| .. _`simpletest`: | .. _`simpletest`: | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -27,6 +27,8 @@ pytest.skip | ||||||
| 
 | 
 | ||||||
| .. autofunction:: _pytest.outcomes.skip(msg, [allow_module_level=False]) | .. autofunction:: _pytest.outcomes.skip(msg, [allow_module_level=False]) | ||||||
| 
 | 
 | ||||||
|  | .. _`pytest.importorskip ref`: | ||||||
|  | 
 | ||||||
| pytest.importorskip | pytest.importorskip | ||||||
| ~~~~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~~~ | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -179,16 +179,15 @@ information. | ||||||
| Skipping on a missing import dependency | Skipping on a missing import dependency | ||||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||||
| 
 | 
 | ||||||
| You can use the following helper at module level | You can skip tests on a missing import by using :ref:`pytest.importorskip ref` | ||||||
| or within a test or test setup function: | at module level, within a test, or test setup function. | ||||||
| 
 | 
 | ||||||
| .. code-block:: python | .. code-block:: python | ||||||
| 
 | 
 | ||||||
|     docutils = pytest.importorskip("docutils") |     docutils = pytest.importorskip("docutils") | ||||||
| 
 | 
 | ||||||
| If ``docutils`` cannot be imported here, this will lead to a | If ``docutils`` cannot be imported here, this will lead to a skip outcome of | ||||||
| skip outcome of the test.  You can also skip based on the | the test. You can also skip based on the version number of a library: | ||||||
| version number of a library: |  | ||||||
| 
 | 
 | ||||||
| .. code-block:: python | .. code-block:: python | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -46,14 +46,16 @@ def is_generator(func): | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def iscoroutinefunction(func): | def iscoroutinefunction(func): | ||||||
|     """Return True if func is a decorated coroutine function. |  | ||||||
| 
 |  | ||||||
|     Note: copied and modified from Python 3.5's builtin couroutines.py to avoid import asyncio directly, |  | ||||||
|     which in turns also initializes the "logging" module as side-effect (see issue #8). |  | ||||||
|     """ |     """ | ||||||
|     return getattr(func, "_is_coroutine", False) or ( |     Return True if func is a coroutine function (a function defined with async | ||||||
|         hasattr(inspect, "iscoroutinefunction") and inspect.iscoroutinefunction(func) |     def syntax, and doesn't contain yield), or a function decorated with | ||||||
|     ) |     @asyncio.coroutine. | ||||||
|  | 
 | ||||||
|  |     Note: copied and modified from Python 3.5's builtin couroutines.py to avoid | ||||||
|  |     importing asyncio directly, which in turns also initializes the "logging" | ||||||
|  |     module as a side-effect (see issue #8). | ||||||
|  |     """ | ||||||
|  |     return inspect.iscoroutinefunction(func) or getattr(func, "_is_coroutine", False) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def getlocation(function, curdir=None): | def getlocation(function, curdir=None): | ||||||
|  | @ -84,7 +86,7 @@ def num_mock_patch_args(function): | ||||||
|     ) |     ) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def getfuncargnames(function, is_method=False, cls=None): | def getfuncargnames(function, *, name: str = "", is_method=False, cls=None): | ||||||
|     """Returns the names of a function's mandatory arguments. |     """Returns the names of a function's mandatory arguments. | ||||||
| 
 | 
 | ||||||
|     This should return the names of all function arguments that: |     This should return the names of all function arguments that: | ||||||
|  | @ -97,11 +99,12 @@ def getfuncargnames(function, is_method=False, cls=None): | ||||||
|     be treated as a bound method even though it's not unless, only in |     be treated as a bound method even though it's not unless, only in | ||||||
|     the case of cls, the function is a static method. |     the case of cls, the function is a static method. | ||||||
| 
 | 
 | ||||||
|  |     The name parameter should be the original name in which the function was collected. | ||||||
|  | 
 | ||||||
|     @RonnyPfannschmidt: This function should be refactored when we |     @RonnyPfannschmidt: This function should be refactored when we | ||||||
|     revisit fixtures. The fixture mechanism should ask the node for |     revisit fixtures. The fixture mechanism should ask the node for | ||||||
|     the fixture names, and not try to obtain directly from the |     the fixture names, and not try to obtain directly from the | ||||||
|     function object well after collection has occurred. |     function object well after collection has occurred. | ||||||
| 
 |  | ||||||
|     """ |     """ | ||||||
|     # The parameters attribute of a Signature object contains an |     # The parameters attribute of a Signature object contains an | ||||||
|     # ordered mapping of parameter names to Parameter instances.  This |     # ordered mapping of parameter names to Parameter instances.  This | ||||||
|  | @ -124,11 +127,14 @@ def getfuncargnames(function, is_method=False, cls=None): | ||||||
|         ) |         ) | ||||||
|         and p.default is Parameter.empty |         and p.default is Parameter.empty | ||||||
|     ) |     ) | ||||||
|  |     if not name: | ||||||
|  |         name = function.__name__ | ||||||
|  | 
 | ||||||
|     # If this function should be treated as a bound method even though |     # If this function should be treated as a bound method even though | ||||||
|     # it's passed as an unbound method or function, remove the first |     # it's passed as an unbound method or function, remove the first | ||||||
|     # parameter name. |     # parameter name. | ||||||
|     if is_method or ( |     if is_method or ( | ||||||
|         cls and not isinstance(cls.__dict__.get(function.__name__, None), staticmethod) |         cls and not isinstance(cls.__dict__.get(name, None), staticmethod) | ||||||
|     ): |     ): | ||||||
|         arg_names = arg_names[1:] |         arg_names = arg_names[1:] | ||||||
|     # Remove any names that will be replaced with mocks. |     # Remove any names that will be replaced with mocks. | ||||||
|  | @ -251,7 +257,7 @@ def get_real_method(obj, holder): | ||||||
|     try: |     try: | ||||||
|         is_method = hasattr(obj, "__func__") |         is_method = hasattr(obj, "__func__") | ||||||
|         obj = get_real_func(obj) |         obj = get_real_func(obj) | ||||||
|     except Exception: |     except Exception:  # pragma: no cover | ||||||
|         return obj |         return obj | ||||||
|     if is_method and hasattr(obj, "__get__") and callable(obj.__get__): |     if is_method and hasattr(obj, "__get__") and callable(obj.__get__): | ||||||
|         obj = obj.__get__(holder) |         obj = obj.__get__(holder) | ||||||
|  |  | ||||||
|  | @ -695,7 +695,9 @@ class Config: | ||||||
|     def _do_configure(self): |     def _do_configure(self): | ||||||
|         assert not self._configured |         assert not self._configured | ||||||
|         self._configured = True |         self._configured = True | ||||||
|         self.hook.pytest_configure.call_historic(kwargs=dict(config=self)) |         with warnings.catch_warnings(): | ||||||
|  |             warnings.simplefilter("default") | ||||||
|  |             self.hook.pytest_configure.call_historic(kwargs=dict(config=self)) | ||||||
| 
 | 
 | ||||||
|     def _ensure_unconfigure(self): |     def _ensure_unconfigure(self): | ||||||
|         if self._configured: |         if self._configured: | ||||||
|  |  | ||||||
|  | @ -818,7 +818,7 @@ class FixtureDef: | ||||||
|             where=baseid, |             where=baseid, | ||||||
|         ) |         ) | ||||||
|         self.params = params |         self.params = params | ||||||
|         self.argnames = getfuncargnames(func, is_method=unittest) |         self.argnames = getfuncargnames(func, name=argname, is_method=unittest) | ||||||
|         self.unittest = unittest |         self.unittest = unittest | ||||||
|         self.ids = ids |         self.ids = ids | ||||||
|         self._finalizers = [] |         self._finalizers = [] | ||||||
|  | @ -1142,7 +1142,7 @@ class FixtureManager: | ||||||
| 
 | 
 | ||||||
|     def getfixtureinfo(self, node, func, cls, funcargs=True): |     def getfixtureinfo(self, node, func, cls, funcargs=True): | ||||||
|         if funcargs and not getattr(node, "nofuncargs", False): |         if funcargs and not getattr(node, "nofuncargs", False): | ||||||
|             argnames = getfuncargnames(func, cls=cls) |             argnames = getfuncargnames(func, name=node.name, cls=cls) | ||||||
|         else: |         else: | ||||||
|             argnames = () |             argnames = () | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -157,14 +157,21 @@ xfail.Exception = XFailed  # type: ignore | ||||||
| def importorskip( | def importorskip( | ||||||
|     modname: str, minversion: Optional[str] = None, reason: Optional[str] = None |     modname: str, minversion: Optional[str] = None, reason: Optional[str] = None | ||||||
| ) -> Any: | ) -> Any: | ||||||
|     """Imports and returns the requested module ``modname``, or skip the current test |     """Imports and returns the requested module ``modname``, or skip the | ||||||
|     if the module cannot be imported. |     current test if the module cannot be imported. | ||||||
| 
 | 
 | ||||||
|     :param str modname: the name of the module to import |     :param str modname: the name of the module to import | ||||||
|     :param str minversion: if given, the imported module ``__version__`` attribute must be |     :param str minversion: if given, the imported module's ``__version__`` | ||||||
|         at least this minimal version, otherwise the test is still skipped. |         attribute must be at least this minimal version, otherwise the test is | ||||||
|     :param str reason: if given, this reason is shown as the message when the module |         still skipped. | ||||||
|         cannot be imported. |     :param str reason: if given, this reason is shown as the message when the | ||||||
|  |         module cannot be imported. | ||||||
|  |     :returns: The imported module. This should be assigned to its canonical | ||||||
|  |         name. | ||||||
|  | 
 | ||||||
|  |     Example:: | ||||||
|  | 
 | ||||||
|  |         docutils = pytest.importorskip("docutils") | ||||||
|     """ |     """ | ||||||
|     import warnings |     import warnings | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -630,6 +630,12 @@ class Testdir: | ||||||
|         return p |         return p | ||||||
| 
 | 
 | ||||||
|     def copy_example(self, name=None): |     def copy_example(self, name=None): | ||||||
|  |         """Copy file from project's directory into the testdir. | ||||||
|  | 
 | ||||||
|  |         :param str name: The name of the file to copy. | ||||||
|  |         :return: path to the copied directory (inside ``self.tmpdir``). | ||||||
|  | 
 | ||||||
|  |         """ | ||||||
|         import warnings |         import warnings | ||||||
|         from _pytest.warning_types import PYTESTER_COPY_EXAMPLE |         from _pytest.warning_types import PYTESTER_COPY_EXAMPLE | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -23,6 +23,7 @@ from _pytest.compat import getfslineno | ||||||
| from _pytest.compat import getimfunc | from _pytest.compat import getimfunc | ||||||
| from _pytest.compat import getlocation | from _pytest.compat import getlocation | ||||||
| from _pytest.compat import is_generator | from _pytest.compat import is_generator | ||||||
|  | from _pytest.compat import iscoroutinefunction | ||||||
| from _pytest.compat import NOTSET | from _pytest.compat import NOTSET | ||||||
| from _pytest.compat import REGEX_TYPE | from _pytest.compat import REGEX_TYPE | ||||||
| from _pytest.compat import safe_getattr | from _pytest.compat import safe_getattr | ||||||
|  | @ -150,19 +151,25 @@ def pytest_configure(config): | ||||||
| 
 | 
 | ||||||
| @hookimpl(trylast=True) | @hookimpl(trylast=True) | ||||||
| def pytest_pyfunc_call(pyfuncitem): | def pytest_pyfunc_call(pyfuncitem): | ||||||
|     testfunction = pyfuncitem.obj |     def async_warn(): | ||||||
|     iscoroutinefunction = getattr(inspect, "iscoroutinefunction", None) |         msg = "async def functions are not natively supported and have been skipped.\n" | ||||||
|     if iscoroutinefunction is not None and iscoroutinefunction(testfunction): |  | ||||||
|         msg = "Coroutine functions are not natively supported and have been skipped.\n" |  | ||||||
|         msg += "You need to install a suitable plugin for your async framework, for example:\n" |         msg += "You need to install a suitable plugin for your async framework, for example:\n" | ||||||
|         msg += "  - pytest-asyncio\n" |         msg += "  - pytest-asyncio\n" | ||||||
|         msg += "  - pytest-trio\n" |         msg += "  - pytest-trio\n" | ||||||
|         msg += "  - pytest-tornasync" |         msg += "  - pytest-tornasync" | ||||||
|         warnings.warn(PytestUnhandledCoroutineWarning(msg.format(pyfuncitem.nodeid))) |         warnings.warn(PytestUnhandledCoroutineWarning(msg.format(pyfuncitem.nodeid))) | ||||||
|         skip(msg="coroutine function and no async plugin installed (see warnings)") |         skip(msg="async def function and no async plugin installed (see warnings)") | ||||||
|  | 
 | ||||||
|  |     testfunction = pyfuncitem.obj | ||||||
|  |     if iscoroutinefunction(testfunction) or ( | ||||||
|  |         sys.version_info >= (3, 6) and inspect.isasyncgenfunction(testfunction) | ||||||
|  |     ): | ||||||
|  |         async_warn() | ||||||
|     funcargs = pyfuncitem.funcargs |     funcargs = pyfuncitem.funcargs | ||||||
|     testargs = {arg: funcargs[arg] for arg in pyfuncitem._fixtureinfo.argnames} |     testargs = {arg: funcargs[arg] for arg in pyfuncitem._fixtureinfo.argnames} | ||||||
|     testfunction(**testargs) |     result = testfunction(**testargs) | ||||||
|  |     if hasattr(result, "__await__") or hasattr(result, "__aiter__"): | ||||||
|  |         async_warn() | ||||||
|     return True |     return True | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1189,6 +1189,8 @@ def test_warn_on_async_function(testdir): | ||||||
|             pass |             pass | ||||||
|         async def test_2(): |         async def test_2(): | ||||||
|             pass |             pass | ||||||
|  |         def test_3(): | ||||||
|  |             return test_2() | ||||||
|     """ |     """ | ||||||
|     ) |     ) | ||||||
|     result = testdir.runpytest() |     result = testdir.runpytest() | ||||||
|  | @ -1196,11 +1198,43 @@ def test_warn_on_async_function(testdir): | ||||||
|         [ |         [ | ||||||
|             "test_async.py::test_1", |             "test_async.py::test_1", | ||||||
|             "test_async.py::test_2", |             "test_async.py::test_2", | ||||||
|             "*Coroutine functions are not natively supported*", |             "test_async.py::test_3", | ||||||
|             "*2 skipped, 2 warnings in*", |             "*async def functions are not natively supported*", | ||||||
|  |             "*3 skipped, 3 warnings in*", | ||||||
|         ] |         ] | ||||||
|     ) |     ) | ||||||
|     # ensure our warning message appears only once |     # ensure our warning message appears only once | ||||||
|     assert ( |     assert ( | ||||||
|         result.stdout.str().count("Coroutine functions are not natively supported") == 1 |         result.stdout.str().count("async def functions are not natively supported") == 1 | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @pytest.mark.filterwarnings("default") | ||||||
|  | @pytest.mark.skipif( | ||||||
|  |     sys.version_info < (3, 6), reason="async gen syntax available in Python 3.6+" | ||||||
|  | ) | ||||||
|  | def test_warn_on_async_gen_function(testdir): | ||||||
|  |     testdir.makepyfile( | ||||||
|  |         test_async=""" | ||||||
|  |         async def test_1(): | ||||||
|  |             yield | ||||||
|  |         async def test_2(): | ||||||
|  |             yield | ||||||
|  |         def test_3(): | ||||||
|  |             return test_2() | ||||||
|  |     """ | ||||||
|  |     ) | ||||||
|  |     result = testdir.runpytest() | ||||||
|  |     result.stdout.fnmatch_lines( | ||||||
|  |         [ | ||||||
|  |             "test_async.py::test_1", | ||||||
|  |             "test_async.py::test_2", | ||||||
|  |             "test_async.py::test_3", | ||||||
|  |             "*async def functions are not natively supported*", | ||||||
|  |             "*3 skipped, 3 warnings in*", | ||||||
|  |         ] | ||||||
|  |     ) | ||||||
|  |     # ensure our warning message appears only once | ||||||
|  |     assert ( | ||||||
|  |         result.stdout.str().count("async def functions are not natively supported") == 1 | ||||||
|     ) |     ) | ||||||
|  |  | ||||||
|  | @ -1143,52 +1143,6 @@ def test_unorderable_types(testdir): | ||||||
|     assert result.ret == ExitCode.NO_TESTS_COLLECTED |     assert result.ret == ExitCode.NO_TESTS_COLLECTED | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def test_collect_functools_partial(testdir): |  | ||||||
|     """ |  | ||||||
|     Test that collection of functools.partial object works, and arguments |  | ||||||
|     to the wrapped functions are dealt with correctly (see #811). |  | ||||||
|     """ |  | ||||||
|     testdir.makepyfile( |  | ||||||
|         """ |  | ||||||
|         import functools |  | ||||||
|         import pytest |  | ||||||
| 
 |  | ||||||
|         @pytest.fixture |  | ||||||
|         def fix1(): |  | ||||||
|             return 'fix1' |  | ||||||
| 
 |  | ||||||
|         @pytest.fixture |  | ||||||
|         def fix2(): |  | ||||||
|             return 'fix2' |  | ||||||
| 
 |  | ||||||
|         def check1(i, fix1): |  | ||||||
|             assert i == 2 |  | ||||||
|             assert fix1 == 'fix1' |  | ||||||
| 
 |  | ||||||
|         def check2(fix1, i): |  | ||||||
|             assert i == 2 |  | ||||||
|             assert fix1 == 'fix1' |  | ||||||
| 
 |  | ||||||
|         def check3(fix1, i, fix2): |  | ||||||
|             assert i == 2 |  | ||||||
|             assert fix1 == 'fix1' |  | ||||||
|             assert fix2 == 'fix2' |  | ||||||
| 
 |  | ||||||
|         test_ok_1 = functools.partial(check1, i=2) |  | ||||||
|         test_ok_2 = functools.partial(check1, i=2, fix1='fix1') |  | ||||||
|         test_ok_3 = functools.partial(check1, 2) |  | ||||||
|         test_ok_4 = functools.partial(check2, i=2) |  | ||||||
|         test_ok_5 = functools.partial(check3, i=2) |  | ||||||
|         test_ok_6 = functools.partial(check3, i=2, fix1='fix1') |  | ||||||
| 
 |  | ||||||
|         test_fail_1 = functools.partial(check2, 2) |  | ||||||
|         test_fail_2 = functools.partial(check3, 2) |  | ||||||
|     """ |  | ||||||
|     ) |  | ||||||
|     result = testdir.inline_run() |  | ||||||
|     result.assertoutcome(passed=6, failed=2) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| @pytest.mark.filterwarnings("default") | @pytest.mark.filterwarnings("default") | ||||||
| def test_dont_collect_non_function_callable(testdir): | def test_dont_collect_non_function_callable(testdir): | ||||||
|     """Test for issue https://github.com/pytest-dev/pytest/issues/331 |     """Test for issue https://github.com/pytest-dev/pytest/issues/331 | ||||||
|  |  | ||||||
|  | @ -9,7 +9,9 @@ from _pytest.pathlib import Path | ||||||
| from _pytest.pytester import get_public_names | from _pytest.pytester import get_public_names | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def test_getfuncargnames(): | def test_getfuncargnames_functions(): | ||||||
|  |     """Test getfuncargnames for normal functions""" | ||||||
|  | 
 | ||||||
|     def f(): |     def f(): | ||||||
|         pass |         pass | ||||||
| 
 | 
 | ||||||
|  | @ -30,18 +32,56 @@ def test_getfuncargnames(): | ||||||
| 
 | 
 | ||||||
|     assert fixtures.getfuncargnames(j) == ("arg1", "arg2") |     assert fixtures.getfuncargnames(j) == ("arg1", "arg2") | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | def test_getfuncargnames_methods(): | ||||||
|  |     """Test getfuncargnames for normal methods""" | ||||||
|  | 
 | ||||||
|     class A: |     class A: | ||||||
|         def f(self, arg1, arg2="hello"): |         def f(self, arg1, arg2="hello"): | ||||||
|             pass |             pass | ||||||
| 
 | 
 | ||||||
|  |     assert fixtures.getfuncargnames(A().f) == ("arg1",) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_getfuncargnames_staticmethod(): | ||||||
|  |     """Test getfuncargnames for staticmethods""" | ||||||
|  | 
 | ||||||
|  |     class A: | ||||||
|         @staticmethod |         @staticmethod | ||||||
|         def static(arg1, arg2): |         def static(arg1, arg2, x=1): | ||||||
|             pass |             pass | ||||||
| 
 | 
 | ||||||
|     assert fixtures.getfuncargnames(A().f) == ("arg1",) |  | ||||||
|     assert fixtures.getfuncargnames(A.static, cls=A) == ("arg1", "arg2") |     assert fixtures.getfuncargnames(A.static, cls=A) == ("arg1", "arg2") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | def test_getfuncargnames_partial(): | ||||||
|  |     """Check getfuncargnames for methods defined with functools.partial (#5701)""" | ||||||
|  |     import functools | ||||||
|  | 
 | ||||||
|  |     def check(arg1, arg2, i): | ||||||
|  |         pass | ||||||
|  | 
 | ||||||
|  |     class T: | ||||||
|  |         test_ok = functools.partial(check, i=2) | ||||||
|  | 
 | ||||||
|  |     values = fixtures.getfuncargnames(T().test_ok, name="test_ok") | ||||||
|  |     assert values == ("arg1", "arg2") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_getfuncargnames_staticmethod_partial(): | ||||||
|  |     """Check getfuncargnames for staticmethods defined with functools.partial (#5701)""" | ||||||
|  |     import functools | ||||||
|  | 
 | ||||||
|  |     def check(arg1, arg2, i): | ||||||
|  |         pass | ||||||
|  | 
 | ||||||
|  |     class T: | ||||||
|  |         test_ok = staticmethod(functools.partial(check, i=2)) | ||||||
|  | 
 | ||||||
|  |     values = fixtures.getfuncargnames(T().test_ok, name="test_ok") | ||||||
|  |     assert values == ("arg1", "arg2") | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| @pytest.mark.pytester_example_path("fixtures/fill_fixtures") | @pytest.mark.pytester_example_path("fixtures/fill_fixtures") | ||||||
| class TestFillFixtures: | class TestFillFixtures: | ||||||
|     def test_fillfuncargs_exposed(self): |     def test_fillfuncargs_exposed(self): | ||||||
|  |  | ||||||
|  | @ -1,4 +1,5 @@ | ||||||
| import sys | import sys | ||||||
|  | from functools import partial | ||||||
| from functools import wraps | from functools import wraps | ||||||
| 
 | 
 | ||||||
| import pytest | import pytest | ||||||
|  | @ -72,6 +73,16 @@ def test_get_real_func(): | ||||||
|     assert get_real_func(wrapped_func2) is wrapped_func |     assert get_real_func(wrapped_func2) is wrapped_func | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | def test_get_real_func_partial(): | ||||||
|  |     """Test get_real_func handles partial instances correctly""" | ||||||
|  | 
 | ||||||
|  |     def foo(x): | ||||||
|  |         return x | ||||||
|  | 
 | ||||||
|  |     assert get_real_func(foo) is foo | ||||||
|  |     assert get_real_func(partial(foo)) is foo | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| def test_is_generator_asyncio(testdir): | def test_is_generator_asyncio(testdir): | ||||||
|     testdir.makepyfile( |     testdir.makepyfile( | ||||||
|         """ |         """ | ||||||
|  | @ -91,9 +102,6 @@ def test_is_generator_asyncio(testdir): | ||||||
|     result.stdout.fnmatch_lines(["*1 passed*"]) |     result.stdout.fnmatch_lines(["*1 passed*"]) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @pytest.mark.skipif( |  | ||||||
|     sys.version_info < (3, 5), reason="async syntax available in Python 3.5+" |  | ||||||
| ) |  | ||||||
| def test_is_generator_async_syntax(testdir): | def test_is_generator_async_syntax(testdir): | ||||||
|     testdir.makepyfile( |     testdir.makepyfile( | ||||||
|         """ |         """ | ||||||
|  | @ -113,6 +121,29 @@ def test_is_generator_async_syntax(testdir): | ||||||
|     result.stdout.fnmatch_lines(["*1 passed*"]) |     result.stdout.fnmatch_lines(["*1 passed*"]) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @pytest.mark.skipif( | ||||||
|  |     sys.version_info < (3, 6), reason="async gen syntax available in Python 3.6+" | ||||||
|  | ) | ||||||
|  | def test_is_generator_async_gen_syntax(testdir): | ||||||
|  |     testdir.makepyfile( | ||||||
|  |         """ | ||||||
|  |         from _pytest.compat import is_generator | ||||||
|  |         def test_is_generator_py36(): | ||||||
|  |             async def foo(): | ||||||
|  |                 yield | ||||||
|  |                 await foo() | ||||||
|  | 
 | ||||||
|  |             async def bar(): | ||||||
|  |                 yield | ||||||
|  | 
 | ||||||
|  |             assert not is_generator(foo) | ||||||
|  |             assert not is_generator(bar) | ||||||
|  |     """ | ||||||
|  |     ) | ||||||
|  |     result = testdir.runpytest() | ||||||
|  |     result.stdout.fnmatch_lines(["*1 passed*"]) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| class ErrorsHelper: | class ErrorsHelper: | ||||||
|     @property |     @property | ||||||
|     def raise_exception(self): |     def raise_exception(self): | ||||||
|  |  | ||||||
|  | @ -55,7 +55,7 @@ class TestPasteCapture: | ||||||
|             ] |             ] | ||||||
|         ) |         ) | ||||||
| 
 | 
 | ||||||
|     def test_non_ascii_paste_text(self, testdir): |     def test_non_ascii_paste_text(self, testdir, pastebinlist): | ||||||
|         """Make sure that text which contains non-ascii characters is pasted |         """Make sure that text which contains non-ascii characters is pasted | ||||||
|         correctly. See #1219. |         correctly. See #1219. | ||||||
|         """ |         """ | ||||||
|  | @ -74,6 +74,7 @@ class TestPasteCapture: | ||||||
|                 "*Sending information to Paste Service*", |                 "*Sending information to Paste Service*", | ||||||
|             ] |             ] | ||||||
|         ) |         ) | ||||||
|  |         assert len(pastebinlist) == 1 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class TestPaste: | class TestPaste: | ||||||
|  |  | ||||||
|  | @ -622,3 +622,21 @@ def test_group_warnings_by_message(testdir): | ||||||
|     warning_code = 'warnings.warn(UserWarning("foo"))' |     warning_code = 'warnings.warn(UserWarning("foo"))' | ||||||
|     assert warning_code in result.stdout.str() |     assert warning_code in result.stdout.str() | ||||||
|     assert result.stdout.str().count(warning_code) == 1 |     assert result.stdout.str().count(warning_code) == 1 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def test_pytest_configure_warning(testdir, recwarn): | ||||||
|  |     """Issue 5115.""" | ||||||
|  |     testdir.makeconftest( | ||||||
|  |         """ | ||||||
|  |         def pytest_configure(): | ||||||
|  |             import warnings | ||||||
|  | 
 | ||||||
|  |             warnings.warn("from pytest_configure") | ||||||
|  |         """ | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     result = testdir.runpytest() | ||||||
|  |     assert result.ret == 5 | ||||||
|  |     assert "INTERNALERROR" not in result.stderr.str() | ||||||
|  |     warning = recwarn.pop() | ||||||
|  |     assert str(warning.message) == "from pytest_configure" | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue