diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2768037bc..4d127d3c5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ exclude: doc/en/example/py2py3/test_py2.py repos: -- repo: https://github.com/python/black +- repo: https://github.com/psf/black rev: 19.3b0 hooks: - id: black diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 5ef418e0b..8e59191ab 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -167,7 +167,7 @@ Short version #. Enable and install `pre-commit `_ to ensure style-guides and code checks are followed. #. Target ``master`` for bugfixes and doc changes. #. Target ``features`` for new features or functionality changes. -#. Follow **PEP-8** for naming and `black `_ for formatting. +#. Follow **PEP-8** for naming and `black `_ for formatting. #. Tests are run using ``tox``:: tox -e linting,py37 diff --git a/README.rst b/README.rst index 301e49538..4bf9cce2f 100644 --- a/README.rst +++ b/README.rst @@ -26,7 +26,7 @@ :target: https://dev.azure.com/pytest-dev/pytest .. 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 :target: https://www.codetriage.com/pytest-dev/pytest diff --git a/changelog/5115.bugfix.rst b/changelog/5115.bugfix.rst new file mode 100644 index 000000000..af75499a3 --- /dev/null +++ b/changelog/5115.bugfix.rst @@ -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. diff --git a/changelog/5669.doc.rst b/changelog/5669.doc.rst new file mode 100644 index 000000000..0ec9626ae --- /dev/null +++ b/changelog/5669.doc.rst @@ -0,0 +1 @@ +Add docstring for ``Testdir.copy_example``. diff --git a/changelog/5701.bugfix.rst b/changelog/5701.bugfix.rst new file mode 100644 index 000000000..b654e7447 --- /dev/null +++ b/changelog/5701.bugfix.rst @@ -0,0 +1 @@ +Fix collection of ``staticmethod`` objects defined with ``functools.partial``. diff --git a/changelog/5734.bugfix.rst b/changelog/5734.bugfix.rst new file mode 100644 index 000000000..dc20e6b52 --- /dev/null +++ b/changelog/5734.bugfix.rst @@ -0,0 +1 @@ +Skip async generator test functions, and update the warning message to refer to ``async def`` functions. diff --git a/doc/en/capture.rst b/doc/en/capture.rst index 356da2678..55714c25b 100644 --- a/doc/en/capture.rst +++ b/doc/en/capture.rst @@ -57,7 +57,7 @@ is that you can use print statements for debugging: def setup_function(function): - print("setting up %s" % function) + print("setting up", function) def test_func1(): diff --git a/doc/en/example/assertion/failure_demo.py b/doc/en/example/assertion/failure_demo.py index 129362cd7..26454e48d 100644 --- a/doc/en/example/assertion/failure_demo.py +++ b/doc/en/example/assertion/failure_demo.py @@ -177,7 +177,7 @@ class TestRaises: def test_reinterpret_fails_with_print_for_the_fun_of_it(self): items = [1, 2, 3] - print("items is %r" % items) + print("items is {!r}".format(items)) a, b = items.pop() def test_some_error(self): diff --git a/doc/en/example/markers.rst b/doc/en/example/markers.rst index a1fe65874..909f23a2e 100644 --- a/doc/en/example/markers.rst +++ b/doc/en/example/markers.rst @@ -384,7 +384,7 @@ specifies via named environments: envnames = [mark.args[0] for mark in item.iter_markers(name="env")] if 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: @@ -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()) plat = sys.platform 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. Let's do a little test file to show how this looks like: diff --git a/doc/en/example/multipython.py b/doc/en/example/multipython.py index 3dc1d9b29..9db6879ed 100644 --- a/doc/en/example/multipython.py +++ b/doc/en/example/multipython.py @@ -69,4 +69,4 @@ class Python: @pytest.mark.parametrize("obj", [42, {}, {1: 3}]) def test_basic_objects(python1, python2, obj): python1.dumps(obj) - python2.load_and_is_true("obj == %s" % obj) + python2.load_and_is_true("obj == {}".format(obj)) diff --git a/doc/en/example/nonpython/conftest.py b/doc/en/example/nonpython/conftest.py index c94620e97..93d8285bf 100644 --- a/doc/en/example/nonpython/conftest.py +++ b/doc/en/example/nonpython/conftest.py @@ -33,13 +33,13 @@ class YamlItem(pytest.Item): return "\n".join( [ "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.", ] ) def reportinfo(self): - return self.fspath, 0, "usecase: %s" % self.name + return self.fspath, 0, "usecase: {}".format(self.name) class YamlException(Exception): diff --git a/doc/en/example/reportingdemo.rst b/doc/en/example/reportingdemo.rst index ae8928c51..05d06ecb6 100644 --- a/doc/en/example/reportingdemo.rst +++ b/doc/en/example/reportingdemo.rst @@ -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): items = [1, 2, 3] - print("items is %r" % items) + print("items is {!r}".format(items)) > a, b = items.pop() E TypeError: 'int' object is not iterable diff --git a/doc/en/example/simple.rst b/doc/en/example/simple.rst index dbfba0fa0..b4baa2b9b 100644 --- a/doc/en/example/simple.rst +++ b/doc/en/example/simple.rst @@ -478,7 +478,7 @@ an ``incremental`` marker which is to be used on classes: if "incremental" in item.keywords: previousfailed = getattr(item.parent, "_previousfailed", 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 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: # let's also access a fixture for the fun of it if "tmpdir" in item.fixturenames: - extra = " (%s)" % item.funcargs["tmpdir"] + extra = " ({})".format(item.funcargs["tmpdir"]) else: extra = "" diff --git a/doc/en/fixture.rst b/doc/en/fixture.rst index ea5e2dc86..b494ec0fe 100644 --- a/doc/en/fixture.rst +++ b/doc/en/fixture.rst @@ -629,7 +629,7 @@ through the special :py:class:`request ` object: def smtp_connection(request): smtp_connection = smtplib.SMTP(request.param, 587, timeout=5) yield smtp_connection - print("finalizing %s" % smtp_connection) + print("finalizing {}".format(smtp_connection)) smtp_connection.close() 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"]) def modarg(request): param = request.param - print(" SETUP modarg %s" % param) + print(" SETUP modarg", param) yield param - print(" TEARDOWN modarg %s" % param) + print(" TEARDOWN modarg", param) @pytest.fixture(scope="function", params=[1, 2]) def otherarg(request): param = request.param - print(" SETUP otherarg %s" % param) + print(" SETUP otherarg", param) yield param - print(" TEARDOWN otherarg %s" % param) + print(" TEARDOWN otherarg", param) def test_0(otherarg): - print(" RUN test0 with otherarg %s" % otherarg) + print(" RUN test0 with otherarg", otherarg) def test_1(modarg): - print(" RUN test1 with modarg %s" % modarg) + print(" RUN test1 with modarg", modarg) def test_2(otherarg, modarg): diff --git a/doc/en/getting-started.rst b/doc/en/getting-started.rst index 149bd33f1..f1c28769f 100644 --- a/doc/en/getting-started.rst +++ b/doc/en/getting-started.rst @@ -28,7 +28,7 @@ Install ``pytest`` .. code-block:: bash $ 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`: diff --git a/doc/en/reference.rst b/doc/en/reference.rst index 7e47ffce0..991050c23 100644 --- a/doc/en/reference.rst +++ b/doc/en/reference.rst @@ -27,6 +27,8 @@ pytest.skip .. autofunction:: _pytest.outcomes.skip(msg, [allow_module_level=False]) +.. _`pytest.importorskip ref`: + pytest.importorskip ~~~~~~~~~~~~~~~~~~~ diff --git a/doc/en/skipping.rst b/doc/en/skipping.rst index eb00a6dfb..57f565472 100644 --- a/doc/en/skipping.rst +++ b/doc/en/skipping.rst @@ -179,16 +179,15 @@ information. Skipping on a missing import dependency ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -You can use the following helper at module level -or within a test or test setup function: +You can skip tests on a missing import by using :ref:`pytest.importorskip ref` +at module level or within a test or test setup function. .. code-block:: python docutils = pytest.importorskip("docutils") -If ``docutils`` cannot be imported here, this will lead to a -skip outcome of the test. You can also skip based on the -version number of a library: +If ``docutils`` cannot be imported here, this will lead to a skip outcome of +the test. You can also skip based on the version number of a library: .. code-block:: python diff --git a/src/_pytest/compat.py b/src/_pytest/compat.py index df049991b..2d11231a4 100644 --- a/src/_pytest/compat.py +++ b/src/_pytest/compat.py @@ -46,14 +46,16 @@ def is_generator(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 ( - hasattr(inspect, "iscoroutinefunction") and inspect.iscoroutinefunction(func) - ) + Return True if func is a coroutine function (a function defined with async + 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): @@ -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. 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 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 revisit fixtures. The fixture mechanism should ask the node for the fixture names, and not try to obtain directly from the function object well after collection has occurred. - """ # The parameters attribute of a Signature object contains an # 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 ) + if not name: + name = function.__name__ + # If this function should be treated as a bound method even though # it's passed as an unbound method or function, remove the first # parameter name. 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:] # Remove any names that will be replaced with mocks. @@ -251,7 +257,7 @@ def get_real_method(obj, holder): try: is_method = hasattr(obj, "__func__") obj = get_real_func(obj) - except Exception: + except Exception: # pragma: no cover return obj if is_method and hasattr(obj, "__get__") and callable(obj.__get__): obj = obj.__get__(holder) diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index dbcc4cc67..b861563e9 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -695,7 +695,9 @@ class Config: def _do_configure(self): assert not self._configured 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): if self._configured: diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 1d84a9b0e..d5f9ad2d3 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -818,7 +818,7 @@ class FixtureDef: where=baseid, ) self.params = params - self.argnames = getfuncargnames(func, is_method=unittest) + self.argnames = getfuncargnames(func, name=argname, is_method=unittest) self.unittest = unittest self.ids = ids self._finalizers = [] @@ -1142,7 +1142,7 @@ class FixtureManager: def getfixtureinfo(self, node, func, cls, funcargs=True): if funcargs and not getattr(node, "nofuncargs", False): - argnames = getfuncargnames(func, cls=cls) + argnames = getfuncargnames(func, name=node.name, cls=cls) else: argnames = () diff --git a/src/_pytest/outcomes.py b/src/_pytest/outcomes.py index 5e331a323..13eb0a295 100644 --- a/src/_pytest/outcomes.py +++ b/src/_pytest/outcomes.py @@ -157,14 +157,21 @@ xfail.Exception = XFailed # type: ignore def importorskip( modname: str, minversion: Optional[str] = None, reason: Optional[str] = None ) -> Any: - """Imports and returns the requested module ``modname``, or skip the current test - if the module cannot be imported. + """Imports and returns the requested module ``modname``, or skip the + current test if the module cannot be imported. :param str modname: the name of the module to import - :param str minversion: if given, the imported module ``__version__`` attribute must be - at least this minimal version, otherwise the test is still skipped. - :param str reason: if given, this reason is shown as the message when the module - cannot be imported. + :param str minversion: if given, the imported module ``__version__`` + attribute must be at least this minimal version, otherwise the test is + still skipped. + :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 diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index 5835f01d1..d9ceb0e78 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -630,6 +630,12 @@ class Testdir: return p def copy_example(self, name=None): + """Copy file from project's directory into the testdir. + + :param str name: The name of the file for copy. + :return: path to the copied directory (inside ``self.tmpdir``). + + """ import warnings from _pytest.warning_types import PYTESTER_COPY_EXAMPLE diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 4e6ae8513..913a93bc0 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -23,6 +23,7 @@ from _pytest.compat import getfslineno from _pytest.compat import getimfunc from _pytest.compat import getlocation from _pytest.compat import is_generator +from _pytest.compat import iscoroutinefunction from _pytest.compat import NOTSET from _pytest.compat import REGEX_TYPE from _pytest.compat import safe_getattr @@ -150,19 +151,25 @@ def pytest_configure(config): @hookimpl(trylast=True) def pytest_pyfunc_call(pyfuncitem): - testfunction = pyfuncitem.obj - iscoroutinefunction = getattr(inspect, "iscoroutinefunction", None) - if iscoroutinefunction is not None and iscoroutinefunction(testfunction): - msg = "Coroutine functions are not natively supported and have been skipped.\n" + def async_warn(): + msg = "async def 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 += " - pytest-asyncio\n" msg += " - pytest-trio\n" msg += " - pytest-tornasync" 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 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 diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py index 427c64785..ad9c37737 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -1189,6 +1189,8 @@ def test_warn_on_async_function(testdir): pass async def test_2(): pass + def test_3(): + return test_2() """ ) result = testdir.runpytest() @@ -1196,11 +1198,43 @@ def test_warn_on_async_function(testdir): [ "test_async.py::test_1", "test_async.py::test_2", - "*Coroutine functions are not natively supported*", - "*2 skipped, 2 warnings in*", + "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("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 ) diff --git a/testing/python/collect.py b/testing/python/collect.py index 61fc58579..e6dd3e870 100644 --- a/testing/python/collect.py +++ b/testing/python/collect.py @@ -1143,52 +1143,6 @@ def test_unorderable_types(testdir): 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") def test_dont_collect_non_function_callable(testdir): """Test for issue https://github.com/pytest-dev/pytest/issues/331 diff --git a/testing/python/fixtures.py b/testing/python/fixtures.py index e5bc25a1c..cfe2ed37d 100644 --- a/testing/python/fixtures.py +++ b/testing/python/fixtures.py @@ -9,7 +9,9 @@ from _pytest.pathlib import Path from _pytest.pytester import get_public_names -def test_getfuncargnames(): +def test_getfuncargnames_functions(): + """Test getfuncargnames for normal functions""" + def f(): pass @@ -30,18 +32,56 @@ def test_getfuncargnames(): assert fixtures.getfuncargnames(j) == ("arg1", "arg2") + +def test_getfuncargnames_methods(): + """Test getfuncargnames for normal methods""" + class A: def f(self, arg1, arg2="hello"): pass + assert fixtures.getfuncargnames(A().f) == ("arg1",) + + +def test_getfuncargnames_staticmethod(): + """Test getfuncargnames for staticmethods""" + + class A: @staticmethod - def static(arg1, arg2): + def static(arg1, arg2, x=1): pass - assert fixtures.getfuncargnames(A().f) == ("arg1",) 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") class TestFillFixtures: def test_fillfuncargs_exposed(self): diff --git a/testing/test_compat.py b/testing/test_compat.py index 9e7d05c5d..94dac439d 100644 --- a/testing/test_compat.py +++ b/testing/test_compat.py @@ -1,4 +1,5 @@ import sys +from functools import partial from functools import wraps import pytest @@ -72,6 +73,16 @@ def test_get_real_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): testdir.makepyfile( """ @@ -91,9 +102,6 @@ def test_is_generator_asyncio(testdir): 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): testdir.makepyfile( """ @@ -113,6 +121,29 @@ def test_is_generator_async_syntax(testdir): 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: @property def raise_exception(self): diff --git a/testing/test_pastebin.py b/testing/test_pastebin.py index fd443ed40..9afa1e23f 100644 --- a/testing/test_pastebin.py +++ b/testing/test_pastebin.py @@ -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 correctly. See #1219. """ @@ -74,6 +74,7 @@ class TestPasteCapture: "*Sending information to Paste Service*", ] ) + assert len(pastebinlist) == 1 class TestPaste: diff --git a/testing/test_warnings.py b/testing/test_warnings.py index 4d58c99e3..077636c52 100644 --- a/testing/test_warnings.py +++ b/testing/test_warnings.py @@ -622,3 +622,21 @@ def test_group_warnings_by_message(testdir): warning_code = 'warnings.warn(UserWarning("foo"))' assert warning_code in result.stdout.str() 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"