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