strawberry-graphql 0.275.7__py3-none-any.whl → 0.284.3__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.

Potentially problematic release.


This version of strawberry-graphql might be problematic. Click here for more details.

Files changed (161) hide show
  1. strawberry/__init__.py +2 -0
  2. strawberry/aiohttp/test/client.py +8 -15
  3. strawberry/aiohttp/views.py +15 -64
  4. strawberry/annotation.py +70 -25
  5. strawberry/asgi/__init__.py +22 -56
  6. strawberry/asgi/test/client.py +6 -6
  7. strawberry/chalice/views.py +13 -79
  8. strawberry/channels/handlers/base.py +7 -8
  9. strawberry/channels/handlers/http_handler.py +50 -32
  10. strawberry/channels/handlers/ws_handler.py +12 -14
  11. strawberry/channels/router.py +3 -4
  12. strawberry/channels/testing.py +7 -9
  13. strawberry/cli/__init__.py +7 -6
  14. strawberry/cli/commands/codegen.py +7 -7
  15. strawberry/cli/commands/dev.py +72 -0
  16. strawberry/cli/commands/schema_codegen.py +1 -2
  17. strawberry/cli/commands/server.py +3 -44
  18. strawberry/cli/commands/upgrade/__init__.py +3 -3
  19. strawberry/cli/commands/upgrade/_run_codemod.py +2 -2
  20. strawberry/cli/constants.py +1 -2
  21. strawberry/cli/{debug_server.py → dev_server.py} +3 -7
  22. strawberry/codegen/plugins/print_operation.py +2 -2
  23. strawberry/codegen/plugins/python.py +2 -2
  24. strawberry/codegen/query_codegen.py +20 -30
  25. strawberry/codegen/types.py +32 -32
  26. strawberry/codemods/__init__.py +9 -0
  27. strawberry/codemods/annotated_unions.py +2 -2
  28. strawberry/codemods/maybe_optional.py +118 -0
  29. strawberry/dataloader.py +28 -24
  30. strawberry/directive.py +6 -7
  31. strawberry/django/test/client.py +3 -3
  32. strawberry/django/views.py +21 -91
  33. strawberry/exceptions/__init__.py +4 -4
  34. strawberry/exceptions/conflicting_arguments.py +2 -2
  35. strawberry/exceptions/duplicated_type_name.py +4 -4
  36. strawberry/exceptions/exception.py +3 -3
  37. strawberry/exceptions/handler.py +8 -7
  38. strawberry/exceptions/invalid_argument_type.py +2 -2
  39. strawberry/exceptions/invalid_superclass_interface.py +2 -2
  40. strawberry/exceptions/invalid_union_type.py +4 -4
  41. strawberry/exceptions/missing_arguments_annotations.py +2 -2
  42. strawberry/exceptions/missing_dependencies.py +2 -4
  43. strawberry/exceptions/missing_field_annotation.py +2 -2
  44. strawberry/exceptions/missing_return_annotation.py +2 -2
  45. strawberry/exceptions/object_is_not_a_class.py +2 -2
  46. strawberry/exceptions/object_is_not_an_enum.py +2 -2
  47. strawberry/exceptions/permission_fail_silently_requires_optional.py +2 -2
  48. strawberry/exceptions/private_strawberry_field.py +2 -2
  49. strawberry/exceptions/scalar_already_registered.py +2 -2
  50. strawberry/exceptions/syntax.py +3 -3
  51. strawberry/exceptions/unresolved_field_type.py +2 -2
  52. strawberry/exceptions/utils/source_finder.py +25 -25
  53. strawberry/experimental/pydantic/_compat.py +8 -7
  54. strawberry/experimental/pydantic/conversion.py +2 -2
  55. strawberry/experimental/pydantic/conversion_types.py +2 -2
  56. strawberry/experimental/pydantic/error_type.py +10 -12
  57. strawberry/experimental/pydantic/fields.py +9 -15
  58. strawberry/experimental/pydantic/object_type.py +17 -25
  59. strawberry/experimental/pydantic/utils.py +1 -2
  60. strawberry/ext/mypy_plugin.py +12 -14
  61. strawberry/extensions/base_extension.py +2 -1
  62. strawberry/extensions/context.py +13 -18
  63. strawberry/extensions/directives.py +9 -3
  64. strawberry/extensions/field_extension.py +4 -4
  65. strawberry/extensions/mask_errors.py +24 -13
  66. strawberry/extensions/max_aliases.py +1 -3
  67. strawberry/extensions/parser_cache.py +1 -2
  68. strawberry/extensions/query_depth_limiter.py +18 -14
  69. strawberry/extensions/runner.py +2 -2
  70. strawberry/extensions/tracing/apollo.py +3 -3
  71. strawberry/extensions/tracing/datadog.py +3 -3
  72. strawberry/extensions/tracing/opentelemetry.py +6 -8
  73. strawberry/extensions/tracing/utils.py +3 -1
  74. strawberry/extensions/utils.py +2 -2
  75. strawberry/extensions/validation_cache.py +2 -3
  76. strawberry/fastapi/context.py +6 -6
  77. strawberry/fastapi/router.py +43 -42
  78. strawberry/federation/argument.py +4 -5
  79. strawberry/federation/enum.py +18 -21
  80. strawberry/federation/field.py +94 -97
  81. strawberry/federation/object_type.py +56 -58
  82. strawberry/federation/scalar.py +27 -35
  83. strawberry/federation/schema.py +15 -16
  84. strawberry/federation/schema_directive.py +7 -6
  85. strawberry/federation/schema_directives.py +11 -11
  86. strawberry/federation/union.py +4 -4
  87. strawberry/flask/views.py +16 -85
  88. strawberry/http/__init__.py +30 -20
  89. strawberry/http/async_base_view.py +208 -89
  90. strawberry/http/base.py +28 -11
  91. strawberry/http/exceptions.py +5 -7
  92. strawberry/http/ides.py +2 -3
  93. strawberry/http/sync_base_view.py +115 -69
  94. strawberry/http/types.py +3 -3
  95. strawberry/litestar/controller.py +43 -77
  96. strawberry/permission.py +4 -6
  97. strawberry/printer/ast_from_value.py +3 -5
  98. strawberry/printer/printer.py +18 -15
  99. strawberry/quart/views.py +16 -48
  100. strawberry/relay/exceptions.py +4 -4
  101. strawberry/relay/fields.py +33 -32
  102. strawberry/relay/types.py +32 -35
  103. strawberry/relay/utils.py +11 -23
  104. strawberry/resolvers.py +2 -1
  105. strawberry/sanic/context.py +1 -0
  106. strawberry/sanic/utils.py +3 -3
  107. strawberry/sanic/views.py +15 -54
  108. strawberry/scalars.py +2 -2
  109. strawberry/schema/_graphql_core.py +55 -0
  110. strawberry/schema/base.py +32 -33
  111. strawberry/schema/compat.py +9 -9
  112. strawberry/schema/config.py +10 -1
  113. strawberry/schema/exceptions.py +1 -3
  114. strawberry/schema/name_converter.py +9 -8
  115. strawberry/schema/schema.py +133 -100
  116. strawberry/schema/schema_converter.py +96 -58
  117. strawberry/schema/types/base_scalars.py +1 -1
  118. strawberry/schema/types/concrete_type.py +5 -5
  119. strawberry/schema/validation_rules/maybe_null.py +136 -0
  120. strawberry/schema_codegen/__init__.py +3 -3
  121. strawberry/schema_directive.py +7 -6
  122. strawberry/static/graphiql.html +5 -5
  123. strawberry/streamable.py +35 -0
  124. strawberry/subscriptions/protocols/graphql_transport_ws/handlers.py +5 -16
  125. strawberry/subscriptions/protocols/graphql_transport_ws/types.py +20 -20
  126. strawberry/subscriptions/protocols/graphql_ws/handlers.py +5 -12
  127. strawberry/subscriptions/protocols/graphql_ws/types.py +14 -14
  128. strawberry/test/client.py +18 -18
  129. strawberry/tools/create_type.py +2 -3
  130. strawberry/types/arguments.py +41 -28
  131. strawberry/types/auto.py +3 -4
  132. strawberry/types/base.py +25 -27
  133. strawberry/types/enum.py +22 -25
  134. strawberry/types/execution.py +21 -16
  135. strawberry/types/field.py +109 -130
  136. strawberry/types/fields/resolver.py +19 -21
  137. strawberry/types/info.py +5 -11
  138. strawberry/types/lazy_type.py +2 -3
  139. strawberry/types/maybe.py +12 -3
  140. strawberry/types/mutation.py +115 -118
  141. strawberry/types/nodes.py +2 -2
  142. strawberry/types/object_type.py +43 -63
  143. strawberry/types/scalar.py +37 -43
  144. strawberry/types/union.py +12 -14
  145. strawberry/utils/aio.py +12 -9
  146. strawberry/utils/await_maybe.py +3 -3
  147. strawberry/utils/deprecations.py +2 -2
  148. strawberry/utils/importer.py +1 -2
  149. strawberry/utils/inspect.py +4 -6
  150. strawberry/utils/logging.py +2 -2
  151. strawberry/utils/operation.py +4 -4
  152. strawberry/utils/typing.py +18 -83
  153. {strawberry_graphql-0.275.7.dist-info → strawberry_graphql-0.284.3.dist-info}/METADATA +14 -8
  154. strawberry_graphql-0.284.3.dist-info/RECORD +243 -0
  155. {strawberry_graphql-0.275.7.dist-info → strawberry_graphql-0.284.3.dist-info}/WHEEL +1 -1
  156. strawberry/utils/dataclasses.py +0 -37
  157. strawberry/utils/debug.py +0 -46
  158. strawberry/utils/graphql_lexer.py +0 -35
  159. strawberry_graphql-0.275.7.dist-info/RECORD +0 -241
  160. {strawberry_graphql-0.275.7.dist-info → strawberry_graphql-0.284.3.dist-info}/entry_points.txt +0 -0
  161. {strawberry_graphql-0.275.7.dist-info → strawberry_graphql-0.284.3.dist-info/licenses}/LICENSE +0 -0
@@ -1,15 +1,13 @@
1
1
  import abc
2
2
  import json
3
- from collections.abc import Mapping
3
+ from collections.abc import Callable
4
4
  from typing import (
5
- Any,
6
- Callable,
7
5
  Generic,
8
- Optional,
9
- Union,
6
+ Literal,
10
7
  )
11
8
 
12
9
  from graphql import GraphQLError
10
+ from lia import HTTPException, SyncHTTPRequestAdapter
13
11
 
14
12
  from strawberry.exceptions import MissingQueryError
15
13
  from strawberry.file_uploads.utils import replace_placeholders_with_files
@@ -29,50 +27,18 @@ from strawberry.types.graphql import OperationType
29
27
  from strawberry.types.unset import UNSET
30
28
 
31
29
  from .base import BaseView
32
- from .exceptions import HTTPException
33
30
  from .parse_content_type import parse_content_type
34
- from .types import HTTPMethod, QueryParams
35
31
  from .typevars import Context, Request, Response, RootValue, SubResponse
36
32
 
37
33
 
38
- class SyncHTTPRequestAdapter(abc.ABC):
39
- @property
40
- @abc.abstractmethod
41
- def query_params(self) -> QueryParams: ...
42
-
43
- @property
44
- @abc.abstractmethod
45
- def body(self) -> Union[str, bytes]: ...
46
-
47
- @property
48
- @abc.abstractmethod
49
- def method(self) -> HTTPMethod: ...
50
-
51
- @property
52
- @abc.abstractmethod
53
- def headers(self) -> Mapping[str, str]: ...
54
-
55
- @property
56
- @abc.abstractmethod
57
- def content_type(self) -> Optional[str]: ...
58
-
59
- @property
60
- @abc.abstractmethod
61
- def post_data(self) -> Mapping[str, Union[str, bytes]]: ...
62
-
63
- @property
64
- @abc.abstractmethod
65
- def files(self) -> Mapping[str, Any]: ...
66
-
67
-
68
34
  class SyncBaseHTTPView(
69
35
  abc.ABC,
70
36
  BaseView[Request],
71
37
  Generic[Request, Response, SubResponse, Context, RootValue],
72
38
  ):
73
39
  schema: BaseSchema
74
- graphiql: Optional[bool]
75
- graphql_ide: Optional[GraphQL_IDE]
40
+ graphiql: bool | None
41
+ graphql_ide: GraphQL_IDE | None
76
42
  request_adapter_class: Callable[[Request], SyncHTTPRequestAdapter]
77
43
 
78
44
  # Methods that need to be implemented by individual frameworks
@@ -88,19 +54,25 @@ class SyncBaseHTTPView(
88
54
  def get_context(self, request: Request, response: SubResponse) -> Context: ...
89
55
 
90
56
  @abc.abstractmethod
91
- def get_root_value(self, request: Request) -> Optional[RootValue]: ...
57
+ def get_root_value(self, request: Request) -> RootValue | None: ...
92
58
 
93
59
  @abc.abstractmethod
94
60
  def create_response(
95
- self, response_data: GraphQLHTTPResponse, sub_response: SubResponse
61
+ self,
62
+ response_data: GraphQLHTTPResponse | list[GraphQLHTTPResponse],
63
+ sub_response: SubResponse,
96
64
  ) -> Response: ...
97
65
 
98
66
  @abc.abstractmethod
99
67
  def render_graphql_ide(self, request: Request) -> Response: ...
100
68
 
101
69
  def execute_operation(
102
- self, request: Request, context: Context, root_value: Optional[RootValue]
103
- ) -> ExecutionResult:
70
+ self,
71
+ request: Request,
72
+ context: Context,
73
+ root_value: RootValue | None,
74
+ sub_response: SubResponse,
75
+ ) -> ExecutionResult | list[ExecutionResult]:
104
76
  request_adapter = self.request_adapter_class(request)
105
77
 
106
78
  try:
@@ -116,16 +88,64 @@ class SyncBaseHTTPView(
116
88
  if not self.allow_queries_via_get and request_adapter.method == "GET":
117
89
  allowed_operation_types = allowed_operation_types - {OperationType.QUERY}
118
90
 
119
- return self.schema.execute_sync(
120
- request_data.query,
91
+ if isinstance(request_data, list):
92
+ # batch GraphQL requests
93
+ return [
94
+ self.execute_single(
95
+ request=request,
96
+ request_adapter=request_adapter,
97
+ sub_response=sub_response,
98
+ context=context,
99
+ root_value=root_value,
100
+ request_data=data,
101
+ )
102
+ for data in request_data
103
+ ]
104
+
105
+ return self.execute_single(
106
+ request=request,
107
+ request_adapter=request_adapter,
108
+ sub_response=sub_response,
109
+ context=context,
121
110
  root_value=root_value,
122
- variable_values=request_data.variables,
123
- context_value=context,
124
- operation_name=request_data.operation_name,
125
- allowed_operation_types=allowed_operation_types,
126
- operation_extensions=request_data.extensions,
111
+ request_data=request_data,
127
112
  )
128
113
 
114
+ def execute_single(
115
+ self,
116
+ request: Request,
117
+ request_adapter: SyncHTTPRequestAdapter,
118
+ sub_response: SubResponse,
119
+ context: Context,
120
+ root_value: RootValue | None,
121
+ request_data: GraphQLRequestData,
122
+ ) -> ExecutionResult:
123
+ allowed_operation_types = OperationType.from_http(request_adapter.method)
124
+
125
+ if not self.allow_queries_via_get and request_adapter.method == "GET":
126
+ allowed_operation_types = allowed_operation_types - {OperationType.QUERY}
127
+
128
+ try:
129
+ result = self.schema.execute_sync(
130
+ request_data.query,
131
+ root_value=root_value,
132
+ variable_values=request_data.variables,
133
+ context_value=context,
134
+ operation_name=request_data.operation_name,
135
+ allowed_operation_types=allowed_operation_types,
136
+ operation_extensions=request_data.extensions,
137
+ )
138
+ except CannotGetOperationTypeError as e:
139
+ raise HTTPException(400, e.as_http_error_reason()) from e
140
+ except InvalidOperationTypeError as e:
141
+ raise HTTPException(
142
+ 400, e.as_http_error_reason(request_adapter.method)
143
+ ) from e
144
+ except MissingQueryError as e:
145
+ raise HTTPException(400, "No GraphQL query found in the request") from e
146
+
147
+ return result
148
+
129
149
  def parse_multipart(self, request: SyncHTTPRequestAdapter) -> dict[str, str]:
130
150
  operations = self.parse_json(request.post_data.get("operations", "{}"))
131
151
  files_map = self.parse_json(request.post_data.get("map", "{}"))
@@ -135,8 +155,18 @@ class SyncBaseHTTPView(
135
155
  except KeyError as e:
136
156
  raise HTTPException(400, "File(s) missing in form data") from e
137
157
 
138
- def parse_http_body(self, request: SyncHTTPRequestAdapter) -> GraphQLRequestData:
158
+ def parse_http_body(
159
+ self, request: SyncHTTPRequestAdapter
160
+ ) -> GraphQLRequestData | list[GraphQLRequestData]:
161
+ headers = {key.lower(): value for key, value in request.headers.items()}
139
162
  content_type, params = parse_content_type(request.content_type or "")
163
+ accept = headers.get("accept", "")
164
+
165
+ protocol: Literal["http", "multipart-subscription"] = (
166
+ "multipart-subscription"
167
+ if self._is_multipart_subscriptions(*parse_content_type(accept))
168
+ else "http"
169
+ )
140
170
 
141
171
  if request.method == "GET":
142
172
  data = self.parse_query_params(request.query_params)
@@ -152,6 +182,18 @@ class SyncBaseHTTPView(
152
182
  else:
153
183
  raise HTTPException(400, "Unsupported content type")
154
184
 
185
+ if isinstance(data, list):
186
+ self._validate_batch_request(data, protocol=protocol)
187
+ return [
188
+ GraphQLRequestData(
189
+ query=item.get("query"),
190
+ variables=item.get("variables"),
191
+ operation_name=item.get("operationName"),
192
+ extensions=item.get("extensions"),
193
+ )
194
+ for item in data
195
+ ]
196
+
155
197
  query = data.get("query")
156
198
  if not isinstance(query, (str, type(None))):
157
199
  raise HTTPException(
@@ -189,7 +231,7 @@ class SyncBaseHTTPView(
189
231
  self,
190
232
  request: Request,
191
233
  context: Context = UNSET,
192
- root_value: Optional[RootValue] = UNSET,
234
+ root_value: RootValue | None = UNSET,
193
235
  ) -> Response:
194
236
  request_adapter = self.request_adapter_class(request)
195
237
 
@@ -209,25 +251,29 @@ class SyncBaseHTTPView(
209
251
  )
210
252
  root_value = self.get_root_value(request) if root_value is UNSET else root_value
211
253
 
212
- try:
213
- result = self.execute_operation(
214
- request=request,
215
- context=context,
216
- root_value=root_value,
217
- )
218
- except CannotGetOperationTypeError as e:
219
- raise HTTPException(400, e.as_http_error_reason()) from e
220
- except InvalidOperationTypeError as e:
221
- raise HTTPException(
222
- 400, e.as_http_error_reason(request_adapter.method)
223
- ) from e
224
- except MissingQueryError as e:
225
- raise HTTPException(400, "No GraphQL query found in the request") from e
254
+ result = self.execute_operation(
255
+ request=request,
256
+ context=context,
257
+ root_value=root_value,
258
+ sub_response=sub_response,
259
+ )
226
260
 
227
- response_data = self.process_result(request=request, result=result)
261
+ response_data: GraphQLHTTPResponse | list[GraphQLHTTPResponse]
262
+
263
+ if isinstance(result, list):
264
+ response_data = []
265
+ for execution_result in result:
266
+ processed_result = self.process_result(
267
+ request=request, result=execution_result
268
+ )
269
+ if execution_result.errors:
270
+ self._handle_errors(execution_result.errors, processed_result)
271
+ response_data.append(processed_result)
272
+ else:
273
+ response_data = self.process_result(request=request, result=result)
228
274
 
229
- if result.errors:
230
- self._handle_errors(result.errors, response_data)
275
+ if result.errors:
276
+ self._handle_errors(result.errors, response_data)
231
277
 
232
278
  return self.create_response(
233
279
  response_data=response_data, sub_response=sub_response
strawberry/http/types.py CHANGED
@@ -1,12 +1,12 @@
1
1
  from collections.abc import Mapping
2
- from typing import Any, Optional
3
- from typing_extensions import Literal, TypedDict
2
+ from typing import Any, Literal
3
+ from typing_extensions import TypedDict
4
4
 
5
5
  HTTPMethod = Literal[
6
6
  "GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS", "TRACE"
7
7
  ]
8
8
 
9
- QueryParams = Mapping[str, Optional[str]]
9
+ QueryParams = Mapping[str, str | None]
10
10
 
11
11
 
12
12
  class FormData(TypedDict):
@@ -8,17 +8,13 @@ from datetime import timedelta
8
8
  from typing import (
9
9
  TYPE_CHECKING,
10
10
  Any,
11
- Callable,
12
11
  ClassVar,
13
- Optional,
12
+ TypeAlias,
14
13
  TypedDict,
15
- Union,
16
- cast,
14
+ TypeGuard,
17
15
  )
18
- from typing_extensions import TypeGuard
19
-
20
- from msgspec import Struct
21
16
 
17
+ from lia import HTTPException, LitestarRequestAdapter
22
18
  from litestar import (
23
19
  Controller,
24
20
  MediaType,
@@ -38,35 +34,41 @@ from litestar.exceptions import (
38
34
  )
39
35
  from litestar.response.streaming import Stream
40
36
  from litestar.status_codes import HTTP_200_OK
37
+ from msgspec import Struct
38
+
41
39
  from strawberry.exceptions import InvalidCustomContext
42
40
  from strawberry.http.async_base_view import (
43
41
  AsyncBaseHTTPView,
44
- AsyncHTTPRequestAdapter,
45
42
  AsyncWebSocketAdapter,
46
43
  )
47
44
  from strawberry.http.exceptions import (
48
- HTTPException,
49
45
  NonJsonMessageReceived,
50
46
  NonTextMessageReceived,
51
47
  WebSocketDisconnected,
52
48
  )
53
- from strawberry.http.types import FormData, HTTPMethod, QueryParams
54
49
  from strawberry.http.typevars import Context, RootValue
55
50
  from strawberry.subscriptions import GRAPHQL_TRANSPORT_WS_PROTOCOL, GRAPHQL_WS_PROTOCOL
56
51
 
57
52
  if TYPE_CHECKING:
58
- from collections.abc import AsyncGenerator, AsyncIterator, Mapping, Sequence
53
+ from collections.abc import (
54
+ AsyncGenerator,
55
+ AsyncIterator,
56
+ Callable,
57
+ Mapping,
58
+ Sequence,
59
+ )
59
60
 
60
61
  from litestar.types import AnyCallable, Dependencies
62
+
61
63
  from strawberry.http import GraphQLHTTPResponse
62
64
  from strawberry.http.ides import GraphQL_IDE
63
65
  from strawberry.schema import BaseSchema
64
66
 
65
67
 
66
68
  class BaseContext(Struct, kw_only=True):
67
- request: Optional[Request] = None
68
- websocket: Optional[WebSocket] = None
69
- response: Optional[Response] = None
69
+ request: Request | None = None
70
+ websocket: WebSocket | None = None
71
+ response: Response | None = None
70
72
 
71
73
 
72
74
  class HTTPContextType:
@@ -91,9 +93,9 @@ class WebSocketContextDict(TypedDict):
91
93
  socket: WebSocket
92
94
 
93
95
 
94
- MergedContext = Union[
95
- BaseContext, WebSocketContextDict, HTTPContextDict, dict[str, Any]
96
- ]
96
+ MergedContext: TypeAlias = (
97
+ BaseContext | WebSocketContextDict | HTTPContextDict | dict[str, Any]
98
+ )
97
99
 
98
100
 
99
101
  async def _none_custom_context_getter() -> None:
@@ -105,7 +107,7 @@ async def _none_root_value_getter() -> None:
105
107
 
106
108
 
107
109
  async def _context_getter_ws(
108
- custom_context: Optional[Any], socket: WebSocket
110
+ custom_context: Any | None, socket: WebSocket
109
111
  ) -> MergedContext:
110
112
  if isinstance(custom_context, BaseContext):
111
113
  custom_context.websocket = socket
@@ -127,7 +129,7 @@ def _response_getter() -> Response:
127
129
 
128
130
 
129
131
  async def _context_getter_http(
130
- custom_context: Optional[Any],
132
+ custom_context: Any | None,
131
133
  response: Response,
132
134
  request: Request[Any, Any, Any],
133
135
  ) -> MergedContext:
@@ -148,44 +150,9 @@ async def _context_getter_http(
148
150
 
149
151
 
150
152
  class GraphQLResource(Struct):
151
- data: Optional[dict[str, object]]
152
- errors: Optional[list[object]]
153
- extensions: Optional[dict[str, object]]
154
-
155
-
156
- class LitestarRequestAdapter(AsyncHTTPRequestAdapter):
157
- def __init__(self, request: Request[Any, Any, Any]) -> None:
158
- self.request = request
159
-
160
- @property
161
- def query_params(self) -> QueryParams:
162
- return self.request.query_params
163
-
164
- @property
165
- def method(self) -> HTTPMethod:
166
- return cast("HTTPMethod", self.request.method.upper())
167
-
168
- @property
169
- def headers(self) -> Mapping[str, str]:
170
- return self.request.headers
171
-
172
- @property
173
- def content_type(self) -> Optional[str]:
174
- content_type, params = self.request.content_type
175
-
176
- # combine content type and params
177
- if params:
178
- content_type += "; " + "; ".join(f"{k}={v}" for k, v in params.items())
179
-
180
- return content_type
181
-
182
- async def get_body(self) -> bytes:
183
- return await self.request.body()
184
-
185
- async def get_form_data(self) -> FormData:
186
- multipart_data = await self.request.form()
187
-
188
- return FormData(form=multipart_data, files=multipart_data)
153
+ data: dict[str, object] | None
154
+ errors: list[object] | None
155
+ extensions: dict[str, object] | None
189
156
 
190
157
 
191
158
  class LitestarWebSocketAdapter(AsyncWebSocketAdapter):
@@ -246,12 +213,11 @@ class GraphQLController(
246
213
  }
247
214
 
248
215
  request_adapter_class = LitestarRequestAdapter
249
- websocket_adapter_class = LitestarWebSocketAdapter
216
+ websocket_adapter_class = LitestarWebSocketAdapter # type: ignore
250
217
 
251
218
  allow_queries_via_get: bool = True
252
219
  graphiql_allowed_accept: frozenset[str] = frozenset({"text/html", "*/*"})
253
- graphql_ide: Optional[GraphQL_IDE] = "graphiql"
254
- debug: bool = False
220
+ graphql_ide: GraphQL_IDE | None = "graphiql"
255
221
  connection_init_wait_timeout: timedelta = timedelta(minutes=1)
256
222
  protocols: Sequence[str] = (
257
223
  GRAPHQL_TRANSPORT_WS_PROTOCOL,
@@ -261,18 +227,18 @@ class GraphQLController(
261
227
  keep_alive_interval: float = 1
262
228
 
263
229
  def is_websocket_request(
264
- self, request: Union[Request, WebSocket]
230
+ self, request: Request | WebSocket
265
231
  ) -> TypeGuard[WebSocket]:
266
232
  return isinstance(request, WebSocket)
267
233
 
268
- async def pick_websocket_subprotocol(self, request: WebSocket) -> Optional[str]:
234
+ async def pick_websocket_subprotocol(self, request: WebSocket) -> str | None:
269
235
  subprotocols = request.scope["subprotocols"]
270
236
  intersection = set(subprotocols) & set(self.protocols)
271
237
  sorted_intersection = sorted(intersection, key=subprotocols.index)
272
238
  return next(iter(sorted_intersection), None)
273
239
 
274
240
  async def create_websocket_response(
275
- self, request: WebSocket, subprotocol: Optional[str]
241
+ self, request: WebSocket, subprotocol: str | None
276
242
  ) -> WebSocket:
277
243
  await request.accept(subprotocols=subprotocol)
278
244
  return request
@@ -282,7 +248,7 @@ class GraphQLController(
282
248
  request: Request[Any, Any, Any],
283
249
  context: Any,
284
250
  root_value: Any,
285
- ) -> Response[Union[GraphQLResource, str]]:
251
+ ) -> Response[GraphQLResource | str]:
286
252
  try:
287
253
  return await self.run(
288
254
  request,
@@ -302,7 +268,9 @@ class GraphQLController(
302
268
  return Response(self.graphql_ide_html, media_type=MediaType.HTML)
303
269
 
304
270
  def create_response(
305
- self, response_data: GraphQLHTTPResponse, sub_response: Response[bytes]
271
+ self,
272
+ response_data: GraphQLHTTPResponse | list[GraphQLHTTPResponse],
273
+ sub_response: Response[bytes],
306
274
  ) -> Response[bytes]:
307
275
  response = Response(
308
276
  self.encode_json(response_data).encode(),
@@ -342,7 +310,7 @@ class GraphQLController(
342
310
  context: Any,
343
311
  root_value: Any,
344
312
  response: Response,
345
- ) -> Response[Union[GraphQLResource, str]]:
313
+ ) -> Response[GraphQLResource | str]:
346
314
  self.temporal_response = response
347
315
 
348
316
  return await self.execute_request(
@@ -358,7 +326,7 @@ class GraphQLController(
358
326
  context: Any,
359
327
  root_value: Any,
360
328
  response: Response,
361
- ) -> Response[Union[GraphQLResource, str]]:
329
+ ) -> Response[GraphQLResource | str]:
362
330
  self.temporal_response = response
363
331
 
364
332
  return await self.execute_request(
@@ -382,14 +350,14 @@ class GraphQLController(
382
350
 
383
351
  async def get_context(
384
352
  self,
385
- request: Union[Request[Any, Any, Any], WebSocket],
386
- response: Union[Response, WebSocket],
353
+ request: Request[Any, Any, Any] | WebSocket,
354
+ response: Response | WebSocket,
387
355
  ) -> Context: # pragma: no cover
388
356
  msg = "`get_context` is not used by Litestar's controller"
389
357
  raise ValueError(msg)
390
358
 
391
359
  async def get_root_value(
392
- self, request: Union[Request[Any, Any, Any], WebSocket]
360
+ self, request: Request[Any, Any, Any] | WebSocket
393
361
  ) -> RootValue | None: # pragma: no cover
394
362
  msg = "`get_root_value` is not used by Litestar's controller"
395
363
  raise ValueError(msg)
@@ -401,16 +369,15 @@ class GraphQLController(
401
369
  def make_graphql_controller(
402
370
  schema: BaseSchema,
403
371
  path: str = "",
404
- graphiql: Optional[bool] = None,
405
- graphql_ide: Optional[GraphQL_IDE] = "graphiql",
372
+ graphiql: bool | None = None,
373
+ graphql_ide: GraphQL_IDE | None = "graphiql",
406
374
  allow_queries_via_get: bool = True,
407
375
  keep_alive: bool = False,
408
376
  keep_alive_interval: float = 1,
409
- debug: bool = False,
410
377
  # TODO: root typevar
411
- root_value_getter: Optional[AnyCallable] = None,
378
+ root_value_getter: AnyCallable | None = None,
412
379
  # TODO: context typevar
413
- context_getter: Optional[AnyCallable] = None,
380
+ context_getter: AnyCallable | None = None,
414
381
  subscription_protocols: Sequence[str] = (
415
382
  GRAPHQL_TRANSPORT_WS_PROTOCOL,
416
383
  GRAPHQL_WS_PROTOCOL,
@@ -430,7 +397,7 @@ def make_graphql_controller(
430
397
 
431
398
  schema_: BaseSchema = schema
432
399
  allow_queries_via_get_: bool = allow_queries_via_get
433
- graphql_ide_: Optional[GraphQL_IDE]
400
+ graphql_ide_: GraphQL_IDE | None
434
401
 
435
402
  if graphiql is not None:
436
403
  warnings.warn(
@@ -456,7 +423,6 @@ def make_graphql_controller(
456
423
 
457
424
  _GraphQLController.keep_alive = keep_alive
458
425
  _GraphQLController.keep_alive_interval = keep_alive_interval
459
- _GraphQLController.debug = debug
460
426
  _GraphQLController.protocols = subscription_protocols
461
427
  _GraphQLController.connection_init_wait_timeout = connection_init_wait_timeout
462
428
  _GraphQLController.graphiql_allowed_accept = frozenset({"text/html", "*/*"})
strawberry/permission.py CHANGED
@@ -7,8 +7,6 @@ from inspect import iscoroutinefunction
7
7
  from typing import (
8
8
  TYPE_CHECKING,
9
9
  Any,
10
- Optional,
11
- Union,
12
10
  )
13
11
 
14
12
  from strawberry.exceptions import StrawberryGraphQLError
@@ -50,18 +48,18 @@ class BasePermission(abc.ABC):
50
48
  ```
51
49
  """
52
50
 
53
- message: Optional[str] = None
51
+ message: str | None = None
54
52
 
55
- error_extensions: Optional[GraphQLErrorExtensions] = None
53
+ error_extensions: GraphQLErrorExtensions | None = None
56
54
 
57
55
  error_class: type[GraphQLError] = StrawberryGraphQLError
58
56
 
59
- _schema_directive: Optional[object] = None
57
+ _schema_directive: object | None = None
60
58
 
61
59
  @abc.abstractmethod
62
60
  def has_permission(
63
61
  self, source: Any, info: Info, **kwargs: Any
64
- ) -> Union[bool, Awaitable[bool]]:
62
+ ) -> bool | Awaitable[bool]:
65
63
  """Check if the permission should be accepted.
66
64
 
67
65
  This method should be overridden by the subclasses.
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  import re
4
4
  from collections.abc import Mapping
5
5
  from math import isfinite
6
- from typing import TYPE_CHECKING, Any, Optional, cast
6
+ from typing import TYPE_CHECKING, Any, cast
7
7
 
8
8
  from graphql.language import (
9
9
  BooleanValueNode,
@@ -44,9 +44,7 @@ __all__ = ["ast_from_value"]
44
44
  _re_integer_string = re.compile("^-?(?:0|[1-9][0-9]*)$")
45
45
 
46
46
 
47
- def ast_from_leaf_type(
48
- serialized: object, type_: Optional[GraphQLInputType]
49
- ) -> ValueNode:
47
+ def ast_from_leaf_type(serialized: object, type_: GraphQLInputType | None) -> ValueNode:
50
48
  # Others serialize based on their corresponding Python scalar types.
51
49
  if isinstance(serialized, bool):
52
50
  return BooleanValueNode(value=serialized)
@@ -86,7 +84,7 @@ def ast_from_leaf_type(
86
84
  ) # pragma: no cover
87
85
 
88
86
 
89
- def ast_from_value(value: Any, type_: GraphQLInputType) -> Optional[ValueNode]:
87
+ def ast_from_value(value: Any, type_: GraphQLInputType) -> ValueNode | None:
90
88
  # custom ast_from_value that allows to also serialize custom scalar that aren't
91
89
  # basic types, namely JSON scalar types
92
90