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

67 statements  

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

1""" 

2Instrumentation for Django 3.0 

3 

4Since this file contains `async def` it is conditionally imported in 

5`sentry_sdk.integrations.django` (depending on the existence of 

6`django.core.handlers.asgi`. 

7""" 

8 

9import asyncio 

10 

11from sentry_sdk import Hub, _functools 

12from sentry_sdk._types import MYPY 

13 

14from sentry_sdk.integrations.asgi import SentryAsgiMiddleware 

15 

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

17 from typing import Any 

18 from typing import Union 

19 from typing import Callable 

20 

21 from django.http.response import HttpResponse 

22 

23 

24def patch_django_asgi_handler_impl(cls): 

25 # type: (Any) -> None 

26 

27 from sentry_sdk.integrations.django import DjangoIntegration 

28 

29 old_app = cls.__call__ 

30 

31 async def sentry_patched_asgi_handler(self, scope, receive, send): 

32 # type: (Any, Any, Any, Any) -> Any 

33 if Hub.current.get_integration(DjangoIntegration) is None: 

34 return await old_app(self, scope, receive, send) 

35 

36 middleware = SentryAsgiMiddleware( 

37 old_app.__get__(self, cls), unsafe_context_data=True 

38 )._run_asgi3 

39 return await middleware(scope, receive, send) 

40 

41 cls.__call__ = sentry_patched_asgi_handler 

42 

43 

44def patch_get_response_async(cls, _before_get_response): 

45 # type: (Any, Any) -> None 

46 old_get_response_async = cls.get_response_async 

47 

48 async def sentry_patched_get_response_async(self, request): 

49 # type: (Any, Any) -> Union[HttpResponse, BaseException] 

50 _before_get_response(request) 

51 return await old_get_response_async(self, request) 

52 

53 cls.get_response_async = sentry_patched_get_response_async 

54 

55 

56def patch_channels_asgi_handler_impl(cls): 

57 # type: (Any) -> None 

58 

59 import channels # type: ignore 

60 from sentry_sdk.integrations.django import DjangoIntegration 

61 

62 if channels.__version__ < "3.0.0": 

63 

64 old_app = cls.__call__ 

65 

66 async def sentry_patched_asgi_handler(self, receive, send): 

67 # type: (Any, Any, Any) -> Any 

68 if Hub.current.get_integration(DjangoIntegration) is None: 

69 return await old_app(self, receive, send) 

70 

71 middleware = SentryAsgiMiddleware( 

72 lambda _scope: old_app.__get__(self, cls), unsafe_context_data=True 

73 ) 

74 

75 return await middleware(self.scope)(receive, send) 

76 

77 cls.__call__ = sentry_patched_asgi_handler 

78 

79 else: 

80 # The ASGI handler in Channels >= 3 has the same signature as 

81 # the Django handler. 

82 patch_django_asgi_handler_impl(cls) 

83 

84 

85def wrap_async_view(hub, callback): 

86 # type: (Hub, Any) -> Any 

87 @_functools.wraps(callback) 

88 async def sentry_wrapped_callback(request, *args, **kwargs): 

89 # type: (Any, *Any, **Any) -> Any 

90 

91 with hub.start_span( 

92 op="django.view", description=request.resolver_match.view_name 

93 ): 

94 return await callback(request, *args, **kwargs) 

95 

96 return sentry_wrapped_callback 

97 

98 

99def _asgi_middleware_mixin_factory(_check_middleware_span): 

100 # type: (Callable[..., Any]) -> Any 

101 """ 

102 Mixin class factory that generates a middleware mixin for handling requests 

103 in async mode. 

104 """ 

105 

106 class SentryASGIMixin: 

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

108 _inner = None 

109 

110 def __init__(self, get_response): 

111 # type: (Callable[..., Any]) -> None 

112 self.get_response = get_response 

113 self._acall_method = None 

114 self._async_check() 

115 

116 def _async_check(self): 

117 # type: () -> None 

118 """ 

119 If get_response is a coroutine function, turns us into async mode so 

120 a thread is not consumed during a whole request. 

121 Taken from django.utils.deprecation::MiddlewareMixin._async_check 

122 """ 

123 if asyncio.iscoroutinefunction(self.get_response): 123 ↛ 124line 123 didn't jump to line 124, because the condition on line 123 was never true

124 self._is_coroutine = asyncio.coroutines._is_coroutine # type: ignore 

125 

126 def async_route_check(self): 

127 # type: () -> bool 

128 """ 

129 Function that checks if we are in async mode, 

130 and if we are forwards the handling of requests to __acall__ 

131 """ 

132 return asyncio.iscoroutinefunction(self.get_response) 

133 

134 async def __acall__(self, *args, **kwargs): 

135 # type: (*Any, **Any) -> Any 

136 f = self._acall_method 

137 if f is None: 

138 if hasattr(self._inner, "__acall__"): 

139 self._acall_method = f = self._inner.__acall__ # type: ignore 

140 else: 

141 self._acall_method = f = self._inner 

142 

143 middleware_span = _check_middleware_span(old_method=f) 

144 

145 if middleware_span is None: 

146 return await f(*args, **kwargs) 

147 

148 with middleware_span: 

149 return await f(*args, **kwargs) 

150 

151 return SentryASGIMixin