aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/pytest/py3/_pytest/cacheprovider.py
diff options
context:
space:
mode:
authordeshevoy <deshevoy@yandex-team.ru>2022-02-10 16:46:57 +0300
committerDaniil Cherednik <dcherednik@yandex-team.ru>2022-02-10 16:46:57 +0300
commit28148f76dbfcc644d96427d41c92f36cbf2fdc6e (patch)
treeb83306b6e37edeea782e9eed673d89286c4fef35 /contrib/python/pytest/py3/_pytest/cacheprovider.py
parente988f30484abe5fdeedcc7a5d3c226c01a21800c (diff)
downloadydb-28148f76dbfcc644d96427d41c92f36cbf2fdc6e.tar.gz
Restoring authorship annotation for <deshevoy@yandex-team.ru>. Commit 2 of 2.
Diffstat (limited to 'contrib/python/pytest/py3/_pytest/cacheprovider.py')
-rw-r--r--contrib/python/pytest/py3/_pytest/cacheprovider.py428
1 files changed, 214 insertions, 214 deletions
diff --git a/contrib/python/pytest/py3/_pytest/cacheprovider.py b/contrib/python/pytest/py3/_pytest/cacheprovider.py
index d67e5933cf..03acd03109 100644
--- a/contrib/python/pytest/py3/_pytest/cacheprovider.py
+++ b/contrib/python/pytest/py3/_pytest/cacheprovider.py
@@ -1,8 +1,8 @@
"""Implementation of the cache provider."""
# This plugin was not named "cache" to avoid conflicts with the external
# pytest-cache version.
-import json
-import os
+import json
+import os
from pathlib import Path
from typing import Dict
from typing import Generator
@@ -11,11 +11,11 @@ from typing import List
from typing import Optional
from typing import Set
from typing import Union
-
-import attr
-import py
-
-from .pathlib import resolve_from_str
+
+import attr
+import py
+
+from .pathlib import resolve_from_str
from .pathlib import rm_rf
from .reports import CollectReport
from _pytest import nodes
@@ -32,33 +32,33 @@ from _pytest.main import Session
from _pytest.python import Module
from _pytest.python import Package
from _pytest.reports import TestReport
-
+
README_CONTENT = """\
-# pytest cache directory #
-
-This directory contains data from the pytest's cache plugin,
-which provides the `--lf` and `--ff` options, as well as the `cache` fixture.
-
-**Do not** commit this to version control.
-
+# pytest cache directory #
+
+This directory contains data from the pytest's cache plugin,
+which provides the `--lf` and `--ff` options, as well as the `cache` fixture.
+
+**Do not** commit this to version control.
+
See [the docs](https://docs.pytest.org/en/stable/cache.html) for more information.
-"""
-
+"""
+
CACHEDIR_TAG_CONTENT = b"""\
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
"""
-
+
@final
@attr.s(init=False)
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"
_CACHE_PREFIX_DIRS = "d"
@@ -72,7 +72,7 @@ class Cache:
self._cachedir = cachedir
self._config = config
- @classmethod
+ @classmethod
def for_config(cls, config: Config, *, _ispytest: bool = False) -> "Cache":
"""Create the Cache instance for a Config.
@@ -83,7 +83,7 @@ class Cache:
if config.getoption("cacheclear") and cachedir.is_dir():
cls.clear_cache(cachedir, _ispytest=True)
return cls(cachedir, config, _ispytest=True)
-
+
@classmethod
def clear_cache(cls, cachedir: Path, _ispytest: bool = False) -> None:
"""Clear the sub-directories used to hold cached directories and values.
@@ -96,10 +96,10 @@ class Cache:
if d.is_dir():
rm_rf(d)
- @staticmethod
+ @staticmethod
def cache_dir_from_config(config: Config, *, _ispytest: bool = False) -> Path:
"""Get the path to the cache directory for a Config.
-
+
:meta private:
"""
check_ispytest(_ispytest)
@@ -113,16 +113,16 @@ class Cache:
check_ispytest(_ispytest)
import warnings
from _pytest.warning_types import PytestCacheWarning
-
+
warnings.warn(
PytestCacheWarning(fmt.format(**args) if args else fmt),
self._config.hook,
- stacklevel=3,
- )
-
+ stacklevel=3,
+ )
+
def makedir(self, name: str) -> py.path.local:
"""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.
@@ -131,79 +131,79 @@ class Cache:
Must be a string not containing a ``/`` separator.
Make sure the name contains your plugin or application
identifiers to prevent clashes with other cache users.
- """
+ """
path = Path(name)
if len(path.parts) > 1:
- raise ValueError("name is not allowed to contain path separators")
+ 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)
-
+ res.mkdir(exist_ok=True, parents=True)
+ return py.path.local(res)
+
def _getvaluepath(self, key: str) -> Path:
return self._cachedir.joinpath(self._CACHE_PREFIX_VALUES, Path(key))
-
+
def get(self, key: str, default):
"""Return the cached value for the given key.
If no value was yet cached or the value cannot be read, the specified
- default is returned.
-
+ default is returned.
+
:param key:
Must be a ``/`` separated value. Usually the first
name is the name of your plugin or your application.
:param default:
The value to return in case of a cache-miss or invalid cache value.
- """
- path = self._getvaluepath(key)
- try:
- with path.open("r") as f:
- return json.load(f)
+ """
+ path = self._getvaluepath(key)
+ try:
+ with path.open("r") as f:
+ return json.load(f)
except (ValueError, OSError):
- return default
-
+ return default
+
def set(self, key: str, value: object) -> None:
"""Save value for the given key.
-
+
:param key:
Must be a ``/`` separated value. Usually the first
name is the name of your plugin or your application.
:param value:
Must be of any combination of basic python types,
including nested types like lists of dictionaries.
- """
- path = self._getvaluepath(key)
- try:
- if path.parent.is_dir():
- cache_dir_exists_already = True
- else:
- cache_dir_exists_already = self._cachedir.exists()
+ """
+ path = self._getvaluepath(key)
+ try:
+ if path.parent.is_dir():
+ cache_dir_exists_already = True
+ else:
+ cache_dir_exists_already = self._cachedir.exists()
path.parent.mkdir(exist_ok=True, parents=True)
except OSError:
self.warn("could not create cache path {path}", path=path, _ispytest=True)
- return
+ return
if not cache_dir_exists_already:
self._ensure_supporting_files()
data = json.dumps(value, indent=2, sort_keys=True)
- try:
+ try:
f = path.open("w")
except OSError:
self.warn("cache could not write path {path}", path=path, _ispytest=True)
- else:
- with f:
+ else:
+ with f:
f.write(data)
-
+
def _ensure_supporting_files(self) -> None:
- """Create supporting files in the cache dir that are not really part of the cache."""
+ """Create supporting files in the cache dir that are not really part of the cache."""
readme_path = self._cachedir / "README.md"
readme_path.write_text(README_CONTENT)
-
+
gitignore_path = self._cachedir.joinpath(".gitignore")
msg = "# Created by pytest automatically.\n*\n"
gitignore_path.write_text(msg, encoding="UTF-8")
-
+
cachedir_tag_path = self._cachedir.joinpath("CACHEDIR.TAG")
cachedir_tag_path.write_bytes(CACHEDIR_TAG_CONTENT)
-
+
class LFPluginCollWrapper:
def __init__(self, lfplugin: "LFPlugin") -> None:
@@ -276,17 +276,17 @@ class LFPluginCollSkipfiles:
class LFPlugin:
"""Plugin which implements the --lf (run last-failing) option."""
-
+
def __init__(self, config: Config) -> None:
- self.config = config
- active_keys = "lf", "failedfirst"
- self.active = any(config.getoption(key) for key in active_keys)
+ self.config = config
+ active_keys = "lf", "failedfirst"
+ self.active = any(config.getoption(key) for key in active_keys)
assert config.cache
self.lastfailed: Dict[str, bool] = config.cache.get("cache/lastfailed", {})
self._previously_failed_count: Optional[int] = None
self._report_status: Optional[str] = None
self._skipped_files = 0 # count skipped files during collection due to --lf
-
+
if config.getoption("lf"):
self._last_failed_paths = self.get_last_failed_paths()
config.pluginmanager.register(
@@ -300,25 +300,25 @@ class LFPlugin:
return {x for x in result if x.exists()}
def pytest_report_collectionfinish(self) -> Optional[str]:
- if self.active and self.config.getoption("verbose") >= 0:
+ if self.active and self.config.getoption("verbose") >= 0:
return "run-last-failure: %s" % self._report_status
return None
-
+
def pytest_runtest_logreport(self, report: TestReport) -> None:
- if (report.when == "call" and report.passed) or report.skipped:
- self.lastfailed.pop(report.nodeid, None)
- elif report.failed:
- self.lastfailed[report.nodeid] = True
-
+ if (report.when == "call" and report.passed) or report.skipped:
+ self.lastfailed.pop(report.nodeid, None)
+ elif report.failed:
+ self.lastfailed[report.nodeid] = True
+
def pytest_collectreport(self, report: CollectReport) -> None:
- passed = report.outcome in ("passed", "skipped")
- if passed:
- if report.nodeid in self.lastfailed:
- self.lastfailed.pop(report.nodeid)
- self.lastfailed.update((item.nodeid, True) for item in report.result)
- else:
- self.lastfailed[report.nodeid] = True
-
+ passed = report.outcome in ("passed", "skipped")
+ if passed:
+ if report.nodeid in self.lastfailed:
+ self.lastfailed.pop(report.nodeid)
+ self.lastfailed.update((item.nodeid, True) for item in report.result)
+ else:
+ self.lastfailed[report.nodeid] = True
+
@hookimpl(hookwrapper=True, tryfirst=True)
def pytest_collection_modifyitems(
self, config: Config, items: List[nodes.Item]
@@ -345,11 +345,11 @@ class LFPlugin:
len(self.lastfailed),
)
else:
- if self.config.getoption("lf"):
- items[:] = previously_failed
- config.hook.pytest_deselected(items=previously_passed)
+ if self.config.getoption("lf"):
+ items[:] = previously_failed
+ config.hook.pytest_deselected(items=previously_passed)
else: # --failedfirst
- items[:] = previously_failed + previously_passed
+ items[:] = previously_failed + previously_passed
noun = "failure" if self._previously_failed_count == 1 else "failures"
suffix = " first" if self.config.getoption("failedfirst") else ""
@@ -367,209 +367,209 @@ class LFPlugin:
if self.config.getoption("last_failed_no_failures") == "none":
self._report_status += "deselecting all items."
config.hook.pytest_deselected(items=items[:])
- items[:] = []
+ items[:] = []
else:
self._report_status += "not deselecting items."
-
+
def pytest_sessionfinish(self, session: Session) -> None:
- config = self.config
+ config = self.config
if config.getoption("cacheshow") or hasattr(config, "workerinput"):
- return
-
+ return
+
assert config.cache is not None
- saved_lastfailed = config.cache.get("cache/lastfailed", {})
- if saved_lastfailed != self.lastfailed:
- config.cache.set("cache/lastfailed", self.lastfailed)
-
-
+ saved_lastfailed = config.cache.get("cache/lastfailed", {})
+ if saved_lastfailed != self.lastfailed:
+ config.cache.set("cache/lastfailed", self.lastfailed)
+
+
class NFPlugin:
"""Plugin which implements the --nf (run new-first) option."""
-
+
def __init__(self, config: Config) -> None:
- self.config = config
- self.active = config.option.newfirst
+ self.config = config
+ self.active = config.option.newfirst
assert config.cache is not None
self.cached_nodeids = set(config.cache.get("cache/nodeids", []))
-
+
@hookimpl(hookwrapper=True, tryfirst=True)
def pytest_collection_modifyitems(
self, items: List[nodes.Item]
) -> Generator[None, None, None]:
yield
- if self.active:
+ if self.active:
new_items: Dict[str, nodes.Item] = {}
other_items: Dict[str, nodes.Item] = {}
- for item in items:
- if item.nodeid not in self.cached_nodeids:
- new_items[item.nodeid] = item
- else:
- other_items[item.nodeid] = item
-
- items[:] = self._get_increasing_order(
+ for item in items:
+ if item.nodeid not in self.cached_nodeids:
+ new_items[item.nodeid] = item
+ else:
+ other_items[item.nodeid] = item
+
+ items[:] = self._get_increasing_order(
new_items.values()
) + self._get_increasing_order(other_items.values())
self.cached_nodeids.update(new_items)
else:
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]
-
+
def pytest_sessionfinish(self) -> None:
- config = self.config
+ config = self.config
if config.getoption("cacheshow") or hasattr(config, "workerinput"):
- return
-
+ return
+
if config.getoption("collectonly"):
return
-
+
assert config.cache is not None
config.cache.set("cache/nodeids", sorted(self.cached_nodeids))
-
+
def pytest_addoption(parser: Parser) -> None:
- group = parser.getgroup("general")
- group.addoption(
- "--lf",
- "--last-failed",
- action="store_true",
- dest="lf",
- help="rerun only the tests that failed "
- "at the last run (or all if none failed)",
- )
- group.addoption(
- "--ff",
- "--failed-first",
- action="store_true",
- dest="failedfirst",
+ group = parser.getgroup("general")
+ group.addoption(
+ "--lf",
+ "--last-failed",
+ action="store_true",
+ dest="lf",
+ help="rerun only the tests that failed "
+ "at the last run (or all if none failed)",
+ )
+ group.addoption(
+ "--ff",
+ "--failed-first",
+ action="store_true",
+ dest="failedfirst",
help="run all tests, but run the last failures first.\n"
- "This may re-order tests and thus lead to "
+ "This may re-order tests and thus lead to "
"repeated fixture setup/teardown.",
- )
- group.addoption(
- "--nf",
- "--new-first",
- action="store_true",
- dest="newfirst",
- help="run tests from new files first, then the rest of the tests "
- "sorted by file mtime",
- )
- group.addoption(
- "--cache-show",
+ )
+ group.addoption(
+ "--nf",
+ "--new-first",
+ action="store_true",
+ dest="newfirst",
+ help="run tests from new files first, then the rest of the tests "
+ "sorted by file mtime",
+ )
+ group.addoption(
+ "--cache-show",
action="append",
nargs="?",
- dest="cacheshow",
+ dest="cacheshow",
help=(
"show cache contents, don't perform collection or tests. "
"Optional argument: glob (default: '*')."
),
- )
- group.addoption(
- "--cache-clear",
- action="store_true",
- dest="cacheclear",
- help="remove all cache contents at start of test run.",
- )
- cache_dir_default = ".pytest_cache"
- if "TOX_ENV_DIR" in os.environ:
- cache_dir_default = os.path.join(os.environ["TOX_ENV_DIR"], cache_dir_default)
- parser.addini("cache_dir", default=cache_dir_default, help="cache directory path.")
- group.addoption(
- "--lfnf",
- "--last-failed-no-failures",
- action="store",
- dest="last_failed_no_failures",
- choices=("all", "none"),
- default="all",
+ )
+ group.addoption(
+ "--cache-clear",
+ action="store_true",
+ dest="cacheclear",
+ help="remove all cache contents at start of test run.",
+ )
+ cache_dir_default = ".pytest_cache"
+ if "TOX_ENV_DIR" in os.environ:
+ cache_dir_default = os.path.join(os.environ["TOX_ENV_DIR"], cache_dir_default)
+ parser.addini("cache_dir", default=cache_dir_default, help="cache directory path.")
+ group.addoption(
+ "--lfnf",
+ "--last-failed-no-failures",
+ action="store",
+ dest="last_failed_no_failures",
+ choices=("all", "none"),
+ default="all",
help="which tests to run with no previously (known) failures.",
- )
-
-
+ )
+
+
def pytest_cmdline_main(config: Config) -> Optional[Union[int, ExitCode]]:
- if config.option.cacheshow:
- from _pytest.main import wrap_session
-
- return wrap_session(config, cacheshow)
+ if config.option.cacheshow:
+ from _pytest.main import wrap_session
+
+ return wrap_session(config, cacheshow)
return None
-
-
+
+
@hookimpl(tryfirst=True)
def pytest_configure(config: Config) -> None:
config.cache = Cache.for_config(config, _ispytest=True)
- config.pluginmanager.register(LFPlugin(config), "lfplugin")
- config.pluginmanager.register(NFPlugin(config), "nfplugin")
-
-
+ config.pluginmanager.register(LFPlugin(config), "lfplugin")
+ config.pluginmanager.register(NFPlugin(config), "nfplugin")
+
+
@fixture
def cache(request: FixtureRequest) -> Cache:
"""Return a cache object that can persist state between testing sessions.
-
- cache.get(key, default)
- cache.set(key, value)
-
+
+ cache.get(key, default)
+ cache.set(key, value)
+
Keys must be ``/`` separated strings, where the first part is usually the
- name of your plugin or application to avoid clashes with other cache users.
-
- Values can be any object handled by the json stdlib module.
- """
+ name of your plugin or application to avoid clashes with other cache users.
+
+ Values can be any object handled by the json stdlib module.
+ """
assert request.config.cache is not None
- return request.config.cache
-
-
+ return request.config.cache
+
+
def pytest_report_header(config: Config) -> Optional[str]:
- """Display cachedir with --cache-show and if non-default."""
+ """Display cachedir with --cache-show and if non-default."""
if config.option.verbose > 0 or config.getini("cache_dir") != ".pytest_cache":
assert config.cache is not None
- cachedir = config.cache._cachedir
- # TODO: evaluate generating upward relative paths
- # starting with .., ../.. if sensible
-
- try:
+ cachedir = config.cache._cachedir
+ # TODO: evaluate generating upward relative paths
+ # starting with .., ../.. if sensible
+
+ try:
displaypath = cachedir.relative_to(config.rootpath)
- except ValueError:
- displaypath = cachedir
+ except ValueError:
+ displaypath = cachedir
return f"cachedir: {displaypath}"
return None
-
-
+
+
def cacheshow(config: Config, session: Session) -> int:
- from pprint import pformat
-
+ from pprint import pformat
+
assert config.cache is not None
tw = TerminalWriter()
- tw.line("cachedir: " + str(config.cache._cachedir))
- if not config.cache._cachedir.is_dir():
- tw.line("cache is empty")
- return 0
+ tw.line("cachedir: " + str(config.cache._cachedir))
+ if not config.cache._cachedir.is_dir():
+ tw.line("cache is empty")
+ return 0
glob = config.option.cacheshow[0]
if glob is None:
glob = "*"
- dummy = object()
- basedir = config.cache._cachedir
+ dummy = object()
+ basedir = config.cache._cachedir
vdir = basedir / Cache._CACHE_PREFIX_VALUES
tw.sep("-", "cache values for %r" % glob)
for valpath in sorted(x for x in vdir.rglob(glob) if x.is_file()):
key = str(valpath.relative_to(vdir))
- val = config.cache.get(key, dummy)
- if val is dummy:
- tw.line("%s contains unreadable content, will be ignored" % key)
- else:
- tw.line("%s contains:" % key)
- for line in pformat(val).splitlines():
- tw.line(" " + line)
-
+ val = config.cache.get(key, dummy)
+ if val is dummy:
+ tw.line("%s contains unreadable content, will be ignored" % key)
+ else:
+ tw.line("%s contains:" % key)
+ for line in pformat(val).splitlines():
+ tw.line(" " + line)
+
ddir = basedir / Cache._CACHE_PREFIX_DIRS
- if ddir.is_dir():
+ if ddir.is_dir():
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_file():
+ for p in contents:
+ # if p.check(dir=1):
+ # print("%s/" % p.relto(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}")
- return 0
+ return 0