Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/sentry_sdk/integrations/bottle.py: 10%

111 statements  

« prev     ^ index     » next       coverage.py v6.4.4, created at 2023-07-17 14:22 -0600

1from __future__ import absolute_import 

2 

3from sentry_sdk.hub import Hub 

4from sentry_sdk.tracing import SOURCE_FOR_STYLE 

5from sentry_sdk.utils import ( 

6 capture_internal_exceptions, 

7 event_from_exception, 

8 transaction_from_function, 

9) 

10from sentry_sdk.integrations import Integration, DidNotEnable 

11from sentry_sdk.integrations.wsgi import SentryWsgiMiddleware 

12from sentry_sdk.integrations._wsgi_common import RequestExtractor 

13 

14from sentry_sdk._types import MYPY 

15 

16if MYPY: 16 ↛ 17line 16 didn't jump to line 17, because the condition on line 16 was never true

17 from sentry_sdk.integrations.wsgi import _ScopedResponse 

18 from typing import Any 

19 from typing import Dict 

20 from typing import Callable 

21 from typing import Optional 

22 from bottle import FileUpload, FormsDict, LocalRequest # type: ignore 

23 

24 from sentry_sdk._types import EventProcessor, Event 

25 

26try: 

27 from bottle import ( 

28 Bottle, 

29 Route, 

30 request as bottle_request, 

31 HTTPResponse, 

32 __version__ as BOTTLE_VERSION, 

33 ) 

34except ImportError: 

35 raise DidNotEnable("Bottle not installed") 

36 

37 

38TRANSACTION_STYLE_VALUES = ("endpoint", "url") 

39 

40 

41class BottleIntegration(Integration): 

42 identifier = "bottle" 

43 

44 transaction_style = "" 

45 

46 def __init__(self, transaction_style="endpoint"): 

47 # type: (str) -> None 

48 

49 if transaction_style not in TRANSACTION_STYLE_VALUES: 

50 raise ValueError( 

51 "Invalid value for transaction_style: %s (must be in %s)" 

52 % (transaction_style, TRANSACTION_STYLE_VALUES) 

53 ) 

54 self.transaction_style = transaction_style 

55 

56 @staticmethod 

57 def setup_once(): 

58 # type: () -> None 

59 

60 try: 

61 version = tuple(map(int, BOTTLE_VERSION.replace("-dev", "").split("."))) 

62 except (TypeError, ValueError): 

63 raise DidNotEnable("Unparsable Bottle version: {}".format(version)) 

64 

65 if version < (0, 12): 

66 raise DidNotEnable("Bottle 0.12 or newer required.") 

67 

68 # monkey patch method Bottle.__call__ 

69 old_app = Bottle.__call__ 

70 

71 def sentry_patched_wsgi_app(self, environ, start_response): 

72 # type: (Any, Dict[str, str], Callable[..., Any]) -> _ScopedResponse 

73 

74 hub = Hub.current 

75 integration = hub.get_integration(BottleIntegration) 

76 if integration is None: 

77 return old_app(self, environ, start_response) 

78 

79 return SentryWsgiMiddleware(lambda *a, **kw: old_app(self, *a, **kw))( 

80 environ, start_response 

81 ) 

82 

83 Bottle.__call__ = sentry_patched_wsgi_app 

84 

85 # monkey patch method Bottle._handle 

86 old_handle = Bottle._handle 

87 

88 def _patched_handle(self, environ): 

89 # type: (Bottle, Dict[str, Any]) -> Any 

90 hub = Hub.current 

91 integration = hub.get_integration(BottleIntegration) 

92 if integration is None: 

93 return old_handle(self, environ) 

94 

95 # create new scope 

96 scope_manager = hub.push_scope() 

97 

98 with scope_manager: 

99 app = self 

100 with hub.configure_scope() as scope: 

101 scope._name = "bottle" 

102 scope.add_event_processor( 

103 _make_request_event_processor(app, bottle_request, integration) 

104 ) 

105 res = old_handle(self, environ) 

106 

107 # scope cleanup 

108 return res 

109 

110 Bottle._handle = _patched_handle 

111 

112 # monkey patch method Route._make_callback 

113 old_make_callback = Route._make_callback 

114 

115 def patched_make_callback(self, *args, **kwargs): 

116 # type: (Route, *object, **object) -> Any 

117 hub = Hub.current 

118 integration = hub.get_integration(BottleIntegration) 

119 prepared_callback = old_make_callback(self, *args, **kwargs) 

120 if integration is None: 

121 return prepared_callback 

122 

123 # If an integration is there, a client has to be there. 

124 client = hub.client # type: Any 

125 

126 def wrapped_callback(*args, **kwargs): 

127 # type: (*object, **object) -> Any 

128 

129 try: 

130 res = prepared_callback(*args, **kwargs) 

131 except HTTPResponse: 

132 raise 

133 except Exception as exception: 

134 event, hint = event_from_exception( 

135 exception, 

136 client_options=client.options, 

137 mechanism={"type": "bottle", "handled": False}, 

138 ) 

139 hub.capture_event(event, hint=hint) 

140 raise exception 

141 

142 return res 

143 

144 return wrapped_callback 

145 

146 Route._make_callback = patched_make_callback 

147 

148 

149class BottleRequestExtractor(RequestExtractor): 

150 def env(self): 

151 # type: () -> Dict[str, str] 

152 return self.request.environ 

153 

154 def cookies(self): 

155 # type: () -> Dict[str, str] 

156 return self.request.cookies 

157 

158 def raw_data(self): 

159 # type: () -> bytes 

160 return self.request.body.read() 

161 

162 def form(self): 

163 # type: () -> FormsDict 

164 if self.is_json(): 

165 return None 

166 return self.request.forms.decode() 

167 

168 def files(self): 

169 # type: () -> Optional[Dict[str, str]] 

170 if self.is_json(): 

171 return None 

172 

173 return self.request.files 

174 

175 def size_of_file(self, file): 

176 # type: (FileUpload) -> int 

177 return file.content_length 

178 

179 

180def _set_transaction_name_and_source(event, transaction_style, request): 

181 # type: (Event, str, Any) -> None 

182 name = "" 

183 

184 if transaction_style == "url": 

185 name = request.route.rule or "" 

186 

187 elif transaction_style == "endpoint": 

188 name = ( 

189 request.route.name 

190 or transaction_from_function(request.route.callback) 

191 or "" 

192 ) 

193 

194 event["transaction"] = name 

195 event["transaction_info"] = {"source": SOURCE_FOR_STYLE[transaction_style]} 

196 

197 

198def _make_request_event_processor(app, request, integration): 

199 # type: (Bottle, LocalRequest, BottleIntegration) -> EventProcessor 

200 

201 def event_processor(event, hint): 

202 # type: (Dict[str, Any], Dict[str, Any]) -> Dict[str, Any] 

203 _set_transaction_name_and_source(event, integration.transaction_style, request) 

204 

205 with capture_internal_exceptions(): 

206 BottleRequestExtractor(request).extract_into_event(event) 

207 

208 return event 

209 

210 return event_processor