sf-veritas 0.11.10__cp314-cp314-manylinux_2_28_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. sf_veritas/__init__.py +46 -0
  2. sf_veritas/_auto_preload.py +73 -0
  3. sf_veritas/_sfconfig.c +162 -0
  4. sf_veritas/_sfconfig.cpython-314-x86_64-linux-gnu.so +0 -0
  5. sf_veritas/_sfcrashhandler.c +267 -0
  6. sf_veritas/_sfcrashhandler.cpython-314-x86_64-linux-gnu.so +0 -0
  7. sf_veritas/_sffastlog.c +953 -0
  8. sf_veritas/_sffastlog.cpython-314-x86_64-linux-gnu.so +0 -0
  9. sf_veritas/_sffastnet.c +994 -0
  10. sf_veritas/_sffastnet.cpython-314-x86_64-linux-gnu.so +0 -0
  11. sf_veritas/_sffastnetworkrequest.c +727 -0
  12. sf_veritas/_sffastnetworkrequest.cpython-314-x86_64-linux-gnu.so +0 -0
  13. sf_veritas/_sffuncspan.c +2791 -0
  14. sf_veritas/_sffuncspan.cpython-314-x86_64-linux-gnu.so +0 -0
  15. sf_veritas/_sffuncspan_config.c +730 -0
  16. sf_veritas/_sffuncspan_config.cpython-314-x86_64-linux-gnu.so +0 -0
  17. sf_veritas/_sfheadercheck.c +341 -0
  18. sf_veritas/_sfheadercheck.cpython-314-x86_64-linux-gnu.so +0 -0
  19. sf_veritas/_sfnetworkhop.c +1454 -0
  20. sf_veritas/_sfnetworkhop.cpython-314-x86_64-linux-gnu.so +0 -0
  21. sf_veritas/_sfservice.c +1223 -0
  22. sf_veritas/_sfservice.cpython-314-x86_64-linux-gnu.so +0 -0
  23. sf_veritas/_sfteepreload.c +6227 -0
  24. sf_veritas/app_config.py +57 -0
  25. sf_veritas/cli.py +336 -0
  26. sf_veritas/constants.py +10 -0
  27. sf_veritas/custom_excepthook.py +304 -0
  28. sf_veritas/custom_log_handler.py +146 -0
  29. sf_veritas/custom_output_wrapper.py +153 -0
  30. sf_veritas/custom_print.py +153 -0
  31. sf_veritas/django_app.py +5 -0
  32. sf_veritas/env_vars.py +186 -0
  33. sf_veritas/exception_handling_middleware.py +18 -0
  34. sf_veritas/exception_metaclass.py +69 -0
  35. sf_veritas/fast_frame_info.py +116 -0
  36. sf_veritas/fast_network_hop.py +293 -0
  37. sf_veritas/frame_tools.py +112 -0
  38. sf_veritas/funcspan_config_loader.py +693 -0
  39. sf_veritas/function_span_profiler.py +1313 -0
  40. sf_veritas/get_preload_path.py +34 -0
  41. sf_veritas/import_hook.py +62 -0
  42. sf_veritas/infra_details/__init__.py +3 -0
  43. sf_veritas/infra_details/get_infra_details.py +24 -0
  44. sf_veritas/infra_details/kubernetes/__init__.py +3 -0
  45. sf_veritas/infra_details/kubernetes/get_cluster_name.py +147 -0
  46. sf_veritas/infra_details/kubernetes/get_details.py +7 -0
  47. sf_veritas/infra_details/running_on/__init__.py +17 -0
  48. sf_veritas/infra_details/running_on/kubernetes.py +11 -0
  49. sf_veritas/interceptors.py +543 -0
  50. sf_veritas/libsfnettee.so +0 -0
  51. sf_veritas/local_env_detect.py +118 -0
  52. sf_veritas/package_metadata.py +6 -0
  53. sf_veritas/patches/__init__.py +0 -0
  54. sf_veritas/patches/_patch_tracker.py +74 -0
  55. sf_veritas/patches/concurrent_futures.py +19 -0
  56. sf_veritas/patches/constants.py +1 -0
  57. sf_veritas/patches/exceptions.py +82 -0
  58. sf_veritas/patches/multiprocessing.py +32 -0
  59. sf_veritas/patches/network_libraries/__init__.py +99 -0
  60. sf_veritas/patches/network_libraries/aiohttp.py +294 -0
  61. sf_veritas/patches/network_libraries/curl_cffi.py +363 -0
  62. sf_veritas/patches/network_libraries/http_client.py +670 -0
  63. sf_veritas/patches/network_libraries/httpcore.py +580 -0
  64. sf_veritas/patches/network_libraries/httplib2.py +315 -0
  65. sf_veritas/patches/network_libraries/httpx.py +557 -0
  66. sf_veritas/patches/network_libraries/niquests.py +218 -0
  67. sf_veritas/patches/network_libraries/pycurl.py +399 -0
  68. sf_veritas/patches/network_libraries/requests.py +595 -0
  69. sf_veritas/patches/network_libraries/ssl_socket.py +822 -0
  70. sf_veritas/patches/network_libraries/tornado.py +360 -0
  71. sf_veritas/patches/network_libraries/treq.py +270 -0
  72. sf_veritas/patches/network_libraries/urllib_request.py +483 -0
  73. sf_veritas/patches/network_libraries/utils.py +598 -0
  74. sf_veritas/patches/os.py +17 -0
  75. sf_veritas/patches/threading.py +231 -0
  76. sf_veritas/patches/web_frameworks/__init__.py +54 -0
  77. sf_veritas/patches/web_frameworks/aiohttp.py +798 -0
  78. sf_veritas/patches/web_frameworks/async_websocket_consumer.py +337 -0
  79. sf_veritas/patches/web_frameworks/blacksheep.py +532 -0
  80. sf_veritas/patches/web_frameworks/bottle.py +513 -0
  81. sf_veritas/patches/web_frameworks/cherrypy.py +683 -0
  82. sf_veritas/patches/web_frameworks/cors_utils.py +122 -0
  83. sf_veritas/patches/web_frameworks/django.py +963 -0
  84. sf_veritas/patches/web_frameworks/eve.py +401 -0
  85. sf_veritas/patches/web_frameworks/falcon.py +931 -0
  86. sf_veritas/patches/web_frameworks/fastapi.py +738 -0
  87. sf_veritas/patches/web_frameworks/flask.py +526 -0
  88. sf_veritas/patches/web_frameworks/klein.py +501 -0
  89. sf_veritas/patches/web_frameworks/litestar.py +616 -0
  90. sf_veritas/patches/web_frameworks/pyramid.py +440 -0
  91. sf_veritas/patches/web_frameworks/quart.py +841 -0
  92. sf_veritas/patches/web_frameworks/robyn.py +708 -0
  93. sf_veritas/patches/web_frameworks/sanic.py +874 -0
  94. sf_veritas/patches/web_frameworks/starlette.py +742 -0
  95. sf_veritas/patches/web_frameworks/strawberry.py +1446 -0
  96. sf_veritas/patches/web_frameworks/tornado.py +485 -0
  97. sf_veritas/patches/web_frameworks/utils.py +170 -0
  98. sf_veritas/print_override.py +13 -0
  99. sf_veritas/regular_data_transmitter.py +444 -0
  100. sf_veritas/request_interceptor.py +401 -0
  101. sf_veritas/request_utils.py +550 -0
  102. sf_veritas/segfault_handler.py +116 -0
  103. sf_veritas/server_status.py +1 -0
  104. sf_veritas/shutdown_flag.py +11 -0
  105. sf_veritas/subprocess_startup.py +3 -0
  106. sf_veritas/test_cli.py +145 -0
  107. sf_veritas/thread_local.py +1319 -0
  108. sf_veritas/timeutil.py +114 -0
  109. sf_veritas/transmit_exception_to_sailfish.py +28 -0
  110. sf_veritas/transmitter.py +132 -0
  111. sf_veritas/types.py +47 -0
  112. sf_veritas/unified_interceptor.py +1678 -0
  113. sf_veritas/utils.py +39 -0
  114. sf_veritas-0.11.10.dist-info/METADATA +97 -0
  115. sf_veritas-0.11.10.dist-info/RECORD +141 -0
  116. sf_veritas-0.11.10.dist-info/WHEEL +5 -0
  117. sf_veritas-0.11.10.dist-info/entry_points.txt +2 -0
  118. sf_veritas-0.11.10.dist-info/top_level.txt +1 -0
  119. sf_veritas.libs/libbrotlicommon-6ce2a53c.so.1.0.6 +0 -0
  120. sf_veritas.libs/libbrotlidec-811d1be3.so.1.0.6 +0 -0
  121. sf_veritas.libs/libcom_err-730ca923.so.2.1 +0 -0
  122. sf_veritas.libs/libcrypt-52aca757.so.1.1.0 +0 -0
  123. sf_veritas.libs/libcrypto-bdaed0ea.so.1.1.1k +0 -0
  124. sf_veritas.libs/libcurl-eaa3cf66.so.4.5.0 +0 -0
  125. sf_veritas.libs/libgssapi_krb5-323bbd21.so.2.2 +0 -0
  126. sf_veritas.libs/libidn2-2f4a5893.so.0.3.6 +0 -0
  127. sf_veritas.libs/libk5crypto-9a74ff38.so.3.1 +0 -0
  128. sf_veritas.libs/libkeyutils-2777d33d.so.1.6 +0 -0
  129. sf_veritas.libs/libkrb5-a55300e8.so.3.3 +0 -0
  130. sf_veritas.libs/libkrb5support-e6594cfc.so.0.1 +0 -0
  131. sf_veritas.libs/liblber-2-d20824ef.4.so.2.10.9 +0 -0
  132. sf_veritas.libs/libldap-2-cea2a960.4.so.2.10.9 +0 -0
  133. sf_veritas.libs/libnghttp2-39367a22.so.14.17.0 +0 -0
  134. sf_veritas.libs/libpcre2-8-516f4c9d.so.0.7.1 +0 -0
  135. sf_veritas.libs/libpsl-99becdd3.so.5.3.1 +0 -0
  136. sf_veritas.libs/libsasl2-7de4d792.so.3.0.0 +0 -0
  137. sf_veritas.libs/libselinux-d0805dcb.so.1 +0 -0
  138. sf_veritas.libs/libssh-c11d285b.so.4.8.7 +0 -0
  139. sf_veritas.libs/libssl-60250281.so.1.1.1k +0 -0
  140. sf_veritas.libs/libunistring-05abdd40.so.2.1.0 +0 -0
  141. sf_veritas.libs/libuuid-95b83d40.so.1.3.0 +0 -0
@@ -0,0 +1,337 @@
1
+ import inspect
2
+ import os
3
+ import sys
4
+ import sysconfig
5
+ import threading
6
+ from functools import lru_cache, wraps
7
+ from typing import Any, Callable, Optional, Set, Tuple
8
+
9
+ from ... import _sffuncspan
10
+ from ...constants import SAILFISH_TRACING_HEADER
11
+ from ...custom_excepthook import custom_excepthook
12
+ from ...env_vars import (
13
+ PRINT_CONFIGURATION_STATUSES,
14
+ SF_DEBUG,
15
+ SF_NETWORKHOP_CAPTURE_REQUEST_BODY,
16
+ SF_NETWORKHOP_CAPTURE_REQUEST_HEADERS,
17
+ SF_NETWORKHOP_CAPTURE_RESPONSE_BODY,
18
+ SF_NETWORKHOP_CAPTURE_RESPONSE_HEADERS,
19
+ SF_NETWORKHOP_REQUEST_LIMIT_MB,
20
+ SF_NETWORKHOP_RESPONSE_LIMIT_MB,
21
+ )
22
+ from ...thread_local import get_or_set_sf_trace_id
23
+ from ...fast_network_hop import fast_send_network_hop_fast, fast_send_network_hop, register_endpoint
24
+ from .utils import _unwrap_user_func, reinitialize_log_print_capture_for_worker
25
+
26
+ # Size limits in bytes
27
+ _REQUEST_LIMIT_BYTES = SF_NETWORKHOP_REQUEST_LIMIT_MB * 1024 * 1024
28
+ _RESPONSE_LIMIT_BYTES = SF_NETWORKHOP_RESPONSE_LIMIT_MB * 1024 * 1024
29
+
30
+ # Pre-registered endpoint IDs
31
+ _ENDPOINT_REGISTRY: dict[tuple, int] = {}
32
+
33
+ # ────────────────────────────────────────────────────
34
+ # User-code predicate: skip stdlib & site-packages
35
+ # ────────────────────────────────────────────────────
36
+ _STDLIB = sysconfig.get_paths()["stdlib"]
37
+ _SITE_TAGS = ("site-packages", "dist-packages")
38
+ _SKIP_PREFIXES = (_STDLIB, "/usr/local/lib/python", "/usr/lib/python")
39
+
40
+
41
+ @lru_cache(maxsize=512)
42
+ def _is_user_code(path: Optional[str] = None) -> bool:
43
+ """True only for your application files."""
44
+ if not path or path.startswith("<"):
45
+ return False
46
+ for p in _SKIP_PREFIXES:
47
+ if path.startswith(p):
48
+ return False
49
+ return not any(tag in path for tag in _SITE_TAGS)
50
+
51
+
52
+ # ────────────────────────────────────────────────────
53
+ # Patch AsyncConsumer.__call__ to hook connect + receive
54
+ # ────────────────────────────────────────────────────
55
+ def patch_async_consumer_call():
56
+ """
57
+ Wraps AsyncConsumer.__call__ so that for each HTTP or WebSocket
58
+ connection:
59
+ 1) SAILFISH_TRACING_HEADER → ContextVar
60
+ 2) Emit a NetworkHop at first user frame in websocket_connect
61
+ 3) Dynamically wrap websocket_receive to emit a hop on first message
62
+ 4) Forward any exception to custom_excepthook
63
+ """
64
+ try:
65
+ from channels.consumer import AsyncConsumer # type: ignore
66
+
67
+ orig_call = AsyncConsumer.__call__
68
+ except:
69
+ if PRINT_CONFIGURATION_STATUSES:
70
+ print("Channels AsyncConsumer not found; skipping patch", log=False)
71
+ return
72
+
73
+ if PRINT_CONFIGURATION_STATUSES:
74
+ print("Patching AsyncConsumer.__call__ for NetworkHops", log=False)
75
+
76
+ # CRITICAL: Reinitialize log/print capture once per process (Django Channels doesn't have startup hooks)
77
+ # Note: Profiler is already installed by unified_interceptor.py, no need to reinstall here
78
+ _log_capture_initialized_pids = set()
79
+ def _ensure_log_capture():
80
+ current_pid = os.getpid()
81
+ if current_pid not in _log_capture_initialized_pids:
82
+ try:
83
+ # CRITICAL: Reinitialize log/print capture for worker processes
84
+ reinitialize_log_print_capture_for_worker()
85
+ _log_capture_initialized_pids.add(current_pid)
86
+ except Exception as e:
87
+ if SF_DEBUG:
88
+ print(f"[FuncSpanDebug] [Django Channels] Worker PID={current_pid} failed to reinitialize log capture: {e}", log=False)
89
+
90
+ @wraps(orig_call)
91
+ async def custom_call(self, scope, receive, send):
92
+ # Ensure log capture is initialized (once per worker process)
93
+ _ensure_log_capture()
94
+
95
+ # — Propagate header into ContextVar —
96
+ header_val = None
97
+ if scope["type"] in ("http", "websocket"):
98
+ for name, val in scope.get("headers", []):
99
+ if name.lower() == SAILFISH_TRACING_HEADER.lower().encode():
100
+ header_val = val.decode("utf-8")
101
+ break
102
+ get_or_set_sf_trace_id(header_val, is_associated_with_inbound_request=True)
103
+
104
+ # — Capture request headers if enabled —
105
+ req_headers = None
106
+ if SF_NETWORKHOP_CAPTURE_REQUEST_HEADERS:
107
+ try:
108
+ headers = scope.get("headers", [])
109
+ req_headers = {
110
+ name.decode("utf-8"): val.decode("utf-8")
111
+ for name, val in headers
112
+ }
113
+ if SF_DEBUG:
114
+ print(f"[[async_websocket_consumer]] Captured request headers: {len(req_headers)} headers", log=False)
115
+ except Exception as e:
116
+ if SF_DEBUG:
117
+ print(f"[[async_websocket_consumer]] Failed to capture request headers: {e}", log=False)
118
+
119
+ # — Capture request body if enabled (for HTTP, not WebSocket) —
120
+ req_body = None
121
+ body_parts = []
122
+ body_size = 0
123
+ if SF_NETWORKHOP_CAPTURE_REQUEST_BODY and scope["type"] == "http":
124
+ try:
125
+ # Save original receive before wrapping
126
+ original_receive = receive
127
+
128
+ async def receive_with_body():
129
+ nonlocal body_size
130
+ message = await original_receive()
131
+ if message["type"] == "http.request":
132
+ body_part = message.get("body", b"")
133
+ if body_part and body_size < _REQUEST_LIMIT_BYTES:
134
+ remaining = _REQUEST_LIMIT_BYTES - body_size
135
+ body_parts.append(body_part[:remaining])
136
+ body_size += len(body_part)
137
+ return message
138
+
139
+ receive = receive_with_body
140
+ except Exception as e:
141
+ if SF_DEBUG:
142
+ print(f"[[async_websocket_consumer]] Failed to setup request body capture: {e}", log=False)
143
+
144
+ # OTEL-STYLE: Capture endpoint info and pre-register
145
+ endpoint_id = None
146
+ endpoint_metadata = {} # Store metadata for fallback
147
+
148
+ def tracer(frame, event, _arg):
149
+ nonlocal endpoint_id
150
+ if event == "call":
151
+ fn_path = frame.f_code.co_filename
152
+ fn_name = frame.f_code.co_name
153
+
154
+ if SF_DEBUG:
155
+ print(
156
+ f"[[async_websocket_consumer]] Tracer saw: {fn_name} @ {fn_path}:{frame.f_lineno} "
157
+ f"is_user_code={_is_user_code(fn_path)}",
158
+ log=False,
159
+ )
160
+
161
+ if _is_user_code(fn_path):
162
+ hop_key = (fn_path, frame.f_lineno)
163
+
164
+ # Store metadata for potential fallback
165
+ endpoint_metadata["filename"] = fn_path
166
+ endpoint_metadata["line"] = frame.f_lineno
167
+ endpoint_metadata["name"] = fn_name
168
+
169
+ # Pre-register endpoint if not already registered
170
+ endpoint_id = _ENDPOINT_REGISTRY.get(hop_key)
171
+ if endpoint_id is None:
172
+ if SF_DEBUG:
173
+ # Debug: Check if fast network hop is available
174
+ from ...fast_network_hop import _NETWORKHOP_FAST_OK, _FAST_NETWORKHOP_READY
175
+ print(
176
+ f"[[async_websocket_consumer]] Before register: _NETWORKHOP_FAST_OK={_NETWORKHOP_FAST_OK}, "
177
+ f"_FAST_NETWORKHOP_READY={_FAST_NETWORKHOP_READY}",
178
+ log=False,
179
+ )
180
+
181
+ endpoint_id = register_endpoint(
182
+ line=str(frame.f_lineno),
183
+ column="0",
184
+ name=fn_name,
185
+ entrypoint=fn_path,
186
+ route=None
187
+ )
188
+ if SF_DEBUG:
189
+ print(
190
+ f"[[async_websocket_consumer]] register_endpoint returned: {endpoint_id}",
191
+ log=False,
192
+ )
193
+
194
+ if endpoint_id >= 0:
195
+ _ENDPOINT_REGISTRY[hop_key] = endpoint_id
196
+ if SF_DEBUG:
197
+ print(
198
+ f"[[async_websocket_consumer]] Registered endpoint: {fn_name} @ "
199
+ f"{fn_path}:{frame.f_lineno} (id={endpoint_id})",
200
+ log=False,
201
+ )
202
+
203
+ if SF_DEBUG:
204
+ print(
205
+ f"[[async_websocket_consumer]] Captured endpoint: {fn_name} "
206
+ f"({fn_path}:{frame.f_lineno}) endpoint_id={endpoint_id}",
207
+ log=False,
208
+ )
209
+
210
+ sys.setprofile(None)
211
+ return None
212
+ return tracer
213
+
214
+ sys.setprofile(tracer)
215
+
216
+ # Track if we've emitted for this connection
217
+ hop_emitted = False
218
+
219
+ # Emit NetworkHop function - called when websocket_connect completes
220
+ def emit_network_hop_sync():
221
+ """Emit NetworkHop after websocket_connect handler runs."""
222
+ nonlocal hop_emitted
223
+ if hop_emitted:
224
+ return
225
+ hop_emitted = True
226
+
227
+ if endpoint_id is not None and endpoint_id >= 0:
228
+ # Fast path with C extension
229
+ try:
230
+ _, session_id = get_or_set_sf_trace_id()
231
+
232
+ # Extract raw path and query string from scope for C to parse
233
+ raw_path = scope.get("path", None) # e.g., "/ws/chat"
234
+ raw_query = scope.get("query_string", b"") # e.g., b"room=123"
235
+
236
+ fast_send_network_hop_fast(
237
+ session_id=session_id,
238
+ endpoint_id=endpoint_id,
239
+ raw_path=raw_path,
240
+ raw_query_string=raw_query,
241
+ request_headers=req_headers,
242
+ request_body=None, # WebSocket has no body
243
+ response_headers=None,
244
+ response_body=None,
245
+ )
246
+ if SF_DEBUG:
247
+ print(
248
+ f"[[async_websocket_consumer]] Emitted network hop (fast path): endpoint_id={endpoint_id}",
249
+ log=False,
250
+ )
251
+ except Exception as e:
252
+ if SF_DEBUG:
253
+ print(f"[[async_websocket_consumer]] Failed to emit (fast path): {e}", log=False)
254
+ elif endpoint_metadata:
255
+ # Fallback path without C extension
256
+ try:
257
+ _, session_id = get_or_set_sf_trace_id()
258
+ if SF_DEBUG:
259
+ print(
260
+ f"[[async_websocket_consumer]] Using fallback: {endpoint_metadata['name']} @ "
261
+ f"{endpoint_metadata['filename']}:{endpoint_metadata['line']}",
262
+ log=False,
263
+ )
264
+ fast_send_network_hop(
265
+ session_id=session_id,
266
+ line=str(endpoint_metadata["line"]),
267
+ column="0",
268
+ name=endpoint_metadata["name"],
269
+ entrypoint=endpoint_metadata["filename"],
270
+ )
271
+ if SF_DEBUG:
272
+ print(
273
+ f"[[async_websocket_consumer]] Emitted network hop (fallback): "
274
+ f"{endpoint_metadata['name']}",
275
+ log=False,
276
+ )
277
+ except Exception as e:
278
+ if SF_DEBUG:
279
+ print(f"[[async_websocket_consumer]] Failed to emit (fallback): {e}", log=False)
280
+
281
+ # Capture response headers and body
282
+ resp_headers = None
283
+ resp_body_parts = []
284
+ resp_body_size = 0
285
+
286
+ # Wrap send to capture response data AND emit after websocket.accept
287
+ async def wrapped_send(message):
288
+ nonlocal resp_headers, resp_body_size
289
+
290
+ # Emit NetworkHop after WebSocket is accepted
291
+ if message["type"] == "websocket.accept":
292
+ if SF_DEBUG:
293
+ print(f"[[async_websocket_consumer]] WebSocket accepted, emitting NetworkHop", log=False)
294
+ emit_network_hop_sync()
295
+
296
+ # Capture response headers
297
+ if message["type"] == "http.response.start" and SF_NETWORKHOP_CAPTURE_RESPONSE_HEADERS:
298
+ try:
299
+ headers = message.get("headers", [])
300
+ resp_headers = {
301
+ name.decode("utf-8"): val.decode("utf-8")
302
+ for name, val in headers
303
+ }
304
+ if SF_DEBUG:
305
+ print(f"[[async_websocket_consumer]] Captured response headers: {len(resp_headers)} headers", log=False)
306
+ except Exception as e:
307
+ if SF_DEBUG:
308
+ print(f"[[async_websocket_consumer]] Failed to capture response headers: {e}", log=False)
309
+
310
+ # Capture response body
311
+ if message["type"] == "http.response.body" and SF_NETWORKHOP_CAPTURE_RESPONSE_BODY:
312
+ try:
313
+ body_part = message.get("body", b"")
314
+ if body_part and resp_body_size < _RESPONSE_LIMIT_BYTES:
315
+ remaining = _RESPONSE_LIMIT_BYTES - resp_body_size
316
+ resp_body_parts.append(body_part[:remaining])
317
+ resp_body_size += len(body_part)
318
+ except Exception as e:
319
+ if SF_DEBUG:
320
+ print(f"[[async_websocket_consumer]] Failed to capture response body chunk: {e}", log=False)
321
+
322
+ await send(message)
323
+
324
+ # — Call through to original (handles connect, receive, disconnect) —
325
+ try:
326
+ await orig_call(self, scope, receive, wrapped_send)
327
+ except Exception as exc:
328
+ custom_excepthook(type(exc), exc, exc.__traceback__)
329
+ raise
330
+ finally:
331
+ sys.setprofile(None)
332
+
333
+ # Apply the patch
334
+ AsyncConsumer.__call__ = custom_call
335
+
336
+ if PRINT_CONFIGURATION_STATUSES:
337
+ print("AsyncConsumer.__call__ patched successfully", log=False)