bubble-analysis 0.2.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.

Potentially problematic release.


This version of bubble-analysis might be problematic. Click here for more details.

Files changed (69) hide show
  1. bubble_analysis-0.2.0/LICENSE +21 -0
  2. bubble_analysis-0.2.0/PKG-INFO +264 -0
  3. bubble_analysis-0.2.0/README.md +229 -0
  4. bubble_analysis-0.2.0/bubble/__init__.py +3 -0
  5. bubble_analysis-0.2.0/bubble/cache.py +207 -0
  6. bubble_analysis-0.2.0/bubble/cli.py +470 -0
  7. bubble_analysis-0.2.0/bubble/config.py +52 -0
  8. bubble_analysis-0.2.0/bubble/detectors.py +90 -0
  9. bubble_analysis-0.2.0/bubble/enums.py +65 -0
  10. bubble_analysis-0.2.0/bubble/extractor.py +829 -0
  11. bubble_analysis-0.2.0/bubble/formatters.py +887 -0
  12. bubble_analysis-0.2.0/bubble/integrations/__init__.py +92 -0
  13. bubble_analysis-0.2.0/bubble/integrations/base.py +98 -0
  14. bubble_analysis-0.2.0/bubble/integrations/cli_scripts/__init__.py +49 -0
  15. bubble_analysis-0.2.0/bubble/integrations/cli_scripts/cli.py +108 -0
  16. bubble_analysis-0.2.0/bubble/integrations/cli_scripts/detector.py +149 -0
  17. bubble_analysis-0.2.0/bubble/integrations/django/__init__.py +63 -0
  18. bubble_analysis-0.2.0/bubble/integrations/django/cli.py +111 -0
  19. bubble_analysis-0.2.0/bubble/integrations/django/detector.py +331 -0
  20. bubble_analysis-0.2.0/bubble/integrations/django/semantics.py +40 -0
  21. bubble_analysis-0.2.0/bubble/integrations/fastapi/__init__.py +57 -0
  22. bubble_analysis-0.2.0/bubble/integrations/fastapi/cli.py +110 -0
  23. bubble_analysis-0.2.0/bubble/integrations/fastapi/detector.py +176 -0
  24. bubble_analysis-0.2.0/bubble/integrations/fastapi/semantics.py +14 -0
  25. bubble_analysis-0.2.0/bubble/integrations/flask/__init__.py +57 -0
  26. bubble_analysis-0.2.0/bubble/integrations/flask/cli.py +110 -0
  27. bubble_analysis-0.2.0/bubble/integrations/flask/detector.py +191 -0
  28. bubble_analysis-0.2.0/bubble/integrations/flask/semantics.py +19 -0
  29. bubble_analysis-0.2.0/bubble/integrations/formatters.py +268 -0
  30. bubble_analysis-0.2.0/bubble/integrations/generic/__init__.py +13 -0
  31. bubble_analysis-0.2.0/bubble/integrations/generic/config.py +106 -0
  32. bubble_analysis-0.2.0/bubble/integrations/generic/detector.py +346 -0
  33. bubble_analysis-0.2.0/bubble/integrations/generic/frameworks.py +145 -0
  34. bubble_analysis-0.2.0/bubble/integrations/models.py +68 -0
  35. bubble_analysis-0.2.0/bubble/integrations/queries.py +481 -0
  36. bubble_analysis-0.2.0/bubble/loader.py +118 -0
  37. bubble_analysis-0.2.0/bubble/models.py +397 -0
  38. bubble_analysis-0.2.0/bubble/propagation.py +737 -0
  39. bubble_analysis-0.2.0/bubble/protocols.py +104 -0
  40. bubble_analysis-0.2.0/bubble/queries.py +627 -0
  41. bubble_analysis-0.2.0/bubble/results.py +211 -0
  42. bubble_analysis-0.2.0/bubble/stubs.py +89 -0
  43. bubble_analysis-0.2.0/bubble/timing.py +144 -0
  44. bubble_analysis-0.2.0/bubble_analysis.egg-info/PKG-INFO +264 -0
  45. bubble_analysis-0.2.0/bubble_analysis.egg-info/SOURCES.txt +67 -0
  46. bubble_analysis-0.2.0/bubble_analysis.egg-info/dependency_links.txt +1 -0
  47. bubble_analysis-0.2.0/bubble_analysis.egg-info/entry_points.txt +2 -0
  48. bubble_analysis-0.2.0/bubble_analysis.egg-info/requires.txt +10 -0
  49. bubble_analysis-0.2.0/bubble_analysis.egg-info/top_level.txt +1 -0
  50. bubble_analysis-0.2.0/pyproject.toml +100 -0
  51. bubble_analysis-0.2.0/setup.cfg +4 -0
  52. bubble_analysis-0.2.0/tests/test_callers.py +32 -0
  53. bubble_analysis-0.2.0/tests/test_catches.py +28 -0
  54. bubble_analysis-0.2.0/tests/test_cli_smoke.py +87 -0
  55. bubble_analysis-0.2.0/tests/test_drf_dispatch.py +167 -0
  56. bubble_analysis-0.2.0/tests/test_entrypoints.py +54 -0
  57. bubble_analysis-0.2.0/tests/test_escapes.py +39 -0
  58. bubble_analysis-0.2.0/tests/test_exceptions.py +53 -0
  59. bubble_analysis-0.2.0/tests/test_flask_appbuilder.py +35 -0
  60. bubble_analysis-0.2.0/tests/test_generic_detector.py +193 -0
  61. bubble_analysis-0.2.0/tests/test_generic_handler.py +50 -0
  62. bubble_analysis-0.2.0/tests/test_hierarchy.py +127 -0
  63. bubble_analysis-0.2.0/tests/test_init.py +84 -0
  64. bubble_analysis-0.2.0/tests/test_propagation.py +64 -0
  65. bubble_analysis-0.2.0/tests/test_raises.py +48 -0
  66. bubble_analysis-0.2.0/tests/test_resolution.py +283 -0
  67. bubble_analysis-0.2.0/tests/test_routes_to.py +111 -0
  68. bubble_analysis-0.2.0/tests/test_stats.py +28 -0
  69. bubble_analysis-0.2.0/tests/test_trust_features.py +248 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,264 @@
1
+ Metadata-Version: 2.4
2
+ Name: bubble-analysis
3
+ Version: 0.2.0
4
+ Summary: Static analysis tool for tracing exception flow through Python codebases
5
+ Author-email: Ian McLaughlin <ianm199@github.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/ianm199/flow
8
+ Project-URL: Repository, https://github.com/ianm199/flow
9
+ Project-URL: Issues, https://github.com/ianm199/flow/issues
10
+ Keywords: static-analysis,exceptions,python,code-analysis,flask,fastapi
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Software Development :: Quality Assurance
20
+ Classifier: Topic :: Software Development :: Testing
21
+ Classifier: Typing :: Typed
22
+ Requires-Python: >=3.11
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Requires-Dist: libcst>=1.1.0
26
+ Requires-Dist: typer>=0.12.0
27
+ Requires-Dist: rich>=13.0.0
28
+ Requires-Dist: msgpack>=1.0.0
29
+ Requires-Dist: pyyaml>=6.0
30
+ Provides-Extra: dev
31
+ Requires-Dist: pytest>=7.0; extra == "dev"
32
+ Requires-Dist: pytest-cov>=4.0; extra == "dev"
33
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
34
+ Dynamic: license-file
35
+
36
+ # flow
37
+
38
+ Static analysis tool for tracing exception flow through Python codebases.
39
+
40
+ **What can escape from my API endpoints?** Flow answers this by parsing your code, building a call graph, and computing which exceptions propagate to each entrypoint.
41
+
42
+ ## Quick Start
43
+
44
+ ```bash
45
+ pip install bubble-analysis
46
+ ```
47
+
48
+ ```bash
49
+ # Check Flask routes for uncaught exceptions
50
+ bubble flask audit -d /path/to/project
51
+
52
+ # Check FastAPI routes
53
+ bubble fastapi audit -d /path/to/project
54
+
55
+ # Deep dive into one function
56
+ bubble escapes create_user -d /path/to/project
57
+
58
+ # Visualize the call tree
59
+ bubble trace create_user -d /path/to/project
60
+ ```
61
+
62
+ ## What It Does
63
+
64
+ Flow finds your HTTP routes and CLI scripts, traces the call graph, and reports which exceptions can escape:
65
+
66
+ ```
67
+ $ flow flask audit
68
+
69
+ Scanning 23 flask entrypoints...
70
+
71
+ 3 entrypoints have uncaught exceptions:
72
+
73
+ POST /users/import
74
+ └─ FileNotFoundError (importers.py:45)
75
+ └─ ValidationError (validators.py:12)
76
+
77
+ GET /reports/{id}
78
+ └─ PermissionError (auth.py:89)
79
+
80
+ 20 entrypoints fully covered by exception handlers
81
+ ```
82
+
83
+ For a specific endpoint, see the full picture:
84
+
85
+ ```
86
+ $ flow escapes create_user
87
+
88
+ Exceptions that can escape from POST /users:
89
+
90
+ FRAMEWORK-HANDLED (converted to HTTP response):
91
+ HTTPException
92
+ └─ becomes: HTTP 404
93
+ └─ raised in: routes/users.py:45 (get_user) [high confidence]
94
+
95
+ CAUGHT BY GLOBAL HANDLER:
96
+ ValidationError (@errorhandler(AppError))
97
+ └─ raised in: validators.py:27 (validate_input) [high confidence]
98
+
99
+ UNCAUGHT (will propagate to caller):
100
+ ConnectionError
101
+ └─ raised in: db/client.py:45 (execute) [medium confidence]
102
+ └─ call path: create_user → save_user → db.execute
103
+ ```
104
+
105
+ Visualize as a tree:
106
+
107
+ ```
108
+ $ flow trace create_user
109
+
110
+ POST /users → escapes: ValidationError, ConnectionError
111
+ ├── validate_input() → ValidationError
112
+ │ └── raises ValidationError (validators.py:27)
113
+ └── save_user() → ConnectionError
114
+ └── db.execute() → ConnectionError
115
+ └── raises ConnectionError (db/client.py:45)
116
+ ```
117
+
118
+ ## Features
119
+
120
+ - **Entrypoint detection**: Flask routes, FastAPI routes, CLI scripts (`if __name__ == "__main__"`)
121
+ - **Global handler awareness**: Understands `@errorhandler`, `add_exception_handler`
122
+ - **Exception hierarchy**: Knows that catching `AppError` also catches `ValidationError` if it's a subclass
123
+ - **Polymorphism**: Expands abstract method calls to all concrete implementations
124
+ - **Framework-handled exceptions**: Detects HTTPException, ValidationError → HTTP responses
125
+ - **Confidence levels**: Shows high/medium/low confidence based on resolution quality
126
+ - **Resolution modes**: `--strict` for precision, `--aggressive` for recall
127
+ - **Exception stubs**: Declare what external libraries can raise (requests, sqlalchemy, etc.)
128
+ - **JSON output**: All commands support `-f json` for CI/automation
129
+ - **Caching**: SQLite-based caching for fast repeated analysis
130
+
131
+ ## Commands
132
+
133
+ ### Core Commands (framework-agnostic)
134
+
135
+ | Command | Description |
136
+ |---------|-------------|
137
+ | `bubble raises <exception>` | Find all places an exception is raised |
138
+ | `bubble escapes <function>` | Show what can escape from a specific function |
139
+ | `bubble callers <function>` | Find all callers of a function |
140
+ | `bubble catches <exception>` | Find all places an exception is caught |
141
+ | `bubble trace <function>` | Visualize exception flow as a call tree |
142
+ | `bubble exceptions` | Show the exception class hierarchy |
143
+ | `bubble subclasses <class>` | Show class inheritance tree |
144
+ | `bubble stubs <action>` | Manage exception stubs (`list`, `init`, `validate`) |
145
+ | `bubble stats` | Show codebase statistics |
146
+
147
+ ### Framework-Specific Commands
148
+
149
+ | Command | Description |
150
+ |---------|-------------|
151
+ | `bubble flask audit` | Check Flask routes for escaping exceptions |
152
+ | `bubble flask entrypoints` | List Flask HTTP routes |
153
+ | `bubble flask routes-to <exc>` | Which Flask routes can trigger this exception? |
154
+ | `bubble fastapi audit` | Check FastAPI routes for escaping exceptions |
155
+ | `bubble fastapi entrypoints` | List FastAPI HTTP routes |
156
+ | `bubble fastapi routes-to <exc>` | Which FastAPI routes can trigger this exception? |
157
+ | `bubble cli audit` | Check CLI scripts for escaping exceptions |
158
+ | `bubble cli entrypoints` | List CLI scripts |
159
+ | `bubble cli scripts-to <exc>` | Which CLI scripts can trigger this exception? |
160
+
161
+ All commands accept:
162
+ - `-d, --directory`: Directory to analyze (default: current)
163
+ - `-f, --format`: Output format (`text` or `json`)
164
+ - `--no-cache`: Disable caching
165
+
166
+ The `escapes` command accepts additional flags:
167
+ - `--strict`: High precision mode - only includes precisely resolved calls
168
+ - `--aggressive`: High recall mode - includes fuzzy matches
169
+
170
+ ## Supported Frameworks
171
+
172
+ **Detected automatically:**
173
+ - Flask (`@app.route`, `@blueprint.route`, `@app.errorhandler`)
174
+ - FastAPI (`@router.get/post/put/delete`, `add_exception_handler`)
175
+ - CLI scripts (`if __name__ == "__main__"`)
176
+
177
+ **Not yet supported:**
178
+ - Django
179
+ - Celery tasks
180
+ - Scheduled jobs (APScheduler, etc.)
181
+
182
+ Custom patterns can be added via `.flow/detectors/` (run `bubble init` to set up).
183
+
184
+ ## Adding Custom Detectors
185
+
186
+ Flow is designed to be extended with AI coding agents. The detector interface is intentionally simple: implement a protocol that returns entrypoints and handlers from parsed code.
187
+
188
+ To add support for a new framework (Django, Celery, your internal RPC layer, etc.):
189
+
190
+ 1. Run `bubble init` to create the `.flow/` directory structure
191
+ 2. Point your AI agent at `flow/protocols.py` to see the `EntrypointDetector` interface
192
+ 3. Ask it to implement a detector for your framework in `.flow/detectors/`
193
+
194
+ Example prompt for an AI agent:
195
+
196
+ ```
197
+ Read flow/protocols.py and flow/detectors.py to understand how entrypoint
198
+ detection works. Then implement a detector for Django that finds:
199
+ - Views decorated with @api_view
200
+ - Class-based views inheriting from APIView
201
+ - URL patterns in urls.py
202
+
203
+ Put the implementation in .flow/detectors/django.py
204
+ ```
205
+
206
+ The detector just needs to implement:
207
+ - `detect_entrypoints(functions, classes, ...)` → list of `Entrypoint`
208
+ - `detect_global_handlers(...)` → list of `GlobalHandler`
209
+
210
+ Flow will automatically load any `.py` files in `.flow/detectors/` and use them alongside the built-in Flask/FastAPI detectors.
211
+
212
+ ## Configuration
213
+
214
+ Flow can be configured via `.flow/config.yaml`:
215
+
216
+ ```yaml
217
+ resolution_mode: default # "strict", "default", or "aggressive"
218
+ exclude:
219
+ - vendor
220
+ - migrations
221
+ ```
222
+
223
+ ### Exception Stubs
224
+
225
+ Flow includes built-in stubs for common libraries (requests, sqlalchemy, httpx, redis, boto3). These declare what exceptions external library functions can raise.
226
+
227
+ Add custom stubs in `.flow/stubs/`:
228
+
229
+ ```yaml
230
+ # .flow/stubs/mylib.yaml
231
+ mylib:
232
+ do_thing:
233
+ - MyLibError
234
+ - TimeoutError
235
+ ```
236
+
237
+ Manage stubs with `bubble stubs list` and `bubble stubs validate`.
238
+
239
+ ## How It Works
240
+
241
+ 1. **Parse**: LibCST parses all Python files
242
+ 2. **Extract**: Find functions, classes, raise/catch sites, calls, entrypoints
243
+ 3. **Build call graph**: Track who calls whom, resolve method calls
244
+ 4. **Propagate**: Fixed-point iteration computes which exceptions escape each function
245
+ 5. **Report**: For each entrypoint, show caught vs uncaught exceptions
246
+
247
+ ## Limitations
248
+
249
+ - **Over-approximation**: May report more exceptions than actually possible (e.g., all implementations of an abstract method)
250
+ - **Under-approximation**: Dynamic dispatch, `eval()`, and external libraries can't be fully traced
251
+ - **No runtime info**: Analysis is purely static
252
+
253
+ ## Development
254
+
255
+ ```bash
256
+ git clone https://github.com/ianm199/flow
257
+ cd flow
258
+ pip install -e ".[dev]"
259
+ pytest
260
+ ```
261
+
262
+ ## License
263
+
264
+ MIT
@@ -0,0 +1,229 @@
1
+ # flow
2
+
3
+ Static analysis tool for tracing exception flow through Python codebases.
4
+
5
+ **What can escape from my API endpoints?** Flow answers this by parsing your code, building a call graph, and computing which exceptions propagate to each entrypoint.
6
+
7
+ ## Quick Start
8
+
9
+ ```bash
10
+ pip install bubble-analysis
11
+ ```
12
+
13
+ ```bash
14
+ # Check Flask routes for uncaught exceptions
15
+ bubble flask audit -d /path/to/project
16
+
17
+ # Check FastAPI routes
18
+ bubble fastapi audit -d /path/to/project
19
+
20
+ # Deep dive into one function
21
+ bubble escapes create_user -d /path/to/project
22
+
23
+ # Visualize the call tree
24
+ bubble trace create_user -d /path/to/project
25
+ ```
26
+
27
+ ## What It Does
28
+
29
+ Flow finds your HTTP routes and CLI scripts, traces the call graph, and reports which exceptions can escape:
30
+
31
+ ```
32
+ $ flow flask audit
33
+
34
+ Scanning 23 flask entrypoints...
35
+
36
+ 3 entrypoints have uncaught exceptions:
37
+
38
+ POST /users/import
39
+ └─ FileNotFoundError (importers.py:45)
40
+ └─ ValidationError (validators.py:12)
41
+
42
+ GET /reports/{id}
43
+ └─ PermissionError (auth.py:89)
44
+
45
+ 20 entrypoints fully covered by exception handlers
46
+ ```
47
+
48
+ For a specific endpoint, see the full picture:
49
+
50
+ ```
51
+ $ flow escapes create_user
52
+
53
+ Exceptions that can escape from POST /users:
54
+
55
+ FRAMEWORK-HANDLED (converted to HTTP response):
56
+ HTTPException
57
+ └─ becomes: HTTP 404
58
+ └─ raised in: routes/users.py:45 (get_user) [high confidence]
59
+
60
+ CAUGHT BY GLOBAL HANDLER:
61
+ ValidationError (@errorhandler(AppError))
62
+ └─ raised in: validators.py:27 (validate_input) [high confidence]
63
+
64
+ UNCAUGHT (will propagate to caller):
65
+ ConnectionError
66
+ └─ raised in: db/client.py:45 (execute) [medium confidence]
67
+ └─ call path: create_user → save_user → db.execute
68
+ ```
69
+
70
+ Visualize as a tree:
71
+
72
+ ```
73
+ $ flow trace create_user
74
+
75
+ POST /users → escapes: ValidationError, ConnectionError
76
+ ├── validate_input() → ValidationError
77
+ │ └── raises ValidationError (validators.py:27)
78
+ └── save_user() → ConnectionError
79
+ └── db.execute() → ConnectionError
80
+ └── raises ConnectionError (db/client.py:45)
81
+ ```
82
+
83
+ ## Features
84
+
85
+ - **Entrypoint detection**: Flask routes, FastAPI routes, CLI scripts (`if __name__ == "__main__"`)
86
+ - **Global handler awareness**: Understands `@errorhandler`, `add_exception_handler`
87
+ - **Exception hierarchy**: Knows that catching `AppError` also catches `ValidationError` if it's a subclass
88
+ - **Polymorphism**: Expands abstract method calls to all concrete implementations
89
+ - **Framework-handled exceptions**: Detects HTTPException, ValidationError → HTTP responses
90
+ - **Confidence levels**: Shows high/medium/low confidence based on resolution quality
91
+ - **Resolution modes**: `--strict` for precision, `--aggressive` for recall
92
+ - **Exception stubs**: Declare what external libraries can raise (requests, sqlalchemy, etc.)
93
+ - **JSON output**: All commands support `-f json` for CI/automation
94
+ - **Caching**: SQLite-based caching for fast repeated analysis
95
+
96
+ ## Commands
97
+
98
+ ### Core Commands (framework-agnostic)
99
+
100
+ | Command | Description |
101
+ |---------|-------------|
102
+ | `bubble raises <exception>` | Find all places an exception is raised |
103
+ | `bubble escapes <function>` | Show what can escape from a specific function |
104
+ | `bubble callers <function>` | Find all callers of a function |
105
+ | `bubble catches <exception>` | Find all places an exception is caught |
106
+ | `bubble trace <function>` | Visualize exception flow as a call tree |
107
+ | `bubble exceptions` | Show the exception class hierarchy |
108
+ | `bubble subclasses <class>` | Show class inheritance tree |
109
+ | `bubble stubs <action>` | Manage exception stubs (`list`, `init`, `validate`) |
110
+ | `bubble stats` | Show codebase statistics |
111
+
112
+ ### Framework-Specific Commands
113
+
114
+ | Command | Description |
115
+ |---------|-------------|
116
+ | `bubble flask audit` | Check Flask routes for escaping exceptions |
117
+ | `bubble flask entrypoints` | List Flask HTTP routes |
118
+ | `bubble flask routes-to <exc>` | Which Flask routes can trigger this exception? |
119
+ | `bubble fastapi audit` | Check FastAPI routes for escaping exceptions |
120
+ | `bubble fastapi entrypoints` | List FastAPI HTTP routes |
121
+ | `bubble fastapi routes-to <exc>` | Which FastAPI routes can trigger this exception? |
122
+ | `bubble cli audit` | Check CLI scripts for escaping exceptions |
123
+ | `bubble cli entrypoints` | List CLI scripts |
124
+ | `bubble cli scripts-to <exc>` | Which CLI scripts can trigger this exception? |
125
+
126
+ All commands accept:
127
+ - `-d, --directory`: Directory to analyze (default: current)
128
+ - `-f, --format`: Output format (`text` or `json`)
129
+ - `--no-cache`: Disable caching
130
+
131
+ The `escapes` command accepts additional flags:
132
+ - `--strict`: High precision mode - only includes precisely resolved calls
133
+ - `--aggressive`: High recall mode - includes fuzzy matches
134
+
135
+ ## Supported Frameworks
136
+
137
+ **Detected automatically:**
138
+ - Flask (`@app.route`, `@blueprint.route`, `@app.errorhandler`)
139
+ - FastAPI (`@router.get/post/put/delete`, `add_exception_handler`)
140
+ - CLI scripts (`if __name__ == "__main__"`)
141
+
142
+ **Not yet supported:**
143
+ - Django
144
+ - Celery tasks
145
+ - Scheduled jobs (APScheduler, etc.)
146
+
147
+ Custom patterns can be added via `.flow/detectors/` (run `bubble init` to set up).
148
+
149
+ ## Adding Custom Detectors
150
+
151
+ Flow is designed to be extended with AI coding agents. The detector interface is intentionally simple: implement a protocol that returns entrypoints and handlers from parsed code.
152
+
153
+ To add support for a new framework (Django, Celery, your internal RPC layer, etc.):
154
+
155
+ 1. Run `bubble init` to create the `.flow/` directory structure
156
+ 2. Point your AI agent at `flow/protocols.py` to see the `EntrypointDetector` interface
157
+ 3. Ask it to implement a detector for your framework in `.flow/detectors/`
158
+
159
+ Example prompt for an AI agent:
160
+
161
+ ```
162
+ Read flow/protocols.py and flow/detectors.py to understand how entrypoint
163
+ detection works. Then implement a detector for Django that finds:
164
+ - Views decorated with @api_view
165
+ - Class-based views inheriting from APIView
166
+ - URL patterns in urls.py
167
+
168
+ Put the implementation in .flow/detectors/django.py
169
+ ```
170
+
171
+ The detector just needs to implement:
172
+ - `detect_entrypoints(functions, classes, ...)` → list of `Entrypoint`
173
+ - `detect_global_handlers(...)` → list of `GlobalHandler`
174
+
175
+ Flow will automatically load any `.py` files in `.flow/detectors/` and use them alongside the built-in Flask/FastAPI detectors.
176
+
177
+ ## Configuration
178
+
179
+ Flow can be configured via `.flow/config.yaml`:
180
+
181
+ ```yaml
182
+ resolution_mode: default # "strict", "default", or "aggressive"
183
+ exclude:
184
+ - vendor
185
+ - migrations
186
+ ```
187
+
188
+ ### Exception Stubs
189
+
190
+ Flow includes built-in stubs for common libraries (requests, sqlalchemy, httpx, redis, boto3). These declare what exceptions external library functions can raise.
191
+
192
+ Add custom stubs in `.flow/stubs/`:
193
+
194
+ ```yaml
195
+ # .flow/stubs/mylib.yaml
196
+ mylib:
197
+ do_thing:
198
+ - MyLibError
199
+ - TimeoutError
200
+ ```
201
+
202
+ Manage stubs with `bubble stubs list` and `bubble stubs validate`.
203
+
204
+ ## How It Works
205
+
206
+ 1. **Parse**: LibCST parses all Python files
207
+ 2. **Extract**: Find functions, classes, raise/catch sites, calls, entrypoints
208
+ 3. **Build call graph**: Track who calls whom, resolve method calls
209
+ 4. **Propagate**: Fixed-point iteration computes which exceptions escape each function
210
+ 5. **Report**: For each entrypoint, show caught vs uncaught exceptions
211
+
212
+ ## Limitations
213
+
214
+ - **Over-approximation**: May report more exceptions than actually possible (e.g., all implementations of an abstract method)
215
+ - **Under-approximation**: Dynamic dispatch, `eval()`, and external libraries can't be fully traced
216
+ - **No runtime info**: Analysis is purely static
217
+
218
+ ## Development
219
+
220
+ ```bash
221
+ git clone https://github.com/ianm199/flow
222
+ cd flow
223
+ pip install -e ".[dev]"
224
+ pytest
225
+ ```
226
+
227
+ ## License
228
+
229
+ MIT
@@ -0,0 +1,3 @@
1
+ """Bubble: Exception flow analysis for Python codebases."""
2
+
3
+ __version__ = "0.2.0"