sf-veritas 0.9.7__py3-none-any.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.

Potentially problematic release.


This version of sf-veritas might be problematic. Click here for more details.

Files changed (86) hide show
  1. sf_veritas/.gitignore +2 -0
  2. sf_veritas/__init__.py +4 -0
  3. sf_veritas/app_config.py +49 -0
  4. sf_veritas/cli.py +336 -0
  5. sf_veritas/constants.py +3 -0
  6. sf_veritas/custom_excepthook.py +285 -0
  7. sf_veritas/custom_log_handler.py +53 -0
  8. sf_veritas/custom_output_wrapper.py +107 -0
  9. sf_veritas/custom_print.py +34 -0
  10. sf_veritas/django_app.py +5 -0
  11. sf_veritas/env_vars.py +83 -0
  12. sf_veritas/exception_handling_middleware.py +18 -0
  13. sf_veritas/exception_metaclass.py +69 -0
  14. sf_veritas/frame_tools.py +112 -0
  15. sf_veritas/import_hook.py +62 -0
  16. sf_veritas/infra_details/__init__.py +3 -0
  17. sf_veritas/infra_details/get_infra_details.py +24 -0
  18. sf_veritas/infra_details/kubernetes/__init__.py +3 -0
  19. sf_veritas/infra_details/kubernetes/get_cluster_name.py +147 -0
  20. sf_veritas/infra_details/kubernetes/get_details.py +7 -0
  21. sf_veritas/infra_details/running_on/__init__.py +17 -0
  22. sf_veritas/infra_details/running_on/kubernetes.py +11 -0
  23. sf_veritas/interceptors.py +252 -0
  24. sf_veritas/local_env_detect.py +118 -0
  25. sf_veritas/package_metadata.py +6 -0
  26. sf_veritas/patches/__init__.py +0 -0
  27. sf_veritas/patches/concurrent_futures.py +19 -0
  28. sf_veritas/patches/constants.py +1 -0
  29. sf_veritas/patches/exceptions.py +82 -0
  30. sf_veritas/patches/multiprocessing.py +32 -0
  31. sf_veritas/patches/network_libraries/__init__.py +51 -0
  32. sf_veritas/patches/network_libraries/aiohttp.py +100 -0
  33. sf_veritas/patches/network_libraries/curl_cffi.py +93 -0
  34. sf_veritas/patches/network_libraries/http_client.py +64 -0
  35. sf_veritas/patches/network_libraries/httpcore.py +152 -0
  36. sf_veritas/patches/network_libraries/httplib2.py +76 -0
  37. sf_veritas/patches/network_libraries/httpx.py +123 -0
  38. sf_veritas/patches/network_libraries/niquests.py +192 -0
  39. sf_veritas/patches/network_libraries/pycurl.py +71 -0
  40. sf_veritas/patches/network_libraries/requests.py +187 -0
  41. sf_veritas/patches/network_libraries/tornado.py +139 -0
  42. sf_veritas/patches/network_libraries/treq.py +122 -0
  43. sf_veritas/patches/network_libraries/urllib_request.py +129 -0
  44. sf_veritas/patches/network_libraries/utils.py +101 -0
  45. sf_veritas/patches/os.py +17 -0
  46. sf_veritas/patches/threading.py +32 -0
  47. sf_veritas/patches/web_frameworks/__init__.py +45 -0
  48. sf_veritas/patches/web_frameworks/aiohttp.py +133 -0
  49. sf_veritas/patches/web_frameworks/async_websocket_consumer.py +132 -0
  50. sf_veritas/patches/web_frameworks/blacksheep.py +107 -0
  51. sf_veritas/patches/web_frameworks/bottle.py +142 -0
  52. sf_veritas/patches/web_frameworks/cherrypy.py +246 -0
  53. sf_veritas/patches/web_frameworks/django.py +307 -0
  54. sf_veritas/patches/web_frameworks/eve.py +138 -0
  55. sf_veritas/patches/web_frameworks/falcon.py +229 -0
  56. sf_veritas/patches/web_frameworks/fastapi.py +145 -0
  57. sf_veritas/patches/web_frameworks/flask.py +186 -0
  58. sf_veritas/patches/web_frameworks/klein.py +40 -0
  59. sf_veritas/patches/web_frameworks/litestar.py +217 -0
  60. sf_veritas/patches/web_frameworks/pyramid.py +89 -0
  61. sf_veritas/patches/web_frameworks/quart.py +155 -0
  62. sf_veritas/patches/web_frameworks/robyn.py +114 -0
  63. sf_veritas/patches/web_frameworks/sanic.py +120 -0
  64. sf_veritas/patches/web_frameworks/starlette.py +144 -0
  65. sf_veritas/patches/web_frameworks/strawberry.py +269 -0
  66. sf_veritas/patches/web_frameworks/tornado.py +129 -0
  67. sf_veritas/patches/web_frameworks/utils.py +55 -0
  68. sf_veritas/print_override.py +13 -0
  69. sf_veritas/regular_data_transmitter.py +358 -0
  70. sf_veritas/request_interceptor.py +399 -0
  71. sf_veritas/request_utils.py +104 -0
  72. sf_veritas/server_status.py +1 -0
  73. sf_veritas/shutdown_flag.py +11 -0
  74. sf_veritas/subprocess_startup.py +3 -0
  75. sf_veritas/test_cli.py +145 -0
  76. sf_veritas/thread_local.py +436 -0
  77. sf_veritas/timeutil.py +114 -0
  78. sf_veritas/transmit_exception_to_sailfish.py +28 -0
  79. sf_veritas/transmitter.py +58 -0
  80. sf_veritas/types.py +44 -0
  81. sf_veritas/unified_interceptor.py +323 -0
  82. sf_veritas/utils.py +39 -0
  83. sf_veritas-0.9.7.dist-info/METADATA +83 -0
  84. sf_veritas-0.9.7.dist-info/RECORD +86 -0
  85. sf_veritas-0.9.7.dist-info/WHEEL +4 -0
  86. sf_veritas-0.9.7.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,53 @@
1
+ import logging
2
+ from typing import Optional
3
+
4
+ from . import app_config
5
+ from .env_vars import PRINT_CONFIGURATION_STATUSES, SF_DEBUG
6
+ from .interceptors import LogInterceptor
7
+ from .thread_local import get_or_set_sf_trace_id
8
+
9
+
10
+ class CustomLogHandler(logging.Handler, LogInterceptor):
11
+ def __init__(self):
12
+ logging.Handler.__init__(self)
13
+ LogInterceptor.__init__(self, api_key=app_config._sailfish_api_key)
14
+ if PRINT_CONFIGURATION_STATUSES:
15
+ print("Intercepting log statements")
16
+
17
+ def emit(self, record, trace_id: Optional[str] = None):
18
+ if isinstance(record, logging.LogRecord):
19
+ try:
20
+ log_entry = self.format(record)
21
+ log_level = record.levelname
22
+
23
+ if SF_DEBUG:
24
+ print(
25
+ "[[DEBUG custom_log_handler]]",
26
+ f"trace_id={trace_id}",
27
+ f"[[{log_level}]]",
28
+ log_entry,
29
+ "[[/DEBUG]]",
30
+ log=False,
31
+ )
32
+
33
+ if SF_DEBUG:
34
+ print(
35
+ "CustomLogHandler...SENDING DATA...do_send args=",
36
+ (log_level, log_entry, trace_id),
37
+ trace_id,
38
+ log=False,
39
+ )
40
+ _, trace_id = get_or_set_sf_trace_id(trace_id)
41
+ if SF_DEBUG:
42
+ print(
43
+ "CustomLogHandler...SENDING DATA...do_send args=",
44
+ (log_level, log_entry, trace_id),
45
+ trace_id,
46
+ log=False,
47
+ )
48
+ self.do_send((log_level, log_entry, trace_id), trace_id)
49
+
50
+ except Exception as e: # pylint: disable=broad-exception-caught
51
+ # TODO - Sibyl post-launch - disable this print??
52
+ print("There's an error when emitting!!!!!\n", e)
53
+ self.handleError(record)
@@ -0,0 +1,107 @@
1
+ import re
2
+ import sys
3
+ import threading
4
+
5
+ # from ._to_be_deleted__custom_output_wrapper_stderr import CustomOutputWrapperStdErr
6
+ from .env_vars import PRINT_CONFIGURATION_STATUSES, SF_DEBUG
7
+ from .interceptors import PrintInterceptor
8
+ from .thread_local import _thread_locals, get_or_set_sf_trace_id
9
+
10
+
11
+ class CustomOutputWrapper:
12
+ def __init__(self, original):
13
+ self.original = original
14
+ self.print_interceptor = PrintInterceptor()
15
+ self._lock = threading.Lock()
16
+
17
+ def write(self, msg):
18
+ if (
19
+ hasattr(_thread_locals, "reentrancy_guard_logging_active")
20
+ and _thread_locals.reentrancy_guard_logging_active
21
+ ):
22
+ self.original.write(msg)
23
+ self.original.flush()
24
+ return
25
+
26
+ with self._lock:
27
+ # Add custom tags if SF_DEBUG is enabled
28
+ msg_with_tags = (
29
+ f"[[CUSTOM-OUTPUT]] {msg} [[/CUSTOM-OUTPUT]]\n" if SF_DEBUG else msg
30
+ )
31
+ self.original.write(msg_with_tags)
32
+ self.original.flush()
33
+
34
+ # Intercept the message and log it
35
+ # self.print_interceptor.send(msg_with_tags)
36
+ pattern = r"HTTP\s(POST|GET)\s(\/healthz|\/graphql\/)\s.*"
37
+ if re.match(pattern, msg):
38
+ return
39
+
40
+ _, trace_id = get_or_set_sf_trace_id()
41
+ if SF_DEBUG:
42
+ print(
43
+ "CustomOutputWrapper...SENDING DATA...args=",
44
+ (msg_with_tags, trace_id),
45
+ trace_id,
46
+ log=False,
47
+ )
48
+ self.print_interceptor.do_send((msg_with_tags, trace_id), trace_id)
49
+
50
+ def __getattr__(self, attr):
51
+ return getattr(self.original, attr)
52
+
53
+
54
+ def setup_custom_output_wrappers():
55
+ if PRINT_CONFIGURATION_STATUSES:
56
+ print("setup_custom_output_wrappers")
57
+ sys.stdout = CustomOutputWrapper(sys.stdout)
58
+ # sys.stderr = CustomOutputWrapperStdErr(sys.stderr) # TODO - uncomment
59
+ if PRINT_CONFIGURATION_STATUSES:
60
+ print("setup_custom_output_wrappers...DONE")
61
+
62
+
63
+ def get_custom_output_wrapper_django():
64
+ from django.core.management.base import OutputWrapper
65
+
66
+ class CustomOutputWrapperDjango(OutputWrapper):
67
+ def __init__(self, *args, **kwargs):
68
+ super().__init__(*args, **kwargs)
69
+ self.print_interceptor = PrintInterceptor()
70
+
71
+ def write(self, msg="", style_func=None, ending=None):
72
+ if (
73
+ hasattr(_thread_locals, "reentrancy_guard_logging_active")
74
+ and _thread_locals.reentrancy_guard_logging_active
75
+ ):
76
+ super().write(msg, style_func, ending)
77
+ return
78
+
79
+ # Add custom tags if SF_DEBUG is enabled
80
+ if style_func is None:
81
+ style_func = lambda x: (
82
+ f"[[CUSTOM-OUTPUT-DJANGO]] {x} [[/CUSTOM-OUTPUT-DJANGO]]\n"
83
+ if SF_DEBUG
84
+ else x
85
+ )
86
+
87
+ # Use the original write method
88
+ super().write(msg, style_func, ending)
89
+
90
+ pattern = r"HTTP\s(POST|GET)\s(\/healthz|\/graphql\/)\s.*"
91
+ if re.match(pattern, msg):
92
+ return
93
+
94
+ # Intercept the message and log it
95
+ message = msg if ending is None else msg + ending
96
+
97
+ _, trace_id = get_or_set_sf_trace_id()
98
+ if SF_DEBUG:
99
+ print(
100
+ "get_custom_output_wrapper_django...SENDING DATA...",
101
+ message,
102
+ trace_id,
103
+ log=False,
104
+ )
105
+ self.print_interceptor.do_send((message, trace_id), trace_id)
106
+
107
+ return CustomOutputWrapperDjango
@@ -0,0 +1,34 @@
1
+ import builtins
2
+ import sys
3
+
4
+ from .env_vars import SF_DEBUG
5
+ from .thread_local import (
6
+ _thread_locals,
7
+ activate_reentrancy_guards_sys_stdout,
8
+ get_or_set_sf_trace_id,
9
+ )
10
+
11
+
12
+ def custom_print(*args, log=True, **kwargs):
13
+ """
14
+ Custom print function to intercept print statements.
15
+ """
16
+ # Prepare the message to print
17
+ print_args = ("[[CUSTOM-PRINT]]", *args, "[[/CUSTOM-PRINT]]") if SF_DEBUG else args
18
+ activate_reentrancy_guards_sys_stdout()
19
+ builtins._original_print( # pylint: disable=protected-access; This is added by our interceptor
20
+ *print_args, file=sys.__stdout__, **kwargs
21
+ )
22
+
23
+ message = " ".join(map(str, args))
24
+ # If log is True, send the message to the logging system
25
+ if log and message.strip():
26
+ _, trace_id = get_or_set_sf_trace_id()
27
+ if SF_DEBUG:
28
+ print(
29
+ "UnifiedInterceptor.custom_print...SENDING DATA...args=",
30
+ (message, trace_id),
31
+ trace_id,
32
+ log=False,
33
+ )
34
+ sys.stdout.print_interceptor.do_send((message, trace_id), trace_id)
@@ -0,0 +1,5 @@
1
+ from django.apps import AppConfig
2
+
3
+
4
+ class SFVeritasConfig(AppConfig):
5
+ name = "sf_veritas"
sf_veritas/env_vars.py ADDED
@@ -0,0 +1,83 @@
1
+ import logging
2
+ import os
3
+
4
+ from .utils import strtobool
5
+
6
+ LITE_DEBUGGING = strtobool(os.getenv("LITE_DEBUGGING", "false"))
7
+ SF_DEBUG = strtobool(os.getenv("SF_DEBUG", "false"))
8
+ SF_DEBUG_TRACES = strtobool(os.getenv("SF_DEBUG_TRACES", "false"))
9
+ SF_INTERNAL = strtobool(os.getenv("SF_INTERNAL", "false"))
10
+ IS_SAILFISH_COLLECTOR = strtobool(os.getenv("IS_SAILFISH_COLLECTOR", "false"))
11
+ PRINT_CONFIGURATION_STATUSES = strtobool(
12
+ os.getenv("PRINT_CONFIGURATION_STATUSES", "false")
13
+ )
14
+ # Exception Tools
15
+ SAILFISH_EXCEPTION_LOCALS_HIDE_SELF = strtobool(
16
+ os.getenv("SAILFISH_EXCEPTION_LOCALS_HIDE_SELF", "true")
17
+ )
18
+ SAILFISH_EXCEPTION_LOCALS_HIDE_DUNDER = strtobool(
19
+ os.getenv("SAILFISH_EXCEPTION_LOCALS_HIDE_DUNDER", "true")
20
+ )
21
+ SAILFISH_EXCEPTION_LOCALS_HIDE_SUNDER = strtobool(
22
+ os.getenv("SAILFISH_EXCEPTION_LOCALS_HIDE_SUNDER", "true")
23
+ )
24
+ SAILFISH_EXCEPTION_STACK_DEPTH_LOCALS = int(
25
+ os.getenv("SAILFISH_EXCEPTION_STACK_DEPTH_LOCALS", "5")
26
+ )
27
+ SAILFISH_EXCEPTION_STACK_DEPTH_CODE_TRACE_DEPTH = os.getenv(
28
+ "SAILFISH_EXCEPTION_STACK_DEPTH_CODE_TRACE_DEPTH", "full"
29
+ )
30
+ SAILFISH_EXCEPTION_STACK_DEPTH_CODE_TRACE_TYPE = os.getenv(
31
+ "SAILFISH_EXCEPTION_STACK_DEPTH_CODE_TRACE_TYPE", "line"
32
+ )
33
+ SAILFISH_EXCEPTION_STACK_DEPTH_CODE_TRACE_TYPE_AT_OFFENSIVE_CALL = os.getenv(
34
+ "SAILFISH_EXCEPTION_STACK_DEPTH_CODE_TRACE_TYPE_AT_OFFENSIVE_CALL", "line"
35
+ )
36
+ SAILFISH_EXCEPTION_STACK_DEPTH_CODE_VALUES_AT_FULL_DEPTH_EXCEPTION = strtobool(
37
+ os.getenv(
38
+ "SAILFISH_EXCEPTION_STACK_DEPTH_CODE_VALUES_AT_FULL_DEPTH_EXCEPTION", "false"
39
+ )
40
+ )
41
+ SAILFISH_EXCEPTION_LOCALS_TYPES_TO_IGNORE = os.getenv(
42
+ "SAILFISH_EXCEPTION_LOCALS_TYPES_TO_IGNORE",
43
+ "strawberry.types.info.Info,graphql.type.definition.GraphQLResolveInfo,strawberry.field.StrawberryField",
44
+ )
45
+ SAILFISH_EXCEPTION_FETCH_BEYOND_OFFENDER_DEPTH = int(
46
+ os.getenv("SAILFISH_EXCEPTION_FETCH_BEYOND_OFFENDER_DEPTH", "3")
47
+ )
48
+ SAILFISH_EXCEPTION_FETCH_LOCALS_BEYOND_OFFENDER_DEPTH = int(
49
+ os.getenv(
50
+ "SAILFISH_EXCEPTION_FETCH_LOCALS_BEYOND_OFFENDER_DEPTH", "5"
51
+ ) # Sibyl launch - lower this
52
+ )
53
+ SAILFISH_EXCEPTION_FETCH_ABOVE_OFFENDER_DEPTH = int(
54
+ os.getenv(
55
+ "SAILFISH_EXCEPTION_FETCH_ABOVE_OFFENDER_DEPTH", "3"
56
+ ) # Sibyl launch - lower this
57
+ )
58
+ SAILFISH_EXCEPTION_FETCH_LOCALS_ABOVE_OFFENDER_DEPTH = int(
59
+ os.getenv(
60
+ "SAILFISH_EXCEPTION_FETCH_LOCALS_ABOVE_OFFENDER_DEPTH", "-1"
61
+ ) # Sibyl launch - lower this
62
+ )
63
+ SAILFISH_EXCEPTION_FETCH_ABOVE_OFFENDER_INCLUDE_INSTALLED_PACKAGES = strtobool(
64
+ os.getenv(
65
+ "SAILFISH_EXCEPTION_FETCH_ABOVE_OFFENDER_INCLUDE_INSTALLED_PACKAGES", "false"
66
+ )
67
+ )
68
+
69
+
70
+ def get_log_level():
71
+ if LOG_LEVEL_ENV_VAR == "DEBUG":
72
+ return logging.DEBUG
73
+ if LOG_LEVEL_ENV_VAR == "INFO":
74
+ return logging.INFO
75
+ if LOG_LEVEL_ENV_VAR == "WARN":
76
+ return logging.WARN
77
+ if LOG_LEVEL_ENV_VAR == "ERROR":
78
+ return logging.ERROR
79
+ return logging.CRITICAL
80
+
81
+
82
+ LOG_LEVEL_ENV_VAR = os.getenv("LOG_LEVEL", "INFO")
83
+ LOG_LEVEL = get_log_level()
@@ -0,0 +1,18 @@
1
+ import sys
2
+
3
+ from .custom_excepthook import custom_excepthook
4
+
5
+
6
+ class CustomExceptionMiddleware:
7
+ def __init__(self, app):
8
+ self.app = app
9
+
10
+ async def __call__(self, scope, receive, send):
11
+ try:
12
+ await self.app(scope, receive, send)
13
+ except Exception as exc:
14
+ exc_type, exc_value, exc_traceback = sys.exc_info()
15
+ custom_excepthook(exc_type, exc_value, exc_traceback)
16
+
17
+ def __getattr__(self, attr):
18
+ return getattr(self.original, attr)
@@ -0,0 +1,69 @@
1
+ from abc import ABCMeta
2
+
3
+ from .custom_excepthook import transmit_exception
4
+
5
+
6
+ class ExceptionMeta(ABCMeta):
7
+ """
8
+ Metaclass to add `capture_even_if_caught` functionality to exceptions
9
+ and provide a `transmit_to_sailfish` method for all exceptions.
10
+ """
11
+
12
+ def __new__(cls, name, bases, dct):
13
+ # Wrap or define the `__init__` method
14
+ if "__init__" in dct:
15
+ original_init = dct["__init__"]
16
+
17
+ def wrapped_init(self, *args, **kwargs):
18
+ # Add the `capture_even_if_caught` attribute
19
+ self.capture_even_if_caught = kwargs.pop(
20
+ "capture_even_if_caught", False
21
+ )
22
+ self._handled = False # Ensure `_handled` is initialized
23
+ original_init(self, *args, **kwargs)
24
+
25
+ dct["__init__"] = wrapped_init
26
+ else:
27
+
28
+ def default_init(self, *args, **kwargs):
29
+ # Add the `capture_even_if_caught` attribute
30
+ self.capture_even_if_caught = kwargs.pop(
31
+ "capture_even_if_caught", False
32
+ )
33
+ self._handled = False # Ensure `_handled` is initialized
34
+ super(Exception, self).__init__(*args, **kwargs)
35
+
36
+ dct["__init__"] = default_init
37
+
38
+ # Add `transmit_to_sailfish` method to all exceptions
39
+ def transmit_to_sailfish(self, was_caught: bool = False):
40
+ """
41
+ Transmit this exception to Sailfish.
42
+ """
43
+ if not getattr(self, "_handled", False):
44
+ transmit_exception(type(self), self, self.__traceback__, was_caught)
45
+ setattr(
46
+ self, "_handled", True
47
+ ) # Mark as handled to prevent duplication
48
+
49
+ dct["transmit_to_sailfish"] = transmit_to_sailfish
50
+
51
+ return super().__new__(cls, name, bases, dct)
52
+
53
+ def __call__(cls, *args, **kwargs):
54
+ """
55
+ Intercept exception instantiation to handle `capture_even_if_caught`.
56
+ """
57
+ instance = super().__call__(*args, **kwargs)
58
+ # Automatically handle `capture_even_if_caught` exceptions
59
+ if getattr(instance, "capture_even_if_caught", False):
60
+ instance.transmit_to_sailfish()
61
+ return instance
62
+
63
+
64
+ class PatchedException(Exception, metaclass=ExceptionMeta):
65
+ """
66
+ A patched version of the built-in Exception class with universal interception capabilities.
67
+ """
68
+
69
+ pass
@@ -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
@@ -0,0 +1,62 @@
1
+ import importlib
2
+ import importlib.util
3
+ import sys
4
+ from importlib.abc import MetaPathFinder
5
+
6
+ from .custom_excepthook import custom_excepthook
7
+ from .custom_output_wrapper import get_custom_output_wrapper_django
8
+ from .env_vars import PRINT_CONFIGURATION_STATUSES
9
+
10
+
11
+ class ImportInterceptor:
12
+ def __init__(self, module_name, callback):
13
+ self.module_name = module_name
14
+ self.callback = callback
15
+
16
+ def find_spec(self, fullname, path, target=None):
17
+ if fullname == self.module_name:
18
+ self.callback()
19
+ # Remove the interceptor once the callback has been called
20
+ sys.meta_path.remove(self)
21
+ return importlib.util.find_spec(fullname, path)
22
+ return None
23
+
24
+
25
+ def setup_django_import_hook():
26
+ interceptor = ImportInterceptor(
27
+ "django.core.management", get_custom_output_wrapper_django
28
+ )
29
+ sys.meta_path.insert(0, interceptor)
30
+
31
+
32
+ def wrap_excepthook(old_excepthook):
33
+ def custom_wrapper_excepthook(exc_type, exc_value, exc_traceback):
34
+ # Call your custom exception hook
35
+ custom_excepthook(exc_type, exc_value, exc_traceback)
36
+ # Call the original exception hook (which might be Sentry's)
37
+ if old_excepthook is not None:
38
+ old_excepthook(exc_type, exc_value, exc_traceback)
39
+
40
+ return custom_wrapper_excepthook
41
+
42
+
43
+ def set_excepthook():
44
+ # Wrap the current excepthook
45
+ current_excepthook = sys.excepthook
46
+ wrapped_excepthook = wrap_excepthook(current_excepthook)
47
+ sys.excepthook = wrapped_excepthook
48
+ if PRINT_CONFIGURATION_STATUSES:
49
+ print("Patched sys.excepthook for custom exception handling.")
50
+
51
+
52
+ class ExcepthookImportHook(MetaPathFinder):
53
+ def find_spec(self, fullname, path, target=None):
54
+ print("fullname", fullname, log=False)
55
+ if fullname in sys.modules:
56
+ print("\t> fullname in sys.modules", log=False)
57
+ set_excepthook()
58
+ return None
59
+
60
+
61
+ def install_import_hook():
62
+ sys.meta_path.insert(0, ExcepthookImportHook())
@@ -0,0 +1,3 @@
1
+ from .get_infra_details import get_infra_details
2
+
3
+ __all__ = ["get_infra_details"]
@@ -0,0 +1,24 @@
1
+ from dataclasses import dataclass
2
+ from typing import Any, Dict
3
+
4
+ from .kubernetes import get_details as get_kubernetes_details
5
+ from .running_on import System, running_on
6
+
7
+
8
+ @dataclass
9
+ class InfraDetails:
10
+ system: System
11
+ details: Dict[str, Any]
12
+
13
+
14
+ def get_running_on_details(system: System) -> Dict[str, Any]:
15
+ if system == System.KUBERNETES:
16
+ return get_kubernetes_details()
17
+ if system == System.UNKNOWN:
18
+ return {}
19
+
20
+
21
+ def get_infra_details() -> InfraDetails:
22
+ system = running_on()
23
+ details = get_running_on_details(system)
24
+ return InfraDetails(system=system, details=details)
@@ -0,0 +1,3 @@
1
+ from .get_details import get_details
2
+
3
+ __all__ = ["get_details"]