1"""Run testsuites written for nose."""

2 import warnings

3

4 from _pytest.config import hookimpl

5 from _pytest.deprecated import NOSE_SUPPORT

6 from _pytest.fixtures import getfixturemarker

7 from _pytest.nodes import Item

8 from _pytest.python import Function

9 from _pytest.unittest import TestCaseFunction

10

11

12@hookimpl(trylast=True)

13 def pytest_runtest_setup(item: Item) -> None:

14 if not isinstance(item, Function):

15 return

16 # Don't do nose style setup/teardown on direct unittest style classes.

17 if isinstance(item, TestCaseFunction):

18 return

19

20 # Capture the narrowed type of item for the teardown closure,

21 # see https://github.com/python/mypy/issues/2608

22 func = item

23

24 call_optional(func.obj, "setup", func.nodeid)

25 func.addfinalizer(lambda: call_optional(func.obj, "teardown", func.nodeid))

26

27 # NOTE: Module- and class-level fixtures are handled in python.py

28 # with `pluginmanager.has_plugin("nose")` checks.

29 # It would have been nicer to implement them outside of core, but

30 # it's not straightforward.

31

32

33 def call_optional(obj: object, name: str, nodeid: str) -> bool:

34 method = getattr(obj, name, None)

35 if method is None:

36 return False

37 is_fixture = getfixturemarker(method) is not None

38 if is_fixture:

39 return False

40 if not callable(method):

41 return False

42 # Warn about deprecation of this plugin.

43 method_name = getattr(method, "__name__", str(method))

44 warnings.warn(

45 NOSE_SUPPORT.format(nodeid=nodeid, method=method_name, stage=name), stacklevel=2

46 )

47 # If there are any problems allow the exception to raise rather than

48 # silently ignoring it.

49 method()

50 return True