Move pathlist support to legacypath plugin

This commit is contained in:
Ran Benita 2021-10-16 11:21:02 +03:00
parent d9ca55c648
commit ce7cff9f8e
4 changed files with 92 additions and 25 deletions

View File

@ -49,7 +49,6 @@ from _pytest._code import filter_traceback
from _pytest._io import TerminalWriter from _pytest._io import TerminalWriter
from _pytest.compat import final from _pytest.compat import final
from _pytest.compat import importlib_metadata from _pytest.compat import importlib_metadata
from _pytest.compat import legacy_path
from _pytest.outcomes import fail from _pytest.outcomes import fail
from _pytest.outcomes import Skipped from _pytest.outcomes import Skipped
from _pytest.pathlib import absolutepath from _pytest.pathlib import absolutepath
@ -1369,6 +1368,12 @@ class Config:
self._inicache[name] = val = self._getini(name) self._inicache[name] = val = self._getini(name)
return val return val
# Meant for easy monkeypatching by legacypath plugin.
# Can be inlined back (with no cover removed) once legacypath is gone.
def _getini_unknown_type(self, name: str, type: str, value: Union[str, List[str]]):
msg = f"unknown configuration type: {type}"
raise ValueError(msg, value) # pragma: no cover
def _getini(self, name: str): def _getini(self, name: str):
try: try:
description, type, default = self._parser._inidict[name] description, type, default = self._parser._inidict[name]
@ -1401,13 +1406,7 @@ class Config:
# a_line_list = ["tests", "acceptance"] # a_line_list = ["tests", "acceptance"]
# in this case, we already have a list ready to use. # in this case, we already have a list ready to use.
# #
if type == "pathlist": if 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 [legacy_path(str(dp / x)) for x in input_values]
elif type == "paths":
# TODO: This assert is probably not valid in all cases. # TODO: This assert is probably not valid in all cases.
assert self.inipath is not None assert self.inipath is not None
dp = self.inipath.parent dp = self.inipath.parent
@ -1422,9 +1421,12 @@ class Config:
return value return value
elif type == "bool": elif type == "bool":
return _strtobool(str(value).strip()) return _strtobool(str(value).strip())
else: elif type == "string":
assert type in [None, "string"]
return value return value
elif type is None:
return value
else:
return self._getini_unknown_type(name, type, value)
def _getconftest_pathlist( def _getconftest_pathlist(
self, name: str, path: Path, rootpath: Path self, name: str, path: Path, rootpath: Path

View File

@ -1,4 +1,5 @@
"""Add backward compatibility support for the legacy py path type.""" """Add backward compatibility support for the legacy py path type."""
import shlex
import subprocess import subprocess
from pathlib import Path from pathlib import Path
from typing import List from typing import List
@ -372,6 +373,19 @@ def Session_stardir(self: pytest.Session) -> LEGACY_PATH:
return legacy_path(self.startpath) return legacy_path(self.startpath)
def Config__getini_unknown_type(
self, name: str, type: str, value: Union[str, List[str]]
):
if type == "pathlist":
# 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 [legacy_path(str(dp / x)) for x in input_values]
else:
raise ValueError(f"unknown configuration type: {type}", value)
def pytest_configure(config: pytest.Config) -> None: def pytest_configure(config: pytest.Config) -> None:
mp = pytest.MonkeyPatch() mp = pytest.MonkeyPatch()
config.add_cleanup(mp.undo) config.add_cleanup(mp.undo)
@ -412,3 +426,6 @@ def pytest_configure(config: pytest.Config) -> None:
# Add Session.startdir property. # Add Session.startdir property.
mp.setattr(pytest.Session, "startdir", property(Session_stardir), raising=False) mp.setattr(pytest.Session, "startdir", property(Session_stardir), raising=False)
# Add pathlist configuration type.
mp.setattr(pytest.Config, "_getini_unknown_type", Config__getini_unknown_type)

View File

@ -635,14 +635,11 @@ class TestConfigAPI:
pytest.raises(ValueError, config.getini, "other") pytest.raises(ValueError, config.getini, "other")
@pytest.mark.parametrize("config_type", ["ini", "pyproject"]) @pytest.mark.parametrize("config_type", ["ini", "pyproject"])
@pytest.mark.parametrize("ini_type", ["paths", "pathlist"]) def test_addini_paths(self, pytester: Pytester, config_type: str) -> None:
def test_addini_paths(
self, pytester: Pytester, config_type: str, ini_type: str
) -> None:
pytester.makeconftest( pytester.makeconftest(
f""" """
def pytest_addoption(parser): def pytest_addoption(parser):
parser.addini("paths", "my new ini value", type="{ini_type}") parser.addini("paths", "my new ini value", type="paths")
parser.addini("abc", "abc value") parser.addini("abc", "abc value")
""" """
) )
@ -1521,12 +1518,11 @@ class TestOverrideIniArgs:
assert result.ret == 0 assert result.ret == 0
result.stdout.fnmatch_lines(["custom_option:3.0"]) result.stdout.fnmatch_lines(["custom_option:3.0"])
@pytest.mark.parametrize("ini_type", ["paths", "pathlist"]) def test_override_ini_paths(self, pytester: Pytester) -> None:
def test_override_ini_paths(self, pytester: Pytester, ini_type: str) -> None:
pytester.makeconftest( pytester.makeconftest(
f""" """
def pytest_addoption(parser): def pytest_addoption(parser):
parser.addini("paths", "my new ini value", type="{ini_type}")""" parser.addini("paths", "my new ini value", type="paths")"""
) )
pytester.makeini( pytester.makeini(
""" """
@ -1534,15 +1530,12 @@ class TestOverrideIniArgs:
paths=blah.py""" paths=blah.py"""
) )
pytester.makepyfile( pytester.makepyfile(
rf""" r"""
def test_overriden(pytestconfig): def test_overriden(pytestconfig):
config_paths = pytestconfig.getini("paths") config_paths = pytestconfig.getini("paths")
print(config_paths) print(config_paths)
for cpf in config_paths: for cpf in config_paths:
if "{ini_type}" == "pathlist": print('\nuser_path:%s' % cpf.name)
print('\nuser_path:%s' % cpf.basename)
else:
print('\nuser_path:%s' % cpf.name)
""" """
) )
result = pytester.runpytest( result = pytester.runpytest(

View File

@ -94,3 +94,58 @@ class TestFixtureRequestSessionScoped:
match="path not available in session-scoped context", match="path not available in session-scoped context",
): ):
session_request.fspath session_request.fspath
@pytest.mark.parametrize("config_type", ["ini", "pyproject"])
def test_addini_paths(pytester: pytest.Pytester, config_type: str) -> None:
pytester.makeconftest(
"""
def pytest_addoption(parser):
parser.addini("paths", "my new ini value", type="pathlist")
parser.addini("abc", "abc value")
"""
)
if config_type == "ini":
inipath = pytester.makeini(
"""
[pytest]
paths=hello world/sub.py
"""
)
elif config_type == "pyproject":
inipath = pytester.makepyprojecttoml(
"""
[tool.pytest.ini_options]
paths=["hello", "world/sub.py"]
"""
)
config = pytester.parseconfig()
values = config.getini("paths")
assert len(values) == 2
assert values[0] == inipath.parent.joinpath("hello")
assert values[1] == inipath.parent.joinpath("world/sub.py")
pytest.raises(ValueError, config.getini, "other")
def test_override_ini_paths(pytester: pytest.Pytester) -> None:
pytester.makeconftest(
"""
def pytest_addoption(parser):
parser.addini("paths", "my new ini value", type="pathlist")"""
)
pytester.makeini(
"""
[pytest]
paths=blah.py"""
)
pytester.makepyfile(
r"""
def test_overriden(pytestconfig):
config_paths = pytestconfig.getini("paths")
print(config_paths)
for cpf in config_paths:
print('\nuser_path:%s' % cpf.basename)
"""
)
result = pytester.runpytest("--override-ini", "paths=foo/bar1.py foo/bar2.py", "-s")
result.stdout.fnmatch_lines(["user_path:bar1.py", "user_path:bar2.py"])