argparsing: support parser.addini(type="paths") which returns pathlib.Paths

This commit is contained in:
Ran Benita 2021-04-26 17:46:26 +03:00
parent 634312b14a
commit 113a860a1f
4 changed files with 65 additions and 40 deletions

View File

@ -1,2 +1,7 @@
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``.
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``.

View File

@ -1427,6 +1427,12 @@ class Config:
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":

View File

@ -163,22 +163,35 @@ class Parser:
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)

View File

@ -595,14 +595,14 @@ class TestConfigAPI:
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 @@ class TestConfigAPI:
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
"""
)
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"]
"""
)
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 @@ class TestOverrideIniArgs:
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 @@ class TestOverrideIniArgs:
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"