django-bolt 0.2.0__cp310-abi3-win_amd64.whl → 0.2.3__cp310-abi3-win_amd64.whl
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 django-bolt might be problematic. Click here for more details.
- django_bolt/_core.pyd +0 -0
- django_bolt/testing/__init__.py +1 -2
- django_bolt/testing/client.py +35 -80
- django_bolt/testing/helpers.py +1 -44
- {django_bolt-0.2.0.dist-info → django_bolt-0.2.3.dist-info}/METADATA +34 -19
- {django_bolt-0.2.0.dist-info → django_bolt-0.2.3.dist-info}/RECORD +8 -8
- {django_bolt-0.2.0.dist-info → django_bolt-0.2.3.dist-info}/WHEEL +0 -0
- {django_bolt-0.2.0.dist-info → django_bolt-0.2.3.dist-info}/entry_points.txt +0 -0
django_bolt/_core.pyd
CHANGED
|
Binary file
|
django_bolt/testing/__init__.py
CHANGED
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Provides test clients for in-memory testing without subprocess/network overhead.
|
|
4
4
|
"""
|
|
5
|
-
from django_bolt.testing.client import TestClient
|
|
5
|
+
from django_bolt.testing.client import TestClient
|
|
6
6
|
|
|
7
7
|
__all__ = [
|
|
8
8
|
"TestClient",
|
|
9
|
-
"AsyncTestClient",
|
|
10
9
|
]
|
django_bolt/testing/client.py
CHANGED
|
@@ -129,6 +129,31 @@ class TestClient(httpx.Client):
|
|
|
129
129
|
|
|
130
130
|
__test__ = False # Tell pytest this is not a test class
|
|
131
131
|
|
|
132
|
+
@staticmethod
|
|
133
|
+
def _read_cors_settings_from_django() -> list[str] | None:
|
|
134
|
+
"""Read CORS_ALLOWED_ORIGINS from Django settings (same as production server).
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
List of allowed origins from Django settings, or None if not configured
|
|
138
|
+
"""
|
|
139
|
+
try:
|
|
140
|
+
from django.conf import settings
|
|
141
|
+
|
|
142
|
+
# Check if CORS_ALLOWED_ORIGINS is defined
|
|
143
|
+
if hasattr(settings, 'CORS_ALLOWED_ORIGINS'):
|
|
144
|
+
origins = settings.CORS_ALLOWED_ORIGINS
|
|
145
|
+
if isinstance(origins, (list, tuple)):
|
|
146
|
+
return list(origins)
|
|
147
|
+
|
|
148
|
+
# Check for CORS_ALLOW_ALL_ORIGINS (wildcard)
|
|
149
|
+
if hasattr(settings, 'CORS_ALLOW_ALL_ORIGINS') and settings.CORS_ALLOW_ALL_ORIGINS:
|
|
150
|
+
return ["*"]
|
|
151
|
+
|
|
152
|
+
return None
|
|
153
|
+
except (ImportError, AttributeError):
|
|
154
|
+
# Django not configured or settings not available
|
|
155
|
+
return None
|
|
156
|
+
|
|
132
157
|
def __init__(
|
|
133
158
|
self,
|
|
134
159
|
api: BoltAPI,
|
|
@@ -136,6 +161,7 @@ class TestClient(httpx.Client):
|
|
|
136
161
|
raise_server_exceptions: bool = True,
|
|
137
162
|
use_http_layer: bool = False,
|
|
138
163
|
cors_allowed_origins: list[str] | None = None,
|
|
164
|
+
read_django_settings: bool = True,
|
|
139
165
|
**kwargs: Any,
|
|
140
166
|
):
|
|
141
167
|
"""Initialize test client.
|
|
@@ -146,11 +172,19 @@ class TestClient(httpx.Client):
|
|
|
146
172
|
raise_server_exceptions: If True, raise exceptions from handlers
|
|
147
173
|
use_http_layer: If True, route through Actix HTTP layer (enables testing
|
|
148
174
|
CORS, rate limiting, compression). Default False for fast tests.
|
|
149
|
-
cors_allowed_origins: Global CORS allowed origins for testing
|
|
175
|
+
cors_allowed_origins: Global CORS allowed origins for testing.
|
|
176
|
+
If None and read_django_settings=True, reads from Django settings.
|
|
177
|
+
read_django_settings: If True, read CORS_ALLOWED_ORIGINS from Django settings
|
|
178
|
+
when cors_allowed_origins is None. Default True.
|
|
150
179
|
**kwargs: Additional arguments passed to httpx.Client
|
|
151
180
|
"""
|
|
152
181
|
from django_bolt import _core
|
|
153
182
|
|
|
183
|
+
# If cors_allowed_origins not provided and read_django_settings=True,
|
|
184
|
+
# read from Django settings (same as production server does)
|
|
185
|
+
if cors_allowed_origins is None and read_django_settings:
|
|
186
|
+
cors_allowed_origins = self._read_cors_settings_from_django()
|
|
187
|
+
|
|
154
188
|
# Create test app instance
|
|
155
189
|
self.app_id = _core.create_test_app(api._dispatch, False, cors_allowed_origins)
|
|
156
190
|
|
|
@@ -193,82 +227,3 @@ class TestClient(httpx.Client):
|
|
|
193
227
|
except:
|
|
194
228
|
pass
|
|
195
229
|
return super().__exit__(*args)
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
class AsyncTestClient(httpx.AsyncClient):
|
|
199
|
-
"""Asynchronous test client for django-bolt using per-instance test state."""
|
|
200
|
-
|
|
201
|
-
__test__ = False # Tell pytest this is not a test class
|
|
202
|
-
|
|
203
|
-
def __init__(
|
|
204
|
-
self,
|
|
205
|
-
api: BoltAPI,
|
|
206
|
-
base_url: str = "http://testserver.local",
|
|
207
|
-
raise_server_exceptions: bool = True,
|
|
208
|
-
use_http_layer: bool = False,
|
|
209
|
-
cors_allowed_origins: list[str] | None = None,
|
|
210
|
-
**kwargs: Any,
|
|
211
|
-
):
|
|
212
|
-
"""Initialize async test client.
|
|
213
|
-
|
|
214
|
-
Args:
|
|
215
|
-
api: BoltAPI instance to test
|
|
216
|
-
base_url: Base URL for requests
|
|
217
|
-
raise_server_exceptions: If True, raise exceptions from handlers
|
|
218
|
-
use_http_layer: If True, route through Actix HTTP layer (enables testing
|
|
219
|
-
CORS, rate limiting, compression). Default False for fast tests.
|
|
220
|
-
cors_allowed_origins: Global CORS allowed origins for testing
|
|
221
|
-
**kwargs: Additional arguments passed to httpx.AsyncClient
|
|
222
|
-
"""
|
|
223
|
-
from django_bolt import _core
|
|
224
|
-
|
|
225
|
-
# Create test app instance
|
|
226
|
-
self.app_id = _core.create_test_app(api._dispatch, False, cors_allowed_origins)
|
|
227
|
-
|
|
228
|
-
# Register routes
|
|
229
|
-
rust_routes = [
|
|
230
|
-
(method, path, handler_id, handler)
|
|
231
|
-
for method, path, handler_id, handler in api._routes
|
|
232
|
-
]
|
|
233
|
-
_core.register_test_routes(self.app_id, rust_routes)
|
|
234
|
-
|
|
235
|
-
# Register middleware metadata if any exists
|
|
236
|
-
if api._handler_middleware:
|
|
237
|
-
middleware_data = [
|
|
238
|
-
(handler_id, meta)
|
|
239
|
-
for handler_id, meta in api._handler_middleware.items()
|
|
240
|
-
]
|
|
241
|
-
_core.register_test_middleware_metadata(self.app_id, middleware_data)
|
|
242
|
-
|
|
243
|
-
# Ensure runtime is ready
|
|
244
|
-
_core.ensure_test_runtime(self.app_id)
|
|
245
|
-
|
|
246
|
-
# Create async transport
|
|
247
|
-
class AsyncTransport(httpx.AsyncBaseTransport):
|
|
248
|
-
def __init__(self, app_id: int, raise_exceptions: bool, use_http_layer: bool):
|
|
249
|
-
self._sync_transport = BoltTestTransport(app_id, raise_exceptions, use_http_layer)
|
|
250
|
-
|
|
251
|
-
async def handle_async_request(self, request: httpx.Request) -> httpx.Response:
|
|
252
|
-
return self._sync_transport.handle_request(request)
|
|
253
|
-
|
|
254
|
-
super().__init__(
|
|
255
|
-
base_url=base_url,
|
|
256
|
-
transport=AsyncTransport(self.app_id, raise_server_exceptions, use_http_layer),
|
|
257
|
-
follow_redirects=True,
|
|
258
|
-
**kwargs,
|
|
259
|
-
)
|
|
260
|
-
self.api = api
|
|
261
|
-
|
|
262
|
-
async def __aenter__(self):
|
|
263
|
-
"""Enter async context manager."""
|
|
264
|
-
return await super().__aenter__()
|
|
265
|
-
|
|
266
|
-
async def __aexit__(self, *args):
|
|
267
|
-
"""Exit async context manager and cleanup test app."""
|
|
268
|
-
from django_bolt import _core
|
|
269
|
-
|
|
270
|
-
try:
|
|
271
|
-
_core.destroy_test_app(self.app_id)
|
|
272
|
-
except:
|
|
273
|
-
pass
|
|
274
|
-
return await super().__aexit__(*args)
|
django_bolt/testing/helpers.py
CHANGED
|
@@ -4,7 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
from typing import Any
|
|
5
5
|
|
|
6
6
|
from django_bolt import BoltAPI
|
|
7
|
-
from django_bolt.testing.client import
|
|
7
|
+
from django_bolt.testing.client import TestClient
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
def create_test_client(
|
|
@@ -48,46 +48,3 @@ def create_test_client(
|
|
|
48
48
|
bootstrap_django=bootstrap_django,
|
|
49
49
|
**kwargs,
|
|
50
50
|
)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
def create_async_test_client(
|
|
54
|
-
api: BoltAPI,
|
|
55
|
-
base_url: str = "http://testserver.local",
|
|
56
|
-
raise_server_exceptions: bool = True,
|
|
57
|
-
bootstrap_django: bool = True,
|
|
58
|
-
**kwargs: Any,
|
|
59
|
-
) -> AsyncTestClient:
|
|
60
|
-
"""Create an asynchronous test client.
|
|
61
|
-
|
|
62
|
-
Args:
|
|
63
|
-
api: BoltAPI instance to test
|
|
64
|
-
base_url: Base URL for requests
|
|
65
|
-
raise_server_exceptions: If True, raise handler exceptions instead of 500 responses
|
|
66
|
-
**kwargs: Additional httpx.AsyncClient arguments
|
|
67
|
-
|
|
68
|
-
Returns:
|
|
69
|
-
AsyncTestClient instance
|
|
70
|
-
|
|
71
|
-
Example:
|
|
72
|
-
from django_bolt import BoltAPI
|
|
73
|
-
from django_bolt.testing import create_async_test_client
|
|
74
|
-
|
|
75
|
-
api = BoltAPI()
|
|
76
|
-
|
|
77
|
-
@api.get("/users/{user_id}")
|
|
78
|
-
async def get_user(user_id: int):
|
|
79
|
-
return {"id": user_id, "name": "Test User"}
|
|
80
|
-
|
|
81
|
-
async def test_get_user():
|
|
82
|
-
async with create_async_test_client(api) as client:
|
|
83
|
-
response = await client.get("/users/123")
|
|
84
|
-
assert response.status_code == 200
|
|
85
|
-
assert response.json()["id"] == 123
|
|
86
|
-
"""
|
|
87
|
-
return AsyncTestClient(
|
|
88
|
-
api=api,
|
|
89
|
-
base_url=base_url,
|
|
90
|
-
raise_server_exceptions=raise_server_exceptions,
|
|
91
|
-
bootstrap_django=bootstrap_django,
|
|
92
|
-
**kwargs,
|
|
93
|
-
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: django-bolt
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Classifier: Development Status :: 3 - Alpha
|
|
5
5
|
Classifier: Intended Audience :: Developers
|
|
6
6
|
Classifier: License :: OSI Approved :: MIT License
|
|
@@ -24,6 +24,7 @@ Requires-Dist: django>=4.2
|
|
|
24
24
|
Requires-Dist: click>=8.1
|
|
25
25
|
Requires-Dist: msgspec>=0.18
|
|
26
26
|
Requires-Dist: multipart>=1.3
|
|
27
|
+
Requires-Dist: pyjwt>=2.10.1
|
|
27
28
|
Requires-Dist: pyyaml>=6.0 ; extra == 'yaml'
|
|
28
29
|
Provides-Extra: yaml
|
|
29
30
|
Summary: High-performance API framework for Django with Rust-powered endpoints delivering 60k+ RPS
|
|
@@ -92,6 +93,7 @@ python manage.py runbolt --dev # for development with reload enabled
|
|
|
92
93
|
[django-bolt] Workers: 1, Processes: 1
|
|
93
94
|
[django-bolt] OpenAPI docs enabled at http://0.0.0.0:8000/docs/ #swagger docs builtin
|
|
94
95
|
|
|
96
|
+
python manage.py runbolt --processes 8 workers 1 #for deployment (depends on your cpu cores)
|
|
95
97
|
# processes are python processes that handle request 1 actix worker
|
|
96
98
|
```
|
|
97
99
|
|
|
@@ -166,7 +168,11 @@ python manage.py runbolt --dev # for development with reload enabled
|
|
|
166
168
|
|
|
167
169
|
- ✅ **Global Middleware** - Apply to all routes via `BoltAPI(middleware=[...])`
|
|
168
170
|
- ✅ **Per-Route Middleware** - `@middleware`, `@rate_limit`, `@cors` decorators
|
|
169
|
-
- ✅ **CORS Middleware** - Full CORS support with preflight
|
|
171
|
+
- ✅ **CORS Middleware** - Full CORS support with preflight handling
|
|
172
|
+
- Django settings integration (`CORS_ALLOWED_ORIGINS`, `CORS_ALLOW_ALL_ORIGINS`)
|
|
173
|
+
- Route-level overrides with `@cors()` decorator
|
|
174
|
+
- Origin validation, credentials support, preflight caching
|
|
175
|
+
- Runs entirely in Rust (zero GIL overhead)
|
|
170
176
|
- ✅ **Rate Limiting** - Token bucket algorithm (in Rust, no GIL)
|
|
171
177
|
- ✅ **Compression** - Automatic gzip/brotli/zstd compression (client-negotiated)
|
|
172
178
|
- ✅ **Skip Middleware** - `@skip_middleware("cors", "rate_limit", "compression")`
|
|
@@ -190,13 +196,6 @@ python manage.py runbolt --dev # for development with reload enabled
|
|
|
190
196
|
- Constant-time comparison (timing attack prevention)
|
|
191
197
|
- Fast validation in Rust
|
|
192
198
|
|
|
193
|
-
- ✅ **Session Authentication** - **Complete** (Django session integration)
|
|
194
|
-
|
|
195
|
-
- Django session backend integration
|
|
196
|
-
- Automatic user lookup from session
|
|
197
|
-
- Compatible with Django's session middleware
|
|
198
|
-
- Supports both cookie-based and custom session stores
|
|
199
|
-
|
|
200
199
|
- ✅ **Permission Guards** (all run in Rust):
|
|
201
200
|
|
|
202
201
|
- `AllowAny()` - Public access
|
|
@@ -374,24 +373,45 @@ async def login(username: str, password: str):
|
|
|
374
373
|
|
|
375
374
|
**📖 See [docs/SECURITY.md](docs/SECURITY.md) for complete authentication documentation.**
|
|
376
375
|
|
|
377
|
-
### Middleware
|
|
376
|
+
### Middleware & CORS
|
|
378
377
|
|
|
379
378
|
```python
|
|
380
379
|
from django_bolt import BoltAPI
|
|
381
380
|
from django_bolt.middleware import cors, rate_limit, skip_middleware
|
|
382
381
|
|
|
383
|
-
#
|
|
382
|
+
# Option 1: Use Django settings (recommended for production)
|
|
383
|
+
# In settings.py:
|
|
384
|
+
# CORS_ALLOWED_ORIGINS = ["https://example.com", "https://app.example.com"]
|
|
385
|
+
# CORS_ALLOW_CREDENTIALS = True
|
|
386
|
+
# CORS_MAX_AGE = 3600
|
|
387
|
+
|
|
388
|
+
api = BoltAPI() # Automatically reads Django CORS settings
|
|
389
|
+
|
|
390
|
+
# Option 2: Global middleware config
|
|
384
391
|
api = BoltAPI(
|
|
385
392
|
middleware_config={
|
|
386
393
|
"cors": {
|
|
387
394
|
"origins": ["http://localhost:3000"],
|
|
388
395
|
"methods": ["GET", "POST", "PUT", "DELETE"],
|
|
389
396
|
"credentials": True,
|
|
397
|
+
"max_age": 3600,
|
|
390
398
|
}
|
|
391
399
|
}
|
|
392
400
|
)
|
|
393
401
|
|
|
394
|
-
# Per-route
|
|
402
|
+
# Option 3: Per-route CORS override (overrides global/Django settings)
|
|
403
|
+
@api.get("/public-api")
|
|
404
|
+
@cors(origins=["*"], credentials=False) # Allow all origins
|
|
405
|
+
async def public_endpoint():
|
|
406
|
+
return {"message": "Public endpoint with custom CORS"}
|
|
407
|
+
|
|
408
|
+
# CORS with credentials and specific origins
|
|
409
|
+
@api.post("/auth-endpoint")
|
|
410
|
+
@cors(origins=["https://app.example.com"], credentials=True, max_age=3600)
|
|
411
|
+
async def auth_endpoint():
|
|
412
|
+
return {"message": "Authenticated endpoint with CORS"}
|
|
413
|
+
|
|
414
|
+
# Rate limiting (runs in Rust, no GIL)
|
|
395
415
|
@api.get("/limited")
|
|
396
416
|
@rate_limit(rps=100, burst=200, key="ip") # 100 req/s with burst of 200
|
|
397
417
|
async def limited_endpoint():
|
|
@@ -403,12 +423,6 @@ async def limited_endpoint():
|
|
|
403
423
|
async def user_limited():
|
|
404
424
|
return {"message": "Per-user rate limiting"}
|
|
405
425
|
|
|
406
|
-
# Custom CORS for specific route
|
|
407
|
-
@api.get("/public")
|
|
408
|
-
@cors(origins=["https://example.com"], credentials=True, max_age=3600)
|
|
409
|
-
async def public_endpoint():
|
|
410
|
-
return {"message": "Public endpoint with CORS"}
|
|
411
|
-
|
|
412
426
|
# Skip global middleware
|
|
413
427
|
@api.get("/no-cors")
|
|
414
428
|
@skip_middleware("cors", "rate_limit")
|
|
@@ -606,4 +620,5 @@ Django-Bolt is open source and available under the MIT License.
|
|
|
606
620
|
|
|
607
621
|
---
|
|
608
622
|
|
|
609
|
-
For questions, issues, or feature requests, please visit our [GitHub repository](https://github.com/FarhanAliRaza/django-bolt).
|
|
623
|
+
For questions, issues, or feature requests, please visit our [GitHub repository](https://github.com/FarhanAliRaza/django-bolt).
|
|
624
|
+
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
django_bolt-0.2.
|
|
2
|
-
django_bolt-0.2.
|
|
3
|
-
django_bolt-0.2.
|
|
1
|
+
django_bolt-0.2.3.dist-info/METADATA,sha256=xrjcnw-AiqNcaJNTR9zjgP4cc26YTXJQhANvRaa-EkY,22007
|
|
2
|
+
django_bolt-0.2.3.dist-info/WHEEL,sha256=4EDp_7DiFfWl1yYv5M4wSosAn5L_xgD1dyrQxQxfCx8,95
|
|
3
|
+
django_bolt-0.2.3.dist-info/entry_points.txt,sha256=cUEGAdiOY6BryNhsgOS_50AONPPHajI3yvhqr56ZiaU,51
|
|
4
4
|
django_bolt/__init__.py,sha256=fhXQUJR5SO-GwhF_2N9w0UK6rIFx7kWGfpOPyIKvB9Q,3139
|
|
5
|
-
django_bolt/_core.pyd,sha256=
|
|
5
|
+
django_bolt/_core.pyd,sha256=D_Mam9MZpy5OxV3_xC61vdndqwH4AqIr0hNFfE-zXg4,8941568
|
|
6
6
|
django_bolt/_json.py,sha256=oGxi29DHB8UYvbqjtqtrP6gThk7Qonlw333c4_cTr6s,4917
|
|
7
7
|
django_bolt/admin/__init__.py,sha256=BNN1afSvWvt-377rNzZLdNDEvKyMZXkmB_0MhTxAN8k,601
|
|
8
8
|
django_bolt/admin/admin_detection.py,sha256=1yQkLx9rF5DLMjJqbx8n4c82UgS9JEAnUcPdRfFqAXk,5825
|
|
@@ -83,9 +83,9 @@ django_bolt/responses.py,sha256=B0LXHN431c1-o93DC_xTF-wrnv8CMxn-1Sd0Yi4cwjE,7238
|
|
|
83
83
|
django_bolt/router.py,sha256=wTFsPasg5Sh5hgdFp1kCmUrK6ugwRPWEj7PsxWOc9lc,1532
|
|
84
84
|
django_bolt/serialization.py,sha256=Uza6jzQPTFxTQHnXEWBEDYY-3xDOGZinBSRE0T4KK28,8825
|
|
85
85
|
django_bolt/status_codes.py,sha256=jTZ32kPwj3tC23jh6uC1Ci1P9EKttywsvyWINQIEwGg,9386
|
|
86
|
-
django_bolt/testing/__init__.py,sha256=
|
|
87
|
-
django_bolt/testing/client.py,sha256=
|
|
88
|
-
django_bolt/testing/helpers.py,sha256=
|
|
86
|
+
django_bolt/testing/__init__.py,sha256=TgJjct2WucKL7HyrbMuQfplNptlCPhu4CL-WQZxaoRY,216
|
|
87
|
+
django_bolt/testing/client.py,sha256=Yx7N1lfXmJcH4yF--6K0YehtPY2_wiwb8U3a0qQoqDE,8616
|
|
88
|
+
django_bolt/testing/helpers.py,sha256=hq2HD1IEwHuW8tMgZO9ZW6BFdcE_JQXaV8sZc58LBDc,1472
|
|
89
89
|
django_bolt/typing.py,sha256=sGlFUUXY8EJbYWCgARgNGLZPu7SaxxohZaSx1SRLYaE,8686
|
|
90
90
|
django_bolt/views.py,sha256=KewSpL6g-zZxXnEkwun6hueX3S_Eji-d2dawzoqe-GM,41715
|
|
91
|
-
django_bolt-0.2.
|
|
91
|
+
django_bolt-0.2.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|