fastapi-loopguard 0.3.1__tar.gz → 0.4.0__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 (39) hide show
  1. fastapi_loopguard-0.4.0/PKG-INFO +127 -0
  2. fastapi_loopguard-0.4.0/README.md +82 -0
  3. fastapi_loopguard-0.4.0/assets/error-page-screenshot-console.png +0 -0
  4. fastapi_loopguard-0.4.0/assets/error-page-screenshot-endpoint.png +0 -0
  5. fastapi_loopguard-0.4.0/assets/error-page-screenshot.png +0 -0
  6. {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.0}/pyproject.toml +1 -1
  7. {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.0}/src/fastapi_loopguard/config.py +11 -0
  8. fastapi_loopguard-0.4.0/src/fastapi_loopguard/middleware.py +634 -0
  9. fastapi_loopguard-0.4.0/tests/test_enforcement_mode.py +511 -0
  10. {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.0}/tests/test_middleware.py +1 -0
  11. fastapi_loopguard-0.3.1/PKG-INFO +0 -154
  12. fastapi_loopguard-0.3.1/README.md +0 -109
  13. fastapi_loopguard-0.3.1/src/fastapi_loopguard/middleware.py +0 -236
  14. {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.0}/.github/dependabot.yml +0 -0
  15. {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.0}/.github/workflows/ci.yml +0 -0
  16. {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.0}/.github/workflows/publish.yml +0 -0
  17. {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.0}/.gitignore +0 -0
  18. {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.0}/CLAUDE.md +0 -0
  19. {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.0}/LICENSE +0 -0
  20. {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.0}/assets/loopguard-logo.webp +0 -0
  21. {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.0}/examples/demo_app.py +0 -0
  22. {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.0}/examples/locustfile.py +0 -0
  23. {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.0}/examples/run_stress_test.py +0 -0
  24. {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.0}/examples/stress_app.py +0 -0
  25. {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.0}/src/fastapi_loopguard/__init__.py +0 -0
  26. {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.0}/src/fastapi_loopguard/context.py +0 -0
  27. {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.0}/src/fastapi_loopguard/logging.py +0 -0
  28. {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.0}/src/fastapi_loopguard/metrics.py +0 -0
  29. {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.0}/src/fastapi_loopguard/monitor.py +0 -0
  30. {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.0}/src/fastapi_loopguard/py.typed +0 -0
  31. {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.0}/src/fastapi_loopguard/pytest_plugin.py +0 -0
  32. {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.0}/tests/__init__.py +0 -0
  33. {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.0}/tests/test_config.py +0 -0
  34. {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.0}/tests/test_context.py +0 -0
  35. {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.0}/tests/test_cumulative_blocking.py +0 -0
  36. {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.0}/tests/test_logging.py +0 -0
  37. {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.0}/tests/test_metrics.py +0 -0
  38. {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.0}/tests/test_monitor.py +0 -0
  39. {fastapi_loopguard-0.3.1 → fastapi_loopguard-0.4.0}/tests/test_pytest_plugin.py +0 -0
@@ -0,0 +1,127 @@
1
+ Metadata-Version: 2.4
2
+ Name: fastapi-loopguard
3
+ Version: 0.4.0
4
+ Summary: Detect event-loop blocking in FastAPI/Starlette with per-request attribution
5
+ Project-URL: Homepage, https://github.com/parhamdavari/fastapi-loopguard
6
+ Project-URL: Documentation, https://github.com/parhamdavari/fastapi-loopguard#readme
7
+ Project-URL: Repository, https://github.com/parhamdavari/fastapi-loopguard
8
+ Project-URL: Issues, https://github.com/parhamdavari/fastapi-loopguard/issues
9
+ Author: Parham
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: asyncio,blocking,event-loop,fastapi,middleware,monitoring,performance,starlette
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Framework :: AsyncIO
15
+ Classifier: Framework :: FastAPI
16
+ Classifier: Intended Audience :: Developers
17
+ Classifier: License :: OSI Approved :: MIT License
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Classifier: Topic :: System :: Monitoring
23
+ Classifier: Typing :: Typed
24
+ Requires-Python: >=3.12
25
+ Requires-Dist: starlette<1.0,>=0.37.0
26
+ Provides-Extra: all
27
+ Requires-Dist: prometheus-client>=0.19.0; extra == 'all'
28
+ Requires-Dist: structlog>=24.1.0; extra == 'all'
29
+ Provides-Extra: dev
30
+ Requires-Dist: coverage>=7.4.0; extra == 'dev'
31
+ Requires-Dist: fastapi>=0.110.0; extra == 'dev'
32
+ Requires-Dist: httpx>=0.27.0; extra == 'dev'
33
+ Requires-Dist: mypy>=1.8.0; extra == 'dev'
34
+ Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
35
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
36
+ Requires-Dist: ruff>=0.3.0; extra == 'dev'
37
+ Provides-Extra: prometheus
38
+ Requires-Dist: prometheus-client>=0.19.0; extra == 'prometheus'
39
+ Provides-Extra: stress
40
+ Requires-Dist: locust>=2.20.0; extra == 'stress'
41
+ Requires-Dist: uvicorn>=0.27.0; extra == 'stress'
42
+ Provides-Extra: structlog
43
+ Requires-Dist: structlog>=24.1.0; extra == 'structlog'
44
+ Description-Content-Type: text/markdown
45
+
46
+ <p align="center">
47
+ <img src="assets/loopguard-logo.webp" alt="LoopGuard" width="280" />
48
+ </p>
49
+
50
+ <p align="center">
51
+ <strong>Catch event-loop blocking in FastAPI with per-request attribution.</strong>
52
+ </p>
53
+
54
+ <p align="center">
55
+ <a href="https://badge.fury.io/py/fastapi-loopguard"><img src="https://badge.fury.io/py/fastapi-loopguard.svg" alt="PyPI version"></a>
56
+ <a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.12+-blue.svg" alt="Python 3.12+"></a>
57
+ <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
58
+ </p>
59
+
60
+ ---
61
+
62
+ When a request blocks your event loop (via `time.sleep()`, blocking I/O, or CPU work), LoopGuard detects it **and tells you which endpoint caused it**.
63
+
64
+ ## Install
65
+
66
+ ```bash
67
+ pip install fastapi-loopguard
68
+ ```
69
+
70
+ ## Quick Start
71
+
72
+ ```python
73
+ from fastapi import FastAPI
74
+ from fastapi_loopguard import LoopGuardMiddleware
75
+
76
+ app = FastAPI()
77
+ app.add_middleware(LoopGuardMiddleware)
78
+ ```
79
+
80
+ ## Enforcement Modes
81
+
82
+ | Mode | Behavior | Use Case |
83
+ |------|----------|----------|
84
+ | `"warn"` | Console warnings + headers | **Default** |
85
+ | `"strict"` | HTTP 503 + error page | Development / CI |
86
+ | `"log"` | Silent logging | Production |
87
+
88
+ ```python
89
+ from fastapi_loopguard import LoopGuardConfig
90
+
91
+ # Development: strict enforcement (503 on blocking)
92
+ config = LoopGuardConfig(dev_mode=True)
93
+
94
+ # Production: silent logging
95
+ config = LoopGuardConfig(enforcement_mode="log")
96
+
97
+ app.add_middleware(LoopGuardMiddleware, config=config)
98
+ ```
99
+
100
+ ## What You Get
101
+
102
+ ### Strict Mode
103
+ Returns an educational 503 page that explains what went wrong and how to fix it:
104
+
105
+ <p align="center">
106
+ <img src="assets/error-page-screenshot.png" alt="Strict mode error page" width="600" />
107
+ </p>
108
+
109
+ ---
110
+
111
+ ### Warn Mode
112
+ Adds diagnostic headers to every response for debugging:
113
+
114
+ <p align="center">
115
+ <img src="assets/error-page-screenshot-endpoint.png" alt="Warn mode headers" width="600" />
116
+ </p>
117
+
118
+ ---
119
+
120
+ ### Log Mode
121
+ Writes structured logs with full request attribution:
122
+
123
+ <p align="center">
124
+ <img src="assets/error-page-screenshot-console.png" alt="Console output" width="600" />
125
+ </p>
126
+
127
+ ---
@@ -0,0 +1,82 @@
1
+ <p align="center">
2
+ <img src="assets/loopguard-logo.webp" alt="LoopGuard" width="280" />
3
+ </p>
4
+
5
+ <p align="center">
6
+ <strong>Catch event-loop blocking in FastAPI with per-request attribution.</strong>
7
+ </p>
8
+
9
+ <p align="center">
10
+ <a href="https://badge.fury.io/py/fastapi-loopguard"><img src="https://badge.fury.io/py/fastapi-loopguard.svg" alt="PyPI version"></a>
11
+ <a href="https://www.python.org/downloads/"><img src="https://img.shields.io/badge/python-3.12+-blue.svg" alt="Python 3.12+"></a>
12
+ <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
13
+ </p>
14
+
15
+ ---
16
+
17
+ When a request blocks your event loop (via `time.sleep()`, blocking I/O, or CPU work), LoopGuard detects it **and tells you which endpoint caused it**.
18
+
19
+ ## Install
20
+
21
+ ```bash
22
+ pip install fastapi-loopguard
23
+ ```
24
+
25
+ ## Quick Start
26
+
27
+ ```python
28
+ from fastapi import FastAPI
29
+ from fastapi_loopguard import LoopGuardMiddleware
30
+
31
+ app = FastAPI()
32
+ app.add_middleware(LoopGuardMiddleware)
33
+ ```
34
+
35
+ ## Enforcement Modes
36
+
37
+ | Mode | Behavior | Use Case |
38
+ |------|----------|----------|
39
+ | `"warn"` | Console warnings + headers | **Default** |
40
+ | `"strict"` | HTTP 503 + error page | Development / CI |
41
+ | `"log"` | Silent logging | Production |
42
+
43
+ ```python
44
+ from fastapi_loopguard import LoopGuardConfig
45
+
46
+ # Development: strict enforcement (503 on blocking)
47
+ config = LoopGuardConfig(dev_mode=True)
48
+
49
+ # Production: silent logging
50
+ config = LoopGuardConfig(enforcement_mode="log")
51
+
52
+ app.add_middleware(LoopGuardMiddleware, config=config)
53
+ ```
54
+
55
+ ## What You Get
56
+
57
+ ### Strict Mode
58
+ Returns an educational 503 page that explains what went wrong and how to fix it:
59
+
60
+ <p align="center">
61
+ <img src="assets/error-page-screenshot.png" alt="Strict mode error page" width="600" />
62
+ </p>
63
+
64
+ ---
65
+
66
+ ### Warn Mode
67
+ Adds diagnostic headers to every response for debugging:
68
+
69
+ <p align="center">
70
+ <img src="assets/error-page-screenshot-endpoint.png" alt="Warn mode headers" width="600" />
71
+ </p>
72
+
73
+ ---
74
+
75
+ ### Log Mode
76
+ Writes structured logs with full request attribution:
77
+
78
+ <p align="center">
79
+ <img src="assets/error-page-screenshot-console.png" alt="Console output" width="600" />
80
+ </p>
81
+
82
+ ---
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "fastapi-loopguard"
7
- version = "0.3.1"
7
+ version = "0.4.0"
8
8
  description = "Detect event-loop blocking in FastAPI/Starlette with per-request attribution"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -34,6 +34,10 @@ class LoopGuardConfig:
34
34
  log_blocking_events: bool = True
35
35
  prometheus_enabled: bool = False
36
36
 
37
+ # Enforcement mode: how aggressively to respond to blocking
38
+ # "log" = just log (production), "warn" = loud warnings, "strict" = 503 errors
39
+ enforcement_mode: str = "warn"
40
+
37
41
  # Adaptive threshold settings
38
42
  adaptive_threshold: bool = False
39
43
  adaptive_window_size: int = 1000
@@ -86,3 +90,10 @@ class LoopGuardConfig:
86
90
  raise ValueError(
87
91
  "cumulative_window_ms cannot be less than monitor_interval_ms"
88
92
  )
93
+ # Enforcement mode validation
94
+ valid_enforcement_modes = {"log", "warn", "strict"}
95
+ if self.enforcement_mode not in valid_enforcement_modes:
96
+ raise ValueError(
97
+ f"enforcement_mode must be one of {valid_enforcement_modes}, "
98
+ f"got '{self.enforcement_mode}'"
99
+ )