From 572dfcd160299489e66454de89a608da6f6d468e Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sat, 12 Dec 2020 08:49:58 -0300 Subject: [PATCH 001/630] Compare also paths on Windows when considering ImportPathMismatchError On Windows, os.path.samefile returns false for paths mounted in UNC paths which point to the same location. I couldn't reproduce the actual case reported, but looking at the code it seems this commit should fix the issue. Fix #7678 Fix #8076 --- changelog/7678.bugfix.rst | 2 ++ src/_pytest/pathlib.py | 16 +++++++++++++++- testing/test_pathlib.py | 21 +++++++++++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 changelog/7678.bugfix.rst diff --git a/changelog/7678.bugfix.rst b/changelog/7678.bugfix.rst new file mode 100644 index 00000000000..4adc6ffd119 --- /dev/null +++ b/changelog/7678.bugfix.rst @@ -0,0 +1,2 @@ +Fixed bug where ``ImportPathMismatchError`` would be raised for files compiled in +the host and loaded later from an UNC mounted path (Windows). diff --git a/src/_pytest/pathlib.py b/src/_pytest/pathlib.py index 6a36ae17ab2..8875a28f84b 100644 --- a/src/_pytest/pathlib.py +++ b/src/_pytest/pathlib.py @@ -543,7 +543,7 @@ def import_path( module_file = module_file[: -(len(os.path.sep + "__init__.py"))] try: - is_same = os.path.samefile(str(path), module_file) + is_same = _is_same(str(path), module_file) except FileNotFoundError: is_same = False @@ -553,6 +553,20 @@ def import_path( return mod +# Implement a special _is_same function on Windows which returns True if the two filenames +# compare equal, to circumvent os.path.samefile returning False for mounts in UNC (#7678). +if sys.platform.startswith("win"): + + def _is_same(f1: str, f2: str) -> bool: + return Path(f1) == Path(f2) or os.path.samefile(f1, f2) + + +else: + + def _is_same(f1: str, f2: str) -> bool: + return os.path.samefile(f1, f2) + + def resolve_package_path(path: Path) -> Optional[Path]: """Return the Python package path by looking for the last directory upwards which still contains an __init__.py. diff --git a/testing/test_pathlib.py b/testing/test_pathlib.py index 0507e3d6866..f60b9f26369 100644 --- a/testing/test_pathlib.py +++ b/testing/test_pathlib.py @@ -7,6 +7,7 @@ import py import pytest +from _pytest.monkeypatch import MonkeyPatch from _pytest.pathlib import bestrelpath from _pytest.pathlib import commonpath from _pytest.pathlib import ensure_deletable @@ -414,3 +415,23 @@ def test_visit_ignores_errors(tmpdir) -> None: "bar", "foo", ] + + +@pytest.mark.skipif(not sys.platform.startswith("win"), reason="Windows only") +def test_samefile_false_negatives(tmp_path: Path, monkeypatch: MonkeyPatch) -> None: + """ + import_file() should not raise ImportPathMismatchError if the paths are exactly + equal on Windows. It seems directories mounted as UNC paths make os.path.samefile + return False, even when they are clearly equal. + """ + module_path = tmp_path.joinpath("my_module.py") + module_path.write_text("def foo(): return 42") + monkeypatch.syspath_prepend(tmp_path) + + with monkeypatch.context() as mp: + # Forcibly make os.path.samefile() return False here to ensure we are comparing + # the paths too. Using a context to narrow the patch as much as possible given + # this is an important system function. + mp.setattr(os.path, "samefile", lambda x, y: False) + module = import_path(module_path) + assert getattr(module, "foo")() == 42 From ed658d682961a602305112bee72aa1e8843479f2 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Fri, 11 Dec 2020 13:40:37 +0200 Subject: [PATCH 002/630] Some py.path.local -> pathlib.Path - Some conftest related functions - _confcutdir - Allow arbitrary os.PathLike[str] in gethookproxy. --- src/_pytest/config/__init__.py | 70 +++++++++++---------- src/_pytest/doctest.py | 3 +- src/_pytest/main.py | 18 +++--- src/_pytest/nodes.py | 2 +- src/_pytest/python.py | 2 +- testing/python/fixtures.py | 12 ++-- testing/test_collection.py | 4 +- testing/test_config.py | 18 +++--- testing/test_conftest.py | 109 +++++++++++++++++---------------- testing/test_pluginmanager.py | 29 +++++---- 10 files changed, 141 insertions(+), 126 deletions(-) diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index bd9e2883f9f..0df4ffa01c1 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -50,9 +50,11 @@ from _pytest.compat import importlib_metadata from _pytest.outcomes import fail from _pytest.outcomes import Skipped +from _pytest.pathlib import absolutepath from _pytest.pathlib import bestrelpath from _pytest.pathlib import import_path from _pytest.pathlib import ImportMode +from _pytest.pathlib import resolve_package_path from _pytest.store import Store from _pytest.warning_types import PytestConfigWarning @@ -102,9 +104,7 @@ class ExitCode(enum.IntEnum): class ConftestImportFailure(Exception): def __init__( - self, - path: py.path.local, - excinfo: Tuple[Type[Exception], Exception, TracebackType], + self, path: Path, excinfo: Tuple[Type[Exception], Exception, TracebackType], ) -> None: super().__init__(path, excinfo) self.path = path @@ -342,9 +342,9 @@ def __init__(self) -> None: self._conftest_plugins: Set[types.ModuleType] = set() # State related to local conftest plugins. - self._dirpath2confmods: Dict[py.path.local, List[types.ModuleType]] = {} + self._dirpath2confmods: Dict[Path, List[types.ModuleType]] = {} self._conftestpath2mod: Dict[Path, types.ModuleType] = {} - self._confcutdir: Optional[py.path.local] = None + self._confcutdir: Optional[Path] = None self._noconftest = False self._duplicatepaths: Set[py.path.local] = set() @@ -479,9 +479,9 @@ def _set_initial_conftests(self, namespace: argparse.Namespace) -> None: All builtin and 3rd party plugins will have been loaded, however, so common options will not confuse our logic here. """ - current = py.path.local() + current = Path.cwd() self._confcutdir = ( - current.join(namespace.confcutdir, abs=True) + absolutepath(current / namespace.confcutdir) if namespace.confcutdir else None ) @@ -495,7 +495,7 @@ def _set_initial_conftests(self, namespace: argparse.Namespace) -> None: i = path.find("::") if i != -1: path = path[:i] - anchor = current.join(path, abs=1) + anchor = absolutepath(current / path) if anchor.exists(): # we found some file object self._try_load_conftest(anchor, namespace.importmode) foundanchor = True @@ -503,24 +503,24 @@ def _set_initial_conftests(self, namespace: argparse.Namespace) -> None: self._try_load_conftest(current, namespace.importmode) def _try_load_conftest( - self, anchor: py.path.local, importmode: Union[str, ImportMode] + self, anchor: Path, importmode: Union[str, ImportMode] ) -> None: self._getconftestmodules(anchor, importmode) # let's also consider test* subdirs - if anchor.check(dir=1): - for x in anchor.listdir("test*"): - if x.check(dir=1): + if anchor.is_dir(): + for x in anchor.glob("test*"): + if x.is_dir(): self._getconftestmodules(x, importmode) @lru_cache(maxsize=128) def _getconftestmodules( - self, path: py.path.local, importmode: Union[str, ImportMode], + self, path: Path, importmode: Union[str, ImportMode], ) -> List[types.ModuleType]: if self._noconftest: return [] - if path.isfile(): - directory = path.dirpath() + if path.is_file(): + directory = path.parent else: directory = path @@ -528,18 +528,18 @@ def _getconftestmodules( # and allow users to opt into looking into the rootdir parent # directories instead of requiring to specify confcutdir. clist = [] - for parent in directory.parts(): - if self._confcutdir and self._confcutdir.relto(parent): + for parent in reversed((directory, *directory.parents)): + if self._confcutdir and parent in self._confcutdir.parents: continue - conftestpath = parent.join("conftest.py") - if conftestpath.isfile(): + conftestpath = parent / "conftest.py" + if conftestpath.is_file(): mod = self._importconftest(conftestpath, importmode) clist.append(mod) self._dirpath2confmods[directory] = clist return clist def _rget_with_confmod( - self, name: str, path: py.path.local, importmode: Union[str, ImportMode], + self, name: str, path: Path, importmode: Union[str, ImportMode], ) -> Tuple[types.ModuleType, Any]: modules = self._getconftestmodules(path, importmode) for mod in reversed(modules): @@ -550,21 +550,21 @@ def _rget_with_confmod( raise KeyError(name) def _importconftest( - self, conftestpath: py.path.local, importmode: Union[str, ImportMode], + self, conftestpath: Path, importmode: Union[str, ImportMode], ) -> types.ModuleType: # Use a resolved Path object as key to avoid loading the same conftest # twice with build systems that create build directories containing # symlinks to actual files. # Using Path().resolve() is better than py.path.realpath because # it resolves to the correct path/drive in case-insensitive file systems (#5792) - key = Path(str(conftestpath)).resolve() + key = conftestpath.resolve() with contextlib.suppress(KeyError): return self._conftestpath2mod[key] - pkgpath = conftestpath.pypkgpath() + pkgpath = resolve_package_path(conftestpath) if pkgpath is None: - _ensure_removed_sysmodule(conftestpath.purebasename) + _ensure_removed_sysmodule(conftestpath.stem) try: mod = import_path(conftestpath, mode=importmode) @@ -577,10 +577,10 @@ def _importconftest( self._conftest_plugins.add(mod) self._conftestpath2mod[key] = mod - dirpath = conftestpath.dirpath() + dirpath = conftestpath.parent if dirpath in self._dirpath2confmods: for path, mods in self._dirpath2confmods.items(): - if path and path.relto(dirpath) or path == dirpath: + if path and dirpath in path.parents or path == dirpath: assert mod not in mods mods.append(mod) self.trace(f"loading conftestmodule {mod!r}") @@ -588,7 +588,7 @@ def _importconftest( return mod def _check_non_top_pytest_plugins( - self, mod: types.ModuleType, conftestpath: py.path.local, + self, mod: types.ModuleType, conftestpath: Path, ) -> None: if ( hasattr(mod, "pytest_plugins") @@ -1412,21 +1412,23 @@ def _getini(self, name: str): assert type in [None, "string"] return value - def _getconftest_pathlist( - self, name: str, path: py.path.local - ) -> Optional[List[py.path.local]]: + def _getconftest_pathlist(self, name: str, path: Path) -> Optional[List[Path]]: try: mod, relroots = self.pluginmanager._rget_with_confmod( name, path, self.getoption("importmode") ) except KeyError: return None - modpath = py.path.local(mod.__file__).dirpath() - values: List[py.path.local] = [] + modpath = Path(mod.__file__).parent + values: List[Path] = [] for relroot in relroots: - if not isinstance(relroot, py.path.local): + if isinstance(relroot, Path): + pass + elif isinstance(relroot, py.path.local): + relroot = Path(relroot) + else: relroot = relroot.replace("/", os.sep) - relroot = modpath.join(relroot, abs=True) + relroot = absolutepath(modpath / relroot) values.append(relroot) return values diff --git a/src/_pytest/doctest.py b/src/_pytest/doctest.py index 64e8f0e0eee..d0b6b4c4185 100644 --- a/src/_pytest/doctest.py +++ b/src/_pytest/doctest.py @@ -7,6 +7,7 @@ import types import warnings from contextlib import contextmanager +from pathlib import Path from typing import Any from typing import Callable from typing import Dict @@ -525,7 +526,7 @@ def _find( if self.fspath.basename == "conftest.py": module = self.config.pluginmanager._importconftest( - self.fspath, self.config.getoption("importmode") + Path(self.fspath), self.config.getoption("importmode") ) else: try: diff --git a/src/_pytest/main.py b/src/_pytest/main.py index 41a33d4494c..eab3c9afd27 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -371,22 +371,23 @@ def _in_venv(path: py.path.local) -> bool: def pytest_ignore_collect(path: py.path.local, config: Config) -> Optional[bool]: - ignore_paths = config._getconftest_pathlist("collect_ignore", path=path.dirpath()) + path_ = Path(path) + ignore_paths = config._getconftest_pathlist("collect_ignore", path=path_.parent) ignore_paths = ignore_paths or [] excludeopt = config.getoption("ignore") if excludeopt: - ignore_paths.extend([py.path.local(x) for x in excludeopt]) + ignore_paths.extend(absolutepath(x) for x in excludeopt) - if py.path.local(path) in ignore_paths: + if path_ in ignore_paths: return True ignore_globs = config._getconftest_pathlist( - "collect_ignore_glob", path=path.dirpath() + "collect_ignore_glob", path=path_.parent ) ignore_globs = ignore_globs or [] excludeglobopt = config.getoption("ignore_glob") if excludeglobopt: - ignore_globs.extend([py.path.local(x) for x in excludeglobopt]) + ignore_globs.extend(absolutepath(x) for x in excludeglobopt) if any(fnmatch.fnmatch(str(path), str(glob)) for glob in ignore_globs): return True @@ -512,12 +513,12 @@ def pytest_runtest_logreport( def isinitpath(self, path: py.path.local) -> bool: return path in self._initialpaths - def gethookproxy(self, fspath: py.path.local): + def gethookproxy(self, fspath: "os.PathLike[str]"): # Check if we have the common case of running # hooks with all conftest.py files. pm = self.config.pluginmanager my_conftestmodules = pm._getconftestmodules( - fspath, self.config.getoption("importmode") + Path(fspath), self.config.getoption("importmode") ) remove_mods = pm._conftest_plugins.difference(my_conftestmodules) if remove_mods: @@ -668,8 +669,9 @@ def collect(self) -> Iterator[Union[nodes.Item, nodes.Collector]]: # No point in finding packages when collecting doctests. if not self.config.getoption("doctestmodules", False): pm = self.config.pluginmanager + confcutdir = py.path.local(pm._confcutdir) if pm._confcutdir else None for parent in reversed(argpath.parts()): - if pm._confcutdir and pm._confcutdir.relto(parent): + if confcutdir and confcutdir.relto(parent): break if parent.isdir(): diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py index 27434fb6a67..98bd581b96d 100644 --- a/src/_pytest/nodes.py +++ b/src/_pytest/nodes.py @@ -520,7 +520,7 @@ def from_parent(cls, parent, *, fspath, **kw): """The public constructor.""" return super().from_parent(parent=parent, fspath=fspath, **kw) - def gethookproxy(self, fspath: py.path.local): + def gethookproxy(self, fspath: "os.PathLike[str]"): warnings.warn(FSCOLLECTOR_GETHOOKPROXY_ISINITPATH, stacklevel=2) return self.session.gethookproxy(fspath) diff --git a/src/_pytest/python.py b/src/_pytest/python.py index e48e7531c19..407f924a5f1 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -653,7 +653,7 @@ def setup(self) -> None: func = partial(_call_with_optional_argument, teardown_module, self.obj) self.addfinalizer(func) - def gethookproxy(self, fspath: py.path.local): + def gethookproxy(self, fspath: "os.PathLike[str]"): warnings.warn(FSCOLLECTOR_GETHOOKPROXY_ISINITPATH, stacklevel=2) return self.session.gethookproxy(fspath) diff --git a/testing/python/fixtures.py b/testing/python/fixtures.py index 94547dd245c..ac62de608e5 100644 --- a/testing/python/fixtures.py +++ b/testing/python/fixtures.py @@ -8,6 +8,7 @@ from _pytest.config import ExitCode from _pytest.fixtures import FixtureRequest from _pytest.pytester import get_public_names +from _pytest.pytester import Pytester from _pytest.pytester import Testdir @@ -1961,8 +1962,10 @@ def test_result(arg): reprec = testdir.inline_run("-v", "-s") reprec.assertoutcome(passed=4) - def test_class_function_parametrization_finalization(self, testdir): - p = testdir.makeconftest( + def test_class_function_parametrization_finalization( + self, pytester: Pytester + ) -> None: + p = pytester.makeconftest( """ import pytest import pprint @@ -1984,7 +1987,7 @@ def fin(): request.addfinalizer(fin) """ ) - testdir.makepyfile( + pytester.makepyfile( """ import pytest @@ -1996,8 +1999,7 @@ def test_2(self): pass """ ) - confcut = f"--confcutdir={testdir.tmpdir}" - reprec = testdir.inline_run("-v", "-s", confcut) + reprec = pytester.inline_run("-v", "-s", "--confcutdir", pytester.path) reprec.assertoutcome(passed=8) config = reprec.getcalls("pytest_unconfigure")[0].config values = config.pluginmanager._getconftestmodules(p, importmode="prepend")[ diff --git a/testing/test_collection.py b/testing/test_collection.py index 1138c2bd6f5..862c1aba8d2 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -364,7 +364,9 @@ def pytest_ignore_collect(path, config): def test_collectignore_exclude_on_option(self, pytester: Pytester) -> None: pytester.makeconftest( """ - collect_ignore = ['hello', 'test_world.py'] + import py + from pathlib import Path + collect_ignore = [py.path.local('hello'), 'test_world.py', Path('bye')] def pytest_addoption(parser): parser.addoption("--XX", action="store_true", default=False) def pytest_configure(config): diff --git a/testing/test_config.py b/testing/test_config.py index b931797d429..eacc9c9ebdd 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -574,16 +574,16 @@ def test_getoption(self, pytester: Pytester) -> None: config.getvalue("x") assert config.getoption("x", 1) == 1 - def test_getconftest_pathlist(self, pytester: Pytester, tmpdir) -> None: - somepath = tmpdir.join("x", "y", "z") - p = tmpdir.join("conftest.py") - p.write("pathlist = ['.', %r]" % str(somepath)) + def test_getconftest_pathlist(self, pytester: Pytester, tmp_path: Path) -> None: + somepath = tmp_path.joinpath("x", "y", "z") + p = tmp_path.joinpath("conftest.py") + p.write_text(f"pathlist = ['.', {str(somepath)!r}]") config = pytester.parseconfigure(p) - assert config._getconftest_pathlist("notexist", path=tmpdir) is None - pl = config._getconftest_pathlist("pathlist", path=tmpdir) or [] + assert config._getconftest_pathlist("notexist", path=tmp_path) is None + pl = config._getconftest_pathlist("pathlist", path=tmp_path) or [] print(pl) assert len(pl) == 2 - assert pl[0] == tmpdir + assert pl[0] == tmp_path assert pl[1] == somepath @pytest.mark.parametrize("maybe_type", ["not passed", "None", '"string"']) @@ -1935,10 +1935,10 @@ def test_pytest_plugins_in_non_top_level_conftest_unsupported_no_false_positives assert msg not in res.stdout.str() -def test_conftest_import_error_repr(tmpdir: py.path.local) -> None: +def test_conftest_import_error_repr(tmp_path: Path) -> None: """`ConftestImportFailure` should use a short error message and readable path to the failed conftest.py file.""" - path = tmpdir.join("foo/conftest.py") + path = tmp_path.joinpath("foo/conftest.py") with pytest.raises( ConftestImportFailure, match=re.escape(f"RuntimeError: some error (from {path})"), diff --git a/testing/test_conftest.py b/testing/test_conftest.py index 638321728d7..36e83191bcd 100644 --- a/testing/test_conftest.py +++ b/testing/test_conftest.py @@ -4,6 +4,7 @@ from pathlib import Path from typing import cast from typing import Dict +from typing import Generator from typing import List from typing import Optional @@ -15,7 +16,7 @@ from _pytest.monkeypatch import MonkeyPatch from _pytest.pathlib import symlink_or_skip from _pytest.pytester import Pytester -from _pytest.pytester import Testdir +from _pytest.tmpdir import TempPathFactory def ConftestWithSetinitial(path) -> PytestPluginManager: @@ -25,12 +26,12 @@ def ConftestWithSetinitial(path) -> PytestPluginManager: def conftest_setinitial( - conftest: PytestPluginManager, args, confcutdir: Optional[py.path.local] = None + conftest: PytestPluginManager, args, confcutdir: Optional["os.PathLike[str]"] = None ) -> None: class Namespace: def __init__(self) -> None: self.file_or_dir = args - self.confcutdir = str(confcutdir) + self.confcutdir = os.fspath(confcutdir) if confcutdir is not None else None self.noconftest = False self.pyargs = False self.importmode = "prepend" @@ -42,54 +43,58 @@ def __init__(self) -> None: @pytest.mark.usefixtures("_sys_snapshot") class TestConftestValueAccessGlobal: @pytest.fixture(scope="module", params=["global", "inpackage"]) - def basedir(self, request, tmpdir_factory): - tmpdir = tmpdir_factory.mktemp("basedir", numbered=True) - tmpdir.ensure("adir/conftest.py").write("a=1 ; Directory = 3") - tmpdir.ensure("adir/b/conftest.py").write("b=2 ; a = 1.5") + def basedir( + self, request, tmp_path_factory: TempPathFactory + ) -> Generator[Path, None, None]: + tmpdir = tmp_path_factory.mktemp("basedir", numbered=True) + tmpdir.joinpath("adir/b").mkdir(parents=True) + tmpdir.joinpath("adir/conftest.py").write_text("a=1 ; Directory = 3") + tmpdir.joinpath("adir/b/conftest.py").write_text("b=2 ; a = 1.5") if request.param == "inpackage": - tmpdir.ensure("adir/__init__.py") - tmpdir.ensure("adir/b/__init__.py") + tmpdir.joinpath("adir/__init__.py").touch() + tmpdir.joinpath("adir/b/__init__.py").touch() yield tmpdir - def test_basic_init(self, basedir): + def test_basic_init(self, basedir: Path) -> None: conftest = PytestPluginManager() - p = basedir.join("adir") + p = basedir / "adir" assert conftest._rget_with_confmod("a", p, importmode="prepend")[1] == 1 - def test_immediate_initialiation_and_incremental_are_the_same(self, basedir): + def test_immediate_initialiation_and_incremental_are_the_same( + self, basedir: Path + ) -> None: conftest = PytestPluginManager() assert not len(conftest._dirpath2confmods) conftest._getconftestmodules(basedir, importmode="prepend") snap1 = len(conftest._dirpath2confmods) assert snap1 == 1 - conftest._getconftestmodules(basedir.join("adir"), importmode="prepend") + conftest._getconftestmodules(basedir / "adir", importmode="prepend") assert len(conftest._dirpath2confmods) == snap1 + 1 - conftest._getconftestmodules(basedir.join("b"), importmode="prepend") + conftest._getconftestmodules(basedir / "b", importmode="prepend") assert len(conftest._dirpath2confmods) == snap1 + 2 - def test_value_access_not_existing(self, basedir): + def test_value_access_not_existing(self, basedir: Path) -> None: conftest = ConftestWithSetinitial(basedir) with pytest.raises(KeyError): conftest._rget_with_confmod("a", basedir, importmode="prepend") - def test_value_access_by_path(self, basedir): + def test_value_access_by_path(self, basedir: Path) -> None: conftest = ConftestWithSetinitial(basedir) - adir = basedir.join("adir") + adir = basedir / "adir" assert conftest._rget_with_confmod("a", adir, importmode="prepend")[1] == 1 assert ( - conftest._rget_with_confmod("a", adir.join("b"), importmode="prepend")[1] - == 1.5 + conftest._rget_with_confmod("a", adir / "b", importmode="prepend")[1] == 1.5 ) - def test_value_access_with_confmod(self, basedir): - startdir = basedir.join("adir", "b") - startdir.ensure("xx", dir=True) + def test_value_access_with_confmod(self, basedir: Path) -> None: + startdir = basedir / "adir" / "b" + startdir.joinpath("xx").mkdir() conftest = ConftestWithSetinitial(startdir) mod, value = conftest._rget_with_confmod("a", startdir, importmode="prepend") assert value == 1.5 path = py.path.local(mod.__file__) - assert path.dirpath() == basedir.join("adir", "b") + assert path.dirpath() == basedir / "adir" / "b" assert path.purebasename.startswith("conftest") @@ -102,12 +107,12 @@ def test_conftest_in_nonpkg_with_init(tmp_path: Path, _sys_snapshot) -> None: ConftestWithSetinitial(tmp_path.joinpath("adir-1.0", "b")) -def test_doubledash_considered(testdir: Testdir) -> None: - conf = testdir.mkdir("--option") - conf.join("conftest.py").ensure() +def test_doubledash_considered(pytester: Pytester) -> None: + conf = pytester.mkdir("--option") + conf.joinpath("conftest.py").touch() conftest = PytestPluginManager() - conftest_setinitial(conftest, [conf.basename, conf.basename]) - values = conftest._getconftestmodules(py.path.local(conf), importmode="prepend") + conftest_setinitial(conftest, [conf.name, conf.name]) + values = conftest._getconftestmodules(conf, importmode="prepend") assert len(values) == 1 @@ -127,15 +132,18 @@ def test_conftest_global_import(pytester: Pytester) -> None: pytester.makeconftest("x=3") p = pytester.makepyfile( """ - import py, pytest + from pathlib import Path + import pytest from _pytest.config import PytestPluginManager conf = PytestPluginManager() - mod = conf._importconftest(py.path.local("conftest.py"), importmode="prepend") + mod = conf._importconftest(Path("conftest.py"), importmode="prepend") assert mod.x == 3 import conftest assert conftest is mod, (conftest, mod) - subconf = py.path.local().ensure("sub", "conftest.py") - subconf.write("y=4") + sub = Path("sub") + sub.mkdir() + subconf = sub / "conftest.py" + subconf.write_text("y=4") mod2 = conf._importconftest(subconf, importmode="prepend") assert mod != mod2 assert mod2.y == 4 @@ -147,19 +155,19 @@ def test_conftest_global_import(pytester: Pytester) -> None: assert res.ret == 0 -def test_conftestcutdir(testdir: Testdir) -> None: - conf = testdir.makeconftest("") - p = testdir.mkdir("x") +def test_conftestcutdir(pytester: Pytester) -> None: + conf = pytester.makeconftest("") + p = pytester.mkdir("x") conftest = PytestPluginManager() - conftest_setinitial(conftest, [testdir.tmpdir], confcutdir=p) + conftest_setinitial(conftest, [pytester.path], confcutdir=p) values = conftest._getconftestmodules(p, importmode="prepend") assert len(values) == 0 - values = conftest._getconftestmodules(conf.dirpath(), importmode="prepend") + values = conftest._getconftestmodules(conf.parent, importmode="prepend") assert len(values) == 0 assert Path(conf) not in conftest._conftestpath2mod # but we can still import a conftest directly conftest._importconftest(conf, importmode="prepend") - values = conftest._getconftestmodules(conf.dirpath(), importmode="prepend") + values = conftest._getconftestmodules(conf.parent, importmode="prepend") assert values[0].__file__.startswith(str(conf)) # and all sub paths get updated properly values = conftest._getconftestmodules(p, importmode="prepend") @@ -170,10 +178,8 @@ def test_conftestcutdir(testdir: Testdir) -> None: def test_conftestcutdir_inplace_considered(pytester: Pytester) -> None: conf = pytester.makeconftest("") conftest = PytestPluginManager() - conftest_setinitial(conftest, [conf.parent], confcutdir=py.path.local(conf.parent)) - values = conftest._getconftestmodules( - py.path.local(conf.parent), importmode="prepend" - ) + conftest_setinitial(conftest, [conf.parent], confcutdir=conf.parent) + values = conftest._getconftestmodules(conf.parent, importmode="prepend") assert len(values) == 1 assert values[0].__file__.startswith(str(conf)) @@ -184,7 +190,7 @@ def test_setinitial_conftest_subdirs(pytester: Pytester, name: str) -> None: subconftest = sub.joinpath("conftest.py") subconftest.touch() conftest = PytestPluginManager() - conftest_setinitial(conftest, [sub.parent], confcutdir=py.path.local(pytester.path)) + conftest_setinitial(conftest, [sub.parent], confcutdir=pytester.path) key = subconftest.resolve() if name not in ("whatever", ".dotdir"): assert key in conftest._conftestpath2mod @@ -337,22 +343,19 @@ def pytest_addoption(parser): result.stdout.fnmatch_lines(["*--xyz*"]) -def test_conftest_import_order(testdir: Testdir, monkeypatch: MonkeyPatch) -> None: - ct1 = testdir.makeconftest("") - sub = testdir.mkdir("sub") - ct2 = sub.join("conftest.py") - ct2.write("") +def test_conftest_import_order(pytester: Pytester, monkeypatch: MonkeyPatch) -> None: + ct1 = pytester.makeconftest("") + sub = pytester.mkdir("sub") + ct2 = sub / "conftest.py" + ct2.write_text("") def impct(p, importmode): return p conftest = PytestPluginManager() - conftest._confcutdir = testdir.tmpdir + conftest._confcutdir = pytester.path monkeypatch.setattr(conftest, "_importconftest", impct) - mods = cast( - List[py.path.local], - conftest._getconftestmodules(py.path.local(sub), importmode="prepend"), - ) + mods = cast(List[Path], conftest._getconftestmodules(sub, importmode="prepend")) expected = [ct1, ct2] assert mods == expected diff --git a/testing/test_pluginmanager.py b/testing/test_pluginmanager.py index 2099f5ae1e3..89f10a7db64 100644 --- a/testing/test_pluginmanager.py +++ b/testing/test_pluginmanager.py @@ -8,6 +8,7 @@ from _pytest.config import PytestPluginManager from _pytest.config.exceptions import UsageError from _pytest.main import Session +from _pytest.pytester import Pytester @pytest.fixture @@ -16,14 +17,16 @@ def pytestpm() -> PytestPluginManager: class TestPytestPluginInteractions: - def test_addhooks_conftestplugin(self, testdir, _config_for_test): - testdir.makepyfile( + def test_addhooks_conftestplugin( + self, pytester: Pytester, _config_for_test + ) -> None: + pytester.makepyfile( newhooks=""" def pytest_myhook(xyz): "new hook" """ ) - conf = testdir.makeconftest( + conf = pytester.makeconftest( """ import newhooks def pytest_addhooks(pluginmanager): @@ -54,10 +57,10 @@ def pytest_addhooks(pluginmanager): assert res.ret != 0 res.stderr.fnmatch_lines(["*did not find*sys*"]) - def test_do_option_postinitialize(self, testdir): - config = testdir.parseconfigure() + def test_do_option_postinitialize(self, pytester: Pytester) -> None: + config = pytester.parseconfigure() assert not hasattr(config.option, "test123") - p = testdir.makepyfile( + p = pytester.makepyfile( """ def pytest_addoption(parser): parser.addoption('--test123', action="store_true", @@ -120,20 +123,20 @@ def pytest_plugin_registered(self): finally: undo() - def test_hook_proxy(self, testdir): + def test_hook_proxy(self, pytester: Pytester) -> None: """Test the gethookproxy function(#2016)""" - config = testdir.parseconfig() + config = pytester.parseconfig() session = Session.from_config(config) - testdir.makepyfile(**{"tests/conftest.py": "", "tests/subdir/conftest.py": ""}) + pytester.makepyfile(**{"tests/conftest.py": "", "tests/subdir/conftest.py": ""}) - conftest1 = testdir.tmpdir.join("tests/conftest.py") - conftest2 = testdir.tmpdir.join("tests/subdir/conftest.py") + conftest1 = pytester.path.joinpath("tests/conftest.py") + conftest2 = pytester.path.joinpath("tests/subdir/conftest.py") config.pluginmanager._importconftest(conftest1, importmode="prepend") - ihook_a = session.gethookproxy(testdir.tmpdir.join("tests")) + ihook_a = session.gethookproxy(pytester.path / "tests") assert ihook_a is not None config.pluginmanager._importconftest(conftest2, importmode="prepend") - ihook_b = session.gethookproxy(testdir.tmpdir.join("tests")) + ihook_b = session.gethookproxy(pytester.path / "tests") assert ihook_a is not ihook_b def test_hook_with_addoption(self, testdir): From 54a7356a9fb165d7d3e48153ede01cb62f05ecee Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sat, 12 Dec 2020 23:21:28 +0200 Subject: [PATCH 003/630] Merge pull request #8130 from pytest-dev/release-6.2.0 Prepare release 6.2.0 (cherry picked from commit c475106f12ed87fe908544ff383c5205638c086d) --- changelog/1265.improvement.rst | 1 - changelog/2044.improvement.rst | 1 - changelog/4824.bugfix.rst | 1 - changelog/5299.feature.rst | 2 - changelog/7425.feature.rst | 5 - changelog/7429.doc.rst | 1 - changelog/7469.deprecation.rst | 18 --- changelog/7469.improvement.rst | 23 ---- changelog/7527.improvement.rst | 1 - changelog/7530.deprecation.rst | 4 - changelog/7615.improvement.rst | 1 - changelog/7695.feature.rst | 19 --- changelog/7701.improvement.rst | 1 - changelog/7710.improvement.rst | 4 - changelog/7758.bugfix.rst | 1 - changelog/7780.doc.rst | 1 - changelog/7802.trivial.rst | 1 - changelog/7808.breaking.rst | 1 - changelog/7872.doc.rst | 1 - changelog/7878.doc.rst | 1 - changelog/7911.bugfix.rst | 1 - changelog/7913.bugfix.rst | 1 - changelog/7938.improvement.rst | 1 - changelog/7951.bugfix.rst | 1 - changelog/7981.bugfix.rst | 1 - changelog/7988.deprecation.rst | 3 - changelog/8006.feature.rst | 8 -- changelog/8014.trivial.rst | 2 - changelog/8016.bugfix.rst | 1 - changelog/8023.improvement.rst | 1 - changelog/8032.improvement.rst | 1 - doc/en/announce/index.rst | 1 + doc/en/announce/release-6.2.0.rst | 76 +++++++++++ doc/en/builtin.rst | 10 ++ doc/en/changelog.rst | 197 ++++++++++++++++++++++++++++ doc/en/example/nonpython.rst | 2 +- doc/en/example/parametrize.rst | 6 +- doc/en/example/pythoncollection.rst | 6 +- doc/en/example/reportingdemo.rst | 2 +- doc/en/fixture.rst | 4 +- doc/en/getting-started.rst | 2 +- doc/en/reference.rst | 7 +- doc/en/tmpdir.rst | 4 +- doc/en/writing_plugins.rst | 8 +- 44 files changed, 302 insertions(+), 132 deletions(-) delete mode 100644 changelog/1265.improvement.rst delete mode 100644 changelog/2044.improvement.rst delete mode 100644 changelog/4824.bugfix.rst delete mode 100644 changelog/5299.feature.rst delete mode 100644 changelog/7425.feature.rst delete mode 100644 changelog/7429.doc.rst delete mode 100644 changelog/7469.deprecation.rst delete mode 100644 changelog/7469.improvement.rst delete mode 100644 changelog/7527.improvement.rst delete mode 100644 changelog/7530.deprecation.rst delete mode 100644 changelog/7615.improvement.rst delete mode 100644 changelog/7695.feature.rst delete mode 100644 changelog/7701.improvement.rst delete mode 100644 changelog/7710.improvement.rst delete mode 100644 changelog/7758.bugfix.rst delete mode 100644 changelog/7780.doc.rst delete mode 100644 changelog/7802.trivial.rst delete mode 100644 changelog/7808.breaking.rst delete mode 100644 changelog/7872.doc.rst delete mode 100644 changelog/7878.doc.rst delete mode 100644 changelog/7911.bugfix.rst delete mode 100644 changelog/7913.bugfix.rst delete mode 100644 changelog/7938.improvement.rst delete mode 100644 changelog/7951.bugfix.rst delete mode 100644 changelog/7981.bugfix.rst delete mode 100644 changelog/7988.deprecation.rst delete mode 100644 changelog/8006.feature.rst delete mode 100644 changelog/8014.trivial.rst delete mode 100644 changelog/8016.bugfix.rst delete mode 100644 changelog/8023.improvement.rst delete mode 100644 changelog/8032.improvement.rst create mode 100644 doc/en/announce/release-6.2.0.rst diff --git a/changelog/1265.improvement.rst b/changelog/1265.improvement.rst deleted file mode 100644 index 4e7d98c0a99..00000000000 --- a/changelog/1265.improvement.rst +++ /dev/null @@ -1 +0,0 @@ -Added an ``__str__`` implementation to the :class:`~pytest.pytester.LineMatcher` class which is returned from ``pytester.run_pytest().stdout`` and similar. It returns the entire output, like the existing ``str()`` method. diff --git a/changelog/2044.improvement.rst b/changelog/2044.improvement.rst deleted file mode 100644 index c9e47c3f604..00000000000 --- a/changelog/2044.improvement.rst +++ /dev/null @@ -1 +0,0 @@ -Verbose mode now shows the reason that a test was skipped in the test's terminal line after the "SKIPPED", "XFAIL" or "XPASS". diff --git a/changelog/4824.bugfix.rst b/changelog/4824.bugfix.rst deleted file mode 100644 index f2e6db7ab0f..00000000000 --- a/changelog/4824.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed quadratic behavior and improved performance of collection of items using autouse fixtures and xunit fixtures. diff --git a/changelog/5299.feature.rst b/changelog/5299.feature.rst deleted file mode 100644 index 7853e1833db..00000000000 --- a/changelog/5299.feature.rst +++ /dev/null @@ -1,2 +0,0 @@ -pytest now warns about unraisable exceptions and unhandled thread exceptions that occur in tests on Python>=3.8. -See :ref:`unraisable` for more information. diff --git a/changelog/7425.feature.rst b/changelog/7425.feature.rst deleted file mode 100644 index 47e6f4dbd30..00000000000 --- a/changelog/7425.feature.rst +++ /dev/null @@ -1,5 +0,0 @@ -New :fixture:`pytester` fixture, which is identical to :fixture:`testdir` but its methods return :class:`pathlib.Path` when appropriate instead of ``py.path.local``. - -This is part of the movement to use :class:`pathlib.Path` objects internally, in order to remove the dependency to ``py`` in the future. - -Internally, the old :class:`Testdir <_pytest.pytester.Testdir>` is now a thin wrapper around :class:`Pytester <_pytest.pytester.Pytester>`, preserving the old interface. diff --git a/changelog/7429.doc.rst b/changelog/7429.doc.rst deleted file mode 100644 index e6376b727b2..00000000000 --- a/changelog/7429.doc.rst +++ /dev/null @@ -1 +0,0 @@ -Add more information and use cases about skipping doctests. diff --git a/changelog/7469.deprecation.rst b/changelog/7469.deprecation.rst deleted file mode 100644 index 67d0b2bba46..00000000000 --- a/changelog/7469.deprecation.rst +++ /dev/null @@ -1,18 +0,0 @@ -Directly constructing/calling the following classes/functions is now deprecated: - -- ``_pytest.cacheprovider.Cache`` -- ``_pytest.cacheprovider.Cache.for_config()`` -- ``_pytest.cacheprovider.Cache.clear_cache()`` -- ``_pytest.cacheprovider.Cache.cache_dir_from_config()`` -- ``_pytest.capture.CaptureFixture`` -- ``_pytest.fixtures.FixtureRequest`` -- ``_pytest.fixtures.SubRequest`` -- ``_pytest.logging.LogCaptureFixture`` -- ``_pytest.pytester.Pytester`` -- ``_pytest.pytester.Testdir`` -- ``_pytest.recwarn.WarningsRecorder`` -- ``_pytest.recwarn.WarningsChecker`` -- ``_pytest.tmpdir.TempPathFactory`` -- ``_pytest.tmpdir.TempdirFactory`` - -These have always been considered private, but now issue a deprecation warning, which may become a hard error in pytest 7.0.0. diff --git a/changelog/7469.improvement.rst b/changelog/7469.improvement.rst deleted file mode 100644 index cbd75f05419..00000000000 --- a/changelog/7469.improvement.rst +++ /dev/null @@ -1,23 +0,0 @@ -It is now possible to construct a :class:`MonkeyPatch` object directly as ``pytest.MonkeyPatch()``, -in cases when the :fixture:`monkeypatch` fixture cannot be used. Previously some users imported it -from the private `_pytest.monkeypatch.MonkeyPatch` namespace. - -The types of builtin pytest fixtures are now exported so they may be used in type annotations of test functions. -The newly-exported types are: - -- ``pytest.FixtureRequest`` for the :fixture:`request` fixture. -- ``pytest.Cache`` for the :fixture:`cache` fixture. -- ``pytest.CaptureFixture[str]`` for the :fixture:`capfd` and :fixture:`capsys` fixtures. -- ``pytest.CaptureFixture[bytes]`` for the :fixture:`capfdbinary` and :fixture:`capsysbinary` fixtures. -- ``pytest.LogCaptureFixture`` for the :fixture:`caplog` fixture. -- ``pytest.Pytester`` for the :fixture:`pytester` fixture. -- ``pytest.Testdir`` for the :fixture:`testdir` fixture. -- ``pytest.TempdirFactory`` for the :fixture:`tmpdir_factory` fixture. -- ``pytest.TempPathFactory`` for the :fixture:`tmp_path_factory` fixture. -- ``pytest.MonkeyPatch`` for the :fixture:`monkeypatch` fixture. -- ``pytest.WarningsRecorder`` for the :fixture:`recwarn` fixture. - -Constructing them is not supported (except for `MonkeyPatch`); they are only meant for use in type annotations. -Doing so will emit a deprecation warning, and may become a hard-error in pytest 7.0. - -Subclassing them is also not supported. This is not currently enforced at runtime, but is detected by type-checkers such as mypy. diff --git a/changelog/7527.improvement.rst b/changelog/7527.improvement.rst deleted file mode 100644 index 3a7e063fe6f..00000000000 --- a/changelog/7527.improvement.rst +++ /dev/null @@ -1 +0,0 @@ -When a comparison between :func:`namedtuple ` instances of the same type fails, pytest now shows the differing field names (possibly nested) instead of their indexes. diff --git a/changelog/7530.deprecation.rst b/changelog/7530.deprecation.rst deleted file mode 100644 index 36a763e51f1..00000000000 --- a/changelog/7530.deprecation.rst +++ /dev/null @@ -1,4 +0,0 @@ -The ``--strict`` command-line option has been deprecated, use ``--strict-markers`` instead. - -We have plans to maybe in the future to reintroduce ``--strict`` and make it an encompassing flag for all strictness -related options (``--strict-markers`` and ``--strict-config`` at the moment, more might be introduced in the future). diff --git a/changelog/7615.improvement.rst b/changelog/7615.improvement.rst deleted file mode 100644 index fcf9a1a9b42..00000000000 --- a/changelog/7615.improvement.rst +++ /dev/null @@ -1 +0,0 @@ -:meth:`Node.warn <_pytest.nodes.Node.warn>` now permits any subclass of :class:`Warning`, not just :class:`PytestWarning `. diff --git a/changelog/7695.feature.rst b/changelog/7695.feature.rst deleted file mode 100644 index ec8632fc82a..00000000000 --- a/changelog/7695.feature.rst +++ /dev/null @@ -1,19 +0,0 @@ -A new hook was added, `pytest_markeval_namespace` which should return a dictionary. -This dictionary will be used to augment the "global" variables available to evaluate skipif/xfail/xpass markers. - -Pseudo example - -``conftest.py``: - -.. code-block:: python - - def pytest_markeval_namespace(): - return {"color": "red"} - -``test_func.py``: - -.. code-block:: python - - @pytest.mark.skipif("color == 'blue'", reason="Color is not red") - def test_func(): - assert False diff --git a/changelog/7701.improvement.rst b/changelog/7701.improvement.rst deleted file mode 100644 index e214be9e3fe..00000000000 --- a/changelog/7701.improvement.rst +++ /dev/null @@ -1 +0,0 @@ -Improved reporting when using ``--collected-only``. It will now show the number of collected tests in the summary stats. diff --git a/changelog/7710.improvement.rst b/changelog/7710.improvement.rst deleted file mode 100644 index 91b703ab60f..00000000000 --- a/changelog/7710.improvement.rst +++ /dev/null @@ -1,4 +0,0 @@ -Use strict equality comparison for non-numeric types in :func:`pytest.approx` instead of -raising :class:`TypeError`. - -This was the undocumented behavior before 3.7, but is now officially a supported feature. diff --git a/changelog/7758.bugfix.rst b/changelog/7758.bugfix.rst deleted file mode 100644 index a3119b46c0d..00000000000 --- a/changelog/7758.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed an issue where some files in packages are getting lost from ``--lf`` even though they contain tests that failed. Regressed in pytest 5.4.0. diff --git a/changelog/7780.doc.rst b/changelog/7780.doc.rst deleted file mode 100644 index 631873b156e..00000000000 --- a/changelog/7780.doc.rst +++ /dev/null @@ -1 +0,0 @@ -Classes which should not be inherited from are now marked ``final class`` in the API reference. diff --git a/changelog/7802.trivial.rst b/changelog/7802.trivial.rst deleted file mode 100644 index 1f8bc2c9dc6..00000000000 --- a/changelog/7802.trivial.rst +++ /dev/null @@ -1 +0,0 @@ -The ``attrs`` dependency requirement is now >=19.2.0 instead of >=17.4.0. diff --git a/changelog/7808.breaking.rst b/changelog/7808.breaking.rst deleted file mode 100644 index 114b6a382cf..00000000000 --- a/changelog/7808.breaking.rst +++ /dev/null @@ -1 +0,0 @@ -pytest now supports python3.6+ only. diff --git a/changelog/7872.doc.rst b/changelog/7872.doc.rst deleted file mode 100644 index 46236acbf2a..00000000000 --- a/changelog/7872.doc.rst +++ /dev/null @@ -1 +0,0 @@ -``_pytest.config.argparsing.Parser.addini()`` accepts explicit ``None`` and ``"string"``. diff --git a/changelog/7878.doc.rst b/changelog/7878.doc.rst deleted file mode 100644 index ff5d00d6c02..00000000000 --- a/changelog/7878.doc.rst +++ /dev/null @@ -1 +0,0 @@ -In pull request section, ask to commit after editing changelog and authors file. diff --git a/changelog/7911.bugfix.rst b/changelog/7911.bugfix.rst deleted file mode 100644 index 1ef783fbabb..00000000000 --- a/changelog/7911.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Directories created by by :fixture:`tmp_path` and :fixture:`tmpdir` are now considered stale after 3 days without modification (previous value was 3 hours) to avoid deleting directories still in use in long running test suites. diff --git a/changelog/7913.bugfix.rst b/changelog/7913.bugfix.rst deleted file mode 100644 index f42e7cb9cbd..00000000000 --- a/changelog/7913.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed a crash or hang in :meth:`pytester.spawn <_pytest.pytester.Pytester.spawn>` when the :mod:`readline` module is involved. diff --git a/changelog/7938.improvement.rst b/changelog/7938.improvement.rst deleted file mode 100644 index ffe612d0da6..00000000000 --- a/changelog/7938.improvement.rst +++ /dev/null @@ -1 +0,0 @@ -New ``--sw-skip`` argument which is a shorthand for ``--stepwise-skip``. diff --git a/changelog/7951.bugfix.rst b/changelog/7951.bugfix.rst deleted file mode 100644 index 56c71db7839..00000000000 --- a/changelog/7951.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed handling of recursive symlinks when collecting tests. diff --git a/changelog/7981.bugfix.rst b/changelog/7981.bugfix.rst deleted file mode 100644 index 0a254b5d49d..00000000000 --- a/changelog/7981.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed symlinked directories not being followed during collection. Regressed in pytest 6.1.0. diff --git a/changelog/7988.deprecation.rst b/changelog/7988.deprecation.rst deleted file mode 100644 index 34f646c9ab4..00000000000 --- a/changelog/7988.deprecation.rst +++ /dev/null @@ -1,3 +0,0 @@ -The ``@pytest.yield_fixture`` decorator/function is now deprecated. Use :func:`pytest.fixture` instead. - -``yield_fixture`` has been an alias for ``fixture`` for a very long time, so can be search/replaced safely. diff --git a/changelog/8006.feature.rst b/changelog/8006.feature.rst deleted file mode 100644 index 0203689ba4b..00000000000 --- a/changelog/8006.feature.rst +++ /dev/null @@ -1,8 +0,0 @@ -It is now possible to construct a :class:`~pytest.MonkeyPatch` object directly as ``pytest.MonkeyPatch()``, -in cases when the :fixture:`monkeypatch` fixture cannot be used. Previously some users imported it -from the private `_pytest.monkeypatch.MonkeyPatch` namespace. - -Additionally, :meth:`MonkeyPatch.context ` is now a classmethod, -and can be used as ``with MonkeyPatch.context() as mp: ...``. This is the recommended way to use -``MonkeyPatch`` directly, since unlike the ``monkeypatch`` fixture, an instance created directly -is not ``undo()``-ed automatically. diff --git a/changelog/8014.trivial.rst b/changelog/8014.trivial.rst deleted file mode 100644 index 3b9fb7bc271..00000000000 --- a/changelog/8014.trivial.rst +++ /dev/null @@ -1,2 +0,0 @@ -`.pyc` files created by pytest's assertion rewriting now conform to the newer PEP-552 format on Python>=3.7. -(These files are internal and only interpreted by pytest itself.) diff --git a/changelog/8016.bugfix.rst b/changelog/8016.bugfix.rst deleted file mode 100644 index 94539af5c97..00000000000 --- a/changelog/8016.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed only one doctest being collected when using ``pytest --doctest-modules path/to/an/__init__.py``. diff --git a/changelog/8023.improvement.rst b/changelog/8023.improvement.rst deleted file mode 100644 index c791dabc72d..00000000000 --- a/changelog/8023.improvement.rst +++ /dev/null @@ -1 +0,0 @@ -Added ``'node_modules'`` to default value for :confval:`norecursedirs`. diff --git a/changelog/8032.improvement.rst b/changelog/8032.improvement.rst deleted file mode 100644 index 76789ea5097..00000000000 --- a/changelog/8032.improvement.rst +++ /dev/null @@ -1 +0,0 @@ -:meth:`doClassCleanups ` (introduced in :mod:`unittest` in Python and 3.8) is now called appropriately. diff --git a/doc/en/announce/index.rst b/doc/en/announce/index.rst index f0e44107b69..003a0a1a9ca 100644 --- a/doc/en/announce/index.rst +++ b/doc/en/announce/index.rst @@ -6,6 +6,7 @@ Release announcements :maxdepth: 2 + release-6.2.0 release-6.1.2 release-6.1.1 release-6.1.0 diff --git a/doc/en/announce/release-6.2.0.rst b/doc/en/announce/release-6.2.0.rst new file mode 100644 index 00000000000..af16b830ddd --- /dev/null +++ b/doc/en/announce/release-6.2.0.rst @@ -0,0 +1,76 @@ +pytest-6.2.0 +======================================= + +The pytest team is proud to announce the 6.2.0 release! + +This release contains new features, improvements, bug fixes, and breaking changes, so users +are encouraged to take a look at the CHANGELOG carefully: + + https://docs.pytest.org/en/stable/changelog.html + +For complete documentation, please visit: + + https://docs.pytest.org/en/stable/ + +As usual, you can upgrade from PyPI via: + + pip install -U pytest + +Thanks to all of the contributors to this release: + +* Adam Johnson +* Albert Villanova del Moral +* Anthony Sottile +* Anton +* Ariel Pillemer +* Bruno Oliveira +* Charles Aracil +* Christine M +* Christine Mecklenborg +* Cserna Zsolt +* Dominic Mortlock +* Emiel van de Laar +* Florian Bruhin +* Garvit Shubham +* Gustavo Camargo +* Hugo Martins +* Hugo van Kemenade +* Jakob van Santen +* Josias Aurel +* Jürgen Gmach +* Karthikeyan Singaravelan +* Katarzyna +* Kyle Altendorf +* Manuel Mariñez +* Matthew Hughes +* Matthias Gabriel +* Max Voitko +* Maximilian Cosmo Sitter +* Mikhail Fesenko +* Nimesh Vashistha +* Pedro Algarvio +* Petter Strandmark +* Prakhar Gurunani +* Prashant Sharma +* Ran Benita +* Ronny Pfannschmidt +* Sanket Duthade +* Shubham Adep +* Simon K +* Tanvi Mehta +* Thomas Grainger +* Tim Hoffmann +* Vasilis Gerakaris +* William Jamir Silva +* Zac Hatfield-Dodds +* crricks +* dependabot[bot] +* duthades +* frankgerhardt +* kwgchi +* mickeypash +* symonk + + +Happy testing, +The pytest Development Team diff --git a/doc/en/builtin.rst b/doc/en/builtin.rst index 1d7fe76e3b7..5c7c8dfe666 100644 --- a/doc/en/builtin.rst +++ b/doc/en/builtin.rst @@ -158,6 +158,11 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a function invocation, created as a sub directory of the base temporary directory. + By default, a new base temporary directory is created each test session, + and old bases are removed after 3 sessions, to aid in debugging. If + ``--basetemp`` is used then it is cleared each session. See :ref:`base + temporary directory`. + The returned object is a `py.path.local`_ path object. .. _`py.path.local`: https://py.readthedocs.io/en/latest/path.html @@ -167,6 +172,11 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a function invocation, created as a sub directory of the base temporary directory. + By default, a new base temporary directory is created each test session, + and old bases are removed after 3 sessions, to aid in debugging. If + ``--basetemp`` is used then it is cleared each session. See :ref:`base + temporary directory`. + The returned object is a :class:`pathlib.Path` object. diff --git a/doc/en/changelog.rst b/doc/en/changelog.rst index 3f14921a80a..77340b1bb84 100644 --- a/doc/en/changelog.rst +++ b/doc/en/changelog.rst @@ -28,6 +28,203 @@ with advance notice in the **Deprecations** section of releases. .. towncrier release notes start +pytest 6.2.0 (2020-12-12) +========================= + +Breaking Changes +---------------- + +- `#7808 `_: pytest now supports python3.6+ only. + + + +Deprecations +------------ + +- `#7469 `_: Directly constructing/calling the following classes/functions is now deprecated: + + - ``_pytest.cacheprovider.Cache`` + - ``_pytest.cacheprovider.Cache.for_config()`` + - ``_pytest.cacheprovider.Cache.clear_cache()`` + - ``_pytest.cacheprovider.Cache.cache_dir_from_config()`` + - ``_pytest.capture.CaptureFixture`` + - ``_pytest.fixtures.FixtureRequest`` + - ``_pytest.fixtures.SubRequest`` + - ``_pytest.logging.LogCaptureFixture`` + - ``_pytest.pytester.Pytester`` + - ``_pytest.pytester.Testdir`` + - ``_pytest.recwarn.WarningsRecorder`` + - ``_pytest.recwarn.WarningsChecker`` + - ``_pytest.tmpdir.TempPathFactory`` + - ``_pytest.tmpdir.TempdirFactory`` + + These have always been considered private, but now issue a deprecation warning, which may become a hard error in pytest 7.0.0. + + +- `#7530 `_: The ``--strict`` command-line option has been deprecated, use ``--strict-markers`` instead. + + We have plans to maybe in the future to reintroduce ``--strict`` and make it an encompassing flag for all strictness + related options (``--strict-markers`` and ``--strict-config`` at the moment, more might be introduced in the future). + + +- `#7988 `_: The ``@pytest.yield_fixture`` decorator/function is now deprecated. Use :func:`pytest.fixture` instead. + + ``yield_fixture`` has been an alias for ``fixture`` for a very long time, so can be search/replaced safely. + + + +Features +-------- + +- `#5299 `_: pytest now warns about unraisable exceptions and unhandled thread exceptions that occur in tests on Python>=3.8. + See :ref:`unraisable` for more information. + + +- `#7425 `_: New :fixture:`pytester` fixture, which is identical to :fixture:`testdir` but its methods return :class:`pathlib.Path` when appropriate instead of ``py.path.local``. + + This is part of the movement to use :class:`pathlib.Path` objects internally, in order to remove the dependency to ``py`` in the future. + + Internally, the old :class:`Testdir <_pytest.pytester.Testdir>` is now a thin wrapper around :class:`Pytester <_pytest.pytester.Pytester>`, preserving the old interface. + + +- `#7695 `_: A new hook was added, `pytest_markeval_namespace` which should return a dictionary. + This dictionary will be used to augment the "global" variables available to evaluate skipif/xfail/xpass markers. + + Pseudo example + + ``conftest.py``: + + .. code-block:: python + + def pytest_markeval_namespace(): + return {"color": "red"} + + ``test_func.py``: + + .. code-block:: python + + @pytest.mark.skipif("color == 'blue'", reason="Color is not red") + def test_func(): + assert False + + +- `#8006 `_: It is now possible to construct a :class:`~pytest.MonkeyPatch` object directly as ``pytest.MonkeyPatch()``, + in cases when the :fixture:`monkeypatch` fixture cannot be used. Previously some users imported it + from the private `_pytest.monkeypatch.MonkeyPatch` namespace. + + Additionally, :meth:`MonkeyPatch.context ` is now a classmethod, + and can be used as ``with MonkeyPatch.context() as mp: ...``. This is the recommended way to use + ``MonkeyPatch`` directly, since unlike the ``monkeypatch`` fixture, an instance created directly + is not ``undo()``-ed automatically. + + + +Improvements +------------ + +- `#1265 `_: Added an ``__str__`` implementation to the :class:`~pytest.pytester.LineMatcher` class which is returned from ``pytester.run_pytest().stdout`` and similar. It returns the entire output, like the existing ``str()`` method. + + +- `#2044 `_: Verbose mode now shows the reason that a test was skipped in the test's terminal line after the "SKIPPED", "XFAIL" or "XPASS". + + +- `#7469 `_ The types of builtin pytest fixtures are now exported so they may be used in type annotations of test functions. + The newly-exported types are: + + - ``pytest.FixtureRequest`` for the :fixture:`request` fixture. + - ``pytest.Cache`` for the :fixture:`cache` fixture. + - ``pytest.CaptureFixture[str]`` for the :fixture:`capfd` and :fixture:`capsys` fixtures. + - ``pytest.CaptureFixture[bytes]`` for the :fixture:`capfdbinary` and :fixture:`capsysbinary` fixtures. + - ``pytest.LogCaptureFixture`` for the :fixture:`caplog` fixture. + - ``pytest.Pytester`` for the :fixture:`pytester` fixture. + - ``pytest.Testdir`` for the :fixture:`testdir` fixture. + - ``pytest.TempdirFactory`` for the :fixture:`tmpdir_factory` fixture. + - ``pytest.TempPathFactory`` for the :fixture:`tmp_path_factory` fixture. + - ``pytest.MonkeyPatch`` for the :fixture:`monkeypatch` fixture. + - ``pytest.WarningsRecorder`` for the :fixture:`recwarn` fixture. + + Constructing them is not supported (except for `MonkeyPatch`); they are only meant for use in type annotations. + Doing so will emit a deprecation warning, and may become a hard-error in pytest 7.0. + + Subclassing them is also not supported. This is not currently enforced at runtime, but is detected by type-checkers such as mypy. + + +- `#7527 `_: When a comparison between :func:`namedtuple ` instances of the same type fails, pytest now shows the differing field names (possibly nested) instead of their indexes. + + +- `#7615 `_: :meth:`Node.warn <_pytest.nodes.Node.warn>` now permits any subclass of :class:`Warning`, not just :class:`PytestWarning `. + + +- `#7701 `_: Improved reporting when using ``--collected-only``. It will now show the number of collected tests in the summary stats. + + +- `#7710 `_: Use strict equality comparison for non-numeric types in :func:`pytest.approx` instead of + raising :class:`TypeError`. + + This was the undocumented behavior before 3.7, but is now officially a supported feature. + + +- `#7938 `_: New ``--sw-skip`` argument which is a shorthand for ``--stepwise-skip``. + + +- `#8023 `_: Added ``'node_modules'`` to default value for :confval:`norecursedirs`. + + +- `#8032 `_: :meth:`doClassCleanups ` (introduced in :mod:`unittest` in Python and 3.8) is now called appropriately. + + + +Bug Fixes +--------- + +- `#4824 `_: Fixed quadratic behavior and improved performance of collection of items using autouse fixtures and xunit fixtures. + + +- `#7758 `_: Fixed an issue where some files in packages are getting lost from ``--lf`` even though they contain tests that failed. Regressed in pytest 5.4.0. + + +- `#7911 `_: Directories created by by :fixture:`tmp_path` and :fixture:`tmpdir` are now considered stale after 3 days without modification (previous value was 3 hours) to avoid deleting directories still in use in long running test suites. + + +- `#7913 `_: Fixed a crash or hang in :meth:`pytester.spawn <_pytest.pytester.Pytester.spawn>` when the :mod:`readline` module is involved. + + +- `#7951 `_: Fixed handling of recursive symlinks when collecting tests. + + +- `#7981 `_: Fixed symlinked directories not being followed during collection. Regressed in pytest 6.1.0. + + +- `#8016 `_: Fixed only one doctest being collected when using ``pytest --doctest-modules path/to/an/__init__.py``. + + + +Improved Documentation +---------------------- + +- `#7429 `_: Add more information and use cases about skipping doctests. + + +- `#7780 `_: Classes which should not be inherited from are now marked ``final class`` in the API reference. + + +- `#7872 `_: ``_pytest.config.argparsing.Parser.addini()`` accepts explicit ``None`` and ``"string"``. + + +- `#7878 `_: In pull request section, ask to commit after editing changelog and authors file. + + + +Trivial/Internal Changes +------------------------ + +- `#7802 `_: The ``attrs`` dependency requirement is now >=19.2.0 instead of >=17.4.0. + + +- `#8014 `_: `.pyc` files created by pytest's assertion rewriting now conform to the newer PEP-552 format on Python>=3.7. + (These files are internal and only interpreted by pytest itself.) + + pytest 6.1.2 (2020-10-28) ========================= diff --git a/doc/en/example/nonpython.rst b/doc/en/example/nonpython.rst index 558c56772f1..a3477fe1e1d 100644 --- a/doc/en/example/nonpython.rst +++ b/doc/en/example/nonpython.rst @@ -102,4 +102,4 @@ interesting to just look at the collection tree: - ========================== 2 tests found in 0.12s =========================== + ======================== 2 tests collected in 0.12s ======================== diff --git a/doc/en/example/parametrize.rst b/doc/en/example/parametrize.rst index d5a11b45192..6e2f53984ee 100644 --- a/doc/en/example/parametrize.rst +++ b/doc/en/example/parametrize.rst @@ -175,7 +175,7 @@ objects, they are still using the default pytest representation: - ========================== 8 tests found in 0.12s =========================== + ======================== 8 tests collected in 0.12s ======================== In ``test_timedistance_v3``, we used ``pytest.param`` to specify the test IDs together with the actual data, instead of listing them separately. @@ -252,7 +252,7 @@ If you just collect tests you'll also nicely see 'advanced' and 'basic' as varia - ========================== 4 tests found in 0.12s =========================== + ======================== 4 tests collected in 0.12s ======================== Note that we told ``metafunc.parametrize()`` that your scenario values should be considered class-scoped. With pytest-2.3 this leads to a @@ -328,7 +328,7 @@ Let's first see how it looks like at collection time: - ========================== 2/2 tests found in 0.12s =========================== + ======================== 2 tests collected in 0.12s ======================== And then when we run the test: diff --git a/doc/en/example/pythoncollection.rst b/doc/en/example/pythoncollection.rst index f7917b790ef..a6ce2e742e5 100644 --- a/doc/en/example/pythoncollection.rst +++ b/doc/en/example/pythoncollection.rst @@ -157,7 +157,7 @@ The test collection would look like this: - ========================== 2 tests found in 0.12s =========================== + ======================== 2 tests collected in 0.12s ======================== You can check for multiple glob patterns by adding a space between the patterns: @@ -220,7 +220,7 @@ You can always peek at the collection tree without running tests like this: - ========================== 3 tests found in 0.12s =========================== + ======================== 3 tests collected in 0.12s ======================== .. _customizing-test-collection: @@ -296,7 +296,7 @@ file will be left out: rootdir: $REGENDOC_TMPDIR, configfile: pytest.ini collected 0 items - ========================== no tests found in 0.12s =========================== + ======================= no tests collected in 0.12s ======================== It's also possible to ignore files based on Unix shell-style wildcards by adding patterns to :globalvar:`collect_ignore_glob`. diff --git a/doc/en/example/reportingdemo.rst b/doc/en/example/reportingdemo.rst index f1b973f3b33..6e7dbe49683 100644 --- a/doc/en/example/reportingdemo.rst +++ b/doc/en/example/reportingdemo.rst @@ -446,7 +446,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}".format(items)) + print(f"items is {items!r}") > a, b = items.pop() E TypeError: cannot unpack non-iterable int object diff --git a/doc/en/fixture.rst b/doc/en/fixture.rst index 22b86ffcafc..963fc32e6b0 100644 --- a/doc/en/fixture.rst +++ b/doc/en/fixture.rst @@ -919,7 +919,7 @@ Running the above tests results in the following test IDs being used: - ========================== 10 tests found in 0.12s =========================== + ======================= 10 tests collected in 0.12s ======================== .. _`fixture-parametrize-marks`: @@ -958,7 +958,7 @@ Running this test will *skip* the invocation of ``data_set`` with value ``2``: test_fixture_marks.py::test_data[0] PASSED [ 33%] test_fixture_marks.py::test_data[1] PASSED [ 66%] - test_fixture_marks.py::test_data[2] SKIPPED [100%] + test_fixture_marks.py::test_data[2] SKIPPED (unconditional skip) [100%] ======================= 2 passed, 1 skipped in 0.12s ======================= diff --git a/doc/en/getting-started.rst b/doc/en/getting-started.rst index 4814b850586..fe15c218cde 100644 --- a/doc/en/getting-started.rst +++ b/doc/en/getting-started.rst @@ -28,7 +28,7 @@ Install ``pytest`` .. code-block:: bash $ pytest --version - pytest 6.1.2 + pytest 6.2.0 .. _`simpletest`: diff --git a/doc/en/reference.rst b/doc/en/reference.rst index f62b51414b4..8aa95ca6448 100644 --- a/doc/en/reference.rst +++ b/doc/en/reference.rst @@ -1749,7 +1749,8 @@ All the command-line flags can be obtained by running ``pytest --help``:: failures. --sw, --stepwise exit on test failure and continue from last failing test next time - --stepwise-skip ignore the first failing test but stop on the next + --sw-skip, --stepwise-skip + ignore the first failing test but stop on the next failing test reporting: @@ -1791,9 +1792,9 @@ All the command-line flags can be obtained by running ``pytest --help``:: --maxfail=num exit after first num failures or errors. --strict-config any warnings encountered while parsing the `pytest` section of the configuration file raise errors. - --strict-markers, --strict - markers not registered in the `markers` section of + --strict-markers markers not registered in the `markers` section of the configuration file raise errors. + --strict (deprecated) alias to --strict-markers. -c file load configuration from `file` instead of trying to locate one of the implicit configuration files. --continue-on-collection-errors diff --git a/doc/en/tmpdir.rst b/doc/en/tmpdir.rst index c8da5877b28..adcba02cb15 100644 --- a/doc/en/tmpdir.rst +++ b/doc/en/tmpdir.rst @@ -61,7 +61,7 @@ Running this would result in a passed test except for the last > assert 0 E assert 0 - test_tmp_path.py:13: AssertionError + test_tmp_path.py:11: AssertionError ========================= short test summary info ========================== FAILED test_tmp_path.py::test_create_file - assert 0 ============================ 1 failed in 0.12s ============================= @@ -129,7 +129,7 @@ Running this would result in a passed test except for the last > assert 0 E assert 0 - test_tmpdir.py:9: AssertionError + test_tmpdir.py:6: AssertionError ========================= short test summary info ========================== FAILED test_tmpdir.py::test_create_file - assert 0 ============================ 1 failed in 0.12s ============================= diff --git a/doc/en/writing_plugins.rst b/doc/en/writing_plugins.rst index 41967525b33..908366d5290 100644 --- a/doc/en/writing_plugins.rst +++ b/doc/en/writing_plugins.rst @@ -449,13 +449,7 @@ Additionally it is possible to copy examples for an example folder before runnin test_example.py .. [100%] - ============================= warnings summary ============================= - test_example.py::test_plugin - $REGENDOC_TMPDIR/test_example.py:4: PytestExperimentalApiWarning: testdir.copy_example is an experimental api that may change over time - testdir.copy_example("test_example.py") - - -- Docs: https://docs.pytest.org/en/stable/warnings.html - ======================= 2 passed, 1 warning in 0.12s ======================= + ============================ 2 passed in 0.12s ============================= For more information about the result object that ``runpytest()`` returns, and the methods that it provides please check out the :py:class:`RunResult From 28588bf535b71680df7dbe6bbb936148f06a94ac Mon Sep 17 00:00:00 2001 From: bot2x Date: Sun, 6 Dec 2020 14:50:33 +0530 Subject: [PATCH 004/630] migrates test_warnings.py from testdir to pytester --- testing/test_warnings.py | 224 ++++++++++++++++++++------------------- 1 file changed, 114 insertions(+), 110 deletions(-) diff --git a/testing/test_warnings.py b/testing/test_warnings.py index 66898041f08..574f3f1ec02 100644 --- a/testing/test_warnings.py +++ b/testing/test_warnings.py @@ -6,18 +6,18 @@ import pytest from _pytest.fixtures import FixtureRequest -from _pytest.pytester import Testdir +from _pytest.pytester import Pytester WARNINGS_SUMMARY_HEADER = "warnings summary" @pytest.fixture -def pyfile_with_warnings(testdir: Testdir, request: FixtureRequest) -> str: +def pyfile_with_warnings(pytester: Pytester, request: FixtureRequest) -> str: """Create a test file which calls a function in a module which generates warnings.""" - testdir.syspathinsert() + pytester.syspathinsert() test_name = request.function.__name__ module_name = test_name.lstrip("test_") + "_module" - test_file = testdir.makepyfile( + test_file = pytester.makepyfile( """ import {module_name} def test_func(): @@ -39,9 +39,9 @@ def foo(): @pytest.mark.filterwarnings("default") -def test_normal_flow(testdir, pyfile_with_warnings): +def test_normal_flow(pytester: Pytester, pyfile_with_warnings) -> None: """Check that the warnings section is displayed.""" - result = testdir.runpytest(pyfile_with_warnings) + result = pytester.runpytest(pyfile_with_warnings) result.stdout.fnmatch_lines( [ "*== %s ==*" % WARNINGS_SUMMARY_HEADER, @@ -56,8 +56,8 @@ def test_normal_flow(testdir, pyfile_with_warnings): @pytest.mark.filterwarnings("always") -def test_setup_teardown_warnings(testdir): - testdir.makepyfile( +def test_setup_teardown_warnings(pytester: Pytester) -> None: + pytester.makepyfile( """ import warnings import pytest @@ -72,7 +72,7 @@ def test_func(fix): pass """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines( [ "*== %s ==*" % WARNINGS_SUMMARY_HEADER, @@ -86,10 +86,10 @@ def test_func(fix): @pytest.mark.parametrize("method", ["cmdline", "ini"]) -def test_as_errors(testdir, pyfile_with_warnings, method): +def test_as_errors(pytester: Pytester, pyfile_with_warnings, method) -> None: args = ("-W", "error") if method == "cmdline" else () if method == "ini": - testdir.makeini( + pytester.makeini( """ [pytest] filterwarnings=error @@ -97,7 +97,7 @@ def test_as_errors(testdir, pyfile_with_warnings, method): ) # Use a subprocess, since changing logging level affects other threads # (xdist). - result = testdir.runpytest_subprocess(*args, pyfile_with_warnings) + result = pytester.runpytest_subprocess(*args, pyfile_with_warnings) result.stdout.fnmatch_lines( [ "E UserWarning: user warning", @@ -108,24 +108,24 @@ def test_as_errors(testdir, pyfile_with_warnings, method): @pytest.mark.parametrize("method", ["cmdline", "ini"]) -def test_ignore(testdir, pyfile_with_warnings, method): +def test_ignore(pytester: Pytester, pyfile_with_warnings, method) -> None: args = ("-W", "ignore") if method == "cmdline" else () if method == "ini": - testdir.makeini( + pytester.makeini( """ [pytest] filterwarnings= ignore """ ) - result = testdir.runpytest(*args, pyfile_with_warnings) + result = pytester.runpytest(*args, pyfile_with_warnings) result.stdout.fnmatch_lines(["* 1 passed in *"]) assert WARNINGS_SUMMARY_HEADER not in result.stdout.str() @pytest.mark.filterwarnings("always") -def test_unicode(testdir): - testdir.makepyfile( +def test_unicode(pytester: Pytester) -> None: + pytester.makepyfile( """ import warnings import pytest @@ -140,7 +140,7 @@ def test_func(fix): pass """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines( [ "*== %s ==*" % WARNINGS_SUMMARY_HEADER, @@ -150,9 +150,9 @@ def test_func(fix): ) -def test_works_with_filterwarnings(testdir): +def test_works_with_filterwarnings(pytester: Pytester) -> None: """Ensure our warnings capture does not mess with pre-installed filters (#2430).""" - testdir.makepyfile( + pytester.makepyfile( """ import warnings @@ -170,22 +170,22 @@ def test_my_warning(self): assert True """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["*== 1 passed in *"]) @pytest.mark.parametrize("default_config", ["ini", "cmdline"]) -def test_filterwarnings_mark(testdir, default_config): +def test_filterwarnings_mark(pytester: Pytester, default_config) -> None: """Test ``filterwarnings`` mark works and takes precedence over command line and ini options.""" if default_config == "ini": - testdir.makeini( + pytester.makeini( """ [pytest] filterwarnings = always """ ) - testdir.makepyfile( + pytester.makepyfile( """ import warnings import pytest @@ -202,13 +202,13 @@ def test_show_warning(): warnings.warn(RuntimeWarning()) """ ) - result = testdir.runpytest("-W always" if default_config == "cmdline" else "") + result = pytester.runpytest("-W always" if default_config == "cmdline" else "") result.stdout.fnmatch_lines(["*= 1 failed, 2 passed, 1 warning in *"]) -def test_non_string_warning_argument(testdir): +def test_non_string_warning_argument(pytester: Pytester) -> None: """Non-str argument passed to warning breaks pytest (#2956)""" - testdir.makepyfile( + pytester.makepyfile( """\ import warnings import pytest @@ -217,13 +217,13 @@ def test(): warnings.warn(UserWarning(1, 'foo')) """ ) - result = testdir.runpytest("-W", "always") + result = pytester.runpytest("-W", "always") result.stdout.fnmatch_lines(["*= 1 passed, 1 warning in *"]) -def test_filterwarnings_mark_registration(testdir): +def test_filterwarnings_mark_registration(pytester: Pytester) -> None: """Ensure filterwarnings mark is registered""" - testdir.makepyfile( + pytester.makepyfile( """ import pytest @@ -232,19 +232,19 @@ def test_func(): pass """ ) - result = testdir.runpytest("--strict-markers") + result = pytester.runpytest("--strict-markers") assert result.ret == 0 @pytest.mark.filterwarnings("always") -def test_warning_captured_hook(testdir): - testdir.makeconftest( +def test_warning_captured_hook(pytester: Pytester) -> None: + pytester.makeconftest( """ def pytest_configure(config): config.issue_config_time_warning(UserWarning("config warning"), stacklevel=2) """ ) - testdir.makepyfile( + pytester.makepyfile( """ import pytest, warnings @@ -268,7 +268,7 @@ class WarningCollector: def pytest_warning_recorded(self, warning_message, when, nodeid, location): collected.append((str(warning_message.message), when, nodeid, location)) - result = testdir.runpytest(plugins=[WarningCollector()]) + result = pytester.runpytest(plugins=[WarningCollector()]) result.stdout.fnmatch_lines(["*1 passed*"]) expected = [ @@ -298,9 +298,9 @@ def pytest_warning_recorded(self, warning_message, when, nodeid, location): @pytest.mark.filterwarnings("always") -def test_collection_warnings(testdir): +def test_collection_warnings(pytester: Pytester) -> None: """Check that we also capture warnings issued during test collection (#3251).""" - testdir.makepyfile( + pytester.makepyfile( """ import warnings @@ -310,7 +310,7 @@ def test_foo(): pass """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines( [ "*== %s ==*" % WARNINGS_SUMMARY_HEADER, @@ -322,9 +322,9 @@ def test_foo(): @pytest.mark.filterwarnings("always") -def test_mark_regex_escape(testdir): +def test_mark_regex_escape(pytester: Pytester) -> None: """@pytest.mark.filterwarnings should not try to escape regex characters (#3936)""" - testdir.makepyfile( + pytester.makepyfile( r""" import pytest, warnings @@ -333,15 +333,17 @@ def test_foo(): warnings.warn(UserWarning("some (warning)")) """ ) - result = testdir.runpytest() + result = pytester.runpytest() assert WARNINGS_SUMMARY_HEADER not in result.stdout.str() @pytest.mark.filterwarnings("default") @pytest.mark.parametrize("ignore_pytest_warnings", ["no", "ini", "cmdline"]) -def test_hide_pytest_internal_warnings(testdir, ignore_pytest_warnings): +def test_hide_pytest_internal_warnings( + pytester: Pytester, ignore_pytest_warnings +) -> None: """Make sure we can ignore internal pytest warnings using a warnings filter.""" - testdir.makepyfile( + pytester.makepyfile( """ import pytest import warnings @@ -353,7 +355,7 @@ def test_bar(): """ ) if ignore_pytest_warnings == "ini": - testdir.makeini( + pytester.makeini( """ [pytest] filterwarnings = ignore::pytest.PytestWarning @@ -364,7 +366,7 @@ def test_bar(): if ignore_pytest_warnings == "cmdline" else [] ) - result = testdir.runpytest(*args) + result = pytester.runpytest(*args) if ignore_pytest_warnings != "no": assert WARNINGS_SUMMARY_HEADER not in result.stdout.str() else: @@ -378,15 +380,17 @@ def test_bar(): @pytest.mark.parametrize("ignore_on_cmdline", [True, False]) -def test_option_precedence_cmdline_over_ini(testdir, ignore_on_cmdline): +def test_option_precedence_cmdline_over_ini( + pytester: Pytester, ignore_on_cmdline +) -> None: """Filters defined in the command-line should take precedence over filters in ini files (#3946).""" - testdir.makeini( + pytester.makeini( """ [pytest] filterwarnings = error """ ) - testdir.makepyfile( + pytester.makepyfile( """ import warnings def test(): @@ -394,22 +398,22 @@ def test(): """ ) args = ["-W", "ignore"] if ignore_on_cmdline else [] - result = testdir.runpytest(*args) + result = pytester.runpytest(*args) if ignore_on_cmdline: result.stdout.fnmatch_lines(["* 1 passed in*"]) else: result.stdout.fnmatch_lines(["* 1 failed in*"]) -def test_option_precedence_mark(testdir): +def test_option_precedence_mark(pytester: Pytester) -> None: """Filters defined by marks should always take precedence (#3946).""" - testdir.makeini( + pytester.makeini( """ [pytest] filterwarnings = ignore """ ) - testdir.makepyfile( + pytester.makepyfile( """ import pytest, warnings @pytest.mark.filterwarnings('error') @@ -417,7 +421,7 @@ def test(): warnings.warn(UserWarning('hello')) """ ) - result = testdir.runpytest("-W", "ignore") + result = pytester.runpytest("-W", "ignore") result.stdout.fnmatch_lines(["* 1 failed in*"]) @@ -427,8 +431,8 @@ class TestDeprecationWarningsByDefault: from pytest's own test suite """ - def create_file(self, testdir, mark=""): - testdir.makepyfile( + def create_file(self, pytester: Pytester, mark="") -> None: + pytester.makepyfile( """ import pytest, warnings @@ -443,18 +447,18 @@ def test_foo(): ) @pytest.mark.parametrize("customize_filters", [True, False]) - def test_shown_by_default(self, testdir, customize_filters): + def test_shown_by_default(self, pytester: Pytester, customize_filters) -> None: """Show deprecation warnings by default, even if user has customized the warnings filters (#4013).""" - self.create_file(testdir) + self.create_file(pytester) if customize_filters: - testdir.makeini( + pytester.makeini( """ [pytest] filterwarnings = once::UserWarning """ ) - result = testdir.runpytest_subprocess() + result = pytester.runpytest_subprocess() result.stdout.fnmatch_lines( [ "*== %s ==*" % WARNINGS_SUMMARY_HEADER, @@ -464,9 +468,9 @@ def test_shown_by_default(self, testdir, customize_filters): ] ) - def test_hidden_by_ini(self, testdir): - self.create_file(testdir) - testdir.makeini( + def test_hidden_by_ini(self, pytester: Pytester) -> None: + self.create_file(pytester) + pytester.makeini( """ [pytest] filterwarnings = @@ -474,18 +478,18 @@ def test_hidden_by_ini(self, testdir): ignore::PendingDeprecationWarning """ ) - result = testdir.runpytest_subprocess() + result = pytester.runpytest_subprocess() assert WARNINGS_SUMMARY_HEADER not in result.stdout.str() - def test_hidden_by_mark(self, testdir): + def test_hidden_by_mark(self, pytester: Pytester) -> None: """Should hide the deprecation warning from the function, but the warning during collection should be displayed normally. """ self.create_file( - testdir, + pytester, mark='@pytest.mark.filterwarnings("ignore::PendingDeprecationWarning")', ) - result = testdir.runpytest_subprocess() + result = pytester.runpytest_subprocess() result.stdout.fnmatch_lines( [ "*== %s ==*" % WARNINGS_SUMMARY_HEADER, @@ -494,9 +498,9 @@ def test_hidden_by_mark(self, testdir): ] ) - def test_hidden_by_cmdline(self, testdir): - self.create_file(testdir) - result = testdir.runpytest_subprocess( + def test_hidden_by_cmdline(self, pytester: Pytester) -> None: + self.create_file(pytester) + result = pytester.runpytest_subprocess( "-W", "ignore::DeprecationWarning", "-W", @@ -504,10 +508,10 @@ def test_hidden_by_cmdline(self, testdir): ) assert WARNINGS_SUMMARY_HEADER not in result.stdout.str() - def test_hidden_by_system(self, testdir, monkeypatch): - self.create_file(testdir) + def test_hidden_by_system(self, pytester: Pytester, monkeypatch) -> None: + self.create_file(pytester) monkeypatch.setenv("PYTHONWARNINGS", "once::UserWarning") - result = testdir.runpytest_subprocess() + result = pytester.runpytest_subprocess() assert WARNINGS_SUMMARY_HEADER not in result.stdout.str() @@ -515,13 +519,13 @@ def test_hidden_by_system(self, testdir, monkeypatch): @pytest.mark.skip( reason="This test should be enabled again before pytest 7.0 is released" ) -def test_deprecation_warning_as_error(testdir, change_default): +def test_deprecation_warning_as_error(pytester: Pytester, change_default) -> None: """This ensures that PytestDeprecationWarnings raised by pytest are turned into errors. This test should be enabled as part of each major release, and skipped again afterwards to ensure our deprecations are turning into warnings as expected. """ - testdir.makepyfile( + pytester.makepyfile( """ import warnings, pytest def test(): @@ -529,7 +533,7 @@ def test(): """ ) if change_default == "ini": - testdir.makeini( + pytester.makeini( """ [pytest] filterwarnings = @@ -542,7 +546,7 @@ def test(): if change_default == "cmdline" else () ) - result = testdir.runpytest(*args) + result = pytester.runpytest(*args) if change_default is None: result.stdout.fnmatch_lines(["* 1 failed in *"]) else: @@ -552,23 +556,23 @@ def test(): class TestAssertionWarnings: @staticmethod - def assert_result_warns(result, msg): + def assert_result_warns(result, msg) -> None: result.stdout.fnmatch_lines(["*PytestAssertRewriteWarning: %s*" % msg]) - def test_tuple_warning(self, testdir): - testdir.makepyfile( + def test_tuple_warning(self, pytester: Pytester) -> None: + pytester.makepyfile( """\ def test_foo(): assert (1,2) """ ) - result = testdir.runpytest() + result = pytester.runpytest() self.assert_result_warns( result, "assertion is always true, perhaps remove parentheses?" ) -def test_warnings_checker_twice(): +def test_warnings_checker_twice() -> None: """Issue #4617""" expectation = pytest.warns(UserWarning) with expectation: @@ -579,9 +583,9 @@ def test_warnings_checker_twice(): @pytest.mark.filterwarnings("ignore::pytest.PytestExperimentalApiWarning") @pytest.mark.filterwarnings("always") -def test_group_warnings_by_message(testdir): - testdir.copy_example("warnings/test_group_warnings_by_message.py") - result = testdir.runpytest() +def test_group_warnings_by_message(pytester: Pytester) -> None: + pytester.copy_example("warnings/test_group_warnings_by_message.py") + result = pytester.runpytest() result.stdout.fnmatch_lines( [ "*== %s ==*" % WARNINGS_SUMMARY_HEADER, @@ -611,10 +615,10 @@ def test_group_warnings_by_message(testdir): @pytest.mark.filterwarnings("ignore::pytest.PytestExperimentalApiWarning") @pytest.mark.filterwarnings("always") -def test_group_warnings_by_message_summary(testdir): - testdir.copy_example("warnings/test_group_warnings_by_message_summary") - testdir.syspathinsert() - result = testdir.runpytest() +def test_group_warnings_by_message_summary(pytester: Pytester) -> None: + pytester.copy_example("warnings/test_group_warnings_by_message_summary") + pytester.syspathinsert() + result = pytester.runpytest() result.stdout.fnmatch_lines( [ "*== %s ==*" % WARNINGS_SUMMARY_HEADER, @@ -634,9 +638,9 @@ def test_group_warnings_by_message_summary(testdir): ) -def test_pytest_configure_warning(testdir, recwarn): +def test_pytest_configure_warning(pytester: Pytester, recwarn) -> None: """Issue 5115.""" - testdir.makeconftest( + pytester.makeconftest( """ def pytest_configure(): import warnings @@ -645,7 +649,7 @@ def pytest_configure(): """ ) - result = testdir.runpytest() + result = pytester.runpytest() assert result.ret == 5 assert "INTERNALERROR" not in result.stderr.str() warning = recwarn.pop() @@ -654,7 +658,7 @@ def pytest_configure(): class TestStackLevel: @pytest.fixture - def capwarn(self, testdir): + def capwarn(self, pytester: Pytester): class CapturedWarnings: captured: List[ Tuple[warnings.WarningMessage, Optional[Tuple[str, int, str]]] @@ -664,16 +668,16 @@ class CapturedWarnings: def pytest_warning_recorded(cls, warning_message, when, nodeid, location): cls.captured.append((warning_message, location)) - testdir.plugins = [CapturedWarnings()] + pytester.plugins = [CapturedWarnings()] return CapturedWarnings - def test_issue4445_rewrite(self, testdir, capwarn): + def test_issue4445_rewrite(self, pytester: Pytester, capwarn) -> None: """#4445: Make sure the warning points to a reasonable location See origin of _issue_warning_captured at: _pytest.assertion.rewrite.py:241 """ - testdir.makepyfile(some_mod="") - conftest = testdir.makeconftest( + pytester.makepyfile(some_mod="") + conftest = pytester.makeconftest( """ import some_mod import pytest @@ -681,7 +685,7 @@ def test_issue4445_rewrite(self, testdir, capwarn): pytest.register_assert_rewrite("some_mod") """ ) - testdir.parseconfig() + pytester.parseconfig() # with stacklevel=5 the warning originates from register_assert_rewrite # function in the created conftest.py @@ -694,16 +698,16 @@ def test_issue4445_rewrite(self, testdir, capwarn): assert func == "" # the above conftest.py assert lineno == 4 - def test_issue4445_preparse(self, testdir, capwarn): + def test_issue4445_preparse(self, pytester: Pytester, capwarn) -> None: """#4445: Make sure the warning points to a reasonable location See origin of _issue_warning_captured at: _pytest.config.__init__.py:910 """ - testdir.makeconftest( + pytester.makeconftest( """ import nothing """ ) - testdir.parseconfig("--help") + pytester.parseconfig("--help") # with stacklevel=2 the warning should originate from config._preparse and is # thrown by an errorneous conftest.py @@ -716,29 +720,29 @@ def test_issue4445_preparse(self, testdir, capwarn): assert func == "_preparse" @pytest.mark.filterwarnings("default") - def test_conftest_warning_captured(self, testdir: Testdir) -> None: + def test_conftest_warning_captured(self, pytester: Pytester) -> None: """Warnings raised during importing of conftest.py files is captured (#2891).""" - testdir.makeconftest( + pytester.makeconftest( """ import warnings warnings.warn(UserWarning("my custom warning")) """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines( ["conftest.py:2", "*UserWarning: my custom warning*"] ) - def test_issue4445_import_plugin(self, testdir, capwarn): + def test_issue4445_import_plugin(self, pytester: Pytester, capwarn) -> None: """#4445: Make sure the warning points to a reasonable location""" - testdir.makepyfile( + pytester.makepyfile( some_plugin=""" import pytest pytest.skip("thing", allow_module_level=True) """ ) - testdir.syspathinsert() - testdir.parseconfig("-p", "some_plugin") + pytester.syspathinsert() + pytester.parseconfig("-p", "some_plugin") # with stacklevel=2 the warning should originate from # config.PytestPluginManager.import_plugin is thrown by a skipped plugin @@ -751,11 +755,11 @@ def test_issue4445_import_plugin(self, testdir, capwarn): assert f"config{os.sep}__init__.py" in file assert func == "_warn_about_skipped_plugins" - def test_issue4445_issue5928_mark_generator(self, testdir): + def test_issue4445_issue5928_mark_generator(self, pytester: Pytester) -> None: """#4445 and #5928: Make sure the warning from an unknown mark points to the test file where this mark is used. """ - testfile = testdir.makepyfile( + testfile = pytester.makepyfile( """ import pytest @@ -764,7 +768,7 @@ def test_it(): pass """ ) - result = testdir.runpytest_subprocess() + result = pytester.runpytest_subprocess() # with stacklevel=2 the warning should originate from the above created test file result.stdout.fnmatch_lines_random( [ From 4ed9a385192c252cc4d64a0775fa744b8fb95063 Mon Sep 17 00:00:00 2001 From: antonblr Date: Wed, 9 Dec 2020 21:47:58 -0800 Subject: [PATCH 005/630] tests: Migrate testing/python to pytester fixture --- src/_pytest/pytester.py | 16 +- testing/python/collect.py | 584 +++++----- testing/python/fixtures.py | 1254 ++++++++++++---------- testing/python/integration.py | 103 +- testing/python/metafunc.py | 362 ++++--- testing/python/raises.py | 19 +- testing/python/show_fixtures_per_test.py | 45 +- 7 files changed, 1251 insertions(+), 1132 deletions(-) diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index 6833eb02149..0d1f8f278f9 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -1190,7 +1190,9 @@ def parseconfigure(self, *args: Union[str, "os.PathLike[str]"]) -> Config: config._do_configure() return config - def getitem(self, source: str, funcname: str = "test_func") -> Item: + def getitem( + self, source: Union[str, "os.PathLike[str]"], funcname: str = "test_func" + ) -> Item: """Return the test item for a test function. Writes the source to a python file and runs pytest's collection on @@ -1210,7 +1212,7 @@ def getitem(self, source: str, funcname: str = "test_func") -> Item: funcname, source, items ) - def getitems(self, source: str) -> List[Item]: + def getitems(self, source: Union[str, "os.PathLike[str]"]) -> List[Item]: """Return all test items collected from the module. Writes the source to a Python file and runs pytest's collection on @@ -1220,7 +1222,11 @@ def getitems(self, source: str) -> List[Item]: return self.genitems([modcol]) def getmodulecol( - self, source: Union[str, Path], configargs=(), *, withinit: bool = False + self, + source: Union[str, "os.PathLike[str]"], + configargs=(), + *, + withinit: bool = False, ): """Return the module collection node for ``source``. @@ -1238,7 +1244,9 @@ def getmodulecol( Whether to also write an ``__init__.py`` file to the same directory to ensure it is a package. """ - if isinstance(source, Path): + # TODO: Remove type ignore in next mypy release (> 0.790). + # https://github.com/python/typeshed/pull/4582 + if isinstance(source, os.PathLike): # type: ignore[misc] path = self.path.joinpath(source) assert not withinit, "not supported for paths" else: diff --git a/testing/python/collect.py b/testing/python/collect.py index 4d5f4c6895f..c52fb017d0c 100644 --- a/testing/python/collect.py +++ b/testing/python/collect.py @@ -1,3 +1,4 @@ +import os import sys import textwrap from typing import Any @@ -6,42 +7,50 @@ import _pytest._code import pytest from _pytest.config import ExitCode +from _pytest.monkeypatch import MonkeyPatch from _pytest.nodes import Collector -from _pytest.pytester import Testdir +from _pytest.pytester import Pytester +from _pytest.python import Class +from _pytest.python import Instance class TestModule: - def test_failing_import(self, testdir): - modcol = testdir.getmodulecol("import alksdjalskdjalkjals") + def test_failing_import(self, pytester: Pytester) -> None: + modcol = pytester.getmodulecol("import alksdjalskdjalkjals") pytest.raises(Collector.CollectError, modcol.collect) - def test_import_duplicate(self, testdir): - a = testdir.mkdir("a") - b = testdir.mkdir("b") - p = a.ensure("test_whatever.py") - p.pyimport() - del sys.modules["test_whatever"] - b.ensure("test_whatever.py") - result = testdir.runpytest() + def test_import_duplicate(self, pytester: Pytester) -> None: + a = pytester.mkdir("a") + b = pytester.mkdir("b") + p1 = a.joinpath("test_whatever.py") + p1.touch() + p2 = b.joinpath("test_whatever.py") + p2.touch() + # ensure we don't have it imported already + sys.modules.pop(p1.stem, None) + + result = pytester.runpytest() result.stdout.fnmatch_lines( [ "*import*mismatch*", "*imported*test_whatever*", - "*%s*" % a.join("test_whatever.py"), + "*%s*" % p1, "*not the same*", - "*%s*" % b.join("test_whatever.py"), + "*%s*" % p2, "*HINT*", ] ) - def test_import_prepend_append(self, testdir, monkeypatch): - root1 = testdir.mkdir("root1") - root2 = testdir.mkdir("root2") - root1.ensure("x456.py") - root2.ensure("x456.py") - p = root2.join("test_x456.py") + def test_import_prepend_append( + self, pytester: Pytester, monkeypatch: MonkeyPatch + ) -> None: + root1 = pytester.mkdir("root1") + root2 = pytester.mkdir("root2") + root1.joinpath("x456.py").touch() + root2.joinpath("x456.py").touch() + p = root2.joinpath("test_x456.py") monkeypatch.syspath_prepend(str(root1)) - p.write( + p.write_text( textwrap.dedent( """\ import x456 @@ -52,25 +61,26 @@ def test(): ) ) ) - with root2.as_cwd(): - reprec = testdir.inline_run("--import-mode=append") + with monkeypatch.context() as mp: + mp.chdir(root2) + reprec = pytester.inline_run("--import-mode=append") reprec.assertoutcome(passed=0, failed=1) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=1) - def test_syntax_error_in_module(self, testdir): - modcol = testdir.getmodulecol("this is a syntax error") + def test_syntax_error_in_module(self, pytester: Pytester) -> None: + modcol = pytester.getmodulecol("this is a syntax error") pytest.raises(modcol.CollectError, modcol.collect) pytest.raises(modcol.CollectError, modcol.collect) - def test_module_considers_pluginmanager_at_import(self, testdir): - modcol = testdir.getmodulecol("pytest_plugins='xasdlkj',") + def test_module_considers_pluginmanager_at_import(self, pytester: Pytester) -> None: + modcol = pytester.getmodulecol("pytest_plugins='xasdlkj',") pytest.raises(ImportError, lambda: modcol.obj) - def test_invalid_test_module_name(self, testdir): - a = testdir.mkdir("a") - a.ensure("test_one.part1.py") - result = testdir.runpytest() + def test_invalid_test_module_name(self, pytester: Pytester) -> None: + a = pytester.mkdir("a") + a.joinpath("test_one.part1.py").touch() + result = pytester.runpytest() result.stdout.fnmatch_lines( [ "ImportError while importing test module*test_one.part1*", @@ -79,24 +89,26 @@ def test_invalid_test_module_name(self, testdir): ) @pytest.mark.parametrize("verbose", [0, 1, 2]) - def test_show_traceback_import_error(self, testdir, verbose): + def test_show_traceback_import_error( + self, pytester: Pytester, verbose: int + ) -> None: """Import errors when collecting modules should display the traceback (#1976). With low verbosity we omit pytest and internal modules, otherwise show all traceback entries. """ - testdir.makepyfile( + pytester.makepyfile( foo_traceback_import_error=""" from bar_traceback_import_error import NOT_AVAILABLE """, bar_traceback_import_error="", ) - testdir.makepyfile( + pytester.makepyfile( """ import foo_traceback_import_error """ ) args = ("-v",) * verbose - result = testdir.runpytest(*args) + result = pytester.runpytest(*args) result.stdout.fnmatch_lines( [ "ImportError while importing test module*", @@ -113,12 +125,12 @@ def test_show_traceback_import_error(self, testdir, verbose): else: assert "_pytest" not in stdout - def test_show_traceback_import_error_unicode(self, testdir): + def test_show_traceback_import_error_unicode(self, pytester: Pytester) -> None: """Check test modules collected which raise ImportError with unicode messages are handled properly (#2336). """ - testdir.makepyfile("raise ImportError('Something bad happened ☺')") - result = testdir.runpytest() + pytester.makepyfile("raise ImportError('Something bad happened ☺')") + result = pytester.runpytest() result.stdout.fnmatch_lines( [ "ImportError while importing test module*", @@ -130,15 +142,15 @@ def test_show_traceback_import_error_unicode(self, testdir): class TestClass: - def test_class_with_init_warning(self, testdir): - testdir.makepyfile( + def test_class_with_init_warning(self, pytester: Pytester) -> None: + pytester.makepyfile( """ class TestClass1(object): def __init__(self): pass """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines( [ "*cannot collect test class 'TestClass1' because it has " @@ -146,15 +158,15 @@ def __init__(self): ] ) - def test_class_with_new_warning(self, testdir): - testdir.makepyfile( + def test_class_with_new_warning(self, pytester: Pytester) -> None: + pytester.makepyfile( """ class TestClass1(object): def __new__(self): pass """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines( [ "*cannot collect test class 'TestClass1' because it has " @@ -162,19 +174,19 @@ def __new__(self): ] ) - def test_class_subclassobject(self, testdir): - testdir.getmodulecol( + def test_class_subclassobject(self, pytester: Pytester) -> None: + pytester.getmodulecol( """ class test(object): pass """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["*collected 0*"]) - def test_static_method(self, testdir): + def test_static_method(self, pytester: Pytester) -> None: """Support for collecting staticmethod tests (#2528, #2699)""" - testdir.getmodulecol( + pytester.getmodulecol( """ import pytest class Test(object): @@ -191,11 +203,11 @@ def test_fix(fix): assert fix == 1 """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["*collected 2 items*", "*2 passed in*"]) - def test_setup_teardown_class_as_classmethod(self, testdir): - testdir.makepyfile( + def test_setup_teardown_class_as_classmethod(self, pytester: Pytester) -> None: + pytester.makepyfile( test_mod1=""" class TestClassMethod(object): @classmethod @@ -208,11 +220,11 @@ def teardown_class(cls): pass """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["*1 passed*"]) - def test_issue1035_obj_has_getattr(self, testdir): - modcol = testdir.getmodulecol( + def test_issue1035_obj_has_getattr(self, pytester: Pytester) -> None: + modcol = pytester.getmodulecol( """ class Chameleon(object): def __getattr__(self, name): @@ -223,22 +235,22 @@ def __getattr__(self, name): colitems = modcol.collect() assert len(colitems) == 0 - def test_issue1579_namedtuple(self, testdir): - testdir.makepyfile( + def test_issue1579_namedtuple(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import collections TestCase = collections.namedtuple('TestCase', ['a']) """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines( "*cannot collect test class 'TestCase' " "because it has a __new__ constructor*" ) - def test_issue2234_property(self, testdir): - testdir.makepyfile( + def test_issue2234_property(self, pytester: Pytester) -> None: + pytester.makepyfile( """ class TestCase(object): @property @@ -246,20 +258,20 @@ def prop(self): raise NotImplementedError() """ ) - result = testdir.runpytest() + result = pytester.runpytest() assert result.ret == ExitCode.NO_TESTS_COLLECTED class TestFunction: - def test_getmodulecollector(self, testdir): - item = testdir.getitem("def test_func(): pass") + def test_getmodulecollector(self, pytester: Pytester) -> None: + item = pytester.getitem("def test_func(): pass") modcol = item.getparent(pytest.Module) assert isinstance(modcol, pytest.Module) assert hasattr(modcol.obj, "test_func") @pytest.mark.filterwarnings("default") - def test_function_as_object_instance_ignored(self, testdir): - testdir.makepyfile( + def test_function_as_object_instance_ignored(self, pytester: Pytester) -> None: + pytester.makepyfile( """ class A(object): def __call__(self, tmpdir): @@ -268,7 +280,7 @@ def __call__(self, tmpdir): test_a = A() """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines( [ "collected 0 items", @@ -278,37 +290,37 @@ def __call__(self, tmpdir): ) @staticmethod - def make_function(testdir, **kwargs): + def make_function(pytester: Pytester, **kwargs: Any) -> Any: from _pytest.fixtures import FixtureManager - config = testdir.parseconfigure() - session = testdir.Session.from_config(config) + config = pytester.parseconfigure() + session = pytester.Session.from_config(config) session._fixturemanager = FixtureManager(session) return pytest.Function.from_parent(parent=session, **kwargs) - def test_function_equality(self, testdir): + def test_function_equality(self, pytester: Pytester) -> None: def func1(): pass def func2(): pass - f1 = self.make_function(testdir, name="name", callobj=func1) + f1 = self.make_function(pytester, name="name", callobj=func1) assert f1 == f1 f2 = self.make_function( - testdir, name="name", callobj=func2, originalname="foobar" + pytester, name="name", callobj=func2, originalname="foobar" ) assert f1 != f2 - def test_repr_produces_actual_test_id(self, testdir): + def test_repr_produces_actual_test_id(self, pytester: Pytester) -> None: f = self.make_function( - testdir, name=r"test[\xe5]", callobj=self.test_repr_produces_actual_test_id + pytester, name=r"test[\xe5]", callobj=self.test_repr_produces_actual_test_id ) assert repr(f) == r"" - def test_issue197_parametrize_emptyset(self, testdir): - testdir.makepyfile( + def test_issue197_parametrize_emptyset(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @pytest.mark.parametrize('arg', []) @@ -316,11 +328,11 @@ def test_function(arg): pass """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(skipped=1) - def test_single_tuple_unwraps_values(self, testdir): - testdir.makepyfile( + def test_single_tuple_unwraps_values(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @pytest.mark.parametrize(('arg',), [(1,)]) @@ -328,11 +340,11 @@ def test_function(arg): assert arg == 1 """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=1) - def test_issue213_parametrize_value_no_equal(self, testdir): - testdir.makepyfile( + def test_issue213_parametrize_value_no_equal(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest class A(object): @@ -343,12 +355,12 @@ def test_function(arg): assert arg.__class__.__name__ == "A" """ ) - reprec = testdir.inline_run("--fulltrace") + reprec = pytester.inline_run("--fulltrace") reprec.assertoutcome(passed=1) - def test_parametrize_with_non_hashable_values(self, testdir): + def test_parametrize_with_non_hashable_values(self, pytester: Pytester) -> None: """Test parametrization with non-hashable values.""" - testdir.makepyfile( + pytester.makepyfile( """ archival_mapping = { '1.0': {'tag': '1.0'}, @@ -363,12 +375,14 @@ def test_archival_to_version(key, value): assert value == archival_mapping[key] """ ) - rec = testdir.inline_run() + rec = pytester.inline_run() rec.assertoutcome(passed=2) - def test_parametrize_with_non_hashable_values_indirect(self, testdir): + def test_parametrize_with_non_hashable_values_indirect( + self, pytester: Pytester + ) -> None: """Test parametrization with non-hashable values with indirect parametrization.""" - testdir.makepyfile( + pytester.makepyfile( """ archival_mapping = { '1.0': {'tag': '1.0'}, @@ -392,12 +406,12 @@ def test_archival_to_version(key, value): assert value == archival_mapping[key] """ ) - rec = testdir.inline_run() + rec = pytester.inline_run() rec.assertoutcome(passed=2) - def test_parametrize_overrides_fixture(self, testdir): + def test_parametrize_overrides_fixture(self, pytester: Pytester) -> None: """Test parametrization when parameter overrides existing fixture with same name.""" - testdir.makepyfile( + pytester.makepyfile( """ import pytest @@ -421,12 +435,14 @@ def test_overridden_via_multiparam(other, value): assert value == 'overridden' """ ) - rec = testdir.inline_run() + rec = pytester.inline_run() rec.assertoutcome(passed=3) - def test_parametrize_overrides_parametrized_fixture(self, testdir): + def test_parametrize_overrides_parametrized_fixture( + self, pytester: Pytester + ) -> None: """Test parametrization when parameter overrides existing parametrized fixture with same name.""" - testdir.makepyfile( + pytester.makepyfile( """ import pytest @@ -440,12 +456,14 @@ def test_overridden_via_param(value): assert value == 'overridden' """ ) - rec = testdir.inline_run() + rec = pytester.inline_run() rec.assertoutcome(passed=1) - def test_parametrize_overrides_indirect_dependency_fixture(self, testdir): + def test_parametrize_overrides_indirect_dependency_fixture( + self, pytester: Pytester + ) -> None: """Test parametrization when parameter overrides a fixture that a test indirectly depends on""" - testdir.makepyfile( + pytester.makepyfile( """ import pytest @@ -471,11 +489,11 @@ def test_it(fix1): assert not fix3_instantiated """ ) - rec = testdir.inline_run() + rec = pytester.inline_run() rec.assertoutcome(passed=1) - def test_parametrize_with_mark(self, testdir): - items = testdir.getitems( + def test_parametrize_with_mark(self, pytester: Pytester) -> None: + items = pytester.getitems( """ import pytest @pytest.mark.foo @@ -495,8 +513,8 @@ def test_function(arg): ) assert "foo" in keywords[1] and "bar" in keywords[1] and "baz" in keywords[1] - def test_parametrize_with_empty_string_arguments(self, testdir): - items = testdir.getitems( + def test_parametrize_with_empty_string_arguments(self, pytester: Pytester) -> None: + items = pytester.getitems( """\ import pytest @@ -508,8 +526,8 @@ def test(v, w): ... names = {item.name for item in items} assert names == {"test[-]", "test[ -]", "test[- ]", "test[ - ]"} - def test_function_equality_with_callspec(self, testdir): - items = testdir.getitems( + def test_function_equality_with_callspec(self, pytester: Pytester) -> None: + items = pytester.getitems( """ import pytest @pytest.mark.parametrize('arg', [1,2]) @@ -520,8 +538,8 @@ def test_function(arg): assert items[0] != items[1] assert not (items[0] == items[1]) - def test_pyfunc_call(self, testdir): - item = testdir.getitem("def test_func(): raise ValueError") + def test_pyfunc_call(self, pytester: Pytester) -> None: + item = pytester.getitem("def test_func(): raise ValueError") config = item.config class MyPlugin1: @@ -537,8 +555,8 @@ def pytest_pyfunc_call(self): config.hook.pytest_runtest_setup(item=item) config.hook.pytest_pyfunc_call(pyfuncitem=item) - def test_multiple_parametrize(self, testdir): - modcol = testdir.getmodulecol( + def test_multiple_parametrize(self, pytester: Pytester) -> None: + modcol = pytester.getmodulecol( """ import pytest @pytest.mark.parametrize('x', [0, 1]) @@ -553,8 +571,8 @@ def test1(x, y): assert colitems[2].name == "test1[3-0]" assert colitems[3].name == "test1[3-1]" - def test_issue751_multiple_parametrize_with_ids(self, testdir): - modcol = testdir.getmodulecol( + def test_issue751_multiple_parametrize_with_ids(self, pytester: Pytester) -> None: + modcol = pytester.getmodulecol( """ import pytest @pytest.mark.parametrize('x', [0], ids=['c']) @@ -572,8 +590,8 @@ def test2(self, x, y): assert colitems[2].name == "test2[a-c]" assert colitems[3].name == "test2[b-c]" - def test_parametrize_skipif(self, testdir): - testdir.makepyfile( + def test_parametrize_skipif(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @@ -584,11 +602,11 @@ def test_skip_if(x): assert x < 2 """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["* 2 passed, 1 skipped in *"]) - def test_parametrize_skip(self, testdir): - testdir.makepyfile( + def test_parametrize_skip(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @@ -599,11 +617,11 @@ def test_skip(x): assert x < 2 """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["* 2 passed, 1 skipped in *"]) - def test_parametrize_skipif_no_skip(self, testdir): - testdir.makepyfile( + def test_parametrize_skipif_no_skip(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @@ -614,11 +632,11 @@ def test_skipif_no_skip(x): assert x < 2 """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["* 1 failed, 2 passed in *"]) - def test_parametrize_xfail(self, testdir): - testdir.makepyfile( + def test_parametrize_xfail(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @@ -629,11 +647,11 @@ def test_xfail(x): assert x < 2 """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["* 2 passed, 1 xfailed in *"]) - def test_parametrize_passed(self, testdir): - testdir.makepyfile( + def test_parametrize_passed(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @@ -644,11 +662,11 @@ def test_xfail(x): pass """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["* 2 passed, 1 xpassed in *"]) - def test_parametrize_xfail_passed(self, testdir): - testdir.makepyfile( + def test_parametrize_xfail_passed(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @@ -659,11 +677,11 @@ def test_passed(x): pass """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["* 3 passed in *"]) - def test_function_originalname(self, testdir: Testdir) -> None: - items = testdir.getitems( + def test_function_originalname(self, pytester: Pytester) -> None: + items = pytester.getitems( """ import pytest @@ -685,14 +703,14 @@ def test_no_param(): "test_no_param", ] - def test_function_with_square_brackets(self, testdir: Testdir) -> None: + def test_function_with_square_brackets(self, pytester: Pytester) -> None: """Check that functions with square brackets don't cause trouble.""" - p1 = testdir.makepyfile( + p1 = pytester.makepyfile( """ locals()["test_foo[name]"] = lambda: None """ ) - result = testdir.runpytest("-v", str(p1)) + result = pytester.runpytest("-v", str(p1)) result.stdout.fnmatch_lines( [ "test_function_with_square_brackets.py::test_foo[[]name[]] PASSED *", @@ -702,23 +720,23 @@ def test_function_with_square_brackets(self, testdir: Testdir) -> None: class TestSorting: - def test_check_equality(self, testdir) -> None: - modcol = testdir.getmodulecol( + def test_check_equality(self, pytester: Pytester) -> None: + modcol = pytester.getmodulecol( """ def test_pass(): pass def test_fail(): assert 0 """ ) - fn1 = testdir.collect_by_name(modcol, "test_pass") + fn1 = pytester.collect_by_name(modcol, "test_pass") assert isinstance(fn1, pytest.Function) - fn2 = testdir.collect_by_name(modcol, "test_pass") + fn2 = pytester.collect_by_name(modcol, "test_pass") assert isinstance(fn2, pytest.Function) assert fn1 == fn2 assert fn1 != modcol assert hash(fn1) == hash(fn2) - fn3 = testdir.collect_by_name(modcol, "test_fail") + fn3 = pytester.collect_by_name(modcol, "test_fail") assert isinstance(fn3, pytest.Function) assert not (fn1 == fn3) assert fn1 != fn3 @@ -730,8 +748,8 @@ def test_fail(): assert 0 assert [1, 2, 3] != fn # type: ignore[comparison-overlap] assert modcol != fn - def test_allow_sane_sorting_for_decorators(self, testdir): - modcol = testdir.getmodulecol( + def test_allow_sane_sorting_for_decorators(self, pytester: Pytester) -> None: + modcol = pytester.getmodulecol( """ def dec(f): g = lambda: f(2) @@ -754,8 +772,8 @@ def test_a(y): class TestConftestCustomization: - def test_pytest_pycollect_module(self, testdir): - testdir.makeconftest( + def test_pytest_pycollect_module(self, pytester: Pytester) -> None: + pytester.makeconftest( """ import pytest class MyModule(pytest.Module): @@ -765,14 +783,15 @@ def pytest_pycollect_makemodule(path, parent): return MyModule.from_parent(fspath=path, parent=parent) """ ) - testdir.makepyfile("def test_some(): pass") - testdir.makepyfile(test_xyz="def test_func(): pass") - result = testdir.runpytest("--collect-only") + pytester.makepyfile("def test_some(): pass") + pytester.makepyfile(test_xyz="def test_func(): pass") + result = pytester.runpytest("--collect-only") result.stdout.fnmatch_lines(["* None: + b = pytester.path.joinpath("a", "b") + b.mkdir(parents=True) + b.joinpath("conftest.py").write_text( textwrap.dedent( """\ import pytest @@ -784,7 +803,7 @@ def pytest_pycollect_makemodule(): """ ) ) - b.join("test_module.py").write( + b.joinpath("test_module.py").write_text( textwrap.dedent( """\ def test_hello(): @@ -792,12 +811,13 @@ def test_hello(): """ ) ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=1) - def test_customized_pymakeitem(self, testdir): - b = testdir.mkdir("a").mkdir("b") - b.join("conftest.py").write( + def test_customized_pymakeitem(self, pytester: Pytester) -> None: + b = pytester.path.joinpath("a", "b") + b.mkdir(parents=True) + b.joinpath("conftest.py").write_text( textwrap.dedent( """\ import pytest @@ -812,7 +832,7 @@ def pytest_pycollect_makeitem(): """ ) ) - b.join("test_module.py").write( + b.joinpath("test_module.py").write_text( textwrap.dedent( """\ import pytest @@ -825,11 +845,11 @@ def test_hello(obj): """ ) ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=1) - def test_pytest_pycollect_makeitem(self, testdir): - testdir.makeconftest( + def test_pytest_pycollect_makeitem(self, pytester: Pytester) -> None: + pytester.makeconftest( """ import pytest class MyFunction(pytest.Function): @@ -839,16 +859,16 @@ def pytest_pycollect_makeitem(collector, name, obj): return MyFunction.from_parent(name=name, parent=collector) """ ) - testdir.makepyfile("def some(): pass") - result = testdir.runpytest("--collect-only") + pytester.makepyfile("def some(): pass") + result = pytester.runpytest("--collect-only") result.stdout.fnmatch_lines(["*MyFunction*some*"]) - def test_issue2369_collect_module_fileext(self, testdir): + def test_issue2369_collect_module_fileext(self, pytester: Pytester) -> None: """Ensure we can collect files with weird file extensions as Python modules (#2369)""" # We'll implement a little finder and loader to import files containing # Python source code whose file extension is ".narf". - testdir.makeconftest( + pytester.makeconftest( """ import sys, os, imp from _pytest.python import Module @@ -866,17 +886,17 @@ def pytest_collect_file(path, parent): if path.ext == ".narf": return Module.from_parent(fspath=path, parent=parent)""" ) - testdir.makefile( + pytester.makefile( ".narf", """\ def test_something(): assert 1 + 1 == 2""", ) # Use runpytest_subprocess, since we're futzing with sys.meta_path. - result = testdir.runpytest_subprocess() + result = pytester.runpytest_subprocess() result.stdout.fnmatch_lines(["*1 passed*"]) - def test_early_ignored_attributes(self, testdir: Testdir) -> None: + def test_early_ignored_attributes(self, pytester: Pytester) -> None: """Builtin attributes should be ignored early on, even if configuration would otherwise allow them. @@ -884,14 +904,14 @@ def test_early_ignored_attributes(self, testdir: Testdir) -> None: although it tests PytestCollectionWarning is not raised, while it would have been raised otherwise. """ - testdir.makeini( + pytester.makeini( """ [pytest] python_classes=* python_functions=* """ ) - testdir.makepyfile( + pytester.makepyfile( """ class TestEmpty: pass @@ -900,15 +920,15 @@ def test_real(): pass """ ) - items, rec = testdir.inline_genitems() + items, rec = pytester.inline_genitems() assert rec.ret == 0 assert len(items) == 1 -def test_setup_only_available_in_subdir(testdir): - sub1 = testdir.mkpydir("sub1") - sub2 = testdir.mkpydir("sub2") - sub1.join("conftest.py").write( +def test_setup_only_available_in_subdir(pytester: Pytester) -> None: + sub1 = pytester.mkpydir("sub1") + sub2 = pytester.mkpydir("sub2") + sub1.joinpath("conftest.py").write_text( textwrap.dedent( """\ import pytest @@ -921,7 +941,7 @@ def pytest_runtest_teardown(item): """ ) ) - sub2.join("conftest.py").write( + sub2.joinpath("conftest.py").write_text( textwrap.dedent( """\ import pytest @@ -934,14 +954,14 @@ def pytest_runtest_teardown(item): """ ) ) - sub1.join("test_in_sub1.py").write("def test_1(): pass") - sub2.join("test_in_sub2.py").write("def test_2(): pass") - result = testdir.runpytest("-v", "-s") + sub1.joinpath("test_in_sub1.py").write_text("def test_1(): pass") + sub2.joinpath("test_in_sub2.py").write_text("def test_2(): pass") + result = pytester.runpytest("-v", "-s") result.assert_outcomes(passed=2) -def test_modulecol_roundtrip(testdir): - modcol = testdir.getmodulecol("pass", withinit=False) +def test_modulecol_roundtrip(pytester: Pytester) -> None: + modcol = pytester.getmodulecol("pass", withinit=False) trail = modcol.nodeid newcol = modcol.session.perform_collect([trail], genitems=0)[0] assert modcol.name == newcol.name @@ -956,8 +976,8 @@ def test_skip_simple(self): assert excinfo.traceback[-2].frame.code.name == "test_skip_simple" assert not excinfo.traceback[-2].ishidden() - def test_traceback_argsetup(self, testdir): - testdir.makeconftest( + def test_traceback_argsetup(self, pytester: Pytester) -> None: + pytester.makeconftest( """ import pytest @@ -966,8 +986,8 @@ def hello(request): raise ValueError("xyz") """ ) - p = testdir.makepyfile("def test(hello): pass") - result = testdir.runpytest(p) + p = pytester.makepyfile("def test(hello): pass") + result = pytester.runpytest(p) assert result.ret != 0 out = result.stdout.str() assert "xyz" in out @@ -975,14 +995,14 @@ def hello(request): numentries = out.count("_ _ _") # separator for traceback entries assert numentries == 0 - result = testdir.runpytest("--fulltrace", p) + result = pytester.runpytest("--fulltrace", p) out = result.stdout.str() assert "conftest.py:5: ValueError" in out numentries = out.count("_ _ _ _") # separator for traceback entries assert numentries > 3 - def test_traceback_error_during_import(self, testdir): - testdir.makepyfile( + def test_traceback_error_during_import(self, pytester: Pytester) -> None: + pytester.makepyfile( """ x = 1 x = 2 @@ -990,21 +1010,23 @@ def test_traceback_error_during_import(self, testdir): asd """ ) - result = testdir.runpytest() + result = pytester.runpytest() assert result.ret != 0 out = result.stdout.str() assert "x = 1" not in out assert "x = 2" not in out result.stdout.fnmatch_lines([" *asd*", "E*NameError*"]) - result = testdir.runpytest("--fulltrace") + result = pytester.runpytest("--fulltrace") out = result.stdout.str() assert "x = 1" in out assert "x = 2" in out result.stdout.fnmatch_lines([">*asd*", "E*NameError*"]) - def test_traceback_filter_error_during_fixture_collection(self, testdir): + def test_traceback_filter_error_during_fixture_collection( + self, pytester: Pytester + ) -> None: """Integration test for issue #995.""" - testdir.makepyfile( + pytester.makepyfile( """ import pytest @@ -1022,7 +1044,7 @@ def test_failing_fixture(fail_fixture): pass """ ) - result = testdir.runpytest() + result = pytester.runpytest() assert result.ret != 0 out = result.stdout.str() assert "INTERNALERROR>" not in out @@ -1039,6 +1061,7 @@ def test_filter_traceback_generated_code(self) -> None: """ from _pytest._code import filter_traceback + tb = None try: ns: Dict[str, Any] = {} exec("def foo(): raise ValueError", ns) @@ -1051,7 +1074,7 @@ def test_filter_traceback_generated_code(self) -> None: assert isinstance(traceback[-1].path, str) assert not filter_traceback(traceback[-1]) - def test_filter_traceback_path_no_longer_valid(self, testdir) -> None: + def test_filter_traceback_path_no_longer_valid(self, pytester: Pytester) -> None: """Test that filter_traceback() works with the fact that _pytest._code.code.Code.path attribute might return an str object. @@ -1060,13 +1083,14 @@ def test_filter_traceback_path_no_longer_valid(self, testdir) -> None: """ from _pytest._code import filter_traceback - testdir.syspathinsert() - testdir.makepyfile( + pytester.syspathinsert() + pytester.makepyfile( filter_traceback_entry_as_str=""" def foo(): raise ValueError """ ) + tb = None try: import filter_traceback_entry_as_str @@ -1075,15 +1099,15 @@ def foo(): _, _, tb = sys.exc_info() assert tb is not None - testdir.tmpdir.join("filter_traceback_entry_as_str.py").remove() + pytester.path.joinpath("filter_traceback_entry_as_str.py").unlink() traceback = _pytest._code.Traceback(tb) assert isinstance(traceback[-1].path, str) assert filter_traceback(traceback[-1]) class TestReportInfo: - def test_itemreport_reportinfo(self, testdir): - testdir.makeconftest( + def test_itemreport_reportinfo(self, pytester: Pytester) -> None: + pytester.makeconftest( """ import pytest class MyFunction(pytest.Function): @@ -1094,26 +1118,27 @@ def pytest_pycollect_makeitem(collector, name, obj): return MyFunction.from_parent(name=name, parent=collector) """ ) - item = testdir.getitem("def test_func(): pass") + item = pytester.getitem("def test_func(): pass") item.config.pluginmanager.getplugin("runner") assert item.location == ("ABCDE", 42, "custom") - def test_func_reportinfo(self, testdir): - item = testdir.getitem("def test_func(): pass") + def test_func_reportinfo(self, pytester: Pytester) -> None: + item = pytester.getitem("def test_func(): pass") fspath, lineno, modpath = item.reportinfo() assert fspath == item.fspath assert lineno == 0 assert modpath == "test_func" - def test_class_reportinfo(self, testdir): - modcol = testdir.getmodulecol( + def test_class_reportinfo(self, pytester: Pytester) -> None: + modcol = pytester.getmodulecol( """ # lineno 0 class TestClass(object): def test_hello(self): pass """ ) - classcol = testdir.collect_by_name(modcol, "TestClass") + classcol = pytester.collect_by_name(modcol, "TestClass") + assert isinstance(classcol, Class) fspath, lineno, msg = classcol.reportinfo() assert fspath == modcol.fspath assert lineno == 1 @@ -1122,26 +1147,28 @@ def test_hello(self): pass @pytest.mark.filterwarnings( "ignore:usage of Generator.Function is deprecated, please use pytest.Function instead" ) - def test_reportinfo_with_nasty_getattr(self, testdir): + def test_reportinfo_with_nasty_getattr(self, pytester: Pytester) -> None: # https://github.com/pytest-dev/pytest/issues/1204 - modcol = testdir.getmodulecol( + modcol = pytester.getmodulecol( """ # lineno 0 class TestClass(object): def __getattr__(self, name): return "this is not an int" - def test_foo(self): + def intest_foo(self): pass """ ) - classcol = testdir.collect_by_name(modcol, "TestClass") - instance = classcol.collect()[0] + classcol = pytester.collect_by_name(modcol, "TestClass") + assert isinstance(classcol, Class) + instance = list(classcol.collect())[0] + assert isinstance(instance, Instance) fspath, lineno, msg = instance.reportinfo() -def test_customized_python_discovery(testdir): - testdir.makeini( +def test_customized_python_discovery(pytester: Pytester) -> None: + pytester.makeini( """ [pytest] python_files=check_*.py @@ -1149,7 +1176,7 @@ def test_customized_python_discovery(testdir): python_functions=check """ ) - p = testdir.makepyfile( + p = pytester.makepyfile( """ def check_simple(): pass @@ -1158,41 +1185,41 @@ def check_meth(self): pass """ ) - p2 = p.new(basename=p.basename.replace("test", "check")) - p.move(p2) - result = testdir.runpytest("--collect-only", "-s") + p2 = p.with_name(p.name.replace("test", "check")) + p.rename(p2) + result = pytester.runpytest("--collect-only", "-s") result.stdout.fnmatch_lines( ["*check_customized*", "*check_simple*", "*CheckMyApp*", "*check_meth*"] ) - result = testdir.runpytest() + result = pytester.runpytest() assert result.ret == 0 result.stdout.fnmatch_lines(["*2 passed*"]) -def test_customized_python_discovery_functions(testdir): - testdir.makeini( +def test_customized_python_discovery_functions(pytester: Pytester) -> None: + pytester.makeini( """ [pytest] python_functions=_test """ ) - testdir.makepyfile( + pytester.makepyfile( """ def _test_underscore(): pass """ ) - result = testdir.runpytest("--collect-only", "-s") + result = pytester.runpytest("--collect-only", "-s") result.stdout.fnmatch_lines(["*_test_underscore*"]) - result = testdir.runpytest() + result = pytester.runpytest() assert result.ret == 0 result.stdout.fnmatch_lines(["*1 passed*"]) -def test_unorderable_types(testdir): - testdir.makepyfile( +def test_unorderable_types(pytester: Pytester) -> None: + pytester.makepyfile( """ class TestJoinEmpty(object): pass @@ -1205,19 +1232,19 @@ class Test(object): TestFoo = make_test() """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.no_fnmatch_line("*TypeError*") assert result.ret == ExitCode.NO_TESTS_COLLECTED @pytest.mark.filterwarnings("default") -def test_dont_collect_non_function_callable(testdir): +def test_dont_collect_non_function_callable(pytester: Pytester) -> None: """Test for issue https://github.com/pytest-dev/pytest/issues/331 In this case an INTERNALERROR occurred trying to report the failure of a test like this one because pytest failed to get the source lines. """ - testdir.makepyfile( + pytester.makepyfile( """ class Oh(object): def __call__(self): @@ -1229,7 +1256,7 @@ def test_real(): pass """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines( [ "*collected 1 item*", @@ -1239,21 +1266,21 @@ def test_real(): ) -def test_class_injection_does_not_break_collection(testdir): +def test_class_injection_does_not_break_collection(pytester: Pytester) -> None: """Tests whether injection during collection time will terminate testing. In this case the error should not occur if the TestClass itself is modified during collection time, and the original method list is still used for collection. """ - testdir.makeconftest( + pytester.makeconftest( """ from test_inject import TestClass def pytest_generate_tests(metafunc): TestClass.changed_var = {} """ ) - testdir.makepyfile( + pytester.makepyfile( test_inject=''' class TestClass(object): def test_injection(self): @@ -1261,7 +1288,7 @@ def test_injection(self): pass ''' ) - result = testdir.runpytest() + result = pytester.runpytest() assert ( "RuntimeError: dictionary changed size during iteration" not in result.stdout.str() @@ -1269,16 +1296,16 @@ def test_injection(self): result.stdout.fnmatch_lines(["*1 passed*"]) -def test_syntax_error_with_non_ascii_chars(testdir): +def test_syntax_error_with_non_ascii_chars(pytester: Pytester) -> None: """Fix decoding issue while formatting SyntaxErrors during collection (#578).""" - testdir.makepyfile("☃") - result = testdir.runpytest() + pytester.makepyfile("☃") + result = pytester.runpytest() result.stdout.fnmatch_lines(["*ERROR collecting*", "*SyntaxError*", "*1 error in*"]) -def test_collect_error_with_fulltrace(testdir): - testdir.makepyfile("assert 0") - result = testdir.runpytest("--fulltrace") +def test_collect_error_with_fulltrace(pytester: Pytester) -> None: + pytester.makepyfile("assert 0") + result = pytester.runpytest("--fulltrace") result.stdout.fnmatch_lines( [ "collected 0 items / 1 error", @@ -1295,14 +1322,14 @@ def test_collect_error_with_fulltrace(testdir): ) -def test_skip_duplicates_by_default(testdir): +def test_skip_duplicates_by_default(pytester: Pytester) -> None: """Test for issue https://github.com/pytest-dev/pytest/issues/1609 (#1609) Ignore duplicate directories. """ - a = testdir.mkdir("a") - fh = a.join("test_a.py") - fh.write( + a = pytester.mkdir("a") + fh = a.joinpath("test_a.py") + fh.write_text( textwrap.dedent( """\ import pytest @@ -1311,18 +1338,18 @@ def test_real(): """ ) ) - result = testdir.runpytest(a.strpath, a.strpath) + result = pytester.runpytest(str(a), str(a)) result.stdout.fnmatch_lines(["*collected 1 item*"]) -def test_keep_duplicates(testdir): +def test_keep_duplicates(pytester: Pytester) -> None: """Test for issue https://github.com/pytest-dev/pytest/issues/1609 (#1609) Use --keep-duplicates to collect tests from duplicate directories. """ - a = testdir.mkdir("a") - fh = a.join("test_a.py") - fh.write( + a = pytester.mkdir("a") + fh = a.joinpath("test_a.py") + fh.write_text( textwrap.dedent( """\ import pytest @@ -1331,24 +1358,24 @@ def test_real(): """ ) ) - result = testdir.runpytest("--keep-duplicates", a.strpath, a.strpath) + result = pytester.runpytest("--keep-duplicates", str(a), str(a)) result.stdout.fnmatch_lines(["*collected 2 item*"]) -def test_package_collection_infinite_recursion(testdir): - testdir.copy_example("collect/package_infinite_recursion") - result = testdir.runpytest() +def test_package_collection_infinite_recursion(pytester: Pytester) -> None: + pytester.copy_example("collect/package_infinite_recursion") + result = pytester.runpytest() result.stdout.fnmatch_lines(["*1 passed*"]) -def test_package_collection_init_given_as_argument(testdir): +def test_package_collection_init_given_as_argument(pytester: Pytester) -> None: """Regression test for #3749""" - p = testdir.copy_example("collect/package_init_given_as_arg") - result = testdir.runpytest(p / "pkg" / "__init__.py") + p = pytester.copy_example("collect/package_init_given_as_arg") + result = pytester.runpytest(p / "pkg" / "__init__.py") result.stdout.fnmatch_lines(["*1 passed*"]) -def test_package_with_modules(testdir): +def test_package_with_modules(pytester: Pytester) -> None: """ . └── root @@ -1363,32 +1390,35 @@ def test_package_with_modules(testdir): └── test_in_sub2.py """ - root = testdir.mkpydir("root") - sub1 = root.mkdir("sub1") - sub1.ensure("__init__.py") - sub1_test = sub1.mkdir("sub1_1") - sub1_test.ensure("__init__.py") - sub2 = root.mkdir("sub2") - sub2_test = sub2.mkdir("sub2") + root = pytester.mkpydir("root") + sub1 = root.joinpath("sub1") + sub1_test = sub1.joinpath("sub1_1") + sub1_test.mkdir(parents=True) + for d in (sub1, sub1_test): + d.joinpath("__init__.py").touch() - sub1_test.join("test_in_sub1.py").write("def test_1(): pass") - sub2_test.join("test_in_sub2.py").write("def test_2(): pass") + sub2 = root.joinpath("sub2") + sub2_test = sub2.joinpath("test") + sub2_test.mkdir(parents=True) + + sub1_test.joinpath("test_in_sub1.py").write_text("def test_1(): pass") + sub2_test.joinpath("test_in_sub2.py").write_text("def test_2(): pass") # Execute from . - result = testdir.runpytest("-v", "-s") + result = pytester.runpytest("-v", "-s") result.assert_outcomes(passed=2) # Execute from . with one argument "root" - result = testdir.runpytest("-v", "-s", "root") + result = pytester.runpytest("-v", "-s", "root") result.assert_outcomes(passed=2) # Chdir into package's root and execute with no args - root.chdir() - result = testdir.runpytest("-v", "-s") + os.chdir(root) + result = pytester.runpytest("-v", "-s") result.assert_outcomes(passed=2) -def test_package_ordering(testdir): +def test_package_ordering(pytester: Pytester) -> None: """ . └── root @@ -1402,22 +1432,24 @@ def test_package_ordering(testdir): └── test_sub2.py """ - testdir.makeini( + pytester.makeini( """ [pytest] python_files=*.py """ ) - root = testdir.mkpydir("root") - sub1 = root.mkdir("sub1") - sub1.ensure("__init__.py") - sub2 = root.mkdir("sub2") - sub2_test = sub2.mkdir("sub2") - - root.join("Test_root.py").write("def test_1(): pass") - sub1.join("Test_sub1.py").write("def test_2(): pass") - sub2_test.join("test_sub2.py").write("def test_3(): pass") + root = pytester.mkpydir("root") + sub1 = root.joinpath("sub1") + sub1.mkdir() + sub1.joinpath("__init__.py").touch() + sub2 = root.joinpath("sub2") + sub2_test = sub2.joinpath("test") + sub2_test.mkdir(parents=True) + + root.joinpath("Test_root.py").write_text("def test_1(): pass") + sub1.joinpath("Test_sub1.py").write_text("def test_2(): pass") + sub2_test.joinpath("test_sub2.py").write_text("def test_3(): pass") # Execute from . - result = testdir.runpytest("-v", "-s") + result = pytester.runpytest("-v", "-s") result.assert_outcomes(passed=3) diff --git a/testing/python/fixtures.py b/testing/python/fixtures.py index ac62de608e5..862a65abe10 100644 --- a/testing/python/fixtures.py +++ b/testing/python/fixtures.py @@ -1,3 +1,4 @@ +import os import sys import textwrap from pathlib import Path @@ -7,9 +8,10 @@ from _pytest.compat import getfuncargnames from _pytest.config import ExitCode from _pytest.fixtures import FixtureRequest +from _pytest.monkeypatch import MonkeyPatch from _pytest.pytester import get_public_names from _pytest.pytester import Pytester -from _pytest.pytester import Testdir +from _pytest.python import Function def test_getfuncargnames_functions(): @@ -91,9 +93,9 @@ def test_fillfuncargs_exposed(self): # used by oejskit, kept for compatibility assert pytest._fillfuncargs == fixtures._fillfuncargs - def test_funcarg_lookupfails(self, testdir): - testdir.copy_example() - result = testdir.runpytest() # "--collect-only") + def test_funcarg_lookupfails(self, pytester: Pytester) -> None: + pytester.copy_example() + result = pytester.runpytest() # "--collect-only") assert result.ret != 0 result.stdout.fnmatch_lines( """ @@ -103,60 +105,63 @@ def test_funcarg_lookupfails(self, testdir): """ ) - def test_detect_recursive_dependency_error(self, testdir): - testdir.copy_example() - result = testdir.runpytest() + def test_detect_recursive_dependency_error(self, pytester: Pytester) -> None: + pytester.copy_example() + result = pytester.runpytest() result.stdout.fnmatch_lines( ["*recursive dependency involving fixture 'fix1' detected*"] ) - def test_funcarg_basic(self, testdir): - testdir.copy_example() - item = testdir.getitem(Path("test_funcarg_basic.py")) + def test_funcarg_basic(self, pytester: Pytester) -> None: + pytester.copy_example() + item = pytester.getitem(Path("test_funcarg_basic.py")) + assert isinstance(item, Function) item._request._fillfixtures() del item.funcargs["request"] assert len(get_public_names(item.funcargs)) == 2 assert item.funcargs["some"] == "test_func" assert item.funcargs["other"] == 42 - def test_funcarg_lookup_modulelevel(self, testdir): - testdir.copy_example() - reprec = testdir.inline_run() + def test_funcarg_lookup_modulelevel(self, pytester: Pytester) -> None: + pytester.copy_example() + reprec = pytester.inline_run() reprec.assertoutcome(passed=2) - def test_funcarg_lookup_classlevel(self, testdir): - p = testdir.copy_example() - result = testdir.runpytest(p) + def test_funcarg_lookup_classlevel(self, pytester: Pytester) -> None: + p = pytester.copy_example() + result = pytester.runpytest(p) result.stdout.fnmatch_lines(["*1 passed*"]) - def test_conftest_funcargs_only_available_in_subdir(self, testdir): - testdir.copy_example() - result = testdir.runpytest("-v") + def test_conftest_funcargs_only_available_in_subdir( + self, pytester: Pytester + ) -> None: + pytester.copy_example() + result = pytester.runpytest("-v") result.assert_outcomes(passed=2) - def test_extend_fixture_module_class(self, testdir): - testfile = testdir.copy_example() - result = testdir.runpytest() + def test_extend_fixture_module_class(self, pytester: Pytester) -> None: + testfile = pytester.copy_example() + result = pytester.runpytest() result.stdout.fnmatch_lines(["*1 passed*"]) - result = testdir.runpytest(testfile) + result = pytester.runpytest(testfile) result.stdout.fnmatch_lines(["*1 passed*"]) - def test_extend_fixture_conftest_module(self, testdir): - p = testdir.copy_example() - result = testdir.runpytest() + def test_extend_fixture_conftest_module(self, pytester: Pytester) -> None: + p = pytester.copy_example() + result = pytester.runpytest() result.stdout.fnmatch_lines(["*1 passed*"]) - result = testdir.runpytest(str(next(Path(str(p)).rglob("test_*.py")))) + result = pytester.runpytest(str(next(Path(str(p)).rglob("test_*.py")))) result.stdout.fnmatch_lines(["*1 passed*"]) - def test_extend_fixture_conftest_conftest(self, testdir): - p = testdir.copy_example() - result = testdir.runpytest() + def test_extend_fixture_conftest_conftest(self, pytester: Pytester) -> None: + p = pytester.copy_example() + result = pytester.runpytest() result.stdout.fnmatch_lines(["*1 passed*"]) - result = testdir.runpytest(str(next(Path(str(p)).rglob("test_*.py")))) + result = pytester.runpytest(str(next(Path(str(p)).rglob("test_*.py")))) result.stdout.fnmatch_lines(["*1 passed*"]) - def test_extend_fixture_conftest_plugin(self, testdir): - testdir.makepyfile( + def test_extend_fixture_conftest_plugin(self, pytester: Pytester) -> None: + pytester.makepyfile( testplugin=""" import pytest @@ -165,8 +170,8 @@ def foo(): return 7 """ ) - testdir.syspathinsert() - testdir.makeconftest( + pytester.syspathinsert() + pytester.makeconftest( """ import pytest @@ -177,18 +182,18 @@ def foo(foo): return foo + 7 """ ) - testdir.makepyfile( + pytester.makepyfile( """ def test_foo(foo): assert foo == 14 """ ) - result = testdir.runpytest("-s") + result = pytester.runpytest("-s") assert result.ret == 0 - def test_extend_fixture_plugin_plugin(self, testdir): + def test_extend_fixture_plugin_plugin(self, pytester: Pytester) -> None: # Two plugins should extend each order in loading order - testdir.makepyfile( + pytester.makepyfile( testplugin0=""" import pytest @@ -197,7 +202,7 @@ def foo(): return 7 """ ) - testdir.makepyfile( + pytester.makepyfile( testplugin1=""" import pytest @@ -206,8 +211,8 @@ def foo(foo): return foo + 7 """ ) - testdir.syspathinsert() - testdir.makepyfile( + pytester.syspathinsert() + pytester.makepyfile( """ pytest_plugins = ['testplugin0', 'testplugin1'] @@ -215,12 +220,14 @@ def test_foo(foo): assert foo == 14 """ ) - result = testdir.runpytest() + result = pytester.runpytest() assert result.ret == 0 - def test_override_parametrized_fixture_conftest_module(self, testdir): + def test_override_parametrized_fixture_conftest_module( + self, pytester: Pytester + ) -> None: """Test override of the parametrized fixture with non-parametrized one on the test module level.""" - testdir.makeconftest( + pytester.makeconftest( """ import pytest @@ -229,7 +236,7 @@ def spam(request): return request.param """ ) - testfile = testdir.makepyfile( + testfile = pytester.makepyfile( """ import pytest @@ -241,14 +248,16 @@ def test_spam(spam): assert spam == 'spam' """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["*1 passed*"]) - result = testdir.runpytest(testfile) + result = pytester.runpytest(testfile) result.stdout.fnmatch_lines(["*1 passed*"]) - def test_override_parametrized_fixture_conftest_conftest(self, testdir): + def test_override_parametrized_fixture_conftest_conftest( + self, pytester: Pytester + ) -> None: """Test override of the parametrized fixture with non-parametrized one on the conftest level.""" - testdir.makeconftest( + pytester.makeconftest( """ import pytest @@ -257,8 +266,8 @@ def spam(request): return request.param """ ) - subdir = testdir.mkpydir("subdir") - subdir.join("conftest.py").write( + subdir = pytester.mkpydir("subdir") + subdir.joinpath("conftest.py").write_text( textwrap.dedent( """\ import pytest @@ -269,8 +278,8 @@ def spam(): """ ) ) - testfile = subdir.join("test_spam.py") - testfile.write( + testfile = subdir.joinpath("test_spam.py") + testfile.write_text( textwrap.dedent( """\ def test_spam(spam): @@ -278,14 +287,16 @@ def test_spam(spam): """ ) ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["*1 passed*"]) - result = testdir.runpytest(testfile) + result = pytester.runpytest(testfile) result.stdout.fnmatch_lines(["*1 passed*"]) - def test_override_non_parametrized_fixture_conftest_module(self, testdir): + def test_override_non_parametrized_fixture_conftest_module( + self, pytester: Pytester + ) -> None: """Test override of the non-parametrized fixture with parametrized one on the test module level.""" - testdir.makeconftest( + pytester.makeconftest( """ import pytest @@ -294,7 +305,7 @@ def spam(): return 'spam' """ ) - testfile = testdir.makepyfile( + testfile = pytester.makepyfile( """ import pytest @@ -309,14 +320,16 @@ def test_spam(spam): params['spam'] += 1 """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["*3 passed*"]) - result = testdir.runpytest(testfile) + result = pytester.runpytest(testfile) result.stdout.fnmatch_lines(["*3 passed*"]) - def test_override_non_parametrized_fixture_conftest_conftest(self, testdir): + def test_override_non_parametrized_fixture_conftest_conftest( + self, pytester: Pytester + ) -> None: """Test override of the non-parametrized fixture with parametrized one on the conftest level.""" - testdir.makeconftest( + pytester.makeconftest( """ import pytest @@ -325,8 +338,8 @@ def spam(): return 'spam' """ ) - subdir = testdir.mkpydir("subdir") - subdir.join("conftest.py").write( + subdir = pytester.mkpydir("subdir") + subdir.joinpath("conftest.py").write_text( textwrap.dedent( """\ import pytest @@ -337,8 +350,8 @@ def spam(request): """ ) ) - testfile = subdir.join("test_spam.py") - testfile.write( + testfile = subdir.joinpath("test_spam.py") + testfile.write_text( textwrap.dedent( """\ params = {'spam': 1} @@ -349,18 +362,18 @@ def test_spam(spam): """ ) ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["*3 passed*"]) - result = testdir.runpytest(testfile) + result = pytester.runpytest(testfile) result.stdout.fnmatch_lines(["*3 passed*"]) def test_override_autouse_fixture_with_parametrized_fixture_conftest_conftest( - self, testdir - ): + self, pytester: Pytester + ) -> None: """Test override of the autouse fixture with parametrized one on the conftest level. This test covers the issue explained in issue 1601 """ - testdir.makeconftest( + pytester.makeconftest( """ import pytest @@ -369,8 +382,8 @@ def spam(): return 'spam' """ ) - subdir = testdir.mkpydir("subdir") - subdir.join("conftest.py").write( + subdir = pytester.mkpydir("subdir") + subdir.joinpath("conftest.py").write_text( textwrap.dedent( """\ import pytest @@ -381,8 +394,8 @@ def spam(request): """ ) ) - testfile = subdir.join("test_spam.py") - testfile.write( + testfile = subdir.joinpath("test_spam.py") + testfile.write_text( textwrap.dedent( """\ params = {'spam': 1} @@ -393,16 +406,18 @@ def test_spam(spam): """ ) ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["*3 passed*"]) - result = testdir.runpytest(testfile) + result = pytester.runpytest(testfile) result.stdout.fnmatch_lines(["*3 passed*"]) - def test_override_fixture_reusing_super_fixture_parametrization(self, testdir): + def test_override_fixture_reusing_super_fixture_parametrization( + self, pytester: Pytester + ) -> None: """Override a fixture at a lower level, reusing the higher-level fixture that is parametrized (#1953). """ - testdir.makeconftest( + pytester.makeconftest( """ import pytest @@ -411,7 +426,7 @@ def foo(request): return request.param """ ) - testdir.makepyfile( + pytester.makepyfile( """ import pytest @@ -423,14 +438,16 @@ def test_spam(foo): assert foo in (2, 4) """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["*2 passed*"]) - def test_override_parametrize_fixture_and_indirect(self, testdir): + def test_override_parametrize_fixture_and_indirect( + self, pytester: Pytester + ) -> None: """Override a fixture at a lower level, reusing the higher-level fixture that is parametrized, while also using indirect parametrization. """ - testdir.makeconftest( + pytester.makeconftest( """ import pytest @@ -439,7 +456,7 @@ def foo(request): return request.param """ ) - testdir.makepyfile( + pytester.makepyfile( """ import pytest @@ -457,14 +474,14 @@ def test_spam(bar, foo): assert foo in (2, 4) """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["*2 passed*"]) def test_override_top_level_fixture_reusing_super_fixture_parametrization( - self, testdir - ): + self, pytester: Pytester + ) -> None: """Same as the above test, but with another level of overwriting.""" - testdir.makeconftest( + pytester.makeconftest( """ import pytest @@ -473,7 +490,7 @@ def foo(request): return request.param """ ) - testdir.makepyfile( + pytester.makepyfile( """ import pytest @@ -491,15 +508,17 @@ def test_spam(self, foo): assert foo in (2, 4) """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["*2 passed*"]) - def test_override_parametrized_fixture_with_new_parametrized_fixture(self, testdir): + def test_override_parametrized_fixture_with_new_parametrized_fixture( + self, pytester: Pytester + ) -> None: """Overriding a parametrized fixture, while also parametrizing the new fixture and simultaneously requesting the overwritten fixture as parameter, yields the same value as ``request.param``. """ - testdir.makeconftest( + pytester.makeconftest( """ import pytest @@ -508,7 +527,7 @@ def foo(request): return request.param """ ) - testdir.makepyfile( + pytester.makepyfile( """ import pytest @@ -521,13 +540,13 @@ def test_spam(foo): assert foo in (20, 40) """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["*2 passed*"]) - def test_autouse_fixture_plugin(self, testdir): + def test_autouse_fixture_plugin(self, pytester: Pytester) -> None: # A fixture from a plugin has no baseid set, which screwed up # the autouse fixture handling. - testdir.makepyfile( + pytester.makepyfile( testplugin=""" import pytest @@ -536,8 +555,8 @@ def foo(request): request.function.foo = 7 """ ) - testdir.syspathinsert() - testdir.makepyfile( + pytester.syspathinsert() + pytester.makepyfile( """ pytest_plugins = 'testplugin' @@ -545,11 +564,11 @@ def test_foo(request): assert request.function.foo == 7 """ ) - result = testdir.runpytest() + result = pytester.runpytest() assert result.ret == 0 - def test_funcarg_lookup_error(self, testdir): - testdir.makeconftest( + def test_funcarg_lookup_error(self, pytester: Pytester) -> None: + pytester.makeconftest( """ import pytest @@ -566,13 +585,13 @@ def c_fixture(): pass def d_fixture(): pass """ ) - testdir.makepyfile( + pytester.makepyfile( """ def test_lookup_error(unknown): pass """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines( [ "*ERROR at setup of test_lookup_error*", @@ -586,9 +605,9 @@ def test_lookup_error(unknown): ) result.stdout.no_fnmatch_line("*INTERNAL*") - def test_fixture_excinfo_leak(self, testdir): + def test_fixture_excinfo_leak(self, pytester: Pytester) -> None: # on python2 sys.excinfo would leak into fixture executions - testdir.makepyfile( + pytester.makepyfile( """ import sys import traceback @@ -607,13 +626,13 @@ def test_leak(leak): assert sys.exc_info() == (None, None, None) """ ) - result = testdir.runpytest() + result = pytester.runpytest() assert result.ret == 0 class TestRequestBasic: - def test_request_attributes(self, testdir): - item = testdir.getitem( + def test_request_attributes(self, pytester: Pytester) -> None: + item = pytester.getitem( """ import pytest @@ -622,6 +641,7 @@ def something(request): pass def test_func(something): pass """ ) + assert isinstance(item, Function) req = fixtures.FixtureRequest(item, _ispytest=True) assert req.function == item.obj assert req.keywords == item.keywords @@ -631,8 +651,8 @@ def test_func(something): pass assert req.config == item.config assert repr(req).find(req.function.__name__) != -1 - def test_request_attributes_method(self, testdir): - (item,) = testdir.getitems( + def test_request_attributes_method(self, pytester: Pytester) -> None: + (item,) = pytester.getitems( """ import pytest class TestB(object): @@ -644,12 +664,13 @@ def test_func(self, something): pass """ ) + assert isinstance(item, Function) req = item._request assert req.cls.__name__ == "TestB" assert req.instance.__class__ == req.cls - def test_request_contains_funcarg_arg2fixturedefs(self, testdir): - modcol = testdir.getmodulecol( + def test_request_contains_funcarg_arg2fixturedefs(self, pytester: Pytester) -> None: + modcol = pytester.getmodulecol( """ import pytest @pytest.fixture @@ -660,7 +681,7 @@ def test_method(self, something): pass """ ) - (item1,) = testdir.genitems([modcol]) + (item1,) = pytester.genitems([modcol]) assert item1.name == "test_method" arg2fixturedefs = fixtures.FixtureRequest( item1, _ispytest=True @@ -672,14 +693,14 @@ def test_method(self, something): hasattr(sys, "pypy_version_info"), reason="this method of test doesn't work on pypy", ) - def test_request_garbage(self, testdir): + def test_request_garbage(self, pytester: Pytester) -> None: try: import xdist # noqa except ImportError: pass else: pytest.xfail("this test is flaky when executed with xdist") - testdir.makepyfile( + pytester.makepyfile( """ import sys import pytest @@ -705,11 +726,11 @@ def test_func(): pass """ ) - result = testdir.runpytest_subprocess() + result = pytester.runpytest_subprocess() result.stdout.fnmatch_lines(["* 1 passed in *"]) - def test_getfixturevalue_recursive(self, testdir): - testdir.makeconftest( + def test_getfixturevalue_recursive(self, pytester: Pytester) -> None: + pytester.makeconftest( """ import pytest @@ -718,7 +739,7 @@ def something(request): return 1 """ ) - testdir.makepyfile( + pytester.makepyfile( """ import pytest @@ -729,10 +750,10 @@ def test_func(something): assert something == 2 """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=1) - def test_getfixturevalue_teardown(self, testdir): + def test_getfixturevalue_teardown(self, pytester: Pytester) -> None: """ Issue #1895 @@ -743,7 +764,7 @@ def test_getfixturevalue_teardown(self, testdir): `inner` dependent on `resource` when it is used via `getfixturevalue`: `test_func` will then cause the `resource`'s finalizer to be called first because of this. """ - testdir.makepyfile( + pytester.makepyfile( """ import pytest @@ -767,11 +788,11 @@ def test_func(resource): pass """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["* 2 passed in *"]) - def test_getfixturevalue(self, testdir): - item = testdir.getitem( + def test_getfixturevalue(self, pytester: Pytester) -> None: + item = pytester.getitem( """ import pytest values = [2] @@ -783,6 +804,7 @@ def other(request): def test_func(something): pass """ ) + assert isinstance(item, Function) req = item._request with pytest.raises(pytest.FixtureLookupError): @@ -800,8 +822,8 @@ def test_func(something): pass assert len(get_public_names(item.funcargs)) == 2 assert "request" in item.funcargs - def test_request_addfinalizer(self, testdir): - item = testdir.getitem( + def test_request_addfinalizer(self, pytester: Pytester) -> None: + item = pytester.getitem( """ import pytest teardownlist = [] @@ -811,18 +833,21 @@ def something(request): def test_func(something): pass """ ) + assert isinstance(item, Function) item.session._setupstate.prepare(item) item._request._fillfixtures() # successively check finalization calls - teardownlist = item.getparent(pytest.Module).obj.teardownlist + parent = item.getparent(pytest.Module) + assert parent is not None + teardownlist = parent.obj.teardownlist ss = item.session._setupstate assert not teardownlist ss.teardown_exact(item, None) print(ss.stack) assert teardownlist == [1] - def test_request_addfinalizer_failing_setup(self, testdir): - testdir.makepyfile( + def test_request_addfinalizer_failing_setup(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest values = [1] @@ -836,11 +861,13 @@ def test_finalizer_ran(): assert not values """ ) - reprec = testdir.inline_run("-s") + reprec = pytester.inline_run("-s") reprec.assertoutcome(failed=1, passed=1) - def test_request_addfinalizer_failing_setup_module(self, testdir): - testdir.makepyfile( + def test_request_addfinalizer_failing_setup_module( + self, pytester: Pytester + ) -> None: + pytester.makepyfile( """ import pytest values = [1, 2] @@ -853,12 +880,14 @@ def test_fix(myfix): pass """ ) - reprec = testdir.inline_run("-s") + reprec = pytester.inline_run("-s") mod = reprec.getcalls("pytest_runtest_setup")[0].item.module assert not mod.values - def test_request_addfinalizer_partial_setup_failure(self, testdir): - p = testdir.makepyfile( + def test_request_addfinalizer_partial_setup_failure( + self, pytester: Pytester + ) -> None: + p = pytester.makepyfile( """ import pytest values = [] @@ -871,17 +900,19 @@ def test_second(): assert len(values) == 1 """ ) - result = testdir.runpytest(p) + result = pytester.runpytest(p) result.stdout.fnmatch_lines( ["*1 error*"] # XXX the whole module collection fails ) - def test_request_subrequest_addfinalizer_exceptions(self, testdir): + def test_request_subrequest_addfinalizer_exceptions( + self, pytester: Pytester + ) -> None: """ Ensure exceptions raised during teardown by a finalizer are suppressed until all finalizers are called, re-raising the first exception (#2440) """ - testdir.makepyfile( + pytester.makepyfile( """ import pytest values = [] @@ -905,19 +936,19 @@ def test_second(): assert values == [3, 2, 1] """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines( ["*Exception: Error in excepts fixture", "* 2 passed, 1 error in *"] ) - def test_request_getmodulepath(self, testdir): - modcol = testdir.getmodulecol("def test_somefunc(): pass") - (item,) = testdir.genitems([modcol]) + def test_request_getmodulepath(self, pytester: Pytester) -> None: + modcol = pytester.getmodulecol("def test_somefunc(): pass") + (item,) = pytester.genitems([modcol]) req = fixtures.FixtureRequest(item, _ispytest=True) assert req.fspath == modcol.fspath - def test_request_fixturenames(self, testdir): - testdir.makepyfile( + def test_request_fixturenames(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest from _pytest.pytester import get_public_names @@ -936,17 +967,17 @@ def test_function(request, farg): "tmp_path", "tmp_path_factory"]) """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=1) - def test_request_fixturenames_dynamic_fixture(self, testdir): + def test_request_fixturenames_dynamic_fixture(self, pytester: Pytester) -> None: """Regression test for #3057""" - testdir.copy_example("fixtures/test_getfixturevalue_dynamic.py") - result = testdir.runpytest() + pytester.copy_example("fixtures/test_getfixturevalue_dynamic.py") + result = pytester.runpytest() result.stdout.fnmatch_lines(["*1 passed*"]) - def test_setupdecorator_and_xunit(self, testdir): - testdir.makepyfile( + def test_setupdecorator_and_xunit(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest values = [] @@ -974,13 +1005,14 @@ def test_all(): "function", "method", "function"] """ ) - reprec = testdir.inline_run("-v") + reprec = pytester.inline_run("-v") reprec.assertoutcome(passed=3) - def test_fixtures_sub_subdir_normalize_sep(self, testdir): + def test_fixtures_sub_subdir_normalize_sep(self, pytester: Pytester) -> None: # this tests that normalization of nodeids takes place - b = testdir.mkdir("tests").mkdir("unit") - b.join("conftest.py").write( + b = pytester.path.joinpath("tests", "unit") + b.mkdir(parents=True) + b.joinpath("conftest.py").write_text( textwrap.dedent( """\ import pytest @@ -990,9 +1022,9 @@ def arg1(): """ ) ) - p = b.join("test_module.py") - p.write("def test_func(arg1): pass") - result = testdir.runpytest(p, "--fixtures") + p = b.joinpath("test_module.py") + p.write_text("def test_func(arg1): pass") + result = pytester.runpytest(p, "--fixtures") assert result.ret == 0 result.stdout.fnmatch_lines( """ @@ -1001,13 +1033,13 @@ def arg1(): """ ) - def test_show_fixtures_color_yes(self, testdir): - testdir.makepyfile("def test_this(): assert 1") - result = testdir.runpytest("--color=yes", "--fixtures") + def test_show_fixtures_color_yes(self, pytester: Pytester) -> None: + pytester.makepyfile("def test_this(): assert 1") + result = pytester.runpytest("--color=yes", "--fixtures") assert "\x1b[32mtmpdir" in result.stdout.str() - def test_newstyle_with_request(self, testdir): - testdir.makepyfile( + def test_newstyle_with_request(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @pytest.fixture() @@ -1017,11 +1049,11 @@ def test_1(arg): pass """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=1) - def test_setupcontext_no_param(self, testdir): - testdir.makepyfile( + def test_setupcontext_no_param(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @pytest.fixture(params=[1,2]) @@ -1035,13 +1067,13 @@ def test_1(arg): assert arg in (1,2) """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=2) class TestRequestMarking: - def test_applymarker(self, testdir): - item1, item2 = testdir.getitems( + def test_applymarker(self, pytester: Pytester) -> None: + item1, item2 = pytester.getitems( """ import pytest @@ -1065,8 +1097,8 @@ def test_func2(self, something): with pytest.raises(ValueError): req1.applymarker(42) # type: ignore[arg-type] - def test_accesskeywords(self, testdir): - testdir.makepyfile( + def test_accesskeywords(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @pytest.fixture() @@ -1078,11 +1110,11 @@ def test_function(keywords): assert "abc" not in keywords """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=1) - def test_accessmarker_dynamic(self, testdir): - testdir.makeconftest( + def test_accessmarker_dynamic(self, pytester: Pytester) -> None: + pytester.makeconftest( """ import pytest @pytest.fixture() @@ -1094,7 +1126,7 @@ def marking(request): request.applymarker(pytest.mark.XYZ("hello")) """ ) - testdir.makepyfile( + pytester.makepyfile( """ import pytest def test_fun1(keywords): @@ -1105,13 +1137,13 @@ def test_fun2(keywords): assert "abc" not in keywords """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=2) class TestFixtureUsages: - def test_noargfixturedec(self, testdir): - testdir.makepyfile( + def test_noargfixturedec(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @pytest.fixture @@ -1122,11 +1154,11 @@ def test_func(arg1): assert arg1 == 1 """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=1) - def test_receives_funcargs(self, testdir): - testdir.makepyfile( + def test_receives_funcargs(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @pytest.fixture() @@ -1144,11 +1176,11 @@ def test_all(arg1, arg2): assert arg2 == 2 """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=2) - def test_receives_funcargs_scope_mismatch(self, testdir): - testdir.makepyfile( + def test_receives_funcargs_scope_mismatch(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @pytest.fixture(scope="function") @@ -1163,7 +1195,7 @@ def test_add(arg2): assert arg2 == 2 """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines( [ "*ScopeMismatch*involved factories*", @@ -1173,8 +1205,10 @@ def test_add(arg2): ] ) - def test_receives_funcargs_scope_mismatch_issue660(self, testdir): - testdir.makepyfile( + def test_receives_funcargs_scope_mismatch_issue660( + self, pytester: Pytester + ) -> None: + pytester.makepyfile( """ import pytest @pytest.fixture(scope="function") @@ -1189,13 +1223,13 @@ def test_add(arg1, arg2): assert arg2 == 2 """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines( ["*ScopeMismatch*involved factories*", "* def arg2*", "*1 error*"] ) - def test_invalid_scope(self, testdir): - testdir.makepyfile( + def test_invalid_scope(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @pytest.fixture(scope="functions") @@ -1206,14 +1240,14 @@ def test_nothing(badscope): pass """ ) - result = testdir.runpytest_inprocess() + result = pytester.runpytest_inprocess() result.stdout.fnmatch_lines( "*Fixture 'badscope' from test_invalid_scope.py got an unexpected scope value 'functions'" ) @pytest.mark.parametrize("scope", ["function", "session"]) - def test_parameters_without_eq_semantics(self, scope, testdir): - testdir.makepyfile( + def test_parameters_without_eq_semantics(self, scope, pytester: Pytester) -> None: + pytester.makepyfile( """ class NoEq1: # fails on `a == b` statement def __eq__(self, _): @@ -1240,11 +1274,11 @@ def test2(no_eq): scope=scope ) ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["*4 passed*"]) - def test_funcarg_parametrized_and_used_twice(self, testdir): - testdir.makepyfile( + def test_funcarg_parametrized_and_used_twice(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest values = [] @@ -1262,11 +1296,13 @@ def test_add(arg1, arg2): assert len(values) == arg1 """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["*2 passed*"]) - def test_factory_uses_unknown_funcarg_as_dependency_error(self, testdir): - testdir.makepyfile( + def test_factory_uses_unknown_funcarg_as_dependency_error( + self, pytester: Pytester + ) -> None: + pytester.makepyfile( """ import pytest @@ -1282,7 +1318,7 @@ def test_missing(call_fail): pass """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines( """ *pytest.fixture()* @@ -1293,8 +1329,8 @@ def test_missing(call_fail): """ ) - def test_factory_setup_as_classes_fails(self, testdir): - testdir.makepyfile( + def test_factory_setup_as_classes_fails(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest class arg1(object): @@ -1304,12 +1340,12 @@ def __init__(self, request): """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() values = reprec.getfailedcollections() assert len(values) == 1 - def test_usefixtures_marker(self, testdir): - testdir.makepyfile( + def test_usefixtures_marker(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @@ -1330,17 +1366,17 @@ def test_two(self): pytest.mark.usefixtures("myfix")(TestClass) """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=2) - def test_usefixtures_ini(self, testdir): - testdir.makeini( + def test_usefixtures_ini(self, pytester: Pytester) -> None: + pytester.makeini( """ [pytest] usefixtures = myfix """ ) - testdir.makeconftest( + pytester.makeconftest( """ import pytest @@ -1350,7 +1386,7 @@ def myfix(request): """ ) - testdir.makepyfile( + pytester.makepyfile( """ class TestClass(object): def test_one(self): @@ -1359,19 +1395,19 @@ def test_two(self): assert self.hello == "world" """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=2) - def test_usefixtures_seen_in_showmarkers(self, testdir): - result = testdir.runpytest("--markers") + def test_usefixtures_seen_in_showmarkers(self, pytester: Pytester) -> None: + result = pytester.runpytest("--markers") result.stdout.fnmatch_lines( """ *usefixtures(fixturename1*mark tests*fixtures* """ ) - def test_request_instance_issue203(self, testdir): - testdir.makepyfile( + def test_request_instance_issue203(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @@ -1384,11 +1420,11 @@ def test_hello(self, setup1): assert self.arg1 == 1 """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=1) - def test_fixture_parametrized_with_iterator(self, testdir): - testdir.makepyfile( + def test_fixture_parametrized_with_iterator(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @@ -1411,14 +1447,14 @@ def test_2(arg2): values.append(arg2*10) """ ) - reprec = testdir.inline_run("-v") + reprec = pytester.inline_run("-v") reprec.assertoutcome(passed=4) values = reprec.getcalls("pytest_runtest_call")[0].item.module.values assert values == [1, 2, 10, 20] - def test_setup_functions_as_fixtures(self, testdir): + def test_setup_functions_as_fixtures(self, pytester: Pytester) -> None: """Ensure setup_* methods obey fixture scope rules (#517, #3094).""" - testdir.makepyfile( + pytester.makepyfile( """ import pytest @@ -1452,15 +1488,14 @@ def test_printer_2(self): pass """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["* 2 passed in *"]) class TestFixtureManagerParseFactories: @pytest.fixture - def testdir(self, request): - testdir = request.getfixturevalue("testdir") - testdir.makeconftest( + def pytester(self, pytester: Pytester) -> Pytester: + pytester.makeconftest( """ import pytest @@ -1477,10 +1512,10 @@ def item(request): return request._pyfuncitem """ ) - return testdir + return pytester - def test_parsefactories_evil_objects_issue214(self, testdir): - testdir.makepyfile( + def test_parsefactories_evil_objects_issue214(self, pytester: Pytester) -> None: + pytester.makepyfile( """ class A(object): def __call__(self): @@ -1492,11 +1527,11 @@ def test_hello(): pass """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=1, failed=0) - def test_parsefactories_conftest(self, testdir): - testdir.makepyfile( + def test_parsefactories_conftest(self, pytester: Pytester) -> None: + pytester.makepyfile( """ def test_hello(item, fm): for name in ("fm", "hello", "item"): @@ -1506,11 +1541,13 @@ def test_hello(item, fm): assert fac.func.__name__ == name """ ) - reprec = testdir.inline_run("-s") + reprec = pytester.inline_run("-s") reprec.assertoutcome(passed=1) - def test_parsefactories_conftest_and_module_and_class(self, testdir): - testdir.makepyfile( + def test_parsefactories_conftest_and_module_and_class( + self, pytester: Pytester + ) -> None: + pytester.makepyfile( """\ import pytest @@ -1531,15 +1568,17 @@ def test_hello(self, item, fm): assert faclist[2].func(item._request) == "class" """ ) - reprec = testdir.inline_run("-s") + reprec = pytester.inline_run("-s") reprec.assertoutcome(passed=1) - def test_parsefactories_relative_node_ids(self, testdir): + def test_parsefactories_relative_node_ids( + self, pytester: Pytester, monkeypatch: MonkeyPatch + ) -> None: # example mostly taken from: # https://mail.python.org/pipermail/pytest-dev/2014-September/002617.html - runner = testdir.mkdir("runner") - package = testdir.mkdir("package") - package.join("conftest.py").write( + runner = pytester.mkdir("runner") + package = pytester.mkdir("package") + package.joinpath("conftest.py").write_text( textwrap.dedent( """\ import pytest @@ -1549,7 +1588,7 @@ def one(): """ ) ) - package.join("test_x.py").write( + package.joinpath("test_x.py").write_text( textwrap.dedent( """\ def test_x(one): @@ -1557,9 +1596,10 @@ def test_x(one): """ ) ) - sub = package.mkdir("sub") - sub.join("__init__.py").ensure() - sub.join("conftest.py").write( + sub = package.joinpath("sub") + sub.mkdir() + sub.joinpath("__init__.py").touch() + sub.joinpath("conftest.py").write_text( textwrap.dedent( """\ import pytest @@ -1569,7 +1609,7 @@ def one(): """ ) ) - sub.join("test_y.py").write( + sub.joinpath("test_y.py").write_text( textwrap.dedent( """\ def test_x(one): @@ -1577,20 +1617,21 @@ def test_x(one): """ ) ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=2) - with runner.as_cwd(): - reprec = testdir.inline_run("..") + with monkeypatch.context() as mp: + mp.chdir(runner) + reprec = pytester.inline_run("..") reprec.assertoutcome(passed=2) - def test_package_xunit_fixture(self, testdir): - testdir.makepyfile( + def test_package_xunit_fixture(self, pytester: Pytester) -> None: + pytester.makepyfile( __init__="""\ values = [] """ ) - package = testdir.mkdir("package") - package.join("__init__.py").write( + package = pytester.mkdir("package") + package.joinpath("__init__.py").write_text( textwrap.dedent( """\ from .. import values @@ -1601,7 +1642,7 @@ def teardown_module(): """ ) ) - package.join("test_x.py").write( + package.joinpath("test_x.py").write_text( textwrap.dedent( """\ from .. import values @@ -1610,8 +1651,8 @@ def test_x(): """ ) ) - package = testdir.mkdir("package2") - package.join("__init__.py").write( + package = pytester.mkdir("package2") + package.joinpath("__init__.py").write_text( textwrap.dedent( """\ from .. import values @@ -1622,7 +1663,7 @@ def teardown_module(): """ ) ) - package.join("test_x.py").write( + package.joinpath("test_x.py").write_text( textwrap.dedent( """\ from .. import values @@ -1631,19 +1672,19 @@ def test_x(): """ ) ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=2) - def test_package_fixture_complex(self, testdir): - testdir.makepyfile( + def test_package_fixture_complex(self, pytester: Pytester) -> None: + pytester.makepyfile( __init__="""\ values = [] """ ) - testdir.syspathinsert(testdir.tmpdir.dirname) - package = testdir.mkdir("package") - package.join("__init__.py").write("") - package.join("conftest.py").write( + pytester.syspathinsert(pytester.path.name) + package = pytester.mkdir("package") + package.joinpath("__init__.py").write_text("") + package.joinpath("conftest.py").write_text( textwrap.dedent( """\ import pytest @@ -1661,7 +1702,7 @@ def two(): """ ) ) - package.join("test_x.py").write( + package.joinpath("test_x.py").write_text( textwrap.dedent( """\ from .. import values @@ -1672,19 +1713,19 @@ def test_package(one): """ ) ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=2) - def test_collect_custom_items(self, testdir): - testdir.copy_example("fixtures/custom_item") - result = testdir.runpytest("foo") + def test_collect_custom_items(self, pytester: Pytester) -> None: + pytester.copy_example("fixtures/custom_item") + result = pytester.runpytest("foo") result.stdout.fnmatch_lines(["*passed*"]) class TestAutouseDiscovery: @pytest.fixture - def testdir(self, testdir): - testdir.makeconftest( + def pytester(self, pytester: Pytester) -> Pytester: + pytester.makeconftest( """ import pytest @pytest.fixture(autouse=True) @@ -1707,10 +1748,10 @@ def item(request): return request._pyfuncitem """ ) - return testdir + return pytester - def test_parsefactories_conftest(self, testdir): - testdir.makepyfile( + def test_parsefactories_conftest(self, pytester: Pytester) -> None: + pytester.makepyfile( """ from _pytest.pytester import get_public_names def test_check_setup(item, fm): @@ -1720,11 +1761,11 @@ def test_check_setup(item, fm): assert "perfunction" in autousenames """ ) - reprec = testdir.inline_run("-s") + reprec = pytester.inline_run("-s") reprec.assertoutcome(passed=1) - def test_two_classes_separated_autouse(self, testdir): - testdir.makepyfile( + def test_two_classes_separated_autouse(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest class TestA(object): @@ -1743,11 +1784,11 @@ def test_setup2(self): assert self.values == [1] """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=2) - def test_setup_at_classlevel(self, testdir): - testdir.makepyfile( + def test_setup_at_classlevel(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest class TestClass(object): @@ -1760,12 +1801,12 @@ def test_method2(self): assert self.funcname == "test_method2" """ ) - reprec = testdir.inline_run("-s") + reprec = pytester.inline_run("-s") reprec.assertoutcome(passed=2) @pytest.mark.xfail(reason="'enabled' feature not implemented") - def test_setup_enabled_functionnode(self, testdir): - testdir.makepyfile( + def test_setup_enabled_functionnode(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @@ -1788,13 +1829,13 @@ def test_func2(request): assert "db" in request.fixturenames """ ) - reprec = testdir.inline_run("-s") + reprec = pytester.inline_run("-s") reprec.assertoutcome(passed=2) - def test_callables_nocode(self, testdir): + def test_callables_nocode(self, pytester: Pytester) -> None: """An imported mock.call would break setup/factory discovery due to it being callable and __code__ not being a code object.""" - testdir.makepyfile( + pytester.makepyfile( """ class _call(tuple): def __call__(self, *k, **kw): @@ -1805,13 +1846,13 @@ def __getattr__(self, k): call = _call() """ ) - reprec = testdir.inline_run("-s") + reprec = pytester.inline_run("-s") reprec.assertoutcome(failed=0, passed=0) - def test_autouse_in_conftests(self, testdir): - a = testdir.mkdir("a") - b = testdir.mkdir("a1") - conftest = testdir.makeconftest( + def test_autouse_in_conftests(self, pytester: Pytester) -> None: + a = pytester.mkdir("a") + b = pytester.mkdir("a1") + conftest = pytester.makeconftest( """ import pytest @pytest.fixture(autouse=True) @@ -1819,18 +1860,18 @@ def hello(): xxx """ ) - conftest.move(a.join(conftest.basename)) - a.join("test_something.py").write("def test_func(): pass") - b.join("test_otherthing.py").write("def test_func(): pass") - result = testdir.runpytest() + conftest.rename(a.joinpath(conftest.name)) + a.joinpath("test_something.py").write_text("def test_func(): pass") + b.joinpath("test_otherthing.py").write_text("def test_func(): pass") + result = pytester.runpytest() result.stdout.fnmatch_lines( """ *1 passed*1 error* """ ) - def test_autouse_in_module_and_two_classes(self, testdir): - testdir.makepyfile( + def test_autouse_in_module_and_two_classes(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest values = [] @@ -1851,14 +1892,14 @@ def test_world(self): assert values == ["module", "module", "A", "module"], values """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=3) class TestAutouseManagement: - def test_autouse_conftest_mid_directory(self, testdir): - pkgdir = testdir.mkpydir("xyz123") - pkgdir.join("conftest.py").write( + def test_autouse_conftest_mid_directory(self, pytester: Pytester) -> None: + pkgdir = pytester.mkpydir("xyz123") + pkgdir.joinpath("conftest.py").write_text( textwrap.dedent( """\ import pytest @@ -1869,8 +1910,11 @@ def app(): """ ) ) - t = pkgdir.ensure("tests", "test_app.py") - t.write( + sub = pkgdir.joinpath("tests") + sub.mkdir() + t = sub.joinpath("test_app.py") + t.touch() + t.write_text( textwrap.dedent( """\ import sys @@ -1879,11 +1923,11 @@ def test_app(): """ ) ) - reprec = testdir.inline_run("-s") + reprec = pytester.inline_run("-s") reprec.assertoutcome(passed=1) - def test_funcarg_and_setup(self, testdir): - testdir.makepyfile( + def test_funcarg_and_setup(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest values = [] @@ -1906,11 +1950,11 @@ def test_hello2(arg): assert arg == 0 """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=2) - def test_uses_parametrized_resource(self, testdir): - testdir.makepyfile( + def test_uses_parametrized_resource(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest values = [] @@ -1932,11 +1976,11 @@ def test_hello(): """ ) - reprec = testdir.inline_run("-s") + reprec = pytester.inline_run("-s") reprec.assertoutcome(passed=2) - def test_session_parametrized_function(self, testdir): - testdir.makepyfile( + def test_session_parametrized_function(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @@ -1959,7 +2003,7 @@ def test_result(arg): assert values[:arg] == [1,2][:arg] """ ) - reprec = testdir.inline_run("-v", "-s") + reprec = pytester.inline_run("-v", "-s") reprec.assertoutcome(passed=4) def test_class_function_parametrization_finalization( @@ -2007,8 +2051,8 @@ def test_2(self): ].values assert values == ["fin_a1", "fin_a2", "fin_b1", "fin_b2"] * 2 - def test_scope_ordering(self, testdir): - testdir.makepyfile( + def test_scope_ordering(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest values = [] @@ -2027,11 +2071,11 @@ def test_method(self): assert values == [1,3,2] """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=1) - def test_parametrization_setup_teardown_ordering(self, testdir): - testdir.makepyfile( + def test_parametrization_setup_teardown_ordering(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest values = [] @@ -2056,11 +2100,11 @@ def test_finish(): "setup-2", "step1-2", "step2-2", "teardown-2",] """ ) - reprec = testdir.inline_run("-s") + reprec = pytester.inline_run("-s") reprec.assertoutcome(passed=5) - def test_ordering_autouse_before_explicit(self, testdir): - testdir.makepyfile( + def test_ordering_autouse_before_explicit(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @@ -2075,14 +2119,16 @@ def test_hello(arg1): assert values == [1,2] """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=1) @pytest.mark.parametrize("param1", ["", "params=[1]"], ids=["p00", "p01"]) @pytest.mark.parametrize("param2", ["", "params=[1]"], ids=["p10", "p11"]) - def test_ordering_dependencies_torndown_first(self, testdir, param1, param2): + def test_ordering_dependencies_torndown_first( + self, pytester: Pytester, param1, param2 + ) -> None: """#226""" - testdir.makepyfile( + pytester.makepyfile( """ import pytest values = [] @@ -2102,13 +2148,13 @@ def test_check(): """ % locals() ) - reprec = testdir.inline_run("-s") + reprec = pytester.inline_run("-s") reprec.assertoutcome(passed=2) class TestFixtureMarker: - def test_parametrize(self, testdir): - testdir.makepyfile( + def test_parametrize(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @pytest.fixture(params=["a", "b", "c"]) @@ -2121,11 +2167,11 @@ def test_result(): assert values == list("abc") """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=4) - def test_multiple_parametrization_issue_736(self, testdir): - testdir.makepyfile( + def test_multiple_parametrization_issue_736(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @@ -2139,19 +2185,21 @@ def test_issue(foo, foobar): assert foobar in [4,5,6] """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=9) @pytest.mark.parametrize( "param_args", ["'fixt, val'", "'fixt,val'", "['fixt', 'val']", "('fixt', 'val')"], ) - def test_override_parametrized_fixture_issue_979(self, testdir, param_args): + def test_override_parametrized_fixture_issue_979( + self, pytester: Pytester, param_args + ) -> None: """Make sure a parametrized argument can override a parametrized fixture. This was a regression introduced in the fix for #736. """ - testdir.makepyfile( + pytester.makepyfile( """ import pytest @@ -2165,11 +2213,11 @@ def test_foo(fixt, val): """ % param_args ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=2) - def test_scope_session(self, testdir): - testdir.makepyfile( + def test_scope_session(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest values = [] @@ -2189,11 +2237,11 @@ def test3(self, arg): assert len(values) == 1 """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=3) - def test_scope_session_exc(self, testdir): - testdir.makepyfile( + def test_scope_session_exc(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest values = [] @@ -2210,11 +2258,11 @@ def test_last(): assert values == [1] """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(skipped=2, passed=1) - def test_scope_session_exc_two_fix(self, testdir): - testdir.makepyfile( + def test_scope_session_exc_two_fix(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest values = [] @@ -2236,11 +2284,11 @@ def test_last(): assert m == [] """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(skipped=2, passed=1) - def test_scope_exc(self, testdir): - testdir.makepyfile( + def test_scope_exc(self, pytester: Pytester) -> None: + pytester.makepyfile( test_foo=""" def test_foo(fix): pass @@ -2265,11 +2313,11 @@ def test_last(req_list): assert req_list == [1] """, ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(skipped=2, passed=1) - def test_scope_module_uses_session(self, testdir): - testdir.makepyfile( + def test_scope_module_uses_session(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest values = [] @@ -2289,11 +2337,11 @@ def test3(self, arg): assert len(values) == 1 """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=3) - def test_scope_module_and_finalizer(self, testdir): - testdir.makeconftest( + def test_scope_module_and_finalizer(self, pytester: Pytester) -> None: + pytester.makeconftest( """ import pytest finalized_list = [] @@ -2311,7 +2359,7 @@ def finalized(request): return len(finalized_list) """ ) - testdir.makepyfile( + pytester.makepyfile( test_mod1=""" def test_1(arg, created, finalized): assert created == 1 @@ -2329,11 +2377,11 @@ def test_4(arg, created, finalized): assert finalized == 2 """, ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=4) - def test_scope_mismatch_various(self, testdir): - testdir.makeconftest( + def test_scope_mismatch_various(self, pytester: Pytester) -> None: + pytester.makeconftest( """ import pytest finalized = [] @@ -2343,7 +2391,7 @@ def arg(request): pass """ ) - testdir.makepyfile( + pytester.makepyfile( test_mod1=""" import pytest @pytest.fixture(scope="session") @@ -2353,14 +2401,14 @@ def test_1(arg): pass """ ) - result = testdir.runpytest() + result = pytester.runpytest() assert result.ret != 0 result.stdout.fnmatch_lines( ["*ScopeMismatch*You tried*function*session*request*"] ) - def test_dynamic_scope(self, testdir): - testdir.makeconftest( + def test_dynamic_scope(self, pytester: Pytester) -> None: + pytester.makeconftest( """ import pytest @@ -2383,7 +2431,7 @@ def dynamic_fixture(calls=[]): """ ) - testdir.makepyfile( + pytester.makepyfile( """ def test_first(dynamic_fixture): assert dynamic_fixture == 1 @@ -2395,14 +2443,14 @@ def test_second(dynamic_fixture): """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=2) - reprec = testdir.inline_run("--extend-scope") + reprec = pytester.inline_run("--extend-scope") reprec.assertoutcome(passed=1, failed=1) - def test_dynamic_scope_bad_return(self, testdir): - testdir.makepyfile( + def test_dynamic_scope_bad_return(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @@ -2415,14 +2463,14 @@ def fixture(): """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines( "Fixture 'fixture' from test_dynamic_scope_bad_return.py " "got an unexpected scope value 'wrong-scope'" ) - def test_register_only_with_mark(self, testdir): - testdir.makeconftest( + def test_register_only_with_mark(self, pytester: Pytester) -> None: + pytester.makeconftest( """ import pytest @pytest.fixture() @@ -2430,7 +2478,7 @@ def arg(): return 1 """ ) - testdir.makepyfile( + pytester.makepyfile( test_mod1=""" import pytest @pytest.fixture() @@ -2440,11 +2488,11 @@ def test_1(arg): assert arg == 2 """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=1) - def test_parametrize_and_scope(self, testdir): - testdir.makepyfile( + def test_parametrize_and_scope(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @pytest.fixture(scope="module", params=["a", "b", "c"]) @@ -2455,7 +2503,7 @@ def test_param(arg): values.append(arg) """ ) - reprec = testdir.inline_run("-v") + reprec = pytester.inline_run("-v") reprec.assertoutcome(passed=3) values = reprec.getcalls("pytest_runtest_call")[0].item.module.values assert len(values) == 3 @@ -2463,8 +2511,8 @@ def test_param(arg): assert "b" in values assert "c" in values - def test_scope_mismatch(self, testdir): - testdir.makeconftest( + def test_scope_mismatch(self, pytester: Pytester) -> None: + pytester.makeconftest( """ import pytest @pytest.fixture(scope="function") @@ -2472,7 +2520,7 @@ def arg(request): pass """ ) - testdir.makepyfile( + pytester.makepyfile( """ import pytest @pytest.fixture(scope="session") @@ -2482,11 +2530,11 @@ def test_mismatch(arg): pass """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["*ScopeMismatch*", "*1 error*"]) - def test_parametrize_separated_order(self, testdir): - testdir.makepyfile( + def test_parametrize_separated_order(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @@ -2501,19 +2549,19 @@ def test_2(arg): values.append(arg) """ ) - reprec = testdir.inline_run("-v") + reprec = pytester.inline_run("-v") reprec.assertoutcome(passed=4) values = reprec.getcalls("pytest_runtest_call")[0].item.module.values assert values == [1, 1, 2, 2] - def test_module_parametrized_ordering(self, testdir): - testdir.makeini( + def test_module_parametrized_ordering(self, pytester: Pytester) -> None: + pytester.makeini( """ [pytest] console_output_style=classic """ ) - testdir.makeconftest( + pytester.makeconftest( """ import pytest @@ -2525,7 +2573,7 @@ def marg(): pass """ ) - testdir.makepyfile( + pytester.makepyfile( test_mod1=""" def test_func(sarg): pass @@ -2543,7 +2591,7 @@ def test_func4(marg): pass """, ) - result = testdir.runpytest("-v") + result = pytester.runpytest("-v") result.stdout.fnmatch_lines( """ test_mod1.py::test_func[s1] PASSED @@ -2565,14 +2613,14 @@ def test_func4(marg): """ ) - def test_dynamic_parametrized_ordering(self, testdir): - testdir.makeini( + def test_dynamic_parametrized_ordering(self, pytester: Pytester) -> None: + pytester.makeini( """ [pytest] console_output_style=classic """ ) - testdir.makeconftest( + pytester.makeconftest( """ import pytest @@ -2592,7 +2640,7 @@ def reprovision(request, flavor, encap): pass """ ) - testdir.makepyfile( + pytester.makepyfile( """ def test(reprovision): pass @@ -2600,7 +2648,7 @@ def test2(reprovision): pass """ ) - result = testdir.runpytest("-v") + result = pytester.runpytest("-v") result.stdout.fnmatch_lines( """ test_dynamic_parametrized_ordering.py::test[flavor1-vxlan] PASSED @@ -2614,14 +2662,14 @@ def test2(reprovision): """ ) - def test_class_ordering(self, testdir): - testdir.makeini( + def test_class_ordering(self, pytester: Pytester) -> None: + pytester.makeini( """ [pytest] console_output_style=classic """ ) - testdir.makeconftest( + pytester.makeconftest( """ import pytest @@ -2642,7 +2690,7 @@ def fin(): request.addfinalizer(fin) """ ) - testdir.makepyfile( + pytester.makepyfile( """ import pytest @@ -2656,7 +2704,7 @@ def test_3(self): pass """ ) - result = testdir.runpytest("-vs") + result = pytester.runpytest("-vs") result.stdout.re_match_lines( r""" test_class_ordering.py::TestClass2::test_1\[a-1\] PASSED @@ -2674,8 +2722,10 @@ def test_3(self): """ ) - def test_parametrize_separated_order_higher_scope_first(self, testdir): - testdir.makepyfile( + def test_parametrize_separated_order_higher_scope_first( + self, pytester: Pytester + ) -> None: + pytester.makepyfile( """ import pytest @@ -2704,7 +2754,7 @@ def test_4(modarg, arg): values.append("test4") """ ) - reprec = testdir.inline_run("-v") + reprec = pytester.inline_run("-v") reprec.assertoutcome(passed=12) values = reprec.getcalls("pytest_runtest_call")[0].item.module.values expected = [ @@ -2750,8 +2800,8 @@ def test_4(modarg, arg): pprint.pprint(list(zip(values, expected))) assert values == expected - def test_parametrized_fixture_teardown_order(self, testdir): - testdir.makepyfile( + def test_parametrized_fixture_teardown_order(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @pytest.fixture(params=[1,2], scope="class") @@ -2783,7 +2833,7 @@ def test_finish(): assert not values """ ) - result = testdir.runpytest("-v") + result = pytester.runpytest("-v") result.stdout.fnmatch_lines( """ *3 passed* @@ -2791,8 +2841,8 @@ def test_finish(): ) result.stdout.no_fnmatch_line("*error*") - def test_fixture_finalizer(self, testdir): - testdir.makeconftest( + def test_fixture_finalizer(self, pytester: Pytester) -> None: + pytester.makeconftest( """ import pytest import sys @@ -2801,13 +2851,13 @@ def test_fixture_finalizer(self, testdir): def browser(request): def finalize(): - sys.stdout.write('Finalized') + sys.stdout.write_text('Finalized') request.addfinalizer(finalize) return {} """ ) - b = testdir.mkdir("subdir") - b.join("test_overridden_fixture_finalizer.py").write( + b = pytester.mkdir("subdir") + b.joinpath("test_overridden_fixture_finalizer.py").write_text( textwrap.dedent( """\ import pytest @@ -2821,12 +2871,12 @@ def test_browser(browser): """ ) ) - reprec = testdir.runpytest("-s") + reprec = pytester.runpytest("-s") for test in ["test_browser"]: reprec.stdout.fnmatch_lines(["*Finalized*"]) - def test_class_scope_with_normal_tests(self, testdir): - testpath = testdir.makepyfile( + def test_class_scope_with_normal_tests(self, pytester: Pytester) -> None: + testpath = pytester.makepyfile( """ import pytest @@ -2849,12 +2899,12 @@ class Test2(object): def test_c(self, a): assert a == 3""" ) - reprec = testdir.inline_run(testpath) + reprec = pytester.inline_run(testpath) for test in ["test_a", "test_b", "test_c"]: assert reprec.matchreport(test).passed - def test_request_is_clean(self, testdir): - testdir.makepyfile( + def test_request_is_clean(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest values = [] @@ -2865,12 +2915,12 @@ def test_fix(fix): pass """ ) - reprec = testdir.inline_run("-s") + reprec = pytester.inline_run("-s") values = reprec.getcalls("pytest_runtest_call")[0].item.module.values assert values == [1, 2] - def test_parametrize_separated_lifecycle(self, testdir): - testdir.makepyfile( + def test_parametrize_separated_lifecycle(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @@ -2886,7 +2936,7 @@ def test_2(arg): values.append(arg) """ ) - reprec = testdir.inline_run("-vs") + reprec = pytester.inline_run("-vs") reprec.assertoutcome(passed=4) values = reprec.getcalls("pytest_runtest_call")[0].item.module.values import pprint @@ -2898,8 +2948,10 @@ def test_2(arg): assert values[3] == values[4] == 2 assert values[5] == "fin2" - def test_parametrize_function_scoped_finalizers_called(self, testdir): - testdir.makepyfile( + def test_parametrize_function_scoped_finalizers_called( + self, pytester: Pytester + ) -> None: + pytester.makepyfile( """ import pytest @@ -2919,13 +2971,15 @@ def test_3(): assert values == [1, "fin1", 2, "fin2", 1, "fin1", 2, "fin2"] """ ) - reprec = testdir.inline_run("-v") + reprec = pytester.inline_run("-v") reprec.assertoutcome(passed=5) @pytest.mark.parametrize("scope", ["session", "function", "module"]) - def test_finalizer_order_on_parametrization(self, scope, testdir): + def test_finalizer_order_on_parametrization( + self, scope, pytester: Pytester + ) -> None: """#246""" - testdir.makepyfile( + pytester.makepyfile( """ import pytest values = [] @@ -2956,12 +3010,12 @@ def test_other(): """ % {"scope": scope} ) - reprec = testdir.inline_run("-lvs") + reprec = pytester.inline_run("-lvs") reprec.assertoutcome(passed=3) - def test_class_scope_parametrization_ordering(self, testdir): + def test_class_scope_parametrization_ordering(self, pytester: Pytester) -> None: """#396""" - testdir.makepyfile( + pytester.makepyfile( """ import pytest values = [] @@ -2982,7 +3036,7 @@ def test_population(self, human): values.append("test_population") """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=6) values = reprec.getcalls("pytest_runtest_call")[0].item.module.values assert values == [ @@ -2998,8 +3052,8 @@ def test_population(self, human): "fin Doe", ] - def test_parametrize_setup_function(self, testdir): - testdir.makepyfile( + def test_parametrize_setup_function(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @@ -3028,11 +3082,13 @@ def test_3(): """ ) - reprec = testdir.inline_run("-v") + reprec = pytester.inline_run("-v") reprec.assertoutcome(passed=6) - def test_fixture_marked_function_not_collected_as_test(self, testdir): - testdir.makepyfile( + def test_fixture_marked_function_not_collected_as_test( + self, pytester: Pytester + ) -> None: + pytester.makepyfile( """ import pytest @pytest.fixture @@ -3043,11 +3099,11 @@ def test_something(test_app): assert test_app == 1 """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=1) - def test_params_and_ids(self, testdir): - testdir.makepyfile( + def test_params_and_ids(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @@ -3060,11 +3116,11 @@ def test_foo(fix): assert 1 """ ) - res = testdir.runpytest("-v") + res = pytester.runpytest("-v") res.stdout.fnmatch_lines(["*test_foo*alpha*", "*test_foo*beta*"]) - def test_params_and_ids_yieldfixture(self, testdir): - testdir.makepyfile( + def test_params_and_ids_yieldfixture(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @@ -3076,12 +3132,14 @@ def test_foo(fix): assert 1 """ ) - res = testdir.runpytest("-v") + res = pytester.runpytest("-v") res.stdout.fnmatch_lines(["*test_foo*alpha*", "*test_foo*beta*"]) - def test_deterministic_fixture_collection(self, testdir, monkeypatch): + def test_deterministic_fixture_collection( + self, pytester: Pytester, monkeypatch + ) -> None: """#920""" - testdir.makepyfile( + pytester.makepyfile( """ import pytest @@ -3106,21 +3164,21 @@ def test_foo(B): """ ) monkeypatch.setenv("PYTHONHASHSEED", "1") - out1 = testdir.runpytest_subprocess("-v") + out1 = pytester.runpytest_subprocess("-v") monkeypatch.setenv("PYTHONHASHSEED", "2") - out2 = testdir.runpytest_subprocess("-v") - out1 = [ + out2 = pytester.runpytest_subprocess("-v") + output1 = [ line for line in out1.outlines if line.startswith("test_deterministic_fixture_collection.py::test_foo") ] - out2 = [ + output2 = [ line for line in out2.outlines if line.startswith("test_deterministic_fixture_collection.py::test_foo") ] - assert len(out1) == 12 - assert out1 == out2 + assert len(output1) == 12 + assert output1 == output2 class TestRequestScopeAccess: @@ -3134,8 +3192,8 @@ class TestRequestScopeAccess: ], ) - def test_setup(self, testdir, scope, ok, error): - testdir.makepyfile( + def test_setup(self, pytester: Pytester, scope, ok, error) -> None: + pytester.makepyfile( """ import pytest @pytest.fixture(scope=%r, autouse=True) @@ -3152,11 +3210,11 @@ def test_func(): """ % (scope, ok.split(), error.split()) ) - reprec = testdir.inline_run("-l") + reprec = pytester.inline_run("-l") reprec.assertoutcome(passed=1) - def test_funcarg(self, testdir, scope, ok, error): - testdir.makepyfile( + def test_funcarg(self, pytester: Pytester, scope, ok, error) -> None: + pytester.makepyfile( """ import pytest @pytest.fixture(scope=%r) @@ -3173,13 +3231,13 @@ def test_func(arg): """ % (scope, ok.split(), error.split()) ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=1) class TestErrors: - def test_subfactory_missing_funcarg(self, testdir): - testdir.makepyfile( + def test_subfactory_missing_funcarg(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @pytest.fixture() @@ -3189,14 +3247,14 @@ def test_something(gen): pass """ ) - result = testdir.runpytest() + result = pytester.runpytest() assert result.ret != 0 result.stdout.fnmatch_lines( ["*def gen(qwe123):*", "*fixture*qwe123*not found*", "*1 error*"] ) - def test_issue498_fixture_finalizer_failing(self, testdir): - testdir.makepyfile( + def test_issue498_fixture_finalizer_failing(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @pytest.fixture @@ -3215,7 +3273,7 @@ def test_3(): assert values[0] != values[1] """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines( """ *ERROR*teardown*test_1* @@ -3226,8 +3284,8 @@ def test_3(): """ ) - def test_setupfunc_missing_funcarg(self, testdir): - testdir.makepyfile( + def test_setupfunc_missing_funcarg(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @pytest.fixture(autouse=True) @@ -3237,7 +3295,7 @@ def test_something(): pass """ ) - result = testdir.runpytest() + result = pytester.runpytest() assert result.ret != 0 result.stdout.fnmatch_lines( ["*def gen(qwe123):*", "*fixture*qwe123*not found*", "*1 error*"] @@ -3245,12 +3303,12 @@ def test_something(): class TestShowFixtures: - def test_funcarg_compat(self, testdir): - config = testdir.parseconfigure("--funcargs") + def test_funcarg_compat(self, pytester: Pytester) -> None: + config = pytester.parseconfigure("--funcargs") assert config.option.showfixtures - def test_show_fixtures(self, testdir): - result = testdir.runpytest("--fixtures") + def test_show_fixtures(self, pytester: Pytester) -> None: + result = pytester.runpytest("--fixtures") result.stdout.fnmatch_lines( [ "tmpdir_factory [[]session scope[]]", @@ -3260,8 +3318,8 @@ def test_show_fixtures(self, testdir): ] ) - def test_show_fixtures_verbose(self, testdir): - result = testdir.runpytest("--fixtures", "-v") + def test_show_fixtures_verbose(self, pytester: Pytester) -> None: + result = pytester.runpytest("--fixtures", "-v") result.stdout.fnmatch_lines( [ "tmpdir_factory [[]session scope[]] -- *tmpdir.py*", @@ -3271,8 +3329,8 @@ def test_show_fixtures_verbose(self, testdir): ] ) - def test_show_fixtures_testmodule(self, testdir): - p = testdir.makepyfile( + def test_show_fixtures_testmodule(self, pytester: Pytester) -> None: + p = pytester.makepyfile( ''' import pytest @pytest.fixture @@ -3283,7 +3341,7 @@ def arg1(): """ hello world """ ''' ) - result = testdir.runpytest("--fixtures", p) + result = pytester.runpytest("--fixtures", p) result.stdout.fnmatch_lines( """ *tmpdir @@ -3295,8 +3353,8 @@ def arg1(): result.stdout.no_fnmatch_line("*arg0*") @pytest.mark.parametrize("testmod", [True, False]) - def test_show_fixtures_conftest(self, testdir, testmod): - testdir.makeconftest( + def test_show_fixtures_conftest(self, pytester: Pytester, testmod) -> None: + pytester.makeconftest( ''' import pytest @pytest.fixture @@ -3305,13 +3363,13 @@ def arg1(): ''' ) if testmod: - testdir.makepyfile( + pytester.makepyfile( """ def test_hello(): pass """ ) - result = testdir.runpytest("--fixtures") + result = pytester.runpytest("--fixtures") result.stdout.fnmatch_lines( """ *tmpdir* @@ -3321,8 +3379,8 @@ def test_hello(): """ ) - def test_show_fixtures_trimmed_doc(self, testdir): - p = testdir.makepyfile( + def test_show_fixtures_trimmed_doc(self, pytester: Pytester) -> None: + p = pytester.makepyfile( textwrap.dedent( '''\ import pytest @@ -3343,7 +3401,7 @@ def arg2(): ''' ) ) - result = testdir.runpytest("--fixtures", p) + result = pytester.runpytest("--fixtures", p) result.stdout.fnmatch_lines( textwrap.dedent( """\ @@ -3358,8 +3416,8 @@ def arg2(): ) ) - def test_show_fixtures_indented_doc(self, testdir): - p = testdir.makepyfile( + def test_show_fixtures_indented_doc(self, pytester: Pytester) -> None: + p = pytester.makepyfile( textwrap.dedent( '''\ import pytest @@ -3372,7 +3430,7 @@ def fixture1(): ''' ) ) - result = testdir.runpytest("--fixtures", p) + result = pytester.runpytest("--fixtures", p) result.stdout.fnmatch_lines( textwrap.dedent( """\ @@ -3384,8 +3442,10 @@ def fixture1(): ) ) - def test_show_fixtures_indented_doc_first_line_unindented(self, testdir): - p = testdir.makepyfile( + def test_show_fixtures_indented_doc_first_line_unindented( + self, pytester: Pytester + ) -> None: + p = pytester.makepyfile( textwrap.dedent( '''\ import pytest @@ -3398,7 +3458,7 @@ def fixture1(): ''' ) ) - result = testdir.runpytest("--fixtures", p) + result = pytester.runpytest("--fixtures", p) result.stdout.fnmatch_lines( textwrap.dedent( """\ @@ -3411,8 +3471,8 @@ def fixture1(): ) ) - def test_show_fixtures_indented_in_class(self, testdir): - p = testdir.makepyfile( + def test_show_fixtures_indented_in_class(self, pytester: Pytester) -> None: + p = pytester.makepyfile( textwrap.dedent( '''\ import pytest @@ -3426,7 +3486,7 @@ def fixture1(self): ''' ) ) - result = testdir.runpytest("--fixtures", p) + result = pytester.runpytest("--fixtures", p) result.stdout.fnmatch_lines( textwrap.dedent( """\ @@ -3439,9 +3499,9 @@ def fixture1(self): ) ) - def test_show_fixtures_different_files(self, testdir): + def test_show_fixtures_different_files(self, pytester: Pytester) -> None: """`--fixtures` only shows fixtures from first file (#833).""" - testdir.makepyfile( + pytester.makepyfile( test_a=''' import pytest @@ -3454,7 +3514,7 @@ def test_a(fix_a): pass ''' ) - testdir.makepyfile( + pytester.makepyfile( test_b=''' import pytest @@ -3467,7 +3527,7 @@ def test_b(fix_b): pass ''' ) - result = testdir.runpytest("--fixtures") + result = pytester.runpytest("--fixtures") result.stdout.fnmatch_lines( """ * fixtures defined from test_a * @@ -3480,8 +3540,8 @@ def test_b(fix_b): """ ) - def test_show_fixtures_with_same_name(self, testdir): - testdir.makeconftest( + def test_show_fixtures_with_same_name(self, pytester: Pytester) -> None: + pytester.makeconftest( ''' import pytest @pytest.fixture @@ -3490,13 +3550,13 @@ def arg1(): return "Hello World" ''' ) - testdir.makepyfile( + pytester.makepyfile( """ def test_foo(arg1): assert arg1 == "Hello World" """ ) - testdir.makepyfile( + pytester.makepyfile( ''' import pytest @pytest.fixture @@ -3507,7 +3567,7 @@ def test_bar(arg1): assert arg1 == "Hi" ''' ) - result = testdir.runpytest("--fixtures") + result = pytester.runpytest("--fixtures") result.stdout.fnmatch_lines( """ * fixtures defined from conftest * @@ -3531,8 +3591,8 @@ def foo(): class TestContextManagerFixtureFuncs: - def test_simple(self, testdir: Testdir) -> None: - testdir.makepyfile( + def test_simple(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @pytest.fixture @@ -3547,7 +3607,7 @@ def test_2(arg1): assert 0 """ ) - result = testdir.runpytest("-s") + result = pytester.runpytest("-s") result.stdout.fnmatch_lines( """ *setup* @@ -3559,8 +3619,8 @@ def test_2(arg1): """ ) - def test_scoped(self, testdir: Testdir) -> None: - testdir.makepyfile( + def test_scoped(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @pytest.fixture(scope="module") @@ -3574,7 +3634,7 @@ def test_2(arg1): print("test2", arg1) """ ) - result = testdir.runpytest("-s") + result = pytester.runpytest("-s") result.stdout.fnmatch_lines( """ *setup* @@ -3584,8 +3644,8 @@ def test_2(arg1): """ ) - def test_setup_exception(self, testdir: Testdir) -> None: - testdir.makepyfile( + def test_setup_exception(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @pytest.fixture(scope="module") @@ -3596,7 +3656,7 @@ def test_1(arg1): pass """ ) - result = testdir.runpytest("-s") + result = pytester.runpytest("-s") result.stdout.fnmatch_lines( """ *pytest.fail*setup* @@ -3604,8 +3664,8 @@ def test_1(arg1): """ ) - def test_teardown_exception(self, testdir: Testdir) -> None: - testdir.makepyfile( + def test_teardown_exception(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @pytest.fixture(scope="module") @@ -3616,7 +3676,7 @@ def test_1(arg1): pass """ ) - result = testdir.runpytest("-s") + result = pytester.runpytest("-s") result.stdout.fnmatch_lines( """ *pytest.fail*teardown* @@ -3624,8 +3684,8 @@ def test_1(arg1): """ ) - def test_yields_more_than_one(self, testdir: Testdir) -> None: - testdir.makepyfile( + def test_yields_more_than_one(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @pytest.fixture(scope="module") @@ -3636,7 +3696,7 @@ def test_1(arg1): pass """ ) - result = testdir.runpytest("-s") + result = pytester.runpytest("-s") result.stdout.fnmatch_lines( """ *fixture function* @@ -3644,8 +3704,8 @@ def test_1(arg1): """ ) - def test_custom_name(self, testdir: Testdir) -> None: - testdir.makepyfile( + def test_custom_name(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @pytest.fixture(name='meow') @@ -3655,13 +3715,13 @@ def test_1(meow): print(meow) """ ) - result = testdir.runpytest("-s") + result = pytester.runpytest("-s") result.stdout.fnmatch_lines(["*mew*"]) class TestParameterizedSubRequest: - def test_call_from_fixture(self, testdir): - testdir.makepyfile( + def test_call_from_fixture(self, pytester: Pytester) -> None: + pytester.makepyfile( test_call_from_fixture=""" import pytest @@ -3677,7 +3737,7 @@ def test_foo(request, get_named_fixture): pass """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines( [ "The requested fixture has no parameter defined for test:", @@ -3690,8 +3750,8 @@ def test_foo(request, get_named_fixture): ] ) - def test_call_from_test(self, testdir): - testdir.makepyfile( + def test_call_from_test(self, pytester: Pytester) -> None: + pytester.makepyfile( test_call_from_test=""" import pytest @@ -3703,7 +3763,7 @@ def test_foo(request): request.getfixturevalue('fix_with_param') """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines( [ "The requested fixture has no parameter defined for test:", @@ -3716,8 +3776,8 @@ def test_foo(request): ] ) - def test_external_fixture(self, testdir): - testdir.makeconftest( + def test_external_fixture(self, pytester: Pytester) -> None: + pytester.makeconftest( """ import pytest @@ -3727,13 +3787,13 @@ def fix_with_param(request): """ ) - testdir.makepyfile( + pytester.makepyfile( test_external_fixture=""" def test_foo(request): request.getfixturevalue('fix_with_param') """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines( [ "The requested fixture has no parameter defined for test:", @@ -3747,11 +3807,11 @@ def test_foo(request): ] ) - def test_non_relative_path(self, testdir): - tests_dir = testdir.mkdir("tests") - fixdir = testdir.mkdir("fixtures") - fixfile = fixdir.join("fix.py") - fixfile.write( + def test_non_relative_path(self, pytester: Pytester) -> None: + tests_dir = pytester.mkdir("tests") + fixdir = pytester.mkdir("fixtures") + fixfile = fixdir.joinpath("fix.py") + fixfile.write_text( textwrap.dedent( """\ import pytest @@ -3763,8 +3823,8 @@ def fix_with_param(request): ) ) - testfile = tests_dir.join("test_foos.py") - testfile.write( + testfile = tests_dir.joinpath("test_foos.py") + testfile.write_text( textwrap.dedent( """\ from fix import fix_with_param @@ -3775,9 +3835,9 @@ def test_foo(request): ) ) - tests_dir.chdir() - testdir.syspathinsert(fixdir) - result = testdir.runpytest() + os.chdir(tests_dir) + pytester.syspathinsert(fixdir) + result = pytester.runpytest() result.stdout.fnmatch_lines( [ "The requested fixture has no parameter defined for test:", @@ -3792,9 +3852,9 @@ def test_foo(request): ) # With non-overlapping rootdir, passing tests_dir. - rootdir = testdir.mkdir("rootdir") - rootdir.chdir() - result = testdir.runpytest("--rootdir", rootdir, tests_dir) + rootdir = pytester.mkdir("rootdir") + os.chdir(rootdir) + result = pytester.runpytest("--rootdir", rootdir, tests_dir) result.stdout.fnmatch_lines( [ "The requested fixture has no parameter defined for test:", @@ -3809,8 +3869,8 @@ def test_foo(request): ) -def test_pytest_fixture_setup_and_post_finalizer_hook(testdir): - testdir.makeconftest( +def test_pytest_fixture_setup_and_post_finalizer_hook(pytester: Pytester) -> None: + pytester.makeconftest( """ def pytest_fixture_setup(fixturedef, request): print('ROOT setup hook called for {0} from {1}'.format(fixturedef.argname, request.node.name)) @@ -3818,7 +3878,7 @@ def pytest_fixture_post_finalizer(fixturedef, request): print('ROOT finalizer hook called for {0} from {1}'.format(fixturedef.argname, request.node.name)) """ ) - testdir.makepyfile( + pytester.makepyfile( **{ "tests/conftest.py": """ def pytest_fixture_setup(fixturedef, request): @@ -3839,7 +3899,7 @@ def test_func(my_fixture): """, } ) - result = testdir.runpytest("-s") + result = pytester.runpytest("-s") assert result.ret == 0 result.stdout.fnmatch_lines( [ @@ -3856,10 +3916,12 @@ class TestScopeOrdering: """Class of tests that ensure fixtures are ordered based on their scopes (#2405)""" @pytest.mark.parametrize("variant", ["mark", "autouse"]) - def test_func_closure_module_auto(self, testdir, variant, monkeypatch): + def test_func_closure_module_auto( + self, pytester: Pytester, variant, monkeypatch + ) -> None: """Semantically identical to the example posted in #2405 when ``use_mark=True``""" monkeypatch.setenv("FIXTURE_ACTIVATION_VARIANT", variant) - testdir.makepyfile( + pytester.makepyfile( """ import warnings import os @@ -3885,16 +3947,18 @@ def test_func(m1): pass """ ) - items, _ = testdir.inline_genitems() + items, _ = pytester.inline_genitems() request = FixtureRequest(items[0], _ispytest=True) assert request.fixturenames == "m1 f1".split() - def test_func_closure_with_native_fixtures(self, testdir, monkeypatch) -> None: + def test_func_closure_with_native_fixtures( + self, pytester: Pytester, monkeypatch: MonkeyPatch + ) -> None: """Sanity check that verifies the order returned by the closures and the actual fixture execution order: The execution order may differ because of fixture inter-dependencies. """ monkeypatch.setattr(pytest, "FIXTURE_ORDER", [], raising=False) - testdir.makepyfile( + pytester.makepyfile( """ import pytest @@ -3931,19 +3995,19 @@ def f2(): def test_foo(f1, p1, m1, f2, s1): pass """ ) - items, _ = testdir.inline_genitems() + items, _ = pytester.inline_genitems() request = FixtureRequest(items[0], _ispytest=True) # order of fixtures based on their scope and position in the parameter list assert ( request.fixturenames == "s1 my_tmpdir_factory p1 m1 f1 f2 my_tmpdir".split() ) - testdir.runpytest() + pytester.runpytest() # actual fixture execution differs: dependent fixtures must be created first ("my_tmpdir") FIXTURE_ORDER = pytest.FIXTURE_ORDER # type: ignore[attr-defined] assert FIXTURE_ORDER == "s1 my_tmpdir_factory p1 m1 my_tmpdir f1 f2".split() - def test_func_closure_module(self, testdir): - testdir.makepyfile( + def test_func_closure_module(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @@ -3957,15 +4021,15 @@ def test_func(f1, m1): pass """ ) - items, _ = testdir.inline_genitems() + items, _ = pytester.inline_genitems() request = FixtureRequest(items[0], _ispytest=True) assert request.fixturenames == "m1 f1".split() - def test_func_closure_scopes_reordered(self, testdir): + def test_func_closure_scopes_reordered(self, pytester: Pytester) -> None: """Test ensures that fixtures are ordered by scope regardless of the order of the parameters, although fixtures of same scope keep the declared order """ - testdir.makepyfile( + pytester.makepyfile( """ import pytest @@ -3990,13 +4054,15 @@ def test_func(self, f2, f1, c1, m1, s1): pass """ ) - items, _ = testdir.inline_genitems() + items, _ = pytester.inline_genitems() request = FixtureRequest(items[0], _ispytest=True) assert request.fixturenames == "s1 m1 c1 f2 f1".split() - def test_func_closure_same_scope_closer_root_first(self, testdir): + def test_func_closure_same_scope_closer_root_first( + self, pytester: Pytester + ) -> None: """Auto-use fixtures of same scope are ordered by closer-to-root first""" - testdir.makeconftest( + pytester.makeconftest( """ import pytest @@ -4004,7 +4070,7 @@ def test_func_closure_same_scope_closer_root_first(self, testdir): def m_conf(): pass """ ) - testdir.makepyfile( + pytester.makepyfile( **{ "sub/conftest.py": """ import pytest @@ -4030,13 +4096,13 @@ def test_func(m_test, f1): """, } ) - items, _ = testdir.inline_genitems() + items, _ = pytester.inline_genitems() request = FixtureRequest(items[0], _ispytest=True) assert request.fixturenames == "p_sub m_conf m_sub m_test f1".split() - def test_func_closure_all_scopes_complex(self, testdir): + def test_func_closure_all_scopes_complex(self, pytester: Pytester) -> None: """Complex test involving all scopes and mixing autouse with normal fixtures""" - testdir.makeconftest( + pytester.makeconftest( """ import pytest @@ -4047,8 +4113,8 @@ def s1(): pass def p1(): pass """ ) - testdir.makepyfile(**{"__init__.py": ""}) - testdir.makepyfile( + pytester.makepyfile(**{"__init__.py": ""}) + pytester.makepyfile( """ import pytest @@ -4074,11 +4140,11 @@ def test_func(self, f2, f1, m2): pass """ ) - items, _ = testdir.inline_genitems() + items, _ = pytester.inline_genitems() request = FixtureRequest(items[0], _ispytest=True) assert request.fixturenames == "s1 p1 m1 m2 c1 f2 f1".split() - def test_multiple_packages(self, testdir): + def test_multiple_packages(self, pytester: Pytester) -> None: """Complex test involving multiple package fixtures. Make sure teardowns are executed in order. . @@ -4093,11 +4159,12 @@ def test_multiple_packages(self, testdir): ├── conftest.py └── test_2.py """ - root = testdir.mkdir("root") - root.join("__init__.py").write("values = []") - sub1 = root.mkdir("sub1") - sub1.ensure("__init__.py") - sub1.join("conftest.py").write( + root = pytester.mkdir("root") + root.joinpath("__init__.py").write_text("values = []") + sub1 = root.joinpath("sub1") + sub1.mkdir() + sub1.joinpath("__init__.py").touch() + sub1.joinpath("conftest.py").write_text( textwrap.dedent( """\ import pytest @@ -4110,7 +4177,7 @@ def fix(): """ ) ) - sub1.join("test_1.py").write( + sub1.joinpath("test_1.py").write_text( textwrap.dedent( """\ from .. import values @@ -4119,9 +4186,10 @@ def test_1(fix): """ ) ) - sub2 = root.mkdir("sub2") - sub2.ensure("__init__.py") - sub2.join("conftest.py").write( + sub2 = root.joinpath("sub2") + sub2.mkdir() + sub2.joinpath("__init__.py").touch() + sub2.joinpath("conftest.py").write_text( textwrap.dedent( """\ import pytest @@ -4134,7 +4202,7 @@ def fix(): """ ) ) - sub2.join("test_2.py").write( + sub2.joinpath("test_2.py").write_text( textwrap.dedent( """\ from .. import values @@ -4143,14 +4211,14 @@ def test_2(fix): """ ) ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=2) - def test_class_fixture_self_instance(self, testdir): + def test_class_fixture_self_instance(self, pytester: Pytester) -> None: """Check that plugin classes which implement fixtures receive the plugin instance as self (see #2270). """ - testdir.makeconftest( + pytester.makeconftest( """ import pytest @@ -4168,14 +4236,14 @@ def myfix(self): """ ) - testdir.makepyfile( + pytester.makepyfile( """ class TestClass(object): def test_1(self, myfix): assert myfix == 1 """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=1) @@ -4190,9 +4258,9 @@ def fix(): assert fix() == 1 -def test_fixture_param_shadowing(testdir): +def test_fixture_param_shadowing(pytester: Pytester) -> None: """Parametrized arguments would be shadowed if a fixture with the same name also exists (#5036)""" - testdir.makepyfile( + pytester.makepyfile( """ import pytest @@ -4225,7 +4293,7 @@ def test_indirect(arg2): """ ) # Only one test should have run - result = testdir.runpytest("-v") + result = pytester.runpytest("-v") result.assert_outcomes(passed=4) result.stdout.fnmatch_lines(["*::test_direct[[]1[]]*"]) result.stdout.fnmatch_lines(["*::test_normal_fixture[[]a[]]*"]) @@ -4233,9 +4301,9 @@ def test_indirect(arg2): result.stdout.fnmatch_lines(["*::test_indirect[[]1[]]*"]) -def test_fixture_named_request(testdir): - testdir.copy_example("fixtures/test_fixture_named_request.py") - result = testdir.runpytest() +def test_fixture_named_request(pytester: Pytester) -> None: + pytester.copy_example("fixtures/test_fixture_named_request.py") + result = pytester.runpytest() result.stdout.fnmatch_lines( [ "*'request' is a reserved word for fixtures, use another name:", @@ -4244,9 +4312,9 @@ def test_fixture_named_request(testdir): ) -def test_indirect_fixture_does_not_break_scope(testdir): +def test_indirect_fixture_does_not_break_scope(pytester: Pytester) -> None: """Ensure that fixture scope is respected when using indirect fixtures (#570)""" - testdir.makepyfile( + pytester.makepyfile( """ import pytest instantiated = [] @@ -4291,14 +4359,14 @@ def test_check_fixture_instantiations(): ] """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.assert_outcomes(passed=7) -def test_fixture_parametrization_nparray(testdir): +def test_fixture_parametrization_nparray(pytester: Pytester) -> None: pytest.importorskip("numpy") - testdir.makepyfile( + pytester.makepyfile( """ from numpy import linspace from pytest import fixture @@ -4311,18 +4379,18 @@ def test_bug(value): assert value == value """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.assert_outcomes(passed=10) -def test_fixture_arg_ordering(testdir): +def test_fixture_arg_ordering(pytester: Pytester) -> None: """ This test describes how fixtures in the same scope but without explicit dependencies between them are created. While users should make dependencies explicit, often they rely on this order, so this test exists to catch regressions in this regard. See #6540 and #6492. """ - p1 = testdir.makepyfile( + p1 = pytester.makepyfile( """ import pytest @@ -4346,12 +4414,12 @@ def test_suffix(fix_combined): assert suffixes == ["fix_1", "fix_2", "fix_3", "fix_4", "fix_5"] """ ) - result = testdir.runpytest("-vv", str(p1)) + result = pytester.runpytest("-vv", str(p1)) assert result.ret == 0 -def test_yield_fixture_with_no_value(testdir): - testdir.makepyfile( +def test_yield_fixture_with_no_value(pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @pytest.fixture(name='custom') @@ -4364,7 +4432,7 @@ def test_fixt(custom): """ ) expected = "E ValueError: custom did not yield a value" - result = testdir.runpytest() + result = pytester.runpytest() result.assert_outcomes(errors=1) result.stdout.fnmatch_lines([expected]) assert result.ret == ExitCode.TESTS_FAILED diff --git a/testing/python/integration.py b/testing/python/integration.py index f006e5ed4ee..5dce6bdca28 100644 --- a/testing/python/integration.py +++ b/testing/python/integration.py @@ -3,13 +3,14 @@ import pytest from _pytest import runner from _pytest._code import getfslineno +from _pytest.pytester import Pytester class TestOEJSKITSpecials: def test_funcarg_non_pycollectobj( - self, testdir, recwarn + self, pytester: Pytester, recwarn ) -> None: # rough jstests usage - testdir.makeconftest( + pytester.makeconftest( """ import pytest def pytest_pycollect_makeitem(collector, name, obj): @@ -20,7 +21,7 @@ def reportinfo(self): return self.fspath, 3, "xyz" """ ) - modcol = testdir.getmodulecol( + modcol = pytester.getmodulecol( """ import pytest @pytest.fixture @@ -39,8 +40,10 @@ class MyClass(object): pytest._fillfuncargs(clscol) assert clscol.funcargs["arg1"] == 42 - def test_autouse_fixture(self, testdir, recwarn) -> None: # rough jstests usage - testdir.makeconftest( + def test_autouse_fixture( + self, pytester: Pytester, recwarn + ) -> None: # rough jstests usage + pytester.makeconftest( """ import pytest def pytest_pycollect_makeitem(collector, name, obj): @@ -51,7 +54,7 @@ def reportinfo(self): return self.fspath, 3, "xyz" """ ) - modcol = testdir.getmodulecol( + modcol = pytester.getmodulecol( """ import pytest @pytest.fixture(autouse=True) @@ -125,8 +128,8 @@ def f(x, y, z): values = getfuncargnames(f) assert values == ("y", "z") - def test_unittest_mock(self, testdir): - testdir.makepyfile( + def test_unittest_mock(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import unittest.mock class T(unittest.TestCase): @@ -137,11 +140,11 @@ def test_hello(self, abspath): abspath.assert_any_call("hello") """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=1) - def test_unittest_mock_and_fixture(self, testdir): - testdir.makepyfile( + def test_unittest_mock_and_fixture(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import os.path import unittest.mock @@ -158,12 +161,12 @@ def test_hello(inject_me): os.path.abspath("hello") """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=1) - def test_unittest_mock_and_pypi_mock(self, testdir): + def test_unittest_mock_and_pypi_mock(self, pytester: Pytester) -> None: pytest.importorskip("mock", "1.0.1") - testdir.makepyfile( + pytester.makepyfile( """ import mock import unittest.mock @@ -181,15 +184,15 @@ def test_hello_mock(self, abspath): abspath.assert_any_call("hello") """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=2) - def test_mock_sentinel_check_against_numpy_like(self, testdir): + def test_mock_sentinel_check_against_numpy_like(self, pytester: Pytester) -> None: """Ensure our function that detects mock arguments compares against sentinels using identity to circumvent objects which can't be compared with equality against others in a truth context, like with numpy arrays (#5606). """ - testdir.makepyfile( + pytester.makepyfile( dummy=""" class NumpyLike: def __init__(self, value): @@ -199,7 +202,7 @@ def __eq__(self, other): FOO = NumpyLike(10) """ ) - testdir.makepyfile( + pytester.makepyfile( """ from unittest.mock import patch import dummy @@ -209,12 +212,12 @@ def test_hello(self): assert dummy.FOO.value == 50 """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=1) - def test_mock(self, testdir): + def test_mock(self, pytester: Pytester) -> None: pytest.importorskip("mock", "1.0.1") - testdir.makepyfile( + pytester.makepyfile( """ import os import unittest @@ -237,7 +240,7 @@ def test_someting(normpath, abspath, tmpdir): assert os.path.basename("123") == "mock_basename" """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=2) calls = reprec.getcalls("pytest_runtest_logreport") funcnames = [ @@ -245,9 +248,9 @@ def test_someting(normpath, abspath, tmpdir): ] assert funcnames == ["T.test_hello", "test_someting"] - def test_mock_sorting(self, testdir): + def test_mock_sorting(self, pytester: Pytester) -> None: pytest.importorskip("mock", "1.0.1") - testdir.makepyfile( + pytester.makepyfile( """ import os import mock @@ -263,15 +266,15 @@ def test_three(abspath): pass """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() calls = reprec.getreports("pytest_runtest_logreport") calls = [x for x in calls if x.when == "call"] names = [x.nodeid.split("::")[-1] for x in calls] assert names == ["test_one", "test_two", "test_three"] - def test_mock_double_patch_issue473(self, testdir): + def test_mock_double_patch_issue473(self, pytester: Pytester) -> None: pytest.importorskip("mock", "1.0.1") - testdir.makepyfile( + pytester.makepyfile( """ from mock import patch from pytest import mark @@ -284,13 +287,13 @@ def test_simple_thing(self, mock_path, mock_getcwd): pass """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=1) class TestReRunTests: - def test_rerun(self, testdir): - testdir.makeconftest( + def test_rerun(self, pytester: Pytester) -> None: + pytester.makeconftest( """ from _pytest.runner import runtestprotocol def pytest_runtest_protocol(item, nextitem): @@ -298,7 +301,7 @@ def pytest_runtest_protocol(item, nextitem): runtestprotocol(item, log=True, nextitem=nextitem) """ ) - testdir.makepyfile( + pytester.makepyfile( """ import pytest count = 0 @@ -314,7 +317,7 @@ def test_fix(fix): pass """ ) - result = testdir.runpytest("-s") + result = pytester.runpytest("-s") result.stdout.fnmatch_lines( """ *fix count 0* @@ -336,21 +339,21 @@ def test_pytestconfig_is_session_scoped() -> None: class TestNoselikeTestAttribute: - def test_module_with_global_test(self, testdir): - testdir.makepyfile( + def test_module_with_global_test(self, pytester: Pytester) -> None: + pytester.makepyfile( """ __test__ = False def test_hello(): pass """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() assert not reprec.getfailedcollections() calls = reprec.getreports("pytest_runtest_logreport") assert not calls - def test_class_and_method(self, testdir): - testdir.makepyfile( + def test_class_and_method(self, pytester: Pytester) -> None: + pytester.makepyfile( """ __test__ = True def test_func(): @@ -363,13 +366,13 @@ def test_method(self): pass """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() assert not reprec.getfailedcollections() calls = reprec.getreports("pytest_runtest_logreport") assert not calls - def test_unittest_class(self, testdir): - testdir.makepyfile( + def test_unittest_class(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import unittest class TC(unittest.TestCase): @@ -381,20 +384,20 @@ def test_2(self): pass """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() assert not reprec.getfailedcollections() call = reprec.getcalls("pytest_collection_modifyitems")[0] assert len(call.items) == 1 assert call.items[0].cls.__name__ == "TC" - def test_class_with_nasty_getattr(self, testdir): + def test_class_with_nasty_getattr(self, pytester: Pytester) -> None: """Make sure we handle classes with a custom nasty __getattr__ right. With a custom __getattr__ which e.g. returns a function (like with a RPC wrapper), we shouldn't assume this meant "__test__ = True". """ # https://github.com/pytest-dev/pytest/issues/1204 - testdir.makepyfile( + pytester.makepyfile( """ class MetaModel(type): @@ -413,7 +416,7 @@ def test_blah(self): pass """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() assert not reprec.getfailedcollections() call = reprec.getcalls("pytest_collection_modifyitems")[0] assert not call.items @@ -422,8 +425,8 @@ def test_blah(self): class TestParameterize: """#351""" - def test_idfn_marker(self, testdir): - testdir.makepyfile( + def test_idfn_marker(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @@ -440,11 +443,11 @@ def test_params(a, b): pass """ ) - res = testdir.runpytest("--collect-only") + res = pytester.runpytest("--collect-only") res.stdout.fnmatch_lines(["*spam-2*", "*ham-2*"]) - def test_idfn_fixture(self, testdir): - testdir.makepyfile( + def test_idfn_fixture(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @@ -468,5 +471,5 @@ def test_params(a, b): pass """ ) - res = testdir.runpytest("--collect-only") + res = pytester.runpytest("--collect-only") res.stdout.fnmatch_lines(["*spam-2*", "*ham-2*"]) diff --git a/testing/python/metafunc.py b/testing/python/metafunc.py index 676f1d988bc..c50ea53d255 100644 --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -23,7 +23,7 @@ from _pytest.compat import getfuncargnames from _pytest.compat import NOTSET from _pytest.outcomes import fail -from _pytest.pytester import Testdir +from _pytest.pytester import Pytester from _pytest.python import _idval from _pytest.python import idmaker @@ -123,7 +123,7 @@ def func(x): ): metafunc.parametrize("x", [1], scope="doggy") # type: ignore[arg-type] - def test_parametrize_request_name(self, testdir: Testdir) -> None: + def test_parametrize_request_name(self, pytester: Pytester) -> None: """Show proper error when 'request' is used as a parameter name in parametrize (#6183)""" def func(request): @@ -550,12 +550,12 @@ def getini(self, name): ) assert result == [expected] - def test_parametrize_ids_exception(self, testdir: Testdir) -> None: + def test_parametrize_ids_exception(self, pytester: Pytester) -> None: """ - :param testdir: the instance of Testdir class, a temporary + :param pytester: the instance of Pytester class, a temporary test directory. """ - testdir.makepyfile( + pytester.makepyfile( """ import pytest @@ -567,7 +567,7 @@ def test_foo(arg): pass """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines( [ "*Exception: bad ids", @@ -575,8 +575,8 @@ def test_foo(arg): ] ) - def test_parametrize_ids_returns_non_string(self, testdir: Testdir) -> None: - testdir.makepyfile( + def test_parametrize_ids_returns_non_string(self, pytester: Pytester) -> None: + pytester.makepyfile( """\ import pytest @@ -592,7 +592,7 @@ def test_int(arg): assert arg """ ) - result = testdir.runpytest("-vv", "-s") + result = pytester.runpytest("-vv", "-s") result.stdout.fnmatch_lines( [ "test_parametrize_ids_returns_non_string.py::test[arg0] PASSED", @@ -682,7 +682,7 @@ def func(x, y): ): metafunc.parametrize("x, y", [("a", "b")], indirect={}) # type: ignore[arg-type] - def test_parametrize_indirect_list_functional(self, testdir: Testdir) -> None: + def test_parametrize_indirect_list_functional(self, pytester: Pytester) -> None: """ #714 Test parametrization with 'indirect' parameter applied on @@ -690,10 +690,10 @@ def test_parametrize_indirect_list_functional(self, testdir: Testdir) -> None: be used directly rather than being passed to the fixture y. - :param testdir: the instance of Testdir class, a temporary + :param pytester: the instance of Pytester class, a temporary test directory. """ - testdir.makepyfile( + pytester.makepyfile( """ import pytest @pytest.fixture(scope='function') @@ -708,7 +708,7 @@ def test_simple(x,y): assert len(y) == 1 """ ) - result = testdir.runpytest("-v") + result = pytester.runpytest("-v") result.stdout.fnmatch_lines(["*test_simple*a-b*", "*1 passed*"]) def test_parametrize_indirect_list_error(self) -> None: @@ -722,7 +722,7 @@ def func(x, y): metafunc.parametrize("x, y", [("a", "b")], indirect=["x", "z"]) def test_parametrize_uses_no_fixture_error_indirect_false( - self, testdir: Testdir + self, pytester: Pytester ) -> None: """The 'uses no fixture' error tells the user at collection time that the parametrize data they've set up doesn't correspond to the @@ -731,7 +731,7 @@ def test_parametrize_uses_no_fixture_error_indirect_false( #714 """ - testdir.makepyfile( + pytester.makepyfile( """ import pytest @@ -740,14 +740,14 @@ def test_simple(x): assert len(x) == 3 """ ) - result = testdir.runpytest("--collect-only") + result = pytester.runpytest("--collect-only") result.stdout.fnmatch_lines(["*uses no argument 'y'*"]) def test_parametrize_uses_no_fixture_error_indirect_true( - self, testdir: Testdir + self, pytester: Pytester ) -> None: """#714""" - testdir.makepyfile( + pytester.makepyfile( """ import pytest @pytest.fixture(scope='function') @@ -762,14 +762,14 @@ def test_simple(x): assert len(x) == 3 """ ) - result = testdir.runpytest("--collect-only") + result = pytester.runpytest("--collect-only") result.stdout.fnmatch_lines(["*uses no fixture 'y'*"]) def test_parametrize_indirect_uses_no_fixture_error_indirect_string( - self, testdir: Testdir + self, pytester: Pytester ) -> None: """#714""" - testdir.makepyfile( + pytester.makepyfile( """ import pytest @pytest.fixture(scope='function') @@ -781,14 +781,14 @@ def test_simple(x): assert len(x) == 3 """ ) - result = testdir.runpytest("--collect-only") + result = pytester.runpytest("--collect-only") result.stdout.fnmatch_lines(["*uses no fixture 'y'*"]) def test_parametrize_indirect_uses_no_fixture_error_indirect_list( - self, testdir: Testdir + self, pytester: Pytester ) -> None: """#714""" - testdir.makepyfile( + pytester.makepyfile( """ import pytest @pytest.fixture(scope='function') @@ -800,12 +800,14 @@ def test_simple(x): assert len(x) == 3 """ ) - result = testdir.runpytest("--collect-only") + result = pytester.runpytest("--collect-only") result.stdout.fnmatch_lines(["*uses no fixture 'y'*"]) - def test_parametrize_argument_not_in_indirect_list(self, testdir: Testdir) -> None: + def test_parametrize_argument_not_in_indirect_list( + self, pytester: Pytester + ) -> None: """#714""" - testdir.makepyfile( + pytester.makepyfile( """ import pytest @pytest.fixture(scope='function') @@ -817,13 +819,13 @@ def test_simple(x): assert len(x) == 3 """ ) - result = testdir.runpytest("--collect-only") + result = pytester.runpytest("--collect-only") result.stdout.fnmatch_lines(["*uses no argument 'y'*"]) def test_parametrize_gives_indicative_error_on_function_with_default_argument( - self, testdir + self, pytester: Pytester ) -> None: - testdir.makepyfile( + pytester.makepyfile( """ import pytest @@ -832,13 +834,13 @@ def test_simple(x, y=1): assert len(x) == 1 """ ) - result = testdir.runpytest("--collect-only") + result = pytester.runpytest("--collect-only") result.stdout.fnmatch_lines( ["*already takes an argument 'y' with a default value"] ) - def test_parametrize_functional(self, testdir: Testdir) -> None: - testdir.makepyfile( + def test_parametrize_functional(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest def pytest_generate_tests(metafunc): @@ -853,7 +855,7 @@ def test_simple(x,y): assert y == 2 """ ) - result = testdir.runpytest("-v") + result = pytester.runpytest("-v") result.stdout.fnmatch_lines( ["*test_simple*1-2*", "*test_simple*2-2*", "*2 passed*"] ) @@ -884,8 +886,8 @@ def test_parametrize_twoargs(self) -> None: assert metafunc._calls[1].funcargs == dict(x=3, y=4) assert metafunc._calls[1].id == "3-4" - def test_parametrize_multiple_times(self, testdir: Testdir) -> None: - testdir.makepyfile( + def test_parametrize_multiple_times(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest pytestmark = pytest.mark.parametrize("x", [1,2]) @@ -897,12 +899,12 @@ def test_meth(self, x, y): assert 0, x """ ) - result = testdir.runpytest() + result = pytester.runpytest() assert result.ret == 1 result.assert_outcomes(failed=6) - def test_parametrize_CSV(self, testdir: Testdir) -> None: - testdir.makepyfile( + def test_parametrize_CSV(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @pytest.mark.parametrize("x, y,", [(1,2), (2,3)]) @@ -910,11 +912,11 @@ def test_func(x, y): assert x+1 == y """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=2) - def test_parametrize_class_scenarios(self, testdir: Testdir) -> None: - testdir.makepyfile( + def test_parametrize_class_scenarios(self, pytester: Pytester) -> None: + pytester.makepyfile( """ # same as doc/en/example/parametrize scenario example def pytest_generate_tests(metafunc): @@ -941,7 +943,7 @@ def test_3(self, arg, arg2): pass """ ) - result = testdir.runpytest("-v") + result = pytester.runpytest("-v") assert result.ret == 0 result.stdout.fnmatch_lines( """ @@ -978,8 +980,8 @@ def function4(arg1, *args, **kwargs): class TestMetafuncFunctional: - def test_attributes(self, testdir: Testdir) -> None: - p = testdir.makepyfile( + def test_attributes(self, pytester: Pytester) -> None: + p = pytester.makepyfile( """ # assumes that generate/provide runs in the same process import sys, pytest @@ -1005,11 +1007,11 @@ def test_method(self, metafunc, pytestconfig): assert metafunc.cls == TestClass """ ) - result = testdir.runpytest(p, "-v") + result = pytester.runpytest(p, "-v") result.assert_outcomes(passed=2) - def test_two_functions(self, testdir: Testdir) -> None: - p = testdir.makepyfile( + def test_two_functions(self, pytester: Pytester) -> None: + p = pytester.makepyfile( """ def pytest_generate_tests(metafunc): metafunc.parametrize('arg1', [10, 20], ids=['0', '1']) @@ -1021,7 +1023,7 @@ def test_func2(arg1): assert arg1 in (10, 20) """ ) - result = testdir.runpytest("-v", p) + result = pytester.runpytest("-v", p) result.stdout.fnmatch_lines( [ "*test_func1*0*PASS*", @@ -1032,8 +1034,8 @@ def test_func2(arg1): ] ) - def test_noself_in_method(self, testdir: Testdir) -> None: - p = testdir.makepyfile( + def test_noself_in_method(self, pytester: Pytester) -> None: + p = pytester.makepyfile( """ def pytest_generate_tests(metafunc): assert 'xyz' not in metafunc.fixturenames @@ -1043,11 +1045,11 @@ def test_hello(xyz): pass """ ) - result = testdir.runpytest(p) + result = pytester.runpytest(p) result.assert_outcomes(passed=1) - def test_generate_tests_in_class(self, testdir: Testdir) -> None: - p = testdir.makepyfile( + def test_generate_tests_in_class(self, pytester: Pytester) -> None: + p = pytester.makepyfile( """ class TestClass(object): def pytest_generate_tests(self, metafunc): @@ -1057,11 +1059,11 @@ def test_myfunc(self, hello): assert hello == "world" """ ) - result = testdir.runpytest("-v", p) + result = pytester.runpytest("-v", p) result.stdout.fnmatch_lines(["*test_myfunc*hello*PASS*", "*1 passed*"]) - def test_two_functions_not_same_instance(self, testdir: Testdir) -> None: - p = testdir.makepyfile( + def test_two_functions_not_same_instance(self, pytester: Pytester) -> None: + p = pytester.makepyfile( """ def pytest_generate_tests(metafunc): metafunc.parametrize('arg1', [10, 20], ids=["0", "1"]) @@ -1072,13 +1074,13 @@ def test_func(self, arg1): self.x = 1 """ ) - result = testdir.runpytest("-v", p) + result = pytester.runpytest("-v", p) result.stdout.fnmatch_lines( ["*test_func*0*PASS*", "*test_func*1*PASS*", "*2 pass*"] ) - def test_issue28_setup_method_in_generate_tests(self, testdir: Testdir) -> None: - p = testdir.makepyfile( + def test_issue28_setup_method_in_generate_tests(self, pytester: Pytester) -> None: + p = pytester.makepyfile( """ def pytest_generate_tests(metafunc): metafunc.parametrize('arg1', [1]) @@ -1090,11 +1092,11 @@ def setup_method(self, func): self.val = 1 """ ) - result = testdir.runpytest(p) + result = pytester.runpytest(p) result.assert_outcomes(passed=1) - def test_parametrize_functional2(self, testdir: Testdir) -> None: - testdir.makepyfile( + def test_parametrize_functional2(self, pytester: Pytester) -> None: + pytester.makepyfile( """ def pytest_generate_tests(metafunc): metafunc.parametrize("arg1", [1,2]) @@ -1103,13 +1105,13 @@ def test_hello(arg1, arg2): assert 0, (arg1, arg2) """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines( ["*(1, 4)*", "*(1, 5)*", "*(2, 4)*", "*(2, 5)*", "*4 failed*"] ) - def test_parametrize_and_inner_getfixturevalue(self, testdir: Testdir) -> None: - p = testdir.makepyfile( + def test_parametrize_and_inner_getfixturevalue(self, pytester: Pytester) -> None: + p = pytester.makepyfile( """ def pytest_generate_tests(metafunc): metafunc.parametrize("arg1", [1], indirect=True) @@ -1129,11 +1131,11 @@ def test_func1(arg1, arg2): assert arg1 == 11 """ ) - result = testdir.runpytest("-v", p) + result = pytester.runpytest("-v", p) result.stdout.fnmatch_lines(["*test_func1*1*PASS*", "*1 passed*"]) - def test_parametrize_on_setup_arg(self, testdir: Testdir) -> None: - p = testdir.makepyfile( + def test_parametrize_on_setup_arg(self, pytester: Pytester) -> None: + p = pytester.makepyfile( """ def pytest_generate_tests(metafunc): assert "arg1" in metafunc.fixturenames @@ -1152,17 +1154,17 @@ def test_func(arg2): assert arg2 == 10 """ ) - result = testdir.runpytest("-v", p) + result = pytester.runpytest("-v", p) result.stdout.fnmatch_lines(["*test_func*1*PASS*", "*1 passed*"]) - def test_parametrize_with_ids(self, testdir: Testdir) -> None: - testdir.makeini( + def test_parametrize_with_ids(self, pytester: Pytester) -> None: + pytester.makeini( """ [pytest] console_output_style=classic """ ) - testdir.makepyfile( + pytester.makepyfile( """ import pytest def pytest_generate_tests(metafunc): @@ -1173,14 +1175,14 @@ def test_function(a, b): assert a == b """ ) - result = testdir.runpytest("-v") + result = pytester.runpytest("-v") assert result.ret == 1 result.stdout.fnmatch_lines_random( ["*test_function*basic*PASSED", "*test_function*advanced*FAILED"] ) - def test_parametrize_without_ids(self, testdir: Testdir) -> None: - testdir.makepyfile( + def test_parametrize_without_ids(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest def pytest_generate_tests(metafunc): @@ -1191,7 +1193,7 @@ def test_function(a, b): assert 1 """ ) - result = testdir.runpytest("-v") + result = pytester.runpytest("-v") result.stdout.fnmatch_lines( """ *test_function*1-b0* @@ -1199,8 +1201,8 @@ def test_function(a, b): """ ) - def test_parametrize_with_None_in_ids(self, testdir: Testdir) -> None: - testdir.makepyfile( + def test_parametrize_with_None_in_ids(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest def pytest_generate_tests(metafunc): @@ -1211,7 +1213,7 @@ def test_function(a, b): assert a == b """ ) - result = testdir.runpytest("-v") + result = pytester.runpytest("-v") assert result.ret == 1 result.stdout.fnmatch_lines_random( [ @@ -1221,9 +1223,9 @@ def test_function(a, b): ] ) - def test_fixture_parametrized_empty_ids(self, testdir: Testdir) -> None: + def test_fixture_parametrized_empty_ids(self, pytester: Pytester) -> None: """Fixtures parametrized with empty ids cause an internal error (#1849).""" - testdir.makepyfile( + pytester.makepyfile( """ import pytest @@ -1235,12 +1237,12 @@ def test_temp(temp): pass """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["* 1 skipped *"]) - def test_parametrized_empty_ids(self, testdir: Testdir) -> None: + def test_parametrized_empty_ids(self, pytester: Pytester) -> None: """Tests parametrized with empty ids cause an internal error (#1849).""" - testdir.makepyfile( + pytester.makepyfile( """ import pytest @@ -1249,12 +1251,12 @@ def test_temp(temp): pass """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["* 1 skipped *"]) - def test_parametrized_ids_invalid_type(self, testdir: Testdir) -> None: + def test_parametrized_ids_invalid_type(self, pytester: Pytester) -> None: """Test error with non-strings/non-ints, without generator (#1857).""" - testdir.makepyfile( + pytester.makepyfile( """ import pytest @@ -1263,7 +1265,7 @@ def test_ids_numbers(x,expected): assert x * 2 == expected """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines( [ "In test_ids_numbers: ids must be list of string/float/int/bool," @@ -1272,9 +1274,9 @@ def test_ids_numbers(x,expected): ) def test_parametrize_with_identical_ids_get_unique_names( - self, testdir: Testdir + self, pytester: Pytester ) -> None: - testdir.makepyfile( + pytester.makepyfile( """ import pytest def pytest_generate_tests(metafunc): @@ -1285,7 +1287,7 @@ def test_function(a, b): assert a == b """ ) - result = testdir.runpytest("-v") + result = pytester.runpytest("-v") assert result.ret == 1 result.stdout.fnmatch_lines_random( ["*test_function*a0*PASSED*", "*test_function*a1*FAILED*"] @@ -1293,9 +1295,9 @@ def test_function(a, b): @pytest.mark.parametrize(("scope", "length"), [("module", 2), ("function", 4)]) def test_parametrize_scope_overrides( - self, testdir: Testdir, scope: str, length: int + self, pytester: Pytester, scope: str, length: int ) -> None: - testdir.makepyfile( + pytester.makepyfile( """ import pytest values = [] @@ -1316,11 +1318,11 @@ def test_checklength(): """ % (scope, length) ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=5) - def test_parametrize_issue323(self, testdir: Testdir) -> None: - testdir.makepyfile( + def test_parametrize_issue323(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @@ -1334,11 +1336,11 @@ def test_it2(foo): pass """ ) - reprec = testdir.inline_run("--collect-only") + reprec = pytester.inline_run("--collect-only") assert not reprec.getcalls("pytest_internalerror") - def test_usefixtures_seen_in_generate_tests(self, testdir: Testdir) -> None: - testdir.makepyfile( + def test_usefixtures_seen_in_generate_tests(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest def pytest_generate_tests(metafunc): @@ -1350,13 +1352,13 @@ def test_function(): pass """ ) - reprec = testdir.runpytest() + reprec = pytester.runpytest() reprec.assert_outcomes(passed=1) - def test_generate_tests_only_done_in_subdir(self, testdir: Testdir) -> None: - sub1 = testdir.mkpydir("sub1") - sub2 = testdir.mkpydir("sub2") - sub1.join("conftest.py").write( + def test_generate_tests_only_done_in_subdir(self, pytester: Pytester) -> None: + sub1 = pytester.mkpydir("sub1") + sub2 = pytester.mkpydir("sub2") + sub1.joinpath("conftest.py").write_text( textwrap.dedent( """\ def pytest_generate_tests(metafunc): @@ -1364,7 +1366,7 @@ def pytest_generate_tests(metafunc): """ ) ) - sub2.join("conftest.py").write( + sub2.joinpath("conftest.py").write_text( textwrap.dedent( """\ def pytest_generate_tests(metafunc): @@ -1372,13 +1374,13 @@ def pytest_generate_tests(metafunc): """ ) ) - sub1.join("test_in_sub1.py").write("def test_1(): pass") - sub2.join("test_in_sub2.py").write("def test_2(): pass") - result = testdir.runpytest("--keep-duplicates", "-v", "-s", sub1, sub2, sub1) + sub1.joinpath("test_in_sub1.py").write_text("def test_1(): pass") + sub2.joinpath("test_in_sub2.py").write_text("def test_2(): pass") + result = pytester.runpytest("--keep-duplicates", "-v", "-s", sub1, sub2, sub1) result.assert_outcomes(passed=3) - def test_generate_same_function_names_issue403(self, testdir: Testdir) -> None: - testdir.makepyfile( + def test_generate_same_function_names_issue403(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @@ -1392,12 +1394,12 @@ def test_foo(x): test_y = make_tests() """ ) - reprec = testdir.runpytest() + reprec = pytester.runpytest() reprec.assert_outcomes(passed=4) - def test_parametrize_misspelling(self, testdir: Testdir) -> None: + def test_parametrize_misspelling(self, pytester: Pytester) -> None: """#463""" - testdir.makepyfile( + pytester.makepyfile( """ import pytest @@ -1406,7 +1408,7 @@ def test_foo(x): pass """ ) - result = testdir.runpytest("--collectonly") + result = pytester.runpytest("--collectonly") result.stdout.fnmatch_lines( [ "collected 0 items / 1 error", @@ -1426,8 +1428,8 @@ class TestMetafuncFunctionalAuto: """Tests related to automatically find out the correct scope for parametrized tests (#1832).""" - def test_parametrize_auto_scope(self, testdir: Testdir) -> None: - testdir.makepyfile( + def test_parametrize_auto_scope(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @@ -1445,11 +1447,11 @@ def test_2(animal): """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["* 3 passed *"]) - def test_parametrize_auto_scope_indirect(self, testdir: Testdir) -> None: - testdir.makepyfile( + def test_parametrize_auto_scope_indirect(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @@ -1468,11 +1470,11 @@ def test_2(animal, echo): assert echo in (1, 2, 3) """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["* 3 passed *"]) - def test_parametrize_auto_scope_override_fixture(self, testdir: Testdir) -> None: - testdir.makepyfile( + def test_parametrize_auto_scope_override_fixture(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @@ -1485,11 +1487,11 @@ def test_1(animal): assert animal in ('dog', 'cat') """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["* 2 passed *"]) - def test_parametrize_all_indirects(self, testdir: Testdir) -> None: - testdir.makepyfile( + def test_parametrize_all_indirects(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @@ -1512,11 +1514,11 @@ def test_2(animal, echo): assert echo in (1, 2, 3) """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["* 3 passed *"]) def test_parametrize_some_arguments_auto_scope( - self, testdir: Testdir, monkeypatch + self, pytester: Pytester, monkeypatch ) -> None: """Integration test for (#3941)""" class_fix_setup: List[object] = [] @@ -1524,7 +1526,7 @@ def test_parametrize_some_arguments_auto_scope( func_fix_setup: List[object] = [] monkeypatch.setattr(sys, "func_fix_setup", func_fix_setup, raising=False) - testdir.makepyfile( + pytester.makepyfile( """ import pytest import sys @@ -1545,13 +1547,13 @@ def test_bar(self): pass """ ) - result = testdir.runpytest_inprocess() + result = pytester.runpytest_inprocess() result.stdout.fnmatch_lines(["* 4 passed in *"]) assert func_fix_setup == [True] * 4 assert class_fix_setup == [10, 20] - def test_parametrize_issue634(self, testdir: Testdir) -> None: - testdir.makepyfile( + def test_parametrize_issue634(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @@ -1579,7 +1581,7 @@ def pytest_generate_tests(metafunc): metafunc.parametrize('foo', params, indirect=True) """ ) - result = testdir.runpytest("-s") + result = pytester.runpytest("-s") output = result.stdout.str() assert output.count("preparing foo-2") == 1 assert output.count("preparing foo-3") == 1 @@ -1588,7 +1590,7 @@ def pytest_generate_tests(metafunc): class TestMarkersWithParametrization: """#308""" - def test_simple_mark(self, testdir: Testdir) -> None: + def test_simple_mark(self, pytester: Pytester) -> None: s = """ import pytest @@ -1601,7 +1603,7 @@ def test_simple_mark(self, testdir: Testdir) -> None: def test_increment(n, expected): assert n + 1 == expected """ - items = testdir.getitems(s) + items = pytester.getitems(s) assert len(items) == 3 for item in items: assert "foo" in item.keywords @@ -1609,7 +1611,7 @@ def test_increment(n, expected): assert "bar" in items[1].keywords assert "bar" not in items[2].keywords - def test_select_based_on_mark(self, testdir: Testdir) -> None: + def test_select_based_on_mark(self, pytester: Pytester) -> None: s = """ import pytest @@ -1621,14 +1623,14 @@ def test_select_based_on_mark(self, testdir: Testdir) -> None: def test_increment(n, expected): assert n + 1 == expected """ - testdir.makepyfile(s) - rec = testdir.inline_run("-m", "foo") + pytester.makepyfile(s) + rec = pytester.inline_run("-m", "foo") passed, skipped, fail = rec.listoutcomes() assert len(passed) == 1 assert len(skipped) == 0 assert len(fail) == 0 - def test_simple_xfail(self, testdir: Testdir) -> None: + def test_simple_xfail(self, pytester: Pytester) -> None: s = """ import pytest @@ -1640,12 +1642,12 @@ def test_simple_xfail(self, testdir: Testdir) -> None: def test_increment(n, expected): assert n + 1 == expected """ - testdir.makepyfile(s) - reprec = testdir.inline_run() + pytester.makepyfile(s) + reprec = pytester.inline_run() # xfail is skip?? reprec.assertoutcome(passed=2, skipped=1) - def test_simple_xfail_single_argname(self, testdir: Testdir) -> None: + def test_simple_xfail_single_argname(self, pytester: Pytester) -> None: s = """ import pytest @@ -1657,11 +1659,11 @@ def test_simple_xfail_single_argname(self, testdir: Testdir) -> None: def test_isEven(n): assert n % 2 == 0 """ - testdir.makepyfile(s) - reprec = testdir.inline_run() + pytester.makepyfile(s) + reprec = pytester.inline_run() reprec.assertoutcome(passed=2, skipped=1) - def test_xfail_with_arg(self, testdir: Testdir) -> None: + def test_xfail_with_arg(self, pytester: Pytester) -> None: s = """ import pytest @@ -1673,11 +1675,11 @@ def test_xfail_with_arg(self, testdir: Testdir) -> None: def test_increment(n, expected): assert n + 1 == expected """ - testdir.makepyfile(s) - reprec = testdir.inline_run() + pytester.makepyfile(s) + reprec = pytester.inline_run() reprec.assertoutcome(passed=2, skipped=1) - def test_xfail_with_kwarg(self, testdir: Testdir) -> None: + def test_xfail_with_kwarg(self, pytester: Pytester) -> None: s = """ import pytest @@ -1689,11 +1691,11 @@ def test_xfail_with_kwarg(self, testdir: Testdir) -> None: def test_increment(n, expected): assert n + 1 == expected """ - testdir.makepyfile(s) - reprec = testdir.inline_run() + pytester.makepyfile(s) + reprec = pytester.inline_run() reprec.assertoutcome(passed=2, skipped=1) - def test_xfail_with_arg_and_kwarg(self, testdir: Testdir) -> None: + def test_xfail_with_arg_and_kwarg(self, pytester: Pytester) -> None: s = """ import pytest @@ -1705,12 +1707,12 @@ def test_xfail_with_arg_and_kwarg(self, testdir: Testdir) -> None: def test_increment(n, expected): assert n + 1 == expected """ - testdir.makepyfile(s) - reprec = testdir.inline_run() + pytester.makepyfile(s) + reprec = pytester.inline_run() reprec.assertoutcome(passed=2, skipped=1) @pytest.mark.parametrize("strict", [True, False]) - def test_xfail_passing_is_xpass(self, testdir: Testdir, strict: bool) -> None: + def test_xfail_passing_is_xpass(self, pytester: Pytester, strict: bool) -> None: s = """ import pytest @@ -1726,12 +1728,12 @@ def test_increment(n, expected): """.format( strict=strict ) - testdir.makepyfile(s) - reprec = testdir.inline_run() + pytester.makepyfile(s) + reprec = pytester.inline_run() passed, failed = (2, 1) if strict else (3, 0) reprec.assertoutcome(passed=passed, failed=failed) - def test_parametrize_called_in_generate_tests(self, testdir: Testdir) -> None: + def test_parametrize_called_in_generate_tests(self, pytester: Pytester) -> None: s = """ import pytest @@ -1750,13 +1752,15 @@ def pytest_generate_tests(metafunc): def test_increment(n, expected): assert n + 1 == expected """ - testdir.makepyfile(s) - reprec = testdir.inline_run() + pytester.makepyfile(s) + reprec = pytester.inline_run() reprec.assertoutcome(passed=2, skipped=2) - def test_parametrize_ID_generation_string_int_works(self, testdir: Testdir) -> None: + def test_parametrize_ID_generation_string_int_works( + self, pytester: Pytester + ) -> None: """#290""" - testdir.makepyfile( + pytester.makepyfile( """ import pytest @@ -1769,11 +1773,11 @@ def test_limit(limit, myfixture): return """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=2) @pytest.mark.parametrize("strict", [True, False]) - def test_parametrize_marked_value(self, testdir: Testdir, strict: bool) -> None: + def test_parametrize_marked_value(self, pytester: Pytester, strict: bool) -> None: s = """ import pytest @@ -1792,19 +1796,19 @@ def test_increment(n, expected): """.format( strict=strict ) - testdir.makepyfile(s) - reprec = testdir.inline_run() + pytester.makepyfile(s) + reprec = pytester.inline_run() passed, failed = (0, 2) if strict else (2, 0) reprec.assertoutcome(passed=passed, failed=failed) - def test_pytest_make_parametrize_id(self, testdir: Testdir) -> None: - testdir.makeconftest( + def test_pytest_make_parametrize_id(self, pytester: Pytester) -> None: + pytester.makeconftest( """ def pytest_make_parametrize_id(config, val): return str(val * 2) """ ) - testdir.makepyfile( + pytester.makepyfile( """ import pytest @@ -1813,17 +1817,17 @@ def test_func(x): pass """ ) - result = testdir.runpytest("-v") + result = pytester.runpytest("-v") result.stdout.fnmatch_lines(["*test_func*0*PASS*", "*test_func*2*PASS*"]) - def test_pytest_make_parametrize_id_with_argname(self, testdir: Testdir) -> None: - testdir.makeconftest( + def test_pytest_make_parametrize_id_with_argname(self, pytester: Pytester) -> None: + pytester.makeconftest( """ def pytest_make_parametrize_id(config, val, argname): return str(val * 2 if argname == 'x' else val * 10) """ ) - testdir.makepyfile( + pytester.makepyfile( """ import pytest @@ -1836,13 +1840,13 @@ def test_func_b(y): pass """ ) - result = testdir.runpytest("-v") + result = pytester.runpytest("-v") result.stdout.fnmatch_lines( ["*test_func_a*0*PASS*", "*test_func_a*2*PASS*", "*test_func_b*10*PASS*"] ) - def test_parametrize_positional_args(self, testdir: Testdir) -> None: - testdir.makepyfile( + def test_parametrize_positional_args(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @@ -1851,11 +1855,11 @@ def test_foo(a): pass """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.assert_outcomes(passed=1) - def test_parametrize_iterator(self, testdir: Testdir) -> None: - testdir.makepyfile( + def test_parametrize_iterator(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import itertools import pytest @@ -1877,7 +1881,7 @@ def test_converted_to_str(a, b): pass """ ) - result = testdir.runpytest("-vv", "-s") + result = pytester.runpytest("-vv", "-s") result.stdout.fnmatch_lines( [ "test_parametrize_iterator.py::test1[param0] PASSED", diff --git a/testing/python/raises.py b/testing/python/raises.py index 80634eebfbf..a3991adaef1 100644 --- a/testing/python/raises.py +++ b/testing/python/raises.py @@ -3,6 +3,7 @@ import pytest from _pytest.outcomes import Failed +from _pytest.pytester import Pytester class TestRaises: @@ -50,8 +51,8 @@ class E(Exception): pprint.pprint(excinfo) raise E() - def test_raises_as_contextmanager(self, testdir): - testdir.makepyfile( + def test_raises_as_contextmanager(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest import _pytest._code @@ -75,11 +76,11 @@ def test_raise_wrong_exception_passes_by(): 1/0 """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["*3 passed*"]) - def test_does_not_raise(self, testdir): - testdir.makepyfile( + def test_does_not_raise(self, pytester: Pytester) -> None: + pytester.makepyfile( """ from contextlib import contextmanager import pytest @@ -100,11 +101,11 @@ def test_division(example_input, expectation): assert (6 / example_input) is not None """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["*4 passed*"]) - def test_does_not_raise_does_raise(self, testdir): - testdir.makepyfile( + def test_does_not_raise_does_raise(self, pytester: Pytester) -> None: + pytester.makepyfile( """ from contextlib import contextmanager import pytest @@ -123,7 +124,7 @@ def test_division(example_input, expectation): assert (6 / example_input) is not None """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["*2 failed*"]) def test_noclass(self) -> None: diff --git a/testing/python/show_fixtures_per_test.py b/testing/python/show_fixtures_per_test.py index ef841819d09..2a15132738d 100644 --- a/testing/python/show_fixtures_per_test.py +++ b/testing/python/show_fixtures_per_test.py @@ -1,11 +1,14 @@ -def test_no_items_should_not_show_output(testdir): - result = testdir.runpytest("--fixtures-per-test") +from _pytest.pytester import Pytester + + +def test_no_items_should_not_show_output(pytester: Pytester) -> None: + result = pytester.runpytest("--fixtures-per-test") result.stdout.no_fnmatch_line("*fixtures used by*") assert result.ret == 0 -def test_fixtures_in_module(testdir): - p = testdir.makepyfile( +def test_fixtures_in_module(pytester: Pytester) -> None: + p = pytester.makepyfile( ''' import pytest @pytest.fixture @@ -19,7 +22,7 @@ def test_arg1(arg1): ''' ) - result = testdir.runpytest("--fixtures-per-test", p) + result = pytester.runpytest("--fixtures-per-test", p) assert result.ret == 0 result.stdout.fnmatch_lines( @@ -33,8 +36,8 @@ def test_arg1(arg1): result.stdout.no_fnmatch_line("*_arg0*") -def test_fixtures_in_conftest(testdir): - testdir.makeconftest( +def test_fixtures_in_conftest(pytester: Pytester) -> None: + pytester.makeconftest( ''' import pytest @pytest.fixture @@ -50,7 +53,7 @@ def arg3(arg1, arg2): """ ''' ) - p = testdir.makepyfile( + p = pytester.makepyfile( """ def test_arg2(arg2): pass @@ -58,7 +61,7 @@ def test_arg3(arg3): pass """ ) - result = testdir.runpytest("--fixtures-per-test", p) + result = pytester.runpytest("--fixtures-per-test", p) assert result.ret == 0 result.stdout.fnmatch_lines( @@ -80,8 +83,8 @@ def test_arg3(arg3): ) -def test_should_show_fixtures_used_by_test(testdir): - testdir.makeconftest( +def test_should_show_fixtures_used_by_test(pytester: Pytester) -> None: + pytester.makeconftest( ''' import pytest @pytest.fixture @@ -92,7 +95,7 @@ def arg2(): """arg2 from conftest""" ''' ) - p = testdir.makepyfile( + p = pytester.makepyfile( ''' import pytest @pytest.fixture @@ -102,7 +105,7 @@ def test_args(arg1, arg2): pass ''' ) - result = testdir.runpytest("--fixtures-per-test", p) + result = pytester.runpytest("--fixtures-per-test", p) assert result.ret == 0 result.stdout.fnmatch_lines( @@ -117,8 +120,8 @@ def test_args(arg1, arg2): ) -def test_verbose_include_private_fixtures_and_loc(testdir): - testdir.makeconftest( +def test_verbose_include_private_fixtures_and_loc(pytester: Pytester) -> None: + pytester.makeconftest( ''' import pytest @pytest.fixture @@ -129,7 +132,7 @@ def arg2(_arg1): """arg2 from conftest""" ''' ) - p = testdir.makepyfile( + p = pytester.makepyfile( ''' import pytest @pytest.fixture @@ -139,7 +142,7 @@ def test_args(arg2, arg3): pass ''' ) - result = testdir.runpytest("--fixtures-per-test", "-v", p) + result = pytester.runpytest("--fixtures-per-test", "-v", p) assert result.ret == 0 result.stdout.fnmatch_lines( @@ -156,8 +159,8 @@ def test_args(arg2, arg3): ) -def test_doctest_items(testdir): - testdir.makepyfile( +def test_doctest_items(pytester: Pytester) -> None: + pytester.makepyfile( ''' def foo(): """ @@ -166,13 +169,13 @@ def foo(): """ ''' ) - testdir.maketxtfile( + pytester.maketxtfile( """ >>> 1 + 1 2 """ ) - result = testdir.runpytest( + result = pytester.runpytest( "--fixtures-per-test", "--doctest-modules", "--doctest-glob=*.txt", "-v" ) assert result.ret == 0 From 92b444a91494a51dfd4d8a127707e5ab4f42c517 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Dec 2020 03:07:27 +0000 Subject: [PATCH 006/630] build(deps): bump pytest-bdd in /testing/plugins_integration Bumps [pytest-bdd](https://github.com/pytest-dev/pytest-bdd) from 4.0.1 to 4.0.2. - [Release notes](https://github.com/pytest-dev/pytest-bdd/releases) - [Changelog](https://github.com/pytest-dev/pytest-bdd/blob/master/CHANGES.rst) - [Commits](https://github.com/pytest-dev/pytest-bdd/compare/4.0.1...4.0.2) Signed-off-by: dependabot[bot] --- testing/plugins_integration/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/plugins_integration/requirements.txt b/testing/plugins_integration/requirements.txt index d0ee9b571e2..19cc088db83 100644 --- a/testing/plugins_integration/requirements.txt +++ b/testing/plugins_integration/requirements.txt @@ -1,7 +1,7 @@ anyio[curio,trio]==2.0.2 django==3.1.4 pytest-asyncio==0.14.0 -pytest-bdd==4.0.1 +pytest-bdd==4.0.2 pytest-cov==2.10.1 pytest-django==4.1.0 pytest-flakes==4.0.3 From a09d8b15998b78d9e0bbad38879ccb1b00f95069 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Dec 2020 03:07:30 +0000 Subject: [PATCH 007/630] build(deps): bump pytest-html in /testing/plugins_integration Bumps [pytest-html](https://github.com/pytest-dev/pytest-html) from 3.1.0 to 3.1.1. - [Release notes](https://github.com/pytest-dev/pytest-html/releases) - [Changelog](https://github.com/pytest-dev/pytest-html/blob/master/CHANGES.rst) - [Commits](https://github.com/pytest-dev/pytest-html/compare/v3.1.0...v3.1.1) Signed-off-by: dependabot[bot] --- testing/plugins_integration/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/plugins_integration/requirements.txt b/testing/plugins_integration/requirements.txt index d0ee9b571e2..92a006723ef 100644 --- a/testing/plugins_integration/requirements.txt +++ b/testing/plugins_integration/requirements.txt @@ -5,7 +5,7 @@ pytest-bdd==4.0.1 pytest-cov==2.10.1 pytest-django==4.1.0 pytest-flakes==4.0.3 -pytest-html==3.1.0 +pytest-html==3.1.1 pytest-mock==3.3.1 pytest-rerunfailures==9.1.1 pytest-sugar==0.9.4 From 2cb34a99cbf423c50b7b6592a54f80f68bb9fdc0 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Mon, 14 Dec 2020 15:54:59 +0200 Subject: [PATCH 008/630] Some py.path.local -> pathlib.Path --- src/_pytest/assertion/rewrite.py | 5 ++- src/_pytest/config/argparsing.py | 15 +++++---- src/_pytest/fixtures.py | 15 ++++----- src/_pytest/main.py | 43 +++++++++++++------------ src/_pytest/monkeypatch.py | 13 ++------ src/_pytest/nodes.py | 10 ++++-- src/_pytest/pathlib.py | 6 ++-- testing/test_assertrewrite.py | 6 ++-- testing/test_main.py | 54 ++++++++++++++------------------ testing/test_nodes.py | 11 +++++-- 10 files changed, 84 insertions(+), 94 deletions(-) diff --git a/src/_pytest/assertion/rewrite.py b/src/_pytest/assertion/rewrite.py index 805d4c8b35b..a01be76b4d3 100644 --- a/src/_pytest/assertion/rewrite.py +++ b/src/_pytest/assertion/rewrite.py @@ -27,8 +27,6 @@ from typing import TYPE_CHECKING from typing import Union -import py - from _pytest._io.saferepr import saferepr from _pytest._version import version from _pytest.assertion import util @@ -37,6 +35,7 @@ ) from _pytest.config import Config from _pytest.main import Session +from _pytest.pathlib import absolutepath from _pytest.pathlib import fnmatch_ex from _pytest.store import StoreKey @@ -215,7 +214,7 @@ def _should_rewrite(self, name: str, fn: str, state: "AssertionState") -> bool: return True if self.session is not None: - if self.session.isinitpath(py.path.local(fn)): + if self.session.isinitpath(absolutepath(fn)): state.trace(f"matched test file (was specified on cmdline): {fn!r}") return True diff --git a/src/_pytest/config/argparsing.py b/src/_pytest/config/argparsing.py index 9a481965526..5a09ea781e6 100644 --- a/src/_pytest/config/argparsing.py +++ b/src/_pytest/config/argparsing.py @@ -1,4 +1,5 @@ import argparse +import os import sys import warnings from gettext import gettext @@ -14,8 +15,6 @@ from typing import TYPE_CHECKING from typing import Union -import py - import _pytest._io from _pytest.compat import final from _pytest.config.exceptions import UsageError @@ -97,14 +96,14 @@ def addoption(self, *opts: str, **attrs: Any) -> None: def parse( self, - args: Sequence[Union[str, py.path.local]], + args: Sequence[Union[str, "os.PathLike[str]"]], namespace: Optional[argparse.Namespace] = None, ) -> argparse.Namespace: from _pytest._argcomplete import try_argcomplete self.optparser = self._getparser() try_argcomplete(self.optparser) - strargs = [str(x) if isinstance(x, py.path.local) else x for x in args] + strargs = [os.fspath(x) for x in args] return self.optparser.parse_args(strargs, namespace=namespace) def _getparser(self) -> "MyOptionParser": @@ -128,7 +127,7 @@ def _getparser(self) -> "MyOptionParser": def parse_setoption( self, - args: Sequence[Union[str, py.path.local]], + args: Sequence[Union[str, "os.PathLike[str]"]], option: argparse.Namespace, namespace: Optional[argparse.Namespace] = None, ) -> List[str]: @@ -139,7 +138,7 @@ def parse_setoption( def parse_known_args( self, - args: Sequence[Union[str, py.path.local]], + args: Sequence[Union[str, "os.PathLike[str]"]], namespace: Optional[argparse.Namespace] = None, ) -> argparse.Namespace: """Parse and return a namespace object with known arguments at this point.""" @@ -147,13 +146,13 @@ def parse_known_args( def parse_known_and_unknown_args( self, - args: Sequence[Union[str, py.path.local]], + args: Sequence[Union[str, "os.PathLike[str]"]], namespace: Optional[argparse.Namespace] = None, ) -> Tuple[argparse.Namespace, List[str]]: """Parse and return a namespace object with known arguments, and the remaining arguments unknown at this point.""" optparser = self._getparser() - strargs = [str(x) if isinstance(x, py.path.local) else x for x in args] + strargs = [os.fspath(x) for x in args] return optparser.parse_known_args(strargs, namespace=namespace) def addini( diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 273bcafd393..c24ab7069cb 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -648,12 +648,13 @@ def _compute_fixture_value(self, fixturedef: "FixtureDef[object]") -> None: if has_params: frame = inspect.stack()[3] frameinfo = inspect.getframeinfo(frame[0]) - source_path = py.path.local(frameinfo.filename) + source_path = absolutepath(frameinfo.filename) source_lineno = frameinfo.lineno - rel_source_path = source_path.relto(funcitem.config.rootdir) - if rel_source_path: - source_path_str = rel_source_path - else: + try: + source_path_str = str( + source_path.relative_to(funcitem.config.rootpath) + ) + except ValueError: source_path_str = str(source_path) msg = ( "The requested fixture has no parameter defined for test:\n" @@ -876,7 +877,7 @@ def formatrepr(self) -> "FixtureLookupErrorRepr": class FixtureLookupErrorRepr(TerminalRepr): def __init__( self, - filename: Union[str, py.path.local], + filename: Union[str, "os.PathLike[str]"], firstlineno: int, tblines: Sequence[str], errorstring: str, @@ -903,7 +904,7 @@ def toterminal(self, tw: TerminalWriter) -> None: f"{FormattedExcinfo.flow_marker} {line.strip()}", red=True, ) tw.line() - tw.line("%s:%d" % (self.filename, self.firstlineno + 1)) + tw.line("%s:%d" % (os.fspath(self.filename), self.firstlineno + 1)) def fail_fixturefunc(fixturefunc, msg: str) -> "NoReturn": diff --git a/src/_pytest/main.py b/src/_pytest/main.py index eab3c9afd27..d536f9d8066 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -467,7 +467,7 @@ def __init__(self, config: Config) -> None: self.shouldfail: Union[bool, str] = False self.trace = config.trace.root.get("collection") self.startdir = config.invocation_dir - self._initialpaths: FrozenSet[py.path.local] = frozenset() + self._initialpaths: FrozenSet[Path] = frozenset() self._bestrelpathcache: Dict[Path, str] = _bestrelpath_cache(config.rootpath) @@ -510,8 +510,8 @@ def pytest_runtest_logreport( pytest_collectreport = pytest_runtest_logreport - def isinitpath(self, path: py.path.local) -> bool: - return path in self._initialpaths + def isinitpath(self, path: Union[str, "os.PathLike[str]"]) -> bool: + return Path(path) in self._initialpaths def gethookproxy(self, fspath: "os.PathLike[str]"): # Check if we have the common case of running @@ -601,14 +601,14 @@ def perform_collect( self.trace.root.indent += 1 self._notfound: List[Tuple[str, Sequence[nodes.Collector]]] = [] - self._initial_parts: List[Tuple[py.path.local, List[str]]] = [] + self._initial_parts: List[Tuple[Path, List[str]]] = [] self.items: List[nodes.Item] = [] hook = self.config.hook items: Sequence[Union[nodes.Item, nodes.Collector]] = self.items try: - initialpaths: List[py.path.local] = [] + initialpaths: List[Path] = [] for arg in args: fspath, parts = resolve_collection_argument( self.config.invocation_params.dir, @@ -669,13 +669,13 @@ def collect(self) -> Iterator[Union[nodes.Item, nodes.Collector]]: # No point in finding packages when collecting doctests. if not self.config.getoption("doctestmodules", False): pm = self.config.pluginmanager - confcutdir = py.path.local(pm._confcutdir) if pm._confcutdir else None - for parent in reversed(argpath.parts()): - if confcutdir and confcutdir.relto(parent): + confcutdir = pm._confcutdir + for parent in (argpath, *argpath.parents): + if confcutdir and parent in confcutdir.parents: break - if parent.isdir(): - pkginit = parent.join("__init__.py") + if parent.is_dir(): + pkginit = py.path.local(parent / "__init__.py") if pkginit.isfile() and pkginit not in node_cache1: col = self._collectfile(pkginit, handle_dupes=False) if col: @@ -685,7 +685,7 @@ def collect(self) -> Iterator[Union[nodes.Item, nodes.Collector]]: # If it's a directory argument, recurse and look for any Subpackages. # Let the Package collector deal with subnodes, don't collect here. - if argpath.check(dir=1): + if argpath.is_dir(): assert not names, "invalid arg {!r}".format((argpath, names)) seen_dirs: Set[py.path.local] = set() @@ -717,15 +717,16 @@ def collect(self) -> Iterator[Union[nodes.Item, nodes.Collector]]: node_cache2[key] = x yield x else: - assert argpath.check(file=1) + assert argpath.is_file() - if argpath in node_cache1: - col = node_cache1[argpath] + argpath_ = py.path.local(argpath) + if argpath_ in node_cache1: + col = node_cache1[argpath_] else: - collect_root = pkg_roots.get(argpath.dirname, self) - col = collect_root._collectfile(argpath, handle_dupes=False) + collect_root = pkg_roots.get(argpath_.dirname, self) + col = collect_root._collectfile(argpath_, handle_dupes=False) if col: - node_cache1[argpath] = col + node_cache1[argpath_] = col matching = [] work: List[ @@ -782,9 +783,7 @@ def collect(self) -> Iterator[Union[nodes.Item, nodes.Collector]]: # first yielded item will be the __init__ Module itself, so # just use that. If this special case isn't taken, then all the # files in the package will be yielded. - if argpath.basename == "__init__.py" and isinstance( - matching[0], Package - ): + if argpath.name == "__init__.py" and isinstance(matching[0], Package): try: yield next(iter(matching[0].collect())) except StopIteration: @@ -833,7 +832,7 @@ def search_pypath(module_name: str) -> str: def resolve_collection_argument( invocation_path: Path, arg: str, *, as_pypath: bool = False -) -> Tuple[py.path.local, List[str]]: +) -> Tuple[Path, List[str]]: """Parse path arguments optionally containing selection parts and return (fspath, names). Command-line arguments can point to files and/or directories, and optionally contain @@ -875,4 +874,4 @@ def resolve_collection_argument( else "directory argument cannot contain :: selection parts: {arg}" ) raise UsageError(msg.format(arg=arg)) - return py.path.local(str(fspath)), parts + return fspath, parts diff --git a/src/_pytest/monkeypatch.py b/src/_pytest/monkeypatch.py index a052f693ac0..d012b8a535a 100644 --- a/src/_pytest/monkeypatch.py +++ b/src/_pytest/monkeypatch.py @@ -4,7 +4,6 @@ import sys import warnings from contextlib import contextmanager -from pathlib import Path from typing import Any from typing import Generator from typing import List @@ -325,20 +324,14 @@ def syspath_prepend(self, path) -> None: invalidate_caches() - def chdir(self, path) -> None: + def chdir(self, path: Union[str, "os.PathLike[str]"]) -> None: """Change the current working directory to the specified path. - Path can be a string or a py.path.local object. + Path can be a string or a path object. """ if self._cwd is None: self._cwd = os.getcwd() - if hasattr(path, "chdir"): - path.chdir() - elif isinstance(path, Path): - # Modern python uses the fspath protocol here LEGACY - os.chdir(str(path)) - else: - os.chdir(path) + os.chdir(path) def undo(self) -> None: """Undo previous changes. diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py index 98bd581b96d..fee0770eb2b 100644 --- a/src/_pytest/nodes.py +++ b/src/_pytest/nodes.py @@ -480,10 +480,14 @@ def _prunetraceback(self, excinfo: ExceptionInfo[BaseException]) -> None: excinfo.traceback = ntraceback.filter() -def _check_initialpaths_for_relpath(session, fspath): +def _check_initialpaths_for_relpath( + session: "Session", fspath: py.path.local +) -> Optional[str]: for initial_path in session._initialpaths: - if fspath.common(initial_path) == initial_path: - return fspath.relto(initial_path) + initial_path_ = py.path.local(initial_path) + if fspath.common(initial_path_) == initial_path_: + return fspath.relto(initial_path_) + return None class FSCollector(Collector): diff --git a/src/_pytest/pathlib.py b/src/_pytest/pathlib.py index 8875a28f84b..2e452eb1cc9 100644 --- a/src/_pytest/pathlib.py +++ b/src/_pytest/pathlib.py @@ -30,8 +30,6 @@ from typing import TypeVar from typing import Union -import py - from _pytest.compat import assert_never from _pytest.outcomes import skip from _pytest.warning_types import PytestWarning @@ -456,7 +454,7 @@ class ImportPathMismatchError(ImportError): def import_path( - p: Union[str, py.path.local, Path], + p: Union[str, "os.PathLike[str]"], *, mode: Union[str, ImportMode] = ImportMode.prepend, ) -> ModuleType: @@ -482,7 +480,7 @@ def import_path( """ mode = ImportMode(mode) - path = Path(str(p)) + path = Path(p) if not path.exists(): raise ImportError(path) diff --git a/testing/test_assertrewrite.py b/testing/test_assertrewrite.py index 84d5276e729..ffe18260f90 100644 --- a/testing/test_assertrewrite.py +++ b/testing/test_assertrewrite.py @@ -17,8 +17,6 @@ from typing import Optional from typing import Set -import py - import _pytest._code import pytest from _pytest.assertion import util @@ -1311,7 +1309,7 @@ def hook( import importlib.machinery self.find_spec_calls: List[str] = [] - self.initial_paths: Set[py.path.local] = set() + self.initial_paths: Set[Path] = set() class StubSession: _initialpaths = self.initial_paths @@ -1346,7 +1344,7 @@ def fix(): return 1 pytester.makepyfile(test_foo="def test_foo(): pass") pytester.makepyfile(bar="def bar(): pass") foobar_path = pytester.makepyfile(foobar="def foobar(): pass") - self.initial_paths.add(py.path.local(foobar_path)) + self.initial_paths.add(foobar_path) # conftest files should always be rewritten assert hook.find_spec("conftest") is not None diff --git a/testing/test_main.py b/testing/test_main.py index 3e94668e82f..f45607abc30 100644 --- a/testing/test_main.py +++ b/testing/test_main.py @@ -4,13 +4,12 @@ from pathlib import Path from typing import Optional -import py.path - import pytest from _pytest.config import ExitCode from _pytest.config import UsageError from _pytest.main import resolve_collection_argument from _pytest.main import validate_basetemp +from _pytest.pytester import Pytester from _pytest.pytester import Testdir @@ -109,40 +108,37 @@ def test_validate_basetemp_integration(testdir): class TestResolveCollectionArgument: @pytest.fixture - def invocation_dir(self, testdir: Testdir) -> py.path.local: - testdir.syspathinsert(str(testdir.tmpdir / "src")) - testdir.chdir() - - pkg = testdir.tmpdir.join("src/pkg").ensure_dir() - pkg.join("__init__.py").ensure() - pkg.join("test.py").ensure() - return testdir.tmpdir + def invocation_path(self, pytester: Pytester) -> Path: + pytester.syspathinsert(pytester.path / "src") + pytester.chdir() - @pytest.fixture - def invocation_path(self, invocation_dir: py.path.local) -> Path: - return Path(str(invocation_dir)) + pkg = pytester.path.joinpath("src/pkg") + pkg.mkdir(parents=True) + pkg.joinpath("__init__.py").touch() + pkg.joinpath("test.py").touch() + return pytester.path - def test_file(self, invocation_dir: py.path.local, invocation_path: Path) -> None: + def test_file(self, invocation_path: Path) -> None: """File and parts.""" assert resolve_collection_argument(invocation_path, "src/pkg/test.py") == ( - invocation_dir / "src/pkg/test.py", + invocation_path / "src/pkg/test.py", [], ) assert resolve_collection_argument(invocation_path, "src/pkg/test.py::") == ( - invocation_dir / "src/pkg/test.py", + invocation_path / "src/pkg/test.py", [""], ) assert resolve_collection_argument( invocation_path, "src/pkg/test.py::foo::bar" - ) == (invocation_dir / "src/pkg/test.py", ["foo", "bar"]) + ) == (invocation_path / "src/pkg/test.py", ["foo", "bar"]) assert resolve_collection_argument( invocation_path, "src/pkg/test.py::foo::bar::" - ) == (invocation_dir / "src/pkg/test.py", ["foo", "bar", ""]) + ) == (invocation_path / "src/pkg/test.py", ["foo", "bar", ""]) - def test_dir(self, invocation_dir: py.path.local, invocation_path: Path) -> None: + def test_dir(self, invocation_path: Path) -> None: """Directory and parts.""" assert resolve_collection_argument(invocation_path, "src/pkg") == ( - invocation_dir / "src/pkg", + invocation_path / "src/pkg", [], ) @@ -156,16 +152,16 @@ def test_dir(self, invocation_dir: py.path.local, invocation_path: Path) -> None ): resolve_collection_argument(invocation_path, "src/pkg::foo::bar") - def test_pypath(self, invocation_dir: py.path.local, invocation_path: Path) -> None: + def test_pypath(self, invocation_path: Path) -> None: """Dotted name and parts.""" assert resolve_collection_argument( invocation_path, "pkg.test", as_pypath=True - ) == (invocation_dir / "src/pkg/test.py", []) + ) == (invocation_path / "src/pkg/test.py", []) assert resolve_collection_argument( invocation_path, "pkg.test::foo::bar", as_pypath=True - ) == (invocation_dir / "src/pkg/test.py", ["foo", "bar"]) + ) == (invocation_path / "src/pkg/test.py", ["foo", "bar"]) assert resolve_collection_argument(invocation_path, "pkg", as_pypath=True) == ( - invocation_dir / "src/pkg", + invocation_path / "src/pkg", [], ) @@ -191,13 +187,11 @@ def test_does_not_exist(self, invocation_path: Path) -> None: ): resolve_collection_argument(invocation_path, "foobar", as_pypath=True) - def test_absolute_paths_are_resolved_correctly( - self, invocation_dir: py.path.local, invocation_path: Path - ) -> None: + def test_absolute_paths_are_resolved_correctly(self, invocation_path: Path) -> None: """Absolute paths resolve back to absolute paths.""" - full_path = str(invocation_dir / "src") + full_path = str(invocation_path / "src") assert resolve_collection_argument(invocation_path, full_path) == ( - py.path.local(os.path.abspath("src")), + Path(os.path.abspath("src")), [], ) @@ -206,7 +200,7 @@ def test_absolute_paths_are_resolved_correctly( drive, full_path_without_drive = os.path.splitdrive(full_path) assert resolve_collection_argument( invocation_path, full_path_without_drive - ) == (py.path.local(os.path.abspath("src")), []) + ) == (Path(os.path.abspath("src")), []) def test_module_full_path_without_drive(testdir): diff --git a/testing/test_nodes.py b/testing/test_nodes.py index f3824c57090..bae31f0a39c 100644 --- a/testing/test_nodes.py +++ b/testing/test_nodes.py @@ -1,3 +1,4 @@ +from typing import cast from typing import List from typing import Type @@ -73,17 +74,21 @@ def test__check_initialpaths_for_relpath() -> None: class FakeSession1: _initialpaths = [cwd] - assert nodes._check_initialpaths_for_relpath(FakeSession1, cwd) == "" + session = cast(pytest.Session, FakeSession1) + + assert nodes._check_initialpaths_for_relpath(session, cwd) == "" sub = cwd.join("file") class FakeSession2: _initialpaths = [cwd] - assert nodes._check_initialpaths_for_relpath(FakeSession2, sub) == "file" + session = cast(pytest.Session, FakeSession2) + + assert nodes._check_initialpaths_for_relpath(session, sub) == "file" outside = py.path.local("/outside") - assert nodes._check_initialpaths_for_relpath(FakeSession2, outside) is None + assert nodes._check_initialpaths_for_relpath(session, outside) is None def test_failure_with_changed_cwd(pytester: Pytester) -> None: From 592b32bd69cb43aace8cd5525fa0b3712ee767be Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Mon, 14 Dec 2020 18:16:14 +0200 Subject: [PATCH 009/630] hookspec: add pathlib.Path alternatives to py.path.local parameters in hooks As part of the ongoing migration for py.path to pathlib, make sure all hooks which take a py.path.local also take an equivalent pathlib.Path. --- changelog/8144.feature.rst | 7 +++++++ src/_pytest/hookspec.py | 42 ++++++++++++++++++++++++++++++++------ src/_pytest/main.py | 14 ++++++++----- src/_pytest/python.py | 25 +++++++++++++++-------- src/_pytest/terminal.py | 7 +++++-- testing/test_terminal.py | 6 +++--- 6 files changed, 76 insertions(+), 25 deletions(-) create mode 100644 changelog/8144.feature.rst diff --git a/changelog/8144.feature.rst b/changelog/8144.feature.rst new file mode 100644 index 00000000000..01f40e21521 --- /dev/null +++ b/changelog/8144.feature.rst @@ -0,0 +1,7 @@ +The following hooks now receive an additional ``pathlib.Path`` argument, equivalent to an existing ``py.path.local`` argument: + +- :func:`pytest_ignore_collect <_pytest.hookspec.pytest_ignore_collect>` - The ``fspath`` parameter (equivalent to existing ``path`` parameter). +- :func:`pytest_collect_file <_pytest.hookspec.pytest_collect_file>` - The ``fspath`` parameter (equivalent to existing ``path`` parameter). +- :func:`pytest_pycollect_makemodule <_pytest.hookspec.pytest_pycollect_makemodule>` - The ``fspath`` parameter (equivalent to existing ``path`` parameter). +- :func:`pytest_report_header <_pytest.hookspec.pytest_report_header>` - The ``startpath`` parameter (equivalent to existing ``startdir`` parameter). +- :func:`pytest_report_collectionfinish <_pytest.hookspec.pytest_report_collectionfinish>` - The ``startpath`` parameter (equivalent to existing ``startdir`` parameter). diff --git a/src/_pytest/hookspec.py b/src/_pytest/hookspec.py index e499b742c7e..22bebf5b783 100644 --- a/src/_pytest/hookspec.py +++ b/src/_pytest/hookspec.py @@ -1,5 +1,6 @@ """Hook specifications for pytest plugins which are invoked by pytest itself and by builtin plugins.""" +from pathlib import Path from typing import Any from typing import Dict from typing import List @@ -261,7 +262,9 @@ def pytest_collection_finish(session: "Session") -> None: @hookspec(firstresult=True) -def pytest_ignore_collect(path: py.path.local, config: "Config") -> Optional[bool]: +def pytest_ignore_collect( + fspath: Path, path: py.path.local, config: "Config" +) -> Optional[bool]: """Return True to prevent considering this path for collection. This hook is consulted for all files and directories prior to calling @@ -269,19 +272,29 @@ def pytest_ignore_collect(path: py.path.local, config: "Config") -> Optional[boo Stops at first non-None result, see :ref:`firstresult`. + :param pathlib.Path fspath: The path to analyze. :param py.path.local path: The path to analyze. :param _pytest.config.Config config: The pytest config object. + + .. versionchanged:: 6.3.0 + The ``fspath`` parameter was added as a :class:`pathlib.Path` + equivalent of the ``path`` parameter. """ def pytest_collect_file( - path: py.path.local, parent: "Collector" + fspath: Path, path: py.path.local, parent: "Collector" ) -> "Optional[Collector]": """Create a Collector for the given path, or None if not relevant. The new node needs to have the specified ``parent`` as a parent. + :param pathlib.Path fspath: The path to analyze. :param py.path.local path: The path to collect. + + .. versionchanged:: 6.3.0 + The ``fspath`` parameter was added as a :class:`pathlib.Path` + equivalent of the ``path`` parameter. """ @@ -321,7 +334,9 @@ def pytest_make_collect_report(collector: "Collector") -> "Optional[CollectRepor @hookspec(firstresult=True) -def pytest_pycollect_makemodule(path: py.path.local, parent) -> Optional["Module"]: +def pytest_pycollect_makemodule( + fspath: Path, path: py.path.local, parent +) -> Optional["Module"]: """Return a Module collector or None for the given path. This hook will be called for each matching test module path. @@ -330,7 +345,12 @@ def pytest_pycollect_makemodule(path: py.path.local, parent) -> Optional["Module Stops at first non-None result, see :ref:`firstresult`. - :param py.path.local path: The path of module to collect. + :param pathlib.Path fspath: The path of the module to collect. + :param py.path.local path: The path of the module to collect. + + .. versionchanged:: 6.3.0 + The ``fspath`` parameter was added as a :class:`pathlib.Path` + equivalent of the ``path`` parameter. """ @@ -653,11 +673,12 @@ def pytest_assertion_pass(item: "Item", lineno: int, orig: str, expl: str) -> No def pytest_report_header( - config: "Config", startdir: py.path.local + config: "Config", startpath: Path, startdir: py.path.local ) -> Union[str, List[str]]: """Return a string or list of strings to be displayed as header info for terminal reporting. :param _pytest.config.Config config: The pytest config object. + :param Path startpath: The starting dir. :param py.path.local startdir: The starting dir. .. note:: @@ -672,11 +693,15 @@ def pytest_report_header( This function should be implemented only in plugins or ``conftest.py`` files situated at the tests root directory due to how pytest :ref:`discovers plugins during startup `. + + .. versionchanged:: 6.3.0 + The ``startpath`` parameter was added as a :class:`pathlib.Path` + equivalent of the ``startdir`` parameter. """ def pytest_report_collectionfinish( - config: "Config", startdir: py.path.local, items: Sequence["Item"], + config: "Config", startpath: Path, startdir: py.path.local, items: Sequence["Item"], ) -> Union[str, List[str]]: """Return a string or list of strings to be displayed after collection has finished successfully. @@ -686,6 +711,7 @@ def pytest_report_collectionfinish( .. versionadded:: 3.2 :param _pytest.config.Config config: The pytest config object. + :param Path startpath: The starting path. :param py.path.local startdir: The starting dir. :param items: List of pytest items that are going to be executed; this list should not be modified. @@ -695,6 +721,10 @@ def pytest_report_collectionfinish( ran before it. If you want to have your line(s) displayed first, use :ref:`trylast=True `. + + .. versionchanged:: 6.3.0 + The ``startpath`` parameter was added as a :class:`pathlib.Path` + equivalent of the ``startdir`` parameter. """ diff --git a/src/_pytest/main.py b/src/_pytest/main.py index d536f9d8066..e7c31ecc1d5 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -532,9 +532,10 @@ def gethookproxy(self, fspath: "os.PathLike[str]"): def _recurse(self, direntry: "os.DirEntry[str]") -> bool: if direntry.name == "__pycache__": return False - path = py.path.local(direntry.path) - ihook = self.gethookproxy(path.dirpath()) - if ihook.pytest_ignore_collect(path=path, config=self.config): + fspath = Path(direntry.path) + path = py.path.local(fspath) + ihook = self.gethookproxy(fspath.parent) + if ihook.pytest_ignore_collect(fspath=fspath, path=path, config=self.config): return False norecursepatterns = self.config.getini("norecursedirs") if any(path.check(fnmatch=pat) for pat in norecursepatterns): @@ -544,6 +545,7 @@ def _recurse(self, direntry: "os.DirEntry[str]") -> bool: def _collectfile( self, path: py.path.local, handle_dupes: bool = True ) -> Sequence[nodes.Collector]: + fspath = Path(path) assert ( path.isfile() ), "{!r} is not a file (isdir={!r}, exists={!r}, islink={!r})".format( @@ -551,7 +553,9 @@ def _collectfile( ) ihook = self.gethookproxy(path) if not self.isinitpath(path): - if ihook.pytest_ignore_collect(path=path, config=self.config): + if ihook.pytest_ignore_collect( + fspath=fspath, path=path, config=self.config + ): return () if handle_dupes: @@ -563,7 +567,7 @@ def _collectfile( else: duplicate_paths.add(path) - return ihook.pytest_collect_file(path=path, parent=self) # type: ignore[no-any-return] + return ihook.pytest_collect_file(fspath=fspath, path=path, parent=self) # type: ignore[no-any-return] @overload def perform_collect( diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 407f924a5f1..18e449b9361 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -10,6 +10,7 @@ from collections import Counter from collections import defaultdict from functools import partial +from pathlib import Path from typing import Any from typing import Callable from typing import Dict @@ -187,17 +188,19 @@ def pytest_pyfunc_call(pyfuncitem: "Function") -> Optional[object]: def pytest_collect_file( - path: py.path.local, parent: nodes.Collector + fspath: Path, path: py.path.local, parent: nodes.Collector ) -> Optional["Module"]: ext = path.ext if ext == ".py": - if not parent.session.isinitpath(path): + if not parent.session.isinitpath(fspath): if not path_matches_patterns( path, parent.config.getini("python_files") + ["__init__.py"] ): return None - ihook = parent.session.gethookproxy(path) - module: Module = ihook.pytest_pycollect_makemodule(path=path, parent=parent) + ihook = parent.session.gethookproxy(fspath) + module: Module = ihook.pytest_pycollect_makemodule( + fspath=fspath, path=path, parent=parent + ) return module return None @@ -664,9 +667,10 @@ def isinitpath(self, path: py.path.local) -> bool: def _recurse(self, direntry: "os.DirEntry[str]") -> bool: if direntry.name == "__pycache__": return False - path = py.path.local(direntry.path) - ihook = self.session.gethookproxy(path.dirpath()) - if ihook.pytest_ignore_collect(path=path, config=self.config): + fspath = Path(direntry.path) + path = py.path.local(fspath) + ihook = self.session.gethookproxy(fspath.parent) + if ihook.pytest_ignore_collect(fspath=fspath, path=path, config=self.config): return False norecursepatterns = self.config.getini("norecursedirs") if any(path.check(fnmatch=pat) for pat in norecursepatterns): @@ -676,6 +680,7 @@ def _recurse(self, direntry: "os.DirEntry[str]") -> bool: def _collectfile( self, path: py.path.local, handle_dupes: bool = True ) -> Sequence[nodes.Collector]: + fspath = Path(path) assert ( path.isfile() ), "{!r} is not a file (isdir={!r}, exists={!r}, islink={!r})".format( @@ -683,7 +688,9 @@ def _collectfile( ) ihook = self.session.gethookproxy(path) if not self.session.isinitpath(path): - if ihook.pytest_ignore_collect(path=path, config=self.config): + if ihook.pytest_ignore_collect( + fspath=fspath, path=path, config=self.config + ): return () if handle_dupes: @@ -695,7 +702,7 @@ def _collectfile( else: duplicate_paths.add(path) - return ihook.pytest_collect_file(path=path, parent=self) # type: ignore[no-any-return] + return ihook.pytest_collect_file(fspath=fspath, path=path, parent=self) # type: ignore[no-any-return] def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]: this_path = self.fspath.dirpath() diff --git a/src/_pytest/terminal.py b/src/_pytest/terminal.py index 0e0ed70e5be..39adfaaa310 100644 --- a/src/_pytest/terminal.py +++ b/src/_pytest/terminal.py @@ -710,7 +710,7 @@ def pytest_sessionstart(self, session: "Session") -> None: msg += " -- " + str(sys.executable) self.write_line(msg) lines = self.config.hook.pytest_report_header( - config=self.config, startdir=self.startdir + config=self.config, startpath=self.startpath, startdir=self.startdir ) self._write_report_lines_from_hooks(lines) @@ -745,7 +745,10 @@ def pytest_collection_finish(self, session: "Session") -> None: self.report_collect(True) lines = self.config.hook.pytest_report_collectionfinish( - config=self.config, startdir=self.startdir, items=session.items + config=self.config, + startpath=self.startpath, + startdir=self.startdir, + items=session.items, ) self._write_report_lines_from_hooks(lines) diff --git a/testing/test_terminal.py b/testing/test_terminal.py index 7ad5849d4b9..6319188a75e 100644 --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -1010,7 +1010,7 @@ def test_more_quiet_reporting(self, pytester: Pytester) -> None: def test_report_collectionfinish_hook(self, pytester: Pytester, params) -> None: pytester.makeconftest( """ - def pytest_report_collectionfinish(config, startdir, items): + def pytest_report_collectionfinish(config, startpath, startdir, items): return ['hello from hook: {0} items'.format(len(items))] """ ) @@ -1436,8 +1436,8 @@ def pytest_report_header(config): ) pytester.mkdir("a").joinpath("conftest.py").write_text( """ -def pytest_report_header(config, startdir): - return ["line1", str(startdir)] +def pytest_report_header(config, startdir, startpath): + return ["line1", str(startpath)] """ ) result = pytester.runpytest("a") From 8eef8c6004af955f1074905e9656480eeeb3bb67 Mon Sep 17 00:00:00 2001 From: Anton <44246099+antonblr@users.noreply.github.com> Date: Tue, 15 Dec 2020 03:02:32 -0800 Subject: [PATCH 010/630] tests: Migrate to pytester - incremental update (#8145) --- testing/code/test_excinfo.py | 57 ++-- testing/examples/test_issue519.py | 9 +- testing/io/test_terminalwriter.py | 5 +- testing/logging/test_fixture.py | 50 +-- testing/logging/test_reporting.py | 275 +++++++-------- testing/test_cacheprovider.py | 533 ++++++++++++++++-------------- testing/test_conftest.py | 21 +- testing/test_monkeypatch.py | 68 ++-- testing/test_pluginmanager.py | 131 +++++--- testing/test_reports.py | 102 +++--- testing/test_runner.py | 253 +++++++------- 11 files changed, 801 insertions(+), 703 deletions(-) diff --git a/testing/code/test_excinfo.py b/testing/code/test_excinfo.py index 5b9e3eda529..44d7ab549e8 100644 --- a/testing/code/test_excinfo.py +++ b/testing/code/test_excinfo.py @@ -5,6 +5,7 @@ import queue import sys import textwrap +from pathlib import Path from typing import Any from typing import Dict from typing import Tuple @@ -19,7 +20,10 @@ from _pytest._code.code import ExceptionInfo from _pytest._code.code import FormattedExcinfo from _pytest._io import TerminalWriter +from _pytest.pathlib import import_path from _pytest.pytester import LineMatcher +from _pytest.pytester import Pytester + if TYPE_CHECKING: from _pytest._code.code import _TracebackStyle @@ -155,10 +159,10 @@ def test_traceback_cut(self): newtraceback = traceback.cut(path=path, lineno=firstlineno + 2) assert len(newtraceback) == 1 - def test_traceback_cut_excludepath(self, testdir): - p = testdir.makepyfile("def f(): raise ValueError") + def test_traceback_cut_excludepath(self, pytester: Pytester) -> None: + p = pytester.makepyfile("def f(): raise ValueError") with pytest.raises(ValueError) as excinfo: - p.pyimport().f() + import_path(p).f() # type: ignore[attr-defined] basedir = py.path.local(pytest.__file__).dirpath() newtraceback = excinfo.traceback.cut(excludepath=basedir) for x in newtraceback: @@ -406,8 +410,8 @@ def test_match_succeeds(): excinfo.match(r".*zero.*") -def test_match_raises_error(testdir): - testdir.makepyfile( +def test_match_raises_error(pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest def test_division_zero(): @@ -416,14 +420,14 @@ def test_division_zero(): excinfo.match(r'[123]+') """ ) - result = testdir.runpytest() + result = pytester.runpytest() assert result.ret != 0 exc_msg = "Regex pattern '[[]123[]]+' does not match 'division by zero'." result.stdout.fnmatch_lines([f"E * AssertionError: {exc_msg}"]) result.stdout.no_fnmatch_line("*__tracebackhide__ = True*") - result = testdir.runpytest("--fulltrace") + result = pytester.runpytest("--fulltrace") assert result.ret != 0 result.stdout.fnmatch_lines( ["*__tracebackhide__ = True*", f"E * AssertionError: {exc_msg}"] @@ -432,15 +436,14 @@ def test_division_zero(): class TestFormattedExcinfo: @pytest.fixture - def importasmod(self, request, _sys_snapshot): + def importasmod(self, tmp_path: Path, _sys_snapshot): def importasmod(source): source = textwrap.dedent(source) - tmpdir = request.getfixturevalue("tmpdir") - modpath = tmpdir.join("mod.py") - tmpdir.ensure("__init__.py") - modpath.write(source) + modpath = tmp_path.joinpath("mod.py") + tmp_path.joinpath("__init__.py").touch() + modpath.write_text(source) importlib.invalidate_caches() - return modpath.pyimport() + return import_path(modpath) return importasmod @@ -682,7 +685,7 @@ def entry(): p = FormattedExcinfo(style="short") reprtb = p.repr_traceback_entry(excinfo.traceback[-2]) lines = reprtb.lines - basename = py.path.local(mod.__file__).basename + basename = Path(mod.__file__).name assert lines[0] == " func1()" assert reprtb.reprfileloc is not None assert basename in str(reprtb.reprfileloc.path) @@ -948,7 +951,9 @@ def f(): assert line.endswith("mod.py") assert tw_mock.lines[12] == ":3: ValueError" - def test_toterminal_long_missing_source(self, importasmod, tmpdir, tw_mock): + def test_toterminal_long_missing_source( + self, importasmod, tmp_path: Path, tw_mock + ) -> None: mod = importasmod( """ def g(x): @@ -958,7 +963,7 @@ def f(): """ ) excinfo = pytest.raises(ValueError, mod.f) - tmpdir.join("mod.py").remove() + tmp_path.joinpath("mod.py").unlink() excinfo.traceback = excinfo.traceback.filter() repr = excinfo.getrepr() repr.toterminal(tw_mock) @@ -978,7 +983,9 @@ def f(): assert line.endswith("mod.py") assert tw_mock.lines[10] == ":3: ValueError" - def test_toterminal_long_incomplete_source(self, importasmod, tmpdir, tw_mock): + def test_toterminal_long_incomplete_source( + self, importasmod, tmp_path: Path, tw_mock + ) -> None: mod = importasmod( """ def g(x): @@ -988,7 +995,7 @@ def f(): """ ) excinfo = pytest.raises(ValueError, mod.f) - tmpdir.join("mod.py").write("asdf") + tmp_path.joinpath("mod.py").write_text("asdf") excinfo.traceback = excinfo.traceback.filter() repr = excinfo.getrepr() repr.toterminal(tw_mock) @@ -1374,16 +1381,18 @@ def test_repr_traceback_with_unicode(style, encoding): assert repr_traceback is not None -def test_cwd_deleted(testdir): - testdir.makepyfile( +def test_cwd_deleted(pytester: Pytester) -> None: + pytester.makepyfile( """ - def test(tmpdir): - tmpdir.chdir() - tmpdir.remove() + import os + + def test(tmp_path): + os.chdir(tmp_path) + tmp_path.unlink() assert False """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["* 1 failed in *"]) result.stdout.no_fnmatch_line("*INTERNALERROR*") result.stderr.no_fnmatch_line("*INTERNALERROR*") diff --git a/testing/examples/test_issue519.py b/testing/examples/test_issue519.py index e83f18fdc93..85ba545e671 100644 --- a/testing/examples/test_issue519.py +++ b/testing/examples/test_issue519.py @@ -1,3 +1,6 @@ -def test_510(testdir): - testdir.copy_example("issue_519.py") - testdir.runpytest("issue_519.py") +from _pytest.pytester import Pytester + + +def test_510(pytester: Pytester) -> None: + pytester.copy_example("issue_519.py") + pytester.runpytest("issue_519.py") diff --git a/testing/io/test_terminalwriter.py b/testing/io/test_terminalwriter.py index db0ccf06a40..fac7593eadd 100644 --- a/testing/io/test_terminalwriter.py +++ b/testing/io/test_terminalwriter.py @@ -3,6 +3,7 @@ import re import shutil import sys +from pathlib import Path from typing import Generator from unittest import mock @@ -64,10 +65,10 @@ def test_terminalwriter_not_unicode() -> None: class TestTerminalWriter: @pytest.fixture(params=["path", "stringio"]) def tw( - self, request, tmpdir + self, request, tmp_path: Path ) -> Generator[terminalwriter.TerminalWriter, None, None]: if request.param == "path": - p = tmpdir.join("tmpfile") + p = tmp_path.joinpath("tmpfile") f = open(str(p), "w+", encoding="utf8") tw = terminalwriter.TerminalWriter(f) diff --git a/testing/logging/test_fixture.py b/testing/logging/test_fixture.py index ffd51bcad7a..f82df19715b 100644 --- a/testing/logging/test_fixture.py +++ b/testing/logging/test_fixture.py @@ -2,14 +2,14 @@ import pytest from _pytest.logging import caplog_records_key -from _pytest.pytester import Testdir +from _pytest.pytester import Pytester logger = logging.getLogger(__name__) sublogger = logging.getLogger(__name__ + ".baz") -def test_fixture_help(testdir): - result = testdir.runpytest("--fixtures") +def test_fixture_help(pytester: Pytester) -> None: + result = pytester.runpytest("--fixtures") result.stdout.fnmatch_lines(["*caplog*"]) @@ -28,12 +28,12 @@ def test_change_level(caplog): assert "CRITICAL" in caplog.text -def test_change_level_undo(testdir: Testdir) -> None: +def test_change_level_undo(pytester: Pytester) -> None: """Ensure that 'set_level' is undone after the end of the test. Tests the logging output themselves (affacted both by logger and handler levels). """ - testdir.makepyfile( + pytester.makepyfile( """ import logging @@ -49,17 +49,17 @@ def test2(caplog): assert 0 """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["*log from test1*", "*2 failed in *"]) result.stdout.no_fnmatch_line("*log from test2*") -def test_change_level_undos_handler_level(testdir: Testdir) -> None: +def test_change_level_undos_handler_level(pytester: Pytester) -> None: """Ensure that 'set_level' is undone after the end of the test (handler). Issue #7569. Tests the handler level specifically. """ - testdir.makepyfile( + pytester.makepyfile( """ import logging @@ -78,7 +78,7 @@ def test3(caplog): assert caplog.handler.level == 43 """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.assert_outcomes(passed=3) @@ -172,8 +172,8 @@ def test_caplog_captures_for_all_stages(caplog, logging_during_setup_and_teardow assert set(caplog._item._store[caplog_records_key]) == {"setup", "call"} -def test_ini_controls_global_log_level(testdir): - testdir.makepyfile( +def test_ini_controls_global_log_level(pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest import logging @@ -187,20 +187,20 @@ def test_log_level_override(request, caplog): assert 'ERROR' in caplog.text """ ) - testdir.makeini( + pytester.makeini( """ [pytest] log_level=ERROR """ ) - result = testdir.runpytest() + result = pytester.runpytest() # make sure that that we get a '0' exit code for the testsuite assert result.ret == 0 -def test_caplog_can_override_global_log_level(testdir): - testdir.makepyfile( +def test_caplog_can_override_global_log_level(pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest import logging @@ -227,19 +227,19 @@ def test_log_level_override(request, caplog): assert "message won't be shown" not in caplog.text """ ) - testdir.makeini( + pytester.makeini( """ [pytest] log_level=WARNING """ ) - result = testdir.runpytest() + result = pytester.runpytest() assert result.ret == 0 -def test_caplog_captures_despite_exception(testdir): - testdir.makepyfile( +def test_caplog_captures_despite_exception(pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest import logging @@ -255,26 +255,28 @@ def test_log_level_override(request, caplog): raise Exception() """ ) - testdir.makeini( + pytester.makeini( """ [pytest] log_level=WARNING """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["*ERROR message will be shown*"]) result.stdout.no_fnmatch_line("*DEBUG message won't be shown*") assert result.ret == 1 -def test_log_report_captures_according_to_config_option_upon_failure(testdir): +def test_log_report_captures_according_to_config_option_upon_failure( + pytester: Pytester, +) -> None: """Test that upon failure: (1) `caplog` succeeded to capture the DEBUG message and assert on it => No `Exception` is raised. (2) The `DEBUG` message does NOT appear in the `Captured log call` report. (3) The stdout, `INFO`, and `WARNING` messages DO appear in the test reports due to `--log-level=INFO`. """ - testdir.makepyfile( + pytester.makepyfile( """ import pytest import logging @@ -299,7 +301,7 @@ def test_that_fails(request, caplog): """ ) - result = testdir.runpytest("--log-level=INFO") + result = pytester.runpytest("--log-level=INFO") result.stdout.no_fnmatch_line("*Exception: caplog failed to capture DEBUG*") result.stdout.no_fnmatch_line("*DEBUG log message*") result.stdout.fnmatch_lines( diff --git a/testing/logging/test_reporting.py b/testing/logging/test_reporting.py index fc9f1082346..a5ab8b98ba7 100644 --- a/testing/logging/test_reporting.py +++ b/testing/logging/test_reporting.py @@ -6,12 +6,13 @@ import pytest from _pytest.capture import CaptureManager from _pytest.config import ExitCode -from _pytest.pytester import Testdir +from _pytest.fixtures import FixtureRequest +from _pytest.pytester import Pytester from _pytest.terminal import TerminalReporter -def test_nothing_logged(testdir): - testdir.makepyfile( +def test_nothing_logged(pytester: Pytester) -> None: + pytester.makepyfile( """ import sys @@ -21,7 +22,7 @@ def test_foo(): assert False """ ) - result = testdir.runpytest() + result = pytester.runpytest() assert result.ret == 1 result.stdout.fnmatch_lines(["*- Captured stdout call -*", "text going to stdout"]) result.stdout.fnmatch_lines(["*- Captured stderr call -*", "text going to stderr"]) @@ -29,8 +30,8 @@ def test_foo(): result.stdout.fnmatch_lines(["*- Captured *log call -*"]) -def test_messages_logged(testdir): - testdir.makepyfile( +def test_messages_logged(pytester: Pytester) -> None: + pytester.makepyfile( """ import sys import logging @@ -44,15 +45,15 @@ def test_foo(): assert False """ ) - result = testdir.runpytest("--log-level=INFO") + result = pytester.runpytest("--log-level=INFO") assert result.ret == 1 result.stdout.fnmatch_lines(["*- Captured *log call -*", "*text going to logger*"]) result.stdout.fnmatch_lines(["*- Captured stdout call -*", "text going to stdout"]) result.stdout.fnmatch_lines(["*- Captured stderr call -*", "text going to stderr"]) -def test_root_logger_affected(testdir): - testdir.makepyfile( +def test_root_logger_affected(pytester: Pytester) -> None: + pytester.makepyfile( """ import logging logger = logging.getLogger() @@ -65,8 +66,8 @@ def test_foo(): assert 0 """ ) - log_file = testdir.tmpdir.join("pytest.log").strpath - result = testdir.runpytest("--log-level=ERROR", "--log-file=pytest.log") + log_file = str(pytester.path.joinpath("pytest.log")) + result = pytester.runpytest("--log-level=ERROR", "--log-file=pytest.log") assert result.ret == 1 # The capture log calls in the stdout section only contain the @@ -87,8 +88,8 @@ def test_foo(): assert "error text going to logger" in contents -def test_log_cli_level_log_level_interaction(testdir): - testdir.makepyfile( +def test_log_cli_level_log_level_interaction(pytester: Pytester) -> None: + pytester.makepyfile( """ import logging logger = logging.getLogger() @@ -102,7 +103,7 @@ def test_foo(): """ ) - result = testdir.runpytest("--log-cli-level=INFO", "--log-level=ERROR") + result = pytester.runpytest("--log-cli-level=INFO", "--log-level=ERROR") assert result.ret == 1 result.stdout.fnmatch_lines( @@ -117,8 +118,8 @@ def test_foo(): result.stdout.no_re_match_line("DEBUG") -def test_setup_logging(testdir): - testdir.makepyfile( +def test_setup_logging(pytester: Pytester) -> None: + pytester.makepyfile( """ import logging @@ -132,7 +133,7 @@ def test_foo(): assert False """ ) - result = testdir.runpytest("--log-level=INFO") + result = pytester.runpytest("--log-level=INFO") assert result.ret == 1 result.stdout.fnmatch_lines( [ @@ -144,8 +145,8 @@ def test_foo(): ) -def test_teardown_logging(testdir): - testdir.makepyfile( +def test_teardown_logging(pytester: Pytester) -> None: + pytester.makepyfile( """ import logging @@ -159,7 +160,7 @@ def teardown_function(function): assert False """ ) - result = testdir.runpytest("--log-level=INFO") + result = pytester.runpytest("--log-level=INFO") assert result.ret == 1 result.stdout.fnmatch_lines( [ @@ -172,9 +173,9 @@ def teardown_function(function): @pytest.mark.parametrize("enabled", [True, False]) -def test_log_cli_enabled_disabled(testdir, enabled): +def test_log_cli_enabled_disabled(pytester: Pytester, enabled: bool) -> None: msg = "critical message logged by test" - testdir.makepyfile( + pytester.makepyfile( """ import logging def test_log_cli(): @@ -184,13 +185,13 @@ def test_log_cli(): ) ) if enabled: - testdir.makeini( + pytester.makeini( """ [pytest] log_cli=true """ ) - result = testdir.runpytest() + result = pytester.runpytest() if enabled: result.stdout.fnmatch_lines( [ @@ -204,9 +205,9 @@ def test_log_cli(): assert msg not in result.stdout.str() -def test_log_cli_default_level(testdir): +def test_log_cli_default_level(pytester: Pytester) -> None: # Default log file level - testdir.makepyfile( + pytester.makepyfile( """ import pytest import logging @@ -217,14 +218,14 @@ def test_log_cli(request): logging.getLogger('catchlog').warning("WARNING message will be shown") """ ) - testdir.makeini( + pytester.makeini( """ [pytest] log_cli=true """ ) - result = testdir.runpytest() + result = pytester.runpytest() # fnmatch_lines does an assertion internally result.stdout.fnmatch_lines( @@ -238,10 +239,12 @@ def test_log_cli(request): assert result.ret == 0 -def test_log_cli_default_level_multiple_tests(testdir, request): +def test_log_cli_default_level_multiple_tests( + pytester: Pytester, request: FixtureRequest +) -> None: """Ensure we reset the first newline added by the live logger between tests""" filename = request.node.name + ".py" - testdir.makepyfile( + pytester.makepyfile( """ import logging @@ -252,14 +255,14 @@ def test_log_2(): logging.warning("log message from test_log_2") """ ) - testdir.makeini( + pytester.makeini( """ [pytest] log_cli=true """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines( [ f"{filename}::test_log_1 ", @@ -273,11 +276,13 @@ def test_log_2(): ) -def test_log_cli_default_level_sections(testdir, request): +def test_log_cli_default_level_sections( + pytester: Pytester, request: FixtureRequest +) -> None: """Check that with live logging enable we are printing the correct headers during start/setup/call/teardown/finish.""" filename = request.node.name + ".py" - testdir.makeconftest( + pytester.makeconftest( """ import pytest import logging @@ -290,7 +295,7 @@ def pytest_runtest_logfinish(): """ ) - testdir.makepyfile( + pytester.makepyfile( """ import pytest import logging @@ -308,14 +313,14 @@ def test_log_2(fix): logging.warning("log message from test_log_2") """ ) - testdir.makeini( + pytester.makeini( """ [pytest] log_cli=true """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines( [ f"{filename}::test_log_1 ", @@ -347,11 +352,13 @@ def test_log_2(fix): ) -def test_live_logs_unknown_sections(testdir, request): +def test_live_logs_unknown_sections( + pytester: Pytester, request: FixtureRequest +) -> None: """Check that with live logging enable we are printing the correct headers during start/setup/call/teardown/finish.""" filename = request.node.name + ".py" - testdir.makeconftest( + pytester.makeconftest( """ import pytest import logging @@ -367,7 +374,7 @@ def pytest_runtest_logfinish(): """ ) - testdir.makepyfile( + pytester.makepyfile( """ import pytest import logging @@ -383,14 +390,14 @@ def test_log_1(fix): """ ) - testdir.makeini( + pytester.makeini( """ [pytest] log_cli=true """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines( [ "*WARNING*Unknown Section*", @@ -409,11 +416,13 @@ def test_log_1(fix): ) -def test_sections_single_new_line_after_test_outcome(testdir, request): +def test_sections_single_new_line_after_test_outcome( + pytester: Pytester, request: FixtureRequest +) -> None: """Check that only a single new line is written between log messages during teardown/finish.""" filename = request.node.name + ".py" - testdir.makeconftest( + pytester.makeconftest( """ import pytest import logging @@ -427,7 +436,7 @@ def pytest_runtest_logfinish(): """ ) - testdir.makepyfile( + pytester.makepyfile( """ import pytest import logging @@ -443,14 +452,14 @@ def test_log_1(fix): logging.warning("log message from test_log_1") """ ) - testdir.makeini( + pytester.makeini( """ [pytest] log_cli=true """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines( [ f"{filename}::test_log_1 ", @@ -487,9 +496,9 @@ def test_log_1(fix): ) -def test_log_cli_level(testdir): +def test_log_cli_level(pytester: Pytester) -> None: # Default log file level - testdir.makepyfile( + pytester.makepyfile( """ import pytest import logging @@ -501,14 +510,14 @@ def test_log_cli(request): print('PASSED') """ ) - testdir.makeini( + pytester.makeini( """ [pytest] log_cli=true """ ) - result = testdir.runpytest("-s", "--log-cli-level=INFO") + result = pytester.runpytest("-s", "--log-cli-level=INFO") # fnmatch_lines does an assertion internally result.stdout.fnmatch_lines( @@ -522,7 +531,7 @@ def test_log_cli(request): # make sure that that we get a '0' exit code for the testsuite assert result.ret == 0 - result = testdir.runpytest("-s", "--log-level=INFO") + result = pytester.runpytest("-s", "--log-level=INFO") # fnmatch_lines does an assertion internally result.stdout.fnmatch_lines( @@ -537,15 +546,15 @@ def test_log_cli(request): assert result.ret == 0 -def test_log_cli_ini_level(testdir): - testdir.makeini( +def test_log_cli_ini_level(pytester: Pytester) -> None: + pytester.makeini( """ [pytest] log_cli=true log_cli_level = INFO """ ) - testdir.makepyfile( + pytester.makepyfile( """ import pytest import logging @@ -558,7 +567,7 @@ def test_log_cli(request): """ ) - result = testdir.runpytest("-s") + result = pytester.runpytest("-s") # fnmatch_lines does an assertion internally result.stdout.fnmatch_lines( @@ -577,11 +586,11 @@ def test_log_cli(request): "cli_args", ["", "--log-level=WARNING", "--log-file-level=WARNING", "--log-cli-level=WARNING"], ) -def test_log_cli_auto_enable(testdir, cli_args): +def test_log_cli_auto_enable(pytester: Pytester, cli_args: str) -> None: """Check that live logs are enabled if --log-level or --log-cli-level is passed on the CLI. It should not be auto enabled if the same configs are set on the INI file. """ - testdir.makepyfile( + pytester.makepyfile( """ import logging @@ -591,7 +600,7 @@ def test_log_1(): """ ) - testdir.makeini( + pytester.makeini( """ [pytest] log_level=INFO @@ -599,7 +608,7 @@ def test_log_1(): """ ) - result = testdir.runpytest(cli_args) + result = pytester.runpytest(cli_args) stdout = result.stdout.str() if cli_args == "--log-cli-level=WARNING": result.stdout.fnmatch_lines( @@ -620,9 +629,9 @@ def test_log_1(): assert "WARNING" not in stdout -def test_log_file_cli(testdir): +def test_log_file_cli(pytester: Pytester) -> None: # Default log file level - testdir.makepyfile( + pytester.makepyfile( """ import pytest import logging @@ -635,9 +644,9 @@ def test_log_file(request): """ ) - log_file = testdir.tmpdir.join("pytest.log").strpath + log_file = str(pytester.path.joinpath("pytest.log")) - result = testdir.runpytest( + result = pytester.runpytest( "-s", f"--log-file={log_file}", "--log-file-level=WARNING" ) @@ -653,9 +662,9 @@ def test_log_file(request): assert "This log message won't be shown" not in contents -def test_log_file_cli_level(testdir): +def test_log_file_cli_level(pytester: Pytester) -> None: # Default log file level - testdir.makepyfile( + pytester.makepyfile( """ import pytest import logging @@ -668,9 +677,9 @@ def test_log_file(request): """ ) - log_file = testdir.tmpdir.join("pytest.log").strpath + log_file = str(pytester.path.joinpath("pytest.log")) - result = testdir.runpytest("-s", f"--log-file={log_file}", "--log-file-level=INFO") + result = pytester.runpytest("-s", f"--log-file={log_file}", "--log-file-level=INFO") # fnmatch_lines does an assertion internally result.stdout.fnmatch_lines(["test_log_file_cli_level.py PASSED"]) @@ -684,22 +693,22 @@ def test_log_file(request): assert "This log message won't be shown" not in contents -def test_log_level_not_changed_by_default(testdir): - testdir.makepyfile( +def test_log_level_not_changed_by_default(pytester: Pytester) -> None: + pytester.makepyfile( """ import logging def test_log_file(): assert logging.getLogger().level == logging.WARNING """ ) - result = testdir.runpytest("-s") + result = pytester.runpytest("-s") result.stdout.fnmatch_lines(["* 1 passed in *"]) -def test_log_file_ini(testdir): - log_file = testdir.tmpdir.join("pytest.log").strpath +def test_log_file_ini(pytester: Pytester) -> None: + log_file = str(pytester.path.joinpath("pytest.log")) - testdir.makeini( + pytester.makeini( """ [pytest] log_file={} @@ -708,7 +717,7 @@ def test_log_file_ini(testdir): log_file ) ) - testdir.makepyfile( + pytester.makepyfile( """ import pytest import logging @@ -721,7 +730,7 @@ def test_log_file(request): """ ) - result = testdir.runpytest("-s") + result = pytester.runpytest("-s") # fnmatch_lines does an assertion internally result.stdout.fnmatch_lines(["test_log_file_ini.py PASSED"]) @@ -735,10 +744,10 @@ def test_log_file(request): assert "This log message won't be shown" not in contents -def test_log_file_ini_level(testdir): - log_file = testdir.tmpdir.join("pytest.log").strpath +def test_log_file_ini_level(pytester: Pytester) -> None: + log_file = str(pytester.path.joinpath("pytest.log")) - testdir.makeini( + pytester.makeini( """ [pytest] log_file={} @@ -747,7 +756,7 @@ def test_log_file_ini_level(testdir): log_file ) ) - testdir.makepyfile( + pytester.makepyfile( """ import pytest import logging @@ -760,7 +769,7 @@ def test_log_file(request): """ ) - result = testdir.runpytest("-s") + result = pytester.runpytest("-s") # fnmatch_lines does an assertion internally result.stdout.fnmatch_lines(["test_log_file_ini_level.py PASSED"]) @@ -774,10 +783,10 @@ def test_log_file(request): assert "This log message won't be shown" not in contents -def test_log_file_unicode(testdir): - log_file = testdir.tmpdir.join("pytest.log").strpath +def test_log_file_unicode(pytester: Pytester) -> None: + log_file = str(pytester.path.joinpath("pytest.log")) - testdir.makeini( + pytester.makeini( """ [pytest] log_file={} @@ -786,7 +795,7 @@ def test_log_file_unicode(testdir): log_file ) ) - testdir.makepyfile( + pytester.makepyfile( """\ import logging @@ -797,7 +806,7 @@ def test_log_file(): """ ) - result = testdir.runpytest() + result = pytester.runpytest() # make sure that that we get a '0' exit code for the testsuite assert result.ret == 0 @@ -810,11 +819,13 @@ def test_log_file(): @pytest.mark.parametrize("has_capture_manager", [True, False]) -def test_live_logging_suspends_capture(has_capture_manager: bool, request) -> None: +def test_live_logging_suspends_capture( + has_capture_manager: bool, request: FixtureRequest +) -> None: """Test that capture manager is suspended when we emitting messages for live logging. This tests the implementation calls instead of behavior because it is difficult/impossible to do it using - ``testdir`` facilities because they do their own capturing. + ``pytester`` facilities because they do their own capturing. We parametrize the test to also make sure _LiveLoggingStreamHandler works correctly if no capture manager plugin is installed. @@ -856,8 +867,8 @@ def section(self, *args, **kwargs): assert cast(io.StringIO, out_file).getvalue() == "\nsome message\n" -def test_collection_live_logging(testdir): - testdir.makepyfile( +def test_collection_live_logging(pytester: Pytester) -> None: + pytester.makepyfile( """ import logging @@ -865,22 +876,22 @@ def test_collection_live_logging(testdir): """ ) - result = testdir.runpytest("--log-cli-level=INFO") + result = pytester.runpytest("--log-cli-level=INFO") result.stdout.fnmatch_lines( ["*--- live log collection ---*", "*Normal message*", "collected 0 items"] ) @pytest.mark.parametrize("verbose", ["", "-q", "-qq"]) -def test_collection_collect_only_live_logging(testdir, verbose): - testdir.makepyfile( +def test_collection_collect_only_live_logging(pytester: Pytester, verbose: str) -> None: + pytester.makepyfile( """ def test_simple(): pass """ ) - result = testdir.runpytest("--collect-only", "--log-cli-level=INFO", verbose) + result = pytester.runpytest("--collect-only", "--log-cli-level=INFO", verbose) expected_lines = [] @@ -907,10 +918,10 @@ def test_simple(): result.stdout.fnmatch_lines(expected_lines) -def test_collection_logging_to_file(testdir): - log_file = testdir.tmpdir.join("pytest.log").strpath +def test_collection_logging_to_file(pytester: Pytester) -> None: + log_file = str(pytester.path.joinpath("pytest.log")) - testdir.makeini( + pytester.makeini( """ [pytest] log_file={} @@ -920,7 +931,7 @@ def test_collection_logging_to_file(testdir): ) ) - testdir.makepyfile( + pytester.makepyfile( """ import logging @@ -932,7 +943,7 @@ def test_simple(): """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.no_fnmatch_line("*--- live log collection ---*") @@ -945,10 +956,10 @@ def test_simple(): assert "info message in test_simple" in contents -def test_log_in_hooks(testdir): - log_file = testdir.tmpdir.join("pytest.log").strpath +def test_log_in_hooks(pytester: Pytester) -> None: + log_file = str(pytester.path.joinpath("pytest.log")) - testdir.makeini( + pytester.makeini( """ [pytest] log_file={} @@ -958,7 +969,7 @@ def test_log_in_hooks(testdir): log_file ) ) - testdir.makeconftest( + pytester.makeconftest( """ import logging @@ -972,7 +983,7 @@ def pytest_sessionfinish(session, exitstatus): logging.info('sessionfinish') """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["*sessionstart*", "*runtestloop*", "*sessionfinish*"]) with open(log_file) as rfh: contents = rfh.read() @@ -981,10 +992,10 @@ def pytest_sessionfinish(session, exitstatus): assert "sessionfinish" in contents -def test_log_in_runtest_logreport(testdir): - log_file = testdir.tmpdir.join("pytest.log").strpath +def test_log_in_runtest_logreport(pytester: Pytester) -> None: + log_file = str(pytester.path.joinpath("pytest.log")) - testdir.makeini( + pytester.makeini( """ [pytest] log_file={} @@ -994,7 +1005,7 @@ def test_log_in_runtest_logreport(testdir): log_file ) ) - testdir.makeconftest( + pytester.makeconftest( """ import logging logger = logging.getLogger(__name__) @@ -1003,29 +1014,29 @@ def pytest_runtest_logreport(report): logger.info("logreport") """ ) - testdir.makepyfile( + pytester.makepyfile( """ def test_first(): assert True """ ) - testdir.runpytest() + pytester.runpytest() with open(log_file) as rfh: contents = rfh.read() assert contents.count("logreport") == 3 -def test_log_set_path(testdir): - report_dir_base = testdir.tmpdir.strpath +def test_log_set_path(pytester: Pytester) -> None: + report_dir_base = str(pytester.path) - testdir.makeini( + pytester.makeini( """ [pytest] log_file_level = DEBUG log_cli=true """ ) - testdir.makeconftest( + pytester.makeconftest( """ import os import pytest @@ -1040,7 +1051,7 @@ def pytest_runtest_setup(item): repr(report_dir_base) ) ) - testdir.makepyfile( + pytester.makepyfile( """ import logging logger = logging.getLogger("testcase-logger") @@ -1053,7 +1064,7 @@ def test_second(): assert True """ ) - testdir.runpytest() + pytester.runpytest() with open(os.path.join(report_dir_base, "test_first")) as rfh: content = rfh.read() assert "message from test 1" in content @@ -1063,10 +1074,10 @@ def test_second(): assert "message from test 2" in content -def test_colored_captured_log(testdir): +def test_colored_captured_log(pytester: Pytester) -> None: """Test that the level names of captured log messages of a failing test are colored.""" - testdir.makepyfile( + pytester.makepyfile( """ import logging @@ -1077,7 +1088,7 @@ def test_foo(): assert False """ ) - result = testdir.runpytest("--log-level=INFO", "--color=yes") + result = pytester.runpytest("--log-level=INFO", "--color=yes") assert result.ret == 1 result.stdout.fnmatch_lines( [ @@ -1087,9 +1098,9 @@ def test_foo(): ) -def test_colored_ansi_esc_caplogtext(testdir): +def test_colored_ansi_esc_caplogtext(pytester: Pytester) -> None: """Make sure that caplog.text does not contain ANSI escape sequences.""" - testdir.makepyfile( + pytester.makepyfile( """ import logging @@ -1100,11 +1111,11 @@ def test_foo(caplog): assert '\x1b' not in caplog.text """ ) - result = testdir.runpytest("--log-level=INFO", "--color=yes") + result = pytester.runpytest("--log-level=INFO", "--color=yes") assert result.ret == 0 -def test_logging_emit_error(testdir: Testdir) -> None: +def test_logging_emit_error(pytester: Pytester) -> None: """An exception raised during emit() should fail the test. The default behavior of logging is to print "Logging error" @@ -1112,7 +1123,7 @@ def test_logging_emit_error(testdir: Testdir) -> None: pytest overrides this behavior to propagate the exception. """ - testdir.makepyfile( + pytester.makepyfile( """ import logging @@ -1120,7 +1131,7 @@ def test_bad_log(): logging.warning('oops', 'first', 2) """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.assert_outcomes(failed=1) result.stdout.fnmatch_lines( [ @@ -1130,10 +1141,10 @@ def test_bad_log(): ) -def test_logging_emit_error_supressed(testdir: Testdir) -> None: +def test_logging_emit_error_supressed(pytester: Pytester) -> None: """If logging is configured to silently ignore errors, pytest doesn't propagate errors either.""" - testdir.makepyfile( + pytester.makepyfile( """ import logging @@ -1142,13 +1153,15 @@ def test_bad_log(monkeypatch): logging.warning('oops', 'first', 2) """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.assert_outcomes(passed=1) -def test_log_file_cli_subdirectories_are_successfully_created(testdir): - path = testdir.makepyfile(""" def test_logger(): pass """) +def test_log_file_cli_subdirectories_are_successfully_created( + pytester: Pytester, +) -> None: + path = pytester.makepyfile(""" def test_logger(): pass """) expected = os.path.join(os.path.dirname(str(path)), "foo", "bar") - result = testdir.runpytest("--log-file=foo/bar/logf.log") + result = pytester.runpytest("--log-file=foo/bar/logf.log") assert "logf.log" in os.listdir(expected) assert result.ret == ExitCode.OK diff --git a/testing/test_cacheprovider.py b/testing/test_cacheprovider.py index ccc7304b02a..7f0827bd488 100644 --- a/testing/test_cacheprovider.py +++ b/testing/test_cacheprovider.py @@ -1,31 +1,32 @@ import os import shutil -import stat import sys - -import py +from pathlib import Path +from typing import List import pytest from _pytest.config import ExitCode +from _pytest.monkeypatch import MonkeyPatch from _pytest.pytester import Pytester -from _pytest.pytester import Testdir pytest_plugins = ("pytester",) class TestNewAPI: - def test_config_cache_makedir(self, testdir): - testdir.makeini("[pytest]") - config = testdir.parseconfigure() + def test_config_cache_makedir(self, pytester: Pytester) -> None: + pytester.makeini("[pytest]") + config = pytester.parseconfigure() + assert config.cache is not None with pytest.raises(ValueError): config.cache.makedir("key/name") p = config.cache.makedir("name") assert p.check() - def test_config_cache_dataerror(self, testdir): - testdir.makeini("[pytest]") - config = testdir.parseconfigure() + def test_config_cache_dataerror(self, pytester: Pytester) -> None: + pytester.makeini("[pytest]") + config = pytester.parseconfigure() + assert config.cache is not None cache = config.cache pytest.raises(TypeError, lambda: cache.set("key/name", cache)) config.cache.set("key/name", 0) @@ -34,39 +35,45 @@ def test_config_cache_dataerror(self, testdir): assert val == -2 @pytest.mark.filterwarnings("ignore:could not create cache path") - def test_cache_writefail_cachfile_silent(self, testdir): - testdir.makeini("[pytest]") - testdir.tmpdir.join(".pytest_cache").write("gone wrong") - config = testdir.parseconfigure() + def test_cache_writefail_cachfile_silent(self, pytester: Pytester) -> None: + pytester.makeini("[pytest]") + pytester.path.joinpath(".pytest_cache").write_text("gone wrong") + config = pytester.parseconfigure() cache = config.cache + assert cache is not None cache.set("test/broken", []) @pytest.mark.skipif(sys.platform.startswith("win"), reason="no chmod on windows") @pytest.mark.filterwarnings( "ignore:could not create cache path:pytest.PytestWarning" ) - def test_cache_writefail_permissions(self, testdir): - testdir.makeini("[pytest]") - cache_dir = str(testdir.tmpdir.ensure_dir(".pytest_cache")) - mode = os.stat(cache_dir)[stat.ST_MODE] - testdir.tmpdir.ensure_dir(".pytest_cache").chmod(0) + def test_cache_writefail_permissions(self, pytester: Pytester) -> None: + pytester.makeini("[pytest]") + cache_dir = pytester.path.joinpath(".pytest_cache") + cache_dir.mkdir() + mode = cache_dir.stat().st_mode + cache_dir.chmod(0) try: - config = testdir.parseconfigure() + config = pytester.parseconfigure() cache = config.cache + assert cache is not None cache.set("test/broken", []) finally: - testdir.tmpdir.ensure_dir(".pytest_cache").chmod(mode) + cache_dir.chmod(mode) @pytest.mark.skipif(sys.platform.startswith("win"), reason="no chmod on windows") @pytest.mark.filterwarnings("default") - def test_cache_failure_warns(self, testdir, monkeypatch): + def test_cache_failure_warns( + self, pytester: Pytester, monkeypatch: MonkeyPatch + ) -> None: monkeypatch.setenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD", "1") - cache_dir = str(testdir.tmpdir.ensure_dir(".pytest_cache")) - mode = os.stat(cache_dir)[stat.ST_MODE] - testdir.tmpdir.ensure_dir(".pytest_cache").chmod(0) + cache_dir = pytester.path.joinpath(".pytest_cache") + cache_dir.mkdir() + mode = cache_dir.stat().st_mode + cache_dir.chmod(0) try: - testdir.makepyfile("def test_error(): raise Exception") - result = testdir.runpytest() + pytester.makepyfile("def test_error(): raise Exception") + result = pytester.runpytest() assert result.ret == 1 # warnings from nodeids, lastfailed, and stepwise result.stdout.fnmatch_lines( @@ -81,28 +88,28 @@ def test_cache_failure_warns(self, testdir, monkeypatch): ] ) finally: - testdir.tmpdir.ensure_dir(".pytest_cache").chmod(mode) + cache_dir.chmod(mode) - def test_config_cache(self, testdir): - testdir.makeconftest( + def test_config_cache(self, pytester: Pytester) -> None: + pytester.makeconftest( """ def pytest_configure(config): # see that we get cache information early on assert hasattr(config, "cache") """ ) - testdir.makepyfile( + pytester.makepyfile( """ def test_session(pytestconfig): assert hasattr(pytestconfig, "cache") """ ) - result = testdir.runpytest() + result = pytester.runpytest() assert result.ret == 0 result.stdout.fnmatch_lines(["*1 passed*"]) - def test_cachefuncarg(self, testdir): - testdir.makepyfile( + def test_cachefuncarg(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest def test_cachefuncarg(cache): @@ -114,13 +121,13 @@ def test_cachefuncarg(cache): assert val == [1] """ ) - result = testdir.runpytest() + result = pytester.runpytest() assert result.ret == 0 result.stdout.fnmatch_lines(["*1 passed*"]) - def test_custom_rel_cache_dir(self, testdir): + def test_custom_rel_cache_dir(self, pytester: Pytester) -> None: rel_cache_dir = os.path.join("custom_cache_dir", "subdir") - testdir.makeini( + pytester.makeini( """ [pytest] cache_dir = {cache_dir} @@ -128,14 +135,14 @@ def test_custom_rel_cache_dir(self, testdir): cache_dir=rel_cache_dir ) ) - testdir.makepyfile(test_errored="def test_error():\n assert False") - testdir.runpytest() - assert testdir.tmpdir.join(rel_cache_dir).isdir() + pytester.makepyfile(test_errored="def test_error():\n assert False") + pytester.runpytest() + assert pytester.path.joinpath(rel_cache_dir).is_dir() - def test_custom_abs_cache_dir(self, testdir, tmpdir_factory): + def test_custom_abs_cache_dir(self, pytester: Pytester, tmpdir_factory) -> None: tmp = str(tmpdir_factory.mktemp("tmp")) abs_cache_dir = os.path.join(tmp, "custom_cache_dir") - testdir.makeini( + pytester.makeini( """ [pytest] cache_dir = {cache_dir} @@ -143,13 +150,15 @@ def test_custom_abs_cache_dir(self, testdir, tmpdir_factory): cache_dir=abs_cache_dir ) ) - testdir.makepyfile(test_errored="def test_error():\n assert False") - testdir.runpytest() - assert py.path.local(abs_cache_dir).isdir() + pytester.makepyfile(test_errored="def test_error():\n assert False") + pytester.runpytest() + assert Path(abs_cache_dir).is_dir() - def test_custom_cache_dir_with_env_var(self, testdir, monkeypatch): + def test_custom_cache_dir_with_env_var( + self, pytester: Pytester, monkeypatch: MonkeyPatch + ) -> None: monkeypatch.setenv("env_var", "custom_cache_dir") - testdir.makeini( + pytester.makeini( """ [pytest] cache_dir = {cache_dir} @@ -157,31 +166,33 @@ def test_custom_cache_dir_with_env_var(self, testdir, monkeypatch): cache_dir="$env_var" ) ) - testdir.makepyfile(test_errored="def test_error():\n assert False") - testdir.runpytest() - assert testdir.tmpdir.join("custom_cache_dir").isdir() + pytester.makepyfile(test_errored="def test_error():\n assert False") + pytester.runpytest() + assert pytester.path.joinpath("custom_cache_dir").is_dir() @pytest.mark.parametrize("env", ((), ("TOX_ENV_DIR", "/tox_env_dir"))) -def test_cache_reportheader(env, testdir, monkeypatch): - testdir.makepyfile("""def test_foo(): pass""") +def test_cache_reportheader(env, pytester: Pytester, monkeypatch: MonkeyPatch) -> None: + pytester.makepyfile("""def test_foo(): pass""") if env: monkeypatch.setenv(*env) expected = os.path.join(env[1], ".pytest_cache") else: monkeypatch.delenv("TOX_ENV_DIR", raising=False) expected = ".pytest_cache" - result = testdir.runpytest("-v") + result = pytester.runpytest("-v") result.stdout.fnmatch_lines(["cachedir: %s" % expected]) -def test_cache_reportheader_external_abspath(testdir, tmpdir_factory): +def test_cache_reportheader_external_abspath( + pytester: Pytester, tmpdir_factory +) -> None: external_cache = tmpdir_factory.mktemp( "test_cache_reportheader_external_abspath_abs" ) - testdir.makepyfile("def test_hello(): pass") - testdir.makeini( + pytester.makepyfile("def test_hello(): pass") + pytester.makeini( """ [pytest] cache_dir = {abscache} @@ -189,15 +200,15 @@ def test_cache_reportheader_external_abspath(testdir, tmpdir_factory): abscache=external_cache ) ) - result = testdir.runpytest("-v") + result = pytester.runpytest("-v") result.stdout.fnmatch_lines([f"cachedir: {external_cache}"]) -def test_cache_show(testdir): - result = testdir.runpytest("--cache-show") +def test_cache_show(pytester: Pytester) -> None: + result = pytester.runpytest("--cache-show") assert result.ret == 0 result.stdout.fnmatch_lines(["*cache is empty*"]) - testdir.makeconftest( + pytester.makeconftest( """ def pytest_configure(config): config.cache.set("my/name", [1,2,3]) @@ -208,10 +219,10 @@ def pytest_configure(config): dp.ensure("world") """ ) - result = testdir.runpytest() + result = pytester.runpytest() assert result.ret == 5 # no tests executed - result = testdir.runpytest("--cache-show") + result = pytester.runpytest("--cache-show") result.stdout.fnmatch_lines( [ "*cachedir:*", @@ -228,7 +239,7 @@ def pytest_configure(config): ) assert result.ret == 0 - result = testdir.runpytest("--cache-show", "*/hello") + result = pytester.runpytest("--cache-show", "*/hello") result.stdout.fnmatch_lines( [ "*cachedir:*", @@ -246,25 +257,27 @@ def pytest_configure(config): class TestLastFailed: - def test_lastfailed_usecase(self, testdir, monkeypatch): + def test_lastfailed_usecase( + self, pytester: Pytester, monkeypatch: MonkeyPatch + ) -> None: monkeypatch.setattr("sys.dont_write_bytecode", True) - p = testdir.makepyfile( + p = pytester.makepyfile( """ def test_1(): assert 0 def test_2(): assert 0 def test_3(): assert 1 """ ) - result = testdir.runpytest(str(p)) + result = pytester.runpytest(str(p)) result.stdout.fnmatch_lines(["*2 failed*"]) - p = testdir.makepyfile( + p = pytester.makepyfile( """ def test_1(): assert 1 def test_2(): assert 1 def test_3(): assert 0 """ ) - result = testdir.runpytest(str(p), "--lf") + result = pytester.runpytest(str(p), "--lf") result.stdout.fnmatch_lines( [ "collected 3 items / 1 deselected / 2 selected", @@ -272,7 +285,7 @@ def test_3(): assert 0 "*= 2 passed, 1 deselected in *", ] ) - result = testdir.runpytest(str(p), "--lf") + result = pytester.runpytest(str(p), "--lf") result.stdout.fnmatch_lines( [ "collected 3 items", @@ -280,27 +293,27 @@ def test_3(): assert 0 "*1 failed*2 passed*", ] ) - testdir.tmpdir.join(".pytest_cache").mkdir(".git") - result = testdir.runpytest(str(p), "--lf", "--cache-clear") + pytester.path.joinpath(".pytest_cache", ".git").mkdir(parents=True) + result = pytester.runpytest(str(p), "--lf", "--cache-clear") result.stdout.fnmatch_lines(["*1 failed*2 passed*"]) - assert testdir.tmpdir.join(".pytest_cache", "README.md").isfile() - assert testdir.tmpdir.join(".pytest_cache", ".git").isdir() + assert pytester.path.joinpath(".pytest_cache", "README.md").is_file() + assert pytester.path.joinpath(".pytest_cache", ".git").is_dir() # Run this again to make sure clear-cache is robust if os.path.isdir(".pytest_cache"): shutil.rmtree(".pytest_cache") - result = testdir.runpytest("--lf", "--cache-clear") + result = pytester.runpytest("--lf", "--cache-clear") result.stdout.fnmatch_lines(["*1 failed*2 passed*"]) - def test_failedfirst_order(self, testdir): - testdir.makepyfile( + def test_failedfirst_order(self, pytester: Pytester) -> None: + pytester.makepyfile( test_a="def test_always_passes(): pass", test_b="def test_always_fails(): assert 0", ) - result = testdir.runpytest() + result = pytester.runpytest() # Test order will be collection order; alphabetical result.stdout.fnmatch_lines(["test_a.py*", "test_b.py*"]) - result = testdir.runpytest("--ff") + result = pytester.runpytest("--ff") # Test order will be failing tests first result.stdout.fnmatch_lines( [ @@ -311,40 +324,42 @@ def test_failedfirst_order(self, testdir): ] ) - def test_lastfailed_failedfirst_order(self, testdir): - testdir.makepyfile( + def test_lastfailed_failedfirst_order(self, pytester: Pytester) -> None: + pytester.makepyfile( test_a="def test_always_passes(): assert 1", test_b="def test_always_fails(): assert 0", ) - result = testdir.runpytest() + result = pytester.runpytest() # Test order will be collection order; alphabetical result.stdout.fnmatch_lines(["test_a.py*", "test_b.py*"]) - result = testdir.runpytest("--lf", "--ff") + result = pytester.runpytest("--lf", "--ff") # Test order will be failing tests first result.stdout.fnmatch_lines(["test_b.py*"]) result.stdout.no_fnmatch_line("*test_a.py*") - def test_lastfailed_difference_invocations(self, testdir, monkeypatch): + def test_lastfailed_difference_invocations( + self, pytester: Pytester, monkeypatch: MonkeyPatch + ) -> None: monkeypatch.setattr("sys.dont_write_bytecode", True) - testdir.makepyfile( + pytester.makepyfile( test_a=""" def test_a1(): assert 0 def test_a2(): assert 1 """, test_b="def test_b1(): assert 0", ) - p = testdir.tmpdir.join("test_a.py") - p2 = testdir.tmpdir.join("test_b.py") + p = pytester.path.joinpath("test_a.py") + p2 = pytester.path.joinpath("test_b.py") - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["*2 failed*"]) - result = testdir.runpytest("--lf", p2) + result = pytester.runpytest("--lf", p2) result.stdout.fnmatch_lines(["*1 failed*"]) - testdir.makepyfile(test_b="def test_b1(): assert 1") - result = testdir.runpytest("--lf", p2) + pytester.makepyfile(test_b="def test_b1(): assert 1") + result = pytester.runpytest("--lf", p2) result.stdout.fnmatch_lines(["*1 passed*"]) - result = testdir.runpytest("--lf", p) + result = pytester.runpytest("--lf", p) result.stdout.fnmatch_lines( [ "collected 2 items / 1 deselected / 1 selected", @@ -353,21 +368,23 @@ def test_a2(): assert 1 ] ) - def test_lastfailed_usecase_splice(self, testdir, monkeypatch): + def test_lastfailed_usecase_splice( + self, pytester: Pytester, monkeypatch: MonkeyPatch + ) -> None: monkeypatch.setattr("sys.dont_write_bytecode", True) - testdir.makepyfile( + pytester.makepyfile( "def test_1(): assert 0", test_something="def test_2(): assert 0" ) - p2 = testdir.tmpdir.join("test_something.py") - result = testdir.runpytest() + p2 = pytester.path.joinpath("test_something.py") + result = pytester.runpytest() result.stdout.fnmatch_lines(["*2 failed*"]) - result = testdir.runpytest("--lf", p2) + result = pytester.runpytest("--lf", p2) result.stdout.fnmatch_lines(["*1 failed*"]) - result = testdir.runpytest("--lf") + result = pytester.runpytest("--lf") result.stdout.fnmatch_lines(["*2 failed*"]) - def test_lastfailed_xpass(self, testdir): - testdir.inline_runsource( + def test_lastfailed_xpass(self, pytester: Pytester) -> None: + pytester.inline_runsource( """ import pytest @pytest.mark.xfail @@ -375,15 +392,16 @@ def test_hello(): assert 1 """ ) - config = testdir.parseconfigure() + config = pytester.parseconfigure() + assert config.cache is not None lastfailed = config.cache.get("cache/lastfailed", -1) assert lastfailed == -1 - def test_non_serializable_parametrize(self, testdir): + def test_non_serializable_parametrize(self, pytester: Pytester) -> None: """Test that failed parametrized tests with unmarshable parameters don't break pytest-cache. """ - testdir.makepyfile( + pytester.makepyfile( r""" import pytest @@ -394,26 +412,26 @@ def test_fail(val): assert False """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["*1 failed in*"]) - def test_terminal_report_lastfailed(self, testdir): - test_a = testdir.makepyfile( + def test_terminal_report_lastfailed(self, pytester: Pytester) -> None: + test_a = pytester.makepyfile( test_a=""" def test_a1(): pass def test_a2(): pass """ ) - test_b = testdir.makepyfile( + test_b = pytester.makepyfile( test_b=""" def test_b1(): assert 0 def test_b2(): assert 0 """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["collected 4 items", "*2 failed, 2 passed in*"]) - result = testdir.runpytest("--lf") + result = pytester.runpytest("--lf") result.stdout.fnmatch_lines( [ "collected 2 items", @@ -422,7 +440,7 @@ def test_b2(): assert 0 ] ) - result = testdir.runpytest(test_a, "--lf") + result = pytester.runpytest(test_a, "--lf") result.stdout.fnmatch_lines( [ "collected 2 items", @@ -431,7 +449,7 @@ def test_b2(): assert 0 ] ) - result = testdir.runpytest(test_b, "--lf") + result = pytester.runpytest(test_b, "--lf") result.stdout.fnmatch_lines( [ "collected 2 items", @@ -440,7 +458,7 @@ def test_b2(): assert 0 ] ) - result = testdir.runpytest("test_b.py::test_b1", "--lf") + result = pytester.runpytest("test_b.py::test_b1", "--lf") result.stdout.fnmatch_lines( [ "collected 1 item", @@ -449,17 +467,17 @@ def test_b2(): assert 0 ] ) - def test_terminal_report_failedfirst(self, testdir): - testdir.makepyfile( + def test_terminal_report_failedfirst(self, pytester: Pytester) -> None: + pytester.makepyfile( test_a=""" def test_a1(): assert 0 def test_a2(): pass """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["collected 2 items", "*1 failed, 1 passed in*"]) - result = testdir.runpytest("--ff") + result = pytester.runpytest("--ff") result.stdout.fnmatch_lines( [ "collected 2 items", @@ -468,9 +486,11 @@ def test_a2(): pass ] ) - def test_lastfailed_collectfailure(self, testdir, monkeypatch): + def test_lastfailed_collectfailure( + self, pytester: Pytester, monkeypatch: MonkeyPatch + ) -> None: - testdir.makepyfile( + pytester.makepyfile( test_maybe=""" import os env = os.environ @@ -485,8 +505,9 @@ def rlf(fail_import, fail_run): monkeypatch.setenv("FAILIMPORT", str(fail_import)) monkeypatch.setenv("FAILTEST", str(fail_run)) - testdir.runpytest("-q") - config = testdir.parseconfigure() + pytester.runpytest("-q") + config = pytester.parseconfigure() + assert config.cache is not None lastfailed = config.cache.get("cache/lastfailed", -1) return lastfailed @@ -499,8 +520,10 @@ def rlf(fail_import, fail_run): lastfailed = rlf(fail_import=0, fail_run=1) assert list(lastfailed) == ["test_maybe.py::test_hello"] - def test_lastfailed_failure_subset(self, testdir, monkeypatch): - testdir.makepyfile( + def test_lastfailed_failure_subset( + self, pytester: Pytester, monkeypatch: MonkeyPatch + ) -> None: + pytester.makepyfile( test_maybe=""" import os env = os.environ @@ -511,7 +534,7 @@ def test_hello(): """ ) - testdir.makepyfile( + pytester.makepyfile( test_maybe2=""" import os env = os.environ @@ -530,8 +553,9 @@ def rlf(fail_import, fail_run, args=()): monkeypatch.setenv("FAILIMPORT", str(fail_import)) monkeypatch.setenv("FAILTEST", str(fail_run)) - result = testdir.runpytest("-q", "--lf", *args) - config = testdir.parseconfigure() + result = pytester.runpytest("-q", "--lf", *args) + config = pytester.parseconfigure() + assert config.cache is not None lastfailed = config.cache.get("cache/lastfailed", -1) return result, lastfailed @@ -552,61 +576,63 @@ def rlf(fail_import, fail_run, args=()): assert list(lastfailed) == ["test_maybe.py"] result.stdout.fnmatch_lines(["*2 passed*"]) - def test_lastfailed_creates_cache_when_needed(self, testdir): + def test_lastfailed_creates_cache_when_needed(self, pytester: Pytester) -> None: # Issue #1342 - testdir.makepyfile(test_empty="") - testdir.runpytest("-q", "--lf") + pytester.makepyfile(test_empty="") + pytester.runpytest("-q", "--lf") assert not os.path.exists(".pytest_cache/v/cache/lastfailed") - testdir.makepyfile(test_successful="def test_success():\n assert True") - testdir.runpytest("-q", "--lf") + pytester.makepyfile(test_successful="def test_success():\n assert True") + pytester.runpytest("-q", "--lf") assert not os.path.exists(".pytest_cache/v/cache/lastfailed") - testdir.makepyfile(test_errored="def test_error():\n assert False") - testdir.runpytest("-q", "--lf") + pytester.makepyfile(test_errored="def test_error():\n assert False") + pytester.runpytest("-q", "--lf") assert os.path.exists(".pytest_cache/v/cache/lastfailed") - def test_xfail_not_considered_failure(self, testdir): - testdir.makepyfile( + def test_xfail_not_considered_failure(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @pytest.mark.xfail def test(): assert 0 """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["*1 xfailed*"]) - assert self.get_cached_last_failed(testdir) == [] + assert self.get_cached_last_failed(pytester) == [] - def test_xfail_strict_considered_failure(self, testdir): - testdir.makepyfile( + def test_xfail_strict_considered_failure(self, pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @pytest.mark.xfail(strict=True) def test(): pass """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["*1 failed*"]) - assert self.get_cached_last_failed(testdir) == [ + assert self.get_cached_last_failed(pytester) == [ "test_xfail_strict_considered_failure.py::test" ] @pytest.mark.parametrize("mark", ["mark.xfail", "mark.skip"]) - def test_failed_changed_to_xfail_or_skip(self, testdir, mark): - testdir.makepyfile( + def test_failed_changed_to_xfail_or_skip( + self, pytester: Pytester, mark: str + ) -> None: + pytester.makepyfile( """ import pytest def test(): assert 0 """ ) - result = testdir.runpytest() - assert self.get_cached_last_failed(testdir) == [ + result = pytester.runpytest() + assert self.get_cached_last_failed(pytester) == [ "test_failed_changed_to_xfail_or_skip.py::test" ] assert result.ret == 1 - testdir.makepyfile( + pytester.makepyfile( """ import pytest @pytest.{mark} @@ -615,66 +641,69 @@ def test(): assert 0 mark=mark ) ) - result = testdir.runpytest() + result = pytester.runpytest() assert result.ret == 0 - assert self.get_cached_last_failed(testdir) == [] + assert self.get_cached_last_failed(pytester) == [] assert result.ret == 0 @pytest.mark.parametrize("quiet", [True, False]) @pytest.mark.parametrize("opt", ["--ff", "--lf"]) - def test_lf_and_ff_prints_no_needless_message(self, quiet, opt, testdir): + def test_lf_and_ff_prints_no_needless_message( + self, quiet: bool, opt: str, pytester: Pytester + ) -> None: # Issue 3853 - testdir.makepyfile("def test(): assert 0") + pytester.makepyfile("def test(): assert 0") args = [opt] if quiet: args.append("-q") - result = testdir.runpytest(*args) + result = pytester.runpytest(*args) result.stdout.no_fnmatch_line("*run all*") - result = testdir.runpytest(*args) + result = pytester.runpytest(*args) if quiet: result.stdout.no_fnmatch_line("*run all*") else: assert "rerun previous" in result.stdout.str() - def get_cached_last_failed(self, testdir): - config = testdir.parseconfigure() + def get_cached_last_failed(self, pytester: Pytester) -> List[str]: + config = pytester.parseconfigure() + assert config.cache is not None return sorted(config.cache.get("cache/lastfailed", {})) - def test_cache_cumulative(self, testdir): + def test_cache_cumulative(self, pytester: Pytester) -> None: """Test workflow where user fixes errors gradually file by file using --lf.""" # 1. initial run - test_bar = testdir.makepyfile( + test_bar = pytester.makepyfile( test_bar=""" def test_bar_1(): pass def test_bar_2(): assert 0 """ ) - test_foo = testdir.makepyfile( + test_foo = pytester.makepyfile( test_foo=""" def test_foo_3(): pass def test_foo_4(): assert 0 """ ) - testdir.runpytest() - assert self.get_cached_last_failed(testdir) == [ + pytester.runpytest() + assert self.get_cached_last_failed(pytester) == [ "test_bar.py::test_bar_2", "test_foo.py::test_foo_4", ] # 2. fix test_bar_2, run only test_bar.py - testdir.makepyfile( + pytester.makepyfile( test_bar=""" def test_bar_1(): pass def test_bar_2(): pass """ ) - result = testdir.runpytest(test_bar) + result = pytester.runpytest(test_bar) result.stdout.fnmatch_lines(["*2 passed*"]) # ensure cache does not forget that test_foo_4 failed once before - assert self.get_cached_last_failed(testdir) == ["test_foo.py::test_foo_4"] + assert self.get_cached_last_failed(pytester) == ["test_foo.py::test_foo_4"] - result = testdir.runpytest("--last-failed") + result = pytester.runpytest("--last-failed") result.stdout.fnmatch_lines( [ "collected 1 item", @@ -682,16 +711,16 @@ def test_bar_2(): pass "*= 1 failed in *", ] ) - assert self.get_cached_last_failed(testdir) == ["test_foo.py::test_foo_4"] + assert self.get_cached_last_failed(pytester) == ["test_foo.py::test_foo_4"] # 3. fix test_foo_4, run only test_foo.py - test_foo = testdir.makepyfile( + test_foo = pytester.makepyfile( test_foo=""" def test_foo_3(): pass def test_foo_4(): pass """ ) - result = testdir.runpytest(test_foo, "--last-failed") + result = pytester.runpytest(test_foo, "--last-failed") result.stdout.fnmatch_lines( [ "collected 2 items / 1 deselected / 1 selected", @@ -699,29 +728,31 @@ def test_foo_4(): pass "*= 1 passed, 1 deselected in *", ] ) - assert self.get_cached_last_failed(testdir) == [] + assert self.get_cached_last_failed(pytester) == [] - result = testdir.runpytest("--last-failed") + result = pytester.runpytest("--last-failed") result.stdout.fnmatch_lines(["*4 passed*"]) - assert self.get_cached_last_failed(testdir) == [] + assert self.get_cached_last_failed(pytester) == [] - def test_lastfailed_no_failures_behavior_all_passed(self, testdir): - testdir.makepyfile( + def test_lastfailed_no_failures_behavior_all_passed( + self, pytester: Pytester + ) -> None: + pytester.makepyfile( """ def test_1(): pass def test_2(): pass """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["*2 passed*"]) - result = testdir.runpytest("--lf") + result = pytester.runpytest("--lf") result.stdout.fnmatch_lines(["*2 passed*"]) - result = testdir.runpytest("--lf", "--lfnf", "all") + result = pytester.runpytest("--lf", "--lfnf", "all") result.stdout.fnmatch_lines(["*2 passed*"]) # Ensure the list passed to pytest_deselected is a copy, # and not a reference which is cleared right after. - testdir.makeconftest( + pytester.makeconftest( """ deselected = [] @@ -734,7 +765,7 @@ def pytest_sessionfinish(): """ ) - result = testdir.runpytest("--lf", "--lfnf", "none") + result = pytester.runpytest("--lf", "--lfnf", "none") result.stdout.fnmatch_lines( [ "collected 2 items / 2 deselected", @@ -745,26 +776,28 @@ def pytest_sessionfinish(): ) assert result.ret == ExitCode.NO_TESTS_COLLECTED - def test_lastfailed_no_failures_behavior_empty_cache(self, testdir): - testdir.makepyfile( + def test_lastfailed_no_failures_behavior_empty_cache( + self, pytester: Pytester + ) -> None: + pytester.makepyfile( """ def test_1(): pass def test_2(): assert 0 """ ) - result = testdir.runpytest("--lf", "--cache-clear") + result = pytester.runpytest("--lf", "--cache-clear") result.stdout.fnmatch_lines(["*1 failed*1 passed*"]) - result = testdir.runpytest("--lf", "--cache-clear", "--lfnf", "all") + result = pytester.runpytest("--lf", "--cache-clear", "--lfnf", "all") result.stdout.fnmatch_lines(["*1 failed*1 passed*"]) - result = testdir.runpytest("--lf", "--cache-clear", "--lfnf", "none") + result = pytester.runpytest("--lf", "--cache-clear", "--lfnf", "none") result.stdout.fnmatch_lines(["*2 desel*"]) - def test_lastfailed_skip_collection(self, testdir): + def test_lastfailed_skip_collection(self, pytester: Pytester) -> None: """ Test --lf behavior regarding skipping collection of files that are not marked as failed in the cache (#5172). """ - testdir.makepyfile( + pytester.makepyfile( **{ "pkg1/test_1.py": """ import pytest @@ -782,10 +815,10 @@ def test_1(i): } ) # first run: collects 8 items (test_1: 3, test_2: 5) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["collected 8 items", "*2 failed*6 passed*"]) # second run: collects only 5 items from test_2, because all tests from test_1 have passed - result = testdir.runpytest("--lf") + result = pytester.runpytest("--lf") result.stdout.fnmatch_lines( [ "collected 2 items", @@ -795,14 +828,14 @@ def test_1(i): ) # add another file and check if message is correct when skipping more than 1 file - testdir.makepyfile( + pytester.makepyfile( **{ "pkg1/test_3.py": """ def test_3(): pass """ } ) - result = testdir.runpytest("--lf") + result = pytester.runpytest("--lf") result.stdout.fnmatch_lines( [ "collected 2 items", @@ -811,18 +844,20 @@ def test_3(): pass ] ) - def test_lastfailed_with_known_failures_not_being_selected(self, testdir): - testdir.makepyfile( + def test_lastfailed_with_known_failures_not_being_selected( + self, pytester: Pytester + ) -> None: + pytester.makepyfile( **{ "pkg1/test_1.py": """def test_1(): assert 0""", "pkg1/test_2.py": """def test_2(): pass""", } ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["collected 2 items", "* 1 failed, 1 passed in *"]) - py.path.local("pkg1/test_1.py").remove() - result = testdir.runpytest("--lf") + Path("pkg1/test_1.py").unlink() + result = pytester.runpytest("--lf") result.stdout.fnmatch_lines( [ "collected 1 item", @@ -832,8 +867,8 @@ def test_lastfailed_with_known_failures_not_being_selected(self, testdir): ) # Recreate file with known failure. - testdir.makepyfile(**{"pkg1/test_1.py": """def test_1(): assert 0"""}) - result = testdir.runpytest("--lf") + pytester.makepyfile(**{"pkg1/test_1.py": """def test_1(): assert 0"""}) + result = pytester.runpytest("--lf") result.stdout.fnmatch_lines( [ "collected 1 item", @@ -843,8 +878,8 @@ def test_lastfailed_with_known_failures_not_being_selected(self, testdir): ) # Remove/rename test: collects the file again. - testdir.makepyfile(**{"pkg1/test_1.py": """def test_renamed(): assert 0"""}) - result = testdir.runpytest("--lf", "-rf") + pytester.makepyfile(**{"pkg1/test_1.py": """def test_renamed(): assert 0"""}) + result = pytester.runpytest("--lf", "-rf") result.stdout.fnmatch_lines( [ "collected 2 items", @@ -856,7 +891,7 @@ def test_lastfailed_with_known_failures_not_being_selected(self, testdir): ] ) - result = testdir.runpytest("--lf", "--co") + result = pytester.runpytest("--lf", "--co") result.stdout.fnmatch_lines( [ "collected 1 item", @@ -867,13 +902,13 @@ def test_lastfailed_with_known_failures_not_being_selected(self, testdir): ] ) - def test_lastfailed_args_with_deselected(self, testdir: Testdir) -> None: + def test_lastfailed_args_with_deselected(self, pytester: Pytester) -> None: """Test regression with --lf running into NoMatch error. This was caused by it not collecting (non-failed) nodes given as arguments. """ - testdir.makepyfile( + pytester.makepyfile( **{ "pkg1/test_1.py": """ def test_pass(): pass @@ -881,11 +916,11 @@ def test_fail(): assert 0 """, } ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["collected 2 items", "* 1 failed, 1 passed in *"]) assert result.ret == 1 - result = testdir.runpytest("pkg1/test_1.py::test_pass", "--lf", "--co") + result = pytester.runpytest("pkg1/test_1.py::test_pass", "--lf", "--co") assert result.ret == 0 result.stdout.fnmatch_lines( [ @@ -898,7 +933,7 @@ def test_fail(): assert 0 consecutive=True, ) - result = testdir.runpytest( + result = pytester.runpytest( "pkg1/test_1.py::test_pass", "pkg1/test_1.py::test_fail", "--lf", "--co" ) assert result.ret == 0 @@ -913,9 +948,9 @@ def test_fail(): assert 0 ], ) - def test_lastfailed_with_class_items(self, testdir: Testdir) -> None: + def test_lastfailed_with_class_items(self, pytester: Pytester) -> None: """Test regression with --lf deselecting whole classes.""" - testdir.makepyfile( + pytester.makepyfile( **{ "pkg1/test_1.py": """ class TestFoo: @@ -926,11 +961,11 @@ def test_other(): assert 0 """, } ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["collected 3 items", "* 2 failed, 1 passed in *"]) assert result.ret == 1 - result = testdir.runpytest("--lf", "--co") + result = pytester.runpytest("--lf", "--co") assert result.ret == 0 result.stdout.fnmatch_lines( [ @@ -947,8 +982,8 @@ def test_other(): assert 0 consecutive=True, ) - def test_lastfailed_with_all_filtered(self, testdir: Testdir) -> None: - testdir.makepyfile( + def test_lastfailed_with_all_filtered(self, pytester: Pytester) -> None: + pytester.makepyfile( **{ "pkg1/test_1.py": """ def test_fail(): assert 0 @@ -956,19 +991,19 @@ def test_pass(): pass """, } ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["collected 2 items", "* 1 failed, 1 passed in *"]) assert result.ret == 1 # Remove known failure. - testdir.makepyfile( + pytester.makepyfile( **{ "pkg1/test_1.py": """ def test_pass(): pass """, } ) - result = testdir.runpytest("--lf", "--co") + result = pytester.runpytest("--lf", "--co") result.stdout.fnmatch_lines( [ "collected 1 item", @@ -1015,8 +1050,8 @@ def test_packages(self, pytester: Pytester) -> None: class TestNewFirst: - def test_newfirst_usecase(self, testdir): - testdir.makepyfile( + def test_newfirst_usecase(self, pytester: Pytester, testdir) -> None: + pytester.makepyfile( **{ "test_1/test_1.py": """ def test_1(): assert 1 @@ -1026,24 +1061,24 @@ def test_1(): assert 1 """, } ) - testdir.tmpdir.join("test_1/test_1.py").setmtime(1) - result = testdir.runpytest("-v") + p1 = pytester.path.joinpath("test_1/test_1.py") + os.utime(p1, ns=(p1.stat().st_atime_ns, int(1e9))) + + result = pytester.runpytest("-v") result.stdout.fnmatch_lines( ["*test_1/test_1.py::test_1 PASSED*", "*test_2/test_2.py::test_1 PASSED*"] ) - result = testdir.runpytest("-v", "--nf") + result = pytester.runpytest("-v", "--nf") result.stdout.fnmatch_lines( ["*test_2/test_2.py::test_1 PASSED*", "*test_1/test_1.py::test_1 PASSED*"] ) - testdir.tmpdir.join("test_1/test_1.py").write( - "def test_1(): assert 1\n" "def test_2(): assert 1\n" - ) - testdir.tmpdir.join("test_1/test_1.py").setmtime(1) + p1.write_text("def test_1(): assert 1\n" "def test_2(): assert 1\n") + os.utime(p1, ns=(p1.stat().st_atime_ns, int(1e9))) - result = testdir.runpytest("--nf", "--collect-only", "-q") + result = pytester.runpytest("--nf", "--collect-only", "-q") result.stdout.fnmatch_lines( [ "test_1/test_1.py::test_2", @@ -1053,15 +1088,15 @@ def test_1(): assert 1 ) # Newest first with (plugin) pytest_collection_modifyitems hook. - testdir.makepyfile( + pytester.makepyfile( myplugin=""" def pytest_collection_modifyitems(items): items[:] = sorted(items, key=lambda item: item.nodeid) print("new_items:", [x.nodeid for x in items]) """ ) - testdir.syspathinsert() - result = testdir.runpytest("--nf", "-p", "myplugin", "--collect-only", "-q") + pytester.syspathinsert() + result = pytester.runpytest("--nf", "-p", "myplugin", "--collect-only", "-q") result.stdout.fnmatch_lines( [ "new_items: *test_1.py*test_1.py*test_2.py*", @@ -1071,8 +1106,8 @@ def pytest_collection_modifyitems(items): ] ) - def test_newfirst_parametrize(self, testdir): - testdir.makepyfile( + def test_newfirst_parametrize(self, pytester: Pytester) -> None: + pytester.makepyfile( **{ "test_1/test_1.py": """ import pytest @@ -1087,9 +1122,10 @@ def test_1(num): assert num } ) - testdir.tmpdir.join("test_1/test_1.py").setmtime(1) + p1 = pytester.path.joinpath("test_1/test_1.py") + os.utime(p1, ns=(p1.stat().st_atime_ns, int(1e9))) - result = testdir.runpytest("-v") + result = pytester.runpytest("-v") result.stdout.fnmatch_lines( [ "*test_1/test_1.py::test_1[1*", @@ -1099,7 +1135,7 @@ def test_1(num): assert num ] ) - result = testdir.runpytest("-v", "--nf") + result = pytester.runpytest("-v", "--nf") result.stdout.fnmatch_lines( [ "*test_2/test_2.py::test_1[1*", @@ -1109,20 +1145,20 @@ def test_1(num): assert num ] ) - testdir.tmpdir.join("test_1/test_1.py").write( + p1.write_text( "import pytest\n" "@pytest.mark.parametrize('num', [1, 2, 3])\n" "def test_1(num): assert num\n" ) - testdir.tmpdir.join("test_1/test_1.py").setmtime(1) + os.utime(p1, ns=(p1.stat().st_atime_ns, int(1e9))) # Running only a subset does not forget about existing ones. - result = testdir.runpytest("-v", "--nf", "test_2/test_2.py") + result = pytester.runpytest("-v", "--nf", "test_2/test_2.py") result.stdout.fnmatch_lines( ["*test_2/test_2.py::test_1[1*", "*test_2/test_2.py::test_1[2*"] ) - result = testdir.runpytest("-v", "--nf") + result = pytester.runpytest("-v", "--nf") result.stdout.fnmatch_lines( [ "*test_1/test_1.py::test_1[3*", @@ -1135,27 +1171,28 @@ def test_1(num): assert num class TestReadme: - def check_readme(self, testdir): - config = testdir.parseconfigure() + def check_readme(self, pytester: Pytester) -> bool: + config = pytester.parseconfigure() + assert config.cache is not None readme = config.cache._cachedir.joinpath("README.md") return readme.is_file() - def test_readme_passed(self, testdir): - testdir.makepyfile("def test_always_passes(): pass") - testdir.runpytest() - assert self.check_readme(testdir) is True + def test_readme_passed(self, pytester: Pytester) -> None: + pytester.makepyfile("def test_always_passes(): pass") + pytester.runpytest() + assert self.check_readme(pytester) is True - def test_readme_failed(self, testdir): - testdir.makepyfile("def test_always_fails(): assert 0") - testdir.runpytest() - assert self.check_readme(testdir) is True + def test_readme_failed(self, pytester: Pytester) -> None: + pytester.makepyfile("def test_always_fails(): assert 0") + pytester.runpytest() + assert self.check_readme(pytester) is True -def test_gitignore(testdir): +def test_gitignore(pytester: Pytester) -> None: """Ensure we automatically create .gitignore file in the pytest_cache directory (#3286).""" from _pytest.cacheprovider import Cache - config = testdir.parseconfig() + config = pytester.parseconfig() cache = Cache.for_config(config, _ispytest=True) cache.set("foo", "bar") msg = "# Created by pytest automatically.\n*\n" @@ -1168,16 +1205,16 @@ def test_gitignore(testdir): assert gitignore_path.read_text(encoding="UTF-8") == "custom" -def test_does_not_create_boilerplate_in_existing_dirs(testdir): +def test_does_not_create_boilerplate_in_existing_dirs(pytester: Pytester) -> None: from _pytest.cacheprovider import Cache - testdir.makeini( + pytester.makeini( """ [pytest] cache_dir = . """ ) - config = testdir.parseconfig() + config = pytester.parseconfig() cache = Cache.for_config(config, _ispytest=True) cache.set("foo", "bar") @@ -1186,12 +1223,12 @@ def test_does_not_create_boilerplate_in_existing_dirs(testdir): assert not os.path.exists("README.md") -def test_cachedir_tag(testdir): +def test_cachedir_tag(pytester: Pytester) -> None: """Ensure we automatically create CACHEDIR.TAG file in the pytest_cache directory (#4278).""" from _pytest.cacheprovider import Cache from _pytest.cacheprovider import CACHEDIR_TAG_CONTENT - config = testdir.parseconfig() + config = pytester.parseconfig() cache = Cache.for_config(config, _ispytest=True) cache.set("foo", "bar") cachedir_tag_path = cache._cachedir.joinpath("CACHEDIR.TAG") diff --git a/testing/test_conftest.py b/testing/test_conftest.py index 36e83191bcd..80f2a6d0bc0 100644 --- a/testing/test_conftest.py +++ b/testing/test_conftest.py @@ -8,8 +8,6 @@ from typing import List from typing import Optional -import py - import pytest from _pytest.config import ExitCode from _pytest.config import PytestPluginManager @@ -93,9 +91,9 @@ def test_value_access_with_confmod(self, basedir: Path) -> None: conftest = ConftestWithSetinitial(startdir) mod, value = conftest._rget_with_confmod("a", startdir, importmode="prepend") assert value == 1.5 - path = py.path.local(mod.__file__) - assert path.dirpath() == basedir / "adir" / "b" - assert path.purebasename.startswith("conftest") + path = Path(mod.__file__) + assert path.parent == basedir / "adir" / "b" + assert path.stem == "conftest" def test_conftest_in_nonpkg_with_init(tmp_path: Path, _sys_snapshot) -> None: @@ -361,11 +359,10 @@ def impct(p, importmode): def test_fixture_dependency(pytester: Pytester) -> None: - ct1 = pytester.makeconftest("") - ct1 = pytester.makepyfile("__init__.py") - ct1.write_text("") + pytester.makeconftest("") + pytester.path.joinpath("__init__.py").touch() sub = pytester.mkdir("sub") - sub.joinpath("__init__.py").write_text("") + sub.joinpath("__init__.py").touch() sub.joinpath("conftest.py").write_text( textwrap.dedent( """\ @@ -387,7 +384,7 @@ def bar(foo): ) subsub = sub.joinpath("subsub") subsub.mkdir() - subsub.joinpath("__init__.py").write_text("") + subsub.joinpath("__init__.py").touch() subsub.joinpath("test_bar.py").write_text( textwrap.dedent( """\ @@ -525,8 +522,8 @@ def test_parsefactories_relative_node_ids( """#616""" dirs = self._setup_tree(pytester) print("pytest run in cwd: %s" % (dirs[chdir].relative_to(pytester.path))) - print("pytestarg : %s" % (testarg)) - print("expected pass : %s" % (expect_ntests_passed)) + print("pytestarg : %s" % testarg) + print("expected pass : %s" % expect_ntests_passed) os.chdir(dirs[chdir]) reprec = pytester.inline_run(testarg, "-q", "--traceconfig") reprec.assertoutcome(passed=expect_ntests_passed) diff --git a/testing/test_monkeypatch.py b/testing/test_monkeypatch.py index c20ff7480a8..0b97a0e5adb 100644 --- a/testing/test_monkeypatch.py +++ b/testing/test_monkeypatch.py @@ -2,15 +2,14 @@ import re import sys import textwrap +from pathlib import Path from typing import Dict from typing import Generator from typing import Type -import py - import pytest from _pytest.monkeypatch import MonkeyPatch -from _pytest.pytester import Testdir +from _pytest.pytester import Pytester @pytest.fixture @@ -233,8 +232,8 @@ def test_setenv_prepend() -> None: assert "XYZ123" not in os.environ -def test_monkeypatch_plugin(testdir: Testdir) -> None: - reprec = testdir.inline_runsource( +def test_monkeypatch_plugin(pytester: Pytester) -> None: + reprec = pytester.inline_runsource( """ def test_method(monkeypatch): assert monkeypatch.__class__.__name__ == "MonkeyPatch" @@ -268,33 +267,33 @@ def test_syspath_prepend_double_undo(mp: MonkeyPatch) -> None: sys.path[:] = old_syspath -def test_chdir_with_path_local(mp: MonkeyPatch, tmpdir: py.path.local) -> None: - mp.chdir(tmpdir) - assert os.getcwd() == tmpdir.strpath +def test_chdir_with_path_local(mp: MonkeyPatch, tmp_path: Path) -> None: + mp.chdir(tmp_path) + assert os.getcwd() == str(tmp_path) -def test_chdir_with_str(mp: MonkeyPatch, tmpdir: py.path.local) -> None: - mp.chdir(tmpdir.strpath) - assert os.getcwd() == tmpdir.strpath +def test_chdir_with_str(mp: MonkeyPatch, tmp_path: Path) -> None: + mp.chdir(str(tmp_path)) + assert os.getcwd() == str(tmp_path) -def test_chdir_undo(mp: MonkeyPatch, tmpdir: py.path.local) -> None: +def test_chdir_undo(mp: MonkeyPatch, tmp_path: Path) -> None: cwd = os.getcwd() - mp.chdir(tmpdir) + mp.chdir(tmp_path) mp.undo() assert os.getcwd() == cwd -def test_chdir_double_undo(mp: MonkeyPatch, tmpdir: py.path.local) -> None: - mp.chdir(tmpdir.strpath) +def test_chdir_double_undo(mp: MonkeyPatch, tmp_path: Path) -> None: + mp.chdir(str(tmp_path)) mp.undo() - tmpdir.chdir() + os.chdir(tmp_path) mp.undo() - assert os.getcwd() == tmpdir.strpath + assert os.getcwd() == str(tmp_path) -def test_issue185_time_breaks(testdir: Testdir) -> None: - testdir.makepyfile( +def test_issue185_time_breaks(pytester: Pytester) -> None: + pytester.makepyfile( """ import time def test_m(monkeypatch): @@ -303,7 +302,7 @@ def f(): monkeypatch.setattr(time, "time", f) """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines( """ *1 passed* @@ -311,9 +310,9 @@ def f(): ) -def test_importerror(testdir: Testdir) -> None: - p = testdir.mkpydir("package") - p.join("a.py").write( +def test_importerror(pytester: Pytester) -> None: + p = pytester.mkpydir("package") + p.joinpath("a.py").write_text( textwrap.dedent( """\ import doesnotexist @@ -322,7 +321,7 @@ def test_importerror(testdir: Testdir) -> None: """ ) ) - testdir.tmpdir.join("test_importerror.py").write( + pytester.path.joinpath("test_importerror.py").write_text( textwrap.dedent( """\ def test_importerror(monkeypatch): @@ -330,7 +329,7 @@ def test_importerror(monkeypatch): """ ) ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines( """ *import error in package.a: No module named 'doesnotexist'* @@ -420,16 +419,18 @@ class A: def test_syspath_prepend_with_namespace_packages( - testdir: Testdir, monkeypatch: MonkeyPatch + pytester: Pytester, monkeypatch: MonkeyPatch ) -> None: for dirname in "hello", "world": - d = testdir.mkdir(dirname) - ns = d.mkdir("ns_pkg") - ns.join("__init__.py").write( + d = pytester.mkdir(dirname) + ns = d.joinpath("ns_pkg") + ns.mkdir() + ns.joinpath("__init__.py").write_text( "__import__('pkg_resources').declare_namespace(__name__)" ) - lib = ns.mkdir(dirname) - lib.join("__init__.py").write("def check(): return %r" % dirname) + lib = ns.joinpath(dirname) + lib.mkdir() + lib.joinpath("__init__.py").write_text("def check(): return %r" % dirname) monkeypatch.syspath_prepend("hello") import ns_pkg.hello @@ -446,8 +447,7 @@ def test_syspath_prepend_with_namespace_packages( assert ns_pkg.world.check() == "world" # Should invalidate caches via importlib.invalidate_caches. - tmpdir = testdir.tmpdir - modules_tmpdir = tmpdir.mkdir("modules_tmpdir") + modules_tmpdir = pytester.mkdir("modules_tmpdir") monkeypatch.syspath_prepend(str(modules_tmpdir)) - modules_tmpdir.join("main_app.py").write("app = True") + modules_tmpdir.joinpath("main_app.py").write_text("app = True") from main_app import app # noqa: F401 diff --git a/testing/test_pluginmanager.py b/testing/test_pluginmanager.py index 89f10a7db64..a5282a50795 100644 --- a/testing/test_pluginmanager.py +++ b/testing/test_pluginmanager.py @@ -1,13 +1,17 @@ import os +import shutil import sys import types from typing import List import pytest +from _pytest.config import Config from _pytest.config import ExitCode from _pytest.config import PytestPluginManager from _pytest.config.exceptions import UsageError from _pytest.main import Session +from _pytest.monkeypatch import MonkeyPatch +from _pytest.pathlib import import_path from _pytest.pytester import Pytester @@ -18,7 +22,7 @@ def pytestpm() -> PytestPluginManager: class TestPytestPluginInteractions: def test_addhooks_conftestplugin( - self, pytester: Pytester, _config_for_test + self, pytester: Pytester, _config_for_test: Config ) -> None: pytester.makepyfile( newhooks=""" @@ -45,15 +49,15 @@ def pytest_myhook(xyz): res = config.hook.pytest_myhook(xyz=10) assert res == [11] - def test_addhooks_nohooks(self, testdir): - testdir.makeconftest( + def test_addhooks_nohooks(self, pytester: Pytester) -> None: + pytester.makeconftest( """ import sys def pytest_addhooks(pluginmanager): pluginmanager.add_hookspecs(sys) """ ) - res = testdir.runpytest() + res = pytester.runpytest() assert res.ret != 0 res.stderr.fnmatch_lines(["*did not find*sys*"]) @@ -70,8 +74,8 @@ def pytest_addoption(parser): config.pluginmanager._importconftest(p, importmode="prepend") assert config.option.test123 - def test_configure(self, testdir): - config = testdir.parseconfig() + def test_configure(self, pytester: Pytester) -> None: + config = pytester.parseconfig() values = [] class A: @@ -90,7 +94,7 @@ def pytest_configure(self): config.pluginmanager.register(A()) assert len(values) == 2 - def test_hook_tracing(self, _config_for_test) -> None: + def test_hook_tracing(self, _config_for_test: Config) -> None: pytestpm = _config_for_test.pluginmanager # fully initialized with plugins saveindent = [] @@ -139,9 +143,9 @@ def test_hook_proxy(self, pytester: Pytester) -> None: ihook_b = session.gethookproxy(pytester.path / "tests") assert ihook_a is not ihook_b - def test_hook_with_addoption(self, testdir): + def test_hook_with_addoption(self, pytester: Pytester) -> None: """Test that hooks can be used in a call to pytest_addoption""" - testdir.makepyfile( + pytester.makepyfile( newhooks=""" import pytest @pytest.hookspec(firstresult=True) @@ -149,7 +153,7 @@ def pytest_default_value(): pass """ ) - testdir.makepyfile( + pytester.makepyfile( myplugin=""" import newhooks def pytest_addhooks(pluginmanager): @@ -159,30 +163,32 @@ def pytest_addoption(parser, pluginmanager): parser.addoption("--config", help="Config, defaults to %(default)s", default=default_value) """ ) - testdir.makeconftest( + pytester.makeconftest( """ pytest_plugins=("myplugin",) def pytest_default_value(): return "default_value" """ ) - res = testdir.runpytest("--help") + res = pytester.runpytest("--help") res.stdout.fnmatch_lines(["*--config=CONFIG*default_value*"]) -def test_default_markers(testdir): - result = testdir.runpytest("--markers") +def test_default_markers(pytester: Pytester) -> None: + result = pytester.runpytest("--markers") result.stdout.fnmatch_lines(["*tryfirst*first*", "*trylast*last*"]) -def test_importplugin_error_message(testdir, pytestpm): +def test_importplugin_error_message( + pytester: Pytester, pytestpm: PytestPluginManager +) -> None: """Don't hide import errors when importing plugins and provide an easy to debug message. See #375 and #1998. """ - testdir.syspathinsert(testdir.tmpdir) - testdir.makepyfile( + pytester.syspathinsert(pytester.path) + pytester.makepyfile( qwe="""\ def test_traceback(): raise ImportError('Not possible to import: ☺') @@ -199,7 +205,7 @@ def test_traceback(): class TestPytestPluginManager: - def test_register_imported_modules(self): + def test_register_imported_modules(self) -> None: pm = PytestPluginManager() mod = types.ModuleType("x.y.pytest_hello") pm.register(mod) @@ -219,23 +225,27 @@ def test_canonical_import(self, monkeypatch): assert pm.get_plugin("pytest_xyz") == mod assert pm.is_registered(mod) - def test_consider_module(self, testdir, pytestpm: PytestPluginManager) -> None: - testdir.syspathinsert() - testdir.makepyfile(pytest_p1="#") - testdir.makepyfile(pytest_p2="#") + def test_consider_module( + self, pytester: Pytester, pytestpm: PytestPluginManager + ) -> None: + pytester.syspathinsert() + pytester.makepyfile(pytest_p1="#") + pytester.makepyfile(pytest_p2="#") mod = types.ModuleType("temp") mod.__dict__["pytest_plugins"] = ["pytest_p1", "pytest_p2"] pytestpm.consider_module(mod) assert pytestpm.get_plugin("pytest_p1").__name__ == "pytest_p1" assert pytestpm.get_plugin("pytest_p2").__name__ == "pytest_p2" - def test_consider_module_import_module(self, testdir, _config_for_test) -> None: + def test_consider_module_import_module( + self, pytester: Pytester, _config_for_test: Config + ) -> None: pytestpm = _config_for_test.pluginmanager mod = types.ModuleType("x") mod.__dict__["pytest_plugins"] = "pytest_a" - aplugin = testdir.makepyfile(pytest_a="#") - reprec = testdir.make_hook_recorder(pytestpm) - testdir.syspathinsert(aplugin.dirpath()) + aplugin = pytester.makepyfile(pytest_a="#") + reprec = pytester.make_hook_recorder(pytestpm) + pytester.syspathinsert(aplugin.parent) pytestpm.consider_module(mod) call = reprec.getcall(pytestpm.hook.pytest_plugin_registered.name) assert call.plugin.__name__ == "pytest_a" @@ -245,30 +255,37 @@ def test_consider_module_import_module(self, testdir, _config_for_test) -> None: values = reprec.getcalls("pytest_plugin_registered") assert len(values) == 1 - def test_consider_env_fails_to_import(self, monkeypatch, pytestpm): + def test_consider_env_fails_to_import( + self, monkeypatch: MonkeyPatch, pytestpm: PytestPluginManager + ) -> None: monkeypatch.setenv("PYTEST_PLUGINS", "nonexisting", prepend=",") with pytest.raises(ImportError): pytestpm.consider_env() @pytest.mark.filterwarnings("always") - def test_plugin_skip(self, testdir, monkeypatch): - p = testdir.makepyfile( + def test_plugin_skip(self, pytester: Pytester, monkeypatch: MonkeyPatch) -> None: + p = pytester.makepyfile( skipping1=""" import pytest pytest.skip("hello", allow_module_level=True) """ ) - p.copy(p.dirpath("skipping2.py")) + shutil.copy(p, p.with_name("skipping2.py")) monkeypatch.setenv("PYTEST_PLUGINS", "skipping2") - result = testdir.runpytest("-p", "skipping1", syspathinsert=True) + result = pytester.runpytest("-p", "skipping1", syspathinsert=True) assert result.ret == ExitCode.NO_TESTS_COLLECTED result.stdout.fnmatch_lines( ["*skipped plugin*skipping1*hello*", "*skipped plugin*skipping2*hello*"] ) - def test_consider_env_plugin_instantiation(self, testdir, monkeypatch, pytestpm): - testdir.syspathinsert() - testdir.makepyfile(xy123="#") + def test_consider_env_plugin_instantiation( + self, + pytester: Pytester, + monkeypatch: MonkeyPatch, + pytestpm: PytestPluginManager, + ) -> None: + pytester.syspathinsert() + pytester.makepyfile(xy123="#") monkeypatch.setitem(os.environ, "PYTEST_PLUGINS", "xy123") l1 = len(pytestpm.get_plugins()) pytestpm.consider_env() @@ -279,9 +296,11 @@ def test_consider_env_plugin_instantiation(self, testdir, monkeypatch, pytestpm) l3 = len(pytestpm.get_plugins()) assert l2 == l3 - def test_pluginmanager_ENV_startup(self, testdir, monkeypatch): - testdir.makepyfile(pytest_x500="#") - p = testdir.makepyfile( + def test_pluginmanager_ENV_startup( + self, pytester: Pytester, monkeypatch: MonkeyPatch + ) -> None: + pytester.makepyfile(pytest_x500="#") + p = pytester.makepyfile( """ import pytest def test_hello(pytestconfig): @@ -290,17 +309,19 @@ def test_hello(pytestconfig): """ ) monkeypatch.setenv("PYTEST_PLUGINS", "pytest_x500", prepend=",") - result = testdir.runpytest(p, syspathinsert=True) + result = pytester.runpytest(p, syspathinsert=True) assert result.ret == 0 result.stdout.fnmatch_lines(["*1 passed*"]) - def test_import_plugin_importname(self, testdir, pytestpm): + def test_import_plugin_importname( + self, pytester: Pytester, pytestpm: PytestPluginManager + ) -> None: pytest.raises(ImportError, pytestpm.import_plugin, "qweqwex.y") pytest.raises(ImportError, pytestpm.import_plugin, "pytest_qweqwx.y") - testdir.syspathinsert() + pytester.syspathinsert() pluginname = "pytest_hello" - testdir.makepyfile(**{pluginname: ""}) + pytester.makepyfile(**{pluginname: ""}) pytestpm.import_plugin("pytest_hello") len1 = len(pytestpm.get_plugins()) pytestpm.import_plugin("pytest_hello") @@ -311,25 +332,29 @@ def test_import_plugin_importname(self, testdir, pytestpm): plugin2 = pytestpm.get_plugin("pytest_hello") assert plugin2 is plugin1 - def test_import_plugin_dotted_name(self, testdir, pytestpm): + def test_import_plugin_dotted_name( + self, pytester: Pytester, pytestpm: PytestPluginManager + ) -> None: pytest.raises(ImportError, pytestpm.import_plugin, "qweqwex.y") pytest.raises(ImportError, pytestpm.import_plugin, "pytest_qweqwex.y") - testdir.syspathinsert() - testdir.mkpydir("pkg").join("plug.py").write("x=3") + pytester.syspathinsert() + pytester.mkpydir("pkg").joinpath("plug.py").write_text("x=3") pluginname = "pkg.plug" pytestpm.import_plugin(pluginname) mod = pytestpm.get_plugin("pkg.plug") assert mod.x == 3 - def test_consider_conftest_deps(self, testdir, pytestpm): - mod = testdir.makepyfile("pytest_plugins='xyz'").pyimport() + def test_consider_conftest_deps( + self, pytester: Pytester, pytestpm: PytestPluginManager, + ) -> None: + mod = import_path(pytester.makepyfile("pytest_plugins='xyz'")) with pytest.raises(ImportError): pytestpm.consider_conftest(mod) class TestPytestPluginManagerBootstrapming: - def test_preparse_args(self, pytestpm): + def test_preparse_args(self, pytestpm: PytestPluginManager) -> None: pytest.raises( ImportError, lambda: pytestpm.consider_preparse(["xyz", "-p", "hello123"]) ) @@ -346,7 +371,7 @@ def test_preparse_args(self, pytestpm): with pytest.raises(UsageError, match="^plugin main cannot be disabled$"): pytestpm.consider_preparse(["-p", "no:main"]) - def test_plugin_prevent_register(self, pytestpm): + def test_plugin_prevent_register(self, pytestpm: PytestPluginManager) -> None: pytestpm.consider_preparse(["xyz", "-p", "no:abc"]) l1 = pytestpm.get_plugins() pytestpm.register(42, name="abc") @@ -354,7 +379,9 @@ def test_plugin_prevent_register(self, pytestpm): assert len(l2) == len(l1) assert 42 not in l2 - def test_plugin_prevent_register_unregistered_alredy_registered(self, pytestpm): + def test_plugin_prevent_register_unregistered_alredy_registered( + self, pytestpm: PytestPluginManager + ) -> None: pytestpm.register(42, name="abc") l1 = pytestpm.get_plugins() assert 42 in l1 @@ -363,8 +390,8 @@ def test_plugin_prevent_register_unregistered_alredy_registered(self, pytestpm): assert 42 not in l2 def test_plugin_prevent_register_stepwise_on_cacheprovider_unregister( - self, pytestpm - ): + self, pytestpm: PytestPluginManager + ) -> None: """From PR #4304: The only way to unregister a module is documented at the end of https://docs.pytest.org/en/stable/plugins.html. @@ -380,7 +407,7 @@ def test_plugin_prevent_register_stepwise_on_cacheprovider_unregister( assert 42 not in l2 assert 43 not in l2 - def test_blocked_plugin_can_be_used(self, pytestpm): + def test_blocked_plugin_can_be_used(self, pytestpm: PytestPluginManager) -> None: pytestpm.consider_preparse(["xyz", "-p", "no:abc", "-p", "abc"]) assert pytestpm.has_plugin("abc") diff --git a/testing/test_reports.py b/testing/test_reports.py index b97b1fc2970..b376f6198ae 100644 --- a/testing/test_reports.py +++ b/testing/test_reports.py @@ -1,29 +1,30 @@ -from pathlib import Path from typing import Sequence from typing import Union +import py.path + import pytest from _pytest._code.code import ExceptionChainRepr from _pytest._code.code import ExceptionRepr from _pytest.config import Config -from _pytest.pytester import Testdir +from _pytest.pytester import Pytester from _pytest.reports import CollectReport from _pytest.reports import TestReport class TestReportSerialization: - def test_xdist_longrepr_to_str_issue_241(self, testdir: Testdir) -> None: + def test_xdist_longrepr_to_str_issue_241(self, pytester: Pytester) -> None: """Regarding issue pytest-xdist#241. This test came originally from test_remote.py in xdist (ca03269). """ - testdir.makepyfile( + pytester.makepyfile( """ def test_a(): assert False def test_b(): pass """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reports = reprec.getreports("pytest_runtest_logreport") assert len(reports) == 6 test_a_call = reports[1] @@ -35,12 +36,12 @@ def test_b(): pass assert test_b_call.outcome == "passed" assert test_b_call._to_json()["longrepr"] is None - def test_xdist_report_longrepr_reprcrash_130(self, testdir: Testdir) -> None: + def test_xdist_report_longrepr_reprcrash_130(self, pytester: Pytester) -> None: """Regarding issue pytest-xdist#130 This test came originally from test_remote.py in xdist (ca03269). """ - reprec = testdir.inline_runsource( + reprec = pytester.inline_runsource( """ def test_fail(): assert False, 'Expected Message' @@ -74,14 +75,14 @@ def test_fail(): # Missing section attribute PR171 assert added_section in a.longrepr.sections - def test_reprentries_serialization_170(self, testdir: Testdir) -> None: + def test_reprentries_serialization_170(self, pytester: Pytester) -> None: """Regarding issue pytest-xdist#170 This test came originally from test_remote.py in xdist (ca03269). """ from _pytest._code.code import ReprEntry - reprec = testdir.inline_runsource( + reprec = pytester.inline_runsource( """ def test_repr_entry(): x = 0 @@ -120,14 +121,14 @@ def test_repr_entry(): assert rep_entry.reprlocals.lines == a_entry.reprlocals.lines assert rep_entry.style == a_entry.style - def test_reprentries_serialization_196(self, testdir: Testdir) -> None: + def test_reprentries_serialization_196(self, pytester: Pytester) -> None: """Regarding issue pytest-xdist#196 This test came originally from test_remote.py in xdist (ca03269). """ from _pytest._code.code import ReprEntryNative - reprec = testdir.inline_runsource( + reprec = pytester.inline_runsource( """ def test_repr_entry_native(): x = 0 @@ -149,9 +150,9 @@ def test_repr_entry_native(): assert isinstance(rep_entries[i], ReprEntryNative) assert rep_entries[i].lines == a_entries[i].lines - def test_itemreport_outcomes(self, testdir: Testdir) -> None: + def test_itemreport_outcomes(self, pytester: Pytester) -> None: # This test came originally from test_remote.py in xdist (ca03269). - reprec = testdir.inline_runsource( + reprec = pytester.inline_runsource( """ import pytest def test_pass(): pass @@ -183,9 +184,9 @@ def test_xfail_imperative(): if rep.failed: assert newrep.longreprtext == rep.longreprtext - def test_collectreport_passed(self, testdir: Testdir) -> None: + def test_collectreport_passed(self, pytester: Pytester) -> None: """This test came originally from test_remote.py in xdist (ca03269).""" - reprec = testdir.inline_runsource("def test_func(): pass") + reprec = pytester.inline_runsource("def test_func(): pass") reports = reprec.getreports("pytest_collectreport") for rep in reports: d = rep._to_json() @@ -194,9 +195,9 @@ def test_collectreport_passed(self, testdir: Testdir) -> None: assert newrep.failed == rep.failed assert newrep.skipped == rep.skipped - def test_collectreport_fail(self, testdir: Testdir) -> None: + def test_collectreport_fail(self, pytester: Pytester) -> None: """This test came originally from test_remote.py in xdist (ca03269).""" - reprec = testdir.inline_runsource("qwe abc") + reprec = pytester.inline_runsource("qwe abc") reports = reprec.getreports("pytest_collectreport") assert reports for rep in reports: @@ -208,9 +209,9 @@ def test_collectreport_fail(self, testdir: Testdir) -> None: if rep.failed: assert newrep.longrepr == str(rep.longrepr) - def test_extended_report_deserialization(self, testdir: Testdir) -> None: + def test_extended_report_deserialization(self, pytester: Pytester) -> None: """This test came originally from test_remote.py in xdist (ca03269).""" - reprec = testdir.inline_runsource("qwe abc") + reprec = pytester.inline_runsource("qwe abc") reports = reprec.getreports("pytest_collectreport") assert reports for rep in reports: @@ -224,33 +225,33 @@ def test_extended_report_deserialization(self, testdir: Testdir) -> None: if rep.failed: assert newrep.longrepr == str(rep.longrepr) - def test_paths_support(self, testdir: Testdir) -> None: + def test_paths_support(self, pytester: Pytester) -> None: """Report attributes which are py.path or pathlib objects should become strings.""" - testdir.makepyfile( + pytester.makepyfile( """ def test_a(): assert False """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reports = reprec.getreports("pytest_runtest_logreport") assert len(reports) == 3 test_a_call = reports[1] - test_a_call.path1 = testdir.tmpdir # type: ignore[attr-defined] - test_a_call.path2 = Path(testdir.tmpdir) # type: ignore[attr-defined] + test_a_call.path1 = py.path.local(pytester.path) # type: ignore[attr-defined] + test_a_call.path2 = pytester.path # type: ignore[attr-defined] data = test_a_call._to_json() - assert data["path1"] == str(testdir.tmpdir) - assert data["path2"] == str(testdir.tmpdir) + assert data["path1"] == str(pytester.path) + assert data["path2"] == str(pytester.path) - def test_deserialization_failure(self, testdir: Testdir) -> None: + def test_deserialization_failure(self, pytester: Pytester) -> None: """Check handling of failure during deserialization of report types.""" - testdir.makepyfile( + pytester.makepyfile( """ def test_a(): assert False """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reports = reprec.getreports("pytest_runtest_logreport") assert len(reports) == 3 test_a_call = reports[1] @@ -265,9 +266,11 @@ def test_a(): TestReport._from_json(data) @pytest.mark.parametrize("report_class", [TestReport, CollectReport]) - def test_chained_exceptions(self, testdir: Testdir, tw_mock, report_class) -> None: + def test_chained_exceptions( + self, pytester: Pytester, tw_mock, report_class + ) -> None: """Check serialization/deserialization of report objects containing chained exceptions (#5786)""" - testdir.makepyfile( + pytester.makepyfile( """ def foo(): raise ValueError('value error') @@ -283,7 +286,7 @@ def test_a(): ) ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() if report_class is TestReport: reports: Union[ Sequence[TestReport], Sequence[CollectReport] @@ -338,14 +341,14 @@ def check_longrepr(longrepr: ExceptionChainRepr) -> None: # elsewhere and we do check the contents of the longrepr object after loading it. loaded_report.longrepr.toterminal(tw_mock) - def test_chained_exceptions_no_reprcrash(self, testdir: Testdir, tw_mock) -> None: + def test_chained_exceptions_no_reprcrash(self, pytester: Pytester, tw_mock) -> None: """Regression test for tracebacks without a reprcrash (#5971) This happens notably on exceptions raised by multiprocess.pool: the exception transfer from subprocess to main process creates an artificial exception, which ExceptionInfo can't obtain the ReprFileLocation from. """ - testdir.makepyfile( + pytester.makepyfile( """ from concurrent.futures import ProcessPoolExecutor @@ -358,8 +361,8 @@ def test_a(): """ ) - testdir.syspathinsert() - reprec = testdir.inline_run() + pytester.syspathinsert() + reprec = pytester.inline_run() reports = reprec.getreports("pytest_runtest_logreport") @@ -396,12 +399,13 @@ def check_longrepr(longrepr: object) -> None: loaded_report.longrepr.toterminal(tw_mock) def test_report_prevent_ConftestImportFailure_hiding_exception( - self, testdir: Testdir + self, pytester: Pytester ) -> None: - sub_dir = testdir.tmpdir.join("ns").ensure_dir() - sub_dir.join("conftest").new(ext=".py").write("import unknown") + sub_dir = pytester.path.joinpath("ns") + sub_dir.mkdir() + sub_dir.joinpath("conftest.py").write_text("import unknown") - result = testdir.runpytest_subprocess(".") + result = pytester.runpytest_subprocess(".") result.stdout.fnmatch_lines(["E *Error: No module named 'unknown'"]) result.stdout.no_fnmatch_line("ERROR - *ConftestImportFailure*") @@ -409,14 +413,14 @@ def test_report_prevent_ConftestImportFailure_hiding_exception( class TestHooks: """Test that the hooks are working correctly for plugins""" - def test_test_report(self, testdir: Testdir, pytestconfig: Config) -> None: - testdir.makepyfile( + def test_test_report(self, pytester: Pytester, pytestconfig: Config) -> None: + pytester.makepyfile( """ def test_a(): assert False def test_b(): pass """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reports = reprec.getreports("pytest_runtest_logreport") assert len(reports) == 6 for rep in reports: @@ -431,14 +435,14 @@ def test_b(): pass assert new_rep.when == rep.when assert new_rep.outcome == rep.outcome - def test_collect_report(self, testdir: Testdir, pytestconfig: Config) -> None: - testdir.makepyfile( + def test_collect_report(self, pytester: Pytester, pytestconfig: Config) -> None: + pytester.makepyfile( """ def test_a(): assert False def test_b(): pass """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reports = reprec.getreports("pytest_collectreport") assert len(reports) == 2 for rep in reports: @@ -457,14 +461,14 @@ def test_b(): pass "hook_name", ["pytest_runtest_logreport", "pytest_collectreport"] ) def test_invalid_report_types( - self, testdir: Testdir, pytestconfig: Config, hook_name: str + self, pytester: Pytester, pytestconfig: Config, hook_name: str ) -> None: - testdir.makepyfile( + pytester.makepyfile( """ def test_a(): pass """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reports = reprec.getreports(hook_name) assert reports rep = reports[0] diff --git a/testing/test_runner.py b/testing/test_runner.py index a1f1db48d06..8ce0f67354f 100644 --- a/testing/test_runner.py +++ b/testing/test_runner.py @@ -2,26 +2,28 @@ import os import sys import types +from pathlib import Path from typing import Dict from typing import List from typing import Tuple from typing import Type -import py - -import _pytest._code import pytest from _pytest import outcomes from _pytest import reports from _pytest import runner +from _pytest._code import ExceptionInfo +from _pytest._code.code import ExceptionChainRepr from _pytest.config import ExitCode +from _pytest.monkeypatch import MonkeyPatch from _pytest.outcomes import OutcomeException +from _pytest.pytester import Pytester class TestSetupState: - def test_setup(self, testdir) -> None: + def test_setup(self, pytester: Pytester) -> None: ss = runner.SetupState() - item = testdir.getitem("def test_func(): pass") + item = pytester.getitem("def test_func(): pass") values = [1] ss.prepare(item) ss.addfinalizer(values.pop, colitem=item) @@ -29,15 +31,15 @@ def test_setup(self, testdir) -> None: ss._pop_and_teardown() assert not values - def test_teardown_exact_stack_empty(self, testdir) -> None: - item = testdir.getitem("def test_func(): pass") + def test_teardown_exact_stack_empty(self, pytester: Pytester) -> None: + item = pytester.getitem("def test_func(): pass") ss = runner.SetupState() 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) -> None: - item = testdir.getitem( + def test_setup_fails_and_failure_is_cached(self, pytester: Pytester) -> None: + item = pytester.getitem( """ def setup_module(mod): raise ValueError(42) @@ -48,7 +50,7 @@ def test_func(): pass pytest.raises(ValueError, lambda: ss.prepare(item)) pytest.raises(ValueError, lambda: ss.prepare(item)) - def test_teardown_multiple_one_fails(self, testdir) -> None: + def test_teardown_multiple_one_fails(self, pytester: Pytester) -> None: r = [] def fin1(): @@ -60,7 +62,7 @@ def fin2(): def fin3(): r.append("fin3") - item = testdir.getitem("def test_func(): pass") + item = pytester.getitem("def test_func(): pass") ss = runner.SetupState() ss.addfinalizer(fin1, item) ss.addfinalizer(fin2, item) @@ -70,7 +72,7 @@ def fin3(): assert err.value.args == ("oops",) assert r == ["fin3", "fin1"] - def test_teardown_multiple_fail(self, testdir) -> None: + def test_teardown_multiple_fail(self, pytester: Pytester) -> None: # Ensure the first exception is the one which is re-raised. # Ideally both would be reported however. def fin1(): @@ -79,7 +81,7 @@ def fin1(): def fin2(): raise Exception("oops2") - item = testdir.getitem("def test_func(): pass") + item = pytester.getitem("def test_func(): pass") ss = runner.SetupState() ss.addfinalizer(fin1, item) ss.addfinalizer(fin2, item) @@ -87,7 +89,7 @@ def fin2(): ss._callfinalizers(item) assert err.value.args == ("oops2",) - def test_teardown_multiple_scopes_one_fails(self, testdir) -> None: + def test_teardown_multiple_scopes_one_fails(self, pytester: Pytester) -> None: module_teardown = [] def fin_func(): @@ -96,7 +98,7 @@ def fin_func(): def fin_module(): module_teardown.append("fin_module") - item = testdir.getitem("def test_func(): pass") + item = pytester.getitem("def test_func(): pass") ss = runner.SetupState() ss.addfinalizer(fin_module, item.listchain()[-2]) ss.addfinalizer(fin_func, item) @@ -107,8 +109,8 @@ def fin_module(): class BaseFunctionalTests: - def test_passfunction(self, testdir) -> None: - reports = testdir.runitem( + def test_passfunction(self, pytester: Pytester) -> None: + reports = pytester.runitem( """ def test_func(): pass @@ -120,8 +122,8 @@ def test_func(): assert rep.outcome == "passed" assert not rep.longrepr - def test_failfunction(self, testdir) -> None: - reports = testdir.runitem( + def test_failfunction(self, pytester: Pytester) -> None: + reports = pytester.runitem( """ def test_func(): assert 0 @@ -135,8 +137,8 @@ def test_func(): assert rep.outcome == "failed" # assert isinstance(rep.longrepr, ReprExceptionInfo) - def test_skipfunction(self, testdir) -> None: - reports = testdir.runitem( + def test_skipfunction(self, pytester: Pytester) -> None: + reports = pytester.runitem( """ import pytest def test_func(): @@ -155,8 +157,8 @@ def test_func(): # assert rep.skipped.location.path # assert not rep.skipped.failurerepr - def test_skip_in_setup_function(self, testdir) -> None: - reports = testdir.runitem( + def test_skip_in_setup_function(self, pytester: Pytester) -> None: + reports = pytester.runitem( """ import pytest def setup_function(func): @@ -176,8 +178,8 @@ def test_func(): assert len(reports) == 2 assert reports[1].passed # teardown - def test_failure_in_setup_function(self, testdir) -> None: - reports = testdir.runitem( + def test_failure_in_setup_function(self, pytester: Pytester) -> None: + reports = pytester.runitem( """ import pytest def setup_function(func): @@ -193,8 +195,8 @@ def test_func(): assert rep.when == "setup" assert len(reports) == 2 - def test_failure_in_teardown_function(self, testdir) -> None: - reports = testdir.runitem( + def test_failure_in_teardown_function(self, pytester: Pytester) -> None: + reports = pytester.runitem( """ import pytest def teardown_function(func): @@ -213,8 +215,8 @@ def test_func(): # assert rep.longrepr.reprcrash.lineno == 3 # assert rep.longrepr.reprtraceback.reprentries - def test_custom_failure_repr(self, testdir) -> None: - testdir.makepyfile( + def test_custom_failure_repr(self, pytester: Pytester) -> None: + pytester.makepyfile( conftest=""" import pytest class Function(pytest.Function): @@ -222,7 +224,7 @@ def repr_failure(self, excinfo): return "hello" """ ) - reports = testdir.runitem( + reports = pytester.runitem( """ import pytest def test_func(): @@ -238,8 +240,8 @@ def test_func(): # assert rep.failed.where.path.basename == "test_func.py" # assert rep.failed.failurerepr == "hello" - def test_teardown_final_returncode(self, testdir) -> None: - rec = testdir.inline_runsource( + def test_teardown_final_returncode(self, pytester: Pytester) -> None: + rec = pytester.inline_runsource( """ def test_func(): pass @@ -249,8 +251,8 @@ def teardown_function(func): ) assert rec.ret == 1 - def test_logstart_logfinish_hooks(self, testdir) -> None: - rec = testdir.inline_runsource( + def test_logstart_logfinish_hooks(self, pytester: Pytester) -> None: + rec = pytester.inline_runsource( """ import pytest def test_func(): @@ -266,8 +268,8 @@ def test_func(): assert rep.nodeid == "test_logstart_logfinish_hooks.py::test_func" assert rep.location == ("test_logstart_logfinish_hooks.py", 1, "test_func") - def test_exact_teardown_issue90(self, testdir) -> None: - rec = testdir.inline_runsource( + def test_exact_teardown_issue90(self, pytester: Pytester) -> None: + rec = pytester.inline_runsource( """ import pytest @@ -306,9 +308,9 @@ def teardown_function(func): assert reps[5].nodeid.endswith("test_func") assert reps[5].failed - def test_exact_teardown_issue1206(self, testdir) -> None: + def test_exact_teardown_issue1206(self, pytester: Pytester) -> None: """Issue shadowing error with wrong number of arguments on teardown_method.""" - rec = testdir.inline_runsource( + rec = pytester.inline_runsource( """ import pytest @@ -335,14 +337,19 @@ def test_method(self): assert reps[2].nodeid.endswith("test_method") assert reps[2].failed assert reps[2].when == "teardown" - assert reps[2].longrepr.reprcrash.message in ( + longrepr = reps[2].longrepr + assert isinstance(longrepr, ExceptionChainRepr) + assert longrepr.reprcrash + assert longrepr.reprcrash.message in ( "TypeError: teardown_method() missing 2 required positional arguments: 'y' and 'z'", # Python >= 3.10 "TypeError: TestClass.teardown_method() missing 2 required positional arguments: 'y' and 'z'", ) - def test_failure_in_setup_function_ignores_custom_repr(self, testdir) -> None: - testdir.makepyfile( + def test_failure_in_setup_function_ignores_custom_repr( + self, pytester: Pytester + ) -> None: + pytester.makepyfile( conftest=""" import pytest class Function(pytest.Function): @@ -350,7 +357,7 @@ def repr_failure(self, excinfo): assert 0 """ ) - reports = testdir.runitem( + reports = pytester.runitem( """ def setup_function(func): raise ValueError(42) @@ -369,9 +376,9 @@ def test_func(): # assert rep.outcome.where.path.basename == "test_func.py" # assert instanace(rep.failed.failurerepr, PythonFailureRepr) - def test_systemexit_does_not_bail_out(self, testdir) -> None: + def test_systemexit_does_not_bail_out(self, pytester: Pytester) -> None: try: - reports = testdir.runitem( + reports = pytester.runitem( """ def test_func(): raise SystemExit(42) @@ -383,9 +390,9 @@ def test_func(): assert rep.failed assert rep.when == "call" - def test_exit_propagates(self, testdir) -> None: + def test_exit_propagates(self, pytester: Pytester) -> None: try: - testdir.runitem( + pytester.runitem( """ import pytest def test_func(): @@ -405,9 +412,9 @@ def f(item): return f - def test_keyboardinterrupt_propagates(self, testdir) -> None: + def test_keyboardinterrupt_propagates(self, pytester: Pytester) -> None: try: - testdir.runitem( + pytester.runitem( """ def test_func(): raise KeyboardInterrupt("fake") @@ -420,8 +427,8 @@ def test_func(): class TestSessionReports: - def test_collect_result(self, testdir) -> None: - col = testdir.getmodulecol( + def test_collect_result(self, pytester: Pytester) -> None: + col = pytester.getmodulecol( """ def test_func1(): pass @@ -489,8 +496,8 @@ def raise_assertion(): @pytest.mark.xfail -def test_runtest_in_module_ordering(testdir) -> None: - p1 = testdir.makepyfile( +def test_runtest_in_module_ordering(pytester: Pytester) -> None: + p1 = pytester.makepyfile( """ import pytest def pytest_runtest_setup(item): # runs after class-level! @@ -517,7 +524,7 @@ def pytest_runtest_teardown(item): del item.function.mylist """ ) - result = testdir.runpytest(p1) + result = pytester.runpytest(p1) result.stdout.fnmatch_lines(["*2 passed*"]) @@ -547,8 +554,8 @@ def test_pytest_fail() -> None: assert s.startswith("Failed") -def test_pytest_exit_msg(testdir) -> None: - testdir.makeconftest( +def test_pytest_exit_msg(pytester: Pytester) -> None: + pytester.makeconftest( """ import pytest @@ -556,7 +563,7 @@ def pytest_configure(config): pytest.exit('oh noes') """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stderr.fnmatch_lines(["Exit: oh noes"]) @@ -570,22 +577,22 @@ def _strip_resource_warnings(lines): ] -def test_pytest_exit_returncode(testdir) -> None: - testdir.makepyfile( +def test_pytest_exit_returncode(pytester: Pytester) -> None: + pytester.makepyfile( """\ import pytest def test_foo(): pytest.exit("some exit msg", 99) """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["*! *Exit: some exit msg !*"]) assert _strip_resource_warnings(result.stderr.lines) == [] assert result.ret == 99 # It prints to stderr also in case of exit during pytest_sessionstart. - testdir.makeconftest( + pytester.makeconftest( """\ import pytest @@ -593,7 +600,7 @@ def pytest_sessionstart(): pytest.exit("during_sessionstart", 98) """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["*! *Exit: during_sessionstart !*"]) assert _strip_resource_warnings(result.stderr.lines) == [ "Exit: during_sessionstart" @@ -601,9 +608,9 @@ def pytest_sessionstart(): assert result.ret == 98 -def test_pytest_fail_notrace_runtest(testdir) -> None: +def test_pytest_fail_notrace_runtest(pytester: Pytester) -> None: """Test pytest.fail(..., pytrace=False) does not show tracebacks during test run.""" - testdir.makepyfile( + pytester.makepyfile( """ import pytest def test_hello(): @@ -612,14 +619,14 @@ def teardown_function(function): pytest.fail("world", pytrace=False) """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["world", "hello"]) result.stdout.no_fnmatch_line("*def teardown_function*") -def test_pytest_fail_notrace_collection(testdir) -> None: +def test_pytest_fail_notrace_collection(pytester: Pytester) -> None: """Test pytest.fail(..., pytrace=False) does not show tracebacks during collection.""" - testdir.makepyfile( + pytester.makepyfile( """ import pytest def some_internal_function(): @@ -627,17 +634,17 @@ def some_internal_function(): some_internal_function() """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["hello"]) result.stdout.no_fnmatch_line("*def some_internal_function()*") -def test_pytest_fail_notrace_non_ascii(testdir) -> None: +def test_pytest_fail_notrace_non_ascii(pytester: Pytester) -> None: """Fix pytest.fail with pytrace=False with non-ascii characters (#1178). This tests with native and unicode strings containing non-ascii chars. """ - testdir.makepyfile( + pytester.makepyfile( """\ import pytest @@ -645,28 +652,28 @@ def test_hello(): pytest.fail('oh oh: ☺', pytrace=False) """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["*test_hello*", "oh oh: ☺"]) result.stdout.no_fnmatch_line("*def test_hello*") -def test_pytest_no_tests_collected_exit_status(testdir) -> None: - result = testdir.runpytest() +def test_pytest_no_tests_collected_exit_status(pytester: Pytester) -> None: + result = pytester.runpytest() result.stdout.fnmatch_lines(["*collected 0 items*"]) assert result.ret == ExitCode.NO_TESTS_COLLECTED - testdir.makepyfile( + pytester.makepyfile( test_foo=""" def test_foo(): assert 1 """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["*collected 1 item*"]) result.stdout.fnmatch_lines(["*1 passed*"]) assert result.ret == ExitCode.OK - result = testdir.runpytest("-k nonmatch") + result = pytester.runpytest("-k nonmatch") result.stdout.fnmatch_lines(["*collected 1 item*"]) result.stdout.fnmatch_lines(["*1 deselected*"]) assert result.ret == ExitCode.NO_TESTS_COLLECTED @@ -677,7 +684,7 @@ def test_exception_printing_skip() -> None: try: pytest.skip("hello") except pytest.skip.Exception: - excinfo = _pytest._code.ExceptionInfo.from_current() + excinfo = ExceptionInfo.from_current() s = excinfo.exconly(tryshort=True) assert s.startswith("Skipped") @@ -698,10 +705,10 @@ def f(): excrepr = excinfo.getrepr() assert excrepr is not None assert excrepr.reprcrash is not None - path = py.path.local(excrepr.reprcrash.path) + path = Path(excrepr.reprcrash.path) # check that importorskip reports the actual call # in this test the test_runner.py file - assert path.purebasename == "test_runner" + assert path.stem == "test_runner" pytest.raises(SyntaxError, pytest.importorskip, "x y z") pytest.raises(SyntaxError, pytest.importorskip, "x=y") mod = types.ModuleType("hello123") @@ -712,9 +719,7 @@ def f(): mod2 = pytest.importorskip("hello123", minversion="1.3") assert mod2 == mod except pytest.skip.Exception: # pragma: no cover - assert False, "spurious skip: {}".format( - _pytest._code.ExceptionInfo.from_current() - ) + assert False, f"spurious skip: {ExceptionInfo.from_current()}" def test_importorskip_imports_last_module_part() -> None: @@ -732,14 +737,12 @@ def test_importorskip_dev_module(monkeypatch) -> None: with pytest.raises(pytest.skip.Exception): pytest.importorskip("mockmodule1", minversion="0.14.0") except pytest.skip.Exception: # pragma: no cover - assert False, "spurious skip: {}".format( - _pytest._code.ExceptionInfo.from_current() - ) + assert False, f"spurious skip: {ExceptionInfo.from_current()}" -def test_importorskip_module_level(testdir) -> None: +def test_importorskip_module_level(pytester: Pytester) -> None: """`importorskip` must be able to skip entire modules when used at module level.""" - testdir.makepyfile( + pytester.makepyfile( """ import pytest foobarbaz = pytest.importorskip("foobarbaz") @@ -748,13 +751,13 @@ def test_foo(): pass """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["*collected 0 items / 1 skipped*"]) -def test_importorskip_custom_reason(testdir) -> None: +def test_importorskip_custom_reason(pytester: Pytester) -> None: """Make sure custom reasons are used.""" - testdir.makepyfile( + pytester.makepyfile( """ import pytest foobarbaz = pytest.importorskip("foobarbaz2", reason="just because") @@ -763,13 +766,13 @@ def test_foo(): pass """ ) - result = testdir.runpytest("-ra") + result = pytester.runpytest("-ra") result.stdout.fnmatch_lines(["*just because*"]) result.stdout.fnmatch_lines(["*collected 0 items / 1 skipped*"]) -def test_pytest_cmdline_main(testdir) -> None: - p = testdir.makepyfile( +def test_pytest_cmdline_main(pytester: Pytester) -> None: + p = pytester.makepyfile( """ import pytest def test_hello(): @@ -786,8 +789,8 @@ def test_hello(): assert ret == 0 -def test_unicode_in_longrepr(testdir) -> None: - testdir.makeconftest( +def test_unicode_in_longrepr(pytester: Pytester) -> None: + pytester.makeconftest( """\ import pytest @pytest.hookimpl(hookwrapper=True) @@ -798,19 +801,19 @@ def pytest_runtest_makereport(): rep.longrepr = 'ä' """ ) - testdir.makepyfile( + pytester.makepyfile( """ def test_out(): assert 0 """ ) - result = testdir.runpytest() + result = pytester.runpytest() assert result.ret == 1 assert "UnicodeEncodeError" not in result.stderr.str() -def test_failure_in_setup(testdir) -> None: - testdir.makepyfile( +def test_failure_in_setup(pytester: Pytester) -> None: + pytester.makepyfile( """ def setup_module(): 0/0 @@ -818,24 +821,26 @@ def test_func(): pass """ ) - result = testdir.runpytest("--tb=line") + result = pytester.runpytest("--tb=line") result.stdout.no_fnmatch_line("*def setup_module*") -def test_makereport_getsource(testdir) -> None: - testdir.makepyfile( +def test_makereport_getsource(pytester: Pytester) -> None: + pytester.makepyfile( """ def test_foo(): if False: pass else: assert False """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.no_fnmatch_line("*INTERNALERROR*") result.stdout.fnmatch_lines(["*else: assert False*"]) -def test_makereport_getsource_dynamic_code(testdir, monkeypatch) -> None: +def test_makereport_getsource_dynamic_code( + pytester: Pytester, monkeypatch: MonkeyPatch +) -> None: """Test that exception in dynamically generated code doesn't break getting the source line.""" import inspect @@ -849,7 +854,7 @@ def findsource(obj): monkeypatch.setattr(inspect, "findsource", findsource) - testdir.makepyfile( + pytester.makepyfile( """ import pytest @@ -861,7 +866,7 @@ def test_fix(foo): assert False """ ) - result = testdir.runpytest("-vv") + result = pytester.runpytest("-vv") result.stdout.no_fnmatch_line("*INTERNALERROR*") result.stdout.fnmatch_lines(["*test_fix*", "*fixture*'missing'*not found*"]) @@ -896,12 +901,12 @@ def runtest(self): assert not hasattr(sys, "last_traceback") -def test_current_test_env_var(testdir, monkeypatch) -> None: +def test_current_test_env_var(pytester: Pytester, monkeypatch: MonkeyPatch) -> None: pytest_current_test_vars: List[Tuple[str, str]] = [] monkeypatch.setattr( sys, "pytest_current_test_vars", pytest_current_test_vars, raising=False ) - testdir.makepyfile( + pytester.makepyfile( """ import pytest import sys @@ -917,7 +922,7 @@ def test(fix): sys.pytest_current_test_vars.append(('call', os.environ['PYTEST_CURRENT_TEST'])) """ ) - result = testdir.runpytest_inprocess() + result = pytester.runpytest_inprocess() assert result.ret == 0 test_id = "test_current_test_env_var.py::test" assert pytest_current_test_vars == [ @@ -934,8 +939,8 @@ class TestReportContents: def getrunner(self): return lambda item: runner.runtestprotocol(item, log=False) - def test_longreprtext_pass(self, testdir) -> None: - reports = testdir.runitem( + def test_longreprtext_pass(self, pytester: Pytester) -> None: + reports = pytester.runitem( """ def test_func(): pass @@ -944,9 +949,9 @@ def test_func(): rep = reports[1] assert rep.longreprtext == "" - def test_longreprtext_skip(self, testdir) -> None: + def test_longreprtext_skip(self, pytester: Pytester) -> None: """TestReport.longreprtext can handle non-str ``longrepr`` attributes (#7559)""" - reports = testdir.runitem( + reports = pytester.runitem( """ import pytest def test_func(): @@ -957,22 +962,22 @@ def test_func(): assert isinstance(call_rep.longrepr, tuple) assert "Skipped" in call_rep.longreprtext - def test_longreprtext_collect_skip(self, testdir) -> None: + def test_longreprtext_collect_skip(self, pytester: Pytester) -> None: """CollectReport.longreprtext can handle non-str ``longrepr`` attributes (#7559)""" - testdir.makepyfile( + pytester.makepyfile( """ import pytest pytest.skip(allow_module_level=True) """ ) - rec = testdir.inline_run() + rec = pytester.inline_run() calls = rec.getcalls("pytest_collectreport") _, call = calls assert isinstance(call.report.longrepr, tuple) assert "Skipped" in call.report.longreprtext - def test_longreprtext_failure(self, testdir) -> None: - reports = testdir.runitem( + def test_longreprtext_failure(self, pytester: Pytester) -> None: + reports = pytester.runitem( """ def test_func(): x = 1 @@ -982,8 +987,8 @@ def test_func(): rep = reports[1] assert "assert 1 == 4" in rep.longreprtext - def test_captured_text(self, testdir) -> None: - reports = testdir.runitem( + def test_captured_text(self, pytester: Pytester) -> None: + reports = pytester.runitem( """ import pytest import sys @@ -1012,8 +1017,8 @@ def test_func(fix): assert call.capstderr == "setup: stderr\ncall: stderr\n" assert teardown.capstderr == "setup: stderr\ncall: stderr\nteardown: stderr\n" - def test_no_captured_text(self, testdir) -> None: - reports = testdir.runitem( + def test_no_captured_text(self, pytester: Pytester) -> None: + reports = pytester.runitem( """ def test_func(): pass @@ -1023,8 +1028,8 @@ def test_func(): assert rep.capstdout == "" assert rep.capstderr == "" - def test_longrepr_type(self, testdir) -> None: - reports = testdir.runitem( + def test_longrepr_type(self, pytester: Pytester) -> None: + reports = pytester.runitem( """ import pytest def test_func(): @@ -1032,7 +1037,7 @@ def test_func(): """ ) rep = reports[1] - assert isinstance(rep.longrepr, _pytest._code.code.ExceptionRepr) + assert isinstance(rep.longrepr, ExceptionChainRepr) def test_outcome_exception_bad_msg() -> None: From 9ccbf5b89906604dc76daee1fc07c1d26b6b5128 Mon Sep 17 00:00:00 2001 From: Jakob van Santen Date: Tue, 15 Dec 2020 12:49:29 +0100 Subject: [PATCH 011/630] python_api: handle array-like args in approx() (#8137) --- changelog/8132.bugfix.rst | 10 ++++++++++ src/_pytest/python_api.py | 33 +++++++++++++++++++++++++++------ testing/python/approx.py | 30 ++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 6 deletions(-) create mode 100644 changelog/8132.bugfix.rst diff --git a/changelog/8132.bugfix.rst b/changelog/8132.bugfix.rst new file mode 100644 index 00000000000..5be5e567491 --- /dev/null +++ b/changelog/8132.bugfix.rst @@ -0,0 +1,10 @@ +Fixed regression in ``approx``: in 6.2.0 ``approx`` no longer raises +``TypeError`` when dealing with non-numeric types, falling back to normal comparison. +Before 6.2.0, array types like tf.DeviceArray fell through to the scalar case, +and happened to compare correctly to a scalar if they had only one element. +After 6.2.0, these types began failing, because they inherited neither from +standard Python number hierarchy nor from ``numpy.ndarray``. + +``approx`` now converts arguments to ``numpy.ndarray`` if they expose the array +protocol and are not scalars. This treats array-like objects like numpy arrays, +regardless of size. diff --git a/src/_pytest/python_api.py b/src/_pytest/python_api.py index bae2076892b..81ce4f89539 100644 --- a/src/_pytest/python_api.py +++ b/src/_pytest/python_api.py @@ -15,9 +15,14 @@ from typing import Pattern from typing import Tuple from typing import Type +from typing import TYPE_CHECKING from typing import TypeVar from typing import Union +if TYPE_CHECKING: + from numpy import ndarray + + import _pytest._code from _pytest.compat import final from _pytest.compat import STRING_TYPES @@ -232,10 +237,11 @@ def __repr__(self) -> str: def __eq__(self, actual) -> bool: """Return whether the given value is equal to the expected value within the pre-specified tolerance.""" - if _is_numpy_array(actual): + asarray = _as_numpy_array(actual) + if asarray is not None: # Call ``__eq__()`` manually to prevent infinite-recursion with # numpy<1.13. See #3748. - return all(self.__eq__(a) for a in actual.flat) + return all(self.__eq__(a) for a in asarray.flat) # Short-circuit exact equality. if actual == self.expected: @@ -521,6 +527,7 @@ def approx(expected, rel=None, abs=None, nan_ok: bool = False) -> ApproxBase: elif isinstance(expected, Mapping): cls = ApproxMapping elif _is_numpy_array(expected): + expected = _as_numpy_array(expected) cls = ApproxNumpy elif ( isinstance(expected, Iterable) @@ -536,16 +543,30 @@ def approx(expected, rel=None, abs=None, nan_ok: bool = False) -> ApproxBase: def _is_numpy_array(obj: object) -> bool: - """Return true if the given object is a numpy array. + """ + Return true if the given object is implicitly convertible to ndarray, + and numpy is already imported. + """ + return _as_numpy_array(obj) is not None + - A special effort is made to avoid importing numpy unless it's really necessary. +def _as_numpy_array(obj: object) -> Optional["ndarray"]: + """ + Return an ndarray if the given object is implicitly convertible to ndarray, + and numpy is already imported, otherwise None. """ import sys np: Any = sys.modules.get("numpy") if np is not None: - return isinstance(obj, np.ndarray) - return False + # avoid infinite recursion on numpy scalars, which have __array__ + if np.isscalar(obj): + return None + elif isinstance(obj, np.ndarray): + return obj + elif hasattr(obj, "__array__") or hasattr("obj", "__array_interface__"): + return np.asarray(obj) + return None # builtin pytest.raises helper diff --git a/testing/python/approx.py b/testing/python/approx.py index 91c1f3f85de..e76d6b774d6 100644 --- a/testing/python/approx.py +++ b/testing/python/approx.py @@ -447,6 +447,36 @@ def test_numpy_array_wrong_shape(self): assert a12 != approx(a21) assert a21 != approx(a12) + def test_numpy_array_protocol(self): + """ + array-like objects such as tensorflow's DeviceArray are handled like ndarray. + See issue #8132 + """ + np = pytest.importorskip("numpy") + + class DeviceArray: + def __init__(self, value, size): + self.value = value + self.size = size + + def __array__(self): + return self.value * np.ones(self.size) + + class DeviceScalar: + def __init__(self, value): + self.value = value + + def __array__(self): + return np.array(self.value) + + expected = 1 + actual = 1 + 1e-6 + assert approx(expected) == DeviceArray(actual, size=1) + assert approx(expected) == DeviceArray(actual, size=2) + assert approx(expected) == DeviceScalar(actual) + assert approx(DeviceScalar(expected)) == actual + assert approx(DeviceScalar(expected)) == DeviceScalar(actual) + def test_doctests(self, mocked_doctest_runner) -> None: import doctest From 56600414df31431f1e0c2d964d0ad6761f83dd5d Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Tue, 15 Dec 2020 12:39:06 -0300 Subject: [PATCH 012/630] Merge pull request #8149 from pytest-dev/release-6.2.1 Prepare release 6.2.1 (cherry picked from commit a566eb9c7085d7732127420bd7ce5ec1f7319fba) --- changelog/7678.bugfix.rst | 2 -- changelog/8132.bugfix.rst | 10 ---------- doc/en/announce/index.rst | 1 + doc/en/announce/release-6.2.1.rst | 20 ++++++++++++++++++++ doc/en/changelog.rst | 22 ++++++++++++++++++++++ doc/en/getting-started.rst | 2 +- 6 files changed, 44 insertions(+), 13 deletions(-) delete mode 100644 changelog/7678.bugfix.rst delete mode 100644 changelog/8132.bugfix.rst create mode 100644 doc/en/announce/release-6.2.1.rst diff --git a/changelog/7678.bugfix.rst b/changelog/7678.bugfix.rst deleted file mode 100644 index 4adc6ffd119..00000000000 --- a/changelog/7678.bugfix.rst +++ /dev/null @@ -1,2 +0,0 @@ -Fixed bug where ``ImportPathMismatchError`` would be raised for files compiled in -the host and loaded later from an UNC mounted path (Windows). diff --git a/changelog/8132.bugfix.rst b/changelog/8132.bugfix.rst deleted file mode 100644 index 5be5e567491..00000000000 --- a/changelog/8132.bugfix.rst +++ /dev/null @@ -1,10 +0,0 @@ -Fixed regression in ``approx``: in 6.2.0 ``approx`` no longer raises -``TypeError`` when dealing with non-numeric types, falling back to normal comparison. -Before 6.2.0, array types like tf.DeviceArray fell through to the scalar case, -and happened to compare correctly to a scalar if they had only one element. -After 6.2.0, these types began failing, because they inherited neither from -standard Python number hierarchy nor from ``numpy.ndarray``. - -``approx`` now converts arguments to ``numpy.ndarray`` if they expose the array -protocol and are not scalars. This treats array-like objects like numpy arrays, -regardless of size. diff --git a/doc/en/announce/index.rst b/doc/en/announce/index.rst index 003a0a1a9ca..e7cac2a1c41 100644 --- a/doc/en/announce/index.rst +++ b/doc/en/announce/index.rst @@ -6,6 +6,7 @@ Release announcements :maxdepth: 2 + release-6.2.1 release-6.2.0 release-6.1.2 release-6.1.1 diff --git a/doc/en/announce/release-6.2.1.rst b/doc/en/announce/release-6.2.1.rst new file mode 100644 index 00000000000..f9e71618351 --- /dev/null +++ b/doc/en/announce/release-6.2.1.rst @@ -0,0 +1,20 @@ +pytest-6.2.1 +======================================= + +pytest 6.2.1 has just been released to PyPI. + +This is a bug-fix release, being a drop-in replacement. To upgrade:: + + pip install --upgrade pytest + +The full changelog is available at https://docs.pytest.org/en/stable/changelog.html. + +Thanks to all of the contributors to this release: + +* Bruno Oliveira +* Jakob van Santen +* Ran Benita + + +Happy testing, +The pytest Development Team diff --git a/doc/en/changelog.rst b/doc/en/changelog.rst index 77340b1bb84..6d66ad1d8dc 100644 --- a/doc/en/changelog.rst +++ b/doc/en/changelog.rst @@ -28,6 +28,28 @@ with advance notice in the **Deprecations** section of releases. .. towncrier release notes start +pytest 6.2.1 (2020-12-15) +========================= + +Bug Fixes +--------- + +- `#7678 `_: Fixed bug where ``ImportPathMismatchError`` would be raised for files compiled in + the host and loaded later from an UNC mounted path (Windows). + + +- `#8132 `_: Fixed regression in ``approx``: in 6.2.0 ``approx`` no longer raises + ``TypeError`` when dealing with non-numeric types, falling back to normal comparison. + Before 6.2.0, array types like tf.DeviceArray fell through to the scalar case, + and happened to compare correctly to a scalar if they had only one element. + After 6.2.0, these types began failing, because they inherited neither from + standard Python number hierarchy nor from ``numpy.ndarray``. + + ``approx`` now converts arguments to ``numpy.ndarray`` if they expose the array + protocol and are not scalars. This treats array-like objects like numpy arrays, + regardless of size. + + pytest 6.2.0 (2020-12-12) ========================= diff --git a/doc/en/getting-started.rst b/doc/en/getting-started.rst index fe15c218cde..09410585dc7 100644 --- a/doc/en/getting-started.rst +++ b/doc/en/getting-started.rst @@ -28,7 +28,7 @@ Install ``pytest`` .. code-block:: bash $ pytest --version - pytest 6.2.0 + pytest 6.2.1 .. _`simpletest`: From d46ecbc18b74b895b71e257bf07836cd2cfae89e Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Tue, 15 Dec 2020 20:16:28 +0200 Subject: [PATCH 013/630] terminal: fix "()" skip reason in test status line --- changelog/8152.bugfix.rst | 1 + src/_pytest/outcomes.py | 2 +- src/_pytest/terminal.py | 2 ++ testing/test_terminal.py | 26 ++++++++++++++++++++++++++ 4 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 changelog/8152.bugfix.rst diff --git a/changelog/8152.bugfix.rst b/changelog/8152.bugfix.rst new file mode 100644 index 00000000000..d79a832de41 --- /dev/null +++ b/changelog/8152.bugfix.rst @@ -0,0 +1 @@ +Fixed "()" being shown as a skip reason in the verbose test summary line when the reason is empty. diff --git a/src/_pytest/outcomes.py b/src/_pytest/outcomes.py index f0607cbd849..8f6203fd7fa 100644 --- a/src/_pytest/outcomes.py +++ b/src/_pytest/outcomes.py @@ -38,7 +38,7 @@ def __init__(self, msg: Optional[str] = None, pytrace: bool = True) -> None: self.pytrace = pytrace def __repr__(self) -> str: - if self.msg: + if self.msg is not None: return self.msg return f"<{self.__class__.__name__} instance>" diff --git a/src/_pytest/terminal.py b/src/_pytest/terminal.py index 39adfaaa310..f5d4e1f8ddc 100644 --- a/src/_pytest/terminal.py +++ b/src/_pytest/terminal.py @@ -1403,4 +1403,6 @@ def _get_raw_skip_reason(report: TestReport) -> str: _, _, reason = report.longrepr if reason.startswith("Skipped: "): reason = reason[len("Skipped: ") :] + elif reason == "Skipped": + reason = "" return reason diff --git a/testing/test_terminal.py b/testing/test_terminal.py index 6319188a75e..6d0a23fe0f1 100644 --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -366,6 +366,26 @@ def test_3(): @pytest.mark.xfail(reason="") def test_4(): assert False + + @pytest.mark.skip + def test_5(): + pass + + @pytest.mark.xfail + def test_6(): + pass + + def test_7(): + pytest.skip() + + def test_8(): + pytest.skip("888 is great") + + def test_9(): + pytest.xfail() + + def test_10(): + pytest.xfail("It's 🕙 o'clock") """ ) result = pytester.runpytest("-v") @@ -375,6 +395,12 @@ def test_4(): "test_verbose_skip_reason.py::test_2 XPASS (456) *", "test_verbose_skip_reason.py::test_3 XFAIL (789) *", "test_verbose_skip_reason.py::test_4 XFAIL *", + "test_verbose_skip_reason.py::test_5 SKIPPED (unconditional skip) *", + "test_verbose_skip_reason.py::test_6 XPASS *", + "test_verbose_skip_reason.py::test_7 SKIPPED *", + "test_verbose_skip_reason.py::test_8 SKIPPED (888 is great) *", + "test_verbose_skip_reason.py::test_9 XFAIL *", + "test_verbose_skip_reason.py::test_10 XFAIL (It's 🕙 o'clock) *", ] ) From f1a1de22579c11fd1e4301e0c6648ae46a74a1d3 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 16 Dec 2020 07:50:02 -0300 Subject: [PATCH 014/630] Use manual trigger to prepare release PRs (#8150) Co-authored-by: Ran Benita --- .github/workflows/prepare-release-pr.yml | 42 +++++++ scripts/prepare-release-pr.py | 151 +++++++++++++++++++++++ tox.ini | 7 ++ 3 files changed, 200 insertions(+) create mode 100644 .github/workflows/prepare-release-pr.yml create mode 100644 scripts/prepare-release-pr.py diff --git a/.github/workflows/prepare-release-pr.yml b/.github/workflows/prepare-release-pr.yml new file mode 100644 index 00000000000..848dd78a413 --- /dev/null +++ b/.github/workflows/prepare-release-pr.yml @@ -0,0 +1,42 @@ +name: prepare release pr + +on: + workflow_dispatch: + inputs: + branch: + description: 'Branch to base the release from' + required: true + default: '' + major: + description: 'Major release? (yes/no)' + required: true + default: 'no' + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: "3.8" + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install --upgrade setuptools tox + + - name: Prepare release PR (minor/patch release) + if: github.event.inputs.branch.major == 'no' + run: | + tox -e prepare-release-pr -- ${{ github.event.inputs.branch }} ${{ secrets.chatops }} + + - name: Prepare release PR (major release) + if: github.event.inputs.branch.major == 'yes' + run: | + tox -e prepare-release-pr -- ${{ github.event.inputs.branch }} ${{ secrets.chatops }} --major diff --git a/scripts/prepare-release-pr.py b/scripts/prepare-release-pr.py new file mode 100644 index 00000000000..538a5af5a41 --- /dev/null +++ b/scripts/prepare-release-pr.py @@ -0,0 +1,151 @@ +""" +This script is part of the pytest release process which is triggered manually in the Actions +tab of the repository. + +The user will need to enter the base branch to start the release from (for example +``6.1.x`` or ``master``) and if it should be a major release. + +The appropriate version will be obtained based on the given branch automatically. + +After that, it will create a release using the `release` tox environment, and push a new PR. + +**Secret**: currently the secret is defined in the @pytestbot account, +which the core maintainers have access to. There we created a new secret named `chatops` +with write access to the repository. +""" +import argparse +import re +from pathlib import Path +from subprocess import check_call +from subprocess import check_output +from subprocess import run + +from colorama import Fore +from colorama import init +from github3.repos import Repository + + +class InvalidFeatureRelease(Exception): + pass + + +SLUG = "pytest-dev/pytest" + +PR_BODY = """\ +Created automatically from manual trigger. + +Once all builds pass and it has been **approved** by one or more maintainers, the build +can be released by pushing a tag `{version}` to this repository. +""" + + +def login(token: str) -> Repository: + import github3 + + github = github3.login(token=token) + owner, repo = SLUG.split("/") + return github.repository(owner, repo) + + +def prepare_release_pr(base_branch: str, is_major: bool, token: str) -> None: + print() + print(f"Processing release for branch {Fore.CYAN}{base_branch}") + + check_call(["git", "checkout", f"origin/{base_branch}"]) + + try: + version = find_next_version(base_branch, is_major) + except InvalidFeatureRelease as e: + print(f"{Fore.RED}{e}") + raise SystemExit(1) + + print(f"Version: {Fore.CYAN}{version}") + + release_branch = f"release-{version}" + + run( + ["git", "config", "user.name", "pytest bot"], + text=True, + check=True, + capture_output=True, + ) + run( + ["git", "config", "user.email", "pytestbot@gmail.com"], + text=True, + check=True, + capture_output=True, + ) + + run( + ["git", "checkout", "-b", release_branch, f"origin/{base_branch}"], + text=True, + check=True, + capture_output=True, + ) + + print(f"Branch {Fore.CYAN}{release_branch}{Fore.RESET} created.") + + # important to use tox here because we have changed branches, so dependencies + # might have changed as well + cmdline = ["tox", "-e", "release", "--", version, "--skip-check-links"] + print("Running", " ".join(cmdline)) + run( + cmdline, text=True, check=True, capture_output=True, + ) + + oauth_url = f"https://{token}:x-oauth-basic@github.com/{SLUG}.git" + run( + ["git", "push", oauth_url, f"HEAD:{release_branch}", "--force"], + text=True, + check=True, + capture_output=True, + ) + print(f"Branch {Fore.CYAN}{release_branch}{Fore.RESET} pushed.") + + body = PR_BODY.format(version=version) + repo = login(token) + pr = repo.create_pull( + f"Prepare release {version}", base=base_branch, head=release_branch, body=body, + ) + print(f"Pull request {Fore.CYAN}{pr.url}{Fore.RESET} created.") + + +def find_next_version(base_branch: str, is_major: bool) -> str: + output = check_output(["git", "tag"], encoding="UTF-8") + valid_versions = [] + for v in output.splitlines(): + m = re.match(r"\d.\d.\d+$", v.strip()) + if m: + valid_versions.append(tuple(int(x) for x in v.split("."))) + + valid_versions.sort() + last_version = valid_versions[-1] + + changelog = Path("changelog") + + features = list(changelog.glob("*.feature.rst")) + breaking = list(changelog.glob("*.breaking.rst")) + is_feature_release = features or breaking + + if is_major: + return f"{last_version[0]+1}.0.0" + elif is_feature_release: + return f"{last_version[0]}.{last_version[1] + 1}.0" + else: + return f"{last_version[0]}.{last_version[1]}.{last_version[2] + 1}" + + +def main() -> None: + init(autoreset=True) + parser = argparse.ArgumentParser() + parser.add_argument("base_branch") + parser.add_argument("token") + parser.add_argument("--major", action="store_true", default=False) + options = parser.parse_args() + prepare_release_pr( + base_branch=options.base_branch, is_major=options.major, token=options.token + ) + + +if __name__ == "__main__": + main() diff --git a/tox.ini b/tox.ini index f0cfaa460fb..43e151c07aa 100644 --- a/tox.ini +++ b/tox.ini @@ -157,6 +157,13 @@ passenv = {[testenv:release]passenv} deps = {[testenv:release]deps} commands = python scripts/release-on-comment.py {posargs} +[testenv:prepare-release-pr] +decription = prepare a release PR from a manual trigger in GitHub actions +usedevelop = {[testenv:release]usedevelop} +passenv = {[testenv:release]passenv} +deps = {[testenv:release]deps} +commands = python scripts/prepare-release-pr.py {posargs} + [testenv:publish-gh-release-notes] description = create GitHub release after deployment basepython = python3 From a1c5111a404aada1444f28cf23bf52cbb00402d3 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 16 Dec 2020 07:53:05 -0300 Subject: [PATCH 015/630] Fix events variable in prepare-release-pr.yml --- .github/workflows/prepare-release-pr.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/prepare-release-pr.yml b/.github/workflows/prepare-release-pr.yml index 848dd78a413..dec35236430 100644 --- a/.github/workflows/prepare-release-pr.yml +++ b/.github/workflows/prepare-release-pr.yml @@ -32,11 +32,11 @@ jobs: pip install --upgrade setuptools tox - name: Prepare release PR (minor/patch release) - if: github.event.inputs.branch.major == 'no' + if: github.event.inputs.major == 'no' run: | tox -e prepare-release-pr -- ${{ github.event.inputs.branch }} ${{ secrets.chatops }} - name: Prepare release PR (major release) - if: github.event.inputs.branch.major == 'yes' + if: github.event.inputs.major == 'yes' run: | tox -e prepare-release-pr -- ${{ github.event.inputs.branch }} ${{ secrets.chatops }} --major From cab16f3aac4f81090fb376b3cb520804b4ebdd15 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 16 Dec 2020 11:19:45 -0300 Subject: [PATCH 016/630] Use transparent PNG for logo (#8159) --- README.rst | 3 ++- doc/en/conf.py | 2 +- doc/en/img/pytest1.png | Bin 6010 -> 40974 bytes doc/en/img/pytest_logo_curves.svg | 29 +++++++++++++++++++++++++++++ 4 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 doc/en/img/pytest_logo_curves.svg diff --git a/README.rst b/README.rst index 398d6451c58..0fb4e363b1c 100644 --- a/README.rst +++ b/README.rst @@ -1,6 +1,7 @@ -.. image:: https://docs.pytest.org/en/stable/_static/pytest1.png +.. image:: https://github.com/pytest-dev/pytest/raw/master/doc/en/img/pytest_logo_curves.svg :target: https://docs.pytest.org/en/stable/ :align: center + :height: 200 :alt: pytest diff --git a/doc/en/conf.py b/doc/en/conf.py index 2f3a2baf44b..e34ae6856f0 100644 --- a/doc/en/conf.py +++ b/doc/en/conf.py @@ -159,7 +159,7 @@ # The name of an image file (relative to this directory) to place at the top # of the sidebar. -html_logo = "img/pytest1.png" +html_logo = "img/pytest_logo_curves.svg" # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 diff --git a/doc/en/img/pytest1.png b/doc/en/img/pytest1.png index e8064a694ca8eff0f2167d29ad13609f9a99a46e..498e70485d214c242ab75ce24f93225223fc5f8c 100644 GIT binary patch literal 40974 zcmeFYc|6o@^gpbkER`+EUfGHeLb8QYDf_-x3XweqV@Z;oHpw26o$O;7#-6grjAbw= z+b~Rw!I<$}qx<`N{(AoYJdMpL0d&>)txS$jwMYLv!NJ?Hh(P zG<0a{Fa1$)<*MAhN8o=)AKtxngJz%l-zRw9I~tnIwmUb}js3<~CUqjyAzYm^Pk!#I zyW9{ixXJP+I!m3U-LyTN#b@Sw_z?|-bDgqzj@Ryc$Va~l(=YtQw3OXYv#9Y@A)C#b zWAgK4!UryV zVgonW(R9Sq;HhKaLO!@L34Y->!Bf;gBldqT39$PAtv_3FV^4j_>5!=%Tsd>k!bU=T znSiXU?1a<=8m(MII?e-KD=^BNTI@av*lcvJsi`>`|24Rao}HLLc38=@S8{N0Fl(_r zLn1#c-=5mOCeUo$H1*0cUO-OxeF*NmN&N(SZBtV)0y@##)3eps>{oppT)T(% zOYCvH!6qwP`3>i50j?lk#$8=!3I#MTRWMmuSskXB4MOTdkl_ae zuVf=?+gZW8i~ODssPEFoVJI#SsPEP<)YjIzdptg%n5OLiN5p`7xj8N_E}`KHxO!hh zUO^!t>O7z*?w>%qm_(J&y>Ik~y{QjA6>jLyJ@|&qLqrlC^&5#R=l**p@{k?-jh>z! z(+YwANNF91S+PjV%35^?{~7{xo-=SsV`5%JCMS<+Z`u6!+Qagzl93;ilS>{=KYMb( zSL~VfbLt0QORf9MQ>Q4}omU+rkPTk7@ZH$ilmGj2PbAJU{9vV$#bspzf7jI>dtqIe zy4oIrdvt){mXmxc-1}u{%q;b72Ob`t%x4E;xdB9ayHu!&z9I&1NC|ufEzSY+~xOe zE`xPLfAf@+izKP$_vuUvhNm?OtXzCNnLr>MDWwNjF={rltu3`3>_Cv><2e%(6AD+U zPVoHhIQjh)Jw5Tj?2;x04>V5oCB*&}jn;*(3{GRPJ_R71MKK%CbpNwR(vhSytaBz?xR8jCHZr|LDlRN_M-tX^(I@_sb z_UpxxpWTSyZ0D4fZ5UU`@J~qi)}%rqpH8CE{>oN4wldFp4(GR&P^2oe)m6SR3*2+{ zC)ssnj1|=%Uq(!DM0c+9>jhFD|KI#`zl>j*xITVl!S^X0G%+n`53!r0XWd}Gqq6lY zRIhAz0YcfF`5pc<0`ip3ir;dnf9>B6^>3^Fmv;P1!TzPA|3~N5|7whX^%lro{?(rU zuc^cP_*Ax$c?rC>mcbz~!WVn~0iYiQMG(e>69K=}M3d$0zkmFb!9P3r=L`R@iU(UE zCl10BVnpL0ucl&{G>89!nAAVdUjKLbpG^K41T7W*|K|;$`SH&i{vW-87$krefO@SW zStF7@Oyh{i5H_mSAQD;8#Gt=|!&FlPDixikp=bHqRQellsof-+=QRJdk^WlR|DPWV zsf38$_cD3R{akz{l`;DHb0!$m@>w^`h3NdN=i{|wWTM}+)83X5;=bwm+Lz~)!J)@j z-Wo%0sf&t9UXX-kVU9*-KV?6+RJbhi{P|g>I{}o!stX@KH~btKSwNGT`n@wUJX=aP z_7_Z15WVz?qlY1(N>mBcxSsXkgi#@7nt^{8|4$Ogz|X~K57@-V=LcRzUHQMCe-ij7 zf&ULlV6SpYt9r<&*+HW-d%RNTGL1g{-DRuJX@w-cfoYv)g%+@uI@0V=MCY-_hKI*> zzL^j2mx*xETzEOPixpjQ|B|s=B60?#agdQtE7A3^90VWD1@LC|J_mJ6_?p2SPw=J# z^^l#livi{&&8gpO zUn;3D(-d2|uPTG>20?#%a{Kjm!@Tan-s4hx--D#>PF6j*RY*|K+n&C6803}DV+mK{ zR}jZ;fgAeIn^T>q+5Q(hne-r8(ArIXGFF3E=$)iKPJ<-0c;;>E*BXMaULFGM7x3+* z-U0uhd5t~RwVQS_$m0|sP`5w1X>;&pEOKJI?tpp6EqBh4ezYJp8M(jO=d{8tuxQFa z{e*sr-rDGY{P3z>m7n+dE1z#>e-}eTgGCzcC;k-ymfvEjWLoqumD9|sBC0(lr@<#< zKUpdj=7h*nDbmRNvShxBOwm5$l!G<>eB z8?5&^FJXA$*`B)y^~QBw@IseP>w$D$&w$l3(H;nSAH4`eZjl0Q!~buM3&b6(%~22#flzClK}Zy_Ls(5*qp#$eJgK&yyO3^@j$EWOfFDe z%TY-{&uhpkgeXLr6r$3;y&q;jr%C;!!D4~U`TtoC)(otWMr*(dy7!sV`}axfX@u>& zzjpB}t9Fa3@&f^?Q7z?iVRLG*)WPeM0oc2L8Mw0k>(2gvTKt+Z?#;H1xd0xK*={HZ zenDmHxlfV2?tzds^vGU+Wu|cDim+;x1T~?F{gg-96rwmBn2u24wXg2y1$Y7J>*E1D z0)OqvO@&}YU3boI!;CxU)=xnF1bKdn6s;E~cGRU zX(~OMuf9x@Q&&_!A1LaKdUccC+y9}lgBiVgVURf<=p9ZOYrGto-K^w41{em6^X(vYJzuGpWqldu}h!;e}6o-w-`S{P5%RpjG)%-x}8g-1x8S3gz9h|NQRmO@ z-`ul4J=b?e)0G?21(WYgus0TjsWt61j~)NL(34zQr%iLua`Ee63Qbn9$&h*jBK^F% z;k1lKljE(|l+MKkVtapoNB>$E>{_R*tBp9EK$=~+PD^Nxl%xFKZq|#I7VQjWWmC13 zzEJGIGR`=_;~vLjwiYAj;anV1nN18~oBMM{;hE6l(*Oz#Qbj8-r;$mY+KOS^Sr4qW}7-x+!>SURu4nGexe^w9j~=&2@qdzlIi^Z5^41EW;N3* z&HdV1-w}l}rq^LvSr<$Ae)q1|*KYOA*sJud>3^ImEx*>f9k93eBsRSK^fYVP@85~C z+znzLFa9u&17ErP=6qwY1MS#8$6Vu?T2YqL1Yh(Z`h>utK*+|`)Y8(@I1~0yCn)?? zcF86Kk{+TyOh;=I_=9X>jm}?f5GwZw+B_Fn9qhJ8>FV;C-6_fWa+VGRakC{ZKf*dI zf3JHuI_)=f^rKtkO%^-XH1w`kPwOuGl@=jI@+;R@VOzXVUCc?ubO);-bKF;-hHh?F zoZAO1S}ttK?*ysiE1ql3hmmZn|#xVdfI1N)8fU>-!ZY)pO;(z>;|i&)h;XenZVt;nXLi z)5qO7W_F8aJNrdvMF!k+n9Vqp?+f(8E>3kdbF9s6*z0C2kRQCLZps~Q-1$i8O`p&v zC>ZruxXMW)0&ZOOToQ_CSo`MDm%)0DbP>Pt>Tvi9mK)OHLe}0>9vzfrN9wKhe_~e6 zH9StlIszvBY&&8^TwkQvHMrv#R|@RJiobadXV@o-egT1TW@Di2qKQvSR`b6F!-J74>$Cu&_hEf(M5_q zKdYYnbqt=|K*Q=ouCxu5)iim|q9L6*{8UTzs6%tIpWoSq`hRrH)QUIM`WOV0ws18e(?)f+KO8PmfP^A@Cb4#Cky4 zON)M@MwMi6wBeOqq2X^NS}~)gdg=2WhH+Hdxz)BLM7k&4YBgu4WAssr7hl!jp(<*f zX0oIH%47CVVg=LjDyp7F3+u<7;rhAS(|LJcrt}Di_?>c5GOWJ$B z=aK5A_LF@ovk=Skw*@v96E5RE3h|qM%-Y#laOd;(bD5QE@>w1(u*x@mqVuG5<303B zsk)i(Bay-*$LN9I#Tt2|R#Vm=>rcnI6vs~faIEgDkC0O*#mtEy6;!sbB(IbAM#-uT zf5Lw!h&@)57x*Ub1VTDJTGaK{ELIfC@6ewCw|XgOSCtclo*O^o?)J*t6!j4`+j`uW zx!E*Vdm^*HAG5nbVPGDIVmWu73wt;!KfqK-ylP82L`EXZuW7(5#X*P~d?Y+#1>IR6 zTS02p&ye)&({;-()#J~FuXWAWCI#@wqK(0(2EGe1dq{i)r_7{xh<3nknNJ}-AH>2- zzDk#La60ItgvCt8DSLiL`?>svUec|q5T>{9gHQ6NFq2kF^w_KP)}7Kr>_|_4$GSj< zp9ve3tZ`QF=TiEe(D`i0-?aZ7l z*(QImcmXA*LY8t-`KZ$T37+)|?9oF^XtU1Jv65?>0nCw}>1PAI;swhmg2=O3l@0PJ z%~_wlQtd=s(5xU=V5&osXY&A35`EQfbO_p_3g`T%_G&4gvuzm}aq12i(HHrEh z*W%l%{ezU4UxX!c1i9otm5G0EJy3?RY4ZXY&%&n)k6+hOM@j zwLrC^`8$XFx+1ux+DPq9ZI}aeS)gQd1ac>jGa_tjD!?X)7AM(9!$pgeo{|qO^CX_( zWNLWFB`%17+P!y(=71p)WC^Zej}O|3Uj&Ffn2$MNW_G}~WPkLSh$d!AWMtexI-wSu z(++7M%2Pyr8{Bgrc-KX7pmH;IM*?bx=St5oi63OT0+KMlpDq1cz2Vm>v&a!RXFn%_o+q+`(U>_jlrDx#!n6t+1b`QpbCrmg;NxG zkiHBpZk6$2slWM(?_M;ed-W-?v)O5?oq%+>S%MoZ>s&XGC47II6(_pX*^hU=57JJV z<7mM^atm!^O>HO43YNYJlkZy#iB8bKZf} z@3`vo1)9FRewUzI+Th@F%W3wM`@;?emH@2XKu+h8BgAo%NQo$PSu{WNoX4?mmp$B7 zzg5Laxo96{-jSuKy2Y1gj%-6bk1X5C=1)LP3P)$hxb=E4sWR?tFrLH2ZpQo0whzAsnH1vgHe^Yxv} zIOAS(Uf`>InRcT?Qjy^dbRE_XU5}?n*RoP6pTzQy3&jg6+BBbXPP4NxhxAN8!j`D0 zI^#oj2e%g{SL2p{P2J}Jk3yb5Jwl|{wUwFrszOoR7i{eFEsp2E+F95b;z}`FmUB_n zH_D9`Uxwb9Rq>mD6#|MrK>tL=VrN9Vb&yrrsXu{>xvceS>hJWBN<;}`?_L@4x_2?=7i@l^+Ih>ayEw-hbxyY*y!Ch$iObYaa{+ z_5BRwht{H(-*OlK8J^%T(AcHNwwW9Ay%RF?m=En?{dx^=Pv1qaj0=ZhmxHOVO+~%V z-D>@qYDLhxqe)yw96H@X`$Y9~jb(#ICKq;k(iUpRy+-%<1KyY^-iCx3NXX-(j@CSL zJj9NxuwO1$2#)M%mVW)w_K_<6P*RAVsrfq#)jViIp-P?kts`8r4$8uW^k%Jz&U9&8^V#(cIxgP*4Mww{8U*xC zR7lb6FZ_STK`49U7iq6YKCSYgs_$9ZN!1_OPC3qVkAfb@&aP}ZUt44NADQs0e~+|F zO_g^YZ<|wVFlMnS^fW?0@(;8?Jt>Y6XD`z$D|#Y*FH<$B>v)r5RV~#U^qjs91xC(g z62kD$@8_1`g<^6f9+1xMcApu!y`+_N;4r`VU|rjPc7vzsZ4~16d-P}ra(SCGsg;ND z3G{T5pR1cohQeC3WhC*XAp2T%?&{88RbuH)GS19hgWuoVaGTg2dK6##&BQKnYZjjj=>&Rr6Zvj=21*w!lDLcxBM zdA2v=>eg@>@>pj;r<8b}#3Z`m_t7)lDTW2e{t?a2MXj8>7&j22K&%P6J)frNEpOMg z7T^&(@?v2qVeUK3RM2Tpeb#3uMcjGHp*MvPIdhC7j~Aw9LVZgV@ghbcPI3DU|7ERm z-2k|~+jD38FH_Q5JFag7uql@g_&rSYji>G@8!gR_yuuqFw~3TXPvsvLNApcfNlsYC zq4or#P``0?-V1Kn!W5qbzBhgeHs~g56{?A$ATEDxx=xOhE>6sWyTrF}p>St5d@Gxz zwdxvF;Jr8hYBB`FpVeCejP&6>cIfz182{@A#og4;O{cLmJ$0LG~n+SpkO16S0W z=SZ>)q5Is;q@CX9%AJFr%58V&x^f!o>*d`XduNZfYk98pmEv`tCw1)4H#|(^nk!^` zg|m~K2#BNX2F}VU_M6D*;U%&ogo9<>O*ZOLHDK%vy0;!d?B+KUAwMXz-1j`cF`PWO z5>rudl9#0{=y8i?sEM>>oTv6ne|J~94wWCi&UP#u`^89Qe4ePo}6bNpH+oMw#En3A> z9zeX!++R-Xxj??En&L?-r{)H>(@(d*e(SnFlg^=1A!7HV=SBND0ml>073q5*ws!Mxq)}<4qBBzN) z(r%YLFu`|MdV-9*HHhNuA^E?hON%Xf1MInia-OQ&>i4UCZ({w@ z0QhmIm+*G?Ly1kZN}Ry)wHhhY#VKX42%qq0A}G77SK96FFUXGf%Sh2KKj(P4_;g}whs$C#KIa868u76d6-j)+ z-uFZ~DNol4k!4@URuvz2-a!I=D1b-g3*~hsZ?KG7HA{6`lz*^&-__XF`y9!I3?C;1 z-{M^bA=BBFP`ZD9W88Ku;JTY#j`>=P%8!~es=C z*f><`>9#&k$B4{epIez96rd{_arsfnfa z3=t8geM+ZOmw7P=l@gIA3AO$p2k>t4x=um^BU^o@+Jgn31)iFbCuob3u)wnix_ z@(HLyq$9Jyex=u2H-4nmw6X?OAaKMSiuF3Y{_Xl3X;*qzqS$CWF?3#2;BJ*$bLRGm z*K;;DmCJd;QDtjww7A2NagI&fjjzZfCo6+#oRy8g`fCNSUg8hD*O(#2@#}VG&ZV!% zXmJY;=3T#8Rhw&ueU#?L`K;h*qkRL%He#pe>D~(<9Dlpu#0uLF+GIYaWc1x|e?BhU zulEO2#Sij(wvvf9HnlFYwi!zKUivkkn3wTumJ_fX|6e)}n9h3S%{{pJa%6{Jz}q0L ztq~%P9onz(3%V=IJn1gc6ZaZ#*%(;c(}#6G`a`r+L8`{EF+?AZd*j*jBX%Zztw>~( zN2zyTLd&xRVC%2-JVZUzmh(yH2NcV9UL$3;5bHa=nHLe#vcyh5uPBWnD>mlI{SFTK zpx(KUY|U?cO|z&RRFZu?zNq8UrQ-`OagD=9&#gw6%=rYXZVZrlPFs4H$f>?J=yi*9 zY5T$9O51Din1ag6*v)14@P&@hCcH)PJ-2?Z;^Ch0lV6R9WGJz{dbX30jM_|N8*x#e zb-~)3n(WSYe&b|s_S{&0aqQD!$Tj<31DPHA&l4; z%{;+p7y7+RU6j98o)Xxc^})UKlW?A#oaF!X;j166<%>aHDmyMKHD+^QxjFRAf{i&L`l%_UdRiAiey}o!#da5^pM~ws&~)rUco#@}8PY zEw^2pz+Af=01_Q9gCPVNNB$dn4s z7xBnrKXiX3V`Z^zzca{`6vU6<0zj0&1|EA)lc@OH#+_g!o8P}vA{gtGIj=w$;IbA| z2ltHyesyo^%z<@zT(rVWDH8+K5(Ng|(|b2~mw4~tB0fAKH!0vP_Y!j7KifBEx9gI+ z7|2SCyH|zFinHv}`o%sx#eZnH=#-l|)|%MK49Xh!!nCnwo26wrfYIARCGg{$(CZ7d zcHTutjgUL$OsqREa&`U)5WWynFBPxRRBl zU8QeHI*!03%QWdOKB>E$u`3;g`eh#TgzZvYU?n}OVOG6}b5BzO7w-cP>*m_vW(WSG zV-9)3)w*>xY42j8N~!A3?N`;!g~yd1a%_5I% z>x;Ckz~kR=g*SByJ}jk3RSu9p8;;~#swvG_T8}UWC$I4P+YZn6fL7O|r!-Wnr7O6} zj9!bpzoaOp;%4+vvibdtw1)eofCpc6)9X9dD;W$++EzCf`ib{TUtSRqLn~JZ&t&Wv zZKN2*UpL0|-3VtZ$vN)UfA+Nv*R2HJ?E5(-Sd0;p7yeEM6MUwT1s2_+TMTica=UmZN|J2&4UB}4LUyZJa@XSpHqhsT^?C6*E#RW`}|UNnsr zCToy&W3|$PwxU;do-1|!M0BHXU8Z?-{UzSOr>%0ZM#6QoZxpx==HzwNmLIuvs@n5m zVrX0GyoC5VvbSy!3-k9NgF!4?m;1^)Kfb0X1R*y2gQkY=uVJi z=pU-(nDYdz=7(I%L6GOaFDX1V+B^GoYmGq-;rr#LrZh4c7(n2Qr0k!z`%z%q&=FS^ra~_6yjcP^*&MLlQ^a1 zkZHb|(jFI16tX?mbyVnVn7d|Ukue=B?5A5y(%Od>+?JJPm;2=@%n~Bjw+_V^5|*2X z&%NR9yN7s~bsePw6g@_pck?)Uy0C)1XosceJI=^gIxY(t%hmhcloBdq`iSeNDZ)yn4v1$(y%l60SVK%_gAy zy@8F57A6m4$swX>P?gFBMhtP>#OO9$(X5`2AipPd51wW}bzNfO{5<6By8&|h3na#~ zJ0qPr+|mUE9vTMwW=!OXr^6U*^di;eme6?)y_u`XH-^W5OQVV1c## zVn<95=2qljxI4=gRo1VNBrC)L`*{?k z%QxYry1N(NGR%9BJd%BX54$W;koFSE?j`=9_C? zD*leAL!s{E;?~}vYR}u*8}RnlPxsd#Kr9su529IM0tIj16>)vI$>;X%Q4*<1*c{dM z#X5{y>rE6c`RVOR&tz+y53NrTYU3)3VjBAt&8z651A3cQ?anU1x zy#j%ub}t=gtU*#;h`sd}Kg@1AQ2(>GZqrrd!pnOB^HtfXhg&OSN?lm%WRDZ74?3Rd zZj8}gEbHDT+Zv6}(xbV`!ow2cEK9Uu2~9b-(;L4G_fxqFIpsW6R9!2~4cjlfsh&mM z{BGhx8#X@uWbN8jmT~rSJgM-czM(wr!(~)Od)}qr%9#Uw-Wx*C& zT&z-HK40dxrc7qpZ9ZeZQ7{=nPJ0pl++FWkX|t<={?}>|fCZ=OO9C(mNB`E>u;cUY z6Lk@|_w_*+R+qVfcJ~kP_UNV2`aRcHxQA9jj35BgU%mQrBc1ZzW0Z2_W(t_N;NW_R zgP2hpeTRE@CfL#!GR;{|MT4Lf`{E-2$*#8ky2NqsZdxz7=icsu?9H;98~M3@=?bEa z!D{Xu_ZqL-I@Nw$8(|=}P;w<-0}wkrkU$3Llw6@hS>ld^6uWKE>DErN);HN!}mi?npGxxAIsh0e$$Do3G(I z!wd1pt}ooq_bh-y0&S~mX`9qQ7 ztu7&JlA4LONAb@{K->diMw)&JU4SxP8Ot)B+p(a~P;4-}sL@Yq!i(Y1veIt^@R@#_k&h0P}w4on}G4prhgbu}${JsIsNt}hm3n3~e9h$C@4vYC2| z<3-uptLwk^q52Io*HN5;5AeHUFN;m&1Fp@Ve`JQwBYPb1miuYzwWjeE567D*!SB~= zxu;4rK#>K|DS$hT?+H&f74T)LyZ74}fTrGw+6Z z26Zsu6|H=CUicxHpg0Sz3CPTU5QOAbhaR%^kf%{ zmLBKde3{m-vva6MW3;BUVTl2?yCAlia0s}Mbi@e(9hhTq!S!y!4?P)rw7c^p@3)Xr zJ?*_i)tOaiFCeZRWR92LnP5~wgQ}~@QuX`|m2+WDme$z;)*n3h;;SEy``YZgwJPW& zm}Z5X^>AEy(6;t-HM!H1@%^~m7YhDTe4>SUh{l$;O$l5A?;mf2`vIxE@NG{;A0%1w zE%$UoNuQ#B3m={$17`Qd@2aFYIs!>dTS(6-1it^N!v8w;?impe$K$6*eBTSwsswI0gp!hsFG=C!UY4+(+{dZ)2?h7B z{?JbVU81#*>tj=zo?F}eroG$kI*D!Uwk`{W17SeMR-Yg@hSb~Mo@1`o)A3&L1G~+t z(O5s>?GJ?*lo#ZvH(84*EO7c5HR(KGi3lo8k9^MpnPkCt5IZbBa(W4z?OmI*Xj<@ApgH3qaag+@b!(Yoc3Zsn{|!q zcKl%vC8=|5qKMxO6F|MwJ4=}QxLz*PU+FCGR|1!wNwwTB(r`KF;W&J*t)feqAH$b_ zyI@Oq)ym=}Vg$>dOtv=TIrf+hR<>iRy>W*3+j0QadDyw=a6gR2C-^5MI}6)M*nZ)n zZbvqX$d8Mry|1F`NZPF~E7K-U5Aad@izt-V|wBTk0?`iWA&mL|gUW-}ZdmBF9{5{tW}CS~S!xb1a zJ1J~MTGNK{n-n?p5R_$S0{A~^V1egc>06mD^?7=?9`03qUbwgn|M{NAv^SIVbd1>w z3v(!3s$l@m^=J#p@80tI3fOPB!mrfvUfgh^a=V5&zPrNoxUhS3=_e!>73f}g>vd16 zg8N-@$7g{3O8=fQJGPSFKKM^laVAfw>}&a8p11dh2FIPx<<*y%kbk$)9^-w*U}IiA zI27u=mpfz@QgTzX9!1T@lfMah>=$YOF7a;@3R5N7FrfAo8KxgW(-UQ{^}5se)4P2E z6btp1*K`~7j%F0!X5isYdD|VPy!Mn_+5xK(NPB{5nm_jqUae>P!MZ8AL(}VT^=*^V z;wh6hum0nLFsiQny3zEG@> z_A~nVGhqKLy~Ik7eCH|;meSiECqLle4n?HD3G`gj%sE{IR#A4*@Ms}HV8JsUI`?{( zHjLGfILtE^feUFUH+5ZcKPSC|)dt2RBBR(A6tB&ZjhcT1l3bgb`kpk!jWGt)FOq3r zB-UBIHI$SgcI9xWlHe2M*(JWOgKfW93?;e+(Ip(%HLA&ViS4im!`%)zo;Vs&4fMY(tvYz|upF*r3xn?Fj<(`yOF5heO3qQW5 zv-vU8&RhlC7#t!pg7u}vB^YHH05HP~w!T^g{k-+`=J)Ib-S2?@Sa>Q4m!&i7L$3J* ztY*^;%!P=7!Ni(@MZ^LB@}a%CQJDpBjYP%~ca$(cY}#UWJnh}O4~D_CKTNe50&zd( zE&JN3S5iOAfDI=5Fkv`JWKuAQU~}qXgsOAkg?D_oj==rJ0u@o)>Ff3WPO-JYiXY9m-W29(PO#yELec334p znDMxDm`Gy7y)h}7Ybb~Clv>J_>O()Hp17_X*z!b8;q-KQiG{@{#U?Qlj0z3#IfAvdGS zj^znkr#hOeGv|*~M)6 zboUe>-diFV2iizO8jto4+;-=sJWz99zMzXQ{a zC>j9pp+!hYkGAsd7);LK<42bRu#S(|`lhV*eA0jT@45;=y3DOD%!$_gAl}fU+3DS< z@&Lo=>DPc{V0RfqvtE@v8IRQ|kG7QCb;_2jcj=7fVm3MlibTVbkmAoRX$H&XPh&L7 zumE(NJ^D>ixD*e^dyF(8-&maWICdSQ`o#W~7)KX%xUn0PRgfv+9cM^6t+An=Wom$} z4>HLn>_jiqqn|j24EM7&ap&>?7m?ZjLXLsNeI zM8Ll@@tIRPX8Iit4?7S43a?V3)bvGoir^96T!SGzoRW}E9We?($DP?No z2-qUOtpzfp=nFrr%Tui1r~sKfqjaVD$=iole-EDiWd1&@{6$K7|UDUKFt344G0^mg30RY zeUHVAmCI%8ln4lw;4ZVGVeayicBxM3rX6cFckdv38gICG)N!2maLm>Sw=5d~6f5W9 z*_2%{M6%iSDz>u%skjD0+Q{t#T^uIL%_WH$f6Gvk3`3Ch6Dobul>hFz!F zk$}fxU8|m*qyp-Av-1Ve&*Xbh#76)V^9D(cu?WJ=M$nFf$$#f zXE>=UFsya&QNDh|^~*ph7Pl7t?#KS#eQZ64gY^ZMR`}KR9{Ias2yGFXI%)1|o8ba|05=&rO8Y`V7a)PSO3f-$zJq_dd++WVb|1 zmqkk44VNwai_*k3;d<3CmM4H3m5pH*>yJU{_pWJI(zCME(XY0~mU|jjMDQ5Nc&zl~ z5>?=!tI-`(&!5tAlE_>%Y+1&+pW)Pdl#0=b4O#c6D?ujwY=}ZvQIHM;1?Ea_Ab7ug zk5t4DU~B4yWWAWL+t%da51!Ci+;`9h=f3;b7j!Gm_XN8hRYL0-7lfS7+7gjg5C^@~ zV7dnRW;pJoO8#)+~o18V<#gcUQpESs(;TE613gt)~PWh?tg^Vfgpc6 z&Y!aDnP|BnH)j-OE2V1o)t_RTJx%e0n;cMTRnd^t#Pq?a=RO?Y3b5Ylz4X)0m5Z}; zP^Evw{6A|j#MYTPu)spJbCGTU-qh2pa!{ZmkftvcRc9jw`G=J2g^DD7!^XD1+(6wd!-0n7A%G+^RDs2^KLqej$KVCohowBbuIRb?tInMY`v(!} z4^I4Gq)70}`sX|IFwK9(DbQX4|#O(6@S`H)4J{I8^B{q>Tl}dE2`STCJ+$>nt@AG8d4)9Eabyd;%PX zdd_>8IGwM5?g+6B_~smB?~`_%-|BWiHbLpq1_7YfF0()DZ85qL$4}_6-sb|{;zFPI zBZ#88Lq$o8ZJu!*V3O8_O6a=N2QLi83WQ6HssxB`@qJg-bF~_M_n?53=!QSpFgujg zL?v}gGd%HyI8g0p+O;48N=qWRL_opM+7_H^(WD*`iM`-u~X z6I_svt6J=T$-;8}XaffOd%Hna0X24hB(0ZuuPRtp)`sVpXn=&OK`j${2{=Oo;yVlM z-8aHw6xbQ|+xwL^7tQ;9-Hoa9U)KN8y7Hk={~Q#@2S#t#j0+6ta8MD;woHe5`CYd3 ze#b>{w2Y6L!|jgp?fS~0pRm!(<7(&8!a7V~?9~P3s|P=KPoOOy8Dd~@83sQJDzdX4 zdXFy|Wof*sS_7%6ns%;M(1{(_7jo8oUEh`E^Ze7O=TPv_>Gv3G+pHgSxO-ae!|wN# zZ|thweDT)OYsaD7o3swoC98i@9S4opTsM*GP!M}#TPxllA-)3p?8~vPJg_gaaz_*m zFSc9ry>mLT;3%JPOr)Ma{^xJ@+nBz3&G2E!Yumir*ResMX{l??Zpwtb#T!xz=EAT4 zjK#viv9#;_AZ>Ej&(hdJhd6@bF558YXW;cGJ!6v$!FOlg_8>dBqDsR>7ymE_c{omH zwI)WbA@_>TA0lFTM#W2+>>{yk@D~I^ywdE%p|YXNJbToDd^Yv9=x+pBm#Uf|d0;!8 z;wH|U=ijR+bzB2|z>O!0J$7K4x)V>b&BX9h3?X`#LzS>6IfCE$IaSpUsZ_nuIK=Fs zWskaAxo7Vgw;kgVls<>^+YhO882d!jI;g2LFM=|d^@i&g?Qm?J6jc(XtDFVHiyeV* zkwMGgvk^NnliMWG-AiHn!;|cw1OUwJiGjbC{Z zp}Yg>?eA~$^gh?;#8DVi1wyYfyBpG>>p_IQ|J zP!D~;GsyvNdMRXxr$H;#`x zQ}l9`n9yt-KyuyWBL8JpqvA7|82*t_K;^awIDH;K&vUfnba>S&9J2{~e&07vqhLX7 z>9w^>%O}C6fYYCmoVcyF}C?;EJUiaajh<3gBeeH%+iJ*yU46 zVgX4NrTc9+7K87+AQX3$$qUaAJPcoW`x2=M{m39613+_%ZL0AzzckM;FdvyK$D~Ab zRR8*T=0p~h-1`0Pk08pKC?HpB03S4KCo-bEi9%D}6!Y=xy%A4Ec6E}2*ld~2MEyPw zjn&s_0$NJOAKwLDAGT65&w7rxTl?V3sx+0?<@5Xd+X!6VlUJ0|*r0wO<5asJx4(2INDa z2xq<3w|`#Yss*pK1{}1(z={)$b7v#@20(EOvnw{7tN#)x(UNk~7Zlp>F#%W4wba$S zf}O`|`PWI%DgbiqphgR`3!hdkTcBj0axA%4`1s`7=JjPl(}5I4c<-ussF_mh_x{@| z9trLyd)IS2P3FQ>m>&*%6B=k23iLQ3pgHAZ(YRO8eN1y?_i*@Ok)DBp_kI~2>YmA&f3$?aN`&2+fYsP#Cq}DX#&te?6hBrQeEH>m$uDtb%(6rF}dOa<6H@O|zI>!c| zdG<*XcNRDm$ZXb+Yh<~LpBi8t2|dEbY&EfWBD?eWO*MQgUO(TXbd7+R z)XAu^HdB3b-__k|>iBIZi5@-Ac!!>k=I4ZTZ^G$6x2Go8Z7tam|YL3@!x4z z{~q`9d_mBeZJeP(MQ56S6w*TNVB=2!gdlKfPY8e6$tTxw>@*|Yy*@dwu^N!Qc6&k#x;8Wg%C%s-v%AyYQ5!j_ zv)4Wqj{!!Pj#y4Ne`ti~0Lxnd`5SPaRrYD;QK9)wV3m7?-6`+rYx{LQ zR4I3Bb=iP4Pn`a+`d*!7^F;VzHux(yR#g5^q%ua4G(c`(v}6ULGU1*ttod#(0;pzNcq$unC`Iqh^Y&>MJATK8 zvTw>Bb{q;U{0l17eNIGXY=+{WD0ZVP{k?kGj&opYM;b2qeu+C68C*V{o)H6%>cT}@ z>b9#T(}!7ra_Citw*CK{8vrRYkmG7>XQ?N={_=+X<3L?1e=&f+{To#N790j#+>Hvh zVr+1nxu#f$Z50NW`ww2I{9o<8`9IX}_Xn($rAQ^pIxX+0Bq7;NMM<({-(}zTeT+$p zWJ^N!q%v7%EZK%J_OUNn#!h6N>@gTK?rU^EzW3w)A3ozJ*X#8<=Un@B&Uv15LG{Dc zF9Vxs*vZmv0m#uU)<0i0?`^0XV**S{IPG{b=EhtAcD_PV0uzyD-gZL*BXq$eePH`_ z-^y}N02=M{Q<*Cx`U1t8Q zzl!71tw1Z{y;Ut0dU_%I#BuAdMlj=yEZFjus|bz5&Fkq=62J=_JS^SdV6zz$3{`NV z#@%^XD)8u4mAOjc9HaM|5jpM{IfXo&Q#sK%tdZL!bG{*MCJ)E@27eqEK@W%gpD;-JDC9h27wozXR>XkY zMHn_6nYDs2Da@QG^Y%W$$L0j&FNhPoaeD6w=-cT!{IzrM7A3YR$;+MyxPuco_g*{? z=$#qbM(Pd1x3gISoy(r6`9p%_>PDc5&*pw_*`lK-7g=S*Ub6vE?V~KC)J$sVzGL`} zbML+mhU)N`cU^No@c&0Co9<>3aFpliWrX?NJ&@SqPX)G`NF683Cf?OJi?#itQzmw} z%pqb8oVvejOl&y@6hYluP1tjWOFh>?4>+yAEx6J`RTRh~@6N)Zv`&bKI!daVF$h!B zy$L;-oPfU=*3~1^^B?{BA@}pk<2ss!<3dAgOB~lfzj-NRhMwg=L zMUUPmtDJ5cqP{u)@Eq*q8?z%EAsxeZ4>;cOKi)-S=U=I+K2F|%!*Hvc`F59;Ji82? z5Q6jNq+MUBu=!DeF2S>BE0VT!_}V?2CsOKs#a^pN$1;7I zOtU!1lnd)7aWHr7-xm-~U;}9$2A?53%r+!9QcHec;y!Ne@<~b|GH-1DR=`t5GRS6e zkuCZ7id#Q$R!;2{u(*EQ6He!b&>Z~=bwV|h5@Ak%#u_t-12w`QfS1xqUZ~0LCtJwK zIE~WtqKEl*%PcFY>z^NOD}(jnmgkAFnH&Axiw3@T-%gkUgD>?|0XMx zFVNevWa2F$_&xJe07&yW$sL?jS0;AwP3sVALS4lx6#remxHGh!9VlVs4P6V-!e5$IB! zqqX^AR|eW}R4DVIodT|*zeEe+(JdC=<`xEbh|A04SBj)7g73Hq$bhKcf*D)@%#w7& zeb&JdG+!ij`(z(3&fFv8(UH>InkMhp0?DMXZ6%sEB34~bF)?jd{1Zvd=e51@pygn& zfY}%koH=y!kGN5{Fx43S_)n#&_4HYwkC>);uiN$}@c`7dzIUfEKiUvfwYRC28C|;k z=#bu{5Petu3N#3F=MmexJVm~DYV%y|<}ZxIbMcM2#s`bB(bFS=o|b|_lTOpm_NPAX za9#=(mi(ZuR_TUzv7u|W!ph(L<+D9vOT~WQvBDtiZn%2)(76*duYuz-ON?2Lmtn_X z09QxfaT$p$@nX~d0-x#=a#Wb&_0GVc8EZ-Qq-jR{HX{fnA0<0Ja}@6jRNDw4TX=X7 z+7fsiINy~9G-y2bFEAbH!AS}~sSqrewx_Fby7s{o1n>+Iyl?GR209CDdcSv7%ir)< zcM!0Iknk=GUMWC%)IMu0eqizQ8D;8%{?Q!141T0a#O_9uu|8xe9@mDYICmZ`9xV5a zH|TcN@J$znm~T=i{#Jb{*P(x*>&Y^f?V1J_BTh`xUlXiQyqhX-t}(liYsTLD#yQK8 zFom3tdF59*E4sxTB9`2+g3CSLI~HnBJY3(d0xglv{&3v?v?dI19CO~>ThIG4N#;b2 z4c(`|ceSq;JI4QOmPUBCFukJ~T(;wiHfGPOs)EfhAtPVSP4i^mK7c-6*u5D`@Dq$r z{KJU1I?JYRo~5GF)tM?bgr_7{1muzoE|Rvk@rx5f)umSSCo= z`yUZAv>Quvmh>sehvtom zxcugCxCj3*YV{SWcg`1+M1eD7h?0NuFV5IfnPj z7~XaZRMlk2{4m|z>s1h5Bvl6#V0rJ*<(~*IW~HrVnSe(dc1t`t6St&iH0`f&Ics;P zl_CH8^>itzaqFwDh5k0v#xH}O#h)!*d#RSKO!yDrsLF3e<&6nCTzL1DI^EZbLZ3wW zTg>cBULXq7MB8NAy^+ z&@B7KfpW|U+P8;JvlXaIl=nDfq{9MjISDMj9A7zXjDK$utFCJQ=#&Wo4>>{-(#CW* z?WNtZNMk((m0bVy_o>uBph}wRl?VgGl;PMxj|ki(-{Pku7P_?w zK2!WYCGv0kG&A;FtZl=lf4P3KQt9dmVeV|j6RhMZ4<5t@kl=ng=<&W*RrtJ zB73kncY3>vP)ZXi9(&z~owaNtahq&Po;k%$r*~Ybl5vH{J5=$JvdM6jV!%d&ubA_j zip7;LAHQCXlQ#UpPh5naP1y{6s>8r(QlvP2f3Y*=L(m0pe*yhUHowlQKp|H8VlmA$ z;OqH*(9;q8LqIS%dkrmXPiMe!df<{f&bIBtb%jQ?PfRKrEep0)2Pi8Qv)sUL^0B$G`Kwc?^M`R+gY?sVi^<4Pt_hmq95G$ zz^}r!A9zuZ$A5?mnI8E9mFp78_^uMcb5kMZ;go{GSg!<{atAQEjV`sSANB5Zu-2F> z&x{95Bu??t-SA-^%SNp%E)G9Z7PJnFXP#J%W{^-^7c>_Lbfo{OSLHi8cxqRz5aHBWA4U(UF16~j}fLWSjk^Le|@6=w6@%J6grE@lR6q@p`N<2kv)2sMCUr87z>Asyc)lMqoX@ZPk5^P7`b{`F%b#sVP62EYugc;BFpI6rp;p9-#)e)etCOcEQ(CUPk`mAE zffw;?99=}^B)RYt`wJpanI5l6HlufUuq&|DiOh%O@wTq9x|>t|vP75bn+zstUa6=5o5(Vz-9^gCB*!bjZr4 zC-q(PJ?;grb~Z2y!t0BBu?}#-byE3r#?y4AV4Fcj312snkO;eMk@g&cXny8ATkP?- zAiefakeynr*I>S(apP*`fLe&*0b5KF!EW!@LL7HOMBTQ?#)&veU{=Hz; zo=jmMQMZ-Uj?bcI;ceII)<+MC*9072nKf%%HjAczFN&i~8KfqDoojE}3LZr{2(b`aH~17K8w-h&H#Tv7b%e zd?+BGJ)6kAxWZnQiy|8N*L_P#h^XH76mc@tM-XSZ8UM?|Nyx5CY}VT1@T|+3`{WUe zU(?VR-|_(3*vGg(10xrB*(Askj8gsRu~elr|DEyDHc zvq&%UX@?V3c&}6r1jFcw>&+)u2j-2)=X9HHU5qYR%s<7cF9?6>*>!dF4o2B4NlqY? z^dq(43|)P`4C9IoD|&HBXVA=!7$qx4JXrO)MV4?h%7ta)p$N^(_(o5n$sEI5H|s*e z_NPZS651z=M~%LX)z>AgZ1t9VKJ%Grts6A#9i4F^?e7_%Zyw~#j7fd6`eoQ>$;rcc z#Mqr<-q5(M^lQa+O5WHg+V|d(JF*?Quqm^Z`CwPBsKxEQ$U->5Ul`q4NTPm@uN=~| zOzo9uQn<{pD9Vw zi2vHHN>y{>c`%t2)8~CvJO0>V_QD}wcavo+DJ={0tPyUy8(Psf4kmQ=WkN|MZjR^f zg-TptSLG*C!(GfWb;yE>|CB9G*-iU0`^p8 zyRK7Jtg>>JM?xs)-e*b{3q7e=uTi&mb+C49u&CX4h1(t>x93NJ|NYSgqJS{iJ=@m( zGM4|hBxoz$xszi>tMsn11h|9Dc|phz&J4YvXiJTV@403^~7}y| z-Reh-#7lv@pCdAI8rY!w&q+nW-wJX2%d;w?+V3mlMc@Vk_ZKp&^iX4u8^nqVhb{;D zI(*9_x80~tE3sCMeriz3-{H2ZJ$|up%s6lh>-=D z049)Osn97+ zioqVqz_4ON5kPq(kSl1jdE0k(Hq~x8>?pBZ5AOfcZs}u{LPnR`EA5XYLP|`^g~|ta z!r5YRo(VA9y+}73eoPYV2CjAN>uJv>Urw8a@)Z)Jz2nrx_ic^=Ckt4_-wrpZ-~__0 zW9F6Lx+N>opu%%zz}Y;(k}vx?T_;OX$_1d9mNO+l+>WjYqsHKmtM4LOD#{U_J}^pS z+4ZW2Zu(CxrWD*`OE)Wy*-~sJU!ipU#l=(UybJxmhS^xxy;rtlzgAG$td0(i%u&jDV=SsKRuKmD{`HGQx%!8n*@y$6mgI|Rmx$`s> zj4vw3^8rNi%L8%R0jrVd>Cgp_&fxzPUuudL*a$v~tHxVN3x>FP1~uvQx9m@U=Q^I5 z)R=g-%&uLflA=rnr%OrYtz?|2@6V=GA66EK$?FFCCgJQJ$q+J@=r(Xu#GpDdB=w!d z`lqP+?Tv49x>sd#i9$!` zCYaL-g!JWOb{Luj)^To6fJyfIcVt{mLVPY(VUt+_n&-fEz1)iHT8(jh4>li%SIJd$ z5f^{k25)YcKWMkG|5V}ofqd2794;ISFdt5NJbdD(oSqZ0>w)MNd#v#pL0ex!HH<&h z!)iJ$&>-XY5gK~ZZxDL9I`BEWods>j_#nvV*wY|uIeJD?FRy55#Xi-OCm|xGXro&M zqTDgu$NrJz1Sa|TtAgMoUEh;mXP|q2q|FhF!<9b<`b0kX0i=WJrHuB%GIOa$JOcy6U&3@?u+{cy??o}jY_j^A;| zfd2C--iiZKt!pngtQFO2zOQj9dtVvM*A{f|+P(=<>=;R9|F|ptEh{F(o+y!g{0KqU z_}9Bw&jX4P2>lw|_1J$}G|*x6QF8y)P==)*!Gf6Il`%cj3ke}(-0pj9wM4v=%F=ON zZ`c#kFpF}|^HEGB8Xv3$r&Ns0xZk&>?5h#xQJx1^UxdBVKT3iTw$3O)w9R%+2{m}L zWm(^s?Hm%!qv(&oy15;*uqQ-X$gCAMG3zswoEHRY*~zDhteutCsXMoQVYU!{^is-b zVDX%dwRQWu1c`#YX~!OR3`2O(j7tL8IAhYH-dH#mUK2oyfK)Ts4S3s%SpSOFl zg8R=vaOVAMTj$9+8gDbo1?;;fMhzto?)ExqI}4T+IUrr6)@Jq<}brEWB|P;v~?oA9W1B zY^qY~rWzjfP`dV1`;{>aVcY0!VYFHWe|HX!(&g_u`Nk-1jVR&{sp5WH>KEKm$HRkz z{;eL2Sr4q1H;X;#RP0I_99VYpxfv|pM{O=LmTR9(1@I?FYvOxzyeCHrM!bmPR3 zf11c!{CeupeKLW-%{Kq9&tG0H|DHq8Y@{QPs@RAc=UZS2? ze{1InWih^8eJJeEkDx?U{1G*5h{iMZ~q}FQGnj3m(?s9~9RS~7r z_RPIi$|$jJuQ)P3v3`Ui ztm`kWX%Y~Nr=lNqj@%<+Sy{$bi3|ynG0sq+2dgUu-42$QE+a~P8aY`b$9#80Tnabz z9Ydli=t0z`tJ3LmJmyFyuN$nI zDM~XM4{|N6mZr=0 z_h069SSAlHIAA}mch?PGUj4K5`>11Mlu2AenL4d%(h0vQ{?{a!3Gg9de|rzBo7=h`8iKj1>l zJs%iU32nNb?vjaeos)E({!f(nZG;=;Te3!2QhI+%FW%dchvxd79zXd=vbozRd< zk}}yV-3_ly7~NS}#U4)C`Ln6L^0AR)ugv ztX(-2qF|J~B5{4!@j^Xwi2jqQiP~DzXnZ+K8%(yaCWP$N(eo)VX`@-z#YK;dSO`6&@!)HdH40o3VK=^SBp=q^n6Jdi5`>d+70F6BG z>R6KUZe%yZwQ>`#o44*N1J7g#c}a0VWSn3nO!AFI;B?l;hKzX0zR-THc6s$?IP%ga zBF0rx^PtCyFfEYzWJ+P={xm8bzSbFfNYP&hLH_H3tMr!_(eW<{00J_N>6=d)Hq6LB zT^nn`Ty@k?zDYVa+V*GURVpJs;3nraYG`n>YR;rM zS~5ec5S-r4y06-T;EIdxxYfOJirgIg@t){hn=^O$m+Wm=o$5uLf?38U`ua9cr@JD7 z5o&SlT6?%m2zh4-GIB+I=bY^WW{~X0k!8~B&!|Eh-WHZpc_`d!RoKe>bGLIx{=B$| z5tZnRYfEYL)ppC>6?G50B}r|VnGgEJUZK&!oav}R!lj&~;rH^i(4$oh#*F^iTsCka z$q2&snGX z6OLnK3qkixCa=il8K2$gVNC$&tKu#-IzukQ_40IF;JRt%&l1kVSbubF)d8YK;uq?^b7|3}9C;@*!qet&g6s)2sU9p(kqnUq*xnU=Nt}gC zW`mY8U#ZJJf|EJdN!#o7a(kY=EIv0R)e1VZ0naHirVjr@ZP-o=X3ZYWH(mU#UH4~|LDWb9Tbu&`tN9duX z2QLX%2wRojDDQ*HDVb3j^`Wk?H$F4#qRK*!z7YC~Do^8BU{DFF1sz{;97J-V32Yrv zG>T!|H(jGL83X%1BCWQH(`UAD6E^|)=h(`X5HjQ7{O$++(B^{!o_b=wqv5egWhrj1 zuM*4WCH9)}3Nmeu3Uc7@_YZgrjc$UF&Wnk17q+I;f@R6ik+0zdwH$ z^7q(^!z-=^7~ZLTXdj@DpZN`vFzS3GT|7#c86ru^Dj~A>X6Q-CzPo=bAKF8&P-V+c zmc|!E!eFq>xog&Un#xVeVgRU!VYD^qfn?W6XU&hq03~nZ(YvLHUk}-kH31gt0J6k-YOI^5 zVE%%BwSw47QYLd{JW}>^%!;R%PIC_knDDMc41UCt^)Q*9j64zA{i=)FoO%6PRJ8Kr1+xr->t5xfwya zn?-sx@ESE@ljeMG)7POH-&SPQBExfY;+JA(t$dxPCjt3-F4ti|rhH^0*KJmyG<`Xs zfN$o@&ol$rYR_sjVOR8Ickz_n?=O*(#3i>(S6@&GHIU_oKXRT&zeeFY$``7?BabS* z?AQg8h(Q+KSXDQ+rnCJH>}bTYZui_D6d7~YV5c&T^~qi8pO7>gg{Gp72pa~7t~Wdj zD}?-E{=jE~6<*NjxdCW)%paqQpK2>!KY%l0W>qaG*$VV--%(ORyr9NJ(9H}lnrZtC z2EvYLR;ynt!sW`Vd&*75HT$Omci64;c=<{`@%cr59!?C~G6eDhwRuEyDkIHvbqr)U zKhwrJm;Xq@VC=$HGODs^CKbEQsS#l`^{tMz^19>hau~H8;`qFKtR-3}*0&yDt$L8P z@(5D)c`eYF3VuETszV!9uwJ zRR7Lp_t?NC|7wOaERfTP!OT~`v!AuM$6t__mydP#7VVuL%19aAR`vMoVV7>D!(vhv zU=l)dfE+u%qSC!^PgLBoWz0sK@F-p9bz8n+C7x_SY*-I8wQ?;+1Dj6LvE@>P#)$OZ zKF=nIAwj~Y_@56e%M z3|?9fu^(c55CSU(PW&bA3kp*8ttB1i)A}XdhQ(h)Ok>$qLz{>o>u$1(P;p+KC4$P< z#QZIt#Zys-j5!al$pUvKAc4O{Kd{EL+B|k>5db~zsI-yO z`g=iNRG_(Y&3n{jGv}3XT|rg*ZVRPIhzR^XlJPUQOU^wVaQ&l_(YL-78D z5P^=~;=XtY};>YThe;ex&85 zd+hQ>+pA}XweY<<7EPhtmaU<8K>QFb&~h#Hdw~U&jJesG%~6K>@rEO zwaO*0S>sBBx@NsriNw)HEyXb6SP>Vm9hz{x8QS73f2HOw6sjOKJ~BiJXy^IG%Cc&oVx6IpbsoyEE_X1hbrP+=P||iZDzCha#Z@y!YT@0|epv`EdMUwd%kkrt>SiFV6fk4;=CrI@$nEJI`w1hTHxqbrh2YcjX2TCAIBRIv2B$wG9XQ-e!l zGc5D8*H#>Zv!oYnAHH~g-HgmXDtshA8^nK6dCeMVerrhVR;iYGH0IXKJ4&%9F>UD9 z+*ewnuKM2l>5V2JDTUSQ9lpz4vt?&P3l=3#_}z;BH2VQX-R5~METI=Xi}XKvdTAj!vg#4lfeb5rzt}bQr|Ms3(Ys7p&Y~x)hY$QfzUe_EaX9a zXlBYN(a^Q>Y%Hvsi8S#n%znM(@!wKYetoq0x!6_JSv}5mqOvqMa@R;w{#WVBX}-&y zlBGKs%2ZFrdL3~&J@3sy!cKV8uO$yXb3a$eZ%4B_Ym)>&1Hm9;of3-;4AVe&bmqy+ z7Xy^{gCg9E>WF{Grq@N;!W+|YLQ8qN3nrx0B0ZA#HMgS^d)B46?fqZi98j`wgyAK# zgk5W*M{oTR(QLe5Ov7mWDMB9k(GQKtnT3LZ=(Kh`TP^anCct!0A{(5>z`1vUsf;ap zezOa?tA&fi1XsV@LhAKPYh}hRMWog$IDyy@wmXzN&IPHd3o?GDz-*L!DlXa)@#|Np zY+aYbKybzt$Vl_%i3wS)qC&1}M6qj#yk*yrACQFP zF6&xGzKJW)dp7>oA)tf8vA!vi&J&b`UlLuHqvl~qZ>&J(KEaSS9t7Jt^s@8)O)N5N zT|RNz^3~Gl?Sa;c8b`?9hG@bh3%$itMI?ari!beuB#~>C<*#JS-=8&GJktizUNMH? zsGj?K#C2kcj4s39$j`SH`ai3un^MU;l*zZ5g81%OrOp_02q%<{<|r?%sv70F9<4vI zCiQDMV{Ym6un@XX&Sw3}BUBi=e#c=zcWxk>EAqA0Ie$hx-W|RKxTS)+PqZL)(1H zUBMTO;hDyrh+R_p2KMkyY<6AvoW9sm@US1;E`uz|j*)R3HPCQX^-d>$IYqmLt{0RS||>Ke-R-mT@JaT+Tmh$7Zh2Ciz@7fjhhzJhT8BDv-e|7o*>J$$Gan-CS6#~6J%jAE8S^+_AoklKwM>%--ka@hBXq5tL;JtJu zBxOs9@WU7u7+BVO2^7Der3mz)T>?>HwOnzvSHdOhAL%qeI;OZRW3H5>1B*gz||0d$q+hpQB2!ItC7Ge&ELqa-Q~E@X6l#wQQa7uLW5_a(S_G zt^D&z0+<-nX9^0!wEnhd3Xy$RQen8{rES_UT?Bvp?{mpgs? zqCPm;vArpsUQ!|yLYEFDY5)?MG;w>l*GsCx^ZtnwRm<1nPmvMs-Es;5jUE60mG*wP zu3h%lXL?SFfEM>TY}XLbuHT#pJnR$db6fvd-2<$YXZPMMjQI4?4(Q0kc47r1z?0pn zA=@(A0Q7e0y?>H|p0%!UWG^EmiMBtVYsdv(Vly zv&BmNO8v_JZ#J+3ZQS!Lv7U6+v?Ng@Q-LD-PY4Sj#Q7otxf+%-c2jgs&OaGI7m697 z)t~swCM*6M7r%h~r);~@Dt+=#N`k;%;YDydQToSCed^S2i;*7=`2T`@$R3AuL9aeG z)6nQnED|tDUN9>CJZ5Zm_%cY*-x^tJp;xb8p85TdnGT@bc6k2DXKY|*ToD<(N~^;S zAec?jsp>1g3Mpx4K`^lN=7qYSRIQTuas+gZk&>(h|Gw z;7_+`GsGQShxP8W6cqv?))st6X#lvt?EOO_DnNo=7qfxOKwEF9%QK> za?J!pZcKk%Rh4rVAwqplfLC9TL8m_J86B-c@RHiu*+voHwQ0oj6KnZC+S9@iJb5<> zUxR&MY3KYSTxnzqjgI^d(+mP7X!eU#QrrWXa0;w{((ej zAvWKYj~+a*h(5Mbd#pe{HX8F^c%gh*^{$t>`Q(45|M$#4659hdzUuVWgymTy?@y>h zd2!$?|F<)H93DaDnW{tlK4pjHg?hB|H^PQ@BWCt?kQ`jEmuIcUwMl>xodJj|u*h%X z6w`>b+9PotU(Q?789!tL()fiG{p{;2vEjwTJ>*0>Z#$cF#~yfKXp^AU2&^L6E&%tQ zs+M|p`3>@N!zXO6|DVhr6&lq+5QcO?I9+HYHfp9k-Kl5d`!D}nqYKw)&xl~AM%Bv{ z+wr0^V=@WW{6Ekb3`NtF))5vUthVjQvmVAN%(H%uRBt}(I^?Mxp#cs z0Ltt0obSPQRe!nK+(7X0N=AdCN> zvj}SY=Lz<$aU_ouXE8dLIHOdrE^r1=c(HZaJ={BAz};MX{G5+N|LU~=uGxMXWNRJi z_h{?Gub}-ux5^(!WRiWBhG#i%+cZHDuJf6E{kw4O>3MMN*8KGR;w++^CCiB$HA&DD zQ~vmXCf8tUE#J=~eqf%%?x|INjlE7A^%huK4UdwT^LQ*%Gsd4@q`e7Z;ZH4s9RCK` ziNA*ct7pK z+W_|qKCFU|(*A4t2n4v~4k?%E`f>Pl=^xpOe*KxR6D|M0vY|ncJRtZk*cLjUa)WQ_ ztiXBMh#0&0sH~G2W8+ z{kEc*q9Sk-6u$7d1X`JCOu3X(d8j7!J8ha;jQu02m4~C{x``7woMIkV|GoINi-uln zzH=!1%0Jl|2x&~C5R3OTHXlzP^?Mv%3&#=@kqu92i#r`VST2FoXnoV6(NXf9KAMQN zuhr@P&=voycC3Mx zD<3>tf`_d(!JO=R@I6ISX@sA2VJ8M9A$Wx8V7^y$-bZ9l^v1 zxJJLkldzFBM5xAxgHYo9Y`Cc)T9hn9++3IG7m>ORnXM6g+el?o&$ zd|9V?YY2wY=YfSk03goyZxIE2R(VcvQXq5@T}?I9fSH~A zKm^lNW>5R$iHY3tr0A@y?xIk#NGb43i{#`e&uYdcY2QEHx^GmC@_d&J@>UJj`2Ful zzZE^=#~Y--mG6G8PVC!5PV*Hb)uRpWh7|*wdN~Y- zmNu7dPaNY~ciYZw8KF#C#*o)9x6Ip#E=95z7afjiQvgDfYwv>+*#V>8SY*n(a5En= zWJ`G!@7?VIaCA)FCa|d?j1f7)wx8d#rSq!cN(BJ;xNfxkail7ab+0$PGfC2$+CvK+M>0$b1J!gC%F^9`@dj&hbQ2 zClNP(aUFeFz-w|$> zqG8rQ*8-x-q_uH)8&>ZoAS@zWQVq$lnqjt$Iik$*YE<)zTIko%*rQ&W{$l7z6nk~K ziIf?9K#Q%#`jSRl=^>$yYMVwsE{zmHQ%&!%h6FbkZl6|Yyg7<79A^&8D`6|#cm^VRpxzZg ze5dU#D|xh;{Jp`Fo&xe{6OraZy%U3Q{QjSa{(mkUD{dO= z>_*X2f*B)Y>|MYkO)o~$MayD=@+8D-?HEc_s!nXy_mp#gW5-7(aINLjWeq??S_B|B zwU{aq-E8B9kdNBi<0WMPLgu1yDTo-GO7IqW=oEV-ec5~99x^@@>lF}y*Hip{bv2@B z2@Ch~eJOiOK!L=TOXsXbCg3qLmBrViL%*TitV64rdwnNNSX8TZl(8EFcXgmdNHs>I zl9sP0aeBig)!$0VJUw@Onyg3NWZo#q(ycN(8JC*A_LJtPtQn>=qW$j4;!I7&lSUt>aL@EchW%Di|rxY zXBkY>A;edcxbB`~3Py!%k%8L1Z}uMxhsSmo@!94=ZDxwm3#73=4sRJo%yXa~OYiM( zZX1)l1K&6b*t6cD51Omd`MUkIXaOTEotO+Na~CJ^bac zzBPy9<##AbrAM~4g_)XWbnPB5cSLH)Blhl-5=SQJSlD1ss4-~|190u(jED|A_kd*nk0uhl(o@2zw z_$T=d%R0rL)u#@r{o%oIruJ6J#PzbllO9p&q9RDc84|Mef<*_vbWc(IB`H(;?%{J5 zTkjViy89MQPD@Xo*Ru)AQUhH4N%xZJ;;JwGmT(dp1KKGyn$YMWXA-~#zFus)`DwTT zGR0Y8!Qt**gsZk|KfRQS_v)8kPh(G#x(k znEOH$)T+Eh0c*y=TP~Va9*|<^#*sNWWTS4&Nh%@A1%rkh$>b|6Ds}}< zCemr+zrKJw-FLhj_@~QqoU5v9l^}(w0K01*rHj6t_+%3(R9SEog*ZO2LyR*tREyhq zq$kYU*MP-_XaE5%9TC0MG!E^Gqsu)G!NF4OjgxkChWec%19gmBpH=QTbS+9kYxohv zPf-T2w<0^%-Y=T=9(4idcl`9bZM^rFMRX|exxsaUUx3$FX@-Jjfo$v^5LtVZZ@NcZ zsO;XXTNEir!M^sRNzZlYzXdT*4n0U~@iK2Buiq}xO&JQWV|=EeoPe4=RlVWI+~|oB zxwvvyn$<69c_%!IrS#$C%!jF@sUq;DXSgX-89USOFqG)_Mc-;ZOi*RdhRfe zm(QJr*jrNJMt=RfEdy%8H2*e>iQP~#bL_BB6896`1TNUUi5D3JeXaQNzJoPzR7PuH z$vx9Kq~eZ@-u>zANJR)(N2Nx7PK~Hjl6p9aSDS>`uSzIYb{E%Neb#yIP;~Rrqepvt zeD1Ga=klFngX2;)L!ZFmk%J1ddET!q3KFU@9<1!OdMTTSK^d8un%v|fN=4Dx5-Eiu z9x_MXFN&AnCiWGwZx7LQQp>)D++d92125G(Q(Qo!!)CTpA>%HERK*>Ai%A0HN+$58 zMc4dz^rKSKlWrk%5#@w2mu@6M6?ioro#H>8sozk4JTMzIZEZwe4OW5gO6obEH{1A$ zN=uX6N?MJ*Yh9oiUr}MyU-O7DPKb&&ao@MyR#C1i^&$q=_O>KRFhzh^LuEcSV3v*Q9l`O3+8QNmD z5;F&0r{^o*ThVBihSUVf{sR5TSJ=_nncq=dzssHj$|NN( z8Sla&ot?qNBX-|#rkYEFokIemmpHTYbD^QgS#6G!7yA5#o{ikVtHvV3$`mergTKcJ zt^Q_a_IoSEdSdnkalK9VTjkvkwG6W7l+gBOzRjJe6>Z?ceOn1+=LY4E!2Pu!<#XTa z4yCHh!f`E{)zS2(=4Sj_Fl^=fLBBT>G~+a#B~Lo4vmYPyt}(uMS}k}nI-rxs`f!o; z!pFABI^K(G!W*$vU}Wj5kXWxTR~r%G#O4Fa9_>dk_!)aOP=tj8*pmhQX5ioN{0Z9j z?5`QfR+O^mj_d~o9xOd?9{c%IkdtNw_9`oyH5pi|<`^U3hrP5&Nn-B&;D=HmjKTwE+cZ|!ochKIMWOdq(Kz$HHkejX!N zQbJ1;6EP6(Q=!$17N7bI9dW9P#7_NVM|5w}^vqT?Sg-vCc8hV(t&XHhPEk@tBkCz? zFrWo2>(%+NNyO1YaB&f$0fc2`ev{*g-F?y$khZdJ2aZ2G+lM|0UmLvlR$c@p{Cd(Q zC1S5;(r43*=IRR?Iiz1_?3{wLF`_x$Tlo;Sa#c#JsA$c4NX=bPE!f=I?HjJsW%ob} z3jMGG^EaVZ;(FLEqtSIr-d}TYd-IKQsM}9=F-SbnTaZ|F$ojfqR8u^7MpNth-XhGkBK33Vav|m z!r|1yuK~`(c|6J9ma_{!4;Wv^key1h(&#c~X{~97!wJ;P3eaPP!Gl!|{w2Dt-Fs#v(Ix z1?7l-T^Fb~GI@-QEmqCdRVoE^Pq@FOK>0Au0G6E0!oUAEntrg=-RO}`n(mOb1xDIy zt$=dqqBke1na3-RUXpa$*cqLzmt4Ym0PhMW+3+D>ph3wUPdtVM^$1V)(u zwn)N*?09=r_G;`*P-do0d7d?3MJt6e6tzg4Z!@-WZtOPaTCO4F_HCoBsKE&oz3m70 zeM;X6Q!E6?Q_}ylFzgcHVdJW1mZN|!G3ibnnOo>1Rsqw=m>e*O z(5?`Vzb}EYKMicP)ioMe_4_DR4)z%}v5HIUn_`u_D?5OV?f)e2YGG2RDpyvn7&zo+(*mwy%6~a# z*Yd*cQC;77ZEZx`SlIs1?4~KrQDwizXA7~q>Zh@x{ft?V#Es1z{Rc2wxQUeL0Po%2 zLKw`s&O%NB1Y!l%yVMWaLB46SXmm=hnLnbshx>6s#r&y@DCpQz#Oib94*fpJS; zs_`-=-9qz-(%Y4PnE~>_N1zbf0#Tolp@HOWrA4#i$wm0VFthzvAO$0@FcLRgEWFKlne8-Hn+!5i`B`9pNW~Lmhzc|#1CiC(#0E4Ef!-%`ZE30j88|TWEVC4kv*i7=l3p%!vbwB>0M2wOC%pE z34uz#dIZO9pGA`aa__bazwJuu?u?4VuW=|k_SV(bI-1DpRp^CX8GI_Tts}1x^W50N zWwm(W=C-1R*@|2+@!yJ`Dzj!x1oS3$nXhi*H$zS@Rvr$|)U4%mfi@C2jjXHB@s|oB zle@d$?<+R>RQEDIi@@*u%gxo*Q5PtVr%vT)Gh=sk%(=N{r;?=Mvs5b_rX>cs@-Ja| zViLk9&z6BHblsKL>GeK@CL`coT<>z^)b{tI9dG!^yGz*D5Xv6|1FFDC2++mVvy1RH z=y0yn@i-`M-!nx!&)WBEYipC|8BM|19qjCk(>X-M#q~Dk?gcTgJ#};Zj))7oPv`_= zSlsWts)W$+%6Gta%Iq7p{xh-nNIFK__<2_veKRjjU_k!o?MUR4rm)6^*mmwL7kSVXrKo<%HjGa9_6d z<(zL83(Zxhb0 zw*LI!h304GAq_@BSp0tGohM5O1*k1FWkmUu?Nf1aZ+5dCt5WR|&-l1#BMy0)o^HQ^ zNGk@RlSr0VD`~`RVQ)SR%JLm`hqOg@b;!)fAYh_G|BUi|>OVElV6!=6GTV*V{#1qi>qZ=$CNZ?b| z?)ZY<31h?Fo1a`+36S=DaZf~l7zF6#;-}_9`XehUZpah!mfLdistaJ8X-SfdVxEZq zh&a`w(qP^sErje83chA22tVsLw(Hy93urM$-NS$u9IGC0<2KXs+sj)JKQ+k!Zxx<< z1hzsFtItR`U`Qt1S$U;a6?!+@RzL3L$*J)-DGj^E;mbzCyn#>!V{6?f2RNA)+HQX0 zxaa&+Kb?~2#J-f_i72h-y5l~cUJGn9Ak4umOgY?JOzg$Pl}bUoEGI~$P8OMvLY^sS z8=KY2v)m;+MB`>evOaw`bYV%nd=O>6Glr+WIN|aT(3aC zHW6=BuD#B_c=33Z?fztAW$os;Zr~&#`FV4_tc=RyzETX8zWU?GcT?J~iCzlwiDEDS zo(11W8do;B_pmSakj*qC-SJL`= zp@l;|5e8~%>VM-HJ4x2`ul_WvJ*t{%VTa64e?L5tGy2AJp~Cl$wCkZS{VZW_4$##y K(yUasL;e>})_utU diff --git a/doc/en/img/pytest_logo_curves.svg b/doc/en/img/pytest_logo_curves.svg new file mode 100644 index 00000000000..e05ceb11233 --- /dev/null +++ b/doc/en/img/pytest_logo_curves.svg @@ -0,0 +1,29 @@ + + + + + From 534d174fd24f8066518e396fbdd559eb7cf7b5a9 Mon Sep 17 00:00:00 2001 From: Chris NeJame Date: Wed, 16 Dec 2020 11:53:14 -0500 Subject: [PATCH 017/630] Clarify fixture execution order and provide visual aids (#7381) Co-authored-by: Bruno Oliveira Co-authored-by: Ran Benita --- AUTHORS | 1 + .../example/fixtures/fixture_availability.svg | 132 ++ .../fixtures/fixture_availability_plugins.svg | 142 ++ .../example/fixtures/test_fixtures_order.py | 38 - .../fixtures/test_fixtures_order_autouse.py | 45 + .../fixtures/test_fixtures_order_autouse.svg | 64 + ..._fixtures_order_autouse_multiple_scopes.py | 31 + ...fixtures_order_autouse_multiple_scopes.svg | 76 + ...est_fixtures_order_autouse_temp_effects.py | 36 + ...st_fixtures_order_autouse_temp_effects.svg | 100 + .../test_fixtures_order_dependencies.py | 45 + .../test_fixtures_order_dependencies.svg | 60 + .../test_fixtures_order_dependencies_flat.svg | 51 + ...st_fixtures_order_dependencies_unclear.svg | 60 + .../fixtures/test_fixtures_order_scope.py | 36 + .../fixtures/test_fixtures_order_scope.svg | 55 + .../test_fixtures_request_different_scope.py | 29 + .../test_fixtures_request_different_scope.svg | 115 ++ doc/en/fixture.rst | 1751 +++++++++++++---- 19 files changed, 2413 insertions(+), 454 deletions(-) create mode 100644 doc/en/example/fixtures/fixture_availability.svg create mode 100644 doc/en/example/fixtures/fixture_availability_plugins.svg delete mode 100644 doc/en/example/fixtures/test_fixtures_order.py create mode 100644 doc/en/example/fixtures/test_fixtures_order_autouse.py create mode 100644 doc/en/example/fixtures/test_fixtures_order_autouse.svg create mode 100644 doc/en/example/fixtures/test_fixtures_order_autouse_multiple_scopes.py create mode 100644 doc/en/example/fixtures/test_fixtures_order_autouse_multiple_scopes.svg create mode 100644 doc/en/example/fixtures/test_fixtures_order_autouse_temp_effects.py create mode 100644 doc/en/example/fixtures/test_fixtures_order_autouse_temp_effects.svg create mode 100644 doc/en/example/fixtures/test_fixtures_order_dependencies.py create mode 100644 doc/en/example/fixtures/test_fixtures_order_dependencies.svg create mode 100644 doc/en/example/fixtures/test_fixtures_order_dependencies_flat.svg create mode 100644 doc/en/example/fixtures/test_fixtures_order_dependencies_unclear.svg create mode 100644 doc/en/example/fixtures/test_fixtures_order_scope.py create mode 100644 doc/en/example/fixtures/test_fixtures_order_scope.svg create mode 100644 doc/en/example/fixtures/test_fixtures_request_different_scope.py create mode 100644 doc/en/example/fixtures/test_fixtures_request_different_scope.svg diff --git a/AUTHORS b/AUTHORS index 72391122eb5..20798f3093d 100644 --- a/AUTHORS +++ b/AUTHORS @@ -56,6 +56,7 @@ Charles Cloud Charles Machalow Charnjit SiNGH (CCSJ) Chris Lamb +Chris NeJame Christian Boelsen Christian Fetzer Christian Neumüller diff --git a/doc/en/example/fixtures/fixture_availability.svg b/doc/en/example/fixtures/fixture_availability.svg new file mode 100644 index 00000000000..3ca28447c45 --- /dev/null +++ b/doc/en/example/fixtures/fixture_availability.svg @@ -0,0 +1,132 @@ + + + + + + + + + + + tests + + + + + + + + + + subpackage + + + + + + + + + + test_subpackage.py + + + + + + + + + + + innermost + + test_order + + mid + + + + + + + 1 + + + + + + + 2 + + + + + + + 3 + + + + + + + + + test_top.py + + + + + + + + + innermost + + test_order + + + + + + + 1 + + + 2 + + + top + + order + diff --git a/doc/en/example/fixtures/fixture_availability_plugins.svg b/doc/en/example/fixtures/fixture_availability_plugins.svg new file mode 100644 index 00000000000..88e32d90809 --- /dev/null +++ b/doc/en/example/fixtures/fixture_availability_plugins.svg @@ -0,0 +1,142 @@ + + + + + + + + + + + plugin_a + + + + + + + + 4 + + + + + + + + + plugin_b + + + + + + + + 4 + + + + + + + + + tests + + + + + + + + 3 + + + + + + + + + subpackage + + + + + + + + 2 + + + + + + + + + test_subpackage.py + + + + + + + + 1 + + + + + + + + + + + + + inner + + test_order + + mid + + order + + + a_fix + + b_fix + diff --git a/doc/en/example/fixtures/test_fixtures_order.py b/doc/en/example/fixtures/test_fixtures_order.py deleted file mode 100644 index 97b3e80052b..00000000000 --- a/doc/en/example/fixtures/test_fixtures_order.py +++ /dev/null @@ -1,38 +0,0 @@ -import pytest - -# fixtures documentation order example -order = [] - - -@pytest.fixture(scope="session") -def s1(): - order.append("s1") - - -@pytest.fixture(scope="module") -def m1(): - order.append("m1") - - -@pytest.fixture -def f1(f3): - order.append("f1") - - -@pytest.fixture -def f3(): - order.append("f3") - - -@pytest.fixture(autouse=True) -def a1(): - order.append("a1") - - -@pytest.fixture -def f2(): - order.append("f2") - - -def test_order(f1, m1, f2, s1): - assert order == ["s1", "m1", "a1", "f3", "f1", "f2"] diff --git a/doc/en/example/fixtures/test_fixtures_order_autouse.py b/doc/en/example/fixtures/test_fixtures_order_autouse.py new file mode 100644 index 00000000000..ec282ab4b2b --- /dev/null +++ b/doc/en/example/fixtures/test_fixtures_order_autouse.py @@ -0,0 +1,45 @@ +import pytest + + +@pytest.fixture +def order(): + return [] + + +@pytest.fixture +def a(order): + order.append("a") + + +@pytest.fixture +def b(a, order): + order.append("b") + + +@pytest.fixture(autouse=True) +def c(b, order): + order.append("c") + + +@pytest.fixture +def d(b, order): + order.append("d") + + +@pytest.fixture +def e(d, order): + order.append("e") + + +@pytest.fixture +def f(e, order): + order.append("f") + + +@pytest.fixture +def g(f, c, order): + order.append("g") + + +def test_order_and_g(g, order): + assert order == ["a", "b", "c", "d", "e", "f", "g"] diff --git a/doc/en/example/fixtures/test_fixtures_order_autouse.svg b/doc/en/example/fixtures/test_fixtures_order_autouse.svg new file mode 100644 index 00000000000..36362e4fb00 --- /dev/null +++ b/doc/en/example/fixtures/test_fixtures_order_autouse.svg @@ -0,0 +1,64 @@ + + + + + + + + + + + + + autouse + + order + + a + + b + + c + + d + + e + + f + + g + + test_order + diff --git a/doc/en/example/fixtures/test_fixtures_order_autouse_multiple_scopes.py b/doc/en/example/fixtures/test_fixtures_order_autouse_multiple_scopes.py new file mode 100644 index 00000000000..de0c2642793 --- /dev/null +++ b/doc/en/example/fixtures/test_fixtures_order_autouse_multiple_scopes.py @@ -0,0 +1,31 @@ +import pytest + + +@pytest.fixture(scope="class") +def order(): + return [] + + +@pytest.fixture(scope="class", autouse=True) +def c1(order): + order.append("c1") + + +@pytest.fixture(scope="class") +def c2(order): + order.append("c2") + + +@pytest.fixture(scope="class") +def c3(order, c1): + order.append("c3") + + +class TestClassWithC1Request: + def test_order(self, order, c1, c3): + assert order == ["c1", "c3"] + + +class TestClassWithoutC1Request: + def test_order(self, order, c2): + assert order == ["c1", "c2"] diff --git a/doc/en/example/fixtures/test_fixtures_order_autouse_multiple_scopes.svg b/doc/en/example/fixtures/test_fixtures_order_autouse_multiple_scopes.svg new file mode 100644 index 00000000000..9f2180fe548 --- /dev/null +++ b/doc/en/example/fixtures/test_fixtures_order_autouse_multiple_scopes.svg @@ -0,0 +1,76 @@ + + + + + + + + order + + c1 + + c3 + + test_order + + + + + + TestWithC1Request + + + + + + + order + + c1 + + c2 + + test_order + + + + + + TestWithoutC1Request + + + + + autouse + diff --git a/doc/en/example/fixtures/test_fixtures_order_autouse_temp_effects.py b/doc/en/example/fixtures/test_fixtures_order_autouse_temp_effects.py new file mode 100644 index 00000000000..ba01ad32f57 --- /dev/null +++ b/doc/en/example/fixtures/test_fixtures_order_autouse_temp_effects.py @@ -0,0 +1,36 @@ +import pytest + + +@pytest.fixture +def order(): + return [] + + +@pytest.fixture +def c1(order): + order.append("c1") + + +@pytest.fixture +def c2(order): + order.append("c2") + + +class TestClassWithAutouse: + @pytest.fixture(autouse=True) + def c3(self, order, c2): + order.append("c3") + + def test_req(self, order, c1): + assert order == ["c2", "c3", "c1"] + + def test_no_req(self, order): + assert order == ["c2", "c3"] + + +class TestClassWithoutAutouse: + def test_req(self, order, c1): + assert order == ["c1"] + + def test_no_req(self, order): + assert order == [] diff --git a/doc/en/example/fixtures/test_fixtures_order_autouse_temp_effects.svg b/doc/en/example/fixtures/test_fixtures_order_autouse_temp_effects.svg new file mode 100644 index 00000000000..ac62ae46b40 --- /dev/null +++ b/doc/en/example/fixtures/test_fixtures_order_autouse_temp_effects.svg @@ -0,0 +1,100 @@ + + + + + + + + + + + TestWithAutouse + + + + + + + + + order + + c2 + + c3 + + c1 + + test_req + + + + + order + + c2 + + c3 + + test_no_req + + + autouse + + + + + + + + + TestWithoutAutouse + + + + + + order + + c1 + + test_req + + + + + order + + test_no_req + diff --git a/doc/en/example/fixtures/test_fixtures_order_dependencies.py b/doc/en/example/fixtures/test_fixtures_order_dependencies.py new file mode 100644 index 00000000000..b3512c2a64d --- /dev/null +++ b/doc/en/example/fixtures/test_fixtures_order_dependencies.py @@ -0,0 +1,45 @@ +import pytest + + +@pytest.fixture +def order(): + return [] + + +@pytest.fixture +def a(order): + order.append("a") + + +@pytest.fixture +def b(a, order): + order.append("b") + + +@pytest.fixture +def c(a, b, order): + order.append("c") + + +@pytest.fixture +def d(c, b, order): + order.append("d") + + +@pytest.fixture +def e(d, b, order): + order.append("e") + + +@pytest.fixture +def f(e, order): + order.append("f") + + +@pytest.fixture +def g(f, c, order): + order.append("g") + + +def test_order(g, order): + assert order == ["a", "b", "c", "d", "e", "f", "g"] diff --git a/doc/en/example/fixtures/test_fixtures_order_dependencies.svg b/doc/en/example/fixtures/test_fixtures_order_dependencies.svg new file mode 100644 index 00000000000..24418e63c9d --- /dev/null +++ b/doc/en/example/fixtures/test_fixtures_order_dependencies.svg @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + order + + a + + b + + c + + d + + e + + f + + g + + test_order + diff --git a/doc/en/example/fixtures/test_fixtures_order_dependencies_flat.svg b/doc/en/example/fixtures/test_fixtures_order_dependencies_flat.svg new file mode 100644 index 00000000000..bbe7ad28339 --- /dev/null +++ b/doc/en/example/fixtures/test_fixtures_order_dependencies_flat.svg @@ -0,0 +1,51 @@ + + + + + order + + a + + b + + c + + d + + e + + f + + g + + test_order + diff --git a/doc/en/example/fixtures/test_fixtures_order_dependencies_unclear.svg b/doc/en/example/fixtures/test_fixtures_order_dependencies_unclear.svg new file mode 100644 index 00000000000..150724f80a3 --- /dev/null +++ b/doc/en/example/fixtures/test_fixtures_order_dependencies_unclear.svg @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + order + + a + + b + + c + + d + + e + + f + + g + + test_order + diff --git a/doc/en/example/fixtures/test_fixtures_order_scope.py b/doc/en/example/fixtures/test_fixtures_order_scope.py new file mode 100644 index 00000000000..5d9487cab34 --- /dev/null +++ b/doc/en/example/fixtures/test_fixtures_order_scope.py @@ -0,0 +1,36 @@ +import pytest + + +@pytest.fixture(scope="session") +def order(): + return [] + + +@pytest.fixture +def func(order): + order.append("function") + + +@pytest.fixture(scope="class") +def cls(order): + order.append("class") + + +@pytest.fixture(scope="module") +def mod(order): + order.append("module") + + +@pytest.fixture(scope="package") +def pack(order): + order.append("package") + + +@pytest.fixture(scope="session") +def sess(order): + order.append("session") + + +class TestClass: + def test_order(self, func, cls, mod, pack, sess, order): + assert order == ["session", "package", "module", "class", "function"] diff --git a/doc/en/example/fixtures/test_fixtures_order_scope.svg b/doc/en/example/fixtures/test_fixtures_order_scope.svg new file mode 100644 index 00000000000..ebaf7e4e245 --- /dev/null +++ b/doc/en/example/fixtures/test_fixtures_order_scope.svg @@ -0,0 +1,55 @@ + + + + + + + order + + sess + + pack + + mod + + cls + + func + + test_order + + + + + + TestClass + + diff --git a/doc/en/example/fixtures/test_fixtures_request_different_scope.py b/doc/en/example/fixtures/test_fixtures_request_different_scope.py new file mode 100644 index 00000000000..00e2e46d845 --- /dev/null +++ b/doc/en/example/fixtures/test_fixtures_request_different_scope.py @@ -0,0 +1,29 @@ +import pytest + + +@pytest.fixture +def order(): + return [] + + +@pytest.fixture +def outer(order, inner): + order.append("outer") + + +class TestOne: + @pytest.fixture + def inner(self, order): + order.append("one") + + def test_order(self, order, outer): + assert order == ["one", "outer"] + + +class TestTwo: + @pytest.fixture + def inner(self, order): + order.append("two") + + def test_order(self, order, outer): + assert order == ["two", "outer"] diff --git a/doc/en/example/fixtures/test_fixtures_request_different_scope.svg b/doc/en/example/fixtures/test_fixtures_request_different_scope.svg new file mode 100644 index 00000000000..ad98469ced0 --- /dev/null +++ b/doc/en/example/fixtures/test_fixtures_request_different_scope.svg @@ -0,0 +1,115 @@ + + + + + + + + + + test_fixtures_request_different_scope.py + + + + + + + + + + + + inner + + test_order + + + + + + TestOne + + + + + + + + 1 + + + + + + + 2 + + + + + + + + + + + inner + + test_order + + + + + + TestTwo + + + + + + + + 1 + + + 2 + + + outer + + order + diff --git a/doc/en/fixture.rst b/doc/en/fixture.rst index 963fc32e6b0..c74984563ab 100644 --- a/doc/en/fixture.rst +++ b/doc/en/fixture.rst @@ -12,6 +12,8 @@ pytest fixtures: explicit, modular, scalable .. _`xUnit`: https://en.wikipedia.org/wiki/XUnit .. _`Software test fixtures`: https://en.wikipedia.org/wiki/Test_fixture#Software .. _`Dependency injection`: https://en.wikipedia.org/wiki/Dependency_injection +.. _`Transaction`: https://en.wikipedia.org/wiki/Transaction_processing +.. _`linearizable`: https://en.wikipedia.org/wiki/Linearizability `Software test fixtures`_ initialize test functions. They provide a fixed baseline so that tests execute reliably and produce consistent, @@ -35,6 +37,10 @@ style of setup/teardown functions: to configuration and component options, or to re-use fixtures across function, class, module or whole test session scopes. +* teardown logic can be easily, and safely managed, no matter how many fixtures + are used, without the need to carefully handle errors by hand or micromanage + the order that cleanup steps are added. + In addition, pytest continues to support :ref:`xunitsetup`. You can mix both styles, moving incrementally from classic to new style, as you prefer. You can also start out from existing :ref:`unittest.TestCase @@ -115,32 +121,529 @@ for reference: .. _`@pytest.fixture`: .. _`pytest.fixture`: -Fixtures as Function arguments ------------------------------------------ +What fixtures are +----------------- + +Before we dive into what fixtures are, let's first look at what a test is. + +In the simplest terms, a test is meant to look at the result of a particular +behavior, and make sure that result aligns with what you would expect. +Behavior is not something that can be empirically measured, which is why writing +tests can be challenging. + +"Behavior" is the way in which some system **acts in response** to a particular +situation and/or stimuli. But exactly *how* or *why* something is done is not +quite as important as *what* was done. + +You can think of a test as being broken down into four steps: + +1. **Arrange** +2. **Act** +3. **Assert** +4. **Cleanup** + +**Arrange** is where we prepare everything for our test. This means pretty +much everything except for the "**act**". It's lining up the dominoes so that +the **act** can do its thing in one, state-changing step. This can mean +preparing objects, starting/killing services, entering records into a database, +or even things like defining a URL to query, generating some credentials for a +user that doesn't exist yet, or just waiting for some process to finish. + +**Act** is the singular, state-changing action that kicks off the **behavior** +we want to test. This behavior is what carries out the changing of the state of +the system under test (SUT), and it's the resulting changed state that we can +look at to make a judgement about the behavior. This typically takes the form of +a function/method call. + +**Assert** is where we look at that resulting state and check if it looks how +we'd expect after the dust has settled. It's where we gather evidence to say the +behavior does or does not aligns with what we expect. The ``assert`` in our test +is where we take that measurement/observation and apply our judgement to it. If +something should be green, we'd say ``assert thing == "green"``. + +**Cleanup** is where the test picks up after itself, so other tests aren't being +accidentally influenced by it. + +At it's core, the test is ultimately the **act** and **assert** steps, with the +**arrange** step only providing the context. **Behavior** exists between **act** +and **assert**. + +Back to fixtures +^^^^^^^^^^^^^^^^ + +"Fixtures", in the literal sense, are each of the **arrange** steps and data. They're +everything that test needs to do its thing. + +At a basic level, test functions request fixtures by declaring them as +arguments, as in the ``test_ehlo(smtp_connection):`` in the previous example. -Test functions can receive fixture objects by naming them as an input -argument. For each argument name, a fixture function with that name provides -the fixture object. Fixture functions are registered by marking them with -:py:func:`@pytest.fixture `. Let's look at a simple -self-contained test module containing a fixture and a test function -using it: +In pytest, "fixtures" are functions you define that serve this purpose. But they +don't have to be limited to just the **arrange** steps. They can provide the +**act** step, as well, and this can be a powerful technique for designing more +complex tests, especially given how pytest's fixture system works. But we'll get +into that further down. + +We can tell pytest that a particular function is a fixture by decorating it with +:py:func:`@pytest.fixture `. Here's a simple example of +what a fixture in pytest might look like: .. code-block:: python - # content of ./test_smtpsimple.py import pytest + class Fruit: + def __init__(self, name): + self.name = name + + def __eq__(self, other): + return self.name == other.name + + @pytest.fixture - def smtp_connection(): - import smtplib + def my_fruit(): + return Fruit("apple") + + + @pytest.fixture + def fruit_basket(my_fruit): + return [Fruit("banana"), my_fruit] + + + def test_my_fruit_in_basket(my_fruit, fruit_basket): + assert my_fruit in fruit_basket + + + +Tests don't have to be limited to a single fixture, either. They can depend on +as many fixtures as you want, and fixtures can use other fixtures, as well. This +is where pytest's fixture system really shines. + +Don't be afraid to break things up if it makes things cleaner. + +"Requesting" fixtures +--------------------- + +So fixtures are how we *prepare* for a test, but how do we tell pytest what +tests and fixtures need which fixtures? + +At a basic level, test functions request fixtures by declaring them as +arguments, as in the ``test_my_fruit_in_basket(my_fruit, fruit_basket):`` in the +previous example. + +At a basic level, pytest depends on a test to tell it what fixtures it needs, so +we have to build that information into the test itself. We have to make the test +"**request**" the fixtures it depends on, and to do this, we have to +list those fixtures as parameters in the test function's "signature" (which is +the ``def test_something(blah, stuff, more):`` line). + +When pytest goes to run a test, it looks at the parameters in that test +function's signature, and then searches for fixtures that have the same names as +those parameters. Once pytest finds them, it runs those fixtures, captures what +they returned (if anything), and passes those objects into the test function as +arguments. + +Quick example +^^^^^^^^^^^^^ + +.. code-block:: python + + import pytest + + + class Fruit: + def __init__(self, name): + self.name = name + self.cubed = False + + def cube(self): + self.cubed = True + + + class FruitSalad: + def __init__(self, *fruit_bowl): + self.fruit = fruit_bowl + self._cube_fruit() + + def _cube_fruit(self): + for fruit in self.fruit: + fruit.cube() + + + # Arrange + @pytest.fixture + def fruit_bowl(): + return [Fruit("apple"), Fruit("banana")] + + + def test_fruit_salad(fruit_bowl): + # Act + fruit_salad = FruitSalad(*fruit_bowl) + + # Assert + assert all(fruit.cubed for fruit in fruit_salad.fruit) + +In this example, ``test_fruit_salad`` "**requests**" ``fruit_bowl`` (i.e. +``def test_fruit_salad(fruit_bowl):``), and when pytest sees this, it will +execute the ``fruit_bowl`` fixture function and pass the object it returns into +``test_fruit_salad`` as the ``fruit_bowl`` argument. + +Here's roughly +what's happening if we were to do it by hand: + +.. code-block:: python + + def fruit_bowl(): + return [Fruit("apple"), Fruit("banana")] + + + def test_fruit_salad(fruit_bowl): + # Act + fruit_salad = FruitSalad(*fruit_bowl) + + # Assert + assert all(fruit.cubed for fruit in fruit_salad.fruit) + + + # Arrange + bowl = fruit_bowl() + test_fruit_salad(fruit_bowl=bowl) + +Fixtures can **request** other fixtures +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +One of pytest's greatest strengths is its extremely flexible fixture system. It +allows us to boil down complex requirements for tests into more simple and +organized functions, where we only need to have each one describe the things +they are dependent on. We'll get more into this further down, but for now, +here's a quick example to demonstrate how fixtures can use other fixtures: + +.. code-block:: python + + # contents of test_append.py + import pytest + + + # Arrange + @pytest.fixture + def first_entry(): + return "a" + + + # Arrange + @pytest.fixture + def order(first_entry): + return [first_entry] + + + def test_string(order): + # Act + order.append("b") + + # Assert + assert order == ["a", "b"] + + +Notice that this is the same example from above, but very little changed. The +fixtures in pytest **request** fixtures just like tests. All the same +**requesting** rules apply to fixtures that do for tests. Here's how this +example would work if we did it by hand: + +.. code-block:: python + + def first_entry(): + return "a" + + + def order(first_entry): + return [first_entry] + + + def test_string(order): + # Act + order.append("b") + + # Assert + assert order == ["a", "b"] + + + entry = first_entry() + the_list = order(first_entry=entry) + test_string(order=the_list) + +Fixtures are reusable +^^^^^^^^^^^^^^^^^^^^^ + +One of the things that makes pytest's fixture system so powerful, is that it +gives us the abilty to define a generic setup step that can reused over and +over, just like a normal function would be used. Two different tests can request +the same fixture and have pytest give each test their own result from that +fixture. + +This is extremely useful for making sure tests aren't affected by each other. We +can use this system to make sure each test gets its own fresh batch of data and +is starting from a clean state so it can provide consistent, repeatable results. + +Here's an example of how this can come in handy: + +.. code-block:: python + + # contents of test_append.py + import pytest + + + # Arrange + @pytest.fixture + def first_entry(): + return "a" + + + # Arrange + @pytest.fixture + def order(first_entry): + return [first_entry] + + + def test_string(order): + # Act + order.append("b") + + # Assert + assert order == ["a", "b"] + + + def test_int(order): + # Act + order.append(2) + + # Assert + assert order == ["a", 2] + + +Each test here is being given its own copy of that ``list`` object, +which means the ``order`` fixture is getting executed twice (the same +is true for the ``first_entry`` fixture). If we were to do this by hand as +well, it would look something like this: + +.. code-block:: python + + def first_entry(): + return "a" + + + def order(first_entry): + return [first_entry] + + def test_string(order): + # Act + order.append("b") + + # Assert + assert order == ["a", "b"] + + + def test_int(order): + # Act + order.append(2) + + # Assert + assert order == ["a", 2] + + + entry = first_entry() + the_list = order(first_entry=entry) + test_string(order=the_list) + + entry = first_entry() + the_list = order(first_entry=entry) + test_int(order=the_list) + +A test/fixture can **request** more than one fixture at a time +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Tests and fixtures aren't limited to **requesting** a single fixture at a time. +They can request as many as they like. Here's another quick example to +demonstrate: + +.. code-block:: python + + # contents of test_append.py + import pytest + + + # Arrange + @pytest.fixture + def first_entry(): + return "a" + + + # Arrange + @pytest.fixture + def second_entry(): + return 2 + + + # Arrange + @pytest.fixture + def order(first_entry, second_entry): + return [first_entry, second_entry] + + + # Arrange + @pytest.fixture + def expected_list(): + return ["a", 2, 3.0] + + + def test_string(order, expected_list): + # Act + order.append(3.0) + + # Assert + assert order == expected_list + +Fixtures can be **requested** more than once per test (return values are cached) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Fixtures can also be **requested** more than once during the same test, and +pytest won't execute them again for that test. This means we can **request** +fixtures in multiple fixtures that are dependent on them (and even again in the +test itself) without those fixtures being executed more than once. + +.. code-block:: python + + # contents of test_append.py + import pytest + + + # Arrange + @pytest.fixture + def first_entry(): + return "a" + + + # Arrange + @pytest.fixture + def order(): + return [] + + + # Act + @pytest.fixture + def append_first(order, first_entry): + return order.append(first_entry) + + + def test_string_only(append_first, order, first_entry): + # Assert + assert order == [first_entry] + +If a **requested** fixture was executed once for every time it was **requested** +during a test, then this test would fail because both ``append_first`` and +``test_string_only`` would see ``order`` as an empty list (i.e. ``[]``), but +since the return value of ``order`` was cached (along with any side effects +executing it may have had) after the first time it was called, both the test and +``append_first`` were referencing the same object, and the test saw the effect +``append_first`` had on that object. + +.. _`autouse`: +.. _`autouse fixtures`: + +Autouse fixtures (fixtures you don't have to request) +----------------------------------------------------- + +Sometimes you may want to have a fixture (or even several) that you know all +your tests will depend on. "Autouse" fixtures are a convenient way to make all +tests automatically **request** them. This can cut out a +lot of redundant **requests**, and can even provide more advanced fixture usage +(more on that further down). + +We can make a fixture an autouse fixture by passing in ``autouse=True`` to the +fixture's decorator. Here's a simple example for how they can be used: + +.. code-block:: python + + # contents of test_append.py + import pytest + + + @pytest.fixture + def first_entry(): + return "a" + + + @pytest.fixture + def order(first_entry): + return [] + + + @pytest.fixture(autouse=True) + def append_first(order, first_entry): + return order.append(first_entry) + + + def test_string_only(order, first_entry): + assert order == [first_entry] + + + def test_string_and_int(order, first_entry): + order.append(2) + assert order == [first_entry, 2] + +In this example, the ``append_first`` fixture is an autouse fixture. Because it +happens automatically, both tests are affected by it, even though neither test +**requested** it. That doesn't mean they *can't* be **requested** though; just +that it isn't *necessary*. + +.. _smtpshared: + +Scope: sharing fixtures across classes, modules, packages or session +-------------------------------------------------------------------- + +.. regendoc:wipe + +Fixtures requiring network access depend on connectivity and are +usually time-expensive to create. Extending the previous example, we +can add a ``scope="module"`` parameter to the +:py:func:`@pytest.fixture ` invocation +to cause a ``smtp_connection`` fixture function, responsible to create a connection to a preexisting SMTP server, to only be invoked +once per test *module* (the default is to invoke once per test *function*). +Multiple test functions in a test module will thus +each receive the same ``smtp_connection`` fixture instance, thus saving time. +Possible values for ``scope`` are: ``function``, ``class``, ``module``, ``package`` or ``session``. + +The next example puts the fixture function into a separate ``conftest.py`` file +so that tests from multiple test modules in the directory can +access the fixture function: + +.. code-block:: python + + # content of conftest.py + import pytest + import smtplib + + + @pytest.fixture(scope="module") + def smtp_connection(): return smtplib.SMTP("smtp.gmail.com", 587, timeout=5) +.. code-block:: python + + # content of test_module.py + + def test_ehlo(smtp_connection): response, msg = smtp_connection.ehlo() assert response == 250 + assert b"smtp.gmail.com" in msg + assert 0 # for demo purposes + + + def test_noop(smtp_connection): + response, msg = smtp_connection.noop() + assert response == 250 assert 0 # for demo purposes Here, the ``test_ehlo`` needs the ``smtp_connection`` fixture value. pytest @@ -149,442 +652,967 @@ marked ``smtp_connection`` fixture function. Running the test looks like this: .. code-block:: pytest - $ pytest test_smtpsimple.py + $ pytest test_module.py =========================== test session starts ============================ platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR - collected 1 item + collected 2 items + + test_module.py FF [100%] + + ================================= FAILURES ================================= + ________________________________ test_ehlo _________________________________ + + smtp_connection = + + def test_ehlo(smtp_connection): + response, msg = smtp_connection.ehlo() + assert response == 250 + assert b"smtp.gmail.com" in msg + > assert 0 # for demo purposes + E assert 0 + + test_module.py:7: AssertionError + ________________________________ test_noop _________________________________ + + smtp_connection = + + def test_noop(smtp_connection): + response, msg = smtp_connection.noop() + assert response == 250 + > assert 0 # for demo purposes + E assert 0 + + test_module.py:13: AssertionError + ========================= short test summary info ========================== + FAILED test_module.py::test_ehlo - assert 0 + FAILED test_module.py::test_noop - assert 0 + ============================ 2 failed in 0.12s ============================= + +You see the two ``assert 0`` failing and more importantly you can also see +that the **exactly same** ``smtp_connection`` object was passed into the +two test functions because pytest shows the incoming argument values in the +traceback. As a result, the two test functions using ``smtp_connection`` run +as quick as a single one because they reuse the same instance. + +If you decide that you rather want to have a session-scoped ``smtp_connection`` +instance, you can simply declare it: + +.. code-block:: python + + @pytest.fixture(scope="session") + def smtp_connection(): + # the returned fixture value will be shared for + # all tests requesting it + ... + + +Fixture scopes +^^^^^^^^^^^^^^ + +Fixtures are created when first requested by a test, and are destroyed based on their ``scope``: + +* ``function``: the default scope, the fixture is destroyed at the end of the test. +* ``class``: the fixture is destroyed during teardown of the last test in the class. +* ``module``: the fixture is destroyed during teardown of the last test in the module. +* ``package``: the fixture is destroyed during teardown of the last test in the package. +* ``session``: the fixture is destroyed at the end of the test session. + +.. note:: + + Pytest only caches one instance of a fixture at a time, which + means that when using a parametrized fixture, pytest may invoke a fixture more than once in + the given scope. + +.. _dynamic scope: + +Dynamic scope +^^^^^^^^^^^^^ + +.. versionadded:: 5.2 + +In some cases, you might want to change the scope of the fixture without changing the code. +To do that, pass a callable to ``scope``. The callable must return a string with a valid scope +and will be executed only once - during the fixture definition. It will be called with two +keyword arguments - ``fixture_name`` as a string and ``config`` with a configuration object. + +This can be especially useful when dealing with fixtures that need time for setup, like spawning +a docker container. You can use the command-line argument to control the scope of the spawned +containers for different environments. See the example below. + +.. code-block:: python + + def determine_scope(fixture_name, config): + if config.getoption("--keep-containers", None): + return "session" + return "function" + + + @pytest.fixture(scope=determine_scope) + def docker_container(): + yield spawn_container() + +Fixture errors +-------------- + +pytest does its best to put all the fixtures for a given test in a linear order +so that it can see which fixture happens first, second, third, and so on. If an +earlier fixture has a problem, though, and raises an exception, pytest will stop +executing fixtures for that test and mark the test as having an error. + +When a test is marked as having an error, it doesn't mean the test failed, +though. It just means the test couldn't even be attempted because one of the +things it depends on had a problem. + +This is one reason why it's a good idea to cut out as many unnecessary +dependencies as possible for a given test. That way a problem in something +unrelated isn't causing us to have an incomplete picture of what may or may not +have issues. + +Here's a quick example to help explain: + +.. code-block:: python + + import pytest + + + @pytest.fixture + def order(): + return [] + + + @pytest.fixture + def append_first(order): + order.append(1) + + + @pytest.fixture + def append_second(order, append_first): + order.extend([2]) + + + @pytest.fixture(autouse=True) + def append_third(order, append_second): + order += [3] + + + def test_order(order): + assert order == [1, 2, 3] + + +If, for whatever reason, ``order.append(1)`` had a bug and it raises an exception, +we wouldn't be able to know if ``order.extend([2])`` or ``order += [3]`` would +also have problems. After ``append_first`` throws an exception, pytest won't run +any more fixtures for ``test_order``, and it won't even try to run +``test_order`` itself. The only things that would've run would be ``order`` and +``append_first``. + + + + +.. _`finalization`: - test_smtpsimple.py F [100%] +Teardown/Cleanup (AKA Fixture finalization) +------------------------------------------- + +When we run our tests, we'll want to make sure they clean up after themselves so +they don't mess with any other tests (and also so that we don't leave behind a +mountain of test data to bloat the system). Fixtures in pytest offer a very +useful teardown system, which allows us to define the specific steps necessary +for each fixture to clean up after itself. + +This system can be leveraged in two ways. + +.. _`yield fixtures`: + +1. ``yield`` fixtures (recommended) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +"Yield" fixtures ``yield`` instead of ``return``. With these +fixtures, we can run some code and pass an object back to the requesting +fixture/test, just like with the other fixtures. The only differences are: + +1. ``return`` is swapped out for ``yield``. +2. Any teardown code for that fixture is placed *after* the ``yield``. + +Once pytest figures out a linear order for the fixtures, it will run each one up +until it returns or yields, and then move on to the next fixture in the list to +do the same thing. + +Once the test is finished, pytest will go back down the list of fixtures, but in +the *reverse order*, taking each one that yielded, and running the code inside +it that was *after* the ``yield`` statement. + +As a simple example, let's say we want to test sending email from one user to +another. We'll have to first make each user, then send the email from one user +to the other, and finally assert that the other user received that message in +their inbox. If we want to clean up after the test runs, we'll likely have to +make sure the other user's mailbox is emptied before deleting that user, +otherwise the system may complain. + +Here's what that might look like: + +.. code-block:: python + + import pytest + + from emaillib import Email, MailAdminClient + + + @pytest.fixture + def mail_admin(): + return MailAdminClient() + + + @pytest.fixture + def sending_user(mail_admin): + user = mail_admin.create_user() + yield user + admin_client.delete_user(user) + + + @pytest.fixture + def receiving_user(mail_admin): + user = mail_admin.create_user() + yield user + admin_client.delete_user(user) + + + def test_email_received(receiving_user, email): + email = Email(subject="Hey!", body="How's it going?") + sending_user.send_email(_email, receiving_user) + assert email in receiving_user.inbox + +Because ``receiving_user`` is the last fixture to run during setup, it's the first to run +during teardown. + +There is a risk that even having the order right on the teardown side of things +doesn't guarantee a safe cleanup. That's covered in a bit more detail in +:ref:`safe teardowns`. + +Handling errors for yield fixture +""""""""""""""""""""""""""""""""" + +If a yield fixture raises an exception before yielding, pytest won't try to run +the teardown code after that yield fixture's ``yield`` statement. But, for every +fixture that has already run successfully for that test, pytest will still +attempt to tear them down as it normally would. + +2. Adding finalizers directly +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +While yield fixtures are considered to be the cleaner and more straighforward +option, there is another choice, and that is to add "finalizer" functions +directly to the test's `request-context`_ object. It brings a similar result as +yield fixtures, but requires a bit more verbosity. + +In order to use this approach, we have to request the `request-context`_ object +(just like we would request another fixture) in the fixture we need to add +teardown code for, and then pass a callable, containing that teardown code, to +its ``addfinalizer`` method. + +We have to be careful though, because pytest will run that finalizer once it's +been added, even if that fixture raises an exception after adding the finalizer. +So to make sure we don't run the finalizer code when we wouldn't need to, we +would only add the finalizer once the fixture would have done something that +we'd need to teardown. + +Here's how the previous example would look using the ``addfinalizer`` method: + +.. code-block:: python + + import pytest + + from emaillib import Email, MailAdminClient + + + @pytest.fixture + def mail_admin(): + return MailAdminClient() + + + @pytest.fixture + def sending_user(mail_admin): + user = mail_admin.create_user() + yield user + admin_client.delete_user(user) + + + @pytest.fixture + def receiving_user(mail_admin, request): + user = mail_admin.create_user() + + def delete_user(): + admin_client.delete_user(user) + + request.addfinalizer(delete_user) + return user + + + @pytest.fixture + def email(sending_user, receiving_user, request): + _email = Email(subject="Hey!", body="How's it going?") + sending_user.send_email(_email, receiving_user) + + def empty_mailbox(): + receiving_user.delete_email(_email) + + request.addfinalizer(empty_mailbox) + return _email + + + def test_email_received(receiving_user, email): + assert email in receiving_user.inbox + + +It's a bit longer than yield fixtures and a bit more complex, but it +does offer some nuances for when you're in a pinch. + +.. _`safe teardowns`: + +Safe teardowns +-------------- + +The fixture system of pytest is *very* powerful, but it's still being run by a +computer, so it isn't able to figure out how to safely teardown everything we +throw at it. If we aren't careful, an error in the wrong spot might leave stuff +from our tests behind, and that can cause further issues pretty quickly. + +For example, consider the following tests (based off of the mail example from +above): + +.. code-block:: python + + import pytest + + from emaillib import Email, MailAdminClient + + + @pytest.fixture + def setup(): + mail_admin = MailAdminClient() + sending_user = mail_admin.create_user() + receiving_user = mail_admin.create_user() + email = Email(subject="Hey!", body="How's it going?") + sending_user.send_emai(email, receiving_user) + yield receiving_user, email + receiving_user.delete_email(email) + admin_client.delete_user(sending_user) + admin_client.delete_user(receiving_user) + + + def test_email_received(setup): + receiving_user, email = setup + assert email in receiving_user.inbox + +This version is a lot more compact, but it's also harder to read, doesn't have a +very descriptive fixture name, and none of the fixtures can be reused easily. + +There's also a more serious issue, which is that if any of those steps in the +setup raise an exception, none of the teardown code will run. + +One option might be to go with the ``addfinalizer`` method instead of yield +fixtures, but that might get pretty complex and difficult to maintain (and it +wouldn't be compact anymore). + +.. _`safe fixture structure`: + +Safe fixture structure +^^^^^^^^^^^^^^^^^^^^^^ + +The safest and simplest fixture structure requires limiting fixtures to only +making one state-changing action each, and then bundling them together with +their teardown code, as :ref:`the email examples above ` showed. + +The chance that a state-changing operation can fail but still modify state is +neglibible, as most of these operations tend to be `transaction`_-based (at +least at the level of testing where state could be left behind). So if we make +sure that any successful state-changing action gets torn down by moving it to a +separate fixture function and separating it from other, potentially failing +state-changing actions, then our tests will stand the best chance at leaving the +test environment the way they found it. + +For an example, let's say we have a website with a login page, and we have +access to an admin API where we can generate users. For our test, we want to: + +1. Create a user through that admin API +2. Launch a browser using Selenium +3. Go to the login page of our site +4. Log in as the user we created +5. Assert that their name is in the header of the landing page + +We wouldn't want to leave that user in the system, nor would we want to leave +that browser session running, so we'll want to make sure the fixtures that +create those things clean up after themselves. + +Here's what that might look like: + +.. note:: + + For this example, certain fixtures (i.e. ``base_url`` and + ``admin_credentials``) are implied to exist elsewhere. So for now, let's + assume they exist, and we're just not looking at them. + +.. code-block:: python + + from uuid import uuid4 + from urllib.parse import urljoin + + from selenium.webdriver import Chrome + import pytest + + from src.utils.pages import LoginPage, LandingPage + from src.utils import AdminApiClient + from src.utils.data_types import User + + + @pytest.fixture + def admin_client(base_url, admin_credentials): + return AdminApiClient(base_url, **admin_credentials) + + + @pytest.fixture + def user(admin_client): + _user = User(name="Susan", username=f"testuser-{uuid4()}", password="P4$$word") + admin_client.create_user(_user) + yield _user + admin_client.delete_user(_user) + + + @pytest.fixture + def driver(): + _driver = Chrome() + yield _driver + _driver.quit() + + + @pytest.fixture + def login(driver, base_url, user): + driver.get(urljoin(base_url, "/login")) + page = LoginPage(driver) + page.login(user) + + + @pytest.fixture + def landing_page(driver, login): + return LandingPage(driver) + + + def test_name_on_landing_page_after_login(landing_page, user): + assert landing_page.header == f"Welcome, {user.name}!" + +The way the dependencies are laid out means it's unclear if the ``user`` fixture +would execute before the ``driver`` fixture. But that's ok, because those are +atomic operations, and so it doesn't matter which one runs first because the +sequence of events for the test is still `linearizable`_. But what *does* matter +is that, no matter which one runs first, if the one raises an exception while +the other would not have, neither will have left anything behind. If ``driver`` +executes before ``user``, and ``user`` raises an exception, the driver will +still quit, and the user was never made. And if ``driver`` was the one to raise +the exception, then the driver would never have been started and the user would +never have been made. + +.. note: + + While the ``user`` fixture doesn't *actually* need to happen before the + ``driver`` fixture, if we made ``driver`` request ``user``, it might save + some time in the event that making the user raises an exception, since it + won't bother trying to start the driver, which is a fairly expensive + operation. + +.. _`conftest.py`: +.. _`conftest`: - ================================= FAILURES ================================= - ________________________________ test_ehlo _________________________________ +Fixture availabiility +--------------------- - smtp_connection = +Fixture availability is determined from the perspective of the test. A fixture +is only available for tests to request if they are in the scope that fixture is +defined in. If a fixture is defined inside a class, it can only be requested by +tests inside that class. But if a fixture is defined inside the global scope of +the module, than every test in that module, even if it's defined inside a class, +can request it. - def test_ehlo(smtp_connection): - response, msg = smtp_connection.ehlo() - assert response == 250 - > assert 0 # for demo purposes - E assert 0 +Similarly, a test can also only be affected by an autouse fixture if that test +is in the same scope that autouse fixture is defined in (see +:ref:`autouse order`). - test_smtpsimple.py:14: AssertionError - ========================= short test summary info ========================== - FAILED test_smtpsimple.py::test_ehlo - assert 0 - ============================ 1 failed in 0.12s ============================= +A fixture can also request any other fixture, no matter where it's defined, so +long as the test requesting them can see all fixtures involved. -In the failure traceback we see that the test function was called with a -``smtp_connection`` argument, the ``smtplib.SMTP()`` instance created by the fixture -function. The test function fails on our deliberate ``assert 0``. Here is -the exact protocol used by ``pytest`` to call the test function this way: +For example, here's a test file with a fixture (``outer``) that requests a +fixture (``inner``) from a scope it wasn't defined in: -1. pytest :ref:`finds ` the test ``test_ehlo`` because - of the ``test_`` prefix. The test function needs a function argument - named ``smtp_connection``. A matching fixture function is discovered by - looking for a fixture-marked function named ``smtp_connection``. +.. literalinclude:: example/fixtures/test_fixtures_request_different_scope.py -2. ``smtp_connection()`` is called to create an instance. +From the tests' perspectives, they have no problem seeing each of the fixtures +they're dependent on: -3. ``test_ehlo()`` is called and fails in the last - line of the test function. +.. image:: example/fixtures/test_fixtures_request_different_scope.svg + :align: center -Note that if you misspell a function argument or want -to use one that isn't available, you'll see an error -with a list of available function arguments. +So when they run, ``outer`` will have no problem finding ``inner``, because +pytest searched from the tests' perspectives. .. note:: + The scope a fixture is defined in has no bearing on the order it will be + instantiated in: the order is mandated by the logic described + :ref:`here `. - You can always issue: +``conftest.py``: sharing fixtures across multiple files +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - .. code-block:: bash +The ``conftest.py`` file serves as a means of providing fixtures for an entire +directory. Fixtures defined in a ``conftest.py`` can be used by any test +in that package without needing to import them (pytest will automatically +discover them). - pytest --fixtures test_simplefactory.py +You can have multiple nested directories/packages containing your tests, and +each directory can have its own ``conftest.py`` with its own fixtures, adding on +to the ones provided by the ``conftest.py`` files in parent directories. - to see available fixtures (fixtures with leading ``_`` are only shown if you add the ``-v`` option). +For example, given a test file structure like this: -Fixtures: a prime example of dependency injection ---------------------------------------------------- +:: -Fixtures allow test functions to easily receive and work -against specific pre-initialized application objects without having -to care about import/setup/cleanup details. -It's a prime example of `dependency injection`_ where fixture -functions take the role of the *injector* and test functions are the -*consumers* of fixture objects. + tests/ + __init__.py -.. _`conftest.py`: -.. _`conftest`: + conftest.py + # content of tests/conftest.py + import pytest -``conftest.py``: sharing fixture functions ------------------------------------------- + @pytest.fixture + def order(): + return [] -If during implementing your tests you realize that you -want to use a fixture function from multiple test files you can move it -to a ``conftest.py`` file. -You don't need to import the fixture you want to use in a test, it -automatically gets discovered by pytest. The discovery of -fixture functions starts at test classes, then test modules, then -``conftest.py`` files and finally builtin and third party plugins. + @pytest.fixture + def top(order, innermost): + order.append("top") -You can also use the ``conftest.py`` file to implement -:ref:`local per-directory plugins `. + test_top.py + # content of tests/test_top.py + import pytest -Sharing test data ------------------ + @pytest.fixture + def innermost(order): + order.append("innermost top") -If you want to make test data from files available to your tests, a good way -to do this is by loading these data in a fixture for use by your tests. -This makes use of the automatic caching mechanisms of pytest. + def test_order(order, top): + assert order == ["innermost top", "top"] -Another good approach is by adding the data files in the ``tests`` folder. -There are also community plugins available to help managing this aspect of -testing, e.g. `pytest-datadir `__ -and `pytest-datafiles `__. + subpackage/ + __init__.py -.. _smtpshared: + conftest.py + # content of tests/subpackage/conftest.py + import pytest -Scope: sharing fixtures across classes, modules, packages or session --------------------------------------------------------------------- + @pytest.fixture + def mid(order): + order.append("mid subpackage") -.. regendoc:wipe + test_subpackage.py + # content of tests/subpackage/test_subpackage.py + import pytest -Fixtures requiring network access depend on connectivity and are -usually time-expensive to create. Extending the previous example, we -can add a ``scope="module"`` parameter to the -:py:func:`@pytest.fixture ` invocation -to cause the decorated ``smtp_connection`` fixture function to only be invoked -once per test *module* (the default is to invoke once per test *function*). -Multiple test functions in a test module will thus -each receive the same ``smtp_connection`` fixture instance, thus saving time. -Possible values for ``scope`` are: ``function``, ``class``, ``module``, ``package`` or ``session``. + @pytest.fixture + def innermost(order, mid): + order.append("innermost subpackage") -The next example puts the fixture function into a separate ``conftest.py`` file -so that tests from multiple test modules in the directory can -access the fixture function: + def test_order(order, top): + assert order == ["mid subpackage", "innermost subpackage", "top"] -.. code-block:: python +The boundaries of the scopes can be visualized like this: - # content of conftest.py - import pytest - import smtplib +.. image:: example/fixtures/fixture_availability.svg + :align: center +The directories become their own sort of scope where fixtures that are defined +in a ``conftest.py`` file in that directory become available for that whole +scope. - @pytest.fixture(scope="module") - def smtp_connection(): - return smtplib.SMTP("smtp.gmail.com", 587, timeout=5) +Tests are allowed to search upward (stepping outside a circle) for fixtures, but +can never go down (stepping inside a circle) to continue their search. So +``tests/subpackage/test_subpackage.py::test_order`` would be able to find the +``innermost`` fixture defined in ``tests/subpackage/test_subpackage.py``, but +the one defined in ``tests/test_top.py`` would be unavailable to it because it +would have to step down a level (step inside a circle) to find it. -The name of the fixture again is ``smtp_connection`` and you can access its -result by listing the name ``smtp_connection`` as an input parameter in any -test or fixture function (in or below the directory where ``conftest.py`` is -located): +The first fixture the test finds is the one that will be used, so +:ref:`fixtures can be overriden ` if you need to change or +extend what one does for a particular scope. -.. code-block:: python +You can also use the ``conftest.py`` file to implement +:ref:`local per-directory plugins `. - # content of test_module.py +Fixtures from third-party plugins +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Fixtures don't have to be defined in this structure to be available for tests, +though. They can also be provided by third-party plugins that are installed, and +this is how many pytest plugins operate. As long as those plugins are installed, +the fixtures they provide can be requested from anywhere in your test suite. - def test_ehlo(smtp_connection): - response, msg = smtp_connection.ehlo() - assert response == 250 - assert b"smtp.gmail.com" in msg - assert 0 # for demo purposes +Because they're provided from outside the structure of your test suite, +third-party plugins don't really provide a scope like `conftest.py` files and +the directories in your test suite do. As a result, pytest will search for +fixtures stepping out through scopes as explained previously, only reaching +fixtures defined in plugins *last*. +For example, given the following file structure: - def test_noop(smtp_connection): - response, msg = smtp_connection.noop() - assert response == 250 - assert 0 # for demo purposes +:: -We deliberately insert failing ``assert 0`` statements in order to -inspect what is going on and can now run the tests: + tests/ + __init__.py -.. code-block:: pytest + conftest.py + # content of tests/conftest.py + import pytest - $ pytest test_module.py - =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y - cachedir: $PYTHON_PREFIX/.pytest_cache - rootdir: $REGENDOC_TMPDIR - collected 2 items + @pytest.fixture + def order(): + return [] - test_module.py FF [100%] + subpackage/ + __init__.py - ================================= FAILURES ================================= - ________________________________ test_ehlo _________________________________ + conftest.py + # content of tests/subpackage/conftest.py + import pytest - smtp_connection = + @pytest.fixture(autouse=True) + def mid(order, b_fix): + order.append("mid subpackage") - def test_ehlo(smtp_connection): - response, msg = smtp_connection.ehlo() - assert response == 250 - assert b"smtp.gmail.com" in msg - > assert 0 # for demo purposes - E assert 0 + test_subpackage.py + # content of tests/subpackage/test_subpackage.py + import pytest - test_module.py:7: AssertionError - ________________________________ test_noop _________________________________ + @pytest.fixture + def inner(order, mid, a_fix): + order.append("inner subpackage") - smtp_connection = + def test_order(order, inner): + assert order == ["b_fix", "mid subpackage", "a_fix", "inner subpackage"] - def test_noop(smtp_connection): - response, msg = smtp_connection.noop() - assert response == 250 - > assert 0 # for demo purposes - E assert 0 +If ``plugin_a`` is installed and provides the fixture ``a_fix``, and +``plugin_b`` is installed and provides the fixture ``b_fix``, then this is what +the test's search for fixtures would look like: - test_module.py:13: AssertionError - ========================= short test summary info ========================== - FAILED test_module.py::test_ehlo - assert 0 - FAILED test_module.py::test_noop - assert 0 - ============================ 2 failed in 0.12s ============================= +.. image:: example/fixtures/fixture_availability_plugins.svg + :align: center -You see the two ``assert 0`` failing and more importantly you can also see -that the same (module-scoped) ``smtp_connection`` object was passed into the -two test functions because pytest shows the incoming argument values in the -traceback. As a result, the two test functions using ``smtp_connection`` run -as quick as a single one because they reuse the same instance. +pytest will only search for ``a_fix`` and ``b_fix`` in the plugins after +searching for them first in the scopes inside ``tests/``. -If you decide that you rather want to have a session-scoped ``smtp_connection`` -instance, you can simply declare it: +.. note: -.. code-block:: python + pytest can tell you what fixtures are available for a given test if you call + ``pytests`` along with the test's name (or the scope it's in), and provide + the ``--fixtures`` flag, e.g. ``pytest --fixtures test_something.py`` + (fixtures with names that start with ``_`` will only be shown if you also + provide the ``-v`` flag). - @pytest.fixture(scope="session") - def smtp_connection(): - # the returned fixture value will be shared for - # all tests needing it - ... +Sharing test data +----------------- +If you want to make test data from files available to your tests, a good way +to do this is by loading these data in a fixture for use by your tests. +This makes use of the automatic caching mechanisms of pytest. -Fixture scopes -^^^^^^^^^^^^^^ +Another good approach is by adding the data files in the ``tests`` folder. +There are also community plugins available to help managing this aspect of +testing, e.g. `pytest-datadir `__ +and `pytest-datafiles `__. -Fixtures are created when first requested by a test, and are destroyed based on their ``scope``: +.. _`fixture order`: -* ``function``: the default scope, the fixture is destroyed at the end of the test. -* ``class``: the fixture is destroyed during teardown of the last test in the class. -* ``module``: the fixture is destroyed during teardown of the last test in the module. -* ``package``: the fixture is destroyed during teardown of the last test in the package. -* ``session``: the fixture is destroyed at the end of the test session. +Fixture instantiation order +--------------------------- -.. note:: +When pytest wants to execute a test, once it knows what fixtures will be +executed, it has to figure out the order they'll be executed in. To do this, it +considers 3 factors: - Pytest only caches one instance of a fixture at a time, which - means that when using a parametrized fixture, pytest may invoke a fixture more than once in - the given scope. +1. scope +2. dependencies +3. autouse -.. _dynamic scope: +Names of fixtures or tests, where they're defined, the order they're defined in, +and the order fixtures are requested in have no bearing on execution order +beyond coincidence. While pytest will try to make sure coincidences like these +stay consistent from run to run, it's not something that should be depended on. +If you want to control the order, it's safest to rely on these 3 things and make +sure dependencies are clearly established. -Dynamic scope -^^^^^^^^^^^^^ +Higher-scoped fixtures are executed first +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. versionadded:: 5.2 +Within a function request for fixtures, those of higher-scopes (such as +``session``) are executed before lower-scoped fixtures (such as ``function`` or +``class``). -In some cases, you might want to change the scope of the fixture without changing the code. -To do that, pass a callable to ``scope``. The callable must return a string with a valid scope -and will be executed only once - during the fixture definition. It will be called with two -keyword arguments - ``fixture_name`` as a string and ``config`` with a configuration object. +Here's an example: -This can be especially useful when dealing with fixtures that need time for setup, like spawning -a docker container. You can use the command-line argument to control the scope of the spawned -containers for different environments. See the example below. +.. literalinclude:: example/fixtures/test_fixtures_order_scope.py -.. code-block:: python +The test will pass because the larger scoped fixtures are executing first. - def determine_scope(fixture_name, config): - if config.getoption("--keep-containers", None): - return "session" - return "function" +The order breaks down to this: +.. image:: example/fixtures/test_fixtures_order_scope.svg + :align: center - @pytest.fixture(scope=determine_scope) - def docker_container(): - yield spawn_container() +Fixtures of the same order execute based on dependencies +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +When a fixture requests another fixture, the other fixture is executed first. +So if fixture ``a`` requests fixture ``b``, fixture ``b`` will execute first, +because ``a`` depends on ``b`` and can't operate without it. Even if ``a`` +doesn't need the result of ``b``, it can still request ``b`` if it needs to make +sure it is executed after ``b``. +For example: -Order: Higher-scoped fixtures are instantiated first ----------------------------------------------------- +.. literalinclude:: example/fixtures/test_fixtures_order_dependencies.py +If we map out what depends on what, we get something that look like this: +.. image:: example/fixtures/test_fixtures_order_dependencies.svg + :align: center -Within a function request for fixtures, those of higher-scopes (such as ``session``) are instantiated before -lower-scoped fixtures (such as ``function`` or ``class``). The relative order of fixtures of same scope follows -the declared order in the test function and honours dependencies between fixtures. Autouse fixtures will be -instantiated before explicitly used fixtures. +The rules provided by each fixture (as to what fixture(s) each one has to come +after) are comprehensive enough that it can be flattened to this: -Consider the code below: +.. image:: example/fixtures/test_fixtures_order_dependencies_flat.svg + :align: center -.. literalinclude:: example/fixtures/test_fixtures_order.py +Enough information has to be provided through these requests in order for pytest +to be able to figure out a clear, linear chain of dependencies, and as a result, +an order of operations for a given test. If there's any ambiguity, and the order +of operations can be interpreted more than one way, you should assume pytest +could go with any one of those interpretations at any point. -The fixtures requested by ``test_order`` will be instantiated in the following order: +For example, if ``d`` didn't request ``c``, i.e.the graph would look like this: -1. ``s1``: is the highest-scoped fixture (``session``). -2. ``m1``: is the second highest-scoped fixture (``module``). -3. ``a1``: is a ``function``-scoped ``autouse`` fixture: it will be instantiated before other fixtures - within the same scope. -4. ``f3``: is a ``function``-scoped fixture, required by ``f1``: it needs to be instantiated at this point -5. ``f1``: is the first ``function``-scoped fixture in ``test_order`` parameter list. -6. ``f2``: is the last ``function``-scoped fixture in ``test_order`` parameter list. +.. image:: example/fixtures/test_fixtures_order_dependencies_unclear.svg + :align: center +Because nothing requested ``c`` other than ``g``, and ``g`` also requests ``f``, +it's now unclear if ``c`` should go before/after ``f``, ``e``, or ``d``. The +only rules that were set for ``c`` is that it must execute after ``b`` and +before ``g``. -.. _`finalization`: +pytest doesn't know where ``c`` should go in the case, so it should be assumed +that it could go anywhere between ``g`` and ``b``. -Fixture finalization / executing teardown code -------------------------------------------------------------- +This isn't necessarily bad, but it's something to keep in mind. If the order +they execute in could affect the behavior a test is targetting, or could +otherwise influence the result of a test, then the order should be defined +explicitely in a way that allows pytest to linearize/"flatten" that order. -pytest supports execution of fixture specific finalization code -when the fixture goes out of scope. By using a ``yield`` statement instead of ``return``, all -the code after the *yield* statement serves as the teardown code: +.. _`autouse order`: -.. code-block:: python +Autouse fixtures are executed first within their scope +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - # content of conftest.py +Autouse fixtures are assumed to apply to every test that could reference them, +so they are executed before other fixtures in that scope. Fixtures that are +requested by autouse fixtures effectively become autouse fixtures themselves for +the tests that the real autouse fixture applies to. - import smtplib - import pytest +So if fixture ``a`` is autouse and fixture ``b`` is not, but fixture ``a`` +requests fixture ``b``, then fixture ``b`` will effectively be an autouse +fixture as well, but only for the tests that ``a`` applies to. +In the last example, the graph became unclear if ``d`` didn't request ``c``. But +if ``c`` was autouse, then ``b`` and ``a`` would effectively also be autouse +because ``c`` depends on them. As a result, they would all be shifted above +non-autouse fixtures within that scope. - @pytest.fixture(scope="module") - def smtp_connection(): - smtp_connection = smtplib.SMTP("smtp.gmail.com", 587, timeout=5) - yield smtp_connection # provide the fixture value - print("teardown smtp") - smtp_connection.close() +So if the test file looked like this: -The ``print`` and ``smtp.close()`` statements will execute when the last test in -the module has finished execution, regardless of the exception status of the -tests. +.. literalinclude:: example/fixtures/test_fixtures_order_autouse.py -Let's execute it: +the graph would look like this: -.. code-block:: pytest +.. image:: example/fixtures/test_fixtures_order_autouse.svg + :align: center - $ pytest -s -q --tb=no - FFteardown smtp +Because ``c`` can now be put above ``d`` in the graph, pytest can once again +linearize the graph to this: - ========================= short test summary info ========================== - FAILED test_module.py::test_ehlo - assert 0 - FAILED test_module.py::test_noop - assert 0 - 2 failed in 0.12s +In this example, ``c`` makes ``b`` and ``a`` effectively autouse fixtures as +well. -We see that the ``smtp_connection`` instance is finalized after the two -tests finished execution. Note that if we decorated our fixture -function with ``scope='function'`` then fixture setup and cleanup would -occur around each single test. In either case the test -module itself does not need to change or know about these details -of fixture setup. +Be careful with autouse, though, as an autouse fixture will automatically +execute for every test that can reach it, even if they don't request it. For +example, consider this file: -Note that we can also seamlessly use the ``yield`` syntax with ``with`` statements: +.. literalinclude:: example/fixtures/test_fixtures_order_autouse_multiple_scopes.py -.. code-block:: python +Even though nothing in ``TestClassWithC1Request`` is requesting ``c1``, it still +is executed for the tests inside it anyway: - # content of test_yield2.py +.. image:: example/fixtures/test_fixtures_order_autouse_multiple_scopes.svg + :align: center - import smtplib - import pytest +But just because one autouse fixture requested a non-autouse fixture, that +doesn't mean the non-autouse fixture becomes an autouse fixture for all contexts +that it can apply to. It only effectively becomes an auotuse fixture for the +contexts the real autouse fixture (the one that requested the non-autouse +fixture) can apply to. +For example, take a look at this test file: - @pytest.fixture(scope="module") - def smtp_connection(): - with smtplib.SMTP("smtp.gmail.com", 587, timeout=5) as smtp_connection: - yield smtp_connection # provide the fixture value +.. literalinclude:: example/fixtures/test_fixtures_order_autouse_temp_effects.py +It would break down to something like this: -The ``smtp_connection`` connection will be closed after the test finished -execution because the ``smtp_connection`` object automatically closes when -the ``with`` statement ends. +.. image:: example/fixtures/test_fixtures_order_autouse_temp_effects.svg + :align: center -Using the contextlib.ExitStack context manager finalizers will always be called -regardless if the fixture *setup* code raises an exception. This is handy to properly -close all resources created by a fixture even if one of them fails to be created/acquired: +For ``test_req`` and ``test_no_req`` inside ``TestClassWithAutouse``, ``c3`` +effectively makes ``c2`` an autouse fixture, which is why ``c2`` and ``c3`` are +executed for both tests, despite not being requested, and why ``c2`` and ``c3`` +are executed before ``c1`` for ``test_req``. -.. code-block:: python +If this made ``c2`` an *actual* autouse fixture, then ``c2`` would also execute +for the tests inside ``TestClassWithoutAutouse``, since they can reference +``c2`` if they wanted to. But it doesn't, because from the perspective of the +``TestClassWithoutAutouse`` tests, ``c2`` isn't an autouse fixture, since they +can't see ``c3``. - # content of test_yield3.py - import contextlib +.. note: - import pytest + pytest can tell you what order the fixtures will execute in for a given test + if you call ``pytests`` along with the test's name (or the scope it's in), + and provide the ``--setup-plan`` flag, e.g. + ``pytest --setup-plan test_something.py`` (fixtures with names that start + with ``_`` will only be shown if you also provide the ``-v`` flag). - @contextlib.contextmanager - def connect(port): - ... # create connection - yield - ... # close connection +Running multiple ``assert`` statements safely +--------------------------------------------- +Sometimes you may want to run multiple asserts after doing all that setup, which +makes sense as, in more complex systems, a single action can kick off multiple +behaviors. pytest has a convenient way of handling this and it combines a bunch +of what we've gone over so far. - @pytest.fixture - def equipments(): - with contextlib.ExitStack() as stack: - yield [stack.enter_context(connect(port)) for port in ("C1", "C3", "C28")] +All that's needed is stepping up to a larger scope, then having the **act** +step defined as an autouse fixture, and finally, making sure all the fixtures +are targetting that highler level scope. -In the example above, if ``"C28"`` fails with an exception, ``"C1"`` and ``"C3"`` will still -be properly closed. +Let's pull :ref:`an example from above `, and tweak it a +bit. Let's say that in addition to checking for a welcome message in the header, +we also want to check for a sign out button, and a link to the user's profile. -Note that if an exception happens during the *setup* code (before the ``yield`` keyword), the -*teardown* code (after the ``yield``) will not be called. +Let's take a look at how we can structure that so we can run multiple asserts +without having to repeat all those steps again. -An alternative option for executing *teardown* code is to -make use of the ``addfinalizer`` method of the `request-context`_ object to register -finalization functions. +.. note:: -Here's the ``smtp_connection`` fixture changed to use ``addfinalizer`` for cleanup: + For this example, certain fixtures (i.e. ``base_url`` and + ``admin_credentials``) are implied to exist elsewhere. So for now, let's + assume they exist, and we're just not looking at them. .. code-block:: python - # content of conftest.py - import smtplib + # contents of tests/end_to_end/test_login.py + from uuid import uuid4 + from urllib.parse import urljoin + + from selenium.webdriver import Chrome import pytest + from src.utils.pages import LoginPage, LandingPage + from src.utils import AdminApiClient + from src.utils.data_types import User - @pytest.fixture(scope="module") - def smtp_connection(request): - smtp_connection = smtplib.SMTP("smtp.gmail.com", 587, timeout=5) - def fin(): - print("teardown smtp_connection") - smtp_connection.close() + @pytest.fixture(scope="class") + def admin_client(base_url, admin_credentials): + return AdminApiClient(base_url, **admin_credentials) - request.addfinalizer(fin) - return smtp_connection # provide the fixture value + @pytest.fixture(scope="class") + def user(admin_client): + _user = User(name="Susan", username=f"testuser-{uuid4()}", password="P4$$word") + admin_client.create_user(_user) + yield _user + admin_client.delete_user(_user) -Here's the ``equipments`` fixture changed to use ``addfinalizer`` for cleanup: -.. code-block:: python + @pytest.fixture(scope="class") + def driver(): + _driver = Chrome() + yield _driver + _driver.quit() - # content of test_yield3.py - import contextlib - import functools + @pytest.fixture(scope="class") + def landing_page(driver, login): + return LandingPage(driver) - import pytest + class TestLandingPageSuccess: + @pytest.fixture(scope="class", autouse=True) + def login(self, driver, base_url, user): + driver.get(urljoin(base_url, "/login")) + page = LoginPage(driver) + page.login(user) - @contextlib.contextmanager - def connect(port): - ... # create connection - yield - ... # close connection + def test_name_in_header(self, landing_page, user): + assert landing_page.header == f"Welcome, {user.name}!" + def test_sign_out_button(self, landing_page): + assert landing_page.sign_out_button.is_displayed() - @pytest.fixture - def equipments(request): - r = [] - for port in ("C1", "C3", "C28"): - cm = connect(port) - equip = cm.__enter__() - request.addfinalizer(functools.partial(cm.__exit__, None, None, None)) - r.append(equip) - return r + def test_profile_link(self, landing_page, user): + profile_href = urljoin(base_url, f"/profile?id={user.profile_id}") + assert landing_page.profile_link.get_attribute("href") == profile_href +Notice that the methods are only referencing ``self`` in the signature as a +formality. No state is tied to the actual test class as it might be in the +``unittest.TestCase`` framework. Everything is managed by the pytest fixture +system. -Both ``yield`` and ``addfinalizer`` methods work similarly by calling their code after the test -ends. Of course, if an exception happens before the finalize function is registered then it -will not be executed. +Each method only has to request the fixtures that it actually needs without +worrying about order. This is because the **act** fixture is an autouse fixture, +and it made sure all the other fixtures executed before it. There's no more +changes of state that need to take place, so the tests are free to make as many +non-state-changing queries as they want without risking stepping on the toes of +the other tests. + +The ``login`` fixture is defined inside the class as well, because not every one +of the other tests in the module will be expecting a successful login, and the **act** may need to +be handled a little differently for another test class. For example, if we +wanted to write another test scenario around submitting bad credentials, we +could handle it by adding something like this to the test file: + +.. note: + + It's assumed that the page object for this (i.e. ``LoginPage``) raises a + custom exception, ``BadCredentialsException``, when it recognizes text + signifying that on the login form after attempting to log in. + +.. code-block:: python + + class TestLandingPageBadCredentials: + @pytest.fixture(scope="class") + def faux_user(self, user): + _user = deepcopy(user) + _user.password = "badpass" + return _user + + def test_raises_bad_credentials_exception(self, login_page, faux_user): + with pytest.raises(BadCredentialsException): + login_page.login(faux_user) .. _`request-context`: @@ -1239,116 +2267,7 @@ into an ini-file: Currently this will not generate any error or warning, but this is intended to be handled by `#3664 `_. - -.. _`autouse`: -.. _`autouse fixtures`: - -Autouse fixtures (xUnit setup on steroids) ----------------------------------------------------------------------- - -.. regendoc:wipe - -Occasionally, you may want to have fixtures get invoked automatically -without declaring a function argument explicitly or a `usefixtures`_ decorator. -As a practical example, suppose we have a database fixture which has a -begin/rollback/commit architecture and we want to automatically surround -each test method by a transaction and a rollback. Here is a dummy -self-contained implementation of this idea: - -.. code-block:: python - - # content of test_db_transact.py - - import pytest - - - class DB: - def __init__(self): - self.intransaction = [] - - def begin(self, name): - self.intransaction.append(name) - - def rollback(self): - self.intransaction.pop() - - - @pytest.fixture(scope="module") - def db(): - return DB() - - - class TestClass: - @pytest.fixture(autouse=True) - def transact(self, request, db): - db.begin(request.function.__name__) - yield - db.rollback() - - def test_method1(self, db): - assert db.intransaction == ["test_method1"] - - def test_method2(self, db): - assert db.intransaction == ["test_method2"] - -The class-level ``transact`` fixture is marked with *autouse=true* -which implies that all test methods in the class will use this fixture -without a need to state it in the test function signature or with a -class-level ``usefixtures`` decorator. - -If we run it, we get two passing tests: - -.. code-block:: pytest - - $ pytest -q - .. [100%] - 2 passed in 0.12s - -Here is how autouse fixtures work in other scopes: - -- autouse fixtures obey the ``scope=`` keyword-argument: if an autouse fixture - has ``scope='session'`` it will only be run once, no matter where it is - defined. ``scope='class'`` means it will be run once per class, etc. - -- if an autouse fixture is defined in a test module, all its test - functions automatically use it. - -- if an autouse fixture is defined in a conftest.py file then all tests in - all test modules below its directory will invoke the fixture. - -- lastly, and **please use that with care**: if you define an autouse - fixture in a plugin, it will be invoked for all tests in all projects - where the plugin is installed. This can be useful if a fixture only - anyway works in the presence of certain settings e. g. in the ini-file. Such - a global fixture should always quickly determine if it should do - any work and avoid otherwise expensive imports or computation. - -Note that the above ``transact`` fixture may very well be a fixture that -you want to make available in your project without having it generally -active. The canonical way to do that is to put the transact definition -into a conftest.py file **without** using ``autouse``: - -.. code-block:: python - - # content of conftest.py - @pytest.fixture - def transact(request, db): - db.begin() - yield - db.rollback() - -and then e.g. have a TestClass using it by declaring the need: - -.. code-block:: python - - @pytest.mark.usefixtures("transact") - class TestClass: - def test_method1(self): - ... - -All test methods in this TestClass will use the transaction fixture while -other test classes or functions in the module will not use it unless -they also add a ``transact`` reference. +.. _`override fixtures`: Overriding fixtures on various levels ------------------------------------- From 8255effc5b17594987f77c3165b022ee31e2e70a Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 16 Dec 2020 15:43:58 -0300 Subject: [PATCH 018/630] Remove Travis badge from README Since we stopped testing Python 3.5, we no longer use Travis. --- README.rst | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.rst b/README.rst index 0fb4e363b1c..46b07e59d15 100644 --- a/README.rst +++ b/README.rst @@ -20,9 +20,6 @@ :target: https://codecov.io/gh/pytest-dev/pytest :alt: Code coverage Status -.. image:: https://travis-ci.org/pytest-dev/pytest.svg?branch=master - :target: https://travis-ci.org/pytest-dev/pytest - .. image:: https://github.com/pytest-dev/pytest/workflows/main/badge.svg :target: https://github.com/pytest-dev/pytest/actions?query=workflow%3Amain From 3eef150f2e2a2d2eee3c41a18e10e37a148d4e3b Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 17 Dec 2020 08:19:50 -0300 Subject: [PATCH 019/630] Remove other references to Travis --- scripts/append_codecov_token.py | 4 ++-- scripts/publish-gh-release-notes.py | 9 +++------ tox.ini | 3 +-- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/scripts/append_codecov_token.py b/scripts/append_codecov_token.py index 8eecb0fa51a..5c617aafb54 100644 --- a/scripts/append_codecov_token.py +++ b/scripts/append_codecov_token.py @@ -1,8 +1,8 @@ """ Appends the codecov token to the 'codecov.yml' file at the root of the repository. -This is done by CI during PRs and builds on the pytest-dev repository so we can upload coverage, at least -until codecov grows some native integration like it has with Travis and AppVeyor. +This is done by CI during PRs and builds on the pytest-dev repository so we can +upload coverage, at least until codecov grows some native integration with GitHub Actions. See discussion in https://github.com/pytest-dev/pytest/pull/6441 for more information. """ diff --git a/scripts/publish-gh-release-notes.py b/scripts/publish-gh-release-notes.py index 2531b0221b9..68cbd7adffd 100644 --- a/scripts/publish-gh-release-notes.py +++ b/scripts/publish-gh-release-notes.py @@ -1,7 +1,7 @@ """ Script used to publish GitHub release notes extracted from CHANGELOG.rst. -This script is meant to be executed after a successful deployment in Travis. +This script is meant to be executed after a successful deployment in GitHub actions. Uses the following environment variables: @@ -12,11 +12,8 @@ https://github.com/settings/tokens - It should be encrypted using: - - $travis encrypt GH_RELEASE_NOTES_TOKEN= -r pytest-dev/pytest - - And the contents pasted in the ``deploy.env.secure`` section in the ``travis.yml`` file. + This token should be set in a secret in the repository, which is exposed as an + environment variable in the main.yml workflow file. The script also requires ``pandoc`` to be previously installed in the system. diff --git a/tox.ini b/tox.ini index 43e151c07aa..908f56ea681 100644 --- a/tox.ini +++ b/tox.ini @@ -2,7 +2,6 @@ isolated_build = True minversion = 3.20.0 distshare = {homedir}/.tox/distshare -# make sure to update environment list in travis.yml and appveyor.yml envlist = linting py36 @@ -23,7 +22,7 @@ commands = doctesting: {env:_PYTEST_TOX_COVERAGE_RUN:} pytest --doctest-modules --pyargs _pytest coverage: coverage combine coverage: coverage report -m -passenv = USER USERNAME COVERAGE_* TRAVIS PYTEST_ADDOPTS TERM +passenv = USER USERNAME COVERAGE_* PYTEST_ADDOPTS TERM setenv = _PYTEST_TOX_DEFAULT_POSARGS={env:_PYTEST_TOX_POSARGS_DOCTESTING:} {env:_PYTEST_TOX_POSARGS_LSOF:} {env:_PYTEST_TOX_POSARGS_XDIST:} From bd894e3065ba6fa13327ad5dfc94f2b6208cf0ff Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Thu, 17 Dec 2020 16:55:36 +0000 Subject: [PATCH 020/630] Add Changelog to setup.cfg (#8166) Co-authored-by: Thomas Grainger Co-authored-by: Bruno Oliveira --- setup.cfg | 2 ++ 1 file changed, 2 insertions(+) diff --git a/setup.cfg b/setup.cfg index 09c07d5bb6c..14fdb6df5c0 100644 --- a/setup.cfg +++ b/setup.cfg @@ -26,6 +26,8 @@ classifiers = Topic :: Utilities keywords = test, unittest project_urls = + Changelog=https://docs.pytest.org/en/stable/changelog.html + Twitter=https://twitter.com/pytestdotorg Source=https://github.com/pytest-dev/pytest Tracker=https://github.com/pytest-dev/pytest/issues From 1264404fe712de864aa365416cee648f3cd56128 Mon Sep 17 00:00:00 2001 From: antonblr Date: Thu, 17 Dec 2020 21:01:20 -0800 Subject: [PATCH 021/630] infra: Temporary pin setup-python GH action to v2.1.4 --- .github/workflows/main.yml | 9 ++++++--- .github/workflows/prepare-release-pr.yml | 3 ++- .github/workflows/release-on-comment.yml | 3 ++- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2b779279fdc..4ae366a7c40 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -123,7 +123,8 @@ jobs: with: fetch-depth: 0 - name: Set up Python ${{ matrix.python }} - uses: actions/setup-python@v2 + # TODO: Use "v2" tag once https://github.com/actions/setup-python/issues/171 is resolved. + uses: actions/setup-python@v2.1.4 with: python-version: ${{ matrix.python }} - name: Install dependencies @@ -158,7 +159,8 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 + # TODO: Use "v2" tag once https://github.com/actions/setup-python/issues/171 is resolved. + - uses: actions/setup-python@v2.1.4 - name: set PY run: echo "name=PY::$(python -c 'import hashlib, sys;print(hashlib.sha256(sys.version.encode()+sys.executable.encode()).hexdigest())')" >> $GITHUB_ENV - uses: actions/cache@v2 @@ -184,7 +186,8 @@ jobs: with: fetch-depth: 0 - name: Set up Python - uses: actions/setup-python@v2 + # TODO: Use "v2" tag once https://github.com/actions/setup-python/issues/171 is resolved. + uses: actions/setup-python@v2.1.4 with: python-version: "3.7" - name: Install dependencies diff --git a/.github/workflows/prepare-release-pr.yml b/.github/workflows/prepare-release-pr.yml index dec35236430..e4bf51d1184 100644 --- a/.github/workflows/prepare-release-pr.yml +++ b/.github/workflows/prepare-release-pr.yml @@ -22,7 +22,8 @@ jobs: fetch-depth: 0 - name: Set up Python - uses: actions/setup-python@v2 + # TODO: Use "v2" tag once https://github.com/actions/setup-python/issues/171 is resolved. + uses: actions/setup-python@v2.1.4 with: python-version: "3.8" diff --git a/.github/workflows/release-on-comment.yml b/.github/workflows/release-on-comment.yml index 94863d896b9..f51cd86e9ee 100644 --- a/.github/workflows/release-on-comment.yml +++ b/.github/workflows/release-on-comment.yml @@ -19,7 +19,8 @@ jobs: fetch-depth: 0 - name: Set up Python - uses: actions/setup-python@v2 + # TODO: Use "v2" tag once https://github.com/actions/setup-python/issues/171 is resolved. + uses: actions/setup-python@v2.1.4 with: python-version: "3.8" - name: Install dependencies From 4da445dc2e9212f724ae0dc0f7c6fdf465801a8e Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Fri, 18 Dec 2020 10:44:20 -0800 Subject: [PATCH 022/630] Revert "infra: Temporary pin setup-python GH action to v2.1.4" --- .github/workflows/main.yml | 9 +++------ .github/workflows/prepare-release-pr.yml | 3 +-- .github/workflows/release-on-comment.yml | 3 +-- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4ae366a7c40..2b779279fdc 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -123,8 +123,7 @@ jobs: with: fetch-depth: 0 - name: Set up Python ${{ matrix.python }} - # TODO: Use "v2" tag once https://github.com/actions/setup-python/issues/171 is resolved. - uses: actions/setup-python@v2.1.4 + uses: actions/setup-python@v2 with: python-version: ${{ matrix.python }} - name: Install dependencies @@ -159,8 +158,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - # TODO: Use "v2" tag once https://github.com/actions/setup-python/issues/171 is resolved. - - uses: actions/setup-python@v2.1.4 + - uses: actions/setup-python@v2 - name: set PY run: echo "name=PY::$(python -c 'import hashlib, sys;print(hashlib.sha256(sys.version.encode()+sys.executable.encode()).hexdigest())')" >> $GITHUB_ENV - uses: actions/cache@v2 @@ -186,8 +184,7 @@ jobs: with: fetch-depth: 0 - name: Set up Python - # TODO: Use "v2" tag once https://github.com/actions/setup-python/issues/171 is resolved. - uses: actions/setup-python@v2.1.4 + uses: actions/setup-python@v2 with: python-version: "3.7" - name: Install dependencies diff --git a/.github/workflows/prepare-release-pr.yml b/.github/workflows/prepare-release-pr.yml index e4bf51d1184..dec35236430 100644 --- a/.github/workflows/prepare-release-pr.yml +++ b/.github/workflows/prepare-release-pr.yml @@ -22,8 +22,7 @@ jobs: fetch-depth: 0 - name: Set up Python - # TODO: Use "v2" tag once https://github.com/actions/setup-python/issues/171 is resolved. - uses: actions/setup-python@v2.1.4 + uses: actions/setup-python@v2 with: python-version: "3.8" diff --git a/.github/workflows/release-on-comment.yml b/.github/workflows/release-on-comment.yml index f51cd86e9ee..94863d896b9 100644 --- a/.github/workflows/release-on-comment.yml +++ b/.github/workflows/release-on-comment.yml @@ -19,8 +19,7 @@ jobs: fetch-depth: 0 - name: Set up Python - # TODO: Use "v2" tag once https://github.com/actions/setup-python/issues/171 is resolved. - uses: actions/setup-python@v2.1.4 + uses: actions/setup-python@v2 with: python-version: "3.8" - name: Install dependencies From 293a7c962da6aedbccf6ce4fc6fecf345e835432 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Fri, 18 Dec 2020 10:45:28 -0800 Subject: [PATCH 023/630] Use new pypy3 github actions syntax --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2b779279fdc..beb50178528 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -89,7 +89,7 @@ jobs: os: ubuntu-latest tox_env: "py39-xdist" - name: "ubuntu-pypy3" - python: "pypy3" + python: "pypy-3.7" os: ubuntu-latest tox_env: "pypy3-xdist" From 15156e94c49da11c6fc5a57d576d655cc7794fdf Mon Sep 17 00:00:00 2001 From: antonblr Date: Tue, 15 Dec 2020 20:16:05 -0800 Subject: [PATCH 024/630] tests: Migrate to pytester - final update --- .github/workflows/main.yml | 3 +- src/_pytest/nodes.py | 2 +- src/_pytest/python.py | 2 +- testing/deprecated_test.py | 35 ++- testing/test_cacheprovider.py | 2 +- testing/test_collection.py | 25 +- testing/test_debugging.py | 5 +- testing/test_junitxml.py | 425 ++++++++++++++++++++-------------- testing/test_link_resolve.py | 29 ++- testing/test_main.py | 29 ++- testing/test_parseopt.py | 2 +- testing/test_pytester.py | 273 +++++++++++----------- testing/test_unittest.py | 387 ++++++++++++++++--------------- 13 files changed, 656 insertions(+), 563 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index beb50178528..1b6e85fd87e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -123,7 +123,8 @@ jobs: with: fetch-depth: 0 - name: Set up Python ${{ matrix.python }} - uses: actions/setup-python@v2 + # https://github.com/actions/setup-python/issues/171 + uses: actions/setup-python@v2.1.4 with: python-version: ${{ matrix.python }} - name: Install dependencies diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py index fee0770eb2b..27c76a04302 100644 --- a/src/_pytest/nodes.py +++ b/src/_pytest/nodes.py @@ -528,7 +528,7 @@ def gethookproxy(self, fspath: "os.PathLike[str]"): warnings.warn(FSCOLLECTOR_GETHOOKPROXY_ISINITPATH, stacklevel=2) return self.session.gethookproxy(fspath) - def isinitpath(self, path: py.path.local) -> bool: + def isinitpath(self, path: "os.PathLike[str]") -> bool: warnings.warn(FSCOLLECTOR_GETHOOKPROXY_ISINITPATH, stacklevel=2) return self.session.isinitpath(path) diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 18e449b9361..018e368f45e 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -660,7 +660,7 @@ def gethookproxy(self, fspath: "os.PathLike[str]"): warnings.warn(FSCOLLECTOR_GETHOOKPROXY_ISINITPATH, stacklevel=2) return self.session.gethookproxy(fspath) - def isinitpath(self, path: py.path.local) -> bool: + def isinitpath(self, path: "os.PathLike[str]") -> bool: warnings.warn(FSCOLLECTOR_GETHOOKPROXY_ISINITPATH, stacklevel=2) return self.session.isinitpath(path) diff --git a/testing/deprecated_test.py b/testing/deprecated_test.py index d213414ee45..6d92d181f99 100644 --- a/testing/deprecated_test.py +++ b/testing/deprecated_test.py @@ -5,24 +5,23 @@ import pytest from _pytest import deprecated from _pytest.pytester import Pytester -from _pytest.pytester import Testdir @pytest.mark.parametrize("attribute", pytest.collect.__all__) # type: ignore # false positive due to dynamic attribute -def test_pytest_collect_module_deprecated(attribute): +def test_pytest_collect_module_deprecated(attribute) -> None: with pytest.warns(DeprecationWarning, match=attribute): getattr(pytest.collect, attribute) @pytest.mark.parametrize("plugin", sorted(deprecated.DEPRECATED_EXTERNAL_PLUGINS)) @pytest.mark.filterwarnings("default") -def test_external_plugins_integrated(testdir, plugin): - testdir.syspathinsert() - testdir.makepyfile(**{plugin: ""}) +def test_external_plugins_integrated(pytester: Pytester, plugin) -> None: + pytester.syspathinsert() + pytester.makepyfile(**{plugin: ""}) with pytest.warns(pytest.PytestConfigWarning): - testdir.parseconfig("-p", plugin) + pytester.parseconfig("-p", plugin) def test_fillfuncargs_is_deprecated() -> None: @@ -49,32 +48,32 @@ def test_fillfixtures_is_deprecated() -> None: _pytest.fixtures.fillfixtures(mock.Mock()) -def test_minus_k_dash_is_deprecated(testdir) -> None: - threepass = testdir.makepyfile( +def test_minus_k_dash_is_deprecated(pytester: Pytester) -> None: + threepass = pytester.makepyfile( test_threepass=""" def test_one(): assert 1 def test_two(): assert 1 def test_three(): assert 1 """ ) - result = testdir.runpytest("-k=-test_two", threepass) + result = pytester.runpytest("-k=-test_two", threepass) result.stdout.fnmatch_lines(["*The `-k '-expr'` syntax*deprecated*"]) -def test_minus_k_colon_is_deprecated(testdir) -> None: - threepass = testdir.makepyfile( +def test_minus_k_colon_is_deprecated(pytester: Pytester) -> None: + threepass = pytester.makepyfile( test_threepass=""" def test_one(): assert 1 def test_two(): assert 1 def test_three(): assert 1 """ ) - result = testdir.runpytest("-k", "test_two:", threepass) + result = pytester.runpytest("-k", "test_two:", threepass) result.stdout.fnmatch_lines(["*The `-k 'expr:'` syntax*deprecated*"]) -def test_fscollector_gethookproxy_isinitpath(testdir: Testdir) -> None: - module = testdir.getmodulecol( +def test_fscollector_gethookproxy_isinitpath(pytester: Pytester) -> None: + module = pytester.getmodulecol( """ def test_foo(): pass """, @@ -85,16 +84,16 @@ def test_foo(): pass assert isinstance(package, pytest.Package) with pytest.warns(pytest.PytestDeprecationWarning, match="gethookproxy"): - package.gethookproxy(testdir.tmpdir) + package.gethookproxy(pytester.path) with pytest.warns(pytest.PytestDeprecationWarning, match="isinitpath"): - package.isinitpath(testdir.tmpdir) + package.isinitpath(pytester.path) # The methods on Session are *not* deprecated. session = module.session with warnings.catch_warnings(record=True) as rec: - session.gethookproxy(testdir.tmpdir) - session.isinitpath(testdir.tmpdir) + session.gethookproxy(pytester.path) + session.isinitpath(pytester.path) assert len(rec) == 0 diff --git a/testing/test_cacheprovider.py b/testing/test_cacheprovider.py index 7f0827bd488..ebd455593f3 100644 --- a/testing/test_cacheprovider.py +++ b/testing/test_cacheprovider.py @@ -1050,7 +1050,7 @@ def test_packages(self, pytester: Pytester) -> None: class TestNewFirst: - def test_newfirst_usecase(self, pytester: Pytester, testdir) -> None: + def test_newfirst_usecase(self, pytester: Pytester) -> None: pytester.makepyfile( **{ "test_1/test_1.py": """ diff --git a/testing/test_collection.py b/testing/test_collection.py index 862c1aba8d2..2d03fda39de 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -6,6 +6,8 @@ from pathlib import Path from typing import List +import py.path + import pytest from _pytest.config import ExitCode from _pytest.fixtures import FixtureRequest @@ -16,7 +18,6 @@ from _pytest.pathlib import symlink_or_skip from _pytest.pytester import HookRecorder from _pytest.pytester import Pytester -from _pytest.pytester import Testdir def ensure_file(file_path: Path) -> Path: @@ -206,15 +207,17 @@ def test_ignored_virtualenvs_norecursedirs_precedence( "Activate.ps1", ), ) - def test__in_venv(self, testdir: Testdir, fname: str) -> None: + def test__in_venv(self, pytester: Pytester, fname: str) -> None: """Directly test the virtual env detection function""" bindir = "Scripts" if sys.platform.startswith("win") else "bin" # no bin/activate, not a virtualenv - base_path = testdir.tmpdir.mkdir("venv") - assert _in_venv(base_path) is False + base_path = pytester.mkdir("venv") + assert _in_venv(py.path.local(base_path)) is False # with bin/activate, totally a virtualenv - base_path.ensure(bindir, fname) - assert _in_venv(base_path) is True + bin_path = base_path.joinpath(bindir) + bin_path.mkdir() + bin_path.joinpath(fname).touch() + assert _in_venv(py.path.local(base_path)) is True def test_custom_norecursedirs(self, pytester: Pytester) -> None: pytester.makeini( @@ -264,7 +267,7 @@ def test_testpaths_ini(self, pytester: Pytester, monkeypatch: MonkeyPatch) -> No class TestCollectPluginHookRelay: - def test_pytest_collect_file(self, testdir: Testdir) -> None: + def test_pytest_collect_file(self, pytester: Pytester) -> None: wascalled = [] class Plugin: @@ -273,8 +276,8 @@ def pytest_collect_file(self, path): # Ignore hidden files, e.g. .testmondata. wascalled.append(path) - testdir.makefile(".abc", "xyz") - pytest.main(testdir.tmpdir, plugins=[Plugin()]) + pytester.makefile(".abc", "xyz") + pytest.main(py.path.local(pytester.path), plugins=[Plugin()]) assert len(wascalled) == 1 assert wascalled[0].ext == ".abc" @@ -1336,7 +1339,7 @@ def test_does_not_put_src_on_path(pytester: Pytester) -> None: assert result.ret == ExitCode.OK -def test_fscollector_from_parent(testdir: Testdir, request: FixtureRequest) -> None: +def test_fscollector_from_parent(pytester: Pytester, request: FixtureRequest) -> None: """Ensure File.from_parent can forward custom arguments to the constructor. Context: https://github.com/pytest-dev/pytest-cpp/pull/47 @@ -1352,7 +1355,7 @@ def from_parent(cls, parent, *, fspath, x): return super().from_parent(parent=parent, fspath=fspath, x=x) collector = MyCollector.from_parent( - parent=request.session, fspath=testdir.tmpdir / "foo", x=10 + parent=request.session, fspath=py.path.local(pytester.path) / "foo", x=10 ) assert collector.x == 10 diff --git a/testing/test_debugging.py b/testing/test_debugging.py index ed96f7ec781..8218b7a0ede 100644 --- a/testing/test_debugging.py +++ b/testing/test_debugging.py @@ -21,11 +21,10 @@ @pytest.fixture(autouse=True) -def pdb_env(request): +def pdb_env(request, monkeypatch: MonkeyPatch): if "pytester" in request.fixturenames: # Disable pdb++ with inner tests. - pytester = request.getfixturevalue("testdir") - pytester.monkeypatch.setenv("PDBPP_HIJACK_PDB", "0") + monkeypatch.setenv("PDBPP_HIJACK_PDB", "0") def runpdb_and_get_report(pytester: Pytester, source: str): diff --git a/testing/test_junitxml.py b/testing/test_junitxml.py index 006bea96280..3e445dcefc5 100644 --- a/testing/test_junitxml.py +++ b/testing/test_junitxml.py @@ -4,24 +4,31 @@ from pathlib import Path from typing import cast from typing import List +from typing import Optional from typing import Tuple from typing import TYPE_CHECKING +from typing import TypeVar +from typing import Union from xml.dom import minidom -import py import xmlschema import pytest from _pytest.config import Config from _pytest.junitxml import bin_xml_escape from _pytest.junitxml import LogXML +from _pytest.monkeypatch import MonkeyPatch +from _pytest.pytester import Pytester +from _pytest.pytester import RunResult from _pytest.reports import BaseReport from _pytest.reports import TestReport from _pytest.store import Store +T = TypeVar("T") + @pytest.fixture(scope="session") -def schema(): +def schema() -> xmlschema.XMLSchema: """Return an xmlschema.XMLSchema object for the junit-10.xsd file.""" fn = Path(__file__).parent / "example_scripts/junit-10.xsd" with fn.open() as f: @@ -29,7 +36,7 @@ def schema(): @pytest.fixture -def run_and_parse(testdir, schema): +def run_and_parse(pytester: Pytester, schema: xmlschema.XMLSchema) -> T: """Fixture that returns a function that can be used to execute pytest and return the parsed ``DomNode`` of the root xml node. @@ -37,18 +44,20 @@ def run_and_parse(testdir, schema): "xunit2" is also automatically validated against the schema. """ - def run(*args, family="xunit1"): + def run( + *args: Union[str, "os.PathLike[str]"], family: Optional[str] = "xunit1", + ) -> Tuple[RunResult, "DomNode"]: if family: args = ("-o", "junit_family=" + family) + args - xml_path = testdir.tmpdir.join("junit.xml") - result = testdir.runpytest("--junitxml=%s" % xml_path, *args) + xml_path = pytester.path.joinpath("junit.xml") + result = pytester.runpytest("--junitxml=%s" % xml_path, *args) if family == "xunit2": with xml_path.open() as f: schema.validate(f) xmldoc = minidom.parse(str(xml_path)) return result, DomNode(xmldoc) - return run + return cast(T, run) def assert_attr(node, **kwargs): @@ -130,8 +139,10 @@ def next_sibling(self): class TestPython: @parametrize_families - def test_summing_simple(self, testdir, run_and_parse, xunit_family): - testdir.makepyfile( + def test_summing_simple( + self, pytester: Pytester, run_and_parse, xunit_family + ) -> None: + pytester.makepyfile( """ import pytest def test_pass(): @@ -154,8 +165,10 @@ def test_xpass(): node.assert_attr(name="pytest", errors=0, failures=1, skipped=2, tests=5) @parametrize_families - def test_summing_simple_with_errors(self, testdir, run_and_parse, xunit_family): - testdir.makepyfile( + def test_summing_simple_with_errors( + self, pytester: Pytester, run_and_parse, xunit_family + ) -> None: + pytester.makepyfile( """ import pytest @pytest.fixture @@ -181,8 +194,10 @@ def test_xpass(): node.assert_attr(name="pytest", errors=1, failures=2, skipped=1, tests=5) @parametrize_families - def test_hostname_in_xml(self, testdir, run_and_parse, xunit_family): - testdir.makepyfile( + def test_hostname_in_xml( + self, pytester: Pytester, run_and_parse, xunit_family + ) -> None: + pytester.makepyfile( """ def test_pass(): pass @@ -193,8 +208,10 @@ def test_pass(): node.assert_attr(hostname=platform.node()) @parametrize_families - def test_timestamp_in_xml(self, testdir, run_and_parse, xunit_family): - testdir.makepyfile( + def test_timestamp_in_xml( + self, pytester: Pytester, run_and_parse, xunit_family + ) -> None: + pytester.makepyfile( """ def test_pass(): pass @@ -206,8 +223,10 @@ def test_pass(): timestamp = datetime.strptime(node["timestamp"], "%Y-%m-%dT%H:%M:%S.%f") assert start_time <= timestamp < datetime.now() - def test_timing_function(self, testdir, run_and_parse, mock_timing): - testdir.makepyfile( + def test_timing_function( + self, pytester: Pytester, run_and_parse, mock_timing + ) -> None: + pytester.makepyfile( """ from _pytest import timing def setup_module(): @@ -226,8 +245,12 @@ def test_sleep(): @pytest.mark.parametrize("duration_report", ["call", "total"]) def test_junit_duration_report( - self, testdir, monkeypatch, duration_report, run_and_parse - ): + self, + pytester: Pytester, + monkeypatch: MonkeyPatch, + duration_report, + run_and_parse, + ) -> None: # mock LogXML.node_reporter so it always sets a known duration to each test report object original_node_reporter = LogXML.node_reporter @@ -239,7 +262,7 @@ def node_reporter_wrapper(s, report): monkeypatch.setattr(LogXML, "node_reporter", node_reporter_wrapper) - testdir.makepyfile( + pytester.makepyfile( """ def test_foo(): pass @@ -256,8 +279,8 @@ def test_foo(): assert val == 1.0 @parametrize_families - def test_setup_error(self, testdir, run_and_parse, xunit_family): - testdir.makepyfile( + def test_setup_error(self, pytester: Pytester, run_and_parse, xunit_family) -> None: + pytester.makepyfile( """ import pytest @@ -279,8 +302,10 @@ def test_function(arg): assert "ValueError" in fnode.toxml() @parametrize_families - def test_teardown_error(self, testdir, run_and_parse, xunit_family): - testdir.makepyfile( + def test_teardown_error( + self, pytester: Pytester, run_and_parse, xunit_family + ) -> None: + pytester.makepyfile( """ import pytest @@ -302,8 +327,10 @@ def test_function(arg): assert "ValueError" in fnode.toxml() @parametrize_families - def test_call_failure_teardown_error(self, testdir, run_and_parse, xunit_family): - testdir.makepyfile( + def test_call_failure_teardown_error( + self, pytester: Pytester, run_and_parse, xunit_family + ) -> None: + pytester.makepyfile( """ import pytest @@ -331,8 +358,10 @@ def test_function(arg): ) @parametrize_families - def test_skip_contains_name_reason(self, testdir, run_and_parse, xunit_family): - testdir.makepyfile( + def test_skip_contains_name_reason( + self, pytester: Pytester, run_and_parse, xunit_family + ) -> None: + pytester.makepyfile( """ import pytest def test_skip(): @@ -349,8 +378,10 @@ def test_skip(): snode.assert_attr(type="pytest.skip", message="hello23") @parametrize_families - def test_mark_skip_contains_name_reason(self, testdir, run_and_parse, xunit_family): - testdir.makepyfile( + def test_mark_skip_contains_name_reason( + self, pytester: Pytester, run_and_parse, xunit_family + ) -> None: + pytester.makepyfile( """ import pytest @pytest.mark.skip(reason="hello24") @@ -371,9 +402,9 @@ def test_skip(): @parametrize_families def test_mark_skipif_contains_name_reason( - self, testdir, run_and_parse, xunit_family - ): - testdir.makepyfile( + self, pytester: Pytester, run_and_parse, xunit_family + ) -> None: + pytester.makepyfile( """ import pytest GLOBAL_CONDITION = True @@ -395,9 +426,9 @@ def test_skip(): @parametrize_families def test_mark_skip_doesnt_capture_output( - self, testdir, run_and_parse, xunit_family - ): - testdir.makepyfile( + self, pytester: Pytester, run_and_parse, xunit_family + ) -> None: + pytester.makepyfile( """ import pytest @pytest.mark.skip(reason="foo") @@ -411,8 +442,10 @@ def test_skip(): assert "bar!" not in node_xml @parametrize_families - def test_classname_instance(self, testdir, run_and_parse, xunit_family): - testdir.makepyfile( + def test_classname_instance( + self, pytester: Pytester, run_and_parse, xunit_family + ) -> None: + pytester.makepyfile( """ class TestClass(object): def test_method(self): @@ -429,9 +462,11 @@ def test_method(self): ) @parametrize_families - def test_classname_nested_dir(self, testdir, run_and_parse, xunit_family): - p = testdir.tmpdir.ensure("sub", "test_hello.py") - p.write("def test_func(): 0/0") + def test_classname_nested_dir( + self, pytester: Pytester, run_and_parse, xunit_family + ) -> None: + p = pytester.mkdir("sub").joinpath("test_hello.py") + p.write_text("def test_func(): 0/0") result, dom = run_and_parse(family=xunit_family) assert result.ret node = dom.find_first_by_tag("testsuite") @@ -440,9 +475,11 @@ def test_classname_nested_dir(self, testdir, run_and_parse, xunit_family): tnode.assert_attr(classname="sub.test_hello", name="test_func") @parametrize_families - def test_internal_error(self, testdir, run_and_parse, xunit_family): - testdir.makeconftest("def pytest_runtest_protocol(): 0 / 0") - testdir.makepyfile("def test_function(): pass") + def test_internal_error( + self, pytester: Pytester, run_and_parse, xunit_family + ) -> None: + pytester.makeconftest("def pytest_runtest_protocol(): 0 / 0") + pytester.makepyfile("def test_function(): pass") result, dom = run_and_parse(family=xunit_family) assert result.ret node = dom.find_first_by_tag("testsuite") @@ -458,9 +495,9 @@ def test_internal_error(self, testdir, run_and_parse, xunit_family): ) @parametrize_families def test_failure_function( - self, testdir, junit_logging, run_and_parse, xunit_family - ): - testdir.makepyfile( + self, pytester: Pytester, junit_logging, run_and_parse, xunit_family + ) -> None: + pytester.makepyfile( """ import logging import sys @@ -521,8 +558,10 @@ def test_fail(): ), "Found unexpected content: system-err" @parametrize_families - def test_failure_verbose_message(self, testdir, run_and_parse, xunit_family): - testdir.makepyfile( + def test_failure_verbose_message( + self, pytester: Pytester, run_and_parse, xunit_family + ) -> None: + pytester.makepyfile( """ import sys def test_fail(): @@ -536,8 +575,10 @@ def test_fail(): fnode.assert_attr(message="AssertionError: An error\nassert 0") @parametrize_families - def test_failure_escape(self, testdir, run_and_parse, xunit_family): - testdir.makepyfile( + def test_failure_escape( + self, pytester: Pytester, run_and_parse, xunit_family + ) -> None: + pytester.makepyfile( """ import pytest @pytest.mark.parametrize('arg1', "<&'", ids="<&'") @@ -564,8 +605,10 @@ def test_func(arg1): assert "%s\n" % char in text @parametrize_families - def test_junit_prefixing(self, testdir, run_and_parse, xunit_family): - testdir.makepyfile( + def test_junit_prefixing( + self, pytester: Pytester, run_and_parse, xunit_family + ) -> None: + pytester.makepyfile( """ def test_func(): assert 0 @@ -586,8 +629,10 @@ def test_hello(self): ) @parametrize_families - def test_xfailure_function(self, testdir, run_and_parse, xunit_family): - testdir.makepyfile( + def test_xfailure_function( + self, pytester: Pytester, run_and_parse, xunit_family + ) -> None: + pytester.makepyfile( """ import pytest def test_xfail(): @@ -604,8 +649,10 @@ def test_xfail(): fnode.assert_attr(type="pytest.xfail", message="42") @parametrize_families - def test_xfailure_marker(self, testdir, run_and_parse, xunit_family): - testdir.makepyfile( + def test_xfailure_marker( + self, pytester: Pytester, run_and_parse, xunit_family + ) -> None: + pytester.makepyfile( """ import pytest @pytest.mark.xfail(reason="42") @@ -625,8 +672,10 @@ def test_xfail(): @pytest.mark.parametrize( "junit_logging", ["no", "log", "system-out", "system-err", "out-err", "all"] ) - def test_xfail_captures_output_once(self, testdir, junit_logging, run_and_parse): - testdir.makepyfile( + def test_xfail_captures_output_once( + self, pytester: Pytester, junit_logging, run_and_parse + ) -> None: + pytester.makepyfile( """ import sys import pytest @@ -652,8 +701,10 @@ def test_fail(): assert len(tnode.find_by_tag("system-out")) == 0 @parametrize_families - def test_xfailure_xpass(self, testdir, run_and_parse, xunit_family): - testdir.makepyfile( + def test_xfailure_xpass( + self, pytester: Pytester, run_and_parse, xunit_family + ) -> None: + pytester.makepyfile( """ import pytest @pytest.mark.xfail @@ -669,8 +720,10 @@ def test_xpass(): tnode.assert_attr(classname="test_xfailure_xpass", name="test_xpass") @parametrize_families - def test_xfailure_xpass_strict(self, testdir, run_and_parse, xunit_family): - testdir.makepyfile( + def test_xfailure_xpass_strict( + self, pytester: Pytester, run_and_parse, xunit_family + ) -> None: + pytester.makepyfile( """ import pytest @pytest.mark.xfail(strict=True, reason="This needs to fail!") @@ -688,8 +741,10 @@ def test_xpass(): fnode.assert_attr(message="[XPASS(strict)] This needs to fail!") @parametrize_families - def test_collect_error(self, testdir, run_and_parse, xunit_family): - testdir.makepyfile("syntax error") + def test_collect_error( + self, pytester: Pytester, run_and_parse, xunit_family + ) -> None: + pytester.makepyfile("syntax error") result, dom = run_and_parse(family=xunit_family) assert result.ret node = dom.find_first_by_tag("testsuite") @@ -699,9 +754,9 @@ def test_collect_error(self, testdir, run_and_parse, xunit_family): fnode.assert_attr(message="collection failure") assert "SyntaxError" in fnode.toxml() - def test_unicode(self, testdir, run_and_parse): + def test_unicode(self, pytester: Pytester, run_and_parse) -> None: value = "hx\xc4\x85\xc4\x87\n" - testdir.makepyfile( + pytester.makepyfile( """\ # coding: latin1 def test_hello(): @@ -716,9 +771,9 @@ def test_hello(): fnode = tnode.find_first_by_tag("failure") assert "hx" in fnode.toxml() - def test_assertion_binchars(self, testdir, run_and_parse): + def test_assertion_binchars(self, pytester: Pytester, run_and_parse) -> None: """This test did fail when the escaping wasn't strict.""" - testdir.makepyfile( + pytester.makepyfile( """ M1 = '\x01\x02\x03\x04' @@ -732,8 +787,10 @@ def test_str_compare(): print(dom.toxml()) @pytest.mark.parametrize("junit_logging", ["no", "system-out"]) - def test_pass_captures_stdout(self, testdir, run_and_parse, junit_logging): - testdir.makepyfile( + def test_pass_captures_stdout( + self, pytester: Pytester, run_and_parse, junit_logging + ) -> None: + pytester.makepyfile( """ def test_pass(): print('hello-stdout') @@ -753,8 +810,10 @@ def test_pass(): ), "'hello-stdout' should be in system-out" @pytest.mark.parametrize("junit_logging", ["no", "system-err"]) - def test_pass_captures_stderr(self, testdir, run_and_parse, junit_logging): - testdir.makepyfile( + def test_pass_captures_stderr( + self, pytester: Pytester, run_and_parse, junit_logging + ) -> None: + pytester.makepyfile( """ import sys def test_pass(): @@ -775,8 +834,10 @@ def test_pass(): ), "'hello-stderr' should be in system-err" @pytest.mark.parametrize("junit_logging", ["no", "system-out"]) - def test_setup_error_captures_stdout(self, testdir, run_and_parse, junit_logging): - testdir.makepyfile( + def test_setup_error_captures_stdout( + self, pytester: Pytester, run_and_parse, junit_logging + ) -> None: + pytester.makepyfile( """ import pytest @@ -802,8 +863,10 @@ def test_function(arg): ), "'hello-stdout' should be in system-out" @pytest.mark.parametrize("junit_logging", ["no", "system-err"]) - def test_setup_error_captures_stderr(self, testdir, run_and_parse, junit_logging): - testdir.makepyfile( + def test_setup_error_captures_stderr( + self, pytester: Pytester, run_and_parse, junit_logging + ) -> None: + pytester.makepyfile( """ import sys import pytest @@ -830,8 +893,10 @@ def test_function(arg): ), "'hello-stderr' should be in system-err" @pytest.mark.parametrize("junit_logging", ["no", "system-out"]) - def test_avoid_double_stdout(self, testdir, run_and_parse, junit_logging): - testdir.makepyfile( + def test_avoid_double_stdout( + self, pytester: Pytester, run_and_parse, junit_logging + ) -> None: + pytester.makepyfile( """ import sys import pytest @@ -858,7 +923,7 @@ def test_function(arg): assert "hello-stdout teardown" in systemout.toxml() -def test_mangle_test_address(): +def test_mangle_test_address() -> None: from _pytest.junitxml import mangle_test_address address = "::".join(["a/my.py.thing.py", "Class", "()", "method", "[a-1-::]"]) @@ -866,7 +931,7 @@ def test_mangle_test_address(): assert newnames == ["a.my.py.thing", "Class", "method", "[a-1-::]"] -def test_dont_configure_on_workers(tmpdir) -> None: +def test_dont_configure_on_workers(tmp_path: Path) -> None: gotten: List[object] = [] class FakeConfig: @@ -882,8 +947,8 @@ def getini(self, name): return "pytest" junitprefix = None - # XXX: shouldn't need tmpdir ? - xmlpath = str(tmpdir.join("junix.xml")) + # XXX: shouldn't need tmp_path ? + xmlpath = str(tmp_path.joinpath("junix.xml")) register = gotten.append fake_config = cast(Config, FakeConfig()) @@ -898,8 +963,10 @@ def getini(self, name): class TestNonPython: @parametrize_families - def test_summing_simple(self, testdir, run_and_parse, xunit_family): - testdir.makeconftest( + def test_summing_simple( + self, pytester: Pytester, run_and_parse, xunit_family + ) -> None: + pytester.makeconftest( """ import pytest def pytest_collect_file(path, parent): @@ -912,7 +979,7 @@ def repr_failure(self, excinfo): return "custom item runtest failed" """ ) - testdir.tmpdir.join("myfile.xyz").write("hello") + pytester.path.joinpath("myfile.xyz").write_text("hello") result, dom = run_and_parse(family=xunit_family) assert result.ret node = dom.find_first_by_tag("testsuite") @@ -925,9 +992,9 @@ def repr_failure(self, excinfo): @pytest.mark.parametrize("junit_logging", ["no", "system-out"]) -def test_nullbyte(testdir, junit_logging): +def test_nullbyte(pytester: Pytester, junit_logging) -> None: # A null byte can not occur in XML (see section 2.2 of the spec) - testdir.makepyfile( + pytester.makepyfile( """ import sys def test_print_nullbyte(): @@ -936,9 +1003,9 @@ def test_print_nullbyte(): assert False """ ) - xmlf = testdir.tmpdir.join("junit.xml") - testdir.runpytest("--junitxml=%s" % xmlf, "-o", "junit_logging=%s" % junit_logging) - text = xmlf.read() + xmlf = pytester.path.joinpath("junit.xml") + pytester.runpytest("--junitxml=%s" % xmlf, "-o", "junit_logging=%s" % junit_logging) + text = xmlf.read_text() assert "\x00" not in text if junit_logging == "system-out": assert "#x00" in text @@ -947,9 +1014,9 @@ def test_print_nullbyte(): @pytest.mark.parametrize("junit_logging", ["no", "system-out"]) -def test_nullbyte_replace(testdir, junit_logging): +def test_nullbyte_replace(pytester: Pytester, junit_logging) -> None: # Check if the null byte gets replaced - testdir.makepyfile( + pytester.makepyfile( """ import sys def test_print_nullbyte(): @@ -958,16 +1025,16 @@ def test_print_nullbyte(): assert False """ ) - xmlf = testdir.tmpdir.join("junit.xml") - testdir.runpytest("--junitxml=%s" % xmlf, "-o", "junit_logging=%s" % junit_logging) - text = xmlf.read() + xmlf = pytester.path.joinpath("junit.xml") + pytester.runpytest("--junitxml=%s" % xmlf, "-o", "junit_logging=%s" % junit_logging) + text = xmlf.read_text() if junit_logging == "system-out": assert "#x0" in text if junit_logging == "no": assert "#x0" not in text -def test_invalid_xml_escape(): +def test_invalid_xml_escape() -> None: # Test some more invalid xml chars, the full range should be # tested really but let's just test the edges of the ranges # instead. @@ -1003,52 +1070,52 @@ def test_invalid_xml_escape(): assert chr(i) == bin_xml_escape(chr(i)) -def test_logxml_path_expansion(tmpdir, monkeypatch): - home_tilde = py.path.local(os.path.expanduser("~")).join("test.xml") - xml_tilde = LogXML("~%stest.xml" % tmpdir.sep, None) - assert xml_tilde.logfile == home_tilde +def test_logxml_path_expansion(tmp_path: Path, monkeypatch: MonkeyPatch) -> None: + home_tilde = Path(os.path.expanduser("~")).joinpath("test.xml") + xml_tilde = LogXML(Path("~", "test.xml"), None) + assert xml_tilde.logfile == str(home_tilde) - monkeypatch.setenv("HOME", str(tmpdir)) + monkeypatch.setenv("HOME", str(tmp_path)) home_var = os.path.normpath(os.path.expandvars("$HOME/test.xml")) - xml_var = LogXML("$HOME%stest.xml" % tmpdir.sep, None) - assert xml_var.logfile == home_var + xml_var = LogXML(Path("$HOME", "test.xml"), None) + assert xml_var.logfile == str(home_var) -def test_logxml_changingdir(testdir): - testdir.makepyfile( +def test_logxml_changingdir(pytester: Pytester) -> None: + pytester.makepyfile( """ def test_func(): import os os.chdir("a") """ ) - testdir.tmpdir.mkdir("a") - result = testdir.runpytest("--junitxml=a/x.xml") + pytester.mkdir("a") + result = pytester.runpytest("--junitxml=a/x.xml") assert result.ret == 0 - assert testdir.tmpdir.join("a/x.xml").check() + assert pytester.path.joinpath("a/x.xml").exists() -def test_logxml_makedir(testdir): +def test_logxml_makedir(pytester: Pytester) -> None: """--junitxml should automatically create directories for the xml file""" - testdir.makepyfile( + pytester.makepyfile( """ def test_pass(): pass """ ) - result = testdir.runpytest("--junitxml=path/to/results.xml") + result = pytester.runpytest("--junitxml=path/to/results.xml") assert result.ret == 0 - assert testdir.tmpdir.join("path/to/results.xml").check() + assert pytester.path.joinpath("path/to/results.xml").exists() -def test_logxml_check_isdir(testdir): +def test_logxml_check_isdir(pytester: Pytester) -> None: """Give an error if --junit-xml is a directory (#2089)""" - result = testdir.runpytest("--junit-xml=.") + result = pytester.runpytest("--junit-xml=.") result.stderr.fnmatch_lines(["*--junitxml must be a filename*"]) -def test_escaped_parametrized_names_xml(testdir, run_and_parse): - testdir.makepyfile( +def test_escaped_parametrized_names_xml(pytester: Pytester, run_and_parse) -> None: + pytester.makepyfile( """\ import pytest @pytest.mark.parametrize('char', ["\\x00"]) @@ -1062,8 +1129,10 @@ def test_func(char): node.assert_attr(name="test_func[\\x00]") -def test_double_colon_split_function_issue469(testdir, run_and_parse): - testdir.makepyfile( +def test_double_colon_split_function_issue469( + pytester: Pytester, run_and_parse +) -> None: + pytester.makepyfile( """ import pytest @pytest.mark.parametrize('param', ["double::colon"]) @@ -1078,8 +1147,8 @@ def test_func(param): node.assert_attr(name="test_func[double::colon]") -def test_double_colon_split_method_issue469(testdir, run_and_parse): - testdir.makepyfile( +def test_double_colon_split_method_issue469(pytester: Pytester, run_and_parse) -> None: + pytester.makepyfile( """ import pytest class TestClass(object): @@ -1095,8 +1164,8 @@ def test_func(self, param): node.assert_attr(name="test_func[double::colon]") -def test_unicode_issue368(testdir) -> None: - path = testdir.tmpdir.join("test.xml") +def test_unicode_issue368(pytester: Pytester) -> None: + path = pytester.path.joinpath("test.xml") log = LogXML(str(path), None) ustr = "ВНИ!" @@ -1125,8 +1194,8 @@ class Report(BaseReport): log.pytest_sessionfinish() -def test_record_property(testdir, run_and_parse): - testdir.makepyfile( +def test_record_property(pytester: Pytester, run_and_parse) -> None: + pytester.makepyfile( """ import pytest @@ -1147,8 +1216,8 @@ def test_record(record_property, other): result.stdout.fnmatch_lines(["*= 1 passed in *"]) -def test_record_property_same_name(testdir, run_and_parse): - testdir.makepyfile( +def test_record_property_same_name(pytester: Pytester, run_and_parse) -> None: + pytester.makepyfile( """ def test_record_with_same_name(record_property): record_property("foo", "bar") @@ -1165,8 +1234,8 @@ def test_record_with_same_name(record_property): @pytest.mark.parametrize("fixture_name", ["record_property", "record_xml_attribute"]) -def test_record_fixtures_without_junitxml(testdir, fixture_name): - testdir.makepyfile( +def test_record_fixtures_without_junitxml(pytester: Pytester, fixture_name) -> None: + pytester.makepyfile( """ def test_record({fixture_name}): {fixture_name}("foo", "bar") @@ -1174,19 +1243,19 @@ def test_record({fixture_name}): fixture_name=fixture_name ) ) - result = testdir.runpytest() + result = pytester.runpytest() assert result.ret == 0 @pytest.mark.filterwarnings("default") -def test_record_attribute(testdir, run_and_parse): - testdir.makeini( +def test_record_attribute(pytester: Pytester, run_and_parse) -> None: + pytester.makeini( """ [pytest] junit_family = xunit1 """ ) - testdir.makepyfile( + pytester.makepyfile( """ import pytest @@ -1209,15 +1278,17 @@ def test_record(record_xml_attribute, other): @pytest.mark.filterwarnings("default") @pytest.mark.parametrize("fixture_name", ["record_xml_attribute", "record_property"]) -def test_record_fixtures_xunit2(testdir, fixture_name, run_and_parse): +def test_record_fixtures_xunit2( + pytester: Pytester, fixture_name, run_and_parse +) -> None: """Ensure record_xml_attribute and record_property drop values when outside of legacy family.""" - testdir.makeini( + pytester.makeini( """ [pytest] junit_family = xunit2 """ ) - testdir.makepyfile( + pytester.makepyfile( """ import pytest @@ -1246,13 +1317,15 @@ def test_record({fixture_name}, other): result.stdout.fnmatch_lines(expected_lines) -def test_random_report_log_xdist(testdir, monkeypatch, run_and_parse): +def test_random_report_log_xdist( + pytester: Pytester, monkeypatch: MonkeyPatch, run_and_parse +) -> None: """`xdist` calls pytest_runtest_logreport as they are executed by the workers, with nodes from several nodes overlapping, so junitxml must cope with that to produce correct reports (#1064).""" pytest.importorskip("xdist") monkeypatch.delenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD", raising=False) - testdir.makepyfile( + pytester.makepyfile( """ import pytest, time @pytest.mark.parametrize('i', list(range(30))) @@ -1271,8 +1344,8 @@ def test_x(i): @parametrize_families -def test_root_testsuites_tag(testdir, run_and_parse, xunit_family): - testdir.makepyfile( +def test_root_testsuites_tag(pytester: Pytester, run_and_parse, xunit_family) -> None: + pytester.makepyfile( """ def test_x(): pass @@ -1285,8 +1358,8 @@ def test_x(): assert suite_node.tag == "testsuite" -def test_runs_twice(testdir, run_and_parse): - f = testdir.makepyfile( +def test_runs_twice(pytester: Pytester, run_and_parse) -> None: + f = pytester.makepyfile( """ def test_pass(): pass @@ -1299,10 +1372,12 @@ def test_pass(): assert first == second -def test_runs_twice_xdist(testdir, run_and_parse): +def test_runs_twice_xdist( + pytester: Pytester, monkeypatch: MonkeyPatch, run_and_parse +) -> None: pytest.importorskip("xdist") - testdir.monkeypatch.delenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD") - f = testdir.makepyfile( + monkeypatch.delenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD") + f = pytester.makepyfile( """ def test_pass(): pass @@ -1315,9 +1390,9 @@ def test_pass(): assert first == second -def test_fancy_items_regression(testdir, run_and_parse): +def test_fancy_items_regression(pytester: Pytester, run_and_parse) -> None: # issue 1259 - testdir.makeconftest( + pytester.makeconftest( """ import pytest class FunItem(pytest.Item): @@ -1341,7 +1416,7 @@ def pytest_collect_file(path, parent): """ ) - testdir.makepyfile( + pytester.makepyfile( """ def test_pass(): pass @@ -1368,8 +1443,8 @@ def test_pass(): @parametrize_families -def test_global_properties(testdir, xunit_family) -> None: - path = testdir.tmpdir.join("test_global_properties.xml") +def test_global_properties(pytester: Pytester, xunit_family) -> None: + path = pytester.path.joinpath("test_global_properties.xml") log = LogXML(str(path), None, family=xunit_family) class Report(BaseReport): @@ -1402,9 +1477,9 @@ class Report(BaseReport): assert actual == expected -def test_url_property(testdir) -> None: +def test_url_property(pytester: Pytester) -> None: test_url = "http://www.github.com/pytest-dev" - path = testdir.tmpdir.join("test_url_property.xml") + path = pytester.path.joinpath("test_url_property.xml") log = LogXML(str(path), None) class Report(BaseReport): @@ -1429,8 +1504,10 @@ class Report(BaseReport): @parametrize_families -def test_record_testsuite_property(testdir, run_and_parse, xunit_family): - testdir.makepyfile( +def test_record_testsuite_property( + pytester: Pytester, run_and_parse, xunit_family +) -> None: + pytester.makepyfile( """ def test_func1(record_testsuite_property): record_testsuite_property("stats", "all good") @@ -1449,27 +1526,27 @@ def test_func2(record_testsuite_property): p2_node.assert_attr(name="stats", value="10") -def test_record_testsuite_property_junit_disabled(testdir): - testdir.makepyfile( +def test_record_testsuite_property_junit_disabled(pytester: Pytester) -> None: + pytester.makepyfile( """ def test_func1(record_testsuite_property): record_testsuite_property("stats", "all good") """ ) - result = testdir.runpytest() + result = pytester.runpytest() assert result.ret == 0 @pytest.mark.parametrize("junit", [True, False]) -def test_record_testsuite_property_type_checking(testdir, junit): - testdir.makepyfile( +def test_record_testsuite_property_type_checking(pytester: Pytester, junit) -> None: + pytester.makepyfile( """ def test_func1(record_testsuite_property): record_testsuite_property(1, 2) """ ) args = ("--junitxml=tests.xml",) if junit else () - result = testdir.runpytest(*args) + result = pytester.runpytest(*args) assert result.ret == 1 result.stdout.fnmatch_lines( ["*TypeError: name parameter needs to be a string, but int given"] @@ -1478,9 +1555,11 @@ def test_func1(record_testsuite_property): @pytest.mark.parametrize("suite_name", ["my_suite", ""]) @parametrize_families -def test_set_suite_name(testdir, suite_name, run_and_parse, xunit_family): +def test_set_suite_name( + pytester: Pytester, suite_name, run_and_parse, xunit_family +) -> None: if suite_name: - testdir.makeini( + pytester.makeini( """ [pytest] junit_suite_name={suite_name} @@ -1492,7 +1571,7 @@ def test_set_suite_name(testdir, suite_name, run_and_parse, xunit_family): expected = suite_name else: expected = "pytest" - testdir.makepyfile( + pytester.makepyfile( """ import pytest @@ -1506,8 +1585,8 @@ def test_func(): node.assert_attr(name=expected) -def test_escaped_skipreason_issue3533(testdir, run_and_parse): - testdir.makepyfile( +def test_escaped_skipreason_issue3533(pytester: Pytester, run_and_parse) -> None: + pytester.makepyfile( """ import pytest @pytest.mark.skip(reason='1 <> 2') @@ -1524,9 +1603,9 @@ def test_skip(): @parametrize_families def test_logging_passing_tests_disabled_does_not_log_test_output( - testdir, run_and_parse, xunit_family -): - testdir.makeini( + pytester: Pytester, run_and_parse, xunit_family +) -> None: + pytester.makeini( """ [pytest] junit_log_passing_tests=False @@ -1536,7 +1615,7 @@ def test_logging_passing_tests_disabled_does_not_log_test_output( family=xunit_family ) ) - testdir.makepyfile( + pytester.makepyfile( """ import pytest import logging @@ -1558,9 +1637,9 @@ def test_func(): @parametrize_families @pytest.mark.parametrize("junit_logging", ["no", "system-out", "system-err"]) def test_logging_passing_tests_disabled_logs_output_for_failing_test_issue5430( - testdir, junit_logging, run_and_parse, xunit_family -): - testdir.makeini( + pytester: Pytester, junit_logging, run_and_parse, xunit_family +) -> None: + pytester.makeini( """ [pytest] junit_log_passing_tests=False @@ -1569,7 +1648,7 @@ def test_logging_passing_tests_disabled_logs_output_for_failing_test_issue5430( family=xunit_family ) ) - testdir.makepyfile( + pytester.makepyfile( """ import pytest import logging diff --git a/testing/test_link_resolve.py b/testing/test_link_resolve.py index 7eaf4124796..60a86ada36e 100644 --- a/testing/test_link_resolve.py +++ b/testing/test_link_resolve.py @@ -3,15 +3,14 @@ import sys import textwrap from contextlib import contextmanager +from pathlib import Path from string import ascii_lowercase -import py.path - -from _pytest import pytester +from _pytest.pytester import Pytester @contextmanager -def subst_path_windows(filename): +def subst_path_windows(filepath: Path): for c in ascii_lowercase[7:]: # Create a subst drive from H-Z. c += ":" if not os.path.exists(c): @@ -20,14 +19,14 @@ def subst_path_windows(filename): else: raise AssertionError("Unable to find suitable drive letter for subst.") - directory = filename.dirpath() - basename = filename.basename + directory = filepath.parent + basename = filepath.name args = ["subst", drive, str(directory)] subprocess.check_call(args) assert os.path.exists(drive) try: - filename = py.path.local(drive) / basename + filename = Path(drive, os.sep, basename) yield filename finally: args = ["subst", "/D", drive] @@ -35,9 +34,9 @@ def subst_path_windows(filename): @contextmanager -def subst_path_linux(filename): - directory = filename.dirpath() - basename = filename.basename +def subst_path_linux(filepath: Path): + directory = filepath.parent + basename = filepath.name target = directory / ".." / "sub2" os.symlink(str(directory), str(target), target_is_directory=True) @@ -49,11 +48,11 @@ def subst_path_linux(filename): pass -def test_link_resolve(testdir: pytester.Testdir) -> None: +def test_link_resolve(pytester: Pytester) -> None: """See: https://github.com/pytest-dev/pytest/issues/5965.""" - sub1 = testdir.mkpydir("sub1") - p = sub1.join("test_foo.py") - p.write( + sub1 = pytester.mkpydir("sub1") + p = sub1.joinpath("test_foo.py") + p.write_text( textwrap.dedent( """ import pytest @@ -68,7 +67,7 @@ def test_foo(): subst = subst_path_windows with subst(p) as subst_p: - result = testdir.runpytest(str(subst_p), "-v") + result = pytester.runpytest(str(subst_p), "-v") # i.e.: Make sure that the error is reported as a relative path, not as a # resolved path. # See: https://github.com/pytest-dev/pytest/issues/5965 diff --git a/testing/test_main.py b/testing/test_main.py index f45607abc30..2ed111895cd 100644 --- a/testing/test_main.py +++ b/testing/test_main.py @@ -10,7 +10,6 @@ from _pytest.main import resolve_collection_argument from _pytest.main import validate_basetemp from _pytest.pytester import Pytester -from _pytest.pytester import Testdir @pytest.mark.parametrize( @@ -21,9 +20,9 @@ pytest.param((False, SystemExit)), ), ) -def test_wrap_session_notify_exception(ret_exc, testdir): +def test_wrap_session_notify_exception(ret_exc, pytester: Pytester) -> None: returncode, exc = ret_exc - c1 = testdir.makeconftest( + c1 = pytester.makeconftest( """ import pytest @@ -38,7 +37,7 @@ def pytest_internalerror(excrepr, excinfo): returncode=returncode, exc=exc.__name__ ) ) - result = testdir.runpytest() + result = pytester.runpytest() if returncode: assert result.ret == returncode else: @@ -65,9 +64,9 @@ def pytest_internalerror(excrepr, excinfo): @pytest.mark.parametrize("returncode", (None, 42)) def test_wrap_session_exit_sessionfinish( - returncode: Optional[int], testdir: Testdir + returncode: Optional[int], pytester: Pytester ) -> None: - testdir.makeconftest( + pytester.makeconftest( """ import pytest def pytest_sessionfinish(): @@ -76,7 +75,7 @@ def pytest_sessionfinish(): returncode=returncode ) ) - result = testdir.runpytest() + result = pytester.runpytest() if returncode: assert result.ret == returncode else: @@ -101,8 +100,8 @@ def test_validate_basetemp_fails(tmp_path, basetemp, monkeypatch): validate_basetemp(basetemp) -def test_validate_basetemp_integration(testdir): - result = testdir.runpytest("--basetemp=.") +def test_validate_basetemp_integration(pytester: Pytester) -> None: + result = pytester.runpytest("--basetemp=.") result.stderr.fnmatch_lines("*basetemp must not be*") @@ -203,14 +202,14 @@ def test_absolute_paths_are_resolved_correctly(self, invocation_path: Path) -> N ) == (Path(os.path.abspath("src")), []) -def test_module_full_path_without_drive(testdir): +def test_module_full_path_without_drive(pytester: Pytester) -> None: """Collect and run test using full path except for the drive letter (#7628). Passing a full path without a drive letter would trigger a bug in py.path.local where it would keep the full path without the drive letter around, instead of resolving to the full path, resulting in fixtures node ids not matching against test node ids correctly. """ - testdir.makepyfile( + pytester.makepyfile( **{ "project/conftest.py": """ import pytest @@ -220,7 +219,7 @@ def fix(): return 1 } ) - testdir.makepyfile( + pytester.makepyfile( **{ "project/tests/dummy_test.py": """ def test(fix): @@ -228,12 +227,12 @@ def test(fix): """ } ) - fn = testdir.tmpdir.join("project/tests/dummy_test.py") - assert fn.isfile() + fn = pytester.path.joinpath("project/tests/dummy_test.py") + assert fn.is_file() drive, path = os.path.splitdrive(str(fn)) - result = testdir.runpytest(path, "-v") + result = pytester.runpytest(path, "-v") result.stdout.fnmatch_lines( [ os.path.join("project", "tests", "dummy_test.py") + "::test PASSED *", diff --git a/testing/test_parseopt.py b/testing/test_parseopt.py index a124009c401..c33337b67b3 100644 --- a/testing/test_parseopt.py +++ b/testing/test_parseopt.py @@ -315,7 +315,7 @@ def test_argcomplete(pytester: Pytester, monkeypatch: MonkeyPatch) -> None: shlex.quote(sys.executable) ) ) - # alternative would be extended Testdir.{run(),_run(),popen()} to be able + # alternative would be extended Pytester.{run(),_run(),popen()} to be able # to handle a keyword argument env that replaces os.environ in popen or # extends the copy, advantage: could not forget to restore monkeypatch.setenv("_ARGCOMPLETE", "1") diff --git a/testing/test_pytester.py b/testing/test_pytester.py index f2e8dd5a36a..a9ba1a046f1 100644 --- a/testing/test_pytester.py +++ b/testing/test_pytester.py @@ -2,26 +2,26 @@ import subprocess import sys import time +from pathlib import Path +from types import ModuleType from typing import List -import py.path - -import _pytest.pytester as pytester +import _pytest.pytester as pytester_mod import pytest from _pytest.config import ExitCode from _pytest.config import PytestPluginManager +from _pytest.monkeypatch import MonkeyPatch from _pytest.pytester import CwdSnapshot from _pytest.pytester import HookRecorder from _pytest.pytester import LineMatcher from _pytest.pytester import Pytester from _pytest.pytester import SysModulesSnapshot from _pytest.pytester import SysPathsSnapshot -from _pytest.pytester import Testdir -def test_make_hook_recorder(testdir) -> None: - item = testdir.getitem("def test_func(): pass") - recorder = testdir.make_hook_recorder(item.config.pluginmanager) +def test_make_hook_recorder(pytester: Pytester) -> None: + item = pytester.getitem("def test_func(): pass") + recorder = pytester.make_hook_recorder(item.config.pluginmanager) assert not recorder.getfailures() # (The silly condition is to fool mypy that the code below this is reachable) @@ -35,11 +35,11 @@ class rep: skipped = False when = "call" - recorder.hook.pytest_runtest_logreport(report=rep) + recorder.hook.pytest_runtest_logreport(report=rep) # type: ignore[attr-defined] failures = recorder.getfailures() - assert failures == [rep] + assert failures == [rep] # type: ignore[comparison-overlap] failures = recorder.getfailures() - assert failures == [rep] + assert failures == [rep] # type: ignore[comparison-overlap] class rep2: excinfo = None @@ -50,14 +50,14 @@ class rep2: rep2.passed = False rep2.skipped = True - recorder.hook.pytest_runtest_logreport(report=rep2) + recorder.hook.pytest_runtest_logreport(report=rep2) # type: ignore[attr-defined] - modcol = testdir.getmodulecol("") + modcol = pytester.getmodulecol("") rep3 = modcol.config.hook.pytest_make_collect_report(collector=modcol) rep3.passed = False rep3.failed = True rep3.skipped = False - recorder.hook.pytest_collectreport(report=rep3) + recorder.hook.pytest_collectreport(report=rep3) # type: ignore[attr-defined] passed, skipped, failed = recorder.listoutcomes() assert not passed and skipped and failed @@ -68,55 +68,55 @@ class rep2: assert numfailed == 1 assert len(recorder.getfailedcollections()) == 1 - recorder.unregister() + recorder.unregister() # type: ignore[attr-defined] recorder.clear() - recorder.hook.pytest_runtest_logreport(report=rep3) + recorder.hook.pytest_runtest_logreport(report=rep3) # type: ignore[attr-defined] pytest.raises(ValueError, recorder.getfailures) -def test_parseconfig(testdir) -> None: - config1 = testdir.parseconfig() - config2 = testdir.parseconfig() +def test_parseconfig(pytester: Pytester) -> None: + config1 = pytester.parseconfig() + config2 = pytester.parseconfig() assert config2 is not config1 -def test_testdir_runs_with_plugin(testdir) -> None: - testdir.makepyfile( +def test_pytester_runs_with_plugin(pytester: Pytester) -> None: + pytester.makepyfile( """ pytest_plugins = "pytester" - def test_hello(testdir): + def test_hello(pytester): assert 1 """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.assert_outcomes(passed=1) -def test_testdir_with_doctest(testdir): - """Check that testdir can be used within doctests. +def test_pytester_with_doctest(pytester: Pytester): + """Check that pytester can be used within doctests. It used to use `request.function`, which is `None` with doctests.""" - testdir.makepyfile( + pytester.makepyfile( **{ "sub/t-doctest.py": """ ''' >>> import os - >>> testdir = getfixture("testdir") - >>> str(testdir.makepyfile("content")).replace(os.sep, '/') + >>> pytester = getfixture("pytester") + >>> str(pytester.makepyfile("content")).replace(os.sep, '/') '.../basetemp/sub.t-doctest0/sub.py' ''' """, "sub/__init__.py": "", } ) - result = testdir.runpytest( + result = pytester.runpytest( "-p", "pytester", "--doctest-modules", "sub/t-doctest.py" ) assert result.ret == 0 -def test_runresult_assertion_on_xfail(testdir) -> None: - testdir.makepyfile( +def test_runresult_assertion_on_xfail(pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @@ -127,13 +127,13 @@ def test_potato(): assert False """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.assert_outcomes(xfailed=1) assert result.ret == 0 -def test_runresult_assertion_on_xpassed(testdir) -> None: - testdir.makepyfile( +def test_runresult_assertion_on_xpassed(pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @@ -144,13 +144,13 @@ def test_potato(): assert True """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.assert_outcomes(xpassed=1) assert result.ret == 0 -def test_xpassed_with_strict_is_considered_a_failure(testdir) -> None: - testdir.makepyfile( +def test_xpassed_with_strict_is_considered_a_failure(pytester: Pytester) -> None: + pytester.makepyfile( """ import pytest @@ -161,7 +161,7 @@ def test_potato(): assert True """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.assert_outcomes(failed=1) assert result.ret != 0 @@ -202,28 +202,28 @@ def test_hookrecorder_basic(holder) -> None: assert call._name == "pytest_xyz_noarg" -def test_makepyfile_unicode(testdir) -> None: - testdir.makepyfile(chr(0xFFFD)) +def test_makepyfile_unicode(pytester: Pytester) -> None: + pytester.makepyfile(chr(0xFFFD)) -def test_makepyfile_utf8(testdir) -> None: +def test_makepyfile_utf8(pytester: Pytester) -> None: """Ensure makepyfile accepts utf-8 bytes as input (#2738)""" utf8_contents = """ def setup_function(function): mixed_encoding = 'São Paulo' """.encode() - p = testdir.makepyfile(utf8_contents) - assert "mixed_encoding = 'São Paulo'".encode() in p.read("rb") + p = pytester.makepyfile(utf8_contents) + assert "mixed_encoding = 'São Paulo'".encode() in p.read_bytes() class TestInlineRunModulesCleanup: - def test_inline_run_test_module_not_cleaned_up(self, testdir) -> None: - test_mod = testdir.makepyfile("def test_foo(): assert True") - result = testdir.inline_run(str(test_mod)) + def test_inline_run_test_module_not_cleaned_up(self, pytester: Pytester) -> None: + test_mod = pytester.makepyfile("def test_foo(): assert True") + result = pytester.inline_run(str(test_mod)) assert result.ret == ExitCode.OK # rewrite module, now test should fail if module was re-imported - test_mod.write("def test_foo(): assert False") - result2 = testdir.inline_run(str(test_mod)) + test_mod.write_text("def test_foo(): assert False") + result2 = pytester.inline_run(str(test_mod)) assert result2.ret == ExitCode.TESTS_FAILED def spy_factory(self): @@ -243,20 +243,20 @@ def restore(self): return SysModulesSnapshotSpy def test_inline_run_taking_and_restoring_a_sys_modules_snapshot( - self, testdir, monkeypatch + self, pytester: Pytester, monkeypatch: MonkeyPatch ) -> None: spy_factory = self.spy_factory() - monkeypatch.setattr(pytester, "SysModulesSnapshot", spy_factory) - testdir.syspathinsert() + monkeypatch.setattr(pytester_mod, "SysModulesSnapshot", spy_factory) + pytester.syspathinsert() original = dict(sys.modules) - testdir.makepyfile(import1="# you son of a silly person") - testdir.makepyfile(import2="# my hovercraft is full of eels") - test_mod = testdir.makepyfile( + pytester.makepyfile(import1="# you son of a silly person") + pytester.makepyfile(import2="# my hovercraft is full of eels") + test_mod = pytester.makepyfile( """ import import1 def test_foo(): import import2""" ) - testdir.inline_run(str(test_mod)) + pytester.inline_run(str(test_mod)) assert len(spy_factory.instances) == 1 spy = spy_factory.instances[0] assert spy._spy_restore_count == 1 @@ -264,55 +264,57 @@ def test_foo(): import import2""" assert all(sys.modules[x] is original[x] for x in sys.modules) def test_inline_run_sys_modules_snapshot_restore_preserving_modules( - self, testdir, monkeypatch + self, pytester: Pytester, monkeypatch: MonkeyPatch ) -> None: spy_factory = self.spy_factory() - monkeypatch.setattr(pytester, "SysModulesSnapshot", spy_factory) - test_mod = testdir.makepyfile("def test_foo(): pass") - testdir.inline_run(str(test_mod)) + monkeypatch.setattr(pytester_mod, "SysModulesSnapshot", spy_factory) + test_mod = pytester.makepyfile("def test_foo(): pass") + pytester.inline_run(str(test_mod)) spy = spy_factory.instances[0] assert not spy._spy_preserve("black_knight") assert spy._spy_preserve("zope") assert spy._spy_preserve("zope.interface") assert spy._spy_preserve("zopelicious") - def test_external_test_module_imports_not_cleaned_up(self, testdir) -> None: - testdir.syspathinsert() - testdir.makepyfile(imported="data = 'you son of a silly person'") + def test_external_test_module_imports_not_cleaned_up( + self, pytester: Pytester + ) -> None: + pytester.syspathinsert() + pytester.makepyfile(imported="data = 'you son of a silly person'") import imported - test_mod = testdir.makepyfile( + test_mod = pytester.makepyfile( """ def test_foo(): import imported imported.data = 42""" ) - testdir.inline_run(str(test_mod)) + pytester.inline_run(str(test_mod)) assert imported.data == 42 -def test_assert_outcomes_after_pytest_error(testdir) -> None: - testdir.makepyfile("def test_foo(): assert True") +def test_assert_outcomes_after_pytest_error(pytester: Pytester) -> None: + pytester.makepyfile("def test_foo(): assert True") - result = testdir.runpytest("--unexpected-argument") + result = pytester.runpytest("--unexpected-argument") with pytest.raises(ValueError, match="Pytest terminal summary report not found"): result.assert_outcomes(passed=0) -def test_cwd_snapshot(testdir: Testdir) -> None: - tmpdir = testdir.tmpdir - foo = tmpdir.ensure("foo", dir=1) - bar = tmpdir.ensure("bar", dir=1) - foo.chdir() +def test_cwd_snapshot(pytester: Pytester) -> None: + foo = pytester.mkdir("foo") + bar = pytester.mkdir("bar") + os.chdir(foo) snapshot = CwdSnapshot() - bar.chdir() - assert py.path.local() == bar + os.chdir(bar) + assert Path().absolute() == bar snapshot.restore() - assert py.path.local() == foo + assert Path().absolute() == foo class TestSysModulesSnapshot: key = "my-test-module" + mod = ModuleType("something") def test_remove_added(self) -> None: original = dict(sys.modules) @@ -323,9 +325,9 @@ def test_remove_added(self) -> None: snapshot.restore() assert sys.modules == original - def test_add_removed(self, monkeypatch) -> None: + def test_add_removed(self, monkeypatch: MonkeyPatch) -> None: assert self.key not in sys.modules - monkeypatch.setitem(sys.modules, self.key, "something") + monkeypatch.setitem(sys.modules, self.key, self.mod) assert self.key in sys.modules original = dict(sys.modules) snapshot = SysModulesSnapshot() @@ -334,9 +336,9 @@ def test_add_removed(self, monkeypatch) -> None: snapshot.restore() assert sys.modules == original - def test_restore_reloaded(self, monkeypatch) -> None: + def test_restore_reloaded(self, monkeypatch: MonkeyPatch) -> None: assert self.key not in sys.modules - monkeypatch.setitem(sys.modules, self.key, "something") + monkeypatch.setitem(sys.modules, self.key, self.mod) assert self.key in sys.modules original = dict(sys.modules) snapshot = SysModulesSnapshot() @@ -344,11 +346,12 @@ def test_restore_reloaded(self, monkeypatch) -> None: snapshot.restore() assert sys.modules == original - def test_preserve_modules(self, monkeypatch) -> None: + def test_preserve_modules(self, monkeypatch: MonkeyPatch) -> None: key = [self.key + str(i) for i in range(3)] assert not any(k in sys.modules for k in key) for i, k in enumerate(key): - monkeypatch.setitem(sys.modules, k, "something" + str(i)) + mod = ModuleType("something" + str(i)) + monkeypatch.setitem(sys.modules, k, mod) original = dict(sys.modules) def preserve(name): @@ -361,7 +364,7 @@ def preserve(name): snapshot.restore() assert sys.modules == original - def test_preserve_container(self, monkeypatch) -> None: + def test_preserve_container(self, monkeypatch: MonkeyPatch) -> None: original = dict(sys.modules) assert self.key not in original replacement = dict(sys.modules) @@ -381,7 +384,7 @@ class TestSysPathsSnapshot: def path(n: int) -> str: return "my-dirty-little-secret-" + str(n) - def test_restore(self, monkeypatch, path_type) -> None: + def test_restore(self, monkeypatch: MonkeyPatch, path_type) -> None: other_path_type = self.other_path[path_type] for i in range(10): assert self.path(i) not in getattr(sys, path_type) @@ -404,7 +407,7 @@ def test_restore(self, monkeypatch, path_type) -> None: assert getattr(sys, path_type) == original assert getattr(sys, other_path_type) == original_other - def test_preserve_container(self, monkeypatch, path_type) -> None: + def test_preserve_container(self, monkeypatch: MonkeyPatch, path_type) -> None: other_path_type = self.other_path[path_type] original_data = list(getattr(sys, path_type)) original_other = getattr(sys, other_path_type) @@ -419,49 +422,47 @@ def test_preserve_container(self, monkeypatch, path_type) -> None: assert getattr(sys, other_path_type) == original_other_data -def test_testdir_subprocess(testdir) -> None: - testfile = testdir.makepyfile("def test_one(): pass") - assert testdir.runpytest_subprocess(testfile).ret == 0 +def test_pytester_subprocess(pytester: Pytester) -> None: + testfile = pytester.makepyfile("def test_one(): pass") + assert pytester.runpytest_subprocess(testfile).ret == 0 -def test_testdir_subprocess_via_runpytest_arg(testdir) -> None: - testfile = testdir.makepyfile( +def test_pytester_subprocess_via_runpytest_arg(pytester: Pytester) -> None: + testfile = pytester.makepyfile( """ - def test_testdir_subprocess(testdir): + def test_pytester_subprocess(pytester): import os - testfile = testdir.makepyfile( + testfile = pytester.makepyfile( \""" import os def test_one(): assert {} != os.getpid() \""".format(os.getpid()) ) - assert testdir.runpytest(testfile).ret == 0 + assert pytester.runpytest(testfile).ret == 0 """ ) - result = testdir.runpytest_subprocess( - "-p", "pytester", "--runpytest", "subprocess", testfile - ) + result = pytester.runpytest("-p", "pytester", "--runpytest", "subprocess", testfile) assert result.ret == 0 -def test_unicode_args(testdir) -> None: - result = testdir.runpytest("-k", "אבג") +def test_unicode_args(pytester: Pytester) -> None: + result = pytester.runpytest("-k", "אבג") assert result.ret == ExitCode.NO_TESTS_COLLECTED -def test_testdir_run_no_timeout(testdir) -> None: - testfile = testdir.makepyfile("def test_no_timeout(): pass") - assert testdir.runpytest_subprocess(testfile).ret == ExitCode.OK +def test_pytester_run_no_timeout(pytester: Pytester) -> None: + testfile = pytester.makepyfile("def test_no_timeout(): pass") + assert pytester.runpytest_subprocess(testfile).ret == ExitCode.OK -def test_testdir_run_with_timeout(testdir) -> None: - testfile = testdir.makepyfile("def test_no_timeout(): pass") +def test_pytester_run_with_timeout(pytester: Pytester) -> None: + testfile = pytester.makepyfile("def test_no_timeout(): pass") timeout = 120 start = time.time() - result = testdir.runpytest_subprocess(testfile, timeout=timeout) + result = pytester.runpytest_subprocess(testfile, timeout=timeout) end = time.time() duration = end - start @@ -469,16 +470,16 @@ def test_testdir_run_with_timeout(testdir) -> None: assert duration < timeout -def test_testdir_run_timeout_expires(testdir) -> None: - testfile = testdir.makepyfile( +def test_pytester_run_timeout_expires(pytester: Pytester) -> None: + testfile = pytester.makepyfile( """ import time def test_timeout(): time.sleep(10)""" ) - with pytest.raises(testdir.TimeoutExpired): - testdir.runpytest_subprocess(testfile, timeout=1) + with pytest.raises(pytester.TimeoutExpired): + pytester.runpytest_subprocess(testfile, timeout=1) def test_linematcher_with_nonlist() -> None: @@ -533,7 +534,7 @@ def test_linematcher_match_failure() -> None: ] -def test_linematcher_consecutive(): +def test_linematcher_consecutive() -> None: lm = LineMatcher(["1", "", "2"]) with pytest.raises(pytest.fail.Exception) as excinfo: lm.fnmatch_lines(["1", "2"], consecutive=True) @@ -554,7 +555,7 @@ def test_linematcher_consecutive(): @pytest.mark.parametrize("function", ["no_fnmatch_line", "no_re_match_line"]) -def test_linematcher_no_matching(function) -> None: +def test_linematcher_no_matching(function: str) -> None: if function == "no_fnmatch_line": good_pattern = "*.py OK*" bad_pattern = "*X.py OK*" @@ -615,7 +616,7 @@ def test_linematcher_string_api() -> None: assert str(lm) == "foo\nbar" -def test_pytester_addopts_before_testdir(request, monkeypatch) -> None: +def test_pytester_addopts_before_testdir(request, monkeypatch: MonkeyPatch) -> None: orig = os.environ.get("PYTEST_ADDOPTS", None) monkeypatch.setenv("PYTEST_ADDOPTS", "--orig-unused") testdir = request.getfixturevalue("testdir") @@ -626,9 +627,9 @@ def test_pytester_addopts_before_testdir(request, monkeypatch) -> None: assert os.environ.get("PYTEST_ADDOPTS") == orig -def test_run_stdin(testdir) -> None: - with pytest.raises(testdir.TimeoutExpired): - testdir.run( +def test_run_stdin(pytester: Pytester) -> None: + with pytest.raises(pytester.TimeoutExpired): + pytester.run( sys.executable, "-c", "import sys, time; time.sleep(1); print(sys.stdin.read())", @@ -636,8 +637,8 @@ def test_run_stdin(testdir) -> None: timeout=0.1, ) - with pytest.raises(testdir.TimeoutExpired): - result = testdir.run( + with pytest.raises(pytester.TimeoutExpired): + result = pytester.run( sys.executable, "-c", "import sys, time; time.sleep(1); print(sys.stdin.read())", @@ -645,7 +646,7 @@ def test_run_stdin(testdir) -> None: timeout=0.1, ) - result = testdir.run( + result = pytester.run( sys.executable, "-c", "import sys; print(sys.stdin.read())", @@ -656,8 +657,8 @@ def test_run_stdin(testdir) -> None: assert result.ret == 0 -def test_popen_stdin_pipe(testdir) -> None: - proc = testdir.popen( +def test_popen_stdin_pipe(pytester: Pytester) -> None: + proc = pytester.popen( [sys.executable, "-c", "import sys; print(sys.stdin.read())"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, @@ -670,8 +671,8 @@ def test_popen_stdin_pipe(testdir) -> None: assert proc.returncode == 0 -def test_popen_stdin_bytes(testdir) -> None: - proc = testdir.popen( +def test_popen_stdin_bytes(pytester: Pytester) -> None: + proc = pytester.popen( [sys.executable, "-c", "import sys; print(sys.stdin.read())"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, @@ -683,18 +684,18 @@ def test_popen_stdin_bytes(testdir) -> None: assert proc.returncode == 0 -def test_popen_default_stdin_stderr_and_stdin_None(testdir) -> None: +def test_popen_default_stdin_stderr_and_stdin_None(pytester: Pytester) -> None: # stdout, stderr default to pipes, # stdin can be None to not close the pipe, avoiding # "ValueError: flush of closed file" with `communicate()`. # # Wraps the test to make it not hang when run with "-s". - p1 = testdir.makepyfile( + p1 = pytester.makepyfile( ''' import sys - def test_inner(testdir): - p1 = testdir.makepyfile( + def test_inner(pytester): + p1 = pytester.makepyfile( """ import sys print(sys.stdin.read()) # empty @@ -702,14 +703,14 @@ def test_inner(testdir): sys.stderr.write('stderr') """ ) - proc = testdir.popen([sys.executable, str(p1)], stdin=None) + proc = pytester.popen([sys.executable, str(p1)], stdin=None) stdout, stderr = proc.communicate(b"ignored") assert stdout.splitlines() == [b"", b"stdout"] assert stderr.splitlines() == [b"stderr"] assert proc.returncode == 0 ''' ) - result = testdir.runpytest("-p", "pytester", str(p1)) + result = pytester.runpytest("-p", "pytester", str(p1)) assert result.ret == 0 @@ -740,22 +741,22 @@ def test_run_result_repr() -> None: errlines = ["some", "nasty", "errors", "happened"] # known exit code - r = pytester.RunResult(1, outlines, errlines, duration=0.5) + r = pytester_mod.RunResult(1, outlines, errlines, duration=0.5) assert ( repr(r) == "" ) # unknown exit code: just the number - r = pytester.RunResult(99, outlines, errlines, duration=0.5) + r = pytester_mod.RunResult(99, outlines, errlines, duration=0.5) assert ( repr(r) == "" ) -def test_testdir_outcomes_with_multiple_errors(testdir): - p1 = testdir.makepyfile( +def test_pytester_outcomes_with_multiple_errors(pytester: Pytester) -> None: + p1 = pytester.makepyfile( """ import pytest @@ -770,7 +771,7 @@ def test_error2(bad_fixture): pass """ ) - result = testdir.runpytest(str(p1)) + result = pytester.runpytest(str(p1)) result.assert_outcomes(errors=2) assert result.parseoutcomes() == {"errors": 2} @@ -784,7 +785,7 @@ def test_parse_summary_line_always_plural(): "======= 1 failed, 1 passed, 1 warning, 1 error in 0.13s ====", "done.", ] - assert pytester.RunResult.parse_summary_nouns(lines) == { + assert pytester_mod.RunResult.parse_summary_nouns(lines) == { "errors": 1, "failed": 1, "passed": 1, @@ -797,7 +798,7 @@ def test_parse_summary_line_always_plural(): "======= 1 failed, 1 passed, 2 warnings, 2 errors in 0.13s ====", "done.", ] - assert pytester.RunResult.parse_summary_nouns(lines) == { + assert pytester_mod.RunResult.parse_summary_nouns(lines) == { "errors": 2, "failed": 1, "passed": 1, @@ -805,10 +806,10 @@ def test_parse_summary_line_always_plural(): } -def test_makefile_joins_absolute_path(testdir: Testdir) -> None: - absfile = testdir.tmpdir / "absfile" - p1 = testdir.makepyfile(**{str(absfile): ""}) - assert str(p1) == str(testdir.tmpdir / "absfile.py") +def test_makefile_joins_absolute_path(pytester: Pytester) -> None: + absfile = pytester.path / "absfile" + p1 = pytester.makepyfile(**{str(absfile): ""}) + assert str(p1) == str(pytester.path / "absfile.py") def test_testtmproot(testdir): diff --git a/testing/test_unittest.py b/testing/test_unittest.py index 8b00cb826ac..feee09286c2 100644 --- a/testing/test_unittest.py +++ b/testing/test_unittest.py @@ -4,11 +4,12 @@ import pytest from _pytest.config import ExitCode -from _pytest.pytester import Testdir +from _pytest.monkeypatch import MonkeyPatch +from _pytest.pytester import Pytester -def test_simple_unittest(testdir): - testpath = testdir.makepyfile( +def test_simple_unittest(pytester: Pytester) -> None: + testpath = pytester.makepyfile( """ import unittest class MyTestCase(unittest.TestCase): @@ -18,13 +19,13 @@ def test_failing(self): self.assertEqual('foo', 'bar') """ ) - reprec = testdir.inline_run(testpath) + reprec = pytester.inline_run(testpath) assert reprec.matchreport("testpassing").passed assert reprec.matchreport("test_failing").failed -def test_runTest_method(testdir): - testdir.makepyfile( +def test_runTest_method(pytester: Pytester) -> None: + pytester.makepyfile( """ import unittest class MyTestCaseWithRunTest(unittest.TestCase): @@ -37,7 +38,7 @@ def test_something(self): pass """ ) - result = testdir.runpytest("-v") + result = pytester.runpytest("-v") result.stdout.fnmatch_lines( """ *MyTestCaseWithRunTest::runTest* @@ -47,8 +48,8 @@ def test_something(self): ) -def test_isclasscheck_issue53(testdir): - testpath = testdir.makepyfile( +def test_isclasscheck_issue53(pytester: Pytester) -> None: + testpath = pytester.makepyfile( """ import unittest class _E(object): @@ -57,12 +58,12 @@ def __getattr__(self, tag): E = _E() """ ) - result = testdir.runpytest(testpath) + result = pytester.runpytest(testpath) assert result.ret == ExitCode.NO_TESTS_COLLECTED -def test_setup(testdir): - testpath = testdir.makepyfile( +def test_setup(pytester: Pytester) -> None: + testpath = pytester.makepyfile( """ import unittest class MyTestCase(unittest.TestCase): @@ -78,14 +79,14 @@ def teardown_method(self, method): """ ) - reprec = testdir.inline_run("-s", testpath) + reprec = pytester.inline_run("-s", testpath) assert reprec.matchreport("test_both", when="call").passed rep = reprec.matchreport("test_both", when="teardown") assert rep.failed and "42" in str(rep.longrepr) -def test_setUpModule(testdir): - testpath = testdir.makepyfile( +def test_setUpModule(pytester: Pytester) -> None: + testpath = pytester.makepyfile( """ values = [] @@ -102,12 +103,12 @@ def test_world(): assert values == [1] """ ) - result = testdir.runpytest(testpath) + result = pytester.runpytest(testpath) result.stdout.fnmatch_lines(["*2 passed*"]) -def test_setUpModule_failing_no_teardown(testdir): - testpath = testdir.makepyfile( +def test_setUpModule_failing_no_teardown(pytester: Pytester) -> None: + testpath = pytester.makepyfile( """ values = [] @@ -121,14 +122,14 @@ def test_hello(): pass """ ) - reprec = testdir.inline_run(testpath) + reprec = pytester.inline_run(testpath) reprec.assertoutcome(passed=0, failed=1) call = reprec.getcalls("pytest_runtest_setup")[0] assert not call.item.module.values -def test_new_instances(testdir): - testpath = testdir.makepyfile( +def test_new_instances(pytester: Pytester) -> None: + testpath = pytester.makepyfile( """ import unittest class MyTestCase(unittest.TestCase): @@ -138,13 +139,13 @@ def test_func2(self): assert not hasattr(self, 'x') """ ) - reprec = testdir.inline_run(testpath) + reprec = pytester.inline_run(testpath) reprec.assertoutcome(passed=2) -def test_function_item_obj_is_instance(testdir): +def test_function_item_obj_is_instance(pytester: Pytester) -> None: """item.obj should be a bound method on unittest.TestCase function items (#5390).""" - testdir.makeconftest( + pytester.makeconftest( """ def pytest_runtest_makereport(item, call): if call.when == 'call': @@ -152,7 +153,7 @@ def pytest_runtest_makereport(item, call): assert isinstance(item.obj.__self__, class_) """ ) - testdir.makepyfile( + pytester.makepyfile( """ import unittest @@ -161,12 +162,12 @@ def test_foo(self): pass """ ) - result = testdir.runpytest_inprocess() + result = pytester.runpytest_inprocess() result.stdout.fnmatch_lines(["* 1 passed in*"]) -def test_teardown(testdir): - testpath = testdir.makepyfile( +def test_teardown(pytester: Pytester) -> None: + testpath = pytester.makepyfile( """ import unittest class MyTestCase(unittest.TestCase): @@ -180,14 +181,14 @@ def test_check(self): self.assertEqual(MyTestCase.values, [None]) """ ) - reprec = testdir.inline_run(testpath) + reprec = pytester.inline_run(testpath) passed, skipped, failed = reprec.countoutcomes() assert failed == 0, failed assert passed == 2 assert passed + skipped + failed == 2 -def test_teardown_issue1649(testdir): +def test_teardown_issue1649(pytester: Pytester) -> None: """ Are TestCase objects cleaned up? Often unittest TestCase objects set attributes that are large and expensive during setUp. @@ -195,7 +196,7 @@ def test_teardown_issue1649(testdir): The TestCase will not be cleaned up if the test fails, because it would then exist in the stackframe. """ - testpath = testdir.makepyfile( + testpath = pytester.makepyfile( """ import unittest class TestCaseObjectsShouldBeCleanedUp(unittest.TestCase): @@ -206,14 +207,14 @@ def test_demo(self): """ ) - testdir.inline_run("-s", testpath) + pytester.inline_run("-s", testpath) gc.collect() for obj in gc.get_objects(): assert type(obj).__name__ != "TestCaseObjectsShouldBeCleanedUp" -def test_unittest_skip_issue148(testdir): - testpath = testdir.makepyfile( +def test_unittest_skip_issue148(pytester: Pytester) -> None: + testpath = pytester.makepyfile( """ import unittest @@ -229,12 +230,12 @@ def tearDownClass(self): xxx """ ) - reprec = testdir.inline_run(testpath) + reprec = pytester.inline_run(testpath) reprec.assertoutcome(skipped=1) -def test_method_and_teardown_failing_reporting(testdir): - testdir.makepyfile( +def test_method_and_teardown_failing_reporting(pytester: Pytester) -> None: + pytester.makepyfile( """ import unittest class TC(unittest.TestCase): @@ -244,7 +245,7 @@ def test_method(self): assert False, "down2" """ ) - result = testdir.runpytest("-s") + result = pytester.runpytest("-s") assert result.ret == 1 result.stdout.fnmatch_lines( [ @@ -257,8 +258,8 @@ def test_method(self): ) -def test_setup_failure_is_shown(testdir): - testdir.makepyfile( +def test_setup_failure_is_shown(pytester: Pytester) -> None: + pytester.makepyfile( """ import unittest import pytest @@ -270,14 +271,14 @@ def test_method(self): xyz """ ) - result = testdir.runpytest("-s") + result = pytester.runpytest("-s") assert result.ret == 1 result.stdout.fnmatch_lines(["*setUp*", "*assert 0*down1*", "*1 failed*"]) result.stdout.no_fnmatch_line("*never42*") -def test_setup_setUpClass(testdir): - testpath = testdir.makepyfile( +def test_setup_setUpClass(pytester: Pytester) -> None: + testpath = pytester.makepyfile( """ import unittest import pytest @@ -297,12 +298,12 @@ def test_teareddown(): assert MyTestCase.x == 0 """ ) - reprec = testdir.inline_run(testpath) + reprec = pytester.inline_run(testpath) reprec.assertoutcome(passed=3) -def test_setup_class(testdir): - testpath = testdir.makepyfile( +def test_setup_class(pytester: Pytester) -> None: + testpath = pytester.makepyfile( """ import unittest import pytest @@ -320,13 +321,13 @@ def test_teareddown(): assert MyTestCase.x == 0 """ ) - reprec = testdir.inline_run(testpath) + reprec = pytester.inline_run(testpath) reprec.assertoutcome(passed=3) @pytest.mark.parametrize("type", ["Error", "Failure"]) -def test_testcase_adderrorandfailure_defers(testdir, type): - testdir.makepyfile( +def test_testcase_adderrorandfailure_defers(pytester: Pytester, type: str) -> None: + pytester.makepyfile( """ from unittest import TestCase import pytest @@ -344,13 +345,13 @@ def test_hello(self): """ % (type, type) ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.no_fnmatch_line("*should not raise*") @pytest.mark.parametrize("type", ["Error", "Failure"]) -def test_testcase_custom_exception_info(testdir, type): - testdir.makepyfile( +def test_testcase_custom_exception_info(pytester: Pytester, type: str) -> None: + pytester.makepyfile( """ from unittest import TestCase import py, pytest @@ -375,7 +376,7 @@ def test_hello(self): """ % locals() ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines( [ "NOTE: Incompatible Exception Representation*", @@ -385,8 +386,10 @@ def test_hello(self): ) -def test_testcase_totally_incompatible_exception_info(testdir): - (item,) = testdir.getitems( +def test_testcase_totally_incompatible_exception_info(pytester: Pytester) -> None: + import _pytest.unittest + + (item,) = pytester.getitems( """ from unittest import TestCase class MyTestCase(TestCase): @@ -394,13 +397,15 @@ def test_hello(self): pass """ ) - item.addError(None, 42) - excinfo = item._excinfo.pop(0) - assert "ERROR: Unknown Incompatible" in str(excinfo.getrepr()) + assert isinstance(item, _pytest.unittest.TestCaseFunction) + item.addError(None, 42) # type: ignore[arg-type] + excinfo = item._excinfo + assert excinfo is not None + assert "ERROR: Unknown Incompatible" in str(excinfo.pop(0).getrepr()) -def test_module_level_pytestmark(testdir): - testpath = testdir.makepyfile( +def test_module_level_pytestmark(pytester: Pytester) -> None: + testpath = pytester.makepyfile( """ import unittest import pytest @@ -410,7 +415,7 @@ def test_func1(self): assert 0 """ ) - reprec = testdir.inline_run(testpath, "-s") + reprec = pytester.inline_run(testpath, "-s") reprec.assertoutcome(skipped=1) @@ -421,8 +426,8 @@ def setup_class(cls): # https://twistedmatrix.com/trac/ticket/9227 cls.ignore_unclosed_socket_warning = ("-W", "always") - def test_trial_testcase_runtest_not_collected(self, testdir): - testdir.makepyfile( + def test_trial_testcase_runtest_not_collected(self, pytester: Pytester) -> None: + pytester.makepyfile( """ from twisted.trial.unittest import TestCase @@ -431,9 +436,9 @@ def test_hello(self): pass """ ) - reprec = testdir.inline_run(*self.ignore_unclosed_socket_warning) + reprec = pytester.inline_run(*self.ignore_unclosed_socket_warning) reprec.assertoutcome(passed=1) - testdir.makepyfile( + pytester.makepyfile( """ from twisted.trial.unittest import TestCase @@ -442,11 +447,11 @@ def runTest(self): pass """ ) - reprec = testdir.inline_run(*self.ignore_unclosed_socket_warning) + reprec = pytester.inline_run(*self.ignore_unclosed_socket_warning) reprec.assertoutcome(passed=1) - def test_trial_exceptions_with_skips(self, testdir): - testdir.makepyfile( + def test_trial_exceptions_with_skips(self, pytester: Pytester) -> None: + pytester.makepyfile( """ from twisted.trial import unittest import pytest @@ -480,7 +485,7 @@ def test_method(self): pass """ ) - result = testdir.runpytest("-rxs", *self.ignore_unclosed_socket_warning) + result = pytester.runpytest("-rxs", *self.ignore_unclosed_socket_warning) result.stdout.fnmatch_lines_random( [ "*XFAIL*test_trial_todo*", @@ -495,8 +500,8 @@ def test_method(self): ) assert result.ret == 1 - def test_trial_error(self, testdir): - testdir.makepyfile( + def test_trial_error(self, pytester: Pytester) -> None: + pytester.makepyfile( """ from twisted.trial.unittest import TestCase from twisted.internet.defer import Deferred @@ -533,7 +538,7 @@ def f(_): # will crash both at test time and at teardown """ ) - result = testdir.runpytest("-vv", "-oconsole_output_style=classic") + result = pytester.runpytest("-vv", "-oconsole_output_style=classic") result.stdout.fnmatch_lines( [ "test_trial_error.py::TC::test_four FAILED", @@ -557,8 +562,8 @@ def f(_): ] ) - def test_trial_pdb(self, testdir): - p = testdir.makepyfile( + def test_trial_pdb(self, pytester: Pytester) -> None: + p = pytester.makepyfile( """ from twisted.trial import unittest import pytest @@ -567,12 +572,12 @@ def test_hello(self): assert 0, "hellopdb" """ ) - child = testdir.spawn_pytest(p) + child = pytester.spawn_pytest(str(p)) child.expect("hellopdb") child.sendeof() - def test_trial_testcase_skip_property(self, testdir): - testpath = testdir.makepyfile( + def test_trial_testcase_skip_property(self, pytester: Pytester) -> None: + testpath = pytester.makepyfile( """ from twisted.trial import unittest class MyTestCase(unittest.TestCase): @@ -581,11 +586,11 @@ def test_func(self): pass """ ) - reprec = testdir.inline_run(testpath, "-s") + reprec = pytester.inline_run(testpath, "-s") reprec.assertoutcome(skipped=1) - def test_trial_testfunction_skip_property(self, testdir): - testpath = testdir.makepyfile( + def test_trial_testfunction_skip_property(self, pytester: Pytester) -> None: + testpath = pytester.makepyfile( """ from twisted.trial import unittest class MyTestCase(unittest.TestCase): @@ -594,11 +599,11 @@ def test_func(self): test_func.skip = 'dont run' """ ) - reprec = testdir.inline_run(testpath, "-s") + reprec = pytester.inline_run(testpath, "-s") reprec.assertoutcome(skipped=1) - def test_trial_testcase_todo_property(self, testdir): - testpath = testdir.makepyfile( + def test_trial_testcase_todo_property(self, pytester: Pytester) -> None: + testpath = pytester.makepyfile( """ from twisted.trial import unittest class MyTestCase(unittest.TestCase): @@ -607,11 +612,11 @@ def test_func(self): assert 0 """ ) - reprec = testdir.inline_run(testpath, "-s") + reprec = pytester.inline_run(testpath, "-s") reprec.assertoutcome(skipped=1) - def test_trial_testfunction_todo_property(self, testdir): - testpath = testdir.makepyfile( + def test_trial_testfunction_todo_property(self, pytester: Pytester) -> None: + testpath = pytester.makepyfile( """ from twisted.trial import unittest class MyTestCase(unittest.TestCase): @@ -620,15 +625,15 @@ def test_func(self): test_func.todo = 'dont run' """ ) - reprec = testdir.inline_run( + reprec = pytester.inline_run( testpath, "-s", *self.ignore_unclosed_socket_warning ) reprec.assertoutcome(skipped=1) -def test_djangolike_testcase(testdir): +def test_djangolike_testcase(pytester: Pytester) -> None: # contributed from Morten Breekevold - testdir.makepyfile( + pytester.makepyfile( """ from unittest import TestCase, main @@ -671,7 +676,7 @@ def _post_teardown(self): print("_post_teardown()") """ ) - result = testdir.runpytest("-s") + result = pytester.runpytest("-s") assert result.ret == 0 result.stdout.fnmatch_lines( [ @@ -684,8 +689,8 @@ def _post_teardown(self): ) -def test_unittest_not_shown_in_traceback(testdir): - testdir.makepyfile( +def test_unittest_not_shown_in_traceback(pytester: Pytester) -> None: + pytester.makepyfile( """ import unittest class t(unittest.TestCase): @@ -694,12 +699,12 @@ def test_hello(self): self.assertEqual(x, 4) """ ) - res = testdir.runpytest() + res = pytester.runpytest() res.stdout.no_fnmatch_line("*failUnlessEqual*") -def test_unorderable_types(testdir): - testdir.makepyfile( +def test_unorderable_types(pytester: Pytester) -> None: + pytester.makepyfile( """ import unittest class TestJoinEmpty(unittest.TestCase): @@ -713,13 +718,13 @@ class Test(unittest.TestCase): TestFoo = make_test() """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.no_fnmatch_line("*TypeError*") assert result.ret == ExitCode.NO_TESTS_COLLECTED -def test_unittest_typerror_traceback(testdir): - testdir.makepyfile( +def test_unittest_typerror_traceback(pytester: Pytester) -> None: + pytester.makepyfile( """ import unittest class TestJoinEmpty(unittest.TestCase): @@ -727,14 +732,16 @@ def test_hello(self, arg1): pass """ ) - result = testdir.runpytest() + result = pytester.runpytest() assert "TypeError" in result.stdout.str() assert result.ret == 1 @pytest.mark.parametrize("runner", ["pytest", "unittest"]) -def test_unittest_expected_failure_for_failing_test_is_xfail(testdir, runner): - script = testdir.makepyfile( +def test_unittest_expected_failure_for_failing_test_is_xfail( + pytester: Pytester, runner +) -> None: + script = pytester.makepyfile( """ import unittest class MyTestCase(unittest.TestCase): @@ -746,19 +753,21 @@ def test_failing_test_is_xfail(self): """ ) if runner == "pytest": - result = testdir.runpytest("-rxX") + result = pytester.runpytest("-rxX") result.stdout.fnmatch_lines( ["*XFAIL*MyTestCase*test_failing_test_is_xfail*", "*1 xfailed*"] ) else: - result = testdir.runpython(script) + result = pytester.runpython(script) result.stderr.fnmatch_lines(["*1 test in*", "*OK*(expected failures=1)*"]) assert result.ret == 0 @pytest.mark.parametrize("runner", ["pytest", "unittest"]) -def test_unittest_expected_failure_for_passing_test_is_fail(testdir, runner): - script = testdir.makepyfile( +def test_unittest_expected_failure_for_passing_test_is_fail( + pytester: Pytester, runner +) -> None: + script = pytester.makepyfile( """ import unittest class MyTestCase(unittest.TestCase): @@ -771,20 +780,20 @@ def test_passing_test_is_fail(self): ) if runner == "pytest": - result = testdir.runpytest("-rxX") + result = pytester.runpytest("-rxX") result.stdout.fnmatch_lines( ["*MyTestCase*test_passing_test_is_fail*", "*1 failed*"] ) else: - result = testdir.runpython(script) + result = pytester.runpython(script) result.stderr.fnmatch_lines(["*1 test in*", "*(unexpected successes=1)*"]) assert result.ret == 1 @pytest.mark.parametrize("stmt", ["return", "yield"]) -def test_unittest_setup_interaction(testdir: Testdir, stmt: str) -> None: - testdir.makepyfile( +def test_unittest_setup_interaction(pytester: Pytester, stmt: str) -> None: + pytester.makepyfile( """ import unittest import pytest @@ -811,12 +820,12 @@ def test_classattr(self): stmt=stmt ) ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["*3 passed*"]) -def test_non_unittest_no_setupclass_support(testdir): - testpath = testdir.makepyfile( +def test_non_unittest_no_setupclass_support(pytester: Pytester) -> None: + testpath = pytester.makepyfile( """ class TestFoo(object): x = 0 @@ -837,12 +846,12 @@ def test_not_teareddown(): """ ) - reprec = testdir.inline_run(testpath) + reprec = pytester.inline_run(testpath) reprec.assertoutcome(passed=2) -def test_no_teardown_if_setupclass_failed(testdir): - testpath = testdir.makepyfile( +def test_no_teardown_if_setupclass_failed(pytester: Pytester) -> None: + testpath = pytester.makepyfile( """ import unittest @@ -865,13 +874,13 @@ def test_notTornDown(): assert MyTestCase.x == 1 """ ) - reprec = testdir.inline_run(testpath) + reprec = pytester.inline_run(testpath) reprec.assertoutcome(passed=1, failed=1) -def test_cleanup_functions(testdir): +def test_cleanup_functions(pytester: Pytester) -> None: """Ensure functions added with addCleanup are always called after each test ends (#6947)""" - testdir.makepyfile( + pytester.makepyfile( """ import unittest @@ -890,7 +899,7 @@ def test_func_3_check_cleanups(self): assert cleanups == ["test_func_1", "test_func_2"] """ ) - result = testdir.runpytest("-v") + result = pytester.runpytest("-v") result.stdout.fnmatch_lines( [ "*::test_func_1 PASSED *", @@ -900,8 +909,8 @@ def test_func_3_check_cleanups(self): ) -def test_issue333_result_clearing(testdir): - testdir.makeconftest( +def test_issue333_result_clearing(pytester: Pytester) -> None: + pytester.makeconftest( """ import pytest @pytest.hookimpl(hookwrapper=True) @@ -910,7 +919,7 @@ def pytest_runtest_call(item): assert 0 """ ) - testdir.makepyfile( + pytester.makepyfile( """ import unittest class TestIt(unittest.TestCase): @@ -919,12 +928,12 @@ def test_func(self): """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(failed=1) -def test_unittest_raise_skip_issue748(testdir): - testdir.makepyfile( +def test_unittest_raise_skip_issue748(pytester: Pytester) -> None: + pytester.makepyfile( test_foo=""" import unittest @@ -933,7 +942,7 @@ def test_one(self): raise unittest.SkipTest('skipping due to reasons') """ ) - result = testdir.runpytest("-v", "-rs") + result = pytester.runpytest("-v", "-rs") result.stdout.fnmatch_lines( """ *SKIP*[1]*test_foo.py*skipping due to reasons* @@ -942,8 +951,8 @@ def test_one(self): ) -def test_unittest_skip_issue1169(testdir): - testdir.makepyfile( +def test_unittest_skip_issue1169(pytester: Pytester) -> None: + pytester.makepyfile( test_foo=""" import unittest @@ -953,7 +962,7 @@ def test_skip(self): self.fail() """ ) - result = testdir.runpytest("-v", "-rs") + result = pytester.runpytest("-v", "-rs") result.stdout.fnmatch_lines( """ *SKIP*[1]*skipping due to reasons* @@ -962,8 +971,8 @@ def test_skip(self): ) -def test_class_method_containing_test_issue1558(testdir): - testdir.makepyfile( +def test_class_method_containing_test_issue1558(pytester: Pytester) -> None: + pytester.makepyfile( test_foo=""" import unittest @@ -975,16 +984,16 @@ def test_should_not_run(self): test_should_not_run.__test__ = False """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(passed=1) @pytest.mark.parametrize("base", ["builtins.object", "unittest.TestCase"]) -def test_usefixtures_marker_on_unittest(base, testdir): +def test_usefixtures_marker_on_unittest(base, pytester: Pytester) -> None: """#3498""" module = base.rsplit(".", 1)[0] pytest.importorskip(module) - testdir.makepyfile( + pytester.makepyfile( conftest=""" import pytest @@ -1013,7 +1022,7 @@ def pytest_collection_modifyitems(items): """ ) - testdir.makepyfile( + pytester.makepyfile( """ import pytest import {module} @@ -1038,16 +1047,16 @@ def test_two(self): ) ) - result = testdir.runpytest("-s") + result = pytester.runpytest("-s") result.assert_outcomes(passed=2) -def test_testcase_handles_init_exceptions(testdir): +def test_testcase_handles_init_exceptions(pytester: Pytester) -> None: """ Regression test to make sure exceptions in the __init__ method are bubbled up correctly. See https://github.com/pytest-dev/pytest/issues/3788 """ - testdir.makepyfile( + pytester.makepyfile( """ from unittest import TestCase import pytest @@ -1058,14 +1067,14 @@ def test_hello(self): pass """ ) - result = testdir.runpytest() + result = pytester.runpytest() assert "should raise this exception" in result.stdout.str() result.stdout.no_fnmatch_line("*ERROR at teardown of MyTestCase.test_hello*") -def test_error_message_with_parametrized_fixtures(testdir): - testdir.copy_example("unittest/test_parametrized_fixture_error_message.py") - result = testdir.runpytest() +def test_error_message_with_parametrized_fixtures(pytester: Pytester) -> None: + pytester.copy_example("unittest/test_parametrized_fixture_error_message.py") + result = pytester.runpytest() result.stdout.fnmatch_lines( [ "*test_two does not support fixtures*", @@ -1083,15 +1092,17 @@ def test_error_message_with_parametrized_fixtures(testdir): ("test_setup_skip_module.py", "1 error"), ], ) -def test_setup_inheritance_skipping(testdir, test_name, expected_outcome): +def test_setup_inheritance_skipping( + pytester: Pytester, test_name, expected_outcome +) -> None: """Issue #4700""" - testdir.copy_example(f"unittest/{test_name}") - result = testdir.runpytest() + pytester.copy_example(f"unittest/{test_name}") + result = pytester.runpytest() result.stdout.fnmatch_lines([f"* {expected_outcome} in *"]) -def test_BdbQuit(testdir): - testdir.makepyfile( +def test_BdbQuit(pytester: Pytester) -> None: + pytester.makepyfile( test_foo=""" import unittest @@ -1104,12 +1115,12 @@ def test_should_not_run(self): pass """ ) - reprec = testdir.inline_run() + reprec = pytester.inline_run() reprec.assertoutcome(failed=1, passed=1) -def test_exit_outcome(testdir): - testdir.makepyfile( +def test_exit_outcome(pytester: Pytester) -> None: + pytester.makepyfile( test_foo=""" import pytest import unittest @@ -1122,11 +1133,11 @@ def test_should_not_run(self): pass """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["*Exit: pytest_exit called*", "*= no tests ran in *"]) -def test_trace(testdir, monkeypatch): +def test_trace(pytester: Pytester, monkeypatch: MonkeyPatch) -> None: calls = [] def check_call(*args, **kwargs): @@ -1141,7 +1152,7 @@ def runcall(*args, **kwargs): monkeypatch.setattr("_pytest.debugging.pytestPDB._init_pdb", check_call) - p1 = testdir.makepyfile( + p1 = pytester.makepyfile( """ import unittest @@ -1150,12 +1161,12 @@ def test(self): self.assertEqual('foo', 'foo') """ ) - result = testdir.runpytest("--trace", str(p1)) + result = pytester.runpytest("--trace", str(p1)) assert len(calls) == 2 assert result.ret == 0 -def test_pdb_teardown_called(testdir, monkeypatch) -> None: +def test_pdb_teardown_called(pytester: Pytester, monkeypatch: MonkeyPatch) -> None: """Ensure tearDown() is always called when --pdb is given in the command-line. We delay the normal tearDown() calls when --pdb is given, so this ensures we are calling @@ -1166,7 +1177,7 @@ def test_pdb_teardown_called(testdir, monkeypatch) -> None: pytest, "test_pdb_teardown_called_teardowns", teardowns, raising=False ) - testdir.makepyfile( + pytester.makepyfile( """ import unittest import pytest @@ -1182,7 +1193,7 @@ def test_2(self): pass """ ) - result = testdir.runpytest_inprocess("--pdb") + result = pytester.runpytest_inprocess("--pdb") result.stdout.fnmatch_lines("* 2 passed in *") assert teardowns == [ "test_pdb_teardown_called.MyTestCase.test_1", @@ -1191,12 +1202,14 @@ def test_2(self): @pytest.mark.parametrize("mark", ["@unittest.skip", "@pytest.mark.skip"]) -def test_pdb_teardown_skipped(testdir, monkeypatch, mark: str) -> None: +def test_pdb_teardown_skipped( + pytester: Pytester, monkeypatch: MonkeyPatch, mark: str +) -> None: """With --pdb, setUp and tearDown should not be called for skipped tests.""" tracked: List[str] = [] monkeypatch.setattr(pytest, "test_pdb_teardown_skipped", tracked, raising=False) - testdir.makepyfile( + pytester.makepyfile( """ import unittest import pytest @@ -1217,29 +1230,29 @@ def test_1(self): mark=mark ) ) - result = testdir.runpytest_inprocess("--pdb") + result = pytester.runpytest_inprocess("--pdb") result.stdout.fnmatch_lines("* 1 skipped in *") assert tracked == [] -def test_async_support(testdir): +def test_async_support(pytester: Pytester) -> None: pytest.importorskip("unittest.async_case") - testdir.copy_example("unittest/test_unittest_asyncio.py") - reprec = testdir.inline_run() + pytester.copy_example("unittest/test_unittest_asyncio.py") + reprec = pytester.inline_run() reprec.assertoutcome(failed=1, passed=2) -def test_asynctest_support(testdir): +def test_asynctest_support(pytester: Pytester) -> None: """Check asynctest support (#7110)""" pytest.importorskip("asynctest") - testdir.copy_example("unittest/test_unittest_asynctest.py") - reprec = testdir.inline_run() + pytester.copy_example("unittest/test_unittest_asynctest.py") + reprec = pytester.inline_run() reprec.assertoutcome(failed=1, passed=2) -def test_plain_unittest_does_not_support_async(testdir): +def test_plain_unittest_does_not_support_async(pytester: Pytester) -> None: """Async functions in plain unittest.TestCase subclasses are not supported without plugins. This test exists here to avoid introducing this support by accident, leading users @@ -1247,8 +1260,8 @@ def test_plain_unittest_does_not_support_async(testdir): See https://github.com/pytest-dev/pytest-asyncio/issues/180 for more context. """ - testdir.copy_example("unittest/test_unittest_plain_async.py") - result = testdir.runpytest_subprocess() + pytester.copy_example("unittest/test_unittest_plain_async.py") + result = pytester.runpytest_subprocess() if hasattr(sys, "pypy_version_info"): # in PyPy we can't reliable get the warning about the coroutine not being awaited, # because it depends on the coroutine being garbage collected; given that @@ -1265,8 +1278,8 @@ def test_plain_unittest_does_not_support_async(testdir): @pytest.mark.skipif( sys.version_info < (3, 8), reason="Feature introduced in Python 3.8" ) -def test_do_class_cleanups_on_success(testdir): - testpath = testdir.makepyfile( +def test_do_class_cleanups_on_success(pytester: Pytester) -> None: + testpath = pytester.makepyfile( """ import unittest class MyTestCase(unittest.TestCase): @@ -1284,7 +1297,7 @@ def test_cleanup_called_exactly_once(): assert MyTestCase.values == [1] """ ) - reprec = testdir.inline_run(testpath) + reprec = pytester.inline_run(testpath) passed, skipped, failed = reprec.countoutcomes() assert failed == 0 assert passed == 3 @@ -1293,8 +1306,8 @@ def test_cleanup_called_exactly_once(): @pytest.mark.skipif( sys.version_info < (3, 8), reason="Feature introduced in Python 3.8" ) -def test_do_class_cleanups_on_setupclass_failure(testdir): - testpath = testdir.makepyfile( +def test_do_class_cleanups_on_setupclass_failure(pytester: Pytester) -> None: + testpath = pytester.makepyfile( """ import unittest class MyTestCase(unittest.TestCase): @@ -1311,7 +1324,7 @@ def test_cleanup_called_exactly_once(): assert MyTestCase.values == [1] """ ) - reprec = testdir.inline_run(testpath) + reprec = pytester.inline_run(testpath) passed, skipped, failed = reprec.countoutcomes() assert failed == 1 assert passed == 1 @@ -1320,8 +1333,8 @@ def test_cleanup_called_exactly_once(): @pytest.mark.skipif( sys.version_info < (3, 8), reason="Feature introduced in Python 3.8" ) -def test_do_class_cleanups_on_teardownclass_failure(testdir): - testpath = testdir.makepyfile( +def test_do_class_cleanups_on_teardownclass_failure(pytester: Pytester) -> None: + testpath = pytester.makepyfile( """ import unittest class MyTestCase(unittest.TestCase): @@ -1342,13 +1355,13 @@ def test_cleanup_called_exactly_once(): assert MyTestCase.values == [1] """ ) - reprec = testdir.inline_run(testpath) + reprec = pytester.inline_run(testpath) passed, skipped, failed = reprec.countoutcomes() assert passed == 3 -def test_do_cleanups_on_success(testdir): - testpath = testdir.makepyfile( +def test_do_cleanups_on_success(pytester: Pytester) -> None: + testpath = pytester.makepyfile( """ import unittest class MyTestCase(unittest.TestCase): @@ -1365,14 +1378,14 @@ def test_cleanup_called_the_right_number_of_times(): assert MyTestCase.values == [1, 1] """ ) - reprec = testdir.inline_run(testpath) + reprec = pytester.inline_run(testpath) passed, skipped, failed = reprec.countoutcomes() assert failed == 0 assert passed == 3 -def test_do_cleanups_on_setup_failure(testdir): - testpath = testdir.makepyfile( +def test_do_cleanups_on_setup_failure(pytester: Pytester) -> None: + testpath = pytester.makepyfile( """ import unittest class MyTestCase(unittest.TestCase): @@ -1390,14 +1403,14 @@ def test_cleanup_called_the_right_number_of_times(): assert MyTestCase.values == [1, 1] """ ) - reprec = testdir.inline_run(testpath) + reprec = pytester.inline_run(testpath) passed, skipped, failed = reprec.countoutcomes() assert failed == 2 assert passed == 1 -def test_do_cleanups_on_teardown_failure(testdir): - testpath = testdir.makepyfile( +def test_do_cleanups_on_teardown_failure(pytester: Pytester) -> None: + testpath = pytester.makepyfile( """ import unittest class MyTestCase(unittest.TestCase): @@ -1416,7 +1429,7 @@ def test_cleanup_called_the_right_number_of_times(): assert MyTestCase.values == [1, 1] """ ) - reprec = testdir.inline_run(testpath) + reprec = pytester.inline_run(testpath) passed, skipped, failed = reprec.countoutcomes() assert failed == 2 assert passed == 1 From 196b173c8a86833b96f90128a6cc9928e17b6c23 Mon Sep 17 00:00:00 2001 From: antonblr Date: Fri, 18 Dec 2020 12:36:20 -0800 Subject: [PATCH 025/630] address comments --- .github/workflows/main.yml | 3 +- src/_pytest/nodes.py | 2 +- src/_pytest/python.py | 2 +- testing/test_debugging.py | 5 +- testing/test_junitxml.py | 175 +++++++++++++++++++++---------------- testing/test_pytester.py | 27 +++--- 6 files changed, 121 insertions(+), 93 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1b6e85fd87e..beb50178528 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -123,8 +123,7 @@ jobs: with: fetch-depth: 0 - name: Set up Python ${{ matrix.python }} - # https://github.com/actions/setup-python/issues/171 - uses: actions/setup-python@v2.1.4 + uses: actions/setup-python@v2 with: python-version: ${{ matrix.python }} - name: Install dependencies diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py index 27c76a04302..1b3ec5571b1 100644 --- a/src/_pytest/nodes.py +++ b/src/_pytest/nodes.py @@ -528,7 +528,7 @@ def gethookproxy(self, fspath: "os.PathLike[str]"): warnings.warn(FSCOLLECTOR_GETHOOKPROXY_ISINITPATH, stacklevel=2) return self.session.gethookproxy(fspath) - def isinitpath(self, path: "os.PathLike[str]") -> bool: + def isinitpath(self, path: Union[str, "os.PathLike[str]"]) -> bool: warnings.warn(FSCOLLECTOR_GETHOOKPROXY_ISINITPATH, stacklevel=2) return self.session.isinitpath(path) diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 018e368f45e..3ff04455fbf 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -660,7 +660,7 @@ def gethookproxy(self, fspath: "os.PathLike[str]"): warnings.warn(FSCOLLECTOR_GETHOOKPROXY_ISINITPATH, stacklevel=2) return self.session.gethookproxy(fspath) - def isinitpath(self, path: "os.PathLike[str]") -> bool: + def isinitpath(self, path: Union[str, "os.PathLike[str]"]) -> bool: warnings.warn(FSCOLLECTOR_GETHOOKPROXY_ISINITPATH, stacklevel=2) return self.session.isinitpath(path) diff --git a/testing/test_debugging.py b/testing/test_debugging.py index 8218b7a0ede..e1b57299d25 100644 --- a/testing/test_debugging.py +++ b/testing/test_debugging.py @@ -21,10 +21,11 @@ @pytest.fixture(autouse=True) -def pdb_env(request, monkeypatch: MonkeyPatch): +def pdb_env(request): if "pytester" in request.fixturenames: # Disable pdb++ with inner tests. - monkeypatch.setenv("PDBPP_HIJACK_PDB", "0") + pytester = request.getfixturevalue("pytester") + pytester._monkeypatch.setenv("PDBPP_HIJACK_PDB", "0") def runpdb_and_get_report(pytester: Pytester, source: str): diff --git a/testing/test_junitxml.py b/testing/test_junitxml.py index 3e445dcefc5..1c76351eafc 100644 --- a/testing/test_junitxml.py +++ b/testing/test_junitxml.py @@ -7,7 +7,6 @@ from typing import Optional from typing import Tuple from typing import TYPE_CHECKING -from typing import TypeVar from typing import Union from xml.dom import minidom @@ -24,8 +23,6 @@ from _pytest.reports import TestReport from _pytest.store import Store -T = TypeVar("T") - @pytest.fixture(scope="session") def schema() -> xmlschema.XMLSchema: @@ -35,29 +32,34 @@ def schema() -> xmlschema.XMLSchema: return xmlschema.XMLSchema(f) -@pytest.fixture -def run_and_parse(pytester: Pytester, schema: xmlschema.XMLSchema) -> T: - """Fixture that returns a function that can be used to execute pytest and - return the parsed ``DomNode`` of the root xml node. - - The ``family`` parameter is used to configure the ``junit_family`` of the written report. - "xunit2" is also automatically validated against the schema. - """ +class RunAndParse: + def __init__(self, pytester: Pytester, schema: xmlschema.XMLSchema) -> None: + self.pytester = pytester + self.schema = schema - def run( - *args: Union[str, "os.PathLike[str]"], family: Optional[str] = "xunit1", + def __call__( + self, *args: Union[str, "os.PathLike[str]"], family: Optional[str] = "xunit1" ) -> Tuple[RunResult, "DomNode"]: if family: args = ("-o", "junit_family=" + family) + args - xml_path = pytester.path.joinpath("junit.xml") - result = pytester.runpytest("--junitxml=%s" % xml_path, *args) + xml_path = self.pytester.path.joinpath("junit.xml") + result = self.pytester.runpytest("--junitxml=%s" % xml_path, *args) if family == "xunit2": with xml_path.open() as f: - schema.validate(f) + self.schema.validate(f) xmldoc = minidom.parse(str(xml_path)) return result, DomNode(xmldoc) - return cast(T, run) + +@pytest.fixture +def run_and_parse(pytester: Pytester, schema: xmlschema.XMLSchema) -> RunAndParse: + """Fixture that returns a function that can be used to execute pytest and + return the parsed ``DomNode`` of the root xml node. + + The ``family`` parameter is used to configure the ``junit_family`` of the written report. + "xunit2" is also automatically validated against the schema. + """ + return RunAndParse(pytester, schema) def assert_attr(node, **kwargs): @@ -140,7 +142,7 @@ def next_sibling(self): class TestPython: @parametrize_families def test_summing_simple( - self, pytester: Pytester, run_and_parse, xunit_family + self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str ) -> None: pytester.makepyfile( """ @@ -166,7 +168,7 @@ def test_xpass(): @parametrize_families def test_summing_simple_with_errors( - self, pytester: Pytester, run_and_parse, xunit_family + self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str ) -> None: pytester.makepyfile( """ @@ -195,7 +197,7 @@ def test_xpass(): @parametrize_families def test_hostname_in_xml( - self, pytester: Pytester, run_and_parse, xunit_family + self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str ) -> None: pytester.makepyfile( """ @@ -209,7 +211,7 @@ def test_pass(): @parametrize_families def test_timestamp_in_xml( - self, pytester: Pytester, run_and_parse, xunit_family + self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str ) -> None: pytester.makepyfile( """ @@ -224,7 +226,7 @@ def test_pass(): assert start_time <= timestamp < datetime.now() def test_timing_function( - self, pytester: Pytester, run_and_parse, mock_timing + self, pytester: Pytester, run_and_parse: RunAndParse, mock_timing ) -> None: pytester.makepyfile( """ @@ -248,8 +250,8 @@ def test_junit_duration_report( self, pytester: Pytester, monkeypatch: MonkeyPatch, - duration_report, - run_and_parse, + duration_report: str, + run_and_parse: RunAndParse, ) -> None: # mock LogXML.node_reporter so it always sets a known duration to each test report object @@ -279,7 +281,9 @@ def test_foo(): assert val == 1.0 @parametrize_families - def test_setup_error(self, pytester: Pytester, run_and_parse, xunit_family) -> None: + def test_setup_error( + self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str + ) -> None: pytester.makepyfile( """ import pytest @@ -303,7 +307,7 @@ def test_function(arg): @parametrize_families def test_teardown_error( - self, pytester: Pytester, run_and_parse, xunit_family + self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str ) -> None: pytester.makepyfile( """ @@ -328,7 +332,7 @@ def test_function(arg): @parametrize_families def test_call_failure_teardown_error( - self, pytester: Pytester, run_and_parse, xunit_family + self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str ) -> None: pytester.makepyfile( """ @@ -359,7 +363,7 @@ def test_function(arg): @parametrize_families def test_skip_contains_name_reason( - self, pytester: Pytester, run_and_parse, xunit_family + self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str ) -> None: pytester.makepyfile( """ @@ -379,7 +383,7 @@ def test_skip(): @parametrize_families def test_mark_skip_contains_name_reason( - self, pytester: Pytester, run_and_parse, xunit_family + self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str ) -> None: pytester.makepyfile( """ @@ -402,7 +406,7 @@ def test_skip(): @parametrize_families def test_mark_skipif_contains_name_reason( - self, pytester: Pytester, run_and_parse, xunit_family + self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str ) -> None: pytester.makepyfile( """ @@ -426,7 +430,7 @@ def test_skip(): @parametrize_families def test_mark_skip_doesnt_capture_output( - self, pytester: Pytester, run_and_parse, xunit_family + self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str ) -> None: pytester.makepyfile( """ @@ -443,7 +447,7 @@ def test_skip(): @parametrize_families def test_classname_instance( - self, pytester: Pytester, run_and_parse, xunit_family + self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str ) -> None: pytester.makepyfile( """ @@ -463,7 +467,7 @@ def test_method(self): @parametrize_families def test_classname_nested_dir( - self, pytester: Pytester, run_and_parse, xunit_family + self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str ) -> None: p = pytester.mkdir("sub").joinpath("test_hello.py") p.write_text("def test_func(): 0/0") @@ -476,7 +480,7 @@ def test_classname_nested_dir( @parametrize_families def test_internal_error( - self, pytester: Pytester, run_and_parse, xunit_family + self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str ) -> None: pytester.makeconftest("def pytest_runtest_protocol(): 0 / 0") pytester.makepyfile("def test_function(): pass") @@ -495,7 +499,11 @@ def test_internal_error( ) @parametrize_families def test_failure_function( - self, pytester: Pytester, junit_logging, run_and_parse, xunit_family + self, + pytester: Pytester, + junit_logging, + run_and_parse: RunAndParse, + xunit_family, ) -> None: pytester.makepyfile( """ @@ -559,7 +567,7 @@ def test_fail(): @parametrize_families def test_failure_verbose_message( - self, pytester: Pytester, run_and_parse, xunit_family + self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str ) -> None: pytester.makepyfile( """ @@ -576,7 +584,7 @@ def test_fail(): @parametrize_families def test_failure_escape( - self, pytester: Pytester, run_and_parse, xunit_family + self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str ) -> None: pytester.makepyfile( """ @@ -606,7 +614,7 @@ def test_func(arg1): @parametrize_families def test_junit_prefixing( - self, pytester: Pytester, run_and_parse, xunit_family + self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str ) -> None: pytester.makepyfile( """ @@ -630,7 +638,7 @@ def test_hello(self): @parametrize_families def test_xfailure_function( - self, pytester: Pytester, run_and_parse, xunit_family + self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str ) -> None: pytester.makepyfile( """ @@ -650,7 +658,7 @@ def test_xfail(): @parametrize_families def test_xfailure_marker( - self, pytester: Pytester, run_and_parse, xunit_family + self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str ) -> None: pytester.makepyfile( """ @@ -673,7 +681,7 @@ def test_xfail(): "junit_logging", ["no", "log", "system-out", "system-err", "out-err", "all"] ) def test_xfail_captures_output_once( - self, pytester: Pytester, junit_logging, run_and_parse + self, pytester: Pytester, junit_logging: str, run_and_parse: RunAndParse ) -> None: pytester.makepyfile( """ @@ -702,7 +710,7 @@ def test_fail(): @parametrize_families def test_xfailure_xpass( - self, pytester: Pytester, run_and_parse, xunit_family + self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str ) -> None: pytester.makepyfile( """ @@ -721,7 +729,7 @@ def test_xpass(): @parametrize_families def test_xfailure_xpass_strict( - self, pytester: Pytester, run_and_parse, xunit_family + self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str ) -> None: pytester.makepyfile( """ @@ -742,7 +750,7 @@ def test_xpass(): @parametrize_families def test_collect_error( - self, pytester: Pytester, run_and_parse, xunit_family + self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str ) -> None: pytester.makepyfile("syntax error") result, dom = run_and_parse(family=xunit_family) @@ -754,7 +762,7 @@ def test_collect_error( fnode.assert_attr(message="collection failure") assert "SyntaxError" in fnode.toxml() - def test_unicode(self, pytester: Pytester, run_and_parse) -> None: + def test_unicode(self, pytester: Pytester, run_and_parse: RunAndParse) -> None: value = "hx\xc4\x85\xc4\x87\n" pytester.makepyfile( """\ @@ -771,7 +779,9 @@ def test_hello(): fnode = tnode.find_first_by_tag("failure") assert "hx" in fnode.toxml() - def test_assertion_binchars(self, pytester: Pytester, run_and_parse) -> None: + def test_assertion_binchars( + self, pytester: Pytester, run_and_parse: RunAndParse + ) -> None: """This test did fail when the escaping wasn't strict.""" pytester.makepyfile( """ @@ -788,7 +798,7 @@ def test_str_compare(): @pytest.mark.parametrize("junit_logging", ["no", "system-out"]) def test_pass_captures_stdout( - self, pytester: Pytester, run_and_parse, junit_logging + self, pytester: Pytester, run_and_parse: RunAndParse, junit_logging: str ) -> None: pytester.makepyfile( """ @@ -811,7 +821,7 @@ def test_pass(): @pytest.mark.parametrize("junit_logging", ["no", "system-err"]) def test_pass_captures_stderr( - self, pytester: Pytester, run_and_parse, junit_logging + self, pytester: Pytester, run_and_parse: RunAndParse, junit_logging: str ) -> None: pytester.makepyfile( """ @@ -835,7 +845,7 @@ def test_pass(): @pytest.mark.parametrize("junit_logging", ["no", "system-out"]) def test_setup_error_captures_stdout( - self, pytester: Pytester, run_and_parse, junit_logging + self, pytester: Pytester, run_and_parse: RunAndParse, junit_logging: str ) -> None: pytester.makepyfile( """ @@ -864,7 +874,7 @@ def test_function(arg): @pytest.mark.parametrize("junit_logging", ["no", "system-err"]) def test_setup_error_captures_stderr( - self, pytester: Pytester, run_and_parse, junit_logging + self, pytester: Pytester, run_and_parse: RunAndParse, junit_logging: str ) -> None: pytester.makepyfile( """ @@ -894,7 +904,7 @@ def test_function(arg): @pytest.mark.parametrize("junit_logging", ["no", "system-out"]) def test_avoid_double_stdout( - self, pytester: Pytester, run_and_parse, junit_logging + self, pytester: Pytester, run_and_parse: RunAndParse, junit_logging: str ) -> None: pytester.makepyfile( """ @@ -964,7 +974,7 @@ def getini(self, name): class TestNonPython: @parametrize_families def test_summing_simple( - self, pytester: Pytester, run_and_parse, xunit_family + self, pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str ) -> None: pytester.makeconftest( """ @@ -992,7 +1002,7 @@ def repr_failure(self, excinfo): @pytest.mark.parametrize("junit_logging", ["no", "system-out"]) -def test_nullbyte(pytester: Pytester, junit_logging) -> None: +def test_nullbyte(pytester: Pytester, junit_logging: str) -> None: # A null byte can not occur in XML (see section 2.2 of the spec) pytester.makepyfile( """ @@ -1014,7 +1024,7 @@ def test_print_nullbyte(): @pytest.mark.parametrize("junit_logging", ["no", "system-out"]) -def test_nullbyte_replace(pytester: Pytester, junit_logging) -> None: +def test_nullbyte_replace(pytester: Pytester, junit_logging: str) -> None: # Check if the null byte gets replaced pytester.makepyfile( """ @@ -1114,7 +1124,9 @@ def test_logxml_check_isdir(pytester: Pytester) -> None: result.stderr.fnmatch_lines(["*--junitxml must be a filename*"]) -def test_escaped_parametrized_names_xml(pytester: Pytester, run_and_parse) -> None: +def test_escaped_parametrized_names_xml( + pytester: Pytester, run_and_parse: RunAndParse +) -> None: pytester.makepyfile( """\ import pytest @@ -1130,7 +1142,7 @@ def test_func(char): def test_double_colon_split_function_issue469( - pytester: Pytester, run_and_parse + pytester: Pytester, run_and_parse: RunAndParse ) -> None: pytester.makepyfile( """ @@ -1147,7 +1159,9 @@ def test_func(param): node.assert_attr(name="test_func[double::colon]") -def test_double_colon_split_method_issue469(pytester: Pytester, run_and_parse) -> None: +def test_double_colon_split_method_issue469( + pytester: Pytester, run_and_parse: RunAndParse +) -> None: pytester.makepyfile( """ import pytest @@ -1194,7 +1208,7 @@ class Report(BaseReport): log.pytest_sessionfinish() -def test_record_property(pytester: Pytester, run_and_parse) -> None: +def test_record_property(pytester: Pytester, run_and_parse: RunAndParse) -> None: pytester.makepyfile( """ import pytest @@ -1216,7 +1230,9 @@ def test_record(record_property, other): result.stdout.fnmatch_lines(["*= 1 passed in *"]) -def test_record_property_same_name(pytester: Pytester, run_and_parse) -> None: +def test_record_property_same_name( + pytester: Pytester, run_and_parse: RunAndParse +) -> None: pytester.makepyfile( """ def test_record_with_same_name(record_property): @@ -1234,7 +1250,9 @@ def test_record_with_same_name(record_property): @pytest.mark.parametrize("fixture_name", ["record_property", "record_xml_attribute"]) -def test_record_fixtures_without_junitxml(pytester: Pytester, fixture_name) -> None: +def test_record_fixtures_without_junitxml( + pytester: Pytester, fixture_name: str +) -> None: pytester.makepyfile( """ def test_record({fixture_name}): @@ -1248,7 +1266,7 @@ def test_record({fixture_name}): @pytest.mark.filterwarnings("default") -def test_record_attribute(pytester: Pytester, run_and_parse) -> None: +def test_record_attribute(pytester: Pytester, run_and_parse: RunAndParse) -> None: pytester.makeini( """ [pytest] @@ -1279,7 +1297,7 @@ def test_record(record_xml_attribute, other): @pytest.mark.filterwarnings("default") @pytest.mark.parametrize("fixture_name", ["record_xml_attribute", "record_property"]) def test_record_fixtures_xunit2( - pytester: Pytester, fixture_name, run_and_parse + pytester: Pytester, fixture_name: str, run_and_parse: RunAndParse ) -> None: """Ensure record_xml_attribute and record_property drop values when outside of legacy family.""" pytester.makeini( @@ -1318,7 +1336,7 @@ def test_record({fixture_name}, other): def test_random_report_log_xdist( - pytester: Pytester, monkeypatch: MonkeyPatch, run_and_parse + pytester: Pytester, monkeypatch: MonkeyPatch, run_and_parse: RunAndParse ) -> None: """`xdist` calls pytest_runtest_logreport as they are executed by the workers, with nodes from several nodes overlapping, so junitxml must cope with that @@ -1344,7 +1362,9 @@ def test_x(i): @parametrize_families -def test_root_testsuites_tag(pytester: Pytester, run_and_parse, xunit_family) -> None: +def test_root_testsuites_tag( + pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str +) -> None: pytester.makepyfile( """ def test_x(): @@ -1358,7 +1378,7 @@ def test_x(): assert suite_node.tag == "testsuite" -def test_runs_twice(pytester: Pytester, run_and_parse) -> None: +def test_runs_twice(pytester: Pytester, run_and_parse: RunAndParse) -> None: f = pytester.makepyfile( """ def test_pass(): @@ -1373,7 +1393,7 @@ def test_pass(): def test_runs_twice_xdist( - pytester: Pytester, monkeypatch: MonkeyPatch, run_and_parse + pytester: Pytester, monkeypatch: MonkeyPatch, run_and_parse: RunAndParse ) -> None: pytest.importorskip("xdist") monkeypatch.delenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD") @@ -1390,7 +1410,7 @@ def test_pass(): assert first == second -def test_fancy_items_regression(pytester: Pytester, run_and_parse) -> None: +def test_fancy_items_regression(pytester: Pytester, run_and_parse: RunAndParse) -> None: # issue 1259 pytester.makeconftest( """ @@ -1443,7 +1463,7 @@ def test_pass(): @parametrize_families -def test_global_properties(pytester: Pytester, xunit_family) -> None: +def test_global_properties(pytester: Pytester, xunit_family: str) -> None: path = pytester.path.joinpath("test_global_properties.xml") log = LogXML(str(path), None, family=xunit_family) @@ -1505,7 +1525,7 @@ class Report(BaseReport): @parametrize_families def test_record_testsuite_property( - pytester: Pytester, run_and_parse, xunit_family + pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str ) -> None: pytester.makepyfile( """ @@ -1538,7 +1558,9 @@ def test_func1(record_testsuite_property): @pytest.mark.parametrize("junit", [True, False]) -def test_record_testsuite_property_type_checking(pytester: Pytester, junit) -> None: +def test_record_testsuite_property_type_checking( + pytester: Pytester, junit: bool +) -> None: pytester.makepyfile( """ def test_func1(record_testsuite_property): @@ -1556,7 +1578,7 @@ def test_func1(record_testsuite_property): @pytest.mark.parametrize("suite_name", ["my_suite", ""]) @parametrize_families def test_set_suite_name( - pytester: Pytester, suite_name, run_and_parse, xunit_family + pytester: Pytester, suite_name: str, run_and_parse: RunAndParse, xunit_family: str ) -> None: if suite_name: pytester.makeini( @@ -1585,7 +1607,9 @@ def test_func(): node.assert_attr(name=expected) -def test_escaped_skipreason_issue3533(pytester: Pytester, run_and_parse) -> None: +def test_escaped_skipreason_issue3533( + pytester: Pytester, run_and_parse: RunAndParse +) -> None: pytester.makepyfile( """ import pytest @@ -1603,7 +1627,7 @@ def test_skip(): @parametrize_families def test_logging_passing_tests_disabled_does_not_log_test_output( - pytester: Pytester, run_and_parse, xunit_family + pytester: Pytester, run_and_parse: RunAndParse, xunit_family: str ) -> None: pytester.makeini( """ @@ -1637,7 +1661,10 @@ def test_func(): @parametrize_families @pytest.mark.parametrize("junit_logging", ["no", "system-out", "system-err"]) def test_logging_passing_tests_disabled_logs_output_for_failing_test_issue5430( - pytester: Pytester, junit_logging, run_and_parse, xunit_family + pytester: Pytester, + junit_logging: str, + run_and_parse: RunAndParse, + xunit_family: str, ) -> None: pytester.makeini( """ diff --git a/testing/test_pytester.py b/testing/test_pytester.py index a9ba1a046f1..57d6f4fd9eb 100644 --- a/testing/test_pytester.py +++ b/testing/test_pytester.py @@ -92,7 +92,7 @@ def test_hello(pytester): result.assert_outcomes(passed=1) -def test_pytester_with_doctest(pytester: Pytester): +def test_pytester_with_doctest(pytester: Pytester) -> None: """Check that pytester can be used within doctests. It used to use `request.function`, which is `None` with doctests.""" @@ -314,20 +314,19 @@ def test_cwd_snapshot(pytester: Pytester) -> None: class TestSysModulesSnapshot: key = "my-test-module" - mod = ModuleType("something") def test_remove_added(self) -> None: original = dict(sys.modules) assert self.key not in sys.modules snapshot = SysModulesSnapshot() - sys.modules[self.key] = "something" # type: ignore + sys.modules[self.key] = ModuleType("something") assert self.key in sys.modules snapshot.restore() assert sys.modules == original def test_add_removed(self, monkeypatch: MonkeyPatch) -> None: assert self.key not in sys.modules - monkeypatch.setitem(sys.modules, self.key, self.mod) + monkeypatch.setitem(sys.modules, self.key, ModuleType("something")) assert self.key in sys.modules original = dict(sys.modules) snapshot = SysModulesSnapshot() @@ -338,11 +337,11 @@ def test_add_removed(self, monkeypatch: MonkeyPatch) -> None: def test_restore_reloaded(self, monkeypatch: MonkeyPatch) -> None: assert self.key not in sys.modules - monkeypatch.setitem(sys.modules, self.key, self.mod) + monkeypatch.setitem(sys.modules, self.key, ModuleType("something")) assert self.key in sys.modules original = dict(sys.modules) snapshot = SysModulesSnapshot() - sys.modules[self.key] = "something else" # type: ignore + sys.modules[self.key] = ModuleType("something else") snapshot.restore() assert sys.modules == original @@ -358,9 +357,9 @@ def preserve(name): return name in (key[0], key[1], "some-other-key") snapshot = SysModulesSnapshot(preserve=preserve) - sys.modules[key[0]] = original[key[0]] = "something else0" # type: ignore - sys.modules[key[1]] = original[key[1]] = "something else1" # type: ignore - sys.modules[key[2]] = "something else2" # type: ignore + sys.modules[key[0]] = original[key[0]] = ModuleType("something else0") + sys.modules[key[1]] = original[key[1]] = ModuleType("something else1") + sys.modules[key[2]] = ModuleType("something else2") snapshot.restore() assert sys.modules == original @@ -368,7 +367,7 @@ def test_preserve_container(self, monkeypatch: MonkeyPatch) -> None: original = dict(sys.modules) assert self.key not in original replacement = dict(sys.modules) - replacement[self.key] = "life of brian" # type: ignore + replacement[self.key] = ModuleType("life of brian") snapshot = SysModulesSnapshot() monkeypatch.setattr(sys, "modules", replacement) snapshot.restore() @@ -442,7 +441,9 @@ def test_one(): assert pytester.runpytest(testfile).ret == 0 """ ) - result = pytester.runpytest("-p", "pytester", "--runpytest", "subprocess", testfile) + result = pytester.runpytest_inprocess( + "-p", "pytester", "--runpytest", "subprocess", testfile + ) assert result.ret == 0 @@ -777,7 +778,7 @@ def test_error2(bad_fixture): assert result.parseoutcomes() == {"errors": 2} -def test_parse_summary_line_always_plural(): +def test_parse_summary_line_always_plural() -> None: """Parsing summaries always returns plural nouns (#6505)""" lines = [ "some output 1", @@ -812,6 +813,6 @@ def test_makefile_joins_absolute_path(pytester: Pytester) -> None: assert str(p1) == str(pytester.path / "absfile.py") -def test_testtmproot(testdir): +def test_testtmproot(testdir) -> None: """Check test_tmproot is a py.path attribute for backward compatibility.""" assert testdir.test_tmproot.check(dir=1) From 73586be08fe33c483ae3c3509a05459969ba2ab9 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Fri, 18 Dec 2020 20:33:39 +0200 Subject: [PATCH 026/630] terminal: remove unused union arm in WarningReport.fslocation --- src/_pytest/terminal.py | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/src/_pytest/terminal.py b/src/_pytest/terminal.py index f5d4e1f8ddc..d3d1a4b666e 100644 --- a/src/_pytest/terminal.py +++ b/src/_pytest/terminal.py @@ -285,15 +285,13 @@ class WarningReport: User friendly message about the warning. :ivar str|None nodeid: nodeid that generated the warning (see ``get_location``). - :ivar tuple|py.path.local fslocation: + :ivar tuple fslocation: File system location of the source of the warning (see ``get_location``). """ message = attr.ib(type=str) nodeid = attr.ib(type=Optional[str], default=None) - fslocation = attr.ib( - type=Optional[Union[Tuple[str, int], py.path.local]], default=None - ) + fslocation = attr.ib(type=Optional[Tuple[str, int]], default=None) count_towards_summary = True def get_location(self, config: Config) -> Optional[str]: @@ -301,14 +299,9 @@ def get_location(self, config: Config) -> Optional[str]: if self.nodeid: return self.nodeid if self.fslocation: - if isinstance(self.fslocation, tuple) and len(self.fslocation) >= 2: - filename, linenum = self.fslocation[:2] - relpath = bestrelpath( - config.invocation_params.dir, absolutepath(filename) - ) - return f"{relpath}:{linenum}" - else: - return str(self.fslocation) + filename, linenum = self.fslocation + relpath = bestrelpath(config.invocation_params.dir, absolutepath(filename)) + return f"{relpath}:{linenum}" return None From 2c05a7babb4fa31775730e596a908f7c1f861765 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Fri, 18 Dec 2020 20:39:33 +0200 Subject: [PATCH 027/630] config: let main() accept any os.PathLike instead of just py.path.local Really it ought to only take the List[str], but for backward compatibility, at least get rid of the explicit py.path.local check. --- src/_pytest/config/__init__.py | 10 ++++++---- testing/test_collection.py | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 0df4ffa01c1..c9a0e78bfcf 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -128,7 +128,7 @@ def filter_traceback_for_conftest_import_failure( def main( - args: Optional[Union[List[str], py.path.local]] = None, + args: Optional[Union[List[str], "os.PathLike[str]"]] = None, plugins: Optional[Sequence[Union[str, _PluggyPlugin]]] = None, ) -> Union[int, ExitCode]: """Perform an in-process test run. @@ -295,13 +295,15 @@ def get_plugin_manager() -> "PytestPluginManager": def _prepareconfig( - args: Optional[Union[py.path.local, List[str]]] = None, + args: Optional[Union[List[str], "os.PathLike[str]"]] = None, plugins: Optional[Sequence[Union[str, _PluggyPlugin]]] = None, ) -> "Config": if args is None: args = sys.argv[1:] - elif isinstance(args, py.path.local): - args = [str(args)] + # TODO: Remove type-ignore after next mypy release. + # https://github.com/python/typeshed/commit/076983eec45e739c68551cb6119fd7d85fd4afa9 + elif isinstance(args, os.PathLike): # type: ignore[misc] + args = [os.fspath(args)] elif not isinstance(args, list): msg = "`args` parameter expected to be a list of strings, got: {!r} (type: {})" raise TypeError(msg.format(args, type(args))) diff --git a/testing/test_collection.py b/testing/test_collection.py index 2d03fda39de..9733b4fbd47 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -277,7 +277,7 @@ def pytest_collect_file(self, path): wascalled.append(path) pytester.makefile(".abc", "xyz") - pytest.main(py.path.local(pytester.path), plugins=[Plugin()]) + pytest.main(pytester.path, plugins=[Plugin()]) assert len(wascalled) == 1 assert wascalled[0].ext == ".abc" From 042d12fae6e03f97ac25311504f6697154eff08e Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sat, 19 Dec 2020 14:02:24 +0200 Subject: [PATCH 028/630] doctest: use Path instead of py.path where possible --- src/_pytest/doctest.py | 21 +++++++++++---------- testing/test_doctest.py | 17 ++++++++--------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/_pytest/doctest.py b/src/_pytest/doctest.py index d0b6b4c4185..24f8882579b 100644 --- a/src/_pytest/doctest.py +++ b/src/_pytest/doctest.py @@ -36,6 +36,7 @@ from _pytest.fixtures import FixtureRequest from _pytest.nodes import Collector from _pytest.outcomes import OutcomeException +from _pytest.pathlib import fnmatch_ex from _pytest.pathlib import import_path from _pytest.python_api import approx from _pytest.warning_types import PytestWarning @@ -120,32 +121,32 @@ def pytest_unconfigure() -> None: def pytest_collect_file( - path: py.path.local, parent: Collector, + fspath: Path, path: py.path.local, parent: Collector, ) -> Optional[Union["DoctestModule", "DoctestTextfile"]]: config = parent.config - if path.ext == ".py": - if config.option.doctestmodules and not _is_setup_py(path): + if fspath.suffix == ".py": + if config.option.doctestmodules and not _is_setup_py(fspath): mod: DoctestModule = DoctestModule.from_parent(parent, fspath=path) return mod - elif _is_doctest(config, path, parent): + elif _is_doctest(config, fspath, parent): txt: DoctestTextfile = DoctestTextfile.from_parent(parent, fspath=path) return txt return None -def _is_setup_py(path: py.path.local) -> bool: - if path.basename != "setup.py": +def _is_setup_py(path: Path) -> bool: + if path.name != "setup.py": return False - contents = path.read_binary() + contents = path.read_bytes() return b"setuptools" in contents or b"distutils" in contents -def _is_doctest(config: Config, path: py.path.local, parent) -> bool: - if path.ext in (".txt", ".rst") and parent.session.isinitpath(path): +def _is_doctest(config: Config, path: Path, parent: Collector) -> bool: + if path.suffix in (".txt", ".rst") and parent.session.isinitpath(path): return True globs = config.getoption("doctestglob") or ["test*.txt"] for glob in globs: - if path.check(fnmatch=glob): + if fnmatch_ex(glob, path): return True return False diff --git a/testing/test_doctest.py b/testing/test_doctest.py index 6e3880330a9..08d0aacf68c 100644 --- a/testing/test_doctest.py +++ b/testing/test_doctest.py @@ -1,10 +1,9 @@ import inspect import textwrap +from pathlib import Path from typing import Callable from typing import Optional -import py - import pytest from _pytest.doctest import _get_checker from _pytest.doctest import _is_mocked @@ -1496,25 +1495,25 @@ def test_warning_on_unwrap_of_broken_object( assert inspect.unwrap.__module__ == "inspect" -def test_is_setup_py_not_named_setup_py(tmp_path): +def test_is_setup_py_not_named_setup_py(tmp_path: Path) -> None: not_setup_py = tmp_path.joinpath("not_setup.py") not_setup_py.write_text('from setuptools import setup; setup(name="foo")') - assert not _is_setup_py(py.path.local(str(not_setup_py))) + assert not _is_setup_py(not_setup_py) @pytest.mark.parametrize("mod", ("setuptools", "distutils.core")) -def test_is_setup_py_is_a_setup_py(tmpdir, mod): - setup_py = tmpdir.join("setup.py") - setup_py.write(f'from {mod} import setup; setup(name="foo")') +def test_is_setup_py_is_a_setup_py(tmp_path: Path, mod: str) -> None: + setup_py = tmp_path.joinpath("setup.py") + setup_py.write_text(f'from {mod} import setup; setup(name="foo")', "utf-8") assert _is_setup_py(setup_py) @pytest.mark.parametrize("mod", ("setuptools", "distutils.core")) -def test_is_setup_py_different_encoding(tmp_path, mod): +def test_is_setup_py_different_encoding(tmp_path: Path, mod: str) -> None: setup_py = tmp_path.joinpath("setup.py") contents = ( "# -*- coding: cp1252 -*-\n" 'from {} import setup; setup(name="foo", description="€")\n'.format(mod) ) setup_py.write_bytes(contents.encode("cp1252")) - assert _is_setup_py(py.path.local(str(setup_py))) + assert _is_setup_py(setup_py) From 7aa224083205adb650a7b1132e6b9e861361426e Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sat, 19 Dec 2020 14:06:17 +0200 Subject: [PATCH 029/630] testing/test_nodes: fix fake session to be more accurate The type of _initialpaths is `FrozenSet[Path]`. --- testing/test_nodes.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/testing/test_nodes.py b/testing/test_nodes.py index bae31f0a39c..59d9f409eac 100644 --- a/testing/test_nodes.py +++ b/testing/test_nodes.py @@ -1,3 +1,4 @@ +from pathlib import Path from typing import cast from typing import List from typing import Type @@ -69,23 +70,23 @@ def test(): def test__check_initialpaths_for_relpath() -> None: """Ensure that it handles dirs, and does not always use dirname.""" - cwd = py.path.local() + cwd = Path.cwd() class FakeSession1: - _initialpaths = [cwd] + _initialpaths = frozenset({cwd}) session = cast(pytest.Session, FakeSession1) - assert nodes._check_initialpaths_for_relpath(session, cwd) == "" + assert nodes._check_initialpaths_for_relpath(session, py.path.local(cwd)) == "" - sub = cwd.join("file") + sub = cwd / "file" class FakeSession2: - _initialpaths = [cwd] + _initialpaths = frozenset({cwd}) session = cast(pytest.Session, FakeSession2) - assert nodes._check_initialpaths_for_relpath(session, sub) == "file" + assert nodes._check_initialpaths_for_relpath(session, py.path.local(sub)) == "file" outside = py.path.local("/outside") assert nodes._check_initialpaths_for_relpath(session, outside) is None From 2ec372df8b987207efc4ad0f33c2f82df5c9e2e5 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sat, 19 Dec 2020 22:19:51 +0200 Subject: [PATCH 030/630] mark: export pytest.Mark for typing purposes The type cannot be constructed directly, but is exported for use in type annotations, since it is reachable through existing public API. --- changelog/7469.deprecation.rst | 5 +++++ changelog/7469.feature.rst | 10 +++++++++ doc/en/reference.rst | 4 ++-- src/_pytest/mark/structures.py | 39 +++++++++++++++++++++++++--------- src/pytest/__init__.py | 2 ++ 5 files changed, 48 insertions(+), 12 deletions(-) create mode 100644 changelog/7469.deprecation.rst create mode 100644 changelog/7469.feature.rst diff --git a/changelog/7469.deprecation.rst b/changelog/7469.deprecation.rst new file mode 100644 index 00000000000..79419dacef2 --- /dev/null +++ b/changelog/7469.deprecation.rst @@ -0,0 +1,5 @@ +Directly constructing the following classes is now deprecated: + +- ``_pytest.mark.structures.Mark`` + +These have always been considered private, but now issue a deprecation warning, which may become a hard error in pytest 7.0.0. diff --git a/changelog/7469.feature.rst b/changelog/7469.feature.rst new file mode 100644 index 00000000000..f4bc52414bc --- /dev/null +++ b/changelog/7469.feature.rst @@ -0,0 +1,10 @@ +The types of objects used in pytest's API are now exported so they may be used in type annotations. + +The newly-exported types are: + +- ``pytest.Mark`` for :class:`marks `. + +Constructing them directly is not supported; they are only meant for use in type annotations. +Doing so will emit a deprecation warning, and may become a hard-error in pytest 7.0. + +Subclassing them is also not supported. This is not currently enforced at runtime, but is detected by type-checkers such as mypy. diff --git a/doc/en/reference.rst b/doc/en/reference.rst index 8aa95ca6448..3fc62ee7279 100644 --- a/doc/en/reference.rst +++ b/doc/en/reference.rst @@ -239,7 +239,7 @@ For example: def test_function(): ... -Will create and attach a :class:`Mark <_pytest.mark.structures.Mark>` object to the collected +Will create and attach a :class:`Mark ` object to the collected :class:`Item `, which can then be accessed by fixtures or hooks with :meth:`Node.iter_markers <_pytest.nodes.Node.iter_markers>`. The ``mark`` object will have the following attributes: @@ -863,7 +863,7 @@ MarkGenerator Mark ~~~~ -.. autoclass:: _pytest.mark.structures.Mark +.. autoclass:: pytest.Mark() :members: diff --git a/src/_pytest/mark/structures.py b/src/_pytest/mark/structures.py index 6c126cf4a29..29b9586871f 100644 --- a/src/_pytest/mark/structures.py +++ b/src/_pytest/mark/structures.py @@ -28,6 +28,7 @@ from ..compat import NOTSET from ..compat import NotSetType from _pytest.config import Config +from _pytest.deprecated import check_ispytest from _pytest.outcomes import fail from _pytest.warning_types import PytestUnknownMarkWarning @@ -200,21 +201,38 @@ def _for_parametrize( @final -@attr.s(frozen=True) +@attr.s(frozen=True, init=False, auto_attribs=True) class Mark: #: Name of the mark. - name = attr.ib(type=str) + name: str #: Positional arguments of the mark decorator. - args = attr.ib(type=Tuple[Any, ...]) + args: Tuple[Any, ...] #: Keyword arguments of the mark decorator. - kwargs = attr.ib(type=Mapping[str, Any]) + kwargs: Mapping[str, Any] #: Source Mark for ids with parametrize Marks. - _param_ids_from = attr.ib(type=Optional["Mark"], default=None, repr=False) + _param_ids_from: Optional["Mark"] = attr.ib(default=None, repr=False) #: Resolved/generated ids with parametrize Marks. - _param_ids_generated = attr.ib( - type=Optional[Sequence[str]], default=None, repr=False - ) + _param_ids_generated: Optional[Sequence[str]] = attr.ib(default=None, repr=False) + + def __init__( + self, + name: str, + args: Tuple[Any, ...], + kwargs: Mapping[str, Any], + param_ids_from: Optional["Mark"] = None, + param_ids_generated: Optional[Sequence[str]] = None, + *, + _ispytest: bool = False, + ) -> None: + """:meta private:""" + check_ispytest(_ispytest) + # Weirdness to bypass frozen=True. + object.__setattr__(self, "name", name) + object.__setattr__(self, "args", args) + object.__setattr__(self, "kwargs", kwargs) + object.__setattr__(self, "_param_ids_from", param_ids_from) + object.__setattr__(self, "_param_ids_generated", param_ids_generated) def _has_param_ids(self) -> bool: return "ids" in self.kwargs or len(self.args) >= 4 @@ -243,6 +261,7 @@ def combined_with(self, other: "Mark") -> "Mark": self.args + other.args, dict(self.kwargs, **other.kwargs), param_ids_from=param_ids_from, + _ispytest=True, ) @@ -320,7 +339,7 @@ def with_args(self, *args: object, **kwargs: object) -> "MarkDecorator": :rtype: MarkDecorator """ - mark = Mark(self.name, args, kwargs) + mark = Mark(self.name, args, kwargs, _ispytest=True) return self.__class__(self.mark.combined_with(mark)) # Type ignored because the overloads overlap with an incompatible @@ -515,7 +534,7 @@ def __getattr__(self, name: str) -> MarkDecorator: 2, ) - return MarkDecorator(Mark(name, (), {})) + return MarkDecorator(Mark(name, (), {}, _ispytest=True)) MARK_GEN = MarkGenerator() diff --git a/src/pytest/__init__.py b/src/pytest/__init__.py index 70177f95040..4b194e0c8e0 100644 --- a/src/pytest/__init__.py +++ b/src/pytest/__init__.py @@ -21,6 +21,7 @@ from _pytest.freeze_support import freeze_includes from _pytest.logging import LogCaptureFixture from _pytest.main import Session +from _pytest.mark import Mark from _pytest.mark import MARK_GEN as mark from _pytest.mark import param from _pytest.monkeypatch import MonkeyPatch @@ -89,6 +90,7 @@ "LogCaptureFixture", "main", "mark", + "Mark", "Module", "MonkeyPatch", "Package", From 69c302479e3f76450f29e7d2de24254d5eda6492 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sun, 20 Dec 2020 15:11:01 +0200 Subject: [PATCH 031/630] mark: export pytest.MarkDecorator for typing purposes The type cannot be constructed directly, but is exported for use in type annotations, since it is reachable through existing public API. --- changelog/7469.deprecation.rst | 1 + changelog/7469.feature.rst | 1 + doc/en/reference.rst | 2 +- src/_pytest/fixtures.py | 2 +- src/_pytest/mark/structures.py | 40 +++++++++++++++++++--------------- src/pytest/__init__.py | 2 ++ 6 files changed, 28 insertions(+), 20 deletions(-) diff --git a/changelog/7469.deprecation.rst b/changelog/7469.deprecation.rst index 79419dacef2..6922b3bbb17 100644 --- a/changelog/7469.deprecation.rst +++ b/changelog/7469.deprecation.rst @@ -1,5 +1,6 @@ Directly constructing the following classes is now deprecated: - ``_pytest.mark.structures.Mark`` +- ``_pytest.mark.structures.MarkDecorator`` These have always been considered private, but now issue a deprecation warning, which may become a hard error in pytest 7.0.0. diff --git a/changelog/7469.feature.rst b/changelog/7469.feature.rst index f4bc52414bc..66113aa580f 100644 --- a/changelog/7469.feature.rst +++ b/changelog/7469.feature.rst @@ -3,6 +3,7 @@ The types of objects used in pytest's API are now exported so they may be used i The newly-exported types are: - ``pytest.Mark`` for :class:`marks `. +- ``pytest.MarkDecorator`` for :class:`mark decorators `. Constructing them directly is not supported; they are only meant for use in type annotations. Doing so will emit a deprecation warning, and may become a hard-error in pytest 7.0. diff --git a/doc/en/reference.rst b/doc/en/reference.rst index 3fc62ee7279..8bd4111a1c0 100644 --- a/doc/en/reference.rst +++ b/doc/en/reference.rst @@ -849,7 +849,7 @@ Item MarkDecorator ~~~~~~~~~~~~~ -.. autoclass:: _pytest.mark.MarkDecorator +.. autoclass:: pytest.MarkDecorator() :members: diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index c24ab7069cb..dbb039bf2a9 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -551,7 +551,7 @@ def applymarker(self, marker: Union[str, MarkDecorator]) -> None: on all function invocations. :param marker: - A :py:class:`_pytest.mark.MarkDecorator` object created by a call + A :class:`pytest.MarkDecorator` object created by a call to ``pytest.mark.NAME(...)``. """ self.node.add_marker(marker) diff --git a/src/_pytest/mark/structures.py b/src/_pytest/mark/structures.py index 29b9586871f..8bce33e685a 100644 --- a/src/_pytest/mark/structures.py +++ b/src/_pytest/mark/structures.py @@ -268,14 +268,14 @@ def combined_with(self, other: "Mark") -> "Mark": # A generic parameter designating an object to which a Mark may # be applied -- a test function (callable) or class. # Note: a lambda is not allowed, but this can't be represented. -_Markable = TypeVar("_Markable", bound=Union[Callable[..., object], type]) +Markable = TypeVar("Markable", bound=Union[Callable[..., object], type]) -@attr.s +@attr.s(init=False, auto_attribs=True) class MarkDecorator: """A decorator for applying a mark on test functions and classes. - MarkDecorators are created with ``pytest.mark``:: + ``MarkDecorators`` are created with ``pytest.mark``:: mark1 = pytest.mark.NAME # Simple MarkDecorator mark2 = pytest.mark.NAME(name1=value) # Parametrized MarkDecorator @@ -286,7 +286,7 @@ class MarkDecorator: def test_function(): pass - When a MarkDecorator is called it does the following: + When a ``MarkDecorator`` is called, it does the following: 1. If called with a single class as its only positional argument and no additional keyword arguments, it attaches the mark to the class so it @@ -295,19 +295,24 @@ def test_function(): 2. If called with a single function as its only positional argument and no additional keyword arguments, it attaches the mark to the function, containing all the arguments already stored internally in the - MarkDecorator. + ``MarkDecorator``. - 3. When called in any other case, it returns a new MarkDecorator instance - with the original MarkDecorator's content updated with the arguments - passed to this call. + 3. When called in any other case, it returns a new ``MarkDecorator`` + instance with the original ``MarkDecorator``'s content updated with + the arguments passed to this call. - Note: The rules above prevent MarkDecorators from storing only a single - function or class reference as their positional argument with no + Note: The rules above prevent a ``MarkDecorator`` from storing only a + single function or class reference as its positional argument with no additional keyword or positional arguments. You can work around this by using `with_args()`. """ - mark = attr.ib(type=Mark, validator=attr.validators.instance_of(Mark)) + mark: Mark + + def __init__(self, mark: Mark, *, _ispytest: bool = False) -> None: + """:meta private:""" + check_ispytest(_ispytest) + self.mark = mark @property def name(self) -> str: @@ -326,6 +331,7 @@ def kwargs(self) -> Mapping[str, Any]: @property def markname(self) -> str: + """:meta private:""" return self.name # for backward-compat (2.4.1 had this attr) def __repr__(self) -> str: @@ -336,17 +342,15 @@ def with_args(self, *args: object, **kwargs: object) -> "MarkDecorator": Unlike calling the MarkDecorator, with_args() can be used even if the sole argument is a callable/class. - - :rtype: MarkDecorator """ mark = Mark(self.name, args, kwargs, _ispytest=True) - return self.__class__(self.mark.combined_with(mark)) + return MarkDecorator(self.mark.combined_with(mark), _ispytest=True) # Type ignored because the overloads overlap with an incompatible # return type. Not much we can do about that. Thankfully mypy picks # the first match so it works out even if we break the rules. @overload - def __call__(self, arg: _Markable) -> _Markable: # type: ignore[misc] + def __call__(self, arg: Markable) -> Markable: # type: ignore[misc] pass @overload @@ -405,7 +409,7 @@ def store_mark(obj, mark: Mark) -> None: class _SkipMarkDecorator(MarkDecorator): @overload # type: ignore[override,misc] - def __call__(self, arg: _Markable) -> _Markable: + def __call__(self, arg: Markable) -> Markable: ... @overload @@ -423,7 +427,7 @@ def __call__( # type: ignore[override] class _XfailMarkDecorator(MarkDecorator): @overload # type: ignore[override,misc] - def __call__(self, arg: _Markable) -> _Markable: + def __call__(self, arg: Markable) -> Markable: ... @overload @@ -534,7 +538,7 @@ def __getattr__(self, name: str) -> MarkDecorator: 2, ) - return MarkDecorator(Mark(name, (), {}, _ispytest=True)) + return MarkDecorator(Mark(name, (), {}, _ispytest=True), _ispytest=True) MARK_GEN = MarkGenerator() diff --git a/src/pytest/__init__.py b/src/pytest/__init__.py index 4b194e0c8e0..1d5b38ee091 100644 --- a/src/pytest/__init__.py +++ b/src/pytest/__init__.py @@ -23,6 +23,7 @@ from _pytest.main import Session from _pytest.mark import Mark from _pytest.mark import MARK_GEN as mark +from _pytest.mark import MarkDecorator from _pytest.mark import param from _pytest.monkeypatch import MonkeyPatch from _pytest.nodes import Collector @@ -91,6 +92,7 @@ "main", "mark", "Mark", + "MarkDecorator", "Module", "MonkeyPatch", "Package", From 6aa4d1c7ab968aecf44ad89e568a4515bd7e5343 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sun, 20 Dec 2020 15:36:24 +0200 Subject: [PATCH 032/630] mark: export pytest.MarkGenerator for typing purposes The type cannot be constructed directly, but is exported for use in type annotations, since it is reachable through existing public API. --- changelog/7469.deprecation.rst | 1 + changelog/7469.feature.rst | 1 + doc/en/reference.rst | 2 +- src/_pytest/mark/structures.py | 11 +++++++---- src/pytest/__init__.py | 2 ++ testing/test_mark.py | 4 ++-- 6 files changed, 14 insertions(+), 7 deletions(-) diff --git a/changelog/7469.deprecation.rst b/changelog/7469.deprecation.rst index 6922b3bbb17..6bbc8075522 100644 --- a/changelog/7469.deprecation.rst +++ b/changelog/7469.deprecation.rst @@ -2,5 +2,6 @@ Directly constructing the following classes is now deprecated: - ``_pytest.mark.structures.Mark`` - ``_pytest.mark.structures.MarkDecorator`` +- ``_pytest.mark.structures.MarkGenerator`` These have always been considered private, but now issue a deprecation warning, which may become a hard error in pytest 7.0.0. diff --git a/changelog/7469.feature.rst b/changelog/7469.feature.rst index 66113aa580f..81f93d1f7af 100644 --- a/changelog/7469.feature.rst +++ b/changelog/7469.feature.rst @@ -4,6 +4,7 @@ The newly-exported types are: - ``pytest.Mark`` for :class:`marks `. - ``pytest.MarkDecorator`` for :class:`mark decorators `. +- ``pytest.MarkGenerator`` for the :class:`pytest.mark ` singleton. Constructing them directly is not supported; they are only meant for use in type annotations. Doing so will emit a deprecation warning, and may become a hard-error in pytest 7.0. diff --git a/doc/en/reference.rst b/doc/en/reference.rst index 8bd4111a1c0..c8e8dca7561 100644 --- a/doc/en/reference.rst +++ b/doc/en/reference.rst @@ -856,7 +856,7 @@ MarkDecorator MarkGenerator ~~~~~~~~~~~~~ -.. autoclass:: _pytest.mark.MarkGenerator +.. autoclass:: pytest.MarkGenerator() :members: diff --git a/src/_pytest/mark/structures.py b/src/_pytest/mark/structures.py index 8bce33e685a..ae6920735a2 100644 --- a/src/_pytest/mark/structures.py +++ b/src/_pytest/mark/structures.py @@ -488,9 +488,6 @@ def test_function(): applies a 'slowtest' :class:`Mark` on ``test_function``. """ - _config: Optional[Config] = None - _markers: Set[str] = set() - # See TYPE_CHECKING above. if TYPE_CHECKING: skip: _SkipMarkDecorator @@ -500,7 +497,13 @@ def test_function(): usefixtures: _UsefixturesMarkDecorator filterwarnings: _FilterwarningsMarkDecorator + def __init__(self, *, _ispytest: bool = False) -> None: + check_ispytest(_ispytest) + self._config: Optional[Config] = None + self._markers: Set[str] = set() + def __getattr__(self, name: str) -> MarkDecorator: + """Generate a new :class:`MarkDecorator` with the given name.""" if name[0] == "_": raise AttributeError("Marker name must NOT start with underscore") @@ -541,7 +544,7 @@ def __getattr__(self, name: str) -> MarkDecorator: return MarkDecorator(Mark(name, (), {}, _ispytest=True), _ispytest=True) -MARK_GEN = MarkGenerator() +MARK_GEN = MarkGenerator(_ispytest=True) @final diff --git a/src/pytest/__init__.py b/src/pytest/__init__.py index 1d5b38ee091..74cf00ee2c4 100644 --- a/src/pytest/__init__.py +++ b/src/pytest/__init__.py @@ -24,6 +24,7 @@ from _pytest.mark import Mark from _pytest.mark import MARK_GEN as mark from _pytest.mark import MarkDecorator +from _pytest.mark import MarkGenerator from _pytest.mark import param from _pytest.monkeypatch import MonkeyPatch from _pytest.nodes import Collector @@ -93,6 +94,7 @@ "mark", "Mark", "MarkDecorator", + "MarkGenerator", "Module", "MonkeyPatch", "Package", diff --git a/testing/test_mark.py b/testing/test_mark.py index e0b91f0cef4..5f4b3e063e4 100644 --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -21,7 +21,7 @@ def test_pytest_exists_in_namespace_all(self, attr: str, modulename: str) -> Non assert attr in module.__all__ # type: ignore def test_pytest_mark_notcallable(self) -> None: - mark = MarkGenerator() + mark = MarkGenerator(_ispytest=True) with pytest.raises(TypeError): mark() # type: ignore[operator] @@ -40,7 +40,7 @@ class SomeClass: assert pytest.mark.foo.with_args(SomeClass) is not SomeClass # type: ignore[comparison-overlap] def test_pytest_mark_name_starts_with_underscore(self) -> None: - mark = MarkGenerator() + mark = MarkGenerator(_ispytest=True) with pytest.raises(AttributeError): mark._some_name From 1839713b71db4f9d656c5ae3de32a9abcaa99009 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Dec 2020 03:07:23 +0000 Subject: [PATCH 033/630] build(deps): bump pytest-mock in /testing/plugins_integration Bumps [pytest-mock](https://github.com/pytest-dev/pytest-mock) from 3.3.1 to 3.4.0. - [Release notes](https://github.com/pytest-dev/pytest-mock/releases) - [Changelog](https://github.com/pytest-dev/pytest-mock/blob/master/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest-mock/compare/v3.3.1...v3.4.0) Signed-off-by: dependabot[bot] --- testing/plugins_integration/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/plugins_integration/requirements.txt b/testing/plugins_integration/requirements.txt index 99ddef0706e..b2ca3e3236a 100644 --- a/testing/plugins_integration/requirements.txt +++ b/testing/plugins_integration/requirements.txt @@ -6,7 +6,7 @@ pytest-cov==2.10.1 pytest-django==4.1.0 pytest-flakes==4.0.3 pytest-html==3.1.1 -pytest-mock==3.3.1 +pytest-mock==3.4.0 pytest-rerunfailures==9.1.1 pytest-sugar==0.9.4 pytest-trio==0.7.0 From 92ba96b0612e6b06bb8f4ab05bd75481d2504806 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sat, 19 Dec 2020 14:11:00 +0200 Subject: [PATCH 034/630] code: convert from py.path to pathlib --- changelog/8174.trivial.rst | 5 +++ src/_pytest/_code/code.py | 55 +++++++++++++++------------- src/_pytest/fixtures.py | 8 +++- src/_pytest/nodes.py | 8 ++-- src/_pytest/python.py | 6 ++- testing/code/test_excinfo.py | 71 ++++++++++++++++++------------------ testing/code/test_source.py | 11 +++--- 7 files changed, 90 insertions(+), 74 deletions(-) create mode 100644 changelog/8174.trivial.rst diff --git a/changelog/8174.trivial.rst b/changelog/8174.trivial.rst new file mode 100644 index 00000000000..001ae4cb193 --- /dev/null +++ b/changelog/8174.trivial.rst @@ -0,0 +1,5 @@ +The following changes have been made to internal pytest types/functions: + +- The ``path`` property of ``_pytest.code.Code`` returns ``Path`` instead of ``py.path.local``. +- The ``path`` property of ``_pytest.code.TracebackEntry`` returns ``Path`` instead of ``py.path.local``. +- The ``_pytest.code.getfslineno()`` function returns ``Path`` instead of ``py.path.local``. diff --git a/src/_pytest/_code/code.py b/src/_pytest/_code/code.py index 423069330a5..043a23a79af 100644 --- a/src/_pytest/_code/code.py +++ b/src/_pytest/_code/code.py @@ -43,6 +43,8 @@ from _pytest._io.saferepr import saferepr from _pytest.compat import final from _pytest.compat import get_real_func +from _pytest.pathlib import absolutepath +from _pytest.pathlib import bestrelpath if TYPE_CHECKING: from typing_extensions import Literal @@ -78,16 +80,16 @@ def name(self) -> str: return self.raw.co_name @property - def path(self) -> Union[py.path.local, str]: + def path(self) -> Union[Path, str]: """Return a path object pointing to source code, or an ``str`` in case of ``OSError`` / non-existing file.""" if not self.raw.co_filename: return "" try: - p = py.path.local(self.raw.co_filename) + p = absolutepath(self.raw.co_filename) # maybe don't try this checking - if not p.check(): - raise OSError("py.path check failed.") + if not p.exists(): + raise OSError("path check failed.") return p except OSError: # XXX maybe try harder like the weird logic @@ -223,7 +225,7 @@ def statement(self) -> "Source": return source.getstatement(self.lineno) @property - def path(self) -> Union[py.path.local, str]: + def path(self) -> Union[Path, str]: """Path to the source code.""" return self.frame.code.path @@ -336,10 +338,10 @@ def f(cur: TracebackType) -> Iterable[TracebackEntry]: def cut( self, - path=None, + path: Optional[Union[Path, str]] = None, lineno: Optional[int] = None, firstlineno: Optional[int] = None, - excludepath: Optional[py.path.local] = None, + excludepath: Optional[Path] = None, ) -> "Traceback": """Return a Traceback instance wrapping part of this Traceback. @@ -353,17 +355,19 @@ def cut( for x in self: code = x.frame.code codepath = code.path + if path is not None and codepath != path: + continue if ( - (path is None or codepath == path) - and ( - excludepath is None - or not isinstance(codepath, py.path.local) - or not codepath.relto(excludepath) - ) - and (lineno is None or x.lineno == lineno) - and (firstlineno is None or x.frame.code.firstlineno == firstlineno) + excludepath is not None + and isinstance(codepath, Path) + and excludepath in codepath.parents ): - return Traceback(x._rawentry, self._excinfo) + continue + if lineno is not None and x.lineno != lineno: + continue + if firstlineno is not None and x.frame.code.firstlineno != firstlineno: + continue + return Traceback(x._rawentry, self._excinfo) return self @overload @@ -801,7 +805,8 @@ def repr_traceback_entry( message = "in %s" % (entry.name) else: message = excinfo and excinfo.typename or "" - path = self._makepath(entry.path) + entry_path = entry.path + path = self._makepath(entry_path) reprfileloc = ReprFileLocation(path, entry.lineno + 1, message) localsrepr = self.repr_locals(entry.locals) return ReprEntry(lines, reprargs, localsrepr, reprfileloc, style) @@ -814,15 +819,15 @@ def repr_traceback_entry( lines.extend(self.get_exconly(excinfo, indent=4)) return ReprEntry(lines, None, None, None, style) - def _makepath(self, path): - if not self.abspath: + def _makepath(self, path: Union[Path, str]) -> str: + if not self.abspath and isinstance(path, Path): try: - np = py.path.local().bestrelpath(path) + np = bestrelpath(Path.cwd(), path) except OSError: - return path + return str(path) if len(np) < len(str(path)): - path = np - return path + return np + return str(path) def repr_traceback(self, excinfo: ExceptionInfo[BaseException]) -> "ReprTraceback": traceback = excinfo.traceback @@ -1181,7 +1186,7 @@ def toterminal(self, tw: TerminalWriter) -> None: tw.line("") -def getfslineno(obj: object) -> Tuple[Union[str, py.path.local], int]: +def getfslineno(obj: object) -> Tuple[Union[str, Path], int]: """Return source location (path, lineno) for the given object. If the source cannot be determined return ("", -1). @@ -1203,7 +1208,7 @@ def getfslineno(obj: object) -> Tuple[Union[str, py.path.local], int]: except TypeError: return "", -1 - fspath = fn and py.path.local(fn) or "" + fspath = fn and absolutepath(fn) or "" lineno = -1 if fspath: try: diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index c24ab7069cb..6db1c5906f0 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -5,6 +5,7 @@ import warnings from collections import defaultdict from collections import deque +from pathlib import Path from types import TracebackType from typing import Any from typing import Callable @@ -58,6 +59,7 @@ from _pytest.outcomes import fail from _pytest.outcomes import TEST_OUTCOME from _pytest.pathlib import absolutepath +from _pytest.pathlib import bestrelpath from _pytest.store import StoreKey if TYPE_CHECKING: @@ -718,7 +720,11 @@ def _factorytraceback(self) -> List[str]: for fixturedef in self._get_fixturestack(): factory = fixturedef.func fs, lineno = getfslineno(factory) - p = self._pyfuncitem.session.fspath.bestrelpath(fs) + if isinstance(fs, Path): + session: Session = self._pyfuncitem.session + p = bestrelpath(Path(session.fspath), fs) + else: + p = fs args = _format_args(factory) lines.append("%s:%d: def %s%s" % (p, lineno + 1, factory.__name__, args)) return lines diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py index 1b3ec5571b1..da2a0a7ea79 100644 --- a/src/_pytest/nodes.py +++ b/src/_pytest/nodes.py @@ -39,7 +39,7 @@ SEP = "/" -tracebackcutdir = py.path.local(_pytest.__file__).dirpath() +tracebackcutdir = Path(_pytest.__file__).parent def iterparentnodeids(nodeid: str) -> Iterator[str]: @@ -416,9 +416,7 @@ def repr_failure( return self._repr_failure_py(excinfo, style) -def get_fslocation_from_item( - node: "Node", -) -> Tuple[Union[str, py.path.local], Optional[int]]: +def get_fslocation_from_item(node: "Node") -> Tuple[Union[str, Path], Optional[int]]: """Try to extract the actual location from a node, depending on available attributes: * "location": a pair (path, lineno) @@ -474,7 +472,7 @@ def repr_failure( # type: ignore[override] def _prunetraceback(self, excinfo: ExceptionInfo[BaseException]) -> None: if hasattr(self, "fspath"): traceback = excinfo.traceback - ntraceback = traceback.cut(path=self.fspath) + ntraceback = traceback.cut(path=Path(self.fspath)) if ntraceback == traceback: ntraceback = ntraceback.cut(excludepath=tracebackcutdir) excinfo.traceback = ntraceback.filter() diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 3ff04455fbf..27bbb24fe2c 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -340,7 +340,11 @@ def reportinfo(self) -> Tuple[Union[py.path.local, str], int, str]: fspath: Union[py.path.local, str] = file_path lineno = compat_co_firstlineno else: - fspath, lineno = getfslineno(obj) + path, lineno = getfslineno(obj) + if isinstance(path, Path): + fspath = py.path.local(path) + else: + fspath = path modpath = self.getmodpath() assert isinstance(lineno, int) return fspath, lineno, modpath diff --git a/testing/code/test_excinfo.py b/testing/code/test_excinfo.py index 44d7ab549e8..19c888403f2 100644 --- a/testing/code/test_excinfo.py +++ b/testing/code/test_excinfo.py @@ -1,7 +1,6 @@ import importlib import io import operator -import os import queue import sys import textwrap @@ -12,14 +11,14 @@ from typing import TYPE_CHECKING from typing import Union -import py - import _pytest import pytest from _pytest._code.code import ExceptionChainRepr from _pytest._code.code import ExceptionInfo from _pytest._code.code import FormattedExcinfo from _pytest._io import TerminalWriter +from _pytest.monkeypatch import MonkeyPatch +from _pytest.pathlib import bestrelpath from _pytest.pathlib import import_path from _pytest.pytester import LineMatcher from _pytest.pytester import Pytester @@ -150,9 +149,10 @@ def xyz(): " except somenoname: # type: ignore[name-defined] # noqa: F821", ] - def test_traceback_cut(self): + def test_traceback_cut(self) -> None: co = _pytest._code.Code.from_function(f) path, firstlineno = co.path, co.firstlineno + assert isinstance(path, Path) traceback = self.excinfo.traceback newtraceback = traceback.cut(path=path, firstlineno=firstlineno) assert len(newtraceback) == 1 @@ -163,11 +163,11 @@ def test_traceback_cut_excludepath(self, pytester: Pytester) -> None: p = pytester.makepyfile("def f(): raise ValueError") with pytest.raises(ValueError) as excinfo: import_path(p).f() # type: ignore[attr-defined] - basedir = py.path.local(pytest.__file__).dirpath() + basedir = Path(pytest.__file__).parent newtraceback = excinfo.traceback.cut(excludepath=basedir) for x in newtraceback: - if hasattr(x, "path"): - assert not py.path.local(x.path).relto(basedir) + assert isinstance(x.path, Path) + assert basedir not in x.path.parents assert newtraceback[-1].frame.code.path == p def test_traceback_filter(self): @@ -376,7 +376,7 @@ def test_excinfo_no_python_sourcecode(tmpdir): for item in excinfo.traceback: print(item) # XXX: for some reason jinja.Template.render is printed in full item.source # shouldn't fail - if isinstance(item.path, py.path.local) and item.path.basename == "test.txt": + if isinstance(item.path, Path) and item.path.name == "test.txt": assert str(item.source) == "{{ h()}}:" @@ -392,16 +392,16 @@ def test_entrysource_Queue_example(): assert s.startswith("def get") -def test_codepath_Queue_example(): +def test_codepath_Queue_example() -> None: try: queue.Queue().get(timeout=0.001) except queue.Empty: excinfo = _pytest._code.ExceptionInfo.from_current() entry = excinfo.traceback[-1] path = entry.path - assert isinstance(path, py.path.local) - assert path.basename.lower() == "queue.py" - assert path.check() + assert isinstance(path, Path) + assert path.name.lower() == "queue.py" + assert path.exists() def test_match_succeeds(): @@ -805,21 +805,21 @@ def entry(): raised = 0 - orig_getcwd = os.getcwd + orig_path_cwd = Path.cwd def raiseos(): nonlocal raised upframe = sys._getframe().f_back assert upframe is not None - if upframe.f_code.co_name == "checked_call": + if upframe.f_code.co_name == "_makepath": # Only raise with expected calls, but not via e.g. inspect for # py38-windows. raised += 1 raise OSError(2, "custom_oserror") - return orig_getcwd() + return orig_path_cwd() - monkeypatch.setattr(os, "getcwd", raiseos) - assert p._makepath(__file__) == __file__ + monkeypatch.setattr(Path, "cwd", raiseos) + assert p._makepath(Path(__file__)) == __file__ assert raised == 1 repr_tb = p.repr_traceback(excinfo) @@ -1015,7 +1015,9 @@ def f(): assert line.endswith("mod.py") assert tw_mock.lines[10] == ":3: ValueError" - def test_toterminal_long_filenames(self, importasmod, tw_mock): + def test_toterminal_long_filenames( + self, importasmod, tw_mock, monkeypatch: MonkeyPatch + ) -> None: mod = importasmod( """ def f(): @@ -1023,25 +1025,22 @@ def f(): """ ) excinfo = pytest.raises(ValueError, mod.f) - path = py.path.local(mod.__file__) - old = path.dirpath().chdir() - try: - repr = excinfo.getrepr(abspath=False) - repr.toterminal(tw_mock) - x = py.path.local().bestrelpath(path) - if len(x) < len(str(path)): - msg = tw_mock.get_write_msg(-2) - assert msg == "mod.py" - assert tw_mock.lines[-1] == ":3: ValueError" - - repr = excinfo.getrepr(abspath=True) - repr.toterminal(tw_mock) + path = Path(mod.__file__) + monkeypatch.chdir(path.parent) + repr = excinfo.getrepr(abspath=False) + repr.toterminal(tw_mock) + x = bestrelpath(Path.cwd(), path) + if len(x) < len(str(path)): msg = tw_mock.get_write_msg(-2) - assert msg == path - line = tw_mock.lines[-1] - assert line == ":3: ValueError" - finally: - old.chdir() + assert msg == "mod.py" + assert tw_mock.lines[-1] == ":3: ValueError" + + repr = excinfo.getrepr(abspath=True) + repr.toterminal(tw_mock) + msg = tw_mock.get_write_msg(-2) + assert msg == str(path) + line = tw_mock.lines[-1] + assert line == ":3: ValueError" @pytest.mark.parametrize( "reproptions", diff --git a/testing/code/test_source.py b/testing/code/test_source.py index 04d0ea9323d..6b8443fd243 100644 --- a/testing/code/test_source.py +++ b/testing/code/test_source.py @@ -6,13 +6,12 @@ import linecache import sys import textwrap +from pathlib import Path from types import CodeType from typing import Any from typing import Dict from typing import Optional -import py.path - import pytest from _pytest._code import Code from _pytest._code import Frame @@ -352,8 +351,8 @@ def f(x) -> None: fspath, lineno = getfslineno(f) - assert isinstance(fspath, py.path.local) - assert fspath.basename == "test_source.py" + assert isinstance(fspath, Path) + assert fspath.name == "test_source.py" assert lineno == f.__code__.co_firstlineno - 1 # see findsource class A: @@ -362,8 +361,8 @@ class A: fspath, lineno = getfslineno(A) _, A_lineno = inspect.findsource(A) - assert isinstance(fspath, py.path.local) - assert fspath.basename == "test_source.py" + assert isinstance(fspath, Path) + assert fspath.name == "test_source.py" assert lineno == A_lineno assert getfslineno(3) == ("", -1) From 8b220fad4de5e36d3b62d57ca0121b4865f7e518 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sat, 19 Dec 2020 14:48:56 +0200 Subject: [PATCH 035/630] testing/test_helpconfig: remove unclear comment --- testing/test_helpconfig.py | 1 - 1 file changed, 1 deletion(-) diff --git a/testing/test_helpconfig.py b/testing/test_helpconfig.py index c2533ef304a..9a433b1b17e 100644 --- a/testing/test_helpconfig.py +++ b/testing/test_helpconfig.py @@ -16,7 +16,6 @@ def test_version_less_verbose(pytester: Pytester, pytestconfig, monkeypatch) -> monkeypatch.delenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD") result = pytester.runpytest("--version") assert result.ret == 0 - # p = py.path.local(py.__file__).dirpath() result.stderr.fnmatch_lines([f"pytest {pytest.__version__}"]) From 170a2c50408c551c31dbe51e9572c174d0c5cdde Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sat, 19 Dec 2020 14:49:24 +0200 Subject: [PATCH 036/630] testing/test_config: check inipath instead of inifile inifile is soft-deprecated in favor of inipath. --- testing/test_config.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/testing/test_config.py b/testing/test_config.py index eacc9c9ebdd..8c1441e0680 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -11,7 +11,6 @@ from typing import Union import attr -import py.path import _pytest._code import pytest @@ -28,6 +27,7 @@ from _pytest.config.findpaths import get_common_ancestor from _pytest.config.findpaths import locate_config from _pytest.monkeypatch import MonkeyPatch +from _pytest.pathlib import absolutepath from _pytest.pytester import Pytester @@ -854,8 +854,8 @@ def test_inifilename(self, tmp_path: Path) -> None: ) ) - inifile = "../../foo/bar.ini" - option_dict = {"inifilename": inifile, "capture": "no"} + inifilename = "../../foo/bar.ini" + option_dict = {"inifilename": inifilename, "capture": "no"} cwd = tmp_path.joinpath("a/b") cwd.mkdir(parents=True) @@ -873,14 +873,14 @@ def test_inifilename(self, tmp_path: Path) -> None: with MonkeyPatch.context() as mp: mp.chdir(cwd) config = Config.fromdictargs(option_dict, ()) - inipath = py.path.local(inifile) + inipath = absolutepath(inifilename) assert config.args == [str(cwd)] - assert config.option.inifilename == inifile + assert config.option.inifilename == inifilename assert config.option.capture == "no" # this indicates this is the file used for getting configuration values - assert config.inifile == inipath + assert config.inipath == inipath assert config.inicfg.get("name") == "value" assert config.inicfg.get("should_not_be_set") is None From a21841300826fd67b43e5bb3ff1a04e11759dcc1 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sat, 19 Dec 2020 14:51:23 +0200 Subject: [PATCH 037/630] pathlib: missing type annotation for fnmatch_ex --- src/_pytest/pathlib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_pytest/pathlib.py b/src/_pytest/pathlib.py index 2e452eb1cc9..d3908a3fdc0 100644 --- a/src/_pytest/pathlib.py +++ b/src/_pytest/pathlib.py @@ -387,7 +387,7 @@ def resolve_from_str(input: str, rootpath: Path) -> Path: return rootpath.joinpath(input) -def fnmatch_ex(pattern: str, path) -> bool: +def fnmatch_ex(pattern: str, path: Union[str, "os.PathLike[str]"]) -> bool: """A port of FNMatcher from py.path.common which works with PurePath() instances. The difference between this algorithm and PurePath.match() is that the From 4faed282613dbbe7196543cc8b45885269f39d4a Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sat, 19 Dec 2020 15:16:01 +0200 Subject: [PATCH 038/630] testing: convert some tmpdir to tmp_path The tmpdir fixture (and its factory variant) is soft-deprecated in favor of the tmp_path fixture. --- testing/test_cacheprovider.py | 15 ++- testing/test_pathlib.py | 204 +++++++++++++++++++--------------- 2 files changed, 124 insertions(+), 95 deletions(-) diff --git a/testing/test_cacheprovider.py b/testing/test_cacheprovider.py index ebd455593f3..2cb657efc16 100644 --- a/testing/test_cacheprovider.py +++ b/testing/test_cacheprovider.py @@ -8,6 +8,7 @@ from _pytest.config import ExitCode from _pytest.monkeypatch import MonkeyPatch from _pytest.pytester import Pytester +from _pytest.tmpdir import TempPathFactory pytest_plugins = ("pytester",) @@ -139,9 +140,11 @@ def test_custom_rel_cache_dir(self, pytester: Pytester) -> None: pytester.runpytest() assert pytester.path.joinpath(rel_cache_dir).is_dir() - def test_custom_abs_cache_dir(self, pytester: Pytester, tmpdir_factory) -> None: - tmp = str(tmpdir_factory.mktemp("tmp")) - abs_cache_dir = os.path.join(tmp, "custom_cache_dir") + def test_custom_abs_cache_dir( + self, pytester: Pytester, tmp_path_factory: TempPathFactory + ) -> None: + tmp = tmp_path_factory.mktemp("tmp") + abs_cache_dir = tmp / "custom_cache_dir" pytester.makeini( """ [pytest] @@ -152,7 +155,7 @@ def test_custom_abs_cache_dir(self, pytester: Pytester, tmpdir_factory) -> None: ) pytester.makepyfile(test_errored="def test_error():\n assert False") pytester.runpytest() - assert Path(abs_cache_dir).is_dir() + assert abs_cache_dir.is_dir() def test_custom_cache_dir_with_env_var( self, pytester: Pytester, monkeypatch: MonkeyPatch @@ -185,9 +188,9 @@ def test_cache_reportheader(env, pytester: Pytester, monkeypatch: MonkeyPatch) - def test_cache_reportheader_external_abspath( - pytester: Pytester, tmpdir_factory + pytester: Pytester, tmp_path_factory: TempPathFactory ) -> None: - external_cache = tmpdir_factory.mktemp( + external_cache = tmp_path_factory.mktemp( "test_cache_reportheader_external_abspath_abs" ) diff --git a/testing/test_pathlib.py b/testing/test_pathlib.py index f60b9f26369..48149084ece 100644 --- a/testing/test_pathlib.py +++ b/testing/test_pathlib.py @@ -1,8 +1,11 @@ import os.path +import pickle import sys import unittest.mock from pathlib import Path from textwrap import dedent +from types import ModuleType +from typing import Generator import py @@ -20,6 +23,7 @@ from _pytest.pathlib import resolve_package_path from _pytest.pathlib import symlink_or_skip from _pytest.pathlib import visit +from _pytest.tmpdir import TempPathFactory class TestFNMatcherPort: @@ -96,38 +100,40 @@ class TestImportPath: """ @pytest.fixture(scope="session") - def path1(self, tmpdir_factory): - path = tmpdir_factory.mktemp("path") + def path1(self, tmp_path_factory: TempPathFactory) -> Generator[Path, None, None]: + path = tmp_path_factory.mktemp("path") self.setuptestfs(path) yield path - assert path.join("samplefile").check() + assert path.joinpath("samplefile").exists() - def setuptestfs(self, path): + def setuptestfs(self, path: Path) -> None: # print "setting up test fs for", repr(path) - samplefile = path.ensure("samplefile") - samplefile.write("samplefile\n") + samplefile = path / "samplefile" + samplefile.write_text("samplefile\n") - execfile = path.ensure("execfile") - execfile.write("x=42") + execfile = path / "execfile" + execfile.write_text("x=42") - execfilepy = path.ensure("execfile.py") - execfilepy.write("x=42") + execfilepy = path / "execfile.py" + execfilepy.write_text("x=42") d = {1: 2, "hello": "world", "answer": 42} - path.ensure("samplepickle").dump(d) - - sampledir = path.ensure("sampledir", dir=1) - sampledir.ensure("otherfile") - - otherdir = path.ensure("otherdir", dir=1) - otherdir.ensure("__init__.py") - - module_a = otherdir.ensure("a.py") - module_a.write("from .b import stuff as result\n") - module_b = otherdir.ensure("b.py") - module_b.write('stuff="got it"\n') - module_c = otherdir.ensure("c.py") - module_c.write( + path.joinpath("samplepickle").write_bytes(pickle.dumps(d, 1)) + + sampledir = path / "sampledir" + sampledir.mkdir() + sampledir.joinpath("otherfile").touch() + + otherdir = path / "otherdir" + otherdir.mkdir() + otherdir.joinpath("__init__.py").touch() + + module_a = otherdir / "a.py" + module_a.write_text("from .b import stuff as result\n") + module_b = otherdir / "b.py" + module_b.write_text('stuff="got it"\n') + module_c = otherdir / "c.py" + module_c.write_text( dedent( """ import py; @@ -136,8 +142,8 @@ def setuptestfs(self, path): """ ) ) - module_d = otherdir.ensure("d.py") - module_d.write( + module_d = otherdir / "d.py" + module_d.write_text( dedent( """ import py; @@ -147,122 +153,141 @@ def setuptestfs(self, path): ) ) - def test_smoke_test(self, path1): - obj = import_path(path1.join("execfile.py")) + def test_smoke_test(self, path1: Path) -> None: + obj = import_path(path1 / "execfile.py") assert obj.x == 42 # type: ignore[attr-defined] assert obj.__name__ == "execfile" - def test_renamed_dir_creates_mismatch(self, tmpdir, monkeypatch): - p = tmpdir.ensure("a", "test_x123.py") + def test_renamed_dir_creates_mismatch( + self, tmp_path: Path, monkeypatch: MonkeyPatch + ) -> None: + tmp_path.joinpath("a").mkdir() + p = tmp_path.joinpath("a", "test_x123.py") + p.touch() import_path(p) - tmpdir.join("a").move(tmpdir.join("b")) + tmp_path.joinpath("a").rename(tmp_path.joinpath("b")) with pytest.raises(ImportPathMismatchError): - import_path(tmpdir.join("b", "test_x123.py")) + import_path(tmp_path.joinpath("b", "test_x123.py")) # Errors can be ignored. monkeypatch.setenv("PY_IGNORE_IMPORTMISMATCH", "1") - import_path(tmpdir.join("b", "test_x123.py")) + import_path(tmp_path.joinpath("b", "test_x123.py")) # PY_IGNORE_IMPORTMISMATCH=0 does not ignore error. monkeypatch.setenv("PY_IGNORE_IMPORTMISMATCH", "0") with pytest.raises(ImportPathMismatchError): - import_path(tmpdir.join("b", "test_x123.py")) + import_path(tmp_path.joinpath("b", "test_x123.py")) - def test_messy_name(self, tmpdir): + def test_messy_name(self, tmp_path: Path) -> None: # http://bitbucket.org/hpk42/py-trunk/issue/129 - path = tmpdir.ensure("foo__init__.py") + path = tmp_path / "foo__init__.py" + path.touch() module = import_path(path) assert module.__name__ == "foo__init__" - def test_dir(self, tmpdir): - p = tmpdir.join("hello_123") - p_init = p.ensure("__init__.py") + def test_dir(self, tmp_path: Path) -> None: + p = tmp_path / "hello_123" + p.mkdir() + p_init = p / "__init__.py" + p_init.touch() m = import_path(p) assert m.__name__ == "hello_123" m = import_path(p_init) assert m.__name__ == "hello_123" - def test_a(self, path1): - otherdir = path1.join("otherdir") - mod = import_path(otherdir.join("a.py")) + def test_a(self, path1: Path) -> None: + otherdir = path1 / "otherdir" + mod = import_path(otherdir / "a.py") assert mod.result == "got it" # type: ignore[attr-defined] assert mod.__name__ == "otherdir.a" - def test_b(self, path1): - otherdir = path1.join("otherdir") - mod = import_path(otherdir.join("b.py")) + def test_b(self, path1: Path) -> None: + otherdir = path1 / "otherdir" + mod = import_path(otherdir / "b.py") assert mod.stuff == "got it" # type: ignore[attr-defined] assert mod.__name__ == "otherdir.b" - def test_c(self, path1): - otherdir = path1.join("otherdir") - mod = import_path(otherdir.join("c.py")) + def test_c(self, path1: Path) -> None: + otherdir = path1 / "otherdir" + mod = import_path(otherdir / "c.py") assert mod.value == "got it" # type: ignore[attr-defined] - def test_d(self, path1): - otherdir = path1.join("otherdir") - mod = import_path(otherdir.join("d.py")) + def test_d(self, path1: Path) -> None: + otherdir = path1 / "otherdir" + mod = import_path(otherdir / "d.py") assert mod.value2 == "got it" # type: ignore[attr-defined] - def test_import_after(self, tmpdir): - tmpdir.ensure("xxxpackage", "__init__.py") - mod1path = tmpdir.ensure("xxxpackage", "module1.py") + def test_import_after(self, tmp_path: Path) -> None: + tmp_path.joinpath("xxxpackage").mkdir() + tmp_path.joinpath("xxxpackage", "__init__.py").touch() + mod1path = tmp_path.joinpath("xxxpackage", "module1.py") + mod1path.touch() mod1 = import_path(mod1path) assert mod1.__name__ == "xxxpackage.module1" from xxxpackage import module1 assert module1 is mod1 - def test_check_filepath_consistency(self, monkeypatch, tmpdir): + def test_check_filepath_consistency( + self, monkeypatch: MonkeyPatch, tmp_path: Path + ) -> None: name = "pointsback123" - ModuleType = type(os) - p = tmpdir.ensure(name + ".py") + p = tmp_path.joinpath(name + ".py") + p.touch() for ending in (".pyc", ".pyo"): mod = ModuleType(name) - pseudopath = tmpdir.ensure(name + ending) + pseudopath = tmp_path.joinpath(name + ending) + pseudopath.touch() mod.__file__ = str(pseudopath) monkeypatch.setitem(sys.modules, name, mod) newmod = import_path(p) assert mod == newmod monkeypatch.undo() mod = ModuleType(name) - pseudopath = tmpdir.ensure(name + "123.py") + pseudopath = tmp_path.joinpath(name + "123.py") + pseudopath.touch() mod.__file__ = str(pseudopath) monkeypatch.setitem(sys.modules, name, mod) with pytest.raises(ImportPathMismatchError) as excinfo: import_path(p) modname, modfile, orig = excinfo.value.args assert modname == name - assert modfile == pseudopath + assert modfile == str(pseudopath) assert orig == p assert issubclass(ImportPathMismatchError, ImportError) - def test_issue131_on__init__(self, tmpdir): + def test_issue131_on__init__(self, tmp_path: Path) -> None: # __init__.py files may be namespace packages, and thus the # __file__ of an imported module may not be ourselves # see issue - p1 = tmpdir.ensure("proja", "__init__.py") - p2 = tmpdir.ensure("sub", "proja", "__init__.py") + tmp_path.joinpath("proja").mkdir() + p1 = tmp_path.joinpath("proja", "__init__.py") + p1.touch() + tmp_path.joinpath("sub", "proja").mkdir(parents=True) + p2 = tmp_path.joinpath("sub", "proja", "__init__.py") + p2.touch() m1 = import_path(p1) m2 = import_path(p2) assert m1 == m2 - def test_ensuresyspath_append(self, tmpdir): - root1 = tmpdir.mkdir("root1") - file1 = root1.ensure("x123.py") + def test_ensuresyspath_append(self, tmp_path: Path) -> None: + root1 = tmp_path / "root1" + root1.mkdir() + file1 = root1 / "x123.py" + file1.touch() assert str(root1) not in sys.path import_path(file1, mode="append") assert str(root1) == sys.path[-1] assert str(root1) not in sys.path[:-1] - def test_invalid_path(self, tmpdir): + def test_invalid_path(self, tmp_path: Path) -> None: with pytest.raises(ImportError): - import_path(tmpdir.join("invalid.py")) + import_path(tmp_path / "invalid.py") @pytest.fixture - def simple_module(self, tmpdir): - fn = tmpdir.join("mymod.py") - fn.write( + def simple_module(self, tmp_path: Path) -> Path: + fn = tmp_path / "mymod.py" + fn.write_text( dedent( """ def foo(x): return 40 + x @@ -271,19 +296,21 @@ def foo(x): return 40 + x ) return fn - def test_importmode_importlib(self, simple_module): + def test_importmode_importlib(self, simple_module: Path) -> None: """`importlib` mode does not change sys.path.""" module = import_path(simple_module, mode="importlib") assert module.foo(2) == 42 # type: ignore[attr-defined] - assert simple_module.dirname not in sys.path + assert str(simple_module.parent) not in sys.path - def test_importmode_twice_is_different_module(self, simple_module): + def test_importmode_twice_is_different_module(self, simple_module: Path) -> None: """`importlib` mode always returns a new module.""" module1 = import_path(simple_module, mode="importlib") module2 = import_path(simple_module, mode="importlib") assert module1 is not module2 - def test_no_meta_path_found(self, simple_module, monkeypatch): + def test_no_meta_path_found( + self, simple_module: Path, monkeypatch: MonkeyPatch + ) -> None: """Even without any meta_path should still import module.""" monkeypatch.setattr(sys, "meta_path", []) module = import_path(simple_module, mode="importlib") @@ -299,7 +326,7 @@ def test_no_meta_path_found(self, simple_module, monkeypatch): import_path(simple_module, mode="importlib") -def test_resolve_package_path(tmp_path): +def test_resolve_package_path(tmp_path: Path) -> None: pkg = tmp_path / "pkg1" pkg.mkdir() (pkg / "__init__.py").touch() @@ -309,7 +336,7 @@ def test_resolve_package_path(tmp_path): assert resolve_package_path(pkg.joinpath("subdir", "__init__.py")) == pkg -def test_package_unimportable(tmp_path): +def test_package_unimportable(tmp_path: Path) -> None: pkg = tmp_path / "pkg1-1" pkg.mkdir() pkg.joinpath("__init__.py").touch() @@ -323,7 +350,7 @@ def test_package_unimportable(tmp_path): assert not resolve_package_path(pkg) -def test_access_denied_during_cleanup(tmp_path, monkeypatch): +def test_access_denied_during_cleanup(tmp_path: Path, monkeypatch: MonkeyPatch) -> None: """Ensure that deleting a numbered dir does not fail because of OSErrors (#4262).""" path = tmp_path / "temp-1" path.mkdir() @@ -338,7 +365,7 @@ def renamed_failed(*args): assert not lock_path.is_file() -def test_long_path_during_cleanup(tmp_path): +def test_long_path_during_cleanup(tmp_path: Path) -> None: """Ensure that deleting long path works (particularly on Windows (#6775)).""" path = (tmp_path / ("a" * 250)).resolve() if sys.platform == "win32": @@ -354,14 +381,14 @@ def test_long_path_during_cleanup(tmp_path): assert not os.path.isdir(extended_path) -def test_get_extended_length_path_str(): +def test_get_extended_length_path_str() -> None: assert get_extended_length_path_str(r"c:\foo") == r"\\?\c:\foo" assert get_extended_length_path_str(r"\\share\foo") == r"\\?\UNC\share\foo" assert get_extended_length_path_str(r"\\?\UNC\share\foo") == r"\\?\UNC\share\foo" assert get_extended_length_path_str(r"\\?\c:\foo") == r"\\?\c:\foo" -def test_suppress_error_removing_lock(tmp_path): +def test_suppress_error_removing_lock(tmp_path: Path) -> None: """ensure_deletable should be resilient if lock file cannot be removed (#5456, #7491)""" path = tmp_path / "dir" path.mkdir() @@ -406,15 +433,14 @@ def test_commonpath() -> None: assert commonpath(path, path.parent.parent) == path.parent.parent -def test_visit_ignores_errors(tmpdir) -> None: - symlink_or_skip("recursive", tmpdir.join("recursive")) - tmpdir.join("foo").write_binary(b"") - tmpdir.join("bar").write_binary(b"") +def test_visit_ignores_errors(tmp_path: Path) -> None: + symlink_or_skip("recursive", tmp_path / "recursive") + tmp_path.joinpath("foo").write_bytes(b"") + tmp_path.joinpath("bar").write_bytes(b"") - assert [entry.name for entry in visit(tmpdir, recurse=lambda entry: False)] == [ - "bar", - "foo", - ] + assert [ + entry.name for entry in visit(str(tmp_path), recurse=lambda entry: False) + ] == ["bar", "foo"] @pytest.mark.skipif(not sys.platform.startswith("win"), reason="Windows only") From ca4effc8225edf7fc828a4291642c82349ed8107 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sat, 19 Dec 2020 14:52:10 +0200 Subject: [PATCH 039/630] Convert most of the collection code from py.path to pathlib --- changelog/8174.trivial.rst | 1 + src/_pytest/config/__init__.py | 2 +- src/_pytest/main.py | 84 +++++++++++++++++----------------- src/_pytest/python.py | 61 ++++++++++++------------ testing/test_collection.py | 4 +- 5 files changed, 77 insertions(+), 75 deletions(-) diff --git a/changelog/8174.trivial.rst b/changelog/8174.trivial.rst index 001ae4cb193..7649764618f 100644 --- a/changelog/8174.trivial.rst +++ b/changelog/8174.trivial.rst @@ -3,3 +3,4 @@ The following changes have been made to internal pytest types/functions: - The ``path`` property of ``_pytest.code.Code`` returns ``Path`` instead of ``py.path.local``. - The ``path`` property of ``_pytest.code.TracebackEntry`` returns ``Path`` instead of ``py.path.local``. - The ``_pytest.code.getfslineno()`` function returns ``Path`` instead of ``py.path.local``. +- The ``_pytest.python.path_matches_patterns()`` function takes ``Path`` instead of ``py.path.local``. diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index c9a0e78bfcf..760b0f55c7b 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -348,7 +348,7 @@ def __init__(self) -> None: self._conftestpath2mod: Dict[Path, types.ModuleType] = {} self._confcutdir: Optional[Path] = None self._noconftest = False - self._duplicatepaths: Set[py.path.local] = set() + self._duplicatepaths: Set[Path] = set() # plugins that were explicitly skipped with pytest.skip # list of (module name, skip reason) diff --git a/src/_pytest/main.py b/src/_pytest/main.py index e7c31ecc1d5..79afdde6155 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -37,6 +37,7 @@ from _pytest.outcomes import exit from _pytest.pathlib import absolutepath from _pytest.pathlib import bestrelpath +from _pytest.pathlib import fnmatch_ex from _pytest.pathlib import visit from _pytest.reports import CollectReport from _pytest.reports import TestReport @@ -353,11 +354,14 @@ def pytest_runtestloop(session: "Session") -> bool: return True -def _in_venv(path: py.path.local) -> bool: +def _in_venv(path: Path) -> bool: """Attempt to detect if ``path`` is the root of a Virtual Environment by checking for the existence of the appropriate activate script.""" - bindir = path.join("Scripts" if sys.platform.startswith("win") else "bin") - if not bindir.isdir(): + bindir = path.joinpath("Scripts" if sys.platform.startswith("win") else "bin") + try: + if not bindir.is_dir(): + return False + except OSError: return False activates = ( "activate", @@ -367,33 +371,32 @@ def _in_venv(path: py.path.local) -> bool: "Activate.bat", "Activate.ps1", ) - return any([fname.basename in activates for fname in bindir.listdir()]) + return any(fname.name in activates for fname in bindir.iterdir()) -def pytest_ignore_collect(path: py.path.local, config: Config) -> Optional[bool]: - path_ = Path(path) - ignore_paths = config._getconftest_pathlist("collect_ignore", path=path_.parent) +def pytest_ignore_collect(fspath: Path, config: Config) -> Optional[bool]: + ignore_paths = config._getconftest_pathlist("collect_ignore", path=fspath.parent) ignore_paths = ignore_paths or [] excludeopt = config.getoption("ignore") if excludeopt: ignore_paths.extend(absolutepath(x) for x in excludeopt) - if path_ in ignore_paths: + if fspath in ignore_paths: return True ignore_globs = config._getconftest_pathlist( - "collect_ignore_glob", path=path_.parent + "collect_ignore_glob", path=fspath.parent ) ignore_globs = ignore_globs or [] excludeglobopt = config.getoption("ignore_glob") if excludeglobopt: ignore_globs.extend(absolutepath(x) for x in excludeglobopt) - if any(fnmatch.fnmatch(str(path), str(glob)) for glob in ignore_globs): + if any(fnmatch.fnmatch(str(fspath), str(glob)) for glob in ignore_globs): return True allow_in_venv = config.getoption("collect_in_virtualenv") - if not allow_in_venv and _in_venv(path): + if not allow_in_venv and _in_venv(fspath): return True return None @@ -538,21 +541,21 @@ def _recurse(self, direntry: "os.DirEntry[str]") -> bool: if ihook.pytest_ignore_collect(fspath=fspath, path=path, config=self.config): return False norecursepatterns = self.config.getini("norecursedirs") - if any(path.check(fnmatch=pat) for pat in norecursepatterns): + if any(fnmatch_ex(pat, fspath) for pat in norecursepatterns): return False return True def _collectfile( - self, path: py.path.local, handle_dupes: bool = True + self, fspath: Path, handle_dupes: bool = True ) -> Sequence[nodes.Collector]: - fspath = Path(path) + path = py.path.local(fspath) assert ( - path.isfile() + fspath.is_file() ), "{!r} is not a file (isdir={!r}, exists={!r}, islink={!r})".format( - path, path.isdir(), path.exists(), path.islink() + fspath, fspath.is_dir(), fspath.exists(), fspath.is_symlink() ) - ihook = self.gethookproxy(path) - if not self.isinitpath(path): + ihook = self.gethookproxy(fspath) + if not self.isinitpath(fspath): if ihook.pytest_ignore_collect( fspath=fspath, path=path, config=self.config ): @@ -562,10 +565,10 @@ def _collectfile( keepduplicates = self.config.getoption("keepduplicates") if not keepduplicates: duplicate_paths = self.config.pluginmanager._duplicatepaths - if path in duplicate_paths: + if fspath in duplicate_paths: return () else: - duplicate_paths.add(path) + duplicate_paths.add(fspath) return ihook.pytest_collect_file(fspath=fspath, path=path, parent=self) # type: ignore[no-any-return] @@ -652,10 +655,8 @@ def collect(self) -> Iterator[Union[nodes.Item, nodes.Collector]]: from _pytest.python import Package # Keep track of any collected nodes in here, so we don't duplicate fixtures. - node_cache1: Dict[py.path.local, Sequence[nodes.Collector]] = {} - node_cache2: Dict[ - Tuple[Type[nodes.Collector], py.path.local], nodes.Collector - ] = ({}) + node_cache1: Dict[Path, Sequence[nodes.Collector]] = {} + node_cache2: Dict[Tuple[Type[nodes.Collector], Path], nodes.Collector] = ({}) # Keep track of any collected collectors in matchnodes paths, so they # are not collected more than once. @@ -679,31 +680,31 @@ def collect(self) -> Iterator[Union[nodes.Item, nodes.Collector]]: break if parent.is_dir(): - pkginit = py.path.local(parent / "__init__.py") - if pkginit.isfile() and pkginit not in node_cache1: + pkginit = parent / "__init__.py" + if pkginit.is_file() and pkginit not in node_cache1: col = self._collectfile(pkginit, handle_dupes=False) if col: if isinstance(col[0], Package): pkg_roots[str(parent)] = col[0] - node_cache1[col[0].fspath] = [col[0]] + node_cache1[Path(col[0].fspath)] = [col[0]] # If it's a directory argument, recurse and look for any Subpackages. # Let the Package collector deal with subnodes, don't collect here. if argpath.is_dir(): assert not names, "invalid arg {!r}".format((argpath, names)) - seen_dirs: Set[py.path.local] = set() + seen_dirs: Set[Path] = set() for direntry in visit(str(argpath), self._recurse): if not direntry.is_file(): continue - path = py.path.local(direntry.path) - dirpath = path.dirpath() + path = Path(direntry.path) + dirpath = path.parent if dirpath not in seen_dirs: # Collect packages first. seen_dirs.add(dirpath) - pkginit = dirpath.join("__init__.py") + pkginit = dirpath / "__init__.py" if pkginit.exists(): for x in self._collectfile(pkginit): yield x @@ -714,23 +715,22 @@ def collect(self) -> Iterator[Union[nodes.Item, nodes.Collector]]: continue for x in self._collectfile(path): - key = (type(x), x.fspath) - if key in node_cache2: - yield node_cache2[key] + key2 = (type(x), Path(x.fspath)) + if key2 in node_cache2: + yield node_cache2[key2] else: - node_cache2[key] = x + node_cache2[key2] = x yield x else: assert argpath.is_file() - argpath_ = py.path.local(argpath) - if argpath_ in node_cache1: - col = node_cache1[argpath_] + if argpath in node_cache1: + col = node_cache1[argpath] else: - collect_root = pkg_roots.get(argpath_.dirname, self) - col = collect_root._collectfile(argpath_, handle_dupes=False) + collect_root = pkg_roots.get(str(argpath.parent), self) + col = collect_root._collectfile(argpath, handle_dupes=False) if col: - node_cache1[argpath_] = col + node_cache1[argpath] = col matching = [] work: List[ @@ -846,7 +846,7 @@ def resolve_collection_argument( This function ensures the path exists, and returns a tuple: - (py.path.path("/full/path/to/pkg/tests/test_foo.py"), ["TestClass", "test_foo"]) + (Path("/full/path/to/pkg/tests/test_foo.py"), ["TestClass", "test_foo"]) When as_pypath is True, expects that the command-line argument actually contains module paths instead of file-system paths: diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 27bbb24fe2c..b4605092000 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -66,6 +66,8 @@ from _pytest.mark.structures import normalize_mark_list from _pytest.outcomes import fail from _pytest.outcomes import skip +from _pytest.pathlib import bestrelpath +from _pytest.pathlib import fnmatch_ex from _pytest.pathlib import import_path from _pytest.pathlib import ImportPathMismatchError from _pytest.pathlib import parts @@ -190,11 +192,10 @@ def pytest_pyfunc_call(pyfuncitem: "Function") -> Optional[object]: def pytest_collect_file( fspath: Path, path: py.path.local, parent: nodes.Collector ) -> Optional["Module"]: - ext = path.ext - if ext == ".py": + if fspath.suffix == ".py": if not parent.session.isinitpath(fspath): if not path_matches_patterns( - path, parent.config.getini("python_files") + ["__init__.py"] + fspath, parent.config.getini("python_files") + ["__init__.py"] ): return None ihook = parent.session.gethookproxy(fspath) @@ -205,13 +206,13 @@ def pytest_collect_file( return None -def path_matches_patterns(path: py.path.local, patterns: Iterable[str]) -> bool: +def path_matches_patterns(path: Path, patterns: Iterable[str]) -> bool: """Return whether path matches any of the patterns in the list of globs given.""" - return any(path.fnmatch(pattern) for pattern in patterns) + return any(fnmatch_ex(pattern, path) for pattern in patterns) -def pytest_pycollect_makemodule(path: py.path.local, parent) -> "Module": - if path.basename == "__init__.py": +def pytest_pycollect_makemodule(fspath: Path, path: py.path.local, parent) -> "Module": + if fspath.name == "__init__.py": pkg: Package = Package.from_parent(parent, fspath=path) return pkg mod: Module = Module.from_parent(parent, fspath=path) @@ -677,21 +678,21 @@ def _recurse(self, direntry: "os.DirEntry[str]") -> bool: if ihook.pytest_ignore_collect(fspath=fspath, path=path, config=self.config): return False norecursepatterns = self.config.getini("norecursedirs") - if any(path.check(fnmatch=pat) for pat in norecursepatterns): + if any(fnmatch_ex(pat, fspath) for pat in norecursepatterns): return False return True def _collectfile( - self, path: py.path.local, handle_dupes: bool = True + self, fspath: Path, handle_dupes: bool = True ) -> Sequence[nodes.Collector]: - fspath = Path(path) + path = py.path.local(fspath) assert ( - path.isfile() + fspath.is_file() ), "{!r} is not a file (isdir={!r}, exists={!r}, islink={!r})".format( - path, path.isdir(), path.exists(), path.islink() + path, fspath.is_dir(), fspath.exists(), fspath.is_symlink() ) - ihook = self.session.gethookproxy(path) - if not self.session.isinitpath(path): + ihook = self.session.gethookproxy(fspath) + if not self.session.isinitpath(fspath): if ihook.pytest_ignore_collect( fspath=fspath, path=path, config=self.config ): @@ -701,32 +702,32 @@ def _collectfile( keepduplicates = self.config.getoption("keepduplicates") if not keepduplicates: duplicate_paths = self.config.pluginmanager._duplicatepaths - if path in duplicate_paths: + if fspath in duplicate_paths: return () else: - duplicate_paths.add(path) + duplicate_paths.add(fspath) return ihook.pytest_collect_file(fspath=fspath, path=path, parent=self) # type: ignore[no-any-return] def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]: - this_path = self.fspath.dirpath() - init_module = this_path.join("__init__.py") - if init_module.check(file=1) and path_matches_patterns( + this_path = Path(self.fspath).parent + init_module = this_path / "__init__.py" + if init_module.is_file() and path_matches_patterns( init_module, self.config.getini("python_files") ): - yield Module.from_parent(self, fspath=init_module) - pkg_prefixes: Set[py.path.local] = set() + yield Module.from_parent(self, fspath=py.path.local(init_module)) + pkg_prefixes: Set[Path] = set() for direntry in visit(str(this_path), recurse=self._recurse): - path = py.path.local(direntry.path) + path = Path(direntry.path) # We will visit our own __init__.py file, in which case we skip it. if direntry.is_file(): - if direntry.name == "__init__.py" and path.dirpath() == this_path: + if direntry.name == "__init__.py" and path.parent == this_path: continue parts_ = parts(direntry.path) if any( - str(pkg_prefix) in parts_ and pkg_prefix.join("__init__.py") != path + str(pkg_prefix) in parts_ and pkg_prefix / "__init__.py" != path for pkg_prefix in pkg_prefixes ): continue @@ -736,7 +737,7 @@ def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]: elif not direntry.is_dir(): # Broken symlink or invalid/missing file. continue - elif path.join("__init__.py").check(file=1): + elif path.joinpath("__init__.py").is_file(): pkg_prefixes.add(path) @@ -1416,13 +1417,13 @@ def _show_fixtures_per_test(config: Config, session: Session) -> None: import _pytest.config session.perform_collect() - curdir = py.path.local() + curdir = Path.cwd() tw = _pytest.config.create_terminal_writer(config) verbose = config.getvalue("verbose") - def get_best_relpath(func): + def get_best_relpath(func) -> str: loc = getlocation(func, str(curdir)) - return curdir.bestrelpath(py.path.local(loc)) + return bestrelpath(curdir, Path(loc)) def write_fixture(fixture_def: fixtures.FixtureDef[object]) -> None: argname = fixture_def.argname @@ -1472,7 +1473,7 @@ def _showfixtures_main(config: Config, session: Session) -> None: import _pytest.config session.perform_collect() - curdir = py.path.local() + curdir = Path.cwd() tw = _pytest.config.create_terminal_writer(config) verbose = config.getvalue("verbose") @@ -1494,7 +1495,7 @@ def _showfixtures_main(config: Config, session: Session) -> None: ( len(fixturedef.baseid), fixturedef.func.__module__, - curdir.bestrelpath(py.path.local(loc)), + bestrelpath(curdir, Path(loc)), fixturedef.argname, fixturedef, ) diff --git a/testing/test_collection.py b/testing/test_collection.py index 9733b4fbd47..3dd9283eced 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -212,12 +212,12 @@ def test__in_venv(self, pytester: Pytester, fname: str) -> None: bindir = "Scripts" if sys.platform.startswith("win") else "bin" # no bin/activate, not a virtualenv base_path = pytester.mkdir("venv") - assert _in_venv(py.path.local(base_path)) is False + assert _in_venv(base_path) is False # with bin/activate, totally a virtualenv bin_path = base_path.joinpath(bindir) bin_path.mkdir() bin_path.joinpath(fname).touch() - assert _in_venv(py.path.local(base_path)) is True + assert _in_venv(base_path) is True def test_custom_norecursedirs(self, pytester: Pytester) -> None: pytester.makeini( From 5e323becb7df4ecafc7fba16b31a5a8f83d5e28d Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Tue, 22 Dec 2020 21:13:34 +0200 Subject: [PATCH 040/630] Revert "doc: temporary workaround for pytest-pygments lexing error" Support was added in pytest-pygments 2.2.0. This reverts commit 0feeddf8edb87052402fafe690d019e3eb75dfa4. --- doc/en/fixture.rst | 2 +- doc/en/requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/en/fixture.rst b/doc/en/fixture.rst index c74984563ab..752385adc89 100644 --- a/doc/en/fixture.rst +++ b/doc/en/fixture.rst @@ -1975,7 +1975,7 @@ Example: Running this test will *skip* the invocation of ``data_set`` with value ``2``: -.. code-block:: +.. code-block:: pytest $ pytest test_fixture_marks.py -v =========================== test session starts ============================ diff --git a/doc/en/requirements.txt b/doc/en/requirements.txt index fa37acfb447..20246acb750 100644 --- a/doc/en/requirements.txt +++ b/doc/en/requirements.txt @@ -1,5 +1,5 @@ pallets-sphinx-themes -pygments-pytest>=1.1.0 +pygments-pytest>=2.2.0 sphinx-removed-in>=0.2.0 sphinx>=3.1,<4 sphinxcontrib-trio From 35d6a7e78e016b0bbdbbbb7674cbe2207155ca49 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Tue, 22 Dec 2020 14:57:04 -0800 Subject: [PATCH 041/630] Add badge for pre-commit.ci See #8186 --- README.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.rst b/README.rst index 46b07e59d15..778faf89e50 100644 --- a/README.rst +++ b/README.rst @@ -23,6 +23,10 @@ .. image:: https://github.com/pytest-dev/pytest/workflows/main/badge.svg :target: https://github.com/pytest-dev/pytest/actions?query=workflow%3Amain +.. image:: https://results.pre-commit.ci/badge/github/pytest-dev/pytest/master.svg + :target: https://results.pre-commit.ci/latest/github/pytest-dev/pytest/master + :alt: pre-commit.ci status + .. image:: https://img.shields.io/badge/code%20style-black-000000.svg :target: https://github.com/psf/black From 6d3a66d947a57fed99dcb4bae47062cd9ce6a5f2 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sat, 26 Dec 2020 19:54:07 +0200 Subject: [PATCH 042/630] nodes: avoid needing to expose NodeKeywords for typing It adds no value over exporting just the ABC so do that to reduce the API surface. --- src/_pytest/nodes.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py index da2a0a7ea79..c6eb49dec4a 100644 --- a/src/_pytest/nodes.py +++ b/src/_pytest/nodes.py @@ -1,10 +1,12 @@ import os import warnings from pathlib import Path +from typing import Any from typing import Callable from typing import Iterable from typing import Iterator from typing import List +from typing import MutableMapping from typing import Optional from typing import overload from typing import Set @@ -148,8 +150,9 @@ def __init__( #: Filesystem path where this node was collected from (can be None). self.fspath = fspath or getattr(parent, "fspath", None) + # The explicit annotation is to avoid publicly exposing NodeKeywords. #: Keywords/markers collected from all scopes. - self.keywords = NodeKeywords(self) + self.keywords: MutableMapping[str, Any] = NodeKeywords(self) #: The marker objects belonging to this node. self.own_markers: List[Mark] = [] From bd76042344b3c3318dddf991c08d49bbce2251bb Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sat, 26 Dec 2020 20:49:17 +0200 Subject: [PATCH 043/630] python: export pytest.Metafunc for typing purposes The type cannot be constructed directly, but is exported for use in type annotations, since it is reachable through existing public API. --- changelog/7469.deprecation.rst | 1 + changelog/7469.feature.rst | 1 + doc/en/deprecations.rst | 4 ++-- doc/en/funcarg_compare.rst | 4 ++-- doc/en/reference.rst | 4 ++-- src/_pytest/python.py | 12 +++++++++++- src/pytest/__init__.py | 2 ++ testing/python/metafunc.py | 2 +- 8 files changed, 22 insertions(+), 8 deletions(-) diff --git a/changelog/7469.deprecation.rst b/changelog/7469.deprecation.rst index 6bbc8075522..bcf4266d8bd 100644 --- a/changelog/7469.deprecation.rst +++ b/changelog/7469.deprecation.rst @@ -3,5 +3,6 @@ Directly constructing the following classes is now deprecated: - ``_pytest.mark.structures.Mark`` - ``_pytest.mark.structures.MarkDecorator`` - ``_pytest.mark.structures.MarkGenerator`` +- ``_pytest.python.Metafunc`` These have always been considered private, but now issue a deprecation warning, which may become a hard error in pytest 7.0.0. diff --git a/changelog/7469.feature.rst b/changelog/7469.feature.rst index 81f93d1f7af..0ab2b48c42e 100644 --- a/changelog/7469.feature.rst +++ b/changelog/7469.feature.rst @@ -5,6 +5,7 @@ The newly-exported types are: - ``pytest.Mark`` for :class:`marks `. - ``pytest.MarkDecorator`` for :class:`mark decorators `. - ``pytest.MarkGenerator`` for the :class:`pytest.mark ` singleton. +- ``pytest.Metafunc`` for the :class:`metafunc ` argument to the `pytest_generate_tests ` hook. Constructing them directly is not supported; they are only meant for use in type annotations. Doing so will emit a deprecation warning, and may become a hard-error in pytest 7.0. diff --git a/doc/en/deprecations.rst b/doc/en/deprecations.rst index 5ef1053e0b4..ec2397e596f 100644 --- a/doc/en/deprecations.rst +++ b/doc/en/deprecations.rst @@ -397,8 +397,8 @@ Metafunc.addcall .. versionremoved:: 4.0 -``_pytest.python.Metafunc.addcall`` was a precursor to the current parametrized mechanism. Users should use -:meth:`_pytest.python.Metafunc.parametrize` instead. +``Metafunc.addcall`` was a precursor to the current parametrized mechanism. Users should use +:meth:`pytest.Metafunc.parametrize` instead. Example: diff --git a/doc/en/funcarg_compare.rst b/doc/en/funcarg_compare.rst index 0c4913edff8..5e2a050063c 100644 --- a/doc/en/funcarg_compare.rst +++ b/doc/en/funcarg_compare.rst @@ -47,7 +47,7 @@ There are several limitations and difficulties with this approach: 2. parametrizing the "db" resource is not straight forward: you need to apply a "parametrize" decorator or implement a :py:func:`~hookspec.pytest_generate_tests` hook - calling :py:func:`~python.Metafunc.parametrize` which + calling :py:func:`~pytest.Metafunc.parametrize` which performs parametrization at the places where the resource is used. Moreover, you need to modify the factory to use an ``extrakey`` parameter containing ``request.param`` to the @@ -113,7 +113,7 @@ This new way of parametrizing funcarg factories should in many cases allow to re-use already written factories because effectively ``request.param`` was already used when test functions/classes were parametrized via -:py:func:`metafunc.parametrize(indirect=True) <_pytest.python.Metafunc.parametrize>` calls. +:py:func:`metafunc.parametrize(indirect=True) ` calls. Of course it's perfectly fine to combine parametrization and scoping: diff --git a/doc/en/reference.rst b/doc/en/reference.rst index c8e8dca7561..7f2ae01058f 100644 --- a/doc/en/reference.rst +++ b/doc/en/reference.rst @@ -138,7 +138,7 @@ pytest.mark.parametrize **Tutorial**: :doc:`parametrize`. -This mark has the same signature as :py:meth:`_pytest.python.Metafunc.parametrize`; see there. +This mark has the same signature as :py:meth:`pytest.Metafunc.parametrize`; see there. .. _`pytest.mark.skip ref`: @@ -870,7 +870,7 @@ Mark Metafunc ~~~~~~~~ -.. autoclass:: _pytest.python.Metafunc +.. autoclass:: pytest.Metafunc() :members: Module diff --git a/src/_pytest/python.py b/src/_pytest/python.py index b4605092000..31d91853f2f 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -55,6 +55,7 @@ from _pytest.config import ExitCode from _pytest.config import hookimpl from _pytest.config.argparsing import Parser +from _pytest.deprecated import check_ispytest from _pytest.deprecated import FSCOLLECTOR_GETHOOKPROXY_ISINITPATH from _pytest.fixtures import FuncFixtureInfo from _pytest.main import Session @@ -467,7 +468,12 @@ def _genfunctions(self, name: str, funcobj) -> Iterator["Function"]: fixtureinfo = definition._fixtureinfo metafunc = Metafunc( - definition, fixtureinfo, self.config, cls=cls, module=module + definition=definition, + fixtureinfo=fixtureinfo, + config=self.config, + cls=cls, + module=module, + _ispytest=True, ) methods = [] if hasattr(module, "pytest_generate_tests"): @@ -971,7 +977,11 @@ def __init__( config: Config, cls=None, module=None, + *, + _ispytest: bool = False, ) -> None: + check_ispytest(_ispytest) + #: Access to the underlying :class:`_pytest.python.FunctionDefinition`. self.definition = definition diff --git a/src/pytest/__init__.py b/src/pytest/__init__.py index 74cf00ee2c4..f97b0ac2e8c 100644 --- a/src/pytest/__init__.py +++ b/src/pytest/__init__.py @@ -40,6 +40,7 @@ from _pytest.python import Class from _pytest.python import Function from _pytest.python import Instance +from _pytest.python import Metafunc from _pytest.python import Module from _pytest.python import Package from _pytest.python_api import approx @@ -95,6 +96,7 @@ "Mark", "MarkDecorator", "MarkGenerator", + "Metafunc", "Module", "MonkeyPatch", "Package", diff --git a/testing/python/metafunc.py b/testing/python/metafunc.py index c50ea53d255..58a902a3a59 100644 --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -47,7 +47,7 @@ class DefinitionMock(python.FunctionDefinition): names = getfuncargnames(func) fixtureinfo: Any = FuncFixtureInfoMock(names) definition: Any = DefinitionMock._create(func, "mock::nodeid") - return python.Metafunc(definition, fixtureinfo, config) + return python.Metafunc(definition, fixtureinfo, config, _ispytest=True) def test_no_funcargs(self) -> None: def function(): From 96ea867fec556a8d0e2b60392927572da38c88df Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sat, 26 Dec 2020 21:23:23 +0200 Subject: [PATCH 044/630] runner: export pytest.CallInfo for typing purposes The type cannot be constructed directly, but is exported for use in type annotations, since it is reachable through existing public API. This also documents `from_call` as public, because at least pytest-forked uses it, so we must treat it as public already anyway. --- changelog/7469.deprecation.rst | 1 + changelog/7469.feature.rst | 1 + doc/en/reference.rst | 2 +- src/_pytest/runner.py | 73 +++++++++++++++++++++++----------- src/pytest/__init__.py | 2 + 5 files changed, 54 insertions(+), 25 deletions(-) diff --git a/changelog/7469.deprecation.rst b/changelog/7469.deprecation.rst index bcf4266d8bd..0d7908ef8ac 100644 --- a/changelog/7469.deprecation.rst +++ b/changelog/7469.deprecation.rst @@ -4,5 +4,6 @@ Directly constructing the following classes is now deprecated: - ``_pytest.mark.structures.MarkDecorator`` - ``_pytest.mark.structures.MarkGenerator`` - ``_pytest.python.Metafunc`` +- ``_pytest.runner.CallInfo`` These have always been considered private, but now issue a deprecation warning, which may become a hard error in pytest 7.0.0. diff --git a/changelog/7469.feature.rst b/changelog/7469.feature.rst index 0ab2b48c42e..f9948d686f9 100644 --- a/changelog/7469.feature.rst +++ b/changelog/7469.feature.rst @@ -6,6 +6,7 @@ The newly-exported types are: - ``pytest.MarkDecorator`` for :class:`mark decorators `. - ``pytest.MarkGenerator`` for the :class:`pytest.mark ` singleton. - ``pytest.Metafunc`` for the :class:`metafunc ` argument to the `pytest_generate_tests ` hook. +- ``pytest.runner.CallInfo`` for the :class:`CallInfo ` type passed to various hooks. Constructing them directly is not supported; they are only meant for use in type annotations. Doing so will emit a deprecation warning, and may become a hard-error in pytest 7.0. diff --git a/doc/en/reference.rst b/doc/en/reference.rst index 7f2ae01058f..bc6c5670a5c 100644 --- a/doc/en/reference.rst +++ b/doc/en/reference.rst @@ -758,7 +758,7 @@ Full reference to objects accessible from :ref:`fixtures ` or :ref:`hoo CallInfo ~~~~~~~~ -.. autoclass:: _pytest.runner.CallInfo() +.. autoclass:: pytest.CallInfo() :members: diff --git a/src/_pytest/runner.py b/src/_pytest/runner.py index 794690ddb0b..df046a78aca 100644 --- a/src/_pytest/runner.py +++ b/src/_pytest/runner.py @@ -26,6 +26,7 @@ from _pytest._code.code import TerminalRepr from _pytest.compat import final from _pytest.config.argparsing import Parser +from _pytest.deprecated import check_ispytest from _pytest.nodes import Collector from _pytest.nodes import Item from _pytest.nodes import Node @@ -260,34 +261,47 @@ def call_runtest_hook( @final -@attr.s(repr=False) +@attr.s(repr=False, init=False, auto_attribs=True) class CallInfo(Generic[TResult]): - """Result/Exception info a function invocation. - - :param T result: - The return value of the call, if it didn't raise. Can only be - accessed if excinfo is None. - :param Optional[ExceptionInfo] excinfo: - The captured exception of the call, if it raised. - :param float start: - The system time when the call started, in seconds since the epoch. - :param float stop: - The system time when the call ended, in seconds since the epoch. - :param float duration: - The call duration, in seconds. - :param str when: - The context of invocation: "setup", "call", "teardown", ... - """ - - _result = attr.ib(type="Optional[TResult]") - excinfo = attr.ib(type=Optional[ExceptionInfo[BaseException]]) - start = attr.ib(type=float) - stop = attr.ib(type=float) - duration = attr.ib(type=float) - when = attr.ib(type="Literal['collect', 'setup', 'call', 'teardown']") + """Result/Exception info of a function invocation.""" + + _result: Optional[TResult] + #: The captured exception of the call, if it raised. + excinfo: Optional[ExceptionInfo[BaseException]] + #: The system time when the call started, in seconds since the epoch. + start: float + #: The system time when the call ended, in seconds since the epoch. + stop: float + #: The call duration, in seconds. + duration: float + #: The context of invocation: "collect", "setup", "call" or "teardown". + when: "Literal['collect', 'setup', 'call', 'teardown']" + + def __init__( + self, + result: Optional[TResult], + excinfo: Optional[ExceptionInfo[BaseException]], + start: float, + stop: float, + duration: float, + when: "Literal['collect', 'setup', 'call', 'teardown']", + *, + _ispytest: bool = False, + ) -> None: + check_ispytest(_ispytest) + self._result = result + self.excinfo = excinfo + self.start = start + self.stop = stop + self.duration = duration + self.when = when @property def result(self) -> TResult: + """The return value of the call, if it didn't raise. + + Can only be accessed if excinfo is None. + """ if self.excinfo is not None: raise AttributeError(f"{self!r} has no valid result") # The cast is safe because an exception wasn't raised, hence @@ -304,6 +318,16 @@ def from_call( Union[Type[BaseException], Tuple[Type[BaseException], ...]] ] = None, ) -> "CallInfo[TResult]": + """Call func, wrapping the result in a CallInfo. + + :param func: + The function to call. Called without arguments. + :param when: + The phase in which the function is called. + :param reraise: + Exception or exceptions that shall propagate if raised by the + function, instead of being wrapped in the CallInfo. + """ excinfo = None start = timing.time() precise_start = timing.perf_counter() @@ -325,6 +349,7 @@ def from_call( when=when, result=result, excinfo=excinfo, + _ispytest=True, ) def __repr__(self) -> str: diff --git a/src/pytest/__init__.py b/src/pytest/__init__.py index f97b0ac2e8c..53917340fd7 100644 --- a/src/pytest/__init__.py +++ b/src/pytest/__init__.py @@ -48,6 +48,7 @@ from _pytest.recwarn import deprecated_call from _pytest.recwarn import WarningsRecorder from _pytest.recwarn import warns +from _pytest.runner import CallInfo from _pytest.tmpdir import TempdirFactory from _pytest.tmpdir import TempPathFactory from _pytest.warning_types import PytestAssertRewriteWarning @@ -69,6 +70,7 @@ "_fillfuncargs", "approx", "Cache", + "CallInfo", "CaptureFixture", "Class", "cmdline", From 8550c29180afb6f1138c81645f7d92f644aaf74d Mon Sep 17 00:00:00 2001 From: antonblr Date: Tue, 22 Dec 2020 20:27:00 -0800 Subject: [PATCH 045/630] coverage: Include code that runs in subprocesses --- .github/workflows/main.yml | 17 +++------ scripts/append_codecov_token.py | 36 ------------------- ...{report-coverage.sh => upload-coverage.sh} | 2 -- 3 files changed, 4 insertions(+), 51 deletions(-) delete mode 100644 scripts/append_codecov_token.py rename scripts/{report-coverage.sh => upload-coverage.sh} (88%) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index beb50178528..a3ea24b7cb0 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -137,22 +137,13 @@ jobs: - name: Test with coverage if: "matrix.use_coverage" - env: - _PYTEST_TOX_COVERAGE_RUN: "coverage run -m" - COVERAGE_PROCESS_START: ".coveragerc" - _PYTEST_TOX_EXTRA_DEP: "coverage-enable-subprocess" - run: "tox -e ${{ matrix.tox_env }}" - - - name: Prepare coverage token - if: (matrix.use_coverage && ( github.repository == 'pytest-dev/pytest' || github.event_name == 'pull_request' )) - run: | - python scripts/append_codecov_token.py + run: "tox -e ${{ matrix.tox_env }}-coverage" - - name: Report coverage - if: (matrix.use_coverage) + - name: Upload coverage + if: matrix.use_coverage && github.repository == 'pytest-dev/pytest' env: CODECOV_NAME: ${{ matrix.name }} - run: bash scripts/report-coverage.sh -F GHA,${{ runner.os }} + run: bash scripts/upload-coverage.sh -F GHA,${{ runner.os }} linting: runs-on: ubuntu-latest diff --git a/scripts/append_codecov_token.py b/scripts/append_codecov_token.py deleted file mode 100644 index 5c617aafb54..00000000000 --- a/scripts/append_codecov_token.py +++ /dev/null @@ -1,36 +0,0 @@ -""" -Appends the codecov token to the 'codecov.yml' file at the root of the repository. - -This is done by CI during PRs and builds on the pytest-dev repository so we can -upload coverage, at least until codecov grows some native integration with GitHub Actions. - -See discussion in https://github.com/pytest-dev/pytest/pull/6441 for more information. -""" -import os.path -from textwrap import dedent - - -def main(): - this_dir = os.path.dirname(__file__) - cov_file = os.path.join(this_dir, "..", "codecov.yml") - - assert os.path.isfile(cov_file), "{cov_file} does not exist".format( - cov_file=cov_file - ) - - with open(cov_file, "a") as f: - # token from: https://codecov.io/gh/pytest-dev/pytest/settings - # use same URL to regenerate it if needed - text = dedent( - """ - codecov: - token: "1eca3b1f-31a2-4fb8-a8c3-138b441b50a7" - """ - ) - f.write(text) - - print("Token updated:", cov_file) - - -if __name__ == "__main__": - main() diff --git a/scripts/report-coverage.sh b/scripts/upload-coverage.sh similarity index 88% rename from scripts/report-coverage.sh rename to scripts/upload-coverage.sh index fbcf20ca929..ad3dd482814 100755 --- a/scripts/report-coverage.sh +++ b/scripts/upload-coverage.sh @@ -10,9 +10,7 @@ else PATH="$PWD/.tox/${TOXENV##*,}/bin:$PATH" fi -python -m coverage combine python -m coverage xml -python -m coverage report -m # Set --connect-timeout to work around https://github.com/curl/curl/issues/4461 curl -S -L --connect-timeout 5 --retry 6 -s https://codecov.io/bash -o codecov-upload.sh bash codecov-upload.sh -Z -X fix -f coverage.xml "$@" From bf9b59b3c8571b26f65159ed3fec831eae434561 Mon Sep 17 00:00:00 2001 From: Christophe Bedard Date: Sun, 27 Dec 2020 09:55:21 -0500 Subject: [PATCH 046/630] Add missing space in '--version' help message --- src/_pytest/helpconfig.py | 2 +- testing/test_helpconfig.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/_pytest/helpconfig.py b/src/_pytest/helpconfig.py index 4384d07b261..b9360cecf67 100644 --- a/src/_pytest/helpconfig.py +++ b/src/_pytest/helpconfig.py @@ -51,7 +51,7 @@ def pytest_addoption(parser: Parser) -> None: action="count", default=0, dest="version", - help="display pytest version and information about plugins." + help="display pytest version and information about plugins. " "When given twice, also display information about plugins.", ) group._addoption( diff --git a/testing/test_helpconfig.py b/testing/test_helpconfig.py index 9a433b1b17e..571a4783e67 100644 --- a/testing/test_helpconfig.py +++ b/testing/test_helpconfig.py @@ -28,6 +28,9 @@ def test_help(pytester: Pytester) -> None: For example: -m 'mark1 and not mark2'. reporting: --durations=N * + -V, --version display pytest version and information about plugins. + When given twice, also display information about + plugins. *setup.cfg* *minversion* *to see*markers*pytest --markers* From ee03e31831900c3a7aba9f94a9693a833a3ab9de Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 30 Dec 2020 11:56:09 +0200 Subject: [PATCH 047/630] [pre-commit.ci] pre-commit autoupdate (#8201) * [pre-commit.ci] pre-commit autoupdate * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * manual fixes after configuration update * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Anthony Sottile --- .pre-commit-config.yaml | 20 +++++----- doc/en/doctest.rst | 2 +- doc/en/writing_plugins.rst | 3 +- doc/en/xunit_setup.rst | 14 +++---- scripts/prepare-release-pr.py | 10 ++++- scripts/release-on-comment.py | 5 ++- src/_pytest/_code/code.py | 6 +-- src/_pytest/_io/saferepr.py | 7 +++- src/_pytest/cacheprovider.py | 3 +- src/_pytest/capture.py | 10 ++++- src/_pytest/compat.py | 3 +- src/_pytest/config/__init__.py | 39 ++++++++++++++----- src/_pytest/config/findpaths.py | 4 +- src/_pytest/doctest.py | 19 ++++++--- src/_pytest/fixtures.py | 21 +++++++--- src/_pytest/freeze_support.py | 3 +- src/_pytest/hookspec.py | 25 +++++++----- src/_pytest/junitxml.py | 2 +- src/_pytest/logging.py | 6 ++- src/_pytest/main.py | 8 ++-- src/_pytest/mark/__init__.py | 5 ++- src/_pytest/mark/expression.py | 10 +++-- src/_pytest/mark/structures.py | 8 +--- src/_pytest/monkeypatch.py | 14 +++++-- src/_pytest/nodes.py | 5 ++- src/_pytest/pytester.py | 19 ++++++--- src/_pytest/python.py | 11 ++++-- src/_pytest/reports.py | 2 +- src/_pytest/terminal.py | 7 +++- src/_pytest/threadexception.py | 4 +- src/_pytest/tmpdir.py | 5 ++- .../test_compare_recursive_dataclasses.py | 10 ++++- testing/io/test_terminalwriter.py | 15 +++++-- testing/python/approx.py | 3 +- testing/python/metafunc.py | 10 ++++- testing/test_capture.py | 3 +- testing/test_debugging.py | 4 +- testing/test_doctest.py | 4 +- testing/test_mark.py | 13 +++++-- testing/test_mark_expression.py | 18 +++++++-- testing/test_monkeypatch.py | 4 +- testing/test_pluginmanager.py | 4 +- testing/test_runner_xunit.py | 4 +- testing/test_stepwise.py | 7 +++- testing/test_warnings.py | 2 +- 45 files changed, 280 insertions(+), 121 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 68cc3273bba..c8e19b283f8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,16 +1,16 @@ repos: - repo: https://github.com/psf/black - rev: 19.10b0 + rev: 20.8b1 hooks: - id: black args: [--safe, --quiet] - repo: https://github.com/asottile/blacken-docs - rev: v1.8.0 + rev: v1.9.1 hooks: - id: blacken-docs - additional_dependencies: [black==19.10b0] + additional_dependencies: [black==20.8b1] - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v3.2.0 + rev: v3.4.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer @@ -21,7 +21,7 @@ repos: exclude: _pytest/(debugging|hookspec).py language_version: python3 - repo: https://gitlab.com/pycqa/flake8 - rev: 3.8.3 + rev: 3.8.4 hooks: - id: flake8 language_version: python3 @@ -29,23 +29,21 @@ repos: - flake8-typing-imports==1.9.0 - flake8-docstrings==1.5.0 - repo: https://github.com/asottile/reorder_python_imports - rev: v2.3.5 + rev: v2.3.6 hooks: - id: reorder-python-imports args: ['--application-directories=.:src', --py36-plus] - repo: https://github.com/asottile/pyupgrade - rev: v2.7.2 + rev: v2.7.4 hooks: - id: pyupgrade args: [--py36-plus] - repo: https://github.com/asottile/setup-cfg-fmt - rev: v1.11.0 + rev: v1.16.0 hooks: - id: setup-cfg-fmt - # TODO: when upgrading setup-cfg-fmt this can be removed - args: [--max-py-version=3.9] - repo: https://github.com/pre-commit/pygrep-hooks - rev: v1.6.0 + rev: v1.7.0 hooks: - id: python-use-type-annotations - repo: https://github.com/pre-commit/mirrors-mypy diff --git a/doc/en/doctest.rst b/doc/en/doctest.rst index f8d010679f0..486868bb806 100644 --- a/doc/en/doctest.rst +++ b/doc/en/doctest.rst @@ -48,7 +48,7 @@ and functions, including from test modules: # content of mymodule.py def something(): - """ a doctest in a docstring + """a doctest in a docstring >>> something() 42 """ diff --git a/doc/en/writing_plugins.rst b/doc/en/writing_plugins.rst index 908366d5290..f53f561cfad 100644 --- a/doc/en/writing_plugins.rst +++ b/doc/en/writing_plugins.rst @@ -762,8 +762,7 @@ declaring the hook functions directly in your plugin module, for example: """Simple plugin to defer pytest-xdist hook functions.""" def pytest_testnodedown(self, node, error): - """standard xdist hook function. - """ + """standard xdist hook function.""" def pytest_configure(config): diff --git a/doc/en/xunit_setup.rst b/doc/en/xunit_setup.rst index 8b3366f62ae..4fea863be63 100644 --- a/doc/en/xunit_setup.rst +++ b/doc/en/xunit_setup.rst @@ -36,7 +36,7 @@ which will usually be called once for all the functions: def teardown_module(module): - """ teardown any state that was previously setup with a setup_module + """teardown any state that was previously setup with a setup_module method. """ @@ -52,14 +52,14 @@ and after all test methods of the class are called: @classmethod def setup_class(cls): - """ setup any state specific to the execution of the given class (which + """setup any state specific to the execution of the given class (which usually contains tests). """ @classmethod def teardown_class(cls): - """ teardown any state that was previously setup with a call to + """teardown any state that was previously setup with a call to setup_class. """ @@ -71,13 +71,13 @@ Similarly, the following methods are called around each method invocation: .. code-block:: python def setup_method(self, method): - """ setup any state tied to the execution of the given method in a + """setup any state tied to the execution of the given method in a class. setup_method is invoked for every test method of a class. """ def teardown_method(self, method): - """ teardown any state that was previously setup with a setup_method + """teardown any state that was previously setup with a setup_method call. """ @@ -89,13 +89,13 @@ you can also use the following functions to implement fixtures: .. code-block:: python def setup_function(function): - """ setup any state tied to the execution of the given function. + """setup any state tied to the execution of the given function. Invoked for every test function in the module. """ def teardown_function(function): - """ teardown any state that was previously setup with a setup_function + """teardown any state that was previously setup with a setup_function call. """ diff --git a/scripts/prepare-release-pr.py b/scripts/prepare-release-pr.py index 538a5af5a41..296de46ea0c 100644 --- a/scripts/prepare-release-pr.py +++ b/scripts/prepare-release-pr.py @@ -90,7 +90,10 @@ def prepare_release_pr(base_branch: str, is_major: bool, token: str) -> None: cmdline = ["tox", "-e", "release", "--", version, "--skip-check-links"] print("Running", " ".join(cmdline)) run( - cmdline, text=True, check=True, capture_output=True, + cmdline, + text=True, + check=True, + capture_output=True, ) oauth_url = f"https://{token}:x-oauth-basic@github.com/{SLUG}.git" @@ -105,7 +108,10 @@ def prepare_release_pr(base_branch: str, is_major: bool, token: str) -> None: body = PR_BODY.format(version=version) repo = login(token) pr = repo.create_pull( - f"Prepare release {version}", base=base_branch, head=release_branch, body=body, + f"Prepare release {version}", + base=base_branch, + head=release_branch, + body=body, ) print(f"Pull request {Fore.CYAN}{pr.url}{Fore.RESET} created.") diff --git a/scripts/release-on-comment.py b/scripts/release-on-comment.py index 44431a4fc3f..f8af9c0fc83 100644 --- a/scripts/release-on-comment.py +++ b/scripts/release-on-comment.py @@ -153,7 +153,10 @@ def trigger_release(payload_path: Path, token: str) -> None: cmdline = ["tox", "-e", "release", "--", version, "--skip-check-links"] print("Running", " ".join(cmdline)) run( - cmdline, text=True, check=True, capture_output=True, + cmdline, + text=True, + check=True, + capture_output=True, ) oauth_url = f"https://{token}:x-oauth-basic@github.com/{SLUG}.git" diff --git a/src/_pytest/_code/code.py b/src/_pytest/_code/code.py index 043a23a79af..b8521756067 100644 --- a/src/_pytest/_code/code.py +++ b/src/_pytest/_code/code.py @@ -272,9 +272,9 @@ def ishidden(self) -> bool: Mostly for internal use. """ - tbh: Union[bool, Callable[[Optional[ExceptionInfo[BaseException]]], bool]] = ( - False - ) + tbh: Union[ + bool, Callable[[Optional[ExceptionInfo[BaseException]]], bool] + ] = False for maybe_ns_dct in (self.frame.f_locals, self.frame.f_globals): # in normal cases, f_locals and f_globals are dictionaries # however via `exec(...)` / `eval(...)` they can be other types diff --git a/src/_pytest/_io/saferepr.py b/src/_pytest/_io/saferepr.py index 5eb1e088905..440b8cbbb54 100644 --- a/src/_pytest/_io/saferepr.py +++ b/src/_pytest/_io/saferepr.py @@ -107,7 +107,12 @@ def _format( if objid in context or p is None: # Type ignored because _format is private. super()._format( # type: ignore[misc] - object, stream, indent, allowance, context, level, + object, + stream, + indent, + allowance, + context, + level, ) return diff --git a/src/_pytest/cacheprovider.py b/src/_pytest/cacheprovider.py index 03acd03109e..480319c03b4 100755 --- a/src/_pytest/cacheprovider.py +++ b/src/_pytest/cacheprovider.py @@ -219,7 +219,8 @@ def pytest_make_collect_report(self, collector: nodes.Collector): # Sort any lf-paths to the beginning. lf_paths = self.lfplugin._last_failed_paths res.result = sorted( - res.result, key=lambda x: 0 if Path(str(x.fspath)) in lf_paths else 1, + res.result, + key=lambda x: 0 if Path(str(x.fspath)) in lf_paths else 1, ) return diff --git a/src/_pytest/capture.py b/src/_pytest/capture.py index 086302658cb..355f42591a7 100644 --- a/src/_pytest/capture.py +++ b/src/_pytest/capture.py @@ -556,7 +556,11 @@ def __init__(self, in_, out, err) -> None: def __repr__(self) -> str: return "".format( - self.out, self.err, self.in_, self._state, self._in_suspended, + self.out, + self.err, + self.in_, + self._state, + self._in_suspended, ) def start_capturing(self) -> None: @@ -843,7 +847,9 @@ def __init__( def _start(self) -> None: if self._capture is None: self._capture = MultiCapture( - in_=None, out=self.captureclass(1), err=self.captureclass(2), + in_=None, + out=self.captureclass(1), + err=self.captureclass(2), ) self._capture.start_capturing() diff --git a/src/_pytest/compat.py b/src/_pytest/compat.py index c7f86ea9c0a..0b87c7bbc08 100644 --- a/src/_pytest/compat.py +++ b/src/_pytest/compat.py @@ -143,7 +143,8 @@ def getfuncargnames( parameters = signature(function).parameters except (ValueError, TypeError) as e: fail( - f"Could not determine arguments of {function!r}: {e}", pytrace=False, + f"Could not determine arguments of {function!r}: {e}", + pytrace=False, ) arg_names = tuple( diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 760b0f55c7b..c029c29a3a2 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -104,7 +104,9 @@ class ExitCode(enum.IntEnum): class ConftestImportFailure(Exception): def __init__( - self, path: Path, excinfo: Tuple[Type[Exception], Exception, TracebackType], + self, + path: Path, + excinfo: Tuple[Type[Exception], Exception, TracebackType], ) -> None: super().__init__(path, excinfo) self.path = path @@ -269,7 +271,9 @@ def get_config( config = Config( pluginmanager, invocation_params=Config.InvocationParams( - args=args or (), plugins=plugins, dir=Path.cwd(), + args=args or (), + plugins=plugins, + dir=Path.cwd(), ), ) @@ -364,7 +368,10 @@ def __init__(self) -> None: encoding: str = getattr(err, "encoding", "utf8") try: err = open( - os.dup(err.fileno()), mode=err.mode, buffering=1, encoding=encoding, + os.dup(err.fileno()), + mode=err.mode, + buffering=1, + encoding=encoding, ) except Exception: pass @@ -516,7 +523,9 @@ def _try_load_conftest( @lru_cache(maxsize=128) def _getconftestmodules( - self, path: Path, importmode: Union[str, ImportMode], + self, + path: Path, + importmode: Union[str, ImportMode], ) -> List[types.ModuleType]: if self._noconftest: return [] @@ -541,7 +550,10 @@ def _getconftestmodules( return clist def _rget_with_confmod( - self, name: str, path: Path, importmode: Union[str, ImportMode], + self, + name: str, + path: Path, + importmode: Union[str, ImportMode], ) -> Tuple[types.ModuleType, Any]: modules = self._getconftestmodules(path, importmode) for mod in reversed(modules): @@ -552,7 +564,9 @@ def _rget_with_confmod( raise KeyError(name) def _importconftest( - self, conftestpath: Path, importmode: Union[str, ImportMode], + self, + conftestpath: Path, + importmode: Union[str, ImportMode], ) -> types.ModuleType: # Use a resolved Path object as key to avoid loading the same conftest # twice with build systems that create build directories containing @@ -590,7 +604,9 @@ def _importconftest( return mod def _check_non_top_pytest_plugins( - self, mod: types.ModuleType, conftestpath: Path, + self, + mod: types.ModuleType, + conftestpath: Path, ) -> None: if ( hasattr(mod, "pytest_plugins") @@ -1227,7 +1243,11 @@ def _checkversion(self) -> None: if Version(minver) > Version(pytest.__version__): raise pytest.UsageError( "%s: 'minversion' requires pytest-%s, actual pytest-%s'" - % (self.inipath, minver, pytest.__version__,) + % ( + self.inipath, + minver, + pytest.__version__, + ) ) def _validate_config_options(self) -> None: @@ -1502,7 +1522,8 @@ def _warn_about_missing_assertion(self, mode: str) -> None: "(are you using python -O?)\n" ) self.issue_config_time_warning( - PytestConfigWarning(warning_text), stacklevel=3, + PytestConfigWarning(warning_text), + stacklevel=3, ) def _warn_about_skipped_plugins(self) -> None: diff --git a/src/_pytest/config/findpaths.py b/src/_pytest/config/findpaths.py index 2edf54536ba..05f21ece5d4 100644 --- a/src/_pytest/config/findpaths.py +++ b/src/_pytest/config/findpaths.py @@ -83,9 +83,7 @@ def make_scalar(v: object) -> Union[str, List[str]]: def locate_config( args: Iterable[Path], -) -> Tuple[ - Optional[Path], Optional[Path], Dict[str, Union[str, List[str]]], -]: +) -> Tuple[Optional[Path], Optional[Path], Dict[str, Union[str, List[str]]]]: """Search in the list of arguments for a valid ini-file for pytest, and return a tuple of (rootdir, inifile, cfg-dict).""" config_names = [ diff --git a/src/_pytest/doctest.py b/src/_pytest/doctest.py index 24f8882579b..255ca80b913 100644 --- a/src/_pytest/doctest.py +++ b/src/_pytest/doctest.py @@ -121,7 +121,9 @@ def pytest_unconfigure() -> None: def pytest_collect_file( - fspath: Path, path: py.path.local, parent: Collector, + fspath: Path, + path: py.path.local, + parent: Collector, ) -> Optional[Union["DoctestModule", "DoctestTextfile"]]: config = parent.config if fspath.suffix == ".py": @@ -193,7 +195,11 @@ def __init__( self.continue_on_failure = continue_on_failure def report_failure( - self, out, test: "doctest.DocTest", example: "doctest.Example", got: str, + self, + out, + test: "doctest.DocTest", + example: "doctest.Example", + got: str, ) -> None: failure = doctest.DocTestFailure(test, example, got) if self.continue_on_failure: @@ -303,13 +309,14 @@ def _disable_output_capturing_for_darwin(self) -> None: # TODO: Type ignored -- breaks Liskov Substitution. def repr_failure( # type: ignore[override] - self, excinfo: ExceptionInfo[BaseException], + self, + excinfo: ExceptionInfo[BaseException], ) -> Union[str, TerminalRepr]: import doctest failures: Optional[ Sequence[Union[doctest.DocTestFailure, doctest.UnexpectedException]] - ] = (None) + ] = None if isinstance( excinfo.value, (doctest.DocTestFailure, doctest.UnexpectedException) ): @@ -510,7 +517,9 @@ def _find_lineno(self, obj, source_lines): obj = getattr(obj, "fget", obj) # Type ignored because this is a private function. return doctest.DocTestFinder._find_lineno( # type: ignore - self, obj, source_lines, + self, + obj, + source_lines, ) def _find( diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index d3ec1296ab0..53f33d3e13d 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -238,7 +238,7 @@ def getfixturemarker(obj: object) -> Optional["FixtureFunctionMarker"]: def get_parametrized_fixture_keys(item: nodes.Item, scopenum: int) -> Iterator[_Key]: """Return list of keys for all parametrized arguments which match - the specified scope. """ + the specified scope.""" assert scopenum < scopenum_function # function try: callspec = item.callspec # type: ignore[attr-defined] @@ -443,7 +443,7 @@ def __init__(self, pyfuncitem, *, _ispytest: bool = False) -> None: fixtureinfo: FuncFixtureInfo = pyfuncitem._fixtureinfo self._arg2fixturedefs = fixtureinfo.name2fixturedefs.copy() self._arg2index: Dict[str, int] = {} - self._fixturemanager: FixtureManager = (pyfuncitem.session._fixturemanager) + self._fixturemanager: FixtureManager = pyfuncitem.session._fixturemanager @property def fixturenames(self) -> List[str]: @@ -700,7 +700,10 @@ def _schedule_finalizers( ) def _check_scope( - self, argname: str, invoking_scope: "_Scope", requested_scope: "_Scope", + self, + argname: str, + invoking_scope: "_Scope", + requested_scope: "_Scope", ) -> None: if argname == "request": return @@ -907,7 +910,8 @@ def toterminal(self, tw: TerminalWriter) -> None: ) for line in lines[1:]: tw.line( - f"{FormattedExcinfo.flow_marker} {line.strip()}", red=True, + f"{FormattedExcinfo.flow_marker} {line.strip()}", + red=True, ) tw.line() tw.line("%s:%d" % (os.fspath(self.filename), self.firstlineno + 1)) @@ -1167,7 +1171,8 @@ def _params_converter( def wrap_function_to_error_out_if_called_directly( - function: _FixtureFunction, fixture_marker: "FixtureFunctionMarker", + function: _FixtureFunction, + fixture_marker: "FixtureFunctionMarker", ) -> _FixtureFunction: """Wrap the given fixture function so we can raise an error about it being called directly, instead of used as an argument in a test function.""" @@ -1332,7 +1337,11 @@ def fixture( ``@pytest.fixture(name='')``. """ fixture_marker = FixtureFunctionMarker( - scope=scope, params=params, autouse=autouse, ids=ids, name=name, + scope=scope, + params=params, + autouse=autouse, + ids=ids, + name=name, ) # Direct decoration. diff --git a/src/_pytest/freeze_support.py b/src/_pytest/freeze_support.py index 8b93ed5f7f8..69b7d59ff69 100644 --- a/src/_pytest/freeze_support.py +++ b/src/_pytest/freeze_support.py @@ -18,7 +18,8 @@ def freeze_includes() -> List[str]: def _iter_all_modules( - package: Union[str, types.ModuleType], prefix: str = "", + package: Union[str, types.ModuleType], + prefix: str = "", ) -> Iterator[str]: """Iterate over the names of all modules that can be found in the given package, recursively. diff --git a/src/_pytest/hookspec.py b/src/_pytest/hookspec.py index 22bebf5b783..41c12a2ccd8 100644 --- a/src/_pytest/hookspec.py +++ b/src/_pytest/hookspec.py @@ -540,7 +540,8 @@ def pytest_runtest_logreport(report: "TestReport") -> None: @hookspec(firstresult=True) def pytest_report_to_serializable( - config: "Config", report: Union["CollectReport", "TestReport"], + config: "Config", + report: Union["CollectReport", "TestReport"], ) -> Optional[Dict[str, Any]]: """Serialize the given report object into a data structure suitable for sending over the wire, e.g. converted to JSON.""" @@ -548,7 +549,8 @@ def pytest_report_to_serializable( @hookspec(firstresult=True) def pytest_report_from_serializable( - config: "Config", data: Dict[str, Any], + config: "Config", + data: Dict[str, Any], ) -> Optional[Union["CollectReport", "TestReport"]]: """Restore a report object previously serialized with pytest_report_to_serializable().""" @@ -597,7 +599,8 @@ def pytest_sessionstart(session: "Session") -> None: def pytest_sessionfinish( - session: "Session", exitstatus: Union[int, "ExitCode"], + session: "Session", + exitstatus: Union[int, "ExitCode"], ) -> None: """Called after whole test run finished, right before returning the exit status to the system. @@ -701,7 +704,10 @@ def pytest_report_header( def pytest_report_collectionfinish( - config: "Config", startpath: Path, startdir: py.path.local, items: Sequence["Item"], + config: "Config", + startpath: Path, + startdir: py.path.local, + items: Sequence["Item"], ) -> Union[str, List[str]]: """Return a string or list of strings to be displayed after collection has finished successfully. @@ -731,9 +737,7 @@ def pytest_report_collectionfinish( @hookspec(firstresult=True) def pytest_report_teststatus( report: Union["CollectReport", "TestReport"], config: "Config" -) -> Tuple[ - str, str, Union[str, Mapping[str, bool]], -]: +) -> Tuple[str, str, Union[str, Mapping[str, bool]]]: """Return result-category, shortletter and verbose word for status reporting. @@ -758,7 +762,9 @@ def pytest_report_teststatus( def pytest_terminal_summary( - terminalreporter: "TerminalReporter", exitstatus: "ExitCode", config: "Config", + terminalreporter: "TerminalReporter", + exitstatus: "ExitCode", + config: "Config", ) -> None: """Add a section to terminal summary reporting. @@ -865,7 +871,8 @@ def pytest_markeval_namespace(config: "Config") -> Dict[str, Any]: def pytest_internalerror( - excrepr: "ExceptionRepr", excinfo: "ExceptionInfo[BaseException]", + excrepr: "ExceptionRepr", + excinfo: "ExceptionInfo[BaseException]", ) -> Optional[bool]: """Called for internal errors. diff --git a/src/_pytest/junitxml.py b/src/_pytest/junitxml.py index c4761cd3b87..690fd976ca9 100644 --- a/src/_pytest/junitxml.py +++ b/src/_pytest/junitxml.py @@ -486,7 +486,7 @@ def __init__( ) self.node_reporters: Dict[ Tuple[Union[str, TestReport], object], _NodeReporter - ] = ({}) + ] = {} self.node_reporters_ordered: List[_NodeReporter] = [] self.global_properties: List[Tuple[str, str]] = [] diff --git a/src/_pytest/logging.py b/src/_pytest/logging.py index 2e4847328ab..e0d71c7eb54 100644 --- a/src/_pytest/logging.py +++ b/src/_pytest/logging.py @@ -685,9 +685,11 @@ def pytest_runtest_logreport(self) -> None: def _runtest_for(self, item: nodes.Item, when: str) -> Generator[None, None, None]: """Implement the internals of the pytest_runtest_xxx() hooks.""" with catching_logs( - self.caplog_handler, level=self.log_level, + self.caplog_handler, + level=self.log_level, ) as caplog_handler, catching_logs( - self.report_handler, level=self.log_level, + self.report_handler, + level=self.log_level, ) as report_handler: caplog_handler.reset() report_handler.reset() diff --git a/src/_pytest/main.py b/src/_pytest/main.py index 79afdde6155..5036601f9bb 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -116,7 +116,9 @@ def pytest_addoption(parser: Parser) -> None: help="markers not registered in the `markers` section of the configuration file raise errors.", ) group._addoption( - "--strict", action="store_true", help="(deprecated) alias to --strict-markers.", + "--strict", + action="store_true", + help="(deprecated) alias to --strict-markers.", ) group._addoption( "-c", @@ -656,11 +658,11 @@ def collect(self) -> Iterator[Union[nodes.Item, nodes.Collector]]: # Keep track of any collected nodes in here, so we don't duplicate fixtures. node_cache1: Dict[Path, Sequence[nodes.Collector]] = {} - node_cache2: Dict[Tuple[Type[nodes.Collector], Path], nodes.Collector] = ({}) + node_cache2: Dict[Tuple[Type[nodes.Collector], Path], nodes.Collector] = {} # Keep track of any collected collectors in matchnodes paths, so they # are not collected more than once. - matchnodes_cache: Dict[Tuple[Type[nodes.Collector], str], CollectReport] = ({}) + matchnodes_cache: Dict[Tuple[Type[nodes.Collector], str], CollectReport] = {} # Dirnames of pkgs with dunder-init files. pkg_roots: Dict[str, Package] = {} diff --git a/src/_pytest/mark/__init__.py b/src/_pytest/mark/__init__.py index 329a11c4ae8..97fb36ef73b 100644 --- a/src/_pytest/mark/__init__.py +++ b/src/_pytest/mark/__init__.py @@ -56,7 +56,10 @@ def param( @pytest.mark.parametrize( "test_input,expected", - [("3+5", 8), pytest.param("6*9", 42, marks=pytest.mark.xfail),], + [ + ("3+5", 8), + pytest.param("6*9", 42, marks=pytest.mark.xfail), + ], ) def test_eval(test_input, expected): assert eval(test_input) == expected diff --git a/src/_pytest/mark/expression.py b/src/_pytest/mark/expression.py index dc3991b10c4..2e7dcf93cd4 100644 --- a/src/_pytest/mark/expression.py +++ b/src/_pytest/mark/expression.py @@ -102,7 +102,8 @@ def lex(self, input: str) -> Iterator[Token]: pos += len(value) else: raise ParseError( - pos + 1, 'unexpected character "{}"'.format(input[pos]), + pos + 1, + 'unexpected character "{}"'.format(input[pos]), ) yield Token(TokenType.EOF, "", pos) @@ -120,7 +121,8 @@ def reject(self, expected: Sequence[TokenType]) -> "NoReturn": raise ParseError( self.current.pos + 1, "expected {}; got {}".format( - " OR ".join(type.value for type in expected), self.current.type.value, + " OR ".join(type.value for type in expected), + self.current.type.value, ), ) @@ -204,7 +206,9 @@ def compile(self, input: str) -> "Expression": """ astexpr = expression(Scanner(input)) code: types.CodeType = compile( - astexpr, filename="", mode="eval", + astexpr, + filename="", + mode="eval", ) return Expression(code) diff --git a/src/_pytest/mark/structures.py b/src/_pytest/mark/structures.py index ae6920735a2..d2f21e1684d 100644 --- a/src/_pytest/mark/structures.py +++ b/src/_pytest/mark/structures.py @@ -460,15 +460,11 @@ def __call__( # type: ignore[override] ... class _UsefixturesMarkDecorator(MarkDecorator): - def __call__( # type: ignore[override] - self, *fixtures: str - ) -> MarkDecorator: + def __call__(self, *fixtures: str) -> MarkDecorator: # type: ignore[override] ... class _FilterwarningsMarkDecorator(MarkDecorator): - def __call__( # type: ignore[override] - self, *filters: str - ) -> MarkDecorator: + def __call__(self, *filters: str) -> MarkDecorator: # type: ignore[override] ... diff --git a/src/_pytest/monkeypatch.py b/src/_pytest/monkeypatch.py index d012b8a535a..ffef87173a8 100644 --- a/src/_pytest/monkeypatch.py +++ b/src/_pytest/monkeypatch.py @@ -124,7 +124,7 @@ class MonkeyPatch: def __init__(self) -> None: self._setattr: List[Tuple[object, str, object]] = [] - self._setitem: List[Tuple[MutableMapping[Any, Any], object, object]] = ([]) + self._setitem: List[Tuple[MutableMapping[Any, Any], object, object]] = [] self._cwd: Optional[str] = None self._savesyspath: Optional[List[str]] = None @@ -157,13 +157,21 @@ def test_partial(monkeypatch): @overload def setattr( - self, target: str, name: object, value: Notset = ..., raising: bool = ..., + self, + target: str, + name: object, + value: Notset = ..., + raising: bool = ..., ) -> None: ... @overload def setattr( - self, target: object, name: str, value: object, raising: bool = ..., + self, + target: object, + name: str, + value: object, + raising: bool = ..., ) -> None: ... diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py index c6eb49dec4a..2a96d55ad05 100644 --- a/src/_pytest/nodes.py +++ b/src/_pytest/nodes.py @@ -231,7 +231,10 @@ def warn(self, warning: Warning) -> None: path, lineno = get_fslocation_from_item(self) assert lineno is not None warnings.warn_explicit( - warning, category=None, filename=str(path), lineno=lineno + 1, + warning, + category=None, + filename=str(path), + lineno=lineno + 1, ) # Methods for ordering nodes. diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index 0d1f8f278f9..4544d2c2bbb 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -291,13 +291,15 @@ def getcall(self, name: str) -> ParsedCall: @overload def getreports( - self, names: "Literal['pytest_collectreport']", + self, + names: "Literal['pytest_collectreport']", ) -> Sequence[CollectReport]: ... @overload def getreports( - self, names: "Literal['pytest_runtest_logreport']", + self, + names: "Literal['pytest_runtest_logreport']", ) -> Sequence[TestReport]: ... @@ -354,13 +356,15 @@ def matchreport( @overload def getfailures( - self, names: "Literal['pytest_collectreport']", + self, + names: "Literal['pytest_collectreport']", ) -> Sequence[CollectReport]: ... @overload def getfailures( - self, names: "Literal['pytest_runtest_logreport']", + self, + names: "Literal['pytest_runtest_logreport']", ) -> Sequence[TestReport]: ... @@ -419,7 +423,10 @@ def assertoutcome(self, passed: int = 0, skipped: int = 0, failed: int = 0) -> N outcomes = self.listoutcomes() assertoutcome( - outcomes, passed=passed, skipped=skipped, failed=failed, + outcomes, + passed=passed, + skipped=skipped, + failed=failed, ) def clear(self) -> None: @@ -659,7 +666,7 @@ def __init__( self._request = request self._mod_collections: WeakKeyDictionary[ Collector, List[Union[Item, Collector]] - ] = (WeakKeyDictionary()) + ] = WeakKeyDictionary() if request.function: name: str = request.function.__name__ else: diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 31d91853f2f..50ea60c2dff 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -1202,7 +1202,9 @@ def _validate_ids( return new_ids def _resolve_arg_value_types( - self, argnames: Sequence[str], indirect: Union[bool, Sequence[str]], + self, + argnames: Sequence[str], + indirect: Union[bool, Sequence[str]], ) -> Dict[str, "Literal['params', 'funcargs']"]: """Resolve if each parametrized argument must be considered a parameter to a fixture or a "funcarg" to the function, based on the @@ -1240,7 +1242,9 @@ def _resolve_arg_value_types( return valtypes def _validate_if_using_arg_names( - self, argnames: Sequence[str], indirect: Union[bool, Sequence[str]], + self, + argnames: Sequence[str], + indirect: Union[bool, Sequence[str]], ) -> None: """Check if all argnames are being used, by default values, or directly/indirectly. @@ -1691,7 +1695,8 @@ def _prunetraceback(self, excinfo: ExceptionInfo[BaseException]) -> None: # TODO: Type ignored -- breaks Liskov Substitution. def repr_failure( # type: ignore[override] - self, excinfo: ExceptionInfo[BaseException], + self, + excinfo: ExceptionInfo[BaseException], ) -> Union[str, TerminalRepr]: style = self.config.getoption("tbstyle", "auto") if style == "auto": diff --git a/src/_pytest/reports.py b/src/_pytest/reports.py index 58f12517c5b..bcd40fb362e 100644 --- a/src/_pytest/reports.py +++ b/src/_pytest/reports.py @@ -307,7 +307,7 @@ def from_item_and_call(cls, item: Item, call: "CallInfo[None]") -> "TestReport": Tuple[str, int, str], str, TerminalRepr, - ] = (None) + ] = None else: if not isinstance(excinfo, ExceptionInfo): outcome = "failed" diff --git a/src/_pytest/terminal.py b/src/_pytest/terminal.py index d3d1a4b666e..eea9214e70f 100644 --- a/src/_pytest/terminal.py +++ b/src/_pytest/terminal.py @@ -468,7 +468,9 @@ def pytest_internalerror(self, excrepr: ExceptionRepr) -> bool: return True def pytest_warning_recorded( - self, warning_message: warnings.WarningMessage, nodeid: str, + self, + warning_message: warnings.WarningMessage, + nodeid: str, ) -> None: from _pytest.warnings import warning_record_to_str @@ -1306,7 +1308,8 @@ def _get_line_with_reprcrash_message( def _folded_skips( - startpath: Path, skipped: Sequence[CollectReport], + startpath: Path, + skipped: Sequence[CollectReport], ) -> List[Tuple[int, str, Optional[int], str]]: d: Dict[Tuple[str, Optional[int], str], List[CollectReport]] = {} for event in skipped: diff --git a/src/_pytest/threadexception.py b/src/_pytest/threadexception.py index 1c1f62fdb73..d084dc6e6a2 100644 --- a/src/_pytest/threadexception.py +++ b/src/_pytest/threadexception.py @@ -69,7 +69,9 @@ def thread_exception_runtest_hook() -> Generator[None, None, None]: msg = f"Exception in thread {thread_name}\n\n" msg += "".join( traceback.format_exception( - cm.args.exc_type, cm.args.exc_value, cm.args.exc_traceback, + cm.args.exc_type, + cm.args.exc_value, + cm.args.exc_traceback, ) ) warnings.warn(pytest.PytestUnhandledThreadExceptionWarning(msg)) diff --git a/src/_pytest/tmpdir.py b/src/_pytest/tmpdir.py index 08c445e2bf8..29c7e19d7b4 100644 --- a/src/_pytest/tmpdir.py +++ b/src/_pytest/tmpdir.py @@ -53,7 +53,10 @@ def __init__( @classmethod def from_config( - cls, config: Config, *, _ispytest: bool = False, + cls, + config: Config, + *, + _ispytest: bool = False, ) -> "TempPathFactory": """Create a factory according to pytest configuration. diff --git a/testing/example_scripts/dataclasses/test_compare_recursive_dataclasses.py b/testing/example_scripts/dataclasses/test_compare_recursive_dataclasses.py index 167140e16a6..0945790f004 100644 --- a/testing/example_scripts/dataclasses/test_compare_recursive_dataclasses.py +++ b/testing/example_scripts/dataclasses/test_compare_recursive_dataclasses.py @@ -29,10 +29,16 @@ class C3: def test_recursive_dataclasses(): left = C3( - S(10, "ten"), C2(C(S(1, "one"), S(2, "two")), S(2, "three")), "equal", "left", + S(10, "ten"), + C2(C(S(1, "one"), S(2, "two")), S(2, "three")), + "equal", + "left", ) right = C3( - S(20, "xxx"), C2(C(S(1, "one"), S(2, "yyy")), S(3, "three")), "equal", "right", + S(20, "xxx"), + C2(C(S(1, "one"), S(2, "yyy")), S(3, "three")), + "equal", + "right", ) assert left == right diff --git a/testing/io/test_terminalwriter.py b/testing/io/test_terminalwriter.py index fac7593eadd..4866c94a558 100644 --- a/testing/io/test_terminalwriter.py +++ b/testing/io/test_terminalwriter.py @@ -258,13 +258,22 @@ def test_combining(self) -> None: id="with markup and code_highlight", ), pytest.param( - True, False, "assert 0\n", id="with markup but no code_highlight", + True, + False, + "assert 0\n", + id="with markup but no code_highlight", ), pytest.param( - False, True, "assert 0\n", id="without markup but with code_highlight", + False, + True, + "assert 0\n", + id="without markup but with code_highlight", ), pytest.param( - False, False, "assert 0\n", id="neither markup nor code_highlight", + False, + False, + "assert 0\n", + id="neither markup nor code_highlight", ), ], ) diff --git a/testing/python/approx.py b/testing/python/approx.py index e76d6b774d6..db6124e3914 100644 --- a/testing/python/approx.py +++ b/testing/python/approx.py @@ -514,7 +514,8 @@ def test_foo(): ) def test_expected_value_type_error(self, x, name): with pytest.raises( - TypeError, match=fr"pytest.approx\(\) does not support nested {name}:", + TypeError, + match=fr"pytest.approx\(\) does not support nested {name}:", ): approx(x) diff --git a/testing/python/metafunc.py b/testing/python/metafunc.py index 58a902a3a59..6577ff18ebf 100644 --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -514,7 +514,10 @@ def getini(self, name): ] for config, expected in values: result = idmaker( - ("a",), [pytest.param("string")], idfn=lambda _: "ação", config=config, + ("a",), + [pytest.param("string")], + idfn=lambda _: "ação", + config=config, ) assert result == [expected] @@ -546,7 +549,10 @@ def getini(self, name): ] for config, expected in values: result = idmaker( - ("a",), [pytest.param("string")], ids=["ação"], config=config, + ("a",), + [pytest.param("string")], + ids=["ação"], + config=config, ) assert result == [expected] diff --git a/testing/test_capture.py b/testing/test_capture.py index 3a5c617fe5a..4d89f0b9e40 100644 --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -1431,7 +1431,8 @@ def test_capattr(): @pytest.mark.skipif( - not sys.platform.startswith("win"), reason="only on windows", + not sys.platform.startswith("win"), + reason="only on windows", ) def test_py36_windowsconsoleio_workaround_non_standard_streams() -> None: """ diff --git a/testing/test_debugging.py b/testing/test_debugging.py index e1b57299d25..9cdd411667d 100644 --- a/testing/test_debugging.py +++ b/testing/test_debugging.py @@ -877,7 +877,9 @@ def test_pdb_custom_cls_without_pdb( assert custom_pdb_calls == [] def test_pdb_custom_cls_with_set_trace( - self, pytester: Pytester, monkeypatch: MonkeyPatch, + self, + pytester: Pytester, + monkeypatch: MonkeyPatch, ) -> None: pytester.makepyfile( custom_pdb=""" diff --git a/testing/test_doctest.py b/testing/test_doctest.py index 08d0aacf68c..b63665349a1 100644 --- a/testing/test_doctest.py +++ b/testing/test_doctest.py @@ -69,7 +69,9 @@ def my_func(): @pytest.mark.parametrize("filename", ["__init__", "whatever"]) def test_collect_module_two_doctest_no_modulelevel( - self, pytester: Pytester, filename: str, + self, + pytester: Pytester, + filename: str, ) -> None: path = pytester.makepyfile( **{ diff --git a/testing/test_mark.py b/testing/test_mark.py index 5f4b3e063e4..420faf91ec9 100644 --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -356,8 +356,14 @@ def test_func(arg): "foo or or", "at column 8: expected not OR left parenthesis OR identifier; got or", ), - ("(foo", "at column 5: expected right parenthesis; got end of input",), - ("foo bar", "at column 5: expected end of input; got identifier",), + ( + "(foo", + "at column 5: expected right parenthesis; got end of input", + ), + ( + "foo bar", + "at column 5: expected end of input; got identifier", + ), ( "or or", "at column 1: expected not OR left parenthesis OR identifier; got or", @@ -863,7 +869,8 @@ def test_one(): assert passed + skipped + failed == 0 @pytest.mark.parametrize( - "keyword", ["__", "+", ".."], + "keyword", + ["__", "+", ".."], ) def test_no_magic_values(self, pytester: Pytester, keyword: str) -> None: """Make sure the tests do not match on magic values, diff --git a/testing/test_mark_expression.py b/testing/test_mark_expression.py index faca02d9330..d37324f51cd 100644 --- a/testing/test_mark_expression.py +++ b/testing/test_mark_expression.py @@ -70,7 +70,11 @@ def test_syntax_oddeties(expr: str, expected: bool) -> None: ("expr", "column", "message"), ( ("(", 2, "expected not OR left parenthesis OR identifier; got end of input"), - (" (", 3, "expected not OR left parenthesis OR identifier; got end of input",), + ( + " (", + 3, + "expected not OR left parenthesis OR identifier; got end of input", + ), ( ")", 1, @@ -81,7 +85,11 @@ def test_syntax_oddeties(expr: str, expected: bool) -> None: 1, "expected not OR left parenthesis OR identifier; got right parenthesis", ), - ("not", 4, "expected not OR left parenthesis OR identifier; got end of input",), + ( + "not", + 4, + "expected not OR left parenthesis OR identifier; got end of input", + ), ( "not not", 8, @@ -98,7 +106,11 @@ def test_syntax_oddeties(expr: str, expected: bool) -> None: 10, "expected not OR left parenthesis OR identifier; got end of input", ), - ("ident and or", 11, "expected not OR left parenthesis OR identifier; got or",), + ( + "ident and or", + 11, + "expected not OR left parenthesis OR identifier; got or", + ), ("ident ident", 7, "expected end of input; got identifier"), ), ) diff --git a/testing/test_monkeypatch.py b/testing/test_monkeypatch.py index 0b97a0e5adb..95521818021 100644 --- a/testing/test_monkeypatch.py +++ b/testing/test_monkeypatch.py @@ -348,7 +348,9 @@ class SampleInherit(Sample): @pytest.mark.parametrize( - "Sample", [Sample, SampleInherit], ids=["new", "new-inherit"], + "Sample", + [Sample, SampleInherit], + ids=["new", "new-inherit"], ) def test_issue156_undo_staticmethod(Sample: Type[Sample]) -> None: monkeypatch = MonkeyPatch() diff --git a/testing/test_pluginmanager.py b/testing/test_pluginmanager.py index a5282a50795..9835b24a046 100644 --- a/testing/test_pluginmanager.py +++ b/testing/test_pluginmanager.py @@ -346,7 +346,9 @@ def test_import_plugin_dotted_name( assert mod.x == 3 def test_consider_conftest_deps( - self, pytester: Pytester, pytestpm: PytestPluginManager, + self, + pytester: Pytester, + pytestpm: PytestPluginManager, ) -> None: mod = import_path(pytester.makepyfile("pytest_plugins='xyz'")) with pytest.raises(ImportError): diff --git a/testing/test_runner_xunit.py b/testing/test_runner_xunit.py index e90d761f633..e077ac41e2c 100644 --- a/testing/test_runner_xunit.py +++ b/testing/test_runner_xunit.py @@ -242,7 +242,9 @@ def test_function2(hello): @pytest.mark.parametrize("arg", ["", "arg"]) def test_setup_teardown_function_level_with_optional_argument( - pytester: Pytester, monkeypatch, arg: str, + pytester: Pytester, + monkeypatch, + arg: str, ) -> None: """Parameter to setup/teardown xunit-style functions parameter is now optional (#1728).""" import sys diff --git a/testing/test_stepwise.py b/testing/test_stepwise.py index ff2ec16b707..85489fce803 100644 --- a/testing/test_stepwise.py +++ b/testing/test_stepwise.py @@ -138,7 +138,12 @@ def test_fail_and_continue_with_stepwise(stepwise_pytester: Pytester) -> None: @pytest.mark.parametrize("stepwise_skip", ["--stepwise-skip", "--sw-skip"]) def test_run_with_skip_option(stepwise_pytester: Pytester, stepwise_skip: str) -> None: result = stepwise_pytester.runpytest( - "-v", "--strict-markers", "--stepwise", stepwise_skip, "--fail", "--fail-last", + "-v", + "--strict-markers", + "--stepwise", + stepwise_skip, + "--fail", + "--fail-last", ) assert _strip_resource_warnings(result.stderr.lines) == [] diff --git a/testing/test_warnings.py b/testing/test_warnings.py index 574f3f1ec02..8c9c227b26a 100644 --- a/testing/test_warnings.py +++ b/testing/test_warnings.py @@ -662,7 +662,7 @@ def capwarn(self, pytester: Pytester): class CapturedWarnings: captured: List[ Tuple[warnings.WarningMessage, Optional[Tuple[str, int, str]]] - ] = ([]) + ] = [] @classmethod def pytest_warning_recorded(cls, warning_message, when, nodeid, location): From 48c9a96a03261e7cfa5aad0367a9186d9032904a Mon Sep 17 00:00:00 2001 From: Anton <44246099+antonblr@users.noreply.github.com> Date: Wed, 30 Dec 2020 19:00:37 -0800 Subject: [PATCH 048/630] Fix failing staticmethod tests if they are inherited (#8205) * Fix failing staticmethod tests if they are inherited * add comments, set default=None --- AUTHORS | 1 + changelog/8061.bugfix.rst | 1 + src/_pytest/compat.py | 7 ++++++- testing/python/fixtures.py | 14 ++++++++++++++ 4 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 changelog/8061.bugfix.rst diff --git a/AUTHORS b/AUTHORS index 20798f3093d..abac9f010a1 100644 --- a/AUTHORS +++ b/AUTHORS @@ -29,6 +29,7 @@ Andy Freeland Anthon van der Neut Anthony Shaw Anthony Sottile +Anton Grinevich Anton Lodder Antony Lee Arel Cordero diff --git a/changelog/8061.bugfix.rst b/changelog/8061.bugfix.rst new file mode 100644 index 00000000000..2c8980fb34e --- /dev/null +++ b/changelog/8061.bugfix.rst @@ -0,0 +1 @@ +Fixed failing staticmethod test cases if they are inherited from a parent test class. diff --git a/src/_pytest/compat.py b/src/_pytest/compat.py index 0b87c7bbc08..b354fcb3f63 100644 --- a/src/_pytest/compat.py +++ b/src/_pytest/compat.py @@ -163,7 +163,12 @@ def getfuncargnames( # 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(name, None), staticmethod) + # Not using `getattr` because we don't want to resolve the staticmethod. + # Not using `cls.__dict__` because we want to check the entire MRO. + cls + and not isinstance( + inspect.getattr_static(cls, name, default=None), staticmethod + ) ): arg_names = arg_names[1:] # Remove any names that will be replaced with mocks. diff --git a/testing/python/fixtures.py b/testing/python/fixtures.py index 862a65abe10..12340e690eb 100644 --- a/testing/python/fixtures.py +++ b/testing/python/fixtures.py @@ -59,6 +59,20 @@ def static(arg1, arg2, x=1): assert getfuncargnames(A.static, cls=A) == ("arg1", "arg2") +def test_getfuncargnames_staticmethod_inherited() -> None: + """Test getfuncargnames for inherited staticmethods (#8061)""" + + class A: + @staticmethod + def static(arg1, arg2, x=1): + raise NotImplementedError() + + class B(A): + pass + + assert getfuncargnames(B.static, cls=B) == ("arg1", "arg2") + + def test_getfuncargnames_partial(): """Check getfuncargnames for methods defined with functools.partial (#5701)""" import functools From 5f11a35b99e3606b003f695b908496c363796074 Mon Sep 17 00:00:00 2001 From: mefmund Date: Thu, 31 Dec 2020 19:25:44 +0100 Subject: [PATCH 049/630] Add missing fixture (#8207) Co-authored-by: Florian Bruhin Co-authored-by: Bruno Oliveira --- doc/en/fixture.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/en/fixture.rst b/doc/en/fixture.rst index 752385adc89..a629bb7d47f 100644 --- a/doc/en/fixture.rst +++ b/doc/en/fixture.rst @@ -879,9 +879,9 @@ Here's what that might look like: admin_client.delete_user(user) - def test_email_received(receiving_user, email): + def test_email_received(sending_user, receiving_user, email): email = Email(subject="Hey!", body="How's it going?") - sending_user.send_email(_email, receiving_user) + sending_user.send_email(email, receiving_user) assert email in receiving_user.inbox Because ``receiving_user`` is the last fixture to run during setup, it's the first to run From 7d306e9e86f114a812bcf234211ff7b8533d11f9 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Wed, 30 Dec 2020 14:37:00 +0200 Subject: [PATCH 050/630] reports: BaseReport.{passed,failed,skipped} more friendly to mypy Not smart enough to understand the previous code. --- src/_pytest/reports.py | 17 +++++++++++++---- testing/test_terminal.py | 6 +++--- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/_pytest/reports.py b/src/_pytest/reports.py index bcd40fb362e..c78d5423974 100644 --- a/src/_pytest/reports.py +++ b/src/_pytest/reports.py @@ -65,6 +65,7 @@ class BaseReport: ] sections: List[Tuple[str, str]] nodeid: str + outcome: "Literal['passed', 'failed', 'skipped']" def __init__(self, **kw: Any) -> None: self.__dict__.update(kw) @@ -141,9 +142,17 @@ def capstderr(self) -> str: content for (prefix, content) in self.get_sections("Captured stderr") ) - passed = property(lambda x: x.outcome == "passed") - failed = property(lambda x: x.outcome == "failed") - skipped = property(lambda x: x.outcome == "skipped") + @property + def passed(self) -> bool: + return self.outcome == "passed" + + @property + def failed(self) -> bool: + return self.outcome == "failed" + + @property + def skipped(self) -> bool: + return self.outcome == "skipped" @property def fspath(self) -> str: @@ -348,7 +357,7 @@ class CollectReport(BaseReport): def __init__( self, nodeid: str, - outcome: "Literal['passed', 'skipped', 'failed']", + outcome: "Literal['passed', 'failed', 'skipped']", longrepr, result: Optional[List[Union[Item, Collector]]], sections: Iterable[Tuple[str, str]] = (), diff --git a/testing/test_terminal.py b/testing/test_terminal.py index 6d0a23fe0f1..e536f70989c 100644 --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -2230,19 +2230,19 @@ class X: ev1 = cast(CollectReport, X()) ev1.when = "execute" - ev1.skipped = True + ev1.skipped = True # type: ignore[misc] ev1.longrepr = longrepr ev2 = cast(CollectReport, X()) ev2.when = "execute" ev2.longrepr = longrepr - ev2.skipped = True + ev2.skipped = True # type: ignore[misc] # ev3 might be a collection report ev3 = cast(CollectReport, X()) ev3.when = "collect" ev3.longrepr = longrepr - ev3.skipped = True + ev3.skipped = True # type: ignore[misc] values = _folded_skips(Path.cwd(), [ev1, ev2, ev3]) assert len(values) == 1 From 73c410523097a699559ce5ae12b9caf9c50972fc Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Wed, 30 Dec 2020 14:44:43 +0200 Subject: [PATCH 051/630] reports: improve a type annotation --- src/_pytest/reports.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/_pytest/reports.py b/src/_pytest/reports.py index c78d5423974..d2d7115b2e5 100644 --- a/src/_pytest/reports.py +++ b/src/_pytest/reports.py @@ -358,7 +358,9 @@ def __init__( self, nodeid: str, outcome: "Literal['passed', 'failed', 'skipped']", - longrepr, + longrepr: Union[ + None, ExceptionInfo[BaseException], Tuple[str, int, str], str, TerminalRepr + ], result: Optional[List[Union[Item, Collector]]], sections: Iterable[Tuple[str, str]] = (), **extra, From 20c59e3aa4446faf2fd1ac04322e8076c2aebe3f Mon Sep 17 00:00:00 2001 From: sousajf1 Date: Fri, 1 Jan 2021 15:25:11 +0000 Subject: [PATCH 052/630] pytest-dev#8204 migrate some tests to tmp_path fixture (#8209) migrating some tests from tmpdir to tmp_path fixture --- testing/code/test_excinfo.py | 6 +++--- testing/test_argcomplete.py | 17 +++++++++++------ 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/testing/code/test_excinfo.py b/testing/code/test_excinfo.py index 19c888403f2..e6a9cbaf737 100644 --- a/testing/code/test_excinfo.py +++ b/testing/code/test_excinfo.py @@ -364,12 +364,12 @@ def test_excinfo_no_sourcecode(): assert s == " File '':1 in \n ???\n" -def test_excinfo_no_python_sourcecode(tmpdir): +def test_excinfo_no_python_sourcecode(tmp_path: Path) -> None: # XXX: simplified locally testable version - tmpdir.join("test.txt").write("{{ h()}}:") + tmp_path.joinpath("test.txt").write_text("{{ h()}}:") jinja2 = pytest.importorskip("jinja2") - loader = jinja2.FileSystemLoader(str(tmpdir)) + loader = jinja2.FileSystemLoader(str(tmp_path)) env = jinja2.Environment(loader=loader) template = env.get_template("test.txt") excinfo = pytest.raises(ValueError, template.render, h=h) diff --git a/testing/test_argcomplete.py b/testing/test_argcomplete.py index a3224be5126..8c10e230b0c 100644 --- a/testing/test_argcomplete.py +++ b/testing/test_argcomplete.py @@ -1,7 +1,9 @@ import subprocess import sys +from pathlib import Path import pytest +from _pytest.monkeypatch import MonkeyPatch # Test for _argcomplete but not specific for any application. @@ -65,19 +67,22 @@ def __call__(self, prefix, **kwargs): class TestArgComplete: @pytest.mark.skipif("sys.platform in ('win32', 'darwin')") - def test_compare_with_compgen(self, tmpdir): + def test_compare_with_compgen( + self, tmp_path: Path, monkeypatch: MonkeyPatch + ) -> None: from _pytest._argcomplete import FastFilesCompleter ffc = FastFilesCompleter() fc = FilesCompleter() - with tmpdir.as_cwd(): - assert equal_with_bash("", ffc, fc, out=sys.stdout) + monkeypatch.chdir(tmp_path) - tmpdir.ensure("data") + assert equal_with_bash("", ffc, fc, out=sys.stdout) - for x in ["d", "data", "doesnotexist", ""]: - assert equal_with_bash(x, ffc, fc, out=sys.stdout) + tmp_path.cwd().joinpath("data").touch() + + for x in ["d", "data", "doesnotexist", ""]: + assert equal_with_bash(x, ffc, fc, out=sys.stdout) @pytest.mark.skipif("sys.platform in ('win32', 'darwin')") def test_remove_dir_prefix(self): From ac428f67ebb2469d91476cbe8ec7e10da6f6b106 Mon Sep 17 00:00:00 2001 From: sousajo Date: Fri, 1 Jan 2021 16:55:03 +0000 Subject: [PATCH 053/630] pytest-dev#8204 migrate tests on testing/code/test_source to tmp_path --- testing/code/test_source.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/testing/code/test_source.py b/testing/code/test_source.py index 6b8443fd243..083a7911f55 100644 --- a/testing/code/test_source.py +++ b/testing/code/test_source.py @@ -17,6 +17,7 @@ from _pytest._code import Frame from _pytest._code import getfslineno from _pytest._code import Source +from _pytest.pathlib import import_path def test_source_str_function() -> None: @@ -285,7 +286,9 @@ def g(): assert lines == ["def f():", " def g():", " pass"] -def test_source_of_class_at_eof_without_newline(tmpdir, _sys_snapshot) -> None: +def test_source_of_class_at_eof_without_newline( + tmpdir, _sys_snapshot, tmp_path: Path +) -> None: # this test fails because the implicit inspect.getsource(A) below # does not return the "x = 1" last line. source = Source( @@ -295,9 +298,10 @@ def method(self): x = 1 """ ) - path = tmpdir.join("a.py") - path.write(source) - s2 = Source(tmpdir.join("a.py").pyimport().A) + path = tmp_path.joinpath("a.py") + path.write_text(str(source)) + mod: Any = import_path(path) + s2 = Source(mod.A) assert str(source).strip() == str(s2).strip() From b02f1c8ae74d8c5273e3d651539e931af64c34fa Mon Sep 17 00:00:00 2001 From: Hong Xu Date: Fri, 1 Jan 2021 12:21:39 -0800 Subject: [PATCH 054/630] DOC: Update multiple references to testdir to pytester In https://docs.pytest.org/en/stable/reference.html#testdir, it is suggested: > New code should avoid using testdir in favor of pytester. Multiple spots in the documents still use testdir and they can be quite confusing (especially the plugin writing guide). --- CONTRIBUTING.rst | 14 +++++++------- doc/en/writing_plugins.rst | 18 +++++++++--------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 2669cb19509..054f809a818 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -324,20 +324,20 @@ Here is a simple overview, with pytest-specific bits: Writing Tests ~~~~~~~~~~~~~ -Writing tests for plugins or for pytest itself is often done using the `testdir fixture `_, as a "black-box" test. +Writing tests for plugins or for pytest itself is often done using the `pytester fixture `_, as a "black-box" test. For example, to ensure a simple test passes you can write: .. code-block:: python - def test_true_assertion(testdir): - testdir.makepyfile( + def test_true_assertion(pytester): + pytester.makepyfile( """ def test_foo(): assert True """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.assert_outcomes(failed=0, passed=1) @@ -346,14 +346,14 @@ Alternatively, it is possible to make checks based on the actual output of the t .. code-block:: python - def test_true_assertion(testdir): - testdir.makepyfile( + def test_true_assertion(pytester): + pytester.makepyfile( """ def test_foo(): assert False """ ) - result = testdir.runpytest() + result = pytester.runpytest() result.stdout.fnmatch_lines(["*assert False*", "*1 failed*"]) When choosing a file where to write a new test, take a look at the existing files and see if there's diff --git a/doc/en/writing_plugins.rst b/doc/en/writing_plugins.rst index f53f561cfad..e9806a6664d 100644 --- a/doc/en/writing_plugins.rst +++ b/doc/en/writing_plugins.rst @@ -337,7 +337,7 @@ testing directory: Alternatively you can invoke pytest with the ``-p pytester`` command line option. -This will allow you to use the :py:class:`testdir <_pytest.pytester.Testdir>` +This will allow you to use the :py:class:`pytester <_pytest.pytester.Pytester>` fixture for testing your plugin code. Let's demonstrate what you can do with the plugin with an example. Imagine we @@ -374,17 +374,17 @@ string value of ``Hello World!`` if we do not supply a value or ``Hello return _hello -Now the ``testdir`` fixture provides a convenient API for creating temporary +Now the ``pytester`` fixture provides a convenient API for creating temporary ``conftest.py`` files and test files. It also allows us to run the tests and return a result object, with which we can assert the tests' outcomes. .. code-block:: python - def test_hello(testdir): + def test_hello(pytester): """Make sure that our plugin works.""" # create a temporary conftest.py file - testdir.makeconftest( + pytester.makeconftest( """ import pytest @@ -399,7 +399,7 @@ return a result object, with which we can assert the tests' outcomes. ) # create a temporary pytest test file - testdir.makepyfile( + pytester.makepyfile( """ def test_hello_default(hello): assert hello() == "Hello World!" @@ -410,7 +410,7 @@ return a result object, with which we can assert the tests' outcomes. ) # run all tests with pytest - result = testdir.runpytest() + result = pytester.runpytest() # check that all 4 tests passed result.assert_outcomes(passed=4) @@ -430,9 +430,9 @@ Additionally it is possible to copy examples for an example folder before runnin # content of test_example.py - def test_plugin(testdir): - testdir.copy_example("test_example.py") - testdir.runpytest("-k", "test_example") + def test_plugin(pytester): + pytester.copy_example("test_example.py") + pytester.runpytest("-k", "test_example") def test_example(): From 6c575ad8c8aa298a8e8d11612d837c51880d528a Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Fri, 1 Jan 2021 14:42:14 +0200 Subject: [PATCH 055/630] fixtures: simplify FixtureRequest._get_fixturestack() --- src/_pytest/fixtures.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 53f33d3e13d..aed81029f44 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -607,14 +607,11 @@ def _get_active_fixturedef( def _get_fixturestack(self) -> List["FixtureDef[Any]"]: current = self values: List[FixtureDef[Any]] = [] - while 1: - fixturedef = getattr(current, "_fixturedef", None) - if fixturedef is None: - values.reverse() - return values - values.append(fixturedef) - assert isinstance(current, SubRequest) + while isinstance(current, SubRequest): + values.append(current._fixturedef) # type: ignore[has-type] current = current._parent_request + values.reverse() + return values def _compute_fixture_value(self, fixturedef: "FixtureDef[object]") -> None: """Create a SubRequest based on "self" and call the execute method From ade253c7906b082add837fbac8c193ec85847fbc Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Fri, 1 Jan 2021 23:18:17 +0200 Subject: [PATCH 056/630] fixtures: type annotate FixtureRequest.keywords --- src/_pytest/fixtures.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index aed81029f44..5bdee3096b1 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -16,6 +16,7 @@ from typing import Iterable from typing import Iterator from typing import List +from typing import MutableMapping from typing import Optional from typing import overload from typing import Sequence @@ -525,9 +526,10 @@ def fspath(self) -> py.path.local: return self._pyfuncitem.fspath # type: ignore @property - def keywords(self): + def keywords(self) -> MutableMapping[str, Any]: """Keywords/markers dictionary for the underlying node.""" - return self.node.keywords + node: nodes.Node = self.node + return node.keywords @property def session(self) -> "Session": From 8ee6d0a8666f91c9c537afacbe3c61f54e342f28 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Thu, 26 Nov 2020 14:50:44 +0200 Subject: [PATCH 057/630] Always use getfixturemarker() to access _pytestfixturefunction Keep knowledge of how the marker is stored encapsulated in one place. --- src/_pytest/nose.py | 24 +++++++++++++++--------- testing/python/integration.py | 4 +++- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/_pytest/nose.py b/src/_pytest/nose.py index bb8f99772ac..de91af85af6 100644 --- a/src/_pytest/nose.py +++ b/src/_pytest/nose.py @@ -2,11 +2,12 @@ from _pytest import python from _pytest import unittest from _pytest.config import hookimpl +from _pytest.fixtures import getfixturemarker from _pytest.nodes import Item @hookimpl(trylast=True) -def pytest_runtest_setup(item): +def pytest_runtest_setup(item) -> None: if is_potential_nosetest(item): if not call_optional(item.obj, "setup"): # Call module level setup if there is no object level one. @@ -15,7 +16,7 @@ def pytest_runtest_setup(item): item.session._setupstate.addfinalizer((lambda: teardown_nose(item)), item) -def teardown_nose(item): +def teardown_nose(item) -> None: if is_potential_nosetest(item): if not call_optional(item.obj, "teardown"): call_optional(item.parent.obj, "teardown") @@ -29,11 +30,16 @@ def is_potential_nosetest(item: Item) -> bool: ) -def call_optional(obj, name): +def call_optional(obj: object, name: str) -> bool: method = getattr(obj, name, None) - isfixture = hasattr(method, "_pytestfixturefunction") - if method is not None and not isfixture and callable(method): - # If there's any problems allow the exception to raise rather than - # silently ignoring them. - method() - return True + if method is None: + return False + is_fixture = getfixturemarker(method) is not None + if is_fixture: + return False + if not callable(method): + return False + # If there are any problems allow the exception to raise rather than + # silently ignoring it. + method() + return True diff --git a/testing/python/integration.py b/testing/python/integration.py index 5dce6bdca28..8576fcee341 100644 --- a/testing/python/integration.py +++ b/testing/python/integration.py @@ -3,6 +3,7 @@ import pytest from _pytest import runner from _pytest._code import getfslineno +from _pytest.fixtures import getfixturemarker from _pytest.pytester import Pytester @@ -334,7 +335,8 @@ def test_fix(fix): def test_pytestconfig_is_session_scoped() -> None: from _pytest.fixtures import pytestconfig - marker = pytestconfig._pytestfixturefunction # type: ignore + marker = getfixturemarker(pytestconfig) + assert marker is not None assert marker.scope == "session" From 2ff88098a7340142ed2c6d2c090b8c9fac001a5e Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Tue, 6 Oct 2020 20:19:52 +0300 Subject: [PATCH 058/630] python: inline a simple method I don't think it adds much value! --- src/_pytest/python.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 50ea60c2dff..29ebd176bbb 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -922,10 +922,6 @@ def copy(self) -> "CallSpec2": cs._idlist = list(self._idlist) return cs - def _checkargnotcontained(self, arg: str) -> None: - if arg in self.params or arg in self.funcargs: - raise ValueError(f"duplicate {arg!r}") - def getparam(self, name: str) -> object: try: return self.params[name] @@ -947,7 +943,8 @@ def setmulti2( param_index: int, ) -> None: for arg, val in zip(argnames, valset): - self._checkargnotcontained(arg) + if arg in self.params or arg in self.funcargs: + raise ValueError(f"duplicate {arg!r}") valtype_for_arg = valtypes[arg] if valtype_for_arg == "params": self.params[arg] = val From 14b5f5e528e52e22d05f086060c5f0dc08a6b37b Mon Sep 17 00:00:00 2001 From: Hong Xu Date: Sat, 2 Jan 2021 00:34:52 -0800 Subject: [PATCH 059/630] DOC: Mark pytest module Pytest document currently does not index the top-level package name `pytest`, which causes some trouble when building documentation that cross-refers to the pytest package via ``:mod:`pytest` ``. --- doc/en/reference.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/en/reference.rst b/doc/en/reference.rst index bc6c5670a5c..51c52b33ae9 100644 --- a/doc/en/reference.rst +++ b/doc/en/reference.rst @@ -3,6 +3,8 @@ API Reference ============= +.. module:: pytest + This page contains the full reference to pytest's API. .. contents:: From 8e00df4c4b62f08df0003e578c581e0b5728e571 Mon Sep 17 00:00:00 2001 From: bengartner Date: Mon, 4 Jan 2021 07:58:11 -0600 Subject: [PATCH 060/630] Add dot prefix if file makefile extension is invalid for pathlib (#8222) --- AUTHORS | 1 + changelog/8192.bugfix.rst | 3 +++ src/_pytest/pytester.py | 14 ++++++++++++++ testing/test_pytester.py | 31 +++++++++++++++++++++++++++++++ 4 files changed, 49 insertions(+) create mode 100644 changelog/8192.bugfix.rst diff --git a/AUTHORS b/AUTHORS index abac9f010a1..e75baa8cd92 100644 --- a/AUTHORS +++ b/AUTHORS @@ -40,6 +40,7 @@ Aron Curzon Aviral Verma Aviv Palivoda Barney Gale +Ben Gartner Ben Webb Benjamin Peterson Bernard Pratz diff --git a/changelog/8192.bugfix.rst b/changelog/8192.bugfix.rst new file mode 100644 index 00000000000..5b26ecbe45c --- /dev/null +++ b/changelog/8192.bugfix.rst @@ -0,0 +1,3 @@ +``testdir.makefile` now silently accepts values which don't start with ``.`` to maintain backward compatibility with older pytest versions. + +``pytester.makefile`` now issues a clearer error if the ``.`` is missing in the ``ext`` argument. diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index 4544d2c2bbb..95b22b3b23e 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -64,6 +64,7 @@ from _pytest.tmpdir import TempPathFactory from _pytest.warning_types import PytestWarning + if TYPE_CHECKING: from typing_extensions import Literal @@ -750,6 +751,11 @@ def _makefile( ) -> Path: items = list(files.items()) + if ext and not ext.startswith("."): + raise ValueError( + f"pytester.makefile expects a file extension, try .{ext} instead of {ext}" + ) + def to_text(s: Union[Any, bytes]) -> str: return s.decode(encoding) if isinstance(s, bytes) else str(s) @@ -1559,6 +1565,14 @@ def finalize(self) -> None: def makefile(self, ext, *args, **kwargs) -> py.path.local: """See :meth:`Pytester.makefile`.""" + if ext and not ext.startswith("."): + # pytester.makefile is going to throw a ValueError in a way that + # testdir.makefile did not, because + # pathlib.Path is stricter suffixes than py.path + # This ext arguments is likely user error, but since testdir has + # allowed this, we will prepend "." as a workaround to avoid breaking + # testdir usage that worked before + ext = "." + ext return py.path.local(str(self._pytester.makefile(ext, *args, **kwargs))) def makeconftest(self, source) -> py.path.local: diff --git a/testing/test_pytester.py b/testing/test_pytester.py index 57d6f4fd9eb..5823d51155c 100644 --- a/testing/test_pytester.py +++ b/testing/test_pytester.py @@ -17,6 +17,7 @@ from _pytest.pytester import Pytester from _pytest.pytester import SysModulesSnapshot from _pytest.pytester import SysPathsSnapshot +from _pytest.pytester import Testdir def test_make_hook_recorder(pytester: Pytester) -> None: @@ -816,3 +817,33 @@ def test_makefile_joins_absolute_path(pytester: Pytester) -> None: def test_testtmproot(testdir) -> None: """Check test_tmproot is a py.path attribute for backward compatibility.""" assert testdir.test_tmproot.check(dir=1) + + +def test_testdir_makefile_dot_prefixes_extension_silently( + testdir: Testdir, +) -> None: + """For backwards compat #8192""" + p1 = testdir.makefile("foo.bar", "") + assert ".foo.bar" in str(p1) + + +def test_pytester_makefile_dot_prefixes_extension_with_warning( + pytester: Pytester, +) -> None: + with pytest.raises( + ValueError, + match="pytester.makefile expects a file extension, try .foo.bar instead of foo.bar", + ): + pytester.makefile("foo.bar", "") + + +def test_testdir_makefile_ext_none_raises_type_error(testdir) -> None: + """For backwards compat #8192""" + with pytest.raises(TypeError): + testdir.makefile(None, "") + + +def test_testdir_makefile_ext_empty_string_makes_file(testdir) -> None: + """For backwards compat #8192""" + p1 = testdir.makefile("", "") + assert "test_testdir_makefile" in str(p1) From 65b8391ead4ef3849e6181e7ec899625b78ba992 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 5 Jan 2021 19:39:50 +0100 Subject: [PATCH 061/630] doc: Add note about training early bird discount --- doc/en/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/index.rst b/doc/en/index.rst index ad2057ff14a..58f6c1d86c7 100644 --- a/doc/en/index.rst +++ b/doc/en/index.rst @@ -2,7 +2,7 @@ .. sidebar:: Next Open Trainings - - `Professional testing with Python `_, via Python Academy, February 1-3 2021, Leipzig (Germany) and remote. + - `Professional testing with Python `_, via Python Academy, February 1-3 2021, remote and Leipzig (Germany). **Early-bird discount available until January 15th**. Also see `previous talks and blogposts `_. From 78fb97105f38dc286353bbc331a243b6e753fe3c Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 6 Jan 2021 13:33:33 +0100 Subject: [PATCH 062/630] Make code.FormattedExcinfo.get_source more defensive When line_index was a large negative number, get_source failed on `source.lines[line_index]`. Use the same dummy Source as with a large positive line_index. --- src/_pytest/_code/code.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/_pytest/_code/code.py b/src/_pytest/_code/code.py index b8521756067..af3bdf0561b 100644 --- a/src/_pytest/_code/code.py +++ b/src/_pytest/_code/code.py @@ -721,11 +721,11 @@ def get_source( ) -> List[str]: """Return formatted and marked up source lines.""" lines = [] - if source is None or line_index >= len(source.lines): + if source is not None and line_index < 0: + line_index += len(source.lines) + if source is None or line_index >= len(source.lines) or line_index < 0: source = Source("???") line_index = 0 - if line_index < 0: - line_index += len(source) space_prefix = " " if short: lines.append(space_prefix + source.lines[line_index].strip()) From 80c33c817879ca9cf1eb66e4fa2ff7a01f33bb39 Mon Sep 17 00:00:00 2001 From: Gergely Imreh Date: Fri, 8 Jan 2021 11:43:51 +0000 Subject: [PATCH 063/630] Add missing import into example script in documentation --- doc/en/skipping.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/en/skipping.rst b/doc/en/skipping.rst index 282820545c3..610d3d43bca 100644 --- a/doc/en/skipping.rst +++ b/doc/en/skipping.rst @@ -405,6 +405,7 @@ test instances when using parametrize: .. code-block:: python + import sys import pytest From af78efc7fa903bd7f445c451212ab625c11954cd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Jan 2021 03:15:59 +0000 Subject: [PATCH 064/630] build(deps): bump pytest-mock in /testing/plugins_integration Bumps [pytest-mock](https://github.com/pytest-dev/pytest-mock) from 3.4.0 to 3.5.1. - [Release notes](https://github.com/pytest-dev/pytest-mock/releases) - [Changelog](https://github.com/pytest-dev/pytest-mock/blob/master/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest-mock/compare/v3.4.0...v3.5.1) Signed-off-by: dependabot[bot] --- testing/plugins_integration/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/plugins_integration/requirements.txt b/testing/plugins_integration/requirements.txt index b2ca3e3236a..c48a5cfd257 100644 --- a/testing/plugins_integration/requirements.txt +++ b/testing/plugins_integration/requirements.txt @@ -6,7 +6,7 @@ pytest-cov==2.10.1 pytest-django==4.1.0 pytest-flakes==4.0.3 pytest-html==3.1.1 -pytest-mock==3.4.0 +pytest-mock==3.5.1 pytest-rerunfailures==9.1.1 pytest-sugar==0.9.4 pytest-trio==0.7.0 From cfa0c3b0d94eea25db8f7de57ce93c52598009db Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Jan 2021 03:16:03 +0000 Subject: [PATCH 065/630] build(deps): bump django in /testing/plugins_integration Bumps [django](https://github.com/django/django) from 3.1.4 to 3.1.5. - [Release notes](https://github.com/django/django/releases) - [Commits](https://github.com/django/django/compare/3.1.4...3.1.5) Signed-off-by: dependabot[bot] --- testing/plugins_integration/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/plugins_integration/requirements.txt b/testing/plugins_integration/requirements.txt index b2ca3e3236a..f5036023097 100644 --- a/testing/plugins_integration/requirements.txt +++ b/testing/plugins_integration/requirements.txt @@ -1,5 +1,5 @@ anyio[curio,trio]==2.0.2 -django==3.1.4 +django==3.1.5 pytest-asyncio==0.14.0 pytest-bdd==4.0.2 pytest-cov==2.10.1 From 42d5545f42f7f11345913efedf852cbea3753e58 Mon Sep 17 00:00:00 2001 From: Anton <44246099+antonblr@users.noreply.github.com> Date: Wed, 13 Jan 2021 17:02:26 -0800 Subject: [PATCH 066/630] unittest: cleanup unexpected success handling (#8231) * unittest: cleanup unexpected success handling * update comment --- src/_pytest/skipping.py | 11 +---------- src/_pytest/unittest.py | 24 ++++++++++++------------ testing/test_unittest.py | 9 +++++++-- 3 files changed, 20 insertions(+), 24 deletions(-) diff --git a/src/_pytest/skipping.py b/src/_pytest/skipping.py index 9aacfecee7a..c7afef5db87 100644 --- a/src/_pytest/skipping.py +++ b/src/_pytest/skipping.py @@ -234,7 +234,6 @@ def evaluate_xfail_marks(item: Item) -> Optional[Xfail]: skipped_by_mark_key = StoreKey[bool]() # Saves the xfail mark evaluation. Can be refreshed during call if None. xfailed_key = StoreKey[Optional[Xfail]]() -unexpectedsuccess_key = StoreKey[str]() @hookimpl(tryfirst=True) @@ -271,15 +270,7 @@ def pytest_runtest_makereport(item: Item, call: CallInfo[None]): outcome = yield rep = outcome.get_result() xfailed = item._store.get(xfailed_key, None) - # unittest special case, see setting of unexpectedsuccess_key - if unexpectedsuccess_key in item._store and rep.when == "call": - reason = item._store[unexpectedsuccess_key] - if reason: - rep.longrepr = f"Unexpected success: {reason}" - else: - rep.longrepr = "Unexpected success" - rep.outcome = "failed" - elif item.config.option.runxfail: + if item.config.option.runxfail: pass # don't interfere elif call.excinfo and isinstance(call.excinfo.value, xfail.Exception): assert call.excinfo.value.msg is not None diff --git a/src/_pytest/unittest.py b/src/_pytest/unittest.py index 55f15efe4b7..cc616578b09 100644 --- a/src/_pytest/unittest.py +++ b/src/_pytest/unittest.py @@ -30,10 +30,10 @@ from _pytest.python import PyCollector from _pytest.runner import CallInfo from _pytest.skipping import skipped_by_mark_key -from _pytest.skipping import unexpectedsuccess_key if TYPE_CHECKING: import unittest + import twisted.trial.unittest from _pytest.fixtures import _Scope @@ -273,9 +273,18 @@ def addExpectedFailure( self._addexcinfo(sys.exc_info()) def addUnexpectedSuccess( - self, testcase: "unittest.TestCase", reason: str = "" + self, + testcase: "unittest.TestCase", + reason: Optional["twisted.trial.unittest.Todo"] = None, ) -> None: - self._store[unexpectedsuccess_key] = reason + msg = "Unexpected success" + if reason: + msg += f": {reason.reason}" + # Preserve unittest behaviour - fail the test. Explicitly not an XPASS. + try: + fail(msg, pytrace=False) + except fail.Exception: + self._addexcinfo(sys.exc_info()) def addSuccess(self, testcase: "unittest.TestCase") -> None: pass @@ -283,15 +292,6 @@ def addSuccess(self, testcase: "unittest.TestCase") -> None: def stopTest(self, testcase: "unittest.TestCase") -> None: pass - def _expecting_failure(self, test_method) -> bool: - """Return True if the given unittest method (or the entire class) is marked - with @expectedFailure.""" - expecting_failure_method = getattr( - test_method, "__unittest_expecting_failure__", False - ) - expecting_failure_class = getattr(self, "__unittest_expecting_failure__", False) - return bool(expecting_failure_class or expecting_failure_method) - def runtest(self) -> None: from _pytest.debugging import maybe_wrap_pytest_function_for_tracing diff --git a/testing/test_unittest.py b/testing/test_unittest.py index feee09286c2..69bafc26d61 100644 --- a/testing/test_unittest.py +++ b/testing/test_unittest.py @@ -765,7 +765,8 @@ def test_failing_test_is_xfail(self): @pytest.mark.parametrize("runner", ["pytest", "unittest"]) def test_unittest_expected_failure_for_passing_test_is_fail( - pytester: Pytester, runner + pytester: Pytester, + runner: str, ) -> None: script = pytester.makepyfile( """ @@ -782,7 +783,11 @@ def test_passing_test_is_fail(self): if runner == "pytest": result = pytester.runpytest("-rxX") result.stdout.fnmatch_lines( - ["*MyTestCase*test_passing_test_is_fail*", "*1 failed*"] + [ + "*MyTestCase*test_passing_test_is_fail*", + "Unexpected success", + "*1 failed*", + ] ) else: result = pytester.runpython(script) From addbd3161e37edffebb2c0f6527da49b0515d1a1 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Thu, 14 Jan 2021 16:51:18 +0200 Subject: [PATCH 067/630] nose,fixtures: use the public item API for adding finalizers --- src/_pytest/fixtures.py | 10 +++------- src/_pytest/nose.py | 2 +- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 5bdee3096b1..43a40a86449 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -543,10 +543,8 @@ def addfinalizer(self, finalizer: Callable[[], object]) -> None: self._addfinalizer(finalizer, scope=self.scope) def _addfinalizer(self, finalizer: Callable[[], object], scope) -> None: - colitem = self._getscopeitem(scope) - self._pyfuncitem.session._setupstate.addfinalizer( - finalizer=finalizer, colitem=colitem - ) + item = self._getscopeitem(scope) + item.addfinalizer(finalizer) def applymarker(self, marker: Union[str, MarkDecorator]) -> None: """Apply a marker to a single test function invocation. @@ -694,9 +692,7 @@ def _schedule_finalizers( self, fixturedef: "FixtureDef[object]", subrequest: "SubRequest" ) -> None: # If fixture function failed it might have registered finalizers. - self.session._setupstate.addfinalizer( - functools.partial(fixturedef.finish, request=subrequest), subrequest.node - ) + subrequest.node.addfinalizer(lambda: fixturedef.finish(request=subrequest)) def _check_scope( self, diff --git a/src/_pytest/nose.py b/src/_pytest/nose.py index de91af85af6..5bba030a509 100644 --- a/src/_pytest/nose.py +++ b/src/_pytest/nose.py @@ -13,7 +13,7 @@ def pytest_runtest_setup(item) -> None: # Call module level setup if there is no object level one. call_optional(item.parent.obj, "setup") # XXX This implies we only call teardown when setup worked. - item.session._setupstate.addfinalizer((lambda: teardown_nose(item)), item) + item.addfinalizer(lambda: teardown_nose(item)) def teardown_nose(item) -> None: From 096bae6c68840c19bdc97cdfdf99b96dbcb2c427 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Thu, 14 Jan 2021 17:05:01 +0200 Subject: [PATCH 068/630] unittest: add clarifying comment on unittest.SkipTest -> pytest.skip code I was tempted to remove it, until I figured out why it was there. --- src/_pytest/unittest.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/_pytest/unittest.py b/src/_pytest/unittest.py index cc616578b09..6a90188cab9 100644 --- a/src/_pytest/unittest.py +++ b/src/_pytest/unittest.py @@ -343,6 +343,10 @@ def pytest_runtest_makereport(item: Item, call: CallInfo[None]) -> None: except AttributeError: pass + # Convert unittest.SkipTest to pytest.skip. + # This is actually only needed for nose, which reuses unittest.SkipTest for + # its own nose.SkipTest. For unittest TestCases, SkipTest is already + # handled internally, and doesn't reach here. unittest = sys.modules.get("unittest") if ( unittest @@ -350,7 +354,6 @@ def pytest_runtest_makereport(item: Item, call: CallInfo[None]) -> None: and isinstance(call.excinfo.value, unittest.SkipTest) # type: ignore[attr-defined] ): excinfo = call.excinfo - # Let's substitute the excinfo with a pytest.skip one. call2 = CallInfo[None].from_call( lambda: pytest.skip(str(excinfo.value)), call.when ) From 3dde519f53b4480a3b30d58a1c71ca4505ae5ff7 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Thu, 14 Jan 2021 17:42:38 +0200 Subject: [PATCH 069/630] nose: type annotate with some resulting refactoring --- src/_pytest/nose.py | 48 +++++++++++++++++++++++---------------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/src/_pytest/nose.py b/src/_pytest/nose.py index 5bba030a509..16d5224e9fa 100644 --- a/src/_pytest/nose.py +++ b/src/_pytest/nose.py @@ -1,33 +1,35 @@ """Run testsuites written for nose.""" -from _pytest import python -from _pytest import unittest from _pytest.config import hookimpl from _pytest.fixtures import getfixturemarker from _pytest.nodes import Item +from _pytest.python import Function +from _pytest.unittest import TestCaseFunction @hookimpl(trylast=True) -def pytest_runtest_setup(item) -> None: - if is_potential_nosetest(item): - if not call_optional(item.obj, "setup"): - # Call module level setup if there is no object level one. - call_optional(item.parent.obj, "setup") - # XXX This implies we only call teardown when setup worked. - item.addfinalizer(lambda: teardown_nose(item)) - - -def teardown_nose(item) -> None: - if is_potential_nosetest(item): - if not call_optional(item.obj, "teardown"): - call_optional(item.parent.obj, "teardown") - - -def is_potential_nosetest(item: Item) -> bool: - # Extra check needed since we do not do nose style setup/teardown - # on direct unittest style classes. - return isinstance(item, python.Function) and not isinstance( - item, unittest.TestCaseFunction - ) +def pytest_runtest_setup(item: Item) -> None: + if not isinstance(item, Function): + return + # Don't do nose style setup/teardown on direct unittest style classes. + if isinstance(item, TestCaseFunction): + return + + # Capture the narrowed type of item for the teardown closure, + # see https://github.com/python/mypy/issues/2608 + func = item + + if not call_optional(func.obj, "setup"): + # Call module level setup if there is no object level one. + assert func.parent is not None + call_optional(func.parent.obj, "setup") # type: ignore[attr-defined] + + def teardown_nose() -> None: + if not call_optional(func.obj, "teardown"): + assert func.parent is not None + call_optional(func.parent.obj, "teardown") # type: ignore[attr-defined] + + # XXX This implies we only call teardown when setup worked. + func.addfinalizer(teardown_nose) def call_optional(obj: object, name: str) -> bool: From 7f989203ed58119bf63e026cbb99df274c7700d6 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Thu, 14 Jan 2021 11:58:59 +0200 Subject: [PATCH 070/630] Improve way in which skip location is fixed up when skipped by mark When `pytest.skip()` is called inside a test function, the skip location should be reported as the line that made the call, however when `pytest.skip()` is called by the `pytest.mark.skip` and similar mechanisms, the location should be reported at the item's location, because the exact location is some irrelevant internal code. Currently the item-location case is implemented by the caller setting a boolean key on the item's store and the `skipping` plugin checking it and fixing up the location if needed. This is really roundabout IMO and breaks encapsulation. Instead, allow the caller to specify directly on the skip exception whether to use the item's location or not. For now, this is entirely private. --- src/_pytest/outcomes.py | 5 +++++ src/_pytest/reports.py | 7 ++++++- src/_pytest/skipping.py | 18 +----------------- src/_pytest/unittest.py | 6 ++---- 4 files changed, 14 insertions(+), 22 deletions(-) diff --git a/src/_pytest/outcomes.py b/src/_pytest/outcomes.py index 8f6203fd7fa..756b4098b36 100644 --- a/src/_pytest/outcomes.py +++ b/src/_pytest/outcomes.py @@ -58,9 +58,14 @@ def __init__( msg: Optional[str] = None, pytrace: bool = True, allow_module_level: bool = False, + *, + _use_item_location: bool = False, ) -> None: OutcomeException.__init__(self, msg=msg, pytrace=pytrace) self.allow_module_level = allow_module_level + # If true, the skip location is reported as the item's location, + # instead of the place that raises the exception/calls skip(). + self._use_item_location = _use_item_location class Failed(OutcomeException): diff --git a/src/_pytest/reports.py b/src/_pytest/reports.py index d2d7115b2e5..303f731ddaa 100644 --- a/src/_pytest/reports.py +++ b/src/_pytest/reports.py @@ -324,7 +324,12 @@ def from_item_and_call(cls, item: Item, call: "CallInfo[None]") -> "TestReport": elif isinstance(excinfo.value, skip.Exception): outcome = "skipped" r = excinfo._getreprcrash() - longrepr = (str(r.path), r.lineno, r.message) + if excinfo.value._use_item_location: + filename, line = item.reportinfo()[:2] + assert line is not None + longrepr = str(filename), line + 1, r.message + else: + longrepr = (str(r.path), r.lineno, r.message) else: outcome = "failed" if call.when == "call": diff --git a/src/_pytest/skipping.py b/src/_pytest/skipping.py index c7afef5db87..1ad312919ca 100644 --- a/src/_pytest/skipping.py +++ b/src/_pytest/skipping.py @@ -230,8 +230,6 @@ def evaluate_xfail_marks(item: Item) -> Optional[Xfail]: return None -# Whether skipped due to skip or skipif marks. -skipped_by_mark_key = StoreKey[bool]() # Saves the xfail mark evaluation. Can be refreshed during call if None. xfailed_key = StoreKey[Optional[Xfail]]() @@ -239,9 +237,8 @@ def evaluate_xfail_marks(item: Item) -> Optional[Xfail]: @hookimpl(tryfirst=True) def pytest_runtest_setup(item: Item) -> None: skipped = evaluate_skip_marks(item) - item._store[skipped_by_mark_key] = skipped is not None if skipped: - skip(skipped.reason) + raise skip.Exception(skipped.reason, _use_item_location=True) item._store[xfailed_key] = xfailed = evaluate_xfail_marks(item) if xfailed and not item.config.option.runxfail and not xfailed.run: @@ -292,19 +289,6 @@ def pytest_runtest_makereport(item: Item, call: CallInfo[None]): rep.outcome = "passed" rep.wasxfail = xfailed.reason - if ( - item._store.get(skipped_by_mark_key, True) - and rep.skipped - and type(rep.longrepr) is tuple - ): - # Skipped by mark.skipif; change the location of the failure - # to point to the item definition, otherwise it will display - # the location of where the skip exception was raised within pytest. - _, _, reason = rep.longrepr - filename, line = item.reportinfo()[:2] - assert line is not None - rep.longrepr = str(filename), line + 1, reason - def pytest_report_teststatus(report: BaseReport) -> Optional[Tuple[str, str, str]]: if hasattr(report, "wasxfail"): diff --git a/src/_pytest/unittest.py b/src/_pytest/unittest.py index 6a90188cab9..719eb4e8823 100644 --- a/src/_pytest/unittest.py +++ b/src/_pytest/unittest.py @@ -29,7 +29,6 @@ from _pytest.python import Function from _pytest.python import PyCollector from _pytest.runner import CallInfo -from _pytest.skipping import skipped_by_mark_key if TYPE_CHECKING: import unittest @@ -150,7 +149,7 @@ def cleanup(*args): def fixture(self, request: FixtureRequest) -> Generator[None, None, None]: if _is_skipped(self): reason = self.__unittest_skip_why__ - pytest.skip(reason) + raise pytest.skip.Exception(reason, _use_item_location=True) if setup is not None: try: if pass_self: @@ -256,9 +255,8 @@ def addFailure( def addSkip(self, testcase: "unittest.TestCase", reason: str) -> None: try: - skip(reason) + raise pytest.skip.Exception(reason, _use_item_location=True) except skip.Exception: - self._store[skipped_by_mark_key] = True self._addexcinfo(sys.exc_info()) def addExpectedFailure( From 25e657bfc15aecd8fa8787a028ffc10fc9ac96d5 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Thu, 14 Jan 2021 18:14:39 +0200 Subject: [PATCH 071/630] Deprecate raising unittest.SkipTest to skip tests during collection It is not very clear why this code exists -- we are not running any unittest or nose code during collection, and really these frameworks don't have the concept of collection at all, and just raising these exceptions at e.g. the module level would cause an error. So unless I'm missing something, I don't think anyone is using this. Deprecate it so we can eventually clear up this code and keep unittest more tightly restricted to its plugin. --- changelog/8242.deprecation.rst | 7 +++++++ doc/en/deprecations.rst | 14 ++++++++++++++ src/_pytest/deprecated.py | 5 +++++ src/_pytest/runner.py | 7 +++++++ testing/deprecated_test.py | 17 +++++++++++++++++ 5 files changed, 50 insertions(+) create mode 100644 changelog/8242.deprecation.rst diff --git a/changelog/8242.deprecation.rst b/changelog/8242.deprecation.rst new file mode 100644 index 00000000000..b2e8566eaa9 --- /dev/null +++ b/changelog/8242.deprecation.rst @@ -0,0 +1,7 @@ +Raising :class:`unittest.SkipTest` to skip collection of tests during the +pytest collection phase is deprecated. Use :func:`pytest.skip` instead. + +Note: This deprecation only relates to using `unittest.SkipTest` during test +collection. You are probably not doing that. Ordinary usage of +:class:`unittest.SkipTest` / :meth:`unittest.TestCase.skipTest` / +:func:`unittest.skip` in unittest test cases is fully supported. diff --git a/doc/en/deprecations.rst b/doc/en/deprecations.rst index ec2397e596f..0dcbd8ceb36 100644 --- a/doc/en/deprecations.rst +++ b/doc/en/deprecations.rst @@ -18,6 +18,20 @@ Deprecated Features Below is a complete list of all pytest features which are considered deprecated. Using those features will issue :class:`PytestWarning` or subclasses, which can be filtered using :ref:`standard warning filters `. +Raising ``unittest.SkipTest`` during collection +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. deprecated:: 6.3 + +Raising :class:`unittest.SkipTest` to skip collection of tests during the +pytest collection phase is deprecated. Use :func:`pytest.skip` instead. + +Note: This deprecation only relates to using `unittest.SkipTest` during test +collection. You are probably not doing that. Ordinary usage of +:class:`unittest.SkipTest` / :meth:`unittest.TestCase.skipTest` / +:func:`unittest.skip` in unittest test cases is fully supported. + + The ``--strict`` command-line option ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/src/_pytest/deprecated.py b/src/_pytest/deprecated.py index 19b31d66538..fa91f909769 100644 --- a/src/_pytest/deprecated.py +++ b/src/_pytest/deprecated.py @@ -64,6 +64,11 @@ PRIVATE = PytestDeprecationWarning("A private pytest class or function was used.") +UNITTEST_SKIP_DURING_COLLECTION = PytestDeprecationWarning( + "Raising unittest.SkipTest to skip tests during collection is deprecated. " + "Use pytest.skip() instead." +) + # You want to make some `__init__` or function "private". # diff --git a/src/_pytest/runner.py b/src/_pytest/runner.py index df046a78aca..844e41f8057 100644 --- a/src/_pytest/runner.py +++ b/src/_pytest/runner.py @@ -2,6 +2,7 @@ import bdb import os import sys +import warnings from typing import Callable from typing import cast from typing import Dict @@ -27,6 +28,7 @@ from _pytest.compat import final from _pytest.config.argparsing import Parser from _pytest.deprecated import check_ispytest +from _pytest.deprecated import UNITTEST_SKIP_DURING_COLLECTION from _pytest.nodes import Collector from _pytest.nodes import Item from _pytest.nodes import Node @@ -374,6 +376,11 @@ def pytest_make_collect_report(collector: Collector) -> CollectReport: # Type ignored because unittest is loaded dynamically. skip_exceptions.append(unittest.SkipTest) # type: ignore if isinstance(call.excinfo.value, tuple(skip_exceptions)): + if unittest is not None and isinstance( + call.excinfo.value, unittest.SkipTest # type: ignore[attr-defined] + ): + warnings.warn(UNITTEST_SKIP_DURING_COLLECTION, stacklevel=2) + outcome = "skipped" r_ = collector._repr_failure_py(call.excinfo, "line") assert isinstance(r_, ExceptionChainRepr), repr(r_) diff --git a/testing/deprecated_test.py b/testing/deprecated_test.py index 6d92d181f99..18300f62a1a 100644 --- a/testing/deprecated_test.py +++ b/testing/deprecated_test.py @@ -136,3 +136,20 @@ def __init__(self, foo: int, *, _ispytest: bool = False) -> None: # Doesn't warn. PrivateInit(10, _ispytest=True) + + +def test_raising_unittest_skiptest_during_collection_is_deprecated( + pytester: Pytester, +) -> None: + pytester.makepyfile( + """ + import unittest + raise unittest.SkipTest() + """ + ) + result = pytester.runpytest() + result.stdout.fnmatch_lines( + [ + "*PytestDeprecationWarning: Raising unittest.SkipTest*", + ] + ) From a9e43152bc5081afccc6de6ef5526ff35c525fed Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Sun, 17 Jan 2021 14:23:07 +0100 Subject: [PATCH 072/630] alter the PyObjMixin to carry over typing information from Node as PyObjMixin is always supposed to be mixed in the mro before nodes.Node the behavior doesn't change, but all the typing information carry over to help mypy. extracted from #8037 --- changelog/8248.trivial.rst | 1 + src/_pytest/python.py | 18 +++++------------- 2 files changed, 6 insertions(+), 13 deletions(-) create mode 100644 changelog/8248.trivial.rst diff --git a/changelog/8248.trivial.rst b/changelog/8248.trivial.rst new file mode 100644 index 00000000000..0a9319d9cd5 --- /dev/null +++ b/changelog/8248.trivial.rst @@ -0,0 +1 @@ +Internal Restructure: let python.PyObjMixing inherit from nodes.Node to carry over typing information. diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 29ebd176bbb..eabd7b31154 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -23,7 +23,6 @@ from typing import Sequence from typing import Set from typing import Tuple -from typing import Type from typing import TYPE_CHECKING from typing import Union @@ -255,20 +254,13 @@ def pytest_pycollect_makeitem(collector: "PyCollector", name: str, obj: object): return res -class PyobjMixin: - _ALLOW_MARKERS = True - - # Function and attributes that the mixin needs (for type-checking only). - if TYPE_CHECKING: - name: str = "" - parent: Optional[nodes.Node] = None - own_markers: List[Mark] = [] +class PyobjMixin(nodes.Node): + """this mix-in inherits from Node to carry over the typing information - def getparent(self, cls: Type[nodes._NodeType]) -> Optional[nodes._NodeType]: - ... + as its intended to always mix in before a node + its position in the mro is unaffected""" - def listchain(self) -> List[nodes.Node]: - ... + _ALLOW_MARKERS = True @property def module(self): From 9ba1821e9121c3ee49a68b4863cd7ee50de4a5a5 Mon Sep 17 00:00:00 2001 From: Andreas Motl Date: Sun, 17 Jan 2021 19:23:57 +0100 Subject: [PATCH 073/630] Fix faulthandler for Twisted Logger when used with "--capture=no" The Twisted Logger will return an invalid file descriptor since it is not backed by an FD. So, let's also forward this to the same code path as with `pytest-xdist`. --- AUTHORS | 1 + changelog/8249.bugfix.rst | 1 + src/_pytest/faulthandler.py | 7 ++++++- testing/test_faulthandler.py | 25 +++++++++++++++++++++++++ 4 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 changelog/8249.bugfix.rst diff --git a/AUTHORS b/AUTHORS index e75baa8cd92..0c721a49610 100644 --- a/AUTHORS +++ b/AUTHORS @@ -21,6 +21,7 @@ Anders Hovmöller Andras Mitzki Andras Tim Andrea Cimatoribus +Andreas Motl Andreas Zeidler Andrey Paramonov Andrzej Klajnert diff --git a/changelog/8249.bugfix.rst b/changelog/8249.bugfix.rst new file mode 100644 index 00000000000..aa084c75738 --- /dev/null +++ b/changelog/8249.bugfix.rst @@ -0,0 +1 @@ +Fix the ``faulthandler`` plugin for occasions when running with ``twisted.logger`` and using ``pytest --capture=no``. diff --git a/src/_pytest/faulthandler.py b/src/_pytest/faulthandler.py index d0cc0430c49..ff673b5b164 100644 --- a/src/_pytest/faulthandler.py +++ b/src/_pytest/faulthandler.py @@ -69,7 +69,12 @@ def pytest_unconfigure(self, config: Config) -> None: @staticmethod def _get_stderr_fileno(): try: - return sys.stderr.fileno() + fileno = sys.stderr.fileno() + # The Twisted Logger will return an invalid file descriptor since it is not backed + # by an FD. So, let's also forward this to the same code path as with pytest-xdist. + if fileno == -1: + raise AttributeError() + return fileno except (AttributeError, io.UnsupportedOperation): # pytest-xdist monkeypatches sys.stderr with an object that is not an actual file. # https://docs.python.org/3/library/faulthandler.html#issue-with-file-descriptors diff --git a/testing/test_faulthandler.py b/testing/test_faulthandler.py index caf39813cf4..370084c125f 100644 --- a/testing/test_faulthandler.py +++ b/testing/test_faulthandler.py @@ -1,3 +1,4 @@ +import io import sys import pytest @@ -135,3 +136,27 @@ def test(): result.stdout.no_fnmatch_line(warning_line) result.stdout.fnmatch_lines("*1 passed*") assert result.ret == 0 + + +def test_get_stderr_fileno_invalid_fd() -> None: + """Test for faulthandler being able to handle invalid file descriptors for stderr (#8249).""" + from _pytest.faulthandler import FaultHandlerHooks + + class StdErrWrapper(io.StringIO): + """ + Mimic ``twisted.logger.LoggingFile`` to simulate returning an invalid file descriptor. + + https://github.com/twisted/twisted/blob/twisted-20.3.0/src/twisted/logger/_io.py#L132-L139 + """ + + def fileno(self): + return -1 + + wrapper = StdErrWrapper() + + with pytest.MonkeyPatch.context() as mp: + mp.setattr("sys.stderr", wrapper) + + # Even when the stderr wrapper signals an invalid file descriptor, + # ``_get_stderr_fileno()`` should return the real one. + assert FaultHandlerHooks._get_stderr_fileno() == 2 From eef2d1a8e268ef727eec8f1c38119ee76cdaebc2 Mon Sep 17 00:00:00 2001 From: Jeff Widman Date: Tue, 19 Jan 2021 06:45:45 -0800 Subject: [PATCH 074/630] Fix pep8 import order in docs (#8253) --- doc/en/example/parametrize.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/en/example/parametrize.rst b/doc/en/example/parametrize.rst index 6e2f53984ee..a65ee5f2fd9 100644 --- a/doc/en/example/parametrize.rst +++ b/doc/en/example/parametrize.rst @@ -97,10 +97,10 @@ the argument name: # content of test_time.py - import pytest - from datetime import datetime, timedelta + import pytest + testdata = [ (datetime(2001, 12, 12), datetime(2001, 12, 11), timedelta(1)), (datetime(2001, 12, 11), datetime(2001, 12, 12), timedelta(-1)), From adc0f29b8f8fa8a63e0592c38503855a5b615a29 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 20 Jan 2021 10:05:36 -0300 Subject: [PATCH 075/630] Always handle faulthandler stderr even if already enabled It seems the code that would not install pytest's faulthandler support if it was already enabled is not really needed at all, and even detrimental when using `python -X dev -m pytest` to run Python in "dev" mode. Also simplified the plugin by removing the hook class, now the hooks will always be active so there's no need to delay the hook definitions anymore. Fix #8258 --- changelog/8258.bugfix.rst | 3 + src/_pytest/faulthandler.py | 134 +++++++++++++++-------------------- testing/test_faulthandler.py | 29 +++----- 3 files changed, 70 insertions(+), 96 deletions(-) create mode 100644 changelog/8258.bugfix.rst diff --git a/changelog/8258.bugfix.rst b/changelog/8258.bugfix.rst new file mode 100644 index 00000000000..6518ec0b738 --- /dev/null +++ b/changelog/8258.bugfix.rst @@ -0,0 +1,3 @@ +Fixed issue where pytest's ``faulthandler`` support would not dump traceback on crashes +if the :mod:`faulthandler` module was already enabled during pytest startup (using +``python -X dev -m pytest`` for example). diff --git a/src/_pytest/faulthandler.py b/src/_pytest/faulthandler.py index ff673b5b164..9592de82d6d 100644 --- a/src/_pytest/faulthandler.py +++ b/src/_pytest/faulthandler.py @@ -25,92 +25,72 @@ def pytest_addoption(parser: Parser) -> None: def pytest_configure(config: Config) -> None: import faulthandler - if not faulthandler.is_enabled(): - # faulthhandler is not enabled, so install plugin that does the actual work - # of enabling faulthandler before each test executes. - config.pluginmanager.register(FaultHandlerHooks(), "faulthandler-hooks") - else: - # Do not handle dumping to stderr if faulthandler is already enabled, so warn - # users that the option is being ignored. - timeout = FaultHandlerHooks.get_timeout_config_value(config) - if timeout > 0: - config.issue_config_time_warning( - pytest.PytestConfigWarning( - "faulthandler module enabled before pytest configuration step, " - "'faulthandler_timeout' option ignored" - ), - stacklevel=2, - ) - - -class FaultHandlerHooks: - """Implements hooks that will actually install fault handler before tests execute, - as well as correctly handle pdb and internal errors.""" - - def pytest_configure(self, config: Config) -> None: - import faulthandler + stderr_fd_copy = os.dup(get_stderr_fileno()) + config._store[fault_handler_stderr_key] = open(stderr_fd_copy, "w") + faulthandler.enable(file=config._store[fault_handler_stderr_key]) - stderr_fd_copy = os.dup(self._get_stderr_fileno()) - config._store[fault_handler_stderr_key] = open(stderr_fd_copy, "w") - faulthandler.enable(file=config._store[fault_handler_stderr_key]) - def pytest_unconfigure(self, config: Config) -> None: - import faulthandler +def pytest_unconfigure(config: Config) -> None: + import faulthandler - faulthandler.disable() - # close our dup file installed during pytest_configure - # re-enable the faulthandler, attaching it to the default sys.stderr - # so we can see crashes after pytest has finished, usually during - # garbage collection during interpreter shutdown + faulthandler.disable() + # Close the dup file installed during pytest_configure. + if fault_handler_stderr_key in config._store: config._store[fault_handler_stderr_key].close() del config._store[fault_handler_stderr_key] - faulthandler.enable(file=self._get_stderr_fileno()) + # Re-enable the faulthandler, attaching it to the default sys.stderr + # so we can see crashes after pytest has finished, usually during + # garbage collection during interpreter shutdown. + faulthandler.enable(file=get_stderr_fileno()) + + +def get_stderr_fileno() -> int: + try: + fileno = sys.stderr.fileno() + # The Twisted Logger will return an invalid file descriptor since it is not backed + # by an FD. So, let's also forward this to the same code path as with pytest-xdist. + if fileno == -1: + raise AttributeError() + return fileno + except (AttributeError, io.UnsupportedOperation): + # pytest-xdist monkeypatches sys.stderr with an object that is not an actual file. + # https://docs.python.org/3/library/faulthandler.html#issue-with-file-descriptors + # This is potentially dangerous, but the best we can do. + return sys.__stderr__.fileno() + + +def get_timeout_config_value(config: Config) -> float: + return float(config.getini("faulthandler_timeout") or 0.0) + + +@pytest.hookimpl(hookwrapper=True, trylast=True) +def pytest_runtest_protocol(item: Item) -> Generator[None, None, None]: + timeout = get_timeout_config_value(item.config) + stderr = item.config._store[fault_handler_stderr_key] + if timeout > 0 and stderr is not None: + import faulthandler - @staticmethod - def _get_stderr_fileno(): + faulthandler.dump_traceback_later(timeout, file=stderr) try: - fileno = sys.stderr.fileno() - # The Twisted Logger will return an invalid file descriptor since it is not backed - # by an FD. So, let's also forward this to the same code path as with pytest-xdist. - if fileno == -1: - raise AttributeError() - return fileno - except (AttributeError, io.UnsupportedOperation): - # pytest-xdist monkeypatches sys.stderr with an object that is not an actual file. - # https://docs.python.org/3/library/faulthandler.html#issue-with-file-descriptors - # This is potentially dangerous, but the best we can do. - return sys.__stderr__.fileno() - - @staticmethod - def get_timeout_config_value(config): - return float(config.getini("faulthandler_timeout") or 0.0) - - @pytest.hookimpl(hookwrapper=True, trylast=True) - def pytest_runtest_protocol(self, item: Item) -> Generator[None, None, None]: - timeout = self.get_timeout_config_value(item.config) - stderr = item.config._store[fault_handler_stderr_key] - if timeout > 0 and stderr is not None: - import faulthandler - - faulthandler.dump_traceback_later(timeout, file=stderr) - try: - yield - finally: - faulthandler.cancel_dump_traceback_later() - else: yield + finally: + faulthandler.cancel_dump_traceback_later() + else: + yield - @pytest.hookimpl(tryfirst=True) - def pytest_enter_pdb(self) -> None: - """Cancel any traceback dumping due to timeout before entering pdb.""" - import faulthandler - faulthandler.cancel_dump_traceback_later() +@pytest.hookimpl(tryfirst=True) +def pytest_enter_pdb() -> None: + """Cancel any traceback dumping due to timeout before entering pdb.""" + import faulthandler - @pytest.hookimpl(tryfirst=True) - def pytest_exception_interact(self) -> None: - """Cancel any traceback dumping due to an interactive exception being - raised.""" - import faulthandler + faulthandler.cancel_dump_traceback_later() + + +@pytest.hookimpl(tryfirst=True) +def pytest_exception_interact() -> None: + """Cancel any traceback dumping due to an interactive exception being + raised.""" + import faulthandler - faulthandler.cancel_dump_traceback_later() + faulthandler.cancel_dump_traceback_later() diff --git a/testing/test_faulthandler.py b/testing/test_faulthandler.py index 370084c125f..411e841a31a 100644 --- a/testing/test_faulthandler.py +++ b/testing/test_faulthandler.py @@ -94,7 +94,7 @@ def test_cancel_timeout_on_hook(monkeypatch, hook_name) -> None: to timeout before entering pdb (pytest-dev/pytest-faulthandler#12) or any other interactive exception (pytest-dev/pytest-faulthandler#14).""" import faulthandler - from _pytest.faulthandler import FaultHandlerHooks + from _pytest import faulthandler as faulthandler_plugin called = [] @@ -104,19 +104,18 @@ def test_cancel_timeout_on_hook(monkeypatch, hook_name) -> None: # call our hook explicitly, we can trust that pytest will call the hook # for us at the appropriate moment - hook_func = getattr(FaultHandlerHooks, hook_name) - hook_func(self=None) + hook_func = getattr(faulthandler_plugin, hook_name) + hook_func() assert called == [1] -@pytest.mark.parametrize("faulthandler_timeout", [0, 2]) -def test_already_initialized(faulthandler_timeout: int, pytester: Pytester) -> None: - """Test for faulthandler being initialized earlier than pytest (#6575).""" +def test_already_initialized_crash(pytester: Pytester) -> None: + """Even if faulthandler is already initialized, we still dump tracebacks on crashes (#8258).""" pytester.makepyfile( """ def test(): import faulthandler - assert faulthandler.is_enabled() + faulthandler._sigabrt() """ ) result = pytester.run( @@ -125,22 +124,14 @@ def test(): "faulthandler", "-mpytest", pytester.path, - "-o", - f"faulthandler_timeout={faulthandler_timeout}", ) - # ensure warning is emitted if faulthandler_timeout is configured - warning_line = "*faulthandler.py*faulthandler module enabled before*" - if faulthandler_timeout > 0: - result.stdout.fnmatch_lines(warning_line) - else: - result.stdout.no_fnmatch_line(warning_line) - result.stdout.fnmatch_lines("*1 passed*") - assert result.ret == 0 + result.stderr.fnmatch_lines(["*Fatal Python error*"]) + assert result.ret != 0 def test_get_stderr_fileno_invalid_fd() -> None: """Test for faulthandler being able to handle invalid file descriptors for stderr (#8249).""" - from _pytest.faulthandler import FaultHandlerHooks + from _pytest.faulthandler import get_stderr_fileno class StdErrWrapper(io.StringIO): """ @@ -159,4 +150,4 @@ def fileno(self): # Even when the stderr wrapper signals an invalid file descriptor, # ``_get_stderr_fileno()`` should return the real one. - assert FaultHandlerHooks._get_stderr_fileno() == 2 + assert get_stderr_fileno() == 2 From d4f8e4b40ce481df0f34b2eac5d61aefae4ed2e1 Mon Sep 17 00:00:00 2001 From: Hong Xu Date: Thu, 21 Jan 2021 04:58:52 -0800 Subject: [PATCH 076/630] Explain how to create binary files in the doc of `pytest.makefile`. (#8255) --- src/_pytest/pytester.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index 95b22b3b23e..8ca21d1c538 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -777,7 +777,7 @@ def to_text(s: Union[Any, bytes]) -> str: return ret def makefile(self, ext: str, *args: str, **kwargs: str) -> Path: - r"""Create new file(s) in the test directory. + r"""Create new text file(s) in the test directory. :param str ext: The extension the file(s) should use, including the dot, e.g. `.py`. @@ -797,6 +797,12 @@ def makefile(self, ext: str, *args: str, **kwargs: str) -> Path: pytester.makefile(".ini", pytest="[pytest]\naddopts=-rs\n") + To create binary files, use :meth:`pathlib.Path.write_bytes` directly: + + .. code-block:: python + + filename = pytester.path.joinpath("foo.bin") + filename.write_bytes(b"...") """ return self._makefile(ext, args, kwargs) From da70f61f67cc37209a5d97bca18303bdd7bccbc8 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Wed, 30 Dec 2020 16:02:16 +0200 Subject: [PATCH 077/630] runner: complete type annotations of SetupState --- src/_pytest/runner.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/_pytest/runner.py b/src/_pytest/runner.py index 844e41f8057..a49879432a2 100644 --- a/src/_pytest/runner.py +++ b/src/_pytest/runner.py @@ -403,22 +403,22 @@ def pytest_make_collect_report(collector: Collector) -> CollectReport: class SetupState: """Shared state for setting up/tearing down test items or collectors.""" - def __init__(self): + def __init__(self) -> None: self.stack: List[Node] = [] self._finalizers: Dict[Node, List[Callable[[], object]]] = {} - def addfinalizer(self, finalizer: Callable[[], object], colitem) -> None: + def addfinalizer(self, finalizer: Callable[[], object], colitem: Node) -> None: """Attach a finalizer to the given colitem.""" assert colitem and not isinstance(colitem, tuple) assert callable(finalizer) # assert colitem in self.stack # some unit tests don't setup stack :/ self._finalizers.setdefault(colitem, []).append(finalizer) - def _pop_and_teardown(self): + def _pop_and_teardown(self) -> None: colitem = self.stack.pop() self._teardown_with_finalization(colitem) - def _callfinalizers(self, colitem) -> None: + def _callfinalizers(self, colitem: Node) -> None: finalizers = self._finalizers.pop(colitem, None) exc = None while finalizers: @@ -433,7 +433,7 @@ def _callfinalizers(self, colitem) -> None: if exc: raise exc - def _teardown_with_finalization(self, colitem) -> None: + def _teardown_with_finalization(self, colitem: Node) -> None: self._callfinalizers(colitem) colitem.teardown() for colitem in self._finalizers: @@ -446,11 +446,11 @@ def teardown_all(self) -> None: self._teardown_with_finalization(key) assert not self._finalizers - def teardown_exact(self, item, nextitem) -> None: + def teardown_exact(self, item: Item, nextitem: Optional[Item]) -> None: needed_collectors = nextitem and nextitem.listchain() or [] self._teardown_towards(needed_collectors) - def _teardown_towards(self, needed_collectors) -> None: + def _teardown_towards(self, needed_collectors: List[Node]) -> None: exc = None while self.stack: if self.stack == needed_collectors[: len(self.stack)]: @@ -465,7 +465,7 @@ def _teardown_towards(self, needed_collectors) -> None: if exc: raise exc - def prepare(self, colitem) -> None: + def prepare(self, colitem: Item) -> None: """Setup objects along the collector chain to the test-method.""" # Check if the last collection node has raised an error. From f7b0b1dd1f6f2722da2cc1a908fdd2843cbdb111 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Wed, 30 Dec 2020 17:20:19 +0200 Subject: [PATCH 078/630] runner: use node's Store to keep private SetupState state instead of an attribute This way it gets proper typing and decoupling. --- src/_pytest/runner.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/_pytest/runner.py b/src/_pytest/runner.py index a49879432a2..7087c6c590a 100644 --- a/src/_pytest/runner.py +++ b/src/_pytest/runner.py @@ -33,8 +33,10 @@ from _pytest.nodes import Item from _pytest.nodes import Node from _pytest.outcomes import Exit +from _pytest.outcomes import OutcomeException from _pytest.outcomes import Skipped from _pytest.outcomes import TEST_OUTCOME +from _pytest.store import StoreKey if TYPE_CHECKING: from typing_extensions import Literal @@ -465,14 +467,16 @@ def _teardown_towards(self, needed_collectors: List[Node]) -> None: if exc: raise exc + _prepare_exc_key = StoreKey[Union[OutcomeException, Exception]]() + def prepare(self, colitem: Item) -> None: """Setup objects along the collector chain to the test-method.""" # Check if the last collection node has raised an error. for col in self.stack: - if hasattr(col, "_prepare_exc"): - exc = col._prepare_exc # type: ignore[attr-defined] - raise exc + prepare_exc = col._store.get(self._prepare_exc_key, None) + if prepare_exc: + raise prepare_exc needed_collectors = colitem.listchain() for col in needed_collectors[len(self.stack) :]: @@ -480,7 +484,7 @@ def prepare(self, colitem: Item) -> None: try: col.setup() except TEST_OUTCOME as e: - col._prepare_exc = e # type: ignore[attr-defined] + col._store[self._prepare_exc_key] = e raise e From 410622f719fd0175cf875f528f122788715b1f05 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Wed, 30 Dec 2020 17:27:43 +0200 Subject: [PATCH 079/630] runner: reorder SetupState method to make more sense The setup stuff happens before the teardown stuff, so put it first so that reading the code from top to bottom makes more sense. --- src/_pytest/runner.py | 52 +++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/_pytest/runner.py b/src/_pytest/runner.py index 7087c6c590a..d46dba56f5a 100644 --- a/src/_pytest/runner.py +++ b/src/_pytest/runner.py @@ -409,6 +409,26 @@ def __init__(self) -> None: self.stack: List[Node] = [] self._finalizers: Dict[Node, List[Callable[[], object]]] = {} + _prepare_exc_key = StoreKey[Union[OutcomeException, Exception]]() + + def prepare(self, colitem: Item) -> None: + """Setup objects along the collector chain to the test-method.""" + + # Check if the last collection node has raised an error. + for col in self.stack: + prepare_exc = col._store.get(self._prepare_exc_key, None) + if prepare_exc: + raise prepare_exc + + needed_collectors = colitem.listchain() + for col in needed_collectors[len(self.stack) :]: + self.stack.append(col) + try: + col.setup() + except TEST_OUTCOME as e: + col._store[self._prepare_exc_key] = e + raise e + def addfinalizer(self, finalizer: Callable[[], object], colitem: Node) -> None: """Attach a finalizer to the given colitem.""" assert colitem and not isinstance(colitem, tuple) @@ -441,13 +461,6 @@ def _teardown_with_finalization(self, colitem: Node) -> None: for colitem in self._finalizers: assert colitem in self.stack - def teardown_all(self) -> None: - while self.stack: - self._pop_and_teardown() - for key in list(self._finalizers): - self._teardown_with_finalization(key) - assert not self._finalizers - def teardown_exact(self, item: Item, nextitem: Optional[Item]) -> None: needed_collectors = nextitem and nextitem.listchain() or [] self._teardown_towards(needed_collectors) @@ -467,25 +480,12 @@ def _teardown_towards(self, needed_collectors: List[Node]) -> None: if exc: raise exc - _prepare_exc_key = StoreKey[Union[OutcomeException, Exception]]() - - def prepare(self, colitem: Item) -> None: - """Setup objects along the collector chain to the test-method.""" - - # Check if the last collection node has raised an error. - for col in self.stack: - prepare_exc = col._store.get(self._prepare_exc_key, None) - if prepare_exc: - raise prepare_exc - - needed_collectors = colitem.listchain() - for col in needed_collectors[len(self.stack) :]: - self.stack.append(col) - try: - col.setup() - except TEST_OUTCOME as e: - col._store[self._prepare_exc_key] = e - raise e + def teardown_all(self) -> None: + while self.stack: + self._pop_and_teardown() + for key in list(self._finalizers): + self._teardown_with_finalization(key) + assert not self._finalizers def collect_one_node(collector: Collector) -> CollectReport: From 42ae8180ddf36fe4810bdfcce72aa72cf8cd3b43 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Wed, 30 Dec 2020 17:33:09 +0200 Subject: [PATCH 080/630] runner: inline SetupState._teardown_towards() Doesn't add much. --- src/_pytest/runner.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/_pytest/runner.py b/src/_pytest/runner.py index d46dba56f5a..e132a2d8f4a 100644 --- a/src/_pytest/runner.py +++ b/src/_pytest/runner.py @@ -463,9 +463,6 @@ def _teardown_with_finalization(self, colitem: Node) -> None: def teardown_exact(self, item: Item, nextitem: Optional[Item]) -> None: needed_collectors = nextitem and nextitem.listchain() or [] - self._teardown_towards(needed_collectors) - - def _teardown_towards(self, needed_collectors: List[Node]) -> None: exc = None while self.stack: if self.stack == needed_collectors[: len(self.stack)]: From 5f4e55fb6d01a1dd199ed0f484e460be7d0e222a Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Wed, 30 Dec 2020 17:34:00 +0200 Subject: [PATCH 081/630] runner: remove dead code in teardown_all() When the stack is empty, the finalizers which are supposed to be attached to nodes in the stack really ought to be empty as well. So the code here is dead. If this doesn't happen, the assert will trigger. --- src/_pytest/runner.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/_pytest/runner.py b/src/_pytest/runner.py index e132a2d8f4a..017573cec9f 100644 --- a/src/_pytest/runner.py +++ b/src/_pytest/runner.py @@ -480,8 +480,6 @@ def teardown_exact(self, item: Item, nextitem: Optional[Item]) -> None: def teardown_all(self) -> None: while self.stack: self._pop_and_teardown() - for key in list(self._finalizers): - self._teardown_with_finalization(key) assert not self._finalizers From ceb4d6f6d595532be599e6382b571f3b4f614df9 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Wed, 30 Dec 2020 17:36:42 +0200 Subject: [PATCH 082/630] runner: inline a couple of SetupState methods Code is clearer this way. --- src/_pytest/runner.py | 6 ------ testing/test_runner.py | 6 ++++-- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/_pytest/runner.py b/src/_pytest/runner.py index 017573cec9f..5d35f520ac6 100644 --- a/src/_pytest/runner.py +++ b/src/_pytest/runner.py @@ -438,9 +438,6 @@ def addfinalizer(self, finalizer: Callable[[], object], colitem: Node) -> None: def _pop_and_teardown(self) -> None: colitem = self.stack.pop() - self._teardown_with_finalization(colitem) - - def _callfinalizers(self, colitem: Node) -> None: finalizers = self._finalizers.pop(colitem, None) exc = None while finalizers: @@ -454,9 +451,6 @@ def _callfinalizers(self, colitem: Node) -> None: exc = e if exc: raise exc - - def _teardown_with_finalization(self, colitem: Node) -> None: - self._callfinalizers(colitem) colitem.teardown() for colitem in self._finalizers: assert colitem in self.stack diff --git a/testing/test_runner.py b/testing/test_runner.py index 8ce0f67354f..20c81a62f22 100644 --- a/testing/test_runner.py +++ b/testing/test_runner.py @@ -64,11 +64,12 @@ def fin3(): item = pytester.getitem("def test_func(): pass") ss = runner.SetupState() + ss.prepare(item) ss.addfinalizer(fin1, item) ss.addfinalizer(fin2, item) ss.addfinalizer(fin3, item) with pytest.raises(Exception) as err: - ss._callfinalizers(item) + ss.teardown_exact(item, None) assert err.value.args == ("oops",) assert r == ["fin3", "fin1"] @@ -83,10 +84,11 @@ def fin2(): item = pytester.getitem("def test_func(): pass") ss = runner.SetupState() + ss.prepare(item) ss.addfinalizer(fin1, item) ss.addfinalizer(fin2, item) with pytest.raises(Exception) as err: - ss._callfinalizers(item) + ss.teardown_exact(item, None) assert err.value.args == ("oops2",) def test_teardown_multiple_scopes_one_fails(self, pytester: Pytester) -> None: From 2b14edb108f32c49c15b5e0c0ef4d27880b09e0f Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Fri, 1 Jan 2021 12:08:12 +0200 Subject: [PATCH 083/630] runner: express SetupState.teardown_all() in terms of teardown_exact() and remove it Makes it easier to understand with fewer methods. --- src/_pytest/runner.py | 13 +++++-------- testing/python/fixtures.py | 2 +- testing/test_runner.py | 13 +++++++------ 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/_pytest/runner.py b/src/_pytest/runner.py index 5d35f520ac6..525aafc76db 100644 --- a/src/_pytest/runner.py +++ b/src/_pytest/runner.py @@ -105,7 +105,7 @@ def pytest_sessionstart(session: "Session") -> None: def pytest_sessionfinish(session: "Session") -> None: - session._setupstate.teardown_all() + session._setupstate.teardown_exact(None) def pytest_runtest_protocol(item: Item, nextitem: Optional[Item]) -> bool: @@ -177,7 +177,7 @@ def pytest_runtest_call(item: Item) -> None: def pytest_runtest_teardown(item: Item, nextitem: Optional[Item]) -> None: _update_current_test_var(item, "teardown") - item.session._setupstate.teardown_exact(item, nextitem) + item.session._setupstate.teardown_exact(nextitem) _update_current_test_var(item, None) @@ -455,7 +455,7 @@ def _pop_and_teardown(self) -> None: for colitem in self._finalizers: assert colitem in self.stack - def teardown_exact(self, item: Item, nextitem: Optional[Item]) -> None: + def teardown_exact(self, nextitem: Optional[Item]) -> None: needed_collectors = nextitem and nextitem.listchain() or [] exc = None while self.stack: @@ -470,11 +470,8 @@ def teardown_exact(self, item: Item, nextitem: Optional[Item]) -> None: exc = e if exc: raise exc - - def teardown_all(self) -> None: - while self.stack: - self._pop_and_teardown() - assert not self._finalizers + if nextitem is None: + assert not self._finalizers def collect_one_node(collector: Collector) -> CollectReport: diff --git a/testing/python/fixtures.py b/testing/python/fixtures.py index 12340e690eb..d1297339645 100644 --- a/testing/python/fixtures.py +++ b/testing/python/fixtures.py @@ -856,7 +856,7 @@ def test_func(something): pass teardownlist = parent.obj.teardownlist ss = item.session._setupstate assert not teardownlist - ss.teardown_exact(item, None) + ss.teardown_exact(None) print(ss.stack) assert teardownlist == [1] diff --git a/testing/test_runner.py b/testing/test_runner.py index 20c81a62f22..aca1bd7ceb0 100644 --- a/testing/test_runner.py +++ b/testing/test_runner.py @@ -34,9 +34,10 @@ def test_setup(self, pytester: Pytester) -> None: def test_teardown_exact_stack_empty(self, pytester: Pytester) -> None: item = pytester.getitem("def test_func(): pass") ss = runner.SetupState() - ss.teardown_exact(item, None) - ss.teardown_exact(item, None) - ss.teardown_exact(item, None) + ss.prepare(item) + ss.teardown_exact(None) + ss.teardown_exact(None) + ss.teardown_exact(None) def test_setup_fails_and_failure_is_cached(self, pytester: Pytester) -> None: item = pytester.getitem( @@ -69,7 +70,7 @@ def fin3(): ss.addfinalizer(fin2, item) ss.addfinalizer(fin3, item) with pytest.raises(Exception) as err: - ss.teardown_exact(item, None) + ss.teardown_exact(None) assert err.value.args == ("oops",) assert r == ["fin3", "fin1"] @@ -88,7 +89,7 @@ def fin2(): ss.addfinalizer(fin1, item) ss.addfinalizer(fin2, item) with pytest.raises(Exception) as err: - ss.teardown_exact(item, None) + ss.teardown_exact(None) assert err.value.args == ("oops2",) def test_teardown_multiple_scopes_one_fails(self, pytester: Pytester) -> None: @@ -106,7 +107,7 @@ def fin_module(): ss.addfinalizer(fin_func, item) ss.prepare(item) with pytest.raises(Exception, match="oops1"): - ss.teardown_exact(item, None) + ss.teardown_exact(None) assert module_teardown From d1fcd425a3da18ecec35a6028093ebff830edd46 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Fri, 1 Jan 2021 12:51:33 +0200 Subject: [PATCH 084/630] runner: inline SetupState._pop_and_teardown() This will enable a simplification in the next commit. --- src/_pytest/runner.py | 37 +++++++++++++++++-------------------- testing/test_runner.py | 2 +- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/src/_pytest/runner.py b/src/_pytest/runner.py index 525aafc76db..8102a6019f2 100644 --- a/src/_pytest/runner.py +++ b/src/_pytest/runner.py @@ -436,25 +436,6 @@ def addfinalizer(self, finalizer: Callable[[], object], colitem: Node) -> None: # assert colitem in self.stack # some unit tests don't setup stack :/ self._finalizers.setdefault(colitem, []).append(finalizer) - def _pop_and_teardown(self) -> None: - colitem = self.stack.pop() - finalizers = self._finalizers.pop(colitem, None) - exc = None - while finalizers: - fin = finalizers.pop() - try: - fin() - except TEST_OUTCOME as e: - # XXX Only first exception will be seen by user, - # ideally all should be reported. - if exc is None: - exc = e - if exc: - raise exc - colitem.teardown() - for colitem in self._finalizers: - assert colitem in self.stack - def teardown_exact(self, nextitem: Optional[Item]) -> None: needed_collectors = nextitem and nextitem.listchain() or [] exc = None @@ -462,7 +443,23 @@ def teardown_exact(self, nextitem: Optional[Item]) -> None: if self.stack == needed_collectors[: len(self.stack)]: break try: - self._pop_and_teardown() + colitem = self.stack.pop() + finalizers = self._finalizers.pop(colitem, None) + inner_exc = None + while finalizers: + fin = finalizers.pop() + try: + fin() + except TEST_OUTCOME as e: + # XXX Only first exception will be seen by user, + # ideally all should be reported. + if inner_exc is None: + inner_exc = e + if inner_exc: + raise inner_exc + colitem.teardown() + for colitem in self._finalizers: + assert colitem in self.stack except TEST_OUTCOME as e: # XXX Only first exception will be seen by user, # ideally all should be reported. diff --git a/testing/test_runner.py b/testing/test_runner.py index aca1bd7ceb0..f53ad2d0820 100644 --- a/testing/test_runner.py +++ b/testing/test_runner.py @@ -28,7 +28,7 @@ def test_setup(self, pytester: Pytester) -> None: ss.prepare(item) ss.addfinalizer(values.pop, colitem=item) assert values - ss._pop_and_teardown() + ss.teardown_exact(None) assert not values def test_teardown_exact_stack_empty(self, pytester: Pytester) -> None: From 14d71b2c228c3ee92a1e7aa93a6ea64444f19697 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Fri, 1 Jan 2021 13:55:49 +0200 Subject: [PATCH 085/630] runner: make sure SetupState._finalizers is always set for a node in the stack This makes the stack <-> _finalizers correspondence clearer. --- src/_pytest/runner.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/_pytest/runner.py b/src/_pytest/runner.py index 8102a6019f2..b25438c2326 100644 --- a/src/_pytest/runner.py +++ b/src/_pytest/runner.py @@ -423,6 +423,7 @@ def prepare(self, colitem: Item) -> None: needed_collectors = colitem.listchain() for col in needed_collectors[len(self.stack) :]: self.stack.append(col) + self._finalizers.setdefault(col, []) try: col.setup() except TEST_OUTCOME as e: @@ -444,7 +445,7 @@ def teardown_exact(self, nextitem: Optional[Item]) -> None: break try: colitem = self.stack.pop() - finalizers = self._finalizers.pop(colitem, None) + finalizers = self._finalizers.pop(colitem) inner_exc = None while finalizers: fin = finalizers.pop() From bb3d43c9a6d16174a05058686b9460ceff911e5a Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Fri, 1 Jan 2021 13:10:43 +0200 Subject: [PATCH 086/630] runner: ensure item.teardown() is called even if a finalizer raised If one finalizer fails, all of the subsequent finalizers still run, so the `teardown()` method should behave the same. --- src/_pytest/runner.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/_pytest/runner.py b/src/_pytest/runner.py index b25438c2326..c221b42a045 100644 --- a/src/_pytest/runner.py +++ b/src/_pytest/runner.py @@ -446,6 +446,7 @@ def teardown_exact(self, nextitem: Optional[Item]) -> None: try: colitem = self.stack.pop() finalizers = self._finalizers.pop(colitem) + finalizers.insert(0, colitem.teardown) inner_exc = None while finalizers: fin = finalizers.pop() @@ -456,11 +457,10 @@ def teardown_exact(self, nextitem: Optional[Item]) -> None: # ideally all should be reported. if inner_exc is None: inner_exc = e - if inner_exc: - raise inner_exc - colitem.teardown() for colitem in self._finalizers: assert colitem in self.stack + if inner_exc: + raise inner_exc except TEST_OUTCOME as e: # XXX Only first exception will be seen by user, # ideally all should be reported. From 0d4121d24bf4c1efdee67a45b831ccdbdda78f2c Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Fri, 1 Jan 2021 13:06:50 +0200 Subject: [PATCH 087/630] runner: collapse exception handling in SetupState.teardown_exact() This is equivalent but simpler. --- src/_pytest/runner.py | 37 ++++++++++++++----------------------- 1 file changed, 14 insertions(+), 23 deletions(-) diff --git a/src/_pytest/runner.py b/src/_pytest/runner.py index c221b42a045..3aa0a6c4bf9 100644 --- a/src/_pytest/runner.py +++ b/src/_pytest/runner.py @@ -443,29 +443,20 @@ def teardown_exact(self, nextitem: Optional[Item]) -> None: while self.stack: if self.stack == needed_collectors[: len(self.stack)]: break - try: - colitem = self.stack.pop() - finalizers = self._finalizers.pop(colitem) - finalizers.insert(0, colitem.teardown) - inner_exc = None - while finalizers: - fin = finalizers.pop() - try: - fin() - except TEST_OUTCOME as e: - # XXX Only first exception will be seen by user, - # ideally all should be reported. - if inner_exc is None: - inner_exc = e - for colitem in self._finalizers: - assert colitem in self.stack - if inner_exc: - raise inner_exc - except TEST_OUTCOME as e: - # XXX Only first exception will be seen by user, - # ideally all should be reported. - if exc is None: - exc = e + colitem = self.stack.pop() + finalizers = self._finalizers.pop(colitem) + finalizers.insert(0, colitem.teardown) + while finalizers: + fin = finalizers.pop() + try: + fin() + except TEST_OUTCOME as e: + # XXX Only first exception will be seen by user, + # ideally all should be reported. + if exc is None: + exc = e + for colitem in self._finalizers: + assert colitem in self.stack if exc: raise exc if nextitem is None: From c83d030028806b119cb61824ed984524c9faad6d Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Fri, 1 Jan 2021 15:53:38 +0200 Subject: [PATCH 088/630] testing/test_runner: make SetupState tests use a proper SetupState Previously the tests (probably unintentionally) mixed a fresh SetupState and the generated item Session's SetupState, which led to some serious head scratching when prodding it a bit. --- testing/test_runner.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/testing/test_runner.py b/testing/test_runner.py index f53ad2d0820..f1038ce96bb 100644 --- a/testing/test_runner.py +++ b/testing/test_runner.py @@ -22,8 +22,8 @@ class TestSetupState: def test_setup(self, pytester: Pytester) -> None: - ss = runner.SetupState() item = pytester.getitem("def test_func(): pass") + ss = item.session._setupstate values = [1] ss.prepare(item) ss.addfinalizer(values.pop, colitem=item) @@ -33,7 +33,7 @@ def test_setup(self, pytester: Pytester) -> None: def test_teardown_exact_stack_empty(self, pytester: Pytester) -> None: item = pytester.getitem("def test_func(): pass") - ss = runner.SetupState() + ss = item.session._setupstate ss.prepare(item) ss.teardown_exact(None) ss.teardown_exact(None) @@ -47,9 +47,11 @@ def setup_module(mod): def test_func(): pass """ ) - ss = runner.SetupState() - pytest.raises(ValueError, lambda: ss.prepare(item)) - pytest.raises(ValueError, lambda: ss.prepare(item)) + ss = item.session._setupstate + with pytest.raises(ValueError): + ss.prepare(item) + with pytest.raises(ValueError): + ss.prepare(item) def test_teardown_multiple_one_fails(self, pytester: Pytester) -> None: r = [] @@ -64,7 +66,7 @@ def fin3(): r.append("fin3") item = pytester.getitem("def test_func(): pass") - ss = runner.SetupState() + ss = item.session._setupstate ss.prepare(item) ss.addfinalizer(fin1, item) ss.addfinalizer(fin2, item) @@ -84,7 +86,7 @@ def fin2(): raise Exception("oops2") item = pytester.getitem("def test_func(): pass") - ss = runner.SetupState() + ss = item.session._setupstate ss.prepare(item) ss.addfinalizer(fin1, item) ss.addfinalizer(fin2, item) @@ -102,7 +104,7 @@ def fin_module(): module_teardown.append("fin_module") item = pytester.getitem("def test_func(): pass") - ss = runner.SetupState() + ss = item.session._setupstate ss.addfinalizer(fin_module, item.listchain()[-2]) ss.addfinalizer(fin_func, item) ss.prepare(item) From 637300d13d69848226f0f6bbc24102dafdfd6357 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Fri, 1 Jan 2021 16:05:54 +0200 Subject: [PATCH 089/630] testing: fix some tests to be more realistic Perform the operations in the order and context in which they can legally occur. --- testing/python/fixtures.py | 15 +++++++++++---- testing/test_runner.py | 7 ++++--- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/testing/python/fixtures.py b/testing/python/fixtures.py index d1297339645..3d78ebf5826 100644 --- a/testing/python/fixtures.py +++ b/testing/python/fixtures.py @@ -130,7 +130,8 @@ def test_funcarg_basic(self, pytester: Pytester) -> None: pytester.copy_example() item = pytester.getitem(Path("test_funcarg_basic.py")) assert isinstance(item, Function) - item._request._fillfixtures() + # Execute's item's setup, which fills fixtures. + item.session._setupstate.prepare(item) del item.funcargs["request"] assert len(get_public_names(item.funcargs)) == 2 assert item.funcargs["some"] == "test_func" @@ -809,18 +810,25 @@ def test_getfixturevalue(self, pytester: Pytester) -> None: item = pytester.getitem( """ import pytest - values = [2] + @pytest.fixture - def something(request): return 1 + def something(request): + return 1 + + values = [2] @pytest.fixture def other(request): return values.pop() + def test_func(something): pass """ ) assert isinstance(item, Function) req = item._request + # Execute item's setup. + item.session._setupstate.prepare(item) + with pytest.raises(pytest.FixtureLookupError): req.getfixturevalue("notexists") val = req.getfixturevalue("something") @@ -831,7 +839,6 @@ def test_func(something): pass assert val2 == 2 val2 = req.getfixturevalue("other") # see about caching assert val2 == 2 - item._request._fillfixtures() assert item.funcargs["something"] == 1 assert len(get_public_names(item.funcargs)) == 2 assert "request" in item.funcargs diff --git a/testing/test_runner.py b/testing/test_runner.py index f1038ce96bb..0e90ea9cca2 100644 --- a/testing/test_runner.py +++ b/testing/test_runner.py @@ -104,13 +104,14 @@ def fin_module(): module_teardown.append("fin_module") item = pytester.getitem("def test_func(): pass") + mod = item.listchain()[-2] ss = item.session._setupstate - ss.addfinalizer(fin_module, item.listchain()[-2]) - ss.addfinalizer(fin_func, item) ss.prepare(item) + ss.addfinalizer(fin_module, mod) + ss.addfinalizer(fin_func, item) with pytest.raises(Exception, match="oops1"): ss.teardown_exact(None) - assert module_teardown + assert module_teardown == ["fin_module"] class BaseFunctionalTests: From 6db082a4486923637b4f427c95ab379d81b78528 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Fri, 1 Jan 2021 17:35:22 +0200 Subject: [PATCH 090/630] fixtures: make sure to properly setup stack for _fill_fixtures_impl This code is weird, dead, deprecated and will be removed in pytest 7, but for now some tests execute it, so fix it up in preparation for some changes. --- src/_pytest/fixtures.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 43a40a86449..481bda8f497 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -372,6 +372,7 @@ def _fill_fixtures_impl(function: "Function") -> None: fi = fm.getfixtureinfo(function.parent, function.obj, None) function._fixtureinfo = fi request = function._request = FixtureRequest(function, _ispytest=True) + fm.session._setupstate.prepare(function) request._fillfixtures() # Prune out funcargs for jstests. newfuncargs = {} From 960ebae943927805091153bfe17677c5f3734198 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Fri, 1 Jan 2021 14:13:39 +0200 Subject: [PATCH 091/630] runner: enable a commented assertion in SetupState.addfinalizer The assertion ensures that when `addfinalizer(finalizer, node)` is called, the node is in the stack. This then would ensure that the finalization is actually properly executed properly during the node's teardown. Anything else indicates something is wrong. Previous commits fixed all of the tests which previously failed this, so can be reenabeld now. --- src/_pytest/runner.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/_pytest/runner.py b/src/_pytest/runner.py index 3aa0a6c4bf9..9759441bd1e 100644 --- a/src/_pytest/runner.py +++ b/src/_pytest/runner.py @@ -434,8 +434,8 @@ def addfinalizer(self, finalizer: Callable[[], object], colitem: Node) -> None: """Attach a finalizer to the given colitem.""" assert colitem and not isinstance(colitem, tuple) assert callable(finalizer) - # assert colitem in self.stack # some unit tests don't setup stack :/ - self._finalizers.setdefault(colitem, []).append(finalizer) + assert colitem in self.stack, (colitem, self.stack) + self._finalizers[colitem].append(finalizer) def teardown_exact(self, nextitem: Optional[Item]) -> None: needed_collectors = nextitem and nextitem.listchain() or [] From 03c3a90c686c4cb0a146e4139125b30cba27075a Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Fri, 1 Jan 2021 21:48:03 +0200 Subject: [PATCH 092/630] runner: replace setdefault with an unconditional set The already-exists case is not supposed to happen. --- src/_pytest/runner.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/_pytest/runner.py b/src/_pytest/runner.py index 9759441bd1e..fe3590d448c 100644 --- a/src/_pytest/runner.py +++ b/src/_pytest/runner.py @@ -422,8 +422,10 @@ def prepare(self, colitem: Item) -> None: needed_collectors = colitem.listchain() for col in needed_collectors[len(self.stack) :]: + assert col not in self.stack + assert col not in self._finalizers self.stack.append(col) - self._finalizers.setdefault(col, []) + self._finalizers[col] = [] try: col.setup() except TEST_OUTCOME as e: From 1db78bec311b9ad161dd201a1796abf82feeb8a8 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Fri, 1 Jan 2021 21:50:38 +0200 Subject: [PATCH 093/630] runner: use insertion-ordered dict instead of stack, dict pair Since dicts are now ordered, we can use the finalizers dict itself as the dict, simplifying the code. --- src/_pytest/runner.py | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/_pytest/runner.py b/src/_pytest/runner.py index fe3590d448c..5dbb26aef0b 100644 --- a/src/_pytest/runner.py +++ b/src/_pytest/runner.py @@ -406,8 +406,7 @@ class SetupState: """Shared state for setting up/tearing down test items or collectors.""" def __init__(self) -> None: - self.stack: List[Node] = [] - self._finalizers: Dict[Node, List[Callable[[], object]]] = {} + self.stack: Dict[Node, List[Callable[[], object]]] = {} _prepare_exc_key = StoreKey[Union[OutcomeException, Exception]]() @@ -423,9 +422,7 @@ def prepare(self, colitem: Item) -> None: needed_collectors = colitem.listchain() for col in needed_collectors[len(self.stack) :]: assert col not in self.stack - assert col not in self._finalizers - self.stack.append(col) - self._finalizers[col] = [] + self.stack[col] = [] try: col.setup() except TEST_OUTCOME as e: @@ -437,16 +434,15 @@ def addfinalizer(self, finalizer: Callable[[], object], colitem: Node) -> None: assert colitem and not isinstance(colitem, tuple) assert callable(finalizer) assert colitem in self.stack, (colitem, self.stack) - self._finalizers[colitem].append(finalizer) + self.stack[colitem].append(finalizer) def teardown_exact(self, nextitem: Optional[Item]) -> None: needed_collectors = nextitem and nextitem.listchain() or [] exc = None while self.stack: - if self.stack == needed_collectors[: len(self.stack)]: + if list(self.stack.keys()) == needed_collectors[: len(self.stack)]: break - colitem = self.stack.pop() - finalizers = self._finalizers.pop(colitem) + colitem, finalizers = self.stack.popitem() finalizers.insert(0, colitem.teardown) while finalizers: fin = finalizers.pop() @@ -457,12 +453,10 @@ def teardown_exact(self, nextitem: Optional[Item]) -> None: # ideally all should be reported. if exc is None: exc = e - for colitem in self._finalizers: - assert colitem in self.stack if exc: raise exc if nextitem is None: - assert not self._finalizers + assert not self.stack def collect_one_node(collector: Collector) -> CollectReport: From 0d19aff562680321a4dab33b1623edb424896d24 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Fri, 1 Jan 2021 22:03:52 +0200 Subject: [PATCH 094/630] runner: schedule node.teardown() call already at setup This is more elegant. --- src/_pytest/runner.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/_pytest/runner.py b/src/_pytest/runner.py index 5dbb26aef0b..63f9227ecda 100644 --- a/src/_pytest/runner.py +++ b/src/_pytest/runner.py @@ -422,7 +422,7 @@ def prepare(self, colitem: Item) -> None: needed_collectors = colitem.listchain() for col in needed_collectors[len(self.stack) :]: assert col not in self.stack - self.stack[col] = [] + self.stack[col] = [col.teardown] try: col.setup() except TEST_OUTCOME as e: @@ -443,7 +443,6 @@ def teardown_exact(self, nextitem: Optional[Item]) -> None: if list(self.stack.keys()) == needed_collectors[: len(self.stack)]: break colitem, finalizers = self.stack.popitem() - finalizers.insert(0, colitem.teardown) while finalizers: fin = finalizers.pop() try: From c30feeef8b12ff2a755ce0fc61a5ed1f59e83c0c Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Fri, 1 Jan 2021 23:14:04 +0200 Subject: [PATCH 095/630] runner: add docstring to SetupState and improve variable naming a bit --- src/_pytest/fixtures.py | 4 +- src/_pytest/runner.py | 96 +++++++++++++++++++++++++++++++++++------ testing/test_runner.py | 2 +- 3 files changed, 87 insertions(+), 15 deletions(-) diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 481bda8f497..269369642e3 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -544,8 +544,8 @@ def addfinalizer(self, finalizer: Callable[[], object]) -> None: self._addfinalizer(finalizer, scope=self.scope) def _addfinalizer(self, finalizer: Callable[[], object], scope) -> None: - item = self._getscopeitem(scope) - item.addfinalizer(finalizer) + node = self._getscopeitem(scope) + node.addfinalizer(finalizer) def applymarker(self, marker: Union[str, MarkDecorator]) -> None: """Apply a marker to a single test function invocation. diff --git a/src/_pytest/runner.py b/src/_pytest/runner.py index 63f9227ecda..7bb92cecf81 100644 --- a/src/_pytest/runner.py +++ b/src/_pytest/runner.py @@ -403,23 +403,86 @@ def pytest_make_collect_report(collector: Collector) -> CollectReport: class SetupState: - """Shared state for setting up/tearing down test items or collectors.""" + """Shared state for setting up/tearing down test items or collectors + in a session. + + Suppose we have a collection tree as follows: + + + + + + + + The SetupState maintains a stack. The stack starts out empty: + + [] + + During the setup phase of item1, prepare(item1) is called. What it does + is: + + push session to stack, run session.setup() + push mod1 to stack, run mod1.setup() + push item1 to stack, run item1.setup() + + The stack is: + + [session, mod1, item1] + + While the stack is in this shape, it is allowed to add finalizers to + each of session, mod1, item1 using addfinalizer(). + + During the teardown phase of item1, teardown_exact(item2) is called, + where item2 is the next item to item1. What it does is: + + pop item1 from stack, run its teardowns + pop mod1 from stack, run its teardowns + + mod1 was popped because it ended its purpose with item1. The stack is: + + [session] + + During the setup phase of item2, prepare(item2) is called. What it does + is: + + push mod2 to stack, run mod2.setup() + push item2 to stack, run item2.setup() + + Stack: + + [session, mod2, item2] + + During the teardown phase of item2, teardown_exact(None) is called, + because item2 is the last item. What it does is: + + pop item2 from stack, run its teardowns + pop mod2 from stack, run its teardowns + pop session from stack, run its teardowns + + Stack: + + [] + + The end! + """ def __init__(self) -> None: + # Maps node -> the node's finalizers. + # The stack is in the dict insertion order. self.stack: Dict[Node, List[Callable[[], object]]] = {} _prepare_exc_key = StoreKey[Union[OutcomeException, Exception]]() - def prepare(self, colitem: Item) -> None: - """Setup objects along the collector chain to the test-method.""" - - # Check if the last collection node has raised an error. + def prepare(self, item: Item) -> None: + """Setup objects along the collector chain to the item.""" + # If a collector fails its setup, fail its entire subtree of items. + # The setup is not retried for each item - the same exception is used. for col in self.stack: prepare_exc = col._store.get(self._prepare_exc_key, None) if prepare_exc: raise prepare_exc - needed_collectors = colitem.listchain() + needed_collectors = item.listchain() for col in needed_collectors[len(self.stack) :]: assert col not in self.stack self.stack[col] = [col.teardown] @@ -429,20 +492,29 @@ def prepare(self, colitem: Item) -> None: col._store[self._prepare_exc_key] = e raise e - def addfinalizer(self, finalizer: Callable[[], object], colitem: Node) -> None: - """Attach a finalizer to the given colitem.""" - assert colitem and not isinstance(colitem, tuple) + def addfinalizer(self, finalizer: Callable[[], object], node: Node) -> None: + """Attach a finalizer to the given node. + + The node must be currently active in the stack. + """ + assert node and not isinstance(node, tuple) assert callable(finalizer) - assert colitem in self.stack, (colitem, self.stack) - self.stack[colitem].append(finalizer) + assert node in self.stack, (node, self.stack) + self.stack[node].append(finalizer) def teardown_exact(self, nextitem: Optional[Item]) -> None: + """Teardown the current stack up until reaching nodes that nextitem + also descends from. + + When nextitem is None (meaning we're at the last item), the entire + stack is torn down. + """ needed_collectors = nextitem and nextitem.listchain() or [] exc = None while self.stack: if list(self.stack.keys()) == needed_collectors[: len(self.stack)]: break - colitem, finalizers = self.stack.popitem() + node, finalizers = self.stack.popitem() while finalizers: fin = finalizers.pop() try: diff --git a/testing/test_runner.py b/testing/test_runner.py index 0e90ea9cca2..e3f2863079f 100644 --- a/testing/test_runner.py +++ b/testing/test_runner.py @@ -26,7 +26,7 @@ def test_setup(self, pytester: Pytester) -> None: ss = item.session._setupstate values = [1] ss.prepare(item) - ss.addfinalizer(values.pop, colitem=item) + ss.addfinalizer(values.pop, item) assert values ss.teardown_exact(None) assert not values From 48fb989a71674189bec2e732fefb8b35b89d58f5 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sun, 24 Jan 2021 14:45:49 +0200 Subject: [PATCH 096/630] runner: avoid using node's store in SetupState SetupState maintains its own state, so it can store the exception itself, instead of using the node's store, which is better avoided when possible. This also reduces the lifetime of the reference-cycle-inducing exception objects which is never a bad thing. --- src/_pytest/runner.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/_pytest/runner.py b/src/_pytest/runner.py index 7bb92cecf81..ae76a247271 100644 --- a/src/_pytest/runner.py +++ b/src/_pytest/runner.py @@ -36,7 +36,6 @@ from _pytest.outcomes import OutcomeException from _pytest.outcomes import Skipped from _pytest.outcomes import TEST_OUTCOME -from _pytest.store import StoreKey if TYPE_CHECKING: from typing_extensions import Literal @@ -467,29 +466,33 @@ class SetupState: """ def __init__(self) -> None: - # Maps node -> the node's finalizers. # The stack is in the dict insertion order. - self.stack: Dict[Node, List[Callable[[], object]]] = {} - - _prepare_exc_key = StoreKey[Union[OutcomeException, Exception]]() + self.stack: Dict[ + Node, + Tuple[ + # Node's finalizers. + List[Callable[[], object]], + # Node's exception, if its setup raised. + Optional[Union[OutcomeException, Exception]], + ], + ] = {} def prepare(self, item: Item) -> None: """Setup objects along the collector chain to the item.""" # If a collector fails its setup, fail its entire subtree of items. # The setup is not retried for each item - the same exception is used. - for col in self.stack: - prepare_exc = col._store.get(self._prepare_exc_key, None) + for col, (finalizers, prepare_exc) in self.stack.items(): if prepare_exc: raise prepare_exc needed_collectors = item.listchain() for col in needed_collectors[len(self.stack) :]: assert col not in self.stack - self.stack[col] = [col.teardown] + self.stack[col] = ([col.teardown], None) try: col.setup() except TEST_OUTCOME as e: - col._store[self._prepare_exc_key] = e + self.stack[col] = (self.stack[col][0], e) raise e def addfinalizer(self, finalizer: Callable[[], object], node: Node) -> None: @@ -500,7 +503,7 @@ def addfinalizer(self, finalizer: Callable[[], object], node: Node) -> None: assert node and not isinstance(node, tuple) assert callable(finalizer) assert node in self.stack, (node, self.stack) - self.stack[node].append(finalizer) + self.stack[node][0].append(finalizer) def teardown_exact(self, nextitem: Optional[Item]) -> None: """Teardown the current stack up until reaching nodes that nextitem @@ -514,7 +517,7 @@ def teardown_exact(self, nextitem: Optional[Item]) -> None: while self.stack: if list(self.stack.keys()) == needed_collectors[: len(self.stack)]: break - node, finalizers = self.stack.popitem() + node, (finalizers, prepare_exc) = self.stack.popitem() while finalizers: fin = finalizers.pop() try: From 83ee1a1f3b940b82223a162c4a695a84669c0ea3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Jan 2021 03:16:50 +0000 Subject: [PATCH 097/630] build(deps): bump pytest-cov in /testing/plugins_integration Bumps [pytest-cov](https://github.com/pytest-dev/pytest-cov) from 2.10.1 to 2.11.1. - [Release notes](https://github.com/pytest-dev/pytest-cov/releases) - [Changelog](https://github.com/pytest-dev/pytest-cov/blob/master/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest-cov/compare/v2.10.1...v2.11.1) Signed-off-by: dependabot[bot] --- testing/plugins_integration/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/plugins_integration/requirements.txt b/testing/plugins_integration/requirements.txt index ae5c9a93fef..86c2a862c9b 100644 --- a/testing/plugins_integration/requirements.txt +++ b/testing/plugins_integration/requirements.txt @@ -2,7 +2,7 @@ anyio[curio,trio]==2.0.2 django==3.1.5 pytest-asyncio==0.14.0 pytest-bdd==4.0.2 -pytest-cov==2.10.1 +pytest-cov==2.11.1 pytest-django==4.1.0 pytest-flakes==4.0.3 pytest-html==3.1.1 From 2a890286f8487c8f3bdc5de90c2fd522da9fd6a7 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 25 Jan 2021 11:52:23 -0300 Subject: [PATCH 098/630] Merge pull request #8275 from pytest-dev/release-6.2.2 Prepare release 6.2.2 (cherry picked from commit 8220eca963472e7918ef7e108bdc1cd8ed155a4a) --- changelog/8152.bugfix.rst | 1 - changelog/8249.bugfix.rst | 1 - doc/en/announce/index.rst | 1 + doc/en/announce/release-6.2.2.rst | 21 +++++++++++++++++++++ doc/en/changelog.rst | 12 ++++++++++++ doc/en/example/parametrize.rst | 4 ++-- doc/en/getting-started.rst | 2 +- 7 files changed, 37 insertions(+), 5 deletions(-) delete mode 100644 changelog/8152.bugfix.rst delete mode 100644 changelog/8249.bugfix.rst create mode 100644 doc/en/announce/release-6.2.2.rst diff --git a/changelog/8152.bugfix.rst b/changelog/8152.bugfix.rst deleted file mode 100644 index d79a832de41..00000000000 --- a/changelog/8152.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed "()" being shown as a skip reason in the verbose test summary line when the reason is empty. diff --git a/changelog/8249.bugfix.rst b/changelog/8249.bugfix.rst deleted file mode 100644 index aa084c75738..00000000000 --- a/changelog/8249.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fix the ``faulthandler`` plugin for occasions when running with ``twisted.logger`` and using ``pytest --capture=no``. diff --git a/doc/en/announce/index.rst b/doc/en/announce/index.rst index e7cac2a1c41..a7656c5ee26 100644 --- a/doc/en/announce/index.rst +++ b/doc/en/announce/index.rst @@ -6,6 +6,7 @@ Release announcements :maxdepth: 2 + release-6.2.2 release-6.2.1 release-6.2.0 release-6.1.2 diff --git a/doc/en/announce/release-6.2.2.rst b/doc/en/announce/release-6.2.2.rst new file mode 100644 index 00000000000..c3999c53860 --- /dev/null +++ b/doc/en/announce/release-6.2.2.rst @@ -0,0 +1,21 @@ +pytest-6.2.2 +======================================= + +pytest 6.2.2 has just been released to PyPI. + +This is a bug-fix release, being a drop-in replacement. To upgrade:: + + pip install --upgrade pytest + +The full changelog is available at https://docs.pytest.org/en/stable/changelog.html. + +Thanks to all of the contributors to this release: + +* Adam Johnson +* Bruno Oliveira +* Chris NeJame +* Ran Benita + + +Happy testing, +The pytest Development Team diff --git a/doc/en/changelog.rst b/doc/en/changelog.rst index 6d66ad1d8dc..3e854f59971 100644 --- a/doc/en/changelog.rst +++ b/doc/en/changelog.rst @@ -28,6 +28,18 @@ with advance notice in the **Deprecations** section of releases. .. towncrier release notes start +pytest 6.2.2 (2021-01-25) +========================= + +Bug Fixes +--------- + +- `#8152 `_: Fixed "()" being shown as a skip reason in the verbose test summary line when the reason is empty. + + +- `#8249 `_: Fix the ``faulthandler`` plugin for occasions when running with ``twisted.logger`` and using ``pytest --capture=no``. + + pytest 6.2.1 (2020-12-15) ========================= diff --git a/doc/en/example/parametrize.rst b/doc/en/example/parametrize.rst index a65ee5f2fd9..771c7e16f28 100644 --- a/doc/en/example/parametrize.rst +++ b/doc/en/example/parametrize.rst @@ -637,13 +637,13 @@ Then run ``pytest`` with verbose mode and with only the ``basic`` marker: platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR - collecting ... collected 14 items / 11 deselected / 3 selected + collecting ... collected 24 items / 21 deselected / 3 selected test_pytest_param_example.py::test_eval[1+7-8] PASSED [ 33%] test_pytest_param_example.py::test_eval[basic_2+4] PASSED [ 66%] test_pytest_param_example.py::test_eval[basic_6*9] XFAIL [100%] - =============== 2 passed, 11 deselected, 1 xfailed in 0.12s ================ + =============== 2 passed, 21 deselected, 1 xfailed in 0.12s ================ As the result: diff --git a/doc/en/getting-started.rst b/doc/en/getting-started.rst index 09410585dc7..1275dff902e 100644 --- a/doc/en/getting-started.rst +++ b/doc/en/getting-started.rst @@ -28,7 +28,7 @@ Install ``pytest`` .. code-block:: bash $ pytest --version - pytest 6.2.1 + pytest 6.2.2 .. _`simpletest`: From 781b73bb523c705d65151d959736b017328e7227 Mon Sep 17 00:00:00 2001 From: Christian Steinmeyer Date: Mon, 25 Jan 2021 16:02:59 +0100 Subject: [PATCH 099/630] Mention that class variables are shared between tests Close #8252 --- doc/en/getting-started.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/en/getting-started.rst b/doc/en/getting-started.rst index 1275dff902e..28fd862cf3b 100644 --- a/doc/en/getting-started.rst +++ b/doc/en/getting-started.rst @@ -210,6 +210,8 @@ This is outlined below: FAILED test_class_demo.py::TestClassDemoInstance::test_two - assert 0 2 failed in 0.12s +Note that attributes added at class level are *class attributes*, so they will be shared between tests. + Request a unique temporary directory for functional tests -------------------------------------------------------------- From 33861098d9145d3441c317c9d9b2265654b53b05 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 25 Jan 2021 12:28:00 -0300 Subject: [PATCH 100/630] Only re-enable fauthandler during unconfigure if it was enabled before --- src/_pytest/faulthandler.py | 9 +++++---- testing/test_faulthandler.py | 37 +++++++++++++++++++++++++++--------- 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/src/_pytest/faulthandler.py b/src/_pytest/faulthandler.py index 9592de82d6d..c8eb0310128 100644 --- a/src/_pytest/faulthandler.py +++ b/src/_pytest/faulthandler.py @@ -12,6 +12,7 @@ fault_handler_stderr_key = StoreKey[TextIO]() +fault_handler_originally_enabled_key = StoreKey[bool]() def pytest_addoption(parser: Parser) -> None: @@ -27,6 +28,7 @@ def pytest_configure(config: Config) -> None: stderr_fd_copy = os.dup(get_stderr_fileno()) config._store[fault_handler_stderr_key] = open(stderr_fd_copy, "w") + config._store[fault_handler_originally_enabled_key] = faulthandler.is_enabled() faulthandler.enable(file=config._store[fault_handler_stderr_key]) @@ -38,10 +40,9 @@ def pytest_unconfigure(config: Config) -> None: if fault_handler_stderr_key in config._store: config._store[fault_handler_stderr_key].close() del config._store[fault_handler_stderr_key] - # Re-enable the faulthandler, attaching it to the default sys.stderr - # so we can see crashes after pytest has finished, usually during - # garbage collection during interpreter shutdown. - faulthandler.enable(file=get_stderr_fileno()) + if config._store.get(fault_handler_originally_enabled_key, False): + # Re-enable the faulthandler if it was originally enabled. + faulthandler.enable(file=get_stderr_fileno()) def get_stderr_fileno() -> int: diff --git a/testing/test_faulthandler.py b/testing/test_faulthandler.py index 411e841a31a..5b7911f21f8 100644 --- a/testing/test_faulthandler.py +++ b/testing/test_faulthandler.py @@ -19,22 +19,41 @@ def test_crash(): assert result.ret != 0 -def test_crash_near_exit(pytester: Pytester) -> None: - """Test that fault handler displays crashes that happen even after - pytest is exiting (for example, when the interpreter is shutting down).""" +def setup_crashing_test(pytester: Pytester) -> None: pytester.makepyfile( """ - import faulthandler - import atexit - def test_ok(): - atexit.register(faulthandler._sigabrt) - """ + import faulthandler + import atexit + def test_ok(): + atexit.register(faulthandler._sigabrt) + """ ) - result = pytester.runpytest_subprocess() + + +def test_crash_during_shutdown_captured(pytester: Pytester) -> None: + """ + Re-enable faulthandler if pytest encountered it enabled during configure. + We should be able to then see crashes during interpreter shutdown. + """ + setup_crashing_test(pytester) + args = (sys.executable, "-Xfaulthandler", "-mpytest") + result = pytester.run(*args) result.stderr.fnmatch_lines(["*Fatal Python error*"]) assert result.ret != 0 +def test_crash_during_shutdown_not_captured(pytester: Pytester) -> None: + """ + Check that pytest leaves faulthandler disabled if it was not enabled during configure. + This prevents us from seeing crashes during interpreter shutdown (see #8260). + """ + setup_crashing_test(pytester) + args = (sys.executable, "-mpytest") + result = pytester.run(*args) + result.stderr.no_fnmatch_line("*Fatal Python error*") + assert result.ret != 0 + + def test_disabled(pytester: Pytester) -> None: """Test option to disable fault handler in the command line.""" pytester.makepyfile( From 6806091b9346e930a8029f4c07b66a987db9855a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 25 Jan 2021 16:41:17 +0000 Subject: [PATCH 101/630] [pre-commit.ci] pre-commit autoupdate --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c8e19b283f8..9130a79a06b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -47,7 +47,7 @@ repos: hooks: - id: python-use-type-annotations - repo: https://github.com/pre-commit/mirrors-mypy - rev: v0.790 + rev: v0.800 hooks: - id: mypy files: ^(src/|testing/) From dfe933cdb4a1885f98c0067701c06c34aa388006 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 25 Jan 2021 15:21:08 -0300 Subject: [PATCH 102/630] Remove mypy workaround after 0.800 update --- src/_pytest/threadexception.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/_pytest/threadexception.py b/src/_pytest/threadexception.py index d084dc6e6a2..b250a52346f 100644 --- a/src/_pytest/threadexception.py +++ b/src/_pytest/threadexception.py @@ -34,11 +34,10 @@ class catch_threading_exception: """ def __init__(self) -> None: - # See https://github.com/python/typeshed/issues/4767 regarding the underscore. - self.args: Optional["threading._ExceptHookArgs"] = None - self._old_hook: Optional[Callable[["threading._ExceptHookArgs"], Any]] = None + self.args: Optional["threading.ExceptHookArgs"] = None + self._old_hook: Optional[Callable[["threading.ExceptHookArgs"], Any]] = None - def _hook(self, args: "threading._ExceptHookArgs") -> None: + def _hook(self, args: "threading.ExceptHookArgs") -> None: self.args = args def __enter__(self) -> "catch_threading_exception": From 8bb3977cb6f8d422dd0d469d9d3c8dbb87704d4f Mon Sep 17 00:00:00 2001 From: Hong Xu Date: Tue, 26 Jan 2021 00:48:01 -0800 Subject: [PATCH 103/630] Doc: Move the module declaration to index.rst When the declaration stays in reference.rst, it creates duplicated "pytest" symbols such as `pytest.pytest.mark.filterwarnings`. --- doc/en/index.rst | 1 + doc/en/reference.rst | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/en/index.rst b/doc/en/index.rst index 58f6c1d86c7..7c4d9394de9 100644 --- a/doc/en/index.rst +++ b/doc/en/index.rst @@ -11,6 +11,7 @@ pytest: helps you write better programs ======================================= +.. module:: pytest The ``pytest`` framework makes it easy to write small tests, yet scales to support complex functional testing for applications and libraries. diff --git a/doc/en/reference.rst b/doc/en/reference.rst index 51c52b33ae9..bc6c5670a5c 100644 --- a/doc/en/reference.rst +++ b/doc/en/reference.rst @@ -3,8 +3,6 @@ API Reference ============= -.. module:: pytest - This page contains the full reference to pytest's API. .. contents:: From 56cea26445095c5be7376dd9a6693d2f976b24ed Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Wed, 27 Jan 2021 10:23:18 +0100 Subject: [PATCH 104/630] Doc: Fix typo --- doc/en/usage.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/usage.rst b/doc/en/usage.rst index fbd3333dabc..0a26182d451 100644 --- a/doc/en/usage.rst +++ b/doc/en/usage.rst @@ -497,7 +497,7 @@ The plugins are automatically enabled for pytest runs, unless the ``-p no:threadexception`` (for thread exceptions) options are given on the command-line. -The warnings may be silenced selectivly using the :ref:`pytest.mark.filterwarnings ref` +The warnings may be silenced selectively using the :ref:`pytest.mark.filterwarnings ref` mark. The warning categories are :class:`pytest.PytestUnraisableExceptionWarning` and :class:`pytest.PytestUnhandledThreadExceptionWarning`. From 0b510bcc5173ae8f3f29750d2097c6ef07a5a142 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Fri, 29 Jan 2021 16:06:36 +0200 Subject: [PATCH 105/630] changelog: fix missing tick Messes with the rendering. --- changelog/8192.bugfix.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog/8192.bugfix.rst b/changelog/8192.bugfix.rst index 5b26ecbe45c..8920b200a21 100644 --- a/changelog/8192.bugfix.rst +++ b/changelog/8192.bugfix.rst @@ -1,3 +1,3 @@ -``testdir.makefile` now silently accepts values which don't start with ``.`` to maintain backward compatibility with older pytest versions. +``testdir.makefile`` now silently accepts values which don't start with ``.`` to maintain backward compatibility with older pytest versions. ``pytester.makefile`` now issues a clearer error if the ``.`` is missing in the ``ext`` argument. From beda7a8a31a690a50d98e14263fcb2348ecb8bd6 Mon Sep 17 00:00:00 2001 From: Maximilian Cosmo Sitter <48606431+mcsitter@users.noreply.github.com> Date: Fri, 29 Jan 2021 15:19:54 +0100 Subject: [PATCH 106/630] Add plugin list --- .github/workflows/update-plugin-list.yml | 33 + README.rst | 2 +- changelog/5105.doc.rst | 1 + doc/en/_templates/globaltoc.html | 1 + doc/en/_templates/links.html | 1 - doc/en/contents.rst | 1 + doc/en/index.rst | 2 +- doc/en/plugin_list.rst | 831 +++++++++++++++++++++++ doc/en/plugins.rst | 2 +- doc/en/writing_plugins.rst | 2 +- scripts/update-plugin-list.py | 86 +++ 11 files changed, 957 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/update-plugin-list.yml create mode 100644 changelog/5105.doc.rst create mode 100644 doc/en/plugin_list.rst create mode 100644 scripts/update-plugin-list.py diff --git a/.github/workflows/update-plugin-list.yml b/.github/workflows/update-plugin-list.yml new file mode 100644 index 00000000000..10b5cb99478 --- /dev/null +++ b/.github/workflows/update-plugin-list.yml @@ -0,0 +1,33 @@ +name: Update Plugin List + +on: + schedule: + # Run daily at midnight. + - cron: '0 0 * * *' + workflow_dispatch: + +jobs: + createPullRequest: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Setup Python + uses: actions/setup-python@v2 + with: + python-version: 3.8 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install packaging requests tabulate[widechars] + - name: Update Plugin List + run: python scripts/update-plugin-list.py + - name: Create Pull Request + uses: peter-evans/create-pull-request@2455e1596942c2902952003bbb574afbbe2ab2e6 + with: + commit-message: '[automated] Update plugin list' + branch: update-plugin-list/patch + delete-branch: true + branch-suffix: short-commit-hash + title: '[automated] Update plugin list' + body: '[automated] Update plugin list' diff --git a/README.rst b/README.rst index 778faf89e50..159bf1d4f75 100644 --- a/README.rst +++ b/README.rst @@ -93,7 +93,7 @@ Features - Python 3.6+ and PyPy3 -- Rich plugin architecture, with over 850+ `external plugins `_ and thriving community +- Rich plugin architecture, with over 850+ `external plugins `_ and thriving community Documentation diff --git a/changelog/5105.doc.rst b/changelog/5105.doc.rst new file mode 100644 index 00000000000..f0cc8bab7b4 --- /dev/null +++ b/changelog/5105.doc.rst @@ -0,0 +1 @@ +Add automatically generated :doc:`plugin_list`. The list is updated on a periodic schedule. diff --git a/doc/en/_templates/globaltoc.html b/doc/en/_templates/globaltoc.html index 4522eb2dec9..5fc1ea13e5b 100644 --- a/doc/en/_templates/globaltoc.html +++ b/doc/en/_templates/globaltoc.html @@ -7,6 +7,7 @@

{{ _('Table Of Contents') }}

  • API Reference
  • Examples
  • Customize
  • +
  • 3rd party plugins
  • Changelog
  • Contributing
  • Backwards Compatibility
  • diff --git a/doc/en/_templates/links.html b/doc/en/_templates/links.html index 6f27757a348..c253ecabfd2 100644 --- a/doc/en/_templates/links.html +++ b/doc/en/_templates/links.html @@ -2,7 +2,6 @@

    Useful Links

    diff --git a/doc/en/contents.rst b/doc/en/contents.rst index 58a08744ced..a439f1eda96 100644 --- a/doc/en/contents.rst +++ b/doc/en/contents.rst @@ -28,6 +28,7 @@ Full pytest documentation nose xunit_setup plugins + plugin_list writing_plugins logging reference diff --git a/doc/en/index.rst b/doc/en/index.rst index 7c4d9394de9..f74ef90a785 100644 --- a/doc/en/index.rst +++ b/doc/en/index.rst @@ -72,7 +72,7 @@ Features - Python 3.6+ and PyPy 3 -- Rich plugin architecture, with over 315+ `external plugins `_ and thriving community +- Rich plugin architecture, with over 315+ :doc:`external plugins ` and thriving community Documentation diff --git a/doc/en/plugin_list.rst b/doc/en/plugin_list.rst new file mode 100644 index 00000000000..927838ac942 --- /dev/null +++ b/doc/en/plugin_list.rst @@ -0,0 +1,831 @@ +Plugins List +============ + +PyPI projects that match "pytest-\*" are considered plugins and are listed +automatically. Packages classified as inactive are excluded. +This list contains 820 plugins. + +============================================================================================================== ======================================================================================================================================================================== ============== ===================== ============================================ +name summary last release status requires +============================================================================================================== ======================================================================================================================================================================== ============== ===================== ============================================ +`pytest-adaptavist `_ pytest plugin for generating test execution results within Jira Test Management (tm4j) Feb 05, 2020 N/A pytest (>=3.4.1) +`pytest-adf `_ Pytest plugin for writing Azure Data Factory integration tests Jun 03, 2020 4 - Beta pytest (>=3.5.0) +`pytest-aggreport `_ pytest plugin for pytest-repeat that generate aggregate report of the same test cases with additional statistics details. Jul 19, 2019 4 - Beta pytest (>=4.3.1) +`pytest-aiofiles `_ pytest fixtures for writing aiofiles tests with pyfakefs May 14, 2017 5 - Production/Stable N/A +`pytest-aiohttp `_ pytest plugin for aiohttp support Dec 05, 2017 N/A pytest +`pytest-aiohttp-client `_ Pytest `client` fixture for the Aiohttp Nov 01, 2020 N/A pytest (>=6) +`pytest-aioresponses `_ py.test integration for aioresponses Dec 21, 2020 4 - Beta pytest (>=3.5.0) +`pytest-aioworkers `_ A plugin to test aioworkers project with pytest Dec 04, 2019 4 - Beta pytest (>=3.5.0) +`pytest-airflow `_ pytest support for airflow. Apr 03, 2019 3 - Alpha pytest (>=4.4.0) +`pytest-alembic `_ A pytest plugin for verifying alembic migrations. Jul 13, 2020 N/A pytest (>=1.0) +`pytest-allclose `_ Pytest fixture extending Numpy's allclose function Jul 30, 2019 5 - Production/Stable pytest +`pytest-allure-adaptor `_ Plugin for py.test to generate allure xml reports Jan 10, 2018 N/A pytest (>=2.7.3) +`pytest-allure-adaptor2 `_ Plugin for py.test to generate allure xml reports Oct 14, 2020 N/A pytest (>=2.7.3) +`pytest-allure-dsl `_ pytest plugin to test case doc string dls instructions Oct 25, 2020 4 - Beta pytest +`pytest-alphamoon `_ Static code checks used at Alphamoon Nov 20, 2020 4 - Beta pytest (>=3.5.0) +`pytest-android `_ This fixture provides a configured "driver" for Android Automated Testing, using uiautomator2. Feb 21, 2019 3 - Alpha pytest +`pytest-annotate `_ pytest-annotate: Generate PyAnnotate annotations from your pytest tests. Aug 23, 2019 3 - Alpha pytest (<6.0.0,>=3.2.0) +`pytest-ansible `_ Plugin for py.test to simplify calling ansible modules from tests or fixtures Oct 26, 2020 5 - Production/Stable pytest +`pytest-ansible-playbook `_ Pytest fixture which runs given ansible playbook file. Mar 08, 2019 4 - Beta N/A +`pytest-ansible-playbook-runner `_ Pytest fixture which runs given ansible playbook file. Dec 02, 2020 4 - Beta pytest (>=3.1.0) +`pytest-antilru `_ Bust functools.lru_cache when running pytest to avoid test pollution Apr 11, 2019 5 - Production/Stable pytest +`pytest-anything `_ Pytest fixtures to assert anything and something Apr 03, 2020 N/A N/A +`pytest-aoc `_ Downloads puzzle inputs for Advent of Code and synthesizes PyTest fixtures Dec 01, 2020 N/A pytest ; extra == 'dev' +`pytest-apistellar `_ apistellar plugin for pytest. Jun 18, 2019 N/A N/A +`pytest-appengine `_ AppEngine integration that works well with pytest-django Feb 27, 2017 N/A N/A +`pytest-appium `_ Pytest plugin for appium Dec 05, 2019 N/A N/A +`pytest-approvaltests `_ A plugin to use approvaltests with pytest Aug 02, 2019 4 - Beta N/A +`pytest-arraydiff `_ pytest plugin to help with comparing array output from tests Dec 06, 2018 4 - Beta pytest +`pytest-asgi-server `_ Convenient ASGI client/server fixtures for Pytest Dec 12, 2020 N/A pytest (>=5.4.1) +`pytest-asptest `_ test Answer Set Programming programs Apr 28, 2018 4 - Beta N/A +`pytest-assertutil `_ pytest-assertutil May 10, 2019 N/A N/A +`pytest-assert-utils `_ Useful assertion utilities for use with pytest Aug 25, 2020 3 - Alpha N/A +`pytest-assume `_ A pytest plugin that allows multiple failures per test Dec 08, 2020 N/A pytest (>=2.7) +`pytest-ast-back-to-python `_ A plugin for pytest devs to view how assertion rewriting recodes the AST Sep 29, 2019 4 - Beta N/A +`pytest-astropy `_ Meta-package containing dependencies for testing Jan 16, 2020 5 - Production/Stable pytest (>=4.6) +`pytest-astropy-header `_ pytest plugin to add diagnostic information to the header of the test output Dec 18, 2019 3 - Alpha pytest (>=2.8) +`pytest-ast-transformer `_ May 04, 2019 3 - Alpha pytest +`pytest-asyncio `_ Pytest support for asyncio. Jun 23, 2020 4 - Beta pytest (>=5.4.0) +`pytest-asyncio-cooperative `_ Run all your asynchronous tests cooperatively. Jan 03, 2021 4 - Beta N/A +`pytest-asyncio-network-simulator `_ pytest-asyncio-network-simulator: Plugin for pytest for simulator the network in tests Jul 31, 2018 3 - Alpha pytest (<3.7.0,>=3.3.2) +`pytest-async-mongodb `_ pytest plugin for async MongoDB Oct 18, 2017 5 - Production/Stable pytest (>=2.5.2) +`pytest-atomic `_ Skip rest of tests if previous test failed. Nov 24, 2018 4 - Beta N/A +`pytest-attrib `_ pytest plugin to select tests based on attributes similar to the nose-attrib plugin May 24, 2016 4 - Beta N/A +`pytest-austin `_ Austin plugin for pytest Oct 11, 2020 4 - Beta N/A +`pytest-autochecklog `_ automatically check condition and log all the checks Apr 25, 2015 4 - Beta N/A +`pytest-automock `_ Pytest plugin for automatical mocks creation Apr 22, 2020 N/A pytest ; extra == 'dev' +`pytest-auto-parametrize `_ pytest plugin: avoid repeating arguments in parametrize Oct 02, 2016 3 - Alpha N/A +`pytest-avoidance `_ Makes pytest skip tests that don not need rerunning May 23, 2019 4 - Beta pytest (>=3.5.0) +`pytest-aws `_ pytest plugin for testing AWS resource configurations Oct 04, 2017 4 - Beta N/A +`pytest-axe `_ pytest plugin for axe-selenium-python Nov 12, 2018 N/A pytest (>=3.0.0) +`pytest-azurepipelines `_ Formatting PyTest output for Azure Pipelines UI Jul 23, 2020 4 - Beta pytest (>=3.5.0) +`pytest-bandit `_ A bandit plugin for pytest Sep 25, 2019 4 - Beta pytest (>=3.5.0) +`pytest-base-url `_ pytest plugin for URL based testing Jun 19, 2020 5 - Production/Stable pytest (>=2.7.3) +`pytest-bdd `_ BDD for pytest Dec 07, 2020 6 - Mature pytest (>=4.3) +`pytest-bdd-splinter `_ Common steps for pytest bdd and splinter integration Aug 12, 2019 5 - Production/Stable pytest (>=4.0.0) +`pytest-bdd-web `_ A simple plugin to use with pytest Jan 02, 2020 4 - Beta pytest (>=3.5.0) +`pytest-bdd-wrappers `_ Feb 11, 2020 2 - Pre-Alpha N/A +`pytest-beakerlib `_ A pytest plugin that reports test results to the BeakerLib framework Mar 17, 2017 5 - Production/Stable pytest +`pytest-beds `_ Fixtures for testing Google Appengine (GAE) apps Jun 07, 2016 4 - Beta N/A +`pytest-bench `_ Benchmark utility that plugs into pytest. Jul 21, 2014 3 - Alpha N/A +`pytest-benchmark `_ A ``pytest`` fixture for benchmarking code. It will group the tests into rounds that are calibrated to the chosen timer. See calibration and FAQ. Jan 10, 2020 5 - Production/Stable pytest (>=3.8) +`pytest-bigchaindb `_ A BigchainDB plugin for pytest. Jan 10, 2020 4 - Beta N/A +`pytest-black `_ A pytest plugin to enable format checking with black Oct 05, 2020 4 - Beta N/A +`pytest-black-multipy `_ Allow '--black' on older Pythons Jan 14, 2021 5 - Production/Stable pytest (!=3.7.3,>=3.5) ; extra == 'testing' +`pytest-blame `_ A pytest plugin helps developers to debug by providing useful commits history. May 04, 2019 N/A pytest (>=4.4.0) +`pytest-blink1 `_ Pytest plugin to emit notifications via the Blink(1) RGB LED Jan 07, 2018 4 - Beta N/A +`pytest-blockage `_ Disable network requests during a test run. Feb 13, 2019 N/A pytest +`pytest-blocker `_ pytest plugin to mark a test as blocker and skip all other tests Sep 07, 2015 4 - Beta N/A +`pytest-board `_ Local continuous test runner with pytest and watchdog. Jan 20, 2019 N/A N/A +`pytest-bpdb `_ A py.test plug-in to enable drop to bpdb debugger on test failure. Jan 19, 2015 2 - Pre-Alpha N/A +`pytest-bravado `_ Pytest-bravado automatically generates from OpenAPI specification client fixtures. Jan 20, 2021 N/A N/A +`pytest-breed-adapter `_ A simple plugin to connect with breed-server Nov 07, 2018 4 - Beta pytest (>=3.5.0) +`pytest-briefcase `_ A pytest plugin for running tests on a Briefcase project. Jun 14, 2020 4 - Beta pytest (>=3.5.0) +`pytest-browser `_ A pytest plugin for console based browser test selection just after the collection phase Dec 10, 2016 3 - Alpha N/A +`pytest-browsermob-proxy `_ BrowserMob proxy plugin for py.test. Jun 11, 2013 4 - Beta N/A +`pytest-browserstack-local `_ ``py.test`` plugin to run ``BrowserStackLocal`` in background. Feb 09, 2018 N/A N/A +`pytest-bug `_ Pytest plugin for marking tests as a bug Jun 02, 2020 5 - Production/Stable pytest (>=3.6.0) +`pytest-bugzilla `_ py.test bugzilla integration plugin May 05, 2010 4 - Beta N/A +`pytest-bugzilla-notifier `_ A plugin that allows you to execute create, update, and read information from BugZilla bugs Jun 15, 2018 4 - Beta pytest (>=2.9.2) +`pytest-buildkite `_ Plugin for pytest that automatically publishes coverage and pytest report annotations to Buildkite. Jul 13, 2019 4 - Beta pytest (>=3.5.0) +`pytest-bwrap `_ Run your tests in Bubblewrap sandboxes Oct 26, 2018 3 - Alpha N/A +`pytest-cache `_ pytest plugin with mechanisms for caching across test runs Jun 04, 2013 3 - Alpha N/A +`pytest-cagoule `_ Pytest plugin to only run tests affected by changes Jan 01, 2020 3 - Alpha N/A +`pytest-camel-collect `_ Enable CamelCase-aware pytest class collection Aug 02, 2020 N/A pytest (>=2.9) +`pytest-canonical-data `_ A plugin which allows to compare results with canonical results, based on previous runs May 08, 2020 2 - Pre-Alpha pytest (>=3.5.0) +`pytest-caprng `_ A plugin that replays pRNG state on failure. May 02, 2018 4 - Beta N/A +`pytest-capture-deprecatedwarnings `_ pytest plugin to capture all deprecatedwarnings and put them in one file Apr 30, 2019 N/A N/A +`pytest-cases `_ Separate test code from test cases in pytest. Jan 25, 2021 5 - Production/Stable N/A +`pytest-cassandra `_ Cassandra CCM Test Fixtures for pytest Nov 04, 2017 1 - Planning N/A +`pytest-catchlog `_ py.test plugin to catch log messages. This is a fork of pytest-capturelog. Jan 24, 2016 4 - Beta pytest (>=2.6) +`pytest-catch-server `_ Pytest plugin with server for catching HTTP requests. Dec 12, 2019 5 - Production/Stable N/A +`pytest-celery `_ pytest-celery a shim pytest plugin to enable celery.contrib.pytest Aug 05, 2020 N/A N/A +`pytest-chalice `_ A set of py.test fixtures for AWS Chalice Jul 01, 2020 4 - Beta N/A +`pytest-change-report `_ turn . into √,turn F into x Sep 14, 2020 N/A pytest +`pytest-chdir `_ A pytest fixture for changing current working directory Jan 28, 2020 N/A pytest (>=5.0.0,<6.0.0) +`pytest-check `_ A pytest plugin that allows multiple failures per test. Dec 27, 2020 5 - Production/Stable N/A +`pytest-checkdocs `_ check the README when running tests Jan 01, 2021 5 - Production/Stable pytest (!=3.7.3,>=3.5) ; extra == 'testing' +`pytest-checkipdb `_ plugin to check if there are ipdb debugs left Jul 22, 2020 5 - Production/Stable pytest (>=2.9.2) +`pytest-check-links `_ Check links in files Jul 29, 2020 N/A N/A +`pytest-check-mk `_ pytest plugin to test Check_MK checks Nov 19, 2015 4 - Beta pytest +`pytest-circleci `_ py.test plugin for CircleCI May 03, 2019 N/A N/A +`pytest-circleci-parallelized `_ Parallelize pytest across CircleCI workers. Mar 26, 2019 N/A N/A +`pytest-ckan `_ Backport of CKAN 2.9 pytest plugin and fixtures to CAKN 2.8 Apr 28, 2020 4 - Beta pytest +`pytest-clarity `_ A plugin providing an alternative, colourful diff output for failing assertions. Jan 23, 2020 3 - Alpha N/A +`pytest-cldf `_ Easy quality control for CLDF datasets using pytest May 06, 2019 N/A N/A +`pytest-click `_ Py.test plugin for Click Aug 29, 2020 5 - Production/Stable pytest (>=5.0) +`pytest-clld `_ May 06, 2020 N/A pytest (>=3.6) +`pytest-cloud `_ Distributed tests planner plugin for pytest testing framework. Oct 05, 2020 6 - Mature N/A +`pytest-cloudflare-worker `_ pytest plugin for testing cloudflare workers Oct 19, 2020 4 - Beta pytest (>=6.0.0) +`pytest-cobra `_ PyTest plugin for testing Smart Contracts for Ethereum blockchain. Jun 29, 2019 3 - Alpha pytest (<4.0.0,>=3.7.1) +`pytest-codecheckers `_ pytest plugin to add source code sanity checks (pep8 and friends) Feb 13, 2010 N/A N/A +`pytest-codegen `_ Automatically create pytest test signatures Aug 23, 2020 2 - Pre-Alpha N/A +`pytest-codestyle `_ pytest plugin to run pycodestyle Mar 23, 2020 3 - Alpha N/A +`pytest-collect-formatter `_ Formatter for pytest collect output Nov 19, 2020 5 - Production/Stable N/A +`pytest-colordots `_ Colorizes the progress indicators Oct 06, 2017 5 - Production/Stable N/A +`pytest-commander `_ An interactive GUI test runner for PyTest Nov 21, 2020 N/A pytest (>=5.0.0) +`pytest-common-subject `_ pytest framework for testing different aspects of a common method Nov 12, 2020 N/A pytest (>=3.6,<7) +`pytest-concurrent `_ Concurrently execute test cases with multithread, multiprocess and gevent Jan 12, 2019 4 - Beta pytest (>=3.1.1) +`pytest-config `_ Base configurations and utilities for developing your Python project test suite with pytest. Nov 07, 2014 5 - Production/Stable N/A +`pytest-confluence-report `_ Package stands for pytest plugin to upload results into Confluence page. Nov 06, 2020 N/A N/A +`pytest-console-scripts `_ Pytest plugin for testing console scripts Nov 20, 2020 4 - Beta N/A +`pytest-consul `_ pytest plugin with fixtures for testing consul aware apps Nov 24, 2018 3 - Alpha pytest +`pytest-contextfixture `_ Define pytest fixtures as context managers. Mar 12, 2013 4 - Beta N/A +`pytest-contexts `_ A plugin to run tests written with the Contexts framework using pytest Jul 23, 2018 4 - Beta N/A +`pytest-cookies `_ The pytest plugin for your Cookiecutter templates. 🍪 Feb 14, 2020 5 - Production/Stable pytest (<6.0.0,>=3.3.0) +`pytest-couchdbkit `_ py.test extension for per-test couchdb databases using couchdbkit Apr 17, 2012 N/A N/A +`pytest-count `_ count erros and send email Jan 12, 2018 4 - Beta N/A +`pytest-cov `_ Pytest plugin for measuring coverage. Jan 20, 2021 5 - Production/Stable pytest (>=4.6) +`pytest-cover `_ Pytest plugin for measuring coverage. Forked from `pytest-cov`. Aug 01, 2015 5 - Production/Stable N/A +`pytest-coverage `_ Jun 17, 2015 N/A N/A +`pytest-coverage-context `_ Coverage dynamic context support for PyTest, including sub-processes Jan 04, 2021 4 - Beta pytest (>=6.1.0) +`pytest-cov-exclude `_ Pytest plugin for excluding tests based on coverage data Apr 29, 2016 4 - Beta pytest (>=2.8.0,<2.9.0); extra == 'dev' +`pytest-cpp `_ Use pytest's runner to discover and execute C++ tests Dec 10, 2020 4 - Beta pytest (!=5.4.0,!=5.4.1) +`pytest-cram `_ Run cram tests with pytest. Aug 08, 2020 N/A N/A +`pytest-crate `_ Manages CrateDB instances during your integration tests May 28, 2019 3 - Alpha pytest (>=4.0) +`pytest-cricri `_ A Cricri plugin for pytest. Jan 27, 2018 N/A pytest +`pytest-crontab `_ add crontab task in crontab Dec 09, 2019 N/A N/A +`pytest-csv `_ CSV output for pytest. Jun 24, 2019 N/A pytest (>=4.4) +`pytest-curio `_ Pytest support for curio. Oct 07, 2020 N/A N/A +`pytest-curl-report `_ pytest plugin to generate curl command line report Dec 11, 2016 4 - Beta N/A +`pytest-custom-exit-code `_ Exit pytest test session with custom exit code in different scenarios Aug 07, 2019 4 - Beta pytest (>=4.0.2) +`pytest-custom-report `_ Configure the symbols displayed for test outcomes Jan 30, 2019 N/A pytest +`pytest-cython `_ A plugin for testing Cython extension modules Jan 26, 2021 4 - Beta pytest (>=2.7.3) +`pytest-darker `_ A pytest plugin for checking of modified code using Darker Aug 16, 2020 N/A pytest (>=6.0.1) ; extra == 'test' +`pytest-dash `_ pytest fixtures to run dash applications. Mar 18, 2019 N/A N/A +`pytest-data `_ Useful functions for managing data for pytest fixtures Nov 01, 2016 5 - Production/Stable N/A +`pytest-databricks `_ Pytest plugin for remote Databricks notebooks testing Jul 29, 2020 N/A pytest +`pytest-datadir `_ pytest plugin for test data directories and files Oct 22, 2019 5 - Production/Stable pytest (>=2.7.0) +`pytest-datadir-mgr `_ Manager for test data providing downloads, caching of generated files, and a context for temp directories. Jan 08, 2021 5 - Production/Stable pytest (>=6.0.1,<7.0.0) +`pytest-datadir-ng `_ Fixtures for pytest allowing test functions/methods to easily retrieve test resources from the local filesystem. Dec 25, 2019 5 - Production/Stable pytest +`pytest-data-file `_ Fixture "data" and "case_data" for test from yaml file Dec 04, 2019 N/A N/A +`pytest-datafiles `_ py.test plugin to create a 'tmpdir' containing predefined files/directories. Oct 07, 2018 5 - Production/Stable pytest (>=3.6) +`pytest-datafixtures `_ Data fixtures for pytest made simple Dec 05, 2020 5 - Production/Stable N/A +`pytest-dataplugin `_ A pytest plugin for managing an archive of test data. Sep 16, 2017 1 - Planning N/A +`pytest-datarecorder `_ A py.test plugin recording and comparing test output. Apr 20, 2020 5 - Production/Stable pytest +`pytest-datatest `_ A pytest plugin for test driven data-wrangling (this is the development version of datatest's pytest integration). Oct 15, 2020 4 - Beta pytest (>=3.3) +`pytest-db `_ Session scope fixture "db" for mysql query or change Dec 04, 2019 N/A N/A +`pytest-dbfixtures `_ Databases fixtures plugin for py.test. Dec 07, 2016 4 - Beta N/A +`pytest-dbt-adapter `_ A pytest plugin for testing dbt adapter plugins Jan 07, 2021 N/A pytest (<7,>=6) +`pytest-dbus-notification `_ D-BUS notifications for pytest results. Mar 05, 2014 5 - Production/Stable N/A +`pytest-deadfixtures `_ A simple plugin to list unused fixtures in pytest Jul 23, 2020 5 - Production/Stable N/A +`pytest-dependency `_ Manage dependencies of tests Feb 14, 2020 4 - Beta N/A +`pytest-depends `_ Tests that depend on other tests Apr 05, 2020 5 - Production/Stable pytest (>=3) +`pytest-deprecate `_ Mark tests as testing a deprecated feature with a warning note. Jul 01, 2019 N/A N/A +`pytest-describe `_ Describe-style plugin for pytest Apr 21, 2020 3 - Alpha pytest (>=2.6.0) +`pytest-describe-it `_ plugin for rich text descriptions Jul 19, 2019 4 - Beta pytest +`pytest-devpi-server `_ DevPI server fixture for py.test May 28, 2019 5 - Production/Stable pytest +`pytest-diamond `_ pytest plugin for diamond Aug 31, 2015 4 - Beta N/A +`pytest-dicom `_ pytest plugin to provide DICOM fixtures Dec 19, 2018 3 - Alpha pytest +`pytest-dictsdiff `_ Jul 26, 2019 N/A N/A +`pytest-diff `_ A simple plugin to use with pytest Mar 30, 2019 4 - Beta pytest (>=3.5.0) +`pytest-diffeo `_ Common py.test support for Diffeo packages Apr 08, 2016 3 - Alpha N/A +`pytest-disable `_ pytest plugin to disable a test and skip it from testrun Sep 10, 2015 4 - Beta N/A +`pytest-disable-plugin `_ Disable plugins per test Feb 28, 2019 4 - Beta pytest (>=3.5.0) +`pytest-discord `_ A pytest plugin to notify test results to a Discord channel. Aug 15, 2020 3 - Alpha pytest (!=6.0.0,<7,>=3.3.2) +`pytest-django `_ A Django plugin for pytest. Oct 22, 2020 5 - Production/Stable pytest (>=5.4.0) +`pytest-django-ahead `_ A Django plugin for pytest. Oct 27, 2016 5 - Production/Stable pytest (>=2.9) +`pytest-djangoapp `_ Nice pytest plugin to help you with Django pluggable application testing. Sep 21, 2020 4 - Beta N/A +`pytest-django-cache-xdist `_ A djangocachexdist plugin for pytest May 12, 2020 4 - Beta N/A +`pytest-django-casperjs `_ Integrate CasperJS with your django tests as a pytest fixture. Mar 15, 2015 2 - Pre-Alpha N/A +`pytest-django-dotenv `_ Pytest plugin used to setup environment variables with django-dotenv Nov 26, 2019 4 - Beta pytest (>=2.6.0) +`pytest-django-factories `_ Factories for your Django models that can be used as Pytest fixtures. Nov 12, 2020 4 - Beta N/A +`pytest-django-gcir `_ A Django plugin for pytest. Mar 06, 2018 5 - Production/Stable N/A +`pytest-django-haystack `_ Cleanup your Haystack indexes between tests Sep 03, 2017 5 - Production/Stable pytest (>=2.3.4) +`pytest-django-ifactory `_ A model instance factory for pytest-django Jan 13, 2021 3 - Alpha N/A +`pytest-django-lite `_ The bare minimum to integrate py.test with Django. Jan 30, 2014 N/A N/A +`pytest-django-model `_ A Simple Way to Test your Django Models Feb 14, 2019 4 - Beta N/A +`pytest-django-ordering `_ A pytest plugin for preserving the order in which Django runs tests. Jul 25, 2019 5 - Production/Stable pytest (>=2.3.0) +`pytest-django-queries `_ Generate performance reports from your django database performance tests. Sep 03, 2020 N/A N/A +`pytest-djangorestframework `_ A djangorestframework plugin for pytest Aug 11, 2019 4 - Beta N/A +`pytest-django-rq `_ A pytest plugin to help writing unit test for django-rq Apr 13, 2020 4 - Beta N/A +`pytest-django-sqlcounts `_ py.test plugin for reporting the number of SQLs executed per django testcase. Jun 16, 2015 4 - Beta N/A +`pytest-django-testing-postgresql `_ Use a temporary PostgreSQL database with pytest-django Dec 05, 2019 3 - Alpha N/A +`pytest-doc `_ A documentation plugin for py.test. Jun 28, 2015 5 - Production/Stable N/A +`pytest-docgen `_ An RST Documentation Generator for pytest-based test suites Apr 17, 2020 N/A N/A +`pytest-docker `_ Simple pytest fixtures for Docker and docker-compose based tests Sep 22, 2020 N/A pytest (<7.0,>=4.0) +`pytest-docker-butla `_ Jun 16, 2019 3 - Alpha N/A +`pytest-dockerc `_ Run, manage and stop Docker Compose project from Docker API Oct 09, 2020 5 - Production/Stable pytest (>=3.0) +`pytest-docker-compose `_ Manages Docker containers during your integration tests Jan 26, 2021 5 - Production/Stable pytest (>=3.3) +`pytest-docker-db `_ A plugin to use docker databases for pytests Apr 19, 2020 5 - Production/Stable pytest (>=3.1.1) +`pytest-docker-fixtures `_ pytest docker fixtures Sep 30, 2020 3 - Alpha N/A +`pytest-docker-pexpect `_ pytest plugin for writing functional tests with pexpect and docker Jan 14, 2019 N/A pytest +`pytest-docker-postgresql `_ A simple plugin to use with pytest Sep 24, 2019 4 - Beta pytest (>=3.5.0) +`pytest-docker-py `_ Easy to use, simple to extend, pytest plugin that minimally leverages docker-py. Nov 27, 2018 N/A pytest (==4.0.0) +`pytest-docker-registry-fixtures `_ Pytest fixtures for testing with docker registries. Jan 25, 2021 4 - Beta pytest +`pytest-docker-tools `_ Docker integration tests for pytest Jan 15, 2021 4 - Beta pytest (>=6.0.1,<7.0.0) +`pytest-docs `_ Documentation tool for pytest Nov 11, 2018 4 - Beta pytest (>=3.5.0) +`pytest-docstyle `_ pytest plugin to run pydocstyle Mar 23, 2020 3 - Alpha N/A +`pytest-doctest-custom `_ A py.test plugin for customizing string representations of doctest results. Jul 25, 2016 4 - Beta N/A +`pytest-doctest-ellipsis-markers `_ Setup additional values for ELLIPSIS_MARKER for doctests Jan 12, 2018 4 - Beta N/A +`pytest-doctest-import `_ A simple pytest plugin to import names and add them to the doctest namespace. Nov 13, 2018 4 - Beta pytest (>=3.3.0) +`pytest-doctestplus `_ Pytest plugin with advanced doctest features. Jan 15, 2021 3 - Alpha pytest (>=4.6) +`pytest-doctest-ufunc `_ A plugin to run doctests in docstrings of Numpy ufuncs Aug 02, 2020 4 - Beta pytest (>=3.5.0) +`pytest-dolphin `_ Some extra stuff that we use ininternally Nov 30, 2016 4 - Beta pytest (==3.0.4) +`pytest-doorstop `_ A pytest plugin for adding test results into doorstop items. Jun 09, 2020 4 - Beta pytest (>=3.5.0) +`pytest-dotenv `_ A py.test plugin that parses environment files before running tests Jun 16, 2020 4 - Beta pytest (>=5.0.0) +`pytest-drf `_ A Django REST framework plugin for pytest. Nov 12, 2020 5 - Production/Stable pytest (>=3.6) +`pytest-drivings `_ Tool to allow webdriver automation to be ran locally or remotely Jan 13, 2021 N/A N/A +`pytest-drop-dup-tests `_ A Pytest plugin to drop duplicated tests during collection May 23, 2020 4 - Beta pytest (>=2.7) +`pytest-dump2json `_ A pytest plugin for dumping test results to json. Jun 29, 2015 N/A N/A +`pytest-dynamicrerun `_ A pytest plugin to rerun tests dynamically based off of test outcome and output. Aug 15, 2020 4 - Beta N/A +`pytest-dynamodb `_ DynamoDB fixtures for pytest Feb 20, 2020 5 - Production/Stable pytest (>=3.0.0) +`pytest-easy-addoption `_ pytest-easy-addoption: Easy way to work with pytest addoption Jan 22, 2020 N/A N/A +`pytest-easy-api `_ Simple API testing with pytest Mar 26, 2018 N/A N/A +`pytest-easyMPI `_ Package that supports mpi tests in pytest Oct 21, 2020 N/A N/A +`pytest-easyread `_ pytest plugin that makes terminal printouts of the reports easier to read Nov 17, 2017 N/A N/A +`pytest-ec2 `_ Pytest execution on EC2 instance Oct 22, 2019 3 - Alpha N/A +`pytest-echo `_ pytest plugin with mechanisms for echoing environment variables, package version and generic attributes Jan 08, 2020 5 - Production/Stable N/A +`pytest-elasticsearch `_ Elasticsearch process and client fixtures for py.test. Feb 19, 2020 5 - Production/Stable pytest (>=3.0.0) +`pytest-elements `_ Tool to help automate user interfaces Jan 13, 2021 N/A pytest (>=5.4,<6.0) +`pytest-elk-reporter `_ A simple plugin to use with pytest Jan 24, 2021 4 - Beta pytest (>=3.5.0) +`pytest-email `_ Send execution result email Jul 08, 2020 N/A pytest +`pytest-emoji `_ A pytest plugin that adds emojis to your test result report Feb 19, 2019 4 - Beta pytest (>=4.2.1) +`pytest-emoji-output `_ Pytest plugin to represent test output with emoji support Oct 03, 2020 4 - Beta N/A +`pytest-enabler `_ Enable installed pytest plugins Jan 19, 2021 5 - Production/Stable pytest (!=3.7.3,>=3.5) ; extra == 'testing' +`pytest-enhancements `_ Improvements for pytest (rejected upstream) Oct 30, 2019 4 - Beta N/A +`pytest-env `_ py.test plugin that allows you to add environment variables. Jun 16, 2017 4 - Beta N/A +`pytest-envfiles `_ A py.test plugin that parses environment files before running tests Oct 08, 2015 3 - Alpha N/A +`pytest-env-info `_ Push information about the running pytest into envvars Nov 25, 2017 4 - Beta pytest (>=3.1.1) +`pytest-envraw `_ py.test plugin that allows you to add environment variables. Aug 27, 2020 4 - Beta pytest (>=2.6.0) +`pytest-envvars `_ Pytest plugin to validate use of envvars on your tests Jun 13, 2020 5 - Production/Stable pytest (>=3.0.0) +`pytest-env-yaml `_ Apr 02, 2019 N/A N/A +`pytest-eradicate `_ pytest plugin to check for commented out code Sep 08, 2020 N/A pytest (>=2.4.2) +`pytest-error-for-skips `_ Pytest plugin to treat skipped tests a test failure Dec 19, 2019 4 - Beta pytest (>=4.6) +`pytest-eth `_ PyTest plugin for testing Smart Contracts for Ethereum Virtual Machine (EVM). Aug 14, 2020 1 - Planning N/A +`pytest-ethereum `_ pytest-ethereum: Pytest library for ethereum projects. Jun 24, 2019 3 - Alpha pytest (==3.3.2); extra == 'dev' +`pytest-eucalyptus `_ Pytest Plugin for BDD Aug 13, 2019 N/A pytest (>=4.2.0) +`pytest-excel `_ pytest plugin for generating excel reports Oct 06, 2020 5 - Production/Stable N/A +`pytest-exceptional `_ Better exceptions Mar 16, 2017 4 - Beta N/A +`pytest-exception-script `_ Walk your code through exception script to check it's resiliency to failures. Aug 04, 2020 3 - Alpha pytest +`pytest-executable `_ pytest plugin for testing executables Aug 10, 2020 4 - Beta pytest (<6.1,>=4.3) +`pytest-expect `_ py.test plugin to store test expectations and mark tests based on them Apr 21, 2016 4 - Beta N/A +`pytest-expecter `_ Better testing with expecter and pytest. Jul 08, 2020 5 - Production/Stable N/A +`pytest-expectr `_ This plugin is used to expect multiple assert using pytest framework. Oct 05, 2018 N/A pytest (>=2.4.2) +`pytest-exploratory `_ Interactive console for pytest. Jan 20, 2021 N/A pytest (>=5.3) +`pytest-external-blockers `_ a special outcome for tests that are blocked for external reasons Oct 04, 2016 N/A N/A +`pytest-extra-durations `_ A pytest plugin to get durations on a per-function basis and per module basis. Apr 21, 2020 4 - Beta pytest (>=3.5.0) +`pytest-fabric `_ Provides test utilities to run fabric task tests by using docker containers Sep 12, 2018 5 - Production/Stable N/A +`pytest-factory `_ Use factories for test setup with py.test Sep 06, 2020 3 - Alpha pytest (>4.3) +`pytest-factoryboy `_ Factory Boy support for pytest. Dec 30, 2020 6 - Mature pytest (>=4.6) +`pytest-factoryboy-fixtures `_ Generates pytest fixtures that allow the use of type hinting Jun 25, 2020 N/A N/A +`pytest-factoryboy-state `_ Simple factoryboy random state management Dec 11, 2020 4 - Beta pytest (>=5.0) +`pytest-failed-to-verify `_ A pytest plugin that helps better distinguishing real test failures from setup flakiness. Aug 08, 2019 5 - Production/Stable pytest (>=4.1.0) +`pytest-faker `_ Faker integration with the pytest framework. Dec 19, 2016 6 - Mature N/A +`pytest-falcon `_ Pytest helpers for Falcon. Sep 07, 2016 4 - Beta N/A +`pytest-falcon-client `_ Pytest `client` fixture for the Falcon Framework Mar 19, 2019 N/A N/A +`pytest-fantasy `_ Pytest plugin for Flask Fantasy Framework Mar 14, 2019 N/A N/A +`pytest-fastapi `_ Dec 27, 2020 N/A N/A +`pytest-fastest `_ Use SCM and coverage to run only needed tests Mar 05, 2020 N/A N/A +`pytest-faulthandler `_ py.test plugin that activates the fault handler module for tests (dummy package) Jul 04, 2019 6 - Mature pytest (>=5.0) +`pytest-fauxfactory `_ Integration of fauxfactory into pytest. Dec 06, 2017 5 - Production/Stable pytest (>=3.2) +`pytest-figleaf `_ py.test figleaf coverage plugin Jan 18, 2010 5 - Production/Stable N/A +`pytest-filedata `_ easily load data from files Jan 17, 2019 4 - Beta N/A +`pytest-filemarker `_ A pytest plugin that runs marked tests when files change. Dec 01, 2020 N/A pytest +`pytest-filter-case `_ run test cases filter by mark Nov 05, 2020 N/A N/A +`pytest-filter-subpackage `_ Pytest plugin for filtering based on sub-packages Jan 09, 2020 3 - Alpha pytest (>=3.0) +`pytest-finer-verdicts `_ A pytest plugin to treat non-assertion failures as test errors. Jun 18, 2020 N/A pytest (>=5.4.3) +`pytest-firefox `_ pytest plugin to manipulate firefox Aug 08, 2017 3 - Alpha pytest (>=3.0.2) +`pytest-fixture-config `_ Fixture configuration utils for py.test May 28, 2019 5 - Production/Stable pytest +`pytest-fixture-marker `_ A pytest plugin to add markers based on fixtures used. Oct 11, 2020 5 - Production/Stable N/A +`pytest-fixture-order `_ pytest plugin to control fixture evaluation order Aug 25, 2020 N/A pytest (>=3.0) +`pytest-fixtures `_ Common fixtures for pytest May 01, 2019 5 - Production/Stable N/A +`pytest-fixture-tools `_ Plugin for pytest which provides tools for fixtures Aug 18, 2020 6 - Mature pytest +`pytest-flake8 `_ pytest plugin to check FLAKE8 requirements Dec 16, 2020 4 - Beta pytest (>=3.5) +`pytest-flake8dir `_ A pytest fixture for testing flake8 plugins. Dec 13, 2020 5 - Production/Stable pytest +`pytest-flakefinder `_ Runs tests multiple times to expose flakiness. Jul 28, 2020 4 - Beta pytest (>=2.7.1) +`pytest-flakes `_ pytest plugin to check source code with pyflakes Nov 28, 2020 5 - Production/Stable N/A +`pytest-flaptastic `_ Flaptastic py.test plugin Mar 17, 2019 N/A N/A +`pytest-flask `_ A set of py.test fixtures to test Flask applications. Nov 09, 2020 5 - Production/Stable pytest (>=5.2) +`pytest-flask-sqlalchemy `_ A pytest plugin for preserving test isolation in Flask-SQlAlchemy using database transactions. Apr 04, 2019 4 - Beta pytest (>=3.2.1) +`pytest-flask-sqlalchemy-transactions `_ Run tests in transactions using pytest, Flask, and SQLalchemy. Aug 02, 2018 4 - Beta pytest (>=3.2.1) +`pytest-focus `_ A pytest plugin that alerts user of failed test cases with screen notifications May 04, 2019 4 - Beta pytest +`pytest-forcefail `_ py.test plugin to make the test failing regardless of pytest.mark.xfail May 15, 2018 4 - Beta N/A +`pytest-forward-compatability `_ A name to avoid typosquating pytest-foward-compatibility Sep 06, 2020 N/A N/A +`pytest-forward-compatibility `_ A pytest plugin to shim pytest commandline options for fowards compatibility Sep 29, 2020 N/A N/A +`pytest-freezegun `_ Wrap tests with fixtures in freeze_time Jul 19, 2020 4 - Beta pytest (>=3.0.0) +`pytest-freeze-reqs `_ Check if requirement files are frozen Nov 14, 2019 N/A N/A +`pytest-func-cov `_ Pytest plugin for measuring function coverage May 24, 2020 3 - Alpha pytest (>=5) +`pytest-fxa `_ pytest plugin for Firefox Accounts Aug 28, 2018 5 - Production/Stable N/A +`pytest-fxtest `_ Oct 27, 2020 N/A N/A +`pytest-gc `_ The garbage collector plugin for py.test Feb 01, 2018 N/A N/A +`pytest-gcov `_ Uses gcov to measure test coverage of a C library Feb 01, 2018 3 - Alpha N/A +`pytest-gevent `_ Ensure that gevent is properly patched when invoking pytest Feb 25, 2020 N/A pytest +`pytest-gherkin `_ A flexible framework for executing BDD gherkin tests Jul 27, 2019 3 - Alpha pytest (>=5.0.0) +`pytest-ghostinspector `_ For finding/executing Ghost Inspector tests May 17, 2016 3 - Alpha N/A +`pytest-girder `_ A set of pytest fixtures for testing Girder applications. Jan 18, 2021 N/A N/A +`pytest-git `_ Git repository fixture for py.test May 28, 2019 5 - Production/Stable pytest +`pytest-gitcov `_ Pytest plugin for reporting on coverage of the last git commit. Jan 11, 2020 2 - Pre-Alpha N/A +`pytest-git-fixtures `_ Pytest fixtures for testing with git. Jan 25, 2021 4 - Beta pytest +`pytest-github `_ Plugin for py.test that associates tests with github issues using a marker. Mar 07, 2019 5 - Production/Stable N/A +`pytest-github-actions-annotate-failures `_ pytest plugin to annotate failed tests with a workflow command for GitHub Actions Oct 13, 2020 N/A pytest (>=4.0.0) +`pytest-gitignore `_ py.test plugin to ignore the same files as git Jul 17, 2015 4 - Beta N/A +`pytest-gnupg-fixtures `_ Pytest fixtures for testing with gnupg. Jan 12, 2021 4 - Beta pytest +`pytest-golden `_ Plugin for pytest that offloads expected outputs to data files Nov 23, 2020 N/A pytest (>=6.1.2,<7.0.0) +`pytest-graphql-schema `_ Get graphql schema as fixture for pytest Oct 18, 2019 N/A N/A +`pytest-greendots `_ Green progress dots Feb 08, 2014 3 - Alpha N/A +`pytest-growl `_ Growl notifications for pytest results. Jan 13, 2014 5 - Production/Stable N/A +`pytest-grpc `_ pytest plugin for grpc May 01, 2020 N/A pytest (>=3.6.0) +`pytest-hammertime `_ Display "🔨 " instead of "." for passed pytest tests. Jul 28, 2018 N/A pytest +`pytest-harvest `_ Store data created during your pytest tests execution, and retrieve it at the end of the session, e.g. for applicative benchmarking purposes. Dec 08, 2020 5 - Production/Stable N/A +`pytest-helm-chart `_ A plugin to provide different types and configs of Kubernetes clusters that can be used for testing. Jun 15, 2020 4 - Beta pytest (>=5.4.2,<6.0.0) +`pytest-helm-charts `_ A plugin to provide different types and configs of Kubernetes clusters that can be used for testing. Dec 22, 2020 4 - Beta pytest (>=6.1.2,<7.0.0) +`pytest-helper `_ Functions to help in using the pytest testing framework May 31, 2019 5 - Production/Stable N/A +`pytest-helpers `_ pytest helpers May 17, 2020 N/A pytest +`pytest-helpers-namespace `_ PyTest Helpers Namespace Jan 07, 2019 5 - Production/Stable pytest (>=2.9.1) +`pytest-hidecaptured `_ Hide captured output May 04, 2018 4 - Beta pytest (>=2.8.5) +`pytest-historic `_ Custom report to display pytest historical execution records Apr 08, 2020 N/A pytest +`pytest-historic-hook `_ Custom listener to store execution results into MYSQL DB, which is used for pytest-historic report Apr 08, 2020 N/A pytest +`pytest-homeassistant `_ A pytest plugin for use with homeassistant custom components. Aug 12, 2020 4 - Beta N/A +`pytest-homeassistant-custom-component `_ Experimental package to automatically extract test plugins for Home Assistant custom components Jan 05, 2021 3 - Alpha pytest (==6.1.2) +`pytest-honors `_ Report on tests that honor constraints, and guard against regressions Mar 06, 2020 4 - Beta N/A +`pytest-hoverfly-wrapper `_ Integrates the Hoverfly HTTP proxy into Pytest Oct 25, 2020 4 - Beta N/A +`pytest-html `_ pytest plugin for generating HTML reports Dec 13, 2020 5 - Production/Stable pytest (!=6.0.0,>=5.0) +`pytest-html-lee `_ optimized pytest plugin for generating HTML reports Jun 30, 2020 5 - Production/Stable pytest (>=5.0) +`pytest-html-profiling `_ Pytest plugin for generating HTML reports with per-test profiling and optionally call graph visualizations. Based on pytest-html by Dave Hunt. Feb 11, 2020 5 - Production/Stable pytest (>=3.0) +`pytest-html-reporter `_ Generates a static html report based on pytest framework Sep 28, 2020 N/A N/A +`pytest-html-thread `_ pytest plugin for generating HTML reports Dec 29, 2020 5 - Production/Stable N/A +`pytest-http `_ Fixture "http" for http requests Dec 05, 2019 N/A N/A +`pytest-httpbin `_ Easily test your HTTP library against a local copy of httpbin Feb 11, 2019 5 - Production/Stable N/A +`pytest-http-mocker `_ Pytest plugin for http mocking (via https://github.com/vilus/mocker) Oct 20, 2019 N/A N/A +`pytest-httpretty `_ A thin wrapper of HTTPretty for pytest Feb 16, 2014 3 - Alpha N/A +`pytest-httpserver `_ pytest-httpserver is a httpserver for pytest Oct 18, 2020 3 - Alpha pytest ; extra == 'dev' +`pytest-httpx `_ Send responses to httpx. Nov 25, 2020 5 - Production/Stable pytest (==6.*) +`pytest-hue `_ Visualise PyTest status via your Phillips Hue lights May 09, 2019 N/A N/A +`pytest-hypo-25 `_ help hypo module for pytest Jan 12, 2020 3 - Alpha N/A +`pytest-ibutsu `_ A plugin to sent pytest results to an Ibutsu server Dec 02, 2020 4 - Beta pytest +`pytest-icdiff `_ use icdiff for better error messages in pytest assertions Apr 08, 2020 4 - Beta N/A +`pytest-idapro `_ A pytest plugin for idapython. Allows a pytest setup to run tests outside and inside IDA in an automated manner by runnig pytest inside IDA and by mocking idapython api Nov 03, 2018 N/A N/A +`pytest-ignore-flaky `_ ignore failures from flaky tests (pytest plugin) Jan 14, 2019 5 - Production/Stable pytest (>=3.7) +`pytest-image-diff `_ Sep 03, 2020 3 - Alpha pytest +`pytest-incremental `_ an incremental test runner (pytest plugin) Dec 09, 2018 4 - Beta N/A +`pytest-influxdb `_ Plugin for influxdb and pytest integration. Sep 22, 2020 N/A N/A +`pytest-info-collector `_ pytest plugin to collect information from tests May 26, 2019 3 - Alpha N/A +`pytest-informative-node `_ display more node ininformation. Apr 25, 2019 4 - Beta N/A +`pytest-infrastructure `_ pytest stack validation prior to testing executing Apr 12, 2020 4 - Beta N/A +`pytest-inmanta `_ A py.test plugin providing fixtures to simplify inmanta modules testing. Oct 12, 2020 5 - Production/Stable N/A +`pytest-inmanta-extensions `_ Inmanta tests package Nov 25, 2020 5 - Production/Stable N/A +`pytest-Inomaly `_ A simple image diff plugin for pytest Feb 13, 2018 4 - Beta N/A +`pytest-insta `_ A practical snapshot testing plugin for pytest Nov 29, 2020 N/A pytest (>=6.0.2,<7.0.0) +`pytest-instafail `_ pytest plugin to show failures instantly Jun 14, 2020 4 - Beta pytest (>=2.9) +`pytest-instrument `_ pytest plugin to instrument tests Apr 05, 2020 5 - Production/Stable pytest (>=5.1.0) +`pytest-integration `_ Organizing pytests by integration or not Apr 16, 2020 N/A N/A +`pytest-interactive `_ A pytest plugin for console based interactive test selection just after the collection phase Nov 30, 2017 3 - Alpha N/A +`pytest-invenio `_ Pytest fixtures for Invenio. Dec 17, 2020 5 - Production/Stable pytest (<7,>=6) +`pytest-involve `_ Run tests covering a specific file or changeset Feb 02, 2020 4 - Beta pytest (>=3.5.0) +`pytest-ipdb `_ A py.test plug-in to enable drop to ipdb debugger on test failure. Sep 02, 2014 2 - Pre-Alpha N/A +`pytest-ipynb `_ THIS PROJECT IS ABANDONED Jan 29, 2019 3 - Alpha N/A +`pytest-isort `_ py.test plugin to check import ordering using isort Jan 13, 2021 5 - Production/Stable N/A +`pytest-it `_ Pytest plugin to display test reports as a plaintext spec, inspired by Rspec: https://github.com/mattduck/pytest-it. Jan 22, 2020 4 - Beta N/A +`pytest-iterassert `_ Nicer list and iterable assertion messages for pytest May 11, 2020 3 - Alpha N/A +`pytest-jasmine `_ Run jasmine tests from your pytest test suite Nov 04, 2017 1 - Planning N/A +`pytest-jest `_ A custom jest-pytest oriented Pytest reporter May 22, 2018 4 - Beta pytest (>=3.3.2) +`pytest-jira `_ py.test JIRA integration plugin, using markers Nov 29, 2019 N/A N/A +`pytest-jobserver `_ Limit parallel tests with posix jobserver. May 15, 2019 5 - Production/Stable pytest +`pytest-joke `_ Test failures are better served with humor. Oct 08, 2019 4 - Beta pytest (>=4.2.1) +`pytest-json `_ Generate JSON test reports Jan 18, 2016 4 - Beta N/A +`pytest-jsonlint `_ UNKNOWN Aug 04, 2016 N/A N/A +`pytest-json-report `_ A pytest plugin to report test results as JSON files Oct 23, 2020 4 - Beta pytest (>=4.2.0) +`pytest-kafka `_ Zookeeper, Kafka server, and Kafka consumer fixtures for Pytest Nov 01, 2019 N/A pytest +`pytest-kind `_ Kubernetes test support with KIND for pytest Jan 24, 2021 5 - Production/Stable N/A +`pytest-kivy `_ Kivy GUI tests fixtures using pytest Dec 21, 2020 4 - Beta pytest (>=3.6) +`pytest-knows `_ A pytest plugin that can automaticly skip test case based on dependence info calculated by trace Aug 22, 2014 N/A N/A +`pytest-konira `_ Run Konira DSL tests with py.test Oct 09, 2011 N/A N/A +`pytest-krtech-common `_ pytest krtech common library Nov 28, 2016 4 - Beta N/A +`pytest-kwparametrize `_ Alternate syntax for @pytest.mark.parametrize with test cases as dictionaries and default value fallbacks Jan 22, 2021 N/A pytest (>=6) +`pytest-lambda `_ Define pytest fixtures with lambda functions. Dec 28, 2020 3 - Alpha pytest (>=3.6,<7) +`pytest-lamp `_ Jan 06, 2017 3 - Alpha N/A +`pytest-layab `_ Pytest fixtures for layab. Oct 05, 2020 5 - Production/Stable N/A +`pytest-lazy-fixture `_ It helps to use fixtures in pytest.mark.parametrize Feb 01, 2020 4 - Beta pytest (>=3.2.5) +`pytest-ldap `_ python-ldap fixtures for pytest Aug 18, 2020 N/A pytest +`pytest-leaks `_ A pytest plugin to trace resource leaks. Nov 27, 2019 1 - Planning N/A +`pytest-level `_ Select tests of a given level or lower Oct 21, 2019 N/A pytest +`pytest-libfaketime `_ A python-libfaketime plugin for pytest. Dec 22, 2018 4 - Beta pytest (>=3.0.0) +`pytest-libiio `_ A pytest plugin to manage interfacing with libiio contexts Jan 09, 2021 4 - Beta N/A +`pytest-libnotify `_ Pytest plugin that shows notifications about the test run Nov 12, 2018 3 - Alpha pytest +`pytest-ligo `_ Jan 16, 2020 4 - Beta N/A +`pytest-lineno `_ A pytest plugin to show the line numbers of test functions Dec 04, 2020 N/A pytest +`pytest-lisa `_ Pytest plugin for organizing tests. Jan 21, 2021 3 - Alpha pytest (>=6.1.2,<7.0.0) +`pytest-listener `_ A simple network listener May 28, 2019 5 - Production/Stable pytest +`pytest-litf `_ A pytest plugin that stream output in LITF format Jan 18, 2021 4 - Beta pytest (>=3.1.1) +`pytest-live `_ Live results for pytest Mar 08, 2020 N/A pytest +`pytest-localftpserver `_ A PyTest plugin which provides an FTP fixture for your tests Jan 27, 2021 5 - Production/Stable pytest +`pytest-localserver `_ py.test plugin to test server connections locally. Nov 14, 2018 4 - Beta N/A +`pytest-localstack `_ Pytest plugin for AWS integration tests Aug 22, 2019 4 - Beta pytest (>=3.3.0) +`pytest-lockable `_ lockable resource plugin for pytest Oct 05, 2020 3 - Alpha pytest +`pytest-locker `_ Used to lock object during testing. Essentially changing assertions from being hard coded to asserting that nothing changed Aug 11, 2020 N/A pytest (>=5.4) +`pytest-logbook `_ py.test plugin to capture logbook log messages Nov 23, 2015 5 - Production/Stable pytest (>=2.8) +`pytest-logfest `_ Pytest plugin providing three logger fixtures with basic or full writing to log files Jul 21, 2019 4 - Beta pytest (>=3.5.0) +`pytest-logger `_ Plugin configuring handlers for loggers from Python logging module. Jul 25, 2019 4 - Beta pytest (>=3.2) +`pytest-logging `_ Configures logging and allows tweaking the log level with a py.test flag Nov 04, 2015 4 - Beta N/A +`pytest-log-report `_ Package for creating a pytest test run reprot Dec 26, 2019 N/A N/A +`pytest-manual-marker `_ pytest marker for marking manual tests Nov 28, 2018 3 - Alpha pytest +`pytest-markdown `_ Test your markdown docs with pytest Jan 15, 2021 4 - Beta pytest (>=6.0.1,<7.0.0) +`pytest-marker-bugzilla `_ py.test bugzilla integration plugin, using markers Jan 09, 2020 N/A N/A +`pytest-markers-presence `_ A simple plugin to detect missed pytest tags and markers" Dec 21, 2020 4 - Beta pytest (>=6.0) +`pytest-markfiltration `_ UNKNOWN Nov 08, 2011 3 - Alpha N/A +`pytest-mark-no-py3 `_ pytest plugin and bowler codemod to help migrate tests to Python 3 May 17, 2019 N/A pytest +`pytest-marks `_ UNKNOWN Nov 23, 2012 3 - Alpha N/A +`pytest-matcher `_ Match test output against patterns stored in files Apr 23, 2020 5 - Production/Stable pytest (>=3.4) +`pytest-match-skip `_ Skip matching marks. Matches partial marks using wildcards. May 15, 2019 4 - Beta pytest (>=4.4.1) +`pytest-mat-report `_ this is report Jan 20, 2021 N/A N/A +`pytest-matrix `_ Provide tools for generating tests from combinations of fixtures. Jun 24, 2020 5 - Production/Stable pytest (>=5.4.3,<6.0.0) +`pytest-mccabe `_ pytest plugin to run the mccabe code complexity checker. Jul 22, 2020 3 - Alpha pytest (>=5.4.0) +`pytest-md `_ Plugin for generating Markdown reports for pytest results Jul 11, 2019 3 - Alpha pytest (>=4.2.1) +`pytest-md-report `_ A pytest plugin to make a test results report with Markdown table format. Aug 14, 2020 4 - Beta pytest (!=6.0.0,<7,>=3.3.2) +`pytest-memprof `_ Estimates memory consumption of test functions Mar 29, 2019 4 - Beta N/A +`pytest-menu `_ A pytest plugin for console based interactive test selection just after the collection phase Oct 04, 2017 3 - Alpha pytest (>=2.4.2) +`pytest-mercurial `_ pytest plugin to write integration tests for projects using Mercurial Python internals Nov 21, 2020 1 - Planning N/A +`pytest-messenger `_ Pytest to Slack reporting plugin Dec 16, 2020 5 - Production/Stable N/A +`pytest-metadata `_ pytest plugin for test session metadata Nov 27, 2020 5 - Production/Stable pytest (>=2.9.0) +`pytest-metrics `_ Custom metrics report for pytest Apr 04, 2020 N/A pytest +`pytest-mimesis `_ Mimesis integration with the pytest test runner Mar 21, 2020 5 - Production/Stable pytest (>=4.2) +`pytest-minecraft `_ A pytest plugin for running tests against Minecraft releases Sep 26, 2020 N/A pytest (>=6.0.1,<7.0.0) +`pytest-missing-fixtures `_ Pytest plugin that creates missing fixtures Oct 14, 2020 4 - Beta pytest (>=3.5.0) +`pytest-ml `_ Test your machine learning! May 04, 2019 4 - Beta N/A +`pytest-mocha `_ pytest plugin to display test execution output like a mochajs Apr 02, 2020 4 - Beta pytest (>=5.4.0) +`pytest-mock `_ Thin-wrapper around the mock package for easier use with pytest Jan 10, 2021 5 - Production/Stable pytest (>=5.0) +`pytest-mock-api `_ A mock API server with configurable routes and responses available as a fixture. Feb 13, 2019 1 - Planning pytest (>=4.0.0) +`pytest-mock-helper `_ Help you mock HTTP call and generate mock code Jan 24, 2018 N/A pytest +`pytest-mockito `_ Base fixtures for mockito Jul 11, 2018 4 - Beta N/A +`pytest-mockredis `_ An in-memory mock of a Redis server that runs in a separate thread. This is to be used for unit-tests that require a Redis database. Jan 02, 2018 2 - Pre-Alpha N/A +`pytest-mock-resources `_ A pytest plugin for easily instantiating reproducible mock resources. Oct 08, 2020 N/A pytest (>=1.0) +`pytest-mock-server `_ Mock server plugin for pytest Apr 06, 2020 4 - Beta N/A +`pytest-mockservers `_ A set of fixtures to test your requests to HTTP/UDP servers Mar 31, 2020 N/A pytest (>=4.3.0) +`pytest-modifyjunit `_ Utility for adding additional properties to junit xml for IDM QE Jan 10, 2019 N/A N/A +`pytest-modifyscope `_ pytest plugin to modify fixture scope Apr 12, 2020 N/A pytest +`pytest-molecule `_ PyTest Molecule Plugin :: discover and run molecule tests Jan 25, 2021 5 - Production/Stable N/A +`pytest-mongo `_ MongoDB process and client fixtures plugin for py.test. Jan 12, 2021 5 - Production/Stable pytest (>=3.0.0) +`pytest-mongodb `_ pytest plugin for MongoDB fixtures Dec 07, 2019 5 - Production/Stable pytest (>=2.5.2) +`pytest-monitor `_ Pytest plugin for analyzing resource usage. Nov 20, 2020 5 - Production/Stable pytest +`pytest-monkeyplus `_ pytest's monkeypatch subclass with extra functionalities Sep 18, 2012 5 - Production/Stable N/A +`pytest-monkeytype `_ pytest-monkeytype: Generate Monkeytype annotations from your pytest tests. Jul 29, 2020 4 - Beta N/A +`pytest-moto `_ Fixtures for integration tests of AWS services,uses moto mocking library. Aug 28, 2015 1 - Planning N/A +`pytest-mp `_ A test batcher for multiprocessed Pytest runs May 23, 2018 4 - Beta pytest +`pytest-mpi `_ pytest plugin to collect information from tests Jun 20, 2020 3 - Alpha N/A +`pytest-mpl `_ pytest plugin to help with testing figures output from Matplotlib Nov 05, 2020 4 - Beta pytest +`pytest-mproc `_ low-startup-overhead, scalable, distributed-testing pytest plugin Aug 08, 2020 4 - Beta N/A +`pytest-multihost `_ Utility for writing multi-host tests for pytest Apr 07, 2020 4 - Beta N/A +`pytest-multilog `_ Multi-process logs handling and other helpers for pytest Nov 15, 2020 N/A N/A +`pytest-mutagen `_ Add the mutation testing feature to pytest Jul 24, 2020 N/A pytest (>=5.4) +`pytest-mypy `_ Mypy static type checker plugin for Pytest Nov 14, 2020 4 - Beta pytest (>=3.5) +`pytest-mypyd `_ Mypy static type checker plugin for Pytest Aug 20, 2019 4 - Beta pytest (<4.7,>=2.8) ; python_version < "3.5" +`pytest-mypy-plugins `_ pytest plugin for writing tests for mypy plugins Oct 26, 2020 3 - Alpha pytest (>=6.0.0) +`pytest-mypy-testing `_ Pytest plugin to check mypy output. Apr 24, 2020 N/A pytest +`pytest-mysql `_ MySQL process and client fixtures for pytest Jul 21, 2020 5 - Production/Stable pytest (>=3.0.0) +`pytest-needle `_ pytest plugin for visual testing websites using selenium Dec 10, 2018 4 - Beta pytest (<5.0.0,>=3.0.0) +`pytest-neo `_ pytest-neo is a plugin for pytest that shows tests like screen of Matrix. Apr 23, 2019 3 - Alpha pytest (>=3.7.2) +`pytest-network `_ A simple plugin to disable network on socket level. May 07, 2020 N/A N/A +`pytest-nginx `_ nginx fixture for pytest Aug 12, 2017 5 - Production/Stable N/A +`pytest-nginx-iplweb `_ nginx fixture for pytest - iplweb temporary fork Mar 01, 2019 5 - Production/Stable N/A +`pytest-ngrok `_ Jan 22, 2020 3 - Alpha N/A +`pytest-ngsfixtures `_ pytest ngs fixtures Sep 06, 2019 2 - Pre-Alpha pytest (>=5.0.0) +`pytest-nice `_ A pytest plugin that alerts user of failed test cases with screen notifications May 04, 2019 4 - Beta pytest +`pytest-nocustom `_ Run all tests without custom markers May 04, 2019 5 - Production/Stable N/A +`pytest-nodev `_ Test-driven source code search for Python. Jul 21, 2016 4 - Beta pytest (>=2.8.1) +`pytest-notebook `_ A pytest plugin for testing Jupyter Notebooks Sep 16, 2020 4 - Beta pytest (>=3.5.0) +`pytest-notice `_ Send pytest execution result email Nov 05, 2020 N/A N/A +`pytest-notification `_ A pytest plugin for sending a desktop notification and playing a sound upon completion of tests Jun 19, 2020 N/A pytest (>=4) +`pytest-notifier `_ A pytest plugin to notify test result Jun 12, 2020 3 - Alpha pytest +`pytest-notimplemented `_ Pytest markers for not implemented features and tests. Aug 27, 2019 N/A pytest (>=5.1,<6.0) +`pytest-notion `_ A PyTest Reporter to send test runs to Notion.so Aug 07, 2019 N/A N/A +`pytest-nunit `_ A pytest plugin for generating NUnit3 test result XML output Aug 04, 2020 4 - Beta pytest (>=3.5.0) +`pytest-ochrus `_ pytest results data-base and HTML reporter Feb 21, 2018 4 - Beta N/A +`pytest-odoo `_ py.test plugin to run Odoo tests Aug 19, 2020 4 - Beta pytest (>=2.9) +`pytest-odoo-fixtures `_ Project description Jun 25, 2019 N/A N/A +`pytest-oerp `_ pytest plugin to test OpenERP modules Feb 28, 2012 3 - Alpha N/A +`pytest-ok `_ The ultimate pytest output plugin Apr 01, 2019 4 - Beta N/A +`pytest-only `_ Use @pytest.mark.only to run a single test Jan 19, 2020 N/A N/A +`pytest-oot `_ Run object-oriented tests in a simple format Sep 18, 2016 4 - Beta N/A +`pytest-openfiles `_ Pytest plugin for detecting inadvertent open file handles Apr 16, 2020 3 - Alpha pytest (>=4.6) +`pytest-opentmi `_ pytest plugin for publish results to opentmi Jun 10, 2020 5 - Production/Stable pytest (>=5.0) +`pytest-optional `_ include/exclude values of fixtures in pytest Oct 07, 2015 N/A N/A +`pytest-optional-tests `_ Easy declaration of optional tests (i.e., that are not run by default) Jul 09, 2019 4 - Beta pytest (>=4.5.0) +`pytest-orchestration `_ A pytest plugin for orchestrating tests Jul 18, 2019 N/A N/A +`pytest-order `_ pytest plugin to run your tests in a specific order Jan 27, 2021 4 - Beta pytest (>=3.7) +`pytest-ordering `_ pytest plugin to run your tests in a specific order Nov 14, 2018 4 - Beta pytest +`pytest-osxnotify `_ OS X notifications for py.test results. May 15, 2015 N/A N/A +`pytest-pact `_ A simple plugin to use with pytest Jan 07, 2019 4 - Beta N/A +`pytest-parallel `_ a pytest plugin for parallel and concurrent testing Apr 30, 2020 3 - Alpha pytest (>=3.0.0) +`pytest-param `_ pytest plugin to test all, first, last or random params Sep 11, 2016 4 - Beta pytest (>=2.6.0) +`pytest-paramark `_ Configure pytest fixtures using a combination of"parametrize" and markers Jan 10, 2020 4 - Beta pytest (>=4.5.0) +`pytest-parametrization `_ Simpler PyTest parametrization Jul 28, 2019 5 - Production/Stable N/A +`pytest-parametrize-cases `_ A more user-friendly way to write parametrized tests. Dec 12, 2020 N/A pytest (>=6.1.2,<7.0.0) +`pytest-parametrized `_ Pytest plugin for parametrizing tests with default iterables. Oct 19, 2020 5 - Production/Stable pytest +`pytest-parawtf `_ Finally spell paramete?ri[sz]e correctly Dec 03, 2018 4 - Beta pytest (>=3.6.0) +`pytest-pass `_ Check out https://github.com/elilutsky/pytest-pass Dec 04, 2019 N/A N/A +`pytest-paste-config `_ Allow setting the path to a paste config file Sep 18, 2013 3 - Alpha N/A +`pytest-pdb `_ pytest plugin which adds pdb helper commands related to pytest. Jul 31, 2018 N/A N/A +`pytest-peach `_ pytest plugin for fuzzing with Peach API Security Apr 12, 2019 4 - Beta pytest (>=2.8.7) +`pytest-pep257 `_ py.test plugin for pep257 Jul 09, 2016 N/A N/A +`pytest-pep8 `_ pytest plugin to check PEP8 requirements Apr 27, 2014 N/A N/A +`pytest-percent `_ Change the exit code of pytest test sessions when a required percent of tests pass. May 21, 2020 N/A pytest (>=5.2.0) +`pytest-performance `_ A simple plugin to ensure the execution of critical sections of code has not been impacted Sep 11, 2020 5 - Production/Stable pytest (>=3.7.0) +`pytest-pgsql `_ Pytest plugins and helpers for tests using a Postgres database. May 13, 2020 5 - Production/Stable pytest (>=3.0.0) +`pytest-picked `_ Run the tests related to the changed files Dec 23, 2020 N/A pytest (>=3.5.0) +`pytest-pigeonhole `_ Jun 25, 2018 5 - Production/Stable pytest (>=3.4) +`pytest-pikachu `_ Show surprise when tests are passing Sep 30, 2019 4 - Beta pytest +`pytest-pilot `_ Slice in your test base thanks to powerful markers. Oct 09, 2020 5 - Production/Stable N/A +`pytest-pings `_ 🦊 The pytest plugin for Firefox Telemetry 📊 Jun 29, 2019 3 - Alpha pytest (>=5.0.0) +`pytest-pinned `_ A simple pytest plugin for pinning tests Jan 21, 2021 4 - Beta pytest (>=3.5.0) +`pytest-pinpoint `_ A pytest plugin which runs SBFL algorithms to detect faults. Sep 25, 2020 N/A pytest (>=4.4.0) +`pytest-pipeline `_ Pytest plugin for functional testing of data analysispipelines Jan 24, 2017 3 - Alpha N/A +`pytest-platform-markers `_ Markers for pytest to skip tests on specific platforms Sep 09, 2019 4 - Beta pytest (>=3.6.0) +`pytest-play `_ pytest plugin that let you automate actions and assertions with test metrics reporting executing plain YAML files Jun 12, 2019 5 - Production/Stable N/A +`pytest-playbook `_ Pytest plugin for reading playbooks. Jan 21, 2021 3 - Alpha pytest (>=6.1.2,<7.0.0) +`pytest-playwright `_ A pytest wrapper with fixtures for Playwright to automate web browsers Jan 21, 2021 N/A pytest +`pytest-plt `_ Fixtures for quickly making Matplotlib plots in tests Aug 17, 2020 5 - Production/Stable pytest +`pytest-plugin-helpers `_ A plugin to help developing and testing other plugins Nov 23, 2019 4 - Beta pytest (>=3.5.0) +`pytest-plus `_ PyTest Plus Plugin :: extends pytest functionality Mar 19, 2020 5 - Production/Stable pytest (>=3.50) +`pytest-pmisc `_ Mar 21, 2019 5 - Production/Stable N/A +`pytest-pointers `_ Pytest plugin to define functions you test with special marks for better navigation and reports Dec 14, 2020 N/A N/A +`pytest-polarion-cfme `_ pytest plugin for collecting test cases and recording test results Nov 13, 2017 3 - Alpha N/A +`pytest-polarion-collect `_ pytest plugin for collecting polarion test cases data Jun 18, 2020 3 - Alpha pytest +`pytest-polecat `_ Provides Polecat pytest fixtures Aug 12, 2019 4 - Beta N/A +`pytest-ponyorm `_ PonyORM in Pytest Oct 31, 2018 N/A pytest (>=3.1.1) +`pytest-poo `_ Visualize your crappy tests Jul 14, 2013 5 - Production/Stable N/A +`pytest-poo-fail `_ Visualize your failed tests with poo Feb 12, 2015 5 - Production/Stable N/A +`pytest-pop `_ A pytest plugin to help with testing pop projects Aug 13, 2020 5 - Production/Stable pytest (>=5.4.0) +`pytest-postgres `_ Run PostgreSQL in Docker container in Pytest. Mar 22, 2020 N/A pytest +`pytest-postgresql `_ Postgresql fixtures and fixture factories for Pytest. Oct 29, 2020 5 - Production/Stable pytest (>=3.0.0) +`pytest-power `_ pytest plugin with powerful fixtures Dec 31, 2020 N/A pytest (>=5.4) +`pytest-pride `_ Minitest-style test colors Apr 02, 2016 3 - Alpha N/A +`pytest-print `_ pytest-print adds the printer fixture you can use to print messages to the user (directly to the pytest runner, not stdout) Oct 23, 2020 5 - Production/Stable pytest (>=3.0.0) +`pytest-profiling `_ Profiling plugin for py.test May 28, 2019 5 - Production/Stable pytest +`pytest-progress `_ pytest plugin for instant test progress status Oct 06, 2020 5 - Production/Stable N/A +`pytest-prometheus `_ Report test pass / failures to a Prometheus PushGateway Oct 03, 2017 N/A N/A +`pytest-prosper `_ Test helpers for Prosper projects Sep 24, 2018 N/A N/A +`pytest-pspec `_ A rspec format reporter for Python ptest Jun 02, 2020 4 - Beta pytest (>=3.0.0) +`pytest-pudb `_ Pytest PuDB debugger integration Oct 25, 2018 3 - Alpha pytest (>=2.0) +`pytest-purkinje `_ py.test plugin for purkinje test runner Oct 28, 2017 2 - Pre-Alpha N/A +`pytest-pycharm `_ Plugin for py.test to enter PyCharm debugger on uncaught exceptions Aug 13, 2020 5 - Production/Stable pytest (>=2.3) +`pytest-pycodestyle `_ pytest plugin to run pycodestyle Aug 10, 2020 3 - Alpha N/A +`pytest-pydev `_ py.test plugin to connect to a remote debug server with PyDev or PyCharm. Nov 15, 2017 3 - Alpha N/A +`pytest-pydocstyle `_ pytest plugin to run pydocstyle Aug 10, 2020 3 - Alpha N/A +`pytest-pylint `_ pytest plugin to check source code with pylint Nov 09, 2020 5 - Production/Stable pytest (>=5.4) +`pytest-pypi `_ Easily test your HTTP library against a local copy of pypi Mar 04, 2018 3 - Alpha N/A +`pytest-pypom-navigation `_ Core engine for cookiecutter-qa and pytest-play packages Feb 18, 2019 4 - Beta pytest (>=3.0.7) +`pytest-pyppeteer `_ A plugin to run pyppeteer in pytest. Nov 27, 2020 4 - Beta pytest (>=6.0.2) +`pytest-pyq `_ Pytest fixture "q" for pyq Mar 10, 2020 5 - Production/Stable N/A +`pytest-pyramid `_ pytest pyramid providing basic fixtures for testing pyramid applications with pytest test suite Jun 05, 2020 4 - Beta pytest +`pytest-pyramid-server `_ Pyramid server fixture for py.test May 28, 2019 5 - Production/Stable pytest +`pytest-pytestrail `_ Pytest plugin for interaction with TestRail Aug 27, 2020 4 - Beta pytest (>=3.8.0) +`pytest-pythonpath `_ pytest plugin for adding to the PYTHONPATH from command line or configs. Aug 22, 2018 5 - Production/Stable N/A +`pytest-qml `_ Run QML Tests with pytest Dec 02, 2020 4 - Beta pytest (>=6.0.0) +`pytest-qt `_ pytest support for PyQt and PySide applications Dec 07, 2019 5 - Production/Stable pytest (>=3.0.0) +`pytest-qt-app `_ QT app fixture for py.test Dec 23, 2015 5 - Production/Stable N/A +`pytest-quarantine `_ A plugin for pytest to manage expected test failures Nov 24, 2019 5 - Production/Stable pytest (>=4.6) +`pytest-quickcheck `_ pytest plugin to generate random data inspired by QuickCheck Nov 15, 2020 4 - Beta pytest (<6.0.0,>=4.0) +`pytest-rabbitmq `_ RabbitMQ process and client fixtures for pytest Jan 11, 2021 5 - Production/Stable pytest (>=3.0.0) +`pytest-race `_ Race conditions tester for pytest Nov 21, 2016 4 - Beta N/A +`pytest-rage `_ pytest plugin to implement PEP712 Oct 21, 2011 3 - Alpha N/A +`pytest-raises `_ An implementation of pytest.raises as a pytest.mark fixture Apr 23, 2020 N/A pytest (>=3.2.2) +`pytest-raisesregexp `_ Simple pytest plugin to look for regex in Exceptions Dec 18, 2015 N/A N/A +`pytest-raisin `_ Plugin enabling the use of exception instances with pytest.raises Jun 25, 2020 N/A pytest +`pytest-random `_ py.test plugin to randomize tests Apr 28, 2013 3 - Alpha N/A +`pytest-randomly `_ Pytest plugin to randomly order tests and control random.seed. Nov 16, 2020 5 - Production/Stable pytest +`pytest-randomness `_ Pytest plugin about random seed management May 30, 2019 3 - Alpha N/A +`pytest-random-num `_ Randomise the order in which pytest tests are run with some control over the randomness Oct 19, 2020 5 - Production/Stable N/A +`pytest-random-order `_ Randomise the order in which pytest tests are run with some control over the randomness Nov 30, 2018 5 - Production/Stable pytest (>=3.0.0) +`pytest-readme `_ Test your README.md file Dec 28, 2014 5 - Production/Stable N/A +`pytest-reana `_ Pytest fixtures for REANA. Nov 24, 2020 3 - Alpha N/A +`pytest-recording `_ A pytest plugin that allows you recording of network interactions via VCR.py Nov 25, 2020 4 - Beta pytest (>=3.5.0) +`pytest-recordings `_ Provides pytest plugins for reporting request/response traffic, screenshots, and more to ReportPortal Aug 13, 2020 N/A N/A +`pytest-redis `_ Redis fixtures and fixture factories for Pytest. Oct 15, 2019 5 - Production/Stable pytest (>=3.0.0) +`pytest-redmine `_ Pytest plugin for redmine Mar 19, 2018 1 - Planning N/A +`pytest-ref `_ A plugin to store reference files to ease regression testing Nov 23, 2019 4 - Beta pytest (>=3.5.0) +`pytest-reference-formatter `_ Conveniently run pytest with a dot-formatted test reference. Oct 01, 2019 4 - Beta N/A +`pytest-regressions `_ Easy to use fixtures to write regression tests. Jan 27, 2021 5 - Production/Stable pytest (>=3.5.0) +`pytest-regtest `_ pytest plugin for regression tests Sep 16, 2020 N/A N/A +`pytest-relaxed `_ Relaxed test discovery/organization for pytest Jun 14, 2019 5 - Production/Stable pytest (<5,>=3) +`pytest-remfiles `_ Pytest plugin to create a temporary directory with remote files Jul 01, 2019 5 - Production/Stable N/A +`pytest-remotedata `_ Pytest plugin for controlling remote data access. Jul 20, 2019 3 - Alpha pytest (>=3.1) +`pytest-remove-stale-bytecode `_ py.test plugin to remove stale byte code files. Mar 04, 2020 4 - Beta pytest +`pytest-reorder `_ Reorder tests depending on their paths and names. May 31, 2018 4 - Beta pytest +`pytest-repeat `_ pytest plugin for repeating tests Oct 31, 2020 5 - Production/Stable pytest (>=3.6) +`pytest-replay `_ Saves previous test runs and allow re-execute previous pytest runs to reproduce crashes or flaky tests Dec 09, 2020 4 - Beta pytest (>=3.0.0) +`pytest-repo-health `_ A pytest plugin to report on repository standards conformance Nov 03, 2020 3 - Alpha pytest +`pytest-report `_ Creates json report that is compatible with atom.io's linter message format May 11, 2016 4 - Beta N/A +`pytest-reporter `_ Generate Pytest reports with templates Nov 05, 2020 4 - Beta pytest +`pytest-reporter-html1 `_ A basic HTML report template for Pytest Nov 02, 2020 4 - Beta N/A +`pytest-reportinfra `_ Pytest plugin for reportinfra Aug 11, 2019 3 - Alpha N/A +`pytest-reporting `_ A plugin to report summarized results in a table format Oct 25, 2019 4 - Beta pytest (>=3.5.0) +`pytest-reportlog `_ Replacement for the --resultlog option, focused in simplicity and extensibility Dec 11, 2020 3 - Alpha pytest (>=5.2) +`pytest-report-me `_ A pytest plugin to generate report. Dec 31, 2020 N/A pytest +`pytest-report-parameters `_ pytest plugin for adding tests' parameters to junit report Jun 18, 2020 3 - Alpha pytest (>=2.4.2) +`pytest-reportportal `_ Agent for Reporting results of tests to the Report Portal Dec 14, 2020 N/A pytest (>=3.0.7) +`pytest-reqs `_ pytest plugin to check pinned requirements May 12, 2019 N/A pytest (>=2.4.2) +`pytest-requests `_ A simple plugin to use with pytest Jun 24, 2019 4 - Beta pytest (>=3.5.0) +`pytest-reraise `_ Make multi-threaded pytest test cases fail when they should Jun 03, 2020 5 - Production/Stable N/A +`pytest-rerun `_ Re-run only changed files in specified branch Jul 08, 2019 N/A pytest (>=3.6) +`pytest-rerunfailures `_ pytest plugin to re-run tests to eliminate flaky failures Sep 29, 2020 5 - Production/Stable pytest (>=5.0) +`pytest-resilient-circuits `_ Resilient Circuits fixtures for PyTest. Jan 21, 2021 N/A N/A +`pytest-resource `_ Load resource fixture plugin to use with pytest Nov 14, 2018 4 - Beta N/A +`pytest-resource-path `_ Provides path for uniform access to test resources in isolated directory Aug 18, 2020 5 - Production/Stable pytest (>=3.5.0) +`pytest-responsemock `_ Simplified requests calls mocking for pytest Oct 10, 2020 5 - Production/Stable N/A +`pytest-responses `_ py.test integration for responses Jan 29, 2019 N/A N/A +`pytest-restrict `_ Pytest plugin to restrict the test types allowed Dec 03, 2020 5 - Production/Stable pytest +`pytest-rethinkdb `_ A RethinkDB plugin for pytest. Jul 24, 2016 4 - Beta N/A +`pytest-reverse `_ Pytest plugin to reverse test order. Dec 27, 2020 5 - Production/Stable pytest +`pytest-ringo `_ pytest plugin to test webapplications using the Ringo webframework Sep 27, 2017 3 - Alpha N/A +`pytest-rng `_ Fixtures for seeding tests and making randomness reproducible Aug 08, 2019 5 - Production/Stable pytest +`pytest-roast `_ pytest plugin for ROAST configuration override and fixtures Jan 14, 2021 5 - Production/Stable pytest (<6) +`pytest-rotest `_ Pytest integration with rotest Sep 08, 2019 N/A pytest (>=3.5.0) +`pytest-rpc `_ Extend py.test for RPC OpenStack testing. Feb 22, 2019 4 - Beta pytest (~=3.6) +`pytest-rt `_ pytest data collector plugin for Testgr Jan 24, 2021 N/A N/A +`pytest-rts `_ Coverage-based regression test selection (RTS) plugin for pytest Dec 21, 2020 N/A pytest +`pytest-runfailed `_ implement a --failed option for pytest Mar 24, 2016 N/A N/A +`pytest-runner `_ Invoke py.test as distutils command with dependency resolution Oct 26, 2019 5 - Production/Stable pytest (!=3.7.3,>=3.5) ; extra == 'testing' +`pytest-salt `_ Pytest Salt Plugin Jan 27, 2020 4 - Beta N/A +`pytest-salt-containers `_ A Pytest plugin that builds and creates docker containers Nov 09, 2016 4 - Beta N/A +`pytest-salt-factories `_ Pytest Salt Plugin Jan 19, 2021 4 - Beta pytest (>=6.1.1) +`pytest-salt-from-filenames `_ Simple PyTest Plugin For Salt's Test Suite Specifically Jan 29, 2019 4 - Beta pytest (>=4.1) +`pytest-salt-runtests-bridge `_ Simple PyTest Plugin For Salt's Test Suite Specifically Dec 05, 2019 4 - Beta pytest (>=4.1) +`pytest-sanic `_ a pytest plugin for Sanic Sep 24, 2020 N/A pytest (>=5.2) +`pytest-sanity `_ Dec 07, 2020 N/A N/A +`pytest-sa-pg `_ May 14, 2019 N/A N/A +`pytest-sbase `_ A complete web automation framework for end-to-end testing. Jan 27, 2021 5 - Production/Stable N/A +`pytest-scenario `_ pytest plugin for test scenarios Feb 06, 2017 3 - Alpha N/A +`pytest-schema `_ 👍 Validate return values against a schema-like object in testing Aug 31, 2020 5 - Production/Stable pytest (>=3.5.0) +`pytest-securestore `_ An encrypted password store for use within pytest cases Jun 19, 2019 4 - Beta N/A +`pytest-select `_ A pytest plugin which allows to (de-)select tests from a file. Jan 18, 2019 3 - Alpha pytest (>=3.0) +`pytest-selenium `_ pytest plugin for Selenium Sep 19, 2020 5 - Production/Stable pytest (>=5.0.0) +`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Jan 27, 2021 5 - Production/Stable N/A +`pytest-selenium-enhancer `_ pytest plugin for Selenium Nov 26, 2020 5 - Production/Stable N/A +`pytest-selenium-pdiff `_ A pytest package implementing perceptualdiff for Selenium tests. Apr 06, 2017 2 - Pre-Alpha N/A +`pytest-send-email `_ Send pytest execution result email Dec 04, 2019 N/A N/A +`pytest-sentry `_ A pytest plugin to send testrun information to Sentry.io Dec 16, 2020 N/A N/A +`pytest-server-fixtures `_ Extensible server fixures for py.test May 28, 2019 5 - Production/Stable pytest +`pytest-serverless `_ Automatically mocks resources from serverless.yml in pytest using moto. Dec 26, 2020 4 - Beta N/A +`pytest-services `_ Services plugin for pytest testing framework Oct 30, 2020 6 - Mature N/A +`pytest-session2file `_ pytest-session2file (aka: pytest-session_to_file for v0.1.0 - v0.1.2) is a py.test plugin for capturing and saving to file the stdout of py.test. Jan 26, 2021 3 - Alpha pytest +`pytest-session-fixture-globalize `_ py.test plugin to make session fixtures behave as if written in conftest, even if it is written in some modules May 15, 2018 4 - Beta N/A +`pytest-session_to_file `_ pytest-session_to_file is a py.test plugin for capturing and saving to file the stdout of py.test. Oct 01, 2015 3 - Alpha N/A +`pytest-sftpserver `_ py.test plugin to locally test sftp server connections. Sep 16, 2019 4 - Beta N/A +`pytest-shard `_ Dec 11, 2020 4 - Beta pytest +`pytest-shell `_ A pytest plugin for testing shell scripts and line-based processes Jan 18, 2020 N/A N/A +`pytest-sheraf `_ Versatile ZODB abstraction layer - pytest fixtures Feb 11, 2020 N/A pytest +`pytest-sherlock `_ pytest plugin help to find coupled tests Jul 13, 2020 5 - Production/Stable pytest (>=3.5.1) +`pytest-shortcuts `_ Expand command-line shortcuts listed in pytest configuration Oct 29, 2020 4 - Beta pytest (>=3.5.0) +`pytest-shutil `_ A goodie-bag of unix shell and environment tools for py.test May 28, 2019 5 - Production/Stable pytest +`pytest-simple-plugin `_ Simple pytest plugin Nov 27, 2019 N/A N/A +`pytest-simple-settings `_ simple-settings plugin for pytest Nov 17, 2020 4 - Beta pytest +`pytest-single-file-logging `_ Allow for multiple processes to log to a single file May 05, 2016 4 - Beta pytest (>=2.8.1) +`pytest-skipper `_ A plugin that selects only tests with changes in execution path Mar 26, 2017 3 - Alpha pytest (>=3.0.6) +`pytest-skippy `_ Automatically skip tests that don't need to run! Jan 27, 2018 3 - Alpha pytest (>=2.3.4) +`pytest-slack `_ Pytest to Slack reporting plugin Dec 15, 2020 5 - Production/Stable N/A +`pytest-smartcollect `_ A plugin for collecting tests that touch changed code Oct 04, 2018 N/A pytest (>=3.5.0) +`pytest-smartcov `_ Smart coverage plugin for pytest. Sep 30, 2017 3 - Alpha N/A +`pytest-snail `_ Plugin for adding a marker to slow running tests. 🐌 Nov 04, 2019 3 - Alpha pytest (>=5.0.1) +`pytest-snapci `_ py.test plugin for Snap-CI Nov 12, 2015 N/A N/A +`pytest-snapshot `_ A plugin to enable snapshot testing with pytest. Jan 22, 2021 4 - Beta pytest (>=3.0.0) +`pytest-snmpserver `_ Sep 14, 2020 N/A N/A +`pytest-socket `_ Pytest Plugin to disable socket calls during tests May 31, 2020 4 - Beta pytest (>=3.6.3) +`pytest-soft-assertions `_ May 05, 2020 3 - Alpha pytest +`pytest-solr `_ Solr process and client fixtures for py.test. May 11, 2020 3 - Alpha pytest (>=3.0.0) +`pytest-sorter `_ A simple plugin to first execute tests that historically failed more Jul 23, 2020 4 - Beta pytest (>=3.1.1) +`pytest-sourceorder `_ Test-ordering plugin for pytest Apr 11, 2017 4 - Beta pytest +`pytest-spark `_ pytest plugin to run the tests with support of pyspark. Feb 23, 2020 4 - Beta pytest +`pytest-spawner `_ py.test plugin to spawn process and communicate with them. Jul 31, 2015 4 - Beta N/A +`pytest-spec `_ Library pytest-spec is a pytest plugin to display test execution output like a SPECIFICATION. Jan 14, 2021 N/A N/A +`pytest-sphinx `_ Doctest plugin for pytest with support for Sphinx-specific doctest-directives Aug 05, 2020 4 - Beta N/A +`pytest-spiratest `_ Exports unit tests as test runs in SpiraTest/Team/Plan Oct 30, 2020 N/A N/A +`pytest-splinter `_ Splinter plugin for pytest testing framework Dec 25, 2020 6 - Mature N/A +`pytest-split `_ Pytest plugin for splitting test suite based on test execution time Apr 07, 2020 1 - Planning N/A +`pytest-splitio `_ Split.io SDK integration for e2e tests Sep 22, 2020 N/A pytest (<7,>=5.0) +`pytest-split-tests `_ A Pytest plugin for running a subset of your tests by splitting them in to equally sized groups. Forked from Mark Adams' original project pytest-test-groups. May 28, 2019 N/A pytest (>=2.5) +`pytest-splunk-addon `_ A Dynamic test tool for Splunk Apps and Add-ons Jan 05, 2021 N/A pytest (>5.4.0,<6.1) +`pytest-splunk-addon-ui-smartx `_ Library to support testing Splunk Add-on UX Jan 18, 2021 N/A N/A +`pytest-splunk-env `_ pytest fixtures for interaction with Splunk Enterprise and Splunk Cloud Oct 22, 2020 N/A pytest (>=6.1.1,<7.0.0) +`pytest-sqitch `_ sqitch for pytest Apr 06, 2020 4 - Beta N/A +`pytest-sqlalchemy `_ pytest plugin with sqlalchemy related fixtures Mar 13, 2018 3 - Alpha N/A +`pytest-sql-bigquery `_ Yet another SQL-testing framework for BigQuery provided by pytest plugin Dec 19, 2019 N/A pytest +`pytest-ssh `_ pytest plugin for ssh command run May 27, 2019 N/A pytest +`pytest-start-from `_ Start pytest run from a given point Apr 11, 2016 N/A N/A +`pytest-statsd `_ pytest plugin for reporting to graphite Nov 30, 2018 5 - Production/Stable pytest (>=3.0.0) +`pytest-stepfunctions `_ A small description Jul 07, 2020 4 - Beta pytest +`pytest-steps `_ Create step-wise / incremental tests in pytest. Apr 25, 2020 5 - Production/Stable N/A +`pytest-stepwise `_ Run a test suite one failing test at a time. Dec 01, 2015 4 - Beta N/A +`pytest-stoq `_ A plugin to pytest stoq Nov 04, 2020 4 - Beta N/A +`pytest-stress `_ A Pytest plugin that allows you to loop tests for a user defined amount of time. Dec 07, 2019 4 - Beta pytest (>=3.6.0) +`pytest-structlog `_ Structured logging assertions Jul 16, 2020 N/A pytest +`pytest-structmpd `_ provide structured temporary directory Oct 17, 2018 N/A N/A +`pytest-stub `_ Stub packages, modules and attributes. Apr 28, 2020 5 - Production/Stable N/A +`pytest-stubprocess `_ Provide stub implementations for subprocesses in Python tests Sep 17, 2018 3 - Alpha pytest (>=3.5.0) +`pytest-study `_ A pytest plugin to organize long run tests (named studies) without interfering the regular tests Sep 26, 2017 3 - Alpha pytest (>=2.0) +`pytest-subprocess `_ A plugin to fake subprocess for pytest Aug 22, 2020 5 - Production/Stable pytest (>=4.0.0) +`pytest-subtesthack `_ A hack to explicitly set up and tear down fixtures. Jan 31, 2016 N/A N/A +`pytest-subtests `_ unittest subTest() support and subtests fixture Dec 13, 2020 4 - Beta pytest (>=5.3.0) +`pytest-subunit `_ pytest-subunit is a plugin for py.test which outputs testsresult in subunit format. Aug 29, 2017 N/A N/A +`pytest-sugar `_ pytest-sugar is a plugin for pytest that changes the default look and feel of pytest (e.g. progressbar, show tests that fail instantly). Jul 06, 2020 3 - Alpha N/A +`pytest-sugar-bugfix159 `_ Workaround for https://github.com/Frozenball/pytest-sugar/issues/159 Nov 07, 2018 5 - Production/Stable pytest (!=3.7.3,>=3.5); extra == 'testing' +`pytest-super-check `_ Pytest plugin to check your TestCase classes call super in setUp, tearDown, etc. Dec 13, 2020 5 - Production/Stable pytest +`pytest-svn `_ SVN repository fixture for py.test May 28, 2019 5 - Production/Stable pytest +`pytest-symbols `_ pytest-symbols is a pytest plugin that adds support for passing test environment symbols into pytest tests. Nov 20, 2017 3 - Alpha N/A +`pytest-tap `_ Test Anything Protocol (TAP) reporting plugin for pytest Nov 07, 2020 5 - Production/Stable pytest (>=3.0) +`pytest-target `_ Pytest plugin for remote target orchestration. Jan 21, 2021 3 - Alpha pytest (>=6.1.2,<7.0.0) +`pytest-tblineinfo `_ tblineinfo is a py.test plugin that insert the node id in the final py.test report when --tb=line option is used Dec 01, 2015 3 - Alpha pytest (>=2.0) +`pytest-teamcity-logblock `_ py.test plugin to introduce block structure in teamcity build log, if output is not captured May 15, 2018 4 - Beta N/A +`pytest-telegram `_ Pytest to Telegram reporting plugin Dec 10, 2020 5 - Production/Stable N/A +`pytest-tempdir `_ Predictable and repeatable tempdir support. Oct 11, 2019 4 - Beta pytest (>=2.8.1) +`pytest-terraform `_ A pytest plugin for using terraform fixtures Oct 20, 2020 N/A pytest (>=6.0.0,<6.1.0) +`pytest-terraform-fixture `_ generate terraform resources to use with pytest Nov 14, 2018 4 - Beta N/A +`pytest-testbook `_ A plugin to run tests written in Jupyter notebook Dec 11, 2016 3 - Alpha N/A +`pytest-testconfig `_ Test configuration plugin for pytest. Jan 11, 2020 4 - Beta pytest (>=3.5.0) +`pytest-testdirectory `_ A py.test plugin providing temporary directories in unit tests. Nov 06, 2018 5 - Production/Stable pytest +`pytest-testdox `_ A testdox format reporter for pytest Oct 13, 2020 5 - Production/Stable pytest (>=3.7.0) +`pytest-test-groups `_ A Pytest plugin for running a subset of your tests by splitting them in to equally sized groups. Oct 25, 2016 5 - Production/Stable N/A +`pytest-testinfra `_ Test infrastructures Nov 12, 2020 5 - Production/Stable pytest (!=3.0.2) +`pytest-testlink-adaptor `_ pytest reporting plugin for testlink Dec 20, 2018 4 - Beta pytest (>=2.6) +`pytest-testmon `_ selects tests affected by changed files and methods Aug 05, 2020 4 - Beta N/A +`pytest-testobject `_ Plugin to use TestObject Suites with Pytest Sep 24, 2019 4 - Beta pytest (>=3.1.1) +`pytest-testrail `_ pytest plugin for creating TestRail runs and adding results Aug 27, 2020 N/A pytest (>=3.6) +`pytest-testrail2 `_ A small example package Nov 17, 2020 N/A pytest (>=5) +`pytest-testrail-api `_ Плагин Pytest, для интеграции с TestRail Dec 09, 2020 N/A pytest (>=5.5) +`pytest-testrail-client `_ pytest plugin for Testrail Sep 29, 2020 5 - Production/Stable N/A +`pytest-testrail-e2e `_ pytest plugin for creating TestRail runs and adding results Jun 11, 2020 N/A pytest (>=3.6) +`pytest-testrail-plugin `_ PyTest plugin for TestRail Apr 21, 2020 3 - Alpha pytest +`pytest-testrail-reporter `_ Sep 10, 2018 N/A N/A +`pytest-testslide `_ TestSlide fixture for pytest Jan 07, 2021 5 - Production/Stable pytest (~=6.2) +`pytest-test-this `_ Plugin for py.test to run relevant tests, based on naively checking if a test contains a reference to the symbol you supply Sep 15, 2019 2 - Pre-Alpha pytest (>=2.3) +`pytest-tesults `_ Tesults plugin for pytest May 18, 2020 5 - Production/Stable pytest (>=3.5.0) +`pytest-tezos `_ pytest-ligo Jan 16, 2020 4 - Beta N/A +`pytest-thawgun `_ Pytest plugin for time travel May 26, 2020 3 - Alpha N/A +`pytest-threadleak `_ Detects thread leaks Sep 08, 2017 4 - Beta N/A +`pytest-timeit `_ A pytest plugin to time test function runs Oct 13, 2016 4 - Beta N/A +`pytest-timeout `_ py.test plugin to abort hanging tests Jul 15, 2020 5 - Production/Stable pytest (>=3.6.0) +`pytest-timeouts `_ Linux-only Pytest plugin to control durations of various test case execution phases Sep 21, 2019 5 - Production/Stable N/A +`pytest-timer `_ A timer plugin for pytest Dec 13, 2020 N/A N/A +`pytest-tipsi-django `_ Oct 14, 2020 4 - Beta pytest (>=6.0.0) +`pytest-tipsi-testing `_ Better fixtures management. Various helpers Nov 04, 2020 4 - Beta pytest (>=3.3.0) +`pytest-tldr `_ A pytest plugin that limits the output to just the things you need. Aug 08, 2020 4 - Beta pytest (>=3.5.0) +`pytest-tm4j-reporter `_ Cloud Jira Test Management (TM4J) PyTest reporter plugin Sep 01, 2020 N/A pytest +`pytest-todo `_ A small plugin for the pytest testing framework, marking TODO comments as failure May 23, 2019 4 - Beta pytest +`pytest-tomato `_ Mar 01, 2019 5 - Production/Stable N/A +`pytest-toolbelt `_ This is just a collection of utilities for pytest, but don't really belong in pytest proper. Aug 12, 2019 3 - Alpha N/A +`pytest-toolbox `_ Numerous useful plugins for pytest. Apr 07, 2018 N/A pytest (>=3.5.0) +`pytest-tornado `_ A py.test plugin providing fixtures and markers to simplify testing of asynchronous tornado applications. Jun 17, 2020 5 - Production/Stable pytest (>=3.6) +`pytest-tornado5 `_ A py.test plugin providing fixtures and markers to simplify testing of asynchronous tornado applications. Nov 16, 2018 5 - Production/Stable pytest (>=3.6) +`pytest-tornado-yen3 `_ A py.test plugin providing fixtures and markers to simplify testing of asynchronous tornado applications. Oct 15, 2018 5 - Production/Stable N/A +`pytest-tornasync `_ py.test plugin for testing Python 3.5+ Tornado code Jul 15, 2019 3 - Alpha pytest (>=3.0) +`pytest-track `_ Oct 23, 2020 3 - Alpha pytest (>=3.0) +`pytest-translations `_ Test your translation files. Oct 26, 2020 5 - Production/Stable N/A +`pytest-travis-fold `_ Folds captured output sections in Travis CI build log Nov 29, 2017 4 - Beta pytest (>=2.6.0) +`pytest-trello `_ Plugin for py.test that integrates trello using markers Nov 20, 2015 5 - Production/Stable N/A +`pytest-trepan `_ Pytest plugin for trepan debugger. Jul 28, 2018 5 - Production/Stable N/A +`pytest-trialtemp `_ py.test plugin for using the same _trial_temp working directory as trial Jun 08, 2015 N/A N/A +`pytest-trio `_ Pytest plugin for trio Oct 16, 2020 N/A N/A +`pytest-tspwplib `_ A simple plugin to use with tspwplib Jan 08, 2021 4 - Beta pytest (>=3.5.0) +`pytest-tstcls `_ Test Class Base Mar 23, 2020 5 - Production/Stable N/A +`pytest-twisted `_ A twisted plugin for pytest. Sep 11, 2020 5 - Production/Stable pytest (>=2.3) +`pytest-typhoon-xray `_ Typhoon HIL plugin for pytest Jun 22, 2020 4 - Beta pytest (>=5.4.2) +`pytest-tytest `_ Typhoon HIL plugin for pytest May 25, 2020 4 - Beta pytest (>=5.4.2) +`pytest-ubersmith `_ Easily mock calls to ubersmith at the `requests` level. Apr 13, 2015 N/A N/A +`pytest-ui `_ Text User Interface for running python tests May 03, 2020 4 - Beta pytest +`pytest-unhandled-exception-exit-code `_ Plugin for py.test set a different exit code on uncaught exceptions Jun 22, 2020 5 - Production/Stable pytest (>=2.3) +`pytest-unittest-filter `_ A pytest plugin for filtering unittest-based test classes Jan 12, 2019 4 - Beta pytest (>=3.1.0) +`pytest-unmarked `_ Run only unmarked tests Aug 27, 2019 5 - Production/Stable N/A +`pytest-unordered `_ Test equality of unordered collections in pytest Nov 02, 2020 4 - Beta pytest (>=6.0.0) +`pytest-vagrant `_ A py.test plugin providing access to vagrant. Mar 23, 2020 5 - Production/Stable pytest +`pytest-valgrind `_ Mar 15, 2020 N/A N/A +`pytest-variables `_ pytest plugin for providing variables to tests/fixtures Oct 23, 2019 5 - Production/Stable pytest (>=2.4.2) +`pytest-vcr `_ Plugin for managing VCR.py cassettes Apr 26, 2019 5 - Production/Stable pytest (>=3.6.0) +`pytest-vcrpandas `_ Test from HTTP interactions to dataframe processed. Jan 12, 2019 4 - Beta pytest +`pytest-venv `_ py.test fixture for creating a virtual environment Aug 04, 2020 4 - Beta pytest +`pytest-verbose-parametrize `_ More descriptive output for parametrized py.test tests May 28, 2019 5 - Production/Stable pytest +`pytest-virtualenv `_ Virtualenv fixture for py.test May 28, 2019 5 - Production/Stable pytest +`pytest-voluptuous `_ Pytest plugin for asserting data against voluptuous schema. Jun 09, 2020 N/A pytest +`pytest-vscodedebug `_ A pytest plugin to easily enable debugging tests within Visual Studio Code Dec 04, 2020 4 - Beta N/A +`pytest-vts `_ pytest plugin for automatic recording of http stubbed tests Jun 05, 2019 N/A pytest (>=2.3) +`pytest-vw `_ pytest-vw makes your failing test cases succeed under CI tools scrutiny Oct 07, 2015 4 - Beta N/A +`pytest-vyper `_ Plugin for the vyper smart contract language. May 28, 2020 2 - Pre-Alpha N/A +`pytest-wa-e2e-plugin `_ Pytest plugin for testing whatsapp bots with end to end tests Feb 18, 2020 4 - Beta pytest (>=3.5.0) +`pytest-watch `_ Local continuous test runner with pytest and watchdog. May 20, 2018 N/A N/A +`pytest-wdl `_ Pytest plugin for testing WDL workflows. Nov 17, 2020 5 - Production/Stable N/A +`pytest-webdriver `_ Selenium webdriver fixture for py.test May 28, 2019 5 - Production/Stable pytest +`pytest-wetest `_ Welian API Automation test framework pytest plugin Nov 10, 2018 4 - Beta N/A +`pytest-whirlwind `_ Testing Tornado. Jun 12, 2020 N/A N/A +`pytest-wholenodeid `_ pytest addon for displaying the whole node id for failures Aug 26, 2015 4 - Beta pytest (>=2.0) +`pytest-winnotify `_ Windows tray notifications for py.test results. Apr 22, 2016 N/A N/A +`pytest-workflow `_ A pytest plugin for configuring workflow/pipeline tests using YAML files Dec 14, 2020 5 - Production/Stable pytest (>=5.4.0) +`pytest-xdist `_ pytest xdist plugin for distributed testing and loop-on-failing modes Dec 14, 2020 5 - Production/Stable pytest (>=6.0.0) +`pytest-xdist-debug-for-graingert `_ pytest xdist plugin for distributed testing and loop-on-failing modes Jul 24, 2019 5 - Production/Stable pytest (>=4.4.0) +`pytest-xdist-forked `_ forked from pytest-xdist Feb 10, 2020 5 - Production/Stable pytest (>=4.4.0) +`pytest-xfiles `_ Pytest fixtures providing data read from function, module or package related (x)files. Feb 27, 2018 N/A N/A +`pytest-xlog `_ Extended logging for test and decorators May 31, 2020 4 - Beta N/A +`pytest-xpara `_ An extended parametrizing plugin of pytest. Oct 30, 2017 3 - Alpha pytest +`pytest-xprocess `_ A pytest plugin for managing processes across test runs. Nov 26, 2020 4 - Beta pytest (>=2.8) +`pytest-xray `_ May 30, 2019 3 - Alpha N/A +`pytest-xrayjira `_ Mar 17, 2020 3 - Alpha pytest (==4.3.1) +`pytest-xray-server `_ Nov 29, 2020 3 - Alpha N/A +`pytest-xvfb `_ A pytest plugin to run Xvfb for tests. Jun 09, 2020 4 - Beta pytest (>=2.8.1) +`pytest-yaml `_ This plugin is used to load yaml output to your test using pytest framework. Oct 05, 2018 N/A pytest +`pytest-yamltree `_ Create or check file/directory trees described by YAML Mar 02, 2020 4 - Beta pytest (>=3.1.1) +`pytest-yamlwsgi `_ Run tests against wsgi apps defined in yaml May 11, 2010 N/A N/A +`pytest-yapf `_ Run yapf Jul 06, 2017 4 - Beta pytest (>=3.1.1) +`pytest-yapf3 `_ Validate your Python file format with yapf Aug 03, 2020 5 - Production/Stable pytest (>=5.4) +`pytest-yield `_ PyTest plugin to run tests concurrently, each `yield` switch context to other one Jan 23, 2019 N/A N/A +`pytest-zafira `_ A Zafira plugin for pytest Sep 18, 2019 5 - Production/Stable pytest (==4.1.1) +`pytest-zap `_ OWASP ZAP plugin for py.test. May 12, 2014 4 - Beta N/A +`pytest-zigzag `_ Extend py.test for RPC OpenStack testing. Feb 27, 2019 4 - Beta pytest (~=3.6) +============================================================================================================== ======================================================================================================================================================================== ============== ===================== ============================================ diff --git a/doc/en/plugins.rst b/doc/en/plugins.rst index 855b597392b..8090e7d18a4 100644 --- a/doc/en/plugins.rst +++ b/doc/en/plugins.rst @@ -58,7 +58,7 @@ Here is a little annotated list for some popular plugins: To see a complete list of all plugins with their latest testing status against different pytest and Python versions, please visit -`plugincompat `_. +:doc:`plugin_list`. You may also discover more plugins through a `pytest- pypi.org search`_. diff --git a/doc/en/writing_plugins.rst b/doc/en/writing_plugins.rst index e9806a6664d..92a3dd7dd48 100644 --- a/doc/en/writing_plugins.rst +++ b/doc/en/writing_plugins.rst @@ -122,7 +122,7 @@ you can copy from: * a custom collection example plugin: :ref:`yaml plugin` * builtin plugins which provide pytest's own functionality -* many `external plugins `_ providing additional features +* many :doc:`external plugins ` providing additional features All of these plugins implement :ref:`hooks ` and/or :ref:`fixtures ` to extend and add functionality. diff --git a/scripts/update-plugin-list.py b/scripts/update-plugin-list.py new file mode 100644 index 00000000000..f80e63127ee --- /dev/null +++ b/scripts/update-plugin-list.py @@ -0,0 +1,86 @@ +import datetime +import pathlib +import re + +import packaging.version +import requests +import tabulate + +FILE_HEAD = r"""Plugins List +============ + +PyPI projects that match "pytest-\*" are considered plugins and are listed +automatically. Packages classified as inactive are excluded. +""" +DEVELOPMENT_STATUS_CLASSIFIERS = ( + "Development Status :: 1 - Planning", + "Development Status :: 2 - Pre-Alpha", + "Development Status :: 3 - Alpha", + "Development Status :: 4 - Beta", + "Development Status :: 5 - Production/Stable", + "Development Status :: 6 - Mature", + "Development Status :: 7 - Inactive", +) + + +def iter_plugins(): + regex = r">([\d\w-]*)" + response = requests.get("https://pypi.org/simple") + for match in re.finditer(regex, response.text): + name = match.groups()[0] + if not name.startswith("pytest-"): + continue + response = requests.get(f"https://pypi.org/pypi/{name}/json") + if response.status_code == 404: + # Some packages, like pytest-azurepipelines42, are included in https://pypi.org/simple but + # return 404 on the JSON API. Skip. + continue + response.raise_for_status() + info = response.json()["info"] + if "Development Status :: 7 - Inactive" in info["classifiers"]: + continue + for classifier in DEVELOPMENT_STATUS_CLASSIFIERS: + if classifier in info["classifiers"]: + status = classifier[22:] + break + else: + status = "N/A" + requires = "N/A" + if info["requires_dist"]: + for requirement in info["requires_dist"]: + if requirement == "pytest" or "pytest " in requirement: + requires = requirement + break + releases = response.json()["releases"] + for release in sorted(releases, key=packaging.version.parse, reverse=True): + if releases[release]: + release_date = datetime.date.fromisoformat( + releases[release][-1]["upload_time_iso_8601"].split("T")[0] + ) + last_release = release_date.strftime("%b %d, %Y") + break + name = f'`{info["name"]} <{info["project_url"]}>`_' + summary = info["summary"].replace("\n", "") + summary = re.sub(r"_\b", "", summary) + yield { + "name": name, + "summary": summary, + "last release": last_release, + "status": status, + "requires": requires, + } + + +def main(): + plugins = list(iter_plugins()) + plugin_table = tabulate.tabulate(plugins, headers="keys", tablefmt="rst") + plugin_list = pathlib.Path("doc", "en", "plugin_list.rst") + with plugin_list.open("w") as f: + f.write(FILE_HEAD) + f.write(f"This list contains {len(plugins)} plugins.\n\n") + f.write(plugin_table) + f.write("\n") + + +if __name__ == "__main__": + main() From 07f0eb26b4da671659fefbfcce59d5d56e91f21a Mon Sep 17 00:00:00 2001 From: Hong Xu Date: Fri, 29 Jan 2021 07:54:06 -0800 Subject: [PATCH 107/630] Doc: Clarify pytester.run (#8294) Co-authored-by: Bruno Oliveira --- src/_pytest/pytester.py | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index 8ca21d1c538..d428654de88 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -1296,16 +1296,16 @@ def collect_by_name( def popen( self, - cmdargs, + cmdargs: Sequence[Union[str, "os.PathLike[str]"]], stdout: Union[int, TextIO] = subprocess.PIPE, stderr: Union[int, TextIO] = subprocess.PIPE, stdin=CLOSE_STDIN, **kw, ): - """Invoke subprocess.Popen. + """Invoke :py:class:`subprocess.Popen`. - Calls subprocess.Popen making sure the current working directory is - in the PYTHONPATH. + Calls :py:class:`subprocess.Popen` making sure the current working + directory is in the ``PYTHONPATH``. You probably want to use :py:meth:`run` instead. """ @@ -1340,21 +1340,30 @@ def run( ) -> RunResult: """Run a command with arguments. - Run a process using subprocess.Popen saving the stdout and stderr. + Run a process using :py:class:`subprocess.Popen` saving the stdout and + stderr. :param cmdargs: - The sequence of arguments to pass to `subprocess.Popen()`, with path-like objects - being converted to ``str`` automatically. + The sequence of arguments to pass to :py:class:`subprocess.Popen`, + with path-like objects being converted to :py:class:`str` + automatically. :param timeout: The period in seconds after which to timeout and raise :py:class:`Pytester.TimeoutExpired`. :param stdin: - Optional standard input. Bytes are being send, closing - the pipe, otherwise it is passed through to ``popen``. - Defaults to ``CLOSE_STDIN``, which translates to using a pipe - (``subprocess.PIPE``) that gets closed. + Optional standard input. - :rtype: RunResult + - If it is :py:attr:`CLOSE_STDIN` (Default), then this method calls + :py:class:`subprocess.Popen` with ``stdin=subprocess.PIPE``, and + the standard input is closed immediately after the new command is + started. + + - If it is of type :py:class:`bytes`, these bytes are sent to the + standard input of the command. + + - Otherwise, it is passed through to :py:class:`subprocess.Popen`. + For further information in this case, consult the document of the + ``stdin`` parameter in :py:class:`subprocess.Popen`. """ __tracebackhide__ = True From afea19079786761ee167e2bb4e0c90e3f44ba544 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Fri, 29 Jan 2021 20:40:43 +0200 Subject: [PATCH 108/630] Remove some no longer needed type-ignores --- src/_pytest/cacheprovider.py | 2 +- src/_pytest/monkeypatch.py | 2 +- src/_pytest/pytester.py | 10 +++------- src/_pytest/python.py | 3 +-- src/_pytest/python_api.py | 2 +- .../example_scripts/unittest/test_unittest_asyncio.py | 2 +- testing/test_assertrewrite.py | 4 ++-- 7 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/_pytest/cacheprovider.py b/src/_pytest/cacheprovider.py index 480319c03b4..585cebf6c9d 100755 --- a/src/_pytest/cacheprovider.py +++ b/src/_pytest/cacheprovider.py @@ -415,7 +415,7 @@ def pytest_collection_modifyitems( self.cached_nodeids.update(item.nodeid for item in items) def _get_increasing_order(self, items: Iterable[nodes.Item]) -> List[nodes.Item]: - return sorted(items, key=lambda item: item.fspath.mtime(), reverse=True) # type: ignore[no-any-return] + return sorted(items, key=lambda item: item.fspath.mtime(), reverse=True) def pytest_sessionfinish(self) -> None: config = self.config diff --git a/src/_pytest/monkeypatch.py b/src/_pytest/monkeypatch.py index ffef87173a8..2c432065625 100644 --- a/src/_pytest/monkeypatch.py +++ b/src/_pytest/monkeypatch.py @@ -91,7 +91,7 @@ def annotated_getattr(obj: object, name: str, ann: str) -> object: def derive_importpath(import_path: str, raising: bool) -> Tuple[str, object]: - if not isinstance(import_path, str) or "." not in import_path: # type: ignore[unreachable] + if not isinstance(import_path, str) or "." not in import_path: raise TypeError(f"must be absolute import path string, not {import_path!r}") module, attr = import_path.rsplit(".", 1) target = resolve(module) diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index d428654de88..4fe6e288b43 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -1096,7 +1096,7 @@ def pytest_configure(x, config: Config) -> None: class reprec: # type: ignore pass - reprec.ret = ret # type: ignore + reprec.ret = ret # Typically we reraise keyboard interrupts from the child run # because it's our user requesting interruption of the testing. @@ -1263,9 +1263,7 @@ def getmodulecol( Whether to also write an ``__init__.py`` file to the same directory to ensure it is a package. """ - # TODO: Remove type ignore in next mypy release (> 0.790). - # https://github.com/python/typeshed/pull/4582 - if isinstance(source, os.PathLike): # type: ignore[misc] + if isinstance(source, os.PathLike): path = self.path.joinpath(source) assert not withinit, "not supported for paths" else: @@ -1367,10 +1365,8 @@ def run( """ __tracebackhide__ = True - # TODO: Remove type ignore in next mypy release. - # https://github.com/python/typeshed/pull/4582 cmdargs = tuple( - os.fspath(arg) if isinstance(arg, os.PathLike) else arg for arg in cmdargs # type: ignore[misc] + os.fspath(arg) if isinstance(arg, os.PathLike) else arg for arg in cmdargs ) p1 = self.path.joinpath("stdout") p2 = self.path.joinpath("stderr") diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 29ebd176bbb..3d903ff9b0b 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -139,8 +139,7 @@ def pytest_cmdline_main(config: Config) -> Optional[Union[int, ExitCode]]: def pytest_generate_tests(metafunc: "Metafunc") -> None: for marker in metafunc.definition.iter_markers(name="parametrize"): - # TODO: Fix this type-ignore (overlapping kwargs). - metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) # type: ignore[misc] + metafunc.parametrize(*marker.args, **marker.kwargs, _param_mark=marker) def pytest_configure(config: Config) -> None: diff --git a/src/_pytest/python_api.py b/src/_pytest/python_api.py index 81ce4f89539..7e0c86479d4 100644 --- a/src/_pytest/python_api.py +++ b/src/_pytest/python_api.py @@ -713,7 +713,7 @@ def raises( else: excepted_exceptions = expected_exception for exc in excepted_exceptions: - if not isinstance(exc, type) or not issubclass(exc, BaseException): # type: ignore[unreachable] + if not isinstance(exc, type) or not issubclass(exc, BaseException): msg = "expected exception must be a BaseException type, not {}" # type: ignore[unreachable] not_a = exc.__name__ if isinstance(exc, type) else type(exc).__name__ raise TypeError(msg.format(not_a)) diff --git a/testing/example_scripts/unittest/test_unittest_asyncio.py b/testing/example_scripts/unittest/test_unittest_asyncio.py index bbc752de5c1..1cd2168604c 100644 --- a/testing/example_scripts/unittest/test_unittest_asyncio.py +++ b/testing/example_scripts/unittest/test_unittest_asyncio.py @@ -1,5 +1,5 @@ from typing import List -from unittest import IsolatedAsyncioTestCase # type: ignore +from unittest import IsolatedAsyncioTestCase teardowns: List[None] = [] diff --git a/testing/test_assertrewrite.py b/testing/test_assertrewrite.py index ffe18260f90..cba03406e86 100644 --- a/testing/test_assertrewrite.py +++ b/testing/test_assertrewrite.py @@ -381,7 +381,7 @@ def f6() -> None: ) def f7() -> None: - assert False or x() # type: ignore[unreachable] + assert False or x() assert ( getmsg(f7, {"x": x}) @@ -471,7 +471,7 @@ def f1() -> None: assert getmsg(f1) == "assert ((3 % 2) and False)" def f2() -> None: - assert False or 4 % 2 # type: ignore[unreachable] + assert False or 4 % 2 assert getmsg(f2) == "assert (False or (4 % 2))" From f0f19aa8d960dc71eaa00a0f01d3459a9626bbea Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 29 Jan 2021 21:08:37 +0100 Subject: [PATCH 109/630] doc: Point out two-argument form of monkeypatch.setattr See the "for convenience" part here: https://docs.pytest.org/en/stable/reference.html#pytest.MonkeyPatch.setattr --- doc/en/monkeypatch.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/en/monkeypatch.rst b/doc/en/monkeypatch.rst index 9480f008f7c..4964ee54815 100644 --- a/doc/en/monkeypatch.rst +++ b/doc/en/monkeypatch.rst @@ -16,6 +16,7 @@ functionality in tests: .. code-block:: python monkeypatch.setattr(obj, name, value, raising=True) + monkeypatch.setattr("somemodule.obj.name", value, raising=True) monkeypatch.delattr(obj, name, raising=True) monkeypatch.setitem(mapping, name, value) monkeypatch.delitem(obj, name, raising=True) From de06f468ed2bb64864f68e05a219e4a0f3986428 Mon Sep 17 00:00:00 2001 From: bluetech Date: Sat, 30 Jan 2021 00:30:28 +0000 Subject: [PATCH 110/630] [automated] Update plugin list --- doc/en/plugin_list.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/doc/en/plugin_list.rst b/doc/en/plugin_list.rst index 927838ac942..a4ab7df46bc 100644 --- a/doc/en/plugin_list.rst +++ b/doc/en/plugin_list.rst @@ -3,7 +3,7 @@ Plugins List PyPI projects that match "pytest-\*" are considered plugins and are listed automatically. Packages classified as inactive are excluded. -This list contains 820 plugins. +This list contains 821 plugins. ============================================================================================================== ======================================================================================================================================================================== ============== ===================== ============================================ name summary last release status requires @@ -546,6 +546,7 @@ name `pytest-poo `_ Visualize your crappy tests Jul 14, 2013 5 - Production/Stable N/A `pytest-poo-fail `_ Visualize your failed tests with poo Feb 12, 2015 5 - Production/Stable N/A `pytest-pop `_ A pytest plugin to help with testing pop projects Aug 13, 2020 5 - Production/Stable pytest (>=5.4.0) +`pytest-portion `_ Select a portion of the collected tests Jan 28, 2021 4 - Beta pytest (>=3.5.0) `pytest-postgres `_ Run PostgreSQL in Docker container in Pytest. Mar 22, 2020 N/A pytest `pytest-postgresql `_ Postgresql fixtures and fixture factories for Pytest. Oct 29, 2020 5 - Production/Stable pytest (>=3.0.0) `pytest-power `_ pytest plugin with powerful fixtures Dec 31, 2020 N/A pytest (>=5.4) @@ -633,7 +634,7 @@ name `pytest-rotest `_ Pytest integration with rotest Sep 08, 2019 N/A pytest (>=3.5.0) `pytest-rpc `_ Extend py.test for RPC OpenStack testing. Feb 22, 2019 4 - Beta pytest (~=3.6) `pytest-rt `_ pytest data collector plugin for Testgr Jan 24, 2021 N/A N/A -`pytest-rts `_ Coverage-based regression test selection (RTS) plugin for pytest Dec 21, 2020 N/A pytest +`pytest-rts `_ Coverage-based regression test selection (RTS) plugin for pytest Jan 29, 2021 N/A pytest `pytest-runfailed `_ implement a --failed option for pytest Mar 24, 2016 N/A N/A `pytest-runner `_ Invoke py.test as distutils command with dependency resolution Oct 26, 2019 5 - Production/Stable pytest (!=3.7.3,>=3.5) ; extra == 'testing' `pytest-salt `_ Pytest Salt Plugin Jan 27, 2020 4 - Beta N/A @@ -644,13 +645,13 @@ name `pytest-sanic `_ a pytest plugin for Sanic Sep 24, 2020 N/A pytest (>=5.2) `pytest-sanity `_ Dec 07, 2020 N/A N/A `pytest-sa-pg `_ May 14, 2019 N/A N/A -`pytest-sbase `_ A complete web automation framework for end-to-end testing. Jan 27, 2021 5 - Production/Stable N/A +`pytest-sbase `_ A complete web automation framework for end-to-end testing. Jan 29, 2021 5 - Production/Stable N/A `pytest-scenario `_ pytest plugin for test scenarios Feb 06, 2017 3 - Alpha N/A `pytest-schema `_ 👍 Validate return values against a schema-like object in testing Aug 31, 2020 5 - Production/Stable pytest (>=3.5.0) `pytest-securestore `_ An encrypted password store for use within pytest cases Jun 19, 2019 4 - Beta N/A `pytest-select `_ A pytest plugin which allows to (de-)select tests from a file. Jan 18, 2019 3 - Alpha pytest (>=3.0) `pytest-selenium `_ pytest plugin for Selenium Sep 19, 2020 5 - Production/Stable pytest (>=5.0.0) -`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Jan 27, 2021 5 - Production/Stable N/A +`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Jan 29, 2021 5 - Production/Stable N/A `pytest-selenium-enhancer `_ pytest plugin for Selenium Nov 26, 2020 5 - Production/Stable N/A `pytest-selenium-pdiff `_ A pytest package implementing perceptualdiff for Selenium tests. Apr 06, 2017 2 - Pre-Alpha N/A `pytest-send-email `_ Send pytest execution result email Dec 04, 2019 N/A N/A From c971f2f474df929dd06b5c4dae27514c7fb47e70 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sat, 30 Jan 2021 13:01:41 +0200 Subject: [PATCH 111/630] ci: give pytest bot the credit for updating the plugin list --- .github/workflows/update-plugin-list.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/update-plugin-list.yml b/.github/workflows/update-plugin-list.yml index 10b5cb99478..3f4ec092bd9 100644 --- a/.github/workflows/update-plugin-list.yml +++ b/.github/workflows/update-plugin-list.yml @@ -26,6 +26,7 @@ jobs: uses: peter-evans/create-pull-request@2455e1596942c2902952003bbb574afbbe2ab2e6 with: commit-message: '[automated] Update plugin list' + author: 'pytest bot ' branch: update-plugin-list/patch delete-branch: true branch-suffix: short-commit-hash From 5c61d60b0d9328889799b8cf698841b61ff7035b Mon Sep 17 00:00:00 2001 From: pytest bot Date: Mon, 1 Feb 2021 00:32:44 +0000 Subject: [PATCH 112/630] [automated] Update plugin list --- doc/en/plugin_list.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/en/plugin_list.rst b/doc/en/plugin_list.rst index a4ab7df46bc..9c6b6f0aa41 100644 --- a/doc/en/plugin_list.rst +++ b/doc/en/plugin_list.rst @@ -34,7 +34,7 @@ name `pytest-apistellar `_ apistellar plugin for pytest. Jun 18, 2019 N/A N/A `pytest-appengine `_ AppEngine integration that works well with pytest-django Feb 27, 2017 N/A N/A `pytest-appium `_ Pytest plugin for appium Dec 05, 2019 N/A N/A -`pytest-approvaltests `_ A plugin to use approvaltests with pytest Aug 02, 2019 4 - Beta N/A +`pytest-approvaltests `_ A plugin to use approvaltests with pytest Jan 31, 2021 4 - Beta pytest (>=3.5.0) `pytest-arraydiff `_ pytest plugin to help with comparing array output from tests Dec 06, 2018 4 - Beta pytest `pytest-asgi-server `_ Convenient ASGI client/server fixtures for Pytest Dec 12, 2020 N/A pytest (>=5.4.1) `pytest-asptest `_ test Answer Set Programming programs Apr 28, 2018 4 - Beta N/A @@ -123,7 +123,7 @@ name `pytest-codestyle `_ pytest plugin to run pycodestyle Mar 23, 2020 3 - Alpha N/A `pytest-collect-formatter `_ Formatter for pytest collect output Nov 19, 2020 5 - Production/Stable N/A `pytest-colordots `_ Colorizes the progress indicators Oct 06, 2017 5 - Production/Stable N/A -`pytest-commander `_ An interactive GUI test runner for PyTest Nov 21, 2020 N/A pytest (>=5.0.0) +`pytest-commander `_ An interactive GUI test runner for PyTest Jan 31, 2021 N/A pytest (>=5.0.0) `pytest-common-subject `_ pytest framework for testing different aspects of a common method Nov 12, 2020 N/A pytest (>=3.6,<7) `pytest-concurrent `_ Concurrently execute test cases with multithread, multiprocess and gevent Jan 12, 2019 4 - Beta pytest (>=3.1.1) `pytest-config `_ Base configurations and utilities for developing your Python project test suite with pytest. Nov 07, 2014 5 - Production/Stable N/A @@ -339,7 +339,7 @@ name `pytest-homeassistant `_ A pytest plugin for use with homeassistant custom components. Aug 12, 2020 4 - Beta N/A `pytest-homeassistant-custom-component `_ Experimental package to automatically extract test plugins for Home Assistant custom components Jan 05, 2021 3 - Alpha pytest (==6.1.2) `pytest-honors `_ Report on tests that honor constraints, and guard against regressions Mar 06, 2020 4 - Beta N/A -`pytest-hoverfly-wrapper `_ Integrates the Hoverfly HTTP proxy into Pytest Oct 25, 2020 4 - Beta N/A +`pytest-hoverfly-wrapper `_ Integrates the Hoverfly HTTP proxy into Pytest Jan 31, 2021 4 - Beta N/A `pytest-html `_ pytest plugin for generating HTML reports Dec 13, 2020 5 - Production/Stable pytest (!=6.0.0,>=5.0) `pytest-html-lee `_ optimized pytest plugin for generating HTML reports Jun 30, 2020 5 - Production/Stable pytest (>=5.0) `pytest-html-profiling `_ Pytest plugin for generating HTML reports with per-test profiling and optionally call graph visualizations. Based on pytest-html by Dave Hunt. Feb 11, 2020 5 - Production/Stable pytest (>=3.0) @@ -548,7 +548,7 @@ name `pytest-pop `_ A pytest plugin to help with testing pop projects Aug 13, 2020 5 - Production/Stable pytest (>=5.4.0) `pytest-portion `_ Select a portion of the collected tests Jan 28, 2021 4 - Beta pytest (>=3.5.0) `pytest-postgres `_ Run PostgreSQL in Docker container in Pytest. Mar 22, 2020 N/A pytest -`pytest-postgresql `_ Postgresql fixtures and fixture factories for Pytest. Oct 29, 2020 5 - Production/Stable pytest (>=3.0.0) +`pytest-postgresql `_ Postgresql fixtures and fixture factories for Pytest. Jan 31, 2021 5 - Production/Stable pytest (>=3.0.0) `pytest-power `_ pytest plugin with powerful fixtures Dec 31, 2020 N/A pytest (>=5.4) `pytest-pride `_ Minitest-style test colors Apr 02, 2016 3 - Alpha N/A `pytest-print `_ pytest-print adds the printer fixture you can use to print messages to the user (directly to the pytest runner, not stdout) Oct 23, 2020 5 - Production/Stable pytest (>=3.0.0) @@ -645,13 +645,13 @@ name `pytest-sanic `_ a pytest plugin for Sanic Sep 24, 2020 N/A pytest (>=5.2) `pytest-sanity `_ Dec 07, 2020 N/A N/A `pytest-sa-pg `_ May 14, 2019 N/A N/A -`pytest-sbase `_ A complete web automation framework for end-to-end testing. Jan 29, 2021 5 - Production/Stable N/A +`pytest-sbase `_ A complete web automation framework for end-to-end testing. Jan 31, 2021 5 - Production/Stable N/A `pytest-scenario `_ pytest plugin for test scenarios Feb 06, 2017 3 - Alpha N/A `pytest-schema `_ 👍 Validate return values against a schema-like object in testing Aug 31, 2020 5 - Production/Stable pytest (>=3.5.0) `pytest-securestore `_ An encrypted password store for use within pytest cases Jun 19, 2019 4 - Beta N/A `pytest-select `_ A pytest plugin which allows to (de-)select tests from a file. Jan 18, 2019 3 - Alpha pytest (>=3.0) `pytest-selenium `_ pytest plugin for Selenium Sep 19, 2020 5 - Production/Stable pytest (>=5.0.0) -`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Jan 29, 2021 5 - Production/Stable N/A +`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Jan 31, 2021 5 - Production/Stable N/A `pytest-selenium-enhancer `_ pytest plugin for Selenium Nov 26, 2020 5 - Production/Stable N/A `pytest-selenium-pdiff `_ A pytest package implementing perceptualdiff for Selenium tests. Apr 06, 2017 2 - Pre-Alpha N/A `pytest-send-email `_ Send pytest execution result email Dec 04, 2019 N/A N/A From 1b2ca22e9bf6720445ca8eab928b44cd27b46c8f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 1 Feb 2021 16:49:41 +0000 Subject: [PATCH 113/630] [pre-commit.ci] pre-commit autoupdate --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9130a79a06b..2eed21fc883 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,7 +5,7 @@ repos: - id: black args: [--safe, --quiet] - repo: https://github.com/asottile/blacken-docs - rev: v1.9.1 + rev: v1.9.2 hooks: - id: blacken-docs additional_dependencies: [black==20.8b1] @@ -34,7 +34,7 @@ repos: - id: reorder-python-imports args: ['--application-directories=.:src', --py36-plus] - repo: https://github.com/asottile/pyupgrade - rev: v2.7.4 + rev: v2.9.0 hooks: - id: pyupgrade args: [--py36-plus] From 3165b1e3238df1424431f754182c66fd6d18bee4 Mon Sep 17 00:00:00 2001 From: pytest bot Date: Tue, 2 Feb 2021 00:31:11 +0000 Subject: [PATCH 114/630] [automated] Update plugin list --- doc/en/plugin_list.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/en/plugin_list.rst b/doc/en/plugin_list.rst index a4ab7df46bc..8747bf75e8a 100644 --- a/doc/en/plugin_list.rst +++ b/doc/en/plugin_list.rst @@ -34,7 +34,7 @@ name `pytest-apistellar `_ apistellar plugin for pytest. Jun 18, 2019 N/A N/A `pytest-appengine `_ AppEngine integration that works well with pytest-django Feb 27, 2017 N/A N/A `pytest-appium `_ Pytest plugin for appium Dec 05, 2019 N/A N/A -`pytest-approvaltests `_ A plugin to use approvaltests with pytest Aug 02, 2019 4 - Beta N/A +`pytest-approvaltests `_ A plugin to use approvaltests with pytest Jan 31, 2021 4 - Beta pytest (>=3.5.0) `pytest-arraydiff `_ pytest plugin to help with comparing array output from tests Dec 06, 2018 4 - Beta pytest `pytest-asgi-server `_ Convenient ASGI client/server fixtures for Pytest Dec 12, 2020 N/A pytest (>=5.4.1) `pytest-asptest `_ test Answer Set Programming programs Apr 28, 2018 4 - Beta N/A @@ -123,7 +123,7 @@ name `pytest-codestyle `_ pytest plugin to run pycodestyle Mar 23, 2020 3 - Alpha N/A `pytest-collect-formatter `_ Formatter for pytest collect output Nov 19, 2020 5 - Production/Stable N/A `pytest-colordots `_ Colorizes the progress indicators Oct 06, 2017 5 - Production/Stable N/A -`pytest-commander `_ An interactive GUI test runner for PyTest Nov 21, 2020 N/A pytest (>=5.0.0) +`pytest-commander `_ An interactive GUI test runner for PyTest Jan 31, 2021 N/A pytest (>=5.0.0) `pytest-common-subject `_ pytest framework for testing different aspects of a common method Nov 12, 2020 N/A pytest (>=3.6,<7) `pytest-concurrent `_ Concurrently execute test cases with multithread, multiprocess and gevent Jan 12, 2019 4 - Beta pytest (>=3.1.1) `pytest-config `_ Base configurations and utilities for developing your Python project test suite with pytest. Nov 07, 2014 5 - Production/Stable N/A @@ -337,9 +337,9 @@ name `pytest-historic `_ Custom report to display pytest historical execution records Apr 08, 2020 N/A pytest `pytest-historic-hook `_ Custom listener to store execution results into MYSQL DB, which is used for pytest-historic report Apr 08, 2020 N/A pytest `pytest-homeassistant `_ A pytest plugin for use with homeassistant custom components. Aug 12, 2020 4 - Beta N/A -`pytest-homeassistant-custom-component `_ Experimental package to automatically extract test plugins for Home Assistant custom components Jan 05, 2021 3 - Alpha pytest (==6.1.2) +`pytest-homeassistant-custom-component `_ Experimental package to automatically extract test plugins for Home Assistant custom components Feb 01, 2021 3 - Alpha pytest (==6.2.2) `pytest-honors `_ Report on tests that honor constraints, and guard against regressions Mar 06, 2020 4 - Beta N/A -`pytest-hoverfly-wrapper `_ Integrates the Hoverfly HTTP proxy into Pytest Oct 25, 2020 4 - Beta N/A +`pytest-hoverfly-wrapper `_ Integrates the Hoverfly HTTP proxy into Pytest Jan 31, 2021 4 - Beta N/A `pytest-html `_ pytest plugin for generating HTML reports Dec 13, 2020 5 - Production/Stable pytest (!=6.0.0,>=5.0) `pytest-html-lee `_ optimized pytest plugin for generating HTML reports Jun 30, 2020 5 - Production/Stable pytest (>=5.0) `pytest-html-profiling `_ Pytest plugin for generating HTML reports with per-test profiling and optionally call graph visualizations. Based on pytest-html by Dave Hunt. Feb 11, 2020 5 - Production/Stable pytest (>=3.0) @@ -548,7 +548,7 @@ name `pytest-pop `_ A pytest plugin to help with testing pop projects Aug 13, 2020 5 - Production/Stable pytest (>=5.4.0) `pytest-portion `_ Select a portion of the collected tests Jan 28, 2021 4 - Beta pytest (>=3.5.0) `pytest-postgres `_ Run PostgreSQL in Docker container in Pytest. Mar 22, 2020 N/A pytest -`pytest-postgresql `_ Postgresql fixtures and fixture factories for Pytest. Oct 29, 2020 5 - Production/Stable pytest (>=3.0.0) +`pytest-postgresql `_ Postgresql fixtures and fixture factories for Pytest. Jan 31, 2021 5 - Production/Stable pytest (>=3.0.0) `pytest-power `_ pytest plugin with powerful fixtures Dec 31, 2020 N/A pytest (>=5.4) `pytest-pride `_ Minitest-style test colors Apr 02, 2016 3 - Alpha N/A `pytest-print `_ pytest-print adds the printer fixture you can use to print messages to the user (directly to the pytest runner, not stdout) Oct 23, 2020 5 - Production/Stable pytest (>=3.0.0) @@ -645,13 +645,13 @@ name `pytest-sanic `_ a pytest plugin for Sanic Sep 24, 2020 N/A pytest (>=5.2) `pytest-sanity `_ Dec 07, 2020 N/A N/A `pytest-sa-pg `_ May 14, 2019 N/A N/A -`pytest-sbase `_ A complete web automation framework for end-to-end testing. Jan 29, 2021 5 - Production/Stable N/A +`pytest-sbase `_ A complete web automation framework for end-to-end testing. Jan 31, 2021 5 - Production/Stable N/A `pytest-scenario `_ pytest plugin for test scenarios Feb 06, 2017 3 - Alpha N/A `pytest-schema `_ 👍 Validate return values against a schema-like object in testing Aug 31, 2020 5 - Production/Stable pytest (>=3.5.0) `pytest-securestore `_ An encrypted password store for use within pytest cases Jun 19, 2019 4 - Beta N/A `pytest-select `_ A pytest plugin which allows to (de-)select tests from a file. Jan 18, 2019 3 - Alpha pytest (>=3.0) `pytest-selenium `_ pytest plugin for Selenium Sep 19, 2020 5 - Production/Stable pytest (>=5.0.0) -`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Jan 29, 2021 5 - Production/Stable N/A +`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Jan 31, 2021 5 - Production/Stable N/A `pytest-selenium-enhancer `_ pytest plugin for Selenium Nov 26, 2020 5 - Production/Stable N/A `pytest-selenium-pdiff `_ A pytest package implementing perceptualdiff for Selenium tests. Apr 06, 2017 2 - Pre-Alpha N/A `pytest-send-email `_ Send pytest execution result email Dec 04, 2019 N/A N/A @@ -695,7 +695,7 @@ name `pytest-split `_ Pytest plugin for splitting test suite based on test execution time Apr 07, 2020 1 - Planning N/A `pytest-splitio `_ Split.io SDK integration for e2e tests Sep 22, 2020 N/A pytest (<7,>=5.0) `pytest-split-tests `_ A Pytest plugin for running a subset of your tests by splitting them in to equally sized groups. Forked from Mark Adams' original project pytest-test-groups. May 28, 2019 N/A pytest (>=2.5) -`pytest-splunk-addon `_ A Dynamic test tool for Splunk Apps and Add-ons Jan 05, 2021 N/A pytest (>5.4.0,<6.1) +`pytest-splunk-addon `_ A Dynamic test tool for Splunk Apps and Add-ons Feb 01, 2021 N/A pytest (>5.4.0,<6.1) `pytest-splunk-addon-ui-smartx `_ Library to support testing Splunk Add-on UX Jan 18, 2021 N/A N/A `pytest-splunk-env `_ pytest fixtures for interaction with Splunk Enterprise and Splunk Cloud Oct 22, 2020 N/A pytest (>=6.1.1,<7.0.0) `pytest-sqitch `_ sqitch for pytest Apr 06, 2020 4 - Beta N/A From 100c8deab5fccda043b2087bd450d074c1f8756d Mon Sep 17 00:00:00 2001 From: Matthew Hughes Date: Sat, 30 Jan 2021 19:50:56 +0000 Subject: [PATCH 115/630] Fix various typos in fixture docs --- doc/en/fixture.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/en/fixture.rst b/doc/en/fixture.rst index a629bb7d47f..ca44fbd8263 100644 --- a/doc/en/fixture.rst +++ b/doc/en/fixture.rst @@ -375,7 +375,7 @@ Fixtures are reusable ^^^^^^^^^^^^^^^^^^^^^ One of the things that makes pytest's fixture system so powerful, is that it -gives us the abilty to define a generic setup step that can reused over and +gives us the ability to define a generic setup step that can reused over and over, just like a normal function would be used. Two different tests can request the same fixture and have pytest give each test their own result from that fixture. @@ -902,7 +902,7 @@ attempt to tear them down as it normally would. 2. Adding finalizers directly ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -While yield fixtures are considered to be the cleaner and more straighforward +While yield fixtures are considered to be the cleaner and more straightforward option, there is another choice, and that is to add "finalizer" functions directly to the test's `request-context`_ object. It brings a similar result as yield fixtures, but requires a bit more verbosity. @@ -1026,7 +1026,7 @@ making one state-changing action each, and then bundling them together with their teardown code, as :ref:`the email examples above ` showed. The chance that a state-changing operation can fail but still modify state is -neglibible, as most of these operations tend to be `transaction`_-based (at +negligible, as most of these operations tend to be `transaction`_-based (at least at the level of testing where state could be left behind). So if we make sure that any successful state-changing action gets torn down by moving it to a separate fixture function and separating it from other, potentially failing @@ -1124,7 +1124,7 @@ never have been made. .. _`conftest.py`: .. _`conftest`: -Fixture availabiility +Fixture availability --------------------- Fixture availability is determined from the perspective of the test. A fixture @@ -1410,9 +1410,9 @@ pytest doesn't know where ``c`` should go in the case, so it should be assumed that it could go anywhere between ``g`` and ``b``. This isn't necessarily bad, but it's something to keep in mind. If the order -they execute in could affect the behavior a test is targetting, or could +they execute in could affect the behavior a test is targeting, or could otherwise influence the result of a test, then the order should be defined -explicitely in a way that allows pytest to linearize/"flatten" that order. +explicitly in a way that allows pytest to linearize/"flatten" that order. .. _`autouse order`: @@ -1506,7 +1506,7 @@ of what we've gone over so far. All that's needed is stepping up to a larger scope, then having the **act** step defined as an autouse fixture, and finally, making sure all the fixtures -are targetting that highler level scope. +are targeting that higher level scope. Let's pull :ref:`an example from above `, and tweak it a bit. Let's say that in addition to checking for a welcome message in the header, @@ -1777,7 +1777,7 @@ Parametrizing fixtures ----------------------------------------------------------------- Fixture functions can be parametrized in which case they will be called -multiple times, each time executing the set of dependent tests, i. e. the +multiple times, each time executing the set of dependent tests, i.e. the tests that depend on this fixture. Test functions usually do not need to be aware of their re-running. Fixture parametrization helps to write exhaustive functional tests for components which themselves can be From 298541f540a0b566002c8e6a01e7e093c6750158 Mon Sep 17 00:00:00 2001 From: Matthew Hughes Date: Sat, 30 Jan 2021 22:14:35 +0000 Subject: [PATCH 116/630] Add basic emaillib for tests in fixture.rst This will be used to power regendoc runs for later tests --- doc/en/fixture.rst | 42 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/doc/en/fixture.rst b/doc/en/fixture.rst index ca44fbd8263..38636ff7514 100644 --- a/doc/en/fixture.rst +++ b/doc/en/fixture.rst @@ -844,12 +844,42 @@ Once the test is finished, pytest will go back down the list of fixtures, but in the *reverse order*, taking each one that yielded, and running the code inside it that was *after* the ``yield`` statement. -As a simple example, let's say we want to test sending email from one user to -another. We'll have to first make each user, then send the email from one user -to the other, and finally assert that the other user received that message in -their inbox. If we want to clean up after the test runs, we'll likely have to -make sure the other user's mailbox is emptied before deleting that user, -otherwise the system may complain. +As a simple example, consider this basic email module: + +.. code-block:: python + + # content of emaillib.py + class MailAdminClient: + def create_user(self): + return MailUser() + + def delete_user(self, user): + # do some cleanup + pass + + + class MailUser: + def __init__(self): + self.inbox = [] + + def send_email(self, email, other): + other.inbox.append(email) + + def clear_mailbox(self): + self.mailbox.clear() + + + class Email: + def __init__(self, subject, body): + self.body = body + self.subject = subject + +Let's say we want to test sending email from one user to another. We'll have to +first make each user, then send the email from one user to the other, and +finally assert that the other user received that message in their inbox. If we +want to clean up after the test runs, we'll likely have to make sure the other +user's mailbox is emptied before deleting that user, otherwise the system may +complain. Here's what that might look like: From 1d895dd46cba716ae96ceb5688d52bdff5445168 Mon Sep 17 00:00:00 2001 From: pytest bot Date: Thu, 4 Feb 2021 00:26:06 +0000 Subject: [PATCH 117/630] [automated] Update plugin list --- doc/en/plugin_list.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/plugin_list.rst b/doc/en/plugin_list.rst index 8747bf75e8a..6aae603a8a7 100644 --- a/doc/en/plugin_list.rst +++ b/doc/en/plugin_list.rst @@ -212,7 +212,7 @@ name `pytest-docker-pexpect `_ pytest plugin for writing functional tests with pexpect and docker Jan 14, 2019 N/A pytest `pytest-docker-postgresql `_ A simple plugin to use with pytest Sep 24, 2019 4 - Beta pytest (>=3.5.0) `pytest-docker-py `_ Easy to use, simple to extend, pytest plugin that minimally leverages docker-py. Nov 27, 2018 N/A pytest (==4.0.0) -`pytest-docker-registry-fixtures `_ Pytest fixtures for testing with docker registries. Jan 25, 2021 4 - Beta pytest +`pytest-docker-registry-fixtures `_ Pytest fixtures for testing with docker registries. Feb 03, 2021 4 - Beta pytest `pytest-docker-tools `_ Docker integration tests for pytest Jan 15, 2021 4 - Beta pytest (>=6.0.1,<7.0.0) `pytest-docs `_ Documentation tool for pytest Nov 11, 2018 4 - Beta pytest (>=3.5.0) `pytest-docstyle `_ pytest plugin to run pydocstyle Mar 23, 2020 3 - Alpha N/A From b77f071befd67492776cefb632341639e98e30b6 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 4 Feb 2021 11:40:28 +0100 Subject: [PATCH 118/630] doc: Remove past training --- doc/en/index.rst | 6 ------ 1 file changed, 6 deletions(-) diff --git a/doc/en/index.rst b/doc/en/index.rst index f74ef90a785..0361805d95a 100644 --- a/doc/en/index.rst +++ b/doc/en/index.rst @@ -1,11 +1,5 @@ :orphan: -.. sidebar:: Next Open Trainings - - - `Professional testing with Python `_, via Python Academy, February 1-3 2021, remote and Leipzig (Germany). **Early-bird discount available until January 15th**. - - Also see `previous talks and blogposts `_. - .. _features: pytest: helps you write better programs From 97cfd66806913250b3b26775da66ff75f6673b29 Mon Sep 17 00:00:00 2001 From: Matthew Hughes Date: Sat, 30 Jan 2021 22:23:04 +0000 Subject: [PATCH 119/630] Add regendoc runs for emaillib tests in fixture Also update these tests ensure they pass, and be explicit about the test file called in an existing test to avoid unintentional calls to the added tests --- doc/en/fixture.rst | 43 ++++++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/doc/en/fixture.rst b/doc/en/fixture.rst index 38636ff7514..de1591d2d61 100644 --- a/doc/en/fixture.rst +++ b/doc/en/fixture.rst @@ -829,6 +829,8 @@ This system can be leveraged in two ways. 1. ``yield`` fixtures (recommended) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +.. regendoc: wipe + "Yield" fixtures ``yield`` instead of ``return``. With these fixtures, we can run some code and pass an object back to the requesting fixture/test, just like with the other fixtures. The only differences are: @@ -866,13 +868,13 @@ As a simple example, consider this basic email module: other.inbox.append(email) def clear_mailbox(self): - self.mailbox.clear() + self.inbox.clear() class Email: def __init__(self, subject, body): - self.body = body self.subject = subject + self.body = body Let's say we want to test sending email from one user to another. We'll have to first make each user, then send the email from one user to the other, and @@ -885,6 +887,7 @@ Here's what that might look like: .. code-block:: python + # content of test_emaillib.py import pytest from emaillib import Email, MailAdminClient @@ -899,17 +902,17 @@ Here's what that might look like: def sending_user(mail_admin): user = mail_admin.create_user() yield user - admin_client.delete_user(user) + mail_admin.delete_user(user) @pytest.fixture def receiving_user(mail_admin): user = mail_admin.create_user() yield user - admin_client.delete_user(user) + mail_admin.delete_user(user) - def test_email_received(sending_user, receiving_user, email): + def test_email_received(sending_user, receiving_user): email = Email(subject="Hey!", body="How's it going?") sending_user.send_email(email, receiving_user) assert email in receiving_user.inbox @@ -921,6 +924,10 @@ There is a risk that even having the order right on the teardown side of things doesn't guarantee a safe cleanup. That's covered in a bit more detail in :ref:`safe teardowns`. +.. code-block:: pytest + + $ pytest -q test_emaillib.py + Handling errors for yield fixture """"""""""""""""""""""""""""""""" @@ -952,6 +959,7 @@ Here's how the previous example would look using the ``addfinalizer`` method: .. code-block:: python + # content of test_emaillib.py import pytest from emaillib import Email, MailAdminClient @@ -966,7 +974,7 @@ Here's how the previous example would look using the ``addfinalizer`` method: def sending_user(mail_admin): user = mail_admin.create_user() yield user - admin_client.delete_user(user) + mail_admin.delete_user(user) @pytest.fixture @@ -974,7 +982,7 @@ Here's how the previous example would look using the ``addfinalizer`` method: user = mail_admin.create_user() def delete_user(): - admin_client.delete_user(user) + mail_admin.delete_user(user) request.addfinalizer(delete_user) return user @@ -986,7 +994,7 @@ Here's how the previous example would look using the ``addfinalizer`` method: sending_user.send_email(_email, receiving_user) def empty_mailbox(): - receiving_user.delete_email(_email) + receiving_user.clear_mailbox() request.addfinalizer(empty_mailbox) return _email @@ -999,6 +1007,10 @@ Here's how the previous example would look using the ``addfinalizer`` method: It's a bit longer than yield fixtures and a bit more complex, but it does offer some nuances for when you're in a pinch. +.. code-block:: pytest + + $ pytest -q test_emaillib.py + .. _`safe teardowns`: Safe teardowns @@ -1014,6 +1026,7 @@ above): .. code-block:: python + # content of test_emaillib.py import pytest from emaillib import Email, MailAdminClient @@ -1025,11 +1038,11 @@ above): sending_user = mail_admin.create_user() receiving_user = mail_admin.create_user() email = Email(subject="Hey!", body="How's it going?") - sending_user.send_emai(email, receiving_user) + sending_user.send_email(email, receiving_user) yield receiving_user, email - receiving_user.delete_email(email) - admin_client.delete_user(sending_user) - admin_client.delete_user(receiving_user) + receiving_user.clear_mailbox() + mail_admin.delete_user(sending_user) + mail_admin.delete_user(receiving_user) def test_email_received(setup): @@ -1046,6 +1059,10 @@ One option might be to go with the ``addfinalizer`` method instead of yield fixtures, but that might get pretty complex and difficult to maintain (and it wouldn't be compact anymore). +.. code-block:: pytest + + $ pytest -q test_emaillib.py + .. _`safe fixture structure`: Safe fixture structure @@ -1676,7 +1693,7 @@ again, nothing much has changed: .. code-block:: pytest - $ pytest -s -q --tb=no + $ pytest -s -q --tb=no test_module.py FFfinalizing (smtp.gmail.com) ========================= short test summary info ========================== From 709c211e68bf3a8aa452b61056b5294a21050dfb Mon Sep 17 00:00:00 2001 From: Matthew Hughes Date: Thu, 4 Feb 2021 18:00:17 +0000 Subject: [PATCH 120/630] Run regendoc over fixture docs This is the result of running: $ cd doc/en && make regen REGENDOC_FILES=fixture.rst --- doc/en/fixture.rst | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/doc/en/fixture.rst b/doc/en/fixture.rst index de1591d2d61..d72a0e619b5 100644 --- a/doc/en/fixture.rst +++ b/doc/en/fixture.rst @@ -927,6 +927,8 @@ doesn't guarantee a safe cleanup. That's covered in a bit more detail in .. code-block:: pytest $ pytest -q test_emaillib.py + . [100%] + 1 passed in 0.12s Handling errors for yield fixture """"""""""""""""""""""""""""""""" @@ -1010,6 +1012,8 @@ does offer some nuances for when you're in a pinch. .. code-block:: pytest $ pytest -q test_emaillib.py + . [100%] + 1 passed in 0.12s .. _`safe teardowns`: @@ -1062,6 +1066,8 @@ wouldn't be compact anymore). .. code-block:: pytest $ pytest -q test_emaillib.py + . [100%] + 1 passed in 0.12s .. _`safe fixture structure`: @@ -1978,11 +1984,13 @@ Running the above tests results in the following test IDs being used: platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR - collected 10 items + collected 11 items + + @@ -1994,7 +2002,7 @@ Running the above tests results in the following test IDs being used: - ======================= 10 tests collected in 0.12s ======================== + ======================= 11 tests collected in 0.12s ======================== .. _`fixture-parametrize-marks`: From 80c223474c98fd59a07776994e672e934866c7d5 Mon Sep 17 00:00:00 2001 From: Hong Xu Date: Thu, 4 Feb 2021 13:44:22 -0800 Subject: [PATCH 121/630] Type annotation polishing for symbols around Pytester.run (#8298) * Type annotation polishing for symbols around Pytester.run Hopefully these will help document readers understand pertinent methods and constants better. Following up #8294 * Use NOTSET instead of object --- src/_pytest/pytester.py | 42 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index 4fe6e288b43..853dfbe9489 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -20,6 +20,7 @@ from typing import Callable from typing import Dict from typing import Generator +from typing import IO from typing import Iterable from typing import List from typing import Optional @@ -41,6 +42,8 @@ from _pytest._code import Source from _pytest.capture import _get_multicapture from _pytest.compat import final +from _pytest.compat import NOTSET +from _pytest.compat import NotSetType from _pytest.config import _PluggyPlugin from _pytest.config import Config from _pytest.config import ExitCode @@ -66,6 +69,7 @@ if TYPE_CHECKING: + from typing_extensions import Final from typing_extensions import Literal import pexpect @@ -651,7 +655,7 @@ class Pytester: __test__ = False - CLOSE_STDIN = object + CLOSE_STDIN: "Final" = NOTSET class TimeoutExpired(Exception): pass @@ -1297,13 +1301,13 @@ def popen( cmdargs: Sequence[Union[str, "os.PathLike[str]"]], stdout: Union[int, TextIO] = subprocess.PIPE, stderr: Union[int, TextIO] = subprocess.PIPE, - stdin=CLOSE_STDIN, + stdin: Union[NotSetType, bytes, IO[Any], int] = CLOSE_STDIN, **kw, ): """Invoke :py:class:`subprocess.Popen`. Calls :py:class:`subprocess.Popen` making sure the current working - directory is in the ``PYTHONPATH``. + directory is in ``PYTHONPATH``. You probably want to use :py:meth:`run` instead. """ @@ -1334,7 +1338,7 @@ def run( self, *cmdargs: Union[str, "os.PathLike[str]"], timeout: Optional[float] = None, - stdin=CLOSE_STDIN, + stdin: Union[NotSetType, bytes, IO[Any], int] = CLOSE_STDIN, ) -> RunResult: """Run a command with arguments. @@ -1426,21 +1430,17 @@ def _dump_lines(self, lines, fp): def _getpytestargs(self) -> Tuple[str, ...]: return sys.executable, "-mpytest" - def runpython(self, script) -> RunResult: - """Run a python script using sys.executable as interpreter. - - :rtype: RunResult - """ + def runpython(self, script: "os.PathLike[str]") -> RunResult: + """Run a python script using sys.executable as interpreter.""" return self.run(sys.executable, script) - def runpython_c(self, command): - """Run python -c "command". - - :rtype: RunResult - """ + def runpython_c(self, command: str) -> RunResult: + """Run ``python -c "command"``.""" return self.run(sys.executable, "-c", command) - def runpytest_subprocess(self, *args, timeout: Optional[float] = None) -> RunResult: + def runpytest_subprocess( + self, *args: Union[str, "os.PathLike[str]"], timeout: Optional[float] = None + ) -> RunResult: """Run pytest as a subprocess with given arguments. Any plugins added to the :py:attr:`plugins` list will be added using the @@ -1454,8 +1454,6 @@ def runpytest_subprocess(self, *args, timeout: Optional[float] = None) -> RunRes :param timeout: The period in seconds after which to timeout and raise :py:class:`Pytester.TimeoutExpired`. - - :rtype: RunResult """ __tracebackhide__ = True p = make_numbered_dir(root=self.path, prefix="runpytest-") @@ -1529,9 +1527,9 @@ class Testdir: __test__ = False - CLOSE_STDIN = Pytester.CLOSE_STDIN - TimeoutExpired = Pytester.TimeoutExpired - Session = Pytester.Session + CLOSE_STDIN: "Final" = Pytester.CLOSE_STDIN + TimeoutExpired: "Final" = Pytester.TimeoutExpired + Session: "Final" = Pytester.Session def __init__(self, pytester: Pytester, *, _ispytest: bool = False) -> None: check_ispytest(_ispytest) @@ -1695,8 +1693,8 @@ def collect_by_name( def popen( self, cmdargs, - stdout: Union[int, TextIO] = subprocess.PIPE, - stderr: Union[int, TextIO] = subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, stdin=CLOSE_STDIN, **kw, ): From d358a060add416e11b0e231cbfe9d97b02335ad0 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Thu, 4 Feb 2021 11:52:13 +0200 Subject: [PATCH 122/630] config/argparsing: use proper deprecations instead of ad-hoc DeprecationWarning Proper for removing this in the next major pytest release. --- changelog/8315.deprecation.rst | 5 +++++ doc/en/deprecations.rst | 13 +++++++++++++ src/_pytest/config/argparsing.py | 22 ++++++---------------- src/_pytest/deprecated.py | 19 +++++++++++++++++++ 4 files changed, 43 insertions(+), 16 deletions(-) create mode 100644 changelog/8315.deprecation.rst diff --git a/changelog/8315.deprecation.rst b/changelog/8315.deprecation.rst new file mode 100644 index 00000000000..9b49d7c2f19 --- /dev/null +++ b/changelog/8315.deprecation.rst @@ -0,0 +1,5 @@ +Several behaviors of :meth:`Parser.addoption <_pytest.config.argparsing.Parser.addoption>` are now +scheduled for removal in pytest 7 (deprecated since pytest 2.4.0): + +- ``parser.addoption(..., help=".. %default ..")`` - use ``%(default)s`` instead. +- ``parser.addoption(..., type="int/string/float/complex")`` - use ``type=int`` etc. instead. diff --git a/doc/en/deprecations.rst b/doc/en/deprecations.rst index 0dcbd8ceb36..a3d7fd49a33 100644 --- a/doc/en/deprecations.rst +++ b/doc/en/deprecations.rst @@ -18,6 +18,19 @@ Deprecated Features Below is a complete list of all pytest features which are considered deprecated. Using those features will issue :class:`PytestWarning` or subclasses, which can be filtered using :ref:`standard warning filters `. + +Backward compatibilities in ``Parser.addoption`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. deprecated:: 2.4 + +Several behaviors of :meth:`Parser.addoption <_pytest.config.argparsing.Parser.addoption>` are now +scheduled for removal in pytest 7 (deprecated since pytest 2.4.0): + +- ``parser.addoption(..., help=".. %default ..")`` - use ``%(default)s`` instead. +- ``parser.addoption(..., type="int/string/float/complex")`` - use ``type=int`` etc. instead. + + Raising ``unittest.SkipTest`` during collection ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/src/_pytest/config/argparsing.py b/src/_pytest/config/argparsing.py index 5a09ea781e6..cf738cc2b8e 100644 --- a/src/_pytest/config/argparsing.py +++ b/src/_pytest/config/argparsing.py @@ -18,6 +18,9 @@ import _pytest._io from _pytest.compat import final from _pytest.config.exceptions import UsageError +from _pytest.deprecated import ARGUMENT_PERCENT_DEFAULT +from _pytest.deprecated import ARGUMENT_TYPE_STR +from _pytest.deprecated import ARGUMENT_TYPE_STR_CHOICE if TYPE_CHECKING: from typing import NoReturn @@ -212,12 +215,7 @@ def __init__(self, *names: str, **attrs: Any) -> None: self._short_opts: List[str] = [] self._long_opts: List[str] = [] if "%default" in (attrs.get("help") or ""): - warnings.warn( - 'pytest now uses argparse. "%default" should be' - ' changed to "%(default)s" ', - DeprecationWarning, - stacklevel=3, - ) + warnings.warn(ARGUMENT_PERCENT_DEFAULT, stacklevel=3) try: typ = attrs["type"] except KeyError: @@ -227,11 +225,7 @@ def __init__(self, *names: str, **attrs: Any) -> None: if isinstance(typ, str): if typ == "choice": warnings.warn( - "`type` argument to addoption() is the string %r." - " For choices this is optional and can be omitted, " - " but when supplied should be a type (for example `str` or `int`)." - " (options: %s)" % (typ, names), - DeprecationWarning, + ARGUMENT_TYPE_STR_CHOICE.format(typ=typ, names=names), stacklevel=4, ) # argparse expects a type here take it from @@ -239,11 +233,7 @@ def __init__(self, *names: str, **attrs: Any) -> None: attrs["type"] = type(attrs["choices"][0]) else: warnings.warn( - "`type` argument to addoption() is the string %r, " - " but when supplied should be a type (for example `str` or `int`)." - " (options: %s)" % (typ, names), - DeprecationWarning, - stacklevel=4, + ARGUMENT_TYPE_STR.format(typ=typ, names=names), stacklevel=4 ) attrs["type"] = Argument._typ_map[typ] # Used in test_parseopt -> test_parse_defaultgetter. diff --git a/src/_pytest/deprecated.py b/src/_pytest/deprecated.py index fa91f909769..5efc004ac94 100644 --- a/src/_pytest/deprecated.py +++ b/src/_pytest/deprecated.py @@ -69,6 +69,25 @@ "Use pytest.skip() instead." ) +ARGUMENT_PERCENT_DEFAULT = PytestDeprecationWarning( + 'pytest now uses argparse. "%default" should be changed to "%(default)s"', +) + +ARGUMENT_TYPE_STR_CHOICE = UnformattedWarning( + PytestDeprecationWarning, + "`type` argument to addoption() is the string {typ!r}." + " For choices this is optional and can be omitted, " + " but when supplied should be a type (for example `str` or `int`)." + " (options: {names})", +) + +ARGUMENT_TYPE_STR = UnformattedWarning( + PytestDeprecationWarning, + "`type` argument to addoption() is the string {typ!r}, " + " but when supplied should be a type (for example `str` or `int`)." + " (options: {names})", +) + # You want to make some `__init__` or function "private". # From 16af1a31fd137b7af2d4404af49406d2efa9880d Mon Sep 17 00:00:00 2001 From: pytest bot Date: Fri, 5 Feb 2021 00:24:50 +0000 Subject: [PATCH 123/630] [automated] Update plugin list --- doc/en/plugin_list.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/en/plugin_list.rst b/doc/en/plugin_list.rst index 6aae603a8a7..e43f313422a 100644 --- a/doc/en/plugin_list.rst +++ b/doc/en/plugin_list.rst @@ -349,7 +349,7 @@ name `pytest-httpbin `_ Easily test your HTTP library against a local copy of httpbin Feb 11, 2019 5 - Production/Stable N/A `pytest-http-mocker `_ Pytest plugin for http mocking (via https://github.com/vilus/mocker) Oct 20, 2019 N/A N/A `pytest-httpretty `_ A thin wrapper of HTTPretty for pytest Feb 16, 2014 3 - Alpha N/A -`pytest-httpserver `_ pytest-httpserver is a httpserver for pytest Oct 18, 2020 3 - Alpha pytest ; extra == 'dev' +`pytest-httpserver `_ pytest-httpserver is a httpserver for pytest Feb 04, 2021 3 - Alpha pytest ; extra == 'dev' `pytest-httpx `_ Send responses to httpx. Nov 25, 2020 5 - Production/Stable pytest (==6.*) `pytest-hue `_ Visualise PyTest status via your Phillips Hue lights May 09, 2019 N/A N/A `pytest-hypo-25 `_ help hypo module for pytest Jan 12, 2020 3 - Alpha N/A @@ -422,7 +422,7 @@ name `pytest-manual-marker `_ pytest marker for marking manual tests Nov 28, 2018 3 - Alpha pytest `pytest-markdown `_ Test your markdown docs with pytest Jan 15, 2021 4 - Beta pytest (>=6.0.1,<7.0.0) `pytest-marker-bugzilla `_ py.test bugzilla integration plugin, using markers Jan 09, 2020 N/A N/A -`pytest-markers-presence `_ A simple plugin to detect missed pytest tags and markers" Dec 21, 2020 4 - Beta pytest (>=6.0) +`pytest-markers-presence `_ A simple plugin to detect missed pytest tags and markers" Feb 04, 2021 4 - Beta pytest (>=6.0) `pytest-markfiltration `_ UNKNOWN Nov 08, 2011 3 - Alpha N/A `pytest-mark-no-py3 `_ pytest plugin and bowler codemod to help migrate tests to Python 3 May 17, 2019 N/A pytest `pytest-marks `_ UNKNOWN Nov 23, 2012 3 - Alpha N/A @@ -645,13 +645,13 @@ name `pytest-sanic `_ a pytest plugin for Sanic Sep 24, 2020 N/A pytest (>=5.2) `pytest-sanity `_ Dec 07, 2020 N/A N/A `pytest-sa-pg `_ May 14, 2019 N/A N/A -`pytest-sbase `_ A complete web automation framework for end-to-end testing. Jan 31, 2021 5 - Production/Stable N/A +`pytest-sbase `_ A complete web automation framework for end-to-end testing. Feb 04, 2021 5 - Production/Stable N/A `pytest-scenario `_ pytest plugin for test scenarios Feb 06, 2017 3 - Alpha N/A `pytest-schema `_ 👍 Validate return values against a schema-like object in testing Aug 31, 2020 5 - Production/Stable pytest (>=3.5.0) `pytest-securestore `_ An encrypted password store for use within pytest cases Jun 19, 2019 4 - Beta N/A `pytest-select `_ A pytest plugin which allows to (de-)select tests from a file. Jan 18, 2019 3 - Alpha pytest (>=3.0) `pytest-selenium `_ pytest plugin for Selenium Sep 19, 2020 5 - Production/Stable pytest (>=5.0.0) -`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Jan 31, 2021 5 - Production/Stable N/A +`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Feb 04, 2021 5 - Production/Stable N/A `pytest-selenium-enhancer `_ pytest plugin for Selenium Nov 26, 2020 5 - Production/Stable N/A `pytest-selenium-pdiff `_ A pytest package implementing perceptualdiff for Selenium tests. Apr 06, 2017 2 - Pre-Alpha N/A `pytest-send-email `_ Send pytest execution result email Dec 04, 2019 N/A N/A From e3c0fd3203fb13d9a43dc6cc6e45627e5c33234a Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 4 Feb 2021 23:14:08 -0300 Subject: [PATCH 124/630] Update plugin-list every Sunday instead of everyday Every day seems a bit excessive lately, let's make it less frequent. --- .github/workflows/update-plugin-list.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/update-plugin-list.yml b/.github/workflows/update-plugin-list.yml index 3f4ec092bd9..9b071aa3d4f 100644 --- a/.github/workflows/update-plugin-list.yml +++ b/.github/workflows/update-plugin-list.yml @@ -2,8 +2,9 @@ name: Update Plugin List on: schedule: - # Run daily at midnight. - - cron: '0 0 * * *' + # At 00:00 on Sunday. + # https://crontab.guru + - cron: '0 0 * * 0' workflow_dispatch: jobs: From c604f3f0c52b51fc61ba7d208d522c2614aaa10d Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Fri, 5 Feb 2021 17:47:37 +0100 Subject: [PATCH 125/630] doc: Remove confusing fixture sentence There is no previous `test_ehlo` example (it follows much later) - and the same thing is described further down in ""Requesting" fixtures" already. --- doc/en/fixture.rst | 3 --- 1 file changed, 3 deletions(-) diff --git a/doc/en/fixture.rst b/doc/en/fixture.rst index d72a0e619b5..6ffd77920be 100644 --- a/doc/en/fixture.rst +++ b/doc/en/fixture.rst @@ -174,9 +174,6 @@ Back to fixtures "Fixtures", in the literal sense, are each of the **arrange** steps and data. They're everything that test needs to do its thing. -At a basic level, test functions request fixtures by declaring them as -arguments, as in the ``test_ehlo(smtp_connection):`` in the previous example. - In pytest, "fixtures" are functions you define that serve this purpose. But they don't have to be limited to just the **arrange** steps. They can provide the **act** step, as well, and this can be a powerful technique for designing more From bcfe253f5b5367d8537e011e3a9e56bae220d411 Mon Sep 17 00:00:00 2001 From: Pax <13646646+paxcodes@users.noreply.github.com> Date: Fri, 5 Feb 2021 12:03:58 -0800 Subject: [PATCH 126/630] Type annotation for request.param (#8319) --- src/_pytest/fixtures.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 269369642e3..6a57fffd144 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -758,7 +758,7 @@ def __init__( self, request: "FixtureRequest", scope: "_Scope", - param, + param: Any, param_index: int, fixturedef: "FixtureDef[object]", *, From f674f6da9fec826bc8c4e08781e7309322fc12cc Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sun, 31 Jan 2021 01:49:25 +0200 Subject: [PATCH 127/630] runner: add a safety assert to SetupState.prepare This ensures that the teardown for the previous item was done properly for this (next) item, i.e. there are no leftover teardowns. --- src/_pytest/hookspec.py | 6 +++--- src/_pytest/runner.py | 5 ++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/_pytest/hookspec.py b/src/_pytest/hookspec.py index 41c12a2ccd8..b0b8fd53d85 100644 --- a/src/_pytest/hookspec.py +++ b/src/_pytest/hookspec.py @@ -509,9 +509,9 @@ def pytest_runtest_teardown(item: "Item", nextitem: Optional["Item"]) -> None: :param nextitem: The scheduled-to-be-next test item (None if no further test item is - scheduled). This argument can be used to perform exact teardowns, - i.e. calling just enough finalizers so that nextitem only needs to - call setup-functions. + scheduled). This argument is used to perform exact teardowns, i.e. + calling just enough finalizers so that nextitem only needs to call + setup functions. """ diff --git a/src/_pytest/runner.py b/src/_pytest/runner.py index ae76a247271..124cf531a2d 100644 --- a/src/_pytest/runner.py +++ b/src/_pytest/runner.py @@ -479,15 +479,18 @@ def __init__(self) -> None: def prepare(self, item: Item) -> None: """Setup objects along the collector chain to the item.""" + needed_collectors = item.listchain() + # If a collector fails its setup, fail its entire subtree of items. # The setup is not retried for each item - the same exception is used. for col, (finalizers, prepare_exc) in self.stack.items(): + assert col in needed_collectors, "previous item was not torn down properly" if prepare_exc: raise prepare_exc - needed_collectors = item.listchain() for col in needed_collectors[len(self.stack) :]: assert col not in self.stack + # Push onto the stack. self.stack[col] = ([col.teardown], None) try: col.setup() From f42b68ccaa4a64b3f7ef1cfcff50b4f39b63ceb9 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sun, 31 Jan 2021 12:14:06 +0200 Subject: [PATCH 128/630] runner: rename SetupState.prepare -> setup This is the usual terminology we use, and matches better with `teardown_exact()` and `pytest_runtest_setup()`. --- src/_pytest/fixtures.py | 2 +- src/_pytest/runner.py | 22 +++++++++++----------- testing/python/fixtures.py | 6 +++--- testing/test_runner.py | 14 +++++++------- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 269369642e3..40b482d0482 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -372,7 +372,7 @@ def _fill_fixtures_impl(function: "Function") -> None: fi = fm.getfixtureinfo(function.parent, function.obj, None) function._fixtureinfo = fi request = function._request = FixtureRequest(function, _ispytest=True) - fm.session._setupstate.prepare(function) + fm.session._setupstate.setup(function) request._fillfixtures() # Prune out funcargs for jstests. newfuncargs = {} diff --git a/src/_pytest/runner.py b/src/_pytest/runner.py index 124cf531a2d..153b134fe79 100644 --- a/src/_pytest/runner.py +++ b/src/_pytest/runner.py @@ -151,7 +151,7 @@ def show_test_item(item: Item) -> None: def pytest_runtest_setup(item: Item) -> None: _update_current_test_var(item, "setup") - item.session._setupstate.prepare(item) + item.session._setupstate.setup(item) def pytest_runtest_call(item: Item) -> None: @@ -417,7 +417,7 @@ class SetupState: [] - During the setup phase of item1, prepare(item1) is called. What it does + During the setup phase of item1, setup(item1) is called. What it does is: push session to stack, run session.setup() @@ -441,7 +441,7 @@ class SetupState: [session] - During the setup phase of item2, prepare(item2) is called. What it does + During the setup phase of item2, setup(item2) is called. What it does is: push mod2 to stack, run mod2.setup() @@ -477,16 +477,16 @@ def __init__(self) -> None: ], ] = {} - def prepare(self, item: Item) -> None: + def setup(self, item: Item) -> None: """Setup objects along the collector chain to the item.""" needed_collectors = item.listchain() # If a collector fails its setup, fail its entire subtree of items. # The setup is not retried for each item - the same exception is used. - for col, (finalizers, prepare_exc) in self.stack.items(): + for col, (finalizers, exc) in self.stack.items(): assert col in needed_collectors, "previous item was not torn down properly" - if prepare_exc: - raise prepare_exc + if exc: + raise exc for col in needed_collectors[len(self.stack) :]: assert col not in self.stack @@ -494,9 +494,9 @@ def prepare(self, item: Item) -> None: self.stack[col] = ([col.teardown], None) try: col.setup() - except TEST_OUTCOME as e: - self.stack[col] = (self.stack[col][0], e) - raise e + except TEST_OUTCOME as exc: + self.stack[col] = (self.stack[col][0], exc) + raise exc def addfinalizer(self, finalizer: Callable[[], object], node: Node) -> None: """Attach a finalizer to the given node. @@ -520,7 +520,7 @@ def teardown_exact(self, nextitem: Optional[Item]) -> None: while self.stack: if list(self.stack.keys()) == needed_collectors[: len(self.stack)]: break - node, (finalizers, prepare_exc) = self.stack.popitem() + node, (finalizers, _) = self.stack.popitem() while finalizers: fin = finalizers.pop() try: diff --git a/testing/python/fixtures.py b/testing/python/fixtures.py index 3d78ebf5826..3d5099c5399 100644 --- a/testing/python/fixtures.py +++ b/testing/python/fixtures.py @@ -131,7 +131,7 @@ def test_funcarg_basic(self, pytester: Pytester) -> None: item = pytester.getitem(Path("test_funcarg_basic.py")) assert isinstance(item, Function) # Execute's item's setup, which fills fixtures. - item.session._setupstate.prepare(item) + item.session._setupstate.setup(item) del item.funcargs["request"] assert len(get_public_names(item.funcargs)) == 2 assert item.funcargs["some"] == "test_func" @@ -827,7 +827,7 @@ def test_func(something): pass req = item._request # Execute item's setup. - item.session._setupstate.prepare(item) + item.session._setupstate.setup(item) with pytest.raises(pytest.FixtureLookupError): req.getfixturevalue("notexists") @@ -855,7 +855,7 @@ def test_func(something): pass """ ) assert isinstance(item, Function) - item.session._setupstate.prepare(item) + item.session._setupstate.setup(item) item._request._fillfixtures() # successively check finalization calls parent = item.getparent(pytest.Module) diff --git a/testing/test_runner.py b/testing/test_runner.py index e3f2863079f..abb87c6d3d4 100644 --- a/testing/test_runner.py +++ b/testing/test_runner.py @@ -25,7 +25,7 @@ def test_setup(self, pytester: Pytester) -> None: item = pytester.getitem("def test_func(): pass") ss = item.session._setupstate values = [1] - ss.prepare(item) + ss.setup(item) ss.addfinalizer(values.pop, item) assert values ss.teardown_exact(None) @@ -34,7 +34,7 @@ def test_setup(self, pytester: Pytester) -> None: def test_teardown_exact_stack_empty(self, pytester: Pytester) -> None: item = pytester.getitem("def test_func(): pass") ss = item.session._setupstate - ss.prepare(item) + ss.setup(item) ss.teardown_exact(None) ss.teardown_exact(None) ss.teardown_exact(None) @@ -49,9 +49,9 @@ def test_func(): pass ) ss = item.session._setupstate with pytest.raises(ValueError): - ss.prepare(item) + ss.setup(item) with pytest.raises(ValueError): - ss.prepare(item) + ss.setup(item) def test_teardown_multiple_one_fails(self, pytester: Pytester) -> None: r = [] @@ -67,7 +67,7 @@ def fin3(): item = pytester.getitem("def test_func(): pass") ss = item.session._setupstate - ss.prepare(item) + ss.setup(item) ss.addfinalizer(fin1, item) ss.addfinalizer(fin2, item) ss.addfinalizer(fin3, item) @@ -87,7 +87,7 @@ def fin2(): item = pytester.getitem("def test_func(): pass") ss = item.session._setupstate - ss.prepare(item) + ss.setup(item) ss.addfinalizer(fin1, item) ss.addfinalizer(fin2, item) with pytest.raises(Exception) as err: @@ -106,7 +106,7 @@ def fin_module(): item = pytester.getitem("def test_func(): pass") mod = item.listchain()[-2] ss = item.session._setupstate - ss.prepare(item) + ss.setup(item) ss.addfinalizer(fin_module, mod) ss.addfinalizer(fin_func, item) with pytest.raises(Exception, match="oops1"): From 5822888d735e2cd617225686611275fa8fbafbea Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sun, 31 Jan 2021 12:23:10 +0200 Subject: [PATCH 129/630] runner: add clarifying comments on why runtestprotocol re-inits the FixtureRequest --- src/_pytest/runner.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/_pytest/runner.py b/src/_pytest/runner.py index 153b134fe79..e43dd2dc818 100644 --- a/src/_pytest/runner.py +++ b/src/_pytest/runner.py @@ -120,6 +120,8 @@ def runtestprotocol( ) -> List[TestReport]: hasrequest = hasattr(item, "_request") if hasrequest and not item._request: # type: ignore[attr-defined] + # This only happens if the item is re-run, as is done by + # pytest-rerunfailures. item._initrequest() # type: ignore[attr-defined] rep = call_and_report(item, "setup", log) reports = [rep] From 3c6bd7eb27cad576520ddae5d615df6b5ad47dc7 Mon Sep 17 00:00:00 2001 From: pytest bot Date: Sun, 7 Feb 2021 00:44:13 +0000 Subject: [PATCH 130/630] [automated] Update plugin list --- doc/en/plugin_list.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/doc/en/plugin_list.rst b/doc/en/plugin_list.rst index e43f313422a..cb70b0dcfdf 100644 --- a/doc/en/plugin_list.rst +++ b/doc/en/plugin_list.rst @@ -3,7 +3,7 @@ Plugins List PyPI projects that match "pytest-\*" are considered plugins and are listed automatically. Packages classified as inactive are excluded. -This list contains 821 plugins. +This list contains 822 plugins. ============================================================================================================== ======================================================================================================================================================================== ============== ===================== ============================================ name summary last release status requires @@ -499,6 +499,7 @@ name `pytest-oot `_ Run object-oriented tests in a simple format Sep 18, 2016 4 - Beta N/A `pytest-openfiles `_ Pytest plugin for detecting inadvertent open file handles Apr 16, 2020 3 - Alpha pytest (>=4.6) `pytest-opentmi `_ pytest plugin for publish results to opentmi Jun 10, 2020 5 - Production/Stable pytest (>=5.0) +`pytest-operator `_ Fixtures for Operators Feb 04, 2021 N/A N/A `pytest-optional `_ include/exclude values of fixtures in pytest Oct 07, 2015 N/A N/A `pytest-optional-tests `_ Easy declaration of optional tests (i.e., that are not run by default) Jul 09, 2019 4 - Beta pytest (>=4.5.0) `pytest-orchestration `_ A pytest plugin for orchestrating tests Jul 18, 2019 N/A N/A @@ -630,7 +631,7 @@ name `pytest-reverse `_ Pytest plugin to reverse test order. Dec 27, 2020 5 - Production/Stable pytest `pytest-ringo `_ pytest plugin to test webapplications using the Ringo webframework Sep 27, 2017 3 - Alpha N/A `pytest-rng `_ Fixtures for seeding tests and making randomness reproducible Aug 08, 2019 5 - Production/Stable pytest -`pytest-roast `_ pytest plugin for ROAST configuration override and fixtures Jan 14, 2021 5 - Production/Stable pytest (<6) +`pytest-roast `_ pytest plugin for ROAST configuration override and fixtures Feb 05, 2021 5 - Production/Stable pytest (<6) `pytest-rotest `_ Pytest integration with rotest Sep 08, 2019 N/A pytest (>=3.5.0) `pytest-rpc `_ Extend py.test for RPC OpenStack testing. Feb 22, 2019 4 - Beta pytest (~=3.6) `pytest-rt `_ pytest data collector plugin for Testgr Jan 24, 2021 N/A N/A @@ -645,13 +646,13 @@ name `pytest-sanic `_ a pytest plugin for Sanic Sep 24, 2020 N/A pytest (>=5.2) `pytest-sanity `_ Dec 07, 2020 N/A N/A `pytest-sa-pg `_ May 14, 2019 N/A N/A -`pytest-sbase `_ A complete web automation framework for end-to-end testing. Feb 04, 2021 5 - Production/Stable N/A +`pytest-sbase `_ A complete web automation framework for end-to-end testing. Feb 06, 2021 5 - Production/Stable N/A `pytest-scenario `_ pytest plugin for test scenarios Feb 06, 2017 3 - Alpha N/A `pytest-schema `_ 👍 Validate return values against a schema-like object in testing Aug 31, 2020 5 - Production/Stable pytest (>=3.5.0) `pytest-securestore `_ An encrypted password store for use within pytest cases Jun 19, 2019 4 - Beta N/A `pytest-select `_ A pytest plugin which allows to (de-)select tests from a file. Jan 18, 2019 3 - Alpha pytest (>=3.0) `pytest-selenium `_ pytest plugin for Selenium Sep 19, 2020 5 - Production/Stable pytest (>=5.0.0) -`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Feb 04, 2021 5 - Production/Stable N/A +`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Feb 06, 2021 5 - Production/Stable N/A `pytest-selenium-enhancer `_ pytest plugin for Selenium Nov 26, 2020 5 - Production/Stable N/A `pytest-selenium-pdiff `_ A pytest package implementing perceptualdiff for Selenium tests. Apr 06, 2017 2 - Pre-Alpha N/A `pytest-send-email `_ Send pytest execution result email Dec 04, 2019 N/A N/A From e8d7a7b843972399d457ab6a912855bac90e61fc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Feb 2021 03:01:58 +0000 Subject: [PATCH 131/630] build(deps): bump anyio[curio,trio] in /testing/plugins_integration Bumps [anyio[curio,trio]](https://github.com/agronholm/anyio) from 2.0.2 to 2.1.0. - [Release notes](https://github.com/agronholm/anyio/releases) - [Changelog](https://github.com/agronholm/anyio/blob/master/docs/versionhistory.rst) - [Commits](https://github.com/agronholm/anyio/compare/2.0.2...2.1.0) Signed-off-by: dependabot[bot] --- testing/plugins_integration/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/plugins_integration/requirements.txt b/testing/plugins_integration/requirements.txt index 86c2a862c9b..f92f62c6967 100644 --- a/testing/plugins_integration/requirements.txt +++ b/testing/plugins_integration/requirements.txt @@ -1,4 +1,4 @@ -anyio[curio,trio]==2.0.2 +anyio[curio,trio]==2.1.0 django==3.1.5 pytest-asyncio==0.14.0 pytest-bdd==4.0.2 From ef14f286a389c930f54fc4a424fba4e0a5730c1f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Feb 2021 03:02:01 +0000 Subject: [PATCH 132/630] build(deps): bump django in /testing/plugins_integration Bumps [django](https://github.com/django/django) from 3.1.5 to 3.1.6. - [Release notes](https://github.com/django/django/releases) - [Commits](https://github.com/django/django/compare/3.1.5...3.1.6) Signed-off-by: dependabot[bot] --- testing/plugins_integration/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/plugins_integration/requirements.txt b/testing/plugins_integration/requirements.txt index 86c2a862c9b..d9337f6e034 100644 --- a/testing/plugins_integration/requirements.txt +++ b/testing/plugins_integration/requirements.txt @@ -1,5 +1,5 @@ anyio[curio,trio]==2.0.2 -django==3.1.5 +django==3.1.6 pytest-asyncio==0.14.0 pytest-bdd==4.0.2 pytest-cov==2.11.1 From 6432fc23029c650d7244d9bd103cc7b7ea161a93 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 8 Feb 2021 16:50:48 +0000 Subject: [PATCH 133/630] [pre-commit.ci] pre-commit autoupdate --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2eed21fc883..6b2d09e814e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -29,12 +29,12 @@ repos: - flake8-typing-imports==1.9.0 - flake8-docstrings==1.5.0 - repo: https://github.com/asottile/reorder_python_imports - rev: v2.3.6 + rev: v2.4.0 hooks: - id: reorder-python-imports args: ['--application-directories=.:src', --py36-plus] - repo: https://github.com/asottile/pyupgrade - rev: v2.9.0 + rev: v2.10.0 hooks: - id: pyupgrade args: [--py36-plus] @@ -43,7 +43,7 @@ repos: hooks: - id: setup-cfg-fmt - repo: https://github.com/pre-commit/pygrep-hooks - rev: v1.7.0 + rev: v1.7.1 hooks: - id: python-use-type-annotations - repo: https://github.com/pre-commit/mirrors-mypy From 3d831225bb3e3934e592b9573ab665f4d2922e83 Mon Sep 17 00:00:00 2001 From: Sylvain Bellemare Date: Wed, 10 Feb 2021 22:17:39 +0000 Subject: [PATCH 134/630] Remove duplicate '>=' in setup.cfg --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 14fdb6df5c0..ab6b2fb9379 100644 --- a/setup.cfg +++ b/setup.cfg @@ -54,7 +54,7 @@ python_requires = >=3.6 package_dir = =src setup_requires = - setuptools>=>=42.0 + setuptools>=42.0 setuptools-scm>=3.4 zip_safe = no From a0ae5fd652786c18f3f9a6218abbf16a14449b45 Mon Sep 17 00:00:00 2001 From: pytest bot Date: Sun, 14 Feb 2021 00:45:06 +0000 Subject: [PATCH 135/630] [automated] Update plugin list --- doc/en/plugin_list.rst | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/doc/en/plugin_list.rst b/doc/en/plugin_list.rst index cb70b0dcfdf..c7c9fdd2957 100644 --- a/doc/en/plugin_list.rst +++ b/doc/en/plugin_list.rst @@ -3,7 +3,7 @@ Plugins List PyPI projects that match "pytest-\*" are considered plugins and are listed automatically. Packages classified as inactive are excluded. -This list contains 822 plugins. +This list contains 827 plugins. ============================================================================================================== ======================================================================================================================================================================== ============== ===================== ============================================ name summary last release status requires @@ -34,7 +34,7 @@ name `pytest-apistellar `_ apistellar plugin for pytest. Jun 18, 2019 N/A N/A `pytest-appengine `_ AppEngine integration that works well with pytest-django Feb 27, 2017 N/A N/A `pytest-appium `_ Pytest plugin for appium Dec 05, 2019 N/A N/A -`pytest-approvaltests `_ A plugin to use approvaltests with pytest Jan 31, 2021 4 - Beta pytest (>=3.5.0) +`pytest-approvaltests `_ A plugin to use approvaltests with pytest Feb 07, 2021 4 - Beta pytest (>=3.5.0) `pytest-arraydiff `_ pytest plugin to help with comparing array output from tests Dec 06, 2018 4 - Beta pytest `pytest-asgi-server `_ Convenient ASGI client/server fixtures for Pytest Dec 12, 2020 N/A pytest (>=5.4.1) `pytest-asptest `_ test Answer Set Programming programs Apr 28, 2018 4 - Beta N/A @@ -148,6 +148,7 @@ name `pytest-csv `_ CSV output for pytest. Jun 24, 2019 N/A pytest (>=4.4) `pytest-curio `_ Pytest support for curio. Oct 07, 2020 N/A N/A `pytest-curl-report `_ pytest plugin to generate curl command line report Dec 11, 2016 4 - Beta N/A +`pytest-custom-concurrency `_ Custom grouping concurrence for pytest Feb 08, 2021 N/A N/A `pytest-custom-exit-code `_ Exit pytest test session with custom exit code in different scenarios Aug 07, 2019 4 - Beta pytest (>=4.0.2) `pytest-custom-report `_ Configure the symbols displayed for test outcomes Jan 30, 2019 N/A pytest `pytest-cython `_ A plugin for testing Cython extension modules Jan 26, 2021 4 - Beta pytest (>=2.7.3) @@ -212,7 +213,7 @@ name `pytest-docker-pexpect `_ pytest plugin for writing functional tests with pexpect and docker Jan 14, 2019 N/A pytest `pytest-docker-postgresql `_ A simple plugin to use with pytest Sep 24, 2019 4 - Beta pytest (>=3.5.0) `pytest-docker-py `_ Easy to use, simple to extend, pytest plugin that minimally leverages docker-py. Nov 27, 2018 N/A pytest (==4.0.0) -`pytest-docker-registry-fixtures `_ Pytest fixtures for testing with docker registries. Feb 03, 2021 4 - Beta pytest +`pytest-docker-registry-fixtures `_ Pytest fixtures for testing with docker registries. Feb 10, 2021 4 - Beta pytest `pytest-docker-tools `_ Docker integration tests for pytest Jan 15, 2021 4 - Beta pytest (>=6.0.1,<7.0.0) `pytest-docs `_ Documentation tool for pytest Nov 11, 2018 4 - Beta pytest (>=3.5.0) `pytest-docstyle `_ pytest plugin to run pydocstyle Mar 23, 2020 3 - Alpha N/A @@ -306,6 +307,7 @@ name `pytest-freezegun `_ Wrap tests with fixtures in freeze_time Jul 19, 2020 4 - Beta pytest (>=3.0.0) `pytest-freeze-reqs `_ Check if requirement files are frozen Nov 14, 2019 N/A N/A `pytest-func-cov `_ Pytest plugin for measuring function coverage May 24, 2020 3 - Alpha pytest (>=5) +`pytest-funparam `_ An alternative way to parametrize test cases Feb 13, 2021 4 - Beta pytest (>=4.6.0) `pytest-fxa `_ pytest plugin for Firefox Accounts Aug 28, 2018 5 - Production/Stable N/A `pytest-fxtest `_ Oct 27, 2020 N/A N/A `pytest-gc `_ The garbage collector plugin for py.test Feb 01, 2018 N/A N/A @@ -313,7 +315,7 @@ name `pytest-gevent `_ Ensure that gevent is properly patched when invoking pytest Feb 25, 2020 N/A pytest `pytest-gherkin `_ A flexible framework for executing BDD gherkin tests Jul 27, 2019 3 - Alpha pytest (>=5.0.0) `pytest-ghostinspector `_ For finding/executing Ghost Inspector tests May 17, 2016 3 - Alpha N/A -`pytest-girder `_ A set of pytest fixtures for testing Girder applications. Jan 18, 2021 N/A N/A +`pytest-girder `_ A set of pytest fixtures for testing Girder applications. Feb 11, 2021 N/A N/A `pytest-git `_ Git repository fixture for py.test May 28, 2019 5 - Production/Stable pytest `pytest-gitcov `_ Pytest plugin for reporting on coverage of the last git commit. Jan 11, 2020 2 - Pre-Alpha N/A `pytest-git-fixtures `_ Pytest fixtures for testing with git. Jan 25, 2021 4 - Beta pytest @@ -337,7 +339,7 @@ name `pytest-historic `_ Custom report to display pytest historical execution records Apr 08, 2020 N/A pytest `pytest-historic-hook `_ Custom listener to store execution results into MYSQL DB, which is used for pytest-historic report Apr 08, 2020 N/A pytest `pytest-homeassistant `_ A pytest plugin for use with homeassistant custom components. Aug 12, 2020 4 - Beta N/A -`pytest-homeassistant-custom-component `_ Experimental package to automatically extract test plugins for Home Assistant custom components Feb 01, 2021 3 - Alpha pytest (==6.2.2) +`pytest-homeassistant-custom-component `_ Experimental package to automatically extract test plugins for Home Assistant custom components Feb 10, 2021 3 - Alpha pytest (==6.2.2) `pytest-honors `_ Report on tests that honor constraints, and guard against regressions Mar 06, 2020 4 - Beta N/A `pytest-hoverfly-wrapper `_ Integrates the Hoverfly HTTP proxy into Pytest Jan 31, 2021 4 - Beta N/A `pytest-html `_ pytest plugin for generating HTML reports Dec 13, 2020 5 - Production/Stable pytest (!=6.0.0,>=5.0) @@ -353,7 +355,7 @@ name `pytest-httpx `_ Send responses to httpx. Nov 25, 2020 5 - Production/Stable pytest (==6.*) `pytest-hue `_ Visualise PyTest status via your Phillips Hue lights May 09, 2019 N/A N/A `pytest-hypo-25 `_ help hypo module for pytest Jan 12, 2020 3 - Alpha N/A -`pytest-ibutsu `_ A plugin to sent pytest results to an Ibutsu server Dec 02, 2020 4 - Beta pytest +`pytest-ibutsu `_ A plugin to sent pytest results to an Ibutsu server Feb 11, 2021 4 - Beta pytest `pytest-icdiff `_ use icdiff for better error messages in pytest assertions Apr 08, 2020 4 - Beta N/A `pytest-idapro `_ A pytest plugin for idapython. Allows a pytest setup to run tests outside and inside IDA in an automated manner by runnig pytest inside IDA and by mocking idapython api Nov 03, 2018 N/A N/A `pytest-ignore-flaky `_ ignore failures from flaky tests (pytest plugin) Jan 14, 2019 5 - Production/Stable pytest (>=3.7) @@ -381,6 +383,7 @@ name `pytest-jasmine `_ Run jasmine tests from your pytest test suite Nov 04, 2017 1 - Planning N/A `pytest-jest `_ A custom jest-pytest oriented Pytest reporter May 22, 2018 4 - Beta pytest (>=3.3.2) `pytest-jira `_ py.test JIRA integration plugin, using markers Nov 29, 2019 N/A N/A +`pytest-jira-xray `_ pytest plugin to integrate tests with JIRA XRAY Feb 12, 2021 3 - Alpha pytest `pytest-jobserver `_ Limit parallel tests with posix jobserver. May 15, 2019 5 - Production/Stable pytest `pytest-joke `_ Test failures are better served with humor. Oct 08, 2019 4 - Beta pytest (>=4.2.1) `pytest-json `_ Generate JSON test reports Jan 18, 2016 4 - Beta N/A @@ -457,7 +460,7 @@ name `pytest-molecule `_ PyTest Molecule Plugin :: discover and run molecule tests Jan 25, 2021 5 - Production/Stable N/A `pytest-mongo `_ MongoDB process and client fixtures plugin for py.test. Jan 12, 2021 5 - Production/Stable pytest (>=3.0.0) `pytest-mongodb `_ pytest plugin for MongoDB fixtures Dec 07, 2019 5 - Production/Stable pytest (>=2.5.2) -`pytest-monitor `_ Pytest plugin for analyzing resource usage. Nov 20, 2020 5 - Production/Stable pytest +`pytest-monitor `_ Pytest plugin for analyzing resource usage. Feb 07, 2021 5 - Production/Stable pytest `pytest-monkeyplus `_ pytest's monkeypatch subclass with extra functionalities Sep 18, 2012 5 - Production/Stable N/A `pytest-monkeytype `_ pytest-monkeytype: Generate Monkeytype annotations from your pytest tests. Jul 29, 2020 4 - Beta N/A `pytest-moto `_ Fixtures for integration tests of AWS services,uses moto mocking library. Aug 28, 2015 1 - Planning N/A @@ -499,7 +502,7 @@ name `pytest-oot `_ Run object-oriented tests in a simple format Sep 18, 2016 4 - Beta N/A `pytest-openfiles `_ Pytest plugin for detecting inadvertent open file handles Apr 16, 2020 3 - Alpha pytest (>=4.6) `pytest-opentmi `_ pytest plugin for publish results to opentmi Jun 10, 2020 5 - Production/Stable pytest (>=5.0) -`pytest-operator `_ Fixtures for Operators Feb 04, 2021 N/A N/A +`pytest-operator `_ Fixtures for Operators Feb 12, 2021 N/A N/A `pytest-optional `_ include/exclude values of fixtures in pytest Oct 07, 2015 N/A N/A `pytest-optional-tests `_ Easy declaration of optional tests (i.e., that are not run by default) Jul 09, 2019 4 - Beta pytest (>=4.5.0) `pytest-orchestration `_ A pytest plugin for orchestrating tests Jul 18, 2019 N/A N/A @@ -515,6 +518,7 @@ name `pytest-parametrized `_ Pytest plugin for parametrizing tests with default iterables. Oct 19, 2020 5 - Production/Stable pytest `pytest-parawtf `_ Finally spell paramete?ri[sz]e correctly Dec 03, 2018 4 - Beta pytest (>=3.6.0) `pytest-pass `_ Check out https://github.com/elilutsky/pytest-pass Dec 04, 2019 N/A N/A +`pytest-passrunner `_ Pytest plugin providing the 'run_on_pass' marker Feb 10, 2021 5 - Production/Stable pytest (>=4.6.0) `pytest-paste-config `_ Allow setting the path to a paste config file Sep 18, 2013 3 - Alpha N/A `pytest-pdb `_ pytest plugin which adds pdb helper commands related to pytest. Jul 31, 2018 N/A N/A `pytest-peach `_ pytest plugin for fuzzing with Peach API Security Apr 12, 2019 4 - Beta pytest (>=2.8.7) @@ -549,7 +553,7 @@ name `pytest-pop `_ A pytest plugin to help with testing pop projects Aug 13, 2020 5 - Production/Stable pytest (>=5.4.0) `pytest-portion `_ Select a portion of the collected tests Jan 28, 2021 4 - Beta pytest (>=3.5.0) `pytest-postgres `_ Run PostgreSQL in Docker container in Pytest. Mar 22, 2020 N/A pytest -`pytest-postgresql `_ Postgresql fixtures and fixture factories for Pytest. Jan 31, 2021 5 - Production/Stable pytest (>=3.0.0) +`pytest-postgresql `_ Postgresql fixtures and fixture factories for Pytest. Feb 11, 2021 5 - Production/Stable pytest (>=3.0.0) `pytest-power `_ pytest plugin with powerful fixtures Dec 31, 2020 N/A pytest (>=5.4) `pytest-pride `_ Minitest-style test colors Apr 02, 2016 3 - Alpha N/A `pytest-print `_ pytest-print adds the printer fixture you can use to print messages to the user (directly to the pytest runner, not stdout) Oct 23, 2020 5 - Production/Stable pytest (>=3.0.0) @@ -637,7 +641,7 @@ name `pytest-rt `_ pytest data collector plugin for Testgr Jan 24, 2021 N/A N/A `pytest-rts `_ Coverage-based regression test selection (RTS) plugin for pytest Jan 29, 2021 N/A pytest `pytest-runfailed `_ implement a --failed option for pytest Mar 24, 2016 N/A N/A -`pytest-runner `_ Invoke py.test as distutils command with dependency resolution Oct 26, 2019 5 - Production/Stable pytest (!=3.7.3,>=3.5) ; extra == 'testing' +`pytest-runner `_ Invoke py.test as distutils command with dependency resolution Feb 12, 2021 5 - Production/Stable pytest (!=3.7.3,>=3.5) ; extra == 'testing' `pytest-salt `_ Pytest Salt Plugin Jan 27, 2020 4 - Beta N/A `pytest-salt-containers `_ A Pytest plugin that builds and creates docker containers Nov 09, 2016 4 - Beta N/A `pytest-salt-factories `_ Pytest Salt Plugin Jan 19, 2021 4 - Beta pytest (>=6.1.1) @@ -646,19 +650,19 @@ name `pytest-sanic `_ a pytest plugin for Sanic Sep 24, 2020 N/A pytest (>=5.2) `pytest-sanity `_ Dec 07, 2020 N/A N/A `pytest-sa-pg `_ May 14, 2019 N/A N/A -`pytest-sbase `_ A complete web automation framework for end-to-end testing. Feb 06, 2021 5 - Production/Stable N/A +`pytest-sbase `_ A complete web automation framework for end-to-end testing. Feb 13, 2021 5 - Production/Stable N/A `pytest-scenario `_ pytest plugin for test scenarios Feb 06, 2017 3 - Alpha N/A `pytest-schema `_ 👍 Validate return values against a schema-like object in testing Aug 31, 2020 5 - Production/Stable pytest (>=3.5.0) `pytest-securestore `_ An encrypted password store for use within pytest cases Jun 19, 2019 4 - Beta N/A `pytest-select `_ A pytest plugin which allows to (de-)select tests from a file. Jan 18, 2019 3 - Alpha pytest (>=3.0) `pytest-selenium `_ pytest plugin for Selenium Sep 19, 2020 5 - Production/Stable pytest (>=5.0.0) -`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Feb 06, 2021 5 - Production/Stable N/A +`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Feb 13, 2021 5 - Production/Stable N/A `pytest-selenium-enhancer `_ pytest plugin for Selenium Nov 26, 2020 5 - Production/Stable N/A `pytest-selenium-pdiff `_ A pytest package implementing perceptualdiff for Selenium tests. Apr 06, 2017 2 - Pre-Alpha N/A `pytest-send-email `_ Send pytest execution result email Dec 04, 2019 N/A N/A `pytest-sentry `_ A pytest plugin to send testrun information to Sentry.io Dec 16, 2020 N/A N/A `pytest-server-fixtures `_ Extensible server fixures for py.test May 28, 2019 5 - Production/Stable pytest -`pytest-serverless `_ Automatically mocks resources from serverless.yml in pytest using moto. Dec 26, 2020 4 - Beta N/A +`pytest-serverless `_ Automatically mocks resources from serverless.yml in pytest using moto. Feb 11, 2021 4 - Beta N/A `pytest-services `_ Services plugin for pytest testing framework Oct 30, 2020 6 - Mature N/A `pytest-session2file `_ pytest-session2file (aka: pytest-session_to_file for v0.1.0 - v0.1.2) is a py.test plugin for capturing and saving to file the stdout of py.test. Jan 26, 2021 3 - Alpha pytest `pytest-session-fixture-globalize `_ py.test plugin to make session fixtures behave as if written in conftest, even if it is written in some modules May 15, 2018 4 - Beta N/A @@ -691,12 +695,12 @@ name `pytest-spawner `_ py.test plugin to spawn process and communicate with them. Jul 31, 2015 4 - Beta N/A `pytest-spec `_ Library pytest-spec is a pytest plugin to display test execution output like a SPECIFICATION. Jan 14, 2021 N/A N/A `pytest-sphinx `_ Doctest plugin for pytest with support for Sphinx-specific doctest-directives Aug 05, 2020 4 - Beta N/A -`pytest-spiratest `_ Exports unit tests as test runs in SpiraTest/Team/Plan Oct 30, 2020 N/A N/A +`pytest-spiratest `_ Exports unit tests as test runs in SpiraTest/Team/Plan Feb 12, 2021 N/A N/A `pytest-splinter `_ Splinter plugin for pytest testing framework Dec 25, 2020 6 - Mature N/A `pytest-split `_ Pytest plugin for splitting test suite based on test execution time Apr 07, 2020 1 - Planning N/A `pytest-splitio `_ Split.io SDK integration for e2e tests Sep 22, 2020 N/A pytest (<7,>=5.0) `pytest-split-tests `_ A Pytest plugin for running a subset of your tests by splitting them in to equally sized groups. Forked from Mark Adams' original project pytest-test-groups. May 28, 2019 N/A pytest (>=2.5) -`pytest-splunk-addon `_ A Dynamic test tool for Splunk Apps and Add-ons Feb 01, 2021 N/A pytest (>5.4.0,<6.1) +`pytest-splunk-addon `_ A Dynamic test tool for Splunk Apps and Add-ons Feb 10, 2021 N/A pytest (>5.4.0,<6.1) `pytest-splunk-addon-ui-smartx `_ Library to support testing Splunk Add-on UX Jan 18, 2021 N/A N/A `pytest-splunk-env `_ pytest fixtures for interaction with Splunk Enterprise and Splunk Cloud Oct 22, 2020 N/A pytest (>=6.1.1,<7.0.0) `pytest-sqitch `_ sqitch for pytest Apr 06, 2020 4 - Beta N/A @@ -708,7 +712,7 @@ name `pytest-stepfunctions `_ A small description Jul 07, 2020 4 - Beta pytest `pytest-steps `_ Create step-wise / incremental tests in pytest. Apr 25, 2020 5 - Production/Stable N/A `pytest-stepwise `_ Run a test suite one failing test at a time. Dec 01, 2015 4 - Beta N/A -`pytest-stoq `_ A plugin to pytest stoq Nov 04, 2020 4 - Beta N/A +`pytest-stoq `_ A plugin to pytest stoq Feb 09, 2021 4 - Beta N/A `pytest-stress `_ A Pytest plugin that allows you to loop tests for a user defined amount of time. Dec 07, 2019 4 - Beta pytest (>=3.6.0) `pytest-structlog `_ Structured logging assertions Jul 16, 2020 N/A pytest `pytest-structmpd `_ provide structured temporary directory Oct 17, 2018 N/A N/A @@ -795,6 +799,7 @@ name `pytest-vcrpandas `_ Test from HTTP interactions to dataframe processed. Jan 12, 2019 4 - Beta pytest `pytest-venv `_ py.test fixture for creating a virtual environment Aug 04, 2020 4 - Beta pytest `pytest-verbose-parametrize `_ More descriptive output for parametrized py.test tests May 28, 2019 5 - Production/Stable pytest +`pytest-vimqf `_ A simple pytest plugin that will shrink pytest output when specified, to fit vim quickfix window. Feb 08, 2021 4 - Beta pytest (>=6.2.2,<7.0.0) `pytest-virtualenv `_ Virtualenv fixture for py.test May 28, 2019 5 - Production/Stable pytest `pytest-voluptuous `_ Pytest plugin for asserting data against voluptuous schema. Jun 09, 2020 N/A pytest `pytest-vscodedebug `_ A pytest plugin to easily enable debugging tests within Visual Studio Code Dec 04, 2020 4 - Beta N/A @@ -810,7 +815,7 @@ name `pytest-wholenodeid `_ pytest addon for displaying the whole node id for failures Aug 26, 2015 4 - Beta pytest (>=2.0) `pytest-winnotify `_ Windows tray notifications for py.test results. Apr 22, 2016 N/A N/A `pytest-workflow `_ A pytest plugin for configuring workflow/pipeline tests using YAML files Dec 14, 2020 5 - Production/Stable pytest (>=5.4.0) -`pytest-xdist `_ pytest xdist plugin for distributed testing and loop-on-failing modes Dec 14, 2020 5 - Production/Stable pytest (>=6.0.0) +`pytest-xdist `_ pytest xdist plugin for distributed testing and loop-on-failing modes Feb 09, 2021 5 - Production/Stable pytest (>=6.0.0) `pytest-xdist-debug-for-graingert `_ pytest xdist plugin for distributed testing and loop-on-failing modes Jul 24, 2019 5 - Production/Stable pytest (>=4.4.0) `pytest-xdist-forked `_ forked from pytest-xdist Feb 10, 2020 5 - Production/Stable pytest (>=4.4.0) `pytest-xfiles `_ Pytest fixtures providing data read from function, module or package related (x)files. Feb 27, 2018 N/A N/A From 532543b4ef0d35eeac9e8c04a937ab7190ae5988 Mon Sep 17 00:00:00 2001 From: maskypy40 <60002142+maskypy40@users.noreply.github.com> Date: Wed, 17 Feb 2021 09:33:04 +0100 Subject: [PATCH 136/630] Remove empty lines from code-block In assert.rst at line 175 and further there is an example of an assert encountering comparisons. The code-block for this example starts with a comment (line 177) and then it has 2 empty lines. Comparing this example code (test_assert2.py) with the previously mentioned example code on the same page (i.e. test_assert1.py) you can see that there should not be 2 empty lines after the comment. These 2 empty lines are removed. --- doc/en/assert.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/doc/en/assert.rst b/doc/en/assert.rst index b83e30e76db..e6a23bcf3ac 100644 --- a/doc/en/assert.rst +++ b/doc/en/assert.rst @@ -175,8 +175,6 @@ when it encounters comparisons. For example: .. code-block:: python # content of test_assert2.py - - def test_set_comparison(): set1 = set("1308") set2 = set("8035") From 565fe0fb7d19a5cb66fe0d11a273d38fad856d28 Mon Sep 17 00:00:00 2001 From: Vincent Poulailleau Date: Thu, 18 Feb 2021 15:27:39 +0100 Subject: [PATCH 137/630] Update number of plugins According to the source, there are 801 plugins now! --- doc/en/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/index.rst b/doc/en/index.rst index 0361805d95a..084725ec2c1 100644 --- a/doc/en/index.rst +++ b/doc/en/index.rst @@ -66,7 +66,7 @@ Features - Python 3.6+ and PyPy 3 -- Rich plugin architecture, with over 315+ :doc:`external plugins ` and thriving community +- Rich plugin architecture, with over 800+ :doc:`external plugins ` and thriving community Documentation From e6012612b9a7fe2ef6a6c5f46b95855d5977d438 Mon Sep 17 00:00:00 2001 From: Feanil Patel Date: Fri, 19 Feb 2021 13:46:29 -0500 Subject: [PATCH 138/630] Remove a redundant paragraph doc This paragraph looks like it is a more verbose version of the sentence right above it. Removing it doesn't reduce the amount of information here but does make the section flow a little better. --- doc/en/fixture.rst | 6 ------ 1 file changed, 6 deletions(-) diff --git a/doc/en/fixture.rst b/doc/en/fixture.rst index 6ffd77920be..028786f6523 100644 --- a/doc/en/fixture.rst +++ b/doc/en/fixture.rst @@ -228,12 +228,6 @@ At a basic level, test functions request fixtures by declaring them as arguments, as in the ``test_my_fruit_in_basket(my_fruit, fruit_basket):`` in the previous example. -At a basic level, pytest depends on a test to tell it what fixtures it needs, so -we have to build that information into the test itself. We have to make the test -"**request**" the fixtures it depends on, and to do this, we have to -list those fixtures as parameters in the test function's "signature" (which is -the ``def test_something(blah, stuff, more):`` line). - When pytest goes to run a test, it looks at the parameters in that test function's signature, and then searches for fixtures that have the same names as those parameters. Once pytest finds them, it runs those fixtures, captures what From 0e5e4e03e64c9a607bcaf40f8192ca3049f8db53 Mon Sep 17 00:00:00 2001 From: Matthew Hughes Date: Sat, 20 Feb 2021 18:01:42 +0000 Subject: [PATCH 139/630] Remove some unused 'tmpdir's --- testing/code/test_source.py | 4 +--- testing/python/collect.py | 2 +- testing/python/integration.py | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/testing/code/test_source.py b/testing/code/test_source.py index 083a7911f55..5f2c6b1ea54 100644 --- a/testing/code/test_source.py +++ b/testing/code/test_source.py @@ -286,9 +286,7 @@ def g(): assert lines == ["def f():", " def g():", " pass"] -def test_source_of_class_at_eof_without_newline( - tmpdir, _sys_snapshot, tmp_path: Path -) -> None: +def test_source_of_class_at_eof_without_newline(_sys_snapshot, tmp_path: Path) -> None: # this test fails because the implicit inspect.getsource(A) below # does not return the "x = 1" last line. source = Source( diff --git a/testing/python/collect.py b/testing/python/collect.py index c52fb017d0c..22ac6464b89 100644 --- a/testing/python/collect.py +++ b/testing/python/collect.py @@ -274,7 +274,7 @@ def test_function_as_object_instance_ignored(self, pytester: Pytester) -> None: pytester.makepyfile( """ class A(object): - def __call__(self, tmpdir): + def __call__(self): 0/0 test_a = A() diff --git a/testing/python/integration.py b/testing/python/integration.py index 8576fcee341..77ebfa23ef2 100644 --- a/testing/python/integration.py +++ b/testing/python/integration.py @@ -234,7 +234,7 @@ def mock_basename(path): @mock.patch("os.path.abspath") @mock.patch("os.path.normpath") @mock.patch("os.path.basename", new=mock_basename) - def test_someting(normpath, abspath, tmpdir): + def test_someting(normpath, abspath): abspath.return_value = "this" os.path.normpath(os.path.abspath("hello")) normpath.assert_any_call("this") From 09d4c5e30a54ea1a914d75d19e0762c53a264566 Mon Sep 17 00:00:00 2001 From: Matthew Hughes Date: Sat, 20 Feb 2021 18:05:43 +0000 Subject: [PATCH 140/630] Rename variables 'tmpdir'->'tmp_path' Rename this variables reflecting the migrations made with 3bcd316f0 and ed658d682 --- testing/test_collection.py | 50 ++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/testing/test_collection.py b/testing/test_collection.py index 3dd9283eced..cf34ef118ca 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -127,16 +127,16 @@ def test_foo(): class TestCollectFS: def test_ignored_certain_directories(self, pytester: Pytester) -> None: - tmpdir = pytester.path - ensure_file(tmpdir / "build" / "test_notfound.py") - ensure_file(tmpdir / "dist" / "test_notfound.py") - ensure_file(tmpdir / "_darcs" / "test_notfound.py") - ensure_file(tmpdir / "CVS" / "test_notfound.py") - ensure_file(tmpdir / "{arch}" / "test_notfound.py") - ensure_file(tmpdir / ".whatever" / "test_notfound.py") - ensure_file(tmpdir / ".bzr" / "test_notfound.py") - ensure_file(tmpdir / "normal" / "test_found.py") - for x in Path(str(tmpdir)).rglob("test_*.py"): + tmp_path = pytester.path + ensure_file(tmp_path / "build" / "test_notfound.py") + ensure_file(tmp_path / "dist" / "test_notfound.py") + ensure_file(tmp_path / "_darcs" / "test_notfound.py") + ensure_file(tmp_path / "CVS" / "test_notfound.py") + ensure_file(tmp_path / "{arch}" / "test_notfound.py") + ensure_file(tmp_path / ".whatever" / "test_notfound.py") + ensure_file(tmp_path / ".bzr" / "test_notfound.py") + ensure_file(tmp_path / "normal" / "test_found.py") + for x in Path(str(tmp_path)).rglob("test_*.py"): x.write_text("def test_hello(): pass", "utf-8") result = pytester.runpytest("--collect-only") @@ -226,10 +226,12 @@ def test_custom_norecursedirs(self, pytester: Pytester) -> None: norecursedirs = mydir xyz* """ ) - tmpdir = pytester.path - ensure_file(tmpdir / "mydir" / "test_hello.py").write_text("def test_1(): pass") - ensure_file(tmpdir / "xyz123" / "test_2.py").write_text("def test_2(): 0/0") - ensure_file(tmpdir / "xy" / "test_ok.py").write_text("def test_3(): pass") + tmp_path = pytester.path + ensure_file(tmp_path / "mydir" / "test_hello.py").write_text( + "def test_1(): pass" + ) + ensure_file(tmp_path / "xyz123" / "test_2.py").write_text("def test_2(): 0/0") + ensure_file(tmp_path / "xy" / "test_ok.py").write_text("def test_3(): pass") rec = pytester.inline_run() rec.assertoutcome(passed=1) rec = pytester.inline_run("xyz123/test_2.py") @@ -242,10 +244,10 @@ def test_testpaths_ini(self, pytester: Pytester, monkeypatch: MonkeyPatch) -> No testpaths = gui uts """ ) - tmpdir = pytester.path - ensure_file(tmpdir / "env" / "test_1.py").write_text("def test_env(): pass") - ensure_file(tmpdir / "gui" / "test_2.py").write_text("def test_gui(): pass") - ensure_file(tmpdir / "uts" / "test_3.py").write_text("def test_uts(): pass") + tmp_path = pytester.path + ensure_file(tmp_path / "env" / "test_1.py").write_text("def test_env(): pass") + ensure_file(tmp_path / "gui" / "test_2.py").write_text("def test_gui(): pass") + ensure_file(tmp_path / "uts" / "test_3.py").write_text("def test_uts(): pass") # executing from rootdir only tests from `testpaths` directories # are collected @@ -255,7 +257,7 @@ def test_testpaths_ini(self, pytester: Pytester, monkeypatch: MonkeyPatch) -> No # check that explicitly passing directories in the command-line # collects the tests for dirname in ("env", "gui", "uts"): - items, reprec = pytester.inline_genitems(tmpdir.joinpath(dirname)) + items, reprec = pytester.inline_genitems(tmp_path.joinpath(dirname)) assert [x.name for x in items] == ["test_%s" % dirname] # changing cwd to each subdirectory and running pytest without @@ -628,9 +630,9 @@ def test_method(self): class Test_getinitialnodes: def test_global_file(self, pytester: Pytester) -> None: - tmpdir = pytester.path - x = ensure_file(tmpdir / "x.py") - with tmpdir.cwd(): + tmp_path = pytester.path + x = ensure_file(tmp_path / "x.py") + with tmp_path.cwd(): config = pytester.parseconfigure(x) col = pytester.getnode(config, x) assert isinstance(col, pytest.Module) @@ -645,8 +647,8 @@ def test_pkgfile(self, pytester: Pytester) -> None: The parent chain should match: Module -> Package -> Session. Session's parent should always be None. """ - tmpdir = pytester.path - subdir = tmpdir.joinpath("subdir") + tmp_path = pytester.path + subdir = tmp_path.joinpath("subdir") x = ensure_file(subdir / "x.py") ensure_file(subdir / "__init__.py") with subdir.cwd(): From 56421aed0127d034841fc3af8b5039c1b63fe983 Mon Sep 17 00:00:00 2001 From: pytest bot Date: Sun, 21 Feb 2021 00:46:01 +0000 Subject: [PATCH 141/630] [automated] Update plugin list --- doc/en/plugin_list.rst | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/doc/en/plugin_list.rst b/doc/en/plugin_list.rst index c7c9fdd2957..5d93204bcd4 100644 --- a/doc/en/plugin_list.rst +++ b/doc/en/plugin_list.rst @@ -3,7 +3,7 @@ Plugins List PyPI projects that match "pytest-\*" are considered plugins and are listed automatically. Packages classified as inactive are excluded. -This list contains 827 plugins. +This list contains 831 plugins. ============================================================================================================== ======================================================================================================================================================================== ============== ===================== ============================================ name summary last release status requires @@ -29,7 +29,7 @@ name `pytest-ansible-playbook `_ Pytest fixture which runs given ansible playbook file. Mar 08, 2019 4 - Beta N/A `pytest-ansible-playbook-runner `_ Pytest fixture which runs given ansible playbook file. Dec 02, 2020 4 - Beta pytest (>=3.1.0) `pytest-antilru `_ Bust functools.lru_cache when running pytest to avoid test pollution Apr 11, 2019 5 - Production/Stable pytest -`pytest-anything `_ Pytest fixtures to assert anything and something Apr 03, 2020 N/A N/A +`pytest-anything `_ Pytest fixtures to assert anything and something Feb 18, 2021 N/A N/A `pytest-aoc `_ Downloads puzzle inputs for Advent of Code and synthesizes PyTest fixtures Dec 01, 2020 N/A pytest ; extra == 'dev' `pytest-apistellar `_ apistellar plugin for pytest. Jun 18, 2019 N/A N/A `pytest-appengine `_ AppEngine integration that works well with pytest-django Feb 27, 2017 N/A N/A @@ -73,6 +73,7 @@ name `pytest-black `_ A pytest plugin to enable format checking with black Oct 05, 2020 4 - Beta N/A `pytest-black-multipy `_ Allow '--black' on older Pythons Jan 14, 2021 5 - Production/Stable pytest (!=3.7.3,>=3.5) ; extra == 'testing' `pytest-blame `_ A pytest plugin helps developers to debug by providing useful commits history. May 04, 2019 N/A pytest (>=4.4.0) +`pytest-blender `_ Blender Pytest plugin. Feb 15, 2021 N/A pytest (==6.2.1) ; extra == 'dev' `pytest-blink1 `_ Pytest plugin to emit notifications via the Blink(1) RGB LED Jan 07, 2018 4 - Beta N/A `pytest-blockage `_ Disable network requests during a test run. Feb 13, 2019 N/A pytest `pytest-blocker `_ pytest plugin to mark a test as blocker and skip all other tests Sep 07, 2015 4 - Beta N/A @@ -95,7 +96,7 @@ name `pytest-canonical-data `_ A plugin which allows to compare results with canonical results, based on previous runs May 08, 2020 2 - Pre-Alpha pytest (>=3.5.0) `pytest-caprng `_ A plugin that replays pRNG state on failure. May 02, 2018 4 - Beta N/A `pytest-capture-deprecatedwarnings `_ pytest plugin to capture all deprecatedwarnings and put them in one file Apr 30, 2019 N/A N/A -`pytest-cases `_ Separate test code from test cases in pytest. Jan 25, 2021 5 - Production/Stable N/A +`pytest-cases `_ Separate test code from test cases in pytest. Feb 19, 2021 5 - Production/Stable N/A `pytest-cassandra `_ Cassandra CCM Test Fixtures for pytest Nov 04, 2017 1 - Planning N/A `pytest-catchlog `_ py.test plugin to catch log messages. This is a fork of pytest-capturelog. Jan 24, 2016 4 - Beta pytest (>=2.6) `pytest-catch-server `_ Pytest plugin with server for catching HTTP requests. Dec 12, 2019 5 - Production/Stable N/A @@ -157,7 +158,7 @@ name `pytest-data `_ Useful functions for managing data for pytest fixtures Nov 01, 2016 5 - Production/Stable N/A `pytest-databricks `_ Pytest plugin for remote Databricks notebooks testing Jul 29, 2020 N/A pytest `pytest-datadir `_ pytest plugin for test data directories and files Oct 22, 2019 5 - Production/Stable pytest (>=2.7.0) -`pytest-datadir-mgr `_ Manager for test data providing downloads, caching of generated files, and a context for temp directories. Jan 08, 2021 5 - Production/Stable pytest (>=6.0.1,<7.0.0) +`pytest-datadir-mgr `_ Manager for test data providing downloads, caching of generated files, and a context for temp directories. Feb 17, 2021 5 - Production/Stable pytest (>=6.0.1,<7.0.0) `pytest-datadir-ng `_ Fixtures for pytest allowing test functions/methods to easily retrieve test resources from the local filesystem. Dec 25, 2019 5 - Production/Stable pytest `pytest-data-file `_ Fixture "data" and "case_data" for test from yaml file Dec 04, 2019 N/A N/A `pytest-datafiles `_ py.test plugin to create a 'tmpdir' containing predefined files/directories. Oct 07, 2018 5 - Production/Stable pytest (>=3.6) @@ -183,7 +184,7 @@ name `pytest-diffeo `_ Common py.test support for Diffeo packages Apr 08, 2016 3 - Alpha N/A `pytest-disable `_ pytest plugin to disable a test and skip it from testrun Sep 10, 2015 4 - Beta N/A `pytest-disable-plugin `_ Disable plugins per test Feb 28, 2019 4 - Beta pytest (>=3.5.0) -`pytest-discord `_ A pytest plugin to notify test results to a Discord channel. Aug 15, 2020 3 - Alpha pytest (!=6.0.0,<7,>=3.3.2) +`pytest-discord `_ A pytest plugin to notify test results to a Discord channel. Feb 14, 2021 3 - Alpha pytest (!=6.0.0,<7,>=3.3.2) `pytest-django `_ A Django plugin for pytest. Oct 22, 2020 5 - Production/Stable pytest (>=5.4.0) `pytest-django-ahead `_ A Django plugin for pytest. Oct 27, 2016 5 - Production/Stable pytest (>=2.9) `pytest-djangoapp `_ Nice pytest plugin to help you with Django pluggable application testing. Sep 21, 2020 4 - Beta N/A @@ -213,7 +214,7 @@ name `pytest-docker-pexpect `_ pytest plugin for writing functional tests with pexpect and docker Jan 14, 2019 N/A pytest `pytest-docker-postgresql `_ A simple plugin to use with pytest Sep 24, 2019 4 - Beta pytest (>=3.5.0) `pytest-docker-py `_ Easy to use, simple to extend, pytest plugin that minimally leverages docker-py. Nov 27, 2018 N/A pytest (==4.0.0) -`pytest-docker-registry-fixtures `_ Pytest fixtures for testing with docker registries. Feb 10, 2021 4 - Beta pytest +`pytest-docker-registry-fixtures `_ Pytest fixtures for testing with docker registries. Feb 17, 2021 4 - Beta pytest `pytest-docker-tools `_ Docker integration tests for pytest Jan 15, 2021 4 - Beta pytest (>=6.0.1,<7.0.0) `pytest-docs `_ Documentation tool for pytest Nov 11, 2018 4 - Beta pytest (>=3.5.0) `pytest-docstyle `_ pytest plugin to run pydocstyle Mar 23, 2020 3 - Alpha N/A @@ -351,7 +352,7 @@ name `pytest-httpbin `_ Easily test your HTTP library against a local copy of httpbin Feb 11, 2019 5 - Production/Stable N/A `pytest-http-mocker `_ Pytest plugin for http mocking (via https://github.com/vilus/mocker) Oct 20, 2019 N/A N/A `pytest-httpretty `_ A thin wrapper of HTTPretty for pytest Feb 16, 2014 3 - Alpha N/A -`pytest-httpserver `_ pytest-httpserver is a httpserver for pytest Feb 04, 2021 3 - Alpha pytest ; extra == 'dev' +`pytest-httpserver `_ pytest-httpserver is a httpserver for pytest Feb 14, 2021 3 - Alpha pytest ; extra == 'dev' `pytest-httpx `_ Send responses to httpx. Nov 25, 2020 5 - Production/Stable pytest (==6.*) `pytest-hue `_ Visualise PyTest status via your Phillips Hue lights May 09, 2019 N/A N/A `pytest-hypo-25 `_ help hypo module for pytest Jan 12, 2020 3 - Alpha N/A @@ -366,7 +367,7 @@ name `pytest-informative-node `_ display more node ininformation. Apr 25, 2019 4 - Beta N/A `pytest-infrastructure `_ pytest stack validation prior to testing executing Apr 12, 2020 4 - Beta N/A `pytest-inmanta `_ A py.test plugin providing fixtures to simplify inmanta modules testing. Oct 12, 2020 5 - Production/Stable N/A -`pytest-inmanta-extensions `_ Inmanta tests package Nov 25, 2020 5 - Production/Stable N/A +`pytest-inmanta-extensions `_ Inmanta tests package Jan 07, 2021 5 - Production/Stable N/A `pytest-Inomaly `_ A simple image diff plugin for pytest Feb 13, 2018 4 - Beta N/A `pytest-insta `_ A practical snapshot testing plugin for pytest Nov 29, 2020 N/A pytest (>=6.0.2,<7.0.0) `pytest-instafail `_ pytest plugin to show failures instantly Jun 14, 2020 4 - Beta pytest (>=2.9) @@ -452,7 +453,7 @@ name `pytest-mock-helper `_ Help you mock HTTP call and generate mock code Jan 24, 2018 N/A pytest `pytest-mockito `_ Base fixtures for mockito Jul 11, 2018 4 - Beta N/A `pytest-mockredis `_ An in-memory mock of a Redis server that runs in a separate thread. This is to be used for unit-tests that require a Redis database. Jan 02, 2018 2 - Pre-Alpha N/A -`pytest-mock-resources `_ A pytest plugin for easily instantiating reproducible mock resources. Oct 08, 2020 N/A pytest (>=1.0) +`pytest-mock-resources `_ A pytest plugin for easily instantiating reproducible mock resources. Feb 17, 2021 N/A pytest (>=1.0) `pytest-mock-server `_ Mock server plugin for pytest Apr 06, 2020 4 - Beta N/A `pytest-mockservers `_ A set of fixtures to test your requests to HTTP/UDP servers Mar 31, 2020 N/A pytest (>=4.3.0) `pytest-modifyjunit `_ Utility for adding additional properties to junit xml for IDM QE Jan 10, 2019 N/A N/A @@ -474,6 +475,7 @@ name `pytest-mypy `_ Mypy static type checker plugin for Pytest Nov 14, 2020 4 - Beta pytest (>=3.5) `pytest-mypyd `_ Mypy static type checker plugin for Pytest Aug 20, 2019 4 - Beta pytest (<4.7,>=2.8) ; python_version < "3.5" `pytest-mypy-plugins `_ pytest plugin for writing tests for mypy plugins Oct 26, 2020 3 - Alpha pytest (>=6.0.0) +`pytest-mypy-plugins-shim `_ Substitute for "pytest-mypy-plugins" for Python implementations which aren't supported by mypy. Feb 14, 2021 N/A pytest (>=6.0.0) `pytest-mypy-testing `_ Pytest plugin to check mypy output. Apr 24, 2020 N/A pytest `pytest-mysql `_ MySQL process and client fixtures for pytest Jul 21, 2020 5 - Production/Stable pytest (>=3.0.0) `pytest-needle `_ pytest plugin for visual testing websites using selenium Dec 10, 2018 4 - Beta pytest (<5.0.0,>=3.0.0) @@ -502,11 +504,11 @@ name `pytest-oot `_ Run object-oriented tests in a simple format Sep 18, 2016 4 - Beta N/A `pytest-openfiles `_ Pytest plugin for detecting inadvertent open file handles Apr 16, 2020 3 - Alpha pytest (>=4.6) `pytest-opentmi `_ pytest plugin for publish results to opentmi Jun 10, 2020 5 - Production/Stable pytest (>=5.0) -`pytest-operator `_ Fixtures for Operators Feb 12, 2021 N/A N/A +`pytest-operator `_ Fixtures for Operators Feb 20, 2021 N/A N/A `pytest-optional `_ include/exclude values of fixtures in pytest Oct 07, 2015 N/A N/A `pytest-optional-tests `_ Easy declaration of optional tests (i.e., that are not run by default) Jul 09, 2019 4 - Beta pytest (>=4.5.0) `pytest-orchestration `_ A pytest plugin for orchestrating tests Jul 18, 2019 N/A N/A -`pytest-order `_ pytest plugin to run your tests in a specific order Jan 27, 2021 4 - Beta pytest (>=3.7) +`pytest-order `_ pytest plugin to run your tests in a specific order Feb 16, 2021 4 - Beta pytest (>=3.7) `pytest-ordering `_ pytest plugin to run your tests in a specific order Nov 14, 2018 4 - Beta pytest `pytest-osxnotify `_ OS X notifications for py.test results. May 15, 2015 N/A N/A `pytest-pact `_ A simple plugin to use with pytest Jan 07, 2019 4 - Beta N/A @@ -571,7 +573,7 @@ name `pytest-pylint `_ pytest plugin to check source code with pylint Nov 09, 2020 5 - Production/Stable pytest (>=5.4) `pytest-pypi `_ Easily test your HTTP library against a local copy of pypi Mar 04, 2018 3 - Alpha N/A `pytest-pypom-navigation `_ Core engine for cookiecutter-qa and pytest-play packages Feb 18, 2019 4 - Beta pytest (>=3.0.7) -`pytest-pyppeteer `_ A plugin to run pyppeteer in pytest. Nov 27, 2020 4 - Beta pytest (>=6.0.2) +`pytest-pyppeteer `_ A plugin to run pyppeteer in pytest. Feb 16, 2021 4 - Beta pytest (>=6.0.2) `pytest-pyq `_ Pytest fixture "q" for pyq Mar 10, 2020 5 - Production/Stable N/A `pytest-pyramid `_ pytest pyramid providing basic fixtures for testing pyramid applications with pytest test suite Jun 05, 2020 4 - Beta pytest `pytest-pyramid-server `_ Pyramid server fixture for py.test May 28, 2019 5 - Production/Stable pytest @@ -619,13 +621,13 @@ name `pytest-reportlog `_ Replacement for the --resultlog option, focused in simplicity and extensibility Dec 11, 2020 3 - Alpha pytest (>=5.2) `pytest-report-me `_ A pytest plugin to generate report. Dec 31, 2020 N/A pytest `pytest-report-parameters `_ pytest plugin for adding tests' parameters to junit report Jun 18, 2020 3 - Alpha pytest (>=2.4.2) -`pytest-reportportal `_ Agent for Reporting results of tests to the Report Portal Dec 14, 2020 N/A pytest (>=3.0.7) +`pytest-reportportal `_ Agent for Reporting results of tests to the Report Portal Feb 15, 2021 N/A pytest (>=3.0.7) `pytest-reqs `_ pytest plugin to check pinned requirements May 12, 2019 N/A pytest (>=2.4.2) `pytest-requests `_ A simple plugin to use with pytest Jun 24, 2019 4 - Beta pytest (>=3.5.0) `pytest-reraise `_ Make multi-threaded pytest test cases fail when they should Jun 03, 2020 5 - Production/Stable N/A `pytest-rerun `_ Re-run only changed files in specified branch Jul 08, 2019 N/A pytest (>=3.6) `pytest-rerunfailures `_ pytest plugin to re-run tests to eliminate flaky failures Sep 29, 2020 5 - Production/Stable pytest (>=5.0) -`pytest-resilient-circuits `_ Resilient Circuits fixtures for PyTest. Jan 21, 2021 N/A N/A +`pytest-resilient-circuits `_ Resilient Circuits fixtures for PyTest. Feb 19, 2021 N/A N/A `pytest-resource `_ Load resource fixture plugin to use with pytest Nov 14, 2018 4 - Beta N/A `pytest-resource-path `_ Provides path for uniform access to test resources in isolated directory Aug 18, 2020 5 - Production/Stable pytest (>=3.5.0) `pytest-responsemock `_ Simplified requests calls mocking for pytest Oct 10, 2020 5 - Production/Stable N/A @@ -639,30 +641,30 @@ name `pytest-rotest `_ Pytest integration with rotest Sep 08, 2019 N/A pytest (>=3.5.0) `pytest-rpc `_ Extend py.test for RPC OpenStack testing. Feb 22, 2019 4 - Beta pytest (~=3.6) `pytest-rt `_ pytest data collector plugin for Testgr Jan 24, 2021 N/A N/A -`pytest-rts `_ Coverage-based regression test selection (RTS) plugin for pytest Jan 29, 2021 N/A pytest +`pytest-rts `_ Coverage-based regression test selection (RTS) plugin for pytest Feb 15, 2021 N/A pytest `pytest-runfailed `_ implement a --failed option for pytest Mar 24, 2016 N/A N/A `pytest-runner `_ Invoke py.test as distutils command with dependency resolution Feb 12, 2021 5 - Production/Stable pytest (!=3.7.3,>=3.5) ; extra == 'testing' `pytest-salt `_ Pytest Salt Plugin Jan 27, 2020 4 - Beta N/A `pytest-salt-containers `_ A Pytest plugin that builds and creates docker containers Nov 09, 2016 4 - Beta N/A -`pytest-salt-factories `_ Pytest Salt Plugin Jan 19, 2021 4 - Beta pytest (>=6.1.1) +`pytest-salt-factories `_ Pytest Salt Plugin Feb 19, 2021 4 - Beta pytest (>=6.1.1) `pytest-salt-from-filenames `_ Simple PyTest Plugin For Salt's Test Suite Specifically Jan 29, 2019 4 - Beta pytest (>=4.1) `pytest-salt-runtests-bridge `_ Simple PyTest Plugin For Salt's Test Suite Specifically Dec 05, 2019 4 - Beta pytest (>=4.1) `pytest-sanic `_ a pytest plugin for Sanic Sep 24, 2020 N/A pytest (>=5.2) `pytest-sanity `_ Dec 07, 2020 N/A N/A `pytest-sa-pg `_ May 14, 2019 N/A N/A -`pytest-sbase `_ A complete web automation framework for end-to-end testing. Feb 13, 2021 5 - Production/Stable N/A +`pytest-sbase `_ A complete web automation framework for end-to-end testing. Feb 19, 2021 5 - Production/Stable N/A `pytest-scenario `_ pytest plugin for test scenarios Feb 06, 2017 3 - Alpha N/A `pytest-schema `_ 👍 Validate return values against a schema-like object in testing Aug 31, 2020 5 - Production/Stable pytest (>=3.5.0) `pytest-securestore `_ An encrypted password store for use within pytest cases Jun 19, 2019 4 - Beta N/A `pytest-select `_ A pytest plugin which allows to (de-)select tests from a file. Jan 18, 2019 3 - Alpha pytest (>=3.0) `pytest-selenium `_ pytest plugin for Selenium Sep 19, 2020 5 - Production/Stable pytest (>=5.0.0) -`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Feb 13, 2021 5 - Production/Stable N/A +`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Feb 19, 2021 5 - Production/Stable N/A `pytest-selenium-enhancer `_ pytest plugin for Selenium Nov 26, 2020 5 - Production/Stable N/A `pytest-selenium-pdiff `_ A pytest package implementing perceptualdiff for Selenium tests. Apr 06, 2017 2 - Pre-Alpha N/A `pytest-send-email `_ Send pytest execution result email Dec 04, 2019 N/A N/A `pytest-sentry `_ A pytest plugin to send testrun information to Sentry.io Dec 16, 2020 N/A N/A `pytest-server-fixtures `_ Extensible server fixures for py.test May 28, 2019 5 - Production/Stable pytest -`pytest-serverless `_ Automatically mocks resources from serverless.yml in pytest using moto. Feb 11, 2021 4 - Beta N/A +`pytest-serverless `_ Automatically mocks resources from serverless.yml in pytest using moto. Feb 20, 2021 4 - Beta N/A `pytest-services `_ Services plugin for pytest testing framework Oct 30, 2020 6 - Mature N/A `pytest-session2file `_ pytest-session2file (aka: pytest-session_to_file for v0.1.0 - v0.1.2) is a py.test plugin for capturing and saving to file the stdout of py.test. Jan 26, 2021 3 - Alpha pytest `pytest-session-fixture-globalize `_ py.test plugin to make session fixtures behave as if written in conftest, even if it is written in some modules May 15, 2018 4 - Beta N/A @@ -682,6 +684,7 @@ name `pytest-slack `_ Pytest to Slack reporting plugin Dec 15, 2020 5 - Production/Stable N/A `pytest-smartcollect `_ A plugin for collecting tests that touch changed code Oct 04, 2018 N/A pytest (>=3.5.0) `pytest-smartcov `_ Smart coverage plugin for pytest. Sep 30, 2017 3 - Alpha N/A +`pytest-smtp `_ Send email with pytest execution result Feb 20, 2021 N/A pytest `pytest-snail `_ Plugin for adding a marker to slow running tests. 🐌 Nov 04, 2019 3 - Alpha pytest (>=5.0.1) `pytest-snapci `_ py.test plugin for Snap-CI Nov 12, 2015 N/A N/A `pytest-snapshot `_ A plugin to enable snapshot testing with pytest. Jan 22, 2021 4 - Beta pytest (>=3.0.0) @@ -706,6 +709,7 @@ name `pytest-sqitch `_ sqitch for pytest Apr 06, 2020 4 - Beta N/A `pytest-sqlalchemy `_ pytest plugin with sqlalchemy related fixtures Mar 13, 2018 3 - Alpha N/A `pytest-sql-bigquery `_ Yet another SQL-testing framework for BigQuery provided by pytest plugin Dec 19, 2019 N/A pytest +`pytest-srcpaths `_ Add paths to sys.path Feb 18, 2021 N/A N/A `pytest-ssh `_ pytest plugin for ssh command run May 27, 2019 N/A pytest `pytest-start-from `_ Start pytest run from a given point Apr 11, 2016 N/A N/A `pytest-statsd `_ pytest plugin for reporting to graphite Nov 30, 2018 5 - Production/Stable pytest (>=3.0.0) From c9bb4c418f889820f786cbf8e63cbc057c653399 Mon Sep 17 00:00:00 2001 From: Matthew Hughes Date: Sun, 21 Feb 2021 13:10:00 +0000 Subject: [PATCH 142/630] fixup! Rename variables 'tmpdir'->'tmp_path' * Add some more of these * Also reintroduce+rename instances of fixture usages that were 'tmpdir'->'tmp_path' --- testing/python/collect.py | 2 +- testing/python/integration.py | 2 +- testing/test_conftest.py | 14 +++++++------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/testing/python/collect.py b/testing/python/collect.py index 22ac6464b89..4256851e254 100644 --- a/testing/python/collect.py +++ b/testing/python/collect.py @@ -274,7 +274,7 @@ def test_function_as_object_instance_ignored(self, pytester: Pytester) -> None: pytester.makepyfile( """ class A(object): - def __call__(self): + def __call__(self, tmp_path): 0/0 test_a = A() diff --git a/testing/python/integration.py b/testing/python/integration.py index 77ebfa23ef2..1ab2149ff07 100644 --- a/testing/python/integration.py +++ b/testing/python/integration.py @@ -234,7 +234,7 @@ def mock_basename(path): @mock.patch("os.path.abspath") @mock.patch("os.path.normpath") @mock.patch("os.path.basename", new=mock_basename) - def test_someting(normpath, abspath): + def test_someting(normpath, abspath, tmp_path): abspath.return_value = "this" os.path.normpath(os.path.abspath("hello")) normpath.assert_any_call("this") diff --git a/testing/test_conftest.py b/testing/test_conftest.py index 80f2a6d0bc0..3497b7cc4fd 100644 --- a/testing/test_conftest.py +++ b/testing/test_conftest.py @@ -44,15 +44,15 @@ class TestConftestValueAccessGlobal: def basedir( self, request, tmp_path_factory: TempPathFactory ) -> Generator[Path, None, None]: - tmpdir = tmp_path_factory.mktemp("basedir", numbered=True) - tmpdir.joinpath("adir/b").mkdir(parents=True) - tmpdir.joinpath("adir/conftest.py").write_text("a=1 ; Directory = 3") - tmpdir.joinpath("adir/b/conftest.py").write_text("b=2 ; a = 1.5") + tmp_path = tmp_path_factory.mktemp("basedir", numbered=True) + tmp_path.joinpath("adir/b").mkdir(parents=True) + tmp_path.joinpath("adir/conftest.py").write_text("a=1 ; Directory = 3") + tmp_path.joinpath("adir/b/conftest.py").write_text("b=2 ; a = 1.5") if request.param == "inpackage": - tmpdir.joinpath("adir/__init__.py").touch() - tmpdir.joinpath("adir/b/__init__.py").touch() + tmp_path.joinpath("adir/__init__.py").touch() + tmp_path.joinpath("adir/b/__init__.py").touch() - yield tmpdir + yield tmp_path def test_basic_init(self, basedir: Path) -> None: conftest = PytestPluginManager() From 060cbef2604c7f0dd5122aca0e0cdd77c5b2c01f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Feb 2021 03:01:27 +0000 Subject: [PATCH 143/630] build(deps): bump django in /testing/plugins_integration Bumps [django](https://github.com/django/django) from 3.1.6 to 3.1.7. - [Release notes](https://github.com/django/django/releases) - [Commits](https://github.com/django/django/compare/3.1.6...3.1.7) Signed-off-by: dependabot[bot] --- testing/plugins_integration/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/plugins_integration/requirements.txt b/testing/plugins_integration/requirements.txt index 6460e838535..730d1f028fe 100644 --- a/testing/plugins_integration/requirements.txt +++ b/testing/plugins_integration/requirements.txt @@ -1,5 +1,5 @@ anyio[curio,trio]==2.1.0 -django==3.1.6 +django==3.1.7 pytest-asyncio==0.14.0 pytest-bdd==4.0.2 pytest-cov==2.11.1 From e503e27579cb7a8bc7bb6efb252e6aada5a17ee0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 22 Feb 2021 17:04:30 +0000 Subject: [PATCH 144/630] [pre-commit.ci] pre-commit autoupdate --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6b2d09e814e..fed7ca83cbb 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -47,7 +47,7 @@ repos: hooks: - id: python-use-type-annotations - repo: https://github.com/pre-commit/mirrors-mypy - rev: v0.800 + rev: v0.812 hooks: - id: mypy files: ^(src/|testing/) From 54a154c86f4806327081b80193cebca7934468d0 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 23 Feb 2021 17:56:42 +0100 Subject: [PATCH 145/630] Allow Class.from_parent to forward custom parameters to the constructor Similarly to #7143, at work we have a project with a custom pytest.Class subclass, adding an additional argument to the constructor. All from_parent implementations in pytest accept and forward *kw, except Class (before this change) and DoctestItem - since I'm not familiar with doctest support, I've left the latter as-is. --- changelog/8367.bugfix.rst | 1 + src/_pytest/python.py | 4 ++-- testing/test_collection.py | 18 ++++++++++++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 changelog/8367.bugfix.rst diff --git a/changelog/8367.bugfix.rst b/changelog/8367.bugfix.rst new file mode 100644 index 00000000000..f4b03670108 --- /dev/null +++ b/changelog/8367.bugfix.rst @@ -0,0 +1 @@ +Fix ``Class.from_parent`` so it forwards extra keyword arguments to the constructor. diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 726241cb5b9..944c395a84d 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -763,9 +763,9 @@ class Class(PyCollector): """Collector for test methods.""" @classmethod - def from_parent(cls, parent, *, name, obj=None): + def from_parent(cls, parent, *, name, obj=None, **kw): """The public constructor.""" - return super().from_parent(name=name, parent=parent) + return super().from_parent(name=name, parent=parent, **kw) def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]: if not safe_getattr(self.obj, "__test__", True): diff --git a/testing/test_collection.py b/testing/test_collection.py index 3dd9283eced..39538ae98cc 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -1360,6 +1360,24 @@ def from_parent(cls, parent, *, fspath, x): assert collector.x == 10 +def test_class_from_parent(pytester: Pytester, request: FixtureRequest) -> None: + """Ensure Class.from_parent can forward custom arguments to the constructor.""" + + class MyCollector(pytest.Class): + def __init__(self, name, parent, x): + super().__init__(name, parent) + self.x = x + + @classmethod + def from_parent(cls, parent, *, name, x): + return super().from_parent(parent=parent, name=name, x=x) + + collector = MyCollector.from_parent( + parent=request.session, name="foo", x=10 + ) + assert collector.x == 10 + + class TestImportModeImportlib: def test_collect_duplicate_names(self, pytester: Pytester) -> None: """--import-mode=importlib can import modules with same names that are not in packages.""" From 3b7fc2c9c839148d19518af655a1d347351286b0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 23 Feb 2021 17:02:45 +0000 Subject: [PATCH 146/630] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- testing/test_collection.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/testing/test_collection.py b/testing/test_collection.py index 39538ae98cc..298c2dde1a0 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -1372,9 +1372,7 @@ def __init__(self, name, parent, x): def from_parent(cls, parent, *, name, x): return super().from_parent(parent=parent, name=name, x=x) - collector = MyCollector.from_parent( - parent=request.session, name="foo", x=10 - ) + collector = MyCollector.from_parent(parent=request.session, name="foo", x=10) assert collector.x == 10 From 9d09d1991186d842dce4dc2ea023996b3c091fc8 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 23 Feb 2021 18:03:10 +0100 Subject: [PATCH 147/630] Fix typo in changelog See #7143 --- doc/en/changelog.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/changelog.rst b/doc/en/changelog.rst index 3e854f59971..967fca2098a 100644 --- a/doc/en/changelog.rst +++ b/doc/en/changelog.rst @@ -1028,7 +1028,7 @@ Bug Fixes - `#7110 `_: Fixed regression: ``asyncbase.TestCase`` tests are executed correctly again. -- `#7143 `_: Fix ``File.from_constructor`` so it forwards extra keyword arguments to the constructor. +- `#7143 `_: Fix ``File.from_parent`` so it forwards extra keyword arguments to the constructor. - `#7145 `_: Classes with broken ``__getattribute__`` methods are displayed correctly during failures. From 514f8e068080b374b470f6c9f349c48edfc0d3d8 Mon Sep 17 00:00:00 2001 From: Matthew Hughes Date: Wed, 24 Feb 2021 20:55:35 +0000 Subject: [PATCH 148/630] fixup! Rename variables 'tmpdir'->'tmp_path' --- testing/test_collection.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/testing/test_collection.py b/testing/test_collection.py index cf34ef118ca..9060ef36c2d 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -136,7 +136,7 @@ def test_ignored_certain_directories(self, pytester: Pytester) -> None: ensure_file(tmp_path / ".whatever" / "test_notfound.py") ensure_file(tmp_path / ".bzr" / "test_notfound.py") ensure_file(tmp_path / "normal" / "test_found.py") - for x in Path(str(tmp_path)).rglob("test_*.py"): + for x in tmp_path.rglob("test_*.py"): x.write_text("def test_hello(): pass", "utf-8") result = pytester.runpytest("--collect-only") @@ -632,8 +632,7 @@ class Test_getinitialnodes: def test_global_file(self, pytester: Pytester) -> None: tmp_path = pytester.path x = ensure_file(tmp_path / "x.py") - with tmp_path.cwd(): - config = pytester.parseconfigure(x) + config = pytester.parseconfigure(x) col = pytester.getnode(config, x) assert isinstance(col, pytest.Module) assert col.name == "x.py" From b7f2d7ca61d6169495e5780fffc252daaacd6583 Mon Sep 17 00:00:00 2001 From: Simon K Date: Thu, 25 Feb 2021 08:28:57 +0000 Subject: [PATCH 149/630] Fixed an issue where `getpass.getuser()` contained illegal characters for file directories (#8365) * retry writing pytest-of dir when invalid chars are in directory name * add unit tests for getbasetemp() and changelog * patch _basetemp & _given_basetemp for testing basetemp() * Tweak changelog for #8317, tidy up comments --- changelog/8317.bugfix.rst | 1 + src/_pytest/tmpdir.py | 7 ++++++- testing/test_tmpdir.py | 12 ++++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 changelog/8317.bugfix.rst diff --git a/changelog/8317.bugfix.rst b/changelog/8317.bugfix.rst new file mode 100644 index 00000000000..7312880a11f --- /dev/null +++ b/changelog/8317.bugfix.rst @@ -0,0 +1 @@ +Fixed an issue where illegal directory characters derived from ``getpass.getuser()`` raised an ``OSError``. diff --git a/src/_pytest/tmpdir.py b/src/_pytest/tmpdir.py index 29c7e19d7b4..47729ae5fee 100644 --- a/src/_pytest/tmpdir.py +++ b/src/_pytest/tmpdir.py @@ -115,7 +115,12 @@ def getbasetemp(self) -> Path: # use a sub-directory in the temproot to speed-up # make_numbered_dir() call rootdir = temproot.joinpath(f"pytest-of-{user}") - rootdir.mkdir(exist_ok=True) + try: + rootdir.mkdir(exist_ok=True) + except OSError: + # getuser() likely returned illegal characters for the platform, use unknown back off mechanism + rootdir = temproot.joinpath("pytest-of-unknown") + rootdir.mkdir(exist_ok=True) basetemp = make_numbered_dir_with_cleanup( prefix="pytest-", root=rootdir, keep=3, lock_timeout=LOCK_TIMEOUT ) diff --git a/testing/test_tmpdir.py b/testing/test_tmpdir.py index d123287aa38..4dec9c59a3c 100644 --- a/testing/test_tmpdir.py +++ b/testing/test_tmpdir.py @@ -11,6 +11,7 @@ import pytest from _pytest import pathlib from _pytest.config import Config +from _pytest.monkeypatch import MonkeyPatch from _pytest.pathlib import cleanup_numbered_dir from _pytest.pathlib import create_cleanup_lock from _pytest.pathlib import make_numbered_dir @@ -445,3 +446,14 @@ def test(tmp_path): # running a second time and ensure we don't crash result = pytester.runpytest("--basetemp=tmp") assert result.ret == 0 + + +def test_tmp_path_factory_handles_invalid_dir_characters( + tmp_path_factory: TempPathFactory, monkeypatch: MonkeyPatch +) -> None: + monkeypatch.setattr("getpass.getuser", lambda: "os/<:*?;>agnostic") + # _basetemp / _given_basetemp are cached / set in parallel runs, patch them + monkeypatch.setattr(tmp_path_factory, "_basetemp", None) + monkeypatch.setattr(tmp_path_factory, "_given_basetemp", None) + p = tmp_path_factory.getbasetemp() + assert "pytest-of-unknown" in str(p) From 22c0dace3b67ac2aaf8d45f6f73ed9838c30e8eb Mon Sep 17 00:00:00 2001 From: Simon K Date: Thu, 25 Feb 2021 20:32:27 +0000 Subject: [PATCH 150/630] change istestfunction to callable() (#8374) --- src/_pytest/python.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 944c395a84d..40116ab9c5a 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -384,10 +384,7 @@ def istestfunction(self, obj: object, name: str) -> bool: if isinstance(obj, staticmethod): # staticmethods need to be unwrapped. obj = safe_getattr(obj, "__func__", False) - return ( - safe_getattr(obj, "__call__", False) - and fixtures.getfixturemarker(obj) is None - ) + return callable(obj) and fixtures.getfixturemarker(obj) is None else: return False From a623b1b0861719a48d89f3ee7ac18250b7ea06ca Mon Sep 17 00:00:00 2001 From: pytest bot Date: Sun, 28 Feb 2021 00:48:46 +0000 Subject: [PATCH 151/630] [automated] Update plugin list --- doc/en/plugin_list.rst | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/doc/en/plugin_list.rst b/doc/en/plugin_list.rst index 5d93204bcd4..f50b6033ff1 100644 --- a/doc/en/plugin_list.rst +++ b/doc/en/plugin_list.rst @@ -3,7 +3,7 @@ Plugins List PyPI projects that match "pytest-\*" are considered plugins and are listed automatically. Packages classified as inactive are excluded. -This list contains 831 plugins. +This list contains 833 plugins. ============================================================================================================== ======================================================================================================================================================================== ============== ===================== ============================================ name summary last release status requires @@ -11,6 +11,7 @@ name `pytest-adaptavist `_ pytest plugin for generating test execution results within Jira Test Management (tm4j) Feb 05, 2020 N/A pytest (>=3.4.1) `pytest-adf `_ Pytest plugin for writing Azure Data Factory integration tests Jun 03, 2020 4 - Beta pytest (>=3.5.0) `pytest-aggreport `_ pytest plugin for pytest-repeat that generate aggregate report of the same test cases with additional statistics details. Jul 19, 2019 4 - Beta pytest (>=4.3.1) +`pytest-aio `_ Pytest plugin for testing async python code Feb 27, 2021 4 - Beta pytest ; extra == 'tests' `pytest-aiofiles `_ pytest fixtures for writing aiofiles tests with pyfakefs May 14, 2017 5 - Production/Stable N/A `pytest-aiohttp `_ pytest plugin for aiohttp support Dec 05, 2017 N/A pytest `pytest-aiohttp-client `_ Pytest `client` fixture for the Aiohttp Nov 01, 2020 N/A pytest (>=6) @@ -59,7 +60,7 @@ name `pytest-aws `_ pytest plugin for testing AWS resource configurations Oct 04, 2017 4 - Beta N/A `pytest-axe `_ pytest plugin for axe-selenium-python Nov 12, 2018 N/A pytest (>=3.0.0) `pytest-azurepipelines `_ Formatting PyTest output for Azure Pipelines UI Jul 23, 2020 4 - Beta pytest (>=3.5.0) -`pytest-bandit `_ A bandit plugin for pytest Sep 25, 2019 4 - Beta pytest (>=3.5.0) +`pytest-bandit `_ A bandit plugin for pytest Feb 23, 2021 4 - Beta pytest (>=3.5.0) `pytest-base-url `_ pytest plugin for URL based testing Jun 19, 2020 5 - Production/Stable pytest (>=2.7.3) `pytest-bdd `_ BDD for pytest Dec 07, 2020 6 - Mature pytest (>=4.3) `pytest-bdd-splinter `_ Common steps for pytest bdd and splinter integration Aug 12, 2019 5 - Production/Stable pytest (>=4.0.0) @@ -105,7 +106,7 @@ name `pytest-change-report `_ turn . into √,turn F into x Sep 14, 2020 N/A pytest `pytest-chdir `_ A pytest fixture for changing current working directory Jan 28, 2020 N/A pytest (>=5.0.0,<6.0.0) `pytest-check `_ A pytest plugin that allows multiple failures per test. Dec 27, 2020 5 - Production/Stable N/A -`pytest-checkdocs `_ check the README when running tests Jan 01, 2021 5 - Production/Stable pytest (!=3.7.3,>=3.5) ; extra == 'testing' +`pytest-checkdocs `_ check the README when running tests Feb 27, 2021 5 - Production/Stable pytest (>=4.6) ; extra == 'testing' `pytest-checkipdb `_ plugin to check if there are ipdb debugs left Jul 22, 2020 5 - Production/Stable pytest (>=2.9.2) `pytest-check-links `_ Check links in files Jul 29, 2020 N/A N/A `pytest-check-mk `_ pytest plugin to test Check_MK checks Nov 19, 2015 4 - Beta pytest @@ -152,6 +153,7 @@ name `pytest-custom-concurrency `_ Custom grouping concurrence for pytest Feb 08, 2021 N/A N/A `pytest-custom-exit-code `_ Exit pytest test session with custom exit code in different scenarios Aug 07, 2019 4 - Beta pytest (>=4.0.2) `pytest-custom-report `_ Configure the symbols displayed for test outcomes Jan 30, 2019 N/A pytest +`pytest-custom-scheduling `_ Custom grouping for pytest-xdist Feb 22, 2021 N/A N/A `pytest-cython `_ A plugin for testing Cython extension modules Jan 26, 2021 4 - Beta pytest (>=2.7.3) `pytest-darker `_ A pytest plugin for checking of modified code using Darker Aug 16, 2020 N/A pytest (>=6.0.1) ; extra == 'test' `pytest-dash `_ pytest fixtures to run dash applications. Mar 18, 2019 N/A N/A @@ -298,7 +300,7 @@ name `pytest-flakefinder `_ Runs tests multiple times to expose flakiness. Jul 28, 2020 4 - Beta pytest (>=2.7.1) `pytest-flakes `_ pytest plugin to check source code with pyflakes Nov 28, 2020 5 - Production/Stable N/A `pytest-flaptastic `_ Flaptastic py.test plugin Mar 17, 2019 N/A N/A -`pytest-flask `_ A set of py.test fixtures to test Flask applications. Nov 09, 2020 5 - Production/Stable pytest (>=5.2) +`pytest-flask `_ A set of py.test fixtures to test Flask applications. Feb 27, 2021 5 - Production/Stable pytest (>=5.2) `pytest-flask-sqlalchemy `_ A pytest plugin for preserving test isolation in Flask-SQlAlchemy using database transactions. Apr 04, 2019 4 - Beta pytest (>=3.2.1) `pytest-flask-sqlalchemy-transactions `_ Run tests in transactions using pytest, Flask, and SQLalchemy. Aug 02, 2018 4 - Beta pytest (>=3.2.1) `pytest-focus `_ A pytest plugin that alerts user of failed test cases with screen notifications May 04, 2019 4 - Beta pytest @@ -316,14 +318,14 @@ name `pytest-gevent `_ Ensure that gevent is properly patched when invoking pytest Feb 25, 2020 N/A pytest `pytest-gherkin `_ A flexible framework for executing BDD gherkin tests Jul 27, 2019 3 - Alpha pytest (>=5.0.0) `pytest-ghostinspector `_ For finding/executing Ghost Inspector tests May 17, 2016 3 - Alpha N/A -`pytest-girder `_ A set of pytest fixtures for testing Girder applications. Feb 11, 2021 N/A N/A +`pytest-girder `_ A set of pytest fixtures for testing Girder applications. Feb 25, 2021 N/A N/A `pytest-git `_ Git repository fixture for py.test May 28, 2019 5 - Production/Stable pytest `pytest-gitcov `_ Pytest plugin for reporting on coverage of the last git commit. Jan 11, 2020 2 - Pre-Alpha N/A `pytest-git-fixtures `_ Pytest fixtures for testing with git. Jan 25, 2021 4 - Beta pytest `pytest-github `_ Plugin for py.test that associates tests with github issues using a marker. Mar 07, 2019 5 - Production/Stable N/A `pytest-github-actions-annotate-failures `_ pytest plugin to annotate failed tests with a workflow command for GitHub Actions Oct 13, 2020 N/A pytest (>=4.0.0) `pytest-gitignore `_ py.test plugin to ignore the same files as git Jul 17, 2015 4 - Beta N/A -`pytest-gnupg-fixtures `_ Pytest fixtures for testing with gnupg. Jan 12, 2021 4 - Beta pytest +`pytest-gnupg-fixtures `_ Pytest fixtures for testing with gnupg. Feb 22, 2021 4 - Beta pytest `pytest-golden `_ Plugin for pytest that offloads expected outputs to data files Nov 23, 2020 N/A pytest (>=6.1.2,<7.0.0) `pytest-graphql-schema `_ Get graphql schema as fixture for pytest Oct 18, 2019 N/A N/A `pytest-greendots `_ Green progress dots Feb 08, 2014 3 - Alpha N/A @@ -356,7 +358,7 @@ name `pytest-httpx `_ Send responses to httpx. Nov 25, 2020 5 - Production/Stable pytest (==6.*) `pytest-hue `_ Visualise PyTest status via your Phillips Hue lights May 09, 2019 N/A N/A `pytest-hypo-25 `_ help hypo module for pytest Jan 12, 2020 3 - Alpha N/A -`pytest-ibutsu `_ A plugin to sent pytest results to an Ibutsu server Feb 11, 2021 4 - Beta pytest +`pytest-ibutsu `_ A plugin to sent pytest results to an Ibutsu server Feb 24, 2021 4 - Beta pytest `pytest-icdiff `_ use icdiff for better error messages in pytest assertions Apr 08, 2020 4 - Beta N/A `pytest-idapro `_ A pytest plugin for idapython. Allows a pytest setup to run tests outside and inside IDA in an automated manner by runnig pytest inside IDA and by mocking idapython api Nov 03, 2018 N/A N/A `pytest-ignore-flaky `_ ignore failures from flaky tests (pytest plugin) Jan 14, 2019 5 - Production/Stable pytest (>=3.7) @@ -369,7 +371,7 @@ name `pytest-inmanta `_ A py.test plugin providing fixtures to simplify inmanta modules testing. Oct 12, 2020 5 - Production/Stable N/A `pytest-inmanta-extensions `_ Inmanta tests package Jan 07, 2021 5 - Production/Stable N/A `pytest-Inomaly `_ A simple image diff plugin for pytest Feb 13, 2018 4 - Beta N/A -`pytest-insta `_ A practical snapshot testing plugin for pytest Nov 29, 2020 N/A pytest (>=6.0.2,<7.0.0) +`pytest-insta `_ A practical snapshot testing plugin for pytest Feb 25, 2021 N/A pytest (>=6.0.2,<7.0.0) `pytest-instafail `_ pytest plugin to show failures instantly Jun 14, 2020 4 - Beta pytest (>=2.9) `pytest-instrument `_ pytest plugin to instrument tests Apr 05, 2020 5 - Production/Stable pytest (>=5.1.0) `pytest-integration `_ Organizing pytests by integration or not Apr 16, 2020 N/A N/A @@ -417,7 +419,7 @@ name `pytest-localserver `_ py.test plugin to test server connections locally. Nov 14, 2018 4 - Beta N/A `pytest-localstack `_ Pytest plugin for AWS integration tests Aug 22, 2019 4 - Beta pytest (>=3.3.0) `pytest-lockable `_ lockable resource plugin for pytest Oct 05, 2020 3 - Alpha pytest -`pytest-locker `_ Used to lock object during testing. Essentially changing assertions from being hard coded to asserting that nothing changed Aug 11, 2020 N/A pytest (>=5.4) +`pytest-locker `_ Used to lock object during testing. Essentially changing assertions from being hard coded to asserting that nothing changed Feb 25, 2021 N/A pytest (>=5.4) `pytest-logbook `_ py.test plugin to capture logbook log messages Nov 23, 2015 5 - Production/Stable pytest (>=2.8) `pytest-logfest `_ Pytest plugin providing three logger fixtures with basic or full writing to log files Jul 21, 2019 4 - Beta pytest (>=3.5.0) `pytest-logger `_ Plugin configuring handlers for loggers from Python logging module. Jul 25, 2019 4 - Beta pytest (>=3.2) @@ -503,7 +505,7 @@ name `pytest-only `_ Use @pytest.mark.only to run a single test Jan 19, 2020 N/A N/A `pytest-oot `_ Run object-oriented tests in a simple format Sep 18, 2016 4 - Beta N/A `pytest-openfiles `_ Pytest plugin for detecting inadvertent open file handles Apr 16, 2020 3 - Alpha pytest (>=4.6) -`pytest-opentmi `_ pytest plugin for publish results to opentmi Jun 10, 2020 5 - Production/Stable pytest (>=5.0) +`pytest-opentmi `_ pytest plugin for publish results to opentmi Feb 26, 2021 5 - Production/Stable pytest (>=5.0) `pytest-operator `_ Fixtures for Operators Feb 20, 2021 N/A N/A `pytest-optional `_ include/exclude values of fixtures in pytest Oct 07, 2015 N/A N/A `pytest-optional-tests `_ Easy declaration of optional tests (i.e., that are not run by default) Jul 09, 2019 4 - Beta pytest (>=4.5.0) @@ -540,7 +542,7 @@ name `pytest-platform-markers `_ Markers for pytest to skip tests on specific platforms Sep 09, 2019 4 - Beta pytest (>=3.6.0) `pytest-play `_ pytest plugin that let you automate actions and assertions with test metrics reporting executing plain YAML files Jun 12, 2019 5 - Production/Stable N/A `pytest-playbook `_ Pytest plugin for reading playbooks. Jan 21, 2021 3 - Alpha pytest (>=6.1.2,<7.0.0) -`pytest-playwright `_ A pytest wrapper with fixtures for Playwright to automate web browsers Jan 21, 2021 N/A pytest +`pytest-playwright `_ A pytest wrapper with fixtures for Playwright to automate web browsers Feb 25, 2021 N/A pytest `pytest-plt `_ Fixtures for quickly making Matplotlib plots in tests Aug 17, 2020 5 - Production/Stable pytest `pytest-plugin-helpers `_ A plugin to help developing and testing other plugins Nov 23, 2019 4 - Beta pytest (>=3.5.0) `pytest-plus `_ PyTest Plus Plugin :: extends pytest functionality Mar 19, 2020 5 - Production/Stable pytest (>=3.50) @@ -555,7 +557,7 @@ name `pytest-pop `_ A pytest plugin to help with testing pop projects Aug 13, 2020 5 - Production/Stable pytest (>=5.4.0) `pytest-portion `_ Select a portion of the collected tests Jan 28, 2021 4 - Beta pytest (>=3.5.0) `pytest-postgres `_ Run PostgreSQL in Docker container in Pytest. Mar 22, 2020 N/A pytest -`pytest-postgresql `_ Postgresql fixtures and fixture factories for Pytest. Feb 11, 2021 5 - Production/Stable pytest (>=3.0.0) +`pytest-postgresql `_ Postgresql fixtures and fixture factories for Pytest. Feb 23, 2021 5 - Production/Stable pytest (>=3.0.0) `pytest-power `_ pytest plugin with powerful fixtures Dec 31, 2020 N/A pytest (>=5.4) `pytest-pride `_ Minitest-style test colors Apr 02, 2016 3 - Alpha N/A `pytest-print `_ pytest-print adds the printer fixture you can use to print messages to the user (directly to the pytest runner, not stdout) Oct 23, 2020 5 - Production/Stable pytest (>=3.0.0) @@ -575,7 +577,7 @@ name `pytest-pypom-navigation `_ Core engine for cookiecutter-qa and pytest-play packages Feb 18, 2019 4 - Beta pytest (>=3.0.7) `pytest-pyppeteer `_ A plugin to run pyppeteer in pytest. Feb 16, 2021 4 - Beta pytest (>=6.0.2) `pytest-pyq `_ Pytest fixture "q" for pyq Mar 10, 2020 5 - Production/Stable N/A -`pytest-pyramid `_ pytest pyramid providing basic fixtures for testing pyramid applications with pytest test suite Jun 05, 2020 4 - Beta pytest +`pytest-pyramid `_ pytest_pyramid - provides fixtures for testing pyramid applications with pytest test suite Feb 26, 2021 5 - Production/Stable pytest `pytest-pyramid-server `_ Pyramid server fixture for py.test May 28, 2019 5 - Production/Stable pytest `pytest-pytestrail `_ Pytest plugin for interaction with TestRail Aug 27, 2020 4 - Beta pytest (>=3.8.0) `pytest-pythonpath `_ pytest plugin for adding to the PYTHONPATH from command line or configs. Aug 22, 2018 5 - Production/Stable N/A @@ -649,16 +651,16 @@ name `pytest-salt-factories `_ Pytest Salt Plugin Feb 19, 2021 4 - Beta pytest (>=6.1.1) `pytest-salt-from-filenames `_ Simple PyTest Plugin For Salt's Test Suite Specifically Jan 29, 2019 4 - Beta pytest (>=4.1) `pytest-salt-runtests-bridge `_ Simple PyTest Plugin For Salt's Test Suite Specifically Dec 05, 2019 4 - Beta pytest (>=4.1) -`pytest-sanic `_ a pytest plugin for Sanic Sep 24, 2020 N/A pytest (>=5.2) +`pytest-sanic `_ a pytest plugin for Sanic Feb 27, 2021 N/A pytest (>=5.2) `pytest-sanity `_ Dec 07, 2020 N/A N/A `pytest-sa-pg `_ May 14, 2019 N/A N/A -`pytest-sbase `_ A complete web automation framework for end-to-end testing. Feb 19, 2021 5 - Production/Stable N/A +`pytest-sbase `_ A complete web automation framework for end-to-end testing. Feb 27, 2021 5 - Production/Stable N/A `pytest-scenario `_ pytest plugin for test scenarios Feb 06, 2017 3 - Alpha N/A `pytest-schema `_ 👍 Validate return values against a schema-like object in testing Aug 31, 2020 5 - Production/Stable pytest (>=3.5.0) `pytest-securestore `_ An encrypted password store for use within pytest cases Jun 19, 2019 4 - Beta N/A `pytest-select `_ A pytest plugin which allows to (de-)select tests from a file. Jan 18, 2019 3 - Alpha pytest (>=3.0) `pytest-selenium `_ pytest plugin for Selenium Sep 19, 2020 5 - Production/Stable pytest (>=5.0.0) -`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Feb 19, 2021 5 - Production/Stable N/A +`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Feb 27, 2021 5 - Production/Stable N/A `pytest-selenium-enhancer `_ pytest plugin for Selenium Nov 26, 2020 5 - Production/Stable N/A `pytest-selenium-pdiff `_ A pytest package implementing perceptualdiff for Selenium tests. Apr 06, 2017 2 - Pre-Alpha N/A `pytest-send-email `_ Send pytest execution result email Dec 04, 2019 N/A N/A @@ -703,7 +705,7 @@ name `pytest-split `_ Pytest plugin for splitting test suite based on test execution time Apr 07, 2020 1 - Planning N/A `pytest-splitio `_ Split.io SDK integration for e2e tests Sep 22, 2020 N/A pytest (<7,>=5.0) `pytest-split-tests `_ A Pytest plugin for running a subset of your tests by splitting them in to equally sized groups. Forked from Mark Adams' original project pytest-test-groups. May 28, 2019 N/A pytest (>=2.5) -`pytest-splunk-addon `_ A Dynamic test tool for Splunk Apps and Add-ons Feb 10, 2021 N/A pytest (>5.4.0,<6.1) +`pytest-splunk-addon `_ A Dynamic test tool for Splunk Apps and Add-ons Feb 26, 2021 N/A pytest (>5.4.0,<6.1) `pytest-splunk-addon-ui-smartx `_ Library to support testing Splunk Add-on UX Jan 18, 2021 N/A N/A `pytest-splunk-env `_ pytest fixtures for interaction with Splunk Enterprise and Splunk Cloud Oct 22, 2020 N/A pytest (>=6.1.1,<7.0.0) `pytest-sqitch `_ sqitch for pytest Apr 06, 2020 4 - Beta N/A @@ -778,7 +780,7 @@ name `pytest-tornado5 `_ A py.test plugin providing fixtures and markers to simplify testing of asynchronous tornado applications. Nov 16, 2018 5 - Production/Stable pytest (>=3.6) `pytest-tornado-yen3 `_ A py.test plugin providing fixtures and markers to simplify testing of asynchronous tornado applications. Oct 15, 2018 5 - Production/Stable N/A `pytest-tornasync `_ py.test plugin for testing Python 3.5+ Tornado code Jul 15, 2019 3 - Alpha pytest (>=3.0) -`pytest-track `_ Oct 23, 2020 3 - Alpha pytest (>=3.0) +`pytest-track `_ Feb 26, 2021 3 - Alpha pytest (>=3.0) `pytest-translations `_ Test your translation files. Oct 26, 2020 5 - Production/Stable N/A `pytest-travis-fold `_ Folds captured output sections in Travis CI build log Nov 29, 2017 4 - Beta pytest (>=2.6.0) `pytest-trello `_ Plugin for py.test that integrates trello using markers Nov 20, 2015 5 - Production/Stable N/A From 62ef87579647b7e97fba016f8300eb42d3fbe38d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Mar 2021 03:02:48 +0000 Subject: [PATCH 152/630] build(deps): bump anyio[curio,trio] in /testing/plugins_integration Bumps [anyio[curio,trio]](https://github.com/agronholm/anyio) from 2.1.0 to 2.2.0. - [Release notes](https://github.com/agronholm/anyio/releases) - [Changelog](https://github.com/agronholm/anyio/blob/master/docs/versionhistory.rst) - [Commits](https://github.com/agronholm/anyio/compare/2.1.0...2.2.0) Signed-off-by: dependabot[bot] --- testing/plugins_integration/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/plugins_integration/requirements.txt b/testing/plugins_integration/requirements.txt index 730d1f028fe..712ac339ec1 100644 --- a/testing/plugins_integration/requirements.txt +++ b/testing/plugins_integration/requirements.txt @@ -1,4 +1,4 @@ -anyio[curio,trio]==2.1.0 +anyio[curio,trio]==2.2.0 django==3.1.7 pytest-asyncio==0.14.0 pytest-bdd==4.0.2 From 897b5a3bd6c4ede255770d9830ee119970bb1ea2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Mar 2021 03:02:50 +0000 Subject: [PATCH 153/630] build(deps): bump twisted in /testing/plugins_integration Bumps [twisted](https://github.com/twisted/twisted) from 20.3.0 to 21.2.0. - [Release notes](https://github.com/twisted/twisted/releases) - [Changelog](https://github.com/twisted/twisted/blob/twisted-21.2.0/NEWS.rst) - [Commits](https://github.com/twisted/twisted/compare/twisted-20.3.0...twisted-21.2.0) Signed-off-by: dependabot[bot] --- testing/plugins_integration/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/plugins_integration/requirements.txt b/testing/plugins_integration/requirements.txt index 730d1f028fe..69011e651e5 100644 --- a/testing/plugins_integration/requirements.txt +++ b/testing/plugins_integration/requirements.txt @@ -11,5 +11,5 @@ pytest-rerunfailures==9.1.1 pytest-sugar==0.9.4 pytest-trio==0.7.0 pytest-twisted==1.13.2 -twisted==20.3.0 +twisted==21.2.0 pytest-xvfb==2.0.0 From decca74788d2f5d7b8fcac142579927346fc0a4b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 1 Mar 2021 16:53:28 +0000 Subject: [PATCH 154/630] [pre-commit.ci] pre-commit autoupdate --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fed7ca83cbb..b324b1f484e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -43,7 +43,7 @@ repos: hooks: - id: setup-cfg-fmt - repo: https://github.com/pre-commit/pygrep-hooks - rev: v1.7.1 + rev: v1.8.0 hooks: - id: python-use-type-annotations - repo: https://github.com/pre-commit/mirrors-mypy From c14a9adba35ac675ce3e825d34d01d5bb51748c3 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 4 Mar 2021 11:56:21 +0100 Subject: [PATCH 155/630] Fix skip signature (#8392) * Fix test_strict_and_skip The `--strict` argument was removed in #2552, but the removal wasn't actually correct - see #1472. * Fix argument handling in pytest.mark.skip See #8384 * Raise from None * Fix test name --- changelog/8384.bugfix.rst | 1 + doc/en/reference.rst | 2 +- src/_pytest/skipping.py | 13 +++++-------- testing/test_skipping.py | 18 +++++++++++++++++- 4 files changed, 24 insertions(+), 10 deletions(-) create mode 100644 changelog/8384.bugfix.rst diff --git a/changelog/8384.bugfix.rst b/changelog/8384.bugfix.rst new file mode 100644 index 00000000000..3b70987490e --- /dev/null +++ b/changelog/8384.bugfix.rst @@ -0,0 +1 @@ +The ``@pytest.mark.skip`` decorator now correctly handles its arguments. When the ``reason`` argument is accidentally given both positional and as a keyword (e.g. because it was confused with ``skipif``), a ``TypeError`` now occurs. Before, such tests were silently skipped, and the positional argument ignored. Additionally, ``reason`` is now documented correctly as positional or keyword (rather than keyword-only). diff --git a/doc/en/reference.rst b/doc/en/reference.rst index bc6c5670a5c..9ad82b3e4b9 100644 --- a/doc/en/reference.rst +++ b/doc/en/reference.rst @@ -150,7 +150,7 @@ pytest.mark.skip Unconditionally skip a test function. -.. py:function:: pytest.mark.skip(*, reason=None) +.. py:function:: pytest.mark.skip(reason=None) :keyword str reason: Reason why the test function is being skipped. diff --git a/src/_pytest/skipping.py b/src/_pytest/skipping.py index 1ad312919ca..7fe9783a4fa 100644 --- a/src/_pytest/skipping.py +++ b/src/_pytest/skipping.py @@ -161,7 +161,7 @@ def evaluate_condition(item: Item, mark: Mark, condition: object) -> Tuple[bool, class Skip: """The result of evaluate_skip_marks().""" - reason = attr.ib(type=str) + reason = attr.ib(type=str, default="unconditional skip") def evaluate_skip_marks(item: Item) -> Optional[Skip]: @@ -184,13 +184,10 @@ def evaluate_skip_marks(item: Item) -> Optional[Skip]: return Skip(reason) for mark in item.iter_markers(name="skip"): - if "reason" in mark.kwargs: - reason = mark.kwargs["reason"] - elif mark.args: - reason = mark.args[0] - else: - reason = "unconditional skip" - return Skip(reason) + try: + return Skip(*mark.args, **mark.kwargs) + except TypeError as e: + raise TypeError(str(e) + " - maybe you meant pytest.mark.skipif?") from None return None diff --git a/testing/test_skipping.py b/testing/test_skipping.py index fc66eb18e64..349de6e080f 100644 --- a/testing/test_skipping.py +++ b/testing/test_skipping.py @@ -861,9 +861,25 @@ def test_hello(): pass """ ) - result = pytester.runpytest("-rs") + result = pytester.runpytest("-rs", "--strict-markers") result.stdout.fnmatch_lines(["*unconditional skip*", "*1 skipped*"]) + def test_wrong_skip_usage(self, pytester: Pytester) -> None: + pytester.makepyfile( + """ + import pytest + @pytest.mark.skip(False, reason="I thought this was skipif") + def test_hello(): + pass + """ + ) + result = pytester.runpytest() + result.stdout.fnmatch_lines( + [ + "*TypeError: __init__() got multiple values for argument 'reason' - maybe you meant pytest.mark.skipif?" + ] + ) + class TestSkipif: def test_skipif_conditional(self, pytester: Pytester) -> None: From 19a2f7425ddec3b614da7c915e0cf8bb24b6906f Mon Sep 17 00:00:00 2001 From: Alexandros Tzannes Date: Thu, 4 Mar 2021 15:45:57 -0500 Subject: [PATCH 156/630] Merge pull request #8399 from atzannes/master closes #8394 Generated fixture names for unittest/xunit/nose should start with underscore --- changelog/8394.bugfix.rst | 1 + src/_pytest/python.py | 8 +++---- src/_pytest/unittest.py | 2 +- testing/test_nose.py | 44 +++++++++++++++++++++++++++++++++++++++ testing/test_unittest.py | 24 +++++++++++++++++++++ 5 files changed, 74 insertions(+), 5 deletions(-) create mode 100644 changelog/8394.bugfix.rst diff --git a/changelog/8394.bugfix.rst b/changelog/8394.bugfix.rst new file mode 100644 index 00000000000..a0fb5bb71fd --- /dev/null +++ b/changelog/8394.bugfix.rst @@ -0,0 +1 @@ +Use private names for internal fixtures that handle classic setup/teardown so that they don't show up with the default ``--fixtures`` invocation (but they still show up with ``--fixtures -v``). diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 40116ab9c5a..c19d2ed4fb4 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -528,7 +528,7 @@ def _inject_setup_module_fixture(self) -> None: autouse=True, scope="module", # Use a unique name to speed up lookup. - name=f"xunit_setup_module_fixture_{self.obj.__name__}", + name=f"_xunit_setup_module_fixture_{self.obj.__name__}", ) def xunit_setup_module_fixture(request) -> Generator[None, None, None]: if setup_module is not None: @@ -557,7 +557,7 @@ def _inject_setup_function_fixture(self) -> None: autouse=True, scope="function", # Use a unique name to speed up lookup. - name=f"xunit_setup_function_fixture_{self.obj.__name__}", + name=f"_xunit_setup_function_fixture_{self.obj.__name__}", ) def xunit_setup_function_fixture(request) -> Generator[None, None, None]: if request.instance is not None: @@ -809,7 +809,7 @@ def _inject_setup_class_fixture(self) -> None: autouse=True, scope="class", # Use a unique name to speed up lookup. - name=f"xunit_setup_class_fixture_{self.obj.__qualname__}", + name=f"_xunit_setup_class_fixture_{self.obj.__qualname__}", ) def xunit_setup_class_fixture(cls) -> Generator[None, None, None]: if setup_class is not None: @@ -838,7 +838,7 @@ def _inject_setup_method_fixture(self) -> None: autouse=True, scope="function", # Use a unique name to speed up lookup. - name=f"xunit_setup_method_fixture_{self.obj.__qualname__}", + name=f"_xunit_setup_method_fixture_{self.obj.__qualname__}", ) def xunit_setup_method_fixture(self, request) -> Generator[None, None, None]: method = request.function diff --git a/src/_pytest/unittest.py b/src/_pytest/unittest.py index 719eb4e8823..3f88d7a9e2c 100644 --- a/src/_pytest/unittest.py +++ b/src/_pytest/unittest.py @@ -144,7 +144,7 @@ def cleanup(*args): scope=scope, autouse=True, # Use a unique name to speed up lookup. - name=f"unittest_{setup_name}_fixture_{obj.__qualname__}", + name=f"_unittest_{setup_name}_fixture_{obj.__qualname__}", ) def fixture(self, request: FixtureRequest) -> Generator[None, None, None]: if _is_skipped(self): diff --git a/testing/test_nose.py b/testing/test_nose.py index 13429afafd4..77f79b53b3c 100644 --- a/testing/test_nose.py +++ b/testing/test_nose.py @@ -211,6 +211,50 @@ def test_world(): result.stdout.fnmatch_lines(["*2 passed*"]) +def test_fixtures_nose_setup_issue8394(pytester: Pytester) -> None: + pytester.makepyfile( + """ + def setup_module(): + pass + + def teardown_module(): + pass + + def setup_function(func): + pass + + def teardown_function(func): + pass + + def test_world(): + pass + + class Test(object): + def setup_class(cls): + pass + + def teardown_class(cls): + pass + + def setup_method(self, meth): + pass + + def teardown_method(self, meth): + pass + + def test_method(self): pass + """ + ) + match = "*no docstring available*" + result = pytester.runpytest("--fixtures") + assert result.ret == 0 + result.stdout.no_fnmatch_line(match) + + result = pytester.runpytest("--fixtures", "-v") + assert result.ret == 0 + result.stdout.fnmatch_lines([match, match, match, match]) + + def test_nose_setup_ordering(pytester: Pytester) -> None: pytester.makepyfile( """ diff --git a/testing/test_unittest.py b/testing/test_unittest.py index 69bafc26d61..d7f7737153d 100644 --- a/testing/test_unittest.py +++ b/testing/test_unittest.py @@ -302,6 +302,30 @@ def test_teareddown(): reprec.assertoutcome(passed=3) +def test_fixtures_setup_setUpClass_issue8394(pytester: Pytester) -> None: + pytester.makepyfile( + """ + import unittest + class MyTestCase(unittest.TestCase): + @classmethod + def setUpClass(cls): + pass + def test_func1(self): + pass + @classmethod + def tearDownClass(cls): + pass + """ + ) + result = pytester.runpytest("--fixtures") + assert result.ret == 0 + result.stdout.no_fnmatch_line("*no docstring available*") + + result = pytester.runpytest("--fixtures", "-v") + assert result.ret == 0 + result.stdout.fnmatch_lines(["*no docstring available*"]) + + def test_setup_class(pytester: Pytester) -> None: testpath = pytester.makepyfile( """ From 7c792e96c68c0308ca7d6b913dd9fc82cf727012 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Fri, 5 Mar 2021 22:22:53 -0300 Subject: [PATCH 157/630] Add type annotations to the description instead of signature This configures Sphinx autodoc to include the type annotations along with the description of the function/method, instead of including it into the signature. Fix #8405 --- doc/en/conf.py | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/en/conf.py b/doc/en/conf.py index e34ae6856f0..d9c1f3c2d3f 100644 --- a/doc/en/conf.py +++ b/doc/en/conf.py @@ -35,6 +35,7 @@ # sys.path.insert(0, os.path.abspath('.')) autodoc_member_order = "bysource" +autodoc_typehints = "description" todo_include_todos = 1 # -- General configuration ----------------------------------------------------- From 22dad53a248f50f50b5e000d63a8d3c798868d98 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Sun, 17 Jan 2021 21:20:29 +0100 Subject: [PATCH 158/630] implement Node.path as pathlib.Path * reorganize lastfailed node sort Co-authored-by: Bruno Oliveira --- changelog/8251.deprecation.rst | 1 + changelog/8251.feature.rst | 1 + doc/en/deprecations.rst | 10 +++ src/_pytest/cacheprovider.py | 13 ++-- src/_pytest/compat.py | 12 ++++ src/_pytest/deprecated.py | 8 +++ src/_pytest/doctest.py | 21 ++++--- src/_pytest/fixtures.py | 30 ++++++--- src/_pytest/main.py | 11 +++- src/_pytest/nodes.py | 85 ++++++++++++++++++++------ src/_pytest/pytester.py | 5 +- src/_pytest/python.py | 22 ++++--- testing/plugins_integration/pytest.ini | 1 + testing/python/collect.py | 6 +- testing/python/fixtures.py | 4 +- testing/test_collection.py | 32 +++++----- testing/test_mark.py | 3 +- testing/test_runner.py | 4 +- testing/test_terminal.py | 2 +- 19 files changed, 194 insertions(+), 77 deletions(-) create mode 100644 changelog/8251.deprecation.rst create mode 100644 changelog/8251.feature.rst diff --git a/changelog/8251.deprecation.rst b/changelog/8251.deprecation.rst new file mode 100644 index 00000000000..1d988bfc83b --- /dev/null +++ b/changelog/8251.deprecation.rst @@ -0,0 +1 @@ +Deprecate ``Node.fspath`` as we plan to move off `py.path.local `__ and switch to :mod:``pathlib``. diff --git a/changelog/8251.feature.rst b/changelog/8251.feature.rst new file mode 100644 index 00000000000..49aede797a0 --- /dev/null +++ b/changelog/8251.feature.rst @@ -0,0 +1 @@ +Implement ``Node.path`` as a ``pathlib.Path``. diff --git a/doc/en/deprecations.rst b/doc/en/deprecations.rst index a3d7fd49a33..6ecb37b385a 100644 --- a/doc/en/deprecations.rst +++ b/doc/en/deprecations.rst @@ -19,6 +19,16 @@ Below is a complete list of all pytest features which are considered deprecated. :class:`PytestWarning` or subclasses, which can be filtered using :ref:`standard warning filters `. +``Node.fspath`` in favor of ``pathlib`` and ``Node.path`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. deprecated:: 6.3 + +As pytest tries to move off `py.path.local `__ we ported most of the node internals to :mod:`pathlib`. + +Pytest will provide compatibility for quite a while. + + Backward compatibilities in ``Parser.addoption`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/src/_pytest/cacheprovider.py b/src/_pytest/cacheprovider.py index 585cebf6c9d..03e20bea18c 100755 --- a/src/_pytest/cacheprovider.py +++ b/src/_pytest/cacheprovider.py @@ -218,14 +218,17 @@ def pytest_make_collect_report(self, collector: nodes.Collector): # Sort any lf-paths to the beginning. lf_paths = self.lfplugin._last_failed_paths + res.result = sorted( res.result, - key=lambda x: 0 if Path(str(x.fspath)) in lf_paths else 1, + # use stable sort to priorize last failed + key=lambda x: x.path in lf_paths, + reverse=True, ) return elif isinstance(collector, Module): - if Path(str(collector.fspath)) in self.lfplugin._last_failed_paths: + if collector.path in self.lfplugin._last_failed_paths: out = yield res = out.get_result() result = res.result @@ -246,7 +249,7 @@ def pytest_make_collect_report(self, collector: nodes.Collector): for x in result if x.nodeid in lastfailed # Include any passed arguments (not trivial to filter). - or session.isinitpath(x.fspath) + or session.isinitpath(x.path) # Keep all sub-collectors. or isinstance(x, nodes.Collector) ] @@ -266,7 +269,7 @@ def pytest_make_collect_report( # test-bearing paths and doesn't try to include the paths of their # packages, so don't filter them. if isinstance(collector, Module) and not isinstance(collector, Package): - if Path(str(collector.fspath)) not in self.lfplugin._last_failed_paths: + if collector.path not in self.lfplugin._last_failed_paths: self.lfplugin._skipped_files += 1 return CollectReport( @@ -415,7 +418,7 @@ def pytest_collection_modifyitems( self.cached_nodeids.update(item.nodeid for item in items) def _get_increasing_order(self, items: Iterable[nodes.Item]) -> List[nodes.Item]: - return sorted(items, key=lambda item: item.fspath.mtime(), reverse=True) + return sorted(items, key=lambda item: item.path.stat().st_mtime, reverse=True) # type: ignore[no-any-return] def pytest_sessionfinish(self) -> None: config = self.config diff --git a/src/_pytest/compat.py b/src/_pytest/compat.py index b354fcb3f63..b9cbf85e04f 100644 --- a/src/_pytest/compat.py +++ b/src/_pytest/compat.py @@ -2,6 +2,7 @@ import enum import functools import inspect +import os import re import sys from contextlib import contextmanager @@ -18,6 +19,7 @@ from typing import Union import attr +import py from _pytest.outcomes import fail from _pytest.outcomes import TEST_OUTCOME @@ -30,6 +32,16 @@ _T = TypeVar("_T") _S = TypeVar("_S") +#: constant to prepare valuing py.path.local replacements/lazy proxies later on +# intended for removal in pytest 8.0 or 9.0 + +LEGACY_PATH = py.path.local + + +def legacy_path(path: Union[str, "os.PathLike[str]"]) -> LEGACY_PATH: + """Internal wrapper to prepare lazy proxies for py.path.local instances""" + return py.path.local(path) + # fmt: off # Singleton type for NOTSET, as described in: diff --git a/src/_pytest/deprecated.py b/src/_pytest/deprecated.py index 5efc004ac94..c203eadc1ad 100644 --- a/src/_pytest/deprecated.py +++ b/src/_pytest/deprecated.py @@ -89,6 +89,12 @@ ) +NODE_FSPATH = UnformattedWarning( + PytestDeprecationWarning, + "{type}.fspath is deprecated and will be replaced by {type}.path.\n" + "see TODO;URL for details on replacing py.path.local with pathlib.Path", +) + # You want to make some `__init__` or function "private". # # def my_private_function(some, args): @@ -106,6 +112,8 @@ # # All other calls will get the default _ispytest=False and trigger # the warning (possibly error in the future). + + def check_ispytest(ispytest: bool) -> None: if not ispytest: warn(PRIVATE, stacklevel=3) diff --git a/src/_pytest/doctest.py b/src/_pytest/doctest.py index 255ca80b913..4942a8f793b 100644 --- a/src/_pytest/doctest.py +++ b/src/_pytest/doctest.py @@ -30,6 +30,7 @@ from _pytest._code.code import ReprFileLocation from _pytest._code.code import TerminalRepr from _pytest._io import TerminalWriter +from _pytest.compat import legacy_path from _pytest.compat import safe_getattr from _pytest.config import Config from _pytest.config.argparsing import Parser @@ -128,10 +129,10 @@ def pytest_collect_file( config = parent.config if fspath.suffix == ".py": if config.option.doctestmodules and not _is_setup_py(fspath): - mod: DoctestModule = DoctestModule.from_parent(parent, fspath=path) + mod: DoctestModule = DoctestModule.from_parent(parent, path=fspath) return mod elif _is_doctest(config, fspath, parent): - txt: DoctestTextfile = DoctestTextfile.from_parent(parent, fspath=path) + txt: DoctestTextfile = DoctestTextfile.from_parent(parent, path=fspath) return txt return None @@ -378,7 +379,7 @@ def repr_failure( # type: ignore[override] def reportinfo(self): assert self.dtest is not None - return self.fspath, self.dtest.lineno, "[doctest] %s" % self.name + return legacy_path(self.path), self.dtest.lineno, "[doctest] %s" % self.name def _get_flag_lookup() -> Dict[str, int]: @@ -425,9 +426,9 @@ def collect(self) -> Iterable[DoctestItem]: # Inspired by doctest.testfile; ideally we would use it directly, # but it doesn't support passing a custom checker. encoding = self.config.getini("doctest_encoding") - text = self.fspath.read_text(encoding) - filename = str(self.fspath) - name = self.fspath.basename + text = self.path.read_text(encoding) + filename = str(self.path) + name = self.path.name globs = {"__name__": "__main__"} optionflags = get_optionflags(self) @@ -534,16 +535,16 @@ def _find( self, tests, obj, name, module, source_lines, globs, seen ) - if self.fspath.basename == "conftest.py": + if self.path.name == "conftest.py": module = self.config.pluginmanager._importconftest( - Path(self.fspath), self.config.getoption("importmode") + self.path, self.config.getoption("importmode") ) else: try: - module = import_path(self.fspath) + module = import_path(self.path) except ImportError: if self.config.getvalue("doctest_ignore_import_errors"): - pytest.skip("unable to import module %r" % self.fspath) + pytest.skip("unable to import module %r" % self.path) else: raise # Uses internal doctest module parsing mechanism. diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 0521d736118..722400ff7aa 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -28,7 +28,6 @@ from typing import Union import attr -import py import _pytest from _pytest import nodes @@ -46,6 +45,8 @@ from _pytest.compat import getimfunc from _pytest.compat import getlocation from _pytest.compat import is_generator +from _pytest.compat import LEGACY_PATH +from _pytest.compat import legacy_path from _pytest.compat import NOTSET from _pytest.compat import safe_getattr from _pytest.config import _PluggyPlugin @@ -53,6 +54,7 @@ from _pytest.config.argparsing import Parser from _pytest.deprecated import check_ispytest from _pytest.deprecated import FILLFUNCARGS +from _pytest.deprecated import NODE_FSPATH from _pytest.deprecated import YIELD_FIXTURE from _pytest.mark import Mark from _pytest.mark import ParameterSet @@ -256,12 +258,12 @@ def get_parametrized_fixture_keys(item: nodes.Item, scopenum: int) -> Iterator[_ if scopenum == 0: # session key: _Key = (argname, param_index) elif scopenum == 1: # package - key = (argname, param_index, item.fspath.dirpath()) + key = (argname, param_index, item.path.parent) elif scopenum == 2: # module - key = (argname, param_index, item.fspath) + key = (argname, param_index, item.path) elif scopenum == 3: # class item_cls = item.cls # type: ignore[attr-defined] - key = (argname, param_index, item.fspath, item_cls) + key = (argname, param_index, item.path, item_cls) yield key @@ -519,12 +521,17 @@ def module(self): return self._pyfuncitem.getparent(_pytest.python.Module).obj @property - def fspath(self) -> py.path.local: - """The file system path of the test module which collected this test.""" + def fspath(self) -> LEGACY_PATH: + """(deprecated) The file system path of the test module which collected this test.""" + warnings.warn(NODE_FSPATH.format(type=type(self).__name__), stacklevel=2) + return legacy_path(self.path) + + @property + def path(self) -> Path: if self.scope not in ("function", "class", "module", "package"): raise AttributeError(f"module not available in {self.scope}-scoped context") # TODO: Remove ignore once _pyfuncitem is properly typed. - return self._pyfuncitem.fspath # type: ignore + return self._pyfuncitem.path # type: ignore @property def keywords(self) -> MutableMapping[str, Any]: @@ -1040,7 +1047,7 @@ def finish(self, request: SubRequest) -> None: if exc: raise exc finally: - hook = self._fixturemanager.session.gethookproxy(request.node.fspath) + hook = self._fixturemanager.session.gethookproxy(request.node.path) hook.pytest_fixture_post_finalizer(fixturedef=self, request=request) # Even if finalization fails, we invalidate the cached fixture # value and remove all finalizers because they may be bound methods @@ -1075,7 +1082,7 @@ def execute(self, request: SubRequest) -> _FixtureValue: self.finish(request) assert self.cached_result is None - hook = self._fixturemanager.session.gethookproxy(request.node.fspath) + hook = self._fixturemanager.session.gethookproxy(request.node.path) result = hook.pytest_fixture_setup(fixturedef=self, request=request) return result @@ -1623,6 +1630,11 @@ def parsefactories( self._holderobjseen.add(holderobj) autousenames = [] for name in dir(holderobj): + # ugly workaround for one of the fspath deprecated property of node + # todo: safely generalize + if isinstance(holderobj, nodes.Node) and name == "fspath": + continue + # The attribute can be an arbitrary descriptor, so the attribute # access below can raise. safe_getatt() ignores such exceptions. obj = safe_getattr(holderobj, name, None) diff --git a/src/_pytest/main.py b/src/_pytest/main.py index 5036601f9bb..3dc00fa691e 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -464,7 +464,12 @@ class Session(nodes.FSCollector): def __init__(self, config: Config) -> None: super().__init__( - config.rootdir, parent=None, config=config, session=self, nodeid="" + path=config.rootpath, + fspath=config.rootdir, + parent=None, + config=config, + session=self, + nodeid="", ) self.testsfailed = 0 self.testscollected = 0 @@ -688,7 +693,7 @@ def collect(self) -> Iterator[Union[nodes.Item, nodes.Collector]]: if col: if isinstance(col[0], Package): pkg_roots[str(parent)] = col[0] - node_cache1[Path(col[0].fspath)] = [col[0]] + node_cache1[col[0].path] = [col[0]] # If it's a directory argument, recurse and look for any Subpackages. # Let the Package collector deal with subnodes, don't collect here. @@ -717,7 +722,7 @@ def collect(self) -> Iterator[Union[nodes.Item, nodes.Collector]]: continue for x in self._collectfile(path): - key2 = (type(x), Path(x.fspath)) + key2 = (type(x), x.path) if key2 in node_cache2: yield node_cache2[key2] else: diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py index 2a96d55ad05..47752d34c61 100644 --- a/src/_pytest/nodes.py +++ b/src/_pytest/nodes.py @@ -23,9 +23,12 @@ from _pytest._code.code import ExceptionInfo from _pytest._code.code import TerminalRepr from _pytest.compat import cached_property +from _pytest.compat import LEGACY_PATH +from _pytest.compat import legacy_path from _pytest.config import Config from _pytest.config import ConftestImportFailure from _pytest.deprecated import FSCOLLECTOR_GETHOOKPROXY_ISINITPATH +from _pytest.deprecated import NODE_FSPATH from _pytest.mark.structures import Mark from _pytest.mark.structures import MarkDecorator from _pytest.mark.structures import NodeKeywords @@ -79,6 +82,26 @@ def iterparentnodeids(nodeid: str) -> Iterator[str]: pos = at + len(sep) +def _imply_path( + path: Optional[Path], fspath: Optional[LEGACY_PATH] +) -> Tuple[Path, LEGACY_PATH]: + if path is not None: + if fspath is not None: + if Path(fspath) != path: + raise ValueError( + f"Path({fspath!r}) != {path!r}\n" + "if both path and fspath are given they need to be equal" + ) + assert Path(fspath) == path, f"{fspath} != {path}" + else: + fspath = legacy_path(path) + return path, fspath + + else: + assert fspath is not None + return Path(fspath), fspath + + _NodeType = TypeVar("_NodeType", bound="Node") @@ -110,7 +133,7 @@ class Node(metaclass=NodeMeta): "parent", "config", "session", - "fspath", + "path", "_nodeid", "_store", "__dict__", @@ -123,6 +146,7 @@ def __init__( config: Optional[Config] = None, session: "Optional[Session]" = None, fspath: Optional[py.path.local] = None, + path: Optional[Path] = None, nodeid: Optional[str] = None, ) -> None: #: A unique name within the scope of the parent node. @@ -148,7 +172,7 @@ def __init__( self.session = parent.session #: Filesystem path where this node was collected from (can be None). - self.fspath = fspath or getattr(parent, "fspath", None) + self.path = _imply_path(path or getattr(parent, "path", None), fspath=fspath)[0] # The explicit annotation is to avoid publicly exposing NodeKeywords. #: Keywords/markers collected from all scopes. @@ -174,6 +198,17 @@ def __init__( # own use. Currently only intended for internal plugins. self._store = Store() + @property + def fspath(self): + """(deprecated) returns a py.path.local copy of self.path""" + warnings.warn(NODE_FSPATH.format(type=type(self).__name__), stacklevel=2) + return py.path.local(self.path) + + @fspath.setter + def fspath(self, value: py.path.local): + warnings.warn(NODE_FSPATH.format(type=type(self).__name__), stacklevel=2) + self.path = Path(value) + @classmethod def from_parent(cls, parent: "Node", **kw): """Public constructor for Nodes. @@ -195,7 +230,7 @@ def from_parent(cls, parent: "Node", **kw): @property def ihook(self): """fspath-sensitive hook proxy used to call pytest hooks.""" - return self.session.gethookproxy(self.fspath) + return self.session.gethookproxy(self.path) def __repr__(self) -> str: return "<{} {}>".format(self.__class__.__name__, getattr(self, "name", None)) @@ -476,9 +511,9 @@ def repr_failure( # type: ignore[override] return self._repr_failure_py(excinfo, style=tbstyle) def _prunetraceback(self, excinfo: ExceptionInfo[BaseException]) -> None: - if hasattr(self, "fspath"): + if hasattr(self, "path"): traceback = excinfo.traceback - ntraceback = traceback.cut(path=Path(self.fspath)) + ntraceback = traceback.cut(path=self.path) if ntraceback == traceback: ntraceback = ntraceback.cut(excludepath=tracebackcutdir) excinfo.traceback = ntraceback.filter() @@ -497,36 +532,52 @@ def _check_initialpaths_for_relpath( class FSCollector(Collector): def __init__( self, - fspath: py.path.local, + fspath: Optional[py.path.local], + path: Optional[Path], parent=None, config: Optional[Config] = None, session: Optional["Session"] = None, nodeid: Optional[str] = None, ) -> None: + path, fspath = _imply_path(path, fspath=fspath) name = fspath.basename - if parent is not None: - rel = fspath.relto(parent.fspath) - if rel: - name = rel + if parent is not None and parent.path != path: + try: + rel = path.relative_to(parent.path) + except ValueError: + pass + else: + name = str(rel) name = name.replace(os.sep, SEP) - self.fspath = fspath + self.path = Path(fspath) session = session or parent.session if nodeid is None: - nodeid = self.fspath.relto(session.config.rootdir) - - if not nodeid: + try: + nodeid = str(self.path.relative_to(session.config.rootpath)) + except ValueError: nodeid = _check_initialpaths_for_relpath(session, fspath) + if nodeid and os.sep != SEP: nodeid = nodeid.replace(os.sep, SEP) - super().__init__(name, parent, config, session, nodeid=nodeid, fspath=fspath) + super().__init__( + name, parent, config, session, nodeid=nodeid, fspath=fspath, path=path + ) @classmethod - def from_parent(cls, parent, *, fspath, **kw): + def from_parent( + cls, + parent, + *, + fspath: Optional[py.path.local] = None, + path: Optional[Path] = None, + **kw, + ): """The public constructor.""" - return super().from_parent(parent=parent, fspath=fspath, **kw) + path, fspath = _imply_path(path, fspath=fspath) + return super().from_parent(parent=parent, fspath=fspath, path=path, **kw) def gethookproxy(self, fspath: "os.PathLike[str]"): warnings.warn(FSCOLLECTOR_GETHOOKPROXY_ISINITPATH, stacklevel=2) diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index 853dfbe9489..f2a6d2aab92 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -61,6 +61,7 @@ from _pytest.outcomes import fail from _pytest.outcomes import importorskip from _pytest.outcomes import skip +from _pytest.pathlib import bestrelpath from _pytest.pathlib import make_numbered_dir from _pytest.reports import CollectReport from _pytest.reports import TestReport @@ -976,10 +977,10 @@ def getpathnode(self, path: Union[str, "os.PathLike[str]"]): :param py.path.local path: Path to the file. """ - path = py.path.local(path) + path = Path(path) config = self.parseconfigure(path) session = Session.from_config(config) - x = session.fspath.bestrelpath(path) + x = bestrelpath(session.path, path) config.hook.pytest_sessionstart(session=session) res = session.perform_collect([x], genitems=False)[0] config.hook.pytest_sessionfinish(session=session, exitstatus=ExitCode.OK) diff --git a/src/_pytest/python.py b/src/_pytest/python.py index c19d2ed4fb4..7d518dbbf4b 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -577,7 +577,7 @@ def _importtestmodule(self): # We assume we are only called once per module. importmode = self.config.getoption("--import-mode") try: - mod = import_path(self.fspath, mode=importmode) + mod = import_path(self.path, mode=importmode) except SyntaxError as e: raise self.CollectError( ExceptionInfo.from_current().getrepr(style="short") @@ -603,10 +603,10 @@ def _importtestmodule(self): ) formatted_tb = str(exc_repr) raise self.CollectError( - "ImportError while importing test module '{fspath}'.\n" + "ImportError while importing test module '{path}'.\n" "Hint: make sure your test modules/packages have valid Python names.\n" "Traceback:\n" - "{traceback}".format(fspath=self.fspath, traceback=formatted_tb) + "{traceback}".format(path=self.path, traceback=formatted_tb) ) from e except skip.Exception as e: if e.allow_module_level: @@ -624,18 +624,26 @@ def _importtestmodule(self): class Package(Module): def __init__( self, - fspath: py.path.local, + fspath: Optional[py.path.local], parent: nodes.Collector, # NOTE: following args are unused: config=None, session=None, nodeid=None, + path=Optional[Path], ) -> None: # NOTE: Could be just the following, but kept as-is for compat. # nodes.FSCollector.__init__(self, fspath, parent=parent) + path, fspath = nodes._imply_path(path, fspath=fspath) session = parent.session nodes.FSCollector.__init__( - self, fspath, parent=parent, config=config, session=session, nodeid=nodeid + self, + fspath=fspath, + path=path, + parent=parent, + config=config, + session=session, + nodeid=nodeid, ) self.name = os.path.basename(str(fspath.dirname)) @@ -704,12 +712,12 @@ def _collectfile( return ihook.pytest_collect_file(fspath=fspath, path=path, parent=self) # type: ignore[no-any-return] def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]: - this_path = Path(self.fspath).parent + this_path = self.path.parent init_module = this_path / "__init__.py" if init_module.is_file() and path_matches_patterns( init_module, self.config.getini("python_files") ): - yield Module.from_parent(self, fspath=py.path.local(init_module)) + yield Module.from_parent(self, path=init_module) pkg_prefixes: Set[Path] = set() for direntry in visit(str(this_path), recurse=self._recurse): path = Path(direntry.path) diff --git a/testing/plugins_integration/pytest.ini b/testing/plugins_integration/pytest.ini index f6c77b0dee5..b42b07d145a 100644 --- a/testing/plugins_integration/pytest.ini +++ b/testing/plugins_integration/pytest.ini @@ -2,3 +2,4 @@ addopts = --strict-markers filterwarnings = error::pytest.PytestWarning + ignore:.*.fspath is deprecated and will be replaced by .*.path.*:pytest.PytestDeprecationWarning diff --git a/testing/python/collect.py b/testing/python/collect.py index 4256851e254..bb4c937c01e 100644 --- a/testing/python/collect.py +++ b/testing/python/collect.py @@ -1125,7 +1125,8 @@ def pytest_pycollect_makeitem(collector, name, obj): def test_func_reportinfo(self, pytester: Pytester) -> None: item = pytester.getitem("def test_func(): pass") fspath, lineno, modpath = item.reportinfo() - assert fspath == item.fspath + with pytest.warns(DeprecationWarning): + assert fspath == item.fspath assert lineno == 0 assert modpath == "test_func" @@ -1140,7 +1141,8 @@ def test_hello(self): pass classcol = pytester.collect_by_name(modcol, "TestClass") assert isinstance(classcol, Class) fspath, lineno, msg = classcol.reportinfo() - assert fspath == modcol.fspath + with pytest.warns(DeprecationWarning): + assert fspath == modcol.fspath assert lineno == 1 assert msg == "TestClass" diff --git a/testing/python/fixtures.py b/testing/python/fixtures.py index 3d5099c5399..e62143e5c18 100644 --- a/testing/python/fixtures.py +++ b/testing/python/fixtures.py @@ -966,7 +966,9 @@ def test_request_getmodulepath(self, pytester: Pytester) -> None: modcol = pytester.getmodulecol("def test_somefunc(): pass") (item,) = pytester.genitems([modcol]) req = fixtures.FixtureRequest(item, _ispytest=True) - assert req.fspath == modcol.fspath + assert req.path == modcol.path + with pytest.warns(pytest.PytestDeprecationWarning): + assert req.fspath == modcol.fspath def test_request_fixturenames(self, pytester: Pytester) -> None: pytester.makepyfile( diff --git a/testing/test_collection.py b/testing/test_collection.py index 055d27476a6..248071111f3 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -464,13 +464,13 @@ def test_collect_topdir(self, pytester: Pytester) -> None: config = pytester.parseconfig(id) topdir = pytester.path rcol = Session.from_config(config) - assert topdir == rcol.fspath + assert topdir == rcol.path # rootid = rcol.nodeid # root2 = rcol.perform_collect([rcol.nodeid], genitems=False)[0] # assert root2 == rcol, rootid colitems = rcol.perform_collect([rcol.nodeid], genitems=False) assert len(colitems) == 1 - assert colitems[0].fspath == p + assert colitems[0].path == p def get_reported_items(self, hookrec: HookRecorder) -> List[Item]: """Return pytest.Item instances reported by the pytest_collectreport hook""" @@ -494,10 +494,10 @@ def test_collect_protocol_single_function(self, pytester: Pytester) -> None: topdir = pytester.path # noqa hookrec.assert_contains( [ - ("pytest_collectstart", "collector.fspath == topdir"), - ("pytest_make_collect_report", "collector.fspath == topdir"), - ("pytest_collectstart", "collector.fspath == p"), - ("pytest_make_collect_report", "collector.fspath == p"), + ("pytest_collectstart", "collector.path == topdir"), + ("pytest_make_collect_report", "collector.path == topdir"), + ("pytest_collectstart", "collector.path == p"), + ("pytest_make_collect_report", "collector.path == p"), ("pytest_pycollect_makeitem", "name == 'test_func'"), ("pytest_collectreport", "report.result[0].name == 'test_func'"), ] @@ -547,7 +547,7 @@ def pytest_collect_file(path, parent): assert len(items) == 2 hookrec.assert_contains( [ - ("pytest_collectstart", "collector.fspath == collector.session.fspath"), + ("pytest_collectstart", "collector.path == collector.session.path"), ( "pytest_collectstart", "collector.__class__.__name__ == 'SpecialFile'", @@ -570,7 +570,7 @@ def test_collect_subdir_event_ordering(self, pytester: Pytester) -> None: pprint.pprint(hookrec.calls) hookrec.assert_contains( [ - ("pytest_collectstart", "collector.fspath == test_aaa"), + ("pytest_collectstart", "collector.path == test_aaa"), ("pytest_pycollect_makeitem", "name == 'test_func'"), ("pytest_collectreport", "report.nodeid.startswith('aaa/test_aaa.py')"), ] @@ -592,10 +592,10 @@ def test_collect_two_commandline_args(self, pytester: Pytester) -> None: pprint.pprint(hookrec.calls) hookrec.assert_contains( [ - ("pytest_collectstart", "collector.fspath == test_aaa"), + ("pytest_collectstart", "collector.path == test_aaa"), ("pytest_pycollect_makeitem", "name == 'test_func'"), ("pytest_collectreport", "report.nodeid == 'aaa/test_aaa.py'"), - ("pytest_collectstart", "collector.fspath == test_bbb"), + ("pytest_collectstart", "collector.path == test_bbb"), ("pytest_pycollect_makeitem", "name == 'test_func'"), ("pytest_collectreport", "report.nodeid == 'bbb/test_bbb.py'"), ] @@ -609,7 +609,9 @@ def test_serialization_byid(self, pytester: Pytester) -> None: items2, hookrec = pytester.inline_genitems(item.nodeid) (item2,) = items2 assert item2.name == item.name - assert item2.fspath == item.fspath + with pytest.warns(DeprecationWarning): + assert item2.fspath == item.fspath + assert item2.path == item.path def test_find_byid_without_instance_parents(self, pytester: Pytester) -> None: p = pytester.makepyfile( @@ -1347,14 +1349,10 @@ def test_fscollector_from_parent(pytester: Pytester, request: FixtureRequest) -> """ class MyCollector(pytest.File): - def __init__(self, fspath, parent, x): - super().__init__(fspath, parent) + def __init__(self, *k, x, **kw): + super().__init__(*k, **kw) self.x = x - @classmethod - def from_parent(cls, parent, *, fspath, x): - return super().from_parent(parent=parent, fspath=fspath, x=x) - collector = MyCollector.from_parent( parent=request.session, fspath=py.path.local(pytester.path) / "foo", x=10 ) diff --git a/testing/test_mark.py b/testing/test_mark.py index 420faf91ec9..77991f9e273 100644 --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -1048,11 +1048,12 @@ class TestBarClass(BaseTests): # assert skipped_k == failed_k == 0 -def test_addmarker_order() -> None: +def test_addmarker_order(pytester) -> None: session = mock.Mock() session.own_markers = [] session.parent = None session.nodeid = "" + session.path = pytester.path node = Node.from_parent(session, name="Test") node.add_marker("foo") node.add_marker("bar") diff --git a/testing/test_runner.py b/testing/test_runner.py index abb87c6d3d4..a34cd98f964 100644 --- a/testing/test_runner.py +++ b/testing/test_runner.py @@ -447,9 +447,9 @@ class TestClass(object): assert not rep.skipped assert rep.passed locinfo = rep.location - assert locinfo[0] == col.fspath.basename + assert locinfo[0] == col.path.name assert not locinfo[1] - assert locinfo[2] == col.fspath.basename + assert locinfo[2] == col.path.name res = rep.result assert len(res) == 2 assert res[0].name == "test_func1" diff --git a/testing/test_terminal.py b/testing/test_terminal.py index e536f70989c..53bced8e68e 100644 --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -133,7 +133,7 @@ def test_show_runtest_logstart(self, pytester: Pytester, linecomp) -> None: item.config.pluginmanager.register(tr) location = item.reportinfo() tr.config.hook.pytest_runtest_logstart( - nodeid=item.nodeid, location=location, fspath=str(item.fspath) + nodeid=item.nodeid, location=location, fspath=str(item.path) ) linecomp.assert_contains_lines(["*test_show_runtest_logstart.py*"]) From 77cb110258c09d29eff90078722115ca8a0a1619 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Mon, 22 Feb 2021 09:43:52 +0100 Subject: [PATCH 159/630] drop usage of py.path.local calls Co-authored-by: Bruno Oliveira --- .pre-commit-config.yaml | 6 ++++ src/_pytest/_code/code.py | 3 +- src/_pytest/cacheprovider.py | 7 ++-- src/_pytest/compat.py | 11 +++--- src/_pytest/config/__init__.py | 25 ++++++------- src/_pytest/deprecated.py | 2 +- src/_pytest/doctest.py | 5 ++- src/_pytest/hookspec.py | 22 ++++++------ src/_pytest/main.py | 6 ++-- src/_pytest/nodes.py | 28 +++++++-------- src/_pytest/pytester.py | 65 +++++++++++++++++----------------- src/_pytest/python.py | 20 +++++------ src/_pytest/reports.py | 4 +-- src/_pytest/tmpdir.py | 23 ++++++------ testing/test_collection.py | 10 +++--- testing/test_main.py | 2 +- testing/test_nodes.py | 9 +++-- testing/test_parseopt.py | 9 +++-- testing/test_pathlib.py | 7 ++-- testing/test_reports.py | 5 ++- 20 files changed, 138 insertions(+), 131 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b324b1f484e..24196151952 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -87,3 +87,9 @@ repos: xml\. ) types: [python] + - id: py-path-deprecated + name: py.path usage is deprecated + language: pygrep + entry: \bpy\.path\.local + exclude: docs + types: [python] diff --git a/src/_pytest/_code/code.py b/src/_pytest/_code/code.py index b8521756067..331aaabc780 100644 --- a/src/_pytest/_code/code.py +++ b/src/_pytest/_code/code.py @@ -31,7 +31,6 @@ import attr import pluggy -import py import _pytest from _pytest._code.source import findsource @@ -1230,7 +1229,7 @@ def getfslineno(obj: object) -> Tuple[Union[str, Path], int]: if _PLUGGY_DIR.name == "__init__.py": _PLUGGY_DIR = _PLUGGY_DIR.parent _PYTEST_DIR = Path(_pytest.__file__).parent -_PY_DIR = Path(py.__file__).parent +_PY_DIR = Path(__import__("py").__file__).parent def filter_traceback(entry: TracebackEntry) -> bool: diff --git a/src/_pytest/cacheprovider.py b/src/_pytest/cacheprovider.py index 03e20bea18c..a7ec7989184 100755 --- a/src/_pytest/cacheprovider.py +++ b/src/_pytest/cacheprovider.py @@ -13,7 +13,6 @@ from typing import Union import attr -import py from .pathlib import resolve_from_str from .pathlib import rm_rf @@ -21,6 +20,8 @@ from _pytest import nodes from _pytest._io import TerminalWriter from _pytest.compat import final +from _pytest.compat import LEGACY_PATH +from _pytest.compat import legacy_path from _pytest.config import Config from _pytest.config import ExitCode from _pytest.config import hookimpl @@ -120,7 +121,7 @@ def warn(self, fmt: str, *, _ispytest: bool = False, **args: object) -> None: stacklevel=3, ) - def makedir(self, name: str) -> py.path.local: + def makedir(self, name: str) -> LEGACY_PATH: """Return a directory path object with the given name. If the directory does not yet exist, it will be created. You can use @@ -137,7 +138,7 @@ def makedir(self, name: str) -> py.path.local: raise ValueError("name is not allowed to contain path separators") res = self._cachedir.joinpath(self._CACHE_PREFIX_DIRS, path) res.mkdir(exist_ok=True, parents=True) - return py.path.local(res) + return legacy_path(res) def _getvaluepath(self, key: str) -> Path: return self._cachedir.joinpath(self._CACHE_PREFIX_VALUES, Path(key)) diff --git a/src/_pytest/compat.py b/src/_pytest/compat.py index b9cbf85e04f..4236618e8b4 100644 --- a/src/_pytest/compat.py +++ b/src/_pytest/compat.py @@ -32,15 +32,18 @@ _T = TypeVar("_T") _S = TypeVar("_S") -#: constant to prepare valuing py.path.local replacements/lazy proxies later on +#: constant to prepare valuing pylib path replacements/lazy proxies later on # intended for removal in pytest 8.0 or 9.0 -LEGACY_PATH = py.path.local +# fmt: off +# intentional space to create a fake difference for the verification +LEGACY_PATH = py.path. local +# fmt: on def legacy_path(path: Union[str, "os.PathLike[str]"]) -> LEGACY_PATH: - """Internal wrapper to prepare lazy proxies for py.path.local instances""" - return py.path.local(path) + """Internal wrapper to prepare lazy proxies for legacy_path instances""" + return LEGACY_PATH(path) # fmt: off diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index c029c29a3a2..3f138efa750 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -32,7 +32,6 @@ from typing import Union import attr -import py from pluggy import HookimplMarker from pluggy import HookspecMarker from pluggy import PluginManager @@ -48,6 +47,8 @@ from _pytest._io import TerminalWriter from _pytest.compat import final from _pytest.compat import importlib_metadata +from _pytest.compat import LEGACY_PATH +from _pytest.compat import legacy_path from _pytest.outcomes import fail from _pytest.outcomes import Skipped from _pytest.pathlib import absolutepath @@ -937,15 +938,15 @@ def __init__( self.cache: Optional[Cache] = None @property - def invocation_dir(self) -> py.path.local: + def invocation_dir(self) -> LEGACY_PATH: """The directory from which pytest was invoked. Prefer to use :attr:`invocation_params.dir `, which is a :class:`pathlib.Path`. - :type: py.path.local + :type: LEGACY_PATH """ - return py.path.local(str(self.invocation_params.dir)) + return legacy_path(str(self.invocation_params.dir)) @property def rootpath(self) -> Path: @@ -958,14 +959,14 @@ def rootpath(self) -> Path: return self._rootpath @property - def rootdir(self) -> py.path.local: + def rootdir(self) -> LEGACY_PATH: """The path to the :ref:`rootdir `. Prefer to use :attr:`rootpath`, which is a :class:`pathlib.Path`. - :type: py.path.local + :type: LEGACY_PATH """ - return py.path.local(str(self.rootpath)) + return legacy_path(str(self.rootpath)) @property def inipath(self) -> Optional[Path]: @@ -978,14 +979,14 @@ def inipath(self) -> Optional[Path]: return self._inipath @property - def inifile(self) -> Optional[py.path.local]: + def inifile(self) -> Optional[LEGACY_PATH]: """The path to the :ref:`configfile `. Prefer to use :attr:`inipath`, which is a :class:`pathlib.Path`. - :type: Optional[py.path.local] + :type: Optional[LEGACY_PATH] """ - return py.path.local(str(self.inipath)) if self.inipath else None + return legacy_path(str(self.inipath)) if self.inipath else None def add_cleanup(self, func: Callable[[], None]) -> None: """Add a function to be called when the config object gets out of @@ -1420,7 +1421,7 @@ def _getini(self, name: str): assert self.inipath is not None dp = self.inipath.parent input_values = shlex.split(value) if isinstance(value, str) else value - return [py.path.local(str(dp / x)) for x in input_values] + return [legacy_path(str(dp / x)) for x in input_values] elif type == "args": return shlex.split(value) if isinstance(value, str) else value elif type == "linelist": @@ -1446,7 +1447,7 @@ def _getconftest_pathlist(self, name: str, path: Path) -> Optional[List[Path]]: for relroot in relroots: if isinstance(relroot, Path): pass - elif isinstance(relroot, py.path.local): + elif isinstance(relroot, LEGACY_PATH): relroot = Path(relroot) else: relroot = relroot.replace("/", os.sep) diff --git a/src/_pytest/deprecated.py b/src/_pytest/deprecated.py index c203eadc1ad..596574877bf 100644 --- a/src/_pytest/deprecated.py +++ b/src/_pytest/deprecated.py @@ -92,7 +92,7 @@ NODE_FSPATH = UnformattedWarning( PytestDeprecationWarning, "{type}.fspath is deprecated and will be replaced by {type}.path.\n" - "see TODO;URL for details on replacing py.path.local with pathlib.Path", + "see https://docs.pytest.org/en/latest/deprecations.html#node-fspath-in-favor-of-pathlib-and-node-path", ) # You want to make some `__init__` or function "private". diff --git a/src/_pytest/doctest.py b/src/_pytest/doctest.py index 4942a8f793b..41d295daaba 100644 --- a/src/_pytest/doctest.py +++ b/src/_pytest/doctest.py @@ -22,14 +22,13 @@ from typing import TYPE_CHECKING from typing import Union -import py.path - import pytest from _pytest import outcomes from _pytest._code.code import ExceptionInfo from _pytest._code.code import ReprFileLocation from _pytest._code.code import TerminalRepr from _pytest._io import TerminalWriter +from _pytest.compat import LEGACY_PATH from _pytest.compat import legacy_path from _pytest.compat import safe_getattr from _pytest.config import Config @@ -123,7 +122,7 @@ def pytest_unconfigure() -> None: def pytest_collect_file( fspath: Path, - path: py.path.local, + path: LEGACY_PATH, parent: Collector, ) -> Optional[Union["DoctestModule", "DoctestTextfile"]]: config = parent.config diff --git a/src/_pytest/hookspec.py b/src/_pytest/hookspec.py index b0b8fd53d85..7d5f767db7e 100644 --- a/src/_pytest/hookspec.py +++ b/src/_pytest/hookspec.py @@ -11,7 +11,6 @@ from typing import TYPE_CHECKING from typing import Union -import py.path from pluggy import HookspecMarker from _pytest.deprecated import WARNING_CAPTURED_HOOK @@ -42,6 +41,7 @@ from _pytest.reports import TestReport from _pytest.runner import CallInfo from _pytest.terminal import TerminalReporter + from _pytest.compat import LEGACY_PATH hookspec = HookspecMarker("pytest") @@ -263,7 +263,7 @@ def pytest_collection_finish(session: "Session") -> None: @hookspec(firstresult=True) def pytest_ignore_collect( - fspath: Path, path: py.path.local, config: "Config" + fspath: Path, path: "LEGACY_PATH", config: "Config" ) -> Optional[bool]: """Return True to prevent considering this path for collection. @@ -273,7 +273,7 @@ def pytest_ignore_collect( Stops at first non-None result, see :ref:`firstresult`. :param pathlib.Path fspath: The path to analyze. - :param py.path.local path: The path to analyze. + :param LEGACY_PATH path: The path to analyze. :param _pytest.config.Config config: The pytest config object. .. versionchanged:: 6.3.0 @@ -283,14 +283,14 @@ def pytest_ignore_collect( def pytest_collect_file( - fspath: Path, path: py.path.local, parent: "Collector" + fspath: Path, path: "LEGACY_PATH", parent: "Collector" ) -> "Optional[Collector]": """Create a Collector for the given path, or None if not relevant. The new node needs to have the specified ``parent`` as a parent. :param pathlib.Path fspath: The path to analyze. - :param py.path.local path: The path to collect. + :param LEGACY_PATH path: The path to collect. .. versionchanged:: 6.3.0 The ``fspath`` parameter was added as a :class:`pathlib.Path` @@ -335,7 +335,7 @@ def pytest_make_collect_report(collector: "Collector") -> "Optional[CollectRepor @hookspec(firstresult=True) def pytest_pycollect_makemodule( - fspath: Path, path: py.path.local, parent + fspath: Path, path: "LEGACY_PATH", parent ) -> Optional["Module"]: """Return a Module collector or None for the given path. @@ -346,7 +346,7 @@ def pytest_pycollect_makemodule( Stops at first non-None result, see :ref:`firstresult`. :param pathlib.Path fspath: The path of the module to collect. - :param py.path.local path: The path of the module to collect. + :param legacy_path path: The path of the module to collect. .. versionchanged:: 6.3.0 The ``fspath`` parameter was added as a :class:`pathlib.Path` @@ -676,13 +676,13 @@ def pytest_assertion_pass(item: "Item", lineno: int, orig: str, expl: str) -> No def pytest_report_header( - config: "Config", startpath: Path, startdir: py.path.local + config: "Config", startpath: Path, startdir: "LEGACY_PATH" ) -> Union[str, List[str]]: """Return a string or list of strings to be displayed as header info for terminal reporting. :param _pytest.config.Config config: The pytest config object. :param Path startpath: The starting dir. - :param py.path.local startdir: The starting dir. + :param LEGACY_PATH startdir: The starting dir. .. note:: @@ -706,7 +706,7 @@ def pytest_report_header( def pytest_report_collectionfinish( config: "Config", startpath: Path, - startdir: py.path.local, + startdir: "LEGACY_PATH", items: Sequence["Item"], ) -> Union[str, List[str]]: """Return a string or list of strings to be displayed after collection @@ -718,7 +718,7 @@ def pytest_report_collectionfinish( :param _pytest.config.Config config: The pytest config object. :param Path startpath: The starting path. - :param py.path.local startdir: The starting dir. + :param LEGACY_PATH startdir: The starting dir. :param items: List of pytest items that are going to be executed; this list should not be modified. .. note:: diff --git a/src/_pytest/main.py b/src/_pytest/main.py index 3dc00fa691e..3e7213489ff 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -21,11 +21,11 @@ from typing import Union import attr -import py import _pytest._code from _pytest import nodes from _pytest.compat import final +from _pytest.compat import legacy_path from _pytest.config import Config from _pytest.config import directory_arg from _pytest.config import ExitCode @@ -543,7 +543,7 @@ def _recurse(self, direntry: "os.DirEntry[str]") -> bool: if direntry.name == "__pycache__": return False fspath = Path(direntry.path) - path = py.path.local(fspath) + path = legacy_path(fspath) ihook = self.gethookproxy(fspath.parent) if ihook.pytest_ignore_collect(fspath=fspath, path=path, config=self.config): return False @@ -555,7 +555,7 @@ def _recurse(self, direntry: "os.DirEntry[str]") -> bool: def _collectfile( self, fspath: Path, handle_dupes: bool = True ) -> Sequence[nodes.Collector]: - path = py.path.local(fspath) + path = legacy_path(fspath) assert ( fspath.is_file() ), "{!r} is not a file (isdir={!r}, exists={!r}, islink={!r})".format( diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py index 47752d34c61..9d93659e1ad 100644 --- a/src/_pytest/nodes.py +++ b/src/_pytest/nodes.py @@ -16,8 +16,6 @@ from typing import TypeVar from typing import Union -import py - import _pytest._code from _pytest._code import getfslineno from _pytest._code.code import ExceptionInfo @@ -145,7 +143,7 @@ def __init__( parent: "Optional[Node]" = None, config: Optional[Config] = None, session: "Optional[Session]" = None, - fspath: Optional[py.path.local] = None, + fspath: Optional[LEGACY_PATH] = None, path: Optional[Path] = None, nodeid: Optional[str] = None, ) -> None: @@ -199,13 +197,13 @@ def __init__( self._store = Store() @property - def fspath(self): - """(deprecated) returns a py.path.local copy of self.path""" + def fspath(self) -> LEGACY_PATH: + """(deprecated) returns a legacy_path copy of self.path""" warnings.warn(NODE_FSPATH.format(type=type(self).__name__), stacklevel=2) - return py.path.local(self.path) + return legacy_path(self.path) @fspath.setter - def fspath(self, value: py.path.local): + def fspath(self, value: LEGACY_PATH) -> None: warnings.warn(NODE_FSPATH.format(type=type(self).__name__), stacklevel=2) self.path = Path(value) @@ -464,7 +462,7 @@ def get_fslocation_from_item(node: "Node") -> Tuple[Union[str, Path], Optional[i * "obj": a Python object that the node wraps. * "fspath": just a path - :rtype: A tuple of (str|py.path.local, int) with filename and line number. + :rtype: A tuple of (str|Path, int) with filename and line number. """ # See Item.location. location: Optional[Tuple[str, Optional[int], str]] = getattr(node, "location", None) @@ -520,10 +518,10 @@ def _prunetraceback(self, excinfo: ExceptionInfo[BaseException]) -> None: def _check_initialpaths_for_relpath( - session: "Session", fspath: py.path.local + session: "Session", fspath: LEGACY_PATH ) -> Optional[str]: for initial_path in session._initialpaths: - initial_path_ = py.path.local(initial_path) + initial_path_ = legacy_path(initial_path) if fspath.common(initial_path_) == initial_path_: return fspath.relto(initial_path_) return None @@ -532,7 +530,7 @@ def _check_initialpaths_for_relpath( class FSCollector(Collector): def __init__( self, - fspath: Optional[py.path.local], + fspath: Optional[LEGACY_PATH], path: Optional[Path], parent=None, config: Optional[Config] = None, @@ -571,7 +569,7 @@ def from_parent( cls, parent, *, - fspath: Optional[py.path.local] = None, + fspath: Optional[LEGACY_PATH] = None, path: Optional[Path] = None, **kw, ): @@ -638,8 +636,10 @@ def add_report_section(self, when: str, key: str, content: str) -> None: if content: self._report_sections.append((when, key, content)) - def reportinfo(self) -> Tuple[Union[py.path.local, str], Optional[int], str]: - return self.fspath, None, "" + def reportinfo(self) -> Tuple[Union[LEGACY_PATH, str], Optional[int], str]: + + # TODO: enable Path objects in reportinfo + return legacy_path(self.path), None, "" @cached_property def location(self) -> Tuple[str, Optional[int], str]: diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index f2a6d2aab92..699738e1282 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -34,7 +34,6 @@ from weakref import WeakKeyDictionary import attr -import py from iniconfig import IniConfig from iniconfig import SectionWrapper @@ -42,6 +41,8 @@ from _pytest._code import Source from _pytest.capture import _get_multicapture from _pytest.compat import final +from _pytest.compat import LEGACY_PATH +from _pytest.compat import legacy_path from _pytest.compat import NOTSET from _pytest.compat import NotSetType from _pytest.config import _PluggyPlugin @@ -475,7 +476,7 @@ def pytester(request: FixtureRequest, tmp_path_factory: TempPathFactory) -> "Pyt def testdir(pytester: "Pytester") -> "Testdir": """ Identical to :fixture:`pytester`, and provides an instance whose methods return - legacy ``py.path.local`` objects instead when applicable. + legacy ``LEGACY_PATH`` objects instead when applicable. New code should avoid using :fixture:`testdir` in favor of :fixture:`pytester`. """ @@ -934,10 +935,10 @@ def copy_example(self, name: Optional[str] = None) -> Path: example_path = example_dir.joinpath(name) if example_path.is_dir() and not example_path.joinpath("__init__.py").is_file(): - # TODO: py.path.local.copy can copy files to existing directories, + # TODO: legacy_path.copy can copy files to existing directories, # while with shutil.copytree the destination directory cannot exist, - # we will need to roll our own in order to drop py.path.local completely - py.path.local(example_path).copy(py.path.local(self.path)) + # we will need to roll our own in order to drop legacy_path completely + legacy_path(example_path).copy(legacy_path(self.path)) return self.path elif example_path.is_file(): result = self.path.joinpath(example_path.name) @@ -958,12 +959,12 @@ def getnode( :param _pytest.config.Config config: A pytest config. See :py:meth:`parseconfig` and :py:meth:`parseconfigure` for creating it. - :param py.path.local arg: + :param os.PathLike[str] arg: Path to the file. """ session = Session.from_config(config) assert "::" not in str(arg) - p = py.path.local(arg) + p = legacy_path(arg) config.hook.pytest_sessionstart(session=session) res = session.perform_collect([str(p)], genitems=False)[0] config.hook.pytest_sessionfinish(session=session, exitstatus=ExitCode.OK) @@ -975,7 +976,7 @@ def getpathnode(self, path: Union[str, "os.PathLike[str]"]): This is like :py:meth:`getnode` but uses :py:meth:`parseconfigure` to create the (configured) pytest Config instance. - :param py.path.local path: Path to the file. + :param os.PathLike[str] path: Path to the file. """ path = Path(path) config = self.parseconfigure(path) @@ -1520,10 +1521,10 @@ def assert_contains_lines(self, lines2: Sequence[str]) -> None: @attr.s(repr=False, str=False, init=False) class Testdir: """ - Similar to :class:`Pytester`, but this class works with legacy py.path.local objects instead. + Similar to :class:`Pytester`, but this class works with legacy legacy_path objects instead. All methods just forward to an internal :class:`Pytester` instance, converting results - to `py.path.local` objects as necessary. + to `legacy_path` objects as necessary. """ __test__ = False @@ -1537,13 +1538,13 @@ def __init__(self, pytester: Pytester, *, _ispytest: bool = False) -> None: self._pytester = pytester @property - def tmpdir(self) -> py.path.local: + def tmpdir(self) -> LEGACY_PATH: """Temporary directory where tests are executed.""" - return py.path.local(self._pytester.path) + return legacy_path(self._pytester.path) @property - def test_tmproot(self) -> py.path.local: - return py.path.local(self._pytester._test_tmproot) + def test_tmproot(self) -> LEGACY_PATH: + return legacy_path(self._pytester._test_tmproot) @property def request(self): @@ -1573,7 +1574,7 @@ def finalize(self) -> None: """See :meth:`Pytester._finalize`.""" return self._pytester._finalize() - def makefile(self, ext, *args, **kwargs) -> py.path.local: + def makefile(self, ext, *args, **kwargs) -> LEGACY_PATH: """See :meth:`Pytester.makefile`.""" if ext and not ext.startswith("."): # pytester.makefile is going to throw a ValueError in a way that @@ -1583,47 +1584,47 @@ def makefile(self, ext, *args, **kwargs) -> py.path.local: # allowed this, we will prepend "." as a workaround to avoid breaking # testdir usage that worked before ext = "." + ext - return py.path.local(str(self._pytester.makefile(ext, *args, **kwargs))) + return legacy_path(self._pytester.makefile(ext, *args, **kwargs)) - def makeconftest(self, source) -> py.path.local: + def makeconftest(self, source) -> LEGACY_PATH: """See :meth:`Pytester.makeconftest`.""" - return py.path.local(str(self._pytester.makeconftest(source))) + return legacy_path(self._pytester.makeconftest(source)) - def makeini(self, source) -> py.path.local: + def makeini(self, source) -> LEGACY_PATH: """See :meth:`Pytester.makeini`.""" - return py.path.local(str(self._pytester.makeini(source))) + return legacy_path(self._pytester.makeini(source)) def getinicfg(self, source: str) -> SectionWrapper: """See :meth:`Pytester.getinicfg`.""" return self._pytester.getinicfg(source) - def makepyprojecttoml(self, source) -> py.path.local: + def makepyprojecttoml(self, source) -> LEGACY_PATH: """See :meth:`Pytester.makepyprojecttoml`.""" - return py.path.local(str(self._pytester.makepyprojecttoml(source))) + return legacy_path(self._pytester.makepyprojecttoml(source)) - def makepyfile(self, *args, **kwargs) -> py.path.local: + def makepyfile(self, *args, **kwargs) -> LEGACY_PATH: """See :meth:`Pytester.makepyfile`.""" - return py.path.local(str(self._pytester.makepyfile(*args, **kwargs))) + return legacy_path(self._pytester.makepyfile(*args, **kwargs)) - def maketxtfile(self, *args, **kwargs) -> py.path.local: + def maketxtfile(self, *args, **kwargs) -> LEGACY_PATH: """See :meth:`Pytester.maketxtfile`.""" - return py.path.local(str(self._pytester.maketxtfile(*args, **kwargs))) + return legacy_path(self._pytester.maketxtfile(*args, **kwargs)) def syspathinsert(self, path=None) -> None: """See :meth:`Pytester.syspathinsert`.""" return self._pytester.syspathinsert(path) - def mkdir(self, name) -> py.path.local: + def mkdir(self, name) -> LEGACY_PATH: """See :meth:`Pytester.mkdir`.""" - return py.path.local(str(self._pytester.mkdir(name))) + return legacy_path(self._pytester.mkdir(name)) - def mkpydir(self, name) -> py.path.local: + def mkpydir(self, name) -> LEGACY_PATH: """See :meth:`Pytester.mkpydir`.""" - return py.path.local(str(self._pytester.mkpydir(name))) + return legacy_path(self._pytester.mkpydir(name)) - def copy_example(self, name=None) -> py.path.local: + def copy_example(self, name=None) -> LEGACY_PATH: """See :meth:`Pytester.copy_example`.""" - return py.path.local(str(self._pytester.copy_example(name))) + return legacy_path(self._pytester.copy_example(name)) def getnode(self, config: Config, arg) -> Optional[Union[Item, Collector]]: """See :meth:`Pytester.getnode`.""" diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 7d518dbbf4b..ccd685f54a9 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -26,8 +26,6 @@ from typing import TYPE_CHECKING from typing import Union -import py - import _pytest from _pytest import fixtures from _pytest import nodes @@ -45,6 +43,8 @@ from _pytest.compat import getlocation from _pytest.compat import is_async_function from _pytest.compat import is_generator +from _pytest.compat import LEGACY_PATH +from _pytest.compat import legacy_path from _pytest.compat import NOTSET from _pytest.compat import REGEX_TYPE from _pytest.compat import safe_getattr @@ -189,7 +189,7 @@ def pytest_pyfunc_call(pyfuncitem: "Function") -> Optional[object]: def pytest_collect_file( - fspath: Path, path: py.path.local, parent: nodes.Collector + fspath: Path, path: LEGACY_PATH, parent: nodes.Collector ) -> Optional["Module"]: if fspath.suffix == ".py": if not parent.session.isinitpath(fspath): @@ -210,7 +210,7 @@ def path_matches_patterns(path: Path, patterns: Iterable[str]) -> bool: return any(fnmatch_ex(pattern, path) for pattern in patterns) -def pytest_pycollect_makemodule(fspath: Path, path: py.path.local, parent) -> "Module": +def pytest_pycollect_makemodule(fspath: Path, path: LEGACY_PATH, parent) -> "Module": if fspath.name == "__init__.py": pkg: Package = Package.from_parent(parent, fspath=path) return pkg @@ -321,7 +321,7 @@ def getmodpath(self, stopatmodule: bool = True, includemodule: bool = False) -> parts.reverse() return ".".join(parts) - def reportinfo(self) -> Tuple[Union[py.path.local, str], int, str]: + def reportinfo(self) -> Tuple[Union[LEGACY_PATH, str], int, str]: # XXX caching? obj = self.obj compat_co_firstlineno = getattr(obj, "compat_co_firstlineno", None) @@ -330,12 +330,12 @@ def reportinfo(self) -> Tuple[Union[py.path.local, str], int, str]: file_path = sys.modules[obj.__module__].__file__ if file_path.endswith(".pyc"): file_path = file_path[:-1] - fspath: Union[py.path.local, str] = file_path + fspath: Union[LEGACY_PATH, str] = file_path lineno = compat_co_firstlineno else: path, lineno = getfslineno(obj) if isinstance(path, Path): - fspath = py.path.local(path) + fspath = legacy_path(path) else: fspath = path modpath = self.getmodpath() @@ -624,7 +624,7 @@ def _importtestmodule(self): class Package(Module): def __init__( self, - fspath: Optional[py.path.local], + fspath: Optional[LEGACY_PATH], parent: nodes.Collector, # NOTE: following args are unused: config=None, @@ -675,7 +675,7 @@ def _recurse(self, direntry: "os.DirEntry[str]") -> bool: if direntry.name == "__pycache__": return False fspath = Path(direntry.path) - path = py.path.local(fspath) + path = legacy_path(fspath) ihook = self.session.gethookproxy(fspath.parent) if ihook.pytest_ignore_collect(fspath=fspath, path=path, config=self.config): return False @@ -687,7 +687,7 @@ def _recurse(self, direntry: "os.DirEntry[str]") -> bool: def _collectfile( self, fspath: Path, handle_dupes: bool = True ) -> Sequence[nodes.Collector]: - path = py.path.local(fspath) + path = legacy_path(fspath) assert ( fspath.is_file() ), "{!r} is not a file (isdir={!r}, exists={!r}, islink={!r})".format( diff --git a/src/_pytest/reports.py b/src/_pytest/reports.py index 303f731ddaa..657e0683378 100644 --- a/src/_pytest/reports.py +++ b/src/_pytest/reports.py @@ -15,7 +15,6 @@ from typing import Union import attr -import py from _pytest._code.code import ExceptionChainRepr from _pytest._code.code import ExceptionInfo @@ -30,6 +29,7 @@ from _pytest._code.code import TerminalRepr from _pytest._io import TerminalWriter from _pytest.compat import final +from _pytest.compat import LEGACY_PATH from _pytest.config import Config from _pytest.nodes import Collector from _pytest.nodes import Item @@ -500,7 +500,7 @@ def serialize_exception_longrepr(rep: BaseReport) -> Dict[str, Any]: else: d["longrepr"] = report.longrepr for name in d: - if isinstance(d[name], (py.path.local, Path)): + if isinstance(d[name], (LEGACY_PATH, Path)): d[name] = str(d[name]) elif name == "result": d[name] = None # for now diff --git a/src/_pytest/tmpdir.py b/src/_pytest/tmpdir.py index 47729ae5fee..d30e1e57feb 100644 --- a/src/_pytest/tmpdir.py +++ b/src/_pytest/tmpdir.py @@ -6,13 +6,14 @@ from typing import Optional import attr -import py from .pathlib import ensure_reset_dir from .pathlib import LOCK_TIMEOUT from .pathlib import make_numbered_dir from .pathlib import make_numbered_dir_with_cleanup from _pytest.compat import final +from _pytest.compat import LEGACY_PATH +from _pytest.compat import legacy_path from _pytest.config import Config from _pytest.deprecated import check_ispytest from _pytest.fixtures import fixture @@ -133,7 +134,7 @@ def getbasetemp(self) -> Path: @final @attr.s(init=False) class TempdirFactory: - """Backward comptibility wrapper that implements :class:``py.path.local`` + """Backward comptibility wrapper that implements :class:``_pytest.compat.LEGACY_PATH`` for :class:``TempPathFactory``.""" _tmppath_factory = attr.ib(type=TempPathFactory) @@ -144,13 +145,13 @@ def __init__( check_ispytest(_ispytest) self._tmppath_factory = tmppath_factory - def mktemp(self, basename: str, numbered: bool = True) -> py.path.local: - """Same as :meth:`TempPathFactory.mktemp`, but returns a ``py.path.local`` object.""" - return py.path.local(self._tmppath_factory.mktemp(basename, numbered).resolve()) + def mktemp(self, basename: str, numbered: bool = True) -> LEGACY_PATH: + """Same as :meth:`TempPathFactory.mktemp`, but returns a ``_pytest.compat.LEGACY_PATH`` object.""" + return legacy_path(self._tmppath_factory.mktemp(basename, numbered).resolve()) - def getbasetemp(self) -> py.path.local: + def getbasetemp(self) -> LEGACY_PATH: """Backward compat wrapper for ``_tmppath_factory.getbasetemp``.""" - return py.path.local(self._tmppath_factory.getbasetemp().resolve()) + return legacy_path(self._tmppath_factory.getbasetemp().resolve()) def get_user() -> Optional[str]: @@ -202,7 +203,7 @@ def _mk_tmp(request: FixtureRequest, factory: TempPathFactory) -> Path: @fixture -def tmpdir(tmp_path: Path) -> py.path.local: +def tmpdir(tmp_path: Path) -> LEGACY_PATH: """Return a temporary directory path object which is unique to each test function invocation, created as a sub directory of the base temporary directory. @@ -212,11 +213,11 @@ def tmpdir(tmp_path: Path) -> py.path.local: ``--basetemp`` is used then it is cleared each session. See :ref:`base temporary directory`. - The returned object is a `py.path.local`_ path object. + The returned object is a `legacy_path`_ object. - .. _`py.path.local`: https://py.readthedocs.io/en/latest/path.html + .. _legacy_path: https://py.readthedocs.io/en/latest/path.html """ - return py.path.local(tmp_path) + return legacy_path(tmp_path) @fixture diff --git a/testing/test_collection.py b/testing/test_collection.py index 248071111f3..5f0b5902ab8 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -6,8 +6,6 @@ from pathlib import Path from typing import List -import py.path - import pytest from _pytest.config import ExitCode from _pytest.fixtures import FixtureRequest @@ -369,9 +367,10 @@ def pytest_ignore_collect(path, config): def test_collectignore_exclude_on_option(self, pytester: Pytester) -> None: pytester.makeconftest( """ - import py + # potentially avoid dependency on pylib + from _pytest.compat import legacy_path from pathlib import Path - collect_ignore = [py.path.local('hello'), 'test_world.py', Path('bye')] + collect_ignore = [legacy_path('hello'), 'test_world.py', Path('bye')] def pytest_addoption(parser): parser.addoption("--XX", action="store_true", default=False) def pytest_configure(config): @@ -1347,6 +1346,7 @@ def test_fscollector_from_parent(pytester: Pytester, request: FixtureRequest) -> Context: https://github.com/pytest-dev/pytest-cpp/pull/47 """ + from _pytest.compat import legacy_path class MyCollector(pytest.File): def __init__(self, *k, x, **kw): @@ -1354,7 +1354,7 @@ def __init__(self, *k, x, **kw): self.x = x collector = MyCollector.from_parent( - parent=request.session, fspath=py.path.local(pytester.path) / "foo", x=10 + parent=request.session, fspath=legacy_path(pytester.path) / "foo", x=10 ) assert collector.x == 10 diff --git a/testing/test_main.py b/testing/test_main.py index 2ed111895cd..4450029342e 100644 --- a/testing/test_main.py +++ b/testing/test_main.py @@ -205,7 +205,7 @@ def test_absolute_paths_are_resolved_correctly(self, invocation_path: Path) -> N def test_module_full_path_without_drive(pytester: Pytester) -> None: """Collect and run test using full path except for the drive letter (#7628). - Passing a full path without a drive letter would trigger a bug in py.path.local + Passing a full path without a drive letter would trigger a bug in legacy_path where it would keep the full path without the drive letter around, instead of resolving to the full path, resulting in fixtures node ids not matching against test node ids correctly. """ diff --git a/testing/test_nodes.py b/testing/test_nodes.py index 59d9f409eac..dde161777cd 100644 --- a/testing/test_nodes.py +++ b/testing/test_nodes.py @@ -3,10 +3,9 @@ from typing import List from typing import Type -import py - import pytest from _pytest import nodes +from _pytest.compat import legacy_path from _pytest.pytester import Pytester from _pytest.warning_types import PytestWarning @@ -77,7 +76,7 @@ class FakeSession1: session = cast(pytest.Session, FakeSession1) - assert nodes._check_initialpaths_for_relpath(session, py.path.local(cwd)) == "" + assert nodes._check_initialpaths_for_relpath(session, legacy_path(cwd)) == "" sub = cwd / "file" @@ -86,9 +85,9 @@ class FakeSession2: session = cast(pytest.Session, FakeSession2) - assert nodes._check_initialpaths_for_relpath(session, py.path.local(sub)) == "file" + assert nodes._check_initialpaths_for_relpath(session, legacy_path(sub)) == "file" - outside = py.path.local("/outside") + outside = legacy_path("/outside") assert nodes._check_initialpaths_for_relpath(session, outside) is None diff --git a/testing/test_parseopt.py b/testing/test_parseopt.py index c33337b67b3..6ba9269e564 100644 --- a/testing/test_parseopt.py +++ b/testing/test_parseopt.py @@ -4,9 +4,8 @@ import subprocess import sys -import py - import pytest +from _pytest.compat import legacy_path from _pytest.config import argparsing as parseopt from _pytest.config.exceptions import UsageError from _pytest.monkeypatch import MonkeyPatch @@ -124,11 +123,11 @@ def test_parse(self, parser: parseopt.Parser) -> None: assert not getattr(args, parseopt.FILE_OR_DIR) def test_parse2(self, parser: parseopt.Parser) -> None: - args = parser.parse([py.path.local()]) - assert getattr(args, parseopt.FILE_OR_DIR)[0] == py.path.local() + args = parser.parse([legacy_path(".")]) + assert getattr(args, parseopt.FILE_OR_DIR)[0] == legacy_path(".") def test_parse_known_args(self, parser: parseopt.Parser) -> None: - parser.parse_known_args([py.path.local()]) + parser.parse_known_args([legacy_path(".")]) parser.addoption("--hello", action="store_true") ns = parser.parse_known_args(["x", "--y", "--hello", "this"]) assert ns.hello diff --git a/testing/test_pathlib.py b/testing/test_pathlib.py index 48149084ece..d71e44e36b6 100644 --- a/testing/test_pathlib.py +++ b/testing/test_pathlib.py @@ -7,9 +7,8 @@ from types import ModuleType from typing import Generator -import py - import pytest +from _pytest.compat import legacy_path from _pytest.monkeypatch import MonkeyPatch from _pytest.pathlib import bestrelpath from _pytest.pathlib import commonpath @@ -28,14 +27,14 @@ class TestFNMatcherPort: """Test that our port of py.common.FNMatcher (fnmatch_ex) produces the - same results as the original py.path.local.fnmatch method.""" + same results as the original legacy_path.fnmatch method.""" @pytest.fixture(params=["pathlib", "py.path"]) def match(self, request): if request.param == "py.path": def match_(pattern, path): - return py.path.local(path).fnmatch(pattern) + return legacy_path(path).fnmatch(pattern) else: assert request.param == "pathlib" diff --git a/testing/test_reports.py b/testing/test_reports.py index b376f6198ae..3da63c2c873 100644 --- a/testing/test_reports.py +++ b/testing/test_reports.py @@ -1,11 +1,10 @@ from typing import Sequence from typing import Union -import py.path - import pytest from _pytest._code.code import ExceptionChainRepr from _pytest._code.code import ExceptionRepr +from _pytest.compat import legacy_path from _pytest.config import Config from _pytest.pytester import Pytester from _pytest.reports import CollectReport @@ -237,7 +236,7 @@ def test_a(): reports = reprec.getreports("pytest_runtest_logreport") assert len(reports) == 3 test_a_call = reports[1] - test_a_call.path1 = py.path.local(pytester.path) # type: ignore[attr-defined] + test_a_call.path1 = legacy_path(pytester.path) # type: ignore[attr-defined] test_a_call.path2 = pytester.path # type: ignore[attr-defined] data = test_a_call._to_json() assert data["path1"] == str(pytester.path) From 620e8196561e0d8b359b25f1c494ea0002d3cb3b Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 6 Mar 2021 22:59:33 +0100 Subject: [PATCH 160/630] Add enterPy training (#8396) --- doc/en/index.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/en/index.rst b/doc/en/index.rst index 084725ec2c1..7f74534027c 100644 --- a/doc/en/index.rst +++ b/doc/en/index.rst @@ -1,5 +1,11 @@ :orphan: +.. sidebar:: Next Open Trainings + + - `Professionelles Testen für Python mit pytest `_ (German), part of the enterPy conference, April 22nd, remote. + + Also see `previous talks and blogposts `_. + .. _features: pytest: helps you write better programs From f0ad73c4b05b4307e3dbf890f4e2772ea73e0a2e Mon Sep 17 00:00:00 2001 From: pytest bot Date: Sun, 7 Mar 2021 00:48:50 +0000 Subject: [PATCH 161/630] [automated] Update plugin list --- doc/en/plugin_list.rst | 43 ++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/doc/en/plugin_list.rst b/doc/en/plugin_list.rst index f50b6033ff1..8426f469c94 100644 --- a/doc/en/plugin_list.rst +++ b/doc/en/plugin_list.rst @@ -3,7 +3,7 @@ Plugins List PyPI projects that match "pytest-\*" are considered plugins and are listed automatically. Packages classified as inactive are excluded. -This list contains 833 plugins. +This list contains 836 plugins. ============================================================================================================== ======================================================================================================================================================================== ============== ===================== ============================================ name summary last release status requires @@ -11,7 +11,7 @@ name `pytest-adaptavist `_ pytest plugin for generating test execution results within Jira Test Management (tm4j) Feb 05, 2020 N/A pytest (>=3.4.1) `pytest-adf `_ Pytest plugin for writing Azure Data Factory integration tests Jun 03, 2020 4 - Beta pytest (>=3.5.0) `pytest-aggreport `_ pytest plugin for pytest-repeat that generate aggregate report of the same test cases with additional statistics details. Jul 19, 2019 4 - Beta pytest (>=4.3.1) -`pytest-aio `_ Pytest plugin for testing async python code Feb 27, 2021 4 - Beta pytest ; extra == 'tests' +`pytest-aio `_ Pytest plugin for testing async python code Mar 02, 2021 4 - Beta pytest ; extra == 'tests' `pytest-aiofiles `_ pytest fixtures for writing aiofiles tests with pyfakefs May 14, 2017 5 - Production/Stable N/A `pytest-aiohttp `_ pytest plugin for aiohttp support Dec 05, 2017 N/A pytest `pytest-aiohttp-client `_ Pytest `client` fixture for the Aiohttp Nov 01, 2020 N/A pytest (>=6) @@ -152,8 +152,9 @@ name `pytest-curl-report `_ pytest plugin to generate curl command line report Dec 11, 2016 4 - Beta N/A `pytest-custom-concurrency `_ Custom grouping concurrence for pytest Feb 08, 2021 N/A N/A `pytest-custom-exit-code `_ Exit pytest test session with custom exit code in different scenarios Aug 07, 2019 4 - Beta pytest (>=4.0.2) +`pytest-custom-nodeid `_ Custom grouping for pytest-xdist, rename test cases name and test cases nodeid, support allure report Mar 02, 2021 N/A N/A `pytest-custom-report `_ Configure the symbols displayed for test outcomes Jan 30, 2019 N/A pytest -`pytest-custom-scheduling `_ Custom grouping for pytest-xdist Feb 22, 2021 N/A N/A +`pytest-custom-scheduling `_ Custom grouping for pytest-xdist, rename test cases name and test cases nodeid, support allure report Mar 01, 2021 N/A N/A `pytest-cython `_ A plugin for testing Cython extension modules Jan 26, 2021 4 - Beta pytest (>=2.7.3) `pytest-darker `_ A pytest plugin for checking of modified code using Darker Aug 16, 2020 N/A pytest (>=6.0.1) ; extra == 'test' `pytest-dash `_ pytest fixtures to run dash applications. Mar 18, 2019 N/A N/A @@ -200,7 +201,7 @@ name `pytest-django-lite `_ The bare minimum to integrate py.test with Django. Jan 30, 2014 N/A N/A `pytest-django-model `_ A Simple Way to Test your Django Models Feb 14, 2019 4 - Beta N/A `pytest-django-ordering `_ A pytest plugin for preserving the order in which Django runs tests. Jul 25, 2019 5 - Production/Stable pytest (>=2.3.0) -`pytest-django-queries `_ Generate performance reports from your django database performance tests. Sep 03, 2020 N/A N/A +`pytest-django-queries `_ Generate performance reports from your django database performance tests. Mar 01, 2021 N/A N/A `pytest-djangorestframework `_ A djangorestframework plugin for pytest Aug 11, 2019 4 - Beta N/A `pytest-django-rq `_ A pytest plugin to help writing unit test for django-rq Apr 13, 2020 4 - Beta N/A `pytest-django-sqlcounts `_ py.test plugin for reporting the number of SQLs executed per django testcase. Jun 16, 2015 4 - Beta N/A @@ -216,8 +217,8 @@ name `pytest-docker-pexpect `_ pytest plugin for writing functional tests with pexpect and docker Jan 14, 2019 N/A pytest `pytest-docker-postgresql `_ A simple plugin to use with pytest Sep 24, 2019 4 - Beta pytest (>=3.5.0) `pytest-docker-py `_ Easy to use, simple to extend, pytest plugin that minimally leverages docker-py. Nov 27, 2018 N/A pytest (==4.0.0) -`pytest-docker-registry-fixtures `_ Pytest fixtures for testing with docker registries. Feb 17, 2021 4 - Beta pytest -`pytest-docker-tools `_ Docker integration tests for pytest Jan 15, 2021 4 - Beta pytest (>=6.0.1,<7.0.0) +`pytest-docker-registry-fixtures `_ Pytest fixtures for testing with docker registries. Mar 04, 2021 4 - Beta pytest +`pytest-docker-tools `_ Docker integration tests for pytest Mar 02, 2021 4 - Beta pytest (>=6.0.1,<7.0.0) `pytest-docs `_ Documentation tool for pytest Nov 11, 2018 4 - Beta pytest (>=3.5.0) `pytest-docstyle `_ pytest plugin to run pydocstyle Mar 23, 2020 3 - Alpha N/A `pytest-doctest-custom `_ A py.test plugin for customizing string representations of doctest results. Jul 25, 2016 4 - Beta N/A @@ -274,6 +275,7 @@ name `pytest-factoryboy `_ Factory Boy support for pytest. Dec 30, 2020 6 - Mature pytest (>=4.6) `pytest-factoryboy-fixtures `_ Generates pytest fixtures that allow the use of type hinting Jun 25, 2020 N/A N/A `pytest-factoryboy-state `_ Simple factoryboy random state management Dec 11, 2020 4 - Beta pytest (>=5.0) +`pytest-failed-screenshot `_ Test case fails,take a screenshot,save it,attach it to the allure Feb 28, 2021 N/A N/A `pytest-failed-to-verify `_ A pytest plugin that helps better distinguishing real test failures from setup flakiness. Aug 08, 2019 5 - Production/Stable pytest (>=4.1.0) `pytest-faker `_ Faker integration with the pytest framework. Dec 19, 2016 6 - Mature N/A `pytest-falcon `_ Pytest helpers for Falcon. Sep 07, 2016 4 - Beta N/A @@ -321,11 +323,11 @@ name `pytest-girder `_ A set of pytest fixtures for testing Girder applications. Feb 25, 2021 N/A N/A `pytest-git `_ Git repository fixture for py.test May 28, 2019 5 - Production/Stable pytest `pytest-gitcov `_ Pytest plugin for reporting on coverage of the last git commit. Jan 11, 2020 2 - Pre-Alpha N/A -`pytest-git-fixtures `_ Pytest fixtures for testing with git. Jan 25, 2021 4 - Beta pytest +`pytest-git-fixtures `_ Pytest fixtures for testing with git. Mar 04, 2021 4 - Beta pytest `pytest-github `_ Plugin for py.test that associates tests with github issues using a marker. Mar 07, 2019 5 - Production/Stable N/A `pytest-github-actions-annotate-failures `_ pytest plugin to annotate failed tests with a workflow command for GitHub Actions Oct 13, 2020 N/A pytest (>=4.0.0) `pytest-gitignore `_ py.test plugin to ignore the same files as git Jul 17, 2015 4 - Beta N/A -`pytest-gnupg-fixtures `_ Pytest fixtures for testing with gnupg. Feb 22, 2021 4 - Beta pytest +`pytest-gnupg-fixtures `_ Pytest fixtures for testing with gnupg. Mar 04, 2021 4 - Beta pytest `pytest-golden `_ Plugin for pytest that offloads expected outputs to data files Nov 23, 2020 N/A pytest (>=6.1.2,<7.0.0) `pytest-graphql-schema `_ Get graphql schema as fixture for pytest Oct 18, 2019 N/A N/A `pytest-greendots `_ Green progress dots Feb 08, 2014 3 - Alpha N/A @@ -342,8 +344,9 @@ name `pytest-historic `_ Custom report to display pytest historical execution records Apr 08, 2020 N/A pytest `pytest-historic-hook `_ Custom listener to store execution results into MYSQL DB, which is used for pytest-historic report Apr 08, 2020 N/A pytest `pytest-homeassistant `_ A pytest plugin for use with homeassistant custom components. Aug 12, 2020 4 - Beta N/A -`pytest-homeassistant-custom-component `_ Experimental package to automatically extract test plugins for Home Assistant custom components Feb 10, 2021 3 - Alpha pytest (==6.2.2) +`pytest-homeassistant-custom-component `_ Experimental package to automatically extract test plugins for Home Assistant custom components Mar 03, 2021 3 - Alpha pytest (==6.2.2) `pytest-honors `_ Report on tests that honor constraints, and guard against regressions Mar 06, 2020 4 - Beta N/A +`pytest-hoverfly `_ Simplify working with Hoverfly from pytest Mar 04, 2021 N/A pytest (>=5.0) `pytest-hoverfly-wrapper `_ Integrates the Hoverfly HTTP proxy into Pytest Jan 31, 2021 4 - Beta N/A `pytest-html `_ pytest plugin for generating HTML reports Dec 13, 2020 5 - Production/Stable pytest (!=6.0.0,>=5.0) `pytest-html-lee `_ optimized pytest plugin for generating HTML reports Jun 30, 2020 5 - Production/Stable pytest (>=5.0) @@ -355,7 +358,7 @@ name `pytest-http-mocker `_ Pytest plugin for http mocking (via https://github.com/vilus/mocker) Oct 20, 2019 N/A N/A `pytest-httpretty `_ A thin wrapper of HTTPretty for pytest Feb 16, 2014 3 - Alpha N/A `pytest-httpserver `_ pytest-httpserver is a httpserver for pytest Feb 14, 2021 3 - Alpha pytest ; extra == 'dev' -`pytest-httpx `_ Send responses to httpx. Nov 25, 2020 5 - Production/Stable pytest (==6.*) +`pytest-httpx `_ Send responses to httpx. Mar 01, 2021 5 - Production/Stable pytest (==6.*) `pytest-hue `_ Visualise PyTest status via your Phillips Hue lights May 09, 2019 N/A N/A `pytest-hypo-25 `_ help hypo module for pytest Jan 12, 2020 3 - Alpha N/A `pytest-ibutsu `_ A plugin to sent pytest results to an Ibutsu server Feb 24, 2021 4 - Beta pytest @@ -371,7 +374,7 @@ name `pytest-inmanta `_ A py.test plugin providing fixtures to simplify inmanta modules testing. Oct 12, 2020 5 - Production/Stable N/A `pytest-inmanta-extensions `_ Inmanta tests package Jan 07, 2021 5 - Production/Stable N/A `pytest-Inomaly `_ A simple image diff plugin for pytest Feb 13, 2018 4 - Beta N/A -`pytest-insta `_ A practical snapshot testing plugin for pytest Feb 25, 2021 N/A pytest (>=6.0.2,<7.0.0) +`pytest-insta `_ A practical snapshot testing plugin for pytest Mar 03, 2021 N/A pytest (>=6.0.2,<7.0.0) `pytest-instafail `_ pytest plugin to show failures instantly Jun 14, 2020 4 - Beta pytest (>=2.9) `pytest-instrument `_ pytest plugin to instrument tests Apr 05, 2020 5 - Production/Stable pytest (>=5.1.0) `pytest-integration `_ Organizing pytests by integration or not Apr 16, 2020 N/A N/A @@ -642,29 +645,29 @@ name `pytest-roast `_ pytest plugin for ROAST configuration override and fixtures Feb 05, 2021 5 - Production/Stable pytest (<6) `pytest-rotest `_ Pytest integration with rotest Sep 08, 2019 N/A pytest (>=3.5.0) `pytest-rpc `_ Extend py.test for RPC OpenStack testing. Feb 22, 2019 4 - Beta pytest (~=3.6) -`pytest-rt `_ pytest data collector plugin for Testgr Jan 24, 2021 N/A N/A -`pytest-rts `_ Coverage-based regression test selection (RTS) plugin for pytest Feb 15, 2021 N/A pytest +`pytest-rt `_ pytest data collector plugin for Testgr Mar 03, 2021 N/A N/A +`pytest-rts `_ Coverage-based regression test selection (RTS) plugin for pytest Mar 03, 2021 N/A pytest `pytest-runfailed `_ implement a --failed option for pytest Mar 24, 2016 N/A N/A `pytest-runner `_ Invoke py.test as distutils command with dependency resolution Feb 12, 2021 5 - Production/Stable pytest (!=3.7.3,>=3.5) ; extra == 'testing' `pytest-salt `_ Pytest Salt Plugin Jan 27, 2020 4 - Beta N/A `pytest-salt-containers `_ A Pytest plugin that builds and creates docker containers Nov 09, 2016 4 - Beta N/A -`pytest-salt-factories `_ Pytest Salt Plugin Feb 19, 2021 4 - Beta pytest (>=6.1.1) +`pytest-salt-factories `_ Pytest Salt Plugin Mar 05, 2021 4 - Beta pytest (>=6.1.1) `pytest-salt-from-filenames `_ Simple PyTest Plugin For Salt's Test Suite Specifically Jan 29, 2019 4 - Beta pytest (>=4.1) `pytest-salt-runtests-bridge `_ Simple PyTest Plugin For Salt's Test Suite Specifically Dec 05, 2019 4 - Beta pytest (>=4.1) `pytest-sanic `_ a pytest plugin for Sanic Feb 27, 2021 N/A pytest (>=5.2) `pytest-sanity `_ Dec 07, 2020 N/A N/A `pytest-sa-pg `_ May 14, 2019 N/A N/A -`pytest-sbase `_ A complete web automation framework for end-to-end testing. Feb 27, 2021 5 - Production/Stable N/A +`pytest-sbase `_ A complete web automation framework for end-to-end testing. Mar 06, 2021 5 - Production/Stable N/A `pytest-scenario `_ pytest plugin for test scenarios Feb 06, 2017 3 - Alpha N/A `pytest-schema `_ 👍 Validate return values against a schema-like object in testing Aug 31, 2020 5 - Production/Stable pytest (>=3.5.0) `pytest-securestore `_ An encrypted password store for use within pytest cases Jun 19, 2019 4 - Beta N/A `pytest-select `_ A pytest plugin which allows to (de-)select tests from a file. Jan 18, 2019 3 - Alpha pytest (>=3.0) `pytest-selenium `_ pytest plugin for Selenium Sep 19, 2020 5 - Production/Stable pytest (>=5.0.0) -`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Feb 27, 2021 5 - Production/Stable N/A +`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Mar 06, 2021 5 - Production/Stable N/A `pytest-selenium-enhancer `_ pytest plugin for Selenium Nov 26, 2020 5 - Production/Stable N/A `pytest-selenium-pdiff `_ A pytest package implementing perceptualdiff for Selenium tests. Apr 06, 2017 2 - Pre-Alpha N/A `pytest-send-email `_ Send pytest execution result email Dec 04, 2019 N/A N/A -`pytest-sentry `_ A pytest plugin to send testrun information to Sentry.io Dec 16, 2020 N/A N/A +`pytest-sentry `_ A pytest plugin to send testrun information to Sentry.io Mar 02, 2021 N/A N/A `pytest-server-fixtures `_ Extensible server fixures for py.test May 28, 2019 5 - Production/Stable pytest `pytest-serverless `_ Automatically mocks resources from serverless.yml in pytest using moto. Feb 20, 2021 4 - Beta N/A `pytest-services `_ Services plugin for pytest testing framework Oct 30, 2020 6 - Mature N/A @@ -726,7 +729,7 @@ name `pytest-stubprocess `_ Provide stub implementations for subprocesses in Python tests Sep 17, 2018 3 - Alpha pytest (>=3.5.0) `pytest-study `_ A pytest plugin to organize long run tests (named studies) without interfering the regular tests Sep 26, 2017 3 - Alpha pytest (>=2.0) `pytest-subprocess `_ A plugin to fake subprocess for pytest Aug 22, 2020 5 - Production/Stable pytest (>=4.0.0) -`pytest-subtesthack `_ A hack to explicitly set up and tear down fixtures. Jan 31, 2016 N/A N/A +`pytest-subtesthack `_ A hack to explicitly set up and tear down fixtures. Mar 02, 2021 N/A N/A `pytest-subtests `_ unittest subTest() support and subtests fixture Dec 13, 2020 4 - Beta pytest (>=5.3.0) `pytest-subunit `_ pytest-subunit is a plugin for py.test which outputs testsresult in subunit format. Aug 29, 2017 N/A N/A `pytest-sugar `_ pytest-sugar is a plugin for pytest that changes the default look and feel of pytest (e.g. progressbar, show tests that fail instantly). Jul 06, 2020 3 - Alpha N/A @@ -827,10 +830,10 @@ name `pytest-xfiles `_ Pytest fixtures providing data read from function, module or package related (x)files. Feb 27, 2018 N/A N/A `pytest-xlog `_ Extended logging for test and decorators May 31, 2020 4 - Beta N/A `pytest-xpara `_ An extended parametrizing plugin of pytest. Oct 30, 2017 3 - Alpha pytest -`pytest-xprocess `_ A pytest plugin for managing processes across test runs. Nov 26, 2020 4 - Beta pytest (>=2.8) +`pytest-xprocess `_ A pytest plugin for managing processes across test runs. Mar 02, 2021 4 - Beta pytest (>=2.8) `pytest-xray `_ May 30, 2019 3 - Alpha N/A `pytest-xrayjira `_ Mar 17, 2020 3 - Alpha pytest (==4.3.1) -`pytest-xray-server `_ Nov 29, 2020 3 - Alpha N/A +`pytest-xray-server `_ Mar 03, 2021 3 - Alpha N/A `pytest-xvfb `_ A pytest plugin to run Xvfb for tests. Jun 09, 2020 4 - Beta pytest (>=2.8.1) `pytest-yaml `_ This plugin is used to load yaml output to your test using pytest framework. Oct 05, 2018 N/A pytest `pytest-yamltree `_ Create or check file/directory trees described by YAML Mar 02, 2020 4 - Beta pytest (>=3.1.1) From 412fc001a0770d44b281d9b39f736df3cdc80c37 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Sun, 7 Mar 2021 14:57:19 +0100 Subject: [PATCH 162/630] fix bug in test for issue 519 assert the actual outcome and fix the filename --- changelog/8411.trivial.rst | 1 + testing/example_scripts/issue_519.py | 4 ++-- testing/examples/test_issue519.py | 5 +++-- 3 files changed, 6 insertions(+), 4 deletions(-) create mode 100644 changelog/8411.trivial.rst diff --git a/changelog/8411.trivial.rst b/changelog/8411.trivial.rst new file mode 100644 index 00000000000..8f169a900fa --- /dev/null +++ b/changelog/8411.trivial.rst @@ -0,0 +1 @@ +Assert the outcomes for the issue 518 test and fix the test. diff --git a/testing/example_scripts/issue_519.py b/testing/example_scripts/issue_519.py index 3928294886f..e44367fca04 100644 --- a/testing/example_scripts/issue_519.py +++ b/testing/example_scripts/issue_519.py @@ -20,12 +20,12 @@ def checked_order(): yield order pprint.pprint(order) assert order == [ - ("testing/example_scripts/issue_519.py", "fix1", "arg1v1"), + ("issue_519.py", "fix1", "arg1v1"), ("test_one[arg1v1-arg2v1]", "fix2", "arg2v1"), ("test_two[arg1v1-arg2v1]", "fix2", "arg2v1"), ("test_one[arg1v1-arg2v2]", "fix2", "arg2v2"), ("test_two[arg1v1-arg2v2]", "fix2", "arg2v2"), - ("testing/example_scripts/issue_519.py", "fix1", "arg1v2"), + ("issue_519.py", "fix1", "arg1v2"), ("test_one[arg1v2-arg2v1]", "fix2", "arg2v1"), ("test_two[arg1v2-arg2v1]", "fix2", "arg2v1"), ("test_one[arg1v2-arg2v2]", "fix2", "arg2v2"), diff --git a/testing/examples/test_issue519.py b/testing/examples/test_issue519.py index 85ba545e671..7b9c109889e 100644 --- a/testing/examples/test_issue519.py +++ b/testing/examples/test_issue519.py @@ -1,6 +1,7 @@ from _pytest.pytester import Pytester -def test_510(pytester: Pytester) -> None: +def test_519(pytester: Pytester) -> None: pytester.copy_example("issue_519.py") - pytester.runpytest("issue_519.py") + res = pytester.runpytest("issue_519.py") + res.assert_outcomes(passed=8) From 125633728f4215c59a5e0da2985f888a2a945325 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 8 Mar 2021 16:54:31 +0000 Subject: [PATCH 163/630] [pre-commit.ci] pre-commit autoupdate --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 24196151952..83a50be93c4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,7 +5,7 @@ repos: - id: black args: [--safe, --quiet] - repo: https://github.com/asottile/blacken-docs - rev: v1.9.2 + rev: v1.10.0 hooks: - id: blacken-docs additional_dependencies: [black==20.8b1] From ddde3266c6ec0b198e9e2ae68bd1692baf329071 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 8 Mar 2021 16:55:53 +0000 Subject: [PATCH 164/630] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- testing/test_doctest.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/testing/test_doctest.py b/testing/test_doctest.py index b63665349a1..1d33e737830 100644 --- a/testing/test_doctest.py +++ b/testing/test_doctest.py @@ -730,12 +730,11 @@ def test_unicode_doctest(self, pytester: Pytester): test_unicode_doctest=""" .. doctest:: - >>> print( - ... "Hi\\n\\nByé") + >>> print("Hi\\n\\nByé") Hi ... Byé - >>> 1/0 # Byé + >>> 1 / 0 # Byé 1 """ ) From 7a6ec5616d071e3b21f0ce1c07c66388b3cce81a Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Mon, 8 Mar 2021 19:12:08 -0800 Subject: [PATCH 165/630] clean up mkdtemp usage Committed via https://github.com/asottile/all-repos --- doc/en/fixture.rst | 12 +++++------- testing/test_assertrewrite.py | 6 ++---- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/doc/en/fixture.rst b/doc/en/fixture.rst index 028786f6523..50bc9ee8e34 100644 --- a/doc/en/fixture.rst +++ b/doc/en/fixture.rst @@ -2228,7 +2228,6 @@ file: # content of conftest.py import os - import shutil import tempfile import pytest @@ -2236,12 +2235,11 @@ file: @pytest.fixture def cleandir(): - old_cwd = os.getcwd() - newpath = tempfile.mkdtemp() - os.chdir(newpath) - yield - os.chdir(old_cwd) - shutil.rmtree(newpath) + with tempfile.TemporaryDirectory() as newpath: + old_cwd = os.getcwd() + os.chdir(newpath) + yield + os.chdir(old_cwd) and declare its use in a test module via a ``usefixtures`` marker: diff --git a/testing/test_assertrewrite.py b/testing/test_assertrewrite.py index cba03406e86..f7d9d62ef63 100644 --- a/testing/test_assertrewrite.py +++ b/testing/test_assertrewrite.py @@ -1395,12 +1395,10 @@ def test_cwd_changed(self, pytester: Pytester, monkeypatch) -> None: **{ "test_setup_nonexisting_cwd.py": """\ import os - import shutil import tempfile - d = tempfile.mkdtemp() - os.chdir(d) - shutil.rmtree(d) + with tempfile.TemporaryDirectory() as d: + os.chdir(d) """, "test_test.py": """\ def test(): From dbed1ff68fce457c8dd94e2926dd296b0c1c5ca0 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Tue, 9 Mar 2021 21:56:16 +0100 Subject: [PATCH 166/630] adopt main terminology in the configs ref pytest-dev/meta#8 --- .github/workflows/main.yml | 8 ++++---- CONTRIBUTING.rst | 4 ++-- README.rst | 10 +++++----- doc/en/development_guide.rst | 2 +- doc/en/funcarg_compare.rst | 2 +- doc/en/index.rst | 4 ++-- doc/en/license.rst | 2 +- testing/test_config.py | 2 +- tox.ini | 4 ++-- 9 files changed, 19 insertions(+), 19 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a3ea24b7cb0..7872f978ae1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -3,7 +3,7 @@ name: main on: push: branches: - - master + - main - "[0-9]+.[0-9]+.x" tags: - "[0-9]+.[0-9]+.[0-9]+" @@ -11,7 +11,7 @@ on: pull_request: branches: - - master + - main - "[0-9]+.[0-9]+.x" jobs: @@ -56,7 +56,7 @@ jobs: - name: "windows-py37-pluggy" python: "3.7" os: windows-latest - tox_env: "py37-pluggymaster-xdist" + tox_env: "py37-pluggymain-xdist" - name: "windows-py38" python: "3.8" os: windows-latest @@ -75,7 +75,7 @@ jobs: - name: "ubuntu-py37-pluggy" python: "3.7" os: ubuntu-latest - tox_env: "py37-pluggymaster-xdist" + tox_env: "py37-pluggymain-xdist" - name: "ubuntu-py37-freeze" python: "3.7" os: ubuntu-latest diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 054f809a818..ba783d5c106 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -236,7 +236,7 @@ Here is a simple overview, with pytest-specific bits: $ cd pytest # now, create your own branch off "master": - $ git checkout -b your-bugfix-branch-name master + $ git checkout -b your-bugfix-branch-name main Given we have "major.minor.micro" version numbers, bug fixes will usually be released in micro releases whereas features will be released in @@ -318,7 +318,7 @@ Here is a simple overview, with pytest-specific bits: compare: your-branch-name base-fork: pytest-dev/pytest - base: master + base: main Writing Tests diff --git a/README.rst b/README.rst index 159bf1d4f75..d0014e8bfff 100644 --- a/README.rst +++ b/README.rst @@ -1,4 +1,4 @@ -.. image:: https://github.com/pytest-dev/pytest/raw/master/doc/en/img/pytest_logo_curves.svg +.. image:: https://github.com/pytest-dev/pytest/raw/main/doc/en/img/pytest_logo_curves.svg :target: https://docs.pytest.org/en/stable/ :align: center :height: 200 @@ -16,14 +16,14 @@ .. image:: https://img.shields.io/pypi/pyversions/pytest.svg :target: https://pypi.org/project/pytest/ -.. image:: https://codecov.io/gh/pytest-dev/pytest/branch/master/graph/badge.svg +.. image:: https://codecov.io/gh/pytest-dev/pytest/branch/main/graph/badge.svg :target: https://codecov.io/gh/pytest-dev/pytest :alt: Code coverage Status .. image:: https://github.com/pytest-dev/pytest/workflows/main/badge.svg :target: https://github.com/pytest-dev/pytest/actions?query=workflow%3Amain -.. image:: https://results.pre-commit.ci/badge/github/pytest-dev/pytest/master.svg +.. image:: https://results.pre-commit.ci/badge/github/pytest-dev/pytest/main.svg :target: https://results.pre-commit.ci/latest/github/pytest-dev/pytest/master :alt: pre-commit.ci status @@ -151,8 +151,8 @@ Tidelift will coordinate the fix and disclosure. License ------- -Copyright Holger Krekel and others, 2004-2020. +Copyright Holger Krekel and others, 2004-2021. Distributed under the terms of the `MIT`_ license, pytest is free and open source software. -.. _`MIT`: https://github.com/pytest-dev/pytest/blob/master/LICENSE +.. _`MIT`: https://github.com/pytest-dev/pytest/blob/main/LICENSE diff --git a/doc/en/development_guide.rst b/doc/en/development_guide.rst index 77076d4834e..3ee0ebbc239 100644 --- a/doc/en/development_guide.rst +++ b/doc/en/development_guide.rst @@ -4,4 +4,4 @@ Development Guide The contributing guidelines are to be found :ref:`here `. The release procedure for pytest is documented on -`GitHub `_. +`GitHub `_. diff --git a/doc/en/funcarg_compare.rst b/doc/en/funcarg_compare.rst index 5e2a050063c..0f44d1da05c 100644 --- a/doc/en/funcarg_compare.rst +++ b/doc/en/funcarg_compare.rst @@ -168,7 +168,7 @@ pytest for a long time offered a pytest_configure and a pytest_sessionstart hook which are often used to setup global resources. This suffers from several problems: -1. in distributed testing the master process would setup test resources +1. in distributed testing the managing process would setup test resources that are never needed because it only co-ordinates the test run activities of the worker processes. diff --git a/doc/en/index.rst b/doc/en/index.rst index 7f74534027c..cfe3a271ede 100644 --- a/doc/en/index.rst +++ b/doc/en/index.rst @@ -129,8 +129,8 @@ Tidelift will coordinate the fix and disclosure. License ------- -Copyright Holger Krekel and others, 2004-2020. +Copyright Holger Krekel and others, 2004-2021. Distributed under the terms of the `MIT`_ license, pytest is free and open source software. -.. _`MIT`: https://github.com/pytest-dev/pytest/blob/master/LICENSE +.. _`MIT`: https://github.com/pytest-dev/pytest/blob/main/LICENSE diff --git a/doc/en/license.rst b/doc/en/license.rst index c6c10bbf358..13765be1595 100644 --- a/doc/en/license.rst +++ b/doc/en/license.rst @@ -29,4 +29,4 @@ Distributed under the terms of the `MIT`_ license, pytest is free and open sourc OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -.. _`MIT`: https://github.com/pytest-dev/pytest/blob/master/LICENSE +.. _`MIT`: https://github.com/pytest-dev/pytest/blob/main/LICENSE diff --git a/testing/test_config.py b/testing/test_config.py index 8c1441e0680..b23264e1d79 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -1780,7 +1780,7 @@ class DummyPlugin: ) def test_config_blocked_default_plugins(pytester: Pytester, plugin: str) -> None: if plugin == "debugging": - # Fixed in xdist master (after 1.27.0). + # Fixed in xdist (after 1.27.0). # https://github.com/pytest-dev/pytest-xdist/pull/422 try: import xdist # noqa: F401 diff --git a/tox.ini b/tox.ini index 908f56ea681..da5a634de8b 100644 --- a/tox.ini +++ b/tox.ini @@ -9,7 +9,7 @@ envlist = py38 py39 pypy3 - py37-{pexpect,xdist,unittestextras,numpy,pluggymaster} + py37-{pexpect,xdist,unittestextras,numpy,pluggymain} doctesting plugins py37-freeze @@ -45,7 +45,7 @@ deps = doctesting: PyYAML numpy: numpy>=1.19.4 pexpect: pexpect>=4.8.0 - pluggymaster: git+https://github.com/pytest-dev/pluggy.git@master + pluggymain: pluggy @ git+https://github.com/pytest-dev/pluggy.git pygments>=2.7.2 unittestextras: twisted unittestextras: asynctest From 4a19533ba5f65f839b6bd0fe307a053049230796 Mon Sep 17 00:00:00 2001 From: Daniele Procida Date: Wed, 10 Mar 2021 18:27:30 +0000 Subject: [PATCH 167/630] Renamed Install to Getting started; moved notes to index --- doc/en/_templates/globaltoc.html | 2 +- doc/en/getting-started.rst | 18 +++++------------- doc/en/index.rst | 17 +++++++++++++---- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/doc/en/_templates/globaltoc.html b/doc/en/_templates/globaltoc.html index 5fc1ea13e5b..25666d8db78 100644 --- a/doc/en/_templates/globaltoc.html +++ b/doc/en/_templates/globaltoc.html @@ -2,7 +2,7 @@

    {{ _('Table Of Contents') }}

    • Home
    • -
    • Install
    • +
    • Get started
    • Contents
    • API Reference
    • Examples
    • diff --git a/doc/en/getting-started.rst b/doc/en/getting-started.rst index 28fd862cf3b..fad6825545c 100644 --- a/doc/en/getting-started.rst +++ b/doc/en/getting-started.rst @@ -1,22 +1,14 @@ -Installation and Getting Started +Get Started =================================== -**Pythons**: Python 3.6, 3.7, 3.8, 3.9, PyPy3 - -**Platforms**: Linux and Windows - -**PyPI package name**: `pytest `_ - -**Documentation as PDF**: `download latest `_ - -``pytest`` is a framework that makes building simple and scalable tests easy. Tests are expressive and readable—no boilerplate code required. Get started in minutes with a small unit test or complex functional test for your application or library. - .. _`getstarted`: .. _`installation`: Install ``pytest`` ---------------------------------------- +``pytest`` requires: Python 3.6, 3.7, 3.8, 3.9, or PyPy3. + 1. Run the following command in your command line: .. code-block:: bash @@ -35,7 +27,7 @@ Install ``pytest`` Create your first test ---------------------------------------------------------- -Create a simple test function with just four lines of code: +Create a new file called ``test_sample.py``, containing a function, and a test: .. code-block:: python @@ -47,7 +39,7 @@ Create a simple test function with just four lines of code: def test_answer(): assert func(3) == 5 -That’s it. You can now execute the test function: +The test .. code-block:: pytest diff --git a/doc/en/index.rst b/doc/en/index.rst index cfe3a271ede..5060d9720ce 100644 --- a/doc/en/index.rst +++ b/doc/en/index.rst @@ -13,10 +13,19 @@ pytest: helps you write better programs .. module:: pytest -The ``pytest`` framework makes it easy to write small tests, yet -scales to support complex functional testing for applications and libraries. +The ``pytest`` framework makes it easy to write small, readable tests, and can +scale to support complex functional testing for applications and libraries. -An example of a simple test: + +**Pythons**: ``pytest`` requires: Python 3.6, 3.7, 3.8, 3.9, or PyPy3. + +**PyPI package name**: `pytest `_ + +**Documentation as PDF**: `download latest `_ + + +A quick example +--------------- .. code-block:: python @@ -56,7 +65,7 @@ To execute it: ============================ 1 failed in 0.12s ============================= Due to ``pytest``'s detailed assertion introspection, only plain ``assert`` statements are used. -See :ref:`Getting Started ` for more examples. +See :ref:`Get started ` for a basic introduction to using pytest. Features From d2d975d6cf916c3f0802341e202869157e82022a Mon Sep 17 00:00:00 2001 From: Daniele Procida Date: Wed, 10 Mar 2021 19:18:21 +0000 Subject: [PATCH 168/630] Reworded titles of some clear how-to guides Files retitled: assert.rst bash-completion.rst capture.rst existingtestsuite.rst mark.rst monkeypatch.rst nose.rst parametrize.rst plugins.rst skipping.rst tmpdir.rst usage.rst --- doc/en/assert.rst | 2 +- doc/en/bash-completion.rst | 4 ++-- doc/en/capture.rst | 2 +- doc/en/existingtestsuite.rst | 4 ++-- doc/en/mark.rst | 4 ++-- doc/en/monkeypatch.rst | 2 +- doc/en/nose.rst | 2 +- doc/en/parametrize.rst | 2 +- doc/en/plugins.rst | 4 ++-- doc/en/skipping.rst | 4 ++-- doc/en/tmpdir.rst | 4 ++-- doc/en/usage.rst | 2 +- 12 files changed, 18 insertions(+), 18 deletions(-) diff --git a/doc/en/assert.rst b/doc/en/assert.rst index e6a23bcf3ac..c0314f34404 100644 --- a/doc/en/assert.rst +++ b/doc/en/assert.rst @@ -1,5 +1,5 @@ -The writing and reporting of assertions in tests +How to write and report assertions in tests ================================================== .. _`assertfeedback`: diff --git a/doc/en/bash-completion.rst b/doc/en/bash-completion.rst index eaa07881414..245dfd6d9a8 100644 --- a/doc/en/bash-completion.rst +++ b/doc/en/bash-completion.rst @@ -1,8 +1,8 @@ .. _bash_completion: -Setting up bash completion -========================== +How to set up bash completion +============================= When using bash as your shell, ``pytest`` can use argcomplete (https://argcomplete.readthedocs.io/) for auto-completion. diff --git a/doc/en/capture.rst b/doc/en/capture.rst index caaebdf81a8..ea0063853ec 100644 --- a/doc/en/capture.rst +++ b/doc/en/capture.rst @@ -1,7 +1,7 @@ .. _`captures`: -Capturing of the stdout/stderr output +How to capture stdout/stderr output ========================================================= Default stdout/stderr/stdin capturing behaviour diff --git a/doc/en/existingtestsuite.rst b/doc/en/existingtestsuite.rst index 1e3e192bf3f..9909e7d113a 100644 --- a/doc/en/existingtestsuite.rst +++ b/doc/en/existingtestsuite.rst @@ -1,7 +1,7 @@ .. _existingtestsuite: -Using pytest with an existing test suite -=========================================== +How to use pytest with an existing test suite +============================================== Pytest can be used with most existing test suites, but its behavior differs from other test runners such as :ref:`nose ` or diff --git a/doc/en/mark.rst b/doc/en/mark.rst index 7370342a965..33f9d18bfe3 100644 --- a/doc/en/mark.rst +++ b/doc/en/mark.rst @@ -1,7 +1,7 @@ .. _mark: -Marking test functions with attributes -====================================== +How to mark test functions with attributes +=========================================== By using the ``pytest.mark`` helper you can easily set metadata on your test functions. You can find the full list of builtin markers diff --git a/doc/en/monkeypatch.rst b/doc/en/monkeypatch.rst index 4964ee54815..d3c7689e430 100644 --- a/doc/en/monkeypatch.rst +++ b/doc/en/monkeypatch.rst @@ -1,5 +1,5 @@ -Monkeypatching/mocking modules and environments +How to monkeypatch/mock modules and environments ================================================================ .. currentmodule:: _pytest.monkeypatch diff --git a/doc/en/nose.rst b/doc/en/nose.rst index e16d764692c..486b3db75be 100644 --- a/doc/en/nose.rst +++ b/doc/en/nose.rst @@ -1,6 +1,6 @@ .. _`noseintegration`: -Running tests written for nose +How to run tests written for nose ======================================= ``pytest`` has basic support for running tests written for nose_. diff --git a/doc/en/parametrize.rst b/doc/en/parametrize.rst index 9e531ddd45d..7f5f6ea0cd8 100644 --- a/doc/en/parametrize.rst +++ b/doc/en/parametrize.rst @@ -6,7 +6,7 @@ .. _`parametrize-basics`: -Parametrizing fixtures and test functions +How to parametrize fixtures and test functions ========================================================================== pytest enables test parametrization at several levels: diff --git a/doc/en/plugins.rst b/doc/en/plugins.rst index 8090e7d18a4..ade51abad84 100644 --- a/doc/en/plugins.rst +++ b/doc/en/plugins.rst @@ -2,8 +2,8 @@ .. _`extplugins`: .. _`using plugins`: -Installing and Using plugins -============================ +How to install and use plugins +=============================== This section talks about installing and using third party plugins. For writing your own plugins, please refer to :ref:`writing-plugins`. diff --git a/doc/en/skipping.rst b/doc/en/skipping.rst index 610d3d43bca..66db74a1fa4 100644 --- a/doc/en/skipping.rst +++ b/doc/en/skipping.rst @@ -2,8 +2,8 @@ .. _skipping: -Skip and xfail: dealing with tests that cannot succeed -====================================================== +How to use skip and xfail to deal with tests that cannot succeed +================================================================= You can mark test functions that cannot be run on certain platforms or that you expect to fail so pytest can deal with them accordingly and diff --git a/doc/en/tmpdir.rst b/doc/en/tmpdir.rst index adcba02cb15..379c6d84c8e 100644 --- a/doc/en/tmpdir.rst +++ b/doc/en/tmpdir.rst @@ -2,8 +2,8 @@ .. _`tmpdir handling`: .. _tmpdir: -Temporary directories and files -================================================ +How to use temporary directories and files in tests +=================================================== The ``tmp_path`` fixture ------------------------ diff --git a/doc/en/usage.rst b/doc/en/usage.rst index 0a26182d451..32af9fbeba3 100644 --- a/doc/en/usage.rst +++ b/doc/en/usage.rst @@ -1,7 +1,7 @@ .. _usage: -Usage and Invocations +How to invoke pytest ========================================== From a362200d479ef4876f198d01f8def9aca38bd09a Mon Sep 17 00:00:00 2001 From: Daniele Procida Date: Thu, 11 Mar 2021 17:22:54 +0000 Subject: [PATCH 169/630] Moved documentation topics to group them by theme. Reordered items in: * _templates/globaltoc.html * contents.rst --- doc/en/_templates/globaltoc.html | 4 ++-- doc/en/contents.rst | 41 ++++++++++++++++++++++++++------ 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/doc/en/_templates/globaltoc.html b/doc/en/_templates/globaltoc.html index 25666d8db78..4069e59ebd7 100644 --- a/doc/en/_templates/globaltoc.html +++ b/doc/en/_templates/globaltoc.html @@ -3,11 +3,11 @@

      {{ _('Table Of Contents') }}

      • Home
      • Get started
      • -
      • Contents
      • -
      • API Reference
      • Examples
      • Customize
      • +
      • API Reference
      • 3rd party plugins
      • +
      • Complete table of contents
      • Changelog
      • Contributing
      • Backwards Compatibility
      • diff --git a/doc/en/contents.rst b/doc/en/contents.rst index a439f1eda96..1bcc7f12b2d 100644 --- a/doc/en/contents.rst +++ b/doc/en/contents.rst @@ -7,38 +7,65 @@ Full pytest documentation .. `Download latest version as EPUB `_ + +Start here +----------- + .. toctree:: :maxdepth: 2 getting-started + + +How-to guides +------------- + +.. toctree:: + :maxdepth: 2 + usage existingtestsuite assert - fixture mark monkeypatch tmpdir capture - warnings - doctest skipping parametrize + plugins + nose + bash-completion + + +Reference guides +----------------- + +.. toctree:: + :maxdepth: 2 + + fixture + warnings + doctest cache unittest - nose xunit_setup - plugins plugin_list writing_plugins logging + customize reference + +Further topics +----------------- + +.. toctree:: + :maxdepth: 2 + goodpractices flaky pythonpath - customize example/index - bash-completion backwards-compatibility deprecations From d8695410a4e91bbbedc0fa770bb299e74b725cbd Mon Sep 17 00:00:00 2001 From: Daniele Procida Date: Thu, 11 Mar 2021 20:30:47 +0000 Subject: [PATCH 170/630] Added how-to and reference directories. Moved various documents into subdirectories, how-to and reference. Updated multiple links to use `:ref:` instead of `:doc:`, meaning that files can henceforth be moved around without breaking references. --- changelog/5105.doc.rst | 2 +- doc/en/contents.rst | 46 +++++++++++----------- doc/en/example/index.rst | 6 +-- doc/en/getting-started.rst | 2 + doc/en/{ => how-to}/assert.rst | 0 doc/en/{ => how-to}/bash-completion.rst | 0 doc/en/{ => how-to}/capture.rst | 0 doc/en/{ => how-to}/existingtestsuite.rst | 0 doc/en/{ => how-to}/mark.rst | 0 doc/en/{ => how-to}/monkeypatch.rst | 1 + doc/en/{ => how-to}/nose.rst | 0 doc/en/{ => how-to}/parametrize.rst | 0 doc/en/{ => how-to}/plugins.rst | 2 +- doc/en/{ => how-to}/skipping.rst | 2 +- doc/en/{ => how-to}/tmpdir.rst | 0 doc/en/{ => how-to}/usage.rst | 0 doc/en/index.rst | 2 +- doc/en/{ => reference}/cache.rst | 0 doc/en/{ => reference}/customize.rst | 0 doc/en/{ => reference}/doctest.rst | 1 + doc/en/{ => reference}/fixture.rst | 32 +++++++-------- doc/en/{ => reference}/logging.rst | 0 doc/en/{ => reference}/plugin_list.rst | 2 + doc/en/{ => reference}/reference.rst | 36 ++++++++--------- doc/en/{ => reference}/unittest.rst | 0 doc/en/{ => reference}/warnings.rst | 0 doc/en/{ => reference}/writing_plugins.rst | 2 +- doc/en/{ => reference}/xunit_setup.rst | 0 28 files changed, 71 insertions(+), 65 deletions(-) rename doc/en/{ => how-to}/assert.rst (100%) rename doc/en/{ => how-to}/bash-completion.rst (100%) rename doc/en/{ => how-to}/capture.rst (100%) rename doc/en/{ => how-to}/existingtestsuite.rst (100%) rename doc/en/{ => how-to}/mark.rst (100%) rename doc/en/{ => how-to}/monkeypatch.rst (99%) rename doc/en/{ => how-to}/nose.rst (100%) rename doc/en/{ => how-to}/parametrize.rst (100%) rename doc/en/{ => how-to}/plugins.rst (99%) rename doc/en/{ => how-to}/skipping.rst (99%) rename doc/en/{ => how-to}/tmpdir.rst (100%) rename doc/en/{ => how-to}/usage.rst (100%) rename doc/en/{ => reference}/cache.rst (100%) rename doc/en/{ => reference}/customize.rst (100%) rename doc/en/{ => reference}/doctest.rst (99%) rename doc/en/{ => reference}/fixture.rst (98%) rename doc/en/{ => reference}/logging.rst (100%) rename doc/en/{ => reference}/plugin_list.rst (99%) rename doc/en/{ => reference}/reference.rst (98%) rename doc/en/{ => reference}/unittest.rst (100%) rename doc/en/{ => reference}/warnings.rst (100%) rename doc/en/{ => reference}/writing_plugins.rst (99%) rename doc/en/{ => reference}/xunit_setup.rst (100%) diff --git a/changelog/5105.doc.rst b/changelog/5105.doc.rst index f0cc8bab7b4..71c1edc9fc4 100644 --- a/changelog/5105.doc.rst +++ b/changelog/5105.doc.rst @@ -1 +1 @@ -Add automatically generated :doc:`plugin_list`. The list is updated on a periodic schedule. +Add automatically generated :ref:`plugin-list`. The list is updated on a periodic schedule. diff --git a/doc/en/contents.rst b/doc/en/contents.rst index 1bcc7f12b2d..84b87a35fb2 100644 --- a/doc/en/contents.rst +++ b/doc/en/contents.rst @@ -23,18 +23,18 @@ How-to guides .. toctree:: :maxdepth: 2 - usage - existingtestsuite - assert - mark - monkeypatch - tmpdir - capture - skipping - parametrize - plugins - nose - bash-completion + how-to/usage + how-to/existingtestsuite + how-to/assert + how-to/mark + how-to/monkeypatch + how-to/tmpdir + how-to/capture + how-to/skipping + how-to/parametrize + how-to/plugins + how-to/nose + how-to/bash-completion Reference guides @@ -43,17 +43,17 @@ Reference guides .. toctree:: :maxdepth: 2 - fixture - warnings - doctest - cache - unittest - xunit_setup - plugin_list - writing_plugins - logging - customize - reference + reference/fixture + reference/warnings + reference/doctest + reference/cache + reference/unittest + reference/xunit_setup + reference/plugin_list + reference/writing_plugins + reference/logging + reference/customize + reference/reference Further topics diff --git a/doc/en/example/index.rst b/doc/en/example/index.rst index 6876082d418..71e855534ff 100644 --- a/doc/en/example/index.rst +++ b/doc/en/example/index.rst @@ -13,12 +13,12 @@ answers. For basic examples, see -- :doc:`../getting-started` for basic introductory examples +- :ref:`get-started` for basic introductory examples - :ref:`assert` for basic assertion examples - :ref:`Fixtures ` for basic fixture/setup examples - :ref:`parametrize` for basic test function parametrization -- :doc:`../unittest` for basic unittest integration -- :doc:`../nose` for basic nosetests integration +- :ref:`unittest` for basic unittest integration +- :ref:`noseintegration` for basic nosetests integration The following examples aim at various use cases you might encounter. diff --git a/doc/en/getting-started.rst b/doc/en/getting-started.rst index fad6825545c..32ded553950 100644 --- a/doc/en/getting-started.rst +++ b/doc/en/getting-started.rst @@ -1,3 +1,5 @@ +.. _get-started: + Get Started =================================== diff --git a/doc/en/assert.rst b/doc/en/how-to/assert.rst similarity index 100% rename from doc/en/assert.rst rename to doc/en/how-to/assert.rst diff --git a/doc/en/bash-completion.rst b/doc/en/how-to/bash-completion.rst similarity index 100% rename from doc/en/bash-completion.rst rename to doc/en/how-to/bash-completion.rst diff --git a/doc/en/capture.rst b/doc/en/how-to/capture.rst similarity index 100% rename from doc/en/capture.rst rename to doc/en/how-to/capture.rst diff --git a/doc/en/existingtestsuite.rst b/doc/en/how-to/existingtestsuite.rst similarity index 100% rename from doc/en/existingtestsuite.rst rename to doc/en/how-to/existingtestsuite.rst diff --git a/doc/en/mark.rst b/doc/en/how-to/mark.rst similarity index 100% rename from doc/en/mark.rst rename to doc/en/how-to/mark.rst diff --git a/doc/en/monkeypatch.rst b/doc/en/how-to/monkeypatch.rst similarity index 99% rename from doc/en/monkeypatch.rst rename to doc/en/how-to/monkeypatch.rst index d3c7689e430..f34ba7982b4 100644 --- a/doc/en/monkeypatch.rst +++ b/doc/en/how-to/monkeypatch.rst @@ -1,3 +1,4 @@ +.. _monkeypatching: How to monkeypatch/mock modules and environments ================================================================ diff --git a/doc/en/nose.rst b/doc/en/how-to/nose.rst similarity index 100% rename from doc/en/nose.rst rename to doc/en/how-to/nose.rst diff --git a/doc/en/parametrize.rst b/doc/en/how-to/parametrize.rst similarity index 100% rename from doc/en/parametrize.rst rename to doc/en/how-to/parametrize.rst diff --git a/doc/en/plugins.rst b/doc/en/how-to/plugins.rst similarity index 99% rename from doc/en/plugins.rst rename to doc/en/how-to/plugins.rst index ade51abad84..60c442c1f42 100644 --- a/doc/en/plugins.rst +++ b/doc/en/how-to/plugins.rst @@ -58,7 +58,7 @@ Here is a little annotated list for some popular plugins: To see a complete list of all plugins with their latest testing status against different pytest and Python versions, please visit -:doc:`plugin_list`. +:ref:`plugin-list`. You may also discover more plugins through a `pytest- pypi.org search`_. diff --git a/doc/en/skipping.rst b/doc/en/how-to/skipping.rst similarity index 99% rename from doc/en/skipping.rst rename to doc/en/how-to/skipping.rst index 66db74a1fa4..2637326327d 100644 --- a/doc/en/skipping.rst +++ b/doc/en/how-to/skipping.rst @@ -365,7 +365,7 @@ Examples Here is a simple test file with the several usages: -.. literalinclude:: example/xfail_demo.py +.. literalinclude:: /example/xfail_demo.py Running it with the report-on-xfail option gives this output: diff --git a/doc/en/tmpdir.rst b/doc/en/how-to/tmpdir.rst similarity index 100% rename from doc/en/tmpdir.rst rename to doc/en/how-to/tmpdir.rst diff --git a/doc/en/usage.rst b/doc/en/how-to/usage.rst similarity index 100% rename from doc/en/usage.rst rename to doc/en/how-to/usage.rst diff --git a/doc/en/index.rst b/doc/en/index.rst index 5060d9720ce..102426d462a 100644 --- a/doc/en/index.rst +++ b/doc/en/index.rst @@ -81,7 +81,7 @@ Features - Python 3.6+ and PyPy 3 -- Rich plugin architecture, with over 800+ :doc:`external plugins ` and thriving community +- Rich plugin architecture, with over 800+ :ref:`external plugins ` and thriving community Documentation diff --git a/doc/en/cache.rst b/doc/en/reference/cache.rst similarity index 100% rename from doc/en/cache.rst rename to doc/en/reference/cache.rst diff --git a/doc/en/customize.rst b/doc/en/reference/customize.rst similarity index 100% rename from doc/en/customize.rst rename to doc/en/reference/customize.rst diff --git a/doc/en/doctest.rst b/doc/en/reference/doctest.rst similarity index 99% rename from doc/en/doctest.rst rename to doc/en/reference/doctest.rst index 486868bb806..b6ef1f5b835 100644 --- a/doc/en/doctest.rst +++ b/doc/en/reference/doctest.rst @@ -1,3 +1,4 @@ +.. _doctest: Doctest integration for modules and test files ========================================================= diff --git a/doc/en/fixture.rst b/doc/en/reference/fixture.rst similarity index 98% rename from doc/en/fixture.rst rename to doc/en/reference/fixture.rst index 50bc9ee8e34..0fc25118baa 100644 --- a/doc/en/fixture.rst +++ b/doc/en/reference/fixture.rst @@ -1188,12 +1188,12 @@ long as the test requesting them can see all fixtures involved. For example, here's a test file with a fixture (``outer``) that requests a fixture (``inner``) from a scope it wasn't defined in: -.. literalinclude:: example/fixtures/test_fixtures_request_different_scope.py +.. literalinclude:: /example/fixtures/test_fixtures_request_different_scope.py From the tests' perspectives, they have no problem seeing each of the fixtures they're dependent on: -.. image:: example/fixtures/test_fixtures_request_different_scope.svg +.. image:: /example/fixtures/test_fixtures_request_different_scope.svg :align: center So when they run, ``outer`` will have no problem finding ``inner``, because @@ -1270,7 +1270,7 @@ For example, given a test file structure like this: The boundaries of the scopes can be visualized like this: -.. image:: example/fixtures/fixture_availability.svg +.. image:: /example/fixtures/fixture_availability.svg :align: center The directories become their own sort of scope where fixtures that are defined @@ -1346,7 +1346,7 @@ If ``plugin_a`` is installed and provides the fixture ``a_fix``, and ``plugin_b`` is installed and provides the fixture ``b_fix``, then this is what the test's search for fixtures would look like: -.. image:: example/fixtures/fixture_availability_plugins.svg +.. image:: /example/fixtures/fixture_availability_plugins.svg :align: center pytest will only search for ``a_fix`` and ``b_fix`` in the plugins after @@ -1401,13 +1401,13 @@ Within a function request for fixtures, those of higher-scopes (such as Here's an example: -.. literalinclude:: example/fixtures/test_fixtures_order_scope.py +.. literalinclude:: /example/fixtures/test_fixtures_order_scope.py The test will pass because the larger scoped fixtures are executing first. The order breaks down to this: -.. image:: example/fixtures/test_fixtures_order_scope.svg +.. image:: /example/fixtures/test_fixtures_order_scope.svg :align: center Fixtures of the same order execute based on dependencies @@ -1421,17 +1421,17 @@ sure it is executed after ``b``. For example: -.. literalinclude:: example/fixtures/test_fixtures_order_dependencies.py +.. literalinclude:: /example/fixtures/test_fixtures_order_dependencies.py If we map out what depends on what, we get something that look like this: -.. image:: example/fixtures/test_fixtures_order_dependencies.svg +.. image:: /example/fixtures/test_fixtures_order_dependencies.svg :align: center The rules provided by each fixture (as to what fixture(s) each one has to come after) are comprehensive enough that it can be flattened to this: -.. image:: example/fixtures/test_fixtures_order_dependencies_flat.svg +.. image:: /example/fixtures/test_fixtures_order_dependencies_flat.svg :align: center Enough information has to be provided through these requests in order for pytest @@ -1442,7 +1442,7 @@ could go with any one of those interpretations at any point. For example, if ``d`` didn't request ``c``, i.e.the graph would look like this: -.. image:: example/fixtures/test_fixtures_order_dependencies_unclear.svg +.. image:: /example/fixtures/test_fixtures_order_dependencies_unclear.svg :align: center Because nothing requested ``c`` other than ``g``, and ``g`` also requests ``f``, @@ -1479,11 +1479,11 @@ non-autouse fixtures within that scope. So if the test file looked like this: -.. literalinclude:: example/fixtures/test_fixtures_order_autouse.py +.. literalinclude:: /example/fixtures/test_fixtures_order_autouse.py the graph would look like this: -.. image:: example/fixtures/test_fixtures_order_autouse.svg +.. image:: /example/fixtures/test_fixtures_order_autouse.svg :align: center Because ``c`` can now be put above ``d`` in the graph, pytest can once again @@ -1496,12 +1496,12 @@ Be careful with autouse, though, as an autouse fixture will automatically execute for every test that can reach it, even if they don't request it. For example, consider this file: -.. literalinclude:: example/fixtures/test_fixtures_order_autouse_multiple_scopes.py +.. literalinclude:: /example/fixtures/test_fixtures_order_autouse_multiple_scopes.py Even though nothing in ``TestClassWithC1Request`` is requesting ``c1``, it still is executed for the tests inside it anyway: -.. image:: example/fixtures/test_fixtures_order_autouse_multiple_scopes.svg +.. image:: /example/fixtures/test_fixtures_order_autouse_multiple_scopes.svg :align: center But just because one autouse fixture requested a non-autouse fixture, that @@ -1512,11 +1512,11 @@ fixture) can apply to. For example, take a look at this test file: -.. literalinclude:: example/fixtures/test_fixtures_order_autouse_temp_effects.py +.. literalinclude:: /example/fixtures/test_fixtures_order_autouse_temp_effects.py It would break down to something like this: -.. image:: example/fixtures/test_fixtures_order_autouse_temp_effects.svg +.. image:: /example/fixtures/test_fixtures_order_autouse_temp_effects.svg :align: center For ``test_req`` and ``test_no_req`` inside ``TestClassWithAutouse``, ``c3`` diff --git a/doc/en/logging.rst b/doc/en/reference/logging.rst similarity index 100% rename from doc/en/logging.rst rename to doc/en/reference/logging.rst diff --git a/doc/en/plugin_list.rst b/doc/en/reference/plugin_list.rst similarity index 99% rename from doc/en/plugin_list.rst rename to doc/en/reference/plugin_list.rst index 8426f469c94..b433d3f9a2d 100644 --- a/doc/en/plugin_list.rst +++ b/doc/en/reference/plugin_list.rst @@ -1,3 +1,5 @@ +.. _plugin-list: + Plugins List ============ diff --git a/doc/en/reference.rst b/doc/en/reference/reference.rst similarity index 98% rename from doc/en/reference.rst rename to doc/en/reference/reference.rst index 9ad82b3e4b9..d18150bac0b 100644 --- a/doc/en/reference.rst +++ b/doc/en/reference/reference.rst @@ -136,7 +136,7 @@ Add warning filters to marked test items. pytest.mark.parametrize ~~~~~~~~~~~~~~~~~~~~~~~ -**Tutorial**: :doc:`parametrize`. +:ref:`parametrize`. This mark has the same signature as :py:meth:`pytest.Metafunc.parametrize`; see there. @@ -146,7 +146,7 @@ This mark has the same signature as :py:meth:`pytest.Metafunc.parametrize`; see pytest.mark.skip ~~~~~~~~~~~~~~~~ -**Tutorial**: :ref:`skip`. +:ref:`skip`. Unconditionally skip a test function. @@ -160,7 +160,7 @@ Unconditionally skip a test function. pytest.mark.skipif ~~~~~~~~~~~~~~~~~~ -**Tutorial**: :ref:`skipif`. +:ref:`skipif`. Skip a test function if a condition is ``True``. @@ -325,7 +325,7 @@ Under the hood, the cache plugin uses the simple capsys ~~~~~~ -**Tutorial**: :doc:`capture`. +:ref:`captures`. .. autofunction:: _pytest.capture.capsys() :no-auto-options: @@ -350,7 +350,7 @@ capsys capsysbinary ~~~~~~~~~~~~ -**Tutorial**: :doc:`capture`. +:ref:`captures`. .. autofunction:: _pytest.capture.capsysbinary() :no-auto-options: @@ -372,7 +372,7 @@ capsysbinary capfd ~~~~~~ -**Tutorial**: :doc:`capture`. +:ref:`captures`. .. autofunction:: _pytest.capture.capfd() :no-auto-options: @@ -394,7 +394,7 @@ capfd capfdbinary ~~~~~~~~~~~~ -**Tutorial**: :doc:`capture`. +:ref:`captures`. .. autofunction:: _pytest.capture.capfdbinary() :no-auto-options: @@ -416,7 +416,7 @@ capfdbinary doctest_namespace ~~~~~~~~~~~~~~~~~ -**Tutorial**: :doc:`doctest`. +:ref:`doctest`. .. autofunction:: _pytest.doctest.doctest_namespace() @@ -436,7 +436,7 @@ doctest_namespace request ~~~~~~~ -**Tutorial**: :ref:`request example`. +:ref:`request example`. The ``request`` fixture is a special fixture providing information of the requesting test function. @@ -477,7 +477,7 @@ record_testsuite_property caplog ~~~~~~ -**Tutorial**: :doc:`logging`. +:ref:`logging`. .. autofunction:: _pytest.logging.caplog() :no-auto-options: @@ -493,7 +493,7 @@ caplog monkeypatch ~~~~~~~~~~~ -**Tutorial**: :doc:`monkeypatch`. +:ref:`monkeypatching`. .. autofunction:: _pytest.monkeypatch.monkeypatch() :no-auto-options: @@ -576,7 +576,7 @@ Each recorded warning is an instance of :class:`warnings.WarningMessage`. tmp_path ~~~~~~~~ -**Tutorial**: :doc:`tmpdir` +:ref:`tmpdir` .. autofunction:: _pytest.tmpdir.tmp_path() :no-auto-options: @@ -587,7 +587,7 @@ tmp_path tmp_path_factory ~~~~~~~~~~~~~~~~ -**Tutorial**: :ref:`tmp_path_factory example` +:ref:`tmp_path_factory example` .. _`tmp_path_factory factory api`: @@ -601,7 +601,7 @@ tmp_path_factory tmpdir ~~~~~~ -**Tutorial**: :doc:`tmpdir` +:ref:`tmpdir` .. autofunction:: _pytest.tmpdir.tmpdir() :no-auto-options: @@ -612,7 +612,7 @@ tmpdir tmpdir_factory ~~~~~~~~~~~~~~ -**Tutorial**: :ref:`tmpdir factory example` +:ref:`tmpdir factory example` .. _`tmpdir factory api`: @@ -626,7 +626,7 @@ tmpdir_factory Hooks ----- -**Tutorial**: :doc:`writing_plugins`. +:ref:`writing-plugins`. .. currentmodule:: _pytest.hookspec @@ -1178,13 +1178,13 @@ passed multiple times. The expected format is ``name=value``. For example:: Default encoding to use to decode text files with docstrings. - :doc:`See how pytest handles doctests `. + :ref:`See how pytest handles doctests `. .. confval:: doctest_optionflags One or more doctest flag names from the standard ``doctest`` module. - :doc:`See how pytest handles doctests `. + :ref:`See how pytest handles doctests `. .. confval:: empty_parameter_set_mark diff --git a/doc/en/unittest.rst b/doc/en/reference/unittest.rst similarity index 100% rename from doc/en/unittest.rst rename to doc/en/reference/unittest.rst diff --git a/doc/en/warnings.rst b/doc/en/reference/warnings.rst similarity index 100% rename from doc/en/warnings.rst rename to doc/en/reference/warnings.rst diff --git a/doc/en/writing_plugins.rst b/doc/en/reference/writing_plugins.rst similarity index 99% rename from doc/en/writing_plugins.rst rename to doc/en/reference/writing_plugins.rst index 92a3dd7dd48..b4d9f49ff46 100644 --- a/doc/en/writing_plugins.rst +++ b/doc/en/reference/writing_plugins.rst @@ -122,7 +122,7 @@ you can copy from: * a custom collection example plugin: :ref:`yaml plugin` * builtin plugins which provide pytest's own functionality -* many :doc:`external plugins ` providing additional features +* many :ref:`external plugins ` providing additional features All of these plugins implement :ref:`hooks ` and/or :ref:`fixtures ` to extend and add functionality. diff --git a/doc/en/xunit_setup.rst b/doc/en/reference/xunit_setup.rst similarity index 100% rename from doc/en/xunit_setup.rst rename to doc/en/reference/xunit_setup.rst From ff2ee96b8cb7493c71ac13d221bd724c6e2d173f Mon Sep 17 00:00:00 2001 From: Daniele Procida Date: Fri, 12 Mar 2021 22:16:47 +0000 Subject: [PATCH 171/630] Split fixtures documentation into separate documents. Sections have been moved to: * reference/fixtures.rst * how-to/fixtures.rst * fixtures.rst according to their function. Further refinement and rewriting will be required. Some material has been moved to a new "Anatomy of a test" document, in anticipation that material from other sections will also find a natural home there later. Removed several unneeded reference targets from fixtures documentation. --- doc/en/anatomy.rst | 46 ++ doc/en/contents.rst | 5 +- doc/en/example/simple.rst | 2 +- doc/en/fixtures.rst | 157 ++++ .../fixture.rst => how-to/fixtures.rst} | 687 +----------------- doc/en/reference/fixtures.rst | 452 ++++++++++++ 6 files changed, 683 insertions(+), 666 deletions(-) create mode 100644 doc/en/anatomy.rst create mode 100644 doc/en/fixtures.rst rename doc/en/{reference/fixture.rst => how-to/fixtures.rst} (70%) create mode 100644 doc/en/reference/fixtures.rst diff --git a/doc/en/anatomy.rst b/doc/en/anatomy.rst new file mode 100644 index 00000000000..e86dd74251e --- /dev/null +++ b/doc/en/anatomy.rst @@ -0,0 +1,46 @@ +.. _test-anatomy: + +Anatomy of a test +================= + +In the simplest terms, a test is meant to look at the result of a particular +behavior, and make sure that result aligns with what you would expect. +Behavior is not something that can be empirically measured, which is why writing +tests can be challenging. + +"Behavior" is the way in which some system **acts in response** to a particular +situation and/or stimuli. But exactly *how* or *why* something is done is not +quite as important as *what* was done. + +You can think of a test as being broken down into four steps: + +1. **Arrange** +2. **Act** +3. **Assert** +4. **Cleanup** + +**Arrange** is where we prepare everything for our test. This means pretty +much everything except for the "**act**". It's lining up the dominoes so that +the **act** can do its thing in one, state-changing step. This can mean +preparing objects, starting/killing services, entering records into a database, +or even things like defining a URL to query, generating some credentials for a +user that doesn't exist yet, or just waiting for some process to finish. + +**Act** is the singular, state-changing action that kicks off the **behavior** +we want to test. This behavior is what carries out the changing of the state of +the system under test (SUT), and it's the resulting changed state that we can +look at to make a judgement about the behavior. This typically takes the form of +a function/method call. + +**Assert** is where we look at that resulting state and check if it looks how +we'd expect after the dust has settled. It's where we gather evidence to say the +behavior does or does not aligns with what we expect. The ``assert`` in our test +is where we take that measurement/observation and apply our judgement to it. If +something should be green, we'd say ``assert thing == "green"``. + +**Cleanup** is where the test picks up after itself, so other tests aren't being +accidentally influenced by it. + +At its core, the test is ultimately the **act** and **assert** steps, with the +**arrange** step only providing the context. **Behavior** exists between **act** +and **assert**. diff --git a/doc/en/contents.rst b/doc/en/contents.rst index 84b87a35fb2..45f26cc128e 100644 --- a/doc/en/contents.rst +++ b/doc/en/contents.rst @@ -35,6 +35,7 @@ How-to guides how-to/plugins how-to/nose how-to/bash-completion + how-to/fixtures Reference guides @@ -43,7 +44,7 @@ Reference guides .. toctree:: :maxdepth: 2 - reference/fixture + reference/fixtures reference/warnings reference/doctest reference/cache @@ -62,6 +63,8 @@ Further topics .. toctree:: :maxdepth: 2 + anatomy + fixtures goodpractices flaky pythonpath diff --git a/doc/en/example/simple.rst b/doc/en/example/simple.rst index b641e61f718..18f1945c544 100644 --- a/doc/en/example/simple.rst +++ b/doc/en/example/simple.rst @@ -69,7 +69,7 @@ Here is a basic pattern to achieve this: For this to work we need to add a command line option and -provide the ``cmdopt`` through a :ref:`fixture function `: +provide the ``cmdopt`` through a :ref:`fixture function `: .. code-block:: python diff --git a/doc/en/fixtures.rst b/doc/en/fixtures.rst new file mode 100644 index 00000000000..7d53ba0892f --- /dev/null +++ b/doc/en/fixtures.rst @@ -0,0 +1,157 @@ +.. _about-fixtures: + +About fixtures +=============== + +.. seealso:: :ref:`how-to-fixtures` +.. seealso:: :ref:`Fixtures reference ` + + +What fixtures are +----------------- + +In testing, a `fixture `_ +provides a defined, reliable and consistent context for the tests. This could +include environment (for example a database configured with known parameters) +or content (such as a dataset). + +Fixtures define the steps and data that constitute the *arrange* phase of a +test (see :ref:`test-anatomy`). In pytest, they are functions you define that +serve this purpose. They can also be used to define a test's *act* phase; this +is a powerful technique for designing more complex tests. + +The services, state, or other operating environments set up by fixtures are +accessed by test functions through arguments. For each fixture used by a test +function there is typically a parameter (named after the fixture) in the test +function's definition. + +We can tell pytest that a particular function is a fixture by decorating it with +:py:func:`@pytest.fixture `. Here's a simple example of +what a fixture in pytest might look like: + +.. code-block:: python + + import pytest + + + class Fruit: + def __init__(self, name): + self.name = name + + def __eq__(self, other): + return self.name == other.name + + + @pytest.fixture + def my_fruit(): + return Fruit("apple") + + + @pytest.fixture + def fruit_basket(my_fruit): + return [Fruit("banana"), my_fruit] + + + def test_my_fruit_in_basket(my_fruit, fruit_basket): + assert my_fruit in fruit_basket + +Tests don't have to be limited to a single fixture, either. They can depend on +as many fixtures as you want, and fixtures can use other fixtures, as well. This +is where pytest's fixture system really shines. + + +Improvements over xUnit-style setup/teardown functions +----------------------------------------------------------- + +pytest fixtures offer dramatic improvements over the classic xUnit +style of setup/teardown functions: + +* fixtures have explicit names and are activated by declaring their use + from test functions, modules, classes or whole projects. + +* fixtures are implemented in a modular manner, as each fixture name + triggers a *fixture function* which can itself use other fixtures. + +* fixture management scales from simple unit to complex + functional testing, allowing to parametrize fixtures and tests according + to configuration and component options, or to re-use fixtures + across function, class, module or whole test session scopes. + +* teardown logic can be easily, and safely managed, no matter how many fixtures + are used, without the need to carefully handle errors by hand or micromanage + the order that cleanup steps are added. + +In addition, pytest continues to support :ref:`xunitsetup`. You can mix +both styles, moving incrementally from classic to new style, as you +prefer. You can also start out from existing :ref:`unittest.TestCase +style ` or :ref:`nose based ` projects. + + + +Fixture errors +-------------- + +pytest does its best to put all the fixtures for a given test in a linear order +so that it can see which fixture happens first, second, third, and so on. If an +earlier fixture has a problem, though, and raises an exception, pytest will stop +executing fixtures for that test and mark the test as having an error. + +When a test is marked as having an error, it doesn't mean the test failed, +though. It just means the test couldn't even be attempted because one of the +things it depends on had a problem. + +This is one reason why it's a good idea to cut out as many unnecessary +dependencies as possible for a given test. That way a problem in something +unrelated isn't causing us to have an incomplete picture of what may or may not +have issues. + +Here's a quick example to help explain: + +.. code-block:: python + + import pytest + + + @pytest.fixture + def order(): + return [] + + + @pytest.fixture + def append_first(order): + order.append(1) + + + @pytest.fixture + def append_second(order, append_first): + order.extend([2]) + + + @pytest.fixture(autouse=True) + def append_third(order, append_second): + order += [3] + + + def test_order(order): + assert order == [1, 2, 3] + + +If, for whatever reason, ``order.append(1)`` had a bug and it raises an exception, +we wouldn't be able to know if ``order.extend([2])`` or ``order += [3]`` would +also have problems. After ``append_first`` throws an exception, pytest won't run +any more fixtures for ``test_order``, and it won't even try to run +``test_order`` itself. The only things that would've run would be ``order`` and +``append_first``. + + +Sharing test data +----------------- + +If you want to make test data from files available to your tests, a good way +to do this is by loading these data in a fixture for use by your tests. +This makes use of the automatic caching mechanisms of pytest. + +Another good approach is by adding the data files in the ``tests`` folder. +There are also community plugins available to help managing this aspect of +testing, e.g. `pytest-datadir `__ +and `pytest-datafiles `__. diff --git a/doc/en/reference/fixture.rst b/doc/en/how-to/fixtures.rst similarity index 70% rename from doc/en/reference/fixture.rst rename to doc/en/how-to/fixtures.rst index 0fc25118baa..a2eb211e182 100644 --- a/doc/en/reference/fixture.rst +++ b/doc/en/how-to/fixtures.rst @@ -1,232 +1,17 @@ -.. _fixture: -.. _fixtures: -.. _`fixture functions`: +.. _how-to-fixtures: -pytest fixtures: explicit, modular, scalable -======================================================== +How to use fixtures +==================== -.. currentmodule:: _pytest.python +.. seealso:: :ref:`about-fixtures` +.. seealso:: :ref:`Fixtures reference ` - -.. _`xUnit`: https://en.wikipedia.org/wiki/XUnit -.. _`Software test fixtures`: https://en.wikipedia.org/wiki/Test_fixture#Software -.. _`Dependency injection`: https://en.wikipedia.org/wiki/Dependency_injection -.. _`Transaction`: https://en.wikipedia.org/wiki/Transaction_processing -.. _`linearizable`: https://en.wikipedia.org/wiki/Linearizability - -`Software test fixtures`_ initialize test functions. They provide a -fixed baseline so that tests execute reliably and produce consistent, -repeatable, results. Initialization may setup services, state, or -other operating environments. These are accessed by test functions -through arguments; for each fixture used by a test function there is -typically a parameter (named after the fixture) in the test function's -definition. - -pytest fixtures offer dramatic improvements over the classic xUnit -style of setup/teardown functions: - -* fixtures have explicit names and are activated by declaring their use - from test functions, modules, classes or whole projects. - -* fixtures are implemented in a modular manner, as each fixture name - triggers a *fixture function* which can itself use other fixtures. - -* fixture management scales from simple unit to complex - functional testing, allowing to parametrize fixtures and tests according - to configuration and component options, or to re-use fixtures - across function, class, module or whole test session scopes. - -* teardown logic can be easily, and safely managed, no matter how many fixtures - are used, without the need to carefully handle errors by hand or micromanage - the order that cleanup steps are added. - -In addition, pytest continues to support :ref:`xunitsetup`. You can mix -both styles, moving incrementally from classic to new style, as you -prefer. You can also start out from existing :ref:`unittest.TestCase -style ` or :ref:`nose based ` projects. - -:ref:`Fixtures ` are defined using the -:ref:`@pytest.fixture ` decorator, :ref:`described -below `. Pytest has useful built-in fixtures, listed here -for reference: - - :fixture:`capfd` - Capture, as text, output to file descriptors ``1`` and ``2``. - - :fixture:`capfdbinary` - Capture, as bytes, output to file descriptors ``1`` and ``2``. - - :fixture:`caplog` - Control logging and access log entries. - - :fixture:`capsys` - Capture, as text, output to ``sys.stdout`` and ``sys.stderr``. - - :fixture:`capsysbinary` - Capture, as bytes, output to ``sys.stdout`` and ``sys.stderr``. - - :fixture:`cache` - Store and retrieve values across pytest runs. - - :fixture:`doctest_namespace` - Provide a dict injected into the docstests namespace. - - :fixture:`monkeypatch` - Temporarily modify classes, functions, dictionaries, - ``os.environ``, and other objects. - - :fixture:`pytestconfig` - Access to configuration values, pluginmanager and plugin hooks. - - :fixture:`record_property` - Add extra properties to the test. - - :fixture:`record_testsuite_property` - Add extra properties to the test suite. - - :fixture:`recwarn` - Record warnings emitted by test functions. - - :fixture:`request` - Provide information on the executing test function. - - :fixture:`testdir` - Provide a temporary test directory to aid in running, and - testing, pytest plugins. - - :fixture:`tmp_path` - Provide a :class:`pathlib.Path` object to a temporary directory - which is unique to each test function. - - :fixture:`tmp_path_factory` - Make session-scoped temporary directories and return - :class:`pathlib.Path` objects. - - :fixture:`tmpdir` - Provide a :class:`py.path.local` object to a temporary - directory which is unique to each test function; - replaced by :fixture:`tmp_path`. - - .. _`py.path.local`: https://py.readthedocs.io/en/latest/path.html - - :fixture:`tmpdir_factory` - Make session-scoped temporary directories and return - :class:`py.path.local` objects; - replaced by :fixture:`tmp_path_factory`. - -.. _`funcargs`: -.. _`funcarg mechanism`: -.. _`fixture function`: -.. _`@pytest.fixture`: -.. _`pytest.fixture`: - -What fixtures are ------------------ - -Before we dive into what fixtures are, let's first look at what a test is. - -In the simplest terms, a test is meant to look at the result of a particular -behavior, and make sure that result aligns with what you would expect. -Behavior is not something that can be empirically measured, which is why writing -tests can be challenging. - -"Behavior" is the way in which some system **acts in response** to a particular -situation and/or stimuli. But exactly *how* or *why* something is done is not -quite as important as *what* was done. - -You can think of a test as being broken down into four steps: - -1. **Arrange** -2. **Act** -3. **Assert** -4. **Cleanup** - -**Arrange** is where we prepare everything for our test. This means pretty -much everything except for the "**act**". It's lining up the dominoes so that -the **act** can do its thing in one, state-changing step. This can mean -preparing objects, starting/killing services, entering records into a database, -or even things like defining a URL to query, generating some credentials for a -user that doesn't exist yet, or just waiting for some process to finish. - -**Act** is the singular, state-changing action that kicks off the **behavior** -we want to test. This behavior is what carries out the changing of the state of -the system under test (SUT), and it's the resulting changed state that we can -look at to make a judgement about the behavior. This typically takes the form of -a function/method call. - -**Assert** is where we look at that resulting state and check if it looks how -we'd expect after the dust has settled. It's where we gather evidence to say the -behavior does or does not aligns with what we expect. The ``assert`` in our test -is where we take that measurement/observation and apply our judgement to it. If -something should be green, we'd say ``assert thing == "green"``. - -**Cleanup** is where the test picks up after itself, so other tests aren't being -accidentally influenced by it. - -At it's core, the test is ultimately the **act** and **assert** steps, with the -**arrange** step only providing the context. **Behavior** exists between **act** -and **assert**. - -Back to fixtures -^^^^^^^^^^^^^^^^ - -"Fixtures", in the literal sense, are each of the **arrange** steps and data. They're -everything that test needs to do its thing. - -In pytest, "fixtures" are functions you define that serve this purpose. But they -don't have to be limited to just the **arrange** steps. They can provide the -**act** step, as well, and this can be a powerful technique for designing more -complex tests, especially given how pytest's fixture system works. But we'll get -into that further down. - -We can tell pytest that a particular function is a fixture by decorating it with -:py:func:`@pytest.fixture `. Here's a simple example of -what a fixture in pytest might look like: - -.. code-block:: python - - import pytest - - - class Fruit: - def __init__(self, name): - self.name = name - - def __eq__(self, other): - return self.name == other.name - - - @pytest.fixture - def my_fruit(): - return Fruit("apple") - - - @pytest.fixture - def fruit_basket(my_fruit): - return [Fruit("banana"), my_fruit] - - - def test_my_fruit_in_basket(my_fruit, fruit_basket): - assert my_fruit in fruit_basket - - - -Tests don't have to be limited to a single fixture, either. They can depend on -as many fixtures as you want, and fixtures can use other fixtures, as well. This -is where pytest's fixture system really shines. - -Don't be afraid to break things up if it makes things cleaner. - "Requesting" fixtures --------------------- -So fixtures are how we *prepare* for a test, but how do we tell pytest what -tests and fixtures need which fixtures? - -At a basic level, test functions request fixtures by declaring them as -arguments, as in the ``test_my_fruit_in_basket(my_fruit, fruit_basket):`` in the -previous example. +At a basic level, test functions request fixtures they require by declaring +them as arguments. When pytest goes to run a test, it looks at the parameters in that test function's signature, and then searches for fixtures that have the same names as @@ -234,6 +19,7 @@ those parameters. Once pytest finds them, it runs those fixtures, captures what they returned (if anything), and passes those objects into the test function as arguments. + Quick example ^^^^^^^^^^^^^ @@ -300,6 +86,7 @@ what's happening if we were to do it by hand: bowl = fruit_bowl() test_fruit_salad(fruit_bowl=bowl) + Fixtures can **request** other fixtures ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -744,62 +531,6 @@ containers for different environments. See the example below. def docker_container(): yield spawn_container() -Fixture errors --------------- - -pytest does its best to put all the fixtures for a given test in a linear order -so that it can see which fixture happens first, second, third, and so on. If an -earlier fixture has a problem, though, and raises an exception, pytest will stop -executing fixtures for that test and mark the test as having an error. - -When a test is marked as having an error, it doesn't mean the test failed, -though. It just means the test couldn't even be attempted because one of the -things it depends on had a problem. - -This is one reason why it's a good idea to cut out as many unnecessary -dependencies as possible for a given test. That way a problem in something -unrelated isn't causing us to have an incomplete picture of what may or may not -have issues. - -Here's a quick example to help explain: - -.. code-block:: python - - import pytest - - - @pytest.fixture - def order(): - return [] - - - @pytest.fixture - def append_first(order): - order.append(1) - - - @pytest.fixture - def append_second(order, append_first): - order.extend([2]) - - - @pytest.fixture(autouse=True) - def append_third(order, append_second): - order += [3] - - - def test_order(order): - assert order == [1, 2, 3] - - -If, for whatever reason, ``order.append(1)`` had a bug and it raises an exception, -we wouldn't be able to know if ``order.extend([2])`` or ``order += [3]`` would -also have problems. After ``append_first`` throws an exception, pytest won't run -any more fixtures for ``test_order``, and it won't even try to run -``test_order`` itself. The only things that would've run would be ``order`` and -``append_first``. - - .. _`finalization`: @@ -1070,12 +801,13 @@ making one state-changing action each, and then bundling them together with their teardown code, as :ref:`the email examples above ` showed. The chance that a state-changing operation can fail but still modify state is -negligible, as most of these operations tend to be `transaction`_-based (at -least at the level of testing where state could be left behind). So if we make -sure that any successful state-changing action gets torn down by moving it to a -separate fixture function and separating it from other, potentially failing -state-changing actions, then our tests will stand the best chance at leaving the -test environment the way they found it. +negligible, as most of these operations tend to be `transaction +`_-based (at least at the +level of testing where state could be left behind). So if we make sure that any +successful state-changing action gets torn down by moving it to a separate +fixture function and separating it from other, potentially failing +state-changing actions, then our tests will stand the best chance at leaving +the test environment the way they found it. For an example, let's say we have a website with a login page, and we have access to an admin API where we can generate users. For our test, we want to: @@ -1146,12 +878,13 @@ Here's what that might look like: def test_name_on_landing_page_after_login(landing_page, user): assert landing_page.header == f"Welcome, {user.name}!" -The way the dependencies are laid out means it's unclear if the ``user`` fixture -would execute before the ``driver`` fixture. But that's ok, because those are -atomic operations, and so it doesn't matter which one runs first because the -sequence of events for the test is still `linearizable`_. But what *does* matter -is that, no matter which one runs first, if the one raises an exception while -the other would not have, neither will have left anything behind. If ``driver`` +The way the dependencies are laid out means it's unclear if the ``user`` +fixture would execute before the ``driver`` fixture. But that's ok, because +those are atomic operations, and so it doesn't matter which one runs first +because the sequence of events for the test is still `linearizable +`_. But what *does* matter is +that, no matter which one runs first, if the one raises an exception while the +other would not have, neither will have left anything behind. If ``driver`` executes before ``user``, and ``user`` raises an exception, the driver will still quit, and the user was never made. And if ``driver`` was the one to raise the exception, then the driver would never have been started and the user would @@ -1165,380 +898,6 @@ never have been made. won't bother trying to start the driver, which is a fairly expensive operation. -.. _`conftest.py`: -.. _`conftest`: - -Fixture availability ---------------------- - -Fixture availability is determined from the perspective of the test. A fixture -is only available for tests to request if they are in the scope that fixture is -defined in. If a fixture is defined inside a class, it can only be requested by -tests inside that class. But if a fixture is defined inside the global scope of -the module, than every test in that module, even if it's defined inside a class, -can request it. - -Similarly, a test can also only be affected by an autouse fixture if that test -is in the same scope that autouse fixture is defined in (see -:ref:`autouse order`). - -A fixture can also request any other fixture, no matter where it's defined, so -long as the test requesting them can see all fixtures involved. - -For example, here's a test file with a fixture (``outer``) that requests a -fixture (``inner``) from a scope it wasn't defined in: - -.. literalinclude:: /example/fixtures/test_fixtures_request_different_scope.py - -From the tests' perspectives, they have no problem seeing each of the fixtures -they're dependent on: - -.. image:: /example/fixtures/test_fixtures_request_different_scope.svg - :align: center - -So when they run, ``outer`` will have no problem finding ``inner``, because -pytest searched from the tests' perspectives. - -.. note:: - The scope a fixture is defined in has no bearing on the order it will be - instantiated in: the order is mandated by the logic described - :ref:`here `. - -``conftest.py``: sharing fixtures across multiple files -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The ``conftest.py`` file serves as a means of providing fixtures for an entire -directory. Fixtures defined in a ``conftest.py`` can be used by any test -in that package without needing to import them (pytest will automatically -discover them). - -You can have multiple nested directories/packages containing your tests, and -each directory can have its own ``conftest.py`` with its own fixtures, adding on -to the ones provided by the ``conftest.py`` files in parent directories. - -For example, given a test file structure like this: - -:: - - tests/ - __init__.py - - conftest.py - # content of tests/conftest.py - import pytest - - @pytest.fixture - def order(): - return [] - - @pytest.fixture - def top(order, innermost): - order.append("top") - - test_top.py - # content of tests/test_top.py - import pytest - - @pytest.fixture - def innermost(order): - order.append("innermost top") - - def test_order(order, top): - assert order == ["innermost top", "top"] - - subpackage/ - __init__.py - - conftest.py - # content of tests/subpackage/conftest.py - import pytest - - @pytest.fixture - def mid(order): - order.append("mid subpackage") - - test_subpackage.py - # content of tests/subpackage/test_subpackage.py - import pytest - - @pytest.fixture - def innermost(order, mid): - order.append("innermost subpackage") - - def test_order(order, top): - assert order == ["mid subpackage", "innermost subpackage", "top"] - -The boundaries of the scopes can be visualized like this: - -.. image:: /example/fixtures/fixture_availability.svg - :align: center - -The directories become their own sort of scope where fixtures that are defined -in a ``conftest.py`` file in that directory become available for that whole -scope. - -Tests are allowed to search upward (stepping outside a circle) for fixtures, but -can never go down (stepping inside a circle) to continue their search. So -``tests/subpackage/test_subpackage.py::test_order`` would be able to find the -``innermost`` fixture defined in ``tests/subpackage/test_subpackage.py``, but -the one defined in ``tests/test_top.py`` would be unavailable to it because it -would have to step down a level (step inside a circle) to find it. - -The first fixture the test finds is the one that will be used, so -:ref:`fixtures can be overriden ` if you need to change or -extend what one does for a particular scope. - -You can also use the ``conftest.py`` file to implement -:ref:`local per-directory plugins `. - -Fixtures from third-party plugins -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Fixtures don't have to be defined in this structure to be available for tests, -though. They can also be provided by third-party plugins that are installed, and -this is how many pytest plugins operate. As long as those plugins are installed, -the fixtures they provide can be requested from anywhere in your test suite. - -Because they're provided from outside the structure of your test suite, -third-party plugins don't really provide a scope like `conftest.py` files and -the directories in your test suite do. As a result, pytest will search for -fixtures stepping out through scopes as explained previously, only reaching -fixtures defined in plugins *last*. - -For example, given the following file structure: - -:: - - tests/ - __init__.py - - conftest.py - # content of tests/conftest.py - import pytest - - @pytest.fixture - def order(): - return [] - - subpackage/ - __init__.py - - conftest.py - # content of tests/subpackage/conftest.py - import pytest - - @pytest.fixture(autouse=True) - def mid(order, b_fix): - order.append("mid subpackage") - - test_subpackage.py - # content of tests/subpackage/test_subpackage.py - import pytest - - @pytest.fixture - def inner(order, mid, a_fix): - order.append("inner subpackage") - - def test_order(order, inner): - assert order == ["b_fix", "mid subpackage", "a_fix", "inner subpackage"] - -If ``plugin_a`` is installed and provides the fixture ``a_fix``, and -``plugin_b`` is installed and provides the fixture ``b_fix``, then this is what -the test's search for fixtures would look like: - -.. image:: /example/fixtures/fixture_availability_plugins.svg - :align: center - -pytest will only search for ``a_fix`` and ``b_fix`` in the plugins after -searching for them first in the scopes inside ``tests/``. - -.. note: - - pytest can tell you what fixtures are available for a given test if you call - ``pytests`` along with the test's name (or the scope it's in), and provide - the ``--fixtures`` flag, e.g. ``pytest --fixtures test_something.py`` - (fixtures with names that start with ``_`` will only be shown if you also - provide the ``-v`` flag). - -Sharing test data ------------------ - -If you want to make test data from files available to your tests, a good way -to do this is by loading these data in a fixture for use by your tests. -This makes use of the automatic caching mechanisms of pytest. - -Another good approach is by adding the data files in the ``tests`` folder. -There are also community plugins available to help managing this aspect of -testing, e.g. `pytest-datadir `__ -and `pytest-datafiles `__. - -.. _`fixture order`: - -Fixture instantiation order ---------------------------- - -When pytest wants to execute a test, once it knows what fixtures will be -executed, it has to figure out the order they'll be executed in. To do this, it -considers 3 factors: - -1. scope -2. dependencies -3. autouse - -Names of fixtures or tests, where they're defined, the order they're defined in, -and the order fixtures are requested in have no bearing on execution order -beyond coincidence. While pytest will try to make sure coincidences like these -stay consistent from run to run, it's not something that should be depended on. -If you want to control the order, it's safest to rely on these 3 things and make -sure dependencies are clearly established. - -Higher-scoped fixtures are executed first -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Within a function request for fixtures, those of higher-scopes (such as -``session``) are executed before lower-scoped fixtures (such as ``function`` or -``class``). - -Here's an example: - -.. literalinclude:: /example/fixtures/test_fixtures_order_scope.py - -The test will pass because the larger scoped fixtures are executing first. - -The order breaks down to this: - -.. image:: /example/fixtures/test_fixtures_order_scope.svg - :align: center - -Fixtures of the same order execute based on dependencies -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -When a fixture requests another fixture, the other fixture is executed first. -So if fixture ``a`` requests fixture ``b``, fixture ``b`` will execute first, -because ``a`` depends on ``b`` and can't operate without it. Even if ``a`` -doesn't need the result of ``b``, it can still request ``b`` if it needs to make -sure it is executed after ``b``. - -For example: - -.. literalinclude:: /example/fixtures/test_fixtures_order_dependencies.py - -If we map out what depends on what, we get something that look like this: - -.. image:: /example/fixtures/test_fixtures_order_dependencies.svg - :align: center - -The rules provided by each fixture (as to what fixture(s) each one has to come -after) are comprehensive enough that it can be flattened to this: - -.. image:: /example/fixtures/test_fixtures_order_dependencies_flat.svg - :align: center - -Enough information has to be provided through these requests in order for pytest -to be able to figure out a clear, linear chain of dependencies, and as a result, -an order of operations for a given test. If there's any ambiguity, and the order -of operations can be interpreted more than one way, you should assume pytest -could go with any one of those interpretations at any point. - -For example, if ``d`` didn't request ``c``, i.e.the graph would look like this: - -.. image:: /example/fixtures/test_fixtures_order_dependencies_unclear.svg - :align: center - -Because nothing requested ``c`` other than ``g``, and ``g`` also requests ``f``, -it's now unclear if ``c`` should go before/after ``f``, ``e``, or ``d``. The -only rules that were set for ``c`` is that it must execute after ``b`` and -before ``g``. - -pytest doesn't know where ``c`` should go in the case, so it should be assumed -that it could go anywhere between ``g`` and ``b``. - -This isn't necessarily bad, but it's something to keep in mind. If the order -they execute in could affect the behavior a test is targeting, or could -otherwise influence the result of a test, then the order should be defined -explicitly in a way that allows pytest to linearize/"flatten" that order. - -.. _`autouse order`: - -Autouse fixtures are executed first within their scope -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Autouse fixtures are assumed to apply to every test that could reference them, -so they are executed before other fixtures in that scope. Fixtures that are -requested by autouse fixtures effectively become autouse fixtures themselves for -the tests that the real autouse fixture applies to. - -So if fixture ``a`` is autouse and fixture ``b`` is not, but fixture ``a`` -requests fixture ``b``, then fixture ``b`` will effectively be an autouse -fixture as well, but only for the tests that ``a`` applies to. - -In the last example, the graph became unclear if ``d`` didn't request ``c``. But -if ``c`` was autouse, then ``b`` and ``a`` would effectively also be autouse -because ``c`` depends on them. As a result, they would all be shifted above -non-autouse fixtures within that scope. - -So if the test file looked like this: - -.. literalinclude:: /example/fixtures/test_fixtures_order_autouse.py - -the graph would look like this: - -.. image:: /example/fixtures/test_fixtures_order_autouse.svg - :align: center - -Because ``c`` can now be put above ``d`` in the graph, pytest can once again -linearize the graph to this: - -In this example, ``c`` makes ``b`` and ``a`` effectively autouse fixtures as -well. - -Be careful with autouse, though, as an autouse fixture will automatically -execute for every test that can reach it, even if they don't request it. For -example, consider this file: - -.. literalinclude:: /example/fixtures/test_fixtures_order_autouse_multiple_scopes.py - -Even though nothing in ``TestClassWithC1Request`` is requesting ``c1``, it still -is executed for the tests inside it anyway: - -.. image:: /example/fixtures/test_fixtures_order_autouse_multiple_scopes.svg - :align: center - -But just because one autouse fixture requested a non-autouse fixture, that -doesn't mean the non-autouse fixture becomes an autouse fixture for all contexts -that it can apply to. It only effectively becomes an auotuse fixture for the -contexts the real autouse fixture (the one that requested the non-autouse -fixture) can apply to. - -For example, take a look at this test file: - -.. literalinclude:: /example/fixtures/test_fixtures_order_autouse_temp_effects.py - -It would break down to something like this: - -.. image:: /example/fixtures/test_fixtures_order_autouse_temp_effects.svg - :align: center - -For ``test_req`` and ``test_no_req`` inside ``TestClassWithAutouse``, ``c3`` -effectively makes ``c2`` an autouse fixture, which is why ``c2`` and ``c3`` are -executed for both tests, despite not being requested, and why ``c2`` and ``c3`` -are executed before ``c1`` for ``test_req``. - -If this made ``c2`` an *actual* autouse fixture, then ``c2`` would also execute -for the tests inside ``TestClassWithoutAutouse``, since they can reference -``c2`` if they wanted to. But it doesn't, because from the perspective of the -``TestClassWithoutAutouse`` tests, ``c2`` isn't an autouse fixture, since they -can't see ``c3``. - - -.. note: - - pytest can tell you what order the fixtures will execute in for a given test - if you call ``pytests`` along with the test's name (or the scope it's in), - and provide the ``--setup-plan`` flag, e.g. - ``pytest --setup-plan test_something.py`` (fixtures with names that start - with ``_`` will only be shown if you also provide the ``-v`` flag). - Running multiple ``assert`` statements safely --------------------------------------------- diff --git a/doc/en/reference/fixtures.rst b/doc/en/reference/fixtures.rst new file mode 100644 index 00000000000..69e1d20b1c2 --- /dev/null +++ b/doc/en/reference/fixtures.rst @@ -0,0 +1,452 @@ +.. _reference-fixtures: +.. _fixture: +.. _fixtures: +.. _`@pytest.fixture`: +.. _`pytest.fixture`: + + +pytest fixtures: explicit, modular, scalable +======================================================== + +.. seealso:: :ref:`about-fixtures` +.. seealso:: :ref:`how-to-fixtures` + + +.. currentmodule:: _pytest.python + +.. _`Dependency injection`: https://en.wikipedia.org/wiki/Dependency_injection + + +Built-in fixtures +----------------- + +:ref:`Fixtures ` are defined using the :ref:`@pytest.fixture +` decorator. Pytest has several useful built-in fixtures: + + :fixture:`capfd` + Capture, as text, output to file descriptors ``1`` and ``2``. + + :fixture:`capfdbinary` + Capture, as bytes, output to file descriptors ``1`` and ``2``. + + :fixture:`caplog` + Control logging and access log entries. + + :fixture:`capsys` + Capture, as text, output to ``sys.stdout`` and ``sys.stderr``. + + :fixture:`capsysbinary` + Capture, as bytes, output to ``sys.stdout`` and ``sys.stderr``. + + :fixture:`cache` + Store and retrieve values across pytest runs. + + :fixture:`doctest_namespace` + Provide a dict injected into the docstests namespace. + + :fixture:`monkeypatch` + Temporarily modify classes, functions, dictionaries, + ``os.environ``, and other objects. + + :fixture:`pytestconfig` + Access to configuration values, pluginmanager and plugin hooks. + + :fixture:`record_property` + Add extra properties to the test. + + :fixture:`record_testsuite_property` + Add extra properties to the test suite. + + :fixture:`recwarn` + Record warnings emitted by test functions. + + :fixture:`request` + Provide information on the executing test function. + + :fixture:`testdir` + Provide a temporary test directory to aid in running, and + testing, pytest plugins. + + :fixture:`tmp_path` + Provide a :class:`pathlib.Path` object to a temporary directory + which is unique to each test function. + + :fixture:`tmp_path_factory` + Make session-scoped temporary directories and return + :class:`pathlib.Path` objects. + + :fixture:`tmpdir` + Provide a :class:`py.path.local` object to a temporary + directory which is unique to each test function; + replaced by :fixture:`tmp_path`. + + .. _`py.path.local`: https://py.readthedocs.io/en/latest/path.html + + :fixture:`tmpdir_factory` + Make session-scoped temporary directories and return + :class:`py.path.local` objects; + replaced by :fixture:`tmp_path_factory`. + + +.. _`conftest.py`: +.. _`conftest`: + +Fixture availability +--------------------- + +Fixture availability is determined from the perspective of the test. A fixture +is only available for tests to request if they are in the scope that fixture is +defined in. If a fixture is defined inside a class, it can only be requested by +tests inside that class. But if a fixture is defined inside the global scope of +the module, than every test in that module, even if it's defined inside a class, +can request it. + +Similarly, a test can also only be affected by an autouse fixture if that test +is in the same scope that autouse fixture is defined in (see +:ref:`autouse order`). + +A fixture can also request any other fixture, no matter where it's defined, so +long as the test requesting them can see all fixtures involved. + +For example, here's a test file with a fixture (``outer``) that requests a +fixture (``inner``) from a scope it wasn't defined in: + +.. literalinclude:: /example/fixtures/test_fixtures_request_different_scope.py + +From the tests' perspectives, they have no problem seeing each of the fixtures +they're dependent on: + +.. image:: /example/fixtures/test_fixtures_request_different_scope.svg + :align: center + +So when they run, ``outer`` will have no problem finding ``inner``, because +pytest searched from the tests' perspectives. + +.. note:: + The scope a fixture is defined in has no bearing on the order it will be + instantiated in: the order is mandated by the logic described + :ref:`here `. + +``conftest.py``: sharing fixtures across multiple files +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ``conftest.py`` file serves as a means of providing fixtures for an entire +directory. Fixtures defined in a ``conftest.py`` can be used by any test +in that package without needing to import them (pytest will automatically +discover them). + +You can have multiple nested directories/packages containing your tests, and +each directory can have its own ``conftest.py`` with its own fixtures, adding on +to the ones provided by the ``conftest.py`` files in parent directories. + +For example, given a test file structure like this: + +:: + + tests/ + __init__.py + + conftest.py + # content of tests/conftest.py + import pytest + + @pytest.fixture + def order(): + return [] + + @pytest.fixture + def top(order, innermost): + order.append("top") + + test_top.py + # content of tests/test_top.py + import pytest + + @pytest.fixture + def innermost(order): + order.append("innermost top") + + def test_order(order, top): + assert order == ["innermost top", "top"] + + subpackage/ + __init__.py + + conftest.py + # content of tests/subpackage/conftest.py + import pytest + + @pytest.fixture + def mid(order): + order.append("mid subpackage") + + test_subpackage.py + # content of tests/subpackage/test_subpackage.py + import pytest + + @pytest.fixture + def innermost(order, mid): + order.append("innermost subpackage") + + def test_order(order, top): + assert order == ["mid subpackage", "innermost subpackage", "top"] + +The boundaries of the scopes can be visualized like this: + +.. image:: /example/fixtures/fixture_availability.svg + :align: center + +The directories become their own sort of scope where fixtures that are defined +in a ``conftest.py`` file in that directory become available for that whole +scope. + +Tests are allowed to search upward (stepping outside a circle) for fixtures, but +can never go down (stepping inside a circle) to continue their search. So +``tests/subpackage/test_subpackage.py::test_order`` would be able to find the +``innermost`` fixture defined in ``tests/subpackage/test_subpackage.py``, but +the one defined in ``tests/test_top.py`` would be unavailable to it because it +would have to step down a level (step inside a circle) to find it. + +The first fixture the test finds is the one that will be used, so +:ref:`fixtures can be overriden ` if you need to change or +extend what one does for a particular scope. + +You can also use the ``conftest.py`` file to implement +:ref:`local per-directory plugins `. + +Fixtures from third-party plugins +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Fixtures don't have to be defined in this structure to be available for tests, +though. They can also be provided by third-party plugins that are installed, and +this is how many pytest plugins operate. As long as those plugins are installed, +the fixtures they provide can be requested from anywhere in your test suite. + +Because they're provided from outside the structure of your test suite, +third-party plugins don't really provide a scope like `conftest.py` files and +the directories in your test suite do. As a result, pytest will search for +fixtures stepping out through scopes as explained previously, only reaching +fixtures defined in plugins *last*. + +For example, given the following file structure: + +:: + + tests/ + __init__.py + + conftest.py + # content of tests/conftest.py + import pytest + + @pytest.fixture + def order(): + return [] + + subpackage/ + __init__.py + + conftest.py + # content of tests/subpackage/conftest.py + import pytest + + @pytest.fixture(autouse=True) + def mid(order, b_fix): + order.append("mid subpackage") + + test_subpackage.py + # content of tests/subpackage/test_subpackage.py + import pytest + + @pytest.fixture + def inner(order, mid, a_fix): + order.append("inner subpackage") + + def test_order(order, inner): + assert order == ["b_fix", "mid subpackage", "a_fix", "inner subpackage"] + +If ``plugin_a`` is installed and provides the fixture ``a_fix``, and +``plugin_b`` is installed and provides the fixture ``b_fix``, then this is what +the test's search for fixtures would look like: + +.. image:: /example/fixtures/fixture_availability_plugins.svg + :align: center + +pytest will only search for ``a_fix`` and ``b_fix`` in the plugins after +searching for them first in the scopes inside ``tests/``. + +.. note: + + pytest can tell you what fixtures are available for a given test if you call + ``pytests`` along with the test's name (or the scope it's in), and provide + the ``--fixtures`` flag, e.g. ``pytest --fixtures test_something.py`` + (fixtures with names that start with ``_`` will only be shown if you also + provide the ``-v`` flag). + + +.. _`fixture order`: + +Fixture instantiation order +--------------------------- + +When pytest wants to execute a test, once it knows what fixtures will be +executed, it has to figure out the order they'll be executed in. To do this, it +considers 3 factors: + +1. scope +2. dependencies +3. autouse + +Names of fixtures or tests, where they're defined, the order they're defined in, +and the order fixtures are requested in have no bearing on execution order +beyond coincidence. While pytest will try to make sure coincidences like these +stay consistent from run to run, it's not something that should be depended on. +If you want to control the order, it's safest to rely on these 3 things and make +sure dependencies are clearly established. + +Higher-scoped fixtures are executed first +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Within a function request for fixtures, those of higher-scopes (such as +``session``) are executed before lower-scoped fixtures (such as ``function`` or +``class``). + +Here's an example: + +.. literalinclude:: /example/fixtures/test_fixtures_order_scope.py + +The test will pass because the larger scoped fixtures are executing first. + +The order breaks down to this: + +.. image:: /example/fixtures/test_fixtures_order_scope.svg + :align: center + +Fixtures of the same order execute based on dependencies +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +When a fixture requests another fixture, the other fixture is executed first. +So if fixture ``a`` requests fixture ``b``, fixture ``b`` will execute first, +because ``a`` depends on ``b`` and can't operate without it. Even if ``a`` +doesn't need the result of ``b``, it can still request ``b`` if it needs to make +sure it is executed after ``b``. + +For example: + +.. literalinclude:: /example/fixtures/test_fixtures_order_dependencies.py + +If we map out what depends on what, we get something that look like this: + +.. image:: /example/fixtures/test_fixtures_order_dependencies.svg + :align: center + +The rules provided by each fixture (as to what fixture(s) each one has to come +after) are comprehensive enough that it can be flattened to this: + +.. image:: /example/fixtures/test_fixtures_order_dependencies_flat.svg + :align: center + +Enough information has to be provided through these requests in order for pytest +to be able to figure out a clear, linear chain of dependencies, and as a result, +an order of operations for a given test. If there's any ambiguity, and the order +of operations can be interpreted more than one way, you should assume pytest +could go with any one of those interpretations at any point. + +For example, if ``d`` didn't request ``c``, i.e.the graph would look like this: + +.. image:: /example/fixtures/test_fixtures_order_dependencies_unclear.svg + :align: center + +Because nothing requested ``c`` other than ``g``, and ``g`` also requests ``f``, +it's now unclear if ``c`` should go before/after ``f``, ``e``, or ``d``. The +only rules that were set for ``c`` is that it must execute after ``b`` and +before ``g``. + +pytest doesn't know where ``c`` should go in the case, so it should be assumed +that it could go anywhere between ``g`` and ``b``. + +This isn't necessarily bad, but it's something to keep in mind. If the order +they execute in could affect the behavior a test is targeting, or could +otherwise influence the result of a test, then the order should be defined +explicitly in a way that allows pytest to linearize/"flatten" that order. + +.. _`autouse order`: + +Autouse fixtures are executed first within their scope +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Autouse fixtures are assumed to apply to every test that could reference them, +so they are executed before other fixtures in that scope. Fixtures that are +requested by autouse fixtures effectively become autouse fixtures themselves for +the tests that the real autouse fixture applies to. + +So if fixture ``a`` is autouse and fixture ``b`` is not, but fixture ``a`` +requests fixture ``b``, then fixture ``b`` will effectively be an autouse +fixture as well, but only for the tests that ``a`` applies to. + +In the last example, the graph became unclear if ``d`` didn't request ``c``. But +if ``c`` was autouse, then ``b`` and ``a`` would effectively also be autouse +because ``c`` depends on them. As a result, they would all be shifted above +non-autouse fixtures within that scope. + +So if the test file looked like this: + +.. literalinclude:: /example/fixtures/test_fixtures_order_autouse.py + +the graph would look like this: + +.. image:: /example/fixtures/test_fixtures_order_autouse.svg + :align: center + +Because ``c`` can now be put above ``d`` in the graph, pytest can once again +linearize the graph to this: + +In this example, ``c`` makes ``b`` and ``a`` effectively autouse fixtures as +well. + +Be careful with autouse, though, as an autouse fixture will automatically +execute for every test that can reach it, even if they don't request it. For +example, consider this file: + +.. literalinclude:: /example/fixtures/test_fixtures_order_autouse_multiple_scopes.py + +Even though nothing in ``TestClassWithC1Request`` is requesting ``c1``, it still +is executed for the tests inside it anyway: + +.. image:: /example/fixtures/test_fixtures_order_autouse_multiple_scopes.svg + :align: center + +But just because one autouse fixture requested a non-autouse fixture, that +doesn't mean the non-autouse fixture becomes an autouse fixture for all contexts +that it can apply to. It only effectively becomes an auotuse fixture for the +contexts the real autouse fixture (the one that requested the non-autouse +fixture) can apply to. + +For example, take a look at this test file: + +.. literalinclude:: /example/fixtures/test_fixtures_order_autouse_temp_effects.py + +It would break down to something like this: + +.. image:: /example/fixtures/test_fixtures_order_autouse_temp_effects.svg + :align: center + +For ``test_req`` and ``test_no_req`` inside ``TestClassWithAutouse``, ``c3`` +effectively makes ``c2`` an autouse fixture, which is why ``c2`` and ``c3`` are +executed for both tests, despite not being requested, and why ``c2`` and ``c3`` +are executed before ``c1`` for ``test_req``. + +If this made ``c2`` an *actual* autouse fixture, then ``c2`` would also execute +for the tests inside ``TestClassWithoutAutouse``, since they can reference +``c2`` if they wanted to. But it doesn't, because from the perspective of the +``TestClassWithoutAutouse`` tests, ``c2`` isn't an autouse fixture, since they +can't see ``c3``. + + +.. note: + + pytest can tell you what order the fixtures will execute in for a given test + if you call ``pytests`` along with the test's name (or the scope it's in), + and provide the ``--setup-plan`` flag, e.g. + ``pytest --setup-plan test_something.py`` (fixtures with names that start + with ``_`` will only be shown if you also provide the ``-v`` flag). From 8410d9ac549c5a743a55350bce2ac4c6c592fa98 Mon Sep 17 00:00:00 2001 From: Daniele Procida Date: Fri, 12 Mar 2021 22:57:53 +0000 Subject: [PATCH 172/630] Created a new Explanation section and new landing pages for main sections. Added landing pages for: How-to guides Reference guides Explanation Added links to the main landing pages to the sidebar. --- doc/en/_templates/globaltoc.html | 14 +++++++++++--- doc/en/contents.rst | 18 +++++++++++++----- doc/en/{ => explanation}/anatomy.rst | 0 doc/en/{ => explanation}/fixtures.rst | 0 doc/en/{ => explanation}/flaky.rst | 0 doc/en/{ => explanation}/goodpractices.rst | 2 +- doc/en/explanation/index.rst | 13 +++++++++++++ doc/en/{ => explanation}/pythonpath.rst | 0 doc/en/how-to/index.rst | 21 +++++++++++++++++++++ doc/en/reference/index.rst | 19 +++++++++++++++++++ 10 files changed, 78 insertions(+), 9 deletions(-) rename doc/en/{ => explanation}/anatomy.rst (100%) rename doc/en/{ => explanation}/fixtures.rst (100%) rename doc/en/{ => explanation}/flaky.rst (100%) rename doc/en/{ => explanation}/goodpractices.rst (99%) create mode 100644 doc/en/explanation/index.rst rename doc/en/{ => explanation}/pythonpath.rst (100%) create mode 100644 doc/en/how-to/index.rst create mode 100644 doc/en/reference/index.rst diff --git a/doc/en/_templates/globaltoc.html b/doc/en/_templates/globaltoc.html index 4069e59ebd7..b6e74e8fbaf 100644 --- a/doc/en/_templates/globaltoc.html +++ b/doc/en/_templates/globaltoc.html @@ -2,12 +2,20 @@

        {{ _('Table Of Contents') }}

        • Home
        • +
        • Get started
        • +
        • How-to guides
        • +
        • Reference guides
        • +
        • Explanation
        • +
        • Examples
        • -
        • Customize
        • -
        • API Reference
        • -
        • 3rd party plugins
        • + +
        • Customize
        • +
        • API Reference
        • +
        • 3rd party plugins
        • +
        • Complete table of contents
        • +
        • Changelog
        • Contributing
        • Backwards Compatibility
        • diff --git a/doc/en/contents.rst b/doc/en/contents.rst index 45f26cc128e..16c8f273b62 100644 --- a/doc/en/contents.rst +++ b/doc/en/contents.rst @@ -57,17 +57,25 @@ Reference guides reference/reference +Explanation +----------------- + +.. toctree:: + :maxdepth: 2 + + explanation/anatomy + explanation/fixtures + explanation/goodpractices + explanation/flaky + explanation/pythonpath + + Further topics ----------------- .. toctree:: :maxdepth: 2 - anatomy - fixtures - goodpractices - flaky - pythonpath example/index backwards-compatibility diff --git a/doc/en/anatomy.rst b/doc/en/explanation/anatomy.rst similarity index 100% rename from doc/en/anatomy.rst rename to doc/en/explanation/anatomy.rst diff --git a/doc/en/fixtures.rst b/doc/en/explanation/fixtures.rst similarity index 100% rename from doc/en/fixtures.rst rename to doc/en/explanation/fixtures.rst diff --git a/doc/en/flaky.rst b/doc/en/explanation/flaky.rst similarity index 100% rename from doc/en/flaky.rst rename to doc/en/explanation/flaky.rst diff --git a/doc/en/goodpractices.rst b/doc/en/explanation/goodpractices.rst similarity index 99% rename from doc/en/goodpractices.rst rename to doc/en/explanation/goodpractices.rst index 4b3c0af10a6..77c176182fc 100644 --- a/doc/en/goodpractices.rst +++ b/doc/en/explanation/goodpractices.rst @@ -48,7 +48,7 @@ Conventions for Python test discovery * ``test`` prefixed test functions or methods outside of class * ``test`` prefixed test functions or methods inside ``Test`` prefixed test classes (without an ``__init__`` method) -For examples of how to customize your test discovery :doc:`example/pythoncollection`. +For examples of how to customize your test discovery :doc:`/example/pythoncollection`. Within Python modules, ``pytest`` also discovers tests using the standard :ref:`unittest.TestCase ` subclassing technique. diff --git a/doc/en/explanation/index.rst b/doc/en/explanation/index.rst new file mode 100644 index 00000000000..518fac99915 --- /dev/null +++ b/doc/en/explanation/index.rst @@ -0,0 +1,13 @@ +:orphan: + +Explanation +================ + +.. toctree:: + :maxdepth: 1 + + anatomy + fixtures + goodpractices + flaky + pythonpath diff --git a/doc/en/pythonpath.rst b/doc/en/explanation/pythonpath.rst similarity index 100% rename from doc/en/pythonpath.rst rename to doc/en/explanation/pythonpath.rst diff --git a/doc/en/how-to/index.rst b/doc/en/how-to/index.rst new file mode 100644 index 00000000000..c206a30570a --- /dev/null +++ b/doc/en/how-to/index.rst @@ -0,0 +1,21 @@ +:orphan: + +How-to guides +================ + +.. toctree:: + :maxdepth: 1 + + usage + existingtestsuite + assert + mark + monkeypatch + tmpdir + capture + skipping + parametrize + plugins + nose + bash-completion + fixtures diff --git a/doc/en/reference/index.rst b/doc/en/reference/index.rst new file mode 100644 index 00000000000..b62484cdf3c --- /dev/null +++ b/doc/en/reference/index.rst @@ -0,0 +1,19 @@ +:orphan: + +Reference guides +================ + +.. toctree:: + :maxdepth: 1 + + fixtures + warnings + doctest + cache + unittest + xunit_setup + plugin_list + writing_plugins + logging + customize + reference From 843e9eac2cc235746e525e794d0b69b33dc6a5d5 Mon Sep 17 00:00:00 2001 From: Daniele Procida Date: Fri, 12 Mar 2021 23:14:18 +0000 Subject: [PATCH 173/630] Added myself to AUTHORS --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 0c721a49610..e5477e1f1a5 100644 --- a/AUTHORS +++ b/AUTHORS @@ -78,6 +78,7 @@ Daniel Grana Daniel Hahler Daniel Nuri Daniel Wandschneider +Daniele Procida Danielle Jenkins Daniil Galiev Dave Hunt From 639c4e64a19efe8e79995e363ac819fd2008633d Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sat, 13 Mar 2021 12:44:30 +0200 Subject: [PATCH 174/630] doc: fix broken links in left-hand TOC Due to recent renamings. --- doc/en/_templates/globaltoc.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/en/_templates/globaltoc.html b/doc/en/_templates/globaltoc.html index 4069e59ebd7..edc0e471613 100644 --- a/doc/en/_templates/globaltoc.html +++ b/doc/en/_templates/globaltoc.html @@ -4,9 +4,9 @@

          {{ _('Table Of Contents') }}

        • Home
        • Get started
        • Examples
        • -
        • Customize
        • -
        • API Reference
        • -
        • 3rd party plugins
        • +
        • Customize
        • +
        • API Reference
        • +
        • 3rd party plugins
        • Complete table of contents
        • Changelog
        • Contributing
        • From 5bbfb4e0581f64a572e98044b343b54972e955ce Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sat, 13 Mar 2021 10:54:40 +0200 Subject: [PATCH 175/630] Remove `_` prefix from TypeVars which appear in public API The prefixes make the API Reference docs (for e.g. `pytest.raises`, `pytest.fixture`) uglier. Being under `_pytest` is sufficient from a privacy perspective, so let's drop them. --- src/_pytest/_code/code.py | 18 +++++++------- src/_pytest/fixtures.py | 52 +++++++++++++++++++-------------------- src/_pytest/python_api.py | 26 ++++++++++---------- 3 files changed, 48 insertions(+), 48 deletions(-) diff --git a/src/_pytest/_code/code.py b/src/_pytest/_code/code.py index 331aaabc780..8345c4d7979 100644 --- a/src/_pytest/_code/code.py +++ b/src/_pytest/_code/code.py @@ -436,26 +436,26 @@ def recursionindex(self) -> Optional[int]: ) -_E = TypeVar("_E", bound=BaseException, covariant=True) +E = TypeVar("E", bound=BaseException, covariant=True) @final @attr.s(repr=False) -class ExceptionInfo(Generic[_E]): +class ExceptionInfo(Generic[E]): """Wraps sys.exc_info() objects and offers help for navigating the traceback.""" _assert_start_repr = "AssertionError('assert " - _excinfo = attr.ib(type=Optional[Tuple[Type["_E"], "_E", TracebackType]]) + _excinfo = attr.ib(type=Optional[Tuple[Type["E"], "E", TracebackType]]) _striptext = attr.ib(type=str, default="") _traceback = attr.ib(type=Optional[Traceback], default=None) @classmethod def from_exc_info( cls, - exc_info: Tuple[Type[_E], _E, TracebackType], + exc_info: Tuple[Type[E], E, TracebackType], exprinfo: Optional[str] = None, - ) -> "ExceptionInfo[_E]": + ) -> "ExceptionInfo[E]": """Return an ExceptionInfo for an existing exc_info tuple. .. warning:: @@ -500,17 +500,17 @@ def from_current( return ExceptionInfo.from_exc_info(exc_info, exprinfo) @classmethod - def for_later(cls) -> "ExceptionInfo[_E]": + def for_later(cls) -> "ExceptionInfo[E]": """Return an unfilled ExceptionInfo.""" return cls(None) - def fill_unfilled(self, exc_info: Tuple[Type[_E], _E, TracebackType]) -> None: + def fill_unfilled(self, exc_info: Tuple[Type[E], E, TracebackType]) -> None: """Fill an unfilled ExceptionInfo created with ``for_later()``.""" assert self._excinfo is None, "ExceptionInfo was already filled" self._excinfo = exc_info @property - def type(self) -> Type[_E]: + def type(self) -> Type[E]: """The exception class.""" assert ( self._excinfo is not None @@ -518,7 +518,7 @@ def type(self) -> Type[_E]: return self._excinfo[0] @property - def value(self) -> _E: + def value(self) -> E: """The exception value.""" assert ( self._excinfo is not None diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 722400ff7aa..5ff8ba3caa9 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -79,18 +79,18 @@ # The value of the fixture -- return/yield of the fixture function (type variable). -_FixtureValue = TypeVar("_FixtureValue") +FixtureValue = TypeVar("FixtureValue") # The type of the fixture function (type variable). -_FixtureFunction = TypeVar("_FixtureFunction", bound=Callable[..., object]) +FixtureFunction = TypeVar("FixtureFunction", bound=Callable[..., object]) # The type of a fixture function (type alias generic in fixture value). _FixtureFunc = Union[ - Callable[..., _FixtureValue], Callable[..., Generator[_FixtureValue, None, None]] + Callable[..., FixtureValue], Callable[..., Generator[FixtureValue, None, None]] ] # The type of FixtureDef.cached_result (type alias generic in fixture value). _FixtureCachedResult = Union[ Tuple[ # The result. - _FixtureValue, + FixtureValue, # Cache key. object, None, @@ -106,8 +106,8 @@ @attr.s(frozen=True) -class PseudoFixtureDef(Generic[_FixtureValue]): - cached_result = attr.ib(type="_FixtureCachedResult[_FixtureValue]") +class PseudoFixtureDef(Generic[FixtureValue]): + cached_result = attr.ib(type="_FixtureCachedResult[FixtureValue]") scope = attr.ib(type="_Scope") @@ -928,11 +928,11 @@ def fail_fixturefunc(fixturefunc, msg: str) -> "NoReturn": def call_fixture_func( - fixturefunc: "_FixtureFunc[_FixtureValue]", request: FixtureRequest, kwargs -) -> _FixtureValue: + fixturefunc: "_FixtureFunc[FixtureValue]", request: FixtureRequest, kwargs +) -> FixtureValue: if is_generator(fixturefunc): fixturefunc = cast( - Callable[..., Generator[_FixtureValue, None, None]], fixturefunc + Callable[..., Generator[FixtureValue, None, None]], fixturefunc ) generator = fixturefunc(**kwargs) try: @@ -942,7 +942,7 @@ def call_fixture_func( finalizer = functools.partial(_teardown_yield_fixture, fixturefunc, generator) request.addfinalizer(finalizer) else: - fixturefunc = cast(Callable[..., _FixtureValue], fixturefunc) + fixturefunc = cast(Callable[..., FixtureValue], fixturefunc) fixture_result = fixturefunc(**kwargs) return fixture_result @@ -985,7 +985,7 @@ def _eval_scope_callable( @final -class FixtureDef(Generic[_FixtureValue]): +class FixtureDef(Generic[FixtureValue]): """A container for a factory definition.""" def __init__( @@ -993,7 +993,7 @@ def __init__( fixturemanager: "FixtureManager", baseid: Optional[str], argname: str, - func: "_FixtureFunc[_FixtureValue]", + func: "_FixtureFunc[FixtureValue]", scope: "Union[_Scope, Callable[[str, Config], _Scope]]", params: Optional[Sequence[object]], unittest: bool = False, @@ -1026,7 +1026,7 @@ def __init__( ) self.unittest = unittest self.ids = ids - self.cached_result: Optional[_FixtureCachedResult[_FixtureValue]] = None + self.cached_result: Optional[_FixtureCachedResult[FixtureValue]] = None self._finalizers: List[Callable[[], object]] = [] def addfinalizer(self, finalizer: Callable[[], object]) -> None: @@ -1055,7 +1055,7 @@ def finish(self, request: SubRequest) -> None: self.cached_result = None self._finalizers = [] - def execute(self, request: SubRequest) -> _FixtureValue: + def execute(self, request: SubRequest) -> FixtureValue: # Get required arguments and register our own finish() # with their finalization. for argname in self.argnames: @@ -1096,8 +1096,8 @@ def __repr__(self) -> str: def resolve_fixture_function( - fixturedef: FixtureDef[_FixtureValue], request: FixtureRequest -) -> "_FixtureFunc[_FixtureValue]": + fixturedef: FixtureDef[FixtureValue], request: FixtureRequest +) -> "_FixtureFunc[FixtureValue]": """Get the actual callable that can be called to obtain the fixture value, dealing with unittest-specific instances and bound methods.""" fixturefunc = fixturedef.func @@ -1123,8 +1123,8 @@ def resolve_fixture_function( def pytest_fixture_setup( - fixturedef: FixtureDef[_FixtureValue], request: SubRequest -) -> _FixtureValue: + fixturedef: FixtureDef[FixtureValue], request: SubRequest +) -> FixtureValue: """Execution of fixture setup.""" kwargs = {} for argname in fixturedef.argnames: @@ -1174,9 +1174,9 @@ def _params_converter( def wrap_function_to_error_out_if_called_directly( - function: _FixtureFunction, + function: FixtureFunction, fixture_marker: "FixtureFunctionMarker", -) -> _FixtureFunction: +) -> FixtureFunction: """Wrap the given fixture function so we can raise an error about it being called directly, instead of used as an argument in a test function.""" message = ( @@ -1194,7 +1194,7 @@ def result(*args, **kwargs): # further than this point and lose useful wrappings like @mock.patch (#3774). result.__pytest_wrapped__ = _PytestWrapper(function) # type: ignore[attr-defined] - return cast(_FixtureFunction, result) + return cast(FixtureFunction, result) @final @@ -1213,7 +1213,7 @@ class FixtureFunctionMarker: ) name = attr.ib(type=Optional[str], default=None) - def __call__(self, function: _FixtureFunction) -> _FixtureFunction: + def __call__(self, function: FixtureFunction) -> FixtureFunction: if inspect.isclass(function): raise ValueError("class fixtures not supported (maybe in the future)") @@ -1241,7 +1241,7 @@ def __call__(self, function: _FixtureFunction) -> _FixtureFunction: @overload def fixture( - fixture_function: _FixtureFunction, + fixture_function: FixtureFunction, *, scope: "Union[_Scope, Callable[[str, Config], _Scope]]" = ..., params: Optional[Iterable[object]] = ..., @@ -1253,7 +1253,7 @@ def fixture( ] ] = ..., name: Optional[str] = ..., -) -> _FixtureFunction: +) -> FixtureFunction: ... @@ -1276,7 +1276,7 @@ def fixture( def fixture( - fixture_function: Optional[_FixtureFunction] = None, + fixture_function: Optional[FixtureFunction] = None, *, scope: "Union[_Scope, Callable[[str, Config], _Scope]]" = "function", params: Optional[Iterable[object]] = None, @@ -1288,7 +1288,7 @@ def fixture( ] ] = None, name: Optional[str] = None, -) -> Union[FixtureFunctionMarker, _FixtureFunction]: +) -> Union[FixtureFunctionMarker, FixtureFunction]: """Decorator to mark a fixture factory function. This decorator can be used, with or without parameters, to define a diff --git a/src/_pytest/python_api.py b/src/_pytest/python_api.py index 7e0c86479d4..426c7e18453 100644 --- a/src/_pytest/python_api.py +++ b/src/_pytest/python_api.py @@ -571,31 +571,31 @@ def _as_numpy_array(obj: object) -> Optional["ndarray"]: # builtin pytest.raises helper -_E = TypeVar("_E", bound=BaseException) +E = TypeVar("E", bound=BaseException) @overload def raises( - expected_exception: Union[Type[_E], Tuple[Type[_E], ...]], + expected_exception: Union[Type[E], Tuple[Type[E], ...]], *, match: Optional[Union[str, Pattern[str]]] = ..., -) -> "RaisesContext[_E]": +) -> "RaisesContext[E]": ... @overload def raises( - expected_exception: Union[Type[_E], Tuple[Type[_E], ...]], + expected_exception: Union[Type[E], Tuple[Type[E], ...]], func: Callable[..., Any], *args: Any, **kwargs: Any, -) -> _pytest._code.ExceptionInfo[_E]: +) -> _pytest._code.ExceptionInfo[E]: ... def raises( - expected_exception: Union[Type[_E], Tuple[Type[_E], ...]], *args: Any, **kwargs: Any -) -> Union["RaisesContext[_E]", _pytest._code.ExceptionInfo[_E]]: + expected_exception: Union[Type[E], Tuple[Type[E], ...]], *args: Any, **kwargs: Any +) -> Union["RaisesContext[E]", _pytest._code.ExceptionInfo[E]]: r"""Assert that a code block/function call raises ``expected_exception`` or raise a failure exception otherwise. @@ -709,7 +709,7 @@ def raises( __tracebackhide__ = True if isinstance(expected_exception, type): - excepted_exceptions: Tuple[Type[_E], ...] = (expected_exception,) + excepted_exceptions: Tuple[Type[E], ...] = (expected_exception,) else: excepted_exceptions = expected_exception for exc in excepted_exceptions: @@ -750,19 +750,19 @@ def raises( @final -class RaisesContext(Generic[_E]): +class RaisesContext(Generic[E]): def __init__( self, - expected_exception: Union[Type[_E], Tuple[Type[_E], ...]], + expected_exception: Union[Type[E], Tuple[Type[E], ...]], message: str, match_expr: Optional[Union[str, Pattern[str]]] = None, ) -> None: self.expected_exception = expected_exception self.message = message self.match_expr = match_expr - self.excinfo: Optional[_pytest._code.ExceptionInfo[_E]] = None + self.excinfo: Optional[_pytest._code.ExceptionInfo[E]] = None - def __enter__(self) -> _pytest._code.ExceptionInfo[_E]: + def __enter__(self) -> _pytest._code.ExceptionInfo[E]: self.excinfo = _pytest._code.ExceptionInfo.for_later() return self.excinfo @@ -779,7 +779,7 @@ def __exit__( if not issubclass(exc_type, self.expected_exception): return False # Cast to narrow the exception type now that it's verified. - exc_info = cast(Tuple[Type[_E], _E, TracebackType], (exc_type, exc_val, exc_tb)) + exc_info = cast(Tuple[Type[E], E, TracebackType], (exc_type, exc_val, exc_tb)) self.excinfo.fill_unfilled(exc_info) if self.match_expr is not None: self.excinfo.match(self.match_expr) From 96ef7d678b43d6f35624def3a3a610efe5f731df Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sat, 13 Mar 2021 11:11:41 +0200 Subject: [PATCH 176/630] code: fix outdated reference in ExceptionInfo.exconly docstring No such thing as `_pytest._code.AssertionError`, it's just the built-in `AssertionError`. --- src/_pytest/_code/code.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/_pytest/_code/code.py b/src/_pytest/_code/code.py index 8345c4d7979..610f4e68d3a 100644 --- a/src/_pytest/_code/code.py +++ b/src/_pytest/_code/code.py @@ -562,10 +562,10 @@ def __repr__(self) -> str: def exconly(self, tryshort: bool = False) -> str: """Return the exception as a string. - When 'tryshort' resolves to True, and the exception is a - _pytest._code._AssertionError, only the actual exception part of - the exception representation is returned (so 'AssertionError: ' is - removed from the beginning). + When 'tryshort' resolves to True, and the exception is an + AssertionError, only the actual exception part of the exception + representation is returned (so 'AssertionError: ' is removed from + the beginning). """ lines = format_exception_only(self.type, self.value) text = "".join(lines) From f2d65c85f4e5761b0e763b04932df600aec5a480 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sat, 13 Mar 2021 11:10:34 +0200 Subject: [PATCH 177/630] code: export ExceptionInfo for typing purposes This type is most prominent in `pytest.raises` and we should allow to refer to it by a public name. The type is not in a perfectly "exposable" state. In particular: - The `traceback` property with type `Traceback` which is derived from the `py.code` API and exposes a bunch more types transitively. This stuff is *not* exported and probably won't be. - The `getrepr` method which probably should be private. But they're already used in the wild so no point in just hiding them now. The __init__ API is hidden -- the public API for this are the `from_*` classmethods. --- changelog/7469.deprecation.rst | 1 + changelog/7469.feature.rst | 5 +++-- doc/en/how-to/assert.rst | 2 +- doc/en/reference/reference.rst | 2 +- src/_pytest/_code/code.py | 28 +++++++++++++++++++++------- src/_pytest/config/__init__.py | 2 +- src/_pytest/doctest.py | 2 +- src/_pytest/nodes.py | 2 +- src/_pytest/unittest.py | 2 +- src/pytest/__init__.py | 2 ++ testing/test_unittest.py | 26 +++++++++++++++++--------- 11 files changed, 50 insertions(+), 24 deletions(-) diff --git a/changelog/7469.deprecation.rst b/changelog/7469.deprecation.rst index 0d7908ef8ac..e01764caa91 100644 --- a/changelog/7469.deprecation.rst +++ b/changelog/7469.deprecation.rst @@ -5,5 +5,6 @@ Directly constructing the following classes is now deprecated: - ``_pytest.mark.structures.MarkGenerator`` - ``_pytest.python.Metafunc`` - ``_pytest.runner.CallInfo`` +- ``_pytest._code.ExceptionInfo`` These have always been considered private, but now issue a deprecation warning, which may become a hard error in pytest 7.0.0. diff --git a/changelog/7469.feature.rst b/changelog/7469.feature.rst index f9948d686f9..ea8df523952 100644 --- a/changelog/7469.feature.rst +++ b/changelog/7469.feature.rst @@ -5,8 +5,9 @@ The newly-exported types are: - ``pytest.Mark`` for :class:`marks `. - ``pytest.MarkDecorator`` for :class:`mark decorators `. - ``pytest.MarkGenerator`` for the :class:`pytest.mark ` singleton. -- ``pytest.Metafunc`` for the :class:`metafunc ` argument to the `pytest_generate_tests ` hook. -- ``pytest.runner.CallInfo`` for the :class:`CallInfo ` type passed to various hooks. +- ``pytest.Metafunc`` for the :class:`metafunc ` argument to the :func:`pytest_generate_tests ` hook. +- ``pytest.CallInfo`` for the :class:`CallInfo ` type passed to various hooks. +- ``pytest.ExceptionInfo`` for the :class:`ExceptionInfo ` type returned from :func:`pytest.raises` and passed to various hooks. Constructing them directly is not supported; they are only meant for use in type annotations. Doing so will emit a deprecation warning, and may become a hard-error in pytest 7.0. diff --git a/doc/en/how-to/assert.rst b/doc/en/how-to/assert.rst index c0314f34404..34b765b3b98 100644 --- a/doc/en/how-to/assert.rst +++ b/doc/en/how-to/assert.rst @@ -98,7 +98,7 @@ and if you need to have access to the actual exception info you may use: f() assert "maximum recursion" in str(excinfo.value) -``excinfo`` is an ``ExceptionInfo`` instance, which is a wrapper around +``excinfo`` is an :class:`~pytest.ExceptionInfo` instance, which is a wrapper around the actual exception raised. The main attributes of interest are ``.type``, ``.value`` and ``.traceback``. diff --git a/doc/en/reference/reference.rst b/doc/en/reference/reference.rst index d18150bac0b..d9986336ce8 100644 --- a/doc/en/reference/reference.rst +++ b/doc/en/reference/reference.rst @@ -793,7 +793,7 @@ Config ExceptionInfo ~~~~~~~~~~~~~ -.. autoclass:: _pytest._code.ExceptionInfo +.. autoclass:: pytest.ExceptionInfo() :members: diff --git a/src/_pytest/_code/code.py b/src/_pytest/_code/code.py index 610f4e68d3a..1b4760a0c87 100644 --- a/src/_pytest/_code/code.py +++ b/src/_pytest/_code/code.py @@ -42,6 +42,7 @@ from _pytest._io.saferepr import saferepr from _pytest.compat import final from _pytest.compat import get_real_func +from _pytest.deprecated import check_ispytest from _pytest.pathlib import absolutepath from _pytest.pathlib import bestrelpath @@ -440,15 +441,28 @@ def recursionindex(self) -> Optional[int]: @final -@attr.s(repr=False) +@attr.s(repr=False, init=False) class ExceptionInfo(Generic[E]): """Wraps sys.exc_info() objects and offers help for navigating the traceback.""" _assert_start_repr = "AssertionError('assert " _excinfo = attr.ib(type=Optional[Tuple[Type["E"], "E", TracebackType]]) - _striptext = attr.ib(type=str, default="") - _traceback = attr.ib(type=Optional[Traceback], default=None) + _striptext = attr.ib(type=str) + _traceback = attr.ib(type=Optional[Traceback]) + + def __init__( + self, + excinfo: Optional[Tuple[Type["E"], "E", TracebackType]], + striptext: str = "", + traceback: Optional[Traceback] = None, + *, + _ispytest: bool = False, + ) -> None: + check_ispytest(_ispytest) + self._excinfo = excinfo + self._striptext = striptext + self._traceback = traceback @classmethod def from_exc_info( @@ -475,7 +489,7 @@ def from_exc_info( if exprinfo and exprinfo.startswith(cls._assert_start_repr): _striptext = "AssertionError: " - return cls(exc_info, _striptext) + return cls(exc_info, _striptext, _ispytest=True) @classmethod def from_current( @@ -502,7 +516,7 @@ def from_current( @classmethod def for_later(cls) -> "ExceptionInfo[E]": """Return an unfilled ExceptionInfo.""" - return cls(None) + return cls(None, _ispytest=True) def fill_unfilled(self, exc_info: Tuple[Type[E], E, TracebackType]) -> None: """Fill an unfilled ExceptionInfo created with ``for_later()``.""" @@ -922,7 +936,7 @@ def repr_excinfo( if e.__cause__ is not None and self.chain: e = e.__cause__ excinfo_ = ( - ExceptionInfo((type(e), e, e.__traceback__)) + ExceptionInfo.from_exc_info((type(e), e, e.__traceback__)) if e.__traceback__ else None ) @@ -932,7 +946,7 @@ def repr_excinfo( ): e = e.__context__ excinfo_ = ( - ExceptionInfo((type(e), e, e.__traceback__)) + ExceptionInfo.from_exc_info((type(e), e, e.__traceback__)) if e.__traceback__ else None ) diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 3f138efa750..144f1c9d112 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -145,7 +145,7 @@ def main( try: config = _prepareconfig(args, plugins) except ConftestImportFailure as e: - exc_info = ExceptionInfo(e.excinfo) + exc_info = ExceptionInfo.from_exc_info(e.excinfo) tw = TerminalWriter(sys.stderr) tw.line(f"ImportError while loading conftest '{e.path}'.", red=True) exc_info.traceback = exc_info.traceback.filter( diff --git a/src/_pytest/doctest.py b/src/_pytest/doctest.py index 41d295daaba..b8e46297a81 100644 --- a/src/_pytest/doctest.py +++ b/src/_pytest/doctest.py @@ -365,7 +365,7 @@ def repr_failure( # type: ignore[override] example, failure.got, report_choice ).split("\n") else: - inner_excinfo = ExceptionInfo(failure.exc_info) + inner_excinfo = ExceptionInfo.from_exc_info(failure.exc_info) lines += ["UNEXPECTED EXCEPTION: %s" % repr(inner_excinfo.value)] lines += [ x.strip("\n") diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py index 9d93659e1ad..0e23c733060 100644 --- a/src/_pytest/nodes.py +++ b/src/_pytest/nodes.py @@ -396,7 +396,7 @@ def _repr_failure_py( from _pytest.fixtures import FixtureLookupError if isinstance(excinfo.value, ConftestImportFailure): - excinfo = ExceptionInfo(excinfo.value.excinfo) + excinfo = ExceptionInfo.from_exc_info(excinfo.value.excinfo) if isinstance(excinfo.value, fail.Exception): if not excinfo.value.pytrace: style = "value" diff --git a/src/_pytest/unittest.py b/src/_pytest/unittest.py index 3f88d7a9e2c..17fccc26867 100644 --- a/src/_pytest/unittest.py +++ b/src/_pytest/unittest.py @@ -209,7 +209,7 @@ def _addexcinfo(self, rawexcinfo: "_SysExcInfoType") -> None: # Unwrap potential exception info (see twisted trial support below). rawexcinfo = getattr(rawexcinfo, "_rawexcinfo", rawexcinfo) try: - excinfo = _pytest._code.ExceptionInfo(rawexcinfo) # type: ignore[arg-type] + excinfo = _pytest._code.ExceptionInfo[BaseException].from_exc_info(rawexcinfo) # type: ignore[arg-type] # Invoke the attributes to trigger storing the traceback # trial causes some issue there. excinfo.value diff --git a/src/pytest/__init__.py b/src/pytest/__init__.py index 53917340fd7..02a82386d08 100644 --- a/src/pytest/__init__.py +++ b/src/pytest/__init__.py @@ -2,6 +2,7 @@ """pytest: unit and functional testing with Python.""" from . import collect from _pytest import __version__ +from _pytest._code import ExceptionInfo from _pytest.assertion import register_assert_rewrite from _pytest.cacheprovider import Cache from _pytest.capture import CaptureFixture @@ -79,6 +80,7 @@ "console_main", "deprecated_call", "exit", + "ExceptionInfo", "ExitCode", "fail", "File", diff --git a/testing/test_unittest.py b/testing/test_unittest.py index d7f7737153d..fd4c01d800d 100644 --- a/testing/test_unittest.py +++ b/testing/test_unittest.py @@ -377,24 +377,32 @@ def test_hello(self): def test_testcase_custom_exception_info(pytester: Pytester, type: str) -> None: pytester.makepyfile( """ + from typing import Generic, TypeVar from unittest import TestCase - import py, pytest - import _pytest._code + import pytest, _pytest._code + class MyTestCase(TestCase): def run(self, result): excinfo = pytest.raises(ZeroDivisionError, lambda: 0/0) - # we fake an incompatible exception info - from _pytest.monkeypatch import MonkeyPatch - mp = MonkeyPatch() - def t(*args): - mp.undo() - raise TypeError() - mp.setattr(_pytest._code, 'ExceptionInfo', t) + # We fake an incompatible exception info. + class FakeExceptionInfo(Generic[TypeVar("E")]): + def __init__(self, *args, **kwargs): + mp.undo() + raise TypeError() + @classmethod + def from_current(cls): + return cls() + @classmethod + def from_exc_info(cls, *args, **kwargs): + return cls() + mp = pytest.MonkeyPatch() + mp.setattr(_pytest._code, 'ExceptionInfo', FakeExceptionInfo) try: excinfo = excinfo._excinfo result.add%(type)s(self, excinfo) finally: mp.undo() + def test_hello(self): pass """ From af9f27a874be2a86621a58a5de6c7cecbbb67168 Mon Sep 17 00:00:00 2001 From: Pierre Mourlanne Date: Sat, 13 Mar 2021 15:01:23 +0100 Subject: [PATCH 178/630] Approx decimal sequence mapping (#8422) --- changelog/8421.feature.rst | 1 + src/_pytest/python_api.py | 2 ++ testing/python/approx.py | 14 ++++++++++++++ 3 files changed, 17 insertions(+) create mode 100644 changelog/8421.feature.rst diff --git a/changelog/8421.feature.rst b/changelog/8421.feature.rst new file mode 100644 index 00000000000..c729ca3950a --- /dev/null +++ b/changelog/8421.feature.rst @@ -0,0 +1 @@ +:func:`pytest.approx` now works on :class:`~decimal.Decimal` within mappings/dicts and sequences/lists. diff --git a/src/_pytest/python_api.py b/src/_pytest/python_api.py index 7e0c86479d4..2285e22a380 100644 --- a/src/_pytest/python_api.py +++ b/src/_pytest/python_api.py @@ -72,6 +72,8 @@ def __ne__(self, actual) -> bool: return not (actual == self) def _approx_scalar(self, x) -> "ApproxScalar": + if isinstance(x, Decimal): + return ApproxDecimal(x, rel=self.rel, abs=self.abs, nan_ok=self.nan_ok) return ApproxScalar(x, rel=self.rel, abs=self.abs, nan_ok=self.nan_ok) def _yield_comparisons(self, actual): diff --git a/testing/python/approx.py b/testing/python/approx.py index db6124e3914..a0a91484298 100644 --- a/testing/python/approx.py +++ b/testing/python/approx.py @@ -313,6 +313,12 @@ def test_list(self): assert approx(expected, rel=5e-7, abs=0) == actual assert approx(expected, rel=5e-8, abs=0) != actual + def test_list_decimal(self): + actual = [Decimal("1.000001"), Decimal("2.000001")] + expected = [Decimal("1"), Decimal("2")] + + assert actual == approx(expected) + def test_list_wrong_len(self): assert [1, 2] != approx([1]) assert [1, 2] != approx([1, 2, 3]) @@ -346,6 +352,14 @@ def test_dict(self): assert approx(expected, rel=5e-7, abs=0) == actual assert approx(expected, rel=5e-8, abs=0) != actual + def test_dict_decimal(self): + actual = {"a": Decimal("1.000001"), "b": Decimal("2.000001")} + # Dictionaries became ordered in python3.6, so switch up the order here + # to make sure it doesn't matter. + expected = {"b": Decimal("2"), "a": Decimal("1")} + + assert actual == approx(expected) + def test_dict_wrong_len(self): assert {"a": 1, "b": 2} != approx({"a": 1}) assert {"a": 1, "b": 2} != approx({"a": 1, "c": 2}) From 8c350b98375c90afcfd9066e082c66f848ecc886 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sat, 13 Mar 2021 20:30:24 +0200 Subject: [PATCH 179/630] setup.cfg: fix setuptools dash-separator warnings Warnings like this: python3.9/site-packages/setuptools/dist.py:634: UserWarning: Usage of dash-separated 'upload-dir' will not be supported in future versions. Please use the underscore name 'upload_dir' instead --- setup.cfg | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setup.cfg b/setup.cfg index ab6b2fb9379..bf8d2c85040 100644 --- a/setup.cfg +++ b/setup.cfg @@ -77,12 +77,12 @@ _pytest = py.typed pytest = py.typed [build_sphinx] -source-dir = doc/en/ -build-dir = doc/build +source_dir = doc/en/ +build_dir = doc/build all_files = 1 [upload_sphinx] -upload-dir = doc/en/build/html +upload_dir = doc/en/build/html [check-manifest] ignore = From ba7bd3d77cfdd3a0e959fd525dbf6210691fe8d4 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sat, 13 Mar 2021 19:45:11 +0200 Subject: [PATCH 180/630] doc/tmpdir: significantly reduce space dedicated to tmpdir/tmpdir_factory Emphasize tmp_path/tmp_path_factory and just note the legacy ones. --- doc/en/how-to/tmpdir.rst | 98 +++++++--------------------------- doc/en/reference/reference.rst | 8 ++- 2 files changed, 22 insertions(+), 84 deletions(-) diff --git a/doc/en/how-to/tmpdir.rst b/doc/en/how-to/tmpdir.rst index 379c6d84c8e..1823c8a4e1b 100644 --- a/doc/en/how-to/tmpdir.rst +++ b/doc/en/how-to/tmpdir.rst @@ -8,14 +8,11 @@ How to use temporary directories and files in tests The ``tmp_path`` fixture ------------------------ - - - You can use the ``tmp_path`` fixture which will provide a temporary directory unique to the test invocation, created in the `base temporary directory`_. -``tmp_path`` is a ``pathlib.Path`` object. Here is an example test usage: +``tmp_path`` is a :class:`pathlib.Path` object. Here is an example test usage: .. code-block:: python @@ -71,82 +68,12 @@ Running this would result in a passed test except for the last The ``tmp_path_factory`` fixture -------------------------------- - - - The ``tmp_path_factory`` is a session-scoped fixture which can be used to create arbitrary temporary directories from any other fixture or test. -It is intended to replace ``tmpdir_factory``, and returns :class:`pathlib.Path` instances. - -See :ref:`tmp_path_factory API ` for details. - - -The 'tmpdir' fixture --------------------- - -You can use the ``tmpdir`` fixture which will -provide a temporary directory unique to the test invocation, -created in the `base temporary directory`_. - -``tmpdir`` is a `py.path.local`_ object which offers ``os.path`` methods -and more. Here is an example test usage: - -.. code-block:: python - - # content of test_tmpdir.py - def test_create_file(tmpdir): - p = tmpdir.mkdir("sub").join("hello.txt") - p.write("content") - assert p.read() == "content" - assert len(tmpdir.listdir()) == 1 - assert 0 - -Running this would result in a passed test except for the last -``assert 0`` line which we use to look at values: - -.. code-block:: pytest - - $ pytest test_tmpdir.py - =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y - cachedir: $PYTHON_PREFIX/.pytest_cache - rootdir: $REGENDOC_TMPDIR - collected 1 item - - test_tmpdir.py F [100%] - - ================================= FAILURES ================================= - _____________________________ test_create_file _____________________________ - - tmpdir = local('PYTEST_TMPDIR/test_create_file0') - - def test_create_file(tmpdir): - p = tmpdir.mkdir("sub").join("hello.txt") - p.write("content") - assert p.read() == "content" - assert len(tmpdir.listdir()) == 1 - > assert 0 - E assert 0 - - test_tmpdir.py:6: AssertionError - ========================= short test summary info ========================== - FAILED test_tmpdir.py::test_create_file - assert 0 - ============================ 1 failed in 0.12s ============================= - -.. _`tmpdir factory example`: - -The 'tmpdir_factory' fixture ----------------------------- - - - -The ``tmpdir_factory`` is a session-scoped fixture which can be used -to create arbitrary temporary directories from any other fixture or test. - For example, suppose your test suite needs a large image on disk, which is generated procedurally. Instead of computing the same image for each test -that uses it into its own ``tmpdir``, you can generate it once per-session +that uses it into its own ``tmp_path``, you can generate it once per-session to save time: .. code-block:: python @@ -156,10 +83,10 @@ to save time: @pytest.fixture(scope="session") - def image_file(tmpdir_factory): + def image_file(tmp_path_factory): img = compute_expensive_image() - fn = tmpdir_factory.mktemp("data").join("img.png") - img.save(str(fn)) + fn = tmp_path_factory.mktemp("data") / "img.png" + img.save(fn) return fn @@ -168,7 +95,20 @@ to save time: img = load_image(image_file) # compute and test histogram -See :ref:`tmpdir_factory API ` for details. +See :ref:`tmp_path_factory API ` for details. + +.. _`tmpdir and tmpdir_factory`: + +The ``tmpdir`` and ``tmpdir_factory`` fixtures +--------------------------------------------------- + +The ``tmpdir`` and ``tmpdir_factory`` fixtures are similar to ``tmp_path`` +and ``tmp_path_factory``, but use/return legacy `py.path.local`_ objects +rather than standard :class:`pathlib.Path` objects. These days, prefer to +use ``tmp_path`` and ``tmp_path_factory``. + +See :fixture:`tmpdir ` :fixture:`tmpdir_factory ` +API for details. .. _`base temporary directory`: diff --git a/doc/en/reference/reference.rst b/doc/en/reference/reference.rst index d9986336ce8..a8faa044f9b 100644 --- a/doc/en/reference/reference.rst +++ b/doc/en/reference/reference.rst @@ -582,7 +582,7 @@ tmp_path :no-auto-options: -.. fixture:: _pytest.tmpdir.tmp_path_factory +.. fixture:: tmp_path_factory tmp_path_factory ~~~~~~~~~~~~~~~~ @@ -601,7 +601,7 @@ tmp_path_factory tmpdir ~~~~~~ -:ref:`tmpdir` +:ref:`tmpdir and tmpdir_factory` .. autofunction:: _pytest.tmpdir.tmpdir() :no-auto-options: @@ -612,9 +612,7 @@ tmpdir tmpdir_factory ~~~~~~~~~~~~~~ -:ref:`tmpdir factory example` - -.. _`tmpdir factory api`: +:ref:`tmpdir and tmpdir_factory` ``tmp_path_factory`` is an instance of :class:`~pytest.TempdirFactory`: From 493b54e21e689607cdc20dadd0df63860f92907c Mon Sep 17 00:00:00 2001 From: pytest bot Date: Sun, 14 Mar 2021 00:49:15 +0000 Subject: [PATCH 181/630] [automated] Update plugin list --- doc/en/plugin_list.rst | 851 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 851 insertions(+) create mode 100644 doc/en/plugin_list.rst diff --git a/doc/en/plugin_list.rst b/doc/en/plugin_list.rst new file mode 100644 index 00000000000..ca3e8cc73d0 --- /dev/null +++ b/doc/en/plugin_list.rst @@ -0,0 +1,851 @@ +Plugins List +============ + +PyPI projects that match "pytest-\*" are considered plugins and are listed +automatically. Packages classified as inactive are excluded. +This list contains 840 plugins. + +============================================================================================================== ======================================================================================================================================================================== ============== ===================== ============================================ +name summary last release status requires +============================================================================================================== ======================================================================================================================================================================== ============== ===================== ============================================ +`pytest-adaptavist `_ pytest plugin for generating test execution results within Jira Test Management (tm4j) Feb 05, 2020 N/A pytest (>=3.4.1) +`pytest-adf `_ Pytest plugin for writing Azure Data Factory integration tests Mar 10, 2021 4 - Beta pytest (>=3.5.0) +`pytest-adf-azure-identity `_ Pytest plugin for writing Azure Data Factory integration tests Mar 06, 2021 4 - Beta pytest (>=3.5.0) +`pytest-aggreport `_ pytest plugin for pytest-repeat that generate aggregate report of the same test cases with additional statistics details. Mar 07, 2021 4 - Beta pytest (>=6.2.2) +`pytest-aio `_ Pytest plugin for testing async python code Mar 02, 2021 4 - Beta pytest ; extra == 'tests' +`pytest-aiofiles `_ pytest fixtures for writing aiofiles tests with pyfakefs May 14, 2017 5 - Production/Stable N/A +`pytest-aiohttp `_ pytest plugin for aiohttp support Dec 05, 2017 N/A pytest +`pytest-aiohttp-client `_ Pytest `client` fixture for the Aiohttp Nov 01, 2020 N/A pytest (>=6) +`pytest-aioresponses `_ py.test integration for aioresponses Dec 21, 2020 4 - Beta pytest (>=3.5.0) +`pytest-aioworkers `_ A plugin to test aioworkers project with pytest Dec 04, 2019 4 - Beta pytest (>=3.5.0) +`pytest-airflow `_ pytest support for airflow. Apr 03, 2019 3 - Alpha pytest (>=4.4.0) +`pytest-alembic `_ A pytest plugin for verifying alembic migrations. Jul 13, 2020 N/A pytest (>=1.0) +`pytest-allclose `_ Pytest fixture extending Numpy's allclose function Jul 30, 2019 5 - Production/Stable pytest +`pytest-allure-adaptor `_ Plugin for py.test to generate allure xml reports Jan 10, 2018 N/A pytest (>=2.7.3) +`pytest-allure-adaptor2 `_ Plugin for py.test to generate allure xml reports Oct 14, 2020 N/A pytest (>=2.7.3) +`pytest-allure-dsl `_ pytest plugin to test case doc string dls instructions Oct 25, 2020 4 - Beta pytest +`pytest-alphamoon `_ Static code checks used at Alphamoon Nov 20, 2020 4 - Beta pytest (>=3.5.0) +`pytest-android `_ This fixture provides a configured "driver" for Android Automated Testing, using uiautomator2. Feb 21, 2019 3 - Alpha pytest +`pytest-annotate `_ pytest-annotate: Generate PyAnnotate annotations from your pytest tests. Aug 23, 2019 3 - Alpha pytest (<6.0.0,>=3.2.0) +`pytest-ansible `_ Plugin for py.test to simplify calling ansible modules from tests or fixtures Oct 26, 2020 5 - Production/Stable pytest +`pytest-ansible-playbook `_ Pytest fixture which runs given ansible playbook file. Mar 08, 2019 4 - Beta N/A +`pytest-ansible-playbook-runner `_ Pytest fixture which runs given ansible playbook file. Dec 02, 2020 4 - Beta pytest (>=3.1.0) +`pytest-antilru `_ Bust functools.lru_cache when running pytest to avoid test pollution Apr 11, 2019 5 - Production/Stable pytest +`pytest-anything `_ Pytest fixtures to assert anything and something Feb 18, 2021 N/A N/A +`pytest-aoc `_ Downloads puzzle inputs for Advent of Code and synthesizes PyTest fixtures Dec 01, 2020 N/A pytest ; extra == 'dev' +`pytest-apistellar `_ apistellar plugin for pytest. Jun 18, 2019 N/A N/A +`pytest-appengine `_ AppEngine integration that works well with pytest-django Feb 27, 2017 N/A N/A +`pytest-appium `_ Pytest plugin for appium Dec 05, 2019 N/A N/A +`pytest-approvaltests `_ A plugin to use approvaltests with pytest Feb 07, 2021 4 - Beta pytest (>=3.5.0) +`pytest-arraydiff `_ pytest plugin to help with comparing array output from tests Dec 06, 2018 4 - Beta pytest +`pytest-asgi-server `_ Convenient ASGI client/server fixtures for Pytest Dec 12, 2020 N/A pytest (>=5.4.1) +`pytest-asptest `_ test Answer Set Programming programs Apr 28, 2018 4 - Beta N/A +`pytest-assertutil `_ pytest-assertutil May 10, 2019 N/A N/A +`pytest-assert-utils `_ Useful assertion utilities for use with pytest Aug 25, 2020 3 - Alpha N/A +`pytest-assume `_ A pytest plugin that allows multiple failures per test Dec 08, 2020 N/A pytest (>=2.7) +`pytest-ast-back-to-python `_ A plugin for pytest devs to view how assertion rewriting recodes the AST Sep 29, 2019 4 - Beta N/A +`pytest-astropy `_ Meta-package containing dependencies for testing Jan 16, 2020 5 - Production/Stable pytest (>=4.6) +`pytest-astropy-header `_ pytest plugin to add diagnostic information to the header of the test output Dec 18, 2019 3 - Alpha pytest (>=2.8) +`pytest-ast-transformer `_ May 04, 2019 3 - Alpha pytest +`pytest-asyncio `_ Pytest support for asyncio. Jun 23, 2020 4 - Beta pytest (>=5.4.0) +`pytest-asyncio-cooperative `_ Run all your asynchronous tests cooperatively. Jan 03, 2021 4 - Beta N/A +`pytest-asyncio-network-simulator `_ pytest-asyncio-network-simulator: Plugin for pytest for simulator the network in tests Jul 31, 2018 3 - Alpha pytest (<3.7.0,>=3.3.2) +`pytest-async-mongodb `_ pytest plugin for async MongoDB Oct 18, 2017 5 - Production/Stable pytest (>=2.5.2) +`pytest-atomic `_ Skip rest of tests if previous test failed. Nov 24, 2018 4 - Beta N/A +`pytest-attrib `_ pytest plugin to select tests based on attributes similar to the nose-attrib plugin May 24, 2016 4 - Beta N/A +`pytest-austin `_ Austin plugin for pytest Oct 11, 2020 4 - Beta N/A +`pytest-autochecklog `_ automatically check condition and log all the checks Apr 25, 2015 4 - Beta N/A +`pytest-automock `_ Pytest plugin for automatical mocks creation Apr 22, 2020 N/A pytest ; extra == 'dev' +`pytest-auto-parametrize `_ pytest plugin: avoid repeating arguments in parametrize Oct 02, 2016 3 - Alpha N/A +`pytest-avoidance `_ Makes pytest skip tests that don not need rerunning May 23, 2019 4 - Beta pytest (>=3.5.0) +`pytest-aws `_ pytest plugin for testing AWS resource configurations Oct 04, 2017 4 - Beta N/A +`pytest-axe `_ pytest plugin for axe-selenium-python Nov 12, 2018 N/A pytest (>=3.0.0) +`pytest-azurepipelines `_ Formatting PyTest output for Azure Pipelines UI Jul 23, 2020 4 - Beta pytest (>=3.5.0) +`pytest-bandit `_ A bandit plugin for pytest Feb 23, 2021 4 - Beta pytest (>=3.5.0) +`pytest-base-url `_ pytest plugin for URL based testing Jun 19, 2020 5 - Production/Stable pytest (>=2.7.3) +`pytest-bdd `_ BDD for pytest Dec 07, 2020 6 - Mature pytest (>=4.3) +`pytest-bdd-splinter `_ Common steps for pytest bdd and splinter integration Aug 12, 2019 5 - Production/Stable pytest (>=4.0.0) +`pytest-bdd-web `_ A simple plugin to use with pytest Jan 02, 2020 4 - Beta pytest (>=3.5.0) +`pytest-bdd-wrappers `_ Feb 11, 2020 2 - Pre-Alpha N/A +`pytest-beakerlib `_ A pytest plugin that reports test results to the BeakerLib framework Mar 17, 2017 5 - Production/Stable pytest +`pytest-beds `_ Fixtures for testing Google Appengine (GAE) apps Jun 07, 2016 4 - Beta N/A +`pytest-bench `_ Benchmark utility that plugs into pytest. Jul 21, 2014 3 - Alpha N/A +`pytest-benchmark `_ A ``pytest`` fixture for benchmarking code. It will group the tests into rounds that are calibrated to the chosen timer. See calibration and FAQ. Jan 10, 2020 5 - Production/Stable pytest (>=3.8) +`pytest-bigchaindb `_ A BigchainDB plugin for pytest. Jan 10, 2020 4 - Beta N/A +`pytest-black `_ A pytest plugin to enable format checking with black Oct 05, 2020 4 - Beta N/A +`pytest-black-multipy `_ Allow '--black' on older Pythons Jan 14, 2021 5 - Production/Stable pytest (!=3.7.3,>=3.5) ; extra == 'testing' +`pytest-blame `_ A pytest plugin helps developers to debug by providing useful commits history. May 04, 2019 N/A pytest (>=4.4.0) +`pytest-blender `_ Blender Pytest plugin. Feb 15, 2021 N/A pytest (==6.2.1) ; extra == 'dev' +`pytest-blink1 `_ Pytest plugin to emit notifications via the Blink(1) RGB LED Jan 07, 2018 4 - Beta N/A +`pytest-blockage `_ Disable network requests during a test run. Feb 13, 2019 N/A pytest +`pytest-blocker `_ pytest plugin to mark a test as blocker and skip all other tests Sep 07, 2015 4 - Beta N/A +`pytest-board `_ Local continuous test runner with pytest and watchdog. Jan 20, 2019 N/A N/A +`pytest-bpdb `_ A py.test plug-in to enable drop to bpdb debugger on test failure. Jan 19, 2015 2 - Pre-Alpha N/A +`pytest-bravado `_ Pytest-bravado automatically generates from OpenAPI specification client fixtures. Jan 20, 2021 N/A N/A +`pytest-breed-adapter `_ A simple plugin to connect with breed-server Nov 07, 2018 4 - Beta pytest (>=3.5.0) +`pytest-briefcase `_ A pytest plugin for running tests on a Briefcase project. Jun 14, 2020 4 - Beta pytest (>=3.5.0) +`pytest-browser `_ A pytest plugin for console based browser test selection just after the collection phase Dec 10, 2016 3 - Alpha N/A +`pytest-browsermob-proxy `_ BrowserMob proxy plugin for py.test. Jun 11, 2013 4 - Beta N/A +`pytest-browserstack-local `_ ``py.test`` plugin to run ``BrowserStackLocal`` in background. Feb 09, 2018 N/A N/A +`pytest-bug `_ Pytest plugin for marking tests as a bug Jun 02, 2020 5 - Production/Stable pytest (>=3.6.0) +`pytest-bugzilla `_ py.test bugzilla integration plugin May 05, 2010 4 - Beta N/A +`pytest-bugzilla-notifier `_ A plugin that allows you to execute create, update, and read information from BugZilla bugs Jun 15, 2018 4 - Beta pytest (>=2.9.2) +`pytest-buildkite `_ Plugin for pytest that automatically publishes coverage and pytest report annotations to Buildkite. Jul 13, 2019 4 - Beta pytest (>=3.5.0) +`pytest-bwrap `_ Run your tests in Bubblewrap sandboxes Oct 26, 2018 3 - Alpha N/A +`pytest-cache `_ pytest plugin with mechanisms for caching across test runs Jun 04, 2013 3 - Alpha N/A +`pytest-cagoule `_ Pytest plugin to only run tests affected by changes Jan 01, 2020 3 - Alpha N/A +`pytest-camel-collect `_ Enable CamelCase-aware pytest class collection Aug 02, 2020 N/A pytest (>=2.9) +`pytest-canonical-data `_ A plugin which allows to compare results with canonical results, based on previous runs May 08, 2020 2 - Pre-Alpha pytest (>=3.5.0) +`pytest-caprng `_ A plugin that replays pRNG state on failure. May 02, 2018 4 - Beta N/A +`pytest-capture-deprecatedwarnings `_ pytest plugin to capture all deprecatedwarnings and put them in one file Apr 30, 2019 N/A N/A +`pytest-cases `_ Separate test code from test cases in pytest. Feb 19, 2021 5 - Production/Stable N/A +`pytest-cassandra `_ Cassandra CCM Test Fixtures for pytest Nov 04, 2017 1 - Planning N/A +`pytest-catchlog `_ py.test plugin to catch log messages. This is a fork of pytest-capturelog. Jan 24, 2016 4 - Beta pytest (>=2.6) +`pytest-catch-server `_ Pytest plugin with server for catching HTTP requests. Dec 12, 2019 5 - Production/Stable N/A +`pytest-celery `_ pytest-celery a shim pytest plugin to enable celery.contrib.pytest Aug 05, 2020 N/A N/A +`pytest-chalice `_ A set of py.test fixtures for AWS Chalice Jul 01, 2020 4 - Beta N/A +`pytest-change-report `_ turn . into √,turn F into x Sep 14, 2020 N/A pytest +`pytest-chdir `_ A pytest fixture for changing current working directory Jan 28, 2020 N/A pytest (>=5.0.0,<6.0.0) +`pytest-check `_ A pytest plugin that allows multiple failures per test. Dec 27, 2020 5 - Production/Stable N/A +`pytest-checkdocs `_ check the README when running tests Feb 27, 2021 5 - Production/Stable pytest (>=4.6) ; extra == 'testing' +`pytest-checkipdb `_ plugin to check if there are ipdb debugs left Jul 22, 2020 5 - Production/Stable pytest (>=2.9.2) +`pytest-check-links `_ Check links in files Jul 29, 2020 N/A N/A +`pytest-check-mk `_ pytest plugin to test Check_MK checks Nov 19, 2015 4 - Beta pytest +`pytest-circleci `_ py.test plugin for CircleCI May 03, 2019 N/A N/A +`pytest-circleci-parallelized `_ Parallelize pytest across CircleCI workers. Mar 26, 2019 N/A N/A +`pytest-ckan `_ Backport of CKAN 2.9 pytest plugin and fixtures to CAKN 2.8 Apr 28, 2020 4 - Beta pytest +`pytest-clarity `_ A plugin providing an alternative, colourful diff output for failing assertions. Jan 23, 2020 3 - Alpha N/A +`pytest-cldf `_ Easy quality control for CLDF datasets using pytest May 06, 2019 N/A N/A +`pytest-click `_ Py.test plugin for Click Aug 29, 2020 5 - Production/Stable pytest (>=5.0) +`pytest-clld `_ May 06, 2020 N/A pytest (>=3.6) +`pytest-cloud `_ Distributed tests planner plugin for pytest testing framework. Oct 05, 2020 6 - Mature N/A +`pytest-cloudflare-worker `_ pytest plugin for testing cloudflare workers Oct 19, 2020 4 - Beta pytest (>=6.0.0) +`pytest-cobra `_ PyTest plugin for testing Smart Contracts for Ethereum blockchain. Jun 29, 2019 3 - Alpha pytest (<4.0.0,>=3.7.1) +`pytest-codecheckers `_ pytest plugin to add source code sanity checks (pep8 and friends) Feb 13, 2010 N/A N/A +`pytest-codegen `_ Automatically create pytest test signatures Aug 23, 2020 2 - Pre-Alpha N/A +`pytest-codestyle `_ pytest plugin to run pycodestyle Mar 23, 2020 3 - Alpha N/A +`pytest-collect-formatter `_ Formatter for pytest collect output Nov 19, 2020 5 - Production/Stable N/A +`pytest-colordots `_ Colorizes the progress indicators Oct 06, 2017 5 - Production/Stable N/A +`pytest-commander `_ An interactive GUI test runner for PyTest Jan 31, 2021 N/A pytest (>=5.0.0) +`pytest-common-subject `_ pytest framework for testing different aspects of a common method Nov 12, 2020 N/A pytest (>=3.6,<7) +`pytest-concurrent `_ Concurrently execute test cases with multithread, multiprocess and gevent Jan 12, 2019 4 - Beta pytest (>=3.1.1) +`pytest-config `_ Base configurations and utilities for developing your Python project test suite with pytest. Nov 07, 2014 5 - Production/Stable N/A +`pytest-confluence-report `_ Package stands for pytest plugin to upload results into Confluence page. Nov 06, 2020 N/A N/A +`pytest-console-scripts `_ Pytest plugin for testing console scripts Nov 20, 2020 4 - Beta N/A +`pytest-consul `_ pytest plugin with fixtures for testing consul aware apps Nov 24, 2018 3 - Alpha pytest +`pytest-contextfixture `_ Define pytest fixtures as context managers. Mar 12, 2013 4 - Beta N/A +`pytest-contexts `_ A plugin to run tests written with the Contexts framework using pytest Jul 23, 2018 4 - Beta N/A +`pytest-cookies `_ The pytest plugin for your Cookiecutter templates. 🍪 Feb 14, 2020 5 - Production/Stable pytest (<6.0.0,>=3.3.0) +`pytest-couchdbkit `_ py.test extension for per-test couchdb databases using couchdbkit Apr 17, 2012 N/A N/A +`pytest-count `_ count erros and send email Jan 12, 2018 4 - Beta N/A +`pytest-cov `_ Pytest plugin for measuring coverage. Jan 20, 2021 5 - Production/Stable pytest (>=4.6) +`pytest-cover `_ Pytest plugin for measuring coverage. Forked from `pytest-cov`. Aug 01, 2015 5 - Production/Stable N/A +`pytest-coverage `_ Jun 17, 2015 N/A N/A +`pytest-coverage-context `_ Coverage dynamic context support for PyTest, including sub-processes Jan 04, 2021 4 - Beta pytest (>=6.1.0) +`pytest-cov-exclude `_ Pytest plugin for excluding tests based on coverage data Apr 29, 2016 4 - Beta pytest (>=2.8.0,<2.9.0); extra == 'dev' +`pytest-cpp `_ Use pytest's runner to discover and execute C++ tests Dec 10, 2020 4 - Beta pytest (!=5.4.0,!=5.4.1) +`pytest-cram `_ Run cram tests with pytest. Aug 08, 2020 N/A N/A +`pytest-crate `_ Manages CrateDB instances during your integration tests May 28, 2019 3 - Alpha pytest (>=4.0) +`pytest-cricri `_ A Cricri plugin for pytest. Jan 27, 2018 N/A pytest +`pytest-crontab `_ add crontab task in crontab Dec 09, 2019 N/A N/A +`pytest-csv `_ CSV output for pytest. Jun 24, 2019 N/A pytest (>=4.4) +`pytest-curio `_ Pytest support for curio. Oct 07, 2020 N/A N/A +`pytest-curl-report `_ pytest plugin to generate curl command line report Dec 11, 2016 4 - Beta N/A +`pytest-custom-concurrency `_ Custom grouping concurrence for pytest Feb 08, 2021 N/A N/A +`pytest-custom-exit-code `_ Exit pytest test session with custom exit code in different scenarios Aug 07, 2019 4 - Beta pytest (>=4.0.2) +`pytest-custom-nodeid `_ Custom grouping for pytest-xdist, rename test cases name and test cases nodeid, support allure report Mar 07, 2021 N/A N/A +`pytest-custom-report `_ Configure the symbols displayed for test outcomes Jan 30, 2019 N/A pytest +`pytest-custom-scheduling `_ Custom grouping for pytest-xdist, rename test cases name and test cases nodeid, support allure report Mar 01, 2021 N/A N/A +`pytest-cython `_ A plugin for testing Cython extension modules Jan 26, 2021 4 - Beta pytest (>=2.7.3) +`pytest-darker `_ A pytest plugin for checking of modified code using Darker Aug 16, 2020 N/A pytest (>=6.0.1) ; extra == 'test' +`pytest-dash `_ pytest fixtures to run dash applications. Mar 18, 2019 N/A N/A +`pytest-data `_ Useful functions for managing data for pytest fixtures Nov 01, 2016 5 - Production/Stable N/A +`pytest-databricks `_ Pytest plugin for remote Databricks notebooks testing Jul 29, 2020 N/A pytest +`pytest-datadir `_ pytest plugin for test data directories and files Oct 22, 2019 5 - Production/Stable pytest (>=2.7.0) +`pytest-datadir-mgr `_ Manager for test data providing downloads, caching of generated files, and a context for temp directories. Feb 17, 2021 5 - Production/Stable pytest (>=6.0.1,<7.0.0) +`pytest-datadir-ng `_ Fixtures for pytest allowing test functions/methods to easily retrieve test resources from the local filesystem. Dec 25, 2019 5 - Production/Stable pytest +`pytest-data-file `_ Fixture "data" and "case_data" for test from yaml file Dec 04, 2019 N/A N/A +`pytest-datafiles `_ py.test plugin to create a 'tmpdir' containing predefined files/directories. Oct 07, 2018 5 - Production/Stable pytest (>=3.6) +`pytest-datafixtures `_ Data fixtures for pytest made simple Dec 05, 2020 5 - Production/Stable N/A +`pytest-dataplugin `_ A pytest plugin for managing an archive of test data. Sep 16, 2017 1 - Planning N/A +`pytest-datarecorder `_ A py.test plugin recording and comparing test output. Apr 20, 2020 5 - Production/Stable pytest +`pytest-datatest `_ A pytest plugin for test driven data-wrangling (this is the development version of datatest's pytest integration). Oct 15, 2020 4 - Beta pytest (>=3.3) +`pytest-db `_ Session scope fixture "db" for mysql query or change Dec 04, 2019 N/A N/A +`pytest-dbfixtures `_ Databases fixtures plugin for py.test. Dec 07, 2016 4 - Beta N/A +`pytest-dbt-adapter `_ A pytest plugin for testing dbt adapter plugins Jan 07, 2021 N/A pytest (<7,>=6) +`pytest-dbus-notification `_ D-BUS notifications for pytest results. Mar 05, 2014 5 - Production/Stable N/A +`pytest-deadfixtures `_ A simple plugin to list unused fixtures in pytest Jul 23, 2020 5 - Production/Stable N/A +`pytest-dependency `_ Manage dependencies of tests Feb 14, 2020 4 - Beta N/A +`pytest-depends `_ Tests that depend on other tests Apr 05, 2020 5 - Production/Stable pytest (>=3) +`pytest-deprecate `_ Mark tests as testing a deprecated feature with a warning note. Jul 01, 2019 N/A N/A +`pytest-describe `_ Describe-style plugin for pytest Apr 21, 2020 3 - Alpha pytest (>=2.6.0) +`pytest-describe-it `_ plugin for rich text descriptions Jul 19, 2019 4 - Beta pytest +`pytest-devpi-server `_ DevPI server fixture for py.test May 28, 2019 5 - Production/Stable pytest +`pytest-diamond `_ pytest plugin for diamond Aug 31, 2015 4 - Beta N/A +`pytest-dicom `_ pytest plugin to provide DICOM fixtures Dec 19, 2018 3 - Alpha pytest +`pytest-dictsdiff `_ Jul 26, 2019 N/A N/A +`pytest-diff `_ A simple plugin to use with pytest Mar 30, 2019 4 - Beta pytest (>=3.5.0) +`pytest-diffeo `_ Common py.test support for Diffeo packages Apr 08, 2016 3 - Alpha N/A +`pytest-disable `_ pytest plugin to disable a test and skip it from testrun Sep 10, 2015 4 - Beta N/A +`pytest-disable-plugin `_ Disable plugins per test Feb 28, 2019 4 - Beta pytest (>=3.5.0) +`pytest-discord `_ A pytest plugin to notify test results to a Discord channel. Feb 14, 2021 3 - Alpha pytest (!=6.0.0,<7,>=3.3.2) +`pytest-django `_ A Django plugin for pytest. Oct 22, 2020 5 - Production/Stable pytest (>=5.4.0) +`pytest-django-ahead `_ A Django plugin for pytest. Oct 27, 2016 5 - Production/Stable pytest (>=2.9) +`pytest-djangoapp `_ Nice pytest plugin to help you with Django pluggable application testing. Sep 21, 2020 4 - Beta N/A +`pytest-django-cache-xdist `_ A djangocachexdist plugin for pytest May 12, 2020 4 - Beta N/A +`pytest-django-casperjs `_ Integrate CasperJS with your django tests as a pytest fixture. Mar 15, 2015 2 - Pre-Alpha N/A +`pytest-django-dotenv `_ Pytest plugin used to setup environment variables with django-dotenv Nov 26, 2019 4 - Beta pytest (>=2.6.0) +`pytest-django-factories `_ Factories for your Django models that can be used as Pytest fixtures. Nov 12, 2020 4 - Beta N/A +`pytest-django-gcir `_ A Django plugin for pytest. Mar 06, 2018 5 - Production/Stable N/A +`pytest-django-haystack `_ Cleanup your Haystack indexes between tests Sep 03, 2017 5 - Production/Stable pytest (>=2.3.4) +`pytest-django-ifactory `_ A model instance factory for pytest-django Jan 13, 2021 3 - Alpha N/A +`pytest-django-lite `_ The bare minimum to integrate py.test with Django. Jan 30, 2014 N/A N/A +`pytest-django-model `_ A Simple Way to Test your Django Models Feb 14, 2019 4 - Beta N/A +`pytest-django-ordering `_ A pytest plugin for preserving the order in which Django runs tests. Jul 25, 2019 5 - Production/Stable pytest (>=2.3.0) +`pytest-django-queries `_ Generate performance reports from your django database performance tests. Mar 01, 2021 N/A N/A +`pytest-djangorestframework `_ A djangorestframework plugin for pytest Aug 11, 2019 4 - Beta N/A +`pytest-django-rq `_ A pytest plugin to help writing unit test for django-rq Apr 13, 2020 4 - Beta N/A +`pytest-django-sqlcounts `_ py.test plugin for reporting the number of SQLs executed per django testcase. Jun 16, 2015 4 - Beta N/A +`pytest-django-testing-postgresql `_ Use a temporary PostgreSQL database with pytest-django Dec 05, 2019 3 - Alpha N/A +`pytest-doc `_ A documentation plugin for py.test. Jun 28, 2015 5 - Production/Stable N/A +`pytest-docgen `_ An RST Documentation Generator for pytest-based test suites Apr 17, 2020 N/A N/A +`pytest-docker `_ Simple pytest fixtures for Docker and docker-compose based tests Sep 22, 2020 N/A pytest (<7.0,>=4.0) +`pytest-docker-butla `_ Jun 16, 2019 3 - Alpha N/A +`pytest-dockerc `_ Run, manage and stop Docker Compose project from Docker API Oct 09, 2020 5 - Production/Stable pytest (>=3.0) +`pytest-docker-compose `_ Manages Docker containers during your integration tests Jan 26, 2021 5 - Production/Stable pytest (>=3.3) +`pytest-docker-db `_ A plugin to use docker databases for pytests Apr 19, 2020 5 - Production/Stable pytest (>=3.1.1) +`pytest-docker-fixtures `_ pytest docker fixtures Sep 30, 2020 3 - Alpha N/A +`pytest-docker-git-fixtures `_ Pytest fixtures for testing with git scm. Mar 11, 2021 4 - Beta pytest +`pytest-docker-pexpect `_ pytest plugin for writing functional tests with pexpect and docker Jan 14, 2019 N/A pytest +`pytest-docker-postgresql `_ A simple plugin to use with pytest Sep 24, 2019 4 - Beta pytest (>=3.5.0) +`pytest-docker-py `_ Easy to use, simple to extend, pytest plugin that minimally leverages docker-py. Nov 27, 2018 N/A pytest (==4.0.0) +`pytest-docker-registry-fixtures `_ Pytest fixtures for testing with docker registries. Mar 04, 2021 4 - Beta pytest +`pytest-docker-tools `_ Docker integration tests for pytest Mar 02, 2021 4 - Beta pytest (>=6.0.1,<7.0.0) +`pytest-docs `_ Documentation tool for pytest Nov 11, 2018 4 - Beta pytest (>=3.5.0) +`pytest-docstyle `_ pytest plugin to run pydocstyle Mar 23, 2020 3 - Alpha N/A +`pytest-doctest-custom `_ A py.test plugin for customizing string representations of doctest results. Jul 25, 2016 4 - Beta N/A +`pytest-doctest-ellipsis-markers `_ Setup additional values for ELLIPSIS_MARKER for doctests Jan 12, 2018 4 - Beta N/A +`pytest-doctest-import `_ A simple pytest plugin to import names and add them to the doctest namespace. Nov 13, 2018 4 - Beta pytest (>=3.3.0) +`pytest-doctestplus `_ Pytest plugin with advanced doctest features. Jan 15, 2021 3 - Alpha pytest (>=4.6) +`pytest-doctest-ufunc `_ A plugin to run doctests in docstrings of Numpy ufuncs Aug 02, 2020 4 - Beta pytest (>=3.5.0) +`pytest-dolphin `_ Some extra stuff that we use ininternally Nov 30, 2016 4 - Beta pytest (==3.0.4) +`pytest-doorstop `_ A pytest plugin for adding test results into doorstop items. Jun 09, 2020 4 - Beta pytest (>=3.5.0) +`pytest-dotenv `_ A py.test plugin that parses environment files before running tests Jun 16, 2020 4 - Beta pytest (>=5.0.0) +`pytest-drf `_ A Django REST framework plugin for pytest. Nov 12, 2020 5 - Production/Stable pytest (>=3.6) +`pytest-drivings `_ Tool to allow webdriver automation to be ran locally or remotely Jan 13, 2021 N/A N/A +`pytest-drop-dup-tests `_ A Pytest plugin to drop duplicated tests during collection May 23, 2020 4 - Beta pytest (>=2.7) +`pytest-dump2json `_ A pytest plugin for dumping test results to json. Jun 29, 2015 N/A N/A +`pytest-dynamicrerun `_ A pytest plugin to rerun tests dynamically based off of test outcome and output. Aug 15, 2020 4 - Beta N/A +`pytest-dynamodb `_ DynamoDB fixtures for pytest Feb 20, 2020 5 - Production/Stable pytest (>=3.0.0) +`pytest-easy-addoption `_ pytest-easy-addoption: Easy way to work with pytest addoption Jan 22, 2020 N/A N/A +`pytest-easy-api `_ Simple API testing with pytest Mar 26, 2018 N/A N/A +`pytest-easyMPI `_ Package that supports mpi tests in pytest Oct 21, 2020 N/A N/A +`pytest-easyread `_ pytest plugin that makes terminal printouts of the reports easier to read Nov 17, 2017 N/A N/A +`pytest-ec2 `_ Pytest execution on EC2 instance Oct 22, 2019 3 - Alpha N/A +`pytest-echo `_ pytest plugin with mechanisms for echoing environment variables, package version and generic attributes Jan 08, 2020 5 - Production/Stable N/A +`pytest-elasticsearch `_ Elasticsearch process and client fixtures for py.test. Feb 19, 2020 5 - Production/Stable pytest (>=3.0.0) +`pytest-elements `_ Tool to help automate user interfaces Jan 13, 2021 N/A pytest (>=5.4,<6.0) +`pytest-elk-reporter `_ A simple plugin to use with pytest Jan 24, 2021 4 - Beta pytest (>=3.5.0) +`pytest-email `_ Send execution result email Jul 08, 2020 N/A pytest +`pytest-emoji `_ A pytest plugin that adds emojis to your test result report Feb 19, 2019 4 - Beta pytest (>=4.2.1) +`pytest-emoji-output `_ Pytest plugin to represent test output with emoji support Oct 03, 2020 4 - Beta N/A +`pytest-enabler `_ Enable installed pytest plugins Jan 19, 2021 5 - Production/Stable pytest (!=3.7.3,>=3.5) ; extra == 'testing' +`pytest-enhancements `_ Improvements for pytest (rejected upstream) Oct 30, 2019 4 - Beta N/A +`pytest-env `_ py.test plugin that allows you to add environment variables. Jun 16, 2017 4 - Beta N/A +`pytest-envfiles `_ A py.test plugin that parses environment files before running tests Oct 08, 2015 3 - Alpha N/A +`pytest-env-info `_ Push information about the running pytest into envvars Nov 25, 2017 4 - Beta pytest (>=3.1.1) +`pytest-envraw `_ py.test plugin that allows you to add environment variables. Aug 27, 2020 4 - Beta pytest (>=2.6.0) +`pytest-envvars `_ Pytest plugin to validate use of envvars on your tests Jun 13, 2020 5 - Production/Stable pytest (>=3.0.0) +`pytest-env-yaml `_ Apr 02, 2019 N/A N/A +`pytest-eradicate `_ pytest plugin to check for commented out code Sep 08, 2020 N/A pytest (>=2.4.2) +`pytest-error-for-skips `_ Pytest plugin to treat skipped tests a test failure Dec 19, 2019 4 - Beta pytest (>=4.6) +`pytest-eth `_ PyTest plugin for testing Smart Contracts for Ethereum Virtual Machine (EVM). Aug 14, 2020 1 - Planning N/A +`pytest-ethereum `_ pytest-ethereum: Pytest library for ethereum projects. Jun 24, 2019 3 - Alpha pytest (==3.3.2); extra == 'dev' +`pytest-eucalyptus `_ Pytest Plugin for BDD Aug 13, 2019 N/A pytest (>=4.2.0) +`pytest-excel `_ pytest plugin for generating excel reports Oct 06, 2020 5 - Production/Stable N/A +`pytest-exceptional `_ Better exceptions Mar 16, 2017 4 - Beta N/A +`pytest-exception-script `_ Walk your code through exception script to check it's resiliency to failures. Aug 04, 2020 3 - Alpha pytest +`pytest-executable `_ pytest plugin for testing executables Aug 10, 2020 4 - Beta pytest (<6.1,>=4.3) +`pytest-expect `_ py.test plugin to store test expectations and mark tests based on them Apr 21, 2016 4 - Beta N/A +`pytest-expecter `_ Better testing with expecter and pytest. Jul 08, 2020 5 - Production/Stable N/A +`pytest-expectr `_ This plugin is used to expect multiple assert using pytest framework. Oct 05, 2018 N/A pytest (>=2.4.2) +`pytest-exploratory `_ Interactive console for pytest. Jan 20, 2021 N/A pytest (>=5.3) +`pytest-external-blockers `_ a special outcome for tests that are blocked for external reasons Oct 04, 2016 N/A N/A +`pytest-extra-durations `_ A pytest plugin to get durations on a per-function basis and per module basis. Apr 21, 2020 4 - Beta pytest (>=3.5.0) +`pytest-fabric `_ Provides test utilities to run fabric task tests by using docker containers Sep 12, 2018 5 - Production/Stable N/A +`pytest-factory `_ Use factories for test setup with py.test Sep 06, 2020 3 - Alpha pytest (>4.3) +`pytest-factoryboy `_ Factory Boy support for pytest. Dec 30, 2020 6 - Mature pytest (>=4.6) +`pytest-factoryboy-fixtures `_ Generates pytest fixtures that allow the use of type hinting Jun 25, 2020 N/A N/A +`pytest-factoryboy-state `_ Simple factoryboy random state management Dec 11, 2020 4 - Beta pytest (>=5.0) +`pytest-failed-screenshot `_ Test case fails,take a screenshot,save it,attach it to the allure Feb 28, 2021 N/A N/A +`pytest-failed-to-verify `_ A pytest plugin that helps better distinguishing real test failures from setup flakiness. Aug 08, 2019 5 - Production/Stable pytest (>=4.1.0) +`pytest-faker `_ Faker integration with the pytest framework. Dec 19, 2016 6 - Mature N/A +`pytest-falcon `_ Pytest helpers for Falcon. Sep 07, 2016 4 - Beta N/A +`pytest-falcon-client `_ Pytest `client` fixture for the Falcon Framework Mar 19, 2019 N/A N/A +`pytest-fantasy `_ Pytest plugin for Flask Fantasy Framework Mar 14, 2019 N/A N/A +`pytest-fastapi `_ Dec 27, 2020 N/A N/A +`pytest-fastest `_ Use SCM and coverage to run only needed tests Mar 05, 2020 N/A N/A +`pytest-faulthandler `_ py.test plugin that activates the fault handler module for tests (dummy package) Jul 04, 2019 6 - Mature pytest (>=5.0) +`pytest-fauxfactory `_ Integration of fauxfactory into pytest. Dec 06, 2017 5 - Production/Stable pytest (>=3.2) +`pytest-figleaf `_ py.test figleaf coverage plugin Jan 18, 2010 5 - Production/Stable N/A +`pytest-filedata `_ easily load data from files Jan 17, 2019 4 - Beta N/A +`pytest-filemarker `_ A pytest plugin that runs marked tests when files change. Dec 01, 2020 N/A pytest +`pytest-filter-case `_ run test cases filter by mark Nov 05, 2020 N/A N/A +`pytest-filter-subpackage `_ Pytest plugin for filtering based on sub-packages Jan 09, 2020 3 - Alpha pytest (>=3.0) +`pytest-finer-verdicts `_ A pytest plugin to treat non-assertion failures as test errors. Jun 18, 2020 N/A pytest (>=5.4.3) +`pytest-firefox `_ pytest plugin to manipulate firefox Aug 08, 2017 3 - Alpha pytest (>=3.0.2) +`pytest-fixture-config `_ Fixture configuration utils for py.test May 28, 2019 5 - Production/Stable pytest +`pytest-fixture-marker `_ A pytest plugin to add markers based on fixtures used. Oct 11, 2020 5 - Production/Stable N/A +`pytest-fixture-order `_ pytest plugin to control fixture evaluation order Aug 25, 2020 N/A pytest (>=3.0) +`pytest-fixtures `_ Common fixtures for pytest May 01, 2019 5 - Production/Stable N/A +`pytest-fixture-tools `_ Plugin for pytest which provides tools for fixtures Aug 18, 2020 6 - Mature pytest +`pytest-flake8 `_ pytest plugin to check FLAKE8 requirements Dec 16, 2020 4 - Beta pytest (>=3.5) +`pytest-flake8dir `_ A pytest fixture for testing flake8 plugins. Dec 13, 2020 5 - Production/Stable pytest +`pytest-flakefinder `_ Runs tests multiple times to expose flakiness. Jul 28, 2020 4 - Beta pytest (>=2.7.1) +`pytest-flakes `_ pytest plugin to check source code with pyflakes Nov 28, 2020 5 - Production/Stable N/A +`pytest-flaptastic `_ Flaptastic py.test plugin Mar 17, 2019 N/A N/A +`pytest-flask `_ A set of py.test fixtures to test Flask applications. Feb 27, 2021 5 - Production/Stable pytest (>=5.2) +`pytest-flask-sqlalchemy `_ A pytest plugin for preserving test isolation in Flask-SQlAlchemy using database transactions. Apr 04, 2019 4 - Beta pytest (>=3.2.1) +`pytest-flask-sqlalchemy-transactions `_ Run tests in transactions using pytest, Flask, and SQLalchemy. Aug 02, 2018 4 - Beta pytest (>=3.2.1) +`pytest-focus `_ A pytest plugin that alerts user of failed test cases with screen notifications May 04, 2019 4 - Beta pytest +`pytest-forcefail `_ py.test plugin to make the test failing regardless of pytest.mark.xfail May 15, 2018 4 - Beta N/A +`pytest-forward-compatability `_ A name to avoid typosquating pytest-foward-compatibility Sep 06, 2020 N/A N/A +`pytest-forward-compatibility `_ A pytest plugin to shim pytest commandline options for fowards compatibility Sep 29, 2020 N/A N/A +`pytest-freezegun `_ Wrap tests with fixtures in freeze_time Jul 19, 2020 4 - Beta pytest (>=3.0.0) +`pytest-freeze-reqs `_ Check if requirement files are frozen Nov 14, 2019 N/A N/A +`pytest-func-cov `_ Pytest plugin for measuring function coverage May 24, 2020 3 - Alpha pytest (>=5) +`pytest-funparam `_ An alternative way to parametrize test cases Feb 13, 2021 4 - Beta pytest (>=4.6.0) +`pytest-fxa `_ pytest plugin for Firefox Accounts Aug 28, 2018 5 - Production/Stable N/A +`pytest-fxtest `_ Oct 27, 2020 N/A N/A +`pytest-gc `_ The garbage collector plugin for py.test Feb 01, 2018 N/A N/A +`pytest-gcov `_ Uses gcov to measure test coverage of a C library Feb 01, 2018 3 - Alpha N/A +`pytest-gevent `_ Ensure that gevent is properly patched when invoking pytest Feb 25, 2020 N/A pytest +`pytest-gherkin `_ A flexible framework for executing BDD gherkin tests Jul 27, 2019 3 - Alpha pytest (>=5.0.0) +`pytest-ghostinspector `_ For finding/executing Ghost Inspector tests May 17, 2016 3 - Alpha N/A +`pytest-girder `_ A set of pytest fixtures for testing Girder applications. Mar 12, 2021 N/A N/A +`pytest-git `_ Git repository fixture for py.test May 28, 2019 5 - Production/Stable pytest +`pytest-gitcov `_ Pytest plugin for reporting on coverage of the last git commit. Jan 11, 2020 2 - Pre-Alpha N/A +`pytest-git-fixtures `_ Pytest fixtures for testing with git. Mar 11, 2021 4 - Beta pytest +`pytest-github `_ Plugin for py.test that associates tests with github issues using a marker. Mar 07, 2019 5 - Production/Stable N/A +`pytest-github-actions-annotate-failures `_ pytest plugin to annotate failed tests with a workflow command for GitHub Actions Oct 13, 2020 N/A pytest (>=4.0.0) +`pytest-gitignore `_ py.test plugin to ignore the same files as git Jul 17, 2015 4 - Beta N/A +`pytest-gnupg-fixtures `_ Pytest fixtures for testing with gnupg. Mar 04, 2021 4 - Beta pytest +`pytest-golden `_ Plugin for pytest that offloads expected outputs to data files Nov 23, 2020 N/A pytest (>=6.1.2,<7.0.0) +`pytest-graphql-schema `_ Get graphql schema as fixture for pytest Oct 18, 2019 N/A N/A +`pytest-greendots `_ Green progress dots Feb 08, 2014 3 - Alpha N/A +`pytest-growl `_ Growl notifications for pytest results. Jan 13, 2014 5 - Production/Stable N/A +`pytest-grpc `_ pytest plugin for grpc May 01, 2020 N/A pytest (>=3.6.0) +`pytest-hammertime `_ Display "🔨 " instead of "." for passed pytest tests. Jul 28, 2018 N/A pytest +`pytest-harvest `_ Store data created during your pytest tests execution, and retrieve it at the end of the session, e.g. for applicative benchmarking purposes. Dec 08, 2020 5 - Production/Stable N/A +`pytest-helm-chart `_ A plugin to provide different types and configs of Kubernetes clusters that can be used for testing. Jun 15, 2020 4 - Beta pytest (>=5.4.2,<6.0.0) +`pytest-helm-charts `_ A plugin to provide different types and configs of Kubernetes clusters that can be used for testing. Dec 22, 2020 4 - Beta pytest (>=6.1.2,<7.0.0) +`pytest-helper `_ Functions to help in using the pytest testing framework May 31, 2019 5 - Production/Stable N/A +`pytest-helpers `_ pytest helpers May 17, 2020 N/A pytest +`pytest-helpers-namespace `_ PyTest Helpers Namespace Jan 07, 2019 5 - Production/Stable pytest (>=2.9.1) +`pytest-hidecaptured `_ Hide captured output May 04, 2018 4 - Beta pytest (>=2.8.5) +`pytest-historic `_ Custom report to display pytest historical execution records Apr 08, 2020 N/A pytest +`pytest-historic-hook `_ Custom listener to store execution results into MYSQL DB, which is used for pytest-historic report Apr 08, 2020 N/A pytest +`pytest-homeassistant `_ A pytest plugin for use with homeassistant custom components. Aug 12, 2020 4 - Beta N/A +`pytest-homeassistant-custom-component `_ Experimental package to automatically extract test plugins for Home Assistant custom components Mar 03, 2021 3 - Alpha pytest (==6.2.2) +`pytest-honors `_ Report on tests that honor constraints, and guard against regressions Mar 06, 2020 4 - Beta N/A +`pytest-hoverfly `_ Simplify working with Hoverfly from pytest Mar 04, 2021 N/A pytest (>=5.0) +`pytest-hoverfly-wrapper `_ Integrates the Hoverfly HTTP proxy into Pytest Jan 31, 2021 4 - Beta N/A +`pytest-html `_ pytest plugin for generating HTML reports Dec 13, 2020 5 - Production/Stable pytest (!=6.0.0,>=5.0) +`pytest-html-lee `_ optimized pytest plugin for generating HTML reports Jun 30, 2020 5 - Production/Stable pytest (>=5.0) +`pytest-html-profiling `_ Pytest plugin for generating HTML reports with per-test profiling and optionally call graph visualizations. Based on pytest-html by Dave Hunt. Feb 11, 2020 5 - Production/Stable pytest (>=3.0) +`pytest-html-reporter `_ Generates a static html report based on pytest framework Sep 28, 2020 N/A N/A +`pytest-html-thread `_ pytest plugin for generating HTML reports Dec 29, 2020 5 - Production/Stable N/A +`pytest-http `_ Fixture "http" for http requests Dec 05, 2019 N/A N/A +`pytest-httpbin `_ Easily test your HTTP library against a local copy of httpbin Feb 11, 2019 5 - Production/Stable N/A +`pytest-http-mocker `_ Pytest plugin for http mocking (via https://github.com/vilus/mocker) Oct 20, 2019 N/A N/A +`pytest-httpretty `_ A thin wrapper of HTTPretty for pytest Feb 16, 2014 3 - Alpha N/A +`pytest-httpserver `_ pytest-httpserver is a httpserver for pytest Feb 14, 2021 3 - Alpha pytest ; extra == 'dev' +`pytest-httpx `_ Send responses to httpx. Mar 01, 2021 5 - Production/Stable pytest (==6.*) +`pytest-hue `_ Visualise PyTest status via your Phillips Hue lights May 09, 2019 N/A N/A +`pytest-hypo-25 `_ help hypo module for pytest Jan 12, 2020 3 - Alpha N/A +`pytest-ibutsu `_ A plugin to sent pytest results to an Ibutsu server Mar 09, 2021 4 - Beta pytest +`pytest-icdiff `_ use icdiff for better error messages in pytest assertions Apr 08, 2020 4 - Beta N/A +`pytest-idapro `_ A pytest plugin for idapython. Allows a pytest setup to run tests outside and inside IDA in an automated manner by runnig pytest inside IDA and by mocking idapython api Nov 03, 2018 N/A N/A +`pytest-ignore-flaky `_ ignore failures from flaky tests (pytest plugin) Jan 14, 2019 5 - Production/Stable pytest (>=3.7) +`pytest-image-diff `_ Sep 03, 2020 3 - Alpha pytest +`pytest-incremental `_ an incremental test runner (pytest plugin) Dec 09, 2018 4 - Beta N/A +`pytest-influxdb `_ Plugin for influxdb and pytest integration. Sep 22, 2020 N/A N/A +`pytest-info-collector `_ pytest plugin to collect information from tests May 26, 2019 3 - Alpha N/A +`pytest-informative-node `_ display more node ininformation. Apr 25, 2019 4 - Beta N/A +`pytest-infrastructure `_ pytest stack validation prior to testing executing Apr 12, 2020 4 - Beta N/A +`pytest-inmanta `_ A py.test plugin providing fixtures to simplify inmanta modules testing. Oct 12, 2020 5 - Production/Stable N/A +`pytest-inmanta-extensions `_ Inmanta tests package Jan 07, 2021 5 - Production/Stable N/A +`pytest-Inomaly `_ A simple image diff plugin for pytest Feb 13, 2018 4 - Beta N/A +`pytest-insta `_ A practical snapshot testing plugin for pytest Mar 03, 2021 N/A pytest (>=6.0.2,<7.0.0) +`pytest-instafail `_ pytest plugin to show failures instantly Jun 14, 2020 4 - Beta pytest (>=2.9) +`pytest-instrument `_ pytest plugin to instrument tests Apr 05, 2020 5 - Production/Stable pytest (>=5.1.0) +`pytest-integration `_ Organizing pytests by integration or not Apr 16, 2020 N/A N/A +`pytest-interactive `_ A pytest plugin for console based interactive test selection just after the collection phase Nov 30, 2017 3 - Alpha N/A +`pytest-invenio `_ Pytest fixtures for Invenio. Dec 17, 2020 5 - Production/Stable pytest (<7,>=6) +`pytest-involve `_ Run tests covering a specific file or changeset Feb 02, 2020 4 - Beta pytest (>=3.5.0) +`pytest-ipdb `_ A py.test plug-in to enable drop to ipdb debugger on test failure. Sep 02, 2014 2 - Pre-Alpha N/A +`pytest-ipynb `_ THIS PROJECT IS ABANDONED Jan 29, 2019 3 - Alpha N/A +`pytest-isort `_ py.test plugin to check import ordering using isort Jan 13, 2021 5 - Production/Stable N/A +`pytest-it `_ Pytest plugin to display test reports as a plaintext spec, inspired by Rspec: https://github.com/mattduck/pytest-it. Jan 22, 2020 4 - Beta N/A +`pytest-iterassert `_ Nicer list and iterable assertion messages for pytest May 11, 2020 3 - Alpha N/A +`pytest-jasmine `_ Run jasmine tests from your pytest test suite Nov 04, 2017 1 - Planning N/A +`pytest-jest `_ A custom jest-pytest oriented Pytest reporter May 22, 2018 4 - Beta pytest (>=3.3.2) +`pytest-jira `_ py.test JIRA integration plugin, using markers Nov 29, 2019 N/A N/A +`pytest-jira-xray `_ pytest plugin to integrate tests with JIRA XRAY Feb 12, 2021 3 - Alpha pytest +`pytest-jobserver `_ Limit parallel tests with posix jobserver. May 15, 2019 5 - Production/Stable pytest +`pytest-joke `_ Test failures are better served with humor. Oct 08, 2019 4 - Beta pytest (>=4.2.1) +`pytest-json `_ Generate JSON test reports Jan 18, 2016 4 - Beta N/A +`pytest-jsonlint `_ UNKNOWN Aug 04, 2016 N/A N/A +`pytest-json-report `_ A pytest plugin to report test results as JSON files Oct 23, 2020 4 - Beta pytest (>=4.2.0) +`pytest-kafka `_ Zookeeper, Kafka server, and Kafka consumer fixtures for Pytest Nov 01, 2019 N/A pytest +`pytest-kind `_ Kubernetes test support with KIND for pytest Jan 24, 2021 5 - Production/Stable N/A +`pytest-kivy `_ Kivy GUI tests fixtures using pytest Dec 21, 2020 4 - Beta pytest (>=3.6) +`pytest-knows `_ A pytest plugin that can automaticly skip test case based on dependence info calculated by trace Aug 22, 2014 N/A N/A +`pytest-konira `_ Run Konira DSL tests with py.test Oct 09, 2011 N/A N/A +`pytest-krtech-common `_ pytest krtech common library Nov 28, 2016 4 - Beta N/A +`pytest-kwparametrize `_ Alternate syntax for @pytest.mark.parametrize with test cases as dictionaries and default value fallbacks Jan 22, 2021 N/A pytest (>=6) +`pytest-lambda `_ Define pytest fixtures with lambda functions. Dec 28, 2020 3 - Alpha pytest (>=3.6,<7) +`pytest-lamp `_ Jan 06, 2017 3 - Alpha N/A +`pytest-layab `_ Pytest fixtures for layab. Oct 05, 2020 5 - Production/Stable N/A +`pytest-lazy-fixture `_ It helps to use fixtures in pytest.mark.parametrize Feb 01, 2020 4 - Beta pytest (>=3.2.5) +`pytest-ldap `_ python-ldap fixtures for pytest Aug 18, 2020 N/A pytest +`pytest-leaks `_ A pytest plugin to trace resource leaks. Nov 27, 2019 1 - Planning N/A +`pytest-level `_ Select tests of a given level or lower Oct 21, 2019 N/A pytest +`pytest-libfaketime `_ A python-libfaketime plugin for pytest. Dec 22, 2018 4 - Beta pytest (>=3.0.0) +`pytest-libiio `_ A pytest plugin to manage interfacing with libiio contexts Jan 09, 2021 4 - Beta N/A +`pytest-libnotify `_ Pytest plugin that shows notifications about the test run Nov 12, 2018 3 - Alpha pytest +`pytest-ligo `_ Jan 16, 2020 4 - Beta N/A +`pytest-lineno `_ A pytest plugin to show the line numbers of test functions Dec 04, 2020 N/A pytest +`pytest-lisa `_ Pytest plugin for organizing tests. Jan 21, 2021 3 - Alpha pytest (>=6.1.2,<7.0.0) +`pytest-listener `_ A simple network listener May 28, 2019 5 - Production/Stable pytest +`pytest-litf `_ A pytest plugin that stream output in LITF format Jan 18, 2021 4 - Beta pytest (>=3.1.1) +`pytest-live `_ Live results for pytest Mar 08, 2020 N/A pytest +`pytest-localftpserver `_ A PyTest plugin which provides an FTP fixture for your tests Jan 27, 2021 5 - Production/Stable pytest +`pytest-localserver `_ py.test plugin to test server connections locally. Nov 14, 2018 4 - Beta N/A +`pytest-localstack `_ Pytest plugin for AWS integration tests Aug 22, 2019 4 - Beta pytest (>=3.3.0) +`pytest-lockable `_ lockable resource plugin for pytest Oct 05, 2020 3 - Alpha pytest +`pytest-locker `_ Used to lock object during testing. Essentially changing assertions from being hard coded to asserting that nothing changed Feb 25, 2021 N/A pytest (>=5.4) +`pytest-logbook `_ py.test plugin to capture logbook log messages Nov 23, 2015 5 - Production/Stable pytest (>=2.8) +`pytest-logfest `_ Pytest plugin providing three logger fixtures with basic or full writing to log files Jul 21, 2019 4 - Beta pytest (>=3.5.0) +`pytest-logger `_ Plugin configuring handlers for loggers from Python logging module. Jul 25, 2019 4 - Beta pytest (>=3.2) +`pytest-logging `_ Configures logging and allows tweaking the log level with a py.test flag Nov 04, 2015 4 - Beta N/A +`pytest-log-report `_ Package for creating a pytest test run reprot Dec 26, 2019 N/A N/A +`pytest-manual-marker `_ pytest marker for marking manual tests Nov 28, 2018 3 - Alpha pytest +`pytest-markdown `_ Test your markdown docs with pytest Jan 15, 2021 4 - Beta pytest (>=6.0.1,<7.0.0) +`pytest-marker-bugzilla `_ py.test bugzilla integration plugin, using markers Jan 09, 2020 N/A N/A +`pytest-markers-presence `_ A simple plugin to detect missed pytest tags and markers" Feb 04, 2021 4 - Beta pytest (>=6.0) +`pytest-markfiltration `_ UNKNOWN Nov 08, 2011 3 - Alpha N/A +`pytest-mark-no-py3 `_ pytest plugin and bowler codemod to help migrate tests to Python 3 May 17, 2019 N/A pytest +`pytest-marks `_ UNKNOWN Nov 23, 2012 3 - Alpha N/A +`pytest-matcher `_ Match test output against patterns stored in files Apr 23, 2020 5 - Production/Stable pytest (>=3.4) +`pytest-match-skip `_ Skip matching marks. Matches partial marks using wildcards. May 15, 2019 4 - Beta pytest (>=4.4.1) +`pytest-mat-report `_ this is report Jan 20, 2021 N/A N/A +`pytest-matrix `_ Provide tools for generating tests from combinations of fixtures. Jun 24, 2020 5 - Production/Stable pytest (>=5.4.3,<6.0.0) +`pytest-mccabe `_ pytest plugin to run the mccabe code complexity checker. Jul 22, 2020 3 - Alpha pytest (>=5.4.0) +`pytest-md `_ Plugin for generating Markdown reports for pytest results Jul 11, 2019 3 - Alpha pytest (>=4.2.1) +`pytest-md-report `_ A pytest plugin to make a test results report with Markdown table format. Aug 14, 2020 4 - Beta pytest (!=6.0.0,<7,>=3.3.2) +`pytest-memprof `_ Estimates memory consumption of test functions Mar 29, 2019 4 - Beta N/A +`pytest-menu `_ A pytest plugin for console based interactive test selection just after the collection phase Oct 04, 2017 3 - Alpha pytest (>=2.4.2) +`pytest-mercurial `_ pytest plugin to write integration tests for projects using Mercurial Python internals Nov 21, 2020 1 - Planning N/A +`pytest-messenger `_ Pytest to Slack reporting plugin Dec 16, 2020 5 - Production/Stable N/A +`pytest-metadata `_ pytest plugin for test session metadata Nov 27, 2020 5 - Production/Stable pytest (>=2.9.0) +`pytest-metrics `_ Custom metrics report for pytest Apr 04, 2020 N/A pytest +`pytest-mimesis `_ Mimesis integration with the pytest test runner Mar 21, 2020 5 - Production/Stable pytest (>=4.2) +`pytest-minecraft `_ A pytest plugin for running tests against Minecraft releases Sep 26, 2020 N/A pytest (>=6.0.1,<7.0.0) +`pytest-missing-fixtures `_ Pytest plugin that creates missing fixtures Oct 14, 2020 4 - Beta pytest (>=3.5.0) +`pytest-ml `_ Test your machine learning! May 04, 2019 4 - Beta N/A +`pytest-mocha `_ pytest plugin to display test execution output like a mochajs Apr 02, 2020 4 - Beta pytest (>=5.4.0) +`pytest-mock `_ Thin-wrapper around the mock package for easier use with pytest Jan 10, 2021 5 - Production/Stable pytest (>=5.0) +`pytest-mock-api `_ A mock API server with configurable routes and responses available as a fixture. Feb 13, 2019 1 - Planning pytest (>=4.0.0) +`pytest-mock-helper `_ Help you mock HTTP call and generate mock code Jan 24, 2018 N/A pytest +`pytest-mockito `_ Base fixtures for mockito Jul 11, 2018 4 - Beta N/A +`pytest-mockredis `_ An in-memory mock of a Redis server that runs in a separate thread. This is to be used for unit-tests that require a Redis database. Jan 02, 2018 2 - Pre-Alpha N/A +`pytest-mock-resources `_ A pytest plugin for easily instantiating reproducible mock resources. Feb 17, 2021 N/A pytest (>=1.0) +`pytest-mock-server `_ Mock server plugin for pytest Apr 06, 2020 4 - Beta N/A +`pytest-mockservers `_ A set of fixtures to test your requests to HTTP/UDP servers Mar 31, 2020 N/A pytest (>=4.3.0) +`pytest-modifyjunit `_ Utility for adding additional properties to junit xml for IDM QE Jan 10, 2019 N/A N/A +`pytest-modifyscope `_ pytest plugin to modify fixture scope Apr 12, 2020 N/A pytest +`pytest-molecule `_ PyTest Molecule Plugin :: discover and run molecule tests Jan 25, 2021 5 - Production/Stable N/A +`pytest-mongo `_ MongoDB process and client fixtures plugin for py.test. Jan 12, 2021 5 - Production/Stable pytest (>=3.0.0) +`pytest-mongodb `_ pytest plugin for MongoDB fixtures Dec 07, 2019 5 - Production/Stable pytest (>=2.5.2) +`pytest-monitor `_ Pytest plugin for analyzing resource usage. Feb 07, 2021 5 - Production/Stable pytest +`pytest-monkeyplus `_ pytest's monkeypatch subclass with extra functionalities Sep 18, 2012 5 - Production/Stable N/A +`pytest-monkeytype `_ pytest-monkeytype: Generate Monkeytype annotations from your pytest tests. Jul 29, 2020 4 - Beta N/A +`pytest-moto `_ Fixtures for integration tests of AWS services,uses moto mocking library. Aug 28, 2015 1 - Planning N/A +`pytest-mp `_ A test batcher for multiprocessed Pytest runs May 23, 2018 4 - Beta pytest +`pytest-mpi `_ pytest plugin to collect information from tests Mar 14, 2021 3 - Alpha N/A +`pytest-mpl `_ pytest plugin to help with testing figures output from Matplotlib Nov 05, 2020 4 - Beta pytest +`pytest-mproc `_ low-startup-overhead, scalable, distributed-testing pytest plugin Mar 07, 2021 4 - Beta pytest +`pytest-multihost `_ Utility for writing multi-host tests for pytest Apr 07, 2020 4 - Beta N/A +`pytest-multilog `_ Multi-process logs handling and other helpers for pytest Nov 15, 2020 N/A N/A +`pytest-mutagen `_ Add the mutation testing feature to pytest Jul 24, 2020 N/A pytest (>=5.4) +`pytest-mypy `_ Mypy static type checker plugin for Pytest Nov 14, 2020 4 - Beta pytest (>=3.5) +`pytest-mypyd `_ Mypy static type checker plugin for Pytest Aug 20, 2019 4 - Beta pytest (<4.7,>=2.8) ; python_version < "3.5" +`pytest-mypy-plugins `_ pytest plugin for writing tests for mypy plugins Oct 26, 2020 3 - Alpha pytest (>=6.0.0) +`pytest-mypy-plugins-shim `_ Substitute for "pytest-mypy-plugins" for Python implementations which aren't supported by mypy. Feb 14, 2021 N/A pytest (>=6.0.0) +`pytest-mypy-testing `_ Pytest plugin to check mypy output. Apr 24, 2020 N/A pytest +`pytest-mysql `_ MySQL process and client fixtures for pytest Jul 21, 2020 5 - Production/Stable pytest (>=3.0.0) +`pytest-needle `_ pytest plugin for visual testing websites using selenium Dec 10, 2018 4 - Beta pytest (<5.0.0,>=3.0.0) +`pytest-neo `_ pytest-neo is a plugin for pytest that shows tests like screen of Matrix. Apr 23, 2019 3 - Alpha pytest (>=3.7.2) +`pytest-network `_ A simple plugin to disable network on socket level. May 07, 2020 N/A N/A +`pytest-nginx `_ nginx fixture for pytest Aug 12, 2017 5 - Production/Stable N/A +`pytest-nginx-iplweb `_ nginx fixture for pytest - iplweb temporary fork Mar 01, 2019 5 - Production/Stable N/A +`pytest-ngrok `_ Jan 22, 2020 3 - Alpha N/A +`pytest-ngsfixtures `_ pytest ngs fixtures Sep 06, 2019 2 - Pre-Alpha pytest (>=5.0.0) +`pytest-nice `_ A pytest plugin that alerts user of failed test cases with screen notifications May 04, 2019 4 - Beta pytest +`pytest-nocustom `_ Run all tests without custom markers May 04, 2019 5 - Production/Stable N/A +`pytest-nodev `_ Test-driven source code search for Python. Jul 21, 2016 4 - Beta pytest (>=2.8.1) +`pytest-notebook `_ A pytest plugin for testing Jupyter Notebooks Sep 16, 2020 4 - Beta pytest (>=3.5.0) +`pytest-notice `_ Send pytest execution result email Nov 05, 2020 N/A N/A +`pytest-notification `_ A pytest plugin for sending a desktop notification and playing a sound upon completion of tests Jun 19, 2020 N/A pytest (>=4) +`pytest-notifier `_ A pytest plugin to notify test result Jun 12, 2020 3 - Alpha pytest +`pytest-notimplemented `_ Pytest markers for not implemented features and tests. Aug 27, 2019 N/A pytest (>=5.1,<6.0) +`pytest-notion `_ A PyTest Reporter to send test runs to Notion.so Aug 07, 2019 N/A N/A +`pytest-nunit `_ A pytest plugin for generating NUnit3 test result XML output Aug 04, 2020 4 - Beta pytest (>=3.5.0) +`pytest-ochrus `_ pytest results data-base and HTML reporter Feb 21, 2018 4 - Beta N/A +`pytest-odoo `_ py.test plugin to run Odoo tests Aug 19, 2020 4 - Beta pytest (>=2.9) +`pytest-odoo-fixtures `_ Project description Jun 25, 2019 N/A N/A +`pytest-oerp `_ pytest plugin to test OpenERP modules Feb 28, 2012 3 - Alpha N/A +`pytest-ok `_ The ultimate pytest output plugin Apr 01, 2019 4 - Beta N/A +`pytest-only `_ Use @pytest.mark.only to run a single test Jan 19, 2020 N/A N/A +`pytest-oot `_ Run object-oriented tests in a simple format Sep 18, 2016 4 - Beta N/A +`pytest-openfiles `_ Pytest plugin for detecting inadvertent open file handles Apr 16, 2020 3 - Alpha pytest (>=4.6) +`pytest-opentmi `_ pytest plugin for publish results to opentmi Feb 26, 2021 5 - Production/Stable pytest (>=5.0) +`pytest-operator `_ Fixtures for Operators Feb 20, 2021 N/A N/A +`pytest-optional `_ include/exclude values of fixtures in pytest Oct 07, 2015 N/A N/A +`pytest-optional-tests `_ Easy declaration of optional tests (i.e., that are not run by default) Jul 09, 2019 4 - Beta pytest (>=4.5.0) +`pytest-orchestration `_ A pytest plugin for orchestrating tests Jul 18, 2019 N/A N/A +`pytest-order `_ pytest plugin to run your tests in a specific order Feb 16, 2021 4 - Beta pytest (>=3.7) +`pytest-ordering `_ pytest plugin to run your tests in a specific order Nov 14, 2018 4 - Beta pytest +`pytest-osxnotify `_ OS X notifications for py.test results. May 15, 2015 N/A N/A +`pytest-pact `_ A simple plugin to use with pytest Jan 07, 2019 4 - Beta N/A +`pytest-parallel `_ a pytest plugin for parallel and concurrent testing Apr 30, 2020 3 - Alpha pytest (>=3.0.0) +`pytest-param `_ pytest plugin to test all, first, last or random params Sep 11, 2016 4 - Beta pytest (>=2.6.0) +`pytest-paramark `_ Configure pytest fixtures using a combination of"parametrize" and markers Jan 10, 2020 4 - Beta pytest (>=4.5.0) +`pytest-parametrization `_ Simpler PyTest parametrization Jul 28, 2019 5 - Production/Stable N/A +`pytest-parametrize-cases `_ A more user-friendly way to write parametrized tests. Dec 12, 2020 N/A pytest (>=6.1.2,<7.0.0) +`pytest-parametrized `_ Pytest plugin for parametrizing tests with default iterables. Oct 19, 2020 5 - Production/Stable pytest +`pytest-parawtf `_ Finally spell paramete?ri[sz]e correctly Dec 03, 2018 4 - Beta pytest (>=3.6.0) +`pytest-pass `_ Check out https://github.com/elilutsky/pytest-pass Dec 04, 2019 N/A N/A +`pytest-passrunner `_ Pytest plugin providing the 'run_on_pass' marker Feb 10, 2021 5 - Production/Stable pytest (>=4.6.0) +`pytest-paste-config `_ Allow setting the path to a paste config file Sep 18, 2013 3 - Alpha N/A +`pytest-pdb `_ pytest plugin which adds pdb helper commands related to pytest. Jul 31, 2018 N/A N/A +`pytest-peach `_ pytest plugin for fuzzing with Peach API Security Apr 12, 2019 4 - Beta pytest (>=2.8.7) +`pytest-pep257 `_ py.test plugin for pep257 Jul 09, 2016 N/A N/A +`pytest-pep8 `_ pytest plugin to check PEP8 requirements Apr 27, 2014 N/A N/A +`pytest-percent `_ Change the exit code of pytest test sessions when a required percent of tests pass. May 21, 2020 N/A pytest (>=5.2.0) +`pytest-performance `_ A simple plugin to ensure the execution of critical sections of code has not been impacted Sep 11, 2020 5 - Production/Stable pytest (>=3.7.0) +`pytest-persistence `_ Pytest tool for persistent objects Mar 09, 2021 N/A N/A +`pytest-pgsql `_ Pytest plugins and helpers for tests using a Postgres database. May 13, 2020 5 - Production/Stable pytest (>=3.0.0) +`pytest-picked `_ Run the tests related to the changed files Dec 23, 2020 N/A pytest (>=3.5.0) +`pytest-pigeonhole `_ Jun 25, 2018 5 - Production/Stable pytest (>=3.4) +`pytest-pikachu `_ Show surprise when tests are passing Sep 30, 2019 4 - Beta pytest +`pytest-pilot `_ Slice in your test base thanks to powerful markers. Oct 09, 2020 5 - Production/Stable N/A +`pytest-pings `_ 🦊 The pytest plugin for Firefox Telemetry 📊 Jun 29, 2019 3 - Alpha pytest (>=5.0.0) +`pytest-pinned `_ A simple pytest plugin for pinning tests Jan 21, 2021 4 - Beta pytest (>=3.5.0) +`pytest-pinpoint `_ A pytest plugin which runs SBFL algorithms to detect faults. Sep 25, 2020 N/A pytest (>=4.4.0) +`pytest-pipeline `_ Pytest plugin for functional testing of data analysispipelines Jan 24, 2017 3 - Alpha N/A +`pytest-platform-markers `_ Markers for pytest to skip tests on specific platforms Sep 09, 2019 4 - Beta pytest (>=3.6.0) +`pytest-play `_ pytest plugin that let you automate actions and assertions with test metrics reporting executing plain YAML files Jun 12, 2019 5 - Production/Stable N/A +`pytest-playbook `_ Pytest plugin for reading playbooks. Jan 21, 2021 3 - Alpha pytest (>=6.1.2,<7.0.0) +`pytest-playwright `_ A pytest wrapper with fixtures for Playwright to automate web browsers Feb 25, 2021 N/A pytest +`pytest-plt `_ Fixtures for quickly making Matplotlib plots in tests Aug 17, 2020 5 - Production/Stable pytest +`pytest-plugin-helpers `_ A plugin to help developing and testing other plugins Nov 23, 2019 4 - Beta pytest (>=3.5.0) +`pytest-plus `_ PyTest Plus Plugin :: extends pytest functionality Mar 19, 2020 5 - Production/Stable pytest (>=3.50) +`pytest-pmisc `_ Mar 21, 2019 5 - Production/Stable N/A +`pytest-pointers `_ Pytest plugin to define functions you test with special marks for better navigation and reports Dec 14, 2020 N/A N/A +`pytest-polarion-cfme `_ pytest plugin for collecting test cases and recording test results Nov 13, 2017 3 - Alpha N/A +`pytest-polarion-collect `_ pytest plugin for collecting polarion test cases data Jun 18, 2020 3 - Alpha pytest +`pytest-polecat `_ Provides Polecat pytest fixtures Aug 12, 2019 4 - Beta N/A +`pytest-ponyorm `_ PonyORM in Pytest Oct 31, 2018 N/A pytest (>=3.1.1) +`pytest-poo `_ Visualize your crappy tests Jul 14, 2013 5 - Production/Stable N/A +`pytest-poo-fail `_ Visualize your failed tests with poo Feb 12, 2015 5 - Production/Stable N/A +`pytest-pop `_ A pytest plugin to help with testing pop projects Aug 13, 2020 5 - Production/Stable pytest (>=5.4.0) +`pytest-portion `_ Select a portion of the collected tests Jan 28, 2021 4 - Beta pytest (>=3.5.0) +`pytest-postgres `_ Run PostgreSQL in Docker container in Pytest. Mar 22, 2020 N/A pytest +`pytest-postgresql `_ Postgresql fixtures and fixture factories for Pytest. Feb 23, 2021 5 - Production/Stable pytest (>=3.0.0) +`pytest-power `_ pytest plugin with powerful fixtures Dec 31, 2020 N/A pytest (>=5.4) +`pytest-pride `_ Minitest-style test colors Apr 02, 2016 3 - Alpha N/A +`pytest-print `_ pytest-print adds the printer fixture you can use to print messages to the user (directly to the pytest runner, not stdout) Oct 23, 2020 5 - Production/Stable pytest (>=3.0.0) +`pytest-profiling `_ Profiling plugin for py.test May 28, 2019 5 - Production/Stable pytest +`pytest-progress `_ pytest plugin for instant test progress status Oct 06, 2020 5 - Production/Stable N/A +`pytest-prometheus `_ Report test pass / failures to a Prometheus PushGateway Oct 03, 2017 N/A N/A +`pytest-prosper `_ Test helpers for Prosper projects Sep 24, 2018 N/A N/A +`pytest-pspec `_ A rspec format reporter for Python ptest Jun 02, 2020 4 - Beta pytest (>=3.0.0) +`pytest-pudb `_ Pytest PuDB debugger integration Oct 25, 2018 3 - Alpha pytest (>=2.0) +`pytest-purkinje `_ py.test plugin for purkinje test runner Oct 28, 2017 2 - Pre-Alpha N/A +`pytest-pycharm `_ Plugin for py.test to enter PyCharm debugger on uncaught exceptions Aug 13, 2020 5 - Production/Stable pytest (>=2.3) +`pytest-pycodestyle `_ pytest plugin to run pycodestyle Aug 10, 2020 3 - Alpha N/A +`pytest-pydev `_ py.test plugin to connect to a remote debug server with PyDev or PyCharm. Nov 15, 2017 3 - Alpha N/A +`pytest-pydocstyle `_ pytest plugin to run pydocstyle Aug 10, 2020 3 - Alpha N/A +`pytest-pylint `_ pytest plugin to check source code with pylint Nov 09, 2020 5 - Production/Stable pytest (>=5.4) +`pytest-pypi `_ Easily test your HTTP library against a local copy of pypi Mar 04, 2018 3 - Alpha N/A +`pytest-pypom-navigation `_ Core engine for cookiecutter-qa and pytest-play packages Feb 18, 2019 4 - Beta pytest (>=3.0.7) +`pytest-pyppeteer `_ A plugin to run pyppeteer in pytest. Feb 16, 2021 4 - Beta pytest (>=6.0.2) +`pytest-pyq `_ Pytest fixture "q" for pyq Mar 10, 2020 5 - Production/Stable N/A +`pytest-pyramid `_ pytest_pyramid - provides fixtures for testing pyramid applications with pytest test suite Feb 26, 2021 5 - Production/Stable pytest +`pytest-pyramid-server `_ Pyramid server fixture for py.test May 28, 2019 5 - Production/Stable pytest +`pytest-pytestrail `_ Pytest plugin for interaction with TestRail Aug 27, 2020 4 - Beta pytest (>=3.8.0) +`pytest-pythonpath `_ pytest plugin for adding to the PYTHONPATH from command line or configs. Aug 22, 2018 5 - Production/Stable N/A +`pytest-qml `_ Run QML Tests with pytest Dec 02, 2020 4 - Beta pytest (>=6.0.0) +`pytest-qt `_ pytest support for PyQt and PySide applications Dec 07, 2019 5 - Production/Stable pytest (>=3.0.0) +`pytest-qt-app `_ QT app fixture for py.test Dec 23, 2015 5 - Production/Stable N/A +`pytest-quarantine `_ A plugin for pytest to manage expected test failures Nov 24, 2019 5 - Production/Stable pytest (>=4.6) +`pytest-quickcheck `_ pytest plugin to generate random data inspired by QuickCheck Nov 15, 2020 4 - Beta pytest (<6.0.0,>=4.0) +`pytest-rabbitmq `_ RabbitMQ process and client fixtures for pytest Jan 11, 2021 5 - Production/Stable pytest (>=3.0.0) +`pytest-race `_ Race conditions tester for pytest Nov 21, 2016 4 - Beta N/A +`pytest-rage `_ pytest plugin to implement PEP712 Oct 21, 2011 3 - Alpha N/A +`pytest-raises `_ An implementation of pytest.raises as a pytest.mark fixture Apr 23, 2020 N/A pytest (>=3.2.2) +`pytest-raisesregexp `_ Simple pytest plugin to look for regex in Exceptions Dec 18, 2015 N/A N/A +`pytest-raisin `_ Plugin enabling the use of exception instances with pytest.raises Jun 25, 2020 N/A pytest +`pytest-random `_ py.test plugin to randomize tests Apr 28, 2013 3 - Alpha N/A +`pytest-randomly `_ Pytest plugin to randomly order tests and control random.seed. Nov 16, 2020 5 - Production/Stable pytest +`pytest-randomness `_ Pytest plugin about random seed management May 30, 2019 3 - Alpha N/A +`pytest-random-num `_ Randomise the order in which pytest tests are run with some control over the randomness Oct 19, 2020 5 - Production/Stable N/A +`pytest-random-order `_ Randomise the order in which pytest tests are run with some control over the randomness Nov 30, 2018 5 - Production/Stable pytest (>=3.0.0) +`pytest-readme `_ Test your README.md file Dec 28, 2014 5 - Production/Stable N/A +`pytest-reana `_ Pytest fixtures for REANA. Nov 24, 2020 3 - Alpha N/A +`pytest-recording `_ A pytest plugin that allows you recording of network interactions via VCR.py Nov 25, 2020 4 - Beta pytest (>=3.5.0) +`pytest-recordings `_ Provides pytest plugins for reporting request/response traffic, screenshots, and more to ReportPortal Aug 13, 2020 N/A N/A +`pytest-redis `_ Redis fixtures and fixture factories for Pytest. Oct 15, 2019 5 - Production/Stable pytest (>=3.0.0) +`pytest-redmine `_ Pytest plugin for redmine Mar 19, 2018 1 - Planning N/A +`pytest-ref `_ A plugin to store reference files to ease regression testing Nov 23, 2019 4 - Beta pytest (>=3.5.0) +`pytest-reference-formatter `_ Conveniently run pytest with a dot-formatted test reference. Oct 01, 2019 4 - Beta N/A +`pytest-regressions `_ Easy to use fixtures to write regression tests. Jan 27, 2021 5 - Production/Stable pytest (>=3.5.0) +`pytest-regtest `_ pytest plugin for regression tests Sep 16, 2020 N/A N/A +`pytest-relaxed `_ Relaxed test discovery/organization for pytest Jun 14, 2019 5 - Production/Stable pytest (<5,>=3) +`pytest-remfiles `_ Pytest plugin to create a temporary directory with remote files Jul 01, 2019 5 - Production/Stable N/A +`pytest-remotedata `_ Pytest plugin for controlling remote data access. Jul 20, 2019 3 - Alpha pytest (>=3.1) +`pytest-remove-stale-bytecode `_ py.test plugin to remove stale byte code files. Mar 04, 2020 4 - Beta pytest +`pytest-reorder `_ Reorder tests depending on their paths and names. May 31, 2018 4 - Beta pytest +`pytest-repeat `_ pytest plugin for repeating tests Oct 31, 2020 5 - Production/Stable pytest (>=3.6) +`pytest-replay `_ Saves previous test runs and allow re-execute previous pytest runs to reproduce crashes or flaky tests Dec 09, 2020 4 - Beta pytest (>=3.0.0) +`pytest-repo-health `_ A pytest plugin to report on repository standards conformance Nov 03, 2020 3 - Alpha pytest +`pytest-report `_ Creates json report that is compatible with atom.io's linter message format May 11, 2016 4 - Beta N/A +`pytest-reporter `_ Generate Pytest reports with templates Nov 05, 2020 4 - Beta pytest +`pytest-reporter-html1 `_ A basic HTML report template for Pytest Nov 02, 2020 4 - Beta N/A +`pytest-reportinfra `_ Pytest plugin for reportinfra Aug 11, 2019 3 - Alpha N/A +`pytest-reporting `_ A plugin to report summarized results in a table format Oct 25, 2019 4 - Beta pytest (>=3.5.0) +`pytest-reportlog `_ Replacement for the --resultlog option, focused in simplicity and extensibility Dec 11, 2020 3 - Alpha pytest (>=5.2) +`pytest-report-me `_ A pytest plugin to generate report. Dec 31, 2020 N/A pytest +`pytest-report-parameters `_ pytest plugin for adding tests' parameters to junit report Jun 18, 2020 3 - Alpha pytest (>=2.4.2) +`pytest-reportportal `_ Agent for Reporting results of tests to the Report Portal Feb 15, 2021 N/A pytest (>=3.0.7) +`pytest-reqs `_ pytest plugin to check pinned requirements May 12, 2019 N/A pytest (>=2.4.2) +`pytest-requests `_ A simple plugin to use with pytest Jun 24, 2019 4 - Beta pytest (>=3.5.0) +`pytest-reraise `_ Make multi-threaded pytest test cases fail when they should Jun 03, 2020 5 - Production/Stable N/A +`pytest-rerun `_ Re-run only changed files in specified branch Jul 08, 2019 N/A pytest (>=3.6) +`pytest-rerunfailures `_ pytest plugin to re-run tests to eliminate flaky failures Sep 29, 2020 5 - Production/Stable pytest (>=5.0) +`pytest-resilient-circuits `_ Resilient Circuits fixtures for PyTest. Feb 19, 2021 N/A N/A +`pytest-resource `_ Load resource fixture plugin to use with pytest Nov 14, 2018 4 - Beta N/A +`pytest-resource-path `_ Provides path for uniform access to test resources in isolated directory Aug 18, 2020 5 - Production/Stable pytest (>=3.5.0) +`pytest-responsemock `_ Simplified requests calls mocking for pytest Oct 10, 2020 5 - Production/Stable N/A +`pytest-responses `_ py.test integration for responses Jan 29, 2019 N/A N/A +`pytest-restrict `_ Pytest plugin to restrict the test types allowed Dec 03, 2020 5 - Production/Stable pytest +`pytest-rethinkdb `_ A RethinkDB plugin for pytest. Jul 24, 2016 4 - Beta N/A +`pytest-reverse `_ Pytest plugin to reverse test order. Dec 27, 2020 5 - Production/Stable pytest +`pytest-ringo `_ pytest plugin to test webapplications using the Ringo webframework Sep 27, 2017 3 - Alpha N/A +`pytest-rng `_ Fixtures for seeding tests and making randomness reproducible Aug 08, 2019 5 - Production/Stable pytest +`pytest-roast `_ pytest plugin for ROAST configuration override and fixtures Feb 05, 2021 5 - Production/Stable pytest (<6) +`pytest-rotest `_ Pytest integration with rotest Sep 08, 2019 N/A pytest (>=3.5.0) +`pytest-rpc `_ Extend py.test for RPC OpenStack testing. Feb 22, 2019 4 - Beta pytest (~=3.6) +`pytest-rt `_ pytest data collector plugin for Testgr Mar 03, 2021 N/A N/A +`pytest-rts `_ Coverage-based regression test selection (RTS) plugin for pytest Mar 03, 2021 N/A pytest +`pytest-runfailed `_ implement a --failed option for pytest Mar 24, 2016 N/A N/A +`pytest-runner `_ Invoke py.test as distutils command with dependency resolution Feb 12, 2021 5 - Production/Stable pytest (!=3.7.3,>=3.5) ; extra == 'testing' +`pytest-salt `_ Pytest Salt Plugin Jan 27, 2020 4 - Beta N/A +`pytest-salt-containers `_ A Pytest plugin that builds and creates docker containers Nov 09, 2016 4 - Beta N/A +`pytest-salt-factories `_ Pytest Salt Plugin Mar 05, 2021 4 - Beta pytest (>=6.1.1) +`pytest-salt-from-filenames `_ Simple PyTest Plugin For Salt's Test Suite Specifically Jan 29, 2019 4 - Beta pytest (>=4.1) +`pytest-salt-runtests-bridge `_ Simple PyTest Plugin For Salt's Test Suite Specifically Dec 05, 2019 4 - Beta pytest (>=4.1) +`pytest-sanic `_ a pytest plugin for Sanic Feb 27, 2021 N/A pytest (>=5.2) +`pytest-sanity `_ Dec 07, 2020 N/A N/A +`pytest-sa-pg `_ May 14, 2019 N/A N/A +`pytest-sbase `_ A complete web automation framework for end-to-end testing. Mar 10, 2021 5 - Production/Stable N/A +`pytest-scenario `_ pytest plugin for test scenarios Feb 06, 2017 3 - Alpha N/A +`pytest-schema `_ 👍 Validate return values against a schema-like object in testing Aug 31, 2020 5 - Production/Stable pytest (>=3.5.0) +`pytest-securestore `_ An encrypted password store for use within pytest cases Jun 19, 2019 4 - Beta N/A +`pytest-select `_ A pytest plugin which allows to (de-)select tests from a file. Jan 18, 2019 3 - Alpha pytest (>=3.0) +`pytest-selenium `_ pytest plugin for Selenium Sep 19, 2020 5 - Production/Stable pytest (>=5.0.0) +`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Mar 10, 2021 5 - Production/Stable N/A +`pytest-selenium-enhancer `_ pytest plugin for Selenium Nov 26, 2020 5 - Production/Stable N/A +`pytest-selenium-pdiff `_ A pytest package implementing perceptualdiff for Selenium tests. Apr 06, 2017 2 - Pre-Alpha N/A +`pytest-send-email `_ Send pytest execution result email Dec 04, 2019 N/A N/A +`pytest-sentry `_ A pytest plugin to send testrun information to Sentry.io Mar 02, 2021 N/A N/A +`pytest-server-fixtures `_ Extensible server fixures for py.test May 28, 2019 5 - Production/Stable pytest +`pytest-serverless `_ Automatically mocks resources from serverless.yml in pytest using moto. Feb 20, 2021 4 - Beta N/A +`pytest-services `_ Services plugin for pytest testing framework Oct 30, 2020 6 - Mature N/A +`pytest-session2file `_ pytest-session2file (aka: pytest-session_to_file for v0.1.0 - v0.1.2) is a py.test plugin for capturing and saving to file the stdout of py.test. Jan 26, 2021 3 - Alpha pytest +`pytest-session-fixture-globalize `_ py.test plugin to make session fixtures behave as if written in conftest, even if it is written in some modules May 15, 2018 4 - Beta N/A +`pytest-session_to_file `_ pytest-session_to_file is a py.test plugin for capturing and saving to file the stdout of py.test. Oct 01, 2015 3 - Alpha N/A +`pytest-sftpserver `_ py.test plugin to locally test sftp server connections. Sep 16, 2019 4 - Beta N/A +`pytest-shard `_ Dec 11, 2020 4 - Beta pytest +`pytest-shell `_ A pytest plugin for testing shell scripts and line-based processes Jan 18, 2020 N/A N/A +`pytest-sheraf `_ Versatile ZODB abstraction layer - pytest fixtures Feb 11, 2020 N/A pytest +`pytest-sherlock `_ pytest plugin help to find coupled tests Jul 13, 2020 5 - Production/Stable pytest (>=3.5.1) +`pytest-shortcuts `_ Expand command-line shortcuts listed in pytest configuration Oct 29, 2020 4 - Beta pytest (>=3.5.0) +`pytest-shutil `_ A goodie-bag of unix shell and environment tools for py.test May 28, 2019 5 - Production/Stable pytest +`pytest-simple-plugin `_ Simple pytest plugin Nov 27, 2019 N/A N/A +`pytest-simple-settings `_ simple-settings plugin for pytest Nov 17, 2020 4 - Beta pytest +`pytest-single-file-logging `_ Allow for multiple processes to log to a single file May 05, 2016 4 - Beta pytest (>=2.8.1) +`pytest-skipper `_ A plugin that selects only tests with changes in execution path Mar 26, 2017 3 - Alpha pytest (>=3.0.6) +`pytest-skippy `_ Automatically skip tests that don't need to run! Jan 27, 2018 3 - Alpha pytest (>=2.3.4) +`pytest-slack `_ Pytest to Slack reporting plugin Dec 15, 2020 5 - Production/Stable N/A +`pytest-smartcollect `_ A plugin for collecting tests that touch changed code Oct 04, 2018 N/A pytest (>=3.5.0) +`pytest-smartcov `_ Smart coverage plugin for pytest. Sep 30, 2017 3 - Alpha N/A +`pytest-smtp `_ Send email with pytest execution result Feb 20, 2021 N/A pytest +`pytest-snail `_ Plugin for adding a marker to slow running tests. 🐌 Nov 04, 2019 3 - Alpha pytest (>=5.0.1) +`pytest-snapci `_ py.test plugin for Snap-CI Nov 12, 2015 N/A N/A +`pytest-snapshot `_ A plugin to enable snapshot testing with pytest. Jan 22, 2021 4 - Beta pytest (>=3.0.0) +`pytest-snmpserver `_ Sep 14, 2020 N/A N/A +`pytest-socket `_ Pytest Plugin to disable socket calls during tests May 31, 2020 4 - Beta pytest (>=3.6.3) +`pytest-soft-assertions `_ May 05, 2020 3 - Alpha pytest +`pytest-solr `_ Solr process and client fixtures for py.test. May 11, 2020 3 - Alpha pytest (>=3.0.0) +`pytest-sorter `_ A simple plugin to first execute tests that historically failed more Jul 23, 2020 4 - Beta pytest (>=3.1.1) +`pytest-sourceorder `_ Test-ordering plugin for pytest Apr 11, 2017 4 - Beta pytest +`pytest-spark `_ pytest plugin to run the tests with support of pyspark. Feb 23, 2020 4 - Beta pytest +`pytest-spawner `_ py.test plugin to spawn process and communicate with them. Jul 31, 2015 4 - Beta N/A +`pytest-spec `_ Library pytest-spec is a pytest plugin to display test execution output like a SPECIFICATION. Jan 14, 2021 N/A N/A +`pytest-sphinx `_ Doctest plugin for pytest with support for Sphinx-specific doctest-directives Aug 05, 2020 4 - Beta N/A +`pytest-spiratest `_ Exports unit tests as test runs in SpiraTest/Team/Plan Feb 12, 2021 N/A N/A +`pytest-splinter `_ Splinter plugin for pytest testing framework Dec 25, 2020 6 - Mature N/A +`pytest-split `_ Pytest plugin for splitting test suite based on test execution time Apr 07, 2020 1 - Planning N/A +`pytest-splitio `_ Split.io SDK integration for e2e tests Sep 22, 2020 N/A pytest (<7,>=5.0) +`pytest-split-tests `_ A Pytest plugin for running a subset of your tests by splitting them in to equally sized groups. Forked from Mark Adams' original project pytest-test-groups. May 28, 2019 N/A pytest (>=2.5) +`pytest-splunk-addon `_ A Dynamic test tool for Splunk Apps and Add-ons Feb 26, 2021 N/A pytest (>5.4.0,<6.1) +`pytest-splunk-addon-ui-smartx `_ Library to support testing Splunk Add-on UX Jan 18, 2021 N/A N/A +`pytest-splunk-env `_ pytest fixtures for interaction with Splunk Enterprise and Splunk Cloud Oct 22, 2020 N/A pytest (>=6.1.1,<7.0.0) +`pytest-sqitch `_ sqitch for pytest Apr 06, 2020 4 - Beta N/A +`pytest-sqlalchemy `_ pytest plugin with sqlalchemy related fixtures Mar 13, 2018 3 - Alpha N/A +`pytest-sql-bigquery `_ Yet another SQL-testing framework for BigQuery provided by pytest plugin Dec 19, 2019 N/A pytest +`pytest-srcpaths `_ Add paths to sys.path Feb 18, 2021 N/A N/A +`pytest-ssh `_ pytest plugin for ssh command run May 27, 2019 N/A pytest +`pytest-start-from `_ Start pytest run from a given point Apr 11, 2016 N/A N/A +`pytest-statsd `_ pytest plugin for reporting to graphite Nov 30, 2018 5 - Production/Stable pytest (>=3.0.0) +`pytest-stepfunctions `_ A small description Jul 07, 2020 4 - Beta pytest +`pytest-steps `_ Create step-wise / incremental tests in pytest. Apr 25, 2020 5 - Production/Stable N/A +`pytest-stepwise `_ Run a test suite one failing test at a time. Dec 01, 2015 4 - Beta N/A +`pytest-stoq `_ A plugin to pytest stoq Feb 09, 2021 4 - Beta N/A +`pytest-stress `_ A Pytest plugin that allows you to loop tests for a user defined amount of time. Dec 07, 2019 4 - Beta pytest (>=3.6.0) +`pytest-structlog `_ Structured logging assertions Jul 16, 2020 N/A pytest +`pytest-structmpd `_ provide structured temporary directory Oct 17, 2018 N/A N/A +`pytest-stub `_ Stub packages, modules and attributes. Apr 28, 2020 5 - Production/Stable N/A +`pytest-stubprocess `_ Provide stub implementations for subprocesses in Python tests Sep 17, 2018 3 - Alpha pytest (>=3.5.0) +`pytest-study `_ A pytest plugin to organize long run tests (named studies) without interfering the regular tests Sep 26, 2017 3 - Alpha pytest (>=2.0) +`pytest-subprocess `_ A plugin to fake subprocess for pytest Aug 22, 2020 5 - Production/Stable pytest (>=4.0.0) +`pytest-subtesthack `_ A hack to explicitly set up and tear down fixtures. Mar 02, 2021 N/A N/A +`pytest-subtests `_ unittest subTest() support and subtests fixture Dec 13, 2020 4 - Beta pytest (>=5.3.0) +`pytest-subunit `_ pytest-subunit is a plugin for py.test which outputs testsresult in subunit format. Aug 29, 2017 N/A N/A +`pytest-sugar `_ pytest-sugar is a plugin for pytest that changes the default look and feel of pytest (e.g. progressbar, show tests that fail instantly). Jul 06, 2020 3 - Alpha N/A +`pytest-sugar-bugfix159 `_ Workaround for https://github.com/Frozenball/pytest-sugar/issues/159 Nov 07, 2018 5 - Production/Stable pytest (!=3.7.3,>=3.5); extra == 'testing' +`pytest-super-check `_ Pytest plugin to check your TestCase classes call super in setUp, tearDown, etc. Dec 13, 2020 5 - Production/Stable pytest +`pytest-svn `_ SVN repository fixture for py.test May 28, 2019 5 - Production/Stable pytest +`pytest-symbols `_ pytest-symbols is a pytest plugin that adds support for passing test environment symbols into pytest tests. Nov 20, 2017 3 - Alpha N/A +`pytest-tap `_ Test Anything Protocol (TAP) reporting plugin for pytest Nov 07, 2020 5 - Production/Stable pytest (>=3.0) +`pytest-target `_ Pytest plugin for remote target orchestration. Jan 21, 2021 3 - Alpha pytest (>=6.1.2,<7.0.0) +`pytest-tblineinfo `_ tblineinfo is a py.test plugin that insert the node id in the final py.test report when --tb=line option is used Dec 01, 2015 3 - Alpha pytest (>=2.0) +`pytest-teamcity-logblock `_ py.test plugin to introduce block structure in teamcity build log, if output is not captured May 15, 2018 4 - Beta N/A +`pytest-telegram `_ Pytest to Telegram reporting plugin Dec 10, 2020 5 - Production/Stable N/A +`pytest-tempdir `_ Predictable and repeatable tempdir support. Oct 11, 2019 4 - Beta pytest (>=2.8.1) +`pytest-terraform `_ A pytest plugin for using terraform fixtures Oct 20, 2020 N/A pytest (>=6.0.0,<6.1.0) +`pytest-terraform-fixture `_ generate terraform resources to use with pytest Nov 14, 2018 4 - Beta N/A +`pytest-testbook `_ A plugin to run tests written in Jupyter notebook Dec 11, 2016 3 - Alpha N/A +`pytest-testconfig `_ Test configuration plugin for pytest. Jan 11, 2020 4 - Beta pytest (>=3.5.0) +`pytest-testdirectory `_ A py.test plugin providing temporary directories in unit tests. Nov 06, 2018 5 - Production/Stable pytest +`pytest-testdox `_ A testdox format reporter for pytest Oct 13, 2020 5 - Production/Stable pytest (>=3.7.0) +`pytest-test-groups `_ A Pytest plugin for running a subset of your tests by splitting them in to equally sized groups. Oct 25, 2016 5 - Production/Stable N/A +`pytest-testinfra `_ Test infrastructures Nov 12, 2020 5 - Production/Stable pytest (!=3.0.2) +`pytest-testlink-adaptor `_ pytest reporting plugin for testlink Dec 20, 2018 4 - Beta pytest (>=2.6) +`pytest-testmon `_ selects tests affected by changed files and methods Aug 05, 2020 4 - Beta N/A +`pytest-testobject `_ Plugin to use TestObject Suites with Pytest Sep 24, 2019 4 - Beta pytest (>=3.1.1) +`pytest-testrail `_ pytest plugin for creating TestRail runs and adding results Aug 27, 2020 N/A pytest (>=3.6) +`pytest-testrail2 `_ A small example package Nov 17, 2020 N/A pytest (>=5) +`pytest-testrail-api `_ Плагин Pytest, для интеграции с TestRail Dec 09, 2020 N/A pytest (>=5.5) +`pytest-testrail-client `_ pytest plugin for Testrail Sep 29, 2020 5 - Production/Stable N/A +`pytest-testrail-e2e `_ pytest plugin for creating TestRail runs and adding results Jun 11, 2020 N/A pytest (>=3.6) +`pytest-testrail-plugin `_ PyTest plugin for TestRail Apr 21, 2020 3 - Alpha pytest +`pytest-testrail-reporter `_ Sep 10, 2018 N/A N/A +`pytest-testslide `_ TestSlide fixture for pytest Jan 07, 2021 5 - Production/Stable pytest (~=6.2) +`pytest-test-this `_ Plugin for py.test to run relevant tests, based on naively checking if a test contains a reference to the symbol you supply Sep 15, 2019 2 - Pre-Alpha pytest (>=2.3) +`pytest-tesults `_ Tesults plugin for pytest May 18, 2020 5 - Production/Stable pytest (>=3.5.0) +`pytest-tezos `_ pytest-ligo Jan 16, 2020 4 - Beta N/A +`pytest-thawgun `_ Pytest plugin for time travel May 26, 2020 3 - Alpha N/A +`pytest-threadleak `_ Detects thread leaks Sep 08, 2017 4 - Beta N/A +`pytest-timeit `_ A pytest plugin to time test function runs Oct 13, 2016 4 - Beta N/A +`pytest-timeout `_ py.test plugin to abort hanging tests Jul 15, 2020 5 - Production/Stable pytest (>=3.6.0) +`pytest-timeouts `_ Linux-only Pytest plugin to control durations of various test case execution phases Sep 21, 2019 5 - Production/Stable N/A +`pytest-timer `_ A timer plugin for pytest Dec 13, 2020 N/A N/A +`pytest-tipsi-django `_ Oct 14, 2020 4 - Beta pytest (>=6.0.0) +`pytest-tipsi-testing `_ Better fixtures management. Various helpers Nov 04, 2020 4 - Beta pytest (>=3.3.0) +`pytest-tldr `_ A pytest plugin that limits the output to just the things you need. Mar 12, 2021 4 - Beta pytest (>=3.5.0) +`pytest-tm4j-reporter `_ Cloud Jira Test Management (TM4J) PyTest reporter plugin Sep 01, 2020 N/A pytest +`pytest-todo `_ A small plugin for the pytest testing framework, marking TODO comments as failure May 23, 2019 4 - Beta pytest +`pytest-tomato `_ Mar 01, 2019 5 - Production/Stable N/A +`pytest-toolbelt `_ This is just a collection of utilities for pytest, but don't really belong in pytest proper. Aug 12, 2019 3 - Alpha N/A +`pytest-toolbox `_ Numerous useful plugins for pytest. Apr 07, 2018 N/A pytest (>=3.5.0) +`pytest-tornado `_ A py.test plugin providing fixtures and markers to simplify testing of asynchronous tornado applications. Jun 17, 2020 5 - Production/Stable pytest (>=3.6) +`pytest-tornado5 `_ A py.test plugin providing fixtures and markers to simplify testing of asynchronous tornado applications. Nov 16, 2018 5 - Production/Stable pytest (>=3.6) +`pytest-tornado-yen3 `_ A py.test plugin providing fixtures and markers to simplify testing of asynchronous tornado applications. Oct 15, 2018 5 - Production/Stable N/A +`pytest-tornasync `_ py.test plugin for testing Python 3.5+ Tornado code Jul 15, 2019 3 - Alpha pytest (>=3.0) +`pytest-track `_ Feb 26, 2021 3 - Alpha pytest (>=3.0) +`pytest-translations `_ Test your translation files. Oct 26, 2020 5 - Production/Stable N/A +`pytest-travis-fold `_ Folds captured output sections in Travis CI build log Nov 29, 2017 4 - Beta pytest (>=2.6.0) +`pytest-trello `_ Plugin for py.test that integrates trello using markers Nov 20, 2015 5 - Production/Stable N/A +`pytest-trepan `_ Pytest plugin for trepan debugger. Jul 28, 2018 5 - Production/Stable N/A +`pytest-trialtemp `_ py.test plugin for using the same _trial_temp working directory as trial Jun 08, 2015 N/A N/A +`pytest-trio `_ Pytest plugin for trio Oct 16, 2020 N/A N/A +`pytest-tspwplib `_ A simple plugin to use with tspwplib Jan 08, 2021 4 - Beta pytest (>=3.5.0) +`pytest-tstcls `_ Test Class Base Mar 23, 2020 5 - Production/Stable N/A +`pytest-twisted `_ A twisted plugin for pytest. Sep 11, 2020 5 - Production/Stable pytest (>=2.3) +`pytest-typhoon-xray `_ Typhoon HIL plugin for pytest Jun 22, 2020 4 - Beta pytest (>=5.4.2) +`pytest-tytest `_ Typhoon HIL plugin for pytest May 25, 2020 4 - Beta pytest (>=5.4.2) +`pytest-ubersmith `_ Easily mock calls to ubersmith at the `requests` level. Apr 13, 2015 N/A N/A +`pytest-ui `_ Text User Interface for running python tests May 03, 2020 4 - Beta pytest +`pytest-unhandled-exception-exit-code `_ Plugin for py.test set a different exit code on uncaught exceptions Jun 22, 2020 5 - Production/Stable pytest (>=2.3) +`pytest-unittest-filter `_ A pytest plugin for filtering unittest-based test classes Jan 12, 2019 4 - Beta pytest (>=3.1.0) +`pytest-unmarked `_ Run only unmarked tests Aug 27, 2019 5 - Production/Stable N/A +`pytest-unordered `_ Test equality of unordered collections in pytest Nov 02, 2020 4 - Beta pytest (>=6.0.0) +`pytest-vagrant `_ A py.test plugin providing access to vagrant. Mar 23, 2020 5 - Production/Stable pytest +`pytest-valgrind `_ Mar 15, 2020 N/A N/A +`pytest-variables `_ pytest plugin for providing variables to tests/fixtures Oct 23, 2019 5 - Production/Stable pytest (>=2.4.2) +`pytest-vcr `_ Plugin for managing VCR.py cassettes Apr 26, 2019 5 - Production/Stable pytest (>=3.6.0) +`pytest-vcrpandas `_ Test from HTTP interactions to dataframe processed. Jan 12, 2019 4 - Beta pytest +`pytest-venv `_ py.test fixture for creating a virtual environment Aug 04, 2020 4 - Beta pytest +`pytest-verbose-parametrize `_ More descriptive output for parametrized py.test tests May 28, 2019 5 - Production/Stable pytest +`pytest-vimqf `_ A simple pytest plugin that will shrink pytest output when specified, to fit vim quickfix window. Feb 08, 2021 4 - Beta pytest (>=6.2.2,<7.0.0) +`pytest-virtualenv `_ Virtualenv fixture for py.test May 28, 2019 5 - Production/Stable pytest +`pytest-voluptuous `_ Pytest plugin for asserting data against voluptuous schema. Jun 09, 2020 N/A pytest +`pytest-vscodedebug `_ A pytest plugin to easily enable debugging tests within Visual Studio Code Dec 04, 2020 4 - Beta N/A +`pytest-vts `_ pytest plugin for automatic recording of http stubbed tests Jun 05, 2019 N/A pytest (>=2.3) +`pytest-vw `_ pytest-vw makes your failing test cases succeed under CI tools scrutiny Oct 07, 2015 4 - Beta N/A +`pytest-vyper `_ Plugin for the vyper smart contract language. May 28, 2020 2 - Pre-Alpha N/A +`pytest-wa-e2e-plugin `_ Pytest plugin for testing whatsapp bots with end to end tests Feb 18, 2020 4 - Beta pytest (>=3.5.0) +`pytest-watch `_ Local continuous test runner with pytest and watchdog. May 20, 2018 N/A N/A +`pytest-wdl `_ Pytest plugin for testing WDL workflows. Nov 17, 2020 5 - Production/Stable N/A +`pytest-webdriver `_ Selenium webdriver fixture for py.test May 28, 2019 5 - Production/Stable pytest +`pytest-wetest `_ Welian API Automation test framework pytest plugin Nov 10, 2018 4 - Beta N/A +`pytest-whirlwind `_ Testing Tornado. Jun 12, 2020 N/A N/A +`pytest-wholenodeid `_ pytest addon for displaying the whole node id for failures Aug 26, 2015 4 - Beta pytest (>=2.0) +`pytest-winnotify `_ Windows tray notifications for py.test results. Apr 22, 2016 N/A N/A +`pytest-workflow `_ A pytest plugin for configuring workflow/pipeline tests using YAML files Dec 14, 2020 5 - Production/Stable pytest (>=5.4.0) +`pytest-xdist `_ pytest xdist plugin for distributed testing and loop-on-failing modes Feb 09, 2021 5 - Production/Stable pytest (>=6.0.0) +`pytest-xdist-debug-for-graingert `_ pytest xdist plugin for distributed testing and loop-on-failing modes Jul 24, 2019 5 - Production/Stable pytest (>=4.4.0) +`pytest-xdist-forked `_ forked from pytest-xdist Feb 10, 2020 5 - Production/Stable pytest (>=4.4.0) +`pytest-xfaillist `_ Maintain a xfaillist in an additional file to avoid merge-conflicts. Mar 07, 2021 N/A pytest (>=6.2.2,<7.0.0) +`pytest-xfiles `_ Pytest fixtures providing data read from function, module or package related (x)files. Feb 27, 2018 N/A N/A +`pytest-xlog `_ Extended logging for test and decorators May 31, 2020 4 - Beta N/A +`pytest-xpara `_ An extended parametrizing plugin of pytest. Oct 30, 2017 3 - Alpha pytest +`pytest-xprocess `_ A pytest plugin for managing processes across test runs. Mar 02, 2021 4 - Beta pytest (>=2.8) +`pytest-xray `_ May 30, 2019 3 - Alpha N/A +`pytest-xrayjira `_ Mar 17, 2020 3 - Alpha pytest (==4.3.1) +`pytest-xray-server `_ Mar 03, 2021 3 - Alpha N/A +`pytest-xvfb `_ A pytest plugin to run Xvfb for tests. Jun 09, 2020 4 - Beta pytest (>=2.8.1) +`pytest-yaml `_ This plugin is used to load yaml output to your test using pytest framework. Oct 05, 2018 N/A pytest +`pytest-yamltree `_ Create or check file/directory trees described by YAML Mar 02, 2020 4 - Beta pytest (>=3.1.1) +`pytest-yamlwsgi `_ Run tests against wsgi apps defined in yaml May 11, 2010 N/A N/A +`pytest-yapf `_ Run yapf Jul 06, 2017 4 - Beta pytest (>=3.1.1) +`pytest-yapf3 `_ Validate your Python file format with yapf Aug 03, 2020 5 - Production/Stable pytest (>=5.4) +`pytest-yield `_ PyTest plugin to run tests concurrently, each `yield` switch context to other one Jan 23, 2019 N/A N/A +`pytest-zafira `_ A Zafira plugin for pytest Sep 18, 2019 5 - Production/Stable pytest (==4.1.1) +`pytest-zap `_ OWASP ZAP plugin for py.test. May 12, 2014 4 - Beta N/A +`pytest-zigzag `_ Extend py.test for RPC OpenStack testing. Feb 27, 2019 4 - Beta pytest (~=3.6) +============================================================================================================== ======================================================================================================================================================================== ============== ===================== ============================================ From 59251e8a2a4137247ce1facd2205a3c30d312911 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sat, 13 Mar 2021 21:22:54 +0200 Subject: [PATCH 182/630] Remove/replace some unneeded usages of py.path --- doc/en/builtin.rst | 4 +- doc/en/example/assertion/test_failures.py | 6 +- doc/en/example/multipython.py | 12 ++-- doc/en/example/simple.rst | 10 ++-- doc/en/getting-started.rst | 20 +++---- doc/en/reference/doctest.rst | 2 +- doc/en/reference/reference.rst | 6 +- doc/en/reference/unittest.rst | 9 +-- src/_pytest/pytester.py | 4 +- src/_pytest/tmpdir.py | 8 +-- .../tmpdir/tmp_path_fixture.py | 7 +++ .../example_scripts/tmpdir/tmpdir_fixture.py | 7 --- testing/python/fixtures.py | 39 ++++++------- testing/test_collection.py | 3 +- testing/test_config.py | 1 - testing/test_parseopt.py | 8 +-- testing/test_pytester.py | 6 +- testing/test_tmpdir.py | 55 +++++++++---------- 18 files changed, 102 insertions(+), 105 deletions(-) create mode 100644 testing/example_scripts/tmpdir/tmp_path_fixture.py delete mode 100644 testing/example_scripts/tmpdir/tmpdir_fixture.py diff --git a/doc/en/builtin.rst b/doc/en/builtin.rst index 5c7c8dfe666..d4d06af1548 100644 --- a/doc/en/builtin.rst +++ b/doc/en/builtin.rst @@ -148,10 +148,10 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a on warning categories. tmpdir_factory [session scope] - Return a :class:`_pytest.tmpdir.TempdirFactory` instance for the test session. + Return a :class:`pytest.TempdirFactory` instance for the test session. tmp_path_factory [session scope] - Return a :class:`_pytest.tmpdir.TempPathFactory` instance for the test session. + Return a :class:`pytest.TempPathFactory` instance for the test session. tmpdir Return a temporary directory path object which is unique to each test diff --git a/doc/en/example/assertion/test_failures.py b/doc/en/example/assertion/test_failures.py index eda06dfc598..350518b43c7 100644 --- a/doc/en/example/assertion/test_failures.py +++ b/doc/en/example/assertion/test_failures.py @@ -5,9 +5,9 @@ pytest_plugins = ("pytester",) -def test_failure_demo_fails_properly(testdir): - target = testdir.tmpdir.join(os.path.basename(failure_demo)) +def test_failure_demo_fails_properly(pytester): + target = pytester.path.joinpath(os.path.basename(failure_demo)) shutil.copy(failure_demo, target) - result = testdir.runpytest(target, syspathinsert=True) + result = pytester.runpytest(target, syspathinsert=True) result.stdout.fnmatch_lines(["*44 failed*"]) assert result.ret != 0 diff --git a/doc/en/example/multipython.py b/doc/en/example/multipython.py index 21bddcd0353..9005d31addd 100644 --- a/doc/en/example/multipython.py +++ b/doc/en/example/multipython.py @@ -12,8 +12,8 @@ @pytest.fixture(params=pythonlist) -def python1(request, tmpdir): - picklefile = tmpdir.join("data.pickle") +def python1(request, tmp_path): + picklefile = tmp_path / "data.pickle" return Python(request.param, picklefile) @@ -30,8 +30,8 @@ def __init__(self, version, picklefile): self.picklefile = picklefile def dumps(self, obj): - dumpfile = self.picklefile.dirpath("dump.py") - dumpfile.write( + dumpfile = self.picklefile.with_name("dump.py") + dumpfile.write_text( textwrap.dedent( r""" import pickle @@ -46,8 +46,8 @@ def dumps(self, obj): subprocess.check_call((self.pythonpath, str(dumpfile))) def load_and_is_true(self, expression): - loadfile = self.picklefile.dirpath("load.py") - loadfile.write( + loadfile = self.picklefile.with_name("load.py") + loadfile.write_text( textwrap.dedent( r""" import pickle diff --git a/doc/en/example/simple.rst b/doc/en/example/simple.rst index 18f1945c544..759f308e2b7 100644 --- a/doc/en/example/simple.rst +++ b/doc/en/example/simple.rst @@ -768,8 +768,8 @@ case we just write some information out to a ``failures`` file: mode = "a" if os.path.exists("failures") else "w" with open("failures", mode) as f: # let's also access a fixture for the fun of it - if "tmpdir" in item.fixturenames: - extra = " ({})".format(item.funcargs["tmpdir"]) + if "tmp_path" in item.fixturenames: + extra = " ({})".format(item.funcargs["tmp_path"]) else: extra = "" @@ -781,7 +781,7 @@ if you then have failing tests: .. code-block:: python # content of test_module.py - def test_fail1(tmpdir): + def test_fail1(tmp_path): assert 0 @@ -804,9 +804,9 @@ and run them: ================================= FAILURES ================================= ________________________________ test_fail1 ________________________________ - tmpdir = local('PYTEST_TMPDIR/test_fail10') + tmp_path = Path('PYTEST_TMPDIR/test_fail10') - def test_fail1(tmpdir): + def test_fail1(tmp_path): > assert 0 E assert 0 diff --git a/doc/en/getting-started.rst b/doc/en/getting-started.rst index 32ded553950..a6a45792773 100644 --- a/doc/en/getting-started.rst +++ b/doc/en/getting-started.rst @@ -213,24 +213,24 @@ Request a unique temporary directory for functional tests .. code-block:: python - # content of test_tmpdir.py - def test_needsfiles(tmpdir): - print(tmpdir) + # content of test_tmp_path.py + def test_needsfiles(tmp_path): + print(tmp_path) assert 0 -List the name ``tmpdir`` in the test function signature and ``pytest`` will lookup and call a fixture factory to create the resource before performing the test function call. Before the test runs, ``pytest`` creates a unique-per-test-invocation temporary directory: +List the name ``tmp_path`` in the test function signature and ``pytest`` will lookup and call a fixture factory to create the resource before performing the test function call. Before the test runs, ``pytest`` creates a unique-per-test-invocation temporary directory: .. code-block:: pytest - $ pytest -q test_tmpdir.py + $ pytest -q test_tmp_path.py F [100%] ================================= FAILURES ================================= _____________________________ test_needsfiles ______________________________ - tmpdir = local('PYTEST_TMPDIR/test_needsfiles0') + tmp_path = Path('PYTEST_TMPDIR/test_needsfiles0') - def test_needsfiles(tmpdir): - print(tmpdir) + def test_needsfiles(tmp_path): + print(tmp_path) > assert 0 E assert 0 @@ -238,10 +238,10 @@ List the name ``tmpdir`` in the test function signature and ``pytest`` will look --------------------------- Captured stdout call --------------------------- PYTEST_TMPDIR/test_needsfiles0 ========================= short test summary info ========================== - FAILED test_tmpdir.py::test_needsfiles - assert 0 + FAILED test_tmp_path.py::test_needsfiles - assert 0 1 failed in 0.12s -More info on tmpdir handling is available at :ref:`Temporary directories and files `. +More info on temporary directory handling is available at :ref:`Temporary directories and files `. Find out what kind of builtin :ref:`pytest fixtures ` exist with the command: diff --git a/doc/en/reference/doctest.rst b/doc/en/reference/doctest.rst index b6ef1f5b835..e12f02b6246 100644 --- a/doc/en/reference/doctest.rst +++ b/doc/en/reference/doctest.rst @@ -194,7 +194,7 @@ It is possible to use fixtures using the ``getfixture`` helper: .. code-block:: text # content of example.rst - >>> tmp = getfixture('tmpdir') + >>> tmp = getfixture('tmp_path') >>> ... >>> diff --git a/doc/en/reference/reference.rst b/doc/en/reference/reference.rst index a8faa044f9b..4c5e668d26e 100644 --- a/doc/en/reference/reference.rst +++ b/doc/en/reference/reference.rst @@ -284,9 +284,9 @@ Example of a fixture requiring another fixture: .. code-block:: python @pytest.fixture - def db_session(tmpdir): - fn = tmpdir / "db.file" - return connect(str(fn)) + def db_session(tmp_path): + fn = tmp_path / "db.file" + return connect(fn) For more details, consult the full :ref:`fixtures docs `. diff --git a/doc/en/reference/unittest.rst b/doc/en/reference/unittest.rst index 130e7705b8d..92c1bbfa2f1 100644 --- a/doc/en/reference/unittest.rst +++ b/doc/en/reference/unittest.rst @@ -190,21 +190,22 @@ and define the fixture function in the context where you want it used. Let's look at an ``initdir`` fixture which makes all test methods of a ``TestCase`` class execute in a temporary directory with a pre-initialized ``samplefile.ini``. Our ``initdir`` fixture itself uses -the pytest builtin :ref:`tmpdir ` fixture to delegate the +the pytest builtin :fixture:`tmp_path` fixture to delegate the creation of a per-test temporary directory: .. code-block:: python # content of test_unittest_cleandir.py + import os import pytest import unittest class MyTest(unittest.TestCase): @pytest.fixture(autouse=True) - def initdir(self, tmpdir): - tmpdir.chdir() # change to pytest-provided temporary directory - tmpdir.join("samplefile.ini").write("# testdata") + def initdir(self, tmp_path, monkeypatch): + monkeypatch.chdir(tmp_path) # change to pytest-provided temporary directory + tmp_path.joinpath("samplefile.ini").write_text("# testdata") def test_method(self): with open("samplefile.ini") as f: diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index 699738e1282..df106abb330 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -875,7 +875,7 @@ def test_something(pytester): def syspathinsert( self, path: Optional[Union[str, "os.PathLike[str]"]] = None ) -> None: - """Prepend a directory to sys.path, defaults to :py:attr:`tmpdir`. + """Prepend a directory to sys.path, defaults to :attr:`path`. This is undone automatically when this object dies at the end of each test. @@ -964,7 +964,7 @@ def getnode( """ session = Session.from_config(config) assert "::" not in str(arg) - p = legacy_path(arg) + p = Path(os.path.abspath(arg)) config.hook.pytest_sessionstart(session=session) res = session.perform_collect([str(p)], genitems=False)[0] config.hook.pytest_sessionfinish(session=session, exitstatus=ExitCode.OK) diff --git a/src/_pytest/tmpdir.py b/src/_pytest/tmpdir.py index d30e1e57feb..63cfa4c2365 100644 --- a/src/_pytest/tmpdir.py +++ b/src/_pytest/tmpdir.py @@ -166,11 +166,11 @@ def get_user() -> Optional[str]: def pytest_configure(config: Config) -> None: - """Create a TempdirFactory and attach it to the config object. + """Create a TempPathFactory and attach it to the config object. This is to comply with existing plugins which expect the handler to be available at pytest_configure time, but ideally should be moved entirely - to the tmpdir_factory session fixture. + to the tmp_path_factory session fixture. """ mp = MonkeyPatch() tmppath_handler = TempPathFactory.from_config(config, _ispytest=True) @@ -182,14 +182,14 @@ def pytest_configure(config: Config) -> None: @fixture(scope="session") def tmpdir_factory(request: FixtureRequest) -> TempdirFactory: - """Return a :class:`_pytest.tmpdir.TempdirFactory` instance for the test session.""" + """Return a :class:`pytest.TempdirFactory` instance for the test session.""" # Set dynamically by pytest_configure() above. return request.config._tmpdirhandler # type: ignore @fixture(scope="session") def tmp_path_factory(request: FixtureRequest) -> TempPathFactory: - """Return a :class:`_pytest.tmpdir.TempPathFactory` instance for the test session.""" + """Return a :class:`pytest.TempPathFactory` instance for the test session.""" # Set dynamically by pytest_configure() above. return request.config._tmp_path_factory # type: ignore diff --git a/testing/example_scripts/tmpdir/tmp_path_fixture.py b/testing/example_scripts/tmpdir/tmp_path_fixture.py new file mode 100644 index 00000000000..8675eb2fa62 --- /dev/null +++ b/testing/example_scripts/tmpdir/tmp_path_fixture.py @@ -0,0 +1,7 @@ +import pytest + + +@pytest.mark.parametrize("a", [r"qwe/\abc"]) +def test_fixture(tmp_path, a): + assert tmp_path.is_dir() + assert list(tmp_path.iterdir()) == [] diff --git a/testing/example_scripts/tmpdir/tmpdir_fixture.py b/testing/example_scripts/tmpdir/tmpdir_fixture.py deleted file mode 100644 index f4ad07462cb..00000000000 --- a/testing/example_scripts/tmpdir/tmpdir_fixture.py +++ /dev/null @@ -1,7 +0,0 @@ -import pytest - - -@pytest.mark.parametrize("a", [r"qwe/\abc"]) -def test_fixture(tmpdir, a): - tmpdir.check(dir=1) - assert tmpdir.listdir() == [] diff --git a/testing/python/fixtures.py b/testing/python/fixtures.py index e62143e5c18..8d0dc538aa5 100644 --- a/testing/python/fixtures.py +++ b/testing/python/fixtures.py @@ -982,11 +982,11 @@ def arg1(): def farg(arg1): pass @pytest.fixture(autouse=True) - def sarg(tmpdir): + def sarg(tmp_path): pass def test_function(request, farg): assert set(get_public_names(request.fixturenames)) == \ - set(["tmpdir", "sarg", "arg1", "request", "farg", + set(["sarg", "arg1", "request", "farg", "tmp_path", "tmp_path_factory"]) """ ) @@ -1059,7 +1059,7 @@ def arg1(): def test_show_fixtures_color_yes(self, pytester: Pytester) -> None: pytester.makepyfile("def test_this(): assert 1") result = pytester.runpytest("--color=yes", "--fixtures") - assert "\x1b[32mtmpdir" in result.stdout.str() + assert "\x1b[32mtmp_path" in result.stdout.str() def test_newstyle_with_request(self, pytester: Pytester) -> None: pytester.makepyfile( @@ -1752,11 +1752,11 @@ def pytester(self, pytester: Pytester) -> Pytester: """ import pytest @pytest.fixture(autouse=True) - def perfunction(request, tmpdir): + def perfunction(request, tmp_path): pass @pytest.fixture() - def arg1(tmpdir): + def arg1(tmp_path): pass @pytest.fixture(autouse=True) def perfunction2(arg1): @@ -3334,9 +3334,9 @@ def test_show_fixtures(self, pytester: Pytester) -> None: result = pytester.runpytest("--fixtures") result.stdout.fnmatch_lines( [ - "tmpdir_factory [[]session scope[]]", + "tmp_path_factory [[]session scope[]]", "*for the test session*", - "tmpdir", + "tmp_path", "*temporary directory*", ] ) @@ -3345,9 +3345,9 @@ def test_show_fixtures_verbose(self, pytester: Pytester) -> None: result = pytester.runpytest("--fixtures", "-v") result.stdout.fnmatch_lines( [ - "tmpdir_factory [[]session scope[]] -- *tmpdir.py*", + "tmp_path_factory [[]session scope[]] -- *tmpdir.py*", "*for the test session*", - "tmpdir -- *tmpdir.py*", + "tmp_path -- *tmpdir.py*", "*temporary directory*", ] ) @@ -3367,7 +3367,7 @@ def arg1(): result = pytester.runpytest("--fixtures", p) result.stdout.fnmatch_lines( """ - *tmpdir + *tmp_path *fixtures defined from* *arg1* *hello world* @@ -3395,7 +3395,7 @@ def test_hello(): result = pytester.runpytest("--fixtures") result.stdout.fnmatch_lines( """ - *tmpdir* + *tmp_path* *fixtures defined from*conftest* *arg1* *hello world* @@ -4000,15 +4000,15 @@ def m1(): FIXTURE_ORDER.append('m1') @pytest.fixture(scope='session') - def my_tmpdir_factory(): - FIXTURE_ORDER.append('my_tmpdir_factory') + def my_tmp_path_factory(): + FIXTURE_ORDER.append('my_tmp_path_factory') @pytest.fixture - def my_tmpdir(my_tmpdir_factory): - FIXTURE_ORDER.append('my_tmpdir') + def my_tmp_path(my_tmp_path_factory): + FIXTURE_ORDER.append('my_tmp_path') @pytest.fixture - def f1(my_tmpdir): + def f1(my_tmp_path): FIXTURE_ORDER.append('f1') @pytest.fixture @@ -4022,12 +4022,13 @@ def test_foo(f1, p1, m1, f2, s1): pass request = FixtureRequest(items[0], _ispytest=True) # order of fixtures based on their scope and position in the parameter list assert ( - request.fixturenames == "s1 my_tmpdir_factory p1 m1 f1 f2 my_tmpdir".split() + request.fixturenames + == "s1 my_tmp_path_factory p1 m1 f1 f2 my_tmp_path".split() ) pytester.runpytest() - # actual fixture execution differs: dependent fixtures must be created first ("my_tmpdir") + # actual fixture execution differs: dependent fixtures must be created first ("my_tmp_path") FIXTURE_ORDER = pytest.FIXTURE_ORDER # type: ignore[attr-defined] - assert FIXTURE_ORDER == "s1 my_tmpdir_factory p1 m1 my_tmpdir f1 f2".split() + assert FIXTURE_ORDER == "s1 my_tmp_path_factory p1 m1 my_tmp_path f1 f2".split() def test_func_closure_module(self, pytester: Pytester) -> None: pytester.makepyfile( diff --git a/testing/test_collection.py b/testing/test_collection.py index 5f0b5902ab8..f015578e2ba 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -1346,7 +1346,6 @@ def test_fscollector_from_parent(pytester: Pytester, request: FixtureRequest) -> Context: https://github.com/pytest-dev/pytest-cpp/pull/47 """ - from _pytest.compat import legacy_path class MyCollector(pytest.File): def __init__(self, *k, x, **kw): @@ -1354,7 +1353,7 @@ def __init__(self, *k, x, **kw): self.x = x collector = MyCollector.from_parent( - parent=request.session, fspath=legacy_path(pytester.path) / "foo", x=10 + parent=request.session, path=pytester.path / "foo", x=10 ) assert collector.x == 10 diff --git a/testing/test_config.py b/testing/test_config.py index b23264e1d79..fbeabaff69f 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -1488,7 +1488,6 @@ def pytest_addoption(parser): ) pytester.makepyfile( """ - import py.path def test_pathlist(pytestconfig): config_paths = pytestconfig.getini("paths") print(config_paths) diff --git a/testing/test_parseopt.py b/testing/test_parseopt.py index 6ba9269e564..1062c8c3725 100644 --- a/testing/test_parseopt.py +++ b/testing/test_parseopt.py @@ -3,9 +3,9 @@ import shlex import subprocess import sys +from pathlib import Path import pytest -from _pytest.compat import legacy_path from _pytest.config import argparsing as parseopt from _pytest.config.exceptions import UsageError from _pytest.monkeypatch import MonkeyPatch @@ -123,11 +123,11 @@ def test_parse(self, parser: parseopt.Parser) -> None: assert not getattr(args, parseopt.FILE_OR_DIR) def test_parse2(self, parser: parseopt.Parser) -> None: - args = parser.parse([legacy_path(".")]) - assert getattr(args, parseopt.FILE_OR_DIR)[0] == legacy_path(".") + args = parser.parse([Path(".")]) + assert getattr(args, parseopt.FILE_OR_DIR)[0] == "." def test_parse_known_args(self, parser: parseopt.Parser) -> None: - parser.parse_known_args([legacy_path(".")]) + parser.parse_known_args([Path(".")]) parser.addoption("--hello", action="store_true") ns = parser.parse_known_args(["x", "--y", "--hello", "this"]) assert ns.hello diff --git a/testing/test_pytester.py b/testing/test_pytester.py index 5823d51155c..7b16c69c23d 100644 --- a/testing/test_pytester.py +++ b/testing/test_pytester.py @@ -618,12 +618,12 @@ def test_linematcher_string_api() -> None: assert str(lm) == "foo\nbar" -def test_pytester_addopts_before_testdir(request, monkeypatch: MonkeyPatch) -> None: +def test_pytest_addopts_before_pytester(request, monkeypatch: MonkeyPatch) -> None: orig = os.environ.get("PYTEST_ADDOPTS", None) monkeypatch.setenv("PYTEST_ADDOPTS", "--orig-unused") - testdir = request.getfixturevalue("testdir") + pytester: Pytester = request.getfixturevalue("pytester") assert "PYTEST_ADDOPTS" not in os.environ - testdir.finalize() + pytester._finalize() assert os.environ.get("PYTEST_ADDOPTS") == "--orig-unused" monkeypatch.undo() assert os.environ.get("PYTEST_ADDOPTS") == orig diff --git a/testing/test_tmpdir.py b/testing/test_tmpdir.py index 4dec9c59a3c..63450eab248 100644 --- a/testing/test_tmpdir.py +++ b/testing/test_tmpdir.py @@ -21,12 +21,11 @@ from _pytest.pathlib import rm_rf from _pytest.pytester import Pytester from _pytest.tmpdir import get_user -from _pytest.tmpdir import TempdirFactory from _pytest.tmpdir import TempPathFactory -def test_tmpdir_fixture(pytester: Pytester) -> None: - p = pytester.copy_example("tmpdir/tmpdir_fixture.py") +def test_tmp_path_fixture(pytester: Pytester) -> None: + p = pytester.copy_example("tmpdir/tmp_path_fixture.py") results = pytester.runpytest(p) results.stdout.fnmatch_lines(["*1 passed*"]) @@ -47,18 +46,16 @@ def option(self): return self -class TestTempdirHandler: +class TestTmpPathHandler: def test_mktemp(self, tmp_path): config = cast(Config, FakeConfig(tmp_path)) - t = TempdirFactory( - TempPathFactory.from_config(config, _ispytest=True), _ispytest=True - ) + t = TempPathFactory.from_config(config, _ispytest=True) tmp = t.mktemp("world") - assert tmp.relto(t.getbasetemp()) == "world0" + assert str(tmp.relative_to(t.getbasetemp())) == "world0" tmp = t.mktemp("this") - assert tmp.relto(t.getbasetemp()).startswith("this") + assert str(tmp.relative_to(t.getbasetemp())).startswith("this") tmp2 = t.mktemp("this") - assert tmp2.relto(t.getbasetemp()).startswith("this") + assert str(tmp2.relative_to(t.getbasetemp())).startswith("this") assert tmp2 != tmp def test_tmppath_relative_basetemp_absolute(self, tmp_path, monkeypatch): @@ -69,12 +66,12 @@ def test_tmppath_relative_basetemp_absolute(self, tmp_path, monkeypatch): assert t.getbasetemp().resolve() == (tmp_path / "hello").resolve() -class TestConfigTmpdir: +class TestConfigTmpPath: def test_getbasetemp_custom_removes_old(self, pytester: Pytester) -> None: mytemp = pytester.path.joinpath("xyz") p = pytester.makepyfile( """ - def test_1(tmpdir): + def test_1(tmp_path): pass """ ) @@ -104,8 +101,8 @@ def test_mktemp(pytester: Pytester, basename: str, is_ok: bool) -> None: mytemp = pytester.mkdir("mytemp") p = pytester.makepyfile( """ - def test_abs_path(tmpdir_factory): - tmpdir_factory.mktemp('{}', numbered=False) + def test_abs_path(tmp_path_factory): + tmp_path_factory.mktemp('{}', numbered=False) """.format( basename ) @@ -157,44 +154,44 @@ def test_1(tmp_path): reprec.assertoutcome(passed=1) -def test_tmpdir_too_long_on_parametrization(pytester: Pytester) -> None: +def test_tmp_path_too_long_on_parametrization(pytester: Pytester) -> None: pytester.makepyfile( """ import pytest @pytest.mark.parametrize("arg", ["1"*1000]) - def test_some(arg, tmpdir): - tmpdir.ensure("hello") + def test_some(arg, tmp_path): + tmp_path.joinpath("hello").touch() """ ) reprec = pytester.inline_run() reprec.assertoutcome(passed=1) -def test_tmpdir_factory(pytester: Pytester) -> None: +def test_tmp_path_factory(pytester: Pytester) -> None: pytester.makepyfile( """ import pytest @pytest.fixture(scope='session') - def session_dir(tmpdir_factory): - return tmpdir_factory.mktemp('data', numbered=False) + def session_dir(tmp_path_factory): + return tmp_path_factory.mktemp('data', numbered=False) def test_some(session_dir): - assert session_dir.isdir() + assert session_dir.is_dir() """ ) reprec = pytester.inline_run() reprec.assertoutcome(passed=1) -def test_tmpdir_fallback_tox_env(pytester: Pytester, monkeypatch) -> None: - """Test that tmpdir works even if environment variables required by getpass +def test_tmp_path_fallback_tox_env(pytester: Pytester, monkeypatch) -> None: + """Test that tmp_path works even if environment variables required by getpass module are missing (#1010). """ monkeypatch.delenv("USER", raising=False) monkeypatch.delenv("USERNAME", raising=False) pytester.makepyfile( """ - def test_some(tmpdir): - assert tmpdir.isdir() + def test_some(tmp_path): + assert tmp_path.is_dir() """ ) reprec = pytester.inline_run() @@ -211,15 +208,15 @@ def break_getuser(monkeypatch): @pytest.mark.usefixtures("break_getuser") @pytest.mark.skipif(sys.platform.startswith("win"), reason="no os.getuid on windows") -def test_tmpdir_fallback_uid_not_found(pytester: Pytester) -> None: - """Test that tmpdir works even if the current process's user id does not +def test_tmp_path_fallback_uid_not_found(pytester: Pytester) -> None: + """Test that tmp_path works even if the current process's user id does not correspond to a valid user. """ pytester.makepyfile( """ - def test_some(tmpdir): - assert tmpdir.isdir() + def test_some(tmp_path): + assert tmp_path.is_dir() """ ) reprec = pytester.inline_run() From d171a3f52d93ab912908aff28ea26ac41615a576 Mon Sep 17 00:00:00 2001 From: Daniele Procida Date: Sat, 13 Mar 2021 23:11:48 +0000 Subject: [PATCH 183/630] Moved more sections from reference to how-to. * re-ordered and grouped how-to guides in how-to landing page * retitled moved documents appropriately * separated writing plugins/writing hook functions into two documents --- doc/en/contents.rst | 29 +- doc/en/explanation/fixtures.rst | 1 + doc/en/how-to/assert.rst | 6 +- doc/en/{reference => how-to}/cache.rst | 4 +- ...{capture.rst => capture-stdout-stderr.rst} | 0 .../capture-warnings.rst} | 4 +- doc/en/{reference => how-to}/doctest.rst | 2 +- doc/en/how-to/index.rst | 51 ++- doc/en/{reference => how-to}/logging.rst | 7 +- doc/en/{reference => how-to}/unittest.rst | 4 +- doc/en/how-to/writing_hook_functions.rst | 313 +++++++++++++++++ .../{reference => how-to}/writing_plugins.rst | 317 ------------------ doc/en/{reference => how-to}/xunit_setup.rst | 2 +- doc/en/reference/fixtures.rst | 2 +- doc/en/reference/index.rst | 7 - 15 files changed, 390 insertions(+), 359 deletions(-) rename doc/en/{reference => how-to}/cache.rst (99%) rename doc/en/how-to/{capture.rst => capture-stdout-stderr.rst} (100%) rename doc/en/{reference/warnings.rst => how-to/capture-warnings.rst} (99%) rename doc/en/{reference => how-to}/doctest.rst (99%) rename doc/en/{reference => how-to}/logging.rst (99%) rename doc/en/{reference => how-to}/unittest.rst (99%) create mode 100644 doc/en/how-to/writing_hook_functions.rst rename doc/en/{reference => how-to}/writing_plugins.rst (59%) rename doc/en/{reference => how-to}/xunit_setup.rst (99%) diff --git a/doc/en/contents.rst b/doc/en/contents.rst index 16c8f273b62..e1854d800af 100644 --- a/doc/en/contents.rst +++ b/doc/en/contents.rst @@ -24,18 +24,30 @@ How-to guides :maxdepth: 2 how-to/usage - how-to/existingtestsuite how-to/assert + how-to/fixtures how-to/mark - how-to/monkeypatch + how-to/parametrize how-to/tmpdir - how-to/capture + how-to/monkeypatch + how-to/doctest + how-to/cache + + how-to/logging + how-to/capture-stdout-stderr + how-to/capture-warnings how-to/skipping - how-to/parametrize + how-to/plugins + how-to/writing_plugins + how-to/writing_hook_functions + + how-to/existingtestsuite + how-to/unittest how-to/nose + how-to/xunit_setup + how-to/bash-completion - how-to/fixtures Reference guides @@ -45,14 +57,7 @@ Reference guides :maxdepth: 2 reference/fixtures - reference/warnings - reference/doctest - reference/cache - reference/unittest - reference/xunit_setup reference/plugin_list - reference/writing_plugins - reference/logging reference/customize reference/reference diff --git a/doc/en/explanation/fixtures.rst b/doc/en/explanation/fixtures.rst index 7d53ba0892f..92b5e797856 100644 --- a/doc/en/explanation/fixtures.rst +++ b/doc/en/explanation/fixtures.rst @@ -6,6 +6,7 @@ About fixtures .. seealso:: :ref:`how-to-fixtures` .. seealso:: :ref:`Fixtures reference ` +pytest fixtures are designed to be explicit, modular and scalable. What fixtures are ----------------- diff --git a/doc/en/how-to/assert.rst b/doc/en/how-to/assert.rst index 34b765b3b98..9269743dbda 100644 --- a/doc/en/how-to/assert.rst +++ b/doc/en/how-to/assert.rst @@ -1,16 +1,14 @@ +.. _`assert`: How to write and report assertions in tests ================================================== -.. _`assertfeedback`: .. _`assert with the assert statement`: -.. _`assert`: - Asserting with the ``assert`` statement --------------------------------------------------------- -``pytest`` allows you to use the standard python ``assert`` for verifying +``pytest`` allows you to use the standard Python ``assert`` for verifying expectations and values in Python tests. For example, you can write the following: diff --git a/doc/en/reference/cache.rst b/doc/en/how-to/cache.rst similarity index 99% rename from doc/en/reference/cache.rst rename to doc/en/how-to/cache.rst index 42ca473545d..ae865fe280e 100644 --- a/doc/en/reference/cache.rst +++ b/doc/en/how-to/cache.rst @@ -2,8 +2,8 @@ .. _cache: -Cache: working with cross-testrun state -======================================= +How to re-run failed tests and maintain state between test runs +=============================================================== diff --git a/doc/en/how-to/capture.rst b/doc/en/how-to/capture-stdout-stderr.rst similarity index 100% rename from doc/en/how-to/capture.rst rename to doc/en/how-to/capture-stdout-stderr.rst diff --git a/doc/en/reference/warnings.rst b/doc/en/how-to/capture-warnings.rst similarity index 99% rename from doc/en/reference/warnings.rst rename to doc/en/how-to/capture-warnings.rst index 5bbbcacbea0..1bafaeeb900 100644 --- a/doc/en/reference/warnings.rst +++ b/doc/en/how-to/capture-warnings.rst @@ -1,7 +1,7 @@ .. _`warnings`: -Warnings Capture -================ +How to capture warnings +======================= diff --git a/doc/en/reference/doctest.rst b/doc/en/how-to/doctest.rst similarity index 99% rename from doc/en/reference/doctest.rst rename to doc/en/how-to/doctest.rst index e12f02b6246..559d7c35ccf 100644 --- a/doc/en/reference/doctest.rst +++ b/doc/en/how-to/doctest.rst @@ -1,6 +1,6 @@ .. _doctest: -Doctest integration for modules and test files +How to run doctests ========================================================= By default, all files matching the ``test*.txt`` pattern will diff --git a/doc/en/how-to/index.rst b/doc/en/how-to/index.rst index c206a30570a..0f3d4957406 100644 --- a/doc/en/how-to/index.rst +++ b/doc/en/how-to/index.rst @@ -3,19 +3,60 @@ How-to guides ================ +Core pytest functionality +------------------------- + .. toctree:: :maxdepth: 1 usage - existingtestsuite assert + fixtures mark - monkeypatch + parametrize tmpdir - capture + monkeypatch + doctest + cache + +test output and outcomes +---------------------------- + +.. toctree:: + :maxdepth: 1 + + logging + capture-stdout-stderr + capture-warnings skipping - parametrize + +Plugins +---------------------------- + +.. toctree:: + :maxdepth: 1 + plugins + writing_plugins + writing_hook_functions + +pytest and other test systems +----------------------------- + +.. toctree:: + :maxdepth: 1 + + existingtestsuite + unittest nose + xunit_setup + +pytest development environment +------------------------------ + +.. toctree:: + :maxdepth: 1 + bash-completion - fixtures + + diff --git a/doc/en/reference/logging.rst b/doc/en/how-to/logging.rst similarity index 99% rename from doc/en/reference/logging.rst rename to doc/en/how-to/logging.rst index 52713854efb..317d6134e1b 100644 --- a/doc/en/reference/logging.rst +++ b/doc/en/how-to/logging.rst @@ -1,10 +1,7 @@ .. _logging: -Logging -------- - - - +How to manage logging +--------------------- pytest captures log messages of level ``WARNING`` or above automatically and displays them in their own section for each failed test in the same manner as captured stdout and stderr. diff --git a/doc/en/reference/unittest.rst b/doc/en/how-to/unittest.rst similarity index 99% rename from doc/en/reference/unittest.rst rename to doc/en/how-to/unittest.rst index 92c1bbfa2f1..2e5763f834c 100644 --- a/doc/en/reference/unittest.rst +++ b/doc/en/how-to/unittest.rst @@ -2,8 +2,8 @@ .. _`unittest.TestCase`: .. _`unittest`: -unittest.TestCase Support -========================= +How to use ``unittest``-based tests with pytest +=============================================== ``pytest`` supports running Python ``unittest``-based tests out of the box. It's meant for leveraging existing ``unittest``-based test suites diff --git a/doc/en/how-to/writing_hook_functions.rst b/doc/en/how-to/writing_hook_functions.rst new file mode 100644 index 00000000000..de2ff3bdc10 --- /dev/null +++ b/doc/en/how-to/writing_hook_functions.rst @@ -0,0 +1,313 @@ +.. _`writinghooks`: + +Writing hook functions +====================== + + +.. _validation: + +hook function validation and execution +-------------------------------------- + +pytest calls hook functions from registered plugins for any +given hook specification. Let's look at a typical hook function +for the ``pytest_collection_modifyitems(session, config, +items)`` hook which pytest calls after collection of all test items is +completed. + +When we implement a ``pytest_collection_modifyitems`` function in our plugin +pytest will during registration verify that you use argument +names which match the specification and bail out if not. + +Let's look at a possible implementation: + +.. code-block:: python + + def pytest_collection_modifyitems(config, items): + # called after collection is completed + # you can modify the ``items`` list + ... + +Here, ``pytest`` will pass in ``config`` (the pytest config object) +and ``items`` (the list of collected test items) but will not pass +in the ``session`` argument because we didn't list it in the function +signature. This dynamic "pruning" of arguments allows ``pytest`` to +be "future-compatible": we can introduce new hook named parameters without +breaking the signatures of existing hook implementations. It is one of +the reasons for the general long-lived compatibility of pytest plugins. + +Note that hook functions other than ``pytest_runtest_*`` are not +allowed to raise exceptions. Doing so will break the pytest run. + + + +.. _firstresult: + +firstresult: stop at first non-None result +------------------------------------------- + +Most calls to ``pytest`` hooks result in a **list of results** which contains +all non-None results of the called hook functions. + +Some hook specifications use the ``firstresult=True`` option so that the hook +call only executes until the first of N registered functions returns a +non-None result which is then taken as result of the overall hook call. +The remaining hook functions will not be called in this case. + +.. _`hookwrapper`: + +hookwrapper: executing around other hooks +------------------------------------------------- + +.. currentmodule:: _pytest.core + + + +pytest plugins can implement hook wrappers which wrap the execution +of other hook implementations. A hook wrapper is a generator function +which yields exactly once. When pytest invokes hooks it first executes +hook wrappers and passes the same arguments as to the regular hooks. + +At the yield point of the hook wrapper pytest will execute the next hook +implementations and return their result to the yield point in the form of +a :py:class:`Result ` instance which encapsulates a result or +exception info. The yield point itself will thus typically not raise +exceptions (unless there are bugs). + +Here is an example definition of a hook wrapper: + +.. code-block:: python + + import pytest + + + @pytest.hookimpl(hookwrapper=True) + def pytest_pyfunc_call(pyfuncitem): + do_something_before_next_hook_executes() + + outcome = yield + # outcome.excinfo may be None or a (cls, val, tb) tuple + + res = outcome.get_result() # will raise if outcome was exception + + post_process_result(res) + + outcome.force_result(new_res) # to override the return value to the plugin system + +Note that hook wrappers don't return results themselves, they merely +perform tracing or other side effects around the actual hook implementations. +If the result of the underlying hook is a mutable object, they may modify +that result but it's probably better to avoid it. + +For more information, consult the +:ref:`pluggy documentation about hookwrappers `. + +.. _plugin-hookorder: + +Hook function ordering / call example +------------------------------------- + +For any given hook specification there may be more than one +implementation and we thus generally view ``hook`` execution as a +``1:N`` function call where ``N`` is the number of registered functions. +There are ways to influence if a hook implementation comes before or +after others, i.e. the position in the ``N``-sized list of functions: + +.. code-block:: python + + # Plugin 1 + @pytest.hookimpl(tryfirst=True) + def pytest_collection_modifyitems(items): + # will execute as early as possible + ... + + + # Plugin 2 + @pytest.hookimpl(trylast=True) + def pytest_collection_modifyitems(items): + # will execute as late as possible + ... + + + # Plugin 3 + @pytest.hookimpl(hookwrapper=True) + def pytest_collection_modifyitems(items): + # will execute even before the tryfirst one above! + outcome = yield + # will execute after all non-hookwrappers executed + +Here is the order of execution: + +1. Plugin3's pytest_collection_modifyitems called until the yield point + because it is a hook wrapper. + +2. Plugin1's pytest_collection_modifyitems is called because it is marked + with ``tryfirst=True``. + +3. Plugin2's pytest_collection_modifyitems is called because it is marked + with ``trylast=True`` (but even without this mark it would come after + Plugin1). + +4. Plugin3's pytest_collection_modifyitems then executing the code after the yield + point. The yield receives a :py:class:`Result ` instance which encapsulates + the result from calling the non-wrappers. Wrappers shall not modify the result. + +It's possible to use ``tryfirst`` and ``trylast`` also in conjunction with +``hookwrapper=True`` in which case it will influence the ordering of hookwrappers +among each other. + + +Declaring new hooks +------------------------ + +.. note:: + + This is a quick overview on how to add new hooks and how they work in general, but a more complete + overview can be found in `the pluggy documentation `__. + +.. currentmodule:: _pytest.hookspec + +Plugins and ``conftest.py`` files may declare new hooks that can then be +implemented by other plugins in order to alter behaviour or interact with +the new plugin: + +.. autofunction:: pytest_addhooks + :noindex: + +Hooks are usually declared as do-nothing functions that contain only +documentation describing when the hook will be called and what return values +are expected. The names of the functions must start with `pytest_` otherwise pytest won't recognize them. + +Here's an example. Let's assume this code is in the ``sample_hook.py`` module. + +.. code-block:: python + + def pytest_my_hook(config): + """ + Receives the pytest config and does things with it + """ + +To register the hooks with pytest they need to be structured in their own module or class. This +class or module can then be passed to the ``pluginmanager`` using the ``pytest_addhooks`` function +(which itself is a hook exposed by pytest). + +.. code-block:: python + + def pytest_addhooks(pluginmanager): + """ This example assumes the hooks are grouped in the 'sample_hook' module. """ + from my_app.tests import sample_hook + + pluginmanager.add_hookspecs(sample_hook) + +For a real world example, see `newhooks.py`_ from `xdist `_. + +.. _`newhooks.py`: https://github.com/pytest-dev/pytest-xdist/blob/974bd566c599dc6a9ea291838c6f226197208b46/xdist/newhooks.py + +Hooks may be called both from fixtures or from other hooks. In both cases, hooks are called +through the ``hook`` object, available in the ``config`` object. Most hooks receive a +``config`` object directly, while fixtures may use the ``pytestconfig`` fixture which provides the same object. + +.. code-block:: python + + @pytest.fixture() + def my_fixture(pytestconfig): + # call the hook called "pytest_my_hook" + # 'result' will be a list of return values from all registered functions. + result = pytestconfig.hook.pytest_my_hook(config=pytestconfig) + +.. note:: + Hooks receive parameters using only keyword arguments. + +Now your hook is ready to be used. To register a function at the hook, other plugins or users must +now simply define the function ``pytest_my_hook`` with the correct signature in their ``conftest.py``. + +Example: + +.. code-block:: python + + def pytest_my_hook(config): + """ + Print all active hooks to the screen. + """ + print(config.hook) + + +.. _`addoptionhooks`: + + +Using hooks in pytest_addoption +------------------------------- + +Occasionally, it is necessary to change the way in which command line options +are defined by one plugin based on hooks in another plugin. For example, +a plugin may expose a command line option for which another plugin needs +to define the default value. The pluginmanager can be used to install and +use hooks to accomplish this. The plugin would define and add the hooks +and use pytest_addoption as follows: + +.. code-block:: python + + # contents of hooks.py + + # Use firstresult=True because we only want one plugin to define this + # default value + @hookspec(firstresult=True) + def pytest_config_file_default_value(): + """ Return the default value for the config file command line option. """ + + + # contents of myplugin.py + + + def pytest_addhooks(pluginmanager): + """ This example assumes the hooks are grouped in the 'hooks' module. """ + from . import hook + + pluginmanager.add_hookspecs(hook) + + + def pytest_addoption(parser, pluginmanager): + default_value = pluginmanager.hook.pytest_config_file_default_value() + parser.addoption( + "--config-file", + help="Config file to use, defaults to %(default)s", + default=default_value, + ) + +The conftest.py that is using myplugin would simply define the hook as follows: + +.. code-block:: python + + def pytest_config_file_default_value(): + return "config.yaml" + + +Optionally using hooks from 3rd party plugins +--------------------------------------------- + +Using new hooks from plugins as explained above might be a little tricky +because of the standard :ref:`validation mechanism `: +if you depend on a plugin that is not installed, validation will fail and +the error message will not make much sense to your users. + +One approach is to defer the hook implementation to a new plugin instead of +declaring the hook functions directly in your plugin module, for example: + +.. code-block:: python + + # contents of myplugin.py + + + class DeferPlugin: + """Simple plugin to defer pytest-xdist hook functions.""" + + def pytest_testnodedown(self, node, error): + """standard xdist hook function.""" + + + def pytest_configure(config): + if config.pluginmanager.hasplugin("xdist"): + config.pluginmanager.register(DeferPlugin()) + +This has the added benefit of allowing you to conditionally install hooks +depending on which plugins are installed. diff --git a/doc/en/reference/writing_plugins.rst b/doc/en/how-to/writing_plugins.rst similarity index 59% rename from doc/en/reference/writing_plugins.rst rename to doc/en/how-to/writing_plugins.rst index b4d9f49ff46..8ec935cc2a3 100644 --- a/doc/en/reference/writing_plugins.rst +++ b/doc/en/how-to/writing_plugins.rst @@ -454,320 +454,3 @@ Additionally it is possible to copy examples for an example folder before runnin For more information about the result object that ``runpytest()`` returns, and the methods that it provides please check out the :py:class:`RunResult <_pytest.pytester.RunResult>` documentation. - - - - -.. _`writinghooks`: - -Writing hook functions -====================== - - -.. _validation: - -hook function validation and execution --------------------------------------- - -pytest calls hook functions from registered plugins for any -given hook specification. Let's look at a typical hook function -for the ``pytest_collection_modifyitems(session, config, -items)`` hook which pytest calls after collection of all test items is -completed. - -When we implement a ``pytest_collection_modifyitems`` function in our plugin -pytest will during registration verify that you use argument -names which match the specification and bail out if not. - -Let's look at a possible implementation: - -.. code-block:: python - - def pytest_collection_modifyitems(config, items): - # called after collection is completed - # you can modify the ``items`` list - ... - -Here, ``pytest`` will pass in ``config`` (the pytest config object) -and ``items`` (the list of collected test items) but will not pass -in the ``session`` argument because we didn't list it in the function -signature. This dynamic "pruning" of arguments allows ``pytest`` to -be "future-compatible": we can introduce new hook named parameters without -breaking the signatures of existing hook implementations. It is one of -the reasons for the general long-lived compatibility of pytest plugins. - -Note that hook functions other than ``pytest_runtest_*`` are not -allowed to raise exceptions. Doing so will break the pytest run. - - - -.. _firstresult: - -firstresult: stop at first non-None result -------------------------------------------- - -Most calls to ``pytest`` hooks result in a **list of results** which contains -all non-None results of the called hook functions. - -Some hook specifications use the ``firstresult=True`` option so that the hook -call only executes until the first of N registered functions returns a -non-None result which is then taken as result of the overall hook call. -The remaining hook functions will not be called in this case. - -.. _`hookwrapper`: - -hookwrapper: executing around other hooks -------------------------------------------------- - -.. currentmodule:: _pytest.core - - - -pytest plugins can implement hook wrappers which wrap the execution -of other hook implementations. A hook wrapper is a generator function -which yields exactly once. When pytest invokes hooks it first executes -hook wrappers and passes the same arguments as to the regular hooks. - -At the yield point of the hook wrapper pytest will execute the next hook -implementations and return their result to the yield point in the form of -a :py:class:`Result ` instance which encapsulates a result or -exception info. The yield point itself will thus typically not raise -exceptions (unless there are bugs). - -Here is an example definition of a hook wrapper: - -.. code-block:: python - - import pytest - - - @pytest.hookimpl(hookwrapper=True) - def pytest_pyfunc_call(pyfuncitem): - do_something_before_next_hook_executes() - - outcome = yield - # outcome.excinfo may be None or a (cls, val, tb) tuple - - res = outcome.get_result() # will raise if outcome was exception - - post_process_result(res) - - outcome.force_result(new_res) # to override the return value to the plugin system - -Note that hook wrappers don't return results themselves, they merely -perform tracing or other side effects around the actual hook implementations. -If the result of the underlying hook is a mutable object, they may modify -that result but it's probably better to avoid it. - -For more information, consult the -:ref:`pluggy documentation about hookwrappers `. - -.. _plugin-hookorder: - -Hook function ordering / call example -------------------------------------- - -For any given hook specification there may be more than one -implementation and we thus generally view ``hook`` execution as a -``1:N`` function call where ``N`` is the number of registered functions. -There are ways to influence if a hook implementation comes before or -after others, i.e. the position in the ``N``-sized list of functions: - -.. code-block:: python - - # Plugin 1 - @pytest.hookimpl(tryfirst=True) - def pytest_collection_modifyitems(items): - # will execute as early as possible - ... - - - # Plugin 2 - @pytest.hookimpl(trylast=True) - def pytest_collection_modifyitems(items): - # will execute as late as possible - ... - - - # Plugin 3 - @pytest.hookimpl(hookwrapper=True) - def pytest_collection_modifyitems(items): - # will execute even before the tryfirst one above! - outcome = yield - # will execute after all non-hookwrappers executed - -Here is the order of execution: - -1. Plugin3's pytest_collection_modifyitems called until the yield point - because it is a hook wrapper. - -2. Plugin1's pytest_collection_modifyitems is called because it is marked - with ``tryfirst=True``. - -3. Plugin2's pytest_collection_modifyitems is called because it is marked - with ``trylast=True`` (but even without this mark it would come after - Plugin1). - -4. Plugin3's pytest_collection_modifyitems then executing the code after the yield - point. The yield receives a :py:class:`Result ` instance which encapsulates - the result from calling the non-wrappers. Wrappers shall not modify the result. - -It's possible to use ``tryfirst`` and ``trylast`` also in conjunction with -``hookwrapper=True`` in which case it will influence the ordering of hookwrappers -among each other. - - -Declaring new hooks ------------------------- - -.. note:: - - This is a quick overview on how to add new hooks and how they work in general, but a more complete - overview can be found in `the pluggy documentation `__. - -.. currentmodule:: _pytest.hookspec - -Plugins and ``conftest.py`` files may declare new hooks that can then be -implemented by other plugins in order to alter behaviour or interact with -the new plugin: - -.. autofunction:: pytest_addhooks - :noindex: - -Hooks are usually declared as do-nothing functions that contain only -documentation describing when the hook will be called and what return values -are expected. The names of the functions must start with `pytest_` otherwise pytest won't recognize them. - -Here's an example. Let's assume this code is in the ``sample_hook.py`` module. - -.. code-block:: python - - def pytest_my_hook(config): - """ - Receives the pytest config and does things with it - """ - -To register the hooks with pytest they need to be structured in their own module or class. This -class or module can then be passed to the ``pluginmanager`` using the ``pytest_addhooks`` function -(which itself is a hook exposed by pytest). - -.. code-block:: python - - def pytest_addhooks(pluginmanager): - """ This example assumes the hooks are grouped in the 'sample_hook' module. """ - from my_app.tests import sample_hook - - pluginmanager.add_hookspecs(sample_hook) - -For a real world example, see `newhooks.py`_ from `xdist `_. - -.. _`newhooks.py`: https://github.com/pytest-dev/pytest-xdist/blob/974bd566c599dc6a9ea291838c6f226197208b46/xdist/newhooks.py - -Hooks may be called both from fixtures or from other hooks. In both cases, hooks are called -through the ``hook`` object, available in the ``config`` object. Most hooks receive a -``config`` object directly, while fixtures may use the ``pytestconfig`` fixture which provides the same object. - -.. code-block:: python - - @pytest.fixture() - def my_fixture(pytestconfig): - # call the hook called "pytest_my_hook" - # 'result' will be a list of return values from all registered functions. - result = pytestconfig.hook.pytest_my_hook(config=pytestconfig) - -.. note:: - Hooks receive parameters using only keyword arguments. - -Now your hook is ready to be used. To register a function at the hook, other plugins or users must -now simply define the function ``pytest_my_hook`` with the correct signature in their ``conftest.py``. - -Example: - -.. code-block:: python - - def pytest_my_hook(config): - """ - Print all active hooks to the screen. - """ - print(config.hook) - - -.. _`addoptionhooks`: - - -Using hooks in pytest_addoption -------------------------------- - -Occasionally, it is necessary to change the way in which command line options -are defined by one plugin based on hooks in another plugin. For example, -a plugin may expose a command line option for which another plugin needs -to define the default value. The pluginmanager can be used to install and -use hooks to accomplish this. The plugin would define and add the hooks -and use pytest_addoption as follows: - -.. code-block:: python - - # contents of hooks.py - - # Use firstresult=True because we only want one plugin to define this - # default value - @hookspec(firstresult=True) - def pytest_config_file_default_value(): - """ Return the default value for the config file command line option. """ - - - # contents of myplugin.py - - - def pytest_addhooks(pluginmanager): - """ This example assumes the hooks are grouped in the 'hooks' module. """ - from . import hook - - pluginmanager.add_hookspecs(hook) - - - def pytest_addoption(parser, pluginmanager): - default_value = pluginmanager.hook.pytest_config_file_default_value() - parser.addoption( - "--config-file", - help="Config file to use, defaults to %(default)s", - default=default_value, - ) - -The conftest.py that is using myplugin would simply define the hook as follows: - -.. code-block:: python - - def pytest_config_file_default_value(): - return "config.yaml" - - -Optionally using hooks from 3rd party plugins ---------------------------------------------- - -Using new hooks from plugins as explained above might be a little tricky -because of the standard :ref:`validation mechanism `: -if you depend on a plugin that is not installed, validation will fail and -the error message will not make much sense to your users. - -One approach is to defer the hook implementation to a new plugin instead of -declaring the hook functions directly in your plugin module, for example: - -.. code-block:: python - - # contents of myplugin.py - - - class DeferPlugin: - """Simple plugin to defer pytest-xdist hook functions.""" - - def pytest_testnodedown(self, node, error): - """standard xdist hook function.""" - - - def pytest_configure(config): - if config.pluginmanager.hasplugin("xdist"): - config.pluginmanager.register(DeferPlugin()) - -This has the added benefit of allowing you to conditionally install hooks -depending on which plugins are installed. diff --git a/doc/en/reference/xunit_setup.rst b/doc/en/how-to/xunit_setup.rst similarity index 99% rename from doc/en/reference/xunit_setup.rst rename to doc/en/how-to/xunit_setup.rst index 4fea863be63..2269c06e003 100644 --- a/doc/en/reference/xunit_setup.rst +++ b/doc/en/how-to/xunit_setup.rst @@ -2,7 +2,7 @@ .. _`classic xunit`: .. _xunitsetup: -classic xunit-style setup +How to implement xunit-style set-up ======================================== This section describes a classic and popular way how you can implement diff --git a/doc/en/reference/fixtures.rst b/doc/en/reference/fixtures.rst index 69e1d20b1c2..6d9b134a763 100644 --- a/doc/en/reference/fixtures.rst +++ b/doc/en/reference/fixtures.rst @@ -5,7 +5,7 @@ .. _`pytest.fixture`: -pytest fixtures: explicit, modular, scalable +Fixtures reference ======================================================== .. seealso:: :ref:`about-fixtures` diff --git a/doc/en/reference/index.rst b/doc/en/reference/index.rst index b62484cdf3c..fbff0978e88 100644 --- a/doc/en/reference/index.rst +++ b/doc/en/reference/index.rst @@ -7,13 +7,6 @@ Reference guides :maxdepth: 1 fixtures - warnings - doctest - cache - unittest - xunit_setup plugin_list - writing_plugins - logging customize reference From 64107be44386c913e331c7f9db7f4291ab27926c Mon Sep 17 00:00:00 2001 From: Daniele Procida Date: Sun, 14 Mar 2021 20:40:50 +0000 Subject: [PATCH 184/630] Amended plugin list script for moved file. --- doc/en/how-to/index.rst | 2 -- scripts/update-plugin-list.py | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/doc/en/how-to/index.rst b/doc/en/how-to/index.rst index 0f3d4957406..35b112994d9 100644 --- a/doc/en/how-to/index.rst +++ b/doc/en/how-to/index.rst @@ -58,5 +58,3 @@ pytest development environment :maxdepth: 1 bash-completion - - diff --git a/scripts/update-plugin-list.py b/scripts/update-plugin-list.py index f80e63127ee..bc4d8a6a66b 100644 --- a/scripts/update-plugin-list.py +++ b/scripts/update-plugin-list.py @@ -74,7 +74,7 @@ def iter_plugins(): def main(): plugins = list(iter_plugins()) plugin_table = tabulate.tabulate(plugins, headers="keys", tablefmt="rst") - plugin_list = pathlib.Path("doc", "en", "plugin_list.rst") + plugin_list = pathlib.Path("doc", "en", "reference", "plugin_list.rst") with plugin_list.open("w") as f: f.write(FILE_HEAD) f.write(f"This list contains {len(plugins)} plugins.\n\n") From 2641761c1c9d2bf7ffaa524400d7352350da379a Mon Sep 17 00:00:00 2001 From: Daniele Procida Date: Mon, 15 Mar 2021 08:22:11 +0000 Subject: [PATCH 185/630] Merge pull request #8441 from evildmp/evolutionary-documentation-restructure Moved more sections from reference to how-to. --- doc/en/contents.rst | 29 +- doc/en/explanation/fixtures.rst | 1 + doc/en/how-to/assert.rst | 6 +- doc/en/{reference => how-to}/cache.rst | 4 +- ...{capture.rst => capture-stdout-stderr.rst} | 0 .../capture-warnings.rst} | 4 +- doc/en/{reference => how-to}/doctest.rst | 2 +- doc/en/how-to/index.rst | 49 +- doc/en/{reference => how-to}/logging.rst | 7 +- doc/en/{reference => how-to}/unittest.rst | 4 +- doc/en/how-to/writing_hook_functions.rst | 313 +++++++ .../{reference => how-to}/writing_plugins.rst | 317 ------- doc/en/{reference => how-to}/xunit_setup.rst | 2 +- doc/en/plugin_list.rst | 851 ------------------ doc/en/reference/fixtures.rst | 2 +- doc/en/reference/index.rst | 7 - scripts/update-plugin-list.py | 2 +- 17 files changed, 389 insertions(+), 1211 deletions(-) rename doc/en/{reference => how-to}/cache.rst (99%) rename doc/en/how-to/{capture.rst => capture-stdout-stderr.rst} (100%) rename doc/en/{reference/warnings.rst => how-to/capture-warnings.rst} (99%) rename doc/en/{reference => how-to}/doctest.rst (99%) rename doc/en/{reference => how-to}/logging.rst (99%) rename doc/en/{reference => how-to}/unittest.rst (99%) create mode 100644 doc/en/how-to/writing_hook_functions.rst rename doc/en/{reference => how-to}/writing_plugins.rst (59%) rename doc/en/{reference => how-to}/xunit_setup.rst (99%) delete mode 100644 doc/en/plugin_list.rst diff --git a/doc/en/contents.rst b/doc/en/contents.rst index 16c8f273b62..e1854d800af 100644 --- a/doc/en/contents.rst +++ b/doc/en/contents.rst @@ -24,18 +24,30 @@ How-to guides :maxdepth: 2 how-to/usage - how-to/existingtestsuite how-to/assert + how-to/fixtures how-to/mark - how-to/monkeypatch + how-to/parametrize how-to/tmpdir - how-to/capture + how-to/monkeypatch + how-to/doctest + how-to/cache + + how-to/logging + how-to/capture-stdout-stderr + how-to/capture-warnings how-to/skipping - how-to/parametrize + how-to/plugins + how-to/writing_plugins + how-to/writing_hook_functions + + how-to/existingtestsuite + how-to/unittest how-to/nose + how-to/xunit_setup + how-to/bash-completion - how-to/fixtures Reference guides @@ -45,14 +57,7 @@ Reference guides :maxdepth: 2 reference/fixtures - reference/warnings - reference/doctest - reference/cache - reference/unittest - reference/xunit_setup reference/plugin_list - reference/writing_plugins - reference/logging reference/customize reference/reference diff --git a/doc/en/explanation/fixtures.rst b/doc/en/explanation/fixtures.rst index 7d53ba0892f..92b5e797856 100644 --- a/doc/en/explanation/fixtures.rst +++ b/doc/en/explanation/fixtures.rst @@ -6,6 +6,7 @@ About fixtures .. seealso:: :ref:`how-to-fixtures` .. seealso:: :ref:`Fixtures reference ` +pytest fixtures are designed to be explicit, modular and scalable. What fixtures are ----------------- diff --git a/doc/en/how-to/assert.rst b/doc/en/how-to/assert.rst index 34b765b3b98..9269743dbda 100644 --- a/doc/en/how-to/assert.rst +++ b/doc/en/how-to/assert.rst @@ -1,16 +1,14 @@ +.. _`assert`: How to write and report assertions in tests ================================================== -.. _`assertfeedback`: .. _`assert with the assert statement`: -.. _`assert`: - Asserting with the ``assert`` statement --------------------------------------------------------- -``pytest`` allows you to use the standard python ``assert`` for verifying +``pytest`` allows you to use the standard Python ``assert`` for verifying expectations and values in Python tests. For example, you can write the following: diff --git a/doc/en/reference/cache.rst b/doc/en/how-to/cache.rst similarity index 99% rename from doc/en/reference/cache.rst rename to doc/en/how-to/cache.rst index 42ca473545d..ae865fe280e 100644 --- a/doc/en/reference/cache.rst +++ b/doc/en/how-to/cache.rst @@ -2,8 +2,8 @@ .. _cache: -Cache: working with cross-testrun state -======================================= +How to re-run failed tests and maintain state between test runs +=============================================================== diff --git a/doc/en/how-to/capture.rst b/doc/en/how-to/capture-stdout-stderr.rst similarity index 100% rename from doc/en/how-to/capture.rst rename to doc/en/how-to/capture-stdout-stderr.rst diff --git a/doc/en/reference/warnings.rst b/doc/en/how-to/capture-warnings.rst similarity index 99% rename from doc/en/reference/warnings.rst rename to doc/en/how-to/capture-warnings.rst index 5bbbcacbea0..1bafaeeb900 100644 --- a/doc/en/reference/warnings.rst +++ b/doc/en/how-to/capture-warnings.rst @@ -1,7 +1,7 @@ .. _`warnings`: -Warnings Capture -================ +How to capture warnings +======================= diff --git a/doc/en/reference/doctest.rst b/doc/en/how-to/doctest.rst similarity index 99% rename from doc/en/reference/doctest.rst rename to doc/en/how-to/doctest.rst index e12f02b6246..559d7c35ccf 100644 --- a/doc/en/reference/doctest.rst +++ b/doc/en/how-to/doctest.rst @@ -1,6 +1,6 @@ .. _doctest: -Doctest integration for modules and test files +How to run doctests ========================================================= By default, all files matching the ``test*.txt`` pattern will diff --git a/doc/en/how-to/index.rst b/doc/en/how-to/index.rst index c206a30570a..35b112994d9 100644 --- a/doc/en/how-to/index.rst +++ b/doc/en/how-to/index.rst @@ -3,19 +3,58 @@ How-to guides ================ +Core pytest functionality +------------------------- + .. toctree:: :maxdepth: 1 usage - existingtestsuite assert + fixtures mark - monkeypatch + parametrize tmpdir - capture + monkeypatch + doctest + cache + +test output and outcomes +---------------------------- + +.. toctree:: + :maxdepth: 1 + + logging + capture-stdout-stderr + capture-warnings skipping - parametrize + +Plugins +---------------------------- + +.. toctree:: + :maxdepth: 1 + plugins + writing_plugins + writing_hook_functions + +pytest and other test systems +----------------------------- + +.. toctree:: + :maxdepth: 1 + + existingtestsuite + unittest nose + xunit_setup + +pytest development environment +------------------------------ + +.. toctree:: + :maxdepth: 1 + bash-completion - fixtures diff --git a/doc/en/reference/logging.rst b/doc/en/how-to/logging.rst similarity index 99% rename from doc/en/reference/logging.rst rename to doc/en/how-to/logging.rst index 52713854efb..317d6134e1b 100644 --- a/doc/en/reference/logging.rst +++ b/doc/en/how-to/logging.rst @@ -1,10 +1,7 @@ .. _logging: -Logging -------- - - - +How to manage logging +--------------------- pytest captures log messages of level ``WARNING`` or above automatically and displays them in their own section for each failed test in the same manner as captured stdout and stderr. diff --git a/doc/en/reference/unittest.rst b/doc/en/how-to/unittest.rst similarity index 99% rename from doc/en/reference/unittest.rst rename to doc/en/how-to/unittest.rst index 92c1bbfa2f1..2e5763f834c 100644 --- a/doc/en/reference/unittest.rst +++ b/doc/en/how-to/unittest.rst @@ -2,8 +2,8 @@ .. _`unittest.TestCase`: .. _`unittest`: -unittest.TestCase Support -========================= +How to use ``unittest``-based tests with pytest +=============================================== ``pytest`` supports running Python ``unittest``-based tests out of the box. It's meant for leveraging existing ``unittest``-based test suites diff --git a/doc/en/how-to/writing_hook_functions.rst b/doc/en/how-to/writing_hook_functions.rst new file mode 100644 index 00000000000..de2ff3bdc10 --- /dev/null +++ b/doc/en/how-to/writing_hook_functions.rst @@ -0,0 +1,313 @@ +.. _`writinghooks`: + +Writing hook functions +====================== + + +.. _validation: + +hook function validation and execution +-------------------------------------- + +pytest calls hook functions from registered plugins for any +given hook specification. Let's look at a typical hook function +for the ``pytest_collection_modifyitems(session, config, +items)`` hook which pytest calls after collection of all test items is +completed. + +When we implement a ``pytest_collection_modifyitems`` function in our plugin +pytest will during registration verify that you use argument +names which match the specification and bail out if not. + +Let's look at a possible implementation: + +.. code-block:: python + + def pytest_collection_modifyitems(config, items): + # called after collection is completed + # you can modify the ``items`` list + ... + +Here, ``pytest`` will pass in ``config`` (the pytest config object) +and ``items`` (the list of collected test items) but will not pass +in the ``session`` argument because we didn't list it in the function +signature. This dynamic "pruning" of arguments allows ``pytest`` to +be "future-compatible": we can introduce new hook named parameters without +breaking the signatures of existing hook implementations. It is one of +the reasons for the general long-lived compatibility of pytest plugins. + +Note that hook functions other than ``pytest_runtest_*`` are not +allowed to raise exceptions. Doing so will break the pytest run. + + + +.. _firstresult: + +firstresult: stop at first non-None result +------------------------------------------- + +Most calls to ``pytest`` hooks result in a **list of results** which contains +all non-None results of the called hook functions. + +Some hook specifications use the ``firstresult=True`` option so that the hook +call only executes until the first of N registered functions returns a +non-None result which is then taken as result of the overall hook call. +The remaining hook functions will not be called in this case. + +.. _`hookwrapper`: + +hookwrapper: executing around other hooks +------------------------------------------------- + +.. currentmodule:: _pytest.core + + + +pytest plugins can implement hook wrappers which wrap the execution +of other hook implementations. A hook wrapper is a generator function +which yields exactly once. When pytest invokes hooks it first executes +hook wrappers and passes the same arguments as to the regular hooks. + +At the yield point of the hook wrapper pytest will execute the next hook +implementations and return their result to the yield point in the form of +a :py:class:`Result ` instance which encapsulates a result or +exception info. The yield point itself will thus typically not raise +exceptions (unless there are bugs). + +Here is an example definition of a hook wrapper: + +.. code-block:: python + + import pytest + + + @pytest.hookimpl(hookwrapper=True) + def pytest_pyfunc_call(pyfuncitem): + do_something_before_next_hook_executes() + + outcome = yield + # outcome.excinfo may be None or a (cls, val, tb) tuple + + res = outcome.get_result() # will raise if outcome was exception + + post_process_result(res) + + outcome.force_result(new_res) # to override the return value to the plugin system + +Note that hook wrappers don't return results themselves, they merely +perform tracing or other side effects around the actual hook implementations. +If the result of the underlying hook is a mutable object, they may modify +that result but it's probably better to avoid it. + +For more information, consult the +:ref:`pluggy documentation about hookwrappers `. + +.. _plugin-hookorder: + +Hook function ordering / call example +------------------------------------- + +For any given hook specification there may be more than one +implementation and we thus generally view ``hook`` execution as a +``1:N`` function call where ``N`` is the number of registered functions. +There are ways to influence if a hook implementation comes before or +after others, i.e. the position in the ``N``-sized list of functions: + +.. code-block:: python + + # Plugin 1 + @pytest.hookimpl(tryfirst=True) + def pytest_collection_modifyitems(items): + # will execute as early as possible + ... + + + # Plugin 2 + @pytest.hookimpl(trylast=True) + def pytest_collection_modifyitems(items): + # will execute as late as possible + ... + + + # Plugin 3 + @pytest.hookimpl(hookwrapper=True) + def pytest_collection_modifyitems(items): + # will execute even before the tryfirst one above! + outcome = yield + # will execute after all non-hookwrappers executed + +Here is the order of execution: + +1. Plugin3's pytest_collection_modifyitems called until the yield point + because it is a hook wrapper. + +2. Plugin1's pytest_collection_modifyitems is called because it is marked + with ``tryfirst=True``. + +3. Plugin2's pytest_collection_modifyitems is called because it is marked + with ``trylast=True`` (but even without this mark it would come after + Plugin1). + +4. Plugin3's pytest_collection_modifyitems then executing the code after the yield + point. The yield receives a :py:class:`Result ` instance which encapsulates + the result from calling the non-wrappers. Wrappers shall not modify the result. + +It's possible to use ``tryfirst`` and ``trylast`` also in conjunction with +``hookwrapper=True`` in which case it will influence the ordering of hookwrappers +among each other. + + +Declaring new hooks +------------------------ + +.. note:: + + This is a quick overview on how to add new hooks and how they work in general, but a more complete + overview can be found in `the pluggy documentation `__. + +.. currentmodule:: _pytest.hookspec + +Plugins and ``conftest.py`` files may declare new hooks that can then be +implemented by other plugins in order to alter behaviour or interact with +the new plugin: + +.. autofunction:: pytest_addhooks + :noindex: + +Hooks are usually declared as do-nothing functions that contain only +documentation describing when the hook will be called and what return values +are expected. The names of the functions must start with `pytest_` otherwise pytest won't recognize them. + +Here's an example. Let's assume this code is in the ``sample_hook.py`` module. + +.. code-block:: python + + def pytest_my_hook(config): + """ + Receives the pytest config and does things with it + """ + +To register the hooks with pytest they need to be structured in their own module or class. This +class or module can then be passed to the ``pluginmanager`` using the ``pytest_addhooks`` function +(which itself is a hook exposed by pytest). + +.. code-block:: python + + def pytest_addhooks(pluginmanager): + """ This example assumes the hooks are grouped in the 'sample_hook' module. """ + from my_app.tests import sample_hook + + pluginmanager.add_hookspecs(sample_hook) + +For a real world example, see `newhooks.py`_ from `xdist `_. + +.. _`newhooks.py`: https://github.com/pytest-dev/pytest-xdist/blob/974bd566c599dc6a9ea291838c6f226197208b46/xdist/newhooks.py + +Hooks may be called both from fixtures or from other hooks. In both cases, hooks are called +through the ``hook`` object, available in the ``config`` object. Most hooks receive a +``config`` object directly, while fixtures may use the ``pytestconfig`` fixture which provides the same object. + +.. code-block:: python + + @pytest.fixture() + def my_fixture(pytestconfig): + # call the hook called "pytest_my_hook" + # 'result' will be a list of return values from all registered functions. + result = pytestconfig.hook.pytest_my_hook(config=pytestconfig) + +.. note:: + Hooks receive parameters using only keyword arguments. + +Now your hook is ready to be used. To register a function at the hook, other plugins or users must +now simply define the function ``pytest_my_hook`` with the correct signature in their ``conftest.py``. + +Example: + +.. code-block:: python + + def pytest_my_hook(config): + """ + Print all active hooks to the screen. + """ + print(config.hook) + + +.. _`addoptionhooks`: + + +Using hooks in pytest_addoption +------------------------------- + +Occasionally, it is necessary to change the way in which command line options +are defined by one plugin based on hooks in another plugin. For example, +a plugin may expose a command line option for which another plugin needs +to define the default value. The pluginmanager can be used to install and +use hooks to accomplish this. The plugin would define and add the hooks +and use pytest_addoption as follows: + +.. code-block:: python + + # contents of hooks.py + + # Use firstresult=True because we only want one plugin to define this + # default value + @hookspec(firstresult=True) + def pytest_config_file_default_value(): + """ Return the default value for the config file command line option. """ + + + # contents of myplugin.py + + + def pytest_addhooks(pluginmanager): + """ This example assumes the hooks are grouped in the 'hooks' module. """ + from . import hook + + pluginmanager.add_hookspecs(hook) + + + def pytest_addoption(parser, pluginmanager): + default_value = pluginmanager.hook.pytest_config_file_default_value() + parser.addoption( + "--config-file", + help="Config file to use, defaults to %(default)s", + default=default_value, + ) + +The conftest.py that is using myplugin would simply define the hook as follows: + +.. code-block:: python + + def pytest_config_file_default_value(): + return "config.yaml" + + +Optionally using hooks from 3rd party plugins +--------------------------------------------- + +Using new hooks from plugins as explained above might be a little tricky +because of the standard :ref:`validation mechanism `: +if you depend on a plugin that is not installed, validation will fail and +the error message will not make much sense to your users. + +One approach is to defer the hook implementation to a new plugin instead of +declaring the hook functions directly in your plugin module, for example: + +.. code-block:: python + + # contents of myplugin.py + + + class DeferPlugin: + """Simple plugin to defer pytest-xdist hook functions.""" + + def pytest_testnodedown(self, node, error): + """standard xdist hook function.""" + + + def pytest_configure(config): + if config.pluginmanager.hasplugin("xdist"): + config.pluginmanager.register(DeferPlugin()) + +This has the added benefit of allowing you to conditionally install hooks +depending on which plugins are installed. diff --git a/doc/en/reference/writing_plugins.rst b/doc/en/how-to/writing_plugins.rst similarity index 59% rename from doc/en/reference/writing_plugins.rst rename to doc/en/how-to/writing_plugins.rst index b4d9f49ff46..8ec935cc2a3 100644 --- a/doc/en/reference/writing_plugins.rst +++ b/doc/en/how-to/writing_plugins.rst @@ -454,320 +454,3 @@ Additionally it is possible to copy examples for an example folder before runnin For more information about the result object that ``runpytest()`` returns, and the methods that it provides please check out the :py:class:`RunResult <_pytest.pytester.RunResult>` documentation. - - - - -.. _`writinghooks`: - -Writing hook functions -====================== - - -.. _validation: - -hook function validation and execution --------------------------------------- - -pytest calls hook functions from registered plugins for any -given hook specification. Let's look at a typical hook function -for the ``pytest_collection_modifyitems(session, config, -items)`` hook which pytest calls after collection of all test items is -completed. - -When we implement a ``pytest_collection_modifyitems`` function in our plugin -pytest will during registration verify that you use argument -names which match the specification and bail out if not. - -Let's look at a possible implementation: - -.. code-block:: python - - def pytest_collection_modifyitems(config, items): - # called after collection is completed - # you can modify the ``items`` list - ... - -Here, ``pytest`` will pass in ``config`` (the pytest config object) -and ``items`` (the list of collected test items) but will not pass -in the ``session`` argument because we didn't list it in the function -signature. This dynamic "pruning" of arguments allows ``pytest`` to -be "future-compatible": we can introduce new hook named parameters without -breaking the signatures of existing hook implementations. It is one of -the reasons for the general long-lived compatibility of pytest plugins. - -Note that hook functions other than ``pytest_runtest_*`` are not -allowed to raise exceptions. Doing so will break the pytest run. - - - -.. _firstresult: - -firstresult: stop at first non-None result -------------------------------------------- - -Most calls to ``pytest`` hooks result in a **list of results** which contains -all non-None results of the called hook functions. - -Some hook specifications use the ``firstresult=True`` option so that the hook -call only executes until the first of N registered functions returns a -non-None result which is then taken as result of the overall hook call. -The remaining hook functions will not be called in this case. - -.. _`hookwrapper`: - -hookwrapper: executing around other hooks -------------------------------------------------- - -.. currentmodule:: _pytest.core - - - -pytest plugins can implement hook wrappers which wrap the execution -of other hook implementations. A hook wrapper is a generator function -which yields exactly once. When pytest invokes hooks it first executes -hook wrappers and passes the same arguments as to the regular hooks. - -At the yield point of the hook wrapper pytest will execute the next hook -implementations and return their result to the yield point in the form of -a :py:class:`Result ` instance which encapsulates a result or -exception info. The yield point itself will thus typically not raise -exceptions (unless there are bugs). - -Here is an example definition of a hook wrapper: - -.. code-block:: python - - import pytest - - - @pytest.hookimpl(hookwrapper=True) - def pytest_pyfunc_call(pyfuncitem): - do_something_before_next_hook_executes() - - outcome = yield - # outcome.excinfo may be None or a (cls, val, tb) tuple - - res = outcome.get_result() # will raise if outcome was exception - - post_process_result(res) - - outcome.force_result(new_res) # to override the return value to the plugin system - -Note that hook wrappers don't return results themselves, they merely -perform tracing or other side effects around the actual hook implementations. -If the result of the underlying hook is a mutable object, they may modify -that result but it's probably better to avoid it. - -For more information, consult the -:ref:`pluggy documentation about hookwrappers `. - -.. _plugin-hookorder: - -Hook function ordering / call example -------------------------------------- - -For any given hook specification there may be more than one -implementation and we thus generally view ``hook`` execution as a -``1:N`` function call where ``N`` is the number of registered functions. -There are ways to influence if a hook implementation comes before or -after others, i.e. the position in the ``N``-sized list of functions: - -.. code-block:: python - - # Plugin 1 - @pytest.hookimpl(tryfirst=True) - def pytest_collection_modifyitems(items): - # will execute as early as possible - ... - - - # Plugin 2 - @pytest.hookimpl(trylast=True) - def pytest_collection_modifyitems(items): - # will execute as late as possible - ... - - - # Plugin 3 - @pytest.hookimpl(hookwrapper=True) - def pytest_collection_modifyitems(items): - # will execute even before the tryfirst one above! - outcome = yield - # will execute after all non-hookwrappers executed - -Here is the order of execution: - -1. Plugin3's pytest_collection_modifyitems called until the yield point - because it is a hook wrapper. - -2. Plugin1's pytest_collection_modifyitems is called because it is marked - with ``tryfirst=True``. - -3. Plugin2's pytest_collection_modifyitems is called because it is marked - with ``trylast=True`` (but even without this mark it would come after - Plugin1). - -4. Plugin3's pytest_collection_modifyitems then executing the code after the yield - point. The yield receives a :py:class:`Result ` instance which encapsulates - the result from calling the non-wrappers. Wrappers shall not modify the result. - -It's possible to use ``tryfirst`` and ``trylast`` also in conjunction with -``hookwrapper=True`` in which case it will influence the ordering of hookwrappers -among each other. - - -Declaring new hooks ------------------------- - -.. note:: - - This is a quick overview on how to add new hooks and how they work in general, but a more complete - overview can be found in `the pluggy documentation `__. - -.. currentmodule:: _pytest.hookspec - -Plugins and ``conftest.py`` files may declare new hooks that can then be -implemented by other plugins in order to alter behaviour or interact with -the new plugin: - -.. autofunction:: pytest_addhooks - :noindex: - -Hooks are usually declared as do-nothing functions that contain only -documentation describing when the hook will be called and what return values -are expected. The names of the functions must start with `pytest_` otherwise pytest won't recognize them. - -Here's an example. Let's assume this code is in the ``sample_hook.py`` module. - -.. code-block:: python - - def pytest_my_hook(config): - """ - Receives the pytest config and does things with it - """ - -To register the hooks with pytest they need to be structured in their own module or class. This -class or module can then be passed to the ``pluginmanager`` using the ``pytest_addhooks`` function -(which itself is a hook exposed by pytest). - -.. code-block:: python - - def pytest_addhooks(pluginmanager): - """ This example assumes the hooks are grouped in the 'sample_hook' module. """ - from my_app.tests import sample_hook - - pluginmanager.add_hookspecs(sample_hook) - -For a real world example, see `newhooks.py`_ from `xdist `_. - -.. _`newhooks.py`: https://github.com/pytest-dev/pytest-xdist/blob/974bd566c599dc6a9ea291838c6f226197208b46/xdist/newhooks.py - -Hooks may be called both from fixtures or from other hooks. In both cases, hooks are called -through the ``hook`` object, available in the ``config`` object. Most hooks receive a -``config`` object directly, while fixtures may use the ``pytestconfig`` fixture which provides the same object. - -.. code-block:: python - - @pytest.fixture() - def my_fixture(pytestconfig): - # call the hook called "pytest_my_hook" - # 'result' will be a list of return values from all registered functions. - result = pytestconfig.hook.pytest_my_hook(config=pytestconfig) - -.. note:: - Hooks receive parameters using only keyword arguments. - -Now your hook is ready to be used. To register a function at the hook, other plugins or users must -now simply define the function ``pytest_my_hook`` with the correct signature in their ``conftest.py``. - -Example: - -.. code-block:: python - - def pytest_my_hook(config): - """ - Print all active hooks to the screen. - """ - print(config.hook) - - -.. _`addoptionhooks`: - - -Using hooks in pytest_addoption -------------------------------- - -Occasionally, it is necessary to change the way in which command line options -are defined by one plugin based on hooks in another plugin. For example, -a plugin may expose a command line option for which another plugin needs -to define the default value. The pluginmanager can be used to install and -use hooks to accomplish this. The plugin would define and add the hooks -and use pytest_addoption as follows: - -.. code-block:: python - - # contents of hooks.py - - # Use firstresult=True because we only want one plugin to define this - # default value - @hookspec(firstresult=True) - def pytest_config_file_default_value(): - """ Return the default value for the config file command line option. """ - - - # contents of myplugin.py - - - def pytest_addhooks(pluginmanager): - """ This example assumes the hooks are grouped in the 'hooks' module. """ - from . import hook - - pluginmanager.add_hookspecs(hook) - - - def pytest_addoption(parser, pluginmanager): - default_value = pluginmanager.hook.pytest_config_file_default_value() - parser.addoption( - "--config-file", - help="Config file to use, defaults to %(default)s", - default=default_value, - ) - -The conftest.py that is using myplugin would simply define the hook as follows: - -.. code-block:: python - - def pytest_config_file_default_value(): - return "config.yaml" - - -Optionally using hooks from 3rd party plugins ---------------------------------------------- - -Using new hooks from plugins as explained above might be a little tricky -because of the standard :ref:`validation mechanism `: -if you depend on a plugin that is not installed, validation will fail and -the error message will not make much sense to your users. - -One approach is to defer the hook implementation to a new plugin instead of -declaring the hook functions directly in your plugin module, for example: - -.. code-block:: python - - # contents of myplugin.py - - - class DeferPlugin: - """Simple plugin to defer pytest-xdist hook functions.""" - - def pytest_testnodedown(self, node, error): - """standard xdist hook function.""" - - - def pytest_configure(config): - if config.pluginmanager.hasplugin("xdist"): - config.pluginmanager.register(DeferPlugin()) - -This has the added benefit of allowing you to conditionally install hooks -depending on which plugins are installed. diff --git a/doc/en/reference/xunit_setup.rst b/doc/en/how-to/xunit_setup.rst similarity index 99% rename from doc/en/reference/xunit_setup.rst rename to doc/en/how-to/xunit_setup.rst index 4fea863be63..2269c06e003 100644 --- a/doc/en/reference/xunit_setup.rst +++ b/doc/en/how-to/xunit_setup.rst @@ -2,7 +2,7 @@ .. _`classic xunit`: .. _xunitsetup: -classic xunit-style setup +How to implement xunit-style set-up ======================================== This section describes a classic and popular way how you can implement diff --git a/doc/en/plugin_list.rst b/doc/en/plugin_list.rst deleted file mode 100644 index ca3e8cc73d0..00000000000 --- a/doc/en/plugin_list.rst +++ /dev/null @@ -1,851 +0,0 @@ -Plugins List -============ - -PyPI projects that match "pytest-\*" are considered plugins and are listed -automatically. Packages classified as inactive are excluded. -This list contains 840 plugins. - -============================================================================================================== ======================================================================================================================================================================== ============== ===================== ============================================ -name summary last release status requires -============================================================================================================== ======================================================================================================================================================================== ============== ===================== ============================================ -`pytest-adaptavist `_ pytest plugin for generating test execution results within Jira Test Management (tm4j) Feb 05, 2020 N/A pytest (>=3.4.1) -`pytest-adf `_ Pytest plugin for writing Azure Data Factory integration tests Mar 10, 2021 4 - Beta pytest (>=3.5.0) -`pytest-adf-azure-identity `_ Pytest plugin for writing Azure Data Factory integration tests Mar 06, 2021 4 - Beta pytest (>=3.5.0) -`pytest-aggreport `_ pytest plugin for pytest-repeat that generate aggregate report of the same test cases with additional statistics details. Mar 07, 2021 4 - Beta pytest (>=6.2.2) -`pytest-aio `_ Pytest plugin for testing async python code Mar 02, 2021 4 - Beta pytest ; extra == 'tests' -`pytest-aiofiles `_ pytest fixtures for writing aiofiles tests with pyfakefs May 14, 2017 5 - Production/Stable N/A -`pytest-aiohttp `_ pytest plugin for aiohttp support Dec 05, 2017 N/A pytest -`pytest-aiohttp-client `_ Pytest `client` fixture for the Aiohttp Nov 01, 2020 N/A pytest (>=6) -`pytest-aioresponses `_ py.test integration for aioresponses Dec 21, 2020 4 - Beta pytest (>=3.5.0) -`pytest-aioworkers `_ A plugin to test aioworkers project with pytest Dec 04, 2019 4 - Beta pytest (>=3.5.0) -`pytest-airflow `_ pytest support for airflow. Apr 03, 2019 3 - Alpha pytest (>=4.4.0) -`pytest-alembic `_ A pytest plugin for verifying alembic migrations. Jul 13, 2020 N/A pytest (>=1.0) -`pytest-allclose `_ Pytest fixture extending Numpy's allclose function Jul 30, 2019 5 - Production/Stable pytest -`pytest-allure-adaptor `_ Plugin for py.test to generate allure xml reports Jan 10, 2018 N/A pytest (>=2.7.3) -`pytest-allure-adaptor2 `_ Plugin for py.test to generate allure xml reports Oct 14, 2020 N/A pytest (>=2.7.3) -`pytest-allure-dsl `_ pytest plugin to test case doc string dls instructions Oct 25, 2020 4 - Beta pytest -`pytest-alphamoon `_ Static code checks used at Alphamoon Nov 20, 2020 4 - Beta pytest (>=3.5.0) -`pytest-android `_ This fixture provides a configured "driver" for Android Automated Testing, using uiautomator2. Feb 21, 2019 3 - Alpha pytest -`pytest-annotate `_ pytest-annotate: Generate PyAnnotate annotations from your pytest tests. Aug 23, 2019 3 - Alpha pytest (<6.0.0,>=3.2.0) -`pytest-ansible `_ Plugin for py.test to simplify calling ansible modules from tests or fixtures Oct 26, 2020 5 - Production/Stable pytest -`pytest-ansible-playbook `_ Pytest fixture which runs given ansible playbook file. Mar 08, 2019 4 - Beta N/A -`pytest-ansible-playbook-runner `_ Pytest fixture which runs given ansible playbook file. Dec 02, 2020 4 - Beta pytest (>=3.1.0) -`pytest-antilru `_ Bust functools.lru_cache when running pytest to avoid test pollution Apr 11, 2019 5 - Production/Stable pytest -`pytest-anything `_ Pytest fixtures to assert anything and something Feb 18, 2021 N/A N/A -`pytest-aoc `_ Downloads puzzle inputs for Advent of Code and synthesizes PyTest fixtures Dec 01, 2020 N/A pytest ; extra == 'dev' -`pytest-apistellar `_ apistellar plugin for pytest. Jun 18, 2019 N/A N/A -`pytest-appengine `_ AppEngine integration that works well with pytest-django Feb 27, 2017 N/A N/A -`pytest-appium `_ Pytest plugin for appium Dec 05, 2019 N/A N/A -`pytest-approvaltests `_ A plugin to use approvaltests with pytest Feb 07, 2021 4 - Beta pytest (>=3.5.0) -`pytest-arraydiff `_ pytest plugin to help with comparing array output from tests Dec 06, 2018 4 - Beta pytest -`pytest-asgi-server `_ Convenient ASGI client/server fixtures for Pytest Dec 12, 2020 N/A pytest (>=5.4.1) -`pytest-asptest `_ test Answer Set Programming programs Apr 28, 2018 4 - Beta N/A -`pytest-assertutil `_ pytest-assertutil May 10, 2019 N/A N/A -`pytest-assert-utils `_ Useful assertion utilities for use with pytest Aug 25, 2020 3 - Alpha N/A -`pytest-assume `_ A pytest plugin that allows multiple failures per test Dec 08, 2020 N/A pytest (>=2.7) -`pytest-ast-back-to-python `_ A plugin for pytest devs to view how assertion rewriting recodes the AST Sep 29, 2019 4 - Beta N/A -`pytest-astropy `_ Meta-package containing dependencies for testing Jan 16, 2020 5 - Production/Stable pytest (>=4.6) -`pytest-astropy-header `_ pytest plugin to add diagnostic information to the header of the test output Dec 18, 2019 3 - Alpha pytest (>=2.8) -`pytest-ast-transformer `_ May 04, 2019 3 - Alpha pytest -`pytest-asyncio `_ Pytest support for asyncio. Jun 23, 2020 4 - Beta pytest (>=5.4.0) -`pytest-asyncio-cooperative `_ Run all your asynchronous tests cooperatively. Jan 03, 2021 4 - Beta N/A -`pytest-asyncio-network-simulator `_ pytest-asyncio-network-simulator: Plugin for pytest for simulator the network in tests Jul 31, 2018 3 - Alpha pytest (<3.7.0,>=3.3.2) -`pytest-async-mongodb `_ pytest plugin for async MongoDB Oct 18, 2017 5 - Production/Stable pytest (>=2.5.2) -`pytest-atomic `_ Skip rest of tests if previous test failed. Nov 24, 2018 4 - Beta N/A -`pytest-attrib `_ pytest plugin to select tests based on attributes similar to the nose-attrib plugin May 24, 2016 4 - Beta N/A -`pytest-austin `_ Austin plugin for pytest Oct 11, 2020 4 - Beta N/A -`pytest-autochecklog `_ automatically check condition and log all the checks Apr 25, 2015 4 - Beta N/A -`pytest-automock `_ Pytest plugin for automatical mocks creation Apr 22, 2020 N/A pytest ; extra == 'dev' -`pytest-auto-parametrize `_ pytest plugin: avoid repeating arguments in parametrize Oct 02, 2016 3 - Alpha N/A -`pytest-avoidance `_ Makes pytest skip tests that don not need rerunning May 23, 2019 4 - Beta pytest (>=3.5.0) -`pytest-aws `_ pytest plugin for testing AWS resource configurations Oct 04, 2017 4 - Beta N/A -`pytest-axe `_ pytest plugin for axe-selenium-python Nov 12, 2018 N/A pytest (>=3.0.0) -`pytest-azurepipelines `_ Formatting PyTest output for Azure Pipelines UI Jul 23, 2020 4 - Beta pytest (>=3.5.0) -`pytest-bandit `_ A bandit plugin for pytest Feb 23, 2021 4 - Beta pytest (>=3.5.0) -`pytest-base-url `_ pytest plugin for URL based testing Jun 19, 2020 5 - Production/Stable pytest (>=2.7.3) -`pytest-bdd `_ BDD for pytest Dec 07, 2020 6 - Mature pytest (>=4.3) -`pytest-bdd-splinter `_ Common steps for pytest bdd and splinter integration Aug 12, 2019 5 - Production/Stable pytest (>=4.0.0) -`pytest-bdd-web `_ A simple plugin to use with pytest Jan 02, 2020 4 - Beta pytest (>=3.5.0) -`pytest-bdd-wrappers `_ Feb 11, 2020 2 - Pre-Alpha N/A -`pytest-beakerlib `_ A pytest plugin that reports test results to the BeakerLib framework Mar 17, 2017 5 - Production/Stable pytest -`pytest-beds `_ Fixtures for testing Google Appengine (GAE) apps Jun 07, 2016 4 - Beta N/A -`pytest-bench `_ Benchmark utility that plugs into pytest. Jul 21, 2014 3 - Alpha N/A -`pytest-benchmark `_ A ``pytest`` fixture for benchmarking code. It will group the tests into rounds that are calibrated to the chosen timer. See calibration and FAQ. Jan 10, 2020 5 - Production/Stable pytest (>=3.8) -`pytest-bigchaindb `_ A BigchainDB plugin for pytest. Jan 10, 2020 4 - Beta N/A -`pytest-black `_ A pytest plugin to enable format checking with black Oct 05, 2020 4 - Beta N/A -`pytest-black-multipy `_ Allow '--black' on older Pythons Jan 14, 2021 5 - Production/Stable pytest (!=3.7.3,>=3.5) ; extra == 'testing' -`pytest-blame `_ A pytest plugin helps developers to debug by providing useful commits history. May 04, 2019 N/A pytest (>=4.4.0) -`pytest-blender `_ Blender Pytest plugin. Feb 15, 2021 N/A pytest (==6.2.1) ; extra == 'dev' -`pytest-blink1 `_ Pytest plugin to emit notifications via the Blink(1) RGB LED Jan 07, 2018 4 - Beta N/A -`pytest-blockage `_ Disable network requests during a test run. Feb 13, 2019 N/A pytest -`pytest-blocker `_ pytest plugin to mark a test as blocker and skip all other tests Sep 07, 2015 4 - Beta N/A -`pytest-board `_ Local continuous test runner with pytest and watchdog. Jan 20, 2019 N/A N/A -`pytest-bpdb `_ A py.test plug-in to enable drop to bpdb debugger on test failure. Jan 19, 2015 2 - Pre-Alpha N/A -`pytest-bravado `_ Pytest-bravado automatically generates from OpenAPI specification client fixtures. Jan 20, 2021 N/A N/A -`pytest-breed-adapter `_ A simple plugin to connect with breed-server Nov 07, 2018 4 - Beta pytest (>=3.5.0) -`pytest-briefcase `_ A pytest plugin for running tests on a Briefcase project. Jun 14, 2020 4 - Beta pytest (>=3.5.0) -`pytest-browser `_ A pytest plugin for console based browser test selection just after the collection phase Dec 10, 2016 3 - Alpha N/A -`pytest-browsermob-proxy `_ BrowserMob proxy plugin for py.test. Jun 11, 2013 4 - Beta N/A -`pytest-browserstack-local `_ ``py.test`` plugin to run ``BrowserStackLocal`` in background. Feb 09, 2018 N/A N/A -`pytest-bug `_ Pytest plugin for marking tests as a bug Jun 02, 2020 5 - Production/Stable pytest (>=3.6.0) -`pytest-bugzilla `_ py.test bugzilla integration plugin May 05, 2010 4 - Beta N/A -`pytest-bugzilla-notifier `_ A plugin that allows you to execute create, update, and read information from BugZilla bugs Jun 15, 2018 4 - Beta pytest (>=2.9.2) -`pytest-buildkite `_ Plugin for pytest that automatically publishes coverage and pytest report annotations to Buildkite. Jul 13, 2019 4 - Beta pytest (>=3.5.0) -`pytest-bwrap `_ Run your tests in Bubblewrap sandboxes Oct 26, 2018 3 - Alpha N/A -`pytest-cache `_ pytest plugin with mechanisms for caching across test runs Jun 04, 2013 3 - Alpha N/A -`pytest-cagoule `_ Pytest plugin to only run tests affected by changes Jan 01, 2020 3 - Alpha N/A -`pytest-camel-collect `_ Enable CamelCase-aware pytest class collection Aug 02, 2020 N/A pytest (>=2.9) -`pytest-canonical-data `_ A plugin which allows to compare results with canonical results, based on previous runs May 08, 2020 2 - Pre-Alpha pytest (>=3.5.0) -`pytest-caprng `_ A plugin that replays pRNG state on failure. May 02, 2018 4 - Beta N/A -`pytest-capture-deprecatedwarnings `_ pytest plugin to capture all deprecatedwarnings and put them in one file Apr 30, 2019 N/A N/A -`pytest-cases `_ Separate test code from test cases in pytest. Feb 19, 2021 5 - Production/Stable N/A -`pytest-cassandra `_ Cassandra CCM Test Fixtures for pytest Nov 04, 2017 1 - Planning N/A -`pytest-catchlog `_ py.test plugin to catch log messages. This is a fork of pytest-capturelog. Jan 24, 2016 4 - Beta pytest (>=2.6) -`pytest-catch-server `_ Pytest plugin with server for catching HTTP requests. Dec 12, 2019 5 - Production/Stable N/A -`pytest-celery `_ pytest-celery a shim pytest plugin to enable celery.contrib.pytest Aug 05, 2020 N/A N/A -`pytest-chalice `_ A set of py.test fixtures for AWS Chalice Jul 01, 2020 4 - Beta N/A -`pytest-change-report `_ turn . into √,turn F into x Sep 14, 2020 N/A pytest -`pytest-chdir `_ A pytest fixture for changing current working directory Jan 28, 2020 N/A pytest (>=5.0.0,<6.0.0) -`pytest-check `_ A pytest plugin that allows multiple failures per test. Dec 27, 2020 5 - Production/Stable N/A -`pytest-checkdocs `_ check the README when running tests Feb 27, 2021 5 - Production/Stable pytest (>=4.6) ; extra == 'testing' -`pytest-checkipdb `_ plugin to check if there are ipdb debugs left Jul 22, 2020 5 - Production/Stable pytest (>=2.9.2) -`pytest-check-links `_ Check links in files Jul 29, 2020 N/A N/A -`pytest-check-mk `_ pytest plugin to test Check_MK checks Nov 19, 2015 4 - Beta pytest -`pytest-circleci `_ py.test plugin for CircleCI May 03, 2019 N/A N/A -`pytest-circleci-parallelized `_ Parallelize pytest across CircleCI workers. Mar 26, 2019 N/A N/A -`pytest-ckan `_ Backport of CKAN 2.9 pytest plugin and fixtures to CAKN 2.8 Apr 28, 2020 4 - Beta pytest -`pytest-clarity `_ A plugin providing an alternative, colourful diff output for failing assertions. Jan 23, 2020 3 - Alpha N/A -`pytest-cldf `_ Easy quality control for CLDF datasets using pytest May 06, 2019 N/A N/A -`pytest-click `_ Py.test plugin for Click Aug 29, 2020 5 - Production/Stable pytest (>=5.0) -`pytest-clld `_ May 06, 2020 N/A pytest (>=3.6) -`pytest-cloud `_ Distributed tests planner plugin for pytest testing framework. Oct 05, 2020 6 - Mature N/A -`pytest-cloudflare-worker `_ pytest plugin for testing cloudflare workers Oct 19, 2020 4 - Beta pytest (>=6.0.0) -`pytest-cobra `_ PyTest plugin for testing Smart Contracts for Ethereum blockchain. Jun 29, 2019 3 - Alpha pytest (<4.0.0,>=3.7.1) -`pytest-codecheckers `_ pytest plugin to add source code sanity checks (pep8 and friends) Feb 13, 2010 N/A N/A -`pytest-codegen `_ Automatically create pytest test signatures Aug 23, 2020 2 - Pre-Alpha N/A -`pytest-codestyle `_ pytest plugin to run pycodestyle Mar 23, 2020 3 - Alpha N/A -`pytest-collect-formatter `_ Formatter for pytest collect output Nov 19, 2020 5 - Production/Stable N/A -`pytest-colordots `_ Colorizes the progress indicators Oct 06, 2017 5 - Production/Stable N/A -`pytest-commander `_ An interactive GUI test runner for PyTest Jan 31, 2021 N/A pytest (>=5.0.0) -`pytest-common-subject `_ pytest framework for testing different aspects of a common method Nov 12, 2020 N/A pytest (>=3.6,<7) -`pytest-concurrent `_ Concurrently execute test cases with multithread, multiprocess and gevent Jan 12, 2019 4 - Beta pytest (>=3.1.1) -`pytest-config `_ Base configurations and utilities for developing your Python project test suite with pytest. Nov 07, 2014 5 - Production/Stable N/A -`pytest-confluence-report `_ Package stands for pytest plugin to upload results into Confluence page. Nov 06, 2020 N/A N/A -`pytest-console-scripts `_ Pytest plugin for testing console scripts Nov 20, 2020 4 - Beta N/A -`pytest-consul `_ pytest plugin with fixtures for testing consul aware apps Nov 24, 2018 3 - Alpha pytest -`pytest-contextfixture `_ Define pytest fixtures as context managers. Mar 12, 2013 4 - Beta N/A -`pytest-contexts `_ A plugin to run tests written with the Contexts framework using pytest Jul 23, 2018 4 - Beta N/A -`pytest-cookies `_ The pytest plugin for your Cookiecutter templates. 🍪 Feb 14, 2020 5 - Production/Stable pytest (<6.0.0,>=3.3.0) -`pytest-couchdbkit `_ py.test extension for per-test couchdb databases using couchdbkit Apr 17, 2012 N/A N/A -`pytest-count `_ count erros and send email Jan 12, 2018 4 - Beta N/A -`pytest-cov `_ Pytest plugin for measuring coverage. Jan 20, 2021 5 - Production/Stable pytest (>=4.6) -`pytest-cover `_ Pytest plugin for measuring coverage. Forked from `pytest-cov`. Aug 01, 2015 5 - Production/Stable N/A -`pytest-coverage `_ Jun 17, 2015 N/A N/A -`pytest-coverage-context `_ Coverage dynamic context support for PyTest, including sub-processes Jan 04, 2021 4 - Beta pytest (>=6.1.0) -`pytest-cov-exclude `_ Pytest plugin for excluding tests based on coverage data Apr 29, 2016 4 - Beta pytest (>=2.8.0,<2.9.0); extra == 'dev' -`pytest-cpp `_ Use pytest's runner to discover and execute C++ tests Dec 10, 2020 4 - Beta pytest (!=5.4.0,!=5.4.1) -`pytest-cram `_ Run cram tests with pytest. Aug 08, 2020 N/A N/A -`pytest-crate `_ Manages CrateDB instances during your integration tests May 28, 2019 3 - Alpha pytest (>=4.0) -`pytest-cricri `_ A Cricri plugin for pytest. Jan 27, 2018 N/A pytest -`pytest-crontab `_ add crontab task in crontab Dec 09, 2019 N/A N/A -`pytest-csv `_ CSV output for pytest. Jun 24, 2019 N/A pytest (>=4.4) -`pytest-curio `_ Pytest support for curio. Oct 07, 2020 N/A N/A -`pytest-curl-report `_ pytest plugin to generate curl command line report Dec 11, 2016 4 - Beta N/A -`pytest-custom-concurrency `_ Custom grouping concurrence for pytest Feb 08, 2021 N/A N/A -`pytest-custom-exit-code `_ Exit pytest test session with custom exit code in different scenarios Aug 07, 2019 4 - Beta pytest (>=4.0.2) -`pytest-custom-nodeid `_ Custom grouping for pytest-xdist, rename test cases name and test cases nodeid, support allure report Mar 07, 2021 N/A N/A -`pytest-custom-report `_ Configure the symbols displayed for test outcomes Jan 30, 2019 N/A pytest -`pytest-custom-scheduling `_ Custom grouping for pytest-xdist, rename test cases name and test cases nodeid, support allure report Mar 01, 2021 N/A N/A -`pytest-cython `_ A plugin for testing Cython extension modules Jan 26, 2021 4 - Beta pytest (>=2.7.3) -`pytest-darker `_ A pytest plugin for checking of modified code using Darker Aug 16, 2020 N/A pytest (>=6.0.1) ; extra == 'test' -`pytest-dash `_ pytest fixtures to run dash applications. Mar 18, 2019 N/A N/A -`pytest-data `_ Useful functions for managing data for pytest fixtures Nov 01, 2016 5 - Production/Stable N/A -`pytest-databricks `_ Pytest plugin for remote Databricks notebooks testing Jul 29, 2020 N/A pytest -`pytest-datadir `_ pytest plugin for test data directories and files Oct 22, 2019 5 - Production/Stable pytest (>=2.7.0) -`pytest-datadir-mgr `_ Manager for test data providing downloads, caching of generated files, and a context for temp directories. Feb 17, 2021 5 - Production/Stable pytest (>=6.0.1,<7.0.0) -`pytest-datadir-ng `_ Fixtures for pytest allowing test functions/methods to easily retrieve test resources from the local filesystem. Dec 25, 2019 5 - Production/Stable pytest -`pytest-data-file `_ Fixture "data" and "case_data" for test from yaml file Dec 04, 2019 N/A N/A -`pytest-datafiles `_ py.test plugin to create a 'tmpdir' containing predefined files/directories. Oct 07, 2018 5 - Production/Stable pytest (>=3.6) -`pytest-datafixtures `_ Data fixtures for pytest made simple Dec 05, 2020 5 - Production/Stable N/A -`pytest-dataplugin `_ A pytest plugin for managing an archive of test data. Sep 16, 2017 1 - Planning N/A -`pytest-datarecorder `_ A py.test plugin recording and comparing test output. Apr 20, 2020 5 - Production/Stable pytest -`pytest-datatest `_ A pytest plugin for test driven data-wrangling (this is the development version of datatest's pytest integration). Oct 15, 2020 4 - Beta pytest (>=3.3) -`pytest-db `_ Session scope fixture "db" for mysql query or change Dec 04, 2019 N/A N/A -`pytest-dbfixtures `_ Databases fixtures plugin for py.test. Dec 07, 2016 4 - Beta N/A -`pytest-dbt-adapter `_ A pytest plugin for testing dbt adapter plugins Jan 07, 2021 N/A pytest (<7,>=6) -`pytest-dbus-notification `_ D-BUS notifications for pytest results. Mar 05, 2014 5 - Production/Stable N/A -`pytest-deadfixtures `_ A simple plugin to list unused fixtures in pytest Jul 23, 2020 5 - Production/Stable N/A -`pytest-dependency `_ Manage dependencies of tests Feb 14, 2020 4 - Beta N/A -`pytest-depends `_ Tests that depend on other tests Apr 05, 2020 5 - Production/Stable pytest (>=3) -`pytest-deprecate `_ Mark tests as testing a deprecated feature with a warning note. Jul 01, 2019 N/A N/A -`pytest-describe `_ Describe-style plugin for pytest Apr 21, 2020 3 - Alpha pytest (>=2.6.0) -`pytest-describe-it `_ plugin for rich text descriptions Jul 19, 2019 4 - Beta pytest -`pytest-devpi-server `_ DevPI server fixture for py.test May 28, 2019 5 - Production/Stable pytest -`pytest-diamond `_ pytest plugin for diamond Aug 31, 2015 4 - Beta N/A -`pytest-dicom `_ pytest plugin to provide DICOM fixtures Dec 19, 2018 3 - Alpha pytest -`pytest-dictsdiff `_ Jul 26, 2019 N/A N/A -`pytest-diff `_ A simple plugin to use with pytest Mar 30, 2019 4 - Beta pytest (>=3.5.0) -`pytest-diffeo `_ Common py.test support for Diffeo packages Apr 08, 2016 3 - Alpha N/A -`pytest-disable `_ pytest plugin to disable a test and skip it from testrun Sep 10, 2015 4 - Beta N/A -`pytest-disable-plugin `_ Disable plugins per test Feb 28, 2019 4 - Beta pytest (>=3.5.0) -`pytest-discord `_ A pytest plugin to notify test results to a Discord channel. Feb 14, 2021 3 - Alpha pytest (!=6.0.0,<7,>=3.3.2) -`pytest-django `_ A Django plugin for pytest. Oct 22, 2020 5 - Production/Stable pytest (>=5.4.0) -`pytest-django-ahead `_ A Django plugin for pytest. Oct 27, 2016 5 - Production/Stable pytest (>=2.9) -`pytest-djangoapp `_ Nice pytest plugin to help you with Django pluggable application testing. Sep 21, 2020 4 - Beta N/A -`pytest-django-cache-xdist `_ A djangocachexdist plugin for pytest May 12, 2020 4 - Beta N/A -`pytest-django-casperjs `_ Integrate CasperJS with your django tests as a pytest fixture. Mar 15, 2015 2 - Pre-Alpha N/A -`pytest-django-dotenv `_ Pytest plugin used to setup environment variables with django-dotenv Nov 26, 2019 4 - Beta pytest (>=2.6.0) -`pytest-django-factories `_ Factories for your Django models that can be used as Pytest fixtures. Nov 12, 2020 4 - Beta N/A -`pytest-django-gcir `_ A Django plugin for pytest. Mar 06, 2018 5 - Production/Stable N/A -`pytest-django-haystack `_ Cleanup your Haystack indexes between tests Sep 03, 2017 5 - Production/Stable pytest (>=2.3.4) -`pytest-django-ifactory `_ A model instance factory for pytest-django Jan 13, 2021 3 - Alpha N/A -`pytest-django-lite `_ The bare minimum to integrate py.test with Django. Jan 30, 2014 N/A N/A -`pytest-django-model `_ A Simple Way to Test your Django Models Feb 14, 2019 4 - Beta N/A -`pytest-django-ordering `_ A pytest plugin for preserving the order in which Django runs tests. Jul 25, 2019 5 - Production/Stable pytest (>=2.3.0) -`pytest-django-queries `_ Generate performance reports from your django database performance tests. Mar 01, 2021 N/A N/A -`pytest-djangorestframework `_ A djangorestframework plugin for pytest Aug 11, 2019 4 - Beta N/A -`pytest-django-rq `_ A pytest plugin to help writing unit test for django-rq Apr 13, 2020 4 - Beta N/A -`pytest-django-sqlcounts `_ py.test plugin for reporting the number of SQLs executed per django testcase. Jun 16, 2015 4 - Beta N/A -`pytest-django-testing-postgresql `_ Use a temporary PostgreSQL database with pytest-django Dec 05, 2019 3 - Alpha N/A -`pytest-doc `_ A documentation plugin for py.test. Jun 28, 2015 5 - Production/Stable N/A -`pytest-docgen `_ An RST Documentation Generator for pytest-based test suites Apr 17, 2020 N/A N/A -`pytest-docker `_ Simple pytest fixtures for Docker and docker-compose based tests Sep 22, 2020 N/A pytest (<7.0,>=4.0) -`pytest-docker-butla `_ Jun 16, 2019 3 - Alpha N/A -`pytest-dockerc `_ Run, manage and stop Docker Compose project from Docker API Oct 09, 2020 5 - Production/Stable pytest (>=3.0) -`pytest-docker-compose `_ Manages Docker containers during your integration tests Jan 26, 2021 5 - Production/Stable pytest (>=3.3) -`pytest-docker-db `_ A plugin to use docker databases for pytests Apr 19, 2020 5 - Production/Stable pytest (>=3.1.1) -`pytest-docker-fixtures `_ pytest docker fixtures Sep 30, 2020 3 - Alpha N/A -`pytest-docker-git-fixtures `_ Pytest fixtures for testing with git scm. Mar 11, 2021 4 - Beta pytest -`pytest-docker-pexpect `_ pytest plugin for writing functional tests with pexpect and docker Jan 14, 2019 N/A pytest -`pytest-docker-postgresql `_ A simple plugin to use with pytest Sep 24, 2019 4 - Beta pytest (>=3.5.0) -`pytest-docker-py `_ Easy to use, simple to extend, pytest plugin that minimally leverages docker-py. Nov 27, 2018 N/A pytest (==4.0.0) -`pytest-docker-registry-fixtures `_ Pytest fixtures for testing with docker registries. Mar 04, 2021 4 - Beta pytest -`pytest-docker-tools `_ Docker integration tests for pytest Mar 02, 2021 4 - Beta pytest (>=6.0.1,<7.0.0) -`pytest-docs `_ Documentation tool for pytest Nov 11, 2018 4 - Beta pytest (>=3.5.0) -`pytest-docstyle `_ pytest plugin to run pydocstyle Mar 23, 2020 3 - Alpha N/A -`pytest-doctest-custom `_ A py.test plugin for customizing string representations of doctest results. Jul 25, 2016 4 - Beta N/A -`pytest-doctest-ellipsis-markers `_ Setup additional values for ELLIPSIS_MARKER for doctests Jan 12, 2018 4 - Beta N/A -`pytest-doctest-import `_ A simple pytest plugin to import names and add them to the doctest namespace. Nov 13, 2018 4 - Beta pytest (>=3.3.0) -`pytest-doctestplus `_ Pytest plugin with advanced doctest features. Jan 15, 2021 3 - Alpha pytest (>=4.6) -`pytest-doctest-ufunc `_ A plugin to run doctests in docstrings of Numpy ufuncs Aug 02, 2020 4 - Beta pytest (>=3.5.0) -`pytest-dolphin `_ Some extra stuff that we use ininternally Nov 30, 2016 4 - Beta pytest (==3.0.4) -`pytest-doorstop `_ A pytest plugin for adding test results into doorstop items. Jun 09, 2020 4 - Beta pytest (>=3.5.0) -`pytest-dotenv `_ A py.test plugin that parses environment files before running tests Jun 16, 2020 4 - Beta pytest (>=5.0.0) -`pytest-drf `_ A Django REST framework plugin for pytest. Nov 12, 2020 5 - Production/Stable pytest (>=3.6) -`pytest-drivings `_ Tool to allow webdriver automation to be ran locally or remotely Jan 13, 2021 N/A N/A -`pytest-drop-dup-tests `_ A Pytest plugin to drop duplicated tests during collection May 23, 2020 4 - Beta pytest (>=2.7) -`pytest-dump2json `_ A pytest plugin for dumping test results to json. Jun 29, 2015 N/A N/A -`pytest-dynamicrerun `_ A pytest plugin to rerun tests dynamically based off of test outcome and output. Aug 15, 2020 4 - Beta N/A -`pytest-dynamodb `_ DynamoDB fixtures for pytest Feb 20, 2020 5 - Production/Stable pytest (>=3.0.0) -`pytest-easy-addoption `_ pytest-easy-addoption: Easy way to work with pytest addoption Jan 22, 2020 N/A N/A -`pytest-easy-api `_ Simple API testing with pytest Mar 26, 2018 N/A N/A -`pytest-easyMPI `_ Package that supports mpi tests in pytest Oct 21, 2020 N/A N/A -`pytest-easyread `_ pytest plugin that makes terminal printouts of the reports easier to read Nov 17, 2017 N/A N/A -`pytest-ec2 `_ Pytest execution on EC2 instance Oct 22, 2019 3 - Alpha N/A -`pytest-echo `_ pytest plugin with mechanisms for echoing environment variables, package version and generic attributes Jan 08, 2020 5 - Production/Stable N/A -`pytest-elasticsearch `_ Elasticsearch process and client fixtures for py.test. Feb 19, 2020 5 - Production/Stable pytest (>=3.0.0) -`pytest-elements `_ Tool to help automate user interfaces Jan 13, 2021 N/A pytest (>=5.4,<6.0) -`pytest-elk-reporter `_ A simple plugin to use with pytest Jan 24, 2021 4 - Beta pytest (>=3.5.0) -`pytest-email `_ Send execution result email Jul 08, 2020 N/A pytest -`pytest-emoji `_ A pytest plugin that adds emojis to your test result report Feb 19, 2019 4 - Beta pytest (>=4.2.1) -`pytest-emoji-output `_ Pytest plugin to represent test output with emoji support Oct 03, 2020 4 - Beta N/A -`pytest-enabler `_ Enable installed pytest plugins Jan 19, 2021 5 - Production/Stable pytest (!=3.7.3,>=3.5) ; extra == 'testing' -`pytest-enhancements `_ Improvements for pytest (rejected upstream) Oct 30, 2019 4 - Beta N/A -`pytest-env `_ py.test plugin that allows you to add environment variables. Jun 16, 2017 4 - Beta N/A -`pytest-envfiles `_ A py.test plugin that parses environment files before running tests Oct 08, 2015 3 - Alpha N/A -`pytest-env-info `_ Push information about the running pytest into envvars Nov 25, 2017 4 - Beta pytest (>=3.1.1) -`pytest-envraw `_ py.test plugin that allows you to add environment variables. Aug 27, 2020 4 - Beta pytest (>=2.6.0) -`pytest-envvars `_ Pytest plugin to validate use of envvars on your tests Jun 13, 2020 5 - Production/Stable pytest (>=3.0.0) -`pytest-env-yaml `_ Apr 02, 2019 N/A N/A -`pytest-eradicate `_ pytest plugin to check for commented out code Sep 08, 2020 N/A pytest (>=2.4.2) -`pytest-error-for-skips `_ Pytest plugin to treat skipped tests a test failure Dec 19, 2019 4 - Beta pytest (>=4.6) -`pytest-eth `_ PyTest plugin for testing Smart Contracts for Ethereum Virtual Machine (EVM). Aug 14, 2020 1 - Planning N/A -`pytest-ethereum `_ pytest-ethereum: Pytest library for ethereum projects. Jun 24, 2019 3 - Alpha pytest (==3.3.2); extra == 'dev' -`pytest-eucalyptus `_ Pytest Plugin for BDD Aug 13, 2019 N/A pytest (>=4.2.0) -`pytest-excel `_ pytest plugin for generating excel reports Oct 06, 2020 5 - Production/Stable N/A -`pytest-exceptional `_ Better exceptions Mar 16, 2017 4 - Beta N/A -`pytest-exception-script `_ Walk your code through exception script to check it's resiliency to failures. Aug 04, 2020 3 - Alpha pytest -`pytest-executable `_ pytest plugin for testing executables Aug 10, 2020 4 - Beta pytest (<6.1,>=4.3) -`pytest-expect `_ py.test plugin to store test expectations and mark tests based on them Apr 21, 2016 4 - Beta N/A -`pytest-expecter `_ Better testing with expecter and pytest. Jul 08, 2020 5 - Production/Stable N/A -`pytest-expectr `_ This plugin is used to expect multiple assert using pytest framework. Oct 05, 2018 N/A pytest (>=2.4.2) -`pytest-exploratory `_ Interactive console for pytest. Jan 20, 2021 N/A pytest (>=5.3) -`pytest-external-blockers `_ a special outcome for tests that are blocked for external reasons Oct 04, 2016 N/A N/A -`pytest-extra-durations `_ A pytest plugin to get durations on a per-function basis and per module basis. Apr 21, 2020 4 - Beta pytest (>=3.5.0) -`pytest-fabric `_ Provides test utilities to run fabric task tests by using docker containers Sep 12, 2018 5 - Production/Stable N/A -`pytest-factory `_ Use factories for test setup with py.test Sep 06, 2020 3 - Alpha pytest (>4.3) -`pytest-factoryboy `_ Factory Boy support for pytest. Dec 30, 2020 6 - Mature pytest (>=4.6) -`pytest-factoryboy-fixtures `_ Generates pytest fixtures that allow the use of type hinting Jun 25, 2020 N/A N/A -`pytest-factoryboy-state `_ Simple factoryboy random state management Dec 11, 2020 4 - Beta pytest (>=5.0) -`pytest-failed-screenshot `_ Test case fails,take a screenshot,save it,attach it to the allure Feb 28, 2021 N/A N/A -`pytest-failed-to-verify `_ A pytest plugin that helps better distinguishing real test failures from setup flakiness. Aug 08, 2019 5 - Production/Stable pytest (>=4.1.0) -`pytest-faker `_ Faker integration with the pytest framework. Dec 19, 2016 6 - Mature N/A -`pytest-falcon `_ Pytest helpers for Falcon. Sep 07, 2016 4 - Beta N/A -`pytest-falcon-client `_ Pytest `client` fixture for the Falcon Framework Mar 19, 2019 N/A N/A -`pytest-fantasy `_ Pytest plugin for Flask Fantasy Framework Mar 14, 2019 N/A N/A -`pytest-fastapi `_ Dec 27, 2020 N/A N/A -`pytest-fastest `_ Use SCM and coverage to run only needed tests Mar 05, 2020 N/A N/A -`pytest-faulthandler `_ py.test plugin that activates the fault handler module for tests (dummy package) Jul 04, 2019 6 - Mature pytest (>=5.0) -`pytest-fauxfactory `_ Integration of fauxfactory into pytest. Dec 06, 2017 5 - Production/Stable pytest (>=3.2) -`pytest-figleaf `_ py.test figleaf coverage plugin Jan 18, 2010 5 - Production/Stable N/A -`pytest-filedata `_ easily load data from files Jan 17, 2019 4 - Beta N/A -`pytest-filemarker `_ A pytest plugin that runs marked tests when files change. Dec 01, 2020 N/A pytest -`pytest-filter-case `_ run test cases filter by mark Nov 05, 2020 N/A N/A -`pytest-filter-subpackage `_ Pytest plugin for filtering based on sub-packages Jan 09, 2020 3 - Alpha pytest (>=3.0) -`pytest-finer-verdicts `_ A pytest plugin to treat non-assertion failures as test errors. Jun 18, 2020 N/A pytest (>=5.4.3) -`pytest-firefox `_ pytest plugin to manipulate firefox Aug 08, 2017 3 - Alpha pytest (>=3.0.2) -`pytest-fixture-config `_ Fixture configuration utils for py.test May 28, 2019 5 - Production/Stable pytest -`pytest-fixture-marker `_ A pytest plugin to add markers based on fixtures used. Oct 11, 2020 5 - Production/Stable N/A -`pytest-fixture-order `_ pytest plugin to control fixture evaluation order Aug 25, 2020 N/A pytest (>=3.0) -`pytest-fixtures `_ Common fixtures for pytest May 01, 2019 5 - Production/Stable N/A -`pytest-fixture-tools `_ Plugin for pytest which provides tools for fixtures Aug 18, 2020 6 - Mature pytest -`pytest-flake8 `_ pytest plugin to check FLAKE8 requirements Dec 16, 2020 4 - Beta pytest (>=3.5) -`pytest-flake8dir `_ A pytest fixture for testing flake8 plugins. Dec 13, 2020 5 - Production/Stable pytest -`pytest-flakefinder `_ Runs tests multiple times to expose flakiness. Jul 28, 2020 4 - Beta pytest (>=2.7.1) -`pytest-flakes `_ pytest plugin to check source code with pyflakes Nov 28, 2020 5 - Production/Stable N/A -`pytest-flaptastic `_ Flaptastic py.test plugin Mar 17, 2019 N/A N/A -`pytest-flask `_ A set of py.test fixtures to test Flask applications. Feb 27, 2021 5 - Production/Stable pytest (>=5.2) -`pytest-flask-sqlalchemy `_ A pytest plugin for preserving test isolation in Flask-SQlAlchemy using database transactions. Apr 04, 2019 4 - Beta pytest (>=3.2.1) -`pytest-flask-sqlalchemy-transactions `_ Run tests in transactions using pytest, Flask, and SQLalchemy. Aug 02, 2018 4 - Beta pytest (>=3.2.1) -`pytest-focus `_ A pytest plugin that alerts user of failed test cases with screen notifications May 04, 2019 4 - Beta pytest -`pytest-forcefail `_ py.test plugin to make the test failing regardless of pytest.mark.xfail May 15, 2018 4 - Beta N/A -`pytest-forward-compatability `_ A name to avoid typosquating pytest-foward-compatibility Sep 06, 2020 N/A N/A -`pytest-forward-compatibility `_ A pytest plugin to shim pytest commandline options for fowards compatibility Sep 29, 2020 N/A N/A -`pytest-freezegun `_ Wrap tests with fixtures in freeze_time Jul 19, 2020 4 - Beta pytest (>=3.0.0) -`pytest-freeze-reqs `_ Check if requirement files are frozen Nov 14, 2019 N/A N/A -`pytest-func-cov `_ Pytest plugin for measuring function coverage May 24, 2020 3 - Alpha pytest (>=5) -`pytest-funparam `_ An alternative way to parametrize test cases Feb 13, 2021 4 - Beta pytest (>=4.6.0) -`pytest-fxa `_ pytest plugin for Firefox Accounts Aug 28, 2018 5 - Production/Stable N/A -`pytest-fxtest `_ Oct 27, 2020 N/A N/A -`pytest-gc `_ The garbage collector plugin for py.test Feb 01, 2018 N/A N/A -`pytest-gcov `_ Uses gcov to measure test coverage of a C library Feb 01, 2018 3 - Alpha N/A -`pytest-gevent `_ Ensure that gevent is properly patched when invoking pytest Feb 25, 2020 N/A pytest -`pytest-gherkin `_ A flexible framework for executing BDD gherkin tests Jul 27, 2019 3 - Alpha pytest (>=5.0.0) -`pytest-ghostinspector `_ For finding/executing Ghost Inspector tests May 17, 2016 3 - Alpha N/A -`pytest-girder `_ A set of pytest fixtures for testing Girder applications. Mar 12, 2021 N/A N/A -`pytest-git `_ Git repository fixture for py.test May 28, 2019 5 - Production/Stable pytest -`pytest-gitcov `_ Pytest plugin for reporting on coverage of the last git commit. Jan 11, 2020 2 - Pre-Alpha N/A -`pytest-git-fixtures `_ Pytest fixtures for testing with git. Mar 11, 2021 4 - Beta pytest -`pytest-github `_ Plugin for py.test that associates tests with github issues using a marker. Mar 07, 2019 5 - Production/Stable N/A -`pytest-github-actions-annotate-failures `_ pytest plugin to annotate failed tests with a workflow command for GitHub Actions Oct 13, 2020 N/A pytest (>=4.0.0) -`pytest-gitignore `_ py.test plugin to ignore the same files as git Jul 17, 2015 4 - Beta N/A -`pytest-gnupg-fixtures `_ Pytest fixtures for testing with gnupg. Mar 04, 2021 4 - Beta pytest -`pytest-golden `_ Plugin for pytest that offloads expected outputs to data files Nov 23, 2020 N/A pytest (>=6.1.2,<7.0.0) -`pytest-graphql-schema `_ Get graphql schema as fixture for pytest Oct 18, 2019 N/A N/A -`pytest-greendots `_ Green progress dots Feb 08, 2014 3 - Alpha N/A -`pytest-growl `_ Growl notifications for pytest results. Jan 13, 2014 5 - Production/Stable N/A -`pytest-grpc `_ pytest plugin for grpc May 01, 2020 N/A pytest (>=3.6.0) -`pytest-hammertime `_ Display "🔨 " instead of "." for passed pytest tests. Jul 28, 2018 N/A pytest -`pytest-harvest `_ Store data created during your pytest tests execution, and retrieve it at the end of the session, e.g. for applicative benchmarking purposes. Dec 08, 2020 5 - Production/Stable N/A -`pytest-helm-chart `_ A plugin to provide different types and configs of Kubernetes clusters that can be used for testing. Jun 15, 2020 4 - Beta pytest (>=5.4.2,<6.0.0) -`pytest-helm-charts `_ A plugin to provide different types and configs of Kubernetes clusters that can be used for testing. Dec 22, 2020 4 - Beta pytest (>=6.1.2,<7.0.0) -`pytest-helper `_ Functions to help in using the pytest testing framework May 31, 2019 5 - Production/Stable N/A -`pytest-helpers `_ pytest helpers May 17, 2020 N/A pytest -`pytest-helpers-namespace `_ PyTest Helpers Namespace Jan 07, 2019 5 - Production/Stable pytest (>=2.9.1) -`pytest-hidecaptured `_ Hide captured output May 04, 2018 4 - Beta pytest (>=2.8.5) -`pytest-historic `_ Custom report to display pytest historical execution records Apr 08, 2020 N/A pytest -`pytest-historic-hook `_ Custom listener to store execution results into MYSQL DB, which is used for pytest-historic report Apr 08, 2020 N/A pytest -`pytest-homeassistant `_ A pytest plugin for use with homeassistant custom components. Aug 12, 2020 4 - Beta N/A -`pytest-homeassistant-custom-component `_ Experimental package to automatically extract test plugins for Home Assistant custom components Mar 03, 2021 3 - Alpha pytest (==6.2.2) -`pytest-honors `_ Report on tests that honor constraints, and guard against regressions Mar 06, 2020 4 - Beta N/A -`pytest-hoverfly `_ Simplify working with Hoverfly from pytest Mar 04, 2021 N/A pytest (>=5.0) -`pytest-hoverfly-wrapper `_ Integrates the Hoverfly HTTP proxy into Pytest Jan 31, 2021 4 - Beta N/A -`pytest-html `_ pytest plugin for generating HTML reports Dec 13, 2020 5 - Production/Stable pytest (!=6.0.0,>=5.0) -`pytest-html-lee `_ optimized pytest plugin for generating HTML reports Jun 30, 2020 5 - Production/Stable pytest (>=5.0) -`pytest-html-profiling `_ Pytest plugin for generating HTML reports with per-test profiling and optionally call graph visualizations. Based on pytest-html by Dave Hunt. Feb 11, 2020 5 - Production/Stable pytest (>=3.0) -`pytest-html-reporter `_ Generates a static html report based on pytest framework Sep 28, 2020 N/A N/A -`pytest-html-thread `_ pytest plugin for generating HTML reports Dec 29, 2020 5 - Production/Stable N/A -`pytest-http `_ Fixture "http" for http requests Dec 05, 2019 N/A N/A -`pytest-httpbin `_ Easily test your HTTP library against a local copy of httpbin Feb 11, 2019 5 - Production/Stable N/A -`pytest-http-mocker `_ Pytest plugin for http mocking (via https://github.com/vilus/mocker) Oct 20, 2019 N/A N/A -`pytest-httpretty `_ A thin wrapper of HTTPretty for pytest Feb 16, 2014 3 - Alpha N/A -`pytest-httpserver `_ pytest-httpserver is a httpserver for pytest Feb 14, 2021 3 - Alpha pytest ; extra == 'dev' -`pytest-httpx `_ Send responses to httpx. Mar 01, 2021 5 - Production/Stable pytest (==6.*) -`pytest-hue `_ Visualise PyTest status via your Phillips Hue lights May 09, 2019 N/A N/A -`pytest-hypo-25 `_ help hypo module for pytest Jan 12, 2020 3 - Alpha N/A -`pytest-ibutsu `_ A plugin to sent pytest results to an Ibutsu server Mar 09, 2021 4 - Beta pytest -`pytest-icdiff `_ use icdiff for better error messages in pytest assertions Apr 08, 2020 4 - Beta N/A -`pytest-idapro `_ A pytest plugin for idapython. Allows a pytest setup to run tests outside and inside IDA in an automated manner by runnig pytest inside IDA and by mocking idapython api Nov 03, 2018 N/A N/A -`pytest-ignore-flaky `_ ignore failures from flaky tests (pytest plugin) Jan 14, 2019 5 - Production/Stable pytest (>=3.7) -`pytest-image-diff `_ Sep 03, 2020 3 - Alpha pytest -`pytest-incremental `_ an incremental test runner (pytest plugin) Dec 09, 2018 4 - Beta N/A -`pytest-influxdb `_ Plugin for influxdb and pytest integration. Sep 22, 2020 N/A N/A -`pytest-info-collector `_ pytest plugin to collect information from tests May 26, 2019 3 - Alpha N/A -`pytest-informative-node `_ display more node ininformation. Apr 25, 2019 4 - Beta N/A -`pytest-infrastructure `_ pytest stack validation prior to testing executing Apr 12, 2020 4 - Beta N/A -`pytest-inmanta `_ A py.test plugin providing fixtures to simplify inmanta modules testing. Oct 12, 2020 5 - Production/Stable N/A -`pytest-inmanta-extensions `_ Inmanta tests package Jan 07, 2021 5 - Production/Stable N/A -`pytest-Inomaly `_ A simple image diff plugin for pytest Feb 13, 2018 4 - Beta N/A -`pytest-insta `_ A practical snapshot testing plugin for pytest Mar 03, 2021 N/A pytest (>=6.0.2,<7.0.0) -`pytest-instafail `_ pytest plugin to show failures instantly Jun 14, 2020 4 - Beta pytest (>=2.9) -`pytest-instrument `_ pytest plugin to instrument tests Apr 05, 2020 5 - Production/Stable pytest (>=5.1.0) -`pytest-integration `_ Organizing pytests by integration or not Apr 16, 2020 N/A N/A -`pytest-interactive `_ A pytest plugin for console based interactive test selection just after the collection phase Nov 30, 2017 3 - Alpha N/A -`pytest-invenio `_ Pytest fixtures for Invenio. Dec 17, 2020 5 - Production/Stable pytest (<7,>=6) -`pytest-involve `_ Run tests covering a specific file or changeset Feb 02, 2020 4 - Beta pytest (>=3.5.0) -`pytest-ipdb `_ A py.test plug-in to enable drop to ipdb debugger on test failure. Sep 02, 2014 2 - Pre-Alpha N/A -`pytest-ipynb `_ THIS PROJECT IS ABANDONED Jan 29, 2019 3 - Alpha N/A -`pytest-isort `_ py.test plugin to check import ordering using isort Jan 13, 2021 5 - Production/Stable N/A -`pytest-it `_ Pytest plugin to display test reports as a plaintext spec, inspired by Rspec: https://github.com/mattduck/pytest-it. Jan 22, 2020 4 - Beta N/A -`pytest-iterassert `_ Nicer list and iterable assertion messages for pytest May 11, 2020 3 - Alpha N/A -`pytest-jasmine `_ Run jasmine tests from your pytest test suite Nov 04, 2017 1 - Planning N/A -`pytest-jest `_ A custom jest-pytest oriented Pytest reporter May 22, 2018 4 - Beta pytest (>=3.3.2) -`pytest-jira `_ py.test JIRA integration plugin, using markers Nov 29, 2019 N/A N/A -`pytest-jira-xray `_ pytest plugin to integrate tests with JIRA XRAY Feb 12, 2021 3 - Alpha pytest -`pytest-jobserver `_ Limit parallel tests with posix jobserver. May 15, 2019 5 - Production/Stable pytest -`pytest-joke `_ Test failures are better served with humor. Oct 08, 2019 4 - Beta pytest (>=4.2.1) -`pytest-json `_ Generate JSON test reports Jan 18, 2016 4 - Beta N/A -`pytest-jsonlint `_ UNKNOWN Aug 04, 2016 N/A N/A -`pytest-json-report `_ A pytest plugin to report test results as JSON files Oct 23, 2020 4 - Beta pytest (>=4.2.0) -`pytest-kafka `_ Zookeeper, Kafka server, and Kafka consumer fixtures for Pytest Nov 01, 2019 N/A pytest -`pytest-kind `_ Kubernetes test support with KIND for pytest Jan 24, 2021 5 - Production/Stable N/A -`pytest-kivy `_ Kivy GUI tests fixtures using pytest Dec 21, 2020 4 - Beta pytest (>=3.6) -`pytest-knows `_ A pytest plugin that can automaticly skip test case based on dependence info calculated by trace Aug 22, 2014 N/A N/A -`pytest-konira `_ Run Konira DSL tests with py.test Oct 09, 2011 N/A N/A -`pytest-krtech-common `_ pytest krtech common library Nov 28, 2016 4 - Beta N/A -`pytest-kwparametrize `_ Alternate syntax for @pytest.mark.parametrize with test cases as dictionaries and default value fallbacks Jan 22, 2021 N/A pytest (>=6) -`pytest-lambda `_ Define pytest fixtures with lambda functions. Dec 28, 2020 3 - Alpha pytest (>=3.6,<7) -`pytest-lamp `_ Jan 06, 2017 3 - Alpha N/A -`pytest-layab `_ Pytest fixtures for layab. Oct 05, 2020 5 - Production/Stable N/A -`pytest-lazy-fixture `_ It helps to use fixtures in pytest.mark.parametrize Feb 01, 2020 4 - Beta pytest (>=3.2.5) -`pytest-ldap `_ python-ldap fixtures for pytest Aug 18, 2020 N/A pytest -`pytest-leaks `_ A pytest plugin to trace resource leaks. Nov 27, 2019 1 - Planning N/A -`pytest-level `_ Select tests of a given level or lower Oct 21, 2019 N/A pytest -`pytest-libfaketime `_ A python-libfaketime plugin for pytest. Dec 22, 2018 4 - Beta pytest (>=3.0.0) -`pytest-libiio `_ A pytest plugin to manage interfacing with libiio contexts Jan 09, 2021 4 - Beta N/A -`pytest-libnotify `_ Pytest plugin that shows notifications about the test run Nov 12, 2018 3 - Alpha pytest -`pytest-ligo `_ Jan 16, 2020 4 - Beta N/A -`pytest-lineno `_ A pytest plugin to show the line numbers of test functions Dec 04, 2020 N/A pytest -`pytest-lisa `_ Pytest plugin for organizing tests. Jan 21, 2021 3 - Alpha pytest (>=6.1.2,<7.0.0) -`pytest-listener `_ A simple network listener May 28, 2019 5 - Production/Stable pytest -`pytest-litf `_ A pytest plugin that stream output in LITF format Jan 18, 2021 4 - Beta pytest (>=3.1.1) -`pytest-live `_ Live results for pytest Mar 08, 2020 N/A pytest -`pytest-localftpserver `_ A PyTest plugin which provides an FTP fixture for your tests Jan 27, 2021 5 - Production/Stable pytest -`pytest-localserver `_ py.test plugin to test server connections locally. Nov 14, 2018 4 - Beta N/A -`pytest-localstack `_ Pytest plugin for AWS integration tests Aug 22, 2019 4 - Beta pytest (>=3.3.0) -`pytest-lockable `_ lockable resource plugin for pytest Oct 05, 2020 3 - Alpha pytest -`pytest-locker `_ Used to lock object during testing. Essentially changing assertions from being hard coded to asserting that nothing changed Feb 25, 2021 N/A pytest (>=5.4) -`pytest-logbook `_ py.test plugin to capture logbook log messages Nov 23, 2015 5 - Production/Stable pytest (>=2.8) -`pytest-logfest `_ Pytest plugin providing three logger fixtures with basic or full writing to log files Jul 21, 2019 4 - Beta pytest (>=3.5.0) -`pytest-logger `_ Plugin configuring handlers for loggers from Python logging module. Jul 25, 2019 4 - Beta pytest (>=3.2) -`pytest-logging `_ Configures logging and allows tweaking the log level with a py.test flag Nov 04, 2015 4 - Beta N/A -`pytest-log-report `_ Package for creating a pytest test run reprot Dec 26, 2019 N/A N/A -`pytest-manual-marker `_ pytest marker for marking manual tests Nov 28, 2018 3 - Alpha pytest -`pytest-markdown `_ Test your markdown docs with pytest Jan 15, 2021 4 - Beta pytest (>=6.0.1,<7.0.0) -`pytest-marker-bugzilla `_ py.test bugzilla integration plugin, using markers Jan 09, 2020 N/A N/A -`pytest-markers-presence `_ A simple plugin to detect missed pytest tags and markers" Feb 04, 2021 4 - Beta pytest (>=6.0) -`pytest-markfiltration `_ UNKNOWN Nov 08, 2011 3 - Alpha N/A -`pytest-mark-no-py3 `_ pytest plugin and bowler codemod to help migrate tests to Python 3 May 17, 2019 N/A pytest -`pytest-marks `_ UNKNOWN Nov 23, 2012 3 - Alpha N/A -`pytest-matcher `_ Match test output against patterns stored in files Apr 23, 2020 5 - Production/Stable pytest (>=3.4) -`pytest-match-skip `_ Skip matching marks. Matches partial marks using wildcards. May 15, 2019 4 - Beta pytest (>=4.4.1) -`pytest-mat-report `_ this is report Jan 20, 2021 N/A N/A -`pytest-matrix `_ Provide tools for generating tests from combinations of fixtures. Jun 24, 2020 5 - Production/Stable pytest (>=5.4.3,<6.0.0) -`pytest-mccabe `_ pytest plugin to run the mccabe code complexity checker. Jul 22, 2020 3 - Alpha pytest (>=5.4.0) -`pytest-md `_ Plugin for generating Markdown reports for pytest results Jul 11, 2019 3 - Alpha pytest (>=4.2.1) -`pytest-md-report `_ A pytest plugin to make a test results report with Markdown table format. Aug 14, 2020 4 - Beta pytest (!=6.0.0,<7,>=3.3.2) -`pytest-memprof `_ Estimates memory consumption of test functions Mar 29, 2019 4 - Beta N/A -`pytest-menu `_ A pytest plugin for console based interactive test selection just after the collection phase Oct 04, 2017 3 - Alpha pytest (>=2.4.2) -`pytest-mercurial `_ pytest plugin to write integration tests for projects using Mercurial Python internals Nov 21, 2020 1 - Planning N/A -`pytest-messenger `_ Pytest to Slack reporting plugin Dec 16, 2020 5 - Production/Stable N/A -`pytest-metadata `_ pytest plugin for test session metadata Nov 27, 2020 5 - Production/Stable pytest (>=2.9.0) -`pytest-metrics `_ Custom metrics report for pytest Apr 04, 2020 N/A pytest -`pytest-mimesis `_ Mimesis integration with the pytest test runner Mar 21, 2020 5 - Production/Stable pytest (>=4.2) -`pytest-minecraft `_ A pytest plugin for running tests against Minecraft releases Sep 26, 2020 N/A pytest (>=6.0.1,<7.0.0) -`pytest-missing-fixtures `_ Pytest plugin that creates missing fixtures Oct 14, 2020 4 - Beta pytest (>=3.5.0) -`pytest-ml `_ Test your machine learning! May 04, 2019 4 - Beta N/A -`pytest-mocha `_ pytest plugin to display test execution output like a mochajs Apr 02, 2020 4 - Beta pytest (>=5.4.0) -`pytest-mock `_ Thin-wrapper around the mock package for easier use with pytest Jan 10, 2021 5 - Production/Stable pytest (>=5.0) -`pytest-mock-api `_ A mock API server with configurable routes and responses available as a fixture. Feb 13, 2019 1 - Planning pytest (>=4.0.0) -`pytest-mock-helper `_ Help you mock HTTP call and generate mock code Jan 24, 2018 N/A pytest -`pytest-mockito `_ Base fixtures for mockito Jul 11, 2018 4 - Beta N/A -`pytest-mockredis `_ An in-memory mock of a Redis server that runs in a separate thread. This is to be used for unit-tests that require a Redis database. Jan 02, 2018 2 - Pre-Alpha N/A -`pytest-mock-resources `_ A pytest plugin for easily instantiating reproducible mock resources. Feb 17, 2021 N/A pytest (>=1.0) -`pytest-mock-server `_ Mock server plugin for pytest Apr 06, 2020 4 - Beta N/A -`pytest-mockservers `_ A set of fixtures to test your requests to HTTP/UDP servers Mar 31, 2020 N/A pytest (>=4.3.0) -`pytest-modifyjunit `_ Utility for adding additional properties to junit xml for IDM QE Jan 10, 2019 N/A N/A -`pytest-modifyscope `_ pytest plugin to modify fixture scope Apr 12, 2020 N/A pytest -`pytest-molecule `_ PyTest Molecule Plugin :: discover and run molecule tests Jan 25, 2021 5 - Production/Stable N/A -`pytest-mongo `_ MongoDB process and client fixtures plugin for py.test. Jan 12, 2021 5 - Production/Stable pytest (>=3.0.0) -`pytest-mongodb `_ pytest plugin for MongoDB fixtures Dec 07, 2019 5 - Production/Stable pytest (>=2.5.2) -`pytest-monitor `_ Pytest plugin for analyzing resource usage. Feb 07, 2021 5 - Production/Stable pytest -`pytest-monkeyplus `_ pytest's monkeypatch subclass with extra functionalities Sep 18, 2012 5 - Production/Stable N/A -`pytest-monkeytype `_ pytest-monkeytype: Generate Monkeytype annotations from your pytest tests. Jul 29, 2020 4 - Beta N/A -`pytest-moto `_ Fixtures for integration tests of AWS services,uses moto mocking library. Aug 28, 2015 1 - Planning N/A -`pytest-mp `_ A test batcher for multiprocessed Pytest runs May 23, 2018 4 - Beta pytest -`pytest-mpi `_ pytest plugin to collect information from tests Mar 14, 2021 3 - Alpha N/A -`pytest-mpl `_ pytest plugin to help with testing figures output from Matplotlib Nov 05, 2020 4 - Beta pytest -`pytest-mproc `_ low-startup-overhead, scalable, distributed-testing pytest plugin Mar 07, 2021 4 - Beta pytest -`pytest-multihost `_ Utility for writing multi-host tests for pytest Apr 07, 2020 4 - Beta N/A -`pytest-multilog `_ Multi-process logs handling and other helpers for pytest Nov 15, 2020 N/A N/A -`pytest-mutagen `_ Add the mutation testing feature to pytest Jul 24, 2020 N/A pytest (>=5.4) -`pytest-mypy `_ Mypy static type checker plugin for Pytest Nov 14, 2020 4 - Beta pytest (>=3.5) -`pytest-mypyd `_ Mypy static type checker plugin for Pytest Aug 20, 2019 4 - Beta pytest (<4.7,>=2.8) ; python_version < "3.5" -`pytest-mypy-plugins `_ pytest plugin for writing tests for mypy plugins Oct 26, 2020 3 - Alpha pytest (>=6.0.0) -`pytest-mypy-plugins-shim `_ Substitute for "pytest-mypy-plugins" for Python implementations which aren't supported by mypy. Feb 14, 2021 N/A pytest (>=6.0.0) -`pytest-mypy-testing `_ Pytest plugin to check mypy output. Apr 24, 2020 N/A pytest -`pytest-mysql `_ MySQL process and client fixtures for pytest Jul 21, 2020 5 - Production/Stable pytest (>=3.0.0) -`pytest-needle `_ pytest plugin for visual testing websites using selenium Dec 10, 2018 4 - Beta pytest (<5.0.0,>=3.0.0) -`pytest-neo `_ pytest-neo is a plugin for pytest that shows tests like screen of Matrix. Apr 23, 2019 3 - Alpha pytest (>=3.7.2) -`pytest-network `_ A simple plugin to disable network on socket level. May 07, 2020 N/A N/A -`pytest-nginx `_ nginx fixture for pytest Aug 12, 2017 5 - Production/Stable N/A -`pytest-nginx-iplweb `_ nginx fixture for pytest - iplweb temporary fork Mar 01, 2019 5 - Production/Stable N/A -`pytest-ngrok `_ Jan 22, 2020 3 - Alpha N/A -`pytest-ngsfixtures `_ pytest ngs fixtures Sep 06, 2019 2 - Pre-Alpha pytest (>=5.0.0) -`pytest-nice `_ A pytest plugin that alerts user of failed test cases with screen notifications May 04, 2019 4 - Beta pytest -`pytest-nocustom `_ Run all tests without custom markers May 04, 2019 5 - Production/Stable N/A -`pytest-nodev `_ Test-driven source code search for Python. Jul 21, 2016 4 - Beta pytest (>=2.8.1) -`pytest-notebook `_ A pytest plugin for testing Jupyter Notebooks Sep 16, 2020 4 - Beta pytest (>=3.5.0) -`pytest-notice `_ Send pytest execution result email Nov 05, 2020 N/A N/A -`pytest-notification `_ A pytest plugin for sending a desktop notification and playing a sound upon completion of tests Jun 19, 2020 N/A pytest (>=4) -`pytest-notifier `_ A pytest plugin to notify test result Jun 12, 2020 3 - Alpha pytest -`pytest-notimplemented `_ Pytest markers for not implemented features and tests. Aug 27, 2019 N/A pytest (>=5.1,<6.0) -`pytest-notion `_ A PyTest Reporter to send test runs to Notion.so Aug 07, 2019 N/A N/A -`pytest-nunit `_ A pytest plugin for generating NUnit3 test result XML output Aug 04, 2020 4 - Beta pytest (>=3.5.0) -`pytest-ochrus `_ pytest results data-base and HTML reporter Feb 21, 2018 4 - Beta N/A -`pytest-odoo `_ py.test plugin to run Odoo tests Aug 19, 2020 4 - Beta pytest (>=2.9) -`pytest-odoo-fixtures `_ Project description Jun 25, 2019 N/A N/A -`pytest-oerp `_ pytest plugin to test OpenERP modules Feb 28, 2012 3 - Alpha N/A -`pytest-ok `_ The ultimate pytest output plugin Apr 01, 2019 4 - Beta N/A -`pytest-only `_ Use @pytest.mark.only to run a single test Jan 19, 2020 N/A N/A -`pytest-oot `_ Run object-oriented tests in a simple format Sep 18, 2016 4 - Beta N/A -`pytest-openfiles `_ Pytest plugin for detecting inadvertent open file handles Apr 16, 2020 3 - Alpha pytest (>=4.6) -`pytest-opentmi `_ pytest plugin for publish results to opentmi Feb 26, 2021 5 - Production/Stable pytest (>=5.0) -`pytest-operator `_ Fixtures for Operators Feb 20, 2021 N/A N/A -`pytest-optional `_ include/exclude values of fixtures in pytest Oct 07, 2015 N/A N/A -`pytest-optional-tests `_ Easy declaration of optional tests (i.e., that are not run by default) Jul 09, 2019 4 - Beta pytest (>=4.5.0) -`pytest-orchestration `_ A pytest plugin for orchestrating tests Jul 18, 2019 N/A N/A -`pytest-order `_ pytest plugin to run your tests in a specific order Feb 16, 2021 4 - Beta pytest (>=3.7) -`pytest-ordering `_ pytest plugin to run your tests in a specific order Nov 14, 2018 4 - Beta pytest -`pytest-osxnotify `_ OS X notifications for py.test results. May 15, 2015 N/A N/A -`pytest-pact `_ A simple plugin to use with pytest Jan 07, 2019 4 - Beta N/A -`pytest-parallel `_ a pytest plugin for parallel and concurrent testing Apr 30, 2020 3 - Alpha pytest (>=3.0.0) -`pytest-param `_ pytest plugin to test all, first, last or random params Sep 11, 2016 4 - Beta pytest (>=2.6.0) -`pytest-paramark `_ Configure pytest fixtures using a combination of"parametrize" and markers Jan 10, 2020 4 - Beta pytest (>=4.5.0) -`pytest-parametrization `_ Simpler PyTest parametrization Jul 28, 2019 5 - Production/Stable N/A -`pytest-parametrize-cases `_ A more user-friendly way to write parametrized tests. Dec 12, 2020 N/A pytest (>=6.1.2,<7.0.0) -`pytest-parametrized `_ Pytest plugin for parametrizing tests with default iterables. Oct 19, 2020 5 - Production/Stable pytest -`pytest-parawtf `_ Finally spell paramete?ri[sz]e correctly Dec 03, 2018 4 - Beta pytest (>=3.6.0) -`pytest-pass `_ Check out https://github.com/elilutsky/pytest-pass Dec 04, 2019 N/A N/A -`pytest-passrunner `_ Pytest plugin providing the 'run_on_pass' marker Feb 10, 2021 5 - Production/Stable pytest (>=4.6.0) -`pytest-paste-config `_ Allow setting the path to a paste config file Sep 18, 2013 3 - Alpha N/A -`pytest-pdb `_ pytest plugin which adds pdb helper commands related to pytest. Jul 31, 2018 N/A N/A -`pytest-peach `_ pytest plugin for fuzzing with Peach API Security Apr 12, 2019 4 - Beta pytest (>=2.8.7) -`pytest-pep257 `_ py.test plugin for pep257 Jul 09, 2016 N/A N/A -`pytest-pep8 `_ pytest plugin to check PEP8 requirements Apr 27, 2014 N/A N/A -`pytest-percent `_ Change the exit code of pytest test sessions when a required percent of tests pass. May 21, 2020 N/A pytest (>=5.2.0) -`pytest-performance `_ A simple plugin to ensure the execution of critical sections of code has not been impacted Sep 11, 2020 5 - Production/Stable pytest (>=3.7.0) -`pytest-persistence `_ Pytest tool for persistent objects Mar 09, 2021 N/A N/A -`pytest-pgsql `_ Pytest plugins and helpers for tests using a Postgres database. May 13, 2020 5 - Production/Stable pytest (>=3.0.0) -`pytest-picked `_ Run the tests related to the changed files Dec 23, 2020 N/A pytest (>=3.5.0) -`pytest-pigeonhole `_ Jun 25, 2018 5 - Production/Stable pytest (>=3.4) -`pytest-pikachu `_ Show surprise when tests are passing Sep 30, 2019 4 - Beta pytest -`pytest-pilot `_ Slice in your test base thanks to powerful markers. Oct 09, 2020 5 - Production/Stable N/A -`pytest-pings `_ 🦊 The pytest plugin for Firefox Telemetry 📊 Jun 29, 2019 3 - Alpha pytest (>=5.0.0) -`pytest-pinned `_ A simple pytest plugin for pinning tests Jan 21, 2021 4 - Beta pytest (>=3.5.0) -`pytest-pinpoint `_ A pytest plugin which runs SBFL algorithms to detect faults. Sep 25, 2020 N/A pytest (>=4.4.0) -`pytest-pipeline `_ Pytest plugin for functional testing of data analysispipelines Jan 24, 2017 3 - Alpha N/A -`pytest-platform-markers `_ Markers for pytest to skip tests on specific platforms Sep 09, 2019 4 - Beta pytest (>=3.6.0) -`pytest-play `_ pytest plugin that let you automate actions and assertions with test metrics reporting executing plain YAML files Jun 12, 2019 5 - Production/Stable N/A -`pytest-playbook `_ Pytest plugin for reading playbooks. Jan 21, 2021 3 - Alpha pytest (>=6.1.2,<7.0.0) -`pytest-playwright `_ A pytest wrapper with fixtures for Playwright to automate web browsers Feb 25, 2021 N/A pytest -`pytest-plt `_ Fixtures for quickly making Matplotlib plots in tests Aug 17, 2020 5 - Production/Stable pytest -`pytest-plugin-helpers `_ A plugin to help developing and testing other plugins Nov 23, 2019 4 - Beta pytest (>=3.5.0) -`pytest-plus `_ PyTest Plus Plugin :: extends pytest functionality Mar 19, 2020 5 - Production/Stable pytest (>=3.50) -`pytest-pmisc `_ Mar 21, 2019 5 - Production/Stable N/A -`pytest-pointers `_ Pytest plugin to define functions you test with special marks for better navigation and reports Dec 14, 2020 N/A N/A -`pytest-polarion-cfme `_ pytest plugin for collecting test cases and recording test results Nov 13, 2017 3 - Alpha N/A -`pytest-polarion-collect `_ pytest plugin for collecting polarion test cases data Jun 18, 2020 3 - Alpha pytest -`pytest-polecat `_ Provides Polecat pytest fixtures Aug 12, 2019 4 - Beta N/A -`pytest-ponyorm `_ PonyORM in Pytest Oct 31, 2018 N/A pytest (>=3.1.1) -`pytest-poo `_ Visualize your crappy tests Jul 14, 2013 5 - Production/Stable N/A -`pytest-poo-fail `_ Visualize your failed tests with poo Feb 12, 2015 5 - Production/Stable N/A -`pytest-pop `_ A pytest plugin to help with testing pop projects Aug 13, 2020 5 - Production/Stable pytest (>=5.4.0) -`pytest-portion `_ Select a portion of the collected tests Jan 28, 2021 4 - Beta pytest (>=3.5.0) -`pytest-postgres `_ Run PostgreSQL in Docker container in Pytest. Mar 22, 2020 N/A pytest -`pytest-postgresql `_ Postgresql fixtures and fixture factories for Pytest. Feb 23, 2021 5 - Production/Stable pytest (>=3.0.0) -`pytest-power `_ pytest plugin with powerful fixtures Dec 31, 2020 N/A pytest (>=5.4) -`pytest-pride `_ Minitest-style test colors Apr 02, 2016 3 - Alpha N/A -`pytest-print `_ pytest-print adds the printer fixture you can use to print messages to the user (directly to the pytest runner, not stdout) Oct 23, 2020 5 - Production/Stable pytest (>=3.0.0) -`pytest-profiling `_ Profiling plugin for py.test May 28, 2019 5 - Production/Stable pytest -`pytest-progress `_ pytest plugin for instant test progress status Oct 06, 2020 5 - Production/Stable N/A -`pytest-prometheus `_ Report test pass / failures to a Prometheus PushGateway Oct 03, 2017 N/A N/A -`pytest-prosper `_ Test helpers for Prosper projects Sep 24, 2018 N/A N/A -`pytest-pspec `_ A rspec format reporter for Python ptest Jun 02, 2020 4 - Beta pytest (>=3.0.0) -`pytest-pudb `_ Pytest PuDB debugger integration Oct 25, 2018 3 - Alpha pytest (>=2.0) -`pytest-purkinje `_ py.test plugin for purkinje test runner Oct 28, 2017 2 - Pre-Alpha N/A -`pytest-pycharm `_ Plugin for py.test to enter PyCharm debugger on uncaught exceptions Aug 13, 2020 5 - Production/Stable pytest (>=2.3) -`pytest-pycodestyle `_ pytest plugin to run pycodestyle Aug 10, 2020 3 - Alpha N/A -`pytest-pydev `_ py.test plugin to connect to a remote debug server with PyDev or PyCharm. Nov 15, 2017 3 - Alpha N/A -`pytest-pydocstyle `_ pytest plugin to run pydocstyle Aug 10, 2020 3 - Alpha N/A -`pytest-pylint `_ pytest plugin to check source code with pylint Nov 09, 2020 5 - Production/Stable pytest (>=5.4) -`pytest-pypi `_ Easily test your HTTP library against a local copy of pypi Mar 04, 2018 3 - Alpha N/A -`pytest-pypom-navigation `_ Core engine for cookiecutter-qa and pytest-play packages Feb 18, 2019 4 - Beta pytest (>=3.0.7) -`pytest-pyppeteer `_ A plugin to run pyppeteer in pytest. Feb 16, 2021 4 - Beta pytest (>=6.0.2) -`pytest-pyq `_ Pytest fixture "q" for pyq Mar 10, 2020 5 - Production/Stable N/A -`pytest-pyramid `_ pytest_pyramid - provides fixtures for testing pyramid applications with pytest test suite Feb 26, 2021 5 - Production/Stable pytest -`pytest-pyramid-server `_ Pyramid server fixture for py.test May 28, 2019 5 - Production/Stable pytest -`pytest-pytestrail `_ Pytest plugin for interaction with TestRail Aug 27, 2020 4 - Beta pytest (>=3.8.0) -`pytest-pythonpath `_ pytest plugin for adding to the PYTHONPATH from command line or configs. Aug 22, 2018 5 - Production/Stable N/A -`pytest-qml `_ Run QML Tests with pytest Dec 02, 2020 4 - Beta pytest (>=6.0.0) -`pytest-qt `_ pytest support for PyQt and PySide applications Dec 07, 2019 5 - Production/Stable pytest (>=3.0.0) -`pytest-qt-app `_ QT app fixture for py.test Dec 23, 2015 5 - Production/Stable N/A -`pytest-quarantine `_ A plugin for pytest to manage expected test failures Nov 24, 2019 5 - Production/Stable pytest (>=4.6) -`pytest-quickcheck `_ pytest plugin to generate random data inspired by QuickCheck Nov 15, 2020 4 - Beta pytest (<6.0.0,>=4.0) -`pytest-rabbitmq `_ RabbitMQ process and client fixtures for pytest Jan 11, 2021 5 - Production/Stable pytest (>=3.0.0) -`pytest-race `_ Race conditions tester for pytest Nov 21, 2016 4 - Beta N/A -`pytest-rage `_ pytest plugin to implement PEP712 Oct 21, 2011 3 - Alpha N/A -`pytest-raises `_ An implementation of pytest.raises as a pytest.mark fixture Apr 23, 2020 N/A pytest (>=3.2.2) -`pytest-raisesregexp `_ Simple pytest plugin to look for regex in Exceptions Dec 18, 2015 N/A N/A -`pytest-raisin `_ Plugin enabling the use of exception instances with pytest.raises Jun 25, 2020 N/A pytest -`pytest-random `_ py.test plugin to randomize tests Apr 28, 2013 3 - Alpha N/A -`pytest-randomly `_ Pytest plugin to randomly order tests and control random.seed. Nov 16, 2020 5 - Production/Stable pytest -`pytest-randomness `_ Pytest plugin about random seed management May 30, 2019 3 - Alpha N/A -`pytest-random-num `_ Randomise the order in which pytest tests are run with some control over the randomness Oct 19, 2020 5 - Production/Stable N/A -`pytest-random-order `_ Randomise the order in which pytest tests are run with some control over the randomness Nov 30, 2018 5 - Production/Stable pytest (>=3.0.0) -`pytest-readme `_ Test your README.md file Dec 28, 2014 5 - Production/Stable N/A -`pytest-reana `_ Pytest fixtures for REANA. Nov 24, 2020 3 - Alpha N/A -`pytest-recording `_ A pytest plugin that allows you recording of network interactions via VCR.py Nov 25, 2020 4 - Beta pytest (>=3.5.0) -`pytest-recordings `_ Provides pytest plugins for reporting request/response traffic, screenshots, and more to ReportPortal Aug 13, 2020 N/A N/A -`pytest-redis `_ Redis fixtures and fixture factories for Pytest. Oct 15, 2019 5 - Production/Stable pytest (>=3.0.0) -`pytest-redmine `_ Pytest plugin for redmine Mar 19, 2018 1 - Planning N/A -`pytest-ref `_ A plugin to store reference files to ease regression testing Nov 23, 2019 4 - Beta pytest (>=3.5.0) -`pytest-reference-formatter `_ Conveniently run pytest with a dot-formatted test reference. Oct 01, 2019 4 - Beta N/A -`pytest-regressions `_ Easy to use fixtures to write regression tests. Jan 27, 2021 5 - Production/Stable pytest (>=3.5.0) -`pytest-regtest `_ pytest plugin for regression tests Sep 16, 2020 N/A N/A -`pytest-relaxed `_ Relaxed test discovery/organization for pytest Jun 14, 2019 5 - Production/Stable pytest (<5,>=3) -`pytest-remfiles `_ Pytest plugin to create a temporary directory with remote files Jul 01, 2019 5 - Production/Stable N/A -`pytest-remotedata `_ Pytest plugin for controlling remote data access. Jul 20, 2019 3 - Alpha pytest (>=3.1) -`pytest-remove-stale-bytecode `_ py.test plugin to remove stale byte code files. Mar 04, 2020 4 - Beta pytest -`pytest-reorder `_ Reorder tests depending on their paths and names. May 31, 2018 4 - Beta pytest -`pytest-repeat `_ pytest plugin for repeating tests Oct 31, 2020 5 - Production/Stable pytest (>=3.6) -`pytest-replay `_ Saves previous test runs and allow re-execute previous pytest runs to reproduce crashes or flaky tests Dec 09, 2020 4 - Beta pytest (>=3.0.0) -`pytest-repo-health `_ A pytest plugin to report on repository standards conformance Nov 03, 2020 3 - Alpha pytest -`pytest-report `_ Creates json report that is compatible with atom.io's linter message format May 11, 2016 4 - Beta N/A -`pytest-reporter `_ Generate Pytest reports with templates Nov 05, 2020 4 - Beta pytest -`pytest-reporter-html1 `_ A basic HTML report template for Pytest Nov 02, 2020 4 - Beta N/A -`pytest-reportinfra `_ Pytest plugin for reportinfra Aug 11, 2019 3 - Alpha N/A -`pytest-reporting `_ A plugin to report summarized results in a table format Oct 25, 2019 4 - Beta pytest (>=3.5.0) -`pytest-reportlog `_ Replacement for the --resultlog option, focused in simplicity and extensibility Dec 11, 2020 3 - Alpha pytest (>=5.2) -`pytest-report-me `_ A pytest plugin to generate report. Dec 31, 2020 N/A pytest -`pytest-report-parameters `_ pytest plugin for adding tests' parameters to junit report Jun 18, 2020 3 - Alpha pytest (>=2.4.2) -`pytest-reportportal `_ Agent for Reporting results of tests to the Report Portal Feb 15, 2021 N/A pytest (>=3.0.7) -`pytest-reqs `_ pytest plugin to check pinned requirements May 12, 2019 N/A pytest (>=2.4.2) -`pytest-requests `_ A simple plugin to use with pytest Jun 24, 2019 4 - Beta pytest (>=3.5.0) -`pytest-reraise `_ Make multi-threaded pytest test cases fail when they should Jun 03, 2020 5 - Production/Stable N/A -`pytest-rerun `_ Re-run only changed files in specified branch Jul 08, 2019 N/A pytest (>=3.6) -`pytest-rerunfailures `_ pytest plugin to re-run tests to eliminate flaky failures Sep 29, 2020 5 - Production/Stable pytest (>=5.0) -`pytest-resilient-circuits `_ Resilient Circuits fixtures for PyTest. Feb 19, 2021 N/A N/A -`pytest-resource `_ Load resource fixture plugin to use with pytest Nov 14, 2018 4 - Beta N/A -`pytest-resource-path `_ Provides path for uniform access to test resources in isolated directory Aug 18, 2020 5 - Production/Stable pytest (>=3.5.0) -`pytest-responsemock `_ Simplified requests calls mocking for pytest Oct 10, 2020 5 - Production/Stable N/A -`pytest-responses `_ py.test integration for responses Jan 29, 2019 N/A N/A -`pytest-restrict `_ Pytest plugin to restrict the test types allowed Dec 03, 2020 5 - Production/Stable pytest -`pytest-rethinkdb `_ A RethinkDB plugin for pytest. Jul 24, 2016 4 - Beta N/A -`pytest-reverse `_ Pytest plugin to reverse test order. Dec 27, 2020 5 - Production/Stable pytest -`pytest-ringo `_ pytest plugin to test webapplications using the Ringo webframework Sep 27, 2017 3 - Alpha N/A -`pytest-rng `_ Fixtures for seeding tests and making randomness reproducible Aug 08, 2019 5 - Production/Stable pytest -`pytest-roast `_ pytest plugin for ROAST configuration override and fixtures Feb 05, 2021 5 - Production/Stable pytest (<6) -`pytest-rotest `_ Pytest integration with rotest Sep 08, 2019 N/A pytest (>=3.5.0) -`pytest-rpc `_ Extend py.test for RPC OpenStack testing. Feb 22, 2019 4 - Beta pytest (~=3.6) -`pytest-rt `_ pytest data collector plugin for Testgr Mar 03, 2021 N/A N/A -`pytest-rts `_ Coverage-based regression test selection (RTS) plugin for pytest Mar 03, 2021 N/A pytest -`pytest-runfailed `_ implement a --failed option for pytest Mar 24, 2016 N/A N/A -`pytest-runner `_ Invoke py.test as distutils command with dependency resolution Feb 12, 2021 5 - Production/Stable pytest (!=3.7.3,>=3.5) ; extra == 'testing' -`pytest-salt `_ Pytest Salt Plugin Jan 27, 2020 4 - Beta N/A -`pytest-salt-containers `_ A Pytest plugin that builds and creates docker containers Nov 09, 2016 4 - Beta N/A -`pytest-salt-factories `_ Pytest Salt Plugin Mar 05, 2021 4 - Beta pytest (>=6.1.1) -`pytest-salt-from-filenames `_ Simple PyTest Plugin For Salt's Test Suite Specifically Jan 29, 2019 4 - Beta pytest (>=4.1) -`pytest-salt-runtests-bridge `_ Simple PyTest Plugin For Salt's Test Suite Specifically Dec 05, 2019 4 - Beta pytest (>=4.1) -`pytest-sanic `_ a pytest plugin for Sanic Feb 27, 2021 N/A pytest (>=5.2) -`pytest-sanity `_ Dec 07, 2020 N/A N/A -`pytest-sa-pg `_ May 14, 2019 N/A N/A -`pytest-sbase `_ A complete web automation framework for end-to-end testing. Mar 10, 2021 5 - Production/Stable N/A -`pytest-scenario `_ pytest plugin for test scenarios Feb 06, 2017 3 - Alpha N/A -`pytest-schema `_ 👍 Validate return values against a schema-like object in testing Aug 31, 2020 5 - Production/Stable pytest (>=3.5.0) -`pytest-securestore `_ An encrypted password store for use within pytest cases Jun 19, 2019 4 - Beta N/A -`pytest-select `_ A pytest plugin which allows to (de-)select tests from a file. Jan 18, 2019 3 - Alpha pytest (>=3.0) -`pytest-selenium `_ pytest plugin for Selenium Sep 19, 2020 5 - Production/Stable pytest (>=5.0.0) -`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Mar 10, 2021 5 - Production/Stable N/A -`pytest-selenium-enhancer `_ pytest plugin for Selenium Nov 26, 2020 5 - Production/Stable N/A -`pytest-selenium-pdiff `_ A pytest package implementing perceptualdiff for Selenium tests. Apr 06, 2017 2 - Pre-Alpha N/A -`pytest-send-email `_ Send pytest execution result email Dec 04, 2019 N/A N/A -`pytest-sentry `_ A pytest plugin to send testrun information to Sentry.io Mar 02, 2021 N/A N/A -`pytest-server-fixtures `_ Extensible server fixures for py.test May 28, 2019 5 - Production/Stable pytest -`pytest-serverless `_ Automatically mocks resources from serverless.yml in pytest using moto. Feb 20, 2021 4 - Beta N/A -`pytest-services `_ Services plugin for pytest testing framework Oct 30, 2020 6 - Mature N/A -`pytest-session2file `_ pytest-session2file (aka: pytest-session_to_file for v0.1.0 - v0.1.2) is a py.test plugin for capturing and saving to file the stdout of py.test. Jan 26, 2021 3 - Alpha pytest -`pytest-session-fixture-globalize `_ py.test plugin to make session fixtures behave as if written in conftest, even if it is written in some modules May 15, 2018 4 - Beta N/A -`pytest-session_to_file `_ pytest-session_to_file is a py.test plugin for capturing and saving to file the stdout of py.test. Oct 01, 2015 3 - Alpha N/A -`pytest-sftpserver `_ py.test plugin to locally test sftp server connections. Sep 16, 2019 4 - Beta N/A -`pytest-shard `_ Dec 11, 2020 4 - Beta pytest -`pytest-shell `_ A pytest plugin for testing shell scripts and line-based processes Jan 18, 2020 N/A N/A -`pytest-sheraf `_ Versatile ZODB abstraction layer - pytest fixtures Feb 11, 2020 N/A pytest -`pytest-sherlock `_ pytest plugin help to find coupled tests Jul 13, 2020 5 - Production/Stable pytest (>=3.5.1) -`pytest-shortcuts `_ Expand command-line shortcuts listed in pytest configuration Oct 29, 2020 4 - Beta pytest (>=3.5.0) -`pytest-shutil `_ A goodie-bag of unix shell and environment tools for py.test May 28, 2019 5 - Production/Stable pytest -`pytest-simple-plugin `_ Simple pytest plugin Nov 27, 2019 N/A N/A -`pytest-simple-settings `_ simple-settings plugin for pytest Nov 17, 2020 4 - Beta pytest -`pytest-single-file-logging `_ Allow for multiple processes to log to a single file May 05, 2016 4 - Beta pytest (>=2.8.1) -`pytest-skipper `_ A plugin that selects only tests with changes in execution path Mar 26, 2017 3 - Alpha pytest (>=3.0.6) -`pytest-skippy `_ Automatically skip tests that don't need to run! Jan 27, 2018 3 - Alpha pytest (>=2.3.4) -`pytest-slack `_ Pytest to Slack reporting plugin Dec 15, 2020 5 - Production/Stable N/A -`pytest-smartcollect `_ A plugin for collecting tests that touch changed code Oct 04, 2018 N/A pytest (>=3.5.0) -`pytest-smartcov `_ Smart coverage plugin for pytest. Sep 30, 2017 3 - Alpha N/A -`pytest-smtp `_ Send email with pytest execution result Feb 20, 2021 N/A pytest -`pytest-snail `_ Plugin for adding a marker to slow running tests. 🐌 Nov 04, 2019 3 - Alpha pytest (>=5.0.1) -`pytest-snapci `_ py.test plugin for Snap-CI Nov 12, 2015 N/A N/A -`pytest-snapshot `_ A plugin to enable snapshot testing with pytest. Jan 22, 2021 4 - Beta pytest (>=3.0.0) -`pytest-snmpserver `_ Sep 14, 2020 N/A N/A -`pytest-socket `_ Pytest Plugin to disable socket calls during tests May 31, 2020 4 - Beta pytest (>=3.6.3) -`pytest-soft-assertions `_ May 05, 2020 3 - Alpha pytest -`pytest-solr `_ Solr process and client fixtures for py.test. May 11, 2020 3 - Alpha pytest (>=3.0.0) -`pytest-sorter `_ A simple plugin to first execute tests that historically failed more Jul 23, 2020 4 - Beta pytest (>=3.1.1) -`pytest-sourceorder `_ Test-ordering plugin for pytest Apr 11, 2017 4 - Beta pytest -`pytest-spark `_ pytest plugin to run the tests with support of pyspark. Feb 23, 2020 4 - Beta pytest -`pytest-spawner `_ py.test plugin to spawn process and communicate with them. Jul 31, 2015 4 - Beta N/A -`pytest-spec `_ Library pytest-spec is a pytest plugin to display test execution output like a SPECIFICATION. Jan 14, 2021 N/A N/A -`pytest-sphinx `_ Doctest plugin for pytest with support for Sphinx-specific doctest-directives Aug 05, 2020 4 - Beta N/A -`pytest-spiratest `_ Exports unit tests as test runs in SpiraTest/Team/Plan Feb 12, 2021 N/A N/A -`pytest-splinter `_ Splinter plugin for pytest testing framework Dec 25, 2020 6 - Mature N/A -`pytest-split `_ Pytest plugin for splitting test suite based on test execution time Apr 07, 2020 1 - Planning N/A -`pytest-splitio `_ Split.io SDK integration for e2e tests Sep 22, 2020 N/A pytest (<7,>=5.0) -`pytest-split-tests `_ A Pytest plugin for running a subset of your tests by splitting them in to equally sized groups. Forked from Mark Adams' original project pytest-test-groups. May 28, 2019 N/A pytest (>=2.5) -`pytest-splunk-addon `_ A Dynamic test tool for Splunk Apps and Add-ons Feb 26, 2021 N/A pytest (>5.4.0,<6.1) -`pytest-splunk-addon-ui-smartx `_ Library to support testing Splunk Add-on UX Jan 18, 2021 N/A N/A -`pytest-splunk-env `_ pytest fixtures for interaction with Splunk Enterprise and Splunk Cloud Oct 22, 2020 N/A pytest (>=6.1.1,<7.0.0) -`pytest-sqitch `_ sqitch for pytest Apr 06, 2020 4 - Beta N/A -`pytest-sqlalchemy `_ pytest plugin with sqlalchemy related fixtures Mar 13, 2018 3 - Alpha N/A -`pytest-sql-bigquery `_ Yet another SQL-testing framework for BigQuery provided by pytest plugin Dec 19, 2019 N/A pytest -`pytest-srcpaths `_ Add paths to sys.path Feb 18, 2021 N/A N/A -`pytest-ssh `_ pytest plugin for ssh command run May 27, 2019 N/A pytest -`pytest-start-from `_ Start pytest run from a given point Apr 11, 2016 N/A N/A -`pytest-statsd `_ pytest plugin for reporting to graphite Nov 30, 2018 5 - Production/Stable pytest (>=3.0.0) -`pytest-stepfunctions `_ A small description Jul 07, 2020 4 - Beta pytest -`pytest-steps `_ Create step-wise / incremental tests in pytest. Apr 25, 2020 5 - Production/Stable N/A -`pytest-stepwise `_ Run a test suite one failing test at a time. Dec 01, 2015 4 - Beta N/A -`pytest-stoq `_ A plugin to pytest stoq Feb 09, 2021 4 - Beta N/A -`pytest-stress `_ A Pytest plugin that allows you to loop tests for a user defined amount of time. Dec 07, 2019 4 - Beta pytest (>=3.6.0) -`pytest-structlog `_ Structured logging assertions Jul 16, 2020 N/A pytest -`pytest-structmpd `_ provide structured temporary directory Oct 17, 2018 N/A N/A -`pytest-stub `_ Stub packages, modules and attributes. Apr 28, 2020 5 - Production/Stable N/A -`pytest-stubprocess `_ Provide stub implementations for subprocesses in Python tests Sep 17, 2018 3 - Alpha pytest (>=3.5.0) -`pytest-study `_ A pytest plugin to organize long run tests (named studies) without interfering the regular tests Sep 26, 2017 3 - Alpha pytest (>=2.0) -`pytest-subprocess `_ A plugin to fake subprocess for pytest Aug 22, 2020 5 - Production/Stable pytest (>=4.0.0) -`pytest-subtesthack `_ A hack to explicitly set up and tear down fixtures. Mar 02, 2021 N/A N/A -`pytest-subtests `_ unittest subTest() support and subtests fixture Dec 13, 2020 4 - Beta pytest (>=5.3.0) -`pytest-subunit `_ pytest-subunit is a plugin for py.test which outputs testsresult in subunit format. Aug 29, 2017 N/A N/A -`pytest-sugar `_ pytest-sugar is a plugin for pytest that changes the default look and feel of pytest (e.g. progressbar, show tests that fail instantly). Jul 06, 2020 3 - Alpha N/A -`pytest-sugar-bugfix159 `_ Workaround for https://github.com/Frozenball/pytest-sugar/issues/159 Nov 07, 2018 5 - Production/Stable pytest (!=3.7.3,>=3.5); extra == 'testing' -`pytest-super-check `_ Pytest plugin to check your TestCase classes call super in setUp, tearDown, etc. Dec 13, 2020 5 - Production/Stable pytest -`pytest-svn `_ SVN repository fixture for py.test May 28, 2019 5 - Production/Stable pytest -`pytest-symbols `_ pytest-symbols is a pytest plugin that adds support for passing test environment symbols into pytest tests. Nov 20, 2017 3 - Alpha N/A -`pytest-tap `_ Test Anything Protocol (TAP) reporting plugin for pytest Nov 07, 2020 5 - Production/Stable pytest (>=3.0) -`pytest-target `_ Pytest plugin for remote target orchestration. Jan 21, 2021 3 - Alpha pytest (>=6.1.2,<7.0.0) -`pytest-tblineinfo `_ tblineinfo is a py.test plugin that insert the node id in the final py.test report when --tb=line option is used Dec 01, 2015 3 - Alpha pytest (>=2.0) -`pytest-teamcity-logblock `_ py.test plugin to introduce block structure in teamcity build log, if output is not captured May 15, 2018 4 - Beta N/A -`pytest-telegram `_ Pytest to Telegram reporting plugin Dec 10, 2020 5 - Production/Stable N/A -`pytest-tempdir `_ Predictable and repeatable tempdir support. Oct 11, 2019 4 - Beta pytest (>=2.8.1) -`pytest-terraform `_ A pytest plugin for using terraform fixtures Oct 20, 2020 N/A pytest (>=6.0.0,<6.1.0) -`pytest-terraform-fixture `_ generate terraform resources to use with pytest Nov 14, 2018 4 - Beta N/A -`pytest-testbook `_ A plugin to run tests written in Jupyter notebook Dec 11, 2016 3 - Alpha N/A -`pytest-testconfig `_ Test configuration plugin for pytest. Jan 11, 2020 4 - Beta pytest (>=3.5.0) -`pytest-testdirectory `_ A py.test plugin providing temporary directories in unit tests. Nov 06, 2018 5 - Production/Stable pytest -`pytest-testdox `_ A testdox format reporter for pytest Oct 13, 2020 5 - Production/Stable pytest (>=3.7.0) -`pytest-test-groups `_ A Pytest plugin for running a subset of your tests by splitting them in to equally sized groups. Oct 25, 2016 5 - Production/Stable N/A -`pytest-testinfra `_ Test infrastructures Nov 12, 2020 5 - Production/Stable pytest (!=3.0.2) -`pytest-testlink-adaptor `_ pytest reporting plugin for testlink Dec 20, 2018 4 - Beta pytest (>=2.6) -`pytest-testmon `_ selects tests affected by changed files and methods Aug 05, 2020 4 - Beta N/A -`pytest-testobject `_ Plugin to use TestObject Suites with Pytest Sep 24, 2019 4 - Beta pytest (>=3.1.1) -`pytest-testrail `_ pytest plugin for creating TestRail runs and adding results Aug 27, 2020 N/A pytest (>=3.6) -`pytest-testrail2 `_ A small example package Nov 17, 2020 N/A pytest (>=5) -`pytest-testrail-api `_ Плагин Pytest, для интеграции с TestRail Dec 09, 2020 N/A pytest (>=5.5) -`pytest-testrail-client `_ pytest plugin for Testrail Sep 29, 2020 5 - Production/Stable N/A -`pytest-testrail-e2e `_ pytest plugin for creating TestRail runs and adding results Jun 11, 2020 N/A pytest (>=3.6) -`pytest-testrail-plugin `_ PyTest plugin for TestRail Apr 21, 2020 3 - Alpha pytest -`pytest-testrail-reporter `_ Sep 10, 2018 N/A N/A -`pytest-testslide `_ TestSlide fixture for pytest Jan 07, 2021 5 - Production/Stable pytest (~=6.2) -`pytest-test-this `_ Plugin for py.test to run relevant tests, based on naively checking if a test contains a reference to the symbol you supply Sep 15, 2019 2 - Pre-Alpha pytest (>=2.3) -`pytest-tesults `_ Tesults plugin for pytest May 18, 2020 5 - Production/Stable pytest (>=3.5.0) -`pytest-tezos `_ pytest-ligo Jan 16, 2020 4 - Beta N/A -`pytest-thawgun `_ Pytest plugin for time travel May 26, 2020 3 - Alpha N/A -`pytest-threadleak `_ Detects thread leaks Sep 08, 2017 4 - Beta N/A -`pytest-timeit `_ A pytest plugin to time test function runs Oct 13, 2016 4 - Beta N/A -`pytest-timeout `_ py.test plugin to abort hanging tests Jul 15, 2020 5 - Production/Stable pytest (>=3.6.0) -`pytest-timeouts `_ Linux-only Pytest plugin to control durations of various test case execution phases Sep 21, 2019 5 - Production/Stable N/A -`pytest-timer `_ A timer plugin for pytest Dec 13, 2020 N/A N/A -`pytest-tipsi-django `_ Oct 14, 2020 4 - Beta pytest (>=6.0.0) -`pytest-tipsi-testing `_ Better fixtures management. Various helpers Nov 04, 2020 4 - Beta pytest (>=3.3.0) -`pytest-tldr `_ A pytest plugin that limits the output to just the things you need. Mar 12, 2021 4 - Beta pytest (>=3.5.0) -`pytest-tm4j-reporter `_ Cloud Jira Test Management (TM4J) PyTest reporter plugin Sep 01, 2020 N/A pytest -`pytest-todo `_ A small plugin for the pytest testing framework, marking TODO comments as failure May 23, 2019 4 - Beta pytest -`pytest-tomato `_ Mar 01, 2019 5 - Production/Stable N/A -`pytest-toolbelt `_ This is just a collection of utilities for pytest, but don't really belong in pytest proper. Aug 12, 2019 3 - Alpha N/A -`pytest-toolbox `_ Numerous useful plugins for pytest. Apr 07, 2018 N/A pytest (>=3.5.0) -`pytest-tornado `_ A py.test plugin providing fixtures and markers to simplify testing of asynchronous tornado applications. Jun 17, 2020 5 - Production/Stable pytest (>=3.6) -`pytest-tornado5 `_ A py.test plugin providing fixtures and markers to simplify testing of asynchronous tornado applications. Nov 16, 2018 5 - Production/Stable pytest (>=3.6) -`pytest-tornado-yen3 `_ A py.test plugin providing fixtures and markers to simplify testing of asynchronous tornado applications. Oct 15, 2018 5 - Production/Stable N/A -`pytest-tornasync `_ py.test plugin for testing Python 3.5+ Tornado code Jul 15, 2019 3 - Alpha pytest (>=3.0) -`pytest-track `_ Feb 26, 2021 3 - Alpha pytest (>=3.0) -`pytest-translations `_ Test your translation files. Oct 26, 2020 5 - Production/Stable N/A -`pytest-travis-fold `_ Folds captured output sections in Travis CI build log Nov 29, 2017 4 - Beta pytest (>=2.6.0) -`pytest-trello `_ Plugin for py.test that integrates trello using markers Nov 20, 2015 5 - Production/Stable N/A -`pytest-trepan `_ Pytest plugin for trepan debugger. Jul 28, 2018 5 - Production/Stable N/A -`pytest-trialtemp `_ py.test plugin for using the same _trial_temp working directory as trial Jun 08, 2015 N/A N/A -`pytest-trio `_ Pytest plugin for trio Oct 16, 2020 N/A N/A -`pytest-tspwplib `_ A simple plugin to use with tspwplib Jan 08, 2021 4 - Beta pytest (>=3.5.0) -`pytest-tstcls `_ Test Class Base Mar 23, 2020 5 - Production/Stable N/A -`pytest-twisted `_ A twisted plugin for pytest. Sep 11, 2020 5 - Production/Stable pytest (>=2.3) -`pytest-typhoon-xray `_ Typhoon HIL plugin for pytest Jun 22, 2020 4 - Beta pytest (>=5.4.2) -`pytest-tytest `_ Typhoon HIL plugin for pytest May 25, 2020 4 - Beta pytest (>=5.4.2) -`pytest-ubersmith `_ Easily mock calls to ubersmith at the `requests` level. Apr 13, 2015 N/A N/A -`pytest-ui `_ Text User Interface for running python tests May 03, 2020 4 - Beta pytest -`pytest-unhandled-exception-exit-code `_ Plugin for py.test set a different exit code on uncaught exceptions Jun 22, 2020 5 - Production/Stable pytest (>=2.3) -`pytest-unittest-filter `_ A pytest plugin for filtering unittest-based test classes Jan 12, 2019 4 - Beta pytest (>=3.1.0) -`pytest-unmarked `_ Run only unmarked tests Aug 27, 2019 5 - Production/Stable N/A -`pytest-unordered `_ Test equality of unordered collections in pytest Nov 02, 2020 4 - Beta pytest (>=6.0.0) -`pytest-vagrant `_ A py.test plugin providing access to vagrant. Mar 23, 2020 5 - Production/Stable pytest -`pytest-valgrind `_ Mar 15, 2020 N/A N/A -`pytest-variables `_ pytest plugin for providing variables to tests/fixtures Oct 23, 2019 5 - Production/Stable pytest (>=2.4.2) -`pytest-vcr `_ Plugin for managing VCR.py cassettes Apr 26, 2019 5 - Production/Stable pytest (>=3.6.0) -`pytest-vcrpandas `_ Test from HTTP interactions to dataframe processed. Jan 12, 2019 4 - Beta pytest -`pytest-venv `_ py.test fixture for creating a virtual environment Aug 04, 2020 4 - Beta pytest -`pytest-verbose-parametrize `_ More descriptive output for parametrized py.test tests May 28, 2019 5 - Production/Stable pytest -`pytest-vimqf `_ A simple pytest plugin that will shrink pytest output when specified, to fit vim quickfix window. Feb 08, 2021 4 - Beta pytest (>=6.2.2,<7.0.0) -`pytest-virtualenv `_ Virtualenv fixture for py.test May 28, 2019 5 - Production/Stable pytest -`pytest-voluptuous `_ Pytest plugin for asserting data against voluptuous schema. Jun 09, 2020 N/A pytest -`pytest-vscodedebug `_ A pytest plugin to easily enable debugging tests within Visual Studio Code Dec 04, 2020 4 - Beta N/A -`pytest-vts `_ pytest plugin for automatic recording of http stubbed tests Jun 05, 2019 N/A pytest (>=2.3) -`pytest-vw `_ pytest-vw makes your failing test cases succeed under CI tools scrutiny Oct 07, 2015 4 - Beta N/A -`pytest-vyper `_ Plugin for the vyper smart contract language. May 28, 2020 2 - Pre-Alpha N/A -`pytest-wa-e2e-plugin `_ Pytest plugin for testing whatsapp bots with end to end tests Feb 18, 2020 4 - Beta pytest (>=3.5.0) -`pytest-watch `_ Local continuous test runner with pytest and watchdog. May 20, 2018 N/A N/A -`pytest-wdl `_ Pytest plugin for testing WDL workflows. Nov 17, 2020 5 - Production/Stable N/A -`pytest-webdriver `_ Selenium webdriver fixture for py.test May 28, 2019 5 - Production/Stable pytest -`pytest-wetest `_ Welian API Automation test framework pytest plugin Nov 10, 2018 4 - Beta N/A -`pytest-whirlwind `_ Testing Tornado. Jun 12, 2020 N/A N/A -`pytest-wholenodeid `_ pytest addon for displaying the whole node id for failures Aug 26, 2015 4 - Beta pytest (>=2.0) -`pytest-winnotify `_ Windows tray notifications for py.test results. Apr 22, 2016 N/A N/A -`pytest-workflow `_ A pytest plugin for configuring workflow/pipeline tests using YAML files Dec 14, 2020 5 - Production/Stable pytest (>=5.4.0) -`pytest-xdist `_ pytest xdist plugin for distributed testing and loop-on-failing modes Feb 09, 2021 5 - Production/Stable pytest (>=6.0.0) -`pytest-xdist-debug-for-graingert `_ pytest xdist plugin for distributed testing and loop-on-failing modes Jul 24, 2019 5 - Production/Stable pytest (>=4.4.0) -`pytest-xdist-forked `_ forked from pytest-xdist Feb 10, 2020 5 - Production/Stable pytest (>=4.4.0) -`pytest-xfaillist `_ Maintain a xfaillist in an additional file to avoid merge-conflicts. Mar 07, 2021 N/A pytest (>=6.2.2,<7.0.0) -`pytest-xfiles `_ Pytest fixtures providing data read from function, module or package related (x)files. Feb 27, 2018 N/A N/A -`pytest-xlog `_ Extended logging for test and decorators May 31, 2020 4 - Beta N/A -`pytest-xpara `_ An extended parametrizing plugin of pytest. Oct 30, 2017 3 - Alpha pytest -`pytest-xprocess `_ A pytest plugin for managing processes across test runs. Mar 02, 2021 4 - Beta pytest (>=2.8) -`pytest-xray `_ May 30, 2019 3 - Alpha N/A -`pytest-xrayjira `_ Mar 17, 2020 3 - Alpha pytest (==4.3.1) -`pytest-xray-server `_ Mar 03, 2021 3 - Alpha N/A -`pytest-xvfb `_ A pytest plugin to run Xvfb for tests. Jun 09, 2020 4 - Beta pytest (>=2.8.1) -`pytest-yaml `_ This plugin is used to load yaml output to your test using pytest framework. Oct 05, 2018 N/A pytest -`pytest-yamltree `_ Create or check file/directory trees described by YAML Mar 02, 2020 4 - Beta pytest (>=3.1.1) -`pytest-yamlwsgi `_ Run tests against wsgi apps defined in yaml May 11, 2010 N/A N/A -`pytest-yapf `_ Run yapf Jul 06, 2017 4 - Beta pytest (>=3.1.1) -`pytest-yapf3 `_ Validate your Python file format with yapf Aug 03, 2020 5 - Production/Stable pytest (>=5.4) -`pytest-yield `_ PyTest plugin to run tests concurrently, each `yield` switch context to other one Jan 23, 2019 N/A N/A -`pytest-zafira `_ A Zafira plugin for pytest Sep 18, 2019 5 - Production/Stable pytest (==4.1.1) -`pytest-zap `_ OWASP ZAP plugin for py.test. May 12, 2014 4 - Beta N/A -`pytest-zigzag `_ Extend py.test for RPC OpenStack testing. Feb 27, 2019 4 - Beta pytest (~=3.6) -============================================================================================================== ======================================================================================================================================================================== ============== ===================== ============================================ diff --git a/doc/en/reference/fixtures.rst b/doc/en/reference/fixtures.rst index 69e1d20b1c2..6d9b134a763 100644 --- a/doc/en/reference/fixtures.rst +++ b/doc/en/reference/fixtures.rst @@ -5,7 +5,7 @@ .. _`pytest.fixture`: -pytest fixtures: explicit, modular, scalable +Fixtures reference ======================================================== .. seealso:: :ref:`about-fixtures` diff --git a/doc/en/reference/index.rst b/doc/en/reference/index.rst index b62484cdf3c..fbff0978e88 100644 --- a/doc/en/reference/index.rst +++ b/doc/en/reference/index.rst @@ -7,13 +7,6 @@ Reference guides :maxdepth: 1 fixtures - warnings - doctest - cache - unittest - xunit_setup plugin_list - writing_plugins - logging customize reference diff --git a/scripts/update-plugin-list.py b/scripts/update-plugin-list.py index f80e63127ee..bc4d8a6a66b 100644 --- a/scripts/update-plugin-list.py +++ b/scripts/update-plugin-list.py @@ -74,7 +74,7 @@ def iter_plugins(): def main(): plugins = list(iter_plugins()) plugin_table = tabulate.tabulate(plugins, headers="keys", tablefmt="rst") - plugin_list = pathlib.Path("doc", "en", "plugin_list.rst") + plugin_list = pathlib.Path("doc", "en", "reference", "plugin_list.rst") with plugin_list.open("w") as f: f.write(FILE_HEAD) f.write(f"This list contains {len(plugins)} plugins.\n\n") From b26d1bb18f925901f894c995182a9b908a3b698b Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sun, 14 Mar 2021 21:58:37 +0200 Subject: [PATCH 186/630] cacheprovider: add cache.mkdir() as a Path-returning replacement to makedir() It is not possible to change a return type in a compatible way, so a new method is added. --- changelog/7259.feature.rst | 2 ++ src/_pytest/cacheprovider.py | 21 +++++++++++++++------ testing/test_cacheprovider.py | 14 +++++++------- 3 files changed, 24 insertions(+), 13 deletions(-) create mode 100644 changelog/7259.feature.rst diff --git a/changelog/7259.feature.rst b/changelog/7259.feature.rst new file mode 100644 index 00000000000..e19aaca5291 --- /dev/null +++ b/changelog/7259.feature.rst @@ -0,0 +1,2 @@ +Added :meth:`cache.mkdir() `, which is similar to the existing :meth:`cache.makedir() `, +but returns a :class:`pathlib.Path` instead of a legacy ``py.path.local``. diff --git a/src/_pytest/cacheprovider.py b/src/_pytest/cacheprovider.py index a7ec7989184..62112c6e343 100755 --- a/src/_pytest/cacheprovider.py +++ b/src/_pytest/cacheprovider.py @@ -60,10 +60,10 @@ class Cache: _cachedir = attr.ib(type=Path, repr=False) _config = attr.ib(type=Config, repr=False) - # sub-directory under cache-dir for directories created by "makedir" + # Sub-directory under cache-dir for directories created by `mkdir()`. _CACHE_PREFIX_DIRS = "d" - # sub-directory under cache-dir for values created by "set" + # Sub-directory under cache-dir for values created by `set()`. _CACHE_PREFIX_VALUES = "v" def __init__( @@ -121,13 +121,15 @@ def warn(self, fmt: str, *, _ispytest: bool = False, **args: object) -> None: stacklevel=3, ) - def makedir(self, name: str) -> LEGACY_PATH: + def mkdir(self, name: str) -> Path: """Return a directory path object with the given name. If the directory does not yet exist, it will be created. You can use it to manage files to e.g. store/retrieve database dumps across test sessions. + .. versionadded:: 6.3 + :param name: Must be a string not containing a ``/`` separator. Make sure the name contains your plugin or application @@ -138,7 +140,14 @@ def makedir(self, name: str) -> LEGACY_PATH: raise ValueError("name is not allowed to contain path separators") res = self._cachedir.joinpath(self._CACHE_PREFIX_DIRS, path) res.mkdir(exist_ok=True, parents=True) - return legacy_path(res) + return res + + def makedir(self, name: str) -> LEGACY_PATH: + """Return a directory path object with the given name. + + Same as :func:`mkdir`, but returns a legacy py path instance. + """ + return legacy_path(self.mkdir(name)) def _getvaluepath(self, key: str) -> Path: return self._cachedir.joinpath(self._CACHE_PREFIX_VALUES, Path(key)) @@ -572,8 +581,8 @@ def cacheshow(config: Config, session: Session) -> int: contents = sorted(ddir.rglob(glob)) tw.sep("-", "cache directories for %r" % glob) for p in contents: - # if p.check(dir=1): - # print("%s/" % p.relto(basedir)) + # if p.is_dir(): + # print("%s/" % p.relative_to(basedir)) if p.is_file(): key = str(p.relative_to(basedir)) tw.line(f"{key} is a file of length {p.stat().st_size:d}") diff --git a/testing/test_cacheprovider.py b/testing/test_cacheprovider.py index 2cb657efc16..e631e4ad854 100644 --- a/testing/test_cacheprovider.py +++ b/testing/test_cacheprovider.py @@ -14,15 +14,15 @@ class TestNewAPI: - def test_config_cache_makedir(self, pytester: Pytester) -> None: + def test_config_cache_mkdir(self, pytester: Pytester) -> None: pytester.makeini("[pytest]") config = pytester.parseconfigure() assert config.cache is not None with pytest.raises(ValueError): - config.cache.makedir("key/name") + config.cache.mkdir("key/name") - p = config.cache.makedir("name") - assert p.check() + p = config.cache.mkdir("name") + assert p.is_dir() def test_config_cache_dataerror(self, pytester: Pytester) -> None: pytester.makeini("[pytest]") @@ -217,9 +217,9 @@ def pytest_configure(config): config.cache.set("my/name", [1,2,3]) config.cache.set("my/hello", "world") config.cache.set("other/some", {1:2}) - dp = config.cache.makedir("mydb") - dp.ensure("hello") - dp.ensure("world") + dp = config.cache.mkdir("mydb") + dp.joinpath("hello").touch() + dp.joinpath("world").touch() """ ) result = pytester.runpytest() From a03ee028177f625823940d91f5672c95b4598b47 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sun, 14 Mar 2021 22:50:27 +0200 Subject: [PATCH 187/630] config: make `collect_ignore` accept any PathLike The main reason is to remove a reference to `py.path`. --- doc/en/reference/reference.rst | 2 +- src/_pytest/config/__init__.py | 4 +--- testing/test_collection.py | 13 ++++++++++--- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/doc/en/reference/reference.rst b/doc/en/reference/reference.rst index 4c5e668d26e..9cb3fba4003 100644 --- a/doc/en/reference/reference.rst +++ b/doc/en/reference/reference.rst @@ -936,7 +936,7 @@ pytest treats some global variables in a special manner when defined in a test m **Tutorial**: :ref:`customizing-test-collection` Can be declared in *conftest.py files* to exclude test directories or modules. -Needs to be ``list[str]``. +Needs to be a list of paths (``str``, :class:`pathlib.Path` or any :class:`os.PathLike`). .. code-block:: python diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 144f1c9d112..624fbd02f8c 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -1445,9 +1445,7 @@ def _getconftest_pathlist(self, name: str, path: Path) -> Optional[List[Path]]: modpath = Path(mod.__file__).parent values: List[Path] = [] for relroot in relroots: - if isinstance(relroot, Path): - pass - elif isinstance(relroot, LEGACY_PATH): + if isinstance(relroot, os.PathLike): relroot = Path(relroot) else: relroot = relroot.replace("/", os.sep) diff --git a/testing/test_collection.py b/testing/test_collection.py index f015578e2ba..33087fd2edb 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -367,12 +367,19 @@ def pytest_ignore_collect(path, config): def test_collectignore_exclude_on_option(self, pytester: Pytester) -> None: pytester.makeconftest( """ - # potentially avoid dependency on pylib - from _pytest.compat import legacy_path from pathlib import Path - collect_ignore = [legacy_path('hello'), 'test_world.py', Path('bye')] + + class MyPathLike: + def __init__(self, path): + self.path = path + def __fspath__(self): + return "path" + + collect_ignore = [MyPathLike('hello'), 'test_world.py', Path('bye')] + def pytest_addoption(parser): parser.addoption("--XX", action="store_true", default=False) + def pytest_configure(config): if config.getvalue("XX"): collect_ignore[:] = [] From f0c7043138507b9e33c2afdc5e00b3ab5af48f49 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sun, 14 Mar 2021 22:20:53 +0200 Subject: [PATCH 188/630] Remove/replace some more unnecessary uses of py.path --- doc/en/example/nonpython/conftest.py | 8 +-- src/_pytest/doctest.py | 2 - src/_pytest/main.py | 2 +- src/_pytest/pytester.py | 2 +- src/_pytest/python.py | 8 +-- testing/acceptance_test.py | 6 +- testing/conftest.py | 8 +-- .../package_infinite_recursion/conftest.py | 2 +- .../fixtures/custom_item/conftest.py | 4 +- .../conftest.py | 4 +- testing/python/collect.py | 12 ++-- testing/python/fixtures.py | 8 +-- testing/test_collection.py | 63 +++++++++---------- testing/test_conftest.py | 2 +- testing/test_junitxml.py | 12 ++-- testing/test_skipping.py | 4 +- testing/test_terminal.py | 6 +- 17 files changed, 75 insertions(+), 78 deletions(-) diff --git a/doc/en/example/nonpython/conftest.py b/doc/en/example/nonpython/conftest.py index bdcc8b76222..8a32814edf8 100644 --- a/doc/en/example/nonpython/conftest.py +++ b/doc/en/example/nonpython/conftest.py @@ -2,9 +2,9 @@ import pytest -def pytest_collect_file(parent, path): - if path.ext == ".yaml" and path.basename.startswith("test"): - return YamlFile.from_parent(parent, fspath=path) +def pytest_collect_file(parent, fspath): + if fspath.suffix == ".yaml" and fspath.name.startswith("test"): + return YamlFile.from_parent(parent, path=fspath) class YamlFile(pytest.File): @@ -12,7 +12,7 @@ def collect(self): # We need a yaml parser, e.g. PyYAML. import yaml - raw = yaml.safe_load(self.fspath.open()) + raw = yaml.safe_load(self.path.open()) for name, spec in sorted(raw.items()): yield YamlItem.from_parent(self, name=name, spec=spec) diff --git a/src/_pytest/doctest.py b/src/_pytest/doctest.py index b8e46297a81..5eaeccc4766 100644 --- a/src/_pytest/doctest.py +++ b/src/_pytest/doctest.py @@ -28,7 +28,6 @@ from _pytest._code.code import ReprFileLocation from _pytest._code.code import TerminalRepr from _pytest._io import TerminalWriter -from _pytest.compat import LEGACY_PATH from _pytest.compat import legacy_path from _pytest.compat import safe_getattr from _pytest.config import Config @@ -122,7 +121,6 @@ def pytest_unconfigure() -> None: def pytest_collect_file( fspath: Path, - path: LEGACY_PATH, parent: Collector, ) -> Optional[Union["DoctestModule", "DoctestTextfile"]]: config = parent.config diff --git a/src/_pytest/main.py b/src/_pytest/main.py index 3e7213489ff..b6de7a8ddc3 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -465,7 +465,7 @@ class Session(nodes.FSCollector): def __init__(self, config: Config) -> None: super().__init__( path=config.rootpath, - fspath=config.rootdir, + fspath=None, parent=None, config=config, session=self, diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index df106abb330..febae07857e 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -912,7 +912,7 @@ def copy_example(self, name: Optional[str] = None) -> Path: example_dir = self._request.config.getini("pytester_example_dir") if example_dir is None: raise ValueError("pytester_example_dir is unset, can't copy examples") - example_dir = Path(str(self._request.config.rootdir)) / example_dir + example_dir = self._request.config.rootpath / example_dir for extra_element in self._request.node.iter_markers("pytester_example_path"): assert extra_element.args diff --git a/src/_pytest/python.py b/src/_pytest/python.py index ccd685f54a9..905b40d893f 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -210,11 +210,11 @@ def path_matches_patterns(path: Path, patterns: Iterable[str]) -> bool: return any(fnmatch_ex(pattern, path) for pattern in patterns) -def pytest_pycollect_makemodule(fspath: Path, path: LEGACY_PATH, parent) -> "Module": +def pytest_pycollect_makemodule(fspath: Path, parent) -> "Module": if fspath.name == "__init__.py": - pkg: Package = Package.from_parent(parent, fspath=path) + pkg: Package = Package.from_parent(parent, path=fspath) return pkg - mod: Module = Module.from_parent(parent, fspath=path) + mod: Module = Module.from_parent(parent, path=fspath) return mod @@ -691,7 +691,7 @@ def _collectfile( assert ( fspath.is_file() ), "{!r} is not a file (isdir={!r}, exists={!r}, islink={!r})".format( - path, fspath.is_dir(), fspath.exists(), fspath.is_symlink() + fspath, fspath.is_dir(), fspath.exists(), fspath.is_symlink() ) ihook = self.session.gethookproxy(fspath) if not self.session.isinitpath(fspath): diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py index b7ec18a9cb6..452849f2bc6 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -304,9 +304,9 @@ def runtest(self): class MyCollector(pytest.File): def collect(self): return [MyItem.from_parent(name="xyz", parent=self)] - def pytest_collect_file(path, parent): - if path.basename.startswith("conftest"): - return MyCollector.from_parent(fspath=path, parent=parent) + def pytest_collect_file(fspath, parent): + if fspath.name.startswith("conftest"): + return MyCollector.from_parent(path=fspath, parent=parent) """ ) result = pytester.runpytest(c.name + "::" + "xyz") diff --git a/testing/conftest.py b/testing/conftest.py index 2dc20bcb2fd..63817b9ad1b 100644 --- a/testing/conftest.py +++ b/testing/conftest.py @@ -114,13 +114,13 @@ def dummy_yaml_custom_test(pytester: Pytester): """ import pytest - def pytest_collect_file(parent, path): - if path.ext == ".yaml" and path.basename.startswith("test"): - return YamlFile.from_parent(fspath=path, parent=parent) + def pytest_collect_file(parent, fspath): + if fspath.suffix == ".yaml" and fspath.name.startswith("test"): + return YamlFile.from_parent(path=fspath, parent=parent) class YamlFile(pytest.File): def collect(self): - yield YamlItem.from_parent(name=self.fspath.basename, parent=self) + yield YamlItem.from_parent(name=self.path.name, parent=self) class YamlItem(pytest.Item): def runtest(self): diff --git a/testing/example_scripts/collect/package_infinite_recursion/conftest.py b/testing/example_scripts/collect/package_infinite_recursion/conftest.py index 9629fa646af..d9e7a89bdaa 100644 --- a/testing/example_scripts/collect/package_infinite_recursion/conftest.py +++ b/testing/example_scripts/collect/package_infinite_recursion/conftest.py @@ -1,2 +1,2 @@ -def pytest_ignore_collect(path): +def pytest_ignore_collect(fspath): return False diff --git a/testing/example_scripts/fixtures/custom_item/conftest.py b/testing/example_scripts/fixtures/custom_item/conftest.py index 161934b58f7..1b3940e95a4 100644 --- a/testing/example_scripts/fixtures/custom_item/conftest.py +++ b/testing/example_scripts/fixtures/custom_item/conftest.py @@ -11,5 +11,5 @@ def collect(self): yield CustomItem.from_parent(name="foo", parent=self) -def pytest_collect_file(path, parent): - return CustomFile.from_parent(fspath=path, parent=parent) +def pytest_collect_file(fspath, parent): + return CustomFile.from_parent(path=fspath, parent=parent) diff --git a/testing/example_scripts/issue88_initial_file_multinodes/conftest.py b/testing/example_scripts/issue88_initial_file_multinodes/conftest.py index a053a638a9f..7227a53b783 100644 --- a/testing/example_scripts/issue88_initial_file_multinodes/conftest.py +++ b/testing/example_scripts/issue88_initial_file_multinodes/conftest.py @@ -6,8 +6,8 @@ def collect(self): return [MyItem.from_parent(name="hello", parent=self)] -def pytest_collect_file(path, parent): - return MyFile.from_parent(fspath=path, parent=parent) +def pytest_collect_file(fspath, parent): + return MyFile.from_parent(path=fspath, parent=parent) class MyItem(pytest.Item): diff --git a/testing/python/collect.py b/testing/python/collect.py index bb4c937c01e..633212d9511 100644 --- a/testing/python/collect.py +++ b/testing/python/collect.py @@ -778,9 +778,9 @@ def test_pytest_pycollect_module(self, pytester: Pytester) -> None: import pytest class MyModule(pytest.Module): pass - def pytest_pycollect_makemodule(path, parent): - if path.basename == "test_xyz.py": - return MyModule.from_parent(fspath=path, parent=parent) + def pytest_pycollect_makemodule(fspath, parent): + if fspath.name == "test_xyz.py": + return MyModule.from_parent(path=fspath, parent=parent) """ ) pytester.makepyfile("def test_some(): pass") @@ -882,9 +882,9 @@ def find_module(self, name, path=None): return Loader() sys.meta_path.append(Finder()) - def pytest_collect_file(path, parent): - if path.ext == ".narf": - return Module.from_parent(fspath=path, parent=parent)""" + def pytest_collect_file(fspath, parent): + if fspath.suffix == ".narf": + return Module.from_parent(path=fspath, parent=parent)""" ) pytester.makefile( ".narf", diff --git a/testing/python/fixtures.py b/testing/python/fixtures.py index 8d0dc538aa5..3eb4b80f39e 100644 --- a/testing/python/fixtures.py +++ b/testing/python/fixtures.py @@ -3208,10 +3208,10 @@ class TestRequestScopeAccess: pytestmark = pytest.mark.parametrize( ("scope", "ok", "error"), [ - ["session", "", "fspath class function module"], - ["module", "module fspath", "cls function"], - ["class", "module fspath cls", "function"], - ["function", "module fspath cls function", ""], + ["session", "", "path class function module"], + ["module", "module path", "cls function"], + ["class", "module path cls", "function"], + ["function", "module path cls function", ""], ], ) diff --git a/testing/test_collection.py b/testing/test_collection.py index 33087fd2edb..98d0e174440 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -95,9 +95,9 @@ def test_getcustomfile_roundtrip(self, pytester: Pytester) -> None: import pytest class CustomFile(pytest.File): pass - def pytest_collect_file(path, parent): - if path.ext == ".xxx": - return CustomFile.from_parent(fspath=path, parent=parent) + def pytest_collect_file(fspath, parent): + if fspath.suffix == ".xxx": + return CustomFile.from_parent(path=fspath, parent=parent) """ ) node = pytester.getpathnode(hello) @@ -271,15 +271,15 @@ def test_pytest_collect_file(self, pytester: Pytester) -> None: wascalled = [] class Plugin: - def pytest_collect_file(self, path): - if not path.basename.startswith("."): + def pytest_collect_file(self, fspath: Path) -> None: + if not fspath.name.startswith("."): # Ignore hidden files, e.g. .testmondata. - wascalled.append(path) + wascalled.append(fspath) pytester.makefile(".abc", "xyz") pytest.main(pytester.path, plugins=[Plugin()]) assert len(wascalled) == 1 - assert wascalled[0].ext == ".abc" + assert wascalled[0].suffix == ".abc" class TestPrunetraceback: @@ -292,15 +292,15 @@ def test_custom_repr_failure(self, pytester: Pytester) -> None: pytester.makeconftest( """ import pytest - def pytest_collect_file(path, parent): - return MyFile.from_parent(fspath=path, parent=parent) + def pytest_collect_file(fspath, parent): + return MyFile.from_parent(path=fspath, parent=parent) class MyError(Exception): pass class MyFile(pytest.File): def collect(self): raise MyError() def repr_failure(self, excinfo): - if excinfo.errisinstance(MyError): + if isinstance(excinfo.value, MyError): return "hello world" return pytest.File.repr_failure(self, excinfo) """ @@ -335,9 +335,8 @@ class TestCustomConftests: def test_ignore_collect_path(self, pytester: Pytester) -> None: pytester.makeconftest( """ - def pytest_ignore_collect(path, config): - return path.basename.startswith("x") or \ - path.basename == "test_one.py" + def pytest_ignore_collect(fspath, config): + return fspath.name.startswith("x") or fspath.name == "test_one.py" """ ) sub = pytester.mkdir("xy123") @@ -352,7 +351,7 @@ def pytest_ignore_collect(path, config): def test_ignore_collect_not_called_on_argument(self, pytester: Pytester) -> None: pytester.makeconftest( """ - def pytest_ignore_collect(path, config): + def pytest_ignore_collect(fspath, config): return True """ ) @@ -420,9 +419,9 @@ def test_pytest_fs_collect_hooks_are_seen(self, pytester: Pytester) -> None: import pytest class MyModule(pytest.Module): pass - def pytest_collect_file(path, parent): - if path.ext == ".py": - return MyModule.from_parent(fspath=path, parent=parent) + def pytest_collect_file(fspath, parent): + if fspath.suffix == ".py": + return MyModule.from_parent(path=fspath, parent=parent) """ ) pytester.mkdir("sub") @@ -438,9 +437,9 @@ def test_pytest_collect_file_from_sister_dir(self, pytester: Pytester) -> None: import pytest class MyModule1(pytest.Module): pass - def pytest_collect_file(path, parent): - if path.ext == ".py": - return MyModule1.from_parent(fspath=path, parent=parent) + def pytest_collect_file(fspath, parent): + if fspath.suffix == ".py": + return MyModule1.from_parent(path=fspath, parent=parent) """ ) conf1.replace(sub1.joinpath(conf1.name)) @@ -449,9 +448,9 @@ def pytest_collect_file(path, parent): import pytest class MyModule2(pytest.Module): pass - def pytest_collect_file(path, parent): - if path.ext == ".py": - return MyModule2.from_parent(fspath=path, parent=parent) + def pytest_collect_file(fspath, parent): + if fspath.suffix == ".py": + return MyModule2.from_parent(path=fspath, parent=parent) """ ) conf2.replace(sub2.joinpath(conf2.name)) @@ -540,9 +539,9 @@ def runtest(self): class SpecialFile(pytest.File): def collect(self): return [SpecialItem.from_parent(name="check", parent=self)] - def pytest_collect_file(path, parent): - if path.basename == %r: - return SpecialFile.from_parent(fspath=path, parent=parent) + def pytest_collect_file(fspath, parent): + if fspath.name == %r: + return SpecialFile.from_parent(path=fspath, parent=parent) """ % p.name ) @@ -762,13 +761,13 @@ def pytest_configure(config): config.pluginmanager.register(Plugin2()) class Plugin2(object): - def pytest_collect_file(self, path, parent): - if path.ext == ".abc": - return MyFile2.from_parent(fspath=path, parent=parent) + def pytest_collect_file(self, fspath, parent): + if fspath.suffix == ".abc": + return MyFile2.from_parent(path=fspath, parent=parent) - def pytest_collect_file(path, parent): - if path.ext == ".abc": - return MyFile1.from_parent(fspath=path, parent=parent) + def pytest_collect_file(fspath, parent): + if fspath.suffix == ".abc": + return MyFile1.from_parent(path=fspath, parent=parent) class MyFile1(pytest.File): def collect(self): diff --git a/testing/test_conftest.py b/testing/test_conftest.py index 3497b7cc4fd..344c9bc5162 100644 --- a/testing/test_conftest.py +++ b/testing/test_conftest.py @@ -629,7 +629,7 @@ def test_hook_proxy(pytester: Pytester) -> None: "root/demo-0/test_foo1.py": "def test1(): pass", "root/demo-a/test_foo2.py": "def test1(): pass", "root/demo-a/conftest.py": """\ - def pytest_ignore_collect(path, config): + def pytest_ignore_collect(fspath, config): return True """, "root/demo-b/test_foo3.py": "def test1(): pass", diff --git a/testing/test_junitxml.py b/testing/test_junitxml.py index 1c76351eafc..139e2a9a705 100644 --- a/testing/test_junitxml.py +++ b/testing/test_junitxml.py @@ -979,9 +979,9 @@ def test_summing_simple( pytester.makeconftest( """ import pytest - def pytest_collect_file(path, parent): - if path.ext == ".xyz": - return MyItem.from_parent(name=path.basename, parent=parent) + def pytest_collect_file(fspath, parent): + if fspath.suffix == ".xyz": + return MyItem.from_parent(name=fspath.name, parent=parent) class MyItem(pytest.Item): def runtest(self): raise ValueError(42) @@ -1430,9 +1430,9 @@ def collect(self): NoFunItem.from_parent(name='b', parent=self), ] - def pytest_collect_file(path, parent): - if path.check(ext='.py'): - return FunCollector.from_parent(fspath=path, parent=parent) + def pytest_collect_file(fspath, parent): + if fspath.suffix == '.py': + return FunCollector.from_parent(path=fspath, parent=parent) """ ) diff --git a/testing/test_skipping.py b/testing/test_skipping.py index 349de6e080f..bba36421ace 100644 --- a/testing/test_skipping.py +++ b/testing/test_skipping.py @@ -1303,7 +1303,7 @@ class MyItem(pytest.Item): def runtest(self): pytest.xfail("Expected Failure") - def pytest_collect_file(path, parent): + def pytest_collect_file(fspath, parent): return MyItem.from_parent(name="foo", parent=parent) """ ) @@ -1377,7 +1377,7 @@ def setup(self): def runtest(self): assert False - def pytest_collect_file(path, parent): + def pytest_collect_file(fspath, parent): return MyItem.from_parent(name="foo", parent=parent) """ ) diff --git a/testing/test_terminal.py b/testing/test_terminal.py index 53bced8e68e..5dc5a1cb69f 100644 --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -1036,8 +1036,8 @@ def test_more_quiet_reporting(self, pytester: Pytester) -> None: def test_report_collectionfinish_hook(self, pytester: Pytester, params) -> None: pytester.makeconftest( """ - def pytest_report_collectionfinish(config, startpath, startdir, items): - return ['hello from hook: {0} items'.format(len(items))] + def pytest_report_collectionfinish(config, startpath, items): + return [f'hello from hook: {len(items)} items'] """ ) pytester.makepyfile( @@ -1462,7 +1462,7 @@ def pytest_report_header(config): ) pytester.mkdir("a").joinpath("conftest.py").write_text( """ -def pytest_report_header(config, startdir, startpath): +def pytest_report_header(config, startpath): return ["line1", str(startpath)] """ ) From 584fd0f3876ccdc87008b1b43d25956a9e55e92a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 15 Mar 2021 17:01:35 +0000 Subject: [PATCH 189/630] [pre-commit.ci] pre-commit autoupdate --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 83a50be93c4..b96fdccf10c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ repos: exclude: _pytest/(debugging|hookspec).py language_version: python3 - repo: https://gitlab.com/pycqa/flake8 - rev: 3.8.4 + rev: 3.9.0 hooks: - id: flake8 language_version: python3 @@ -39,7 +39,7 @@ repos: - id: pyupgrade args: [--py36-plus] - repo: https://github.com/asottile/setup-cfg-fmt - rev: v1.16.0 + rev: v1.17.0 hooks: - id: setup-cfg-fmt - repo: https://github.com/pre-commit/pygrep-hooks From 7781582d5227afed19c2516fc7f438e67b66e458 Mon Sep 17 00:00:00 2001 From: Daniele Procida Date: Mon, 15 Mar 2021 23:20:55 +0000 Subject: [PATCH 190/630] Restructured global TOC * Changed "Table Of Contents" link to a plain heading. * Removed Customize, API Reference, 3rd party plugins links altogether. * Added an "About the project" heading --- doc/en/_templates/globaltoc.html | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/doc/en/_templates/globaltoc.html b/doc/en/_templates/globaltoc.html index b6e74e8fbaf..7c595e7ebf2 100644 --- a/doc/en/_templates/globaltoc.html +++ b/doc/en/_templates/globaltoc.html @@ -1,4 +1,4 @@ -

          {{ _('Table Of Contents') }}

          +

          Contents

          + +

          About the project

          +
          • Changelog
          • Contributing
          • Backwards Compatibility
          • From 1f131afb078e714521ed4e400ad7bfeb5836dd14 Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Tue, 16 Mar 2021 18:54:52 +0100 Subject: [PATCH 191/630] Fix value in error message about negative relative tolerance --- src/_pytest/python_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_pytest/python_api.py b/src/_pytest/python_api.py index 7e0c86479d4..8158afd4592 100644 --- a/src/_pytest/python_api.py +++ b/src/_pytest/python_api.py @@ -317,7 +317,7 @@ def set_default(x, default): if relative_tolerance < 0: raise ValueError( - f"relative tolerance can't be negative: {absolute_tolerance}" + f"relative tolerance can't be negative: {relative_tolerance}" ) if math.isnan(relative_tolerance): raise ValueError("relative tolerance can't be NaN.") From 43b451e95e978f0828b293421c9d0930b7e0db1e Mon Sep 17 00:00:00 2001 From: Petr Viktorin Date: Tue, 16 Mar 2021 20:09:17 +0100 Subject: [PATCH 192/630] Add tests for the error message --- testing/python/approx.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/testing/python/approx.py b/testing/python/approx.py index db6124e3914..a8b2955d764 100644 --- a/testing/python/approx.py +++ b/testing/python/approx.py @@ -141,6 +141,13 @@ def test_negative_tolerance( with pytest.raises(ValueError): 1.1 == approx(1, rel, abs) + def test_negative_tolerance_message(self): + # Error message for negative tolerance should include the value. + with pytest.raises(ValueError, match='-3'): + 0 == approx(1, abs=-3) + with pytest.raises(ValueError, match='-3'): + 0 == approx(1, rel=-3) + def test_inf_tolerance(self): # Everything should be equal if the tolerance is infinite. large_diffs = [(1, 1000), (1e-50, 1e50), (-1.0, -1e300), (0.0, 10)] From da4abd1c8273b39b5bc7d22c1e99e05d3300a9ba Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 16 Mar 2021 19:10:50 +0000 Subject: [PATCH 193/630] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- testing/python/approx.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testing/python/approx.py b/testing/python/approx.py index a8b2955d764..538dbd397d1 100644 --- a/testing/python/approx.py +++ b/testing/python/approx.py @@ -143,9 +143,9 @@ def test_negative_tolerance( def test_negative_tolerance_message(self): # Error message for negative tolerance should include the value. - with pytest.raises(ValueError, match='-3'): + with pytest.raises(ValueError, match="-3"): 0 == approx(1, abs=-3) - with pytest.raises(ValueError, match='-3'): + with pytest.raises(ValueError, match="-3"): 0 == approx(1, rel=-3) def test_inf_tolerance(self): From 6ab461f460a8165f3d580b302ada4351d1128157 Mon Sep 17 00:00:00 2001 From: Daniele Procida Date: Tue, 16 Mar 2021 20:26:05 +0000 Subject: [PATCH 194/630] Explicitly listed the four documentation sections on index.rst --- doc/en/builtin.rst | 2 +- doc/en/explanation/index.rst | 2 ++ doc/en/how-to/index.rst | 2 ++ doc/en/index.rst | 7 +++++-- doc/en/reference/index.rst | 2 ++ doc/en/reference/reference.rst | 2 +- 6 files changed, 13 insertions(+), 4 deletions(-) diff --git a/doc/en/builtin.rst b/doc/en/builtin.rst index d4d06af1548..ad6efd657cd 100644 --- a/doc/en/builtin.rst +++ b/doc/en/builtin.rst @@ -6,7 +6,7 @@ Pytest API and builtin fixtures ================================================ -Most of the information of this page has been moved over to :ref:`reference`. +Most of the information of this page has been moved over to :ref:`api-reference`. For information on plugin hooks and objects, see :ref:`plugins`. diff --git a/doc/en/explanation/index.rst b/doc/en/explanation/index.rst index 518fac99915..53910f1eb7b 100644 --- a/doc/en/explanation/index.rst +++ b/doc/en/explanation/index.rst @@ -1,5 +1,7 @@ :orphan: +.. _explanation: + Explanation ================ diff --git a/doc/en/how-to/index.rst b/doc/en/how-to/index.rst index 35b112994d9..3629591aa5e 100644 --- a/doc/en/how-to/index.rst +++ b/doc/en/how-to/index.rst @@ -1,5 +1,7 @@ :orphan: +.. _how-to: + How-to guides ================ diff --git a/doc/en/index.rst b/doc/en/index.rst index 102426d462a..098b5f18159 100644 --- a/doc/en/index.rst +++ b/doc/en/index.rst @@ -87,7 +87,10 @@ Features Documentation ------------- -Please see :ref:`Contents ` for full documentation, including installation, tutorials and PDF documents. +* :ref:`Get started ` - install pytest and grasp its basics just twenty minutes +* :ref:`How-to guides ` - step-by-step guides, covering a vast range of use-cases and needs +* :ref:`Reference guides ` - includes the complete pytest API reference, lists of plugins and more +* :ref:`Explanation ` - background, discussion of key topics, answers to higher-level questions Bugs/Requests @@ -128,7 +131,7 @@ Save time, reduce risk, and improve code health, while paying the maintainers of `Learn more. `_ Security -^^^^^^^^ +~~~~~~~~ pytest has never been associated with a security vulnerability, but in any case, to report a security vulnerability please use the `Tidelift security contact `_. diff --git a/doc/en/reference/index.rst b/doc/en/reference/index.rst index fbff0978e88..3b2b7b37266 100644 --- a/doc/en/reference/index.rst +++ b/doc/en/reference/index.rst @@ -1,5 +1,7 @@ :orphan: +.. _reference: + Reference guides ================ diff --git a/doc/en/reference/reference.rst b/doc/en/reference/reference.rst index 4c5e668d26e..d9968b8ab51 100644 --- a/doc/en/reference/reference.rst +++ b/doc/en/reference/reference.rst @@ -1,4 +1,4 @@ -.. _`reference`: +.. _`api-reference`: API Reference ============= From e515264eb1ed6505a6b130b278e7496c7bfda152 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Mon, 15 Mar 2021 16:01:58 +0200 Subject: [PATCH 195/630] Remove yet more unnecessary py.path uses --- src/_pytest/fixtures.py | 4 ++-- src/_pytest/nodes.py | 17 ++++++++--------- src/_pytest/python.py | 2 +- testing/python/collect.py | 18 ++++++++---------- testing/test_nodes.py | 7 +++---- 5 files changed, 22 insertions(+), 26 deletions(-) diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 5ff8ba3caa9..b0a895a04ef 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -670,7 +670,7 @@ def _compute_fixture_value(self, fixturedef: "FixtureDef[object]") -> None: "\n\nRequested here:\n{}:{}".format( funcitem.nodeid, fixturedef.argname, - getlocation(fixturedef.func, funcitem.config.rootdir), + getlocation(fixturedef.func, funcitem.config.rootpath), source_path_str, source_lineno, ) @@ -728,7 +728,7 @@ def _factorytraceback(self) -> List[str]: fs, lineno = getfslineno(factory) if isinstance(fs, Path): session: Session = self._pyfuncitem.session - p = bestrelpath(Path(session.fspath), fs) + p = bestrelpath(session.path, fs) else: p = fs args = _format_args(factory) diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py index 0e23c733060..99b7eb1a61c 100644 --- a/src/_pytest/nodes.py +++ b/src/_pytest/nodes.py @@ -32,6 +32,7 @@ from _pytest.mark.structures import NodeKeywords from _pytest.outcomes import fail from _pytest.pathlib import absolutepath +from _pytest.pathlib import commonpath from _pytest.store import Store if TYPE_CHECKING: @@ -517,13 +518,11 @@ def _prunetraceback(self, excinfo: ExceptionInfo[BaseException]) -> None: excinfo.traceback = ntraceback.filter() -def _check_initialpaths_for_relpath( - session: "Session", fspath: LEGACY_PATH -) -> Optional[str]: +def _check_initialpaths_for_relpath(session: "Session", path: Path) -> Optional[str]: for initial_path in session._initialpaths: - initial_path_ = legacy_path(initial_path) - if fspath.common(initial_path_) == initial_path_: - return fspath.relto(initial_path_) + if commonpath(path, initial_path) == initial_path: + rel = str(path.relative_to(initial_path)) + return "" if rel == "." else rel return None @@ -538,7 +537,7 @@ def __init__( nodeid: Optional[str] = None, ) -> None: path, fspath = _imply_path(path, fspath=fspath) - name = fspath.basename + name = path.name if parent is not None and parent.path != path: try: rel = path.relative_to(parent.path) @@ -547,7 +546,7 @@ def __init__( else: name = str(rel) name = name.replace(os.sep, SEP) - self.path = Path(fspath) + self.path = path session = session or parent.session @@ -555,7 +554,7 @@ def __init__( try: nodeid = str(self.path.relative_to(session.config.rootpath)) except ValueError: - nodeid = _check_initialpaths_for_relpath(session, fspath) + nodeid = _check_initialpaths_for_relpath(session, path) if nodeid and os.sep != SEP: nodeid = nodeid.replace(os.sep, SEP) diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 905b40d893f..04fbb45701b 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -645,7 +645,7 @@ def __init__( session=session, nodeid=nodeid, ) - self.name = os.path.basename(str(fspath.dirname)) + self.name = path.parent.name def setup(self) -> None: # Not using fixtures to call setup_module here because autouse fixtures diff --git a/testing/python/collect.py b/testing/python/collect.py index 633212d9511..0edb4452ee3 100644 --- a/testing/python/collect.py +++ b/testing/python/collect.py @@ -933,11 +933,11 @@ def test_setup_only_available_in_subdir(pytester: Pytester) -> None: """\ import pytest def pytest_runtest_setup(item): - assert item.fspath.purebasename == "test_in_sub1" + assert item.path.stem == "test_in_sub1" def pytest_runtest_call(item): - assert item.fspath.purebasename == "test_in_sub1" + assert item.path.stem == "test_in_sub1" def pytest_runtest_teardown(item): - assert item.fspath.purebasename == "test_in_sub1" + assert item.path.stem == "test_in_sub1" """ ) ) @@ -946,11 +946,11 @@ def pytest_runtest_teardown(item): """\ import pytest def pytest_runtest_setup(item): - assert item.fspath.purebasename == "test_in_sub2" + assert item.path.stem == "test_in_sub2" def pytest_runtest_call(item): - assert item.fspath.purebasename == "test_in_sub2" + assert item.path.stem == "test_in_sub2" def pytest_runtest_teardown(item): - assert item.fspath.purebasename == "test_in_sub2" + assert item.path.stem == "test_in_sub2" """ ) ) @@ -1125,8 +1125,7 @@ def pytest_pycollect_makeitem(collector, name, obj): def test_func_reportinfo(self, pytester: Pytester) -> None: item = pytester.getitem("def test_func(): pass") fspath, lineno, modpath = item.reportinfo() - with pytest.warns(DeprecationWarning): - assert fspath == item.fspath + assert str(fspath) == str(item.path) assert lineno == 0 assert modpath == "test_func" @@ -1141,8 +1140,7 @@ def test_hello(self): pass classcol = pytester.collect_by_name(modcol, "TestClass") assert isinstance(classcol, Class) fspath, lineno, msg = classcol.reportinfo() - with pytest.warns(DeprecationWarning): - assert fspath == modcol.fspath + assert str(fspath) == str(modcol.path) assert lineno == 1 assert msg == "TestClass" diff --git a/testing/test_nodes.py b/testing/test_nodes.py index dde161777cd..fbdbce3950d 100644 --- a/testing/test_nodes.py +++ b/testing/test_nodes.py @@ -5,7 +5,6 @@ import pytest from _pytest import nodes -from _pytest.compat import legacy_path from _pytest.pytester import Pytester from _pytest.warning_types import PytestWarning @@ -76,7 +75,7 @@ class FakeSession1: session = cast(pytest.Session, FakeSession1) - assert nodes._check_initialpaths_for_relpath(session, legacy_path(cwd)) == "" + assert nodes._check_initialpaths_for_relpath(session, cwd) == "" sub = cwd / "file" @@ -85,9 +84,9 @@ class FakeSession2: session = cast(pytest.Session, FakeSession2) - assert nodes._check_initialpaths_for_relpath(session, legacy_path(sub)) == "file" + assert nodes._check_initialpaths_for_relpath(session, sub) == "file" - outside = legacy_path("/outside") + outside = Path("/outside") assert nodes._check_initialpaths_for_relpath(session, outside) is None From 6a174afdfe64f89cc1e8399f7c713a0f505080b6 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Mon, 15 Mar 2021 10:15:34 +0200 Subject: [PATCH 196/630] terminal: move startdir attribute to a property that can be deprecated Same as in Config. --- src/_pytest/terminal.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/_pytest/terminal.py b/src/_pytest/terminal.py index eea9214e70f..2c95113e561 100644 --- a/src/_pytest/terminal.py +++ b/src/_pytest/terminal.py @@ -37,6 +37,8 @@ from _pytest._code.code import ExceptionRepr from _pytest._io.wcwidth import wcswidth from _pytest.compat import final +from _pytest.compat import LEGACY_PATH +from _pytest.compat import legacy_path from _pytest.config import _PluggyPlugin from _pytest.config import Config from _pytest.config import ExitCode @@ -318,7 +320,6 @@ def __init__(self, config: Config, file: Optional[TextIO] = None) -> None: self.stats: Dict[str, List[Any]] = {} self._main_color: Optional[str] = None self._known_types: Optional[List[str]] = None - self.startdir = config.invocation_dir self.startpath = config.invocation_params.dir if file is None: file = sys.stdout @@ -381,6 +382,16 @@ def showfspath(self, value: Optional[bool]) -> None: def showlongtestinfo(self) -> bool: return self.verbosity > 0 + @property + def startdir(self) -> LEGACY_PATH: + """The directory from which pytest was invoked. + + Prefer to use ``startpath`` which is a :class:`pathlib.Path`. + + :type: LEGACY_PATH + """ + return legacy_path(self.startpath) + def hasopt(self, char: str) -> bool: char = {"xfailed": "x", "skipped": "s"}.get(char, char) return char in self.reportchars From ccdadb64ea328d16860e7a68fb6768d5bfa8d5a4 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Mon, 15 Mar 2021 15:42:45 +0200 Subject: [PATCH 197/630] main: add Session.startpath, make Session.startdir a property that can be deprecated Same as in Config. --- src/_pytest/main.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/_pytest/main.py b/src/_pytest/main.py index b6de7a8ddc3..06cfb1fd54f 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -25,6 +25,7 @@ import _pytest._code from _pytest import nodes from _pytest.compat import final +from _pytest.compat import LEGACY_PATH from _pytest.compat import legacy_path from _pytest.config import Config from _pytest.config import directory_arg @@ -301,7 +302,7 @@ def wrap_session( finally: # Explicitly break reference cycle. excinfo = None # type: ignore - session.startdir.chdir() + os.chdir(session.startpath) if initstate >= 2: try: config.hook.pytest_sessionfinish( @@ -476,7 +477,6 @@ def __init__(self, config: Config) -> None: self.shouldstop: Union[bool, str] = False self.shouldfail: Union[bool, str] = False self.trace = config.trace.root.get("collection") - self.startdir = config.invocation_dir self._initialpaths: FrozenSet[Path] = frozenset() self._bestrelpathcache: Dict[Path, str] = _bestrelpath_cache(config.rootpath) @@ -497,6 +497,24 @@ def __repr__(self) -> str: self.testscollected, ) + @property + def startpath(self) -> Path: + """The path from which pytest was invoked. + + .. versionadded:: 6.3.0 + """ + return self.config.invocation_params.dir + + @property + def stardir(self) -> LEGACY_PATH: + """The path from which pytest was invoked. + + Prefer to use ``startpath`` which is a :class:`pathlib.Path`. + + :type: LEGACY_PATH + """ + return legacy_path(self.startpath) + def _node_location_to_relpath(self, node_path: Path) -> str: # bestrelpath is a quite slow function. return self._bestrelpathcache[node_path] From 202dd9f423b766b54465068acc47d39a571af2f6 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Mon, 15 Mar 2021 16:37:38 +0200 Subject: [PATCH 198/630] pytester: add & use our own copytree instead of py.path's Fixes the TODO note. --- src/_pytest/pathlib.py | 20 +++++++++++++++++++- src/_pytest/pytester.py | 6 ++---- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/_pytest/pathlib.py b/src/_pytest/pathlib.py index d3908a3fdc0..63764b341e0 100644 --- a/src/_pytest/pathlib.py +++ b/src/_pytest/pathlib.py @@ -583,7 +583,7 @@ def resolve_package_path(path: Path) -> Optional[Path]: def visit( - path: str, recurse: Callable[["os.DirEntry[str]"], bool] + path: Union[str, "os.PathLike[str]"], recurse: Callable[["os.DirEntry[str]"], bool] ) -> Iterator["os.DirEntry[str]"]: """Walk a directory recursively, in breadth-first order. @@ -657,3 +657,21 @@ def bestrelpath(directory: Path, dest: Path) -> str: # Forward from base to dest. *reldest.parts, ) + + +# Originates from py. path.local.copy(), with siginficant trims and adjustments. +# TODO(py38): Replace with shutil.copytree(..., symlinks=True, dirs_exist_ok=True) +def copytree(source: Path, target: Path) -> None: + """Recursively copy a source directory to target.""" + assert source.is_dir() + for entry in visit(source, recurse=lambda entry: not entry.is_symlink()): + x = Path(entry) + relpath = x.relative_to(source) + newx = target / relpath + newx.parent.mkdir(exist_ok=True) + if x.is_symlink(): + newx.symlink_to(os.readlink(x)) + elif x.is_file(): + shutil.copyfile(x, newx) + elif x.is_dir(): + newx.mkdir(exist_ok=True) diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index febae07857e..968a5365134 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -63,6 +63,7 @@ from _pytest.outcomes import importorskip from _pytest.outcomes import skip from _pytest.pathlib import bestrelpath +from _pytest.pathlib import copytree from _pytest.pathlib import make_numbered_dir from _pytest.reports import CollectReport from _pytest.reports import TestReport @@ -935,10 +936,7 @@ def copy_example(self, name: Optional[str] = None) -> Path: example_path = example_dir.joinpath(name) if example_path.is_dir() and not example_path.joinpath("__init__.py").is_file(): - # TODO: legacy_path.copy can copy files to existing directories, - # while with shutil.copytree the destination directory cannot exist, - # we will need to roll our own in order to drop legacy_path completely - legacy_path(example_path).copy(legacy_path(self.path)) + copytree(example_path, self.path) return self.path elif example_path.is_file(): result = self.path.joinpath(example_path.name) From 4690e4c510b170d152551ce796eae94bf81c164f Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Mon, 15 Mar 2021 17:20:55 +0200 Subject: [PATCH 199/630] reports: support any PathLike instead of only Path, py.path The goal is to avoid referring to the legacy py.path. --- src/_pytest/reports.py | 7 +++---- testing/test_reports.py | 13 ++++++++++--- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/_pytest/reports.py b/src/_pytest/reports.py index 657e0683378..b4013f6a2f6 100644 --- a/src/_pytest/reports.py +++ b/src/_pytest/reports.py @@ -1,5 +1,5 @@ +import os from io import StringIO -from pathlib import Path from pprint import pprint from typing import Any from typing import cast @@ -29,7 +29,6 @@ from _pytest._code.code import TerminalRepr from _pytest._io import TerminalWriter from _pytest.compat import final -from _pytest.compat import LEGACY_PATH from _pytest.config import Config from _pytest.nodes import Collector from _pytest.nodes import Item @@ -500,8 +499,8 @@ def serialize_exception_longrepr(rep: BaseReport) -> Dict[str, Any]: else: d["longrepr"] = report.longrepr for name in d: - if isinstance(d[name], (LEGACY_PATH, Path)): - d[name] = str(d[name]) + if isinstance(d[name], os.PathLike): + d[name] = os.fspath(d[name]) elif name == "result": d[name] = None # for now return d diff --git a/testing/test_reports.py b/testing/test_reports.py index 3da63c2c873..31b6cf1afc6 100644 --- a/testing/test_reports.py +++ b/testing/test_reports.py @@ -4,7 +4,6 @@ import pytest from _pytest._code.code import ExceptionChainRepr from _pytest._code.code import ExceptionRepr -from _pytest.compat import legacy_path from _pytest.config import Config from _pytest.pytester import Pytester from _pytest.reports import CollectReport @@ -225,18 +224,26 @@ def test_extended_report_deserialization(self, pytester: Pytester) -> None: assert newrep.longrepr == str(rep.longrepr) def test_paths_support(self, pytester: Pytester) -> None: - """Report attributes which are py.path or pathlib objects should become strings.""" + """Report attributes which are path-like should become strings.""" pytester.makepyfile( """ def test_a(): assert False """ ) + + class MyPathLike: + def __init__(self, path: str) -> None: + self.path = path + + def __fspath__(self) -> str: + return self.path + reprec = pytester.inline_run() reports = reprec.getreports("pytest_runtest_logreport") assert len(reports) == 3 test_a_call = reports[1] - test_a_call.path1 = legacy_path(pytester.path) # type: ignore[attr-defined] + test_a_call.path1 = MyPathLike(str(pytester.path)) # type: ignore[attr-defined] test_a_call.path2 = pytester.path # type: ignore[attr-defined] data = test_a_call._to_json() assert data["path1"] == str(pytester.path) From fb481c7e6f86dcae013204240bcac248fed6c1da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Gmach?= Date: Thu, 18 Mar 2021 12:34:01 +0100 Subject: [PATCH 200/630] fix typo (#8460) --- src/_pytest/tmpdir.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_pytest/tmpdir.py b/src/_pytest/tmpdir.py index 63cfa4c2365..99b54e9bf81 100644 --- a/src/_pytest/tmpdir.py +++ b/src/_pytest/tmpdir.py @@ -134,7 +134,7 @@ def getbasetemp(self) -> Path: @final @attr.s(init=False) class TempdirFactory: - """Backward comptibility wrapper that implements :class:``_pytest.compat.LEGACY_PATH`` + """Backward compatibility wrapper that implements :class:``_pytest.compat.LEGACY_PATH`` for :class:``TempPathFactory``.""" _tmppath_factory = attr.ib(type=TempPathFactory) From ff6d5ae278f374cc9343f7e589e05e162158e2cd Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Thu, 18 Mar 2021 22:13:12 +0100 Subject: [PATCH 201/630] port the rest of the scripts/docs over to the main branch --- CHANGELOG.rst | 2 +- CONTRIBUTING.rst | 16 ++++++++-------- README.rst | 2 +- RELEASING.rst | 20 ++++++++++---------- scripts/prepare-release-pr.py | 2 +- scripts/release-on-comment.py | 10 +++++----- src/_pytest/assertion/__init__.py | 2 +- 7 files changed, 27 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 3865f250c26..481f277813a 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -4,4 +4,4 @@ Changelog The pytest CHANGELOG is located `here `__. -The source document can be found at: https://github.com/pytest-dev/pytest/blob/master/doc/en/changelog.rst +The source document can be found at: https://github.com/pytest-dev/pytest/blob/main/doc/en/changelog.rst diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index ba783d5c106..5855d47914f 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -234,7 +234,7 @@ Here is a simple overview, with pytest-specific bits: $ git clone git@github.com:YOUR_GITHUB_USERNAME/pytest.git $ cd pytest - # now, create your own branch off "master": + # now, create your own branch off "main": $ git checkout -b your-bugfix-branch-name main @@ -387,15 +387,15 @@ Suppose for example that the latest release was 1.2.3, and you want to include a bug fix in 1.2.4 (check https://github.com/pytest-dev/pytest/releases for the actual latest release). The procedure for this is: -#. First, make sure the bug is fixed the ``master`` branch, with a regular pull +#. First, make sure the bug is fixed the ``main`` branch, with a regular pull request, as described above. An exception to this is if the bug fix is not - applicable to ``master`` anymore. + applicable to ``main`` anymore. -#. ``git checkout origin/1.2.x -b backport-XXXX`` # use the master PR number here +#. ``git checkout origin/1.2.x -b backport-XXXX`` # use the main PR number here #. Locate the merge commit on the PR, in the *merged* message, for example: - nicoddemus merged commit 0f8b462 into pytest-dev:master + nicoddemus merged commit 0f8b462 into pytest-dev:main #. ``git cherry-pick -x -m1 REVISION`` # use the revision you found above (``0f8b462``). @@ -408,7 +408,7 @@ actual latest release). The procedure for this is: Who does the backporting ~~~~~~~~~~~~~~~~~~~~~~~~ -As mentioned above, bugs should first be fixed on ``master`` (except in rare occasions +As mentioned above, bugs should first be fixed on ``main`` (except in rare occasions that a bug only happens in a previous release). So who should do the backport procedure described above? @@ -417,8 +417,8 @@ above? 2. However, often the merge is done by another maintainer, in which case it is nice of them to do the backport procedure if they have the time. 3. For bugs submitted by non-maintainers, it is expected that a core developer will to do - the backport, normally the one that merged the PR on ``master``. -4. If a non-maintainers notices a bug which is fixed on ``master`` but has not been backported + the backport, normally the one that merged the PR on ``main``. +4. If a non-maintainers notices a bug which is fixed on ``main`` but has not been backported (due to maintainers forgetting to apply the *needs backport* label, or just plain missing it), they are also welcome to open a PR with the backport. The procedure is simple and really helps with the maintenance of the project. diff --git a/README.rst b/README.rst index d0014e8bfff..ee11a2d600c 100644 --- a/README.rst +++ b/README.rst @@ -24,7 +24,7 @@ :target: https://github.com/pytest-dev/pytest/actions?query=workflow%3Amain .. image:: https://results.pre-commit.ci/badge/github/pytest-dev/pytest/main.svg - :target: https://results.pre-commit.ci/latest/github/pytest-dev/pytest/master + :target: https://results.pre-commit.ci/latest/github/pytest-dev/pytest/main :alt: pre-commit.ci status .. image:: https://img.shields.io/badge/code%20style-black-000000.svg diff --git a/RELEASING.rst b/RELEASING.rst index 9ec2b069c68..600c12539c2 100644 --- a/RELEASING.rst +++ b/RELEASING.rst @@ -32,10 +32,10 @@ and notify it as a comment in the issue. Minor releases ^^^^^^^^^^^^^^ -1. Create a new maintenance branch from ``master``:: +1. Create a new maintenance branch from ``main``:: git fetch --all - git branch 5.2.x upstream/master + git branch 5.2.x upstream/main git push upstream 5.2.x 2. Open a new issue and add this comment to the body:: @@ -48,10 +48,10 @@ notify it as a comment in the issue. Major and release candidates ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -1. Create a new maintenance branch from ``master``:: +1. Create a new maintenance branch from ``main``:: git fetch --all - git branch 6.0.x upstream/master + git branch 6.0.x upstream/main git push upstream 6.0.x 2. For a **major release**, open a new issue and add this comment in the body:: @@ -66,7 +66,7 @@ The automated workflow will publish a PR for a branch ``release-6.0.0`` and notify it as a comment in the issue. At this point on, this follows the same workflow as other maintenance branches: bug-fixes are merged -into ``master`` and ported back to the maintenance branch, even for release candidates. +into ``main`` and ported back to the maintenance branch, even for release candidates. **A note about release candidates** @@ -83,7 +83,7 @@ to be executed on that platform. To release a version ``MAJOR.MINOR.PATCH``, follow these steps: #. For major and minor releases, create a new branch ``MAJOR.MINOR.x`` from - ``upstream/master`` and push it to ``upstream``. + ``upstream/main`` and push it to ``upstream``. #. Create a branch ``release-MAJOR.MINOR.PATCH`` from the ``MAJOR.MINOR.x`` branch. @@ -114,18 +114,18 @@ Both automatic and manual processes described above follow the same steps from t #. Merge the PR. -#. Cherry-pick the CHANGELOG / announce files to the ``master`` branch:: +#. Cherry-pick the CHANGELOG / announce files to the ``main`` branch:: git fetch --all --prune - git checkout origin/master -b cherry-pick-release + git checkout origin/main -b cherry-pick-release git cherry-pick -x -m1 upstream/MAJOR.MINOR.x #. Open a PR for ``cherry-pick-release`` and merge it once CI passes. No need to wait for approvals if there were no conflicts on the previous step. -#. For major and minor releases, tag the release cherry-pick merge commit in master with +#. For major and minor releases, tag the release cherry-pick merge commit in main with a dev tag for the next feature release:: - git checkout master + git checkout main git pull git tag MAJOR.{MINOR+1}.0.dev0 git push git@github.com:pytest-dev/pytest.git MAJOR.{MINOR+1}.0.dev0 diff --git a/scripts/prepare-release-pr.py b/scripts/prepare-release-pr.py index 296de46ea0c..5ba174f23ff 100644 --- a/scripts/prepare-release-pr.py +++ b/scripts/prepare-release-pr.py @@ -3,7 +3,7 @@ tab of the repository. The user will need to enter the base branch to start the release from (for example -``6.1.x`` or ``master``) and if it should be a major release. +``6.1.x`` or ``main``) and if it should be a major release. The appropriate version will be obtained based on the given branch automatically. diff --git a/scripts/release-on-comment.py b/scripts/release-on-comment.py index f8af9c0fc83..3f4c1ed5707 100644 --- a/scripts/release-on-comment.py +++ b/scripts/release-on-comment.py @@ -3,7 +3,7 @@ in issues. This script is started by the `release-on-comment.yml` workflow, which always executes on -`master` and is triggered by two comment related events: +`main` and is triggered by two comment related events: * https://help.github.com/en/actions/reference/events-that-trigger-workflows#issue-comment-event-issue_comment * https://help.github.com/en/actions/reference/events-that-trigger-workflows#issues-event-issues @@ -16,8 +16,8 @@ Then the appropriate version will be obtained based on the given branch name: -* a major release from master if "major" appears in the phrase in that position -* a feature or bug fix release from master (based if there are features in the current changelog +* a major release from main if "major" appears in the phrase in that position +* a feature or bug fix release from main (based if there are features in the current changelog folder) * a bug fix from a maintenance branch @@ -230,11 +230,11 @@ def find_next_version(base_branch: str, is_major: bool) -> str: breaking = list(changelog.glob("*.breaking.rst")) is_feature_release = features or breaking - if is_feature_release and base_branch != "master": + if is_feature_release and base_branch != "main": msg = dedent( f""" Found features or breaking changes in `{base_branch}`, and feature releases can only be - created from `master`: + created from `main`: """ ) msg += "\n".join(f"* `{x.name}`" for x in sorted(features + breaking)) diff --git a/src/_pytest/assertion/__init__.py b/src/_pytest/assertion/__init__.py index a18cf198df0..fd79f34ccd7 100644 --- a/src/_pytest/assertion/__init__.py +++ b/src/_pytest/assertion/__init__.py @@ -104,7 +104,7 @@ def undo() -> None: def pytest_collection(session: "Session") -> None: # This hook is only called when test modules are collected - # so for example not in the master process of pytest-xdist + # so for example not in the managing process of pytest-xdist # (which does not collect test modules). assertstate = session.config._store.get(assertstate_key, None) if assertstate: From 30f1b81eb27f3589079c434688caa7d1e20e70f9 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Thu, 18 Mar 2021 23:08:03 +0100 Subject: [PATCH 202/630] address #8361 - introduce hook caller wrappers that enable backward compat --- src/_pytest/config/__init__.py | 4 +++- src/_pytest/config/compat.py | 41 ++++++++++++++++++++++++++++++++++ src/_pytest/main.py | 4 +++- 3 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 src/_pytest/config/compat.py diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 624fbd02f8c..bdb68b3e2d4 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -917,8 +917,10 @@ def __init__( :type: PytestPluginManager """ + from .compat import PathAwareHookProxy + self.trace = self.pluginmanager.trace.root.get("config") - self.hook = self.pluginmanager.hook + self.hook = PathAwareHookProxy(self.pluginmanager.hook) self._inicache: Dict[str, Any] = {} self._override_ini: Sequence[str] = () self._opt2dest: Dict[str, str] = {} diff --git a/src/_pytest/config/compat.py b/src/_pytest/config/compat.py new file mode 100644 index 00000000000..2adc2d9196f --- /dev/null +++ b/src/_pytest/config/compat.py @@ -0,0 +1,41 @@ +from typing import TYPE_CHECKING + +from _pytest.nodes import _imply_path + +if TYPE_CHECKING: + from ..compat import LEGACY_PATH + + +import functools + +# hookname: (Path, LEGACY_PATH) +imply_paths_hooks = { + "pytest_ignore_collect": ("fspath", "path"), + "pytest_collect_file": ("fspath", "path"), + "pytest_pycollect_makemodule": ("fspath", "path"), + "pytest_report_header": ("startpath", "startdir"), + "pytest_report_collectionfinish": ("startpath", "startdir"), +} + + +class PathAwareHookProxy: + def __init__(self, hook_caller): + self.__hook_caller = hook_caller + + def __getattr__(self, key): + if key not in imply_paths_hooks: + return getattr(self.__hook_caller, key) + else: + hook = getattr(self.__hook_caller, key) + path_var, fspath_var = imply_paths_hooks[key] + + @functools.wraps(hook) + def fixed_hook(**kw): + path_value = kw.pop(path_var, None) + fspath_value: "LEGACY_PATH" = kw.pop(fspath_var, None) + path_value, fspath_value = _imply_path(path_value, fspath_value) + kw[path_var] = path_value + kw[fspath_var] = fspath_value + return hook(**kw) + + return fixed_hook diff --git a/src/_pytest/main.py b/src/_pytest/main.py index 06cfb1fd54f..97a14ea594c 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -551,7 +551,9 @@ def gethookproxy(self, fspath: "os.PathLike[str]"): remove_mods = pm._conftest_plugins.difference(my_conftestmodules) if remove_mods: # One or more conftests are not in use at this fspath. - proxy = FSHookProxy(pm, remove_mods) + from .config.compat import PathAwareHookProxy + + proxy = PathAwareHookProxy(FSHookProxy(pm, remove_mods)) else: # All plugins are active for this fspath. proxy = self.config.hook From 945cc0b5a188d1167b4b8b0da853562ab65cfa95 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sat, 20 Mar 2021 21:52:36 +0200 Subject: [PATCH 203/630] testing: stop relying on comparing to py.path in fnmatcher tests --- testing/test_pathlib.py | 39 +++++++++++---------------------------- 1 file changed, 11 insertions(+), 28 deletions(-) diff --git a/testing/test_pathlib.py b/testing/test_pathlib.py index d71e44e36b6..69635e751fc 100644 --- a/testing/test_pathlib.py +++ b/testing/test_pathlib.py @@ -8,7 +8,6 @@ from typing import Generator import pytest -from _pytest.compat import legacy_path from _pytest.monkeypatch import MonkeyPatch from _pytest.pathlib import bestrelpath from _pytest.pathlib import commonpath @@ -26,23 +25,7 @@ class TestFNMatcherPort: - """Test that our port of py.common.FNMatcher (fnmatch_ex) produces the - same results as the original legacy_path.fnmatch method.""" - - @pytest.fixture(params=["pathlib", "py.path"]) - def match(self, request): - if request.param == "py.path": - - def match_(pattern, path): - return legacy_path(path).fnmatch(pattern) - - else: - assert request.param == "pathlib" - - def match_(pattern, path): - return fnmatch_ex(pattern, path) - - return match_ + """Test our port of py.common.FNMatcher (fnmatch_ex).""" if sys.platform == "win32": drv1 = "c:" @@ -58,19 +41,19 @@ def match_(pattern, path): ("*.py", "bar/foo.py"), ("test_*.py", "foo/test_foo.py"), ("tests/*.py", "tests/foo.py"), - (drv1 + "/*.py", drv1 + "/foo.py"), - (drv1 + "/foo/*.py", drv1 + "/foo/foo.py"), + (f"{drv1}/*.py", f"{drv1}/foo.py"), + (f"{drv1}/foo/*.py", f"{drv1}/foo/foo.py"), ("tests/**/test*.py", "tests/foo/test_foo.py"), ("tests/**/doc/test*.py", "tests/foo/bar/doc/test_foo.py"), ("tests/**/doc/**/test*.py", "tests/foo/doc/bar/test_foo.py"), ], ) - def test_matching(self, match, pattern, path): - assert match(pattern, path) + def test_matching(self, pattern: str, path: str) -> None: + assert fnmatch_ex(pattern, path) - def test_matching_abspath(self, match): + def test_matching_abspath(self) -> None: abspath = os.path.abspath(os.path.join("tests/foo.py")) - assert match("tests/foo.py", abspath) + assert fnmatch_ex("tests/foo.py", abspath) @pytest.mark.parametrize( "pattern, path", @@ -78,16 +61,16 @@ def test_matching_abspath(self, match): ("*.py", "foo.pyc"), ("*.py", "foo/foo.pyc"), ("tests/*.py", "foo/foo.py"), - (drv1 + "/*.py", drv2 + "/foo.py"), - (drv1 + "/foo/*.py", drv2 + "/foo/foo.py"), + (f"{drv1}/*.py", f"{drv2}/foo.py"), + (f"{drv1}/foo/*.py", f"{drv2}/foo/foo.py"), ("tests/**/test*.py", "tests/foo.py"), ("tests/**/test*.py", "foo/test_foo.py"), ("tests/**/doc/test*.py", "tests/foo/bar/doc/foo.py"), ("tests/**/doc/test*.py", "tests/foo/bar/test_foo.py"), ], ) - def test_not_matching(self, match, pattern, path): - assert not match(pattern, path) + def test_not_matching(self, pattern: str, path: str) -> None: + assert not fnmatch_ex(pattern, path) class TestImportPath: From a550db4b6c8c809d6cbf42c11c428da40d3f247e Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Sat, 20 Mar 2021 21:50:40 +0100 Subject: [PATCH 204/630] drop internal py.path.local objects from hook calls --- src/_pytest/main.py | 10 +++------- src/_pytest/python.py | 18 +++++------------- src/_pytest/terminal.py | 3 +-- 3 files changed, 9 insertions(+), 22 deletions(-) diff --git a/src/_pytest/main.py b/src/_pytest/main.py index 97a14ea594c..ffe8b852c43 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -563,9 +563,8 @@ def _recurse(self, direntry: "os.DirEntry[str]") -> bool: if direntry.name == "__pycache__": return False fspath = Path(direntry.path) - path = legacy_path(fspath) ihook = self.gethookproxy(fspath.parent) - if ihook.pytest_ignore_collect(fspath=fspath, path=path, config=self.config): + if ihook.pytest_ignore_collect(fspath=fspath, config=self.config): return False norecursepatterns = self.config.getini("norecursedirs") if any(fnmatch_ex(pat, fspath) for pat in norecursepatterns): @@ -575,7 +574,6 @@ def _recurse(self, direntry: "os.DirEntry[str]") -> bool: def _collectfile( self, fspath: Path, handle_dupes: bool = True ) -> Sequence[nodes.Collector]: - path = legacy_path(fspath) assert ( fspath.is_file() ), "{!r} is not a file (isdir={!r}, exists={!r}, islink={!r})".format( @@ -583,9 +581,7 @@ def _collectfile( ) ihook = self.gethookproxy(fspath) if not self.isinitpath(fspath): - if ihook.pytest_ignore_collect( - fspath=fspath, path=path, config=self.config - ): + if ihook.pytest_ignore_collect(fspath=fspath, config=self.config): return () if handle_dupes: @@ -597,7 +593,7 @@ def _collectfile( else: duplicate_paths.add(fspath) - return ihook.pytest_collect_file(fspath=fspath, path=path, parent=self) # type: ignore[no-any-return] + return ihook.pytest_collect_file(fspath=fspath, parent=self) # type: ignore[no-any-return] @overload def perform_collect( diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 04fbb45701b..652c72123b4 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -188,9 +188,7 @@ def pytest_pyfunc_call(pyfuncitem: "Function") -> Optional[object]: return True -def pytest_collect_file( - fspath: Path, path: LEGACY_PATH, parent: nodes.Collector -) -> Optional["Module"]: +def pytest_collect_file(fspath: Path, parent: nodes.Collector) -> Optional["Module"]: if fspath.suffix == ".py": if not parent.session.isinitpath(fspath): if not path_matches_patterns( @@ -198,9 +196,7 @@ def pytest_collect_file( ): return None ihook = parent.session.gethookproxy(fspath) - module: Module = ihook.pytest_pycollect_makemodule( - fspath=fspath, path=path, parent=parent - ) + module: Module = ihook.pytest_pycollect_makemodule(fspath=fspath, parent=parent) return module return None @@ -675,9 +671,8 @@ def _recurse(self, direntry: "os.DirEntry[str]") -> bool: if direntry.name == "__pycache__": return False fspath = Path(direntry.path) - path = legacy_path(fspath) ihook = self.session.gethookproxy(fspath.parent) - if ihook.pytest_ignore_collect(fspath=fspath, path=path, config=self.config): + if ihook.pytest_ignore_collect(fspath=fspath, config=self.config): return False norecursepatterns = self.config.getini("norecursedirs") if any(fnmatch_ex(pat, fspath) for pat in norecursepatterns): @@ -687,7 +682,6 @@ def _recurse(self, direntry: "os.DirEntry[str]") -> bool: def _collectfile( self, fspath: Path, handle_dupes: bool = True ) -> Sequence[nodes.Collector]: - path = legacy_path(fspath) assert ( fspath.is_file() ), "{!r} is not a file (isdir={!r}, exists={!r}, islink={!r})".format( @@ -695,9 +689,7 @@ def _collectfile( ) ihook = self.session.gethookproxy(fspath) if not self.session.isinitpath(fspath): - if ihook.pytest_ignore_collect( - fspath=fspath, path=path, config=self.config - ): + if ihook.pytest_ignore_collect(fspath=fspath, config=self.config): return () if handle_dupes: @@ -709,7 +701,7 @@ def _collectfile( else: duplicate_paths.add(fspath) - return ihook.pytest_collect_file(fspath=fspath, path=path, parent=self) # type: ignore[no-any-return] + return ihook.pytest_collect_file(fspath=fspath, parent=self) # type: ignore[no-any-return] def collect(self) -> Iterable[Union[nodes.Item, nodes.Collector]]: this_path = self.path.parent diff --git a/src/_pytest/terminal.py b/src/_pytest/terminal.py index 2c95113e561..252f898bfa6 100644 --- a/src/_pytest/terminal.py +++ b/src/_pytest/terminal.py @@ -716,7 +716,7 @@ def pytest_sessionstart(self, session: "Session") -> None: msg += " -- " + str(sys.executable) self.write_line(msg) lines = self.config.hook.pytest_report_header( - config=self.config, startpath=self.startpath, startdir=self.startdir + config=self.config, startpath=self.startpath ) self._write_report_lines_from_hooks(lines) @@ -753,7 +753,6 @@ def pytest_collection_finish(self, session: "Session") -> None: lines = self.config.hook.pytest_report_collectionfinish( config=self.config, startpath=self.startpath, - startdir=self.startdir, items=session.items, ) self._write_report_lines_from_hooks(lines) From 29940227534e6d5860990e54581afb6658d9f6d2 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Sat, 20 Mar 2021 23:01:18 +0100 Subject: [PATCH 205/630] reshape typing for hook invocation proxying --- src/_pytest/config/compat.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/_pytest/config/compat.py b/src/_pytest/config/compat.py index 2adc2d9196f..28b0b524a18 100644 --- a/src/_pytest/config/compat.py +++ b/src/_pytest/config/compat.py @@ -1,13 +1,10 @@ -from typing import TYPE_CHECKING +import functools +from pathlib import Path +from typing import Optional +from ..compat import LEGACY_PATH from _pytest.nodes import _imply_path -if TYPE_CHECKING: - from ..compat import LEGACY_PATH - - -import functools - # hookname: (Path, LEGACY_PATH) imply_paths_hooks = { "pytest_ignore_collect": ("fspath", "path"), @@ -31,8 +28,8 @@ def __getattr__(self, key): @functools.wraps(hook) def fixed_hook(**kw): - path_value = kw.pop(path_var, None) - fspath_value: "LEGACY_PATH" = kw.pop(fspath_var, None) + path_value: Optional[Path] = kw.pop(path_var, None) + fspath_value: Optional[LEGACY_PATH] = kw.pop(fspath_var, None) path_value, fspath_value = _imply_path(path_value, fspath_value) kw[path_var] = path_value kw[fspath_var] = fspath_value From 4ddf6c647c7fdc94efb55e64a01ee4e2c0b696e9 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Sat, 20 Mar 2021 23:39:38 +0100 Subject: [PATCH 206/630] test warnings and fix invocation bugs --- .pre-commit-config.yaml | 2 +- src/_pytest/config/compat.py | 14 ++++++++++++-- src/_pytest/deprecated.py | 4 ++++ testing/deprecated_test.py | 21 +++++++++++++++++++++ 4 files changed, 38 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b96fdccf10c..13220430962 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -89,7 +89,7 @@ repos: types: [python] - id: py-path-deprecated name: py.path usage is deprecated + exclude: docs|src/_pytest/deprecated.py|testing/deprecated_test.py language: pygrep entry: \bpy\.path\.local - exclude: docs types: [python] diff --git a/src/_pytest/config/compat.py b/src/_pytest/config/compat.py index 28b0b524a18..82572a97090 100644 --- a/src/_pytest/config/compat.py +++ b/src/_pytest/config/compat.py @@ -1,8 +1,9 @@ -import functools +import warnings from pathlib import Path from typing import Optional from ..compat import LEGACY_PATH +from ..deprecated import HOOK_LEGACY_PATH_ARG from _pytest.nodes import _imply_path # hookname: (Path, LEGACY_PATH) @@ -19,6 +20,9 @@ class PathAwareHookProxy: def __init__(self, hook_caller): self.__hook_caller = hook_caller + def __dir__(self): + return dir(self.__hook_caller) + def __getattr__(self, key): if key not in imply_paths_hooks: return getattr(self.__hook_caller, key) @@ -26,13 +30,19 @@ def __getattr__(self, key): hook = getattr(self.__hook_caller, key) path_var, fspath_var = imply_paths_hooks[key] - @functools.wraps(hook) def fixed_hook(**kw): path_value: Optional[Path] = kw.pop(path_var, None) fspath_value: Optional[LEGACY_PATH] = kw.pop(fspath_var, None) + if fspath_value is not None: + warnings.warn( + HOOK_LEGACY_PATH_ARG.format( + pylib_path_arg=fspath_var, pathlib_path_arg=path_var + ) + ) path_value, fspath_value = _imply_path(path_value, fspath_value) kw[path_var] = path_value kw[fspath_var] = fspath_value return hook(**kw) + fixed_hook.__name__ = key return fixed_hook diff --git a/src/_pytest/deprecated.py b/src/_pytest/deprecated.py index 596574877bf..9ac4d81b442 100644 --- a/src/_pytest/deprecated.py +++ b/src/_pytest/deprecated.py @@ -95,6 +95,10 @@ "see https://docs.pytest.org/en/latest/deprecations.html#node-fspath-in-favor-of-pathlib-and-node-path", ) +HOOK_LEGACY_PATH_ARG = UnformattedWarning( + PytestDeprecationWarning, + "{pylib_path_arg} : py.path.local is deprecated, please use {pathlib_path_arg} : pathlib.Path", +) # You want to make some `__init__` or function "private". # # def my_private_function(some, args): diff --git a/testing/deprecated_test.py b/testing/deprecated_test.py index 18300f62a1a..41db4242730 100644 --- a/testing/deprecated_test.py +++ b/testing/deprecated_test.py @@ -4,7 +4,9 @@ import pytest from _pytest import deprecated +from _pytest.compat import legacy_path from _pytest.pytester import Pytester +from pytest import PytestDeprecationWarning @pytest.mark.parametrize("attribute", pytest.collect.__all__) # type: ignore @@ -153,3 +155,22 @@ def test_raising_unittest_skiptest_during_collection_is_deprecated( "*PytestDeprecationWarning: Raising unittest.SkipTest*", ] ) + + +def test_hookproxy_warnings_for_fspath(pytestconfig, tmp_path, request): + path = legacy_path(tmp_path) + + with pytest.warns( + PytestDeprecationWarning, + match="path : py.path.local is deprecated, please use fspath : pathlib.Path", + ): + pytestconfig.hook.pytest_ignore_collect( + config=pytestconfig, path=path, fspath=tmp_path + ) + with pytest.warns( + PytestDeprecationWarning, + match="path : py.path.local is deprecated, please use fspath : pathlib.Path", + ): + request.node.ihook.pytest_ignore_collect( + config=pytestconfig, path=path, fspath=tmp_path + ) From 7ac7610089b5e0e0f9ab0c8276c53cd8e4567ed1 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Sat, 20 Mar 2021 23:44:36 +0100 Subject: [PATCH 207/630] add tresting for implication --- testing/deprecated_test.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/testing/deprecated_test.py b/testing/deprecated_test.py index 41db4242730..b4e73dd5b50 100644 --- a/testing/deprecated_test.py +++ b/testing/deprecated_test.py @@ -174,3 +174,6 @@ def test_hookproxy_warnings_for_fspath(pytestconfig, tmp_path, request): request.node.ihook.pytest_ignore_collect( config=pytestconfig, path=path, fspath=tmp_path ) + + pytestconfig.hook.pytest_ignore_collect(config=pytestconfig, fspath=tmp_path) + request.node.ihook.pytest_ignore_collect(config=pytestconfig, fspath=tmp_path) From 3add1a4d0f04e14dc3c8b3ebce53e51d27f187e5 Mon Sep 17 00:00:00 2001 From: pytest bot Date: Sun, 21 Mar 2021 00:50:28 +0000 Subject: [PATCH 208/630] [automated] Update plugin list --- doc/en/reference/plugin_list.rst | 70 +++++++++++++++++--------------- 1 file changed, 38 insertions(+), 32 deletions(-) diff --git a/doc/en/reference/plugin_list.rst b/doc/en/reference/plugin_list.rst index b433d3f9a2d..24cacbc71fc 100644 --- a/doc/en/reference/plugin_list.rst +++ b/doc/en/reference/plugin_list.rst @@ -1,18 +1,17 @@ -.. _plugin-list: - Plugins List ============ PyPI projects that match "pytest-\*" are considered plugins and are listed automatically. Packages classified as inactive are excluded. -This list contains 836 plugins. +This list contains 844 plugins. -============================================================================================================== ======================================================================================================================================================================== ============== ===================== ============================================ +============================================================================================================== ======================================================================================================================================================================== ============== ===================== ================================================ name summary last release status requires -============================================================================================================== ======================================================================================================================================================================== ============== ===================== ============================================ +============================================================================================================== ======================================================================================================================================================================== ============== ===================== ================================================ `pytest-adaptavist `_ pytest plugin for generating test execution results within Jira Test Management (tm4j) Feb 05, 2020 N/A pytest (>=3.4.1) -`pytest-adf `_ Pytest plugin for writing Azure Data Factory integration tests Jun 03, 2020 4 - Beta pytest (>=3.5.0) -`pytest-aggreport `_ pytest plugin for pytest-repeat that generate aggregate report of the same test cases with additional statistics details. Jul 19, 2019 4 - Beta pytest (>=4.3.1) +`pytest-adf `_ Pytest plugin for writing Azure Data Factory integration tests Mar 10, 2021 4 - Beta pytest (>=3.5.0) +`pytest-adf-azure-identity `_ Pytest plugin for writing Azure Data Factory integration tests Mar 06, 2021 4 - Beta pytest (>=3.5.0) +`pytest-aggreport `_ pytest plugin for pytest-repeat that generate aggregate report of the same test cases with additional statistics details. Mar 07, 2021 4 - Beta pytest (>=6.2.2) `pytest-aio `_ Pytest plugin for testing async python code Mar 02, 2021 4 - Beta pytest ; extra == 'tests' `pytest-aiofiles `_ pytest fixtures for writing aiofiles tests with pyfakefs May 14, 2017 5 - Production/Stable N/A `pytest-aiohttp `_ pytest plugin for aiohttp support Dec 05, 2017 N/A pytest @@ -34,6 +33,7 @@ name `pytest-antilru `_ Bust functools.lru_cache when running pytest to avoid test pollution Apr 11, 2019 5 - Production/Stable pytest `pytest-anything `_ Pytest fixtures to assert anything and something Feb 18, 2021 N/A N/A `pytest-aoc `_ Downloads puzzle inputs for Advent of Code and synthesizes PyTest fixtures Dec 01, 2020 N/A pytest ; extra == 'dev' +`pytest-api `_ PyTest-API Python Web Framework built for testing purposes. Mar 20, 2021 N/A N/A `pytest-apistellar `_ apistellar plugin for pytest. Jun 18, 2019 N/A N/A `pytest-appengine `_ AppEngine integration that works well with pytest-django Feb 27, 2017 N/A N/A `pytest-appium `_ Pytest plugin for appium Dec 05, 2019 N/A N/A @@ -110,7 +110,7 @@ name `pytest-check `_ A pytest plugin that allows multiple failures per test. Dec 27, 2020 5 - Production/Stable N/A `pytest-checkdocs `_ check the README when running tests Feb 27, 2021 5 - Production/Stable pytest (>=4.6) ; extra == 'testing' `pytest-checkipdb `_ plugin to check if there are ipdb debugs left Jul 22, 2020 5 - Production/Stable pytest (>=2.9.2) -`pytest-check-links `_ Check links in files Jul 29, 2020 N/A N/A +`pytest-check-links `_ Check links in files Jul 29, 2020 N/A pytest (>=4.6) `pytest-check-mk `_ pytest plugin to test Check_MK checks Nov 19, 2015 4 - Beta pytest `pytest-circleci `_ py.test plugin for CircleCI May 03, 2019 N/A N/A `pytest-circleci-parallelized `_ Parallelize pytest across CircleCI workers. Mar 26, 2019 N/A N/A @@ -154,7 +154,7 @@ name `pytest-curl-report `_ pytest plugin to generate curl command line report Dec 11, 2016 4 - Beta N/A `pytest-custom-concurrency `_ Custom grouping concurrence for pytest Feb 08, 2021 N/A N/A `pytest-custom-exit-code `_ Exit pytest test session with custom exit code in different scenarios Aug 07, 2019 4 - Beta pytest (>=4.0.2) -`pytest-custom-nodeid `_ Custom grouping for pytest-xdist, rename test cases name and test cases nodeid, support allure report Mar 02, 2021 N/A N/A +`pytest-custom-nodeid `_ Custom grouping for pytest-xdist, rename test cases name and test cases nodeid, support allure report Mar 07, 2021 N/A N/A `pytest-custom-report `_ Configure the symbols displayed for test outcomes Jan 30, 2019 N/A pytest `pytest-custom-scheduling `_ Custom grouping for pytest-xdist, rename test cases name and test cases nodeid, support allure report Mar 01, 2021 N/A N/A `pytest-cython `_ A plugin for testing Cython extension modules Jan 26, 2021 4 - Beta pytest (>=2.7.3) @@ -189,7 +189,7 @@ name `pytest-diffeo `_ Common py.test support for Diffeo packages Apr 08, 2016 3 - Alpha N/A `pytest-disable `_ pytest plugin to disable a test and skip it from testrun Sep 10, 2015 4 - Beta N/A `pytest-disable-plugin `_ Disable plugins per test Feb 28, 2019 4 - Beta pytest (>=3.5.0) -`pytest-discord `_ A pytest plugin to notify test results to a Discord channel. Feb 14, 2021 3 - Alpha pytest (!=6.0.0,<7,>=3.3.2) +`pytest-discord `_ A pytest plugin to notify test results to a Discord channel. Mar 20, 2021 3 - Alpha pytest (!=6.0.0,<7,>=3.3.2) `pytest-django `_ A Django plugin for pytest. Oct 22, 2020 5 - Production/Stable pytest (>=5.4.0) `pytest-django-ahead `_ A Django plugin for pytest. Oct 27, 2016 5 - Production/Stable pytest (>=2.9) `pytest-djangoapp `_ Nice pytest plugin to help you with Django pluggable application testing. Sep 21, 2020 4 - Beta N/A @@ -214,8 +214,9 @@ name `pytest-docker-butla `_ Jun 16, 2019 3 - Alpha N/A `pytest-dockerc `_ Run, manage and stop Docker Compose project from Docker API Oct 09, 2020 5 - Production/Stable pytest (>=3.0) `pytest-docker-compose `_ Manages Docker containers during your integration tests Jan 26, 2021 5 - Production/Stable pytest (>=3.3) -`pytest-docker-db `_ A plugin to use docker databases for pytests Apr 19, 2020 5 - Production/Stable pytest (>=3.1.1) +`pytest-docker-db `_ A plugin to use docker databases for pytests Mar 20, 2021 5 - Production/Stable pytest (>=3.1.1) `pytest-docker-fixtures `_ pytest docker fixtures Sep 30, 2020 3 - Alpha N/A +`pytest-docker-git-fixtures `_ Pytest fixtures for testing with git scm. Mar 11, 2021 4 - Beta pytest `pytest-docker-pexpect `_ pytest plugin for writing functional tests with pexpect and docker Jan 14, 2019 N/A pytest `pytest-docker-postgresql `_ A simple plugin to use with pytest Sep 24, 2019 4 - Beta pytest (>=3.5.0) `pytest-docker-py `_ Easy to use, simple to extend, pytest plugin that minimally leverages docker-py. Nov 27, 2018 N/A pytest (==4.0.0) @@ -285,6 +286,7 @@ name `pytest-fantasy `_ Pytest plugin for Flask Fantasy Framework Mar 14, 2019 N/A N/A `pytest-fastapi `_ Dec 27, 2020 N/A N/A `pytest-fastest `_ Use SCM and coverage to run only needed tests Mar 05, 2020 N/A N/A +`pytest-fast-first `_ Pytest plugin that runs fast tests first Mar 18, 2021 3 - Alpha pytest `pytest-faulthandler `_ py.test plugin that activates the fault handler module for tests (dummy package) Jul 04, 2019 6 - Mature pytest (>=5.0) `pytest-fauxfactory `_ Integration of fauxfactory into pytest. Dec 06, 2017 5 - Production/Stable pytest (>=3.2) `pytest-figleaf `_ py.test figleaf coverage plugin Jan 18, 2010 5 - Production/Stable N/A @@ -300,7 +302,7 @@ name `pytest-fixtures `_ Common fixtures for pytest May 01, 2019 5 - Production/Stable N/A `pytest-fixture-tools `_ Plugin for pytest which provides tools for fixtures Aug 18, 2020 6 - Mature pytest `pytest-flake8 `_ pytest plugin to check FLAKE8 requirements Dec 16, 2020 4 - Beta pytest (>=3.5) -`pytest-flake8dir `_ A pytest fixture for testing flake8 plugins. Dec 13, 2020 5 - Production/Stable pytest +`pytest-flake8dir `_ A pytest fixture for testing flake8 plugins. Mar 18, 2021 5 - Production/Stable pytest `pytest-flakefinder `_ Runs tests multiple times to expose flakiness. Jul 28, 2020 4 - Beta pytest (>=2.7.1) `pytest-flakes `_ pytest plugin to check source code with pyflakes Nov 28, 2020 5 - Production/Stable N/A `pytest-flaptastic `_ Flaptastic py.test plugin Mar 17, 2019 N/A N/A @@ -322,10 +324,10 @@ name `pytest-gevent `_ Ensure that gevent is properly patched when invoking pytest Feb 25, 2020 N/A pytest `pytest-gherkin `_ A flexible framework for executing BDD gherkin tests Jul 27, 2019 3 - Alpha pytest (>=5.0.0) `pytest-ghostinspector `_ For finding/executing Ghost Inspector tests May 17, 2016 3 - Alpha N/A -`pytest-girder `_ A set of pytest fixtures for testing Girder applications. Feb 25, 2021 N/A N/A +`pytest-girder `_ A set of pytest fixtures for testing Girder applications. Mar 19, 2021 N/A N/A `pytest-git `_ Git repository fixture for py.test May 28, 2019 5 - Production/Stable pytest `pytest-gitcov `_ Pytest plugin for reporting on coverage of the last git commit. Jan 11, 2020 2 - Pre-Alpha N/A -`pytest-git-fixtures `_ Pytest fixtures for testing with git. Mar 04, 2021 4 - Beta pytest +`pytest-git-fixtures `_ Pytest fixtures for testing with git. Mar 11, 2021 4 - Beta pytest `pytest-github `_ Plugin for py.test that associates tests with github issues using a marker. Mar 07, 2019 5 - Production/Stable N/A `pytest-github-actions-annotate-failures `_ pytest plugin to annotate failed tests with a workflow command for GitHub Actions Oct 13, 2020 N/A pytest (>=4.0.0) `pytest-gitignore `_ py.test plugin to ignore the same files as git Jul 17, 2015 4 - Beta N/A @@ -359,11 +361,11 @@ name `pytest-httpbin `_ Easily test your HTTP library against a local copy of httpbin Feb 11, 2019 5 - Production/Stable N/A `pytest-http-mocker `_ Pytest plugin for http mocking (via https://github.com/vilus/mocker) Oct 20, 2019 N/A N/A `pytest-httpretty `_ A thin wrapper of HTTPretty for pytest Feb 16, 2014 3 - Alpha N/A -`pytest-httpserver `_ pytest-httpserver is a httpserver for pytest Feb 14, 2021 3 - Alpha pytest ; extra == 'dev' +`pytest-httpserver `_ pytest-httpserver is a httpserver for pytest Mar 16, 2021 3 - Alpha pytest ; extra == 'dev' `pytest-httpx `_ Send responses to httpx. Mar 01, 2021 5 - Production/Stable pytest (==6.*) `pytest-hue `_ Visualise PyTest status via your Phillips Hue lights May 09, 2019 N/A N/A `pytest-hypo-25 `_ help hypo module for pytest Jan 12, 2020 3 - Alpha N/A -`pytest-ibutsu `_ A plugin to sent pytest results to an Ibutsu server Feb 24, 2021 4 - Beta pytest +`pytest-ibutsu `_ A plugin to sent pytest results to an Ibutsu server Mar 09, 2021 4 - Beta pytest `pytest-icdiff `_ use icdiff for better error messages in pytest assertions Apr 08, 2020 4 - Beta N/A `pytest-idapro `_ A pytest plugin for idapython. Allows a pytest setup to run tests outside and inside IDA in an automated manner by runnig pytest inside IDA and by mocking idapython api Nov 03, 2018 N/A N/A `pytest-ignore-flaky `_ ignore failures from flaky tests (pytest plugin) Jan 14, 2019 5 - Production/Stable pytest (>=3.7) @@ -374,7 +376,7 @@ name `pytest-informative-node `_ display more node ininformation. Apr 25, 2019 4 - Beta N/A `pytest-infrastructure `_ pytest stack validation prior to testing executing Apr 12, 2020 4 - Beta N/A `pytest-inmanta `_ A py.test plugin providing fixtures to simplify inmanta modules testing. Oct 12, 2020 5 - Production/Stable N/A -`pytest-inmanta-extensions `_ Inmanta tests package Jan 07, 2021 5 - Production/Stable N/A +`pytest-inmanta-extensions `_ Inmanta tests package Mar 17, 2021 5 - Production/Stable N/A `pytest-Inomaly `_ A simple image diff plugin for pytest Feb 13, 2018 4 - Beta N/A `pytest-insta `_ A practical snapshot testing plugin for pytest Mar 03, 2021 N/A pytest (>=6.0.2,<7.0.0) `pytest-instafail `_ pytest plugin to show failures instantly Jun 14, 2020 4 - Beta pytest (>=2.9) @@ -399,7 +401,7 @@ name `pytest-json-report `_ A pytest plugin to report test results as JSON files Oct 23, 2020 4 - Beta pytest (>=4.2.0) `pytest-kafka `_ Zookeeper, Kafka server, and Kafka consumer fixtures for Pytest Nov 01, 2019 N/A pytest `pytest-kind `_ Kubernetes test support with KIND for pytest Jan 24, 2021 5 - Production/Stable N/A -`pytest-kivy `_ Kivy GUI tests fixtures using pytest Dec 21, 2020 4 - Beta pytest (>=3.6) +`pytest-kivy `_ Kivy GUI tests fixtures using pytest Mar 20, 2021 4 - Beta pytest (>=3.6) `pytest-knows `_ A pytest plugin that can automaticly skip test case based on dependence info calculated by trace Aug 22, 2014 N/A N/A `pytest-konira `_ Run Konira DSL tests with py.test Oct 09, 2011 N/A N/A `pytest-krtech-common `_ pytest krtech common library Nov 28, 2016 4 - Beta N/A @@ -423,7 +425,7 @@ name `pytest-localftpserver `_ A PyTest plugin which provides an FTP fixture for your tests Jan 27, 2021 5 - Production/Stable pytest `pytest-localserver `_ py.test plugin to test server connections locally. Nov 14, 2018 4 - Beta N/A `pytest-localstack `_ Pytest plugin for AWS integration tests Aug 22, 2019 4 - Beta pytest (>=3.3.0) -`pytest-lockable `_ lockable resource plugin for pytest Oct 05, 2020 3 - Alpha pytest +`pytest-lockable `_ lockable resource plugin for pytest Mar 16, 2021 3 - Alpha pytest `pytest-locker `_ Used to lock object during testing. Essentially changing assertions from being hard coded to asserting that nothing changed Feb 25, 2021 N/A pytest (>=5.4) `pytest-logbook `_ py.test plugin to capture logbook log messages Nov 23, 2015 5 - Production/Stable pytest (>=2.8) `pytest-logfest `_ Pytest plugin providing three logger fixtures with basic or full writing to log files Jul 21, 2019 4 - Beta pytest (>=3.5.0) @@ -473,9 +475,9 @@ name `pytest-monkeytype `_ pytest-monkeytype: Generate Monkeytype annotations from your pytest tests. Jul 29, 2020 4 - Beta N/A `pytest-moto `_ Fixtures for integration tests of AWS services,uses moto mocking library. Aug 28, 2015 1 - Planning N/A `pytest-mp `_ A test batcher for multiprocessed Pytest runs May 23, 2018 4 - Beta pytest -`pytest-mpi `_ pytest plugin to collect information from tests Jun 20, 2020 3 - Alpha N/A +`pytest-mpi `_ pytest plugin to collect information from tests Mar 14, 2021 3 - Alpha pytest `pytest-mpl `_ pytest plugin to help with testing figures output from Matplotlib Nov 05, 2020 4 - Beta pytest -`pytest-mproc `_ low-startup-overhead, scalable, distributed-testing pytest plugin Aug 08, 2020 4 - Beta N/A +`pytest-mproc `_ low-startup-overhead, scalable, distributed-testing pytest plugin Mar 07, 2021 4 - Beta pytest `pytest-multihost `_ Utility for writing multi-host tests for pytest Apr 07, 2020 4 - Beta N/A `pytest-multilog `_ Multi-process logs handling and other helpers for pytest Nov 15, 2020 N/A N/A `pytest-mutagen `_ Add the mutation testing feature to pytest Jul 24, 2020 N/A pytest (>=5.4) @@ -511,11 +513,11 @@ name `pytest-oot `_ Run object-oriented tests in a simple format Sep 18, 2016 4 - Beta N/A `pytest-openfiles `_ Pytest plugin for detecting inadvertent open file handles Apr 16, 2020 3 - Alpha pytest (>=4.6) `pytest-opentmi `_ pytest plugin for publish results to opentmi Feb 26, 2021 5 - Production/Stable pytest (>=5.0) -`pytest-operator `_ Fixtures for Operators Feb 20, 2021 N/A N/A +`pytest-operator `_ Fixtures for Operators Mar 16, 2021 N/A N/A `pytest-optional `_ include/exclude values of fixtures in pytest Oct 07, 2015 N/A N/A `pytest-optional-tests `_ Easy declaration of optional tests (i.e., that are not run by default) Jul 09, 2019 4 - Beta pytest (>=4.5.0) `pytest-orchestration `_ A pytest plugin for orchestrating tests Jul 18, 2019 N/A N/A -`pytest-order `_ pytest plugin to run your tests in a specific order Feb 16, 2021 4 - Beta pytest (>=3.7) +`pytest-order `_ pytest plugin to run your tests in a specific order Mar 18, 2021 4 - Beta pytest (>=3.7) `pytest-ordering `_ pytest plugin to run your tests in a specific order Nov 14, 2018 4 - Beta pytest `pytest-osxnotify `_ OS X notifications for py.test results. May 15, 2015 N/A N/A `pytest-pact `_ A simple plugin to use with pytest Jan 07, 2019 4 - Beta N/A @@ -535,6 +537,7 @@ name `pytest-pep8 `_ pytest plugin to check PEP8 requirements Apr 27, 2014 N/A N/A `pytest-percent `_ Change the exit code of pytest test sessions when a required percent of tests pass. May 21, 2020 N/A pytest (>=5.2.0) `pytest-performance `_ A simple plugin to ensure the execution of critical sections of code has not been impacted Sep 11, 2020 5 - Production/Stable pytest (>=3.7.0) +`pytest-persistence `_ Pytest tool for persistent objects Mar 09, 2021 N/A N/A `pytest-pgsql `_ Pytest plugins and helpers for tests using a Postgres database. May 13, 2020 5 - Production/Stable pytest (>=3.0.0) `pytest-picked `_ Run the tests related to the changed files Dec 23, 2020 N/A pytest (>=3.5.0) `pytest-pigeonhole `_ Jun 25, 2018 5 - Production/Stable pytest (>=3.4) @@ -648,24 +651,24 @@ name `pytest-rotest `_ Pytest integration with rotest Sep 08, 2019 N/A pytest (>=3.5.0) `pytest-rpc `_ Extend py.test for RPC OpenStack testing. Feb 22, 2019 4 - Beta pytest (~=3.6) `pytest-rt `_ pytest data collector plugin for Testgr Mar 03, 2021 N/A N/A -`pytest-rts `_ Coverage-based regression test selection (RTS) plugin for pytest Mar 03, 2021 N/A pytest +`pytest-rts `_ Coverage-based regression test selection (RTS) plugin for pytest Mar 18, 2021 N/A pytest `pytest-runfailed `_ implement a --failed option for pytest Mar 24, 2016 N/A N/A `pytest-runner `_ Invoke py.test as distutils command with dependency resolution Feb 12, 2021 5 - Production/Stable pytest (!=3.7.3,>=3.5) ; extra == 'testing' `pytest-salt `_ Pytest Salt Plugin Jan 27, 2020 4 - Beta N/A `pytest-salt-containers `_ A Pytest plugin that builds and creates docker containers Nov 09, 2016 4 - Beta N/A -`pytest-salt-factories `_ Pytest Salt Plugin Mar 05, 2021 4 - Beta pytest (>=6.1.1) +`pytest-salt-factories `_ Pytest Salt Plugin Mar 17, 2021 4 - Beta pytest (>=6.1.1) `pytest-salt-from-filenames `_ Simple PyTest Plugin For Salt's Test Suite Specifically Jan 29, 2019 4 - Beta pytest (>=4.1) `pytest-salt-runtests-bridge `_ Simple PyTest Plugin For Salt's Test Suite Specifically Dec 05, 2019 4 - Beta pytest (>=4.1) `pytest-sanic `_ a pytest plugin for Sanic Feb 27, 2021 N/A pytest (>=5.2) `pytest-sanity `_ Dec 07, 2020 N/A N/A `pytest-sa-pg `_ May 14, 2019 N/A N/A -`pytest-sbase `_ A complete web automation framework for end-to-end testing. Mar 06, 2021 5 - Production/Stable N/A +`pytest-sbase `_ A complete web automation framework for end-to-end testing. Mar 19, 2021 5 - Production/Stable N/A `pytest-scenario `_ pytest plugin for test scenarios Feb 06, 2017 3 - Alpha N/A `pytest-schema `_ 👍 Validate return values against a schema-like object in testing Aug 31, 2020 5 - Production/Stable pytest (>=3.5.0) `pytest-securestore `_ An encrypted password store for use within pytest cases Jun 19, 2019 4 - Beta N/A `pytest-select `_ A pytest plugin which allows to (de-)select tests from a file. Jan 18, 2019 3 - Alpha pytest (>=3.0) `pytest-selenium `_ pytest plugin for Selenium Sep 19, 2020 5 - Production/Stable pytest (>=5.0.0) -`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Mar 06, 2021 5 - Production/Stable N/A +`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Mar 19, 2021 5 - Production/Stable N/A `pytest-selenium-enhancer `_ pytest plugin for Selenium Nov 26, 2020 5 - Production/Stable N/A `pytest-selenium-pdiff `_ A pytest package implementing perceptualdiff for Selenium tests. Apr 06, 2017 2 - Pre-Alpha N/A `pytest-send-email `_ Send pytest execution result email Dec 04, 2019 N/A N/A @@ -730,7 +733,7 @@ name `pytest-stub `_ Stub packages, modules and attributes. Apr 28, 2020 5 - Production/Stable N/A `pytest-stubprocess `_ Provide stub implementations for subprocesses in Python tests Sep 17, 2018 3 - Alpha pytest (>=3.5.0) `pytest-study `_ A pytest plugin to organize long run tests (named studies) without interfering the regular tests Sep 26, 2017 3 - Alpha pytest (>=2.0) -`pytest-subprocess `_ A plugin to fake subprocess for pytest Aug 22, 2020 5 - Production/Stable pytest (>=4.0.0) +`pytest-subprocess `_ A plugin to fake subprocess for pytest Mar 20, 2021 5 - Production/Stable pytest (>=4.0.0) `pytest-subtesthack `_ A hack to explicitly set up and tear down fixtures. Mar 02, 2021 N/A N/A `pytest-subtests `_ unittest subTest() support and subtests fixture Dec 13, 2020 4 - Beta pytest (>=5.3.0) `pytest-subunit `_ pytest-subunit is a plugin for py.test which outputs testsresult in subunit format. Aug 29, 2017 N/A N/A @@ -740,7 +743,9 @@ name `pytest-svn `_ SVN repository fixture for py.test May 28, 2019 5 - Production/Stable pytest `pytest-symbols `_ pytest-symbols is a pytest plugin that adds support for passing test environment symbols into pytest tests. Nov 20, 2017 3 - Alpha N/A `pytest-tap `_ Test Anything Protocol (TAP) reporting plugin for pytest Nov 07, 2020 5 - Production/Stable pytest (>=3.0) +`pytest-tape `_ easy assertion with expected results saved to yaml files Mar 17, 2021 4 - Beta N/A `pytest-target `_ Pytest plugin for remote target orchestration. Jan 21, 2021 3 - Alpha pytest (>=6.1.2,<7.0.0) +`pytest-tars `_ Pytest plugin for testing against real servers Mar 19, 2021 2 - Pre-Alpha pytest (<5.0.0,>=4.3.1) ; python_version < "3.5" `pytest-tblineinfo `_ tblineinfo is a py.test plugin that insert the node id in the final py.test report when --tb=line option is used Dec 01, 2015 3 - Alpha pytest (>=2.0) `pytest-teamcity-logblock `_ py.test plugin to introduce block structure in teamcity build log, if output is not captured May 15, 2018 4 - Beta N/A `pytest-telegram `_ Pytest to Telegram reporting plugin Dec 10, 2020 5 - Production/Stable N/A @@ -752,9 +757,9 @@ name `pytest-testdirectory `_ A py.test plugin providing temporary directories in unit tests. Nov 06, 2018 5 - Production/Stable pytest `pytest-testdox `_ A testdox format reporter for pytest Oct 13, 2020 5 - Production/Stable pytest (>=3.7.0) `pytest-test-groups `_ A Pytest plugin for running a subset of your tests by splitting them in to equally sized groups. Oct 25, 2016 5 - Production/Stable N/A -`pytest-testinfra `_ Test infrastructures Nov 12, 2020 5 - Production/Stable pytest (!=3.0.2) +`pytest-testinfra `_ Test infrastructures Mar 18, 2021 5 - Production/Stable pytest (!=3.0.2) `pytest-testlink-adaptor `_ pytest reporting plugin for testlink Dec 20, 2018 4 - Beta pytest (>=2.6) -`pytest-testmon `_ selects tests affected by changed files and methods Aug 05, 2020 4 - Beta N/A +`pytest-testmon `_ selects tests affected by changed files and methods Mar 18, 2021 4 - Beta N/A `pytest-testobject `_ Plugin to use TestObject Suites with Pytest Sep 24, 2019 4 - Beta pytest (>=3.1.1) `pytest-testrail `_ pytest plugin for creating TestRail runs and adding results Aug 27, 2020 N/A pytest (>=3.6) `pytest-testrail2 `_ A small example package Nov 17, 2020 N/A pytest (>=5) @@ -775,7 +780,7 @@ name `pytest-timer `_ A timer plugin for pytest Dec 13, 2020 N/A N/A `pytest-tipsi-django `_ Oct 14, 2020 4 - Beta pytest (>=6.0.0) `pytest-tipsi-testing `_ Better fixtures management. Various helpers Nov 04, 2020 4 - Beta pytest (>=3.3.0) -`pytest-tldr `_ A pytest plugin that limits the output to just the things you need. Aug 08, 2020 4 - Beta pytest (>=3.5.0) +`pytest-tldr `_ A pytest plugin that limits the output to just the things you need. Mar 12, 2021 4 - Beta pytest (>=3.5.0) `pytest-tm4j-reporter `_ Cloud Jira Test Management (TM4J) PyTest reporter plugin Sep 01, 2020 N/A pytest `pytest-todo `_ A small plugin for the pytest testing framework, marking TODO comments as failure May 23, 2019 4 - Beta pytest `pytest-tomato `_ Mar 01, 2019 5 - Production/Stable N/A @@ -829,6 +834,7 @@ name `pytest-xdist `_ pytest xdist plugin for distributed testing and loop-on-failing modes Feb 09, 2021 5 - Production/Stable pytest (>=6.0.0) `pytest-xdist-debug-for-graingert `_ pytest xdist plugin for distributed testing and loop-on-failing modes Jul 24, 2019 5 - Production/Stable pytest (>=4.4.0) `pytest-xdist-forked `_ forked from pytest-xdist Feb 10, 2020 5 - Production/Stable pytest (>=4.4.0) +`pytest-xfaillist `_ Maintain a xfaillist in an additional file to avoid merge-conflicts. Mar 07, 2021 N/A pytest (>=6.2.2,<7.0.0) `pytest-xfiles `_ Pytest fixtures providing data read from function, module or package related (x)files. Feb 27, 2018 N/A N/A `pytest-xlog `_ Extended logging for test and decorators May 31, 2020 4 - Beta N/A `pytest-xpara `_ An extended parametrizing plugin of pytest. Oct 30, 2017 3 - Alpha pytest @@ -846,4 +852,4 @@ name `pytest-zafira `_ A Zafira plugin for pytest Sep 18, 2019 5 - Production/Stable pytest (==4.1.1) `pytest-zap `_ OWASP ZAP plugin for py.test. May 12, 2014 4 - Beta N/A `pytest-zigzag `_ Extend py.test for RPC OpenStack testing. Feb 27, 2019 4 - Beta pytest (~=3.6) -============================================================================================================== ======================================================================================================================================================================== ============== ===================== ============================================ +============================================================================================================== ======================================================================================================================================================================== ============== ===================== ================================================ From 42ece0ca812e0948ba9cbd80ea2836622c2cbbf2 Mon Sep 17 00:00:00 2001 From: symonk Date: Sun, 21 Mar 2021 20:06:18 +0000 Subject: [PATCH 209/630] remove conftest note on pytest_cmdline_main, the hook is invoked --- src/_pytest/hookspec.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/_pytest/hookspec.py b/src/_pytest/hookspec.py index 7d5f767db7e..427d7507e2b 100644 --- a/src/_pytest/hookspec.py +++ b/src/_pytest/hookspec.py @@ -176,9 +176,6 @@ def pytest_cmdline_main(config: "Config") -> Optional[Union["ExitCode", int]]: """Called for performing the main command line action. The default implementation will invoke the configure hooks and runtest_mainloop. - .. note:: - This hook will not be called for ``conftest.py`` files, only for setuptools plugins. - Stops at first non-None result, see :ref:`firstresult`. :param _pytest.config.Config config: The pytest config object. From bc055e8e697b941270c5437c0495405de01798bf Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sun, 21 Mar 2021 22:51:12 +0100 Subject: [PATCH 210/630] Fix required_plugins with prereleases (#8469) * Fix required_plugins with prereleases Fixes #8456 * Fix existing tests * Update changelog/8456.bugfix.rst Co-authored-by: Bruno Oliveira Co-authored-by: Bruno Oliveira --- changelog/8456.bugfix.rst | 1 + src/_pytest/config/__init__.py | 8 +++++--- testing/test_config.py | 30 ++++++++++++++++++++++++------ 3 files changed, 30 insertions(+), 9 deletions(-) create mode 100644 changelog/8456.bugfix.rst diff --git a/changelog/8456.bugfix.rst b/changelog/8456.bugfix.rst new file mode 100644 index 00000000000..da9370b7b1b --- /dev/null +++ b/changelog/8456.bugfix.rst @@ -0,0 +1 @@ +The :confval:`required_plugins` config option now works correctly when pre-releases of plugins are installed, rather than falsely claiming that those plugins aren't installed at all. diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 624fbd02f8c..12e73859d29 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -1270,14 +1270,16 @@ def _validate_plugins(self) -> None: missing_plugins = [] for required_plugin in required_plugins: try: - spec = Requirement(required_plugin) + req = Requirement(required_plugin) except InvalidRequirement: missing_plugins.append(required_plugin) continue - if spec.name not in plugin_dist_info: + if req.name not in plugin_dist_info: missing_plugins.append(required_plugin) - elif Version(plugin_dist_info[spec.name]) not in spec.specifier: + elif not req.specifier.contains( + Version(plugin_dist_info[req.name]), prereleases=True + ): missing_plugins.append(required_plugin) if missing_plugins: diff --git a/testing/test_config.py b/testing/test_config.py index fbeabaff69f..61fea3643b9 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -309,13 +309,14 @@ def pytest_configure(config): result.stdout.no_fnmatch_line("*PytestConfigWarning*") @pytest.mark.parametrize( - "ini_file_text, exception_text", + "ini_file_text, plugin_version, exception_text", [ pytest.param( """ [pytest] required_plugins = a z """, + "1.5", "Missing required plugins: a, z", id="2-missing", ), @@ -324,6 +325,7 @@ def pytest_configure(config): [pytest] required_plugins = a z myplugin """, + "1.5", "Missing required plugins: a, z", id="2-missing-1-ok", ), @@ -332,6 +334,7 @@ def pytest_configure(config): [pytest] required_plugins = myplugin """, + "1.5", None, id="1-ok", ), @@ -340,6 +343,7 @@ def pytest_configure(config): [pytest] required_plugins = myplugin==1.5 """, + "1.5", None, id="1-ok-pin-exact", ), @@ -348,23 +352,35 @@ def pytest_configure(config): [pytest] required_plugins = myplugin>1.0,<2.0 """, + "1.5", None, id="1-ok-pin-loose", ), pytest.param( """ [pytest] - required_plugins = pyplugin==1.6 + required_plugins = myplugin + """, + "1.5a1", + None, + id="1-ok-prerelease", + ), + pytest.param( + """ + [pytest] + required_plugins = myplugin==1.6 """, - "Missing required plugins: pyplugin==1.6", + "1.5", + "Missing required plugins: myplugin==1.6", id="missing-version", ), pytest.param( """ [pytest] - required_plugins = pyplugin==1.6 other==1.0 + required_plugins = myplugin==1.6 other==1.0 """, - "Missing required plugins: other==1.0, pyplugin==1.6", + "1.5", + "Missing required plugins: myplugin==1.6, other==1.0", id="missing-versions", ), pytest.param( @@ -373,6 +389,7 @@ def pytest_configure(config): required_plugins = wont be triggered [pytest] """, + "1.5", None, id="invalid-header", ), @@ -383,6 +400,7 @@ def test_missing_required_plugins( pytester: Pytester, monkeypatch: MonkeyPatch, ini_file_text: str, + plugin_version: str, exception_text: str, ) -> None: """Check 'required_plugins' option with various settings. @@ -408,7 +426,7 @@ def load(self): class DummyDist: entry_points = attr.ib() files = () - version = "1.5" + version = plugin_version @property def metadata(self): From a6cf0a0a0e61b1c257a563520af37fb9483482a9 Mon Sep 17 00:00:00 2001 From: Simon K Date: Mon, 22 Mar 2021 22:06:46 +0000 Subject: [PATCH 211/630] rename master to main in PULL_REQUEST_TEMPLATE.md (#8481) --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 9408fceafe3..5e7282bfd77 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -13,7 +13,7 @@ If this change fixes an issue, please: Unless your change is trivial or a small documentation fix (e.g., a typo or reword of a small section) please: -- [ ] Create a new changelog file in the `changelog` folder, with a name like `..rst`. See [changelog/README.rst](https://github.com/pytest-dev/pytest/blob/master/changelog/README.rst) for details. +- [ ] Create a new changelog file in the `changelog` folder, with a name like `..rst`. See [changelog/README.rst](https://github.com/pytest-dev/pytest/blob/main/changelog/README.rst) for details. Write sentences in the **past or present tense**, examples: From a7d528e0581a7d701df604f4f4d0a8ea17327bd7 Mon Sep 17 00:00:00 2001 From: Daniele Procida Date: Sun, 21 Mar 2021 23:04:12 +0000 Subject: [PATCH 212/630] Restructured 'How to invoke pytest' guide --- doc/en/explanation/pythonpath.rst | 2 +- doc/en/getting-started.rst | 2 +- doc/en/how-to/usage.rst | 289 ++++++++++++++++-------------- 3 files changed, 157 insertions(+), 136 deletions(-) diff --git a/doc/en/explanation/pythonpath.rst b/doc/en/explanation/pythonpath.rst index b8f4de9d95b..6aac3bc86f2 100644 --- a/doc/en/explanation/pythonpath.rst +++ b/doc/en/explanation/pythonpath.rst @@ -133,4 +133,4 @@ Running pytest with ``pytest [...]`` instead of ``python -m pytest [...]`` yield equivalent behaviour, except that the latter will add the current directory to ``sys.path``, which is standard ``python`` behavior. -See also :ref:`cmdline`. +See also :ref:`invoke-python`. diff --git a/doc/en/getting-started.rst b/doc/en/getting-started.rst index a6a45792773..f8fd994b596 100644 --- a/doc/en/getting-started.rst +++ b/doc/en/getting-started.rst @@ -256,7 +256,7 @@ Continue reading Check out additional pytest resources to help you customize tests for your unique workflow: -* ":ref:`cmdline`" for command line invocation examples +* ":ref:`usage`" for command line invocation examples * ":ref:`existingtestsuite`" for working with pre-existing tests * ":ref:`mark`" for information on the ``pytest.mark`` mechanism * ":ref:`fixtures`" for providing a functional baseline to your tests diff --git a/doc/en/how-to/usage.rst b/doc/en/how-to/usage.rst index 32af9fbeba3..cba2e01989b 100644 --- a/doc/en/how-to/usage.rst +++ b/doc/en/how-to/usage.rst @@ -4,77 +4,19 @@ How to invoke pytest ========================================== +.. seealso:: :ref:`Complete pytest command-line flag reference ` -.. _cmdline: +In general, pytest is invoked with the command ``pytest``. This will execute all tests in all files whose names follow +the form ``test_*.py`` or ``\*_test.py`` in the current directory and its subdirectories. More generally, pytest +follows :ref:`standard test discovery rules `. -Calling pytest through ``python -m pytest`` ------------------------------------------------------ - - - -You can invoke testing through the Python interpreter from the command line: - -.. code-block:: text - - python -m pytest [...] - -This is almost equivalent to invoking the command line script ``pytest [...]`` -directly, except that calling via ``python`` will also add the current directory to ``sys.path``. - -Possible exit codes --------------------------------------------------------------- - -Running ``pytest`` can result in six different exit codes: - -:Exit code 0: All tests were collected and passed successfully -:Exit code 1: Tests were collected and run but some of the tests failed -:Exit code 2: Test execution was interrupted by the user -:Exit code 3: Internal error happened while executing tests -:Exit code 4: pytest command line usage error -:Exit code 5: No tests were collected - -They are represented by the :class:`pytest.ExitCode` enum. The exit codes being a part of the public API can be imported and accessed directly using: - -.. code-block:: python - - from pytest import ExitCode +.. seealso:: :ref:`invoke-other` -.. note:: - - If you would like to customize the exit code in some scenarios, specially when - no tests are collected, consider using the - `pytest-custom_exit_code `__ - plugin. - - -Getting help on version, option names, environment variables --------------------------------------------------------------- - -.. code-block:: bash - - pytest --version # shows where pytest was imported from - pytest --fixtures # show available builtin function arguments - pytest -h | --help # show help on command line and config file options - - -The full command-line flags can be found in the :ref:`reference `. - -.. _maxfail: - -Stopping after the first (or N) failures ---------------------------------------------------- - -To stop the testing process after the first (N) failures: - -.. code-block:: bash - - pytest -x # stop after first failure - pytest --maxfail=2 # stop after two failures .. _select-tests: -Specifying tests / selecting tests ---------------------------------------------------- +Specifying which tests to run +------------------------------ Pytest supports several ways to run and select tests from the command-line. @@ -139,8 +81,61 @@ For more information see :ref:`marks `. This will import ``pkg.testing`` and use its filesystem location to find and run tests from. +Possible exit codes +-------------------------------------------------------------- + +Running ``pytest`` can result in six different exit codes: + +:Exit code 0: All tests were collected and passed successfully +:Exit code 1: Tests were collected and run but some of the tests failed +:Exit code 2: Test execution was interrupted by the user +:Exit code 3: Internal error happened while executing tests +:Exit code 4: pytest command line usage error +:Exit code 5: No tests were collected + +They are represented by the :class:`pytest.ExitCode` enum. The exit codes being a part of the public API can be imported and accessed directly using: + +.. code-block:: python + + from pytest import ExitCode + +.. note:: + + If you would like to customize the exit code in some scenarios, specially when + no tests are collected, consider using the + `pytest-custom_exit_code `__ + plugin. + + +Getting help on version, option names, environment variables +-------------------------------------------------------------- + +.. code-block:: bash + + pytest --version # shows where pytest was imported from + pytest --fixtures # show available builtin function arguments + pytest -h | --help # show help on command line and config file options + + + +.. _maxfail: + +Stopping after the first (or N) failures +--------------------------------------------------- + +To stop the testing process after the first (N) failures: + +.. code-block:: bash + + pytest -x # stop after first failure + pytest --maxfail=2 # stop after two failures + + +Managing pytest's output +-------------------------- + Modifying Python traceback printing ----------------------------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Examples for modifying traceback printing: @@ -168,8 +163,8 @@ option you make sure a trace is shown. .. _`pytest.detailed_failed_tests_usage`: -Detailed summary report ------------------------ +Producing a detailed summary report +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The ``-r`` flag can be used to display a "short test summary info" at the end of the test session, making it easy in large test suites to get a clear picture of all failures, skips, xfails, etc. @@ -344,10 +339,66 @@ captured output: PASSED test_example.py::test_ok == 1 failed, 1 passed, 1 skipped, 1 xfailed, 1 xpassed, 1 error in 0.12s === + +Creating resultlog format files +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To create plain-text machine-readable result files you can issue: + +.. code-block:: bash + + pytest --resultlog=path + +and look at the content at the ``path`` location. Such files are used e.g. +by the `PyPy-test`_ web page to show test results over several revisions. + +.. warning:: + + This option is rarely used and is scheduled for removal in pytest 6.0. + + If you use this option, consider using the new `pytest-reportlog `__ plugin instead. + + See `the deprecation docs `__ + for more information. + + +.. _`PyPy-test`: http://buildbot.pypy.org/summary + + +Sending test report to online pastebin service +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +**Creating a URL for each test failure**: + +.. code-block:: bash + + pytest --pastebin=failed + +This will submit test run information to a remote Paste service and +provide a URL for each failure. You may select tests as usual or add +for example ``-x`` if you only want to send one particular failure. + +**Creating a URL for a whole test session log**: + +.. code-block:: bash + + pytest --pastebin=all + +Currently only pasting to the http://bpaste.net service is implemented. + +.. versionchanged:: 5.2 + +If creating the URL fails for any reason, a warning is generated instead of failing the +entire test suite. + + .. _pdb-option: +Using PDB_ (Python Debugger) with pytest +---------------------------------------------------------- + Dropping to PDB_ (Python Debugger) on failures ------------------------------------------------ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. _PDB: http://docs.python.org/library/pdb.html @@ -379,11 +430,11 @@ for example:: >>> sys.last_value AssertionError('assert result == "ok"',) -.. _trace-option: -Dropping to PDB_ (Python Debugger) at the start of a test ----------------------------------------------------------- +.. _trace-option: +Dropping to PDB_ at the start of a test +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``pytest`` allows one to drop into the PDB_ prompt immediately at the start of each test via a command line option: @@ -396,7 +447,7 @@ This will invoke the Python debugger at the start of every test. .. _breakpoints: Setting breakpoints -------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. versionadded: 2.4.0 @@ -413,7 +464,7 @@ in your code and pytest automatically disables its output capture for that test: .. _`breakpoint-builtin`: Using the builtin breakpoint function -------------------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Python 3.7 introduces a builtin ``breakpoint()`` function. Pytest supports the use of ``breakpoint()`` with the following behaviours: @@ -423,6 +474,7 @@ Pytest supports the use of ``breakpoint()`` with the following behaviours: - With ``--pdb`` passed to pytest, the custom internal Pdb trace UI is used with both ``breakpoint()`` and failed tests/unhandled exceptions. - ``--pdbcls`` can be used to specify a custom debugger class. + .. _durations: Profiling test execution duration @@ -540,7 +592,7 @@ instead, configure the ``junit_duration_report`` option like this: .. _record_property example: record_property -^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~ If you want to log additional information for a test, you can use the ``record_property`` fixture: @@ -602,10 +654,9 @@ Will result in: Please note that using this feature will break schema verifications for the latest JUnitXML schema. This might be a problem when used with some CI servers. -record_xml_attribute -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - +record_xml_attribute +~~~~~~~~~~~~~~~~~~~~~~~ To add an additional xml attribute to a testcase element, you can use ``record_xml_attribute`` fixture. This can also be used to override existing values: @@ -714,60 +765,11 @@ The generated XML is compatible with the latest ``xunit`` standard, contrary to and `record_xml_attribute`_. -Creating resultlog format files ----------------------------------------------------- - - -To create plain-text machine-readable result files you can issue: - -.. code-block:: bash - - pytest --resultlog=path - -and look at the content at the ``path`` location. Such files are used e.g. -by the `PyPy-test`_ web page to show test results over several revisions. - -.. warning:: - - This option is rarely used and is scheduled for removal in pytest 6.0. - - If you use this option, consider using the new `pytest-reportlog `__ plugin instead. - - See `the deprecation docs `__ - for more information. - - -.. _`PyPy-test`: http://buildbot.pypy.org/summary - - -Sending test report to online pastebin service ------------------------------------------------------ - -**Creating a URL for each test failure**: - -.. code-block:: bash - - pytest --pastebin=failed - -This will submit test run information to a remote Paste service and -provide a URL for each failure. You may select tests as usual or add -for example ``-x`` if you only want to send one particular failure. - -**Creating a URL for a whole test session log**: - -.. code-block:: bash - - pytest --pastebin=all - -Currently only pasting to the http://bpaste.net service is implemented. - -.. versionchanged:: 5.2 - -If creating the URL fails for any reason, a warning is generated instead of failing the -entire test suite. +Managing loading of plugins +------------------------------- Early loading plugins ---------------------- +~~~~~~~~~~~~~~~~~~~~~~~ You can early-load plugins (internal and external) explicitly in the command-line with the ``-p`` option:: @@ -783,7 +785,7 @@ The option receives a ``name`` parameter, which can be: Disabling plugins ------------------ +~~~~~~~~~~~~~~~~~~ To disable loading specific plugins at invocation time, use the ``-p`` option together with the prefix ``no:``. @@ -795,12 +797,31 @@ executing doctest tests from text files, invoke pytest like this: pytest -p no:doctest -.. _`pytest.main-usage`: -Calling pytest from Python code ----------------------------------------------------- +.. _invoke-other: + +Other ways of calling pytest +----------------------------------------------------- +.. _invoke-python: +Calling pytest through ``python -m pytest`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You can invoke testing through the Python interpreter from the command line: + +.. code-block:: text + + python -m pytest [...] + +This is almost equivalent to invoking the command line script ``pytest [...]`` +directly, except that calling via ``python`` will also add the current directory to ``sys.path``. + + +.. _`pytest.main-usage`: + +Calling pytest from Python code +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ You can invoke ``pytest`` from Python code directly: From f28421cc7068b13ba63c1f60cc21f898cccea36c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 23 Mar 2021 19:05:19 +0000 Subject: [PATCH 213/630] [pre-commit.ci] pre-commit autoupdate (#8480) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b96fdccf10c..7c33ae9012d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -34,7 +34,7 @@ repos: - id: reorder-python-imports args: ['--application-directories=.:src', --py36-plus] - repo: https://github.com/asottile/pyupgrade - rev: v2.10.0 + rev: v2.11.0 hooks: - id: pyupgrade args: [--py36-plus] From 76ab94e4a04084e472dfccac2013f391db6fede6 Mon Sep 17 00:00:00 2001 From: wim glenn Date: Thu, 25 Mar 2021 22:25:19 -0500 Subject: [PATCH 214/630] fix some bunk formatting in the CollectReport, and reword the description of the 'sections' attribute --- src/_pytest/reports.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/_pytest/reports.py b/src/_pytest/reports.py index b4013f6a2f6..6be6000e802 100644 --- a/src/_pytest/reports.py +++ b/src/_pytest/reports.py @@ -281,10 +281,10 @@ def __init__( #: defined properties of the test. self.user_properties = list(user_properties or []) - #: List of pairs ``(str, str)`` of extra information which needs to - #: marshallable. Used by pytest to add captured text - #: from ``stdout`` and ``stderr``, but may be used by other plugins - #: to add arbitrary information to reports. + #: Tuples of str ``(heading, content)`` with extra information + #: for the test report. Used by pytest to add text captured + #: from ``stdout``, ``stderr``, and intercepted logging events. May + #: be used by other plugins to add arbitrary information to reports. self.sections = list(sections) #: Time it took to run just the test. @@ -381,11 +381,10 @@ def __init__( #: The collected items and collection nodes. self.result = result or [] - #: List of pairs ``(str, str)`` of extra information which needs to - #: marshallable. - # Used by pytest to add captured text : from ``stdout`` and ``stderr``, - # but may be used by other plugins : to add arbitrary information to - # reports. + #: Tuples of str ``(heading, content)`` with extra information + #: for the test report. Used by pytest to add text captured + #: from ``stdout``, ``stderr``, and intercepted logging events. May + #: be used by other plugins to add arbitrary information to reports. self.sections = list(sections) self.__dict__.update(extra) From be8d63e33b199cafa2656d49ad7128cc32214004 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Tue, 2 Mar 2021 21:47:37 -0300 Subject: [PATCH 215/630] Increase truncation threshold with -v, disable with -vv Fix #6682 Fix #8403 --- changelog/8403.improvement.rst | 5 + doc/en/how-to/usage.rst | 239 ++++++++++++++++++++++++++++++ src/_pytest/_io/saferepr.py | 35 ++++- src/_pytest/assertion/__init__.py | 2 + src/_pytest/assertion/rewrite.py | 14 +- src/_pytest/assertion/util.py | 5 + testing/io/test_saferepr.py | 8 + testing/test_assertrewrite.py | 53 +++++++ 8 files changed, 352 insertions(+), 9 deletions(-) create mode 100644 changelog/8403.improvement.rst diff --git a/changelog/8403.improvement.rst b/changelog/8403.improvement.rst new file mode 100644 index 00000000000..ec392245f67 --- /dev/null +++ b/changelog/8403.improvement.rst @@ -0,0 +1,5 @@ +By default, pytest will truncate long strings in assert errors so they don't clutter the output too much, +currently at ``240`` characters by default. + +However, in some cases the longer output helps, or is even crucial, to diagnose a failure. Using ``-v`` will +now increase the truncation threshold to ``2400`` characters, and ``-vv`` or higher will disable truncation entirely. diff --git a/doc/en/how-to/usage.rst b/doc/en/how-to/usage.rst index cba2e01989b..5917c856dde 100644 --- a/doc/en/how-to/usage.rst +++ b/doc/en/how-to/usage.rst @@ -161,6 +161,243 @@ will be shown (because KeyboardInterrupt is caught by pytest). By using this option you make sure a trace is shown. +Verbosity +--------- + +The ``-v`` flag controls the verbosity of pytest output in various aspects: test session progress, assertion +details when tests fail, fixtures details with ``--fixtures``, etc. + +.. regendoc:wipe + +Consider this simple file: + +.. code-block:: python + + # content of test_verbosity_example.py + def test_ok(): + pass + + + def test_words_fail(): + fruits1 = ["banana", "apple", "grapes", "melon", "kiwi"] + fruits2 = ["banana", "apple", "orange", "melon", "kiwi"] + assert fruits1 == fruits2 + + + def test_numbers_fail(): + number_to_text1 = {str(x): x for x in range(5)} + number_to_text2 = {str(x * 10): x * 10 for x in range(5)} + assert number_to_text1 == number_to_text2 + + + def test_long_text_fail(): + long_text = "Lorem ipsum dolor sit amet " * 10 + assert "hello world" in long_text + +Executing pytest normally gives us this output (we are skipping the header to focus on the rest): + +.. code-block:: pytest + + $ pytest --no-header + =========================== test session starts =========================== + collected 4 items + + test_verbosity_example.py .FFF [100%] + + ================================ FAILURES ================================= + _____________________________ test_words_fail _____________________________ + + def test_words_fail(): + fruits1 = ["banana", "apple", "grapes", "melon", "kiwi"] + fruits2 = ["banana", "apple", "orange", "melon", "kiwi"] + > assert fruits1 == fruits2 + E AssertionError: assert ['banana', 'a...elon', 'kiwi'] == ['banana', 'a...elon', 'kiwi'] + E At index 2 diff: 'grapes' != 'orange' + E Use -v to get the full diff + + test_verbosity_example.py:8: AssertionError + ____________________________ test_numbers_fail ____________________________ + + def test_numbers_fail(): + number_to_text1 = {str(x): x for x in range(5)} + number_to_text2 = {str(x * 10): x * 10 for x in range(5)} + > assert number_to_text1 == number_to_text2 + E AssertionError: assert {'0': 0, '1':..., '3': 3, ...} == {'0': 0, '10'...'30': 30, ...} + E Omitting 1 identical items, use -vv to show + E Left contains 4 more items: + E {'1': 1, '2': 2, '3': 3, '4': 4} + E Right contains 4 more items: + E {'10': 10, '20': 20, '30': 30, '40': 40} + E Use -v to get the full diff + + test_verbosity_example.py:14: AssertionError + ___________________________ test_long_text_fail ___________________________ + + def test_long_text_fail(): + long_text = "Lorem ipsum dolor sit amet " * 10 + > assert "hello world" in long_text + E AssertionError: assert 'hello world' in 'Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ips... sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet ' + + test_verbosity_example.py:19: AssertionError + ========================= short test summary info ========================= + FAILED test_verbosity_example.py::test_words_fail - AssertionError: asser... + FAILED test_verbosity_example.py::test_numbers_fail - AssertionError: ass... + FAILED test_verbosity_example.py::test_long_text_fail - AssertionError: a... + ======================= 3 failed, 1 passed in 0.08s ======================= + +Notice that: + +* Each test inside the file is shown by a single character in the output: ``.`` for passing, ``F`` for failure. +* ``test_words_fail`` failed, and we are shown a short summary indicating the index 2 of the two lists differ. +* ``test_numbers_fail`` failed, and we are shown a summary of left/right differences on dictionary items. Identical items are omitted. +* ``test_long_text_fail`` failed, and the right hand side of the ``in`` statement is truncated using ``...``` + because it is longer than an internal threshold (240 characters currently). + +Now we can increase pytest's verbosity: + +.. code-block:: pytest + + $ pytest --no-header -v + =========================== test session starts =========================== + collecting ... collected 4 items + + test_verbosity_example.py::test_ok PASSED [ 25%] + test_verbosity_example.py::test_words_fail FAILED [ 50%] + test_verbosity_example.py::test_numbers_fail FAILED [ 75%] + test_verbosity_example.py::test_long_text_fail FAILED [100%] + + ================================ FAILURES ================================= + _____________________________ test_words_fail _____________________________ + + def test_words_fail(): + fruits1 = ["banana", "apple", "grapes", "melon", "kiwi"] + fruits2 = ["banana", "apple", "orange", "melon", "kiwi"] + > assert fruits1 == fruits2 + E AssertionError: assert ['banana', 'a...elon', 'kiwi'] == ['banana', 'a...elon', 'kiwi'] + E At index 2 diff: 'grapes' != 'orange' + E Full diff: + E - ['banana', 'apple', 'orange', 'melon', 'kiwi'] + E ? ^ ^^ + E + ['banana', 'apple', 'grapes', 'melon', 'kiwi'] + E ? ^ ^ + + + test_verbosity_example.py:8: AssertionError + ____________________________ test_numbers_fail ____________________________ + + def test_numbers_fail(): + number_to_text1 = {str(x): x for x in range(5)} + number_to_text2 = {str(x * 10): x * 10 for x in range(5)} + > assert number_to_text1 == number_to_text2 + E AssertionError: assert {'0': 0, '1':..., '3': 3, ...} == {'0': 0, '10'...'30': 30, ...} + E Omitting 1 identical items, use -vv to show + E Left contains 4 more items: + E {'1': 1, '2': 2, '3': 3, '4': 4} + E Right contains 4 more items: + E {'10': 10, '20': 20, '30': 30, '40': 40} + E Full diff: + E - {'0': 0, '10': 10, '20': 20, '30': 30, '40': 40}... + E + E ...Full output truncated (3 lines hidden), use '-vv' to show + + test_verbosity_example.py:14: AssertionError + ___________________________ test_long_text_fail ___________________________ + + def test_long_text_fail(): + long_text = "Lorem ipsum dolor sit amet " * 10 + > assert "hello world" in long_text + E AssertionError: assert 'hello world' in 'Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet ' + + test_verbosity_example.py:19: AssertionError + ========================= short test summary info ========================= + FAILED test_verbosity_example.py::test_words_fail - AssertionError: asser... + FAILED test_verbosity_example.py::test_numbers_fail - AssertionError: ass... + FAILED test_verbosity_example.py::test_long_text_fail - AssertionError: a... + ======================= 3 failed, 1 passed in 0.07s ======================= + +Notice now that: + +* Each test inside the file gets its own line in the output. +* ``test_words_fail`` now shows the two failing lists in full, in addition to which index differs. +* ``test_numbers_fail`` now shows a text diff of the two dictionaries, truncated. +* ``test_long_text_fail`` no longer truncates the right hand side of the ``in`` statement, because the internal + threshold for truncation is larger now (2400 characters currently). + +Now if we increase verbosity even more: + +.. code-block:: pytest + + $ pytest --no-header -vv + =========================== test session starts =========================== + collecting ... collected 4 items + + test_verbosity_example.py::test_ok PASSED [ 25%] + test_verbosity_example.py::test_words_fail FAILED [ 50%] + test_verbosity_example.py::test_numbers_fail FAILED [ 75%] + test_verbosity_example.py::test_long_text_fail FAILED [100%] + + ================================ FAILURES ================================= + _____________________________ test_words_fail _____________________________ + + def test_words_fail(): + fruits1 = ["banana", "apple", "grapes", "melon", "kiwi"] + fruits2 = ["banana", "apple", "orange", "melon", "kiwi"] + > assert fruits1 == fruits2 + E AssertionError: assert ['banana', 'apple', 'grapes', 'melon', 'kiwi'] == ['banana', 'apple', 'orange', 'melon', 'kiwi'] + E At index 2 diff: 'grapes' != 'orange' + E Full diff: + E - ['banana', 'apple', 'orange', 'melon', 'kiwi'] + E ? ^ ^^ + E + ['banana', 'apple', 'grapes', 'melon', 'kiwi'] + E ? ^ ^ + + + test_verbosity_example.py:8: AssertionError + ____________________________ test_numbers_fail ____________________________ + + def test_numbers_fail(): + number_to_text1 = {str(x): x for x in range(5)} + number_to_text2 = {str(x * 10): x * 10 for x in range(5)} + > assert number_to_text1 == number_to_text2 + E AssertionError: assert {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4} == {'0': 0, '10': 10, '20': 20, '30': 30, '40': 40} + E Common items: + E {'0': 0} + E Left contains 4 more items: + E {'1': 1, '2': 2, '3': 3, '4': 4} + E Right contains 4 more items: + E {'10': 10, '20': 20, '30': 30, '40': 40} + E Full diff: + E - {'0': 0, '10': 10, '20': 20, '30': 30, '40': 40} + E ? - - - - - - - - + E + {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4} + + test_verbosity_example.py:14: AssertionError + ___________________________ test_long_text_fail ___________________________ + + def test_long_text_fail(): + long_text = "Lorem ipsum dolor sit amet " * 10 + > assert "hello world" in long_text + E AssertionError: assert 'hello world' in 'Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet ' + + test_verbosity_example.py:19: AssertionError + ========================= short test summary info ========================= + FAILED test_verbosity_example.py::test_words_fail - AssertionError: asser... + FAILED test_verbosity_example.py::test_numbers_fail - AssertionError: ass... + FAILED test_verbosity_example.py::test_long_text_fail - AssertionError: a... + ======================= 3 failed, 1 passed in 0.07s ======================= + +Notice now that: + +* Each test inside the file gets its own line in the output. +* ``test_words_fail`` gives the same output as before in this case. +* ``test_numbers_fail`` now shows a full text diff of the two dictionaries. +* ``test_long_text_fail`` also doesn't truncate on the right hand side as before, but now pytest won't truncate any + text at all, regardless of its size. + +Those were examples of how verbosity affects normal test session output, but verbosity also is used in other +situations, for example you are shown even fixtures that start with ``_`` if you use ``pytest --fixtures -v``. + +Using higher verbosity levels (``-vvv``, ``-vvvv``, ...) is supported, but has no effect in pytest itself at the moment, +however some plugins might make use of higher verbosity. + .. _`pytest.detailed_failed_tests_usage`: Producing a detailed summary report @@ -171,6 +408,8 @@ making it easy in large test suites to get a clear picture of all failures, skip It defaults to ``fE`` to list failures and errors. +.. regendoc:wipe + Example: .. code-block:: python diff --git a/src/_pytest/_io/saferepr.py b/src/_pytest/_io/saferepr.py index 440b8cbbb54..fa123d2cb19 100644 --- a/src/_pytest/_io/saferepr.py +++ b/src/_pytest/_io/saferepr.py @@ -36,12 +36,23 @@ def _ellipsize(s: str, maxsize: int) -> str: class SafeRepr(reprlib.Repr): - """repr.Repr that limits the resulting size of repr() and includes - information on exceptions raised during the call.""" + """ + repr.Repr that limits the resulting size of repr() and includes + information on exceptions raised during the call. + """ - def __init__(self, maxsize: int) -> None: + def __init__(self, maxsize: Optional[int]) -> None: + """ + :param maxsize: + If not None, will truncate the resulting repr to that specific size, using ellipsis + somewhere in the middle to hide the extra text. + If None, will not impose any size limits on the returning repr. + """ super().__init__() - self.maxstring = maxsize + # ``maxstring`` is used by the superclass, and needs to be an int; using a + # very large number in case maxsize is None, meaning we want to disable + # truncation. + self.maxstring = maxsize if maxsize is not None else 1_000_000_000 self.maxsize = maxsize def repr(self, x: object) -> str: @@ -51,7 +62,9 @@ def repr(self, x: object) -> str: raise except BaseException as exc: s = _format_repr_exception(exc, x) - return _ellipsize(s, self.maxsize) + if self.maxsize is not None: + s = _ellipsize(s, self.maxsize) + return s def repr_instance(self, x: object, level: int) -> str: try: @@ -60,7 +73,9 @@ def repr_instance(self, x: object, level: int) -> str: raise except BaseException as exc: s = _format_repr_exception(exc, x) - return _ellipsize(s, self.maxsize) + if self.maxsize is not None: + s = _ellipsize(s, self.maxsize) + return s def safeformat(obj: object) -> str: @@ -75,7 +90,11 @@ def safeformat(obj: object) -> str: return _format_repr_exception(exc, obj) -def saferepr(obj: object, maxsize: int = 240) -> str: +# Maximum size of overall repr of objects to display during assertion errors. +DEFAULT_REPR_MAX_SIZE = 240 + + +def saferepr(obj: object, maxsize: Optional[int] = DEFAULT_REPR_MAX_SIZE) -> str: """Return a size-limited safe repr-string for the given object. Failing __repr__ functions of user instances will be represented @@ -83,7 +102,7 @@ def saferepr(obj: object, maxsize: int = 240) -> str: care to never raise exceptions itself. This function is a wrapper around the Repr/reprlib functionality of the - standard 2.6 lib. + stdlib. """ return SafeRepr(maxsize).repr(obj) diff --git a/src/_pytest/assertion/__init__.py b/src/_pytest/assertion/__init__.py index a18cf198df0..4c9826f3f26 100644 --- a/src/_pytest/assertion/__init__.py +++ b/src/_pytest/assertion/__init__.py @@ -153,6 +153,7 @@ def callbinrepr(op, left: object, right: object) -> Optional[str]: saved_assert_hooks = util._reprcompare, util._assertion_pass util._reprcompare = callbinrepr + util._config = item.config if ihook.pytest_assertion_pass.get_hookimpls(): @@ -164,6 +165,7 @@ def call_assertion_pass_hook(lineno: int, orig: str, expl: str) -> None: yield util._reprcompare, util._assertion_pass = saved_assert_hooks + util._config = None def pytest_sessionfinish(session: "Session") -> None: diff --git a/src/_pytest/assertion/rewrite.py b/src/_pytest/assertion/rewrite.py index a01be76b4d3..6a3222f333d 100644 --- a/src/_pytest/assertion/rewrite.py +++ b/src/_pytest/assertion/rewrite.py @@ -27,6 +27,7 @@ from typing import TYPE_CHECKING from typing import Union +from _pytest._io.saferepr import DEFAULT_REPR_MAX_SIZE from _pytest._io.saferepr import saferepr from _pytest._version import version from _pytest.assertion import util @@ -427,7 +428,18 @@ def _saferepr(obj: object) -> str: sequences, especially '\n{' and '\n}' are likely to be present in JSON reprs. """ - return saferepr(obj).replace("\n", "\\n") + maxsize = _get_maxsize_for_saferepr(util._config) + return saferepr(obj, maxsize=maxsize).replace("\n", "\\n") + + +def _get_maxsize_for_saferepr(config: Optional[Config]) -> Optional[int]: + """Get `maxsize` configuration for saferepr based on the given config object.""" + verbosity = config.getoption("verbose") if config is not None else 0 + if verbosity >= 2: + return None + if verbosity >= 1: + return DEFAULT_REPR_MAX_SIZE * 10 + return DEFAULT_REPR_MAX_SIZE def _format_assertmsg(obj: object) -> str: diff --git a/src/_pytest/assertion/util.py b/src/_pytest/assertion/util.py index da1ffd15e37..0e54335ab36 100644 --- a/src/_pytest/assertion/util.py +++ b/src/_pytest/assertion/util.py @@ -15,6 +15,8 @@ from _pytest._io.saferepr import _pformat_dispatch from _pytest._io.saferepr import safeformat from _pytest._io.saferepr import saferepr +from _pytest.config import Config + # The _reprcompare attribute on the util module is used by the new assertion # interpretation code and assertion rewriter to detect this plugin was @@ -26,6 +28,9 @@ # when pytest_runtest_setup is called. _assertion_pass: Optional[Callable[[int, str, str], None]] = None +# Config object which is assigned during pytest_runtest_protocol. +_config: Optional[Config] = None + def format_explanation(explanation: str) -> str: r"""Format an explanation. diff --git a/testing/io/test_saferepr.py b/testing/io/test_saferepr.py index 7a97cf424c5..63d3af822b1 100644 --- a/testing/io/test_saferepr.py +++ b/testing/io/test_saferepr.py @@ -1,5 +1,6 @@ import pytest from _pytest._io.saferepr import _pformat_dispatch +from _pytest._io.saferepr import DEFAULT_REPR_MAX_SIZE from _pytest._io.saferepr import saferepr @@ -15,6 +16,13 @@ def test_maxsize(): assert s == expected +def test_no_maxsize(): + text = "x" * DEFAULT_REPR_MAX_SIZE * 10 + s = saferepr(text, maxsize=None) + expected = repr(text) + assert s == expected + + def test_maxsize_error_on_instance(): class A: def __repr__(self): diff --git a/testing/test_assertrewrite.py b/testing/test_assertrewrite.py index f7d9d62ef63..38969d2946e 100644 --- a/testing/test_assertrewrite.py +++ b/testing/test_assertrewrite.py @@ -11,6 +11,7 @@ import zipfile from functools import partial from pathlib import Path +from typing import cast from typing import Dict from typing import List from typing import Mapping @@ -19,13 +20,16 @@ import _pytest._code import pytest +from _pytest._io.saferepr import DEFAULT_REPR_MAX_SIZE from _pytest.assertion import util from _pytest.assertion.rewrite import _get_assertion_exprs +from _pytest.assertion.rewrite import _get_maxsize_for_saferepr from _pytest.assertion.rewrite import AssertionRewritingHook from _pytest.assertion.rewrite import get_cache_dir from _pytest.assertion.rewrite import PYC_TAIL from _pytest.assertion.rewrite import PYTEST_TAG from _pytest.assertion.rewrite import rewrite_asserts +from _pytest.config import Config from _pytest.config import ExitCode from _pytest.pathlib import make_numbered_dir from _pytest.pytester import Pytester @@ -1706,3 +1710,52 @@ def test_foo(): cache_tag=sys.implementation.cache_tag ) assert bar_init_pyc.is_file() + + +class TestReprSizeVerbosity: + """ + Check that verbosity also controls the string length threshold to shorten it using + ellipsis. + """ + + @pytest.mark.parametrize( + "verbose, expected_size", + [ + (0, DEFAULT_REPR_MAX_SIZE), + (1, DEFAULT_REPR_MAX_SIZE * 10), + (2, None), + (3, None), + ], + ) + def test_get_maxsize_for_saferepr(self, verbose: int, expected_size) -> None: + class FakeConfig: + def getoption(self, name: str) -> int: + assert name == "verbose" + return verbose + + config = FakeConfig() + assert _get_maxsize_for_saferepr(cast(Config, config)) == expected_size + + def create_test_file(self, pytester: Pytester, size: int) -> None: + pytester.makepyfile( + f""" + def test_very_long_string(): + text = "x" * {size} + assert "hello world" in text + """ + ) + + def test_default_verbosity(self, pytester: Pytester) -> None: + self.create_test_file(pytester, DEFAULT_REPR_MAX_SIZE) + result = pytester.runpytest() + result.stdout.fnmatch_lines(["*xxx...xxx*"]) + + def test_increased_verbosity(self, pytester: Pytester) -> None: + self.create_test_file(pytester, DEFAULT_REPR_MAX_SIZE) + result = pytester.runpytest("-v") + result.stdout.no_fnmatch_line("*xxx...xxx*") + + def test_max_increased_verbosity(self, pytester: Pytester) -> None: + self.create_test_file(pytester, DEFAULT_REPR_MAX_SIZE * 10) + result = pytester.runpytest("-vv") + result.stdout.no_fnmatch_line("*xxx...xxx*") From c1e057065c612b8471ccceaee40f822a48250eef Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Fri, 26 Mar 2021 07:13:52 -0300 Subject: [PATCH 216/630] Fix plugin-list label and script This was changed during the current docs restructing and we missed that it changed the label. --- doc/en/reference/plugin_list.rst | 6 ++++-- scripts/update-plugin-list.py | 7 +++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/doc/en/reference/plugin_list.rst b/doc/en/reference/plugin_list.rst index 24cacbc71fc..97ab28e2151 100644 --- a/doc/en/reference/plugin_list.rst +++ b/doc/en/reference/plugin_list.rst @@ -1,5 +1,7 @@ -Plugins List -============ +.. _plugin-list: + +Plugin List +=========== PyPI projects that match "pytest-\*" are considered plugins and are listed automatically. Packages classified as inactive are excluded. diff --git a/scripts/update-plugin-list.py b/scripts/update-plugin-list.py index bc4d8a6a66b..4e00f1a4536 100644 --- a/scripts/update-plugin-list.py +++ b/scripts/update-plugin-list.py @@ -6,8 +6,11 @@ import requests import tabulate -FILE_HEAD = r"""Plugins List -============ +FILE_HEAD = r"""\ +.. _plugin-list: + +Plugin List +=========== PyPI projects that match "pytest-\*" are considered plugins and are listed automatically. Packages classified as inactive are excluded. From 8c65a4f1749d2a4ba81884aa7e6874822ed02e45 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Fri, 26 Mar 2021 07:27:41 -0300 Subject: [PATCH 217/630] Configure support for Python 3.10 --- .github/workflows/main.yml | 15 +++++++++++++++ tox.ini | 1 + 2 files changed, 16 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7872f978ae1..98e32512f7a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -27,6 +27,8 @@ jobs: "windows-py37", "windows-py37-pluggy", "windows-py38", + "windows-py39", + "windows-py310", "ubuntu-py36", "ubuntu-py37", @@ -34,6 +36,7 @@ jobs: "ubuntu-py37-freeze", "ubuntu-py38", "ubuntu-py39", + "ubuntu-py310", "ubuntu-pypy3", "macos-py37", @@ -62,6 +65,14 @@ jobs: os: windows-latest tox_env: "py38-unittestextras" use_coverage: true + - name: "windows-py39" + python: "3.9" + os: windows-latest + tox_env: "py39-xdist" + - name: "windows-py310" + python: "3.10-dev" + os: windows-latest + tox_env: "py310-xdist" - name: "ubuntu-py36" python: "3.6" @@ -88,6 +99,10 @@ jobs: python: "3.9" os: ubuntu-latest tox_env: "py39-xdist" + - name: "ubuntu-py310" + python: "3.10-dev" + os: ubuntu-latest + tox_env: "py310-xdist" - name: "ubuntu-pypy3" python: "pypy-3.7" os: ubuntu-latest diff --git a/tox.ini b/tox.ini index da5a634de8b..b64608adcf9 100644 --- a/tox.ini +++ b/tox.ini @@ -8,6 +8,7 @@ envlist = py37 py38 py39 + py310 pypy3 py37-{pexpect,xdist,unittestextras,numpy,pluggymain} doctesting From b2954e85d6448d449da5ff1c861b40d218ed5b1d Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sat, 27 Mar 2021 10:33:48 -0300 Subject: [PATCH 218/630] Adjust message for Python 3.10 Now the message includes the class name ("Skip.__init__() got multiple..."). --- testing/test_skipping.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/testing/test_skipping.py b/testing/test_skipping.py index bba36421ace..878b1549fe8 100644 --- a/testing/test_skipping.py +++ b/testing/test_skipping.py @@ -876,7 +876,8 @@ def test_hello(): result = pytester.runpytest() result.stdout.fnmatch_lines( [ - "*TypeError: __init__() got multiple values for argument 'reason' - maybe you meant pytest.mark.skipif?" + "*TypeError: *__init__() got multiple values for argument 'reason'" + " - maybe you meant pytest.mark.skipif?" ] ) From 913cffa45f9230087f589140604ec1eb5f199a83 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sat, 27 Mar 2021 10:37:34 -0300 Subject: [PATCH 219/630] Add warnings filter for disutils deprecation Deprecated in 3.10, scheduled for removal in 3.12 --- pyproject.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index dd4be6c22d5..dc26b4d8ddf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,6 +26,8 @@ filterwarnings = [ # produced by older pyparsing<=2.2.0. "default:Using or importing the ABCs:DeprecationWarning:pyparsing.*", "default:the imp module is deprecated in favour of importlib:DeprecationWarning:nose.*", + # distutils is deprecated in 3.10, scheduled for removal in 3.12 + "ignore:The distutils package is deprecated:DeprecationWarning", # produced by python3.6/site.py itself (3.6.7 on Travis, could not trigger it with 3.6.8)." "ignore:.*U.*mode is deprecated:DeprecationWarning:(?!(pytest|_pytest))", # produced by pytest-xdist From a7416a535af4ca5ab3c1a77af2c19e7729212760 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sat, 27 Mar 2021 11:04:26 -0300 Subject: [PATCH 220/630] Add classifier for Python 3.10 and CHANGELOG entry --- .pre-commit-config.yaml | 1 + changelog/8494.feature.rst | 1 + setup.cfg | 1 + 3 files changed, 3 insertions(+) create mode 100644 changelog/8494.feature.rst diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7c33ae9012d..e78bab46271 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -42,6 +42,7 @@ repos: rev: v1.17.0 hooks: - id: setup-cfg-fmt + args: [--max-py-version=3.10] - repo: https://github.com/pre-commit/pygrep-hooks rev: v1.8.0 hooks: diff --git a/changelog/8494.feature.rst b/changelog/8494.feature.rst new file mode 100644 index 00000000000..eca51d0deb9 --- /dev/null +++ b/changelog/8494.feature.rst @@ -0,0 +1 @@ +Python 3.10 is now supported. diff --git a/setup.cfg b/setup.cfg index bf8d2c85040..e9b8e597fb7 100644 --- a/setup.cfg +++ b/setup.cfg @@ -21,6 +21,7 @@ classifiers = Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 + Programming Language :: Python :: 3.10 Topic :: Software Development :: Libraries Topic :: Software Development :: Testing Topic :: Utilities From bfdfab00dd4e482d9fb95e22de2e98f77f23dac6 Mon Sep 17 00:00:00 2001 From: pytest bot Date: Sun, 28 Mar 2021 00:18:38 +0000 Subject: [PATCH 221/630] [automated] Update plugin list --- doc/en/reference/plugin_list.rst | 46 +++++++++++++++++--------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/doc/en/reference/plugin_list.rst b/doc/en/reference/plugin_list.rst index 97ab28e2151..40c782cf688 100644 --- a/doc/en/reference/plugin_list.rst +++ b/doc/en/reference/plugin_list.rst @@ -1,3 +1,4 @@ +\ .. _plugin-list: Plugin List @@ -5,11 +6,11 @@ Plugin List PyPI projects that match "pytest-\*" are considered plugins and are listed automatically. Packages classified as inactive are excluded. -This list contains 844 plugins. +This list contains 847 plugins. -============================================================================================================== ======================================================================================================================================================================== ============== ===================== ================================================ +============================================================================================================== ======================================================================================================================================================================== ============== ===================== ============================================ name summary last release status requires -============================================================================================================== ======================================================================================================================================================================== ============== ===================== ================================================ +============================================================================================================== ======================================================================================================================================================================== ============== ===================== ============================================ `pytest-adaptavist `_ pytest plugin for generating test execution results within Jira Test Management (tm4j) Feb 05, 2020 N/A pytest (>=3.4.1) `pytest-adf `_ Pytest plugin for writing Azure Data Factory integration tests Mar 10, 2021 4 - Beta pytest (>=3.5.0) `pytest-adf-azure-identity `_ Pytest plugin for writing Azure Data Factory integration tests Mar 06, 2021 4 - Beta pytest (>=3.5.0) @@ -101,7 +102,7 @@ name `pytest-canonical-data `_ A plugin which allows to compare results with canonical results, based on previous runs May 08, 2020 2 - Pre-Alpha pytest (>=3.5.0) `pytest-caprng `_ A plugin that replays pRNG state on failure. May 02, 2018 4 - Beta N/A `pytest-capture-deprecatedwarnings `_ pytest plugin to capture all deprecatedwarnings and put them in one file Apr 30, 2019 N/A N/A -`pytest-cases `_ Separate test code from test cases in pytest. Feb 19, 2021 5 - Production/Stable N/A +`pytest-cases `_ Separate test code from test cases in pytest. Mar 24, 2021 5 - Production/Stable N/A `pytest-cassandra `_ Cassandra CCM Test Fixtures for pytest Nov 04, 2017 1 - Planning N/A `pytest-catchlog `_ py.test plugin to catch log messages. This is a fork of pytest-capturelog. Jan 24, 2016 4 - Beta pytest (>=2.6) `pytest-catch-server `_ Pytest plugin with server for catching HTTP requests. Dec 12, 2019 5 - Production/Stable N/A @@ -178,6 +179,7 @@ name `pytest-dbt-adapter `_ A pytest plugin for testing dbt adapter plugins Jan 07, 2021 N/A pytest (<7,>=6) `pytest-dbus-notification `_ D-BUS notifications for pytest results. Mar 05, 2014 5 - Production/Stable N/A `pytest-deadfixtures `_ A simple plugin to list unused fixtures in pytest Jul 23, 2020 5 - Production/Stable N/A +`pytest-deepcov `_ deepcov Mar 23, 2021 N/A N/A `pytest-dependency `_ Manage dependencies of tests Feb 14, 2020 4 - Beta N/A `pytest-depends `_ Tests that depend on other tests Apr 05, 2020 5 - Production/Stable pytest (>=3) `pytest-deprecate `_ Mark tests as testing a deprecated feature with a warning note. Jul 01, 2019 N/A N/A @@ -223,7 +225,7 @@ name `pytest-docker-postgresql `_ A simple plugin to use with pytest Sep 24, 2019 4 - Beta pytest (>=3.5.0) `pytest-docker-py `_ Easy to use, simple to extend, pytest plugin that minimally leverages docker-py. Nov 27, 2018 N/A pytest (==4.0.0) `pytest-docker-registry-fixtures `_ Pytest fixtures for testing with docker registries. Mar 04, 2021 4 - Beta pytest -`pytest-docker-tools `_ Docker integration tests for pytest Mar 02, 2021 4 - Beta pytest (>=6.0.1,<7.0.0) +`pytest-docker-tools `_ Docker integration tests for pytest Mar 26, 2021 4 - Beta pytest (>=6.0.1,<7.0.0) `pytest-docs `_ Documentation tool for pytest Nov 11, 2018 4 - Beta pytest (>=3.5.0) `pytest-docstyle `_ pytest plugin to run pydocstyle Mar 23, 2020 3 - Alpha N/A `pytest-doctest-custom `_ A py.test plugin for customizing string representations of doctest results. Jul 25, 2016 4 - Beta N/A @@ -331,7 +333,7 @@ name `pytest-gitcov `_ Pytest plugin for reporting on coverage of the last git commit. Jan 11, 2020 2 - Pre-Alpha N/A `pytest-git-fixtures `_ Pytest fixtures for testing with git. Mar 11, 2021 4 - Beta pytest `pytest-github `_ Plugin for py.test that associates tests with github issues using a marker. Mar 07, 2019 5 - Production/Stable N/A -`pytest-github-actions-annotate-failures `_ pytest plugin to annotate failed tests with a workflow command for GitHub Actions Oct 13, 2020 N/A pytest (>=4.0.0) +`pytest-github-actions-annotate-failures `_ pytest plugin to annotate failed tests with a workflow command for GitHub Actions Mar 21, 2021 N/A pytest (>=4.0.0) `pytest-gitignore `_ py.test plugin to ignore the same files as git Jul 17, 2015 4 - Beta N/A `pytest-gnupg-fixtures `_ Pytest fixtures for testing with gnupg. Mar 04, 2021 4 - Beta pytest `pytest-golden `_ Plugin for pytest that offloads expected outputs to data files Nov 23, 2020 N/A pytest (>=6.1.2,<7.0.0) @@ -340,12 +342,12 @@ name `pytest-growl `_ Growl notifications for pytest results. Jan 13, 2014 5 - Production/Stable N/A `pytest-grpc `_ pytest plugin for grpc May 01, 2020 N/A pytest (>=3.6.0) `pytest-hammertime `_ Display "🔨 " instead of "." for passed pytest tests. Jul 28, 2018 N/A pytest -`pytest-harvest `_ Store data created during your pytest tests execution, and retrieve it at the end of the session, e.g. for applicative benchmarking purposes. Dec 08, 2020 5 - Production/Stable N/A +`pytest-harvest `_ Store data created during your pytest tests execution, and retrieve it at the end of the session, e.g. for applicative benchmarking purposes. Mar 22, 2021 5 - Production/Stable N/A `pytest-helm-chart `_ A plugin to provide different types and configs of Kubernetes clusters that can be used for testing. Jun 15, 2020 4 - Beta pytest (>=5.4.2,<6.0.0) `pytest-helm-charts `_ A plugin to provide different types and configs of Kubernetes clusters that can be used for testing. Dec 22, 2020 4 - Beta pytest (>=6.1.2,<7.0.0) `pytest-helper `_ Functions to help in using the pytest testing framework May 31, 2019 5 - Production/Stable N/A `pytest-helpers `_ pytest helpers May 17, 2020 N/A pytest -`pytest-helpers-namespace `_ PyTest Helpers Namespace Jan 07, 2019 5 - Production/Stable pytest (>=2.9.1) +`pytest-helpers-namespace `_ Pytest Helpers Namespace Plugin Mar 24, 2021 5 - Production/Stable pytest (>=6.1.1) `pytest-hidecaptured `_ Hide captured output May 04, 2018 4 - Beta pytest (>=2.8.5) `pytest-historic `_ Custom report to display pytest historical execution records Apr 08, 2020 N/A pytest `pytest-historic-hook `_ Custom listener to store execution results into MYSQL DB, which is used for pytest-historic report Apr 08, 2020 N/A pytest @@ -365,6 +367,7 @@ name `pytest-httpretty `_ A thin wrapper of HTTPretty for pytest Feb 16, 2014 3 - Alpha N/A `pytest-httpserver `_ pytest-httpserver is a httpserver for pytest Mar 16, 2021 3 - Alpha pytest ; extra == 'dev' `pytest-httpx `_ Send responses to httpx. Mar 01, 2021 5 - Production/Stable pytest (==6.*) +`pytest-httpx-blockage `_ Disable httpx requests during a test run Mar 20, 2021 N/A pytest (>=6.2.2) `pytest-hue `_ Visualise PyTest status via your Phillips Hue lights May 09, 2019 N/A N/A `pytest-hypo-25 `_ help hypo module for pytest Jan 12, 2020 3 - Alpha N/A `pytest-ibutsu `_ A plugin to sent pytest results to an Ibutsu server Mar 09, 2021 4 - Beta pytest @@ -377,7 +380,7 @@ name `pytest-info-collector `_ pytest plugin to collect information from tests May 26, 2019 3 - Alpha N/A `pytest-informative-node `_ display more node ininformation. Apr 25, 2019 4 - Beta N/A `pytest-infrastructure `_ pytest stack validation prior to testing executing Apr 12, 2020 4 - Beta N/A -`pytest-inmanta `_ A py.test plugin providing fixtures to simplify inmanta modules testing. Oct 12, 2020 5 - Production/Stable N/A +`pytest-inmanta `_ A py.test plugin providing fixtures to simplify inmanta modules testing. Mar 26, 2021 5 - Production/Stable N/A `pytest-inmanta-extensions `_ Inmanta tests package Mar 17, 2021 5 - Production/Stable N/A `pytest-Inomaly `_ A simple image diff plugin for pytest Feb 13, 2018 4 - Beta N/A `pytest-insta `_ A practical snapshot testing plugin for pytest Mar 03, 2021 N/A pytest (>=6.0.2,<7.0.0) @@ -483,7 +486,7 @@ name `pytest-multihost `_ Utility for writing multi-host tests for pytest Apr 07, 2020 4 - Beta N/A `pytest-multilog `_ Multi-process logs handling and other helpers for pytest Nov 15, 2020 N/A N/A `pytest-mutagen `_ Add the mutation testing feature to pytest Jul 24, 2020 N/A pytest (>=5.4) -`pytest-mypy `_ Mypy static type checker plugin for Pytest Nov 14, 2020 4 - Beta pytest (>=3.5) +`pytest-mypy `_ Mypy static type checker plugin for Pytest Mar 21, 2021 4 - Beta pytest (>=3.5) `pytest-mypyd `_ Mypy static type checker plugin for Pytest Aug 20, 2019 4 - Beta pytest (<4.7,>=2.8) ; python_version < "3.5" `pytest-mypy-plugins `_ pytest plugin for writing tests for mypy plugins Oct 26, 2020 3 - Alpha pytest (>=6.0.0) `pytest-mypy-plugins-shim `_ Substitute for "pytest-mypy-plugins" for Python implementations which aren't supported by mypy. Feb 14, 2021 N/A pytest (>=6.0.0) @@ -539,7 +542,7 @@ name `pytest-pep8 `_ pytest plugin to check PEP8 requirements Apr 27, 2014 N/A N/A `pytest-percent `_ Change the exit code of pytest test sessions when a required percent of tests pass. May 21, 2020 N/A pytest (>=5.2.0) `pytest-performance `_ A simple plugin to ensure the execution of critical sections of code has not been impacted Sep 11, 2020 5 - Production/Stable pytest (>=3.7.0) -`pytest-persistence `_ Pytest tool for persistent objects Mar 09, 2021 N/A N/A +`pytest-persistence `_ Pytest tool for persistent objects Mar 26, 2021 N/A N/A `pytest-pgsql `_ Pytest plugins and helpers for tests using a Postgres database. May 13, 2020 5 - Production/Stable pytest (>=3.0.0) `pytest-picked `_ Run the tests related to the changed files Dec 23, 2020 N/A pytest (>=3.5.0) `pytest-pigeonhole `_ Jun 25, 2018 5 - Production/Stable pytest (>=3.4) @@ -562,7 +565,7 @@ name `pytest-polarion-collect `_ pytest plugin for collecting polarion test cases data Jun 18, 2020 3 - Alpha pytest `pytest-polecat `_ Provides Polecat pytest fixtures Aug 12, 2019 4 - Beta N/A `pytest-ponyorm `_ PonyORM in Pytest Oct 31, 2018 N/A pytest (>=3.1.1) -`pytest-poo `_ Visualize your crappy tests Jul 14, 2013 5 - Production/Stable N/A +`pytest-poo `_ Visualize your crappy tests Mar 25, 2021 5 - Production/Stable pytest (>=2.3.4) `pytest-poo-fail `_ Visualize your failed tests with poo Feb 12, 2015 5 - Production/Stable N/A `pytest-pop `_ A pytest plugin to help with testing pop projects Aug 13, 2020 5 - Production/Stable pytest (>=5.4.0) `pytest-portion `_ Select a portion of the collected tests Jan 28, 2021 4 - Beta pytest (>=3.5.0) @@ -639,7 +642,7 @@ name `pytest-reraise `_ Make multi-threaded pytest test cases fail when they should Jun 03, 2020 5 - Production/Stable N/A `pytest-rerun `_ Re-run only changed files in specified branch Jul 08, 2019 N/A pytest (>=3.6) `pytest-rerunfailures `_ pytest plugin to re-run tests to eliminate flaky failures Sep 29, 2020 5 - Production/Stable pytest (>=5.0) -`pytest-resilient-circuits `_ Resilient Circuits fixtures for PyTest. Feb 19, 2021 N/A N/A +`pytest-resilient-circuits `_ Resilient Circuits fixtures for PyTest. Mar 25, 2021 N/A N/A `pytest-resource `_ Load resource fixture plugin to use with pytest Nov 14, 2018 4 - Beta N/A `pytest-resource-path `_ Provides path for uniform access to test resources in isolated directory Aug 18, 2020 5 - Production/Stable pytest (>=3.5.0) `pytest-responsemock `_ Simplified requests calls mocking for pytest Oct 10, 2020 5 - Production/Stable N/A @@ -653,24 +656,25 @@ name `pytest-rotest `_ Pytest integration with rotest Sep 08, 2019 N/A pytest (>=3.5.0) `pytest-rpc `_ Extend py.test for RPC OpenStack testing. Feb 22, 2019 4 - Beta pytest (~=3.6) `pytest-rt `_ pytest data collector plugin for Testgr Mar 03, 2021 N/A N/A -`pytest-rts `_ Coverage-based regression test selection (RTS) plugin for pytest Mar 18, 2021 N/A pytest +`pytest-rts `_ Coverage-based regression test selection (RTS) plugin for pytest Mar 24, 2021 N/A pytest +`pytest-run-changed `_ Pytest plugin that runs changed tests only Mar 25, 2021 3 - Alpha pytest `pytest-runfailed `_ implement a --failed option for pytest Mar 24, 2016 N/A N/A `pytest-runner `_ Invoke py.test as distutils command with dependency resolution Feb 12, 2021 5 - Production/Stable pytest (!=3.7.3,>=3.5) ; extra == 'testing' `pytest-salt `_ Pytest Salt Plugin Jan 27, 2020 4 - Beta N/A `pytest-salt-containers `_ A Pytest plugin that builds and creates docker containers Nov 09, 2016 4 - Beta N/A -`pytest-salt-factories `_ Pytest Salt Plugin Mar 17, 2021 4 - Beta pytest (>=6.1.1) +`pytest-salt-factories `_ Pytest Salt Plugin Mar 23, 2021 4 - Beta pytest (>=6.1.1) `pytest-salt-from-filenames `_ Simple PyTest Plugin For Salt's Test Suite Specifically Jan 29, 2019 4 - Beta pytest (>=4.1) `pytest-salt-runtests-bridge `_ Simple PyTest Plugin For Salt's Test Suite Specifically Dec 05, 2019 4 - Beta pytest (>=4.1) `pytest-sanic `_ a pytest plugin for Sanic Feb 27, 2021 N/A pytest (>=5.2) `pytest-sanity `_ Dec 07, 2020 N/A N/A `pytest-sa-pg `_ May 14, 2019 N/A N/A -`pytest-sbase `_ A complete web automation framework for end-to-end testing. Mar 19, 2021 5 - Production/Stable N/A +`pytest-sbase `_ A complete web automation framework for end-to-end testing. Mar 26, 2021 5 - Production/Stable N/A `pytest-scenario `_ pytest plugin for test scenarios Feb 06, 2017 3 - Alpha N/A `pytest-schema `_ 👍 Validate return values against a schema-like object in testing Aug 31, 2020 5 - Production/Stable pytest (>=3.5.0) `pytest-securestore `_ An encrypted password store for use within pytest cases Jun 19, 2019 4 - Beta N/A `pytest-select `_ A pytest plugin which allows to (de-)select tests from a file. Jan 18, 2019 3 - Alpha pytest (>=3.0) `pytest-selenium `_ pytest plugin for Selenium Sep 19, 2020 5 - Production/Stable pytest (>=5.0.0) -`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Mar 19, 2021 5 - Production/Stable N/A +`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Mar 26, 2021 5 - Production/Stable N/A `pytest-selenium-enhancer `_ pytest plugin for Selenium Nov 26, 2020 5 - Production/Stable N/A `pytest-selenium-pdiff `_ A pytest package implementing perceptualdiff for Selenium tests. Apr 06, 2017 2 - Pre-Alpha N/A `pytest-send-email `_ Send pytest execution result email Dec 04, 2019 N/A N/A @@ -716,7 +720,7 @@ name `pytest-splitio `_ Split.io SDK integration for e2e tests Sep 22, 2020 N/A pytest (<7,>=5.0) `pytest-split-tests `_ A Pytest plugin for running a subset of your tests by splitting them in to equally sized groups. Forked from Mark Adams' original project pytest-test-groups. May 28, 2019 N/A pytest (>=2.5) `pytest-splunk-addon `_ A Dynamic test tool for Splunk Apps and Add-ons Feb 26, 2021 N/A pytest (>5.4.0,<6.1) -`pytest-splunk-addon-ui-smartx `_ Library to support testing Splunk Add-on UX Jan 18, 2021 N/A N/A +`pytest-splunk-addon-ui-smartx `_ Library to support testing Splunk Add-on UX Mar 24, 2021 N/A N/A `pytest-splunk-env `_ pytest fixtures for interaction with Splunk Enterprise and Splunk Cloud Oct 22, 2020 N/A pytest (>=6.1.1,<7.0.0) `pytest-sqitch `_ sqitch for pytest Apr 06, 2020 4 - Beta N/A `pytest-sqlalchemy `_ pytest plugin with sqlalchemy related fixtures Mar 13, 2018 3 - Alpha N/A @@ -730,7 +734,7 @@ name `pytest-stepwise `_ Run a test suite one failing test at a time. Dec 01, 2015 4 - Beta N/A `pytest-stoq `_ A plugin to pytest stoq Feb 09, 2021 4 - Beta N/A `pytest-stress `_ A Pytest plugin that allows you to loop tests for a user defined amount of time. Dec 07, 2019 4 - Beta pytest (>=3.6.0) -`pytest-structlog `_ Structured logging assertions Jul 16, 2020 N/A pytest +`pytest-structlog `_ Structured logging assertions Mar 26, 2021 N/A pytest `pytest-structmpd `_ provide structured temporary directory Oct 17, 2018 N/A N/A `pytest-stub `_ Stub packages, modules and attributes. Apr 28, 2020 5 - Production/Stable N/A `pytest-stubprocess `_ Provide stub implementations for subprocesses in Python tests Sep 17, 2018 3 - Alpha pytest (>=3.5.0) @@ -747,7 +751,6 @@ name `pytest-tap `_ Test Anything Protocol (TAP) reporting plugin for pytest Nov 07, 2020 5 - Production/Stable pytest (>=3.0) `pytest-tape `_ easy assertion with expected results saved to yaml files Mar 17, 2021 4 - Beta N/A `pytest-target `_ Pytest plugin for remote target orchestration. Jan 21, 2021 3 - Alpha pytest (>=6.1.2,<7.0.0) -`pytest-tars `_ Pytest plugin for testing against real servers Mar 19, 2021 2 - Pre-Alpha pytest (<5.0.0,>=4.3.1) ; python_version < "3.5" `pytest-tblineinfo `_ tblineinfo is a py.test plugin that insert the node id in the final py.test report when --tb=line option is used Dec 01, 2015 3 - Alpha pytest (>=2.0) `pytest-teamcity-logblock `_ py.test plugin to introduce block structure in teamcity build log, if output is not captured May 15, 2018 4 - Beta N/A `pytest-telegram `_ Pytest to Telegram reporting plugin Dec 10, 2020 5 - Production/Stable N/A @@ -851,7 +854,8 @@ name `pytest-yapf `_ Run yapf Jul 06, 2017 4 - Beta pytest (>=3.1.1) `pytest-yapf3 `_ Validate your Python file format with yapf Aug 03, 2020 5 - Production/Stable pytest (>=5.4) `pytest-yield `_ PyTest plugin to run tests concurrently, each `yield` switch context to other one Jan 23, 2019 N/A N/A +`pytest-yuk `_ Display tests you are uneasy with, using 🤢/🤮 for pass/fail of tests marked with yuk. Mar 26, 2021 N/A N/A `pytest-zafira `_ A Zafira plugin for pytest Sep 18, 2019 5 - Production/Stable pytest (==4.1.1) `pytest-zap `_ OWASP ZAP plugin for py.test. May 12, 2014 4 - Beta N/A `pytest-zigzag `_ Extend py.test for RPC OpenStack testing. Feb 27, 2019 4 - Beta pytest (~=3.6) -============================================================================================================== ======================================================================================================================================================================== ============== ===================== ================================================ +============================================================================================================== ======================================================================================================================================================================== ============== ===================== ============================================ From bad1963697ab8d6538368a3e86f7848bfe8c63ea Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Mon, 29 Mar 2021 22:29:35 +0200 Subject: [PATCH 222/630] fix #8361: address review/quality comments --- doc/en/deprecations.rst | 14 ++++++++++++++ src/_pytest/config/compat.py | 19 ++++++++++++++++--- src/_pytest/deprecated.py | 4 +++- 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/doc/en/deprecations.rst b/doc/en/deprecations.rst index 6ecb37b385a..d6b108d5012 100644 --- a/doc/en/deprecations.rst +++ b/doc/en/deprecations.rst @@ -19,6 +19,20 @@ Below is a complete list of all pytest features which are considered deprecated. :class:`PytestWarning` or subclasses, which can be filtered using :ref:`standard warning filters `. +``py.path.local`` arguments for hooks replaced with ``pathlib.Path`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In order to support the transition oto pathlib, the following hooks added additional arugments: + +* ``pytest_ignore_collect(fspath: pathlib.Path)`` +* ``pytest_collect_file(fspath: pathlib.Path)`` +* ``pytest_pycollect_makemodule(fspath: pathlib.Path)`` +* ``pytest_report_header(startpath: pathlib.Path)`` +* ``pytest_report_collectionfinish(startpath: pathlib.Path)`` + +The accompanying ``py.path.local`` based paths have been deprecated. + + ``Node.fspath`` in favor of ``pathlib`` and ``Node.path`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/src/_pytest/config/compat.py b/src/_pytest/config/compat.py index 82572a97090..d9e6e280cee 100644 --- a/src/_pytest/config/compat.py +++ b/src/_pytest/config/compat.py @@ -1,3 +1,4 @@ +import functools import warnings from pathlib import Path from typing import Optional @@ -17,20 +18,31 @@ class PathAwareHookProxy: + """ + this helper wraps around hook callers + until pluggy supports fixingcalls, this one will do + + it currently doesnt return full hook caller proxies for fixed hooks, + this may have to be changed later depending on bugs + """ + def __init__(self, hook_caller): self.__hook_caller = hook_caller def __dir__(self): return dir(self.__hook_caller) - def __getattr__(self, key): + def __getattr__(self, key, _wraps=functools.wraps): + hook = getattr(self.__hook_caller, key) if key not in imply_paths_hooks: - return getattr(self.__hook_caller, key) + self.__dict__[key] = hook + return hook else: - hook = getattr(self.__hook_caller, key) path_var, fspath_var = imply_paths_hooks[key] + @_wraps(hook) def fixed_hook(**kw): + path_value: Optional[Path] = kw.pop(path_var, None) fspath_value: Optional[LEGACY_PATH] = kw.pop(fspath_var, None) if fspath_value is not None: @@ -45,4 +57,5 @@ def fixed_hook(**kw): return hook(**kw) fixed_hook.__name__ = key + self.__dict__[key] = fixed_hook return fixed_hook diff --git a/src/_pytest/deprecated.py b/src/_pytest/deprecated.py index 9ac4d81b442..7a09d516362 100644 --- a/src/_pytest/deprecated.py +++ b/src/_pytest/deprecated.py @@ -97,7 +97,9 @@ HOOK_LEGACY_PATH_ARG = UnformattedWarning( PytestDeprecationWarning, - "{pylib_path_arg} : py.path.local is deprecated, please use {pathlib_path_arg} : pathlib.Path", + "The ({pylib_path_arg}: py.path.local) argument is deprecated, please use ({pathlib_path_arg}: pathlib.Path)\n" + "see https://docs.pytest.org/en/latest/deprecations.html" + "#py-path-local-arguments-for-hooks-replaced-with-pathlib-path", ) # You want to make some `__init__` or function "private". # From b96e229c955cf22ebd4af50b0afb2289b8cd74c9 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 1 Apr 2021 10:12:46 -0300 Subject: [PATCH 223/630] Fix plugin-list label in the docs (#8505) Last time I "fixed" this I left a `\` at the start of the string to avoid an initial newline, but didn't realize it was a raw string. This should fix it now for good. --- doc/en/reference/plugin_list.rst | 2 +- scripts/update-plugin-list.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/en/reference/plugin_list.rst b/doc/en/reference/plugin_list.rst index 40c782cf688..8f47f5ba7aa 100644 --- a/doc/en/reference/plugin_list.rst +++ b/doc/en/reference/plugin_list.rst @@ -1,4 +1,4 @@ -\ + .. _plugin-list: Plugin List diff --git a/scripts/update-plugin-list.py b/scripts/update-plugin-list.py index 4e00f1a4536..4337e8f58c4 100644 --- a/scripts/update-plugin-list.py +++ b/scripts/update-plugin-list.py @@ -6,7 +6,7 @@ import requests import tabulate -FILE_HEAD = r"""\ +FILE_HEAD = r""" .. _plugin-list: Plugin List From 0061ec5555de0298566d1aadc14c26851ba4b9c0 Mon Sep 17 00:00:00 2001 From: Mehera Emrich <73163184+meemrich@users.noreply.github.com> Date: Thu, 1 Apr 2021 15:13:12 +0200 Subject: [PATCH 224/630] Fix typo (#8504) --- doc/en/example/simple.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/example/simple.rst b/doc/en/example/simple.rst index 759f308e2b7..61a5faf77e7 100644 --- a/doc/en/example/simple.rst +++ b/doc/en/example/simple.rst @@ -621,7 +621,7 @@ Package/Directory-level fixtures (setups) ------------------------------------------------------- If you have nested test directories, you can have per-directory fixture scopes -by placing fixture functions in a ``conftest.py`` file in that directory +by placing fixture functions in a ``conftest.py`` file in that directory. You can use all types of fixtures including :ref:`autouse fixtures ` which are the equivalent of xUnit's setup/teardown concept. It's however recommended to have explicit fixture references in your From 778d2b2499e78759997b3e717f1f07a344288c97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Thu, 1 Apr 2021 17:21:45 +0200 Subject: [PATCH 225/630] monkeypatch.syspath_prepend: Skip fixup_namespace_packages if pkg_resources not imported Calling pkg_resources.fixup_namespace_packages() is only needed for packages that use pkg_resources.declare_namespace() and hence they already imported pkg_resources. When pkg_resources is not imported, we don't need to use it. This avoids an unneeded runtime dependency on setuptools. The code is tested by test_syspath_prepend_with_namespace_packages, behavior should remain unchanged, hence no new test was added. When people drop pkg_resources from sys.modules, they are on their own. If someone has a actual use case making this valid to support, they can come in and provide a test, a reference and a fix. --- changelog/8503.trivial.rst | 4 ++++ src/_pytest/monkeypatch.py | 7 +++++-- 2 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 changelog/8503.trivial.rst diff --git a/changelog/8503.trivial.rst b/changelog/8503.trivial.rst new file mode 100644 index 00000000000..26f660bbf3f --- /dev/null +++ b/changelog/8503.trivial.rst @@ -0,0 +1,4 @@ +:meth:`pytest.MonkeyPatch.syspath_prepend` no longer fails when +``setuptools`` is not installed. +It now only calls :func:`pkg_resources.fixup_namespace_packages` if +``pkg_resources`` was previously imported, because it is not needed otherwise. diff --git a/src/_pytest/monkeypatch.py b/src/_pytest/monkeypatch.py index 2c432065625..708b25aa073 100644 --- a/src/_pytest/monkeypatch.py +++ b/src/_pytest/monkeypatch.py @@ -312,14 +312,17 @@ def delenv(self, name: str, raising: bool = True) -> None: def syspath_prepend(self, path) -> None: """Prepend ``path`` to ``sys.path`` list of import locations.""" - from pkg_resources import fixup_namespace_packages if self._savesyspath is None: self._savesyspath = sys.path[:] sys.path.insert(0, str(path)) # https://github.com/pypa/setuptools/blob/d8b901bc/docs/pkg_resources.txt#L162-L171 - fixup_namespace_packages(str(path)) + # this is only needed when pkg_resources was already loaded by the namespace package + if "pkg_resources" in sys.modules: + from pkg_resources import fixup_namespace_packages + + fixup_namespace_packages(str(path)) # A call to syspathinsert() usually means that the caller wants to # import some dynamically created files, thus with python3 we From deb5b5bd963cb1c283053d4ea703de555c162c3c Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Fri, 2 Apr 2021 00:58:05 +0200 Subject: [PATCH 226/630] fix #8371: fix stack-level --- src/_pytest/config/compat.py | 3 ++- testing/deprecated_test.py | 20 +++++++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/_pytest/config/compat.py b/src/_pytest/config/compat.py index d9e6e280cee..c93f3738d4f 100644 --- a/src/_pytest/config/compat.py +++ b/src/_pytest/config/compat.py @@ -49,7 +49,8 @@ def fixed_hook(**kw): warnings.warn( HOOK_LEGACY_PATH_ARG.format( pylib_path_arg=fspath_var, pathlib_path_arg=path_var - ) + ), + stacklevel=2, ) path_value, fspath_value = _imply_path(path_value, fspath_value) kw[path_var] = path_value diff --git a/testing/deprecated_test.py b/testing/deprecated_test.py index b4e73dd5b50..0c84ab422a1 100644 --- a/testing/deprecated_test.py +++ b/testing/deprecated_test.py @@ -160,20 +160,22 @@ def test_raising_unittest_skiptest_during_collection_is_deprecated( def test_hookproxy_warnings_for_fspath(pytestconfig, tmp_path, request): path = legacy_path(tmp_path) - with pytest.warns( - PytestDeprecationWarning, - match="path : py.path.local is deprecated, please use fspath : pathlib.Path", - ): + PATH_WARN_MATCH = r".*path: py\.path\.local\) argument is deprecated, please use \(fspath: pathlib\.Path.*" + + with pytest.warns(PytestDeprecationWarning, match=PATH_WARN_MATCH) as r: pytestconfig.hook.pytest_ignore_collect( config=pytestconfig, path=path, fspath=tmp_path ) - with pytest.warns( - PytestDeprecationWarning, - match="path : py.path.local is deprecated, please use fspath : pathlib.Path", - ): + (record,) = r + assert record.filename == __file__ + assert record.lineno == 166 + + with pytest.warns(PytestDeprecationWarning, match=PATH_WARN_MATCH) as r: request.node.ihook.pytest_ignore_collect( config=pytestconfig, path=path, fspath=tmp_path ) - + (record,) = r + assert record.filename == __file__ + assert record.lineno == 174 pytestconfig.hook.pytest_ignore_collect(config=pytestconfig, fspath=tmp_path) request.node.ihook.pytest_ignore_collect(config=pytestconfig, fspath=tmp_path) From 53ebe34ca2ede967d8636fe0632081319ea80ea9 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Sat, 3 Apr 2021 12:46:50 +0200 Subject: [PATCH 227/630] Apply suggestions from code review Co-authored-by: Bruno Oliveira --- doc/en/deprecations.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/en/deprecations.rst b/doc/en/deprecations.rst index d6b108d5012..db04a48b369 100644 --- a/doc/en/deprecations.rst +++ b/doc/en/deprecations.rst @@ -22,15 +22,15 @@ Below is a complete list of all pytest features which are considered deprecated. ``py.path.local`` arguments for hooks replaced with ``pathlib.Path`` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -In order to support the transition oto pathlib, the following hooks added additional arugments: +In order to support the transition to :mod:`pathlib`, the following hooks now receive additional arguments: -* ``pytest_ignore_collect(fspath: pathlib.Path)`` -* ``pytest_collect_file(fspath: pathlib.Path)`` -* ``pytest_pycollect_makemodule(fspath: pathlib.Path)`` -* ``pytest_report_header(startpath: pathlib.Path)`` -* ``pytest_report_collectionfinish(startpath: pathlib.Path)`` +* :func:`pytest_ignore_collect(fspath: pathlib.Path) <_pytest.hookspec.pytest_ignore_collect>` +* :func:`pytest_collect_file(fspath: pathlib.Path) <_pytest.hookspec.pytest_collect_file>` +* :func:`pytest_pycollect_makemodule(fspath: pathlib.Path) <_pytest.hookspec.pytest_pycollect_makemodule>` +* :func:`pytest_report_header(startpath: pathlib.Path) <_pytest.hookspec.pytest_report_header>` +* :func:`pytest_report_collectionfinish(startpath: pathlib.Path) <_pytest.hookspec.pytest_report_collectionfinish>` -The accompanying ``py.path.local`` based paths have been deprecated. +The accompanying ``py.path.local`` based paths have been deprecated: plugins which manually invoke those hooks should only pass the new ``pathlib.Path`` arguments, and users should change their hook implementations to use the new ``pathlib.Path`` arguments. ``Node.fspath`` in favor of ``pathlib`` and ``Node.path`` From aa10bff750b0468b6187aa3eb7f84553fc07597b Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Sat, 3 Apr 2021 14:51:36 +0200 Subject: [PATCH 228/630] fix deprecation test for path/fspath hook args --- testing/deprecated_test.py | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/testing/deprecated_test.py b/testing/deprecated_test.py index 0c84ab422a1..1d012adf250 100644 --- a/testing/deprecated_test.py +++ b/testing/deprecated_test.py @@ -1,4 +1,5 @@ import re +import sys import warnings from unittest import mock @@ -157,25 +158,23 @@ def test_raising_unittest_skiptest_during_collection_is_deprecated( ) -def test_hookproxy_warnings_for_fspath(pytestconfig, tmp_path, request): +@pytest.mark.parametrize("hooktype", ["hook", "ihook"]) +def test_hookproxy_warnings_for_fspath(tmp_path, hooktype, request): path = legacy_path(tmp_path) PATH_WARN_MATCH = r".*path: py\.path\.local\) argument is deprecated, please use \(fspath: pathlib\.Path.*" + if hooktype == "ihook": + hooks = request.node.ihook + else: + hooks = request.config.hook with pytest.warns(PytestDeprecationWarning, match=PATH_WARN_MATCH) as r: - pytestconfig.hook.pytest_ignore_collect( - config=pytestconfig, path=path, fspath=tmp_path - ) - (record,) = r - assert record.filename == __file__ - assert record.lineno == 166 + l1 = sys._getframe().f_lineno + hooks.pytest_ignore_collect(config=request.config, path=path, fspath=tmp_path) + l2 = sys._getframe().f_lineno - with pytest.warns(PytestDeprecationWarning, match=PATH_WARN_MATCH) as r: - request.node.ihook.pytest_ignore_collect( - config=pytestconfig, path=path, fspath=tmp_path - ) (record,) = r assert record.filename == __file__ - assert record.lineno == 174 - pytestconfig.hook.pytest_ignore_collect(config=pytestconfig, fspath=tmp_path) - request.node.ihook.pytest_ignore_collect(config=pytestconfig, fspath=tmp_path) + assert l1 < record.lineno < l2 + + hooks.pytest_ignore_collect(config=request.config, fspath=tmp_path) From 78122a530401d039a0e5aefe4d0e4b938da0c4d6 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sat, 6 Mar 2021 16:40:42 +0200 Subject: [PATCH 229/630] pathlib: remove useless temporary variable --- src/_pytest/tmpdir.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/_pytest/tmpdir.py b/src/_pytest/tmpdir.py index 99b54e9bf81..c22d4b65f58 100644 --- a/src/_pytest/tmpdir.py +++ b/src/_pytest/tmpdir.py @@ -126,9 +126,9 @@ def getbasetemp(self) -> Path: prefix="pytest-", root=rootdir, keep=3, lock_timeout=LOCK_TIMEOUT ) assert basetemp is not None, basetemp - self._basetemp = t = basetemp - self._trace("new basetemp", t) - return t + self._basetemp = basetemp + self._trace("new basetemp", basetemp) + return basetemp @final From 0dd1e5b4f43bf3c38111410a330fce91b9304b50 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sat, 6 Mar 2021 16:22:59 +0200 Subject: [PATCH 230/630] pathlib: inline ensure_reset_dir() This is only used in TempPathFactory.getbasetemp(). We'll be wanting further control/care there, so move it into there. --- src/_pytest/pathlib.py | 7 ------- src/_pytest/tmpdir.py | 6 ++++-- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/_pytest/pathlib.py b/src/_pytest/pathlib.py index 63764b341e0..eaae0908305 100644 --- a/src/_pytest/pathlib.py +++ b/src/_pytest/pathlib.py @@ -62,13 +62,6 @@ def get_lock_path(path: _AnyPurePath) -> _AnyPurePath: return path.joinpath(".lock") -def ensure_reset_dir(path: Path) -> None: - """Ensure the given path is an empty directory.""" - if path.exists(): - rm_rf(path) - path.mkdir() - - def on_rm_rf_error(func, path: str, exc, *, start_path: Path) -> bool: """Handle known read-only errors during rmtree. diff --git a/src/_pytest/tmpdir.py b/src/_pytest/tmpdir.py index c22d4b65f58..309754fddb1 100644 --- a/src/_pytest/tmpdir.py +++ b/src/_pytest/tmpdir.py @@ -7,10 +7,10 @@ import attr -from .pathlib import ensure_reset_dir from .pathlib import LOCK_TIMEOUT from .pathlib import make_numbered_dir from .pathlib import make_numbered_dir_with_cleanup +from .pathlib import rm_rf from _pytest.compat import final from _pytest.compat import LEGACY_PATH from _pytest.compat import legacy_path @@ -107,7 +107,9 @@ def getbasetemp(self) -> Path: if self._given_basetemp is not None: basetemp = self._given_basetemp - ensure_reset_dir(basetemp) + if basetemp.exists(): + rm_rf(basetemp) + basetemp.mkdir() basetemp = basetemp.resolve() else: from_env = os.environ.get("PYTEST_DEBUG_TEMPROOT") From 1278f8b97e28b8d8058b57de241636d4225e0a52 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sat, 6 Mar 2021 17:01:29 +0200 Subject: [PATCH 231/630] tmpdir: fix temporary directories created with world-readable permissions (Written for a Unix system, but might be applicable to Windows as well). pytest creates a root temporary directory under /tmp, named `pytest-of-`, and creates tmp_path's and other under it. /tmp is shared between all users of the system. This root temporary directory was created with 0o777&~umask permissions, which usually becomes 0o755, meaning any user in the system could list and read the files, which is undesirable. Use 0o700 permissions instead. Also for subdirectories, because the root dir is adjustable. --- changelog/8414.bugfix.rst | 5 +++++ src/_pytest/pathlib.py | 12 ++++++++---- src/_pytest/pytester.py | 4 ++-- src/_pytest/tmpdir.py | 18 +++++++++++------- testing/test_tmpdir.py | 16 ++++++++++++++++ 5 files changed, 42 insertions(+), 13 deletions(-) create mode 100644 changelog/8414.bugfix.rst diff --git a/changelog/8414.bugfix.rst b/changelog/8414.bugfix.rst new file mode 100644 index 00000000000..a8b791ae210 --- /dev/null +++ b/changelog/8414.bugfix.rst @@ -0,0 +1,5 @@ +pytest used to create directories under ``/tmp`` with world-readable +permissions. This means that any user in the system was able to read +information written by tests in temporary directories (such as those created by +the ``tmp_path``/``tmpdir`` fixture). Now the directories are created with +private permissions. diff --git a/src/_pytest/pathlib.py b/src/_pytest/pathlib.py index eaae0908305..e86811f1250 100644 --- a/src/_pytest/pathlib.py +++ b/src/_pytest/pathlib.py @@ -205,7 +205,7 @@ def _force_symlink( pass -def make_numbered_dir(root: Path, prefix: str) -> Path: +def make_numbered_dir(root: Path, prefix: str, mode: int = 0o700) -> Path: """Create a directory with an increased number as suffix for the given prefix.""" for i in range(10): # try up to 10 times to create the folder @@ -213,7 +213,7 @@ def make_numbered_dir(root: Path, prefix: str) -> Path: new_number = max_existing + 1 new_path = root.joinpath(f"{prefix}{new_number}") try: - new_path.mkdir() + new_path.mkdir(mode=mode) except Exception: pass else: @@ -345,13 +345,17 @@ def cleanup_numbered_dir( def make_numbered_dir_with_cleanup( - root: Path, prefix: str, keep: int, lock_timeout: float + root: Path, + prefix: str, + keep: int, + lock_timeout: float, + mode: int, ) -> Path: """Create a numbered dir with a cleanup lock and remove old ones.""" e = None for i in range(10): try: - p = make_numbered_dir(root, prefix) + p = make_numbered_dir(root, prefix, mode) lock_path = create_cleanup_lock(p) register_cleanup_lock_removal(lock_path) except Exception as exc: diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index 968a5365134..5a29c8eaeec 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -1456,7 +1456,7 @@ def runpytest_subprocess( :py:class:`Pytester.TimeoutExpired`. """ __tracebackhide__ = True - p = make_numbered_dir(root=self.path, prefix="runpytest-") + p = make_numbered_dir(root=self.path, prefix="runpytest-", mode=0o700) args = ("--basetemp=%s" % p,) + args plugins = [x for x in self.plugins if isinstance(x, str)] if plugins: @@ -1475,7 +1475,7 @@ def spawn_pytest( The pexpect child is returned. """ basetemp = self.path / "temp-pexpect" - basetemp.mkdir() + basetemp.mkdir(mode=0o700) invoke = " ".join(map(str, self._getpytestargs())) cmd = f"{invoke} --basetemp={basetemp} {string}" return self.spawn(cmd, expect_timeout=expect_timeout) diff --git a/src/_pytest/tmpdir.py b/src/_pytest/tmpdir.py index 309754fddb1..275ec73341a 100644 --- a/src/_pytest/tmpdir.py +++ b/src/_pytest/tmpdir.py @@ -94,14 +94,14 @@ def mktemp(self, basename: str, numbered: bool = True) -> Path: basename = self._ensure_relative_to_basetemp(basename) if not numbered: p = self.getbasetemp().joinpath(basename) - p.mkdir() + p.mkdir(mode=0o700) else: - p = make_numbered_dir(root=self.getbasetemp(), prefix=basename) + p = make_numbered_dir(root=self.getbasetemp(), prefix=basename, mode=0o700) self._trace("mktemp", p) return p def getbasetemp(self) -> Path: - """Return base temporary directory.""" + """Return the base temporary directory, creating it if needed.""" if self._basetemp is not None: return self._basetemp @@ -109,7 +109,7 @@ def getbasetemp(self) -> Path: basetemp = self._given_basetemp if basetemp.exists(): rm_rf(basetemp) - basetemp.mkdir() + basetemp.mkdir(mode=0o700) basetemp = basetemp.resolve() else: from_env = os.environ.get("PYTEST_DEBUG_TEMPROOT") @@ -119,13 +119,17 @@ def getbasetemp(self) -> Path: # make_numbered_dir() call rootdir = temproot.joinpath(f"pytest-of-{user}") try: - rootdir.mkdir(exist_ok=True) + rootdir.mkdir(mode=0o700, exist_ok=True) except OSError: # getuser() likely returned illegal characters for the platform, use unknown back off mechanism rootdir = temproot.joinpath("pytest-of-unknown") - rootdir.mkdir(exist_ok=True) + rootdir.mkdir(mode=0o700, exist_ok=True) basetemp = make_numbered_dir_with_cleanup( - prefix="pytest-", root=rootdir, keep=3, lock_timeout=LOCK_TIMEOUT + prefix="pytest-", + root=rootdir, + keep=3, + lock_timeout=LOCK_TIMEOUT, + mode=0o700, ) assert basetemp is not None, basetemp self._basetemp = basetemp diff --git a/testing/test_tmpdir.py b/testing/test_tmpdir.py index 63450eab248..2ae23ffcaad 100644 --- a/testing/test_tmpdir.py +++ b/testing/test_tmpdir.py @@ -454,3 +454,19 @@ def test_tmp_path_factory_handles_invalid_dir_characters( monkeypatch.setattr(tmp_path_factory, "_given_basetemp", None) p = tmp_path_factory.getbasetemp() assert "pytest-of-unknown" in str(p) + + +@pytest.mark.skipif(not hasattr(os, "getuid"), reason="checks unix permissions") +def test_tmp_path_factory_create_directory_with_safe_permissions( + tmp_path: Path, monkeypatch: MonkeyPatch +) -> None: + """Verify that pytest creates directories under /tmp with private permissions.""" + # Use the test's tmp_path as the system temproot (/tmp). + monkeypatch.setenv("PYTEST_DEBUG_TEMPROOT", str(tmp_path)) + tmp_factory = TempPathFactory(None, lambda *args: None, _ispytest=True) + basetemp = tmp_factory.getbasetemp() + + # No world-readable permissions. + assert (basetemp.stat().st_mode & 0o077) == 0 + # Parent too (pytest-of-foo). + assert (basetemp.parent.stat().st_mode & 0o077) == 0 From c49100cef8073c5de117199d17d632cfd8cb11c1 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sat, 6 Mar 2021 23:17:46 +0200 Subject: [PATCH 232/630] tmpdir: prevent using a non-private root temp directory pytest uses a root temp directory named `/tmp/pytest-of-`. The name is predictable, and the directory might already exists from a previous run, so that's allowed. This makes it possible for my_user to pre-create `/tmp/pytest-of-another_user`, thus giving my_user control of another_user's tempdir. Prevent this scenario by adding a couple of safety checks. I believe they are sufficient. Testing the first check requires changing the owner, which requires root permissions, so can't be unit-tested easily, but I checked it manually. --- changelog/8414.bugfix.rst | 5 +++++ src/_pytest/tmpdir.py | 19 +++++++++++++++++++ testing/test_tmpdir.py | 25 +++++++++++++++++++++++++ 3 files changed, 49 insertions(+) diff --git a/changelog/8414.bugfix.rst b/changelog/8414.bugfix.rst index a8b791ae210..73c3068a839 100644 --- a/changelog/8414.bugfix.rst +++ b/changelog/8414.bugfix.rst @@ -3,3 +3,8 @@ permissions. This means that any user in the system was able to read information written by tests in temporary directories (such as those created by the ``tmp_path``/``tmpdir`` fixture). Now the directories are created with private permissions. + +pytest used silenty use a pre-existing ``/tmp/pytest-of-`` directory, +even if owned by another user. This means another user could pre-create such a +directory and gain control of another user's temporary directory. Now such a +condition results in an error. diff --git a/src/_pytest/tmpdir.py b/src/_pytest/tmpdir.py index 275ec73341a..dcc37dc262f 100644 --- a/src/_pytest/tmpdir.py +++ b/src/_pytest/tmpdir.py @@ -124,6 +124,25 @@ def getbasetemp(self) -> Path: # getuser() likely returned illegal characters for the platform, use unknown back off mechanism rootdir = temproot.joinpath("pytest-of-unknown") rootdir.mkdir(mode=0o700, exist_ok=True) + # Because we use exist_ok=True with a predictable name, make sure + # we are the owners, to prevent any funny business (on unix, where + # temproot is usually shared). + # Also, to keep things private, fixup any world-readable temp + # rootdir's permissions. Historically 0o755 was used, so we can't + # just error out on this, at least for a while. + if hasattr(os, "getuid"): + rootdir_stat = rootdir.stat() + uid = os.getuid() + # getuid shouldn't fail, but cpython defines such a case. + # Let's hope for the best. + if uid != -1: + if rootdir_stat.st_uid != uid: + raise OSError( + f"The temporary directory {rootdir} is not owned by the current user. " + "Fix this and try again." + ) + if (rootdir_stat.st_mode & 0o077) != 0: + os.chmod(rootdir, rootdir_stat.st_mode & ~0o077) basetemp = make_numbered_dir_with_cleanup( prefix="pytest-", root=rootdir, diff --git a/testing/test_tmpdir.py b/testing/test_tmpdir.py index 2ae23ffcaad..40e75663cb7 100644 --- a/testing/test_tmpdir.py +++ b/testing/test_tmpdir.py @@ -470,3 +470,28 @@ def test_tmp_path_factory_create_directory_with_safe_permissions( assert (basetemp.stat().st_mode & 0o077) == 0 # Parent too (pytest-of-foo). assert (basetemp.parent.stat().st_mode & 0o077) == 0 + + +@pytest.mark.skipif(not hasattr(os, "getuid"), reason="checks unix permissions") +def test_tmp_path_factory_fixes_up_world_readable_permissions( + tmp_path: Path, monkeypatch: MonkeyPatch +) -> None: + """Verify that if a /tmp/pytest-of-foo directory already exists with + world-readable permissions, it is fixed. + + pytest used to mkdir with such permissions, that's why we fix it up. + """ + # Use the test's tmp_path as the system temproot (/tmp). + monkeypatch.setenv("PYTEST_DEBUG_TEMPROOT", str(tmp_path)) + tmp_factory = TempPathFactory(None, lambda *args: None, _ispytest=True) + basetemp = tmp_factory.getbasetemp() + + # Before - simulate bad perms. + os.chmod(basetemp.parent, 0o777) + assert (basetemp.parent.stat().st_mode & 0o077) != 0 + + tmp_factory = TempPathFactory(None, lambda *args: None, _ispytest=True) + basetemp = tmp_factory.getbasetemp() + + # After - fixed. + assert (basetemp.parent.stat().st_mode & 0o077) == 0 From 5d2fad53629d1d81efc16dda7df638c1b1a42cad Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sun, 4 Apr 2021 00:43:05 +0300 Subject: [PATCH 233/630] Merge pull request #8519 from pytest-dev/release-6.2.3 Prepare release 6.2.3 (cherry picked from commit 724e22cb003031b88ae6fe50fa3eabdcc538a8bc) --- changelog/8414.bugfix.rst | 10 ---------- doc/en/announce/index.rst | 1 + doc/en/announce/release-6.2.3.rst | 19 +++++++++++++++++++ doc/en/changelog.rst | 18 ++++++++++++++++++ doc/en/example/parametrize.rst | 9 +++++---- doc/en/getting-started.rst | 2 +- 6 files changed, 44 insertions(+), 15 deletions(-) delete mode 100644 changelog/8414.bugfix.rst create mode 100644 doc/en/announce/release-6.2.3.rst diff --git a/changelog/8414.bugfix.rst b/changelog/8414.bugfix.rst deleted file mode 100644 index 73c3068a839..00000000000 --- a/changelog/8414.bugfix.rst +++ /dev/null @@ -1,10 +0,0 @@ -pytest used to create directories under ``/tmp`` with world-readable -permissions. This means that any user in the system was able to read -information written by tests in temporary directories (such as those created by -the ``tmp_path``/``tmpdir`` fixture). Now the directories are created with -private permissions. - -pytest used silenty use a pre-existing ``/tmp/pytest-of-`` directory, -even if owned by another user. This means another user could pre-create such a -directory and gain control of another user's temporary directory. Now such a -condition results in an error. diff --git a/doc/en/announce/index.rst b/doc/en/announce/index.rst index a7656c5ee26..f9720eab920 100644 --- a/doc/en/announce/index.rst +++ b/doc/en/announce/index.rst @@ -6,6 +6,7 @@ Release announcements :maxdepth: 2 + release-6.2.3 release-6.2.2 release-6.2.1 release-6.2.0 diff --git a/doc/en/announce/release-6.2.3.rst b/doc/en/announce/release-6.2.3.rst new file mode 100644 index 00000000000..e45aa6a03e3 --- /dev/null +++ b/doc/en/announce/release-6.2.3.rst @@ -0,0 +1,19 @@ +pytest-6.2.3 +======================================= + +pytest 6.2.3 has just been released to PyPI. + +This is a bug-fix release, being a drop-in replacement. To upgrade:: + + pip install --upgrade pytest + +The full changelog is available at https://docs.pytest.org/en/stable/changelog.html. + +Thanks to all of the contributors to this release: + +* Bruno Oliveira +* Ran Benita + + +Happy testing, +The pytest Development Team diff --git a/doc/en/changelog.rst b/doc/en/changelog.rst index 967fca2098a..04f081b77d8 100644 --- a/doc/en/changelog.rst +++ b/doc/en/changelog.rst @@ -28,6 +28,24 @@ with advance notice in the **Deprecations** section of releases. .. towncrier release notes start +pytest 6.2.3 (2021-04-03) +========================= + +Bug Fixes +--------- + +- `#8414 `_: pytest used to create directories under ``/tmp`` with world-readable + permissions. This means that any user in the system was able to read + information written by tests in temporary directories (such as those created by + the ``tmp_path``/``tmpdir`` fixture). Now the directories are created with + private permissions. + + pytest used silenty use a pre-existing ``/tmp/pytest-of-`` directory, + even if owned by another user. This means another user could pre-create such a + directory and gain control of another user's temporary directory. Now such a + condition results in an error. + + pytest 6.2.2 (2021-01-25) ========================= diff --git a/doc/en/example/parametrize.rst b/doc/en/example/parametrize.rst index 771c7e16f28..f358fe50091 100644 --- a/doc/en/example/parametrize.rst +++ b/doc/en/example/parametrize.rst @@ -508,11 +508,12 @@ Running it results in some skips if we don't have all the python interpreters in .. code-block:: pytest . $ pytest -rs -q multipython.py - ssssssssssss...ssssssssssss [100%] + sssssssssssssssssssssssssss [100%] ========================= short test summary info ========================== - SKIPPED [12] multipython.py:29: 'python3.5' not found - SKIPPED [12] multipython.py:29: 'python3.7' not found - 3 passed, 24 skipped in 0.12s + SKIPPED [9] multipython.py:29: 'python3.5' not found + SKIPPED [9] multipython.py:29: 'python3.6' not found + SKIPPED [9] multipython.py:29: 'python3.7' not found + 27 skipped in 0.12s Indirect parametrization of optional implementations/imports -------------------------------------------------------------------- diff --git a/doc/en/getting-started.rst b/doc/en/getting-started.rst index f8fd994b596..5f2f419e64f 100644 --- a/doc/en/getting-started.rst +++ b/doc/en/getting-started.rst @@ -22,7 +22,7 @@ Install ``pytest`` .. code-block:: bash $ pytest --version - pytest 6.2.2 + pytest 6.2.3 .. _`simpletest`: From f3337c18547a06f17feb655c5eb3f8b4e2bd1126 Mon Sep 17 00:00:00 2001 From: pytest bot Date: Sun, 4 Apr 2021 00:19:23 +0000 Subject: [PATCH 234/630] [automated] Update plugin list --- doc/en/reference/plugin_list.rst | 55 +++++++++++++++++--------------- 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/doc/en/reference/plugin_list.rst b/doc/en/reference/plugin_list.rst index 8f47f5ba7aa..1ce661c4e97 100644 --- a/doc/en/reference/plugin_list.rst +++ b/doc/en/reference/plugin_list.rst @@ -6,11 +6,11 @@ Plugin List PyPI projects that match "pytest-\*" are considered plugins and are listed automatically. Packages classified as inactive are excluded. -This list contains 847 plugins. +This list contains 850 plugins. -============================================================================================================== ======================================================================================================================================================================== ============== ===================== ============================================ +============================================================================================================== ======================================================================================================================================================================== ============== ===================== ================================================ name summary last release status requires -============================================================================================================== ======================================================================================================================================================================== ============== ===================== ============================================ +============================================================================================================== ======================================================================================================================================================================== ============== ===================== ================================================ `pytest-adaptavist `_ pytest plugin for generating test execution results within Jira Test Management (tm4j) Feb 05, 2020 N/A pytest (>=3.4.1) `pytest-adf `_ Pytest plugin for writing Azure Data Factory integration tests Mar 10, 2021 4 - Beta pytest (>=3.5.0) `pytest-adf-azure-identity `_ Pytest plugin for writing Azure Data Factory integration tests Mar 06, 2021 4 - Beta pytest (>=3.5.0) @@ -45,7 +45,7 @@ name `pytest-asgi-server `_ Convenient ASGI client/server fixtures for Pytest Dec 12, 2020 N/A pytest (>=5.4.1) `pytest-asptest `_ test Answer Set Programming programs Apr 28, 2018 4 - Beta N/A `pytest-assertutil `_ pytest-assertutil May 10, 2019 N/A N/A -`pytest-assert-utils `_ Useful assertion utilities for use with pytest Aug 25, 2020 3 - Alpha N/A +`pytest-assert-utils `_ Useful assertion utilities for use with pytest Mar 28, 2021 3 - Alpha N/A `pytest-assume `_ A pytest plugin that allows multiple failures per test Dec 08, 2020 N/A pytest (>=2.7) `pytest-ast-back-to-python `_ A plugin for pytest devs to view how assertion rewriting recodes the AST Sep 29, 2019 4 - Beta N/A `pytest-astropy `_ Meta-package containing dependencies for testing Jan 16, 2020 5 - Production/Stable pytest (>=4.6) @@ -102,7 +102,7 @@ name `pytest-canonical-data `_ A plugin which allows to compare results with canonical results, based on previous runs May 08, 2020 2 - Pre-Alpha pytest (>=3.5.0) `pytest-caprng `_ A plugin that replays pRNG state on failure. May 02, 2018 4 - Beta N/A `pytest-capture-deprecatedwarnings `_ pytest plugin to capture all deprecatedwarnings and put them in one file Apr 30, 2019 N/A N/A -`pytest-cases `_ Separate test code from test cases in pytest. Mar 24, 2021 5 - Production/Stable N/A +`pytest-cases `_ Separate test code from test cases in pytest. Mar 31, 2021 5 - Production/Stable N/A `pytest-cassandra `_ Cassandra CCM Test Fixtures for pytest Nov 04, 2017 1 - Planning N/A `pytest-catchlog `_ py.test plugin to catch log messages. This is a fork of pytest-capturelog. Jan 24, 2016 4 - Beta pytest (>=2.6) `pytest-catch-server `_ Pytest plugin with server for catching HTTP requests. Dec 12, 2019 5 - Production/Stable N/A @@ -111,7 +111,7 @@ name `pytest-change-report `_ turn . into √,turn F into x Sep 14, 2020 N/A pytest `pytest-chdir `_ A pytest fixture for changing current working directory Jan 28, 2020 N/A pytest (>=5.0.0,<6.0.0) `pytest-check `_ A pytest plugin that allows multiple failures per test. Dec 27, 2020 5 - Production/Stable N/A -`pytest-checkdocs `_ check the README when running tests Feb 27, 2021 5 - Production/Stable pytest (>=4.6) ; extra == 'testing' +`pytest-checkdocs `_ check the README when running tests Mar 28, 2021 5 - Production/Stable pytest (>=4.6) ; extra == 'testing' `pytest-checkipdb `_ plugin to check if there are ipdb debugs left Jul 22, 2020 5 - Production/Stable pytest (>=2.9.2) `pytest-check-links `_ Check links in files Jul 29, 2020 N/A pytest (>=4.6) `pytest-check-mk `_ pytest plugin to test Check_MK checks Nov 19, 2015 4 - Beta pytest @@ -123,12 +123,12 @@ name `pytest-click `_ Py.test plugin for Click Aug 29, 2020 5 - Production/Stable pytest (>=5.0) `pytest-clld `_ May 06, 2020 N/A pytest (>=3.6) `pytest-cloud `_ Distributed tests planner plugin for pytest testing framework. Oct 05, 2020 6 - Mature N/A -`pytest-cloudflare-worker `_ pytest plugin for testing cloudflare workers Oct 19, 2020 4 - Beta pytest (>=6.0.0) +`pytest-cloudflare-worker `_ pytest plugin for testing cloudflare workers Mar 30, 2021 4 - Beta pytest (>=6.0.0) `pytest-cobra `_ PyTest plugin for testing Smart Contracts for Ethereum blockchain. Jun 29, 2019 3 - Alpha pytest (<4.0.0,>=3.7.1) `pytest-codecheckers `_ pytest plugin to add source code sanity checks (pep8 and friends) Feb 13, 2010 N/A N/A `pytest-codegen `_ Automatically create pytest test signatures Aug 23, 2020 2 - Pre-Alpha N/A `pytest-codestyle `_ pytest plugin to run pycodestyle Mar 23, 2020 3 - Alpha N/A -`pytest-collect-formatter `_ Formatter for pytest collect output Nov 19, 2020 5 - Production/Stable N/A +`pytest-collect-formatter `_ Formatter for pytest collect output Mar 29, 2021 5 - Production/Stable N/A `pytest-colordots `_ Colorizes the progress indicators Oct 06, 2017 5 - Production/Stable N/A `pytest-commander `_ An interactive GUI test runner for PyTest Jan 31, 2021 N/A pytest (>=5.0.0) `pytest-common-subject `_ pytest framework for testing different aspects of a common method Nov 12, 2020 N/A pytest (>=3.6,<7) @@ -179,7 +179,7 @@ name `pytest-dbt-adapter `_ A pytest plugin for testing dbt adapter plugins Jan 07, 2021 N/A pytest (<7,>=6) `pytest-dbus-notification `_ D-BUS notifications for pytest results. Mar 05, 2014 5 - Production/Stable N/A `pytest-deadfixtures `_ A simple plugin to list unused fixtures in pytest Jul 23, 2020 5 - Production/Stable N/A -`pytest-deepcov `_ deepcov Mar 23, 2021 N/A N/A +`pytest-deepcov `_ deepcov Mar 30, 2021 N/A N/A `pytest-dependency `_ Manage dependencies of tests Feb 14, 2020 4 - Beta N/A `pytest-depends `_ Tests that depend on other tests Apr 05, 2020 5 - Production/Stable pytest (>=3) `pytest-deprecate `_ Mark tests as testing a deprecated feature with a warning note. Jul 01, 2019 N/A N/A @@ -246,6 +246,7 @@ name `pytest-easy-api `_ Simple API testing with pytest Mar 26, 2018 N/A N/A `pytest-easyMPI `_ Package that supports mpi tests in pytest Oct 21, 2020 N/A N/A `pytest-easyread `_ pytest plugin that makes terminal printouts of the reports easier to read Nov 17, 2017 N/A N/A +`pytest-easy-server `_ Pytest plugin for easy testing against servers Apr 03, 2021 3 - Alpha pytest (<5.0.0,>=4.3.1) ; python_version < "3.5" `pytest-ec2 `_ Pytest execution on EC2 instance Oct 22, 2019 3 - Alpha N/A `pytest-echo `_ pytest plugin with mechanisms for echoing environment variables, package version and generic attributes Jan 08, 2020 5 - Production/Stable N/A `pytest-elasticsearch `_ Elasticsearch process and client fixtures for py.test. Feb 19, 2020 5 - Production/Stable pytest (>=3.0.0) @@ -290,7 +291,7 @@ name `pytest-fantasy `_ Pytest plugin for Flask Fantasy Framework Mar 14, 2019 N/A N/A `pytest-fastapi `_ Dec 27, 2020 N/A N/A `pytest-fastest `_ Use SCM and coverage to run only needed tests Mar 05, 2020 N/A N/A -`pytest-fast-first `_ Pytest plugin that runs fast tests first Mar 18, 2021 3 - Alpha pytest +`pytest-fast-first `_ Pytest plugin that runs fast tests first Apr 02, 2021 3 - Alpha pytest `pytest-faulthandler `_ py.test plugin that activates the fault handler module for tests (dummy package) Jul 04, 2019 6 - Mature pytest (>=5.0) `pytest-fauxfactory `_ Integration of fauxfactory into pytest. Dec 06, 2017 5 - Production/Stable pytest (>=3.2) `pytest-figleaf `_ py.test figleaf coverage plugin Jan 18, 2010 5 - Production/Stable N/A @@ -328,7 +329,7 @@ name `pytest-gevent `_ Ensure that gevent is properly patched when invoking pytest Feb 25, 2020 N/A pytest `pytest-gherkin `_ A flexible framework for executing BDD gherkin tests Jul 27, 2019 3 - Alpha pytest (>=5.0.0) `pytest-ghostinspector `_ For finding/executing Ghost Inspector tests May 17, 2016 3 - Alpha N/A -`pytest-girder `_ A set of pytest fixtures for testing Girder applications. Mar 19, 2021 N/A N/A +`pytest-girder `_ A set of pytest fixtures for testing Girder applications. Mar 29, 2021 N/A N/A `pytest-git `_ Git repository fixture for py.test May 28, 2019 5 - Production/Stable pytest `pytest-gitcov `_ Pytest plugin for reporting on coverage of the last git commit. Jan 11, 2020 2 - Pre-Alpha N/A `pytest-git-fixtures `_ Pytest fixtures for testing with git. Mar 11, 2021 4 - Beta pytest @@ -342,7 +343,7 @@ name `pytest-growl `_ Growl notifications for pytest results. Jan 13, 2014 5 - Production/Stable N/A `pytest-grpc `_ pytest plugin for grpc May 01, 2020 N/A pytest (>=3.6.0) `pytest-hammertime `_ Display "🔨 " instead of "." for passed pytest tests. Jul 28, 2018 N/A pytest -`pytest-harvest `_ Store data created during your pytest tests execution, and retrieve it at the end of the session, e.g. for applicative benchmarking purposes. Mar 22, 2021 5 - Production/Stable N/A +`pytest-harvest `_ Store data created during your pytest tests execution, and retrieve it at the end of the session, e.g. for applicative benchmarking purposes. Apr 01, 2021 5 - Production/Stable N/A `pytest-helm-chart `_ A plugin to provide different types and configs of Kubernetes clusters that can be used for testing. Jun 15, 2020 4 - Beta pytest (>=5.4.2,<6.0.0) `pytest-helm-charts `_ A plugin to provide different types and configs of Kubernetes clusters that can be used for testing. Dec 22, 2020 4 - Beta pytest (>=6.1.2,<7.0.0) `pytest-helper `_ Functions to help in using the pytest testing framework May 31, 2019 5 - Production/Stable N/A @@ -369,6 +370,7 @@ name `pytest-httpx `_ Send responses to httpx. Mar 01, 2021 5 - Production/Stable pytest (==6.*) `pytest-httpx-blockage `_ Disable httpx requests during a test run Mar 20, 2021 N/A pytest (>=6.2.2) `pytest-hue `_ Visualise PyTest status via your Phillips Hue lights May 09, 2019 N/A N/A +`pytest-hylang `_ Pytest plugin to allow running tests written in hylang Mar 28, 2021 N/A pytest `pytest-hypo-25 `_ help hypo module for pytest Jan 12, 2020 3 - Alpha N/A `pytest-ibutsu `_ A plugin to sent pytest results to an Ibutsu server Mar 09, 2021 4 - Beta pytest `pytest-icdiff `_ use icdiff for better error messages in pytest assertions Apr 08, 2020 4 - Beta N/A @@ -383,7 +385,7 @@ name `pytest-inmanta `_ A py.test plugin providing fixtures to simplify inmanta modules testing. Mar 26, 2021 5 - Production/Stable N/A `pytest-inmanta-extensions `_ Inmanta tests package Mar 17, 2021 5 - Production/Stable N/A `pytest-Inomaly `_ A simple image diff plugin for pytest Feb 13, 2018 4 - Beta N/A -`pytest-insta `_ A practical snapshot testing plugin for pytest Mar 03, 2021 N/A pytest (>=6.0.2,<7.0.0) +`pytest-insta `_ A practical snapshot testing plugin for pytest Apr 02, 2021 N/A pytest (>=6.0.2,<7.0.0) `pytest-instafail `_ pytest plugin to show failures instantly Jun 14, 2020 4 - Beta pytest (>=2.9) `pytest-instrument `_ pytest plugin to instrument tests Apr 05, 2020 5 - Production/Stable pytest (>=5.1.0) `pytest-integration `_ Organizing pytests by integration or not Apr 16, 2020 N/A N/A @@ -420,7 +422,7 @@ name `pytest-level `_ Select tests of a given level or lower Oct 21, 2019 N/A pytest `pytest-libfaketime `_ A python-libfaketime plugin for pytest. Dec 22, 2018 4 - Beta pytest (>=3.0.0) `pytest-libiio `_ A pytest plugin to manage interfacing with libiio contexts Jan 09, 2021 4 - Beta N/A -`pytest-libnotify `_ Pytest plugin that shows notifications about the test run Nov 12, 2018 3 - Alpha pytest +`pytest-libnotify `_ Pytest plugin that shows notifications about the test run Apr 02, 2021 3 - Alpha pytest `pytest-ligo `_ Jan 16, 2020 4 - Beta N/A `pytest-lineno `_ A pytest plugin to show the line numbers of test functions Dec 04, 2020 N/A pytest `pytest-lisa `_ Pytest plugin for organizing tests. Jan 21, 2021 3 - Alpha pytest (>=6.1.2,<7.0.0) @@ -518,7 +520,7 @@ name `pytest-oot `_ Run object-oriented tests in a simple format Sep 18, 2016 4 - Beta N/A `pytest-openfiles `_ Pytest plugin for detecting inadvertent open file handles Apr 16, 2020 3 - Alpha pytest (>=4.6) `pytest-opentmi `_ pytest plugin for publish results to opentmi Feb 26, 2021 5 - Production/Stable pytest (>=5.0) -`pytest-operator `_ Fixtures for Operators Mar 16, 2021 N/A N/A +`pytest-operator `_ Fixtures for Operators Mar 29, 2021 N/A N/A `pytest-optional `_ include/exclude values of fixtures in pytest Oct 07, 2015 N/A N/A `pytest-optional-tests `_ Easy declaration of optional tests (i.e., that are not run by default) Jul 09, 2019 4 - Beta pytest (>=4.5.0) `pytest-orchestration `_ A pytest plugin for orchestrating tests Jul 18, 2019 N/A N/A @@ -542,7 +544,7 @@ name `pytest-pep8 `_ pytest plugin to check PEP8 requirements Apr 27, 2014 N/A N/A `pytest-percent `_ Change the exit code of pytest test sessions when a required percent of tests pass. May 21, 2020 N/A pytest (>=5.2.0) `pytest-performance `_ A simple plugin to ensure the execution of critical sections of code has not been impacted Sep 11, 2020 5 - Production/Stable pytest (>=3.7.0) -`pytest-persistence `_ Pytest tool for persistent objects Mar 26, 2021 N/A N/A +`pytest-persistence `_ Pytest tool for persistent objects Mar 28, 2021 N/A N/A `pytest-pgsql `_ Pytest plugins and helpers for tests using a Postgres database. May 13, 2020 5 - Production/Stable pytest (>=3.0.0) `pytest-picked `_ Run the tests related to the changed files Dec 23, 2020 N/A pytest (>=3.5.0) `pytest-pigeonhole `_ Jun 25, 2018 5 - Production/Stable pytest (>=3.4) @@ -555,7 +557,7 @@ name `pytest-platform-markers `_ Markers for pytest to skip tests on specific platforms Sep 09, 2019 4 - Beta pytest (>=3.6.0) `pytest-play `_ pytest plugin that let you automate actions and assertions with test metrics reporting executing plain YAML files Jun 12, 2019 5 - Production/Stable N/A `pytest-playbook `_ Pytest plugin for reading playbooks. Jan 21, 2021 3 - Alpha pytest (>=6.1.2,<7.0.0) -`pytest-playwright `_ A pytest wrapper with fixtures for Playwright to automate web browsers Feb 25, 2021 N/A pytest +`pytest-playwright `_ A pytest wrapper with fixtures for Playwright to automate web browsers Apr 01, 2021 N/A pytest `pytest-plt `_ Fixtures for quickly making Matplotlib plots in tests Aug 17, 2020 5 - Production/Stable pytest `pytest-plugin-helpers `_ A plugin to help developing and testing other plugins Nov 23, 2019 4 - Beta pytest (>=3.5.0) `pytest-plus `_ PyTest Plus Plugin :: extends pytest functionality Mar 19, 2020 5 - Production/Stable pytest (>=3.50) @@ -606,7 +608,7 @@ name `pytest-raisesregexp `_ Simple pytest plugin to look for regex in Exceptions Dec 18, 2015 N/A N/A `pytest-raisin `_ Plugin enabling the use of exception instances with pytest.raises Jun 25, 2020 N/A pytest `pytest-random `_ py.test plugin to randomize tests Apr 28, 2013 3 - Alpha N/A -`pytest-randomly `_ Pytest plugin to randomly order tests and control random.seed. Nov 16, 2020 5 - Production/Stable pytest +`pytest-randomly `_ Pytest plugin to randomly order tests and control random.seed. Apr 01, 2021 5 - Production/Stable pytest `pytest-randomness `_ Pytest plugin about random seed management May 30, 2019 3 - Alpha N/A `pytest-random-num `_ Randomise the order in which pytest tests are run with some control over the randomness Oct 19, 2020 5 - Production/Stable N/A `pytest-random-order `_ Randomise the order in which pytest tests are run with some control over the randomness Nov 30, 2018 5 - Production/Stable pytest (>=3.0.0) @@ -656,8 +658,8 @@ name `pytest-rotest `_ Pytest integration with rotest Sep 08, 2019 N/A pytest (>=3.5.0) `pytest-rpc `_ Extend py.test for RPC OpenStack testing. Feb 22, 2019 4 - Beta pytest (~=3.6) `pytest-rt `_ pytest data collector plugin for Testgr Mar 03, 2021 N/A N/A -`pytest-rts `_ Coverage-based regression test selection (RTS) plugin for pytest Mar 24, 2021 N/A pytest -`pytest-run-changed `_ Pytest plugin that runs changed tests only Mar 25, 2021 3 - Alpha pytest +`pytest-rts `_ Coverage-based regression test selection (RTS) plugin for pytest Mar 31, 2021 N/A pytest +`pytest-run-changed `_ Pytest plugin that runs changed tests only Apr 02, 2021 3 - Alpha pytest `pytest-runfailed `_ implement a --failed option for pytest Mar 24, 2016 N/A N/A `pytest-runner `_ Invoke py.test as distutils command with dependency resolution Feb 12, 2021 5 - Production/Stable pytest (!=3.7.3,>=3.5) ; extra == 'testing' `pytest-salt `_ Pytest Salt Plugin Jan 27, 2020 4 - Beta N/A @@ -668,13 +670,13 @@ name `pytest-sanic `_ a pytest plugin for Sanic Feb 27, 2021 N/A pytest (>=5.2) `pytest-sanity `_ Dec 07, 2020 N/A N/A `pytest-sa-pg `_ May 14, 2019 N/A N/A -`pytest-sbase `_ A complete web automation framework for end-to-end testing. Mar 26, 2021 5 - Production/Stable N/A +`pytest-sbase `_ A complete web automation framework for end-to-end testing. Apr 03, 2021 5 - Production/Stable N/A `pytest-scenario `_ pytest plugin for test scenarios Feb 06, 2017 3 - Alpha N/A `pytest-schema `_ 👍 Validate return values against a schema-like object in testing Aug 31, 2020 5 - Production/Stable pytest (>=3.5.0) `pytest-securestore `_ An encrypted password store for use within pytest cases Jun 19, 2019 4 - Beta N/A `pytest-select `_ A pytest plugin which allows to (de-)select tests from a file. Jan 18, 2019 3 - Alpha pytest (>=3.0) `pytest-selenium `_ pytest plugin for Selenium Sep 19, 2020 5 - Production/Stable pytest (>=5.0.0) -`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Mar 26, 2021 5 - Production/Stable N/A +`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Apr 03, 2021 5 - Production/Stable N/A `pytest-selenium-enhancer `_ pytest plugin for Selenium Nov 26, 2020 5 - Production/Stable N/A `pytest-selenium-pdiff `_ A pytest package implementing perceptualdiff for Selenium tests. Apr 06, 2017 2 - Pre-Alpha N/A `pytest-send-email `_ Send pytest execution result email Dec 04, 2019 N/A N/A @@ -705,7 +707,7 @@ name `pytest-snapci `_ py.test plugin for Snap-CI Nov 12, 2015 N/A N/A `pytest-snapshot `_ A plugin to enable snapshot testing with pytest. Jan 22, 2021 4 - Beta pytest (>=3.0.0) `pytest-snmpserver `_ Sep 14, 2020 N/A N/A -`pytest-socket `_ Pytest Plugin to disable socket calls during tests May 31, 2020 4 - Beta pytest (>=3.6.3) +`pytest-socket `_ Pytest Plugin to disable socket calls during tests Mar 30, 2021 4 - Beta pytest (>=3.6.3) `pytest-soft-assertions `_ May 05, 2020 3 - Alpha pytest `pytest-solr `_ Solr process and client fixtures for py.test. May 11, 2020 3 - Alpha pytest (>=3.0.0) `pytest-sorter `_ A simple plugin to first execute tests that historically failed more Jul 23, 2020 4 - Beta pytest (>=3.1.1) @@ -796,7 +798,7 @@ name `pytest-tornado-yen3 `_ A py.test plugin providing fixtures and markers to simplify testing of asynchronous tornado applications. Oct 15, 2018 5 - Production/Stable N/A `pytest-tornasync `_ py.test plugin for testing Python 3.5+ Tornado code Jul 15, 2019 3 - Alpha pytest (>=3.0) `pytest-track `_ Feb 26, 2021 3 - Alpha pytest (>=3.0) -`pytest-translations `_ Test your translation files. Oct 26, 2020 5 - Production/Stable N/A +`pytest-translations `_ Test your translation files. Mar 30, 2021 5 - Production/Stable N/A `pytest-travis-fold `_ Folds captured output sections in Travis CI build log Nov 29, 2017 4 - Beta pytest (>=2.6.0) `pytest-trello `_ Plugin for py.test that integrates trello using markers Nov 20, 2015 5 - Production/Stable N/A `pytest-trepan `_ Pytest plugin for trepan debugger. Jul 28, 2018 5 - Production/Stable N/A @@ -812,7 +814,7 @@ name `pytest-unhandled-exception-exit-code `_ Plugin for py.test set a different exit code on uncaught exceptions Jun 22, 2020 5 - Production/Stable pytest (>=2.3) `pytest-unittest-filter `_ A pytest plugin for filtering unittest-based test classes Jan 12, 2019 4 - Beta pytest (>=3.1.0) `pytest-unmarked `_ Run only unmarked tests Aug 27, 2019 5 - Production/Stable N/A -`pytest-unordered `_ Test equality of unordered collections in pytest Nov 02, 2020 4 - Beta pytest (>=6.0.0) +`pytest-unordered `_ Test equality of unordered collections in pytest Mar 28, 2021 4 - Beta N/A `pytest-vagrant `_ A py.test plugin providing access to vagrant. Mar 23, 2020 5 - Production/Stable pytest `pytest-valgrind `_ Mar 15, 2020 N/A N/A `pytest-variables `_ pytest plugin for providing variables to tests/fixtures Oct 23, 2019 5 - Production/Stable pytest (>=2.4.2) @@ -857,5 +859,6 @@ name `pytest-yuk `_ Display tests you are uneasy with, using 🤢/🤮 for pass/fail of tests marked with yuk. Mar 26, 2021 N/A N/A `pytest-zafira `_ A Zafira plugin for pytest Sep 18, 2019 5 - Production/Stable pytest (==4.1.1) `pytest-zap `_ OWASP ZAP plugin for py.test. May 12, 2014 4 - Beta N/A +`pytest-zebrunner `_ Pytest connector for Zebrunner reporting Mar 29, 2021 4 - Beta pytest (>=6.1.1,<7.0.0) `pytest-zigzag `_ Extend py.test for RPC OpenStack testing. Feb 27, 2019 4 - Beta pytest (~=3.6) -============================================================================================================== ======================================================================================================================================================================== ============== ===================== ============================================ +============================================================================================================== ======================================================================================================================================================================== ============== ===================== ================================================ From 0b299044d10222a2e22f7ded0bd62db78914fb83 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 5 Apr 2021 17:10:40 +0000 Subject: [PATCH 235/630] [pre-commit.ci] pre-commit autoupdate --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e78bab46271..0ba551ee6ff 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,7 +20,7 @@ repos: - id: debug-statements exclude: _pytest/(debugging|hookspec).py language_version: python3 -- repo: https://gitlab.com/pycqa/flake8 +- repo: https://github.com/PyCQA/flake8 rev: 3.9.0 hooks: - id: flake8 From b706a2c04840a8057610f41071fbcf3da1290eb5 Mon Sep 17 00:00:00 2001 From: Tadeu Manoel Date: Mon, 5 Apr 2021 17:10:03 -0300 Subject: [PATCH 236/630] Fix error with --import-mode=importlib and modules containing dataclasses or pickle (#7870) Co-authored-by: Bruno Oliveira Fixes #7856, fixes #7859 --- AUTHORS | 1 + changelog/7856.feature.rst | 2 + doc/en/explanation/goodpractices.rst | 2 +- doc/en/explanation/pythonpath.rst | 19 ++- src/_pytest/config/__init__.py | 39 +++--- src/_pytest/doctest.py | 6 +- src/_pytest/main.py | 10 +- src/_pytest/pathlib.py | 52 ++++++- src/_pytest/python.py | 2 +- testing/code/test_excinfo.py | 4 +- testing/code/test_source.py | 2 +- testing/python/fixtures.py | 6 +- testing/test_config.py | 10 +- testing/test_conftest.py | 78 ++++++++--- testing/test_pathlib.py | 201 ++++++++++++++++++++++----- testing/test_pluginmanager.py | 20 ++- 16 files changed, 348 insertions(+), 106 deletions(-) create mode 100644 changelog/7856.feature.rst diff --git a/AUTHORS b/AUTHORS index e5477e1f1a5..46f283452c3 100644 --- a/AUTHORS +++ b/AUTHORS @@ -98,6 +98,7 @@ Dominic Mortlock Duncan Betts Edison Gustavo Muenz Edoardo Batini +Edson Tadeu M. Manoel Eduardo Schettino Eli Boyarski Elizaveta Shashkova diff --git a/changelog/7856.feature.rst b/changelog/7856.feature.rst new file mode 100644 index 00000000000..22ed4c83bc3 --- /dev/null +++ b/changelog/7856.feature.rst @@ -0,0 +1,2 @@ +:ref:`--import-mode=importlib ` now works with features that +depend on modules being on :py:data:`sys.modules`, such as :mod:`pickle` and :mod:`dataclasses`. diff --git a/doc/en/explanation/goodpractices.rst b/doc/en/explanation/goodpractices.rst index 77c176182fc..fe5db9fd1eb 100644 --- a/doc/en/explanation/goodpractices.rst +++ b/doc/en/explanation/goodpractices.rst @@ -151,7 +151,7 @@ This layout prevents a lot of common pitfalls and has many benefits, which are b .. note:: The new ``--import-mode=importlib`` (see :ref:`import-modes`) doesn't have - any of the drawbacks above because ``sys.path`` and ``sys.modules`` are not changed when importing + any of the drawbacks above because ``sys.path`` is not changed when importing test modules, so users that run into this issue are strongly encouraged to try it and report if the new option works well for them. diff --git a/doc/en/explanation/pythonpath.rst b/doc/en/explanation/pythonpath.rst index 6aac3bc86f2..9aef44618fc 100644 --- a/doc/en/explanation/pythonpath.rst +++ b/doc/en/explanation/pythonpath.rst @@ -16,14 +16,14 @@ import process can be controlled through the ``--import-mode`` command-line flag these values: * ``prepend`` (default): the directory path containing each module will be inserted into the *beginning* - of ``sys.path`` if not already there, and then imported with the `__import__ `__ builtin. + of :py:data:`sys.path` if not already there, and then imported with the `__import__ `__ builtin. This requires test module names to be unique when the test directory tree is not arranged in - packages, because the modules will put in ``sys.modules`` after importing. + packages, because the modules will put in :py:data:`sys.modules` after importing. This is the classic mechanism, dating back from the time Python 2 was still supported. -* ``append``: the directory containing each module is appended to the end of ``sys.path`` if not already +* ``append``: the directory containing each module is appended to the end of :py:data:`sys.path` if not already there, and imported with ``__import__``. This better allows to run test modules against installed versions of a package even if the @@ -41,17 +41,14 @@ these values: we advocate for using :ref:`src ` layouts. Same as ``prepend``, requires test module names to be unique when the test directory tree is - not arranged in packages, because the modules will put in ``sys.modules`` after importing. + not arranged in packages, because the modules will put in :py:data:`sys.modules` after importing. -* ``importlib``: new in pytest-6.0, this mode uses `importlib `__ to import test modules. This gives full control over the import process, and doesn't require - changing ``sys.path`` or ``sys.modules`` at all. +* ``importlib``: new in pytest-6.0, this mode uses `importlib `__ to import test modules. This gives full control over the import process, and doesn't require changing :py:data:`sys.path`. - For this reason this doesn't require test module names to be unique at all, but also makes test - modules non-importable by each other. This was made possible in previous modes, for tests not residing - in Python packages, because of the side-effects of changing ``sys.path`` and ``sys.modules`` - mentioned above. Users which require this should turn their tests into proper packages instead. + For this reason this doesn't require test module names to be unique, but also makes test + modules non-importable by each other. - We intend to make ``importlib`` the default in future releases. + We intend to make ``importlib`` the default in future releases, depending on feedback. ``prepend`` and ``append`` import modes scenarios ------------------------------------------------- diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 12e73859d29..23fa3e3d5db 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -481,7 +481,9 @@ def pytest_configure(self, config: "Config") -> None: # # Internal API for local conftest plugin handling. # - def _set_initial_conftests(self, namespace: argparse.Namespace) -> None: + def _set_initial_conftests( + self, namespace: argparse.Namespace, rootpath: Path + ) -> None: """Load initial conftest files given a preparsed "namespace". As conftest files may add their own command line options which have @@ -507,26 +509,24 @@ def _set_initial_conftests(self, namespace: argparse.Namespace) -> None: path = path[:i] anchor = absolutepath(current / path) if anchor.exists(): # we found some file object - self._try_load_conftest(anchor, namespace.importmode) + self._try_load_conftest(anchor, namespace.importmode, rootpath) foundanchor = True if not foundanchor: - self._try_load_conftest(current, namespace.importmode) + self._try_load_conftest(current, namespace.importmode, rootpath) def _try_load_conftest( - self, anchor: Path, importmode: Union[str, ImportMode] + self, anchor: Path, importmode: Union[str, ImportMode], rootpath: Path ) -> None: - self._getconftestmodules(anchor, importmode) + self._getconftestmodules(anchor, importmode, rootpath) # let's also consider test* subdirs if anchor.is_dir(): for x in anchor.glob("test*"): if x.is_dir(): - self._getconftestmodules(x, importmode) + self._getconftestmodules(x, importmode, rootpath) @lru_cache(maxsize=128) def _getconftestmodules( - self, - path: Path, - importmode: Union[str, ImportMode], + self, path: Path, importmode: Union[str, ImportMode], rootpath: Path ) -> List[types.ModuleType]: if self._noconftest: return [] @@ -545,7 +545,7 @@ def _getconftestmodules( continue conftestpath = parent / "conftest.py" if conftestpath.is_file(): - mod = self._importconftest(conftestpath, importmode) + mod = self._importconftest(conftestpath, importmode, rootpath) clist.append(mod) self._dirpath2confmods[directory] = clist return clist @@ -555,8 +555,9 @@ def _rget_with_confmod( name: str, path: Path, importmode: Union[str, ImportMode], + rootpath: Path, ) -> Tuple[types.ModuleType, Any]: - modules = self._getconftestmodules(path, importmode) + modules = self._getconftestmodules(path, importmode, rootpath=rootpath) for mod in reversed(modules): try: return mod, getattr(mod, name) @@ -565,9 +566,7 @@ def _rget_with_confmod( raise KeyError(name) def _importconftest( - self, - conftestpath: Path, - importmode: Union[str, ImportMode], + self, conftestpath: Path, importmode: Union[str, ImportMode], rootpath: Path ) -> types.ModuleType: # Use a resolved Path object as key to avoid loading the same conftest # twice with build systems that create build directories containing @@ -584,7 +583,7 @@ def _importconftest( _ensure_removed_sysmodule(conftestpath.stem) try: - mod = import_path(conftestpath, mode=importmode) + mod = import_path(conftestpath, mode=importmode, root=rootpath) except Exception as e: assert e.__traceback__ is not None exc_info = (type(e), e, e.__traceback__) @@ -1086,7 +1085,9 @@ def _processopt(self, opt: "Argument") -> None: @hookimpl(trylast=True) def pytest_load_initial_conftests(self, early_config: "Config") -> None: - self.pluginmanager._set_initial_conftests(early_config.known_args_namespace) + self.pluginmanager._set_initial_conftests( + early_config.known_args_namespace, rootpath=early_config.rootpath + ) def _initini(self, args: Sequence[str]) -> None: ns, unknown_args = self._parser.parse_known_and_unknown_args( @@ -1437,10 +1438,12 @@ def _getini(self, name: str): assert type in [None, "string"] return value - def _getconftest_pathlist(self, name: str, path: Path) -> Optional[List[Path]]: + def _getconftest_pathlist( + self, name: str, path: Path, rootpath: Path + ) -> Optional[List[Path]]: try: mod, relroots = self.pluginmanager._rget_with_confmod( - name, path, self.getoption("importmode") + name, path, self.getoption("importmode"), rootpath ) except KeyError: return None diff --git a/src/_pytest/doctest.py b/src/_pytest/doctest.py index 5eaeccc4766..9b877cfd9b2 100644 --- a/src/_pytest/doctest.py +++ b/src/_pytest/doctest.py @@ -534,11 +534,13 @@ def _find( if self.path.name == "conftest.py": module = self.config.pluginmanager._importconftest( - self.path, self.config.getoption("importmode") + self.path, + self.config.getoption("importmode"), + rootpath=self.config.rootpath, ) else: try: - module = import_path(self.path) + module = import_path(self.path, root=self.config.rootpath) except ImportError: if self.config.getvalue("doctest_ignore_import_errors"): pytest.skip("unable to import module %r" % self.path) diff --git a/src/_pytest/main.py b/src/_pytest/main.py index 06cfb1fd54f..dda6c557e02 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -378,7 +378,9 @@ def _in_venv(path: Path) -> bool: def pytest_ignore_collect(fspath: Path, config: Config) -> Optional[bool]: - ignore_paths = config._getconftest_pathlist("collect_ignore", path=fspath.parent) + ignore_paths = config._getconftest_pathlist( + "collect_ignore", path=fspath.parent, rootpath=config.rootpath + ) ignore_paths = ignore_paths or [] excludeopt = config.getoption("ignore") if excludeopt: @@ -388,7 +390,7 @@ def pytest_ignore_collect(fspath: Path, config: Config) -> Optional[bool]: return True ignore_globs = config._getconftest_pathlist( - "collect_ignore_glob", path=fspath.parent + "collect_ignore_glob", path=fspath.parent, rootpath=config.rootpath ) ignore_globs = ignore_globs or [] excludeglobopt = config.getoption("ignore_glob") @@ -546,7 +548,9 @@ def gethookproxy(self, fspath: "os.PathLike[str]"): # hooks with all conftest.py files. pm = self.config.pluginmanager my_conftestmodules = pm._getconftestmodules( - Path(fspath), self.config.getoption("importmode") + Path(fspath), + self.config.getoption("importmode"), + rootpath=self.config.rootpath, ) remove_mods = pm._conftest_plugins.difference(my_conftestmodules) if remove_mods: diff --git a/src/_pytest/pathlib.py b/src/_pytest/pathlib.py index e86811f1250..a0aa800f7ee 100644 --- a/src/_pytest/pathlib.py +++ b/src/_pytest/pathlib.py @@ -23,6 +23,7 @@ from posixpath import sep as posix_sep from types import ModuleType from typing import Callable +from typing import Dict from typing import Iterable from typing import Iterator from typing import Optional @@ -454,6 +455,7 @@ def import_path( p: Union[str, "os.PathLike[str]"], *, mode: Union[str, ImportMode] = ImportMode.prepend, + root: Path, ) -> ModuleType: """Import and return a module from the given path, which can be a file (a module) or a directory (a package). @@ -471,6 +473,11 @@ def import_path( to import the module, which avoids having to use `__import__` and muck with `sys.path` at all. It effectively allows having same-named test modules in different places. + :param root: + Used as an anchor when mode == ImportMode.importlib to obtain + a unique name for the module being imported so it can safely be stored + into ``sys.modules``. + :raises ImportPathMismatchError: If after importing the given `path` and the module `__file__` are different. Only raised in `prepend` and `append` modes. @@ -483,7 +490,7 @@ def import_path( raise ImportError(path) if mode is ImportMode.importlib: - module_name = path.stem + module_name = module_name_from_path(path, root) for meta_importer in sys.meta_path: spec = meta_importer.find_spec(module_name, [str(path.parent)]) @@ -497,7 +504,9 @@ def import_path( "Can't find module {} at location {}".format(module_name, str(path)) ) mod = importlib.util.module_from_spec(spec) + sys.modules[module_name] = mod spec.loader.exec_module(mod) # type: ignore[union-attr] + insert_missing_modules(sys.modules, module_name) return mod pkg_path = resolve_package_path(path) @@ -562,6 +571,47 @@ def _is_same(f1: str, f2: str) -> bool: return os.path.samefile(f1, f2) +def module_name_from_path(path: Path, root: Path) -> str: + """ + Return a dotted module name based on the given path, anchored on root. + + For example: path="projects/src/tests/test_foo.py" and root="/projects", the + resulting module name will be "src.tests.test_foo". + """ + path = path.with_suffix("") + try: + relative_path = path.relative_to(root) + except ValueError: + # If we can't get a relative path to root, use the full path, except + # for the first part ("d:\\" or "/" depending on the platform, for example). + path_parts = path.parts[1:] + else: + # Use the parts for the relative path to the root path. + path_parts = relative_path.parts + + return ".".join(path_parts) + + +def insert_missing_modules(modules: Dict[str, ModuleType], module_name: str) -> None: + """ + Used by ``import_path`` to create intermediate modules when using mode=importlib. + + When we want to import a module as "src.tests.test_foo" for example, we need + to create empty modules "src" and "src.tests" after inserting "src.tests.test_foo", + otherwise "src.tests.test_foo" is not importable by ``__import__``. + """ + module_parts = module_name.split(".") + while module_name: + if module_name not in modules: + module = ModuleType( + module_name, + doc="Empty module created by pytest's importmode=importlib.", + ) + modules[module_name] = module + module_parts.pop(-1) + module_name = ".".join(module_parts) + + def resolve_package_path(path: Path) -> Optional[Path]: """Return the Python package path by looking for the last directory upwards which still contains an __init__.py. diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 04fbb45701b..eec93fc0396 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -577,7 +577,7 @@ def _importtestmodule(self): # We assume we are only called once per module. importmode = self.config.getoption("--import-mode") try: - mod = import_path(self.path, mode=importmode) + mod = import_path(self.path, mode=importmode, root=self.config.rootpath) except SyntaxError as e: raise self.CollectError( ExceptionInfo.from_current().getrepr(style="short") diff --git a/testing/code/test_excinfo.py b/testing/code/test_excinfo.py index e6a9cbaf737..11cf3e60e2d 100644 --- a/testing/code/test_excinfo.py +++ b/testing/code/test_excinfo.py @@ -162,7 +162,7 @@ def test_traceback_cut(self) -> None: def test_traceback_cut_excludepath(self, pytester: Pytester) -> None: p = pytester.makepyfile("def f(): raise ValueError") with pytest.raises(ValueError) as excinfo: - import_path(p).f() # type: ignore[attr-defined] + import_path(p, root=pytester.path).f() # type: ignore[attr-defined] basedir = Path(pytest.__file__).parent newtraceback = excinfo.traceback.cut(excludepath=basedir) for x in newtraceback: @@ -443,7 +443,7 @@ def importasmod(source): tmp_path.joinpath("__init__.py").touch() modpath.write_text(source) importlib.invalidate_caches() - return import_path(modpath) + return import_path(modpath, root=tmp_path) return importasmod diff --git a/testing/code/test_source.py b/testing/code/test_source.py index 5f2c6b1ea54..53e1bb9856b 100644 --- a/testing/code/test_source.py +++ b/testing/code/test_source.py @@ -298,7 +298,7 @@ def method(self): ) path = tmp_path.joinpath("a.py") path.write_text(str(source)) - mod: Any = import_path(path) + mod: Any = import_path(path, root=tmp_path) s2 = Source(mod.A) assert str(source).strip() == str(s2).strip() diff --git a/testing/python/fixtures.py b/testing/python/fixtures.py index 3eb4b80f39e..569b5d67e26 100644 --- a/testing/python/fixtures.py +++ b/testing/python/fixtures.py @@ -2069,9 +2069,9 @@ def test_2(self): reprec = pytester.inline_run("-v", "-s", "--confcutdir", pytester.path) reprec.assertoutcome(passed=8) config = reprec.getcalls("pytest_unconfigure")[0].config - values = config.pluginmanager._getconftestmodules(p, importmode="prepend")[ - 0 - ].values + values = config.pluginmanager._getconftestmodules( + p, importmode="prepend", rootpath=pytester.path + )[0].values assert values == ["fin_a1", "fin_a2", "fin_b1", "fin_b2"] * 2 def test_scope_ordering(self, pytester: Pytester) -> None: diff --git a/testing/test_config.py b/testing/test_config.py index 61fea3643b9..1d1a80aa4ad 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -597,8 +597,14 @@ def test_getconftest_pathlist(self, pytester: Pytester, tmp_path: Path) -> None: p = tmp_path.joinpath("conftest.py") p.write_text(f"pathlist = ['.', {str(somepath)!r}]") config = pytester.parseconfigure(p) - assert config._getconftest_pathlist("notexist", path=tmp_path) is None - pl = config._getconftest_pathlist("pathlist", path=tmp_path) or [] + assert ( + config._getconftest_pathlist("notexist", path=tmp_path, rootpath=tmp_path) + is None + ) + pl = ( + config._getconftest_pathlist("pathlist", path=tmp_path, rootpath=tmp_path) + or [] + ) print(pl) assert len(pl) == 2 assert pl[0] == tmp_path diff --git a/testing/test_conftest.py b/testing/test_conftest.py index 344c9bc5162..3ccdeb96495 100644 --- a/testing/test_conftest.py +++ b/testing/test_conftest.py @@ -35,7 +35,7 @@ def __init__(self) -> None: self.importmode = "prepend" namespace = cast(argparse.Namespace, Namespace()) - conftest._set_initial_conftests(namespace) + conftest._set_initial_conftests(namespace, rootpath=Path(args[0])) @pytest.mark.usefixtures("_sys_snapshot") @@ -57,39 +57,62 @@ def basedir( def test_basic_init(self, basedir: Path) -> None: conftest = PytestPluginManager() p = basedir / "adir" - assert conftest._rget_with_confmod("a", p, importmode="prepend")[1] == 1 + assert ( + conftest._rget_with_confmod("a", p, importmode="prepend", rootpath=basedir)[ + 1 + ] + == 1 + ) def test_immediate_initialiation_and_incremental_are_the_same( self, basedir: Path ) -> None: conftest = PytestPluginManager() assert not len(conftest._dirpath2confmods) - conftest._getconftestmodules(basedir, importmode="prepend") + conftest._getconftestmodules( + basedir, importmode="prepend", rootpath=Path(basedir) + ) snap1 = len(conftest._dirpath2confmods) assert snap1 == 1 - conftest._getconftestmodules(basedir / "adir", importmode="prepend") + conftest._getconftestmodules( + basedir / "adir", importmode="prepend", rootpath=basedir + ) assert len(conftest._dirpath2confmods) == snap1 + 1 - conftest._getconftestmodules(basedir / "b", importmode="prepend") + conftest._getconftestmodules( + basedir / "b", importmode="prepend", rootpath=basedir + ) assert len(conftest._dirpath2confmods) == snap1 + 2 def test_value_access_not_existing(self, basedir: Path) -> None: conftest = ConftestWithSetinitial(basedir) with pytest.raises(KeyError): - conftest._rget_with_confmod("a", basedir, importmode="prepend") + conftest._rget_with_confmod( + "a", basedir, importmode="prepend", rootpath=Path(basedir) + ) def test_value_access_by_path(self, basedir: Path) -> None: conftest = ConftestWithSetinitial(basedir) adir = basedir / "adir" - assert conftest._rget_with_confmod("a", adir, importmode="prepend")[1] == 1 assert ( - conftest._rget_with_confmod("a", adir / "b", importmode="prepend")[1] == 1.5 + conftest._rget_with_confmod( + "a", adir, importmode="prepend", rootpath=basedir + )[1] + == 1 + ) + assert ( + conftest._rget_with_confmod( + "a", adir / "b", importmode="prepend", rootpath=basedir + )[1] + == 1.5 ) def test_value_access_with_confmod(self, basedir: Path) -> None: startdir = basedir / "adir" / "b" startdir.joinpath("xx").mkdir() conftest = ConftestWithSetinitial(startdir) - mod, value = conftest._rget_with_confmod("a", startdir, importmode="prepend") + mod, value = conftest._rget_with_confmod( + "a", startdir, importmode="prepend", rootpath=Path(basedir) + ) assert value == 1.5 path = Path(mod.__file__) assert path.parent == basedir / "adir" / "b" @@ -110,7 +133,9 @@ def test_doubledash_considered(pytester: Pytester) -> None: conf.joinpath("conftest.py").touch() conftest = PytestPluginManager() conftest_setinitial(conftest, [conf.name, conf.name]) - values = conftest._getconftestmodules(conf, importmode="prepend") + values = conftest._getconftestmodules( + conf, importmode="prepend", rootpath=pytester.path + ) assert len(values) == 1 @@ -134,7 +159,7 @@ def test_conftest_global_import(pytester: Pytester) -> None: import pytest from _pytest.config import PytestPluginManager conf = PytestPluginManager() - mod = conf._importconftest(Path("conftest.py"), importmode="prepend") + mod = conf._importconftest(Path("conftest.py"), importmode="prepend", rootpath=Path.cwd()) assert mod.x == 3 import conftest assert conftest is mod, (conftest, mod) @@ -142,7 +167,7 @@ def test_conftest_global_import(pytester: Pytester) -> None: sub.mkdir() subconf = sub / "conftest.py" subconf.write_text("y=4") - mod2 = conf._importconftest(subconf, importmode="prepend") + mod2 = conf._importconftest(subconf, importmode="prepend", rootpath=Path.cwd()) assert mod != mod2 assert mod2.y == 4 import conftest @@ -158,17 +183,25 @@ def test_conftestcutdir(pytester: Pytester) -> None: p = pytester.mkdir("x") conftest = PytestPluginManager() conftest_setinitial(conftest, [pytester.path], confcutdir=p) - values = conftest._getconftestmodules(p, importmode="prepend") + values = conftest._getconftestmodules( + p, importmode="prepend", rootpath=pytester.path + ) assert len(values) == 0 - values = conftest._getconftestmodules(conf.parent, importmode="prepend") + values = conftest._getconftestmodules( + conf.parent, importmode="prepend", rootpath=pytester.path + ) assert len(values) == 0 assert Path(conf) not in conftest._conftestpath2mod # but we can still import a conftest directly - conftest._importconftest(conf, importmode="prepend") - values = conftest._getconftestmodules(conf.parent, importmode="prepend") + conftest._importconftest(conf, importmode="prepend", rootpath=pytester.path) + values = conftest._getconftestmodules( + conf.parent, importmode="prepend", rootpath=pytester.path + ) assert values[0].__file__.startswith(str(conf)) # and all sub paths get updated properly - values = conftest._getconftestmodules(p, importmode="prepend") + values = conftest._getconftestmodules( + p, importmode="prepend", rootpath=pytester.path + ) assert len(values) == 1 assert values[0].__file__.startswith(str(conf)) @@ -177,7 +210,9 @@ def test_conftestcutdir_inplace_considered(pytester: Pytester) -> None: conf = pytester.makeconftest("") conftest = PytestPluginManager() conftest_setinitial(conftest, [conf.parent], confcutdir=conf.parent) - values = conftest._getconftestmodules(conf.parent, importmode="prepend") + values = conftest._getconftestmodules( + conf.parent, importmode="prepend", rootpath=pytester.path + ) assert len(values) == 1 assert values[0].__file__.startswith(str(conf)) @@ -347,13 +382,16 @@ def test_conftest_import_order(pytester: Pytester, monkeypatch: MonkeyPatch) -> ct2 = sub / "conftest.py" ct2.write_text("") - def impct(p, importmode): + def impct(p, importmode, root): return p conftest = PytestPluginManager() conftest._confcutdir = pytester.path monkeypatch.setattr(conftest, "_importconftest", impct) - mods = cast(List[Path], conftest._getconftestmodules(sub, importmode="prepend")) + mods = cast( + List[Path], + conftest._getconftestmodules(sub, importmode="prepend", rootpath=pytester.path), + ) expected = [ct1, ct2] assert mods == expected diff --git a/testing/test_pathlib.py b/testing/test_pathlib.py index 69635e751fc..11db4c5c614 100644 --- a/testing/test_pathlib.py +++ b/testing/test_pathlib.py @@ -5,6 +5,7 @@ from pathlib import Path from textwrap import dedent from types import ModuleType +from typing import Any from typing import Generator import pytest @@ -17,7 +18,9 @@ from _pytest.pathlib import get_lock_path from _pytest.pathlib import import_path from _pytest.pathlib import ImportPathMismatchError +from _pytest.pathlib import insert_missing_modules from _pytest.pathlib import maybe_delete_a_numbered_dir +from _pytest.pathlib import module_name_from_path from _pytest.pathlib import resolve_package_path from _pytest.pathlib import symlink_or_skip from _pytest.pathlib import visit @@ -136,7 +139,7 @@ def setuptestfs(self, path: Path) -> None: ) def test_smoke_test(self, path1: Path) -> None: - obj = import_path(path1 / "execfile.py") + obj = import_path(path1 / "execfile.py", root=path1) assert obj.x == 42 # type: ignore[attr-defined] assert obj.__name__ == "execfile" @@ -146,25 +149,25 @@ def test_renamed_dir_creates_mismatch( tmp_path.joinpath("a").mkdir() p = tmp_path.joinpath("a", "test_x123.py") p.touch() - import_path(p) + import_path(p, root=tmp_path) tmp_path.joinpath("a").rename(tmp_path.joinpath("b")) with pytest.raises(ImportPathMismatchError): - import_path(tmp_path.joinpath("b", "test_x123.py")) + import_path(tmp_path.joinpath("b", "test_x123.py"), root=tmp_path) # Errors can be ignored. monkeypatch.setenv("PY_IGNORE_IMPORTMISMATCH", "1") - import_path(tmp_path.joinpath("b", "test_x123.py")) + import_path(tmp_path.joinpath("b", "test_x123.py"), root=tmp_path) # PY_IGNORE_IMPORTMISMATCH=0 does not ignore error. monkeypatch.setenv("PY_IGNORE_IMPORTMISMATCH", "0") with pytest.raises(ImportPathMismatchError): - import_path(tmp_path.joinpath("b", "test_x123.py")) + import_path(tmp_path.joinpath("b", "test_x123.py"), root=tmp_path) def test_messy_name(self, tmp_path: Path) -> None: # http://bitbucket.org/hpk42/py-trunk/issue/129 path = tmp_path / "foo__init__.py" path.touch() - module = import_path(path) + module = import_path(path, root=tmp_path) assert module.__name__ == "foo__init__" def test_dir(self, tmp_path: Path) -> None: @@ -172,31 +175,31 @@ def test_dir(self, tmp_path: Path) -> None: p.mkdir() p_init = p / "__init__.py" p_init.touch() - m = import_path(p) + m = import_path(p, root=tmp_path) assert m.__name__ == "hello_123" - m = import_path(p_init) + m = import_path(p_init, root=tmp_path) assert m.__name__ == "hello_123" def test_a(self, path1: Path) -> None: otherdir = path1 / "otherdir" - mod = import_path(otherdir / "a.py") + mod = import_path(otherdir / "a.py", root=path1) assert mod.result == "got it" # type: ignore[attr-defined] assert mod.__name__ == "otherdir.a" def test_b(self, path1: Path) -> None: otherdir = path1 / "otherdir" - mod = import_path(otherdir / "b.py") + mod = import_path(otherdir / "b.py", root=path1) assert mod.stuff == "got it" # type: ignore[attr-defined] assert mod.__name__ == "otherdir.b" def test_c(self, path1: Path) -> None: otherdir = path1 / "otherdir" - mod = import_path(otherdir / "c.py") + mod = import_path(otherdir / "c.py", root=path1) assert mod.value == "got it" # type: ignore[attr-defined] def test_d(self, path1: Path) -> None: otherdir = path1 / "otherdir" - mod = import_path(otherdir / "d.py") + mod = import_path(otherdir / "d.py", root=path1) assert mod.value2 == "got it" # type: ignore[attr-defined] def test_import_after(self, tmp_path: Path) -> None: @@ -204,7 +207,7 @@ def test_import_after(self, tmp_path: Path) -> None: tmp_path.joinpath("xxxpackage", "__init__.py").touch() mod1path = tmp_path.joinpath("xxxpackage", "module1.py") mod1path.touch() - mod1 = import_path(mod1path) + mod1 = import_path(mod1path, root=tmp_path) assert mod1.__name__ == "xxxpackage.module1" from xxxpackage import module1 @@ -222,7 +225,7 @@ def test_check_filepath_consistency( pseudopath.touch() mod.__file__ = str(pseudopath) monkeypatch.setitem(sys.modules, name, mod) - newmod = import_path(p) + newmod = import_path(p, root=tmp_path) assert mod == newmod monkeypatch.undo() mod = ModuleType(name) @@ -231,7 +234,7 @@ def test_check_filepath_consistency( mod.__file__ = str(pseudopath) monkeypatch.setitem(sys.modules, name, mod) with pytest.raises(ImportPathMismatchError) as excinfo: - import_path(p) + import_path(p, root=tmp_path) modname, modfile, orig = excinfo.value.args assert modname == name assert modfile == str(pseudopath) @@ -248,8 +251,8 @@ def test_issue131_on__init__(self, tmp_path: Path) -> None: tmp_path.joinpath("sub", "proja").mkdir(parents=True) p2 = tmp_path.joinpath("sub", "proja", "__init__.py") p2.touch() - m1 = import_path(p1) - m2 = import_path(p2) + m1 = import_path(p1, root=tmp_path) + m2 = import_path(p2, root=tmp_path) assert m1 == m2 def test_ensuresyspath_append(self, tmp_path: Path) -> None: @@ -258,44 +261,45 @@ def test_ensuresyspath_append(self, tmp_path: Path) -> None: file1 = root1 / "x123.py" file1.touch() assert str(root1) not in sys.path - import_path(file1, mode="append") + import_path(file1, mode="append", root=tmp_path) assert str(root1) == sys.path[-1] assert str(root1) not in sys.path[:-1] def test_invalid_path(self, tmp_path: Path) -> None: with pytest.raises(ImportError): - import_path(tmp_path / "invalid.py") + import_path(tmp_path / "invalid.py", root=tmp_path) @pytest.fixture def simple_module(self, tmp_path: Path) -> Path: - fn = tmp_path / "mymod.py" - fn.write_text( - dedent( - """ - def foo(x): return 40 + x - """ - ) - ) + fn = tmp_path / "_src/tests/mymod.py" + fn.parent.mkdir(parents=True) + fn.write_text("def foo(x): return 40 + x") return fn - def test_importmode_importlib(self, simple_module: Path) -> None: + def test_importmode_importlib(self, simple_module: Path, tmp_path: Path) -> None: """`importlib` mode does not change sys.path.""" - module = import_path(simple_module, mode="importlib") + module = import_path(simple_module, mode="importlib", root=tmp_path) assert module.foo(2) == 42 # type: ignore[attr-defined] assert str(simple_module.parent) not in sys.path + assert module.__name__ in sys.modules + assert module.__name__ == "_src.tests.mymod" + assert "_src" in sys.modules + assert "_src.tests" in sys.modules - def test_importmode_twice_is_different_module(self, simple_module: Path) -> None: + def test_importmode_twice_is_different_module( + self, simple_module: Path, tmp_path: Path + ) -> None: """`importlib` mode always returns a new module.""" - module1 = import_path(simple_module, mode="importlib") - module2 = import_path(simple_module, mode="importlib") + module1 = import_path(simple_module, mode="importlib", root=tmp_path) + module2 = import_path(simple_module, mode="importlib", root=tmp_path) assert module1 is not module2 def test_no_meta_path_found( - self, simple_module: Path, monkeypatch: MonkeyPatch + self, simple_module: Path, monkeypatch: MonkeyPatch, tmp_path: Path ) -> None: """Even without any meta_path should still import module.""" monkeypatch.setattr(sys, "meta_path", []) - module = import_path(simple_module, mode="importlib") + module = import_path(simple_module, mode="importlib", root=tmp_path) assert module.foo(2) == 42 # type: ignore[attr-defined] # mode='importlib' fails if no spec is found to load the module @@ -305,7 +309,7 @@ def test_no_meta_path_found( importlib.util, "spec_from_file_location", lambda *args: None ) with pytest.raises(ImportError): - import_path(simple_module, mode="importlib") + import_path(simple_module, mode="importlib", root=tmp_path) def test_resolve_package_path(tmp_path: Path) -> None: @@ -441,5 +445,130 @@ def test_samefile_false_negatives(tmp_path: Path, monkeypatch: MonkeyPatch) -> N # the paths too. Using a context to narrow the patch as much as possible given # this is an important system function. mp.setattr(os.path, "samefile", lambda x, y: False) - module = import_path(module_path) + module = import_path(module_path, root=tmp_path) assert getattr(module, "foo")() == 42 + + +class TestImportLibMode: + @pytest.mark.skipif(sys.version_info < (3, 7), reason="Dataclasses in Python3.7+") + def test_importmode_importlib_with_dataclass(self, tmp_path: Path) -> None: + """Ensure that importlib mode works with a module containing dataclasses (#7856).""" + fn = tmp_path.joinpath("_src/tests/test_dataclass.py") + fn.parent.mkdir(parents=True) + fn.write_text( + dedent( + """ + from dataclasses import dataclass + + @dataclass + class Data: + value: str + """ + ) + ) + + module = import_path(fn, mode="importlib", root=tmp_path) + Data: Any = getattr(module, "Data") + data = Data(value="foo") + assert data.value == "foo" + assert data.__module__ == "_src.tests.test_dataclass" + + def test_importmode_importlib_with_pickle(self, tmp_path: Path) -> None: + """Ensure that importlib mode works with pickle (#7859).""" + fn = tmp_path.joinpath("_src/tests/test_pickle.py") + fn.parent.mkdir(parents=True) + fn.write_text( + dedent( + """ + import pickle + + def _action(): + return 42 + + def round_trip(): + s = pickle.dumps(_action) + return pickle.loads(s) + """ + ) + ) + + module = import_path(fn, mode="importlib", root=tmp_path) + round_trip = getattr(module, "round_trip") + action = round_trip() + assert action() == 42 + + def test_importmode_importlib_with_pickle_separate_modules( + self, tmp_path: Path + ) -> None: + """ + Ensure that importlib mode works can load pickles that look similar but are + defined in separate modules. + """ + fn1 = tmp_path.joinpath("_src/m1/tests/test.py") + fn1.parent.mkdir(parents=True) + fn1.write_text( + dedent( + """ + import attr + import pickle + + @attr.s(auto_attribs=True) + class Data: + x: int = 42 + """ + ) + ) + + fn2 = tmp_path.joinpath("_src/m2/tests/test.py") + fn2.parent.mkdir(parents=True) + fn2.write_text( + dedent( + """ + import attr + import pickle + + @attr.s(auto_attribs=True) + class Data: + x: str = "" + """ + ) + ) + + import pickle + + def round_trip(obj): + s = pickle.dumps(obj) + return pickle.loads(s) + + module = import_path(fn1, mode="importlib", root=tmp_path) + Data1 = getattr(module, "Data") + + module = import_path(fn2, mode="importlib", root=tmp_path) + Data2 = getattr(module, "Data") + + assert round_trip(Data1(20)) == Data1(20) + assert round_trip(Data2("hello")) == Data2("hello") + assert Data1.__module__ == "_src.m1.tests.test" + assert Data2.__module__ == "_src.m2.tests.test" + + def test_module_name_from_path(self, tmp_path: Path) -> None: + result = module_name_from_path(tmp_path / "src/tests/test_foo.py", tmp_path) + assert result == "src.tests.test_foo" + + # Path is not relative to root dir: use the full path to obtain the module name. + result = module_name_from_path(Path("/home/foo/test_foo.py"), Path("/bar")) + assert result == "home.foo.test_foo" + + def test_insert_missing_modules(self) -> None: + modules = {"src.tests.foo": ModuleType("src.tests.foo")} + insert_missing_modules(modules, "src.tests.foo") + assert sorted(modules) == ["src", "src.tests", "src.tests.foo"] + + mod = ModuleType("mod", doc="My Module") + modules = {"src": mod} + insert_missing_modules(modules, "src") + assert modules == {"src": mod} + + modules = {} + insert_missing_modules(modules, "") + assert modules == {} diff --git a/testing/test_pluginmanager.py b/testing/test_pluginmanager.py index 9835b24a046..252591dd39a 100644 --- a/testing/test_pluginmanager.py +++ b/testing/test_pluginmanager.py @@ -44,7 +44,9 @@ def pytest_myhook(xyz): pm.hook.pytest_addhooks.call_historic( kwargs=dict(pluginmanager=config.pluginmanager) ) - config.pluginmanager._importconftest(conf, importmode="prepend") + config.pluginmanager._importconftest( + conf, importmode="prepend", rootpath=pytester.path + ) # print(config.pluginmanager.get_plugins()) res = config.hook.pytest_myhook(xyz=10) assert res == [11] @@ -71,7 +73,9 @@ def pytest_addoption(parser): default=True) """ ) - config.pluginmanager._importconftest(p, importmode="prepend") + config.pluginmanager._importconftest( + p, importmode="prepend", rootpath=pytester.path + ) assert config.option.test123 def test_configure(self, pytester: Pytester) -> None: @@ -136,10 +140,14 @@ def test_hook_proxy(self, pytester: Pytester) -> None: conftest1 = pytester.path.joinpath("tests/conftest.py") conftest2 = pytester.path.joinpath("tests/subdir/conftest.py") - config.pluginmanager._importconftest(conftest1, importmode="prepend") + config.pluginmanager._importconftest( + conftest1, importmode="prepend", rootpath=pytester.path + ) ihook_a = session.gethookproxy(pytester.path / "tests") assert ihook_a is not None - config.pluginmanager._importconftest(conftest2, importmode="prepend") + config.pluginmanager._importconftest( + conftest2, importmode="prepend", rootpath=pytester.path + ) ihook_b = session.gethookproxy(pytester.path / "tests") assert ihook_a is not ihook_b @@ -350,7 +358,9 @@ def test_consider_conftest_deps( pytester: Pytester, pytestpm: PytestPluginManager, ) -> None: - mod = import_path(pytester.makepyfile("pytest_plugins='xyz'")) + mod = import_path( + pytester.makepyfile("pytest_plugins='xyz'"), root=pytester.path + ) with pytest.raises(ImportError): pytestpm.consider_conftest(mod) From 97a61916a6caaa31a2995db1dbb8536439a7f930 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Thu, 8 Apr 2021 15:40:58 +0100 Subject: [PATCH 237/630] fix pytest 6.2.3 #8414 changelog entry grammar --- doc/en/changelog.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/changelog.rst b/doc/en/changelog.rst index 04f081b77d8..b07996ae2e7 100644 --- a/doc/en/changelog.rst +++ b/doc/en/changelog.rst @@ -40,7 +40,7 @@ Bug Fixes the ``tmp_path``/``tmpdir`` fixture). Now the directories are created with private permissions. - pytest used silenty use a pre-existing ``/tmp/pytest-of-`` directory, + pytest used to silenty use a pre-existing ``/tmp/pytest-of-`` directory, even if owned by another user. This means another user could pre-create such a directory and gain control of another user's temporary directory. Now such a condition results in an error. From 207c07285fb3353d85e1cb41838cd32dbe1cf412 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 Apr 2021 20:05:44 +0000 Subject: [PATCH 238/630] Bump django from 3.1.7 to 3.1.8 in /testing/plugins_integration Bumps [django](https://github.com/django/django) from 3.1.7 to 3.1.8. - [Release notes](https://github.com/django/django/releases) - [Commits](https://github.com/django/django/compare/3.1.7...3.1.8) Signed-off-by: dependabot[bot] --- testing/plugins_integration/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/plugins_integration/requirements.txt b/testing/plugins_integration/requirements.txt index 63c324a4908..849b21aa109 100644 --- a/testing/plugins_integration/requirements.txt +++ b/testing/plugins_integration/requirements.txt @@ -1,5 +1,5 @@ anyio[curio,trio]==2.2.0 -django==3.1.7 +django==3.1.8 pytest-asyncio==0.14.0 pytest-bdd==4.0.2 pytest-cov==2.11.1 From 70daa279a794452ace136b71fa69c52f24f4887d Mon Sep 17 00:00:00 2001 From: Daniele Procida Date: Fri, 9 Apr 2021 01:19:30 +0100 Subject: [PATCH 239/630] Further rationalised "how to invoke pytest" material. (#8536) Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- doc/en/how-to/failures.rst | 162 ++++++ doc/en/how-to/index.rst | 4 +- doc/en/how-to/output.rst | 713 +++++++++++++++++++++++++ doc/en/how-to/usage.rst | 908 +------------------------------- doc/en/reference/exit-codes.rst | 26 + doc/en/reference/index.rst | 1 + 6 files changed, 909 insertions(+), 905 deletions(-) create mode 100644 doc/en/how-to/failures.rst create mode 100644 doc/en/how-to/output.rst create mode 100644 doc/en/reference/exit-codes.rst diff --git a/doc/en/how-to/failures.rst b/doc/en/how-to/failures.rst new file mode 100644 index 00000000000..bbb660b2269 --- /dev/null +++ b/doc/en/how-to/failures.rst @@ -0,0 +1,162 @@ +.. _how-to-handle-failures: + +How to handle test failures +============================= + +.. _maxfail: + +Stopping after the first (or N) failures +--------------------------------------------------- + +To stop the testing process after the first (N) failures: + +.. code-block:: bash + + pytest -x # stop after first failure + pytest --maxfail=2 # stop after two failures + + +.. _pdb-option: + +Using PDB_ (Python Debugger) with pytest +---------------------------------------------------------- + +Dropping to PDB_ (Python Debugger) on failures +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. _PDB: http://docs.python.org/library/pdb.html + +Python comes with a builtin Python debugger called PDB_. ``pytest`` +allows one to drop into the PDB_ prompt via a command line option: + +.. code-block:: bash + + pytest --pdb + +This will invoke the Python debugger on every failure (or KeyboardInterrupt). +Often you might only want to do this for the first failing test to understand +a certain failure situation: + +.. code-block:: bash + + pytest -x --pdb # drop to PDB on first failure, then end test session + pytest --pdb --maxfail=3 # drop to PDB for first three failures + +Note that on any failure the exception information is stored on +``sys.last_value``, ``sys.last_type`` and ``sys.last_traceback``. In +interactive use, this allows one to drop into postmortem debugging with +any debug tool. One can also manually access the exception information, +for example:: + + >>> import sys + >>> sys.last_traceback.tb_lineno + 42 + >>> sys.last_value + AssertionError('assert result == "ok"',) + + +.. _trace-option: + +Dropping to PDB_ at the start of a test +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``pytest`` allows one to drop into the PDB_ prompt immediately at the start of each test via a command line option: + +.. code-block:: bash + + pytest --trace + +This will invoke the Python debugger at the start of every test. + +.. _breakpoints: + +Setting breakpoints +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. versionadded: 2.4.0 + +To set a breakpoint in your code use the native Python ``import pdb;pdb.set_trace()`` call +in your code and pytest automatically disables its output capture for that test: + +* Output capture in other tests is not affected. +* Any prior test output that has already been captured and will be processed as + such. +* Output capture gets resumed when ending the debugger session (via the + ``continue`` command). + + +.. _`breakpoint-builtin`: + +Using the builtin breakpoint function +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Python 3.7 introduces a builtin ``breakpoint()`` function. +Pytest supports the use of ``breakpoint()`` with the following behaviours: + + - When ``breakpoint()`` is called and ``PYTHONBREAKPOINT`` is set to the default value, pytest will use the custom internal PDB trace UI instead of the system default ``Pdb``. + - When tests are complete, the system will default back to the system ``Pdb`` trace UI. + - With ``--pdb`` passed to pytest, the custom internal Pdb trace UI is used with both ``breakpoint()`` and failed tests/unhandled exceptions. + - ``--pdbcls`` can be used to specify a custom debugger class. + + +.. _faulthandler: + +Fault Handler +------------- + +.. versionadded:: 5.0 + +The `faulthandler `__ standard module +can be used to dump Python tracebacks on a segfault or after a timeout. + +The module is automatically enabled for pytest runs, unless the ``-p no:faulthandler`` is given +on the command-line. + +Also the :confval:`faulthandler_timeout=X` configuration option can be used +to dump the traceback of all threads if a test takes longer than ``X`` +seconds to finish (not available on Windows). + +.. note:: + + This functionality has been integrated from the external + `pytest-faulthandler `__ plugin, with two + small differences: + + * To disable it, use ``-p no:faulthandler`` instead of ``--no-faulthandler``: the former + can be used with any plugin, so it saves one option. + + * The ``--faulthandler-timeout`` command-line option has become the + :confval:`faulthandler_timeout` configuration option. It can still be configured from + the command-line using ``-o faulthandler_timeout=X``. + + +.. _unraisable: + +Warning about unraisable exceptions and unhandled thread exceptions +------------------------------------------------------------------- + +.. versionadded:: 6.2 + +.. note:: + + These features only work on Python>=3.8. + +Unhandled exceptions are exceptions that are raised in a situation in which +they cannot propagate to a caller. The most common case is an exception raised +in a :meth:`__del__ ` implementation. + +Unhandled thread exceptions are exceptions raised in a :class:`~threading.Thread` +but not handled, causing the thread to terminate uncleanly. + +Both types of exceptions are normally considered bugs, but may go unnoticed +because they don't cause the program itself to crash. Pytest detects these +conditions and issues a warning that is visible in the test run summary. + +The plugins are automatically enabled for pytest runs, unless the +``-p no:unraisableexception`` (for unraisable exceptions) and +``-p no:threadexception`` (for thread exceptions) options are given on the +command-line. + +The warnings may be silenced selectively using the :ref:`pytest.mark.filterwarnings ref` +mark. The warning categories are :class:`pytest.PytestUnraisableExceptionWarning` and +:class:`pytest.PytestUnhandledThreadExceptionWarning`. diff --git a/doc/en/how-to/index.rst b/doc/en/how-to/index.rst index 3629591aa5e..cfbf50fd416 100644 --- a/doc/en/how-to/index.rst +++ b/doc/en/how-to/index.rst @@ -21,12 +21,14 @@ Core pytest functionality doctest cache -test output and outcomes +Test output and outcomes ---------------------------- .. toctree:: :maxdepth: 1 + failures + output logging capture-stdout-stderr capture-warnings diff --git a/doc/en/how-to/output.rst b/doc/en/how-to/output.rst new file mode 100644 index 00000000000..465681e6680 --- /dev/null +++ b/doc/en/how-to/output.rst @@ -0,0 +1,713 @@ +.. _how-to-manage-output: + +Managing pytest's output +========================= + +Modifying Python traceback printing +-------------------------------------------------- + +Examples for modifying traceback printing: + +.. code-block:: bash + + pytest --showlocals # show local variables in tracebacks + pytest -l # show local variables (shortcut) + + pytest --tb=auto # (default) 'long' tracebacks for the first and last + # entry, but 'short' style for the other entries + pytest --tb=long # exhaustive, informative traceback formatting + pytest --tb=short # shorter traceback format + pytest --tb=line # only one line per failure + pytest --tb=native # Python standard library formatting + pytest --tb=no # no traceback at all + +The ``--full-trace`` causes very long traces to be printed on error (longer +than ``--tb=long``). It also ensures that a stack trace is printed on +**KeyboardInterrupt** (Ctrl+C). +This is very useful if the tests are taking too long and you interrupt them +with Ctrl+C to find out where the tests are *hanging*. By default no output +will be shown (because KeyboardInterrupt is caught by pytest). By using this +option you make sure a trace is shown. + + +Verbosity +-------------------------------------------------- + +The ``-v`` flag controls the verbosity of pytest output in various aspects: test session progress, assertion +details when tests fail, fixtures details with ``--fixtures``, etc. + +.. regendoc:wipe + +Consider this simple file: + +.. code-block:: python + + # content of test_verbosity_example.py + def test_ok(): + pass + + + def test_words_fail(): + fruits1 = ["banana", "apple", "grapes", "melon", "kiwi"] + fruits2 = ["banana", "apple", "orange", "melon", "kiwi"] + assert fruits1 == fruits2 + + + def test_numbers_fail(): + number_to_text1 = {str(x): x for x in range(5)} + number_to_text2 = {str(x * 10): x * 10 for x in range(5)} + assert number_to_text1 == number_to_text2 + + + def test_long_text_fail(): + long_text = "Lorem ipsum dolor sit amet " * 10 + assert "hello world" in long_text + +Executing pytest normally gives us this output (we are skipping the header to focus on the rest): + +.. code-block:: pytest + + $ pytest --no-header + =========================== test session starts =========================== + collected 4 items + + test_verbosity_example.py .FFF [100%] + + ================================ FAILURES ================================= + _____________________________ test_words_fail _____________________________ + + def test_words_fail(): + fruits1 = ["banana", "apple", "grapes", "melon", "kiwi"] + fruits2 = ["banana", "apple", "orange", "melon", "kiwi"] + > assert fruits1 == fruits2 + E AssertionError: assert ['banana', 'a...elon', 'kiwi'] == ['banana', 'a...elon', 'kiwi'] + E At index 2 diff: 'grapes' != 'orange' + E Use -v to get the full diff + + test_verbosity_example.py:8: AssertionError + ____________________________ test_numbers_fail ____________________________ + + def test_numbers_fail(): + number_to_text1 = {str(x): x for x in range(5)} + number_to_text2 = {str(x * 10): x * 10 for x in range(5)} + > assert number_to_text1 == number_to_text2 + E AssertionError: assert {'0': 0, '1':..., '3': 3, ...} == {'0': 0, '10'...'30': 30, ...} + E Omitting 1 identical items, use -vv to show + E Left contains 4 more items: + E {'1': 1, '2': 2, '3': 3, '4': 4} + E Right contains 4 more items: + E {'10': 10, '20': 20, '30': 30, '40': 40} + E Use -v to get the full diff + + test_verbosity_example.py:14: AssertionError + ___________________________ test_long_text_fail ___________________________ + + def test_long_text_fail(): + long_text = "Lorem ipsum dolor sit amet " * 10 + > assert "hello world" in long_text + E AssertionError: assert 'hello world' in 'Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ips... sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet ' + + test_verbosity_example.py:19: AssertionError + ========================= short test summary info ========================= + FAILED test_verbosity_example.py::test_words_fail - AssertionError: asser... + FAILED test_verbosity_example.py::test_numbers_fail - AssertionError: ass... + FAILED test_verbosity_example.py::test_long_text_fail - AssertionError: a... + ======================= 3 failed, 1 passed in 0.08s ======================= + +Notice that: + +* Each test inside the file is shown by a single character in the output: ``.`` for passing, ``F`` for failure. +* ``test_words_fail`` failed, and we are shown a short summary indicating the index 2 of the two lists differ. +* ``test_numbers_fail`` failed, and we are shown a summary of left/right differences on dictionary items. Identical items are omitted. +* ``test_long_text_fail`` failed, and the right hand side of the ``in`` statement is truncated using ``...``` + because it is longer than an internal threshold (240 characters currently). + +Now we can increase pytest's verbosity: + +.. code-block:: pytest + + $ pytest --no-header -v + =========================== test session starts =========================== + collecting ... collected 4 items + + test_verbosity_example.py::test_ok PASSED [ 25%] + test_verbosity_example.py::test_words_fail FAILED [ 50%] + test_verbosity_example.py::test_numbers_fail FAILED [ 75%] + test_verbosity_example.py::test_long_text_fail FAILED [100%] + + ================================ FAILURES ================================= + _____________________________ test_words_fail _____________________________ + + def test_words_fail(): + fruits1 = ["banana", "apple", "grapes", "melon", "kiwi"] + fruits2 = ["banana", "apple", "orange", "melon", "kiwi"] + > assert fruits1 == fruits2 + E AssertionError: assert ['banana', 'a...elon', 'kiwi'] == ['banana', 'a...elon', 'kiwi'] + E At index 2 diff: 'grapes' != 'orange' + E Full diff: + E - ['banana', 'apple', 'orange', 'melon', 'kiwi'] + E ? ^ ^^ + E + ['banana', 'apple', 'grapes', 'melon', 'kiwi'] + E ? ^ ^ + + + test_verbosity_example.py:8: AssertionError + ____________________________ test_numbers_fail ____________________________ + + def test_numbers_fail(): + number_to_text1 = {str(x): x for x in range(5)} + number_to_text2 = {str(x * 10): x * 10 for x in range(5)} + > assert number_to_text1 == number_to_text2 + E AssertionError: assert {'0': 0, '1':..., '3': 3, ...} == {'0': 0, '10'...'30': 30, ...} + E Omitting 1 identical items, use -vv to show + E Left contains 4 more items: + E {'1': 1, '2': 2, '3': 3, '4': 4} + E Right contains 4 more items: + E {'10': 10, '20': 20, '30': 30, '40': 40} + E Full diff: + E - {'0': 0, '10': 10, '20': 20, '30': 30, '40': 40}... + E + E ...Full output truncated (3 lines hidden), use '-vv' to show + + test_verbosity_example.py:14: AssertionError + ___________________________ test_long_text_fail ___________________________ + + def test_long_text_fail(): + long_text = "Lorem ipsum dolor sit amet " * 10 + > assert "hello world" in long_text + E AssertionError: assert 'hello world' in 'Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet ' + + test_verbosity_example.py:19: AssertionError + ========================= short test summary info ========================= + FAILED test_verbosity_example.py::test_words_fail - AssertionError: asser... + FAILED test_verbosity_example.py::test_numbers_fail - AssertionError: ass... + FAILED test_verbosity_example.py::test_long_text_fail - AssertionError: a... + ======================= 3 failed, 1 passed in 0.07s ======================= + +Notice now that: + +* Each test inside the file gets its own line in the output. +* ``test_words_fail`` now shows the two failing lists in full, in addition to which index differs. +* ``test_numbers_fail`` now shows a text diff of the two dictionaries, truncated. +* ``test_long_text_fail`` no longer truncates the right hand side of the ``in`` statement, because the internal + threshold for truncation is larger now (2400 characters currently). + +Now if we increase verbosity even more: + +.. code-block:: pytest + + $ pytest --no-header -vv + =========================== test session starts =========================== + collecting ... collected 4 items + + test_verbosity_example.py::test_ok PASSED [ 25%] + test_verbosity_example.py::test_words_fail FAILED [ 50%] + test_verbosity_example.py::test_numbers_fail FAILED [ 75%] + test_verbosity_example.py::test_long_text_fail FAILED [100%] + + ================================ FAILURES ================================= + _____________________________ test_words_fail _____________________________ + + def test_words_fail(): + fruits1 = ["banana", "apple", "grapes", "melon", "kiwi"] + fruits2 = ["banana", "apple", "orange", "melon", "kiwi"] + > assert fruits1 == fruits2 + E AssertionError: assert ['banana', 'apple', 'grapes', 'melon', 'kiwi'] == ['banana', 'apple', 'orange', 'melon', 'kiwi'] + E At index 2 diff: 'grapes' != 'orange' + E Full diff: + E - ['banana', 'apple', 'orange', 'melon', 'kiwi'] + E ? ^ ^^ + E + ['banana', 'apple', 'grapes', 'melon', 'kiwi'] + E ? ^ ^ + + + test_verbosity_example.py:8: AssertionError + ____________________________ test_numbers_fail ____________________________ + + def test_numbers_fail(): + number_to_text1 = {str(x): x for x in range(5)} + number_to_text2 = {str(x * 10): x * 10 for x in range(5)} + > assert number_to_text1 == number_to_text2 + E AssertionError: assert {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4} == {'0': 0, '10': 10, '20': 20, '30': 30, '40': 40} + E Common items: + E {'0': 0} + E Left contains 4 more items: + E {'1': 1, '2': 2, '3': 3, '4': 4} + E Right contains 4 more items: + E {'10': 10, '20': 20, '30': 30, '40': 40} + E Full diff: + E - {'0': 0, '10': 10, '20': 20, '30': 30, '40': 40} + E ? - - - - - - - - + E + {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4} + + test_verbosity_example.py:14: AssertionError + ___________________________ test_long_text_fail ___________________________ + + def test_long_text_fail(): + long_text = "Lorem ipsum dolor sit amet " * 10 + > assert "hello world" in long_text + E AssertionError: assert 'hello world' in 'Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet ' + + test_verbosity_example.py:19: AssertionError + ========================= short test summary info ========================= + FAILED test_verbosity_example.py::test_words_fail - AssertionError: asser... + FAILED test_verbosity_example.py::test_numbers_fail - AssertionError: ass... + FAILED test_verbosity_example.py::test_long_text_fail - AssertionError: a... + ======================= 3 failed, 1 passed in 0.07s ======================= + +Notice now that: + +* Each test inside the file gets its own line in the output. +* ``test_words_fail`` gives the same output as before in this case. +* ``test_numbers_fail`` now shows a full text diff of the two dictionaries. +* ``test_long_text_fail`` also doesn't truncate on the right hand side as before, but now pytest won't truncate any + text at all, regardless of its size. + +Those were examples of how verbosity affects normal test session output, but verbosity also is used in other +situations, for example you are shown even fixtures that start with ``_`` if you use ``pytest --fixtures -v``. + +Using higher verbosity levels (``-vvv``, ``-vvvv``, ...) is supported, but has no effect in pytest itself at the moment, +however some plugins might make use of higher verbosity. + +.. _`pytest.detailed_failed_tests_usage`: + +Producing a detailed summary report +-------------------------------------------------- + +The ``-r`` flag can be used to display a "short test summary info" at the end of the test session, +making it easy in large test suites to get a clear picture of all failures, skips, xfails, etc. + +It defaults to ``fE`` to list failures and errors. + +.. regendoc:wipe + +Example: + +.. code-block:: python + + # content of test_example.py + import pytest + + + @pytest.fixture + def error_fixture(): + assert 0 + + + def test_ok(): + print("ok") + + + def test_fail(): + assert 0 + + + def test_error(error_fixture): + pass + + + def test_skip(): + pytest.skip("skipping this test") + + + def test_xfail(): + pytest.xfail("xfailing this test") + + + @pytest.mark.xfail(reason="always xfail") + def test_xpass(): + pass + + +.. code-block:: pytest + + $ pytest -ra + =========================== test session starts ============================ + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + cachedir: $PYTHON_PREFIX/.pytest_cache + rootdir: $REGENDOC_TMPDIR + collected 6 items + + test_example.py .FEsxX [100%] + + ================================== ERRORS ================================== + _______________________ ERROR at setup of test_error _______________________ + + @pytest.fixture + def error_fixture(): + > assert 0 + E assert 0 + + test_example.py:6: AssertionError + ================================= FAILURES ================================= + ________________________________ test_fail _________________________________ + + def test_fail(): + > assert 0 + E assert 0 + + test_example.py:14: AssertionError + ========================= short test summary info ========================== + SKIPPED [1] test_example.py:22: skipping this test + XFAIL test_example.py::test_xfail + reason: xfailing this test + XPASS test_example.py::test_xpass always xfail + ERROR test_example.py::test_error - assert 0 + FAILED test_example.py::test_fail - assert 0 + == 1 failed, 1 passed, 1 skipped, 1 xfailed, 1 xpassed, 1 error in 0.12s === + +The ``-r`` options accepts a number of characters after it, with ``a`` used +above meaning "all except passes". + +Here is the full list of available characters that can be used: + + - ``f`` - failed + - ``E`` - error + - ``s`` - skipped + - ``x`` - xfailed + - ``X`` - xpassed + - ``p`` - passed + - ``P`` - passed with output + +Special characters for (de)selection of groups: + + - ``a`` - all except ``pP`` + - ``A`` - all + - ``N`` - none, this can be used to display nothing (since ``fE`` is the default) + +More than one character can be used, so for example to only see failed and skipped tests, you can execute: + +.. code-block:: pytest + + $ pytest -rfs + =========================== test session starts ============================ + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + cachedir: $PYTHON_PREFIX/.pytest_cache + rootdir: $REGENDOC_TMPDIR + collected 6 items + + test_example.py .FEsxX [100%] + + ================================== ERRORS ================================== + _______________________ ERROR at setup of test_error _______________________ + + @pytest.fixture + def error_fixture(): + > assert 0 + E assert 0 + + test_example.py:6: AssertionError + ================================= FAILURES ================================= + ________________________________ test_fail _________________________________ + + def test_fail(): + > assert 0 + E assert 0 + + test_example.py:14: AssertionError + ========================= short test summary info ========================== + FAILED test_example.py::test_fail - assert 0 + SKIPPED [1] test_example.py:22: skipping this test + == 1 failed, 1 passed, 1 skipped, 1 xfailed, 1 xpassed, 1 error in 0.12s === + +Using ``p`` lists the passing tests, whilst ``P`` adds an extra section "PASSES" with those tests that passed but had +captured output: + +.. code-block:: pytest + + $ pytest -rpP + =========================== test session starts ============================ + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + cachedir: $PYTHON_PREFIX/.pytest_cache + rootdir: $REGENDOC_TMPDIR + collected 6 items + + test_example.py .FEsxX [100%] + + ================================== ERRORS ================================== + _______________________ ERROR at setup of test_error _______________________ + + @pytest.fixture + def error_fixture(): + > assert 0 + E assert 0 + + test_example.py:6: AssertionError + ================================= FAILURES ================================= + ________________________________ test_fail _________________________________ + + def test_fail(): + > assert 0 + E assert 0 + + test_example.py:14: AssertionError + ================================== PASSES ================================== + _________________________________ test_ok __________________________________ + --------------------------- Captured stdout call --------------------------- + ok + ========================= short test summary info ========================== + PASSED test_example.py::test_ok + == 1 failed, 1 passed, 1 skipped, 1 xfailed, 1 xpassed, 1 error in 0.12s === + + +Creating resultlog format files +-------------------------------------------------- + +To create plain-text machine-readable result files you can issue: + +.. code-block:: bash + + pytest --resultlog=path + +and look at the content at the ``path`` location. Such files are used e.g. +by the `PyPy-test`_ web page to show test results over several revisions. + +.. warning:: + + This option is rarely used and is scheduled for removal in pytest 6.0. + + If you use this option, consider using the new `pytest-reportlog `__ plugin instead. + + See `the deprecation docs `__ + for more information. + + +.. _`PyPy-test`: http://buildbot.pypy.org/summary + + +Creating JUnitXML format files +---------------------------------------------------- + +To create result files which can be read by Jenkins_ or other Continuous +integration servers, use this invocation: + +.. code-block:: bash + + pytest --junitxml=path + +to create an XML file at ``path``. + + + +To set the name of the root test suite xml item, you can configure the ``junit_suite_name`` option in your config file: + +.. code-block:: ini + + [pytest] + junit_suite_name = my_suite + +.. versionadded:: 4.0 + +JUnit XML specification seems to indicate that ``"time"`` attribute +should report total test execution times, including setup and teardown +(`1 `_, `2 +`_). +It is the default pytest behavior. To report just call durations +instead, configure the ``junit_duration_report`` option like this: + +.. code-block:: ini + + [pytest] + junit_duration_report = call + +.. _record_property example: + +record_property +~~~~~~~~~~~~~~~~~ + +If you want to log additional information for a test, you can use the +``record_property`` fixture: + +.. code-block:: python + + def test_function(record_property): + record_property("example_key", 1) + assert True + +This will add an extra property ``example_key="1"`` to the generated +``testcase`` tag: + +.. code-block:: xml + + + + + + + +Alternatively, you can integrate this functionality with custom markers: + +.. code-block:: python + + # content of conftest.py + + + def pytest_collection_modifyitems(session, config, items): + for item in items: + for marker in item.iter_markers(name="test_id"): + test_id = marker.args[0] + item.user_properties.append(("test_id", test_id)) + +And in your tests: + +.. code-block:: python + + # content of test_function.py + import pytest + + + @pytest.mark.test_id(1501) + def test_function(): + assert True + +Will result in: + +.. code-block:: xml + + + + + + + +.. warning:: + + Please note that using this feature will break schema verifications for the latest JUnitXML schema. + This might be a problem when used with some CI servers. + + +record_xml_attribute +~~~~~~~~~~~~~~~~~~~~~~~ + +To add an additional xml attribute to a testcase element, you can use +``record_xml_attribute`` fixture. This can also be used to override existing values: + +.. code-block:: python + + def test_function(record_xml_attribute): + record_xml_attribute("assertions", "REQ-1234") + record_xml_attribute("classname", "custom_classname") + print("hello world") + assert True + +Unlike ``record_property``, this will not add a new child element. +Instead, this will add an attribute ``assertions="REQ-1234"`` inside the generated +``testcase`` tag and override the default ``classname`` with ``"classname=custom_classname"``: + +.. code-block:: xml + + + + hello world + + + +.. warning:: + + ``record_xml_attribute`` is an experimental feature, and its interface might be replaced + by something more powerful and general in future versions. The + functionality per-se will be kept, however. + + Using this over ``record_xml_property`` can help when using ci tools to parse the xml report. + However, some parsers are quite strict about the elements and attributes that are allowed. + Many tools use an xsd schema (like the example below) to validate incoming xml. + Make sure you are using attribute names that are allowed by your parser. + + Below is the Scheme used by Jenkins to validate the XML report: + + .. code-block:: xml + + + + + + + + + + + + + + + + + + +.. warning:: + + Please note that using this feature will break schema verifications for the latest JUnitXML schema. + This might be a problem when used with some CI servers. + +.. _record_testsuite_property example: + +record_testsuite_property +^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. versionadded:: 4.5 + +If you want to add a properties node at the test-suite level, which may contains properties +that are relevant to all tests, you can use the ``record_testsuite_property`` session-scoped fixture: + +The ``record_testsuite_property`` session-scoped fixture can be used to add properties relevant +to all tests. + +.. code-block:: python + + import pytest + + + @pytest.fixture(scope="session", autouse=True) + def log_global_env_facts(record_testsuite_property): + record_testsuite_property("ARCH", "PPC") + record_testsuite_property("STORAGE_TYPE", "CEPH") + + + class TestMe: + def test_foo(self): + assert True + +The fixture is a callable which receives ``name`` and ``value`` of a ```` tag +added at the test-suite level of the generated xml: + +.. code-block:: xml + + + + + + + + + +``name`` must be a string, ``value`` will be converted to a string and properly xml-escaped. + +The generated XML is compatible with the latest ``xunit`` standard, contrary to `record_property`_ +and `record_xml_attribute`_. + + +Sending test report to an online pastebin service +-------------------------------------------------- + +**Creating a URL for each test failure**: + +.. code-block:: bash + + pytest --pastebin=failed + +This will submit test run information to a remote Paste service and +provide a URL for each failure. You may select tests as usual or add +for example ``-x`` if you only want to send one particular failure. + +**Creating a URL for a whole test session log**: + +.. code-block:: bash + + pytest --pastebin=all + +Currently only pasting to the http://bpaste.net service is implemented. + +.. versionchanged:: 5.2 + +If creating the URL fails for any reason, a warning is generated instead of failing the +entire test suite. + +.. _jenkins: http://jenkins-ci.org/ diff --git a/doc/en/how-to/usage.rst b/doc/en/how-to/usage.rst index 5917c856dde..517c504df7a 100644 --- a/doc/en/how-to/usage.rst +++ b/doc/en/how-to/usage.rst @@ -6,11 +6,10 @@ How to invoke pytest .. seealso:: :ref:`Complete pytest command-line flag reference ` -In general, pytest is invoked with the command ``pytest``. This will execute all tests in all files whose names follow -the form ``test_*.py`` or ``\*_test.py`` in the current directory and its subdirectories. More generally, pytest -follows :ref:`standard test discovery rules `. - -.. seealso:: :ref:`invoke-other` +In general, pytest is invoked with the command ``pytest`` (see below for :ref:`other ways to invoke pytest +`). This will execute all tests in all files whose names follow the form ``test_*.py`` or ``\*_test.py`` +in the current directory and its subdirectories. More generally, pytest follows :ref:`standard test discovery rules +`. .. _select-tests: @@ -81,32 +80,6 @@ For more information see :ref:`marks `. This will import ``pkg.testing`` and use its filesystem location to find and run tests from. -Possible exit codes --------------------------------------------------------------- - -Running ``pytest`` can result in six different exit codes: - -:Exit code 0: All tests were collected and passed successfully -:Exit code 1: Tests were collected and run but some of the tests failed -:Exit code 2: Test execution was interrupted by the user -:Exit code 3: Internal error happened while executing tests -:Exit code 4: pytest command line usage error -:Exit code 5: No tests were collected - -They are represented by the :class:`pytest.ExitCode` enum. The exit codes being a part of the public API can be imported and accessed directly using: - -.. code-block:: python - - from pytest import ExitCode - -.. note:: - - If you would like to customize the exit code in some scenarios, specially when - no tests are collected, consider using the - `pytest-custom_exit_code `__ - plugin. - - Getting help on version, option names, environment variables -------------------------------------------------------------- @@ -117,603 +90,6 @@ Getting help on version, option names, environment variables pytest -h | --help # show help on command line and config file options - -.. _maxfail: - -Stopping after the first (or N) failures ---------------------------------------------------- - -To stop the testing process after the first (N) failures: - -.. code-block:: bash - - pytest -x # stop after first failure - pytest --maxfail=2 # stop after two failures - - -Managing pytest's output --------------------------- - -Modifying Python traceback printing -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Examples for modifying traceback printing: - -.. code-block:: bash - - pytest --showlocals # show local variables in tracebacks - pytest -l # show local variables (shortcut) - - pytest --tb=auto # (default) 'long' tracebacks for the first and last - # entry, but 'short' style for the other entries - pytest --tb=long # exhaustive, informative traceback formatting - pytest --tb=short # shorter traceback format - pytest --tb=line # only one line per failure - pytest --tb=native # Python standard library formatting - pytest --tb=no # no traceback at all - -The ``--full-trace`` causes very long traces to be printed on error (longer -than ``--tb=long``). It also ensures that a stack trace is printed on -**KeyboardInterrupt** (Ctrl+C). -This is very useful if the tests are taking too long and you interrupt them -with Ctrl+C to find out where the tests are *hanging*. By default no output -will be shown (because KeyboardInterrupt is caught by pytest). By using this -option you make sure a trace is shown. - - -Verbosity ---------- - -The ``-v`` flag controls the verbosity of pytest output in various aspects: test session progress, assertion -details when tests fail, fixtures details with ``--fixtures``, etc. - -.. regendoc:wipe - -Consider this simple file: - -.. code-block:: python - - # content of test_verbosity_example.py - def test_ok(): - pass - - - def test_words_fail(): - fruits1 = ["banana", "apple", "grapes", "melon", "kiwi"] - fruits2 = ["banana", "apple", "orange", "melon", "kiwi"] - assert fruits1 == fruits2 - - - def test_numbers_fail(): - number_to_text1 = {str(x): x for x in range(5)} - number_to_text2 = {str(x * 10): x * 10 for x in range(5)} - assert number_to_text1 == number_to_text2 - - - def test_long_text_fail(): - long_text = "Lorem ipsum dolor sit amet " * 10 - assert "hello world" in long_text - -Executing pytest normally gives us this output (we are skipping the header to focus on the rest): - -.. code-block:: pytest - - $ pytest --no-header - =========================== test session starts =========================== - collected 4 items - - test_verbosity_example.py .FFF [100%] - - ================================ FAILURES ================================= - _____________________________ test_words_fail _____________________________ - - def test_words_fail(): - fruits1 = ["banana", "apple", "grapes", "melon", "kiwi"] - fruits2 = ["banana", "apple", "orange", "melon", "kiwi"] - > assert fruits1 == fruits2 - E AssertionError: assert ['banana', 'a...elon', 'kiwi'] == ['banana', 'a...elon', 'kiwi'] - E At index 2 diff: 'grapes' != 'orange' - E Use -v to get the full diff - - test_verbosity_example.py:8: AssertionError - ____________________________ test_numbers_fail ____________________________ - - def test_numbers_fail(): - number_to_text1 = {str(x): x for x in range(5)} - number_to_text2 = {str(x * 10): x * 10 for x in range(5)} - > assert number_to_text1 == number_to_text2 - E AssertionError: assert {'0': 0, '1':..., '3': 3, ...} == {'0': 0, '10'...'30': 30, ...} - E Omitting 1 identical items, use -vv to show - E Left contains 4 more items: - E {'1': 1, '2': 2, '3': 3, '4': 4} - E Right contains 4 more items: - E {'10': 10, '20': 20, '30': 30, '40': 40} - E Use -v to get the full diff - - test_verbosity_example.py:14: AssertionError - ___________________________ test_long_text_fail ___________________________ - - def test_long_text_fail(): - long_text = "Lorem ipsum dolor sit amet " * 10 - > assert "hello world" in long_text - E AssertionError: assert 'hello world' in 'Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ips... sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet ' - - test_verbosity_example.py:19: AssertionError - ========================= short test summary info ========================= - FAILED test_verbosity_example.py::test_words_fail - AssertionError: asser... - FAILED test_verbosity_example.py::test_numbers_fail - AssertionError: ass... - FAILED test_verbosity_example.py::test_long_text_fail - AssertionError: a... - ======================= 3 failed, 1 passed in 0.08s ======================= - -Notice that: - -* Each test inside the file is shown by a single character in the output: ``.`` for passing, ``F`` for failure. -* ``test_words_fail`` failed, and we are shown a short summary indicating the index 2 of the two lists differ. -* ``test_numbers_fail`` failed, and we are shown a summary of left/right differences on dictionary items. Identical items are omitted. -* ``test_long_text_fail`` failed, and the right hand side of the ``in`` statement is truncated using ``...``` - because it is longer than an internal threshold (240 characters currently). - -Now we can increase pytest's verbosity: - -.. code-block:: pytest - - $ pytest --no-header -v - =========================== test session starts =========================== - collecting ... collected 4 items - - test_verbosity_example.py::test_ok PASSED [ 25%] - test_verbosity_example.py::test_words_fail FAILED [ 50%] - test_verbosity_example.py::test_numbers_fail FAILED [ 75%] - test_verbosity_example.py::test_long_text_fail FAILED [100%] - - ================================ FAILURES ================================= - _____________________________ test_words_fail _____________________________ - - def test_words_fail(): - fruits1 = ["banana", "apple", "grapes", "melon", "kiwi"] - fruits2 = ["banana", "apple", "orange", "melon", "kiwi"] - > assert fruits1 == fruits2 - E AssertionError: assert ['banana', 'a...elon', 'kiwi'] == ['banana', 'a...elon', 'kiwi'] - E At index 2 diff: 'grapes' != 'orange' - E Full diff: - E - ['banana', 'apple', 'orange', 'melon', 'kiwi'] - E ? ^ ^^ - E + ['banana', 'apple', 'grapes', 'melon', 'kiwi'] - E ? ^ ^ + - - test_verbosity_example.py:8: AssertionError - ____________________________ test_numbers_fail ____________________________ - - def test_numbers_fail(): - number_to_text1 = {str(x): x for x in range(5)} - number_to_text2 = {str(x * 10): x * 10 for x in range(5)} - > assert number_to_text1 == number_to_text2 - E AssertionError: assert {'0': 0, '1':..., '3': 3, ...} == {'0': 0, '10'...'30': 30, ...} - E Omitting 1 identical items, use -vv to show - E Left contains 4 more items: - E {'1': 1, '2': 2, '3': 3, '4': 4} - E Right contains 4 more items: - E {'10': 10, '20': 20, '30': 30, '40': 40} - E Full diff: - E - {'0': 0, '10': 10, '20': 20, '30': 30, '40': 40}... - E - E ...Full output truncated (3 lines hidden), use '-vv' to show - - test_verbosity_example.py:14: AssertionError - ___________________________ test_long_text_fail ___________________________ - - def test_long_text_fail(): - long_text = "Lorem ipsum dolor sit amet " * 10 - > assert "hello world" in long_text - E AssertionError: assert 'hello world' in 'Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet ' - - test_verbosity_example.py:19: AssertionError - ========================= short test summary info ========================= - FAILED test_verbosity_example.py::test_words_fail - AssertionError: asser... - FAILED test_verbosity_example.py::test_numbers_fail - AssertionError: ass... - FAILED test_verbosity_example.py::test_long_text_fail - AssertionError: a... - ======================= 3 failed, 1 passed in 0.07s ======================= - -Notice now that: - -* Each test inside the file gets its own line in the output. -* ``test_words_fail`` now shows the two failing lists in full, in addition to which index differs. -* ``test_numbers_fail`` now shows a text diff of the two dictionaries, truncated. -* ``test_long_text_fail`` no longer truncates the right hand side of the ``in`` statement, because the internal - threshold for truncation is larger now (2400 characters currently). - -Now if we increase verbosity even more: - -.. code-block:: pytest - - $ pytest --no-header -vv - =========================== test session starts =========================== - collecting ... collected 4 items - - test_verbosity_example.py::test_ok PASSED [ 25%] - test_verbosity_example.py::test_words_fail FAILED [ 50%] - test_verbosity_example.py::test_numbers_fail FAILED [ 75%] - test_verbosity_example.py::test_long_text_fail FAILED [100%] - - ================================ FAILURES ================================= - _____________________________ test_words_fail _____________________________ - - def test_words_fail(): - fruits1 = ["banana", "apple", "grapes", "melon", "kiwi"] - fruits2 = ["banana", "apple", "orange", "melon", "kiwi"] - > assert fruits1 == fruits2 - E AssertionError: assert ['banana', 'apple', 'grapes', 'melon', 'kiwi'] == ['banana', 'apple', 'orange', 'melon', 'kiwi'] - E At index 2 diff: 'grapes' != 'orange' - E Full diff: - E - ['banana', 'apple', 'orange', 'melon', 'kiwi'] - E ? ^ ^^ - E + ['banana', 'apple', 'grapes', 'melon', 'kiwi'] - E ? ^ ^ + - - test_verbosity_example.py:8: AssertionError - ____________________________ test_numbers_fail ____________________________ - - def test_numbers_fail(): - number_to_text1 = {str(x): x for x in range(5)} - number_to_text2 = {str(x * 10): x * 10 for x in range(5)} - > assert number_to_text1 == number_to_text2 - E AssertionError: assert {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4} == {'0': 0, '10': 10, '20': 20, '30': 30, '40': 40} - E Common items: - E {'0': 0} - E Left contains 4 more items: - E {'1': 1, '2': 2, '3': 3, '4': 4} - E Right contains 4 more items: - E {'10': 10, '20': 20, '30': 30, '40': 40} - E Full diff: - E - {'0': 0, '10': 10, '20': 20, '30': 30, '40': 40} - E ? - - - - - - - - - E + {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4} - - test_verbosity_example.py:14: AssertionError - ___________________________ test_long_text_fail ___________________________ - - def test_long_text_fail(): - long_text = "Lorem ipsum dolor sit amet " * 10 - > assert "hello world" in long_text - E AssertionError: assert 'hello world' in 'Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet Lorem ipsum dolor sit amet ' - - test_verbosity_example.py:19: AssertionError - ========================= short test summary info ========================= - FAILED test_verbosity_example.py::test_words_fail - AssertionError: asser... - FAILED test_verbosity_example.py::test_numbers_fail - AssertionError: ass... - FAILED test_verbosity_example.py::test_long_text_fail - AssertionError: a... - ======================= 3 failed, 1 passed in 0.07s ======================= - -Notice now that: - -* Each test inside the file gets its own line in the output. -* ``test_words_fail`` gives the same output as before in this case. -* ``test_numbers_fail`` now shows a full text diff of the two dictionaries. -* ``test_long_text_fail`` also doesn't truncate on the right hand side as before, but now pytest won't truncate any - text at all, regardless of its size. - -Those were examples of how verbosity affects normal test session output, but verbosity also is used in other -situations, for example you are shown even fixtures that start with ``_`` if you use ``pytest --fixtures -v``. - -Using higher verbosity levels (``-vvv``, ``-vvvv``, ...) is supported, but has no effect in pytest itself at the moment, -however some plugins might make use of higher verbosity. - -.. _`pytest.detailed_failed_tests_usage`: - -Producing a detailed summary report -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The ``-r`` flag can be used to display a "short test summary info" at the end of the test session, -making it easy in large test suites to get a clear picture of all failures, skips, xfails, etc. - -It defaults to ``fE`` to list failures and errors. - -.. regendoc:wipe - -Example: - -.. code-block:: python - - # content of test_example.py - import pytest - - - @pytest.fixture - def error_fixture(): - assert 0 - - - def test_ok(): - print("ok") - - - def test_fail(): - assert 0 - - - def test_error(error_fixture): - pass - - - def test_skip(): - pytest.skip("skipping this test") - - - def test_xfail(): - pytest.xfail("xfailing this test") - - - @pytest.mark.xfail(reason="always xfail") - def test_xpass(): - pass - - -.. code-block:: pytest - - $ pytest -ra - =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y - cachedir: $PYTHON_PREFIX/.pytest_cache - rootdir: $REGENDOC_TMPDIR - collected 6 items - - test_example.py .FEsxX [100%] - - ================================== ERRORS ================================== - _______________________ ERROR at setup of test_error _______________________ - - @pytest.fixture - def error_fixture(): - > assert 0 - E assert 0 - - test_example.py:6: AssertionError - ================================= FAILURES ================================= - ________________________________ test_fail _________________________________ - - def test_fail(): - > assert 0 - E assert 0 - - test_example.py:14: AssertionError - ========================= short test summary info ========================== - SKIPPED [1] test_example.py:22: skipping this test - XFAIL test_example.py::test_xfail - reason: xfailing this test - XPASS test_example.py::test_xpass always xfail - ERROR test_example.py::test_error - assert 0 - FAILED test_example.py::test_fail - assert 0 - == 1 failed, 1 passed, 1 skipped, 1 xfailed, 1 xpassed, 1 error in 0.12s === - -The ``-r`` options accepts a number of characters after it, with ``a`` used -above meaning "all except passes". - -Here is the full list of available characters that can be used: - - - ``f`` - failed - - ``E`` - error - - ``s`` - skipped - - ``x`` - xfailed - - ``X`` - xpassed - - ``p`` - passed - - ``P`` - passed with output - -Special characters for (de)selection of groups: - - - ``a`` - all except ``pP`` - - ``A`` - all - - ``N`` - none, this can be used to display nothing (since ``fE`` is the default) - -More than one character can be used, so for example to only see failed and skipped tests, you can execute: - -.. code-block:: pytest - - $ pytest -rfs - =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y - cachedir: $PYTHON_PREFIX/.pytest_cache - rootdir: $REGENDOC_TMPDIR - collected 6 items - - test_example.py .FEsxX [100%] - - ================================== ERRORS ================================== - _______________________ ERROR at setup of test_error _______________________ - - @pytest.fixture - def error_fixture(): - > assert 0 - E assert 0 - - test_example.py:6: AssertionError - ================================= FAILURES ================================= - ________________________________ test_fail _________________________________ - - def test_fail(): - > assert 0 - E assert 0 - - test_example.py:14: AssertionError - ========================= short test summary info ========================== - FAILED test_example.py::test_fail - assert 0 - SKIPPED [1] test_example.py:22: skipping this test - == 1 failed, 1 passed, 1 skipped, 1 xfailed, 1 xpassed, 1 error in 0.12s === - -Using ``p`` lists the passing tests, whilst ``P`` adds an extra section "PASSES" with those tests that passed but had -captured output: - -.. code-block:: pytest - - $ pytest -rpP - =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y - cachedir: $PYTHON_PREFIX/.pytest_cache - rootdir: $REGENDOC_TMPDIR - collected 6 items - - test_example.py .FEsxX [100%] - - ================================== ERRORS ================================== - _______________________ ERROR at setup of test_error _______________________ - - @pytest.fixture - def error_fixture(): - > assert 0 - E assert 0 - - test_example.py:6: AssertionError - ================================= FAILURES ================================= - ________________________________ test_fail _________________________________ - - def test_fail(): - > assert 0 - E assert 0 - - test_example.py:14: AssertionError - ================================== PASSES ================================== - _________________________________ test_ok __________________________________ - --------------------------- Captured stdout call --------------------------- - ok - ========================= short test summary info ========================== - PASSED test_example.py::test_ok - == 1 failed, 1 passed, 1 skipped, 1 xfailed, 1 xpassed, 1 error in 0.12s === - - -Creating resultlog format files -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To create plain-text machine-readable result files you can issue: - -.. code-block:: bash - - pytest --resultlog=path - -and look at the content at the ``path`` location. Such files are used e.g. -by the `PyPy-test`_ web page to show test results over several revisions. - -.. warning:: - - This option is rarely used and is scheduled for removal in pytest 6.0. - - If you use this option, consider using the new `pytest-reportlog `__ plugin instead. - - See `the deprecation docs `__ - for more information. - - -.. _`PyPy-test`: http://buildbot.pypy.org/summary - - -Sending test report to online pastebin service -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -**Creating a URL for each test failure**: - -.. code-block:: bash - - pytest --pastebin=failed - -This will submit test run information to a remote Paste service and -provide a URL for each failure. You may select tests as usual or add -for example ``-x`` if you only want to send one particular failure. - -**Creating a URL for a whole test session log**: - -.. code-block:: bash - - pytest --pastebin=all - -Currently only pasting to the http://bpaste.net service is implemented. - -.. versionchanged:: 5.2 - -If creating the URL fails for any reason, a warning is generated instead of failing the -entire test suite. - - -.. _pdb-option: - -Using PDB_ (Python Debugger) with pytest ----------------------------------------------------------- - -Dropping to PDB_ (Python Debugger) on failures -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. _PDB: http://docs.python.org/library/pdb.html - -Python comes with a builtin Python debugger called PDB_. ``pytest`` -allows one to drop into the PDB_ prompt via a command line option: - -.. code-block:: bash - - pytest --pdb - -This will invoke the Python debugger on every failure (or KeyboardInterrupt). -Often you might only want to do this for the first failing test to understand -a certain failure situation: - -.. code-block:: bash - - pytest -x --pdb # drop to PDB on first failure, then end test session - pytest --pdb --maxfail=3 # drop to PDB for first three failures - -Note that on any failure the exception information is stored on -``sys.last_value``, ``sys.last_type`` and ``sys.last_traceback``. In -interactive use, this allows one to drop into postmortem debugging with -any debug tool. One can also manually access the exception information, -for example:: - - >>> import sys - >>> sys.last_traceback.tb_lineno - 42 - >>> sys.last_value - AssertionError('assert result == "ok"',) - - -.. _trace-option: - -Dropping to PDB_ at the start of a test -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -``pytest`` allows one to drop into the PDB_ prompt immediately at the start of each test via a command line option: - -.. code-block:: bash - - pytest --trace - -This will invoke the Python debugger at the start of every test. - -.. _breakpoints: - -Setting breakpoints -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. versionadded: 2.4.0 - -To set a breakpoint in your code use the native Python ``import pdb;pdb.set_trace()`` call -in your code and pytest automatically disables its output capture for that test: - -* Output capture in other tests is not affected. -* Any prior test output that has already been captured and will be processed as - such. -* Output capture gets resumed when ending the debugger session (via the - ``continue`` command). - - -.. _`breakpoint-builtin`: - -Using the builtin breakpoint function -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Python 3.7 introduces a builtin ``breakpoint()`` function. -Pytest supports the use of ``breakpoint()`` with the following behaviours: - - - When ``breakpoint()`` is called and ``PYTHONBREAKPOINT`` is set to the default value, pytest will use the custom internal PDB trace UI instead of the system default ``Pdb``. - - When tests are complete, the system will default back to the system ``Pdb`` trace UI. - - With ``--pdb`` passed to pytest, the custom internal Pdb trace UI is used with both ``breakpoint()`` and failed tests/unhandled exceptions. - - ``--pdbcls`` can be used to specify a custom debugger class. - - .. _durations: Profiling test execution duration @@ -730,280 +106,6 @@ To get a list of the slowest 10 test durations over 1.0s long: By default, pytest will not show test durations that are too small (<0.005s) unless ``-vv`` is passed on the command-line. -.. _faulthandler: - -Fault Handler -------------- - -.. versionadded:: 5.0 - -The `faulthandler `__ standard module -can be used to dump Python tracebacks on a segfault or after a timeout. - -The module is automatically enabled for pytest runs, unless the ``-p no:faulthandler`` is given -on the command-line. - -Also the :confval:`faulthandler_timeout=X` configuration option can be used -to dump the traceback of all threads if a test takes longer than ``X`` -seconds to finish (not available on Windows). - -.. note:: - - This functionality has been integrated from the external - `pytest-faulthandler `__ plugin, with two - small differences: - - * To disable it, use ``-p no:faulthandler`` instead of ``--no-faulthandler``: the former - can be used with any plugin, so it saves one option. - - * The ``--faulthandler-timeout`` command-line option has become the - :confval:`faulthandler_timeout` configuration option. It can still be configured from - the command-line using ``-o faulthandler_timeout=X``. - - -.. _unraisable: - -Warning about unraisable exceptions and unhandled thread exceptions -------------------------------------------------------------------- - -.. versionadded:: 6.2 - -.. note:: - - These features only work on Python>=3.8. - -Unhandled exceptions are exceptions that are raised in a situation in which -they cannot propagate to a caller. The most common case is an exception raised -in a :meth:`__del__ ` implementation. - -Unhandled thread exceptions are exceptions raised in a :class:`~threading.Thread` -but not handled, causing the thread to terminate uncleanly. - -Both types of exceptions are normally considered bugs, but may go unnoticed -because they don't cause the program itself to crash. Pytest detects these -conditions and issues a warning that is visible in the test run summary. - -The plugins are automatically enabled for pytest runs, unless the -``-p no:unraisableexception`` (for unraisable exceptions) and -``-p no:threadexception`` (for thread exceptions) options are given on the -command-line. - -The warnings may be silenced selectively using the :ref:`pytest.mark.filterwarnings ref` -mark. The warning categories are :class:`pytest.PytestUnraisableExceptionWarning` and -:class:`pytest.PytestUnhandledThreadExceptionWarning`. - - -Creating JUnitXML format files ----------------------------------------------------- - -To create result files which can be read by Jenkins_ or other Continuous -integration servers, use this invocation: - -.. code-block:: bash - - pytest --junitxml=path - -to create an XML file at ``path``. - - - -To set the name of the root test suite xml item, you can configure the ``junit_suite_name`` option in your config file: - -.. code-block:: ini - - [pytest] - junit_suite_name = my_suite - -.. versionadded:: 4.0 - -JUnit XML specification seems to indicate that ``"time"`` attribute -should report total test execution times, including setup and teardown -(`1 `_, `2 -`_). -It is the default pytest behavior. To report just call durations -instead, configure the ``junit_duration_report`` option like this: - -.. code-block:: ini - - [pytest] - junit_duration_report = call - -.. _record_property example: - -record_property -~~~~~~~~~~~~~~~~~ - -If you want to log additional information for a test, you can use the -``record_property`` fixture: - -.. code-block:: python - - def test_function(record_property): - record_property("example_key", 1) - assert True - -This will add an extra property ``example_key="1"`` to the generated -``testcase`` tag: - -.. code-block:: xml - - - - - - - -Alternatively, you can integrate this functionality with custom markers: - -.. code-block:: python - - # content of conftest.py - - - def pytest_collection_modifyitems(session, config, items): - for item in items: - for marker in item.iter_markers(name="test_id"): - test_id = marker.args[0] - item.user_properties.append(("test_id", test_id)) - -And in your tests: - -.. code-block:: python - - # content of test_function.py - import pytest - - - @pytest.mark.test_id(1501) - def test_function(): - assert True - -Will result in: - -.. code-block:: xml - - - - - - - -.. warning:: - - Please note that using this feature will break schema verifications for the latest JUnitXML schema. - This might be a problem when used with some CI servers. - - -record_xml_attribute -~~~~~~~~~~~~~~~~~~~~~~~ - -To add an additional xml attribute to a testcase element, you can use -``record_xml_attribute`` fixture. This can also be used to override existing values: - -.. code-block:: python - - def test_function(record_xml_attribute): - record_xml_attribute("assertions", "REQ-1234") - record_xml_attribute("classname", "custom_classname") - print("hello world") - assert True - -Unlike ``record_property``, this will not add a new child element. -Instead, this will add an attribute ``assertions="REQ-1234"`` inside the generated -``testcase`` tag and override the default ``classname`` with ``"classname=custom_classname"``: - -.. code-block:: xml - - - - hello world - - - -.. warning:: - - ``record_xml_attribute`` is an experimental feature, and its interface might be replaced - by something more powerful and general in future versions. The - functionality per-se will be kept, however. - - Using this over ``record_xml_property`` can help when using ci tools to parse the xml report. - However, some parsers are quite strict about the elements and attributes that are allowed. - Many tools use an xsd schema (like the example below) to validate incoming xml. - Make sure you are using attribute names that are allowed by your parser. - - Below is the Scheme used by Jenkins to validate the XML report: - - .. code-block:: xml - - - - - - - - - - - - - - - - - - -.. warning:: - - Please note that using this feature will break schema verifications for the latest JUnitXML schema. - This might be a problem when used with some CI servers. - -.. _record_testsuite_property example: - -record_testsuite_property -^^^^^^^^^^^^^^^^^^^^^^^^^ - -.. versionadded:: 4.5 - -If you want to add a properties node at the test-suite level, which may contains properties -that are relevant to all tests, you can use the ``record_testsuite_property`` session-scoped fixture: - -The ``record_testsuite_property`` session-scoped fixture can be used to add properties relevant -to all tests. - -.. code-block:: python - - import pytest - - - @pytest.fixture(scope="session", autouse=True) - def log_global_env_facts(record_testsuite_property): - record_testsuite_property("ARCH", "PPC") - record_testsuite_property("STORAGE_TYPE", "CEPH") - - - class TestMe: - def test_foo(self): - assert True - -The fixture is a callable which receives ``name`` and ``value`` of a ```` tag -added at the test-suite level of the generated xml: - -.. code-block:: xml - - - - - - - - - -``name`` must be a string, ``value`` will be converted to a string and properly xml-escaped. - -The generated XML is compatible with the latest ``xunit`` standard, contrary to `record_property`_ -and `record_xml_attribute`_. - - Managing loading of plugins ------------------------------- @@ -1128,5 +230,3 @@ hook was invoked: reflect changes to those files between the calls. For this reason, making multiple calls to ``pytest.main()`` from the same process (in order to re-run tests, for example) is not recommended. - -.. _jenkins: http://jenkins-ci.org/ diff --git a/doc/en/reference/exit-codes.rst b/doc/en/reference/exit-codes.rst new file mode 100644 index 00000000000..b695ca3702e --- /dev/null +++ b/doc/en/reference/exit-codes.rst @@ -0,0 +1,26 @@ +.. _exit-codes: + +Exit codes +======================================================== + +Running ``pytest`` can result in six different exit codes: + +:Exit code 0: All tests were collected and passed successfully +:Exit code 1: Tests were collected and run but some of the tests failed +:Exit code 2: Test execution was interrupted by the user +:Exit code 3: Internal error happened while executing tests +:Exit code 4: pytest command line usage error +:Exit code 5: No tests were collected + +They are represented by the :class:`pytest.ExitCode` enum. The exit codes being a part of the public API can be imported and accessed directly using: + +.. code-block:: python + + from pytest import ExitCode + +.. note:: + + If you would like to customize the exit code in some scenarios, specially when + no tests are collected, consider using the + `pytest-custom_exit_code `__ + plugin. diff --git a/doc/en/reference/index.rst b/doc/en/reference/index.rst index 3b2b7b37266..d9648400317 100644 --- a/doc/en/reference/index.rst +++ b/doc/en/reference/index.rst @@ -12,3 +12,4 @@ Reference guides plugin_list customize reference + exit-codes From 67d61e56e922302d827c188a69c1b9565e71e608 Mon Sep 17 00:00:00 2001 From: pytest bot Date: Sun, 11 Apr 2021 00:20:34 +0000 Subject: [PATCH 240/630] [automated] Update plugin list --- doc/en/reference/plugin_list.rst | 36 +++++++++++++++++--------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/doc/en/reference/plugin_list.rst b/doc/en/reference/plugin_list.rst index 1ce661c4e97..409c0a41a36 100644 --- a/doc/en/reference/plugin_list.rst +++ b/doc/en/reference/plugin_list.rst @@ -6,7 +6,7 @@ Plugin List PyPI projects that match "pytest-\*" are considered plugins and are listed automatically. Packages classified as inactive are excluded. -This list contains 850 plugins. +This list contains 852 plugins. ============================================================================================================== ======================================================================================================================================================================== ============== ===================== ================================================ name summary last release status requires @@ -15,7 +15,7 @@ name `pytest-adf `_ Pytest plugin for writing Azure Data Factory integration tests Mar 10, 2021 4 - Beta pytest (>=3.5.0) `pytest-adf-azure-identity `_ Pytest plugin for writing Azure Data Factory integration tests Mar 06, 2021 4 - Beta pytest (>=3.5.0) `pytest-aggreport `_ pytest plugin for pytest-repeat that generate aggregate report of the same test cases with additional statistics details. Mar 07, 2021 4 - Beta pytest (>=6.2.2) -`pytest-aio `_ Pytest plugin for testing async python code Mar 02, 2021 4 - Beta pytest ; extra == 'tests' +`pytest-aio `_ Pytest plugin for testing async python code Apr 05, 2021 4 - Beta pytest ; extra == 'tests' `pytest-aiofiles `_ pytest fixtures for writing aiofiles tests with pyfakefs May 14, 2017 5 - Production/Stable N/A `pytest-aiohttp `_ pytest plugin for aiohttp support Dec 05, 2017 N/A pytest `pytest-aiohttp-client `_ Pytest `client` fixture for the Aiohttp Nov 01, 2020 N/A pytest (>=6) @@ -102,7 +102,7 @@ name `pytest-canonical-data `_ A plugin which allows to compare results with canonical results, based on previous runs May 08, 2020 2 - Pre-Alpha pytest (>=3.5.0) `pytest-caprng `_ A plugin that replays pRNG state on failure. May 02, 2018 4 - Beta N/A `pytest-capture-deprecatedwarnings `_ pytest plugin to capture all deprecatedwarnings and put them in one file Apr 30, 2019 N/A N/A -`pytest-cases `_ Separate test code from test cases in pytest. Mar 31, 2021 5 - Production/Stable N/A +`pytest-cases `_ Separate test code from test cases in pytest. Apr 07, 2021 5 - Production/Stable N/A `pytest-cassandra `_ Cassandra CCM Test Fixtures for pytest Nov 04, 2017 1 - Planning N/A `pytest-catchlog `_ py.test plugin to catch log messages. This is a fork of pytest-capturelog. Jan 24, 2016 4 - Beta pytest (>=2.6) `pytest-catch-server `_ Pytest plugin with server for catching HTTP requests. Dec 12, 2019 5 - Production/Stable N/A @@ -129,6 +129,7 @@ name `pytest-codegen `_ Automatically create pytest test signatures Aug 23, 2020 2 - Pre-Alpha N/A `pytest-codestyle `_ pytest plugin to run pycodestyle Mar 23, 2020 3 - Alpha N/A `pytest-collect-formatter `_ Formatter for pytest collect output Mar 29, 2021 5 - Production/Stable N/A +`pytest-collect-formatter2 `_ Formatter for pytest collect output Apr 09, 2021 5 - Production/Stable N/A `pytest-colordots `_ Colorizes the progress indicators Oct 06, 2017 5 - Production/Stable N/A `pytest-commander `_ An interactive GUI test runner for PyTest Jan 31, 2021 N/A pytest (>=5.0.0) `pytest-common-subject `_ pytest framework for testing different aspects of a common method Nov 12, 2020 N/A pytest (>=3.6,<7) @@ -194,9 +195,9 @@ name `pytest-disable `_ pytest plugin to disable a test and skip it from testrun Sep 10, 2015 4 - Beta N/A `pytest-disable-plugin `_ Disable plugins per test Feb 28, 2019 4 - Beta pytest (>=3.5.0) `pytest-discord `_ A pytest plugin to notify test results to a Discord channel. Mar 20, 2021 3 - Alpha pytest (!=6.0.0,<7,>=3.3.2) -`pytest-django `_ A Django plugin for pytest. Oct 22, 2020 5 - Production/Stable pytest (>=5.4.0) +`pytest-django `_ A Django plugin for pytest. Apr 10, 2021 5 - Production/Stable pytest (>=5.4.0) `pytest-django-ahead `_ A Django plugin for pytest. Oct 27, 2016 5 - Production/Stable pytest (>=2.9) -`pytest-djangoapp `_ Nice pytest plugin to help you with Django pluggable application testing. Sep 21, 2020 4 - Beta N/A +`pytest-djangoapp `_ Nice pytest plugin to help you with Django pluggable application testing. Apr 10, 2021 4 - Beta N/A `pytest-django-cache-xdist `_ A djangocachexdist plugin for pytest May 12, 2020 4 - Beta N/A `pytest-django-casperjs `_ Integrate CasperJS with your django tests as a pytest fixture. Mar 15, 2015 2 - Pre-Alpha N/A `pytest-django-dotenv `_ Pytest plugin used to setup environment variables with django-dotenv Nov 26, 2019 4 - Beta pytest (>=2.6.0) @@ -246,7 +247,7 @@ name `pytest-easy-api `_ Simple API testing with pytest Mar 26, 2018 N/A N/A `pytest-easyMPI `_ Package that supports mpi tests in pytest Oct 21, 2020 N/A N/A `pytest-easyread `_ pytest plugin that makes terminal printouts of the reports easier to read Nov 17, 2017 N/A N/A -`pytest-easy-server `_ Pytest plugin for easy testing against servers Apr 03, 2021 3 - Alpha pytest (<5.0.0,>=4.3.1) ; python_version < "3.5" +`pytest-easy-server `_ Pytest plugin for easy testing against servers Apr 05, 2021 4 - Beta pytest (<5.0.0,>=4.3.1) ; python_version < "3.5" `pytest-ec2 `_ Pytest execution on EC2 instance Oct 22, 2019 3 - Alpha N/A `pytest-echo `_ pytest plugin with mechanisms for echoing environment variables, package version and generic attributes Jan 08, 2020 5 - Production/Stable N/A `pytest-elasticsearch `_ Elasticsearch process and client fixtures for py.test. Feb 19, 2020 5 - Production/Stable pytest (>=3.0.0) @@ -299,6 +300,7 @@ name `pytest-filemarker `_ A pytest plugin that runs marked tests when files change. Dec 01, 2020 N/A pytest `pytest-filter-case `_ run test cases filter by mark Nov 05, 2020 N/A N/A `pytest-filter-subpackage `_ Pytest plugin for filtering based on sub-packages Jan 09, 2020 3 - Alpha pytest (>=3.0) +`pytest-find-dependencies `_ A pytest plugin to find dependencies between tests Apr 07, 2021 4 - Beta pytest (>=3.5.0) `pytest-finer-verdicts `_ A pytest plugin to treat non-assertion failures as test errors. Jun 18, 2020 N/A pytest (>=5.4.3) `pytest-firefox `_ pytest plugin to manipulate firefox Aug 08, 2017 3 - Alpha pytest (>=3.0.2) `pytest-fixture-config `_ Fixture configuration utils for py.test May 28, 2019 5 - Production/Stable pytest @@ -329,7 +331,7 @@ name `pytest-gevent `_ Ensure that gevent is properly patched when invoking pytest Feb 25, 2020 N/A pytest `pytest-gherkin `_ A flexible framework for executing BDD gherkin tests Jul 27, 2019 3 - Alpha pytest (>=5.0.0) `pytest-ghostinspector `_ For finding/executing Ghost Inspector tests May 17, 2016 3 - Alpha N/A -`pytest-girder `_ A set of pytest fixtures for testing Girder applications. Mar 29, 2021 N/A N/A +`pytest-girder `_ A set of pytest fixtures for testing Girder applications. Apr 06, 2021 N/A N/A `pytest-git `_ Git repository fixture for py.test May 28, 2019 5 - Production/Stable pytest `pytest-gitcov `_ Pytest plugin for reporting on coverage of the last git commit. Jan 11, 2020 2 - Pre-Alpha N/A `pytest-git-fixtures `_ Pytest fixtures for testing with git. Mar 11, 2021 4 - Beta pytest @@ -353,7 +355,7 @@ name `pytest-historic `_ Custom report to display pytest historical execution records Apr 08, 2020 N/A pytest `pytest-historic-hook `_ Custom listener to store execution results into MYSQL DB, which is used for pytest-historic report Apr 08, 2020 N/A pytest `pytest-homeassistant `_ A pytest plugin for use with homeassistant custom components. Aug 12, 2020 4 - Beta N/A -`pytest-homeassistant-custom-component `_ Experimental package to automatically extract test plugins for Home Assistant custom components Mar 03, 2021 3 - Alpha pytest (==6.2.2) +`pytest-homeassistant-custom-component `_ Experimental package to automatically extract test plugins for Home Assistant custom components Apr 05, 2021 3 - Alpha pytest (==6.2.2) `pytest-honors `_ Report on tests that honor constraints, and guard against regressions Mar 06, 2020 4 - Beta N/A `pytest-hoverfly `_ Simplify working with Hoverfly from pytest Mar 04, 2021 N/A pytest (>=5.0) `pytest-hoverfly-wrapper `_ Integrates the Hoverfly HTTP proxy into Pytest Jan 31, 2021 4 - Beta N/A @@ -368,7 +370,7 @@ name `pytest-httpretty `_ A thin wrapper of HTTPretty for pytest Feb 16, 2014 3 - Alpha N/A `pytest-httpserver `_ pytest-httpserver is a httpserver for pytest Mar 16, 2021 3 - Alpha pytest ; extra == 'dev' `pytest-httpx `_ Send responses to httpx. Mar 01, 2021 5 - Production/Stable pytest (==6.*) -`pytest-httpx-blockage `_ Disable httpx requests during a test run Mar 20, 2021 N/A pytest (>=6.2.2) +`pytest-httpx-blockage `_ Disable httpx requests during a test run Apr 09, 2021 N/A pytest (>=6.2.2) `pytest-hue `_ Visualise PyTest status via your Phillips Hue lights May 09, 2019 N/A N/A `pytest-hylang `_ Pytest plugin to allow running tests written in hylang Mar 28, 2021 N/A pytest `pytest-hypo-25 `_ help hypo module for pytest Jan 12, 2020 3 - Alpha N/A @@ -382,10 +384,10 @@ name `pytest-info-collector `_ pytest plugin to collect information from tests May 26, 2019 3 - Alpha N/A `pytest-informative-node `_ display more node ininformation. Apr 25, 2019 4 - Beta N/A `pytest-infrastructure `_ pytest stack validation prior to testing executing Apr 12, 2020 4 - Beta N/A -`pytest-inmanta `_ A py.test plugin providing fixtures to simplify inmanta modules testing. Mar 26, 2021 5 - Production/Stable N/A +`pytest-inmanta `_ A py.test plugin providing fixtures to simplify inmanta modules testing. Apr 09, 2021 5 - Production/Stable N/A `pytest-inmanta-extensions `_ Inmanta tests package Mar 17, 2021 5 - Production/Stable N/A `pytest-Inomaly `_ A simple image diff plugin for pytest Feb 13, 2018 4 - Beta N/A -`pytest-insta `_ A practical snapshot testing plugin for pytest Apr 02, 2021 N/A pytest (>=6.0.2,<7.0.0) +`pytest-insta `_ A practical snapshot testing plugin for pytest Apr 07, 2021 N/A pytest (>=6.0.2,<7.0.0) `pytest-instafail `_ pytest plugin to show failures instantly Jun 14, 2020 4 - Beta pytest (>=2.9) `pytest-instrument `_ pytest plugin to instrument tests Apr 05, 2020 5 - Production/Stable pytest (>=5.1.0) `pytest-integration `_ Organizing pytests by integration or not Apr 16, 2020 N/A N/A @@ -654,7 +656,7 @@ name `pytest-reverse `_ Pytest plugin to reverse test order. Dec 27, 2020 5 - Production/Stable pytest `pytest-ringo `_ pytest plugin to test webapplications using the Ringo webframework Sep 27, 2017 3 - Alpha N/A `pytest-rng `_ Fixtures for seeding tests and making randomness reproducible Aug 08, 2019 5 - Production/Stable pytest -`pytest-roast `_ pytest plugin for ROAST configuration override and fixtures Feb 05, 2021 5 - Production/Stable pytest (<6) +`pytest-roast `_ pytest plugin for ROAST configuration override and fixtures Apr 05, 2021 5 - Production/Stable pytest `pytest-rotest `_ Pytest integration with rotest Sep 08, 2019 N/A pytest (>=3.5.0) `pytest-rpc `_ Extend py.test for RPC OpenStack testing. Feb 22, 2019 4 - Beta pytest (~=3.6) `pytest-rt `_ pytest data collector plugin for Testgr Mar 03, 2021 N/A N/A @@ -664,19 +666,19 @@ name `pytest-runner `_ Invoke py.test as distutils command with dependency resolution Feb 12, 2021 5 - Production/Stable pytest (!=3.7.3,>=3.5) ; extra == 'testing' `pytest-salt `_ Pytest Salt Plugin Jan 27, 2020 4 - Beta N/A `pytest-salt-containers `_ A Pytest plugin that builds and creates docker containers Nov 09, 2016 4 - Beta N/A -`pytest-salt-factories `_ Pytest Salt Plugin Mar 23, 2021 4 - Beta pytest (>=6.1.1) +`pytest-salt-factories `_ Pytest Salt Plugin Apr 06, 2021 4 - Beta pytest (>=6.1.1) `pytest-salt-from-filenames `_ Simple PyTest Plugin For Salt's Test Suite Specifically Jan 29, 2019 4 - Beta pytest (>=4.1) `pytest-salt-runtests-bridge `_ Simple PyTest Plugin For Salt's Test Suite Specifically Dec 05, 2019 4 - Beta pytest (>=4.1) `pytest-sanic `_ a pytest plugin for Sanic Feb 27, 2021 N/A pytest (>=5.2) `pytest-sanity `_ Dec 07, 2020 N/A N/A `pytest-sa-pg `_ May 14, 2019 N/A N/A -`pytest-sbase `_ A complete web automation framework for end-to-end testing. Apr 03, 2021 5 - Production/Stable N/A +`pytest-sbase `_ A complete web automation framework for end-to-end testing. Apr 10, 2021 5 - Production/Stable N/A `pytest-scenario `_ pytest plugin for test scenarios Feb 06, 2017 3 - Alpha N/A `pytest-schema `_ 👍 Validate return values against a schema-like object in testing Aug 31, 2020 5 - Production/Stable pytest (>=3.5.0) `pytest-securestore `_ An encrypted password store for use within pytest cases Jun 19, 2019 4 - Beta N/A `pytest-select `_ A pytest plugin which allows to (de-)select tests from a file. Jan 18, 2019 3 - Alpha pytest (>=3.0) `pytest-selenium `_ pytest plugin for Selenium Sep 19, 2020 5 - Production/Stable pytest (>=5.0.0) -`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Apr 03, 2021 5 - Production/Stable N/A +`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Apr 10, 2021 5 - Production/Stable N/A `pytest-selenium-enhancer `_ pytest plugin for Selenium Nov 26, 2020 5 - Production/Stable N/A `pytest-selenium-pdiff `_ A pytest package implementing perceptualdiff for Selenium tests. Apr 06, 2017 2 - Pre-Alpha N/A `pytest-send-email `_ Send pytest execution result email Dec 04, 2019 N/A N/A @@ -710,7 +712,7 @@ name `pytest-socket `_ Pytest Plugin to disable socket calls during tests Mar 30, 2021 4 - Beta pytest (>=3.6.3) `pytest-soft-assertions `_ May 05, 2020 3 - Alpha pytest `pytest-solr `_ Solr process and client fixtures for py.test. May 11, 2020 3 - Alpha pytest (>=3.0.0) -`pytest-sorter `_ A simple plugin to first execute tests that historically failed more Jul 23, 2020 4 - Beta pytest (>=3.1.1) +`pytest-sorter `_ A simple plugin to first execute tests that historically failed more Apr 05, 2021 4 - Beta pytest (>=3.1.1) `pytest-sourceorder `_ Test-ordering plugin for pytest Apr 11, 2017 4 - Beta pytest `pytest-spark `_ pytest plugin to run the tests with support of pyspark. Feb 23, 2020 4 - Beta pytest `pytest-spawner `_ py.test plugin to spawn process and communicate with them. Jul 31, 2015 4 - Beta N/A @@ -859,6 +861,6 @@ name `pytest-yuk `_ Display tests you are uneasy with, using 🤢/🤮 for pass/fail of tests marked with yuk. Mar 26, 2021 N/A N/A `pytest-zafira `_ A Zafira plugin for pytest Sep 18, 2019 5 - Production/Stable pytest (==4.1.1) `pytest-zap `_ OWASP ZAP plugin for py.test. May 12, 2014 4 - Beta N/A -`pytest-zebrunner `_ Pytest connector for Zebrunner reporting Mar 29, 2021 4 - Beta pytest (>=6.1.1,<7.0.0) +`pytest-zebrunner `_ Pytest connector for Zebrunner reporting Apr 06, 2021 4 - Beta pytest (>=6.1.1,<7.0.0) `pytest-zigzag `_ Extend py.test for RPC OpenStack testing. Feb 27, 2019 4 - Beta pytest (~=3.6) ============================================================================================================== ======================================================================================================================================================================== ============== ===================== ================================================ From 8be16280423eafbc3c604a81419f40a8c1681fba Mon Sep 17 00:00:00 2001 From: hauntsaninja <> Date: Sun, 11 Apr 2021 15:38:32 -0700 Subject: [PATCH 241/630] Fix assertion rewriting on Python 3.10 Fixes https://github.com/pytest-dev/pytest/issues/8539 This seems to have been the result of https://bugs.python.org/issue43798 --- AUTHORS | 1 + changelog/8539.bugfix.rst | 1 + src/_pytest/assertion/rewrite.py | 18 ++++++++++++++---- 3 files changed, 16 insertions(+), 4 deletions(-) create mode 100644 changelog/8539.bugfix.rst diff --git a/AUTHORS b/AUTHORS index 46f283452c3..9b3ec153306 100644 --- a/AUTHORS +++ b/AUTHORS @@ -277,6 +277,7 @@ Sankt Petersbug Segev Finer Serhii Mozghovyi Seth Junot +Shantanu Jain Shubham Adep Simon Gomizelj Simon Kerr diff --git a/changelog/8539.bugfix.rst b/changelog/8539.bugfix.rst new file mode 100644 index 00000000000..a2098610e29 --- /dev/null +++ b/changelog/8539.bugfix.rst @@ -0,0 +1 @@ +Fixed assertion rewriting on Python 3.10. diff --git a/src/_pytest/assertion/rewrite.py b/src/_pytest/assertion/rewrite.py index 6a3222f333d..537ded257e9 100644 --- a/src/_pytest/assertion/rewrite.py +++ b/src/_pytest/assertion/rewrite.py @@ -684,12 +684,9 @@ def run(self, mod: ast.Module) -> None: if not mod.body: # Nothing to do. return + # Insert some special imports at the top of the module but after any # docstrings and __future__ imports. - aliases = [ - ast.alias("builtins", "@py_builtins"), - ast.alias("_pytest.assertion.rewrite", "@pytest_ar"), - ] doc = getattr(mod, "docstring", None) expect_docstring = doc is None if doc is not None and self.is_rewrite_disabled(doc): @@ -721,6 +718,19 @@ def run(self, mod: ast.Module) -> None: lineno = item.decorator_list[0].lineno else: lineno = item.lineno + if sys.version_info >= (3, 10): + aliases = [ + ast.alias("builtins", "@py_builtins", lineno=lineno, col_offset=0), + ast.alias( + "_pytest.assertion.rewrite", "@pytest_ar", + lineno=lineno, col_offset=0 + ), + ] + else: + aliases = [ + ast.alias("builtins", "@py_builtins"), + ast.alias("_pytest.assertion.rewrite", "@pytest_ar"), + ] imports = [ ast.Import([alias], lineno=lineno, col_offset=0) for alias in aliases ] From da66f004133e6c6ed5081854146c0ceffd3f144e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 11 Apr 2021 22:44:28 +0000 Subject: [PATCH 242/630] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/_pytest/assertion/rewrite.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/_pytest/assertion/rewrite.py b/src/_pytest/assertion/rewrite.py index 537ded257e9..f661fe9475e 100644 --- a/src/_pytest/assertion/rewrite.py +++ b/src/_pytest/assertion/rewrite.py @@ -722,8 +722,10 @@ def run(self, mod: ast.Module) -> None: aliases = [ ast.alias("builtins", "@py_builtins", lineno=lineno, col_offset=0), ast.alias( - "_pytest.assertion.rewrite", "@pytest_ar", - lineno=lineno, col_offset=0 + "_pytest.assertion.rewrite", + "@pytest_ar", + lineno=lineno, + col_offset=0, ), ] else: From e14bd4a480fa18961874f6d2a90e8c48f344043c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Apr 2021 03:01:30 +0000 Subject: [PATCH 243/630] Bump django from 3.1.8 to 3.2 in /testing/plugins_integration Bumps [django](https://github.com/django/django) from 3.1.8 to 3.2. - [Release notes](https://github.com/django/django/releases) - [Commits](https://github.com/django/django/compare/3.1.8...3.2) Signed-off-by: dependabot[bot] --- testing/plugins_integration/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/plugins_integration/requirements.txt b/testing/plugins_integration/requirements.txt index 849b21aa109..aa8464cb929 100644 --- a/testing/plugins_integration/requirements.txt +++ b/testing/plugins_integration/requirements.txt @@ -1,5 +1,5 @@ anyio[curio,trio]==2.2.0 -django==3.1.8 +django==3.2 pytest-asyncio==0.14.0 pytest-bdd==4.0.2 pytest-cov==2.11.1 From 22a17dba4e0d7526ba74213ed89bb2f6d5ad31d0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Apr 2021 03:01:33 +0000 Subject: [PATCH 244/630] Bump pytest-django from 4.1.0 to 4.2.0 in /testing/plugins_integration Bumps [pytest-django](https://github.com/pytest-dev/pytest-django) from 4.1.0 to 4.2.0. - [Release notes](https://github.com/pytest-dev/pytest-django/releases) - [Changelog](https://github.com/pytest-dev/pytest-django/blob/master/docs/changelog.rst) - [Commits](https://github.com/pytest-dev/pytest-django/compare/v4.1.0...v4.2.0) Signed-off-by: dependabot[bot] --- testing/plugins_integration/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/plugins_integration/requirements.txt b/testing/plugins_integration/requirements.txt index 849b21aa109..9bb50ec3a70 100644 --- a/testing/plugins_integration/requirements.txt +++ b/testing/plugins_integration/requirements.txt @@ -3,7 +3,7 @@ django==3.1.8 pytest-asyncio==0.14.0 pytest-bdd==4.0.2 pytest-cov==2.11.1 -pytest-django==4.1.0 +pytest-django==4.2.0 pytest-flakes==4.0.3 pytest-html==3.1.1 pytest-mock==3.5.1 From e3dc34ee41b3703808b22357a071302c48fc6fe6 Mon Sep 17 00:00:00 2001 From: hauntsaninja <> Date: Mon, 12 Apr 2021 11:33:40 -0700 Subject: [PATCH 245/630] fixup comments --- src/_pytest/assertion/rewrite.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/_pytest/assertion/rewrite.py b/src/_pytest/assertion/rewrite.py index f661fe9475e..33e2ef6cc49 100644 --- a/src/_pytest/assertion/rewrite.py +++ b/src/_pytest/assertion/rewrite.py @@ -685,8 +685,8 @@ def run(self, mod: ast.Module) -> None: # Nothing to do. return - # Insert some special imports at the top of the module but after any - # docstrings and __future__ imports. + # We'll insert some special imports at the top of the module, but after any + # docstrings and __future__ imports, so first figure out where that is. doc = getattr(mod, "docstring", None) expect_docstring = doc is None if doc is not None and self.is_rewrite_disabled(doc): @@ -718,6 +718,7 @@ def run(self, mod: ast.Module) -> None: lineno = item.decorator_list[0].lineno else: lineno = item.lineno + # Now actually insert the special imports. if sys.version_info >= (3, 10): aliases = [ ast.alias("builtins", "@py_builtins", lineno=lineno, col_offset=0), @@ -737,6 +738,7 @@ def run(self, mod: ast.Module) -> None: ast.Import([alias], lineno=lineno, col_offset=0) for alias in aliases ] mod.body[pos:pos] = imports + # Collect asserts. nodes: List[ast.AST] = [mod] while nodes: From 4b214a604949897a9a496f8a3b0fb82b9f2002fe Mon Sep 17 00:00:00 2001 From: Albert Villanova del Moral <8515462+albertvillanova@users.noreply.github.com> Date: Wed, 14 Apr 2021 11:06:35 +0200 Subject: [PATCH 246/630] Add tmp_path_factory methods to the docs (#8550) * Add tmp_path_factory methods to the docs Add the methods of tmp_path_factory to the docs. Currently, only the class description appears in the docs. * Add tmpdir_factory methods to the docs --- doc/en/reference/reference.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/en/reference/reference.rst b/doc/en/reference/reference.rst index ce0c4efa509..d4256bd6080 100644 --- a/doc/en/reference/reference.rst +++ b/doc/en/reference/reference.rst @@ -594,6 +594,7 @@ tmp_path_factory ``tmp_path_factory`` is an instance of :class:`~pytest.TempPathFactory`: .. autoclass:: pytest.TempPathFactory() + :members: .. fixture:: tmpdir @@ -617,6 +618,7 @@ tmpdir_factory ``tmp_path_factory`` is an instance of :class:`~pytest.TempdirFactory`: .. autoclass:: pytest.TempdirFactory() + :members: .. _`hook-reference`: From d200598de9588fe6d7a68bddc3e638afce80f153 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 14 Apr 2021 12:49:09 +0300 Subject: [PATCH 247/630] [pre-commit.ci] pre-commit autoupdate (#8547) * [pre-commit.ci] pre-commit autoupdate * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Ran Benita --- .pre-commit-config.yaml | 2 +- src/_pytest/_code/code.py | 2 +- src/_pytest/_io/saferepr.py | 4 ++-- src/_pytest/compat.py | 2 +- src/_pytest/config/__init__.py | 2 +- src/_pytest/fixtures.py | 6 +++--- src/_pytest/main.py | 6 +++--- src/_pytest/mark/expression.py | 2 +- src/_pytest/mark/structures.py | 4 +--- src/_pytest/pastebin.py | 2 +- src/_pytest/pathlib.py | 4 +--- src/_pytest/pytester.py | 2 +- src/_pytest/python.py | 4 ++-- src/_pytest/python_api.py | 4 +--- src/_pytest/recwarn.py | 4 +--- src/_pytest/setuponly.py | 2 +- src/_pytest/terminal.py | 8 ++++---- testing/acceptance_test.py | 2 +- testing/python/raises.py | 4 ++-- testing/test_warnings.py | 2 +- 20 files changed, 30 insertions(+), 38 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 22caac9b9f6..71cccd907ee 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -34,7 +34,7 @@ repos: - id: reorder-python-imports args: ['--application-directories=.:src', --py36-plus] - repo: https://github.com/asottile/pyupgrade - rev: v2.11.0 + rev: v2.12.0 hooks: - id: pyupgrade args: [--py36-plus] diff --git a/src/_pytest/_code/code.py b/src/_pytest/_code/code.py index 1b4760a0c87..5f322629688 100644 --- a/src/_pytest/_code/code.py +++ b/src/_pytest/_code/code.py @@ -968,7 +968,7 @@ def __str__(self) -> str: return io.getvalue().strip() def __repr__(self) -> str: - return "<{} instance at {:0x}>".format(self.__class__, id(self)) + return f"<{self.__class__} instance at {id(self):0x}>" def toterminal(self, tw: TerminalWriter) -> None: raise NotImplementedError() diff --git a/src/_pytest/_io/saferepr.py b/src/_pytest/_io/saferepr.py index fa123d2cb19..e7ff5cab203 100644 --- a/src/_pytest/_io/saferepr.py +++ b/src/_pytest/_io/saferepr.py @@ -12,7 +12,7 @@ def _try_repr_or_str(obj: object) -> str: except (KeyboardInterrupt, SystemExit): raise except BaseException: - return '{}("{}")'.format(type(obj).__name__, obj) + return f'{type(obj).__name__}("{obj}")' def _format_repr_exception(exc: BaseException, obj: object) -> str: @@ -21,7 +21,7 @@ def _format_repr_exception(exc: BaseException, obj: object) -> str: except (KeyboardInterrupt, SystemExit): raise except BaseException as exc: - exc_info = "unpresentable exception ({})".format(_try_repr_or_str(exc)) + exc_info = f"unpresentable exception ({_try_repr_or_str(exc)})" return "<[{} raised in repr()] {} object at 0x{:x}>".format( exc_info, type(obj).__name__, id(obj) ) diff --git a/src/_pytest/compat.py b/src/_pytest/compat.py index 4236618e8b4..0c7e7626870 100644 --- a/src/_pytest/compat.py +++ b/src/_pytest/compat.py @@ -418,4 +418,4 @@ def __get__(self, instance, owner=None): # # This also work for Enums (if you use `is` to compare) and Literals. def assert_never(value: "NoReturn") -> "NoReturn": - assert False, "Unhandled value: {} ({})".format(value, type(value).__name__) + assert False, f"Unhandled value: {value} ({type(value).__name__})" diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 237f5c39ef4..0fd91c2c943 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -721,7 +721,7 @@ def import_plugin(self, modname: str, consider_entry_points: bool = False) -> No __import__(importspec) except ImportError as e: raise ImportError( - 'Error importing plugin "{}": {}'.format(modname, str(e.args[0])) + f'Error importing plugin "{modname}": {e.args[0]}' ).with_traceback(e.__traceback__) from e except Skipped as e: diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index b0a895a04ef..9b07a04bc57 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -859,7 +859,7 @@ def formatrepr(self) -> "FixtureLookupErrorRepr": error_msg = "file %s, line %s: source code not available" addline(error_msg % (fspath, lineno + 1)) else: - addline("file {}, line {}".format(fspath, lineno + 1)) + addline(f"file {fspath}, line {lineno + 1}") for i, line in enumerate(lines): line = line.rstrip() addline(" " + line) @@ -908,7 +908,7 @@ def toterminal(self, tw: TerminalWriter) -> None: lines = self.errorstring.split("\n") if lines: tw.line( - "{} {}".format(FormattedExcinfo.fail_marker, lines[0].strip()), + f"{FormattedExcinfo.fail_marker} {lines[0].strip()}", red=True, ) for line in lines[1:]: @@ -922,7 +922,7 @@ def toterminal(self, tw: TerminalWriter) -> None: def fail_fixturefunc(fixturefunc, msg: str) -> "NoReturn": fs, lineno = getfslineno(fixturefunc) - location = "{}:{}".format(fs, lineno + 1) + location = f"{fs}:{lineno + 1}" source = _pytest._code.Source(fixturefunc) fail(msg + ":\n\n" + str(source.indent()) + "\n" + location, pytrace=False) diff --git a/src/_pytest/main.py b/src/_pytest/main.py index 2c2a8397988..18a8a6592ab 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -294,7 +294,7 @@ def wrap_session( except exit.Exception as exc: if exc.returncode is not None: session.exitstatus = exc.returncode - sys.stderr.write("{}: {}\n".format(type(exc).__name__, exc)) + sys.stderr.write(f"{type(exc).__name__}: {exc}\n") else: if isinstance(excinfo.value, SystemExit): sys.stderr.write("mainloop: caught unexpected SystemExit!\n") @@ -311,7 +311,7 @@ def wrap_session( except exit.Exception as exc: if exc.returncode is not None: session.exitstatus = exc.returncode - sys.stderr.write("{}: {}\n".format(type(exc).__name__, exc)) + sys.stderr.write(f"{type(exc).__name__}: {exc}\n") config._ensure_unconfigure() return session.exitstatus @@ -718,7 +718,7 @@ def collect(self) -> Iterator[Union[nodes.Item, nodes.Collector]]: # If it's a directory argument, recurse and look for any Subpackages. # Let the Package collector deal with subnodes, don't collect here. if argpath.is_dir(): - assert not names, "invalid arg {!r}".format((argpath, names)) + assert not names, f"invalid arg {(argpath, names)!r}" seen_dirs: Set[Path] = set() for direntry in visit(str(argpath), self._recurse): diff --git a/src/_pytest/mark/expression.py b/src/_pytest/mark/expression.py index 2e7dcf93cd4..72e2ed4bdb7 100644 --- a/src/_pytest/mark/expression.py +++ b/src/_pytest/mark/expression.py @@ -103,7 +103,7 @@ def lex(self, input: str) -> Iterator[Token]: else: raise ParseError( pos + 1, - 'unexpected character "{}"'.format(input[pos]), + f'unexpected character "{input[pos]}"', ) yield Token(TokenType.EOF, "", pos) diff --git a/src/_pytest/mark/structures.py b/src/_pytest/mark/structures.py index d2f21e1684d..845a973392f 100644 --- a/src/_pytest/mark/structures.py +++ b/src/_pytest/mark/structures.py @@ -99,9 +99,7 @@ def param( if id is not None: if not isinstance(id, str): - raise TypeError( - "Expected id to be a string, got {}: {!r}".format(type(id), id) - ) + raise TypeError(f"Expected id to be a string, got {type(id)}: {id!r}") id = ascii_escaped(id) return cls(values, marks, id) diff --git a/src/_pytest/pastebin.py b/src/_pytest/pastebin.py index 131873c174a..189ed3c5e10 100644 --- a/src/_pytest/pastebin.py +++ b/src/_pytest/pastebin.py @@ -86,7 +86,7 @@ def create_new_paste(contents: Union[str, bytes]) -> str: return "bad response: %s" % exc_info m = re.search(r'href="/https/github.com/raw/(\w+)"', response) if m: - return "{}/show/{}".format(url, m.group(1)) + return f"{url}/show/{m.group(1)}" else: return "bad response: invalid format ('" + response + "')" diff --git a/src/_pytest/pathlib.py b/src/_pytest/pathlib.py index a0aa800f7ee..f641e6491fc 100644 --- a/src/_pytest/pathlib.py +++ b/src/_pytest/pathlib.py @@ -500,9 +500,7 @@ def import_path( spec = importlib.util.spec_from_file_location(module_name, str(path)) if spec is None: - raise ImportError( - "Can't find module {} at location {}".format(module_name, str(path)) - ) + raise ImportError(f"Can't find module {module_name} at location {path}") mod = importlib.util.module_from_spec(spec) sys.modules[module_name] = mod spec.loader.exec_module(mod) # type: ignore[union-attr] diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index 5a29c8eaeec..037f69e0b19 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -1865,7 +1865,7 @@ def _match_lines( Match lines consecutively? """ if not isinstance(lines2, collections.abc.Sequence): - raise TypeError("invalid type for lines2: {}".format(type(lines2).__name__)) + raise TypeError(f"invalid type for lines2: {type(lines2).__name__}") lines2 = self._getlines(lines2) lines1 = self.lines[:] extralines = [] diff --git a/src/_pytest/python.py b/src/_pytest/python.py index e6808a99a48..5ef5085c797 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -1400,7 +1400,7 @@ def idmaker( # Suffix non-unique IDs to make them unique. for index, test_id in enumerate(resolved_ids): if test_id_counts[test_id] > 1: - resolved_ids[index] = "{}{}".format(test_id, test_id_suffixes[test_id]) + resolved_ids[index] = f"{test_id}{test_id_suffixes[test_id]}" test_id_suffixes[test_id] += 1 return resolved_ids @@ -1449,7 +1449,7 @@ def write_item(item: nodes.Item) -> None: tw.line() tw.sep("-", f"fixtures used by {item.name}") # TODO: Fix this type ignore. - tw.sep("-", "({})".format(get_best_relpath(item.function))) # type: ignore[attr-defined] + tw.sep("-", f"({get_best_relpath(item.function)})") # type: ignore[attr-defined] # dict key not used in loop but needed for sorting. for _, fixturedefs in sorted(info.name2fixturedefs.items()): assert fixturedefs is not None diff --git a/src/_pytest/python_api.py b/src/_pytest/python_api.py index 1531d2fe30e..69f01619a43 100644 --- a/src/_pytest/python_api.py +++ b/src/_pytest/python_api.py @@ -733,9 +733,7 @@ def raises( else: func = args[0] if not callable(func): - raise TypeError( - "{!r} object (type: {}) must be callable".format(func, type(func)) - ) + raise TypeError(f"{func!r} object (type: {type(func)}) must be callable") try: func(*args[1:], **kwargs) except expected_exception as e: diff --git a/src/_pytest/recwarn.py b/src/_pytest/recwarn.py index d872d9da401..eb59fee2940 100644 --- a/src/_pytest/recwarn.py +++ b/src/_pytest/recwarn.py @@ -149,9 +149,7 @@ def warns( else: func = args[0] if not callable(func): - raise TypeError( - "{!r} object (type: {}) must be callable".format(func, type(func)) - ) + raise TypeError(f"{func!r} object (type: {type(func)}) must be callable") with WarningsChecker(expected_warning, _ispytest=True): return func(*args[1:], **kwargs) diff --git a/src/_pytest/setuponly.py b/src/_pytest/setuponly.py index 44a1094c0d2..9a1dd751716 100644 --- a/src/_pytest/setuponly.py +++ b/src/_pytest/setuponly.py @@ -79,7 +79,7 @@ def _show_fixture_action(fixturedef: FixtureDef[object], msg: str) -> None: tw.write(" (fixtures used: {})".format(", ".join(deps))) if hasattr(fixturedef, "cached_param"): - tw.write("[{}]".format(saferepr(fixturedef.cached_param, maxsize=42))) # type: ignore[attr-defined] + tw.write(f"[{saferepr(fixturedef.cached_param, maxsize=42)}]") # type: ignore[attr-defined] tw.flush() diff --git a/src/_pytest/terminal.py b/src/_pytest/terminal.py index 252f898bfa6..01d378abcaf 100644 --- a/src/_pytest/terminal.py +++ b/src/_pytest/terminal.py @@ -588,7 +588,7 @@ def pytest_runtest_logfinish(self, nodeid: str) -> None: if self.verbosity <= 0 and self._show_progress_info: if self._show_progress_info == "count": num_tests = self._session.testscollected - progress_length = len(" [{}/{}]".format(str(num_tests), str(num_tests))) + progress_length = len(f" [{num_tests}/{num_tests}]") else: progress_length = len(" [100%]") @@ -610,7 +610,7 @@ def _get_progress_information_message(self) -> str: if self._show_progress_info == "count": if collected: progress = self._progress_nodeids_reported - counter_format = "{{:{}d}}".format(len(str(collected))) + counter_format = f"{{:{len(str(collected))}d}}" format_string = f" [{counter_format}/{{}}]" return format_string.format(len(progress), collected) return f" [ {collected} / {collected} ]" @@ -704,7 +704,7 @@ def pytest_sessionstart(self, session: "Session") -> None: pypy_version_info = getattr(sys, "pypy_version_info", None) if pypy_version_info: verinfo = ".".join(map(str, pypy_version_info[:3])) - msg += "[pypy-{}-{}]".format(verinfo, pypy_version_info[3]) + msg += f"[pypy-{verinfo}-{pypy_version_info[3]}]" msg += ", pytest-{}, py-{}, pluggy-{}".format( _pytest._version.version, py.__version__, pluggy.__version__ ) @@ -1066,7 +1066,7 @@ def summary_stats(self) -> None: msg = ", ".join(line_parts) main_markup = {main_color: True} - duration = " in {}".format(format_session_duration(session_duration)) + duration = f" in {format_session_duration(session_duration)}" duration_with_markup = self._tw.markup(duration, **main_markup) if display_sep: fullwidth += len(duration_with_markup) - len(duration) diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py index 452849f2bc6..cc3f160d23d 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -188,7 +188,7 @@ def test_not_collectable_arguments(self, pytester: Pytester) -> None: result.stderr.fnmatch_lines( [ f"ERROR: not found: {p2}", - "(no name {!r} in any of [[][]])".format(str(p2)), + f"(no name {str(p2)!r} in any of [[][]])", "", ] ) diff --git a/testing/python/raises.py b/testing/python/raises.py index a3991adaef1..2d62e91091b 100644 --- a/testing/python/raises.py +++ b/testing/python/raises.py @@ -144,7 +144,7 @@ def test_no_raise_message(self) -> None: try: pytest.raises(ValueError, int, "0") except pytest.fail.Exception as e: - assert e.msg == "DID NOT RAISE {}".format(repr(ValueError)) + assert e.msg == f"DID NOT RAISE {repr(ValueError)}" else: assert False, "Expected pytest.raises.Exception" @@ -152,7 +152,7 @@ def test_no_raise_message(self) -> None: with pytest.raises(ValueError): pass except pytest.fail.Exception as e: - assert e.msg == "DID NOT RAISE {}".format(repr(ValueError)) + assert e.msg == f"DID NOT RAISE {repr(ValueError)}" else: assert False, "Expected pytest.raises.Exception" diff --git a/testing/test_warnings.py b/testing/test_warnings.py index 8c9c227b26a..d3c69f1ab52 100644 --- a/testing/test_warnings.py +++ b/testing/test_warnings.py @@ -772,7 +772,7 @@ def test_it(): # with stacklevel=2 the warning should originate from the above created test file result.stdout.fnmatch_lines_random( [ - "*{testfile}:3*".format(testfile=str(testfile)), + f"*{testfile}:3*", "*Unknown pytest.mark.unknown*", ] ) From d2f3d40510f7bede0660d72360897b951e4edceb Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 15 Apr 2021 19:09:06 +0200 Subject: [PATCH 248/630] doc: Update open trainings (#8551) --- doc/en/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/index.rst b/doc/en/index.rst index 098b5f18159..cdaccb104e6 100644 --- a/doc/en/index.rst +++ b/doc/en/index.rst @@ -2,7 +2,7 @@ .. sidebar:: Next Open Trainings - - `Professionelles Testen für Python mit pytest `_ (German), part of the enterPy conference, April 22nd, remote. + - `Professionelles Testen für Python mit pytest `_ (German), part of the enterPy conference, April 22nd (sold out) and May 20th, remote. Also see `previous talks and blogposts `_. From 9078c3ce23882b9dce941b971b919f52ac72c0d5 Mon Sep 17 00:00:00 2001 From: Abdelrahman Elbehery Date: Fri, 16 Apr 2021 19:38:35 +0200 Subject: [PATCH 249/630] fix #8464 wrong root dir when -c is passed (#8537) fix #8464 wrong root dir when -c is passed --- AUTHORS | 1 + changelog/8464.bugfix.rst | 1 + doc/en/reference/customize.rst | 4 +++- src/_pytest/config/findpaths.py | 2 +- testing/test_config.py | 20 ++++++++++++++++++++ 5 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 changelog/8464.bugfix.rst diff --git a/AUTHORS b/AUTHORS index 9b3ec153306..f219c0c834f 100644 --- a/AUTHORS +++ b/AUTHORS @@ -5,6 +5,7 @@ Contributors include:: Aaron Coleman Abdeali JK +Abdelrahman Elbehery Abhijeet Kasurde Adam Johnson Adam Uhlir diff --git a/changelog/8464.bugfix.rst b/changelog/8464.bugfix.rst new file mode 100644 index 00000000000..017c4c078fb --- /dev/null +++ b/changelog/8464.bugfix.rst @@ -0,0 +1 @@ +``-c `` now also properly defines ``rootdir`` as the directory that contains ````. diff --git a/doc/en/reference/customize.rst b/doc/en/reference/customize.rst index 9f7c365dc45..24d2ec93118 100644 --- a/doc/en/reference/customize.rst +++ b/doc/en/reference/customize.rst @@ -145,6 +145,8 @@ Finding the ``rootdir`` Here is the algorithm which finds the rootdir from ``args``: +- If ``-c`` is passed in the command-line, use that as configuration file, and its directory as ``rootdir``. + - Determine the common ancestor directory for the specified ``args`` that are recognised as paths that exist in the file system. If no such paths are found, the common ancestor directory is set to the current working directory. @@ -160,7 +162,7 @@ Here is the algorithm which finds the rootdir from ``args``: ``setup.cfg`` in each of the specified ``args`` and upwards. If one is matched, it becomes the ``configfile`` and its directory becomes the ``rootdir``. -- If no ``configfile`` was found, use the already determined common ancestor as root +- If no ``configfile`` was found and no configuration argument is passed, use the already determined common ancestor as root directory. This allows the use of pytest in structures that are not part of a package and don't have any particular configuration file. diff --git a/src/_pytest/config/findpaths.py b/src/_pytest/config/findpaths.py index 05f21ece5d4..7dde4b92d41 100644 --- a/src/_pytest/config/findpaths.py +++ b/src/_pytest/config/findpaths.py @@ -176,7 +176,7 @@ def determine_setup( inipath: Optional[Path] = inipath_ inicfg = load_config_dict_from_file(inipath_) or {} if rootdir_cmd_arg is None: - rootdir = get_common_ancestor(dirs) + rootdir = inipath_.parent else: ancestor = get_common_ancestor(dirs) rootdir, inipath, inicfg = locate_config([ancestor]) diff --git a/testing/test_config.py b/testing/test_config.py index 1d1a80aa4ad..5f3ff1ea8c4 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -1405,6 +1405,26 @@ def test_with_specific_inifile( assert inipath == p assert ini_config == {"x": "10"} + def test_explicit_config_file_sets_rootdir( + self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch + ) -> None: + tests_dir = tmp_path / "tests" + tests_dir.mkdir() + + monkeypatch.chdir(tmp_path) + + # No config file is explicitly given: rootdir is determined to be cwd. + rootpath, found_inipath, *_ = determine_setup(None, [str(tests_dir)]) + assert rootpath == tmp_path + assert found_inipath is None + + # Config file is explicitly given: rootdir is determined to be inifile's directory. + inipath = tmp_path / "pytest.ini" + inipath.touch() + rootpath, found_inipath, *_ = determine_setup(str(inipath), [str(tests_dir)]) + assert rootpath == tmp_path + assert found_inipath == inipath + def test_with_arg_outside_cwd_without_inifile( self, tmp_path: Path, monkeypatch: MonkeyPatch ) -> None: From c117bc350ec1e570672fda3b2ad234fd52e72b53 Mon Sep 17 00:00:00 2001 From: pytest bot Date: Sun, 18 Apr 2021 00:18:53 +0000 Subject: [PATCH 250/630] [automated] Update plugin list --- doc/en/reference/plugin_list.rst | 36 +++++++++++++++++--------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/doc/en/reference/plugin_list.rst b/doc/en/reference/plugin_list.rst index 409c0a41a36..a69ebd0131d 100644 --- a/doc/en/reference/plugin_list.rst +++ b/doc/en/reference/plugin_list.rst @@ -6,7 +6,7 @@ Plugin List PyPI projects that match "pytest-\*" are considered plugins and are listed automatically. Packages classified as inactive are excluded. -This list contains 852 plugins. +This list contains 854 plugins. ============================================================================================================== ======================================================================================================================================================================== ============== ===================== ================================================ name summary last release status requires @@ -52,7 +52,7 @@ name `pytest-astropy-header `_ pytest plugin to add diagnostic information to the header of the test output Dec 18, 2019 3 - Alpha pytest (>=2.8) `pytest-ast-transformer `_ May 04, 2019 3 - Alpha pytest `pytest-asyncio `_ Pytest support for asyncio. Jun 23, 2020 4 - Beta pytest (>=5.4.0) -`pytest-asyncio-cooperative `_ Run all your asynchronous tests cooperatively. Jan 03, 2021 4 - Beta N/A +`pytest-asyncio-cooperative `_ Run all your asynchronous tests cooperatively. Apr 17, 2021 4 - Beta N/A `pytest-asyncio-network-simulator `_ pytest-asyncio-network-simulator: Plugin for pytest for simulator the network in tests Jul 31, 2018 3 - Alpha pytest (<3.7.0,>=3.3.2) `pytest-async-mongodb `_ pytest plugin for async MongoDB Oct 18, 2017 5 - Production/Stable pytest (>=2.5.2) `pytest-atomic `_ Skip rest of tests if previous test failed. Nov 24, 2018 4 - Beta N/A @@ -74,8 +74,8 @@ name `pytest-beakerlib `_ A pytest plugin that reports test results to the BeakerLib framework Mar 17, 2017 5 - Production/Stable pytest `pytest-beds `_ Fixtures for testing Google Appengine (GAE) apps Jun 07, 2016 4 - Beta N/A `pytest-bench `_ Benchmark utility that plugs into pytest. Jul 21, 2014 3 - Alpha N/A -`pytest-benchmark `_ A ``pytest`` fixture for benchmarking code. It will group the tests into rounds that are calibrated to the chosen timer. See calibration and FAQ. Jan 10, 2020 5 - Production/Stable pytest (>=3.8) -`pytest-bigchaindb `_ A BigchainDB plugin for pytest. Jan 10, 2020 4 - Beta N/A +`pytest-benchmark `_ A ``pytest`` fixture for benchmarking code. It will group the tests into rounds that are calibrated to the chosen timer. Apr 17, 2021 5 - Production/Stable pytest (>=3.8) +`pytest-bigchaindb `_ A BigchainDB plugin for pytest. Apr 17, 2021 4 - Beta N/A `pytest-black `_ A pytest plugin to enable format checking with black Oct 05, 2020 4 - Beta N/A `pytest-black-multipy `_ Allow '--black' on older Pythons Jan 14, 2021 5 - Production/Stable pytest (!=3.7.3,>=3.5) ; extra == 'testing' `pytest-blame `_ A pytest plugin helps developers to debug by providing useful commits history. May 04, 2019 N/A pytest (>=4.4.0) @@ -102,7 +102,7 @@ name `pytest-canonical-data `_ A plugin which allows to compare results with canonical results, based on previous runs May 08, 2020 2 - Pre-Alpha pytest (>=3.5.0) `pytest-caprng `_ A plugin that replays pRNG state on failure. May 02, 2018 4 - Beta N/A `pytest-capture-deprecatedwarnings `_ pytest plugin to capture all deprecatedwarnings and put them in one file Apr 30, 2019 N/A N/A -`pytest-cases `_ Separate test code from test cases in pytest. Apr 07, 2021 5 - Production/Stable N/A +`pytest-cases `_ Separate test code from test cases in pytest. Apr 12, 2021 5 - Production/Stable N/A `pytest-cassandra `_ Cassandra CCM Test Fixtures for pytest Nov 04, 2017 1 - Planning N/A `pytest-catchlog `_ py.test plugin to catch log messages. This is a fork of pytest-capturelog. Jan 24, 2016 4 - Beta pytest (>=2.6) `pytest-catch-server `_ Pytest plugin with server for catching HTTP requests. Dec 12, 2019 5 - Production/Stable N/A @@ -129,7 +129,7 @@ name `pytest-codegen `_ Automatically create pytest test signatures Aug 23, 2020 2 - Pre-Alpha N/A `pytest-codestyle `_ pytest plugin to run pycodestyle Mar 23, 2020 3 - Alpha N/A `pytest-collect-formatter `_ Formatter for pytest collect output Mar 29, 2021 5 - Production/Stable N/A -`pytest-collect-formatter2 `_ Formatter for pytest collect output Apr 09, 2021 5 - Production/Stable N/A +`pytest-collect-formatter2 `_ Formatter for pytest collect output Apr 11, 2021 5 - Production/Stable N/A `pytest-colordots `_ Colorizes the progress indicators Oct 06, 2017 5 - Production/Stable N/A `pytest-commander `_ An interactive GUI test runner for PyTest Jan 31, 2021 N/A pytest (>=5.0.0) `pytest-common-subject `_ pytest framework for testing different aspects of a common method Nov 12, 2020 N/A pytest (>=3.6,<7) @@ -322,7 +322,7 @@ name `pytest-forward-compatibility `_ A pytest plugin to shim pytest commandline options for fowards compatibility Sep 29, 2020 N/A N/A `pytest-freezegun `_ Wrap tests with fixtures in freeze_time Jul 19, 2020 4 - Beta pytest (>=3.0.0) `pytest-freeze-reqs `_ Check if requirement files are frozen Nov 14, 2019 N/A N/A -`pytest-func-cov `_ Pytest plugin for measuring function coverage May 24, 2020 3 - Alpha pytest (>=5) +`pytest-func-cov `_ Pytest plugin for measuring function coverage Apr 15, 2021 3 - Alpha pytest (>=5) `pytest-funparam `_ An alternative way to parametrize test cases Feb 13, 2021 4 - Beta pytest (>=4.6.0) `pytest-fxa `_ pytest plugin for Firefox Accounts Aug 28, 2018 5 - Production/Stable N/A `pytest-fxtest `_ Oct 27, 2020 N/A N/A @@ -355,7 +355,7 @@ name `pytest-historic `_ Custom report to display pytest historical execution records Apr 08, 2020 N/A pytest `pytest-historic-hook `_ Custom listener to store execution results into MYSQL DB, which is used for pytest-historic report Apr 08, 2020 N/A pytest `pytest-homeassistant `_ A pytest plugin for use with homeassistant custom components. Aug 12, 2020 4 - Beta N/A -`pytest-homeassistant-custom-component `_ Experimental package to automatically extract test plugins for Home Assistant custom components Apr 05, 2021 3 - Alpha pytest (==6.2.2) +`pytest-homeassistant-custom-component `_ Experimental package to automatically extract test plugins for Home Assistant custom components Apr 16, 2021 3 - Alpha pytest (==6.2.2) `pytest-honors `_ Report on tests that honor constraints, and guard against regressions Mar 06, 2020 4 - Beta N/A `pytest-hoverfly `_ Simplify working with Hoverfly from pytest Mar 04, 2021 N/A pytest (>=5.0) `pytest-hoverfly-wrapper `_ Integrates the Hoverfly HTTP proxy into Pytest Jan 31, 2021 4 - Beta N/A @@ -402,7 +402,7 @@ name `pytest-jasmine `_ Run jasmine tests from your pytest test suite Nov 04, 2017 1 - Planning N/A `pytest-jest `_ A custom jest-pytest oriented Pytest reporter May 22, 2018 4 - Beta pytest (>=3.3.2) `pytest-jira `_ py.test JIRA integration plugin, using markers Nov 29, 2019 N/A N/A -`pytest-jira-xray `_ pytest plugin to integrate tests with JIRA XRAY Feb 12, 2021 3 - Alpha pytest +`pytest-jira-xray `_ pytest plugin to integrate tests with JIRA XRAY Apr 11, 2021 3 - Alpha pytest `pytest-jobserver `_ Limit parallel tests with posix jobserver. May 15, 2019 5 - Production/Stable pytest `pytest-joke `_ Test failures are better served with humor. Oct 08, 2019 4 - Beta pytest (>=4.2.1) `pytest-json `_ Generate JSON test reports Jan 18, 2016 4 - Beta N/A @@ -434,7 +434,7 @@ name `pytest-localftpserver `_ A PyTest plugin which provides an FTP fixture for your tests Jan 27, 2021 5 - Production/Stable pytest `pytest-localserver `_ py.test plugin to test server connections locally. Nov 14, 2018 4 - Beta N/A `pytest-localstack `_ Pytest plugin for AWS integration tests Aug 22, 2019 4 - Beta pytest (>=3.3.0) -`pytest-lockable `_ lockable resource plugin for pytest Mar 16, 2021 3 - Alpha pytest +`pytest-lockable `_ lockable resource plugin for pytest Apr 14, 2021 3 - Alpha pytest `pytest-locker `_ Used to lock object during testing. Essentially changing assertions from being hard coded to asserting that nothing changed Feb 25, 2021 N/A pytest (>=5.4) `pytest-logbook `_ py.test plugin to capture logbook log messages Nov 23, 2015 5 - Production/Stable pytest (>=2.8) `pytest-logfest `_ Pytest plugin providing three logger fixtures with basic or full writing to log files Jul 21, 2019 4 - Beta pytest (>=3.5.0) @@ -493,7 +493,7 @@ name `pytest-mypy `_ Mypy static type checker plugin for Pytest Mar 21, 2021 4 - Beta pytest (>=3.5) `pytest-mypyd `_ Mypy static type checker plugin for Pytest Aug 20, 2019 4 - Beta pytest (<4.7,>=2.8) ; python_version < "3.5" `pytest-mypy-plugins `_ pytest plugin for writing tests for mypy plugins Oct 26, 2020 3 - Alpha pytest (>=6.0.0) -`pytest-mypy-plugins-shim `_ Substitute for "pytest-mypy-plugins" for Python implementations which aren't supported by mypy. Feb 14, 2021 N/A pytest (>=6.0.0) +`pytest-mypy-plugins-shim `_ Substitute for "pytest-mypy-plugins" for Python implementations which aren't supported by mypy. Apr 12, 2021 N/A N/A `pytest-mypy-testing `_ Pytest plugin to check mypy output. Apr 24, 2020 N/A pytest `pytest-mysql `_ MySQL process and client fixtures for pytest Jul 21, 2020 5 - Production/Stable pytest (>=3.0.0) `pytest-needle `_ pytest plugin for visual testing websites using selenium Dec 10, 2018 4 - Beta pytest (<5.0.0,>=3.0.0) @@ -526,7 +526,7 @@ name `pytest-optional `_ include/exclude values of fixtures in pytest Oct 07, 2015 N/A N/A `pytest-optional-tests `_ Easy declaration of optional tests (i.e., that are not run by default) Jul 09, 2019 4 - Beta pytest (>=4.5.0) `pytest-orchestration `_ A pytest plugin for orchestrating tests Jul 18, 2019 N/A N/A -`pytest-order `_ pytest plugin to run your tests in a specific order Mar 18, 2021 4 - Beta pytest (>=3.7) +`pytest-order `_ pytest plugin to run your tests in a specific order Apr 11, 2021 4 - Beta pytest (>=3.7) `pytest-ordering `_ pytest plugin to run your tests in a specific order Nov 14, 2018 4 - Beta pytest `pytest-osxnotify `_ OS X notifications for py.test results. May 15, 2015 N/A N/A `pytest-pact `_ A simple plugin to use with pytest Jan 07, 2019 4 - Beta N/A @@ -598,6 +598,7 @@ name `pytest-pyramid-server `_ Pyramid server fixture for py.test May 28, 2019 5 - Production/Stable pytest `pytest-pytestrail `_ Pytest plugin for interaction with TestRail Aug 27, 2020 4 - Beta pytest (>=3.8.0) `pytest-pythonpath `_ pytest plugin for adding to the PYTHONPATH from command line or configs. Aug 22, 2018 5 - Production/Stable N/A +`pytest-pytorch `_ pytest plugin for a better developer experience when working with the PyTorch test suite Apr 14, 2021 4 - Beta pytest `pytest-qml `_ Run QML Tests with pytest Dec 02, 2020 4 - Beta pytest (>=6.0.0) `pytest-qt `_ pytest support for PyQt and PySide applications Dec 07, 2019 5 - Production/Stable pytest (>=3.0.0) `pytest-qt-app `_ QT app fixture for py.test Dec 23, 2015 5 - Production/Stable N/A @@ -610,7 +611,7 @@ name `pytest-raisesregexp `_ Simple pytest plugin to look for regex in Exceptions Dec 18, 2015 N/A N/A `pytest-raisin `_ Plugin enabling the use of exception instances with pytest.raises Jun 25, 2020 N/A pytest `pytest-random `_ py.test plugin to randomize tests Apr 28, 2013 3 - Alpha N/A -`pytest-randomly `_ Pytest plugin to randomly order tests and control random.seed. Apr 01, 2021 5 - Production/Stable pytest +`pytest-randomly `_ Pytest plugin to randomly order tests and control random.seed. Apr 11, 2021 5 - Production/Stable pytest `pytest-randomness `_ Pytest plugin about random seed management May 30, 2019 3 - Alpha N/A `pytest-random-num `_ Randomise the order in which pytest tests are run with some control over the randomness Oct 19, 2020 5 - Production/Stable N/A `pytest-random-order `_ Randomise the order in which pytest tests are run with some control over the randomness Nov 30, 2018 5 - Production/Stable pytest (>=3.0.0) @@ -646,7 +647,7 @@ name `pytest-reraise `_ Make multi-threaded pytest test cases fail when they should Jun 03, 2020 5 - Production/Stable N/A `pytest-rerun `_ Re-run only changed files in specified branch Jul 08, 2019 N/A pytest (>=3.6) `pytest-rerunfailures `_ pytest plugin to re-run tests to eliminate flaky failures Sep 29, 2020 5 - Production/Stable pytest (>=5.0) -`pytest-resilient-circuits `_ Resilient Circuits fixtures for PyTest. Mar 25, 2021 N/A N/A +`pytest-resilient-circuits `_ Resilient Circuits fixtures for PyTest. Apr 15, 2021 N/A N/A `pytest-resource `_ Load resource fixture plugin to use with pytest Nov 14, 2018 4 - Beta N/A `pytest-resource-path `_ Provides path for uniform access to test resources in isolated directory Aug 18, 2020 5 - Production/Stable pytest (>=3.5.0) `pytest-responsemock `_ Simplified requests calls mocking for pytest Oct 10, 2020 5 - Production/Stable N/A @@ -657,6 +658,7 @@ name `pytest-ringo `_ pytest plugin to test webapplications using the Ringo webframework Sep 27, 2017 3 - Alpha N/A `pytest-rng `_ Fixtures for seeding tests and making randomness reproducible Aug 08, 2019 5 - Production/Stable pytest `pytest-roast `_ pytest plugin for ROAST configuration override and fixtures Apr 05, 2021 5 - Production/Stable pytest +`pytest-rocketchat `_ Pytest to Rocket.Chat reporting plugin Apr 11, 2021 5 - Production/Stable N/A `pytest-rotest `_ Pytest integration with rotest Sep 08, 2019 N/A pytest (>=3.5.0) `pytest-rpc `_ Extend py.test for RPC OpenStack testing. Feb 22, 2019 4 - Beta pytest (~=3.6) `pytest-rt `_ pytest data collector plugin for Testgr Mar 03, 2021 N/A N/A @@ -666,19 +668,19 @@ name `pytest-runner `_ Invoke py.test as distutils command with dependency resolution Feb 12, 2021 5 - Production/Stable pytest (!=3.7.3,>=3.5) ; extra == 'testing' `pytest-salt `_ Pytest Salt Plugin Jan 27, 2020 4 - Beta N/A `pytest-salt-containers `_ A Pytest plugin that builds and creates docker containers Nov 09, 2016 4 - Beta N/A -`pytest-salt-factories `_ Pytest Salt Plugin Apr 06, 2021 4 - Beta pytest (>=6.1.1) +`pytest-salt-factories `_ Pytest Salt Plugin Apr 14, 2021 4 - Beta pytest (>=6.1.1) `pytest-salt-from-filenames `_ Simple PyTest Plugin For Salt's Test Suite Specifically Jan 29, 2019 4 - Beta pytest (>=4.1) `pytest-salt-runtests-bridge `_ Simple PyTest Plugin For Salt's Test Suite Specifically Dec 05, 2019 4 - Beta pytest (>=4.1) `pytest-sanic `_ a pytest plugin for Sanic Feb 27, 2021 N/A pytest (>=5.2) `pytest-sanity `_ Dec 07, 2020 N/A N/A `pytest-sa-pg `_ May 14, 2019 N/A N/A -`pytest-sbase `_ A complete web automation framework for end-to-end testing. Apr 10, 2021 5 - Production/Stable N/A +`pytest-sbase `_ A complete web automation framework for end-to-end testing. Apr 16, 2021 5 - Production/Stable N/A `pytest-scenario `_ pytest plugin for test scenarios Feb 06, 2017 3 - Alpha N/A `pytest-schema `_ 👍 Validate return values against a schema-like object in testing Aug 31, 2020 5 - Production/Stable pytest (>=3.5.0) `pytest-securestore `_ An encrypted password store for use within pytest cases Jun 19, 2019 4 - Beta N/A `pytest-select `_ A pytest plugin which allows to (de-)select tests from a file. Jan 18, 2019 3 - Alpha pytest (>=3.0) `pytest-selenium `_ pytest plugin for Selenium Sep 19, 2020 5 - Production/Stable pytest (>=5.0.0) -`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Apr 10, 2021 5 - Production/Stable N/A +`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Apr 16, 2021 5 - Production/Stable N/A `pytest-selenium-enhancer `_ pytest plugin for Selenium Nov 26, 2020 5 - Production/Stable N/A `pytest-selenium-pdiff `_ A pytest package implementing perceptualdiff for Selenium tests. Apr 06, 2017 2 - Pre-Alpha N/A `pytest-send-email `_ Send pytest execution result email Dec 04, 2019 N/A N/A From fb35851440e0635a3c144a79acd07c2f7cae6f02 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 Apr 2021 03:01:33 +0000 Subject: [PATCH 251/630] Bump pytest-asyncio in /testing/plugins_integration Bumps [pytest-asyncio](https://github.com/pytest-dev/pytest-asyncio) from 0.14.0 to 0.15.0. - [Release notes](https://github.com/pytest-dev/pytest-asyncio/releases) - [Commits](https://github.com/pytest-dev/pytest-asyncio/compare/v0.14.0...v0.15.0) Signed-off-by: dependabot[bot] --- testing/plugins_integration/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/plugins_integration/requirements.txt b/testing/plugins_integration/requirements.txt index 8a00129c4e5..567c492c431 100644 --- a/testing/plugins_integration/requirements.txt +++ b/testing/plugins_integration/requirements.txt @@ -1,6 +1,6 @@ anyio[curio,trio]==2.2.0 django==3.2 -pytest-asyncio==0.14.0 +pytest-asyncio==0.15.0 pytest-bdd==4.0.2 pytest-cov==2.11.1 pytest-django==4.2.0 From 5d7fe91bd5d6aa95ddc7ef6e409969bdd497510d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 19 Apr 2021 17:11:11 +0000 Subject: [PATCH 252/630] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/PyCQA/flake8: 3.9.0 → 3.9.1](https://github.com/PyCQA/flake8/compare/3.9.0...3.9.1) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 71cccd907ee..0e4b8284d6c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ repos: exclude: _pytest/(debugging|hookspec).py language_version: python3 - repo: https://github.com/PyCQA/flake8 - rev: 3.9.0 + rev: 3.9.1 hooks: - id: flake8 language_version: python3 From fbe66244b8b9e2aabe14a524565664eeca42dc90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0ediv=C3=BD?= <6774676+eumiro@users.noreply.github.com> Date: Mon, 19 Apr 2021 22:39:08 +0200 Subject: [PATCH 253/630] Fix some typos, remove redundant words and escapes (#8564) * doc: Fix typos, remove double words * Remove redundant escapes in regex --- doc/en/changelog.rst | 4 ++-- scripts/release-on-comment.py | 2 +- src/_pytest/capture.py | 2 +- src/_pytest/doctest.py | 2 +- src/_pytest/store.py | 2 +- testing/logging/test_reporting.py | 18 +++++++++--------- testing/python/metafunc.py | 5 ++--- 7 files changed, 17 insertions(+), 18 deletions(-) diff --git a/doc/en/changelog.rst b/doc/en/changelog.rst index b07996ae2e7..0ff7ede3d8d 100644 --- a/doc/en/changelog.rst +++ b/doc/en/changelog.rst @@ -5097,7 +5097,7 @@ Improved Documentation - In one of the simple examples, use ``pytest_collection_modifyitems()`` to skip tests based on a command-line option, allowing its sharing while preventing a - user error when acessing ``pytest.config`` before the argument parsing. + user error when accessing ``pytest.config`` before the argument parsing. (`#2653 `_) @@ -5509,7 +5509,7 @@ Changes Thanks `@ojii`_ for the PR. * Replace minor/patch level version numbers in the documentation with placeholders. - This significantly reduces change-noise as different contributors regnerate + This significantly reduces change-noise as different contributors regenerate the documentation on different platforms. Thanks `@RonnyPfannschmidt`_ for the PR. diff --git a/scripts/release-on-comment.py b/scripts/release-on-comment.py index 3f4c1ed5707..f33def3efc7 100644 --- a/scripts/release-on-comment.py +++ b/scripts/release-on-comment.py @@ -82,7 +82,7 @@ def validate_and_get_issue_comment_payload( ) -> Tuple[str, str, bool]: payload = json.loads(issue_payload_path.read_text(encoding="UTF-8")) body = get_comment_data(payload)["body"] - m = re.match(r"@pytestbot please prepare (major )?release from ([\w\-_\.]+)", body) + m = re.match(r"@pytestbot please prepare (major )?release from ([-_.\w]+)", body) if m: is_major, base_branch = m.group(1) is not None, m.group(2) else: diff --git a/src/_pytest/capture.py b/src/_pytest/capture.py index 355f42591a7..9e2d3fa6233 100644 --- a/src/_pytest/capture.py +++ b/src/_pytest/capture.py @@ -363,7 +363,7 @@ def __init__(self, targetfd: int) -> None: except OSError: # FD capturing is conceptually simple -- create a temporary file, # redirect the FD to it, redirect back when done. But when the - # target FD is invalid it throws a wrench into this loveley scheme. + # target FD is invalid it throws a wrench into this lovely scheme. # # Tests themselves shouldn't care if the FD is valid, FD capturing # should work regardless of external circumstances. So falling back diff --git a/src/_pytest/doctest.py b/src/_pytest/doctest.py index 9b877cfd9b2..3547c422eed 100644 --- a/src/_pytest/doctest.py +++ b/src/_pytest/doctest.py @@ -268,7 +268,7 @@ def from_parent( # type: ignore runner: "doctest.DocTestRunner", dtest: "doctest.DocTest", ): - # incompatible signature due to to imposed limits on sublcass + # incompatible signature due to imposed limits on subclass """The public named constructor.""" return super().from_parent(name=name, parent=parent, runner=runner, dtest=dtest) diff --git a/src/_pytest/store.py b/src/_pytest/store.py index e5008cfc5a1..43fd9e89e6b 100644 --- a/src/_pytest/store.py +++ b/src/_pytest/store.py @@ -25,7 +25,7 @@ class StoreKey(Generic[T]): class Store: - """Store is a type-safe heterogenous mutable mapping that + """Store is a type-safe heterogeneous mutable mapping that allows keys and value types to be defined separately from where it (the Store) is created. diff --git a/testing/logging/test_reporting.py b/testing/logging/test_reporting.py index a5ab8b98ba7..323ff7b2446 100644 --- a/testing/logging/test_reporting.py +++ b/testing/logging/test_reporting.py @@ -235,7 +235,7 @@ def test_log_cli(request): ] ) result.stdout.no_fnmatch_line("*INFO message won't be shown*") - # make sure that that we get a '0' exit code for the testsuite + # make sure that we get a '0' exit code for the testsuite assert result.ret == 0 @@ -528,7 +528,7 @@ def test_log_cli(request): ) result.stdout.no_fnmatch_line("*This log message won't be shown*") - # make sure that that we get a '0' exit code for the testsuite + # make sure that we get a '0' exit code for the testsuite assert result.ret == 0 result = pytester.runpytest("-s", "--log-level=INFO") @@ -542,7 +542,7 @@ def test_log_cli(request): ) result.stdout.no_fnmatch_line("*This log message won't be shown*") - # make sure that that we get a '0' exit code for the testsuite + # make sure that we get a '0' exit code for the testsuite assert result.ret == 0 @@ -578,7 +578,7 @@ def test_log_cli(request): ) result.stdout.no_fnmatch_line("*This log message won't be shown*") - # make sure that that we get a '0' exit code for the testsuite + # make sure that we get a '0' exit code for the testsuite assert result.ret == 0 @@ -653,7 +653,7 @@ def test_log_file(request): # fnmatch_lines does an assertion internally result.stdout.fnmatch_lines(["test_log_file_cli.py PASSED"]) - # make sure that that we get a '0' exit code for the testsuite + # make sure that we get a '0' exit code for the testsuite assert result.ret == 0 assert os.path.isfile(log_file) with open(log_file) as rfh: @@ -684,7 +684,7 @@ def test_log_file(request): # fnmatch_lines does an assertion internally result.stdout.fnmatch_lines(["test_log_file_cli_level.py PASSED"]) - # make sure that that we get a '0' exit code for the testsuite + # make sure that we get a '0' exit code for the testsuite assert result.ret == 0 assert os.path.isfile(log_file) with open(log_file) as rfh: @@ -735,7 +735,7 @@ def test_log_file(request): # fnmatch_lines does an assertion internally result.stdout.fnmatch_lines(["test_log_file_ini.py PASSED"]) - # make sure that that we get a '0' exit code for the testsuite + # make sure that we get a '0' exit code for the testsuite assert result.ret == 0 assert os.path.isfile(log_file) with open(log_file) as rfh: @@ -774,7 +774,7 @@ def test_log_file(request): # fnmatch_lines does an assertion internally result.stdout.fnmatch_lines(["test_log_file_ini_level.py PASSED"]) - # make sure that that we get a '0' exit code for the testsuite + # make sure that we get a '0' exit code for the testsuite assert result.ret == 0 assert os.path.isfile(log_file) with open(log_file) as rfh: @@ -808,7 +808,7 @@ def test_log_file(): result = pytester.runpytest() - # make sure that that we get a '0' exit code for the testsuite + # make sure that we get a '0' exit code for the testsuite assert result.ret == 0 assert os.path.isfile(log_file) with open(log_file, encoding="utf-8") as rfh: diff --git a/testing/python/metafunc.py b/testing/python/metafunc.py index 6577ff18ebf..4a4b141c4b7 100644 --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -692,9 +692,8 @@ def test_parametrize_indirect_list_functional(self, pytester: Pytester) -> None: """ #714 Test parametrization with 'indirect' parameter applied on - particular arguments. As y is is direct, its value should - be used directly rather than being passed to the fixture - y. + particular arguments. As y is direct, its value should + be used directly rather than being passed to the fixture y. :param pytester: the instance of Pytester class, a temporary test directory. From 3cbbb0bb0da3625cb51bb39969dfc9bd9e35e919 Mon Sep 17 00:00:00 2001 From: Johannes Maron Date: Tue, 20 Apr 2021 12:30:27 +0200 Subject: [PATCH 254/630] Fix typos & missing commas in CONTRIBUTING.rst --- CONTRIBUTING.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 5855d47914f..bbc341e0f1d 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -409,7 +409,7 @@ Who does the backporting ~~~~~~~~~~~~~~~~~~~~~~~~ As mentioned above, bugs should first be fixed on ``main`` (except in rare occasions -that a bug only happens in a previous release). So who should do the backport procedure described +that a bug only happens in a previous release). So, who should do the backport procedure described above? 1. If the bug was fixed by a core developer, it is the main responsibility of that core developer @@ -447,7 +447,7 @@ can always reopen the issue/pull request in their own time later if it makes sen When to close ~~~~~~~~~~~~~ -Here are a few general rules the maintainers use to decide when to close issues/PRs because +Here are a few general rules the maintainers use deciding when to close issues/PRs because of lack of inactivity: * Issues labeled ``question`` or ``needs information``: closed after 14 days inactive. @@ -459,15 +459,15 @@ The above are **not hard rules**, but merely **guidelines**, and can be (and oft Closing pull requests ~~~~~~~~~~~~~~~~~~~~~ -When closing a Pull Request, it needs to be acknowledge the time, effort, and interest demonstrated by the person which submitted it. As mentioned previously, it is not the intent of the team to dismiss stalled pull request entirely but to merely to clear up our queue, so a message like the one below is warranted when closing a pull request that went stale: +When closing a Pull Request, it needs to be acknowledging the time, effort, and interest demonstrated by the person which submitted it. As mentioned previously, it is not the intent of the team to dismiss a stalled pull request entirely but to merely to clear up our queue, so a message like the one below is warranted when closing a pull request that went stale: Hi , - First of all we would like to thank you for your time and effort on working on this, the pytest team deeply appreciates it. + First of all, we would like to thank you for your time and effort on working on this, the pytest team deeply appreciates it. We noticed it has been awhile since you have updated this PR, however. pytest is a high activity project, with many issues/PRs being opened daily, so it is hard for us maintainers to track which PRs are ready for merging, for review, or need more attention. - So for those reasons we think it is best to close the PR for now, but with the only intention to cleanup our queue, it is by no means a rejection of your changes. We still encourage you to re-open this PR (it is just a click of a button away) when you are ready to get back to it. + So for those reasons we, think it is best to close the PR for now, but with the only intention to clean up our queue, it is by no means a rejection of your changes. We still encourage you to re-open this PR (it is just a click of a button away) when you are ready to get back to it. Again we appreciate your time for working on this, and hope you might get back to this at a later time! From 7e82f6fc38e4b48860e3e98f657cc7d0d8deef24 Mon Sep 17 00:00:00 2001 From: Stefaan Lippens Date: Tue, 20 Apr 2021 15:18:53 +0200 Subject: [PATCH 255/630] Logging docs: fix reference to pytest.LogCaptureFixture. --- doc/en/how-to/logging.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/how-to/logging.rst b/doc/en/how-to/logging.rst index 317d6134e1b..ef477dc4f8c 100644 --- a/doc/en/how-to/logging.rst +++ b/doc/en/how-to/logging.rst @@ -167,7 +167,7 @@ the records for the ``setup`` and ``call`` stages during teardown like so: -The full API is available at :class:`_pytest.logging.LogCaptureFixture`. +The full API is available at :class:`pytest.LogCaptureFixture`. .. _live_logs: From f9d1fa4c4be59ffee39b4bebff2e88afe44501cb Mon Sep 17 00:00:00 2001 From: Dominic Davis-Foster Date: Wed, 21 Apr 2021 16:46:28 +0100 Subject: [PATCH 256/630] Remove "upload_sphinx" section from setup.cfg The `Sphinx-PyPI-upload` tool the section corresponds to hasn't been updated since 2009, and the new (c. 2018) PyPI doesn't support documentation uploads anyway. --- setup.cfg | 3 --- 1 file changed, 3 deletions(-) diff --git a/setup.cfg b/setup.cfg index e9b8e597fb7..111b36f46ed 100644 --- a/setup.cfg +++ b/setup.cfg @@ -82,9 +82,6 @@ source_dir = doc/en/ build_dir = doc/build all_files = 1 -[upload_sphinx] -upload_dir = doc/en/build/html - [check-manifest] ignore = src/_pytest/_version.py From 0a75c8e57b4b87ee533f3b08612590704b6743bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Thu, 22 Apr 2021 19:13:44 +0200 Subject: [PATCH 257/630] Add a regression test for a more defensive code.FormattedExcinfo.get_source --- testing/code/test_excinfo.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/testing/code/test_excinfo.py b/testing/code/test_excinfo.py index e6a9cbaf737..287733dac74 100644 --- a/testing/code/test_excinfo.py +++ b/testing/code/test_excinfo.py @@ -1397,6 +1397,29 @@ def test(tmp_path): result.stderr.no_fnmatch_line("*INTERNALERROR*") +def test_regression_nagative_line_index(pytester: Pytester) -> None: + """ + With Python 3.10 alphas, there was an INTERNALERROR reported in + https://github.com/pytest-dev/pytest/pull/8227 + This test ensures it does not regress. + """ + pytester.makepyfile( + """ + import ast + import pytest + + + def test_literal_eval(): + with pytest.raises(ValueError, match="^$"): + ast.literal_eval("pytest") + """ + ) + result = pytester.runpytest() + result.stdout.fnmatch_lines(["* 1 failed in *"]) + result.stdout.no_fnmatch_line("*INTERNALERROR*") + result.stderr.no_fnmatch_line("*INTERNALERROR*") + + @pytest.mark.usefixtures("limited_recursion_depth") def test_exception_repr_extraction_error_on_recursion(): """ From c9e30a703564689eac04ee9d10a2eb47bd970262 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sat, 24 Apr 2021 10:09:11 -0300 Subject: [PATCH 258/630] Make pygments part of 'testing' extras Tests in test_terminalwriter depend on pygments being installed to work, so it should be installed with the `testing` extras. --- setup.cfg | 1 + tox.ini | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index e9b8e597fb7..f65866b36d7 100644 --- a/setup.cfg +++ b/setup.cfg @@ -70,6 +70,7 @@ testing = hypothesis>=3.56 mock nose + pygments>=2.7.2 requests xmlschema diff --git a/tox.ini b/tox.ini index b64608adcf9..1c1d2bb2c1f 100644 --- a/tox.ini +++ b/tox.ini @@ -47,7 +47,6 @@ deps = numpy: numpy>=1.19.4 pexpect: pexpect>=4.8.0 pluggymain: pluggy @ git+https://github.com/pytest-dev/pluggy.git - pygments>=2.7.2 unittestextras: twisted unittestextras: asynctest xdist: pytest-xdist>=2.1.0 From 788d445b7b1f2ddc04e97dfdd3e57a833d71f8eb Mon Sep 17 00:00:00 2001 From: pytest bot Date: Sun, 25 Apr 2021 00:20:16 +0000 Subject: [PATCH 259/630] [automated] Update plugin list --- doc/en/reference/plugin_list.rst | 56 +++++++++++++++++--------------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/doc/en/reference/plugin_list.rst b/doc/en/reference/plugin_list.rst index a69ebd0131d..a25c7fb3874 100644 --- a/doc/en/reference/plugin_list.rst +++ b/doc/en/reference/plugin_list.rst @@ -6,12 +6,12 @@ Plugin List PyPI projects that match "pytest-\*" are considered plugins and are listed automatically. Packages classified as inactive are excluded. -This list contains 854 plugins. +This list contains 856 plugins. ============================================================================================================== ======================================================================================================================================================================== ============== ===================== ================================================ name summary last release status requires ============================================================================================================== ======================================================================================================================================================================== ============== ===================== ================================================ -`pytest-adaptavist `_ pytest plugin for generating test execution results within Jira Test Management (tm4j) Feb 05, 2020 N/A pytest (>=3.4.1) +`pytest-adaptavist `_ pytest plugin for generating test execution results within Jira Test Management (tm4j) Apr 22, 2021 N/A pytest (>=3.4.1) `pytest-adf `_ Pytest plugin for writing Azure Data Factory integration tests Mar 10, 2021 4 - Beta pytest (>=3.5.0) `pytest-adf-azure-identity `_ Pytest plugin for writing Azure Data Factory integration tests Mar 06, 2021 4 - Beta pytest (>=3.5.0) `pytest-aggreport `_ pytest plugin for pytest-repeat that generate aggregate report of the same test cases with additional statistics details. Mar 07, 2021 4 - Beta pytest (>=6.2.2) @@ -51,7 +51,7 @@ name `pytest-astropy `_ Meta-package containing dependencies for testing Jan 16, 2020 5 - Production/Stable pytest (>=4.6) `pytest-astropy-header `_ pytest plugin to add diagnostic information to the header of the test output Dec 18, 2019 3 - Alpha pytest (>=2.8) `pytest-ast-transformer `_ May 04, 2019 3 - Alpha pytest -`pytest-asyncio `_ Pytest support for asyncio. Jun 23, 2020 4 - Beta pytest (>=5.4.0) +`pytest-asyncio `_ Pytest support for asyncio. Apr 21, 2021 4 - Beta pytest (>=5.4.0) `pytest-asyncio-cooperative `_ Run all your asynchronous tests cooperatively. Apr 17, 2021 4 - Beta N/A `pytest-asyncio-network-simulator `_ pytest-asyncio-network-simulator: Plugin for pytest for simulator the network in tests Jul 31, 2018 3 - Alpha pytest (<3.7.0,>=3.3.2) `pytest-async-mongodb `_ pytest plugin for async MongoDB Oct 18, 2017 5 - Production/Stable pytest (>=2.5.2) @@ -92,6 +92,7 @@ name `pytest-browsermob-proxy `_ BrowserMob proxy plugin for py.test. Jun 11, 2013 4 - Beta N/A `pytest-browserstack-local `_ ``py.test`` plugin to run ``BrowserStackLocal`` in background. Feb 09, 2018 N/A N/A `pytest-bug `_ Pytest plugin for marking tests as a bug Jun 02, 2020 5 - Production/Stable pytest (>=3.6.0) +`pytest-bugtong-tag `_ pytest-bugtong-tag is a plugin for pytest Apr 23, 2021 N/A N/A `pytest-bugzilla `_ py.test bugzilla integration plugin May 05, 2010 4 - Beta N/A `pytest-bugzilla-notifier `_ A plugin that allows you to execute create, update, and read information from BugZilla bugs Jun 15, 2018 4 - Beta pytest (>=2.9.2) `pytest-buildkite `_ Plugin for pytest that automatically publishes coverage and pytest report annotations to Buildkite. Jul 13, 2019 4 - Beta pytest (>=3.5.0) @@ -111,7 +112,7 @@ name `pytest-change-report `_ turn . into √,turn F into x Sep 14, 2020 N/A pytest `pytest-chdir `_ A pytest fixture for changing current working directory Jan 28, 2020 N/A pytest (>=5.0.0,<6.0.0) `pytest-check `_ A pytest plugin that allows multiple failures per test. Dec 27, 2020 5 - Production/Stable N/A -`pytest-checkdocs `_ check the README when running tests Mar 28, 2021 5 - Production/Stable pytest (>=4.6) ; extra == 'testing' +`pytest-checkdocs `_ check the README when running tests Apr 18, 2021 5 - Production/Stable pytest (>=4.6) ; extra == 'testing' `pytest-checkipdb `_ plugin to check if there are ipdb debugs left Jul 22, 2020 5 - Production/Stable pytest (>=2.9.2) `pytest-check-links `_ Check links in files Jul 29, 2020 N/A pytest (>=4.6) `pytest-check-mk `_ pytest plugin to test Check_MK checks Nov 19, 2015 4 - Beta pytest @@ -153,7 +154,7 @@ name `pytest-crate `_ Manages CrateDB instances during your integration tests May 28, 2019 3 - Alpha pytest (>=4.0) `pytest-cricri `_ A Cricri plugin for pytest. Jan 27, 2018 N/A pytest `pytest-crontab `_ add crontab task in crontab Dec 09, 2019 N/A N/A -`pytest-csv `_ CSV output for pytest. Jun 24, 2019 N/A pytest (>=4.4) +`pytest-csv `_ CSV output for pytest. Apr 22, 2021 N/A pytest (>=6.0) `pytest-curio `_ Pytest support for curio. Oct 07, 2020 N/A N/A `pytest-curl-report `_ pytest plugin to generate curl command line report Dec 11, 2016 4 - Beta N/A `pytest-custom-concurrency `_ Custom grouping concurrence for pytest Feb 08, 2021 N/A N/A @@ -284,7 +285,7 @@ name `pytest-factoryboy `_ Factory Boy support for pytest. Dec 30, 2020 6 - Mature pytest (>=4.6) `pytest-factoryboy-fixtures `_ Generates pytest fixtures that allow the use of type hinting Jun 25, 2020 N/A N/A `pytest-factoryboy-state `_ Simple factoryboy random state management Dec 11, 2020 4 - Beta pytest (>=5.0) -`pytest-failed-screenshot `_ Test case fails,take a screenshot,save it,attach it to the allure Feb 28, 2021 N/A N/A +`pytest-failed-screenshot `_ Test case fails,take a screenshot,save it,attach it to the allure Apr 21, 2021 N/A N/A `pytest-failed-to-verify `_ A pytest plugin that helps better distinguishing real test failures from setup flakiness. Aug 08, 2019 5 - Production/Stable pytest (>=4.1.0) `pytest-faker `_ Faker integration with the pytest framework. Dec 19, 2016 6 - Mature N/A `pytest-falcon `_ Pytest helpers for Falcon. Sep 07, 2016 4 - Beta N/A @@ -300,7 +301,7 @@ name `pytest-filemarker `_ A pytest plugin that runs marked tests when files change. Dec 01, 2020 N/A pytest `pytest-filter-case `_ run test cases filter by mark Nov 05, 2020 N/A N/A `pytest-filter-subpackage `_ Pytest plugin for filtering based on sub-packages Jan 09, 2020 3 - Alpha pytest (>=3.0) -`pytest-find-dependencies `_ A pytest plugin to find dependencies between tests Apr 07, 2021 4 - Beta pytest (>=3.5.0) +`pytest-find-dependencies `_ A pytest plugin to find dependencies between tests Apr 21, 2021 4 - Beta pytest (>=3.5.0) `pytest-finer-verdicts `_ A pytest plugin to treat non-assertion failures as test errors. Jun 18, 2020 N/A pytest (>=5.4.3) `pytest-firefox `_ pytest plugin to manipulate firefox Aug 08, 2017 3 - Alpha pytest (>=3.0.2) `pytest-fixture-config `_ Fixture configuration utils for py.test May 28, 2019 5 - Production/Stable pytest @@ -323,7 +324,7 @@ name `pytest-freezegun `_ Wrap tests with fixtures in freeze_time Jul 19, 2020 4 - Beta pytest (>=3.0.0) `pytest-freeze-reqs `_ Check if requirement files are frozen Nov 14, 2019 N/A N/A `pytest-func-cov `_ Pytest plugin for measuring function coverage Apr 15, 2021 3 - Alpha pytest (>=5) -`pytest-funparam `_ An alternative way to parametrize test cases Feb 13, 2021 4 - Beta pytest (>=4.6.0) +`pytest-funparam `_ An alternative way to parametrize test cases Apr 23, 2021 4 - Beta pytest (>=4.6.0) `pytest-fxa `_ pytest plugin for Firefox Accounts Aug 28, 2018 5 - Production/Stable N/A `pytest-fxtest `_ Oct 27, 2020 N/A N/A `pytest-gc `_ The garbage collector plugin for py.test Feb 01, 2018 N/A N/A @@ -331,7 +332,7 @@ name `pytest-gevent `_ Ensure that gevent is properly patched when invoking pytest Feb 25, 2020 N/A pytest `pytest-gherkin `_ A flexible framework for executing BDD gherkin tests Jul 27, 2019 3 - Alpha pytest (>=5.0.0) `pytest-ghostinspector `_ For finding/executing Ghost Inspector tests May 17, 2016 3 - Alpha N/A -`pytest-girder `_ A set of pytest fixtures for testing Girder applications. Apr 06, 2021 N/A N/A +`pytest-girder `_ A set of pytest fixtures for testing Girder applications. Apr 19, 2021 N/A N/A `pytest-git `_ Git repository fixture for py.test May 28, 2019 5 - Production/Stable pytest `pytest-gitcov `_ Pytest plugin for reporting on coverage of the last git commit. Jan 11, 2020 2 - Pre-Alpha N/A `pytest-git-fixtures `_ Pytest fixtures for testing with git. Mar 11, 2021 4 - Beta pytest @@ -362,7 +363,7 @@ name `pytest-html `_ pytest plugin for generating HTML reports Dec 13, 2020 5 - Production/Stable pytest (!=6.0.0,>=5.0) `pytest-html-lee `_ optimized pytest plugin for generating HTML reports Jun 30, 2020 5 - Production/Stable pytest (>=5.0) `pytest-html-profiling `_ Pytest plugin for generating HTML reports with per-test profiling and optionally call graph visualizations. Based on pytest-html by Dave Hunt. Feb 11, 2020 5 - Production/Stable pytest (>=3.0) -`pytest-html-reporter `_ Generates a static html report based on pytest framework Sep 28, 2020 N/A N/A +`pytest-html-reporter `_ Generates a static html report based on pytest framework Apr 20, 2021 N/A N/A `pytest-html-thread `_ pytest plugin for generating HTML reports Dec 29, 2020 5 - Production/Stable N/A `pytest-http `_ Fixture "http" for http requests Dec 05, 2019 N/A N/A `pytest-httpbin `_ Easily test your HTTP library against a local copy of httpbin Feb 11, 2019 5 - Production/Stable N/A @@ -377,10 +378,10 @@ name `pytest-ibutsu `_ A plugin to sent pytest results to an Ibutsu server Mar 09, 2021 4 - Beta pytest `pytest-icdiff `_ use icdiff for better error messages in pytest assertions Apr 08, 2020 4 - Beta N/A `pytest-idapro `_ A pytest plugin for idapython. Allows a pytest setup to run tests outside and inside IDA in an automated manner by runnig pytest inside IDA and by mocking idapython api Nov 03, 2018 N/A N/A -`pytest-ignore-flaky `_ ignore failures from flaky tests (pytest plugin) Jan 14, 2019 5 - Production/Stable pytest (>=3.7) +`pytest-ignore-flaky `_ ignore failures from flaky tests (pytest plugin) Apr 23, 2021 5 - Production/Stable N/A `pytest-image-diff `_ Sep 03, 2020 3 - Alpha pytest -`pytest-incremental `_ an incremental test runner (pytest plugin) Dec 09, 2018 4 - Beta N/A -`pytest-influxdb `_ Plugin for influxdb and pytest integration. Sep 22, 2020 N/A N/A +`pytest-incremental `_ an incremental test runner (pytest plugin) Apr 24, 2021 5 - Production/Stable N/A +`pytest-influxdb `_ Plugin for influxdb and pytest integration. Apr 20, 2021 N/A N/A `pytest-info-collector `_ pytest plugin to collect information from tests May 26, 2019 3 - Alpha N/A `pytest-informative-node `_ display more node ininformation. Apr 25, 2019 4 - Beta N/A `pytest-infrastructure `_ pytest stack validation prior to testing executing Apr 12, 2020 4 - Beta N/A @@ -402,7 +403,7 @@ name `pytest-jasmine `_ Run jasmine tests from your pytest test suite Nov 04, 2017 1 - Planning N/A `pytest-jest `_ A custom jest-pytest oriented Pytest reporter May 22, 2018 4 - Beta pytest (>=3.3.2) `pytest-jira `_ py.test JIRA integration plugin, using markers Nov 29, 2019 N/A N/A -`pytest-jira-xray `_ pytest plugin to integrate tests with JIRA XRAY Apr 11, 2021 3 - Alpha pytest +`pytest-jira-xray `_ pytest plugin to integrate tests with JIRA XRAY Apr 24, 2021 3 - Alpha pytest `pytest-jobserver `_ Limit parallel tests with posix jobserver. May 15, 2019 5 - Production/Stable pytest `pytest-joke `_ Test failures are better served with humor. Oct 08, 2019 4 - Beta pytest (>=4.2.1) `pytest-json `_ Generate JSON test reports Jan 18, 2016 4 - Beta N/A @@ -466,7 +467,7 @@ name `pytest-missing-fixtures `_ Pytest plugin that creates missing fixtures Oct 14, 2020 4 - Beta pytest (>=3.5.0) `pytest-ml `_ Test your machine learning! May 04, 2019 4 - Beta N/A `pytest-mocha `_ pytest plugin to display test execution output like a mochajs Apr 02, 2020 4 - Beta pytest (>=5.4.0) -`pytest-mock `_ Thin-wrapper around the mock package for easier use with pytest Jan 10, 2021 5 - Production/Stable pytest (>=5.0) +`pytest-mock `_ Thin-wrapper around the mock package for easier use with pytest Apr 24, 2021 5 - Production/Stable pytest (>=5.0) `pytest-mock-api `_ A mock API server with configurable routes and responses available as a fixture. Feb 13, 2019 1 - Planning pytest (>=4.0.0) `pytest-mock-helper `_ Help you mock HTTP call and generate mock code Jan 24, 2018 N/A pytest `pytest-mockito `_ Base fixtures for mockito Jul 11, 2018 4 - Beta N/A @@ -479,7 +480,7 @@ name `pytest-molecule `_ PyTest Molecule Plugin :: discover and run molecule tests Jan 25, 2021 5 - Production/Stable N/A `pytest-mongo `_ MongoDB process and client fixtures plugin for py.test. Jan 12, 2021 5 - Production/Stable pytest (>=3.0.0) `pytest-mongodb `_ pytest plugin for MongoDB fixtures Dec 07, 2019 5 - Production/Stable pytest (>=2.5.2) -`pytest-monitor `_ Pytest plugin for analyzing resource usage. Feb 07, 2021 5 - Production/Stable pytest +`pytest-monitor `_ Pytest plugin for analyzing resource usage. Apr 21, 2021 5 - Production/Stable pytest `pytest-monkeyplus `_ pytest's monkeypatch subclass with extra functionalities Sep 18, 2012 5 - Production/Stable N/A `pytest-monkeytype `_ pytest-monkeytype: Generate Monkeytype annotations from your pytest tests. Jul 29, 2020 4 - Beta N/A `pytest-moto `_ Fixtures for integration tests of AWS services,uses moto mocking library. Aug 28, 2015 1 - Planning N/A @@ -504,7 +505,8 @@ name `pytest-ngrok `_ Jan 22, 2020 3 - Alpha N/A `pytest-ngsfixtures `_ pytest ngs fixtures Sep 06, 2019 2 - Pre-Alpha pytest (>=5.0.0) `pytest-nice `_ A pytest plugin that alerts user of failed test cases with screen notifications May 04, 2019 4 - Beta pytest -`pytest-nocustom `_ Run all tests without custom markers May 04, 2019 5 - Production/Stable N/A +`pytest-nice-parametrize `_ A small snippet for nicer PyTest's Parametrize Apr 17, 2021 5 - Production/Stable N/A +`pytest-nocustom `_ Run all tests without custom markers Apr 17, 2021 5 - Production/Stable N/A `pytest-nodev `_ Test-driven source code search for Python. Jul 21, 2016 4 - Beta pytest (>=2.8.1) `pytest-notebook `_ A pytest plugin for testing Jupyter Notebooks Sep 16, 2020 4 - Beta pytest (>=3.5.0) `pytest-notice `_ Send pytest execution result email Nov 05, 2020 N/A N/A @@ -598,7 +600,7 @@ name `pytest-pyramid-server `_ Pyramid server fixture for py.test May 28, 2019 5 - Production/Stable pytest `pytest-pytestrail `_ Pytest plugin for interaction with TestRail Aug 27, 2020 4 - Beta pytest (>=3.8.0) `pytest-pythonpath `_ pytest plugin for adding to the PYTHONPATH from command line or configs. Aug 22, 2018 5 - Production/Stable N/A -`pytest-pytorch `_ pytest plugin for a better developer experience when working with the PyTorch test suite Apr 14, 2021 4 - Beta pytest +`pytest-pytorch `_ pytest plugin for a better developer experience when working with the PyTorch test suite Apr 21, 2021 4 - Beta pytest `pytest-qml `_ Run QML Tests with pytest Dec 02, 2020 4 - Beta pytest (>=6.0.0) `pytest-qt `_ pytest support for PyQt and PySide applications Dec 07, 2019 5 - Production/Stable pytest (>=3.0.0) `pytest-qt-app `_ QT app fixture for py.test Dec 23, 2015 5 - Production/Stable N/A @@ -658,7 +660,7 @@ name `pytest-ringo `_ pytest plugin to test webapplications using the Ringo webframework Sep 27, 2017 3 - Alpha N/A `pytest-rng `_ Fixtures for seeding tests and making randomness reproducible Aug 08, 2019 5 - Production/Stable pytest `pytest-roast `_ pytest plugin for ROAST configuration override and fixtures Apr 05, 2021 5 - Production/Stable pytest -`pytest-rocketchat `_ Pytest to Rocket.Chat reporting plugin Apr 11, 2021 5 - Production/Stable N/A +`pytest-rocketchat `_ Pytest to Rocket.Chat reporting plugin Apr 18, 2021 5 - Production/Stable N/A `pytest-rotest `_ Pytest integration with rotest Sep 08, 2019 N/A pytest (>=3.5.0) `pytest-rpc `_ Extend py.test for RPC OpenStack testing. Feb 22, 2019 4 - Beta pytest (~=3.6) `pytest-rt `_ pytest data collector plugin for Testgr Mar 03, 2021 N/A N/A @@ -674,17 +676,17 @@ name `pytest-sanic `_ a pytest plugin for Sanic Feb 27, 2021 N/A pytest (>=5.2) `pytest-sanity `_ Dec 07, 2020 N/A N/A `pytest-sa-pg `_ May 14, 2019 N/A N/A -`pytest-sbase `_ A complete web automation framework for end-to-end testing. Apr 16, 2021 5 - Production/Stable N/A +`pytest-sbase `_ A complete web automation framework for end-to-end testing. Apr 24, 2021 5 - Production/Stable N/A `pytest-scenario `_ pytest plugin for test scenarios Feb 06, 2017 3 - Alpha N/A `pytest-schema `_ 👍 Validate return values against a schema-like object in testing Aug 31, 2020 5 - Production/Stable pytest (>=3.5.0) `pytest-securestore `_ An encrypted password store for use within pytest cases Jun 19, 2019 4 - Beta N/A `pytest-select `_ A pytest plugin which allows to (de-)select tests from a file. Jan 18, 2019 3 - Alpha pytest (>=3.0) `pytest-selenium `_ pytest plugin for Selenium Sep 19, 2020 5 - Production/Stable pytest (>=5.0.0) -`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Apr 16, 2021 5 - Production/Stable N/A +`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Apr 24, 2021 5 - Production/Stable N/A `pytest-selenium-enhancer `_ pytest plugin for Selenium Nov 26, 2020 5 - Production/Stable N/A `pytest-selenium-pdiff `_ A pytest package implementing perceptualdiff for Selenium tests. Apr 06, 2017 2 - Pre-Alpha N/A `pytest-send-email `_ Send pytest execution result email Dec 04, 2019 N/A N/A -`pytest-sentry `_ A pytest plugin to send testrun information to Sentry.io Mar 02, 2021 N/A N/A +`pytest-sentry `_ A pytest plugin to send testrun information to Sentry.io Apr 21, 2021 N/A pytest `pytest-server-fixtures `_ Extensible server fixures for py.test May 28, 2019 5 - Production/Stable pytest `pytest-serverless `_ Automatically mocks resources from serverless.yml in pytest using moto. Feb 20, 2021 4 - Beta N/A `pytest-services `_ Services plugin for pytest testing framework Oct 30, 2020 6 - Mature N/A @@ -709,12 +711,12 @@ name `pytest-smtp `_ Send email with pytest execution result Feb 20, 2021 N/A pytest `pytest-snail `_ Plugin for adding a marker to slow running tests. 🐌 Nov 04, 2019 3 - Alpha pytest (>=5.0.1) `pytest-snapci `_ py.test plugin for Snap-CI Nov 12, 2015 N/A N/A -`pytest-snapshot `_ A plugin to enable snapshot testing with pytest. Jan 22, 2021 4 - Beta pytest (>=3.0.0) +`pytest-snapshot `_ A plugin to enable snapshot testing with pytest. Apr 20, 2021 4 - Beta pytest (>=3.0.0) `pytest-snmpserver `_ Sep 14, 2020 N/A N/A `pytest-socket `_ Pytest Plugin to disable socket calls during tests Mar 30, 2021 4 - Beta pytest (>=3.6.3) `pytest-soft-assertions `_ May 05, 2020 3 - Alpha pytest `pytest-solr `_ Solr process and client fixtures for py.test. May 11, 2020 3 - Alpha pytest (>=3.0.0) -`pytest-sorter `_ A simple plugin to first execute tests that historically failed more Apr 05, 2021 4 - Beta pytest (>=3.1.1) +`pytest-sorter `_ A simple plugin to first execute tests that historically failed more Apr 20, 2021 4 - Beta pytest (>=3.1.1) `pytest-sourceorder `_ Test-ordering plugin for pytest Apr 11, 2017 4 - Beta pytest `pytest-spark `_ pytest plugin to run the tests with support of pyspark. Feb 23, 2020 4 - Beta pytest `pytest-spawner `_ py.test plugin to spawn process and communicate with them. Jul 31, 2015 4 - Beta N/A @@ -745,7 +747,7 @@ name `pytest-stub `_ Stub packages, modules and attributes. Apr 28, 2020 5 - Production/Stable N/A `pytest-stubprocess `_ Provide stub implementations for subprocesses in Python tests Sep 17, 2018 3 - Alpha pytest (>=3.5.0) `pytest-study `_ A pytest plugin to organize long run tests (named studies) without interfering the regular tests Sep 26, 2017 3 - Alpha pytest (>=2.0) -`pytest-subprocess `_ A plugin to fake subprocess for pytest Mar 20, 2021 5 - Production/Stable pytest (>=4.0.0) +`pytest-subprocess `_ A plugin to fake subprocess for pytest Apr 18, 2021 5 - Production/Stable pytest (>=4.0.0) `pytest-subtesthack `_ A hack to explicitly set up and tear down fixtures. Mar 02, 2021 N/A N/A `pytest-subtests `_ unittest subTest() support and subtests fixture Dec 13, 2020 4 - Beta pytest (>=5.3.0) `pytest-subunit `_ pytest-subunit is a plugin for py.test which outputs testsresult in subunit format. Aug 29, 2017 N/A N/A @@ -768,13 +770,13 @@ name `pytest-testdirectory `_ A py.test plugin providing temporary directories in unit tests. Nov 06, 2018 5 - Production/Stable pytest `pytest-testdox `_ A testdox format reporter for pytest Oct 13, 2020 5 - Production/Stable pytest (>=3.7.0) `pytest-test-groups `_ A Pytest plugin for running a subset of your tests by splitting them in to equally sized groups. Oct 25, 2016 5 - Production/Stable N/A -`pytest-testinfra `_ Test infrastructures Mar 18, 2021 5 - Production/Stable pytest (!=3.0.2) +`pytest-testinfra `_ Test infrastructures Apr 18, 2021 5 - Production/Stable pytest (!=3.0.2) `pytest-testlink-adaptor `_ pytest reporting plugin for testlink Dec 20, 2018 4 - Beta pytest (>=2.6) `pytest-testmon `_ selects tests affected by changed files and methods Mar 18, 2021 4 - Beta N/A `pytest-testobject `_ Plugin to use TestObject Suites with Pytest Sep 24, 2019 4 - Beta pytest (>=3.1.1) `pytest-testrail `_ pytest plugin for creating TestRail runs and adding results Aug 27, 2020 N/A pytest (>=3.6) `pytest-testrail2 `_ A small example package Nov 17, 2020 N/A pytest (>=5) -`pytest-testrail-api `_ Плагин Pytest, для интеграции с TestRail Dec 09, 2020 N/A pytest (>=5.5) +`pytest-testrail-api `_ Плагин Pytest, для интеграции с TestRail Apr 23, 2021 N/A pytest (>=5.5) `pytest-testrail-client `_ pytest plugin for Testrail Sep 29, 2020 5 - Production/Stable N/A `pytest-testrail-e2e `_ pytest plugin for creating TestRail runs and adding results Jun 11, 2020 N/A pytest (>=3.6) `pytest-testrail-plugin `_ PyTest plugin for TestRail Apr 21, 2020 3 - Alpha pytest From 5a7be03285fd3945933a502e069ea0c85761c615 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Apr 2021 03:01:13 +0000 Subject: [PATCH 260/630] Bump pytest-asyncio in /testing/plugins_integration Bumps [pytest-asyncio](https://github.com/pytest-dev/pytest-asyncio) from 0.15.0 to 0.15.1. - [Release notes](https://github.com/pytest-dev/pytest-asyncio/releases) - [Commits](https://github.com/pytest-dev/pytest-asyncio/compare/v0.15.0...v0.15.1) Signed-off-by: dependabot[bot] --- testing/plugins_integration/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/plugins_integration/requirements.txt b/testing/plugins_integration/requirements.txt index 567c492c431..211c08fc9df 100644 --- a/testing/plugins_integration/requirements.txt +++ b/testing/plugins_integration/requirements.txt @@ -1,6 +1,6 @@ anyio[curio,trio]==2.2.0 django==3.2 -pytest-asyncio==0.15.0 +pytest-asyncio==0.15.1 pytest-bdd==4.0.2 pytest-cov==2.11.1 pytest-django==4.2.0 From 11f6dc9de6ee001dcb13f52d27d849bf8d44a194 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Apr 2021 03:01:15 +0000 Subject: [PATCH 261/630] Bump anyio[curio,trio] in /testing/plugins_integration Bumps [anyio[curio,trio]](https://github.com/agronholm/anyio) from 2.2.0 to 3.0.0. - [Release notes](https://github.com/agronholm/anyio/releases) - [Changelog](https://github.com/agronholm/anyio/blob/master/docs/versionhistory.rst) - [Commits](https://github.com/agronholm/anyio/compare/2.2.0...3.0.0) Signed-off-by: dependabot[bot] --- testing/plugins_integration/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/plugins_integration/requirements.txt b/testing/plugins_integration/requirements.txt index 567c492c431..7e983cc85aa 100644 --- a/testing/plugins_integration/requirements.txt +++ b/testing/plugins_integration/requirements.txt @@ -1,4 +1,4 @@ -anyio[curio,trio]==2.2.0 +anyio[curio,trio]==3.0.0 django==3.2 pytest-asyncio==0.15.0 pytest-bdd==4.0.2 From 8d93f6ff0b1d722d5beb1b8a9a70e9e40276d6a2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Apr 2021 03:01:18 +0000 Subject: [PATCH 262/630] Bump pytest-mock from 3.5.1 to 3.6.0 in /testing/plugins_integration Bumps [pytest-mock](https://github.com/pytest-dev/pytest-mock) from 3.5.1 to 3.6.0. - [Release notes](https://github.com/pytest-dev/pytest-mock/releases) - [Changelog](https://github.com/pytest-dev/pytest-mock/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest-mock/compare/v3.5.1...v3.6.0) Signed-off-by: dependabot[bot] --- testing/plugins_integration/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/plugins_integration/requirements.txt b/testing/plugins_integration/requirements.txt index 567c492c431..9989dbaaf95 100644 --- a/testing/plugins_integration/requirements.txt +++ b/testing/plugins_integration/requirements.txt @@ -6,7 +6,7 @@ pytest-cov==2.11.1 pytest-django==4.2.0 pytest-flakes==4.0.3 pytest-html==3.1.1 -pytest-mock==3.5.1 +pytest-mock==3.6.0 pytest-rerunfailures==9.1.1 pytest-sugar==0.9.4 pytest-trio==0.7.0 From 9ef608ef76a758b34550a977cc3ebe521986cc4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nico=20Schl=C3=B6mer?= Date: Mon, 26 Apr 2021 17:44:27 +0200 Subject: [PATCH 263/630] "fix" a couple of http -> https redirects Found with urli-fix . -a http: -i pytest --- CONTRIBUTING.rst | 4 +-- doc/en/announce/release-2.1.0.rst | 2 +- doc/en/announce/release-2.1.1.rst | 2 +- doc/en/announce/release-2.1.2.rst | 2 +- doc/en/builtin.rst | 2 +- doc/en/changelog.rst | 6 ++-- doc/en/conf.py | 2 +- doc/en/contact.rst | 10 +++--- doc/en/explanation/flaky.rst | 2 +- doc/en/explanation/goodpractices.rst | 2 +- doc/en/getting-started.rst | 2 +- doc/en/how-to/failures.rst | 2 +- doc/en/how-to/fixtures.rst | 2 +- doc/en/how-to/monkeypatch.rst | 2 +- doc/en/how-to/output.rst | 4 +-- doc/en/how-to/plugins.rst | 2 +- doc/en/how-to/xunit_setup.rst | 2 +- doc/en/projects.rst | 46 ++++++++++++++-------------- doc/en/talks.rst | 12 ++++---- src/_pytest/cacheprovider.py | 2 +- src/_pytest/recwarn.py | 2 +- testing/test_pathlib.py | 2 +- 22 files changed, 57 insertions(+), 57 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index bbc341e0f1d..2c7d253e73a 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -160,7 +160,7 @@ the following: - an issue tracker for bug reports and enhancement requests. -- a `changelog `_. +- a `changelog `_. If no contributor strongly objects and two agree, the repository can then be transferred to the ``pytest-dev`` organisation. @@ -259,7 +259,7 @@ Here is a simple overview, with pytest-specific bits: Tox is used to run all the tests and will automatically setup virtualenvs to run the tests in. - (will implicitly use http://www.virtualenv.org/en/latest/):: + (will implicitly use https://virtualenv.pypa.io/en/latest/):: $ pip install tox diff --git a/doc/en/announce/release-2.1.0.rst b/doc/en/announce/release-2.1.0.rst index 29463ab533e..f8bc88c163d 100644 --- a/doc/en/announce/release-2.1.0.rst +++ b/doc/en/announce/release-2.1.0.rst @@ -24,7 +24,7 @@ If you want to install or upgrade pytest, just type one of:: easy_install -U pytest best, -holger krekel / http://merlinux.eu +holger krekel / https://merlinux.eu/ Changes between 2.0.3 and 2.1.0 ---------------------------------------------- diff --git a/doc/en/announce/release-2.1.1.rst b/doc/en/announce/release-2.1.1.rst index c2285eba9fa..369428ed2ea 100644 --- a/doc/en/announce/release-2.1.1.rst +++ b/doc/en/announce/release-2.1.1.rst @@ -20,7 +20,7 @@ If you want to install or upgrade pytest, just type one of:: easy_install -U pytest best, -holger krekel / http://merlinux.eu +holger krekel / https://merlinux.eu/ Changes between 2.1.0 and 2.1.1 ---------------------------------------------- diff --git a/doc/en/announce/release-2.1.2.rst b/doc/en/announce/release-2.1.2.rst index 1975f368a3f..a3c0c1a38a4 100644 --- a/doc/en/announce/release-2.1.2.rst +++ b/doc/en/announce/release-2.1.2.rst @@ -19,7 +19,7 @@ If you want to install or upgrade pytest, just type one of:: easy_install -U pytest best, -holger krekel / http://merlinux.eu +holger krekel / https://merlinux.eu/ Changes between 2.1.1 and 2.1.2 ---------------------------------------- diff --git a/doc/en/builtin.rst b/doc/en/builtin.rst index ad6efd657cd..f0a6eedb106 100644 --- a/doc/en/builtin.rst +++ b/doc/en/builtin.rst @@ -144,7 +144,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a recwarn Return a :class:`WarningsRecorder` instance that records all warnings emitted by test functions. - See http://docs.python.org/library/warnings.html for information + See https://docs.python.org/library/warnings.html for information on warning categories. tmpdir_factory [session scope] diff --git a/doc/en/changelog.rst b/doc/en/changelog.rst index 0ff7ede3d8d..18a288469c5 100644 --- a/doc/en/changelog.rst +++ b/doc/en/changelog.rst @@ -3108,12 +3108,12 @@ Features will not issue the warning. -- `#3632 `_: Richer equality comparison introspection on ``AssertionError`` for objects created using `attrs `__ or `dataclasses `_ (Python 3.7+, `backported to 3.6 `__). +- `#3632 `_: Richer equality comparison introspection on ``AssertionError`` for objects created using `attrs `__ or `dataclasses `_ (Python 3.7+, `backported to 3.6 `__). - `#4278 `_: ``CACHEDIR.TAG`` files are now created inside cache directories. - Those files are part of the `Cache Directory Tagging Standard `__, and can + Those files are part of the `Cache Directory Tagging Standard `__, and can be used by backup or synchronization programs to identify pytest's cache directory as such. @@ -8466,7 +8466,7 @@ Bug fixes: - fixes for making the jython/win32 combination work, note however: jython2.5.1/win32 does not provide a command line launcher, see - http://bugs.jython.org/issue1491 . See pylib install documentation + https://bugs.jython.org/issue1491 . See pylib install documentation for how to work around. - fixes for handling of unicode exception values and unprintable objects diff --git a/doc/en/conf.py b/doc/en/conf.py index d9c1f3c2d3f..6de2f1fbd7d 100644 --- a/doc/en/conf.py +++ b/doc/en/conf.py @@ -252,7 +252,7 @@ "contents", "pytest.tex", "pytest Documentation", - "holger krekel, trainer and consultant, http://merlinux.eu", + "holger krekel, trainer and consultant, https://merlinux.eu/", "manual", ) ] diff --git a/doc/en/contact.rst b/doc/en/contact.rst index efc6a8f57d3..c7cf277afd6 100644 --- a/doc/en/contact.rst +++ b/doc/en/contact.rst @@ -30,19 +30,19 @@ Contact channels consulting. .. _`pytest issue tracker`: https://github.com/pytest-dev/pytest/issues -.. _`old issue tracker`: http://bitbucket.org/hpk42/py-trunk/issues/ +.. _`old issue tracker`: https://bitbucket.org/hpk42/py-trunk/issues/ -.. _`merlinux.eu`: http://merlinux.eu +.. _`merlinux.eu`: https://merlinux.eu/ .. _`get an account`: -.. _tetamap: http://tetamap.wordpress.com +.. _tetamap: https://tetamap.wordpress.com/ -.. _`@pylibcommit`: http://twitter.com/pylibcommit +.. _`@pylibcommit`: https://twitter.com/pylibcommit .. _`Testing in Python`: http://lists.idyll.org/listinfo/testing-in-python -.. _FOAF: http://en.wikipedia.org/wiki/FOAF +.. _FOAF: https://en.wikipedia.org/wiki/FOAF .. _`py-dev`: .. _`development mailing list`: .. _`pytest-dev at python.org (mailing list)`: http://mail.python.org/mailman/listinfo/pytest-dev diff --git a/doc/en/explanation/flaky.rst b/doc/en/explanation/flaky.rst index b6fc1520c0b..e49ebf21068 100644 --- a/doc/en/explanation/flaky.rst +++ b/doc/en/explanation/flaky.rst @@ -113,7 +113,7 @@ Resources * `Eradicating Non-Determinism in Tests `_ by Martin Fowler, 2011 * `No more flaky tests on the Go team `_ by Pavan Sudarshan, 2012 -* `The Build That Cried Broken: Building Trust in your Continuous Integration Tests `_ talk (video) by `Angie Jones `_ at SeleniumConf Austin 2017 +* `The Build That Cried Broken: Building Trust in your Continuous Integration Tests `_ talk (video) by `Angie Jones `_ at SeleniumConf Austin 2017 * `Test and Code Podcast: Flaky Tests and How to Deal with Them `_ by Brian Okken and Anthony Shaw, 2018 * Microsoft: diff --git a/doc/en/explanation/goodpractices.rst b/doc/en/explanation/goodpractices.rst index fe5db9fd1eb..186beae58e3 100644 --- a/doc/en/explanation/goodpractices.rst +++ b/doc/en/explanation/goodpractices.rst @@ -231,7 +231,7 @@ Note that this layout also works in conjunction with the ``src`` layout mentione .. _`virtualenv`: https://pypi.org/project/virtualenv/ -.. _`buildout`: http://www.buildout.org/ +.. _`buildout`: http://www.buildout.org/en/latest/ .. _pip: https://pypi.org/project/pip/ .. _`use tox`: diff --git a/doc/en/getting-started.rst b/doc/en/getting-started.rst index 5f2f419e64f..373affa151a 100644 --- a/doc/en/getting-started.rst +++ b/doc/en/getting-started.rst @@ -71,7 +71,7 @@ The ``[100%]`` refers to the overall progress of running all test cases. After i .. note:: - You can use the ``assert`` statement to verify test expectations. pytest’s `Advanced assertion introspection `_ will intelligently report intermediate values of the assert expression so you can avoid the many names `of JUnit legacy methods `_. + You can use the ``assert`` statement to verify test expectations. pytest’s `Advanced assertion introspection `_ will intelligently report intermediate values of the assert expression so you can avoid the many names `of JUnit legacy methods `_. Run multiple tests ---------------------------------------------------------- diff --git a/doc/en/how-to/failures.rst b/doc/en/how-to/failures.rst index bbb660b2269..0a0ec164dbb 100644 --- a/doc/en/how-to/failures.rst +++ b/doc/en/how-to/failures.rst @@ -24,7 +24,7 @@ Using PDB_ (Python Debugger) with pytest Dropping to PDB_ (Python Debugger) on failures ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. _PDB: http://docs.python.org/library/pdb.html +.. _PDB: https://docs.python.org/library/pdb.html Python comes with a builtin Python debugger called PDB_. ``pytest`` allows one to drop into the PDB_ prompt via a command line option: diff --git a/doc/en/how-to/fixtures.rst b/doc/en/how-to/fixtures.rst index a2eb211e182..1bd4173d9b5 100644 --- a/doc/en/how-to/fixtures.rst +++ b/doc/en/how-to/fixtures.rst @@ -1578,7 +1578,7 @@ Sometimes test functions do not directly need access to a fixture object. For example, tests may require to operate with an empty directory as the current working directory but otherwise do not care for the concrete directory. Here is how you can use the standard `tempfile -`_ and pytest fixtures to +`_ and pytest fixtures to achieve it. We separate the creation of the fixture into a conftest.py file: diff --git a/doc/en/how-to/monkeypatch.rst b/doc/en/how-to/monkeypatch.rst index f34ba7982b4..4f9b5aa99e2 100644 --- a/doc/en/how-to/monkeypatch.rst +++ b/doc/en/how-to/monkeypatch.rst @@ -58,7 +58,7 @@ call ``pkg_resources.fixup_namespace_packages`` and :py:func:`importlib.invalida See the `monkeypatch blog post`_ for some introduction material and a discussion of its motivation. -.. _`monkeypatch blog post`: http://tetamap.wordpress.com/2009/03/03/monkeypatching-in-unit-tests-done-right/ +.. _`monkeypatch blog post`: https://tetamap.wordpress.com//2009/03/03/monkeypatching-in-unit-tests-done-right/ Simple example: monkeypatching functions ---------------------------------------- diff --git a/doc/en/how-to/output.rst b/doc/en/how-to/output.rst index 465681e6680..9f922b841df 100644 --- a/doc/en/how-to/output.rst +++ b/doc/en/how-to/output.rst @@ -703,11 +703,11 @@ for example ``-x`` if you only want to send one particular failure. pytest --pastebin=all -Currently only pasting to the http://bpaste.net service is implemented. +Currently only pasting to the https://bpaste.net/ service is implemented. .. versionchanged:: 5.2 If creating the URL fails for any reason, a warning is generated instead of failing the entire test suite. -.. _jenkins: http://jenkins-ci.org/ +.. _jenkins: https://jenkins-ci.org diff --git a/doc/en/how-to/plugins.rst b/doc/en/how-to/plugins.rst index 60c442c1f42..8af86c16d65 100644 --- a/doc/en/how-to/plugins.rst +++ b/doc/en/how-to/plugins.rst @@ -26,7 +26,7 @@ Here is a little annotated list for some popular plugins: for `django`_ apps, using pytest integration. * `pytest-twisted `_: write tests - for `twisted `_ apps, starting a reactor and + for `twisted `_ apps, starting a reactor and processing deferreds from test functions. * `pytest-cov `__: diff --git a/doc/en/how-to/xunit_setup.rst b/doc/en/how-to/xunit_setup.rst index 2269c06e003..7fbe870ed47 100644 --- a/doc/en/how-to/xunit_setup.rst +++ b/doc/en/how-to/xunit_setup.rst @@ -116,4 +116,4 @@ Remarks: Now the xunit-style functions are integrated with the fixture mechanism and obey the proper scope rules of fixtures involved in the call. -.. _`unittest.py module`: http://docs.python.org/library/unittest.html +.. _`unittest.py module`: https://docs.python.org/library/unittest.html diff --git a/doc/en/projects.rst b/doc/en/projects.rst index 0720bc9dd7e..804a0b20a25 100644 --- a/doc/en/projects.rst +++ b/doc/en/projects.rst @@ -22,38 +22,38 @@ Project examples Here are some examples of projects using ``pytest`` (please send notes via :ref:`contact`): -* `PyPy `_, Python with a JIT compiler, running over +* `PyPy `_, Python with a JIT compiler, running over `21000 tests `_ * the `MoinMoin `_ Wiki Engine * `sentry `_, realtime app-maintenance and exception tracking -* `Astropy `_ and `affiliated packages `_ -* `tox `_, virtualenv/Hudson integration tool -* `PyPM `_ ActiveState's package manager -* `Fom `_ a fluid object mapper for FluidDB +* `Astropy `_ and `affiliated packages `_ +* `tox `_, virtualenv/Hudson integration tool +* `PyPM `_ ActiveState's package manager +* `Fom `_ a fluid object mapper for FluidDB * `applib `_ cross-platform utilities * `six `_ Python 2 and 3 compatibility utilities -* `pediapress `_ MediaWiki articles +* `pediapress `_ MediaWiki articles * `mwlib `_ mediawiki parser and utility library -* `The Translate Toolkit `_ for localization and conversion -* `execnet `_ rapid multi-Python deployment +* `The Translate Toolkit `_ for localization and conversion +* `execnet `_ rapid multi-Python deployment * `pylib `_ cross-platform path, IO, dynamic code library * `bbfreeze `_ create standalone executables from Python scripts * `pdb++ `_ a fancier version of PDB * `pudb `_ full-screen console debugger for python -* `py-s3fuse `_ Amazon S3 FUSE based filesystem +* `py-s3fuse `_ Amazon S3 FUSE based filesystem * `waskr `_ WSGI Stats Middleware * `guachi `_ global persistent configs for Python modules * `Circuits `_ lightweight Event Driven Framework -* `pygtk-helpers `_ easy interaction with PyGTK +* `pygtk-helpers `_ easy interaction with PyGTK * `QuantumCore `_ statusmessage and repoze openid plugin -* `pydataportability `_ libraries for managing the open web -* `XIST `_ extensible HTML/XML generator +* `pydataportability `_ libraries for managing the open web +* `XIST `_ extensible HTML/XML generator * `tiddlyweb `_ optionally headless, extensible RESTful datastore -* `fancycompleter `_ for colorful tab-completion -* `Paludis `_ tools for Gentoo Paludis package manager -* `Gerald `_ schema comparison tool -* `abjad `_ Python API for Formalized Score control -* `bu `_ a microscopic build system +* `fancycompleter `_ for colorful tab-completion +* `Paludis `_ tools for Gentoo Paludis package manager +* `Gerald `_ schema comparison tool +* `abjad `_ Python API for Formalized Score control +* `bu `_ a microscopic build system * `katcp `_ Telescope communication protocol over Twisted * `kss plugin timer `_ * `pyudev `_ a pure Python binding to the Linux library libudev @@ -71,13 +71,13 @@ These projects help integrate ``pytest`` into other Python frameworks: Some organisations using pytest ----------------------------------- -* `Square Kilometre Array, Cape Town `_ +* `Square Kilometre Array, Cape Town `_ * `Some Mozilla QA people `_ use pytest to distribute their Selenium tests -* `Shootq `_ -* `Stups department of Heinrich Heine University Duesseldorf `_ +* `Shootq `_ +* `Stups department of Heinrich Heine University Duesseldorf `_ * cellzome -* `Open End, Gothenborg `_ +* `Open End, Gothenborg `_ * `Laboratory of Bioinformatics, Warsaw `_ -* `merlinux, Germany `_ -* `ESSS, Brazil `_ +* `merlinux, Germany `_ +* `ESSS, Brazil `_ * many more ... (please be so kind to send a note via :ref:`contact`) diff --git a/doc/en/talks.rst b/doc/en/talks.rst index 216ccb8dd8a..6843c82bab5 100644 --- a/doc/en/talks.rst +++ b/doc/en/talks.rst @@ -70,8 +70,8 @@ Talks and blog postings - `pytest introduction from Brian Okken (January 2013) `_ -- pycon australia 2012 pytest talk from Brianna Laugher (`video `_, `slides `_, `code `_) -- `pycon 2012 US talk video from Holger Krekel `_ +- pycon australia 2012 pytest talk from Brianna Laugher (`video `_, `slides `_, `code `_) +- `pycon 2012 US talk video from Holger Krekel `_ - `monkey patching done right`_ (blog post, consult `monkeypatch plugin`_ for up-to-date API) @@ -101,9 +101,9 @@ Plugin specific examples: .. _`many examples in the docs for plugins`: plugins.html .. _`monkeypatch plugin`: monkeypatch.html .. _`application setup in test functions with fixtures`: fixture.html#interdependent-fixtures -.. _`simultaneously test your code on all platforms`: http://tetamap.wordpress.com/2009/03/23/new-simultanously-test-your-code-on-all-platforms/ -.. _`monkey patching done right`: http://tetamap.wordpress.com/2009/03/03/monkeypatching-in-unit-tests-done-right/ -.. _`putting test-hooks into local or global plugins`: http://tetamap.wordpress.com/2009/05/14/putting-test-hooks-into-local-and-global-plugins/ -.. _`parametrizing tests, generalized`: http://tetamap.wordpress.com/2009/05/13/parametrizing-python-tests-generalized/ +.. _`simultaneously test your code on all platforms`: https://tetamap.wordpress.com//2009/03/23/new-simultanously-test-your-code-on-all-platforms/ +.. _`monkey patching done right`: https://tetamap.wordpress.com//2009/03/03/monkeypatching-in-unit-tests-done-right/ +.. _`putting test-hooks into local or global plugins`: https://tetamap.wordpress.com/2009/05/14/putting-test-hooks-into-local-and-global-plugins/ +.. _`parametrizing tests, generalized`: https://tetamap.wordpress.com/2009/05/13/parametrizing-python-tests-generalized/ .. _`generating parametrized tests with fixtures`: parametrize.html#test-generators .. _`test generators and cached setup`: http://bruynooghe.blogspot.com/2010/06/pytest-test-generators-and-cached-setup.html diff --git a/src/_pytest/cacheprovider.py b/src/_pytest/cacheprovider.py index 62112c6e343..f442ea89bcc 100755 --- a/src/_pytest/cacheprovider.py +++ b/src/_pytest/cacheprovider.py @@ -50,7 +50,7 @@ Signature: 8a477f597d28d172789f06886806bc55 # This file is a cache directory tag created by pytest. # For information about cache directory tags, see: -# http://www.bford.info/cachedir/spec.html +# https://bford.info/cachedir/spec.html """ diff --git a/src/_pytest/recwarn.py b/src/_pytest/recwarn.py index eb59fee2940..bc3a2835e35 100644 --- a/src/_pytest/recwarn.py +++ b/src/_pytest/recwarn.py @@ -28,7 +28,7 @@ def recwarn() -> Generator["WarningsRecorder", None, None]: """Return a :class:`WarningsRecorder` instance that records all warnings emitted by test functions. - See http://docs.python.org/library/warnings.html for information + See https://docs.python.org/library/warnings.html for information on warning categories. """ wrec = WarningsRecorder(_ispytest=True) diff --git a/testing/test_pathlib.py b/testing/test_pathlib.py index 11db4c5c614..2cd1e63553c 100644 --- a/testing/test_pathlib.py +++ b/testing/test_pathlib.py @@ -164,7 +164,7 @@ def test_renamed_dir_creates_mismatch( import_path(tmp_path.joinpath("b", "test_x123.py"), root=tmp_path) def test_messy_name(self, tmp_path: Path) -> None: - # http://bitbucket.org/hpk42/py-trunk/issue/129 + # https://bitbucket.org/hpk42/py-trunk/issue/129 path = tmp_path / "foo__init__.py" path.touch() module = import_path(path, root=tmp_path) From f5c22a6b452749dc0c72f002543d69c4e6e7e891 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 26 Apr 2021 17:23:05 +0000 Subject: [PATCH 264/630] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black: 20.8b1 → 21.4b0](https://github.com/psf/black/compare/20.8b1...21.4b0) - [github.com/asottile/reorder_python_imports: v2.4.0 → v2.5.0](https://github.com/asottile/reorder_python_imports/compare/v2.4.0...v2.5.0) - [github.com/asottile/pyupgrade: v2.12.0 → v2.13.0](https://github.com/asottile/pyupgrade/compare/v2.12.0...v2.13.0) --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0e4b8284d6c..c0857ceba17 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/psf/black - rev: 20.8b1 + rev: 21.4b0 hooks: - id: black args: [--safe, --quiet] @@ -29,12 +29,12 @@ repos: - flake8-typing-imports==1.9.0 - flake8-docstrings==1.5.0 - repo: https://github.com/asottile/reorder_python_imports - rev: v2.4.0 + rev: v2.5.0 hooks: - id: reorder-python-imports args: ['--application-directories=.:src', --py36-plus] - repo: https://github.com/asottile/pyupgrade - rev: v2.12.0 + rev: v2.13.0 hooks: - id: pyupgrade args: [--py36-plus] From 992c403fc86724a3c971486399576e60ce9791ee Mon Sep 17 00:00:00 2001 From: Parth Patel <37349063+parthdpa@users.noreply.github.com> Date: Thu, 29 Apr 2021 10:02:43 -0400 Subject: [PATCH 265/630] Improve `iterparentnodeids` to consume `/` parts until the first `::` (#8577) * Fix issue where TestCase.setUpClass is not called for test methods with a / in its name by checking if there is :: before the selected / or any :: after. Also added a test case for this. * removed print statement that was added * Change iterparentnodeids to consume / parts until the first ::. Then consider ::. Tests were changed to reflect this. * Update changelog/8509.improvement.rst Co-authored-by: Ran Benita --- AUTHORS | 1 + changelog/8509.improvement.rst | 5 +++++ src/_pytest/nodes.py | 34 ++++++++++++++++++++++------------ testing/test_nodes.py | 6 ++++-- 4 files changed, 32 insertions(+), 14 deletions(-) create mode 100644 changelog/8509.improvement.rst diff --git a/AUTHORS b/AUTHORS index f219c0c834f..fcae73823fa 100644 --- a/AUTHORS +++ b/AUTHORS @@ -236,6 +236,7 @@ Omar Kohl Omer Hadari Ondřej Súkup Oscar Benjamin +Parth Patel Patrick Hayes Pauli Virtanen Pavel Karateev diff --git a/changelog/8509.improvement.rst b/changelog/8509.improvement.rst new file mode 100644 index 00000000000..d72e8e487f6 --- /dev/null +++ b/changelog/8509.improvement.rst @@ -0,0 +1,5 @@ +Fixed issue where `TestCase.setUpClass` is not called when a test has `/` in its name since pytest 6.2.0. + +This refers to the path part in pytest node IDs, e.g. `TestClass::test_it` in the node ID `tests/test_file.py::TestClass::test_it`. + +Now, instead of assuming that the test name does not contain ``/``, it is assumed that test path does not contain ``::``. We plan to hopefully make both of these work in the future. diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py index 99b7eb1a61c..40bc4d4965b 100644 --- a/src/_pytest/nodes.py +++ b/src/_pytest/nodes.py @@ -62,23 +62,33 @@ def iterparentnodeids(nodeid: str) -> Iterator[str]: "testing/code/test_excinfo.py::TestFormattedExcinfo" "testing/code/test_excinfo.py::TestFormattedExcinfo::test_repr_source" - Note that :: parts are only considered at the last / component. + Note that / components are only considered until the first ::. """ pos = 0 - sep = SEP + first_colons: Optional[int] = nodeid.find("::") + if first_colons == -1: + first_colons = None + # The root Session node - always present. yield "" + # Eagerly consume SEP parts until first colons. while True: - at = nodeid.find(sep, pos) - if at == -1 and sep == SEP: - sep = "::" - elif at == -1: - if nodeid: - yield nodeid + at = nodeid.find(SEP, pos, first_colons) + if at == -1: break - else: - if at: - yield nodeid[:at] - pos = at + len(sep) + if at > 0: + yield nodeid[:at] + pos = at + len(SEP) + # Eagerly consume :: parts. + while True: + at = nodeid.find("::", pos) + if at == -1: + break + if at > 0: + yield nodeid[:at] + pos = at + len("::") + # The node ID itself. + if nodeid: + yield nodeid def _imply_path( diff --git a/testing/test_nodes.py b/testing/test_nodes.py index fbdbce3950d..4d7e6cba2ae 100644 --- a/testing/test_nodes.py +++ b/testing/test_nodes.py @@ -18,11 +18,13 @@ ("a/b/c", ["", "a", "a/b", "a/b/c"]), ("a/bbb/c::D", ["", "a", "a/bbb", "a/bbb/c", "a/bbb/c::D"]), ("a/b/c::D::eee", ["", "a", "a/b", "a/b/c", "a/b/c::D", "a/b/c::D::eee"]), - # :: considered only at the last component. ("::xx", ["", "::xx"]), - ("a/b/c::D/d::e", ["", "a", "a/b", "a/b/c::D", "a/b/c::D/d", "a/b/c::D/d::e"]), + # / only considered until first :: + ("a/b/c::D/d::e", ["", "a", "a/b", "a/b/c", "a/b/c::D/d", "a/b/c::D/d::e"]), # : alone is not a separator. ("a/b::D:e:f::g", ["", "a", "a/b", "a/b::D:e:f", "a/b::D:e:f::g"]), + # / not considered if a part of a test name + ("a/b::c/d::e[/test]", ["", "a", "a/b", "a/b::c/d", "a/b::c/d::e[/test]"]), ), ) def test_iterparentnodeids(nodeid: str, expected: List[str]) -> None: From 9d9b84d175ec1b390829e527700fc7c7dbbea421 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarc=C3=ADsio=20Fischer?= Date: Fri, 30 Apr 2021 07:36:56 -0300 Subject: [PATCH 266/630] Improve pytest.approx error messages readability (Pull request) (#8429) Improve pytest.approx error messages readability (Pull request) --- changelog/8335.improvement.rst | 10 ++ src/_pytest/assertion/util.py | 12 +- src/_pytest/python_api.py | 180 +++++++++++++++++++++++++- testing/python/approx.py | 230 +++++++++++++++++++++++++++++++++ 4 files changed, 428 insertions(+), 4 deletions(-) create mode 100644 changelog/8335.improvement.rst diff --git a/changelog/8335.improvement.rst b/changelog/8335.improvement.rst new file mode 100644 index 00000000000..f6c0e3343f0 --- /dev/null +++ b/changelog/8335.improvement.rst @@ -0,0 +1,10 @@ +Improved :func:`pytest.approx` assertion messages for sequences of numbers. + +The assertion messages now dumps a table with the index and the error of each diff. +Example:: + + > assert [1, 2, 3, 4] == pytest.approx([1, 3, 3, 5]) + E assert comparison failed for 2 values: + E Index | Obtained | Expected + E 1 | 2 | 3 +- 3.0e-06 + E 3 | 4 | 5 +- 5.0e-06 diff --git a/src/_pytest/assertion/util.py b/src/_pytest/assertion/util.py index 0e54335ab36..d29a2a010a9 100644 --- a/src/_pytest/assertion/util.py +++ b/src/_pytest/assertion/util.py @@ -180,7 +180,15 @@ def _compare_eq_any(left: Any, right: Any, verbose: int = 0) -> List[str]: if istext(left) and istext(right): explanation = _diff_text(left, right, verbose) else: - if type(left) == type(right) and ( + from _pytest.python_api import ApproxBase + + if isinstance(left, ApproxBase) or isinstance(right, ApproxBase): + # Although the common order should be obtained == expected, this ensures both ways + approx_side = left if isinstance(left, ApproxBase) else right + other_side = right if isinstance(left, ApproxBase) else left + + explanation = approx_side._repr_compare(other_side) + elif type(left) == type(right) and ( isdatacls(left) or isattrs(left) or isnamedtuple(left) ): # Note: unlike dataclasses/attrs, namedtuples compare only the @@ -196,9 +204,11 @@ def _compare_eq_any(left: Any, right: Any, verbose: int = 0) -> List[str]: explanation = _compare_eq_dict(left, right, verbose) elif verbose > 0: explanation = _compare_eq_verbose(left, right) + if isiterable(left) and isiterable(right): expl = _compare_eq_iterable(left, right, verbose) explanation.extend(expl) + return explanation diff --git a/src/_pytest/python_api.py b/src/_pytest/python_api.py index 69f01619a43..946d15b3122 100644 --- a/src/_pytest/python_api.py +++ b/src/_pytest/python_api.py @@ -1,7 +1,5 @@ import math import pprint -from collections.abc import Iterable -from collections.abc import Mapping from collections.abc import Sized from decimal import Decimal from numbers import Complex @@ -10,9 +8,13 @@ from typing import Callable from typing import cast from typing import Generic +from typing import Iterable +from typing import List +from typing import Mapping from typing import Optional from typing import overload from typing import Pattern +from typing import Sequence from typing import Tuple from typing import Type from typing import TYPE_CHECKING @@ -38,6 +40,32 @@ def _non_numeric_type_error(value, at: Optional[str]) -> TypeError: ) +def _compare_approx( + full_object: object, + message_data: Sequence[Tuple[str, str, str]], + number_of_elements: int, + different_ids: Sequence[object], + max_abs_diff: float, + max_rel_diff: float, +) -> List[str]: + message_list = list(message_data) + message_list.insert(0, ("Index", "Obtained", "Expected")) + max_sizes = [0, 0, 0] + for index, obtained, expected in message_list: + max_sizes[0] = max(max_sizes[0], len(index)) + max_sizes[1] = max(max_sizes[1], len(obtained)) + max_sizes[2] = max(max_sizes[2], len(expected)) + explanation = [ + f"comparison failed. Mismatched elements: {len(different_ids)} / {number_of_elements}:", + f"Max absolute difference: {max_abs_diff}", + f"Max relative difference: {max_rel_diff}", + ] + [ + f"{indexes:<{max_sizes[0]}} | {obtained:<{max_sizes[1]}} | {expected:<{max_sizes[2]}}" + for indexes, obtained, expected in message_list + ] + return explanation + + # builtin pytest.approx helper @@ -60,6 +88,13 @@ def __init__(self, expected, rel=None, abs=None, nan_ok: bool = False) -> None: def __repr__(self) -> str: raise NotImplementedError + def _repr_compare(self, other_side: Any) -> List[str]: + return [ + "comparison failed", + f"Obtained: {other_side}", + f"Expected: {self}", + ] + def __eq__(self, actual) -> bool: return all( a == self._approx_scalar(x) for a, x in self._yield_comparisons(actual) @@ -107,6 +142,66 @@ def __repr__(self) -> str: list_scalars = _recursive_list_map(self._approx_scalar, self.expected.tolist()) return f"approx({list_scalars!r})" + def _repr_compare(self, other_side: "ndarray") -> List[str]: + import itertools + import math + + def get_value_from_nested_list( + nested_list: List[Any], nd_index: Tuple[Any, ...] + ) -> Any: + """ + Helper function to get the value out of a nested list, given an n-dimensional index. + This mimics numpy's indexing, but for raw nested python lists. + """ + value: Any = nested_list + for i in nd_index: + value = value[i] + return value + + np_array_shape = self.expected.shape + approx_side_as_list = _recursive_list_map( + self._approx_scalar, self.expected.tolist() + ) + + if np_array_shape != other_side.shape: + return [ + "Impossible to compare arrays with different shapes.", + f"Shapes: {np_array_shape} and {other_side.shape}", + ] + + number_of_elements = self.expected.size + max_abs_diff = -math.inf + max_rel_diff = -math.inf + different_ids = [] + for index in itertools.product(*(range(i) for i in np_array_shape)): + approx_value = get_value_from_nested_list(approx_side_as_list, index) + other_value = get_value_from_nested_list(other_side, index) + if approx_value != other_value: + abs_diff = abs(approx_value.expected - other_value) + max_abs_diff = max(max_abs_diff, abs_diff) + if other_value == 0.0: + max_rel_diff = math.inf + else: + max_rel_diff = max(max_rel_diff, abs_diff / abs(other_value)) + different_ids.append(index) + + message_data = [ + ( + str(index), + str(get_value_from_nested_list(other_side, index)), + str(get_value_from_nested_list(approx_side_as_list, index)), + ) + for index in different_ids + ] + return _compare_approx( + self.expected, + message_data, + number_of_elements, + different_ids, + max_abs_diff, + max_rel_diff, + ) + def __eq__(self, actual) -> bool: import numpy as np @@ -147,6 +242,44 @@ def __repr__(self) -> str: {k: self._approx_scalar(v) for k, v in self.expected.items()} ) + def _repr_compare(self, other_side: Mapping[object, float]) -> List[str]: + import math + + approx_side_as_map = { + k: self._approx_scalar(v) for k, v in self.expected.items() + } + + number_of_elements = len(approx_side_as_map) + max_abs_diff = -math.inf + max_rel_diff = -math.inf + different_ids = [] + for (approx_key, approx_value), other_value in zip( + approx_side_as_map.items(), other_side.values() + ): + if approx_value != other_value: + max_abs_diff = max( + max_abs_diff, abs(approx_value.expected - other_value) + ) + max_rel_diff = max( + max_rel_diff, + abs((approx_value.expected - other_value) / approx_value.expected), + ) + different_ids.append(approx_key) + + message_data = [ + (str(key), str(other_side[key]), str(approx_side_as_map[key])) + for key in different_ids + ] + + return _compare_approx( + self.expected, + message_data, + number_of_elements, + different_ids, + max_abs_diff, + max_rel_diff, + ) + def __eq__(self, actual) -> bool: try: if set(actual.keys()) != set(self.expected.keys()): @@ -179,6 +312,48 @@ def __repr__(self) -> str: seq_type(self._approx_scalar(x) for x in self.expected) ) + def _repr_compare(self, other_side: Sequence[float]) -> List[str]: + import math + import numpy as np + + if len(self.expected) != len(other_side): + return [ + "Impossible to compare lists with different sizes.", + f"Lengths: {len(self.expected)} and {len(other_side)}", + ] + + approx_side_as_map = _recursive_list_map(self._approx_scalar, self.expected) + + number_of_elements = len(approx_side_as_map) + max_abs_diff = -math.inf + max_rel_diff = -math.inf + different_ids = [] + for i, (approx_value, other_value) in enumerate( + zip(approx_side_as_map, other_side) + ): + if approx_value != other_value: + abs_diff = abs(approx_value.expected - other_value) + max_abs_diff = max(max_abs_diff, abs_diff) + if other_value == 0.0: + max_rel_diff = np.inf + else: + max_rel_diff = max(max_rel_diff, abs_diff / abs(other_value)) + different_ids.append(i) + + message_data = [ + (str(i), str(other_side[i]), str(approx_side_as_map[i])) + for i in different_ids + ] + + return _compare_approx( + self.expected, + message_data, + number_of_elements, + different_ids, + max_abs_diff, + max_rel_diff, + ) + def __eq__(self, actual) -> bool: try: if len(actual) != len(self.expected): @@ -212,7 +387,6 @@ def __repr__(self) -> str: For example, ``1.0 ± 1e-6``, ``(3+4j) ± 5e-6 ∠ ±180°``. """ - # Don't show a tolerance for values that aren't compared using # tolerances, i.e. non-numerics and infinities. Need to call abs to # handle complex numbers, e.g. (inf + 1j). diff --git a/testing/python/approx.py b/testing/python/approx.py index cb1d9b7c86c..d4a152f3ea4 100644 --- a/testing/python/approx.py +++ b/testing/python/approx.py @@ -1,5 +1,6 @@ import operator import sys +from contextlib import contextmanager from decimal import Decimal from fractions import Fraction from operator import eq @@ -43,7 +44,236 @@ def report_failure(self, out, test, example, got): return MyDocTestRunner() +@contextmanager +def temporary_verbosity(config, verbosity=0): + original_verbosity = config.getoption("verbose") + config.option.verbose = verbosity + try: + yield + finally: + config.option.verbose = original_verbosity + + +@pytest.fixture +def assert_approx_raises_regex(pytestconfig): + def do_assert(lhs, rhs, expected_message, verbosity_level=0): + import re + + with temporary_verbosity(pytestconfig, verbosity_level): + with pytest.raises(AssertionError) as e: + assert lhs == approx(rhs) + + nl = "\n" + obtained_message = str(e.value).splitlines()[1:] + assert len(obtained_message) == len(expected_message), ( + "Regex message length doesn't match obtained.\n" + "Obtained:\n" + f"{nl.join(obtained_message)}\n\n" + "Expected regex:\n" + f"{nl.join(expected_message)}\n\n" + ) + + for i, (obtained_line, expected_line) in enumerate( + zip(obtained_message, expected_message) + ): + regex = re.compile(expected_line) + assert regex.match(obtained_line) is not None, ( + "Unexpected error message:\n" + f"{nl.join(obtained_message)}\n\n" + "Did not match regex:\n" + f"{nl.join(expected_message)}\n\n" + f"With verbosity level = {verbosity_level}, on line {i}" + ) + + return do_assert + + +SOME_FLOAT = r"[+-]?([0-9]*[.])?[0-9]+\s*" +SOME_INT = r"[0-9]+\s*" + + class TestApprox: + def test_error_messages(self, assert_approx_raises_regex): + np = pytest.importorskip("numpy") + + assert_approx_raises_regex( + 2.0, + 1.0, + [ + " comparison failed", + f" Obtained: {SOME_FLOAT}", + f" Expected: {SOME_FLOAT} ± {SOME_FLOAT}", + ], + ) + + assert_approx_raises_regex( + {"a": 1.0, "b": 1000.0, "c": 1000000.0}, + { + "a": 2.0, + "b": 1000.0, + "c": 3000000.0, + }, + [ + r" comparison failed. Mismatched elements: 2 / 3:", + rf" Max absolute difference: {SOME_FLOAT}", + rf" Max relative difference: {SOME_FLOAT}", + r" Index \| Obtained\s+\| Expected ", + rf" a \| {SOME_FLOAT} \| {SOME_FLOAT} ± {SOME_FLOAT}", + rf" c \| {SOME_FLOAT} \| {SOME_FLOAT} ± {SOME_FLOAT}", + ], + ) + + assert_approx_raises_regex( + [1.0, 2.0, 3.0, 4.0], + [1.0, 3.0, 3.0, 5.0], + [ + r" comparison failed. Mismatched elements: 2 / 4:", + rf" Max absolute difference: {SOME_FLOAT}", + rf" Max relative difference: {SOME_FLOAT}", + r" Index \| Obtained\s+\| Expected ", + rf" 1 \| {SOME_FLOAT} \| {SOME_FLOAT} ± {SOME_FLOAT}", + rf" 3 \| {SOME_FLOAT} \| {SOME_FLOAT} ± {SOME_FLOAT}", + ], + ) + + a = np.linspace(0, 100, 20) + b = np.linspace(0, 100, 20) + a[10] += 0.5 + assert_approx_raises_regex( + a, + b, + [ + r" comparison failed. Mismatched elements: 1 / 20:", + rf" Max absolute difference: {SOME_FLOAT}", + rf" Max relative difference: {SOME_FLOAT}", + r" Index \| Obtained\s+\| Expected", + rf" \(10,\) \| {SOME_FLOAT} \| {SOME_FLOAT} ± {SOME_FLOAT}", + ], + ) + + assert_approx_raises_regex( + np.array( + [ + [[1.1987311, 12412342.3], [3.214143244, 1423412423415.677]], + [[1, 2], [3, 219371297321973]], + ] + ), + np.array( + [ + [[1.12313, 12412342.3], [3.214143244, 534523542345.677]], + [[1, 2], [3, 7]], + ] + ), + [ + r" comparison failed. Mismatched elements: 3 / 8:", + rf" Max absolute difference: {SOME_FLOAT}", + rf" Max relative difference: {SOME_FLOAT}", + r" Index\s+\| Obtained\s+\| Expected\s+", + rf" \(0, 0, 0\) \| {SOME_FLOAT} \| {SOME_FLOAT} ± {SOME_FLOAT}", + rf" \(0, 1, 1\) \| {SOME_FLOAT} \| {SOME_FLOAT} ± {SOME_FLOAT}", + rf" \(1, 1, 1\) \| {SOME_FLOAT} \| {SOME_FLOAT} ± {SOME_FLOAT}", + ], + ) + + # Specific test for comparison with 0.0 (relative diff will be 'inf') + assert_approx_raises_regex( + [0.0], + [1.0], + [ + r" comparison failed. Mismatched elements: 1 / 1:", + rf" Max absolute difference: {SOME_FLOAT}", + r" Max relative difference: inf", + r" Index \| Obtained\s+\| Expected ", + rf"\s*0\s*\| {SOME_FLOAT} \| {SOME_FLOAT} ± {SOME_FLOAT}", + ], + ) + + assert_approx_raises_regex( + np.array([0.0]), + np.array([1.0]), + [ + r" comparison failed. Mismatched elements: 1 / 1:", + rf" Max absolute difference: {SOME_FLOAT}", + r" Max relative difference: inf", + r" Index \| Obtained\s+\| Expected ", + rf"\s*\(0,\)\s*\| {SOME_FLOAT} \| {SOME_FLOAT} ± {SOME_FLOAT}", + ], + ) + + def test_error_messages_invalid_args(self, assert_approx_raises_regex): + np = pytest.importorskip("numpy") + with pytest.raises(AssertionError) as e: + assert np.array([[1.2, 3.4], [4.0, 5.0]]) == pytest.approx( + np.array([[4.0], [5.0]]) + ) + message = "\n".join(str(e.value).split("\n")[1:]) + assert message == "\n".join( + [ + " Impossible to compare arrays with different shapes.", + " Shapes: (2, 1) and (2, 2)", + ] + ) + + with pytest.raises(AssertionError) as e: + assert [1.0, 2.0, 3.0] == pytest.approx([4.0, 5.0]) + message = "\n".join(str(e.value).split("\n")[1:]) + assert message == "\n".join( + [ + " Impossible to compare lists with different sizes.", + " Lengths: 2 and 3", + ] + ) + + def test_error_messages_with_different_verbosity(self, assert_approx_raises_regex): + np = pytest.importorskip("numpy") + for v in [0, 1, 2]: + # Verbosity level doesn't affect the error message for scalars + assert_approx_raises_regex( + 2.0, + 1.0, + [ + " comparison failed", + f" Obtained: {SOME_FLOAT}", + f" Expected: {SOME_FLOAT} ± {SOME_FLOAT}", + ], + verbosity_level=v, + ) + + a = np.linspace(1, 101, 20) + b = np.linspace(2, 102, 20) + assert_approx_raises_regex( + a, + b, + [ + r" comparison failed. Mismatched elements: 20 / 20:", + rf" Max absolute difference: {SOME_FLOAT}", + rf" Max relative difference: {SOME_FLOAT}", + r" Index \| Obtained\s+\| Expected", + rf" \(0,\)\s+\| {SOME_FLOAT} \| {SOME_FLOAT} ± {SOME_FLOAT}", + rf" \(1,\)\s+\| {SOME_FLOAT} \| {SOME_FLOAT} ± {SOME_FLOAT}", + rf" \(2,\)\s+\| {SOME_FLOAT} \| {SOME_FLOAT} ± {SOME_FLOAT}...", + "", + rf"\s*...Full output truncated \({SOME_INT} lines hidden\), use '-vv' to show", + ], + verbosity_level=0, + ) + + assert_approx_raises_regex( + a, + b, + [ + r" comparison failed. Mismatched elements: 20 / 20:", + rf" Max absolute difference: {SOME_FLOAT}", + rf" Max relative difference: {SOME_FLOAT}", + r" Index \| Obtained\s+\| Expected", + ] + + [ + rf" \({i},\)\s+\| {SOME_FLOAT} \| {SOME_FLOAT} ± {SOME_FLOAT}" + for i in range(20) + ], + verbosity_level=2, + ) + def test_repr_string(self): assert repr(approx(1.0)) == "1.0 ± 1.0e-06" assert repr(approx([1.0, 2.0])) == "approx([1.0 ± 1.0e-06, 2.0 ± 2.0e-06])" From 8f49ffdb57e10264096f479140e258c665b06421 Mon Sep 17 00:00:00 2001 From: pytest bot Date: Sun, 2 May 2021 00:12:49 +0000 Subject: [PATCH 267/630] [automated] Update plugin list --- doc/en/reference/plugin_list.rst | 51 ++++++++++++++++---------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/doc/en/reference/plugin_list.rst b/doc/en/reference/plugin_list.rst index a25c7fb3874..3338e999bb4 100644 --- a/doc/en/reference/plugin_list.rst +++ b/doc/en/reference/plugin_list.rst @@ -6,7 +6,7 @@ Plugin List PyPI projects that match "pytest-\*" are considered plugins and are listed automatically. Packages classified as inactive are excluded. -This list contains 856 plugins. +This list contains 857 plugins. ============================================================================================================== ======================================================================================================================================================================== ============== ===================== ================================================ name summary last release status requires @@ -22,7 +22,7 @@ name `pytest-aioresponses `_ py.test integration for aioresponses Dec 21, 2020 4 - Beta pytest (>=3.5.0) `pytest-aioworkers `_ A plugin to test aioworkers project with pytest Dec 04, 2019 4 - Beta pytest (>=3.5.0) `pytest-airflow `_ pytest support for airflow. Apr 03, 2019 3 - Alpha pytest (>=4.4.0) -`pytest-alembic `_ A pytest plugin for verifying alembic migrations. Jul 13, 2020 N/A pytest (>=1.0) +`pytest-alembic `_ A pytest plugin for verifying alembic migrations. Apr 26, 2021 N/A pytest (>=1.0) `pytest-allclose `_ Pytest fixture extending Numpy's allclose function Jul 30, 2019 5 - Production/Stable pytest `pytest-allure-adaptor `_ Plugin for py.test to generate allure xml reports Jan 10, 2018 N/A pytest (>=2.7.3) `pytest-allure-adaptor2 `_ Plugin for py.test to generate allure xml reports Oct 14, 2020 N/A pytest (>=2.7.3) @@ -55,6 +55,7 @@ name `pytest-asyncio-cooperative `_ Run all your asynchronous tests cooperatively. Apr 17, 2021 4 - Beta N/A `pytest-asyncio-network-simulator `_ pytest-asyncio-network-simulator: Plugin for pytest for simulator the network in tests Jul 31, 2018 3 - Alpha pytest (<3.7.0,>=3.3.2) `pytest-async-mongodb `_ pytest plugin for async MongoDB Oct 18, 2017 5 - Production/Stable pytest (>=2.5.2) +`pytest-async-sqlalchemy `_ Database testing fixtures using the SQLAlchemy asyncio API Apr 25, 2021 4 - Beta pytest (>=6.0.0) `pytest-atomic `_ Skip rest of tests if previous test failed. Nov 24, 2018 4 - Beta N/A `pytest-attrib `_ pytest plugin to select tests based on attributes similar to the nose-attrib plugin May 24, 2016 4 - Beta N/A `pytest-austin `_ Austin plugin for pytest Oct 11, 2020 4 - Beta N/A @@ -112,7 +113,7 @@ name `pytest-change-report `_ turn . into √,turn F into x Sep 14, 2020 N/A pytest `pytest-chdir `_ A pytest fixture for changing current working directory Jan 28, 2020 N/A pytest (>=5.0.0,<6.0.0) `pytest-check `_ A pytest plugin that allows multiple failures per test. Dec 27, 2020 5 - Production/Stable N/A -`pytest-checkdocs `_ check the README when running tests Apr 18, 2021 5 - Production/Stable pytest (>=4.6) ; extra == 'testing' +`pytest-checkdocs `_ check the README when running tests Apr 30, 2021 5 - Production/Stable pytest (>=4.6) ; extra == 'testing' `pytest-checkipdb `_ plugin to check if there are ipdb debugs left Jul 22, 2020 5 - Production/Stable pytest (>=2.9.2) `pytest-check-links `_ Check links in files Jul 29, 2020 N/A pytest (>=4.6) `pytest-check-mk `_ pytest plugin to test Check_MK checks Nov 19, 2015 4 - Beta pytest @@ -137,7 +138,7 @@ name `pytest-concurrent `_ Concurrently execute test cases with multithread, multiprocess and gevent Jan 12, 2019 4 - Beta pytest (>=3.1.1) `pytest-config `_ Base configurations and utilities for developing your Python project test suite with pytest. Nov 07, 2014 5 - Production/Stable N/A `pytest-confluence-report `_ Package stands for pytest plugin to upload results into Confluence page. Nov 06, 2020 N/A N/A -`pytest-console-scripts `_ Pytest plugin for testing console scripts Nov 20, 2020 4 - Beta N/A +`pytest-console-scripts `_ Pytest plugin for testing console scripts Apr 26, 2021 4 - Beta N/A `pytest-consul `_ pytest plugin with fixtures for testing consul aware apps Nov 24, 2018 3 - Alpha pytest `pytest-contextfixture `_ Define pytest fixtures as context managers. Mar 12, 2013 4 - Beta N/A `pytest-contexts `_ A plugin to run tests written with the Contexts framework using pytest Jul 23, 2018 4 - Beta N/A @@ -192,7 +193,6 @@ name `pytest-dicom `_ pytest plugin to provide DICOM fixtures Dec 19, 2018 3 - Alpha pytest `pytest-dictsdiff `_ Jul 26, 2019 N/A N/A `pytest-diff `_ A simple plugin to use with pytest Mar 30, 2019 4 - Beta pytest (>=3.5.0) -`pytest-diffeo `_ Common py.test support for Diffeo packages Apr 08, 2016 3 - Alpha N/A `pytest-disable `_ pytest plugin to disable a test and skip it from testrun Sep 10, 2015 4 - Beta N/A `pytest-disable-plugin `_ Disable plugins per test Feb 28, 2019 4 - Beta pytest (>=3.5.0) `pytest-discord `_ A pytest plugin to notify test results to a Discord channel. Mar 20, 2021 3 - Alpha pytest (!=6.0.0,<7,>=3.3.2) @@ -248,7 +248,7 @@ name `pytest-easy-api `_ Simple API testing with pytest Mar 26, 2018 N/A N/A `pytest-easyMPI `_ Package that supports mpi tests in pytest Oct 21, 2020 N/A N/A `pytest-easyread `_ pytest plugin that makes terminal printouts of the reports easier to read Nov 17, 2017 N/A N/A -`pytest-easy-server `_ Pytest plugin for easy testing against servers Apr 05, 2021 4 - Beta pytest (<5.0.0,>=4.3.1) ; python_version < "3.5" +`pytest-easy-server `_ Pytest plugin for easy testing against servers May 01, 2021 4 - Beta pytest (<5.0.0,>=4.3.1) ; python_version < "3.5" `pytest-ec2 `_ Pytest execution on EC2 instance Oct 22, 2019 3 - Alpha N/A `pytest-echo `_ pytest plugin with mechanisms for echoing environment variables, package version and generic attributes Jan 08, 2020 5 - Production/Stable N/A `pytest-elasticsearch `_ Elasticsearch process and client fixtures for py.test. Feb 19, 2020 5 - Production/Stable pytest (>=3.0.0) @@ -322,7 +322,7 @@ name `pytest-forward-compatability `_ A name to avoid typosquating pytest-foward-compatibility Sep 06, 2020 N/A N/A `pytest-forward-compatibility `_ A pytest plugin to shim pytest commandline options for fowards compatibility Sep 29, 2020 N/A N/A `pytest-freezegun `_ Wrap tests with fixtures in freeze_time Jul 19, 2020 4 - Beta pytest (>=3.0.0) -`pytest-freeze-reqs `_ Check if requirement files are frozen Nov 14, 2019 N/A N/A +`pytest-freeze-reqs `_ Check if requirement files are frozen Apr 29, 2021 N/A N/A `pytest-func-cov `_ Pytest plugin for measuring function coverage Apr 15, 2021 3 - Alpha pytest (>=5) `pytest-funparam `_ An alternative way to parametrize test cases Apr 23, 2021 4 - Beta pytest (>=4.6.0) `pytest-fxa `_ pytest plugin for Firefox Accounts Aug 28, 2018 5 - Production/Stable N/A @@ -351,27 +351,27 @@ name `pytest-helm-charts `_ A plugin to provide different types and configs of Kubernetes clusters that can be used for testing. Dec 22, 2020 4 - Beta pytest (>=6.1.2,<7.0.0) `pytest-helper `_ Functions to help in using the pytest testing framework May 31, 2019 5 - Production/Stable N/A `pytest-helpers `_ pytest helpers May 17, 2020 N/A pytest -`pytest-helpers-namespace `_ Pytest Helpers Namespace Plugin Mar 24, 2021 5 - Production/Stable pytest (>=6.1.1) +`pytest-helpers-namespace `_ Pytest Helpers Namespace Plugin Apr 29, 2021 5 - Production/Stable pytest (>=6.0.0) `pytest-hidecaptured `_ Hide captured output May 04, 2018 4 - Beta pytest (>=2.8.5) `pytest-historic `_ Custom report to display pytest historical execution records Apr 08, 2020 N/A pytest `pytest-historic-hook `_ Custom listener to store execution results into MYSQL DB, which is used for pytest-historic report Apr 08, 2020 N/A pytest `pytest-homeassistant `_ A pytest plugin for use with homeassistant custom components. Aug 12, 2020 4 - Beta N/A -`pytest-homeassistant-custom-component `_ Experimental package to automatically extract test plugins for Home Assistant custom components Apr 16, 2021 3 - Alpha pytest (==6.2.2) +`pytest-homeassistant-custom-component `_ Experimental package to automatically extract test plugins for Home Assistant custom components May 01, 2021 3 - Alpha pytest (==6.2.3) `pytest-honors `_ Report on tests that honor constraints, and guard against regressions Mar 06, 2020 4 - Beta N/A `pytest-hoverfly `_ Simplify working with Hoverfly from pytest Mar 04, 2021 N/A pytest (>=5.0) `pytest-hoverfly-wrapper `_ Integrates the Hoverfly HTTP proxy into Pytest Jan 31, 2021 4 - Beta N/A `pytest-html `_ pytest plugin for generating HTML reports Dec 13, 2020 5 - Production/Stable pytest (!=6.0.0,>=5.0) `pytest-html-lee `_ optimized pytest plugin for generating HTML reports Jun 30, 2020 5 - Production/Stable pytest (>=5.0) `pytest-html-profiling `_ Pytest plugin for generating HTML reports with per-test profiling and optionally call graph visualizations. Based on pytest-html by Dave Hunt. Feb 11, 2020 5 - Production/Stable pytest (>=3.0) -`pytest-html-reporter `_ Generates a static html report based on pytest framework Apr 20, 2021 N/A N/A +`pytest-html-reporter `_ Generates a static html report based on pytest framework Apr 25, 2021 N/A N/A `pytest-html-thread `_ pytest plugin for generating HTML reports Dec 29, 2020 5 - Production/Stable N/A `pytest-http `_ Fixture "http" for http requests Dec 05, 2019 N/A N/A `pytest-httpbin `_ Easily test your HTTP library against a local copy of httpbin Feb 11, 2019 5 - Production/Stable N/A `pytest-http-mocker `_ Pytest plugin for http mocking (via https://github.com/vilus/mocker) Oct 20, 2019 N/A N/A `pytest-httpretty `_ A thin wrapper of HTTPretty for pytest Feb 16, 2014 3 - Alpha N/A `pytest-httpserver `_ pytest-httpserver is a httpserver for pytest Mar 16, 2021 3 - Alpha pytest ; extra == 'dev' -`pytest-httpx `_ Send responses to httpx. Mar 01, 2021 5 - Production/Stable pytest (==6.*) -`pytest-httpx-blockage `_ Disable httpx requests during a test run Apr 09, 2021 N/A pytest (>=6.2.2) +`pytest-httpx `_ Send responses to httpx. Apr 27, 2021 5 - Production/Stable pytest (==6.*) +`pytest-httpx-blockage `_ Disable httpx requests during a test run Apr 28, 2021 N/A pytest (>=6.2.3) `pytest-hue `_ Visualise PyTest status via your Phillips Hue lights May 09, 2019 N/A N/A `pytest-hylang `_ Pytest plugin to allow running tests written in hylang Mar 28, 2021 N/A pytest `pytest-hypo-25 `_ help hypo module for pytest Jan 12, 2020 3 - Alpha N/A @@ -397,7 +397,7 @@ name `pytest-involve `_ Run tests covering a specific file or changeset Feb 02, 2020 4 - Beta pytest (>=3.5.0) `pytest-ipdb `_ A py.test plug-in to enable drop to ipdb debugger on test failure. Sep 02, 2014 2 - Pre-Alpha N/A `pytest-ipynb `_ THIS PROJECT IS ABANDONED Jan 29, 2019 3 - Alpha N/A -`pytest-isort `_ py.test plugin to check import ordering using isort Jan 13, 2021 5 - Production/Stable N/A +`pytest-isort `_ py.test plugin to check import ordering using isort Apr 27, 2021 5 - Production/Stable N/A `pytest-it `_ Pytest plugin to display test reports as a plaintext spec, inspired by Rspec: https://github.com/mattduck/pytest-it. Jan 22, 2020 4 - Beta N/A `pytest-iterassert `_ Nicer list and iterable assertion messages for pytest May 11, 2020 3 - Alpha N/A `pytest-jasmine `_ Run jasmine tests from your pytest test suite Nov 04, 2017 1 - Planning N/A @@ -472,7 +472,7 @@ name `pytest-mock-helper `_ Help you mock HTTP call and generate mock code Jan 24, 2018 N/A pytest `pytest-mockito `_ Base fixtures for mockito Jul 11, 2018 4 - Beta N/A `pytest-mockredis `_ An in-memory mock of a Redis server that runs in a separate thread. This is to be used for unit-tests that require a Redis database. Jan 02, 2018 2 - Pre-Alpha N/A -`pytest-mock-resources `_ A pytest plugin for easily instantiating reproducible mock resources. Feb 17, 2021 N/A pytest (>=1.0) +`pytest-mock-resources `_ A pytest plugin for easily instantiating reproducible mock resources. Apr 26, 2021 N/A pytest (>=1.0) `pytest-mock-server `_ Mock server plugin for pytest Apr 06, 2020 4 - Beta N/A `pytest-mockservers `_ A set of fixtures to test your requests to HTTP/UDP servers Mar 31, 2020 N/A pytest (>=4.3.0) `pytest-modifyjunit `_ Utility for adding additional properties to junit xml for IDM QE Jan 10, 2019 N/A N/A @@ -573,7 +573,7 @@ name `pytest-ponyorm `_ PonyORM in Pytest Oct 31, 2018 N/A pytest (>=3.1.1) `pytest-poo `_ Visualize your crappy tests Mar 25, 2021 5 - Production/Stable pytest (>=2.3.4) `pytest-poo-fail `_ Visualize your failed tests with poo Feb 12, 2015 5 - Production/Stable N/A -`pytest-pop `_ A pytest plugin to help with testing pop projects Aug 13, 2020 5 - Production/Stable pytest (>=5.4.0) +`pytest-pop `_ A pytest plugin to help with testing pop projects Apr 27, 2021 5 - Production/Stable pytest (>=5.4.0) `pytest-portion `_ Select a portion of the collected tests Jan 28, 2021 4 - Beta pytest (>=3.5.0) `pytest-postgres `_ Run PostgreSQL in Docker container in Pytest. Mar 22, 2020 N/A pytest `pytest-postgresql `_ Postgresql fixtures and fixture factories for Pytest. Feb 23, 2021 5 - Production/Stable pytest (>=3.0.0) @@ -651,15 +651,15 @@ name `pytest-rerunfailures `_ pytest plugin to re-run tests to eliminate flaky failures Sep 29, 2020 5 - Production/Stable pytest (>=5.0) `pytest-resilient-circuits `_ Resilient Circuits fixtures for PyTest. Apr 15, 2021 N/A N/A `pytest-resource `_ Load resource fixture plugin to use with pytest Nov 14, 2018 4 - Beta N/A -`pytest-resource-path `_ Provides path for uniform access to test resources in isolated directory Aug 18, 2020 5 - Production/Stable pytest (>=3.5.0) +`pytest-resource-path `_ Provides path for uniform access to test resources in isolated directory May 01, 2021 5 - Production/Stable pytest (>=3.5.0) `pytest-responsemock `_ Simplified requests calls mocking for pytest Oct 10, 2020 5 - Production/Stable N/A -`pytest-responses `_ py.test integration for responses Jan 29, 2019 N/A N/A +`pytest-responses `_ py.test integration for responses Apr 26, 2021 N/A pytest (>=2.5) `pytest-restrict `_ Pytest plugin to restrict the test types allowed Dec 03, 2020 5 - Production/Stable pytest `pytest-rethinkdb `_ A RethinkDB plugin for pytest. Jul 24, 2016 4 - Beta N/A `pytest-reverse `_ Pytest plugin to reverse test order. Dec 27, 2020 5 - Production/Stable pytest `pytest-ringo `_ pytest plugin to test webapplications using the Ringo webframework Sep 27, 2017 3 - Alpha N/A `pytest-rng `_ Fixtures for seeding tests and making randomness reproducible Aug 08, 2019 5 - Production/Stable pytest -`pytest-roast `_ pytest plugin for ROAST configuration override and fixtures Apr 05, 2021 5 - Production/Stable pytest +`pytest-roast `_ pytest plugin for ROAST configuration override and fixtures Apr 30, 2021 5 - Production/Stable pytest `pytest-rocketchat `_ Pytest to Rocket.Chat reporting plugin Apr 18, 2021 5 - Production/Stable N/A `pytest-rotest `_ Pytest integration with rotest Sep 08, 2019 N/A pytest (>=3.5.0) `pytest-rpc `_ Extend py.test for RPC OpenStack testing. Feb 22, 2019 4 - Beta pytest (~=3.6) @@ -676,19 +676,19 @@ name `pytest-sanic `_ a pytest plugin for Sanic Feb 27, 2021 N/A pytest (>=5.2) `pytest-sanity `_ Dec 07, 2020 N/A N/A `pytest-sa-pg `_ May 14, 2019 N/A N/A -`pytest-sbase `_ A complete web automation framework for end-to-end testing. Apr 24, 2021 5 - Production/Stable N/A +`pytest-sbase `_ A complete web automation framework for end-to-end testing. May 01, 2021 5 - Production/Stable N/A `pytest-scenario `_ pytest plugin for test scenarios Feb 06, 2017 3 - Alpha N/A `pytest-schema `_ 👍 Validate return values against a schema-like object in testing Aug 31, 2020 5 - Production/Stable pytest (>=3.5.0) `pytest-securestore `_ An encrypted password store for use within pytest cases Jun 19, 2019 4 - Beta N/A `pytest-select `_ A pytest plugin which allows to (de-)select tests from a file. Jan 18, 2019 3 - Alpha pytest (>=3.0) `pytest-selenium `_ pytest plugin for Selenium Sep 19, 2020 5 - Production/Stable pytest (>=5.0.0) -`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Apr 24, 2021 5 - Production/Stable N/A +`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. May 01, 2021 5 - Production/Stable N/A `pytest-selenium-enhancer `_ pytest plugin for Selenium Nov 26, 2020 5 - Production/Stable N/A `pytest-selenium-pdiff `_ A pytest package implementing perceptualdiff for Selenium tests. Apr 06, 2017 2 - Pre-Alpha N/A `pytest-send-email `_ Send pytest execution result email Dec 04, 2019 N/A N/A `pytest-sentry `_ A pytest plugin to send testrun information to Sentry.io Apr 21, 2021 N/A pytest `pytest-server-fixtures `_ Extensible server fixures for py.test May 28, 2019 5 - Production/Stable pytest -`pytest-serverless `_ Automatically mocks resources from serverless.yml in pytest using moto. Feb 20, 2021 4 - Beta N/A +`pytest-serverless `_ Automatically mocks resources from serverless.yml in pytest using moto. Apr 27, 2021 4 - Beta N/A `pytest-services `_ Services plugin for pytest testing framework Oct 30, 2020 6 - Mature N/A `pytest-session2file `_ pytest-session2file (aka: pytest-session_to_file for v0.1.0 - v0.1.2) is a py.test plugin for capturing and saving to file the stdout of py.test. Jan 26, 2021 3 - Alpha pytest `pytest-session-fixture-globalize `_ py.test plugin to make session fixtures behave as if written in conftest, even if it is written in some modules May 15, 2018 4 - Beta N/A @@ -722,12 +722,13 @@ name `pytest-spawner `_ py.test plugin to spawn process and communicate with them. Jul 31, 2015 4 - Beta N/A `pytest-spec `_ Library pytest-spec is a pytest plugin to display test execution output like a SPECIFICATION. Jan 14, 2021 N/A N/A `pytest-sphinx `_ Doctest plugin for pytest with support for Sphinx-specific doctest-directives Aug 05, 2020 4 - Beta N/A -`pytest-spiratest `_ Exports unit tests as test runs in SpiraTest/Team/Plan Feb 12, 2021 N/A N/A +`pytest-spiratest `_ Exports unit tests as test runs in SpiraTest/Team/Plan Apr 28, 2021 N/A N/A `pytest-splinter `_ Splinter plugin for pytest testing framework Dec 25, 2020 6 - Mature N/A `pytest-split `_ Pytest plugin for splitting test suite based on test execution time Apr 07, 2020 1 - Planning N/A `pytest-splitio `_ Split.io SDK integration for e2e tests Sep 22, 2020 N/A pytest (<7,>=5.0) `pytest-split-tests `_ A Pytest plugin for running a subset of your tests by splitting them in to equally sized groups. Forked from Mark Adams' original project pytest-test-groups. May 28, 2019 N/A pytest (>=2.5) -`pytest-splunk-addon `_ A Dynamic test tool for Splunk Apps and Add-ons Feb 26, 2021 N/A pytest (>5.4.0,<6.1) +`pytest-split-tests-tresorit `_ Feb 22, 2021 1 - Planning N/A +`pytest-splunk-addon `_ A Dynamic test tool for Splunk Apps and Add-ons Apr 28, 2021 N/A pytest (>5.4.0,<6.3) `pytest-splunk-addon-ui-smartx `_ Library to support testing Splunk Add-on UX Mar 24, 2021 N/A N/A `pytest-splunk-env `_ pytest fixtures for interaction with Splunk Enterprise and Splunk Cloud Oct 22, 2020 N/A pytest (>=6.1.1,<7.0.0) `pytest-sqitch `_ sqitch for pytest Apr 06, 2020 4 - Beta N/A @@ -763,7 +764,7 @@ name `pytest-teamcity-logblock `_ py.test plugin to introduce block structure in teamcity build log, if output is not captured May 15, 2018 4 - Beta N/A `pytest-telegram `_ Pytest to Telegram reporting plugin Dec 10, 2020 5 - Production/Stable N/A `pytest-tempdir `_ Predictable and repeatable tempdir support. Oct 11, 2019 4 - Beta pytest (>=2.8.1) -`pytest-terraform `_ A pytest plugin for using terraform fixtures Oct 20, 2020 N/A pytest (>=6.0.0,<6.1.0) +`pytest-terraform `_ A pytest plugin for using terraform fixtures Apr 25, 2021 N/A pytest (>=6.0,<7.0) `pytest-terraform-fixture `_ generate terraform resources to use with pytest Nov 14, 2018 4 - Beta N/A `pytest-testbook `_ A plugin to run tests written in Jupyter notebook Dec 11, 2016 3 - Alpha N/A `pytest-testconfig `_ Test configuration plugin for pytest. Jan 11, 2020 4 - Beta pytest (>=3.5.0) @@ -772,7 +773,7 @@ name `pytest-test-groups `_ A Pytest plugin for running a subset of your tests by splitting them in to equally sized groups. Oct 25, 2016 5 - Production/Stable N/A `pytest-testinfra `_ Test infrastructures Apr 18, 2021 5 - Production/Stable pytest (!=3.0.2) `pytest-testlink-adaptor `_ pytest reporting plugin for testlink Dec 20, 2018 4 - Beta pytest (>=2.6) -`pytest-testmon `_ selects tests affected by changed files and methods Mar 18, 2021 4 - Beta N/A +`pytest-testmon `_ selects tests affected by changed files and methods Apr 28, 2021 4 - Beta N/A `pytest-testobject `_ Plugin to use TestObject Suites with Pytest Sep 24, 2019 4 - Beta pytest (>=3.1.1) `pytest-testrail `_ pytest plugin for creating TestRail runs and adding results Aug 27, 2020 N/A pytest (>=3.6) `pytest-testrail2 `_ A small example package Nov 17, 2020 N/A pytest (>=5) From f1a97512dab4786bf59cfb22db687f4ead587c6d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 May 2021 03:01:15 +0000 Subject: [PATCH 268/630] Bump anyio[curio,trio] in /testing/plugins_integration Bumps [anyio[curio,trio]](https://github.com/agronholm/anyio) from 3.0.0 to 3.0.1. - [Release notes](https://github.com/agronholm/anyio/releases) - [Changelog](https://github.com/agronholm/anyio/blob/master/docs/versionhistory.rst) - [Commits](https://github.com/agronholm/anyio/compare/3.0.0...3.0.1) Signed-off-by: dependabot[bot] --- testing/plugins_integration/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/plugins_integration/requirements.txt b/testing/plugins_integration/requirements.txt index 62d7f9d5ae7..97a35ebbb17 100644 --- a/testing/plugins_integration/requirements.txt +++ b/testing/plugins_integration/requirements.txt @@ -1,4 +1,4 @@ -anyio[curio,trio]==3.0.0 +anyio[curio,trio]==3.0.1 django==3.2 pytest-asyncio==0.15.1 pytest-bdd==4.0.2 From f772e5e2b36c7a83082d5f10b2a67ccbe9d4e24f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 3 May 2021 17:21:32 +0000 Subject: [PATCH 269/630] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black: 21.4b0 → 21.4b2](https://github.com/psf/black/compare/21.4b0...21.4b2) - [github.com/asottile/pyupgrade: v2.13.0 → v2.14.0](https://github.com/asottile/pyupgrade/compare/v2.13.0...v2.14.0) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c0857ceba17..eead96a02d8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/psf/black - rev: 21.4b0 + rev: 21.4b2 hooks: - id: black args: [--safe, --quiet] @@ -34,7 +34,7 @@ repos: - id: reorder-python-imports args: ['--application-directories=.:src', --py36-plus] - repo: https://github.com/asottile/pyupgrade - rev: v2.13.0 + rev: v2.14.0 hooks: - id: pyupgrade args: [--py36-plus] From 4e048e08278fdf4e5ed0181d442c972d271a260e Mon Sep 17 00:00:00 2001 From: alshapton Date: Mon, 3 May 2021 21:42:23 +0100 Subject: [PATCH 270/630] 8625 - small grammar fix (#8627) --- AUTHORS | 1 + doc/en/explanation/fixtures.rst | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index fcae73823fa..ce29748d6e8 100644 --- a/AUTHORS +++ b/AUTHORS @@ -24,6 +24,7 @@ Andras Tim Andrea Cimatoribus Andreas Motl Andreas Zeidler +Andrew Shapton Andrey Paramonov Andrzej Klajnert Andrzej Ostrowski diff --git a/doc/en/explanation/fixtures.rst b/doc/en/explanation/fixtures.rst index 92b5e797856..3bda29d575e 100644 --- a/doc/en/explanation/fixtures.rst +++ b/doc/en/explanation/fixtures.rst @@ -153,6 +153,6 @@ to do this is by loading these data in a fixture for use by your tests. This makes use of the automatic caching mechanisms of pytest. Another good approach is by adding the data files in the ``tests`` folder. -There are also community plugins available to help managing this aspect of +There are also community plugins available to help to manage this aspect of testing, e.g. `pytest-datadir `__ and `pytest-datafiles `__. From f1570dc038585c8e5746f9bfdeb76321cd8be722 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 14 Apr 2021 11:31:28 +0200 Subject: [PATCH 271/630] Ignore various warnings from Python 3.10 https://github.com/benjaminp/six/issues/341 https://github.com/benjaminp/six/pull/352 https://github.com/pypa/setuptools/pull/2517 --- pyproject.toml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index dc26b4d8ddf..bead1ec7fef 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,6 +42,14 @@ filterwarnings = [ "default:invalid escape sequence:DeprecationWarning", # ignore use of unregistered marks, because we use many to test the implementation "ignore::_pytest.warning_types.PytestUnknownMarkWarning", + # https://github.com/benjaminp/six/issues/341 + "ignore:_SixMetaPathImporter\\.exec_module\\(\\) not found; falling back to load_module\\(\\):ImportWarning", + # https://github.com/benjaminp/six/pull/352 + "ignore:_SixMetaPathImporter\\.find_spec\\(\\) not found; falling back to find_module\\(\\):ImportWarning", + # https://github.com/pypa/setuptools/pull/2517 + "ignore:VendorImporter\\.find_spec\\(\\) not found; falling back to find_module\\(\\):ImportWarning", + # https://github.com/pytest-dev/execnet/pull/127 + "ignore:isSet\\(\\) is deprecated, use is_set\\(\\) instead:DeprecationWarning", ] pytester_example_dir = "testing/example_scripts" markers = [ From 710446420c0bb98f0761724ec865a894820bb610 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 14 Apr 2021 11:37:34 +0200 Subject: [PATCH 272/630] Adjust enum reprs for Python 3.10 Potential fix for #8546 --- testing/python/metafunc.py | 5 ++++- testing/test_pytester.py | 14 ++++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/testing/python/metafunc.py b/testing/python/metafunc.py index 4a4b141c4b7..ce689e56d77 100644 --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -448,7 +448,10 @@ def test_idmaker_enum(self) -> None: enum = pytest.importorskip("enum") e = enum.Enum("Foo", "one, two") result = idmaker(("a", "b"), [pytest.param(e.one, e.two)]) - assert result == ["Foo.one-Foo.two"] + if sys.version_info[:2] >= (3, 10): + assert result == ["one-two"] + else: + assert result == ["Foo.one-Foo.two"] def test_idmaker_idfn(self) -> None: """#351""" diff --git a/testing/test_pytester.py b/testing/test_pytester.py index 7b16c69c23d..19f28504d88 100644 --- a/testing/test_pytester.py +++ b/testing/test_pytester.py @@ -744,10 +744,16 @@ def test_run_result_repr() -> None: # known exit code r = pytester_mod.RunResult(1, outlines, errlines, duration=0.5) - assert ( - repr(r) == "" - ) + if sys.version_info[:2] >= (3, 10): + assert repr(r) == ( + "" + ) + else: + assert repr(r) == ( + "" + ) # unknown exit code: just the number r = pytester_mod.RunResult(99, outlines, errlines, duration=0.5) From e354c5c919f3abc83e38a0e4eaca87e27335bb8f Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 4 May 2021 14:27:21 +0200 Subject: [PATCH 273/630] Fix warning filters used in tests --- testing/acceptance_test.py | 4 ++-- testing/python/collect.py | 2 +- testing/test_config.py | 2 +- testing/test_terminal.py | 4 ++-- testing/test_threadexception.py | 6 +++--- testing/test_unraisableexception.py | 6 +++--- testing/test_warnings.py | 30 ++++++++++++++--------------- 7 files changed, 27 insertions(+), 27 deletions(-) diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py index cc3f160d23d..e8a9ec4b843 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -1173,7 +1173,7 @@ def test_usage_error_code(pytester: Pytester) -> None: assert result.ret == ExitCode.USAGE_ERROR -@pytest.mark.filterwarnings("default") +@pytest.mark.filterwarnings("default::pytest.PytestUnhandledCoroutineWarning") def test_warn_on_async_function(pytester: Pytester) -> None: # In the below we .close() the coroutine only to avoid # "RuntimeWarning: coroutine 'test_2' was never awaited" @@ -1206,7 +1206,7 @@ def test_3(): ) -@pytest.mark.filterwarnings("default") +@pytest.mark.filterwarnings("default::pytest.PytestUnhandledCoroutineWarning") def test_warn_on_async_gen_function(pytester: Pytester) -> None: pytester.makepyfile( test_async=""" diff --git a/testing/python/collect.py b/testing/python/collect.py index 0edb4452ee3..af3ffd84433 100644 --- a/testing/python/collect.py +++ b/testing/python/collect.py @@ -1237,7 +1237,7 @@ class Test(object): assert result.ret == ExitCode.NO_TESTS_COLLECTED -@pytest.mark.filterwarnings("default") +@pytest.mark.filterwarnings("default::pytest.PytestCollectionWarning") def test_dont_collect_non_function_callable(pytester: Pytester) -> None: """Test for issue https://github.com/pytest-dev/pytest/issues/331 diff --git a/testing/test_config.py b/testing/test_config.py index 5f3ff1ea8c4..8fb1698c67d 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -290,7 +290,7 @@ def test_silence_unknown_key_warning(self, pytester: Pytester) -> None: result = pytester.runpytest() result.stdout.no_fnmatch_line("*PytestConfigWarning*") - @pytest.mark.filterwarnings("default") + @pytest.mark.filterwarnings("default::pytest.PytestConfigWarning") def test_disable_warnings_plugin_disables_config_warnings( self, pytester: Pytester ) -> None: diff --git a/testing/test_terminal.py b/testing/test_terminal.py index 5dc5a1cb69f..3e3502b5ca9 100644 --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -1618,7 +1618,7 @@ def pytest_terminal_summary(terminalreporter, exitstatus): ) -@pytest.mark.filterwarnings("default") +@pytest.mark.filterwarnings("default::UserWarning") def test_terminal_summary_warnings_are_displayed(pytester: Pytester) -> None: """Test that warnings emitted during pytest_terminal_summary are displayed. (#1305). @@ -1655,7 +1655,7 @@ def test_failure(): assert stdout.count("=== warnings summary ") == 2 -@pytest.mark.filterwarnings("default") +@pytest.mark.filterwarnings("default::UserWarning") def test_terminal_summary_warnings_header_once(pytester: Pytester) -> None: pytester.makepyfile( """ diff --git a/testing/test_threadexception.py b/testing/test_threadexception.py index 399692bc963..5b7519f27d8 100644 --- a/testing/test_threadexception.py +++ b/testing/test_threadexception.py @@ -8,7 +8,7 @@ pytest.skip("threadexception plugin needs Python>=3.8", allow_module_level=True) -@pytest.mark.filterwarnings("default") +@pytest.mark.filterwarnings("default::pytest.PytestUnhandledThreadExceptionWarning") def test_unhandled_thread_exception(pytester: Pytester) -> None: pytester.makepyfile( test_it=""" @@ -42,7 +42,7 @@ def test_2(): pass ) -@pytest.mark.filterwarnings("default") +@pytest.mark.filterwarnings("default::pytest.PytestUnhandledThreadExceptionWarning") def test_unhandled_thread_exception_in_setup(pytester: Pytester) -> None: pytester.makepyfile( test_it=""" @@ -78,7 +78,7 @@ def test_2(): pass ) -@pytest.mark.filterwarnings("default") +@pytest.mark.filterwarnings("default::pytest.PytestUnhandledThreadExceptionWarning") def test_unhandled_thread_exception_in_teardown(pytester: Pytester) -> None: pytester.makepyfile( test_it=""" diff --git a/testing/test_unraisableexception.py b/testing/test_unraisableexception.py index 32f89033409..f625833dcea 100644 --- a/testing/test_unraisableexception.py +++ b/testing/test_unraisableexception.py @@ -8,7 +8,7 @@ pytest.skip("unraisableexception plugin needs Python>=3.8", allow_module_level=True) -@pytest.mark.filterwarnings("default") +@pytest.mark.filterwarnings("default::pytest.PytestUnraisableExceptionWarning") def test_unraisable(pytester: Pytester) -> None: pytester.makepyfile( test_it=""" @@ -40,7 +40,7 @@ def test_2(): pass ) -@pytest.mark.filterwarnings("default") +@pytest.mark.filterwarnings("default::pytest.PytestUnraisableExceptionWarning") def test_unraisable_in_setup(pytester: Pytester) -> None: pytester.makepyfile( test_it=""" @@ -76,7 +76,7 @@ def test_2(): pass ) -@pytest.mark.filterwarnings("default") +@pytest.mark.filterwarnings("default::pytest.PytestUnraisableExceptionWarning") def test_unraisable_in_teardown(pytester: Pytester) -> None: pytester.makepyfile( test_it=""" diff --git a/testing/test_warnings.py b/testing/test_warnings.py index d3c69f1ab52..11678383548 100644 --- a/testing/test_warnings.py +++ b/testing/test_warnings.py @@ -38,7 +38,7 @@ def foo(): return str(test_file) -@pytest.mark.filterwarnings("default") +@pytest.mark.filterwarnings("default::UserWarning", "default::RuntimeWarning") def test_normal_flow(pytester: Pytester, pyfile_with_warnings) -> None: """Check that the warnings section is displayed.""" result = pytester.runpytest(pyfile_with_warnings) @@ -55,7 +55,7 @@ def test_normal_flow(pytester: Pytester, pyfile_with_warnings) -> None: ) -@pytest.mark.filterwarnings("always") +@pytest.mark.filterwarnings("always::UserWarning") def test_setup_teardown_warnings(pytester: Pytester) -> None: pytester.makepyfile( """ @@ -123,7 +123,7 @@ def test_ignore(pytester: Pytester, pyfile_with_warnings, method) -> None: assert WARNINGS_SUMMARY_HEADER not in result.stdout.str() -@pytest.mark.filterwarnings("always") +@pytest.mark.filterwarnings("always::UserWarning") def test_unicode(pytester: Pytester) -> None: pytester.makepyfile( """ @@ -182,7 +182,7 @@ def test_filterwarnings_mark(pytester: Pytester, default_config) -> None: pytester.makeini( """ [pytest] - filterwarnings = always + filterwarnings = always::RuntimeWarning """ ) pytester.makepyfile( @@ -202,7 +202,9 @@ def test_show_warning(): warnings.warn(RuntimeWarning()) """ ) - result = pytester.runpytest("-W always" if default_config == "cmdline" else "") + result = pytester.runpytest( + "-W always::RuntimeWarning" if default_config == "cmdline" else "" + ) result.stdout.fnmatch_lines(["*= 1 failed, 2 passed, 1 warning in *"]) @@ -217,7 +219,7 @@ def test(): warnings.warn(UserWarning(1, 'foo')) """ ) - result = pytester.runpytest("-W", "always") + result = pytester.runpytest("-W", "always::UserWarning") result.stdout.fnmatch_lines(["*= 1 passed, 1 warning in *"]) @@ -236,7 +238,7 @@ def test_func(): assert result.ret == 0 -@pytest.mark.filterwarnings("always") +@pytest.mark.filterwarnings("always::UserWarning") def test_warning_captured_hook(pytester: Pytester) -> None: pytester.makeconftest( """ @@ -297,7 +299,7 @@ def pytest_warning_recorded(self, warning_message, when, nodeid, location): assert collected_result[3] is None, str(collected) -@pytest.mark.filterwarnings("always") +@pytest.mark.filterwarnings("always::UserWarning") def test_collection_warnings(pytester: Pytester) -> None: """Check that we also capture warnings issued during test collection (#3251).""" pytester.makepyfile( @@ -321,7 +323,7 @@ def test_foo(): ) -@pytest.mark.filterwarnings("always") +@pytest.mark.filterwarnings("always::UserWarning") def test_mark_regex_escape(pytester: Pytester) -> None: """@pytest.mark.filterwarnings should not try to escape regex characters (#3936)""" pytester.makepyfile( @@ -337,7 +339,7 @@ def test_foo(): assert WARNINGS_SUMMARY_HEADER not in result.stdout.str() -@pytest.mark.filterwarnings("default") +@pytest.mark.filterwarnings("default::pytest.PytestWarning") @pytest.mark.parametrize("ignore_pytest_warnings", ["no", "ini", "cmdline"]) def test_hide_pytest_internal_warnings( pytester: Pytester, ignore_pytest_warnings @@ -387,7 +389,7 @@ def test_option_precedence_cmdline_over_ini( pytester.makeini( """ [pytest] - filterwarnings = error + filterwarnings = error::UserWarning """ ) pytester.makepyfile( @@ -581,8 +583,7 @@ def test_warnings_checker_twice() -> None: warnings.warn("Message B", UserWarning) -@pytest.mark.filterwarnings("ignore::pytest.PytestExperimentalApiWarning") -@pytest.mark.filterwarnings("always") +@pytest.mark.filterwarnings("always::UserWarning") def test_group_warnings_by_message(pytester: Pytester) -> None: pytester.copy_example("warnings/test_group_warnings_by_message.py") result = pytester.runpytest() @@ -613,8 +614,7 @@ def test_group_warnings_by_message(pytester: Pytester) -> None: ) -@pytest.mark.filterwarnings("ignore::pytest.PytestExperimentalApiWarning") -@pytest.mark.filterwarnings("always") +@pytest.mark.filterwarnings("always::UserWarning") def test_group_warnings_by_message_summary(pytester: Pytester) -> None: pytester.copy_example("warnings/test_group_warnings_by_message_summary") pytester.syspathinsert() From 61be48b485ccc2c7981596a2c9d5bf465a59d068 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 4 May 2021 14:45:10 +0200 Subject: [PATCH 274/630] Fix test_collect_symlink_dir on Windows --- testing/test_collection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/test_collection.py b/testing/test_collection.py index 98d0e174440..f75be459402 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -1225,7 +1225,7 @@ def test_collect_symlink_dir(pytester: Pytester) -> None: """A symlinked directory is collected.""" dir = pytester.mkdir("dir") dir.joinpath("test_it.py").write_text("def test_it(): pass", "utf-8") - pytester.path.joinpath("symlink_dir").symlink_to(dir) + symlink_or_skip(pytester.path.joinpath("symlink_dir"), dir) result = pytester.runpytest() result.assert_outcomes(passed=2) From 382599287f4cab914bd7502887072e7f325cdb09 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 4 May 2021 17:18:05 +0200 Subject: [PATCH 275/630] Fix test_errors_in_xfail_skip_expressions on Python 3.10 --- testing/test_skipping.py | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/testing/test_skipping.py b/testing/test_skipping.py index 878b1549fe8..53bf953a823 100644 --- a/testing/test_skipping.py +++ b/testing/test_skipping.py @@ -1143,21 +1143,34 @@ def test_func(): pypy_version_info = getattr(sys, "pypy_version_info", None) if pypy_version_info is not None and pypy_version_info < (6,): markline = markline[5:] + elif sys.version_info[:2] >= (3, 10): + markline = markline[11:] elif sys.version_info >= (3, 8) or hasattr(sys, "pypy_version_info"): markline = markline[4:] - result.stdout.fnmatch_lines( - [ + + if sys.version_info[:2] >= (3, 10): + expected = [ "*ERROR*test_nameerror*", - "*evaluating*skipif*condition*", "*asd*", - "*ERROR*test_syntax*", - "*evaluating*xfail*condition*", - " syntax error", - markline, - "SyntaxError: invalid syntax", - "*1 pass*2 errors*", + "", + "During handling of the above exception, another exception occurred:", ] - ) + else: + expected = [ + "*ERROR*test_nameerror*", + ] + + expected += [ + "*evaluating*skipif*condition*", + "*asd*", + "*ERROR*test_syntax*", + "*evaluating*xfail*condition*", + " syntax error", + markline, + "SyntaxError: invalid syntax", + "*1 pass*2 errors*", + ] + result.stdout.fnmatch_lines(expected) def test_xfail_skipif_with_globals(pytester: Pytester) -> None: From 4fc67eea4fcf68551da0615edee6cdb61285f067 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 4 May 2021 17:46:35 +0200 Subject: [PATCH 276/630] Add myself to tidelift --- TIDELIFT.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TIDELIFT.rst b/TIDELIFT.rst index b18f4793f81..2fe25841c3a 100644 --- a/TIDELIFT.rst +++ b/TIDELIFT.rst @@ -25,6 +25,7 @@ The current list of contributors receiving funding are: * `@asottile`_ * `@nicoddemus`_ +* `@The-Compiler`_ Contributors interested in receiving a part of the funds just need to submit a PR adding their name to the list. Contributors that want to stop receiving the funds should also submit a PR @@ -56,3 +57,4 @@ funds. Just drop a line to one of the `@pytest-dev/tidelift-admins`_ or use the .. _`@asottile`: https://github.com/asottile .. _`@nicoddemus`: https://github.com/nicoddemus +.. _`@The-Compiler`: https://github.com/The-Compiler From 3f71d63067ceac7817618ff1301effd2fb2e77bb Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 4 May 2021 17:42:13 +0200 Subject: [PATCH 277/630] ci: Force colors on See #7443 --- .github/workflows/main.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 98e32512f7a..756aade8c02 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -13,6 +13,8 @@ on: branches: - main - "[0-9]+.[0-9]+.x" +env: + PYTEST_ADDOPTS: "--color=yes" jobs: build: From 5ae210605912b1adc6fbedc534d1d3bc607e4978 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Tue, 4 May 2021 09:25:11 -0700 Subject: [PATCH 278/630] fix a command in RELEASING.rst --- RELEASING.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASING.rst b/RELEASING.rst index 600c12539c2..6f4c3465d6d 100644 --- a/RELEASING.rst +++ b/RELEASING.rst @@ -117,7 +117,7 @@ Both automatic and manual processes described above follow the same steps from t #. Cherry-pick the CHANGELOG / announce files to the ``main`` branch:: git fetch --all --prune - git checkout origin/main -b cherry-pick-release + git checkout upstream/main -b cherry-pick-release git cherry-pick -x -m1 upstream/MAJOR.MINOR.x #. Open a PR for ``cherry-pick-release`` and merge it once CI passes. No need to wait for approvals if there were no conflicts on the previous step. From 915ff7640bd184db649e7e9655f088dc9eb01bed Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Tue, 4 May 2021 09:23:48 -0700 Subject: [PATCH 279/630] Merge pull request #8632 from pytest-dev/release-6.2.4 Prepare release 6.2.4 (cherry picked from commit 15a45388fa272fde7b2b58254ca32c4998cb84f0) --- changelog/8539.bugfix.rst | 1 - doc/en/announce/index.rst | 1 + doc/en/announce/release-6.2.4.rst | 22 ++++++++++++++++++++++ doc/en/changelog.rst | 9 +++++++++ doc/en/getting-started.rst | 2 +- 5 files changed, 33 insertions(+), 2 deletions(-) delete mode 100644 changelog/8539.bugfix.rst create mode 100644 doc/en/announce/release-6.2.4.rst diff --git a/changelog/8539.bugfix.rst b/changelog/8539.bugfix.rst deleted file mode 100644 index a2098610e29..00000000000 --- a/changelog/8539.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Fixed assertion rewriting on Python 3.10. diff --git a/doc/en/announce/index.rst b/doc/en/announce/index.rst index f9720eab920..7751405a196 100644 --- a/doc/en/announce/index.rst +++ b/doc/en/announce/index.rst @@ -6,6 +6,7 @@ Release announcements :maxdepth: 2 + release-6.2.4 release-6.2.3 release-6.2.2 release-6.2.1 diff --git a/doc/en/announce/release-6.2.4.rst b/doc/en/announce/release-6.2.4.rst new file mode 100644 index 00000000000..fa2e3e78132 --- /dev/null +++ b/doc/en/announce/release-6.2.4.rst @@ -0,0 +1,22 @@ +pytest-6.2.4 +======================================= + +pytest 6.2.4 has just been released to PyPI. + +This is a bug-fix release, being a drop-in replacement. To upgrade:: + + pip install --upgrade pytest + +The full changelog is available at https://docs.pytest.org/en/stable/changelog.html. + +Thanks to all of the contributors to this release: + +* Anthony Sottile +* Bruno Oliveira +* Christian Maurer +* Florian Bruhin +* Ran Benita + + +Happy testing, +The pytest Development Team diff --git a/doc/en/changelog.rst b/doc/en/changelog.rst index 18a288469c5..e2b2635af9d 100644 --- a/doc/en/changelog.rst +++ b/doc/en/changelog.rst @@ -28,6 +28,15 @@ with advance notice in the **Deprecations** section of releases. .. towncrier release notes start +pytest 6.2.4 (2021-05-04) +========================= + +Bug Fixes +--------- + +- `#8539 `_: Fixed assertion rewriting on Python 3.10. + + pytest 6.2.3 (2021-04-03) ========================= diff --git a/doc/en/getting-started.rst b/doc/en/getting-started.rst index 373affa151a..2f082c35d16 100644 --- a/doc/en/getting-started.rst +++ b/doc/en/getting-started.rst @@ -22,7 +22,7 @@ Install ``pytest`` .. code-block:: bash $ pytest --version - pytest 6.2.3 + pytest 6.2.4 .. _`simpletest`: From b7416f7abd068818d57bab82771ff2faadf5cad0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarc=C3=ADsio=20Fischer?= Date: Tue, 4 May 2021 23:08:41 -0300 Subject: [PATCH 280/630] Suggest numpy testing module on pytest approx docs (#8615) Co-authored-by: Zac Hatfield-Dodds Co-authored-by: Tarcisio Fischer Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Bruno Oliveira --- changelog/8337.doc.rst | 1 + src/_pytest/python_api.py | 8 ++++++++ 2 files changed, 9 insertions(+) create mode 100644 changelog/8337.doc.rst diff --git a/changelog/8337.doc.rst b/changelog/8337.doc.rst new file mode 100644 index 00000000000..f2483a6b481 --- /dev/null +++ b/changelog/8337.doc.rst @@ -0,0 +1 @@ +Recommend `numpy.testing `__ module on :func:`pytest.approx` documentation. diff --git a/src/_pytest/python_api.py b/src/_pytest/python_api.py index 946d15b3122..e1ee057a420 100644 --- a/src/_pytest/python_api.py +++ b/src/_pytest/python_api.py @@ -654,6 +654,14 @@ def approx(expected, rel=None, abs=None, nan_ok: bool = False) -> ApproxBase: special case that you explicitly specify an absolute tolerance but not a relative tolerance, only the absolute tolerance is considered. + .. note:: + + ``approx`` can handle numpy arrays, but we recommend the + specialised test helpers in `numpy.testing`__ if you need + support for comparisons, NaNs, or ULP-based tolerances. + + __ https://numpy.org/doc/stable/reference/routines.testing.html + .. warning:: .. versionchanged:: 3.2 From 385988ca1a3f07dcc43759cc43168b71a32b3430 Mon Sep 17 00:00:00 2001 From: mobius-crypt Date: Wed, 5 May 2021 14:16:21 +0200 Subject: [PATCH 281/630] Updated (#8620) --- AUTHORS | 1 + README.rst | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index ce29748d6e8..ec04f98b97e 100644 --- a/AUTHORS +++ b/AUTHORS @@ -163,6 +163,7 @@ Josh Karpel Joshua Bronson Jurko Gospodnetić Justyna Janczyszyn +Justice Ndou Kale Kundert Kamran Ahmad Karl O. Pinc diff --git a/README.rst b/README.rst index ee11a2d600c..806465219af 100644 --- a/README.rst +++ b/README.rst @@ -93,7 +93,7 @@ Features - Python 3.6+ and PyPy3 -- Rich plugin architecture, with over 850+ `external plugins `_ and thriving community +- Rich plugin architecture, with over 850+ `external plugins `_ and thriving community Documentation From 245eefafcf417df7390457b86ad607e19686f83f Mon Sep 17 00:00:00 2001 From: Rahul Kumaresan Date: Wed, 5 May 2021 17:53:39 +0530 Subject: [PATCH 282/630] clarify documentation about tolerance setting in unittest.TestCase.assertAlmost() (#8614) --- src/_pytest/python_api.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/_pytest/python_api.py b/src/_pytest/python_api.py index e1ee057a420..a2ee63d16de 100644 --- a/src/_pytest/python_api.py +++ b/src/_pytest/python_api.py @@ -640,10 +640,9 @@ def approx(expected, rel=None, abs=None, nan_ok: bool = False) -> ApproxBase: - ``unittest.TestCase.assertAlmostEqual(a, b)``: True if ``a`` and ``b`` are within an absolute tolerance of ``1e-7``. No relative tolerance is - considered and the absolute tolerance cannot be changed, so this function - is not appropriate for very large or very small numbers. Also, it's only - available in subclasses of ``unittest.TestCase`` and it's ugly because it - doesn't follow PEP8. `More information...`__ + considered , so this function is not appropriate for very large or very + small numbers. Also, it's only available in subclasses of ``unittest.TestCase`` + and it's ugly because it doesn't follow PEP8. `More information...`__ __ https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertAlmostEqual From 4b6188b3b15cc8b257518ce8d5d73321a3c993a6 Mon Sep 17 00:00:00 2001 From: Rahul Kumaresan Date: Fri, 7 May 2021 00:45:52 +0530 Subject: [PATCH 283/630] add support for precision bit in LEVEL_NAME_FMT regex --- src/_pytest/logging.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_pytest/logging.py b/src/_pytest/logging.py index e0d71c7eb54..6420008316d 100644 --- a/src/_pytest/logging.py +++ b/src/_pytest/logging.py @@ -59,7 +59,7 @@ class ColoredLevelFormatter(logging.Formatter): logging.DEBUG: {"purple"}, logging.NOTSET: set(), } - LEVELNAME_FMT_REGEX = re.compile(r"%\(levelname\)([+-.]?\d*s)") + LEVELNAME_FMT_REGEX = re.compile(r"%\(levelname\)([+-.]?\d*[.]?\d*s)") def __init__(self, terminalwriter: TerminalWriter, *args, **kwargs) -> None: super().__init__(*args, **kwargs) From 113a860a1fd179bd8323e783a359c742ab87b7ff Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Mon, 26 Apr 2021 17:46:26 +0300 Subject: [PATCH 284/630] argparsing: support parser.addini(type="paths") which returns pathlib.Paths --- changelog/7259.feature.rst | 5 +++ src/_pytest/config/__init__.py | 6 +++ src/_pytest/config/argparsing.py | 27 +++++++++---- testing/test_config.py | 67 ++++++++++++++++---------------- 4 files changed, 65 insertions(+), 40 deletions(-) diff --git a/changelog/7259.feature.rst b/changelog/7259.feature.rst index e19aaca5291..41a213f63a6 100644 --- a/changelog/7259.feature.rst +++ b/changelog/7259.feature.rst @@ -1,2 +1,7 @@ Added :meth:`cache.mkdir() `, which is similar to the existing :meth:`cache.makedir() `, but returns a :class:`pathlib.Path` instead of a legacy ``py.path.local``. + +Added a ``paths`` type to :meth:`parser.addini() <_pytest.config.argparsing.Parser.addini>`, +as in ``parser.addini("mypaths", "my paths", type="paths")``, +which is similar to the existing ``pathlist``, +but returns a list of :class:`pathlib.Path` instead of legacy ``py.path.local``. diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 0fd91c2c943..7f18f62cb39 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -1427,6 +1427,12 @@ def _getini(self, name: str): dp = self.inipath.parent input_values = shlex.split(value) if isinstance(value, str) else value return [legacy_path(str(dp / x)) for x in input_values] + elif type == "paths": + # TODO: This assert is probably not valid in all cases. + assert self.inipath is not None + dp = self.inipath.parent + input_values = shlex.split(value) if isinstance(value, str) else value + return [dp / x for x in input_values] elif type == "args": return shlex.split(value) if isinstance(value, str) else value elif type == "linelist": diff --git a/src/_pytest/config/argparsing.py b/src/_pytest/config/argparsing.py index cf738cc2b8e..d24696eaff2 100644 --- a/src/_pytest/config/argparsing.py +++ b/src/_pytest/config/argparsing.py @@ -163,22 +163,35 @@ def addini( name: str, help: str, type: Optional[ - "Literal['string', 'pathlist', 'args', 'linelist', 'bool']" + "Literal['string', 'paths', 'pathlist', 'args', 'linelist', 'bool']" ] = None, default=None, ) -> None: """Register an ini-file option. - :name: Name of the ini-variable. - :type: Type of the variable, can be ``string``, ``pathlist``, ``args``, - ``linelist`` or ``bool``. Defaults to ``string`` if ``None`` or - not passed. - :default: Default value if no ini-file option exists but is queried. + :name: + Name of the ini-variable. + :type: + Type of the variable. Can be: + + * ``string``: a string + * ``bool``: a boolean + * ``args``: a list of strings, separated as in a shell + * ``linelist``: a list of strings, separated by line breaks + * ``paths``: a list of :class:`pathlib.Path`, separated as in a shell + * ``pathlist``: a list of ``py.path``, separated as in a shell + + .. versionadded:: 6.3 + The ``paths`` variable type. + + Defaults to ``string`` if ``None`` or not passed. + :default: + Default value if no ini-file option exists but is queried. The value of ini-variables can be retrieved via a call to :py:func:`config.getini(name) <_pytest.config.Config.getini>`. """ - assert type in (None, "string", "pathlist", "args", "linelist", "bool") + assert type in (None, "string", "paths", "pathlist", "args", "linelist", "bool") self._inidict[name] = (help, type, default) self._ininames.append(name) diff --git a/testing/test_config.py b/testing/test_config.py index 8fb1698c67d..5dabb781242 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -595,14 +595,14 @@ def test_getoption(self, pytester: Pytester) -> None: def test_getconftest_pathlist(self, pytester: Pytester, tmp_path: Path) -> None: somepath = tmp_path.joinpath("x", "y", "z") p = tmp_path.joinpath("conftest.py") - p.write_text(f"pathlist = ['.', {str(somepath)!r}]") + p.write_text(f"mylist = {['.', os.fspath(somepath)]}") config = pytester.parseconfigure(p) assert ( config._getconftest_pathlist("notexist", path=tmp_path, rootpath=tmp_path) is None ) pl = ( - config._getconftest_pathlist("pathlist", path=tmp_path, rootpath=tmp_path) + config._getconftest_pathlist("mylist", path=tmp_path, rootpath=tmp_path) or [] ) print(pl) @@ -634,41 +634,37 @@ def pytest_addoption(parser): assert val == "hello" pytest.raises(ValueError, config.getini, "other") - def make_conftest_for_pathlist(self, pytester: Pytester) -> None: + @pytest.mark.parametrize("config_type", ["ini", "pyproject"]) + @pytest.mark.parametrize("ini_type", ["paths", "pathlist"]) + def test_addini_paths( + self, pytester: Pytester, config_type: str, ini_type: str + ) -> None: pytester.makeconftest( - """ + f""" def pytest_addoption(parser): - parser.addini("paths", "my new ini value", type="pathlist") + parser.addini("paths", "my new ini value", type="{ini_type}") parser.addini("abc", "abc value") """ ) - - def test_addini_pathlist_ini_files(self, pytester: Pytester) -> None: - self.make_conftest_for_pathlist(pytester) - p = pytester.makeini( + if config_type == "ini": + inipath = pytester.makeini( + """ + [pytest] + paths=hello world/sub.py """ - [pytest] - paths=hello world/sub.py - """ - ) - self.check_config_pathlist(pytester, p) - - def test_addini_pathlist_pyproject_toml(self, pytester: Pytester) -> None: - self.make_conftest_for_pathlist(pytester) - p = pytester.makepyprojecttoml( + ) + elif config_type == "pyproject": + inipath = pytester.makepyprojecttoml( + """ + [tool.pytest.ini_options] + paths=["hello", "world/sub.py"] """ - [tool.pytest.ini_options] - paths=["hello", "world/sub.py"] - """ - ) - self.check_config_pathlist(pytester, p) - - def check_config_pathlist(self, pytester: Pytester, config_path: Path) -> None: + ) config = pytester.parseconfig() values = config.getini("paths") assert len(values) == 2 - assert values[0] == config_path.parent.joinpath("hello") - assert values[1] == config_path.parent.joinpath("world/sub.py") + assert values[0] == inipath.parent.joinpath("hello") + assert values[1] == inipath.parent.joinpath("world/sub.py") pytest.raises(ValueError, config.getini, "other") def make_conftest_for_args(self, pytester: Pytester) -> None: @@ -1519,11 +1515,12 @@ def test_pass(pytestconfig): assert result.ret == 0 result.stdout.fnmatch_lines(["custom_option:3.0"]) - def test_override_ini_pathlist(self, pytester: Pytester) -> None: + @pytest.mark.parametrize("ini_type", ["paths", "pathlist"]) + def test_override_ini_paths(self, pytester: Pytester, ini_type: str) -> None: pytester.makeconftest( - """ + f""" def pytest_addoption(parser): - parser.addini("paths", "my new ini value", type="pathlist")""" + parser.addini("paths", "my new ini value", type="{ini_type}")""" ) pytester.makeini( """ @@ -1531,12 +1528,16 @@ def pytest_addoption(parser): paths=blah.py""" ) pytester.makepyfile( - """ - def test_pathlist(pytestconfig): + rf""" + def test_overriden(pytestconfig): config_paths = pytestconfig.getini("paths") print(config_paths) for cpf in config_paths: - print('\\nuser_path:%s' % cpf.basename)""" + if "{ini_type}" == "pathlist": + print('\nuser_path:%s' % cpf.basename) + else: + print('\nuser_path:%s' % cpf.name) + """ ) result = pytester.runpytest( "--override-ini", "paths=foo/bar1.py foo/bar2.py", "-s" From 124691122a95cdeab5e03e1fff3c95d46ab2f627 Mon Sep 17 00:00:00 2001 From: Ross Vandegrift Date: Fri, 7 May 2021 00:48:46 -0700 Subject: [PATCH 285/630] Expand command line argument examples with validation examples (#8610) * Expand command line argument examples with validation examples This shows how to pass more detailed error messages back to the user when they've provided an incorrect value for a custom command line option. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- doc/en/example/simple.rst | 64 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 60 insertions(+), 4 deletions(-) diff --git a/doc/en/example/simple.rst b/doc/en/example/simple.rst index 61a5faf77e7..efe9af6e937 100644 --- a/doc/en/example/simple.rst +++ b/doc/en/example/simple.rst @@ -139,10 +139,66 @@ And now with supplying a command line option: FAILED test_sample.py::test_answer - assert 0 1 failed in 0.12s -You can see that the command line option arrived in our test. This -completes the basic pattern. However, one often rather wants to process -command line options outside of the test and rather pass in different or -more complex objects. +You can see that the command line option arrived in our test. + +We could add simple validation for the input by listing the choices: + +.. code-block:: python + + # content of conftest.py + import pytest + + + def pytest_addoption(parser): + parser.addoption( + "--cmdopt", + action="store", + default="type1", + help="my option: type1 or type2", + choices=("type1", "type2"), + ) + +Now we'll get feedback on a bad argument: + +.. code-block:: pytest + + $ pytest -q --cmdopt=type3 + ERROR: usage: pytest [options] [file_or_dir] [file_or_dir] [...] + pytest: error: argument --cmdopt: invalid choice: 'type3' (choose from 'type1', 'type2') + +If you need to provide more detailed error messages, you can use the +``type`` parameter and raise ``pytest.UsageError``: + +.. code-block:: python + + # content of conftest.py + import pytest + + + def type_checker(value): + msg = "cmdopt must specify a numeric type as typeNNN" + if not value.startswith("type"): + raise pytest.UsageError(msg) + try: + int(value[4:]) + except ValueError: + raise pytest.UsageError(msg) + + return value + + + def pytest_addoption(parser): + parser.addoption( + "--cmdopt", + action="store", + default="type1", + help="my option: type1 or type2", + type=type_checker, + ) + +This completes the basic pattern. However, one often rather wants to +process command line options outside of the test and rather pass in +different or more complex objects. Dynamically adding command line options -------------------------------------------------------------- From 1e3fcece6d2523f4671ff312c9b108333850d294 Mon Sep 17 00:00:00 2001 From: Rahul Kumaresan Date: Fri, 7 May 2021 16:16:40 +0530 Subject: [PATCH 286/630] enhance support for precision bit in LEVELNAME_FMT_REGEX regex --- changelog/8548.bugfix.rst | 1 + src/_pytest/logging.py | 2 +- testing/logging/test_formatter.py | 31 +++++++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 changelog/8548.bugfix.rst diff --git a/changelog/8548.bugfix.rst b/changelog/8548.bugfix.rst new file mode 100644 index 00000000000..9201169fc0b --- /dev/null +++ b/changelog/8548.bugfix.rst @@ -0,0 +1 @@ +Introduce fix to handle precision width in ``log-cli-format`` in turn to fix output coloring for certain formats. diff --git a/src/_pytest/logging.py b/src/_pytest/logging.py index 6420008316d..da1de013d1c 100644 --- a/src/_pytest/logging.py +++ b/src/_pytest/logging.py @@ -59,7 +59,7 @@ class ColoredLevelFormatter(logging.Formatter): logging.DEBUG: {"purple"}, logging.NOTSET: set(), } - LEVELNAME_FMT_REGEX = re.compile(r"%\(levelname\)([+-.]?\d*[.]?\d*s)") + LEVELNAME_FMT_REGEX = re.compile(r"%\(levelname\)([+-.]?\d*(?:\.\d+)?s)") def __init__(self, terminalwriter: TerminalWriter, *args, **kwargs) -> None: super().__init__(*args, **kwargs) diff --git a/testing/logging/test_formatter.py b/testing/logging/test_formatter.py index 335166caa2f..ccc7bfbeb68 100644 --- a/testing/logging/test_formatter.py +++ b/testing/logging/test_formatter.py @@ -36,6 +36,37 @@ class option: assert output == ("dummypath 10 INFO Test Message") +def test_coloredlogformatter_with_width_precision() -> None: + logfmt = "%(filename)-25s %(lineno)4d %(levelname)-8.2s %(message)s" + + record = logging.LogRecord( + name="dummy", + level=logging.INFO, + pathname="dummypath", + lineno=10, + msg="Test Message", + args=(), + exc_info=None, + ) + + class ColorConfig: + class option: + pass + + tw = TerminalWriter() + tw.hasmarkup = True + formatter = ColoredLevelFormatter(tw, logfmt) + output = formatter.format(record) + assert output == ( + "dummypath 10 \x1b[32mINFO \x1b[0m Test Message" + ) + + tw.hasmarkup = False + formatter = ColoredLevelFormatter(tw, logfmt) + output = formatter.format(record) + assert output == ("dummypath 10 INFO Test Message") + + def test_multiline_message() -> None: from _pytest.logging import PercentStyleMultiline From 80acc0ed6f4f1cfaad2de2679089d8376583f6b0 Mon Sep 17 00:00:00 2001 From: Rahul Kumaresan Date: Fri, 7 May 2021 16:25:19 +0530 Subject: [PATCH 287/630] fix test_coloredlogformatter_with_width_precision test --- testing/logging/test_formatter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/logging/test_formatter.py b/testing/logging/test_formatter.py index ccc7bfbeb68..78596f04957 100644 --- a/testing/logging/test_formatter.py +++ b/testing/logging/test_formatter.py @@ -37,7 +37,7 @@ class option: def test_coloredlogformatter_with_width_precision() -> None: - logfmt = "%(filename)-25s %(lineno)4d %(levelname)-8.2s %(message)s" + logfmt = "%(filename)-25s %(lineno)4d %(levelname)-8.8s %(message)s" record = logging.LogRecord( name="dummy", From 9e11d645b1cadec995ccee902b9763abf49b32bf Mon Sep 17 00:00:00 2001 From: Rahul Kumaresan Date: Fri, 7 May 2021 17:56:35 +0530 Subject: [PATCH 288/630] cleanup tests by removal of unused code elements --- testing/logging/test_formatter.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/testing/logging/test_formatter.py b/testing/logging/test_formatter.py index 78596f04957..37971293726 100644 --- a/testing/logging/test_formatter.py +++ b/testing/logging/test_formatter.py @@ -18,10 +18,6 @@ def test_coloredlogformatter() -> None: exc_info=None, ) - class ColorConfig: - class option: - pass - tw = TerminalWriter() tw.hasmarkup = True formatter = ColoredLevelFormatter(tw, logfmt) @@ -49,10 +45,6 @@ def test_coloredlogformatter_with_width_precision() -> None: exc_info=None, ) - class ColorConfig: - class option: - pass - tw = TerminalWriter() tw.hasmarkup = True formatter = ColoredLevelFormatter(tw, logfmt) From 647ac4a79d9736e4f4fb7ae261ffdba3e2f27f6e Mon Sep 17 00:00:00 2001 From: Saiprasad Kale <35297720+Saiprasad16@users.noreply.github.com> Date: Sat, 8 May 2021 23:38:48 +0530 Subject: [PATCH 289/630] Update LICENSE --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index d14fb7ff4b3..643dd0c4c2f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2004-2020 Holger Krekel and others +Copyright (c) 2004-2021 Holger Krekel and others Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in From 6b53714d570a483a301bf2072a316e1edc858065 Mon Sep 17 00:00:00 2001 From: Saiprasad Kale <35297720+Saiprasad16@users.noreply.github.com> Date: Sat, 8 May 2021 23:43:33 +0530 Subject: [PATCH 290/630] Update conf.py --- doc/en/conf.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/en/conf.py b/doc/en/conf.py index 6de2f1fbd7d..ee71ffcae41 100644 --- a/doc/en/conf.py +++ b/doc/en/conf.py @@ -71,7 +71,7 @@ # General information about the project. project = "pytest" -copyright = "2015–2020, holger krekel and pytest-dev team" +copyright = "2015–2021, holger krekel and pytest-dev team" # The language for content autogenerated by Sphinx. Refer to documentation @@ -293,7 +293,7 @@ epub_title = "pytest" epub_author = "holger krekel at merlinux eu" epub_publisher = "holger krekel at merlinux eu" -epub_copyright = "2013-2020, holger krekel et alii" +epub_copyright = "2013-2021, holger krekel et alii" # The language of the text. It defaults to the language option # or en if the language is not set. From 337a729d95719a1a707261b8be1b85a41c309e3a Mon Sep 17 00:00:00 2001 From: Saiprasad Kale <35297720+Saiprasad16@users.noreply.github.com> Date: Sat, 8 May 2021 23:45:20 +0530 Subject: [PATCH 291/630] Update license.rst --- doc/en/license.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/license.rst b/doc/en/license.rst index 13765be1595..d42b0f75280 100644 --- a/doc/en/license.rst +++ b/doc/en/license.rst @@ -9,7 +9,7 @@ Distributed under the terms of the `MIT`_ license, pytest is free and open sourc The MIT License (MIT) - Copyright (c) 2004-2020 Holger Krekel and others + Copyright (c) 2004-2021 Holger Krekel and others Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in From 7caf47acc65ff5cf59641058ebf095caf5ba896e Mon Sep 17 00:00:00 2001 From: Saiprasad Kale <35297720+Saiprasad16@users.noreply.github.com> Date: Sat, 8 May 2021 23:50:17 +0530 Subject: [PATCH 292/630] Update AUTHORS --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index ec04f98b97e..fa9ed10c20e 100644 --- a/AUTHORS +++ b/AUTHORS @@ -116,6 +116,7 @@ Felix Nieuwenhuizen Feng Ma Florian Bruhin Florian Dahlitz +Saiprasad Kale Floris Bruynooghe Gabriel Reis Garvit Shubham From e0a3dc7bf88f7d7f515765abb5fb215d4d1a1820 Mon Sep 17 00:00:00 2001 From: pytest bot Date: Sun, 9 May 2021 00:05:23 +0000 Subject: [PATCH 293/630] [automated] Update plugin list --- doc/en/reference/plugin_list.rst | 44 +++++++++++++++++++------------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/doc/en/reference/plugin_list.rst b/doc/en/reference/plugin_list.rst index 3338e999bb4..fdd49c5dfc6 100644 --- a/doc/en/reference/plugin_list.rst +++ b/doc/en/reference/plugin_list.rst @@ -6,7 +6,7 @@ Plugin List PyPI projects that match "pytest-\*" are considered plugins and are listed automatically. Packages classified as inactive are excluded. -This list contains 857 plugins. +This list contains 865 plugins. ============================================================================================================== ======================================================================================================================================================================== ============== ===================== ================================================ name summary last release status requires @@ -36,7 +36,7 @@ name `pytest-antilru `_ Bust functools.lru_cache when running pytest to avoid test pollution Apr 11, 2019 5 - Production/Stable pytest `pytest-anything `_ Pytest fixtures to assert anything and something Feb 18, 2021 N/A N/A `pytest-aoc `_ Downloads puzzle inputs for Advent of Code and synthesizes PyTest fixtures Dec 01, 2020 N/A pytest ; extra == 'dev' -`pytest-api `_ PyTest-API Python Web Framework built for testing purposes. Mar 20, 2021 N/A N/A +`pytest-api `_ PyTest-API Python Web Framework built for testing purposes. May 04, 2021 N/A N/A `pytest-apistellar `_ apistellar plugin for pytest. Jun 18, 2019 N/A N/A `pytest-appengine `_ AppEngine integration that works well with pytest-django Feb 27, 2017 N/A N/A `pytest-appium `_ Pytest plugin for appium Dec 05, 2019 N/A N/A @@ -104,11 +104,12 @@ name `pytest-canonical-data `_ A plugin which allows to compare results with canonical results, based on previous runs May 08, 2020 2 - Pre-Alpha pytest (>=3.5.0) `pytest-caprng `_ A plugin that replays pRNG state on failure. May 02, 2018 4 - Beta N/A `pytest-capture-deprecatedwarnings `_ pytest plugin to capture all deprecatedwarnings and put them in one file Apr 30, 2019 N/A N/A -`pytest-cases `_ Separate test code from test cases in pytest. Apr 12, 2021 5 - Production/Stable N/A +`pytest-cases `_ Separate test code from test cases in pytest. May 07, 2021 5 - Production/Stable N/A `pytest-cassandra `_ Cassandra CCM Test Fixtures for pytest Nov 04, 2017 1 - Planning N/A `pytest-catchlog `_ py.test plugin to catch log messages. This is a fork of pytest-capturelog. Jan 24, 2016 4 - Beta pytest (>=2.6) `pytest-catch-server `_ Pytest plugin with server for catching HTTP requests. Dec 12, 2019 5 - Production/Stable N/A -`pytest-celery `_ pytest-celery a shim pytest plugin to enable celery.contrib.pytest Aug 05, 2020 N/A N/A +`pytest-cdp-common `_ May 01, 2021 N/A N/A +`pytest-celery `_ pytest-celery a shim pytest plugin to enable celery.contrib.pytest May 06, 2021 N/A N/A `pytest-chalice `_ A set of py.test fixtures for AWS Chalice Jul 01, 2020 4 - Beta N/A `pytest-change-report `_ turn . into √,turn F into x Sep 14, 2020 N/A pytest `pytest-chdir `_ A pytest fixture for changing current working directory Jan 28, 2020 N/A pytest (>=5.0.0,<6.0.0) @@ -127,7 +128,9 @@ name `pytest-cloud `_ Distributed tests planner plugin for pytest testing framework. Oct 05, 2020 6 - Mature N/A `pytest-cloudflare-worker `_ pytest plugin for testing cloudflare workers Mar 30, 2021 4 - Beta pytest (>=6.0.0) `pytest-cobra `_ PyTest plugin for testing Smart Contracts for Ethereum blockchain. Jun 29, 2019 3 - Alpha pytest (<4.0.0,>=3.7.1) +`pytest-codeblocks `_ Test code blocks in your READMEs May 08, 2021 4 - Beta pytest (>=6) `pytest-codecheckers `_ pytest plugin to add source code sanity checks (pep8 and friends) Feb 13, 2010 N/A N/A +`pytest-codecov `_ Pytest plugin for uploading pytest-cov results to codecov.io May 05, 2021 4 - Beta pytest (>=4.6.0) `pytest-codegen `_ Automatically create pytest test signatures Aug 23, 2020 2 - Pre-Alpha N/A `pytest-codestyle `_ pytest plugin to run pycodestyle Mar 23, 2020 3 - Alpha N/A `pytest-collect-formatter `_ Formatter for pytest collect output Mar 29, 2021 5 - Production/Stable N/A @@ -317,6 +320,7 @@ name `pytest-flask `_ A set of py.test fixtures to test Flask applications. Feb 27, 2021 5 - Production/Stable pytest (>=5.2) `pytest-flask-sqlalchemy `_ A pytest plugin for preserving test isolation in Flask-SQlAlchemy using database transactions. Apr 04, 2019 4 - Beta pytest (>=3.2.1) `pytest-flask-sqlalchemy-transactions `_ Run tests in transactions using pytest, Flask, and SQLalchemy. Aug 02, 2018 4 - Beta pytest (>=3.2.1) +`pytest-flyte `_ Pytest fixtures for simplifying Flyte integration testing May 03, 2021 N/A pytest `pytest-focus `_ A pytest plugin that alerts user of failed test cases with screen notifications May 04, 2019 4 - Beta pytest `pytest-forcefail `_ py.test plugin to make the test failing regardless of pytest.mark.xfail May 15, 2018 4 - Beta N/A `pytest-forward-compatability `_ A name to avoid typosquating pytest-foward-compatibility Sep 06, 2020 N/A N/A @@ -386,7 +390,7 @@ name `pytest-informative-node `_ display more node ininformation. Apr 25, 2019 4 - Beta N/A `pytest-infrastructure `_ pytest stack validation prior to testing executing Apr 12, 2020 4 - Beta N/A `pytest-inmanta `_ A py.test plugin providing fixtures to simplify inmanta modules testing. Apr 09, 2021 5 - Production/Stable N/A -`pytest-inmanta-extensions `_ Inmanta tests package Mar 17, 2021 5 - Production/Stable N/A +`pytest-inmanta-extensions `_ Inmanta tests package May 05, 2021 5 - Production/Stable N/A `pytest-Inomaly `_ A simple image diff plugin for pytest Feb 13, 2018 4 - Beta N/A `pytest-insta `_ A practical snapshot testing plugin for pytest Apr 07, 2021 N/A pytest (>=6.0.2,<7.0.0) `pytest-instafail `_ pytest plugin to show failures instantly Jun 14, 2020 4 - Beta pytest (>=2.9) @@ -428,6 +432,7 @@ name `pytest-libnotify `_ Pytest plugin that shows notifications about the test run Apr 02, 2021 3 - Alpha pytest `pytest-ligo `_ Jan 16, 2020 4 - Beta N/A `pytest-lineno `_ A pytest plugin to show the line numbers of test functions Dec 04, 2020 N/A pytest +`pytest-line-profiler `_ Profile code executed by pytest May 03, 2021 4 - Beta pytest (>=3.5.0) `pytest-lisa `_ Pytest plugin for organizing tests. Jan 21, 2021 3 - Alpha pytest (>=6.1.2,<7.0.0) `pytest-listener `_ A simple network listener May 28, 2019 5 - Production/Stable pytest `pytest-litf `_ A pytest plugin that stream output in LITF format Jan 18, 2021 4 - Beta pytest (>=3.1.1) @@ -435,7 +440,7 @@ name `pytest-localftpserver `_ A PyTest plugin which provides an FTP fixture for your tests Jan 27, 2021 5 - Production/Stable pytest `pytest-localserver `_ py.test plugin to test server connections locally. Nov 14, 2018 4 - Beta N/A `pytest-localstack `_ Pytest plugin for AWS integration tests Aug 22, 2019 4 - Beta pytest (>=3.3.0) -`pytest-lockable `_ lockable resource plugin for pytest Apr 14, 2021 3 - Alpha pytest +`pytest-lockable `_ lockable resource plugin for pytest May 08, 2021 5 - Production/Stable pytest `pytest-locker `_ Used to lock object during testing. Essentially changing assertions from being hard coded to asserting that nothing changed Feb 25, 2021 N/A pytest (>=5.4) `pytest-logbook `_ py.test plugin to capture logbook log messages Nov 23, 2015 5 - Production/Stable pytest (>=2.8) `pytest-logfest `_ Pytest plugin providing three logger fixtures with basic or full writing to log files Jul 21, 2019 4 - Beta pytest (>=3.5.0) @@ -455,7 +460,7 @@ name `pytest-matrix `_ Provide tools for generating tests from combinations of fixtures. Jun 24, 2020 5 - Production/Stable pytest (>=5.4.3,<6.0.0) `pytest-mccabe `_ pytest plugin to run the mccabe code complexity checker. Jul 22, 2020 3 - Alpha pytest (>=5.4.0) `pytest-md `_ Plugin for generating Markdown reports for pytest results Jul 11, 2019 3 - Alpha pytest (>=4.2.1) -`pytest-md-report `_ A pytest plugin to make a test results report with Markdown table format. Aug 14, 2020 4 - Beta pytest (!=6.0.0,<7,>=3.3.2) +`pytest-md-report `_ A pytest plugin to make a test results report with Markdown table format. May 04, 2021 4 - Beta pytest (!=6.0.0,<7,>=3.3.2) `pytest-memprof `_ Estimates memory consumption of test functions Mar 29, 2019 4 - Beta N/A `pytest-menu `_ A pytest plugin for console based interactive test selection just after the collection phase Oct 04, 2017 3 - Alpha pytest (>=2.4.2) `pytest-mercurial `_ pytest plugin to write integration tests for projects using Mercurial Python internals Nov 21, 2020 1 - Planning N/A @@ -467,7 +472,7 @@ name `pytest-missing-fixtures `_ Pytest plugin that creates missing fixtures Oct 14, 2020 4 - Beta pytest (>=3.5.0) `pytest-ml `_ Test your machine learning! May 04, 2019 4 - Beta N/A `pytest-mocha `_ pytest plugin to display test execution output like a mochajs Apr 02, 2020 4 - Beta pytest (>=5.4.0) -`pytest-mock `_ Thin-wrapper around the mock package for easier use with pytest Apr 24, 2021 5 - Production/Stable pytest (>=5.0) +`pytest-mock `_ Thin-wrapper around the mock package for easier use with pytest May 06, 2021 5 - Production/Stable pytest (>=5.0) `pytest-mock-api `_ A mock API server with configurable routes and responses available as a fixture. Feb 13, 2019 1 - Planning pytest (>=4.0.0) `pytest-mock-helper `_ Help you mock HTTP call and generate mock code Jan 24, 2018 N/A pytest `pytest-mockito `_ Base fixtures for mockito Jul 11, 2018 4 - Beta N/A @@ -500,6 +505,7 @@ name `pytest-needle `_ pytest plugin for visual testing websites using selenium Dec 10, 2018 4 - Beta pytest (<5.0.0,>=3.0.0) `pytest-neo `_ pytest-neo is a plugin for pytest that shows tests like screen of Matrix. Apr 23, 2019 3 - Alpha pytest (>=3.7.2) `pytest-network `_ A simple plugin to disable network on socket level. May 07, 2020 N/A N/A +`pytest-never-sleep `_ pytest plugin helps to avoid adding tests without mock `time.sleep` May 05, 2021 3 - Alpha pytest (>=3.5.1) `pytest-nginx `_ nginx fixture for pytest Aug 12, 2017 5 - Production/Stable N/A `pytest-nginx-iplweb `_ nginx fixture for pytest - iplweb temporary fork Mar 01, 2019 5 - Production/Stable N/A `pytest-ngrok `_ Jan 22, 2020 3 - Alpha N/A @@ -573,10 +579,10 @@ name `pytest-ponyorm `_ PonyORM in Pytest Oct 31, 2018 N/A pytest (>=3.1.1) `pytest-poo `_ Visualize your crappy tests Mar 25, 2021 5 - Production/Stable pytest (>=2.3.4) `pytest-poo-fail `_ Visualize your failed tests with poo Feb 12, 2015 5 - Production/Stable N/A -`pytest-pop `_ A pytest plugin to help with testing pop projects Apr 27, 2021 5 - Production/Stable pytest (>=5.4.0) +`pytest-pop `_ A pytest plugin to help with testing pop projects May 05, 2021 5 - Production/Stable pytest `pytest-portion `_ Select a portion of the collected tests Jan 28, 2021 4 - Beta pytest (>=3.5.0) `pytest-postgres `_ Run PostgreSQL in Docker container in Pytest. Mar 22, 2020 N/A pytest -`pytest-postgresql `_ Postgresql fixtures and fixture factories for Pytest. Feb 23, 2021 5 - Production/Stable pytest (>=3.0.0) +`pytest-postgresql `_ Postgresql fixtures and fixture factories for Pytest. May 05, 2021 5 - Production/Stable pytest (>=3.0.0) `pytest-power `_ pytest plugin with powerful fixtures Dec 31, 2020 N/A pytest (>=5.4) `pytest-pride `_ Minitest-style test colors Apr 02, 2016 3 - Alpha N/A `pytest-print `_ pytest-print adds the printer fixture you can use to print messages to the user (directly to the pytest runner, not stdout) Oct 23, 2020 5 - Production/Stable pytest (>=3.0.0) @@ -618,7 +624,7 @@ name `pytest-random-num `_ Randomise the order in which pytest tests are run with some control over the randomness Oct 19, 2020 5 - Production/Stable N/A `pytest-random-order `_ Randomise the order in which pytest tests are run with some control over the randomness Nov 30, 2018 5 - Production/Stable pytest (>=3.0.0) `pytest-readme `_ Test your README.md file Dec 28, 2014 5 - Production/Stable N/A -`pytest-reana `_ Pytest fixtures for REANA. Nov 24, 2020 3 - Alpha N/A +`pytest-reana `_ Pytest fixtures for REANA. May 03, 2021 3 - Alpha N/A `pytest-recording `_ A pytest plugin that allows you recording of network interactions via VCR.py Nov 25, 2020 4 - Beta pytest (>=3.5.0) `pytest-recordings `_ Provides pytest plugins for reporting request/response traffic, screenshots, and more to ReportPortal Aug 13, 2020 N/A N/A `pytest-redis `_ Redis fixtures and fixture factories for Pytest. Oct 15, 2019 5 - Production/Stable pytest (>=3.0.0) @@ -627,6 +633,7 @@ name `pytest-reference-formatter `_ Conveniently run pytest with a dot-formatted test reference. Oct 01, 2019 4 - Beta N/A `pytest-regressions `_ Easy to use fixtures to write regression tests. Jan 27, 2021 5 - Production/Stable pytest (>=3.5.0) `pytest-regtest `_ pytest plugin for regression tests Sep 16, 2020 N/A N/A +`pytest-relative-order `_ a pytest plugin that sorts tests using "before" and "after" markers May 04, 2021 4 - Beta N/A `pytest-relaxed `_ Relaxed test discovery/organization for pytest Jun 14, 2019 5 - Production/Stable pytest (<5,>=3) `pytest-remfiles `_ Pytest plugin to create a temporary directory with remote files Jul 01, 2019 5 - Production/Stable N/A `pytest-remotedata `_ Pytest plugin for controlling remote data access. Jul 20, 2019 3 - Alpha pytest (>=3.1) @@ -670,25 +677,25 @@ name `pytest-runner `_ Invoke py.test as distutils command with dependency resolution Feb 12, 2021 5 - Production/Stable pytest (!=3.7.3,>=3.5) ; extra == 'testing' `pytest-salt `_ Pytest Salt Plugin Jan 27, 2020 4 - Beta N/A `pytest-salt-containers `_ A Pytest plugin that builds and creates docker containers Nov 09, 2016 4 - Beta N/A -`pytest-salt-factories `_ Pytest Salt Plugin Apr 14, 2021 4 - Beta pytest (>=6.1.1) +`pytest-salt-factories `_ Pytest Salt Plugin May 06, 2021 4 - Beta pytest (>=6.0.0) `pytest-salt-from-filenames `_ Simple PyTest Plugin For Salt's Test Suite Specifically Jan 29, 2019 4 - Beta pytest (>=4.1) `pytest-salt-runtests-bridge `_ Simple PyTest Plugin For Salt's Test Suite Specifically Dec 05, 2019 4 - Beta pytest (>=4.1) `pytest-sanic `_ a pytest plugin for Sanic Feb 27, 2021 N/A pytest (>=5.2) `pytest-sanity `_ Dec 07, 2020 N/A N/A `pytest-sa-pg `_ May 14, 2019 N/A N/A -`pytest-sbase `_ A complete web automation framework for end-to-end testing. May 01, 2021 5 - Production/Stable N/A +`pytest-sbase `_ A complete web automation framework for end-to-end testing. May 08, 2021 5 - Production/Stable N/A `pytest-scenario `_ pytest plugin for test scenarios Feb 06, 2017 3 - Alpha N/A `pytest-schema `_ 👍 Validate return values against a schema-like object in testing Aug 31, 2020 5 - Production/Stable pytest (>=3.5.0) `pytest-securestore `_ An encrypted password store for use within pytest cases Jun 19, 2019 4 - Beta N/A `pytest-select `_ A pytest plugin which allows to (de-)select tests from a file. Jan 18, 2019 3 - Alpha pytest (>=3.0) `pytest-selenium `_ pytest plugin for Selenium Sep 19, 2020 5 - Production/Stable pytest (>=5.0.0) -`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. May 01, 2021 5 - Production/Stable N/A +`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. May 08, 2021 5 - Production/Stable N/A `pytest-selenium-enhancer `_ pytest plugin for Selenium Nov 26, 2020 5 - Production/Stable N/A `pytest-selenium-pdiff `_ A pytest package implementing perceptualdiff for Selenium tests. Apr 06, 2017 2 - Pre-Alpha N/A `pytest-send-email `_ Send pytest execution result email Dec 04, 2019 N/A N/A `pytest-sentry `_ A pytest plugin to send testrun information to Sentry.io Apr 21, 2021 N/A pytest `pytest-server-fixtures `_ Extensible server fixures for py.test May 28, 2019 5 - Production/Stable pytest -`pytest-serverless `_ Automatically mocks resources from serverless.yml in pytest using moto. Apr 27, 2021 4 - Beta N/A +`pytest-serverless `_ Automatically mocks resources from serverless.yml in pytest using moto. May 02, 2021 4 - Beta N/A `pytest-services `_ Services plugin for pytest testing framework Oct 30, 2020 6 - Mature N/A `pytest-session2file `_ pytest-session2file (aka: pytest-session_to_file for v0.1.0 - v0.1.2) is a py.test plugin for capturing and saving to file the stdout of py.test. Jan 26, 2021 3 - Alpha pytest `pytest-session-fixture-globalize `_ py.test plugin to make session fixtures behave as if written in conftest, even if it is written in some modules May 15, 2018 4 - Beta N/A @@ -720,7 +727,7 @@ name `pytest-sourceorder `_ Test-ordering plugin for pytest Apr 11, 2017 4 - Beta pytest `pytest-spark `_ pytest plugin to run the tests with support of pyspark. Feb 23, 2020 4 - Beta pytest `pytest-spawner `_ py.test plugin to spawn process and communicate with them. Jul 31, 2015 4 - Beta N/A -`pytest-spec `_ Library pytest-spec is a pytest plugin to display test execution output like a SPECIFICATION. Jan 14, 2021 N/A N/A +`pytest-spec `_ Library pytest-spec is a pytest plugin to display test execution output like a SPECIFICATION. May 04, 2021 N/A N/A `pytest-sphinx `_ Doctest plugin for pytest with support for Sphinx-specific doctest-directives Aug 05, 2020 4 - Beta N/A `pytest-spiratest `_ Exports unit tests as test runs in SpiraTest/Team/Plan Apr 28, 2021 N/A N/A `pytest-splinter `_ Splinter plugin for pytest testing framework Dec 25, 2020 6 - Mature N/A @@ -734,11 +741,11 @@ name `pytest-sqitch `_ sqitch for pytest Apr 06, 2020 4 - Beta N/A `pytest-sqlalchemy `_ pytest plugin with sqlalchemy related fixtures Mar 13, 2018 3 - Alpha N/A `pytest-sql-bigquery `_ Yet another SQL-testing framework for BigQuery provided by pytest plugin Dec 19, 2019 N/A pytest -`pytest-srcpaths `_ Add paths to sys.path Feb 18, 2021 N/A N/A +`pytest-srcpaths `_ Add paths to sys.path May 06, 2021 N/A N/A `pytest-ssh `_ pytest plugin for ssh command run May 27, 2019 N/A pytest `pytest-start-from `_ Start pytest run from a given point Apr 11, 2016 N/A N/A `pytest-statsd `_ pytest plugin for reporting to graphite Nov 30, 2018 5 - Production/Stable pytest (>=3.0.0) -`pytest-stepfunctions `_ A small description Jul 07, 2020 4 - Beta pytest +`pytest-stepfunctions `_ A small description May 08, 2021 4 - Beta pytest `pytest-steps `_ Create step-wise / incremental tests in pytest. Apr 25, 2020 5 - Production/Stable N/A `pytest-stepwise `_ Run a test suite one failing test at a time. Dec 01, 2015 4 - Beta N/A `pytest-stoq `_ A plugin to pytest stoq Feb 09, 2021 4 - Beta N/A @@ -848,6 +855,7 @@ name `pytest-xdist `_ pytest xdist plugin for distributed testing and loop-on-failing modes Feb 09, 2021 5 - Production/Stable pytest (>=6.0.0) `pytest-xdist-debug-for-graingert `_ pytest xdist plugin for distributed testing and loop-on-failing modes Jul 24, 2019 5 - Production/Stable pytest (>=4.4.0) `pytest-xdist-forked `_ forked from pytest-xdist Feb 10, 2020 5 - Production/Stable pytest (>=4.4.0) +`pytest-xdist-tracker `_ pytest plugin helps to reproduce failures for particular xdist node May 05, 2021 3 - Alpha pytest (>=3.5.1) `pytest-xfaillist `_ Maintain a xfaillist in an additional file to avoid merge-conflicts. Mar 07, 2021 N/A pytest (>=6.2.2,<7.0.0) `pytest-xfiles `_ Pytest fixtures providing data read from function, module or package related (x)files. Feb 27, 2018 N/A N/A `pytest-xlog `_ Extended logging for test and decorators May 31, 2020 4 - Beta N/A From c519dd64ed3d58910ef9a6dffb8d5efb4e2dcee5 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 10 May 2021 20:25:55 +0000 Subject: [PATCH 294/630] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black: 21.4b2 → 21.5b1](https://github.com/psf/black/compare/21.4b2...21.5b1) - [github.com/PyCQA/flake8: 3.9.1 → 3.9.2](https://github.com/PyCQA/flake8/compare/3.9.1...3.9.2) - [github.com/asottile/pyupgrade: v2.14.0 → v2.15.0](https://github.com/asottile/pyupgrade/compare/v2.14.0...v2.15.0) --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index eead96a02d8..46d6e3b4c52 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/psf/black - rev: 21.4b2 + rev: 21.5b1 hooks: - id: black args: [--safe, --quiet] @@ -21,7 +21,7 @@ repos: exclude: _pytest/(debugging|hookspec).py language_version: python3 - repo: https://github.com/PyCQA/flake8 - rev: 3.9.1 + rev: 3.9.2 hooks: - id: flake8 language_version: python3 @@ -34,7 +34,7 @@ repos: - id: reorder-python-imports args: ['--application-directories=.:src', --py36-plus] - repo: https://github.com/asottile/pyupgrade - rev: v2.14.0 + rev: v2.15.0 hooks: - id: pyupgrade args: [--py36-plus] From 48f14c48ea9ef88dd642a2600f670eb103558956 Mon Sep 17 00:00:00 2001 From: Zack Kneupper Date: Tue, 11 May 2021 04:36:36 -0400 Subject: [PATCH 295/630] Remove unnecessary else clause in repr_failure() (#8661) * Remove unnecessary else clause in repr_failure() * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Co-authored-by: Zachary Kneupper Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- src/_pytest/doctest.py | 96 ++++++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 50 deletions(-) diff --git a/src/_pytest/doctest.py b/src/_pytest/doctest.py index 3547c422eed..cba11a44fc2 100644 --- a/src/_pytest/doctest.py +++ b/src/_pytest/doctest.py @@ -322,58 +322,54 @@ def repr_failure( # type: ignore[override] elif isinstance(excinfo.value, MultipleDoctestFailures): failures = excinfo.value.failures - if failures is not None: - reprlocation_lines = [] - for failure in failures: - example = failure.example - test = failure.test - filename = test.filename - if test.lineno is None: - lineno = None - else: - lineno = test.lineno + example.lineno + 1 - message = type(failure).__name__ - # TODO: ReprFileLocation doesn't expect a None lineno. - reprlocation = ReprFileLocation(filename, lineno, message) # type: ignore[arg-type] - checker = _get_checker() - report_choice = _get_report_choice( - self.config.getoption("doctestreport") - ) - if lineno is not None: - assert failure.test.docstring is not None - lines = failure.test.docstring.splitlines(False) - # add line numbers to the left of the error message - assert test.lineno is not None - lines = [ - "%03d %s" % (i + test.lineno + 1, x) - for (i, x) in enumerate(lines) - ] - # trim docstring error lines to 10 - lines = lines[max(example.lineno - 9, 0) : example.lineno + 1] - else: - lines = [ - "EXAMPLE LOCATION UNKNOWN, not showing all tests of that example" - ] - indent = ">>>" - for line in example.source.splitlines(): - lines.append(f"??? {indent} {line}") - indent = "..." - if isinstance(failure, doctest.DocTestFailure): - lines += checker.output_difference( - example, failure.got, report_choice - ).split("\n") - else: - inner_excinfo = ExceptionInfo.from_exc_info(failure.exc_info) - lines += ["UNEXPECTED EXCEPTION: %s" % repr(inner_excinfo.value)] - lines += [ - x.strip("\n") - for x in traceback.format_exception(*failure.exc_info) - ] - reprlocation_lines.append((reprlocation, lines)) - return ReprFailDoctest(reprlocation_lines) - else: + if failures is None: return super().repr_failure(excinfo) + reprlocation_lines = [] + for failure in failures: + example = failure.example + test = failure.test + filename = test.filename + if test.lineno is None: + lineno = None + else: + lineno = test.lineno + example.lineno + 1 + message = type(failure).__name__ + # TODO: ReprFileLocation doesn't expect a None lineno. + reprlocation = ReprFileLocation(filename, lineno, message) # type: ignore[arg-type] + checker = _get_checker() + report_choice = _get_report_choice(self.config.getoption("doctestreport")) + if lineno is not None: + assert failure.test.docstring is not None + lines = failure.test.docstring.splitlines(False) + # add line numbers to the left of the error message + assert test.lineno is not None + lines = [ + "%03d %s" % (i + test.lineno + 1, x) for (i, x) in enumerate(lines) + ] + # trim docstring error lines to 10 + lines = lines[max(example.lineno - 9, 0) : example.lineno + 1] + else: + lines = [ + "EXAMPLE LOCATION UNKNOWN, not showing all tests of that example" + ] + indent = ">>>" + for line in example.source.splitlines(): + lines.append(f"??? {indent} {line}") + indent = "..." + if isinstance(failure, doctest.DocTestFailure): + lines += checker.output_difference( + example, failure.got, report_choice + ).split("\n") + else: + inner_excinfo = ExceptionInfo.from_exc_info(failure.exc_info) + lines += ["UNEXPECTED EXCEPTION: %s" % repr(inner_excinfo.value)] + lines += [ + x.strip("\n") for x in traceback.format_exception(*failure.exc_info) + ] + reprlocation_lines.append((reprlocation, lines)) + return ReprFailDoctest(reprlocation_lines) + def reportinfo(self): assert self.dtest is not None return legacy_path(self.path), self.dtest.lineno, "[doctest] %s" % self.name From 0a07b71046666a6f55a37bf63d039c8f9a87183c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 May 2021 10:44:32 +0200 Subject: [PATCH 296/630] Bump django from 3.2 to 3.2.2 in /testing/plugins_integration (#8652) Bumps [django](https://github.com/django/django) from 3.2 to 3.2.2. - [Release notes](https://github.com/django/django/releases) - [Commits](https://github.com/django/django/compare/3.2...3.2.2) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- testing/plugins_integration/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/plugins_integration/requirements.txt b/testing/plugins_integration/requirements.txt index 97a35ebbb17..97efaced769 100644 --- a/testing/plugins_integration/requirements.txt +++ b/testing/plugins_integration/requirements.txt @@ -1,5 +1,5 @@ anyio[curio,trio]==3.0.1 -django==3.2 +django==3.2.2 pytest-asyncio==0.15.1 pytest-bdd==4.0.2 pytest-cov==2.11.1 From 822624c252cfc7f0a2619b272901ec5c5fdbb462 Mon Sep 17 00:00:00 2001 From: James Gerity Date: Tue, 11 May 2021 05:04:50 -0400 Subject: [PATCH 297/630] Update help text for `--pdbcls` to match behavior (closes #8655) (#8656) * Clarify help text for --pdbcls * Add changelog entry for --pdbcls --- changelog/8655.doc.rst | 1 + src/_pytest/debugging.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 changelog/8655.doc.rst diff --git a/changelog/8655.doc.rst b/changelog/8655.doc.rst new file mode 100644 index 00000000000..65051a74319 --- /dev/null +++ b/changelog/8655.doc.rst @@ -0,0 +1 @@ +Help text for ``--pdbcls`` more accurately reflects the option's behavior. diff --git a/src/_pytest/debugging.py b/src/_pytest/debugging.py index d3a5c6173f3..cb9dd8660b4 100644 --- a/src/_pytest/debugging.py +++ b/src/_pytest/debugging.py @@ -53,7 +53,7 @@ def pytest_addoption(parser: Parser) -> None: dest="usepdb_cls", metavar="modulename:classname", type=_validate_usepdb_cls, - help="start a custom interactive Python debugger on errors. " + help="specify a custom interactive Python debugger for use with --pdb." "For example: --pdbcls=IPython.terminal.debugger:TerminalPdb", ) group._addoption( From b4cd14aae35736c3617ef8bd91f02d2d1bf86060 Mon Sep 17 00:00:00 2001 From: Zack Kneupper Date: Tue, 11 May 2021 05:10:01 -0400 Subject: [PATCH 298/630] Open log file in context manager (#8659) Co-authored-by: Zachary Kneupper --- src/_pytest/junitxml.py | 66 ++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/src/_pytest/junitxml.py b/src/_pytest/junitxml.py index 690fd976ca9..fafb5fa1aa6 100644 --- a/src/_pytest/junitxml.py +++ b/src/_pytest/junitxml.py @@ -648,39 +648,39 @@ def pytest_sessionfinish(self) -> None: dirname = os.path.dirname(os.path.abspath(self.logfile)) if not os.path.isdir(dirname): os.makedirs(dirname) - logfile = open(self.logfile, "w", encoding="utf-8") - suite_stop_time = timing.time() - suite_time_delta = suite_stop_time - self.suite_start_time - - numtests = ( - self.stats["passed"] - + self.stats["failure"] - + self.stats["skipped"] - + self.stats["error"] - - self.cnt_double_fail_tests - ) - logfile.write('') - - suite_node = ET.Element( - "testsuite", - name=self.suite_name, - errors=str(self.stats["error"]), - failures=str(self.stats["failure"]), - skipped=str(self.stats["skipped"]), - tests=str(numtests), - time="%.3f" % suite_time_delta, - timestamp=datetime.fromtimestamp(self.suite_start_time).isoformat(), - hostname=platform.node(), - ) - global_properties = self._get_global_properties_node() - if global_properties is not None: - suite_node.append(global_properties) - for node_reporter in self.node_reporters_ordered: - suite_node.append(node_reporter.to_xml()) - testsuites = ET.Element("testsuites") - testsuites.append(suite_node) - logfile.write(ET.tostring(testsuites, encoding="unicode")) - logfile.close() + + with open(self.logfile, "w", encoding="utf-8") as logfile: + suite_stop_time = timing.time() + suite_time_delta = suite_stop_time - self.suite_start_time + + numtests = ( + self.stats["passed"] + + self.stats["failure"] + + self.stats["skipped"] + + self.stats["error"] + - self.cnt_double_fail_tests + ) + logfile.write('') + + suite_node = ET.Element( + "testsuite", + name=self.suite_name, + errors=str(self.stats["error"]), + failures=str(self.stats["failure"]), + skipped=str(self.stats["skipped"]), + tests=str(numtests), + time="%.3f" % suite_time_delta, + timestamp=datetime.fromtimestamp(self.suite_start_time).isoformat(), + hostname=platform.node(), + ) + global_properties = self._get_global_properties_node() + if global_properties is not None: + suite_node.append(global_properties) + for node_reporter in self.node_reporters_ordered: + suite_node.append(node_reporter.to_xml()) + testsuites = ET.Element("testsuites") + testsuites.append(suite_node) + logfile.write(ET.tostring(testsuites, encoding="unicode")) def pytest_terminal_summary(self, terminalreporter: TerminalReporter) -> None: terminalreporter.write_sep("-", f"generated xml file: {self.logfile}") From 045ad5ac2d0bf6d321e93e2f3d9dc429301900bf Mon Sep 17 00:00:00 2001 From: Zack Kneupper Date: Tue, 11 May 2021 05:52:55 -0400 Subject: [PATCH 299/630] Replaced if-else statements with ternary expression (#8658) * Replace if-else with ternary expression * assign out variable in readouterr() with ternary expression * assign err variable in readouterr() with ternary expression * Assign precision with ternary expression * ternary expression for collected/collecting * Assign thread_name with ternary expression * Update AUTHORS Co-authored-by: Zachary Kneupper --- AUTHORS | 1 + scripts/release.py | 5 +---- src/_pytest/capture.py | 10 ++-------- src/_pytest/doctest.py | 5 +---- src/_pytest/terminal.py | 5 +---- src/_pytest/threadexception.py | 5 +---- 6 files changed, 7 insertions(+), 24 deletions(-) diff --git a/AUTHORS b/AUTHORS index ec04f98b97e..77b0e483503 100644 --- a/AUTHORS +++ b/AUTHORS @@ -333,5 +333,6 @@ Xuan Luong Xuecong Liao Yoav Caspi Zac Hatfield-Dodds +Zachary Kneupper Zoltán Máté Zsolt Cserna diff --git a/scripts/release.py b/scripts/release.py index 798e42e1fe0..6892a0758ea 100644 --- a/scripts/release.py +++ b/scripts/release.py @@ -100,10 +100,7 @@ def pre_release(version, *, skip_check_links): def changelog(version, write_out=False): - if write_out: - addopts = [] - else: - addopts = ["--draft"] + addopts = [] if write_out else ["--draft"] check_call(["towncrier", "--yes", "--version", version] + addopts) diff --git a/src/_pytest/capture.py b/src/_pytest/capture.py index 9e2d3fa6233..34123718c91 100644 --- a/src/_pytest/capture.py +++ b/src/_pytest/capture.py @@ -618,14 +618,8 @@ def is_started(self) -> bool: return self._state == "started" def readouterr(self) -> CaptureResult[AnyStr]: - if self.out: - out = self.out.snap() - else: - out = "" - if self.err: - err = self.err.snap() - else: - err = "" + out = self.out.snap() if self.out else "" + err = self.err.snap() if self.err else "" return CaptureResult(out, err) diff --git a/src/_pytest/doctest.py b/src/_pytest/doctest.py index cba11a44fc2..e2cca3791ac 100644 --- a/src/_pytest/doctest.py +++ b/src/_pytest/doctest.py @@ -647,10 +647,7 @@ def _remove_unwanted_precision(self, want: str, got: str) -> str: exponent: Optional[str] = w.group("exponent1") if exponent is None: exponent = w.group("exponent2") - if fraction is None: - precision = 0 - else: - precision = len(fraction) + precision = 0 if fraction is None else len(fraction) if exponent is not None: precision -= int(exponent) if float(w.group()) == approx(float(g.group()), abs=10 ** -precision): diff --git a/src/_pytest/terminal.py b/src/_pytest/terminal.py index 01d378abcaf..a8dd0fc6a75 100644 --- a/src/_pytest/terminal.py +++ b/src/_pytest/terminal.py @@ -669,10 +669,7 @@ def report_collect(self, final: bool = False) -> None: skipped = len(self.stats.get("skipped", [])) deselected = len(self.stats.get("deselected", [])) selected = self._numcollected - errors - skipped - deselected - if final: - line = "collected " - else: - line = "collecting " + line = "collected " if final else "collecting " line += ( str(self._numcollected) + " item" + ("" if self._numcollected == 1 else "s") ) diff --git a/src/_pytest/threadexception.py b/src/_pytest/threadexception.py index b250a52346f..43341e739a0 100644 --- a/src/_pytest/threadexception.py +++ b/src/_pytest/threadexception.py @@ -61,10 +61,7 @@ def thread_exception_runtest_hook() -> Generator[None, None, None]: with catch_threading_exception() as cm: yield if cm.args: - if cm.args.thread is not None: - thread_name = cm.args.thread.name - else: - thread_name = "" + thread_name = "" if cm.args.thread is None else cm.args.thread.name msg = f"Exception in thread {thread_name}\n\n" msg += "".join( traceback.format_exception( From 0d6e3b7005d015721a5478f1a21817c6628fbc9d Mon Sep 17 00:00:00 2001 From: Saiprasad Kale <35297720+Saiprasad16@users.noreply.github.com> Date: Tue, 11 May 2021 16:39:25 +0530 Subject: [PATCH 300/630] Update AUTHORS --- AUTHORS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index fa9ed10c20e..269700c8999 100644 --- a/AUTHORS +++ b/AUTHORS @@ -116,7 +116,6 @@ Felix Nieuwenhuizen Feng Ma Florian Bruhin Florian Dahlitz -Saiprasad Kale Floris Bruynooghe Gabriel Reis Garvit Shubham @@ -274,6 +273,7 @@ Ross Lawley Ruaridh Williamson Russel Winder Ryan Wooden +Saiprasad Kale Samuel Dion-Girardeau Samuel Searles-Bryant Samuele Pedroni From 51293de324fc04e778c753a0fd66cb10fe05bf14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Wed, 12 May 2021 13:30:46 +0200 Subject: [PATCH 301/630] Ignore DeprecationWarnings in test_trial_error Fixes https://github.com/pytest-dev/pytest/issues/8663 --- testing/test_unittest.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/testing/test_unittest.py b/testing/test_unittest.py index fd4c01d800d..12bcb9361a4 100644 --- a/testing/test_unittest.py +++ b/testing/test_unittest.py @@ -570,7 +570,9 @@ def f(_): # will crash both at test time and at teardown """ ) - result = pytester.runpytest("-vv", "-oconsole_output_style=classic") + result = pytester.runpytest( + "-vv", "-oconsole_output_style=classic", "-W", "ignore::DeprecationWarning" + ) result.stdout.fnmatch_lines( [ "test_trial_error.py::TC::test_four FAILED", From 33c6ad5bf76231f1a3ba2b75b05ea2cd728f9919 Mon Sep 17 00:00:00 2001 From: Zack Kneupper Date: Fri, 14 May 2021 03:32:50 -0400 Subject: [PATCH 302/630] Replace some for loops, and other minor changes (#8660) * Replace for loop using the operator * Replace for loop with a generator expression inside any() * Replace for loop with a dictionary comprehension * Use list comprehension * Simplify arguments for range() * Change newfuncargs variable to in-line dictionary comprehension * is_ancestor: return base.is_relative_to(query) * Remove unneeded import of pathlib * Try using PurePath * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Import PurePath on new line * Revert and remove is_relative_to Co-authored-by: Zachary Kneupper Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- src/_pytest/doctest.py | 5 +---- src/_pytest/fixtures.py | 9 +++------ src/_pytest/main.py | 5 +---- src/_pytest/python_api.py | 2 +- 4 files changed, 6 insertions(+), 15 deletions(-) diff --git a/src/_pytest/doctest.py b/src/_pytest/doctest.py index e2cca3791ac..870920f5a0d 100644 --- a/src/_pytest/doctest.py +++ b/src/_pytest/doctest.py @@ -145,10 +145,7 @@ def _is_doctest(config: Config, path: Path, parent: Collector) -> bool: if path.suffix in (".txt", ".rst") and parent.session.isinitpath(path): return True globs = config.getoption("doctestglob") or ["test*.txt"] - for glob in globs: - if fnmatch_ex(glob, path): - return True - return False + return any(fnmatch_ex(glob, path) for glob in globs) class ReprFailDoctest(TerminalRepr): diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 9b07a04bc57..1076debb8d9 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -276,7 +276,7 @@ def get_parametrized_fixture_keys(item: nodes.Item, scopenum: int) -> Iterator[_ def reorder_items(items: Sequence[nodes.Item]) -> List[nodes.Item]: argkeys_cache: Dict[int, Dict[nodes.Item, Dict[_Key, None]]] = {} items_by_argkey: Dict[int, Dict[_Key, Deque[nodes.Item]]] = {} - for scopenum in range(0, scopenum_function): + for scopenum in range(scopenum_function): d: Dict[nodes.Item, Dict[_Key, None]] = {} argkeys_cache[scopenum] = d item_d: Dict[_Key, Deque[nodes.Item]] = defaultdict(deque) @@ -296,7 +296,7 @@ def fix_cache_order( argkeys_cache: Dict[int, Dict[nodes.Item, Dict[_Key, None]]], items_by_argkey: Dict[int, Dict[_Key, "Deque[nodes.Item]"]], ) -> None: - for scopenum in range(0, scopenum_function): + for scopenum in range(scopenum_function): for key in argkeys_cache[scopenum].get(item, []): items_by_argkey[scopenum][key].appendleft(item) @@ -377,10 +377,7 @@ def _fill_fixtures_impl(function: "Function") -> None: fm.session._setupstate.setup(function) request._fillfixtures() # Prune out funcargs for jstests. - newfuncargs = {} - for name in fi.argnames: - newfuncargs[name] = function.funcargs[name] - function.funcargs = newfuncargs + function.funcargs = {name: function.funcargs[name] for name in fi.argnames} else: request._fillfixtures() diff --git a/src/_pytest/main.py b/src/_pytest/main.py index 18a8a6592ab..7543495dd88 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -241,10 +241,7 @@ def is_ancestor(base: Path, query: Path) -> bool: """Return whether query is an ancestor of base.""" if base == query: return True - for parent in base.parents: - if parent == query: - return True - return False + return query in base.parents # check if path is an ancestor of cwd if is_ancestor(Path.cwd(), Path(path).absolute()): diff --git a/src/_pytest/python_api.py b/src/_pytest/python_api.py index a2ee63d16de..0c7686d995e 100644 --- a/src/_pytest/python_api.py +++ b/src/_pytest/python_api.py @@ -130,7 +130,7 @@ def _check_type(self) -> None: def _recursive_list_map(f, x): if isinstance(x, list): - return list(_recursive_list_map(f, xi) for xi in x) + return [_recursive_list_map(f, xi) for xi in x] else: return f(x) From c516dba69a05477c002996aaf87561883dc82ad1 Mon Sep 17 00:00:00 2001 From: Rahul Kumaresan Date: Fri, 14 May 2021 18:08:55 +0530 Subject: [PATCH 303/630] add feature to view fixture source location in invocations with --fixtures-per-test option (#8626) * add feature to view fixture source location in invocations with --fixtures-per-test option * remove unrelated changes to show_fixtures_per_test::test_doctest_items * eshew the extraneous else in _show_fixtures_per_test.write_fixture * enable the accommodation of multi-line docstring with --fixtures-per-test option * add feature to view fixture source location in invocations with --fixtures * add colour encoding to fixture location paths * add changelog for #8606 fixing * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- changelog/8606.feature.rst | 5 ++ src/_pytest/python.py | 23 +++---- testing/python/fixtures.py | 26 +++---- testing/python/show_fixtures_per_test.py | 87 +++++++++++++++++++++--- 4 files changed, 108 insertions(+), 33 deletions(-) create mode 100644 changelog/8606.feature.rst diff --git a/changelog/8606.feature.rst b/changelog/8606.feature.rst new file mode 100644 index 00000000000..9705ffe75d6 --- /dev/null +++ b/changelog/8606.feature.rst @@ -0,0 +1,5 @@ +pytest invocations with ``--fixtures-per-test`` and ``--fixtures`` have been enabled with: + +- Fixture location path printed with the fixture name. +- First section of the fixture's docstring printed under the fixture name. +- Whole of fixture's docstring printed under the fixture name using ``--verbose`` option. diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 5ef5085c797..436f463ee18 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -1428,15 +1428,15 @@ def write_fixture(fixture_def: fixtures.FixtureDef[object]) -> None: argname = fixture_def.argname if verbose <= 0 and argname.startswith("_"): return - if verbose > 0: - bestrel = get_best_relpath(fixture_def.func) - funcargspec = f"{argname} -- {bestrel}" - else: - funcargspec = argname - tw.line(funcargspec, green=True) + bestrel = get_best_relpath(fixture_def.func) + tw.write(f"{argname}", green=True) + tw.write(f" -- {bestrel}", yellow=True) + tw.write("\n") fixture_doc = inspect.getdoc(fixture_def.func) if fixture_doc: - write_docstring(tw, fixture_doc) + write_docstring( + tw, fixture_doc.split("\n\n")[0] if verbose <= 0 else fixture_doc + ) else: tw.line(" no docstring available", red=True) @@ -1508,18 +1508,17 @@ def _showfixtures_main(config: Config, session: Session) -> None: tw.line() tw.sep("-", f"fixtures defined from {module}") currentmodule = module - if verbose <= 0 and argname[0] == "_": + if verbose <= 0 and argname.startswith("_"): continue - tw.write(argname, green=True) + tw.write(f"{argname}", green=True) if fixturedef.scope != "function": tw.write(" [%s scope]" % fixturedef.scope, cyan=True) - if verbose > 0: - tw.write(" -- %s" % bestrel, yellow=True) + tw.write(f" -- {bestrel}", yellow=True) tw.write("\n") loc = getlocation(fixturedef.func, str(curdir)) doc = inspect.getdoc(fixturedef.func) if doc: - write_docstring(tw, doc) + write_docstring(tw, doc.split("\n\n")[0] if verbose <= 0 else doc) else: tw.line(f" {loc}: no docstring available", red=True) tw.line() diff --git a/testing/python/fixtures.py b/testing/python/fixtures.py index 569b5d67e26..b7f5b25c544 100644 --- a/testing/python/fixtures.py +++ b/testing/python/fixtures.py @@ -3334,9 +3334,9 @@ def test_show_fixtures(self, pytester: Pytester) -> None: result = pytester.runpytest("--fixtures") result.stdout.fnmatch_lines( [ - "tmp_path_factory [[]session scope[]]", + "tmp_path_factory [[]session scope[]] -- *tmpdir.py*", "*for the test session*", - "tmp_path", + "tmp_path -- *", "*temporary directory*", ] ) @@ -3367,9 +3367,9 @@ def arg1(): result = pytester.runpytest("--fixtures", p) result.stdout.fnmatch_lines( """ - *tmp_path + *tmp_path -- * *fixtures defined from* - *arg1* + *arg1 -- test_show_fixtures_testmodule.py:6* *hello world* """ ) @@ -3429,10 +3429,10 @@ def arg2(): textwrap.dedent( """\ * fixtures defined from test_show_fixtures_trimmed_doc * - arg2 + arg2 -- test_show_fixtures_trimmed_doc.py:10 line1 line2 - arg1 + arg1 -- test_show_fixtures_trimmed_doc.py:3 line1 line2 """ @@ -3458,7 +3458,7 @@ def fixture1(): textwrap.dedent( """\ * fixtures defined from test_show_fixtures_indented_doc * - fixture1 + fixture1 -- test_show_fixtures_indented_doc.py:3 line1 indented line """ @@ -3486,7 +3486,7 @@ def fixture1(): textwrap.dedent( """\ * fixtures defined from test_show_fixtures_indented_doc_first_line_unindented * - fixture1 + fixture1 -- test_show_fixtures_indented_doc_first_line_unindented.py:3 line1 line2 indented line @@ -3514,7 +3514,7 @@ def fixture1(self): textwrap.dedent( """\ * fixtures defined from test_show_fixtures_indented_in_class * - fixture1 + fixture1 -- test_show_fixtures_indented_in_class.py:4 line1 line2 indented line @@ -3554,11 +3554,11 @@ def test_b(fix_b): result.stdout.fnmatch_lines( """ * fixtures defined from test_a * - fix_a + fix_a -- test_a.py:4 Fixture A * fixtures defined from test_b * - fix_b + fix_b -- test_b.py:4 Fixture B """ ) @@ -3594,11 +3594,11 @@ def test_bar(arg1): result.stdout.fnmatch_lines( """ * fixtures defined from conftest * - arg1 + arg1 -- conftest.py:3 Hello World in conftest.py * fixtures defined from test_show_fixtures_with_same_name * - arg1 + arg1 -- test_show_fixtures_with_same_name.py:3 Hi from test module """ ) diff --git a/testing/python/show_fixtures_per_test.py b/testing/python/show_fixtures_per_test.py index 2a15132738d..f756dca41c7 100644 --- a/testing/python/show_fixtures_per_test.py +++ b/testing/python/show_fixtures_per_test.py @@ -29,7 +29,7 @@ def test_arg1(arg1): [ "*fixtures used by test_arg1*", "*(test_fixtures_in_module.py:9)*", - "arg1", + "arg1 -- test_fixtures_in_module.py:6", " arg1 docstring", ] ) @@ -68,17 +68,16 @@ def test_arg3(arg3): [ "*fixtures used by test_arg2*", "*(test_fixtures_in_conftest.py:2)*", - "arg2", + "arg2 -- conftest.py:6", " arg2 docstring", "*fixtures used by test_arg3*", "*(test_fixtures_in_conftest.py:4)*", - "arg1", + "arg1 -- conftest.py:3", " arg1 docstring", - "arg2", + "arg2 -- conftest.py:6", " arg2 docstring", - "arg3", + "arg3 -- conftest.py:9", " arg3", - " docstring", ] ) @@ -112,9 +111,9 @@ def test_args(arg1, arg2): [ "*fixtures used by test_args*", "*(test_should_show_fixtures_used_by_test.py:6)*", - "arg1", + "arg1 -- test_should_show_fixtures_used_by_test.py:3", " arg1 from testmodule", - "arg2", + "arg2 -- conftest.py:6", " arg2 from conftest", ] ) @@ -181,3 +180,75 @@ def foo(): assert result.ret == 0 result.stdout.fnmatch_lines(["*collected 2 items*"]) + + +def test_multiline_docstring_in_module(pytester: Pytester) -> None: + p = pytester.makepyfile( + ''' + import pytest + @pytest.fixture + def arg1(): + """Docstring content that spans across multiple lines, + through second line, + and through third line. + + Docstring content that extends into a second paragraph. + + Docstring content that extends into a third paragraph. + """ + def test_arg1(arg1): + pass + ''' + ) + + result = pytester.runpytest("--fixtures-per-test", p) + assert result.ret == 0 + + result.stdout.fnmatch_lines( + [ + "*fixtures used by test_arg1*", + "*(test_multiline_docstring_in_module.py:13)*", + "arg1 -- test_multiline_docstring_in_module.py:3", + " Docstring content that spans across multiple lines,", + " through second line,", + " and through third line.", + ] + ) + + +def test_verbose_include_multiline_docstring(pytester: Pytester) -> None: + p = pytester.makepyfile( + ''' + import pytest + @pytest.fixture + def arg1(): + """Docstring content that spans across multiple lines, + through second line, + and through third line. + + Docstring content that extends into a second paragraph. + + Docstring content that extends into a third paragraph. + """ + def test_arg1(arg1): + pass + ''' + ) + + result = pytester.runpytest("--fixtures-per-test", "-v", p) + assert result.ret == 0 + + result.stdout.fnmatch_lines( + [ + "*fixtures used by test_arg1*", + "*(test_verbose_include_multiline_docstring.py:13)*", + "arg1 -- test_verbose_include_multiline_docstring.py:3", + " Docstring content that spans across multiple lines,", + " through second line,", + " and through third line.", + " ", + " Docstring content that extends into a second paragraph.", + " ", + " Docstring content that extends into a third paragraph.", + ] + ) From 8b2f83772d3fbfbbef65c37673b8768ba67b71c5 Mon Sep 17 00:00:00 2001 From: Olga Matoula Date: Sat, 15 May 2021 15:15:43 +0100 Subject: [PATCH 304/630] Catch any warning on warns with no arg passed --- AUTHORS | 1 + changelog/8645.improvement.rst | 4 ++++ doc/en/how-to/capture-warnings.rst | 4 ++-- src/_pytest/deprecated.py | 5 +++++ src/_pytest/recwarn.py | 8 +++++--- testing/deprecated_test.py | 8 ++++++++ testing/test_recwarn.py | 13 ++++++++++++- testing/test_tmpdir.py | 13 ++++++++----- 8 files changed, 45 insertions(+), 11 deletions(-) create mode 100644 changelog/8645.improvement.rst diff --git a/AUTHORS b/AUTHORS index 5f300c9d46d..b822d469aed 100644 --- a/AUTHORS +++ b/AUTHORS @@ -231,6 +231,7 @@ Nicholas Murphy Niclas Olofsson Nicolas Delaby Nikolay Kondratyev +Olga Matoula Oleg Pidsadnyi Oleg Sushchenko Oliver Bestwalter diff --git a/changelog/8645.improvement.rst b/changelog/8645.improvement.rst new file mode 100644 index 00000000000..b3a68b0b8c8 --- /dev/null +++ b/changelog/8645.improvement.rst @@ -0,0 +1,4 @@ +Reducing confusion from `pytest.warns(None)` by: + +- Allowing no arguments to be passed in order to catch any exception (no argument defaults to `Warning`). +- Emit a deprecation warning if passed `None`. diff --git a/doc/en/how-to/capture-warnings.rst b/doc/en/how-to/capture-warnings.rst index 1bafaeeb900..28e071c45e0 100644 --- a/doc/en/how-to/capture-warnings.rst +++ b/doc/en/how-to/capture-warnings.rst @@ -332,11 +332,11 @@ You can record raised warnings either using func:`pytest.warns` or with the ``recwarn`` fixture. To record with func:`pytest.warns` without asserting anything about the warnings, -pass ``None`` as the expected warning type: +pass no arguments as the expected warning type and it will default to a generic Warning: .. code-block:: python - with pytest.warns(None) as record: + with pytest.warns() as record: warnings.warn("user", UserWarning) warnings.warn("runtime", RuntimeWarning) diff --git a/src/_pytest/deprecated.py b/src/_pytest/deprecated.py index 7a09d516362..9f4b71bdc60 100644 --- a/src/_pytest/deprecated.py +++ b/src/_pytest/deprecated.py @@ -101,6 +101,11 @@ "see https://docs.pytest.org/en/latest/deprecations.html" "#py-path-local-arguments-for-hooks-replaced-with-pathlib-path", ) + +WARNS_NONE_ARG = PytestDeprecationWarning( + "Please pass an explicit Warning type or tuple of Warning types." +) + # You want to make some `__init__` or function "private". # # def my_private_function(some, args): diff --git a/src/_pytest/recwarn.py b/src/_pytest/recwarn.py index bc3a2835e35..852b4c191b0 100644 --- a/src/_pytest/recwarn.py +++ b/src/_pytest/recwarn.py @@ -17,6 +17,7 @@ from _pytest.compat import final from _pytest.deprecated import check_ispytest +from _pytest.deprecated import WARNS_NONE_ARG from _pytest.fixtures import fixture from _pytest.outcomes import fail @@ -83,7 +84,7 @@ def deprecated_call( @overload def warns( - expected_warning: Optional[Union[Type[Warning], Tuple[Type[Warning], ...]]], + expected_warning: Optional[Union[Type[Warning], Tuple[Type[Warning], ...]]] = ..., *, match: Optional[Union[str, Pattern[str]]] = ..., ) -> "WarningsChecker": @@ -101,7 +102,7 @@ def warns( def warns( - expected_warning: Optional[Union[Type[Warning], Tuple[Type[Warning], ...]]], + expected_warning: Optional[Union[Type[Warning], Tuple[Type[Warning], ...]]] = Warning, *args: Any, match: Optional[Union[str, Pattern[str]]] = None, **kwargs: Any, @@ -232,7 +233,7 @@ def __init__( self, expected_warning: Optional[ Union[Type[Warning], Tuple[Type[Warning], ...]] - ] = None, + ] = Warning, match_expr: Optional[Union[str, Pattern[str]]] = None, *, _ispytest: bool = False, @@ -242,6 +243,7 @@ def __init__( msg = "exceptions must be derived from Warning, not %s" if expected_warning is None: + warnings.warn(WARNS_NONE_ARG, stacklevel=4) expected_warning_tup = None elif isinstance(expected_warning, tuple): for exc in expected_warning: diff --git a/testing/deprecated_test.py b/testing/deprecated_test.py index 1d012adf250..86650877e4a 100644 --- a/testing/deprecated_test.py +++ b/testing/deprecated_test.py @@ -178,3 +178,11 @@ def test_hookproxy_warnings_for_fspath(tmp_path, hooktype, request): assert l1 < record.lineno < l2 hooks.pytest_ignore_collect(config=request.config, fspath=tmp_path) + + +def test_warns_none_is_deprecated(): + with pytest.warns( + PytestDeprecationWarning, + match="Please pass an explicit Warning type or tuple of Warning types.", + ): + pytest.warns(None) diff --git a/testing/test_recwarn.py b/testing/test_recwarn.py index 91efe6d2393..c73ab8a117f 100644 --- a/testing/test_recwarn.py +++ b/testing/test_recwarn.py @@ -298,7 +298,7 @@ def test_record(self) -> None: assert str(record[0].message) == "user" def test_record_only(self) -> None: - with pytest.warns(None) as record: + with pytest.warns() as record: warnings.warn("user", UserWarning) warnings.warn("runtime", RuntimeWarning) @@ -306,6 +306,17 @@ def test_record_only(self) -> None: assert str(record[0].message) == "user" assert str(record[1].message) == "runtime" + def test_record_only_none_deprecated_warn(self) -> None: + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + with pytest.warns(None) as record: + warnings.warn("user", UserWarning) + warnings.warn("runtime", RuntimeWarning) + + assert len(record) == 2 + assert str(record[0].message) == "user" + assert str(record[1].message) == "runtime" + def test_record_by_subclass(self) -> None: with pytest.warns(Warning) as record: warnings.warn("user", UserWarning) diff --git a/testing/test_tmpdir.py b/testing/test_tmpdir.py index 40e75663cb7..fe4b8b8cd13 100644 --- a/testing/test_tmpdir.py +++ b/testing/test_tmpdir.py @@ -1,6 +1,7 @@ import os import stat import sys +import warnings from pathlib import Path from typing import Callable from typing import cast @@ -400,11 +401,13 @@ def test_on_rm_rf_error(self, tmp_path: Path) -> None: assert fn.is_file() # ignored function - with pytest.warns(None) as warninfo: - exc_info4 = (None, PermissionError(), None) - on_rm_rf_error(os.open, str(fn), exc_info4, start_path=tmp_path) - assert fn.is_file() - assert not [x.message for x in warninfo] + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + with pytest.warns(None) as warninfo: + exc_info4 = (None, PermissionError(), None) + on_rm_rf_error(os.open, str(fn), exc_info4, start_path=tmp_path) + assert fn.is_file() + assert not [x.message for x in warninfo] exc_info5 = (None, PermissionError(), None) on_rm_rf_error(os.unlink, str(fn), exc_info5, start_path=tmp_path) From 6ae71a2c2b7bdadf4f16c0eac960838315b51198 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 15 May 2021 17:51:54 +0000 Subject: [PATCH 305/630] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/_pytest/recwarn.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/_pytest/recwarn.py b/src/_pytest/recwarn.py index 852b4c191b0..7156272097f 100644 --- a/src/_pytest/recwarn.py +++ b/src/_pytest/recwarn.py @@ -102,7 +102,9 @@ def warns( def warns( - expected_warning: Optional[Union[Type[Warning], Tuple[Type[Warning], ...]]] = Warning, + expected_warning: Optional[ + Union[Type[Warning], Tuple[Type[Warning], ...]] + ] = Warning, *args: Any, match: Optional[Union[str, Pattern[str]]] = None, **kwargs: Any, From a69ed66c2eac79c8459b06e6933a0b6f307cc0c3 Mon Sep 17 00:00:00 2001 From: pytest bot Date: Sun, 16 May 2021 00:10:52 +0000 Subject: [PATCH 306/630] [automated] Update plugin list --- doc/en/reference/plugin_list.rst | 40 +++++++++++++++++--------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/doc/en/reference/plugin_list.rst b/doc/en/reference/plugin_list.rst index fdd49c5dfc6..45b9fe672db 100644 --- a/doc/en/reference/plugin_list.rst +++ b/doc/en/reference/plugin_list.rst @@ -6,13 +6,13 @@ Plugin List PyPI projects that match "pytest-\*" are considered plugins and are listed automatically. Packages classified as inactive are excluded. -This list contains 865 plugins. +This list contains 867 plugins. ============================================================================================================== ======================================================================================================================================================================== ============== ===================== ================================================ name summary last release status requires ============================================================================================================== ======================================================================================================================================================================== ============== ===================== ================================================ `pytest-adaptavist `_ pytest plugin for generating test execution results within Jira Test Management (tm4j) Apr 22, 2021 N/A pytest (>=3.4.1) -`pytest-adf `_ Pytest plugin for writing Azure Data Factory integration tests Mar 10, 2021 4 - Beta pytest (>=3.5.0) +`pytest-adf `_ Pytest plugin for writing Azure Data Factory integration tests May 10, 2021 4 - Beta pytest (>=3.5.0) `pytest-adf-azure-identity `_ Pytest plugin for writing Azure Data Factory integration tests Mar 06, 2021 4 - Beta pytest (>=3.5.0) `pytest-aggreport `_ pytest plugin for pytest-repeat that generate aggregate report of the same test cases with additional statistics details. Mar 07, 2021 4 - Beta pytest (>=6.2.2) `pytest-aio `_ Pytest plugin for testing async python code Apr 05, 2021 4 - Beta pytest ; extra == 'tests' @@ -22,7 +22,7 @@ name `pytest-aioresponses `_ py.test integration for aioresponses Dec 21, 2020 4 - Beta pytest (>=3.5.0) `pytest-aioworkers `_ A plugin to test aioworkers project with pytest Dec 04, 2019 4 - Beta pytest (>=3.5.0) `pytest-airflow `_ pytest support for airflow. Apr 03, 2019 3 - Alpha pytest (>=4.4.0) -`pytest-alembic `_ A pytest plugin for verifying alembic migrations. Apr 26, 2021 N/A pytest (>=1.0) +`pytest-alembic `_ A pytest plugin for verifying alembic migrations. May 10, 2021 N/A pytest (>=1.0) `pytest-allclose `_ Pytest fixture extending Numpy's allclose function Jul 30, 2019 5 - Production/Stable pytest `pytest-allure-adaptor `_ Plugin for py.test to generate allure xml reports Jan 10, 2018 N/A pytest (>=2.7.3) `pytest-allure-adaptor2 `_ Plugin for py.test to generate allure xml reports Oct 14, 2020 N/A pytest (>=2.7.3) @@ -128,7 +128,7 @@ name `pytest-cloud `_ Distributed tests planner plugin for pytest testing framework. Oct 05, 2020 6 - Mature N/A `pytest-cloudflare-worker `_ pytest plugin for testing cloudflare workers Mar 30, 2021 4 - Beta pytest (>=6.0.0) `pytest-cobra `_ PyTest plugin for testing Smart Contracts for Ethereum blockchain. Jun 29, 2019 3 - Alpha pytest (<4.0.0,>=3.7.1) -`pytest-codeblocks `_ Test code blocks in your READMEs May 08, 2021 4 - Beta pytest (>=6) +`pytest-codeblocks `_ Test code blocks in your READMEs May 10, 2021 4 - Beta pytest (>=6) `pytest-codecheckers `_ pytest plugin to add source code sanity checks (pep8 and friends) Feb 13, 2010 N/A N/A `pytest-codecov `_ Pytest plugin for uploading pytest-cov results to codecov.io May 05, 2021 4 - Beta pytest (>=4.6.0) `pytest-codegen `_ Automatically create pytest test signatures Aug 23, 2020 2 - Pre-Alpha N/A @@ -136,7 +136,7 @@ name `pytest-collect-formatter `_ Formatter for pytest collect output Mar 29, 2021 5 - Production/Stable N/A `pytest-collect-formatter2 `_ Formatter for pytest collect output Apr 11, 2021 5 - Production/Stable N/A `pytest-colordots `_ Colorizes the progress indicators Oct 06, 2017 5 - Production/Stable N/A -`pytest-commander `_ An interactive GUI test runner for PyTest Jan 31, 2021 N/A pytest (>=5.0.0) +`pytest-commander `_ An interactive GUI test runner for PyTest May 09, 2021 N/A pytest (<7.0.0,>=6.2.4) `pytest-common-subject `_ pytest framework for testing different aspects of a common method Nov 12, 2020 N/A pytest (>=3.6,<7) `pytest-concurrent `_ Concurrently execute test cases with multithread, multiprocess and gevent Jan 12, 2019 4 - Beta pytest (>=3.1.1) `pytest-config `_ Base configurations and utilities for developing your Python project test suite with pytest. Nov 07, 2014 5 - Production/Stable N/A @@ -148,7 +148,7 @@ name `pytest-cookies `_ The pytest plugin for your Cookiecutter templates. 🍪 Feb 14, 2020 5 - Production/Stable pytest (<6.0.0,>=3.3.0) `pytest-couchdbkit `_ py.test extension for per-test couchdb databases using couchdbkit Apr 17, 2012 N/A N/A `pytest-count `_ count erros and send email Jan 12, 2018 4 - Beta N/A -`pytest-cov `_ Pytest plugin for measuring coverage. Jan 20, 2021 5 - Production/Stable pytest (>=4.6) +`pytest-cov `_ Pytest plugin for measuring coverage. May 14, 2021 5 - Production/Stable pytest (>=4.6) `pytest-cover `_ Pytest plugin for measuring coverage. Forked from `pytest-cov`. Aug 01, 2015 5 - Production/Stable N/A `pytest-coverage `_ Jun 17, 2015 N/A N/A `pytest-coverage-context `_ Coverage dynamic context support for PyTest, including sub-processes Jan 04, 2021 4 - Beta pytest (>=6.1.0) @@ -186,6 +186,7 @@ name `pytest-dbus-notification `_ D-BUS notifications for pytest results. Mar 05, 2014 5 - Production/Stable N/A `pytest-deadfixtures `_ A simple plugin to list unused fixtures in pytest Jul 23, 2020 5 - Production/Stable N/A `pytest-deepcov `_ deepcov Mar 30, 2021 N/A N/A +`pytest-demo-plugin `_ pytest示例插件 May 15, 2021 N/A N/A `pytest-dependency `_ Manage dependencies of tests Feb 14, 2020 4 - Beta N/A `pytest-depends `_ Tests that depend on other tests Apr 05, 2020 5 - Production/Stable pytest (>=3) `pytest-deprecate `_ Mark tests as testing a deprecated feature with a warning note. Jul 01, 2019 N/A N/A @@ -199,7 +200,7 @@ name `pytest-disable `_ pytest plugin to disable a test and skip it from testrun Sep 10, 2015 4 - Beta N/A `pytest-disable-plugin `_ Disable plugins per test Feb 28, 2019 4 - Beta pytest (>=3.5.0) `pytest-discord `_ A pytest plugin to notify test results to a Discord channel. Mar 20, 2021 3 - Alpha pytest (!=6.0.0,<7,>=3.3.2) -`pytest-django `_ A Django plugin for pytest. Apr 10, 2021 5 - Production/Stable pytest (>=5.4.0) +`pytest-django `_ A Django plugin for pytest. May 15, 2021 5 - Production/Stable pytest (>=5.4.0) `pytest-django-ahead `_ A Django plugin for pytest. Oct 27, 2016 5 - Production/Stable pytest (>=2.9) `pytest-djangoapp `_ Nice pytest plugin to help you with Django pluggable application testing. Apr 10, 2021 4 - Beta N/A `pytest-django-cache-xdist `_ A djangocachexdist plugin for pytest May 12, 2020 4 - Beta N/A @@ -254,7 +255,7 @@ name `pytest-easy-server `_ Pytest plugin for easy testing against servers May 01, 2021 4 - Beta pytest (<5.0.0,>=4.3.1) ; python_version < "3.5" `pytest-ec2 `_ Pytest execution on EC2 instance Oct 22, 2019 3 - Alpha N/A `pytest-echo `_ pytest plugin with mechanisms for echoing environment variables, package version and generic attributes Jan 08, 2020 5 - Production/Stable N/A -`pytest-elasticsearch `_ Elasticsearch process and client fixtures for py.test. Feb 19, 2020 5 - Production/Stable pytest (>=3.0.0) +`pytest-elasticsearch `_ Elasticsearch fixtures and fixture factories for Pytest. May 12, 2021 5 - Production/Stable pytest (>=3.0.0) `pytest-elements `_ Tool to help automate user interfaces Jan 13, 2021 N/A pytest (>=5.4,<6.0) `pytest-elk-reporter `_ A simple plugin to use with pytest Jan 24, 2021 4 - Beta pytest (>=3.5.0) `pytest-email `_ Send execution result email Jul 08, 2020 N/A pytest @@ -313,7 +314,7 @@ name `pytest-fixtures `_ Common fixtures for pytest May 01, 2019 5 - Production/Stable N/A `pytest-fixture-tools `_ Plugin for pytest which provides tools for fixtures Aug 18, 2020 6 - Mature pytest `pytest-flake8 `_ pytest plugin to check FLAKE8 requirements Dec 16, 2020 4 - Beta pytest (>=3.5) -`pytest-flake8dir `_ A pytest fixture for testing flake8 plugins. Mar 18, 2021 5 - Production/Stable pytest +`pytest-flake8dir `_ A pytest fixture for testing flake8 plugins. May 10, 2021 5 - Production/Stable pytest `pytest-flakefinder `_ Runs tests multiple times to expose flakiness. Jul 28, 2020 4 - Beta pytest (>=2.7.1) `pytest-flakes `_ pytest plugin to check source code with pyflakes Nov 28, 2020 5 - Production/Stable N/A `pytest-flaptastic `_ Flaptastic py.test plugin Mar 17, 2019 N/A N/A @@ -397,7 +398,7 @@ name `pytest-instrument `_ pytest plugin to instrument tests Apr 05, 2020 5 - Production/Stable pytest (>=5.1.0) `pytest-integration `_ Organizing pytests by integration or not Apr 16, 2020 N/A N/A `pytest-interactive `_ A pytest plugin for console based interactive test selection just after the collection phase Nov 30, 2017 3 - Alpha N/A -`pytest-invenio `_ Pytest fixtures for Invenio. Dec 17, 2020 5 - Production/Stable pytest (<7,>=6) +`pytest-invenio `_ Pytest fixtures for Invenio. May 11, 2021 5 - Production/Stable pytest (<7,>=6) `pytest-involve `_ Run tests covering a specific file or changeset Feb 02, 2020 4 - Beta pytest (>=3.5.0) `pytest-ipdb `_ A py.test plug-in to enable drop to ipdb debugger on test failure. Sep 02, 2014 2 - Pre-Alpha N/A `pytest-ipynb `_ THIS PROJECT IS ABANDONED Jan 29, 2019 3 - Alpha N/A @@ -619,7 +620,7 @@ name `pytest-raisesregexp `_ Simple pytest plugin to look for regex in Exceptions Dec 18, 2015 N/A N/A `pytest-raisin `_ Plugin enabling the use of exception instances with pytest.raises Jun 25, 2020 N/A pytest `pytest-random `_ py.test plugin to randomize tests Apr 28, 2013 3 - Alpha N/A -`pytest-randomly `_ Pytest plugin to randomly order tests and control random.seed. Apr 11, 2021 5 - Production/Stable pytest +`pytest-randomly `_ Pytest plugin to randomly order tests and control random.seed. May 10, 2021 5 - Production/Stable pytest `pytest-randomness `_ Pytest plugin about random seed management May 30, 2019 3 - Alpha N/A `pytest-random-num `_ Randomise the order in which pytest tests are run with some control over the randomness Oct 19, 2020 5 - Production/Stable N/A `pytest-random-order `_ Randomise the order in which pytest tests are run with some control over the randomness Nov 30, 2018 5 - Production/Stable pytest (>=3.0.0) @@ -656,14 +657,14 @@ name `pytest-reraise `_ Make multi-threaded pytest test cases fail when they should Jun 03, 2020 5 - Production/Stable N/A `pytest-rerun `_ Re-run only changed files in specified branch Jul 08, 2019 N/A pytest (>=3.6) `pytest-rerunfailures `_ pytest plugin to re-run tests to eliminate flaky failures Sep 29, 2020 5 - Production/Stable pytest (>=5.0) -`pytest-resilient-circuits `_ Resilient Circuits fixtures for PyTest. Apr 15, 2021 N/A N/A +`pytest-resilient-circuits `_ Resilient Circuits fixtures for PyTest. May 14, 2021 N/A N/A `pytest-resource `_ Load resource fixture plugin to use with pytest Nov 14, 2018 4 - Beta N/A `pytest-resource-path `_ Provides path for uniform access to test resources in isolated directory May 01, 2021 5 - Production/Stable pytest (>=3.5.0) `pytest-responsemock `_ Simplified requests calls mocking for pytest Oct 10, 2020 5 - Production/Stable N/A `pytest-responses `_ py.test integration for responses Apr 26, 2021 N/A pytest (>=2.5) -`pytest-restrict `_ Pytest plugin to restrict the test types allowed Dec 03, 2020 5 - Production/Stable pytest +`pytest-restrict `_ Pytest plugin to restrict the test types allowed May 10, 2021 5 - Production/Stable pytest `pytest-rethinkdb `_ A RethinkDB plugin for pytest. Jul 24, 2016 4 - Beta N/A -`pytest-reverse `_ Pytest plugin to reverse test order. Dec 27, 2020 5 - Production/Stable pytest +`pytest-reverse `_ Pytest plugin to reverse test order. May 10, 2021 5 - Production/Stable pytest `pytest-ringo `_ pytest plugin to test webapplications using the Ringo webframework Sep 27, 2017 3 - Alpha N/A `pytest-rng `_ Fixtures for seeding tests and making randomness reproducible Aug 08, 2019 5 - Production/Stable pytest `pytest-roast `_ pytest plugin for ROAST configuration override and fixtures Apr 30, 2021 5 - Production/Stable pytest @@ -683,13 +684,13 @@ name `pytest-sanic `_ a pytest plugin for Sanic Feb 27, 2021 N/A pytest (>=5.2) `pytest-sanity `_ Dec 07, 2020 N/A N/A `pytest-sa-pg `_ May 14, 2019 N/A N/A -`pytest-sbase `_ A complete web automation framework for end-to-end testing. May 08, 2021 5 - Production/Stable N/A +`pytest-sbase `_ A complete web automation framework for end-to-end testing. May 12, 2021 5 - Production/Stable N/A `pytest-scenario `_ pytest plugin for test scenarios Feb 06, 2017 3 - Alpha N/A `pytest-schema `_ 👍 Validate return values against a schema-like object in testing Aug 31, 2020 5 - Production/Stable pytest (>=3.5.0) `pytest-securestore `_ An encrypted password store for use within pytest cases Jun 19, 2019 4 - Beta N/A `pytest-select `_ A pytest plugin which allows to (de-)select tests from a file. Jan 18, 2019 3 - Alpha pytest (>=3.0) `pytest-selenium `_ pytest plugin for Selenium Sep 19, 2020 5 - Production/Stable pytest (>=5.0.0) -`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. May 08, 2021 5 - Production/Stable N/A +`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. May 12, 2021 5 - Production/Stable N/A `pytest-selenium-enhancer `_ pytest plugin for Selenium Nov 26, 2020 5 - Production/Stable N/A `pytest-selenium-pdiff `_ A pytest package implementing perceptualdiff for Selenium tests. Apr 06, 2017 2 - Pre-Alpha N/A `pytest-send-email `_ Send pytest execution result email Dec 04, 2019 N/A N/A @@ -719,7 +720,7 @@ name `pytest-snail `_ Plugin for adding a marker to slow running tests. 🐌 Nov 04, 2019 3 - Alpha pytest (>=5.0.1) `pytest-snapci `_ py.test plugin for Snap-CI Nov 12, 2015 N/A N/A `pytest-snapshot `_ A plugin to enable snapshot testing with pytest. Apr 20, 2021 4 - Beta pytest (>=3.0.0) -`pytest-snmpserver `_ Sep 14, 2020 N/A N/A +`pytest-snmpserver `_ May 12, 2021 N/A N/A `pytest-socket `_ Pytest Plugin to disable socket calls during tests Mar 30, 2021 4 - Beta pytest (>=3.6.3) `pytest-soft-assertions `_ May 05, 2020 3 - Alpha pytest `pytest-solr `_ Solr process and client fixtures for py.test. May 11, 2020 3 - Alpha pytest (>=3.0.0) @@ -761,7 +762,7 @@ name `pytest-subunit `_ pytest-subunit is a plugin for py.test which outputs testsresult in subunit format. Aug 29, 2017 N/A N/A `pytest-sugar `_ pytest-sugar is a plugin for pytest that changes the default look and feel of pytest (e.g. progressbar, show tests that fail instantly). Jul 06, 2020 3 - Alpha N/A `pytest-sugar-bugfix159 `_ Workaround for https://github.com/Frozenball/pytest-sugar/issues/159 Nov 07, 2018 5 - Production/Stable pytest (!=3.7.3,>=3.5); extra == 'testing' -`pytest-super-check `_ Pytest plugin to check your TestCase classes call super in setUp, tearDown, etc. Dec 13, 2020 5 - Production/Stable pytest +`pytest-super-check `_ Pytest plugin to check your TestCase classes call super in setUp, tearDown, etc. May 10, 2021 5 - Production/Stable pytest `pytest-svn `_ SVN repository fixture for py.test May 28, 2019 5 - Production/Stable pytest `pytest-symbols `_ pytest-symbols is a pytest plugin that adds support for passing test environment symbols into pytest tests. Nov 20, 2017 3 - Alpha N/A `pytest-tap `_ Test Anything Protocol (TAP) reporting plugin for pytest Nov 07, 2020 5 - Production/Stable pytest (>=3.0) @@ -789,6 +790,7 @@ name `pytest-testrail-e2e `_ pytest plugin for creating TestRail runs and adding results Jun 11, 2020 N/A pytest (>=3.6) `pytest-testrail-plugin `_ PyTest plugin for TestRail Apr 21, 2020 3 - Alpha pytest `pytest-testrail-reporter `_ Sep 10, 2018 N/A N/A +`pytest-testreport `_ May 14, 2021 4 - Beta pytest (>=3.5.0) `pytest-testslide `_ TestSlide fixture for pytest Jan 07, 2021 5 - Production/Stable pytest (~=6.2) `pytest-test-this `_ Plugin for py.test to run relevant tests, based on naively checking if a test contains a reference to the symbol you supply Sep 15, 2019 2 - Pre-Alpha pytest (>=2.3) `pytest-tesults `_ Tesults plugin for pytest May 18, 2020 5 - Production/Stable pytest (>=3.5.0) @@ -874,6 +876,6 @@ name `pytest-yuk `_ Display tests you are uneasy with, using 🤢/🤮 for pass/fail of tests marked with yuk. Mar 26, 2021 N/A N/A `pytest-zafira `_ A Zafira plugin for pytest Sep 18, 2019 5 - Production/Stable pytest (==4.1.1) `pytest-zap `_ OWASP ZAP plugin for py.test. May 12, 2014 4 - Beta N/A -`pytest-zebrunner `_ Pytest connector for Zebrunner reporting Apr 06, 2021 4 - Beta pytest (>=6.1.1,<7.0.0) +`pytest-zebrunner `_ Pytest connector for Zebrunner reporting May 12, 2021 5 - Production/Stable pytest (>=6.0.0,<7.0.0) `pytest-zigzag `_ Extend py.test for RPC OpenStack testing. Feb 27, 2019 4 - Beta pytest (~=3.6) ============================================================================================================== ======================================================================================================================================================================== ============== ===================== ================================================ From dbe66d97b49d94b131fa9ccbcf549f6376f88d9c Mon Sep 17 00:00:00 2001 From: Olga Matoula Date: Sun, 16 May 2021 12:07:39 +0100 Subject: [PATCH 307/630] Add better warning msg for deprecated warns(None) --- src/_pytest/deprecated.py | 3 ++- testing/deprecated_test.py | 5 +++-- testing/test_recwarn.py | 1 + 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/_pytest/deprecated.py b/src/_pytest/deprecated.py index 9f4b71bdc60..99907d12896 100644 --- a/src/_pytest/deprecated.py +++ b/src/_pytest/deprecated.py @@ -103,7 +103,8 @@ ) WARNS_NONE_ARG = PytestDeprecationWarning( - "Please pass an explicit Warning type or tuple of Warning types." + "Passing None to catch any warning has been deprecated, pass no arguments instead:\n" + " Replace pytest.warns(None) by simply pytest.warns()." ) # You want to make some `__init__` or function "private". diff --git a/testing/deprecated_test.py b/testing/deprecated_test.py index 86650877e4a..0974cf6c646 100644 --- a/testing/deprecated_test.py +++ b/testing/deprecated_test.py @@ -183,6 +183,7 @@ def test_hookproxy_warnings_for_fspath(tmp_path, hooktype, request): def test_warns_none_is_deprecated(): with pytest.warns( PytestDeprecationWarning, - match="Please pass an explicit Warning type or tuple of Warning types.", + match=r"Passing None to catch any warning has been deprecated, pass no arguments instead:\n Replace pytest.warns\(None\) by simply pytest.warns\(\).", ): - pytest.warns(None) + with pytest.warns(None): + pass diff --git a/testing/test_recwarn.py b/testing/test_recwarn.py index c73ab8a117f..963f3aef216 100644 --- a/testing/test_recwarn.py +++ b/testing/test_recwarn.py @@ -307,6 +307,7 @@ def test_record_only(self) -> None: assert str(record[1].message) == "runtime" def test_record_only_none_deprecated_warn(self) -> None: + # This should become an error when WARNS_NONE_ARG is removed in Pytest 7.0 with warnings.catch_warnings(): warnings.simplefilter("ignore") with pytest.warns(None) as record: From 24ad886b158a37cf9caf72a48eef1704361e1abf Mon Sep 17 00:00:00 2001 From: Olga Matoula Date: Sun, 16 May 2021 12:10:32 +0100 Subject: [PATCH 308/630] Remove the option to pass None in warns() --- src/_pytest/recwarn.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/_pytest/recwarn.py b/src/_pytest/recwarn.py index 7156272097f..4b61db4968a 100644 --- a/src/_pytest/recwarn.py +++ b/src/_pytest/recwarn.py @@ -84,7 +84,7 @@ def deprecated_call( @overload def warns( - expected_warning: Optional[Union[Type[Warning], Tuple[Type[Warning], ...]]] = ..., + expected_warning: Union[Type[Warning], Tuple[Type[Warning], ...]] = ..., *, match: Optional[Union[str, Pattern[str]]] = ..., ) -> "WarningsChecker": @@ -93,7 +93,7 @@ def warns( @overload def warns( - expected_warning: Optional[Union[Type[Warning], Tuple[Type[Warning], ...]]], + expected_warning: Union[Type[Warning], Tuple[Type[Warning], ...]], func: Callable[..., T], *args: Any, **kwargs: Any, @@ -102,9 +102,7 @@ def warns( def warns( - expected_warning: Optional[ - Union[Type[Warning], Tuple[Type[Warning], ...]] - ] = Warning, + expected_warning: Union[Type[Warning], Tuple[Type[Warning], ...]] = Warning, *args: Any, match: Optional[Union[str, Pattern[str]]] = None, **kwargs: Any, From 2414d23c7803b131d6c8016f5c34579148bc70e4 Mon Sep 17 00:00:00 2001 From: Olga Matoula Date: Sun, 16 May 2021 13:44:56 +0100 Subject: [PATCH 309/630] Remove default arg from overloaded warns --- src/_pytest/recwarn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_pytest/recwarn.py b/src/_pytest/recwarn.py index 4b61db4968a..4feaa3bbb5b 100644 --- a/src/_pytest/recwarn.py +++ b/src/_pytest/recwarn.py @@ -84,7 +84,7 @@ def deprecated_call( @overload def warns( - expected_warning: Union[Type[Warning], Tuple[Type[Warning], ...]] = ..., + expected_warning: Union[Type[Warning], Tuple[Type[Warning], ...]], *, match: Optional[Union[str, Pattern[str]]] = ..., ) -> "WarningsChecker": From c73e354019328df567e43605548542441e065bdd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 May 2021 03:01:15 +0000 Subject: [PATCH 310/630] Bump pytest-cov from 2.11.1 to 2.12.0 in /testing/plugins_integration Bumps [pytest-cov](https://github.com/pytest-dev/pytest-cov) from 2.11.1 to 2.12.0. - [Release notes](https://github.com/pytest-dev/pytest-cov/releases) - [Changelog](https://github.com/pytest-dev/pytest-cov/blob/master/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest-cov/compare/v2.11.1...v2.12.0) Signed-off-by: dependabot[bot] --- testing/plugins_integration/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/plugins_integration/requirements.txt b/testing/plugins_integration/requirements.txt index 97efaced769..be17e911825 100644 --- a/testing/plugins_integration/requirements.txt +++ b/testing/plugins_integration/requirements.txt @@ -2,7 +2,7 @@ anyio[curio,trio]==3.0.1 django==3.2.2 pytest-asyncio==0.15.1 pytest-bdd==4.0.2 -pytest-cov==2.11.1 +pytest-cov==2.12.0 pytest-django==4.2.0 pytest-flakes==4.0.3 pytest-html==3.1.1 From 06718da5d6b5199c312add36b25ef5cb10d4b567 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 May 2021 03:01:19 +0000 Subject: [PATCH 311/630] Bump django from 3.2.2 to 3.2.3 in /testing/plugins_integration Bumps [django](https://github.com/django/django) from 3.2.2 to 3.2.3. - [Release notes](https://github.com/django/django/releases) - [Commits](https://github.com/django/django/compare/3.2.2...3.2.3) Signed-off-by: dependabot[bot] --- testing/plugins_integration/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/plugins_integration/requirements.txt b/testing/plugins_integration/requirements.txt index 97efaced769..89e9dc08017 100644 --- a/testing/plugins_integration/requirements.txt +++ b/testing/plugins_integration/requirements.txt @@ -1,5 +1,5 @@ anyio[curio,trio]==3.0.1 -django==3.2.2 +django==3.2.3 pytest-asyncio==0.15.1 pytest-bdd==4.0.2 pytest-cov==2.11.1 From dd8ad3fa9c9862037e244f8460d0e726f7678779 Mon Sep 17 00:00:00 2001 From: Olga Matoula Date: Mon, 17 May 2021 09:23:08 +0100 Subject: [PATCH 312/630] Split warns matching string in multiple lines --- testing/deprecated_test.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/testing/deprecated_test.py b/testing/deprecated_test.py index 0974cf6c646..027a773b894 100644 --- a/testing/deprecated_test.py +++ b/testing/deprecated_test.py @@ -183,7 +183,10 @@ def test_hookproxy_warnings_for_fspath(tmp_path, hooktype, request): def test_warns_none_is_deprecated(): with pytest.warns( PytestDeprecationWarning, - match=r"Passing None to catch any warning has been deprecated, pass no arguments instead:\n Replace pytest.warns\(None\) by simply pytest.warns\(\).", + match=re.escape( + "Passing None to catch any warning has been deprecated, pass no arguments instead:\n " + "Replace pytest.warns(None) by simply pytest.warns()." + ), ): with pytest.warns(None): pass From 3f414d7bbe82e6d80a7e54c4798d676c8207dc77 Mon Sep 17 00:00:00 2001 From: Olga Matoula Date: Mon, 17 May 2021 09:50:59 +0100 Subject: [PATCH 313/630] Ignore depredcated warns(None) overload errors from mypy --- src/_pytest/recwarn.py | 2 +- testing/deprecated_test.py | 2 +- testing/test_recwarn.py | 2 +- testing/test_tmpdir.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/_pytest/recwarn.py b/src/_pytest/recwarn.py index 4feaa3bbb5b..4b61db4968a 100644 --- a/src/_pytest/recwarn.py +++ b/src/_pytest/recwarn.py @@ -84,7 +84,7 @@ def deprecated_call( @overload def warns( - expected_warning: Union[Type[Warning], Tuple[Type[Warning], ...]], + expected_warning: Union[Type[Warning], Tuple[Type[Warning], ...]] = ..., *, match: Optional[Union[str, Pattern[str]]] = ..., ) -> "WarningsChecker": diff --git a/testing/deprecated_test.py b/testing/deprecated_test.py index 027a773b894..479f1f26df8 100644 --- a/testing/deprecated_test.py +++ b/testing/deprecated_test.py @@ -188,5 +188,5 @@ def test_warns_none_is_deprecated(): "Replace pytest.warns(None) by simply pytest.warns()." ), ): - with pytest.warns(None): + with pytest.warns(None): # type: ignore[call-overload] pass diff --git a/testing/test_recwarn.py b/testing/test_recwarn.py index 963f3aef216..40a2e23fa6c 100644 --- a/testing/test_recwarn.py +++ b/testing/test_recwarn.py @@ -310,7 +310,7 @@ def test_record_only_none_deprecated_warn(self) -> None: # This should become an error when WARNS_NONE_ARG is removed in Pytest 7.0 with warnings.catch_warnings(): warnings.simplefilter("ignore") - with pytest.warns(None) as record: + with pytest.warns(None) as record: # type: ignore[call-overload] warnings.warn("user", UserWarning) warnings.warn("runtime", RuntimeWarning) diff --git a/testing/test_tmpdir.py b/testing/test_tmpdir.py index fe4b8b8cd13..4dff9dff00f 100644 --- a/testing/test_tmpdir.py +++ b/testing/test_tmpdir.py @@ -403,7 +403,7 @@ def test_on_rm_rf_error(self, tmp_path: Path) -> None: # ignored function with warnings.catch_warnings(): warnings.simplefilter("ignore") - with pytest.warns(None) as warninfo: + with pytest.warns(None) as warninfo: # type: ignore[call-overload] exc_info4 = (None, PermissionError(), None) on_rm_rf_error(os.open, str(fn), exc_info4, start_path=tmp_path) assert fn.is_file() From 8872e8e7c7515f33d87b483c687bdba48211aa52 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 May 2021 10:51:47 +0000 Subject: [PATCH 314/630] Bump pytest-django from 4.2.0 to 4.3.0 in /testing/plugins_integration Bumps [pytest-django](https://github.com/pytest-dev/pytest-django) from 4.2.0 to 4.3.0. - [Release notes](https://github.com/pytest-dev/pytest-django/releases) - [Changelog](https://github.com/pytest-dev/pytest-django/blob/master/docs/changelog.rst) - [Commits](https://github.com/pytest-dev/pytest-django/compare/v4.2.0...v4.3.0) Signed-off-by: dependabot[bot] --- testing/plugins_integration/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/plugins_integration/requirements.txt b/testing/plugins_integration/requirements.txt index 56eb13c578b..0b21ba026df 100644 --- a/testing/plugins_integration/requirements.txt +++ b/testing/plugins_integration/requirements.txt @@ -3,7 +3,7 @@ django==3.2.3 pytest-asyncio==0.15.1 pytest-bdd==4.0.2 pytest-cov==2.12.0 -pytest-django==4.2.0 +pytest-django==4.3.0 pytest-flakes==4.0.3 pytest-html==3.1.1 pytest-mock==3.6.0 From 53a74feea7ab558ec52fbc1706ea202ff05bc4f4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 May 2021 10:53:38 +0000 Subject: [PATCH 315/630] Bump pytest-mock from 3.6.0 to 3.6.1 in /testing/plugins_integration Bumps [pytest-mock](https://github.com/pytest-dev/pytest-mock) from 3.6.0 to 3.6.1. - [Release notes](https://github.com/pytest-dev/pytest-mock/releases) - [Changelog](https://github.com/pytest-dev/pytest-mock/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest-mock/compare/v3.6.0...v3.6.1) Signed-off-by: dependabot[bot] --- testing/plugins_integration/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/plugins_integration/requirements.txt b/testing/plugins_integration/requirements.txt index 0b21ba026df..fc9fc196691 100644 --- a/testing/plugins_integration/requirements.txt +++ b/testing/plugins_integration/requirements.txt @@ -6,7 +6,7 @@ pytest-cov==2.12.0 pytest-django==4.3.0 pytest-flakes==4.0.3 pytest-html==3.1.1 -pytest-mock==3.6.0 +pytest-mock==3.6.1 pytest-rerunfailures==9.1.1 pytest-sugar==0.9.4 pytest-trio==0.7.0 From 249c5b37dd0bf341eda8ffcae256ced2efa2f94d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 17 May 2021 17:29:57 +0000 Subject: [PATCH 316/630] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/pre-commit-hooks: v3.4.0 → v4.0.1](https://github.com/pre-commit/pre-commit-hooks/compare/v3.4.0...v4.0.1) - [github.com/asottile/pyupgrade: v2.15.0 → v2.16.0](https://github.com/asottile/pyupgrade/compare/v2.15.0...v2.16.0) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 46d6e3b4c52..6ac7fcc3db3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,7 +10,7 @@ repos: - id: blacken-docs additional_dependencies: [black==20.8b1] - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v3.4.0 + rev: v4.0.1 hooks: - id: trailing-whitespace - id: end-of-file-fixer @@ -34,7 +34,7 @@ repos: - id: reorder-python-imports args: ['--application-directories=.:src', --py36-plus] - repo: https://github.com/asottile/pyupgrade - rev: v2.15.0 + rev: v2.16.0 hooks: - id: pyupgrade args: [--py36-plus] From e2567a680e6327153769ec61c2a9a486d28dbf23 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sun, 16 May 2021 11:12:23 +0300 Subject: [PATCH 317/630] scripts: improve upload-coverage.sh Mostly, verify the bash uploader hash and make it more strict and verbose. --- scripts/upload-coverage.sh | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/scripts/upload-coverage.sh b/scripts/upload-coverage.sh index ad3dd482814..089fb67bfe0 100755 --- a/scripts/upload-coverage.sh +++ b/scripts/upload-coverage.sh @@ -1,16 +1,28 @@ #!/usr/bin/env bash -set -e +set -euo pipefail set -x -if [ -z "$TOXENV" ]; then +# Install coverage. +if [[ -z ${TOXENV+x} || -z $TOXENV ]]; then python -m pip install coverage else # Add last TOXENV to $PATH. PATH="$PWD/.tox/${TOXENV##*,}/bin:$PATH" fi +# Run coverage. python -m coverage xml + +# Download and verify latest Codecov bash uploader. # Set --connect-timeout to work around https://github.com/curl/curl/issues/4461 -curl -S -L --connect-timeout 5 --retry 6 -s https://codecov.io/bash -o codecov-upload.sh -bash codecov-upload.sh -Z -X fix -f coverage.xml "$@" +curl --silent --show-error --location --connect-timeout 5 --retry 6 -o codecov https://codecov.io/bash +VERSION=$(grep --only-matching 'VERSION=\"[0-9\.]*\"' codecov | cut -d'"' -f2) +if command -v sha256sum; then + sha256sum --check --strict --ignore-missing --quiet <(curl --silent "https://raw.githubusercontent.com/codecov/codecov-bash/${VERSION}/SHA256SUM") +else + shasum --algorithm 256 --check --strict --ignore-missing --quiet <(curl --silent "https://raw.githubusercontent.com/codecov/codecov-bash/${VERSION}/SHA256SUM") +fi + +# Upload coverage. +bash codecov -Z -X fix -f coverage.xml "$@" From 6bc6ec323af51605c4503afa00239c32867435b8 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sun, 16 May 2021 11:17:05 +0300 Subject: [PATCH 318/630] ci: reduce workflow permissions Decrease security exposure by restricting what the code executing in the actions is allowed to do (in terms of GitHub operations). --- .github/workflows/main.yml | 27 ++++++++++++++++++++++++ .github/workflows/prepare-release-pr.yml | 6 ++++++ .github/workflows/release-on-comment.yml | 8 +++++++ .github/workflows/update-plugin-list.yml | 13 ++++++++++++ 4 files changed, 54 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 756aade8c02..4c390b4522a 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -13,13 +13,19 @@ on: branches: - main - "[0-9]+.[0-9]+.x" + env: PYTEST_ADDOPTS: "--color=yes" +# Set permissions at the job level. +permissions: {} + jobs: build: runs-on: ${{ matrix.os }} timeout-minutes: 30 + permissions: + contents: read strategy: fail-fast: false @@ -139,10 +145,13 @@ jobs: - uses: actions/checkout@v2 with: fetch-depth: 0 + persist-credentials: false + - name: Set up Python ${{ matrix.python }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.python }} + - name: Install dependencies run: | python -m pip install --upgrade pip @@ -164,19 +173,29 @@ jobs: linting: runs-on: ubuntu-latest + permissions: + contents: read + steps: - uses: actions/checkout@v2 + with: + persist-credentials: false + - uses: actions/setup-python@v2 + - name: set PY run: echo "name=PY::$(python -c 'import hashlib, sys;print(hashlib.sha256(sys.version.encode()+sys.executable.encode()).hexdigest())')" >> $GITHUB_ENV + - uses: actions/cache@v2 with: path: ~/.cache/pre-commit key: pre-commit|${{ env.PY }}|${{ hashFiles('.pre-commit-config.yaml') }} + - name: Install dependencies run: | python -m pip install --upgrade pip pip install tox + - run: tox -e linting deploy: @@ -184,6 +203,8 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 30 + permissions: + contents: read needs: [build] @@ -191,22 +212,28 @@ jobs: - uses: actions/checkout@v2 with: fetch-depth: 0 + persist-credentials: false + - name: Set up Python uses: actions/setup-python@v2 with: python-version: "3.7" + - name: Install dependencies run: | python -m pip install --upgrade pip pip install --upgrade wheel setuptools tox + - name: Build package run: | python setup.py sdist bdist_wheel + - name: Publish package to PyPI uses: pypa/gh-action-pypi-publish@master with: user: __token__ password: ${{ secrets.pypi_token }} + - name: Publish GitHub release notes env: GH_RELEASE_NOTES_TOKEN: ${{ secrets.release_notes }} diff --git a/.github/workflows/prepare-release-pr.yml b/.github/workflows/prepare-release-pr.yml index dec35236430..7a610638a74 100644 --- a/.github/workflows/prepare-release-pr.yml +++ b/.github/workflows/prepare-release-pr.yml @@ -12,14 +12,20 @@ on: required: true default: 'no' +# Set permissions at the job level. +permissions: {} + jobs: build: runs-on: ubuntu-latest + permissions: + contents: read steps: - uses: actions/checkout@v2 with: fetch-depth: 0 + persist-credentials: false - name: Set up Python uses: actions/setup-python@v2 diff --git a/.github/workflows/release-on-comment.yml b/.github/workflows/release-on-comment.yml index 94863d896b9..cca908aa6ae 100644 --- a/.github/workflows/release-on-comment.yml +++ b/.github/workflows/release-on-comment.yml @@ -7,9 +7,14 @@ on: issue_comment: types: [created, edited] +# Set permissions at the job level. +permissions: {} + jobs: build: runs-on: ubuntu-latest + permissions: + contents: read if: (github.event.comment && startsWith(github.event.comment.body, '@pytestbot please')) || (github.event.issue && !github.event.comment && startsWith(github.event.issue.body, '@pytestbot please')) @@ -17,15 +22,18 @@ jobs: - uses: actions/checkout@v2 with: fetch-depth: 0 + persist-credentials: false - name: Set up Python uses: actions/setup-python@v2 with: python-version: "3.8" + - name: Install dependencies run: | python -m pip install --upgrade pip pip install --upgrade setuptools tox + - name: Prepare release run: | tox -e release-on-comment -- $GITHUB_EVENT_PATH ${{ secrets.chatops }} diff --git a/.github/workflows/update-plugin-list.yml b/.github/workflows/update-plugin-list.yml index 9b071aa3d4f..d5a6efb5406 100644 --- a/.github/workflows/update-plugin-list.yml +++ b/.github/workflows/update-plugin-list.yml @@ -7,22 +7,35 @@ on: - cron: '0 0 * * 0' workflow_dispatch: +# Set permissions at the job level. +permissions: {} + jobs: createPullRequest: runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + steps: - name: Checkout uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Setup Python uses: actions/setup-python@v2 with: python-version: 3.8 + - name: Install dependencies run: | python -m pip install --upgrade pip pip install packaging requests tabulate[widechars] + - name: Update Plugin List run: python scripts/update-plugin-list.py + - name: Create Pull Request uses: peter-evans/create-pull-request@2455e1596942c2902952003bbb574afbbe2ab2e6 with: From 864a2bc0a3180efa76de0e9b1ee28b81c6f437bb Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sun, 16 May 2021 11:37:21 +0300 Subject: [PATCH 319/630] ci: use GitHub Actions token instead of `chatops`/`release_notes` secrets It seems more secure to use the controlled & limited token than an ambient secret. --- .github/workflows/main.yml | 4 ++-- .github/workflows/prepare-release-pr.yml | 7 ++++--- .github/workflows/release-on-comment.yml | 5 +++-- scripts/prepare-release-pr.py | 5 ++--- scripts/release-on-comment.py | 4 ++-- 5 files changed, 13 insertions(+), 12 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4c390b4522a..c58a50ff062 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -204,7 +204,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 30 permissions: - contents: read + contents: write needs: [build] @@ -236,7 +236,7 @@ jobs: - name: Publish GitHub release notes env: - GH_RELEASE_NOTES_TOKEN: ${{ secrets.release_notes }} + GH_RELEASE_NOTES_TOKEN: ${{ github.token }} run: | sudo apt-get install pandoc tox -e publish-gh-release-notes diff --git a/.github/workflows/prepare-release-pr.yml b/.github/workflows/prepare-release-pr.yml index 7a610638a74..91977f5b257 100644 --- a/.github/workflows/prepare-release-pr.yml +++ b/.github/workflows/prepare-release-pr.yml @@ -19,7 +19,8 @@ jobs: build: runs-on: ubuntu-latest permissions: - contents: read + contents: write + pull-requests: write steps: - uses: actions/checkout@v2 @@ -40,9 +41,9 @@ jobs: - name: Prepare release PR (minor/patch release) if: github.event.inputs.major == 'no' run: | - tox -e prepare-release-pr -- ${{ github.event.inputs.branch }} ${{ secrets.chatops }} + tox -e prepare-release-pr -- ${{ github.event.inputs.branch }} ${{ github.token }} - name: Prepare release PR (major release) if: github.event.inputs.major == 'yes' run: | - tox -e prepare-release-pr -- ${{ github.event.inputs.branch }} ${{ secrets.chatops }} --major + tox -e prepare-release-pr -- ${{ github.event.inputs.branch }} ${{ github.token }} --major diff --git a/.github/workflows/release-on-comment.yml b/.github/workflows/release-on-comment.yml index cca908aa6ae..32d22155223 100644 --- a/.github/workflows/release-on-comment.yml +++ b/.github/workflows/release-on-comment.yml @@ -14,7 +14,8 @@ jobs: build: runs-on: ubuntu-latest permissions: - contents: read + contents: write + issues: write if: (github.event.comment && startsWith(github.event.comment.body, '@pytestbot please')) || (github.event.issue && !github.event.comment && startsWith(github.event.issue.body, '@pytestbot please')) @@ -36,4 +37,4 @@ jobs: - name: Prepare release run: | - tox -e release-on-comment -- $GITHUB_EVENT_PATH ${{ secrets.chatops }} + tox -e release-on-comment -- $GITHUB_EVENT_PATH ${{ github.token }} diff --git a/scripts/prepare-release-pr.py b/scripts/prepare-release-pr.py index 5ba174f23ff..ca5ed411aec 100644 --- a/scripts/prepare-release-pr.py +++ b/scripts/prepare-release-pr.py @@ -9,9 +9,8 @@ After that, it will create a release using the `release` tox environment, and push a new PR. -**Secret**: currently the secret is defined in the @pytestbot account, -which the core maintainers have access to. There we created a new secret named `chatops` -with write access to the repository. +**Token**: currently the token from the GitHub Actions is used, pushed with +`pytest bot ` commit author. """ import argparse import re diff --git a/scripts/release-on-comment.py b/scripts/release-on-comment.py index f33def3efc7..d5996aa4086 100644 --- a/scripts/release-on-comment.py +++ b/scripts/release-on-comment.py @@ -23,8 +23,8 @@ After that, it will create a release using the `release` tox environment, and push a new PR. -**Secret**: currently the secret is defined in the @pytestbot account, which the core maintainers -have access to. There we created a new secret named `chatops` with write access to the repository. +**Token**: currently the token from the GitHub Actions is used, pushed with +`pytest bot ` commit author. """ import argparse import json From 11538e35e45ce31a0ea43ce39968aa397fc5a790 Mon Sep 17 00:00:00 2001 From: pytest bot Date: Sun, 23 May 2021 00:12:33 +0000 Subject: [PATCH 320/630] [automated] Update plugin list --- doc/en/reference/plugin_list.rst | 37 ++++++++++++++++---------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/doc/en/reference/plugin_list.rst b/doc/en/reference/plugin_list.rst index 45b9fe672db..c6facee7560 100644 --- a/doc/en/reference/plugin_list.rst +++ b/doc/en/reference/plugin_list.rst @@ -6,16 +6,17 @@ Plugin List PyPI projects that match "pytest-\*" are considered plugins and are listed automatically. Packages classified as inactive are excluded. -This list contains 867 plugins. +This list contains 868 plugins. ============================================================================================================== ======================================================================================================================================================================== ============== ===================== ================================================ name summary last release status requires ============================================================================================================== ======================================================================================================================================================================== ============== ===================== ================================================ +`pytest-accept `_ A pytest-plugin for updating doctest outputs May 18, 2021 N/A pytest (>=6,<7) `pytest-adaptavist `_ pytest plugin for generating test execution results within Jira Test Management (tm4j) Apr 22, 2021 N/A pytest (>=3.4.1) `pytest-adf `_ Pytest plugin for writing Azure Data Factory integration tests May 10, 2021 4 - Beta pytest (>=3.5.0) `pytest-adf-azure-identity `_ Pytest plugin for writing Azure Data Factory integration tests Mar 06, 2021 4 - Beta pytest (>=3.5.0) `pytest-aggreport `_ pytest plugin for pytest-repeat that generate aggregate report of the same test cases with additional statistics details. Mar 07, 2021 4 - Beta pytest (>=6.2.2) -`pytest-aio `_ Pytest plugin for testing async python code Apr 05, 2021 4 - Beta pytest ; extra == 'tests' +`pytest-aio `_ Pytest plugin for testing async python code May 21, 2021 4 - Beta pytest ; extra == 'tests' `pytest-aiofiles `_ pytest fixtures for writing aiofiles tests with pyfakefs May 14, 2017 5 - Production/Stable N/A `pytest-aiohttp `_ pytest plugin for aiohttp support Dec 05, 2017 N/A pytest `pytest-aiohttp-client `_ Pytest `client` fixture for the Aiohttp Nov 01, 2020 N/A pytest (>=6) @@ -104,7 +105,7 @@ name `pytest-canonical-data `_ A plugin which allows to compare results with canonical results, based on previous runs May 08, 2020 2 - Pre-Alpha pytest (>=3.5.0) `pytest-caprng `_ A plugin that replays pRNG state on failure. May 02, 2018 4 - Beta N/A `pytest-capture-deprecatedwarnings `_ pytest plugin to capture all deprecatedwarnings and put them in one file Apr 30, 2019 N/A N/A -`pytest-cases `_ Separate test code from test cases in pytest. May 07, 2021 5 - Production/Stable N/A +`pytest-cases `_ Separate test code from test cases in pytest. May 21, 2021 5 - Production/Stable N/A `pytest-cassandra `_ Cassandra CCM Test Fixtures for pytest Nov 04, 2017 1 - Planning N/A `pytest-catchlog `_ py.test plugin to catch log messages. This is a fork of pytest-capturelog. Jan 24, 2016 4 - Beta pytest (>=2.6) `pytest-catch-server `_ Pytest plugin with server for catching HTTP requests. Dec 12, 2019 5 - Production/Stable N/A @@ -128,7 +129,7 @@ name `pytest-cloud `_ Distributed tests planner plugin for pytest testing framework. Oct 05, 2020 6 - Mature N/A `pytest-cloudflare-worker `_ pytest plugin for testing cloudflare workers Mar 30, 2021 4 - Beta pytest (>=6.0.0) `pytest-cobra `_ PyTest plugin for testing Smart Contracts for Ethereum blockchain. Jun 29, 2019 3 - Alpha pytest (<4.0.0,>=3.7.1) -`pytest-codeblocks `_ Test code blocks in your READMEs May 10, 2021 4 - Beta pytest (>=6) +`pytest-codeblocks `_ Test code blocks in your READMEs May 18, 2021 4 - Beta pytest (>=6) `pytest-codecheckers `_ pytest plugin to add source code sanity checks (pep8 and friends) Feb 13, 2010 N/A N/A `pytest-codecov `_ Pytest plugin for uploading pytest-cov results to codecov.io May 05, 2021 4 - Beta pytest (>=4.6.0) `pytest-codegen `_ Automatically create pytest test signatures Aug 23, 2020 2 - Pre-Alpha N/A @@ -144,7 +145,7 @@ name `pytest-console-scripts `_ Pytest plugin for testing console scripts Apr 26, 2021 4 - Beta N/A `pytest-consul `_ pytest plugin with fixtures for testing consul aware apps Nov 24, 2018 3 - Alpha pytest `pytest-contextfixture `_ Define pytest fixtures as context managers. Mar 12, 2013 4 - Beta N/A -`pytest-contexts `_ A plugin to run tests written with the Contexts framework using pytest Jul 23, 2018 4 - Beta N/A +`pytest-contexts `_ A plugin to run tests written with the Contexts framework using pytest May 19, 2021 4 - Beta N/A `pytest-cookies `_ The pytest plugin for your Cookiecutter templates. 🍪 Feb 14, 2020 5 - Production/Stable pytest (<6.0.0,>=3.3.0) `pytest-couchdbkit `_ py.test extension for per-test couchdb databases using couchdbkit Apr 17, 2012 N/A N/A `pytest-count `_ count erros and send email Jan 12, 2018 4 - Beta N/A @@ -413,7 +414,7 @@ name `pytest-joke `_ Test failures are better served with humor. Oct 08, 2019 4 - Beta pytest (>=4.2.1) `pytest-json `_ Generate JSON test reports Jan 18, 2016 4 - Beta N/A `pytest-jsonlint `_ UNKNOWN Aug 04, 2016 N/A N/A -`pytest-json-report `_ A pytest plugin to report test results as JSON files Oct 23, 2020 4 - Beta pytest (>=4.2.0) +`pytest-json-report `_ A pytest plugin to report test results as JSON files May 17, 2021 4 - Beta pytest (>=3.8.0) `pytest-kafka `_ Zookeeper, Kafka server, and Kafka consumer fixtures for Pytest Nov 01, 2019 N/A pytest `pytest-kind `_ Kubernetes test support with KIND for pytest Jan 24, 2021 5 - Production/Stable N/A `pytest-kivy `_ Kivy GUI tests fixtures using pytest Mar 20, 2021 4 - Beta pytest (>=3.6) @@ -499,7 +500,7 @@ name `pytest-mutagen `_ Add the mutation testing feature to pytest Jul 24, 2020 N/A pytest (>=5.4) `pytest-mypy `_ Mypy static type checker plugin for Pytest Mar 21, 2021 4 - Beta pytest (>=3.5) `pytest-mypyd `_ Mypy static type checker plugin for Pytest Aug 20, 2019 4 - Beta pytest (<4.7,>=2.8) ; python_version < "3.5" -`pytest-mypy-plugins `_ pytest plugin for writing tests for mypy plugins Oct 26, 2020 3 - Alpha pytest (>=6.0.0) +`pytest-mypy-plugins `_ pytest plugin for writing tests for mypy plugins May 22, 2021 3 - Alpha pytest (>=6.0.0) `pytest-mypy-plugins-shim `_ Substitute for "pytest-mypy-plugins" for Python implementations which aren't supported by mypy. Apr 12, 2021 N/A N/A `pytest-mypy-testing `_ Pytest plugin to check mypy output. Apr 24, 2020 N/A pytest `pytest-mysql `_ MySQL process and client fixtures for pytest Jul 21, 2020 5 - Production/Stable pytest (>=3.0.0) @@ -568,7 +569,7 @@ name `pytest-platform-markers `_ Markers for pytest to skip tests on specific platforms Sep 09, 2019 4 - Beta pytest (>=3.6.0) `pytest-play `_ pytest plugin that let you automate actions and assertions with test metrics reporting executing plain YAML files Jun 12, 2019 5 - Production/Stable N/A `pytest-playbook `_ Pytest plugin for reading playbooks. Jan 21, 2021 3 - Alpha pytest (>=6.1.2,<7.0.0) -`pytest-playwright `_ A pytest wrapper with fixtures for Playwright to automate web browsers Apr 01, 2021 N/A pytest +`pytest-playwright `_ A pytest wrapper with fixtures for Playwright to automate web browsers May 19, 2021 N/A pytest `pytest-plt `_ Fixtures for quickly making Matplotlib plots in tests Aug 17, 2020 5 - Production/Stable pytest `pytest-plugin-helpers `_ A plugin to help developing and testing other plugins Nov 23, 2019 4 - Beta pytest (>=3.5.0) `pytest-plus `_ PyTest Plus Plugin :: extends pytest functionality Mar 19, 2020 5 - Production/Stable pytest (>=3.50) @@ -583,7 +584,7 @@ name `pytest-pop `_ A pytest plugin to help with testing pop projects May 05, 2021 5 - Production/Stable pytest `pytest-portion `_ Select a portion of the collected tests Jan 28, 2021 4 - Beta pytest (>=3.5.0) `pytest-postgres `_ Run PostgreSQL in Docker container in Pytest. Mar 22, 2020 N/A pytest -`pytest-postgresql `_ Postgresql fixtures and fixture factories for Pytest. May 05, 2021 5 - Production/Stable pytest (>=3.0.0) +`pytest-postgresql `_ Postgresql fixtures and fixture factories for Pytest. May 19, 2021 5 - Production/Stable pytest (>=3.0.0) `pytest-power `_ pytest plugin with powerful fixtures Dec 31, 2020 N/A pytest (>=5.4) `pytest-pride `_ Minitest-style test colors Apr 02, 2016 3 - Alpha N/A `pytest-print `_ pytest-print adds the printer fixture you can use to print messages to the user (directly to the pytest runner, not stdout) Oct 23, 2020 5 - Production/Stable pytest (>=3.0.0) @@ -628,13 +629,13 @@ name `pytest-reana `_ Pytest fixtures for REANA. May 03, 2021 3 - Alpha N/A `pytest-recording `_ A pytest plugin that allows you recording of network interactions via VCR.py Nov 25, 2020 4 - Beta pytest (>=3.5.0) `pytest-recordings `_ Provides pytest plugins for reporting request/response traffic, screenshots, and more to ReportPortal Aug 13, 2020 N/A N/A -`pytest-redis `_ Redis fixtures and fixture factories for Pytest. Oct 15, 2019 5 - Production/Stable pytest (>=3.0.0) +`pytest-redis `_ Redis fixtures and fixture factories for Pytest. May 18, 2021 5 - Production/Stable pytest `pytest-redmine `_ Pytest plugin for redmine Mar 19, 2018 1 - Planning N/A `pytest-ref `_ A plugin to store reference files to ease regression testing Nov 23, 2019 4 - Beta pytest (>=3.5.0) `pytest-reference-formatter `_ Conveniently run pytest with a dot-formatted test reference. Oct 01, 2019 4 - Beta N/A `pytest-regressions `_ Easy to use fixtures to write regression tests. Jan 27, 2021 5 - Production/Stable pytest (>=3.5.0) `pytest-regtest `_ pytest plugin for regression tests Sep 16, 2020 N/A N/A -`pytest-relative-order `_ a pytest plugin that sorts tests using "before" and "after" markers May 04, 2021 4 - Beta N/A +`pytest-relative-order `_ a pytest plugin that sorts tests using "before" and "after" markers May 17, 2021 4 - Beta N/A `pytest-relaxed `_ Relaxed test discovery/organization for pytest Jun 14, 2019 5 - Production/Stable pytest (<5,>=3) `pytest-remfiles `_ Pytest plugin to create a temporary directory with remote files Jul 01, 2019 5 - Production/Stable N/A `pytest-remotedata `_ Pytest plugin for controlling remote data access. Jul 20, 2019 3 - Alpha pytest (>=3.1) @@ -672,25 +673,25 @@ name `pytest-rotest `_ Pytest integration with rotest Sep 08, 2019 N/A pytest (>=3.5.0) `pytest-rpc `_ Extend py.test for RPC OpenStack testing. Feb 22, 2019 4 - Beta pytest (~=3.6) `pytest-rt `_ pytest data collector plugin for Testgr Mar 03, 2021 N/A N/A -`pytest-rts `_ Coverage-based regression test selection (RTS) plugin for pytest Mar 31, 2021 N/A pytest +`pytest-rts `_ Coverage-based regression test selection (RTS) plugin for pytest May 17, 2021 N/A pytest `pytest-run-changed `_ Pytest plugin that runs changed tests only Apr 02, 2021 3 - Alpha pytest `pytest-runfailed `_ implement a --failed option for pytest Mar 24, 2016 N/A N/A -`pytest-runner `_ Invoke py.test as distutils command with dependency resolution Feb 12, 2021 5 - Production/Stable pytest (!=3.7.3,>=3.5) ; extra == 'testing' +`pytest-runner `_ Invoke py.test as distutils command with dependency resolution May 19, 2021 5 - Production/Stable pytest (>=4.6) ; extra == 'testing' `pytest-salt `_ Pytest Salt Plugin Jan 27, 2020 4 - Beta N/A `pytest-salt-containers `_ A Pytest plugin that builds and creates docker containers Nov 09, 2016 4 - Beta N/A `pytest-salt-factories `_ Pytest Salt Plugin May 06, 2021 4 - Beta pytest (>=6.0.0) `pytest-salt-from-filenames `_ Simple PyTest Plugin For Salt's Test Suite Specifically Jan 29, 2019 4 - Beta pytest (>=4.1) `pytest-salt-runtests-bridge `_ Simple PyTest Plugin For Salt's Test Suite Specifically Dec 05, 2019 4 - Beta pytest (>=4.1) -`pytest-sanic `_ a pytest plugin for Sanic Feb 27, 2021 N/A pytest (>=5.2) +`pytest-sanic `_ a pytest plugin for Sanic May 20, 2021 N/A pytest (>=5.2) `pytest-sanity `_ Dec 07, 2020 N/A N/A `pytest-sa-pg `_ May 14, 2019 N/A N/A -`pytest-sbase `_ A complete web automation framework for end-to-end testing. May 12, 2021 5 - Production/Stable N/A +`pytest-sbase `_ A complete web automation framework for end-to-end testing. May 22, 2021 5 - Production/Stable N/A `pytest-scenario `_ pytest plugin for test scenarios Feb 06, 2017 3 - Alpha N/A `pytest-schema `_ 👍 Validate return values against a schema-like object in testing Aug 31, 2020 5 - Production/Stable pytest (>=3.5.0) `pytest-securestore `_ An encrypted password store for use within pytest cases Jun 19, 2019 4 - Beta N/A `pytest-select `_ A pytest plugin which allows to (de-)select tests from a file. Jan 18, 2019 3 - Alpha pytest (>=3.0) `pytest-selenium `_ pytest plugin for Selenium Sep 19, 2020 5 - Production/Stable pytest (>=5.0.0) -`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. May 12, 2021 5 - Production/Stable N/A +`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. May 22, 2021 5 - Production/Stable N/A `pytest-selenium-enhancer `_ pytest plugin for Selenium Nov 26, 2020 5 - Production/Stable N/A `pytest-selenium-pdiff `_ A pytest package implementing perceptualdiff for Selenium tests. Apr 06, 2017 2 - Pre-Alpha N/A `pytest-send-email `_ Send pytest execution result email Dec 04, 2019 N/A N/A @@ -790,7 +791,7 @@ name `pytest-testrail-e2e `_ pytest plugin for creating TestRail runs and adding results Jun 11, 2020 N/A pytest (>=3.6) `pytest-testrail-plugin `_ PyTest plugin for TestRail Apr 21, 2020 3 - Alpha pytest `pytest-testrail-reporter `_ Sep 10, 2018 N/A N/A -`pytest-testreport `_ May 14, 2021 4 - Beta pytest (>=3.5.0) +`pytest-testreport `_ May 18, 2021 4 - Beta pytest (>=3.5.0) `pytest-testslide `_ TestSlide fixture for pytest Jan 07, 2021 5 - Production/Stable pytest (~=6.2) `pytest-test-this `_ Plugin for py.test to run relevant tests, based on naively checking if a test contains a reference to the symbol you supply Sep 15, 2019 2 - Pre-Alpha pytest (>=2.3) `pytest-tesults `_ Tesults plugin for pytest May 18, 2020 5 - Production/Stable pytest (>=3.5.0) @@ -832,7 +833,7 @@ name `pytest-unmarked `_ Run only unmarked tests Aug 27, 2019 5 - Production/Stable N/A `pytest-unordered `_ Test equality of unordered collections in pytest Mar 28, 2021 4 - Beta N/A `pytest-vagrant `_ A py.test plugin providing access to vagrant. Mar 23, 2020 5 - Production/Stable pytest -`pytest-valgrind `_ Mar 15, 2020 N/A N/A +`pytest-valgrind `_ May 19, 2021 N/A N/A `pytest-variables `_ pytest plugin for providing variables to tests/fixtures Oct 23, 2019 5 - Production/Stable pytest (>=2.4.2) `pytest-vcr `_ Plugin for managing VCR.py cassettes Apr 26, 2019 5 - Production/Stable pytest (>=3.6.0) `pytest-vcrpandas `_ Test from HTTP interactions to dataframe processed. Jan 12, 2019 4 - Beta pytest From c90fdc684b083a185523d487d51b35971978e270 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sun, 23 May 2021 20:47:56 +0300 Subject: [PATCH 321/630] code: remove unneeded comparison eval wrapper Given a `RecursionError` traceback, the `Traceback.recursionindex()` method returns the index of the frame which started the recursion (repeated set of frames). To do so it attempts to check whether two frames are equivalent. Just checking the function/line is not enough because the recursion variable(s) might differ (e.g. imagine the numeric value in a recursive factorial implementation). So it also compares the `f_locals` (local variables) of each frame for equivalence. For some reason, the locals comparison is wrapped in an `eval` whose purpose is to evaluate the comparison in one of the compared frame's context (locals + globals in scope). However, I can not think of any way in which the global scope could affect the evaluation. It would have an affect when the locals are bound but that's already done. So this seems unnecessary - remove it. --- src/_pytest/_code/code.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/_pytest/_code/code.py b/src/_pytest/_code/code.py index 992a4d735d5..d9a6b9edbc0 100644 --- a/src/_pytest/_code/code.py +++ b/src/_pytest/_code/code.py @@ -422,21 +422,12 @@ def recursionindex(self) -> Optional[int]: f = entry.frame loc = f.f_locals for otherloc in values: - if f.eval( - co_equal, - __recursioncache_locals_1=loc, - __recursioncache_locals_2=otherloc, - ): + if otherloc == loc: return i values.append(entry.frame.f_locals) return None -co_equal = compile( - "__recursioncache_locals_1 == __recursioncache_locals_2", "?", "eval" -) - - E = TypeVar("E", bound=BaseException, covariant=True) From 538b5c24999e9ebb4fab43faabc8bcc28737bcdf Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sun, 23 May 2021 23:45:49 +0300 Subject: [PATCH 322/630] argparsing: export Parser and OptionGroup for typing purposes `Parser` is used by many plugins and custom hooks. `OptionGroup` is exposed by the `parser.addgroup` API. The constructors of both are marked private, they are not meant to be constructed directly. --- changelog/7259.feature.rst | 2 +- changelog/7469.deprecation.rst | 2 ++ changelog/7469.feature.rst | 2 ++ changelog/8315.deprecation.rst | 2 +- doc/en/deprecations.rst | 2 +- doc/en/reference/reference.rst | 7 ++++++- src/_pytest/config/__init__.py | 5 +++-- src/_pytest/config/argparsing.py | 30 +++++++++++++++++++++--------- src/_pytest/hookspec.py | 8 ++++---- src/pytest/__init__.py | 4 ++++ testing/test_parseopt.py | 10 +++++----- 11 files changed, 50 insertions(+), 24 deletions(-) diff --git a/changelog/7259.feature.rst b/changelog/7259.feature.rst index 41a213f63a6..dd03b48969d 100644 --- a/changelog/7259.feature.rst +++ b/changelog/7259.feature.rst @@ -1,7 +1,7 @@ Added :meth:`cache.mkdir() `, which is similar to the existing :meth:`cache.makedir() `, but returns a :class:`pathlib.Path` instead of a legacy ``py.path.local``. -Added a ``paths`` type to :meth:`parser.addini() <_pytest.config.argparsing.Parser.addini>`, +Added a ``paths`` type to :meth:`parser.addini() `, as in ``parser.addini("mypaths", "my paths", type="paths")``, which is similar to the existing ``pathlist``, but returns a list of :class:`pathlib.Path` instead of legacy ``py.path.local``. diff --git a/changelog/7469.deprecation.rst b/changelog/7469.deprecation.rst index e01764caa91..3762306c736 100644 --- a/changelog/7469.deprecation.rst +++ b/changelog/7469.deprecation.rst @@ -6,5 +6,7 @@ Directly constructing the following classes is now deprecated: - ``_pytest.python.Metafunc`` - ``_pytest.runner.CallInfo`` - ``_pytest._code.ExceptionInfo`` +- ``_pytest.config.argparsing.Parser`` +- ``_pytest.config.argparsing.OptionGroup`` These have always been considered private, but now issue a deprecation warning, which may become a hard error in pytest 7.0.0. diff --git a/changelog/7469.feature.rst b/changelog/7469.feature.rst index ea8df523952..0feef2d7dc0 100644 --- a/changelog/7469.feature.rst +++ b/changelog/7469.feature.rst @@ -8,6 +8,8 @@ The newly-exported types are: - ``pytest.Metafunc`` for the :class:`metafunc ` argument to the :func:`pytest_generate_tests ` hook. - ``pytest.CallInfo`` for the :class:`CallInfo ` type passed to various hooks. - ``pytest.ExceptionInfo`` for the :class:`ExceptionInfo ` type returned from :func:`pytest.raises` and passed to various hooks. +- ``pytest.Parser`` for the :class:`Parser ` type passed to the :func:`pytest_addoption ` hook. +- ``pytest.OptionGroup`` for the :class:`OptionGroup ` type returned from the :func:`parser.addgroup ` method. Constructing them directly is not supported; they are only meant for use in type annotations. Doing so will emit a deprecation warning, and may become a hard-error in pytest 7.0. diff --git a/changelog/8315.deprecation.rst b/changelog/8315.deprecation.rst index 9b49d7c2f19..c30dcf5d13e 100644 --- a/changelog/8315.deprecation.rst +++ b/changelog/8315.deprecation.rst @@ -1,4 +1,4 @@ -Several behaviors of :meth:`Parser.addoption <_pytest.config.argparsing.Parser.addoption>` are now +Several behaviors of :meth:`Parser.addoption ` are now scheduled for removal in pytest 7 (deprecated since pytest 2.4.0): - ``parser.addoption(..., help=".. %default ..")`` - use ``%(default)s`` instead. diff --git a/doc/en/deprecations.rst b/doc/en/deprecations.rst index db04a48b369..c3020fb9498 100644 --- a/doc/en/deprecations.rst +++ b/doc/en/deprecations.rst @@ -48,7 +48,7 @@ Backward compatibilities in ``Parser.addoption`` .. deprecated:: 2.4 -Several behaviors of :meth:`Parser.addoption <_pytest.config.argparsing.Parser.addoption>` are now +Several behaviors of :meth:`Parser.addoption ` are now scheduled for removal in pytest 7 (deprecated since pytest 2.4.0): - ``parser.addoption(..., help=".. %default ..")`` - use ``%(default)s`` instead. diff --git a/doc/en/reference/reference.rst b/doc/en/reference/reference.rst index d4256bd6080..95bf6cf62fb 100644 --- a/doc/en/reference/reference.rst +++ b/doc/en/reference/reference.rst @@ -889,9 +889,14 @@ Node Parser ~~~~~~ -.. autoclass:: _pytest.config.argparsing.Parser() +.. autoclass:: pytest.Parser() :members: +OptionGroup +~~~~~~~~~~~ + +.. autoclass:: pytest.OptionGroup() + :members: PytestPluginManager ~~~~~~~~~~~~~~~~~~~ diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 7f18f62cb39..82bc37873ca 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -909,6 +909,7 @@ def __init__( self._parser = Parser( usage=f"%(prog)s [options] [{_a}] [{_a}] [...]", processopt=self._processopt, + _ispytest=True, ) self.pluginmanager = pluginmanager """The plugin manager handles plugin registration and hook invocation. @@ -1380,8 +1381,8 @@ def getini(self, name: str): """Return configuration value from an :ref:`ini file `. If the specified name hasn't been registered through a prior - :py:func:`parser.addini <_pytest.config.argparsing.Parser.addini>` - call (usually from a plugin), a ValueError is raised. + :func:`parser.addini ` call (usually from a + plugin), a ValueError is raised. """ try: return self._inicache[name] diff --git a/src/_pytest/config/argparsing.py b/src/_pytest/config/argparsing.py index d24696eaff2..51acb4033ec 100644 --- a/src/_pytest/config/argparsing.py +++ b/src/_pytest/config/argparsing.py @@ -21,6 +21,7 @@ from _pytest.deprecated import ARGUMENT_PERCENT_DEFAULT from _pytest.deprecated import ARGUMENT_TYPE_STR from _pytest.deprecated import ARGUMENT_TYPE_STR_CHOICE +from _pytest.deprecated import check_ispytest if TYPE_CHECKING: from typing import NoReturn @@ -43,8 +44,11 @@ def __init__( self, usage: Optional[str] = None, processopt: Optional[Callable[["Argument"], None]] = None, + *, + _ispytest: bool = False, ) -> None: - self._anonymous = OptionGroup("custom options", parser=self) + check_ispytest(_ispytest) + self._anonymous = OptionGroup("custom options", parser=self, _ispytest=True) self._groups: List[OptionGroup] = [] self._processopt = processopt self._usage = usage @@ -67,14 +71,14 @@ def getgroup( :after: Name of another group, used for ordering --help output. The returned group object has an ``addoption`` method with the same - signature as :py:func:`parser.addoption - <_pytest.config.argparsing.Parser.addoption>` but will be shown in the - respective group in the output of ``pytest. --help``. + signature as :func:`parser.addoption ` but + will be shown in the respective group in the output of + ``pytest. --help``. """ for group in self._groups: if group.name == name: return group - group = OptionGroup(name, description, parser=self) + group = OptionGroup(name, description, parser=self, _ispytest=True) i = 0 for i, grp in enumerate(self._groups): if grp.name == after: @@ -334,9 +338,17 @@ def __repr__(self) -> str: class OptionGroup: + """A group of options shown in its own section.""" + def __init__( - self, name: str, description: str = "", parser: Optional[Parser] = None + self, + name: str, + description: str = "", + parser: Optional[Parser] = None, + *, + _ispytest: bool = False, ) -> None: + check_ispytest(_ispytest) self.name = name self.description = description self.options: List[Argument] = [] @@ -346,9 +358,9 @@ def addoption(self, *optnames: str, **attrs: Any) -> None: """Add an option to this group. If a shortened version of a long option is specified, it will - be suppressed in the help. addoption('--twowords', '--two-words') - results in help showing '--two-words' only, but --twowords gets - accepted **and** the automatic destination is in args.twowords. + be suppressed in the help. ``addoption('--twowords', '--two-words')`` + results in help showing ``--two-words`` only, but ``--twowords`` gets + accepted **and** the automatic destination is in ``args.twowords``. """ conflict = set(optnames).intersection( name for opt in self.options for name in opt.names() diff --git a/src/_pytest/hookspec.py b/src/_pytest/hookspec.py index 427d7507e2b..75110a9a3b7 100644 --- a/src/_pytest/hookspec.py +++ b/src/_pytest/hookspec.py @@ -88,11 +88,11 @@ def pytest_addoption(parser: "Parser", pluginmanager: "PytestPluginManager") -> files situated at the tests root directory due to how pytest :ref:`discovers plugins during startup `. - :param _pytest.config.argparsing.Parser parser: + :param pytest.Parser parser: To add command line options, call - :py:func:`parser.addoption(...) <_pytest.config.argparsing.Parser.addoption>`. + :py:func:`parser.addoption(...) `. To add ini-file values call :py:func:`parser.addini(...) - <_pytest.config.argparsing.Parser.addini>`. + `. :param _pytest.config.PytestPluginManager pluginmanager: pytest plugin manager, which can be used to install :py:func:`hookspec`'s @@ -193,7 +193,7 @@ def pytest_load_initial_conftests( :param _pytest.config.Config early_config: The pytest config object. :param List[str] args: Arguments passed on the command line. - :param _pytest.config.argparsing.Parser parser: To add command line options. + :param pytest.Parser parser: To add command line options. """ diff --git a/src/pytest/__init__.py b/src/pytest/__init__.py index 02a82386d08..7a81d234105 100644 --- a/src/pytest/__init__.py +++ b/src/pytest/__init__.py @@ -13,6 +13,8 @@ from _pytest.config import hookspec from _pytest.config import main from _pytest.config import UsageError +from _pytest.config.argparsing import OptionGroup +from _pytest.config.argparsing import Parser from _pytest.debugging import pytestPDB as __pytestPDB from _pytest.fixtures import _fillfuncargs from _pytest.fixtures import fixture @@ -103,8 +105,10 @@ "Metafunc", "Module", "MonkeyPatch", + "OptionGroup", "Package", "param", + "Parser", "PytestAssertRewriteWarning", "PytestCacheWarning", "PytestCollectionWarning", diff --git a/testing/test_parseopt.py b/testing/test_parseopt.py index 1062c8c3725..28529d04378 100644 --- a/testing/test_parseopt.py +++ b/testing/test_parseopt.py @@ -14,12 +14,12 @@ @pytest.fixture def parser() -> parseopt.Parser: - return parseopt.Parser() + return parseopt.Parser(_ispytest=True) class TestParser: def test_no_help_by_default(self) -> None: - parser = parseopt.Parser(usage="xyz") + parser = parseopt.Parser(usage="xyz", _ispytest=True) pytest.raises(UsageError, lambda: parser.parse(["-h"])) def test_custom_prog(self, parser: parseopt.Parser) -> None: @@ -90,13 +90,13 @@ def test_group_ordering(self, parser: parseopt.Parser) -> None: assert groups_names == list("132") def test_group_addoption(self) -> None: - group = parseopt.OptionGroup("hello") + group = parseopt.OptionGroup("hello", _ispytest=True) group.addoption("--option1", action="store_true") assert len(group.options) == 1 assert isinstance(group.options[0], parseopt.Argument) def test_group_addoption_conflict(self) -> None: - group = parseopt.OptionGroup("hello again") + group = parseopt.OptionGroup("hello again", _ispytest=True) group.addoption("--option1", "--option-1", action="store_true") with pytest.raises(ValueError) as err: group.addoption("--option1", "--option-one", action="store_true") @@ -188,7 +188,7 @@ def defaultget(option): elif option.type is str: option.default = "world" - parser = parseopt.Parser(processopt=defaultget) + parser = parseopt.Parser(processopt=defaultget, _ispytest=True) parser.addoption("--this", dest="this", type=int, action="store") parser.addoption("--hello", dest="hello", type=str, action="store") parser.addoption("--no", dest="no", action="store_true") From 4a472952f7e8427102f8ba579071bfe7dedce853 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 May 2021 03:01:18 +0000 Subject: [PATCH 323/630] Bump anyio[curio,trio] in /testing/plugins_integration Bumps [anyio[curio,trio]](https://github.com/agronholm/anyio) from 3.0.1 to 3.1.0. - [Release notes](https://github.com/agronholm/anyio/releases) - [Changelog](https://github.com/agronholm/anyio/blob/master/docs/versionhistory.rst) - [Commits](https://github.com/agronholm/anyio/compare/3.0.1...3.1.0) Signed-off-by: dependabot[bot] --- testing/plugins_integration/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/plugins_integration/requirements.txt b/testing/plugins_integration/requirements.txt index fc9fc196691..2f071be0e46 100644 --- a/testing/plugins_integration/requirements.txt +++ b/testing/plugins_integration/requirements.txt @@ -1,4 +1,4 @@ -anyio[curio,trio]==3.0.1 +anyio[curio,trio]==3.1.0 django==3.2.3 pytest-asyncio==0.15.1 pytest-bdd==4.0.2 From 88d84a57916b592b070f4201dc84f0286d1f9fef Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Mon, 24 May 2021 11:45:07 +0300 Subject: [PATCH 324/630] config: expose Config for typing purposes This type is used in hooks and fixtures. The constructor is publicly documented so is not marked private. --- changelog/7469.feature.rst | 1 + doc/en/builtin.rst | 2 +- doc/en/reference/customize.rst | 6 ++--- doc/en/reference/reference.rst | 2 +- src/_pytest/config/__init__.py | 5 +++-- src/_pytest/config/argparsing.py | 2 +- src/_pytest/fixtures.py | 3 ++- src/_pytest/hookspec.py | 38 ++++++++++++++++---------------- src/_pytest/pytester.py | 6 ++--- src/_pytest/python.py | 2 +- src/pytest/__init__.py | 2 ++ 11 files changed, 37 insertions(+), 32 deletions(-) diff --git a/changelog/7469.feature.rst b/changelog/7469.feature.rst index ea8df523952..8d186c92d93 100644 --- a/changelog/7469.feature.rst +++ b/changelog/7469.feature.rst @@ -2,6 +2,7 @@ The types of objects used in pytest's API are now exported so they may be used i The newly-exported types are: +- ``pytest.Config`` for :class:`Config `. - ``pytest.Mark`` for :class:`marks `. - ``pytest.MarkDecorator`` for :class:`mark decorators `. - ``pytest.MarkGenerator`` for the :class:`pytest.mark ` singleton. diff --git a/doc/en/builtin.rst b/doc/en/builtin.rst index f0a6eedb106..ea2f27a4c9f 100644 --- a/doc/en/builtin.rst +++ b/doc/en/builtin.rst @@ -61,7 +61,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a namespace of doctests. pytestconfig [session scope] - Session-scoped fixture that returns the :class:`_pytest.config.Config` object. + Session-scoped fixture that returns the :class:`pytest.Config` object. Example:: diff --git a/doc/en/reference/customize.rst b/doc/en/reference/customize.rst index 24d2ec93118..d6fb5c6b2dd 100644 --- a/doc/en/reference/customize.rst +++ b/doc/en/reference/customize.rst @@ -179,12 +179,12 @@ Files will only be matched for configuration if: The files are considered in the order above. Options from multiple ``configfiles`` candidates are never merged - the first match wins. -The internal :class:`Config <_pytest.config.Config>` object (accessible via hooks or through the :fixture:`pytestconfig` fixture) +The :class:`Config ` object (accessible via hooks or through the :fixture:`pytestconfig` fixture) will subsequently carry these attributes: -- :attr:`config.rootpath <_pytest.config.Config.rootpath>`: the determined root directory, guaranteed to exist. +- :attr:`config.rootpath `: the determined root directory, guaranteed to exist. -- :attr:`config.inipath <_pytest.config.Config.inipath>`: the determined ``configfile``, may be ``None`` +- :attr:`config.inipath `: the determined ``configfile``, may be ``None`` (it is named ``inipath`` for historical reasons). .. versionadded:: 6.1 diff --git a/doc/en/reference/reference.rst b/doc/en/reference/reference.rst index d4256bd6080..279ceb76d8d 100644 --- a/doc/en/reference/reference.rst +++ b/doc/en/reference/reference.rst @@ -787,7 +787,7 @@ CollectReport Config ~~~~~~ -.. autoclass:: _pytest.config.Config() +.. autoclass:: pytest.Config() :members: ExceptionInfo diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 7f18f62cb39..f24f7d5d60a 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -841,6 +841,7 @@ class Config: """Access to configuration values, pluginmanager and plugin hooks. :param PytestPluginManager pluginmanager: + A pytest PluginManager. :param InvocationParams invocation_params: Object containing parameters regarding the :func:`pytest.main` @@ -1226,8 +1227,8 @@ def _preparse(self, args: List[str], addopts: bool = True) -> None: @hookimpl(hookwrapper=True) def pytest_collection(self) -> Generator[None, None, None]: - """Validate invalid ini keys after collection is done so we take in account - options added by late-loading conftest files.""" + # Validate invalid ini keys after collection is done so we take in account + # options added by late-loading conftest files. yield self._validate_config_options() diff --git a/src/_pytest/config/argparsing.py b/src/_pytest/config/argparsing.py index d24696eaff2..7c75375a19c 100644 --- a/src/_pytest/config/argparsing.py +++ b/src/_pytest/config/argparsing.py @@ -189,7 +189,7 @@ def addini( Default value if no ini-file option exists but is queried. The value of ini-variables can be retrieved via a call to - :py:func:`config.getini(name) <_pytest.config.Config.getini>`. + :py:func:`config.getini(name) `. """ assert type in (None, "string", "paths", "pathlist", "args", "linelist", "bool") self._inidict[name] = (help, type, default) diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 1076debb8d9..ebc44b2b2b1 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -1379,7 +1379,8 @@ def yield_fixture( @fixture(scope="session") def pytestconfig(request: FixtureRequest) -> Config: - """Session-scoped fixture that returns the :class:`_pytest.config.Config` object. + """Session-scoped fixture that returns the session's :class:`pytest.Config` + object. Example:: diff --git a/src/_pytest/hookspec.py b/src/_pytest/hookspec.py index 427d7507e2b..6524356abe2 100644 --- a/src/_pytest/hookspec.py +++ b/src/_pytest/hookspec.py @@ -100,12 +100,12 @@ def pytest_addoption(parser: "Parser", pluginmanager: "PytestPluginManager") -> to change how command line options are added. Options can later be accessed through the - :py:class:`config <_pytest.config.Config>` object, respectively: + :py:class:`config ` object, respectively: - - :py:func:`config.getoption(name) <_pytest.config.Config.getoption>` to + - :py:func:`config.getoption(name) ` to retrieve the value of a command line option. - - :py:func:`config.getini(name) <_pytest.config.Config.getini>` to retrieve + - :py:func:`config.getini(name) ` to retrieve a value read from an ini-style file. The config object is passed around on many internal objects via the ``.config`` @@ -129,7 +129,7 @@ def pytest_configure(config: "Config") -> None: .. note:: This hook is incompatible with ``hookwrapper=True``. - :param _pytest.config.Config config: The pytest config object. + :param pytest.Config config: The pytest config object. """ @@ -166,7 +166,7 @@ def pytest_cmdline_preparse(config: "Config", args: List[str]) -> None: .. note:: This hook will not be called for ``conftest.py`` files, only for setuptools plugins. - :param _pytest.config.Config config: The pytest config object. + :param pytest.Config config: The pytest config object. :param List[str] args: Arguments passed on the command line. """ @@ -178,7 +178,7 @@ def pytest_cmdline_main(config: "Config") -> Optional[Union["ExitCode", int]]: Stops at first non-None result, see :ref:`firstresult`. - :param _pytest.config.Config config: The pytest config object. + :param pytest.Config config: The pytest config object. """ @@ -191,7 +191,7 @@ def pytest_load_initial_conftests( .. note:: This hook will not be called for ``conftest.py`` files, only for setuptools plugins. - :param _pytest.config.Config early_config: The pytest config object. + :param pytest.Config early_config: The pytest config object. :param List[str] args: Arguments passed on the command line. :param _pytest.config.argparsing.Parser parser: To add command line options. """ @@ -246,7 +246,7 @@ def pytest_collection_modifyitems( the items in-place. :param pytest.Session session: The pytest session object. - :param _pytest.config.Config config: The pytest config object. + :param pytest.Config config: The pytest config object. :param List[pytest.Item] items: List of item objects. """ @@ -271,7 +271,7 @@ def pytest_ignore_collect( :param pathlib.Path fspath: The path to analyze. :param LEGACY_PATH path: The path to analyze. - :param _pytest.config.Config config: The pytest config object. + :param pytest.Config config: The pytest config object. .. versionchanged:: 6.3.0 The ``fspath`` parameter was added as a :class:`pathlib.Path` @@ -385,7 +385,7 @@ def pytest_make_parametrize_id( Stops at first non-None result, see :ref:`firstresult`. - :param _pytest.config.Config config: The pytest config object. + :param pytest.Config config: The pytest config object. :param val: The parametrized value. :param str argname: The automatic parameter name produced by pytest. """ @@ -609,7 +609,7 @@ def pytest_sessionfinish( def pytest_unconfigure(config: "Config") -> None: """Called before test process is exited. - :param _pytest.config.Config config: The pytest config object. + :param pytest.Config config: The pytest config object. """ @@ -628,7 +628,7 @@ def pytest_assertrepr_compare( *in* a string will be escaped. Note that all but the first line will be indented slightly, the intention is for the first line to be a summary. - :param _pytest.config.Config config: The pytest config object. + :param pytest.Config config: The pytest config object. """ @@ -677,7 +677,7 @@ def pytest_report_header( ) -> Union[str, List[str]]: """Return a string or list of strings to be displayed as header info for terminal reporting. - :param _pytest.config.Config config: The pytest config object. + :param pytest.Config config: The pytest config object. :param Path startpath: The starting dir. :param LEGACY_PATH startdir: The starting dir. @@ -713,7 +713,7 @@ def pytest_report_collectionfinish( .. versionadded:: 3.2 - :param _pytest.config.Config config: The pytest config object. + :param pytest.Config config: The pytest config object. :param Path startpath: The starting path. :param LEGACY_PATH startdir: The starting dir. :param items: List of pytest items that are going to be executed; this list should not be modified. @@ -752,7 +752,7 @@ def pytest_report_teststatus( for example ``"rerun", "R", ("RERUN", {"yellow": True})``. :param report: The report object whose status is to be returned. - :param _pytest.config.Config config: The pytest config object. + :param pytest.Config config: The pytest config object. Stops at first non-None result, see :ref:`firstresult`. """ @@ -767,7 +767,7 @@ def pytest_terminal_summary( :param _pytest.terminal.TerminalReporter terminalreporter: The internal terminal reporter object. :param int exitstatus: The exit status that will be reported back to the OS. - :param _pytest.config.Config config: The pytest config object. + :param pytest.Config config: The pytest config object. .. versionadded:: 4.2 The ``config`` parameter. @@ -857,7 +857,7 @@ def pytest_markeval_namespace(config: "Config") -> Dict[str, Any]: .. versionadded:: 6.2 - :param _pytest.config.Config config: The pytest config object. + :param pytest.Config config: The pytest config object. :returns: A dictionary of additional globals to add. """ @@ -909,7 +909,7 @@ def pytest_enter_pdb(config: "Config", pdb: "pdb.Pdb") -> None: Can be used by plugins to take special action just before the python debugger enters interactive mode. - :param _pytest.config.Config config: The pytest config object. + :param pytest.Config config: The pytest config object. :param pdb.Pdb pdb: The Pdb instance. """ @@ -920,6 +920,6 @@ def pytest_leave_pdb(config: "Config", pdb: "pdb.Pdb") -> None: Can be used by plugins to take special action just after the python debugger leaves interactive mode. - :param _pytest.config.Config config: The pytest config object. + :param pytest.Config config: The pytest config object. :param pdb.Pdb pdb: The Pdb instance. """ diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index 037f69e0b19..af73b63920b 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -954,7 +954,7 @@ def getnode( ) -> Optional[Union[Collector, Item]]: """Return the collection node of a file. - :param _pytest.config.Config config: + :param pytest.Config config: A pytest config. See :py:meth:`parseconfig` and :py:meth:`parseconfigure` for creating it. :param os.PathLike[str] arg: @@ -1186,7 +1186,7 @@ def parseconfig(self, *args: Union[str, "os.PathLike[str]"]) -> Config: This invokes the pytest bootstrapping code in _pytest.config to create a new :py:class:`_pytest.core.PluginManager` and call the pytest_cmdline_parse hook to create a new - :py:class:`_pytest.config.Config` instance. + :py:class:`pytest.Config` instance. If :py:attr:`plugins` has been populated they should be plugin modules to be registered with the PluginManager. @@ -1206,7 +1206,7 @@ def parseconfig(self, *args: Union[str, "os.PathLike[str]"]) -> Config: def parseconfigure(self, *args: Union[str, "os.PathLike[str]"]) -> Config: """Return a new pytest configured Config instance. - Returns a new :py:class:`_pytest.config.Config` instance like + Returns a new :py:class:`pytest.Config` instance like :py:meth:`parseconfig`, but also calls the pytest_configure hook. """ config = self.parseconfig(*args) diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 436f463ee18..f7dfd696bde 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -970,7 +970,7 @@ def __init__( #: Access to the underlying :class:`_pytest.python.FunctionDefinition`. self.definition = definition - #: Access to the :class:`_pytest.config.Config` object for the test session. + #: Access to the :class:`pytest.Config` object for the test session. self.config = config #: The module object where the test function is defined in. diff --git a/src/pytest/__init__.py b/src/pytest/__init__.py index 02a82386d08..ca28ee2c879 100644 --- a/src/pytest/__init__.py +++ b/src/pytest/__init__.py @@ -7,6 +7,7 @@ from _pytest.cacheprovider import Cache from _pytest.capture import CaptureFixture from _pytest.config import cmdline +from _pytest.config import Config from _pytest.config import console_main from _pytest.config import ExitCode from _pytest.config import hookimpl @@ -77,6 +78,7 @@ "cmdline", "collect", "Collector", + "Config", "console_main", "deprecated_call", "exit", From c0d525e44cc5ca35d7abbd7024f6d999955165fd Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Mon, 24 May 2021 12:16:16 +0300 Subject: [PATCH 325/630] config: expose PytestPluginManager for typing purposes This type is used in hooks and transitively through `Config`. --- changelog/7469.feature.rst | 1 + doc/en/reference/reference.rst | 2 +- src/_pytest/config/__init__.py | 7 ++++++- src/_pytest/hookspec.py | 10 +++++----- src/pytest/__init__.py | 2 ++ 5 files changed, 15 insertions(+), 7 deletions(-) diff --git a/changelog/7469.feature.rst b/changelog/7469.feature.rst index 8d186c92d93..02c936ec603 100644 --- a/changelog/7469.feature.rst +++ b/changelog/7469.feature.rst @@ -8,6 +8,7 @@ The newly-exported types are: - ``pytest.MarkGenerator`` for the :class:`pytest.mark ` singleton. - ``pytest.Metafunc`` for the :class:`metafunc ` argument to the :func:`pytest_generate_tests ` hook. - ``pytest.CallInfo`` for the :class:`CallInfo ` type passed to various hooks. +- ``pytest.PytestPluginManager`` for :class:`PytestPluginManager `. - ``pytest.ExceptionInfo`` for the :class:`ExceptionInfo ` type returned from :func:`pytest.raises` and passed to various hooks. Constructing them directly is not supported; they are only meant for use in type annotations. diff --git a/doc/en/reference/reference.rst b/doc/en/reference/reference.rst index 279ceb76d8d..67fb00d1c0f 100644 --- a/doc/en/reference/reference.rst +++ b/doc/en/reference/reference.rst @@ -896,7 +896,7 @@ Parser PytestPluginManager ~~~~~~~~~~~~~~~~~~~ -.. autoclass:: _pytest.config.PytestPluginManager() +.. autoclass:: pytest.PytestPluginManager() :members: :undoc-members: :inherited-members: diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index f24f7d5d60a..6531a31610a 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -290,7 +290,7 @@ def get_config( def get_plugin_manager() -> "PytestPluginManager": """Obtain a new instance of the - :py:class:`_pytest.config.PytestPluginManager`, with default plugins + :py:class:`pytest.PytestPluginManager`, with default plugins already loaded. This function can be used by integration with other tools, like hooking @@ -632,6 +632,7 @@ def _check_non_top_pytest_plugins( def consider_preparse( self, args: Sequence[str], *, exclude_only: bool = False ) -> None: + """:meta private:""" i = 0 n = len(args) while i < n: @@ -653,6 +654,7 @@ def consider_preparse( self.consider_pluginarg(parg) def consider_pluginarg(self, arg: str) -> None: + """:meta private:""" if arg.startswith("no:"): name = arg[3:] if name in essential_plugins: @@ -678,12 +680,15 @@ def consider_pluginarg(self, arg: str) -> None: self.import_plugin(arg, consider_entry_points=True) def consider_conftest(self, conftestmodule: types.ModuleType) -> None: + """:meta private:""" self.register(conftestmodule, name=conftestmodule.__file__) def consider_env(self) -> None: + """:meta private:""" self._import_plugin_specs(os.environ.get("PYTEST_PLUGINS")) def consider_module(self, mod: types.ModuleType) -> None: + """:meta private:""" self._import_plugin_specs(getattr(mod, "pytest_plugins", [])) def _import_plugin_specs( diff --git a/src/_pytest/hookspec.py b/src/_pytest/hookspec.py index 6524356abe2..bb8502fa81a 100644 --- a/src/_pytest/hookspec.py +++ b/src/_pytest/hookspec.py @@ -56,7 +56,7 @@ def pytest_addhooks(pluginmanager: "PytestPluginManager") -> None: """Called at plugin registration time to allow adding new hooks via a call to ``pluginmanager.add_hookspecs(module_or_class, prefix)``. - :param _pytest.config.PytestPluginManager pluginmanager: pytest plugin manager. + :param pytest.PytestPluginManager pluginmanager: The pytest plugin manager. .. note:: This hook is incompatible with ``hookwrapper=True``. @@ -70,7 +70,7 @@ def pytest_plugin_registered( """A new pytest plugin got registered. :param plugin: The plugin module or instance. - :param _pytest.config.PytestPluginManager manager: pytest plugin manager. + :param pytest.PytestPluginManager manager: pytest plugin manager. .. note:: This hook is incompatible with ``hookwrapper=True``. @@ -94,8 +94,8 @@ def pytest_addoption(parser: "Parser", pluginmanager: "PytestPluginManager") -> To add ini-file values call :py:func:`parser.addini(...) <_pytest.config.argparsing.Parser.addini>`. - :param _pytest.config.PytestPluginManager pluginmanager: - pytest plugin manager, which can be used to install :py:func:`hookspec`'s + :param pytest.PytestPluginManager pluginmanager: + The pytest plugin manager, which can be used to install :py:func:`hookspec`'s or :py:func:`hookimpl`'s and allow one plugin to call another plugin's hooks to change how command line options are added. @@ -152,7 +152,7 @@ def pytest_cmdline_parse( ``plugins`` arg when using `pytest.main`_ to perform an in-process test run. - :param _pytest.config.PytestPluginManager pluginmanager: Pytest plugin manager. + :param pytest.PytestPluginManager pluginmanager: The pytest plugin manager. :param List[str] args: List of arguments passed on the command line. """ diff --git a/src/pytest/__init__.py b/src/pytest/__init__.py index ca28ee2c879..6e2f5e5142a 100644 --- a/src/pytest/__init__.py +++ b/src/pytest/__init__.py @@ -13,6 +13,7 @@ from _pytest.config import hookimpl from _pytest.config import hookspec from _pytest.config import main +from _pytest.config import PytestPluginManager from _pytest.config import UsageError from _pytest.debugging import pytestPDB as __pytestPDB from _pytest.fixtures import _fillfuncargs @@ -114,6 +115,7 @@ "PytestDeprecationWarning", "PytestExperimentalApiWarning", "Pytester", + "PytestPluginManager", "PytestUnhandledCoroutineWarning", "PytestUnhandledThreadExceptionWarning", "PytestUnknownMarkWarning", From 9719237fe6e08cb39e1be3d78299ca5188f53abb Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Mon, 24 May 2021 12:31:28 +0300 Subject: [PATCH 326/630] doc: refer to type by its public name --- doc/en/how-to/writing_plugins.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/how-to/writing_plugins.rst b/doc/en/how-to/writing_plugins.rst index 8ec935cc2a3..3b58a833320 100644 --- a/doc/en/how-to/writing_plugins.rst +++ b/doc/en/how-to/writing_plugins.rst @@ -337,7 +337,7 @@ testing directory: Alternatively you can invoke pytest with the ``-p pytester`` command line option. -This will allow you to use the :py:class:`pytester <_pytest.pytester.Pytester>` +This will allow you to use the :py:class:`pytester ` fixture for testing your plugin code. Let's demonstrate what you can do with the plugin with an example. Imagine we From c5bf5f6fb05ab5eb36509313b6e10e03a7486589 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 26 May 2021 11:19:45 +0300 Subject: [PATCH 327/630] [pre-commit.ci] pre-commit autoupdate (#8699) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [pre-commit.ci] pre-commit autoupdate updates: - [github.com/asottile/pyupgrade: v2.16.0 → v2.18.2](https://github.com/asottile/pyupgrade/compare/v2.16.0...v2.18.2) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- .pre-commit-config.yaml | 2 +- src/_pytest/config/__init__.py | 2 +- testing/test_junitxml.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6ac7fcc3db3..83f7b697b72 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -34,7 +34,7 @@ repos: - id: reorder-python-imports args: ['--application-directories=.:src', --py36-plus] - repo: https://github.com/asottile/pyupgrade - rev: v2.16.0 + rev: v2.18.2 hooks: - id: pyupgrade args: [--py36-plus] diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 7f18f62cb39..c32ccb6ea03 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -1611,7 +1611,7 @@ def parse_warning_filter( raise warnings._OptionError(f"too many fields (max 5): {arg!r}") while len(parts) < 5: parts.append("") - action_, message, category_, module, lineno_ = [s.strip() for s in parts] + action_, message, category_, module, lineno_ = (s.strip() for s in parts) action: str = warnings._getaction(action_) # type: ignore[attr-defined] category: Type[Warning] = warnings._getcategory(category_) # type: ignore[attr-defined] if message and escape: diff --git a/testing/test_junitxml.py b/testing/test_junitxml.py index 139e2a9a705..5cb062932cd 100644 --- a/testing/test_junitxml.py +++ b/testing/test_junitxml.py @@ -1388,7 +1388,7 @@ def test_pass(): result, dom = run_and_parse(f, f) result.stdout.no_fnmatch_line("*INTERNALERROR*") - first, second = [x["classname"] for x in dom.find_by_tag("testcase")] + first, second = (x["classname"] for x in dom.find_by_tag("testcase")) assert first == second @@ -1406,7 +1406,7 @@ def test_pass(): result, dom = run_and_parse(f, "--dist", "each", "--tx", "2*popen") result.stdout.no_fnmatch_line("*INTERNALERROR*") - first, second = [x["classname"] for x in dom.find_by_tag("testcase")] + first, second = (x["classname"] for x in dom.find_by_tag("testcase")) assert first == second From 1d5ee4dd7c4719ddc710b69d13c4fd7fc1895843 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 26 May 2021 14:15:00 +0200 Subject: [PATCH 328/630] doc: Remove training sidebar (#8701) --- doc/en/index.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/doc/en/index.rst b/doc/en/index.rst index cdaccb104e6..59431dbb315 100644 --- a/doc/en/index.rst +++ b/doc/en/index.rst @@ -1,10 +1,12 @@ :orphan: -.. sidebar:: Next Open Trainings +.. + (note: please leave this here, next open training to follow soon) + .. sidebar:: Next Open Trainings - - `Professionelles Testen für Python mit pytest `_ (German), part of the enterPy conference, April 22nd (sold out) and May 20th, remote. + - `Professionelles Testen für Python mit pytest `_ (German), part of the enterPy conference, April 22nd (sold out) and May 20th, remote. - Also see `previous talks and blogposts `_. + Also see `previous talks and blogposts `_. .. _features: From 640b2c0e132658206686a7b9df166cde204c164d Mon Sep 17 00:00:00 2001 From: Chris Rose Date: Wed, 26 May 2021 09:06:47 -0700 Subject: [PATCH 329/630] Make the test class instance behaviour clearer In the "Getting Started" doc, the test class instance example for instance sharing doesn't actually demonstrate anything about the reinstantiation of the class. This change shows clearly how the instance data isn't retained between test runs. --- AUTHORS | 1 + doc/en/getting-started.rst | 30 ++++++++++++------------------ 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/AUTHORS b/AUTHORS index b822d469aed..7f5af5d9241 100644 --- a/AUTHORS +++ b/AUTHORS @@ -62,6 +62,7 @@ Charles Machalow Charnjit SiNGH (CCSJ) Chris Lamb Chris NeJame +Chris Rose Christian Boelsen Christian Fetzer Christian Neumüller diff --git a/doc/en/getting-started.rst b/doc/en/getting-started.rst index 2f082c35d16..707087a9af7 100644 --- a/doc/en/getting-started.rst +++ b/doc/en/getting-started.rst @@ -169,40 +169,34 @@ This is outlined below: # content of test_class_demo.py class TestClassDemoInstance: + value = 0 + def test_one(self): - assert 0 + self.value = 1 + assert self.value == 1 def test_two(self): - assert 0 + assert self.value == 1 .. code-block:: pytest $ pytest -k TestClassDemoInstance -q - FF [100%] + .F [100%] ================================= FAILURES ================================= - ______________________ TestClassDemoInstance.test_one ______________________ - - self = - - def test_one(self): - > assert 0 - E assert 0 - - test_class_demo.py:3: AssertionError ______________________ TestClassDemoInstance.test_two ______________________ self = def test_two(self): - > assert 0 - E assert 0 + > assert self.value == 1 + E assert 0 == 1 + E + where 0 = .value - test_class_demo.py:6: AssertionError + test_class_demo.py:9: AssertionError ========================= short test summary info ========================== - FAILED test_class_demo.py::TestClassDemoInstance::test_one - assert 0 - FAILED test_class_demo.py::TestClassDemoInstance::test_two - assert 0 - 2 failed in 0.12s + FAILED test_class_demo.py::TestClassDemoInstance::test_two - assert 0 == 1 + 1 failed, 1 passed in 0.04s Note that attributes added at class level are *class attributes*, so they will be shared between tests. From 627b4462520606de1c4304c60b27e2b45dfc15ef Mon Sep 17 00:00:00 2001 From: MapleCCC Date: Thu, 27 May 2021 04:33:49 +0800 Subject: [PATCH 330/630] Fix cyclic links in goodpractices.rst --- doc/en/explanation/goodpractices.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/explanation/goodpractices.rst b/doc/en/explanation/goodpractices.rst index 186beae58e3..f66ef5a68c0 100644 --- a/doc/en/explanation/goodpractices.rst +++ b/doc/en/explanation/goodpractices.rst @@ -240,7 +240,7 @@ tox ------ Once you are done with your work and want to make sure that your actual -package passes all tests you may want to look into `tox`_, the +package passes all tests you may want to look into `tox `_, the virtualenv test automation tool and its `pytest support `_. tox helps you to setup virtualenv environments with pre-defined From 4b7d8b2edd6681631e9117b70bbf3b8c3a3ba301 Mon Sep 17 00:00:00 2001 From: pytest bot Date: Sun, 30 May 2021 00:23:19 +0000 Subject: [PATCH 331/630] [automated] Update plugin list --- doc/en/reference/plugin_list.rst | 36 +++++++++++++++++--------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/doc/en/reference/plugin_list.rst b/doc/en/reference/plugin_list.rst index c6facee7560..3165798a583 100644 --- a/doc/en/reference/plugin_list.rst +++ b/doc/en/reference/plugin_list.rst @@ -6,7 +6,7 @@ Plugin List PyPI projects that match "pytest-\*" are considered plugins and are listed automatically. Packages classified as inactive are excluded. -This list contains 868 plugins. +This list contains 870 plugins. ============================================================================================================== ======================================================================================================================================================================== ============== ===================== ================================================ name summary last release status requires @@ -31,7 +31,7 @@ name `pytest-alphamoon `_ Static code checks used at Alphamoon Nov 20, 2020 4 - Beta pytest (>=3.5.0) `pytest-android `_ This fixture provides a configured "driver" for Android Automated Testing, using uiautomator2. Feb 21, 2019 3 - Alpha pytest `pytest-annotate `_ pytest-annotate: Generate PyAnnotate annotations from your pytest tests. Aug 23, 2019 3 - Alpha pytest (<6.0.0,>=3.2.0) -`pytest-ansible `_ Plugin for py.test to simplify calling ansible modules from tests or fixtures Oct 26, 2020 5 - Production/Stable pytest +`pytest-ansible `_ Plugin for py.test to simplify calling ansible modules from tests or fixtures May 25, 2021 5 - Production/Stable N/A `pytest-ansible-playbook `_ Pytest fixture which runs given ansible playbook file. Mar 08, 2019 4 - Beta N/A `pytest-ansible-playbook-runner `_ Pytest fixture which runs given ansible playbook file. Dec 02, 2020 4 - Beta pytest (>=3.1.0) `pytest-antilru `_ Bust functools.lru_cache when running pytest to avoid test pollution Apr 11, 2019 5 - Production/Stable pytest @@ -129,7 +129,7 @@ name `pytest-cloud `_ Distributed tests planner plugin for pytest testing framework. Oct 05, 2020 6 - Mature N/A `pytest-cloudflare-worker `_ pytest plugin for testing cloudflare workers Mar 30, 2021 4 - Beta pytest (>=6.0.0) `pytest-cobra `_ PyTest plugin for testing Smart Contracts for Ethereum blockchain. Jun 29, 2019 3 - Alpha pytest (<4.0.0,>=3.7.1) -`pytest-codeblocks `_ Test code blocks in your READMEs May 18, 2021 4 - Beta pytest (>=6) +`pytest-codeblocks `_ Test code blocks in your READMEs May 25, 2021 4 - Beta pytest (>=6) `pytest-codecheckers `_ pytest plugin to add source code sanity checks (pep8 and friends) Feb 13, 2010 N/A N/A `pytest-codecov `_ Pytest plugin for uploading pytest-cov results to codecov.io May 05, 2021 4 - Beta pytest (>=4.6.0) `pytest-codegen `_ Automatically create pytest test signatures Aug 23, 2020 2 - Pre-Alpha N/A @@ -146,7 +146,7 @@ name `pytest-consul `_ pytest plugin with fixtures for testing consul aware apps Nov 24, 2018 3 - Alpha pytest `pytest-contextfixture `_ Define pytest fixtures as context managers. Mar 12, 2013 4 - Beta N/A `pytest-contexts `_ A plugin to run tests written with the Contexts framework using pytest May 19, 2021 4 - Beta N/A -`pytest-cookies `_ The pytest plugin for your Cookiecutter templates. 🍪 Feb 14, 2020 5 - Production/Stable pytest (<6.0.0,>=3.3.0) +`pytest-cookies `_ The pytest plugin for your Cookiecutter templates. 🍪 May 24, 2021 5 - Production/Stable pytest (>=3.3.0) `pytest-couchdbkit `_ py.test extension for per-test couchdb databases using couchdbkit Apr 17, 2012 N/A N/A `pytest-count `_ count erros and send email Jan 12, 2018 4 - Beta N/A `pytest-cov `_ Pytest plugin for measuring coverage. May 14, 2021 5 - Production/Stable pytest (>=4.6) @@ -232,7 +232,7 @@ name `pytest-docker-postgresql `_ A simple plugin to use with pytest Sep 24, 2019 4 - Beta pytest (>=3.5.0) `pytest-docker-py `_ Easy to use, simple to extend, pytest plugin that minimally leverages docker-py. Nov 27, 2018 N/A pytest (==4.0.0) `pytest-docker-registry-fixtures `_ Pytest fixtures for testing with docker registries. Mar 04, 2021 4 - Beta pytest -`pytest-docker-tools `_ Docker integration tests for pytest Mar 26, 2021 4 - Beta pytest (>=6.0.1,<7.0.0) +`pytest-docker-tools `_ Docker integration tests for pytest May 28, 2021 4 - Beta pytest (>=6.0.1,<7.0.0) `pytest-docs `_ Documentation tool for pytest Nov 11, 2018 4 - Beta pytest (>=3.5.0) `pytest-docstyle `_ pytest plugin to run pydocstyle Mar 23, 2020 3 - Alpha N/A `pytest-doctest-custom `_ A py.test plugin for customizing string representations of doctest results. Jul 25, 2016 4 - Beta N/A @@ -338,7 +338,7 @@ name `pytest-gevent `_ Ensure that gevent is properly patched when invoking pytest Feb 25, 2020 N/A pytest `pytest-gherkin `_ A flexible framework for executing BDD gherkin tests Jul 27, 2019 3 - Alpha pytest (>=5.0.0) `pytest-ghostinspector `_ For finding/executing Ghost Inspector tests May 17, 2016 3 - Alpha N/A -`pytest-girder `_ A set of pytest fixtures for testing Girder applications. Apr 19, 2021 N/A N/A +`pytest-girder `_ A set of pytest fixtures for testing Girder applications. May 28, 2021 N/A N/A `pytest-git `_ Git repository fixture for py.test May 28, 2019 5 - Production/Stable pytest `pytest-gitcov `_ Pytest plugin for reporting on coverage of the last git commit. Jan 11, 2020 2 - Pre-Alpha N/A `pytest-git-fixtures `_ Pytest fixtures for testing with git. Mar 11, 2021 4 - Beta pytest @@ -362,7 +362,7 @@ name `pytest-historic `_ Custom report to display pytest historical execution records Apr 08, 2020 N/A pytest `pytest-historic-hook `_ Custom listener to store execution results into MYSQL DB, which is used for pytest-historic report Apr 08, 2020 N/A pytest `pytest-homeassistant `_ A pytest plugin for use with homeassistant custom components. Aug 12, 2020 4 - Beta N/A -`pytest-homeassistant-custom-component `_ Experimental package to automatically extract test plugins for Home Assistant custom components May 01, 2021 3 - Alpha pytest (==6.2.3) +`pytest-homeassistant-custom-component `_ Experimental package to automatically extract test plugins for Home Assistant custom components May 28, 2021 3 - Alpha pytest (==6.2.4) `pytest-honors `_ Report on tests that honor constraints, and guard against regressions Mar 06, 2020 4 - Beta N/A `pytest-hoverfly `_ Simplify working with Hoverfly from pytest Mar 04, 2021 N/A pytest (>=5.0) `pytest-hoverfly-wrapper `_ Integrates the Hoverfly HTTP proxy into Pytest Jan 31, 2021 4 - Beta N/A @@ -392,13 +392,14 @@ name `pytest-informative-node `_ display more node ininformation. Apr 25, 2019 4 - Beta N/A `pytest-infrastructure `_ pytest stack validation prior to testing executing Apr 12, 2020 4 - Beta N/A `pytest-inmanta `_ A py.test plugin providing fixtures to simplify inmanta modules testing. Apr 09, 2021 5 - Production/Stable N/A -`pytest-inmanta-extensions `_ Inmanta tests package May 05, 2021 5 - Production/Stable N/A +`pytest-inmanta-extensions `_ Inmanta tests package May 27, 2021 5 - Production/Stable N/A `pytest-Inomaly `_ A simple image diff plugin for pytest Feb 13, 2018 4 - Beta N/A `pytest-insta `_ A practical snapshot testing plugin for pytest Apr 07, 2021 N/A pytest (>=6.0.2,<7.0.0) `pytest-instafail `_ pytest plugin to show failures instantly Jun 14, 2020 4 - Beta pytest (>=2.9) `pytest-instrument `_ pytest plugin to instrument tests Apr 05, 2020 5 - Production/Stable pytest (>=5.1.0) `pytest-integration `_ Organizing pytests by integration or not Apr 16, 2020 N/A N/A `pytest-interactive `_ A pytest plugin for console based interactive test selection just after the collection phase Nov 30, 2017 3 - Alpha N/A +`pytest-intercept-remote `_ Pytest plugin for intercepting outgoing connection requests during pytest run. May 24, 2021 4 - Beta pytest (>=4.6) `pytest-invenio `_ Pytest fixtures for Invenio. May 11, 2021 5 - Production/Stable pytest (<7,>=6) `pytest-involve `_ Run tests covering a specific file or changeset Feb 02, 2020 4 - Beta pytest (>=3.5.0) `pytest-ipdb `_ A py.test plug-in to enable drop to ipdb debugger on test failure. Sep 02, 2014 2 - Pre-Alpha N/A @@ -495,6 +496,7 @@ name `pytest-mpi `_ pytest plugin to collect information from tests Mar 14, 2021 3 - Alpha pytest `pytest-mpl `_ pytest plugin to help with testing figures output from Matplotlib Nov 05, 2020 4 - Beta pytest `pytest-mproc `_ low-startup-overhead, scalable, distributed-testing pytest plugin Mar 07, 2021 4 - Beta pytest +`pytest-multi-check `_ Pytest-плагин, реализует возможность мульти проверок и мягких проверок May 26, 2021 N/A pytest `pytest-multihost `_ Utility for writing multi-host tests for pytest Apr 07, 2020 4 - Beta N/A `pytest-multilog `_ Multi-process logs handling and other helpers for pytest Nov 15, 2020 N/A N/A `pytest-mutagen `_ Add the mutation testing feature to pytest Jul 24, 2020 N/A pytest (>=5.4) @@ -581,10 +583,10 @@ name `pytest-ponyorm `_ PonyORM in Pytest Oct 31, 2018 N/A pytest (>=3.1.1) `pytest-poo `_ Visualize your crappy tests Mar 25, 2021 5 - Production/Stable pytest (>=2.3.4) `pytest-poo-fail `_ Visualize your failed tests with poo Feb 12, 2015 5 - Production/Stable N/A -`pytest-pop `_ A pytest plugin to help with testing pop projects May 05, 2021 5 - Production/Stable pytest +`pytest-pop `_ A pytest plugin to help with testing pop projects May 25, 2021 5 - Production/Stable pytest `pytest-portion `_ Select a portion of the collected tests Jan 28, 2021 4 - Beta pytest (>=3.5.0) `pytest-postgres `_ Run PostgreSQL in Docker container in Pytest. Mar 22, 2020 N/A pytest -`pytest-postgresql `_ Postgresql fixtures and fixture factories for Pytest. May 19, 2021 5 - Production/Stable pytest (>=3.0.0) +`pytest-postgresql `_ Postgresql fixtures and fixture factories for Pytest. May 26, 2021 5 - Production/Stable pytest (>=3.0.0) `pytest-power `_ pytest plugin with powerful fixtures Dec 31, 2020 N/A pytest (>=5.4) `pytest-pride `_ Minitest-style test colors Apr 02, 2016 3 - Alpha N/A `pytest-print `_ pytest-print adds the printer fixture you can use to print messages to the user (directly to the pytest runner, not stdout) Oct 23, 2020 5 - Production/Stable pytest (>=3.0.0) @@ -608,7 +610,7 @@ name `pytest-pyramid-server `_ Pyramid server fixture for py.test May 28, 2019 5 - Production/Stable pytest `pytest-pytestrail `_ Pytest plugin for interaction with TestRail Aug 27, 2020 4 - Beta pytest (>=3.8.0) `pytest-pythonpath `_ pytest plugin for adding to the PYTHONPATH from command line or configs. Aug 22, 2018 5 - Production/Stable N/A -`pytest-pytorch `_ pytest plugin for a better developer experience when working with the PyTorch test suite Apr 21, 2021 4 - Beta pytest +`pytest-pytorch `_ pytest plugin for a better developer experience when working with the PyTorch test suite May 25, 2021 4 - Beta pytest `pytest-qml `_ Run QML Tests with pytest Dec 02, 2020 4 - Beta pytest (>=6.0.0) `pytest-qt `_ pytest support for PyQt and PySide applications Dec 07, 2019 5 - Production/Stable pytest (>=3.0.0) `pytest-qt-app `_ QT app fixture for py.test Dec 23, 2015 5 - Production/Stable N/A @@ -629,7 +631,7 @@ name `pytest-reana `_ Pytest fixtures for REANA. May 03, 2021 3 - Alpha N/A `pytest-recording `_ A pytest plugin that allows you recording of network interactions via VCR.py Nov 25, 2020 4 - Beta pytest (>=3.5.0) `pytest-recordings `_ Provides pytest plugins for reporting request/response traffic, screenshots, and more to ReportPortal Aug 13, 2020 N/A N/A -`pytest-redis `_ Redis fixtures and fixture factories for Pytest. May 18, 2021 5 - Production/Stable pytest +`pytest-redis `_ Redis fixtures and fixture factories for Pytest. May 25, 2021 5 - Production/Stable pytest `pytest-redmine `_ Pytest plugin for redmine Mar 19, 2018 1 - Planning N/A `pytest-ref `_ A plugin to store reference files to ease regression testing Nov 23, 2019 4 - Beta pytest (>=3.5.0) `pytest-reference-formatter `_ Conveniently run pytest with a dot-formatted test reference. Oct 01, 2019 4 - Beta N/A @@ -657,7 +659,7 @@ name `pytest-requests `_ A simple plugin to use with pytest Jun 24, 2019 4 - Beta pytest (>=3.5.0) `pytest-reraise `_ Make multi-threaded pytest test cases fail when they should Jun 03, 2020 5 - Production/Stable N/A `pytest-rerun `_ Re-run only changed files in specified branch Jul 08, 2019 N/A pytest (>=3.6) -`pytest-rerunfailures `_ pytest plugin to re-run tests to eliminate flaky failures Sep 29, 2020 5 - Production/Stable pytest (>=5.0) +`pytest-rerunfailures `_ pytest plugin to re-run tests to eliminate flaky failures May 26, 2021 5 - Production/Stable pytest (>=5.3) `pytest-resilient-circuits `_ Resilient Circuits fixtures for PyTest. May 14, 2021 N/A N/A `pytest-resource `_ Load resource fixture plugin to use with pytest Nov 14, 2018 4 - Beta N/A `pytest-resource-path `_ Provides path for uniform access to test resources in isolated directory May 01, 2021 5 - Production/Stable pytest (>=3.5.0) @@ -679,19 +681,19 @@ name `pytest-runner `_ Invoke py.test as distutils command with dependency resolution May 19, 2021 5 - Production/Stable pytest (>=4.6) ; extra == 'testing' `pytest-salt `_ Pytest Salt Plugin Jan 27, 2020 4 - Beta N/A `pytest-salt-containers `_ A Pytest plugin that builds and creates docker containers Nov 09, 2016 4 - Beta N/A -`pytest-salt-factories `_ Pytest Salt Plugin May 06, 2021 4 - Beta pytest (>=6.0.0) +`pytest-salt-factories `_ Pytest Salt Plugin May 27, 2021 4 - Beta pytest (>=6.0.0) `pytest-salt-from-filenames `_ Simple PyTest Plugin For Salt's Test Suite Specifically Jan 29, 2019 4 - Beta pytest (>=4.1) `pytest-salt-runtests-bridge `_ Simple PyTest Plugin For Salt's Test Suite Specifically Dec 05, 2019 4 - Beta pytest (>=4.1) `pytest-sanic `_ a pytest plugin for Sanic May 20, 2021 N/A pytest (>=5.2) `pytest-sanity `_ Dec 07, 2020 N/A N/A `pytest-sa-pg `_ May 14, 2019 N/A N/A -`pytest-sbase `_ A complete web automation framework for end-to-end testing. May 22, 2021 5 - Production/Stable N/A +`pytest-sbase `_ A complete web automation framework for end-to-end testing. May 24, 2021 5 - Production/Stable N/A `pytest-scenario `_ pytest plugin for test scenarios Feb 06, 2017 3 - Alpha N/A `pytest-schema `_ 👍 Validate return values against a schema-like object in testing Aug 31, 2020 5 - Production/Stable pytest (>=3.5.0) `pytest-securestore `_ An encrypted password store for use within pytest cases Jun 19, 2019 4 - Beta N/A `pytest-select `_ A pytest plugin which allows to (de-)select tests from a file. Jan 18, 2019 3 - Alpha pytest (>=3.0) `pytest-selenium `_ pytest plugin for Selenium Sep 19, 2020 5 - Production/Stable pytest (>=5.0.0) -`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. May 22, 2021 5 - Production/Stable N/A +`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. May 24, 2021 5 - Production/Stable N/A `pytest-selenium-enhancer `_ pytest plugin for Selenium Nov 26, 2020 5 - Production/Stable N/A `pytest-selenium-pdiff `_ A pytest package implementing perceptualdiff for Selenium tests. Apr 06, 2017 2 - Pre-Alpha N/A `pytest-send-email `_ Send pytest execution result email Dec 04, 2019 N/A N/A @@ -759,7 +761,7 @@ name `pytest-study `_ A pytest plugin to organize long run tests (named studies) without interfering the regular tests Sep 26, 2017 3 - Alpha pytest (>=2.0) `pytest-subprocess `_ A plugin to fake subprocess for pytest Apr 18, 2021 5 - Production/Stable pytest (>=4.0.0) `pytest-subtesthack `_ A hack to explicitly set up and tear down fixtures. Mar 02, 2021 N/A N/A -`pytest-subtests `_ unittest subTest() support and subtests fixture Dec 13, 2020 4 - Beta pytest (>=5.3.0) +`pytest-subtests `_ unittest subTest() support and subtests fixture May 29, 2021 4 - Beta pytest (>=5.3.0) `pytest-subunit `_ pytest-subunit is a plugin for py.test which outputs testsresult in subunit format. Aug 29, 2017 N/A N/A `pytest-sugar `_ pytest-sugar is a plugin for pytest that changes the default look and feel of pytest (e.g. progressbar, show tests that fail instantly). Jul 06, 2020 3 - Alpha N/A `pytest-sugar-bugfix159 `_ Workaround for https://github.com/Frozenball/pytest-sugar/issues/159 Nov 07, 2018 5 - Production/Stable pytest (!=3.7.3,>=3.5); extra == 'testing' From 20f00997ff5ccce077fc8b7fc1f2e29a3751c5ab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 May 2021 03:01:08 +0000 Subject: [PATCH 332/630] Bump pytest-rerunfailures in /testing/plugins_integration Bumps [pytest-rerunfailures](https://github.com/pytest-dev/pytest-rerunfailures) from 9.1.1 to 10.0. - [Release notes](https://github.com/pytest-dev/pytest-rerunfailures/releases) - [Changelog](https://github.com/pytest-dev/pytest-rerunfailures/blob/master/CHANGES.rst) - [Commits](https://github.com/pytest-dev/pytest-rerunfailures/compare/9.1.1...10.0) Signed-off-by: dependabot[bot] --- testing/plugins_integration/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/plugins_integration/requirements.txt b/testing/plugins_integration/requirements.txt index 2f071be0e46..d517481d66c 100644 --- a/testing/plugins_integration/requirements.txt +++ b/testing/plugins_integration/requirements.txt @@ -7,7 +7,7 @@ pytest-django==4.3.0 pytest-flakes==4.0.3 pytest-html==3.1.1 pytest-mock==3.6.1 -pytest-rerunfailures==9.1.1 +pytest-rerunfailures==10.0 pytest-sugar==0.9.4 pytest-trio==0.7.0 pytest-twisted==1.13.2 From cf7425d22b529a2a8cef5b7d319c62db6f3a65bd Mon Sep 17 00:00:00 2001 From: Denis Laxalde Date: Mon, 31 May 2021 09:36:18 +0200 Subject: [PATCH 333/630] Fix a typo in documentation about tmpdir_factory The section refers to tmpdir_factory, but the sentence mentionned tmp_path_factory. --- doc/en/reference/reference.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/reference/reference.rst b/doc/en/reference/reference.rst index 1a35d89118d..153dad903b7 100644 --- a/doc/en/reference/reference.rst +++ b/doc/en/reference/reference.rst @@ -615,7 +615,7 @@ tmpdir_factory :ref:`tmpdir and tmpdir_factory` -``tmp_path_factory`` is an instance of :class:`~pytest.TempdirFactory`: +``tmpdir_factory`` is an instance of :class:`~pytest.TempdirFactory`: .. autoclass:: pytest.TempdirFactory() :members: From c51520ab2a8afd7d59744077daea338c60016e75 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 31 May 2021 17:32:52 +0000 Subject: [PATCH 334/630] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black: 21.5b1 → 21.5b2](https://github.com/psf/black/compare/21.5b1...21.5b2) - [github.com/asottile/pyupgrade: v2.18.2 → v2.19.0](https://github.com/asottile/pyupgrade/compare/v2.18.2...v2.19.0) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 83f7b697b72..e6ea181f520 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/psf/black - rev: 21.5b1 + rev: 21.5b2 hooks: - id: black args: [--safe, --quiet] @@ -34,7 +34,7 @@ repos: - id: reorder-python-imports args: ['--application-directories=.:src', --py36-plus] - repo: https://github.com/asottile/pyupgrade - rev: v2.18.2 + rev: v2.19.0 hooks: - id: pyupgrade args: [--py36-plus] From 9ad726a9a0d93f6c821cd8d5be583d555451d766 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 1 Jun 2021 17:34:29 +0200 Subject: [PATCH 335/630] Switch to irc.libera.chat (#8722) --- doc/en/contact.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/doc/en/contact.rst b/doc/en/contact.rst index c7cf277afd6..9e714ed2c2f 100644 --- a/doc/en/contact.rst +++ b/doc/en/contact.rst @@ -16,12 +16,13 @@ Contact channels - `pytest-dev at python.org (mailing list)`_ pytest specific announcements and discussions. -- `pytest-commit at python.org (mailing list)`_: for commits and new issues - - :doc:`contribution guide ` for help on submitting pull requests to GitHub. -- ``#pylib`` on irc.freenode.net IRC channel for random questions. +- ``#pytest`` `on irc.libera.chat `_ IRC + channel for random questions (using an IRC client, `via webchat + `_, or `via Matrix + `_). - private mail to Holger.Krekel at gmail com if you want to communicate sensitive issues From 77235e2759085dc7e9bb572f8a4e65f9aa079ae1 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Thu, 3 Jun 2021 08:45:01 -0700 Subject: [PATCH 336/630] Revert "Merge pull request #8227 from encukou/defensive-get_source" This reverts commit 67af623d9e25716cf7bb91dd6ce7354991197954, reversing changes made to aead41e449b07d00c958b938d899841f304dc2fa. --- src/_pytest/_code/code.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/_pytest/_code/code.py b/src/_pytest/_code/code.py index d9a6b9edbc0..2fdd8a086cf 100644 --- a/src/_pytest/_code/code.py +++ b/src/_pytest/_code/code.py @@ -725,11 +725,11 @@ def get_source( ) -> List[str]: """Return formatted and marked up source lines.""" lines = [] - if source is not None and line_index < 0: - line_index += len(source.lines) - if source is None or line_index >= len(source.lines) or line_index < 0: + if source is None or line_index >= len(source.lines): source = Source("???") line_index = 0 + if line_index < 0: + line_index += len(source) space_prefix = " " if short: lines.append(space_prefix + source.lines[line_index].strip()) From 7fb5a9d0336db8b97055139ab2154c77ab8430af Mon Sep 17 00:00:00 2001 From: Dominic Davis-Foster Date: Sat, 5 Jun 2021 16:03:59 +0100 Subject: [PATCH 337/630] Fix typo (setupttools -> setuptools) --- doc/en/example/simple.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/example/simple.rst b/doc/en/example/simple.rst index efe9af6e937..131534a1744 100644 --- a/doc/en/example/simple.rst +++ b/doc/en/example/simple.rst @@ -1068,7 +1068,7 @@ your frozen program work as the pytest runner by some clever argument handling during program startup. This allows you to have a single executable, which is usually more convenient. Please note that the mechanism for plugin discovery used by pytest -(setupttools entry points) doesn't work with frozen executables so pytest +(setuptools entry points) doesn't work with frozen executables so pytest can't find any third party plugins automatically. To include third party plugins like ``pytest-timeout`` they must be imported explicitly and passed on to pytest.main. From 50b166ff0bff6e384709aa0742b684b662403a27 Mon Sep 17 00:00:00 2001 From: pytest bot Date: Sun, 6 Jun 2021 00:17:08 +0000 Subject: [PATCH 338/630] [automated] Update plugin list --- doc/en/reference/plugin_list.rst | 54 +++++++++++++++++--------------- 1 file changed, 28 insertions(+), 26 deletions(-) diff --git a/doc/en/reference/plugin_list.rst b/doc/en/reference/plugin_list.rst index 3165798a583..ddbfac86723 100644 --- a/doc/en/reference/plugin_list.rst +++ b/doc/en/reference/plugin_list.rst @@ -6,12 +6,12 @@ Plugin List PyPI projects that match "pytest-\*" are considered plugins and are listed automatically. Packages classified as inactive are excluded. -This list contains 870 plugins. +This list contains 872 plugins. ============================================================================================================== ======================================================================================================================================================================== ============== ===================== ================================================ name summary last release status requires ============================================================================================================== ======================================================================================================================================================================== ============== ===================== ================================================ -`pytest-accept `_ A pytest-plugin for updating doctest outputs May 18, 2021 N/A pytest (>=6,<7) +`pytest-accept `_ A pytest-plugin for updating doctest outputs Jun 02, 2021 N/A pytest (>=6,<7) `pytest-adaptavist `_ pytest plugin for generating test execution results within Jira Test Management (tm4j) Apr 22, 2021 N/A pytest (>=3.4.1) `pytest-adf `_ Pytest plugin for writing Azure Data Factory integration tests May 10, 2021 4 - Beta pytest (>=3.5.0) `pytest-adf-azure-identity `_ Pytest plugin for writing Azure Data Factory integration tests Mar 06, 2021 4 - Beta pytest (>=3.5.0) @@ -65,6 +65,7 @@ name `pytest-auto-parametrize `_ pytest plugin: avoid repeating arguments in parametrize Oct 02, 2016 3 - Alpha N/A `pytest-avoidance `_ Makes pytest skip tests that don not need rerunning May 23, 2019 4 - Beta pytest (>=3.5.0) `pytest-aws `_ pytest plugin for testing AWS resource configurations Oct 04, 2017 4 - Beta N/A +`pytest-aws-config `_ Protect your AWS credentials in unit tests May 28, 2021 N/A N/A `pytest-axe `_ pytest plugin for axe-selenium-python Nov 12, 2018 N/A pytest (>=3.0.0) `pytest-azurepipelines `_ Formatting PyTest output for Azure Pipelines UI Jul 23, 2020 4 - Beta pytest (>=3.5.0) `pytest-bandit `_ A bandit plugin for pytest Feb 23, 2021 4 - Beta pytest (>=3.5.0) @@ -77,11 +78,12 @@ name `pytest-beds `_ Fixtures for testing Google Appengine (GAE) apps Jun 07, 2016 4 - Beta N/A `pytest-bench `_ Benchmark utility that plugs into pytest. Jul 21, 2014 3 - Alpha N/A `pytest-benchmark `_ A ``pytest`` fixture for benchmarking code. It will group the tests into rounds that are calibrated to the chosen timer. Apr 17, 2021 5 - Production/Stable pytest (>=3.8) -`pytest-bigchaindb `_ A BigchainDB plugin for pytest. Apr 17, 2021 4 - Beta N/A +`pytest-bg-process `_ A simple plugin to use with pytest May 28, 2021 4 - Beta pytest (>=3.5.0) +`pytest-bigchaindb `_ A BigchainDB plugin for pytest. May 28, 2021 4 - Beta N/A `pytest-black `_ A pytest plugin to enable format checking with black Oct 05, 2020 4 - Beta N/A `pytest-black-multipy `_ Allow '--black' on older Pythons Jan 14, 2021 5 - Production/Stable pytest (!=3.7.3,>=3.5) ; extra == 'testing' `pytest-blame `_ A pytest plugin helps developers to debug by providing useful commits history. May 04, 2019 N/A pytest (>=4.4.0) -`pytest-blender `_ Blender Pytest plugin. Feb 15, 2021 N/A pytest (==6.2.1) ; extra == 'dev' +`pytest-blender `_ Blender Pytest plugin. Jun 02, 2021 N/A pytest (==6.2.1) ; extra == 'dev' `pytest-blink1 `_ Pytest plugin to emit notifications via the Blink(1) RGB LED Jan 07, 2018 4 - Beta N/A `pytest-blockage `_ Disable network requests during a test run. Feb 13, 2019 N/A pytest `pytest-blocker `_ pytest plugin to mark a test as blocker and skip all other tests Sep 07, 2015 4 - Beta N/A @@ -105,11 +107,10 @@ name `pytest-canonical-data `_ A plugin which allows to compare results with canonical results, based on previous runs May 08, 2020 2 - Pre-Alpha pytest (>=3.5.0) `pytest-caprng `_ A plugin that replays pRNG state on failure. May 02, 2018 4 - Beta N/A `pytest-capture-deprecatedwarnings `_ pytest plugin to capture all deprecatedwarnings and put them in one file Apr 30, 2019 N/A N/A -`pytest-cases `_ Separate test code from test cases in pytest. May 21, 2021 5 - Production/Stable N/A +`pytest-cases `_ Separate test code from test cases in pytest. Jun 03, 2021 5 - Production/Stable N/A `pytest-cassandra `_ Cassandra CCM Test Fixtures for pytest Nov 04, 2017 1 - Planning N/A `pytest-catchlog `_ py.test plugin to catch log messages. This is a fork of pytest-capturelog. Jan 24, 2016 4 - Beta pytest (>=2.6) `pytest-catch-server `_ Pytest plugin with server for catching HTTP requests. Dec 12, 2019 5 - Production/Stable N/A -`pytest-cdp-common `_ May 01, 2021 N/A N/A `pytest-celery `_ pytest-celery a shim pytest plugin to enable celery.contrib.pytest May 06, 2021 N/A N/A `pytest-chalice `_ A set of py.test fixtures for AWS Chalice Jul 01, 2020 4 - Beta N/A `pytest-change-report `_ turn . into √,turn F into x Sep 14, 2020 N/A pytest @@ -135,7 +136,7 @@ name `pytest-codegen `_ Automatically create pytest test signatures Aug 23, 2020 2 - Pre-Alpha N/A `pytest-codestyle `_ pytest plugin to run pycodestyle Mar 23, 2020 3 - Alpha N/A `pytest-collect-formatter `_ Formatter for pytest collect output Mar 29, 2021 5 - Production/Stable N/A -`pytest-collect-formatter2 `_ Formatter for pytest collect output Apr 11, 2021 5 - Production/Stable N/A +`pytest-collect-formatter2 `_ Formatter for pytest collect output May 31, 2021 5 - Production/Stable N/A `pytest-colordots `_ Colorizes the progress indicators Oct 06, 2017 5 - Production/Stable N/A `pytest-commander `_ An interactive GUI test runner for PyTest May 09, 2021 N/A pytest (<7.0.0,>=6.2.4) `pytest-common-subject `_ pytest framework for testing different aspects of a common method Nov 12, 2020 N/A pytest (>=3.6,<7) @@ -149,12 +150,12 @@ name `pytest-cookies `_ The pytest plugin for your Cookiecutter templates. 🍪 May 24, 2021 5 - Production/Stable pytest (>=3.3.0) `pytest-couchdbkit `_ py.test extension for per-test couchdb databases using couchdbkit Apr 17, 2012 N/A N/A `pytest-count `_ count erros and send email Jan 12, 2018 4 - Beta N/A -`pytest-cov `_ Pytest plugin for measuring coverage. May 14, 2021 5 - Production/Stable pytest (>=4.6) +`pytest-cov `_ Pytest plugin for measuring coverage. Jun 01, 2021 5 - Production/Stable pytest (>=4.6) `pytest-cover `_ Pytest plugin for measuring coverage. Forked from `pytest-cov`. Aug 01, 2015 5 - Production/Stable N/A `pytest-coverage `_ Jun 17, 2015 N/A N/A `pytest-coverage-context `_ Coverage dynamic context support for PyTest, including sub-processes Jan 04, 2021 4 - Beta pytest (>=6.1.0) `pytest-cov-exclude `_ Pytest plugin for excluding tests based on coverage data Apr 29, 2016 4 - Beta pytest (>=2.8.0,<2.9.0); extra == 'dev' -`pytest-cpp `_ Use pytest's runner to discover and execute C++ tests Dec 10, 2020 4 - Beta pytest (!=5.4.0,!=5.4.1) +`pytest-cpp `_ Use pytest's runner to discover and execute C++ tests Jun 04, 2021 5 - Production/Stable pytest (!=5.4.0,!=5.4.1) `pytest-cram `_ Run cram tests with pytest. Aug 08, 2020 N/A N/A `pytest-crate `_ Manages CrateDB instances during your integration tests May 28, 2019 3 - Alpha pytest (>=4.0) `pytest-cricri `_ A Cricri plugin for pytest. Jan 27, 2018 N/A pytest @@ -232,7 +233,7 @@ name `pytest-docker-postgresql `_ A simple plugin to use with pytest Sep 24, 2019 4 - Beta pytest (>=3.5.0) `pytest-docker-py `_ Easy to use, simple to extend, pytest plugin that minimally leverages docker-py. Nov 27, 2018 N/A pytest (==4.0.0) `pytest-docker-registry-fixtures `_ Pytest fixtures for testing with docker registries. Mar 04, 2021 4 - Beta pytest -`pytest-docker-tools `_ Docker integration tests for pytest May 28, 2021 4 - Beta pytest (>=6.0.1,<7.0.0) +`pytest-docker-tools `_ Docker integration tests for pytest Jun 03, 2021 4 - Beta pytest (>=6.0.1,<7.0.0) `pytest-docs `_ Documentation tool for pytest Nov 11, 2018 4 - Beta pytest (>=3.5.0) `pytest-docstyle `_ pytest plugin to run pydocstyle Mar 23, 2020 3 - Alpha N/A `pytest-doctest-custom `_ A py.test plugin for customizing string representations of doctest results. Jul 25, 2016 4 - Beta N/A @@ -248,7 +249,7 @@ name `pytest-drop-dup-tests `_ A Pytest plugin to drop duplicated tests during collection May 23, 2020 4 - Beta pytest (>=2.7) `pytest-dump2json `_ A pytest plugin for dumping test results to json. Jun 29, 2015 N/A N/A `pytest-dynamicrerun `_ A pytest plugin to rerun tests dynamically based off of test outcome and output. Aug 15, 2020 4 - Beta N/A -`pytest-dynamodb `_ DynamoDB fixtures for pytest Feb 20, 2020 5 - Production/Stable pytest (>=3.0.0) +`pytest-dynamodb `_ DynamoDB fixtures for pytest Jun 03, 2021 5 - Production/Stable pytest `pytest-easy-addoption `_ pytest-easy-addoption: Easy way to work with pytest addoption Jan 22, 2020 N/A N/A `pytest-easy-api `_ Simple API testing with pytest Mar 26, 2018 N/A N/A `pytest-easyMPI `_ Package that supports mpi tests in pytest Oct 21, 2020 N/A N/A @@ -362,7 +363,7 @@ name `pytest-historic `_ Custom report to display pytest historical execution records Apr 08, 2020 N/A pytest `pytest-historic-hook `_ Custom listener to store execution results into MYSQL DB, which is used for pytest-historic report Apr 08, 2020 N/A pytest `pytest-homeassistant `_ A pytest plugin for use with homeassistant custom components. Aug 12, 2020 4 - Beta N/A -`pytest-homeassistant-custom-component `_ Experimental package to automatically extract test plugins for Home Assistant custom components May 28, 2021 3 - Alpha pytest (==6.2.4) +`pytest-homeassistant-custom-component `_ Experimental package to automatically extract test plugins for Home Assistant custom components Jun 04, 2021 3 - Alpha pytest (==6.2.4) `pytest-honors `_ Report on tests that honor constraints, and guard against regressions Mar 06, 2020 4 - Beta N/A `pytest-hoverfly `_ Simplify working with Hoverfly from pytest Mar 04, 2021 N/A pytest (>=5.0) `pytest-hoverfly-wrapper `_ Integrates the Hoverfly HTTP proxy into Pytest Jan 31, 2021 4 - Beta N/A @@ -480,7 +481,7 @@ name `pytest-mock-helper `_ Help you mock HTTP call and generate mock code Jan 24, 2018 N/A pytest `pytest-mockito `_ Base fixtures for mockito Jul 11, 2018 4 - Beta N/A `pytest-mockredis `_ An in-memory mock of a Redis server that runs in a separate thread. This is to be used for unit-tests that require a Redis database. Jan 02, 2018 2 - Pre-Alpha N/A -`pytest-mock-resources `_ A pytest plugin for easily instantiating reproducible mock resources. Apr 26, 2021 N/A pytest (>=1.0) +`pytest-mock-resources `_ A pytest plugin for easily instantiating reproducible mock resources. Jun 02, 2021 N/A pytest (>=1.0) `pytest-mock-server `_ Mock server plugin for pytest Apr 06, 2020 4 - Beta N/A `pytest-mockservers `_ A set of fixtures to test your requests to HTTP/UDP servers Mar 31, 2020 N/A pytest (>=4.3.0) `pytest-modifyjunit `_ Utility for adding additional properties to junit xml for IDM QE Jan 10, 2019 N/A N/A @@ -496,7 +497,7 @@ name `pytest-mpi `_ pytest plugin to collect information from tests Mar 14, 2021 3 - Alpha pytest `pytest-mpl `_ pytest plugin to help with testing figures output from Matplotlib Nov 05, 2020 4 - Beta pytest `pytest-mproc `_ low-startup-overhead, scalable, distributed-testing pytest plugin Mar 07, 2021 4 - Beta pytest -`pytest-multi-check `_ Pytest-плагин, реализует возможность мульти проверок и мягких проверок May 26, 2021 N/A pytest +`pytest-multi-check `_ Pytest-плагин, реализует возможность мульти проверок и мягких проверок Jun 03, 2021 N/A pytest `pytest-multihost `_ Utility for writing multi-host tests for pytest Apr 07, 2020 4 - Beta N/A `pytest-multilog `_ Multi-process logs handling and other helpers for pytest Nov 15, 2020 N/A N/A `pytest-mutagen `_ Add the mutation testing feature to pytest Jul 24, 2020 N/A pytest (>=5.4) @@ -505,7 +506,7 @@ name `pytest-mypy-plugins `_ pytest plugin for writing tests for mypy plugins May 22, 2021 3 - Alpha pytest (>=6.0.0) `pytest-mypy-plugins-shim `_ Substitute for "pytest-mypy-plugins" for Python implementations which aren't supported by mypy. Apr 12, 2021 N/A N/A `pytest-mypy-testing `_ Pytest plugin to check mypy output. Apr 24, 2020 N/A pytest -`pytest-mysql `_ MySQL process and client fixtures for pytest Jul 21, 2020 5 - Production/Stable pytest (>=3.0.0) +`pytest-mysql `_ MySQL process and client fixtures for pytest Jun 01, 2021 5 - Production/Stable pytest `pytest-needle `_ pytest plugin for visual testing websites using selenium Dec 10, 2018 4 - Beta pytest (<5.0.0,>=3.0.0) `pytest-neo `_ pytest-neo is a plugin for pytest that shows tests like screen of Matrix. Apr 23, 2019 3 - Alpha pytest (>=3.7.2) `pytest-network `_ A simple plugin to disable network on socket level. May 07, 2020 N/A N/A @@ -534,11 +535,11 @@ name `pytest-oot `_ Run object-oriented tests in a simple format Sep 18, 2016 4 - Beta N/A `pytest-openfiles `_ Pytest plugin for detecting inadvertent open file handles Apr 16, 2020 3 - Alpha pytest (>=4.6) `pytest-opentmi `_ pytest plugin for publish results to opentmi Feb 26, 2021 5 - Production/Stable pytest (>=5.0) -`pytest-operator `_ Fixtures for Operators Mar 29, 2021 N/A N/A +`pytest-operator `_ Fixtures for Operators Jun 03, 2021 N/A N/A `pytest-optional `_ include/exclude values of fixtures in pytest Oct 07, 2015 N/A N/A `pytest-optional-tests `_ Easy declaration of optional tests (i.e., that are not run by default) Jul 09, 2019 4 - Beta pytest (>=4.5.0) `pytest-orchestration `_ A pytest plugin for orchestrating tests Jul 18, 2019 N/A N/A -`pytest-order `_ pytest plugin to run your tests in a specific order Apr 11, 2021 4 - Beta pytest (>=3.7) +`pytest-order `_ pytest plugin to run your tests in a specific order May 30, 2021 4 - Beta pytest (>=5.0) `pytest-ordering `_ pytest plugin to run your tests in a specific order Nov 14, 2018 4 - Beta pytest `pytest-osxnotify `_ OS X notifications for py.test results. May 15, 2015 N/A N/A `pytest-pact `_ A simple plugin to use with pytest Jan 07, 2019 4 - Beta N/A @@ -557,6 +558,7 @@ name `pytest-pep257 `_ py.test plugin for pep257 Jul 09, 2016 N/A N/A `pytest-pep8 `_ pytest plugin to check PEP8 requirements Apr 27, 2014 N/A N/A `pytest-percent `_ Change the exit code of pytest test sessions when a required percent of tests pass. May 21, 2020 N/A pytest (>=5.2.0) +`pytest-perf `_ pytest-perf May 29, 2021 5 - Production/Stable pytest (>=4.6) ; extra == 'testing' `pytest-performance `_ A simple plugin to ensure the execution of critical sections of code has not been impacted Sep 11, 2020 5 - Production/Stable pytest (>=3.7.0) `pytest-persistence `_ Pytest tool for persistent objects Mar 28, 2021 N/A N/A `pytest-pgsql `_ Pytest plugins and helpers for tests using a Postgres database. May 13, 2020 5 - Production/Stable pytest (>=3.0.0) @@ -586,7 +588,7 @@ name `pytest-pop `_ A pytest plugin to help with testing pop projects May 25, 2021 5 - Production/Stable pytest `pytest-portion `_ Select a portion of the collected tests Jan 28, 2021 4 - Beta pytest (>=3.5.0) `pytest-postgres `_ Run PostgreSQL in Docker container in Pytest. Mar 22, 2020 N/A pytest -`pytest-postgresql `_ Postgresql fixtures and fixture factories for Pytest. May 26, 2021 5 - Production/Stable pytest (>=3.0.0) +`pytest-postgresql `_ Postgresql fixtures and fixture factories for Pytest. Jun 01, 2021 5 - Production/Stable pytest (>=3.0.0) `pytest-power `_ pytest plugin with powerful fixtures Dec 31, 2020 N/A pytest (>=5.4) `pytest-pride `_ Minitest-style test colors Apr 02, 2016 3 - Alpha N/A `pytest-print `_ pytest-print adds the printer fixture you can use to print messages to the user (directly to the pytest runner, not stdout) Oct 23, 2020 5 - Production/Stable pytest (>=3.0.0) @@ -612,11 +614,11 @@ name `pytest-pythonpath `_ pytest plugin for adding to the PYTHONPATH from command line or configs. Aug 22, 2018 5 - Production/Stable N/A `pytest-pytorch `_ pytest plugin for a better developer experience when working with the PyTorch test suite May 25, 2021 4 - Beta pytest `pytest-qml `_ Run QML Tests with pytest Dec 02, 2020 4 - Beta pytest (>=6.0.0) -`pytest-qt `_ pytest support for PyQt and PySide applications Dec 07, 2019 5 - Production/Stable pytest (>=3.0.0) +`pytest-qt `_ pytest support for PyQt and PySide applications Jun 03, 2021 5 - Production/Stable pytest (>=3.0.0) `pytest-qt-app `_ QT app fixture for py.test Dec 23, 2015 5 - Production/Stable N/A `pytest-quarantine `_ A plugin for pytest to manage expected test failures Nov 24, 2019 5 - Production/Stable pytest (>=4.6) `pytest-quickcheck `_ pytest plugin to generate random data inspired by QuickCheck Nov 15, 2020 4 - Beta pytest (<6.0.0,>=4.0) -`pytest-rabbitmq `_ RabbitMQ process and client fixtures for pytest Jan 11, 2021 5 - Production/Stable pytest (>=3.0.0) +`pytest-rabbitmq `_ RabbitMQ process and client fixtures for pytest Jun 02, 2021 5 - Production/Stable pytest (>=3.0.0) `pytest-race `_ Race conditions tester for pytest Nov 21, 2016 4 - Beta N/A `pytest-rage `_ pytest plugin to implement PEP712 Oct 21, 2011 3 - Alpha N/A `pytest-raises `_ An implementation of pytest.raises as a pytest.mark fixture Apr 23, 2020 N/A pytest (>=3.2.2) @@ -636,7 +638,7 @@ name `pytest-ref `_ A plugin to store reference files to ease regression testing Nov 23, 2019 4 - Beta pytest (>=3.5.0) `pytest-reference-formatter `_ Conveniently run pytest with a dot-formatted test reference. Oct 01, 2019 4 - Beta N/A `pytest-regressions `_ Easy to use fixtures to write regression tests. Jan 27, 2021 5 - Production/Stable pytest (>=3.5.0) -`pytest-regtest `_ pytest plugin for regression tests Sep 16, 2020 N/A N/A +`pytest-regtest `_ pytest plugin for regression tests Jun 03, 2021 N/A N/A `pytest-relative-order `_ a pytest plugin that sorts tests using "before" and "after" markers May 17, 2021 4 - Beta N/A `pytest-relaxed `_ Relaxed test discovery/organization for pytest Jun 14, 2019 5 - Production/Stable pytest (<5,>=3) `pytest-remfiles `_ Pytest plugin to create a temporary directory with remote files Jul 01, 2019 5 - Production/Stable N/A @@ -657,7 +659,7 @@ name `pytest-reportportal `_ Agent for Reporting results of tests to the Report Portal Feb 15, 2021 N/A pytest (>=3.0.7) `pytest-reqs `_ pytest plugin to check pinned requirements May 12, 2019 N/A pytest (>=2.4.2) `pytest-requests `_ A simple plugin to use with pytest Jun 24, 2019 4 - Beta pytest (>=3.5.0) -`pytest-reraise `_ Make multi-threaded pytest test cases fail when they should Jun 03, 2020 5 - Production/Stable N/A +`pytest-reraise `_ Make multi-threaded pytest test cases fail when they should Jun 03, 2021 5 - Production/Stable pytest (>=4.6) `pytest-rerun `_ Re-run only changed files in specified branch Jul 08, 2019 N/A pytest (>=3.6) `pytest-rerunfailures `_ pytest plugin to re-run tests to eliminate flaky failures May 26, 2021 5 - Production/Stable pytest (>=5.3) `pytest-resilient-circuits `_ Resilient Circuits fixtures for PyTest. May 14, 2021 N/A N/A @@ -687,13 +689,13 @@ name `pytest-sanic `_ a pytest plugin for Sanic May 20, 2021 N/A pytest (>=5.2) `pytest-sanity `_ Dec 07, 2020 N/A N/A `pytest-sa-pg `_ May 14, 2019 N/A N/A -`pytest-sbase `_ A complete web automation framework for end-to-end testing. May 24, 2021 5 - Production/Stable N/A +`pytest-sbase `_ A complete web automation framework for end-to-end testing. Jun 05, 2021 5 - Production/Stable N/A `pytest-scenario `_ pytest plugin for test scenarios Feb 06, 2017 3 - Alpha N/A `pytest-schema `_ 👍 Validate return values against a schema-like object in testing Aug 31, 2020 5 - Production/Stable pytest (>=3.5.0) `pytest-securestore `_ An encrypted password store for use within pytest cases Jun 19, 2019 4 - Beta N/A `pytest-select `_ A pytest plugin which allows to (de-)select tests from a file. Jan 18, 2019 3 - Alpha pytest (>=3.0) `pytest-selenium `_ pytest plugin for Selenium Sep 19, 2020 5 - Production/Stable pytest (>=5.0.0) -`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. May 24, 2021 5 - Production/Stable N/A +`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Jun 05, 2021 5 - Production/Stable N/A `pytest-selenium-enhancer `_ pytest plugin for Selenium Nov 26, 2020 5 - Production/Stable N/A `pytest-selenium-pdiff `_ A pytest package implementing perceptualdiff for Selenium tests. Apr 06, 2017 2 - Pre-Alpha N/A `pytest-send-email `_ Send pytest execution result email Dec 04, 2019 N/A N/A @@ -740,7 +742,7 @@ name `pytest-split-tests `_ A Pytest plugin for running a subset of your tests by splitting them in to equally sized groups. Forked from Mark Adams' original project pytest-test-groups. May 28, 2019 N/A pytest (>=2.5) `pytest-split-tests-tresorit `_ Feb 22, 2021 1 - Planning N/A `pytest-splunk-addon `_ A Dynamic test tool for Splunk Apps and Add-ons Apr 28, 2021 N/A pytest (>5.4.0,<6.3) -`pytest-splunk-addon-ui-smartx `_ Library to support testing Splunk Add-on UX Mar 24, 2021 N/A N/A +`pytest-splunk-addon-ui-smartx `_ Library to support testing Splunk Add-on UX May 31, 2021 N/A N/A `pytest-splunk-env `_ pytest fixtures for interaction with Splunk Enterprise and Splunk Cloud Oct 22, 2020 N/A pytest (>=6.1.1,<7.0.0) `pytest-sqitch `_ sqitch for pytest Apr 06, 2020 4 - Beta N/A `pytest-sqlalchemy `_ pytest plugin with sqlalchemy related fixtures Mar 13, 2018 3 - Alpha N/A @@ -803,7 +805,7 @@ name `pytest-timeit `_ A pytest plugin to time test function runs Oct 13, 2016 4 - Beta N/A `pytest-timeout `_ py.test plugin to abort hanging tests Jul 15, 2020 5 - Production/Stable pytest (>=3.6.0) `pytest-timeouts `_ Linux-only Pytest plugin to control durations of various test case execution phases Sep 21, 2019 5 - Production/Stable N/A -`pytest-timer `_ A timer plugin for pytest Dec 13, 2020 N/A N/A +`pytest-timer `_ A timer plugin for pytest Jun 02, 2021 N/A N/A `pytest-tipsi-django `_ Oct 14, 2020 4 - Beta pytest (>=6.0.0) `pytest-tipsi-testing `_ Better fixtures management. Various helpers Nov 04, 2020 4 - Beta pytest (>=3.3.0) `pytest-tldr `_ A pytest plugin that limits the output to just the things you need. Mar 12, 2021 4 - Beta pytest (>=3.5.0) From bce0ceafe4a7603275824e49fb62bcf20ba0325c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Jun 2021 03:01:43 +0000 Subject: [PATCH 339/630] Bump django from 3.2.3 to 3.2.4 in /testing/plugins_integration Bumps [django](https://github.com/django/django) from 3.2.3 to 3.2.4. - [Release notes](https://github.com/django/django/releases) - [Commits](https://github.com/django/django/compare/3.2.3...3.2.4) --- updated-dependencies: - dependency-name: django dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- testing/plugins_integration/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/plugins_integration/requirements.txt b/testing/plugins_integration/requirements.txt index d517481d66c..c3b6a6469ce 100644 --- a/testing/plugins_integration/requirements.txt +++ b/testing/plugins_integration/requirements.txt @@ -1,5 +1,5 @@ anyio[curio,trio]==3.1.0 -django==3.2.3 +django==3.2.4 pytest-asyncio==0.15.1 pytest-bdd==4.0.2 pytest-cov==2.12.0 From 595b7e894c54078e7f2c5d093c9a30de1ab98ee7 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 8 Jun 2021 01:55:19 +0000 Subject: [PATCH 340/630] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v2.19.0 → v2.19.1](https://github.com/asottile/pyupgrade/compare/v2.19.0...v2.19.1) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e6ea181f520..e641dd87ca6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -34,7 +34,7 @@ repos: - id: reorder-python-imports args: ['--application-directories=.:src', --py36-plus] - repo: https://github.com/asottile/pyupgrade - rev: v2.19.0 + rev: v2.19.1 hooks: - id: pyupgrade args: [--py36-plus] From 479209bde1fc76a915c06b34870a7537f23a44b7 Mon Sep 17 00:00:00 2001 From: Jason Haugen <56001173+haugenj@users.noreply.github.com> Date: Wed, 9 Jun 2021 15:13:48 -0700 Subject: [PATCH 341/630] Fix typo in fixture.rst Example referenced the wrong function name --- doc/en/reference/fixtures.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/reference/fixtures.rst b/doc/en/reference/fixtures.rst index 6d9b134a763..d90b6834828 100644 --- a/doc/en/reference/fixtures.rst +++ b/doc/en/reference/fixtures.rst @@ -410,7 +410,7 @@ example, consider this file: .. literalinclude:: /example/fixtures/test_fixtures_order_autouse_multiple_scopes.py -Even though nothing in ``TestClassWithC1Request`` is requesting ``c1``, it still +Even though nothing in ``TestClassWithoutC1Request`` is requesting ``c1``, it still is executed for the tests inside it anyway: .. image:: /example/fixtures/test_fixtures_order_autouse_multiple_scopes.svg From 58036d463d28a26b60bb221bbf9c93859b51baea Mon Sep 17 00:00:00 2001 From: Benjamin Wohlwend Date: Thu, 10 Jun 2021 09:34:54 +0200 Subject: [PATCH 342/630] updated type hints for caplog.at_level to match caplog.set_level The underlying logging API accepts the log level both as int and as string. --- src/_pytest/logging.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_pytest/logging.py b/src/_pytest/logging.py index da1de013d1c..4ab7b5e972b 100644 --- a/src/_pytest/logging.py +++ b/src/_pytest/logging.py @@ -451,7 +451,7 @@ def set_level(self, level: Union[int, str], logger: Optional[str] = None) -> Non @contextmanager def at_level( - self, level: int, logger: Optional[str] = None + self, level: Union[int, str], logger: Optional[str] = None ) -> Generator[None, None, None]: """Context manager that sets the level for capturing of logs. After the end of the 'with' statement the level is restored to its original From b04aa015b940f4fc3a15ac6b1997cace7c0c3adf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 10 Jun 2021 23:10:22 +0000 Subject: [PATCH 343/630] Bump pytest-cov from 2.12.0 to 2.12.1 in /testing/plugins_integration Bumps [pytest-cov](https://github.com/pytest-dev/pytest-cov) from 2.12.0 to 2.12.1. - [Release notes](https://github.com/pytest-dev/pytest-cov/releases) - [Changelog](https://github.com/pytest-dev/pytest-cov/blob/master/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest-cov/compare/v2.12.0...v2.12.1) --- updated-dependencies: - dependency-name: pytest-cov dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- testing/plugins_integration/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/plugins_integration/requirements.txt b/testing/plugins_integration/requirements.txt index c3b6a6469ce..e6ad8332fd3 100644 --- a/testing/plugins_integration/requirements.txt +++ b/testing/plugins_integration/requirements.txt @@ -2,7 +2,7 @@ anyio[curio,trio]==3.1.0 django==3.2.4 pytest-asyncio==0.15.1 pytest-bdd==4.0.2 -pytest-cov==2.12.0 +pytest-cov==2.12.1 pytest-django==4.3.0 pytest-flakes==4.0.3 pytest-html==3.1.1 From 85faaf8a1d18306fecdea19f9b49f280dcabac51 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 11 Jun 2021 00:18:17 +0000 Subject: [PATCH 344/630] Bump pytest-django from 4.3.0 to 4.4.0 in /testing/plugins_integration Bumps [pytest-django](https://github.com/pytest-dev/pytest-django) from 4.3.0 to 4.4.0. - [Release notes](https://github.com/pytest-dev/pytest-django/releases) - [Changelog](https://github.com/pytest-dev/pytest-django/blob/master/docs/changelog.rst) - [Commits](https://github.com/pytest-dev/pytest-django/compare/v4.3.0...v4.4.0) --- updated-dependencies: - dependency-name: pytest-django dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- testing/plugins_integration/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/plugins_integration/requirements.txt b/testing/plugins_integration/requirements.txt index e6ad8332fd3..14322d56804 100644 --- a/testing/plugins_integration/requirements.txt +++ b/testing/plugins_integration/requirements.txt @@ -3,7 +3,7 @@ django==3.2.4 pytest-asyncio==0.15.1 pytest-bdd==4.0.2 pytest-cov==2.12.1 -pytest-django==4.3.0 +pytest-django==4.4.0 pytest-flakes==4.0.3 pytest-html==3.1.1 pytest-mock==3.6.1 From c85b21eaa02ebd4fe25d73d37a9ed578ec52d2a7 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sat, 12 Jun 2021 11:38:24 -0300 Subject: [PATCH 345/630] Remove outdated docs about pytest.warns and DeprecatedWarning Since #2908, the user doesn't need to set warning filters to capture `DeprecationWarning` with `pytest.warns`. Fix #8666 --- doc/en/how-to/capture-warnings.rst | 27 +++------------------------ testing/test_recwarn.py | 11 +++++++++++ 2 files changed, 14 insertions(+), 24 deletions(-) diff --git a/doc/en/how-to/capture-warnings.rst b/doc/en/how-to/capture-warnings.rst index 28e071c45e0..134d16f0be0 100644 --- a/doc/en/how-to/capture-warnings.rst +++ b/doc/en/how-to/capture-warnings.rst @@ -173,8 +173,6 @@ DeprecationWarning and PendingDeprecationWarning ------------------------------------------------ - - By default pytest will display ``DeprecationWarning`` and ``PendingDeprecationWarning`` warnings from user code and third-party libraries, as recommended by `PEP-0565 `_. This helps users keep their code modern and avoid breakages when deprecated warnings are effectively removed. @@ -230,27 +228,8 @@ that a certain function call triggers a ``DeprecationWarning`` or This test will fail if ``myfunction`` does not issue a deprecation warning when called with a ``17`` argument. -By default, ``DeprecationWarning`` and ``PendingDeprecationWarning`` will not be -caught when using :func:`pytest.warns` or :ref:`recwarn ` because -the default Python warnings filters hide -them. If you wish to record them in your own code, use -``warnings.simplefilter('always')``: - -.. code-block:: python - - import warnings - import pytest - - - def test_deprecation(recwarn): - warnings.simplefilter("always") - myfunction(17) - assert len(recwarn) == 1 - assert recwarn.pop(DeprecationWarning) -The :ref:`recwarn ` fixture automatically ensures to reset the warnings -filter at the end of the test, so no global state is leaked. .. _`asserting warnings`: @@ -317,9 +296,9 @@ additional information: Alternatively, you can examine raised warnings in detail using the :ref:`recwarn ` fixture (see below). -.. note:: - ``DeprecationWarning`` and ``PendingDeprecationWarning`` are treated - differently; see :ref:`ensuring_function_triggers`. + +The :ref:`recwarn ` fixture automatically ensures to reset the warnings +filter at the end of the test, so no global state is leaked. .. _`recording warnings`: diff --git a/testing/test_recwarn.py b/testing/test_recwarn.py index 40a2e23fa6c..cb000d84146 100644 --- a/testing/test_recwarn.py +++ b/testing/test_recwarn.py @@ -27,6 +27,17 @@ def test_method(recwarn): reprec.assertoutcome(passed=1) +@pytest.mark.filterwarnings("") +def test_recwarn_captures_deprecation_warning(recwarn: WarningsRecorder) -> None: + """ + Check that recwarn can capture DeprecationWarning by default + without custom filterwarnings (see #8666). + """ + warnings.warn(DeprecationWarning("some deprecation")) + assert len(recwarn) == 1 + assert recwarn.pop(DeprecationWarning) + + class TestWarningsRecorderChecker: def test_recording(self) -> None: rec = WarningsRecorder(_ispytest=True) From 16685dc279650aee2f3af6cc812edf3e87160150 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sat, 12 Jun 2021 12:10:34 -0300 Subject: [PATCH 346/630] Change os.getuid() guard in tmpdir.py This matches the guard used by typeshed and avoid mypy errors on Windows: https://github.com/python/typeshed/blob/48a346920bbc24165bd9e9cf29a2b5140eae2abc/stdlib/os/__init__.pyi#L388 --- src/_pytest/tmpdir.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/_pytest/tmpdir.py b/src/_pytest/tmpdir.py index dcc37dc262f..8c513ca4652 100644 --- a/src/_pytest/tmpdir.py +++ b/src/_pytest/tmpdir.py @@ -1,6 +1,7 @@ """Support for providing temporary directories to test functions.""" import os import re +import sys import tempfile from pathlib import Path from typing import Optional @@ -130,9 +131,9 @@ def getbasetemp(self) -> Path: # Also, to keep things private, fixup any world-readable temp # rootdir's permissions. Historically 0o755 was used, so we can't # just error out on this, at least for a while. - if hasattr(os, "getuid"): - rootdir_stat = rootdir.stat() + if sys.platform != "win32": uid = os.getuid() + rootdir_stat = rootdir.stat() # getuid shouldn't fail, but cpython defines such a case. # Let's hope for the best. if uid != -1: From 5ea541d415b2be637c459b6fae55db99d1a10073 Mon Sep 17 00:00:00 2001 From: pytest bot Date: Sun, 13 Jun 2021 00:10:04 +0000 Subject: [PATCH 347/630] [automated] Update plugin list --- doc/en/reference/plugin_list.rst | 54 ++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/doc/en/reference/plugin_list.rst b/doc/en/reference/plugin_list.rst index ddbfac86723..0e8e74e49b8 100644 --- a/doc/en/reference/plugin_list.rst +++ b/doc/en/reference/plugin_list.rst @@ -6,7 +6,7 @@ Plugin List PyPI projects that match "pytest-\*" are considered plugins and are listed automatically. Packages classified as inactive are excluded. -This list contains 872 plugins. +This list contains 880 plugins. ============================================================================================================== ======================================================================================================================================================================== ============== ===================== ================================================ name summary last release status requires @@ -56,7 +56,7 @@ name `pytest-asyncio-cooperative `_ Run all your asynchronous tests cooperatively. Apr 17, 2021 4 - Beta N/A `pytest-asyncio-network-simulator `_ pytest-asyncio-network-simulator: Plugin for pytest for simulator the network in tests Jul 31, 2018 3 - Alpha pytest (<3.7.0,>=3.3.2) `pytest-async-mongodb `_ pytest plugin for async MongoDB Oct 18, 2017 5 - Production/Stable pytest (>=2.5.2) -`pytest-async-sqlalchemy `_ Database testing fixtures using the SQLAlchemy asyncio API Apr 25, 2021 4 - Beta pytest (>=6.0.0) +`pytest-async-sqlalchemy `_ Database testing fixtures using the SQLAlchemy asyncio API Jun 11, 2021 4 - Beta pytest (>=6.0.0) `pytest-atomic `_ Skip rest of tests if previous test failed. Nov 24, 2018 4 - Beta N/A `pytest-attrib `_ pytest plugin to select tests based on attributes similar to the nose-attrib plugin May 24, 2016 4 - Beta N/A `pytest-austin `_ Austin plugin for pytest Oct 11, 2020 4 - Beta N/A @@ -123,7 +123,7 @@ name `pytest-circleci `_ py.test plugin for CircleCI May 03, 2019 N/A N/A `pytest-circleci-parallelized `_ Parallelize pytest across CircleCI workers. Mar 26, 2019 N/A N/A `pytest-ckan `_ Backport of CKAN 2.9 pytest plugin and fixtures to CAKN 2.8 Apr 28, 2020 4 - Beta pytest -`pytest-clarity `_ A plugin providing an alternative, colourful diff output for failing assertions. Jan 23, 2020 3 - Alpha N/A +`pytest-clarity `_ A plugin providing an alternative, colourful diff output for failing assertions. Jun 11, 2021 N/A N/A `pytest-cldf `_ Easy quality control for CLDF datasets using pytest May 06, 2019 N/A N/A `pytest-click `_ Py.test plugin for Click Aug 29, 2020 5 - Production/Stable pytest (>=5.0) `pytest-clld `_ May 06, 2020 N/A pytest (>=3.6) @@ -138,7 +138,7 @@ name `pytest-collect-formatter `_ Formatter for pytest collect output Mar 29, 2021 5 - Production/Stable N/A `pytest-collect-formatter2 `_ Formatter for pytest collect output May 31, 2021 5 - Production/Stable N/A `pytest-colordots `_ Colorizes the progress indicators Oct 06, 2017 5 - Production/Stable N/A -`pytest-commander `_ An interactive GUI test runner for PyTest May 09, 2021 N/A pytest (<7.0.0,>=6.2.4) +`pytest-commander `_ An interactive GUI test runner for PyTest Jun 11, 2021 N/A pytest (<7.0.0,>=6.2.4) `pytest-common-subject `_ pytest framework for testing different aspects of a common method Nov 12, 2020 N/A pytest (>=3.6,<7) `pytest-concurrent `_ Concurrently execute test cases with multithread, multiprocess and gevent Jan 12, 2019 4 - Beta pytest (>=3.1.1) `pytest-config `_ Base configurations and utilities for developing your Python project test suite with pytest. Nov 07, 2014 5 - Production/Stable N/A @@ -184,7 +184,7 @@ name `pytest-datatest `_ A pytest plugin for test driven data-wrangling (this is the development version of datatest's pytest integration). Oct 15, 2020 4 - Beta pytest (>=3.3) `pytest-db `_ Session scope fixture "db" for mysql query or change Dec 04, 2019 N/A N/A `pytest-dbfixtures `_ Databases fixtures plugin for py.test. Dec 07, 2016 4 - Beta N/A -`pytest-dbt-adapter `_ A pytest plugin for testing dbt adapter plugins Jan 07, 2021 N/A pytest (<7,>=6) +`pytest-dbt-adapter `_ A pytest plugin for testing dbt adapter plugins Jun 07, 2021 N/A pytest (<7,>=6) `pytest-dbus-notification `_ D-BUS notifications for pytest results. Mar 05, 2014 5 - Production/Stable N/A `pytest-deadfixtures `_ A simple plugin to list unused fixtures in pytest Jul 23, 2020 5 - Production/Stable N/A `pytest-deepcov `_ deepcov Mar 30, 2021 N/A N/A @@ -202,7 +202,7 @@ name `pytest-disable `_ pytest plugin to disable a test and skip it from testrun Sep 10, 2015 4 - Beta N/A `pytest-disable-plugin `_ Disable plugins per test Feb 28, 2019 4 - Beta pytest (>=3.5.0) `pytest-discord `_ A pytest plugin to notify test results to a Discord channel. Mar 20, 2021 3 - Alpha pytest (!=6.0.0,<7,>=3.3.2) -`pytest-django `_ A Django plugin for pytest. May 15, 2021 5 - Production/Stable pytest (>=5.4.0) +`pytest-django `_ A Django plugin for pytest. Jun 06, 2021 5 - Production/Stable pytest (>=5.4.0) `pytest-django-ahead `_ A Django plugin for pytest. Oct 27, 2016 5 - Production/Stable pytest (>=2.9) `pytest-djangoapp `_ Nice pytest plugin to help you with Django pluggable application testing. Apr 10, 2021 4 - Beta N/A `pytest-django-cache-xdist `_ A djangocachexdist plugin for pytest May 12, 2020 4 - Beta N/A @@ -261,8 +261,13 @@ name `pytest-elements `_ Tool to help automate user interfaces Jan 13, 2021 N/A pytest (>=5.4,<6.0) `pytest-elk-reporter `_ A simple plugin to use with pytest Jan 24, 2021 4 - Beta pytest (>=3.5.0) `pytest-email `_ Send execution result email Jul 08, 2020 N/A pytest +`pytest-embedded `_ pytest embedded plugin Jun 11, 2021 N/A pytest (>=6.2.0) +`pytest-embedded-idf `_ pytest embedded plugin for esp-idf project Jun 11, 2021 N/A N/A +`pytest-embedded-qemu-idf `_ pytest embedded plugin for esp-idf project by qemu, not target chip Jun 11, 2021 N/A N/A +`pytest-embedded-serial `_ pytest embedded plugin for testing serial ports Jun 11, 2021 N/A N/A +`pytest-embedded-serial-esp `_ pytest embedded plugin for testing espressif boards via serial ports Jun 11, 2021 N/A N/A `pytest-emoji `_ A pytest plugin that adds emojis to your test result report Feb 19, 2019 4 - Beta pytest (>=4.2.1) -`pytest-emoji-output `_ Pytest plugin to represent test output with emoji support Oct 03, 2020 4 - Beta N/A +`pytest-emoji-output `_ Pytest plugin to represent test output with emoji support Jun 06, 2021 4 - Beta pytest (==6.0.1) `pytest-enabler `_ Enable installed pytest plugins Jan 19, 2021 5 - Production/Stable pytest (!=3.7.3,>=3.5) ; extra == 'testing' `pytest-enhancements `_ Improvements for pytest (rejected upstream) Oct 30, 2019 4 - Beta N/A `pytest-env `_ py.test plugin that allows you to add environment variables. Jun 16, 2017 4 - Beta N/A @@ -339,7 +344,7 @@ name `pytest-gevent `_ Ensure that gevent is properly patched when invoking pytest Feb 25, 2020 N/A pytest `pytest-gherkin `_ A flexible framework for executing BDD gherkin tests Jul 27, 2019 3 - Alpha pytest (>=5.0.0) `pytest-ghostinspector `_ For finding/executing Ghost Inspector tests May 17, 2016 3 - Alpha N/A -`pytest-girder `_ A set of pytest fixtures for testing Girder applications. May 28, 2021 N/A N/A +`pytest-girder `_ A set of pytest fixtures for testing Girder applications. Jun 08, 2021 N/A N/A `pytest-git `_ Git repository fixture for py.test May 28, 2019 5 - Production/Stable pytest `pytest-gitcov `_ Pytest plugin for reporting on coverage of the last git commit. Jan 11, 2020 2 - Pre-Alpha N/A `pytest-git-fixtures `_ Pytest fixtures for testing with git. Mar 11, 2021 4 - Beta pytest @@ -365,7 +370,7 @@ name `pytest-homeassistant `_ A pytest plugin for use with homeassistant custom components. Aug 12, 2020 4 - Beta N/A `pytest-homeassistant-custom-component `_ Experimental package to automatically extract test plugins for Home Assistant custom components Jun 04, 2021 3 - Alpha pytest (==6.2.4) `pytest-honors `_ Report on tests that honor constraints, and guard against regressions Mar 06, 2020 4 - Beta N/A -`pytest-hoverfly `_ Simplify working with Hoverfly from pytest Mar 04, 2021 N/A pytest (>=5.0) +`pytest-hoverfly `_ Simplify working with Hoverfly from pytest Jun 11, 2021 N/A pytest (>=5.0) `pytest-hoverfly-wrapper `_ Integrates the Hoverfly HTTP proxy into Pytest Jan 31, 2021 4 - Beta N/A `pytest-html `_ pytest plugin for generating HTML reports Dec 13, 2020 5 - Production/Stable pytest (!=6.0.0,>=5.0) `pytest-html-lee `_ optimized pytest plugin for generating HTML reports Jun 30, 2020 5 - Production/Stable pytest (>=5.0) @@ -444,7 +449,7 @@ name `pytest-localftpserver `_ A PyTest plugin which provides an FTP fixture for your tests Jan 27, 2021 5 - Production/Stable pytest `pytest-localserver `_ py.test plugin to test server connections locally. Nov 14, 2018 4 - Beta N/A `pytest-localstack `_ Pytest plugin for AWS integration tests Aug 22, 2019 4 - Beta pytest (>=3.3.0) -`pytest-lockable `_ lockable resource plugin for pytest May 08, 2021 5 - Production/Stable pytest +`pytest-lockable `_ lockable resource plugin for pytest Jun 08, 2021 5 - Production/Stable pytest `pytest-locker `_ Used to lock object during testing. Essentially changing assertions from being hard coded to asserting that nothing changed Feb 25, 2021 N/A pytest (>=5.4) `pytest-logbook `_ py.test plugin to capture logbook log messages Nov 23, 2015 5 - Production/Stable pytest (>=2.8) `pytest-logfest `_ Pytest plugin providing three logger fixtures with basic or full writing to log files Jul 21, 2019 4 - Beta pytest (>=3.5.0) @@ -487,7 +492,7 @@ name `pytest-modifyjunit `_ Utility for adding additional properties to junit xml for IDM QE Jan 10, 2019 N/A N/A `pytest-modifyscope `_ pytest plugin to modify fixture scope Apr 12, 2020 N/A pytest `pytest-molecule `_ PyTest Molecule Plugin :: discover and run molecule tests Jan 25, 2021 5 - Production/Stable N/A -`pytest-mongo `_ MongoDB process and client fixtures plugin for py.test. Jan 12, 2021 5 - Production/Stable pytest (>=3.0.0) +`pytest-mongo `_ MongoDB process and client fixtures plugin for Pytest. Jun 07, 2021 5 - Production/Stable pytest `pytest-mongodb `_ pytest plugin for MongoDB fixtures Dec 07, 2019 5 - Production/Stable pytest (>=2.5.2) `pytest-monitor `_ Pytest plugin for analyzing resource usage. Apr 21, 2021 5 - Production/Stable pytest `pytest-monkeyplus `_ pytest's monkeypatch subclass with extra functionalities Sep 18, 2012 5 - Production/Stable N/A @@ -499,7 +504,7 @@ name `pytest-mproc `_ low-startup-overhead, scalable, distributed-testing pytest plugin Mar 07, 2021 4 - Beta pytest `pytest-multi-check `_ Pytest-плагин, реализует возможность мульти проверок и мягких проверок Jun 03, 2021 N/A pytest `pytest-multihost `_ Utility for writing multi-host tests for pytest Apr 07, 2020 4 - Beta N/A -`pytest-multilog `_ Multi-process logs handling and other helpers for pytest Nov 15, 2020 N/A N/A +`pytest-multilog `_ Multi-process logs handling and other helpers for pytest Jun 10, 2021 N/A N/A `pytest-mutagen `_ Add the mutation testing feature to pytest Jul 24, 2020 N/A pytest (>=5.4) `pytest-mypy `_ Mypy static type checker plugin for Pytest Mar 21, 2021 4 - Beta pytest (>=3.5) `pytest-mypyd `_ Mypy static type checker plugin for Pytest Aug 20, 2019 4 - Beta pytest (<4.7,>=2.8) ; python_version < "3.5" @@ -573,7 +578,7 @@ name `pytest-platform-markers `_ Markers for pytest to skip tests on specific platforms Sep 09, 2019 4 - Beta pytest (>=3.6.0) `pytest-play `_ pytest plugin that let you automate actions and assertions with test metrics reporting executing plain YAML files Jun 12, 2019 5 - Production/Stable N/A `pytest-playbook `_ Pytest plugin for reading playbooks. Jan 21, 2021 3 - Alpha pytest (>=6.1.2,<7.0.0) -`pytest-playwright `_ A pytest wrapper with fixtures for Playwright to automate web browsers May 19, 2021 N/A pytest +`pytest-playwright `_ A pytest wrapper with fixtures for Playwright to automate web browsers Jun 07, 2021 N/A pytest `pytest-plt `_ Fixtures for quickly making Matplotlib plots in tests Aug 17, 2020 5 - Production/Stable pytest `pytest-plugin-helpers `_ A plugin to help developing and testing other plugins Nov 23, 2019 4 - Beta pytest (>=3.5.0) `pytest-plus `_ PyTest Plus Plugin :: extends pytest functionality Mar 19, 2020 5 - Production/Stable pytest (>=3.50) @@ -613,8 +618,9 @@ name `pytest-pytestrail `_ Pytest plugin for interaction with TestRail Aug 27, 2020 4 - Beta pytest (>=3.8.0) `pytest-pythonpath `_ pytest plugin for adding to the PYTHONPATH from command line or configs. Aug 22, 2018 5 - Production/Stable N/A `pytest-pytorch `_ pytest plugin for a better developer experience when working with the PyTorch test suite May 25, 2021 4 - Beta pytest +`pytest-qatouch `_ Pytest plugin for uploading test results to your QA Touch Testrun. Jun 10, 2021 4 - Beta pytest (>=6.2.0) `pytest-qml `_ Run QML Tests with pytest Dec 02, 2020 4 - Beta pytest (>=6.0.0) -`pytest-qt `_ pytest support for PyQt and PySide applications Jun 03, 2021 5 - Production/Stable pytest (>=3.0.0) +`pytest-qt `_ pytest support for PyQt and PySide applications Jun 07, 2021 5 - Production/Stable pytest (>=3.0.0) `pytest-qt-app `_ QT app fixture for py.test Dec 23, 2015 5 - Production/Stable N/A `pytest-quarantine `_ A plugin for pytest to manage expected test failures Nov 24, 2019 5 - Production/Stable pytest (>=4.6) `pytest-quickcheck `_ pytest plugin to generate random data inspired by QuickCheck Nov 15, 2020 4 - Beta pytest (<6.0.0,>=4.0) @@ -630,7 +636,7 @@ name `pytest-random-num `_ Randomise the order in which pytest tests are run with some control over the randomness Oct 19, 2020 5 - Production/Stable N/A `pytest-random-order `_ Randomise the order in which pytest tests are run with some control over the randomness Nov 30, 2018 5 - Production/Stable pytest (>=3.0.0) `pytest-readme `_ Test your README.md file Dec 28, 2014 5 - Production/Stable N/A -`pytest-reana `_ Pytest fixtures for REANA. May 03, 2021 3 - Alpha N/A +`pytest-reana `_ Pytest fixtures for REANA. Jun 07, 2021 3 - Alpha N/A `pytest-recording `_ A pytest plugin that allows you recording of network interactions via VCR.py Nov 25, 2020 4 - Beta pytest (>=3.5.0) `pytest-recordings `_ Provides pytest plugins for reporting request/response traffic, screenshots, and more to ReportPortal Aug 13, 2020 N/A N/A `pytest-redis `_ Redis fixtures and fixture factories for Pytest. May 25, 2021 5 - Production/Stable pytest @@ -646,20 +652,20 @@ name `pytest-remove-stale-bytecode `_ py.test plugin to remove stale byte code files. Mar 04, 2020 4 - Beta pytest `pytest-reorder `_ Reorder tests depending on their paths and names. May 31, 2018 4 - Beta pytest `pytest-repeat `_ pytest plugin for repeating tests Oct 31, 2020 5 - Production/Stable pytest (>=3.6) -`pytest-replay `_ Saves previous test runs and allow re-execute previous pytest runs to reproduce crashes or flaky tests Dec 09, 2020 4 - Beta pytest (>=3.0.0) +`pytest-replay `_ Saves previous test runs and allow re-execute previous pytest runs to reproduce crashes or flaky tests Jun 09, 2021 4 - Beta pytest (>=3.0.0) `pytest-repo-health `_ A pytest plugin to report on repository standards conformance Nov 03, 2020 3 - Alpha pytest `pytest-report `_ Creates json report that is compatible with atom.io's linter message format May 11, 2016 4 - Beta N/A `pytest-reporter `_ Generate Pytest reports with templates Nov 05, 2020 4 - Beta pytest -`pytest-reporter-html1 `_ A basic HTML report template for Pytest Nov 02, 2020 4 - Beta N/A +`pytest-reporter-html1 `_ A basic HTML report template for Pytest Jun 08, 2021 4 - Beta N/A `pytest-reportinfra `_ Pytest plugin for reportinfra Aug 11, 2019 3 - Alpha N/A `pytest-reporting `_ A plugin to report summarized results in a table format Oct 25, 2019 4 - Beta pytest (>=3.5.0) `pytest-reportlog `_ Replacement for the --resultlog option, focused in simplicity and extensibility Dec 11, 2020 3 - Alpha pytest (>=5.2) `pytest-report-me `_ A pytest plugin to generate report. Dec 31, 2020 N/A pytest `pytest-report-parameters `_ pytest plugin for adding tests' parameters to junit report Jun 18, 2020 3 - Alpha pytest (>=2.4.2) -`pytest-reportportal `_ Agent for Reporting results of tests to the Report Portal Feb 15, 2021 N/A pytest (>=3.0.7) +`pytest-reportportal `_ Agent for Reporting results of tests to the Report Portal Jun 09, 2021 N/A pytest (>=3.8.0) `pytest-reqs `_ pytest plugin to check pinned requirements May 12, 2019 N/A pytest (>=2.4.2) `pytest-requests `_ A simple plugin to use with pytest Jun 24, 2019 4 - Beta pytest (>=3.5.0) -`pytest-reraise `_ Make multi-threaded pytest test cases fail when they should Jun 03, 2021 5 - Production/Stable pytest (>=4.6) +`pytest-reraise `_ Make multi-threaded pytest test cases fail when they should Jun 09, 2021 5 - Production/Stable pytest (>=4.6) `pytest-rerun `_ Re-run only changed files in specified branch Jul 08, 2019 N/A pytest (>=3.6) `pytest-rerunfailures `_ pytest plugin to re-run tests to eliminate flaky failures May 26, 2021 5 - Production/Stable pytest (>=5.3) `pytest-resilient-circuits `_ Resilient Circuits fixtures for PyTest. May 14, 2021 N/A N/A @@ -683,7 +689,7 @@ name `pytest-runner `_ Invoke py.test as distutils command with dependency resolution May 19, 2021 5 - Production/Stable pytest (>=4.6) ; extra == 'testing' `pytest-salt `_ Pytest Salt Plugin Jan 27, 2020 4 - Beta N/A `pytest-salt-containers `_ A Pytest plugin that builds and creates docker containers Nov 09, 2016 4 - Beta N/A -`pytest-salt-factories `_ Pytest Salt Plugin May 27, 2021 4 - Beta pytest (>=6.0.0) +`pytest-salt-factories `_ Pytest Salt Plugin Jun 11, 2021 4 - Beta pytest (>=6.0.0) `pytest-salt-from-filenames `_ Simple PyTest Plugin For Salt's Test Suite Specifically Jan 29, 2019 4 - Beta pytest (>=4.1) `pytest-salt-runtests-bridge `_ Simple PyTest Plugin For Salt's Test Suite Specifically Dec 05, 2019 4 - Beta pytest (>=4.1) `pytest-sanic `_ a pytest plugin for Sanic May 20, 2021 N/A pytest (>=5.2) @@ -713,6 +719,7 @@ name `pytest-sherlock `_ pytest plugin help to find coupled tests Jul 13, 2020 5 - Production/Stable pytest (>=3.5.1) `pytest-shortcuts `_ Expand command-line shortcuts listed in pytest configuration Oct 29, 2020 4 - Beta pytest (>=3.5.0) `pytest-shutil `_ A goodie-bag of unix shell and environment tools for py.test May 28, 2019 5 - Production/Stable pytest +`pytest-simplehttpserver `_ Simple pytest fixture to spin up an HTTP server Jun 12, 2021 4 - Beta N/A `pytest-simple-plugin `_ Simple pytest plugin Nov 27, 2019 N/A N/A `pytest-simple-settings `_ simple-settings plugin for pytest Nov 17, 2020 4 - Beta pytest `pytest-single-file-logging `_ Allow for multiple processes to log to a single file May 05, 2016 4 - Beta pytest (>=2.8.1) @@ -737,12 +744,12 @@ name `pytest-sphinx `_ Doctest plugin for pytest with support for Sphinx-specific doctest-directives Aug 05, 2020 4 - Beta N/A `pytest-spiratest `_ Exports unit tests as test runs in SpiraTest/Team/Plan Apr 28, 2021 N/A N/A `pytest-splinter `_ Splinter plugin for pytest testing framework Dec 25, 2020 6 - Mature N/A -`pytest-split `_ Pytest plugin for splitting test suite based on test execution time Apr 07, 2020 1 - Planning N/A +`pytest-split `_ Pytest plugin for splitting test suite based on test execution time Jun 08, 2021 4 - Beta N/A `pytest-splitio `_ Split.io SDK integration for e2e tests Sep 22, 2020 N/A pytest (<7,>=5.0) `pytest-split-tests `_ A Pytest plugin for running a subset of your tests by splitting them in to equally sized groups. Forked from Mark Adams' original project pytest-test-groups. May 28, 2019 N/A pytest (>=2.5) `pytest-split-tests-tresorit `_ Feb 22, 2021 1 - Planning N/A `pytest-splunk-addon `_ A Dynamic test tool for Splunk Apps and Add-ons Apr 28, 2021 N/A pytest (>5.4.0,<6.3) -`pytest-splunk-addon-ui-smartx `_ Library to support testing Splunk Add-on UX May 31, 2021 N/A N/A +`pytest-splunk-addon-ui-smartx `_ Library to support testing Splunk Add-on UX Jun 10, 2021 N/A N/A `pytest-splunk-env `_ pytest fixtures for interaction with Splunk Enterprise and Splunk Cloud Oct 22, 2020 N/A pytest (>=6.1.1,<7.0.0) `pytest-sqitch `_ sqitch for pytest Apr 06, 2020 4 - Beta N/A `pytest-sqlalchemy `_ pytest plugin with sqlalchemy related fixtures Mar 13, 2018 3 - Alpha N/A @@ -795,7 +802,7 @@ name `pytest-testrail-e2e `_ pytest plugin for creating TestRail runs and adding results Jun 11, 2020 N/A pytest (>=3.6) `pytest-testrail-plugin `_ PyTest plugin for TestRail Apr 21, 2020 3 - Alpha pytest `pytest-testrail-reporter `_ Sep 10, 2018 N/A N/A -`pytest-testreport `_ May 18, 2021 4 - Beta pytest (>=3.5.0) +`pytest-testreport `_ Jun 10, 2021 4 - Beta pytest (>=3.5.0) `pytest-testslide `_ TestSlide fixture for pytest Jan 07, 2021 5 - Production/Stable pytest (~=6.2) `pytest-test-this `_ Plugin for py.test to run relevant tests, based on naively checking if a test contains a reference to the symbol you supply Sep 15, 2019 2 - Pre-Alpha pytest (>=2.3) `pytest-tesults `_ Tesults plugin for pytest May 18, 2020 5 - Production/Stable pytest (>=3.5.0) @@ -806,6 +813,7 @@ name `pytest-timeout `_ py.test plugin to abort hanging tests Jul 15, 2020 5 - Production/Stable pytest (>=3.6.0) `pytest-timeouts `_ Linux-only Pytest plugin to control durations of various test case execution phases Sep 21, 2019 5 - Production/Stable N/A `pytest-timer `_ A timer plugin for pytest Jun 02, 2021 N/A N/A +`pytest-timestamper `_ Pytest plugin to add a timestamp prefix to the pytest output Jun 06, 2021 N/A N/A `pytest-tipsi-django `_ Oct 14, 2020 4 - Beta pytest (>=6.0.0) `pytest-tipsi-testing `_ Better fixtures management. Various helpers Nov 04, 2020 4 - Beta pytest (>=3.3.0) `pytest-tldr `_ A pytest plugin that limits the output to just the things you need. Mar 12, 2021 4 - Beta pytest (>=3.5.0) From 7eb0792cba262ef2e659525e2e9f93742169efab Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 14 Jun 2021 08:28:45 -0300 Subject: [PATCH 348/630] Add pytest.version_tuple This adds `pytest.version_tuple`, which makes it simpler for users to do something depending on the pytest version, such as declaring hooks which are introduced in later versions. This feature was added originally in https://github.com/pypa/setuptools_scm/pull/475. Followup to https://github.com/pytest-dev/pytest/pull/7605. --- changelog/8761.feature.rst | 1 + doc/en/reference/reference.rst | 31 +++++++++++++++++++++++++++++++ src/_pytest/__init__.py | 7 ++++--- src/pytest/__init__.py | 2 ++ testing/test_helpconfig.py | 6 ++++++ 5 files changed, 44 insertions(+), 3 deletions(-) create mode 100644 changelog/8761.feature.rst diff --git a/changelog/8761.feature.rst b/changelog/8761.feature.rst new file mode 100644 index 00000000000..88288c81002 --- /dev/null +++ b/changelog/8761.feature.rst @@ -0,0 +1 @@ +New :ref:`version-tuple` attribute, which makes it simpler for users to do something depending on the pytest version (such as declaring hooks which are introduced in later versions). diff --git a/doc/en/reference/reference.rst b/doc/en/reference/reference.rst index 153dad903b7..1fe0563ee65 100644 --- a/doc/en/reference/reference.rst +++ b/doc/en/reference/reference.rst @@ -226,6 +226,37 @@ Marks a test function as *expected to fail*. a new release of a library fixes a known bug). +pytest.__version__ +~~~~~~~~~~~~~~~~~~ + +The current pytest version, as a string:: + + >>> import pytest + >>> pytest.__version__ + '7.0.0' + + +.. _`version-tuple`: + +pytest.version_tuple +~~~~~~~~~~~~~~~~~~~~ + +.. versionadded:: 7.0 + +The current pytest version, as a tuple:: + + >>> import pytest + >>> pytest.version_tuple + (7, 0, 0) + +For pre-releases, the last component will be a string with the prerelease version:: + + >>> import pytest + >>> pytest.version_tuple + (7, 0, '0rc1') + + + Custom marks ~~~~~~~~~~~~ diff --git a/src/_pytest/__init__.py b/src/_pytest/__init__.py index 46c7827ed5e..8a406c5c751 100644 --- a/src/_pytest/__init__.py +++ b/src/_pytest/__init__.py @@ -1,8 +1,9 @@ -__all__ = ["__version__"] +__all__ = ["__version__", "version_tuple"] try: - from ._version import version as __version__ -except ImportError: + from ._version import version as __version__, version_tuple +except ImportError: # pragma: no cover # broken installation, we don't even try # unknown only works because we do poor mans version compare __version__ = "unknown" + version_tuple = (0, 0, "unknown") # type:ignore[assignment] diff --git a/src/pytest/__init__.py b/src/pytest/__init__.py index d8169b8080f..3694f0fc471 100644 --- a/src/pytest/__init__.py +++ b/src/pytest/__init__.py @@ -2,6 +2,7 @@ """pytest: unit and functional testing with Python.""" from . import collect from _pytest import __version__ +from _pytest import version_tuple from _pytest._code import ExceptionInfo from _pytest.assertion import register_assert_rewrite from _pytest.cacheprovider import Cache @@ -130,6 +131,7 @@ "Session", "set_trace", "skip", + "version_tuple", "TempPathFactory", "Testdir", "TempdirFactory", diff --git a/testing/test_helpconfig.py b/testing/test_helpconfig.py index 571a4783e67..e2a27b4e706 100644 --- a/testing/test_helpconfig.py +++ b/testing/test_helpconfig.py @@ -19,6 +19,12 @@ def test_version_less_verbose(pytester: Pytester, pytestconfig, monkeypatch) -> result.stderr.fnmatch_lines([f"pytest {pytest.__version__}"]) +def test_versions(): + """Regression check for the public version attributes in pytest.""" + assert isinstance(pytest.__version__, str) + assert isinstance(pytest.version_tuple, tuple) + + def test_help(pytester: Pytester) -> None: result = pytester.runpytest("--help") assert result.ret == 0 From 605fe29d12d62362f9a523f8c46774f9c7b0add2 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 14 Jun 2021 17:42:33 +0000 Subject: [PATCH 349/630] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black: 21.5b2 → 21.6b0](https://github.com/psf/black/compare/21.5b2...21.6b0) - [github.com/asottile/pyupgrade: v2.19.1 → v2.19.4](https://github.com/asottile/pyupgrade/compare/v2.19.1...v2.19.4) - [github.com/pre-commit/pygrep-hooks: v1.8.0 → v1.9.0](https://github.com/pre-commit/pygrep-hooks/compare/v1.8.0...v1.9.0) - [github.com/pre-commit/mirrors-mypy: v0.812 → v0.902](https://github.com/pre-commit/mirrors-mypy/compare/v0.812...v0.902) --- .pre-commit-config.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e641dd87ca6..b01d9c5d6fa 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/psf/black - rev: 21.5b2 + rev: 21.6b0 hooks: - id: black args: [--safe, --quiet] @@ -34,7 +34,7 @@ repos: - id: reorder-python-imports args: ['--application-directories=.:src', --py36-plus] - repo: https://github.com/asottile/pyupgrade - rev: v2.19.1 + rev: v2.19.4 hooks: - id: pyupgrade args: [--py36-plus] @@ -44,11 +44,11 @@ repos: - id: setup-cfg-fmt args: [--max-py-version=3.10] - repo: https://github.com/pre-commit/pygrep-hooks - rev: v1.8.0 + rev: v1.9.0 hooks: - id: python-use-type-annotations - repo: https://github.com/pre-commit/mirrors-mypy - rev: v0.812 + rev: v0.902 hooks: - id: mypy files: ^(src/|testing/) From c4da6fff427191989d0199dc9039cbad1e822fa8 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Sat, 19 Jun 2021 21:31:17 -0700 Subject: [PATCH 350/630] Add missing types-* stub packages --- .pre-commit-config.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b01d9c5d6fa..0490ae968c2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -58,6 +58,8 @@ repos: - py>=1.8.2 - attrs>=19.2.0 - packaging + - types-toml + - types-pkg_resources - repo: local hooks: - id: rst From a1967e95940f04808b704cf3d678349f6278be6e Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Sat, 19 Jun 2021 21:41:00 -0700 Subject: [PATCH 351/630] fix overload of __getitem__ for Traceback --- src/_pytest/_code/code.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/_pytest/_code/code.py b/src/_pytest/_code/code.py index 2fdd8a086cf..bd2132a9a22 100644 --- a/src/_pytest/_code/code.py +++ b/src/_pytest/_code/code.py @@ -48,6 +48,7 @@ if TYPE_CHECKING: from typing_extensions import Literal + from typing_extensions import SupportsIndex from weakref import ReferenceType _TracebackStyle = Literal["long", "short", "line", "no", "native", "value", "auto"] @@ -371,14 +372,16 @@ def cut( return self @overload - def __getitem__(self, key: int) -> TracebackEntry: + def __getitem__(self, key: "SupportsIndex") -> TracebackEntry: ... @overload def __getitem__(self, key: slice) -> "Traceback": ... - def __getitem__(self, key: Union[int, slice]) -> Union[TracebackEntry, "Traceback"]: + def __getitem__( + self, key: Union["SupportsIndex", slice] + ) -> Union[TracebackEntry, "Traceback"]: if isinstance(key, slice): return self.__class__(super().__getitem__(key)) else: From ab3cd644dcd9404aef045110cbcca59ac1a0e928 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Sun, 20 Jun 2021 08:09:18 -0700 Subject: [PATCH 352/630] add temporary ignore for FileHandler stream close --- src/_pytest/logging.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/_pytest/logging.py b/src/_pytest/logging.py index 4ab7b5e972b..8b4865b5d87 100644 --- a/src/_pytest/logging.py +++ b/src/_pytest/logging.py @@ -626,7 +626,8 @@ def set_log_path(self, fname: str) -> None: finally: self.log_file_handler.release() if old_stream: - old_stream.close() + # https://github.com/python/typeshed/pull/5663 + old_stream.close() # type:ignore[attr-defined] def _log_cli_enabled(self): """Return whether live logging is enabled.""" From d2886b8d235c1763aa08059ff562ff1c019061fc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Jun 2021 03:01:37 +0000 Subject: [PATCH 353/630] Bump anyio[curio,trio] in /testing/plugins_integration Bumps [anyio[curio,trio]](https://github.com/agronholm/anyio) from 3.1.0 to 3.2.0. - [Release notes](https://github.com/agronholm/anyio/releases) - [Changelog](https://github.com/agronholm/anyio/blob/master/docs/versionhistory.rst) - [Commits](https://github.com/agronholm/anyio/compare/3.1.0...3.2.0) --- updated-dependencies: - dependency-name: anyio[curio,trio] dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- testing/plugins_integration/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/plugins_integration/requirements.txt b/testing/plugins_integration/requirements.txt index 14322d56804..5038995b5ae 100644 --- a/testing/plugins_integration/requirements.txt +++ b/testing/plugins_integration/requirements.txt @@ -1,4 +1,4 @@ -anyio[curio,trio]==3.1.0 +anyio[curio,trio]==3.2.0 django==3.2.4 pytest-asyncio==0.15.1 pytest-bdd==4.0.2 From ac10fe0679aa44d96aeff7e3b8df56a350edeb38 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 22 Jun 2021 13:25:52 +0200 Subject: [PATCH 354/630] tests: Use less conflicting name for directory Otherwise, if e.g. /outside is used for a Docker container, the test will fail --- testing/test_nodes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/test_nodes.py b/testing/test_nodes.py index 4d7e6cba2ae..9104c1c0588 100644 --- a/testing/test_nodes.py +++ b/testing/test_nodes.py @@ -88,7 +88,7 @@ class FakeSession2: assert nodes._check_initialpaths_for_relpath(session, sub) == "file" - outside = Path("/outside") + outside = Path("/outside-this-does-not-exist") assert nodes._check_initialpaths_for_relpath(session, outside) is None From e44300de7e2354c1e449ce6145627d27af60e15b Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 23 Jun 2021 20:26:08 +0200 Subject: [PATCH 355/630] doc: Add history page (#8784) Based on #8667 --- doc/en/contents.rst | 1 + doc/en/history.rst | 147 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 148 insertions(+) create mode 100644 doc/en/history.rst diff --git a/doc/en/contents.rst b/doc/en/contents.rst index e1854d800af..4623d681880 100644 --- a/doc/en/contents.rst +++ b/doc/en/contents.rst @@ -95,6 +95,7 @@ Further topics license contact + history historical-notes talks projects diff --git a/doc/en/history.rst b/doc/en/history.rst new file mode 100644 index 00000000000..1bc8942a9cf --- /dev/null +++ b/doc/en/history.rst @@ -0,0 +1,147 @@ +History +======= + +pytest has a long and interesting history. The `first commit +`__ +in this repository is from January 2007, and even that commit alone already +tells a lot: The repository originally was from the `py +`__ library (later split off to pytest), and it +originally was a SVN revision, migrated to Mercurial, and finally migrated to +git. + +However, the commit says “create the new development trunk” and is +already quite big: *435 files changed, 58640 insertions(+)*. This is because +pytest originally was born as part of `PyPy `__, to make +it easier to write tests for it. Here's how it evolved from there to its own +project: + + +- Late 2002 / early 2003, `PyPy was + born `__. +- Like that blog post mentioned, from very early on, there was a big + focus on testing. There were various ``testsupport`` files on top of + unittest.py, and as early as June 2003, Holger Krekel (`@hpk42 `__) + `refactored `__ + its test framework to clean things up (``pypy.tool.test``, but still + on top of ``unittest.py``, with nothing pytest-like yet). +- In December 2003, there was `another + iteration `__ + at improving their testing situation, by Stefan Schwarzer, called + ``pypy.tool.newtest``. +- However, it didn’t seem to be around for long, as around June/July + 2004, efforts started on a thing called ``utest``, offering plain + assertions. This seems like the start of something pytest-like, but + unfortunately, it's unclear where the test runner's code was at the time. + The closest thing still around is `this + file `__, + but that doesn’t seem like a complete test runner at all. What can be seen + is that there were `various + efforts `__ + by Laura Creighton and Samuele Pedroni (`@pedronis `__) at automatically + converting existing tests to the new ``utest`` framework. +- Around the same time, for Europython 2004, @hpk42 `started a + project `__ + originally called “std”, intended to be a “complementary standard + library” - already laying out the principles behind what later became + pytest: + + - current “batteries included” are very useful, but + + - some of them are written in a pretty much java-like style, + especially the unittest-framework + - […] + - the best API is one that doesn’t exist + + […] + + - a testing package should require as few boilerplate code as + possible and offer much flexibility + - it should provide premium quality tracebacks and debugging aid + + […] + + - first of all … forget about limited “assertXYZ APIs” and use the + real thing, e.g.:: + + assert x == y + + - this works with plain python but you get unhelpful “assertion + failed” errors with no information + + - std.utest (magic!) actually reinterprets the assertion expression + and offers detailed information about underlying values + +- In September 2004, the ``py-dev`` mailinglist gets born, which `is + now `__ ``pytest-dev``, + but thankfully with all the original archives still intact. + +- Around September/October 2004, the ``std`` project `was renamed + `__ to + ``py`` and ``std.utest`` became ``py.test``. This is also the first time the + `entire source + code `__, + seems to be available, with much of the API still being around today: + + - ``py.path.local``, which is being phased out of pytest (in favour of + pathlib) some 16-17 years later + - The idea of the collection tree, including ``Collector``, + ``FSCollector``, ``Directory``, ``PyCollector``, ``Module``, + ``Class`` + - Arguments like ``-x`` / ``--exitfirst``, ``-l`` / + ``--showlocals``, ``--fulltrace``, ``--pdb``, ``-S`` / + ``--nocapture`` (``-s`` / ``--capture=off`` today), + ``--collectonly`` (``--collect-only`` today) + +- In the same month, the ``py`` library `gets split off + `__ + from ``PyPy`` + +- It seemed to get rather quiet for a while, and little seemed to happen + between October 2004 (removing ``py`` from PyPy) and January + 2007 (first commit in the now-pytest repository). However, there were + various discussions about features/ideas on the mailinglist, and `a + couple of + releases `__ every + couple of months: + + - March 2006: py 0.8.0-alpha2 + - May 2007: py 0.9.0 + - March 2008: py 0.9.1 (first release to be found `in the pytest + changelog `__!) + - August 2008: py 0.9.2 + +- In August 2009, py 1.0.0 was released, `introducing a lot of + fundamental + features `__: + + - funcargs/fixtures + - A `plugin + architecture `__ + which still looks very much the same today! + - Various `default + plugins `__, + including + `monkeypatch `__ + +- Even back there, the + `FAQ `__ + said: + + Clearly, [a second standard library] was ambitious and the naming has + maybe haunted the project rather than helping it. There may be a + project name change and possibly a split up into different projects + sometime. + + and that finally happened in November 2010, when pytest 2.0.0 `was + released `__ + as a package separate from ``py`` (but still called ``py.test``). + +- In August 2016, pytest 3.0.0 `was + released `__, + which adds ``pytest`` (rather than ``py.test``) as the recommended + command-line entry point + +Due to this history, it's diffcult to answer the question when pytest was started. +It depends what point should really be seen as the start of it all. One +possible interpretation is to pick Europython 2004, i.e. around June/July +2004. From f573b56bb6309fd3c761a5697ce0d3d48a4bf0f8 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 23 Jun 2021 20:28:09 +0200 Subject: [PATCH 356/630] Improve cache test and fix it in Docker (#8785) * cache: Move repetitive code to fixture * cache: Explicitly test for chmod result * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix lint Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- testing/test_cacheprovider.py | 78 ++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 38 deletions(-) diff --git a/testing/test_cacheprovider.py b/testing/test_cacheprovider.py index e631e4ad854..e43d4d94bfb 100644 --- a/testing/test_cacheprovider.py +++ b/testing/test_cacheprovider.py @@ -1,7 +1,7 @@ import os import shutil -import sys from pathlib import Path +from typing import Generator from typing import List import pytest @@ -44,52 +44,54 @@ def test_cache_writefail_cachfile_silent(self, pytester: Pytester) -> None: assert cache is not None cache.set("test/broken", []) - @pytest.mark.skipif(sys.platform.startswith("win"), reason="no chmod on windows") - @pytest.mark.filterwarnings( - "ignore:could not create cache path:pytest.PytestWarning" - ) - def test_cache_writefail_permissions(self, pytester: Pytester) -> None: - pytester.makeini("[pytest]") + @pytest.fixture + def unwritable_cache_dir(self, pytester: Pytester) -> Generator[Path, None, None]: cache_dir = pytester.path.joinpath(".pytest_cache") cache_dir.mkdir() mode = cache_dir.stat().st_mode cache_dir.chmod(0) - try: - config = pytester.parseconfigure() - cache = config.cache - assert cache is not None - cache.set("test/broken", []) - finally: - cache_dir.chmod(mode) + if os.access(cache_dir, os.W_OK): + pytest.skip("Failed to make cache dir unwritable") + + yield cache_dir + cache_dir.chmod(mode) + + @pytest.mark.filterwarnings( + "ignore:could not create cache path:pytest.PytestWarning" + ) + def test_cache_writefail_permissions( + self, unwritable_cache_dir: Path, pytester: Pytester + ) -> None: + pytester.makeini("[pytest]") + config = pytester.parseconfigure() + cache = config.cache + assert cache is not None + cache.set("test/broken", []) - @pytest.mark.skipif(sys.platform.startswith("win"), reason="no chmod on windows") @pytest.mark.filterwarnings("default") def test_cache_failure_warns( - self, pytester: Pytester, monkeypatch: MonkeyPatch + self, + pytester: Pytester, + monkeypatch: MonkeyPatch, + unwritable_cache_dir: Path, ) -> None: monkeypatch.setenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD", "1") - cache_dir = pytester.path.joinpath(".pytest_cache") - cache_dir.mkdir() - mode = cache_dir.stat().st_mode - cache_dir.chmod(0) - try: - pytester.makepyfile("def test_error(): raise Exception") - result = pytester.runpytest() - assert result.ret == 1 - # warnings from nodeids, lastfailed, and stepwise - result.stdout.fnmatch_lines( - [ - # Validate location/stacklevel of warning from cacheprovider. - "*= warnings summary =*", - "*/cacheprovider.py:*", - " */cacheprovider.py:*: PytestCacheWarning: could not create cache path " - "{}/v/cache/nodeids".format(cache_dir), - ' config.cache.set("cache/nodeids", sorted(self.cached_nodeids))', - "*1 failed, 3 warnings in*", - ] - ) - finally: - cache_dir.chmod(mode) + + pytester.makepyfile("def test_error(): raise Exception") + result = pytester.runpytest() + assert result.ret == 1 + # warnings from nodeids, lastfailed, and stepwise + result.stdout.fnmatch_lines( + [ + # Validate location/stacklevel of warning from cacheprovider. + "*= warnings summary =*", + "*/cacheprovider.py:*", + " */cacheprovider.py:*: PytestCacheWarning: could not create cache path " + f"{unwritable_cache_dir}/v/cache/nodeids", + ' config.cache.set("cache/nodeids", sorted(self.cached_nodeids))', + "*1 failed, 3 warnings in*", + ] + ) def test_config_cache(self, pytester: Pytester) -> None: pytester.makeconftest( From 942789bace9b52a31fbddc21dff004d85684dae6 Mon Sep 17 00:00:00 2001 From: James Bourbeau Date: Wed, 23 Jun 2021 14:34:48 -0500 Subject: [PATCH 357/630] Fix API links in 'How to capture warnings' documentation page (#8792) --- AUTHORS | 1 + doc/en/how-to/capture-warnings.rst | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/AUTHORS b/AUTHORS index 7f5af5d9241..f979fee0683 100644 --- a/AUTHORS +++ b/AUTHORS @@ -144,6 +144,7 @@ Iwan Briquemont Jaap Broekhuizen Jakob van Santen Jakub Mitoraj +James Bourbeau Jan Balster Janne Vanhala Jason R. Coombs diff --git a/doc/en/how-to/capture-warnings.rst b/doc/en/how-to/capture-warnings.rst index 134d16f0be0..cfee585cbb1 100644 --- a/doc/en/how-to/capture-warnings.rst +++ b/doc/en/how-to/capture-warnings.rst @@ -244,7 +244,7 @@ Asserting warnings with the warns function -You can check that code raises a particular warning using func:`pytest.warns`, +You can check that code raises a particular warning using :func:`pytest.warns`, which works in a similar manner to :ref:`raises `: .. code-block:: python @@ -272,7 +272,7 @@ argument ``match`` to assert that the exception matches a text or regex:: ... Failed: DID NOT WARN. No warnings of type ...UserWarning... was emitted... -You can also call func:`pytest.warns` on a function or code string: +You can also call :func:`pytest.warns` on a function or code string: .. code-block:: python @@ -307,10 +307,10 @@ filter at the end of the test, so no global state is leaked. Recording warnings ------------------ -You can record raised warnings either using func:`pytest.warns` or with +You can record raised warnings either using :func:`pytest.warns` or with the ``recwarn`` fixture. -To record with func:`pytest.warns` without asserting anything about the warnings, +To record with :func:`pytest.warns` without asserting anything about the warnings, pass no arguments as the expected warning type and it will default to a generic Warning: .. code-block:: python @@ -339,7 +339,7 @@ The ``recwarn`` fixture will record warnings for the whole function: assert w.filename assert w.lineno -Both ``recwarn`` and func:`pytest.warns` return the same interface for recorded +Both ``recwarn`` and :func:`pytest.warns` return the same interface for recorded warnings: a WarningsRecorder instance. To view the recorded warnings, you can iterate over this instance, call ``len`` on it to get the number of recorded warnings, or index into it to get a particular recorded warning. From d7b0e172052d855afe444c599330c907cdc53d93 Mon Sep 17 00:00:00 2001 From: Ronny Pfannschmidt Date: Thu, 24 Jun 2021 11:45:32 +0200 Subject: [PATCH 358/630] issue a warning when Item and Collector are used in diamond inheritance (#8447) * issue a warning when Items and Collector form a diamond addresses #8435 * Apply suggestions from code review Co-authored-by: Ran Benita * Return support for the broken File/Item hybrids * adds deprecation * ads necessary support code in node construction * fix incorrect mypy based assertions * add docs for deprecation of Item/File inheritance * warn when a non-cooperative ctor is encountered * use getattr instead of cast to get the class __init__ for legacy ctors * update documentation references for node inheritance * clean up file+item inheritance test enhance docs move import upwards Co-authored-by: Ran Benita --- changelog/8447.deprecation.rst | 4 ++ doc/en/deprecations.rst | 14 ++++++ src/_pytest/main.py | 2 +- src/_pytest/nodes.py | 88 +++++++++++++++++++++++++++------- testing/test_nodes.py | 31 ++++++++++++ 5 files changed, 122 insertions(+), 17 deletions(-) create mode 100644 changelog/8447.deprecation.rst diff --git a/changelog/8447.deprecation.rst b/changelog/8447.deprecation.rst new file mode 100644 index 00000000000..d1011f8c849 --- /dev/null +++ b/changelog/8447.deprecation.rst @@ -0,0 +1,4 @@ +Defining a custom pytest node type which is both an item and a collector now issues a warning. +It was never sanely supported and triggers hard to debug errors. + +Instead, a separate collector node should be used, which collects the item. See :ref:`non-python tests` for an example. diff --git a/doc/en/deprecations.rst b/doc/en/deprecations.rst index c3020fb9498..cf501d50921 100644 --- a/doc/en/deprecations.rst +++ b/doc/en/deprecations.rst @@ -42,6 +42,20 @@ As pytest tries to move off `py.path.local None: @classmethod def from_config(cls, config: Config) -> "Session": - session: Session = cls._create(config) + session: Session = cls._create(config=config) return session def __repr__(self) -> str: diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py index 40bc4d4965b..4d12f07a27a 100644 --- a/src/_pytest/nodes.py +++ b/src/_pytest/nodes.py @@ -1,8 +1,10 @@ import os import warnings +from inspect import signature from pathlib import Path from typing import Any from typing import Callable +from typing import cast from typing import Iterable from typing import Iterator from typing import List @@ -34,6 +36,7 @@ from _pytest.pathlib import absolutepath from _pytest.pathlib import commonpath from _pytest.store import Store +from _pytest.warning_types import PytestWarning if TYPE_CHECKING: # Imported here due to circular import. @@ -125,7 +128,20 @@ def __call__(self, *k, **kw): fail(msg, pytrace=False) def _create(self, *k, **kw): - return super().__call__(*k, **kw) + try: + return super().__call__(*k, **kw) + except TypeError: + sig = signature(getattr(self, "__init__")) + known_kw = {k: v for k, v in kw.items() if k in sig.parameters} + from .warning_types import PytestDeprecationWarning + + warnings.warn( + PytestDeprecationWarning( + f"{self} is not using a cooperative constructor and only takes {set(known_kw)}" + ) + ) + + return super().__call__(*k, **known_kw) class Node(metaclass=NodeMeta): @@ -539,26 +555,39 @@ def _check_initialpaths_for_relpath(session: "Session", path: Path) -> Optional[ class FSCollector(Collector): def __init__( self, - fspath: Optional[LEGACY_PATH], - path: Optional[Path], - parent=None, + fspath: Optional[LEGACY_PATH] = None, + path_or_parent: Optional[Union[Path, Node]] = None, + path: Optional[Path] = None, + name: Optional[str] = None, + parent: Optional[Node] = None, config: Optional[Config] = None, session: Optional["Session"] = None, nodeid: Optional[str] = None, ) -> None: + if path_or_parent: + if isinstance(path_or_parent, Node): + assert parent is None + parent = cast(FSCollector, path_or_parent) + elif isinstance(path_or_parent, Path): + assert path is None + path = path_or_parent + path, fspath = _imply_path(path, fspath=fspath) - name = path.name - if parent is not None and parent.path != path: - try: - rel = path.relative_to(parent.path) - except ValueError: - pass - else: - name = str(rel) - name = name.replace(os.sep, SEP) + if name is None: + name = path.name + if parent is not None and parent.path != path: + try: + rel = path.relative_to(parent.path) + except ValueError: + pass + else: + name = str(rel) + name = name.replace(os.sep, SEP) self.path = path - session = session or parent.session + if session is None: + assert parent is not None + session = parent.session if nodeid is None: try: @@ -570,7 +599,12 @@ def __init__( nodeid = nodeid.replace(os.sep, SEP) super().__init__( - name, parent, config, session, nodeid=nodeid, fspath=fspath, path=path + name=name, + parent=parent, + config=config, + session=session, + nodeid=nodeid, + path=path, ) @classmethod @@ -610,6 +644,20 @@ class Item(Node): nextitem = None + def __init_subclass__(cls) -> None: + problems = ", ".join( + base.__name__ for base in cls.__bases__ if issubclass(base, Collector) + ) + if problems: + warnings.warn( + f"{cls.__name__} is an Item subclass and should not be a collector, " + f"however its bases {problems} are collectors.\n" + "Please split the Collectors and the Item into separate node types.\n" + "Pytest Doc example: https://docs.pytest.org/en/latest/example/nonpython.html\n" + "example pull request on a plugin: https://github.com/asmeurer/pytest-flakes/pull/40/", + PytestWarning, + ) + def __init__( self, name, @@ -617,8 +665,16 @@ def __init__( config: Optional[Config] = None, session: Optional["Session"] = None, nodeid: Optional[str] = None, + **kw, ) -> None: - super().__init__(name, parent, config, session, nodeid=nodeid) + super().__init__( + name=name, + parent=parent, + config=config, + session=session, + nodeid=nodeid, + **kw, + ) self._report_sections: List[Tuple[str, str, str]] = [] #: A list of tuples (name, value) that holds user defined properties diff --git a/testing/test_nodes.py b/testing/test_nodes.py index 9104c1c0588..52cd0173c7d 100644 --- a/testing/test_nodes.py +++ b/testing/test_nodes.py @@ -5,6 +5,7 @@ import pytest from _pytest import nodes +from _pytest.compat import legacy_path from _pytest.pytester import Pytester from _pytest.warning_types import PytestWarning @@ -39,6 +40,36 @@ def test_node_from_parent_disallowed_arguments() -> None: nodes.Node.from_parent(None, config=None) # type: ignore[arg-type] +def test_subclassing_both_item_and_collector_deprecated( + request, tmp_path: Path +) -> None: + """ + Verifies we warn on diamond inheritance + as well as correctly managing legacy inheritance ctors with missing args + as found in plugins + """ + + with pytest.warns( + PytestWarning, + match=( + "(?m)SoWrong is an Item subclass and should not be a collector, however its bases File are collectors.\n" + "Please split the Collectors and the Item into separate node types.\n.*" + ), + ): + + class SoWrong(nodes.File, nodes.Item): + def __init__(self, fspath, parent): + """Legacy ctor with legacy call # don't wana see""" + super().__init__(fspath, parent) + + with pytest.warns( + PytestWarning, match=".*SoWrong.* not using a cooperative constructor.*" + ): + SoWrong.from_parent( + request.session, fspath=legacy_path(tmp_path / "broken.txt") + ) + + @pytest.mark.parametrize( "warn_type, msg", [(DeprecationWarning, "deprecated"), (PytestWarning, "pytest")] ) From 0d6cb3b281f972951349de50dea36d38846fbe31 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 24 Jun 2021 15:05:56 -0300 Subject: [PATCH 359/630] Allow prereleases from prepare-release workflow (#8628) Fix #7551 --- .github/workflows/prepare-release-pr.yml | 8 ++++++-- scripts/prepare-release-pr.py | 20 +++++++++++++------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/.github/workflows/prepare-release-pr.yml b/.github/workflows/prepare-release-pr.yml index 91977f5b257..7fa036613f3 100644 --- a/.github/workflows/prepare-release-pr.yml +++ b/.github/workflows/prepare-release-pr.yml @@ -11,6 +11,10 @@ on: description: 'Major release? (yes/no)' required: true default: 'no' + prerelease: + description: 'Prerelease (ex: rc1). Leave empty if not a pre-release.' + required: true + default: '' # Set permissions at the job level. permissions: {} @@ -41,9 +45,9 @@ jobs: - name: Prepare release PR (minor/patch release) if: github.event.inputs.major == 'no' run: | - tox -e prepare-release-pr -- ${{ github.event.inputs.branch }} ${{ github.token }} + tox -e prepare-release-pr -- ${{ github.event.inputs.branch }} ${{ github.token }} --prerelease '${{ github.event.inputs.prerelease }}' - name: Prepare release PR (major release) if: github.event.inputs.major == 'yes' run: | - tox -e prepare-release-pr -- ${{ github.event.inputs.branch }} ${{ github.token }} --major + tox -e prepare-release-pr -- ${{ github.event.inputs.branch }} ${{ github.token }} --major --prerelease '${{ github.event.inputs.prerelease }}' diff --git a/scripts/prepare-release-pr.py b/scripts/prepare-release-pr.py index ca5ed411aec..27152fea1ca 100644 --- a/scripts/prepare-release-pr.py +++ b/scripts/prepare-release-pr.py @@ -46,14 +46,16 @@ def login(token: str) -> Repository: return github.repository(owner, repo) -def prepare_release_pr(base_branch: str, is_major: bool, token: str) -> None: +def prepare_release_pr( + base_branch: str, is_major: bool, token: str, prerelease: str +) -> None: print() print(f"Processing release for branch {Fore.CYAN}{base_branch}") check_call(["git", "checkout", f"origin/{base_branch}"]) try: - version = find_next_version(base_branch, is_major) + version = find_next_version(base_branch, is_major, prerelease) except InvalidFeatureRelease as e: print(f"{Fore.RED}{e}") raise SystemExit(1) @@ -115,7 +117,7 @@ def prepare_release_pr(base_branch: str, is_major: bool, token: str) -> None: print(f"Pull request {Fore.CYAN}{pr.url}{Fore.RESET} created.") -def find_next_version(base_branch: str, is_major: bool) -> str: +def find_next_version(base_branch: str, is_major: bool, prerelease: str) -> str: output = check_output(["git", "tag"], encoding="UTF-8") valid_versions = [] for v in output.splitlines(): @@ -133,11 +135,11 @@ def find_next_version(base_branch: str, is_major: bool) -> str: is_feature_release = features or breaking if is_major: - return f"{last_version[0]+1}.0.0" + return f"{last_version[0]+1}.0.0{prerelease}" elif is_feature_release: - return f"{last_version[0]}.{last_version[1] + 1}.0" + return f"{last_version[0]}.{last_version[1] + 1}.0{prerelease}" else: - return f"{last_version[0]}.{last_version[1]}.{last_version[2] + 1}" + return f"{last_version[0]}.{last_version[1]}.{last_version[2] + 1}{prerelease}" def main() -> None: @@ -146,9 +148,13 @@ def main() -> None: parser.add_argument("base_branch") parser.add_argument("token") parser.add_argument("--major", action="store_true", default=False) + parser.add_argument("--prerelease", default="") options = parser.parse_args() prepare_release_pr( - base_branch=options.base_branch, is_major=options.major, token=options.token + base_branch=options.base_branch, + is_major=options.major, + token=options.token, + prerelease=options.prerelease, ) From cb1dba0489ca22fc7b921f66d7f0098bad5d3ada Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 24 Jun 2021 20:21:22 +0200 Subject: [PATCH 360/630] doc: Add new open trainings (#8793) --- doc/en/index.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/en/index.rst b/doc/en/index.rst index 59431dbb315..08d195e7aaa 100644 --- a/doc/en/index.rst +++ b/doc/en/index.rst @@ -1,12 +1,12 @@ :orphan: -.. - (note: please leave this here, next open training to follow soon) - .. sidebar:: Next Open Trainings +.. sidebar:: Next Open Trainings - - `Professionelles Testen für Python mit pytest `_ (German), part of the enterPy conference, April 22nd (sold out) and May 20th, remote. + - `Introduction to pytest `_, part of `Europython 2021 `_, July 27th, remote. + - `pytest: Professionelles Testen (nicht nur) für Python `_ (German), part of `CH-Open Workshoptage `_, September 9th, ETH Zurich, Switzerland. + - `Professional Testing with Python `_, via `Python Academy `_, Q4/2021 (TBD, 3 days), Leipzig (Germany) and remote. - Also see `previous talks and blogposts `_. + Also see `previous talks and blogposts `_. .. _features: From f0e5640497a236ab4a52e1fc4cf849a91ed95de5 Mon Sep 17 00:00:00 2001 From: pytest bot Date: Sun, 27 Jun 2021 00:08:04 +0000 Subject: [PATCH 361/630] [automated] Update plugin list --- doc/en/reference/plugin_list.rst | 69 ++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 31 deletions(-) diff --git a/doc/en/reference/plugin_list.rst b/doc/en/reference/plugin_list.rst index 0e8e74e49b8..4fb1a604121 100644 --- a/doc/en/reference/plugin_list.rst +++ b/doc/en/reference/plugin_list.rst @@ -6,7 +6,7 @@ Plugin List PyPI projects that match "pytest-\*" are considered plugins and are listed automatically. Packages classified as inactive are excluded. -This list contains 880 plugins. +This list contains 887 plugins. ============================================================================================================== ======================================================================================================================================================================== ============== ===================== ================================================ name summary last release status requires @@ -42,18 +42,19 @@ name `pytest-appengine `_ AppEngine integration that works well with pytest-django Feb 27, 2017 N/A N/A `pytest-appium `_ Pytest plugin for appium Dec 05, 2019 N/A N/A `pytest-approvaltests `_ A plugin to use approvaltests with pytest Feb 07, 2021 4 - Beta pytest (>=3.5.0) +`pytest-argus `_ pyest results colection plugin Jun 24, 2021 5 - Production/Stable pytest (>=6.2.4) `pytest-arraydiff `_ pytest plugin to help with comparing array output from tests Dec 06, 2018 4 - Beta pytest `pytest-asgi-server `_ Convenient ASGI client/server fixtures for Pytest Dec 12, 2020 N/A pytest (>=5.4.1) `pytest-asptest `_ test Answer Set Programming programs Apr 28, 2018 4 - Beta N/A `pytest-assertutil `_ pytest-assertutil May 10, 2019 N/A N/A `pytest-assert-utils `_ Useful assertion utilities for use with pytest Mar 28, 2021 3 - Alpha N/A -`pytest-assume `_ A pytest plugin that allows multiple failures per test Dec 08, 2020 N/A pytest (>=2.7) +`pytest-assume `_ A pytest plugin that allows multiple failures per test Jun 24, 2021 N/A pytest (>=2.7) `pytest-ast-back-to-python `_ A plugin for pytest devs to view how assertion rewriting recodes the AST Sep 29, 2019 4 - Beta N/A `pytest-astropy `_ Meta-package containing dependencies for testing Jan 16, 2020 5 - Production/Stable pytest (>=4.6) `pytest-astropy-header `_ pytest plugin to add diagnostic information to the header of the test output Dec 18, 2019 3 - Alpha pytest (>=2.8) `pytest-ast-transformer `_ May 04, 2019 3 - Alpha pytest `pytest-asyncio `_ Pytest support for asyncio. Apr 21, 2021 4 - Beta pytest (>=5.4.0) -`pytest-asyncio-cooperative `_ Run all your asynchronous tests cooperatively. Apr 17, 2021 4 - Beta N/A +`pytest-asyncio-cooperative `_ Run all your asynchronous tests cooperatively. Jun 25, 2021 4 - Beta N/A `pytest-asyncio-network-simulator `_ pytest-asyncio-network-simulator: Plugin for pytest for simulator the network in tests Jul 31, 2018 3 - Alpha pytest (<3.7.0,>=3.3.2) `pytest-async-mongodb `_ pytest plugin for async MongoDB Oct 18, 2017 5 - Production/Stable pytest (>=2.5.2) `pytest-async-sqlalchemy `_ Database testing fixtures using the SQLAlchemy asyncio API Jun 11, 2021 4 - Beta pytest (>=6.0.0) @@ -222,7 +223,7 @@ name `pytest-django-testing-postgresql `_ Use a temporary PostgreSQL database with pytest-django Dec 05, 2019 3 - Alpha N/A `pytest-doc `_ A documentation plugin for py.test. Jun 28, 2015 5 - Production/Stable N/A `pytest-docgen `_ An RST Documentation Generator for pytest-based test suites Apr 17, 2020 N/A N/A -`pytest-docker `_ Simple pytest fixtures for Docker and docker-compose based tests Sep 22, 2020 N/A pytest (<7.0,>=4.0) +`pytest-docker `_ Simple pytest fixtures for Docker and docker-compose based tests Jun 14, 2021 N/A pytest (<7.0,>=4.0) `pytest-docker-butla `_ Jun 16, 2019 3 - Alpha N/A `pytest-dockerc `_ Run, manage and stop Docker Compose project from Docker API Oct 09, 2020 5 - Production/Stable pytest (>=3.0) `pytest-docker-compose `_ Manages Docker containers during your integration tests Jan 26, 2021 5 - Production/Stable pytest (>=3.3) @@ -248,6 +249,7 @@ name `pytest-drivings `_ Tool to allow webdriver automation to be ran locally or remotely Jan 13, 2021 N/A N/A `pytest-drop-dup-tests `_ A Pytest plugin to drop duplicated tests during collection May 23, 2020 4 - Beta pytest (>=2.7) `pytest-dump2json `_ A pytest plugin for dumping test results to json. Jun 29, 2015 N/A N/A +`pytest-duration-insights `_ Jun 25, 2021 N/A N/A `pytest-dynamicrerun `_ A pytest plugin to rerun tests dynamically based off of test outcome and output. Aug 15, 2020 4 - Beta N/A `pytest-dynamodb `_ DynamoDB fixtures for pytest Jun 03, 2021 5 - Production/Stable pytest `pytest-easy-addoption `_ pytest-easy-addoption: Easy way to work with pytest addoption Jan 22, 2020 N/A N/A @@ -261,11 +263,11 @@ name `pytest-elements `_ Tool to help automate user interfaces Jan 13, 2021 N/A pytest (>=5.4,<6.0) `pytest-elk-reporter `_ A simple plugin to use with pytest Jan 24, 2021 4 - Beta pytest (>=3.5.0) `pytest-email `_ Send execution result email Jul 08, 2020 N/A pytest -`pytest-embedded `_ pytest embedded plugin Jun 11, 2021 N/A pytest (>=6.2.0) -`pytest-embedded-idf `_ pytest embedded plugin for esp-idf project Jun 11, 2021 N/A N/A -`pytest-embedded-qemu-idf `_ pytest embedded plugin for esp-idf project by qemu, not target chip Jun 11, 2021 N/A N/A -`pytest-embedded-serial `_ pytest embedded plugin for testing serial ports Jun 11, 2021 N/A N/A -`pytest-embedded-serial-esp `_ pytest embedded plugin for testing espressif boards via serial ports Jun 11, 2021 N/A N/A +`pytest-embedded `_ pytest embedded plugin Jun 16, 2021 N/A pytest (>=6.2.0) +`pytest-embedded-idf `_ pytest embedded plugin for esp-idf project Jun 16, 2021 N/A N/A +`pytest-embedded-qemu-idf `_ pytest embedded plugin for esp-idf project by qemu, not target chip Jun 16, 2021 N/A N/A +`pytest-embedded-serial `_ pytest embedded plugin for testing serial ports Jun 16, 2021 N/A N/A +`pytest-embedded-serial-esp `_ pytest embedded plugin for testing espressif boards via serial ports Jun 16, 2021 N/A N/A `pytest-emoji `_ A pytest plugin that adds emojis to your test result report Feb 19, 2019 4 - Beta pytest (>=4.2.1) `pytest-emoji-output `_ Pytest plugin to represent test output with emoji support Jun 06, 2021 4 - Beta pytest (==6.0.1) `pytest-enabler `_ Enable installed pytest plugins Jan 19, 2021 5 - Production/Stable pytest (!=3.7.3,>=3.5) ; extra == 'testing' @@ -288,6 +290,7 @@ name `pytest-expect `_ py.test plugin to store test expectations and mark tests based on them Apr 21, 2016 4 - Beta N/A `pytest-expecter `_ Better testing with expecter and pytest. Jul 08, 2020 5 - Production/Stable N/A `pytest-expectr `_ This plugin is used to expect multiple assert using pytest framework. Oct 05, 2018 N/A pytest (>=2.4.2) +`pytest-explicit `_ A Pytest plugin to ignore certain marked tests by default Jun 15, 2021 5 - Production/Stable pytest `pytest-exploratory `_ Interactive console for pytest. Jan 20, 2021 N/A pytest (>=5.3) `pytest-external-blockers `_ a special outcome for tests that are blocked for external reasons Oct 04, 2016 N/A N/A `pytest-extra-durations `_ A pytest plugin to get durations on a per-function basis and per module basis. Apr 21, 2020 4 - Beta pytest (>=3.5.0) @@ -387,7 +390,7 @@ name `pytest-hue `_ Visualise PyTest status via your Phillips Hue lights May 09, 2019 N/A N/A `pytest-hylang `_ Pytest plugin to allow running tests written in hylang Mar 28, 2021 N/A pytest `pytest-hypo-25 `_ help hypo module for pytest Jan 12, 2020 3 - Alpha N/A -`pytest-ibutsu `_ A plugin to sent pytest results to an Ibutsu server Mar 09, 2021 4 - Beta pytest +`pytest-ibutsu `_ A plugin to sent pytest results to an Ibutsu server Jun 16, 2021 4 - Beta pytest `pytest-icdiff `_ use icdiff for better error messages in pytest assertions Apr 08, 2020 4 - Beta N/A `pytest-idapro `_ A pytest plugin for idapython. Allows a pytest setup to run tests outside and inside IDA in an automated manner by runnig pytest inside IDA and by mocking idapython api Nov 03, 2018 N/A N/A `pytest-ignore-flaky `_ ignore failures from flaky tests (pytest plugin) Apr 23, 2021 5 - Production/Stable N/A @@ -397,7 +400,7 @@ name `pytest-info-collector `_ pytest plugin to collect information from tests May 26, 2019 3 - Alpha N/A `pytest-informative-node `_ display more node ininformation. Apr 25, 2019 4 - Beta N/A `pytest-infrastructure `_ pytest stack validation prior to testing executing Apr 12, 2020 4 - Beta N/A -`pytest-inmanta `_ A py.test plugin providing fixtures to simplify inmanta modules testing. Apr 09, 2021 5 - Production/Stable N/A +`pytest-inmanta `_ A py.test plugin providing fixtures to simplify inmanta modules testing. Jun 18, 2021 5 - Production/Stable N/A `pytest-inmanta-extensions `_ Inmanta tests package May 27, 2021 5 - Production/Stable N/A `pytest-Inomaly `_ A simple image diff plugin for pytest Feb 13, 2018 4 - Beta N/A `pytest-insta `_ A practical snapshot testing plugin for pytest Apr 07, 2021 N/A pytest (>=6.0.2,<7.0.0) @@ -421,7 +424,7 @@ name `pytest-joke `_ Test failures are better served with humor. Oct 08, 2019 4 - Beta pytest (>=4.2.1) `pytest-json `_ Generate JSON test reports Jan 18, 2016 4 - Beta N/A `pytest-jsonlint `_ UNKNOWN Aug 04, 2016 N/A N/A -`pytest-json-report `_ A pytest plugin to report test results as JSON files May 17, 2021 4 - Beta pytest (>=3.8.0) +`pytest-json-report `_ A pytest plugin to report test results as JSON files Jun 18, 2021 4 - Beta pytest (>=3.8.0) `pytest-kafka `_ Zookeeper, Kafka server, and Kafka consumer fixtures for Pytest Nov 01, 2019 N/A pytest `pytest-kind `_ Kubernetes test support with KIND for pytest Jan 24, 2021 5 - Production/Stable N/A `pytest-kivy `_ Kivy GUI tests fixtures using pytest Mar 20, 2021 4 - Beta pytest (>=3.6) @@ -437,7 +440,7 @@ name `pytest-leaks `_ A pytest plugin to trace resource leaks. Nov 27, 2019 1 - Planning N/A `pytest-level `_ Select tests of a given level or lower Oct 21, 2019 N/A pytest `pytest-libfaketime `_ A python-libfaketime plugin for pytest. Dec 22, 2018 4 - Beta pytest (>=3.0.0) -`pytest-libiio `_ A pytest plugin to manage interfacing with libiio contexts Jan 09, 2021 4 - Beta N/A +`pytest-libiio `_ A pytest plugin to manage interfacing with libiio contexts Jun 15, 2021 4 - Beta N/A `pytest-libnotify `_ Pytest plugin that shows notifications about the test run Apr 02, 2021 3 - Alpha pytest `pytest-ligo `_ Jan 16, 2020 4 - Beta N/A `pytest-lineno `_ A pytest plugin to show the line numbers of test functions Dec 04, 2020 N/A pytest @@ -452,6 +455,7 @@ name `pytest-lockable `_ lockable resource plugin for pytest Jun 08, 2021 5 - Production/Stable pytest `pytest-locker `_ Used to lock object during testing. Essentially changing assertions from being hard coded to asserting that nothing changed Feb 25, 2021 N/A pytest (>=5.4) `pytest-logbook `_ py.test plugin to capture logbook log messages Nov 23, 2015 5 - Production/Stable pytest (>=2.8) +`pytest-logdog `_ Pytest plugin to test logging Jun 15, 2021 1 - Planning pytest (>=6.2.0) `pytest-logfest `_ Pytest plugin providing three logger fixtures with basic or full writing to log files Jul 21, 2019 4 - Beta pytest (>=3.5.0) `pytest-logger `_ Plugin configuring handlers for loggers from Python logging module. Jul 25, 2019 4 - Beta pytest (>=3.2) `pytest-logging `_ Configures logging and allows tweaking the log level with a py.test flag Nov 04, 2015 4 - Beta N/A @@ -510,7 +514,7 @@ name `pytest-mypyd `_ Mypy static type checker plugin for Pytest Aug 20, 2019 4 - Beta pytest (<4.7,>=2.8) ; python_version < "3.5" `pytest-mypy-plugins `_ pytest plugin for writing tests for mypy plugins May 22, 2021 3 - Alpha pytest (>=6.0.0) `pytest-mypy-plugins-shim `_ Substitute for "pytest-mypy-plugins" for Python implementations which aren't supported by mypy. Apr 12, 2021 N/A N/A -`pytest-mypy-testing `_ Pytest plugin to check mypy output. Apr 24, 2020 N/A pytest +`pytest-mypy-testing `_ Pytest plugin to check mypy output. Jun 13, 2021 N/A pytest `pytest-mysql `_ MySQL process and client fixtures for pytest Jun 01, 2021 5 - Production/Stable pytest `pytest-needle `_ pytest plugin for visual testing websites using selenium Dec 10, 2018 4 - Beta pytest (<5.0.0,>=3.0.0) `pytest-neo `_ pytest-neo is a plugin for pytest that shows tests like screen of Matrix. Apr 23, 2019 3 - Alpha pytest (>=3.7.2) @@ -563,7 +567,7 @@ name `pytest-pep257 `_ py.test plugin for pep257 Jul 09, 2016 N/A N/A `pytest-pep8 `_ pytest plugin to check PEP8 requirements Apr 27, 2014 N/A N/A `pytest-percent `_ Change the exit code of pytest test sessions when a required percent of tests pass. May 21, 2020 N/A pytest (>=5.2.0) -`pytest-perf `_ pytest-perf May 29, 2021 5 - Production/Stable pytest (>=4.6) ; extra == 'testing' +`pytest-perf `_ pytest-perf Jun 26, 2021 5 - Production/Stable pytest (>=4.6) ; extra == 'testing' `pytest-performance `_ A simple plugin to ensure the execution of critical sections of code has not been impacted Sep 11, 2020 5 - Production/Stable pytest (>=3.7.0) `pytest-persistence `_ Pytest tool for persistent objects Mar 28, 2021 N/A N/A `pytest-pgsql `_ Pytest plugins and helpers for tests using a Postgres database. May 13, 2020 5 - Production/Stable pytest (>=3.0.0) @@ -596,7 +600,7 @@ name `pytest-postgresql `_ Postgresql fixtures and fixture factories for Pytest. Jun 01, 2021 5 - Production/Stable pytest (>=3.0.0) `pytest-power `_ pytest plugin with powerful fixtures Dec 31, 2020 N/A pytest (>=5.4) `pytest-pride `_ Minitest-style test colors Apr 02, 2016 3 - Alpha N/A -`pytest-print `_ pytest-print adds the printer fixture you can use to print messages to the user (directly to the pytest runner, not stdout) Oct 23, 2020 5 - Production/Stable pytest (>=3.0.0) +`pytest-print `_ pytest-print adds the printer fixture you can use to print messages to the user (directly to the pytest runner, not stdout) Jun 17, 2021 5 - Production/Stable pytest (>=6) `pytest-profiling `_ Profiling plugin for py.test May 28, 2019 5 - Production/Stable pytest `pytest-progress `_ pytest plugin for instant test progress status Oct 06, 2020 5 - Production/Stable N/A `pytest-prometheus `_ Report test pass / failures to a Prometheus PushGateway Oct 03, 2017 N/A N/A @@ -618,9 +622,9 @@ name `pytest-pytestrail `_ Pytest plugin for interaction with TestRail Aug 27, 2020 4 - Beta pytest (>=3.8.0) `pytest-pythonpath `_ pytest plugin for adding to the PYTHONPATH from command line or configs. Aug 22, 2018 5 - Production/Stable N/A `pytest-pytorch `_ pytest plugin for a better developer experience when working with the PyTorch test suite May 25, 2021 4 - Beta pytest -`pytest-qatouch `_ Pytest plugin for uploading test results to your QA Touch Testrun. Jun 10, 2021 4 - Beta pytest (>=6.2.0) +`pytest-qatouch `_ Pytest plugin for uploading test results to your QA Touch Testrun. Jun 26, 2021 4 - Beta pytest (>=6.2.0) `pytest-qml `_ Run QML Tests with pytest Dec 02, 2020 4 - Beta pytest (>=6.0.0) -`pytest-qt `_ pytest support for PyQt and PySide applications Jun 07, 2021 5 - Production/Stable pytest (>=3.0.0) +`pytest-qt `_ pytest support for PyQt and PySide applications Jun 13, 2021 5 - Production/Stable pytest (>=3.0.0) `pytest-qt-app `_ QT app fixture for py.test Dec 23, 2015 5 - Production/Stable N/A `pytest-quarantine `_ A plugin for pytest to manage expected test failures Nov 24, 2019 5 - Production/Stable pytest (>=4.6) `pytest-quickcheck `_ pytest plugin to generate random data inspired by QuickCheck Nov 15, 2020 4 - Beta pytest (<6.0.0,>=4.0) @@ -649,6 +653,7 @@ name `pytest-relaxed `_ Relaxed test discovery/organization for pytest Jun 14, 2019 5 - Production/Stable pytest (<5,>=3) `pytest-remfiles `_ Pytest plugin to create a temporary directory with remote files Jul 01, 2019 5 - Production/Stable N/A `pytest-remotedata `_ Pytest plugin for controlling remote data access. Jul 20, 2019 3 - Alpha pytest (>=3.1) +`pytest-remote-response `_ Pytest plugin for capturing and mocking connection requests. Jun 23, 2021 3 - Alpha pytest (>=4.6) `pytest-remove-stale-bytecode `_ py.test plugin to remove stale byte code files. Mar 04, 2020 4 - Beta pytest `pytest-reorder `_ Reorder tests depending on their paths and names. May 31, 2018 4 - Beta pytest `pytest-repeat `_ pytest plugin for repeating tests Oct 31, 2020 5 - Production/Stable pytest (>=3.6) @@ -662,10 +667,10 @@ name `pytest-reportlog `_ Replacement for the --resultlog option, focused in simplicity and extensibility Dec 11, 2020 3 - Alpha pytest (>=5.2) `pytest-report-me `_ A pytest plugin to generate report. Dec 31, 2020 N/A pytest `pytest-report-parameters `_ pytest plugin for adding tests' parameters to junit report Jun 18, 2020 3 - Alpha pytest (>=2.4.2) -`pytest-reportportal `_ Agent for Reporting results of tests to the Report Portal Jun 09, 2021 N/A pytest (>=3.8.0) +`pytest-reportportal `_ Agent for Reporting results of tests to the Report Portal Jun 18, 2021 N/A pytest (>=3.8.0) `pytest-reqs `_ pytest plugin to check pinned requirements May 12, 2019 N/A pytest (>=2.4.2) `pytest-requests `_ A simple plugin to use with pytest Jun 24, 2019 4 - Beta pytest (>=3.5.0) -`pytest-reraise `_ Make multi-threaded pytest test cases fail when they should Jun 09, 2021 5 - Production/Stable pytest (>=4.6) +`pytest-reraise `_ Make multi-threaded pytest test cases fail when they should Jun 17, 2021 5 - Production/Stable pytest (>=4.6) `pytest-rerun `_ Re-run only changed files in specified branch Jul 08, 2019 N/A pytest (>=3.6) `pytest-rerunfailures `_ pytest plugin to re-run tests to eliminate flaky failures May 26, 2021 5 - Production/Stable pytest (>=5.3) `pytest-resilient-circuits `_ Resilient Circuits fixtures for PyTest. May 14, 2021 N/A N/A @@ -689,19 +694,19 @@ name `pytest-runner `_ Invoke py.test as distutils command with dependency resolution May 19, 2021 5 - Production/Stable pytest (>=4.6) ; extra == 'testing' `pytest-salt `_ Pytest Salt Plugin Jan 27, 2020 4 - Beta N/A `pytest-salt-containers `_ A Pytest plugin that builds and creates docker containers Nov 09, 2016 4 - Beta N/A -`pytest-salt-factories `_ Pytest Salt Plugin Jun 11, 2021 4 - Beta pytest (>=6.0.0) +`pytest-salt-factories `_ Pytest Salt Plugin Jun 20, 2021 4 - Beta pytest (>=6.0.0) `pytest-salt-from-filenames `_ Simple PyTest Plugin For Salt's Test Suite Specifically Jan 29, 2019 4 - Beta pytest (>=4.1) `pytest-salt-runtests-bridge `_ Simple PyTest Plugin For Salt's Test Suite Specifically Dec 05, 2019 4 - Beta pytest (>=4.1) `pytest-sanic `_ a pytest plugin for Sanic May 20, 2021 N/A pytest (>=5.2) `pytest-sanity `_ Dec 07, 2020 N/A N/A `pytest-sa-pg `_ May 14, 2019 N/A N/A -`pytest-sbase `_ A complete web automation framework for end-to-end testing. Jun 05, 2021 5 - Production/Stable N/A +`pytest-sbase `_ A complete web automation framework for end-to-end testing. Jun 21, 2021 5 - Production/Stable N/A `pytest-scenario `_ pytest plugin for test scenarios Feb 06, 2017 3 - Alpha N/A `pytest-schema `_ 👍 Validate return values against a schema-like object in testing Aug 31, 2020 5 - Production/Stable pytest (>=3.5.0) `pytest-securestore `_ An encrypted password store for use within pytest cases Jun 19, 2019 4 - Beta N/A `pytest-select `_ A pytest plugin which allows to (de-)select tests from a file. Jan 18, 2019 3 - Alpha pytest (>=3.0) `pytest-selenium `_ pytest plugin for Selenium Sep 19, 2020 5 - Production/Stable pytest (>=5.0.0) -`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Jun 05, 2021 5 - Production/Stable N/A +`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Jun 21, 2021 5 - Production/Stable N/A `pytest-selenium-enhancer `_ pytest plugin for Selenium Nov 26, 2020 5 - Production/Stable N/A `pytest-selenium-pdiff `_ A pytest package implementing perceptualdiff for Selenium tests. Apr 06, 2017 2 - Pre-Alpha N/A `pytest-send-email `_ Send pytest execution result email Dec 04, 2019 N/A N/A @@ -719,7 +724,7 @@ name `pytest-sherlock `_ pytest plugin help to find coupled tests Jul 13, 2020 5 - Production/Stable pytest (>=3.5.1) `pytest-shortcuts `_ Expand command-line shortcuts listed in pytest configuration Oct 29, 2020 4 - Beta pytest (>=3.5.0) `pytest-shutil `_ A goodie-bag of unix shell and environment tools for py.test May 28, 2019 5 - Production/Stable pytest -`pytest-simplehttpserver `_ Simple pytest fixture to spin up an HTTP server Jun 12, 2021 4 - Beta N/A +`pytest-simplehttpserver `_ Simple pytest fixture to spin up an HTTP server Jun 24, 2021 4 - Beta N/A `pytest-simple-plugin `_ Simple pytest plugin Nov 27, 2019 N/A N/A `pytest-simple-settings `_ simple-settings plugin for pytest Nov 17, 2020 4 - Beta pytest `pytest-single-file-logging `_ Allow for multiple processes to log to a single file May 05, 2016 4 - Beta pytest (>=2.8.1) @@ -744,12 +749,12 @@ name `pytest-sphinx `_ Doctest plugin for pytest with support for Sphinx-specific doctest-directives Aug 05, 2020 4 - Beta N/A `pytest-spiratest `_ Exports unit tests as test runs in SpiraTest/Team/Plan Apr 28, 2021 N/A N/A `pytest-splinter `_ Splinter plugin for pytest testing framework Dec 25, 2020 6 - Mature N/A -`pytest-split `_ Pytest plugin for splitting test suite based on test execution time Jun 08, 2021 4 - Beta N/A +`pytest-split `_ Pytest plugin for splitting test suite based on test execution time Jun 17, 2021 4 - Beta pytest (>=5,<7) `pytest-splitio `_ Split.io SDK integration for e2e tests Sep 22, 2020 N/A pytest (<7,>=5.0) `pytest-split-tests `_ A Pytest plugin for running a subset of your tests by splitting them in to equally sized groups. Forked from Mark Adams' original project pytest-test-groups. May 28, 2019 N/A pytest (>=2.5) `pytest-split-tests-tresorit `_ Feb 22, 2021 1 - Planning N/A -`pytest-splunk-addon `_ A Dynamic test tool for Splunk Apps and Add-ons Apr 28, 2021 N/A pytest (>5.4.0,<6.3) -`pytest-splunk-addon-ui-smartx `_ Library to support testing Splunk Add-on UX Jun 10, 2021 N/A N/A +`pytest-splunk-addon `_ A Dynamic test tool for Splunk Apps and Add-ons Jun 21, 2021 N/A pytest (>5.4.0,<6.3) +`pytest-splunk-addon-ui-smartx `_ Library to support testing Splunk Add-on UX Jun 17, 2021 N/A N/A `pytest-splunk-env `_ pytest fixtures for interaction with Splunk Enterprise and Splunk Cloud Oct 22, 2020 N/A pytest (>=6.1.1,<7.0.0) `pytest-sqitch `_ sqitch for pytest Apr 06, 2020 4 - Beta N/A `pytest-sqlalchemy `_ pytest plugin with sqlalchemy related fixtures Mar 13, 2018 3 - Alpha N/A @@ -768,7 +773,7 @@ name `pytest-stub `_ Stub packages, modules and attributes. Apr 28, 2020 5 - Production/Stable N/A `pytest-stubprocess `_ Provide stub implementations for subprocesses in Python tests Sep 17, 2018 3 - Alpha pytest (>=3.5.0) `pytest-study `_ A pytest plugin to organize long run tests (named studies) without interfering the regular tests Sep 26, 2017 3 - Alpha pytest (>=2.0) -`pytest-subprocess `_ A plugin to fake subprocess for pytest Apr 18, 2021 5 - Production/Stable pytest (>=4.0.0) +`pytest-subprocess `_ A plugin to fake subprocess for pytest Jun 18, 2021 5 - Production/Stable pytest (>=4.0.0) `pytest-subtesthack `_ A hack to explicitly set up and tear down fixtures. Mar 02, 2021 N/A N/A `pytest-subtests `_ unittest subTest() support and subtests fixture May 29, 2021 4 - Beta pytest (>=5.3.0) `pytest-subunit `_ pytest-subunit is a plugin for py.test which outputs testsresult in subunit format. Aug 29, 2017 N/A N/A @@ -791,13 +796,13 @@ name `pytest-testdirectory `_ A py.test plugin providing temporary directories in unit tests. Nov 06, 2018 5 - Production/Stable pytest `pytest-testdox `_ A testdox format reporter for pytest Oct 13, 2020 5 - Production/Stable pytest (>=3.7.0) `pytest-test-groups `_ A Pytest plugin for running a subset of your tests by splitting them in to equally sized groups. Oct 25, 2016 5 - Production/Stable N/A -`pytest-testinfra `_ Test infrastructures Apr 18, 2021 5 - Production/Stable pytest (!=3.0.2) +`pytest-testinfra `_ Test infrastructures Jun 20, 2021 5 - Production/Stable pytest (!=3.0.2) `pytest-testlink-adaptor `_ pytest reporting plugin for testlink Dec 20, 2018 4 - Beta pytest (>=2.6) `pytest-testmon `_ selects tests affected by changed files and methods Apr 28, 2021 4 - Beta N/A `pytest-testobject `_ Plugin to use TestObject Suites with Pytest Sep 24, 2019 4 - Beta pytest (>=3.1.1) `pytest-testrail `_ pytest plugin for creating TestRail runs and adding results Aug 27, 2020 N/A pytest (>=3.6) `pytest-testrail2 `_ A small example package Nov 17, 2020 N/A pytest (>=5) -`pytest-testrail-api `_ Плагин Pytest, для интеграции с TestRail Apr 23, 2021 N/A pytest (>=5.5) +`pytest-testrail-api `_ Плагин Pytest, для интеграции с TestRail Jun 23, 2021 N/A pytest (>=5.5) `pytest-testrail-client `_ pytest plugin for Testrail Sep 29, 2020 5 - Production/Stable N/A `pytest-testrail-e2e `_ pytest plugin for creating TestRail runs and adding results Jun 11, 2020 N/A pytest (>=3.6) `pytest-testrail-plugin `_ PyTest plugin for TestRail Apr 21, 2020 3 - Alpha pytest @@ -844,9 +849,11 @@ name `pytest-unittest-filter `_ A pytest plugin for filtering unittest-based test classes Jan 12, 2019 4 - Beta pytest (>=3.1.0) `pytest-unmarked `_ Run only unmarked tests Aug 27, 2019 5 - Production/Stable N/A `pytest-unordered `_ Test equality of unordered collections in pytest Mar 28, 2021 4 - Beta N/A +`pytest-upload-report `_ pytest-upload-report is a plugin for pytest that upload your test report for test results. Jun 18, 2021 5 - Production/Stable N/A `pytest-vagrant `_ A py.test plugin providing access to vagrant. Mar 23, 2020 5 - Production/Stable pytest `pytest-valgrind `_ May 19, 2021 N/A N/A `pytest-variables `_ pytest plugin for providing variables to tests/fixtures Oct 23, 2019 5 - Production/Stable pytest (>=2.4.2) +`pytest-variant `_ Variant support for Pytest Jun 20, 2021 N/A N/A `pytest-vcr `_ Plugin for managing VCR.py cassettes Apr 26, 2019 5 - Production/Stable pytest (>=3.6.0) `pytest-vcrpandas `_ Test from HTTP interactions to dataframe processed. Jan 12, 2019 4 - Beta pytest `pytest-venv `_ py.test fixture for creating a virtual environment Aug 04, 2020 4 - Beta pytest @@ -867,7 +874,7 @@ name `pytest-wholenodeid `_ pytest addon for displaying the whole node id for failures Aug 26, 2015 4 - Beta pytest (>=2.0) `pytest-winnotify `_ Windows tray notifications for py.test results. Apr 22, 2016 N/A N/A `pytest-workflow `_ A pytest plugin for configuring workflow/pipeline tests using YAML files Dec 14, 2020 5 - Production/Stable pytest (>=5.4.0) -`pytest-xdist `_ pytest xdist plugin for distributed testing and loop-on-failing modes Feb 09, 2021 5 - Production/Stable pytest (>=6.0.0) +`pytest-xdist `_ pytest xdist plugin for distributed testing and loop-on-failing modes Jun 16, 2021 5 - Production/Stable pytest (>=6.0.0) `pytest-xdist-debug-for-graingert `_ pytest xdist plugin for distributed testing and loop-on-failing modes Jul 24, 2019 5 - Production/Stable pytest (>=4.4.0) `pytest-xdist-forked `_ forked from pytest-xdist Feb 10, 2020 5 - Production/Stable pytest (>=4.4.0) `pytest-xdist-tracker `_ pytest plugin helps to reproduce failures for particular xdist node May 05, 2021 3 - Alpha pytest (>=3.5.1) From 6573107b97e96c327db282d45623061edb5afddd Mon Sep 17 00:00:00 2001 From: Srip Date: Sun, 27 Jun 2021 06:29:16 -0700 Subject: [PATCH 362/630] doc: Adding exit code for pytest usage examples (#8790) (#8800) * doc: Adding exit code for pytest usage examples (#8790) Fix #8790 Co-authored-by: Bruno Oliveira Co-authored-by: Sripradha Karkala --- doc/en/how-to/usage.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/doc/en/how-to/usage.rst b/doc/en/how-to/usage.rst index 517c504df7a..ce8247b44ee 100644 --- a/doc/en/how-to/usage.rst +++ b/doc/en/how-to/usage.rst @@ -168,15 +168,15 @@ You can invoke ``pytest`` from Python code directly: .. code-block:: python - pytest.main() + retcode = pytest.main() this acts as if you would call "pytest" from the command line. -It will not raise ``SystemExit`` but return the exitcode instead. +It will not raise :class:`SystemExit` but return the :ref:`exit code ` instead. You can pass in options and arguments: .. code-block:: python - pytest.main(["-x", "mytestdir"]) + retcode = pytest.main(["-x", "mytestdir"]) You can specify additional plugins to ``pytest.main``: @@ -191,7 +191,8 @@ You can specify additional plugins to ``pytest.main``: print("*** test run reporting finishing") - pytest.main(["-qq"], plugins=[MyPlugin()]) + if __name__ == "__main__": + sys.exit(pytest.main(["-qq"], plugins=[MyPlugin()])) Running it will show that ``MyPlugin`` was added and its hook was invoked: From 51742e14a157847ba02e89df4b5be629f9f5b00c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 28 Jun 2021 03:01:47 +0000 Subject: [PATCH 363/630] Bump anyio[curio,trio] in /testing/plugins_integration Bumps [anyio[curio,trio]](https://github.com/agronholm/anyio) from 3.2.0 to 3.2.1. - [Release notes](https://github.com/agronholm/anyio/releases) - [Changelog](https://github.com/agronholm/anyio/blob/master/docs/versionhistory.rst) - [Commits](https://github.com/agronholm/anyio/compare/3.2.0...3.2.1) --- updated-dependencies: - dependency-name: anyio[curio,trio] dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- testing/plugins_integration/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/plugins_integration/requirements.txt b/testing/plugins_integration/requirements.txt index 5038995b5ae..daede39556f 100644 --- a/testing/plugins_integration/requirements.txt +++ b/testing/plugins_integration/requirements.txt @@ -1,4 +1,4 @@ -anyio[curio,trio]==3.2.0 +anyio[curio,trio]==3.2.1 django==3.2.4 pytest-asyncio==0.15.1 pytest-bdd==4.0.2 From c19f63d39dbc6ae41b3c2a92fdc5847d8274c4ad Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 28 Jun 2021 12:24:48 +0200 Subject: [PATCH 364/630] Adjust releasing infra/docs (#8795) * Adjust releasing infra/docs Follow-up for #8150, see #7507 * Add suggestions --- .github/workflows/release-on-comment.yml | 40 ---- RELEASING.rst | 88 +++++--- scripts/release-on-comment.py | 261 ----------------------- tox.ini | 7 - 4 files changed, 59 insertions(+), 337 deletions(-) delete mode 100644 .github/workflows/release-on-comment.yml delete mode 100644 scripts/release-on-comment.py diff --git a/.github/workflows/release-on-comment.yml b/.github/workflows/release-on-comment.yml deleted file mode 100644 index 32d22155223..00000000000 --- a/.github/workflows/release-on-comment.yml +++ /dev/null @@ -1,40 +0,0 @@ -# part of our release process, see `release-on-comment.py` -name: release on comment - -on: - issues: - types: [opened, edited] - issue_comment: - types: [created, edited] - -# Set permissions at the job level. -permissions: {} - -jobs: - build: - runs-on: ubuntu-latest - permissions: - contents: write - issues: write - - if: (github.event.comment && startsWith(github.event.comment.body, '@pytestbot please')) || (github.event.issue && !github.event.comment && startsWith(github.event.issue.body, '@pytestbot please')) - - steps: - - uses: actions/checkout@v2 - with: - fetch-depth: 0 - persist-credentials: false - - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: "3.8" - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install --upgrade setuptools tox - - - name: Prepare release - run: | - tox -e release-on-comment -- $GITHUB_EVENT_PATH ${{ github.token }} diff --git a/RELEASING.rst b/RELEASING.rst index 6f4c3465d6d..25ce90d0f65 100644 --- a/RELEASING.rst +++ b/RELEASING.rst @@ -14,60 +14,90 @@ Preparing: Automatic Method ~~~~~~~~~~~~~~~~~~~~~~~~~~~ We have developed an automated workflow for releases, that uses GitHub workflows and is triggered -by opening an issue. +by `manually running `__ +the `prepare-release-pr workflow `__ +on GitHub Actions. -Bug-fix releases -^^^^^^^^^^^^^^^^ +The automation will decide the new version number based on the following criteria: -A bug-fix release is always done from a maintenance branch, so for example to release bug-fix -``5.1.2``, open a new issue and add this comment to the body:: +- If the "major release" input is set to "yes", release a new major release + (e.g. 7.0.0 -> 8.0.0) +- If there are any ``.feature.rst`` or ``.breaking.rst`` files in the + ``changelog`` directory, release a new minor release (e.g. 7.0.0 -> 7.1.0) +- Otherwise, release a bugfix release (e.g. 7.0.0 -> 7.0.1) +- If the "prerelease" input is set, append the string to the version number + (e.g. 7.0.0 -> 8.0.0rc1), if "major" is set, and "prerelease" is set to `rc1`) - @pytestbot please prepare release from 5.1.x +Bug-fix and minor releases +^^^^^^^^^^^^^^^^^^^^^^^^^^ -Where ``5.1.x`` is the maintenance branch for the ``5.1`` series. +Bug-fix and minor releases are always done from a maintenance branch. First, +consider double-checking the ``changelog`` directory to see if there are any +breaking changes or new features. -The automated workflow will publish a PR for a branch ``release-5.1.2`` -and notify it as a comment in the issue. +For a new minor release, first create a new maintenance branch from ``main``:: -Minor releases -^^^^^^^^^^^^^^ + git fetch --all + git branch 7.1.x upstream/main + git push upstream 7.1.x -1. Create a new maintenance branch from ``main``:: +Then, trigger the workflow with the following inputs: - git fetch --all - git branch 5.2.x upstream/main - git push upstream 5.2.x +- branch: **7.1.x** +- major release: **no** +- prerelease: empty + +Or via the commandline using `GitHub's cli `__:: -2. Open a new issue and add this comment to the body:: + gh workflow run prepare-release-pr.yml -f branch=7.1.x -f major=no -f prerelease= - @pytestbot please prepare release from 5.2.x +Where ``7.1.x`` is the maintenance branch for the ``7.1`` series. The automated +workflow will publish a PR for a branch ``release-7.1.0``. -The automated workflow will publish a PR for a branch ``release-5.2.0`` and -notify it as a comment in the issue. +Similarly, for a bug-fix release, use the existing maintenance branch and +trigger the workflow with e.g. ``branch: 7.0.x`` to get a new ``release-7.0.1`` +PR. -Major and release candidates -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Major releases +^^^^^^^^^^^^^^ 1. Create a new maintenance branch from ``main``:: git fetch --all - git branch 6.0.x upstream/main - git push upstream 6.0.x + git branch 8.0.x upstream/main + git push upstream 8.0.x -2. For a **major release**, open a new issue and add this comment in the body:: +2. Trigger the workflow with the following inputs: - @pytestbot please prepare major release from 6.0.x + - branch: **8.0.x** + - major release: **yes** + - prerelease: empty - For a **release candidate**, the comment must be (TODO: `#7551 `__):: +Or via the commandline:: - @pytestbot please prepare release candidate from 6.0.x + gh workflow run prepare-release-pr.yml -f branch=8.0.x -f major=yes -f prerelease= -The automated workflow will publish a PR for a branch ``release-6.0.0`` and -notify it as a comment in the issue. +The automated workflow will publish a PR for a branch ``release-8.0.0``. At this point on, this follows the same workflow as other maintenance branches: bug-fixes are merged into ``main`` and ported back to the maintenance branch, even for release candidates. +Release candidates +^^^^^^^^^^^^^^^^^^ + +To release a release candidate, set the "prerelease" input to the version number +suffix to use. To release a ``8.0.0rc1``, proceed like under "major releases", but set: + +- branch: 8.0.x +- major release: yes +- prerelease: **rc1** + +Or via the commandline:: + + gh workflow run prepare-release-pr.yml -f branch=8.0.x -f major=yes -f prerelease=rc1 + +The automated workflow will publish a PR for a branch ``release-8.0.0rc1``. + **A note about release candidates** During release candidates we can merge small improvements into diff --git a/scripts/release-on-comment.py b/scripts/release-on-comment.py deleted file mode 100644 index d5996aa4086..00000000000 --- a/scripts/release-on-comment.py +++ /dev/null @@ -1,261 +0,0 @@ -""" -This script is part of the pytest release process which is triggered by comments -in issues. - -This script is started by the `release-on-comment.yml` workflow, which always executes on -`main` and is triggered by two comment related events: - -* https://help.github.com/en/actions/reference/events-that-trigger-workflows#issue-comment-event-issue_comment -* https://help.github.com/en/actions/reference/events-that-trigger-workflows#issues-event-issues - -This script receives the payload and a secrets on the command line. - -The payload must contain a comment with a phrase matching this pseudo-regular expression: - - @pytestbot please prepare (major )? release from - -Then the appropriate version will be obtained based on the given branch name: - -* a major release from main if "major" appears in the phrase in that position -* a feature or bug fix release from main (based if there are features in the current changelog - folder) -* a bug fix from a maintenance branch - -After that, it will create a release using the `release` tox environment, and push a new PR. - -**Token**: currently the token from the GitHub Actions is used, pushed with -`pytest bot ` commit author. -""" -import argparse -import json -import os -import re -import traceback -from pathlib import Path -from subprocess import CalledProcessError -from subprocess import check_call -from subprocess import check_output -from subprocess import run -from textwrap import dedent -from typing import Dict -from typing import Optional -from typing import Tuple - -from colorama import Fore -from colorama import init -from github3.repos import Repository - - -class InvalidFeatureRelease(Exception): - pass - - -SLUG = "pytest-dev/pytest" - -PR_BODY = """\ -Created automatically from {comment_url}. - -Once all builds pass and it has been **approved** by one or more maintainers, the build -can be released by pushing a tag `{version}` to this repository. - -Closes #{issue_number}. -""" - - -def login(token: str) -> Repository: - import github3 - - github = github3.login(token=token) - owner, repo = SLUG.split("/") - return github.repository(owner, repo) - - -def get_comment_data(payload: Dict) -> str: - if "comment" in payload: - return payload["comment"] - else: - return payload["issue"] - - -def validate_and_get_issue_comment_payload( - issue_payload_path: Optional[Path], -) -> Tuple[str, str, bool]: - payload = json.loads(issue_payload_path.read_text(encoding="UTF-8")) - body = get_comment_data(payload)["body"] - m = re.match(r"@pytestbot please prepare (major )?release from ([-_.\w]+)", body) - if m: - is_major, base_branch = m.group(1) is not None, m.group(2) - else: - is_major, base_branch = False, None - return payload, base_branch, is_major - - -def print_and_exit(msg) -> None: - print(msg) - raise SystemExit(1) - - -def trigger_release(payload_path: Path, token: str) -> None: - payload, base_branch, is_major = validate_and_get_issue_comment_payload( - payload_path - ) - if base_branch is None: - url = get_comment_data(payload)["html_url"] - print_and_exit( - f"Comment {Fore.CYAN}{url}{Fore.RESET} did not match the trigger command." - ) - print() - print(f"Precessing release for branch {Fore.CYAN}{base_branch}") - - repo = login(token) - - issue_number = payload["issue"]["number"] - issue = repo.issue(issue_number) - - check_call(["git", "checkout", f"origin/{base_branch}"]) - - try: - version = find_next_version(base_branch, is_major) - except InvalidFeatureRelease as e: - issue.create_comment(str(e)) - print_and_exit(f"{Fore.RED}{e}") - - error_contents = "" - try: - print(f"Version: {Fore.CYAN}{version}") - - release_branch = f"release-{version}" - - run( - ["git", "config", "user.name", "pytest bot"], - text=True, - check=True, - capture_output=True, - ) - run( - ["git", "config", "user.email", "pytestbot@gmail.com"], - text=True, - check=True, - capture_output=True, - ) - - run( - ["git", "checkout", "-b", release_branch, f"origin/{base_branch}"], - text=True, - check=True, - capture_output=True, - ) - - print(f"Branch {Fore.CYAN}{release_branch}{Fore.RESET} created.") - - # important to use tox here because we have changed branches, so dependencies - # might have changed as well - cmdline = ["tox", "-e", "release", "--", version, "--skip-check-links"] - print("Running", " ".join(cmdline)) - run( - cmdline, - text=True, - check=True, - capture_output=True, - ) - - oauth_url = f"https://{token}:x-oauth-basic@github.com/{SLUG}.git" - run( - ["git", "push", oauth_url, f"HEAD:{release_branch}", "--force"], - text=True, - check=True, - capture_output=True, - ) - print(f"Branch {Fore.CYAN}{release_branch}{Fore.RESET} pushed.") - - body = PR_BODY.format( - comment_url=get_comment_data(payload)["html_url"], - version=version, - issue_number=issue_number, - ) - pr = repo.create_pull( - f"Prepare release {version}", - base=base_branch, - head=release_branch, - body=body, - ) - print(f"Pull request {Fore.CYAN}{pr.url}{Fore.RESET} created.") - - comment = issue.create_comment( - f"As requested, opened a PR for release `{version}`: #{pr.number}." - ) - print(f"Notified in original comment {Fore.CYAN}{comment.url}{Fore.RESET}.") - - except CalledProcessError as e: - error_contents = f"CalledProcessError\noutput:\n{e.output}\nstderr:\n{e.stderr}" - except Exception: - error_contents = f"Exception:\n{traceback.format_exc()}" - - if error_contents: - link = f"https://github.com/{SLUG}/actions/runs/{os.environ['GITHUB_RUN_ID']}" - msg = ERROR_COMMENT.format( - version=version, base_branch=base_branch, contents=error_contents, link=link - ) - issue.create_comment(msg) - print_and_exit(f"{Fore.RED}{error_contents}") - else: - print(f"{Fore.GREEN}Success.") - - -ERROR_COMMENT = """\ -The request to prepare release `{version}` from {base_branch} failed with: - -``` -{contents} -``` - -See: {link}. -""" - - -def find_next_version(base_branch: str, is_major: bool) -> str: - output = check_output(["git", "tag"], encoding="UTF-8") - valid_versions = [] - for v in output.splitlines(): - m = re.match(r"\d.\d.\d+$", v.strip()) - if m: - valid_versions.append(tuple(int(x) for x in v.split("."))) - - valid_versions.sort() - last_version = valid_versions[-1] - - changelog = Path("changelog") - - features = list(changelog.glob("*.feature.rst")) - breaking = list(changelog.glob("*.breaking.rst")) - is_feature_release = features or breaking - - if is_feature_release and base_branch != "main": - msg = dedent( - f""" - Found features or breaking changes in `{base_branch}`, and feature releases can only be - created from `main`: - """ - ) - msg += "\n".join(f"* `{x.name}`" for x in sorted(features + breaking)) - raise InvalidFeatureRelease(msg) - - if is_major: - return f"{last_version[0]+1}.0.0" - elif is_feature_release: - return f"{last_version[0]}.{last_version[1] + 1}.0" - else: - return f"{last_version[0]}.{last_version[1]}.{last_version[2] + 1}" - - -def main() -> None: - init(autoreset=True) - parser = argparse.ArgumentParser() - parser.add_argument("payload") - parser.add_argument("token") - options = parser.parse_args() - trigger_release(Path(options.payload), options.token) - - -if __name__ == "__main__": - main() diff --git a/tox.ini b/tox.ini index 1c1d2bb2c1f..c6dd3f477ee 100644 --- a/tox.ini +++ b/tox.ini @@ -149,13 +149,6 @@ deps = towncrier commands = python scripts/release.py {posargs} -[testenv:release-on-comment] -decription = do a release from a comment on GitHub -usedevelop = {[testenv:release]usedevelop} -passenv = {[testenv:release]passenv} -deps = {[testenv:release]deps} -commands = python scripts/release-on-comment.py {posargs} - [testenv:prepare-release-pr] decription = prepare a release PR from a manual trigger in GitHub actions usedevelop = {[testenv:release]usedevelop} From e729e0d1e6b44d0d4305df4813514029d5bf9830 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 28 Jun 2021 14:15:25 +0200 Subject: [PATCH 365/630] scripts: Don't capture output in prepare-release-pr.py (#8805) We don't need to access the output in the script, and if we capture it, we don't get any output on CI, so no way to see why a release failed. --- scripts/prepare-release-pr.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/scripts/prepare-release-pr.py b/scripts/prepare-release-pr.py index 27152fea1ca..82da553cb2d 100644 --- a/scripts/prepare-release-pr.py +++ b/scripts/prepare-release-pr.py @@ -66,22 +66,16 @@ def prepare_release_pr( run( ["git", "config", "user.name", "pytest bot"], - text=True, check=True, - capture_output=True, ) run( ["git", "config", "user.email", "pytestbot@gmail.com"], - text=True, check=True, - capture_output=True, ) run( ["git", "checkout", "-b", release_branch, f"origin/{base_branch}"], - text=True, check=True, - capture_output=True, ) print(f"Branch {Fore.CYAN}{release_branch}{Fore.RESET} created.") @@ -92,17 +86,13 @@ def prepare_release_pr( print("Running", " ".join(cmdline)) run( cmdline, - text=True, check=True, - capture_output=True, ) oauth_url = f"https://{token}:x-oauth-basic@github.com/{SLUG}.git" run( ["git", "push", oauth_url, f"HEAD:{release_branch}", "--force"], - text=True, check=True, - capture_output=True, ) print(f"Branch {Fore.CYAN}{release_branch}{Fore.RESET} pushed.") From c4c3b6d4fd9e127c30b3b3c9e9fcd8b844aa7bca Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 28 Jun 2021 19:41:22 +0200 Subject: [PATCH 366/630] Adjust fixme comments (#8811) --- src/_pytest/mark/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/_pytest/mark/__init__.py b/src/_pytest/mark/__init__.py index 97fb36ef73b..0dc3718e89b 100644 --- a/src/_pytest/mark/__init__.py +++ b/src/_pytest/mark/__init__.py @@ -190,12 +190,12 @@ def deselect_by_keyword(items: "List[Item]", config: Config) -> None: return if keywordexpr.startswith("-"): - # To be removed in pytest 7.0.0. + # To be removed in pytest 8.0.0. warnings.warn(MINUS_K_DASH, stacklevel=2) keywordexpr = "not " + keywordexpr[1:] selectuntil = False if keywordexpr[-1:] == ":": - # To be removed in pytest 7.0.0. + # To be removed in pytest 8.0.0. warnings.warn(MINUS_K_COLON, stacklevel=2) selectuntil = True keywordexpr = keywordexpr[:-1] From ba1ad7d517a9e67ae80bc6c644faf288e7c4ed99 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 28 Jun 2021 19:47:49 +0200 Subject: [PATCH 367/630] Opt-out from regendoc for problematic example (#8810) * Opt-out from regendoc for problematic example See #8807 * Add comment --- doc/en/how-to/skipping.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/en/how-to/skipping.rst b/doc/en/how-to/skipping.rst index 2637326327d..156570062f1 100644 --- a/doc/en/how-to/skipping.rst +++ b/doc/en/how-to/skipping.rst @@ -369,9 +369,12 @@ Here is a simple test file with the several usages: Running it with the report-on-xfail option gives this output: +.. FIXME: Use $ instead of ! again to reenable regendoc once it's fixed: + https://github.com/pytest-dev/pytest/issues/8807 + .. code-block:: pytest - example $ pytest -rx xfail_demo.py + ! pytest -rx xfail_demo.py =========================== test session starts ============================ platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y cachedir: $PYTHON_PREFIX/.pytest_cache From 109312ba86a7142dfff30f3309d6f7a596f02db1 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sun, 17 Jan 2021 08:43:36 -0300 Subject: [PATCH 368/630] Make --version write to stdout rather than stderr Fix #8246 --- changelog/8246.breaking.rst | 1 + src/_pytest/helpconfig.py | 6 +++--- testing/test_config.py | 2 +- testing/test_helpconfig.py | 6 +++--- 4 files changed, 8 insertions(+), 7 deletions(-) create mode 100644 changelog/8246.breaking.rst diff --git a/changelog/8246.breaking.rst b/changelog/8246.breaking.rst new file mode 100644 index 00000000000..ba9e5c8beea --- /dev/null +++ b/changelog/8246.breaking.rst @@ -0,0 +1 @@ +``--version`` now writes version information to ``stdout`` rather than ``stderr``. diff --git a/src/_pytest/helpconfig.py b/src/_pytest/helpconfig.py index b9360cecf67..78da45f0c3e 100644 --- a/src/_pytest/helpconfig.py +++ b/src/_pytest/helpconfig.py @@ -127,7 +127,7 @@ def unset_tracing() -> None: def showversion(config: Config) -> None: if config.option.version > 1: - sys.stderr.write( + sys.stdout.write( "This is pytest version {}, imported from {}\n".format( pytest.__version__, pytest.__file__ ) @@ -135,9 +135,9 @@ def showversion(config: Config) -> None: plugininfo = getpluginversioninfo(config) if plugininfo: for line in plugininfo: - sys.stderr.write(line + "\n") + sys.stdout.write(line + "\n") else: - sys.stderr.write(f"pytest {pytest.__version__}\n") + sys.stdout.write(f"pytest {pytest.__version__}\n") def pytest_cmdline_main(config: Config) -> Optional[Union[int, ExitCode]]: diff --git a/testing/test_config.py b/testing/test_config.py index 5dabb781242..1fdcea9bff0 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -1756,7 +1756,7 @@ def pytest_addoption(parser): assert result.ret == ExitCode.USAGE_ERROR result = pytester.runpytest("--version") - result.stderr.fnmatch_lines([f"pytest {pytest.__version__}"]) + result.stdout.fnmatch_lines([f"pytest {pytest.__version__}"]) assert result.ret == ExitCode.USAGE_ERROR diff --git a/testing/test_helpconfig.py b/testing/test_helpconfig.py index e2a27b4e706..dd77b55b5a8 100644 --- a/testing/test_helpconfig.py +++ b/testing/test_helpconfig.py @@ -7,16 +7,16 @@ def test_version_verbose(pytester: Pytester, pytestconfig, monkeypatch) -> None: monkeypatch.delenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD") result = pytester.runpytest("--version", "--version") assert result.ret == 0 - result.stderr.fnmatch_lines([f"*pytest*{pytest.__version__}*imported from*"]) + result.stdout.fnmatch_lines([f"*pytest*{pytest.__version__}*imported from*"]) if pytestconfig.pluginmanager.list_plugin_distinfo(): - result.stderr.fnmatch_lines(["*setuptools registered plugins:", "*at*"]) + result.stdout.fnmatch_lines(["*setuptools registered plugins:", "*at*"]) def test_version_less_verbose(pytester: Pytester, pytestconfig, monkeypatch) -> None: monkeypatch.delenv("PYTEST_DISABLE_PLUGIN_AUTOLOAD") result = pytester.runpytest("--version") assert result.ret == 0 - result.stderr.fnmatch_lines([f"pytest {pytest.__version__}"]) + result.stdout.fnmatch_lines([f"pytest {pytest.__version__}"]) def test_versions(): From 4d965557e2fca3bd9486c4bec7ed139ef8dc7413 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 28 Jun 2021 20:58:55 +0200 Subject: [PATCH 369/630] ci: Persist git credentials for prepare-release-pr (#8813) See #8599, follow-up to #8681 --- .github/workflows/prepare-release-pr.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/prepare-release-pr.yml b/.github/workflows/prepare-release-pr.yml index 7fa036613f3..08d4fc8a975 100644 --- a/.github/workflows/prepare-release-pr.yml +++ b/.github/workflows/prepare-release-pr.yml @@ -30,7 +30,6 @@ jobs: - uses: actions/checkout@v2 with: fetch-depth: 0 - persist-credentials: false - name: Set up Python uses: actions/setup-python@v2 From 9cdb7223b5f465d6511e2da1885ad37701eb8d02 Mon Sep 17 00:00:00 2001 From: Srip Date: Tue, 29 Jun 2021 23:11:20 -0700 Subject: [PATCH 370/630] doc: Adding the missing sys import (#8819) (#8827) Co-authored-by: Sripradha Karkala --- doc/en/how-to/usage.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/en/how-to/usage.rst b/doc/en/how-to/usage.rst index ce8247b44ee..f9b38b19ea6 100644 --- a/doc/en/how-to/usage.rst +++ b/doc/en/how-to/usage.rst @@ -184,6 +184,7 @@ You can specify additional plugins to ``pytest.main``: # content of myinvoke.py import pytest + import sys class MyPlugin: From 501637547ecefa584db3793f71f1863da5ffc25f Mon Sep 17 00:00:00 2001 From: ericluoliu <54130092+ericluoliu@users.noreply.github.com> Date: Wed, 30 Jun 2021 06:53:32 -0700 Subject: [PATCH 371/630] Issue #8823 - remove bots from contributors list (#8828) * fix-contributors-list * remove-bots * delete-extraneous-file * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- scripts/release.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/release.py b/scripts/release.py index 6892a0758ea..2cfe063e981 100644 --- a/scripts/release.py +++ b/scripts/release.py @@ -20,7 +20,7 @@ def announce(version): stdout = check_output(["git", "log", f"{last_version}..HEAD", "--format=%aN"]) stdout = stdout.decode("utf-8") - contributors = set(stdout.splitlines()) + contributors = {name for name in stdout.splitlines() if not name.endswith("[bot]")} template_name = ( "release.minor.rst" if version.endswith(".0") else "release.patch.rst" From 8c25a140322d81ee6d2129556ee6e670e588be73 Mon Sep 17 00:00:00 2001 From: Eric Liu <54130092+ericluoliu@users.noreply.github.com> Date: Sat, 3 Jul 2021 03:20:12 -0700 Subject: [PATCH 372/630] closes #8824 Changelog rewording for 7.0 (#8826) * fixed changelog/*.rst docs * add author name * fixed-documents * fix issue 8761 doc * fix issue 8645 doc * fix issue 8447 doc * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- AUTHORS | 1 + changelog/7469.deprecation.rst | 2 +- changelog/7469.feature.rst | 2 +- changelog/8242.deprecation.rst | 2 +- changelog/8248.trivial.rst | 2 +- changelog/8315.deprecation.rst | 2 +- changelog/8411.trivial.rst | 1 - changelog/8447.deprecation.rst | 2 +- changelog/{8503.trivial.rst => 8503.bugfix.rst} | 0 changelog/8509.improvement.rst | 4 ++-- changelog/8606.feature.rst | 2 +- changelog/8645.deprecation.rst | 4 ++++ changelog/8645.improvement.rst | 4 ---- 13 files changed, 14 insertions(+), 14 deletions(-) delete mode 100644 changelog/8411.trivial.rst rename changelog/{8503.trivial.rst => 8503.bugfix.rst} (100%) create mode 100644 changelog/8645.deprecation.rst delete mode 100644 changelog/8645.improvement.rst diff --git a/AUTHORS b/AUTHORS index f979fee0683..5c3610e699c 100644 --- a/AUTHORS +++ b/AUTHORS @@ -107,6 +107,7 @@ Eli Boyarski Elizaveta Shashkova Endre Galaczi Eric Hunsberger +Eric Liu Eric Siegerman Erik Aronesty Erik M. Bray diff --git a/changelog/7469.deprecation.rst b/changelog/7469.deprecation.rst index 3762306c736..423b1633ba8 100644 --- a/changelog/7469.deprecation.rst +++ b/changelog/7469.deprecation.rst @@ -9,4 +9,4 @@ Directly constructing the following classes is now deprecated: - ``_pytest.config.argparsing.Parser`` - ``_pytest.config.argparsing.OptionGroup`` -These have always been considered private, but now issue a deprecation warning, which may become a hard error in pytest 7.0.0. +These have always been considered private, but now issue a deprecation warning, which may become a hard error in pytest 8.0.0. diff --git a/changelog/7469.feature.rst b/changelog/7469.feature.rst index 9922631fee9..3013170870a 100644 --- a/changelog/7469.feature.rst +++ b/changelog/7469.feature.rst @@ -14,6 +14,6 @@ The newly-exported types are: - ``pytest.OptionGroup`` for the :class:`OptionGroup ` type returned from the :func:`parser.addgroup ` method. Constructing them directly is not supported; they are only meant for use in type annotations. -Doing so will emit a deprecation warning, and may become a hard-error in pytest 7.0. +Doing so will emit a deprecation warning, and may become a hard-error in pytest 8.0. Subclassing them is also not supported. This is not currently enforced at runtime, but is detected by type-checkers such as mypy. diff --git a/changelog/8242.deprecation.rst b/changelog/8242.deprecation.rst index b2e8566eaa9..3875c5867e3 100644 --- a/changelog/8242.deprecation.rst +++ b/changelog/8242.deprecation.rst @@ -1,7 +1,7 @@ Raising :class:`unittest.SkipTest` to skip collection of tests during the pytest collection phase is deprecated. Use :func:`pytest.skip` instead. -Note: This deprecation only relates to using `unittest.SkipTest` during test +Note: This deprecation only relates to using :class:`unittest.SkipTest` during test collection. You are probably not doing that. Ordinary usage of :class:`unittest.SkipTest` / :meth:`unittest.TestCase.skipTest` / :func:`unittest.skip` in unittest test cases is fully supported. diff --git a/changelog/8248.trivial.rst b/changelog/8248.trivial.rst index 0a9319d9cd5..3044071fa81 100644 --- a/changelog/8248.trivial.rst +++ b/changelog/8248.trivial.rst @@ -1 +1 @@ -Internal Restructure: let python.PyObjMixing inherit from nodes.Node to carry over typing information. +Internal Restructure: let ``python.PyObjMixin`` inherit from ``nodes.Node`` to carry over typing information. diff --git a/changelog/8315.deprecation.rst b/changelog/8315.deprecation.rst index c30dcf5d13e..b204dcedd9b 100644 --- a/changelog/8315.deprecation.rst +++ b/changelog/8315.deprecation.rst @@ -1,5 +1,5 @@ Several behaviors of :meth:`Parser.addoption ` are now -scheduled for removal in pytest 7 (deprecated since pytest 2.4.0): +scheduled for removal in pytest 8 (deprecated since pytest 2.4.0): - ``parser.addoption(..., help=".. %default ..")`` - use ``%(default)s`` instead. - ``parser.addoption(..., type="int/string/float/complex")`` - use ``type=int`` etc. instead. diff --git a/changelog/8411.trivial.rst b/changelog/8411.trivial.rst deleted file mode 100644 index 8f169a900fa..00000000000 --- a/changelog/8411.trivial.rst +++ /dev/null @@ -1 +0,0 @@ -Assert the outcomes for the issue 518 test and fix the test. diff --git a/changelog/8447.deprecation.rst b/changelog/8447.deprecation.rst index d1011f8c849..0116483a3bb 100644 --- a/changelog/8447.deprecation.rst +++ b/changelog/8447.deprecation.rst @@ -1,4 +1,4 @@ -Defining a custom pytest node type which is both an item and a collector now issues a warning. +Defining a custom pytest node type which is both an :class:`pytest.Item ` and a :class:`pytest.Collector ` now issues a warning. It was never sanely supported and triggers hard to debug errors. Instead, a separate collector node should be used, which collects the item. See :ref:`non-python tests` for an example. diff --git a/changelog/8503.trivial.rst b/changelog/8503.bugfix.rst similarity index 100% rename from changelog/8503.trivial.rst rename to changelog/8503.bugfix.rst diff --git a/changelog/8509.improvement.rst b/changelog/8509.improvement.rst index d72e8e487f6..982888c1fea 100644 --- a/changelog/8509.improvement.rst +++ b/changelog/8509.improvement.rst @@ -1,5 +1,5 @@ -Fixed issue where `TestCase.setUpClass` is not called when a test has `/` in its name since pytest 6.2.0. +Fixed issue where :meth:`unittest.TestCase.setUpClass` is not called when a test has `/` in its name since pytest 6.2.0. -This refers to the path part in pytest node IDs, e.g. `TestClass::test_it` in the node ID `tests/test_file.py::TestClass::test_it`. +This refers to the path part in pytest node IDs, e.g. ``TestClass::test_it`` in the node ID ``tests/test_file.py::TestClass::test_it``. Now, instead of assuming that the test name does not contain ``/``, it is assumed that test path does not contain ``::``. We plan to hopefully make both of these work in the future. diff --git a/changelog/8606.feature.rst b/changelog/8606.feature.rst index 9705ffe75d6..d918ecd1ec1 100644 --- a/changelog/8606.feature.rst +++ b/changelog/8606.feature.rst @@ -1,4 +1,4 @@ -pytest invocations with ``--fixtures-per-test`` and ``--fixtures`` have been enabled with: +pytest invocations with ``--fixtures-per-test`` and ``--fixtures`` have been enriched with: - Fixture location path printed with the fixture name. - First section of the fixture's docstring printed under the fixture name. diff --git a/changelog/8645.deprecation.rst b/changelog/8645.deprecation.rst new file mode 100644 index 00000000000..722a5d764de --- /dev/null +++ b/changelog/8645.deprecation.rst @@ -0,0 +1,4 @@ +:func:`pytest.warns(None) ` is now deprecated because many people used +it to mean "this code does not emit warnings", but it actually had the effect of +checking that the code emits at least one warning of any type - like ``pytest.warns()`` +or ``pytest.warns(Warning)``. diff --git a/changelog/8645.improvement.rst b/changelog/8645.improvement.rst deleted file mode 100644 index b3a68b0b8c8..00000000000 --- a/changelog/8645.improvement.rst +++ /dev/null @@ -1,4 +0,0 @@ -Reducing confusion from `pytest.warns(None)` by: - -- Allowing no arguments to be passed in order to catch any exception (no argument defaults to `Warning`). -- Emit a deprecation warning if passed `None`. From 8347b59295535ffdf0d3c69e5acf81bfad7d5dd7 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sun, 4 Jul 2021 10:27:43 +0200 Subject: [PATCH 373/630] Update remaining 7.0 references (#8834) --- doc/en/changelog.rst | 4 ++-- testing/test_recwarn.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/en/changelog.rst b/doc/en/changelog.rst index e2b2635af9d..80983ecdfb3 100644 --- a/doc/en/changelog.rst +++ b/doc/en/changelog.rst @@ -119,7 +119,7 @@ Deprecations - ``_pytest.tmpdir.TempPathFactory`` - ``_pytest.tmpdir.TempdirFactory`` - These have always been considered private, but now issue a deprecation warning, which may become a hard error in pytest 7.0.0. + These have always been considered private, but now issue a deprecation warning, which may become a hard error in pytest 8.0.0. - `#7530 `_: The ``--strict`` command-line option has been deprecated, use ``--strict-markers`` instead. @@ -205,7 +205,7 @@ Improvements - ``pytest.WarningsRecorder`` for the :fixture:`recwarn` fixture. Constructing them is not supported (except for `MonkeyPatch`); they are only meant for use in type annotations. - Doing so will emit a deprecation warning, and may become a hard-error in pytest 7.0. + Doing so will emit a deprecation warning, and may become a hard-error in pytest 8.0. Subclassing them is also not supported. This is not currently enforced at runtime, but is detected by type-checkers such as mypy. diff --git a/testing/test_recwarn.py b/testing/test_recwarn.py index cb000d84146..b82fd9a865b 100644 --- a/testing/test_recwarn.py +++ b/testing/test_recwarn.py @@ -318,7 +318,7 @@ def test_record_only(self) -> None: assert str(record[1].message) == "runtime" def test_record_only_none_deprecated_warn(self) -> None: - # This should become an error when WARNS_NONE_ARG is removed in Pytest 7.0 + # This should become an error when WARNS_NONE_ARG is removed in Pytest 8.0 with warnings.catch_warnings(): warnings.simplefilter("ignore") with pytest.warns(None) as record: # type: ignore[call-overload] From 2d5990c443e89c39c5afa662cd6696ba1b90ff31 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Jul 2021 03:01:49 +0000 Subject: [PATCH 374/630] build(deps): bump django in /testing/plugins_integration Bumps [django](https://github.com/django/django) from 3.2.4 to 3.2.5. - [Release notes](https://github.com/django/django/releases) - [Commits](https://github.com/django/django/compare/3.2.4...3.2.5) --- updated-dependencies: - dependency-name: django dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- testing/plugins_integration/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/plugins_integration/requirements.txt b/testing/plugins_integration/requirements.txt index daede39556f..d47dd1e721e 100644 --- a/testing/plugins_integration/requirements.txt +++ b/testing/plugins_integration/requirements.txt @@ -1,5 +1,5 @@ anyio[curio,trio]==3.2.1 -django==3.2.4 +django==3.2.5 pytest-asyncio==0.15.1 pytest-bdd==4.0.2 pytest-cov==2.12.1 From 3e108012d9d6d4b674bfb44c71d2278e8d5a2d28 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Jul 2021 03:01:51 +0000 Subject: [PATCH 375/630] build(deps): bump pytest-rerunfailures in /testing/plugins_integration Bumps [pytest-rerunfailures](https://github.com/pytest-dev/pytest-rerunfailures) from 10.0 to 10.1. - [Release notes](https://github.com/pytest-dev/pytest-rerunfailures/releases) - [Changelog](https://github.com/pytest-dev/pytest-rerunfailures/blob/master/CHANGES.rst) - [Commits](https://github.com/pytest-dev/pytest-rerunfailures/compare/10.0...10.1) --- updated-dependencies: - dependency-name: pytest-rerunfailures dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- testing/plugins_integration/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/plugins_integration/requirements.txt b/testing/plugins_integration/requirements.txt index daede39556f..7da3879235d 100644 --- a/testing/plugins_integration/requirements.txt +++ b/testing/plugins_integration/requirements.txt @@ -7,7 +7,7 @@ pytest-django==4.4.0 pytest-flakes==4.0.3 pytest-html==3.1.1 pytest-mock==3.6.1 -pytest-rerunfailures==10.0 +pytest-rerunfailures==10.1 pytest-sugar==0.9.4 pytest-trio==0.7.0 pytest-twisted==1.13.2 From 904637baa74687c7f02f6fde742e3b6293e79fa9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Jul 2021 03:01:53 +0000 Subject: [PATCH 376/630] build(deps): bump pytest-bdd in /testing/plugins_integration Bumps [pytest-bdd](https://github.com/pytest-dev/pytest-bdd) from 4.0.2 to 4.1.0. - [Release notes](https://github.com/pytest-dev/pytest-bdd/releases) - [Changelog](https://github.com/pytest-dev/pytest-bdd/blob/master/CHANGES.rst) - [Commits](https://github.com/pytest-dev/pytest-bdd/compare/4.0.2...4.1.0) --- updated-dependencies: - dependency-name: pytest-bdd dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- testing/plugins_integration/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/plugins_integration/requirements.txt b/testing/plugins_integration/requirements.txt index daede39556f..3d047123849 100644 --- a/testing/plugins_integration/requirements.txt +++ b/testing/plugins_integration/requirements.txt @@ -1,7 +1,7 @@ anyio[curio,trio]==3.2.1 django==3.2.4 pytest-asyncio==0.15.1 -pytest-bdd==4.0.2 +pytest-bdd==4.1.0 pytest-cov==2.12.1 pytest-django==4.4.0 pytest-flakes==4.0.3 From e942b12b94ca89a5a8300c38d8a04b1cb7fdb2e5 Mon Sep 17 00:00:00 2001 From: Taneli Hukkinen <3275109+hukkin@users.noreply.github.com> Date: Tue, 6 Jul 2021 00:26:01 +0300 Subject: [PATCH 377/630] Support TOML v1.0.0 syntax in `pyproject.toml` --- .pre-commit-config.yaml | 2 +- AUTHORS | 1 + changelog/8789.feature.rst | 1 + setup.cfg | 2 +- src/_pytest/config/findpaths.py | 4 ++-- testing/test_findpaths.py | 2 ++ 6 files changed, 8 insertions(+), 4 deletions(-) create mode 100644 changelog/8789.feature.rst diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0490ae968c2..adf3f9bdc23 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -58,7 +58,7 @@ repos: - py>=1.8.2 - attrs>=19.2.0 - packaging - - types-toml + - tomli - types-pkg_resources - repo: local hooks: diff --git a/AUTHORS b/AUTHORS index 5c3610e699c..16a53d94bdf 100644 --- a/AUTHORS +++ b/AUTHORS @@ -302,6 +302,7 @@ Sven-Hendrik Haase Sylvain Marié Tadek Teleżyński Takafumi Arakaki +Taneli Hukkinen Tanvi Mehta Tarcisio Fischer Tareq Alayan diff --git a/changelog/8789.feature.rst b/changelog/8789.feature.rst new file mode 100644 index 00000000000..198d9ade3c2 --- /dev/null +++ b/changelog/8789.feature.rst @@ -0,0 +1 @@ +Support TOML v1.0.0 syntax in ``pyproject.toml``. diff --git a/setup.cfg b/setup.cfg index 0e86f10e7f6..094dfed87fa 100644 --- a/setup.cfg +++ b/setup.cfg @@ -47,7 +47,7 @@ install_requires = packaging pluggy>=0.12,<1.0.0a1 py>=1.8.2 - toml + tomli>=1.0.0,<2.0.0 atomicwrites>=1.0;sys_platform=="win32" colorama;sys_platform=="win32" importlib-metadata>=0.12;python_version<"3.8" diff --git a/src/_pytest/config/findpaths.py b/src/_pytest/config/findpaths.py index 7dde4b92d41..62214f8d973 100644 --- a/src/_pytest/config/findpaths.py +++ b/src/_pytest/config/findpaths.py @@ -64,9 +64,9 @@ def load_config_dict_from_file( # '.toml' files are considered if they contain a [tool.pytest.ini_options] table. elif filepath.suffix == ".toml": - import toml + import tomli - config = toml.load(str(filepath)) + config = tomli.loads(filepath.read_text(encoding="utf-8")) result = config.get("tool", {}).get("pytest", {}).get("ini_options", None) if result is not None: diff --git a/testing/test_findpaths.py b/testing/test_findpaths.py index af6aeb3a56d..3c876d96b59 100644 --- a/testing/test_findpaths.py +++ b/testing/test_findpaths.py @@ -77,6 +77,7 @@ def test_valid_toml_file(self, tmp_path: Path) -> None: y = 20.0 values = ["tests", "integration"] name = "foo" + heterogeneous_array = [1, "str"] """ ), encoding="utf-8", @@ -86,6 +87,7 @@ def test_valid_toml_file(self, tmp_path: Path) -> None: "y": "20.0", "values": ["tests", "integration"], "name": "foo", + "heterogeneous_array": [1, "str"], } From 953fdabaf0c7f328251fd5a92b93d9e763cec206 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 6 Jul 2021 09:09:04 +0200 Subject: [PATCH 378/630] Adjust doc links for new scheme Closes #8831 --- CONTRIBUTING.rst | 2 +- README.rst | 10 +++--- doc/en/announce/release-2.0.0.rst | 6 ++-- doc/en/announce/release-2.0.1.rst | 2 +- doc/en/announce/release-2.1.0.rst | 2 +- doc/en/announce/release-2.2.0.rst | 4 +-- doc/en/announce/release-2.3.0.rst | 4 +-- doc/en/announce/release-2.3.4.rst | 2 +- doc/en/announce/release-2.9.0.rst | 2 +- doc/en/builtin.rst | 2 +- doc/en/changelog.rst | 56 +++++++++++++++--------------- doc/en/example/markers.rst | 20 +++++------ doc/en/explanation/flaky.rst | 2 +- doc/en/getting-started.rst | 2 +- doc/en/history.rst | 2 +- doc/en/how-to/assert.rst | 2 +- doc/en/how-to/capture-warnings.rst | 6 ++-- doc/en/how-to/doctest.rst | 4 +-- doc/en/how-to/unittest.rst | 4 +-- doc/en/how-to/xunit_setup.rst | 2 +- doc/en/projects.rst | 2 +- doc/en/reference/reference.rst | 2 +- src/_pytest/cacheprovider.py | 2 +- src/_pytest/fixtures.py | 2 +- src/_pytest/mark/structures.py | 2 +- src/_pytest/outcomes.py | 2 +- src/_pytest/python.py | 4 +-- src/_pytest/python_api.py | 2 +- src/_pytest/recwarn.py | 2 +- src/_pytest/skipping.py | 4 +-- src/_pytest/terminal.py | 2 +- src/_pytest/warnings.py | 2 +- testing/test_pluginmanager.py | 2 +- 33 files changed, 83 insertions(+), 83 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 2c7d253e73a..24bca723c8b 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -324,7 +324,7 @@ Here is a simple overview, with pytest-specific bits: Writing Tests ~~~~~~~~~~~~~ -Writing tests for plugins or for pytest itself is often done using the `pytester fixture `_, as a "black-box" test. +Writing tests for plugins or for pytest itself is often done using the `pytester fixture `_, as a "black-box" test. For example, to ensure a simple test passes you can write: diff --git a/README.rst b/README.rst index 806465219af..8b2011fab0b 100644 --- a/README.rst +++ b/README.rst @@ -79,17 +79,17 @@ Due to ``pytest``'s detailed assertion introspection, only plain ``assert`` stat Features -------- -- Detailed info on failing `assert statements `_ (no need to remember ``self.assert*`` names) +- Detailed info on failing `assert statements `_ (no need to remember ``self.assert*`` names) - `Auto-discovery - `_ + `_ of test modules and functions -- `Modular fixtures `_ for +- `Modular fixtures `_ for managing small or parametrized long-lived test resources -- Can run `unittest `_ (or trial), - `nose `_ test suites out of the box +- Can run `unittest `_ (or trial), + `nose `_ test suites out of the box - Python 3.6+ and PyPy3 diff --git a/doc/en/announce/release-2.0.0.rst b/doc/en/announce/release-2.0.0.rst index 1aaad740a4f..ecb1a1db988 100644 --- a/doc/en/announce/release-2.0.0.rst +++ b/doc/en/announce/release-2.0.0.rst @@ -36,12 +36,12 @@ New Features import pytest ; pytest.main(arglist, pluginlist) - see http://pytest.org/en/stable/usage.html for details. + see http://pytest.org/en/stable/how-to/usage.html for details. - new and better reporting information in assert expressions if comparing lists, sequences or strings. - see http://pytest.org/en/stable/assert.html#newreport + see http://pytest.org/en/stable/how-to/assert.html#newreport - new configuration through ini-files (setup.cfg or tox.ini recognized), for example:: @@ -50,7 +50,7 @@ New Features norecursedirs = .hg data* # don't ever recurse in such dirs addopts = -x --pyargs # add these command line options by default - see http://pytest.org/en/stable/customize.html + see http://pytest.org/en/stable/reference/customize.html - improved standard unittest support. In general py.test should now better be able to run custom unittest.TestCases like twisted trial diff --git a/doc/en/announce/release-2.0.1.rst b/doc/en/announce/release-2.0.1.rst index 72401d8098f..4ff3e9f550a 100644 --- a/doc/en/announce/release-2.0.1.rst +++ b/doc/en/announce/release-2.0.1.rst @@ -57,7 +57,7 @@ Changes between 2.0.0 and 2.0.1 - refinements to "collecting" output on non-ttys - refine internal plugin registration and --traceconfig output - introduce a mechanism to prevent/unregister plugins from the - command line, see http://pytest.org/en/stable/plugins.html#cmdunregister + command line, see http://pytest.org/en/stable/how-to/plugins.html#cmdunregister - activate resultlog plugin by default - fix regression wrt yielded tests which due to the collection-before-running semantics were not diff --git a/doc/en/announce/release-2.1.0.rst b/doc/en/announce/release-2.1.0.rst index f8bc88c163d..78247247e2f 100644 --- a/doc/en/announce/release-2.1.0.rst +++ b/doc/en/announce/release-2.1.0.rst @@ -12,7 +12,7 @@ courtesy of Benjamin Peterson. You can now safely use ``assert`` statements in test modules without having to worry about side effects or python optimization ("-OO") options. This is achieved by rewriting assert statements in test modules upon import, using a PEP302 hook. -See https://docs.pytest.org/en/stable/assert.html for +See https://docs.pytest.org/en/stable/how-to/assert.html for detailed information. The work has been partly sponsored by my company, merlinux GmbH. diff --git a/doc/en/announce/release-2.2.0.rst b/doc/en/announce/release-2.2.0.rst index 0193ffb3465..7a32dca173c 100644 --- a/doc/en/announce/release-2.2.0.rst +++ b/doc/en/announce/release-2.2.0.rst @@ -9,7 +9,7 @@ with these improvements: - new @pytest.mark.parametrize decorator to run tests with different arguments - new metafunc.parametrize() API for parametrizing arguments independently - - see examples at http://pytest.org/en/stable/example/parametrize.html + - see examples at http://pytest.org/en/stable/example/how-to/parametrize.html - NOTE that parametrize() related APIs are still a bit experimental and might change in future releases. @@ -78,7 +78,7 @@ Changes between 2.1.3 and 2.2.0 or through plugin hooks. Also introduce a "--strict" option which will treat unregistered markers as errors allowing to avoid typos and maintain a well described set of markers - for your test suite. See examples at http://pytest.org/en/stable/mark.html + for your test suite. See examples at http://pytest.org/en/stable/how-to/mark.html and its links. - issue50: introduce "-m marker" option to select tests based on markers (this is a stricter and more predictable version of "-k" in that "-m" diff --git a/doc/en/announce/release-2.3.0.rst b/doc/en/announce/release-2.3.0.rst index bdd92a98fde..6905b77b923 100644 --- a/doc/en/announce/release-2.3.0.rst +++ b/doc/en/announce/release-2.3.0.rst @@ -13,12 +13,12 @@ re-usable fixture design. For detailed info and tutorial-style examples, see: - http://pytest.org/en/stable/fixture.html + http://pytest.org/en/stable/explanation/fixtures.html Moreover, there is now support for using pytest fixtures/funcargs with unittest-style suites, see here for examples: - http://pytest.org/en/stable/unittest.html + http://pytest.org/en/stable/how-to/unittest.html Besides, more unittest-test suites are now expected to "simply work" with pytest. diff --git a/doc/en/announce/release-2.3.4.rst b/doc/en/announce/release-2.3.4.rst index 26f76630e84..43bf03b02be 100644 --- a/doc/en/announce/release-2.3.4.rst +++ b/doc/en/announce/release-2.3.4.rst @@ -16,7 +16,7 @@ comes with the following fixes and features: - yielded test functions will now have autouse-fixtures active but cannot accept fixtures as funcargs - it's anyway recommended to rather use the post-2.0 parametrize features instead of yield, see: - http://pytest.org/en/stable/example/parametrize.html + http://pytest.org/en/stable/example/how-to/parametrize.html - fix autouse-issue where autouse-fixtures would not be discovered if defined in an a/conftest.py file and tests in a/tests/test_some.py - fix issue226 - LIFO ordering for fixture teardowns diff --git a/doc/en/announce/release-2.9.0.rst b/doc/en/announce/release-2.9.0.rst index 8c2ee05f9bf..9448af58c3a 100644 --- a/doc/en/announce/release-2.9.0.rst +++ b/doc/en/announce/release-2.9.0.rst @@ -131,7 +131,7 @@ The py.test Development Team with same name. -.. _`traceback style docs`: https://pytest.org/en/stable/usage.html#modifying-python-traceback-printing +.. _`traceback style docs`: https://pytest.org/en/stable/how-to/output.html#modifying-python-traceback-printing .. _#1422: https://github.com/pytest-dev/pytest/issues/1422 .. _#1379: https://github.com/pytest-dev/pytest/issues/1379 diff --git a/doc/en/builtin.rst b/doc/en/builtin.rst index ea2f27a4c9f..5208fe65bda 100644 --- a/doc/en/builtin.rst +++ b/doc/en/builtin.rst @@ -144,7 +144,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a recwarn Return a :class:`WarningsRecorder` instance that records all warnings emitted by test functions. - See https://docs.python.org/library/warnings.html for information + See https://docs.python.org/library/how-to/capture-warnings.html for information on warning categories. tmpdir_factory [session scope] diff --git a/doc/en/changelog.rst b/doc/en/changelog.rst index e2b2635af9d..9de93e98a17 100644 --- a/doc/en/changelog.rst +++ b/doc/en/changelog.rst @@ -737,7 +737,7 @@ Features "integration", ] - More information can be found `in the docs `__. + More information can be found `in the docs `__. - `#3342 `_: pytest now includes inline type annotations and exposes them to user programs. @@ -785,7 +785,7 @@ Features We intend to make ``--import-mode=importlib`` the default in future versions, so users are encouraged to try the new mode and provide feedback (both positive or negative) in issue `#7245 `__. - You can read more about this option in `the documentation `__. + You can read more about this option in `the documentation `__. - `#7305 `_: New ``required_plugins`` configuration option allows the user to specify a list of plugins, including version information, that are required for pytest to run. An error is raised if any required plugins are not found when running pytest. @@ -1677,7 +1677,7 @@ Features - `#1682 `_: The ``scope`` parameter of ``@pytest.fixture`` can now be a callable that receives the fixture name and the ``config`` object as keyword-only parameters. - See `the docs `__ for more information. + See `the docs `__ for more information. - `#5764 `_: New behavior of the ``--pastebin`` option: failures to connect to the pastebin server are reported, without failing the pytest run @@ -1816,7 +1816,7 @@ Features - `#5564 `_: New ``Config.invocation_args`` attribute containing the unchanged arguments passed to ``pytest.main()``. -- `#5576 `_: New `NUMBER `__ +- `#5576 `_: New `NUMBER `__ option for doctests to ignore irrelevant differences in floating-point numbers. Inspired by Sébastien Boisgérault's `numtest `__ extension for doctest. @@ -2013,7 +2013,7 @@ Deprecations Features -------- -- `#3457 `_: New `pytest_assertion_pass `__ +- `#3457 `_: New `pytest_assertion_pass `__ hook, called with context information when an assertion *passes*. This hook is still **experimental** so use it with caution. @@ -2026,7 +2026,7 @@ Features `pytest-faulthandler `__ plugin into the core, so users should remove that plugin from their requirements if used. - For more information see the docs: https://docs.pytest.org/en/stable/usage.html#fault-handler + For more information see the docs: https://docs.pytest.org/en/stable/how-to/failures.html#fault-handler - `#5452 `_: When warnings are configured as errors, pytest warnings now appear as originating from ``pytest.`` instead of the internal ``_pytest.warning_types.`` module. @@ -2423,7 +2423,7 @@ Features The existing ``--strict`` option has the same behavior currently, but can be augmented in the future for additional checks. - .. _`markers option`: https://docs.pytest.org/en/stable/reference.html#confval-markers + .. _`markers option`: https://docs.pytest.org/en/stable/reference/reference.html#confval-markers - `#5026 `_: Assertion failure messages for sequences and dicts contain the number of different items now. @@ -2480,7 +2480,7 @@ Features CRITICAL root:test_log_cli_enabled_disabled.py:3 critical message logged by test - The formatting can be changed through the `log_format `__ configuration option. + The formatting can be changed through the `log_format `__ configuration option. - `#5220 `_: ``--fixtures`` now also shows fixture scope for scopes other than ``"function"``. @@ -2616,7 +2616,7 @@ Features .. _pdb++: https://pypi.org/project/pdbpp/ -- `#4875 `_: The `testpaths `__ configuration option is now displayed next +- `#4875 `_: The `testpaths `__ configuration option is now displayed next to the ``rootdir`` and ``inifile`` lines in the pytest header if the option is in effect, i.e., directories or file names were not explicitly passed in the command line. @@ -2871,7 +2871,7 @@ pytest 4.2.0 (2019-01-30) Features -------- -- `#3094 `_: `Classic xunit-style `__ functions and methods +- `#3094 `_: `Classic xunit-style `__ functions and methods now obey the scope of *autouse* fixtures. This fixes a number of surprising issues like ``setup_method`` being called before session-scoped @@ -3379,7 +3379,7 @@ Features existing ``pytest_enter_pdb`` hook. -- `#4147 `_: Add ``--sw``, ``--stepwise`` as an alternative to ``--lf -x`` for stopping at the first failure, but starting the next test invocation from that test. See `the documentation `__ for more info. +- `#4147 `_: Add ``--sw``, ``--stepwise`` as an alternative to ``--lf -x`` for stopping at the first failure, but starting the next test invocation from that test. See `the documentation `__ for more info. - `#4188 `_: Make ``--color`` emit colorful dots when not running in verbose mode. Earlier, it would only colorize the test-by-test output if ``--verbose`` was also passed. @@ -3784,13 +3784,13 @@ Features the standard warnings filters to manage those warnings. This introduces ``PytestWarning``, ``PytestDeprecationWarning`` and ``RemovedInPytest4Warning`` warning types as part of the public API. - Consult `the documentation `__ for more info. + Consult `the documentation `__ for more info. - `#2908 `_: ``DeprecationWarning`` and ``PendingDeprecationWarning`` are now shown by default if no other warning filter is configured. This makes pytest more compliant with `PEP-0506 `_. See - `the docs `_ for + `the docs `_ for more info. @@ -4223,7 +4223,7 @@ Features - Support for Python 3.7's builtin ``breakpoint()`` method, see `Using the builtin breakpoint function - `_ for + `_ for details. (`#3180 `_) - ``monkeypatch`` now supports a ``context()`` function which acts as a context @@ -4363,7 +4363,7 @@ Features - New ``--rootdir`` command-line option to override the rules for discovering the root directory. See `customize - `_ in the documentation for + `_ in the documentation for details. (`#1642 `_) - Fixtures are now instantiated based on their scopes, with higher-scoped @@ -4450,7 +4450,7 @@ Bug Fixes Improved Documentation ---------------------- -- Added a `reference `_ page +- Added a `reference `_ page to the docs. (`#1713 `_) @@ -4610,9 +4610,9 @@ Features `_) - **Incompatible change**: after community feedback the `logging - `_ functionality has + `_ functionality has undergone some changes. Please consult the `logging documentation - `_ + `_ for details. (`#3013 `_) - Console output falls back to "classic" mode when capturing is disabled (``-s``), @@ -4620,10 +4620,10 @@ Features `_) - New `pytest_runtest_logfinish - `_ + `_ hook which is called when a test item has finished executing, analogous to `pytest_runtest_logstart - `_. + `_. (`#3101 `_) - Improve performance when collecting tests using many fixtures. (`#3107 @@ -4865,7 +4865,7 @@ Features markers. Also, a ``caplog`` fixture is available that enables users to test the captured log during specific tests (similar to ``capsys`` for example). For more information, please see the `logging docs - `_. This feature was + `_. This feature was introduced by merging the popular `pytest-catchlog `_ plugin, thanks to `Thomas Hisch `_. Be advised that during the merging the @@ -5161,7 +5161,7 @@ Deprecations and Removals - ``pytest.approx`` no longer supports ``>``, ``>=``, ``<`` and ``<=`` operators to avoid surprising/inconsistent behavior. See `the approx docs - `_ for more + `_ for more information. (`#2003 `_) - All old-style specific behavior in current classes in the pytest's API is @@ -5213,7 +5213,7 @@ Features - Introduced ``@pytest.mark.filterwarnings`` mark which allows overwriting the warnings filter on a per test, class or module level. See the `docs - `_ for more information. (`#2598 `_) @@ -5443,7 +5443,7 @@ New Features [pytest] addopts = -p no:warnings - See the `warnings documentation page `_ for more + See the `warnings documentation page `_ for more information. Thanks `@nicoddemus`_ for the PR. @@ -6517,7 +6517,7 @@ time or change existing behaviors in order to make them less surprising/more use * Fix (`#1422`_): junit record_xml_property doesn't allow multiple records with same name. -.. _`traceback style docs`: https://pytest.org/en/stable/usage.html#modifying-python-traceback-printing +.. _`traceback style docs`: https://docs.pytest.org/en/stable/how-to/output.html#modifying-python-traceback-printing .. _#1609: https://github.com/pytest-dev/pytest/issues/1609 .. _#1422: https://github.com/pytest-dev/pytest/issues/1422 @@ -7776,7 +7776,7 @@ Bug fixes: - yielded test functions will now have autouse-fixtures active but cannot accept fixtures as funcargs - it's anyway recommended to rather use the post-2.0 parametrize features instead of yield, see: - http://pytest.org/en/stable/example/parametrize.html + http://pytest.org/en/stable/example/how-to/parametrize.html - fix autouse-issue where autouse-fixtures would not be discovered if defined in an a/conftest.py file and tests in a/tests/test_some.py - fix issue226 - LIFO ordering for fixture teardowns @@ -8022,7 +8022,7 @@ Bug fixes: or through plugin hooks. Also introduce a "--strict" option which will treat unregistered markers as errors allowing to avoid typos and maintain a well described set of markers - for your test suite. See exaples at http://pytest.org/en/stable/mark.html + for your test suite. See exaples at http://pytest.org/en/stable/how-to/mark.html and its links. - issue50: introduce "-m marker" option to select tests based on markers (this is a stricter and more predictable version of '-k' in that "-m" @@ -8205,7 +8205,7 @@ Bug fixes: - refinements to "collecting" output on non-ttys - refine internal plugin registration and --traceconfig output - introduce a mechanism to prevent/unregister plugins from the - command line, see http://pytest.org/en/stable/plugins.html#cmdunregister + command line, see http://pytest.org/en/stable/how-to/plugins.html#cmdunregister - activate resultlog plugin by default - fix regression wrt yielded tests which due to the collection-before-running semantics were not diff --git a/doc/en/example/markers.rst b/doc/en/example/markers.rst index f0effa02631..66ef7dda7ed 100644 --- a/doc/en/example/markers.rst +++ b/doc/en/example/markers.rst @@ -234,17 +234,17 @@ You can ask which markers exist for your test suite - the list includes our just @pytest.mark.slow: mark test as slow. - @pytest.mark.filterwarnings(warning): add a warning filter to the given test. see https://docs.pytest.org/en/stable/warnings.html#pytest-mark-filterwarnings + @pytest.mark.filterwarnings(warning): add a warning filter to the given test. see https://docs.pytest.org/en/stable/how-to/capture-warnings.html#pytest-mark-filterwarnings @pytest.mark.skip(reason=None): skip the given test function with an optional reason. Example: skip(reason="no way of currently testing this") skips the test. - @pytest.mark.skipif(condition, ..., *, reason=...): skip the given test function if any of the conditions evaluate to True. Example: skipif(sys.platform == 'win32') skips the test if we are on the win32 platform. See https://docs.pytest.org/en/stable/reference.html#pytest-mark-skipif + @pytest.mark.skipif(condition, ..., *, reason=...): skip the given test function if any of the conditions evaluate to True. Example: skipif(sys.platform == 'win32') skips the test if we are on the win32 platform. See https://docs.pytest.org/en/stable/reference/reference.html#pytest-mark-skipif - @pytest.mark.xfail(condition, ..., *, reason=..., run=True, raises=None, strict=xfail_strict): mark the test function as an expected failure if any of the conditions evaluate to True. Optionally specify a reason for better reporting and run=False if you don't even want to execute the test function. If only specific exception(s) are expected, you can list them in raises, and if the test fails in other ways, it will be reported as a true failure. See https://docs.pytest.org/en/stable/reference.html#pytest-mark-xfail + @pytest.mark.xfail(condition, ..., *, reason=..., run=True, raises=None, strict=xfail_strict): mark the test function as an expected failure if any of the conditions evaluate to True. Optionally specify a reason for better reporting and run=False if you don't even want to execute the test function. If only specific exception(s) are expected, you can list them in raises, and if the test fails in other ways, it will be reported as a true failure. See https://docs.pytest.org/en/stable/reference/reference.html#pytest-mark-xfail - @pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in different arguments in turn. argvalues generally needs to be a list of values if argnames specifies only one name or a list of tuples of values if argnames specifies multiple names. Example: @parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2.see https://docs.pytest.org/en/stable/parametrize.html for more info and examples. + @pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in different arguments in turn. argvalues generally needs to be a list of values if argnames specifies only one name or a list of tuples of values if argnames specifies multiple names. Example: @parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2.see https://docs.pytest.org/en/stable/how-to/parametrize.html for more info and examples. - @pytest.mark.usefixtures(fixturename1, fixturename2, ...): mark tests as needing all of the specified fixtures. see https://docs.pytest.org/en/stable/fixture.html#usefixtures + @pytest.mark.usefixtures(fixturename1, fixturename2, ...): mark tests as needing all of the specified fixtures. see https://docs.pytest.org/en/stable/how-to/fixtures.html#usefixtures @pytest.mark.tryfirst: mark a hook implementation function such that the plugin machinery will try to call it first/as early as possible. @@ -428,17 +428,17 @@ The ``--markers`` option always gives you a list of available markers: $ pytest --markers @pytest.mark.env(name): mark test to run only on named environment - @pytest.mark.filterwarnings(warning): add a warning filter to the given test. see https://docs.pytest.org/en/stable/warnings.html#pytest-mark-filterwarnings + @pytest.mark.filterwarnings(warning): add a warning filter to the given test. see https://docs.pytest.org/en/stable/how-to/capture-warnings.html#pytest-mark-filterwarnings @pytest.mark.skip(reason=None): skip the given test function with an optional reason. Example: skip(reason="no way of currently testing this") skips the test. - @pytest.mark.skipif(condition, ..., *, reason=...): skip the given test function if any of the conditions evaluate to True. Example: skipif(sys.platform == 'win32') skips the test if we are on the win32 platform. See https://docs.pytest.org/en/stable/reference.html#pytest-mark-skipif + @pytest.mark.skipif(condition, ..., *, reason=...): skip the given test function if any of the conditions evaluate to True. Example: skipif(sys.platform == 'win32') skips the test if we are on the win32 platform. See https://docs.pytest.org/en/stable/reference/reference.html#pytest-mark-skipif - @pytest.mark.xfail(condition, ..., *, reason=..., run=True, raises=None, strict=xfail_strict): mark the test function as an expected failure if any of the conditions evaluate to True. Optionally specify a reason for better reporting and run=False if you don't even want to execute the test function. If only specific exception(s) are expected, you can list them in raises, and if the test fails in other ways, it will be reported as a true failure. See https://docs.pytest.org/en/stable/reference.html#pytest-mark-xfail + @pytest.mark.xfail(condition, ..., *, reason=..., run=True, raises=None, strict=xfail_strict): mark the test function as an expected failure if any of the conditions evaluate to True. Optionally specify a reason for better reporting and run=False if you don't even want to execute the test function. If only specific exception(s) are expected, you can list them in raises, and if the test fails in other ways, it will be reported as a true failure. See https://docs.pytest.org/en/stable/reference/reference.html#pytest-mark-xfail - @pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in different arguments in turn. argvalues generally needs to be a list of values if argnames specifies only one name or a list of tuples of values if argnames specifies multiple names. Example: @parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2.see https://docs.pytest.org/en/stable/parametrize.html for more info and examples. + @pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in different arguments in turn. argvalues generally needs to be a list of values if argnames specifies only one name or a list of tuples of values if argnames specifies multiple names. Example: @parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2.see https://docs.pytest.org/en/stable/how-to/parametrize.html for more info and examples. - @pytest.mark.usefixtures(fixturename1, fixturename2, ...): mark tests as needing all of the specified fixtures. see https://docs.pytest.org/en/stable/fixture.html#usefixtures + @pytest.mark.usefixtures(fixturename1, fixturename2, ...): mark tests as needing all of the specified fixtures. see https://docs.pytest.org/en/stable/explanation/fixtures.html#usefixtures @pytest.mark.tryfirst: mark a hook implementation function such that the plugin machinery will try to call it first/as early as possible. diff --git a/doc/en/explanation/flaky.rst b/doc/en/explanation/flaky.rst index e49ebf21068..8788fd5bda9 100644 --- a/doc/en/explanation/flaky.rst +++ b/doc/en/explanation/flaky.rst @@ -28,7 +28,7 @@ Flaky tests sometimes appear when a test suite is run in parallel (such as use o Overly strict assertion ~~~~~~~~~~~~~~~~~~~~~~~ -Overly strict assertions can cause problems with floating point comparison as well as timing issues. `pytest.approx `_ is useful here. +Overly strict assertions can cause problems with floating point comparison as well as timing issues. `pytest.approx `_ is useful here. Pytest features diff --git a/doc/en/getting-started.rst b/doc/en/getting-started.rst index 707087a9af7..49c17734e78 100644 --- a/doc/en/getting-started.rst +++ b/doc/en/getting-started.rst @@ -71,7 +71,7 @@ The ``[100%]`` refers to the overall progress of running all test cases. After i .. note:: - You can use the ``assert`` statement to verify test expectations. pytest’s `Advanced assertion introspection `_ will intelligently report intermediate values of the assert expression so you can avoid the many names `of JUnit legacy methods `_. + You can use the ``assert`` statement to verify test expectations. pytest’s `Advanced assertion introspection `_ will intelligently report intermediate values of the assert expression so you can avoid the many names `of JUnit legacy methods `_. Run multiple tests ---------------------------------------------------------- diff --git a/doc/en/history.rst b/doc/en/history.rst index 1bc8942a9cf..98be1b9dbd6 100644 --- a/doc/en/history.rst +++ b/doc/en/history.rst @@ -121,7 +121,7 @@ project: - Various `default plugins `__, including - `monkeypatch `__ + `monkeypatch `__ - Even back there, the `FAQ `__ diff --git a/doc/en/how-to/assert.rst b/doc/en/how-to/assert.rst index 9269743dbda..9507a651e73 100644 --- a/doc/en/how-to/assert.rst +++ b/doc/en/how-to/assert.rst @@ -297,7 +297,7 @@ modules directly discovered by its test collection process, so **asserts in supporting modules which are not themselves test modules will not be rewritten**. You can manually enable assertion rewriting for an imported module by calling -`register_assert_rewrite `_ +`register_assert_rewrite `_ before you import it (a good place to do that is in your root ``conftest.py``). For further information, Benjamin Peterson wrote up `Behind the scenes of pytest's new assertion rewriting `_. diff --git a/doc/en/how-to/capture-warnings.rst b/doc/en/how-to/capture-warnings.rst index cfee585cbb1..188a7316df9 100644 --- a/doc/en/how-to/capture-warnings.rst +++ b/doc/en/how-to/capture-warnings.rst @@ -40,7 +40,7 @@ Running pytest now produces this output: $REGENDOC_TMPDIR/test_show_warnings.py:5: UserWarning: api v1, should use functions from v2 warnings.warn(UserWarning("api v1, should use functions from v2")) - -- Docs: https://docs.pytest.org/en/stable/warnings.html + -- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html ======================= 1 passed, 1 warning in 0.12s ======================= The ``-W`` flag can be passed to control which warnings will be displayed or even turn @@ -144,7 +144,7 @@ decorator or to all tests in a module by setting the :globalvar:`pytestmark` var *plugin.* .. _`-W option`: https://docs.python.org/3/using/cmdline.html#cmdoption-w -.. _warnings.simplefilter: https://docs.python.org/3/library/warnings.html#warnings.simplefilter +.. _warnings.simplefilter: https://docs.python.org/3/library/how-to/capture-warnings.html#warnings.simplefilter .. _`pytest-warnings`: https://github.com/fschulze/pytest-warnings Disabling warnings summary @@ -398,7 +398,7 @@ defines an ``__init__`` constructor, as this prevents the class from being insta $REGENDOC_TMPDIR/test_pytest_warnings.py:1: PytestCollectionWarning: cannot collect test class 'Test' because it has a __init__ constructor (from: test_pytest_warnings.py) class Test: - -- Docs: https://docs.pytest.org/en/stable/warnings.html + -- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html 1 warning in 0.12s These warnings might be filtered using the same builtin mechanisms used to filter other types of warnings. diff --git a/doc/en/how-to/doctest.rst b/doc/en/how-to/doctest.rst index 559d7c35ccf..c08ce509329 100644 --- a/doc/en/how-to/doctest.rst +++ b/doc/en/how-to/doctest.rst @@ -95,7 +95,7 @@ that will be used for those doctest files using the Using 'doctest' options ----------------------- -Python's standard ``doctest`` module provides some `options `__ +Python's standard ``doctest`` module provides some `options `__ to configure the strictness of doctest tests. In pytest, you can enable those flags using the configuration file. @@ -252,7 +252,7 @@ For the same reasons one might want to skip normal tests, it is also possible to tests inside doctests. To skip a single check inside a doctest you can use the standard -`doctest.SKIP `__ directive: +`doctest.SKIP `__ directive: .. code-block:: python diff --git a/doc/en/how-to/unittest.rst b/doc/en/how-to/unittest.rst index 2e5763f834c..6f0d334b01f 100644 --- a/doc/en/how-to/unittest.rst +++ b/doc/en/how-to/unittest.rst @@ -27,8 +27,8 @@ Almost all ``unittest`` features are supported: * ``setUpClass/tearDownClass``; * ``setUpModule/tearDownModule``; -.. _`load_tests protocol`: https://docs.python.org/3/library/unittest.html#load-tests-protocol -.. _`subtests`: https://docs.python.org/3/library/unittest.html#distinguishing-test-iterations-using-subtests +.. _`load_tests protocol`: https://docs.python.org/3/library/how-to/unittest.html#load-tests-protocol +.. _`subtests`: https://docs.python.org/3/library/how-to/unittest.html#distinguishing-test-iterations-using-subtests Up to this point pytest does not have support for the following features: diff --git a/doc/en/how-to/xunit_setup.rst b/doc/en/how-to/xunit_setup.rst index 7fbe870ed47..6d119299f14 100644 --- a/doc/en/how-to/xunit_setup.rst +++ b/doc/en/how-to/xunit_setup.rst @@ -116,4 +116,4 @@ Remarks: Now the xunit-style functions are integrated with the fixture mechanism and obey the proper scope rules of fixtures involved in the call. -.. _`unittest.py module`: https://docs.python.org/library/unittest.html +.. _`unittest.py module`: https://docs.python.org/library/how-to/unittest.html diff --git a/doc/en/projects.rst b/doc/en/projects.rst index 804a0b20a25..76ecad7469e 100644 --- a/doc/en/projects.rst +++ b/doc/en/projects.rst @@ -56,7 +56,7 @@ Here are some examples of projects using ``pytest`` (please send notes via :ref: * `bu `_ a microscopic build system * `katcp `_ Telescope communication protocol over Twisted * `kss plugin timer `_ -* `pyudev `_ a pure Python binding to the Linux library libudev +* `pyudev `_ a pure Python binding to the Linux library libudev * `pytest-localserver `_ a plugin for pytest that provides an httpserver and smtpserver * `pytest-monkeyplus `_ a plugin that extends monkeypatch diff --git a/doc/en/reference/reference.rst b/doc/en/reference/reference.rst index 1fe0563ee65..715e4350b72 100644 --- a/doc/en/reference/reference.rst +++ b/doc/en/reference/reference.rst @@ -118,7 +118,7 @@ Add warning filters to marked test items. :keyword str filter: A *warning specification string*, which is composed of contents of the tuple ``(action, message, category, module, lineno)`` - as specified in `The Warnings filter `_ section of + as specified in `The Warnings filter `_ section of the Python documentation, separated by ``":"``. Optional fields can be omitted. Module names passed for filtering are not regex-escaped. diff --git a/src/_pytest/cacheprovider.py b/src/_pytest/cacheprovider.py index f442ea89bcc..f75dfde45e1 100755 --- a/src/_pytest/cacheprovider.py +++ b/src/_pytest/cacheprovider.py @@ -43,7 +43,7 @@ **Do not** commit this to version control. -See [the docs](https://docs.pytest.org/en/stable/cache.html) for more information. +See [the docs](https://docs.pytest.org/en/stable/how-to/cache.html) for more information. """ CACHEDIR_TAG_CONTENT = b"""\ diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index ebc44b2b2b1..b419ad70ae7 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -1179,7 +1179,7 @@ def wrap_function_to_error_out_if_called_directly( message = ( 'Fixture "{name}" called directly. Fixtures are not meant to be called directly,\n' "but are created automatically when test functions request them as parameters.\n" - "See https://docs.pytest.org/en/stable/fixture.html for more information about fixtures, and\n" + "See https://docs.pytest.org/en/stable/explanation/fixtures.html for more information about fixtures, and\n" "https://docs.pytest.org/en/stable/deprecations.html#calling-fixtures-directly about how to update your code." ).format(name=fixture_marker.name or function.__name__) diff --git a/src/_pytest/mark/structures.py b/src/_pytest/mark/structures.py index 845a973392f..d7f0ffec5a4 100644 --- a/src/_pytest/mark/structures.py +++ b/src/_pytest/mark/structures.py @@ -530,7 +530,7 @@ def __getattr__(self, name: str) -> MarkDecorator: warnings.warn( "Unknown pytest.mark.%s - is this a typo? You can register " "custom marks to avoid this warning - for details, see " - "https://docs.pytest.org/en/stable/mark.html" % name, + "https://docs.pytest.org/en/stable/how-to/mark.html" % name, PytestUnknownMarkWarning, 2, ) diff --git a/src/_pytest/outcomes.py b/src/_pytest/outcomes.py index 756b4098b36..2addf557262 100644 --- a/src/_pytest/outcomes.py +++ b/src/_pytest/outcomes.py @@ -137,7 +137,7 @@ def skip(msg: str = "", *, allow_module_level: bool = False) -> "NoReturn": possible to declare a test to be skipped under certain conditions like mismatching platforms or dependencies. Similarly, use the ``# doctest: +SKIP`` directive (see `doctest.SKIP - `_) + `_) to skip a doctest statically. """ __tracebackhide__ = True diff --git a/src/_pytest/python.py b/src/_pytest/python.py index f7dfd696bde..92ec76d5754 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -150,14 +150,14 @@ def pytest_configure(config: Config) -> None: "or a list of tuples of values if argnames specifies multiple names. " "Example: @parametrize('arg1', [1,2]) would lead to two calls of the " "decorated test function, one with arg1=1 and another with arg1=2." - "see https://docs.pytest.org/en/stable/parametrize.html for more info " + "see https://docs.pytest.org/en/stable/how-to/parametrize.html for more info " "and examples.", ) config.addinivalue_line( "markers", "usefixtures(fixturename1, fixturename2, ...): mark tests as needing " "all of the specified fixtures. see " - "https://docs.pytest.org/en/stable/fixture.html#usefixtures ", + "https://docs.pytest.org/en/stable/explanation/fixtures.html#usefixtures ", ) diff --git a/src/_pytest/python_api.py b/src/_pytest/python_api.py index 0c7686d995e..847991a3708 100644 --- a/src/_pytest/python_api.py +++ b/src/_pytest/python_api.py @@ -644,7 +644,7 @@ def approx(expected, rel=None, abs=None, nan_ok: bool = False) -> ApproxBase: small numbers. Also, it's only available in subclasses of ``unittest.TestCase`` and it's ugly because it doesn't follow PEP8. `More information...`__ - __ https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertAlmostEqual + __ https://docs.python.org/3/library/how-to/unittest.html#unittest.TestCase.assertAlmostEqual - ``a == pytest.approx(b, rel=1e-6, abs=1e-12)``: True if the relative tolerance is met w.r.t. ``b`` or if the absolute tolerance is met. diff --git a/src/_pytest/recwarn.py b/src/_pytest/recwarn.py index 4b61db4968a..950d853f51d 100644 --- a/src/_pytest/recwarn.py +++ b/src/_pytest/recwarn.py @@ -29,7 +29,7 @@ def recwarn() -> Generator["WarningsRecorder", None, None]: """Return a :class:`WarningsRecorder` instance that records all warnings emitted by test functions. - See https://docs.python.org/library/warnings.html for information + See https://docs.python.org/library/how-to/capture-warnings.html for information on warning categories. """ wrec = WarningsRecorder(_ispytest=True) diff --git a/src/_pytest/skipping.py b/src/_pytest/skipping.py index 7fe9783a4fa..f7a026ae74e 100644 --- a/src/_pytest/skipping.py +++ b/src/_pytest/skipping.py @@ -68,7 +68,7 @@ def nop(*args, **kwargs): "skipif(condition, ..., *, reason=...): " "skip the given test function if any of the conditions evaluate to True. " "Example: skipif(sys.platform == 'win32') skips the test if we are on the win32 platform. " - "See https://docs.pytest.org/en/stable/reference.html#pytest-mark-skipif", + "See https://docs.pytest.org/en/stable/reference/reference.html#pytest-mark-skipif", ) config.addinivalue_line( "markers", @@ -78,7 +78,7 @@ def nop(*args, **kwargs): "and run=False if you don't even want to execute the test function. " "If only specific exception(s) are expected, you can list them in " "raises, and if the test fails in other ways, it will be reported as " - "a true failure. See https://docs.pytest.org/en/stable/reference.html#pytest-mark-xfail", + "a true failure. See https://docs.pytest.org/en/stable/reference/reference.html#pytest-mark-xfail", ) diff --git a/src/_pytest/terminal.py b/src/_pytest/terminal.py index a8dd0fc6a75..cdc3cf93b45 100644 --- a/src/_pytest/terminal.py +++ b/src/_pytest/terminal.py @@ -958,7 +958,7 @@ def collapsed_location_report(reports: List[WarningReport]) -> str: message = message.rstrip() self._tw.line(message) self._tw.line() - self._tw.line("-- Docs: https://docs.pytest.org/en/stable/warnings.html") + self._tw.line("-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html") def summary_passes(self) -> None: if self.config.option.tbstyle != "no": diff --git a/src/_pytest/warnings.py b/src/_pytest/warnings.py index 35eed96df58..4f831548d89 100644 --- a/src/_pytest/warnings.py +++ b/src/_pytest/warnings.py @@ -21,7 +21,7 @@ def pytest_configure(config: Config) -> None: config.addinivalue_line( "markers", "filterwarnings(warning): add a warning filter to the given test. " - "see https://docs.pytest.org/en/stable/warnings.html#pytest-mark-filterwarnings ", + "see https://docs.pytest.org/en/stable/how-to/capture-warnings.html#pytest-mark-filterwarnings ", ) diff --git a/testing/test_pluginmanager.py b/testing/test_pluginmanager.py index 252591dd39a..9fe23d17792 100644 --- a/testing/test_pluginmanager.py +++ b/testing/test_pluginmanager.py @@ -405,7 +405,7 @@ def test_plugin_prevent_register_stepwise_on_cacheprovider_unregister( self, pytestpm: PytestPluginManager ) -> None: """From PR #4304: The only way to unregister a module is documented at - the end of https://docs.pytest.org/en/stable/plugins.html. + the end of https://docs.pytest.org/en/stable/how-to/plugins.html. When unregister cacheprovider, then unregister stepwise too. """ From 51f645e56745bc7d8d6a53f9c0b5d10609ef4e77 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 6 Jul 2021 07:18:56 +0000 Subject: [PATCH 379/630] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/_pytest/terminal.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/_pytest/terminal.py b/src/_pytest/terminal.py index cdc3cf93b45..0375c4d4e8b 100644 --- a/src/_pytest/terminal.py +++ b/src/_pytest/terminal.py @@ -958,7 +958,9 @@ def collapsed_location_report(reports: List[WarningReport]) -> str: message = message.rstrip() self._tw.line(message) self._tw.line() - self._tw.line("-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html") + self._tw.line( + "-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html" + ) def summary_passes(self) -> None: if self.config.option.tbstyle != "no": From e1a4b4edd2b3931240c9df81c7bd5b45e30e4782 Mon Sep 17 00:00:00 2001 From: Taneli Hukkinen <3275109+hukkin@users.noreply.github.com> Date: Tue, 6 Jul 2021 13:10:44 +0300 Subject: [PATCH 380/630] Convert `TOMLDecodeError` to `UsageError` --- src/_pytest/config/findpaths.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/_pytest/config/findpaths.py b/src/_pytest/config/findpaths.py index 62214f8d973..89ade5f23b9 100644 --- a/src/_pytest/config/findpaths.py +++ b/src/_pytest/config/findpaths.py @@ -66,7 +66,11 @@ def load_config_dict_from_file( elif filepath.suffix == ".toml": import tomli - config = tomli.loads(filepath.read_text(encoding="utf-8")) + toml_text = filepath.read_text(encoding="utf-8") + try: + config = tomli.loads(toml_text) + except tomli.TOMLDecodeError as exc: + raise UsageError(str(exc)) from exc result = config.get("tool", {}).get("pytest", {}).get("ini_options", None) if result is not None: From 40797dee870c3dfcfb964450804d18a32f36396f Mon Sep 17 00:00:00 2001 From: Taneli Hukkinen <3275109+hukkin@users.noreply.github.com> Date: Tue, 6 Jul 2021 13:14:45 +0300 Subject: [PATCH 381/630] Make news indicate that a dependency changed --- changelog/8789.feature.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog/8789.feature.rst b/changelog/8789.feature.rst index 198d9ade3c2..23215c97ef2 100644 --- a/changelog/8789.feature.rst +++ b/changelog/8789.feature.rst @@ -1 +1 @@ -Support TOML v1.0.0 syntax in ``pyproject.toml``. +Switch TOML parser from ``toml`` to ``tomli`` for TOML v1.0.0 support in ``pyproject.toml``. From 5987251407d210bd30484081447d6a20f4ac7e0b Mon Sep 17 00:00:00 2001 From: Taneli Hukkinen <3275109+hukkin@users.noreply.github.com> Date: Tue, 6 Jul 2021 16:53:32 +0300 Subject: [PATCH 382/630] Add a test for invalid TOML file --- testing/test_findpaths.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/testing/test_findpaths.py b/testing/test_findpaths.py index 3c876d96b59..3a2917261a2 100644 --- a/testing/test_findpaths.py +++ b/testing/test_findpaths.py @@ -2,6 +2,7 @@ from textwrap import dedent import pytest +from _pytest.config import UsageError from _pytest.config.findpaths import get_common_ancestor from _pytest.config.findpaths import get_dirs_from_args from _pytest.config.findpaths import load_config_dict_from_file @@ -52,6 +53,13 @@ def test_unsupported_pytest_section_in_cfg_file(self, tmp_path: Path) -> None: load_config_dict_from_file(fn) def test_invalid_toml_file(self, tmp_path: Path) -> None: + """Invalid .toml files should raise `UsageError`.""" + fn = tmp_path / "myconfig.toml" + fn.write_text("]invalid toml[", encoding="utf-8") + with pytest.raises(UsageError): + load_config_dict_from_file(fn) + + def test_custom_toml_file(self, tmp_path: Path) -> None: """.toml files without [tool.pytest.ini_options] are not considered for configuration.""" fn = tmp_path / "myconfig.toml" fn.write_text( From 6740fb9da6dd21b3f1d2f6c3980605f4f7c9e81d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren=20Wegener?= Date: Tue, 6 Jul 2021 16:31:21 +0200 Subject: [PATCH 383/630] doc: Typofix in fixtures how-to (#8860) --- doc/en/how-to/fixtures.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/how-to/fixtures.rst b/doc/en/how-to/fixtures.rst index 1bd4173d9b5..17efb0356d8 100644 --- a/doc/en/how-to/fixtures.rst +++ b/doc/en/how-to/fixtures.rst @@ -153,7 +153,7 @@ Fixtures are reusable ^^^^^^^^^^^^^^^^^^^^^ One of the things that makes pytest's fixture system so powerful, is that it -gives us the ability to define a generic setup step that can reused over and +gives us the ability to define a generic setup step that can be reused over and over, just like a normal function would be used. Two different tests can request the same fixture and have pytest give each test their own result from that fixture. From 3989a9bb054d3b94f438c5d8d77e657ab5960ba0 Mon Sep 17 00:00:00 2001 From: "Adam J. Stewart" Date: Thu, 8 Jul 2021 12:21:30 -0500 Subject: [PATCH 384/630] Docs: monkeypatch.setenv does not accept bool --- src/_pytest/monkeypatch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_pytest/monkeypatch.py b/src/_pytest/monkeypatch.py index 708b25aa073..75fcdaf9194 100644 --- a/src/_pytest/monkeypatch.py +++ b/src/_pytest/monkeypatch.py @@ -36,7 +36,7 @@ def monkeypatch() -> Generator["MonkeyPatch", None, None]: monkeypatch.delattr(obj, name, raising=True) monkeypatch.setitem(mapping, name, value) monkeypatch.delitem(obj, name, raising=True) - monkeypatch.setenv(name, value, prepend=False) + monkeypatch.setenv(name, value, prepend=os.pathsep) monkeypatch.delenv(name, raising=True) monkeypatch.syspath_prepend(path) monkeypatch.chdir(path) From 17f1a0af8e500151e7e830b8bac3ed5241f0aab1 Mon Sep 17 00:00:00 2001 From: "Adam J. Stewart" Date: Thu, 8 Jul 2021 12:43:10 -0500 Subject: [PATCH 385/630] Fix in more places, use default --- doc/en/builtin.rst | 2 +- doc/en/how-to/monkeypatch.rst | 2 +- src/_pytest/monkeypatch.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/en/builtin.rst b/doc/en/builtin.rst index ea2f27a4c9f..99bc951b570 100644 --- a/doc/en/builtin.rst +++ b/doc/en/builtin.rst @@ -132,7 +132,7 @@ For information about fixtures, see :ref:`fixtures`. To see a complete list of a monkeypatch.delattr(obj, name, raising=True) monkeypatch.setitem(mapping, name, value) monkeypatch.delitem(obj, name, raising=True) - monkeypatch.setenv(name, value, prepend=False) + monkeypatch.setenv(name, value, prepend=None) monkeypatch.delenv(name, raising=True) monkeypatch.syspath_prepend(path) monkeypatch.chdir(path) diff --git a/doc/en/how-to/monkeypatch.rst b/doc/en/how-to/monkeypatch.rst index 4f9b5aa99e2..8c5f676b986 100644 --- a/doc/en/how-to/monkeypatch.rst +++ b/doc/en/how-to/monkeypatch.rst @@ -21,7 +21,7 @@ functionality in tests: monkeypatch.delattr(obj, name, raising=True) monkeypatch.setitem(mapping, name, value) monkeypatch.delitem(obj, name, raising=True) - monkeypatch.setenv(name, value, prepend=False) + monkeypatch.setenv(name, value, prepend=None) monkeypatch.delenv(name, raising=True) monkeypatch.syspath_prepend(path) monkeypatch.chdir(path) diff --git a/src/_pytest/monkeypatch.py b/src/_pytest/monkeypatch.py index 75fcdaf9194..e39712a908a 100644 --- a/src/_pytest/monkeypatch.py +++ b/src/_pytest/monkeypatch.py @@ -36,7 +36,7 @@ def monkeypatch() -> Generator["MonkeyPatch", None, None]: monkeypatch.delattr(obj, name, raising=True) monkeypatch.setitem(mapping, name, value) monkeypatch.delitem(obj, name, raising=True) - monkeypatch.setenv(name, value, prepend=os.pathsep) + monkeypatch.setenv(name, value, prepend=None) monkeypatch.delenv(name, raising=True) monkeypatch.syspath_prepend(path) monkeypatch.chdir(path) From fa42be77a67ce50009817f137ad5a7311b0b3afb Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Fri, 9 Jul 2021 10:09:05 -0400 Subject: [PATCH 386/630] Remove upper pinning on tomli the best practice is to only `<` when you know something is broken, and even then prefer `!=` assuming it will be fixed upstream for maximum compatibility --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 094dfed87fa..ca1d2c2bdd1 100644 --- a/setup.cfg +++ b/setup.cfg @@ -47,7 +47,7 @@ install_requires = packaging pluggy>=0.12,<1.0.0a1 py>=1.8.2 - tomli>=1.0.0,<2.0.0 + tomli>=1.0.0 atomicwrites>=1.0;sys_platform=="win32" colorama;sys_platform=="win32" importlib-metadata>=0.12;python_version<"3.8" From b6648219ec64ac52a21d36db50ed0494d062baa2 Mon Sep 17 00:00:00 2001 From: pytest bot Date: Sun, 11 Jul 2021 00:08:26 +0000 Subject: [PATCH 387/630] [automated] Update plugin list --- doc/en/reference/plugin_list.rst | 60 ++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/doc/en/reference/plugin_list.rst b/doc/en/reference/plugin_list.rst index 4fb1a604121..a254c0d4026 100644 --- a/doc/en/reference/plugin_list.rst +++ b/doc/en/reference/plugin_list.rst @@ -6,7 +6,7 @@ Plugin List PyPI projects that match "pytest-\*" are considered plugins and are listed automatically. Packages classified as inactive are excluded. -This list contains 887 plugins. +This list contains 893 plugins. ============================================================================================================== ======================================================================================================================================================================== ============== ===================== ================================================ name summary last release status requires @@ -35,6 +35,7 @@ name `pytest-ansible-playbook `_ Pytest fixture which runs given ansible playbook file. Mar 08, 2019 4 - Beta N/A `pytest-ansible-playbook-runner `_ Pytest fixture which runs given ansible playbook file. Dec 02, 2020 4 - Beta pytest (>=3.1.0) `pytest-antilru `_ Bust functools.lru_cache when running pytest to avoid test pollution Apr 11, 2019 5 - Production/Stable pytest +`pytest-anyio `_ The pytest anyio plugin is built into anyio. You don't need this package. Jun 29, 2021 N/A pytest `pytest-anything `_ Pytest fixtures to assert anything and something Feb 18, 2021 N/A N/A `pytest-aoc `_ Downloads puzzle inputs for Advent of Code and synthesizes PyTest fixtures Dec 01, 2020 N/A pytest ; extra == 'dev' `pytest-api `_ PyTest-API Python Web Framework built for testing purposes. May 04, 2021 N/A N/A @@ -71,7 +72,7 @@ name `pytest-azurepipelines `_ Formatting PyTest output for Azure Pipelines UI Jul 23, 2020 4 - Beta pytest (>=3.5.0) `pytest-bandit `_ A bandit plugin for pytest Feb 23, 2021 4 - Beta pytest (>=3.5.0) `pytest-base-url `_ pytest plugin for URL based testing Jun 19, 2020 5 - Production/Stable pytest (>=2.7.3) -`pytest-bdd `_ BDD for pytest Dec 07, 2020 6 - Mature pytest (>=4.3) +`pytest-bdd `_ BDD for pytest Jul 03, 2021 6 - Mature pytest (>=4.3) `pytest-bdd-splinter `_ Common steps for pytest bdd and splinter integration Aug 12, 2019 5 - Production/Stable pytest (>=4.0.0) `pytest-bdd-web `_ A simple plugin to use with pytest Jan 02, 2020 4 - Beta pytest (>=3.5.0) `pytest-bdd-wrappers `_ Feb 11, 2020 2 - Pre-Alpha N/A @@ -108,7 +109,7 @@ name `pytest-canonical-data `_ A plugin which allows to compare results with canonical results, based on previous runs May 08, 2020 2 - Pre-Alpha pytest (>=3.5.0) `pytest-caprng `_ A plugin that replays pRNG state on failure. May 02, 2018 4 - Beta N/A `pytest-capture-deprecatedwarnings `_ pytest plugin to capture all deprecatedwarnings and put them in one file Apr 30, 2019 N/A N/A -`pytest-cases `_ Separate test code from test cases in pytest. Jun 03, 2021 5 - Production/Stable N/A +`pytest-cases `_ Separate test code from test cases in pytest. Jul 08, 2021 5 - Production/Stable N/A `pytest-cassandra `_ Cassandra CCM Test Fixtures for pytest Nov 04, 2017 1 - Planning N/A `pytest-catchlog `_ py.test plugin to catch log messages. This is a fork of pytest-capturelog. Jan 24, 2016 4 - Beta pytest (>=2.6) `pytest-catch-server `_ Pytest plugin with server for catching HTTP requests. Dec 12, 2019 5 - Production/Stable N/A @@ -240,7 +241,7 @@ name `pytest-doctest-custom `_ A py.test plugin for customizing string representations of doctest results. Jul 25, 2016 4 - Beta N/A `pytest-doctest-ellipsis-markers `_ Setup additional values for ELLIPSIS_MARKER for doctests Jan 12, 2018 4 - Beta N/A `pytest-doctest-import `_ A simple pytest plugin to import names and add them to the doctest namespace. Nov 13, 2018 4 - Beta pytest (>=3.3.0) -`pytest-doctestplus `_ Pytest plugin with advanced doctest features. Jan 15, 2021 3 - Alpha pytest (>=4.6) +`pytest-doctestplus `_ Pytest plugin with advanced doctest features. Jul 02, 2021 3 - Alpha pytest (>=4.6) `pytest-doctest-ufunc `_ A plugin to run doctests in docstrings of Numpy ufuncs Aug 02, 2020 4 - Beta pytest (>=3.5.0) `pytest-dolphin `_ Some extra stuff that we use ininternally Nov 30, 2016 4 - Beta pytest (==3.0.4) `pytest-doorstop `_ A pytest plugin for adding test results into doorstop items. Jun 09, 2020 4 - Beta pytest (>=3.5.0) @@ -263,11 +264,13 @@ name `pytest-elements `_ Tool to help automate user interfaces Jan 13, 2021 N/A pytest (>=5.4,<6.0) `pytest-elk-reporter `_ A simple plugin to use with pytest Jan 24, 2021 4 - Beta pytest (>=3.5.0) `pytest-email `_ Send execution result email Jul 08, 2020 N/A pytest -`pytest-embedded `_ pytest embedded plugin Jun 16, 2021 N/A pytest (>=6.2.0) -`pytest-embedded-idf `_ pytest embedded plugin for esp-idf project Jun 16, 2021 N/A N/A -`pytest-embedded-qemu-idf `_ pytest embedded plugin for esp-idf project by qemu, not target chip Jun 16, 2021 N/A N/A -`pytest-embedded-serial `_ pytest embedded plugin for testing serial ports Jun 16, 2021 N/A N/A -`pytest-embedded-serial-esp `_ pytest embedded plugin for testing espressif boards via serial ports Jun 16, 2021 N/A N/A +`pytest-embedded `_ pytest embedded plugin Jul 09, 2021 N/A pytest (>=6.2.0) +`pytest-embedded-idf `_ pytest embedded plugin for esp-idf project Jul 09, 2021 N/A N/A +`pytest-embedded-jtag `_ pytest embedded plugin for testing with jtag Jul 09, 2021 N/A N/A +`pytest-embedded-qemu `_ pytest embedded plugin for qemu, not target chip Jul 09, 2021 N/A N/A +`pytest-embedded-qemu-idf `_ pytest embedded plugin for esp-idf project by qemu, not target chip Jun 29, 2021 N/A N/A +`pytest-embedded-serial `_ pytest embedded plugin for testing serial ports Jul 09, 2021 N/A N/A +`pytest-embedded-serial-esp `_ pytest embedded plugin for testing espressif boards via serial ports Jul 09, 2021 N/A N/A `pytest-emoji `_ A pytest plugin that adds emojis to your test result report Feb 19, 2019 4 - Beta pytest (>=4.2.1) `pytest-emoji-output `_ Pytest plugin to represent test output with emoji support Jun 06, 2021 4 - Beta pytest (==6.0.1) `pytest-enabler `_ Enable installed pytest plugins Jan 19, 2021 5 - Production/Stable pytest (!=3.7.3,>=3.5) ; extra == 'testing' @@ -311,6 +314,7 @@ name `pytest-faulthandler `_ py.test plugin that activates the fault handler module for tests (dummy package) Jul 04, 2019 6 - Mature pytest (>=5.0) `pytest-fauxfactory `_ Integration of fauxfactory into pytest. Dec 06, 2017 5 - Production/Stable pytest (>=3.2) `pytest-figleaf `_ py.test figleaf coverage plugin Jan 18, 2010 5 - Production/Stable N/A +`pytest-filecov `_ A pytest plugin to detect unused files Jun 27, 2021 4 - Beta pytest `pytest-filedata `_ easily load data from files Jan 17, 2019 4 - Beta N/A `pytest-filemarker `_ A pytest plugin that runs marked tests when files change. Dec 01, 2020 N/A pytest `pytest-filter-case `_ run test cases filter by mark Nov 05, 2020 N/A N/A @@ -371,7 +375,7 @@ name `pytest-historic `_ Custom report to display pytest historical execution records Apr 08, 2020 N/A pytest `pytest-historic-hook `_ Custom listener to store execution results into MYSQL DB, which is used for pytest-historic report Apr 08, 2020 N/A pytest `pytest-homeassistant `_ A pytest plugin for use with homeassistant custom components. Aug 12, 2020 4 - Beta N/A -`pytest-homeassistant-custom-component `_ Experimental package to automatically extract test plugins for Home Assistant custom components Jun 04, 2021 3 - Alpha pytest (==6.2.4) +`pytest-homeassistant-custom-component `_ Experimental package to automatically extract test plugins for Home Assistant custom components Jun 30, 2021 3 - Alpha pytest (==6.2.4) `pytest-honors `_ Report on tests that honor constraints, and guard against regressions Mar 06, 2020 4 - Beta N/A `pytest-hoverfly `_ Simplify working with Hoverfly from pytest Jun 11, 2021 N/A pytest (>=5.0) `pytest-hoverfly-wrapper `_ Integrates the Hoverfly HTTP proxy into Pytest Jan 31, 2021 4 - Beta N/A @@ -400,7 +404,7 @@ name `pytest-info-collector `_ pytest plugin to collect information from tests May 26, 2019 3 - Alpha N/A `pytest-informative-node `_ display more node ininformation. Apr 25, 2019 4 - Beta N/A `pytest-infrastructure `_ pytest stack validation prior to testing executing Apr 12, 2020 4 - Beta N/A -`pytest-inmanta `_ A py.test plugin providing fixtures to simplify inmanta modules testing. Jun 18, 2021 5 - Production/Stable N/A +`pytest-inmanta `_ A py.test plugin providing fixtures to simplify inmanta modules testing. Jun 29, 2021 5 - Production/Stable N/A `pytest-inmanta-extensions `_ Inmanta tests package May 27, 2021 5 - Production/Stable N/A `pytest-Inomaly `_ A simple image diff plugin for pytest Feb 13, 2018 4 - Beta N/A `pytest-insta `_ A practical snapshot testing plugin for pytest Apr 07, 2021 N/A pytest (>=6.0.2,<7.0.0) @@ -427,7 +431,7 @@ name `pytest-json-report `_ A pytest plugin to report test results as JSON files Jun 18, 2021 4 - Beta pytest (>=3.8.0) `pytest-kafka `_ Zookeeper, Kafka server, and Kafka consumer fixtures for Pytest Nov 01, 2019 N/A pytest `pytest-kind `_ Kubernetes test support with KIND for pytest Jan 24, 2021 5 - Production/Stable N/A -`pytest-kivy `_ Kivy GUI tests fixtures using pytest Mar 20, 2021 4 - Beta pytest (>=3.6) +`pytest-kivy `_ Kivy GUI tests fixtures using pytest Jul 06, 2021 4 - Beta pytest (>=3.6) `pytest-knows `_ A pytest plugin that can automaticly skip test case based on dependence info calculated by trace Aug 22, 2014 N/A N/A `pytest-konira `_ Run Konira DSL tests with py.test Oct 09, 2011 N/A N/A `pytest-krtech-common `_ pytest krtech common library Nov 28, 2016 4 - Beta N/A @@ -490,12 +494,12 @@ name `pytest-mock-helper `_ Help you mock HTTP call and generate mock code Jan 24, 2018 N/A pytest `pytest-mockito `_ Base fixtures for mockito Jul 11, 2018 4 - Beta N/A `pytest-mockredis `_ An in-memory mock of a Redis server that runs in a separate thread. This is to be used for unit-tests that require a Redis database. Jan 02, 2018 2 - Pre-Alpha N/A -`pytest-mock-resources `_ A pytest plugin for easily instantiating reproducible mock resources. Jun 02, 2021 N/A pytest (>=1.0) +`pytest-mock-resources `_ A pytest plugin for easily instantiating reproducible mock resources. Jun 29, 2021 N/A pytest (>=1.0) `pytest-mock-server `_ Mock server plugin for pytest Apr 06, 2020 4 - Beta N/A `pytest-mockservers `_ A set of fixtures to test your requests to HTTP/UDP servers Mar 31, 2020 N/A pytest (>=4.3.0) `pytest-modifyjunit `_ Utility for adding additional properties to junit xml for IDM QE Jan 10, 2019 N/A N/A `pytest-modifyscope `_ pytest plugin to modify fixture scope Apr 12, 2020 N/A pytest -`pytest-molecule `_ PyTest Molecule Plugin :: discover and run molecule tests Jan 25, 2021 5 - Production/Stable N/A +`pytest-molecule `_ PyTest Molecule Plugin :: discover and run molecule tests Jul 06, 2021 5 - Production/Stable N/A `pytest-mongo `_ MongoDB process and client fixtures plugin for Pytest. Jun 07, 2021 5 - Production/Stable pytest `pytest-mongodb `_ pytest plugin for MongoDB fixtures Dec 07, 2019 5 - Production/Stable pytest (>=2.5.2) `pytest-monitor `_ Pytest plugin for analyzing resource usage. Apr 21, 2021 5 - Production/Stable pytest @@ -504,7 +508,7 @@ name `pytest-moto `_ Fixtures for integration tests of AWS services,uses moto mocking library. Aug 28, 2015 1 - Planning N/A `pytest-mp `_ A test batcher for multiprocessed Pytest runs May 23, 2018 4 - Beta pytest `pytest-mpi `_ pytest plugin to collect information from tests Mar 14, 2021 3 - Alpha pytest -`pytest-mpl `_ pytest plugin to help with testing figures output from Matplotlib Nov 05, 2020 4 - Beta pytest +`pytest-mpl `_ pytest plugin to help with testing figures output from Matplotlib Jul 02, 2021 4 - Beta pytest `pytest-mproc `_ low-startup-overhead, scalable, distributed-testing pytest plugin Mar 07, 2021 4 - Beta pytest `pytest-multi-check `_ Pytest-плагин, реализует возможность мульти проверок и мягких проверок Jun 03, 2021 N/A pytest `pytest-multihost `_ Utility for writing multi-host tests for pytest Apr 07, 2020 4 - Beta N/A @@ -526,7 +530,8 @@ name `pytest-ngsfixtures `_ pytest ngs fixtures Sep 06, 2019 2 - Pre-Alpha pytest (>=5.0.0) `pytest-nice `_ A pytest plugin that alerts user of failed test cases with screen notifications May 04, 2019 4 - Beta pytest `pytest-nice-parametrize `_ A small snippet for nicer PyTest's Parametrize Apr 17, 2021 5 - Production/Stable N/A -`pytest-nocustom `_ Run all tests without custom markers Apr 17, 2021 5 - Production/Stable N/A +`pytest-nlcov `_ Pytest plugin to get the coverage of the new lines (based on git diff) only Jul 07, 2021 N/A N/A +`pytest-nocustom `_ Run all tests without custom markers Jul 07, 2021 5 - Production/Stable N/A `pytest-nodev `_ Test-driven source code search for Python. Jul 21, 2016 4 - Beta pytest (>=2.8.1) `pytest-notebook `_ A pytest plugin for testing Jupyter Notebooks Sep 16, 2020 4 - Beta pytest (>=3.5.0) `pytest-notice `_ Send pytest execution result email Nov 05, 2020 N/A N/A @@ -544,7 +549,7 @@ name `pytest-oot `_ Run object-oriented tests in a simple format Sep 18, 2016 4 - Beta N/A `pytest-openfiles `_ Pytest plugin for detecting inadvertent open file handles Apr 16, 2020 3 - Alpha pytest (>=4.6) `pytest-opentmi `_ pytest plugin for publish results to opentmi Feb 26, 2021 5 - Production/Stable pytest (>=5.0) -`pytest-operator `_ Fixtures for Operators Jun 03, 2021 N/A N/A +`pytest-operator `_ Fixtures for Operators Jun 28, 2021 N/A N/A `pytest-optional `_ include/exclude values of fixtures in pytest Oct 07, 2015 N/A N/A `pytest-optional-tests `_ Easy declaration of optional tests (i.e., that are not run by default) Jul 09, 2019 4 - Beta pytest (>=4.5.0) `pytest-orchestration `_ A pytest plugin for orchestrating tests Jul 18, 2019 N/A N/A @@ -567,7 +572,7 @@ name `pytest-pep257 `_ py.test plugin for pep257 Jul 09, 2016 N/A N/A `pytest-pep8 `_ pytest plugin to check PEP8 requirements Apr 27, 2014 N/A N/A `pytest-percent `_ Change the exit code of pytest test sessions when a required percent of tests pass. May 21, 2020 N/A pytest (>=5.2.0) -`pytest-perf `_ pytest-perf Jun 26, 2021 5 - Production/Stable pytest (>=4.6) ; extra == 'testing' +`pytest-perf `_ pytest-perf Jun 27, 2021 5 - Production/Stable pytest (>=4.6) ; extra == 'testing' `pytest-performance `_ A simple plugin to ensure the execution of critical sections of code has not been impacted Sep 11, 2020 5 - Production/Stable pytest (>=3.7.0) `pytest-persistence `_ Pytest tool for persistent objects Mar 28, 2021 N/A N/A `pytest-pgsql `_ Pytest plugins and helpers for tests using a Postgres database. May 13, 2020 5 - Production/Stable pytest (>=3.0.0) @@ -641,7 +646,7 @@ name `pytest-random-order `_ Randomise the order in which pytest tests are run with some control over the randomness Nov 30, 2018 5 - Production/Stable pytest (>=3.0.0) `pytest-readme `_ Test your README.md file Dec 28, 2014 5 - Production/Stable N/A `pytest-reana `_ Pytest fixtures for REANA. Jun 07, 2021 3 - Alpha N/A -`pytest-recording `_ A pytest plugin that allows you recording of network interactions via VCR.py Nov 25, 2020 4 - Beta pytest (>=3.5.0) +`pytest-recording `_ A pytest plugin that allows you recording of network interactions via VCR.py Jul 08, 2021 4 - Beta pytest (>=3.5.0) `pytest-recordings `_ Provides pytest plugins for reporting request/response traffic, screenshots, and more to ReportPortal Aug 13, 2020 N/A N/A `pytest-redis `_ Redis fixtures and fixture factories for Pytest. May 25, 2021 5 - Production/Stable pytest `pytest-redmine `_ Pytest plugin for redmine Mar 19, 2018 1 - Planning N/A @@ -653,7 +658,7 @@ name `pytest-relaxed `_ Relaxed test discovery/organization for pytest Jun 14, 2019 5 - Production/Stable pytest (<5,>=3) `pytest-remfiles `_ Pytest plugin to create a temporary directory with remote files Jul 01, 2019 5 - Production/Stable N/A `pytest-remotedata `_ Pytest plugin for controlling remote data access. Jul 20, 2019 3 - Alpha pytest (>=3.1) -`pytest-remote-response `_ Pytest plugin for capturing and mocking connection requests. Jun 23, 2021 3 - Alpha pytest (>=4.6) +`pytest-remote-response `_ Pytest plugin for capturing and mocking connection requests. Jun 30, 2021 4 - Beta pytest (>=4.6) `pytest-remove-stale-bytecode `_ py.test plugin to remove stale byte code files. Mar 04, 2020 4 - Beta pytest `pytest-reorder `_ Reorder tests depending on their paths and names. May 31, 2018 4 - Beta pytest `pytest-repeat `_ pytest plugin for repeating tests Oct 31, 2020 5 - Production/Stable pytest (>=3.6) @@ -672,8 +677,8 @@ name `pytest-requests `_ A simple plugin to use with pytest Jun 24, 2019 4 - Beta pytest (>=3.5.0) `pytest-reraise `_ Make multi-threaded pytest test cases fail when they should Jun 17, 2021 5 - Production/Stable pytest (>=4.6) `pytest-rerun `_ Re-run only changed files in specified branch Jul 08, 2019 N/A pytest (>=3.6) -`pytest-rerunfailures `_ pytest plugin to re-run tests to eliminate flaky failures May 26, 2021 5 - Production/Stable pytest (>=5.3) -`pytest-resilient-circuits `_ Resilient Circuits fixtures for PyTest. May 14, 2021 N/A N/A +`pytest-rerunfailures `_ pytest plugin to re-run tests to eliminate flaky failures Jul 02, 2021 5 - Production/Stable pytest (>=5.3) +`pytest-resilient-circuits `_ Resilient Circuits fixtures for PyTest. Jul 01, 2021 N/A N/A `pytest-resource `_ Load resource fixture plugin to use with pytest Nov 14, 2018 4 - Beta N/A `pytest-resource-path `_ Provides path for uniform access to test resources in isolated directory May 01, 2021 5 - Production/Stable pytest (>=3.5.0) `pytest-responsemock `_ Simplified requests calls mocking for pytest Oct 10, 2020 5 - Production/Stable N/A @@ -700,13 +705,13 @@ name `pytest-sanic `_ a pytest plugin for Sanic May 20, 2021 N/A pytest (>=5.2) `pytest-sanity `_ Dec 07, 2020 N/A N/A `pytest-sa-pg `_ May 14, 2019 N/A N/A -`pytest-sbase `_ A complete web automation framework for end-to-end testing. Jun 21, 2021 5 - Production/Stable N/A +`pytest-sbase `_ A complete web automation framework for end-to-end testing. Jul 06, 2021 5 - Production/Stable N/A `pytest-scenario `_ pytest plugin for test scenarios Feb 06, 2017 3 - Alpha N/A `pytest-schema `_ 👍 Validate return values against a schema-like object in testing Aug 31, 2020 5 - Production/Stable pytest (>=3.5.0) `pytest-securestore `_ An encrypted password store for use within pytest cases Jun 19, 2019 4 - Beta N/A `pytest-select `_ A pytest plugin which allows to (de-)select tests from a file. Jan 18, 2019 3 - Alpha pytest (>=3.0) `pytest-selenium `_ pytest plugin for Selenium Sep 19, 2020 5 - Production/Stable pytest (>=5.0.0) -`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Jun 21, 2021 5 - Production/Stable N/A +`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Jul 06, 2021 5 - Production/Stable N/A `pytest-selenium-enhancer `_ pytest plugin for Selenium Nov 26, 2020 5 - Production/Stable N/A `pytest-selenium-pdiff `_ A pytest package implementing perceptualdiff for Selenium tests. Apr 06, 2017 2 - Pre-Alpha N/A `pytest-send-email `_ Send pytest execution result email Dec 04, 2019 N/A N/A @@ -754,7 +759,7 @@ name `pytest-split-tests `_ A Pytest plugin for running a subset of your tests by splitting them in to equally sized groups. Forked from Mark Adams' original project pytest-test-groups. May 28, 2019 N/A pytest (>=2.5) `pytest-split-tests-tresorit `_ Feb 22, 2021 1 - Planning N/A `pytest-splunk-addon `_ A Dynamic test tool for Splunk Apps and Add-ons Jun 21, 2021 N/A pytest (>5.4.0,<6.3) -`pytest-splunk-addon-ui-smartx `_ Library to support testing Splunk Add-on UX Jun 17, 2021 N/A N/A +`pytest-splunk-addon-ui-smartx `_ Library to support testing Splunk Add-on UX Jun 30, 2021 N/A N/A `pytest-splunk-env `_ pytest fixtures for interaction with Splunk Enterprise and Splunk Cloud Oct 22, 2020 N/A pytest (>=6.1.1,<7.0.0) `pytest-sqitch `_ sqitch for pytest Apr 06, 2020 4 - Beta N/A `pytest-sqlalchemy `_ pytest plugin with sqlalchemy related fixtures Mar 13, 2018 3 - Alpha N/A @@ -807,7 +812,7 @@ name `pytest-testrail-e2e `_ pytest plugin for creating TestRail runs and adding results Jun 11, 2020 N/A pytest (>=3.6) `pytest-testrail-plugin `_ PyTest plugin for TestRail Apr 21, 2020 3 - Alpha pytest `pytest-testrail-reporter `_ Sep 10, 2018 N/A N/A -`pytest-testreport `_ Jun 10, 2021 4 - Beta pytest (>=3.5.0) +`pytest-testreport `_ Jul 01, 2021 4 - Beta pytest (>=3.5.0) `pytest-testslide `_ TestSlide fixture for pytest Jan 07, 2021 5 - Production/Stable pytest (~=6.2) `pytest-test-this `_ Plugin for py.test to run relevant tests, based on naively checking if a test contains a reference to the symbol you supply Sep 15, 2019 2 - Pre-Alpha pytest (>=2.3) `pytest-tesults `_ Tesults plugin for pytest May 18, 2020 5 - Production/Stable pytest (>=3.5.0) @@ -823,6 +828,7 @@ name `pytest-tipsi-testing `_ Better fixtures management. Various helpers Nov 04, 2020 4 - Beta pytest (>=3.3.0) `pytest-tldr `_ A pytest plugin that limits the output to just the things you need. Mar 12, 2021 4 - Beta pytest (>=3.5.0) `pytest-tm4j-reporter `_ Cloud Jira Test Management (TM4J) PyTest reporter plugin Sep 01, 2020 N/A pytest +`pytest-tmreport `_ this is a vue-element ui report for pytest Jul 09, 2021 N/A N/A `pytest-todo `_ A small plugin for the pytest testing framework, marking TODO comments as failure May 23, 2019 4 - Beta pytest `pytest-tomato `_ Mar 01, 2019 5 - Production/Stable N/A `pytest-toolbelt `_ This is just a collection of utilities for pytest, but don't really belong in pytest proper. Aug 12, 2019 3 - Alpha N/A @@ -844,7 +850,7 @@ name `pytest-typhoon-xray `_ Typhoon HIL plugin for pytest Jun 22, 2020 4 - Beta pytest (>=5.4.2) `pytest-tytest `_ Typhoon HIL plugin for pytest May 25, 2020 4 - Beta pytest (>=5.4.2) `pytest-ubersmith `_ Easily mock calls to ubersmith at the `requests` level. Apr 13, 2015 N/A N/A -`pytest-ui `_ Text User Interface for running python tests May 03, 2020 4 - Beta pytest +`pytest-ui `_ Text User Interface for running python tests Jul 05, 2021 4 - Beta pytest `pytest-unhandled-exception-exit-code `_ Plugin for py.test set a different exit code on uncaught exceptions Jun 22, 2020 5 - Production/Stable pytest (>=2.3) `pytest-unittest-filter `_ A pytest plugin for filtering unittest-based test classes Jan 12, 2019 4 - Beta pytest (>=3.1.0) `pytest-unmarked `_ Run only unmarked tests Aug 27, 2019 5 - Production/Stable N/A From 5165bf97c6e1a76e22a3039f189cdd9824c6ffd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Mon, 12 Jul 2021 16:32:27 +0200 Subject: [PATCH 388/630] Revert "Adjust enum reprs for Python 3.10" (#8896) This reverts commit 710446420c0bb98f0761724ec865a894820bb610. The change was reverted in Python 3.10.0b4: https://mail.python.org/archives/list/python-dev@python.org/message/LSTMFAPSPD3BGZ4D6HQFODXZVB3PLYKF/ --- testing/python/metafunc.py | 5 +---- testing/test_pytester.py | 14 ++++---------- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/testing/python/metafunc.py b/testing/python/metafunc.py index ce689e56d77..4a4b141c4b7 100644 --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -448,10 +448,7 @@ def test_idmaker_enum(self) -> None: enum = pytest.importorskip("enum") e = enum.Enum("Foo", "one, two") result = idmaker(("a", "b"), [pytest.param(e.one, e.two)]) - if sys.version_info[:2] >= (3, 10): - assert result == ["one-two"] - else: - assert result == ["Foo.one-Foo.two"] + assert result == ["Foo.one-Foo.two"] def test_idmaker_idfn(self) -> None: """#351""" diff --git a/testing/test_pytester.py b/testing/test_pytester.py index 19f28504d88..7b16c69c23d 100644 --- a/testing/test_pytester.py +++ b/testing/test_pytester.py @@ -744,16 +744,10 @@ def test_run_result_repr() -> None: # known exit code r = pytester_mod.RunResult(1, outlines, errlines, duration=0.5) - if sys.version_info[:2] >= (3, 10): - assert repr(r) == ( - "" - ) - else: - assert repr(r) == ( - "" - ) + assert ( + repr(r) == "" + ) # unknown exit code: just the number r = pytester_mod.RunResult(99, outlines, errlines, duration=0.5) From 5c04674e96f3586aa943b8a3a63741b81ff00ab7 Mon Sep 17 00:00:00 2001 From: Graeme Smecher Date: Mon, 12 Jul 2021 12:27:24 -0700 Subject: [PATCH 389/630] Generate useful parameterization IDs for complex() numbers. --- AUTHORS | 1 + changelog/8898.trivial.rst | 1 + src/_pytest/python.py | 2 +- testing/python/metafunc.py | 2 ++ 4 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 changelog/8898.trivial.rst diff --git a/AUTHORS b/AUTHORS index 16a53d94bdf..d6004f33da5 100644 --- a/AUTHORS +++ b/AUTHORS @@ -125,6 +125,7 @@ Gene Wood George Kussumoto Georgy Dyuldin Gleb Nikonorov +Graeme Smecher Graham Horler Greg Price Gregory Lee diff --git a/changelog/8898.trivial.rst b/changelog/8898.trivial.rst new file mode 100644 index 00000000000..d725157cfc6 --- /dev/null +++ b/changelog/8898.trivial.rst @@ -0,0 +1 @@ +Complex numbers are now treated like floats and integers when generating parameterization IDs. diff --git a/src/_pytest/python.py b/src/_pytest/python.py index f7dfd696bde..d6c14ea520a 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -1334,7 +1334,7 @@ def _idval( if isinstance(val, STRING_TYPES): return _ascii_escaped_by_config(val, config) - elif val is None or isinstance(val, (float, int, bool)): + elif val is None or isinstance(val, (float, int, bool, complex)): return str(val) elif isinstance(val, REGEX_TYPE): return ascii_escaped(val.pattern) diff --git a/testing/python/metafunc.py b/testing/python/metafunc.py index 4a4b141c4b7..ccf9c1d9c24 100644 --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -403,6 +403,7 @@ def test_idmaker_native_strings(self) -> None: pytest.param(tuple("eight"), (8, -8, 8)), pytest.param(b"\xc3\xb4", b"name"), pytest.param(b"\xc3\xb4", "other"), + pytest.param(1.0j, -2.0j), ], ) assert result == [ @@ -418,6 +419,7 @@ def test_idmaker_native_strings(self) -> None: "a9-b9", "\\xc3\\xb4-name", "\\xc3\\xb4-other", + "1j-(-0-2j)", ] def test_idmaker_non_printable_characters(self) -> None: From 3c18a9ecd0aec0e25504219a2ad77431bc1baba3 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 12 Jul 2021 18:09:13 -0300 Subject: [PATCH 390/630] Rename 8898.trivial.rst to 8898.improvement.rst --- changelog/{8898.trivial.rst => 8898.improvement.rst} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename changelog/{8898.trivial.rst => 8898.improvement.rst} (100%) diff --git a/changelog/8898.trivial.rst b/changelog/8898.improvement.rst similarity index 100% rename from changelog/8898.trivial.rst rename to changelog/8898.improvement.rst From 7d1efd5c3a663cbc7d9f0387d7261cae770ed687 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 12 Jul 2021 23:27:47 +0000 Subject: [PATCH 391/630] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v2.19.4 → v2.21.0](https://github.com/asottile/pyupgrade/compare/v2.19.4...v2.21.0) - [github.com/pre-commit/mirrors-mypy: v0.902 → v0.910](https://github.com/pre-commit/mirrors-mypy/compare/v0.902...v0.910) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index adf3f9bdc23..cc951079fdb 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -34,7 +34,7 @@ repos: - id: reorder-python-imports args: ['--application-directories=.:src', --py36-plus] - repo: https://github.com/asottile/pyupgrade - rev: v2.19.4 + rev: v2.21.0 hooks: - id: pyupgrade args: [--py36-plus] @@ -48,7 +48,7 @@ repos: hooks: - id: python-use-type-annotations - repo: https://github.com/pre-commit/mirrors-mypy - rev: v0.902 + rev: v0.910 hooks: - id: mypy files: ^(src/|testing/) From 2c4c57e135917737710d885aa559bf017c308e3d Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Tue, 13 Jul 2021 07:55:00 -0300 Subject: [PATCH 392/630] Revert fspath deprecation It is not clear yet how we should proceed with this deprecation because `pytest.Item.reportinfo` is public API and returns a `py.path` object, and is not clear how plugins and our examples should handle that. Reverting just the deprecation aspect of #8251 so we can get a 7.0.0 release out. We will reintroduce the deprecation later once we have a clear path moving forward with replacing `reportinfo`. Closes #8445 Closes #8821 --- doc/en/deprecations.rst | 9 --------- src/_pytest/deprecated.py | 6 ------ src/_pytest/fixtures.py | 2 -- src/_pytest/nodes.py | 3 --- testing/python/fixtures.py | 3 +-- testing/test_collection.py | 3 +-- 6 files changed, 2 insertions(+), 24 deletions(-) diff --git a/doc/en/deprecations.rst b/doc/en/deprecations.rst index cf501d50921..8136c60ae3e 100644 --- a/doc/en/deprecations.rst +++ b/doc/en/deprecations.rst @@ -33,15 +33,6 @@ In order to support the transition to :mod:`pathlib`, the following hooks now re The accompanying ``py.path.local`` based paths have been deprecated: plugins which manually invoke those hooks should only pass the new ``pathlib.Path`` arguments, and users should change their hook implementations to use the new ``pathlib.Path`` arguments. -``Node.fspath`` in favor of ``pathlib`` and ``Node.path`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. deprecated:: 6.3 - -As pytest tries to move off `py.path.local `__ we ported most of the node internals to :mod:`pathlib`. - -Pytest will provide compatibility for quite a while. - Diamond inheritance between :class:`pytest.File` and :class:`pytest.Item` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/src/_pytest/deprecated.py b/src/_pytest/deprecated.py index 99907d12896..e5fbc0129d0 100644 --- a/src/_pytest/deprecated.py +++ b/src/_pytest/deprecated.py @@ -89,12 +89,6 @@ ) -NODE_FSPATH = UnformattedWarning( - PytestDeprecationWarning, - "{type}.fspath is deprecated and will be replaced by {type}.path.\n" - "see https://docs.pytest.org/en/latest/deprecations.html#node-fspath-in-favor-of-pathlib-and-node-path", -) - HOOK_LEGACY_PATH_ARG = UnformattedWarning( PytestDeprecationWarning, "The ({pylib_path_arg}: py.path.local) argument is deprecated, please use ({pathlib_path_arg}: pathlib.Path)\n" diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index ebc44b2b2b1..e71514f36d2 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -54,7 +54,6 @@ from _pytest.config.argparsing import Parser from _pytest.deprecated import check_ispytest from _pytest.deprecated import FILLFUNCARGS -from _pytest.deprecated import NODE_FSPATH from _pytest.deprecated import YIELD_FIXTURE from _pytest.mark import Mark from _pytest.mark import ParameterSet @@ -520,7 +519,6 @@ def module(self): @property def fspath(self) -> LEGACY_PATH: """(deprecated) The file system path of the test module which collected this test.""" - warnings.warn(NODE_FSPATH.format(type=type(self).__name__), stacklevel=2) return legacy_path(self.path) @property diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py index 4d12f07a27a..c8e2865761c 100644 --- a/src/_pytest/nodes.py +++ b/src/_pytest/nodes.py @@ -28,7 +28,6 @@ from _pytest.config import Config from _pytest.config import ConftestImportFailure from _pytest.deprecated import FSCOLLECTOR_GETHOOKPROXY_ISINITPATH -from _pytest.deprecated import NODE_FSPATH from _pytest.mark.structures import Mark from _pytest.mark.structures import MarkDecorator from _pytest.mark.structures import NodeKeywords @@ -226,12 +225,10 @@ def __init__( @property def fspath(self) -> LEGACY_PATH: """(deprecated) returns a legacy_path copy of self.path""" - warnings.warn(NODE_FSPATH.format(type=type(self).__name__), stacklevel=2) return legacy_path(self.path) @fspath.setter def fspath(self, value: LEGACY_PATH) -> None: - warnings.warn(NODE_FSPATH.format(type=type(self).__name__), stacklevel=2) self.path = Path(value) @classmethod diff --git a/testing/python/fixtures.py b/testing/python/fixtures.py index b7f5b25c544..e634dab45c1 100644 --- a/testing/python/fixtures.py +++ b/testing/python/fixtures.py @@ -967,8 +967,7 @@ def test_request_getmodulepath(self, pytester: Pytester) -> None: (item,) = pytester.genitems([modcol]) req = fixtures.FixtureRequest(item, _ispytest=True) assert req.path == modcol.path - with pytest.warns(pytest.PytestDeprecationWarning): - assert req.fspath == modcol.fspath + assert req.fspath == modcol.fspath def test_request_fixturenames(self, pytester: Pytester) -> None: pytester.makepyfile( diff --git a/testing/test_collection.py b/testing/test_collection.py index f75be459402..1ea569f710b 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -614,8 +614,7 @@ def test_serialization_byid(self, pytester: Pytester) -> None: items2, hookrec = pytester.inline_genitems(item.nodeid) (item2,) = items2 assert item2.name == item.name - with pytest.warns(DeprecationWarning): - assert item2.fspath == item.fspath + assert item2.fspath == item.fspath assert item2.path == item.path def test_find_byid_without_instance_parents(self, pytester: Pytester) -> None: From b88ee5bc552b0189df65fc7276ad656e52ea58ae Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Tue, 13 Jul 2021 08:27:35 -0300 Subject: [PATCH 393/630] Add types-atomicwrites to mypy's pre-commit configuration This is a Windows only dependency, so the problem only shows up when trying to commit on Windows. --- .pre-commit-config.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index adf3f9bdc23..aebc827e49d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -59,6 +59,7 @@ repos: - attrs>=19.2.0 - packaging - tomli + - types-atomicwrites - types-pkg_resources - repo: local hooks: From febb978651de69298b29a81e6160b13ee4034233 Mon Sep 17 00:00:00 2001 From: Naveen-Pratap <34825619+Naveen-Pratap@users.noreply.github.com> Date: Wed, 14 Jul 2021 19:57:26 +0530 Subject: [PATCH 394/630] Update error message for module level skip to include 'allow_module_level' (#8906) Co-authored-by: Naveen Co-authored-by: Bruno Oliveira --- changelog/8432.trivial.rst | 1 + src/_pytest/python.py | 8 ++++---- testing/test_skipping.py | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) create mode 100644 changelog/8432.trivial.rst diff --git a/changelog/8432.trivial.rst b/changelog/8432.trivial.rst new file mode 100644 index 00000000000..af4c7a22617 --- /dev/null +++ b/changelog/8432.trivial.rst @@ -0,0 +1 @@ +Improve error message when :func:`pytest.skip` is used at module level without passing `allow_module_level=True`. diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 79dfb7320a8..8e8d2f38d83 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -608,10 +608,10 @@ def _importtestmodule(self): if e.allow_module_level: raise raise self.CollectError( - "Using pytest.skip outside of a test is not allowed. " - "To decorate a test function, use the @pytest.mark.skip " - "or @pytest.mark.skipif decorators instead, and to skip a " - "module use `pytestmark = pytest.mark.{skip,skipif}." + "Using pytest.skip outside of a test will skip the entire module. " + "If that's your intention, pass `allow_module_level=True`. " + "If you want to skip a specific test or an entire class, " + "use the @pytest.mark.skip or @pytest.mark.skipif decorators." ) from e self.config.pluginmanager.consider_module(mod) return mod diff --git a/testing/test_skipping.py b/testing/test_skipping.py index 53bf953a823..d50a16c9078 100644 --- a/testing/test_skipping.py +++ b/testing/test_skipping.py @@ -1341,7 +1341,7 @@ def test_func(): ) result = pytester.runpytest() result.stdout.fnmatch_lines( - ["*Using pytest.skip outside of a test is not allowed*"] + ["*Using pytest.skip outside of a test will skip the entire module*"] ) From 388691a2051f61563d2c6eabce55576fdf509376 Mon Sep 17 00:00:00 2001 From: Thomas Grainger Date: Wed, 14 Jul 2021 16:53:27 +0100 Subject: [PATCH 395/630] rename tmpdir.rst to tmp_path.rst (#8905) --- changelog/8897.doc.rst | 0 doc/en/contents.rst | 2 +- doc/en/getting-started.rst | 2 +- doc/en/how-to/index.rst | 2 +- doc/en/how-to/{tmpdir.rst => tmp_path.rst} | 5 +++-- doc/en/reference/reference.rst | 2 +- 6 files changed, 7 insertions(+), 6 deletions(-) create mode 100644 changelog/8897.doc.rst rename doc/en/how-to/{tmpdir.rst => tmp_path.rst} (99%) diff --git a/changelog/8897.doc.rst b/changelog/8897.doc.rst new file mode 100644 index 00000000000..e69de29bb2d diff --git a/doc/en/contents.rst b/doc/en/contents.rst index 4623d681880..b5dd35ff297 100644 --- a/doc/en/contents.rst +++ b/doc/en/contents.rst @@ -28,7 +28,7 @@ How-to guides how-to/fixtures how-to/mark how-to/parametrize - how-to/tmpdir + how-to/tmp_path how-to/monkeypatch how-to/doctest how-to/cache diff --git a/doc/en/getting-started.rst b/doc/en/getting-started.rst index 49c17734e78..bbe2a680ee9 100644 --- a/doc/en/getting-started.rst +++ b/doc/en/getting-started.rst @@ -235,7 +235,7 @@ List the name ``tmp_path`` in the test function signature and ``pytest`` will lo FAILED test_tmp_path.py::test_needsfiles - assert 0 1 failed in 0.12s -More info on temporary directory handling is available at :ref:`Temporary directories and files `. +More info on temporary directory handling is available at :ref:`Temporary directories and files `. Find out what kind of builtin :ref:`pytest fixtures ` exist with the command: diff --git a/doc/en/how-to/index.rst b/doc/en/how-to/index.rst index cfbf50fd416..6f52aaecdc3 100644 --- a/doc/en/how-to/index.rst +++ b/doc/en/how-to/index.rst @@ -16,7 +16,7 @@ Core pytest functionality fixtures mark parametrize - tmpdir + tmp_path monkeypatch doctest cache diff --git a/doc/en/how-to/tmpdir.rst b/doc/en/how-to/tmp_path.rst similarity index 99% rename from doc/en/how-to/tmpdir.rst rename to doc/en/how-to/tmp_path.rst index 1823c8a4e1b..740cd2e369f 100644 --- a/doc/en/how-to/tmpdir.rst +++ b/doc/en/how-to/tmp_path.rst @@ -1,6 +1,6 @@ -.. _`tmpdir handling`: -.. _tmpdir: +.. _`tmp_path handling`: +.. _tmp_path: How to use temporary directories and files in tests =================================================== @@ -98,6 +98,7 @@ to save time: See :ref:`tmp_path_factory API ` for details. .. _`tmpdir and tmpdir_factory`: +.. _tmpdir: The ``tmpdir`` and ``tmpdir_factory`` fixtures --------------------------------------------------- diff --git a/doc/en/reference/reference.rst b/doc/en/reference/reference.rst index 715e4350b72..85a565b9bda 100644 --- a/doc/en/reference/reference.rst +++ b/doc/en/reference/reference.rst @@ -607,7 +607,7 @@ Each recorded warning is an instance of :class:`warnings.WarningMessage`. tmp_path ~~~~~~~~ -:ref:`tmpdir` +:ref:`tmp_path` .. autofunction:: _pytest.tmpdir.tmp_path() :no-auto-options: From 0a1f58a91291240802804e88dd5ea1d2859c06f0 Mon Sep 17 00:00:00 2001 From: pytest bot Date: Sun, 18 Jul 2021 00:08:39 +0000 Subject: [PATCH 396/630] [automated] Update plugin list --- doc/en/reference/plugin_list.rst | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/doc/en/reference/plugin_list.rst b/doc/en/reference/plugin_list.rst index a254c0d4026..3fcde8b7ae6 100644 --- a/doc/en/reference/plugin_list.rst +++ b/doc/en/reference/plugin_list.rst @@ -6,7 +6,7 @@ Plugin List PyPI projects that match "pytest-\*" are considered plugins and are listed automatically. Packages classified as inactive are excluded. -This list contains 893 plugins. +This list contains 896 plugins. ============================================================================================================== ======================================================================================================================================================================== ============== ===================== ================================================ name summary last release status requires @@ -91,7 +91,7 @@ name `pytest-blocker `_ pytest plugin to mark a test as blocker and skip all other tests Sep 07, 2015 4 - Beta N/A `pytest-board `_ Local continuous test runner with pytest and watchdog. Jan 20, 2019 N/A N/A `pytest-bpdb `_ A py.test plug-in to enable drop to bpdb debugger on test failure. Jan 19, 2015 2 - Pre-Alpha N/A -`pytest-bravado `_ Pytest-bravado automatically generates from OpenAPI specification client fixtures. Jan 20, 2021 N/A N/A +`pytest-bravado `_ Pytest-bravado automatically generates from OpenAPI specification client fixtures. Jul 13, 2021 N/A N/A `pytest-breed-adapter `_ A simple plugin to connect with breed-server Nov 07, 2018 4 - Beta pytest (>=3.5.0) `pytest-briefcase `_ A pytest plugin for running tests on a Briefcase project. Jun 14, 2020 4 - Beta pytest (>=3.5.0) `pytest-browser `_ A pytest plugin for console based browser test selection just after the collection phase Dec 10, 2016 3 - Alpha N/A @@ -351,7 +351,7 @@ name `pytest-gevent `_ Ensure that gevent is properly patched when invoking pytest Feb 25, 2020 N/A pytest `pytest-gherkin `_ A flexible framework for executing BDD gherkin tests Jul 27, 2019 3 - Alpha pytest (>=5.0.0) `pytest-ghostinspector `_ For finding/executing Ghost Inspector tests May 17, 2016 3 - Alpha N/A -`pytest-girder `_ A set of pytest fixtures for testing Girder applications. Jun 08, 2021 N/A N/A +`pytest-girder `_ A set of pytest fixtures for testing Girder applications. Jul 12, 2021 N/A N/A `pytest-git `_ Git repository fixture for py.test May 28, 2019 5 - Production/Stable pytest `pytest-gitcov `_ Pytest plugin for reporting on coverage of the last git commit. Jan 11, 2020 2 - Pre-Alpha N/A `pytest-git-fixtures `_ Pytest fixtures for testing with git. Mar 11, 2021 4 - Beta pytest @@ -377,7 +377,7 @@ name `pytest-homeassistant `_ A pytest plugin for use with homeassistant custom components. Aug 12, 2020 4 - Beta N/A `pytest-homeassistant-custom-component `_ Experimental package to automatically extract test plugins for Home Assistant custom components Jun 30, 2021 3 - Alpha pytest (==6.2.4) `pytest-honors `_ Report on tests that honor constraints, and guard against regressions Mar 06, 2020 4 - Beta N/A -`pytest-hoverfly `_ Simplify working with Hoverfly from pytest Jun 11, 2021 N/A pytest (>=5.0) +`pytest-hoverfly `_ Simplify working with Hoverfly from pytest Jul 12, 2021 N/A pytest (>=5.0) `pytest-hoverfly-wrapper `_ Integrates the Hoverfly HTTP proxy into Pytest Jan 31, 2021 4 - Beta N/A `pytest-html `_ pytest plugin for generating HTML reports Dec 13, 2020 5 - Production/Stable pytest (!=6.0.0,>=5.0) `pytest-html-lee `_ optimized pytest plugin for generating HTML reports Jun 30, 2020 5 - Production/Stable pytest (>=5.0) @@ -506,6 +506,7 @@ name `pytest-monkeyplus `_ pytest's monkeypatch subclass with extra functionalities Sep 18, 2012 5 - Production/Stable N/A `pytest-monkeytype `_ pytest-monkeytype: Generate Monkeytype annotations from your pytest tests. Jul 29, 2020 4 - Beta N/A `pytest-moto `_ Fixtures for integration tests of AWS services,uses moto mocking library. Aug 28, 2015 1 - Planning N/A +`pytest-motor `_ A pytest plugin for motor, the non-blocking MongoDB driver. Jul 13, 2021 3 - Alpha pytest `pytest-mp `_ A test batcher for multiprocessed Pytest runs May 23, 2018 4 - Beta pytest `pytest-mpi `_ pytest plugin to collect information from tests Mar 14, 2021 3 - Alpha pytest `pytest-mpl `_ pytest plugin to help with testing figures output from Matplotlib Jul 02, 2021 4 - Beta pytest @@ -558,6 +559,7 @@ name `pytest-osxnotify `_ OS X notifications for py.test results. May 15, 2015 N/A N/A `pytest-pact `_ A simple plugin to use with pytest Jan 07, 2019 4 - Beta N/A `pytest-parallel `_ a pytest plugin for parallel and concurrent testing Apr 30, 2020 3 - Alpha pytest (>=3.0.0) +`pytest-parallel-39 `_ a pytest plugin for parallel and concurrent testing Jul 12, 2021 3 - Alpha pytest (>=3.0.0) `pytest-param `_ pytest plugin to test all, first, last or random params Sep 11, 2016 4 - Beta pytest (>=2.6.0) `pytest-paramark `_ Configure pytest fixtures using a combination of"parametrize" and markers Jan 10, 2020 4 - Beta pytest (>=4.5.0) `pytest-parametrization `_ Simpler PyTest parametrization Jul 28, 2019 5 - Production/Stable N/A @@ -627,6 +629,7 @@ name `pytest-pytestrail `_ Pytest plugin for interaction with TestRail Aug 27, 2020 4 - Beta pytest (>=3.8.0) `pytest-pythonpath `_ pytest plugin for adding to the PYTHONPATH from command line or configs. Aug 22, 2018 5 - Production/Stable N/A `pytest-pytorch `_ pytest plugin for a better developer experience when working with the PyTorch test suite May 25, 2021 4 - Beta pytest +`pytest-qasync `_ Pytest support for qasync. Jul 12, 2021 4 - Beta pytest (>=5.4.0) `pytest-qatouch `_ Pytest plugin for uploading test results to your QA Touch Testrun. Jun 26, 2021 4 - Beta pytest (>=6.2.0) `pytest-qml `_ Run QML Tests with pytest Dec 02, 2020 4 - Beta pytest (>=6.0.0) `pytest-qt `_ pytest support for PyQt and PySide applications Jun 13, 2021 5 - Production/Stable pytest (>=3.0.0) @@ -705,13 +708,13 @@ name `pytest-sanic `_ a pytest plugin for Sanic May 20, 2021 N/A pytest (>=5.2) `pytest-sanity `_ Dec 07, 2020 N/A N/A `pytest-sa-pg `_ May 14, 2019 N/A N/A -`pytest-sbase `_ A complete web automation framework for end-to-end testing. Jul 06, 2021 5 - Production/Stable N/A +`pytest-sbase `_ A complete web automation framework for end-to-end testing. Jul 12, 2021 5 - Production/Stable N/A `pytest-scenario `_ pytest plugin for test scenarios Feb 06, 2017 3 - Alpha N/A `pytest-schema `_ 👍 Validate return values against a schema-like object in testing Aug 31, 2020 5 - Production/Stable pytest (>=3.5.0) `pytest-securestore `_ An encrypted password store for use within pytest cases Jun 19, 2019 4 - Beta N/A `pytest-select `_ A pytest plugin which allows to (de-)select tests from a file. Jan 18, 2019 3 - Alpha pytest (>=3.0) `pytest-selenium `_ pytest plugin for Selenium Sep 19, 2020 5 - Production/Stable pytest (>=5.0.0) -`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Jul 06, 2021 5 - Production/Stable N/A +`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Jul 12, 2021 5 - Production/Stable N/A `pytest-selenium-enhancer `_ pytest plugin for Selenium Nov 26, 2020 5 - Production/Stable N/A `pytest-selenium-pdiff `_ A pytest package implementing perceptualdiff for Selenium tests. Apr 06, 2017 2 - Pre-Alpha N/A `pytest-send-email `_ Send pytest execution result email Dec 04, 2019 N/A N/A @@ -758,8 +761,8 @@ name `pytest-splitio `_ Split.io SDK integration for e2e tests Sep 22, 2020 N/A pytest (<7,>=5.0) `pytest-split-tests `_ A Pytest plugin for running a subset of your tests by splitting them in to equally sized groups. Forked from Mark Adams' original project pytest-test-groups. May 28, 2019 N/A pytest (>=2.5) `pytest-split-tests-tresorit `_ Feb 22, 2021 1 - Planning N/A -`pytest-splunk-addon `_ A Dynamic test tool for Splunk Apps and Add-ons Jun 21, 2021 N/A pytest (>5.4.0,<6.3) -`pytest-splunk-addon-ui-smartx `_ Library to support testing Splunk Add-on UX Jun 30, 2021 N/A N/A +`pytest-splunk-addon `_ A Dynamic test tool for Splunk Apps and Add-ons Jul 14, 2021 N/A pytest (>5.4.0,<6.3) +`pytest-splunk-addon-ui-smartx `_ Library to support testing Splunk Add-on UX Jul 14, 2021 N/A N/A `pytest-splunk-env `_ pytest fixtures for interaction with Splunk Enterprise and Splunk Cloud Oct 22, 2020 N/A pytest (>=6.1.1,<7.0.0) `pytest-sqitch `_ sqitch for pytest Apr 06, 2020 4 - Beta N/A `pytest-sqlalchemy `_ pytest plugin with sqlalchemy related fixtures Mar 13, 2018 3 - Alpha N/A @@ -778,7 +781,7 @@ name `pytest-stub `_ Stub packages, modules and attributes. Apr 28, 2020 5 - Production/Stable N/A `pytest-stubprocess `_ Provide stub implementations for subprocesses in Python tests Sep 17, 2018 3 - Alpha pytest (>=3.5.0) `pytest-study `_ A pytest plugin to organize long run tests (named studies) without interfering the regular tests Sep 26, 2017 3 - Alpha pytest (>=2.0) -`pytest-subprocess `_ A plugin to fake subprocess for pytest Jun 18, 2021 5 - Production/Stable pytest (>=4.0.0) +`pytest-subprocess `_ A plugin to fake subprocess for pytest Jul 17, 2021 5 - Production/Stable pytest (>=4.0.0) `pytest-subtesthack `_ A hack to explicitly set up and tear down fixtures. Mar 02, 2021 N/A N/A `pytest-subtests `_ unittest subTest() support and subtests fixture May 29, 2021 4 - Beta pytest (>=5.3.0) `pytest-subunit `_ pytest-subunit is a plugin for py.test which outputs testsresult in subunit format. Aug 29, 2017 N/A N/A @@ -828,7 +831,7 @@ name `pytest-tipsi-testing `_ Better fixtures management. Various helpers Nov 04, 2020 4 - Beta pytest (>=3.3.0) `pytest-tldr `_ A pytest plugin that limits the output to just the things you need. Mar 12, 2021 4 - Beta pytest (>=3.5.0) `pytest-tm4j-reporter `_ Cloud Jira Test Management (TM4J) PyTest reporter plugin Sep 01, 2020 N/A pytest -`pytest-tmreport `_ this is a vue-element ui report for pytest Jul 09, 2021 N/A N/A +`pytest-tmreport `_ this is a vue-element ui report for pytest Jul 16, 2021 N/A N/A `pytest-todo `_ A small plugin for the pytest testing framework, marking TODO comments as failure May 23, 2019 4 - Beta pytest `pytest-tomato `_ Mar 01, 2019 5 - Production/Stable N/A `pytest-toolbelt `_ This is just a collection of utilities for pytest, but don't really belong in pytest proper. Aug 12, 2019 3 - Alpha N/A From 5470d33e82aaa203108423f5d3d9c7228c83090b Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sun, 18 Jul 2021 13:51:09 +0300 Subject: [PATCH 397/630] store: rename Store to Stash The name "stash" is a bit more distinguishable and more evocative of the intended usage. --- src/_pytest/assertion/rewrite.py | 4 +- src/_pytest/config/__init__.py | 4 +- src/_pytest/faulthandler.py | 6 +-- src/_pytest/fixtures.py | 4 +- src/_pytest/junitxml.py | 4 +- src/_pytest/logging.py | 6 +-- src/_pytest/mark/__init__.py | 4 +- src/_pytest/nodes.py | 4 +- src/_pytest/pastebin.py | 4 +- src/_pytest/skipping.py | 4 +- src/_pytest/{store.py => stash.py} | 62 +++++++++++++++--------------- testing/test_junitxml.py | 4 +- testing/test_stash.py | 60 +++++++++++++++++++++++++++++ testing/test_store.py | 60 ----------------------------- 14 files changed, 115 insertions(+), 115 deletions(-) rename src/_pytest/{store.py => stash.py} (58%) create mode 100644 testing/test_stash.py delete mode 100644 testing/test_store.py diff --git a/src/_pytest/assertion/rewrite.py b/src/_pytest/assertion/rewrite.py index 33e2ef6cc49..3c8ffde4e04 100644 --- a/src/_pytest/assertion/rewrite.py +++ b/src/_pytest/assertion/rewrite.py @@ -38,13 +38,13 @@ from _pytest.main import Session from _pytest.pathlib import absolutepath from _pytest.pathlib import fnmatch_ex -from _pytest.store import StoreKey +from _pytest.stash import StashKey if TYPE_CHECKING: from _pytest.assertion import AssertionState -assertstate_key = StoreKey["AssertionState"]() +assertstate_key = StashKey["AssertionState"]() # pytest caches rewritten pycs in pycache dirs diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index f3299113780..feca31f4cbc 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -56,7 +56,7 @@ from _pytest.pathlib import import_path from _pytest.pathlib import ImportMode from _pytest.pathlib import resolve_package_path -from _pytest.store import Store +from _pytest.stash import Stash from _pytest.warning_types import PytestConfigWarning if TYPE_CHECKING: @@ -933,7 +933,7 @@ def __init__( self._cleanup: List[Callable[[], None]] = [] # A place where plugins can store information on the config for their # own use. Currently only intended for internal plugins. - self._store = Store() + self._store = Stash() self.pluginmanager.register(self, "pytestconfig") self._configured = False self.hook.pytest_addoption.call_historic( diff --git a/src/_pytest/faulthandler.py b/src/_pytest/faulthandler.py index c8eb0310128..fc2471f39c2 100644 --- a/src/_pytest/faulthandler.py +++ b/src/_pytest/faulthandler.py @@ -8,11 +8,11 @@ from _pytest.config import Config from _pytest.config.argparsing import Parser from _pytest.nodes import Item -from _pytest.store import StoreKey +from _pytest.stash import StashKey -fault_handler_stderr_key = StoreKey[TextIO]() -fault_handler_originally_enabled_key = StoreKey[bool]() +fault_handler_stderr_key = StashKey[TextIO]() +fault_handler_originally_enabled_key = StashKey[bool]() def pytest_addoption(parser: Parser) -> None: diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index b19c850a6b7..305ec034879 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -62,7 +62,7 @@ from _pytest.outcomes import TEST_OUTCOME from _pytest.pathlib import absolutepath from _pytest.pathlib import bestrelpath -from _pytest.store import StoreKey +from _pytest.stash import StashKey if TYPE_CHECKING: from typing import Deque @@ -149,7 +149,7 @@ def get_scope_node( # Used for storing artificial fixturedefs for direct parametrization. -name2pseudofixturedef_key = StoreKey[Dict[str, "FixtureDef[Any]"]]() +name2pseudofixturedef_key = StashKey[Dict[str, "FixtureDef[Any]"]]() def add_funcarg_pseudo_fixture_def( diff --git a/src/_pytest/junitxml.py b/src/_pytest/junitxml.py index fafb5fa1aa6..d7b00cca163 100644 --- a/src/_pytest/junitxml.py +++ b/src/_pytest/junitxml.py @@ -30,11 +30,11 @@ from _pytest.config.argparsing import Parser from _pytest.fixtures import FixtureRequest from _pytest.reports import TestReport -from _pytest.store import StoreKey +from _pytest.stash import StashKey from _pytest.terminal import TerminalReporter -xml_key = StoreKey["LogXML"]() +xml_key = StashKey["LogXML"]() def bin_xml_escape(arg: object) -> str: diff --git a/src/_pytest/logging.py b/src/_pytest/logging.py index 8b4865b5d87..376a6b31f7e 100644 --- a/src/_pytest/logging.py +++ b/src/_pytest/logging.py @@ -31,15 +31,15 @@ from _pytest.fixtures import fixture from _pytest.fixtures import FixtureRequest from _pytest.main import Session -from _pytest.store import StoreKey +from _pytest.stash import StashKey from _pytest.terminal import TerminalReporter DEFAULT_LOG_FORMAT = "%(levelname)-8s %(name)s:%(filename)s:%(lineno)d %(message)s" DEFAULT_LOG_DATE_FORMAT = "%H:%M:%S" _ANSI_ESCAPE_SEQ = re.compile(r"\x1b\[[\d;]+m") -caplog_handler_key = StoreKey["LogCaptureHandler"]() -caplog_records_key = StoreKey[Dict[str, List[logging.LogRecord]]]() +caplog_handler_key = StashKey["LogCaptureHandler"]() +caplog_records_key = StashKey[Dict[str, List[logging.LogRecord]]]() def _remove_ansi_escape_sequences(text: str) -> str: diff --git a/src/_pytest/mark/__init__.py b/src/_pytest/mark/__init__.py index 0dc3718e89b..877b5ef1c39 100644 --- a/src/_pytest/mark/__init__.py +++ b/src/_pytest/mark/__init__.py @@ -25,7 +25,7 @@ from _pytest.config.argparsing import Parser from _pytest.deprecated import MINUS_K_COLON from _pytest.deprecated import MINUS_K_DASH -from _pytest.store import StoreKey +from _pytest.stash import StashKey if TYPE_CHECKING: from _pytest.nodes import Item @@ -41,7 +41,7 @@ ] -old_mark_config_key = StoreKey[Optional[Config]]() +old_mark_config_key = StashKey[Optional[Config]]() def param( diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py index c8e2865761c..939c7b207c0 100644 --- a/src/_pytest/nodes.py +++ b/src/_pytest/nodes.py @@ -34,7 +34,7 @@ from _pytest.outcomes import fail from _pytest.pathlib import absolutepath from _pytest.pathlib import commonpath -from _pytest.store import Store +from _pytest.stash import Stash from _pytest.warning_types import PytestWarning if TYPE_CHECKING: @@ -220,7 +220,7 @@ def __init__( # A place where plugins can store information on the node for their # own use. Currently only intended for internal plugins. - self._store = Store() + self._store = Stash() @property def fspath(self) -> LEGACY_PATH: diff --git a/src/_pytest/pastebin.py b/src/_pytest/pastebin.py index 189ed3c5e10..1ca7cc4948c 100644 --- a/src/_pytest/pastebin.py +++ b/src/_pytest/pastebin.py @@ -8,11 +8,11 @@ from _pytest.config import Config from _pytest.config import create_terminal_writer from _pytest.config.argparsing import Parser -from _pytest.store import StoreKey +from _pytest.stash import StashKey from _pytest.terminal import TerminalReporter -pastebinfile_key = StoreKey[IO[bytes]]() +pastebinfile_key = StashKey[IO[bytes]]() def pytest_addoption(parser: Parser) -> None: diff --git a/src/_pytest/skipping.py b/src/_pytest/skipping.py index f7a026ae74e..18f9a49bc3a 100644 --- a/src/_pytest/skipping.py +++ b/src/_pytest/skipping.py @@ -21,7 +21,7 @@ from _pytest.outcomes import xfail from _pytest.reports import BaseReport from _pytest.runner import CallInfo -from _pytest.store import StoreKey +from _pytest.stash import StashKey def pytest_addoption(parser: Parser) -> None: @@ -228,7 +228,7 @@ def evaluate_xfail_marks(item: Item) -> Optional[Xfail]: # Saves the xfail mark evaluation. Can be refreshed during call if None. -xfailed_key = StoreKey[Optional[Xfail]]() +xfailed_key = StashKey[Optional[Xfail]]() @hookimpl(tryfirst=True) diff --git a/src/_pytest/store.py b/src/_pytest/stash.py similarity index 58% rename from src/_pytest/store.py rename to src/_pytest/stash.py index 43fd9e89e6b..d6795b197c7 100644 --- a/src/_pytest/store.py +++ b/src/_pytest/stash.py @@ -6,59 +6,59 @@ from typing import Union -__all__ = ["Store", "StoreKey"] +__all__ = ["Stash", "StashKey"] T = TypeVar("T") D = TypeVar("D") -class StoreKey(Generic[T]): - """StoreKey is an object used as a key to a Store. +class StashKey(Generic[T]): + """``StashKey`` is an object used as a key to a ``Stash``. - A StoreKey is associated with the type T of the value of the key. + A ``StashKey`` is associated with the type ``T`` of the value of the key. - A StoreKey is unique and cannot conflict with another key. + A ``StashKey`` is unique and cannot conflict with another key. """ __slots__ = () -class Store: - """Store is a type-safe heterogeneous mutable mapping that +class Stash: + r"""``Stash`` is a type-safe heterogeneous mutable mapping that allows keys and value types to be defined separately from - where it (the Store) is created. + where it (the ``Stash``) is created. - Usually you will be given an object which has a ``Store``: + Usually you will be given an object which has a ``Stash``: .. code-block:: python - store: Store = some_object.store + stash: Stash = some_object.stash - If a module wants to store data in this Store, it creates StoreKeys + If a module wants to store data in this Stash, it creates ``StashKey``\s for its keys (at the module level): .. code-block:: python - some_str_key = StoreKey[str]() - some_bool_key = StoreKey[bool]() + some_str_key = StashKey[str]() + some_bool_key = StashKey[bool]() To store information: .. code-block:: python # Value type must match the key. - store[some_str_key] = "value" - store[some_bool_key] = True + stash[some_str_key] = "value" + stash[some_bool_key] = True To retrieve the information: .. code-block:: python # The static type of some_str is str. - some_str = store[some_str_key] + some_str = stash[some_str_key] # The static type of some_bool is bool. - some_bool = store[some_bool_key] + some_bool = stash[some_bool_key] Why use this? ------------- @@ -75,28 +75,28 @@ class Store: the object. Module External stores its data in private keys of this dict. This doesn't work well because retrieved values are untyped. - Good solution: module Internal adds a ``Store`` to the object. Module - External mints StoreKeys for its own keys. Module External stores and + Good solution: module Internal adds a ``Stash`` to the object. Module + External mints StashKeys for its own keys. Module External stores and retrieves its data using these keys. """ - __slots__ = ("_store",) + __slots__ = ("_storage",) def __init__(self) -> None: - self._store: Dict[StoreKey[Any], object] = {} + self._storage: Dict[StashKey[Any], object] = {} - def __setitem__(self, key: StoreKey[T], value: T) -> None: + def __setitem__(self, key: StashKey[T], value: T) -> None: """Set a value for key.""" - self._store[key] = value + self._storage[key] = value - def __getitem__(self, key: StoreKey[T]) -> T: + def __getitem__(self, key: StashKey[T]) -> T: """Get the value for key. Raises ``KeyError`` if the key wasn't set before. """ - return cast(T, self._store[key]) + return cast(T, self._storage[key]) - def get(self, key: StoreKey[T], default: D) -> Union[T, D]: + def get(self, key: StashKey[T], default: D) -> Union[T, D]: """Get the value for key, or return default if the key wasn't set before.""" try: @@ -104,7 +104,7 @@ def get(self, key: StoreKey[T], default: D) -> Union[T, D]: except KeyError: return default - def setdefault(self, key: StoreKey[T], default: T) -> T: + def setdefault(self, key: StashKey[T], default: T) -> T: """Return the value of key if already set, otherwise set the value of key to default and return default.""" try: @@ -113,13 +113,13 @@ def setdefault(self, key: StoreKey[T], default: T) -> T: self[key] = default return default - def __delitem__(self, key: StoreKey[T]) -> None: + def __delitem__(self, key: StashKey[T]) -> None: """Delete the value for key. Raises ``KeyError`` if the key wasn't set before. """ - del self._store[key] + del self._storage[key] - def __contains__(self, key: StoreKey[T]) -> bool: + def __contains__(self, key: StashKey[T]) -> bool: """Return whether key was set.""" - return key in self._store + return key in self._storage diff --git a/testing/test_junitxml.py b/testing/test_junitxml.py index 5cb062932cd..0ded0685a9b 100644 --- a/testing/test_junitxml.py +++ b/testing/test_junitxml.py @@ -21,7 +21,7 @@ from _pytest.pytester import RunResult from _pytest.reports import BaseReport from _pytest.reports import TestReport -from _pytest.store import Store +from _pytest.stash import Stash @pytest.fixture(scope="session") @@ -951,7 +951,7 @@ class FakeConfig: def __init__(self): self.pluginmanager = self self.option = self - self._store = Store() + self._store = Stash() def getini(self, name): return "pytest" diff --git a/testing/test_stash.py b/testing/test_stash.py new file mode 100644 index 00000000000..f449a056cb4 --- /dev/null +++ b/testing/test_stash.py @@ -0,0 +1,60 @@ +import pytest +from _pytest.stash import Stash +from _pytest.stash import StashKey + + +def test_stash() -> None: + stash = Stash() + + key1 = StashKey[str]() + key2 = StashKey[int]() + + # Basic functionality - single key. + assert key1 not in stash + stash[key1] = "hello" + assert key1 in stash + assert stash[key1] == "hello" + assert stash.get(key1, None) == "hello" + stash[key1] = "world" + assert stash[key1] == "world" + # Has correct type (no mypy error). + stash[key1] + "string" + + # No interaction with another key. + assert key2 not in stash + assert stash.get(key2, None) is None + with pytest.raises(KeyError): + stash[key2] + with pytest.raises(KeyError): + del stash[key2] + stash[key2] = 1 + assert stash[key2] == 1 + # Has correct type (no mypy error). + stash[key2] + 20 + del stash[key1] + with pytest.raises(KeyError): + del stash[key1] + with pytest.raises(KeyError): + stash[key1] + + # setdefault + stash[key1] = "existing" + assert stash.setdefault(key1, "default") == "existing" + assert stash[key1] == "existing" + key_setdefault = StashKey[bytes]() + assert stash.setdefault(key_setdefault, b"default") == b"default" + assert stash[key_setdefault] == b"default" + + # Can't accidentally add attributes to stash object itself. + with pytest.raises(AttributeError): + stash.foo = "nope" # type: ignore[attr-defined] + + # No interaction with anoter stash. + stash2 = Stash() + key3 = StashKey[int]() + assert key2 not in stash2 + stash2[key2] = 100 + stash2[key3] = 200 + assert stash2[key2] + stash2[key3] == 300 + assert stash[key2] == 1 + assert key3 not in stash diff --git a/testing/test_store.py b/testing/test_store.py deleted file mode 100644 index b6d4208a092..00000000000 --- a/testing/test_store.py +++ /dev/null @@ -1,60 +0,0 @@ -import pytest -from _pytest.store import Store -from _pytest.store import StoreKey - - -def test_store() -> None: - store = Store() - - key1 = StoreKey[str]() - key2 = StoreKey[int]() - - # Basic functionality - single key. - assert key1 not in store - store[key1] = "hello" - assert key1 in store - assert store[key1] == "hello" - assert store.get(key1, None) == "hello" - store[key1] = "world" - assert store[key1] == "world" - # Has correct type (no mypy error). - store[key1] + "string" - - # No interaction with another key. - assert key2 not in store - assert store.get(key2, None) is None - with pytest.raises(KeyError): - store[key2] - with pytest.raises(KeyError): - del store[key2] - store[key2] = 1 - assert store[key2] == 1 - # Has correct type (no mypy error). - store[key2] + 20 - del store[key1] - with pytest.raises(KeyError): - del store[key1] - with pytest.raises(KeyError): - store[key1] - - # setdefault - store[key1] = "existing" - assert store.setdefault(key1, "default") == "existing" - assert store[key1] == "existing" - key_setdefault = StoreKey[bytes]() - assert store.setdefault(key_setdefault, b"default") == b"default" - assert store[key_setdefault] == b"default" - - # Can't accidentally add attributes to store object itself. - with pytest.raises(AttributeError): - store.foo = "nope" # type: ignore[attr-defined] - - # No interaction with anoter store. - store2 = Store() - key3 = StoreKey[int]() - assert key2 not in store2 - store2[key2] = 100 - store2[key3] = 200 - assert store2[key2] + store2[key3] == 300 - assert store[key2] == 1 - assert key3 not in store From 5f39e31736ac6be61ac9ba88112cc0505c344c40 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sun, 18 Jul 2021 14:59:16 +0300 Subject: [PATCH 398/630] stash: implement __len__ Part of the MutableMapping abc (though we can't actually implement that). --- src/_pytest/stash.py | 4 ++++ testing/test_stash.py | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/src/_pytest/stash.py b/src/_pytest/stash.py index d6795b197c7..30786d6ef8e 100644 --- a/src/_pytest/stash.py +++ b/src/_pytest/stash.py @@ -123,3 +123,7 @@ def __delitem__(self, key: StashKey[T]) -> None: def __contains__(self, key: StashKey[T]) -> bool: """Return whether key was set.""" return key in self._storage + + def __len__(self) -> int: + """Return how many items exist in the stash.""" + return len(self._storage) diff --git a/testing/test_stash.py b/testing/test_stash.py index f449a056cb4..bb294f5da35 100644 --- a/testing/test_stash.py +++ b/testing/test_stash.py @@ -6,6 +6,9 @@ def test_stash() -> None: stash = Stash() + assert len(stash) == 0 + assert not stash + key1 = StashKey[str]() key2 = StashKey[int]() @@ -19,6 +22,8 @@ def test_stash() -> None: assert stash[key1] == "world" # Has correct type (no mypy error). stash[key1] + "string" + assert len(stash) == 1 + assert stash # No interaction with another key. assert key2 not in stash @@ -44,6 +49,8 @@ def test_stash() -> None: key_setdefault = StashKey[bytes]() assert stash.setdefault(key_setdefault, b"default") == b"default" assert stash[key_setdefault] == b"default" + assert len(stash) == 3 + assert stash # Can't accidentally add attributes to stash object itself. with pytest.raises(AttributeError): From 53357f464ca76e1b310ecd96bf51f71e884a5454 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 19 Jul 2021 22:13:39 +0000 Subject: [PATCH 399/630] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black: 21.6b0 → 21.7b0](https://github.com/psf/black/compare/21.6b0...21.7b0) - [github.com/asottile/pyupgrade: v2.21.0 → v2.21.2](https://github.com/asottile/pyupgrade/compare/v2.21.0...v2.21.2) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0ff7261e88e..6ab5f1d4a22 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/psf/black - rev: 21.6b0 + rev: 21.7b0 hooks: - id: black args: [--safe, --quiet] @@ -34,7 +34,7 @@ repos: - id: reorder-python-imports args: ['--application-directories=.:src', --py36-plus] - repo: https://github.com/asottile/pyupgrade - rev: v2.21.0 + rev: v2.21.2 hooks: - id: pyupgrade args: [--py36-plus] From 5e39f43e7cd577b9b9ef27bfa1985a493a7696e7 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Fri, 23 Jul 2021 07:08:28 -0300 Subject: [PATCH 400/630] contact.rst: Added github discussions Ported from #8932 --- doc/en/contact.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/en/contact.rst b/doc/en/contact.rst index 9e714ed2c2f..561d5e78a04 100644 --- a/doc/en/contact.rst +++ b/doc/en/contact.rst @@ -7,9 +7,9 @@ Contact channels - `pytest issue tracker`_ to report bugs or suggest features (for version 2.0 and above). - +- `pytest discussions`_ at github for general questions. - `pytest on stackoverflow.com `_ - to post questions with the tag ``pytest``. New Questions will usually + to post precise questions with the tag ``pytest``. New Questions will usually be seen by pytest users or developers and answered quickly. - `Testing In Python`_: a mailing list for Python testing tools and discussion. @@ -33,6 +33,8 @@ Contact channels .. _`pytest issue tracker`: https://github.com/pytest-dev/pytest/issues .. _`old issue tracker`: https://bitbucket.org/hpk42/py-trunk/issues/ +.. _`pytest discussions`: https://github.com/pytest-dev/pytest/discussions + .. _`merlinux.eu`: https://merlinux.eu/ .. _`get an account`: From c6bdeb84914d54a1dea04b79492150aa3d1b42f2 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sun, 18 Jul 2021 14:05:58 +0300 Subject: [PATCH 401/630] stash: make Stash and StashKey public It's had enough time to bake - let's allow external plugins to use it. --- changelog/8920.feature.rst | 2 ++ doc/en/how-to/writing_hook_functions.rst | 39 ++++++++++++++++++++++++ doc/en/reference/reference.rst | 12 ++++++++ src/_pytest/config/__init__.py | 12 ++++++-- src/_pytest/nodes.py | 10 ++++-- src/_pytest/stash.py | 29 ++++-------------- src/pytest/__init__.py | 4 +++ 7 files changed, 79 insertions(+), 29 deletions(-) create mode 100644 changelog/8920.feature.rst diff --git a/changelog/8920.feature.rst b/changelog/8920.feature.rst new file mode 100644 index 00000000000..05bdab6da75 --- /dev/null +++ b/changelog/8920.feature.rst @@ -0,0 +1,2 @@ +Added :class:`pytest.Stash`, a facility for plugins to store their data on :class:`~pytest.Config` and :class:`~_pytest.nodes.Node`\s in a type-safe and conflict-free manner. +See :ref:`plugin-stash` for details. diff --git a/doc/en/how-to/writing_hook_functions.rst b/doc/en/how-to/writing_hook_functions.rst index de2ff3bdc10..33b9c86cfc2 100644 --- a/doc/en/how-to/writing_hook_functions.rst +++ b/doc/en/how-to/writing_hook_functions.rst @@ -311,3 +311,42 @@ declaring the hook functions directly in your plugin module, for example: This has the added benefit of allowing you to conditionally install hooks depending on which plugins are installed. + +.. _plugin-stash: + +Storing data on items across hook functions +------------------------------------------- + +Plugins often need to store data on :class:`~pytest.Item`\s in one hook +implementation, and access it in another. One common solution is to just +assign some private attribute directly on the item, but type-checkers like +mypy frown upon this, and it may also cause conflicts with other plugins. +So pytest offers a better way to do this, :attr:`_pytest.nodes.Node.stash `. + +To use the "stash" in your plugins, first create "stash keys" somewhere at the +top level of your plugin: + +.. code-block:: python + + been_there_key: pytest.StashKey[bool]() + done_that_key: pytest.StashKey[str]() + +then use the keys to stash your data at some point: + +.. code-block:: python + + def pytest_runtest_setup(item: pytest.Item) -> None: + item.stash[been_there_key] = True + item.stash[done_that_key] = "no" + +and retrieve them at another point: + +.. code-block:: python + + def pytest_runtest_teardown(item: pytest.Item) -> None: + if not item.stash[been_there_key]: + print("Oh?") + item.stash[done_that_key] = "yes!" + +Stashes are available on all node types (like :class:`~pytest.Class`, +:class:`~pytest.Session`) and also on :class:`~pytest.Config`, if needed. diff --git a/doc/en/reference/reference.rst b/doc/en/reference/reference.rst index 85a565b9bda..b9b68c01d79 100644 --- a/doc/en/reference/reference.rst +++ b/doc/en/reference/reference.rst @@ -962,6 +962,18 @@ Result used within :ref:`hook wrappers `. .. automethod:: pluggy.callers._Result.get_result .. automethod:: pluggy.callers._Result.force_result +Stash +~~~~~ + +.. autoclass:: pytest.Stash + :special-members: __setitem__, __getitem__, __delitem__, __contains__, __len__ + :members: + +.. autoclass:: pytest.StashKey + :show-inheritance: + :members: + + Global Variables ---------------- diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index feca31f4cbc..18423a47ef0 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -923,6 +923,15 @@ def __init__( :type: PytestPluginManager """ + self.stash = Stash() + """A place where plugins can store information on the config for their + own use. + + :type: Stash + """ + # Deprecated alias. Was never public. Can be removed in a few releases. + self._store = self.stash + from .compat import PathAwareHookProxy self.trace = self.pluginmanager.trace.root.get("config") @@ -931,9 +940,6 @@ def __init__( self._override_ini: Sequence[str] = () self._opt2dest: Dict[str, str] = {} self._cleanup: List[Callable[[], None]] = [] - # A place where plugins can store information on the config for their - # own use. Currently only intended for internal plugins. - self._store = Stash() self.pluginmanager.register(self, "pytestconfig") self._configured = False self.hook.pytest_addoption.call_historic( diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py index 939c7b207c0..e695f89bb3c 100644 --- a/src/_pytest/nodes.py +++ b/src/_pytest/nodes.py @@ -218,9 +218,13 @@ def __init__( if self.name != "()": self._nodeid += "::" + self.name - # A place where plugins can store information on the node for their - # own use. Currently only intended for internal plugins. - self._store = Stash() + #: A place where plugins can store information on the node for their + #: own use. + #: + #: :type: Stash + self.stash = Stash() + # Deprecated alias. Was never public. Can be removed in a few releases. + self._store = self.stash @property def fspath(self) -> LEGACY_PATH: diff --git a/src/_pytest/stash.py b/src/_pytest/stash.py index 30786d6ef8e..e61d75b95f7 100644 --- a/src/_pytest/stash.py +++ b/src/_pytest/stash.py @@ -14,7 +14,7 @@ class StashKey(Generic[T]): - """``StashKey`` is an object used as a key to a ``Stash``. + """``StashKey`` is an object used as a key to a :class:`Stash`. A ``StashKey`` is associated with the type ``T`` of the value of the key. @@ -29,17 +29,19 @@ class Stash: allows keys and value types to be defined separately from where it (the ``Stash``) is created. - Usually you will be given an object which has a ``Stash``: + Usually you will be given an object which has a ``Stash``, for example + :class:`~pytest.Config` or a :class:`~_pytest.nodes.Node`: .. code-block:: python stash: Stash = some_object.stash - If a module wants to store data in this Stash, it creates ``StashKey``\s - for its keys (at the module level): + If a module or plugin wants to store data in this ``Stash``, it creates + :class:`StashKey`\s for its keys (at the module level): .. code-block:: python + # At the top-level of the module some_str_key = StashKey[str]() some_bool_key = StashKey[bool]() @@ -59,25 +61,6 @@ class Stash: some_str = stash[some_str_key] # The static type of some_bool is bool. some_bool = stash[some_bool_key] - - Why use this? - ------------- - - Problem: module Internal defines an object. Module External, which - module Internal doesn't know about, receives the object and wants to - attach information to it, to be retrieved later given the object. - - Bad solution 1: Module External assigns private attributes directly on - the object. This doesn't work well because the type checker doesn't - know about these attributes and it complains about undefined attributes. - - Bad solution 2: module Internal adds a ``Dict[str, Any]`` attribute to - the object. Module External stores its data in private keys of this dict. - This doesn't work well because retrieved values are untyped. - - Good solution: module Internal adds a ``Stash`` to the object. Module - External mints StashKeys for its own keys. Module External stores and - retrieves its data using these keys. """ __slots__ = ("_storage",) diff --git a/src/pytest/__init__.py b/src/pytest/__init__.py index 3694f0fc471..bfdc7ae1bf9 100644 --- a/src/pytest/__init__.py +++ b/src/pytest/__init__.py @@ -55,6 +55,8 @@ from _pytest.recwarn import WarningsRecorder from _pytest.recwarn import warns from _pytest.runner import CallInfo +from _pytest.stash import Stash +from _pytest.stash import StashKey from _pytest.tmpdir import TempdirFactory from _pytest.tmpdir import TempPathFactory from _pytest.warning_types import PytestAssertRewriteWarning @@ -131,6 +133,8 @@ "Session", "set_trace", "skip", + "Stash", + "StashKey", "version_tuple", "TempPathFactory", "Testdir", From 2aaea20cb219e7d66751f51508ba2a5f80c4c009 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sun, 18 Jul 2021 14:18:03 +0300 Subject: [PATCH 402/630] Use {node,config}.stash instead of ._store Use the public name. The `_store` name is only because some plugins started using it anyway - will be removed at some point. --- src/_pytest/assertion/__init__.py | 12 ++++++------ src/_pytest/assertion/rewrite.py | 4 ++-- src/_pytest/faulthandler.py | 16 ++++++++-------- src/_pytest/fixtures.py | 2 +- src/_pytest/junitxml.py | 14 +++++++------- src/_pytest/logging.py | 14 +++++++------- src/_pytest/mark/__init__.py | 4 ++-- src/_pytest/pastebin.py | 10 +++++----- src/_pytest/skipping.py | 12 ++++++------ testing/logging/test_fixture.py | 2 +- testing/test_junitxml.py | 2 +- 11 files changed, 46 insertions(+), 46 deletions(-) diff --git a/src/_pytest/assertion/__init__.py b/src/_pytest/assertion/__init__.py index 29c25d03577..480a26ad867 100644 --- a/src/_pytest/assertion/__init__.py +++ b/src/_pytest/assertion/__init__.py @@ -88,13 +88,13 @@ def __init__(self, config: Config, mode) -> None: def install_importhook(config: Config) -> rewrite.AssertionRewritingHook: """Try to install the rewrite hook, raise SystemError if it fails.""" - config._store[assertstate_key] = AssertionState(config, "rewrite") - config._store[assertstate_key].hook = hook = rewrite.AssertionRewritingHook(config) + config.stash[assertstate_key] = AssertionState(config, "rewrite") + config.stash[assertstate_key].hook = hook = rewrite.AssertionRewritingHook(config) sys.meta_path.insert(0, hook) - config._store[assertstate_key].trace("installed rewrite import hook") + config.stash[assertstate_key].trace("installed rewrite import hook") def undo() -> None: - hook = config._store[assertstate_key].hook + hook = config.stash[assertstate_key].hook if hook is not None and hook in sys.meta_path: sys.meta_path.remove(hook) @@ -106,7 +106,7 @@ def pytest_collection(session: "Session") -> None: # This hook is only called when test modules are collected # so for example not in the managing process of pytest-xdist # (which does not collect test modules). - assertstate = session.config._store.get(assertstate_key, None) + assertstate = session.config.stash.get(assertstate_key, None) if assertstate: if assertstate.hook is not None: assertstate.hook.set_session(session) @@ -169,7 +169,7 @@ def call_assertion_pass_hook(lineno: int, orig: str, expl: str) -> None: def pytest_sessionfinish(session: "Session") -> None: - assertstate = session.config._store.get(assertstate_key, None) + assertstate = session.config.stash.get(assertstate_key, None) if assertstate: if assertstate.hook is not None: assertstate.hook.set_session(None) diff --git a/src/_pytest/assertion/rewrite.py b/src/_pytest/assertion/rewrite.py index 3c8ffde4e04..8c85240efe5 100644 --- a/src/_pytest/assertion/rewrite.py +++ b/src/_pytest/assertion/rewrite.py @@ -87,7 +87,7 @@ def find_spec( ) -> Optional[importlib.machinery.ModuleSpec]: if self._writing_pyc: return None - state = self.config._store[assertstate_key] + state = self.config.stash[assertstate_key] if self._early_rewrite_bailout(name, state): return None state.trace("find_module called for: %s" % name) @@ -131,7 +131,7 @@ def exec_module(self, module: types.ModuleType) -> None: assert module.__spec__ is not None assert module.__spec__.origin is not None fn = Path(module.__spec__.origin) - state = self.config._store[assertstate_key] + state = self.config.stash[assertstate_key] self._rewritten_names.add(module.__name__) diff --git a/src/_pytest/faulthandler.py b/src/_pytest/faulthandler.py index fc2471f39c2..aaee307ff2c 100644 --- a/src/_pytest/faulthandler.py +++ b/src/_pytest/faulthandler.py @@ -27,9 +27,9 @@ def pytest_configure(config: Config) -> None: import faulthandler stderr_fd_copy = os.dup(get_stderr_fileno()) - config._store[fault_handler_stderr_key] = open(stderr_fd_copy, "w") - config._store[fault_handler_originally_enabled_key] = faulthandler.is_enabled() - faulthandler.enable(file=config._store[fault_handler_stderr_key]) + config.stash[fault_handler_stderr_key] = open(stderr_fd_copy, "w") + config.stash[fault_handler_originally_enabled_key] = faulthandler.is_enabled() + faulthandler.enable(file=config.stash[fault_handler_stderr_key]) def pytest_unconfigure(config: Config) -> None: @@ -37,10 +37,10 @@ def pytest_unconfigure(config: Config) -> None: faulthandler.disable() # Close the dup file installed during pytest_configure. - if fault_handler_stderr_key in config._store: - config._store[fault_handler_stderr_key].close() - del config._store[fault_handler_stderr_key] - if config._store.get(fault_handler_originally_enabled_key, False): + if fault_handler_stderr_key in config.stash: + config.stash[fault_handler_stderr_key].close() + del config.stash[fault_handler_stderr_key] + if config.stash.get(fault_handler_originally_enabled_key, False): # Re-enable the faulthandler if it was originally enabled. faulthandler.enable(file=get_stderr_fileno()) @@ -67,7 +67,7 @@ def get_timeout_config_value(config: Config) -> float: @pytest.hookimpl(hookwrapper=True, trylast=True) def pytest_runtest_protocol(item: Item) -> Generator[None, None, None]: timeout = get_timeout_config_value(item.config) - stderr = item.config._store[fault_handler_stderr_key] + stderr = item.config.stash[fault_handler_stderr_key] if timeout > 0 and stderr is not None: import faulthandler diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 305ec034879..347d490032a 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -199,7 +199,7 @@ def add_funcarg_pseudo_fixture_def( name2pseudofixturedef = None else: default: Dict[str, FixtureDef[Any]] = {} - name2pseudofixturedef = node._store.setdefault( + name2pseudofixturedef = node.stash.setdefault( name2pseudofixturedef_key, default ) if name2pseudofixturedef is not None and argname in name2pseudofixturedef: diff --git a/src/_pytest/junitxml.py b/src/_pytest/junitxml.py index d7b00cca163..6d13e89a43d 100644 --- a/src/_pytest/junitxml.py +++ b/src/_pytest/junitxml.py @@ -267,7 +267,7 @@ def _warn_incompatibility_with_xunit2( """Emit a PytestWarning about the given fixture being incompatible with newer xunit revisions.""" from _pytest.warning_types import PytestWarning - xml = request.config._store.get(xml_key, None) + xml = request.config.stash.get(xml_key, None) if xml is not None and xml.family not in ("xunit1", "legacy"): request.node.warn( PytestWarning( @@ -322,7 +322,7 @@ def add_attr_noop(name: str, value: object) -> None: attr_func = add_attr_noop - xml = request.config._store.get(xml_key, None) + xml = request.config.stash.get(xml_key, None) if xml is not None: node_reporter = xml.node_reporter(request.node.nodeid) attr_func = node_reporter.add_attribute @@ -370,7 +370,7 @@ def record_func(name: str, value: object) -> None: __tracebackhide__ = True _check_record_param_type("name", name) - xml = request.config._store.get(xml_key, None) + xml = request.config.stash.get(xml_key, None) if xml is not None: record_func = xml.add_global_property # noqa return record_func @@ -428,7 +428,7 @@ def pytest_configure(config: Config) -> None: # Prevent opening xmllog on worker nodes (xdist). if xmlpath and not hasattr(config, "workerinput"): junit_family = config.getini("junit_family") - config._store[xml_key] = LogXML( + config.stash[xml_key] = LogXML( xmlpath, config.option.junitprefix, config.getini("junit_suite_name"), @@ -437,13 +437,13 @@ def pytest_configure(config: Config) -> None: junit_family, config.getini("junit_log_passing_tests"), ) - config.pluginmanager.register(config._store[xml_key]) + config.pluginmanager.register(config.stash[xml_key]) def pytest_unconfigure(config: Config) -> None: - xml = config._store.get(xml_key, None) + xml = config.stash.get(xml_key, None) if xml: - del config._store[xml_key] + del config.stash[xml_key] config.pluginmanager.unregister(xml) diff --git a/src/_pytest/logging.py b/src/_pytest/logging.py index 376a6b31f7e..7ed1820bb31 100644 --- a/src/_pytest/logging.py +++ b/src/_pytest/logging.py @@ -372,7 +372,7 @@ def handler(self) -> LogCaptureHandler: :rtype: LogCaptureHandler """ - return self._item._store[caplog_handler_key] + return self._item.stash[caplog_handler_key] def get_records(self, when: str) -> List[logging.LogRecord]: """Get the logging records for one of the possible test phases. @@ -385,7 +385,7 @@ def get_records(self, when: str) -> List[logging.LogRecord]: .. versionadded:: 3.4 """ - return self._item._store[caplog_records_key].get(when, []) + return self._item.stash[caplog_records_key].get(when, []) @property def text(self) -> str: @@ -694,8 +694,8 @@ def _runtest_for(self, item: nodes.Item, when: str) -> Generator[None, None, Non ) as report_handler: caplog_handler.reset() report_handler.reset() - item._store[caplog_records_key][when] = caplog_handler.records - item._store[caplog_handler_key] = caplog_handler + item.stash[caplog_records_key][when] = caplog_handler.records + item.stash[caplog_handler_key] = caplog_handler yield @@ -707,7 +707,7 @@ def pytest_runtest_setup(self, item: nodes.Item) -> Generator[None, None, None]: self.log_cli_handler.set_when("setup") empty: Dict[str, List[logging.LogRecord]] = {} - item._store[caplog_records_key] = empty + item.stash[caplog_records_key] = empty yield from self._runtest_for(item, "setup") @hookimpl(hookwrapper=True) @@ -721,8 +721,8 @@ def pytest_runtest_teardown(self, item: nodes.Item) -> Generator[None, None, Non self.log_cli_handler.set_when("teardown") yield from self._runtest_for(item, "teardown") - del item._store[caplog_records_key] - del item._store[caplog_handler_key] + del item.stash[caplog_records_key] + del item.stash[caplog_handler_key] @hookimpl def pytest_runtest_logfinish(self) -> None: diff --git a/src/_pytest/mark/__init__.py b/src/_pytest/mark/__init__.py index 877b5ef1c39..05dd1cdc1a6 100644 --- a/src/_pytest/mark/__init__.py +++ b/src/_pytest/mark/__init__.py @@ -269,7 +269,7 @@ def pytest_collection_modifyitems(items: "List[Item]", config: Config) -> None: def pytest_configure(config: Config) -> None: - config._store[old_mark_config_key] = MARK_GEN._config + config.stash[old_mark_config_key] = MARK_GEN._config MARK_GEN._config = config empty_parameterset = config.getini(EMPTY_PARAMETERSET_OPTION) @@ -282,4 +282,4 @@ def pytest_configure(config: Config) -> None: def pytest_unconfigure(config: Config) -> None: - MARK_GEN._config = config._store.get(old_mark_config_key, None) + MARK_GEN._config = config.stash.get(old_mark_config_key, None) diff --git a/src/_pytest/pastebin.py b/src/_pytest/pastebin.py index 1ca7cc4948c..b33383504b0 100644 --- a/src/_pytest/pastebin.py +++ b/src/_pytest/pastebin.py @@ -37,26 +37,26 @@ def pytest_configure(config: Config) -> None: # when using pytest-xdist, for example. if tr is not None: # pastebin file will be UTF-8 encoded binary file. - config._store[pastebinfile_key] = tempfile.TemporaryFile("w+b") + config.stash[pastebinfile_key] = tempfile.TemporaryFile("w+b") oldwrite = tr._tw.write def tee_write(s, **kwargs): oldwrite(s, **kwargs) if isinstance(s, str): s = s.encode("utf-8") - config._store[pastebinfile_key].write(s) + config.stash[pastebinfile_key].write(s) tr._tw.write = tee_write def pytest_unconfigure(config: Config) -> None: - if pastebinfile_key in config._store: - pastebinfile = config._store[pastebinfile_key] + if pastebinfile_key in config.stash: + pastebinfile = config.stash[pastebinfile_key] # Get terminal contents and delete file. pastebinfile.seek(0) sessionlog = pastebinfile.read() pastebinfile.close() - del config._store[pastebinfile_key] + del config.stash[pastebinfile_key] # Undo our patching in the terminal reporter. tr = config.pluginmanager.getplugin("terminalreporter") del tr._tw.__dict__["write"] diff --git a/src/_pytest/skipping.py b/src/_pytest/skipping.py index 18f9a49bc3a..f18716b1431 100644 --- a/src/_pytest/skipping.py +++ b/src/_pytest/skipping.py @@ -237,16 +237,16 @@ def pytest_runtest_setup(item: Item) -> None: if skipped: raise skip.Exception(skipped.reason, _use_item_location=True) - item._store[xfailed_key] = xfailed = evaluate_xfail_marks(item) + item.stash[xfailed_key] = xfailed = evaluate_xfail_marks(item) if xfailed and not item.config.option.runxfail and not xfailed.run: xfail("[NOTRUN] " + xfailed.reason) @hookimpl(hookwrapper=True) def pytest_runtest_call(item: Item) -> Generator[None, None, None]: - xfailed = item._store.get(xfailed_key, None) + xfailed = item.stash.get(xfailed_key, None) if xfailed is None: - item._store[xfailed_key] = xfailed = evaluate_xfail_marks(item) + item.stash[xfailed_key] = xfailed = evaluate_xfail_marks(item) if xfailed and not item.config.option.runxfail and not xfailed.run: xfail("[NOTRUN] " + xfailed.reason) @@ -254,16 +254,16 @@ def pytest_runtest_call(item: Item) -> Generator[None, None, None]: yield # The test run may have added an xfail mark dynamically. - xfailed = item._store.get(xfailed_key, None) + xfailed = item.stash.get(xfailed_key, None) if xfailed is None: - item._store[xfailed_key] = xfailed = evaluate_xfail_marks(item) + item.stash[xfailed_key] = xfailed = evaluate_xfail_marks(item) @hookimpl(hookwrapper=True) def pytest_runtest_makereport(item: Item, call: CallInfo[None]): outcome = yield rep = outcome.get_result() - xfailed = item._store.get(xfailed_key, None) + xfailed = item.stash.get(xfailed_key, None) if item.config.option.runxfail: pass # don't interfere elif call.excinfo and isinstance(call.excinfo.value, xfail.Exception): diff --git a/testing/logging/test_fixture.py b/testing/logging/test_fixture.py index f82df19715b..bcb20de5805 100644 --- a/testing/logging/test_fixture.py +++ b/testing/logging/test_fixture.py @@ -169,7 +169,7 @@ def test_caplog_captures_for_all_stages(caplog, logging_during_setup_and_teardow assert [x.message for x in caplog.get_records("setup")] == ["a_setup_log"] # This reaches into private API, don't use this type of thing in real tests! - assert set(caplog._item._store[caplog_records_key]) == {"setup", "call"} + assert set(caplog._item.stash[caplog_records_key]) == {"setup", "call"} def test_ini_controls_global_log_level(pytester: Pytester) -> None: diff --git a/testing/test_junitxml.py b/testing/test_junitxml.py index 0ded0685a9b..edd1dfde622 100644 --- a/testing/test_junitxml.py +++ b/testing/test_junitxml.py @@ -951,7 +951,7 @@ class FakeConfig: def __init__(self): self.pluginmanager = self self.option = self - self._store = Stash() + self.stash = Stash() def getini(self, name): return "pytest" From 8b5b3575a0113ba348051bfd0bdd4992cdddded6 Mon Sep 17 00:00:00 2001 From: pytest bot Date: Sun, 25 Jul 2021 00:08:36 +0000 Subject: [PATCH 403/630] [automated] Update plugin list --- doc/en/reference/plugin_list.rst | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/doc/en/reference/plugin_list.rst b/doc/en/reference/plugin_list.rst index 3fcde8b7ae6..6dbdf748a41 100644 --- a/doc/en/reference/plugin_list.rst +++ b/doc/en/reference/plugin_list.rst @@ -6,7 +6,7 @@ Plugin List PyPI projects that match "pytest-\*" are considered plugins and are listed automatically. Packages classified as inactive are excluded. -This list contains 896 plugins. +This list contains 897 plugins. ============================================================================================================== ======================================================================================================================================================================== ============== ===================== ================================================ name summary last release status requires @@ -91,7 +91,7 @@ name `pytest-blocker `_ pytest plugin to mark a test as blocker and skip all other tests Sep 07, 2015 4 - Beta N/A `pytest-board `_ Local continuous test runner with pytest and watchdog. Jan 20, 2019 N/A N/A `pytest-bpdb `_ A py.test plug-in to enable drop to bpdb debugger on test failure. Jan 19, 2015 2 - Pre-Alpha N/A -`pytest-bravado `_ Pytest-bravado automatically generates from OpenAPI specification client fixtures. Jul 13, 2021 N/A N/A +`pytest-bravado `_ Pytest-bravado automatically generates from OpenAPI specification client fixtures. Jul 19, 2021 N/A N/A `pytest-breed-adapter `_ A simple plugin to connect with breed-server Nov 07, 2018 4 - Beta pytest (>=3.5.0) `pytest-briefcase `_ A pytest plugin for running tests on a Briefcase project. Jun 14, 2020 4 - Beta pytest (>=3.5.0) `pytest-browser `_ A pytest plugin for console based browser test selection just after the collection phase Dec 10, 2016 3 - Alpha N/A @@ -235,13 +235,13 @@ name `pytest-docker-postgresql `_ A simple plugin to use with pytest Sep 24, 2019 4 - Beta pytest (>=3.5.0) `pytest-docker-py `_ Easy to use, simple to extend, pytest plugin that minimally leverages docker-py. Nov 27, 2018 N/A pytest (==4.0.0) `pytest-docker-registry-fixtures `_ Pytest fixtures for testing with docker registries. Mar 04, 2021 4 - Beta pytest -`pytest-docker-tools `_ Docker integration tests for pytest Jun 03, 2021 4 - Beta pytest (>=6.0.1,<7.0.0) +`pytest-docker-tools `_ Docker integration tests for pytest Jul 23, 2021 4 - Beta pytest (>=6.0.1,<7.0.0) `pytest-docs `_ Documentation tool for pytest Nov 11, 2018 4 - Beta pytest (>=3.5.0) `pytest-docstyle `_ pytest plugin to run pydocstyle Mar 23, 2020 3 - Alpha N/A `pytest-doctest-custom `_ A py.test plugin for customizing string representations of doctest results. Jul 25, 2016 4 - Beta N/A `pytest-doctest-ellipsis-markers `_ Setup additional values for ELLIPSIS_MARKER for doctests Jan 12, 2018 4 - Beta N/A `pytest-doctest-import `_ A simple pytest plugin to import names and add them to the doctest namespace. Nov 13, 2018 4 - Beta pytest (>=3.3.0) -`pytest-doctestplus `_ Pytest plugin with advanced doctest features. Jul 02, 2021 3 - Alpha pytest (>=4.6) +`pytest-doctestplus `_ Pytest plugin with advanced doctest features. Jul 20, 2021 3 - Alpha pytest (>=4.6) `pytest-doctest-ufunc `_ A plugin to run doctests in docstrings of Numpy ufuncs Aug 02, 2020 4 - Beta pytest (>=3.5.0) `pytest-dolphin `_ Some extra stuff that we use ininternally Nov 30, 2016 4 - Beta pytest (==3.0.4) `pytest-doorstop `_ A pytest plugin for adding test results into doorstop items. Jun 09, 2020 4 - Beta pytest (>=3.5.0) @@ -367,7 +367,7 @@ name `pytest-hammertime `_ Display "🔨 " instead of "." for passed pytest tests. Jul 28, 2018 N/A pytest `pytest-harvest `_ Store data created during your pytest tests execution, and retrieve it at the end of the session, e.g. for applicative benchmarking purposes. Apr 01, 2021 5 - Production/Stable N/A `pytest-helm-chart `_ A plugin to provide different types and configs of Kubernetes clusters that can be used for testing. Jun 15, 2020 4 - Beta pytest (>=5.4.2,<6.0.0) -`pytest-helm-charts `_ A plugin to provide different types and configs of Kubernetes clusters that can be used for testing. Dec 22, 2020 4 - Beta pytest (>=6.1.2,<7.0.0) +`pytest-helm-charts `_ A plugin to provide different types and configs of Kubernetes clusters that can be used for testing. Jul 23, 2021 4 - Beta pytest (>=6.1.2,<7.0.0) `pytest-helper `_ Functions to help in using the pytest testing framework May 31, 2019 5 - Production/Stable N/A `pytest-helpers `_ pytest helpers May 17, 2020 N/A pytest `pytest-helpers-namespace `_ Pytest Helpers Namespace Plugin Apr 29, 2021 5 - Production/Stable pytest (>=6.0.0) @@ -411,6 +411,7 @@ name `pytest-instafail `_ pytest plugin to show failures instantly Jun 14, 2020 4 - Beta pytest (>=2.9) `pytest-instrument `_ pytest plugin to instrument tests Apr 05, 2020 5 - Production/Stable pytest (>=5.1.0) `pytest-integration `_ Organizing pytests by integration or not Apr 16, 2020 N/A N/A +`pytest-integration-mark `_ Automatic integration test marking and excluding plugin for pytest Jul 19, 2021 N/A pytest (>=5.2,<7.0) `pytest-interactive `_ A pytest plugin for console based interactive test selection just after the collection phase Nov 30, 2017 3 - Alpha N/A `pytest-intercept-remote `_ Pytest plugin for intercepting outgoing connection requests during pytest run. May 24, 2021 4 - Beta pytest (>=4.6) `pytest-invenio `_ Pytest fixtures for Invenio. May 11, 2021 5 - Production/Stable pytest (<7,>=6) @@ -506,7 +507,7 @@ name `pytest-monkeyplus `_ pytest's monkeypatch subclass with extra functionalities Sep 18, 2012 5 - Production/Stable N/A `pytest-monkeytype `_ pytest-monkeytype: Generate Monkeytype annotations from your pytest tests. Jul 29, 2020 4 - Beta N/A `pytest-moto `_ Fixtures for integration tests of AWS services,uses moto mocking library. Aug 28, 2015 1 - Planning N/A -`pytest-motor `_ A pytest plugin for motor, the non-blocking MongoDB driver. Jul 13, 2021 3 - Alpha pytest +`pytest-motor `_ A pytest plugin for motor, the non-blocking MongoDB driver. Jul 21, 2021 3 - Alpha pytest `pytest-mp `_ A test batcher for multiprocessed Pytest runs May 23, 2018 4 - Beta pytest `pytest-mpi `_ pytest plugin to collect information from tests Mar 14, 2021 3 - Alpha pytest `pytest-mpl `_ pytest plugin to help with testing figures output from Matplotlib Jul 02, 2021 4 - Beta pytest @@ -668,7 +669,7 @@ name `pytest-replay `_ Saves previous test runs and allow re-execute previous pytest runs to reproduce crashes or flaky tests Jun 09, 2021 4 - Beta pytest (>=3.0.0) `pytest-repo-health `_ A pytest plugin to report on repository standards conformance Nov 03, 2020 3 - Alpha pytest `pytest-report `_ Creates json report that is compatible with atom.io's linter message format May 11, 2016 4 - Beta N/A -`pytest-reporter `_ Generate Pytest reports with templates Nov 05, 2020 4 - Beta pytest +`pytest-reporter `_ Generate Pytest reports with templates Jul 22, 2021 4 - Beta pytest `pytest-reporter-html1 `_ A basic HTML report template for Pytest Jun 08, 2021 4 - Beta N/A `pytest-reportinfra `_ Pytest plugin for reportinfra Aug 11, 2019 3 - Alpha N/A `pytest-reporting `_ A plugin to report summarized results in a table format Oct 25, 2019 4 - Beta pytest (>=3.5.0) @@ -708,13 +709,13 @@ name `pytest-sanic `_ a pytest plugin for Sanic May 20, 2021 N/A pytest (>=5.2) `pytest-sanity `_ Dec 07, 2020 N/A N/A `pytest-sa-pg `_ May 14, 2019 N/A N/A -`pytest-sbase `_ A complete web automation framework for end-to-end testing. Jul 12, 2021 5 - Production/Stable N/A +`pytest-sbase `_ A complete web automation framework for end-to-end testing. Jul 20, 2021 5 - Production/Stable N/A `pytest-scenario `_ pytest plugin for test scenarios Feb 06, 2017 3 - Alpha N/A `pytest-schema `_ 👍 Validate return values against a schema-like object in testing Aug 31, 2020 5 - Production/Stable pytest (>=3.5.0) `pytest-securestore `_ An encrypted password store for use within pytest cases Jun 19, 2019 4 - Beta N/A `pytest-select `_ A pytest plugin which allows to (de-)select tests from a file. Jan 18, 2019 3 - Alpha pytest (>=3.0) `pytest-selenium `_ pytest plugin for Selenium Sep 19, 2020 5 - Production/Stable pytest (>=5.0.0) -`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Jul 12, 2021 5 - Production/Stable N/A +`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Jul 20, 2021 5 - Production/Stable N/A `pytest-selenium-enhancer `_ pytest plugin for Selenium Nov 26, 2020 5 - Production/Stable N/A `pytest-selenium-pdiff `_ A pytest package implementing perceptualdiff for Selenium tests. Apr 06, 2017 2 - Pre-Alpha N/A `pytest-send-email `_ Send pytest execution result email Dec 04, 2019 N/A N/A @@ -757,7 +758,7 @@ name `pytest-sphinx `_ Doctest plugin for pytest with support for Sphinx-specific doctest-directives Aug 05, 2020 4 - Beta N/A `pytest-spiratest `_ Exports unit tests as test runs in SpiraTest/Team/Plan Apr 28, 2021 N/A N/A `pytest-splinter `_ Splinter plugin for pytest testing framework Dec 25, 2020 6 - Mature N/A -`pytest-split `_ Pytest plugin for splitting test suite based on test execution time Jun 17, 2021 4 - Beta pytest (>=5,<7) +`pytest-split `_ Pytest plugin for splitting test suite based on test execution time Jul 21, 2021 4 - Beta pytest (>=5,<7) `pytest-splitio `_ Split.io SDK integration for e2e tests Sep 22, 2020 N/A pytest (<7,>=5.0) `pytest-split-tests `_ A Pytest plugin for running a subset of your tests by splitting them in to equally sized groups. Forked from Mark Adams' original project pytest-test-groups. May 28, 2019 N/A pytest (>=2.5) `pytest-split-tests-tresorit `_ Feb 22, 2021 1 - Planning N/A @@ -849,7 +850,7 @@ name `pytest-trio `_ Pytest plugin for trio Oct 16, 2020 N/A N/A `pytest-tspwplib `_ A simple plugin to use with tspwplib Jan 08, 2021 4 - Beta pytest (>=3.5.0) `pytest-tstcls `_ Test Class Base Mar 23, 2020 5 - Production/Stable N/A -`pytest-twisted `_ A twisted plugin for pytest. Sep 11, 2020 5 - Production/Stable pytest (>=2.3) +`pytest-twisted `_ A twisted plugin for pytest. Jul 22, 2021 5 - Production/Stable pytest (>=2.3) `pytest-typhoon-xray `_ Typhoon HIL plugin for pytest Jun 22, 2020 4 - Beta pytest (>=5.4.2) `pytest-tytest `_ Typhoon HIL plugin for pytest May 25, 2020 4 - Beta pytest (>=5.4.2) `pytest-ubersmith `_ Easily mock calls to ubersmith at the `requests` level. Apr 13, 2015 N/A N/A From de8ad539631a8929e119f90c9413073126119a3c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Jul 2021 03:01:26 +0000 Subject: [PATCH 404/630] build(deps): bump pytest-twisted in /testing/plugins_integration Bumps [pytest-twisted](https://github.com/pytest-dev/pytest-twisted) from 1.13.2 to 1.13.3. - [Release notes](https://github.com/pytest-dev/pytest-twisted/releases) - [Commits](https://github.com/pytest-dev/pytest-twisted/compare/v1.13.2...v1.13.3) --- updated-dependencies: - dependency-name: pytest-twisted dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- testing/plugins_integration/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/plugins_integration/requirements.txt b/testing/plugins_integration/requirements.txt index 0aa152ad090..a9409d224c9 100644 --- a/testing/plugins_integration/requirements.txt +++ b/testing/plugins_integration/requirements.txt @@ -10,6 +10,6 @@ pytest-mock==3.6.1 pytest-rerunfailures==10.1 pytest-sugar==0.9.4 pytest-trio==0.7.0 -pytest-twisted==1.13.2 +pytest-twisted==1.13.3 twisted==21.2.0 pytest-xvfb==2.0.0 From b35a3de00651acb2196ac92546f356a74b3142bf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 26 Jul 2021 03:01:29 +0000 Subject: [PATCH 405/630] build(deps): bump anyio[curio,trio] in /testing/plugins_integration Bumps [anyio[curio,trio]](https://github.com/agronholm/anyio) from 3.2.1 to 3.3.0. - [Release notes](https://github.com/agronholm/anyio/releases) - [Changelog](https://github.com/agronholm/anyio/blob/master/docs/versionhistory.rst) - [Commits](https://github.com/agronholm/anyio/compare/3.2.1...3.3.0) --- updated-dependencies: - dependency-name: anyio[curio,trio] dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- testing/plugins_integration/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/plugins_integration/requirements.txt b/testing/plugins_integration/requirements.txt index 0aa152ad090..d2bc76dfa59 100644 --- a/testing/plugins_integration/requirements.txt +++ b/testing/plugins_integration/requirements.txt @@ -1,4 +1,4 @@ -anyio[curio,trio]==3.2.1 +anyio[curio,trio]==3.3.0 django==3.2.5 pytest-asyncio==0.15.1 pytest-bdd==4.1.0 From 1d848314d03ca801069117ad545439e5d818b2ba Mon Sep 17 00:00:00 2001 From: Simon K Date: Mon, 26 Jul 2021 11:48:15 +0100 Subject: [PATCH 406/630] some small quality of life changes (#8454) Co-authored-by: Bruno Oliveira --- src/_pytest/mark/__init__.py | 29 +++++++++++++---------------- src/_pytest/mark/structures.py | 31 ++++++++++++++----------------- 2 files changed, 27 insertions(+), 33 deletions(-) diff --git a/src/_pytest/mark/__init__.py b/src/_pytest/mark/__init__.py index 0dc3718e89b..0cefe47c371 100644 --- a/src/_pytest/mark/__init__.py +++ b/src/_pytest/mark/__init__.py @@ -200,17 +200,12 @@ def deselect_by_keyword(items: "List[Item]", config: Config) -> None: selectuntil = True keywordexpr = keywordexpr[:-1] - try: - expression = Expression.compile(keywordexpr) - except ParseError as e: - raise UsageError( - f"Wrong expression passed to '-k': {keywordexpr}: {e}" - ) from None + expr = _parse_expression(keywordexpr, "Wrong expression passed to '-k'") remaining = [] deselected = [] for colitem in items: - if keywordexpr and not expression.evaluate(KeywordMatcher.from_item(colitem)): + if keywordexpr and not expr.evaluate(KeywordMatcher.from_item(colitem)): deselected.append(colitem) else: if selectuntil: @@ -245,24 +240,26 @@ def deselect_by_mark(items: "List[Item]", config: Config) -> None: if not matchexpr: return - try: - expression = Expression.compile(matchexpr) - except ParseError as e: - raise UsageError(f"Wrong expression passed to '-m': {matchexpr}: {e}") from None - - remaining = [] - deselected = [] + expr = _parse_expression(matchexpr, "Wrong expression passed to '-m'") + remaining: List[Item] = [] + deselected: List[Item] = [] for item in items: - if expression.evaluate(MarkMatcher.from_item(item)): + if expr.evaluate(MarkMatcher.from_item(item)): remaining.append(item) else: deselected.append(item) - if deselected: config.hook.pytest_deselected(items=deselected) items[:] = remaining +def _parse_expression(expr: str, exc_message: str) -> Expression: + try: + return Expression.compile(expr) + except ParseError as e: + raise UsageError(f"{exc_message}: {expr}: {e}") from None + + def pytest_collection_modifyitems(items: "List[Item]", config: Config) -> None: deselect_by_keyword(items, config) deselect_by_mark(items, config) diff --git a/src/_pytest/mark/structures.py b/src/_pytest/mark/structures.py index d7f0ffec5a4..e40a605bdc5 100644 --- a/src/_pytest/mark/structures.py +++ b/src/_pytest/mark/structures.py @@ -40,10 +40,7 @@ def istestfunc(func) -> bool: - return ( - hasattr(func, "__call__") - and getattr(func, "__name__", "") != "" - ) + return callable(func) and getattr(func, "__name__", "") != "" def get_empty_parameterset_mark( @@ -332,9 +329,6 @@ def markname(self) -> str: """:meta private:""" return self.name # for backward-compat (2.4.1 had this attr) - def __repr__(self) -> str: - return f"" - def with_args(self, *args: object, **kwargs: object) -> "MarkDecorator": """Return a MarkDecorator with extra arguments added. @@ -375,18 +369,21 @@ def get_unpacked_marks(obj) -> List[Mark]: def normalize_mark_list(mark_list: Iterable[Union[Mark, MarkDecorator]]) -> List[Mark]: - """Normalize marker decorating helpers to mark objects. + """ + Normalize an iterable of Mark or MarkDecorator objects into a list of marks + by retrieving the `mark` attribute on MarkDecorator instances. - :type List[Union[Mark, Markdecorator]] mark_list: - :rtype: List[Mark] + :param mark_list: marks to normalize + :returns: A new list of the extracted Mark objects """ - extracted = [ - getattr(mark, "mark", mark) for mark in mark_list - ] # unpack MarkDecorator - for mark in extracted: - if not isinstance(mark, Mark): - raise TypeError(f"got {mark!r} instead of Mark") - return [x for x in extracted if isinstance(x, Mark)] + + def parse_mark(mark: Union[Mark, MarkDecorator]) -> Mark: + mark_obj = getattr(mark, "mark", mark) + if not isinstance(mark_obj, Mark): + raise TypeError(f"got {repr(mark_obj)} instead of Mark") + return mark_obj + + return [parse_mark(x) for x in mark_list] def store_mark(obj, mark: Mark) -> None: From 15989ddc8f20e49602858e6e4a2631b71e8b9ef1 Mon Sep 17 00:00:00 2001 From: Emmanuel Arias Date: Mon, 26 Jul 2021 12:14:52 -0300 Subject: [PATCH 407/630] Fix Dead link in README for 'assert statements' Closes: #8926 --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 8b2011fab0b..453c0a4c755 100644 --- a/README.rst +++ b/README.rst @@ -79,7 +79,7 @@ Due to ``pytest``'s detailed assertion introspection, only plain ``assert`` stat Features -------- -- Detailed info on failing `assert statements `_ (no need to remember ``self.assert*`` names) +- Detailed info on failing `assert statements `_ (no need to remember ``self.assert*`` names) - `Auto-discovery `_ From 237bdba061275fc763b68a172fad8616a94bb49b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 26 Jul 2021 18:05:01 +0000 Subject: [PATCH 408/630] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/reorder_python_imports: v2.5.0 → v2.6.0](https://github.com/asottile/reorder_python_imports/compare/v2.5.0...v2.6.0) - [github.com/asottile/pyupgrade: v2.21.2 → v2.23.0](https://github.com/asottile/pyupgrade/compare/v2.21.2...v2.23.0) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6ab5f1d4a22..d97e2babe35 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -29,12 +29,12 @@ repos: - flake8-typing-imports==1.9.0 - flake8-docstrings==1.5.0 - repo: https://github.com/asottile/reorder_python_imports - rev: v2.5.0 + rev: v2.6.0 hooks: - id: reorder-python-imports args: ['--application-directories=.:src', --py36-plus] - repo: https://github.com/asottile/pyupgrade - rev: v2.21.2 + rev: v2.23.0 hooks: - id: pyupgrade args: [--py36-plus] From 6d6bc97231f2d9a68002f1d191828fd3476ca8b8 Mon Sep 17 00:00:00 2001 From: Simon K Date: Tue, 27 Jul 2021 22:50:26 +0100 Subject: [PATCH 409/630] #7124: Fix `--doctest-modules` crashing when `__main__.py` is present (#8949) * Fix ``--doctest-modules`` crashing when ``__main__.py`` is present --- changelog/7124.bugfix.rst | 1 + src/_pytest/doctest.py | 8 +++++++- testing/example_scripts/__init__.py | 0 .../example_scripts/doctest/main_py/__main__.py | 2 ++ .../doctest/main_py/test_normal_module.py | 6 ++++++ testing/test_doctest.py | 14 ++++++++++++++ 6 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 changelog/7124.bugfix.rst create mode 100644 testing/example_scripts/__init__.py create mode 100644 testing/example_scripts/doctest/main_py/__main__.py create mode 100644 testing/example_scripts/doctest/main_py/test_normal_module.py diff --git a/changelog/7124.bugfix.rst b/changelog/7124.bugfix.rst new file mode 100644 index 00000000000..191925d7a24 --- /dev/null +++ b/changelog/7124.bugfix.rst @@ -0,0 +1 @@ +Fixed an issue where ``__main__.py`` would raise an ``ImportError`` when ``--doctest-modules`` was provided. diff --git a/src/_pytest/doctest.py b/src/_pytest/doctest.py index 870920f5a0d..f799f5f9c3e 100644 --- a/src/_pytest/doctest.py +++ b/src/_pytest/doctest.py @@ -125,7 +125,9 @@ def pytest_collect_file( ) -> Optional[Union["DoctestModule", "DoctestTextfile"]]: config = parent.config if fspath.suffix == ".py": - if config.option.doctestmodules and not _is_setup_py(fspath): + if config.option.doctestmodules and not any( + (_is_setup_py(fspath), _is_main_py(fspath)) + ): mod: DoctestModule = DoctestModule.from_parent(parent, path=fspath) return mod elif _is_doctest(config, fspath, parent): @@ -148,6 +150,10 @@ def _is_doctest(config: Config, path: Path, parent: Collector) -> bool: return any(fnmatch_ex(glob, path) for glob in globs) +def _is_main_py(path: Path) -> bool: + return path.name == "__main__.py" + + class ReprFailDoctest(TerminalRepr): def __init__( self, reprlocation_lines: Sequence[Tuple[ReprFileLocation, Sequence[str]]] diff --git a/testing/example_scripts/__init__.py b/testing/example_scripts/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/testing/example_scripts/doctest/main_py/__main__.py b/testing/example_scripts/doctest/main_py/__main__.py new file mode 100644 index 00000000000..e471d06d643 --- /dev/null +++ b/testing/example_scripts/doctest/main_py/__main__.py @@ -0,0 +1,2 @@ +def test_this_is_ignored(): + assert True diff --git a/testing/example_scripts/doctest/main_py/test_normal_module.py b/testing/example_scripts/doctest/main_py/test_normal_module.py new file mode 100644 index 00000000000..700cc9750cf --- /dev/null +++ b/testing/example_scripts/doctest/main_py/test_normal_module.py @@ -0,0 +1,6 @@ +def test_doc(): + """ + >>> 10 > 5 + True + """ + assert False diff --git a/testing/test_doctest.py b/testing/test_doctest.py index 1d33e737830..47b3d6217ed 100644 --- a/testing/test_doctest.py +++ b/testing/test_doctest.py @@ -6,6 +6,7 @@ import pytest from _pytest.doctest import _get_checker +from _pytest.doctest import _is_main_py from _pytest.doctest import _is_mocked from _pytest.doctest import _is_setup_py from _pytest.doctest import _patch_unwrap_mock_aware @@ -811,6 +812,11 @@ def test_valid_setup_py(self, pytester: Pytester): result = pytester.runpytest(p, "--doctest-modules") result.stdout.fnmatch_lines(["*collected 0 items*"]) + def test_main_py_does_not_cause_import_errors(self, pytester: Pytester): + p = pytester.copy_example("doctest/main_py") + result = pytester.runpytest(p, "--doctest-modules") + result.stdout.fnmatch_lines(["*collected 2 items*", "*1 failed, 1 passed*"]) + def test_invalid_setup_py(self, pytester: Pytester): """ Test to make sure that pytest reads setup.py files that are not used @@ -1518,3 +1524,11 @@ def test_is_setup_py_different_encoding(tmp_path: Path, mod: str) -> None: ) setup_py.write_bytes(contents.encode("cp1252")) assert _is_setup_py(setup_py) + + +@pytest.mark.parametrize( + "name, expected", [("__main__.py", True), ("__init__.py", False)] +) +def test_is_main_py(tmp_path: Path, name: str, expected: bool) -> None: + dunder_main = tmp_path.joinpath(name) + assert _is_main_py(dunder_main) == expected From d5c62d03269f2416f26d7740b3417007c5057fe2 Mon Sep 17 00:00:00 2001 From: Simon K Date: Fri, 30 Jul 2021 21:37:38 +0100 Subject: [PATCH 410/630] Making `--debug` more configurable for the pytest user (#8955) Close #8954 --- .gitignore | 3 +++ changelog/8954.feature.rst | 1 + src/_pytest/helpconfig.py | 18 ++++++++----- testing/test_config.py | 52 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 68 insertions(+), 6 deletions(-) create mode 100644 changelog/8954.feature.rst diff --git a/.gitignore b/.gitignore index 23e7c996688..935da3b9a2e 100644 --- a/.gitignore +++ b/.gitignore @@ -53,3 +53,6 @@ coverage.xml # generated by pip pip-wheel-metadata/ + +# pytest debug logs generated via --debug +pytestdebug.log diff --git a/changelog/8954.feature.rst b/changelog/8954.feature.rst new file mode 100644 index 00000000000..7edb430075e --- /dev/null +++ b/changelog/8954.feature.rst @@ -0,0 +1 @@ +``--debug`` flag now accepts a :class:`str` file to route debug logs into, remains defaulted to `pytestdebug.log`. diff --git a/src/_pytest/helpconfig.py b/src/_pytest/helpconfig.py index 78da45f0c3e..e3739ce932d 100644 --- a/src/_pytest/helpconfig.py +++ b/src/_pytest/helpconfig.py @@ -80,10 +80,14 @@ def pytest_addoption(parser: Parser) -> None: ) group.addoption( "--debug", - action="store_true", + action="store", + nargs="?", + const="pytestdebug.log", dest="debug", - default=False, - help="store internal tracing debug information in 'pytestdebug.log'.", + metavar="DEBUG_FILE_NAME", + help="store internal tracing debug information in this log file.\n" + "This file is opened with 'w' and truncated as a result, care advised.\n" + "Defaults to 'pytestdebug.log'.", ) group._addoption( "-o", @@ -98,8 +102,10 @@ def pytest_addoption(parser: Parser) -> None: def pytest_cmdline_parse(): outcome = yield config: Config = outcome.get_result() + if config.option.debug: - path = os.path.abspath("pytestdebug.log") + # --debug | --debug was provided. + path = config.option.debug debugfile = open(path, "w") debugfile.write( "versions pytest-%s, py-%s, " @@ -114,11 +120,11 @@ def pytest_cmdline_parse(): ) config.trace.root.setwriter(debugfile.write) undo_tracing = config.pluginmanager.enable_tracing() - sys.stderr.write("writing pytestdebug information to %s\n" % path) + sys.stderr.write("writing pytest debug information to %s\n" % path) def unset_tracing() -> None: debugfile.close() - sys.stderr.write("wrote pytestdebug information to %s\n" % debugfile.name) + sys.stderr.write("wrote pytest debug information to %s\n" % debugfile.name) config.trace.root.setwriter(None) undo_tracing() diff --git a/testing/test_config.py b/testing/test_config.py index 1fdcea9bff0..996a332a064 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -2042,3 +2042,55 @@ def test_parse_warning_filter_failure(arg: str) -> None: with pytest.raises(warnings._OptionError): parse_warning_filter(arg, escape=True) + + +class TestDebugOptions: + def test_without_debug_does_not_write_log(self, pytester: Pytester) -> None: + result = pytester.runpytest() + result.stderr.no_fnmatch_line( + "*writing pytest debug information to*pytestdebug.log" + ) + result.stderr.no_fnmatch_line( + "*wrote pytest debug information to*pytestdebug.log" + ) + assert not [f.name for f in pytester.path.glob("**/*.log")] + + def test_with_only_debug_writes_pytestdebug_log(self, pytester: Pytester) -> None: + result = pytester.runpytest("--debug") + result.stderr.fnmatch_lines( + [ + "*writing pytest debug information to*pytestdebug.log", + "*wrote pytest debug information to*pytestdebug.log", + ] + ) + assert "pytestdebug.log" in [f.name for f in pytester.path.glob("**/*.log")] + + def test_multiple_custom_debug_logs(self, pytester: Pytester) -> None: + result = pytester.runpytest("--debug", "bar.log") + result.stderr.fnmatch_lines( + [ + "*writing pytest debug information to*bar.log", + "*wrote pytest debug information to*bar.log", + ] + ) + result = pytester.runpytest("--debug", "foo.log") + result.stderr.fnmatch_lines( + [ + "*writing pytest debug information to*foo.log", + "*wrote pytest debug information to*foo.log", + ] + ) + + assert {"bar.log", "foo.log"} == { + f.name for f in pytester.path.glob("**/*.log") + } + + def test_debug_help(self, pytester: Pytester) -> None: + result = pytester.runpytest("-h") + result.stdout.fnmatch_lines( + [ + "*store internal tracing debug information in this log*", + "*This file is opened with 'w' and truncated as a result*", + "*Defaults to 'pytestdebug.log'.", + ] + ) From a9a934e8ceb74aa149018c842fdcbc85e7904e1c Mon Sep 17 00:00:00 2001 From: symonk Date: Sat, 31 Jul 2021 01:49:53 +0100 Subject: [PATCH 411/630] fix minor typo in writing hook functions documentation --- doc/en/how-to/writing_hook_functions.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/en/how-to/writing_hook_functions.rst b/doc/en/how-to/writing_hook_functions.rst index de2ff3bdc10..ac038cd6325 100644 --- a/doc/en/how-to/writing_hook_functions.rst +++ b/doc/en/how-to/writing_hook_functions.rst @@ -261,9 +261,9 @@ and use pytest_addoption as follows: def pytest_addhooks(pluginmanager): """ This example assumes the hooks are grouped in the 'hooks' module. """ - from . import hook + from . import hooks - pluginmanager.add_hookspecs(hook) + pluginmanager.add_hookspecs(hooks) def pytest_addoption(parser, pluginmanager): From 1b9ba65b3f236bbcfdb24063f8cddcf77535877c Mon Sep 17 00:00:00 2001 From: symonk Date: Sat, 31 Jul 2021 14:00:36 +0100 Subject: [PATCH 412/630] typeshed `os.PathLike` is now `@runtime_checkable`, remove mypy ignore --- src/_pytest/config/__init__.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 18423a47ef0..0af41215bb3 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -305,9 +305,7 @@ def _prepareconfig( ) -> "Config": if args is None: args = sys.argv[1:] - # TODO: Remove type-ignore after next mypy release. - # https://github.com/python/typeshed/commit/076983eec45e739c68551cb6119fd7d85fd4afa9 - elif isinstance(args, os.PathLike): # type: ignore[misc] + elif isinstance(args, os.PathLike): args = [os.fspath(args)] elif not isinstance(args, list): msg = "`args` parameter expected to be a list of strings, got: {!r} (type: {})" From 2834b39b65bd97893d35bd138a5071a4dfd52676 Mon Sep 17 00:00:00 2001 From: Emmanuel Arias Date: Sat, 31 Jul 2021 10:26:25 -0300 Subject: [PATCH 413/630] Add examples of parametrizing classes and all tests in a module Closes #8947 PR #8962 --- AUTHORS | 1 + doc/en/how-to/parametrize.rst | 36 ++++++++++++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index d6004f33da5..f68a750954c 100644 --- a/AUTHORS +++ b/AUTHORS @@ -36,6 +36,7 @@ Anton Grinevich Anton Lodder Antony Lee Arel Cordero +Arias Emmanuel Ariel Pillemer Armin Rigo Aron Coyle diff --git a/doc/en/how-to/parametrize.rst b/doc/en/how-to/parametrize.rst index 7f5f6ea0cd8..6c2bac4ea23 100644 --- a/doc/en/how-to/parametrize.rst +++ b/doc/en/how-to/parametrize.rst @@ -110,7 +110,41 @@ the simple test function. And as usual with test function arguments, you can see the ``input`` and ``output`` values in the traceback. Note that you could also use the parametrize marker on a class or a module -(see :ref:`mark`) which would invoke several functions with the argument sets. +(see :ref:`mark`) which would invoke several functions with the argument sets, +for instance: + + +.. code-block:: python + + import pytest + + + @pytest.mark.parametrize("n,expected", [(1, 2), (3, 4)]) + class TestClass: + def test_simple_case(self, n, expected): + assert n + 1 == expected + + def test_weird_simple_case(self, n, expected): + assert (n * 1) + 1 == expected + + +To parametrize all tests in a module, you can assign to the :globalvar:`pytestmark` global variable: + + +.. code-block:: python + + import pytest + + pytestmark = pytest.mark.parametrize("n,expected", [(1, 2), (3, 4)]) + + + class TestClass: + def test_simple_case(self, n, expected): + assert n + 1 == expected + + def test_weird_simple_case(self, n, expected): + assert (n * 1) + 1 == expected + It is also possible to mark individual test instances within parametrize, for example with the builtin ``mark.xfail``: From c27db3bd8e2f336747196ecaf72b153a160da21d Mon Sep 17 00:00:00 2001 From: Simon K Date: Sat, 31 Jul 2021 14:53:43 +0100 Subject: [PATCH 414/630] Deprecate pytest_cmdline_preparse Close #8592 PR #8956 --- changelog/8592.deprecation.rst | 1 + doc/en/deprecations.rst | 23 +++++++++++++++++++++++ src/_pytest/deprecated.py | 5 +++++ src/_pytest/hookspec.py | 2 ++ testing/deprecated_test.py | 17 +++++++++++++++++ 5 files changed, 48 insertions(+) create mode 100644 changelog/8592.deprecation.rst diff --git a/changelog/8592.deprecation.rst b/changelog/8592.deprecation.rst new file mode 100644 index 00000000000..51b7a6b0d1e --- /dev/null +++ b/changelog/8592.deprecation.rst @@ -0,0 +1 @@ +:func:`pytest_cmdline_preparse <_pytest.hookspec.pytest_cmdline_preparse>` has been officially deprecated. It will be removed in a future release. Use :func:`pytest_load_initial_conftests <_pytest.hookspec.pytest_load_initial_conftests>` instead. diff --git a/doc/en/deprecations.rst b/doc/en/deprecations.rst index 8136c60ae3e..95c2a88698d 100644 --- a/doc/en/deprecations.rst +++ b/doc/en/deprecations.rst @@ -33,6 +33,29 @@ In order to support the transition to :mod:`pathlib`, the following hooks now re The accompanying ``py.path.local`` based paths have been deprecated: plugins which manually invoke those hooks should only pass the new ``pathlib.Path`` arguments, and users should change their hook implementations to use the new ``pathlib.Path`` arguments. +Implementing the ``pytest_cmdline_preparse`` hook +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. deprecated:: 7.0 + +Implementing the :func:`pytest_cmdline_preparse <_pytest.hookspec.pytest_cmdline_preparse>` hook has been officially deprecated. +Implement the :func:`pytest_load_initial_conftests <_pytest.hookspec.pytest_load_initial_conftests>` hook instead. + +.. code-block:: python + + def pytest_cmdline_preparse(config: Config, args: List[str]) -> None: + ... + + + # becomes: + + + def pytest_load_initial_conftests( + early_config: Config, parser: Parser, args: List[str] + ) -> None: + ... + + Diamond inheritance between :class:`pytest.File` and :class:`pytest.Item` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/src/_pytest/deprecated.py b/src/_pytest/deprecated.py index e5fbc0129d0..0c5db6d5335 100644 --- a/src/_pytest/deprecated.py +++ b/src/_pytest/deprecated.py @@ -53,6 +53,11 @@ "Please use pytest_warning_recorded instead." ) +WARNING_CMDLINE_PREPARSE_HOOK = PytestDeprecationWarning( + "The pytest_cmdline_preparse hook is deprecated and will be removed in a future release. \n" + "Please use pytest_load_initial_conftests hook instead." +) + FSCOLLECTOR_GETHOOKPROXY_ISINITPATH = PytestDeprecationWarning( "The gethookproxy() and isinitpath() methods of FSCollector and Package are deprecated; " "use self.session.gethookproxy() and self.session.isinitpath() instead. " diff --git a/src/_pytest/hookspec.py b/src/_pytest/hookspec.py index e8fcc3d7372..4fe3cf4b46b 100644 --- a/src/_pytest/hookspec.py +++ b/src/_pytest/hookspec.py @@ -14,6 +14,7 @@ from pluggy import HookspecMarker from _pytest.deprecated import WARNING_CAPTURED_HOOK +from _pytest.deprecated import WARNING_CMDLINE_PREPARSE_HOOK if TYPE_CHECKING: import pdb @@ -157,6 +158,7 @@ def pytest_cmdline_parse( """ +@hookspec(warn_on_impl=WARNING_CMDLINE_PREPARSE_HOOK) def pytest_cmdline_preparse(config: "Config", args: List[str]) -> None: """(**Deprecated**) modify command line arguments before option parsing. diff --git a/testing/deprecated_test.py b/testing/deprecated_test.py index 479f1f26df8..0a5adac562b 100644 --- a/testing/deprecated_test.py +++ b/testing/deprecated_test.py @@ -190,3 +190,20 @@ def test_warns_none_is_deprecated(): ): with pytest.warns(None): # type: ignore[call-overload] pass + + +def test_deprecation_of_cmdline_preparse(pytester: Pytester) -> None: + pytester.makeconftest( + """ + def pytest_cmdline_preparse(config, args): + ... + + """ + ) + result = pytester.runpytest() + result.stdout.fnmatch_lines( + [ + "*PytestDeprecationWarning: The pytest_cmdline_preparse hook is deprecated*", + "*Please use pytest_load_initial_conftests hook instead.*", + ] + ) From ef5d81ad5c37bc06a6365dc61377c18f1001f9de Mon Sep 17 00:00:00 2001 From: Simon K Date: Sat, 31 Jul 2021 15:25:10 +0100 Subject: [PATCH 415/630] add `assert_outcomes(warnings=)` functionality to `RunResult` * expose `warnings=` to pytester `assert_outcomes()` * fix test fallout from adding warnings= to assert_outcomes() * #closes 8593 - Improve test and add a `changelog` entry for the change --- changelog/8953.feature.rst | 2 ++ src/_pytest/pytester.py | 2 ++ src/_pytest/pytester_assertions.py | 3 +++ testing/test_nose.py | 2 +- testing/test_pytester.py | 14 ++++++++++++++ 5 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 changelog/8953.feature.rst diff --git a/changelog/8953.feature.rst b/changelog/8953.feature.rst new file mode 100644 index 00000000000..aa60fa19ca2 --- /dev/null +++ b/changelog/8953.feature.rst @@ -0,0 +1,2 @@ +:class:`RunResult <_pytest.pytester.RunResult>` method :meth:`assert_outcomes <_pytest.pytester.RunResult.assert_outcomes>` now accepts a +``warnings`` argument to assert the total number of warnings captured. diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index af73b63920b..c8258d4b694 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -588,6 +588,7 @@ def assert_outcomes( errors: int = 0, xpassed: int = 0, xfailed: int = 0, + warnings: int = 0, ) -> None: """Assert that the specified outcomes appear with the respective numbers (0 means it didn't occur) in the text output from a test run.""" @@ -603,6 +604,7 @@ def assert_outcomes( errors=errors, xpassed=xpassed, xfailed=xfailed, + warnings=warnings, ) diff --git a/src/_pytest/pytester_assertions.py b/src/_pytest/pytester_assertions.py index 630c1d3331c..45aa41c5a94 100644 --- a/src/_pytest/pytester_assertions.py +++ b/src/_pytest/pytester_assertions.py @@ -42,6 +42,7 @@ def assert_outcomes( errors: int = 0, xpassed: int = 0, xfailed: int = 0, + warnings: int = 0, ) -> None: """Assert that the specified outcomes appear with the respective numbers (0 means it didn't occur) in the text output from a test run.""" @@ -54,6 +55,7 @@ def assert_outcomes( "errors": outcomes.get("errors", 0), "xpassed": outcomes.get("xpassed", 0), "xfailed": outcomes.get("xfailed", 0), + "warnings": outcomes.get("warnings", 0), } expected = { "passed": passed, @@ -62,5 +64,6 @@ def assert_outcomes( "errors": errors, "xpassed": xpassed, "xfailed": xfailed, + "warnings": warnings, } assert obtained == expected diff --git a/testing/test_nose.py b/testing/test_nose.py index 77f79b53b3c..3b3d3d2f5fb 100644 --- a/testing/test_nose.py +++ b/testing/test_nose.py @@ -335,7 +335,7 @@ def test_failing(): """ ) result = pytester.runpytest(p) - result.assert_outcomes(skipped=1) + result.assert_outcomes(skipped=1, warnings=1) def test_SkipTest_in_test(pytester: Pytester) -> None: diff --git a/testing/test_pytester.py b/testing/test_pytester.py index 7b16c69c23d..b0a94223c33 100644 --- a/testing/test_pytester.py +++ b/testing/test_pytester.py @@ -847,3 +847,17 @@ def test_testdir_makefile_ext_empty_string_makes_file(testdir) -> None: """For backwards compat #8192""" p1 = testdir.makefile("", "") assert "test_testdir_makefile" in str(p1) + + +@pytest.mark.filterwarnings("default") +def test_pytester_assert_outcomes_warnings(pytester: Pytester) -> None: + pytester.makepyfile( + """ + import warnings + + def test_with_warning(): + warnings.warn(UserWarning("some custom warning")) + """ + ) + result = pytester.runpytest() + result.assert_outcomes(passed=1, warnings=1) From fed18fb76d9f6c07be3e0b44268bd136e185bd84 Mon Sep 17 00:00:00 2001 From: pytest bot Date: Sun, 1 Aug 2021 00:10:10 +0000 Subject: [PATCH 416/630] [automated] Update plugin list --- doc/en/reference/plugin_list.rst | 39 +++++++++++++++++--------------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/doc/en/reference/plugin_list.rst b/doc/en/reference/plugin_list.rst index 6dbdf748a41..1972a97ec06 100644 --- a/doc/en/reference/plugin_list.rst +++ b/doc/en/reference/plugin_list.rst @@ -6,7 +6,7 @@ Plugin List PyPI projects that match "pytest-\*" are considered plugins and are listed automatically. Packages classified as inactive are excluded. -This list contains 897 plugins. +This list contains 900 plugins. ============================================================================================================== ======================================================================================================================================================================== ============== ===================== ================================================ name summary last release status requires @@ -20,7 +20,7 @@ name `pytest-aiofiles `_ pytest fixtures for writing aiofiles tests with pyfakefs May 14, 2017 5 - Production/Stable N/A `pytest-aiohttp `_ pytest plugin for aiohttp support Dec 05, 2017 N/A pytest `pytest-aiohttp-client `_ Pytest `client` fixture for the Aiohttp Nov 01, 2020 N/A pytest (>=6) -`pytest-aioresponses `_ py.test integration for aioresponses Dec 21, 2020 4 - Beta pytest (>=3.5.0) +`pytest-aioresponses `_ py.test integration for aioresponses Jul 29, 2021 4 - Beta pytest (>=3.5.0) `pytest-aioworkers `_ A plugin to test aioworkers project with pytest Dec 04, 2019 4 - Beta pytest (>=3.5.0) `pytest-airflow `_ pytest support for airflow. Apr 03, 2019 3 - Alpha pytest (>=4.4.0) `pytest-alembic `_ A pytest plugin for verifying alembic migrations. May 10, 2021 N/A pytest (>=1.0) @@ -58,11 +58,12 @@ name `pytest-asyncio-cooperative `_ Run all your asynchronous tests cooperatively. Jun 25, 2021 4 - Beta N/A `pytest-asyncio-network-simulator `_ pytest-asyncio-network-simulator: Plugin for pytest for simulator the network in tests Jul 31, 2018 3 - Alpha pytest (<3.7.0,>=3.3.2) `pytest-async-mongodb `_ pytest plugin for async MongoDB Oct 18, 2017 5 - Production/Stable pytest (>=2.5.2) -`pytest-async-sqlalchemy `_ Database testing fixtures using the SQLAlchemy asyncio API Jun 11, 2021 4 - Beta pytest (>=6.0.0) +`pytest-async-sqlalchemy `_ Database testing fixtures using the SQLAlchemy asyncio API Jul 31, 2021 4 - Beta pytest (>=6.0.0) `pytest-atomic `_ Skip rest of tests if previous test failed. Nov 24, 2018 4 - Beta N/A `pytest-attrib `_ pytest plugin to select tests based on attributes similar to the nose-attrib plugin May 24, 2016 4 - Beta N/A `pytest-austin `_ Austin plugin for pytest Oct 11, 2020 4 - Beta N/A `pytest-autochecklog `_ automatically check condition and log all the checks Apr 25, 2015 4 - Beta N/A +`pytest-automation `_ pytest plugin for building a test suite, using YAML files to extend pytest parameterize functionality. Jul 27, 2021 N/A pytest `pytest-automock `_ Pytest plugin for automatical mocks creation Apr 22, 2020 N/A pytest ; extra == 'dev' `pytest-auto-parametrize `_ pytest plugin: avoid repeating arguments in parametrize Oct 02, 2016 3 - Alpha N/A `pytest-avoidance `_ Makes pytest skip tests that don not need rerunning May 23, 2019 4 - Beta pytest (>=3.5.0) @@ -82,6 +83,7 @@ name `pytest-benchmark `_ A ``pytest`` fixture for benchmarking code. It will group the tests into rounds that are calibrated to the chosen timer. Apr 17, 2021 5 - Production/Stable pytest (>=3.8) `pytest-bg-process `_ A simple plugin to use with pytest May 28, 2021 4 - Beta pytest (>=3.5.0) `pytest-bigchaindb `_ A BigchainDB plugin for pytest. May 28, 2021 4 - Beta N/A +`pytest-bigquery-mock `_ Provides a mock fixture for python bigquery client Jul 28, 2021 N/A pytest (>=5.0) `pytest-black `_ A pytest plugin to enable format checking with black Oct 05, 2020 4 - Beta N/A `pytest-black-multipy `_ Allow '--black' on older Pythons Jan 14, 2021 5 - Production/Stable pytest (!=3.7.3,>=3.5) ; extra == 'testing' `pytest-blame `_ A pytest plugin helps developers to debug by providing useful commits history. May 04, 2019 N/A pytest (>=4.4.0) @@ -92,6 +94,7 @@ name `pytest-board `_ Local continuous test runner with pytest and watchdog. Jan 20, 2019 N/A N/A `pytest-bpdb `_ A py.test plug-in to enable drop to bpdb debugger on test failure. Jan 19, 2015 2 - Pre-Alpha N/A `pytest-bravado `_ Pytest-bravado automatically generates from OpenAPI specification client fixtures. Jul 19, 2021 N/A N/A +`pytest-breakword `_ Use breakword with pytest Jul 28, 2021 N/A pytest (>=6.2.4,<7.0.0) `pytest-breed-adapter `_ A simple plugin to connect with breed-server Nov 07, 2018 4 - Beta pytest (>=3.5.0) `pytest-briefcase `_ A pytest plugin for running tests on a Briefcase project. Jun 14, 2020 4 - Beta pytest (>=3.5.0) `pytest-browser `_ A pytest plugin for console based browser test selection just after the collection phase Dec 10, 2016 3 - Alpha N/A @@ -118,7 +121,7 @@ name `pytest-change-report `_ turn . into √,turn F into x Sep 14, 2020 N/A pytest `pytest-chdir `_ A pytest fixture for changing current working directory Jan 28, 2020 N/A pytest (>=5.0.0,<6.0.0) `pytest-check `_ A pytest plugin that allows multiple failures per test. Dec 27, 2020 5 - Production/Stable N/A -`pytest-checkdocs `_ check the README when running tests Apr 30, 2021 5 - Production/Stable pytest (>=4.6) ; extra == 'testing' +`pytest-checkdocs `_ check the README when running tests Jul 31, 2021 5 - Production/Stable pytest (>=4.6) ; extra == 'testing' `pytest-checkipdb `_ plugin to check if there are ipdb debugs left Jul 22, 2020 5 - Production/Stable pytest (>=2.9.2) `pytest-check-links `_ Check links in files Jul 29, 2020 N/A pytest (>=4.6) `pytest-check-mk `_ pytest plugin to test Check_MK checks Nov 19, 2015 4 - Beta pytest @@ -356,7 +359,7 @@ name `pytest-gitcov `_ Pytest plugin for reporting on coverage of the last git commit. Jan 11, 2020 2 - Pre-Alpha N/A `pytest-git-fixtures `_ Pytest fixtures for testing with git. Mar 11, 2021 4 - Beta pytest `pytest-github `_ Plugin for py.test that associates tests with github issues using a marker. Mar 07, 2019 5 - Production/Stable N/A -`pytest-github-actions-annotate-failures `_ pytest plugin to annotate failed tests with a workflow command for GitHub Actions Mar 21, 2021 N/A pytest (>=4.0.0) +`pytest-github-actions-annotate-failures `_ pytest plugin to annotate failed tests with a workflow command for GitHub Actions Jul 31, 2021 N/A pytest (>=4.0.0) `pytest-gitignore `_ py.test plugin to ignore the same files as git Jul 17, 2015 4 - Beta N/A `pytest-gnupg-fixtures `_ Pytest fixtures for testing with gnupg. Mar 04, 2021 4 - Beta pytest `pytest-golden `_ Plugin for pytest that offloads expected outputs to data files Nov 23, 2020 N/A pytest (>=6.1.2,<7.0.0) @@ -367,7 +370,7 @@ name `pytest-hammertime `_ Display "🔨 " instead of "." for passed pytest tests. Jul 28, 2018 N/A pytest `pytest-harvest `_ Store data created during your pytest tests execution, and retrieve it at the end of the session, e.g. for applicative benchmarking purposes. Apr 01, 2021 5 - Production/Stable N/A `pytest-helm-chart `_ A plugin to provide different types and configs of Kubernetes clusters that can be used for testing. Jun 15, 2020 4 - Beta pytest (>=5.4.2,<6.0.0) -`pytest-helm-charts `_ A plugin to provide different types and configs of Kubernetes clusters that can be used for testing. Jul 23, 2021 4 - Beta pytest (>=6.1.2,<7.0.0) +`pytest-helm-charts `_ A plugin to provide different types and configs of Kubernetes clusters that can be used for testing. Jul 30, 2021 4 - Beta pytest (>=6.1.2,<7.0.0) `pytest-helper `_ Functions to help in using the pytest testing framework May 31, 2019 5 - Production/Stable N/A `pytest-helpers `_ pytest helpers May 17, 2020 N/A pytest `pytest-helpers-namespace `_ Pytest Helpers Namespace Plugin Apr 29, 2021 5 - Production/Stable pytest (>=6.0.0) @@ -375,7 +378,7 @@ name `pytest-historic `_ Custom report to display pytest historical execution records Apr 08, 2020 N/A pytest `pytest-historic-hook `_ Custom listener to store execution results into MYSQL DB, which is used for pytest-historic report Apr 08, 2020 N/A pytest `pytest-homeassistant `_ A pytest plugin for use with homeassistant custom components. Aug 12, 2020 4 - Beta N/A -`pytest-homeassistant-custom-component `_ Experimental package to automatically extract test plugins for Home Assistant custom components Jun 30, 2021 3 - Alpha pytest (==6.2.4) +`pytest-homeassistant-custom-component `_ Experimental package to automatically extract test plugins for Home Assistant custom components Jul 28, 2021 3 - Alpha pytest (==6.2.4) `pytest-honors `_ Report on tests that honor constraints, and guard against regressions Mar 06, 2020 4 - Beta N/A `pytest-hoverfly `_ Simplify working with Hoverfly from pytest Jul 12, 2021 N/A pytest (>=5.0) `pytest-hoverfly-wrapper `_ Integrates the Hoverfly HTTP proxy into Pytest Jan 31, 2021 4 - Beta N/A @@ -398,7 +401,7 @@ name `pytest-icdiff `_ use icdiff for better error messages in pytest assertions Apr 08, 2020 4 - Beta N/A `pytest-idapro `_ A pytest plugin for idapython. Allows a pytest setup to run tests outside and inside IDA in an automated manner by runnig pytest inside IDA and by mocking idapython api Nov 03, 2018 N/A N/A `pytest-ignore-flaky `_ ignore failures from flaky tests (pytest plugin) Apr 23, 2021 5 - Production/Stable N/A -`pytest-image-diff `_ Sep 03, 2020 3 - Alpha pytest +`pytest-image-diff `_ Jul 28, 2021 3 - Alpha pytest `pytest-incremental `_ an incremental test runner (pytest plugin) Apr 24, 2021 5 - Production/Stable N/A `pytest-influxdb `_ Plugin for influxdb and pytest integration. Apr 20, 2021 N/A N/A `pytest-info-collector `_ pytest plugin to collect information from tests May 26, 2019 3 - Alpha N/A @@ -500,7 +503,7 @@ name `pytest-mockservers `_ A set of fixtures to test your requests to HTTP/UDP servers Mar 31, 2020 N/A pytest (>=4.3.0) `pytest-modifyjunit `_ Utility for adding additional properties to junit xml for IDM QE Jan 10, 2019 N/A N/A `pytest-modifyscope `_ pytest plugin to modify fixture scope Apr 12, 2020 N/A pytest -`pytest-molecule `_ PyTest Molecule Plugin :: discover and run molecule tests Jul 06, 2021 5 - Production/Stable N/A +`pytest-molecule `_ PyTest Molecule Plugin :: discover and run molecule tests Jul 30, 2021 5 - Production/Stable N/A `pytest-mongo `_ MongoDB process and client fixtures plugin for Pytest. Jun 07, 2021 5 - Production/Stable pytest `pytest-mongodb `_ pytest plugin for MongoDB fixtures Dec 07, 2019 5 - Production/Stable pytest (>=2.5.2) `pytest-monitor `_ Pytest plugin for analyzing resource usage. Apr 21, 2021 5 - Production/Stable pytest @@ -692,7 +695,7 @@ name `pytest-reverse `_ Pytest plugin to reverse test order. May 10, 2021 5 - Production/Stable pytest `pytest-ringo `_ pytest plugin to test webapplications using the Ringo webframework Sep 27, 2017 3 - Alpha N/A `pytest-rng `_ Fixtures for seeding tests and making randomness reproducible Aug 08, 2019 5 - Production/Stable pytest -`pytest-roast `_ pytest plugin for ROAST configuration override and fixtures Apr 30, 2021 5 - Production/Stable pytest +`pytest-roast `_ pytest plugin for ROAST configuration override and fixtures Jul 29, 2021 5 - Production/Stable pytest `pytest-rocketchat `_ Pytest to Rocket.Chat reporting plugin Apr 18, 2021 5 - Production/Stable N/A `pytest-rotest `_ Pytest integration with rotest Sep 08, 2019 N/A pytest (>=3.5.0) `pytest-rpc `_ Extend py.test for RPC OpenStack testing. Feb 22, 2019 4 - Beta pytest (~=3.6) @@ -706,16 +709,16 @@ name `pytest-salt-factories `_ Pytest Salt Plugin Jun 20, 2021 4 - Beta pytest (>=6.0.0) `pytest-salt-from-filenames `_ Simple PyTest Plugin For Salt's Test Suite Specifically Jan 29, 2019 4 - Beta pytest (>=4.1) `pytest-salt-runtests-bridge `_ Simple PyTest Plugin For Salt's Test Suite Specifically Dec 05, 2019 4 - Beta pytest (>=4.1) -`pytest-sanic `_ a pytest plugin for Sanic May 20, 2021 N/A pytest (>=5.2) +`pytest-sanic `_ a pytest plugin for Sanic Jul 27, 2021 N/A pytest (>=5.2) `pytest-sanity `_ Dec 07, 2020 N/A N/A `pytest-sa-pg `_ May 14, 2019 N/A N/A -`pytest-sbase `_ A complete web automation framework for end-to-end testing. Jul 20, 2021 5 - Production/Stable N/A +`pytest-sbase `_ A complete web automation framework for end-to-end testing. Jul 31, 2021 5 - Production/Stable N/A `pytest-scenario `_ pytest plugin for test scenarios Feb 06, 2017 3 - Alpha N/A `pytest-schema `_ 👍 Validate return values against a schema-like object in testing Aug 31, 2020 5 - Production/Stable pytest (>=3.5.0) `pytest-securestore `_ An encrypted password store for use within pytest cases Jun 19, 2019 4 - Beta N/A `pytest-select `_ A pytest plugin which allows to (de-)select tests from a file. Jan 18, 2019 3 - Alpha pytest (>=3.0) `pytest-selenium `_ pytest plugin for Selenium Sep 19, 2020 5 - Production/Stable pytest (>=5.0.0) -`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Jul 20, 2021 5 - Production/Stable N/A +`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Jul 31, 2021 5 - Production/Stable N/A `pytest-selenium-enhancer `_ pytest plugin for Selenium Nov 26, 2020 5 - Production/Stable N/A `pytest-selenium-pdiff `_ A pytest package implementing perceptualdiff for Selenium tests. Apr 06, 2017 2 - Pre-Alpha N/A `pytest-send-email `_ Send pytest execution result email Dec 04, 2019 N/A N/A @@ -760,10 +763,10 @@ name `pytest-splinter `_ Splinter plugin for pytest testing framework Dec 25, 2020 6 - Mature N/A `pytest-split `_ Pytest plugin for splitting test suite based on test execution time Jul 21, 2021 4 - Beta pytest (>=5,<7) `pytest-splitio `_ Split.io SDK integration for e2e tests Sep 22, 2020 N/A pytest (<7,>=5.0) -`pytest-split-tests `_ A Pytest plugin for running a subset of your tests by splitting them in to equally sized groups. Forked from Mark Adams' original project pytest-test-groups. May 28, 2019 N/A pytest (>=2.5) +`pytest-split-tests `_ A Pytest plugin for running a subset of your tests by splitting them in to equally sized groups. Forked from Mark Adams' original project pytest-test-groups. Jul 30, 2021 5 - Production/Stable pytest (>=2.5) `pytest-split-tests-tresorit `_ Feb 22, 2021 1 - Planning N/A `pytest-splunk-addon `_ A Dynamic test tool for Splunk Apps and Add-ons Jul 14, 2021 N/A pytest (>5.4.0,<6.3) -`pytest-splunk-addon-ui-smartx `_ Library to support testing Splunk Add-on UX Jul 14, 2021 N/A N/A +`pytest-splunk-addon-ui-smartx `_ Library to support testing Splunk Add-on UX Jul 26, 2021 N/A N/A `pytest-splunk-env `_ pytest fixtures for interaction with Splunk Enterprise and Splunk Cloud Oct 22, 2020 N/A pytest (>=6.1.1,<7.0.0) `pytest-sqitch `_ sqitch for pytest Apr 06, 2020 4 - Beta N/A `pytest-sqlalchemy `_ pytest plugin with sqlalchemy related fixtures Mar 13, 2018 3 - Alpha N/A @@ -819,7 +822,7 @@ name `pytest-testreport `_ Jul 01, 2021 4 - Beta pytest (>=3.5.0) `pytest-testslide `_ TestSlide fixture for pytest Jan 07, 2021 5 - Production/Stable pytest (~=6.2) `pytest-test-this `_ Plugin for py.test to run relevant tests, based on naively checking if a test contains a reference to the symbol you supply Sep 15, 2019 2 - Pre-Alpha pytest (>=2.3) -`pytest-tesults `_ Tesults plugin for pytest May 18, 2020 5 - Production/Stable pytest (>=3.5.0) +`pytest-tesults `_ Tesults plugin for pytest Jul 31, 2021 5 - Production/Stable pytest (>=3.5.0) `pytest-tezos `_ pytest-ligo Jan 16, 2020 4 - Beta N/A `pytest-thawgun `_ Pytest plugin for time travel May 26, 2020 3 - Alpha N/A `pytest-threadleak `_ Detects thread leaks Sep 08, 2017 4 - Beta N/A @@ -832,7 +835,7 @@ name `pytest-tipsi-testing `_ Better fixtures management. Various helpers Nov 04, 2020 4 - Beta pytest (>=3.3.0) `pytest-tldr `_ A pytest plugin that limits the output to just the things you need. Mar 12, 2021 4 - Beta pytest (>=3.5.0) `pytest-tm4j-reporter `_ Cloud Jira Test Management (TM4J) PyTest reporter plugin Sep 01, 2020 N/A pytest -`pytest-tmreport `_ this is a vue-element ui report for pytest Jul 16, 2021 N/A N/A +`pytest-tmreport `_ this is a vue-element ui report for pytest Jul 30, 2021 N/A N/A `pytest-todo `_ A small plugin for the pytest testing framework, marking TODO comments as failure May 23, 2019 4 - Beta pytest `pytest-tomato `_ Mar 01, 2019 5 - Production/Stable N/A `pytest-toolbelt `_ This is just a collection of utilities for pytest, but don't really belong in pytest proper. Aug 12, 2019 3 - Alpha N/A @@ -892,7 +895,7 @@ name `pytest-xfiles `_ Pytest fixtures providing data read from function, module or package related (x)files. Feb 27, 2018 N/A N/A `pytest-xlog `_ Extended logging for test and decorators May 31, 2020 4 - Beta N/A `pytest-xpara `_ An extended parametrizing plugin of pytest. Oct 30, 2017 3 - Alpha pytest -`pytest-xprocess `_ A pytest plugin for managing processes across test runs. Mar 02, 2021 4 - Beta pytest (>=2.8) +`pytest-xprocess `_ A pytest plugin for managing processes across test runs. Jul 28, 2021 4 - Beta pytest (>=2.8) `pytest-xray `_ May 30, 2019 3 - Alpha N/A `pytest-xrayjira `_ Mar 17, 2020 3 - Alpha pytest (==4.3.1) `pytest-xray-server `_ Mar 03, 2021 3 - Alpha N/A From a83b359cf0902e0e55b79411dd54480be8a0aa4e Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sun, 1 Aug 2021 06:11:56 -0300 Subject: [PATCH 417/630] Refactor internal scope handling by introducing Scope enum PR #8913 --- changelog/8913.trivial.rst | 1 + src/_pytest/fixtures.py | 237 ++++++++++++++++----------------- src/_pytest/mark/structures.py | 4 +- src/_pytest/python.py | 42 +++--- src/_pytest/scope.py | 89 +++++++++++++ src/_pytest/setuponly.py | 5 +- src/_pytest/unittest.py | 11 +- testing/python/metafunc.py | 43 +++--- testing/test_scope.py | 39 ++++++ 9 files changed, 298 insertions(+), 173 deletions(-) create mode 100644 changelog/8913.trivial.rst create mode 100644 src/_pytest/scope.py create mode 100644 testing/test_scope.py diff --git a/changelog/8913.trivial.rst b/changelog/8913.trivial.rst new file mode 100644 index 00000000000..0d971c475a5 --- /dev/null +++ b/changelog/8913.trivial.rst @@ -0,0 +1 @@ +The private ``CallSpec2._arg2scopenum`` attribute has been removed after an internal refactoring. diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index 347d490032a..79b7c877f46 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -5,6 +5,7 @@ import warnings from collections import defaultdict from collections import deque +from contextlib import suppress from pathlib import Path from types import TracebackType from typing import Any @@ -62,20 +63,21 @@ from _pytest.outcomes import TEST_OUTCOME from _pytest.pathlib import absolutepath from _pytest.pathlib import bestrelpath +from _pytest.scope import HIGH_SCOPES +from _pytest.scope import Scope from _pytest.stash import StashKey + if TYPE_CHECKING: from typing import Deque from typing import NoReturn - from typing_extensions import Literal + from _pytest.scope import _ScopeName from _pytest.main import Session from _pytest.python import CallSpec2 from _pytest.python import Function from _pytest.python import Metafunc - _Scope = Literal["session", "package", "module", "class", "function"] - # The value of the fixture -- return/yield of the fixture function (type variable). FixtureValue = TypeVar("FixtureValue") @@ -104,10 +106,10 @@ ] -@attr.s(frozen=True) +@attr.s(frozen=True, auto_attribs=True) class PseudoFixtureDef(Generic[FixtureValue]): - cached_result = attr.ib(type="_FixtureCachedResult[FixtureValue]") - scope = attr.ib(type="_Scope") + cached_result: "_FixtureCachedResult[FixtureValue]" + _scope: Scope def pytest_sessionstart(session: "Session") -> None: @@ -130,19 +132,19 @@ def get_scope_package(node, fixturedef: "FixtureDef[object]"): def get_scope_node( - node: nodes.Node, scope: "_Scope" + node: nodes.Node, scope: Scope ) -> Optional[Union[nodes.Item, nodes.Collector]]: import _pytest.python - if scope == "function": + if scope is Scope.Function: return node.getparent(nodes.Item) - elif scope == "class": + elif scope is Scope.Class: return node.getparent(_pytest.python.Class) - elif scope == "module": + elif scope is Scope.Module: return node.getparent(_pytest.python.Module) - elif scope == "package": + elif scope is Scope.Package: return node.getparent(_pytest.python.Package) - elif scope == "session": + elif scope is Scope.Session: return node.getparent(_pytest.main.Session) else: assert_never(scope) @@ -166,7 +168,7 @@ def add_funcarg_pseudo_fixture_def( return # Collect funcargs of all callspecs into a list of values. arg2params: Dict[str, List[object]] = {} - arg2scope: Dict[str, _Scope] = {} + arg2scope: Dict[str, Scope] = {} for callspec in metafunc._calls: for argname, argvalue in callspec.funcargs.items(): assert argname not in callspec.params @@ -175,8 +177,8 @@ def add_funcarg_pseudo_fixture_def( callspec.indices[argname] = len(arg2params_list) arg2params_list.append(argvalue) if argname not in arg2scope: - scopenum = callspec._arg2scopenum.get(argname, scopenum_function) - arg2scope[argname] = scopes[scopenum] + scope = callspec._arg2scope.get(argname, Scope.Function) + arg2scope[argname] = scope callspec.funcargs.clear() # Register artificial FixtureDef's so that later at test execution @@ -189,10 +191,12 @@ def add_funcarg_pseudo_fixture_def( # node related to the scope. scope = arg2scope[argname] node = None - if scope != "function": + if scope is not Scope.Function: node = get_scope_node(collector, scope) if node is None: - assert scope == "class" and isinstance(collector, _pytest.python.Module) + assert scope is Scope.Class and isinstance( + collector, _pytest.python.Module + ) # Use module-level collector for class-scope (for now). node = collector if node is None: @@ -238,10 +242,10 @@ def getfixturemarker(obj: object) -> Optional["FixtureFunctionMarker"]: _Key = Tuple[object, ...] -def get_parametrized_fixture_keys(item: nodes.Item, scopenum: int) -> Iterator[_Key]: +def get_parametrized_fixture_keys(item: nodes.Item, scope: Scope) -> Iterator[_Key]: """Return list of keys for all parametrized arguments which match the specified scope.""" - assert scopenum < scopenum_function # function + assert scope is not Scope.Function try: callspec = item.callspec # type: ignore[attr-defined] except AttributeError: @@ -252,67 +256,71 @@ def get_parametrized_fixture_keys(item: nodes.Item, scopenum: int) -> Iterator[_ # sort this so that different calls to # get_parametrized_fixture_keys will be deterministic. for argname, param_index in sorted(cs.indices.items()): - if cs._arg2scopenum[argname] != scopenum: + if cs._arg2scope[argname] != scope: continue - if scopenum == 0: # session + if scope is Scope.Session: key: _Key = (argname, param_index) - elif scopenum == 1: # package + elif scope is Scope.Package: key = (argname, param_index, item.path.parent) - elif scopenum == 2: # module + elif scope is Scope.Module: key = (argname, param_index, item.path) - elif scopenum == 3: # class + elif scope is Scope.Class: item_cls = item.cls # type: ignore[attr-defined] key = (argname, param_index, item.path, item_cls) + else: + assert_never(scope) yield key # Algorithm for sorting on a per-parametrized resource setup basis. -# It is called for scopenum==0 (session) first and performs sorting +# It is called for Session scope first and performs sorting # down to the lower scopes such as to minimize number of "high scope" # setups and teardowns. def reorder_items(items: Sequence[nodes.Item]) -> List[nodes.Item]: - argkeys_cache: Dict[int, Dict[nodes.Item, Dict[_Key, None]]] = {} - items_by_argkey: Dict[int, Dict[_Key, Deque[nodes.Item]]] = {} - for scopenum in range(scopenum_function): + argkeys_cache: Dict[Scope, Dict[nodes.Item, Dict[_Key, None]]] = {} + items_by_argkey: Dict[Scope, Dict[_Key, Deque[nodes.Item]]] = {} + for scope in HIGH_SCOPES: d: Dict[nodes.Item, Dict[_Key, None]] = {} - argkeys_cache[scopenum] = d + argkeys_cache[scope] = d item_d: Dict[_Key, Deque[nodes.Item]] = defaultdict(deque) - items_by_argkey[scopenum] = item_d + items_by_argkey[scope] = item_d for item in items: - keys = dict.fromkeys(get_parametrized_fixture_keys(item, scopenum), None) + keys = dict.fromkeys(get_parametrized_fixture_keys(item, scope), None) if keys: d[item] = keys for key in keys: item_d[key].append(item) items_dict = dict.fromkeys(items, None) - return list(reorder_items_atscope(items_dict, argkeys_cache, items_by_argkey, 0)) + return list( + reorder_items_atscope(items_dict, argkeys_cache, items_by_argkey, Scope.Session) + ) def fix_cache_order( item: nodes.Item, - argkeys_cache: Dict[int, Dict[nodes.Item, Dict[_Key, None]]], - items_by_argkey: Dict[int, Dict[_Key, "Deque[nodes.Item]"]], + argkeys_cache: Dict[Scope, Dict[nodes.Item, Dict[_Key, None]]], + items_by_argkey: Dict[Scope, Dict[_Key, "Deque[nodes.Item]"]], ) -> None: - for scopenum in range(scopenum_function): - for key in argkeys_cache[scopenum].get(item, []): - items_by_argkey[scopenum][key].appendleft(item) + for scope in HIGH_SCOPES: + for key in argkeys_cache[scope].get(item, []): + items_by_argkey[scope][key].appendleft(item) def reorder_items_atscope( items: Dict[nodes.Item, None], - argkeys_cache: Dict[int, Dict[nodes.Item, Dict[_Key, None]]], - items_by_argkey: Dict[int, Dict[_Key, "Deque[nodes.Item]"]], - scopenum: int, + argkeys_cache: Dict[Scope, Dict[nodes.Item, Dict[_Key, None]]], + items_by_argkey: Dict[Scope, Dict[_Key, "Deque[nodes.Item]"]], + scope: Scope, ) -> Dict[nodes.Item, None]: - if scopenum >= scopenum_function or len(items) < 3: + if scope is Scope.Function or len(items) < 3: return items ignore: Set[Optional[_Key]] = set() items_deque = deque(items) items_done: Dict[nodes.Item, None] = {} - scoped_items_by_argkey = items_by_argkey[scopenum] - scoped_argkeys_cache = argkeys_cache[scopenum] + scoped_items_by_argkey = items_by_argkey[scope] + scoped_argkeys_cache = argkeys_cache[scope] while items_deque: no_argkey_group: Dict[nodes.Item, None] = {} slicing_argkey = None @@ -338,7 +346,7 @@ def reorder_items_atscope( break if no_argkey_group: no_argkey_group = reorder_items_atscope( - no_argkey_group, argkeys_cache, items_by_argkey, scopenum + 1 + no_argkey_group, argkeys_cache, items_by_argkey, scope.next_lower() ) for item in no_argkey_group: items_done[item] = None @@ -437,14 +445,18 @@ def __init__(self, pyfuncitem, *, _ispytest: bool = False) -> None: self._pyfuncitem = pyfuncitem #: Fixture for which this request is being performed. self.fixturename: Optional[str] = None - #: Scope string, one of "function", "class", "module", "session". - self.scope: _Scope = "function" + self._scope = Scope.Function self._fixture_defs: Dict[str, FixtureDef[Any]] = {} fixtureinfo: FuncFixtureInfo = pyfuncitem._fixtureinfo self._arg2fixturedefs = fixtureinfo.name2fixturedefs.copy() self._arg2index: Dict[str, int] = {} self._fixturemanager: FixtureManager = pyfuncitem.session._fixturemanager + @property + def scope(self) -> "_ScopeName": + """Scope string, one of "function", "class", "module", "package", "session".""" + return self._scope.value + @property def fixturenames(self) -> List[str]: """Names of all active fixtures in this request.""" @@ -455,7 +467,7 @@ def fixturenames(self) -> List[str]: @property def node(self): """Underlying collection node (depends on current request scope).""" - return self._getscopeitem(self.scope) + return self._getscopeitem(self._scope) def _getnextfixturedef(self, argname: str) -> "FixtureDef[Any]": fixturedefs = self._arg2fixturedefs.get(argname, None) @@ -598,8 +610,7 @@ def _get_active_fixturedef( except FixtureLookupError: if argname == "request": cached_result = (self, [0], None) - scope: _Scope = "function" - return PseudoFixtureDef(cached_result, scope) + return PseudoFixtureDef(cached_result, Scope.Function) raise # Remove indent to prevent the python3 exception # from leaking into the call. @@ -628,7 +639,7 @@ def _compute_fixture_value(self, fixturedef: "FixtureDef[object]") -> None: # (latter managed by fixturedef) argname = fixturedef.argname funcitem = self._pyfuncitem - scope = fixturedef.scope + scope = fixturedef._scope try: param = funcitem.callspec.getparam(argname) except (AttributeError, ValueError): @@ -675,16 +686,15 @@ def _compute_fixture_value(self, fixturedef: "FixtureDef[object]") -> None: param_index = funcitem.callspec.indices[argname] # If a parametrize invocation set a scope it will override # the static scope defined with the fixture function. - paramscopenum = funcitem.callspec._arg2scopenum.get(argname) - if paramscopenum is not None: - scope = scopes[paramscopenum] + with suppress(KeyError): + scope = funcitem.callspec._arg2scope[argname] subrequest = SubRequest( self, scope, param, param_index, fixturedef, _ispytest=True ) # Check if a higher-level scoped fixture accesses a lower level one. - subrequest._check_scope(argname, self.scope, scope) + subrequest._check_scope(argname, self._scope, scope) try: # Call the fixture function. fixturedef.execute(request=subrequest) @@ -700,19 +710,18 @@ def _schedule_finalizers( def _check_scope( self, argname: str, - invoking_scope: "_Scope", - requested_scope: "_Scope", + invoking_scope: Scope, + requested_scope: Scope, ) -> None: if argname == "request": return - if scopemismatch(invoking_scope, requested_scope): + if invoking_scope > requested_scope: # Try to report something helpful. - lines = self._factorytraceback() + text = "\n".join(self._factorytraceback()) fail( - "ScopeMismatch: You tried to access the %r scoped " - "fixture %r with a %r scoped request object, " - "involved factories\n%s" - % ((requested_scope, argname, invoking_scope, "\n".join(lines))), + f"ScopeMismatch: You tried to access the {requested_scope.value} scoped " + f"fixture {argname} with a {invoking_scope.value} scoped request object, " + f"involved factories:\n{text}", pytrace=False, ) @@ -730,17 +739,21 @@ def _factorytraceback(self) -> List[str]: lines.append("%s:%d: def %s%s" % (p, lineno + 1, factory.__name__, args)) return lines - def _getscopeitem(self, scope: "_Scope") -> Union[nodes.Item, nodes.Collector]: - if scope == "function": + def _getscopeitem( + self, scope: Union[Scope, "_ScopeName"] + ) -> Union[nodes.Item, nodes.Collector]: + if isinstance(scope, str): + scope = Scope(scope) + if scope is Scope.Function: # This might also be a non-function Item despite its attribute name. node: Optional[Union[nodes.Item, nodes.Collector]] = self._pyfuncitem - elif scope == "package": + elif scope is Scope.Package: # FIXME: _fixturedef is not defined on FixtureRequest (this class), # but on FixtureRequest (a subclass). node = get_scope_package(self._pyfuncitem, self._fixturedef) # type: ignore[attr-defined] else: node = get_scope_node(self._pyfuncitem, scope) - if node is None and scope == "class": + if node is None and scope is Scope.Class: # Fallback to function item itself. node = self._pyfuncitem assert node, 'Could not obtain a node for scope "{}" for function {!r}'.format( @@ -759,7 +772,7 @@ class SubRequest(FixtureRequest): def __init__( self, request: "FixtureRequest", - scope: "_Scope", + scope: Scope, param: Any, param_index: int, fixturedef: "FixtureDef[object]", @@ -772,7 +785,7 @@ def __init__( if param is not NOTSET: self.param = param self.param_index = param_index - self.scope = scope + self._scope = scope self._fixturedef = fixturedef self._pyfuncitem = request._pyfuncitem self._fixture_defs = request._fixture_defs @@ -801,29 +814,6 @@ def _schedule_finalizers( super()._schedule_finalizers(fixturedef, subrequest) -scopes: List["_Scope"] = ["session", "package", "module", "class", "function"] -scopenum_function = scopes.index("function") - - -def scopemismatch(currentscope: "_Scope", newscope: "_Scope") -> bool: - return scopes.index(newscope) > scopes.index(currentscope) - - -def scope2index(scope: str, descr: str, where: Optional[str] = None) -> int: - """Look up the index of ``scope`` and raise a descriptive value error - if not defined.""" - strscopes: Sequence[str] = scopes - try: - return strscopes.index(scope) - except ValueError: - fail( - "{} {}got an unexpected scope value '{}'".format( - descr, f"from {where} " if where else "", scope - ), - pytrace=False, - ) - - @final class FixtureLookupError(LookupError): """Could not return a requested fixture (missing or invalid).""" @@ -955,10 +945,10 @@ def _teardown_yield_fixture(fixturefunc, it) -> None: def _eval_scope_callable( - scope_callable: "Callable[[str, Config], _Scope]", + scope_callable: "Callable[[str, Config], _ScopeName]", fixture_name: str, config: Config, -) -> "_Scope": +) -> "_ScopeName": try: # Type ignored because there is no typing mechanism to specify # keyword arguments, currently. @@ -989,7 +979,7 @@ def __init__( baseid: Optional[str], argname: str, func: "_FixtureFunc[FixtureValue]", - scope: "Union[_Scope, Callable[[str, Config], _Scope]]", + scope: Union[Scope, "_ScopeName", Callable[[str, Config], "_ScopeName"], None], params: Optional[Sequence[object]], unittest: bool = False, ids: Optional[ @@ -1004,17 +994,16 @@ def __init__( self.has_location = baseid is not None self.func = func self.argname = argname - if callable(scope): - scope_ = _eval_scope_callable(scope, argname, fixturemanager.config) - else: - scope_ = scope - self.scopenum = scope2index( - # TODO: Check if the `or` here is really necessary. - scope_ or "function", # type: ignore[unreachable] - descr=f"Fixture '{func.__name__}'", - where=baseid, - ) - self.scope = scope_ + if scope is None: + scope = Scope.Function + elif callable(scope): + scope = _eval_scope_callable(scope, argname, fixturemanager.config) + + if isinstance(scope, str): + scope = Scope.from_user( + scope, descr=f"Fixture '{func.__name__}'", where=baseid + ) + self._scope = scope self.params: Optional[Sequence[object]] = params self.argnames: Tuple[str, ...] = getfuncargnames( func, name=argname, is_method=unittest @@ -1024,6 +1013,11 @@ def __init__( self.cached_result: Optional[_FixtureCachedResult[FixtureValue]] = None self._finalizers: List[Callable[[], object]] = [] + @property + def scope(self) -> "_ScopeName": + """Scope string, one of "function", "class", "module", "package", "session".""" + return self._scope.value + def addfinalizer(self, finalizer: Callable[[], object]) -> None: self._finalizers.append(finalizer) @@ -1126,7 +1120,7 @@ def pytest_fixture_setup( fixdef = request._get_active_fixturedef(argname) assert fixdef.cached_result is not None result, arg_cache_key, exc = fixdef.cached_result - request._check_scope(argname, request.scope, fixdef.scope) + request._check_scope(argname, request._scope, fixdef._scope) kwargs[argname] = result fixturefunc = resolve_fixture_function(fixturedef, request) @@ -1195,18 +1189,17 @@ def result(*args, **kwargs): @final @attr.s(frozen=True) class FixtureFunctionMarker: - scope = attr.ib(type="Union[_Scope, Callable[[str, Config], _Scope]]") - params = attr.ib(type=Optional[Tuple[object, ...]], converter=_params_converter) - autouse = attr.ib(type=bool, default=False) - ids = attr.ib( - type=Union[ - Tuple[Union[None, str, float, int, bool], ...], - Callable[[Any], Optional[object]], - ], + scope: "Union[_ScopeName, Callable[[str, Config], _ScopeName]]" = attr.ib() + params: Optional[Tuple[object, ...]] = attr.ib(converter=_params_converter) + autouse: bool = attr.ib(default=False) + ids: Union[ + Tuple[Union[None, str, float, int, bool], ...], + Callable[[Any], Optional[object]], + ] = attr.ib( default=None, converter=_ensure_immutable_ids, ) - name = attr.ib(type=Optional[str], default=None) + name: Optional[str] = attr.ib(default=None) def __call__(self, function: FixtureFunction) -> FixtureFunction: if inspect.isclass(function): @@ -1238,7 +1231,7 @@ def __call__(self, function: FixtureFunction) -> FixtureFunction: def fixture( fixture_function: FixtureFunction, *, - scope: "Union[_Scope, Callable[[str, Config], _Scope]]" = ..., + scope: "Union[_ScopeName, Callable[[str, Config], _ScopeName]]" = ..., params: Optional[Iterable[object]] = ..., autouse: bool = ..., ids: Optional[ @@ -1256,7 +1249,7 @@ def fixture( def fixture( fixture_function: None = ..., *, - scope: "Union[_Scope, Callable[[str, Config], _Scope]]" = ..., + scope: "Union[_ScopeName, Callable[[str, Config], _ScopeName]]" = ..., params: Optional[Iterable[object]] = ..., autouse: bool = ..., ids: Optional[ @@ -1273,7 +1266,7 @@ def fixture( def fixture( fixture_function: Optional[FixtureFunction] = None, *, - scope: "Union[_Scope, Callable[[str, Config], _Scope]]" = "function", + scope: "Union[_ScopeName, Callable[[str, Config], _ScopeName]]" = "function", params: Optional[Iterable[object]] = None, autouse: bool = False, ids: Optional[ @@ -1552,15 +1545,15 @@ def merge(otherlist: Iterable[str]) -> None: arg2fixturedefs[argname] = fixturedefs merge(fixturedefs[-1].argnames) - def sort_by_scope(arg_name: str) -> int: + def sort_by_scope(arg_name: str) -> Scope: try: fixturedefs = arg2fixturedefs[arg_name] except KeyError: - return scopes.index("function") + return Scope.Function else: - return fixturedefs[-1].scopenum + return fixturedefs[-1]._scope - fixturenames_closure.sort(key=sort_by_scope) + fixturenames_closure.sort(key=sort_by_scope, reverse=True) return initialnames, fixturenames_closure, arg2fixturedefs def pytest_generate_tests(self, metafunc: "Metafunc") -> None: diff --git a/src/_pytest/mark/structures.py b/src/_pytest/mark/structures.py index e40a605bdc5..76dff5e082c 100644 --- a/src/_pytest/mark/structures.py +++ b/src/_pytest/mark/structures.py @@ -400,7 +400,7 @@ def store_mark(obj, mark: Mark) -> None: # Typing for builtin pytest marks. This is cheating; it gives builtin marks # special privilege, and breaks modularity. But practicality beats purity... if TYPE_CHECKING: - from _pytest.fixtures import _Scope + from _pytest.scope import _ScopeName class _SkipMarkDecorator(MarkDecorator): @overload # type: ignore[override,misc] @@ -450,7 +450,7 @@ def __call__( # type: ignore[override] Callable[[Any], Optional[object]], ] ] = ..., - scope: Optional[_Scope] = ..., + scope: Optional[_ScopeName] = ..., ) -> MarkDecorator: ... diff --git a/src/_pytest/python.py b/src/_pytest/python.py index 8e8d2f38d83..e44320ddf49 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -72,12 +72,13 @@ from _pytest.pathlib import ImportPathMismatchError from _pytest.pathlib import parts from _pytest.pathlib import visit +from _pytest.scope import Scope from _pytest.warning_types import PytestCollectionWarning from _pytest.warning_types import PytestUnhandledCoroutineWarning if TYPE_CHECKING: from typing_extensions import Literal - from _pytest.fixtures import _Scope + from _pytest.scope import _ScopeName def pytest_addoption(parser: Parser) -> None: @@ -896,7 +897,7 @@ def __init__(self, metafunc: "Metafunc") -> None: self._idlist: List[str] = [] self.params: Dict[str, object] = {} # Used for sorting parametrized resources. - self._arg2scopenum: Dict[str, int] = {} + self._arg2scope: Dict[str, Scope] = {} self.marks: List[Mark] = [] self.indices: Dict[str, int] = {} @@ -906,7 +907,7 @@ def copy(self) -> "CallSpec2": cs.params.update(self.params) cs.marks.extend(self.marks) cs.indices.update(self.indices) - cs._arg2scopenum.update(self._arg2scopenum) + cs._arg2scope.update(self._arg2scope) cs._idlist = list(self._idlist) return cs @@ -927,7 +928,7 @@ def setmulti2( valset: Iterable[object], id: str, marks: Iterable[Union[Mark, MarkDecorator]], - scopenum: int, + scope: Scope, param_index: int, ) -> None: for arg, val in zip(argnames, valset): @@ -941,7 +942,7 @@ def setmulti2( else: # pragma: no cover assert False, f"Unhandled valtype for arg: {valtype_for_arg}" self.indices[arg] = param_index - self._arg2scopenum[arg] = scopenum + self._arg2scope[arg] = scope self._idlist.append(id) self.marks.extend(normalize_mark_list(marks)) @@ -999,7 +1000,7 @@ def parametrize( Callable[[Any], Optional[object]], ] ] = None, - scope: "Optional[_Scope]" = None, + scope: "Optional[_ScopeName]" = None, *, _param_mark: Optional[Mark] = None, ) -> None: @@ -1055,8 +1056,6 @@ def parametrize( It will also override any fixture-function defined scope, allowing to set a dynamic scope using test context or configuration. """ - from _pytest.fixtures import scope2index - argnames, parameters = ParameterSet._for_parametrize( argnames, argvalues, @@ -1072,8 +1071,12 @@ def parametrize( pytrace=False, ) - if scope is None: - scope = _find_parametrized_scope(argnames, self._arg2fixturedefs, indirect) + if scope is not None: + scope_ = Scope.from_user( + scope, descr=f"parametrize() call in {self.function.__name__}" + ) + else: + scope_ = _find_parametrized_scope(argnames, self._arg2fixturedefs, indirect) self._validate_if_using_arg_names(argnames, indirect) @@ -1093,10 +1096,6 @@ def parametrize( if _param_mark and _param_mark._param_ids_from and generated_ids is None: object.__setattr__(_param_mark._param_ids_from, "_param_ids_generated", ids) - scopenum = scope2index( - scope, descr=f"parametrize() call in {self.function.__name__}" - ) - # Create the new calls: if we are parametrize() multiple times (by applying the decorator # more than once) then we accumulate those calls generating the cartesian product # of all calls. @@ -1110,7 +1109,7 @@ def parametrize( param_set.values, param_id, param_set.marks, - scopenum, + scope_, param_index, ) newcalls.append(newcallspec) @@ -1263,7 +1262,7 @@ def _find_parametrized_scope( argnames: Sequence[str], arg2fixturedefs: Mapping[str, Sequence[fixtures.FixtureDef[object]]], indirect: Union[bool, Sequence[str]], -) -> "fixtures._Scope": +) -> Scope: """Find the most appropriate scope for a parametrized call based on its arguments. When there's at least one direct argument, always use "function" scope. @@ -1281,17 +1280,14 @@ def _find_parametrized_scope( if all_arguments_are_fixtures: fixturedefs = arg2fixturedefs or {} used_scopes = [ - fixturedef[0].scope + fixturedef[0]._scope for name, fixturedef in fixturedefs.items() if name in argnames ] - if used_scopes: - # Takes the most narrow scope from used fixtures. - for scope in reversed(fixtures.scopes): - if scope in used_scopes: - return scope + # Takes the most narrow scope from used fixtures. + return min(used_scopes, default=Scope.Function) - return "function" + return Scope.Function def _ascii_escaped_by_config(val: Union[str, bytes], config: Optional[Config]) -> str: diff --git a/src/_pytest/scope.py b/src/_pytest/scope.py new file mode 100644 index 00000000000..4f37a0f3be6 --- /dev/null +++ b/src/_pytest/scope.py @@ -0,0 +1,89 @@ +""" +Scope definition and related utilities. + +Those are defined here, instead of in the 'fixtures' module because +their use is spread across many other pytest modules, and centralizing it in 'fixtures' +would cause circular references. + +Also this makes the module light to import, as it should. +""" +from enum import Enum +from functools import total_ordering +from typing import Optional +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from typing_extensions import Literal + + _ScopeName = Literal["session", "package", "module", "class", "function"] + + +@total_ordering +class Scope(Enum): + """ + Represents one of the possible fixture scopes in pytest. + + Scopes are ordered from lower to higher, that is: + + ->>> higher ->>> + + Function < Class < Module < Package < Session + + <<<- lower <<<- + """ + + # Scopes need to be listed from lower to higher. + Function: "_ScopeName" = "function" + Class: "_ScopeName" = "class" + Module: "_ScopeName" = "module" + Package: "_ScopeName" = "package" + Session: "_ScopeName" = "session" + + def next_lower(self) -> "Scope": + """Return the next lower scope.""" + index = _SCOPE_INDICES[self] + if index == 0: + raise ValueError(f"{self} is the lower-most scope") + return _ALL_SCOPES[index - 1] + + def next_higher(self) -> "Scope": + """Return the next higher scope.""" + index = _SCOPE_INDICES[self] + if index == len(_SCOPE_INDICES) - 1: + raise ValueError(f"{self} is the upper-most scope") + return _ALL_SCOPES[index + 1] + + def __lt__(self, other: "Scope") -> bool: + self_index = _SCOPE_INDICES[self] + other_index = _SCOPE_INDICES[other] + return self_index < other_index + + @classmethod + def from_user( + cls, scope_name: "_ScopeName", descr: str, where: Optional[str] = None + ) -> "Scope": + """ + Given a scope name from the user, return the equivalent Scope enum. Should be used + whenever we want to convert a user provided scope name to its enum object. + + If the scope name is invalid, construct a user friendly message and call pytest.fail. + """ + from _pytest.outcomes import fail + + try: + return Scope(scope_name) + except ValueError: + fail( + "{} {}got an unexpected scope value '{}'".format( + descr, f"from {where} " if where else "", scope_name + ), + pytrace=False, + ) + + +_ALL_SCOPES = list(Scope) +_SCOPE_INDICES = {scope: index for index, scope in enumerate(_ALL_SCOPES)} + + +# Ordered list of scopes which can contain many tests (in practice all except Function). +HIGH_SCOPES = [x for x in Scope if x is not Scope.Function] diff --git a/src/_pytest/setuponly.py b/src/_pytest/setuponly.py index 9a1dd751716..531131ce726 100644 --- a/src/_pytest/setuponly.py +++ b/src/_pytest/setuponly.py @@ -9,6 +9,7 @@ from _pytest.config.argparsing import Parser from _pytest.fixtures import FixtureDef from _pytest.fixtures import SubRequest +from _pytest.scope import Scope def pytest_addoption(parser: Parser) -> None: @@ -64,7 +65,9 @@ def _show_fixture_action(fixturedef: FixtureDef[object], msg: str) -> None: tw = config.get_terminal_writer() tw.line() - tw.write(" " * 2 * fixturedef.scopenum) + # Use smaller indentation the higher the scope: Session = 0, Package = 1, etc. + scope_indent = list(reversed(Scope)).index(fixturedef._scope) + tw.write(" " * 2 * scope_indent) tw.write( "{step} {scope} {fixture}".format( step=msg.ljust(8), # align the output to TEARDOWN diff --git a/src/_pytest/unittest.py b/src/_pytest/unittest.py index 17fccc26867..26ea22f33f9 100644 --- a/src/_pytest/unittest.py +++ b/src/_pytest/unittest.py @@ -29,13 +29,12 @@ from _pytest.python import Function from _pytest.python import PyCollector from _pytest.runner import CallInfo +from _pytest.scope import Scope if TYPE_CHECKING: import unittest import twisted.trial.unittest - from _pytest.fixtures import _Scope - _SysExcInfoType = Union[ Tuple[Type[BaseException], BaseException, types.TracebackType], Tuple[None, None, None], @@ -102,7 +101,7 @@ def _inject_setup_teardown_fixtures(self, cls: type) -> None: "setUpClass", "tearDownClass", "doClassCleanups", - scope="class", + scope=Scope.Class, pass_self=False, ) if class_fixture: @@ -113,7 +112,7 @@ def _inject_setup_teardown_fixtures(self, cls: type) -> None: "setup_method", "teardown_method", None, - scope="function", + scope=Scope.Function, pass_self=True, ) if method_fixture: @@ -125,7 +124,7 @@ def _make_xunit_fixture( setup_name: str, teardown_name: str, cleanup_name: Optional[str], - scope: "_Scope", + scope: Scope, pass_self: bool, ): setup = getattr(obj, setup_name, None) @@ -141,7 +140,7 @@ def cleanup(*args): pass @pytest.fixture( - scope=scope, + scope=scope.value, autouse=True, # Use a unique name to speed up lookup. name=f"_unittest_{setup_name}_fixture_{obj.__qualname__}", diff --git a/testing/python/metafunc.py b/testing/python/metafunc.py index ccf9c1d9c24..fc0082eb6b9 100644 --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -26,6 +26,7 @@ from _pytest.pytester import Pytester from _pytest.python import _idval from _pytest.python import idmaker +from _pytest.scope import Scope class TestMetafunc: @@ -142,16 +143,16 @@ def test_find_parametrized_scope(self) -> None: @attr.s class DummyFixtureDef: - scope = attr.ib() + _scope = attr.ib() fixtures_defs = cast( Dict[str, Sequence[fixtures.FixtureDef[object]]], dict( - session_fix=[DummyFixtureDef("session")], - package_fix=[DummyFixtureDef("package")], - module_fix=[DummyFixtureDef("module")], - class_fix=[DummyFixtureDef("class")], - func_fix=[DummyFixtureDef("function")], + session_fix=[DummyFixtureDef(Scope.Session)], + package_fix=[DummyFixtureDef(Scope.Package)], + module_fix=[DummyFixtureDef(Scope.Module)], + class_fix=[DummyFixtureDef(Scope.Class)], + func_fix=[DummyFixtureDef(Scope.Function)], ), ) @@ -160,29 +161,33 @@ class DummyFixtureDef: def find_scope(argnames, indirect): return _find_parametrized_scope(argnames, fixtures_defs, indirect=indirect) - assert find_scope(["func_fix"], indirect=True) == "function" - assert find_scope(["class_fix"], indirect=True) == "class" - assert find_scope(["module_fix"], indirect=True) == "module" - assert find_scope(["package_fix"], indirect=True) == "package" - assert find_scope(["session_fix"], indirect=True) == "session" + assert find_scope(["func_fix"], indirect=True) == Scope.Function + assert find_scope(["class_fix"], indirect=True) == Scope.Class + assert find_scope(["module_fix"], indirect=True) == Scope.Module + assert find_scope(["package_fix"], indirect=True) == Scope.Package + assert find_scope(["session_fix"], indirect=True) == Scope.Session - assert find_scope(["class_fix", "func_fix"], indirect=True) == "function" - assert find_scope(["func_fix", "session_fix"], indirect=True) == "function" - assert find_scope(["session_fix", "class_fix"], indirect=True) == "class" - assert find_scope(["package_fix", "session_fix"], indirect=True) == "package" - assert find_scope(["module_fix", "session_fix"], indirect=True) == "module" + assert find_scope(["class_fix", "func_fix"], indirect=True) == Scope.Function + assert find_scope(["func_fix", "session_fix"], indirect=True) == Scope.Function + assert find_scope(["session_fix", "class_fix"], indirect=True) == Scope.Class + assert ( + find_scope(["package_fix", "session_fix"], indirect=True) == Scope.Package + ) + assert find_scope(["module_fix", "session_fix"], indirect=True) == Scope.Module # when indirect is False or is not for all scopes, always use function - assert find_scope(["session_fix", "module_fix"], indirect=False) == "function" + assert ( + find_scope(["session_fix", "module_fix"], indirect=False) == Scope.Function + ) assert ( find_scope(["session_fix", "module_fix"], indirect=["module_fix"]) - == "function" + == Scope.Function ) assert ( find_scope( ["session_fix", "module_fix"], indirect=["session_fix", "module_fix"] ) - == "module" + == Scope.Module ) def test_parametrize_and_id(self) -> None: diff --git a/testing/test_scope.py b/testing/test_scope.py new file mode 100644 index 00000000000..09ee1343a80 --- /dev/null +++ b/testing/test_scope.py @@ -0,0 +1,39 @@ +import re + +import pytest +from _pytest.scope import Scope + + +def test_ordering() -> None: + assert Scope.Session > Scope.Package + assert Scope.Package > Scope.Module + assert Scope.Module > Scope.Class + assert Scope.Class > Scope.Function + + +def test_next_lower() -> None: + assert Scope.Session.next_lower() is Scope.Package + assert Scope.Package.next_lower() is Scope.Module + assert Scope.Module.next_lower() is Scope.Class + assert Scope.Class.next_lower() is Scope.Function + + with pytest.raises(ValueError, match="Function is the lower-most scope"): + Scope.Function.next_lower() + + +def test_next_higher() -> None: + assert Scope.Function.next_higher() is Scope.Class + assert Scope.Class.next_higher() is Scope.Module + assert Scope.Module.next_higher() is Scope.Package + assert Scope.Package.next_higher() is Scope.Session + + with pytest.raises(ValueError, match="Session is the upper-most scope"): + Scope.Session.next_higher() + + +def test_from_user() -> None: + assert Scope.from_user("module", "for parametrize", "some::id") is Scope.Module + + expected_msg = "for parametrize from some::id got an unexpected scope value 'foo'" + with pytest.raises(pytest.fail.Exception, match=re.escape(expected_msg)): + Scope.from_user("foo", "for parametrize", "some::id") # type:ignore[arg-type] From c22bf3ff3e8378ea8b8c4675bfb4603eeb722341 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Aug 2021 03:01:59 +0000 Subject: [PATCH 418/630] build(deps): bump twisted in /testing/plugins_integration Bumps [twisted](https://github.com/twisted/twisted) from 21.2.0 to 21.7.0. - [Release notes](https://github.com/twisted/twisted/releases) - [Changelog](https://github.com/twisted/twisted/blob/trunk/NEWS.rst) - [Commits](https://github.com/twisted/twisted/compare/twisted-21.2.0...twisted-21.7.0) --- updated-dependencies: - dependency-name: twisted dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- testing/plugins_integration/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/plugins_integration/requirements.txt b/testing/plugins_integration/requirements.txt index 7385658090e..b86327d8178 100644 --- a/testing/plugins_integration/requirements.txt +++ b/testing/plugins_integration/requirements.txt @@ -11,5 +11,5 @@ pytest-rerunfailures==10.1 pytest-sugar==0.9.4 pytest-trio==0.7.0 pytest-twisted==1.13.3 -twisted==21.2.0 +twisted==21.7.0 pytest-xvfb==2.0.0 From b33c81b57f63a06ee01d5b768dadd295fdcf0125 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 2 Aug 2021 18:53:11 +0000 Subject: [PATCH 419/630] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v2.23.0 → v2.23.1](https://github.com/asottile/pyupgrade/compare/v2.23.0...v2.23.1) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d97e2babe35..214e59e2182 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -34,7 +34,7 @@ repos: - id: reorder-python-imports args: ['--application-directories=.:src', --py36-plus] - repo: https://github.com/asottile/pyupgrade - rev: v2.23.0 + rev: v2.23.1 hooks: - id: pyupgrade args: [--py36-plus] From bacc8498e93d1b84fa7e8deb282d832b703fba86 Mon Sep 17 00:00:00 2001 From: Simon K Date: Tue, 3 Aug 2021 14:31:18 +0100 Subject: [PATCH 420/630] `pytest_assertion_pass` is no longer considered `experimental` (#8967) * `pytest_assertion_pass` is no longer considered `experimental` * adding changelog --- changelog/8967.trivial.rst | 2 ++ src/_pytest/hookspec.py | 9 +-------- 2 files changed, 3 insertions(+), 8 deletions(-) create mode 100644 changelog/8967.trivial.rst diff --git a/changelog/8967.trivial.rst b/changelog/8967.trivial.rst new file mode 100644 index 00000000000..d5f773241f5 --- /dev/null +++ b/changelog/8967.trivial.rst @@ -0,0 +1,2 @@ +:func:`pytest_assertion_pass <_pytest.hookspec.pytest_assertion_pass>` is no longer considered experimental and +future changes to it will be considered more carefully. diff --git a/src/_pytest/hookspec.py b/src/_pytest/hookspec.py index 4fe3cf4b46b..29a713b007d 100644 --- a/src/_pytest/hookspec.py +++ b/src/_pytest/hookspec.py @@ -635,7 +635,7 @@ def pytest_assertrepr_compare( def pytest_assertion_pass(item: "Item", lineno: int, orig: str, expl: str) -> None: - """**(Experimental)** Called whenever an assertion passes. + """Called whenever an assertion passes. .. versionadded:: 5.0 @@ -659,13 +659,6 @@ def pytest_assertion_pass(item: "Item", lineno: int, orig: str, expl: str) -> No :param int lineno: Line number of the assert statement. :param str orig: String with the original assertion. :param str expl: String with the assert explanation. - - .. note:: - - This hook is **experimental**, so its parameters or even the hook itself might - be changed/removed without warning in any future pytest release. - - If you find this hook useful, please share your feedback in an issue. """ From 4d2944e1d4e4cf02e74a7a2b4ffccbb5c559db46 Mon Sep 17 00:00:00 2001 From: pytest bot Date: Sun, 8 Aug 2021 00:10:10 +0000 Subject: [PATCH 421/630] [automated] Update plugin list --- doc/en/reference/plugin_list.rst | 38 ++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/doc/en/reference/plugin_list.rst b/doc/en/reference/plugin_list.rst index 1972a97ec06..dc19d7df80b 100644 --- a/doc/en/reference/plugin_list.rst +++ b/doc/en/reference/plugin_list.rst @@ -6,24 +6,25 @@ Plugin List PyPI projects that match "pytest-\*" are considered plugins and are listed automatically. Packages classified as inactive are excluded. -This list contains 900 plugins. +This list contains 904 plugins. ============================================================================================================== ======================================================================================================================================================================== ============== ===================== ================================================ name summary last release status requires ============================================================================================================== ======================================================================================================================================================================== ============== ===================== ================================================ `pytest-accept `_ A pytest-plugin for updating doctest outputs Jun 02, 2021 N/A pytest (>=6,<7) `pytest-adaptavist `_ pytest plugin for generating test execution results within Jira Test Management (tm4j) Apr 22, 2021 N/A pytest (>=3.4.1) +`pytest-addons-test `_ 用于测试pytest的插件 Aug 02, 2021 N/A pytest (>=6.2.4,<7.0.0) `pytest-adf `_ Pytest plugin for writing Azure Data Factory integration tests May 10, 2021 4 - Beta pytest (>=3.5.0) `pytest-adf-azure-identity `_ Pytest plugin for writing Azure Data Factory integration tests Mar 06, 2021 4 - Beta pytest (>=3.5.0) `pytest-aggreport `_ pytest plugin for pytest-repeat that generate aggregate report of the same test cases with additional statistics details. Mar 07, 2021 4 - Beta pytest (>=6.2.2) -`pytest-aio `_ Pytest plugin for testing async python code May 21, 2021 4 - Beta pytest ; extra == 'tests' +`pytest-aio `_ Pytest plugin for testing async python code Aug 04, 2021 4 - Beta pytest ; extra == 'tests' `pytest-aiofiles `_ pytest fixtures for writing aiofiles tests with pyfakefs May 14, 2017 5 - Production/Stable N/A `pytest-aiohttp `_ pytest plugin for aiohttp support Dec 05, 2017 N/A pytest `pytest-aiohttp-client `_ Pytest `client` fixture for the Aiohttp Nov 01, 2020 N/A pytest (>=6) `pytest-aioresponses `_ py.test integration for aioresponses Jul 29, 2021 4 - Beta pytest (>=3.5.0) `pytest-aioworkers `_ A plugin to test aioworkers project with pytest Dec 04, 2019 4 - Beta pytest (>=3.5.0) `pytest-airflow `_ pytest support for airflow. Apr 03, 2019 3 - Alpha pytest (>=4.4.0) -`pytest-alembic `_ A pytest plugin for verifying alembic migrations. May 10, 2021 N/A pytest (>=1.0) +`pytest-alembic `_ A pytest plugin for verifying alembic migrations. Aug 04, 2021 N/A pytest (>=1.0) `pytest-allclose `_ Pytest fixture extending Numpy's allclose function Jul 30, 2019 5 - Production/Stable pytest `pytest-allure-adaptor `_ Plugin for py.test to generate allure xml reports Jan 10, 2018 N/A pytest (>=2.7.3) `pytest-allure-adaptor2 `_ Plugin for py.test to generate allure xml reports Oct 14, 2020 N/A pytest (>=2.7.3) @@ -63,7 +64,7 @@ name `pytest-attrib `_ pytest plugin to select tests based on attributes similar to the nose-attrib plugin May 24, 2016 4 - Beta N/A `pytest-austin `_ Austin plugin for pytest Oct 11, 2020 4 - Beta N/A `pytest-autochecklog `_ automatically check condition and log all the checks Apr 25, 2015 4 - Beta N/A -`pytest-automation `_ pytest plugin for building a test suite, using YAML files to extend pytest parameterize functionality. Jul 27, 2021 N/A pytest +`pytest-automation `_ pytest plugin for building a test suite, using YAML files to extend pytest parameterize functionality. Aug 05, 2021 N/A pytest `pytest-automock `_ Pytest plugin for automatical mocks creation Apr 22, 2020 N/A pytest ; extra == 'dev' `pytest-auto-parametrize `_ pytest plugin: avoid repeating arguments in parametrize Oct 02, 2016 3 - Alpha N/A `pytest-avoidance `_ Makes pytest skip tests that don not need rerunning May 23, 2019 4 - Beta pytest (>=3.5.0) @@ -83,7 +84,7 @@ name `pytest-benchmark `_ A ``pytest`` fixture for benchmarking code. It will group the tests into rounds that are calibrated to the chosen timer. Apr 17, 2021 5 - Production/Stable pytest (>=3.8) `pytest-bg-process `_ A simple plugin to use with pytest May 28, 2021 4 - Beta pytest (>=3.5.0) `pytest-bigchaindb `_ A BigchainDB plugin for pytest. May 28, 2021 4 - Beta N/A -`pytest-bigquery-mock `_ Provides a mock fixture for python bigquery client Jul 28, 2021 N/A pytest (>=5.0) +`pytest-bigquery-mock `_ Provides a mock fixture for python bigquery client Aug 05, 2021 N/A pytest (>=5.0) `pytest-black `_ A pytest plugin to enable format checking with black Oct 05, 2020 4 - Beta N/A `pytest-black-multipy `_ Allow '--black' on older Pythons Jan 14, 2021 5 - Production/Stable pytest (!=3.7.3,>=3.5) ; extra == 'testing' `pytest-blame `_ A pytest plugin helps developers to debug by providing useful commits history. May 04, 2019 N/A pytest (>=4.4.0) @@ -94,7 +95,7 @@ name `pytest-board `_ Local continuous test runner with pytest and watchdog. Jan 20, 2019 N/A N/A `pytest-bpdb `_ A py.test plug-in to enable drop to bpdb debugger on test failure. Jan 19, 2015 2 - Pre-Alpha N/A `pytest-bravado `_ Pytest-bravado automatically generates from OpenAPI specification client fixtures. Jul 19, 2021 N/A N/A -`pytest-breakword `_ Use breakword with pytest Jul 28, 2021 N/A pytest (>=6.2.4,<7.0.0) +`pytest-breakword `_ Use breakword with pytest Aug 04, 2021 N/A pytest (>=6.2.4,<7.0.0) `pytest-breed-adapter `_ A simple plugin to connect with breed-server Nov 07, 2018 4 - Beta pytest (>=3.5.0) `pytest-briefcase `_ A pytest plugin for running tests on a Briefcase project. Jun 14, 2020 4 - Beta pytest (>=3.5.0) `pytest-browser `_ A pytest plugin for console based browser test selection just after the collection phase Dec 10, 2016 3 - Alpha N/A @@ -209,7 +210,7 @@ name `pytest-discord `_ A pytest plugin to notify test results to a Discord channel. Mar 20, 2021 3 - Alpha pytest (!=6.0.0,<7,>=3.3.2) `pytest-django `_ A Django plugin for pytest. Jun 06, 2021 5 - Production/Stable pytest (>=5.4.0) `pytest-django-ahead `_ A Django plugin for pytest. Oct 27, 2016 5 - Production/Stable pytest (>=2.9) -`pytest-djangoapp `_ Nice pytest plugin to help you with Django pluggable application testing. Apr 10, 2021 4 - Beta N/A +`pytest-djangoapp `_ Nice pytest plugin to help you with Django pluggable application testing. Aug 04, 2021 4 - Beta N/A `pytest-django-cache-xdist `_ A djangocachexdist plugin for pytest May 12, 2020 4 - Beta N/A `pytest-django-casperjs `_ Integrate CasperJS with your django tests as a pytest fixture. Mar 15, 2015 2 - Pre-Alpha N/A `pytest-django-dotenv `_ Pytest plugin used to setup environment variables with django-dotenv Nov 26, 2019 4 - Beta pytest (>=2.6.0) @@ -218,6 +219,7 @@ name `pytest-django-haystack `_ Cleanup your Haystack indexes between tests Sep 03, 2017 5 - Production/Stable pytest (>=2.3.4) `pytest-django-ifactory `_ A model instance factory for pytest-django Jan 13, 2021 3 - Alpha N/A `pytest-django-lite `_ The bare minimum to integrate py.test with Django. Jan 30, 2014 N/A N/A +`pytest-django-liveserver-ssl `_ Jul 30, 2021 3 - Alpha N/A `pytest-django-model `_ A Simple Way to Test your Django Models Feb 14, 2019 4 - Beta N/A `pytest-django-ordering `_ A pytest plugin for preserving the order in which Django runs tests. Jul 25, 2019 5 - Production/Stable pytest (>=2.3.0) `pytest-django-queries `_ Generate performance reports from your django database performance tests. Mar 01, 2021 N/A N/A @@ -297,7 +299,7 @@ name `pytest-expecter `_ Better testing with expecter and pytest. Jul 08, 2020 5 - Production/Stable N/A `pytest-expectr `_ This plugin is used to expect multiple assert using pytest framework. Oct 05, 2018 N/A pytest (>=2.4.2) `pytest-explicit `_ A Pytest plugin to ignore certain marked tests by default Jun 15, 2021 5 - Production/Stable pytest -`pytest-exploratory `_ Interactive console for pytest. Jan 20, 2021 N/A pytest (>=5.3) +`pytest-exploratory `_ Interactive console for pytest. Aug 03, 2021 N/A pytest (>=5.3) `pytest-external-blockers `_ a special outcome for tests that are blocked for external reasons Oct 04, 2016 N/A N/A `pytest-extra-durations `_ A pytest plugin to get durations on a per-function basis and per module basis. Apr 21, 2020 4 - Beta pytest (>=3.5.0) `pytest-fabric `_ Provides test utilities to run fabric task tests by using docker containers Sep 12, 2018 5 - Production/Stable N/A @@ -354,7 +356,7 @@ name `pytest-gevent `_ Ensure that gevent is properly patched when invoking pytest Feb 25, 2020 N/A pytest `pytest-gherkin `_ A flexible framework for executing BDD gherkin tests Jul 27, 2019 3 - Alpha pytest (>=5.0.0) `pytest-ghostinspector `_ For finding/executing Ghost Inspector tests May 17, 2016 3 - Alpha N/A -`pytest-girder `_ A set of pytest fixtures for testing Girder applications. Jul 12, 2021 N/A N/A +`pytest-girder `_ A set of pytest fixtures for testing Girder applications. Aug 06, 2021 N/A N/A `pytest-git `_ Git repository fixture for py.test May 28, 2019 5 - Production/Stable pytest `pytest-gitcov `_ Pytest plugin for reporting on coverage of the last git commit. Jan 11, 2020 2 - Pre-Alpha N/A `pytest-git-fixtures `_ Pytest fixtures for testing with git. Mar 11, 2021 4 - Beta pytest @@ -391,7 +393,7 @@ name `pytest-httpbin `_ Easily test your HTTP library against a local copy of httpbin Feb 11, 2019 5 - Production/Stable N/A `pytest-http-mocker `_ Pytest plugin for http mocking (via https://github.com/vilus/mocker) Oct 20, 2019 N/A N/A `pytest-httpretty `_ A thin wrapper of HTTPretty for pytest Feb 16, 2014 3 - Alpha N/A -`pytest-httpserver `_ pytest-httpserver is a httpserver for pytest Mar 16, 2021 3 - Alpha pytest ; extra == 'dev' +`pytest-httpserver `_ pytest-httpserver is a httpserver for pytest Aug 06, 2021 3 - Alpha pytest ; extra == 'dev' `pytest-httpx `_ Send responses to httpx. Apr 27, 2021 5 - Production/Stable pytest (==6.*) `pytest-httpx-blockage `_ Disable httpx requests during a test run Apr 28, 2021 N/A pytest (>=6.2.3) `pytest-hue `_ Visualise PyTest status via your Phillips Hue lights May 09, 2019 N/A N/A @@ -448,7 +450,7 @@ name `pytest-leaks `_ A pytest plugin to trace resource leaks. Nov 27, 2019 1 - Planning N/A `pytest-level `_ Select tests of a given level or lower Oct 21, 2019 N/A pytest `pytest-libfaketime `_ A python-libfaketime plugin for pytest. Dec 22, 2018 4 - Beta pytest (>=3.0.0) -`pytest-libiio `_ A pytest plugin to manage interfacing with libiio contexts Jun 15, 2021 4 - Beta N/A +`pytest-libiio `_ A pytest plugin to manage interfacing with libiio contexts Aug 05, 2021 4 - Beta N/A `pytest-libnotify `_ Pytest plugin that shows notifications about the test run Apr 02, 2021 3 - Alpha pytest `pytest-ligo `_ Jan 16, 2020 4 - Beta N/A `pytest-lineno `_ A pytest plugin to show the line numbers of test functions Dec 04, 2020 N/A pytest @@ -584,7 +586,7 @@ name `pytest-pgsql `_ Pytest plugins and helpers for tests using a Postgres database. May 13, 2020 5 - Production/Stable pytest (>=3.0.0) `pytest-picked `_ Run the tests related to the changed files Dec 23, 2020 N/A pytest (>=3.5.0) `pytest-pigeonhole `_ Jun 25, 2018 5 - Production/Stable pytest (>=3.4) -`pytest-pikachu `_ Show surprise when tests are passing Sep 30, 2019 4 - Beta pytest +`pytest-pikachu `_ Show surprise when tests are passing Aug 05, 2021 5 - Production/Stable pytest `pytest-pilot `_ Slice in your test base thanks to powerful markers. Oct 09, 2020 5 - Production/Stable N/A `pytest-pings `_ 🦊 The pytest plugin for Firefox Telemetry 📊 Jun 29, 2019 3 - Alpha pytest (>=5.0.0) `pytest-pinned `_ A simple pytest plugin for pinning tests Jan 21, 2021 4 - Beta pytest (>=3.5.0) @@ -617,6 +619,7 @@ name `pytest-prometheus `_ Report test pass / failures to a Prometheus PushGateway Oct 03, 2017 N/A N/A `pytest-prosper `_ Test helpers for Prosper projects Sep 24, 2018 N/A N/A `pytest-pspec `_ A rspec format reporter for Python ptest Jun 02, 2020 4 - Beta pytest (>=3.0.0) +`pytest-ptera `_ Use ptera probes in tests Aug 04, 2021 N/A pytest (>=6.2.4,<7.0.0) `pytest-pudb `_ Pytest PuDB debugger integration Oct 25, 2018 3 - Alpha pytest (>=2.0) `pytest-purkinje `_ py.test plugin for purkinje test runner Oct 28, 2017 2 - Pre-Alpha N/A `pytest-pycharm `_ Plugin for py.test to enter PyCharm debugger on uncaught exceptions Aug 13, 2020 5 - Production/Stable pytest (>=2.3) @@ -635,6 +638,7 @@ name `pytest-pytorch `_ pytest plugin for a better developer experience when working with the PyTorch test suite May 25, 2021 4 - Beta pytest `pytest-qasync `_ Pytest support for qasync. Jul 12, 2021 4 - Beta pytest (>=5.4.0) `pytest-qatouch `_ Pytest plugin for uploading test results to your QA Touch Testrun. Jun 26, 2021 4 - Beta pytest (>=6.2.0) +`pytest-qgis `_ A pytest plugin for testing QGIS python plugins Aug 03, 2021 4 - Beta pytest (>=6.2.3) `pytest-qml `_ Run QML Tests with pytest Dec 02, 2020 4 - Beta pytest (>=6.0.0) `pytest-qt `_ pytest support for PyQt and PySide applications Jun 13, 2021 5 - Production/Stable pytest (>=3.0.0) `pytest-qt-app `_ QT app fixture for py.test Dec 23, 2015 5 - Production/Stable N/A @@ -712,13 +716,13 @@ name `pytest-sanic `_ a pytest plugin for Sanic Jul 27, 2021 N/A pytest (>=5.2) `pytest-sanity `_ Dec 07, 2020 N/A N/A `pytest-sa-pg `_ May 14, 2019 N/A N/A -`pytest-sbase `_ A complete web automation framework for end-to-end testing. Jul 31, 2021 5 - Production/Stable N/A +`pytest-sbase `_ A complete web automation framework for end-to-end testing. Aug 06, 2021 5 - Production/Stable N/A `pytest-scenario `_ pytest plugin for test scenarios Feb 06, 2017 3 - Alpha N/A `pytest-schema `_ 👍 Validate return values against a schema-like object in testing Aug 31, 2020 5 - Production/Stable pytest (>=3.5.0) `pytest-securestore `_ An encrypted password store for use within pytest cases Jun 19, 2019 4 - Beta N/A `pytest-select `_ A pytest plugin which allows to (de-)select tests from a file. Jan 18, 2019 3 - Alpha pytest (>=3.0) `pytest-selenium `_ pytest plugin for Selenium Sep 19, 2020 5 - Production/Stable pytest (>=5.0.0) -`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Jul 31, 2021 5 - Production/Stable N/A +`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Aug 06, 2021 5 - Production/Stable N/A `pytest-selenium-enhancer `_ pytest plugin for Selenium Nov 26, 2020 5 - Production/Stable N/A `pytest-selenium-pdiff `_ A pytest package implementing perceptualdiff for Selenium tests. Apr 06, 2017 2 - Pre-Alpha N/A `pytest-send-email `_ Send pytest execution result email Dec 04, 2019 N/A N/A @@ -761,7 +765,7 @@ name `pytest-sphinx `_ Doctest plugin for pytest with support for Sphinx-specific doctest-directives Aug 05, 2020 4 - Beta N/A `pytest-spiratest `_ Exports unit tests as test runs in SpiraTest/Team/Plan Apr 28, 2021 N/A N/A `pytest-splinter `_ Splinter plugin for pytest testing framework Dec 25, 2020 6 - Mature N/A -`pytest-split `_ Pytest plugin for splitting test suite based on test execution time Jul 21, 2021 4 - Beta pytest (>=5,<7) +`pytest-split `_ Pytest plugin for splitting test suite based on test execution time Aug 06, 2021 4 - Beta pytest (>=5,<7) `pytest-splitio `_ Split.io SDK integration for e2e tests Sep 22, 2020 N/A pytest (<7,>=5.0) `pytest-split-tests `_ A Pytest plugin for running a subset of your tests by splitting them in to equally sized groups. Forked from Mark Adams' original project pytest-test-groups. Jul 30, 2021 5 - Production/Stable pytest (>=2.5) `pytest-split-tests-tresorit `_ Feb 22, 2021 1 - Planning N/A @@ -810,7 +814,7 @@ name `pytest-test-groups `_ A Pytest plugin for running a subset of your tests by splitting them in to equally sized groups. Oct 25, 2016 5 - Production/Stable N/A `pytest-testinfra `_ Test infrastructures Jun 20, 2021 5 - Production/Stable pytest (!=3.0.2) `pytest-testlink-adaptor `_ pytest reporting plugin for testlink Dec 20, 2018 4 - Beta pytest (>=2.6) -`pytest-testmon `_ selects tests affected by changed files and methods Apr 28, 2021 4 - Beta N/A +`pytest-testmon `_ selects tests affected by changed files and methods Aug 05, 2021 4 - Beta N/A `pytest-testobject `_ Plugin to use TestObject Suites with Pytest Sep 24, 2019 4 - Beta pytest (>=3.1.1) `pytest-testrail `_ pytest plugin for creating TestRail runs and adding results Aug 27, 2020 N/A pytest (>=3.6) `pytest-testrail2 `_ A small example package Nov 17, 2020 N/A pytest (>=5) @@ -835,7 +839,7 @@ name `pytest-tipsi-testing `_ Better fixtures management. Various helpers Nov 04, 2020 4 - Beta pytest (>=3.3.0) `pytest-tldr `_ A pytest plugin that limits the output to just the things you need. Mar 12, 2021 4 - Beta pytest (>=3.5.0) `pytest-tm4j-reporter `_ Cloud Jira Test Management (TM4J) PyTest reporter plugin Sep 01, 2020 N/A pytest -`pytest-tmreport `_ this is a vue-element ui report for pytest Jul 30, 2021 N/A N/A +`pytest-tmreport `_ this is a vue-element ui report for pytest Aug 02, 2021 N/A N/A `pytest-todo `_ A small plugin for the pytest testing framework, marking TODO comments as failure May 23, 2019 4 - Beta pytest `pytest-tomato `_ Mar 01, 2019 5 - Production/Stable N/A `pytest-toolbelt `_ This is just a collection of utilities for pytest, but don't really belong in pytest proper. Aug 12, 2019 3 - Alpha N/A From 25c65616f4455ac78be1027c2fb4debef827c4e6 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sun, 8 Aug 2021 11:50:15 +0300 Subject: [PATCH 422/630] mark/expression: allow backslash characters in identifiers Fixes #8983. --- changelog/8983.bugfix.rst | 2 ++ src/_pytest/mark/expression.py | 4 ++-- testing/test_mark_expression.py | 16 +++++++++++++++- 3 files changed, 19 insertions(+), 3 deletions(-) create mode 100644 changelog/8983.bugfix.rst diff --git a/changelog/8983.bugfix.rst b/changelog/8983.bugfix.rst new file mode 100644 index 00000000000..403d421d6bd --- /dev/null +++ b/changelog/8983.bugfix.rst @@ -0,0 +1,2 @@ +The test selection options ``pytest -k`` and ``pytest -m`` now support matching names containing backslash (`\\`) characters. +Backslashes are treated literally, not as escape characters (the values being matched against are already escaped). diff --git a/src/_pytest/mark/expression.py b/src/_pytest/mark/expression.py index 72e2ed4bdb7..20b424e1646 100644 --- a/src/_pytest/mark/expression.py +++ b/src/_pytest/mark/expression.py @@ -6,7 +6,7 @@ expr: and_expr ('or' and_expr)* and_expr: not_expr ('and' not_expr)* not_expr: 'not' not_expr | '(' expr ')' | ident -ident: (\w|:|\+|-|\.|\[|\])+ +ident: (\w|:|\+|-|\.|\[|\]|\\)+ The semantics are: @@ -88,7 +88,7 @@ def lex(self, input: str) -> Iterator[Token]: yield Token(TokenType.RPAREN, ")", pos) pos += 1 else: - match = re.match(r"(:?\w|:|\+|-|\.|\[|\])+", input[pos:]) + match = re.match(r"(:?\w|:|\+|-|\.|\[|\]|\\)+", input[pos:]) if match: value = match.group(0) if value == "or": diff --git a/testing/test_mark_expression.py b/testing/test_mark_expression.py index d37324f51cd..c758ee66761 100644 --- a/testing/test_mark_expression.py +++ b/testing/test_mark_expression.py @@ -66,6 +66,20 @@ def test_syntax_oddeties(expr: str, expected: bool) -> None: assert evaluate(expr, matcher) is expected +def test_backslash_not_treated_specially() -> None: + r"""When generating nodeids, if the source name contains special characters + like a newline, they are escaped into two characters like \n. Therefore, a + user will never need to insert a literal newline, only \n (two chars). So + mark expressions themselves do not support escaping, instead they treat + backslashes as regular identifier characters.""" + matcher = {r"\nfoo\n"}.__contains__ + + assert evaluate(r"\nfoo\n", matcher) + assert not evaluate(r"foo", matcher) + with pytest.raises(ParseError): + evaluate("\nfoo\n", matcher) + + @pytest.mark.parametrize( ("expr", "column", "message"), ( @@ -129,6 +143,7 @@ def test_syntax_errors(expr: str, column: int, message: str) -> None: ":::", "a:::c", "a+-b", + r"\nhe\\l\lo\n\t\rbye", "אבגד", "aaאבגדcc", "a[bcd]", @@ -156,7 +171,6 @@ def test_valid_idents(ident: str) -> None: "ident", ( "/", - "\\", "^", "*", "=", From 2439729413e701a19398316711ec5d795ae2ec63 Mon Sep 17 00:00:00 2001 From: Terje Runde Date: Mon, 9 Aug 2021 15:48:47 +0200 Subject: [PATCH 423/630] logging: Make it possible to add cli colors to custom log levels Closes #8803 PR #8804 Co-authored-by: Bruno Oliveira Co-authored-by: Terje Runde Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> --- AUTHORS | 1 + changelog/8803.improvement.rst | 9 ++++++++ doc/en/how-to/logging.rst | 24 ++++++++++++++++++++ src/_pytest/logging.py | 41 +++++++++++++++++++++++----------- 4 files changed, 62 insertions(+), 13 deletions(-) create mode 100644 changelog/8803.improvement.rst diff --git a/AUTHORS b/AUTHORS index f68a750954c..8f184f353ac 100644 --- a/AUTHORS +++ b/AUTHORS @@ -309,6 +309,7 @@ Tanvi Mehta Tarcisio Fischer Tareq Alayan Ted Xiao +Terje Runde Thomas Grainger Thomas Hisch Tim Hoffmann diff --git a/changelog/8803.improvement.rst b/changelog/8803.improvement.rst new file mode 100644 index 00000000000..6080070025f --- /dev/null +++ b/changelog/8803.improvement.rst @@ -0,0 +1,9 @@ +It is now possible to add colors to custom log levels on cli log. + +By using :func:`add_color_level <_pytest.logging.add_color_level` from a ``pytest_configure`` hook, colors can be added:: + + logging_plugin = config.pluginmanager.get_plugin('logging-plugin') + logging_plugin.log_cli_handler.formatter.add_color_level(logging.INFO, 'cyan') + logging_plugin.log_cli_handler.formatter.add_color_level(logging.SPAM, 'blue') + + See :ref:`log_colors` for more information. diff --git a/doc/en/how-to/logging.rst b/doc/en/how-to/logging.rst index ef477dc4f8c..7d8388116bd 100644 --- a/doc/en/how-to/logging.rst +++ b/doc/en/how-to/logging.rst @@ -219,6 +219,30 @@ option names are: You can call ``set_log_path()`` to customize the log_file path dynamically. This functionality is considered **experimental**. +.. _log_colors: + +Customizing Colors +^^^^^^^^^^^^^^^^^^ + +Log levels are colored if colored terminal output is enabled. Changing +from default colors or putting color on custom log levels is supported +through ``add_color_level()``. Example: + +.. code-block:: python + + @pytest.hookimpl + def pytest_configure(config): + logging_plugin = config.pluginmanager.get_plugin("logging-plugin") + + # Change color on existing log level + logging_plugin.log_cli_handler.formatter.add_color_level(logging.INFO, "cyan") + + # Add color to a custom log level (a custom log level `SPAM` is already set up) + logging_plugin.log_cli_handler.formatter.add_color_level(logging.SPAM, "blue") +.. warning:: + + This feature and its API are considered **experimental** and might change + between releases without a deprecation notice. .. _log_release_notes: Release notes diff --git a/src/_pytest/logging.py b/src/_pytest/logging.py index 7ed1820bb31..9580d39f9bc 100644 --- a/src/_pytest/logging.py +++ b/src/_pytest/logging.py @@ -63,28 +63,43 @@ class ColoredLevelFormatter(logging.Formatter): def __init__(self, terminalwriter: TerminalWriter, *args, **kwargs) -> None: super().__init__(*args, **kwargs) + self._terminalwriter = terminalwriter self._original_fmt = self._style._fmt self._level_to_fmt_mapping: Dict[int, str] = {} + for level, color_opts in self.LOGLEVEL_COLOROPTS.items(): + self.add_color_level(level, *color_opts) + + def add_color_level(self, level: int, *color_opts: str) -> None: + """Add or update color opts for a log level. + + :param level: + Log level to apply a style to, e.g. ``logging.INFO``. + :param color_opts: + ANSI escape sequence color options. Capitalized colors indicates + background color, i.e. ``'green', 'Yellow', 'bold'`` will give bold + green text on yellow background. + + .. warning:: + This is an experimental API. + """ + assert self._fmt is not None levelname_fmt_match = self.LEVELNAME_FMT_REGEX.search(self._fmt) if not levelname_fmt_match: return levelname_fmt = levelname_fmt_match.group() - for level, color_opts in self.LOGLEVEL_COLOROPTS.items(): - formatted_levelname = levelname_fmt % { - "levelname": logging.getLevelName(level) - } - - # add ANSI escape sequences around the formatted levelname - color_kwargs = {name: True for name in color_opts} - colorized_formatted_levelname = terminalwriter.markup( - formatted_levelname, **color_kwargs - ) - self._level_to_fmt_mapping[level] = self.LEVELNAME_FMT_REGEX.sub( - colorized_formatted_levelname, self._fmt - ) + formatted_levelname = levelname_fmt % {"levelname": logging.getLevelName(level)} + + # add ANSI escape sequences around the formatted levelname + color_kwargs = {name: True for name in color_opts} + colorized_formatted_levelname = self._terminalwriter.markup( + formatted_levelname, **color_kwargs + ) + self._level_to_fmt_mapping[level] = self.LEVELNAME_FMT_REGEX.sub( + colorized_formatted_levelname, self._fmt + ) def format(self, record: logging.LogRecord) -> str: fmt = self._level_to_fmt_mapping.get(record.levelno, self._original_fmt) From 108f1d7e5e912b4f83eb521c28df185f7ebc2367 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 9 Aug 2021 18:53:50 +0000 Subject: [PATCH 424/630] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v2.23.1 → v2.23.3](https://github.com/asottile/pyupgrade/compare/v2.23.1...v2.23.3) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 214e59e2182..f92d5a5e987 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -34,7 +34,7 @@ repos: - id: reorder-python-imports args: ['--application-directories=.:src', --py36-plus] - repo: https://github.com/asottile/pyupgrade - rev: v2.23.1 + rev: v2.23.3 hooks: - id: pyupgrade args: [--py36-plus] From 7770dacb8dc7ec8551114970a15200a8f2cf5306 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=89loi=20Rivard?= Date: Fri, 11 Jun 2021 17:04:27 +0200 Subject: [PATCH 425/630] pygments themes are customizable --- AUTHORS | 1 + changelog/7132.feature.rst | 1 + doc/en/reference/customize.rst | 8 +++++ doc/en/reference/reference.rst | 8 +++++ src/_pytest/_io/terminalwriter.py | 31 +++++++++++++++--- testing/test_terminal.py | 54 +++++++++++++++++++++++++++++++ 6 files changed, 99 insertions(+), 4 deletions(-) create mode 100644 changelog/7132.feature.rst diff --git a/AUTHORS b/AUTHORS index 8f184f353ac..d5a10710306 100644 --- a/AUTHORS +++ b/AUTHORS @@ -106,6 +106,7 @@ Edson Tadeu M. Manoel Eduardo Schettino Eli Boyarski Elizaveta Shashkova +Éloi Rivard Endre Galaczi Eric Hunsberger Eric Liu diff --git a/changelog/7132.feature.rst b/changelog/7132.feature.rst new file mode 100644 index 00000000000..9fb735ee153 --- /dev/null +++ b/changelog/7132.feature.rst @@ -0,0 +1 @@ +Added two environment variables :envvar:`PYTEST_THEME` and :envvar:`PYTEST_THEME_MODE` to let the users customize the pygments theme used. diff --git a/doc/en/reference/customize.rst b/doc/en/reference/customize.rst index d6fb5c6b2dd..035d0f7ade2 100644 --- a/doc/en/reference/customize.rst +++ b/doc/en/reference/customize.rst @@ -239,3 +239,11 @@ Builtin configuration file options ---------------------------------------------- For the full list of options consult the :ref:`reference documentation `. + +Syntax highlighting theme customization +--------------------------------------- + +The syntax highlighting themes used by pytest can be customized using two environment variables: + +- :envvar:`PYTEST_THEME` sets a `pygment style `_ to use. +- :envvar:`PYTEST_THEME_MODE` sets this style to *light* or *dark*. diff --git a/doc/en/reference/reference.rst b/doc/en/reference/reference.rst index b9b68c01d79..db49b0f1d44 100644 --- a/doc/en/reference/reference.rst +++ b/doc/en/reference/reference.rst @@ -1075,6 +1075,14 @@ Contains comma-separated list of modules that should be loaded as plugins: export PYTEST_PLUGINS=mymodule.plugin,xdist +.. envvar:: PYTEST_THEME + +Sets a `pygment style `_ to use for the code output. + +.. envvar:: PYTEST_THEME_MODE + +Sets the :envvar:`PYTEST_THEME` to be either *dark* or *light*. + .. envvar:: PY_COLORS When set to ``1``, pytest will use color in terminal output. diff --git a/src/_pytest/_io/terminalwriter.py b/src/_pytest/_io/terminalwriter.py index 8edf4cd75fa..379035d858c 100644 --- a/src/_pytest/_io/terminalwriter.py +++ b/src/_pytest/_io/terminalwriter.py @@ -195,16 +195,39 @@ def _write_source(self, lines: Sequence[str], indents: Sequence[str] = ()) -> No def _highlight(self, source: str) -> str: """Highlight the given source code if we have markup support.""" + from _pytest.config.exceptions import UsageError + if not self.hasmarkup or not self.code_highlight: return source try: from pygments.formatters.terminal import TerminalFormatter from pygments.lexers.python import PythonLexer from pygments import highlight + import pygments.util except ImportError: return source else: - highlighted: str = highlight( - source, PythonLexer(), TerminalFormatter(bg="dark") - ) - return highlighted + try: + highlighted: str = highlight( + source, + PythonLexer(), + TerminalFormatter( + bg=os.getenv("PYTEST_THEME_MODE", "dark"), + style=os.getenv("PYTEST_THEME"), + ), + ) + return highlighted + except pygments.util.ClassNotFound: + raise UsageError( + "PYTEST_THEME environment variable had an invalid value: '{}'. " + "Only valid pygment styles are allowed.".format( + os.getenv("PYTEST_THEME") + ) + ) + except pygments.util.OptionError: + raise UsageError( + "PYTEST_THEME_MODE environment variable had an invalid value: '{}'. " + "The only allowed values are 'dark' and 'light'.".format( + os.getenv("PYTEST_THEME_MODE") + ) + ) diff --git a/testing/test_terminal.py b/testing/test_terminal.py index 3e3502b5ca9..c8834b545fb 100644 --- a/testing/test_terminal.py +++ b/testing/test_terminal.py @@ -2408,6 +2408,60 @@ def test_foo(): ) ) + def test_code_highlight_custom_theme( + self, pytester: Pytester, color_mapping, monkeypatch: MonkeyPatch + ) -> None: + pytester.makepyfile( + """ + def test_foo(): + assert 1 == 10 + """ + ) + monkeypatch.setenv("PYTEST_THEME", "solarized-dark") + monkeypatch.setenv("PYTEST_THEME_MODE", "dark") + result = pytester.runpytest("--color=yes") + result.stdout.fnmatch_lines( + color_mapping.format_for_fnmatch( + [ + " {kw}def{hl-reset} {function}test_foo{hl-reset}():", + "> {kw}assert{hl-reset} {number}1{hl-reset} == {number}10{hl-reset}", + "{bold}{red}E assert 1 == 10{reset}", + ] + ) + ) + + def test_code_highlight_invalid_theme( + self, pytester: Pytester, color_mapping, monkeypatch: MonkeyPatch + ) -> None: + pytester.makepyfile( + """ + def test_foo(): + assert 1 == 10 + """ + ) + monkeypatch.setenv("PYTEST_THEME", "invalid") + result = pytester.runpytest_subprocess("--color=yes") + result.stderr.fnmatch_lines( + "ERROR: PYTEST_THEME environment variable had an invalid value: 'invalid'. " + "Only valid pygment styles are allowed." + ) + + def test_code_highlight_invalid_theme_mode( + self, pytester: Pytester, color_mapping, monkeypatch: MonkeyPatch + ) -> None: + pytester.makepyfile( + """ + def test_foo(): + assert 1 == 10 + """ + ) + monkeypatch.setenv("PYTEST_THEME_MODE", "invalid") + result = pytester.runpytest_subprocess("--color=yes") + result.stderr.fnmatch_lines( + "ERROR: PYTEST_THEME_MODE environment variable had an invalid value: 'invalid'. " + "The only allowed values are 'dark' and 'light'." + ) + def test_raw_skip_reason_skipped() -> None: report = SimpleNamespace() From 1cbb67f71b4722fe632f2044bb5f1899c0522c4f Mon Sep 17 00:00:00 2001 From: pytest bot Date: Sun, 15 Aug 2021 00:09:52 +0000 Subject: [PATCH 426/630] [automated] Update plugin list --- doc/en/reference/plugin_list.rst | 52 +++++++++++++++++--------------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/doc/en/reference/plugin_list.rst b/doc/en/reference/plugin_list.rst index dc19d7df80b..5ff8790e33a 100644 --- a/doc/en/reference/plugin_list.rst +++ b/doc/en/reference/plugin_list.rst @@ -6,7 +6,7 @@ Plugin List PyPI projects that match "pytest-\*" are considered plugins and are listed automatically. Packages classified as inactive are excluded. -This list contains 904 plugins. +This list contains 908 plugins. ============================================================================================================== ======================================================================================================================================================================== ============== ===================== ================================================ name summary last release status requires @@ -17,7 +17,7 @@ name `pytest-adf `_ Pytest plugin for writing Azure Data Factory integration tests May 10, 2021 4 - Beta pytest (>=3.5.0) `pytest-adf-azure-identity `_ Pytest plugin for writing Azure Data Factory integration tests Mar 06, 2021 4 - Beta pytest (>=3.5.0) `pytest-aggreport `_ pytest plugin for pytest-repeat that generate aggregate report of the same test cases with additional statistics details. Mar 07, 2021 4 - Beta pytest (>=6.2.2) -`pytest-aio `_ Pytest plugin for testing async python code Aug 04, 2021 4 - Beta pytest ; extra == 'tests' +`pytest-aio `_ Pytest plugin for testing async python code Aug 11, 2021 4 - Beta pytest ; extra == 'tests' `pytest-aiofiles `_ pytest fixtures for writing aiofiles tests with pyfakefs May 14, 2017 5 - Production/Stable N/A `pytest-aiohttp `_ pytest plugin for aiohttp support Dec 05, 2017 N/A pytest `pytest-aiohttp-client `_ Pytest `client` fixture for the Aiohttp Nov 01, 2020 N/A pytest (>=6) @@ -64,7 +64,7 @@ name `pytest-attrib `_ pytest plugin to select tests based on attributes similar to the nose-attrib plugin May 24, 2016 4 - Beta N/A `pytest-austin `_ Austin plugin for pytest Oct 11, 2020 4 - Beta N/A `pytest-autochecklog `_ automatically check condition and log all the checks Apr 25, 2015 4 - Beta N/A -`pytest-automation `_ pytest plugin for building a test suite, using YAML files to extend pytest parameterize functionality. Aug 05, 2021 N/A pytest +`pytest-automation `_ pytest plugin for building a test suite, using YAML files to extend pytest parameterize functionality. Aug 11, 2021 N/A pytest `pytest-automock `_ Pytest plugin for automatical mocks creation Apr 22, 2020 N/A pytest ; extra == 'dev' `pytest-auto-parametrize `_ pytest plugin: avoid repeating arguments in parametrize Oct 02, 2016 3 - Alpha N/A `pytest-avoidance `_ Makes pytest skip tests that don not need rerunning May 23, 2019 4 - Beta pytest (>=3.5.0) @@ -121,7 +121,6 @@ name `pytest-chalice `_ A set of py.test fixtures for AWS Chalice Jul 01, 2020 4 - Beta N/A `pytest-change-report `_ turn . into √,turn F into x Sep 14, 2020 N/A pytest `pytest-chdir `_ A pytest fixture for changing current working directory Jan 28, 2020 N/A pytest (>=5.0.0,<6.0.0) -`pytest-check `_ A pytest plugin that allows multiple failures per test. Dec 27, 2020 5 - Production/Stable N/A `pytest-checkdocs `_ check the README when running tests Jul 31, 2021 5 - Production/Stable pytest (>=4.6) ; extra == 'testing' `pytest-checkipdb `_ plugin to check if there are ipdb debugs left Jul 22, 2020 5 - Production/Stable pytest (>=2.9.2) `pytest-check-links `_ Check links in files Jul 29, 2020 N/A pytest (>=4.6) @@ -136,7 +135,7 @@ name `pytest-cloud `_ Distributed tests planner plugin for pytest testing framework. Oct 05, 2020 6 - Mature N/A `pytest-cloudflare-worker `_ pytest plugin for testing cloudflare workers Mar 30, 2021 4 - Beta pytest (>=6.0.0) `pytest-cobra `_ PyTest plugin for testing Smart Contracts for Ethereum blockchain. Jun 29, 2019 3 - Alpha pytest (<4.0.0,>=3.7.1) -`pytest-codeblocks `_ Test code blocks in your READMEs May 25, 2021 4 - Beta pytest (>=6) +`pytest-codeblocks `_ Test code blocks in your READMEs Aug 12, 2021 4 - Beta pytest (>=6) `pytest-codecheckers `_ pytest plugin to add source code sanity checks (pep8 and friends) Feb 13, 2010 N/A N/A `pytest-codecov `_ Pytest plugin for uploading pytest-cov results to codecov.io May 05, 2021 4 - Beta pytest (>=4.6.0) `pytest-codegen `_ Automatically create pytest test signatures Aug 23, 2020 2 - Pre-Alpha N/A @@ -269,13 +268,13 @@ name `pytest-elements `_ Tool to help automate user interfaces Jan 13, 2021 N/A pytest (>=5.4,<6.0) `pytest-elk-reporter `_ A simple plugin to use with pytest Jan 24, 2021 4 - Beta pytest (>=3.5.0) `pytest-email `_ Send execution result email Jul 08, 2020 N/A pytest -`pytest-embedded `_ pytest embedded plugin Jul 09, 2021 N/A pytest (>=6.2.0) -`pytest-embedded-idf `_ pytest embedded plugin for esp-idf project Jul 09, 2021 N/A N/A -`pytest-embedded-jtag `_ pytest embedded plugin for testing with jtag Jul 09, 2021 N/A N/A -`pytest-embedded-qemu `_ pytest embedded plugin for qemu, not target chip Jul 09, 2021 N/A N/A +`pytest-embedded `_ pytest embedded plugin Aug 12, 2021 N/A pytest (>=6.2.0) +`pytest-embedded-idf `_ pytest embedded plugin for esp-idf project Aug 12, 2021 N/A N/A +`pytest-embedded-jtag `_ pytest embedded plugin for testing with jtag Aug 12, 2021 N/A N/A +`pytest-embedded-qemu `_ pytest embedded plugin for qemu, not target chip Aug 12, 2021 N/A N/A `pytest-embedded-qemu-idf `_ pytest embedded plugin for esp-idf project by qemu, not target chip Jun 29, 2021 N/A N/A -`pytest-embedded-serial `_ pytest embedded plugin for testing serial ports Jul 09, 2021 N/A N/A -`pytest-embedded-serial-esp `_ pytest embedded plugin for testing espressif boards via serial ports Jul 09, 2021 N/A N/A +`pytest-embedded-serial `_ pytest embedded plugin for testing serial ports Aug 12, 2021 N/A N/A +`pytest-embedded-serial-esp `_ pytest embedded plugin for testing espressif boards via serial ports Aug 12, 2021 N/A N/A `pytest-emoji `_ A pytest plugin that adds emojis to your test result report Feb 19, 2019 4 - Beta pytest (>=4.2.1) `pytest-emoji-output `_ Pytest plugin to represent test output with emoji support Jun 06, 2021 4 - Beta pytest (==6.0.1) `pytest-enabler `_ Enable installed pytest plugins Jan 19, 2021 5 - Production/Stable pytest (!=3.7.3,>=3.5) ; extra == 'testing' @@ -333,7 +332,7 @@ name `pytest-fixtures `_ Common fixtures for pytest May 01, 2019 5 - Production/Stable N/A `pytest-fixture-tools `_ Plugin for pytest which provides tools for fixtures Aug 18, 2020 6 - Mature pytest `pytest-flake8 `_ pytest plugin to check FLAKE8 requirements Dec 16, 2020 4 - Beta pytest (>=3.5) -`pytest-flake8dir `_ A pytest fixture for testing flake8 plugins. May 10, 2021 5 - Production/Stable pytest +`pytest-flake8-path `_ A pytest fixture for testing flake8 plugins. Aug 11, 2021 5 - Production/Stable pytest `pytest-flakefinder `_ Runs tests multiple times to expose flakiness. Jul 28, 2020 4 - Beta pytest (>=2.7.1) `pytest-flakes `_ pytest plugin to check source code with pyflakes Nov 28, 2020 5 - Production/Stable N/A `pytest-flaptastic `_ Flaptastic py.test plugin Mar 17, 2019 N/A N/A @@ -372,7 +371,7 @@ name `pytest-hammertime `_ Display "🔨 " instead of "." for passed pytest tests. Jul 28, 2018 N/A pytest `pytest-harvest `_ Store data created during your pytest tests execution, and retrieve it at the end of the session, e.g. for applicative benchmarking purposes. Apr 01, 2021 5 - Production/Stable N/A `pytest-helm-chart `_ A plugin to provide different types and configs of Kubernetes clusters that can be used for testing. Jun 15, 2020 4 - Beta pytest (>=5.4.2,<6.0.0) -`pytest-helm-charts `_ A plugin to provide different types and configs of Kubernetes clusters that can be used for testing. Jul 30, 2021 4 - Beta pytest (>=6.1.2,<7.0.0) +`pytest-helm-charts `_ A plugin to provide different types and configs of Kubernetes clusters that can be used for testing. Aug 13, 2021 4 - Beta pytest (>=6.1.2,<7.0.0) `pytest-helper `_ Functions to help in using the pytest testing framework May 31, 2019 5 - Production/Stable N/A `pytest-helpers `_ pytest helpers May 17, 2020 N/A pytest `pytest-helpers-namespace `_ Pytest Helpers Namespace Plugin Apr 29, 2021 5 - Production/Stable pytest (>=6.0.0) @@ -394,7 +393,7 @@ name `pytest-http-mocker `_ Pytest plugin for http mocking (via https://github.com/vilus/mocker) Oct 20, 2019 N/A N/A `pytest-httpretty `_ A thin wrapper of HTTPretty for pytest Feb 16, 2014 3 - Alpha N/A `pytest-httpserver `_ pytest-httpserver is a httpserver for pytest Aug 06, 2021 3 - Alpha pytest ; extra == 'dev' -`pytest-httpx `_ Send responses to httpx. Apr 27, 2021 5 - Production/Stable pytest (==6.*) +`pytest-httpx `_ Send responses to httpx. Aug 11, 2021 5 - Production/Stable pytest (==6.*) `pytest-httpx-blockage `_ Disable httpx requests during a test run Apr 28, 2021 N/A pytest (>=6.2.3) `pytest-hue `_ Visualise PyTest status via your Phillips Hue lights May 09, 2019 N/A N/A `pytest-hylang `_ Pytest plugin to allow running tests written in hylang Mar 28, 2021 N/A pytest @@ -497,6 +496,7 @@ name `pytest-mocha `_ pytest plugin to display test execution output like a mochajs Apr 02, 2020 4 - Beta pytest (>=5.4.0) `pytest-mock `_ Thin-wrapper around the mock package for easier use with pytest May 06, 2021 5 - Production/Stable pytest (>=5.0) `pytest-mock-api `_ A mock API server with configurable routes and responses available as a fixture. Feb 13, 2019 1 - Planning pytest (>=4.0.0) +`pytest-mock-generator `_ A pytest fixture wrapper for https://pypi.org/project/mock-generator Aug 10, 2021 5 - Production/Stable N/A `pytest-mock-helper `_ Help you mock HTTP call and generate mock code Jan 24, 2018 N/A pytest `pytest-mockito `_ Base fixtures for mockito Jul 11, 2018 4 - Beta N/A `pytest-mockredis `_ An in-memory mock of a Redis server that runs in a separate thread. This is to be used for unit-tests that require a Redis database. Jan 02, 2018 2 - Pre-Alpha N/A @@ -520,6 +520,7 @@ name `pytest-multi-check `_ Pytest-плагин, реализует возможность мульти проверок и мягких проверок Jun 03, 2021 N/A pytest `pytest-multihost `_ Utility for writing multi-host tests for pytest Apr 07, 2020 4 - Beta N/A `pytest-multilog `_ Multi-process logs handling and other helpers for pytest Jun 10, 2021 N/A N/A +`pytest-multithreading `_ a pytest plugin for th and concurrent testing Aug 12, 2021 N/A pytest (>=3.6) `pytest-mutagen `_ Add the mutation testing feature to pytest Jul 24, 2020 N/A pytest (>=5.4) `pytest-mypy `_ Mypy static type checker plugin for Pytest Mar 21, 2021 4 - Beta pytest (>=3.5) `pytest-mypyd `_ Mypy static type checker plugin for Pytest Aug 20, 2019 4 - Beta pytest (<4.7,>=2.8) ; python_version < "3.5" @@ -556,7 +557,7 @@ name `pytest-oot `_ Run object-oriented tests in a simple format Sep 18, 2016 4 - Beta N/A `pytest-openfiles `_ Pytest plugin for detecting inadvertent open file handles Apr 16, 2020 3 - Alpha pytest (>=4.6) `pytest-opentmi `_ pytest plugin for publish results to opentmi Feb 26, 2021 5 - Production/Stable pytest (>=5.0) -`pytest-operator `_ Fixtures for Operators Jun 28, 2021 N/A N/A +`pytest-operator `_ Fixtures for Operators Aug 10, 2021 N/A N/A `pytest-optional `_ include/exclude values of fixtures in pytest Oct 07, 2015 N/A N/A `pytest-optional-tests `_ Easy declaration of optional tests (i.e., that are not run by default) Jul 09, 2019 4 - Beta pytest (>=4.5.0) `pytest-orchestration `_ A pytest plugin for orchestrating tests Jul 18, 2019 N/A N/A @@ -651,12 +652,12 @@ name `pytest-raisesregexp `_ Simple pytest plugin to look for regex in Exceptions Dec 18, 2015 N/A N/A `pytest-raisin `_ Plugin enabling the use of exception instances with pytest.raises Jun 25, 2020 N/A pytest `pytest-random `_ py.test plugin to randomize tests Apr 28, 2013 3 - Alpha N/A -`pytest-randomly `_ Pytest plugin to randomly order tests and control random.seed. May 10, 2021 5 - Production/Stable pytest +`pytest-randomly `_ Pytest plugin to randomly order tests and control random.seed. Aug 13, 2021 5 - Production/Stable pytest `pytest-randomness `_ Pytest plugin about random seed management May 30, 2019 3 - Alpha N/A `pytest-random-num `_ Randomise the order in which pytest tests are run with some control over the randomness Oct 19, 2020 5 - Production/Stable N/A `pytest-random-order `_ Randomise the order in which pytest tests are run with some control over the randomness Nov 30, 2018 5 - Production/Stable pytest (>=3.0.0) `pytest-readme `_ Test your README.md file Dec 28, 2014 5 - Production/Stable N/A -`pytest-reana `_ Pytest fixtures for REANA. Jun 07, 2021 3 - Alpha N/A +`pytest-reana `_ Pytest fixtures for REANA. Aug 09, 2021 3 - Alpha N/A `pytest-recording `_ A pytest plugin that allows you recording of network interactions via VCR.py Jul 08, 2021 4 - Beta pytest (>=3.5.0) `pytest-recordings `_ Provides pytest plugins for reporting request/response traffic, screenshots, and more to ReportPortal Aug 13, 2020 N/A N/A `pytest-redis `_ Redis fixtures and fixture factories for Pytest. May 25, 2021 5 - Production/Stable pytest @@ -694,9 +695,9 @@ name `pytest-resource-path `_ Provides path for uniform access to test resources in isolated directory May 01, 2021 5 - Production/Stable pytest (>=3.5.0) `pytest-responsemock `_ Simplified requests calls mocking for pytest Oct 10, 2020 5 - Production/Stable N/A `pytest-responses `_ py.test integration for responses Apr 26, 2021 N/A pytest (>=2.5) -`pytest-restrict `_ Pytest plugin to restrict the test types allowed May 10, 2021 5 - Production/Stable pytest +`pytest-restrict `_ Pytest plugin to restrict the test types allowed Aug 12, 2021 5 - Production/Stable pytest `pytest-rethinkdb `_ A RethinkDB plugin for pytest. Jul 24, 2016 4 - Beta N/A -`pytest-reverse `_ Pytest plugin to reverse test order. May 10, 2021 5 - Production/Stable pytest +`pytest-reverse `_ Pytest plugin to reverse test order. Aug 12, 2021 5 - Production/Stable pytest `pytest-ringo `_ pytest plugin to test webapplications using the Ringo webframework Sep 27, 2017 3 - Alpha N/A `pytest-rng `_ Fixtures for seeding tests and making randomness reproducible Aug 08, 2019 5 - Production/Stable pytest `pytest-roast `_ pytest plugin for ROAST configuration override and fixtures Jul 29, 2021 5 - Production/Stable pytest @@ -710,19 +711,19 @@ name `pytest-runner `_ Invoke py.test as distutils command with dependency resolution May 19, 2021 5 - Production/Stable pytest (>=4.6) ; extra == 'testing' `pytest-salt `_ Pytest Salt Plugin Jan 27, 2020 4 - Beta N/A `pytest-salt-containers `_ A Pytest plugin that builds and creates docker containers Nov 09, 2016 4 - Beta N/A -`pytest-salt-factories `_ Pytest Salt Plugin Jun 20, 2021 4 - Beta pytest (>=6.0.0) +`pytest-salt-factories `_ Pytest Salt Plugin Aug 14, 2021 4 - Beta pytest (>=6.0.0) `pytest-salt-from-filenames `_ Simple PyTest Plugin For Salt's Test Suite Specifically Jan 29, 2019 4 - Beta pytest (>=4.1) `pytest-salt-runtests-bridge `_ Simple PyTest Plugin For Salt's Test Suite Specifically Dec 05, 2019 4 - Beta pytest (>=4.1) `pytest-sanic `_ a pytest plugin for Sanic Jul 27, 2021 N/A pytest (>=5.2) `pytest-sanity `_ Dec 07, 2020 N/A N/A `pytest-sa-pg `_ May 14, 2019 N/A N/A -`pytest-sbase `_ A complete web automation framework for end-to-end testing. Aug 06, 2021 5 - Production/Stable N/A +`pytest-sbase `_ A complete web automation framework for end-to-end testing. Aug 11, 2021 5 - Production/Stable N/A `pytest-scenario `_ pytest plugin for test scenarios Feb 06, 2017 3 - Alpha N/A `pytest-schema `_ 👍 Validate return values against a schema-like object in testing Aug 31, 2020 5 - Production/Stable pytest (>=3.5.0) `pytest-securestore `_ An encrypted password store for use within pytest cases Jun 19, 2019 4 - Beta N/A `pytest-select `_ A pytest plugin which allows to (de-)select tests from a file. Jan 18, 2019 3 - Alpha pytest (>=3.0) `pytest-selenium `_ pytest plugin for Selenium Sep 19, 2020 5 - Production/Stable pytest (>=5.0.0) -`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Aug 06, 2021 5 - Production/Stable N/A +`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Aug 11, 2021 5 - Production/Stable N/A `pytest-selenium-enhancer `_ pytest plugin for Selenium Nov 26, 2020 5 - Production/Stable N/A `pytest-selenium-pdiff `_ A pytest package implementing perceptualdiff for Selenium tests. Apr 06, 2017 2 - Pre-Alpha N/A `pytest-send-email `_ Send pytest execution result email Dec 04, 2019 N/A N/A @@ -770,7 +771,7 @@ name `pytest-split-tests `_ A Pytest plugin for running a subset of your tests by splitting them in to equally sized groups. Forked from Mark Adams' original project pytest-test-groups. Jul 30, 2021 5 - Production/Stable pytest (>=2.5) `pytest-split-tests-tresorit `_ Feb 22, 2021 1 - Planning N/A `pytest-splunk-addon `_ A Dynamic test tool for Splunk Apps and Add-ons Jul 14, 2021 N/A pytest (>5.4.0,<6.3) -`pytest-splunk-addon-ui-smartx `_ Library to support testing Splunk Add-on UX Jul 26, 2021 N/A N/A +`pytest-splunk-addon-ui-smartx `_ Library to support testing Splunk Add-on UX Aug 13, 2021 N/A N/A `pytest-splunk-env `_ pytest fixtures for interaction with Splunk Enterprise and Splunk Cloud Oct 22, 2020 N/A pytest (>=6.1.1,<7.0.0) `pytest-sqitch `_ sqitch for pytest Apr 06, 2020 4 - Beta N/A `pytest-sqlalchemy `_ pytest plugin with sqlalchemy related fixtures Mar 13, 2018 3 - Alpha N/A @@ -795,9 +796,10 @@ name `pytest-subunit `_ pytest-subunit is a plugin for py.test which outputs testsresult in subunit format. Aug 29, 2017 N/A N/A `pytest-sugar `_ pytest-sugar is a plugin for pytest that changes the default look and feel of pytest (e.g. progressbar, show tests that fail instantly). Jul 06, 2020 3 - Alpha N/A `pytest-sugar-bugfix159 `_ Workaround for https://github.com/Frozenball/pytest-sugar/issues/159 Nov 07, 2018 5 - Production/Stable pytest (!=3.7.3,>=3.5); extra == 'testing' -`pytest-super-check `_ Pytest plugin to check your TestCase classes call super in setUp, tearDown, etc. May 10, 2021 5 - Production/Stable pytest +`pytest-super-check `_ Pytest plugin to check your TestCase classes call super in setUp, tearDown, etc. Aug 12, 2021 5 - Production/Stable pytest `pytest-svn `_ SVN repository fixture for py.test May 28, 2019 5 - Production/Stable pytest `pytest-symbols `_ pytest-symbols is a pytest plugin that adds support for passing test environment symbols into pytest tests. Nov 20, 2017 3 - Alpha N/A +`pytest-takeltest `_ Fixtures for ansible, testinfra and molecule Aug 07, 2021 N/A N/A `pytest-tap `_ Test Anything Protocol (TAP) reporting plugin for pytest Nov 07, 2020 5 - Production/Stable pytest (>=3.0) `pytest-tape `_ easy assertion with expected results saved to yaml files Mar 17, 2021 4 - Beta N/A `pytest-target `_ Pytest plugin for remote target orchestration. Jan 21, 2021 3 - Alpha pytest (>=6.1.2,<7.0.0) @@ -872,6 +874,7 @@ name `pytest-variables `_ pytest plugin for providing variables to tests/fixtures Oct 23, 2019 5 - Production/Stable pytest (>=2.4.2) `pytest-variant `_ Variant support for Pytest Jun 20, 2021 N/A N/A `pytest-vcr `_ Plugin for managing VCR.py cassettes Apr 26, 2019 5 - Production/Stable pytest (>=3.6.0) +`pytest-vcr-delete-on-fail `_ A pytest plugin that automates vcrpy cassettes deletion on test failure. Aug 13, 2021 4 - Beta pytest (>=6.2.2,<7.0.0) `pytest-vcrpandas `_ Test from HTTP interactions to dataframe processed. Jan 12, 2019 4 - Beta pytest `pytest-venv `_ py.test fixture for creating a virtual environment Aug 04, 2020 4 - Beta pytest `pytest-verbose-parametrize `_ More descriptive output for parametrized py.test tests May 28, 2019 5 - Production/Stable pytest @@ -889,6 +892,7 @@ name `pytest-wetest `_ Welian API Automation test framework pytest plugin Nov 10, 2018 4 - Beta N/A `pytest-whirlwind `_ Testing Tornado. Jun 12, 2020 N/A N/A `pytest-wholenodeid `_ pytest addon for displaying the whole node id for failures Aug 26, 2015 4 - Beta pytest (>=2.0) +`pytest-win32consoletitle `_ Pytest progress in console title (Win32 only) Aug 08, 2021 N/A N/A `pytest-winnotify `_ Windows tray notifications for py.test results. Apr 22, 2016 N/A N/A `pytest-workflow `_ A pytest plugin for configuring workflow/pipeline tests using YAML files Dec 14, 2020 5 - Production/Stable pytest (>=5.4.0) `pytest-xdist `_ pytest xdist plugin for distributed testing and loop-on-failing modes Jun 16, 2021 5 - Production/Stable pytest (>=6.0.0) From 5ec89fd6e5d76638227dd80f376bab1ec076c714 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Wed, 18 Aug 2021 12:43:59 +0300 Subject: [PATCH 427/630] Docs: refer to non-deprecated unittest alias --- doc/en/how-to/assert.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/how-to/assert.rst b/doc/en/how-to/assert.rst index 9507a651e73..895afc02043 100644 --- a/doc/en/how-to/assert.rst +++ b/doc/en/how-to/assert.rst @@ -102,7 +102,7 @@ the actual exception raised. The main attributes of interest are You can pass a ``match`` keyword parameter to the context-manager to test that a regular expression matches on the string representation of an exception -(similar to the ``TestCase.assertRaisesRegexp`` method from ``unittest``): +(similar to the ``TestCase.assertRaisesRegex`` method from ``unittest``): .. code-block:: python From 2367e6e9bf4cf021510ed212e2e9f05cf35b8697 Mon Sep 17 00:00:00 2001 From: David Szotten Date: Thu, 19 Aug 2021 11:24:51 +0000 Subject: [PATCH 428/630] refactor ci helper to prepare for re-use --- src/_pytest/assertion/truncate.py | 10 ++-------- src/_pytest/assertion/util.py | 8 +++++++- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/_pytest/assertion/truncate.py b/src/_pytest/assertion/truncate.py index 5ba9ddca75a..ce148dca095 100644 --- a/src/_pytest/assertion/truncate.py +++ b/src/_pytest/assertion/truncate.py @@ -3,10 +3,10 @@ Current default behaviour is to truncate assertion explanations at ~8 terminal lines, unless running in "-vv" mode or running on CI. """ -import os from typing import List from typing import Optional +from _pytest.assertion import util from _pytest.nodes import Item @@ -27,13 +27,7 @@ def truncate_if_required( def _should_truncate_item(item: Item) -> bool: """Whether or not this test item is eligible for truncation.""" verbose = item.config.option.verbose - return verbose < 2 and not _running_on_ci() - - -def _running_on_ci() -> bool: - """Check if we're currently running on a CI system.""" - env_vars = ["CI", "BUILD_NUMBER"] - return any(var in os.environ for var in env_vars) + return verbose < 2 and not util.running_on_ci() def _truncate_explanation( diff --git a/src/_pytest/assertion/util.py b/src/_pytest/assertion/util.py index d29a2a010a9..493ab91425c 100644 --- a/src/_pytest/assertion/util.py +++ b/src/_pytest/assertion/util.py @@ -1,5 +1,6 @@ """Utilities for assertion debugging.""" import collections.abc +import os import pprint from typing import AbstractSet from typing import Any @@ -17,7 +18,6 @@ from _pytest._io.saferepr import saferepr from _pytest.config import Config - # The _reprcompare attribute on the util module is used by the new assertion # interpretation code and assertion rewriter to detect this plugin was # loaded and in turn call the hooks defined here as part of the @@ -490,3 +490,9 @@ def _notin_text(term: str, text: str, verbose: int = 0) -> List[str]: else: newdiff.append(line) return newdiff + + +def running_on_ci() -> bool: + """Check if we're currently running on a CI system.""" + env_vars = ["CI", "BUILD_NUMBER"] + return any(var in os.environ for var in env_vars) From c9870480e095aebefa3777e9f3d7d2533b0336bd Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 19 Aug 2021 18:40:19 +0200 Subject: [PATCH 429/630] doc: Update trainings (#9022) --- doc/en/index.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/en/index.rst b/doc/en/index.rst index 08d195e7aaa..e3dd64a7600 100644 --- a/doc/en/index.rst +++ b/doc/en/index.rst @@ -2,9 +2,8 @@ .. sidebar:: Next Open Trainings - - `Introduction to pytest `_, part of `Europython 2021 `_, July 27th, remote. - `pytest: Professionelles Testen (nicht nur) für Python `_ (German), part of `CH-Open Workshoptage `_, September 9th, ETH Zurich, Switzerland. - - `Professional Testing with Python `_, via `Python Academy `_, Q4/2021 (TBD, 3 days), Leipzig (Germany) and remote. + - `Professional Testing with Python `_, via `Python Academy `_, February 1st to 3rd, 2022, Leipzig (Germany) and remote. Also see `previous talks and blogposts `_. From d5c020d8c5958e14fb401f4e4c706ccc612d41af Mon Sep 17 00:00:00 2001 From: David Szotten Date: Thu, 19 Aug 2021 16:27:07 +0000 Subject: [PATCH 430/630] always show full diff in ci follow-up to #1314, for similar reasons closes #9023 --- changelog/9023.feature.rst | 4 ++++ src/_pytest/assertion/util.py | 2 +- testing/test_assertion.py | 20 ++++++++++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 changelog/9023.feature.rst diff --git a/changelog/9023.feature.rst b/changelog/9023.feature.rst new file mode 100644 index 00000000000..86a819a84e0 --- /dev/null +++ b/changelog/9023.feature.rst @@ -0,0 +1,4 @@ + +Full diffs are now always shown for equality assertions of iterables when +`CI` or ``BUILD_NUMBER`` is found in the environment, even when ``-v`` isn't +used. diff --git a/src/_pytest/assertion/util.py b/src/_pytest/assertion/util.py index 493ab91425c..19f1089c20a 100644 --- a/src/_pytest/assertion/util.py +++ b/src/_pytest/assertion/util.py @@ -287,7 +287,7 @@ def _surrounding_parens_on_own_lines(lines: List[str]) -> None: def _compare_eq_iterable( left: Iterable[Any], right: Iterable[Any], verbose: int = 0 ) -> List[str]: - if not verbose: + if not verbose and not running_on_ci(): return ["Use -v to get the full diff"] # dynamic import to speedup pytest import difflib diff --git a/testing/test_assertion.py b/testing/test_assertion.py index 289fe5b083f..e8717590d53 100644 --- a/testing/test_assertion.py +++ b/testing/test_assertion.py @@ -13,6 +13,7 @@ from _pytest import outcomes from _pytest.assertion import truncate from _pytest.assertion import util +from _pytest.monkeypatch import MonkeyPatch from _pytest.pytester import Pytester @@ -448,6 +449,25 @@ def test_iterable_full_diff(self, left, right, expected) -> None: assert verbose_expl is not None assert "\n".join(verbose_expl).endswith(textwrap.dedent(expected).strip()) + def test_iterable_full_diff_ci( + self, monkeypatch: MonkeyPatch, pytester: Pytester + ) -> None: + pytester.makepyfile( + r""" + def test_full_diff(): + left = [0, 1] + right = [0, 2] + assert left == right + """ + ) + monkeypatch.setenv("CI", "true") + result = pytester.runpytest() + result.stdout.fnmatch_lines(["E Full diff:"]) + + monkeypatch.delenv("CI", raising=False) + result = pytester.runpytest() + result.stdout.fnmatch_lines(["E Use -v to get the full diff"]) + def test_list_different_lengths(self) -> None: expl = callequal([0, 1], [0, 1, 2]) assert expl is not None From ec2099e05753fc0ae62a0741c8a8d3fe5bfa2df0 Mon Sep 17 00:00:00 2001 From: Emmanuel Meric de Bellefon Date: Sat, 21 Aug 2021 11:11:37 +0200 Subject: [PATCH 431/630] Update index.rst --- doc/en/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/index.rst b/doc/en/index.rst index e3dd64a7600..106d7be356d 100644 --- a/doc/en/index.rst +++ b/doc/en/index.rst @@ -109,7 +109,7 @@ Support pytest -------------- `Open Collective`_ is an online funding platform for open and transparent communities. -It provide tools to raise money and share your finances in full transparency. +It provides tools to raise money and share your finances in full transparency. It is the platform of choice for individuals and companies that want to make one-time or monthly donations directly to the project. From 1b130d4404c8cf7e7f841c50e28b4d6c3cf91848 Mon Sep 17 00:00:00 2001 From: pytest bot Date: Sun, 22 Aug 2021 00:09:13 +0000 Subject: [PATCH 432/630] [automated] Update plugin list --- doc/en/reference/plugin_list.rst | 43 ++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/doc/en/reference/plugin_list.rst b/doc/en/reference/plugin_list.rst index 5ff8790e33a..2d53f8f736f 100644 --- a/doc/en/reference/plugin_list.rst +++ b/doc/en/reference/plugin_list.rst @@ -6,7 +6,7 @@ Plugin List PyPI projects that match "pytest-\*" are considered plugins and are listed automatically. Packages classified as inactive are excluded. -This list contains 908 plugins. +This list contains 913 plugins. ============================================================================================================== ======================================================================================================================================================================== ============== ===================== ================================================ name summary last release status requires @@ -17,14 +17,14 @@ name `pytest-adf `_ Pytest plugin for writing Azure Data Factory integration tests May 10, 2021 4 - Beta pytest (>=3.5.0) `pytest-adf-azure-identity `_ Pytest plugin for writing Azure Data Factory integration tests Mar 06, 2021 4 - Beta pytest (>=3.5.0) `pytest-aggreport `_ pytest plugin for pytest-repeat that generate aggregate report of the same test cases with additional statistics details. Mar 07, 2021 4 - Beta pytest (>=6.2.2) -`pytest-aio `_ Pytest plugin for testing async python code Aug 11, 2021 4 - Beta pytest ; extra == 'tests' +`pytest-aio `_ Pytest plugin for testing async python code Aug 20, 2021 4 - Beta pytest ; extra == 'tests' `pytest-aiofiles `_ pytest fixtures for writing aiofiles tests with pyfakefs May 14, 2017 5 - Production/Stable N/A `pytest-aiohttp `_ pytest plugin for aiohttp support Dec 05, 2017 N/A pytest `pytest-aiohttp-client `_ Pytest `client` fixture for the Aiohttp Nov 01, 2020 N/A pytest (>=6) `pytest-aioresponses `_ py.test integration for aioresponses Jul 29, 2021 4 - Beta pytest (>=3.5.0) `pytest-aioworkers `_ A plugin to test aioworkers project with pytest Dec 04, 2019 4 - Beta pytest (>=3.5.0) `pytest-airflow `_ pytest support for airflow. Apr 03, 2019 3 - Alpha pytest (>=4.4.0) -`pytest-alembic `_ A pytest plugin for verifying alembic migrations. Aug 04, 2021 N/A pytest (>=1.0) +`pytest-alembic `_ A pytest plugin for verifying alembic migrations. Aug 17, 2021 N/A pytest (>=1.0) `pytest-allclose `_ Pytest fixture extending Numpy's allclose function Jul 30, 2019 5 - Production/Stable pytest `pytest-allure-adaptor `_ Plugin for py.test to generate allure xml reports Jan 10, 2018 N/A pytest (>=2.7.3) `pytest-allure-adaptor2 `_ Plugin for py.test to generate allure xml reports Oct 14, 2020 N/A pytest (>=2.7.3) @@ -82,8 +82,8 @@ name `pytest-beds `_ Fixtures for testing Google Appengine (GAE) apps Jun 07, 2016 4 - Beta N/A `pytest-bench `_ Benchmark utility that plugs into pytest. Jul 21, 2014 3 - Alpha N/A `pytest-benchmark `_ A ``pytest`` fixture for benchmarking code. It will group the tests into rounds that are calibrated to the chosen timer. Apr 17, 2021 5 - Production/Stable pytest (>=3.8) -`pytest-bg-process `_ A simple plugin to use with pytest May 28, 2021 4 - Beta pytest (>=3.5.0) -`pytest-bigchaindb `_ A BigchainDB plugin for pytest. May 28, 2021 4 - Beta N/A +`pytest-bg-process `_ Pytest plugin to initialize background process Aug 17, 2021 4 - Beta pytest (>=3.5.0) +`pytest-bigchaindb `_ A BigchainDB plugin for pytest. Aug 17, 2021 4 - Beta N/A `pytest-bigquery-mock `_ Provides a mock fixture for python bigquery client Aug 05, 2021 N/A pytest (>=5.0) `pytest-black `_ A pytest plugin to enable format checking with black Oct 05, 2020 4 - Beta N/A `pytest-black-multipy `_ Allow '--black' on older Pythons Jan 14, 2021 5 - Production/Stable pytest (!=3.7.3,>=3.5) ; extra == 'testing' @@ -131,7 +131,7 @@ name `pytest-clarity `_ A plugin providing an alternative, colourful diff output for failing assertions. Jun 11, 2021 N/A N/A `pytest-cldf `_ Easy quality control for CLDF datasets using pytest May 06, 2019 N/A N/A `pytest-click `_ Py.test plugin for Click Aug 29, 2020 5 - Production/Stable pytest (>=5.0) -`pytest-clld `_ May 06, 2020 N/A pytest (>=3.6) +`pytest-clld `_ Aug 16, 2021 N/A pytest (>=3.6) `pytest-cloud `_ Distributed tests planner plugin for pytest testing framework. Oct 05, 2020 6 - Mature N/A `pytest-cloudflare-worker `_ pytest plugin for testing cloudflare workers Mar 30, 2021 4 - Beta pytest (>=6.0.0) `pytest-cobra `_ PyTest plugin for testing Smart Contracts for Ethereum blockchain. Jun 29, 2019 3 - Alpha pytest (<4.0.0,>=3.7.1) @@ -143,7 +143,7 @@ name `pytest-collect-formatter `_ Formatter for pytest collect output Mar 29, 2021 5 - Production/Stable N/A `pytest-collect-formatter2 `_ Formatter for pytest collect output May 31, 2021 5 - Production/Stable N/A `pytest-colordots `_ Colorizes the progress indicators Oct 06, 2017 5 - Production/Stable N/A -`pytest-commander `_ An interactive GUI test runner for PyTest Jun 11, 2021 N/A pytest (<7.0.0,>=6.2.4) +`pytest-commander `_ An interactive GUI test runner for PyTest Aug 17, 2021 N/A pytest (<7.0.0,>=6.2.4) `pytest-common-subject `_ pytest framework for testing different aspects of a common method Nov 12, 2020 N/A pytest (>=3.6,<7) `pytest-concurrent `_ Concurrently execute test cases with multithread, multiprocess and gevent Jan 12, 2019 4 - Beta pytest (>=3.1.1) `pytest-config `_ Base configurations and utilities for developing your Python project test suite with pytest. Nov 07, 2014 5 - Production/Stable N/A @@ -179,7 +179,7 @@ name `pytest-data `_ Useful functions for managing data for pytest fixtures Nov 01, 2016 5 - Production/Stable N/A `pytest-databricks `_ Pytest plugin for remote Databricks notebooks testing Jul 29, 2020 N/A pytest `pytest-datadir `_ pytest plugin for test data directories and files Oct 22, 2019 5 - Production/Stable pytest (>=2.7.0) -`pytest-datadir-mgr `_ Manager for test data providing downloads, caching of generated files, and a context for temp directories. Feb 17, 2021 5 - Production/Stable pytest (>=6.0.1,<7.0.0) +`pytest-datadir-mgr `_ Manager for test data providing downloads, caching of generated files, and a context for temp directories. Aug 16, 2021 5 - Production/Stable pytest `pytest-datadir-ng `_ Fixtures for pytest allowing test functions/methods to easily retrieve test resources from the local filesystem. Dec 25, 2019 5 - Production/Stable pytest `pytest-data-file `_ Fixture "data" and "case_data" for test from yaml file Dec 04, 2019 N/A N/A `pytest-datafiles `_ py.test plugin to create a 'tmpdir' containing predefined files/directories. Oct 07, 2018 5 - Production/Stable pytest (>=3.6) @@ -197,7 +197,7 @@ name `pytest-dependency `_ Manage dependencies of tests Feb 14, 2020 4 - Beta N/A `pytest-depends `_ Tests that depend on other tests Apr 05, 2020 5 - Production/Stable pytest (>=3) `pytest-deprecate `_ Mark tests as testing a deprecated feature with a warning note. Jul 01, 2019 N/A N/A -`pytest-describe `_ Describe-style plugin for pytest Apr 21, 2020 3 - Alpha pytest (>=2.6.0) +`pytest-describe `_ Describe-style plugin for pytest Aug 18, 2021 4 - Beta pytest (>=4.0.0) `pytest-describe-it `_ plugin for rich text descriptions Jul 19, 2019 4 - Beta pytest `pytest-devpi-server `_ DevPI server fixture for py.test May 28, 2019 5 - Production/Stable pytest `pytest-diamond `_ pytest plugin for diamond Aug 31, 2015 4 - Beta N/A @@ -371,7 +371,7 @@ name `pytest-hammertime `_ Display "🔨 " instead of "." for passed pytest tests. Jul 28, 2018 N/A pytest `pytest-harvest `_ Store data created during your pytest tests execution, and retrieve it at the end of the session, e.g. for applicative benchmarking purposes. Apr 01, 2021 5 - Production/Stable N/A `pytest-helm-chart `_ A plugin to provide different types and configs of Kubernetes clusters that can be used for testing. Jun 15, 2020 4 - Beta pytest (>=5.4.2,<6.0.0) -`pytest-helm-charts `_ A plugin to provide different types and configs of Kubernetes clusters that can be used for testing. Aug 13, 2021 4 - Beta pytest (>=6.1.2,<7.0.0) +`pytest-helm-charts `_ A plugin to provide different types and configs of Kubernetes clusters that can be used for testing. Aug 19, 2021 4 - Beta pytest (>=6.1.2,<7.0.0) `pytest-helper `_ Functions to help in using the pytest testing framework May 31, 2019 5 - Production/Stable N/A `pytest-helpers `_ pytest helpers May 17, 2020 N/A pytest `pytest-helpers-namespace `_ Pytest Helpers Namespace Plugin Apr 29, 2021 5 - Production/Stable pytest (>=6.0.0) @@ -393,7 +393,7 @@ name `pytest-http-mocker `_ Pytest plugin for http mocking (via https://github.com/vilus/mocker) Oct 20, 2019 N/A N/A `pytest-httpretty `_ A thin wrapper of HTTPretty for pytest Feb 16, 2014 3 - Alpha N/A `pytest-httpserver `_ pytest-httpserver is a httpserver for pytest Aug 06, 2021 3 - Alpha pytest ; extra == 'dev' -`pytest-httpx `_ Send responses to httpx. Aug 11, 2021 5 - Production/Stable pytest (==6.*) +`pytest-httpx `_ Send responses to httpx. Aug 19, 2021 5 - Production/Stable pytest (==6.*) `pytest-httpx-blockage `_ Disable httpx requests during a test run Apr 28, 2021 N/A pytest (>=6.2.3) `pytest-hue `_ Visualise PyTest status via your Phillips Hue lights May 09, 2019 N/A N/A `pytest-hylang `_ Pytest plugin to allow running tests written in hylang Mar 28, 2021 N/A pytest @@ -408,7 +408,7 @@ name `pytest-info-collector `_ pytest plugin to collect information from tests May 26, 2019 3 - Alpha N/A `pytest-informative-node `_ display more node ininformation. Apr 25, 2019 4 - Beta N/A `pytest-infrastructure `_ pytest stack validation prior to testing executing Apr 12, 2020 4 - Beta N/A -`pytest-inmanta `_ A py.test plugin providing fixtures to simplify inmanta modules testing. Jun 29, 2021 5 - Production/Stable N/A +`pytest-inmanta `_ A py.test plugin providing fixtures to simplify inmanta modules testing. Aug 17, 2021 5 - Production/Stable N/A `pytest-inmanta-extensions `_ Inmanta tests package May 27, 2021 5 - Production/Stable N/A `pytest-Inomaly `_ A simple image diff plugin for pytest Feb 13, 2018 4 - Beta N/A `pytest-insta `_ A practical snapshot testing plugin for pytest Apr 07, 2021 N/A pytest (>=6.0.2,<7.0.0) @@ -461,8 +461,9 @@ name `pytest-localftpserver `_ A PyTest plugin which provides an FTP fixture for your tests Jan 27, 2021 5 - Production/Stable pytest `pytest-localserver `_ py.test plugin to test server connections locally. Nov 14, 2018 4 - Beta N/A `pytest-localstack `_ Pytest plugin for AWS integration tests Aug 22, 2019 4 - Beta pytest (>=3.3.0) -`pytest-lockable `_ lockable resource plugin for pytest Jun 08, 2021 5 - Production/Stable pytest +`pytest-lockable `_ lockable resource plugin for pytest Aug 19, 2021 5 - Production/Stable pytest `pytest-locker `_ Used to lock object during testing. Essentially changing assertions from being hard coded to asserting that nothing changed Feb 25, 2021 N/A pytest (>=5.4) +`pytest-log `_ print log Aug 15, 2021 N/A pytest (>=3.8) `pytest-logbook `_ py.test plugin to capture logbook log messages Nov 23, 2015 5 - Production/Stable pytest (>=2.8) `pytest-logdog `_ Pytest plugin to test logging Jun 15, 2021 1 - Planning pytest (>=6.2.0) `pytest-logfest `_ Pytest plugin providing three logger fixtures with basic or full writing to log files Jul 21, 2019 4 - Beta pytest (>=3.5.0) @@ -500,7 +501,7 @@ name `pytest-mock-helper `_ Help you mock HTTP call and generate mock code Jan 24, 2018 N/A pytest `pytest-mockito `_ Base fixtures for mockito Jul 11, 2018 4 - Beta N/A `pytest-mockredis `_ An in-memory mock of a Redis server that runs in a separate thread. This is to be used for unit-tests that require a Redis database. Jan 02, 2018 2 - Pre-Alpha N/A -`pytest-mock-resources `_ A pytest plugin for easily instantiating reproducible mock resources. Jun 29, 2021 N/A pytest (>=1.0) +`pytest-mock-resources `_ A pytest plugin for easily instantiating reproducible mock resources. Aug 17, 2021 N/A pytest (>=1.0) `pytest-mock-server `_ Mock server plugin for pytest Apr 06, 2020 4 - Beta N/A `pytest-mockservers `_ A set of fixtures to test your requests to HTTP/UDP servers Mar 31, 2020 N/A pytest (>=4.3.0) `pytest-modifyjunit `_ Utility for adding additional properties to junit xml for IDM QE Jan 10, 2019 N/A N/A @@ -576,6 +577,7 @@ name `pytest-pass `_ Check out https://github.com/elilutsky/pytest-pass Dec 04, 2019 N/A N/A `pytest-passrunner `_ Pytest plugin providing the 'run_on_pass' marker Feb 10, 2021 5 - Production/Stable pytest (>=4.6.0) `pytest-paste-config `_ Allow setting the path to a paste config file Sep 18, 2013 3 - Alpha N/A +`pytest-patches `_ A patches test fixture which provides a contextmanager for handling multiple mock patches Aug 21, 2021 4 - Beta pytest (>=3.5.0) `pytest-pdb `_ pytest plugin which adds pdb helper commands related to pytest. Jul 31, 2018 N/A N/A `pytest-peach `_ pytest plugin for fuzzing with Peach API Security Apr 12, 2019 4 - Beta pytest (>=2.8.7) `pytest-pep257 `_ py.test plugin for pep257 Jul 09, 2016 N/A N/A @@ -596,7 +598,8 @@ name `pytest-platform-markers `_ Markers for pytest to skip tests on specific platforms Sep 09, 2019 4 - Beta pytest (>=3.6.0) `pytest-play `_ pytest plugin that let you automate actions and assertions with test metrics reporting executing plain YAML files Jun 12, 2019 5 - Production/Stable N/A `pytest-playbook `_ Pytest plugin for reading playbooks. Jan 21, 2021 3 - Alpha pytest (>=6.1.2,<7.0.0) -`pytest-playwright `_ A pytest wrapper with fixtures for Playwright to automate web browsers Jun 07, 2021 N/A pytest +`pytest-playwright `_ A pytest wrapper with fixtures for Playwright to automate web browsers Aug 16, 2021 N/A pytest +`pytest-playwright-snapshot `_ A pytest wrapper for snapshot testing with playwright Aug 19, 2021 N/A N/A `pytest-plt `_ Fixtures for quickly making Matplotlib plots in tests Aug 17, 2020 5 - Production/Stable pytest `pytest-plugin-helpers `_ A plugin to help developing and testing other plugins Nov 23, 2019 4 - Beta pytest (>=3.5.0) `pytest-plus `_ PyTest Plus Plugin :: extends pytest functionality Mar 19, 2020 5 - Production/Stable pytest (>=3.50) @@ -608,7 +611,7 @@ name `pytest-ponyorm `_ PonyORM in Pytest Oct 31, 2018 N/A pytest (>=3.1.1) `pytest-poo `_ Visualize your crappy tests Mar 25, 2021 5 - Production/Stable pytest (>=2.3.4) `pytest-poo-fail `_ Visualize your failed tests with poo Feb 12, 2015 5 - Production/Stable N/A -`pytest-pop `_ A pytest plugin to help with testing pop projects May 25, 2021 5 - Production/Stable pytest +`pytest-pop `_ A pytest plugin to help with testing pop projects Aug 19, 2021 5 - Production/Stable pytest `pytest-portion `_ Select a portion of the collected tests Jan 28, 2021 4 - Beta pytest (>=3.5.0) `pytest-postgres `_ Run PostgreSQL in Docker container in Pytest. Mar 22, 2020 N/A pytest `pytest-postgresql `_ Postgresql fixtures and fixture factories for Pytest. Jun 01, 2021 5 - Production/Stable pytest (>=3.0.0) @@ -634,6 +637,7 @@ name `pytest-pyq `_ Pytest fixture "q" for pyq Mar 10, 2020 5 - Production/Stable N/A `pytest-pyramid `_ pytest_pyramid - provides fixtures for testing pyramid applications with pytest test suite Feb 26, 2021 5 - Production/Stable pytest `pytest-pyramid-server `_ Pyramid server fixture for py.test May 28, 2019 5 - Production/Stable pytest +`pytest-pyright `_ Pytest plugin for type checking code with Pyright Aug 16, 2021 4 - Beta pytest (>=3.5.0) `pytest-pytestrail `_ Pytest plugin for interaction with TestRail Aug 27, 2020 4 - Beta pytest (>=3.8.0) `pytest-pythonpath `_ pytest plugin for adding to the PYTHONPATH from command line or configs. Aug 22, 2018 5 - Production/Stable N/A `pytest-pytorch `_ pytest plugin for a better developer experience when working with the PyTorch test suite May 25, 2021 4 - Beta pytest @@ -648,6 +652,7 @@ name `pytest-rabbitmq `_ RabbitMQ process and client fixtures for pytest Jun 02, 2021 5 - Production/Stable pytest (>=3.0.0) `pytest-race `_ Race conditions tester for pytest Nov 21, 2016 4 - Beta N/A `pytest-rage `_ pytest plugin to implement PEP712 Oct 21, 2011 3 - Alpha N/A +`pytest-railflow-testrail-reporter `_ Generate json reports along with specified metadata defined in test markers. Aug 18, 2021 5 - Production/Stable pytest `pytest-raises `_ An implementation of pytest.raises as a pytest.mark fixture Apr 23, 2020 N/A pytest (>=3.2.2) `pytest-raisesregexp `_ Simple pytest plugin to look for regex in Exceptions Dec 18, 2015 N/A N/A `pytest-raisin `_ Plugin enabling the use of exception instances with pytest.raises Jun 25, 2020 N/A pytest @@ -711,7 +716,7 @@ name `pytest-runner `_ Invoke py.test as distutils command with dependency resolution May 19, 2021 5 - Production/Stable pytest (>=4.6) ; extra == 'testing' `pytest-salt `_ Pytest Salt Plugin Jan 27, 2020 4 - Beta N/A `pytest-salt-containers `_ A Pytest plugin that builds and creates docker containers Nov 09, 2016 4 - Beta N/A -`pytest-salt-factories `_ Pytest Salt Plugin Aug 14, 2021 4 - Beta pytest (>=6.0.0) +`pytest-salt-factories `_ Pytest Salt Plugin Aug 15, 2021 4 - Beta pytest (>=6.0.0) `pytest-salt-from-filenames `_ Simple PyTest Plugin For Salt's Test Suite Specifically Jan 29, 2019 4 - Beta pytest (>=4.1) `pytest-salt-runtests-bridge `_ Simple PyTest Plugin For Salt's Test Suite Specifically Dec 05, 2019 4 - Beta pytest (>=4.1) `pytest-sanic `_ a pytest plugin for Sanic Jul 27, 2021 N/A pytest (>=5.2) @@ -770,7 +775,7 @@ name `pytest-splitio `_ Split.io SDK integration for e2e tests Sep 22, 2020 N/A pytest (<7,>=5.0) `pytest-split-tests `_ A Pytest plugin for running a subset of your tests by splitting them in to equally sized groups. Forked from Mark Adams' original project pytest-test-groups. Jul 30, 2021 5 - Production/Stable pytest (>=2.5) `pytest-split-tests-tresorit `_ Feb 22, 2021 1 - Planning N/A -`pytest-splunk-addon `_ A Dynamic test tool for Splunk Apps and Add-ons Jul 14, 2021 N/A pytest (>5.4.0,<6.3) +`pytest-splunk-addon `_ A Dynamic test tool for Splunk Apps and Add-ons Aug 19, 2021 N/A pytest (>5.4.0,<6.3) `pytest-splunk-addon-ui-smartx `_ Library to support testing Splunk Add-on UX Aug 13, 2021 N/A N/A `pytest-splunk-env `_ pytest fixtures for interaction with Splunk Enterprise and Splunk Cloud Oct 22, 2020 N/A pytest (>=6.1.1,<7.0.0) `pytest-sqitch `_ sqitch for pytest Apr 06, 2020 4 - Beta N/A @@ -841,7 +846,7 @@ name `pytest-tipsi-testing `_ Better fixtures management. Various helpers Nov 04, 2020 4 - Beta pytest (>=3.3.0) `pytest-tldr `_ A pytest plugin that limits the output to just the things you need. Mar 12, 2021 4 - Beta pytest (>=3.5.0) `pytest-tm4j-reporter `_ Cloud Jira Test Management (TM4J) PyTest reporter plugin Sep 01, 2020 N/A pytest -`pytest-tmreport `_ this is a vue-element ui report for pytest Aug 02, 2021 N/A N/A +`pytest-tmreport `_ this is a vue-element ui report for pytest Aug 16, 2021 N/A N/A `pytest-todo `_ A small plugin for the pytest testing framework, marking TODO comments as failure May 23, 2019 4 - Beta pytest `pytest-tomato `_ Mar 01, 2019 5 - Production/Stable N/A `pytest-toolbelt `_ This is just a collection of utilities for pytest, but don't really belong in pytest proper. Aug 12, 2019 3 - Alpha N/A From e8aa395346ff1a1bdc71bd2a1b995a35ba868c81 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 23 Aug 2021 18:58:43 +0000 Subject: [PATCH 433/630] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/pyupgrade: v2.23.3 → v2.24.0](https://github.com/asottile/pyupgrade/compare/v2.23.3...v2.24.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f92d5a5e987..d6c2cfccef6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -34,7 +34,7 @@ repos: - id: reorder-python-imports args: ['--application-directories=.:src', --py36-plus] - repo: https://github.com/asottile/pyupgrade - rev: v2.23.3 + rev: v2.24.0 hooks: - id: pyupgrade args: [--py36-plus] From e146aaa2e2bbdf78d0eca4b4635bfeff64a0630e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Kul=C3=ADk?= Date: Tue, 24 Aug 2021 12:11:06 +0200 Subject: [PATCH 434/630] Fix cwd removal on Solaris --- testing/test_assertrewrite.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/testing/test_assertrewrite.py b/testing/test_assertrewrite.py index 38969d2946e..d732cbdd709 100644 --- a/testing/test_assertrewrite.py +++ b/testing/test_assertrewrite.py @@ -1388,6 +1388,9 @@ def test_simple_failure(): @pytest.mark.skipif( sys.platform.startswith("win32"), reason="cannot remove cwd on Windows" ) + @pytest.mark.skipif( + sys.platform.startswith("sunos5"), reason="cannot remove cwd on Solaris" + ) def test_cwd_changed(self, pytester: Pytester, monkeypatch) -> None: # Setup conditions for py's fspath trying to import pathlib on py34 # always (previously triggered via xdist only). From 696bf96c83878c163c8e0ed3b590a18a83003838 Mon Sep 17 00:00:00 2001 From: William Jamir Silva Date: Tue, 24 Aug 2021 09:32:40 -0300 Subject: [PATCH 435/630] Fix typo in fixture.rst --- doc/en/reference/fixtures.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/en/reference/fixtures.rst b/doc/en/reference/fixtures.rst index d90b6834828..a333598a9a1 100644 --- a/doc/en/reference/fixtures.rst +++ b/doc/en/reference/fixtures.rst @@ -418,7 +418,7 @@ is executed for the tests inside it anyway: But just because one autouse fixture requested a non-autouse fixture, that doesn't mean the non-autouse fixture becomes an autouse fixture for all contexts -that it can apply to. It only effectively becomes an auotuse fixture for the +that it can apply to. It only effectively becomes an autouse fixture for the contexts the real autouse fixture (the one that requested the non-autouse fixture) can apply to. From fbba504cd5e1a74d528a41a11a7b82297cd7da74 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 26 Aug 2021 09:26:51 -0300 Subject: [PATCH 436/630] Allow pluggy >=1.0 Now that pluggy 1.0 has been released, we can allow pluggy 1.0 with new pytest versions. --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index ca1d2c2bdd1..4594dfd6c1d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -45,7 +45,7 @@ install_requires = attrs>=19.2.0 iniconfig packaging - pluggy>=0.12,<1.0.0a1 + pluggy>=0.12,<2.0 py>=1.8.2 tomli>=1.0.0 atomicwrites>=1.0;sys_platform=="win32" From 109bc2649d8f1573e91f281d1a62e95dee0c2cc5 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Thu, 26 Aug 2021 09:33:50 -0300 Subject: [PATCH 437/630] Adapt docs references to use pluggy 1.0 Also use the intersphinx reference instead of the class directly. --- doc/en/reference/reference.rst | 5 +---- doc/en/requirements.txt | 1 + 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/doc/en/reference/reference.rst b/doc/en/reference/reference.rst index db49b0f1d44..98b5a5064cf 100644 --- a/doc/en/reference/reference.rst +++ b/doc/en/reference/reference.rst @@ -956,11 +956,8 @@ TestReport _Result ~~~~~~~ -Result used within :ref:`hook wrappers `. +Result object used within :ref:`hook wrappers `, see :py:class:`_Result in the pluggy documentation ` for more information. -.. autoclass:: pluggy.callers._Result -.. automethod:: pluggy.callers._Result.get_result -.. automethod:: pluggy.callers._Result.force_result Stash ~~~~~ diff --git a/doc/en/requirements.txt b/doc/en/requirements.txt index 20246acb750..2cd1fc25eb3 100644 --- a/doc/en/requirements.txt +++ b/doc/en/requirements.txt @@ -1,4 +1,5 @@ pallets-sphinx-themes +pluggy>=1.0 pygments-pytest>=2.2.0 sphinx-removed-in>=0.2.0 sphinx>=3.1,<4 From d1aea7d7a8f0fe70cf4e59fc7fd5f624a522da15 Mon Sep 17 00:00:00 2001 From: "oleg.hoefling" Date: Thu, 26 Aug 2021 16:05:03 +0200 Subject: [PATCH 438/630] use intersphinx crossrefs to stdlib docs where possible instead of hardcoded URLs Signed-off-by: oleg.hoefling --- doc/en/changelog.rst | 8 ++++---- doc/en/conf.py | 2 +- doc/en/explanation/goodpractices.rst | 4 +--- doc/en/explanation/pythonpath.rst | 6 +++--- doc/en/getting-started.rst | 2 +- doc/en/how-to/capture-warnings.rst | 8 +++----- doc/en/how-to/doctest.rst | 6 +++--- doc/en/how-to/failures.rst | 22 ++++++++++------------ doc/en/how-to/fixtures.rst | 6 +++--- doc/en/how-to/unittest.rst | 3 +-- doc/en/how-to/xunit_setup.rst | 2 -- doc/en/reference/customize.rst | 2 +- doc/en/reference/reference.rst | 8 ++------ 13 files changed, 33 insertions(+), 46 deletions(-) diff --git a/doc/en/changelog.rst b/doc/en/changelog.rst index 49c09eba041..2eb354e03d9 100644 --- a/doc/en/changelog.rst +++ b/doc/en/changelog.rst @@ -398,7 +398,7 @@ Improvements and should be preferred over them when possible. -- `#7780 `_: Public classes which are not designed to be inherited from are now marked `@final `_. +- `#7780 `_: Public classes which are not designed to be inherited from are now marked :func:`@final `. Code which inherits from these classes will trigger a type-checking (e.g. mypy) error, but will still work in runtime. Currently the ``final`` designation does not appear in the API Reference but hopefully will in the future. @@ -772,7 +772,7 @@ Features - `#6906 `_: Added `--code-highlight` command line option to enable/disable code highlighting in terminal output. -- `#7245 `_: New ``--import-mode=importlib`` option that uses `importlib `__ to import test modules. +- `#7245 `_: New ``--import-mode=importlib`` option that uses :mod:`importlib` to import test modules. Traditionally pytest used ``__import__`` while changing ``sys.path`` to import test modules (which also changes ``sys.modules`` as a side-effect), which works but has a number of drawbacks, like requiring test modules @@ -2019,7 +2019,7 @@ Features This hook is still **experimental** so use it with caution. -- `#5440 `_: The `faulthandler `__ standard library +- `#5440 `_: The :mod:`faulthandler` standard library module is now enabled by default to help users diagnose crashes in C modules. This functionality was provided by integrating the external @@ -3117,7 +3117,7 @@ Features will not issue the warning. -- `#3632 `_: Richer equality comparison introspection on ``AssertionError`` for objects created using `attrs `__ or `dataclasses `_ (Python 3.7+, `backported to 3.6 `__). +- `#3632 `_: Richer equality comparison introspection on ``AssertionError`` for objects created using `attrs `__ or :mod:`dataclasses` (Python 3.7+, `backported to 3.6 `__). - `#4278 `_: ``CACHEDIR.TAG`` files are now created inside cache directories. diff --git a/doc/en/conf.py b/doc/en/conf.py index ee71ffcae41..70b9c93fe40 100644 --- a/doc/en/conf.py +++ b/doc/en/conf.py @@ -348,7 +348,7 @@ # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = { - "pluggy": ("https://pluggy.readthedocs.io/en/latest", None), + "pluggy": ("https://pluggy.readthedocs.io/en/stable", None), "python": ("https://docs.python.org/3", None), } diff --git a/doc/en/explanation/goodpractices.rst b/doc/en/explanation/goodpractices.rst index f66ef5a68c0..90765dd925c 100644 --- a/doc/en/explanation/goodpractices.rst +++ b/doc/en/explanation/goodpractices.rst @@ -7,7 +7,7 @@ Good Integration Practices Install package with pip ------------------------------------------------- -For development, we recommend you use venv_ for virtual environments and +For development, we recommend you use :mod:`venv` for virtual environments and pip_ for installing your application and any dependencies, as well as the ``pytest`` package itself. This ensures your code and dependencies are isolated from your system Python installation. @@ -248,5 +248,3 @@ dependencies and then executing a pre-configured test command with options. It will run tests against the installed package and not against your source code checkout, helping to detect packaging glitches. - -.. _`venv`: https://docs.python.org/3/library/venv.html diff --git a/doc/en/explanation/pythonpath.rst b/doc/en/explanation/pythonpath.rst index 9aef44618fc..2330356b863 100644 --- a/doc/en/explanation/pythonpath.rst +++ b/doc/en/explanation/pythonpath.rst @@ -11,12 +11,12 @@ Import modes pytest as a testing framework needs to import test modules and ``conftest.py`` files for execution. Importing files in Python (at least until recently) is a non-trivial processes, often requiring -changing `sys.path `__. Some aspects of the +changing :data:`sys.path`. Some aspects of the import process can be controlled through the ``--import-mode`` command-line flag, which can assume these values: * ``prepend`` (default): the directory path containing each module will be inserted into the *beginning* - of :py:data:`sys.path` if not already there, and then imported with the `__import__ `__ builtin. + of :py:data:`sys.path` if not already there, and then imported with the :func:`__import__ <__import__>` builtin. This requires test module names to be unique when the test directory tree is not arranged in packages, because the modules will put in :py:data:`sys.modules` after importing. @@ -43,7 +43,7 @@ these values: Same as ``prepend``, requires test module names to be unique when the test directory tree is not arranged in packages, because the modules will put in :py:data:`sys.modules` after importing. -* ``importlib``: new in pytest-6.0, this mode uses `importlib `__ to import test modules. This gives full control over the import process, and doesn't require changing :py:data:`sys.path`. +* ``importlib``: new in pytest-6.0, this mode uses :mod:`importlib` to import test modules. This gives full control over the import process, and doesn't require changing :py:data:`sys.path`. For this reason this doesn't require test module names to be unique, but also makes test modules non-importable by each other. diff --git a/doc/en/getting-started.rst b/doc/en/getting-started.rst index bbe2a680ee9..1c777ebc500 100644 --- a/doc/en/getting-started.rst +++ b/doc/en/getting-started.rst @@ -71,7 +71,7 @@ The ``[100%]`` refers to the overall progress of running all test cases. After i .. note:: - You can use the ``assert`` statement to verify test expectations. pytest’s `Advanced assertion introspection `_ will intelligently report intermediate values of the assert expression so you can avoid the many names `of JUnit legacy methods `_. + You can use the ``assert`` statement to verify test expectations. pytest’s :ref:`Advanced assertion introspection ` will intelligently report intermediate values of the assert expression so you can avoid the many names :ref:`of JUnit legacy methods `. Run multiple tests ---------------------------------------------------------- diff --git a/doc/en/how-to/capture-warnings.rst b/doc/en/how-to/capture-warnings.rst index 188a7316df9..14f8b3366b8 100644 --- a/doc/en/how-to/capture-warnings.rst +++ b/doc/en/how-to/capture-warnings.rst @@ -98,7 +98,7 @@ When a warning matches more than one option in the list, the action for the last is performed. Both ``-W`` command-line option and ``filterwarnings`` ini option are based on Python's own -`-W option`_ and `warnings.simplefilter`_, so please refer to those sections in the Python +:option:`-W option ` and :func:`warnings.simplefilter`, so please refer to those sections in the Python documentation for other examples and advanced usage. .. _`filterwarnings`: @@ -143,8 +143,6 @@ decorator or to all tests in a module by setting the :globalvar:`pytestmark` var *Credits go to Florian Schulze for the reference implementation in the* `pytest-warnings`_ *plugin.* -.. _`-W option`: https://docs.python.org/3/using/cmdline.html#cmdoption-w -.. _warnings.simplefilter: https://docs.python.org/3/library/how-to/capture-warnings.html#warnings.simplefilter .. _`pytest-warnings`: https://github.com/fschulze/pytest-warnings Disabling warnings summary @@ -196,12 +194,12 @@ the regular expression ``".*U.*mode is deprecated"``. .. note:: If warnings are configured at the interpreter level, using - the `PYTHONWARNINGS `_ environment variable or the + the :envvar:`python:PYTHONWARNINGS` environment variable or the ``-W`` command-line option, pytest will not configure any filters by default. Also pytest doesn't follow ``PEP-0506`` suggestion of resetting all warning filters because it might break test suites that configure warning filters themselves - by calling ``warnings.simplefilter`` (see issue `#2430 `_ + by calling :func:`warnings.simplefilter` (see issue `#2430 `_ for an example of that). diff --git a/doc/en/how-to/doctest.rst b/doc/en/how-to/doctest.rst index c08ce509329..681c2037ba1 100644 --- a/doc/en/how-to/doctest.rst +++ b/doc/en/how-to/doctest.rst @@ -4,7 +4,7 @@ How to run doctests ========================================================= By default, all files matching the ``test*.txt`` pattern will -be run through the python standard ``doctest`` module. You +be run through the python standard :mod:`doctest` module. You can change the pattern by issuing: .. code-block:: bash @@ -95,7 +95,7 @@ that will be used for those doctest files using the Using 'doctest' options ----------------------- -Python's standard ``doctest`` module provides some `options `__ +Python's standard :mod:`doctest` module provides some :ref:`options ` to configure the strictness of doctest tests. In pytest, you can enable those flags using the configuration file. @@ -252,7 +252,7 @@ For the same reasons one might want to skip normal tests, it is also possible to tests inside doctests. To skip a single check inside a doctest you can use the standard -`doctest.SKIP `__ directive: +:data:`doctest.SKIP` directive: .. code-block:: python diff --git a/doc/en/how-to/failures.rst b/doc/en/how-to/failures.rst index 0a0ec164dbb..ef87550915a 100644 --- a/doc/en/how-to/failures.rst +++ b/doc/en/how-to/failures.rst @@ -18,16 +18,14 @@ To stop the testing process after the first (N) failures: .. _pdb-option: -Using PDB_ (Python Debugger) with pytest ----------------------------------------------------------- +Using :doc:`python:library/pdb` with pytest +------------------------------------------- -Dropping to PDB_ (Python Debugger) on failures -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Dropping to :doc:`pdb ` on failures +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.. _PDB: https://docs.python.org/library/pdb.html - -Python comes with a builtin Python debugger called PDB_. ``pytest`` -allows one to drop into the PDB_ prompt via a command line option: +Python comes with a builtin Python debugger called :doc:`pdb `. ``pytest`` +allows one to drop into the :doc:`pdb ` prompt via a command line option: .. code-block:: bash @@ -57,10 +55,10 @@ for example:: .. _trace-option: -Dropping to PDB_ at the start of a test -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Dropping to :doc:`pdb ` at the start of a test +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -``pytest`` allows one to drop into the PDB_ prompt immediately at the start of each test via a command line option: +``pytest`` allows one to drop into the :doc:`pdb ` prompt immediately at the start of each test via a command line option: .. code-block:: bash @@ -106,7 +104,7 @@ Fault Handler .. versionadded:: 5.0 -The `faulthandler `__ standard module +The :mod:`faulthandler` standard module can be used to dump Python tracebacks on a segfault or after a timeout. The module is automatically enabled for pytest runs, unless the ``-p no:faulthandler`` is given diff --git a/doc/en/how-to/fixtures.rst b/doc/en/how-to/fixtures.rst index 17efb0356d8..62b429ab061 100644 --- a/doc/en/how-to/fixtures.rst +++ b/doc/en/how-to/fixtures.rst @@ -1577,9 +1577,9 @@ Use fixtures in classes and modules with ``usefixtures`` Sometimes test functions do not directly need access to a fixture object. For example, tests may require to operate with an empty directory as the current working directory but otherwise do not care for the concrete -directory. Here is how you can use the standard `tempfile -`_ and pytest fixtures to -achieve it. We separate the creation of the fixture into a conftest.py +directory. Here is how you can use the standard :mod:`tempfile` +and pytest fixtures to +achieve it. We separate the creation of the fixture into a :file:`conftest.py` file: .. code-block:: python diff --git a/doc/en/how-to/unittest.rst b/doc/en/how-to/unittest.rst index 6f0d334b01f..57b7d257f70 100644 --- a/doc/en/how-to/unittest.rst +++ b/doc/en/how-to/unittest.rst @@ -28,12 +28,11 @@ Almost all ``unittest`` features are supported: * ``setUpModule/tearDownModule``; .. _`load_tests protocol`: https://docs.python.org/3/library/how-to/unittest.html#load-tests-protocol -.. _`subtests`: https://docs.python.org/3/library/how-to/unittest.html#distinguishing-test-iterations-using-subtests Up to this point pytest does not have support for the following features: * `load_tests protocol`_; -* `subtests`_; +* :ref:`subtests `; Benefits out of the box ----------------------- diff --git a/doc/en/how-to/xunit_setup.rst b/doc/en/how-to/xunit_setup.rst index 6d119299f14..5a97b2c85f1 100644 --- a/doc/en/how-to/xunit_setup.rst +++ b/doc/en/how-to/xunit_setup.rst @@ -115,5 +115,3 @@ Remarks: Now the xunit-style functions are integrated with the fixture mechanism and obey the proper scope rules of fixtures involved in the call. - -.. _`unittest.py module`: https://docs.python.org/library/how-to/unittest.html diff --git a/doc/en/reference/customize.rst b/doc/en/reference/customize.rst index 035d0f7ade2..04845f9354a 100644 --- a/doc/en/reference/customize.rst +++ b/doc/en/reference/customize.rst @@ -89,7 +89,7 @@ and can also be used to hold pytest configuration if they have a ``[pytest]`` se setup.cfg ~~~~~~~~~ -``setup.cfg`` files are general purpose configuration files, used originally by `distutils `__, and can also be used to hold pytest configuration +``setup.cfg`` files are general purpose configuration files, used originally by :doc:`distutils `, and can also be used to hold pytest configuration if they have a ``[tool:pytest]`` section. .. code-block:: ini diff --git a/doc/en/reference/reference.rst b/doc/en/reference/reference.rst index 98b5a5064cf..7ff0bd0430e 100644 --- a/doc/en/reference/reference.rst +++ b/doc/en/reference/reference.rst @@ -118,7 +118,7 @@ Add warning filters to marked test items. :keyword str filter: A *warning specification string*, which is composed of contents of the tuple ``(action, message, category, module, lineno)`` - as specified in `The Warnings filter `_ section of + as specified in :ref:`python:warning-filter` section of the Python documentation, separated by ``":"``. Optional fields can be omitted. Module names passed for filtering are not regex-escaped. @@ -958,7 +958,6 @@ _Result Result object used within :ref:`hook wrappers `, see :py:class:`_Result in the pluggy documentation ` for more information. - Stash ~~~~~ @@ -1266,7 +1265,7 @@ passed multiple times. The expected format is ``name=value``. For example:: .. confval:: faulthandler_timeout Dumps the tracebacks of all threads if a test takes longer than ``X`` seconds to run (including - fixture setup and teardown). Implemented using the `faulthandler.dump_traceback_later`_ function, + fixture setup and teardown). Implemented using the :func:`faulthandler.dump_traceback_later` function, so all caveats there apply. .. code-block:: ini @@ -1277,9 +1276,6 @@ passed multiple times. The expected format is ``name=value``. For example:: For more information please refer to :ref:`faulthandler`. -.. _`faulthandler.dump_traceback_later`: https://docs.python.org/3/library/faulthandler.html#faulthandler.dump_traceback_later - - .. confval:: filterwarnings From 44ba2fa37262a822f2a2b2b9cddca703b128726c Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Fri, 27 Aug 2021 09:20:42 -0300 Subject: [PATCH 439/630] Add changelog for #9040 (pluggy 1.0) --- changelog/9040.trivial.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/9040.trivial.rst diff --git a/changelog/9040.trivial.rst b/changelog/9040.trivial.rst new file mode 100644 index 00000000000..aebe21b31d7 --- /dev/null +++ b/changelog/9040.trivial.rst @@ -0,0 +1 @@ +Enable compatibility with ``pluggy 1.0`` or later. From 9501e548524f2314d708ce88e0e572a3b6b565b2 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Fri, 27 Aug 2021 14:26:30 -0300 Subject: [PATCH 440/630] 'prerelease' input is not required in prepare-release-pr workflow The default is the correct value when generating a normal release. --- .github/workflows/prepare-release-pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/prepare-release-pr.yml b/.github/workflows/prepare-release-pr.yml index 08d4fc8a975..2054047721e 100644 --- a/.github/workflows/prepare-release-pr.yml +++ b/.github/workflows/prepare-release-pr.yml @@ -13,7 +13,7 @@ on: default: 'no' prerelease: description: 'Prerelease (ex: rc1). Leave empty if not a pre-release.' - required: true + required: false default: '' # Set permissions at the job level. From 675f8da3a109ff367dcb4204081b3cef3109421b Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Fri, 27 Aug 2021 14:46:28 -0300 Subject: [PATCH 441/630] Fix prepare-release-pr invocation with empty --prerelease argument --- .github/workflows/prepare-release-pr.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/prepare-release-pr.yml b/.github/workflows/prepare-release-pr.yml index 2054047721e..429834b3f21 100644 --- a/.github/workflows/prepare-release-pr.yml +++ b/.github/workflows/prepare-release-pr.yml @@ -44,9 +44,9 @@ jobs: - name: Prepare release PR (minor/patch release) if: github.event.inputs.major == 'no' run: | - tox -e prepare-release-pr -- ${{ github.event.inputs.branch }} ${{ github.token }} --prerelease '${{ github.event.inputs.prerelease }}' + tox -e prepare-release-pr -- ${{ github.event.inputs.branch }} ${{ github.token }} --prerelease='${{ github.event.inputs.prerelease }}' - name: Prepare release PR (major release) if: github.event.inputs.major == 'yes' run: | - tox -e prepare-release-pr -- ${{ github.event.inputs.branch }} ${{ github.token }} --major --prerelease '${{ github.event.inputs.prerelease }}' + tox -e prepare-release-pr -- ${{ github.event.inputs.branch }} ${{ github.token }} --major --prerelease='${{ github.event.inputs.prerelease }}' From 14709f33af108dc77e2a42e334116ddc8de87aff Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sat, 28 Aug 2021 11:59:53 -0300 Subject: [PATCH 442/630] As discussed in Discord, move 3.10 changelog to trivial This was backported to `6.2.x`, but we agreed this should actually a trivial change and go into `6.2.5`, instead of a new `6.3.0`. --- changelog/{8494.feature.rst => 8494.trivial.rst} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename changelog/{8494.feature.rst => 8494.trivial.rst} (100%) diff --git a/changelog/8494.feature.rst b/changelog/8494.trivial.rst similarity index 100% rename from changelog/8494.feature.rst rename to changelog/8494.trivial.rst From 544ecce67774e89bfc5197b457afd4da52191765 Mon Sep 17 00:00:00 2001 From: pytest bot Date: Sun, 29 Aug 2021 00:09:53 +0000 Subject: [PATCH 443/630] [automated] Update plugin list --- doc/en/reference/plugin_list.rst | 51 ++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/doc/en/reference/plugin_list.rst b/doc/en/reference/plugin_list.rst index 2d53f8f736f..6dc1ccbc2b5 100644 --- a/doc/en/reference/plugin_list.rst +++ b/doc/en/reference/plugin_list.rst @@ -6,13 +6,13 @@ Plugin List PyPI projects that match "pytest-\*" are considered plugins and are listed automatically. Packages classified as inactive are excluded. -This list contains 913 plugins. +This list contains 918 plugins. ============================================================================================================== ======================================================================================================================================================================== ============== ===================== ================================================ name summary last release status requires ============================================================================================================== ======================================================================================================================================================================== ============== ===================== ================================================ `pytest-accept `_ A pytest-plugin for updating doctest outputs Jun 02, 2021 N/A pytest (>=6,<7) -`pytest-adaptavist `_ pytest plugin for generating test execution results within Jira Test Management (tm4j) Apr 22, 2021 N/A pytest (>=3.4.1) +`pytest-adaptavist `_ pytest plugin for generating test execution results within Jira Test Management (tm4j) Aug 24, 2021 N/A pytest (>=3.4.1) `pytest-addons-test `_ 用于测试pytest的插件 Aug 02, 2021 N/A pytest (>=6.2.4,<7.0.0) `pytest-adf `_ Pytest plugin for writing Azure Data Factory integration tests May 10, 2021 4 - Beta pytest (>=3.5.0) `pytest-adf-azure-identity `_ Pytest plugin for writing Azure Data Factory integration tests Mar 06, 2021 4 - Beta pytest (>=3.5.0) @@ -64,9 +64,10 @@ name `pytest-attrib `_ pytest plugin to select tests based on attributes similar to the nose-attrib plugin May 24, 2016 4 - Beta N/A `pytest-austin `_ Austin plugin for pytest Oct 11, 2020 4 - Beta N/A `pytest-autochecklog `_ automatically check condition and log all the checks Apr 25, 2015 4 - Beta N/A -`pytest-automation `_ pytest plugin for building a test suite, using YAML files to extend pytest parameterize functionality. Aug 11, 2021 N/A pytest +`pytest-automation `_ pytest plugin for building a test suite, using YAML files to extend pytest parameterize functionality. Aug 24, 2021 N/A pytest `pytest-automock `_ Pytest plugin for automatical mocks creation Apr 22, 2020 N/A pytest ; extra == 'dev' `pytest-auto-parametrize `_ pytest plugin: avoid repeating arguments in parametrize Oct 02, 2016 3 - Alpha N/A +`pytest-autotest `_ This fixture provides a configured "driver" for Android Automated Testing, using uiautomator2. Aug 25, 2021 N/A pytest `pytest-avoidance `_ Makes pytest skip tests that don not need rerunning May 23, 2019 4 - Beta pytest (>=3.5.0) `pytest-aws `_ pytest plugin for testing AWS resource configurations Oct 04, 2017 4 - Beta N/A `pytest-aws-config `_ Protect your AWS credentials in unit tests May 28, 2021 N/A N/A @@ -193,6 +194,7 @@ name `pytest-dbus-notification `_ D-BUS notifications for pytest results. Mar 05, 2014 5 - Production/Stable N/A `pytest-deadfixtures `_ A simple plugin to list unused fixtures in pytest Jul 23, 2020 5 - Production/Stable N/A `pytest-deepcov `_ deepcov Mar 30, 2021 N/A N/A +`pytest-defer `_ Aug 24, 2021 N/A N/A `pytest-demo-plugin `_ pytest示例插件 May 15, 2021 N/A N/A `pytest-dependency `_ Manage dependencies of tests Feb 14, 2020 4 - Beta N/A `pytest-depends `_ Tests that depend on other tests Apr 05, 2020 5 - Production/Stable pytest (>=3) @@ -268,13 +270,13 @@ name `pytest-elements `_ Tool to help automate user interfaces Jan 13, 2021 N/A pytest (>=5.4,<6.0) `pytest-elk-reporter `_ A simple plugin to use with pytest Jan 24, 2021 4 - Beta pytest (>=3.5.0) `pytest-email `_ Send execution result email Jul 08, 2020 N/A pytest -`pytest-embedded `_ pytest embedded plugin Aug 12, 2021 N/A pytest (>=6.2.0) -`pytest-embedded-idf `_ pytest embedded plugin for esp-idf project Aug 12, 2021 N/A N/A -`pytest-embedded-jtag `_ pytest embedded plugin for testing with jtag Aug 12, 2021 N/A N/A -`pytest-embedded-qemu `_ pytest embedded plugin for qemu, not target chip Aug 12, 2021 N/A N/A +`pytest-embedded `_ pytest embedded plugin Aug 26, 2021 N/A pytest (>=6.2.0) +`pytest-embedded-idf `_ pytest embedded plugin for esp-idf project Aug 26, 2021 N/A N/A +`pytest-embedded-jtag `_ pytest embedded plugin for testing with jtag Aug 26, 2021 N/A N/A +`pytest-embedded-qemu `_ pytest embedded plugin for qemu, not target chip Aug 26, 2021 N/A N/A `pytest-embedded-qemu-idf `_ pytest embedded plugin for esp-idf project by qemu, not target chip Jun 29, 2021 N/A N/A -`pytest-embedded-serial `_ pytest embedded plugin for testing serial ports Aug 12, 2021 N/A N/A -`pytest-embedded-serial-esp `_ pytest embedded plugin for testing espressif boards via serial ports Aug 12, 2021 N/A N/A +`pytest-embedded-serial `_ pytest embedded plugin for testing serial ports Aug 26, 2021 N/A N/A +`pytest-embedded-serial-esp `_ pytest embedded plugin for testing espressif boards via serial ports Aug 26, 2021 N/A N/A `pytest-emoji `_ A pytest plugin that adds emojis to your test result report Feb 19, 2019 4 - Beta pytest (>=4.2.1) `pytest-emoji-output `_ Pytest plugin to represent test output with emoji support Jun 06, 2021 4 - Beta pytest (==6.0.1) `pytest-enabler `_ Enable installed pytest plugins Jan 19, 2021 5 - Production/Stable pytest (!=3.7.3,>=3.5) ; extra == 'testing' @@ -331,6 +333,7 @@ name `pytest-fixture-order `_ pytest plugin to control fixture evaluation order Aug 25, 2020 N/A pytest (>=3.0) `pytest-fixtures `_ Common fixtures for pytest May 01, 2019 5 - Production/Stable N/A `pytest-fixture-tools `_ Plugin for pytest which provides tools for fixtures Aug 18, 2020 6 - Mature pytest +`pytest-fixture-typecheck `_ A pytest plugin to assert type annotations at runtime. Aug 24, 2021 N/A pytest `pytest-flake8 `_ pytest plugin to check FLAKE8 requirements Dec 16, 2020 4 - Beta pytest (>=3.5) `pytest-flake8-path `_ A pytest fixture for testing flake8 plugins. Aug 11, 2021 5 - Production/Stable pytest `pytest-flakefinder `_ Runs tests multiple times to expose flakiness. Jul 28, 2020 4 - Beta pytest (>=2.7.1) @@ -383,6 +386,7 @@ name `pytest-honors `_ Report on tests that honor constraints, and guard against regressions Mar 06, 2020 4 - Beta N/A `pytest-hoverfly `_ Simplify working with Hoverfly from pytest Jul 12, 2021 N/A pytest (>=5.0) `pytest-hoverfly-wrapper `_ Integrates the Hoverfly HTTP proxy into Pytest Jan 31, 2021 4 - Beta N/A +`pytest-hpfeeds `_ Helpers for testing hpfeeds in your python project Aug 27, 2021 4 - Beta pytest (>=6.2.4,<7.0.0) `pytest-html `_ pytest plugin for generating HTML reports Dec 13, 2020 5 - Production/Stable pytest (!=6.0.0,>=5.0) `pytest-html-lee `_ optimized pytest plugin for generating HTML reports Jun 30, 2020 5 - Production/Stable pytest (>=5.0) `pytest-html-profiling `_ Pytest plugin for generating HTML reports with per-test profiling and optionally call graph visualizations. Based on pytest-html by Dave Hunt. Feb 11, 2020 5 - Production/Stable pytest (>=3.0) @@ -434,14 +438,14 @@ name `pytest-json `_ Generate JSON test reports Jan 18, 2016 4 - Beta N/A `pytest-jsonlint `_ UNKNOWN Aug 04, 2016 N/A N/A `pytest-json-report `_ A pytest plugin to report test results as JSON files Jun 18, 2021 4 - Beta pytest (>=3.8.0) -`pytest-kafka `_ Zookeeper, Kafka server, and Kafka consumer fixtures for Pytest Nov 01, 2019 N/A pytest +`pytest-kafka `_ Zookeeper, Kafka server, and Kafka consumer fixtures for Pytest Aug 24, 2021 N/A pytest `pytest-kind `_ Kubernetes test support with KIND for pytest Jan 24, 2021 5 - Production/Stable N/A `pytest-kivy `_ Kivy GUI tests fixtures using pytest Jul 06, 2021 4 - Beta pytest (>=3.6) `pytest-knows `_ A pytest plugin that can automaticly skip test case based on dependence info calculated by trace Aug 22, 2014 N/A N/A `pytest-konira `_ Run Konira DSL tests with py.test Oct 09, 2011 N/A N/A `pytest-krtech-common `_ pytest krtech common library Nov 28, 2016 4 - Beta N/A `pytest-kwparametrize `_ Alternate syntax for @pytest.mark.parametrize with test cases as dictionaries and default value fallbacks Jan 22, 2021 N/A pytest (>=6) -`pytest-lambda `_ Define pytest fixtures with lambda functions. Dec 28, 2020 3 - Alpha pytest (>=3.6,<7) +`pytest-lambda `_ Define pytest fixtures with lambda functions. Aug 23, 2021 3 - Alpha pytest (>=3.6,<7) `pytest-lamp `_ Jan 06, 2017 3 - Alpha N/A `pytest-layab `_ Pytest fixtures for layab. Oct 05, 2020 5 - Production/Stable N/A `pytest-lazy-fixture `_ It helps to use fixtures in pytest.mark.parametrize Feb 01, 2020 4 - Beta pytest (>=3.2.5) @@ -458,7 +462,7 @@ name `pytest-listener `_ A simple network listener May 28, 2019 5 - Production/Stable pytest `pytest-litf `_ A pytest plugin that stream output in LITF format Jan 18, 2021 4 - Beta pytest (>=3.1.1) `pytest-live `_ Live results for pytest Mar 08, 2020 N/A pytest -`pytest-localftpserver `_ A PyTest plugin which provides an FTP fixture for your tests Jan 27, 2021 5 - Production/Stable pytest +`pytest-localftpserver `_ A PyTest plugin which provides an FTP fixture for your tests Aug 25, 2021 5 - Production/Stable pytest `pytest-localserver `_ py.test plugin to test server connections locally. Nov 14, 2018 4 - Beta N/A `pytest-localstack `_ Pytest plugin for AWS integration tests Aug 22, 2019 4 - Beta pytest (>=3.3.0) `pytest-lockable `_ lockable resource plugin for pytest Aug 19, 2021 5 - Production/Stable pytest @@ -501,7 +505,7 @@ name `pytest-mock-helper `_ Help you mock HTTP call and generate mock code Jan 24, 2018 N/A pytest `pytest-mockito `_ Base fixtures for mockito Jul 11, 2018 4 - Beta N/A `pytest-mockredis `_ An in-memory mock of a Redis server that runs in a separate thread. This is to be used for unit-tests that require a Redis database. Jan 02, 2018 2 - Pre-Alpha N/A -`pytest-mock-resources `_ A pytest plugin for easily instantiating reproducible mock resources. Aug 17, 2021 N/A pytest (>=1.0) +`pytest-mock-resources `_ A pytest plugin for easily instantiating reproducible mock resources. Aug 24, 2021 N/A pytest (>=1.0) `pytest-mock-server `_ Mock server plugin for pytest Apr 06, 2020 4 - Beta N/A `pytest-mockservers `_ A set of fixtures to test your requests to HTTP/UDP servers Mar 31, 2020 N/A pytest (>=4.3.0) `pytest-modifyjunit `_ Utility for adding additional properties to junit xml for IDM QE Jan 10, 2019 N/A N/A @@ -509,7 +513,7 @@ name `pytest-molecule `_ PyTest Molecule Plugin :: discover and run molecule tests Jul 30, 2021 5 - Production/Stable N/A `pytest-mongo `_ MongoDB process and client fixtures plugin for Pytest. Jun 07, 2021 5 - Production/Stable pytest `pytest-mongodb `_ pytest plugin for MongoDB fixtures Dec 07, 2019 5 - Production/Stable pytest (>=2.5.2) -`pytest-monitor `_ Pytest plugin for analyzing resource usage. Apr 21, 2021 5 - Production/Stable pytest +`pytest-monitor `_ Pytest plugin for analyzing resource usage. Aug 24, 2021 5 - Production/Stable pytest `pytest-monkeyplus `_ pytest's monkeypatch subclass with extra functionalities Sep 18, 2012 5 - Production/Stable N/A `pytest-monkeytype `_ pytest-monkeytype: Generate Monkeytype annotations from your pytest tests. Jul 29, 2020 4 - Beta N/A `pytest-moto `_ Fixtures for integration tests of AWS services,uses moto mocking library. Aug 28, 2015 1 - Planning N/A @@ -558,7 +562,7 @@ name `pytest-oot `_ Run object-oriented tests in a simple format Sep 18, 2016 4 - Beta N/A `pytest-openfiles `_ Pytest plugin for detecting inadvertent open file handles Apr 16, 2020 3 - Alpha pytest (>=4.6) `pytest-opentmi `_ pytest plugin for publish results to opentmi Feb 26, 2021 5 - Production/Stable pytest (>=5.0) -`pytest-operator `_ Fixtures for Operators Aug 10, 2021 N/A N/A +`pytest-operator `_ Fixtures for Operators Aug 26, 2021 N/A N/A `pytest-optional `_ include/exclude values of fixtures in pytest Oct 07, 2015 N/A N/A `pytest-optional-tests `_ Easy declaration of optional tests (i.e., that are not run by default) Jul 09, 2019 4 - Beta pytest (>=4.5.0) `pytest-orchestration `_ A pytest plugin for orchestrating tests Jul 18, 2019 N/A N/A @@ -577,7 +581,7 @@ name `pytest-pass `_ Check out https://github.com/elilutsky/pytest-pass Dec 04, 2019 N/A N/A `pytest-passrunner `_ Pytest plugin providing the 'run_on_pass' marker Feb 10, 2021 5 - Production/Stable pytest (>=4.6.0) `pytest-paste-config `_ Allow setting the path to a paste config file Sep 18, 2013 3 - Alpha N/A -`pytest-patches `_ A patches test fixture which provides a contextmanager for handling multiple mock patches Aug 21, 2021 4 - Beta pytest (>=3.5.0) +`pytest-patches `_ A patches test fixture which provides a contextmanager for handling multiple mock patches Aug 24, 2021 4 - Beta pytest (>=3.5.0) `pytest-pdb `_ pytest plugin which adds pdb helper commands related to pytest. Jul 31, 2018 N/A N/A `pytest-peach `_ pytest plugin for fuzzing with Peach API Security Apr 12, 2019 4 - Beta pytest (>=2.8.7) `pytest-pep257 `_ py.test plugin for pep257 Jul 09, 2016 N/A N/A @@ -652,7 +656,7 @@ name `pytest-rabbitmq `_ RabbitMQ process and client fixtures for pytest Jun 02, 2021 5 - Production/Stable pytest (>=3.0.0) `pytest-race `_ Race conditions tester for pytest Nov 21, 2016 4 - Beta N/A `pytest-rage `_ pytest plugin to implement PEP712 Oct 21, 2011 3 - Alpha N/A -`pytest-railflow-testrail-reporter `_ Generate json reports along with specified metadata defined in test markers. Aug 18, 2021 5 - Production/Stable pytest +`pytest-railflow-testrail-reporter `_ Generate json reports along with specified metadata defined in test markers. Aug 26, 2021 5 - Production/Stable pytest `pytest-raises `_ An implementation of pytest.raises as a pytest.mark fixture Apr 23, 2020 N/A pytest (>=3.2.2) `pytest-raisesregexp `_ Simple pytest plugin to look for regex in Exceptions Dec 18, 2015 N/A N/A `pytest-raisin `_ Plugin enabling the use of exception instances with pytest.raises Jun 25, 2020 N/A pytest @@ -695,7 +699,7 @@ name `pytest-reraise `_ Make multi-threaded pytest test cases fail when they should Jun 17, 2021 5 - Production/Stable pytest (>=4.6) `pytest-rerun `_ Re-run only changed files in specified branch Jul 08, 2019 N/A pytest (>=3.6) `pytest-rerunfailures `_ pytest plugin to re-run tests to eliminate flaky failures Jul 02, 2021 5 - Production/Stable pytest (>=5.3) -`pytest-resilient-circuits `_ Resilient Circuits fixtures for PyTest. Jul 01, 2021 N/A N/A +`pytest-resilient-circuits `_ Resilient Circuits fixtures for PyTest. Aug 25, 2021 N/A N/A `pytest-resource `_ Load resource fixture plugin to use with pytest Nov 14, 2018 4 - Beta N/A `pytest-resource-path `_ Provides path for uniform access to test resources in isolated directory May 01, 2021 5 - Production/Stable pytest (>=3.5.0) `pytest-responsemock `_ Simplified requests calls mocking for pytest Oct 10, 2020 5 - Production/Stable N/A @@ -714,6 +718,7 @@ name `pytest-run-changed `_ Pytest plugin that runs changed tests only Apr 02, 2021 3 - Alpha pytest `pytest-runfailed `_ implement a --failed option for pytest Mar 24, 2016 N/A N/A `pytest-runner `_ Invoke py.test as distutils command with dependency resolution May 19, 2021 5 - Production/Stable pytest (>=4.6) ; extra == 'testing' +`pytest-runtime-xfail `_ Call runtime_xfail() to mark running test as xfail. Aug 26, 2021 N/A N/A `pytest-salt `_ Pytest Salt Plugin Jan 27, 2020 4 - Beta N/A `pytest-salt-containers `_ A Pytest plugin that builds and creates docker containers Nov 09, 2016 4 - Beta N/A `pytest-salt-factories `_ Pytest Salt Plugin Aug 15, 2021 4 - Beta pytest (>=6.0.0) @@ -722,13 +727,13 @@ name `pytest-sanic `_ a pytest plugin for Sanic Jul 27, 2021 N/A pytest (>=5.2) `pytest-sanity `_ Dec 07, 2020 N/A N/A `pytest-sa-pg `_ May 14, 2019 N/A N/A -`pytest-sbase `_ A complete web automation framework for end-to-end testing. Aug 11, 2021 5 - Production/Stable N/A +`pytest-sbase `_ A complete web automation framework for end-to-end testing. Aug 24, 2021 5 - Production/Stable N/A `pytest-scenario `_ pytest plugin for test scenarios Feb 06, 2017 3 - Alpha N/A `pytest-schema `_ 👍 Validate return values against a schema-like object in testing Aug 31, 2020 5 - Production/Stable pytest (>=3.5.0) `pytest-securestore `_ An encrypted password store for use within pytest cases Jun 19, 2019 4 - Beta N/A `pytest-select `_ A pytest plugin which allows to (de-)select tests from a file. Jan 18, 2019 3 - Alpha pytest (>=3.0) `pytest-selenium `_ pytest plugin for Selenium Sep 19, 2020 5 - Production/Stable pytest (>=5.0.0) -`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Aug 11, 2021 5 - Production/Stable N/A +`pytest-seleniumbase `_ A complete web automation framework for end-to-end testing. Aug 24, 2021 5 - Production/Stable N/A `pytest-selenium-enhancer `_ pytest plugin for Selenium Nov 26, 2020 5 - Production/Stable N/A `pytest-selenium-pdiff `_ A pytest package implementing perceptualdiff for Selenium tests. Apr 06, 2017 2 - Pre-Alpha N/A `pytest-send-email `_ Send pytest execution result email Dec 04, 2019 N/A N/A @@ -760,7 +765,7 @@ name `pytest-snapci `_ py.test plugin for Snap-CI Nov 12, 2015 N/A N/A `pytest-snapshot `_ A plugin to enable snapshot testing with pytest. Apr 20, 2021 4 - Beta pytest (>=3.0.0) `pytest-snmpserver `_ May 12, 2021 N/A N/A -`pytest-socket `_ Pytest Plugin to disable socket calls during tests Mar 30, 2021 4 - Beta pytest (>=3.6.3) +`pytest-socket `_ Pytest Plugin to disable socket calls during tests Aug 28, 2021 4 - Beta pytest (>=3.6.3) `pytest-soft-assertions `_ May 05, 2020 3 - Alpha pytest `pytest-solr `_ Solr process and client fixtures for py.test. May 11, 2020 3 - Alpha pytest (>=3.0.0) `pytest-sorter `_ A simple plugin to first execute tests that historically failed more Apr 20, 2021 4 - Beta pytest (>=3.1.1) @@ -775,7 +780,7 @@ name `pytest-splitio `_ Split.io SDK integration for e2e tests Sep 22, 2020 N/A pytest (<7,>=5.0) `pytest-split-tests `_ A Pytest plugin for running a subset of your tests by splitting them in to equally sized groups. Forked from Mark Adams' original project pytest-test-groups. Jul 30, 2021 5 - Production/Stable pytest (>=2.5) `pytest-split-tests-tresorit `_ Feb 22, 2021 1 - Planning N/A -`pytest-splunk-addon `_ A Dynamic test tool for Splunk Apps and Add-ons Aug 19, 2021 N/A pytest (>5.4.0,<6.3) +`pytest-splunk-addon `_ A Dynamic test tool for Splunk Apps and Add-ons Aug 27, 2021 N/A pytest (>5.4.0,<6.3) `pytest-splunk-addon-ui-smartx `_ Library to support testing Splunk Add-on UX Aug 13, 2021 N/A N/A `pytest-splunk-env `_ pytest fixtures for interaction with Splunk Enterprise and Splunk Cloud Oct 22, 2020 N/A pytest (>=6.1.1,<7.0.0) `pytest-sqitch `_ sqitch for pytest Apr 06, 2020 4 - Beta N/A @@ -865,7 +870,7 @@ name `pytest-tspwplib `_ A simple plugin to use with tspwplib Jan 08, 2021 4 - Beta pytest (>=3.5.0) `pytest-tstcls `_ Test Class Base Mar 23, 2020 5 - Production/Stable N/A `pytest-twisted `_ A twisted plugin for pytest. Jul 22, 2021 5 - Production/Stable pytest (>=2.3) -`pytest-typhoon-xray `_ Typhoon HIL plugin for pytest Jun 22, 2020 4 - Beta pytest (>=5.4.2) +`pytest-typhoon-xray `_ Typhoon HIL plugin for pytest Aug 27, 2021 4 - Beta N/A `pytest-tytest `_ Typhoon HIL plugin for pytest May 25, 2020 4 - Beta pytest (>=5.4.2) `pytest-ubersmith `_ Easily mock calls to ubersmith at the `requests` level. Apr 13, 2015 N/A N/A `pytest-ui `_ Text User Interface for running python tests Jul 05, 2021 4 - Beta pytest From ea5ff44ef3e42b9f78524d6971d6cae4a89c6290 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Mon, 30 Aug 2021 14:40:59 -0300 Subject: [PATCH 444/630] Merge pull request #9057 from pytest-dev/release-6.2.5 (cherry picked from commit 16664a1b72c668161553b64e741492206af441bc) --- changelog/8494.trivial.rst | 1 - changelog/9040.trivial.rst | 1 - doc/en/announce/index.rst | 1 + doc/en/announce/release-6.2.5.rst | 30 +++++++++++++++++++++++++ doc/en/changelog.rst | 13 +++++++++++ doc/en/example/markers.rst | 28 +++++++++++------------ doc/en/example/nonpython.rst | 6 ++--- doc/en/example/parametrize.rst | 14 ++++++------ doc/en/example/pythoncollection.rst | 6 ++--- doc/en/example/reportingdemo.rst | 2 +- doc/en/example/simple.rst | 22 +++++++++--------- doc/en/getting-started.rst | 4 ++-- doc/en/how-to/assert.rst | 4 ++-- doc/en/how-to/cache.rst | 8 +++---- doc/en/how-to/capture-stdout-stderr.rst | 2 +- doc/en/how-to/capture-warnings.rst | 2 +- doc/en/how-to/doctest.rst | 4 ++-- doc/en/how-to/fixtures.rst | 10 ++++----- doc/en/how-to/output.rst | 6 ++--- doc/en/how-to/parametrize.rst | 4 ++-- doc/en/how-to/skipping.rst | 2 +- doc/en/how-to/tmp_path.rst | 2 +- doc/en/how-to/unittest.rst | 2 +- doc/en/how-to/writing_plugins.rst | 2 +- doc/en/index.rst | 2 +- 25 files changed, 110 insertions(+), 68 deletions(-) delete mode 100644 changelog/8494.trivial.rst delete mode 100644 changelog/9040.trivial.rst create mode 100644 doc/en/announce/release-6.2.5.rst diff --git a/changelog/8494.trivial.rst b/changelog/8494.trivial.rst deleted file mode 100644 index eca51d0deb9..00000000000 --- a/changelog/8494.trivial.rst +++ /dev/null @@ -1 +0,0 @@ -Python 3.10 is now supported. diff --git a/changelog/9040.trivial.rst b/changelog/9040.trivial.rst deleted file mode 100644 index aebe21b31d7..00000000000 --- a/changelog/9040.trivial.rst +++ /dev/null @@ -1 +0,0 @@ -Enable compatibility with ``pluggy 1.0`` or later. diff --git a/doc/en/announce/index.rst b/doc/en/announce/index.rst index 7751405a196..f0c84cb4c47 100644 --- a/doc/en/announce/index.rst +++ b/doc/en/announce/index.rst @@ -6,6 +6,7 @@ Release announcements :maxdepth: 2 + release-6.2.5 release-6.2.4 release-6.2.3 release-6.2.2 diff --git a/doc/en/announce/release-6.2.5.rst b/doc/en/announce/release-6.2.5.rst new file mode 100644 index 00000000000..bc6b4cf4222 --- /dev/null +++ b/doc/en/announce/release-6.2.5.rst @@ -0,0 +1,30 @@ +pytest-6.2.5 +======================================= + +pytest 6.2.5 has just been released to PyPI. + +This is a bug-fix release, being a drop-in replacement. To upgrade:: + + pip install --upgrade pytest + +The full changelog is available at https://docs.pytest.org/en/stable/changelog.html. + +Thanks to all of the contributors to this release: + +* Anthony Sottile +* Bruno Oliveira +* Brylie Christopher Oxley +* Daniel Asztalos +* Florian Bruhin +* Jason Haugen +* MapleCCC +* Michał Górny +* Miro Hrončok +* Ran Benita +* Ronny Pfannschmidt +* Sylvain Bellemare +* Thomas Güttler + + +Happy testing, +The pytest Development Team diff --git a/doc/en/changelog.rst b/doc/en/changelog.rst index 49c09eba041..6fff9c8806f 100644 --- a/doc/en/changelog.rst +++ b/doc/en/changelog.rst @@ -28,6 +28,19 @@ with advance notice in the **Deprecations** section of releases. .. towncrier release notes start +pytest 6.2.5 (2021-08-29) +========================= + + +Trivial/Internal Changes +------------------------ + +- `#8494 `_: Python 3.10 is now supported. + + +- `#9040 `_: Enable compatibility with ``pluggy 1.0`` or later. + + pytest 6.2.4 (2021-05-04) ========================= diff --git a/doc/en/example/markers.rst b/doc/en/example/markers.rst index 66ef7dda7ed..6f76001dca2 100644 --- a/doc/en/example/markers.rst +++ b/doc/en/example/markers.rst @@ -45,7 +45,7 @@ You can then restrict a test run to only run tests marked with ``webtest``: $ pytest -v -m webtest =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collecting ... collected 4 items / 3 deselected / 1 selected @@ -60,7 +60,7 @@ Or the inverse, running all tests except the webtest ones: $ pytest -v -m "not webtest" =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collecting ... collected 4 items / 1 deselected / 3 selected @@ -82,7 +82,7 @@ tests based on their module, class, method, or function name: $ pytest -v test_server.py::TestClass::test_method =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collecting ... collected 1 item @@ -97,7 +97,7 @@ You can also select on the class: $ pytest -v test_server.py::TestClass =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collecting ... collected 1 item @@ -112,7 +112,7 @@ Or select multiple nodes: $ pytest -v test_server.py::TestClass test_server.py::test_send_http =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collecting ... collected 2 items @@ -156,7 +156,7 @@ The expression matching is now case-insensitive. $ pytest -v -k http # running with the above defined example module =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collecting ... collected 4 items / 3 deselected / 1 selected @@ -171,7 +171,7 @@ And you can also run all tests except the ones that match the keyword: $ pytest -k "not send_http" -v =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collecting ... collected 4 items / 1 deselected / 3 selected @@ -188,7 +188,7 @@ Or to select "http" and "quick" tests: $ pytest -k "http or quick" -v =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collecting ... collected 4 items / 2 deselected / 2 selected @@ -397,7 +397,7 @@ the test needs: $ pytest -E stage2 =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 1 item @@ -412,7 +412,7 @@ and here is one that specifies exactly the environment needed: $ pytest -E stage1 =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 1 item @@ -605,7 +605,7 @@ then you will see two tests skipped and two executed tests as expected: $ pytest -rs # this option reports skip reasons =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 4 items @@ -622,7 +622,7 @@ Note that if you specify a platform via the marker-command line option like this $ pytest -m linux =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 4 items / 3 deselected / 1 selected @@ -686,7 +686,7 @@ We can now use the ``-m option`` to select one set: $ pytest -m interface --tb=short =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 4 items / 2 deselected / 2 selected @@ -713,7 +713,7 @@ or to select both "event" and "interface" tests: $ pytest -m "interface or event" --tb=short =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 4 items / 1 deselected / 3 selected diff --git a/doc/en/example/nonpython.rst b/doc/en/example/nonpython.rst index a3477fe1e1d..4533642d442 100644 --- a/doc/en/example/nonpython.rst +++ b/doc/en/example/nonpython.rst @@ -29,7 +29,7 @@ now execute the test specification: nonpython $ pytest test_simple.yaml =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR/nonpython collected 2 items @@ -66,7 +66,7 @@ consulted when reporting in ``verbose`` mode: nonpython $ pytest -v =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR/nonpython collecting ... collected 2 items @@ -92,7 +92,7 @@ interesting to just look at the collection tree: nonpython $ pytest --collect-only =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR/nonpython collected 2 items diff --git a/doc/en/example/parametrize.rst b/doc/en/example/parametrize.rst index f358fe50091..a91162bcfb2 100644 --- a/doc/en/example/parametrize.rst +++ b/doc/en/example/parametrize.rst @@ -160,7 +160,7 @@ objects, they are still using the default pytest representation: $ pytest test_time.py --collect-only =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 8 items @@ -225,7 +225,7 @@ this is a fully self-contained example which you can run with: $ pytest test_scenarios.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 4 items @@ -240,7 +240,7 @@ If you just collect tests you'll also nicely see 'advanced' and 'basic' as varia $ pytest --collect-only test_scenarios.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 4 items @@ -319,7 +319,7 @@ Let's first see how it looks like at collection time: $ pytest test_backends.py --collect-only =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 2 items @@ -418,7 +418,7 @@ The result of this test will be successful: $ pytest -v test_indirect_list.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collecting ... collected 1 item @@ -573,7 +573,7 @@ If you run this with reporting for skips enabled: $ pytest -rs test_module.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 2 items @@ -635,7 +635,7 @@ Then run ``pytest`` with verbose mode and with only the ``basic`` marker: $ pytest -v -m basic =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collecting ... collected 24 items / 21 deselected / 3 selected diff --git a/doc/en/example/pythoncollection.rst b/doc/en/example/pythoncollection.rst index a6ce2e742e5..15889a37c1e 100644 --- a/doc/en/example/pythoncollection.rst +++ b/doc/en/example/pythoncollection.rst @@ -147,7 +147,7 @@ The test collection would look like this: $ pytest --collect-only =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR, configfile: pytest.ini collected 2 items @@ -209,7 +209,7 @@ You can always peek at the collection tree without running tests like this: . $ pytest --collect-only pythoncollection.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR, configfile: pytest.ini collected 3 items @@ -291,7 +291,7 @@ file will be left out: $ pytest --collect-only =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR, configfile: pytest.ini collected 0 items diff --git a/doc/en/example/reportingdemo.rst b/doc/en/example/reportingdemo.rst index 6e7dbe49683..f7635e0acf1 100644 --- a/doc/en/example/reportingdemo.rst +++ b/doc/en/example/reportingdemo.rst @@ -9,7 +9,7 @@ Here is a nice run of several failures and how ``pytest`` presents things: assertion $ pytest failure_demo.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR/assertion collected 44 items diff --git a/doc/en/example/simple.rst b/doc/en/example/simple.rst index 131534a1744..9bc51a88bbb 100644 --- a/doc/en/example/simple.rst +++ b/doc/en/example/simple.rst @@ -231,7 +231,7 @@ directory with the above conftest.py: $ pytest =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 0 items @@ -296,7 +296,7 @@ and when running it will see a skipped "slow" test: $ pytest -rs # "-rs" means report details on the little 's' =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 2 items @@ -313,7 +313,7 @@ Or run it including the ``slow`` marked test: $ pytest --runslow =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 2 items @@ -457,7 +457,7 @@ which will add the string to the test header accordingly: $ pytest =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache project deps: mylib-1.1 rootdir: $REGENDOC_TMPDIR @@ -486,7 +486,7 @@ which will add info only when run with "--v": $ pytest -v =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python cachedir: $PYTHON_PREFIX/.pytest_cache info1: did you know that ... did you? @@ -501,7 +501,7 @@ and nothing when run plainly: $ pytest =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 0 items @@ -541,7 +541,7 @@ Now we can profile which test functions execute the slowest: $ pytest --durations=3 =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 3 items @@ -647,7 +647,7 @@ If we run this: $ pytest -rx =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 4 items @@ -731,7 +731,7 @@ We can run this: $ pytest =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 7 items @@ -850,7 +850,7 @@ and run them: $ pytest test_module.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 2 items @@ -957,7 +957,7 @@ and run it: $ pytest -s test_module.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 3 items diff --git a/doc/en/getting-started.rst b/doc/en/getting-started.rst index bbe2a680ee9..af38b99fd01 100644 --- a/doc/en/getting-started.rst +++ b/doc/en/getting-started.rst @@ -22,7 +22,7 @@ Install ``pytest`` .. code-block:: bash $ pytest --version - pytest 6.2.4 + pytest 6.2.5 .. _`simpletest`: @@ -47,7 +47,7 @@ The test $ pytest =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 1 item diff --git a/doc/en/how-to/assert.rst b/doc/en/how-to/assert.rst index 895afc02043..0aa4980f1a4 100644 --- a/doc/en/how-to/assert.rst +++ b/doc/en/how-to/assert.rst @@ -29,7 +29,7 @@ you will see the return value of the function call: $ pytest test_assert1.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 1 item @@ -184,7 +184,7 @@ if you run this module: $ pytest test_assert2.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 1 item diff --git a/doc/en/how-to/cache.rst b/doc/en/how-to/cache.rst index ae865fe280e..5b1b2440d56 100644 --- a/doc/en/how-to/cache.rst +++ b/doc/en/how-to/cache.rst @@ -86,7 +86,7 @@ If you then run it with ``--lf``: $ pytest --lf =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 2 items @@ -133,7 +133,7 @@ of ``FF`` and dots): $ pytest --ff =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 50 items @@ -277,7 +277,7 @@ You can always peek at the content of the cache using the $ pytest --cache-show =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR cachedir: $PYTHON_PREFIX/.pytest_cache @@ -358,7 +358,7 @@ filtering: $ pytest --cache-show example/* =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR cachedir: $PYTHON_PREFIX/.pytest_cache diff --git a/doc/en/how-to/capture-stdout-stderr.rst b/doc/en/how-to/capture-stdout-stderr.rst index ea0063853ec..5d405a166a8 100644 --- a/doc/en/how-to/capture-stdout-stderr.rst +++ b/doc/en/how-to/capture-stdout-stderr.rst @@ -83,7 +83,7 @@ of the failing function and hide the other one: $ pytest =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 2 items diff --git a/doc/en/how-to/capture-warnings.rst b/doc/en/how-to/capture-warnings.rst index 188a7316df9..be8dd0aaaf6 100644 --- a/doc/en/how-to/capture-warnings.rst +++ b/doc/en/how-to/capture-warnings.rst @@ -28,7 +28,7 @@ Running pytest now produces this output: $ pytest test_show_warnings.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 1 item diff --git a/doc/en/how-to/doctest.rst b/doc/en/how-to/doctest.rst index c08ce509329..fd1facecefa 100644 --- a/doc/en/how-to/doctest.rst +++ b/doc/en/how-to/doctest.rst @@ -30,7 +30,7 @@ then you can just invoke ``pytest`` directly: $ pytest =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 1 item @@ -59,7 +59,7 @@ and functions, including from test modules: $ pytest --doctest-modules =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 2 items diff --git a/doc/en/how-to/fixtures.rst b/doc/en/how-to/fixtures.rst index 17efb0356d8..a5a3ff20011 100644 --- a/doc/en/how-to/fixtures.rst +++ b/doc/en/how-to/fixtures.rst @@ -432,7 +432,7 @@ marked ``smtp_connection`` fixture function. Running the test looks like this: $ pytest test_module.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 2 items @@ -1331,7 +1331,7 @@ Running the above tests results in the following test IDs being used: $ pytest --collect-only =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 11 items @@ -1384,7 +1384,7 @@ Running this test will *skip* the invocation of ``data_set`` with value ``2``: $ pytest test_fixture_marks.py -v =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collecting ... collected 3 items @@ -1434,7 +1434,7 @@ Here we declare an ``app`` fixture which receives the previously defined $ pytest -v test_appsetup.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collecting ... collected 2 items @@ -1514,7 +1514,7 @@ Let's run the tests in verbose mode and with looking at the print-output: $ pytest -v -s test_module.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y -- $PYTHON_PREFIX/bin/python + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y -- $PYTHON_PREFIX/bin/python cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collecting ... collected 8 items diff --git a/doc/en/how-to/output.rst b/doc/en/how-to/output.rst index 9f922b841df..91cfe2603f1 100644 --- a/doc/en/how-to/output.rst +++ b/doc/en/how-to/output.rst @@ -321,7 +321,7 @@ Example: $ pytest -ra =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 6 items @@ -379,7 +379,7 @@ More than one character can be used, so for example to only see failed and skipp $ pytest -rfs =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 6 items @@ -415,7 +415,7 @@ captured output: $ pytest -rpP =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 6 items diff --git a/doc/en/how-to/parametrize.rst b/doc/en/how-to/parametrize.rst index 6c2bac4ea23..18163db6fa3 100644 --- a/doc/en/how-to/parametrize.rst +++ b/doc/en/how-to/parametrize.rst @@ -56,7 +56,7 @@ them in turn: $ pytest =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 3 items @@ -168,7 +168,7 @@ Let's run this: $ pytest =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 3 items diff --git a/doc/en/how-to/skipping.rst b/doc/en/how-to/skipping.rst index 156570062f1..9b74628d59f 100644 --- a/doc/en/how-to/skipping.rst +++ b/doc/en/how-to/skipping.rst @@ -376,7 +376,7 @@ Running it with the report-on-xfail option gives this output: ! pytest -rx xfail_demo.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR/example collected 7 items diff --git a/doc/en/how-to/tmp_path.rst b/doc/en/how-to/tmp_path.rst index 740cd2e369f..d31f5956ae6 100644 --- a/doc/en/how-to/tmp_path.rst +++ b/doc/en/how-to/tmp_path.rst @@ -36,7 +36,7 @@ Running this would result in a passed test except for the last $ pytest test_tmp_path.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 1 item diff --git a/doc/en/how-to/unittest.rst b/doc/en/how-to/unittest.rst index 6f0d334b01f..620e83aad64 100644 --- a/doc/en/how-to/unittest.rst +++ b/doc/en/how-to/unittest.rst @@ -137,7 +137,7 @@ the ``self.db`` values in the traceback: $ pytest test_unittest_db.py =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 2 items diff --git a/doc/en/how-to/writing_plugins.rst b/doc/en/how-to/writing_plugins.rst index 3b58a833320..e461e2adda8 100644 --- a/doc/en/how-to/writing_plugins.rst +++ b/doc/en/how-to/writing_plugins.rst @@ -442,7 +442,7 @@ Additionally it is possible to copy examples for an example folder before runnin $ pytest =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR, configfile: pytest.ini collected 2 items diff --git a/doc/en/index.rst b/doc/en/index.rst index 106d7be356d..6dbff8de65e 100644 --- a/doc/en/index.rst +++ b/doc/en/index.rst @@ -45,7 +45,7 @@ To execute it: $ pytest =========================== test session starts ============================ - platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-0.x.y + platform linux -- Python 3.x.y, pytest-6.x.y, py-1.x.y, pluggy-1.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 1 item From af42e7154a39efac07668c9db238c90c31b4c1e0 Mon Sep 17 00:00:00 2001 From: Kale Kundert Date: Mon, 30 Aug 2021 14:19:31 -0400 Subject: [PATCH 445/630] Prevent approx from being used without a comparison (#9061) Some of the top search-engine hits for pytest.approx use the function without actually comparing it to anything. This PR will cause these tests to fail by implementing approx.__bool__() to raise an AssertionError that briefly explains how to correctly use approx. --- changelog/9061.breaking.rst | 15 +++++++++++++++ src/_pytest/python_api.py | 5 +++++ testing/python/approx.py | 6 ++++++ 3 files changed, 26 insertions(+) create mode 100644 changelog/9061.breaking.rst diff --git a/changelog/9061.breaking.rst b/changelog/9061.breaking.rst new file mode 100644 index 00000000000..bf639e13214 --- /dev/null +++ b/changelog/9061.breaking.rst @@ -0,0 +1,15 @@ +Using :func:`pytest.approx` in a boolean context now raises an error hinting at the proper usage. + +It is apparently common for users to mistakenly use ``pytest.approx`` like this: + +.. code-block:: python + + assert pytest.approx(actual, expected) + +While the correct usage is: + +.. code-block:: python + + assert actual == pytest.approx(expected) + +The new error message helps catch those mistakes. diff --git a/src/_pytest/python_api.py b/src/_pytest/python_api.py index 847991a3708..8332bbe0a7b 100644 --- a/src/_pytest/python_api.py +++ b/src/_pytest/python_api.py @@ -100,6 +100,11 @@ def __eq__(self, actual) -> bool: a == self._approx_scalar(x) for a, x in self._yield_comparisons(actual) ) + def __bool__(self): + raise AssertionError( + "approx() is not supported in a boolean context.\nDid you mean: `assert a == approx(b)`?" + ) + # Ignore type because of https://github.com/python/mypy/issues/4266. __hash__ = None # type: ignore diff --git a/testing/python/approx.py b/testing/python/approx.py index d4a152f3ea4..0d411d8a6da 100644 --- a/testing/python/approx.py +++ b/testing/python/approx.py @@ -319,6 +319,12 @@ def test_repr_nd_array(self, value, expected_repr_string): np_array = np.array(value) assert repr(approx(np_array)) == expected_repr_string + def test_bool(self): + with pytest.raises(AssertionError) as err: + assert approx(1) + + assert err.match(r"approx\(\) is not supported in a boolean context") + def test_operator_overloading(self): assert 1 == approx(1, rel=1e-6, abs=1e-12) assert not (1 != approx(1, rel=1e-6, abs=1e-12)) From 740abd9684386ff76e5557399a8a16201fa1d833 Mon Sep 17 00:00:00 2001 From: Simon K Date: Mon, 30 Aug 2021 19:24:14 +0100 Subject: [PATCH 446/630] #9062 - Allow `--stepwise-skip` to implicitly enable `--stepwise` (#9064) * #9062 - Allow `--stepwise-skip` to implicitly enable `--stepwise` * Update changelog/9062.improvement.rst Co-authored-by: Bruno Oliveira Co-authored-by: Bruno Oliveira --- changelog/9062.improvement.rst | 1 + doc/en/how-to/cache.rst | 2 +- src/_pytest/stepwise.py | 7 +++++-- testing/test_stepwise.py | 30 ++++++++++++++++++++++++++++++ 4 files changed, 37 insertions(+), 3 deletions(-) create mode 100644 changelog/9062.improvement.rst diff --git a/changelog/9062.improvement.rst b/changelog/9062.improvement.rst new file mode 100644 index 00000000000..bfc7cf00a46 --- /dev/null +++ b/changelog/9062.improvement.rst @@ -0,0 +1 @@ +``--stepwise-skip`` now implicitly enables ``--stepwise`` and can be used on its own. diff --git a/doc/en/how-to/cache.rst b/doc/en/how-to/cache.rst index 5b1b2440d56..45dc81c2122 100644 --- a/doc/en/how-to/cache.rst +++ b/doc/en/how-to/cache.rst @@ -386,4 +386,4 @@ than speed. Stepwise -------- -As an alternative to ``--lf -x``, especially for cases where you expect a large part of the test suite will fail, ``--sw``, ``--stepwise`` allows you to fix them one at a time. The test suite will run until the first failure and then stop. At the next invocation, tests will continue from the last failing test and then run until the next failing test. You may use the ``--stepwise-skip`` option to ignore one failing test and stop the test execution on the second failing test instead. This is useful if you get stuck on a failing test and just want to ignore it until later. +As an alternative to ``--lf -x``, especially for cases where you expect a large part of the test suite will fail, ``--sw``, ``--stepwise`` allows you to fix them one at a time. The test suite will run until the first failure and then stop. At the next invocation, tests will continue from the last failing test and then run until the next failing test. You may use the ``--stepwise-skip`` option to ignore one failing test and stop the test execution on the second failing test instead. This is useful if you get stuck on a failing test and just want to ignore it until later. Providing ``--stepwise-skip`` will also enable ``--stepwise`` implicitly. diff --git a/src/_pytest/stepwise.py b/src/_pytest/stepwise.py index 197577c790f..4d95a96b872 100644 --- a/src/_pytest/stepwise.py +++ b/src/_pytest/stepwise.py @@ -31,13 +31,16 @@ def pytest_addoption(parser: Parser) -> None: action="store_true", default=False, dest="stepwise_skip", - help="ignore the first failing test but stop on the next failing test", + help="ignore the first failing test but stop on the next failing test.\n" + "implicitly enables --stepwise.", ) @pytest.hookimpl def pytest_configure(config: Config) -> None: - # We should always have a cache as cache provider plugin uses tryfirst=True + if config.option.stepwise_skip: + # allow --stepwise-skip to work on it's own merits. + config.option.stepwise = True if config.getoption("stepwise"): config.pluginmanager.register(StepwisePlugin(config), "stepwiseplugin") diff --git a/testing/test_stepwise.py b/testing/test_stepwise.py index 85489fce803..63d29d6241f 100644 --- a/testing/test_stepwise.py +++ b/testing/test_stepwise.py @@ -248,3 +248,33 @@ def test_d(): pass "* 2 passed, 1 deselected, 1 xfailed in *", ] ) + + +def test_stepwise_skip_is_independent(pytester: Pytester) -> None: + pytester.makepyfile( + """ + def test_one(): + assert False + + def test_two(): + assert False + + def test_three(): + assert False + + """ + ) + result = pytester.runpytest("--tb", "no", "--stepwise-skip") + result.assert_outcomes(failed=2) + result.stdout.fnmatch_lines( + [ + "FAILED test_stepwise_skip_is_independent.py::test_one - assert False", + "FAILED test_stepwise_skip_is_independent.py::test_two - assert False", + "*Interrupted: Test failed, continuing from this test next run.*", + ] + ) + + +def test_sw_skip_help(pytester: Pytester) -> None: + result = pytester.runpytest("-h") + result.stdout.fnmatch_lines("*implicitly enables --stepwise.") From 7bcdcc02393e9742cc68f78d8aa8df0ea5dd0162 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 30 Aug 2021 19:25:44 +0000 Subject: [PATCH 447/630] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/psf/black: 21.7b0 → 21.8b0](https://github.com/psf/black/compare/21.7b0...21.8b0) - [github.com/asottile/blacken-docs: v1.10.0 → v1.11.0](https://github.com/asottile/blacken-docs/compare/v1.10.0...v1.11.0) - [github.com/asottile/pyupgrade: v2.24.0 → v2.25.0](https://github.com/asottile/pyupgrade/compare/v2.24.0...v2.25.0) --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d6c2cfccef6..ed5bc96da54 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,11 +1,11 @@ repos: - repo: https://github.com/psf/black - rev: 21.7b0 + rev: 21.8b0 hooks: - id: black args: [--safe, --quiet] - repo: https://github.com/asottile/blacken-docs - rev: v1.10.0 + rev: v1.11.0 hooks: - id: blacken-docs additional_dependencies: [black==20.8b1] @@ -34,7 +34,7 @@ repos: - id: reorder-python-imports args: ['--application-directories=.:src', --py36-plus] - repo: https://github.com/asottile/pyupgrade - rev: v2.24.0 + rev: v2.25.0 hooks: - id: pyupgrade args: [--py36-plus] From 59d314de3d6aff2f8af2b2ea1e96694900efe20e Mon Sep 17 00:00:00 2001 From: Emmanuel Arias Date: Tue, 31 Aug 2021 09:12:11 -0300 Subject: [PATCH 448/630] Show fullname on direct Node construction warning This commit add the fullname on the Node construction warning. Also add a test for this case. --- src/_pytest/nodes.py | 2 +- testing/test_nodes.py | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py index e695f89bb3c..d3f0ddcebb4 100644 --- a/src/_pytest/nodes.py +++ b/src/_pytest/nodes.py @@ -123,7 +123,7 @@ def __call__(self, *k, **kw): "See " "https://docs.pytest.org/en/stable/deprecations.html#node-construction-changed-to-node-from-parent" " for more details." - ).format(name=self.__name__) + ).format(name=f"{self.__module__}.{self.__name__}") fail(msg, pytrace=False) def _create(self, *k, **kw): diff --git a/testing/test_nodes.py b/testing/test_nodes.py index 52cd0173c7d..57a942c25db 100644 --- a/testing/test_nodes.py +++ b/testing/test_nodes.py @@ -6,6 +6,7 @@ import pytest from _pytest import nodes from _pytest.compat import legacy_path +from _pytest.outcomes import OutcomeException from _pytest.pytester import Pytester from _pytest.warning_types import PytestWarning @@ -40,6 +41,19 @@ def test_node_from_parent_disallowed_arguments() -> None: nodes.Node.from_parent(None, config=None) # type: ignore[arg-type] +def test_node_direct_construction_deprecated() -> None: + with pytest.raises( + OutcomeException, + match=( + "Direct construction of _pytest.nodes.Node has been deprecated, please " + "use _pytest.nodes.Node.from_parent.\nSee " + "https://docs.pytest.org/en/stable/deprecations.html#node-construction-changed-to-node-from-parent" + " for more details." + ), + ): + nodes.Node(None, session=None) # type: ignore[arg-type] + + def test_subclassing_both_item_and_collector_deprecated( request, tmp_path: Path ) -> None: From dc9192c8eb1fe18455934b56c9bd0ba6329a1ec5 Mon Sep 17 00:00:00 2001 From: Emmanuel Arias Date: Thu, 2 Sep 2021 00:14:19 -0300 Subject: [PATCH 449/630] add changelog file --- changelog/8994.improvement.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 changelog/8994.improvement.rst diff --git a/changelog/8994.improvement.rst b/changelog/8994.improvement.rst new file mode 100644 index 00000000000..2c8a2ef836d --- /dev/null +++ b/changelog/8994.improvement.rst @@ -0,0 +1,2 @@ +Included the module of the class in the error message about direct +node construction (without using ``from_parent``). From e929d15848da4d538f70a830f642a7bf913feaab Mon Sep 17 00:00:00 2001 From: Andrew Neitsch Date: Thu, 2 Sep 2021 17:40:41 -0600 Subject: [PATCH 450/630] Include figures in PDF docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PDF documentation on readthedocs was missing the figures in the fixtures reference chapter. This PR uses a Sphinx plugin that automatically converts the checked-in SVG files to the PDF input files that LaTeX requires. The SVG-to-PDF conversion is done by inkscape, which gave the best conversion among the tools I tried. However, it [does not yet understand][href-bug] that you can write a plain `href` instead of `xlink:href` in svg files, so I’ve had to edit the SVG files accordingly. [href-bug]: https://github.com/TeX-Live/luatex.git --- .readthedocs.yml | 4 ++++ doc/en/conf.py | 1 + .../example/fixtures/fixture_availability.svg | 10 +++++----- .../fixtures/fixture_availability_plugins.svg | 12 ++++++------ ..._fixtures_order_autouse_multiple_scopes.svg | 6 +++--- ...est_fixtures_order_autouse_temp_effects.svg | 6 +++--- .../fixtures/test_fixtures_order_scope.svg | 4 ++-- .../test_fixtures_request_different_scope.svg | 8 ++++---- doc/en/reference/fixtures.rst | 18 +++++++++--------- doc/en/requirements.txt | 1 + 10 files changed, 38 insertions(+), 32 deletions(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index 0176c264001..10c21fd8f3f 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -7,6 +7,10 @@ python: - method: pip path: . +build: + apt_packages: + - inkscape + formats: - epub - pdf diff --git a/doc/en/conf.py b/doc/en/conf.py index ee71ffcae41..35941080bc0 100644 --- a/doc/en/conf.py +++ b/doc/en/conf.py @@ -55,6 +55,7 @@ "sphinx.ext.viewcode", "sphinx_removed_in", "sphinxcontrib_trio", + "sphinxcontrib.inkscapeconverter", ] # Add any paths that contain templates here, relative to this directory. diff --git a/doc/en/example/fixtures/fixture_availability.svg b/doc/en/example/fixtures/fixture_availability.svg index 3ca28447c45..066caac3449 100644 --- a/doc/en/example/fixtures/fixture_availability.svg +++ b/doc/en/example/fixtures/fixture_availability.svg @@ -1,4 +1,4 @@ - +