xecurecode-python-sdk 0.1.0__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.
xecurecode/__init__.py ADDED
@@ -0,0 +1,17 @@
1
+ """
2
+ XecureCode SDK for Python
3
+ """
4
+
5
+ from .config import ReliabilityConfig
6
+ from .client import ReliabilityClient, init, get_client
7
+ from .payload import create_error_payload
8
+
9
+ __version__ = "0.1.0"
10
+
11
+ __all__ = [
12
+ "ReliabilityConfig",
13
+ "ReliabilityClient",
14
+ "create_error_payload",
15
+ "init",
16
+ "get_client",
17
+ ]
xecurecode/client.py ADDED
@@ -0,0 +1,223 @@
1
+ """
2
+ Reliability Client for Python SDK
3
+ """
4
+
5
+ import asyncio
6
+ import json
7
+ import logging
8
+ import os
9
+ import sys
10
+ import threading
11
+ import time
12
+ from concurrent.futures import ThreadPoolExecutor
13
+ from typing import Any, Dict, Optional
14
+ from urllib.request import Request, urlopen
15
+ from urllib.error import URLError
16
+
17
+ from .config import ReliabilityConfig
18
+ from .payload import create_error_payload
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+ DEFAULT_ENDPOINT = "https://xecurecode-backend.agreeablemeadow-24a6afd4.centralindia.azurecontainerapps.io/api/v1/ingest"
23
+
24
+ DEDUP_WINDOW_MS = 60000
25
+ MIN_INTERVAL_MS = 100
26
+ MAX_PENDING = 100
27
+
28
+
29
+ class ReliabilityClient:
30
+ """Main reliability client for capturing and sending errors"""
31
+
32
+ def __init__(self, config: ReliabilityConfig):
33
+ self.config = config
34
+ self._executor = ThreadPoolExecutor(
35
+ max_workers=1, thread_name_prefix="reliability-sender"
36
+ )
37
+
38
+ # Rate limiting
39
+ self._last_sent_time = 0
40
+ self._pending_sends = 0
41
+ self._lock = threading.Lock()
42
+
43
+ # Deduplication cache
44
+ self._error_cache: Dict[str, float] = {}
45
+ self._cache_cleanup_interval = 60
46
+ self._start_cache_cleanup()
47
+
48
+ # Global exception handlers
49
+ self._setup_global_handlers()
50
+
51
+ logger.info(f"ReliabilityClient initialized for service: {config.service_id}")
52
+
53
+ def _setup_global_handlers(self):
54
+ """Setup global exception handlers"""
55
+ # Store original handlers
56
+ self._original_excepthook = sys.excepthook
57
+ self._original_thread_excepthook = threading.excepthook
58
+
59
+ # Set custom handlers
60
+ sys.excepthook = self._custom_excepthook
61
+ threading.excepthook = self._custom_thread_excepthook
62
+
63
+ def _custom_excepthook(self, exc_type, exc_value, exc_traceback):
64
+ """Custom uncaught exception handler"""
65
+ if issubclass(exc_type, Exception):
66
+ self.capture(exc_value)
67
+ # Call original handler
68
+ if self._original_excepthook:
69
+ self._original_excepthook(exc_type, exc_value, exc_traceback)
70
+
71
+ def _custom_thread_excepthook(self, args):
72
+ """Custom thread exception handler"""
73
+ if args.exc_type and issubclass(args.exc_type, Exception):
74
+ self.capture(args.exc_value)
75
+ if self._original_thread_excepthook:
76
+ self._original_thread_excepthook(args)
77
+
78
+ def _start_cache_cleanup(self):
79
+ """Start background cache cleanup"""
80
+
81
+ def cleanup():
82
+ while True:
83
+ time.sleep(self._cache_cleanup_interval)
84
+ self._cleanup_cache()
85
+
86
+ cleanup_thread = threading.Thread(target=cleanup, daemon=True)
87
+ cleanup_thread.start()
88
+
89
+ def _cleanup_cache(self):
90
+ """Remove old entries from cache"""
91
+ now = time.time() * 1000
92
+ expired_keys = [
93
+ k for k, v in self._error_cache.items() if now - v > DEDUP_WINDOW_MS
94
+ ]
95
+ for k in expired_keys:
96
+ self._error_cache.pop(k, None)
97
+
98
+ def _is_duplicate(self, fingerprint: str) -> bool:
99
+ """Check if error is duplicate"""
100
+ now = time.time() * 1000
101
+ last_time = self._error_cache.get(fingerprint)
102
+
103
+ if last_time is None:
104
+ self._error_cache[fingerprint] = now
105
+ return False
106
+
107
+ if now - last_time > DEDUP_WINDOW_MS:
108
+ self._error_cache[fingerprint] = now
109
+ return False
110
+
111
+ return True
112
+
113
+ def _can_send(self) -> bool:
114
+ """Check if we can send (rate limiting)"""
115
+ now = time.time() * 1000
116
+ with self._lock:
117
+ if now - self._last_sent_time < MIN_INTERVAL_MS:
118
+ return False
119
+ if self._pending_sends >= MAX_PENDING:
120
+ return False
121
+ return True
122
+
123
+ def capture(self, error: Exception, request: Any = None):
124
+ """Capture an error"""
125
+ if error is None:
126
+ return
127
+
128
+ payload = create_error_payload(
129
+ error, self.config.service_id, self.config.mode, request
130
+ )
131
+
132
+ if self._is_duplicate(payload["fingerprint"]):
133
+ logger.debug(f"Duplicate error, skipping: {error}")
134
+ return
135
+
136
+ if not self._can_send():
137
+ logger.debug(f"Rate limited, skipping: {error}")
138
+ return
139
+
140
+ with self._lock:
141
+ self._pending_sends += 1
142
+ self._last_sent_time = time.time() * 1000
143
+
144
+ # Send in background
145
+ self._executor.submit(self._send_to_backend, payload)
146
+
147
+ def _send_to_backend(self, payload: Dict[str, Any]):
148
+ """Send payload to backend with retry"""
149
+ max_retries = 3
150
+ retry_delay = 1
151
+ try:
152
+ for attempt in range(max_retries):
153
+ try:
154
+ self._do_send(payload)
155
+ logger.debug(f"Error sent successfully: {payload['message']}")
156
+ break
157
+ except Exception as e:
158
+ logger.warning(f"Failed to send error (attempt {attempt + 1}): {e}")
159
+ if attempt < max_retries - 1:
160
+ time.sleep(retry_delay * (attempt + 1))
161
+ finally:
162
+ with self._lock:
163
+ self._pending_sends -= 1
164
+
165
+ def _do_send(self, payload: Dict[str, Any]):
166
+ """Perform actual HTTP request"""
167
+ data = json.dumps(payload).encode("utf-8")
168
+
169
+ request = Request(
170
+ DEFAULT_ENDPOINT,
171
+ data=data,
172
+ headers={
173
+ "Content-Type": "application/json",
174
+ "x-api-key": self.config.api_key,
175
+ },
176
+ method="POST",
177
+ )
178
+
179
+ with urlopen(request, timeout=5) as response:
180
+ if response.status >= 400:
181
+ raise Exception(f"HTTP {response.status}")
182
+
183
+ def middleware(self):
184
+ """Create middleware for frameworks"""
185
+ return ReliabilityMiddleware(self)
186
+
187
+ async def flush(self):
188
+ """Flush pending sends"""
189
+ while self._pending_sends > 0:
190
+ time.sleep(0.1)
191
+
192
+ def shutdown(self):
193
+ """Shutdown the client"""
194
+ self._executor.shutdown(wait=True)
195
+
196
+
197
+ class ReliabilityMiddleware:
198
+ """Middleware for FastAPI/Flask"""
199
+
200
+ def __init__(self, client: ReliabilityClient):
201
+ self.client = client
202
+
203
+ async def __call__(self, request, call_next):
204
+ """Process request"""
205
+ try:
206
+ response = await call_next(request)
207
+ return response
208
+ except Exception as e:
209
+ self.client.capture(e, request)
210
+ raise
211
+
212
+
213
+ def get_client() -> ReliabilityClient:
214
+ """Get global client instance"""
215
+ if not hasattr(get_client, "_instance"):
216
+ raise RuntimeError("ReliabilityClient not initialized. Call init() first.")
217
+ return get_client._instance
218
+
219
+
220
+ def init(config: ReliabilityConfig) -> ReliabilityClient:
221
+ """Initialize global client"""
222
+ get_client._instance = ReliabilityClient(config)
223
+ return get_client._instance
xecurecode/config.py ADDED
@@ -0,0 +1,22 @@
1
+ """
2
+ Reliability Config for Python SDK
3
+ """
4
+
5
+ from dataclasses import dataclass
6
+ from typing import Optional
7
+
8
+
9
+ @dataclass
10
+ class ReliabilityConfig:
11
+ api_key: str
12
+ service_id: str
13
+ mode: str = "development"
14
+ timeout: int = 5000
15
+
16
+ def __post_init__(self):
17
+ if not self.api_key:
18
+ raise ValueError("API Key is required")
19
+ if not self.service_id:
20
+ raise ValueError("Service ID is required")
21
+ if self.mode not in ["development", "production"]:
22
+ raise ValueError("Mode must be 'development' or 'production'")
@@ -0,0 +1,71 @@
1
+ """
2
+ Django Integration
3
+ """
4
+
5
+ # To use with Django, add this to your settings.py:
6
+ #
7
+ # RELIABILITY_API_KEY = 'your-api-key'
8
+ # RELIABILITY_SERVICE_ID = 'your-service'
9
+ # RELIABILITY_MODE = 'development'
10
+ #
11
+ # And add to MIDDLEWARE:
12
+ # 'reliability.django_integration.ReliabilityMiddleware'
13
+
14
+ import logging
15
+ from django.core.exceptions import MiddlewareMixin
16
+
17
+ from ..client import ReliabilityClient
18
+ from ..config import ReliabilityConfig
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+ _client = None
23
+
24
+
25
+ def get_client() -> ReliabilityClient:
26
+ """Get or create Django client"""
27
+ global _client
28
+ if _client is None:
29
+ from django.conf import settings
30
+
31
+ api_key = getattr(settings, "RELIABILITY_API_KEY", None)
32
+ service_id = getattr(settings, "RELIABILITY_SERVICE_ID", None)
33
+ mode = getattr(settings, "RELIABILITY_MODE", "development")
34
+
35
+ if not api_key or not service_id:
36
+ logger.warning(
37
+ "Reliability SDK not configured. Add API_KEY and SERVICE_ID to settings."
38
+ )
39
+ return None
40
+
41
+ config = ReliabilityConfig(api_key=api_key, service_id=service_id, mode=mode)
42
+ _client = ReliabilityClient(config)
43
+
44
+ return _client
45
+
46
+
47
+ class ReliabilityMiddleware(MiddlewareMixin):
48
+ """Django middleware for error capture"""
49
+
50
+ def process_exception(self, request, exception):
51
+ """Capture exceptions"""
52
+ client = get_client()
53
+ if client:
54
+ client.capture(exception, request)
55
+ return None
56
+
57
+
58
+ # Django signals for automatic capture
59
+ def setup_django_signals():
60
+ """Setup Django signals for automatic error capture"""
61
+ try:
62
+ from django.core.signals import exception_handler
63
+ from django.dispatch import receiver
64
+
65
+ @receiver(exception_handler)
66
+ def capture_exception(**kwargs):
67
+ client = get_client()
68
+ if client and "request" in kwargs:
69
+ client.capture(kwargs["exception"], kwargs["request"])
70
+ except ImportError:
71
+ pass
@@ -0,0 +1,36 @@
1
+ """
2
+ FastAPI Integration
3
+ """
4
+
5
+ from fastapi import FastAPI, Request, Response
6
+ from fastapi.responses import JSONResponse
7
+ from starlette.middleware.base import BaseHTTPMiddleware
8
+ from typing import Callable
9
+
10
+ from .client import ReliabilityClient
11
+
12
+
13
+ class FastAPIMiddleware(BaseHTTPMiddleware):
14
+ """FastAPI middleware for error capture"""
15
+
16
+ def __init__(self, app: FastAPI, client: ReliabilityClient):
17
+ super().__init__(app)
18
+ self.client = client
19
+
20
+ async def dispatch(self, request: Request, call_next: Callable) -> Response:
21
+ try:
22
+ response = await call_next(request)
23
+ return response
24
+ except Exception as e:
25
+ self.client.capture(e, request)
26
+ raise
27
+
28
+
29
+ def setup_fastapi(app: FastAPI, client: ReliabilityClient):
30
+ """Setup FastAPI integration"""
31
+ app.add_middleware(FastAPIMiddleware, client=client)
32
+
33
+
34
+ def create_fastapi_middleware(client: ReliabilityClient):
35
+ """Create FastAPI middleware for manual use"""
36
+ return FastAPIMiddleware
@@ -0,0 +1,55 @@
1
+ """
2
+ Flask Integration
3
+ """
4
+
5
+ from flask import Flask, Request, Response
6
+ from typing import Callable
7
+
8
+ from .client import ReliabilityClient
9
+
10
+
11
+ class FlaskMiddleware:
12
+ """Flask middleware for error capture"""
13
+
14
+ def __init__(self, client: ReliabilityClient):
15
+ self.client = client
16
+
17
+ def __call__(self, environ: dict, start_response: Callable) -> Response:
18
+ # Wrap start_response to capture exceptions
19
+ def wrapped_start_response(status, headers, exc_info=None):
20
+ return start_response(status, headers, exc_info)
21
+
22
+ try:
23
+ # Use Flask's internal mechanism
24
+ from flask import g
25
+
26
+ return self.app(environ, wrapped_start_response)
27
+ except Exception as e:
28
+ # Get request object from Flask
29
+ from flask import request
30
+
31
+ self.client.capture(e, request)
32
+ raise
33
+
34
+
35
+ def init_flask(app: Flask, client: ReliabilityClient):
36
+ """Initialize Flask integration"""
37
+
38
+ @app.errorhandler(Exception)
39
+ def handle_error(error):
40
+ from flask import request
41
+
42
+ client.capture(error, request)
43
+ return {"error": str(error)}, 500
44
+
45
+
46
+ def create_flask_error_handler(client: ReliabilityClient):
47
+ """Create Flask error handler for manual use"""
48
+
49
+ def handle_error(error):
50
+ from flask import request
51
+
52
+ client.capture(error, request)
53
+ return {"error": str(error)}, 500
54
+
55
+ return handle_error
xecurecode/payload.py ADDED
@@ -0,0 +1,148 @@
1
+ """
2
+ Error Payload for Python SDK
3
+ """
4
+
5
+ import hashlib
6
+ import platform
7
+ import os
8
+ from typing import Dict, Any, Optional, List
9
+ from datetime import datetime
10
+
11
+
12
+ def _generate_fingerprint(error: Exception, stack: Optional[str] = None) -> str:
13
+ """Generate deterministic fingerprint for error"""
14
+ signature = str(error)
15
+ if stack:
16
+ first_line = stack.split("\n")[0] if stack else ""
17
+ signature += first_line
18
+
19
+ return hashlib.sha256(signature.encode()).hexdigest()
20
+
21
+
22
+ def _classify_error(error: Exception) -> str:
23
+ """Classify error type based on message"""
24
+ msg = str(error).lower()
25
+
26
+ if any(
27
+ keyword in msg
28
+ for keyword in ["database", "sql", "psycopg", "mysql", "sqlite", "connection"]
29
+ ):
30
+ return "DATABASE"
31
+ if any(
32
+ keyword in msg
33
+ for keyword in ["network", "http", "socket", "connection", "timeout", "ssl"]
34
+ ):
35
+ return "NETWORK"
36
+ if any(
37
+ keyword in msg
38
+ for keyword in ["validation", "invalid", "required", "constraint", "type"]
39
+ ):
40
+ return "VALIDATION"
41
+ if any(
42
+ keyword in msg
43
+ for keyword in ["permission", "denied", "forbidden", "unauthorized"]
44
+ ):
45
+ return "AUTH"
46
+
47
+ return "RUNTIME"
48
+
49
+
50
+ def _determine_severity(error: Exception) -> str:
51
+ """Determine error severity"""
52
+ msg = str(error).lower()
53
+
54
+ if any(
55
+ keyword in msg
56
+ for keyword in ["database", "connection", "timeout", "fatal", "crash"]
57
+ ):
58
+ return "critical"
59
+ return "warning"
60
+
61
+
62
+ def _format_stack_trace(stack: Optional[str]) -> Optional[str]:
63
+ """Format stack trace"""
64
+ if not stack:
65
+ return None
66
+
67
+ if isinstance(stack, str):
68
+ return stack
69
+
70
+ if hasattr(stack, "__traceback__"):
71
+ import traceback
72
+
73
+ return "".join(traceback.format_tb(stack.__traceback__))
74
+
75
+ return str(stack)
76
+
77
+
78
+ def _build_service_context() -> Dict[str, Any]:
79
+ """Build service context"""
80
+ return {
81
+ "pid": os.getpid(),
82
+ "python_version": platform.python_version(),
83
+ "platform": platform.platform(),
84
+ "hostname": platform.node() or os.environ.get("HOSTNAME", "unknown"),
85
+ }
86
+
87
+
88
+ def _extract_request_context(request: Optional[Any] = None) -> Optional[Dict[str, Any]]:
89
+ """Extract request context from various frameworks"""
90
+ if request is None:
91
+ return None
92
+
93
+ context = {}
94
+
95
+ # FastAPI/Starlette
96
+ if hasattr(request, "method"):
97
+ context["method"] = request.method
98
+
99
+ if hasattr(request, "url"):
100
+ context["url"] = str(request.url)
101
+
102
+ if hasattr(request, "client") and request.client:
103
+ context["ip"] = request.client.host
104
+
105
+ # Flask
106
+ if hasattr(request, "remote_addr"):
107
+ context["ip"] = request.remote_addr
108
+
109
+ if hasattr(request, "path"):
110
+ context["url"] = context.get("url", request.path)
111
+
112
+ # Django
113
+ if hasattr(request, "META"):
114
+ context["ip"] = request.META.get("REMOTE_ADDR")
115
+ context["method"] = request.method
116
+ context["url"] = request.path
117
+
118
+ # Generic headers
119
+ if hasattr(request, "headers"):
120
+ forwarded = request.headers.get("x-forwarded-for")
121
+ if forwarded and not context.get("ip"):
122
+ context["ip"] = forwarded.split(",")[0].strip()
123
+
124
+ return context if context else None
125
+
126
+
127
+ def create_error_payload(
128
+ error: Exception, service_id: str, mode: str, request: Optional[Any] = None
129
+ ) -> Dict[str, Any]:
130
+ """Create structured error payload"""
131
+
132
+ stack = _format_stack_trace(error.__traceback__)
133
+ fingerprint = _generate_fingerprint(error, stack)
134
+
135
+ return {
136
+ "message": str(error),
137
+ "name": type(error).__name__,
138
+ "stack": stack,
139
+ "fingerprint": fingerprint,
140
+ "timestamp": int(datetime.now().timestamp() * 1000),
141
+ "service_id": service_id,
142
+ "environment": mode,
143
+ "occurrence_count": 1,
144
+ "severity": _determine_severity(error),
145
+ "error_type": _classify_error(error),
146
+ "service_context": _build_service_context(),
147
+ "request_context": _extract_request_context(request),
148
+ }
@@ -0,0 +1,243 @@
1
+ Metadata-Version: 2.4
2
+ Name: xecurecode-python-sdk
3
+ Version: 0.1.0
4
+ Summary: AI-driven reliability SDK for Python applications
5
+ Author-email: Xel Team <team@xel.io>
6
+ License: MIT
7
+ Classifier: Development Status :: 4 - Beta
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.8
12
+ Classifier: Programming Language :: Python :: 3.9
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Requires-Python: >=3.8
17
+ Description-Content-Type: text/markdown
18
+ Provides-Extra: fastapi
19
+ Requires-Dist: fastapi>=0.100; extra == "fastapi"
20
+ Provides-Extra: flask
21
+ Requires-Dist: flask>=2.0; extra == "flask"
22
+ Provides-Extra: django
23
+ Requires-Dist: django>=3.0; extra == "django"
24
+
25
+ # XecureTrace Reliability SDK - Python
26
+
27
+ AI-driven reliability SDK for Python applications.
28
+
29
+ The official Python SDK for the XecureTrace Reliability Platform — a human-first failure analysis and recovery recommendation system.
30
+
31
+ ---
32
+
33
+ ## Why This SDK Exists
34
+
35
+ Modern backend systems fail in unpredictable ways.
36
+
37
+ This SDK:
38
+
39
+ - Captures structured runtime errors
40
+ - Generates deterministic fingerprints
41
+ - Classifies error types (DATABASE, NETWORK, VALIDATION, etc.)
42
+ - Determines severity
43
+ - Sends non-blocking telemetry to your backend
44
+ - Never crashes your application
45
+
46
+ **AI assists. Humans decide. Nothing executes automatically.**
47
+
48
+ ---
49
+
50
+ ## Requirements
51
+
52
+ - Python 3.8+
53
+ - FastAPI, Flask, or Django (optional)
54
+
55
+ ---
56
+
57
+ ## Installation
58
+
59
+ ```bash
60
+ pip install xecurecode-python-sdk
61
+ ```
62
+
63
+ Or install with framework support:
64
+
65
+ ```bash
66
+ pip install xecurecode-python-sdk[fastapi,flask,django]
67
+ ```
68
+
69
+ ---
70
+
71
+ ## Quick Start
72
+
73
+ ### 1️⃣ Initialize the SDK
74
+
75
+ ```python
76
+ from reliability import ReliabilityClient, ReliabilityConfig
77
+
78
+ config = ReliabilityConfig(
79
+ api_key="your-api-key",
80
+ service_id="your-service",
81
+ mode="development"
82
+ )
83
+ client = ReliabilityClient(config)
84
+ ```
85
+
86
+ **Configuration Options:**
87
+
88
+ | Parameter | Required | Description |
89
+ |-----------|----------|-------------|
90
+ | `api_key` | Yes | Your API key |
91
+ | `service_id` | Yes | Service identifier |
92
+ | `mode` | Yes | `development` or `production` |
93
+ | `timeout` | No | Request timeout (default: 5000ms) |
94
+
95
+ ---
96
+
97
+ ### 2️⃣ Automatic Global Error Capture
98
+
99
+ The SDK automatically handles:
100
+ - Uncaught exceptions
101
+ - Thread exceptions
102
+
103
+ ```python
104
+ # This will be automatically captured
105
+ raise ValueError("Database timeout")
106
+ ```
107
+
108
+ ---
109
+
110
+ ### 3️⃣ FastAPI Integration
111
+
112
+ ```python
113
+ from fastapi import FastAPI
114
+ from reliability import ReliabilityClient, ReliabilityConfig
115
+ from reliability.fastapi_integration import FastAPIMiddleware
116
+
117
+ config = ReliabilityConfig(...)
118
+ client = ReliabilityClient(config)
119
+
120
+ app = FastAPI()
121
+ app.add_middleware(FastAPIMiddleware, client=client)
122
+ ```
123
+
124
+ ---
125
+
126
+ ### 4️⃣ Flask Integration
127
+
128
+ ```python
129
+ from flask import Flask
130
+ from reliability import ReliabilityClient, ReliabilityConfig
131
+ from reliability.flask_integration import init_flask
132
+
133
+ config = ReliabilityConfig(...)
134
+ client = ReliabilityClient(config)
135
+
136
+ app = Flask(__name__)
137
+ init_flask(app, client)
138
+ ```
139
+
140
+ ---
141
+
142
+ ### 5️⃣ Django Integration
143
+
144
+ In `settings.py`:
145
+
146
+ ```python
147
+ RELIABILITY_API_KEY = "your-api-key"
148
+ RELIABILITY_SERVICE_ID = "your-service"
149
+ RELIABILITY_MODE = "development"
150
+ ```
151
+
152
+ In `settings.py` MIDDLEWARE:
153
+
154
+ ```python
155
+ MIDDLEWARE = [
156
+ ...
157
+ 'reliability.django_integration.ReliabilityMiddleware',
158
+ ...
159
+ ]
160
+ ```
161
+
162
+ ---
163
+
164
+ ### 6️⃣ Manual Capture
165
+
166
+ ```python
167
+ try:
168
+ risky_operation()
169
+ except Exception as e:
170
+ client.capture(e)
171
+
172
+ # With request context
173
+ client.capture(e, request)
174
+ ```
175
+
176
+ ---
177
+
178
+ ## What Gets Sent
179
+
180
+ Example structured payload:
181
+
182
+ ```json
183
+ {
184
+ "message": "Database connection failed",
185
+ "name": "ValueError",
186
+ "fingerprint": "a8c39c21...",
187
+ "timestamp": 1700000000000,
188
+ "service_id": "my-service",
189
+ "environment": "development",
190
+ "severity": "critical",
191
+ "error_type": "DATABASE",
192
+ "service_context": {
193
+ "pid": 12345,
194
+ "python_version": "3.11.0",
195
+ "platform": "Linux-5.10.0",
196
+ "hostname": "server-01"
197
+ },
198
+ "request_context": {
199
+ "method": "GET",
200
+ "url": "/api/users",
201
+ "ip": "10.0.0.5"
202
+ },
203
+ "occurrence_count": 1
204
+ }
205
+ ```
206
+
207
+ ---
208
+
209
+ ## Error Classification
210
+
211
+ | Type | Example |
212
+ |------|---------|
213
+ | DATABASE | Timeout, SQL errors, connection issues |
214
+ | NETWORK | HTTP failures, socket errors |
215
+ | VALIDATION | Invalid input, type errors |
216
+ | AUTH | Permission denied |
217
+ | RUNTIME | Standard Python errors |
218
+
219
+ ---
220
+
221
+ ## Severity Detection
222
+
223
+ - **Critical** → Database, timeout, connection errors
224
+ - **Warning** → Validation, recoverable issues
225
+
226
+ ---
227
+
228
+ ## Design Guarantees
229
+
230
+ - Non-blocking HTTP calls
231
+ - Timeout protected (5s default)
232
+ - Retry logic (3 attempts)
233
+ - Deduplication (1-minute window)
234
+ - Rate limiting (max 100 concurrent)
235
+ - Never throws inside SDK
236
+ - Never crashes host application
237
+ - Works with FastAPI, Flask, Django
238
+
239
+ ---
240
+
241
+ ## License
242
+
243
+ MIT
@@ -0,0 +1,11 @@
1
+ xecurecode/__init__.py,sha256=Lr91KhibWBdQiEDS5gGgtuEWLYTd91rU2aEEoDCFf8s,317
2
+ xecurecode/client.py,sha256=mI49C1ZB1uckghC0ArMmkJKWufzrH6_tUPnzQD9fQiA,6940
3
+ xecurecode/config.py,sha256=DEMBw0oEysZGpzMHi_dJ2_rimLrBijLvYJOCjXeN4Tk,566
4
+ xecurecode/django_integration.py,sha256=4A2qCoQR5jdOWKG2Boqi_C9kiqfCHHcSfMLUl2Ar2Io,2005
5
+ xecurecode/fastapi_integration.py,sha256=nmrLYQI5Blpg6qSnignzNHznpUoNcggxPZ-1KY0R5gw,1021
6
+ xecurecode/flask_integration.py,sha256=_ILiUkR3zBHIxkOB9sopux3yjK8f8gcJiQriDYvZQTc,1435
7
+ xecurecode/payload.py,sha256=t1D6One_bb8m2WYALy0oJt3LOfe2yvQMnMvL9MtPyCA,4078
8
+ xecurecode_python_sdk-0.1.0.dist-info/METADATA,sha256=B4IHwJZ25Jgm9-fTnU34OJWEDHNwgi64ik6TZaluA5g,4962
9
+ xecurecode_python_sdk-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
10
+ xecurecode_python_sdk-0.1.0.dist-info/top_level.txt,sha256=7OncyRHpCaDE2-r_4RIvGbITjwenEIapFFO8vFjkD7w,11
11
+ xecurecode_python_sdk-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ xecurecode