Coverage for /opt/homebrew/lib/python3.11/site-packages/_pytest/_io/saferepr.py: 24%

82 statements  

« prev     ^ index     » next       coverage.py v7.2.3, created at 2023-05-04 13:14 +0700

1import pprint 

2import reprlib 

3from typing import Any 

4from typing import Dict 

5from typing import IO 

6from typing import Optional 

7 

8 

9def _try_repr_or_str(obj: object) -> str: 

10 try: 

11 return repr(obj) 

12 except (KeyboardInterrupt, SystemExit): 

13 raise 

14 except BaseException: 

15 return f'{type(obj).__name__}("{obj}")' 

16 

17 

18def _format_repr_exception(exc: BaseException, obj: object) -> str: 

19 try: 

20 exc_info = _try_repr_or_str(exc) 

21 except (KeyboardInterrupt, SystemExit): 

22 raise 

23 except BaseException as exc: 

24 exc_info = f"unpresentable exception ({_try_repr_or_str(exc)})" 

25 return "<[{} raised in repr()] {} object at 0x{:x}>".format( 

26 exc_info, type(obj).__name__, id(obj) 

27 ) 

28 

29 

30def _ellipsize(s: str, maxsize: int) -> str: 

31 if len(s) > maxsize: 

32 i = max(0, (maxsize - 3) // 2) 

33 j = max(0, maxsize - 3 - i) 

34 return s[:i] + "..." + s[len(s) - j :] 

35 return s 

36 

37 

38class SafeRepr(reprlib.Repr): 

39 """ 

40 repr.Repr that limits the resulting size of repr() and includes 

41 information on exceptions raised during the call. 

42 """ 

43 

44 def __init__(self, maxsize: Optional[int], use_ascii: bool = False) -> None: 

45 """ 

46 :param maxsize: 

47 If not None, will truncate the resulting repr to that specific size, using ellipsis 

48 somewhere in the middle to hide the extra text. 

49 If None, will not impose any size limits on the returning repr. 

50 """ 

51 super().__init__() 

52 # ``maxstring`` is used by the superclass, and needs to be an int; using a 

53 # very large number in case maxsize is None, meaning we want to disable 

54 # truncation. 

55 self.maxstring = maxsize if maxsize is not None else 1_000_000_000 

56 self.maxsize = maxsize 

57 self.use_ascii = use_ascii 

58 

59 def repr(self, x: object) -> str: 

60 try: 

61 if self.use_ascii: 

62 s = ascii(x) 

63 else: 

64 s = super().repr(x) 

65 

66 except (KeyboardInterrupt, SystemExit): 

67 raise 

68 except BaseException as exc: 

69 s = _format_repr_exception(exc, x) 

70 if self.maxsize is not None: 

71 s = _ellipsize(s, self.maxsize) 

72 return s 

73 

74 def repr_instance(self, x: object, level: int) -> str: 

75 try: 

76 s = repr(x) 

77 except (KeyboardInterrupt, SystemExit): 

78 raise 

79 except BaseException as exc: 

80 s = _format_repr_exception(exc, x) 

81 if self.maxsize is not None: 

82 s = _ellipsize(s, self.maxsize) 

83 return s 

84 

85 

86def safeformat(obj: object) -> str: 

87 """Return a pretty printed string for the given object. 

88 

89 Failing __repr__ functions of user instances will be represented 

90 with a short exception info. 

91 """ 

92 try: 

93 return pprint.pformat(obj) 

94 except Exception as exc: 

95 return _format_repr_exception(exc, obj) 

96 

97 

98# Maximum size of overall repr of objects to display during assertion errors. 

99DEFAULT_REPR_MAX_SIZE = 240 

100 

101 

102def saferepr( 

103 obj: object, maxsize: Optional[int] = DEFAULT_REPR_MAX_SIZE, use_ascii: bool = False 

104) -> str: 

105 """Return a size-limited safe repr-string for the given object. 

106 

107 Failing __repr__ functions of user instances will be represented 

108 with a short exception info and 'saferepr' generally takes 

109 care to never raise exceptions itself. 

110 

111 This function is a wrapper around the Repr/reprlib functionality of the 

112 stdlib. 

113 """ 

114 

115 return SafeRepr(maxsize, use_ascii).repr(obj) 

116 

117 

118def saferepr_unlimited(obj: object, use_ascii: bool = True) -> str: 

119 """Return an unlimited-size safe repr-string for the given object. 

120 

121 As with saferepr, failing __repr__ functions of user instances 

122 will be represented with a short exception info. 

123 

124 This function is a wrapper around simple repr. 

125 

126 Note: a cleaner solution would be to alter ``saferepr``this way 

127 when maxsize=None, but that might affect some other code. 

128 """ 

129 try: 

130 if use_ascii: 

131 return ascii(obj) 

132 return repr(obj) 

133 except Exception as exc: 

134 return _format_repr_exception(exc, obj) 

135 

136 

137class AlwaysDispatchingPrettyPrinter(pprint.PrettyPrinter): 

138 """PrettyPrinter that always dispatches (regardless of width).""" 

139 

140 def _format( 

141 self, 

142 object: object, 

143 stream: IO[str], 

144 indent: int, 

145 allowance: int, 

146 context: Dict[int, Any], 

147 level: int, 

148 ) -> None: 

149 # Type ignored because _dispatch is private. 

150 p = self._dispatch.get(type(object).__repr__, None) # type: ignore[attr-defined] 

151 

152 objid = id(object) 

153 if objid in context or p is None: 

154 # Type ignored because _format is private. 

155 super()._format( # type: ignore[misc] 

156 object, 

157 stream, 

158 indent, 

159 allowance, 

160 context, 

161 level, 

162 ) 

163 return 

164 

165 context[objid] = 1 

166 p(self, object, stream, indent, allowance, context, level + 1) 

167 del context[objid] 

168 

169 

170def _pformat_dispatch( 

171 object: object, 

172 indent: int = 1, 

173 width: int = 80, 

174 depth: Optional[int] = None, 

175 *, 

176 compact: bool = False, 

177) -> str: 

178 return AlwaysDispatchingPrettyPrinter( 

179 indent=indent, width=width, depth=depth, compact=compact 

180 ).pformat(object)