config: fix the paths considered for initial conftest discovery
Fixes #11104.
See the issue for a description of the problem.
Now, we use the same logic for initial conftest paths as we do for
deciding the initial args, which was the idea behind checking
`namespace.file_or_dir` and `testpaths` previously.
This fixes the issue of `testpaths` being considered for initial
conftests even when it's not used for the args.
(Another issue in faeb16146b was that the
`testpaths` were not glob-expanded, this is also fixed.)
			
			
This commit is contained in:
		
							parent
							
								
									d97d44a97a
								
							
						
					
					
						commit
						14890329dc
					
				| 
						 | 
				
			
			@ -0,0 +1,3 @@
 | 
			
		|||
Fixed a regression in pytest 7.3.2 which caused to :confval:`testpaths` to be considered for loading initial conftests,
 | 
			
		||||
even when it was not utilized (e.g. when explicit paths were given on the command line).
 | 
			
		||||
Now the ``testpaths`` are only considered when they are in use.
 | 
			
		||||
| 
						 | 
				
			
			@ -527,9 +527,12 @@ class PytestPluginManager(PluginManager):
 | 
			
		|||
    #
 | 
			
		||||
    def _set_initial_conftests(
 | 
			
		||||
        self,
 | 
			
		||||
        namespace: argparse.Namespace,
 | 
			
		||||
        args: Sequence[Union[str, Path]],
 | 
			
		||||
        pyargs: bool,
 | 
			
		||||
        noconftest: bool,
 | 
			
		||||
        rootpath: Path,
 | 
			
		||||
        testpaths_ini: Sequence[str],
 | 
			
		||||
        confcutdir: Optional[Path],
 | 
			
		||||
        importmode: Union[ImportMode, str],
 | 
			
		||||
    ) -> None:
 | 
			
		||||
        """Load initial conftest files given a preparsed "namespace".
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -539,17 +542,12 @@ class PytestPluginManager(PluginManager):
 | 
			
		|||
        common options will not confuse our logic here.
 | 
			
		||||
        """
 | 
			
		||||
        current = Path.cwd()
 | 
			
		||||
        self._confcutdir = (
 | 
			
		||||
            absolutepath(current / namespace.confcutdir)
 | 
			
		||||
            if namespace.confcutdir
 | 
			
		||||
            else None
 | 
			
		||||
        )
 | 
			
		||||
        self._noconftest = namespace.noconftest
 | 
			
		||||
        self._using_pyargs = namespace.pyargs
 | 
			
		||||
        testpaths = namespace.file_or_dir + testpaths_ini
 | 
			
		||||
        self._confcutdir = absolutepath(current / confcutdir) if confcutdir else None
 | 
			
		||||
        self._noconftest = noconftest
 | 
			
		||||
        self._using_pyargs = pyargs
 | 
			
		||||
        foundanchor = False
 | 
			
		||||
        for testpath in testpaths:
 | 
			
		||||
            path = str(testpath)
 | 
			
		||||
        for intitial_path in args:
 | 
			
		||||
            path = str(intitial_path)
 | 
			
		||||
            # remove node-id syntax
 | 
			
		||||
            i = path.find("::")
 | 
			
		||||
            if i != -1:
 | 
			
		||||
| 
						 | 
				
			
			@ -563,10 +561,10 @@ class PytestPluginManager(PluginManager):
 | 
			
		|||
            except OSError:  # pragma: no cover
 | 
			
		||||
                anchor_exists = False
 | 
			
		||||
            if anchor_exists:
 | 
			
		||||
                self._try_load_conftest(anchor, namespace.importmode, rootpath)
 | 
			
		||||
                self._try_load_conftest(anchor, importmode, rootpath)
 | 
			
		||||
                foundanchor = True
 | 
			
		||||
        if not foundanchor:
 | 
			
		||||
            self._try_load_conftest(current, namespace.importmode, rootpath)
 | 
			
		||||
            self._try_load_conftest(current, importmode, rootpath)
 | 
			
		||||
 | 
			
		||||
    def _is_in_confcutdir(self, path: Path) -> bool:
 | 
			
		||||
        """Whether a path is within the confcutdir.
 | 
			
		||||
| 
						 | 
				
			
			@ -1140,10 +1138,25 @@ class Config:
 | 
			
		|||
 | 
			
		||||
    @hookimpl(trylast=True)
 | 
			
		||||
    def pytest_load_initial_conftests(self, early_config: "Config") -> None:
 | 
			
		||||
        self.pluginmanager._set_initial_conftests(
 | 
			
		||||
            early_config.known_args_namespace,
 | 
			
		||||
        # We haven't fully parsed the command line arguments yet, so
 | 
			
		||||
        # early_config.args it not set yet. But we need it for
 | 
			
		||||
        # discovering the initial conftests. So "pre-run" the logic here.
 | 
			
		||||
        # It will be done for real in `parse()`.
 | 
			
		||||
        args, args_source = early_config._decide_args(
 | 
			
		||||
            args=early_config.known_args_namespace.file_or_dir,
 | 
			
		||||
            pyargs=early_config.known_args_namespace.pyargs,
 | 
			
		||||
            testpaths=early_config.getini("testpaths"),
 | 
			
		||||
            invocation_dir=early_config.invocation_params.dir,
 | 
			
		||||
            rootpath=early_config.rootpath,
 | 
			
		||||
            testpaths_ini=self.getini("testpaths"),
 | 
			
		||||
            warn=False,
 | 
			
		||||
        )
 | 
			
		||||
        self.pluginmanager._set_initial_conftests(
 | 
			
		||||
            args=args,
 | 
			
		||||
            pyargs=early_config.known_args_namespace.pyargs,
 | 
			
		||||
            noconftest=early_config.known_args_namespace.noconftest,
 | 
			
		||||
            rootpath=early_config.rootpath,
 | 
			
		||||
            confcutdir=early_config.known_args_namespace.confcutdir,
 | 
			
		||||
            importmode=early_config.known_args_namespace.importmode,
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def _initini(self, args: Sequence[str]) -> None:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1264,11 +1264,18 @@ def test_initial_conftests_with_testpaths(pytester: Pytester) -> None:
 | 
			
		|||
        testpaths = some_path
 | 
			
		||||
        """
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    # No command line args - falls back to testpaths.
 | 
			
		||||
    result = pytester.runpytest()
 | 
			
		||||
    assert result.ret == ExitCode.INTERNAL_ERROR
 | 
			
		||||
    result.stdout.fnmatch_lines(
 | 
			
		||||
        "INTERNALERROR* Exception: pytest_sessionstart hook successfully run"
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    # No fallback.
 | 
			
		||||
    result = pytester.runpytest(".")
 | 
			
		||||
    assert result.ret == ExitCode.NO_TESTS_COLLECTED
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_large_option_breaks_initial_conftests(pytester: Pytester) -> None:
 | 
			
		||||
    """Long option values do not break initial conftests handling (#10169)."""
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,3 @@
 | 
			
		|||
import argparse
 | 
			
		||||
import os
 | 
			
		||||
import textwrap
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
| 
						 | 
				
			
			@ -7,6 +6,8 @@ from typing import Dict
 | 
			
		|||
from typing import Generator
 | 
			
		||||
from typing import List
 | 
			
		||||
from typing import Optional
 | 
			
		||||
from typing import Sequence
 | 
			
		||||
from typing import Union
 | 
			
		||||
 | 
			
		||||
import pytest
 | 
			
		||||
from _pytest.config import ExitCode
 | 
			
		||||
| 
						 | 
				
			
			@ -24,18 +25,18 @@ def ConftestWithSetinitial(path) -> PytestPluginManager:
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
def conftest_setinitial(
 | 
			
		||||
    conftest: PytestPluginManager, args, confcutdir: Optional["os.PathLike[str]"] = None
 | 
			
		||||
    conftest: PytestPluginManager,
 | 
			
		||||
    args: Sequence[Union[str, Path]],
 | 
			
		||||
    confcutdir: Optional[Path] = None,
 | 
			
		||||
) -> None:
 | 
			
		||||
    class Namespace:
 | 
			
		||||
        def __init__(self) -> None:
 | 
			
		||||
            self.file_or_dir = args
 | 
			
		||||
            self.confcutdir = os.fspath(confcutdir) if confcutdir is not None else None
 | 
			
		||||
            self.noconftest = False
 | 
			
		||||
            self.pyargs = False
 | 
			
		||||
            self.importmode = "prepend"
 | 
			
		||||
 | 
			
		||||
    namespace = cast(argparse.Namespace, Namespace())
 | 
			
		||||
    conftest._set_initial_conftests(namespace, rootpath=Path(args[0]), testpaths_ini=[])
 | 
			
		||||
    conftest._set_initial_conftests(
 | 
			
		||||
        args=args,
 | 
			
		||||
        pyargs=False,
 | 
			
		||||
        noconftest=False,
 | 
			
		||||
        rootpath=Path(args[0]),
 | 
			
		||||
        confcutdir=confcutdir,
 | 
			
		||||
        importmode="prepend",
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@pytest.mark.usefixtures("_sys_snapshot")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue