Skip to content

Commit faeb161

Browse files
committedMay 12, 2023
Consider testpaths for initial conftests
The 'testpaths' option is meant to be identical to execute pytest passing the 'testpaths' directories explicitly. Fix #10987
1 parent b241c0b commit faeb161

File tree

5 files changed

+59
-9
lines changed

5 files changed

+59
-9
lines changed
 

‎changelog/10987.bugfix.rst

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
:confval:`testpaths` is now honored to load root ``conftests``.

‎doc/en/reference/reference.rst

+12-4
Original file line numberDiff line numberDiff line change
@@ -1713,13 +1713,12 @@ passed multiple times. The expected format is ``name=value``. For example::
17131713
17141714
.. confval:: testpaths
17151715

1716-
1717-
17181716
Sets list of directories that should be searched for tests when
17191717
no specific directories, files or test ids are given in the command line when
17201718
executing pytest from the :ref:`rootdir <rootdir>` directory.
17211719
File system paths may use shell-style wildcards, including the recursive
17221720
``**`` pattern.
1721+
17231722
Useful when all project tests are in a known location to speed up
17241723
test collection and to avoid picking up undesired tests by accident.
17251724

@@ -1728,8 +1727,17 @@ passed multiple times. The expected format is ``name=value``. For example::
17281727
[pytest]
17291728
testpaths = testing doc
17301729
1731-
This tells pytest to only look for tests in ``testing`` and ``doc``
1732-
directories when executing from the root directory.
1730+
This configuration means that executing:
1731+
1732+
.. code-block:: console
1733+
1734+
pytest
1735+
1736+
has the same practical effects as executing:
1737+
1738+
.. code-block:: console
1739+
1740+
pytest testing doc
17331741
17341742
17351743
.. confval:: tmp_path_retention_count

‎src/_pytest/config/__init__.py

+22-4
Original file line numberDiff line numberDiff line change
@@ -526,7 +526,10 @@ def pytest_configure(self, config: "Config") -> None:
526526
# Internal API for local conftest plugin handling.
527527
#
528528
def _set_initial_conftests(
529-
self, namespace: argparse.Namespace, rootpath: Path
529+
self,
530+
namespace: argparse.Namespace,
531+
rootpath: Path,
532+
testpaths_ini: Sequence[str],
530533
) -> None:
531534
"""Load initial conftest files given a preparsed "namespace".
532535
@@ -543,7 +546,7 @@ def _set_initial_conftests(
543546
)
544547
self._noconftest = namespace.noconftest
545548
self._using_pyargs = namespace.pyargs
546-
testpaths = namespace.file_or_dir
549+
testpaths = namespace.file_or_dir + testpaths_ini
547550
foundanchor = False
548551
for testpath in testpaths:
549552
path = str(testpath)
@@ -552,7 +555,20 @@ def _set_initial_conftests(
552555
if i != -1:
553556
path = path[:i]
554557
anchor = absolutepath(current / path)
555-
if anchor.exists(): # we found some file object
558+
559+
# On Python 3.7 on Windows, anchor.exists() might raise
560+
# if the anchor contains glob characters (for example "*//tests"), specially
561+
# in the case of the 'testpaths' ini option.
562+
# Using an explicit version check to remove this code later once
563+
# Python 3.7 is dropped.
564+
if sys.version_info[:2] == (3, 7):
565+
try:
566+
anchor_exists = anchor.exists()
567+
except OSError: # pragma: no cover
568+
anchor_exists = False
569+
else:
570+
anchor_exists = anchor.exists()
571+
if anchor_exists: # We found some file object.
556572
self._try_load_conftest(anchor, namespace.importmode, rootpath)
557573
foundanchor = True
558574
if not foundanchor:
@@ -1131,7 +1147,9 @@ def _processopt(self, opt: "Argument") -> None:
11311147
@hookimpl(trylast=True)
11321148
def pytest_load_initial_conftests(self, early_config: "Config") -> None:
11331149
self.pluginmanager._set_initial_conftests(
1134-
early_config.known_args_namespace, rootpath=early_config.rootpath
1150+
early_config.known_args_namespace,
1151+
rootpath=early_config.rootpath,
1152+
testpaths_ini=self.getini("testpaths"),
11351153
)
11361154

11371155
def _initini(self, args: Sequence[str]) -> None:

‎testing/test_collection.py

+23
Original file line numberDiff line numberDiff line change
@@ -1247,6 +1247,29 @@ def test_collect_pyargs_with_testpaths(
12471247
result.stdout.fnmatch_lines(["*1 passed in*"])
12481248

12491249

1250+
def test_initial_conftests_with_testpaths(pytester: Pytester) -> None:
1251+
"""The testpaths ini option should load conftests in those paths as 'initial' (#10987)."""
1252+
p = pytester.mkdir("some_path")
1253+
p.joinpath("conftest.py").write_text(
1254+
textwrap.dedent(
1255+
"""
1256+
def pytest_sessionstart(session):
1257+
raise Exception("pytest_sessionstart hook is successfully run")
1258+
"""
1259+
)
1260+
)
1261+
pytester.makeini(
1262+
"""
1263+
[pytest]
1264+
testpaths = some_path
1265+
"""
1266+
)
1267+
result = pytester.runpytest()
1268+
result.stdout.fnmatch_lines(
1269+
"INTERNALERROR* Exception: pytest_sessionstart hook is successfully run"
1270+
)
1271+
1272+
12501273
def test_collect_symlink_file_arg(pytester: Pytester) -> None:
12511274
"""Collect a direct symlink works even if it does not match python_files (#4325)."""
12521275
real = pytester.makepyfile(

‎testing/test_conftest.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def __init__(self) -> None:
3535
self.importmode = "prepend"
3636

3737
namespace = cast(argparse.Namespace, Namespace())
38-
conftest._set_initial_conftests(namespace, rootpath=Path(args[0]))
38+
conftest._set_initial_conftests(namespace, rootpath=Path(args[0]), testpaths_ini=[])
3939

4040

4141
@pytest.mark.usefixtures("_sys_snapshot")

0 commit comments

Comments
 (0)