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
@@ -5,10 +5,7 @@ from itertools import chain
5
5
  from typing import (
6
6
  TYPE_CHECKING,
7
7
  Any,
8
- Callable,
9
- Optional,
10
8
  TypeVar,
11
- Union,
12
9
  cast,
13
10
  overload,
14
11
  )
@@ -46,6 +43,8 @@ from strawberry.types.unset import UNSET
46
43
  from .ast_from_value import ast_from_value
47
44
 
48
45
  if TYPE_CHECKING:
46
+ from collections.abc import Callable
47
+
49
48
  from graphql import (
50
49
  GraphQLArgument,
51
50
  GraphQLEnumType,
@@ -78,7 +77,7 @@ def _serialize_dataclasses(
78
77
 
79
78
  @overload
80
79
  def _serialize_dataclasses(
81
- value: Union[list[object], tuple[object]],
80
+ value: list[object] | tuple[object],
82
81
  *,
83
82
  name_converter: Callable[[str], str] | None = None,
84
83
  ) -> list[object]: ...
@@ -190,7 +189,7 @@ def print_schema_directive(
190
189
 
191
190
 
192
191
  def print_field_directives(
193
- field: Optional[StrawberryField], schema: BaseSchema, *, extras: PrintExtras
192
+ field: StrawberryField | None, schema: BaseSchema, *, extras: PrintExtras
194
193
  ) -> str:
195
194
  if not field:
196
195
  return ""
@@ -363,7 +362,7 @@ def print_extends(type_: GraphQLObjectType, schema: BaseSchema) -> str:
363
362
  from strawberry.schema.schema_converter import GraphQLCoreConverter
364
363
 
365
364
  strawberry_type = cast(
366
- "Optional[StrawberryObjectDefinition]",
365
+ "StrawberryObjectDefinition | None",
367
366
  type_.extensions
368
367
  and type_.extensions.get(GraphQLCoreConverter.DEFINITION_BACKREF),
369
368
  )
@@ -380,7 +379,7 @@ def print_type_directives(
380
379
  from strawberry.schema.schema_converter import GraphQLCoreConverter
381
380
 
382
381
  strawberry_type = cast(
383
- "Optional[StrawberryObjectDefinition]",
382
+ "StrawberryObjectDefinition | None",
384
383
  type_.extensions
385
384
  and type_.extensions.get(GraphQLCoreConverter.DEFINITION_BACKREF),
386
385
  )
@@ -534,9 +533,7 @@ def _all_root_names_are_common_names(schema: BaseSchema) -> bool:
534
533
  )
535
534
 
536
535
 
537
- def print_schema_definition(
538
- schema: BaseSchema, *, extras: PrintExtras
539
- ) -> Optional[str]:
536
+ def print_schema_definition(schema: BaseSchema, *, extras: PrintExtras) -> str | None:
540
537
  # TODO: add support for description
541
538
 
542
539
  if _all_root_names_are_common_names(schema) and not schema.schema_directives:
@@ -558,12 +555,10 @@ def print_schema_definition(
558
555
  return f"schema{directives} {{\n" + "\n".join(operation_types) + "\n}"
559
556
 
560
557
 
561
- def print_directive(
562
- directive: GraphQLDirective, *, schema: BaseSchema
563
- ) -> Optional[str]:
564
- strawberry_directive = directive.extensions["strawberry-definition"]
558
+ def print_directive(directive: GraphQLDirective, *, schema: BaseSchema) -> str | None:
559
+ strawberry_directive = directive.extensions.get("strawberry-definition")
565
560
 
566
- if (
561
+ if strawberry_directive is None or (
567
562
  isinstance(strawberry_directive, StrawberrySchemaDirective)
568
563
  and not strawberry_directive.print_definition
569
564
  ):
@@ -621,6 +616,14 @@ def print_schema(schema: BaseSchema) -> str:
621
616
  if (printed_directive := print_directive(directive, schema=schema)) is not None
622
617
  ]
623
618
 
619
+ if schema.config.enable_experimental_incremental_execution:
620
+ directives.append(
621
+ "directive @defer(if: Boolean, label: String) on FRAGMENT_SPREAD | INLINE_FRAGMENT"
622
+ )
623
+ directives.append(
624
+ "directive @stream(if: Boolean, label: String, initialCount: Int = 0) on FIELD"
625
+ )
626
+
624
627
  def _name_getter(type_: Any) -> str:
625
628
  if hasattr(type_, "name"):
626
629
  return type_.name
strawberry/quart/views.py CHANGED
@@ -1,65 +1,35 @@
1
1
  import asyncio
2
2
  import warnings
3
- from collections.abc import AsyncGenerator, Mapping, Sequence
3
+ from collections.abc import AsyncGenerator, Callable, Mapping, Sequence
4
4
  from datetime import timedelta
5
5
  from json.decoder import JSONDecodeError
6
- from typing import TYPE_CHECKING, Callable, ClassVar, Optional, Union, cast
7
- from typing_extensions import TypeGuard
6
+ from typing import TYPE_CHECKING, ClassVar, TypeGuard, Union
8
7
 
8
+ from lia import HTTPException, QuartHTTPRequestAdapter
9
9
  from quart import Request, Response, Websocket, request, websocket
10
10
  from quart.ctx import has_websocket_context
11
11
  from quart.views import View
12
+
12
13
  from strawberry.http.async_base_view import (
13
14
  AsyncBaseHTTPView,
14
- AsyncHTTPRequestAdapter,
15
15
  AsyncWebSocketAdapter,
16
16
  )
17
17
  from strawberry.http.exceptions import (
18
- HTTPException,
19
18
  NonJsonMessageReceived,
20
19
  NonTextMessageReceived,
21
20
  WebSocketDisconnected,
22
21
  )
23
22
  from strawberry.http.ides import GraphQL_IDE
24
- from strawberry.http.types import FormData, HTTPMethod, QueryParams
25
23
  from strawberry.http.typevars import Context, RootValue
26
24
  from strawberry.subscriptions import GRAPHQL_TRANSPORT_WS_PROTOCOL, GRAPHQL_WS_PROTOCOL
27
25
 
28
26
  if TYPE_CHECKING:
29
27
  from quart.typing import ResponseReturnValue
28
+
30
29
  from strawberry.http import GraphQLHTTPResponse
31
30
  from strawberry.schema.base import BaseSchema
32
31
 
33
32
 
34
- class QuartHTTPRequestAdapter(AsyncHTTPRequestAdapter):
35
- def __init__(self, request: Request) -> None:
36
- self.request = request
37
-
38
- @property
39
- def query_params(self) -> QueryParams:
40
- return self.request.args.to_dict()
41
-
42
- @property
43
- def method(self) -> HTTPMethod:
44
- return cast("HTTPMethod", self.request.method.upper())
45
-
46
- @property
47
- def content_type(self) -> Optional[str]:
48
- return self.request.content_type
49
-
50
- @property
51
- def headers(self) -> Mapping[str, str]:
52
- return self.request.headers # type: ignore
53
-
54
- async def get_body(self) -> str:
55
- return (await self.request.data).decode()
56
-
57
- async def get_form_data(self) -> FormData:
58
- files = await self.request.files
59
- form = await self.request.form
60
- return FormData(files=files, form=form)
61
-
62
-
63
33
  class QuartWebSocketAdapter(AsyncWebSocketAdapter):
64
34
  def __init__(
65
35
  self, view: AsyncBaseHTTPView, request: Websocket, response: Response
@@ -108,17 +78,16 @@ class GraphQLView(
108
78
  methods: ClassVar[list[str]] = ["GET", "POST"]
109
79
  allow_queries_via_get: bool = True
110
80
  request_adapter_class = QuartHTTPRequestAdapter
111
- websocket_adapter_class = QuartWebSocketAdapter
81
+ websocket_adapter_class = QuartWebSocketAdapter # type: ignore
112
82
 
113
83
  def __init__(
114
84
  self,
115
85
  schema: "BaseSchema",
116
- graphiql: Optional[bool] = None,
117
- graphql_ide: Optional[GraphQL_IDE] = "graphiql",
86
+ graphiql: bool | None = None,
87
+ graphql_ide: GraphQL_IDE | None = "graphiql",
118
88
  allow_queries_via_get: bool = True,
119
89
  keep_alive: bool = True,
120
90
  keep_alive_interval: float = 1,
121
- debug: bool = False,
122
91
  subscription_protocols: Sequence[str] = (
123
92
  GRAPHQL_TRANSPORT_WS_PROTOCOL,
124
93
  GRAPHQL_WS_PROTOCOL,
@@ -130,7 +99,6 @@ class GraphQLView(
130
99
  self.allow_queries_via_get = allow_queries_via_get
131
100
  self.keep_alive = keep_alive
132
101
  self.keep_alive_interval = keep_alive_interval
133
- self.debug = debug
134
102
  self.subscription_protocols = subscription_protocols
135
103
  self.connection_init_wait_timeout = connection_init_wait_timeout
136
104
  self.multipart_uploads_enabled = multipart_uploads_enabled
@@ -149,20 +117,20 @@ class GraphQLView(
149
117
  return Response(self.graphql_ide_html)
150
118
 
151
119
  def create_response(
152
- self, response_data: "GraphQLHTTPResponse", sub_response: Response
120
+ self,
121
+ response_data: Union["GraphQLHTTPResponse", list["GraphQLHTTPResponse"]],
122
+ sub_response: Response,
153
123
  ) -> Response:
154
124
  sub_response.set_data(self.encode_json(response_data))
155
125
 
156
126
  return sub_response
157
127
 
158
128
  async def get_context(
159
- self, request: Union[Request, Websocket], response: Response
129
+ self, request: Request | Websocket, response: Response
160
130
  ) -> Context:
161
131
  return {"request": request, "response": response} # type: ignore
162
132
 
163
- async def get_root_value(
164
- self, request: Union[Request, Websocket]
165
- ) -> Optional[RootValue]:
133
+ async def get_root_value(self, request: Request | Websocket) -> RootValue | None:
166
134
  return None
167
135
 
168
136
  async def get_sub_response(self, request: Request) -> Response:
@@ -196,18 +164,18 @@ class GraphQLView(
196
164
  )
197
165
 
198
166
  def is_websocket_request(
199
- self, request: Union[Request, Websocket]
167
+ self, request: Request | Websocket
200
168
  ) -> TypeGuard[Websocket]:
201
169
  return has_websocket_context()
202
170
 
203
- async def pick_websocket_subprotocol(self, request: Websocket) -> Optional[str]:
171
+ async def pick_websocket_subprotocol(self, request: Websocket) -> str | None:
204
172
  protocols = request.requested_subprotocols
205
173
  intersection = set(protocols) & set(self.subscription_protocols)
206
174
  sorted_intersection = sorted(intersection, key=protocols.index)
207
175
  return next(iter(sorted_intersection), None)
208
176
 
209
177
  async def create_websocket_response(
210
- self, request: Websocket, subprotocol: Optional[str]
178
+ self, request: Websocket, subprotocol: str | None
211
179
  ) -> Response:
212
180
  await request.accept(subprotocol=subprotocol)
213
181
  return Response()
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from functools import cached_property
4
- from typing import TYPE_CHECKING, Optional, cast
4
+ from typing import TYPE_CHECKING, cast
5
5
 
6
6
  from strawberry.exceptions.exception import StrawberryException
7
7
  from strawberry.exceptions.utils.source_finder import SourceFinder
@@ -32,7 +32,7 @@ class NodeIDAnnotationError(StrawberryException):
32
32
  super().__init__(self.message)
33
33
 
34
34
  @cached_property
35
- def exception_source(self) -> Optional[ExceptionSource]:
35
+ def exception_source(self) -> ExceptionSource | None:
36
36
  if self.cls is None:
37
37
  return None # pragma: no cover
38
38
 
@@ -63,7 +63,7 @@ class RelayWrongAnnotationError(StrawberryException):
63
63
  super().__init__(self.message)
64
64
 
65
65
  @cached_property
66
- def exception_source(self) -> Optional[ExceptionSource]:
66
+ def exception_source(self) -> ExceptionSource | None:
67
67
  if self.cls is None:
68
68
  return None # pragma: no cover
69
69
 
@@ -97,7 +97,7 @@ class RelayWrongResolverAnnotationError(StrawberryException):
97
97
  super().__init__(self.message)
98
98
 
99
99
  @cached_property
100
- def exception_source(self) -> Optional[ExceptionSource]:
100
+ def exception_source(self) -> ExceptionSource | None:
101
101
  if self.function is None:
102
102
  return None # pragma: no cover
103
103
 
@@ -8,6 +8,7 @@ from collections.abc import (
8
8
  AsyncIterable,
9
9
  AsyncIterator,
10
10
  Awaitable,
11
+ Callable,
11
12
  Iterable,
12
13
  Iterator,
13
14
  Mapping,
@@ -17,13 +18,12 @@ from typing import (
17
18
  TYPE_CHECKING,
18
19
  Annotated,
19
20
  Any,
20
- Callable,
21
21
  ForwardRef,
22
22
  Optional,
23
- Union,
24
23
  cast,
24
+ get_args,
25
+ get_origin,
25
26
  )
26
- from typing_extensions import get_args, get_origin
27
27
 
28
28
  from strawberry.annotation import StrawberryAnnotation
29
29
  from strawberry.extensions.field_extension import (
@@ -47,7 +47,7 @@ from strawberry.utils.typing import eval_type, is_generic_alias, is_optional, is
47
47
  from .types import Connection, GlobalID, Node
48
48
 
49
49
  if TYPE_CHECKING:
50
- from typing_extensions import Literal
50
+ from typing import Literal
51
51
 
52
52
  from strawberry.permission import BasePermission
53
53
  from strawberry.types.info import Info
@@ -81,14 +81,14 @@ class NodeExtension(FieldExtension):
81
81
 
82
82
  def get_node_resolver(
83
83
  self, field: StrawberryField
84
- ) -> Callable[[Info, GlobalID], Union[Node, None, Awaitable[Union[Node, None]]]]:
84
+ ) -> Callable[[Info, GlobalID], Node | None | Awaitable[Node | None]]:
85
85
  type_ = field.type
86
86
  is_optional = isinstance(type_, StrawberryOptional)
87
87
 
88
88
  def resolver(
89
89
  info: Info,
90
90
  id: Annotated[GlobalID, argument(description="The ID of the object.")],
91
- ) -> Union[Node, None, Awaitable[Union[Node, None]]]:
91
+ ) -> Node | None | Awaitable[Node | None]:
92
92
  node_type = id.resolve_type(info)
93
93
  resolved_node = node_type.resolve_node(
94
94
  id.node_id,
@@ -114,7 +114,7 @@ class NodeExtension(FieldExtension):
114
114
 
115
115
  def get_node_list_resolver(
116
116
  self, field: StrawberryField
117
- ) -> Callable[[Info, list[GlobalID]], Union[list[Node], Awaitable[list[Node]]]]:
117
+ ) -> Callable[[Info, list[GlobalID]], list[Node] | Awaitable[list[Node]]]:
118
118
  type_ = field.type
119
119
  assert isinstance(type_, StrawberryList)
120
120
  is_optional = isinstance(type_.of_type, StrawberryOptional)
@@ -124,7 +124,7 @@ class NodeExtension(FieldExtension):
124
124
  ids: Annotated[
125
125
  list[GlobalID], argument(description="The IDs of the objects.")
126
126
  ],
127
- ) -> Union[list[Node], Awaitable[list[Node]]]:
127
+ ) -> list[Node] | Awaitable[list[Node]]:
128
128
  nodes_map: defaultdict[type[Node], list[str]] = defaultdict(list)
129
129
  # Store the index of the node in the list of nodes of the same type
130
130
  # so that we can return them in the same order while also supporting
@@ -180,6 +180,7 @@ class NodeExtension(FieldExtension):
180
180
  for nodes in asyncgen_nodes.values()
181
181
  ),
182
182
  ),
183
+ strict=True,
183
184
  )
184
185
  )
185
186
 
@@ -207,7 +208,7 @@ class NodeExtension(FieldExtension):
207
208
  class ConnectionExtension(FieldExtension):
208
209
  connection_type: type[Connection[Node]]
209
210
 
210
- def __init__(self, max_results: Optional[int] = None) -> None:
211
+ def __init__(self, max_results: int | None = None) -> None:
211
212
  self.max_results = max_results
212
213
 
213
214
  def apply(self, field: StrawberryField) -> None:
@@ -216,7 +217,7 @@ class ConnectionExtension(FieldExtension):
216
217
  StrawberryArgument(
217
218
  python_name="before",
218
219
  graphql_name=None,
219
- type_annotation=StrawberryAnnotation(Optional[str]),
220
+ type_annotation=StrawberryAnnotation(Optional[str]), # noqa: UP045
220
221
  description=(
221
222
  "Returns the items in the list that come before the "
222
223
  "specified cursor."
@@ -226,7 +227,7 @@ class ConnectionExtension(FieldExtension):
226
227
  StrawberryArgument(
227
228
  python_name="after",
228
229
  graphql_name=None,
229
- type_annotation=StrawberryAnnotation(Optional[str]),
230
+ type_annotation=StrawberryAnnotation(Optional[str]), # noqa: UP045
230
231
  description=(
231
232
  "Returns the items in the list that come after the "
232
233
  "specified cursor."
@@ -236,14 +237,14 @@ class ConnectionExtension(FieldExtension):
236
237
  StrawberryArgument(
237
238
  python_name="first",
238
239
  graphql_name=None,
239
- type_annotation=StrawberryAnnotation(Optional[int]),
240
+ type_annotation=StrawberryAnnotation(Optional[int]), # noqa: UP045
240
241
  description="Returns the first n items from the list.",
241
242
  default=None,
242
243
  ),
243
244
  StrawberryArgument(
244
245
  python_name="last",
245
246
  graphql_name=None,
246
- type_annotation=StrawberryAnnotation(Optional[int]),
247
+ type_annotation=StrawberryAnnotation(Optional[int]), # noqa: UP045
247
248
  description=(
248
249
  "Returns the items in the list that come after the "
249
250
  "specified cursor."
@@ -307,10 +308,10 @@ class ConnectionExtension(FieldExtension):
307
308
  source: Any,
308
309
  info: Info,
309
310
  *,
310
- before: Optional[str] = None,
311
- after: Optional[str] = None,
312
- first: Optional[int] = None,
313
- last: Optional[int] = None,
311
+ before: str | None = None,
312
+ after: str | None = None,
313
+ first: int | None = None,
314
+ last: int | None = None,
314
315
  **kwargs: Any,
315
316
  ) -> Any:
316
317
  assert self.connection_type is not None
@@ -330,10 +331,10 @@ class ConnectionExtension(FieldExtension):
330
331
  source: Any,
331
332
  info: Info,
332
333
  *,
333
- before: Optional[str] = None,
334
- after: Optional[str] = None,
335
- first: Optional[int] = None,
336
- last: Optional[int] = None,
334
+ before: str | None = None,
335
+ after: str | None = None,
336
+ first: int | None = None,
337
+ last: int | None = None,
337
338
  **kwargs: Any,
338
339
  ) -> Any:
339
340
  assert self.connection_type is not None
@@ -379,24 +380,24 @@ ConnectionGraphQLType = Any
379
380
 
380
381
 
381
382
  def connection(
382
- graphql_type: Optional[ConnectionGraphQLType] = None,
383
+ graphql_type: ConnectionGraphQLType | None = None,
383
384
  *,
384
- resolver: Optional[_RESOLVER_TYPE[Any]] = None,
385
- name: Optional[str] = None,
385
+ resolver: _RESOLVER_TYPE[Any] | None = None,
386
+ name: str | None = None,
386
387
  is_subscription: bool = False,
387
- description: Optional[str] = None,
388
- permission_classes: Optional[list[type[BasePermission]]] = None,
389
- deprecation_reason: Optional[str] = None,
388
+ description: str | None = None,
389
+ permission_classes: list[type[BasePermission]] | None = None,
390
+ deprecation_reason: str | None = None,
390
391
  default: Any = dataclasses.MISSING,
391
- default_factory: Union[Callable[..., object], object] = dataclasses.MISSING,
392
- metadata: Optional[Mapping[Any, Any]] = None,
393
- directives: Optional[Sequence[object]] = (),
392
+ default_factory: Callable[..., object] | object = dataclasses.MISSING,
393
+ metadata: Mapping[Any, Any] | None = None,
394
+ directives: Sequence[object] | None = (),
394
395
  extensions: list[FieldExtension] | None = None,
395
- max_results: Optional[int] = None,
396
+ max_results: int | None = None,
396
397
  # This init parameter is used by pyright to determine whether this field
397
398
  # is added in the constructor or not. It is not used to change
398
399
  # any behaviour at the moment.
399
- init: Literal[True, False, None] = None,
400
+ init: Literal[True, False] | None = None,
400
401
  ) -> Any:
401
402
  """Annotate a property or a method to create a relay connection field.
402
403
 
strawberry/relay/types.py CHANGED
@@ -19,13 +19,16 @@ from typing import (
19
19
  ClassVar,
20
20
  ForwardRef,
21
21
  Generic,
22
- Optional,
22
+ Literal,
23
+ TypeAlias,
23
24
  TypeVar,
24
25
  Union,
25
26
  cast,
27
+ get_args,
28
+ get_origin,
26
29
  overload,
27
30
  )
28
- from typing_extensions import Literal, Self, TypeAlias, get_args, get_origin
31
+ from typing_extensions import Self
29
32
 
30
33
  from strawberry.relay.exceptions import NodeIDAnnotationError
31
34
  from strawberry.types.base import (
@@ -56,12 +59,9 @@ if TYPE_CHECKING:
56
59
 
57
60
  _T = TypeVar("_T")
58
61
 
59
- NodeIterableType: TypeAlias = Union[
60
- Iterator[_T],
61
- Iterable[_T],
62
- AsyncIterator[_T],
63
- AsyncIterable[_T],
64
- ]
62
+ NodeIterableType: TypeAlias = (
63
+ Iterator[_T] | Iterable[_T] | AsyncIterator[_T] | AsyncIterable[_T]
64
+ )
65
65
  NodeType = TypeVar("NodeType", bound="Node")
66
66
 
67
67
  PREFIX = "arrayconnection"
@@ -110,7 +110,7 @@ class GlobalID:
110
110
  return to_base64(self.type_name, self.node_id)
111
111
 
112
112
  @classmethod
113
- def from_id(cls, value: Union[str, ID]) -> Self:
113
+ def from_id(cls, value: str | ID) -> Self:
114
114
  """Create a new GlobalID from parsing the given value.
115
115
 
116
116
  Args:
@@ -158,7 +158,7 @@ class GlobalID:
158
158
  *,
159
159
  required: bool = ...,
160
160
  ensure_type: None = ...,
161
- ) -> Optional[Node]: ...
161
+ ) -> Node | None: ...
162
162
 
163
163
  async def resolve_node(self, info, *, required=False, ensure_type=None) -> Any:
164
164
  """Resolve the type name and node id info to the node itself.
@@ -268,7 +268,7 @@ class GlobalID:
268
268
  *,
269
269
  required: bool = ...,
270
270
  ensure_type: None = ...,
271
- ) -> Optional[Node]: ...
271
+ ) -> Node | None: ...
272
272
 
273
273
  def resolve_node_sync(self, info, *, required=False, ensure_type=None) -> Any:
274
274
  """Resolve the type name and node id info to the node itself.
@@ -378,7 +378,7 @@ class Node:
378
378
  ```
379
379
  """
380
380
 
381
- _id_attr: ClassVar[Optional[str]] = None
381
+ _id_attr: ClassVar[str | None] = None
382
382
 
383
383
  @field(name="id", description="The Globally Unique ID of this object")
384
384
  @classmethod
@@ -513,7 +513,7 @@ class Node:
513
513
  info: Info,
514
514
  node_ids: Iterable[str],
515
515
  required: Literal[False] = ...,
516
- ) -> AwaitableOrValue[Iterable[Optional[Self]]]: ...
516
+ ) -> AwaitableOrValue[Iterable[Self | None]]: ...
517
517
 
518
518
  @overload
519
519
  @classmethod
@@ -523,10 +523,7 @@ class Node:
523
523
  info: Info,
524
524
  node_ids: Iterable[str],
525
525
  required: bool,
526
- ) -> Union[
527
- AwaitableOrValue[Iterable[Self]],
528
- AwaitableOrValue[Iterable[Optional[Self]]],
529
- ]: ...
526
+ ) -> AwaitableOrValue[Iterable[Self]] | AwaitableOrValue[Iterable[Self | None]]: ...
530
527
 
531
528
  @classmethod
532
529
  def resolve_nodes(
@@ -577,7 +574,7 @@ class Node:
577
574
  *,
578
575
  info: Info,
579
576
  required: Literal[False] = ...,
580
- ) -> AwaitableOrValue[Optional[Self]]: ...
577
+ ) -> AwaitableOrValue[Self | None]: ...
581
578
 
582
579
  @overload
583
580
  @classmethod
@@ -587,7 +584,7 @@ class Node:
587
584
  *,
588
585
  info: Info,
589
586
  required: bool,
590
- ) -> AwaitableOrValue[Optional[Self]]: ...
587
+ ) -> AwaitableOrValue[Self | None]: ...
591
588
 
592
589
  @classmethod
593
590
  def resolve_node(
@@ -596,7 +593,7 @@ class Node:
596
593
  *,
597
594
  info: Info,
598
595
  required: bool = False,
599
- ) -> AwaitableOrValue[Optional[Self]]:
596
+ ) -> AwaitableOrValue[Self | None]:
600
597
  """Resolve a node given its id.
601
598
 
602
599
  This method is a convenience method that calls `resolve_nodes` for
@@ -641,10 +638,10 @@ class PageInfo:
641
638
  has_previous_page: bool = field(
642
639
  description="When paginating backwards, are there more items?",
643
640
  )
644
- start_cursor: Optional[str] = field(
641
+ start_cursor: str | None = field(
645
642
  description="When paginating backwards, the cursor to continue.",
646
643
  )
647
- end_cursor: Optional[str] = field(
644
+ end_cursor: str | None = field(
648
645
  description="When paginating forwards, the cursor to continue.",
649
646
  )
650
647
 
@@ -715,11 +712,11 @@ class Connection(Generic[NodeType]):
715
712
  nodes: NodeIterableType[NodeType],
716
713
  *,
717
714
  info: Info,
718
- before: Optional[str] = None,
719
- after: Optional[str] = None,
720
- first: Optional[int] = None,
721
- last: Optional[int] = None,
722
- max_results: Optional[int] = None,
715
+ before: str | None = None,
716
+ after: str | None = None,
717
+ first: int | None = None,
718
+ last: int | None = None,
719
+ max_results: int | None = None,
723
720
  **kwargs: Any,
724
721
  ) -> AwaitableOrValue[Self]:
725
722
  """Resolve a connection from nodes.
@@ -762,16 +759,16 @@ class ListConnection(Connection[NodeType]):
762
759
  )
763
760
 
764
761
  @classmethod
765
- def resolve_connection( # noqa: PLR0915
762
+ def resolve_connection(
766
763
  cls,
767
764
  nodes: NodeIterableType[NodeType],
768
765
  *,
769
766
  info: Info,
770
- before: Optional[str] = None,
771
- after: Optional[str] = None,
772
- first: Optional[int] = None,
773
- last: Optional[int] = None,
774
- max_results: Optional[int] = None,
767
+ before: str | None = None,
768
+ after: str | None = None,
769
+ first: int | None = None,
770
+ last: int | None = None,
771
+ max_results: int | None = None,
775
772
  **kwargs: Any,
776
773
  ) -> AwaitableOrValue[Self]:
777
774
  """Resolve a connection from the list of nodes.
@@ -820,7 +817,7 @@ class ListConnection(Connection[NodeType]):
820
817
  async def resolver() -> Self:
821
818
  try:
822
819
  iterator = cast(
823
- "Union[AsyncIterator[NodeType], AsyncIterable[NodeType]]",
820
+ "AsyncIterator[NodeType] | AsyncIterable[NodeType]",
824
821
  cast("Sequence", nodes)[
825
822
  slice_metadata.start : slice_metadata.overfetch
826
823
  ],
@@ -886,7 +883,7 @@ class ListConnection(Connection[NodeType]):
886
883
 
887
884
  try:
888
885
  iterator = cast(
889
- "Union[Iterator[NodeType], Iterable[NodeType]]",
886
+ "Iterator[NodeType] | Iterable[NodeType]",
890
887
  cast("Sequence", nodes)[
891
888
  slice_metadata.start : slice_metadata.overfetch
892
889
  ],