# dhrupad-sah-exception-catcher
Automatically catch and report exceptions to Mira Sentinel with rich context and log integration for AI-powered debugging and automatic fix generation.
## Features
- 🚨 **Automatic Exception Catching** - Monitors uncaught exceptions and unhandled rejections
- 📊 **Rich Context** - Collects system info, memory usage, and custom context
- 🔍 **Log Integration** - Correlates exceptions with log data for enhanced debugging
- 🤖 **AI-Powered Fixes** - Integrates with Claude Code for automatic issue resolution
- 📋 **GitHub Integration** - Automatically creates issues and pull requests
- 🔄 **Retry Logic** - Robust error reporting with configurable retries
- 🎯 **Flexible Filtering** - Custom error filtering and context enrichment
## Installation
```bash
# Using PDM (recommended)
pdm add dhrupad-sah-exception-catcher
# Using pip
pip install dhrupad-sah-exception-catcher
# With FastAPI support
pdm add dhrupad-sah-exception-catcher[fastapi]
# With Flask support
pdm add dhrupad-sah-exception-catcher[flask]
# With all framework support
pdm add dhrupad-sah-exception-catcher[fastapi,flask]
```
## Quick Start
### FastAPI Integration (Recommended for APIs)
```python
from fastapi import FastAPI
from exception_catcher import setup_fastapi_mira_sentinel, MiraSentinelConfig
import os
app = FastAPI()
# Set up Mira Sentinel with environment variables
config = MiraSentinelConfig(
sentinel_url=os.getenv("MIRA_SENTINEL_URL"),
service_name=os.getenv("MIRA_SERVICE_NAME", "fastapi-service"),
repo=os.getenv("MIRA_REPO", "company/fastapi-service")
)
# Set up automatic exception catching
sentinel = setup_fastapi_mira_sentinel(
app,
config,
# Optional: Skip client errors
skip_status_codes=[400, 401, 403, 404],
# Optional: Extract custom context
extract_request_context=lambda request: {
"user_id": request.headers.get("x-user-id"),
"trace_id": request.headers.get("x-trace-id")
}
)
# Your routes - exceptions are automatically caught and reported
@app.get("/users/{user_id}")
async def get_user(user_id: int):
user = await get_user_from_db(user_id) # Any error here is caught
return user
# Manual exception reporting is also available
@app.get("/manual-report")
async def manual_report():
try:
await risky_operation()
except Exception as error:
await app.state.mira_sentinel.report_exception(error, {
"context": {"operation": "risky"}
})
raise # Re-raise to send HTTP error response
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
```
### Basic Usage (Non-Framework Apps)
```python
from exception_catcher import MiraSentinelExceptionCatcher, MiraSentinelConfig
config = MiraSentinelConfig(
sentinel_url="https://your-sentinel-instance.com",
service_name="python-service",
repo="company/python-service"
)
catcher = MiraSentinelExceptionCatcher(config)
catcher.initialize()
# That's it! All exceptions are now automatically caught and reported
```
### Environment-Based Auto-Initialization
```python
from exception_catcher import auto_initialize
# Set environment variables:
# MIRA_SENTINEL_URL=https://your-sentinel-instance.com
# MIRA_SERVICE_NAME=python-service
# MIRA_REPO=company/python-service
catcher = auto_initialize()
# Automatically initializes if environment variables are set
```
### Advanced Configuration
```python
from exception_catcher import MiraSentinelExceptionCatcher, MiraSentinelConfig
config = MiraSentinelConfig(
sentinel_url="https://your-sentinel-instance.com",
service_name="python-service",
repo="company/python-service",
api_key="your-api-key", # Optional authentication
timeout=15.0, # HTTP timeout in seconds
retry_attempts=5,
retry_delay=2.0
)
catcher = MiraSentinelExceptionCatcher(config)
# Custom error filtering
catcher.set_error_filter(lambda error:
# Skip test errors
"test" not in str(error).lower()
)
# Enrich context with custom data
catcher.set_context_enricher(lambda error, context: {
"user_id": get_current_user_id(),
"request_id": get_current_request_id(),
"version": os.getenv("APP_VERSION")
})
catcher.initialize()
```
## Manual Exception Reporting
```python
import asyncio
from exception_catcher import ReportOptions
try:
# Some risky operation
await process_payment(payment_data)
except Exception as error:
# Manually report with additional context
await catcher.report_exception(error, ReportOptions(
context={
"payment_id": payment_data.id,
"user_id": payment_data.user_id,
"amount": payment_data.amount
},
tags=["payment", "critical"],
severity="high"
))
raise # Re-throw if needed
```
## Integration with Flask
```python
from flask import Flask
from exception_catcher import setup_flask_mira_sentinel, MiraSentinelConfig
import os
app = Flask(__name__)
config = MiraSentinelConfig(
sentinel_url=os.getenv("MIRA_SENTINEL_URL"),
service_name="flask-api",
repo="company/flask-api"
)
# Set up automatic exception catching
sentinel = setup_flask_mira_sentinel(
app,
config,
include_headers=True,
skip_status_codes=[400, 401, 403, 404],
extract_request_context=lambda request: {
"user_id": request.headers.get("X-User-ID"),
"session_id": request.headers.get("X-Session-ID")
}
)
@app.route("/users/<int:user_id>")
def get_user(user_id):
user = get_user_from_db(user_id) # Any error here is caught
return jsonify(user)
# Manual exception reporting
@app.route("/manual-report")
def manual_report():
try:
risky_operation()
except Exception as error:
# Note: Flask integration runs async in sync context
import asyncio
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
try:
loop.run_until_complete(
app.mira_sentinel.report_exception(error, {
"context": {"operation": "risky"}
})
)
finally:
loop.close()
raise
if __name__ == "__main__":
app.run(debug=True)
```
## Environment Variables
| Variable | Description | Required |
|----------|-------------|----------|
| `MIRA_SENTINEL_URL` | URL of your Mira Sentinel instance | Yes |
| `MIRA_SERVICE_NAME` | Name of your service | Yes |
| `MIRA_REPO` | GitHub repository (owner/repo) | Yes |
| `MIRA_API_KEY` | API key for authentication | No |
| `MIRA_ENABLED` | Enable/disable (default: true) | No |
## Configuration Options
| Option | Type | Description | Default |
|--------|------|-------------|---------|
| `sentinel_url` | str | Mira Sentinel instance URL | Required |
| `service_name` | str | Service name for log correlation | Required |
| `repo` | str | GitHub repository (owner/repo) | Required |
| `api_key` | str | Optional API key | None |
| `enabled` | bool | Enable/disable catching | True |
| `timeout` | float | HTTP timeout (seconds) | 10.0 |
| `retry_attempts` | int | Retry attempts | 3 |
| `retry_delay` | float | Retry delay (seconds) | 1.0 |
## How It Works
1. **Exception Occurs** - Your service throws an exception
2. **Context Collection** - Rich context is automatically collected:
- Error message and stack trace
- System information (Python version, memory, CPU)
- Timestamp for log correlation
- Custom context from your application
3. **Sent to Mira Sentinel** - Exception data is sent to your Sentinel instance
4. **Log Integration** - Sentinel queries logs around the exception time
5. **AI Analysis** - Claude Code analyzes the exception + log context
6. **GitHub Integration** - Issue and PR are automatically created
7. **Timeline Analysis** - Full timeline of events leading to the exception
## Best Practices
### 1. Service Naming
Use consistent service names that match your log labels:
```python
# Good - matches log service label
service_name="api-gateway"
# Bad - doesn't match logs
service_name="my-awesome-service"
```
### 2. Error Filtering
Filter out noise to focus on actionable exceptions:
```python
def should_catch_error(error):
# Skip test environments
if os.getenv("ENV") == "test":
return False
# Skip known non-critical errors
if "ConnectionResetError" in str(error):
return False
# Skip client errors for HTTP frameworks
if hasattr(error, 'status_code') and 400 <= error.status_code < 500:
return False
return True
catcher.set_error_filter(should_catch_error)
```
### 3. Context Enrichment
Add meaningful context for better debugging:
```python
def enrich_context(error, context):
return {
# Business context
"tenant_id": get_current_tenant(),
"feature": get_current_feature(),
# Technical context
"version": os.getenv("APP_VERSION"),
"deployment": os.getenv("DEPLOYMENT_ID"),
# Performance context
"response_time": get_response_time(),
"queue_size": get_queue_size()
}
catcher.set_context_enricher(enrich_context)
```
### 4. Graceful Shutdown
Always clean up on process exit:
```python
import atexit
catcher = MiraSentinelExceptionCatcher(config)
catcher.initialize()
def cleanup():
catcher.shutdown()
atexit.register(cleanup)
```
## Testing
Test your integration:
```python
import asyncio
from exception_catcher import auto_initialize
async def test_integration():
catcher = auto_initialize()
if catcher:
print("✅ Configuration loaded successfully")
# Test connection
is_connected = await catcher.test_connection()
if is_connected:
print("✅ Connection to Mira Sentinel successful")
else:
print("❌ Connection failed - check your MIRA_SENTINEL_URL")
else:
print("❌ Missing required environment variables")
print("Required: MIRA_SENTINEL_URL, MIRA_SERVICE_NAME, MIRA_REPO")
# Run test
asyncio.run(test_integration())
```
## Development
```bash
# Clone the repository
git clone https://github.com/dhrupad-sah/python-exception-catcher
cd python-exception-catcher
# Install dependencies with PDM
pdm install
# Install with development dependencies
pdm install -d
# Run tests
pdm run pytest
# Format code
pdm run black src/
pdm run isort src/
# Type checking
pdm run mypy src/
```
## License
MIT
## Support
For support, please create an issue in the [GitHub repository](https://github.com/dhrupad-sah/python-exception-catcher).
Raw data
{
"_id": null,
"home_page": null,
"name": "dhrupad-sah-exception-catcher",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": null,
"keywords": "exception, error-handling, monitoring, observability, logging, automation, claude, ai-fixing, fastapi, flask, django",
"author": null,
"author_email": "Mira Sentinel Team <noreply@example.com>",
"download_url": "https://files.pythonhosted.org/packages/c5/1a/27065fa9aa2ac6f8ede667ea32100761c18383c0f1bda991b7bbf375ed19/dhrupad_sah_exception_catcher-1.0.4.tar.gz",
"platform": null,
"description": "# dhrupad-sah-exception-catcher\n\nAutomatically catch and report exceptions to Mira Sentinel with rich context and log integration for AI-powered debugging and automatic fix generation.\n\n## Features\n\n- \ud83d\udea8 **Automatic Exception Catching** - Monitors uncaught exceptions and unhandled rejections\n- \ud83d\udcca **Rich Context** - Collects system info, memory usage, and custom context\n- \ud83d\udd0d **Log Integration** - Correlates exceptions with log data for enhanced debugging\n- \ud83e\udd16 **AI-Powered Fixes** - Integrates with Claude Code for automatic issue resolution\n- \ud83d\udccb **GitHub Integration** - Automatically creates issues and pull requests\n- \ud83d\udd04 **Retry Logic** - Robust error reporting with configurable retries\n- \ud83c\udfaf **Flexible Filtering** - Custom error filtering and context enrichment\n\n## Installation\n\n```bash\n# Using PDM (recommended)\npdm add dhrupad-sah-exception-catcher\n\n# Using pip\npip install dhrupad-sah-exception-catcher\n\n# With FastAPI support\npdm add dhrupad-sah-exception-catcher[fastapi]\n\n# With Flask support \npdm add dhrupad-sah-exception-catcher[flask]\n\n# With all framework support\npdm add dhrupad-sah-exception-catcher[fastapi,flask]\n```\n\n## Quick Start\n\n### FastAPI Integration (Recommended for APIs)\n\n```python\nfrom fastapi import FastAPI\nfrom exception_catcher import setup_fastapi_mira_sentinel, MiraSentinelConfig\nimport os\n\napp = FastAPI()\n\n# Set up Mira Sentinel with environment variables\nconfig = MiraSentinelConfig(\n sentinel_url=os.getenv(\"MIRA_SENTINEL_URL\"),\n service_name=os.getenv(\"MIRA_SERVICE_NAME\", \"fastapi-service\"),\n repo=os.getenv(\"MIRA_REPO\", \"company/fastapi-service\")\n)\n\n# Set up automatic exception catching\nsentinel = setup_fastapi_mira_sentinel(\n app,\n config,\n \n # Optional: Skip client errors\n skip_status_codes=[400, 401, 403, 404],\n \n # Optional: Extract custom context\n extract_request_context=lambda request: {\n \"user_id\": request.headers.get(\"x-user-id\"),\n \"trace_id\": request.headers.get(\"x-trace-id\")\n }\n)\n\n# Your routes - exceptions are automatically caught and reported\n@app.get(\"/users/{user_id}\")\nasync def get_user(user_id: int):\n user = await get_user_from_db(user_id) # Any error here is caught\n return user\n\n# Manual exception reporting is also available \n@app.get(\"/manual-report\")\nasync def manual_report():\n try:\n await risky_operation()\n except Exception as error:\n await app.state.mira_sentinel.report_exception(error, {\n \"context\": {\"operation\": \"risky\"}\n })\n raise # Re-raise to send HTTP error response\n\nif __name__ == \"__main__\":\n import uvicorn\n uvicorn.run(app, host=\"0.0.0.0\", port=8000)\n```\n\n### Basic Usage (Non-Framework Apps)\n\n```python\nfrom exception_catcher import MiraSentinelExceptionCatcher, MiraSentinelConfig\n\nconfig = MiraSentinelConfig(\n sentinel_url=\"https://your-sentinel-instance.com\",\n service_name=\"python-service\",\n repo=\"company/python-service\"\n)\n\ncatcher = MiraSentinelExceptionCatcher(config)\ncatcher.initialize()\n\n# That's it! All exceptions are now automatically caught and reported\n```\n\n### Environment-Based Auto-Initialization\n\n```python\nfrom exception_catcher import auto_initialize\n\n# Set environment variables:\n# MIRA_SENTINEL_URL=https://your-sentinel-instance.com\n# MIRA_SERVICE_NAME=python-service\n# MIRA_REPO=company/python-service\n\ncatcher = auto_initialize()\n# Automatically initializes if environment variables are set\n```\n\n### Advanced Configuration\n\n```python\nfrom exception_catcher import MiraSentinelExceptionCatcher, MiraSentinelConfig\n\nconfig = MiraSentinelConfig(\n sentinel_url=\"https://your-sentinel-instance.com\",\n service_name=\"python-service\", \n repo=\"company/python-service\",\n api_key=\"your-api-key\", # Optional authentication\n timeout=15.0, # HTTP timeout in seconds\n retry_attempts=5,\n retry_delay=2.0\n)\n\ncatcher = MiraSentinelExceptionCatcher(config)\n\n# Custom error filtering\ncatcher.set_error_filter(lambda error: \n # Skip test errors\n \"test\" not in str(error).lower()\n)\n\n# Enrich context with custom data\ncatcher.set_context_enricher(lambda error, context: {\n \"user_id\": get_current_user_id(),\n \"request_id\": get_current_request_id(),\n \"version\": os.getenv(\"APP_VERSION\")\n})\n\ncatcher.initialize()\n```\n\n## Manual Exception Reporting\n\n```python\nimport asyncio\nfrom exception_catcher import ReportOptions\n\ntry:\n # Some risky operation\n await process_payment(payment_data)\nexcept Exception as error:\n # Manually report with additional context\n await catcher.report_exception(error, ReportOptions(\n context={\n \"payment_id\": payment_data.id,\n \"user_id\": payment_data.user_id,\n \"amount\": payment_data.amount\n },\n tags=[\"payment\", \"critical\"],\n severity=\"high\"\n ))\n \n raise # Re-throw if needed\n```\n\n## Integration with Flask\n\n```python\nfrom flask import Flask\nfrom exception_catcher import setup_flask_mira_sentinel, MiraSentinelConfig\nimport os\n\napp = Flask(__name__)\n\nconfig = MiraSentinelConfig(\n sentinel_url=os.getenv(\"MIRA_SENTINEL_URL\"),\n service_name=\"flask-api\",\n repo=\"company/flask-api\"\n)\n\n# Set up automatic exception catching\nsentinel = setup_flask_mira_sentinel(\n app,\n config,\n include_headers=True,\n skip_status_codes=[400, 401, 403, 404],\n extract_request_context=lambda request: {\n \"user_id\": request.headers.get(\"X-User-ID\"),\n \"session_id\": request.headers.get(\"X-Session-ID\")\n }\n)\n\n@app.route(\"/users/<int:user_id>\")\ndef get_user(user_id):\n user = get_user_from_db(user_id) # Any error here is caught\n return jsonify(user)\n\n# Manual exception reporting\n@app.route(\"/manual-report\")\ndef manual_report():\n try:\n risky_operation()\n except Exception as error:\n # Note: Flask integration runs async in sync context\n import asyncio\n loop = asyncio.new_event_loop()\n asyncio.set_event_loop(loop)\n try:\n loop.run_until_complete(\n app.mira_sentinel.report_exception(error, {\n \"context\": {\"operation\": \"risky\"}\n })\n )\n finally:\n loop.close()\n raise\n\nif __name__ == \"__main__\":\n app.run(debug=True)\n```\n\n## Environment Variables\n\n| Variable | Description | Required |\n|----------|-------------|----------|\n| `MIRA_SENTINEL_URL` | URL of your Mira Sentinel instance | Yes |\n| `MIRA_SERVICE_NAME` | Name of your service | Yes |\n| `MIRA_REPO` | GitHub repository (owner/repo) | Yes |\n| `MIRA_API_KEY` | API key for authentication | No |\n| `MIRA_ENABLED` | Enable/disable (default: true) | No |\n\n## Configuration Options\n\n| Option | Type | Description | Default |\n|--------|------|-------------|---------|\n| `sentinel_url` | str | Mira Sentinel instance URL | Required |\n| `service_name` | str | Service name for log correlation | Required |\n| `repo` | str | GitHub repository (owner/repo) | Required |\n| `api_key` | str | Optional API key | None |\n| `enabled` | bool | Enable/disable catching | True |\n| `timeout` | float | HTTP timeout (seconds) | 10.0 |\n| `retry_attempts` | int | Retry attempts | 3 |\n| `retry_delay` | float | Retry delay (seconds) | 1.0 |\n\n## How It Works\n\n1. **Exception Occurs** - Your service throws an exception\n2. **Context Collection** - Rich context is automatically collected:\n - Error message and stack trace\n - System information (Python version, memory, CPU)\n - Timestamp for log correlation\n - Custom context from your application\n3. **Sent to Mira Sentinel** - Exception data is sent to your Sentinel instance\n4. **Log Integration** - Sentinel queries logs around the exception time\n5. **AI Analysis** - Claude Code analyzes the exception + log context\n6. **GitHub Integration** - Issue and PR are automatically created\n7. **Timeline Analysis** - Full timeline of events leading to the exception\n\n## Best Practices\n\n### 1. Service Naming\nUse consistent service names that match your log labels:\n\n```python\n# Good - matches log service label\nservice_name=\"api-gateway\"\n\n# Bad - doesn't match logs\nservice_name=\"my-awesome-service\"\n```\n\n### 2. Error Filtering\nFilter out noise to focus on actionable exceptions:\n\n```python\ndef should_catch_error(error):\n # Skip test environments\n if os.getenv(\"ENV\") == \"test\":\n return False\n \n # Skip known non-critical errors\n if \"ConnectionResetError\" in str(error):\n return False\n \n # Skip client errors for HTTP frameworks\n if hasattr(error, 'status_code') and 400 <= error.status_code < 500:\n return False\n \n return True\n\ncatcher.set_error_filter(should_catch_error)\n```\n\n### 3. Context Enrichment\nAdd meaningful context for better debugging:\n\n```python\ndef enrich_context(error, context):\n return {\n # Business context\n \"tenant_id\": get_current_tenant(),\n \"feature\": get_current_feature(),\n \n # Technical context\n \"version\": os.getenv(\"APP_VERSION\"),\n \"deployment\": os.getenv(\"DEPLOYMENT_ID\"),\n \n # Performance context\n \"response_time\": get_response_time(),\n \"queue_size\": get_queue_size()\n }\n\ncatcher.set_context_enricher(enrich_context)\n```\n\n### 4. Graceful Shutdown\nAlways clean up on process exit:\n\n```python\nimport atexit\n\ncatcher = MiraSentinelExceptionCatcher(config)\ncatcher.initialize()\n\ndef cleanup():\n catcher.shutdown()\n\natexit.register(cleanup)\n```\n\n## Testing\n\nTest your integration:\n\n```python\nimport asyncio\nfrom exception_catcher import auto_initialize\n\nasync def test_integration():\n catcher = auto_initialize()\n \n if catcher:\n print(\"\u2705 Configuration loaded successfully\")\n \n # Test connection\n is_connected = await catcher.test_connection()\n if is_connected:\n print(\"\u2705 Connection to Mira Sentinel successful\")\n else:\n print(\"\u274c Connection failed - check your MIRA_SENTINEL_URL\")\n else:\n print(\"\u274c Missing required environment variables\")\n print(\"Required: MIRA_SENTINEL_URL, MIRA_SERVICE_NAME, MIRA_REPO\")\n\n# Run test\nasyncio.run(test_integration())\n```\n\n## Development\n\n```bash\n# Clone the repository\ngit clone https://github.com/dhrupad-sah/python-exception-catcher\ncd python-exception-catcher\n\n# Install dependencies with PDM\npdm install\n\n# Install with development dependencies\npdm install -d\n\n# Run tests\npdm run pytest\n\n# Format code\npdm run black src/\npdm run isort src/\n\n# Type checking\npdm run mypy src/\n```\n\n## License\n\nMIT\n\n## Support\n\nFor support, please create an issue in the [GitHub repository](https://github.com/dhrupad-sah/python-exception-catcher).",
"bugtrack_url": null,
"license": "MIT",
"summary": "Automatically catch and report exceptions to Mira Sentinel with rich context and log integration for AI-powered debugging and automatic fix generation",
"version": "1.0.4",
"project_urls": {
"Documentation": "https://github.com/dhrupad-sah/python-exception-catcher#readme",
"Homepage": "https://github.com/dhrupad-sah/python-exception-catcher",
"Issues": "https://github.com/dhrupad-sah/python-exception-catcher/issues",
"Repository": "https://github.com/dhrupad-sah/python-exception-catcher"
},
"split_keywords": [
"exception",
" error-handling",
" monitoring",
" observability",
" logging",
" automation",
" claude",
" ai-fixing",
" fastapi",
" flask",
" django"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "8a73e1392e2c61a79d2cd7a4d990bcb7e364ef6a198ba2ad089859e59ba384fc",
"md5": "35d99747d0cebb25e6dc78fa6fc83e69",
"sha256": "220a10ac133b228d8e77dcb65ded75b071baa36127c07e0cd995cd5ad5519dc7"
},
"downloads": -1,
"filename": "dhrupad_sah_exception_catcher-1.0.4-py3-none-any.whl",
"has_sig": false,
"md5_digest": "35d99747d0cebb25e6dc78fa6fc83e69",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 15942,
"upload_time": "2025-07-24T15:53:46",
"upload_time_iso_8601": "2025-07-24T15:53:46.202243Z",
"url": "https://files.pythonhosted.org/packages/8a/73/e1392e2c61a79d2cd7a4d990bcb7e364ef6a198ba2ad089859e59ba384fc/dhrupad_sah_exception_catcher-1.0.4-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "c51a27065fa9aa2ac6f8ede667ea32100761c18383c0f1bda991b7bbf375ed19",
"md5": "58f92e6db0f87911b8297ca4158ca678",
"sha256": "5182bfdf8405193872f797e7f001a7544d8e96b72cccbb8867d1fac71d2f84b5"
},
"downloads": -1,
"filename": "dhrupad_sah_exception_catcher-1.0.4.tar.gz",
"has_sig": false,
"md5_digest": "58f92e6db0f87911b8297ca4158ca678",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 15175,
"upload_time": "2025-07-24T15:53:47",
"upload_time_iso_8601": "2025-07-24T15:53:47.440643Z",
"url": "https://files.pythonhosted.org/packages/c5/1a/27065fa9aa2ac6f8ede667ea32100761c18383c0f1bda991b7bbf375ed19/dhrupad_sah_exception_catcher-1.0.4.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-07-24 15:53:47",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "dhrupad-sah",
"github_project": "python-exception-catcher#readme",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"requirements": [
{
"name": "httpx",
"specs": [
[
">=",
"0.25.0"
]
]
},
{
"name": "pydantic",
"specs": [
[
">=",
"2.0.0"
]
]
},
{
"name": "psutil",
"specs": [
[
">=",
"5.9.0"
]
]
}
],
"lcname": "dhrupad-sah-exception-catcher"
}