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,293 @@
1
+ """
2
+ Ultra-fast network hop sender using C extension with endpoint pre-registration.
3
+ """
4
+
5
+ import os
6
+ from logging import getLogger
7
+ from typing import Optional, Tuple
8
+
9
+ from .regular_data_transmitter import NetworkHopsTransmitter
10
+ from .thread_local import is_network_recording_suppressed
11
+
12
+ logger = getLogger(__name__)
13
+
14
+ # Optional native fast path for network hops (C extension)
15
+ # LAZY IMPORT: Don't import at module level to avoid circular import
16
+ # Import will happen in _ensure_fast_networkhop_initialized() when first needed
17
+ _sfnetworkhop = None
18
+ _NETWORKHOP_FAST_OK = None # None = not yet attempted, True = success, False = failed
19
+
20
+ _FAST_NETWORKHOP_READY = False # one-time guard for native networkhop init
21
+
22
+ # GraphQL mutation string for network hops (with optional body/header capture)
23
+ _COLLECT_NETWORKHOP_MUTATION = (
24
+ "mutation collectNetworkHops("
25
+ "$apiKey: String!,"
26
+ "$sessionId: String!,"
27
+ "$timestampMs: String!,"
28
+ "$line: String!,"
29
+ "$column: String!,"
30
+ "$name: String!,"
31
+ "$entrypoint: String!,"
32
+ "$route: String,"
33
+ "$queryParams: String,"
34
+ "$serviceUuid: String,"
35
+ "$requestHeaders: String,"
36
+ "$requestBody: String,"
37
+ "$responseHeaders: String,"
38
+ "$responseBody: String"
39
+ "){collectNetworkHops("
40
+ "apiKey:$apiKey,sessionId:$sessionId,timestampMs:$timestampMs,"
41
+ "line:$line,column:$column,name:$name,entrypoint:$entrypoint,"
42
+ "route:$route,queryParams:$queryParams,serviceUuid:$serviceUuid,requestHeaders:$requestHeaders,"
43
+ "requestBody:$requestBody,responseHeaders:$responseHeaders,"
44
+ "responseBody:$responseBody)}"
45
+ )
46
+
47
+
48
+ def _ensure_fast_networkhop_initialized() -> bool:
49
+ global _FAST_NETWORKHOP_READY, _sfnetworkhop, _NETWORKHOP_FAST_OK
50
+
51
+ SF_DEBUG = os.getenv("SF_DEBUG", "false").lower() == "true"
52
+
53
+ # PERFORMANCE: Skip network hop extension when testing network library only
54
+ if os.getenv("TESTING_NETWORK_LIBRARY_ONLY", "0") == "1":
55
+ if SF_DEBUG:
56
+ print(
57
+ "[[_ensure_fast_networkhop_initialized]] Network hop extension disabled (TESTING_NETWORK_LIBRARY_ONLY=1)",
58
+ log=False,
59
+ )
60
+ return False
61
+
62
+ # LAZY IMPORT: Try to import C extension on first use
63
+ if _NETWORKHOP_FAST_OK is None:
64
+ try:
65
+ from . import _sfnetworkhop as _sfnh_module
66
+
67
+ _sfnetworkhop = _sfnh_module
68
+ _NETWORKHOP_FAST_OK = True
69
+ if SF_DEBUG:
70
+ print(
71
+ f"[[_ensure_fast_networkhop_initialized]] Successfully imported _sfnetworkhop C extension",
72
+ log=False,
73
+ )
74
+ except Exception as e:
75
+ if SF_DEBUG:
76
+ print(
77
+ f"[[_ensure_fast_networkhop_initialized]] Failed to import _sfnetworkhop C extension: {e}",
78
+ log=False,
79
+ )
80
+ _sfnetworkhop = None
81
+ _NETWORKHOP_FAST_OK = False
82
+
83
+ if not _NETWORKHOP_FAST_OK:
84
+ if SF_DEBUG:
85
+ print(
86
+ f"[[_ensure_fast_networkhop_initialized]] C extension not available (_NETWORKHOP_FAST_OK=False)",
87
+ log=False,
88
+ )
89
+ return False
90
+
91
+ if _FAST_NETWORKHOP_READY:
92
+ if SF_DEBUG:
93
+ print(
94
+ f"[[_ensure_fast_networkhop_initialized]] Already initialized, returning True",
95
+ log=False,
96
+ )
97
+ return True
98
+
99
+ from . import app_config
100
+
101
+ endpoint = getattr(app_config, "_sailfish_graphql_endpoint", None)
102
+ api_key = getattr(app_config, "_sailfish_api_key", None)
103
+ service_uuid = getattr(app_config, "_service_uuid", None)
104
+ http2 = 1 if os.getenv("SF_NBPOST_HTTP2", "0") == "1" else 0
105
+
106
+ if SF_DEBUG:
107
+ print(
108
+ f"[[_ensure_fast_networkhop_initialized]] Config: endpoint={bool(endpoint)}, api_key={bool(api_key)}, service_uuid={bool(service_uuid)}",
109
+ log=False,
110
+ )
111
+
112
+ if not (endpoint and api_key and service_uuid):
113
+ if SF_DEBUG:
114
+ print(
115
+ f"[[_ensure_fast_networkhop_initialized]] Missing required config, returning False",
116
+ log=False,
117
+ )
118
+ return False
119
+
120
+ try:
121
+ if SF_DEBUG:
122
+ print(
123
+ f"[[_ensure_fast_networkhop_initialized]] Calling _sfnetworkhop.init() with url={endpoint}",
124
+ log=False,
125
+ )
126
+ ok = _sfnetworkhop.init(
127
+ url=endpoint,
128
+ query=_COLLECT_NETWORKHOP_MUTATION,
129
+ api_key=str(api_key),
130
+ service_uuid=str(service_uuid),
131
+ http2=http2,
132
+ )
133
+ _FAST_NETWORKHOP_READY = bool(ok)
134
+ if SF_DEBUG:
135
+ print(
136
+ f"[[_ensure_fast_networkhop_initialized]] _sfnetworkhop.init() returned {ok}, _FAST_NETWORKHOP_READY={_FAST_NETWORKHOP_READY}",
137
+ log=False,
138
+ )
139
+ except Exception as e:
140
+ if SF_DEBUG:
141
+ print(
142
+ f"[[_ensure_fast_networkhop_initialized]] _sfnetworkhop.init() raised exception: {e}",
143
+ log=False,
144
+ )
145
+ _FAST_NETWORKHOP_READY = False
146
+
147
+ return _FAST_NETWORKHOP_READY
148
+
149
+
150
+ def register_endpoint(
151
+ line: str, column: str, name: str, entrypoint: str, route: str = None
152
+ ) -> int:
153
+ """
154
+ Register a web framework endpoint's invariant fields once.
155
+ Returns an endpoint_id usable with fast_send_network_hop_fast(...).
156
+
157
+ Args:
158
+ line: Line number where the endpoint is defined
159
+ column: Column number (typically "0")
160
+ name: Function name of the endpoint
161
+ entrypoint: File path where the endpoint is defined
162
+ route: Route pattern (e.g., "/api/users/{id}") - optional
163
+ """
164
+ SF_DEBUG = os.getenv("SF_DEBUG", "false").lower() == "true"
165
+
166
+ if SF_DEBUG:
167
+ print(
168
+ f"[[register_endpoint]] Called for {name} @ {entrypoint}:{line} (route={route})",
169
+ log=False,
170
+ )
171
+
172
+ if not _ensure_fast_networkhop_initialized():
173
+ if SF_DEBUG:
174
+ print(
175
+ f"[[register_endpoint]] _ensure_fast_networkhop_initialized() returned False, returning -1",
176
+ log=False,
177
+ )
178
+ return -1
179
+ try:
180
+ eid = _sfnetworkhop.register_endpoint(
181
+ line=line, column=column, name=name, entrypoint=entrypoint, route=route
182
+ )
183
+ if SF_DEBUG:
184
+ print(
185
+ f"[[register_endpoint]] _sfnetworkhop.register_endpoint() returned {eid}",
186
+ log=False,
187
+ )
188
+ return int(eid)
189
+ except Exception as e:
190
+ if SF_DEBUG:
191
+ print(
192
+ f"[[register_endpoint]] _sfnetworkhop.register_endpoint() raised exception: {e}",
193
+ log=False,
194
+ )
195
+ return -1
196
+
197
+
198
+ def fast_send_network_hop_fast(
199
+ session_id: str,
200
+ endpoint_id: int,
201
+ raw_path: str = None,
202
+ raw_query_string: bytes = None,
203
+ request_headers: dict = None,
204
+ request_body: bytes = None,
205
+ response_headers: dict = None,
206
+ response_body: bytes = None,
207
+ ) -> None:
208
+ """
209
+ ULTRA-FAST PATH: Assumes initialization already happened (checked once at startup).
210
+ Optionally accepts request/response headers and body for capture.
211
+
212
+ Args:
213
+ session_id: Unique session identifier
214
+ endpoint_id: Pre-registered endpoint ID
215
+ raw_path: Optional actual request path (e.g., "/log") - passed to C for JSON escaping
216
+ raw_query_string: Optional raw query string bytes (e.g., b"foo=5") - passed to C for decoding/escaping
217
+ request_headers: Optional dict of request headers
218
+ request_body: Optional request body (bytes, str, or list of chunks)
219
+ response_headers: Optional dict of response headers
220
+ response_body: Optional response body (bytes, str, or list of chunks)
221
+ """
222
+ # Check if network recording is suppressed (e.g., by @skip_network_tracing decorator)
223
+ if is_network_recording_suppressed():
224
+ return
225
+
226
+ # HOT PATH OPTIMIZATION: Skip initialization check after first successful init
227
+ if not _FAST_NETWORKHOP_READY:
228
+ if not _ensure_fast_networkhop_initialized():
229
+ return
230
+
231
+ # Direct C call - no exception handling (C code is bulletproof)
232
+ if endpoint_id >= 0:
233
+ # Check if we need the extended path (with route/query/body/header capture)
234
+ has_extended_data = (
235
+ raw_path is not None
236
+ or raw_query_string is not None
237
+ or request_headers is not None
238
+ or request_body is not None
239
+ or response_headers is not None
240
+ or response_body is not None
241
+ )
242
+
243
+ if has_extended_data:
244
+ # Use extended capture path - C handles all decoding/escaping
245
+ _sfnetworkhop.networkhop_with_bodies(
246
+ session_id=session_id,
247
+ endpoint_id=endpoint_id,
248
+ raw_path=raw_path,
249
+ raw_query_string=raw_query_string,
250
+ request_headers=request_headers,
251
+ request_body=request_body,
252
+ response_headers=response_headers,
253
+ response_body=response_body,
254
+ )
255
+ else:
256
+ # Ultra-fast path with NO extra data
257
+ _sfnetworkhop.networkhop_fast(
258
+ session_id=session_id, endpoint_id=endpoint_id
259
+ )
260
+
261
+
262
+ def fast_send_network_hop(
263
+ session_id: str, line: str, column: str, name: str, entrypoint: str
264
+ ):
265
+ """
266
+ Backward-compatible API (not used by patched FastAPI; kept for other callers).
267
+ """
268
+ # Check if network recording is suppressed (e.g., by @skip_network_tracing decorator)
269
+ if is_network_recording_suppressed():
270
+ return
271
+
272
+ if _ensure_fast_networkhop_initialized():
273
+ try:
274
+ # Old slow path in C; kept for compatibility
275
+ _sfnetworkhop.networkhop(
276
+ session_id=session_id,
277
+ line=line,
278
+ column=column,
279
+ name=name,
280
+ entrypoint=entrypoint,
281
+ )
282
+ return
283
+ except Exception:
284
+ pass
285
+
286
+ # Fallback to Python transmitter
287
+ NetworkHopsTransmitter().send(
288
+ session_id=session_id,
289
+ line=line,
290
+ column=column,
291
+ name=name,
292
+ entrypoint=entrypoint,
293
+ )
@@ -0,0 +1,112 @@
1
+ import sys
2
+ from typing import Any, Iterable, List
3
+
4
+ from .env_vars import (
5
+ SAILFISH_EXCEPTION_LOCALS_HIDE_DUNDER,
6
+ SAILFISH_EXCEPTION_LOCALS_HIDE_SELF,
7
+ SAILFISH_EXCEPTION_LOCALS_HIDE_SUNDER,
8
+ SAILFISH_EXCEPTION_LOCALS_TYPES_TO_IGNORE,
9
+ )
10
+
11
+
12
+ def import_type(module_name: str, type_name: str):
13
+ try:
14
+ exec( # pylint: disable=exec-used
15
+ f"from {module_name} import {type_name}",
16
+ globals(),
17
+ )
18
+ return globals().get(type_name, None)
19
+ except ImportError:
20
+ return None
21
+
22
+
23
+ def get_types_from_str(types_str: str) -> List[type]:
24
+ types_list = []
25
+ if types_str:
26
+ for type_path in types_str.split(","):
27
+ type_path_fixed = type_path.replace(" ", "")
28
+ module_name, type_name = type_path_fixed.rsplit(".", 1)
29
+ type_obj = import_type(module_name, type_name)
30
+ if type_obj:
31
+ types_list.append(type_obj)
32
+ return types_list
33
+
34
+
35
+ EXCEPTION_LOCALS_TYPES_TO_IGNORE = get_types_from_str(
36
+ SAILFISH_EXCEPTION_LOCALS_TYPES_TO_IGNORE
37
+ )
38
+
39
+
40
+ def get_current_frame():
41
+ return sys._getframe(1) # pylint: disable=protected-access
42
+
43
+
44
+ def value_type_to_be_ignored(value: Any) -> bool:
45
+ return any(
46
+ isinstance(value, type_obj) for type_obj in EXCEPTION_LOCALS_TYPES_TO_IGNORE
47
+ )
48
+
49
+
50
+ def key_is_str_to_be_ignored(
51
+ key: Any, locals_hide_self: bool, locals_hide_dunder: bool, locals_hide_sunder: bool
52
+ ) -> bool:
53
+ return (
54
+ (locals_hide_self and key == "self")
55
+ or (locals_hide_dunder and key.startswith("__"))
56
+ or (locals_hide_sunder and key.startswith("_"))
57
+ )
58
+
59
+
60
+ def filter_locals(
61
+ iter_locals: Iterable[tuple[str, object]],
62
+ locals_hide_self: bool,
63
+ locals_hide_dunder: bool,
64
+ locals_hide_sunder: bool,
65
+ ) -> Iterable[tuple[str, object]]:
66
+ for key, value in iter_locals:
67
+ if not isinstance(key, str):
68
+ if value_type_to_be_ignored(value):
69
+ continue
70
+ else:
71
+ if key_is_str_to_be_ignored(
72
+ key, locals_hide_self, locals_hide_dunder, locals_hide_sunder
73
+ ):
74
+ continue
75
+ if value_type_to_be_ignored(value):
76
+ continue
77
+ if key in ("args", "field_args") and isinstance(value, list):
78
+ value = list(
79
+ filter_locals(
80
+ enumerate(value),
81
+ locals_hide_self,
82
+ locals_hide_dunder,
83
+ locals_hide_sunder,
84
+ )
85
+ )
86
+ if key in ("kwargs", "field_kwargs") and isinstance(value, dict):
87
+ value = dict(
88
+ filter_locals(
89
+ value.items(),
90
+ locals_hide_self,
91
+ locals_hide_dunder,
92
+ locals_hide_sunder,
93
+ )
94
+ )
95
+ yield key, value
96
+
97
+
98
+ def get_locals(
99
+ iter_locals: Iterable[tuple[str, object]],
100
+ locals_hide_self: bool = SAILFISH_EXCEPTION_LOCALS_HIDE_SELF,
101
+ locals_hide_dunder: bool = SAILFISH_EXCEPTION_LOCALS_HIDE_DUNDER,
102
+ locals_hide_sunder: bool = SAILFISH_EXCEPTION_LOCALS_HIDE_SUNDER,
103
+ ) -> Iterable[tuple[str, object]]:
104
+ """Extract locals from an iterator of key pairs."""
105
+ if not (locals_hide_dunder or locals_hide_sunder):
106
+ yield from iter_locals
107
+ return
108
+ iter_locals_filtered = filter_locals(
109
+ iter_locals, locals_hide_self, locals_hide_dunder, locals_hide_sunder
110
+ )
111
+ for key, value in iter_locals_filtered:
112
+ yield key, value