strawberry-graphql 0.257.0__py3-none-any.whl → 0.257.0.dev1735244504__py3-none-any.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.
- strawberry/__init__.py +0 -2
- strawberry/aiohttp/test/client.py +3 -1
- strawberry/aiohttp/views.py +3 -3
- strawberry/annotation.py +7 -5
- strawberry/asgi/__init__.py +4 -4
- strawberry/channels/handlers/ws_handler.py +3 -3
- strawberry/channels/testing.py +1 -3
- strawberry/cli/__init__.py +5 -7
- strawberry/cli/commands/codegen.py +14 -12
- strawberry/cli/commands/server.py +2 -2
- strawberry/cli/commands/upgrade/_run_codemod.py +3 -2
- strawberry/cli/utils/__init__.py +1 -1
- strawberry/codegen/plugins/python.py +5 -4
- strawberry/codegen/plugins/typescript.py +2 -3
- strawberry/codegen/query_codegen.py +11 -12
- strawberry/codemods/annotated_unions.py +1 -1
- strawberry/codemods/update_imports.py +4 -2
- strawberry/dataloader.py +2 -2
- strawberry/django/__init__.py +2 -2
- strawberry/django/views.py +3 -2
- strawberry/exceptions/__init__.py +2 -4
- strawberry/exceptions/exception.py +1 -1
- strawberry/exceptions/permission_fail_silently_requires_optional.py +1 -2
- strawberry/exceptions/utils/source_finder.py +1 -1
- strawberry/experimental/pydantic/_compat.py +4 -4
- strawberry/experimental/pydantic/conversion.py +5 -4
- strawberry/experimental/pydantic/fields.py +2 -1
- strawberry/experimental/pydantic/object_type.py +2 -6
- strawberry/experimental/pydantic/utils.py +9 -3
- strawberry/ext/mypy_plugin.py +14 -7
- strawberry/extensions/context.py +19 -15
- strawberry/extensions/field_extension.py +54 -53
- strawberry/extensions/query_depth_limiter.py +33 -27
- strawberry/extensions/tracing/datadog.py +1 -1
- strawberry/extensions/tracing/opentelemetry.py +14 -9
- strawberry/fastapi/router.py +3 -2
- strawberry/federation/schema.py +3 -3
- strawberry/flask/views.py +2 -3
- strawberry/http/async_base_view.py +4 -2
- strawberry/http/ides.py +3 -1
- strawberry/http/sync_base_view.py +2 -1
- strawberry/litestar/controller.py +5 -6
- strawberry/permission.py +1 -1
- strawberry/quart/views.py +2 -2
- strawberry/relay/fields.py +3 -28
- strawberry/relay/types.py +1 -1
- strawberry/schema/base.py +2 -0
- strawberry/schema/execute.py +11 -11
- strawberry/schema/name_converter.py +5 -4
- strawberry/schema/schema.py +4 -6
- strawberry/schema/schema_converter.py +19 -24
- strawberry/schema/subscribe.py +4 -4
- strawberry/schema/types/base_scalars.py +2 -4
- strawberry/schema/types/scalar.py +1 -1
- strawberry/schema_codegen/__init__.py +6 -5
- strawberry/subscriptions/protocols/graphql_transport_ws/handlers.py +2 -2
- strawberry/subscriptions/protocols/graphql_ws/handlers.py +3 -0
- strawberry/test/client.py +2 -1
- strawberry/types/arguments.py +2 -2
- strawberry/types/auto.py +3 -3
- strawberry/types/base.py +16 -12
- strawberry/types/field.py +7 -11
- strawberry/types/fields/resolver.py +19 -12
- strawberry/types/union.py +1 -1
- strawberry/types/unset.py +2 -1
- strawberry/utils/debug.py +1 -1
- strawberry/utils/deprecations.py +1 -1
- strawberry/utils/graphql_lexer.py +4 -6
- strawberry/utils/typing.py +2 -1
- {strawberry_graphql-0.257.0.dist-info → strawberry_graphql-0.257.0.dev1735244504.dist-info}/METADATA +2 -2
- {strawberry_graphql-0.257.0.dist-info → strawberry_graphql-0.257.0.dev1735244504.dist-info}/RECORD +74 -75
- {strawberry_graphql-0.257.0.dist-info → strawberry_graphql-0.257.0.dev1735244504.dist-info}/WHEEL +1 -1
- strawberry/types/cast.py +0 -35
- {strawberry_graphql-0.257.0.dist-info → strawberry_graphql-0.257.0.dev1735244504.dist-info}/LICENSE +0 -0
- {strawberry_graphql-0.257.0.dist-info → strawberry_graphql-0.257.0.dev1735244504.dist-info}/entry_points.txt +0 -0
strawberry/ext/mypy_plugin.py
CHANGED
@@ -111,7 +111,9 @@ def lazy_type_analyze_callback(ctx: AnalyzeTypeContext) -> Type:
|
|
111
111
|
return AnyType(TypeOfAny.special_form)
|
112
112
|
|
113
113
|
type_name = ctx.type.args[0]
|
114
|
-
|
114
|
+
type_ = ctx.api.analyze_type(type_name)
|
115
|
+
|
116
|
+
return type_
|
115
117
|
|
116
118
|
|
117
119
|
def _get_named_type(name: str, api: SemanticAnalyzerPluginInterface) -> Any:
|
@@ -145,12 +147,14 @@ def _get_type_for_expr(expr: Expression, api: SemanticAnalyzerPluginInterface) -
|
|
145
147
|
if isinstance(expr, MemberExpr):
|
146
148
|
if expr.fullname:
|
147
149
|
return _get_named_type(expr.fullname, api)
|
148
|
-
|
150
|
+
else:
|
151
|
+
raise InvalidNodeTypeException(expr)
|
149
152
|
|
150
153
|
if isinstance(expr, CallExpr):
|
151
154
|
if expr.analyzed:
|
152
155
|
return _get_type_for_expr(expr.analyzed, api)
|
153
|
-
|
156
|
+
else:
|
157
|
+
raise InvalidNodeTypeException(expr)
|
154
158
|
|
155
159
|
if isinstance(expr, CastExpr):
|
156
160
|
return expr.type
|
@@ -174,6 +178,8 @@ def create_type_hook(ctx: DynamicClassDefContext) -> None:
|
|
174
178
|
SymbolTableNode(GDEF, type_alias, plugin_generated=True),
|
175
179
|
)
|
176
180
|
|
181
|
+
return
|
182
|
+
|
177
183
|
|
178
184
|
def union_hook(ctx: DynamicClassDefContext) -> None:
|
179
185
|
try:
|
@@ -337,12 +343,13 @@ def add_static_method_to_class(
|
|
337
343
|
cls.defs.body.remove(sym.node)
|
338
344
|
|
339
345
|
# For compat with mypy < 0.93
|
340
|
-
if Decimal("0.93")
|
346
|
+
if MypyVersion.VERSION < Decimal("0.93"):
|
341
347
|
function_type = api.named_type("__builtins__.function")
|
342
|
-
elif isinstance(api, SemanticAnalyzerPluginInterface):
|
343
|
-
function_type = api.named_type("builtins.function")
|
344
348
|
else:
|
345
|
-
|
349
|
+
if isinstance(api, SemanticAnalyzerPluginInterface):
|
350
|
+
function_type = api.named_type("builtins.function")
|
351
|
+
else:
|
352
|
+
function_type = api.named_generic_type("builtins.function", [])
|
346
353
|
|
347
354
|
arg_types, arg_names, arg_kinds = [], [], []
|
348
355
|
for arg in args:
|
strawberry/extensions/context.py
CHANGED
@@ -77,7 +77,7 @@ class ExtensionContextManagerBase:
|
|
77
77
|
f"{extension} defines both legacy and new style extension hooks for "
|
78
78
|
"{self.HOOK_NAME}"
|
79
79
|
)
|
80
|
-
|
80
|
+
elif is_legacy:
|
81
81
|
warnings.warn(self.DEPRECATION_MESSAGE, DeprecationWarning, stacklevel=3)
|
82
82
|
return self.from_legacy(extension, on_start, on_end)
|
83
83
|
|
@@ -128,17 +128,19 @@ class ExtensionContextManagerBase:
|
|
128
128
|
|
129
129
|
return WrappedHook(extension=extension, hook=iterator, is_async=True)
|
130
130
|
|
131
|
-
|
132
|
-
def iterator_async() -> Iterator[None]:
|
133
|
-
if on_start:
|
134
|
-
on_start()
|
131
|
+
else:
|
135
132
|
|
136
|
-
|
133
|
+
@contextlib.contextmanager
|
134
|
+
def iterator_async() -> Iterator[None]:
|
135
|
+
if on_start:
|
136
|
+
on_start()
|
137
|
+
|
138
|
+
yield
|
137
139
|
|
138
|
-
|
139
|
-
|
140
|
+
if on_end:
|
141
|
+
on_end()
|
140
142
|
|
141
|
-
|
143
|
+
return WrappedHook(extension=extension, hook=iterator_async, is_async=False)
|
142
144
|
|
143
145
|
@staticmethod
|
144
146
|
def from_callable(
|
@@ -153,13 +155,14 @@ class ExtensionContextManagerBase:
|
|
153
155
|
yield
|
154
156
|
|
155
157
|
return WrappedHook(extension=extension, hook=iterator, is_async=True)
|
158
|
+
else:
|
156
159
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
160
|
+
@contextlib.contextmanager
|
161
|
+
def iterator() -> Iterator[None]:
|
162
|
+
func(extension)
|
163
|
+
yield
|
161
164
|
|
162
|
-
|
165
|
+
return WrappedHook(extension=extension, hook=iterator, is_async=False)
|
163
166
|
|
164
167
|
def __enter__(self) -> None:
|
165
168
|
self.exit_stack = contextlib.ExitStack()
|
@@ -172,7 +175,8 @@ class ExtensionContextManagerBase:
|
|
172
175
|
f"SchemaExtension hook {hook.extension}.{self.HOOK_NAME} "
|
173
176
|
"failed to complete synchronously."
|
174
177
|
)
|
175
|
-
|
178
|
+
else:
|
179
|
+
self.exit_stack.enter_context(hook.hook()) # type: ignore
|
176
180
|
|
177
181
|
def __exit__(
|
178
182
|
self,
|
@@ -99,61 +99,62 @@ def build_field_extension_resolvers(
|
|
99
99
|
f"Please add a resolve_async method to the extension(s)."
|
100
100
|
)
|
101
101
|
return _get_async_resolvers(field.extensions)
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
102
|
+
else:
|
103
|
+
# Try to wrap all sync resolvers in async so that we can use async extensions
|
104
|
+
# on sync fields. This is not possible the other way around since
|
105
|
+
# the result of an async resolver would have to be awaited before calling
|
106
|
+
# the sync extension, making it impossible for the extension to modify
|
107
|
+
# any arguments.
|
108
|
+
non_sync_extensions = [
|
109
|
+
extension for extension in field.extensions if not extension.supports_sync
|
110
|
+
]
|
111
|
+
|
112
|
+
if len(non_sync_extensions) == 0:
|
113
|
+
# Resolve everything sync
|
114
|
+
return _get_sync_resolvers(field.extensions)
|
115
|
+
|
116
|
+
# We have async-only extensions and need to wrap the resolver
|
117
|
+
# That means we can't have sync-only extensions after the first async one
|
118
|
+
|
119
|
+
# Check if we have a chain of sync-compatible
|
120
|
+
# extensions before the async extensions
|
121
|
+
# -> S-S-S-S-A-A-A-A
|
122
|
+
found_sync_extensions = 0
|
123
|
+
|
124
|
+
# All sync only extensions must be found before the first async-only one
|
125
|
+
found_sync_only_extensions = 0
|
126
|
+
for extension in field.extensions:
|
127
|
+
# ...A, abort
|
128
|
+
if extension in non_sync_extensions:
|
129
|
+
break
|
130
|
+
# ...S
|
131
|
+
if extension in non_async_extensions:
|
132
|
+
found_sync_only_extensions += 1
|
133
|
+
found_sync_extensions += 1
|
134
|
+
|
135
|
+
# Length of the chain equals length of non async extensions
|
136
|
+
# All sync extensions run first
|
137
|
+
if len(non_async_extensions) == found_sync_only_extensions:
|
138
|
+
# Prepend sync to async extension to field extensions
|
139
|
+
return list(
|
140
|
+
itertools.chain(
|
141
|
+
_get_sync_resolvers(field.extensions[:found_sync_extensions]),
|
142
|
+
[SyncToAsyncExtension().resolve_async],
|
143
|
+
_get_async_resolvers(field.extensions[found_sync_extensions:]),
|
144
|
+
)
|
143
145
|
)
|
144
|
-
)
|
145
146
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
147
|
+
# Some sync extensions follow the first async-only extension. Error case
|
148
|
+
async_extension_names = ",".join(
|
149
|
+
[extension.__class__.__name__ for extension in non_sync_extensions]
|
150
|
+
)
|
151
|
+
raise TypeError(
|
152
|
+
f"Cannot mix async-only extension(s) {async_extension_names} "
|
153
|
+
f"with sync-only extension(s) {non_async_extension_names} "
|
154
|
+
f"on Field {field.name}. "
|
155
|
+
f"If possible try to change the execution order so that all sync-only "
|
156
|
+
f"extensions are executed first."
|
157
|
+
)
|
157
158
|
|
158
159
|
|
159
160
|
__all__ = ["FieldExtension"]
|
@@ -189,17 +189,18 @@ def resolve_field_value(
|
|
189
189
|
) -> FieldArgumentType:
|
190
190
|
if isinstance(value, StringValueNode):
|
191
191
|
return value.value
|
192
|
-
|
192
|
+
elif isinstance(value, IntValueNode):
|
193
193
|
return int(value.value)
|
194
|
-
|
194
|
+
elif isinstance(value, FloatValueNode):
|
195
195
|
return float(value.value)
|
196
|
-
|
196
|
+
elif isinstance(value, BooleanValueNode):
|
197
197
|
return value.value
|
198
|
-
|
198
|
+
elif isinstance(value, ListValueNode):
|
199
199
|
return [resolve_field_value(v) for v in value.values]
|
200
|
-
|
200
|
+
elif isinstance(value, ObjectValueNode):
|
201
201
|
return {v.name.value: resolve_field_value(v.value) for v in value.fields}
|
202
|
-
|
202
|
+
else:
|
203
|
+
return {}
|
203
204
|
|
204
205
|
|
205
206
|
def get_field_arguments(
|
@@ -249,18 +250,20 @@ def determine_depth(
|
|
249
250
|
return 0
|
250
251
|
|
251
252
|
return 1 + max(
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
253
|
+
map(
|
254
|
+
lambda selection: determine_depth(
|
255
|
+
node=selection,
|
256
|
+
fragments=fragments,
|
257
|
+
depth_so_far=depth_so_far + 1,
|
258
|
+
max_depth=max_depth,
|
259
|
+
context=context,
|
260
|
+
operation_name=operation_name,
|
261
|
+
should_ignore=should_ignore,
|
262
|
+
),
|
263
|
+
node.selection_set.selections,
|
260
264
|
)
|
261
|
-
for selection in node.selection_set.selections
|
262
265
|
)
|
263
|
-
|
266
|
+
elif isinstance(node, FragmentSpreadNode):
|
264
267
|
return determine_depth(
|
265
268
|
node=fragments[node.name.value],
|
266
269
|
fragments=fragments,
|
@@ -270,22 +273,25 @@ def determine_depth(
|
|
270
273
|
operation_name=operation_name,
|
271
274
|
should_ignore=should_ignore,
|
272
275
|
)
|
273
|
-
|
276
|
+
elif isinstance(
|
274
277
|
node, (InlineFragmentNode, FragmentDefinitionNode, OperationDefinitionNode)
|
275
278
|
):
|
276
279
|
return max(
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
280
|
+
map(
|
281
|
+
lambda selection: determine_depth(
|
282
|
+
node=selection,
|
283
|
+
fragments=fragments,
|
284
|
+
depth_so_far=depth_so_far,
|
285
|
+
max_depth=max_depth,
|
286
|
+
context=context,
|
287
|
+
operation_name=operation_name,
|
288
|
+
should_ignore=should_ignore,
|
289
|
+
),
|
290
|
+
node.selection_set.selections,
|
285
291
|
)
|
286
|
-
for selection in node.selection_set.selections
|
287
292
|
)
|
288
|
-
|
293
|
+
else:
|
294
|
+
raise TypeError(f"Depth crawler cannot handle: {node.kind}") # pragma: no cover
|
289
295
|
|
290
296
|
|
291
297
|
def is_ignored(node: FieldNode, ignore: Optional[list[IgnoreType]] = None) -> bool:
|
@@ -67,7 +67,7 @@ class DatadogTracingExtension(SchemaExtension):
|
|
67
67
|
)
|
68
68
|
|
69
69
|
def hash_query(self, query: str) -> str:
|
70
|
-
return hashlib.md5(query.encode("utf-8")).hexdigest()
|
70
|
+
return hashlib.md5(query.encode("utf-8")).hexdigest()
|
71
71
|
|
72
72
|
def on_operation(self) -> Iterator[None]:
|
73
73
|
self._operation_name = self.execution_context.operation_name
|
@@ -45,7 +45,7 @@ class OpenTelemetryExtension(SchemaExtension):
|
|
45
45
|
) -> None:
|
46
46
|
self._arg_filter = arg_filter
|
47
47
|
self._tracer = trace.get_tracer("strawberry")
|
48
|
-
self._span_holder =
|
48
|
+
self._span_holder = dict()
|
49
49
|
if execution_context:
|
50
50
|
self.execution_context = execution_context
|
51
51
|
|
@@ -116,17 +116,18 @@ class OpenTelemetryExtension(SchemaExtension):
|
|
116
116
|
# Put these in decreasing order of use-cases to exit as soon as possible
|
117
117
|
if isinstance(value, (bool, str, bytes, int, float)):
|
118
118
|
return value
|
119
|
-
|
119
|
+
elif isinstance(value, (list, tuple, range)):
|
120
120
|
return self.convert_list_or_tuple_to_allowed_types(value)
|
121
|
-
|
121
|
+
elif isinstance(value, dict):
|
122
122
|
return self.convert_dict_to_allowed_types(value)
|
123
|
-
|
123
|
+
elif isinstance(value, (set, frozenset)):
|
124
124
|
return self.convert_set_to_allowed_types(value)
|
125
|
-
|
125
|
+
elif isinstance(value, complex):
|
126
126
|
return str(value) # Convert complex numbers to strings
|
127
|
-
|
127
|
+
elif isinstance(value, (bytearray, memoryview)):
|
128
128
|
return bytes(value) # Convert bytearray and memoryview to bytes
|
129
|
-
|
129
|
+
else:
|
130
|
+
return str(value)
|
130
131
|
|
131
132
|
def convert_set_to_allowed_types(self, value: Union[set, frozenset]) -> str:
|
132
133
|
return (
|
@@ -191,7 +192,9 @@ class OpenTelemetryExtensionSync(OpenTelemetryExtension):
|
|
191
192
|
**kwargs: Any,
|
192
193
|
) -> Any:
|
193
194
|
if should_skip_tracing(_next, info):
|
194
|
-
|
195
|
+
result = _next(root, info, *args, **kwargs)
|
196
|
+
|
197
|
+
return result
|
195
198
|
|
196
199
|
with self._tracer.start_as_current_span(
|
197
200
|
f"GraphQL Resolving: {info.field_name}",
|
@@ -200,7 +203,9 @@ class OpenTelemetryExtensionSync(OpenTelemetryExtension):
|
|
200
203
|
),
|
201
204
|
) as span:
|
202
205
|
self.add_tags(span, info, kwargs)
|
203
|
-
|
206
|
+
result = _next(root, info, *args, **kwargs)
|
207
|
+
|
208
|
+
return result
|
204
209
|
|
205
210
|
|
206
211
|
__all__ = ["OpenTelemetryExtension", "OpenTelemetryExtensionSync"]
|
strawberry/fastapi/router.py
CHANGED
@@ -92,9 +92,10 @@ class GraphQLRouter(
|
|
92
92
|
**default_context,
|
93
93
|
**custom_context,
|
94
94
|
}
|
95
|
-
|
95
|
+
elif custom_context is None:
|
96
96
|
return default_context
|
97
|
-
|
97
|
+
else:
|
98
|
+
raise InvalidCustomContext()
|
98
99
|
|
99
100
|
# replace the signature parameters of dependency...
|
100
101
|
# ...with the old parameters minus the first argument as it will be replaced...
|
strawberry/federation/schema.py
CHANGED
@@ -172,7 +172,7 @@ class Schema(BaseSchema):
|
|
172
172
|
|
173
173
|
try:
|
174
174
|
result = resolve_reference(**kwargs)
|
175
|
-
except Exception as e:
|
175
|
+
except Exception as e:
|
176
176
|
result = e
|
177
177
|
else:
|
178
178
|
from strawberry.types.arguments import convert_argument
|
@@ -187,7 +187,7 @@ class Schema(BaseSchema):
|
|
187
187
|
scalar_registry=scalar_registry,
|
188
188
|
config=config,
|
189
189
|
)
|
190
|
-
except Exception:
|
190
|
+
except Exception:
|
191
191
|
result = TypeError(f"Unable to resolve reference for {type_name}")
|
192
192
|
|
193
193
|
results.append(result)
|
@@ -271,7 +271,7 @@ class Schema(BaseSchema):
|
|
271
271
|
link_directives: list[object] = [
|
272
272
|
Link(
|
273
273
|
url=url,
|
274
|
-
import_=sorted(directives),
|
274
|
+
import_=list(sorted(directives)),
|
275
275
|
)
|
276
276
|
for url, directives in directive_by_url.items()
|
277
277
|
]
|
strawberry/flask/views.py
CHANGED
@@ -4,7 +4,6 @@ import warnings
|
|
4
4
|
from typing import (
|
5
5
|
TYPE_CHECKING,
|
6
6
|
Any,
|
7
|
-
ClassVar,
|
8
7
|
Optional,
|
9
8
|
Union,
|
10
9
|
cast,
|
@@ -103,7 +102,7 @@ class GraphQLView(
|
|
103
102
|
SyncBaseHTTPView[Request, Response, Response, Context, RootValue],
|
104
103
|
View,
|
105
104
|
):
|
106
|
-
methods
|
105
|
+
methods = ["GET", "POST"]
|
107
106
|
allow_queries_via_get: bool = True
|
108
107
|
request_adapter_class = FlaskHTTPRequestAdapter
|
109
108
|
|
@@ -166,7 +165,7 @@ class AsyncGraphQLView(
|
|
166
165
|
],
|
167
166
|
View,
|
168
167
|
):
|
169
|
-
methods
|
168
|
+
methods = ["GET", "POST"]
|
170
169
|
allow_queries_via_get: bool = True
|
171
170
|
request_adapter_class = AsyncFlaskHTTPRequestAdapter
|
172
171
|
|
@@ -306,7 +306,8 @@ class AsyncBaseHTTPView(
|
|
306
306
|
await websocket.close(4406, "Subprotocol not acceptable")
|
307
307
|
|
308
308
|
return websocket_response
|
309
|
-
|
309
|
+
else:
|
310
|
+
request = cast(Request, request)
|
310
311
|
|
311
312
|
request_adapter = self.request_adapter_class(request)
|
312
313
|
sub_response = await self.get_sub_response(request)
|
@@ -324,7 +325,8 @@ class AsyncBaseHTTPView(
|
|
324
325
|
if self.should_render_graphql_ide(request_adapter):
|
325
326
|
if self.graphql_ide:
|
326
327
|
return await self.render_graphql_ide(request)
|
327
|
-
|
328
|
+
else:
|
329
|
+
raise HTTPException(404, "Not Found")
|
328
330
|
|
329
331
|
try:
|
330
332
|
result = await self.execute_operation(
|
strawberry/http/ides.py
CHANGED
@@ -175,7 +175,8 @@ class SyncBaseHTTPView(
|
|
175
175
|
if self.should_render_graphql_ide(request_adapter):
|
176
176
|
if self.graphql_ide:
|
177
177
|
return self.render_graphql_ide(request)
|
178
|
-
|
178
|
+
else:
|
179
|
+
raise HTTPException(404, "Not Found")
|
179
180
|
|
180
181
|
sub_response = self.get_sub_response(request)
|
181
182
|
context = (
|
@@ -9,7 +9,6 @@ from typing import (
|
|
9
9
|
TYPE_CHECKING,
|
10
10
|
Any,
|
11
11
|
Callable,
|
12
|
-
ClassVar,
|
13
12
|
Optional,
|
14
13
|
TypedDict,
|
15
14
|
Union,
|
@@ -204,13 +203,13 @@ class LitestarWebSocketAdapter(AsyncWebSocketAdapter):
|
|
204
203
|
|
205
204
|
# Litestar internally defaults to an empty string for non-text messages
|
206
205
|
if text == "":
|
207
|
-
raise NonTextMessageReceived
|
206
|
+
raise NonTextMessageReceived()
|
208
207
|
|
209
208
|
try:
|
210
209
|
yield self.view.decode_json(text)
|
211
|
-
except json.JSONDecodeError
|
210
|
+
except json.JSONDecodeError:
|
212
211
|
if not ignore_parsing_errors:
|
213
|
-
raise NonJsonMessageReceived
|
212
|
+
raise NonJsonMessageReceived()
|
214
213
|
except WebSocketDisconnect:
|
215
214
|
pass
|
216
215
|
|
@@ -237,7 +236,7 @@ class GraphQLController(
|
|
237
236
|
],
|
238
237
|
):
|
239
238
|
path: str = ""
|
240
|
-
dependencies:
|
239
|
+
dependencies: Dependencies = {
|
241
240
|
"custom_context": Provide(_none_custom_context_getter),
|
242
241
|
"context": Provide(_context_getter_http),
|
243
242
|
"context_ws": Provide(_context_getter_ws),
|
@@ -446,7 +445,7 @@ def make_graphql_controller(
|
|
446
445
|
|
447
446
|
class _GraphQLController(GraphQLController):
|
448
447
|
path: str = routes_path
|
449
|
-
dependencies:
|
448
|
+
dependencies: Dependencies = {
|
450
449
|
"custom_context": Provide(custom_context_getter_),
|
451
450
|
"context": Provide(_context_getter_http),
|
452
451
|
"context_ws": Provide(_context_getter_ws),
|
strawberry/permission.py
CHANGED
@@ -101,7 +101,7 @@ class BasePermission(abc.ABC):
|
|
101
101
|
if self.error_extensions:
|
102
102
|
# Add our extensions to the error
|
103
103
|
if not error.extensions:
|
104
|
-
error.extensions =
|
104
|
+
error.extensions = dict()
|
105
105
|
error.extensions.update(self.error_extensions)
|
106
106
|
|
107
107
|
raise error
|
strawberry/quart/views.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import warnings
|
2
2
|
from collections.abc import AsyncGenerator, Mapping
|
3
|
-
from typing import TYPE_CHECKING, Callable,
|
3
|
+
from typing import TYPE_CHECKING, Callable, Optional, cast
|
4
4
|
from typing_extensions import TypeGuard
|
5
5
|
|
6
6
|
from quart import Request, Response, request
|
@@ -52,7 +52,7 @@ class GraphQLView(
|
|
52
52
|
],
|
53
53
|
View,
|
54
54
|
):
|
55
|
-
methods
|
55
|
+
methods = ["GET", "POST"]
|
56
56
|
allow_queries_via_get: bool = True
|
57
57
|
request_adapter_class = QuartHTTPRequestAdapter
|
58
58
|
|
strawberry/relay/fields.py
CHANGED
@@ -37,7 +37,6 @@ from strawberry.relay.exceptions import (
|
|
37
37
|
)
|
38
38
|
from strawberry.types.arguments import StrawberryArgument, argument
|
39
39
|
from strawberry.types.base import StrawberryList, StrawberryOptional
|
40
|
-
from strawberry.types.cast import cast as strawberry_cast
|
41
40
|
from strawberry.types.field import _RESOLVER_TYPE, StrawberryField, field
|
42
41
|
from strawberry.types.fields.resolver import StrawberryResolver
|
43
42
|
from strawberry.types.lazy_type import LazyType
|
@@ -89,27 +88,12 @@ class NodeExtension(FieldExtension):
|
|
89
88
|
info: Info,
|
90
89
|
id: Annotated[GlobalID, argument(description="The ID of the object.")],
|
91
90
|
) -> Union[Node, None, Awaitable[Union[Node, None]]]:
|
92
|
-
|
93
|
-
resolved_node = node_type.resolve_node(
|
91
|
+
return id.resolve_type(info).resolve_node(
|
94
92
|
id.node_id,
|
95
93
|
info=info,
|
96
94
|
required=not is_optional,
|
97
95
|
)
|
98
96
|
|
99
|
-
# We are using `strawberry_cast` here to cast the resolved node to make
|
100
|
-
# sure `is_type_of` will not try to find its type again. Very important
|
101
|
-
# when returning a non type (e.g. Django/SQLAlchemy/Pydantic model), as
|
102
|
-
# we could end up resolving to a different type in case more than one
|
103
|
-
# are registered.
|
104
|
-
if inspect.isawaitable(resolved_node):
|
105
|
-
|
106
|
-
async def resolve() -> Any:
|
107
|
-
return strawberry_cast(node_type, await resolved_node)
|
108
|
-
|
109
|
-
return resolve()
|
110
|
-
|
111
|
-
return cast(Node, strawberry_cast(node_type, resolved_node))
|
112
|
-
|
113
97
|
return resolver
|
114
98
|
|
115
99
|
def get_node_list_resolver(
|
@@ -155,14 +139,6 @@ class NodeExtension(FieldExtension):
|
|
155
139
|
if inspect.isasyncgen(nodes)
|
156
140
|
}
|
157
141
|
|
158
|
-
# We are using `strawberry_cast` here to cast the resolved node to make
|
159
|
-
# sure `is_type_of` will not try to find its type again. Very important
|
160
|
-
# when returning a non type (e.g. Django/SQLAlchemy/Pydantic model), as
|
161
|
-
# we could end up resolving to a different type in case more than one
|
162
|
-
# are registered
|
163
|
-
def cast_nodes(node_t: type[Node], nodes: Iterable[Any]) -> list[Node]:
|
164
|
-
return [cast(Node, strawberry_cast(node_t, node)) for node in nodes]
|
165
|
-
|
166
142
|
if awaitable_nodes or asyncgen_nodes:
|
167
143
|
|
168
144
|
async def resolve(resolved: Any = resolved_nodes) -> list[Node]:
|
@@ -185,8 +161,7 @@ class NodeExtension(FieldExtension):
|
|
185
161
|
|
186
162
|
# Resolve any generator to lists
|
187
163
|
resolved = {
|
188
|
-
node_t:
|
189
|
-
for node_t, nodes in resolved.items()
|
164
|
+
node_t: list(nodes) for node_t, nodes in resolved.items()
|
190
165
|
}
|
191
166
|
return [
|
192
167
|
resolved[index_map[gid][0]][index_map[gid][1]] for gid in ids
|
@@ -196,7 +171,7 @@ class NodeExtension(FieldExtension):
|
|
196
171
|
|
197
172
|
# Resolve any generator to lists
|
198
173
|
resolved = {
|
199
|
-
node_t:
|
174
|
+
node_t: list(cast(Iterator[Node], nodes))
|
200
175
|
for node_t, nodes in resolved_nodes.items()
|
201
176
|
}
|
202
177
|
return [resolved[index_map[gid][0]][index_map[gid][1]] for gid in ids]
|