sf-veritas 0.10.3__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.

Potentially problematic release.


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

Files changed (132) hide show
  1. sf_veritas/__init__.py +20 -0
  2. sf_veritas/_sffastlog.c +889 -0
  3. sf_veritas/_sffastlog.cpython-314-x86_64-linux-gnu.so +0 -0
  4. sf_veritas/_sffastnet.c +924 -0
  5. sf_veritas/_sffastnet.cpython-314-x86_64-linux-gnu.so +0 -0
  6. sf_veritas/_sffastnetworkrequest.c +730 -0
  7. sf_veritas/_sffastnetworkrequest.cpython-314-x86_64-linux-gnu.so +0 -0
  8. sf_veritas/_sffuncspan.c +2155 -0
  9. sf_veritas/_sffuncspan.cpython-314-x86_64-linux-gnu.so +0 -0
  10. sf_veritas/_sffuncspan_config.c +617 -0
  11. sf_veritas/_sffuncspan_config.cpython-314-x86_64-linux-gnu.so +0 -0
  12. sf_veritas/_sfheadercheck.c +341 -0
  13. sf_veritas/_sfheadercheck.cpython-314-x86_64-linux-gnu.so +0 -0
  14. sf_veritas/_sfnetworkhop.c +1451 -0
  15. sf_veritas/_sfnetworkhop.cpython-314-x86_64-linux-gnu.so +0 -0
  16. sf_veritas/_sfservice.c +1175 -0
  17. sf_veritas/_sfservice.cpython-314-x86_64-linux-gnu.so +0 -0
  18. sf_veritas/_sfteepreload.c +5167 -0
  19. sf_veritas/app_config.py +49 -0
  20. sf_veritas/cli.py +336 -0
  21. sf_veritas/constants.py +10 -0
  22. sf_veritas/custom_excepthook.py +304 -0
  23. sf_veritas/custom_log_handler.py +129 -0
  24. sf_veritas/custom_output_wrapper.py +144 -0
  25. sf_veritas/custom_print.py +146 -0
  26. sf_veritas/django_app.py +5 -0
  27. sf_veritas/env_vars.py +186 -0
  28. sf_veritas/exception_handling_middleware.py +18 -0
  29. sf_veritas/exception_metaclass.py +69 -0
  30. sf_veritas/fast_frame_info.py +116 -0
  31. sf_veritas/fast_network_hop.py +293 -0
  32. sf_veritas/frame_tools.py +112 -0
  33. sf_veritas/funcspan_config_loader.py +556 -0
  34. sf_veritas/function_span_profiler.py +1174 -0
  35. sf_veritas/import_hook.py +62 -0
  36. sf_veritas/infra_details/__init__.py +3 -0
  37. sf_veritas/infra_details/get_infra_details.py +24 -0
  38. sf_veritas/infra_details/kubernetes/__init__.py +3 -0
  39. sf_veritas/infra_details/kubernetes/get_cluster_name.py +147 -0
  40. sf_veritas/infra_details/kubernetes/get_details.py +7 -0
  41. sf_veritas/infra_details/running_on/__init__.py +17 -0
  42. sf_veritas/infra_details/running_on/kubernetes.py +11 -0
  43. sf_veritas/interceptors.py +497 -0
  44. sf_veritas/libsfnettee.so +0 -0
  45. sf_veritas/local_env_detect.py +118 -0
  46. sf_veritas/package_metadata.py +6 -0
  47. sf_veritas/patches/__init__.py +0 -0
  48. sf_veritas/patches/concurrent_futures.py +19 -0
  49. sf_veritas/patches/constants.py +1 -0
  50. sf_veritas/patches/exceptions.py +82 -0
  51. sf_veritas/patches/multiprocessing.py +32 -0
  52. sf_veritas/patches/network_libraries/__init__.py +76 -0
  53. sf_veritas/patches/network_libraries/aiohttp.py +281 -0
  54. sf_veritas/patches/network_libraries/curl_cffi.py +363 -0
  55. sf_veritas/patches/network_libraries/http_client.py +419 -0
  56. sf_veritas/patches/network_libraries/httpcore.py +515 -0
  57. sf_veritas/patches/network_libraries/httplib2.py +204 -0
  58. sf_veritas/patches/network_libraries/httpx.py +515 -0
  59. sf_veritas/patches/network_libraries/niquests.py +211 -0
  60. sf_veritas/patches/network_libraries/pycurl.py +385 -0
  61. sf_veritas/patches/network_libraries/requests.py +633 -0
  62. sf_veritas/patches/network_libraries/tornado.py +341 -0
  63. sf_veritas/patches/network_libraries/treq.py +270 -0
  64. sf_veritas/patches/network_libraries/urllib_request.py +468 -0
  65. sf_veritas/patches/network_libraries/utils.py +398 -0
  66. sf_veritas/patches/os.py +17 -0
  67. sf_veritas/patches/threading.py +218 -0
  68. sf_veritas/patches/web_frameworks/__init__.py +54 -0
  69. sf_veritas/patches/web_frameworks/aiohttp.py +793 -0
  70. sf_veritas/patches/web_frameworks/async_websocket_consumer.py +317 -0
  71. sf_veritas/patches/web_frameworks/blacksheep.py +527 -0
  72. sf_veritas/patches/web_frameworks/bottle.py +502 -0
  73. sf_veritas/patches/web_frameworks/cherrypy.py +678 -0
  74. sf_veritas/patches/web_frameworks/cors_utils.py +122 -0
  75. sf_veritas/patches/web_frameworks/django.py +944 -0
  76. sf_veritas/patches/web_frameworks/eve.py +395 -0
  77. sf_veritas/patches/web_frameworks/falcon.py +926 -0
  78. sf_veritas/patches/web_frameworks/fastapi.py +724 -0
  79. sf_veritas/patches/web_frameworks/flask.py +520 -0
  80. sf_veritas/patches/web_frameworks/klein.py +501 -0
  81. sf_veritas/patches/web_frameworks/litestar.py +551 -0
  82. sf_veritas/patches/web_frameworks/pyramid.py +428 -0
  83. sf_veritas/patches/web_frameworks/quart.py +824 -0
  84. sf_veritas/patches/web_frameworks/robyn.py +697 -0
  85. sf_veritas/patches/web_frameworks/sanic.py +857 -0
  86. sf_veritas/patches/web_frameworks/starlette.py +723 -0
  87. sf_veritas/patches/web_frameworks/strawberry.py +813 -0
  88. sf_veritas/patches/web_frameworks/tornado.py +481 -0
  89. sf_veritas/patches/web_frameworks/utils.py +91 -0
  90. sf_veritas/print_override.py +13 -0
  91. sf_veritas/regular_data_transmitter.py +409 -0
  92. sf_veritas/request_interceptor.py +401 -0
  93. sf_veritas/request_utils.py +550 -0
  94. sf_veritas/server_status.py +1 -0
  95. sf_veritas/shutdown_flag.py +11 -0
  96. sf_veritas/subprocess_startup.py +3 -0
  97. sf_veritas/test_cli.py +145 -0
  98. sf_veritas/thread_local.py +970 -0
  99. sf_veritas/timeutil.py +114 -0
  100. sf_veritas/transmit_exception_to_sailfish.py +28 -0
  101. sf_veritas/transmitter.py +132 -0
  102. sf_veritas/types.py +47 -0
  103. sf_veritas/unified_interceptor.py +1580 -0
  104. sf_veritas/utils.py +39 -0
  105. sf_veritas-0.10.3.dist-info/METADATA +97 -0
  106. sf_veritas-0.10.3.dist-info/RECORD +132 -0
  107. sf_veritas-0.10.3.dist-info/WHEEL +5 -0
  108. sf_veritas-0.10.3.dist-info/entry_points.txt +2 -0
  109. sf_veritas-0.10.3.dist-info/top_level.txt +1 -0
  110. sf_veritas.libs/libbrotlicommon-6ce2a53c.so.1.0.6 +0 -0
  111. sf_veritas.libs/libbrotlidec-811d1be3.so.1.0.6 +0 -0
  112. sf_veritas.libs/libcom_err-730ca923.so.2.1 +0 -0
  113. sf_veritas.libs/libcrypt-52aca757.so.1.1.0 +0 -0
  114. sf_veritas.libs/libcrypto-bdaed0ea.so.1.1.1k +0 -0
  115. sf_veritas.libs/libcurl-eaa3cf66.so.4.5.0 +0 -0
  116. sf_veritas.libs/libgssapi_krb5-323bbd21.so.2.2 +0 -0
  117. sf_veritas.libs/libidn2-2f4a5893.so.0.3.6 +0 -0
  118. sf_veritas.libs/libk5crypto-9a74ff38.so.3.1 +0 -0
  119. sf_veritas.libs/libkeyutils-2777d33d.so.1.6 +0 -0
  120. sf_veritas.libs/libkrb5-a55300e8.so.3.3 +0 -0
  121. sf_veritas.libs/libkrb5support-e6594cfc.so.0.1 +0 -0
  122. sf_veritas.libs/liblber-2-d20824ef.4.so.2.10.9 +0 -0
  123. sf_veritas.libs/libldap-2-cea2a960.4.so.2.10.9 +0 -0
  124. sf_veritas.libs/libnghttp2-39367a22.so.14.17.0 +0 -0
  125. sf_veritas.libs/libpcre2-8-516f4c9d.so.0.7.1 +0 -0
  126. sf_veritas.libs/libpsl-99becdd3.so.5.3.1 +0 -0
  127. sf_veritas.libs/libsasl2-7de4d792.so.3.0.0 +0 -0
  128. sf_veritas.libs/libselinux-d0805dcb.so.1 +0 -0
  129. sf_veritas.libs/libssh-c11d285b.so.4.8.7 +0 -0
  130. sf_veritas.libs/libssl-60250281.so.1.1.1k +0 -0
  131. sf_veritas.libs/libunistring-05abdd40.so.2.1.0 +0 -0
  132. sf_veritas.libs/libuuid-95b83d40.so.1.3.0 +0 -0
sf_veritas/timeutil.py ADDED
@@ -0,0 +1,114 @@
1
+ import inspect
2
+ import threading
3
+ import time
4
+ from datetime import datetime, timedelta, timezone
5
+ from time import sleep
6
+ from typing import Tuple
7
+
8
+ import ntplib
9
+ import requests
10
+ from dateutil import parser
11
+ from .env_vars import PRINT_CONFIGURATION_STATUSES
12
+
13
+
14
+ def is_log_argument_supported():
15
+ try:
16
+ signature = inspect.signature(print)
17
+ return "log" in signature.parameters
18
+ except (TypeError, ValueError) as err:
19
+ return False
20
+
21
+
22
+ # Determine if the `log` argument is supported
23
+ PRINT_ARGS = {"log": False} if is_log_argument_supported() else {}
24
+
25
+
26
+ class TimeSync:
27
+ _instance = None
28
+ _lock = threading.Lock()
29
+
30
+ @classmethod
31
+ def get_instance(cls):
32
+ if not cls._instance:
33
+ with cls._lock:
34
+ if not cls._instance:
35
+ cls._instance = cls() # Initialize singleton
36
+ return cls._instance
37
+
38
+ def __init__(self):
39
+ if getattr(self, "_initialized", False): # Avoid reinitialization
40
+ return
41
+ if PRINT_CONFIGURATION_STATUSES:
42
+ print("Configurating global time lock", **PRINT_ARGS)
43
+ self.sync_utc_datetime, self.local_time_ms = self._fetch_utc_time()
44
+ self._initialized = True
45
+ if PRINT_CONFIGURATION_STATUSES:
46
+ print("Configurating global time lock...DONE", **PRINT_ARGS)
47
+
48
+ def _fetch_utc_time(self) -> Tuple[datetime, int]:
49
+ try:
50
+ return self._fetch_with_retries(self._fetch_ntp_time)
51
+ except Exception as e:
52
+ if PRINT_CONFIGURATION_STATUSES:
53
+ print(f"NTP sync failed: {e}. Falling back to HTTP APIs.", **PRINT_ARGS)
54
+ return self._fetch_with_retries(self._fetch_utc_time_from_apis)
55
+
56
+ def _fetch_with_retries(
57
+ self, fetch_func, retries=3, backoff_factor=1
58
+ ) -> Tuple[datetime, int]:
59
+ for attempt in range(retries):
60
+ try:
61
+ return fetch_func()
62
+ except Exception as e:
63
+ if PRINT_CONFIGURATION_STATUSES:
64
+ print(f"Attempt {attempt + 1} failed: {e}", **PRINT_ARGS)
65
+ sleep(backoff_factor * (2**attempt))
66
+ raise Exception("All retries failed")
67
+
68
+ def _fetch_ntp_time(self) -> Tuple[datetime, int]:
69
+ client = ntplib.NTPClient()
70
+ response = client.request("pool.ntp.org", version=3)
71
+ utc_datetime = datetime.utcfromtimestamp(response.tx_time).replace(
72
+ tzinfo=timezone.utc
73
+ )
74
+ local_time_ms = int(time.time() * 1000)
75
+ if PRINT_CONFIGURATION_STATUSES:
76
+ print("Time successfully synced using NTP.", **PRINT_ARGS)
77
+ return utc_datetime, local_time_ms
78
+
79
+ def _fetch_utc_time_from_apis(self) -> Tuple[datetime, int]:
80
+ apis = [
81
+ "https://worldtimeapi.org/api/timezone/Etc/UTC",
82
+ "http://worldclockapi.com/api/json/utc/now",
83
+ ]
84
+ for api in apis:
85
+ try:
86
+ response = requests.get(api, timeout=5)
87
+ response.raise_for_status()
88
+ data = response.json()
89
+ if "datetime" in data:
90
+ utc_datetime = parser.isoparse(data["datetime"])
91
+ elif "currentDateTime" in data:
92
+ utc_datetime = parser.isoparse(data["currentDateTime"])
93
+ else:
94
+ raise ValueError("Unexpected API response format")
95
+ local_time_ms = int(time.time() * 1000)
96
+ if PRINT_CONFIGURATION_STATUSES:
97
+ print(f"Time successfully synced using {api}.", **PRINT_ARGS)
98
+ return utc_datetime, local_time_ms
99
+ except Exception as e:
100
+ if PRINT_CONFIGURATION_STATUSES:
101
+ print(f"Failed to fetch time from {api}: {e}", **PRINT_ARGS)
102
+ if PRINT_CONFIGURATION_STATUSES:
103
+ print("All time sources failed. Falling back to system time.", **PRINT_ARGS)
104
+ utc_datetime = datetime.now(timezone.utc)
105
+ local_time_ms = int(time.time() * 1000)
106
+ return utc_datetime, local_time_ms
107
+
108
+ def get_utc_time_in_ms(self) -> int:
109
+ current_local_time_ms = int(time.time() * 1000)
110
+ elapsed_ms = current_local_time_ms - self.local_time_ms
111
+ current_utc_datetime = self.sync_utc_datetime + timedelta(
112
+ milliseconds=elapsed_ms
113
+ )
114
+ return int(current_utc_datetime.timestamp() * 1000)
@@ -0,0 +1,28 @@
1
+ from .custom_excepthook import transmit_exception
2
+
3
+
4
+ def transmit_exception_to_sailfish(
5
+ exc: BaseException,
6
+ force_transmit: bool = False,
7
+ ):
8
+ """
9
+ Transmit an exception to Sailfish using the original traceback captured at the
10
+ point the exception was raised.
11
+
12
+ :param exc: The exception instance.
13
+ :param force_transmit: If True, will transmit even if the exception might
14
+ have already been handled or flagged.
15
+ """
16
+ # Get the exception type and traceback from the exception itself
17
+ exc_type = type(exc)
18
+ exc_traceback = exc.__traceback__ # Automatically fetch the original traceback
19
+
20
+ # In some implementations, you might keep a `_handled` attribute to avoid double transmission.
21
+ if not force_transmit and getattr(exc, "_handled", False):
22
+ return # Already transmitted
23
+
24
+ # Actually send it over to Sailfish
25
+ transmit_exception(exc_type, exc, exc_traceback)
26
+
27
+ # Mark as handled to avoid re-transmission
28
+ setattr(exc, "_handled", True)
@@ -0,0 +1,132 @@
1
+ from typing import Any, Dict, Optional
2
+
3
+ from .env_vars import SF_DEBUG
4
+ from .thread_local import get_or_set_sf_trace_id
5
+
6
+ # Try to import C extension for fast path
7
+ try:
8
+ from . import _sfservice
9
+ _SFSERVICE_AVAILABLE = True
10
+ except Exception:
11
+ _sfservice = None
12
+ _SFSERVICE_AVAILABLE = False
13
+
14
+ # Always have Python fallback available (lazy-loaded when needed)
15
+ collect_metadata_transmitter = None
16
+
17
+
18
+ class SailfishTransmitter(object):
19
+ @classmethod
20
+ def identify(
21
+ cls,
22
+ user_id: str,
23
+ traits: Optional[Dict[str, Any]] = None,
24
+ traits_json: Optional[str] = None,
25
+ override: bool = False,
26
+ ) -> None:
27
+ if traits is not None or traits_json is not None:
28
+ return cls.add_or_update_metadata(user_id, traits, traits_json, override)
29
+ return cls.add_or_update_metadata(user_id, dict(), override=override)
30
+
31
+ @classmethod
32
+ def update_service_details(
33
+ cls,
34
+ service_identifier: Optional[str] = None,
35
+ service_version: Optional[str] = None,
36
+ service_additional_metadata: Optional[Dict[str, Any]] = None,
37
+ git_sha: Optional[str] = None,
38
+ ) -> None:
39
+ """
40
+ Updates service details with metadata.
41
+ Sends mutation updateServiceDetails (Python implementation - no C extension yet).
42
+
43
+ Args:
44
+ service_identifier: Service identifier string
45
+ service_version: Service version string
46
+ service_additional_metadata: Dictionary of additional metadata
47
+ git_sha: Git SHA hash
48
+ """
49
+ # Import here to avoid circular dependency
50
+ from .regular_data_transmitter import UpdateServiceIdentifierMetadata
51
+
52
+ transmitter = UpdateServiceIdentifierMetadata()
53
+ transmitter.send(
54
+ service_identifier=service_identifier,
55
+ service_version=service_version,
56
+ service_additional_metadata=service_additional_metadata,
57
+ git_sha=git_sha,
58
+ )
59
+
60
+ if SF_DEBUG:
61
+ print(f"[[DEBUG]] update_service_details sent: id={service_identifier}, version={service_version}, sha={git_sha}", log=False)
62
+
63
+ @classmethod
64
+ def add_or_update_metadata(
65
+ cls,
66
+ user_id: str,
67
+ traits: Optional[Dict[str, Any]] = None,
68
+ traits_json: Optional[str] = None,
69
+ override: bool = False,
70
+ ) -> None:
71
+ """
72
+ Sets traits and sends to the Sailfish AI backend
73
+
74
+ Args:
75
+ user_id: unique identifier for the user; common uses are username or email
76
+ traits: dictionary of contents to add or update in the user's traits. Defaults to None.
77
+ traits_json: json string of contents to add or update in the user's traits. Defaults to None.
78
+ """
79
+ if traits is None and traits_json is None:
80
+ raise Exception(
81
+ 'Must pass in either traits or traits_json to "add_or_update_traits"'
82
+ )
83
+ if SF_DEBUG:
84
+ print(
85
+ "[[DEBUG - add_or_update_traits]] starting thread [[/DEBUG]]", log=False
86
+ )
87
+
88
+ _, trace_id = get_or_set_sf_trace_id()
89
+ if SF_DEBUG:
90
+ print(
91
+ "add_or_update_metadata...SENDING DATA...args=",
92
+ (user_id, traits, traits_json, override, trace_id),
93
+ trace_id,
94
+ log=False,
95
+ )
96
+
97
+ # Fast path: Use C extension if available
98
+ if _SFSERVICE_AVAILABLE and _sfservice:
99
+ try:
100
+ excluded_fields = []
101
+
102
+ # If traits is provided as dict, serialize it
103
+ if traits_json is None:
104
+ from .utils import serialize_json_with_exclusions
105
+ traits_json, excluded_fields = serialize_json_with_exclusions(traits)
106
+
107
+ # Call C extension
108
+ _sfservice.collect_metadata(
109
+ session_id=str(trace_id),
110
+ user_id=user_id,
111
+ traits_json=traits_json,
112
+ excluded_fields=excluded_fields,
113
+ override=override,
114
+ )
115
+
116
+ if SF_DEBUG:
117
+ print("[[DEBUG]] collect_metadata sent via _sfservice C extension", log=False)
118
+ return
119
+ except Exception as e:
120
+ if SF_DEBUG:
121
+ print(f"[[DEBUG]] Failed to send via C extension: {e}, falling back to Python", log=False)
122
+ # Fall through to Python implementation
123
+
124
+ # Fallback: Use Python implementation (lazy-load if needed)
125
+ global collect_metadata_transmitter
126
+ if collect_metadata_transmitter is None:
127
+ from .interceptors import CollectMetadataTransmitter
128
+ collect_metadata_transmitter = CollectMetadataTransmitter()
129
+
130
+ collect_metadata_transmitter.do_send(
131
+ (user_id, traits, traits_json, override, trace_id), trace_id
132
+ )
sf_veritas/types.py ADDED
@@ -0,0 +1,47 @@
1
+ import json
2
+ from dataclasses import dataclass, field
3
+ from typing import Any, Dict, List, Optional
4
+
5
+
6
+ @dataclass
7
+ class FrameInfo:
8
+ file: str
9
+ line: int
10
+ function: str
11
+ code: str
12
+ locals: Optional[Dict[str, Any]] = field(default_factory=dict)
13
+ column: Optional[int] = None
14
+ offender: Optional[bool] = False
15
+
16
+ def to_dict(self) -> Dict[str, Any]:
17
+ frame_info_dict = {
18
+ "file": self.file,
19
+ "line": self.line,
20
+ "function": self.function,
21
+ "code": self.code,
22
+ }
23
+ if self.locals:
24
+ frame_info_dict["locals"] = {k: str(v) for k, v in self.locals.items()}
25
+ if self.offender:
26
+ frame_info_dict["offender"] = self.offender
27
+ if self.column is not None:
28
+ frame_info_dict["column"] = self.column
29
+ return frame_info_dict
30
+
31
+ def to_json(self) -> str:
32
+ return json.dumps(self.to_dict())
33
+
34
+
35
+ def get_trace_from_json(data_json) -> List[FrameInfo]:
36
+ data = json.loads(data_json)
37
+ return [FrameInfo(**item) for item in data]
38
+
39
+
40
+ class CustomJSONEncoderForFrameInfo(json.JSONEncoder):
41
+ def default(self, obj):
42
+ if isinstance(obj, FrameInfo):
43
+ return obj.to_dict()
44
+ try:
45
+ return super().default(obj)
46
+ except TypeError:
47
+ return str(obj)