port cache plugin internals to pathlib

warning logging got broken by detanglement from config
This commit is contained in:
Ronny Pfannschmidt 2018-06-17 16:23:44 +02:00
parent de98939ebf
commit c7eb53317b
4 changed files with 57 additions and 55 deletions

View File

@ -73,15 +73,17 @@ def main():
environment_marker_support_level = get_environment_marker_support_level() environment_marker_support_level = get_environment_marker_support_level()
if environment_marker_support_level >= 2: if environment_marker_support_level >= 2:
install_requires.append('funcsigs;python_version<"3.0"') install_requires.append('funcsigs;python_version<"3.0"')
install_requires.append('pathlib2;python_version<"3.0"')
install_requires.append('colorama;sys_platform=="win32"') install_requires.append('colorama;sys_platform=="win32"')
elif environment_marker_support_level == 1: elif environment_marker_support_level == 1:
extras_require[':python_version<"3.0"'] = ["funcsigs"] extras_require[':python_version<"3.0"'] = ["funcsigs", "pathlib2"]
extras_require[':sys_platform=="win32"'] = ["colorama"] extras_require[':sys_platform=="win32"'] = ["colorama"]
else: else:
if sys.platform == "win32": if sys.platform == "win32":
install_requires.append("colorama") install_requires.append("colorama")
if sys.version_info < (3, 0): if sys.version_info < (3, 0):
install_requires.append("funcsigs") install_requires.append("funcsigs")
install_requires.append("pathlib2")
setup( setup(
name="pytest", name="pytest",

View File

@ -5,40 +5,41 @@ the name cache was not chosen to ensure pluggy automatically
ignores the external pytest-cache ignores the external pytest-cache
""" """
from __future__ import absolute_import, division, print_function from __future__ import absolute_import, division, print_function
from collections import OrderedDict from collections import OrderedDict
import py import py
import six import six
import attr
import pytest import pytest
import json import json
import os
from os.path import sep as _sep, altsep as _altsep from os.path import sep as _sep, altsep as _altsep
from textwrap import dedent from textwrap import dedent
from . import paths
import logging
log = logging.getLogger(__name__)
@attr.s
class Cache(object): class Cache(object):
def __init__(self, config): @classmethod
self.config = config def for_config(cls, config):
self._cachedir = Cache.cache_dir_from_config(config) cachedir = cls.cache_dir_from_config(config)
self.trace = config.trace.root.get("cache") if config.getoption("cacheclear") and cachedir.exists():
if config.getoption("cacheclear"): shutil.rmtree(str(cachedir))
self.trace("clearing cachedir") cachedir.mkdir()
if self._cachedir.check(): return cls(cachedir)
self._cachedir.remove()
self._cachedir.mkdir() _cachedir = attr.ib(repr=False)
@staticmethod @staticmethod
def cache_dir_from_config(config): def cache_dir_from_config(config):
cache_dir = config.getini("cache_dir") return paths.resolve_from_str(config.getini("cache_dir"), config.rootdir)
cache_dir = os.path.expanduser(cache_dir)
cache_dir = os.path.expandvars(cache_dir)
if os.path.isabs(cache_dir):
return py.path.local(cache_dir)
else:
return config.rootdir.join(cache_dir)
def makedir(self, name): def makedir(self, name):
""" return a directory path object with the given name. If the """ return a directory path object with the given name. If the
@ -52,10 +53,12 @@ class Cache(object):
""" """
if _sep in name or _altsep is not None and _altsep in name: if _sep in name or _altsep is not None and _altsep in name:
raise ValueError("name is not allowed to contain path separators") raise ValueError("name is not allowed to contain path separators")
return self._cachedir.ensure_dir("d", name) res = self._cachedir.joinpath("d", name)
res.mkdir(exist_ok=True, parents=True)
return py.path.local(res)
def _getvaluepath(self, key): def _getvaluepath(self, key):
return self._cachedir.join("v", *key.split("/")) return self._cachedir.joinpath("v", *key.split("/"))
def get(self, key, default): def get(self, key, default):
""" return cached value for the given key. If no value """ return cached value for the given key. If no value
@ -69,13 +72,11 @@ class Cache(object):
""" """
path = self._getvaluepath(key) path = self._getvaluepath(key)
if path.check(): try:
try: with path.open("r") as f:
with path.open("r") as f: return json.load(f)
return json.load(f) except (ValueError, IOError):
except ValueError: return default
self.trace("cache-invalid at %s" % (path,))
return default
def set(self, key, value): def set(self, key, value):
""" save value for the given key. """ save value for the given key.
@ -88,21 +89,16 @@ class Cache(object):
""" """
path = self._getvaluepath(key) path = self._getvaluepath(key)
try: try:
path.dirpath().ensure_dir() path.parent.mkdir(exist_ok=True, parents=True)
except (py.error.EEXIST, py.error.EACCES): except IOError:
self.config.warn( log.warning("could not create cache path %s", path)
code="I9", message="could not create cache path %s" % (path,)
)
return return
try: try:
f = path.open("w") f = path.open("w")
except py.error.ENOTDIR: except py.error.ENOTDIR:
self.config.warn( log.warning("cache could not write path %s", path)
code="I9", message="cache could not write path %s" % (path,)
)
else: else:
with f: with f:
self.trace("cache-write %s: %r" % (key, value))
json.dump(value, f, indent=2, sort_keys=True) json.dump(value, f, indent=2, sort_keys=True)
self._ensure_readme() self._ensure_readme()
@ -297,7 +293,7 @@ def pytest_cmdline_main(config):
@pytest.hookimpl(tryfirst=True) @pytest.hookimpl(tryfirst=True)
def pytest_configure(config): def pytest_configure(config):
config.cache = Cache(config) config.cache = Cache.for_config(config)
config.pluginmanager.register(LFPlugin(config), "lfplugin") config.pluginmanager.register(LFPlugin(config), "lfplugin")
config.pluginmanager.register(NFPlugin(config), "nfplugin") config.pluginmanager.register(NFPlugin(config), "nfplugin")
@ -320,41 +316,40 @@ def cache(request):
def pytest_report_header(config): def pytest_report_header(config):
if config.option.verbose: if config.option.verbose:
relpath = py.path.local().bestrelpath(config.cache._cachedir) relpath = config.cache._cachedir.relative_to(config.rootdir)
return "cachedir: %s" % relpath return "cachedir: {}".format(relpath)
def cacheshow(config, session): def cacheshow(config, session):
from pprint import pprint from pprint import pformat
tw = py.io.TerminalWriter() tw = py.io.TerminalWriter()
tw.line("cachedir: " + str(config.cache._cachedir)) tw.line("cachedir: " + str(config.cache._cachedir))
if not config.cache._cachedir.check(): if not config.cache._cachedir.is_dir():
tw.line("cache is empty") tw.line("cache is empty")
return 0 return 0
dummy = object() dummy = object()
basedir = config.cache._cachedir basedir = config.cache._cachedir
vdir = basedir.join("v") vdir = basedir.joinpath("v")
tw.sep("-", "cache values") tw.sep("-", "cache values")
for valpath in sorted(vdir.visit(lambda x: x.isfile())): for valpath in sorted(x for x in vdir.rglob("*") if x.is_file()):
key = valpath.relto(vdir).replace(valpath.sep, "/") key = "/".join(valpath.relative_to(vdir).parts)
val = config.cache.get(key, dummy) val = config.cache.get(key, dummy)
if val is dummy: if val is dummy:
tw.line("%s contains unreadable content, " "will be ignored" % key) tw.line("%s contains unreadable content, " "will be ignored" % key)
else: else:
tw.line("%s contains:" % key) tw.line("%s contains:" % key)
stream = py.io.TextIO() for line in pformat(val).splitlines():
pprint(val, stream=stream)
for line in stream.getvalue().splitlines():
tw.line(" " + line) tw.line(" " + line)
ddir = basedir.join("d") ddir = basedir.joinpath("d")
if ddir.isdir() and ddir.listdir(): if ddir.is_dir():
contents = sorted(ddir.rglob("*"))
tw.sep("-", "cache directories") tw.sep("-", "cache directories")
for p in sorted(basedir.join("d").visit()): for p in contents:
# if p.check(dir=1): # if p.check(dir=1):
# print("%s/" % p.relto(basedir)) # print("%s/" % p.relto(basedir))
if p.isfile(): if p.is_file():
key = p.relto(basedir) key = p.relative_to(basedir)
tw.line("%s is a file of length %d" % (key, p.size())) tw.line("{} is a file of length {:d}".format(key, p.stat().st_size))
return 0 return 0

View File

@ -29,8 +29,11 @@ _PY2 = not _PY3
if _PY3: if _PY3:
from inspect import signature, Parameter as Parameter from inspect import signature, Parameter as Parameter
from pathlib import Path
else: else:
from funcsigs import signature, Parameter as Parameter from funcsigs import signature, Parameter as Parameter
from pathlib2 import Path
Path
NoneType = type(None) NoneType = type(None)

View File

@ -1,5 +1,7 @@
from __future__ import absolute_import, division, print_function from __future__ import absolute_import, division, print_function
import sys import sys
import py import py
import _pytest import _pytest
import pytest import pytest
@ -26,7 +28,7 @@ class TestNewAPI(object):
cache = config.cache cache = config.cache
pytest.raises(TypeError, lambda: cache.set("key/name", cache)) pytest.raises(TypeError, lambda: cache.set("key/name", cache))
config.cache.set("key/name", 0) config.cache.set("key/name", 0)
config.cache._getvaluepath("key/name").write("123invalid") config.cache._getvaluepath("key/name").write_bytes(b"123invalid")
val = config.cache.get("key/name", -2) val = config.cache.get("key/name", -2)
assert val == -2 assert val == -2