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
strawberry/__init__.py CHANGED
@@ -11,6 +11,7 @@ from .permission import BasePermission
11
11
  from .scalars import ID
12
12
  from .schema import Schema
13
13
  from .schema_directive import schema_directive
14
+ from .streamable import Streamable
14
15
  from .types.arguments import argument
15
16
  from .types.auto import auto
16
17
  from .types.cast import cast
@@ -37,6 +38,7 @@ __all__ = [
37
38
  "Private",
38
39
  "Schema",
39
40
  "Some",
41
+ "Streamable",
40
42
  "argument",
41
43
  "asdict",
42
44
  "auto",
@@ -1,27 +1,20 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import warnings
4
- from typing import (
5
- TYPE_CHECKING,
6
- Any,
7
- Optional,
8
- )
4
+ from typing import Any
9
5
 
10
6
  from strawberry.test.client import BaseGraphQLTestClient, Response
11
7
 
12
- if TYPE_CHECKING:
13
- from collections.abc import Mapping
14
-
15
8
 
16
9
  class GraphQLTestClient(BaseGraphQLTestClient):
17
10
  async def query(
18
11
  self,
19
12
  query: str,
20
- variables: Optional[dict[str, Mapping]] = None,
21
- headers: Optional[dict[str, object]] = None,
22
- asserts_errors: Optional[bool] = None,
23
- files: Optional[dict[str, object]] = None,
24
- assert_no_errors: Optional[bool] = True,
13
+ variables: dict[str, Any] | None = None,
14
+ headers: dict[str, object] | None = None,
15
+ asserts_errors: bool | None = None,
16
+ files: dict[str, object] | None = None,
17
+ assert_no_errors: bool | None = True,
25
18
  ) -> Response:
26
19
  body = self._build_body(query, variables, files)
27
20
 
@@ -54,8 +47,8 @@ class GraphQLTestClient(BaseGraphQLTestClient):
54
47
  async def request(
55
48
  self,
56
49
  body: dict[str, object],
57
- headers: Optional[dict[str, object]] = None,
58
- files: Optional[dict[str, object]] = None,
50
+ headers: dict[str, object] | None = None,
51
+ files: dict[str, object] | None = None,
59
52
  ) -> Any:
60
53
  return await self._client.post(
61
54
  self.url,
@@ -3,32 +3,24 @@ from __future__ import annotations
3
3
  import asyncio
4
4
  import warnings
5
5
  from datetime import timedelta
6
- from io import BytesIO
7
6
  from json.decoder import JSONDecodeError
8
7
  from typing import (
9
8
  TYPE_CHECKING,
10
- Any,
11
- Callable,
12
- Optional,
13
- Union,
14
- cast,
9
+ TypeGuard,
15
10
  )
16
- from typing_extensions import TypeGuard
17
11
 
18
12
  from aiohttp import ClientConnectionResetError, http, web
19
- from aiohttp.multipart import BodyPartReader
13
+ from lia import AiohttpHTTPRequestAdapter, HTTPException
14
+
20
15
  from strawberry.http.async_base_view import (
21
16
  AsyncBaseHTTPView,
22
- AsyncHTTPRequestAdapter,
23
17
  AsyncWebSocketAdapter,
24
18
  )
25
19
  from strawberry.http.exceptions import (
26
- HTTPException,
27
20
  NonJsonMessageReceived,
28
21
  NonTextMessageReceived,
29
22
  WebSocketDisconnected,
30
23
  )
31
- from strawberry.http.types import FormData, HTTPMethod, QueryParams
32
24
  from strawberry.http.typevars import (
33
25
  Context,
34
26
  RootValue,
@@ -36,54 +28,13 @@ from strawberry.http.typevars import (
36
28
  from strawberry.subscriptions import GRAPHQL_TRANSPORT_WS_PROTOCOL, GRAPHQL_WS_PROTOCOL
37
29
 
38
30
  if TYPE_CHECKING:
39
- from collections.abc import AsyncGenerator, Mapping, Sequence
31
+ from collections.abc import AsyncGenerator, Callable, Mapping, Sequence
40
32
 
41
33
  from strawberry.http import GraphQLHTTPResponse
42
34
  from strawberry.http.ides import GraphQL_IDE
43
35
  from strawberry.schema import BaseSchema
44
36
 
45
37
 
46
- class AiohttpHTTPRequestAdapter(AsyncHTTPRequestAdapter):
47
- def __init__(self, request: web.Request) -> None:
48
- self.request = request
49
-
50
- @property
51
- def query_params(self) -> QueryParams:
52
- return self.request.query.copy() # type: ignore[attr-defined]
53
-
54
- async def get_body(self) -> str:
55
- return (await self.request.content.read()).decode()
56
-
57
- @property
58
- def method(self) -> HTTPMethod:
59
- return cast("HTTPMethod", self.request.method.upper())
60
-
61
- @property
62
- def headers(self) -> Mapping[str, str]:
63
- return self.request.headers
64
-
65
- async def get_form_data(self) -> FormData:
66
- reader = await self.request.multipart()
67
-
68
- data: dict[str, Any] = {}
69
- files: dict[str, Any] = {}
70
-
71
- while field := await reader.next():
72
- assert isinstance(field, BodyPartReader)
73
- assert field.name
74
-
75
- if field.filename:
76
- files[field.name] = BytesIO(await field.read(decode=False))
77
- else:
78
- data[field.name] = await field.text()
79
-
80
- return FormData(files=files, form=data)
81
-
82
- @property
83
- def content_type(self) -> Optional[str]:
84
- return self.headers.get("content-type")
85
-
86
-
87
38
  class AiohttpWebSocketAdapter(AsyncWebSocketAdapter):
88
39
  def __init__(
89
40
  self, view: AsyncBaseHTTPView, request: web.Request, ws: web.WebSocketResponse
@@ -119,7 +70,7 @@ class AiohttpWebSocketAdapter(AsyncWebSocketAdapter):
119
70
  class GraphQLView(
120
71
  AsyncBaseHTTPView[
121
72
  web.Request,
122
- Union[web.Response, web.StreamResponse],
73
+ web.Response | web.StreamResponse,
123
74
  web.Response,
124
75
  web.Request,
125
76
  web.WebSocketResponse,
@@ -133,17 +84,16 @@ class GraphQLView(
133
84
 
134
85
  allow_queries_via_get = True
135
86
  request_adapter_class = AiohttpHTTPRequestAdapter
136
- websocket_adapter_class = AiohttpWebSocketAdapter
87
+ websocket_adapter_class = AiohttpWebSocketAdapter # type: ignore
137
88
 
138
89
  def __init__(
139
90
  self,
140
91
  schema: BaseSchema,
141
- graphiql: Optional[bool] = None,
142
- graphql_ide: Optional[GraphQL_IDE] = "graphiql",
92
+ graphiql: bool | None = None,
93
+ graphql_ide: GraphQL_IDE | None = "graphiql",
143
94
  allow_queries_via_get: bool = True,
144
95
  keep_alive: bool = True,
145
96
  keep_alive_interval: float = 1,
146
- debug: bool = False,
147
97
  subscription_protocols: Sequence[str] = (
148
98
  GRAPHQL_TRANSPORT_WS_PROTOCOL,
149
99
  GRAPHQL_WS_PROTOCOL,
@@ -155,7 +105,6 @@ class GraphQLView(
155
105
  self.allow_queries_via_get = allow_queries_via_get
156
106
  self.keep_alive = keep_alive
157
107
  self.keep_alive_interval = keep_alive_interval
158
- self.debug = debug
159
108
  self.subscription_protocols = subscription_protocols
160
109
  self.connection_init_wait_timeout = connection_init_wait_timeout
161
110
  self.multipart_uploads_enabled = multipart_uploads_enabled
@@ -180,12 +129,12 @@ class GraphQLView(
180
129
  ws = web.WebSocketResponse(protocols=self.subscription_protocols)
181
130
  return ws.can_prepare(request).ok
182
131
 
183
- async def pick_websocket_subprotocol(self, request: web.Request) -> Optional[str]:
132
+ async def pick_websocket_subprotocol(self, request: web.Request) -> str | None:
184
133
  ws = web.WebSocketResponse(protocols=self.subscription_protocols)
185
134
  return ws.can_prepare(request).protocol
186
135
 
187
136
  async def create_websocket_response(
188
- self, request: web.Request, subprotocol: Optional[str]
137
+ self, request: web.Request, subprotocol: str | None
189
138
  ) -> web.WebSocketResponse:
190
139
  protocols = [subprotocol] if subprotocol else []
191
140
  ws = web.WebSocketResponse(protocols=protocols)
@@ -201,16 +150,18 @@ class GraphQLView(
201
150
  status=e.status_code,
202
151
  )
203
152
 
204
- async def get_root_value(self, request: web.Request) -> Optional[RootValue]:
153
+ async def get_root_value(self, request: web.Request) -> RootValue | None:
205
154
  return None
206
155
 
207
156
  async def get_context(
208
- self, request: web.Request, response: Union[web.Response, web.WebSocketResponse]
157
+ self, request: web.Request, response: web.Response | web.WebSocketResponse
209
158
  ) -> Context:
210
159
  return {"request": request, "response": response} # type: ignore
211
160
 
212
161
  def create_response(
213
- self, response_data: GraphQLHTTPResponse, sub_response: web.Response
162
+ self,
163
+ response_data: GraphQLHTTPResponse | list[GraphQLHTTPResponse],
164
+ sub_response: web.Response,
214
165
  ) -> web.Response:
215
166
  sub_response.text = self.encode_json(response_data)
216
167
  sub_response.content_type = "application/json"
strawberry/annotation.py CHANGED
@@ -5,18 +5,21 @@ import typing
5
5
  import warnings
6
6
  from collections import abc
7
7
  from enum import Enum
8
+ from types import UnionType
8
9
  from typing import (
9
10
  TYPE_CHECKING,
10
11
  Annotated,
11
12
  Any,
12
13
  ForwardRef,
13
- Optional,
14
14
  TypeVar,
15
15
  Union,
16
16
  cast,
17
+ get_args,
18
+ get_origin,
17
19
  )
18
- from typing_extensions import Self, get_args, get_origin
20
+ from typing_extensions import Self
19
21
 
22
+ from strawberry.streamable import StrawberryStreamable
20
23
  from strawberry.types.base import (
21
24
  StrawberryList,
22
25
  StrawberryMaybe,
@@ -57,14 +60,14 @@ class StrawberryAnnotation:
57
60
 
58
61
  def __init__(
59
62
  self,
60
- annotation: Union[object, str],
63
+ annotation: object | str,
61
64
  *,
62
- namespace: Optional[dict[str, Any]] = None,
65
+ namespace: dict[str, Any] | None = None,
63
66
  ) -> None:
64
67
  self.raw_annotation = annotation
65
68
  self.namespace = namespace
66
69
 
67
- self.__resolve_cache__: Optional[Union[StrawberryType, type]] = None
70
+ self.__resolve_cache__: StrawberryType | type | None = None
68
71
 
69
72
  def __eq__(self, other: object) -> bool:
70
73
  if not isinstance(other, StrawberryAnnotation):
@@ -77,8 +80,8 @@ class StrawberryAnnotation:
77
80
 
78
81
  @staticmethod
79
82
  def from_annotation(
80
- annotation: object, namespace: Optional[dict[str, Any]] = None
81
- ) -> Optional[StrawberryAnnotation]:
83
+ annotation: object, namespace: dict[str, Any] | None = None
84
+ ) -> StrawberryAnnotation | None:
82
85
  if annotation is None:
83
86
  return None
84
87
 
@@ -87,7 +90,7 @@ class StrawberryAnnotation:
87
90
  return annotation
88
91
 
89
92
  @property
90
- def annotation(self) -> Union[object, str]:
93
+ def annotation(self) -> object | str:
91
94
  """Return evaluated type on success or fallback to raw (string) annotation."""
92
95
  try:
93
96
  return self.evaluate()
@@ -97,7 +100,7 @@ class StrawberryAnnotation:
97
100
  return self.raw_annotation
98
101
 
99
102
  @annotation.setter
100
- def annotation(self, value: Union[object, str]) -> None:
103
+ def annotation(self, value: object | str) -> None:
101
104
  self.raw_annotation = value
102
105
 
103
106
  self.__resolve_cache__ = None
@@ -124,16 +127,44 @@ class StrawberryAnnotation:
124
127
 
125
128
  return evaled_type, []
126
129
 
127
- def resolve(self) -> Union[StrawberryType, type]:
130
+ def resolve(
131
+ self,
132
+ *,
133
+ type_definition: StrawberryObjectDefinition | None = None,
134
+ ) -> StrawberryType | type:
128
135
  """Return resolved (transformed) annotation."""
129
- if self.__resolve_cache__ is None:
130
- self.__resolve_cache__ = self._resolve()
131
-
132
- return self.__resolve_cache__
133
-
134
- def _resolve(self) -> Union[StrawberryType, type]:
136
+ if (resolved := self.__resolve_cache__) is None:
137
+ resolved = self._resolve()
138
+ self.__resolve_cache__ = resolved
139
+
140
+ # If this is a generic field, try to resolve it using its origin's
141
+ # specialized type_var_map
142
+ if self._is_type_generic(resolved) and type_definition is not None:
143
+ from strawberry.types.base import StrawberryType
144
+
145
+ specialized_type_var_map = type_definition.specialized_type_var_map
146
+ if specialized_type_var_map and isinstance(resolved, StrawberryType):
147
+ resolved = resolved.copy_with(specialized_type_var_map)
148
+
149
+ # If the field is still generic, try to resolve it from the type_definition
150
+ # that is asking for it.
151
+ if (
152
+ self._is_type_generic(resolved)
153
+ and type_definition.type_var_map
154
+ and isinstance(resolved, StrawberryType)
155
+ ):
156
+ resolved = resolved.copy_with(type_definition.type_var_map)
157
+
158
+ # Resolve the type again to resolve any `Annotated` types
159
+ resolved = self._resolve_evaled_type(resolved)
160
+
161
+ return resolved
162
+
163
+ def _resolve(self) -> StrawberryType | type:
135
164
  evaled_type = cast("Any", self.evaluate())
165
+ return self._resolve_evaled_type(evaled_type)
136
166
 
167
+ def _resolve_evaled_type(self, evaled_type: Any) -> StrawberryType | type:
137
168
  if is_private(evaled_type):
138
169
  return evaled_type
139
170
 
@@ -143,6 +174,8 @@ class StrawberryAnnotation:
143
174
 
144
175
  if self._is_lazy_type(evaled_type):
145
176
  return evaled_type
177
+ if self._is_streamable(evaled_type, args):
178
+ return self.create_list(list[evaled_type])
146
179
  if self._is_list(evaled_type):
147
180
  return self.create_list(evaled_type)
148
181
  if self._is_maybe(evaled_type):
@@ -213,7 +246,7 @@ class StrawberryAnnotation:
213
246
  # passed as we can safely use `Union` for both optional types
214
247
  # (e.g. `Optional[str]`) and optional unions (e.g.
215
248
  # `Optional[Union[TypeA, TypeB]]`)
216
- child_type = Union[non_optional_types] # type: ignore
249
+ child_type = Union[non_optional_types] # type: ignore # noqa: UP007
217
250
 
218
251
  of_type = StrawberryAnnotation(
219
252
  annotation=child_type,
@@ -289,6 +322,20 @@ class StrawberryAnnotation:
289
322
  return False
290
323
  return issubclass(annotation, Enum)
291
324
 
325
+ @classmethod
326
+ def _is_type_generic(cls, type_: StrawberryType | type) -> bool:
327
+ """Returns True if `resolver_type` is generic else False."""
328
+ from strawberry.types.base import StrawberryType
329
+
330
+ if isinstance(type_, StrawberryType):
331
+ return type_.is_graphql_generic
332
+
333
+ # solves the Generic subclass case
334
+ if has_object_definition(type_):
335
+ return type_.__strawberry_definition__.is_graphql_generic
336
+
337
+ return False
338
+
292
339
  @classmethod
293
340
  def _is_graphql_generic(cls, annotation: Any) -> bool:
294
341
  if hasattr(annotation, "__origin__"):
@@ -332,6 +379,10 @@ class StrawberryAnnotation:
332
379
  def _is_maybe(cls, annotation: Any) -> bool:
333
380
  return _annotation_is_maybe(annotation)
334
381
 
382
+ @classmethod
383
+ def _is_streamable(cls, annotation: Any, args: list[Any]) -> bool:
384
+ return any(isinstance(arg, StrawberryStreamable) for arg in args)
385
+
335
386
  @classmethod
336
387
  def _is_strawberry_type(cls, evaled_type: Any) -> bool:
337
388
  # Prevent import cycles
@@ -364,16 +415,10 @@ class StrawberryAnnotation:
364
415
  """Returns True if annotation is a Union."""
365
416
  # this check is needed because unions declared with the new syntax `A | B`
366
417
  # don't have a `__origin__` property on them, but they are instances of
367
- # `UnionType`, which is only available in Python 3.10+
368
- if sys.version_info >= (3, 10):
369
- from types import UnionType
370
-
371
- if isinstance(annotation, UnionType):
372
- return True
418
+ if isinstance(annotation, UnionType):
419
+ return True
373
420
 
374
421
  # unions declared as Union[A, B] fall through to this check
375
- # even on python 3.10+
376
-
377
422
  annotation_origin = getattr(annotation, "__origin__", None)
378
423
 
379
424
  if annotation_origin is typing.Union:
@@ -5,13 +5,10 @@ from datetime import timedelta
5
5
  from json import JSONDecodeError
6
6
  from typing import (
7
7
  TYPE_CHECKING,
8
- Callable,
9
- Optional,
10
- Union,
11
- cast,
8
+ TypeGuard,
12
9
  )
13
- from typing_extensions import TypeGuard
14
10
 
11
+ from lia import HTTPException, StarletteRequestAdapter
15
12
  from starlette import status
16
13
  from starlette.requests import Request
17
14
  from starlette.responses import (
@@ -24,16 +21,13 @@ from starlette.websockets import WebSocket, WebSocketDisconnect, WebSocketState
24
21
 
25
22
  from strawberry.http.async_base_view import (
26
23
  AsyncBaseHTTPView,
27
- AsyncHTTPRequestAdapter,
28
24
  AsyncWebSocketAdapter,
29
25
  )
30
26
  from strawberry.http.exceptions import (
31
- HTTPException,
32
27
  NonJsonMessageReceived,
33
28
  NonTextMessageReceived,
34
29
  WebSocketDisconnected,
35
30
  )
36
- from strawberry.http.types import FormData, HTTPMethod, QueryParams
37
31
  from strawberry.http.typevars import (
38
32
  Context,
39
33
  RootValue,
@@ -41,7 +35,13 @@ from strawberry.http.typevars import (
41
35
  from strawberry.subscriptions import GRAPHQL_TRANSPORT_WS_PROTOCOL, GRAPHQL_WS_PROTOCOL
42
36
 
43
37
  if TYPE_CHECKING:
44
- from collections.abc import AsyncGenerator, AsyncIterator, Mapping, Sequence
38
+ from collections.abc import (
39
+ AsyncGenerator,
40
+ AsyncIterator,
41
+ Callable,
42
+ Mapping,
43
+ Sequence,
44
+ )
45
45
 
46
46
  from starlette.types import Receive, Scope, Send
47
47
 
@@ -50,38 +50,6 @@ if TYPE_CHECKING:
50
50
  from strawberry.schema import BaseSchema
51
51
 
52
52
 
53
- class ASGIRequestAdapter(AsyncHTTPRequestAdapter):
54
- def __init__(self, request: Request) -> None:
55
- self.request = request
56
-
57
- @property
58
- def query_params(self) -> QueryParams:
59
- return self.request.query_params
60
-
61
- @property
62
- def method(self) -> HTTPMethod:
63
- return cast("HTTPMethod", self.request.method.upper())
64
-
65
- @property
66
- def headers(self) -> Mapping[str, str]:
67
- return self.request.headers
68
-
69
- @property
70
- def content_type(self) -> Optional[str]:
71
- return self.request.headers.get("content-type")
72
-
73
- async def get_body(self) -> bytes:
74
- return await self.request.body()
75
-
76
- async def get_form_data(self) -> FormData:
77
- multipart_data = await self.request.form()
78
-
79
- return FormData(
80
- files=multipart_data,
81
- form=multipart_data,
82
- )
83
-
84
-
85
53
  class ASGIWebSocketAdapter(AsyncWebSocketAdapter):
86
54
  def __init__(
87
55
  self, view: AsyncBaseHTTPView, request: WebSocket, response: WebSocket
@@ -127,18 +95,17 @@ class GraphQL(
127
95
  ]
128
96
  ):
129
97
  allow_queries_via_get = True
130
- request_adapter_class = ASGIRequestAdapter
131
- websocket_adapter_class = ASGIWebSocketAdapter
98
+ request_adapter_class = StarletteRequestAdapter
99
+ websocket_adapter_class = ASGIWebSocketAdapter # type: ignore
132
100
 
133
101
  def __init__(
134
102
  self,
135
103
  schema: BaseSchema,
136
- graphiql: Optional[bool] = None,
137
- graphql_ide: Optional[GraphQL_IDE] = "graphiql",
104
+ graphiql: bool | None = None,
105
+ graphql_ide: GraphQL_IDE | None = "graphiql",
138
106
  allow_queries_via_get: bool = True,
139
107
  keep_alive: bool = False,
140
108
  keep_alive_interval: float = 1,
141
- debug: bool = False,
142
109
  subscription_protocols: Sequence[str] = (
143
110
  GRAPHQL_TRANSPORT_WS_PROTOCOL,
144
111
  GRAPHQL_WS_PROTOCOL,
@@ -150,7 +117,6 @@ class GraphQL(
150
117
  self.allow_queries_via_get = allow_queries_via_get
151
118
  self.keep_alive = keep_alive
152
119
  self.keep_alive_interval = keep_alive_interval
153
- self.debug = debug
154
120
  self.protocols = subscription_protocols
155
121
  self.connection_init_wait_timeout = connection_init_wait_timeout
156
122
  self.multipart_uploads_enabled = multipart_uploads_enabled
@@ -181,19 +147,17 @@ class GraphQL(
181
147
  else: # pragma: no cover
182
148
  raise ValueError("Unknown scope type: {!r}".format(scope["type"]))
183
149
 
184
- async def get_root_value(
185
- self, request: Union[Request, WebSocket]
186
- ) -> Optional[RootValue]:
150
+ async def get_root_value(self, request: Request | WebSocket) -> RootValue | None:
187
151
  return None
188
152
 
189
153
  async def get_context(
190
- self, request: Union[Request, WebSocket], response: Union[Response, WebSocket]
154
+ self, request: Request | WebSocket, response: Response | WebSocket
191
155
  ) -> Context:
192
156
  return {"request": request, "response": response} # type: ignore
193
157
 
194
158
  async def get_sub_response(
195
159
  self,
196
- request: Union[Request, WebSocket],
160
+ request: Request | WebSocket,
197
161
  ) -> Response:
198
162
  sub_response = Response()
199
163
  sub_response.status_code = None # type: ignore
@@ -205,7 +169,9 @@ class GraphQL(
205
169
  return HTMLResponse(self.graphql_ide_html)
206
170
 
207
171
  def create_response(
208
- self, response_data: GraphQLHTTPResponse, sub_response: Response
172
+ self,
173
+ response_data: GraphQLHTTPResponse | list[GraphQLHTTPResponse],
174
+ sub_response: Response,
209
175
  ) -> Response:
210
176
  response = Response(
211
177
  self.encode_json(response_data),
@@ -240,18 +206,18 @@ class GraphQL(
240
206
  )
241
207
 
242
208
  def is_websocket_request(
243
- self, request: Union[Request, WebSocket]
209
+ self, request: Request | WebSocket
244
210
  ) -> TypeGuard[WebSocket]:
245
211
  return request.scope["type"] == "websocket"
246
212
 
247
- async def pick_websocket_subprotocol(self, request: WebSocket) -> Optional[str]:
213
+ async def pick_websocket_subprotocol(self, request: WebSocket) -> str | None:
248
214
  protocols = request["subprotocols"]
249
215
  intersection = set(protocols) & set(self.protocols)
250
216
  sorted_intersection = sorted(intersection, key=protocols.index)
251
217
  return next(iter(sorted_intersection), None)
252
218
 
253
219
  async def create_websocket_response(
254
- self, request: WebSocket, subprotocol: Optional[str]
220
+ self, request: WebSocket, subprotocol: str | None
255
221
  ) -> WebSocket:
256
222
  await request.accept(subprotocol=subprotocol)
257
223
  return request
@@ -1,21 +1,21 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import json
4
- from typing import TYPE_CHECKING, Any, Optional
4
+ from typing import TYPE_CHECKING, Any
5
5
 
6
6
  from strawberry.test import BaseGraphQLTestClient
7
7
 
8
8
  if TYPE_CHECKING:
9
9
  from collections.abc import Mapping
10
- from typing_extensions import Literal
10
+ from typing import Literal
11
11
 
12
12
 
13
13
  class GraphQLTestClient(BaseGraphQLTestClient):
14
14
  def _build_body(
15
15
  self,
16
16
  query: str,
17
- variables: Optional[dict[str, Mapping]] = None,
18
- files: Optional[dict[str, object]] = None,
17
+ variables: dict[str, Mapping] | None = None,
18
+ files: dict[str, object] | None = None,
19
19
  ) -> dict[str, object]:
20
20
  body: dict[str, object] = {"query": query}
21
21
 
@@ -36,8 +36,8 @@ class GraphQLTestClient(BaseGraphQLTestClient):
36
36
  def request(
37
37
  self,
38
38
  body: dict[str, object],
39
- headers: Optional[dict[str, object]] = None,
40
- files: Optional[dict[str, object]] = None,
39
+ headers: dict[str, object] | None = None,
40
+ files: dict[str, object] | None = None,
41
41
  ) -> Any:
42
42
  return self._client.post(
43
43
  self.url,