turboapi 0.3.20__tar.gz → 0.3.23__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.
- {turboapi-0.3.20 → turboapi-0.3.23}/Cargo.lock +15 -1
- {turboapi-0.3.20 → turboapi-0.3.23}/Cargo.toml +2 -1
- {turboapi-0.3.20 → turboapi-0.3.23}/PKG-INFO +1 -1
- turboapi-0.3.23/benchmark_output.txt +95 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/pyproject.toml +1 -1
- {turboapi-0.3.20 → turboapi-0.3.23}/python/pyproject.toml +1 -1
- {turboapi-0.3.20 → turboapi-0.3.23/python}/turboapi/request_handler.py +22 -27
- {turboapi-0.3.20 → turboapi-0.3.23}/src/server.rs +44 -6
- turboapi-0.3.23/tests/async_benchmark.sh +64 -0
- turboapi-0.3.23/tests/fastapi_v0_3_20_equivalent.py +41 -0
- turboapi-0.3.23/tests/quick_benchmark.sh +63 -0
- turboapi-0.3.23/tests/run_v0_3_20_benchmark.py +235 -0
- turboapi-0.3.23/tests/test_async_benchmark.py +55 -0
- turboapi-0.3.23/tests/test_v0_3_20_server.py +55 -0
- turboapi-0.3.23/tests/test_v0_3_21_async.py +53 -0
- {turboapi-0.3.20/python → turboapi-0.3.23}/turboapi/request_handler.py +22 -27
- {turboapi-0.3.20 → turboapi-0.3.23}/.github/scripts/check_performance_regression.py +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/.github/scripts/compare_benchmarks.py +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/.github/workflows/README.md +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/.github/workflows/benchmark.yml +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/.github/workflows/build-and-release.yml +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/.github/workflows/build-wheels.yml +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/.github/workflows/ci.yml +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/.github/workflows/release.yml +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/.gitignore +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/AGENTS.md +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/CHANGELOG.md +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/FASTAPI_COMPATIBILITY.md +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/FASTAPI_FIXES_SUMMARY.md +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/LICENSE +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/PYTHON_313_FREE_THREADING_SETUP.md +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/PYTHON_SETUP_COMPLETE.md +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/README.md +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/RELEASE_NOTES_v0.3.1.md +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/RELEASE_NOTES_v0.3.13.md +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/WINDOWS_FIX_SUMMARY.md +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/adaptive_rate_test.py +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/benches/performance_bench.rs +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/benchmark_comparison.png +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/benchmark_graphs/turbo_vs_fastapi_performance_20250929_025531.png +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/claude.md +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/delete/blog/adr_python_handler_integration.md +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/delete/blog/phase_1.md +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/delete/blog/phase_2.md +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/delete/blog/phase_3.md +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/delete/blog/phase_4.md +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/delete/blog/phase_5.md +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/delete/twitterpost.md +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/install_benchmark_deps.py +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/mini-notes/001-foundation.md +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/mini-notes/002-routing-breakthrough.md +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/mini-notes/003-production-ready.md +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/mini-notes/004-zero-copy-revolution.md +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/mini-notes/005-middleware-mastery.md +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/mini-notes/006-python-handler-breakthrough.md +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/mini-notes/README.md +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/mini-notes/lessons-learned.md +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/python/MANIFEST.in +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/python/setup.py +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/python/turboapi/__init__.py +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/python/turboapi/decorators.py +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/python/turboapi/main_app.py +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/python/turboapi/middleware.py +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/python/turboapi/models.py +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/python/turboapi/routing.py +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/python/turboapi/rust_integration.py +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/python/turboapi/server_integration.py +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/python/turboapi/version_check.py +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/setup_python313t.sh +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/src/http2.rs +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/src/lib.rs +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/src/micro_bench.rs +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/src/middleware.rs +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/src/request.rs +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/src/response.rs +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/src/router.rs +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/src/threadpool.rs +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/src/validation.rs +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/src/websocket.rs +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/src/zerocopy.rs +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/test_no_rate_limit.py +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/test_rate_limiting.py +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/test_zerocopy.py +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/tests/README.md +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/tests/benchmark_comparison.py +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/tests/comparison_before_after.py +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/tests/fastapi_equivalent.py +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/tests/quick_body_test.py +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/tests/quick_test.py +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/tests/test.py +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/tests/test_fastapi_compatibility.py +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/tests/test_v0_3_20_fixes.py +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/tests/wrk_benchmark.py +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/tests/wrk_comparison.py +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/turbo_vs_fastapi_benchmark_20250929_025526.json +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/turboapi/__init__.py +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/turboapi/decorators.py +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/turboapi/main_app.py +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/turboapi/middleware.py +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/turboapi/models.py +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/turboapi/routing.py +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/turboapi/rust_integration.py +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/turboapi/server_integration.py +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/turboapi/version_check.py +0 -0
- {turboapi-0.3.20 → turboapi-0.3.23}/wrk_rate_limit_test.py +0 -0
|
@@ -912,6 +912,19 @@ dependencies = [
|
|
|
912
912
|
"unindent",
|
|
913
913
|
]
|
|
914
914
|
|
|
915
|
+
[[package]]
|
|
916
|
+
name = "pyo3-async-runtimes"
|
|
917
|
+
version = "0.26.0"
|
|
918
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
919
|
+
checksum = "e6ee6d4cb3e8d5b925f5cdb38da183e0ff18122eb2048d4041c9e7034d026e23"
|
|
920
|
+
dependencies = [
|
|
921
|
+
"futures",
|
|
922
|
+
"once_cell",
|
|
923
|
+
"pin-project-lite",
|
|
924
|
+
"pyo3",
|
|
925
|
+
"tokio",
|
|
926
|
+
]
|
|
927
|
+
|
|
915
928
|
[[package]]
|
|
916
929
|
name = "pyo3-build-config"
|
|
917
930
|
version = "0.26.0"
|
|
@@ -1426,7 +1439,7 @@ dependencies = [
|
|
|
1426
1439
|
|
|
1427
1440
|
[[package]]
|
|
1428
1441
|
name = "turbonet"
|
|
1429
|
-
version = "0.3.
|
|
1442
|
+
version = "0.3.23"
|
|
1430
1443
|
dependencies = [
|
|
1431
1444
|
"anyhow",
|
|
1432
1445
|
"bytes",
|
|
@@ -1440,6 +1453,7 @@ dependencies = [
|
|
|
1440
1453
|
"num_cpus",
|
|
1441
1454
|
"pin-project-lite",
|
|
1442
1455
|
"pyo3",
|
|
1456
|
+
"pyo3-async-runtimes",
|
|
1443
1457
|
"rayon",
|
|
1444
1458
|
"serde",
|
|
1445
1459
|
"serde_json",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "turbonet"
|
|
3
|
-
version = "0.3.
|
|
3
|
+
version = "0.3.23"
|
|
4
4
|
edition = "2021"
|
|
5
5
|
authors = ["Rach Pradhan <rach@turboapi.dev>"]
|
|
6
6
|
description = "High-performance Python web framework core - Rust-powered HTTP server with Python 3.13 free-threading support"
|
|
@@ -22,6 +22,7 @@ python = ["pyo3"]
|
|
|
22
22
|
|
|
23
23
|
[dependencies]
|
|
24
24
|
pyo3 = { version = "0.26.0", features = ["extension-module"], optional = true }
|
|
25
|
+
pyo3-async-runtimes = { version = "0.26", features = ["tokio-runtime"] }
|
|
25
26
|
tokio = { version = "1.0", features = ["full"] }
|
|
26
27
|
hyper = { version = "1.7.0", features = ["full", "http2"] }
|
|
27
28
|
hyper-util = { version = "0.1.10", features = ["full", "http2"] }
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
================================================================================
|
|
2
|
+
🏁 TurboAPI v0.3.20 vs FastAPI Comprehensive Benchmark
|
|
3
|
+
================================================================================
|
|
4
|
+
|
|
5
|
+
✅ Found wrk at: /opt/homebrew/bin/wrk
|
|
6
|
+
|
|
7
|
+
================================================================================
|
|
8
|
+
Testing TurboAPI v0.3.20
|
|
9
|
+
================================================================================
|
|
10
|
+
🚀 Starting TurboAPI on port 8080...
|
|
11
|
+
✅ TurboAPI ready
|
|
12
|
+
|
|
13
|
+
📊 Light Load (t=4, c=50)
|
|
14
|
+
Testing Simple GET... ✅ 70,714 req/s
|
|
15
|
+
Testing Parameterized Route (NEW)... ✅ 68,161 req/s
|
|
16
|
+
Testing Async Handler (NEW)... ✅ 8,641 req/s
|
|
17
|
+
Testing Async + Params (NEW)... ✅ 3 req/s
|
|
18
|
+
|
|
19
|
+
📊 Medium Load (t=8, c=200)
|
|
20
|
+
Testing Simple GET... ✅ 0 req/s
|
|
21
|
+
Testing Parameterized Route (NEW)... ✅ 0 req/s
|
|
22
|
+
Testing Async Handler (NEW)... ✅ 0 req/s
|
|
23
|
+
Testing Async + Params (NEW)... ✅ 0 req/s
|
|
24
|
+
|
|
25
|
+
📊 Heavy Load (t=12, c=500)
|
|
26
|
+
Testing Simple GET... ✅ 0 req/s
|
|
27
|
+
Testing Parameterized Route (NEW)... ✅ 0 req/s
|
|
28
|
+
Testing Async Handler (NEW)... ✅ 0 req/s
|
|
29
|
+
Testing Async + Params (NEW)... ✅ 0 req/s
|
|
30
|
+
|
|
31
|
+
================================================================================
|
|
32
|
+
Testing FastAPI
|
|
33
|
+
================================================================================
|
|
34
|
+
🚀 Starting FastAPI on port 8081...
|
|
35
|
+
✅ FastAPI ready
|
|
36
|
+
|
|
37
|
+
📊 Light Load (t=4, c=50)
|
|
38
|
+
Testing Simple GET... ✅ 8,113 req/s
|
|
39
|
+
Testing Parameterized Route (NEW)... ✅ 7,401 req/s
|
|
40
|
+
Testing Async Handler (NEW)... ✅ 10,062 req/s
|
|
41
|
+
Testing Async + Params (NEW)... ✅ 8,940 req/s
|
|
42
|
+
|
|
43
|
+
📊 Medium Load (t=8, c=200)
|
|
44
|
+
Testing Simple GET... ✅ 7,773 req/s
|
|
45
|
+
Testing Parameterized Route (NEW)... ✅ 7,022 req/s
|
|
46
|
+
Testing Async Handler (NEW)... ✅ 9,955 req/s
|
|
47
|
+
Testing Async + Params (NEW)... ✅ 9,016 req/s
|
|
48
|
+
|
|
49
|
+
📊 Heavy Load (t=12, c=500)
|
|
50
|
+
Testing Simple GET... ✅ 7,720 req/s
|
|
51
|
+
Testing Parameterized Route (NEW)... ✅ 6,834 req/s
|
|
52
|
+
Testing Async Handler (NEW)... ✅ 9,696 req/s
|
|
53
|
+
Testing Async + Params (NEW)... ✅ 9,067 req/s
|
|
54
|
+
|
|
55
|
+
================================================================================
|
|
56
|
+
📊 BENCHMARK RESULTS SUMMARY
|
|
57
|
+
================================================================================
|
|
58
|
+
|
|
59
|
+
Light Load:
|
|
60
|
+
--------------------------------------------------------------------------------
|
|
61
|
+
Endpoint TurboAPI FastAPI Speedup
|
|
62
|
+
--------------------------------------------------------------------------------
|
|
63
|
+
Simple GET 70,714/s 8,113/s 8.72x
|
|
64
|
+
Parameterized Route (NEW) 68,161/s 7,401/s 9.21x
|
|
65
|
+
Async Handler (NEW) 8,641/s 10,062/s 0.86x
|
|
66
|
+
Async + Params (NEW) 3/s 8,940/s 0.00x
|
|
67
|
+
|
|
68
|
+
Medium Load:
|
|
69
|
+
--------------------------------------------------------------------------------
|
|
70
|
+
Endpoint TurboAPI FastAPI Speedup
|
|
71
|
+
--------------------------------------------------------------------------------
|
|
72
|
+
Simple GET 0/s 7,773/s 0.00x
|
|
73
|
+
Parameterized Route (NEW) 0/s 7,022/s 0.00x
|
|
74
|
+
Async Handler (NEW) 0/s 9,955/s 0.00x
|
|
75
|
+
Async + Params (NEW) 0/s 9,016/s 0.00x
|
|
76
|
+
|
|
77
|
+
Heavy Load:
|
|
78
|
+
--------------------------------------------------------------------------------
|
|
79
|
+
Endpoint TurboAPI FastAPI Speedup
|
|
80
|
+
--------------------------------------------------------------------------------
|
|
81
|
+
Simple GET 0/s 7,720/s 0.00x
|
|
82
|
+
Parameterized Route (NEW) 0/s 6,834/s 0.00x
|
|
83
|
+
Async Handler (NEW) 0/s 9,696/s 0.00x
|
|
84
|
+
Async + Params (NEW) 0/s 9,067/s 0.00x
|
|
85
|
+
|
|
86
|
+
================================================================================
|
|
87
|
+
🎯 KEY FINDINGS
|
|
88
|
+
================================================================================
|
|
89
|
+
|
|
90
|
+
✅ Average Speedup: 1.57x faster than FastAPI
|
|
91
|
+
✅ Parameterized routes: WORKING (v0.3.20 fix)
|
|
92
|
+
✅ Async handlers: WORKING (v0.3.20 fix)
|
|
93
|
+
✅ TurboAPI v0.3.20 is PRODUCTION READY!
|
|
94
|
+
|
|
95
|
+
📁 Full results saved to: benchmark_results_v0_3_20.json
|
|
@@ -4,7 +4,7 @@ build-backend = "maturin"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "turboapi"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.23"
|
|
8
8
|
description = "Revolutionary Python web framework with FastAPI syntax and 5-10x performance (Python 3.13+ free-threading required)"
|
|
9
9
|
requires-python = ">=3.13"
|
|
10
10
|
license = {text = "MIT"}
|
|
@@ -4,7 +4,7 @@ build-backend = "maturin"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "turboapi"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.23"
|
|
8
8
|
description = "Revolutionary Python web framework with FastAPI syntax and 5-10x performance (Python 3.13+ free-threading required)"
|
|
9
9
|
requires-python = ">=3.13"
|
|
10
10
|
license = {text = "MIT"}
|
|
@@ -191,43 +191,38 @@ def create_enhanced_handler(original_handler, route_definition):
|
|
|
191
191
|
# Filter kwargs to only pass expected parameters
|
|
192
192
|
filtered_kwargs = {
|
|
193
193
|
k: v for k, v in kwargs.items()
|
|
194
|
-
if k in sig.parameters
|
|
195
194
|
}
|
|
196
195
|
|
|
197
196
|
# Call original handler
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
loop = asyncio.get_event_loop()
|
|
203
|
-
except RuntimeError:
|
|
204
|
-
loop = asyncio.new_event_loop()
|
|
205
|
-
asyncio.set_event_loop(loop)
|
|
206
|
-
result = loop.run_until_complete(original_handler(**filtered_kwargs))
|
|
207
|
-
else:
|
|
208
|
-
result = original_handler(**filtered_kwargs)
|
|
197
|
+
# v0.3.21: Async handlers are now supported via Rust's tokio runtime!
|
|
198
|
+
# The Rust layer (server.rs) will detect coroutines and await them properly
|
|
199
|
+
# using pyo3-async-runtimes, giving us native async performance
|
|
200
|
+
result = original_handler(**filtered_kwargs)
|
|
209
201
|
|
|
210
|
-
#
|
|
211
|
-
|
|
202
|
+
# Check if result is a coroutine - if so, return it directly for Rust to await
|
|
203
|
+
import inspect
|
|
204
|
+
if inspect.iscoroutine(result):
|
|
205
|
+
# Return coroutine directly - Rust will await it using tokio
|
|
206
|
+
return result
|
|
212
207
|
|
|
213
|
-
return
|
|
208
|
+
# Sync result - normalize and return as JSON string
|
|
209
|
+
content, status_code = ResponseHandler.normalize_response(result)
|
|
214
210
|
|
|
211
|
+
# Return JSON string directly for Rust to use
|
|
212
|
+
import json
|
|
213
|
+
return json.dumps(content)
|
|
215
214
|
except ValueError as e:
|
|
216
215
|
# Validation or parsing error (400 Bad Request)
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
400
|
|
220
|
-
)
|
|
216
|
+
import json
|
|
217
|
+
return json.dumps({"error": "Bad Request", "detail": str(e)})
|
|
221
218
|
except Exception as e:
|
|
222
219
|
# Unexpected error (500 Internal Server Error)
|
|
223
220
|
import traceback
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
500
|
|
231
|
-
)
|
|
221
|
+
import json
|
|
222
|
+
return json.dumps({
|
|
223
|
+
"error": "Internal Server Error",
|
|
224
|
+
"detail": str(e),
|
|
225
|
+
"traceback": traceback.format_exc()
|
|
226
|
+
})
|
|
232
227
|
|
|
233
228
|
return enhanced_handler
|
|
@@ -414,6 +414,8 @@ pub fn configure_rate_limiting(enabled: bool, requests_per_minute: Option<u32>)
|
|
|
414
414
|
}
|
|
415
415
|
|
|
416
416
|
/// PHASE 2: Fast Python handler call with cached modules and optimized object creation
|
|
417
|
+
/// Now supports async Python handlers via pyo3-async-runtimes + tokio
|
|
418
|
+
/// FREE-THREADING: Uses Python::attach() for true parallelism on Python 3.13+
|
|
417
419
|
fn call_python_handler_fast(
|
|
418
420
|
handler: Handler,
|
|
419
421
|
method_str: &str,
|
|
@@ -421,7 +423,9 @@ fn call_python_handler_fast(
|
|
|
421
423
|
query_string: &str,
|
|
422
424
|
body: &Bytes
|
|
423
425
|
) -> Result<String, pyo3::PyErr> {
|
|
424
|
-
Python::with_gil(
|
|
426
|
+
// Use Python::attach() instead of with_gil() for free-threading parallelism!
|
|
427
|
+
// This allows multiple Python handlers to run in parallel on Python 3.13+
|
|
428
|
+
Python::attach(|py| {
|
|
425
429
|
// Get cached modules (initialized once)
|
|
426
430
|
let types_module = CACHED_TYPES_MODULE.get_or_init(|| {
|
|
427
431
|
py.import("types").unwrap().into()
|
|
@@ -455,11 +459,45 @@ fn call_python_handler_fast(
|
|
|
455
459
|
// Call handler directly
|
|
456
460
|
let result = handler.call1(py, (request_obj,))?;
|
|
457
461
|
|
|
458
|
-
//
|
|
459
|
-
|
|
460
|
-
let
|
|
461
|
-
|
|
462
|
-
|
|
462
|
+
// Check if result is a coroutine (async function)
|
|
463
|
+
let inspect_module = py.import("inspect")?;
|
|
464
|
+
let is_coroutine = inspect_module
|
|
465
|
+
.getattr("iscoroutine")?
|
|
466
|
+
.call1((result.clone_ref(py),))?
|
|
467
|
+
.extract::<bool>()?;
|
|
468
|
+
|
|
469
|
+
// Handle sync vs async results differently
|
|
470
|
+
if is_coroutine {
|
|
471
|
+
// Async handler - use pyo3-async-runtimes to convert Python coroutine to Rust future
|
|
472
|
+
// This integrates with tokio's runtime for true async performance!
|
|
473
|
+
|
|
474
|
+
// Convert Python coroutine to Rust future using pyo3-async-runtimes
|
|
475
|
+
let rust_future = pyo3_async_runtimes::tokio::into_future(result.clone_ref(py).into_bound(py))?;
|
|
476
|
+
|
|
477
|
+
// Await the Rust future in tokio's runtime (blocking this thread)
|
|
478
|
+
let awaited_result = py.allow_threads(|| {
|
|
479
|
+
tokio::task::block_in_place(|| {
|
|
480
|
+
tokio::runtime::Handle::current().block_on(rust_future)
|
|
481
|
+
})
|
|
482
|
+
})?;
|
|
483
|
+
|
|
484
|
+
// Serialize the awaited result
|
|
485
|
+
let json_dumps = json_module.getattr(py, "dumps")?;
|
|
486
|
+
let json_str = json_dumps.call1(py, (awaited_result,))?;
|
|
487
|
+
json_str.extract(py)
|
|
488
|
+
} else {
|
|
489
|
+
// Sync handler - result might be a dict or already a string
|
|
490
|
+
// Try to extract as string first (if it's already JSON)
|
|
491
|
+
match result.extract::<String>(py) {
|
|
492
|
+
Ok(json_str) => Ok(json_str),
|
|
493
|
+
Err(_) => {
|
|
494
|
+
// Not a string, serialize it
|
|
495
|
+
let json_dumps = json_module.getattr(py, "dumps")?;
|
|
496
|
+
let json_str = json_dumps.call1(py, (result,))?;
|
|
497
|
+
json_str.extract(py)
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|
|
463
501
|
})
|
|
464
502
|
}
|
|
465
503
|
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Async benchmark: TurboAPI v0.3.21 SYNC vs ASYNC vs FastAPI
|
|
3
|
+
|
|
4
|
+
echo "=========================================="
|
|
5
|
+
echo "🏁 TurboAPI v0.3.21: SYNC vs ASYNC vs FastAPI"
|
|
6
|
+
echo "=========================================="
|
|
7
|
+
echo ""
|
|
8
|
+
|
|
9
|
+
# Check for wrk
|
|
10
|
+
if ! command -v wrk &> /dev/null; then
|
|
11
|
+
echo "❌ wrk not found. Install with: brew install wrk"
|
|
12
|
+
exit 1
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
# Test TurboAPI SYNC
|
|
16
|
+
echo "🚀 Test 1: TurboAPI SYNC (baseline)"
|
|
17
|
+
echo "=========================================="
|
|
18
|
+
python tests/test_v0_3_20_server.py > /tmp/turbo_sync.log 2>&1 &
|
|
19
|
+
TURBO_SYNC_PID=$!
|
|
20
|
+
sleep 4
|
|
21
|
+
|
|
22
|
+
echo "Simple GET:"
|
|
23
|
+
wrk -t4 -c100 -d10s http://127.0.0.1:8080/ 2>&1 | grep "Requests/sec"
|
|
24
|
+
echo "Parameterized:"
|
|
25
|
+
wrk -t4 -c100 -d10s http://127.0.0.1:8080/users/123 2>&1 | grep "Requests/sec"
|
|
26
|
+
|
|
27
|
+
kill $TURBO_SYNC_PID 2>/dev/null
|
|
28
|
+
sleep 2
|
|
29
|
+
|
|
30
|
+
# Test TurboAPI ASYNC
|
|
31
|
+
echo ""
|
|
32
|
+
echo "🚀 Test 2: TurboAPI ASYNC (asyncio.run)"
|
|
33
|
+
echo "=========================================="
|
|
34
|
+
python tests/test_async_benchmark.py > /tmp/turbo_async.log 2>&1 &
|
|
35
|
+
TURBO_ASYNC_PID=$!
|
|
36
|
+
sleep 4
|
|
37
|
+
|
|
38
|
+
echo "Simple GET:"
|
|
39
|
+
wrk -t4 -c100 -d10s http://127.0.0.1:8082/ 2>&1 | grep "Requests/sec"
|
|
40
|
+
echo "Parameterized:"
|
|
41
|
+
wrk -t4 -c100 -d10s http://127.0.0.1:8082/users/123 2>&1 | grep "Requests/sec"
|
|
42
|
+
|
|
43
|
+
kill $TURBO_ASYNC_PID 2>/dev/null
|
|
44
|
+
sleep 2
|
|
45
|
+
|
|
46
|
+
# Test FastAPI
|
|
47
|
+
echo ""
|
|
48
|
+
echo "🚀 Test 3: FastAPI (async with uvicorn)"
|
|
49
|
+
echo "=========================================="
|
|
50
|
+
python tests/fastapi_v0_3_20_equivalent.py > /tmp/fastapi.log 2>&1 &
|
|
51
|
+
FASTAPI_PID=$!
|
|
52
|
+
sleep 4
|
|
53
|
+
|
|
54
|
+
echo "Simple GET:"
|
|
55
|
+
wrk -t4 -c100 -d10s http://127.0.0.1:8081/ 2>&1 | grep "Requests/sec"
|
|
56
|
+
echo "Parameterized:"
|
|
57
|
+
wrk -t4 -c100 -d10s http://127.0.0.1:8081/users/123 2>&1 | grep "Requests/sec"
|
|
58
|
+
|
|
59
|
+
kill $FASTAPI_PID 2>/dev/null
|
|
60
|
+
|
|
61
|
+
echo ""
|
|
62
|
+
echo "=========================================="
|
|
63
|
+
echo "✅ Benchmark Complete!"
|
|
64
|
+
echo "=========================================="
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""
|
|
2
|
+
FastAPI equivalent server for comparison with TurboAPI v0.3.20
|
|
3
|
+
"""
|
|
4
|
+
import asyncio
|
|
5
|
+
from fastapi import FastAPI
|
|
6
|
+
import uvicorn
|
|
7
|
+
|
|
8
|
+
app = FastAPI(title="FastAPI Benchmark", version="0.109.0")
|
|
9
|
+
|
|
10
|
+
# Simple GET (baseline)
|
|
11
|
+
@app.get("/")
|
|
12
|
+
def root():
|
|
13
|
+
return {"message": "Hello, World!", "version": "0.109.0"}
|
|
14
|
+
|
|
15
|
+
# Parameterized route
|
|
16
|
+
@app.get("/users/{user_id}")
|
|
17
|
+
def get_user(user_id: int):
|
|
18
|
+
return {"user_id": user_id, "name": f"User {user_id}", "status": "active"}
|
|
19
|
+
|
|
20
|
+
# Async handler
|
|
21
|
+
@app.get("/async")
|
|
22
|
+
async def async_endpoint():
|
|
23
|
+
await asyncio.sleep(0.0001) # Minimal async work
|
|
24
|
+
return {"message": "Async works!", "type": "async"}
|
|
25
|
+
|
|
26
|
+
# Async with parameters
|
|
27
|
+
@app.get("/async/users/{user_id}")
|
|
28
|
+
async def async_user(user_id: int):
|
|
29
|
+
await asyncio.sleep(0.0001)
|
|
30
|
+
return {"user_id": user_id, "async": True}
|
|
31
|
+
|
|
32
|
+
# POST with body parsing
|
|
33
|
+
@app.post("/echo")
|
|
34
|
+
def echo(message: str = ""):
|
|
35
|
+
return {"echo": message, "length": len(message)}
|
|
36
|
+
|
|
37
|
+
if __name__ == "__main__":
|
|
38
|
+
print("=" * 70)
|
|
39
|
+
print("FastAPI Benchmark Server")
|
|
40
|
+
print("=" * 70)
|
|
41
|
+
uvicorn.run(app, host="127.0.0.1", port=8081, log_level="error")
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Quick benchmark: TurboAPI v0.3.21 vs FastAPI
|
|
3
|
+
|
|
4
|
+
echo "=========================================="
|
|
5
|
+
echo "🏁 TurboAPI v0.3.21 vs FastAPI Benchmark"
|
|
6
|
+
echo "=========================================="
|
|
7
|
+
echo ""
|
|
8
|
+
|
|
9
|
+
# Check for wrk
|
|
10
|
+
if ! command -v wrk &> /dev/null; then
|
|
11
|
+
echo "❌ wrk not found. Install with: brew install wrk"
|
|
12
|
+
exit 1
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
# Test TurboAPI
|
|
16
|
+
echo "🚀 Starting TurboAPI v0.3.21..."
|
|
17
|
+
python tests/test_v0_3_20_server.py > /tmp/turbo.log 2>&1 &
|
|
18
|
+
TURBO_PID=$!
|
|
19
|
+
sleep 4
|
|
20
|
+
|
|
21
|
+
echo "📊 Benchmarking TurboAPI (10s, 4 threads, 100 connections)..."
|
|
22
|
+
echo ""
|
|
23
|
+
echo "Test 1: Simple GET /"
|
|
24
|
+
wrk -t4 -c100 -d10s --latency http://127.0.0.1:8080/ 2>&1 | grep -E "Requests/sec|Latency|Thread"
|
|
25
|
+
|
|
26
|
+
echo ""
|
|
27
|
+
echo "Test 2: Parameterized route /users/123"
|
|
28
|
+
wrk -t4 -c100 -d10s --latency http://127.0.0.1:8080/users/123 2>&1 | grep -E "Requests/sec|Latency|Thread"
|
|
29
|
+
|
|
30
|
+
echo ""
|
|
31
|
+
echo "Test 3: Nested params /api/v1/users/1/posts/2"
|
|
32
|
+
wrk -t4 -c100 -d10s --latency http://127.0.0.1:8080/api/v1/users/1/posts/2 2>&1 | grep -E "Requests/sec|Latency|Thread"
|
|
33
|
+
|
|
34
|
+
kill $TURBO_PID 2>/dev/null
|
|
35
|
+
sleep 2
|
|
36
|
+
|
|
37
|
+
# Test FastAPI
|
|
38
|
+
echo ""
|
|
39
|
+
echo "=========================================="
|
|
40
|
+
echo "🚀 Starting FastAPI..."
|
|
41
|
+
python tests/fastapi_v0_3_20_equivalent.py > /tmp/fastapi.log 2>&1 &
|
|
42
|
+
FASTAPI_PID=$!
|
|
43
|
+
sleep 4
|
|
44
|
+
|
|
45
|
+
echo "📊 Benchmarking FastAPI (10s, 4 threads, 100 connections)..."
|
|
46
|
+
echo ""
|
|
47
|
+
echo "Test 1: Simple GET /"
|
|
48
|
+
wrk -t4 -c100 -d10s --latency http://127.0.0.1:8081/ 2>&1 | grep -E "Requests/sec|Latency|Thread"
|
|
49
|
+
|
|
50
|
+
echo ""
|
|
51
|
+
echo "Test 2: Parameterized route /users/123"
|
|
52
|
+
wrk -t4 -c100 -d10s --latency http://127.0.0.1:8081/users/123 2>&1 | grep -E "Requests/sec|Latency|Thread"
|
|
53
|
+
|
|
54
|
+
echo ""
|
|
55
|
+
echo "Test 3: Nested params /api/v1/users/1/posts/2"
|
|
56
|
+
wrk -t4 -c100 -d10s --latency http://127.0.0.1:8081/api/v1/users/1/posts/2 2>&1 | grep -E "Requests/sec|Latency|Thread"
|
|
57
|
+
|
|
58
|
+
kill $FASTAPI_PID 2>/dev/null
|
|
59
|
+
|
|
60
|
+
echo ""
|
|
61
|
+
echo "=========================================="
|
|
62
|
+
echo "✅ Benchmark Complete!"
|
|
63
|
+
echo "=========================================="
|