fastapi-loopguard 0.4.0__tar.gz → 0.4.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 (37) hide show
  1. {fastapi_loopguard-0.4.0 → fastapi_loopguard-0.4.1}/PKG-INFO +5 -1
  2. {fastapi_loopguard-0.4.0 → fastapi_loopguard-0.4.1}/README.md +4 -0
  3. fastapi_loopguard-0.4.1/docs/CONFIGURATION.md +129 -0
  4. {fastapi_loopguard-0.4.0 → fastapi_loopguard-0.4.1}/pyproject.toml +1 -1
  5. {fastapi_loopguard-0.4.0 → fastapi_loopguard-0.4.1}/src/fastapi_loopguard/config.py +1 -1
  6. {fastapi_loopguard-0.4.0 → fastapi_loopguard-0.4.1}/src/fastapi_loopguard/middleware.py +4 -4
  7. {fastapi_loopguard-0.4.0 → fastapi_loopguard-0.4.1}/tests/test_cumulative_blocking.py +15 -3
  8. {fastapi_loopguard-0.4.0 → fastapi_loopguard-0.4.1}/.github/dependabot.yml +0 -0
  9. {fastapi_loopguard-0.4.0 → fastapi_loopguard-0.4.1}/.github/workflows/ci.yml +0 -0
  10. {fastapi_loopguard-0.4.0 → fastapi_loopguard-0.4.1}/.github/workflows/publish.yml +0 -0
  11. {fastapi_loopguard-0.4.0 → fastapi_loopguard-0.4.1}/.gitignore +0 -0
  12. {fastapi_loopguard-0.4.0 → fastapi_loopguard-0.4.1}/CLAUDE.md +0 -0
  13. {fastapi_loopguard-0.4.0 → fastapi_loopguard-0.4.1}/LICENSE +0 -0
  14. {fastapi_loopguard-0.4.0 → fastapi_loopguard-0.4.1}/assets/error-page-screenshot-console.png +0 -0
  15. {fastapi_loopguard-0.4.0 → fastapi_loopguard-0.4.1}/assets/error-page-screenshot-endpoint.png +0 -0
  16. {fastapi_loopguard-0.4.0 → fastapi_loopguard-0.4.1}/assets/error-page-screenshot.png +0 -0
  17. {fastapi_loopguard-0.4.0 → fastapi_loopguard-0.4.1}/assets/loopguard-logo.webp +0 -0
  18. {fastapi_loopguard-0.4.0 → fastapi_loopguard-0.4.1}/examples/demo_app.py +0 -0
  19. {fastapi_loopguard-0.4.0 → fastapi_loopguard-0.4.1}/examples/locustfile.py +0 -0
  20. {fastapi_loopguard-0.4.0 → fastapi_loopguard-0.4.1}/examples/run_stress_test.py +0 -0
  21. {fastapi_loopguard-0.4.0 → fastapi_loopguard-0.4.1}/examples/stress_app.py +0 -0
  22. {fastapi_loopguard-0.4.0 → fastapi_loopguard-0.4.1}/src/fastapi_loopguard/__init__.py +0 -0
  23. {fastapi_loopguard-0.4.0 → fastapi_loopguard-0.4.1}/src/fastapi_loopguard/context.py +0 -0
  24. {fastapi_loopguard-0.4.0 → fastapi_loopguard-0.4.1}/src/fastapi_loopguard/logging.py +0 -0
  25. {fastapi_loopguard-0.4.0 → fastapi_loopguard-0.4.1}/src/fastapi_loopguard/metrics.py +0 -0
  26. {fastapi_loopguard-0.4.0 → fastapi_loopguard-0.4.1}/src/fastapi_loopguard/monitor.py +0 -0
  27. {fastapi_loopguard-0.4.0 → fastapi_loopguard-0.4.1}/src/fastapi_loopguard/py.typed +0 -0
  28. {fastapi_loopguard-0.4.0 → fastapi_loopguard-0.4.1}/src/fastapi_loopguard/pytest_plugin.py +0 -0
  29. {fastapi_loopguard-0.4.0 → fastapi_loopguard-0.4.1}/tests/__init__.py +0 -0
  30. {fastapi_loopguard-0.4.0 → fastapi_loopguard-0.4.1}/tests/test_config.py +0 -0
  31. {fastapi_loopguard-0.4.0 → fastapi_loopguard-0.4.1}/tests/test_context.py +0 -0
  32. {fastapi_loopguard-0.4.0 → fastapi_loopguard-0.4.1}/tests/test_enforcement_mode.py +0 -0
  33. {fastapi_loopguard-0.4.0 → fastapi_loopguard-0.4.1}/tests/test_logging.py +0 -0
  34. {fastapi_loopguard-0.4.0 → fastapi_loopguard-0.4.1}/tests/test_metrics.py +0 -0
  35. {fastapi_loopguard-0.4.0 → fastapi_loopguard-0.4.1}/tests/test_middleware.py +0 -0
  36. {fastapi_loopguard-0.4.0 → fastapi_loopguard-0.4.1}/tests/test_monitor.py +0 -0
  37. {fastapi_loopguard-0.4.0 → fastapi_loopguard-0.4.1}/tests/test_pytest_plugin.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastapi-loopguard
3
- Version: 0.4.0
3
+ Version: 0.4.1
4
4
  Summary: Detect event-loop blocking in FastAPI/Starlette with per-request attribution
5
5
  Project-URL: Homepage, https://github.com/parhamdavari/fastapi-loopguard
6
6
  Project-URL: Documentation, https://github.com/parhamdavari/fastapi-loopguard#readme
@@ -125,3 +125,7 @@ Writes structured logs with full request attribution:
125
125
  </p>
126
126
 
127
127
  ---
128
+
129
+ <p align="center">
130
+ <a href="docs/CONFIGURATION.md"><strong>Full Configuration Reference</strong></a>
131
+ </p>
@@ -80,3 +80,7 @@ Writes structured logs with full request attribution:
80
80
  </p>
81
81
 
82
82
  ---
83
+
84
+ <p align="center">
85
+ <a href="docs/CONFIGURATION.md"><strong>Full Configuration Reference</strong></a>
86
+ </p>
@@ -0,0 +1,129 @@
1
+ # Configuration Reference
2
+
3
+ All configuration options for `LoopGuardConfig`.
4
+
5
+ ## Quick Reference
6
+
7
+ ```python
8
+ from fastapi_loopguard import LoopGuardConfig
9
+
10
+ config = LoopGuardConfig(
11
+ # Enforcement
12
+ enforcement_mode="warn", # "log" | "warn" | "strict"
13
+ dev_mode=False, # Auto-escalates to strict when True
14
+
15
+ # Detection tuning
16
+ monitor_interval_ms=10.0, # How often to check (ms)
17
+ threshold_multiplier=5.0, # Blocking = lag > baseline × multiplier
18
+ fallback_threshold_ms=50.0, # Threshold if calibration fails
19
+
20
+ # Cumulative detection (enabled by default)
21
+ cumulative_blocking_enabled=True,
22
+ cumulative_blocking_threshold_ms=200.0,
23
+ cumulative_window_ms=1000.0,
24
+
25
+ # Adaptive threshold (disabled by default)
26
+ adaptive_threshold=False,
27
+
28
+ # Integrations
29
+ prometheus_enabled=False,
30
+ log_blocking_events=True,
31
+ )
32
+ ```
33
+
34
+ ---
35
+
36
+ ## Core Settings
37
+
38
+ | Option | Type | Default | Description |
39
+ |--------|------|---------|-------------|
40
+ | `enabled` | bool | `True` | Master switch. Set `False` to disable entirely. |
41
+ | `enforcement_mode` | str | `"warn"` | How to respond: `"log"`, `"warn"`, or `"strict"` |
42
+ | `dev_mode` | bool | `False` | Enables response headers. Auto-escalates to strict mode. |
43
+ | `log_blocking_events` | bool | `True` | Log blocking events to console |
44
+ | `exclude_paths` | frozenset | `{"/health", ...}` | Paths to skip monitoring |
45
+
46
+ ---
47
+
48
+ ## Detection Tuning
49
+
50
+ | Option | Type | Default | Description |
51
+ |--------|------|---------|-------------|
52
+ | `monitor_interval_ms` | float | `10.0` | Sentinel check frequency (ms) |
53
+ | `threshold_multiplier` | float | `5.0` | Blocking detected when lag > baseline × this |
54
+ | `calibration_iterations` | int | `100` | Samples during startup calibration |
55
+ | `fallback_threshold_ms` | float | `50.0` | Used if calibration is unreliable |
56
+
57
+ ---
58
+
59
+ ## Cumulative Blocking Detection
60
+
61
+ Catches "death by a thousand cuts" - many small blocks that add up.
62
+
63
+ | Option | Type | Default | Description |
64
+ |--------|------|---------|-------------|
65
+ | `cumulative_blocking_enabled` | bool | `True` | Enable cumulative detection |
66
+ | `cumulative_blocking_threshold_ms` | float | `200.0` | Alert if total blocking exceeds this... |
67
+ | `cumulative_window_ms` | float | `1000.0` | ...within this time window (ms) |
68
+
69
+ **Example:** With defaults, alerts if blocking totals >200ms within any 1-second window.
70
+
71
+ ---
72
+
73
+ ## Adaptive Threshold
74
+
75
+ Dynamically adjusts threshold based on observed latency. Useful for high-concurrency environments.
76
+
77
+ | Option | Type | Default | Description |
78
+ |--------|------|---------|-------------|
79
+ | `adaptive_threshold` | bool | `False` | Enable adaptive mode |
80
+ | `adaptive_window_size` | int | `1000` | Samples in sliding window |
81
+ | `adaptive_percentile` | float | `0.95` | Percentile for baseline (0.5-0.99) |
82
+ | `adaptive_min_samples` | int | `100` | Min samples before activation |
83
+ | `adaptive_update_interval_ms` | float | `1000.0` | Recalculation frequency (ms) |
84
+
85
+ ---
86
+
87
+ ## Integrations
88
+
89
+ | Option | Type | Default | Description |
90
+ |--------|------|---------|-------------|
91
+ | `prometheus_enabled` | bool | `False` | Expose Prometheus metrics |
92
+
93
+ When enabled, exposes:
94
+ - `loopguard_blocking_events_total` - Counter of blocking events
95
+ - `loopguard_blocking_duration_ms` - Histogram of blocking durations
96
+
97
+ ---
98
+
99
+ ## Common Configurations
100
+
101
+ ### Development (strict enforcement)
102
+ ```python
103
+ config = LoopGuardConfig(dev_mode=True)
104
+ ```
105
+
106
+ ### Production (silent monitoring)
107
+ ```python
108
+ config = LoopGuardConfig(
109
+ enforcement_mode="log",
110
+ prometheus_enabled=True,
111
+ )
112
+ ```
113
+
114
+ ### High-concurrency (adaptive threshold)
115
+ ```python
116
+ config = LoopGuardConfig(
117
+ adaptive_threshold=True,
118
+ adaptive_percentile=0.99,
119
+ )
120
+ ```
121
+
122
+ ### Sensitive detection (lower threshold)
123
+ ```python
124
+ config = LoopGuardConfig(
125
+ monitor_interval_ms=5.0,
126
+ threshold_multiplier=3.0,
127
+ fallback_threshold_ms=30.0,
128
+ )
129
+ ```
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "fastapi-loopguard"
7
- version = "0.4.0"
7
+ version = "0.4.1"
8
8
  description = "Detect event-loop blocking in FastAPI/Starlette with per-request attribution"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -46,7 +46,7 @@ class LoopGuardConfig:
46
46
  adaptive_update_interval_ms: float = 1000.0
47
47
 
48
48
  # Cumulative blocking detection
49
- cumulative_blocking_enabled: bool = False
49
+ cumulative_blocking_enabled: bool = True
50
50
  cumulative_blocking_threshold_ms: float = 200.0
51
51
  cumulative_window_ms: float = 1000.0
52
52
 
@@ -264,10 +264,10 @@ class LoopGuardMiddleware:
264
264
  def _log_console_warning(self, ctx: RequestContext) -> None:
265
265
  """Print attention-grabbing console warning to stderr."""
266
266
  warning = f"""
267
- {'=' * 72}
268
- {'!' * 72}
267
+ {"=" * 72}
268
+ {"!" * 72}
269
269
  LOOPGUARD: Event Loop Blocked!
270
- {'!' * 72}
270
+ {"!" * 72}
271
271
 
272
272
  Request: {ctx.method} {ctx.path}
273
273
  Request ID: {ctx.request_id}
@@ -283,7 +283,7 @@ class LoopGuardMiddleware:
283
283
  subprocess.run(...) -> await asyncio.create_subprocess_exec(...)
284
284
 
285
285
  Docs: https://fastapi.tiangolo.com/async/
286
- {'=' * 72}
286
+ {"=" * 72}
287
287
  """
288
288
  print(warning, file=sys.stderr)
289
289
 
@@ -66,13 +66,25 @@ async def test_cumulative_blocking_detection() -> None:
66
66
 
67
67
 
68
68
  @pytest.mark.asyncio
69
- async def test_cumulative_blocking_disabled_by_default() -> None:
70
- # Verify it doesn't trigger when disabled
69
+ async def test_cumulative_blocking_enabled_by_default() -> None:
70
+ # Verify cumulative blocking is enabled by default
71
71
  config = LoopGuardConfig(
72
72
  enabled=True,
73
73
  monitor_interval_ms=10.0,
74
74
  fallback_threshold_ms=50.0,
75
- # Default is cumulative_blocking_enabled=False
75
+ )
76
+
77
+ assert config.cumulative_blocking_enabled is True
78
+
79
+
80
+ @pytest.mark.asyncio
81
+ async def test_cumulative_blocking_can_be_disabled() -> None:
82
+ # Verify it doesn't trigger when explicitly disabled
83
+ config = LoopGuardConfig(
84
+ enabled=True,
85
+ monitor_interval_ms=10.0,
86
+ fallback_threshold_ms=50.0,
87
+ cumulative_blocking_enabled=False,
76
88
  )
77
89
 
78
90
  assert config.cumulative_blocking_enabled is False