Skip to content

Commit

Permalink
Merge pull request pytest-dev#10988 from nicoddemus/initial-testpaths…
Browse files Browse the repository at this point in the history
…-10987

 Consider testpaths for initial conftests
  • Loading branch information
nicoddemus committed May 12, 2023
2 parents 7840323 + 4cc05e7 commit 76d1523
Show file tree
Hide file tree
Showing 7 changed files with 76 additions and 12 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/stale.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: close needs-information issues
on:
schedule:
- cron: "30 1 * * *"
workflow_dispatch:
workflow_dispatch:

jobs:
close-issues:
Expand Down
1 change: 1 addition & 0 deletions changelog/10169.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix bug where very long option names could cause pytest to break with ``OSError: [Errno 36] File name too long`` on some systems.
1 change: 1 addition & 0 deletions changelog/10987.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
:confval:`testpaths` is now honored to load root ``conftests``.
20 changes: 14 additions & 6 deletions doc/en/reference/reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1713,13 +1713,12 @@ passed multiple times. The expected format is ``name=value``. For example::
.. confval:: testpaths



Sets list of directories that should be searched for tests when
no specific directories, files or test ids are given in the command line when
executing pytest from the :ref:`rootdir <rootdir>` directory.
File system paths may use shell-style wildcards, including the recursive
``**`` pattern.

Useful when all project tests are in a known location to speed up
test collection and to avoid picking up undesired tests by accident.

Expand All @@ -1728,8 +1727,17 @@ passed multiple times. The expected format is ``name=value``. For example::
[pytest]
testpaths = testing doc
This tells pytest to only look for tests in ``testing`` and ``doc``
directories when executing from the root directory.
This configuration means that executing:

.. code-block:: console
pytest
has the same practical effects as executing:

.. code-block:: console
pytest testing doc
.. confval:: tmp_path_retention_count
Expand All @@ -1744,7 +1752,7 @@ passed multiple times. The expected format is ``name=value``. For example::
[pytest]
tmp_path_retention_count = 3
Default: 3
Default: ``3``


.. confval:: tmp_path_retention_policy
Expand All @@ -1763,7 +1771,7 @@ passed multiple times. The expected format is ``name=value``. For example::
[pytest]
tmp_path_retention_policy = "all"
Default: all
Default: ``all``


.. confval:: usefixtures
Expand Down
20 changes: 16 additions & 4 deletions src/_pytest/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,10 @@ def pytest_configure(self, config: "Config") -> None:
# Internal API for local conftest plugin handling.
#
def _set_initial_conftests(
self, namespace: argparse.Namespace, rootpath: Path
self,
namespace: argparse.Namespace,
rootpath: Path,
testpaths_ini: Sequence[str],
) -> None:
"""Load initial conftest files given a preparsed "namespace".
Expand All @@ -543,7 +546,7 @@ def _set_initial_conftests(
)
self._noconftest = namespace.noconftest
self._using_pyargs = namespace.pyargs
testpaths = namespace.file_or_dir
testpaths = namespace.file_or_dir + testpaths_ini
foundanchor = False
for testpath in testpaths:
path = str(testpath)
Expand All @@ -552,7 +555,14 @@ def _set_initial_conftests(
if i != -1:
path = path[:i]
anchor = absolutepath(current / path)
if anchor.exists(): # we found some file object

# Ensure we do not break if what appears to be an anchor
# is in fact a very long option (#10169).
try:
anchor_exists = anchor.exists()
except OSError: # pragma: no cover
anchor_exists = False
if anchor_exists:
self._try_load_conftest(anchor, namespace.importmode, rootpath)
foundanchor = True
if not foundanchor:
Expand Down Expand Up @@ -1131,7 +1141,9 @@ def _processopt(self, opt: "Argument") -> None:
@hookimpl(trylast=True)
def pytest_load_initial_conftests(self, early_config: "Config") -> None:
self.pluginmanager._set_initial_conftests(
early_config.known_args_namespace, rootpath=early_config.rootpath
early_config.known_args_namespace,
rootpath=early_config.rootpath,
testpaths_ini=self.getini("testpaths"),
)

def _initini(self, args: Sequence[str]) -> None:
Expand Down
42 changes: 42 additions & 0 deletions testing/test_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -1247,6 +1247,48 @@ def test_collect_pyargs_with_testpaths(
result.stdout.fnmatch_lines(["*1 passed in*"])


def test_initial_conftests_with_testpaths(pytester: Pytester) -> None:
"""The testpaths ini option should load conftests in those paths as 'initial' (#10987)."""
p = pytester.mkdir("some_path")
p.joinpath("conftest.py").write_text(
textwrap.dedent(
"""
def pytest_sessionstart(session):
raise Exception("pytest_sessionstart hook successfully run")
"""
)
)
pytester.makeini(
"""
[pytest]
testpaths = some_path
"""
)
result = pytester.runpytest()
result.stdout.fnmatch_lines(
"INTERNALERROR* Exception: pytest_sessionstart hook successfully run"
)


def test_large_option_breaks_initial_conftests(pytester: Pytester) -> None:
"""Long option values do not break initial conftests handling (#10169)."""
option_value = "x" * 1024 * 1000
pytester.makeconftest(
"""
def pytest_addoption(parser):
parser.addoption("--xx", default=None)
"""
)
pytester.makepyfile(
f"""
def test_foo(request):
assert request.config.getoption("xx") == {option_value!r}
"""
)
result = pytester.runpytest(f"--xx={option_value}")
assert result.ret == 0


def test_collect_symlink_file_arg(pytester: Pytester) -> None:
"""Collect a direct symlink works even if it does not match python_files (#4325)."""
real = pytester.makepyfile(
Expand Down
2 changes: 1 addition & 1 deletion testing/test_conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def __init__(self) -> None:
self.importmode = "prepend"

namespace = cast(argparse.Namespace, Namespace())
conftest._set_initial_conftests(namespace, rootpath=Path(args[0]))
conftest._set_initial_conftests(namespace, rootpath=Path(args[0]), testpaths_ini=[])


@pytest.mark.usefixtures("_sys_snapshot")
Expand Down

0 comments on commit 76d1523

Please sign in to comment.