Merge pull request #6311 from bluetech/type-annotations-10

Some type annotation & check_untyped_defs fixes
This commit is contained in:
Ran Benita 2020-01-19 20:17:32 +02:00 committed by GitHub
commit 44eb1f580c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 324 additions and 214 deletions

View File

@ -67,7 +67,7 @@ class Code:
return not self == other return not self == other
@property @property
def path(self): def path(self) -> Union[py.path.local, str]:
""" return a path object pointing to source code (note that it """ return a path object pointing to source code (note that it
might not point to an actually existing file). """ might not point to an actually existing file). """
try: try:
@ -335,7 +335,7 @@ class Traceback(List[TracebackEntry]):
(path is None or codepath == path) (path is None or codepath == path)
and ( and (
excludepath is None excludepath is None
or not hasattr(codepath, "relto") or not isinstance(codepath, py.path.local)
or not codepath.relto(excludepath) or not codepath.relto(excludepath)
) )
and (lineno is None or x.lineno == lineno) and (lineno is None or x.lineno == lineno)

View File

@ -5,8 +5,8 @@ import sys
import textwrap import textwrap
import tokenize import tokenize
import warnings import warnings
from ast import PyCF_ONLY_AST as _AST_FLAG
from bisect import bisect_right from bisect import bisect_right
from types import CodeType
from types import FrameType from types import FrameType
from typing import Iterator from typing import Iterator
from typing import List from typing import List
@ -18,6 +18,10 @@ from typing import Union
import py import py
from _pytest.compat import overload from _pytest.compat import overload
from _pytest.compat import TYPE_CHECKING
if TYPE_CHECKING:
from typing_extensions import Literal
class Source: class Source:
@ -121,7 +125,7 @@ class Source:
start, end = self.getstatementrange(lineno) start, end = self.getstatementrange(lineno)
return self[start:end] return self[start:end]
def getstatementrange(self, lineno: int): def getstatementrange(self, lineno: int) -> Tuple[int, int]:
""" return (start, end) tuple which spans the minimal """ return (start, end) tuple which spans the minimal
statement region which containing the given lineno. statement region which containing the given lineno.
""" """
@ -159,14 +163,36 @@ class Source:
def __str__(self) -> str: def __str__(self) -> str:
return "\n".join(self.lines) return "\n".join(self.lines)
@overload
def compile( def compile(
self, self,
filename=None, filename: Optional[str] = ...,
mode="exec", mode: str = ...,
flag: "Literal[0]" = ...,
dont_inherit: int = ...,
_genframe: Optional[FrameType] = ...,
) -> CodeType:
raise NotImplementedError()
@overload # noqa: F811
def compile( # noqa: F811
self,
filename: Optional[str] = ...,
mode: str = ...,
flag: int = ...,
dont_inherit: int = ...,
_genframe: Optional[FrameType] = ...,
) -> Union[CodeType, ast.AST]:
raise NotImplementedError()
def compile( # noqa: F811
self,
filename: Optional[str] = None,
mode: str = "exec",
flag: int = 0, flag: int = 0,
dont_inherit: int = 0, dont_inherit: int = 0,
_genframe: Optional[FrameType] = None, _genframe: Optional[FrameType] = None,
): ) -> Union[CodeType, ast.AST]:
""" return compiled code object. if filename is None """ return compiled code object. if filename is None
invent an artificial filename which displays invent an artificial filename which displays
the source/line position of the caller frame. the source/line position of the caller frame.
@ -196,8 +222,10 @@ class Source:
newex.text = ex.text newex.text = ex.text
raise newex raise newex
else: else:
if flag & _AST_FLAG: if flag & ast.PyCF_ONLY_AST:
assert isinstance(co, ast.AST)
return co return co
assert isinstance(co, CodeType)
lines = [(x + "\n") for x in self.lines] lines = [(x + "\n") for x in self.lines]
# Type ignored because linecache.cache is private. # Type ignored because linecache.cache is private.
linecache.cache[filename] = (1, None, lines, filename) # type: ignore linecache.cache[filename] = (1, None, lines, filename) # type: ignore
@ -209,7 +237,35 @@ class Source:
# #
def compile_(source, filename=None, mode="exec", flags: int = 0, dont_inherit: int = 0): @overload
def compile_(
source: Union[str, bytes, ast.mod, ast.AST],
filename: Optional[str] = ...,
mode: str = ...,
flags: "Literal[0]" = ...,
dont_inherit: int = ...,
) -> CodeType:
raise NotImplementedError()
@overload # noqa: F811
def compile_( # noqa: F811
source: Union[str, bytes, ast.mod, ast.AST],
filename: Optional[str] = ...,
mode: str = ...,
flags: int = ...,
dont_inherit: int = ...,
) -> Union[CodeType, ast.AST]:
raise NotImplementedError()
def compile_( # noqa: F811
source: Union[str, bytes, ast.mod, ast.AST],
filename: Optional[str] = None,
mode: str = "exec",
flags: int = 0,
dont_inherit: int = 0,
) -> Union[CodeType, ast.AST]:
""" compile the given source to a raw code object, """ compile the given source to a raw code object,
and maintain an internal cache which allows later and maintain an internal cache which allows later
retrieval of the source code for the code object retrieval of the source code for the code object
@ -217,14 +273,16 @@ def compile_(source, filename=None, mode="exec", flags: int = 0, dont_inherit: i
""" """
if isinstance(source, ast.AST): if isinstance(source, ast.AST):
# XXX should Source support having AST? # XXX should Source support having AST?
return compile(source, filename, mode, flags, dont_inherit) assert filename is not None
co = compile(source, filename, mode, flags, dont_inherit)
assert isinstance(co, (CodeType, ast.AST))
return co
_genframe = sys._getframe(1) # the caller _genframe = sys._getframe(1) # the caller
s = Source(source) s = Source(source)
co = s.compile(filename, mode, flags, _genframe=_genframe) return s.compile(filename, mode, flags, _genframe=_genframe)
return co
def getfslineno(obj): def getfslineno(obj) -> Tuple[Union[str, py.path.local], int]:
""" Return source location (path, lineno) for the given object. """ Return source location (path, lineno) for the given object.
If the source cannot be determined return ("", -1). If the source cannot be determined return ("", -1).
@ -321,7 +379,7 @@ def getstatementrange_ast(
# don't produce duplicate warnings when compiling source to find ast # don't produce duplicate warnings when compiling source to find ast
with warnings.catch_warnings(): with warnings.catch_warnings():
warnings.simplefilter("ignore") warnings.simplefilter("ignore")
astnode = compile(content, "source", "exec", _AST_FLAG) astnode = ast.parse(content, "source", "exec")
start, end = get_statement_startend2(lineno, astnode) start, end = get_statement_startend2(lineno, astnode)
# we need to correct the end: # we need to correct the end:

View File

@ -57,7 +57,7 @@ def deprecated_call(func=None, *args, **kwargs):
@overload @overload
def warns( def warns(
expected_warning: Union["Type[Warning]", Tuple["Type[Warning]", ...]], expected_warning: Optional[Union["Type[Warning]", Tuple["Type[Warning]", ...]]],
*, *,
match: "Optional[Union[str, Pattern]]" = ... match: "Optional[Union[str, Pattern]]" = ...
) -> "WarningsChecker": ) -> "WarningsChecker":
@ -66,7 +66,7 @@ def warns(
@overload # noqa: F811 @overload # noqa: F811
def warns( # noqa: F811 def warns( # noqa: F811
expected_warning: Union["Type[Warning]", Tuple["Type[Warning]", ...]], expected_warning: Optional[Union["Type[Warning]", Tuple["Type[Warning]", ...]]],
func: Callable, func: Callable,
*args: Any, *args: Any,
match: Optional[Union[str, "Pattern"]] = ..., match: Optional[Union[str, "Pattern"]] = ...,
@ -76,7 +76,7 @@ def warns( # noqa: F811
def warns( # noqa: F811 def warns( # noqa: F811
expected_warning: Union["Type[Warning]", Tuple["Type[Warning]", ...]], expected_warning: Optional[Union["Type[Warning]", Tuple["Type[Warning]", ...]]],
*args: Any, *args: Any,
match: Optional[Union[str, "Pattern"]] = None, match: Optional[Union[str, "Pattern"]] = None,
**kwargs: Any **kwargs: Any

View File

@ -1,5 +1,6 @@
from io import StringIO from io import StringIO
from pprint import pprint from pprint import pprint
from typing import Any
from typing import List from typing import List
from typing import Optional from typing import Optional
from typing import Tuple from typing import Tuple
@ -17,6 +18,7 @@ from _pytest._code.code import ReprFuncArgs
from _pytest._code.code import ReprLocals from _pytest._code.code import ReprLocals
from _pytest._code.code import ReprTraceback from _pytest._code.code import ReprTraceback
from _pytest._code.code import TerminalRepr from _pytest._code.code import TerminalRepr
from _pytest.compat import TYPE_CHECKING
from _pytest.nodes import Node from _pytest.nodes import Node
from _pytest.outcomes import skip from _pytest.outcomes import skip
from _pytest.pathlib import Path from _pytest.pathlib import Path
@ -41,9 +43,14 @@ class BaseReport:
sections = [] # type: List[Tuple[str, str]] sections = [] # type: List[Tuple[str, str]]
nodeid = None # type: str nodeid = None # type: str
def __init__(self, **kw): def __init__(self, **kw: Any) -> None:
self.__dict__.update(kw) self.__dict__.update(kw)
if TYPE_CHECKING:
# Can have arbitrary fields given to __init__().
def __getattr__(self, key: str) -> Any:
raise NotImplementedError()
def toterminal(self, out) -> None: def toterminal(self, out) -> None:
if hasattr(self, "node"): if hasattr(self, "node"):
out.line(getslaveinfoline(self.node)) # type: ignore out.line(getslaveinfoline(self.node)) # type: ignore

View File

@ -4,10 +4,13 @@
import ast import ast
import inspect import inspect
import sys import sys
from types import CodeType
from typing import Any from typing import Any
from typing import Dict from typing import Dict
from typing import Optional from typing import Optional
import py
import _pytest._code import _pytest._code
import pytest import pytest
from _pytest._code import Source from _pytest._code import Source
@ -147,6 +150,10 @@ class TestAccesses:
assert len(x.lines) == 2 assert len(x.lines) == 2
assert str(x) == "def f(x):\n pass" assert str(x) == "def f(x):\n pass"
def test_getrange_step_not_supported(self) -> None:
with pytest.raises(IndexError, match=r"step"):
self.source[::2]
def test_getline(self) -> None: def test_getline(self) -> None:
x = self.source[0] x = self.source[0]
assert x == "def f(x):" assert x == "def f(x):"
@ -449,6 +456,14 @@ def test_idem_compile_and_getsource() -> None:
assert src == expected assert src == expected
def test_compile_ast() -> None:
# We don't necessarily want to support this.
# This test was added just for coverage.
stmt = ast.parse("def x(): pass")
co = _pytest._code.compile(stmt, filename="foo.py")
assert isinstance(co, CodeType)
def test_findsource_fallback() -> None: def test_findsource_fallback() -> None:
from _pytest._code.source import findsource from _pytest._code.source import findsource
@ -488,6 +503,7 @@ def test_getfslineno() -> None:
fspath, lineno = getfslineno(f) fspath, lineno = getfslineno(f)
assert isinstance(fspath, py.path.local)
assert fspath.basename == "test_source.py" assert fspath.basename == "test_source.py"
assert lineno == f.__code__.co_firstlineno - 1 # see findsource assert lineno == f.__code__.co_firstlineno - 1 # see findsource

View File

@ -2,6 +2,7 @@ import os
import subprocess import subprocess
import sys import sys
import time import time
from typing import List
import py.path import py.path
@ -9,6 +10,7 @@ import _pytest.pytester as pytester
import pytest import pytest
from _pytest.config import PytestPluginManager from _pytest.config import PytestPluginManager
from _pytest.main import ExitCode from _pytest.main import ExitCode
from _pytest.outcomes import Failed
from _pytest.pytester import CwdSnapshot from _pytest.pytester import CwdSnapshot
from _pytest.pytester import HookRecorder from _pytest.pytester import HookRecorder
from _pytest.pytester import LineMatcher from _pytest.pytester import LineMatcher
@ -16,7 +18,7 @@ from _pytest.pytester import SysModulesSnapshot
from _pytest.pytester import SysPathsSnapshot from _pytest.pytester import SysPathsSnapshot
def test_make_hook_recorder(testdir): def test_make_hook_recorder(testdir) -> None:
item = testdir.getitem("def test_func(): pass") item = testdir.getitem("def test_func(): pass")
recorder = testdir.make_hook_recorder(item.config.pluginmanager) recorder = testdir.make_hook_recorder(item.config.pluginmanager)
assert not recorder.getfailures() assert not recorder.getfailures()
@ -36,23 +38,23 @@ def test_make_hook_recorder(testdir):
failures = recorder.getfailures() failures = recorder.getfailures()
assert failures == [rep] assert failures == [rep]
class rep: class rep2:
excinfo = None excinfo = None
passed = False passed = False
failed = False failed = False
skipped = True skipped = True
when = "call" when = "call"
rep.passed = False rep2.passed = False
rep.skipped = True rep2.skipped = True
recorder.hook.pytest_runtest_logreport(report=rep) recorder.hook.pytest_runtest_logreport(report=rep2)
modcol = testdir.getmodulecol("") modcol = testdir.getmodulecol("")
rep = modcol.config.hook.pytest_make_collect_report(collector=modcol) rep3 = modcol.config.hook.pytest_make_collect_report(collector=modcol)
rep.passed = False rep3.passed = False
rep.failed = True rep3.failed = True
rep.skipped = False rep3.skipped = False
recorder.hook.pytest_collectreport(report=rep) recorder.hook.pytest_collectreport(report=rep3)
passed, skipped, failed = recorder.listoutcomes() passed, skipped, failed = recorder.listoutcomes()
assert not passed and skipped and failed assert not passed and skipped and failed
@ -65,17 +67,17 @@ def test_make_hook_recorder(testdir):
recorder.unregister() recorder.unregister()
recorder.clear() recorder.clear()
recorder.hook.pytest_runtest_logreport(report=rep) recorder.hook.pytest_runtest_logreport(report=rep3)
pytest.raises(ValueError, recorder.getfailures) pytest.raises(ValueError, recorder.getfailures)
def test_parseconfig(testdir): def test_parseconfig(testdir) -> None:
config1 = testdir.parseconfig() config1 = testdir.parseconfig()
config2 = testdir.parseconfig() config2 = testdir.parseconfig()
assert config2 is not config1 assert config2 is not config1
def test_testdir_runs_with_plugin(testdir): def test_testdir_runs_with_plugin(testdir) -> None:
testdir.makepyfile( testdir.makepyfile(
""" """
pytest_plugins = "pytester" pytest_plugins = "pytester"
@ -87,7 +89,7 @@ def test_testdir_runs_with_plugin(testdir):
result.assert_outcomes(passed=1) result.assert_outcomes(passed=1)
def test_runresult_assertion_on_xfail(testdir): def test_runresult_assertion_on_xfail(testdir) -> None:
testdir.makepyfile( testdir.makepyfile(
""" """
import pytest import pytest
@ -104,7 +106,7 @@ def test_runresult_assertion_on_xfail(testdir):
assert result.ret == 0 assert result.ret == 0
def test_runresult_assertion_on_xpassed(testdir): def test_runresult_assertion_on_xpassed(testdir) -> None:
testdir.makepyfile( testdir.makepyfile(
""" """
import pytest import pytest
@ -121,7 +123,7 @@ def test_runresult_assertion_on_xpassed(testdir):
assert result.ret == 0 assert result.ret == 0
def test_xpassed_with_strict_is_considered_a_failure(testdir): def test_xpassed_with_strict_is_considered_a_failure(testdir) -> None:
testdir.makepyfile( testdir.makepyfile(
""" """
import pytest import pytest
@ -154,13 +156,13 @@ def make_holder():
def pytest_xyz_noarg(): def pytest_xyz_noarg():
"x" "x"
apimod.pytest_xyz = pytest_xyz apimod.pytest_xyz = pytest_xyz # type: ignore
apimod.pytest_xyz_noarg = pytest_xyz_noarg apimod.pytest_xyz_noarg = pytest_xyz_noarg # type: ignore
return apiclass, apimod return apiclass, apimod
@pytest.mark.parametrize("holder", make_holder()) @pytest.mark.parametrize("holder", make_holder())
def test_hookrecorder_basic(holder): def test_hookrecorder_basic(holder) -> None:
pm = PytestPluginManager() pm = PytestPluginManager()
pm.add_hookspecs(holder) pm.add_hookspecs(holder)
rec = HookRecorder(pm) rec = HookRecorder(pm)
@ -168,17 +170,17 @@ def test_hookrecorder_basic(holder):
call = rec.popcall("pytest_xyz") call = rec.popcall("pytest_xyz")
assert call.arg == 123 assert call.arg == 123
assert call._name == "pytest_xyz" assert call._name == "pytest_xyz"
pytest.raises(pytest.fail.Exception, rec.popcall, "abc") pytest.raises(Failed, rec.popcall, "abc")
pm.hook.pytest_xyz_noarg() pm.hook.pytest_xyz_noarg()
call = rec.popcall("pytest_xyz_noarg") call = rec.popcall("pytest_xyz_noarg")
assert call._name == "pytest_xyz_noarg" assert call._name == "pytest_xyz_noarg"
def test_makepyfile_unicode(testdir): def test_makepyfile_unicode(testdir) -> None:
testdir.makepyfile(chr(0xFFFD)) testdir.makepyfile(chr(0xFFFD))
def test_makepyfile_utf8(testdir): def test_makepyfile_utf8(testdir) -> None:
"""Ensure makepyfile accepts utf-8 bytes as input (#2738)""" """Ensure makepyfile accepts utf-8 bytes as input (#2738)"""
utf8_contents = """ utf8_contents = """
def setup_function(function): def setup_function(function):
@ -189,7 +191,7 @@ def test_makepyfile_utf8(testdir):
class TestInlineRunModulesCleanup: class TestInlineRunModulesCleanup:
def test_inline_run_test_module_not_cleaned_up(self, testdir): def test_inline_run_test_module_not_cleaned_up(self, testdir) -> None:
test_mod = testdir.makepyfile("def test_foo(): assert True") test_mod = testdir.makepyfile("def test_foo(): assert True")
result = testdir.inline_run(str(test_mod)) result = testdir.inline_run(str(test_mod))
assert result.ret == ExitCode.OK assert result.ret == ExitCode.OK
@ -200,9 +202,9 @@ class TestInlineRunModulesCleanup:
def spy_factory(self): def spy_factory(self):
class SysModulesSnapshotSpy: class SysModulesSnapshotSpy:
instances = [] instances = [] # type: List[SysModulesSnapshotSpy]
def __init__(self, preserve=None): def __init__(self, preserve=None) -> None:
SysModulesSnapshotSpy.instances.append(self) SysModulesSnapshotSpy.instances.append(self)
self._spy_restore_count = 0 self._spy_restore_count = 0
self._spy_preserve = preserve self._spy_preserve = preserve
@ -216,7 +218,7 @@ class TestInlineRunModulesCleanup:
def test_inline_run_taking_and_restoring_a_sys_modules_snapshot( def test_inline_run_taking_and_restoring_a_sys_modules_snapshot(
self, testdir, monkeypatch self, testdir, monkeypatch
): ) -> None:
spy_factory = self.spy_factory() spy_factory = self.spy_factory()
monkeypatch.setattr(pytester, "SysModulesSnapshot", spy_factory) monkeypatch.setattr(pytester, "SysModulesSnapshot", spy_factory)
testdir.syspathinsert() testdir.syspathinsert()
@ -237,7 +239,7 @@ class TestInlineRunModulesCleanup:
def test_inline_run_sys_modules_snapshot_restore_preserving_modules( def test_inline_run_sys_modules_snapshot_restore_preserving_modules(
self, testdir, monkeypatch self, testdir, monkeypatch
): ) -> None:
spy_factory = self.spy_factory() spy_factory = self.spy_factory()
monkeypatch.setattr(pytester, "SysModulesSnapshot", spy_factory) monkeypatch.setattr(pytester, "SysModulesSnapshot", spy_factory)
test_mod = testdir.makepyfile("def test_foo(): pass") test_mod = testdir.makepyfile("def test_foo(): pass")
@ -248,7 +250,7 @@ class TestInlineRunModulesCleanup:
assert spy._spy_preserve("zope.interface") assert spy._spy_preserve("zope.interface")
assert spy._spy_preserve("zopelicious") assert spy._spy_preserve("zopelicious")
def test_external_test_module_imports_not_cleaned_up(self, testdir): def test_external_test_module_imports_not_cleaned_up(self, testdir) -> None:
testdir.syspathinsert() testdir.syspathinsert()
testdir.makepyfile(imported="data = 'you son of a silly person'") testdir.makepyfile(imported="data = 'you son of a silly person'")
import imported import imported
@ -263,7 +265,7 @@ class TestInlineRunModulesCleanup:
assert imported.data == 42 assert imported.data == 42
def test_assert_outcomes_after_pytest_error(testdir): def test_assert_outcomes_after_pytest_error(testdir) -> None:
testdir.makepyfile("def test_foo(): assert True") testdir.makepyfile("def test_foo(): assert True")
result = testdir.runpytest("--unexpected-argument") result = testdir.runpytest("--unexpected-argument")
@ -271,7 +273,7 @@ def test_assert_outcomes_after_pytest_error(testdir):
result.assert_outcomes(passed=0) result.assert_outcomes(passed=0)
def test_cwd_snapshot(tmpdir): def test_cwd_snapshot(tmpdir) -> None:
foo = tmpdir.ensure("foo", dir=1) foo = tmpdir.ensure("foo", dir=1)
bar = tmpdir.ensure("bar", dir=1) bar = tmpdir.ensure("bar", dir=1)
foo.chdir() foo.chdir()
@ -285,16 +287,16 @@ def test_cwd_snapshot(tmpdir):
class TestSysModulesSnapshot: class TestSysModulesSnapshot:
key = "my-test-module" key = "my-test-module"
def test_remove_added(self): def test_remove_added(self) -> None:
original = dict(sys.modules) original = dict(sys.modules)
assert self.key not in sys.modules assert self.key not in sys.modules
snapshot = SysModulesSnapshot() snapshot = SysModulesSnapshot()
sys.modules[self.key] = "something" sys.modules[self.key] = "something" # type: ignore
assert self.key in sys.modules assert self.key in sys.modules
snapshot.restore() snapshot.restore()
assert sys.modules == original assert sys.modules == original
def test_add_removed(self, monkeypatch): def test_add_removed(self, monkeypatch) -> None:
assert self.key not in sys.modules assert self.key not in sys.modules
monkeypatch.setitem(sys.modules, self.key, "something") monkeypatch.setitem(sys.modules, self.key, "something")
assert self.key in sys.modules assert self.key in sys.modules
@ -305,17 +307,17 @@ class TestSysModulesSnapshot:
snapshot.restore() snapshot.restore()
assert sys.modules == original assert sys.modules == original
def test_restore_reloaded(self, monkeypatch): def test_restore_reloaded(self, monkeypatch) -> None:
assert self.key not in sys.modules assert self.key not in sys.modules
monkeypatch.setitem(sys.modules, self.key, "something") monkeypatch.setitem(sys.modules, self.key, "something")
assert self.key in sys.modules assert self.key in sys.modules
original = dict(sys.modules) original = dict(sys.modules)
snapshot = SysModulesSnapshot() snapshot = SysModulesSnapshot()
sys.modules[self.key] = "something else" sys.modules[self.key] = "something else" # type: ignore
snapshot.restore() snapshot.restore()
assert sys.modules == original assert sys.modules == original
def test_preserve_modules(self, monkeypatch): def test_preserve_modules(self, monkeypatch) -> None:
key = [self.key + str(i) for i in range(3)] key = [self.key + str(i) for i in range(3)]
assert not any(k in sys.modules for k in key) assert not any(k in sys.modules for k in key)
for i, k in enumerate(key): for i, k in enumerate(key):
@ -326,17 +328,17 @@ class TestSysModulesSnapshot:
return name in (key[0], key[1], "some-other-key") return name in (key[0], key[1], "some-other-key")
snapshot = SysModulesSnapshot(preserve=preserve) snapshot = SysModulesSnapshot(preserve=preserve)
sys.modules[key[0]] = original[key[0]] = "something else0" sys.modules[key[0]] = original[key[0]] = "something else0" # type: ignore
sys.modules[key[1]] = original[key[1]] = "something else1" sys.modules[key[1]] = original[key[1]] = "something else1" # type: ignore
sys.modules[key[2]] = "something else2" sys.modules[key[2]] = "something else2" # type: ignore
snapshot.restore() snapshot.restore()
assert sys.modules == original assert sys.modules == original
def test_preserve_container(self, monkeypatch): def test_preserve_container(self, monkeypatch) -> None:
original = dict(sys.modules) original = dict(sys.modules)
assert self.key not in original assert self.key not in original
replacement = dict(sys.modules) replacement = dict(sys.modules)
replacement[self.key] = "life of brian" replacement[self.key] = "life of brian" # type: ignore
snapshot = SysModulesSnapshot() snapshot = SysModulesSnapshot()
monkeypatch.setattr(sys, "modules", replacement) monkeypatch.setattr(sys, "modules", replacement)
snapshot.restore() snapshot.restore()
@ -349,10 +351,10 @@ class TestSysPathsSnapshot:
other_path = {"path": "meta_path", "meta_path": "path"} other_path = {"path": "meta_path", "meta_path": "path"}
@staticmethod @staticmethod
def path(n): def path(n: int) -> str:
return "my-dirty-little-secret-" + str(n) return "my-dirty-little-secret-" + str(n)
def test_restore(self, monkeypatch, path_type): def test_restore(self, monkeypatch, path_type) -> None:
other_path_type = self.other_path[path_type] other_path_type = self.other_path[path_type]
for i in range(10): for i in range(10):
assert self.path(i) not in getattr(sys, path_type) assert self.path(i) not in getattr(sys, path_type)
@ -375,12 +377,12 @@ class TestSysPathsSnapshot:
assert getattr(sys, path_type) == original assert getattr(sys, path_type) == original
assert getattr(sys, other_path_type) == original_other assert getattr(sys, other_path_type) == original_other
def test_preserve_container(self, monkeypatch, path_type): def test_preserve_container(self, monkeypatch, path_type) -> None:
other_path_type = self.other_path[path_type] other_path_type = self.other_path[path_type]
original_data = list(getattr(sys, path_type)) original_data = list(getattr(sys, path_type))
original_other = getattr(sys, other_path_type) original_other = getattr(sys, other_path_type)
original_other_data = list(original_other) original_other_data = list(original_other)
new = [] new = [] # type: List[object]
snapshot = SysPathsSnapshot() snapshot = SysPathsSnapshot()
monkeypatch.setattr(sys, path_type, new) monkeypatch.setattr(sys, path_type, new)
snapshot.restore() snapshot.restore()
@ -390,7 +392,7 @@ class TestSysPathsSnapshot:
assert getattr(sys, other_path_type) == original_other_data assert getattr(sys, other_path_type) == original_other_data
def test_testdir_subprocess(testdir): def test_testdir_subprocess(testdir) -> None:
testfile = testdir.makepyfile("def test_one(): pass") testfile = testdir.makepyfile("def test_one(): pass")
assert testdir.runpytest_subprocess(testfile).ret == 0 assert testdir.runpytest_subprocess(testfile).ret == 0
@ -416,17 +418,17 @@ def test_testdir_subprocess_via_runpytest_arg(testdir) -> None:
assert result.ret == 0 assert result.ret == 0
def test_unicode_args(testdir): def test_unicode_args(testdir) -> None:
result = testdir.runpytest("-k", "💩") result = testdir.runpytest("-k", "💩")
assert result.ret == ExitCode.NO_TESTS_COLLECTED assert result.ret == ExitCode.NO_TESTS_COLLECTED
def test_testdir_run_no_timeout(testdir): def test_testdir_run_no_timeout(testdir) -> None:
testfile = testdir.makepyfile("def test_no_timeout(): pass") testfile = testdir.makepyfile("def test_no_timeout(): pass")
assert testdir.runpytest_subprocess(testfile).ret == ExitCode.OK assert testdir.runpytest_subprocess(testfile).ret == ExitCode.OK
def test_testdir_run_with_timeout(testdir): def test_testdir_run_with_timeout(testdir) -> None:
testfile = testdir.makepyfile("def test_no_timeout(): pass") testfile = testdir.makepyfile("def test_no_timeout(): pass")
timeout = 120 timeout = 120
@ -440,7 +442,7 @@ def test_testdir_run_with_timeout(testdir):
assert duration < timeout assert duration < timeout
def test_testdir_run_timeout_expires(testdir): def test_testdir_run_timeout_expires(testdir) -> None:
testfile = testdir.makepyfile( testfile = testdir.makepyfile(
""" """
import time import time
@ -452,7 +454,7 @@ def test_testdir_run_timeout_expires(testdir):
testdir.runpytest_subprocess(testfile, timeout=1) testdir.runpytest_subprocess(testfile, timeout=1)
def test_linematcher_with_nonlist(): def test_linematcher_with_nonlist() -> None:
"""Test LineMatcher with regard to passing in a set (accidentally).""" """Test LineMatcher with regard to passing in a set (accidentally)."""
lm = LineMatcher([]) lm = LineMatcher([])
@ -467,10 +469,11 @@ def test_linematcher_with_nonlist():
assert lm._getlines(set()) == set() assert lm._getlines(set()) == set()
def test_linematcher_match_failure(): def test_linematcher_match_failure() -> None:
lm = LineMatcher(["foo", "foo", "bar"]) lm = LineMatcher(["foo", "foo", "bar"])
with pytest.raises(pytest.fail.Exception) as e: with pytest.raises(Failed) as e:
lm.fnmatch_lines(["foo", "f*", "baz"]) lm.fnmatch_lines(["foo", "f*", "baz"])
assert e.value.msg is not None
assert e.value.msg.splitlines() == [ assert e.value.msg.splitlines() == [
"exact match: 'foo'", "exact match: 'foo'",
"fnmatch: 'f*'", "fnmatch: 'f*'",
@ -481,8 +484,9 @@ def test_linematcher_match_failure():
] ]
lm = LineMatcher(["foo", "foo", "bar"]) lm = LineMatcher(["foo", "foo", "bar"])
with pytest.raises(pytest.fail.Exception) as e: with pytest.raises(Failed) as e:
lm.re_match_lines(["foo", "^f.*", "baz"]) lm.re_match_lines(["foo", "^f.*", "baz"])
assert e.value.msg is not None
assert e.value.msg.splitlines() == [ assert e.value.msg.splitlines() == [
"exact match: 'foo'", "exact match: 'foo'",
"re.match: '^f.*'", "re.match: '^f.*'",
@ -494,7 +498,7 @@ def test_linematcher_match_failure():
@pytest.mark.parametrize("function", ["no_fnmatch_line", "no_re_match_line"]) @pytest.mark.parametrize("function", ["no_fnmatch_line", "no_re_match_line"])
def test_no_matching(function): def test_no_matching(function) -> None:
if function == "no_fnmatch_line": if function == "no_fnmatch_line":
good_pattern = "*.py OK*" good_pattern = "*.py OK*"
bad_pattern = "*X.py OK*" bad_pattern = "*X.py OK*"
@ -515,7 +519,7 @@ def test_no_matching(function):
# check the function twice to ensure we don't accumulate the internal buffer # check the function twice to ensure we don't accumulate the internal buffer
for i in range(2): for i in range(2):
with pytest.raises(pytest.fail.Exception) as e: with pytest.raises(Failed) as e:
func = getattr(lm, function) func = getattr(lm, function)
func(good_pattern) func(good_pattern)
obtained = str(e.value).splitlines() obtained = str(e.value).splitlines()
@ -542,15 +546,15 @@ def test_no_matching(function):
func(bad_pattern) # bad pattern does not match any line: passes func(bad_pattern) # bad pattern does not match any line: passes
def test_no_matching_after_match(): def test_no_matching_after_match() -> None:
lm = LineMatcher(["1", "2", "3"]) lm = LineMatcher(["1", "2", "3"])
lm.fnmatch_lines(["1", "3"]) lm.fnmatch_lines(["1", "3"])
with pytest.raises(pytest.fail.Exception) as e: with pytest.raises(Failed) as e:
lm.no_fnmatch_line("*") lm.no_fnmatch_line("*")
assert str(e.value).splitlines() == ["fnmatch: '*'", " with: '1'"] assert str(e.value).splitlines() == ["fnmatch: '*'", " with: '1'"]
def test_pytester_addopts(request, monkeypatch): def test_pytester_addopts(request, monkeypatch) -> None:
monkeypatch.setenv("PYTEST_ADDOPTS", "--orig-unused") monkeypatch.setenv("PYTEST_ADDOPTS", "--orig-unused")
testdir = request.getfixturevalue("testdir") testdir = request.getfixturevalue("testdir")
@ -563,7 +567,7 @@ def test_pytester_addopts(request, monkeypatch):
assert os.environ["PYTEST_ADDOPTS"] == "--orig-unused" assert os.environ["PYTEST_ADDOPTS"] == "--orig-unused"
def test_run_stdin(testdir): def test_run_stdin(testdir) -> None:
with pytest.raises(testdir.TimeoutExpired): with pytest.raises(testdir.TimeoutExpired):
testdir.run( testdir.run(
sys.executable, sys.executable,
@ -593,7 +597,7 @@ def test_run_stdin(testdir):
assert result.ret == 0 assert result.ret == 0
def test_popen_stdin_pipe(testdir): def test_popen_stdin_pipe(testdir) -> None:
proc = testdir.popen( proc = testdir.popen(
[sys.executable, "-c", "import sys; print(sys.stdin.read())"], [sys.executable, "-c", "import sys; print(sys.stdin.read())"],
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
@ -607,7 +611,7 @@ def test_popen_stdin_pipe(testdir):
assert proc.returncode == 0 assert proc.returncode == 0
def test_popen_stdin_bytes(testdir): def test_popen_stdin_bytes(testdir) -> None:
proc = testdir.popen( proc = testdir.popen(
[sys.executable, "-c", "import sys; print(sys.stdin.read())"], [sys.executable, "-c", "import sys; print(sys.stdin.read())"],
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
@ -620,7 +624,7 @@ def test_popen_stdin_bytes(testdir):
assert proc.returncode == 0 assert proc.returncode == 0
def test_popen_default_stdin_stderr_and_stdin_None(testdir): def test_popen_default_stdin_stderr_and_stdin_None(testdir) -> None:
# stdout, stderr default to pipes, # stdout, stderr default to pipes,
# stdin can be None to not close the pipe, avoiding # stdin can be None to not close the pipe, avoiding
# "ValueError: flush of closed file" with `communicate()`. # "ValueError: flush of closed file" with `communicate()`.
@ -639,7 +643,7 @@ def test_popen_default_stdin_stderr_and_stdin_None(testdir):
assert proc.returncode == 0 assert proc.returncode == 0
def test_spawn_uses_tmphome(testdir): def test_spawn_uses_tmphome(testdir) -> None:
import os import os
tmphome = str(testdir.tmpdir) tmphome = str(testdir.tmpdir)
@ -665,7 +669,7 @@ def test_spawn_uses_tmphome(testdir):
assert child.wait() == 0, out.decode("utf8") assert child.wait() == 0, out.decode("utf8")
def test_run_result_repr(): def test_run_result_repr() -> None:
outlines = ["some", "normal", "output"] outlines = ["some", "normal", "output"]
errlines = ["some", "nasty", "errors", "happened"] errlines = ["some", "nasty", "errors", "happened"]

View File

@ -1,17 +1,19 @@
import re import re
import warnings import warnings
from typing import Optional
import pytest import pytest
from _pytest.outcomes import Failed
from _pytest.recwarn import WarningsRecorder from _pytest.recwarn import WarningsRecorder
def test_recwarn_stacklevel(recwarn): def test_recwarn_stacklevel(recwarn: WarningsRecorder) -> None:
warnings.warn("hello") warnings.warn("hello")
warn = recwarn.pop() warn = recwarn.pop()
assert warn.filename == __file__ assert warn.filename == __file__
def test_recwarn_functional(testdir): def test_recwarn_functional(testdir) -> None:
testdir.makepyfile( testdir.makepyfile(
""" """
import warnings import warnings
@ -26,7 +28,7 @@ def test_recwarn_functional(testdir):
class TestWarningsRecorderChecker: class TestWarningsRecorderChecker:
def test_recording(self): def test_recording(self) -> None:
rec = WarningsRecorder() rec = WarningsRecorder()
with rec: with rec:
assert not rec.list assert not rec.list
@ -42,23 +44,23 @@ class TestWarningsRecorderChecker:
assert values is rec.list assert values is rec.list
pytest.raises(AssertionError, rec.pop) pytest.raises(AssertionError, rec.pop)
def test_warn_stacklevel(self): def test_warn_stacklevel(self) -> None:
"""#4243""" """#4243"""
rec = WarningsRecorder() rec = WarningsRecorder()
with rec: with rec:
warnings.warn("test", DeprecationWarning, 2) warnings.warn("test", DeprecationWarning, 2)
def test_typechecking(self): def test_typechecking(self) -> None:
from _pytest.recwarn import WarningsChecker from _pytest.recwarn import WarningsChecker
with pytest.raises(TypeError): with pytest.raises(TypeError):
WarningsChecker(5) WarningsChecker(5) # type: ignore
with pytest.raises(TypeError): with pytest.raises(TypeError):
WarningsChecker(("hi", RuntimeWarning)) WarningsChecker(("hi", RuntimeWarning)) # type: ignore
with pytest.raises(TypeError): with pytest.raises(TypeError):
WarningsChecker([DeprecationWarning, RuntimeWarning]) WarningsChecker([DeprecationWarning, RuntimeWarning]) # type: ignore
def test_invalid_enter_exit(self): def test_invalid_enter_exit(self) -> None:
# wrap this test in WarningsRecorder to ensure warning state gets reset # wrap this test in WarningsRecorder to ensure warning state gets reset
with WarningsRecorder(): with WarningsRecorder():
with pytest.raises(RuntimeError): with pytest.raises(RuntimeError):
@ -75,50 +77,52 @@ class TestWarningsRecorderChecker:
class TestDeprecatedCall: class TestDeprecatedCall:
"""test pytest.deprecated_call()""" """test pytest.deprecated_call()"""
def dep(self, i, j=None): def dep(self, i: int, j: Optional[int] = None) -> int:
if i == 0: if i == 0:
warnings.warn("is deprecated", DeprecationWarning, stacklevel=1) warnings.warn("is deprecated", DeprecationWarning, stacklevel=1)
return 42 return 42
def dep_explicit(self, i): def dep_explicit(self, i: int) -> None:
if i == 0: if i == 0:
warnings.warn_explicit( warnings.warn_explicit(
"dep_explicit", category=DeprecationWarning, filename="hello", lineno=3 "dep_explicit", category=DeprecationWarning, filename="hello", lineno=3
) )
def test_deprecated_call_raises(self): def test_deprecated_call_raises(self) -> None:
with pytest.raises(pytest.fail.Exception, match="No warnings of type"): with pytest.raises(Failed, match="No warnings of type"):
pytest.deprecated_call(self.dep, 3, 5) pytest.deprecated_call(self.dep, 3, 5)
def test_deprecated_call(self): def test_deprecated_call(self) -> None:
pytest.deprecated_call(self.dep, 0, 5) pytest.deprecated_call(self.dep, 0, 5)
def test_deprecated_call_ret(self): def test_deprecated_call_ret(self) -> None:
ret = pytest.deprecated_call(self.dep, 0) ret = pytest.deprecated_call(self.dep, 0)
assert ret == 42 assert ret == 42
def test_deprecated_call_preserves(self): def test_deprecated_call_preserves(self) -> None:
onceregistry = warnings.onceregistry.copy() # Type ignored because `onceregistry` and `filters` are not
filters = warnings.filters[:] # documented API.
onceregistry = warnings.onceregistry.copy() # type: ignore
filters = warnings.filters[:] # type: ignore
warn = warnings.warn warn = warnings.warn
warn_explicit = warnings.warn_explicit warn_explicit = warnings.warn_explicit
self.test_deprecated_call_raises() self.test_deprecated_call_raises()
self.test_deprecated_call() self.test_deprecated_call()
assert onceregistry == warnings.onceregistry assert onceregistry == warnings.onceregistry # type: ignore
assert filters == warnings.filters assert filters == warnings.filters # type: ignore
assert warn is warnings.warn assert warn is warnings.warn
assert warn_explicit is warnings.warn_explicit assert warn_explicit is warnings.warn_explicit
def test_deprecated_explicit_call_raises(self): def test_deprecated_explicit_call_raises(self) -> None:
with pytest.raises(pytest.fail.Exception): with pytest.raises(Failed):
pytest.deprecated_call(self.dep_explicit, 3) pytest.deprecated_call(self.dep_explicit, 3)
def test_deprecated_explicit_call(self): def test_deprecated_explicit_call(self) -> None:
pytest.deprecated_call(self.dep_explicit, 0) pytest.deprecated_call(self.dep_explicit, 0)
pytest.deprecated_call(self.dep_explicit, 0) pytest.deprecated_call(self.dep_explicit, 0)
@pytest.mark.parametrize("mode", ["context_manager", "call"]) @pytest.mark.parametrize("mode", ["context_manager", "call"])
def test_deprecated_call_no_warning(self, mode): def test_deprecated_call_no_warning(self, mode) -> None:
"""Ensure deprecated_call() raises the expected failure when its block/function does """Ensure deprecated_call() raises the expected failure when its block/function does
not raise a deprecation warning. not raise a deprecation warning.
""" """
@ -127,7 +131,7 @@ class TestDeprecatedCall:
pass pass
msg = "No warnings of type (.*DeprecationWarning.*, .*PendingDeprecationWarning.*)" msg = "No warnings of type (.*DeprecationWarning.*, .*PendingDeprecationWarning.*)"
with pytest.raises(pytest.fail.Exception, match=msg): with pytest.raises(Failed, match=msg):
if mode == "call": if mode == "call":
pytest.deprecated_call(f) pytest.deprecated_call(f)
else: else:
@ -140,7 +144,7 @@ class TestDeprecatedCall:
@pytest.mark.parametrize("mode", ["context_manager", "call"]) @pytest.mark.parametrize("mode", ["context_manager", "call"])
@pytest.mark.parametrize("call_f_first", [True, False]) @pytest.mark.parametrize("call_f_first", [True, False])
@pytest.mark.filterwarnings("ignore") @pytest.mark.filterwarnings("ignore")
def test_deprecated_call_modes(self, warning_type, mode, call_f_first): def test_deprecated_call_modes(self, warning_type, mode, call_f_first) -> None:
"""Ensure deprecated_call() captures a deprecation warning as expected inside its """Ensure deprecated_call() captures a deprecation warning as expected inside its
block/function. block/function.
""" """
@ -159,7 +163,7 @@ class TestDeprecatedCall:
assert f() == 10 assert f() == 10
@pytest.mark.parametrize("mode", ["context_manager", "call"]) @pytest.mark.parametrize("mode", ["context_manager", "call"])
def test_deprecated_call_exception_is_raised(self, mode): def test_deprecated_call_exception_is_raised(self, mode) -> None:
"""If the block of the code being tested by deprecated_call() raises an exception, """If the block of the code being tested by deprecated_call() raises an exception,
it must raise the exception undisturbed. it must raise the exception undisturbed.
""" """
@ -174,7 +178,7 @@ class TestDeprecatedCall:
with pytest.deprecated_call(): with pytest.deprecated_call():
f() f()
def test_deprecated_call_specificity(self): def test_deprecated_call_specificity(self) -> None:
other_warnings = [ other_warnings = [
Warning, Warning,
UserWarning, UserWarning,
@ -189,40 +193,40 @@ class TestDeprecatedCall:
def f(): def f():
warnings.warn(warning("hi")) warnings.warn(warning("hi"))
with pytest.raises(pytest.fail.Exception): with pytest.raises(Failed):
pytest.deprecated_call(f) pytest.deprecated_call(f)
with pytest.raises(pytest.fail.Exception): with pytest.raises(Failed):
with pytest.deprecated_call(): with pytest.deprecated_call():
f() f()
def test_deprecated_call_supports_match(self): def test_deprecated_call_supports_match(self) -> None:
with pytest.deprecated_call(match=r"must be \d+$"): with pytest.deprecated_call(match=r"must be \d+$"):
warnings.warn("value must be 42", DeprecationWarning) warnings.warn("value must be 42", DeprecationWarning)
with pytest.raises(pytest.fail.Exception): with pytest.raises(Failed):
with pytest.deprecated_call(match=r"must be \d+$"): with pytest.deprecated_call(match=r"must be \d+$"):
warnings.warn("this is not here", DeprecationWarning) warnings.warn("this is not here", DeprecationWarning)
class TestWarns: class TestWarns:
def test_check_callable(self): def test_check_callable(self) -> None:
source = "warnings.warn('w1', RuntimeWarning)" source = "warnings.warn('w1', RuntimeWarning)"
with pytest.raises(TypeError, match=r".* must be callable"): with pytest.raises(TypeError, match=r".* must be callable"):
pytest.warns(RuntimeWarning, source) pytest.warns(RuntimeWarning, source) # type: ignore
def test_several_messages(self): def test_several_messages(self) -> None:
# different messages, b/c Python suppresses multiple identical warnings # different messages, b/c Python suppresses multiple identical warnings
pytest.warns(RuntimeWarning, lambda: warnings.warn("w1", RuntimeWarning)) pytest.warns(RuntimeWarning, lambda: warnings.warn("w1", RuntimeWarning))
with pytest.raises(pytest.fail.Exception): with pytest.raises(Failed):
pytest.warns(UserWarning, lambda: warnings.warn("w2", RuntimeWarning)) pytest.warns(UserWarning, lambda: warnings.warn("w2", RuntimeWarning))
pytest.warns(RuntimeWarning, lambda: warnings.warn("w3", RuntimeWarning)) pytest.warns(RuntimeWarning, lambda: warnings.warn("w3", RuntimeWarning))
def test_function(self): def test_function(self) -> None:
pytest.warns( pytest.warns(
SyntaxWarning, lambda msg: warnings.warn(msg, SyntaxWarning), "syntax" SyntaxWarning, lambda msg: warnings.warn(msg, SyntaxWarning), "syntax"
) )
def test_warning_tuple(self): def test_warning_tuple(self) -> None:
pytest.warns( pytest.warns(
(RuntimeWarning, SyntaxWarning), lambda: warnings.warn("w1", RuntimeWarning) (RuntimeWarning, SyntaxWarning), lambda: warnings.warn("w1", RuntimeWarning)
) )
@ -230,21 +234,21 @@ class TestWarns:
(RuntimeWarning, SyntaxWarning), lambda: warnings.warn("w2", SyntaxWarning) (RuntimeWarning, SyntaxWarning), lambda: warnings.warn("w2", SyntaxWarning)
) )
pytest.raises( pytest.raises(
pytest.fail.Exception, Failed,
lambda: pytest.warns( lambda: pytest.warns(
(RuntimeWarning, SyntaxWarning), (RuntimeWarning, SyntaxWarning),
lambda: warnings.warn("w3", UserWarning), lambda: warnings.warn("w3", UserWarning),
), ),
) )
def test_as_contextmanager(self): def test_as_contextmanager(self) -> None:
with pytest.warns(RuntimeWarning): with pytest.warns(RuntimeWarning):
warnings.warn("runtime", RuntimeWarning) warnings.warn("runtime", RuntimeWarning)
with pytest.warns(UserWarning): with pytest.warns(UserWarning):
warnings.warn("user", UserWarning) warnings.warn("user", UserWarning)
with pytest.raises(pytest.fail.Exception) as excinfo: with pytest.raises(Failed) as excinfo:
with pytest.warns(RuntimeWarning): with pytest.warns(RuntimeWarning):
warnings.warn("user", UserWarning) warnings.warn("user", UserWarning)
excinfo.match( excinfo.match(
@ -252,7 +256,7 @@ class TestWarns:
r"The list of emitted warnings is: \[UserWarning\('user',?\)\]." r"The list of emitted warnings is: \[UserWarning\('user',?\)\]."
) )
with pytest.raises(pytest.fail.Exception) as excinfo: with pytest.raises(Failed) as excinfo:
with pytest.warns(UserWarning): with pytest.warns(UserWarning):
warnings.warn("runtime", RuntimeWarning) warnings.warn("runtime", RuntimeWarning)
excinfo.match( excinfo.match(
@ -260,7 +264,7 @@ class TestWarns:
r"The list of emitted warnings is: \[RuntimeWarning\('runtime',?\)\]." r"The list of emitted warnings is: \[RuntimeWarning\('runtime',?\)\]."
) )
with pytest.raises(pytest.fail.Exception) as excinfo: with pytest.raises(Failed) as excinfo:
with pytest.warns(UserWarning): with pytest.warns(UserWarning):
pass pass
excinfo.match( excinfo.match(
@ -269,7 +273,7 @@ class TestWarns:
) )
warning_classes = (UserWarning, FutureWarning) warning_classes = (UserWarning, FutureWarning)
with pytest.raises(pytest.fail.Exception) as excinfo: with pytest.raises(Failed) as excinfo:
with pytest.warns(warning_classes) as warninfo: with pytest.warns(warning_classes) as warninfo:
warnings.warn("runtime", RuntimeWarning) warnings.warn("runtime", RuntimeWarning)
warnings.warn("import", ImportWarning) warnings.warn("import", ImportWarning)
@ -286,14 +290,14 @@ class TestWarns:
) )
) )
def test_record(self): def test_record(self) -> None:
with pytest.warns(UserWarning) as record: with pytest.warns(UserWarning) as record:
warnings.warn("user", UserWarning) warnings.warn("user", UserWarning)
assert len(record) == 1 assert len(record) == 1
assert str(record[0].message) == "user" assert str(record[0].message) == "user"
def test_record_only(self): def test_record_only(self) -> None:
with pytest.warns(None) as record: with pytest.warns(None) as record:
warnings.warn("user", UserWarning) warnings.warn("user", UserWarning)
warnings.warn("runtime", RuntimeWarning) warnings.warn("runtime", RuntimeWarning)
@ -302,7 +306,7 @@ class TestWarns:
assert str(record[0].message) == "user" assert str(record[0].message) == "user"
assert str(record[1].message) == "runtime" assert str(record[1].message) == "runtime"
def test_record_by_subclass(self): def test_record_by_subclass(self) -> None:
with pytest.warns(Warning) as record: with pytest.warns(Warning) as record:
warnings.warn("user", UserWarning) warnings.warn("user", UserWarning)
warnings.warn("runtime", RuntimeWarning) warnings.warn("runtime", RuntimeWarning)
@ -325,7 +329,7 @@ class TestWarns:
assert str(record[0].message) == "user" assert str(record[0].message) == "user"
assert str(record[1].message) == "runtime" assert str(record[1].message) == "runtime"
def test_double_test(self, testdir): def test_double_test(self, testdir) -> None:
"""If a test is run again, the warning should still be raised""" """If a test is run again, the warning should still be raised"""
testdir.makepyfile( testdir.makepyfile(
""" """
@ -341,32 +345,32 @@ class TestWarns:
result = testdir.runpytest() result = testdir.runpytest()
result.stdout.fnmatch_lines(["*2 passed in*"]) result.stdout.fnmatch_lines(["*2 passed in*"])
def test_match_regex(self): def test_match_regex(self) -> None:
with pytest.warns(UserWarning, match=r"must be \d+$"): with pytest.warns(UserWarning, match=r"must be \d+$"):
warnings.warn("value must be 42", UserWarning) warnings.warn("value must be 42", UserWarning)
with pytest.raises(pytest.fail.Exception): with pytest.raises(Failed):
with pytest.warns(UserWarning, match=r"must be \d+$"): with pytest.warns(UserWarning, match=r"must be \d+$"):
warnings.warn("this is not here", UserWarning) warnings.warn("this is not here", UserWarning)
with pytest.raises(pytest.fail.Exception): with pytest.raises(Failed):
with pytest.warns(FutureWarning, match=r"must be \d+$"): with pytest.warns(FutureWarning, match=r"must be \d+$"):
warnings.warn("value must be 42", UserWarning) warnings.warn("value must be 42", UserWarning)
def test_one_from_multiple_warns(self): def test_one_from_multiple_warns(self) -> None:
with pytest.warns(UserWarning, match=r"aaa"): with pytest.warns(UserWarning, match=r"aaa"):
warnings.warn("cccccccccc", UserWarning) warnings.warn("cccccccccc", UserWarning)
warnings.warn("bbbbbbbbbb", UserWarning) warnings.warn("bbbbbbbbbb", UserWarning)
warnings.warn("aaaaaaaaaa", UserWarning) warnings.warn("aaaaaaaaaa", UserWarning)
def test_none_of_multiple_warns(self): def test_none_of_multiple_warns(self) -> None:
with pytest.raises(pytest.fail.Exception): with pytest.raises(Failed):
with pytest.warns(UserWarning, match=r"aaa"): with pytest.warns(UserWarning, match=r"aaa"):
warnings.warn("bbbbbbbbbb", UserWarning) warnings.warn("bbbbbbbbbb", UserWarning)
warnings.warn("cccccccccc", UserWarning) warnings.warn("cccccccccc", UserWarning)
@pytest.mark.filterwarnings("ignore") @pytest.mark.filterwarnings("ignore")
def test_can_capture_previously_warned(self): def test_can_capture_previously_warned(self) -> None:
def f(): def f():
warnings.warn(UserWarning("ohai")) warnings.warn(UserWarning("ohai"))
return 10 return 10
@ -375,8 +379,8 @@ class TestWarns:
assert pytest.warns(UserWarning, f) == 10 assert pytest.warns(UserWarning, f) == 10
assert pytest.warns(UserWarning, f) == 10 assert pytest.warns(UserWarning, f) == 10
def test_warns_context_manager_with_kwargs(self): def test_warns_context_manager_with_kwargs(self) -> None:
with pytest.raises(TypeError) as excinfo: with pytest.raises(TypeError) as excinfo:
with pytest.warns(UserWarning, foo="bar"): with pytest.warns(UserWarning, foo="bar"): # type: ignore
pass pass
assert "Unexpected keyword arguments" in str(excinfo.value) assert "Unexpected keyword arguments" in str(excinfo.value)

View File

@ -2,6 +2,9 @@ import inspect
import os import os
import sys import sys
import types import types
from typing import Dict
from typing import List
from typing import Tuple
import py import py
@ -11,11 +14,17 @@ from _pytest import main
from _pytest import outcomes from _pytest import outcomes
from _pytest import reports from _pytest import reports
from _pytest import runner from _pytest import runner
from _pytest.outcomes import Exit
from _pytest.outcomes import Failed
from _pytest.outcomes import OutcomeException from _pytest.outcomes import OutcomeException
from _pytest.outcomes import Skipped
if False: # TYPE_CHECKING
from typing import Type
class TestSetupState: class TestSetupState:
def test_setup(self, testdir): def test_setup(self, testdir) -> None:
ss = runner.SetupState() ss = runner.SetupState()
item = testdir.getitem("def test_func(): pass") item = testdir.getitem("def test_func(): pass")
values = [1] values = [1]
@ -25,14 +34,14 @@ class TestSetupState:
ss._pop_and_teardown() ss._pop_and_teardown()
assert not values assert not values
def test_teardown_exact_stack_empty(self, testdir): def test_teardown_exact_stack_empty(self, testdir) -> None:
item = testdir.getitem("def test_func(): pass") item = testdir.getitem("def test_func(): pass")
ss = runner.SetupState() ss = runner.SetupState()
ss.teardown_exact(item, None) ss.teardown_exact(item, None)
ss.teardown_exact(item, None) ss.teardown_exact(item, None)
ss.teardown_exact(item, None) ss.teardown_exact(item, None)
def test_setup_fails_and_failure_is_cached(self, testdir): def test_setup_fails_and_failure_is_cached(self, testdir) -> None:
item = testdir.getitem( item = testdir.getitem(
""" """
def setup_module(mod): def setup_module(mod):
@ -44,7 +53,7 @@ class TestSetupState:
pytest.raises(ValueError, lambda: ss.prepare(item)) pytest.raises(ValueError, lambda: ss.prepare(item))
pytest.raises(ValueError, lambda: ss.prepare(item)) pytest.raises(ValueError, lambda: ss.prepare(item))
def test_teardown_multiple_one_fails(self, testdir): def test_teardown_multiple_one_fails(self, testdir) -> None:
r = [] r = []
def fin1(): def fin1():
@ -66,7 +75,7 @@ class TestSetupState:
assert err.value.args == ("oops",) assert err.value.args == ("oops",)
assert r == ["fin3", "fin1"] assert r == ["fin3", "fin1"]
def test_teardown_multiple_fail(self, testdir): def test_teardown_multiple_fail(self, testdir) -> None:
# Ensure the first exception is the one which is re-raised. # Ensure the first exception is the one which is re-raised.
# Ideally both would be reported however. # Ideally both would be reported however.
def fin1(): def fin1():
@ -83,7 +92,7 @@ class TestSetupState:
ss._callfinalizers(item) ss._callfinalizers(item)
assert err.value.args == ("oops2",) assert err.value.args == ("oops2",)
def test_teardown_multiple_scopes_one_fails(self, testdir): def test_teardown_multiple_scopes_one_fails(self, testdir) -> None:
module_teardown = [] module_teardown = []
def fin_func(): def fin_func():
@ -103,7 +112,7 @@ class TestSetupState:
class BaseFunctionalTests: class BaseFunctionalTests:
def test_passfunction(self, testdir): def test_passfunction(self, testdir) -> None:
reports = testdir.runitem( reports = testdir.runitem(
""" """
def test_func(): def test_func():
@ -116,7 +125,7 @@ class BaseFunctionalTests:
assert rep.outcome == "passed" assert rep.outcome == "passed"
assert not rep.longrepr assert not rep.longrepr
def test_failfunction(self, testdir): def test_failfunction(self, testdir) -> None:
reports = testdir.runitem( reports = testdir.runitem(
""" """
def test_func(): def test_func():
@ -131,7 +140,7 @@ class BaseFunctionalTests:
assert rep.outcome == "failed" assert rep.outcome == "failed"
# assert isinstance(rep.longrepr, ReprExceptionInfo) # assert isinstance(rep.longrepr, ReprExceptionInfo)
def test_skipfunction(self, testdir): def test_skipfunction(self, testdir) -> None:
reports = testdir.runitem( reports = testdir.runitem(
""" """
import pytest import pytest
@ -151,7 +160,7 @@ class BaseFunctionalTests:
# assert rep.skipped.location.path # assert rep.skipped.location.path
# assert not rep.skipped.failurerepr # assert not rep.skipped.failurerepr
def test_skip_in_setup_function(self, testdir): def test_skip_in_setup_function(self, testdir) -> None:
reports = testdir.runitem( reports = testdir.runitem(
""" """
import pytest import pytest
@ -172,7 +181,7 @@ class BaseFunctionalTests:
assert len(reports) == 2 assert len(reports) == 2
assert reports[1].passed # teardown assert reports[1].passed # teardown
def test_failure_in_setup_function(self, testdir): def test_failure_in_setup_function(self, testdir) -> None:
reports = testdir.runitem( reports = testdir.runitem(
""" """
import pytest import pytest
@ -189,7 +198,7 @@ class BaseFunctionalTests:
assert rep.when == "setup" assert rep.when == "setup"
assert len(reports) == 2 assert len(reports) == 2
def test_failure_in_teardown_function(self, testdir): def test_failure_in_teardown_function(self, testdir) -> None:
reports = testdir.runitem( reports = testdir.runitem(
""" """
import pytest import pytest
@ -209,7 +218,7 @@ class BaseFunctionalTests:
# assert rep.longrepr.reprcrash.lineno == 3 # assert rep.longrepr.reprcrash.lineno == 3
# assert rep.longrepr.reprtraceback.reprentries # assert rep.longrepr.reprtraceback.reprentries
def test_custom_failure_repr(self, testdir): def test_custom_failure_repr(self, testdir) -> None:
testdir.makepyfile( testdir.makepyfile(
conftest=""" conftest="""
import pytest import pytest
@ -234,7 +243,7 @@ class BaseFunctionalTests:
# assert rep.failed.where.path.basename == "test_func.py" # assert rep.failed.where.path.basename == "test_func.py"
# assert rep.failed.failurerepr == "hello" # assert rep.failed.failurerepr == "hello"
def test_teardown_final_returncode(self, testdir): def test_teardown_final_returncode(self, testdir) -> None:
rec = testdir.inline_runsource( rec = testdir.inline_runsource(
""" """
def test_func(): def test_func():
@ -245,7 +254,7 @@ class BaseFunctionalTests:
) )
assert rec.ret == 1 assert rec.ret == 1
def test_logstart_logfinish_hooks(self, testdir): def test_logstart_logfinish_hooks(self, testdir) -> None:
rec = testdir.inline_runsource( rec = testdir.inline_runsource(
""" """
import pytest import pytest
@ -262,7 +271,7 @@ class BaseFunctionalTests:
assert rep.nodeid == "test_logstart_logfinish_hooks.py::test_func" assert rep.nodeid == "test_logstart_logfinish_hooks.py::test_func"
assert rep.location == ("test_logstart_logfinish_hooks.py", 1, "test_func") assert rep.location == ("test_logstart_logfinish_hooks.py", 1, "test_func")
def test_exact_teardown_issue90(self, testdir): def test_exact_teardown_issue90(self, testdir) -> None:
rec = testdir.inline_runsource( rec = testdir.inline_runsource(
""" """
import pytest import pytest
@ -302,7 +311,7 @@ class BaseFunctionalTests:
assert reps[5].nodeid.endswith("test_func") assert reps[5].nodeid.endswith("test_func")
assert reps[5].failed assert reps[5].failed
def test_exact_teardown_issue1206(self, testdir): def test_exact_teardown_issue1206(self, testdir) -> None:
"""issue shadowing error with wrong number of arguments on teardown_method.""" """issue shadowing error with wrong number of arguments on teardown_method."""
rec = testdir.inline_runsource( rec = testdir.inline_runsource(
""" """
@ -338,7 +347,7 @@ class BaseFunctionalTests:
"TypeError: teardown_method() takes exactly 4 arguments (2 given)", "TypeError: teardown_method() takes exactly 4 arguments (2 given)",
) )
def test_failure_in_setup_function_ignores_custom_repr(self, testdir): def test_failure_in_setup_function_ignores_custom_repr(self, testdir) -> None:
testdir.makepyfile( testdir.makepyfile(
conftest=""" conftest="""
import pytest import pytest
@ -366,7 +375,7 @@ class BaseFunctionalTests:
# assert rep.outcome.where.path.basename == "test_func.py" # assert rep.outcome.where.path.basename == "test_func.py"
# assert instanace(rep.failed.failurerepr, PythonFailureRepr) # assert instanace(rep.failed.failurerepr, PythonFailureRepr)
def test_systemexit_does_not_bail_out(self, testdir): def test_systemexit_does_not_bail_out(self, testdir) -> None:
try: try:
reports = testdir.runitem( reports = testdir.runitem(
""" """
@ -380,7 +389,7 @@ class BaseFunctionalTests:
assert rep.failed assert rep.failed
assert rep.when == "call" assert rep.when == "call"
def test_exit_propagates(self, testdir): def test_exit_propagates(self, testdir) -> None:
try: try:
testdir.runitem( testdir.runitem(
""" """
@ -389,7 +398,7 @@ class BaseFunctionalTests:
raise pytest.exit.Exception() raise pytest.exit.Exception()
""" """
) )
except pytest.exit.Exception: except Exit:
pass pass
else: else:
pytest.fail("did not raise") pytest.fail("did not raise")
@ -402,7 +411,7 @@ class TestExecutionNonForked(BaseFunctionalTests):
return f return f
def test_keyboardinterrupt_propagates(self, testdir): def test_keyboardinterrupt_propagates(self, testdir) -> None:
try: try:
testdir.runitem( testdir.runitem(
""" """
@ -424,7 +433,7 @@ class TestExecutionForked(BaseFunctionalTests):
boxed = pytest.importorskip("xdist.boxed") boxed = pytest.importorskip("xdist.boxed")
return boxed.forked_run_report return boxed.forked_run_report
def test_suicide(self, testdir): def test_suicide(self, testdir) -> None:
reports = testdir.runitem( reports = testdir.runitem(
""" """
def test_func(): def test_func():
@ -438,7 +447,7 @@ class TestExecutionForked(BaseFunctionalTests):
class TestSessionReports: class TestSessionReports:
def test_collect_result(self, testdir): def test_collect_result(self, testdir) -> None:
col = testdir.getmodulecol( col = testdir.getmodulecol(
""" """
def test_func1(): def test_func1():
@ -461,20 +470,24 @@ class TestSessionReports:
assert res[1].name == "TestClass" assert res[1].name == "TestClass"
reporttypes = [reports.BaseReport, reports.TestReport, reports.CollectReport] reporttypes = [
reports.BaseReport,
reports.TestReport,
reports.CollectReport,
] # type: List[Type[reports.BaseReport]]
@pytest.mark.parametrize( @pytest.mark.parametrize(
"reporttype", reporttypes, ids=[x.__name__ for x in reporttypes] "reporttype", reporttypes, ids=[x.__name__ for x in reporttypes]
) )
def test_report_extra_parameters(reporttype): def test_report_extra_parameters(reporttype: "Type[reports.BaseReport]") -> None:
args = list(inspect.signature(reporttype.__init__).parameters.keys())[1:] args = list(inspect.signature(reporttype.__init__).parameters.keys())[1:]
basekw = dict.fromkeys(args, []) basekw = dict.fromkeys(args, []) # type: Dict[str, List[object]]
report = reporttype(newthing=1, **basekw) report = reporttype(newthing=1, **basekw)
assert report.newthing == 1 assert report.newthing == 1
def test_callinfo(): def test_callinfo() -> None:
ci = runner.CallInfo.from_call(lambda: 0, "123") ci = runner.CallInfo.from_call(lambda: 0, "123")
assert ci.when == "123" assert ci.when == "123"
assert ci.result == 0 assert ci.result == 0
@ -503,7 +516,7 @@ def test_callinfo():
@pytest.mark.xfail @pytest.mark.xfail
def test_runtest_in_module_ordering(testdir): def test_runtest_in_module_ordering(testdir) -> None:
p1 = testdir.makepyfile( p1 = testdir.makepyfile(
""" """
import pytest import pytest
@ -534,12 +547,12 @@ def test_runtest_in_module_ordering(testdir):
result.stdout.fnmatch_lines(["*2 passed*"]) result.stdout.fnmatch_lines(["*2 passed*"])
def test_outcomeexception_exceptionattributes(): def test_outcomeexception_exceptionattributes() -> None:
outcome = outcomes.OutcomeException("test") outcome = outcomes.OutcomeException("test")
assert outcome.args[0] == outcome.msg assert outcome.args[0] == outcome.msg
def test_outcomeexception_passes_except_Exception(): def test_outcomeexception_passes_except_Exception() -> None:
with pytest.raises(outcomes.OutcomeException): with pytest.raises(outcomes.OutcomeException):
try: try:
raise outcomes.OutcomeException("test") raise outcomes.OutcomeException("test")
@ -547,20 +560,22 @@ def test_outcomeexception_passes_except_Exception():
pass pass
def test_pytest_exit(): def test_pytest_exit() -> None:
with pytest.raises(pytest.exit.Exception) as excinfo: assert Exit == pytest.exit.Exception # type: ignore
with pytest.raises(Exit) as excinfo:
pytest.exit("hello") pytest.exit("hello")
assert excinfo.errisinstance(pytest.exit.Exception) assert excinfo.errisinstance(Exit)
def test_pytest_fail(): def test_pytest_fail() -> None:
with pytest.raises(pytest.fail.Exception) as excinfo: assert Failed == pytest.fail.Exception # type: ignore
with pytest.raises(Failed) as excinfo:
pytest.fail("hello") pytest.fail("hello")
s = excinfo.exconly(tryshort=True) s = excinfo.exconly(tryshort=True)
assert s.startswith("Failed") assert s.startswith("Failed")
def test_pytest_exit_msg(testdir): def test_pytest_exit_msg(testdir) -> None:
testdir.makeconftest( testdir.makeconftest(
""" """
import pytest import pytest
@ -583,7 +598,7 @@ def _strip_resource_warnings(lines):
] ]
def test_pytest_exit_returncode(testdir): def test_pytest_exit_returncode(testdir) -> None:
testdir.makepyfile( testdir.makepyfile(
"""\ """\
import pytest import pytest
@ -614,7 +629,7 @@ def test_pytest_exit_returncode(testdir):
assert result.ret == 98 assert result.ret == 98
def test_pytest_fail_notrace_runtest(testdir): def test_pytest_fail_notrace_runtest(testdir) -> None:
"""Test pytest.fail(..., pytrace=False) does not show tracebacks during test run.""" """Test pytest.fail(..., pytrace=False) does not show tracebacks during test run."""
testdir.makepyfile( testdir.makepyfile(
""" """
@ -630,7 +645,7 @@ def test_pytest_fail_notrace_runtest(testdir):
result.stdout.no_fnmatch_line("*def teardown_function*") result.stdout.no_fnmatch_line("*def teardown_function*")
def test_pytest_fail_notrace_collection(testdir): def test_pytest_fail_notrace_collection(testdir) -> None:
"""Test pytest.fail(..., pytrace=False) does not show tracebacks during collection.""" """Test pytest.fail(..., pytrace=False) does not show tracebacks during collection."""
testdir.makepyfile( testdir.makepyfile(
""" """
@ -645,7 +660,7 @@ def test_pytest_fail_notrace_collection(testdir):
result.stdout.no_fnmatch_line("*def some_internal_function()*") result.stdout.no_fnmatch_line("*def some_internal_function()*")
def test_pytest_fail_notrace_non_ascii(testdir): def test_pytest_fail_notrace_non_ascii(testdir) -> None:
"""Fix pytest.fail with pytrace=False with non-ascii characters (#1178). """Fix pytest.fail with pytrace=False with non-ascii characters (#1178).
This tests with native and unicode strings containing non-ascii chars. This tests with native and unicode strings containing non-ascii chars.
@ -663,7 +678,7 @@ def test_pytest_fail_notrace_non_ascii(testdir):
result.stdout.no_fnmatch_line("*def test_hello*") result.stdout.no_fnmatch_line("*def test_hello*")
def test_pytest_no_tests_collected_exit_status(testdir): def test_pytest_no_tests_collected_exit_status(testdir) -> None:
result = testdir.runpytest() result = testdir.runpytest()
result.stdout.fnmatch_lines(["*collected 0 items*"]) result.stdout.fnmatch_lines(["*collected 0 items*"])
assert result.ret == main.ExitCode.NO_TESTS_COLLECTED assert result.ret == main.ExitCode.NO_TESTS_COLLECTED
@ -685,16 +700,17 @@ def test_pytest_no_tests_collected_exit_status(testdir):
assert result.ret == main.ExitCode.NO_TESTS_COLLECTED assert result.ret == main.ExitCode.NO_TESTS_COLLECTED
def test_exception_printing_skip(): def test_exception_printing_skip() -> None:
assert Skipped == pytest.skip.Exception # type: ignore
try: try:
pytest.skip("hello") pytest.skip("hello")
except pytest.skip.Exception: except Skipped:
excinfo = _pytest._code.ExceptionInfo.from_current() excinfo = _pytest._code.ExceptionInfo.from_current()
s = excinfo.exconly(tryshort=True) s = excinfo.exconly(tryshort=True)
assert s.startswith("Skipped") assert s.startswith("Skipped")
def test_importorskip(monkeypatch): def test_importorskip(monkeypatch) -> None:
importorskip = pytest.importorskip importorskip = pytest.importorskip
def f(): def f():
@ -705,45 +721,49 @@ def test_importorskip(monkeypatch):
assert sysmod is sys assert sysmod is sys
# path = pytest.importorskip("os.path") # path = pytest.importorskip("os.path")
# assert path == os.path # assert path == os.path
excinfo = pytest.raises(pytest.skip.Exception, f) excinfo = pytest.raises(Skipped, f)
path = py.path.local(excinfo.getrepr().reprcrash.path) assert excinfo is not None
excrepr = excinfo.getrepr()
assert excrepr is not None
assert excrepr.reprcrash is not None
path = py.path.local(excrepr.reprcrash.path)
# check that importorskip reports the actual call # check that importorskip reports the actual call
# in this test the test_runner.py file # in this test the test_runner.py file
assert path.purebasename == "test_runner" assert path.purebasename == "test_runner"
pytest.raises(SyntaxError, pytest.importorskip, "x y z") pytest.raises(SyntaxError, pytest.importorskip, "x y z")
pytest.raises(SyntaxError, pytest.importorskip, "x=y") pytest.raises(SyntaxError, pytest.importorskip, "x=y")
mod = types.ModuleType("hello123") mod = types.ModuleType("hello123")
mod.__version__ = "1.3" mod.__version__ = "1.3" # type: ignore
monkeypatch.setitem(sys.modules, "hello123", mod) monkeypatch.setitem(sys.modules, "hello123", mod)
with pytest.raises(pytest.skip.Exception): with pytest.raises(Skipped):
pytest.importorskip("hello123", minversion="1.3.1") pytest.importorskip("hello123", minversion="1.3.1")
mod2 = pytest.importorskip("hello123", minversion="1.3") mod2 = pytest.importorskip("hello123", minversion="1.3")
assert mod2 == mod assert mod2 == mod
except pytest.skip.Exception: except Skipped:
print(_pytest._code.ExceptionInfo.from_current()) print(_pytest._code.ExceptionInfo.from_current())
pytest.fail("spurious skip") pytest.fail("spurious skip")
def test_importorskip_imports_last_module_part(): def test_importorskip_imports_last_module_part() -> None:
ospath = pytest.importorskip("os.path") ospath = pytest.importorskip("os.path")
assert os.path == ospath assert os.path == ospath
def test_importorskip_dev_module(monkeypatch): def test_importorskip_dev_module(monkeypatch) -> None:
try: try:
mod = types.ModuleType("mockmodule") mod = types.ModuleType("mockmodule")
mod.__version__ = "0.13.0.dev-43290" mod.__version__ = "0.13.0.dev-43290" # type: ignore
monkeypatch.setitem(sys.modules, "mockmodule", mod) monkeypatch.setitem(sys.modules, "mockmodule", mod)
mod2 = pytest.importorskip("mockmodule", minversion="0.12.0") mod2 = pytest.importorskip("mockmodule", minversion="0.12.0")
assert mod2 == mod assert mod2 == mod
with pytest.raises(pytest.skip.Exception): with pytest.raises(Skipped):
pytest.importorskip("mockmodule1", minversion="0.14.0") pytest.importorskip("mockmodule1", minversion="0.14.0")
except pytest.skip.Exception: except Skipped:
print(_pytest._code.ExceptionInfo.from_current()) print(_pytest._code.ExceptionInfo.from_current())
pytest.fail("spurious skip") pytest.fail("spurious skip")
def test_importorskip_module_level(testdir): def test_importorskip_module_level(testdir) -> None:
"""importorskip must be able to skip entire modules when used at module level""" """importorskip must be able to skip entire modules when used at module level"""
testdir.makepyfile( testdir.makepyfile(
""" """
@ -758,7 +778,7 @@ def test_importorskip_module_level(testdir):
result.stdout.fnmatch_lines(["*collected 0 items / 1 skipped*"]) result.stdout.fnmatch_lines(["*collected 0 items / 1 skipped*"])
def test_importorskip_custom_reason(testdir): def test_importorskip_custom_reason(testdir) -> None:
"""make sure custom reasons are used""" """make sure custom reasons are used"""
testdir.makepyfile( testdir.makepyfile(
""" """
@ -774,7 +794,7 @@ def test_importorskip_custom_reason(testdir):
result.stdout.fnmatch_lines(["*collected 0 items / 1 skipped*"]) result.stdout.fnmatch_lines(["*collected 0 items / 1 skipped*"])
def test_pytest_cmdline_main(testdir): def test_pytest_cmdline_main(testdir) -> None:
p = testdir.makepyfile( p = testdir.makepyfile(
""" """
import pytest import pytest
@ -792,7 +812,7 @@ def test_pytest_cmdline_main(testdir):
assert ret == 0 assert ret == 0
def test_unicode_in_longrepr(testdir): def test_unicode_in_longrepr(testdir) -> None:
testdir.makeconftest( testdir.makeconftest(
"""\ """\
import pytest import pytest
@ -815,7 +835,7 @@ def test_unicode_in_longrepr(testdir):
assert "UnicodeEncodeError" not in result.stderr.str() assert "UnicodeEncodeError" not in result.stderr.str()
def test_failure_in_setup(testdir): def test_failure_in_setup(testdir) -> None:
testdir.makepyfile( testdir.makepyfile(
""" """
def setup_module(): def setup_module():
@ -828,7 +848,7 @@ def test_failure_in_setup(testdir):
result.stdout.no_fnmatch_line("*def setup_module*") result.stdout.no_fnmatch_line("*def setup_module*")
def test_makereport_getsource(testdir): def test_makereport_getsource(testdir) -> None:
testdir.makepyfile( testdir.makepyfile(
""" """
def test_foo(): def test_foo():
@ -841,17 +861,17 @@ def test_makereport_getsource(testdir):
result.stdout.fnmatch_lines(["*else: assert False*"]) result.stdout.fnmatch_lines(["*else: assert False*"])
def test_makereport_getsource_dynamic_code(testdir, monkeypatch): def test_makereport_getsource_dynamic_code(testdir, monkeypatch) -> None:
"""Test that exception in dynamically generated code doesn't break getting the source line.""" """Test that exception in dynamically generated code doesn't break getting the source line."""
import inspect import inspect
original_findsource = inspect.findsource original_findsource = inspect.findsource
def findsource(obj, *args, **kwargs): def findsource(obj):
# Can be triggered by dynamically created functions # Can be triggered by dynamically created functions
if obj.__name__ == "foo": if obj.__name__ == "foo":
raise IndexError() raise IndexError()
return original_findsource(obj, *args, **kwargs) return original_findsource(obj)
monkeypatch.setattr(inspect, "findsource", findsource) monkeypatch.setattr(inspect, "findsource", findsource)
@ -872,7 +892,7 @@ def test_makereport_getsource_dynamic_code(testdir, monkeypatch):
result.stdout.fnmatch_lines(["*test_fix*", "*fixture*'missing'*not found*"]) result.stdout.fnmatch_lines(["*test_fix*", "*fixture*'missing'*not found*"])
def test_store_except_info_on_error(): def test_store_except_info_on_error() -> None:
""" Test that upon test failure, the exception info is stored on """ Test that upon test failure, the exception info is stored on
sys.last_traceback and friends. sys.last_traceback and friends.
""" """
@ -891,6 +911,7 @@ def test_store_except_info_on_error():
pass pass
# Check that exception info is stored on sys # Check that exception info is stored on sys
assert sys.last_type is IndexError assert sys.last_type is IndexError
assert isinstance(sys.last_value, IndexError)
assert sys.last_value.args[0] == "TEST" assert sys.last_value.args[0] == "TEST"
assert sys.last_traceback assert sys.last_traceback
@ -902,8 +923,8 @@ def test_store_except_info_on_error():
assert not hasattr(sys, "last_traceback") assert not hasattr(sys, "last_traceback")
def test_current_test_env_var(testdir, monkeypatch): def test_current_test_env_var(testdir, monkeypatch) -> None:
pytest_current_test_vars = [] pytest_current_test_vars = [] # type: List[Tuple[str, str]]
monkeypatch.setattr( monkeypatch.setattr(
sys, "pytest_current_test_vars", pytest_current_test_vars, raising=False sys, "pytest_current_test_vars", pytest_current_test_vars, raising=False
) )
@ -942,7 +963,7 @@ class TestReportContents:
def getrunner(self): def getrunner(self):
return lambda item: runner.runtestprotocol(item, log=False) return lambda item: runner.runtestprotocol(item, log=False)
def test_longreprtext_pass(self, testdir): def test_longreprtext_pass(self, testdir) -> None:
reports = testdir.runitem( reports = testdir.runitem(
""" """
def test_func(): def test_func():
@ -952,7 +973,7 @@ class TestReportContents:
rep = reports[1] rep = reports[1]
assert rep.longreprtext == "" assert rep.longreprtext == ""
def test_longreprtext_failure(self, testdir): def test_longreprtext_failure(self, testdir) -> None:
reports = testdir.runitem( reports = testdir.runitem(
""" """
def test_func(): def test_func():
@ -963,7 +984,7 @@ class TestReportContents:
rep = reports[1] rep = reports[1]
assert "assert 1 == 4" in rep.longreprtext assert "assert 1 == 4" in rep.longreprtext
def test_captured_text(self, testdir): def test_captured_text(self, testdir) -> None:
reports = testdir.runitem( reports = testdir.runitem(
""" """
import pytest import pytest
@ -993,7 +1014,7 @@ class TestReportContents:
assert call.capstderr == "setup: stderr\ncall: stderr\n" assert call.capstderr == "setup: stderr\ncall: stderr\n"
assert teardown.capstderr == "setup: stderr\ncall: stderr\nteardown: stderr\n" assert teardown.capstderr == "setup: stderr\ncall: stderr\nteardown: stderr\n"
def test_no_captured_text(self, testdir): def test_no_captured_text(self, testdir) -> None:
reports = testdir.runitem( reports = testdir.runitem(
""" """
def test_func(): def test_func():
@ -1005,10 +1026,10 @@ class TestReportContents:
assert rep.capstderr == "" assert rep.capstderr == ""
def test_outcome_exception_bad_msg(): def test_outcome_exception_bad_msg() -> None:
"""Check that OutcomeExceptions validate their input to prevent confusing errors (#5578)""" """Check that OutcomeExceptions validate their input to prevent confusing errors (#5578)"""
def func(): def func() -> None:
pass pass
expected = ( expected = (
@ -1016,5 +1037,5 @@ def test_outcome_exception_bad_msg():
"Perhaps you meant to use a mark?" "Perhaps you meant to use a mark?"
) )
with pytest.raises(TypeError) as excinfo: with pytest.raises(TypeError) as excinfo:
OutcomeException(func) OutcomeException(func) # type: ignore
assert str(excinfo.value) == expected assert str(excinfo.value) == expected