Use a hack to make typing of pytest.fail.Exception & co work

Mypy currently is unable to handle assigning attributes on function:
https://github.com/python/mypy/issues/2087.
pytest uses this for the outcome exceptions -- `pytest.fail.Exception`,
`pytest.exit.Exception` etc, and this is the canonical name by which they
are referred.

Initially we started working around this with type: ignores, and later
by switching e.g. `pytest.fail.Exception` with the direct exception
`Failed`. But this causes a lot of churn and is not as nice. And I also
found that some code relies on it, in skipping.py:

    def pytest_configure(config):
        if config.option.runxfail:
            # yay a hack
            import pytest

            old = pytest.xfail
            config._cleanup.append(lambda: setattr(pytest, "xfail", old))

            def nop(*args, **kwargs):
                pass

            nop.Exception = xfail.Exception
            setattr(pytest, "xfail", nop)
        ...

So it seems better to support it. Use a hack to make it work. The rest
of the commit rolls back all of the workarounds we added up to now.

`pytest.raises.Exception` also exists, but it's not used much so I kept
it as-is for now.

Hopefully in the future mypy supports this and this ugliness can be
removed.
This commit is contained in:
Ran Benita
2020-02-16 21:46:11 +02:00
parent d18c75baa3
commit 24dcc76495
9 changed files with 86 additions and 70 deletions

View File

@@ -904,6 +904,8 @@ class TestTracebackCutting:
pytest.skip("xxx")
assert excinfo.traceback[-1].frame.code.name == "skip"
assert excinfo.traceback[-1].ishidden()
assert excinfo.traceback[-2].frame.code.name == "test_skip_simple"
assert not excinfo.traceback[-2].ishidden()
def test_traceback_argsetup(self, testdir):
testdir.makeconftest(

View File

@@ -16,7 +16,7 @@ from hypothesis import strategies
import pytest
from _pytest import fixtures
from _pytest import python
from _pytest.outcomes import Failed
from _pytest.outcomes import fail
from _pytest.pytester import Testdir
from _pytest.python import _idval
@@ -99,7 +99,7 @@ class TestMetafunc:
({"x": 2}, "2"),
]
with pytest.raises(
Failed,
fail.Exception,
match=(
r"In func: ids must be list of string/float/int/bool, found:"
r" Exc\(from_gen\) \(type: <class .*Exc'>\) at index 2"
@@ -113,7 +113,7 @@ class TestMetafunc:
metafunc = self.Metafunc(func)
with pytest.raises(
Failed,
fail.Exception,
match=r"parametrize\(\) call in func got an unexpected scope value 'doggy'",
):
metafunc.parametrize("x", [1], scope="doggy")
@@ -126,7 +126,7 @@ class TestMetafunc:
metafunc = self.Metafunc(func)
with pytest.raises(
Failed,
fail.Exception,
match=r"'request' is a reserved name and cannot be used in @pytest.mark.parametrize",
):
metafunc.parametrize("request", [1])
@@ -205,10 +205,10 @@ class TestMetafunc:
metafunc = self.Metafunc(func)
with pytest.raises(Failed):
with pytest.raises(fail.Exception):
metafunc.parametrize("x", [1, 2], ids=["basic"])
with pytest.raises(Failed):
with pytest.raises(fail.Exception):
metafunc.parametrize(
("x", "y"), [("abc", "def"), ("ghi", "jkl")], ids=["one"]
)
@@ -689,7 +689,7 @@ class TestMetafunc:
metafunc = self.Metafunc(func)
with pytest.raises(
Failed,
fail.Exception,
match="In func: expected Sequence or boolean for indirect, got dict",
):
metafunc.parametrize("x, y", [("a", "b")], indirect={}) # type: ignore[arg-type] # noqa: F821
@@ -730,7 +730,7 @@ class TestMetafunc:
pass
metafunc = self.Metafunc(func)
with pytest.raises(Failed):
with pytest.raises(fail.Exception):
metafunc.parametrize("x, y", [("a", "b")], indirect=["x", "z"])
def test_parametrize_uses_no_fixture_error_indirect_false(