Skip to content

Commit 68c3707

Browse files
committed
argparsing: support parser.addini(type="paths") which returns pathlib.Paths
1 parent 0341893 commit 68c3707

File tree

4 files changed

+65
-40
lines changed

4 files changed

+65
-40
lines changed

changelog/7259.feature.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,7 @@
11
Added :meth:`cache.mkdir() <pytest.Cache.mkdir>`, which is similar to the existing :meth:`cache.makedir() <pytest.Cache.makedir>`,
22
but returns a :class:`pathlib.Path` instead of a legacy ``py.path.local``.
3+
4+
Added a ``paths`` type to :meth:`parser.addini() <_pytest.config.argparsing.Parser.addini>`,
5+
as in ``parser.addini("mypaths", "my paths", type="paths")``,
6+
which is similar to the existing ``pathlist``,
7+
but returns a list of :class:`pathlib.Path` instead of legacy ``py.path.local``.

src/_pytest/config/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1427,6 +1427,12 @@ def _getini(self, name: str):
14271427
dp = self.inipath.parent
14281428
input_values = shlex.split(value) if isinstance(value, str) else value
14291429
return [legacy_path(str(dp / x)) for x in input_values]
1430+
elif type == "paths":
1431+
# TODO: This assert is probably not valid in all cases.
1432+
assert self.inipath is not None
1433+
dp = self.inipath.parent
1434+
input_values = shlex.split(value) if isinstance(value, str) else value
1435+
return [dp / x for x in input_values]
14301436
elif type == "args":
14311437
return shlex.split(value) if isinstance(value, str) else value
14321438
elif type == "linelist":

src/_pytest/config/argparsing.py

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -163,22 +163,35 @@ def addini(
163163
name: str,
164164
help: str,
165165
type: Optional[
166-
"Literal['string', 'pathlist', 'args', 'linelist', 'bool']"
166+
"Literal['string', 'paths', 'pathlist', 'args', 'linelist', 'bool']"
167167
] = None,
168168
default=None,
169169
) -> None:
170170
"""Register an ini-file option.
171171
172-
:name: Name of the ini-variable.
173-
:type: Type of the variable, can be ``string``, ``pathlist``, ``args``,
174-
``linelist`` or ``bool``. Defaults to ``string`` if ``None`` or
175-
not passed.
176-
:default: Default value if no ini-file option exists but is queried.
172+
:name:
173+
Name of the ini-variable.
174+
:type:
175+
Type of the variable. Can be:
176+
177+
* ``string``: a string
178+
* ``bool``: a boolean
179+
* ``args``: a list of strings, separated as in a shell
180+
* ``linelist``: a list of strings, separated by line breaks
181+
* ``paths``: a list of :class:`pathlib.Path`, separated as in a shell
182+
* ``pathlist``: a list of ``py.path.local``, separated as in a shell
183+
184+
.. versionadded:: 6.3
185+
The ``paths`` variable type.
186+
187+
Defaults to ``string`` if ``None`` or not passed.
188+
:default:
189+
Default value if no ini-file option exists but is queried.
177190
178191
The value of ini-variables can be retrieved via a call to
179192
:py:func:`config.getini(name) <_pytest.config.Config.getini>`.
180193
"""
181-
assert type in (None, "string", "pathlist", "args", "linelist", "bool")
194+
assert type in (None, "string", "paths", "pathlist", "args", "linelist", "bool")
182195
self._inidict[name] = (help, type, default)
183196
self._ininames.append(name)
184197

testing/test_config.py

Lines changed: 34 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -595,14 +595,14 @@ def test_getoption(self, pytester: Pytester) -> None:
595595
def test_getconftest_pathlist(self, pytester: Pytester, tmp_path: Path) -> None:
596596
somepath = tmp_path.joinpath("x", "y", "z")
597597
p = tmp_path.joinpath("conftest.py")
598-
p.write_text(f"pathlist = ['.', {str(somepath)!r}]")
598+
p.write_text(f"mylist = ['.', {str(somepath)!r}]")
599599
config = pytester.parseconfigure(p)
600600
assert (
601601
config._getconftest_pathlist("notexist", path=tmp_path, rootpath=tmp_path)
602602
is None
603603
)
604604
pl = (
605-
config._getconftest_pathlist("pathlist", path=tmp_path, rootpath=tmp_path)
605+
config._getconftest_pathlist("mylist", path=tmp_path, rootpath=tmp_path)
606606
or []
607607
)
608608
print(pl)
@@ -634,41 +634,37 @@ def pytest_addoption(parser):
634634
assert val == "hello"
635635
pytest.raises(ValueError, config.getini, "other")
636636

637-
def make_conftest_for_pathlist(self, pytester: Pytester) -> None:
637+
@pytest.mark.parametrize("config_type", ["ini", "pyproject"])
638+
@pytest.mark.parametrize("ini_type", ["paths", "pathlist"])
639+
def test_addini_paths(
640+
self, pytester: Pytester, config_type: str, ini_type: str
641+
) -> None:
638642
pytester.makeconftest(
639-
"""
643+
f"""
640644
def pytest_addoption(parser):
641-
parser.addini("paths", "my new ini value", type="pathlist")
645+
parser.addini("paths", "my new ini value", type="{ini_type}")
642646
parser.addini("abc", "abc value")
643647
"""
644648
)
645-
646-
def test_addini_pathlist_ini_files(self, pytester: Pytester) -> None:
647-
self.make_conftest_for_pathlist(pytester)
648-
p = pytester.makeini(
649+
if config_type == "ini":
650+
inipath = pytester.makeini(
651+
"""
652+
[pytest]
653+
paths=hello world/sub.py
649654
"""
650-
[pytest]
651-
paths=hello world/sub.py
652-
"""
653-
)
654-
self.check_config_pathlist(pytester, p)
655-
656-
def test_addini_pathlist_pyproject_toml(self, pytester: Pytester) -> None:
657-
self.make_conftest_for_pathlist(pytester)
658-
p = pytester.makepyprojecttoml(
655+
)
656+
elif config_type == "pyproject":
657+
inipath = pytester.makepyprojecttoml(
658+
"""
659+
[tool.pytest.ini_options]
660+
paths=["hello", "world/sub.py"]
659661
"""
660-
[tool.pytest.ini_options]
661-
paths=["hello", "world/sub.py"]
662-
"""
663-
)
664-
self.check_config_pathlist(pytester, p)
665-
666-
def check_config_pathlist(self, pytester: Pytester, config_path: Path) -> None:
662+
)
667663
config = pytester.parseconfig()
668664
values = config.getini("paths")
669665
assert len(values) == 2
670-
assert values[0] == config_path.parent.joinpath("hello")
671-
assert values[1] == config_path.parent.joinpath("world/sub.py")
666+
assert values[0] == inipath.parent.joinpath("hello")
667+
assert values[1] == inipath.parent.joinpath("world/sub.py")
672668
pytest.raises(ValueError, config.getini, "other")
673669

674670
def make_conftest_for_args(self, pytester: Pytester) -> None:
@@ -1519,24 +1515,29 @@ def test_pass(pytestconfig):
15191515
assert result.ret == 0
15201516
result.stdout.fnmatch_lines(["custom_option:3.0"])
15211517

1522-
def test_override_ini_pathlist(self, pytester: Pytester) -> None:
1518+
@pytest.mark.parametrize("ini_type", ["paths", "pathlist"])
1519+
def test_override_ini_paths(self, pytester: Pytester, ini_type: str) -> None:
15231520
pytester.makeconftest(
1524-
"""
1521+
f"""
15251522
def pytest_addoption(parser):
1526-
parser.addini("paths", "my new ini value", type="pathlist")"""
1523+
parser.addini("paths", "my new ini value", type="{ini_type}")"""
15271524
)
15281525
pytester.makeini(
15291526
"""
15301527
[pytest]
15311528
paths=blah.py"""
15321529
)
15331530
pytester.makepyfile(
1534-
"""
1535-
def test_pathlist(pytestconfig):
1531+
rf"""
1532+
def test_overriden(pytestconfig):
15361533
config_paths = pytestconfig.getini("paths")
15371534
print(config_paths)
15381535
for cpf in config_paths:
1539-
print('\\nuser_path:%s' % cpf.basename)"""
1536+
if "{ini_type}" == "pathlist":
1537+
print('\nuser_path:%s' % cpf.basename)
1538+
else:
1539+
print('\nuser_path:%s' % cpf.name)
1540+
"""
15401541
)
15411542
result = pytester.runpytest(
15421543
"--override-ini", "paths=foo/bar1.py foo/bar2.py", "-s"

0 commit comments

Comments
 (0)