debugbundle-python 0.1.0__tar.gz → 0.1.1__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.
Files changed (38) hide show
  1. {debugbundle_python-0.1.0/src/debugbundle_python.egg-info → debugbundle_python-0.1.1}/PKG-INFO +1 -1
  2. {debugbundle_python-0.1.0 → debugbundle_python-0.1.1}/pyproject.toml +1 -1
  3. {debugbundle_python-0.1.0 → debugbundle_python-0.1.1}/src/debugbundle/config.py +5 -5
  4. {debugbundle_python-0.1.0 → debugbundle_python-0.1.1}/src/debugbundle/core.py +10 -4
  5. {debugbundle_python-0.1.0 → debugbundle_python-0.1.1}/src/debugbundle/relay.py +1 -0
  6. {debugbundle_python-0.1.0 → debugbundle_python-0.1.1/src/debugbundle_python.egg-info}/PKG-INFO +1 -1
  7. {debugbundle_python-0.1.0 → debugbundle_python-0.1.1}/tests/test_framework_integrations.py +6 -6
  8. {debugbundle_python-0.1.0 → debugbundle_python-0.1.1}/tests/test_relay.py +25 -1
  9. {debugbundle_python-0.1.0 → debugbundle_python-0.1.1}/tests/test_remote_config.py +8 -5
  10. {debugbundle_python-0.1.0 → debugbundle_python-0.1.1}/tests/test_repository_metadata.py +9 -0
  11. {debugbundle_python-0.1.0 → debugbundle_python-0.1.1}/LICENSE +0 -0
  12. {debugbundle_python-0.1.0 → debugbundle_python-0.1.1}/README.md +0 -0
  13. {debugbundle_python-0.1.0 → debugbundle_python-0.1.1}/setup.cfg +0 -0
  14. {debugbundle_python-0.1.0 → debugbundle_python-0.1.1}/src/debugbundle/__init__.py +0 -0
  15. {debugbundle_python-0.1.0 → debugbundle_python-0.1.1}/src/debugbundle/integrations/__init__.py +0 -0
  16. {debugbundle_python-0.1.0 → debugbundle_python-0.1.1}/src/debugbundle/integrations/common.py +0 -0
  17. {debugbundle_python-0.1.0 → debugbundle_python-0.1.1}/src/debugbundle/integrations/django.py +0 -0
  18. {debugbundle_python-0.1.0 → debugbundle_python-0.1.1}/src/debugbundle/integrations/fastapi.py +0 -0
  19. {debugbundle_python-0.1.0 → debugbundle_python-0.1.1}/src/debugbundle/integrations/flask.py +0 -0
  20. {debugbundle_python-0.1.0 → debugbundle_python-0.1.1}/src/debugbundle/integrations/relay_django.py +0 -0
  21. {debugbundle_python-0.1.0 → debugbundle_python-0.1.1}/src/debugbundle/integrations/relay_fastapi.py +0 -0
  22. {debugbundle_python-0.1.0 → debugbundle_python-0.1.1}/src/debugbundle/integrations/relay_flask.py +0 -0
  23. {debugbundle_python-0.1.0 → debugbundle_python-0.1.1}/src/debugbundle/logger_integrations.py +0 -0
  24. {debugbundle_python-0.1.0 → debugbundle_python-0.1.1}/src/debugbundle/py.typed +0 -0
  25. {debugbundle_python-0.1.0 → debugbundle_python-0.1.1}/src/debugbundle/redaction.py +0 -0
  26. {debugbundle_python-0.1.0 → debugbundle_python-0.1.1}/src/debugbundle/suppression.py +0 -0
  27. {debugbundle_python-0.1.0 → debugbundle_python-0.1.1}/src/debugbundle/transport.py +0 -0
  28. {debugbundle_python-0.1.0 → debugbundle_python-0.1.1}/src/debugbundle/trigger_token.py +0 -0
  29. {debugbundle_python-0.1.0 → debugbundle_python-0.1.1}/src/debugbundle_python.egg-info/SOURCES.txt +0 -0
  30. {debugbundle_python-0.1.0 → debugbundle_python-0.1.1}/src/debugbundle_python.egg-info/dependency_links.txt +0 -0
  31. {debugbundle_python-0.1.0 → debugbundle_python-0.1.1}/src/debugbundle_python.egg-info/requires.txt +0 -0
  32. {debugbundle_python-0.1.0 → debugbundle_python-0.1.1}/src/debugbundle_python.egg-info/top_level.txt +0 -0
  33. {debugbundle_python-0.1.0 → debugbundle_python-0.1.1}/tests/test_contracts.py +0 -0
  34. {debugbundle_python-0.1.0 → debugbundle_python-0.1.1}/tests/test_hooks.py +0 -0
  35. {debugbundle_python-0.1.0 → debugbundle_python-0.1.1}/tests/test_http_integration.py +0 -0
  36. {debugbundle_python-0.1.0 → debugbundle_python-0.1.1}/tests/test_schema_validation.py +0 -0
  37. {debugbundle_python-0.1.0 → debugbundle_python-0.1.1}/tests/test_sdk.py +0 -0
  38. {debugbundle_python-0.1.0 → debugbundle_python-0.1.1}/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.0
3
+ Version: 0.1.1
4
4
  Summary: DebugBundle SDK for Python
5
5
  Author: DebugBundle
6
6
  License-Expression: AGPL-3.0-only
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "debugbundle-python"
7
- version = "0.1.0"
7
+ version = "0.1.1"
8
8
  description = "DebugBundle SDK for Python"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -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="all",
41
- capture_breadcrumbs="local_only",
42
- capture_probe_events="standalone_when_activated",
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="warning",
48
- capture_request_events="off",
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
  )
@@ -590,19 +590,25 @@ class DebugBundleSdk:
590
590
 
591
591
  def _should_capture_request_event(self, response: Mapping[str, object] | None) -> bool:
592
592
  policy = self._capture_policy.capture_request_events
593
+ status_code = None
594
+ if response is not None:
595
+ candidate = response.get("status_code") or response.get("response_status")
596
+ if isinstance(candidate, int):
597
+ status_code = candidate
598
+ if status_code is not None and status_code >= 500:
599
+ return True
593
600
  if policy == "off":
594
601
  return False
595
602
  if policy == "all":
596
603
  return True
597
604
  if response is None:
598
605
  return policy == "filtered"
599
- status_code = response.get("status_code") or response.get("response_status")
600
- if not isinstance(status_code, int):
606
+ if status_code is None:
601
607
  return policy == "filtered"
602
608
  if policy == "failures_only":
603
- return status_code >= 500
609
+ return False
604
610
  if policy == "filtered":
605
- return status_code >= 500
611
+ return False
606
612
  return True
607
613
 
608
614
  def _emit_probe_events(self, label: str, data: dict[str, object], directives: list[RemoteProbeDirective]) -> None:
@@ -15,6 +15,7 @@ ACCEPTED_EVENT_TYPES = frozenset(
15
15
  "frontend_exception",
16
16
  "error_suppressed",
17
17
  "frontend_breadcrumb",
18
+ "request_event",
18
19
  "probe_event",
19
20
  }
20
21
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: debugbundle-python
3
- Version: 0.1.0
3
+ Version: 0.1.1
4
4
  Summary: DebugBundle SDK for Python
5
5
  Author: DebugBundle
6
6
  License-Expression: 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 ok_response(request: object) -> HttpResponse:
182
- logging.getLogger("django-test").warning("django ok warning")
183
- return HttpResponse("ok")
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(ok_response, sdk=sdk)
186
- request = factory.get("/ok", HTTP_X_DEBUGBUNDLE_TRACE_ID="trace-django")
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 == 200
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": "all",
115
- "capture_breadcrumbs": "local_only",
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 still allowed", level="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"] == "warning still allowed"
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