1 from typing import Generator

2 from typing import Optional

3 from typing import Union

4

5 import pytest

6 from _pytest._io.saferepr import saferepr

7 from _pytest.config import Config

8 from _pytest.config import ExitCode

9 from _pytest.config.argparsing import Parser

10 from _pytest.fixtures import FixtureDef

11 from _pytest.fixtures import SubRequest

12 from _pytest.scope import Scope

13

14

15 def pytest_addoption(parser: Parser) -> None:

16 group = parser.getgroup("debugconfig")

17 group.addoption(

18 "--setuponly",

19 "--setup-only",

20 action="store_true",

21 help="Only setup fixtures, do not execute tests",

22 )

23 group.addoption(

24 "--setupshow",

25 "--setup-show",

26 action="store_true",

27 help="Show setup of fixtures while executing tests",

28 )

29

30

31@pytest.hookimpl(hookwrapper=True)

32 def pytest_fixture_setup(

33 fixturedef: FixtureDef[object], request: SubRequest

34) -> Generator[None, None, None]:

35 yield

36 if request.config.option.setupshow:

37 if hasattr(request, "param"):

38 # Save the fixture parameter so ._show_fixture_action() can

39 # display it now and during the teardown (in .finish()).

40 if fixturedef.ids:

41 if callable(fixturedef.ids):

42 param = fixturedef.ids(request.param)

43 else:

44 param = fixturedef.ids[request.param_index]

45 else:

46 param = request.param

47 fixturedef.cached_param = param # type: ignore[attr-defined]

48 _show_fixture_action(fixturedef, "SETUP")

49

50

51 def pytest_fixture_post_finalizer(fixturedef: FixtureDef[object]) -> None:

52 if fixturedef.cached_result is not None:

53 config = fixturedef._fixturemanager.config

54 if config.option.setupshow:

55 _show_fixture_action(fixturedef, "TEARDOWN")

56 if hasattr(fixturedef, "cached_param"):

57 del fixturedef.cached_param # type: ignore[attr-defined]

58

59

60 def _show_fixture_action(fixturedef: FixtureDef[object], msg: str) -> None:

61 config = fixturedef._fixturemanager.config

62 capman = config.pluginmanager.getplugin("capturemanager")

63 if capman:

64 capman.suspend_global_capture()

65

66 tw = config.get_terminal_writer()

67 tw.line()

68 # Use smaller indentation the higher the scope: Session = 0, Package = 1, etc.

69 scope_indent = list(reversed(Scope)).index(fixturedef._scope)

70 tw.write(" " * 2 * scope_indent)

71 tw.write(

72 "{step} {scope} {fixture}".format(

73 step=msg.ljust(8), # align the output to TEARDOWN

74 scope=fixturedef.scope[0].upper(),

75 fixture=fixturedef.argname,

76 )

77 )

78

79 if msg == "SETUP":

80 deps = sorted(arg for arg in fixturedef.argnames if arg != "request")

81 if deps:

82 tw.write(" (fixtures used: {})".format(", ".join(deps)))

83

84 if hasattr(fixturedef, "cached_param"):

85 tw.write(f"[{saferepr(fixturedef.cached_param, maxsize=42)}]") # type: ignore[attr-defined]

86

87 tw.flush()

88

89 if capman:

90 capman.resume_global_capture()

91

92

93@pytest.hookimpl(tryfirst=True)

94 def pytest_cmdline_main(config: Config) -> Optional[Union[int, ExitCode]]:

95 if config.option.setuponly:

96 config.option.setupshow = True

97 return None