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