django-bolt 0.1.0__cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.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/__init__.py +147 -0
- django_bolt/_core.abi3.so +0 -0
- django_bolt/admin/__init__.py +25 -0
- django_bolt/admin/admin_detection.py +179 -0
- django_bolt/admin/asgi_bridge.py +267 -0
- django_bolt/admin/routes.py +91 -0
- django_bolt/admin/static.py +155 -0
- django_bolt/admin/static_routes.py +111 -0
- django_bolt/api.py +1011 -0
- django_bolt/apps.py +7 -0
- django_bolt/async_collector.py +228 -0
- django_bolt/auth/README.md +464 -0
- django_bolt/auth/REVOCATION_EXAMPLE.md +391 -0
- django_bolt/auth/__init__.py +84 -0
- django_bolt/auth/backends.py +236 -0
- django_bolt/auth/guards.py +224 -0
- django_bolt/auth/jwt_utils.py +212 -0
- django_bolt/auth/revocation.py +286 -0
- django_bolt/auth/token.py +335 -0
- django_bolt/binding.py +363 -0
- django_bolt/bootstrap.py +77 -0
- django_bolt/cli.py +133 -0
- django_bolt/compression.py +104 -0
- django_bolt/decorators.py +159 -0
- django_bolt/dependencies.py +128 -0
- django_bolt/error_handlers.py +305 -0
- django_bolt/exceptions.py +294 -0
- django_bolt/health.py +129 -0
- django_bolt/logging/__init__.py +6 -0
- django_bolt/logging/config.py +357 -0
- django_bolt/logging/middleware.py +296 -0
- django_bolt/management/__init__.py +1 -0
- django_bolt/management/commands/__init__.py +0 -0
- django_bolt/management/commands/runbolt.py +427 -0
- django_bolt/middleware/__init__.py +32 -0
- django_bolt/middleware/compiler.py +131 -0
- django_bolt/middleware/middleware.py +247 -0
- django_bolt/openapi/__init__.py +23 -0
- django_bolt/openapi/config.py +196 -0
- django_bolt/openapi/plugins.py +439 -0
- django_bolt/openapi/routes.py +152 -0
- django_bolt/openapi/schema_generator.py +581 -0
- django_bolt/openapi/spec/__init__.py +68 -0
- django_bolt/openapi/spec/base.py +74 -0
- django_bolt/openapi/spec/callback.py +24 -0
- django_bolt/openapi/spec/components.py +72 -0
- django_bolt/openapi/spec/contact.py +21 -0
- django_bolt/openapi/spec/discriminator.py +25 -0
- django_bolt/openapi/spec/encoding.py +67 -0
- django_bolt/openapi/spec/enums.py +41 -0
- django_bolt/openapi/spec/example.py +36 -0
- django_bolt/openapi/spec/external_documentation.py +21 -0
- django_bolt/openapi/spec/header.py +132 -0
- django_bolt/openapi/spec/info.py +50 -0
- django_bolt/openapi/spec/license.py +28 -0
- django_bolt/openapi/spec/link.py +66 -0
- django_bolt/openapi/spec/media_type.py +51 -0
- django_bolt/openapi/spec/oauth_flow.py +36 -0
- django_bolt/openapi/spec/oauth_flows.py +28 -0
- django_bolt/openapi/spec/open_api.py +87 -0
- django_bolt/openapi/spec/operation.py +105 -0
- django_bolt/openapi/spec/parameter.py +147 -0
- django_bolt/openapi/spec/path_item.py +78 -0
- django_bolt/openapi/spec/paths.py +27 -0
- django_bolt/openapi/spec/reference.py +38 -0
- django_bolt/openapi/spec/request_body.py +38 -0
- django_bolt/openapi/spec/response.py +48 -0
- django_bolt/openapi/spec/responses.py +44 -0
- django_bolt/openapi/spec/schema.py +678 -0
- django_bolt/openapi/spec/security_requirement.py +28 -0
- django_bolt/openapi/spec/security_scheme.py +69 -0
- django_bolt/openapi/spec/server.py +34 -0
- django_bolt/openapi/spec/server_variable.py +32 -0
- django_bolt/openapi/spec/tag.py +32 -0
- django_bolt/openapi/spec/xml.py +44 -0
- django_bolt/pagination.py +669 -0
- django_bolt/param_functions.py +49 -0
- django_bolt/params.py +337 -0
- django_bolt/request_parsing.py +128 -0
- django_bolt/responses.py +214 -0
- django_bolt/router.py +48 -0
- django_bolt/serialization.py +193 -0
- django_bolt/status_codes.py +321 -0
- django_bolt/testing/__init__.py +10 -0
- django_bolt/testing/client.py +274 -0
- django_bolt/testing/helpers.py +93 -0
- django_bolt/tests/__init__.py +0 -0
- django_bolt/tests/admin_tests/__init__.py +1 -0
- django_bolt/tests/admin_tests/conftest.py +6 -0
- django_bolt/tests/admin_tests/test_admin_with_django.py +278 -0
- django_bolt/tests/admin_tests/urls.py +9 -0
- django_bolt/tests/cbv/__init__.py +0 -0
- django_bolt/tests/cbv/test_class_views.py +570 -0
- django_bolt/tests/cbv/test_class_views_django_orm.py +703 -0
- django_bolt/tests/cbv/test_class_views_features.py +1173 -0
- django_bolt/tests/cbv/test_class_views_with_client.py +622 -0
- django_bolt/tests/conftest.py +165 -0
- django_bolt/tests/test_action_decorator.py +399 -0
- django_bolt/tests/test_auth_secret_key.py +83 -0
- django_bolt/tests/test_decorator_syntax.py +159 -0
- django_bolt/tests/test_error_handling.py +481 -0
- django_bolt/tests/test_file_response.py +192 -0
- django_bolt/tests/test_global_cors.py +172 -0
- django_bolt/tests/test_guards_auth.py +441 -0
- django_bolt/tests/test_guards_integration.py +303 -0
- django_bolt/tests/test_health.py +283 -0
- django_bolt/tests/test_integration_validation.py +400 -0
- django_bolt/tests/test_json_validation.py +536 -0
- django_bolt/tests/test_jwt_auth.py +327 -0
- django_bolt/tests/test_jwt_token.py +458 -0
- django_bolt/tests/test_logging.py +837 -0
- django_bolt/tests/test_logging_merge.py +419 -0
- django_bolt/tests/test_middleware.py +492 -0
- django_bolt/tests/test_middleware_server.py +230 -0
- django_bolt/tests/test_model_viewset.py +323 -0
- django_bolt/tests/test_models.py +24 -0
- django_bolt/tests/test_pagination.py +1258 -0
- django_bolt/tests/test_parameter_validation.py +178 -0
- django_bolt/tests/test_syntax.py +626 -0
- django_bolt/tests/test_testing_utilities.py +163 -0
- django_bolt/tests/test_testing_utilities_simple.py +123 -0
- django_bolt/tests/test_viewset_unified.py +346 -0
- django_bolt/typing.py +273 -0
- django_bolt/views.py +1110 -0
- django_bolt-0.1.0.dist-info/METADATA +629 -0
- django_bolt-0.1.0.dist-info/RECORD +128 -0
- django_bolt-0.1.0.dist-info/WHEEL +4 -0
- django_bolt-0.1.0.dist-info/entry_points.txt +2 -0
django_bolt/typing.py
ADDED
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Type introspection and field definition system for parameter binding.
|
|
3
|
+
|
|
4
|
+
Inspired by Litestar's architecture but built from scratch for Django-Bolt's
|
|
5
|
+
msgspec-first, async-only design with focus on performance.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import inspect
|
|
10
|
+
import msgspec
|
|
11
|
+
from dataclasses import dataclass
|
|
12
|
+
from typing import Any, get_origin, get_args, Union, Optional, List, Dict, Annotated
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"FieldDefinition",
|
|
16
|
+
"is_msgspec_struct",
|
|
17
|
+
"is_simple_type",
|
|
18
|
+
"is_sequence_type",
|
|
19
|
+
"is_optional",
|
|
20
|
+
"unwrap_optional",
|
|
21
|
+
"infer_param_source",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# Simple scalar types that map to query parameters
|
|
26
|
+
SIMPLE_TYPES = (str, int, float, bool, bytes)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def is_msgspec_struct(annotation: Any) -> bool:
|
|
30
|
+
"""Check if type is a msgspec.Struct."""
|
|
31
|
+
try:
|
|
32
|
+
return isinstance(annotation, type) and issubclass(annotation, msgspec.Struct)
|
|
33
|
+
except (TypeError, AttributeError):
|
|
34
|
+
return False
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def is_simple_type(annotation: Any) -> bool:
|
|
38
|
+
"""Check if annotation is a simple scalar type (str, int, float, bool, bytes)."""
|
|
39
|
+
origin = get_origin(annotation)
|
|
40
|
+
if origin is not None:
|
|
41
|
+
# Unwrap Optional, List, etc.
|
|
42
|
+
return False
|
|
43
|
+
return annotation in SIMPLE_TYPES or annotation is Any
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def is_sequence_type(annotation: Any) -> bool:
|
|
47
|
+
"""Check if annotation is a sequence type like List[T]."""
|
|
48
|
+
origin = get_origin(annotation)
|
|
49
|
+
return origin in (list, List, tuple, set, frozenset)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def is_optional(annotation: Any) -> bool:
|
|
53
|
+
"""Check if annotation is Optional[T] or T | None."""
|
|
54
|
+
origin = get_origin(annotation)
|
|
55
|
+
if origin is Union:
|
|
56
|
+
args = get_args(annotation)
|
|
57
|
+
return type(None) in args
|
|
58
|
+
return False
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def unwrap_optional(annotation: Any) -> Any:
|
|
62
|
+
"""Unwrap Optional[T] to get T."""
|
|
63
|
+
origin = get_origin(annotation)
|
|
64
|
+
if origin is Union:
|
|
65
|
+
args = tuple(a for a in get_args(annotation) if a is not type(None))
|
|
66
|
+
return args[0] if len(args) == 1 else Union[args] # type: ignore
|
|
67
|
+
return annotation
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def is_dataclass_type(annotation: Any) -> bool:
|
|
71
|
+
"""Check if annotation is a dataclass."""
|
|
72
|
+
try:
|
|
73
|
+
from dataclasses import is_dataclass
|
|
74
|
+
return is_dataclass(annotation)
|
|
75
|
+
except (TypeError, AttributeError):
|
|
76
|
+
return False
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def infer_param_source(
|
|
80
|
+
name: str,
|
|
81
|
+
annotation: Any,
|
|
82
|
+
path_params: set[str],
|
|
83
|
+
http_method: str
|
|
84
|
+
) -> str:
|
|
85
|
+
"""
|
|
86
|
+
Infer parameter source based on type and context.
|
|
87
|
+
|
|
88
|
+
Inference rules (in priority order):
|
|
89
|
+
1. If name matches path parameter -> "path"
|
|
90
|
+
2. If special name (request, req) -> "request"
|
|
91
|
+
3. If simple type (str, int, float, bool) -> "query"
|
|
92
|
+
4. If msgspec.Struct or dataclass -> "body" (if method allows body)
|
|
93
|
+
5. Default -> "query"
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
name: Parameter name
|
|
97
|
+
annotation: Type annotation
|
|
98
|
+
path_params: Set of path parameter names from route pattern
|
|
99
|
+
http_method: HTTP method (GET, POST, etc.)
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
Source string: "path", "query", "body", "request", etc.
|
|
103
|
+
"""
|
|
104
|
+
# 1. Path parameters
|
|
105
|
+
if name in path_params:
|
|
106
|
+
return "path"
|
|
107
|
+
|
|
108
|
+
# 2. Special request parameter
|
|
109
|
+
if name in {"request", "req"}:
|
|
110
|
+
return "request"
|
|
111
|
+
|
|
112
|
+
# Unwrap Optional if present
|
|
113
|
+
unwrapped = unwrap_optional(annotation)
|
|
114
|
+
|
|
115
|
+
# 3. Simple types -> query params
|
|
116
|
+
if is_simple_type(unwrapped):
|
|
117
|
+
return "query"
|
|
118
|
+
|
|
119
|
+
# 4. Sequence of simple types -> query (for list params)
|
|
120
|
+
if is_sequence_type(unwrapped):
|
|
121
|
+
args = get_args(unwrapped)
|
|
122
|
+
if args and is_simple_type(args[0]):
|
|
123
|
+
return "query"
|
|
124
|
+
|
|
125
|
+
# 5. Complex types (msgspec.Struct, dataclass) -> body (if allowed)
|
|
126
|
+
if is_msgspec_struct(unwrapped) or is_dataclass_type(unwrapped):
|
|
127
|
+
if http_method in {"POST", "PUT", "PATCH"}:
|
|
128
|
+
return "body"
|
|
129
|
+
# For GET/DELETE/HEAD, this will trigger validation error later
|
|
130
|
+
return "body"
|
|
131
|
+
|
|
132
|
+
# 6. Default to query
|
|
133
|
+
return "query"
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
@dataclass(frozen=True)
|
|
137
|
+
class FieldDefinition:
|
|
138
|
+
"""
|
|
139
|
+
Represents a parsed function parameter with type metadata.
|
|
140
|
+
|
|
141
|
+
This is the core data structure for parameter binding, containing
|
|
142
|
+
all information needed to extract and validate a parameter value
|
|
143
|
+
from an HTTP request.
|
|
144
|
+
"""
|
|
145
|
+
|
|
146
|
+
name: str
|
|
147
|
+
"""Parameter name"""
|
|
148
|
+
|
|
149
|
+
annotation: Any
|
|
150
|
+
"""Raw type annotation"""
|
|
151
|
+
|
|
152
|
+
default: Any
|
|
153
|
+
"""Default value (inspect.Parameter.empty if required)"""
|
|
154
|
+
|
|
155
|
+
source: str
|
|
156
|
+
"""Parameter source: 'path', 'query', 'body', 'header', 'cookie', 'form', 'file', 'request', 'dependency'"""
|
|
157
|
+
|
|
158
|
+
alias: Optional[str] = None
|
|
159
|
+
"""Alternative name for the parameter (e.g., 'user-id' for 'user_id')"""
|
|
160
|
+
|
|
161
|
+
embed: Optional[bool] = None
|
|
162
|
+
"""For body params: whether to embed in a wrapper object"""
|
|
163
|
+
|
|
164
|
+
dependency: Any = None
|
|
165
|
+
"""Depends marker for dependency injection"""
|
|
166
|
+
|
|
167
|
+
kind: inspect._ParameterKind = inspect.Parameter.POSITIONAL_OR_KEYWORD
|
|
168
|
+
"""Parameter kind (positional, keyword-only, etc.)"""
|
|
169
|
+
|
|
170
|
+
# Cached type properties for performance
|
|
171
|
+
_is_optional: Optional[bool] = None
|
|
172
|
+
_is_simple: Optional[bool] = None
|
|
173
|
+
_is_struct: Optional[bool] = None
|
|
174
|
+
_unwrapped: Optional[Any] = None
|
|
175
|
+
|
|
176
|
+
@property
|
|
177
|
+
def is_optional(self) -> bool:
|
|
178
|
+
"""Check if parameter is optional (has default or Optional type)."""
|
|
179
|
+
if self._is_optional is None:
|
|
180
|
+
object.__setattr__(
|
|
181
|
+
self,
|
|
182
|
+
"_is_optional",
|
|
183
|
+
self.default is not inspect.Parameter.empty or is_optional(self.annotation)
|
|
184
|
+
)
|
|
185
|
+
return self._is_optional # type: ignore
|
|
186
|
+
|
|
187
|
+
@property
|
|
188
|
+
def is_required(self) -> bool:
|
|
189
|
+
"""Check if parameter is required."""
|
|
190
|
+
return not self.is_optional
|
|
191
|
+
|
|
192
|
+
@property
|
|
193
|
+
def is_simple_type(self) -> bool:
|
|
194
|
+
"""Check if parameter type is simple (str, int, etc.)."""
|
|
195
|
+
if self._is_simple is None:
|
|
196
|
+
unwrapped = self.unwrapped_annotation
|
|
197
|
+
object.__setattr__(self, "_is_simple", is_simple_type(unwrapped))
|
|
198
|
+
return self._is_simple # type: ignore
|
|
199
|
+
|
|
200
|
+
@property
|
|
201
|
+
def is_msgspec_struct(self) -> bool:
|
|
202
|
+
"""Check if parameter type is a msgspec.Struct."""
|
|
203
|
+
if self._is_struct is None:
|
|
204
|
+
unwrapped = self.unwrapped_annotation
|
|
205
|
+
object.__setattr__(self, "_is_struct", is_msgspec_struct(unwrapped))
|
|
206
|
+
return self._is_struct # type: ignore
|
|
207
|
+
|
|
208
|
+
@property
|
|
209
|
+
def unwrapped_annotation(self) -> Any:
|
|
210
|
+
"""Get annotation with Optional unwrapped."""
|
|
211
|
+
if self._unwrapped is None:
|
|
212
|
+
object.__setattr__(self, "_unwrapped", unwrap_optional(self.annotation))
|
|
213
|
+
return self._unwrapped
|
|
214
|
+
|
|
215
|
+
@property
|
|
216
|
+
def field_alias(self) -> str:
|
|
217
|
+
"""Get the alias or name."""
|
|
218
|
+
return self.alias or self.name
|
|
219
|
+
|
|
220
|
+
@classmethod
|
|
221
|
+
def from_parameter(
|
|
222
|
+
cls,
|
|
223
|
+
parameter: inspect.Parameter,
|
|
224
|
+
annotation: Any,
|
|
225
|
+
path_params: set[str],
|
|
226
|
+
http_method: str,
|
|
227
|
+
explicit_marker: Any = None
|
|
228
|
+
) -> "FieldDefinition":
|
|
229
|
+
"""
|
|
230
|
+
Create FieldDefinition from inspect.Parameter.
|
|
231
|
+
|
|
232
|
+
Args:
|
|
233
|
+
parameter: The inspect.Parameter object
|
|
234
|
+
annotation: Type annotation from type hints
|
|
235
|
+
path_params: Set of path parameter names
|
|
236
|
+
http_method: HTTP method for inference
|
|
237
|
+
explicit_marker: Explicit Param or Depends marker if present
|
|
238
|
+
|
|
239
|
+
Returns:
|
|
240
|
+
FieldDefinition instance
|
|
241
|
+
"""
|
|
242
|
+
from .params import Param, Depends as DependsMarker
|
|
243
|
+
|
|
244
|
+
name = parameter.name
|
|
245
|
+
default = parameter.default
|
|
246
|
+
|
|
247
|
+
# Handle explicit markers
|
|
248
|
+
source: str
|
|
249
|
+
alias: Optional[str] = None
|
|
250
|
+
embed: Optional[bool] = None
|
|
251
|
+
dependency: Any = None
|
|
252
|
+
|
|
253
|
+
if isinstance(explicit_marker, Param):
|
|
254
|
+
source = explicit_marker.source
|
|
255
|
+
alias = explicit_marker.alias
|
|
256
|
+
embed = explicit_marker.embed
|
|
257
|
+
elif isinstance(explicit_marker, DependsMarker):
|
|
258
|
+
source = "dependency"
|
|
259
|
+
dependency = explicit_marker
|
|
260
|
+
else:
|
|
261
|
+
# Infer source from type and context
|
|
262
|
+
source = infer_param_source(name, annotation, path_params, http_method)
|
|
263
|
+
|
|
264
|
+
return cls(
|
|
265
|
+
name=name,
|
|
266
|
+
annotation=annotation,
|
|
267
|
+
default=default,
|
|
268
|
+
source=source,
|
|
269
|
+
alias=alias,
|
|
270
|
+
embed=embed,
|
|
271
|
+
dependency=dependency,
|
|
272
|
+
kind=parameter.kind,
|
|
273
|
+
)
|