Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/hypothesis/internal/escalation.py: 40%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

91 statements  

1# This file is part of Hypothesis, which may be found at 

2# https://github.com/HypothesisWorks/hypothesis/ 

3# 

4# Copyright the Hypothesis Authors. 

5# Individual contributors are listed in AUTHORS.rst and the git log. 

6# 

7# This Source Code Form is subject to the terms of the Mozilla Public License, 

8# v. 2.0. If a copy of the MPL was not distributed with this file, You can 

9# obtain one at https://mozilla.org/MPL/2.0/. 

10 

11import contextlib 

12import os 

13import sys 

14import textwrap 

15import traceback 

16from inspect import getframeinfo 

17from pathlib import Path 

18from typing import Dict, NamedTuple, Optional, Type 

19 

20import hypothesis 

21from hypothesis.errors import ( 

22 DeadlineExceeded, 

23 HypothesisException, 

24 StopTest, 

25 UnsatisfiedAssumption, 

26 _Trimmable, 

27) 

28from hypothesis.internal.compat import BaseExceptionGroup 

29from hypothesis.utils.dynamicvariables import DynamicVariable 

30 

31 

32def belongs_to(package): 

33 if not hasattr(package, "__file__"): # pragma: no cover 

34 return lambda filepath: False 

35 

36 root = Path(package.__file__).resolve().parent 

37 cache = {str: {}, bytes: {}} 

38 

39 def accept(filepath): 

40 ftype = type(filepath) 

41 try: 

42 return cache[ftype][filepath] 

43 except KeyError: 

44 pass 

45 try: 

46 Path(filepath).resolve().relative_to(root) 

47 result = True 

48 except Exception: 

49 result = False 

50 cache[ftype][filepath] = result 

51 return result 

52 

53 accept.__name__ = f"is_{package.__name__}_file" 

54 return accept 

55 

56 

57PREVENT_ESCALATION = os.getenv("HYPOTHESIS_DO_NOT_ESCALATE") == "true" 

58 

59FILE_CACHE: Dict[bytes, bool] = {} 

60 

61 

62is_hypothesis_file = belongs_to(hypothesis) 

63 

64HYPOTHESIS_CONTROL_EXCEPTIONS = (DeadlineExceeded, StopTest, UnsatisfiedAssumption) 

65 

66 

67def escalate_hypothesis_internal_error(): 

68 if PREVENT_ESCALATION: 

69 return 

70 

71 _, e, tb = sys.exc_info() 

72 

73 if getattr(e, "hypothesis_internal_never_escalate", False): 

74 return 

75 

76 filepath = None if tb is None else traceback.extract_tb(tb)[-1][0] 

77 if is_hypothesis_file(filepath) and not isinstance( 

78 e, (HypothesisException, *HYPOTHESIS_CONTROL_EXCEPTIONS) 

79 ): 

80 raise 

81 

82 

83def get_trimmed_traceback(exception=None): 

84 """Return the current traceback, minus any frames added by Hypothesis.""" 

85 if exception is None: 

86 _, exception, tb = sys.exc_info() 

87 else: 

88 tb = exception.__traceback__ 

89 # Avoid trimming the traceback if we're in verbose mode, or the error 

90 # was raised inside Hypothesis. Additionally, the environment variable 

91 # HYPOTHESIS_NO_TRACEBACK_TRIM is respected if nonempty, because verbose 

92 # mode is prohibitively slow when debugging strategy recursion errors. 

93 if ( 

94 tb is None 

95 or os.environ.get("HYPOTHESIS_NO_TRACEBACK_TRIM", None) 

96 or hypothesis.settings.default.verbosity >= hypothesis.Verbosity.debug 

97 or is_hypothesis_file(traceback.extract_tb(tb)[-1][0]) 

98 and not isinstance(exception, _Trimmable) 

99 ): 

100 return tb 

101 while tb.tb_next is not None and ( 

102 # If the frame is from one of our files, it's been added by Hypothesis. 

103 is_hypothesis_file(getframeinfo(tb.tb_frame).filename) 

104 # But our `@proxies` decorator overrides the source location, 

105 # so we check for an attribute it injects into the frame too. 

106 or tb.tb_frame.f_globals.get("__hypothesistracebackhide__") is True 

107 ): 

108 tb = tb.tb_next 

109 return tb 

110 

111 

112class InterestingOrigin(NamedTuple): 

113 # The `interesting_origin` is how Hypothesis distinguishes between multiple 

114 # failures, for reporting and also to replay from the example database (even 

115 # if report_multiple_bugs=False). We traditionally use the exception type and 

116 # location, but have extracted this logic in order to see through `except ...:` 

117 # blocks and understand the __cause__ (`raise x from y`) or __context__ that 

118 # first raised an exception as well as PEP-654 exception groups. 

119 exc_type: Type[BaseException] 

120 filename: Optional[str] 

121 lineno: Optional[int] 

122 context: "InterestingOrigin | tuple[()]" 

123 group_elems: "tuple[InterestingOrigin, ...]" 

124 

125 def __str__(self) -> str: 

126 ctx = "" 

127 if self.context: 

128 ctx = textwrap.indent(f"\ncontext: {self.context}", prefix=" ") 

129 group = "" 

130 if self.group_elems: 

131 chunks = "\n ".join(str(x) for x in self.group_elems) 

132 group = textwrap.indent(f"\nchild exceptions:\n {chunks}", prefix=" ") 

133 return f"{self.exc_type.__name__} at {self.filename}:{self.lineno}{ctx}{group}" 

134 

135 @classmethod 

136 def from_exception(cls, exception: BaseException, /) -> "InterestingOrigin": 

137 filename, lineno = None, None 

138 if tb := get_trimmed_traceback(exception): 

139 filename, lineno, *_ = traceback.extract_tb(tb)[-1] 

140 return cls( 

141 type(exception), 

142 filename, 

143 lineno, 

144 # Note that if __cause__ is set it is always equal to __context__, explicitly 

145 # to support introspection when debugging, so we can use that unconditionally. 

146 cls.from_exception(exception.__context__) if exception.__context__ else (), 

147 # We distinguish exception groups by the inner exceptions, as for __context__ 

148 ( 

149 tuple(map(cls.from_exception, exception.exceptions)) 

150 if isinstance(exception, BaseExceptionGroup) 

151 else () 

152 ), 

153 ) 

154 

155 

156current_pytest_item = DynamicVariable(None) 

157 

158 

159def _get_exceptioninfo(): 

160 # ExceptionInfo was moved to the top-level namespace in Pytest 7.0 

161 if "pytest" in sys.modules: 

162 with contextlib.suppress(Exception): 

163 # From Pytest 7, __init__ warns on direct calls. 

164 return sys.modules["pytest"].ExceptionInfo.from_exc_info 

165 if "_pytest._code" in sys.modules: # old versions only 

166 with contextlib.suppress(Exception): 

167 return sys.modules["_pytest._code"].ExceptionInfo 

168 return None # pragma: no cover # coverage tests always use pytest 

169 

170 

171def format_exception(err, tb): 

172 # Try using Pytest to match the currently configured traceback style 

173 ExceptionInfo = _get_exceptioninfo() 

174 if current_pytest_item.value is not None and ExceptionInfo is not None: 

175 item = current_pytest_item.value 

176 return str(item.repr_failure(ExceptionInfo((type(err), err, tb)))) + "\n" 

177 

178 # Or use better_exceptions, if that's installed and enabled 

179 if "better_exceptions" in sys.modules: 

180 better_exceptions = sys.modules["better_exceptions"] 

181 if sys.excepthook is better_exceptions.excepthook: 

182 return "".join(better_exceptions.format_exception(type(err), err, tb)) 

183 

184 # If all else fails, use the standard-library formatting tools 

185 return "".join(traceback.format_exception(type(err), err, tb))