Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consider testpaths for initial conftests #10988

Merged
merged 4 commits into from
May 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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