insider-python 0.1.2__py3-none-any.whl → 0.1.3__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.
insider/_version.py CHANGED
@@ -5,4 +5,4 @@ lookup on every beacon. Bump this and `[project].version` together when
5
5
  cutting a release.
6
6
  """
7
7
 
8
- __version__ = "0.1.2"
8
+ __version__ = "0.1.3"
insider/client.py CHANGED
@@ -18,7 +18,7 @@ import atexit
18
18
  import os
19
19
  import subprocess
20
20
  import threading
21
- from typing import Any, Callable, Dict, Iterable, List, Optional
21
+ from typing import Any, Callable, Dict, Iterable, List, Optional, Sequence, Union
22
22
 
23
23
  from ._envelope import build_envelope, enforce_size_budget
24
24
  from ._version import __version__
@@ -33,6 +33,16 @@ from .transport import BackgroundTransport
33
33
  VALID_KINDS = {"error", "perf", "log", "custom"}
34
34
  VALID_LEVELS = {"debug", "info", "warning", "error", "fatal"}
35
35
 
36
+ IntegrationLike = Union[Any, type]
37
+
38
+
39
+ def _setup_integrations(integrations: Sequence[IntegrationLike]) -> None:
40
+ for integration in integrations:
41
+ instance = integration() if isinstance(integration, type) else integration
42
+ setup_once = getattr(instance, "setup_once", None)
43
+ if callable(setup_once):
44
+ setup_once()
45
+
36
46
 
37
47
  # ---------------------------------------------------------------------------
38
48
  # DSN resolution
@@ -259,6 +269,8 @@ def _set_active(client: Optional[Client]) -> None:
259
269
  @safe
260
270
  def init(
261
271
  dsn: Optional[str] = None,
272
+ *,
273
+ integrations: Optional[Sequence[IntegrationLike]] = None,
262
274
  **kwargs: Any,
263
275
  ) -> Optional[Client]:
264
276
  """
@@ -268,6 +280,9 @@ def init(
268
280
  Calling `init` a second time is allowed but logs a warning and
269
281
  closes the previous client first. The new client becomes the
270
282
  process-global one.
283
+
284
+ Pass framework integrations via `integrations=[...]`. Each integration's
285
+ `setup_once()` runs after the client is active.
271
286
  """
272
287
  global _active_client
273
288
  raw = _resolve_dsn_string(dsn)
@@ -280,6 +295,8 @@ def init(
280
295
  debug(f"invalid DSN: {exc}; entering disabled mode")
281
296
  return None
282
297
 
298
+ integration_list = list(integrations or [])
299
+
283
300
  with _init_lock:
284
301
  if _active_client is not None:
285
302
  debug("re-initializing; closing previous client")
@@ -290,6 +307,7 @@ def init(
290
307
  client = Client(parsed, **kwargs)
291
308
  _set_active(client)
292
309
 
310
+ _setup_integrations(integration_list)
293
311
  atexit.register(_atexit_close)
294
312
  return client
295
313
 
@@ -13,6 +13,7 @@ from typing import Any, Dict
13
13
  from django.apps import AppConfig
14
14
 
15
15
  from ... import init
16
+ from ...integrations.django import DjangoIntegration
16
17
  from ...safety import debug
17
18
 
18
19
 
@@ -57,4 +58,4 @@ class InsiderConfig(AppConfig):
57
58
  # env-var fallback in `init` still applies if Django's settings
58
59
  # didn't define it.
59
60
  dsn = kwargs.pop("dsn", None)
60
- init(dsn, **kwargs)
61
+ init(dsn, integrations=[DjangoIntegration()], **kwargs)
@@ -4,6 +4,10 @@ auto-capture any unhandled exception that escapes a view.
4
4
 
5
5
  The middleware is a no-op when the SDK is in disabled mode.
6
6
 
7
+ Prefer the Sentry-style integration instead — see
8
+ `insider.integrations.django.DjangoIntegration` and `wsgi.py` init.
9
+ This middleware remains for backward compatibility.
10
+
7
11
  What we attach to the scope:
8
12
 
9
13
  - method, path, route (URL name if available), query_string
@@ -25,31 +29,12 @@ None so Django continues its normal 500 handling.
25
29
 
26
30
  from __future__ import annotations
27
31
 
28
- from typing import Any, Callable, Dict, Optional
32
+ from typing import Any, Callable
29
33
 
30
- from ... import capture_exception
31
34
  from ...client import _client
32
- from ...safety import debug, safe
33
-
34
-
35
- # Request headers that are safe to forward to the dashboard. We keep an
36
- # allow-list rather than a deny-list because there are too many possible
37
- # custom headers to enumerate scary ones. Scrubbing further masks names
38
- # matching the default deny-list (Authorization, Cookie, etc.) at envelope
39
- # build time.
40
- _SAFE_HEADERS = {
41
- "accept",
42
- "accept-encoding",
43
- "accept-language",
44
- "content-type",
45
- "content-length",
46
- "host",
47
- "referer",
48
- "user-agent",
49
- "x-forwarded-for",
50
- "x-real-ip",
51
- "x-request-id",
52
- }
35
+ from ...integrations.django.capture import capture_request_exception
36
+ from ...integrations.django.request import build_request_ctx
37
+ from ...safety import safe
53
38
 
54
39
 
55
40
  class InsiderMiddleware:
@@ -67,7 +52,7 @@ class InsiderMiddleware:
67
52
  if client is None:
68
53
  return self.get_response(request)
69
54
 
70
- ctx = self._build_request_ctx(request, client.send_default_pii)
55
+ ctx = build_request_ctx(request, client.send_default_pii)
71
56
  client.scope.set_request(ctx)
72
57
  try:
73
58
  return self.get_response(request)
@@ -76,89 +61,5 @@ class InsiderMiddleware:
76
61
 
77
62
  @safe
78
63
  def process_exception(self, request: Any, exception: BaseException) -> None:
79
- # Scope's already set in __call__; capture inherits the request ctx.
80
- capture_exception(exception)
81
- return None # let Django render the 500
82
-
83
- # ------------------------------------------------------------------
84
-
85
- @staticmethod
86
- def _build_request_ctx(request: Any, send_default_pii: bool) -> Dict[str, Any]:
87
- try:
88
- method = getattr(request, "method", None)
89
- path = getattr(request, "path", None)
90
- query = getattr(request, "META", {}).get("QUERY_STRING") or None
91
- route = None
92
- try:
93
- # ResolverMatch is set after urls resolve; in middleware
94
- # __call__ before view, it's usually None. We still try.
95
- match = getattr(request, "resolver_match", None)
96
- if match is not None:
97
- route = match.view_name
98
- except Exception:
99
- pass
100
-
101
- headers = _extract_headers(getattr(request, "META", {}))
102
-
103
- ctx: Dict[str, Any] = {
104
- "method": method,
105
- "path": path,
106
- "query_string": query,
107
- "route": route,
108
- "headers": headers,
109
- }
110
-
111
- if send_default_pii:
112
- ctx["body"] = _read_body(request)
113
- user = getattr(request, "user", None)
114
- user_id = getattr(user, "id", None) if user is not None else None
115
- if user_id is not None:
116
- ctx["user"] = {"id": user_id}
117
-
118
- return ctx
119
- except Exception as exc:
120
- debug(f"request ctx build failed: {exc}")
121
- return {}
122
-
123
-
124
- def _extract_headers(meta: Dict[str, Any]) -> Dict[str, str]:
125
- """Convert Django's META dict into a real headers dict, allow-listed."""
126
- headers: Dict[str, str] = {}
127
- for key, value in meta.items():
128
- if not key.startswith("HTTP_") and key not in (
129
- "CONTENT_TYPE",
130
- "CONTENT_LENGTH",
131
- ):
132
- continue
133
- name = key
134
- if key.startswith("HTTP_"):
135
- name = key[len("HTTP_") :]
136
- name = name.replace("_", "-").lower()
137
- if name not in _SAFE_HEADERS:
138
- # We still include unknown headers; the scrubber will mask any
139
- # whose name is in the deny-list. But we cap obviously huge or
140
- # weird ones here.
141
- if len(str(value)) > 4096:
142
- continue
143
- try:
144
- headers[name] = str(value)
145
- except Exception:
146
- pass
147
- return headers
148
-
149
-
150
- def _read_body(request: Any) -> Optional[str]:
151
- """
152
- Return a string version of the request body, or None.
153
- We don't consume `request.body` if it hasn't been read yet, to avoid
154
- breaking downstream views; if it's accessible, we take it.
155
- """
156
- try:
157
- raw = getattr(request, "body", None)
158
- if raw is None:
159
- return None
160
- if isinstance(raw, bytes):
161
- return raw.decode("utf-8", errors="replace")
162
- return str(raw)
163
- except Exception:
64
+ capture_request_exception(request, exception)
164
65
  return None
@@ -0,0 +1,20 @@
1
+ """
2
+ Framework integrations for Insider.
3
+
4
+ Each integration exposes a `setup_once()` hook that patches into the host
5
+ framework exactly once per process. Pass instances to `insider.init`:
6
+
7
+ insider.init(dsn=..., integrations=[DjangoIntegration()])
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from typing import Protocol, runtime_checkable
13
+
14
+
15
+ @runtime_checkable
16
+ class Integration(Protocol):
17
+ """Minimal integration contract."""
18
+
19
+ def setup_once(self) -> None:
20
+ """Install hooks into the host framework. Idempotent."""
@@ -0,0 +1,50 @@
1
+ """
2
+ Sentry-style Django integration.
3
+
4
+ Install with `insider.init(..., integrations=[DjangoIntegration()])` in
5
+ `wsgi.py` / `asgi.py` before `get_wsgi_application()`. No middleware or
6
+ `INSTALLED_APPS` wiring required.
7
+
8
+ Hooks installed (each once per process):
9
+
10
+ - `got_request_exception` → auto-capture unhandled view errors
11
+ - `BaseHandler.get_response` → request context on the SDK scope
12
+ - `WSGIHandler.__call__` → capture catastrophic escapes
13
+ - `APIView.initial` (when DRF is present) → DRF request body access
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import threading
19
+
20
+ from ...safety import debug, safe
21
+ from . import drf, handler, signals, wsgi
22
+
23
+
24
+ class DjangoIntegration:
25
+ """Patch Django's request/exception path for automatic error capture."""
26
+
27
+ identifier = "django"
28
+
29
+ _lock = threading.Lock()
30
+ _installed = False
31
+
32
+ @safe
33
+ def setup_once(self) -> None:
34
+ cls = type(self)
35
+ with cls._lock:
36
+ if cls._installed:
37
+ return
38
+ cls._installed = True
39
+
40
+ try:
41
+ import django # noqa: F401
42
+ except ImportError:
43
+ debug("DjangoIntegration: django is not installed")
44
+ cls._installed = False
45
+ return
46
+
47
+ signals.install()
48
+ handler.install()
49
+ wsgi.install()
50
+ drf.install()
@@ -0,0 +1,43 @@
1
+ """
2
+ Shared request-exception capture with de-duplication.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import Any
8
+
9
+ from ... import capture_exception
10
+ from ...client import _client
11
+ from ...safety import safe
12
+ from .request import build_request_ctx
13
+
14
+ _CAPTURED_ATTR = "_insider_exception_captured"
15
+
16
+
17
+ @safe
18
+ def capture_request_exception(request: Any, exception: BaseException) -> None:
19
+ """
20
+ Capture an unhandled request exception once per request.
21
+
22
+ Middleware `process_exception` and Django's `got_request_exception`
23
+ signal can both fire for the same failure; the request flag prevents
24
+ double-beaming.
25
+ """
26
+ if getattr(request, _CAPTURED_ATTR, False):
27
+ return
28
+ setattr(request, _CAPTURED_ATTR, True)
29
+
30
+ client = _client()
31
+ if client is None:
32
+ return
33
+
34
+ # Scope may already be set by middleware or the get_response patch.
35
+ if client.scope.current_request() is None:
36
+ ctx = build_request_ctx(request, client.send_default_pii)
37
+ client.scope.set_request(ctx)
38
+ try:
39
+ capture_exception(exception)
40
+ finally:
41
+ client.scope.clear_request()
42
+ else:
43
+ capture_exception(exception)
@@ -0,0 +1,40 @@
1
+ """
2
+ Optional DRF patch: link DRF Request objects for accurate body reads.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import Any
8
+
9
+ from ...safety import debug, safe
10
+ from .request import attach_drf_request_backref
11
+
12
+ _patched = False
13
+
14
+
15
+ def install() -> None:
16
+ global _patched
17
+ if _patched:
18
+ return
19
+ try:
20
+ from rest_framework.views import APIView
21
+ except ImportError:
22
+ return
23
+
24
+ old_initial = APIView.initial
25
+
26
+ @safe
27
+ def patched_initial(
28
+ self: Any,
29
+ request: Any,
30
+ *args: Any,
31
+ **kwargs: Any,
32
+ ) -> Any:
33
+ try:
34
+ attach_drf_request_backref(request._request, request)
35
+ except Exception as exc:
36
+ debug(f"drf initial backref failed: {exc}")
37
+ return old_initial(self, request, *args, **kwargs)
38
+
39
+ APIView.initial = patched_initial # type: ignore[method-assign]
40
+ _patched = True
@@ -0,0 +1,42 @@
1
+ """
2
+ Patch `BaseHandler.get_response` to attach request context for the request lifetime.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import Any, Callable
8
+
9
+ from ...client import _client
10
+ from ...safety import debug, safe
11
+ from .request import build_request_ctx
12
+
13
+ _patched = False
14
+
15
+
16
+ def install() -> None:
17
+ global _patched
18
+ if _patched:
19
+ return
20
+ try:
21
+ from django.core.handlers.base import BaseHandler
22
+ except ImportError:
23
+ debug("django BaseHandler unavailable; skipping get_response patch")
24
+ return
25
+
26
+ old_get_response = BaseHandler.get_response
27
+
28
+ @safe
29
+ def patched_get_response(self: Any, request: Any) -> Any:
30
+ client = _client()
31
+ if client is None:
32
+ return old_get_response(self, request)
33
+
34
+ ctx = build_request_ctx(request, client.send_default_pii)
35
+ client.scope.set_request(ctx)
36
+ try:
37
+ return old_get_response(self, request)
38
+ finally:
39
+ client.scope.clear_request()
40
+
41
+ BaseHandler.get_response = patched_get_response # type: ignore[method-assign]
42
+ _patched = True
@@ -0,0 +1,140 @@
1
+ """
2
+ Build Insider request-context dicts from Django (and DRF) request objects.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ import weakref
8
+ from typing import Any, Dict, Optional
9
+
10
+ from ...safety import debug
11
+
12
+ # Request headers that are safe to forward to the dashboard. We keep an
13
+ # allow-list rather than a deny-list because there are too many possible
14
+ # custom headers to enumerate scary ones. Scrubbing further masks names
15
+ # matching the default deny-list (Authorization, Cookie, etc.) at envelope
16
+ # build time.
17
+ _SAFE_HEADERS = {
18
+ "accept",
19
+ "accept-encoding",
20
+ "accept-language",
21
+ "content-type",
22
+ "content-length",
23
+ "host",
24
+ "referer",
25
+ "user-agent",
26
+ "x-forwarded-for",
27
+ "x-real-ip",
28
+ "x-request-id",
29
+ }
30
+
31
+ _DRF_BACKREF_ATTR = "_insider_drf_request_backref"
32
+
33
+
34
+ def attach_drf_request_backref(django_request: Any, drf_request: Any) -> None:
35
+ """Link a DRF Request to its wrapped Django request for body reads."""
36
+ try:
37
+ setattr(django_request, _DRF_BACKREF_ATTR, weakref.ref(drf_request))
38
+ except Exception as exc:
39
+ debug(f"drf request backref failed: {exc}")
40
+
41
+
42
+ def build_request_ctx(request: Any, send_default_pii: bool) -> Dict[str, Any]:
43
+ try:
44
+ request = _resolve_drf_request(request)
45
+
46
+ method = getattr(request, "method", None)
47
+ path = getattr(request, "path", None)
48
+ meta = getattr(request, "META", {})
49
+ query = meta.get("QUERY_STRING") or None
50
+ route = None
51
+ try:
52
+ match = getattr(request, "resolver_match", None)
53
+ if match is not None:
54
+ route = match.view_name
55
+ except Exception:
56
+ pass
57
+
58
+ headers = _extract_headers(meta)
59
+
60
+ ctx: Dict[str, Any] = {
61
+ "method": method,
62
+ "path": path,
63
+ "query_string": query,
64
+ "route": route,
65
+ "headers": headers,
66
+ }
67
+
68
+ if send_default_pii:
69
+ ctx["body"] = _read_body(request)
70
+ user = getattr(request, "user", None)
71
+ user_id = getattr(user, "id", None) if user is not None else None
72
+ if user_id is not None:
73
+ ctx["user"] = {"id": user_id}
74
+
75
+ return ctx
76
+ except Exception as exc:
77
+ debug(f"request ctx build failed: {exc}")
78
+ return {}
79
+
80
+
81
+ def _resolve_drf_request(request: Any) -> Any:
82
+ """Prefer the DRF Request when a weak backref was attached in `initial`."""
83
+ try:
84
+ backref = getattr(request, _DRF_BACKREF_ATTR, None)
85
+ if backref is not None:
86
+ drf_request = backref()
87
+ if drf_request is not None:
88
+ return drf_request
89
+ except Exception:
90
+ pass
91
+ return request
92
+
93
+
94
+ def _extract_headers(meta: Dict[str, Any]) -> Dict[str, str]:
95
+ """Convert Django's META dict into a real headers dict, allow-listed."""
96
+ headers: Dict[str, str] = {}
97
+ for key, value in meta.items():
98
+ if not key.startswith("HTTP_") and key not in (
99
+ "CONTENT_TYPE",
100
+ "CONTENT_LENGTH",
101
+ ):
102
+ continue
103
+ name = key
104
+ if key.startswith("HTTP_"):
105
+ name = key[len("HTTP_") :]
106
+ name = name.replace("_", "-").lower()
107
+ if name not in _SAFE_HEADERS:
108
+ if len(str(value)) > 4096:
109
+ continue
110
+ try:
111
+ headers[name] = str(value)
112
+ except Exception:
113
+ pass
114
+ return headers
115
+
116
+
117
+ def _read_body(request: Any) -> Optional[str]:
118
+ """
119
+ Return a string version of the request body, or None.
120
+ We don't consume `request.body` if it hasn't been read yet, to avoid
121
+ breaking downstream views; if it's accessible, we take it.
122
+ """
123
+ try:
124
+ data = getattr(request, "data", None)
125
+ if data is not None and not isinstance(data, (str, bytes)):
126
+ try:
127
+ import json
128
+
129
+ return json.dumps(data)
130
+ except Exception:
131
+ return str(data)
132
+
133
+ raw = getattr(request, "body", None)
134
+ if raw is None:
135
+ return None
136
+ if isinstance(raw, bytes):
137
+ return raw.decode("utf-8", errors="replace")
138
+ return str(raw)
139
+ except Exception:
140
+ return None
@@ -0,0 +1,39 @@
1
+ """
2
+ Connect Django's `got_request_exception` signal to Insider capture.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ import sys
8
+ from typing import Any
9
+
10
+ from ...safety import debug, safe
11
+ from .capture import capture_request_exception
12
+
13
+ _connected = False
14
+
15
+
16
+ def install() -> None:
17
+ global _connected
18
+ if _connected:
19
+ return
20
+ try:
21
+ from django.core import signals
22
+ except ImportError:
23
+ debug("django signals unavailable; skipping got_request_exception hook")
24
+ return
25
+
26
+ signals.got_request_exception.connect(_on_got_request_exception)
27
+ _connected = True
28
+
29
+
30
+ @safe
31
+ def _on_got_request_exception(
32
+ sender: Any,
33
+ request: Any,
34
+ **kwargs: Any,
35
+ ) -> None:
36
+ exc = sys.exc_info()[1]
37
+ if exc is None:
38
+ return
39
+ capture_request_exception(request, exc)
@@ -0,0 +1,43 @@
1
+ """
2
+ Patch `WSGIHandler.__call__` to capture exceptions that escape Django entirely.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import Any, Callable
8
+
9
+ from ... import capture_exception
10
+ from ...client import _client
11
+ from ...safety import debug, safe
12
+
13
+ _patched = False
14
+
15
+
16
+ def install() -> None:
17
+ global _patched
18
+ if _patched:
19
+ return
20
+ try:
21
+ from django.core.handlers.wsgi import WSGIHandler
22
+ except ImportError:
23
+ debug("django WSGIHandler unavailable; skipping WSGI patch")
24
+ return
25
+
26
+ old_call: Callable[..., Any] = WSGIHandler.__call__
27
+
28
+ @safe
29
+ def patched_call(
30
+ self: Any,
31
+ environ: Any,
32
+ start_response: Any,
33
+ ) -> Any:
34
+ if _client() is None:
35
+ return old_call(self, environ, start_response)
36
+ try:
37
+ return old_call(self, environ, start_response)
38
+ except BaseException as exc:
39
+ capture_exception(exc)
40
+ raise
41
+
42
+ WSGIHandler.__call__ = patched_call # type: ignore[method-assign]
43
+ _patched = True
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: insider-python
3
- Version: 0.1.2
3
+ Version: 0.1.3
4
4
  Summary: Python SDK for Insider — ship Beacons to your Insider server.
5
5
  Author: Insider
6
6
  License-Expression: MIT
@@ -24,6 +24,7 @@ Provides-Extra: dev
24
24
  Requires-Dist: pytest>=8; extra == "dev"
25
25
  Requires-Dist: pytest-django>=4.8; extra == "dev"
26
26
  Requires-Dist: django>=4.2; extra == "dev"
27
+ Requires-Dist: djangorestframework>=3.14; extra == "dev"
27
28
 
28
29
  # insider-python
29
30
 
@@ -68,26 +69,31 @@ insider.capture_message("cache miss spiked", level="warning")
68
69
 
69
70
  ### Django
70
71
 
71
- Add the integration to `INSTALLED_APPS` and configure via settings:
72
+ Initialize in `wsgi.py` (or `asgi.py`) before `get_wsgi_application()`:
72
73
 
73
74
  ```python
74
- INSTALLED_APPS = [
75
- # ...
76
- "insider.contrib.django",
77
- ]
78
-
79
- MIDDLEWARE = [
80
- # ...
81
- "insider.contrib.django.middleware.InsiderMiddleware",
82
- ]
83
-
84
- INSIDER_DSN = "https://<beacon_token>@insider.example.com/<project_uuid>"
85
- INSIDER_ENVIRONMENT = "production"
86
- INSIDER_RELEASE = "1.2.3"
75
+ import os
76
+ import insider
77
+ from insider.integrations.django import DjangoIntegration
78
+
79
+ insider.init(
80
+ dsn=os.environ.get("INSIDER_DSN"),
81
+ environment="production",
82
+ release="1.2.3",
83
+ integrations=[DjangoIntegration()],
84
+ )
85
+
86
+ from django.core.wsgi import get_wsgi_application
87
+ application = get_wsgi_application()
87
88
  ```
88
89
 
89
- That's the whole setup. Every unhandled exception in a view is now a
90
- Beacon in your dashboard.
90
+ That's the whole setup. Every unhandled exception in a view including
91
+ Django REST Framework API views — is now a Beacon in your dashboard.
92
+ No middleware, no `INSTALLED_APPS`, and no `EXCEPTION_HANDLER` wiring.
93
+
94
+ **Legacy setup** (still supported): add `insider.contrib.django` to
95
+ `INSTALLED_APPS` and `InsiderMiddleware` to `MIDDLEWARE`. Prefer the
96
+ `wsgi.py` pattern for new projects.
91
97
 
92
98
  ## Configuration
93
99
 
@@ -0,0 +1,27 @@
1
+ insider/__init__.py,sha256=DgDevX2wcy9lonZiHaq5dJLieD3_fp3p6l3lotuTpGE,781
2
+ insider/_envelope.py,sha256=mZiNl-Cha4piAhbIgFuU_KOZnaK4GMrYquFJxLn0D5Q,6684
3
+ insider/_version.py,sha256=tSvSo1IuInvJvdgaCfYY3AwA4nRKslqnuXXy0IEtaZE,246
4
+ insider/client.py,sha256=MkwlGp6iIMNsoHtg9pDWX1OqxetC27rmODh5y5qQtyI,12196
5
+ insider/dsn.py,sha256=S8BbRhmKKKU1N6W8cpMSQoyDO_EuUJ1PkcjhGrrf5hw,3009
6
+ insider/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ insider/safety.py,sha256=mtzzWLujuvmd24W2VtVcMGO_81GPBbB2tYuVqU2KsuI,1910
8
+ insider/scope.py,sha256=F1eMWt6uu14uBdncc5DC6Lh735EPcUv1yF4prBi--pc,1708
9
+ insider/scrubbing.py,sha256=U8N3MS5VtDUNpIWN4PZ8bvrPqmjRRXq5hWGXxzoF5Gw,2853
10
+ insider/stacktrace.py,sha256=tgdS9dQ-bVyz59_874BstuCYaP6rGn2k6HMo9sW8Ks8,5310
11
+ insider/transport.py,sha256=8ncFb3C0LgF2hMRoj_fhxj49NIUD6hsVAHfPZKtNcn4,7292
12
+ insider/contrib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
+ insider/contrib/django/__init__.py,sha256=UMGrJgVO-FqszZo9Cviw7oWS8PLdXVHt3hwXZ_YMj8A,680
14
+ insider/contrib/django/apps.py,sha256=Ct584Pa-S9I82omP3t_Xsn4NrdlqfoNf0eUN9xFa7Hs,2036
15
+ insider/contrib/django/middleware.py,sha256=lX5sGDZw4rJ6gsNEcOpqHspkvzqilxTlUxOszQxwzpI,2077
16
+ insider/integrations/__init__.py,sha256=MrpszNOGT5-2b2310wS3GDjIQWQ7R_-S-OYIMZ5iu1E,526
17
+ insider/integrations/django/__init__.py,sha256=EIUHPMhBSw4pXWaXiteYh5lzThYEzE9gAZBfDypnu4M,1325
18
+ insider/integrations/django/capture.py,sha256=UQfOdHvbD2mhHaTntQq-mXMDpho199V7j6BR6k6ppWY,1182
19
+ insider/integrations/django/drf.py,sha256=MXY4erP9gPBiEvf8Hi0CLrUBczXY1o7Y5wVCjSsQeCY,899
20
+ insider/integrations/django/handler.py,sha256=V2lUvkqWBtechnd2Kj6wToxmWZqpbG7E8BKOxYap-fc,1103
21
+ insider/integrations/django/request.py,sha256=pOuQxnFWeA3KPAc0eVaRtkxGRyr2PetGKEg7OlTnqF4,4181
22
+ insider/integrations/django/signals.py,sha256=kwH7npDG3AC9MILB9f4YUYofy5iwOosapGkBm_3dZY4,802
23
+ insider/integrations/django/wsgi.py,sha256=kJ5qo5LUdRYJtp4NkiD38DZzbb__idJU6FAltYhgtus,1044
24
+ insider_python-0.1.3.dist-info/METADATA,sha256=TL3DKG0aJQXFcKFZJi1Ri6F3BrDaTATEDECgWCtt0t8,3971
25
+ insider_python-0.1.3.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
26
+ insider_python-0.1.3.dist-info/top_level.txt,sha256=g_YKp2jCaaefmasZ2nOa9capm0X8q2sAWI_eEClKIos,8
27
+ insider_python-0.1.3.dist-info/RECORD,,
@@ -1,19 +0,0 @@
1
- insider/__init__.py,sha256=DgDevX2wcy9lonZiHaq5dJLieD3_fp3p6l3lotuTpGE,781
2
- insider/_envelope.py,sha256=mZiNl-Cha4piAhbIgFuU_KOZnaK4GMrYquFJxLn0D5Q,6684
3
- insider/_version.py,sha256=lxOKSZJbU9hAXhlPRaq8AWb80mXq8wTnJsQhaOYn7ao,246
4
- insider/client.py,sha256=qLl7iQQ1N4zygXisGxOOyXNkq16UuWozhEHPGK_24Xs,11540
5
- insider/dsn.py,sha256=S8BbRhmKKKU1N6W8cpMSQoyDO_EuUJ1PkcjhGrrf5hw,3009
6
- insider/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
- insider/safety.py,sha256=mtzzWLujuvmd24W2VtVcMGO_81GPBbB2tYuVqU2KsuI,1910
8
- insider/scope.py,sha256=F1eMWt6uu14uBdncc5DC6Lh735EPcUv1yF4prBi--pc,1708
9
- insider/scrubbing.py,sha256=U8N3MS5VtDUNpIWN4PZ8bvrPqmjRRXq5hWGXxzoF5Gw,2853
10
- insider/stacktrace.py,sha256=tgdS9dQ-bVyz59_874BstuCYaP6rGn2k6HMo9sW8Ks8,5310
11
- insider/transport.py,sha256=8ncFb3C0LgF2hMRoj_fhxj49NIUD6hsVAHfPZKtNcn4,7292
12
- insider/contrib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
- insider/contrib/django/__init__.py,sha256=UMGrJgVO-FqszZo9Cviw7oWS8PLdXVHt3hwXZ_YMj8A,680
14
- insider/contrib/django/apps.py,sha256=WOKn-fNp_SSPNyt8xXz_fXYVhkyepafAi1uxpWC9_Bo,1947
15
- insider/contrib/django/middleware.py,sha256=JWnP_p4JBGyKQMswevjWxoUBSE5o4xgLKYOm1Zrb30g,5365
16
- insider_python-0.1.2.dist-info/METADATA,sha256=ag7XgxFizqFCl4TuPkSlcid8kCp3qvdj6dzyj5Kr0bM,3581
17
- insider_python-0.1.2.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
18
- insider_python-0.1.2.dist-info/top_level.txt,sha256=g_YKp2jCaaefmasZ2nOa9capm0X8q2sAWI_eEClKIos,8
19
- insider_python-0.1.2.dist-info/RECORD,,