| 393 | |
| 394 | @attr.s(slots=True, auto_attribs=True) |
| 395 | class FuncFixtureInfo: |
| 396 | # Original function argument names. |
| 397 | argnames: Tuple[str, ...] |
| 398 | # Argnames that function immediately requires. These include argnames + |
| 399 | # fixture names specified via usefixtures and via autouse=True in fixture |
| 400 | # definitions. |
| 401 | initialnames: Tuple[str, ...] |
| 402 | names_closure: List[str] |
| 403 | name2fixturedefs: Dict[str, Sequence["FixtureDef[Any]"]] |
| 404 | |
| 405 | def prune_dependency_tree(self) -> None: |
| 406 | """Recompute names_closure from initialnames and name2fixturedefs. |
| 407 | |
| 408 | Can only reduce names_closure, which means that the new closure will |
| 409 | always be a subset of the old one. The order is preserved. |
| 410 | |
| 411 | This method is needed because direct parametrization may shadow some |
| 412 | of the fixtures that were included in the originally built dependency |
| 413 | tree. In this way the dependency tree can get pruned, and the closure |
| 414 | of argnames may get reduced. |
| 415 | """ |
| 416 | closure: Set[str] = set() |
| 417 | working_set = set(self.initialnames) |
| 418 | while working_set: |
| 419 | argname = working_set.pop() |
| 420 | # Argname may be smth not included in the original names_closure, |
| 421 | # in which case we ignore it. This currently happens with pseudo |
| 422 | # FixtureDefs which wrap 'get_direct_param_fixture_func(request)'. |
| 423 | # So they introduce the new dependency 'request' which might have |
| 424 | # been missing in the original tree (closure). |
| 425 | if argname not in closure and argname in self.names_closure: |
| 426 | closure.add(argname) |
| 427 | if argname in self.name2fixturedefs: |
| 428 | working_set.update(self.name2fixturedefs[argname][-1].argnames) |
| 429 | |
| 430 | self.names_closure[:] = sorted(closure, key=self.names_closure.index) |
| 431 | |
| 432 | |
| 433 | class FixtureRequest: |