1 import inspect

2 import warnings

3 from types import FunctionType

4 from typing import Any

5 from typing import Generic

6 from typing import Type

7 from typing import TypeVar

8

9 import attr

10

11 from _pytest.compat import final

12

13

14 class PytestWarning(UserWarning):

15 """Base class for all warnings emitted by pytest."""

16

17 __module__ = "pytest"

18

19

20@final

21 class PytestAssertRewriteWarning(PytestWarning):

22 """Warning emitted by the pytest assert rewrite module."""

23

24 __module__ = "pytest"

25

26

27@final

28 class PytestCacheWarning(PytestWarning):

29 """Warning emitted by the cache plugin in various situations."""

30

31 __module__ = "pytest"

32

33

34@final

35 class PytestConfigWarning(PytestWarning):

36 """Warning emitted for configuration issues."""

37

38 __module__ = "pytest"

39

40

41@final

42 class PytestCollectionWarning(PytestWarning):

43 """Warning emitted when pytest is not able to collect a file or symbol in a module."""

44

45 __module__ = "pytest"

46

47

48 class PytestDeprecationWarning(PytestWarning, DeprecationWarning):

49 """Warning class for features that will be removed in a future version."""

50

51 __module__ = "pytest"

52

53

54 class PytestRemovedIn8Warning(PytestDeprecationWarning):

55 """Warning class for features that will be removed in pytest 8."""

56

57 __module__ = "pytest"

58

59

60 class PytestReturnNotNoneWarning(PytestRemovedIn8Warning):

61 """Warning emitted when a test function is returning value other than None."""

62

63 __module__ = "pytest"

64

65

66@final

67 class PytestExperimentalApiWarning(PytestWarning, FutureWarning):

68 """Warning category used to denote experiments in pytest.

69

70 Use sparingly as the API might change or even be removed completely in a

71 future version.

72 """

73

74 __module__ = "pytest"

75

76 @classmethod

77 def simple(cls, apiname: str) -> "PytestExperimentalApiWarning":

78 return cls(

79 "{apiname} is an experimental api that may change over time".format(

80 apiname=apiname

81 )

82 )

83

84

85@final

86 class PytestUnhandledCoroutineWarning(PytestReturnNotNoneWarning):

87 """Warning emitted for an unhandled coroutine.

88

89 A coroutine was encountered when collecting test functions, but was not

90 handled by any async-aware plugin.

91 Coroutine test functions are not natively supported.

92 """

93

94 __module__ = "pytest"

95

96

97@final

98 class PytestUnknownMarkWarning(PytestWarning):

99 """Warning emitted on use of unknown markers.

100

101 See :ref:`mark` for details.

102 """

103

104 __module__ = "pytest"

105

106

107@final

108 class PytestUnraisableExceptionWarning(PytestWarning):

109 """An unraisable exception was reported.

110

111 Unraisable exceptions are exceptions raised in :meth:`__del__ <object.__del__>`

112 implementations and similar situations when the exception cannot be raised

113 as normal.

114 """

115

116 __module__ = "pytest"

117

118

119@final

120 class PytestUnhandledThreadExceptionWarning(PytestWarning):

121 """An unhandled exception occurred in a :class:`~threading.Thread`.

122

123 Such exceptions don't propagate normally.

124 """

125

126 __module__ = "pytest"

127

128

129_W = TypeVar("_W", bound=PytestWarning)

130

131

132@final

133@attr.s(auto_attribs=True)

134 class UnformattedWarning(Generic[_W]):

135 """A warning meant to be formatted during runtime.

136

137 This is used to hold warnings that need to format their message at runtime,

138 as opposed to a direct message.

139 """

140

141 category: Type["_W"]

142 template: str

143

144 def format(self, **kwargs: Any) -> _W:

145 """Return an instance of the warning category, formatted with given kwargs."""

146 return self.category(self.template.format(**kwargs))

147

148

149 def warn_explicit_for(method: FunctionType, message: PytestWarning) -> None:

150 """

151 Issue the warning :param:`message` for the definition of the given :param:`method`

152

153 this helps to log warnigns for functions defined prior to finding an issue with them

154 (like hook wrappers being marked in a legacy mechanism)

155 """

156 lineno = method.__code__.co_firstlineno

157 filename = inspect.getfile(method)

158 module = method.__module__

159 mod_globals = method.__globals__

160 try:

161 warnings.warn_explicit(

162 message,

163 type(message),

164 filename=filename,

165 module=module,

166 registry=mod_globals.setdefault("__warningregistry__", {}),

167 lineno=lineno,

168 )

169 except Warning as w:

170 # If warnings are errors (e.g. -Werror), location information gets lost, so we add it to the message.

171 raise type(w)(f"{w}\n at {filename}:{lineno}") from None