debugbundle-python 0.1.0__tar.gz → 0.1.2__tar.gz
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.
- {debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/PKG-INFO +22 -2
- {debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/README.md +21 -1
- {debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/pyproject.toml +1 -1
- {debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/src/debugbundle/config.py +5 -5
- {debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/src/debugbundle/core.py +65 -5
- {debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/src/debugbundle/relay.py +1 -0
- {debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/src/debugbundle_python.egg-info/PKG-INFO +22 -2
- {debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/tests/test_framework_integrations.py +6 -6
- {debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/tests/test_relay.py +25 -1
- {debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/tests/test_remote_config.py +8 -5
- {debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/tests/test_repository_metadata.py +9 -0
- {debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/tests/test_sdk.py +13 -1
- {debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/LICENSE +0 -0
- {debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/setup.cfg +0 -0
- {debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/src/debugbundle/__init__.py +0 -0
- {debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/src/debugbundle/integrations/__init__.py +0 -0
- {debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/src/debugbundle/integrations/common.py +0 -0
- {debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/src/debugbundle/integrations/django.py +0 -0
- {debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/src/debugbundle/integrations/fastapi.py +0 -0
- {debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/src/debugbundle/integrations/flask.py +0 -0
- {debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/src/debugbundle/integrations/relay_django.py +0 -0
- {debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/src/debugbundle/integrations/relay_fastapi.py +0 -0
- {debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/src/debugbundle/integrations/relay_flask.py +0 -0
- {debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/src/debugbundle/logger_integrations.py +0 -0
- {debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/src/debugbundle/py.typed +0 -0
- {debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/src/debugbundle/redaction.py +0 -0
- {debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/src/debugbundle/suppression.py +0 -0
- {debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/src/debugbundle/transport.py +0 -0
- {debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/src/debugbundle/trigger_token.py +0 -0
- {debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/src/debugbundle_python.egg-info/SOURCES.txt +0 -0
- {debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/src/debugbundle_python.egg-info/dependency_links.txt +0 -0
- {debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/src/debugbundle_python.egg-info/requires.txt +0 -0
- {debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/src/debugbundle_python.egg-info/top_level.txt +0 -0
- {debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/tests/test_contracts.py +0 -0
- {debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/tests/test_hooks.py +0 -0
- {debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/tests/test_http_integration.py +0 -0
- {debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/tests/test_schema_validation.py +0 -0
- {debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/tests/test_trigger_token.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: debugbundle-python
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: DebugBundle SDK for Python
|
|
5
5
|
Author: DebugBundle
|
|
6
6
|
License-Expression: AGPL-3.0-only
|
|
@@ -59,8 +59,28 @@ debugbundle.flush()
|
|
|
59
59
|
|
|
60
60
|
## Status
|
|
61
61
|
|
|
62
|
-
This repository currently contains the full Phase 18 Python SDK scope in eleven implementation slices: core SDK surface, buffering, redaction, duplicate suppression, probe buffering, vanilla runtime hooks, framework integrations for Django, Flask, and FastAPI, remote config polling and capture-policy enforcement, optional `structlog` and `loguru` auto-detection when `capture_logging()` is enabled, contract-aligned `EventEnvelope` emission for log, request, exception, suppression, and probe payloads, explicit public wrapper signatures and a validated buildable typed package artifact, real HTTP integration coverage against a lightweight mock ingestion server, vendored machine-readable schema validation for all event types the Python SDK currently emits, a standalone CI workflow that validates Ruff, mypy, pytest, and package builds for the Python 3.10+ support floor actually used by the package, an enforced per-file coverage gate that keeps every shipped Python SDK module at or above the required 80% minimum,
|
|
62
|
+
This repository currently contains the full Phase 18 Python SDK scope in eleven implementation slices: core SDK surface, buffering, redaction, duplicate suppression, probe buffering, vanilla runtime hooks, framework integrations for Django, Flask, and FastAPI, remote config polling and capture-policy enforcement, optional `structlog` and `loguru` auto-detection when `capture_logging()` is enabled, contract-aligned `EventEnvelope` emission for log, request, exception, suppression, and probe payloads, explicit public wrapper signatures and a validated buildable typed package artifact, real HTTP integration coverage against a lightweight mock ingestion server, vendored machine-readable schema validation for all event types the Python SDK currently emits, a standalone CI workflow that validates Ruff, mypy, pytest, and package builds for the Python 3.10+ support floor actually used by the package, an enforced per-file coverage gate that keeps every shipped Python SDK module at or above the required 80% minimum, request-local framework correlation binding so `X-DebugBundle-Trace-Id` flows through Django, Flask, and FastAPI into the emitted event correlation metadata for cross-context linking, and safe backend runtime process facts on exception payloads without reading environment variables.
|
|
63
|
+
|
|
64
|
+
## Runtime Context
|
|
65
|
+
|
|
66
|
+
Backend exception events now include safe runtime process facts when the host exposes them, including:
|
|
67
|
+
|
|
68
|
+
- Python version
|
|
69
|
+
- platform
|
|
70
|
+
- architecture
|
|
71
|
+
- pid
|
|
72
|
+
- cwd
|
|
73
|
+
- uptime
|
|
74
|
+
- hostname
|
|
75
|
+
- thread id
|
|
76
|
+
- best-effort memory metadata
|
|
77
|
+
|
|
78
|
+
The SDK does not read or emit environment variables in this runtime block.
|
|
63
79
|
|
|
64
80
|
## Docs
|
|
65
81
|
|
|
66
82
|
https://debugbundle.com/docs/sdk-python
|
|
83
|
+
|
|
84
|
+
## License
|
|
85
|
+
|
|
86
|
+
AGPL-3.0-only
|
|
@@ -20,8 +20,28 @@ debugbundle.flush()
|
|
|
20
20
|
|
|
21
21
|
## Status
|
|
22
22
|
|
|
23
|
-
This repository currently contains the full Phase 18 Python SDK scope in eleven implementation slices: core SDK surface, buffering, redaction, duplicate suppression, probe buffering, vanilla runtime hooks, framework integrations for Django, Flask, and FastAPI, remote config polling and capture-policy enforcement, optional `structlog` and `loguru` auto-detection when `capture_logging()` is enabled, contract-aligned `EventEnvelope` emission for log, request, exception, suppression, and probe payloads, explicit public wrapper signatures and a validated buildable typed package artifact, real HTTP integration coverage against a lightweight mock ingestion server, vendored machine-readable schema validation for all event types the Python SDK currently emits, a standalone CI workflow that validates Ruff, mypy, pytest, and package builds for the Python 3.10+ support floor actually used by the package, an enforced per-file coverage gate that keeps every shipped Python SDK module at or above the required 80% minimum,
|
|
23
|
+
This repository currently contains the full Phase 18 Python SDK scope in eleven implementation slices: core SDK surface, buffering, redaction, duplicate suppression, probe buffering, vanilla runtime hooks, framework integrations for Django, Flask, and FastAPI, remote config polling and capture-policy enforcement, optional `structlog` and `loguru` auto-detection when `capture_logging()` is enabled, contract-aligned `EventEnvelope` emission for log, request, exception, suppression, and probe payloads, explicit public wrapper signatures and a validated buildable typed package artifact, real HTTP integration coverage against a lightweight mock ingestion server, vendored machine-readable schema validation for all event types the Python SDK currently emits, a standalone CI workflow that validates Ruff, mypy, pytest, and package builds for the Python 3.10+ support floor actually used by the package, an enforced per-file coverage gate that keeps every shipped Python SDK module at or above the required 80% minimum, request-local framework correlation binding so `X-DebugBundle-Trace-Id` flows through Django, Flask, and FastAPI into the emitted event correlation metadata for cross-context linking, and safe backend runtime process facts on exception payloads without reading environment variables.
|
|
24
|
+
|
|
25
|
+
## Runtime Context
|
|
26
|
+
|
|
27
|
+
Backend exception events now include safe runtime process facts when the host exposes them, including:
|
|
28
|
+
|
|
29
|
+
- Python version
|
|
30
|
+
- platform
|
|
31
|
+
- architecture
|
|
32
|
+
- pid
|
|
33
|
+
- cwd
|
|
34
|
+
- uptime
|
|
35
|
+
- hostname
|
|
36
|
+
- thread id
|
|
37
|
+
- best-effort memory metadata
|
|
38
|
+
|
|
39
|
+
The SDK does not read or emit environment variables in this runtime block.
|
|
24
40
|
|
|
25
41
|
## Docs
|
|
26
42
|
|
|
27
43
|
https://debugbundle.com/docs/sdk-python
|
|
44
|
+
|
|
45
|
+
## License
|
|
46
|
+
|
|
47
|
+
AGPL-3.0-only
|
|
@@ -37,15 +37,15 @@ class RemoteConfigSnapshot:
|
|
|
37
37
|
BALANCED_CAPTURE_POLICY = CapturePolicy(
|
|
38
38
|
preset="balanced",
|
|
39
39
|
capture_logs="warning",
|
|
40
|
-
capture_request_events="
|
|
41
|
-
capture_breadcrumbs="
|
|
42
|
-
capture_probe_events="
|
|
40
|
+
capture_request_events="failures_only",
|
|
41
|
+
capture_breadcrumbs="exception_only",
|
|
42
|
+
capture_probe_events="buffer_only",
|
|
43
43
|
)
|
|
44
44
|
|
|
45
45
|
MINIMAL_CAPTURE_POLICY = CapturePolicy(
|
|
46
46
|
preset="minimal",
|
|
47
|
-
capture_logs="
|
|
48
|
-
capture_request_events="
|
|
47
|
+
capture_logs="error",
|
|
48
|
+
capture_request_events="failures_only",
|
|
49
49
|
capture_breadcrumbs="local_only",
|
|
50
50
|
capture_probe_events="buffer_only",
|
|
51
51
|
)
|
|
@@ -2,9 +2,12 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import logging
|
|
5
|
+
import os
|
|
5
6
|
import platform
|
|
7
|
+
import socket
|
|
6
8
|
import sys
|
|
7
9
|
import threading
|
|
10
|
+
import time
|
|
8
11
|
import traceback
|
|
9
12
|
import uuid
|
|
10
13
|
from collections import deque
|
|
@@ -32,10 +35,16 @@ from .suppression import EventSuppressionTracker
|
|
|
32
35
|
from .transport import HttpTransport, Transport, coerce_transport_response
|
|
33
36
|
from .trigger_token import resolve_request_trigger_directives
|
|
34
37
|
|
|
38
|
+
try:
|
|
39
|
+
import resource
|
|
40
|
+
except ImportError: # pragma: no cover - resource is unavailable on some platforms.
|
|
41
|
+
resource = None # type: ignore[assignment]
|
|
42
|
+
|
|
35
43
|
DEFAULT_BATCH_SIZE = 25
|
|
36
44
|
DEFAULT_FLUSH_INTERVAL = 5.0
|
|
37
45
|
DEFAULT_ENDPOINT = "https://api.debugbundle.com/v1/events"
|
|
38
46
|
DEFAULT_LOG_LEVEL = "warning"
|
|
47
|
+
PROCESS_START_MONOTONIC = time.monotonic()
|
|
39
48
|
SCHEMA_VERSION = "2026-03-01"
|
|
40
49
|
LEVEL_RANKS = {
|
|
41
50
|
"debug": 10,
|
|
@@ -225,7 +234,7 @@ class DebugBundleSdk:
|
|
|
225
234
|
"handled": handled,
|
|
226
235
|
"request": request_payload,
|
|
227
236
|
"response": response_payload,
|
|
228
|
-
"runtime":
|
|
237
|
+
"runtime": _runtime_process_facts(),
|
|
229
238
|
}
|
|
230
239
|
if self._probe_flush_on_error:
|
|
231
240
|
probe_data = self._build_probe_data()
|
|
@@ -590,19 +599,25 @@ class DebugBundleSdk:
|
|
|
590
599
|
|
|
591
600
|
def _should_capture_request_event(self, response: Mapping[str, object] | None) -> bool:
|
|
592
601
|
policy = self._capture_policy.capture_request_events
|
|
602
|
+
status_code = None
|
|
603
|
+
if response is not None:
|
|
604
|
+
candidate = response.get("status_code") or response.get("response_status")
|
|
605
|
+
if isinstance(candidate, int):
|
|
606
|
+
status_code = candidate
|
|
607
|
+
if status_code is not None and status_code >= 500:
|
|
608
|
+
return True
|
|
593
609
|
if policy == "off":
|
|
594
610
|
return False
|
|
595
611
|
if policy == "all":
|
|
596
612
|
return True
|
|
597
613
|
if response is None:
|
|
598
614
|
return policy == "filtered"
|
|
599
|
-
|
|
600
|
-
if not isinstance(status_code, int):
|
|
615
|
+
if status_code is None:
|
|
601
616
|
return policy == "filtered"
|
|
602
617
|
if policy == "failures_only":
|
|
603
|
-
return
|
|
618
|
+
return False
|
|
604
619
|
if policy == "filtered":
|
|
605
|
-
return
|
|
620
|
+
return False
|
|
606
621
|
return True
|
|
607
622
|
|
|
608
623
|
def _emit_probe_events(self, label: str, data: dict[str, object], directives: list[RemoteProbeDirective]) -> None:
|
|
@@ -693,6 +708,51 @@ def _redact_mapping(value: object, redact_fields: set[str]) -> Any:
|
|
|
693
708
|
return value
|
|
694
709
|
|
|
695
710
|
|
|
711
|
+
def _runtime_process_facts() -> dict[str, object]:
|
|
712
|
+
return {
|
|
713
|
+
"version": platform.python_version(),
|
|
714
|
+
"platform": sys.platform,
|
|
715
|
+
"arch": platform.machine() or None,
|
|
716
|
+
"pid": os.getpid(),
|
|
717
|
+
"cwd": _safe_cwd(),
|
|
718
|
+
"uptime_sec": round(max(0.0, time.monotonic() - PROCESS_START_MONOTONIC), 3),
|
|
719
|
+
"hostname": _safe_hostname(),
|
|
720
|
+
"thread_id": threading.get_ident(),
|
|
721
|
+
"memory": _memory_facts(),
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
|
|
725
|
+
def _safe_cwd() -> str | None:
|
|
726
|
+
try:
|
|
727
|
+
return os.getcwd()
|
|
728
|
+
except OSError:
|
|
729
|
+
return None
|
|
730
|
+
|
|
731
|
+
|
|
732
|
+
def _safe_hostname() -> str | None:
|
|
733
|
+
try:
|
|
734
|
+
return socket.gethostname()
|
|
735
|
+
except OSError:
|
|
736
|
+
return None
|
|
737
|
+
|
|
738
|
+
|
|
739
|
+
def _memory_facts() -> dict[str, object]:
|
|
740
|
+
memory: dict[str, object] = {
|
|
741
|
+
"rss": None,
|
|
742
|
+
"heap_total": None,
|
|
743
|
+
"heap_used": None,
|
|
744
|
+
"external": None,
|
|
745
|
+
"peak": None,
|
|
746
|
+
}
|
|
747
|
+
if resource is None:
|
|
748
|
+
return memory
|
|
749
|
+
|
|
750
|
+
usage = resource.getrusage(resource.RUSAGE_SELF)
|
|
751
|
+
# ru_maxrss is KiB on Linux and bytes on macOS/BSD.
|
|
752
|
+
memory["peak"] = usage.ru_maxrss if sys.platform == "darwin" else usage.ru_maxrss * 1024
|
|
753
|
+
return memory
|
|
754
|
+
|
|
755
|
+
|
|
696
756
|
def _backend_exception_request_payload(candidate: object | None) -> dict[str, object]:
|
|
697
757
|
mapping = _dict_from_object(candidate)
|
|
698
758
|
payload: dict[str, object] = {
|
{debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/src/debugbundle_python.egg-info/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: debugbundle-python
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: DebugBundle SDK for Python
|
|
5
5
|
Author: DebugBundle
|
|
6
6
|
License-Expression: AGPL-3.0-only
|
|
@@ -59,8 +59,28 @@ debugbundle.flush()
|
|
|
59
59
|
|
|
60
60
|
## Status
|
|
61
61
|
|
|
62
|
-
This repository currently contains the full Phase 18 Python SDK scope in eleven implementation slices: core SDK surface, buffering, redaction, duplicate suppression, probe buffering, vanilla runtime hooks, framework integrations for Django, Flask, and FastAPI, remote config polling and capture-policy enforcement, optional `structlog` and `loguru` auto-detection when `capture_logging()` is enabled, contract-aligned `EventEnvelope` emission for log, request, exception, suppression, and probe payloads, explicit public wrapper signatures and a validated buildable typed package artifact, real HTTP integration coverage against a lightweight mock ingestion server, vendored machine-readable schema validation for all event types the Python SDK currently emits, a standalone CI workflow that validates Ruff, mypy, pytest, and package builds for the Python 3.10+ support floor actually used by the package, an enforced per-file coverage gate that keeps every shipped Python SDK module at or above the required 80% minimum,
|
|
62
|
+
This repository currently contains the full Phase 18 Python SDK scope in eleven implementation slices: core SDK surface, buffering, redaction, duplicate suppression, probe buffering, vanilla runtime hooks, framework integrations for Django, Flask, and FastAPI, remote config polling and capture-policy enforcement, optional `structlog` and `loguru` auto-detection when `capture_logging()` is enabled, contract-aligned `EventEnvelope` emission for log, request, exception, suppression, and probe payloads, explicit public wrapper signatures and a validated buildable typed package artifact, real HTTP integration coverage against a lightweight mock ingestion server, vendored machine-readable schema validation for all event types the Python SDK currently emits, a standalone CI workflow that validates Ruff, mypy, pytest, and package builds for the Python 3.10+ support floor actually used by the package, an enforced per-file coverage gate that keeps every shipped Python SDK module at or above the required 80% minimum, request-local framework correlation binding so `X-DebugBundle-Trace-Id` flows through Django, Flask, and FastAPI into the emitted event correlation metadata for cross-context linking, and safe backend runtime process facts on exception payloads without reading environment variables.
|
|
63
|
+
|
|
64
|
+
## Runtime Context
|
|
65
|
+
|
|
66
|
+
Backend exception events now include safe runtime process facts when the host exposes them, including:
|
|
67
|
+
|
|
68
|
+
- Python version
|
|
69
|
+
- platform
|
|
70
|
+
- architecture
|
|
71
|
+
- pid
|
|
72
|
+
- cwd
|
|
73
|
+
- uptime
|
|
74
|
+
- hostname
|
|
75
|
+
- thread id
|
|
76
|
+
- best-effort memory metadata
|
|
77
|
+
|
|
78
|
+
The SDK does not read or emit environment variables in this runtime block.
|
|
63
79
|
|
|
64
80
|
## Docs
|
|
65
81
|
|
|
66
82
|
https://debugbundle.com/docs/sdk-python
|
|
83
|
+
|
|
84
|
+
## License
|
|
85
|
+
|
|
86
|
+
AGPL-3.0-only
|
|
@@ -178,14 +178,14 @@ def test_django_middleware_captures_requests_logs_and_errors() -> None:
|
|
|
178
178
|
|
|
179
179
|
factory = RequestFactory()
|
|
180
180
|
|
|
181
|
-
def
|
|
182
|
-
logging.getLogger("django-test").warning("django
|
|
183
|
-
return HttpResponse("
|
|
181
|
+
def server_error_response(request: object) -> HttpResponse:
|
|
182
|
+
logging.getLogger("django-test").warning("django server warning")
|
|
183
|
+
return HttpResponse("failed", status=503)
|
|
184
184
|
|
|
185
|
-
middleware = DebugBundleDjangoMiddleware(
|
|
186
|
-
request = factory.get("/
|
|
185
|
+
middleware = DebugBundleDjangoMiddleware(server_error_response, sdk=sdk)
|
|
186
|
+
request = factory.get("/failed", HTTP_X_DEBUGBUNDLE_TRACE_ID="trace-django")
|
|
187
187
|
response = middleware(request)
|
|
188
|
-
assert response.status_code ==
|
|
188
|
+
assert response.status_code == 503
|
|
189
189
|
|
|
190
190
|
raising_middleware = DebugBundleDjangoMiddleware(
|
|
191
191
|
lambda request: (_ for _ in ()).throw(RuntimeError("django failure")),
|
|
@@ -166,7 +166,7 @@ def test_preserves_correlation_trace_id() -> None:
|
|
|
166
166
|
|
|
167
167
|
def test_accepts_all_supported_event_types() -> None:
|
|
168
168
|
handler = BrowserRelayHandler(allowed_origins=["https://example.com"])
|
|
169
|
-
for event_type in ("frontend_exception", "error_suppressed", "frontend_breadcrumb", "probe_event"):
|
|
169
|
+
for event_type in ("frontend_exception", "error_suppressed", "frontend_breadcrumb", "request_event", "probe_event"):
|
|
170
170
|
evt = _valid_event(event_type)
|
|
171
171
|
response = handler.handle(
|
|
172
172
|
_make_request(body={"batch": [evt]}, ip_address=f"10.0.0.{hash(event_type) % 254 + 1}")
|
|
@@ -174,6 +174,30 @@ def test_accepts_all_supported_event_types() -> None:
|
|
|
174
174
|
assert response.status == 202, f"Failed for event type: {event_type}"
|
|
175
175
|
|
|
176
176
|
|
|
177
|
+
def test_accepts_browser_request_event_payloads() -> None:
|
|
178
|
+
accepted: list[BrowserRelayAcceptedBatch] = []
|
|
179
|
+
handler = BrowserRelayHandler(
|
|
180
|
+
allowed_origins=["https://example.com"],
|
|
181
|
+
on_accept=lambda batch: accepted.append(batch),
|
|
182
|
+
)
|
|
183
|
+
event = _valid_event("request_event")
|
|
184
|
+
event["payload"] = {
|
|
185
|
+
"method": "POST",
|
|
186
|
+
"path": "/v1/billing/checkout",
|
|
187
|
+
"query": {"plan": "team"},
|
|
188
|
+
"headers": {},
|
|
189
|
+
"response_status": 503,
|
|
190
|
+
"duration_ms": 84,
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
response = handler.handle(_make_request(body={"batch": [event]}))
|
|
194
|
+
|
|
195
|
+
assert response.status == 202
|
|
196
|
+
assert len(accepted) == 1
|
|
197
|
+
assert accepted[0].events[0]["event_type"] == "request_event"
|
|
198
|
+
assert accepted[0].events[0]["payload"]["response_status"] == 503
|
|
199
|
+
|
|
200
|
+
|
|
177
201
|
def test_rejects_event_with_missing_required_fields() -> None:
|
|
178
202
|
handler = BrowserRelayHandler(allowed_origins=["https://example.com"])
|
|
179
203
|
bad_event = {"event_type": "frontend_exception"}
|
|
@@ -111,8 +111,8 @@ def test_remote_config_uses_etag_and_activates_heavy_probes_only_while_directive
|
|
|
111
111
|
"capture_policy": {
|
|
112
112
|
"preset": "balanced",
|
|
113
113
|
"capture_logs": "warning",
|
|
114
|
-
"capture_request_events": "
|
|
115
|
-
"capture_breadcrumbs": "
|
|
114
|
+
"capture_request_events": "failures_only",
|
|
115
|
+
"capture_breadcrumbs": "exception_only",
|
|
116
116
|
"capture_probe_events": "standalone_when_activated",
|
|
117
117
|
},
|
|
118
118
|
},
|
|
@@ -173,14 +173,17 @@ def test_failed_init_config_fetch_falls_back_to_minimal_policy() -> None:
|
|
|
173
173
|
probes_poll_interval=25000,
|
|
174
174
|
)
|
|
175
175
|
|
|
176
|
-
sdk.capture_message("warning
|
|
176
|
+
sdk.capture_message("warning blocked", level="warning")
|
|
177
|
+
sdk.capture_message("error still allowed", level="error")
|
|
177
178
|
sdk.capture_message("info blocked", level="info")
|
|
178
179
|
sdk.capture_request({"method": "GET", "path": "/ok", "headers": {}}, {"status_code": 200})
|
|
180
|
+
sdk.capture_request({"method": "GET", "path": "/boom", "headers": {}}, {"status_code": 503})
|
|
179
181
|
sdk.flush()
|
|
180
182
|
|
|
181
183
|
events = _events(transport)
|
|
182
|
-
assert [event["event_type"] for event in events] == ["log_event"]
|
|
183
|
-
assert events[0]["payload"]["message"] == "
|
|
184
|
+
assert [event["event_type"] for event in events] == ["log_event", "request_event"]
|
|
185
|
+
assert events[0]["payload"]["message"] == "error still allowed"
|
|
186
|
+
assert events[1]["payload"]["response_status"] == 503
|
|
184
187
|
|
|
185
188
|
|
|
186
189
|
def test_capture_policy_filters_logs_and_request_events_from_remote_config() -> None:
|
|
@@ -25,6 +25,15 @@ def test_pyproject_points_to_standalone_repository_urls() -> None:
|
|
|
25
25
|
assert 'Issues = "https://github.com/debugbundle/debugbundle-python/issues"' in pyproject
|
|
26
26
|
|
|
27
27
|
|
|
28
|
+
def test_standalone_changelog_and_security_policy_are_launch_ready() -> None:
|
|
29
|
+
changelog = (REPO_ROOT / "CHANGELOG.md").read_text(encoding="utf-8")
|
|
30
|
+
security = (REPO_ROOT / "SECURITY.md").read_text(encoding="utf-8")
|
|
31
|
+
|
|
32
|
+
assert "## [Unreleased]" in changelog
|
|
33
|
+
assert "## [0.1.0] - 2026-05-07" in changelog
|
|
34
|
+
assert "https://github.com/debugbundle/debugbundle-python/security/advisories/new" in security
|
|
35
|
+
|
|
36
|
+
|
|
28
37
|
def test_standalone_ci_workflow_covers_python_sdk_checks() -> None:
|
|
29
38
|
workflow = (REPO_ROOT / ".github/workflows/ci.yml").read_text(encoding="utf-8")
|
|
30
39
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import platform
|
|
4
|
+
import sys
|
|
4
5
|
import uuid
|
|
5
6
|
from dataclasses import dataclass
|
|
6
7
|
|
|
@@ -234,7 +235,18 @@ def test_emits_contract_compliant_event_envelopes() -> None:
|
|
|
234
235
|
assert exception_event["payload"]["handled"] is True
|
|
235
236
|
assert exception_event["payload"]["request"]["path"] == "/checkout"
|
|
236
237
|
assert exception_event["payload"]["response"]["status_code"] == 500
|
|
237
|
-
|
|
238
|
+
runtime = exception_event["payload"]["runtime"]
|
|
239
|
+
assert runtime["version"] == platform.python_version()
|
|
240
|
+
assert runtime["platform"] == sys.platform
|
|
241
|
+
assert isinstance(runtime["arch"], str)
|
|
242
|
+
assert isinstance(runtime["pid"], int)
|
|
243
|
+
assert isinstance(runtime["cwd"], str)
|
|
244
|
+
assert isinstance(runtime["uptime_sec"], (int, float))
|
|
245
|
+
assert runtime["uptime_sec"] >= 0
|
|
246
|
+
assert isinstance(runtime["hostname"], str)
|
|
247
|
+
assert isinstance(runtime["thread_id"], int)
|
|
248
|
+
assert isinstance(runtime["memory"], dict)
|
|
249
|
+
assert "environment" not in runtime
|
|
238
250
|
|
|
239
251
|
|
|
240
252
|
def test_suppresses_duplicate_exceptions_after_the_first_three() -> None:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/src/debugbundle/integrations/__init__.py
RENAMED
|
File without changes
|
{debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/src/debugbundle/integrations/common.py
RENAMED
|
File without changes
|
{debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/src/debugbundle/integrations/django.py
RENAMED
|
File without changes
|
{debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/src/debugbundle/integrations/fastapi.py
RENAMED
|
File without changes
|
|
File without changes
|
{debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/src/debugbundle/integrations/relay_django.py
RENAMED
|
File without changes
|
{debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/src/debugbundle/integrations/relay_fastapi.py
RENAMED
|
File without changes
|
{debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/src/debugbundle/integrations/relay_flask.py
RENAMED
|
File without changes
|
{debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/src/debugbundle/logger_integrations.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/src/debugbundle_python.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/src/debugbundle_python.egg-info/requires.txt
RENAMED
|
File without changes
|
{debugbundle_python-0.1.0 → debugbundle_python-0.1.2}/src/debugbundle_python.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|