Merge remote-tracking branch 'upstream/master' into mm
Conflicts: src/_pytest/outcomes.py
This commit is contained in:
		
						commit
						d7f082519a
					
				| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -167,7 +167,7 @@ Short version
 | 
			
		|||
#. 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 ``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``::
 | 
			
		||||
 | 
			
		||||
    tox -e linting,py37
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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):
 | 
			
		||||
        print("setting up %s" % function)
 | 
			
		||||
        print("setting up", function)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def test_func1():
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 = ""
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -629,7 +629,7 @@ through the special :py:class:`request <FixtureRequest>` 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):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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`:
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,6 +27,8 @@ pytest.skip
 | 
			
		|||
 | 
			
		||||
.. autofunction:: _pytest.outcomes.skip(msg, [allow_module_level=False])
 | 
			
		||||
 | 
			
		||||
.. _`pytest.importorskip ref`:
 | 
			
		||||
 | 
			
		||||
pytest.importorskip
 | 
			
		||||
~~~~~~~~~~~~~~~~~~~
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 = ()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
    )
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue