django-bolt 0.2.0__cp310-abi3-win_amd64.whl → 0.2.2__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.2.dist-info}/METADATA +33 -19
- {django_bolt-0.2.0.dist-info → django_bolt-0.2.2.dist-info}/RECORD +8 -8
- {django_bolt-0.2.0.dist-info → django_bolt-0.2.2.dist-info}/WHEEL +0 -0
- {django_bolt-0.2.0.dist-info → django_bolt-0.2.2.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.2
|
|
4
4
|
Classifier: Development Status :: 3 - Alpha
|
|
5
5
|
Classifier: Intended Audience :: Developers
|
|
6
6
|
Classifier: License :: OSI Approved :: MIT License
|
|
@@ -92,6 +92,7 @@ python manage.py runbolt --dev # for development with reload enabled
|
|
|
92
92
|
[django-bolt] Workers: 1, Processes: 1
|
|
93
93
|
[django-bolt] OpenAPI docs enabled at http://0.0.0.0:8000/docs/ #swagger docs builtin
|
|
94
94
|
|
|
95
|
+
python manage.py runbolt --processes 8 workers 1 #for deployment (depends on your cpu cores)
|
|
95
96
|
# processes are python processes that handle request 1 actix worker
|
|
96
97
|
```
|
|
97
98
|
|
|
@@ -166,7 +167,11 @@ python manage.py runbolt --dev # for development with reload enabled
|
|
|
166
167
|
|
|
167
168
|
- ✅ **Global Middleware** - Apply to all routes via `BoltAPI(middleware=[...])`
|
|
168
169
|
- ✅ **Per-Route Middleware** - `@middleware`, `@rate_limit`, `@cors` decorators
|
|
169
|
-
- ✅ **CORS Middleware** - Full CORS support with preflight
|
|
170
|
+
- ✅ **CORS Middleware** - Full CORS support with preflight handling
|
|
171
|
+
- Django settings integration (`CORS_ALLOWED_ORIGINS`, `CORS_ALLOW_ALL_ORIGINS`)
|
|
172
|
+
- Route-level overrides with `@cors()` decorator
|
|
173
|
+
- Origin validation, credentials support, preflight caching
|
|
174
|
+
- Runs entirely in Rust (zero GIL overhead)
|
|
170
175
|
- ✅ **Rate Limiting** - Token bucket algorithm (in Rust, no GIL)
|
|
171
176
|
- ✅ **Compression** - Automatic gzip/brotli/zstd compression (client-negotiated)
|
|
172
177
|
- ✅ **Skip Middleware** - `@skip_middleware("cors", "rate_limit", "compression")`
|
|
@@ -190,13 +195,6 @@ python manage.py runbolt --dev # for development with reload enabled
|
|
|
190
195
|
- Constant-time comparison (timing attack prevention)
|
|
191
196
|
- Fast validation in Rust
|
|
192
197
|
|
|
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
198
|
- ✅ **Permission Guards** (all run in Rust):
|
|
201
199
|
|
|
202
200
|
- `AllowAny()` - Public access
|
|
@@ -374,24 +372,45 @@ async def login(username: str, password: str):
|
|
|
374
372
|
|
|
375
373
|
**📖 See [docs/SECURITY.md](docs/SECURITY.md) for complete authentication documentation.**
|
|
376
374
|
|
|
377
|
-
### Middleware
|
|
375
|
+
### Middleware & CORS
|
|
378
376
|
|
|
379
377
|
```python
|
|
380
378
|
from django_bolt import BoltAPI
|
|
381
379
|
from django_bolt.middleware import cors, rate_limit, skip_middleware
|
|
382
380
|
|
|
383
|
-
#
|
|
381
|
+
# Option 1: Use Django settings (recommended for production)
|
|
382
|
+
# In settings.py:
|
|
383
|
+
# CORS_ALLOWED_ORIGINS = ["https://example.com", "https://app.example.com"]
|
|
384
|
+
# CORS_ALLOW_CREDENTIALS = True
|
|
385
|
+
# CORS_MAX_AGE = 3600
|
|
386
|
+
|
|
387
|
+
api = BoltAPI() # Automatically reads Django CORS settings
|
|
388
|
+
|
|
389
|
+
# Option 2: Global middleware config
|
|
384
390
|
api = BoltAPI(
|
|
385
391
|
middleware_config={
|
|
386
392
|
"cors": {
|
|
387
393
|
"origins": ["http://localhost:3000"],
|
|
388
394
|
"methods": ["GET", "POST", "PUT", "DELETE"],
|
|
389
395
|
"credentials": True,
|
|
396
|
+
"max_age": 3600,
|
|
390
397
|
}
|
|
391
398
|
}
|
|
392
399
|
)
|
|
393
400
|
|
|
394
|
-
# Per-route
|
|
401
|
+
# Option 3: Per-route CORS override (overrides global/Django settings)
|
|
402
|
+
@api.get("/public-api")
|
|
403
|
+
@cors(origins=["*"], credentials=False) # Allow all origins
|
|
404
|
+
async def public_endpoint():
|
|
405
|
+
return {"message": "Public endpoint with custom CORS"}
|
|
406
|
+
|
|
407
|
+
# CORS with credentials and specific origins
|
|
408
|
+
@api.post("/auth-endpoint")
|
|
409
|
+
@cors(origins=["https://app.example.com"], credentials=True, max_age=3600)
|
|
410
|
+
async def auth_endpoint():
|
|
411
|
+
return {"message": "Authenticated endpoint with CORS"}
|
|
412
|
+
|
|
413
|
+
# Rate limiting (runs in Rust, no GIL)
|
|
395
414
|
@api.get("/limited")
|
|
396
415
|
@rate_limit(rps=100, burst=200, key="ip") # 100 req/s with burst of 200
|
|
397
416
|
async def limited_endpoint():
|
|
@@ -403,12 +422,6 @@ async def limited_endpoint():
|
|
|
403
422
|
async def user_limited():
|
|
404
423
|
return {"message": "Per-user rate limiting"}
|
|
405
424
|
|
|
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
425
|
# Skip global middleware
|
|
413
426
|
@api.get("/no-cors")
|
|
414
427
|
@skip_middleware("cors", "rate_limit")
|
|
@@ -606,4 +619,5 @@ Django-Bolt is open source and available under the MIT License.
|
|
|
606
619
|
|
|
607
620
|
---
|
|
608
621
|
|
|
609
|
-
For questions, issues, or feature requests, please visit our [GitHub repository](https://github.com/FarhanAliRaza/django-bolt).
|
|
622
|
+
For questions, issues, or feature requests, please visit our [GitHub repository](https://github.com/FarhanAliRaza/django-bolt).
|
|
623
|
+
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
django_bolt-0.2.
|
|
2
|
-
django_bolt-0.2.
|
|
3
|
-
django_bolt-0.2.
|
|
1
|
+
django_bolt-0.2.2.dist-info/METADATA,sha256=Df6mtO4KEFHivc0NVXio1G_8k-f1NF0jCMazmVgEVw8,21978
|
|
2
|
+
django_bolt-0.2.2.dist-info/WHEEL,sha256=4EDp_7DiFfWl1yYv5M4wSosAn5L_xgD1dyrQxQxfCx8,95
|
|
3
|
+
django_bolt-0.2.2.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=2_iF6HF1hgI4YXmW-dvJM81gPnL3PwHViqXv9QedDMo,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.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|