fixtures: clean up getfixtureclosure()
Some code cleanups - no functional changes.
This commit is contained in:
		
							parent
							
								
									9c11275553
								
							
						
					
					
						commit
						48b0395648
					
				| 
						 | 
					@ -1402,6 +1402,12 @@ def _get_direct_parametrize_args(node: nodes.Node) -> Set[str]:
 | 
				
			||||||
    return parametrize_argnames
 | 
					    return parametrize_argnames
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def deduplicate_names(*seqs: Iterable[str]) -> Tuple[str, ...]:
 | 
				
			||||||
 | 
					    """De-duplicate the sequence of names while keeping the original order."""
 | 
				
			||||||
 | 
					    # Ideally we would use a set, but it does not preserve insertion order.
 | 
				
			||||||
 | 
					    return tuple(dict.fromkeys(name for seq in seqs for name in seq))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class FixtureManager:
 | 
					class FixtureManager:
 | 
				
			||||||
    """pytest fixture definitions and information is stored and managed
 | 
					    """pytest fixture definitions and information is stored and managed
 | 
				
			||||||
    from this class.
 | 
					    from this class.
 | 
				
			||||||
| 
						 | 
					@ -1476,14 +1482,18 @@ class FixtureManager:
 | 
				
			||||||
            argnames = getfuncargnames(func, name=node.name, cls=cls)
 | 
					            argnames = getfuncargnames(func, name=node.name, cls=cls)
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            argnames = ()
 | 
					            argnames = ()
 | 
				
			||||||
 | 
					        usefixturesnames = self._getusefixturesnames(node)
 | 
				
			||||||
 | 
					        autousenames = self._getautousenames(node.nodeid)
 | 
				
			||||||
 | 
					        initialnames = deduplicate_names(autousenames, usefixturesnames, argnames)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        usefixtures = tuple(
 | 
					        direct_parametrize_args = _get_direct_parametrize_args(node)
 | 
				
			||||||
            arg for mark in node.iter_markers(name="usefixtures") for arg in mark.args
 | 
					
 | 
				
			||||||
        )
 | 
					        names_closure, arg2fixturedefs = self.getfixtureclosure(
 | 
				
			||||||
        initialnames = usefixtures + argnames
 | 
					            parentnode=node,
 | 
				
			||||||
        initialnames, names_closure, arg2fixturedefs = self.getfixtureclosure(
 | 
					            initialnames=initialnames,
 | 
				
			||||||
            initialnames, node, ignore_args=_get_direct_parametrize_args(node)
 | 
					            ignore_args=direct_parametrize_args,
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return FuncFixtureInfo(argnames, initialnames, names_closure, arg2fixturedefs)
 | 
					        return FuncFixtureInfo(argnames, initialnames, names_closure, arg2fixturedefs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def pytest_plugin_registered(self, plugin: _PluggyPlugin) -> None:
 | 
					    def pytest_plugin_registered(self, plugin: _PluggyPlugin) -> None:
 | 
				
			||||||
| 
						 | 
					@ -1515,12 +1525,17 @@ class FixtureManager:
 | 
				
			||||||
            if basenames:
 | 
					            if basenames:
 | 
				
			||||||
                yield from basenames
 | 
					                yield from basenames
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _getusefixturesnames(self, node: nodes.Item) -> Iterator[str]:
 | 
				
			||||||
 | 
					        """Return the names of usefixtures fixtures applicable to node."""
 | 
				
			||||||
 | 
					        for mark in node.iter_markers(name="usefixtures"):
 | 
				
			||||||
 | 
					            yield from mark.args
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def getfixtureclosure(
 | 
					    def getfixtureclosure(
 | 
				
			||||||
        self,
 | 
					        self,
 | 
				
			||||||
        fixturenames: Tuple[str, ...],
 | 
					 | 
				
			||||||
        parentnode: nodes.Node,
 | 
					        parentnode: nodes.Node,
 | 
				
			||||||
 | 
					        initialnames: Tuple[str, ...],
 | 
				
			||||||
        ignore_args: AbstractSet[str],
 | 
					        ignore_args: AbstractSet[str],
 | 
				
			||||||
    ) -> Tuple[Tuple[str, ...], List[str], Dict[str, Sequence[FixtureDef[Any]]]]:
 | 
					    ) -> Tuple[List[str], Dict[str, Sequence[FixtureDef[Any]]]]:
 | 
				
			||||||
        # Collect the closure of all fixtures, starting with the given
 | 
					        # Collect the closure of all fixtures, starting with the given
 | 
				
			||||||
        # fixturenames as the initial set.  As we have to visit all
 | 
					        # fixturenames as the initial set.  As we have to visit all
 | 
				
			||||||
        # factory definitions anyway, we also return an arg2fixturedefs
 | 
					        # factory definitions anyway, we also return an arg2fixturedefs
 | 
				
			||||||
| 
						 | 
					@ -1529,19 +1544,7 @@ class FixtureManager:
 | 
				
			||||||
        # (discovering matching fixtures for a given name/node is expensive).
 | 
					        # (discovering matching fixtures for a given name/node is expensive).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        parentid = parentnode.nodeid
 | 
					        parentid = parentnode.nodeid
 | 
				
			||||||
        fixturenames_closure = list(self._getautousenames(parentid))
 | 
					        fixturenames_closure = list(initialnames)
 | 
				
			||||||
 | 
					 | 
				
			||||||
        def merge(otherlist: Iterable[str]) -> None:
 | 
					 | 
				
			||||||
            for arg in otherlist:
 | 
					 | 
				
			||||||
                if arg not in fixturenames_closure:
 | 
					 | 
				
			||||||
                    fixturenames_closure.append(arg)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        merge(fixturenames)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        # At this point, fixturenames_closure contains what we call "initialnames",
 | 
					 | 
				
			||||||
        # which is a set of fixturenames the function immediately requests. We
 | 
					 | 
				
			||||||
        # need to return it as well, so save this.
 | 
					 | 
				
			||||||
        initialnames = tuple(fixturenames_closure)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        arg2fixturedefs: Dict[str, Sequence[FixtureDef[Any]]] = {}
 | 
					        arg2fixturedefs: Dict[str, Sequence[FixtureDef[Any]]] = {}
 | 
				
			||||||
        lastlen = -1
 | 
					        lastlen = -1
 | 
				
			||||||
| 
						 | 
					@ -1555,7 +1558,9 @@ class FixtureManager:
 | 
				
			||||||
                fixturedefs = self.getfixturedefs(argname, parentid)
 | 
					                fixturedefs = self.getfixturedefs(argname, parentid)
 | 
				
			||||||
                if fixturedefs:
 | 
					                if fixturedefs:
 | 
				
			||||||
                    arg2fixturedefs[argname] = fixturedefs
 | 
					                    arg2fixturedefs[argname] = fixturedefs
 | 
				
			||||||
                    merge(fixturedefs[-1].argnames)
 | 
					                    for arg in fixturedefs[-1].argnames:
 | 
				
			||||||
 | 
					                        if arg not in fixturenames_closure:
 | 
				
			||||||
 | 
					                            fixturenames_closure.append(arg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        def sort_by_scope(arg_name: str) -> Scope:
 | 
					        def sort_by_scope(arg_name: str) -> Scope:
 | 
				
			||||||
            try:
 | 
					            try:
 | 
				
			||||||
| 
						 | 
					@ -1566,7 +1571,7 @@ class FixtureManager:
 | 
				
			||||||
                return fixturedefs[-1]._scope
 | 
					                return fixturedefs[-1]._scope
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        fixturenames_closure.sort(key=sort_by_scope, reverse=True)
 | 
					        fixturenames_closure.sort(key=sort_by_scope, reverse=True)
 | 
				
			||||||
        return initialnames, fixturenames_closure, arg2fixturedefs
 | 
					        return fixturenames_closure, arg2fixturedefs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def pytest_generate_tests(self, metafunc: "Metafunc") -> None:
 | 
					    def pytest_generate_tests(self, metafunc: "Metafunc") -> None:
 | 
				
			||||||
        """Generate new tests based on parametrized fixtures used by the given metafunc"""
 | 
					        """Generate new tests based on parametrized fixtures used by the given metafunc"""
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,6 +6,7 @@ from pathlib import Path
 | 
				
			||||||
import pytest
 | 
					import pytest
 | 
				
			||||||
from _pytest.compat import getfuncargnames
 | 
					from _pytest.compat import getfuncargnames
 | 
				
			||||||
from _pytest.config import ExitCode
 | 
					from _pytest.config import ExitCode
 | 
				
			||||||
 | 
					from _pytest.fixtures import deduplicate_names
 | 
				
			||||||
from _pytest.fixtures import TopRequest
 | 
					from _pytest.fixtures import TopRequest
 | 
				
			||||||
from _pytest.monkeypatch import MonkeyPatch
 | 
					from _pytest.monkeypatch import MonkeyPatch
 | 
				
			||||||
from _pytest.pytester import get_public_names
 | 
					from _pytest.pytester import get_public_names
 | 
				
			||||||
| 
						 | 
					@ -4531,3 +4532,10 @@ def test_yield_fixture_with_no_value(pytester: Pytester) -> None:
 | 
				
			||||||
    result.assert_outcomes(errors=1)
 | 
					    result.assert_outcomes(errors=1)
 | 
				
			||||||
    result.stdout.fnmatch_lines([expected])
 | 
					    result.stdout.fnmatch_lines([expected])
 | 
				
			||||||
    assert result.ret == ExitCode.TESTS_FAILED
 | 
					    assert result.ret == ExitCode.TESTS_FAILED
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def test_deduplicate_names() -> None:
 | 
				
			||||||
 | 
					    items = deduplicate_names("abacd")
 | 
				
			||||||
 | 
					    assert items == ("a", "b", "c", "d")
 | 
				
			||||||
 | 
					    items = deduplicate_names(items + ("g", "f", "g", "e", "b"))
 | 
				
			||||||
 | 
					    assert items == ("a", "b", "c", "d", "g", "f", "e")
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue