Merge pull request #8446 from bluetech/unnecessary-py-path-2

More py.path removal work
This commit is contained in:
Ran Benita 2021-03-18 10:20:59 +02:00 committed by GitHub
commit fe215bda6b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 111 additions and 98 deletions

View File

@ -0,0 +1,2 @@
Added :meth:`cache.mkdir() <pytest.Cache.mkdir>`, which is similar to the existing :meth:`cache.makedir() <pytest.Cache.makedir>`,
but returns a :class:`pathlib.Path` instead of a legacy ``py.path.local``.

View File

@ -2,9 +2,9 @@
import pytest import pytest
def pytest_collect_file(parent, path): def pytest_collect_file(parent, fspath):
if path.ext == ".yaml" and path.basename.startswith("test"): if fspath.suffix == ".yaml" and fspath.name.startswith("test"):
return YamlFile.from_parent(parent, fspath=path) return YamlFile.from_parent(parent, path=fspath)
class YamlFile(pytest.File): class YamlFile(pytest.File):
@ -12,7 +12,7 @@ class YamlFile(pytest.File):
# We need a yaml parser, e.g. PyYAML. # We need a yaml parser, e.g. PyYAML.
import yaml import yaml
raw = yaml.safe_load(self.fspath.open()) raw = yaml.safe_load(self.path.open())
for name, spec in sorted(raw.items()): for name, spec in sorted(raw.items()):
yield YamlItem.from_parent(self, name=name, spec=spec) yield YamlItem.from_parent(self, name=name, spec=spec)

View File

@ -936,7 +936,7 @@ pytest treats some global variables in a special manner when defined in a test m
**Tutorial**: :ref:`customizing-test-collection` **Tutorial**: :ref:`customizing-test-collection`
Can be declared in *conftest.py files* to exclude test directories or modules. 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 .. code-block:: python

View File

@ -60,10 +60,10 @@ class Cache:
_cachedir = attr.ib(type=Path, repr=False) _cachedir = attr.ib(type=Path, repr=False)
_config = attr.ib(type=Config, 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" _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" _CACHE_PREFIX_VALUES = "v"
def __init__( def __init__(
@ -121,13 +121,15 @@ class Cache:
stacklevel=3, stacklevel=3,
) )
def makedir(self, name: str) -> LEGACY_PATH: def mkdir(self, name: str) -> Path:
"""Return a directory path object with the given name. """Return a directory path object with the given name.
If the directory does not yet exist, it will be created. You can use 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 it to manage files to e.g. store/retrieve database dumps across test
sessions. sessions.
.. versionadded:: 6.3
:param name: :param name:
Must be a string not containing a ``/`` separator. Must be a string not containing a ``/`` separator.
Make sure the name contains your plugin or application Make sure the name contains your plugin or application
@ -138,7 +140,14 @@ class Cache:
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 = self._cachedir.joinpath(self._CACHE_PREFIX_DIRS, path)
res.mkdir(exist_ok=True, parents=True) 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: def _getvaluepath(self, key: str) -> Path:
return self._cachedir.joinpath(self._CACHE_PREFIX_VALUES, Path(key)) 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)) contents = sorted(ddir.rglob(glob))
tw.sep("-", "cache directories for %r" % glob) tw.sep("-", "cache directories for %r" % glob)
for p in contents: for p in contents:
# if p.check(dir=1): # if p.is_dir():
# print("%s/" % p.relto(basedir)) # print("%s/" % p.relative_to(basedir))
if p.is_file(): if p.is_file():
key = str(p.relative_to(basedir)) key = str(p.relative_to(basedir))
tw.line(f"{key} is a file of length {p.stat().st_size:d}") tw.line(f"{key} is a file of length {p.stat().st_size:d}")

View File

@ -1445,9 +1445,7 @@ class Config:
modpath = Path(mod.__file__).parent modpath = Path(mod.__file__).parent
values: List[Path] = [] values: List[Path] = []
for relroot in relroots: for relroot in relroots:
if isinstance(relroot, Path): if isinstance(relroot, os.PathLike):
pass
elif isinstance(relroot, LEGACY_PATH):
relroot = Path(relroot) relroot = Path(relroot)
else: else:
relroot = relroot.replace("/", os.sep) relroot = relroot.replace("/", os.sep)

View File

@ -28,7 +28,6 @@ from _pytest._code.code import ExceptionInfo
from _pytest._code.code import ReprFileLocation from _pytest._code.code import ReprFileLocation
from _pytest._code.code import TerminalRepr from _pytest._code.code import TerminalRepr
from _pytest._io import TerminalWriter from _pytest._io import TerminalWriter
from _pytest.compat import LEGACY_PATH
from _pytest.compat import legacy_path from _pytest.compat import legacy_path
from _pytest.compat import safe_getattr from _pytest.compat import safe_getattr
from _pytest.config import Config from _pytest.config import Config
@ -122,7 +121,6 @@ def pytest_unconfigure() -> None:
def pytest_collect_file( def pytest_collect_file(
fspath: Path, fspath: Path,
path: LEGACY_PATH,
parent: Collector, parent: Collector,
) -> Optional[Union["DoctestModule", "DoctestTextfile"]]: ) -> Optional[Union["DoctestModule", "DoctestTextfile"]]:
config = parent.config config = parent.config

View File

@ -465,7 +465,7 @@ class Session(nodes.FSCollector):
def __init__(self, config: Config) -> None: def __init__(self, config: Config) -> None:
super().__init__( super().__init__(
path=config.rootpath, path=config.rootpath,
fspath=config.rootdir, fspath=None,
parent=None, parent=None,
config=config, config=config,
session=self, session=self,

View File

@ -912,7 +912,7 @@ class Pytester:
example_dir = self._request.config.getini("pytester_example_dir") example_dir = self._request.config.getini("pytester_example_dir")
if example_dir is None: if example_dir is None:
raise ValueError("pytester_example_dir is unset, can't copy examples") 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"): for extra_element in self._request.node.iter_markers("pytester_example_path"):
assert extra_element.args assert extra_element.args

View File

@ -210,11 +210,11 @@ def path_matches_patterns(path: Path, patterns: Iterable[str]) -> bool:
return any(fnmatch_ex(pattern, path) for pattern in patterns) 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": if fspath.name == "__init__.py":
pkg: Package = Package.from_parent(parent, fspath=path) pkg: Package = Package.from_parent(parent, path=fspath)
return pkg return pkg
mod: Module = Module.from_parent(parent, fspath=path) mod: Module = Module.from_parent(parent, path=fspath)
return mod return mod
@ -691,7 +691,7 @@ class Package(Module):
assert ( assert (
fspath.is_file() fspath.is_file()
), "{!r} is not a file (isdir={!r}, exists={!r}, islink={!r})".format( ), "{!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) ihook = self.session.gethookproxy(fspath)
if not self.session.isinitpath(fspath): if not self.session.isinitpath(fspath):

View File

@ -304,9 +304,9 @@ class TestGeneralUsage:
class MyCollector(pytest.File): class MyCollector(pytest.File):
def collect(self): def collect(self):
return [MyItem.from_parent(name="xyz", parent=self)] return [MyItem.from_parent(name="xyz", parent=self)]
def pytest_collect_file(path, parent): def pytest_collect_file(fspath, parent):
if path.basename.startswith("conftest"): if fspath.name.startswith("conftest"):
return MyCollector.from_parent(fspath=path, parent=parent) return MyCollector.from_parent(path=fspath, parent=parent)
""" """
) )
result = pytester.runpytest(c.name + "::" + "xyz") result = pytester.runpytest(c.name + "::" + "xyz")

View File

@ -114,13 +114,13 @@ def dummy_yaml_custom_test(pytester: Pytester):
""" """
import pytest import pytest
def pytest_collect_file(parent, path): def pytest_collect_file(parent, fspath):
if path.ext == ".yaml" and path.basename.startswith("test"): if fspath.suffix == ".yaml" and fspath.name.startswith("test"):
return YamlFile.from_parent(fspath=path, parent=parent) return YamlFile.from_parent(path=fspath, parent=parent)
class YamlFile(pytest.File): class YamlFile(pytest.File):
def collect(self): 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): class YamlItem(pytest.Item):
def runtest(self): def runtest(self):

View File

@ -1,2 +1,2 @@
def pytest_ignore_collect(path): def pytest_ignore_collect(fspath):
return False return False

View File

@ -11,5 +11,5 @@ class CustomFile(pytest.File):
yield CustomItem.from_parent(name="foo", parent=self) yield CustomItem.from_parent(name="foo", parent=self)
def pytest_collect_file(path, parent): def pytest_collect_file(fspath, parent):
return CustomFile.from_parent(fspath=path, parent=parent) return CustomFile.from_parent(path=fspath, parent=parent)

View File

@ -6,8 +6,8 @@ class MyFile(pytest.File):
return [MyItem.from_parent(name="hello", parent=self)] return [MyItem.from_parent(name="hello", parent=self)]
def pytest_collect_file(path, parent): def pytest_collect_file(fspath, parent):
return MyFile.from_parent(fspath=path, parent=parent) return MyFile.from_parent(path=fspath, parent=parent)
class MyItem(pytest.Item): class MyItem(pytest.Item):

View File

@ -778,9 +778,9 @@ class TestConftestCustomization:
import pytest import pytest
class MyModule(pytest.Module): class MyModule(pytest.Module):
pass pass
def pytest_pycollect_makemodule(path, parent): def pytest_pycollect_makemodule(fspath, parent):
if path.basename == "test_xyz.py": if fspath.name == "test_xyz.py":
return MyModule.from_parent(fspath=path, parent=parent) return MyModule.from_parent(path=fspath, parent=parent)
""" """
) )
pytester.makepyfile("def test_some(): pass") pytester.makepyfile("def test_some(): pass")
@ -882,9 +882,9 @@ class TestConftestCustomization:
return Loader() return Loader()
sys.meta_path.append(Finder()) sys.meta_path.append(Finder())
def pytest_collect_file(path, parent): def pytest_collect_file(fspath, parent):
if path.ext == ".narf": if fspath.suffix == ".narf":
return Module.from_parent(fspath=path, parent=parent)""" return Module.from_parent(path=fspath, parent=parent)"""
) )
pytester.makefile( pytester.makefile(
".narf", ".narf",

View File

@ -3208,10 +3208,10 @@ class TestRequestScopeAccess:
pytestmark = pytest.mark.parametrize( pytestmark = pytest.mark.parametrize(
("scope", "ok", "error"), ("scope", "ok", "error"),
[ [
["session", "", "fspath class function module"], ["session", "", "path class function module"],
["module", "module fspath", "cls function"], ["module", "module path", "cls function"],
["class", "module fspath cls", "function"], ["class", "module path cls", "function"],
["function", "module fspath cls function", ""], ["function", "module path cls function", ""],
], ],
) )

View File

@ -14,15 +14,15 @@ pytest_plugins = ("pytester",)
class TestNewAPI: class TestNewAPI:
def test_config_cache_makedir(self, pytester: Pytester) -> None: def test_config_cache_mkdir(self, pytester: Pytester) -> None:
pytester.makeini("[pytest]") pytester.makeini("[pytest]")
config = pytester.parseconfigure() config = pytester.parseconfigure()
assert config.cache is not None assert config.cache is not None
with pytest.raises(ValueError): with pytest.raises(ValueError):
config.cache.makedir("key/name") config.cache.mkdir("key/name")
p = config.cache.makedir("name") p = config.cache.mkdir("name")
assert p.check() assert p.is_dir()
def test_config_cache_dataerror(self, pytester: Pytester) -> None: def test_config_cache_dataerror(self, pytester: Pytester) -> None:
pytester.makeini("[pytest]") pytester.makeini("[pytest]")
@ -217,9 +217,9 @@ def test_cache_show(pytester: Pytester) -> None:
config.cache.set("my/name", [1,2,3]) config.cache.set("my/name", [1,2,3])
config.cache.set("my/hello", "world") config.cache.set("my/hello", "world")
config.cache.set("other/some", {1:2}) config.cache.set("other/some", {1:2})
dp = config.cache.makedir("mydb") dp = config.cache.mkdir("mydb")
dp.ensure("hello") dp.joinpath("hello").touch()
dp.ensure("world") dp.joinpath("world").touch()
""" """
) )
result = pytester.runpytest() result = pytester.runpytest()

View File

@ -95,9 +95,9 @@ class TestCollector:
import pytest import pytest
class CustomFile(pytest.File): class CustomFile(pytest.File):
pass pass
def pytest_collect_file(path, parent): def pytest_collect_file(fspath, parent):
if path.ext == ".xxx": if fspath.suffix == ".xxx":
return CustomFile.from_parent(fspath=path, parent=parent) return CustomFile.from_parent(path=fspath, parent=parent)
""" """
) )
node = pytester.getpathnode(hello) node = pytester.getpathnode(hello)
@ -271,15 +271,15 @@ class TestCollectPluginHookRelay:
wascalled = [] wascalled = []
class Plugin: class Plugin:
def pytest_collect_file(self, path): def pytest_collect_file(self, fspath: Path) -> None:
if not path.basename.startswith("."): if not fspath.name.startswith("."):
# Ignore hidden files, e.g. .testmondata. # Ignore hidden files, e.g. .testmondata.
wascalled.append(path) wascalled.append(fspath)
pytester.makefile(".abc", "xyz") pytester.makefile(".abc", "xyz")
pytest.main(pytester.path, plugins=[Plugin()]) pytest.main(pytester.path, plugins=[Plugin()])
assert len(wascalled) == 1 assert len(wascalled) == 1
assert wascalled[0].ext == ".abc" assert wascalled[0].suffix == ".abc"
class TestPrunetraceback: class TestPrunetraceback:
@ -292,15 +292,15 @@ class TestPrunetraceback:
pytester.makeconftest( pytester.makeconftest(
""" """
import pytest import pytest
def pytest_collect_file(path, parent): def pytest_collect_file(fspath, parent):
return MyFile.from_parent(fspath=path, parent=parent) return MyFile.from_parent(path=fspath, parent=parent)
class MyError(Exception): class MyError(Exception):
pass pass
class MyFile(pytest.File): class MyFile(pytest.File):
def collect(self): def collect(self):
raise MyError() raise MyError()
def repr_failure(self, excinfo): def repr_failure(self, excinfo):
if excinfo.errisinstance(MyError): if isinstance(excinfo.value, MyError):
return "hello world" return "hello world"
return pytest.File.repr_failure(self, excinfo) return pytest.File.repr_failure(self, excinfo)
""" """
@ -335,9 +335,8 @@ class TestCustomConftests:
def test_ignore_collect_path(self, pytester: Pytester) -> None: def test_ignore_collect_path(self, pytester: Pytester) -> None:
pytester.makeconftest( pytester.makeconftest(
""" """
def pytest_ignore_collect(path, config): def pytest_ignore_collect(fspath, config):
return path.basename.startswith("x") or \ return fspath.name.startswith("x") or fspath.name == "test_one.py"
path.basename == "test_one.py"
""" """
) )
sub = pytester.mkdir("xy123") sub = pytester.mkdir("xy123")
@ -352,7 +351,7 @@ class TestCustomConftests:
def test_ignore_collect_not_called_on_argument(self, pytester: Pytester) -> None: def test_ignore_collect_not_called_on_argument(self, pytester: Pytester) -> None:
pytester.makeconftest( pytester.makeconftest(
""" """
def pytest_ignore_collect(path, config): def pytest_ignore_collect(fspath, config):
return True return True
""" """
) )
@ -367,12 +366,19 @@ class TestCustomConftests:
def test_collectignore_exclude_on_option(self, pytester: Pytester) -> None: def test_collectignore_exclude_on_option(self, pytester: Pytester) -> None:
pytester.makeconftest( pytester.makeconftest(
""" """
# potentially avoid dependency on pylib
from _pytest.compat import legacy_path
from pathlib import 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): def pytest_addoption(parser):
parser.addoption("--XX", action="store_true", default=False) parser.addoption("--XX", action="store_true", default=False)
def pytest_configure(config): def pytest_configure(config):
if config.getvalue("XX"): if config.getvalue("XX"):
collect_ignore[:] = [] collect_ignore[:] = []
@ -413,9 +419,9 @@ class TestCustomConftests:
import pytest import pytest
class MyModule(pytest.Module): class MyModule(pytest.Module):
pass pass
def pytest_collect_file(path, parent): def pytest_collect_file(fspath, parent):
if path.ext == ".py": if fspath.suffix == ".py":
return MyModule.from_parent(fspath=path, parent=parent) return MyModule.from_parent(path=fspath, parent=parent)
""" """
) )
pytester.mkdir("sub") pytester.mkdir("sub")
@ -431,9 +437,9 @@ class TestCustomConftests:
import pytest import pytest
class MyModule1(pytest.Module): class MyModule1(pytest.Module):
pass pass
def pytest_collect_file(path, parent): def pytest_collect_file(fspath, parent):
if path.ext == ".py": if fspath.suffix == ".py":
return MyModule1.from_parent(fspath=path, parent=parent) return MyModule1.from_parent(path=fspath, parent=parent)
""" """
) )
conf1.replace(sub1.joinpath(conf1.name)) conf1.replace(sub1.joinpath(conf1.name))
@ -442,9 +448,9 @@ class TestCustomConftests:
import pytest import pytest
class MyModule2(pytest.Module): class MyModule2(pytest.Module):
pass pass
def pytest_collect_file(path, parent): def pytest_collect_file(fspath, parent):
if path.ext == ".py": if fspath.suffix == ".py":
return MyModule2.from_parent(fspath=path, parent=parent) return MyModule2.from_parent(path=fspath, parent=parent)
""" """
) )
conf2.replace(sub2.joinpath(conf2.name)) conf2.replace(sub2.joinpath(conf2.name))
@ -533,9 +539,9 @@ class TestSession:
class SpecialFile(pytest.File): class SpecialFile(pytest.File):
def collect(self): def collect(self):
return [SpecialItem.from_parent(name="check", parent=self)] return [SpecialItem.from_parent(name="check", parent=self)]
def pytest_collect_file(path, parent): def pytest_collect_file(fspath, parent):
if path.basename == %r: if fspath.name == %r:
return SpecialFile.from_parent(fspath=path, parent=parent) return SpecialFile.from_parent(path=fspath, parent=parent)
""" """
% p.name % p.name
) )
@ -755,13 +761,13 @@ def test_matchnodes_two_collections_same_file(pytester: Pytester) -> None:
config.pluginmanager.register(Plugin2()) config.pluginmanager.register(Plugin2())
class Plugin2(object): class Plugin2(object):
def pytest_collect_file(self, path, parent): def pytest_collect_file(self, fspath, parent):
if path.ext == ".abc": if fspath.suffix == ".abc":
return MyFile2.from_parent(fspath=path, parent=parent) return MyFile2.from_parent(path=fspath, parent=parent)
def pytest_collect_file(path, parent): def pytest_collect_file(fspath, parent):
if path.ext == ".abc": if fspath.suffix == ".abc":
return MyFile1.from_parent(fspath=path, parent=parent) return MyFile1.from_parent(path=fspath, parent=parent)
class MyFile1(pytest.File): class MyFile1(pytest.File):
def collect(self): def collect(self):

View File

@ -629,7 +629,7 @@ def test_hook_proxy(pytester: Pytester) -> None:
"root/demo-0/test_foo1.py": "def test1(): pass", "root/demo-0/test_foo1.py": "def test1(): pass",
"root/demo-a/test_foo2.py": "def test1(): pass", "root/demo-a/test_foo2.py": "def test1(): pass",
"root/demo-a/conftest.py": """\ "root/demo-a/conftest.py": """\
def pytest_ignore_collect(path, config): def pytest_ignore_collect(fspath, config):
return True return True
""", """,
"root/demo-b/test_foo3.py": "def test1(): pass", "root/demo-b/test_foo3.py": "def test1(): pass",

View File

@ -979,9 +979,9 @@ class TestNonPython:
pytester.makeconftest( pytester.makeconftest(
""" """
import pytest import pytest
def pytest_collect_file(path, parent): def pytest_collect_file(fspath, parent):
if path.ext == ".xyz": if fspath.suffix == ".xyz":
return MyItem.from_parent(name=path.basename, parent=parent) return MyItem.from_parent(name=fspath.name, parent=parent)
class MyItem(pytest.Item): class MyItem(pytest.Item):
def runtest(self): def runtest(self):
raise ValueError(42) raise ValueError(42)
@ -1430,9 +1430,9 @@ def test_fancy_items_regression(pytester: Pytester, run_and_parse: RunAndParse)
NoFunItem.from_parent(name='b', parent=self), NoFunItem.from_parent(name='b', parent=self),
] ]
def pytest_collect_file(path, parent): def pytest_collect_file(fspath, parent):
if path.check(ext='.py'): if fspath.suffix == '.py':
return FunCollector.from_parent(fspath=path, parent=parent) return FunCollector.from_parent(path=fspath, parent=parent)
""" """
) )

View File

@ -1303,7 +1303,7 @@ def test_xfail_item(pytester: Pytester) -> None:
def runtest(self): def runtest(self):
pytest.xfail("Expected Failure") pytest.xfail("Expected Failure")
def pytest_collect_file(path, parent): def pytest_collect_file(fspath, parent):
return MyItem.from_parent(name="foo", parent=parent) return MyItem.from_parent(name="foo", parent=parent)
""" """
) )
@ -1377,7 +1377,7 @@ def test_mark_xfail_item(pytester: Pytester) -> None:
def runtest(self): def runtest(self):
assert False assert False
def pytest_collect_file(path, parent): def pytest_collect_file(fspath, parent):
return MyItem.from_parent(name="foo", parent=parent) return MyItem.from_parent(name="foo", parent=parent)
""" """
) )

View File

@ -1036,8 +1036,8 @@ class TestTerminalFunctional:
def test_report_collectionfinish_hook(self, pytester: Pytester, params) -> None: def test_report_collectionfinish_hook(self, pytester: Pytester, params) -> None:
pytester.makeconftest( pytester.makeconftest(
""" """
def pytest_report_collectionfinish(config, startpath, startdir, items): def pytest_report_collectionfinish(config, startpath, items):
return ['hello from hook: {0} items'.format(len(items))] return [f'hello from hook: {len(items)} items']
""" """
) )
pytester.makepyfile( pytester.makepyfile(
@ -1462,7 +1462,7 @@ class TestGenericReporting:
) )
pytester.mkdir("a").joinpath("conftest.py").write_text( 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)] return ["line1", str(startpath)]
""" """
) )