routeflow-python 0.1.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.
- routeflow_python-0.1.0/LICENSE +21 -0
- routeflow_python-0.1.0/PKG-INFO +453 -0
- routeflow_python-0.1.0/README.md +408 -0
- routeflow_python-0.1.0/pyproject.toml +60 -0
- routeflow_python-0.1.0/setup.cfg +4 -0
- routeflow_python-0.1.0/src/routeflow_python/__init__.py +61 -0
- routeflow_python-0.1.0/src/routeflow_python/client.py +313 -0
- routeflow_python-0.1.0/src/routeflow_python/config.py +119 -0
- routeflow_python-0.1.0/src/routeflow_python/django.py +267 -0
- routeflow_python-0.1.0/src/routeflow_python/fastapi.py +193 -0
- routeflow_python-0.1.0/src/routeflow_python/flask.py +237 -0
- routeflow_python-0.1.0/src/routeflow_python/models.py +178 -0
- routeflow_python-0.1.0/src/routeflow_python.egg-info/PKG-INFO +453 -0
- routeflow_python-0.1.0/src/routeflow_python.egg-info/SOURCES.txt +18 -0
- routeflow_python-0.1.0/src/routeflow_python.egg-info/dependency_links.txt +1 -0
- routeflow_python-0.1.0/src/routeflow_python.egg-info/requires.txt +27 -0
- routeflow_python-0.1.0/src/routeflow_python.egg-info/top_level.txt +1 -0
- routeflow_python-0.1.0/tests/test_django_basic.py +218 -0
- routeflow_python-0.1.0/tests/test_fastapi_basic.py +141 -0
- routeflow_python-0.1.0/tests/test_flask_basic.py +98 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Routeflow
|
|
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,453 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: routeflow-python
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python SDK for Routeflow backend telemetry
|
|
5
|
+
Author: Routeflow Team
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/fatherGoose1/routeflow-python
|
|
8
|
+
Project-URL: Repository, https://github.com/fatherGoose1/routeflow-python
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Framework :: Flask
|
|
17
|
+
Classifier: Framework :: FastAPI
|
|
18
|
+
Classifier: Framework :: Django
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: requests>=2.28.0
|
|
23
|
+
Provides-Extra: flask
|
|
24
|
+
Requires-Dist: flask>=2.0.0; extra == "flask"
|
|
25
|
+
Provides-Extra: fastapi
|
|
26
|
+
Requires-Dist: fastapi>=0.100.0; extra == "fastapi"
|
|
27
|
+
Requires-Dist: uvicorn>=0.20.0; extra == "fastapi"
|
|
28
|
+
Provides-Extra: django
|
|
29
|
+
Requires-Dist: django>=3.2.0; extra == "django"
|
|
30
|
+
Provides-Extra: all
|
|
31
|
+
Requires-Dist: flask>=2.0.0; extra == "all"
|
|
32
|
+
Requires-Dist: fastapi>=0.100.0; extra == "all"
|
|
33
|
+
Requires-Dist: uvicorn>=0.20.0; extra == "all"
|
|
34
|
+
Requires-Dist: django>=3.2.0; extra == "all"
|
|
35
|
+
Provides-Extra: dev
|
|
36
|
+
Requires-Dist: flask>=2.0.0; extra == "dev"
|
|
37
|
+
Requires-Dist: fastapi>=0.100.0; extra == "dev"
|
|
38
|
+
Requires-Dist: uvicorn>=0.20.0; extra == "dev"
|
|
39
|
+
Requires-Dist: django>=3.2.0; extra == "dev"
|
|
40
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
41
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
42
|
+
Requires-Dist: requests-mock>=1.9.0; extra == "dev"
|
|
43
|
+
Requires-Dist: httpx>=0.24.0; extra == "dev"
|
|
44
|
+
Dynamic: license-file
|
|
45
|
+
|
|
46
|
+
# Routeflow Python SDK
|
|
47
|
+
|
|
48
|
+
**Production-ready backend instrumentation for Flask, FastAPI, and Django applications.**
|
|
49
|
+
|
|
50
|
+
Automatically capture and send backend request telemetry to Routeflow with zero per-route changes. Non-blocking, efficient, and privacy-focused.
|
|
51
|
+
|
|
52
|
+
## Features
|
|
53
|
+
|
|
54
|
+
- ✅ **One-line integration** - Just `init_routeflow(app)`
|
|
55
|
+
- ✅ **Non-blocking** - Background worker with batching, no request latency
|
|
56
|
+
- ✅ **Privacy-first** - No request bodies, headers (except trace headers), or query strings
|
|
57
|
+
- ✅ **Production-ready** - Retry logic, queue limits, graceful degradation
|
|
58
|
+
- ✅ **Lightweight** - Minimal dependencies (just `requests`)
|
|
59
|
+
- ✅ **Observable** - Built-in stats for monitoring SDK behavior
|
|
60
|
+
|
|
61
|
+
## Installation
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
pip install routeflow-python
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Or install with your framework:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# For Flask
|
|
71
|
+
pip install routeflow-python[flask]
|
|
72
|
+
|
|
73
|
+
# For FastAPI
|
|
74
|
+
pip install routeflow-python[fastapi]
|
|
75
|
+
|
|
76
|
+
# For Django
|
|
77
|
+
pip install routeflow-python[django]
|
|
78
|
+
|
|
79
|
+
# For all frameworks
|
|
80
|
+
pip install routeflow-python[all]
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Quick Start
|
|
84
|
+
|
|
85
|
+
### 1. Set Environment Variables
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
export ROUTEFLOW_INGEST_KEY=rf_live_your-ingest-key-here
|
|
89
|
+
export ROUTEFLOW_ENV=production
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
The Routeflow SDK automatically sends data to Routeflow Cloud.
|
|
93
|
+
|
|
94
|
+
### 2. Initialize in Your App
|
|
95
|
+
|
|
96
|
+
Choose your framework:
|
|
97
|
+
|
|
98
|
+
#### Flask
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
from flask import Flask
|
|
102
|
+
from routeflow_python import init_routeflow
|
|
103
|
+
|
|
104
|
+
app = Flask(__name__)
|
|
105
|
+
|
|
106
|
+
# ONE LINE - that's it!
|
|
107
|
+
init_routeflow(app)
|
|
108
|
+
|
|
109
|
+
@app.route('/api/users')
|
|
110
|
+
def get_users():
|
|
111
|
+
return {'users': []}
|
|
112
|
+
|
|
113
|
+
@app.route('/api/users/<user_id>')
|
|
114
|
+
def get_user(user_id):
|
|
115
|
+
return {'id': user_id, 'name': 'John'}
|
|
116
|
+
|
|
117
|
+
if __name__ == '__main__':
|
|
118
|
+
app.run()
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
#### FastAPI
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
from fastapi import FastAPI
|
|
125
|
+
from routeflow_python import init_routeflow_fastapi
|
|
126
|
+
|
|
127
|
+
app = FastAPI()
|
|
128
|
+
|
|
129
|
+
# ONE LINE - that's it!
|
|
130
|
+
init_routeflow_fastapi(app)
|
|
131
|
+
|
|
132
|
+
@app.get("/api/users")
|
|
133
|
+
def get_users():
|
|
134
|
+
return {"users": []}
|
|
135
|
+
|
|
136
|
+
@app.get("/api/users/{user_id}")
|
|
137
|
+
def get_user(user_id: int):
|
|
138
|
+
return {"id": user_id, "name": "John"}
|
|
139
|
+
|
|
140
|
+
if __name__ == "__main__":
|
|
141
|
+
import uvicorn
|
|
142
|
+
uvicorn.run(app, host="0.0.0.0", port=8000)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
#### Django
|
|
146
|
+
|
|
147
|
+
Add middleware in `settings.py`:
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
# settings.py
|
|
151
|
+
from routeflow_python import init_routeflow_django
|
|
152
|
+
|
|
153
|
+
MIDDLEWARE = [
|
|
154
|
+
'django.middleware.security.SecurityMiddleware',
|
|
155
|
+
# ... other middleware
|
|
156
|
+
'routeflow_python.django.RouteflowDjangoMiddleware', # Add this
|
|
157
|
+
]
|
|
158
|
+
|
|
159
|
+
# Initialize Routeflow
|
|
160
|
+
init_routeflow_django()
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Then define views as normal:
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
# views.py
|
|
167
|
+
from django.http import JsonResponse
|
|
168
|
+
|
|
169
|
+
def get_users(request):
|
|
170
|
+
return JsonResponse({"users": []})
|
|
171
|
+
|
|
172
|
+
def get_user(request, user_id):
|
|
173
|
+
return JsonResponse({"id": user_id, "name": "John"})
|
|
174
|
+
|
|
175
|
+
# urls.py
|
|
176
|
+
from django.urls import path
|
|
177
|
+
from .views import get_users, get_user
|
|
178
|
+
|
|
179
|
+
urlpatterns = [
|
|
180
|
+
path('api/users/', get_users),
|
|
181
|
+
path('api/users/<int:user_id>/', get_user),
|
|
182
|
+
]
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### 3. That's It! 🎉
|
|
186
|
+
|
|
187
|
+
Routeflow will now automatically capture:
|
|
188
|
+
- Request timestamps
|
|
189
|
+
- HTTP methods and routes (e.g., `GET /api/users/<user_id>`)
|
|
190
|
+
- Status codes
|
|
191
|
+
- Latency
|
|
192
|
+
- Environment info
|
|
193
|
+
- Code version (from CI variables)
|
|
194
|
+
|
|
195
|
+
## What Data is Collected?
|
|
196
|
+
|
|
197
|
+
### ✅ Collected
|
|
198
|
+
|
|
199
|
+
- **Timestamp**: When the request started
|
|
200
|
+
- **Trace ID**: From `x-routeflow-trace-id` header or auto-generated
|
|
201
|
+
- **Frontend Route**: From `x-routeflow-frontend-route` header (e.g., `/checkout`)
|
|
202
|
+
- **Backend Method**: HTTP method (GET, POST, etc.)
|
|
203
|
+
- **Backend Path**: Route pattern (e.g., `/api/orders`, `/api/users/{user_id}`)
|
|
204
|
+
- **Handler**: Endpoint/view name (e.g., `api.create_order`, `myapp.views.get_user`)
|
|
205
|
+
- **Status Code**: HTTP response status
|
|
206
|
+
- **Latency**: Request duration in milliseconds
|
|
207
|
+
- **Environment**: From `ROUTEFLOW_ENV`
|
|
208
|
+
- **Code Version**: From `ROUTEFLOW_CODE_VERSION` or CI variables
|
|
209
|
+
|
|
210
|
+
### ❌ NOT Collected
|
|
211
|
+
|
|
212
|
+
- Request bodies
|
|
213
|
+
- Request headers (except trace headers)
|
|
214
|
+
- Query strings
|
|
215
|
+
- Path parameters
|
|
216
|
+
- User data
|
|
217
|
+
- Secrets or credentials
|
|
218
|
+
- Exception messages or stack traces
|
|
219
|
+
|
|
220
|
+
**Privacy is built-in.** Routes are automatically sanitized to remove query strings.
|
|
221
|
+
|
|
222
|
+
## Configuration
|
|
223
|
+
|
|
224
|
+
### Environment Variables
|
|
225
|
+
|
|
226
|
+
| Variable | Required | Default | Description |
|
|
227
|
+
|----------|----------|---------|-------------|
|
|
228
|
+
| `ROUTEFLOW_INGEST_KEY` | ✅ Yes | - | API key for authentication |
|
|
229
|
+
| `ROUTEFLOW_ENV` | No | `development` | Environment name |
|
|
230
|
+
| `ROUTEFLOW_ENABLED` | No | `true` | Enable/disable SDK |
|
|
231
|
+
| `ROUTEFLOW_SAMPLE_RATE` | No | `1.0` | Sampling rate (0.0-1.0) |
|
|
232
|
+
| `ROUTEFLOW_CODE_VERSION` | No | Auto-detected | Code/git version |
|
|
233
|
+
| `ROUTEFLOW_MAX_EVENTS_PER_BATCH` | No | `50` | Max events per batch |
|
|
234
|
+
| `ROUTEFLOW_FLUSH_INTERVAL_MS` | No | `1000` | Flush interval in ms |
|
|
235
|
+
| `ROUTEFLOW_QUEUE_MAXSIZE` | No | `5000` | Max queue size |
|
|
236
|
+
| `ROUTEFLOW_TIMEOUT_MS` | No | `1500` | HTTP timeout in ms |
|
|
237
|
+
|
|
238
|
+
### Programmatic Configuration
|
|
239
|
+
|
|
240
|
+
You can override environment variables programmatically:
|
|
241
|
+
|
|
242
|
+
```python
|
|
243
|
+
init_routeflow(
|
|
244
|
+
app,
|
|
245
|
+
ingest_key="rf_live_your-key",
|
|
246
|
+
environment="production",
|
|
247
|
+
sample_rate=0.1, # Sample 10% of requests
|
|
248
|
+
)
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## Performance & Behavior
|
|
252
|
+
|
|
253
|
+
### Non-Blocking Design
|
|
254
|
+
|
|
255
|
+
The SDK is designed to **never slow down your application**:
|
|
256
|
+
|
|
257
|
+
1. **No I/O on request thread** - Events are enqueued instantly
|
|
258
|
+
2. **Background worker** - Separate thread batches and sends events
|
|
259
|
+
3. **Bounded queue** - If queue fills, events are dropped (not blocked)
|
|
260
|
+
4. **Graceful degradation** - Backend down? Events are dropped safely
|
|
261
|
+
|
|
262
|
+
### Batching
|
|
263
|
+
|
|
264
|
+
Events are batched for efficiency:
|
|
265
|
+
- Batches up to 50 events (configurable)
|
|
266
|
+
- Flushes every 1000ms (configurable)
|
|
267
|
+
- Automatic flush on process exit (best-effort)
|
|
268
|
+
|
|
269
|
+
### Retry Logic
|
|
270
|
+
|
|
271
|
+
- **5xx errors**: Retry with exponential backoff (3 attempts max)
|
|
272
|
+
- **4xx errors**: No retry (validation/auth errors)
|
|
273
|
+
- **Network errors**: Retry with backoff
|
|
274
|
+
- **Final failure**: Drop batch and increment counter
|
|
275
|
+
|
|
276
|
+
### Queue Behavior
|
|
277
|
+
|
|
278
|
+
If the queue fills up (5000 events by default):
|
|
279
|
+
- New events are **dropped immediately**
|
|
280
|
+
- A warning is logged
|
|
281
|
+
- Counter `dropped_queue_full` is incremented
|
|
282
|
+
- **Your app is never blocked**
|
|
283
|
+
|
|
284
|
+
## Monitoring SDK Health
|
|
285
|
+
|
|
286
|
+
Get real-time statistics:
|
|
287
|
+
|
|
288
|
+
```python
|
|
289
|
+
from routeflow_python import get_routeflow_stats
|
|
290
|
+
|
|
291
|
+
stats = get_routeflow_stats()
|
|
292
|
+
print(stats)
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Output:
|
|
296
|
+
```python
|
|
297
|
+
{
|
|
298
|
+
'enqueued': 1523,
|
|
299
|
+
'sent': 1500,
|
|
300
|
+
'dropped_queue_full': 0,
|
|
301
|
+
'dropped_sampled': 23,
|
|
302
|
+
'dropped_validation': 0,
|
|
303
|
+
'dropped_http_4xx': 0,
|
|
304
|
+
'dropped_http_5xx': 0,
|
|
305
|
+
'retries': 2,
|
|
306
|
+
'last_error_at': None
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Metrics Explained
|
|
311
|
+
|
|
312
|
+
- `enqueued`: Events successfully queued
|
|
313
|
+
- `sent`: Events successfully delivered
|
|
314
|
+
- `dropped_sampled`: Events skipped due to sampling
|
|
315
|
+
- `dropped_queue_full`: Events dropped due to full queue
|
|
316
|
+
- `dropped_validation`: Events failed validation
|
|
317
|
+
- `dropped_http_4xx`: Events dropped due to 4xx errors
|
|
318
|
+
- `dropped_http_5xx`: Events dropped after retries failed
|
|
319
|
+
- `retries`: Number of retry attempts
|
|
320
|
+
- `last_error_at`: Timestamp of last error (ISO 8601)
|
|
321
|
+
|
|
322
|
+
## Frontend Integration
|
|
323
|
+
|
|
324
|
+
Your frontend should send these headers:
|
|
325
|
+
|
|
326
|
+
```javascript
|
|
327
|
+
fetch('/api/users', {
|
|
328
|
+
headers: {
|
|
329
|
+
'x-routeflow-trace-id': generateTraceId(), // UUID
|
|
330
|
+
'x-routeflow-frontend-route': window.location.pathname, // e.g., "/checkout"
|
|
331
|
+
}
|
|
332
|
+
})
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
If these headers are not present:
|
|
336
|
+
- `trace_id` is auto-generated
|
|
337
|
+
- `frontend_route` defaults to `"__unknown__"`
|
|
338
|
+
|
|
339
|
+
## Advanced Usage
|
|
340
|
+
|
|
341
|
+
### Manual Shutdown
|
|
342
|
+
|
|
343
|
+
For testing or graceful shutdown:
|
|
344
|
+
|
|
345
|
+
```python
|
|
346
|
+
from routeflow_python import shutdown_routeflow
|
|
347
|
+
|
|
348
|
+
# Flush events and stop worker (waits up to 2 seconds)
|
|
349
|
+
shutdown_routeflow(timeout_sec=2.0)
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
This is called automatically on process exit via `atexit`.
|
|
353
|
+
|
|
354
|
+
### Sampling
|
|
355
|
+
|
|
356
|
+
Sample only a percentage of requests:
|
|
357
|
+
|
|
358
|
+
```python
|
|
359
|
+
# Sample 10% of requests
|
|
360
|
+
init_routeflow(app, sample_rate=0.1)
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
Or via environment:
|
|
364
|
+
```bash
|
|
365
|
+
export ROUTEFLOW_SAMPLE_RATE=0.1
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
### Disable in Development
|
|
369
|
+
|
|
370
|
+
```bash
|
|
371
|
+
export ROUTEFLOW_ENABLED=false
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
Or:
|
|
375
|
+
```python
|
|
376
|
+
init_routeflow(app, enabled=False)
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
## Example Application
|
|
380
|
+
|
|
381
|
+
```python
|
|
382
|
+
from flask import Flask, jsonify
|
|
383
|
+
from routeflow_python import init_routeflow, get_routeflow_stats
|
|
384
|
+
|
|
385
|
+
app = Flask(__name__)
|
|
386
|
+
init_routeflow(app)
|
|
387
|
+
|
|
388
|
+
@app.route('/')
|
|
389
|
+
def index():
|
|
390
|
+
return {'message': 'Hello World'}
|
|
391
|
+
|
|
392
|
+
@app.route('/api/users/<int:user_id>')
|
|
393
|
+
def get_user(user_id):
|
|
394
|
+
return {'id': user_id, 'name': 'John'}
|
|
395
|
+
|
|
396
|
+
@app.route('/_stats/routeflow')
|
|
397
|
+
def routeflow_stats():
|
|
398
|
+
"""Endpoint to check SDK health."""
|
|
399
|
+
return jsonify(get_routeflow_stats())
|
|
400
|
+
|
|
401
|
+
if __name__ == '__main__':
|
|
402
|
+
app.run(debug=True)
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
Test it:
|
|
406
|
+
```bash
|
|
407
|
+
curl http://localhost:5000/api/users/123
|
|
408
|
+
curl http://localhost:5000/_stats/routeflow
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
## Error Handling
|
|
412
|
+
|
|
413
|
+
The SDK is designed to **never crash your application**:
|
|
414
|
+
|
|
415
|
+
- All exceptions are caught and logged
|
|
416
|
+
- Failed events are dropped, not retried indefinitely
|
|
417
|
+
- Queue overflow drops events instead of blocking
|
|
418
|
+
- Invalid configuration logs warnings but doesn't crash
|
|
419
|
+
|
|
420
|
+
## Code Version Detection
|
|
421
|
+
|
|
422
|
+
The SDK automatically detects code version from:
|
|
423
|
+
|
|
424
|
+
1. `ROUTEFLOW_CODE_VERSION` (explicit)
|
|
425
|
+
2. `VERCEL_GIT_COMMIT_SHA` (Vercel)
|
|
426
|
+
3. `GIT_SHA` (generic)
|
|
427
|
+
4. `COMMIT_SHA` (generic)
|
|
428
|
+
5. `SOURCE_VERSION` (Azure)
|
|
429
|
+
6. `HEROKU_SLUG_COMMIT` (Heroku)
|
|
430
|
+
|
|
431
|
+
Or set it explicitly:
|
|
432
|
+
```bash
|
|
433
|
+
export ROUTEFLOW_CODE_VERSION=$(git rev-parse HEAD)
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
## Requirements
|
|
437
|
+
|
|
438
|
+
- Python 3.10+
|
|
439
|
+
- requests >= 2.28.0
|
|
440
|
+
|
|
441
|
+
**Framework-specific:**
|
|
442
|
+
- Flask 2.x or 3.x (for Flask integration)
|
|
443
|
+
- FastAPI 0.100+ (for FastAPI integration)
|
|
444
|
+
- Django 3.2+ / 4.x / 5.x (for Django integration)
|
|
445
|
+
|
|
446
|
+
## License
|
|
447
|
+
|
|
448
|
+
MIT
|
|
449
|
+
|
|
450
|
+
## Support
|
|
451
|
+
|
|
452
|
+
For issues, questions, or feature requests, visit the [Routeflow repository](https://github.com/routeflow/routeflow).
|
|
453
|
+
# routeflow-python
|