strawberry-graphql 0.254.1__py3-none-any.whl → 0.256.0__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.
Files changed (155) hide show
  1. strawberry/__init__.py +9 -9
  2. strawberry/aiohttp/test/client.py +10 -8
  3. strawberry/aiohttp/views.py +5 -7
  4. strawberry/annotation.py +13 -16
  5. strawberry/asgi/__init__.py +3 -6
  6. strawberry/asgi/test/client.py +9 -8
  7. strawberry/chalice/views.py +4 -2
  8. strawberry/channels/__init__.py +1 -1
  9. strawberry/channels/handlers/base.py +3 -7
  10. strawberry/channels/handlers/http_handler.py +5 -6
  11. strawberry/channels/handlers/ws_handler.py +3 -4
  12. strawberry/channels/testing.py +5 -9
  13. strawberry/cli/commands/codegen.py +9 -9
  14. strawberry/cli/commands/upgrade/__init__.py +2 -3
  15. strawberry/cli/commands/upgrade/_run_codemod.py +7 -5
  16. strawberry/codegen/exceptions.py +2 -2
  17. strawberry/codegen/plugins/print_operation.py +6 -6
  18. strawberry/codegen/plugins/python.py +6 -6
  19. strawberry/codegen/plugins/typescript.py +3 -3
  20. strawberry/codegen/query_codegen.py +29 -34
  21. strawberry/codegen/types.py +35 -34
  22. strawberry/codemods/annotated_unions.py +5 -2
  23. strawberry/dataloader.py +13 -20
  24. strawberry/directive.py +12 -5
  25. strawberry/django/test/client.py +4 -4
  26. strawberry/django/views.py +4 -5
  27. strawberry/exceptions/__init__.py +24 -24
  28. strawberry/exceptions/conflicting_arguments.py +2 -2
  29. strawberry/exceptions/duplicated_type_name.py +3 -3
  30. strawberry/exceptions/handler.py +7 -7
  31. strawberry/exceptions/invalid_union_type.py +2 -2
  32. strawberry/exceptions/missing_arguments_annotations.py +2 -2
  33. strawberry/exceptions/missing_field_annotation.py +2 -2
  34. strawberry/exceptions/object_is_not_an_enum.py +2 -2
  35. strawberry/exceptions/private_strawberry_field.py +2 -2
  36. strawberry/exceptions/syntax.py +4 -4
  37. strawberry/exceptions/utils/source_finder.py +7 -6
  38. strawberry/experimental/pydantic/__init__.py +3 -3
  39. strawberry/experimental/pydantic/_compat.py +14 -14
  40. strawberry/experimental/pydantic/conversion.py +2 -2
  41. strawberry/experimental/pydantic/conversion_types.py +3 -3
  42. strawberry/experimental/pydantic/error_type.py +18 -16
  43. strawberry/experimental/pydantic/exceptions.py +5 -5
  44. strawberry/experimental/pydantic/fields.py +2 -13
  45. strawberry/experimental/pydantic/object_type.py +20 -22
  46. strawberry/experimental/pydantic/utils.py +6 -10
  47. strawberry/ext/dataclasses/dataclasses.py +3 -3
  48. strawberry/ext/mypy_plugin.py +6 -9
  49. strawberry/extensions/__init__.py +7 -8
  50. strawberry/extensions/add_validation_rules.py +5 -3
  51. strawberry/extensions/base_extension.py +4 -4
  52. strawberry/extensions/context.py +15 -14
  53. strawberry/extensions/directives.py +2 -2
  54. strawberry/extensions/disable_validation.py +1 -1
  55. strawberry/extensions/field_extension.py +2 -1
  56. strawberry/extensions/mask_errors.py +3 -2
  57. strawberry/extensions/max_aliases.py +2 -2
  58. strawberry/extensions/max_tokens.py +1 -1
  59. strawberry/extensions/parser_cache.py +2 -1
  60. strawberry/extensions/pyinstrument.py +4 -1
  61. strawberry/extensions/query_depth_limiter.py +13 -13
  62. strawberry/extensions/runner.py +7 -7
  63. strawberry/extensions/tracing/apollo.py +11 -9
  64. strawberry/extensions/tracing/datadog.py +3 -1
  65. strawberry/extensions/tracing/opentelemetry.py +7 -10
  66. strawberry/extensions/utils.py +3 -3
  67. strawberry/extensions/validation_cache.py +2 -1
  68. strawberry/fastapi/context.py +3 -3
  69. strawberry/fastapi/router.py +9 -14
  70. strawberry/federation/__init__.py +4 -4
  71. strawberry/federation/argument.py +2 -1
  72. strawberry/federation/enum.py +8 -8
  73. strawberry/federation/field.py +25 -28
  74. strawberry/federation/object_type.py +24 -26
  75. strawberry/federation/scalar.py +7 -8
  76. strawberry/federation/schema.py +30 -36
  77. strawberry/federation/schema_directive.py +5 -5
  78. strawberry/federation/schema_directives.py +14 -14
  79. strawberry/federation/union.py +3 -2
  80. strawberry/field_extensions/input_mutation.py +1 -2
  81. strawberry/file_uploads/utils.py +4 -3
  82. strawberry/flask/views.py +3 -2
  83. strawberry/http/__init__.py +6 -6
  84. strawberry/http/async_base_view.py +9 -14
  85. strawberry/http/base.py +5 -4
  86. strawberry/http/ides.py +1 -1
  87. strawberry/http/parse_content_type.py +1 -2
  88. strawberry/http/sync_base_view.py +3 -5
  89. strawberry/http/temporal_response.py +1 -2
  90. strawberry/http/types.py +3 -2
  91. strawberry/litestar/controller.py +8 -14
  92. strawberry/parent.py +1 -2
  93. strawberry/permission.py +6 -8
  94. strawberry/printer/ast_from_value.py +2 -1
  95. strawberry/printer/printer.py +50 -30
  96. strawberry/quart/views.py +3 -3
  97. strawberry/relay/exceptions.py +4 -4
  98. strawberry/relay/fields.py +43 -63
  99. strawberry/relay/types.py +29 -27
  100. strawberry/relay/utils.py +4 -4
  101. strawberry/sanic/utils.py +4 -4
  102. strawberry/sanic/views.py +5 -7
  103. strawberry/scalars.py +2 -2
  104. strawberry/schema/base.py +16 -11
  105. strawberry/schema/compat.py +4 -4
  106. strawberry/schema/execute.py +6 -10
  107. strawberry/schema/name_converter.py +3 -3
  108. strawberry/schema/schema.py +37 -25
  109. strawberry/schema/schema_converter.py +22 -24
  110. strawberry/schema/subscribe.py +5 -4
  111. strawberry/schema/types/base_scalars.py +1 -1
  112. strawberry/schema/types/concrete_type.py +2 -2
  113. strawberry/schema/types/scalar.py +3 -4
  114. strawberry/schema_codegen/__init__.py +4 -4
  115. strawberry/schema_directive.py +8 -8
  116. strawberry/subscriptions/protocols/graphql_transport_ws/handlers.py +8 -9
  117. strawberry/subscriptions/protocols/graphql_transport_ws/types.py +16 -16
  118. strawberry/subscriptions/protocols/graphql_ws/handlers.py +6 -5
  119. strawberry/subscriptions/protocols/graphql_ws/types.py +13 -13
  120. strawberry/test/__init__.py +1 -1
  121. strawberry/test/client.py +21 -19
  122. strawberry/tools/create_type.py +4 -3
  123. strawberry/tools/merge_types.py +1 -2
  124. strawberry/types/__init__.py +1 -1
  125. strawberry/types/arguments.py +10 -12
  126. strawberry/types/auto.py +2 -2
  127. strawberry/types/base.py +17 -21
  128. strawberry/types/enum.py +3 -5
  129. strawberry/types/execution.py +8 -12
  130. strawberry/types/field.py +26 -31
  131. strawberry/types/fields/resolver.py +15 -17
  132. strawberry/types/graphql.py +2 -2
  133. strawberry/types/info.py +5 -9
  134. strawberry/types/lazy_type.py +3 -5
  135. strawberry/types/mutation.py +25 -28
  136. strawberry/types/nodes.py +11 -9
  137. strawberry/types/object_type.py +14 -16
  138. strawberry/types/private.py +1 -2
  139. strawberry/types/scalar.py +2 -2
  140. strawberry/types/type_resolver.py +5 -5
  141. strawberry/types/union.py +8 -11
  142. strawberry/types/unset.py +3 -3
  143. strawberry/utils/aio.py +3 -8
  144. strawberry/utils/await_maybe.py +3 -2
  145. strawberry/utils/debug.py +2 -2
  146. strawberry/utils/deprecations.py +2 -2
  147. strawberry/utils/inspect.py +3 -5
  148. strawberry/utils/str_converters.py +1 -1
  149. strawberry/utils/typing.py +38 -67
  150. {strawberry_graphql-0.254.1.dist-info → strawberry_graphql-0.256.0.dist-info}/METADATA +3 -6
  151. strawberry_graphql-0.256.0.dist-info/RECORD +236 -0
  152. strawberry_graphql-0.254.1.dist-info/RECORD +0 -236
  153. {strawberry_graphql-0.254.1.dist-info → strawberry_graphql-0.256.0.dist-info}/LICENSE +0 -0
  154. {strawberry_graphql-0.254.1.dist-info → strawberry_graphql-0.256.0.dist-info}/WHEEL +0 -0
  155. {strawberry_graphql-0.254.1.dist-info → strawberry_graphql-0.256.0.dist-info}/entry_points.txt +0 -0
strawberry/http/ides.py CHANGED
@@ -22,4 +22,4 @@ def get_graphql_ide_html(
22
22
  return template
23
23
 
24
24
 
25
- __all__ = ["get_graphql_ide_html", "GraphQL_IDE"]
25
+ __all__ = ["GraphQL_IDE", "get_graphql_ide_html"]
@@ -1,8 +1,7 @@
1
1
  from email.message import Message
2
- from typing import Dict, Tuple
3
2
 
4
3
 
5
- def parse_content_type(content_type: str) -> Tuple[str, Dict[str, str]]:
4
+ def parse_content_type(content_type: str) -> tuple[str, dict[str, str]]:
6
5
  """Parse a content type header into a mime-type and a dictionary of parameters."""
7
6
  email = Message()
8
7
  email["content-type"] = content_type
@@ -1,12 +1,10 @@
1
1
  import abc
2
2
  import json
3
+ from collections.abc import Mapping
3
4
  from typing import (
4
5
  Any,
5
6
  Callable,
6
- Dict,
7
7
  Generic,
8
- List,
9
- Mapping,
10
8
  Optional,
11
9
  Union,
12
10
  )
@@ -126,7 +124,7 @@ class SyncBaseHTTPView(
126
124
  allowed_operation_types=allowed_operation_types,
127
125
  )
128
126
 
129
- def parse_multipart(self, request: SyncHTTPRequestAdapter) -> Dict[str, str]:
127
+ def parse_multipart(self, request: SyncHTTPRequestAdapter) -> dict[str, str]:
130
128
  operations = self.parse_json(request.post_data.get("operations", "{}"))
131
129
  files_map = self.parse_json(request.post_data.get("map", "{}"))
132
130
 
@@ -159,7 +157,7 @@ class SyncBaseHTTPView(
159
157
  )
160
158
 
161
159
  def _handle_errors(
162
- self, errors: List[GraphQLError], response_data: GraphQLHTTPResponse
160
+ self, errors: list[GraphQLError], response_data: GraphQLHTTPResponse
163
161
  ) -> None:
164
162
  """Hook to allow custom handling of errors, used by the Sentry Integration."""
165
163
 
@@ -1,11 +1,10 @@
1
1
  from dataclasses import dataclass, field
2
- from typing import Dict
3
2
 
4
3
 
5
4
  @dataclass
6
5
  class TemporalResponse:
7
6
  status_code: int = 200
8
- headers: Dict[str, str] = field(default_factory=dict)
7
+ headers: dict[str, str] = field(default_factory=dict)
9
8
 
10
9
 
11
10
  __all__ = ["TemporalResponse"]
strawberry/http/types.py CHANGED
@@ -1,4 +1,5 @@
1
- from typing import Any, Mapping, Optional
1
+ from collections.abc import Mapping
2
+ from typing import Any, Optional
2
3
  from typing_extensions import Literal, TypedDict
3
4
 
4
5
  HTTPMethod = Literal[
@@ -13,4 +14,4 @@ class FormData(TypedDict):
13
14
  form: Mapping[str, Any]
14
15
 
15
16
 
16
- __all__ = ["HTTPMethod", "QueryParams", "FormData"]
17
+ __all__ = ["FormData", "HTTPMethod", "QueryParams"]
@@ -8,14 +8,8 @@ from datetime import timedelta
8
8
  from typing import (
9
9
  TYPE_CHECKING,
10
10
  Any,
11
- AsyncGenerator,
12
- AsyncIterator,
13
11
  Callable,
14
- Dict,
15
- FrozenSet,
16
12
  Optional,
17
- Tuple,
18
- Type,
19
13
  TypedDict,
20
14
  Union,
21
15
  cast,
@@ -60,7 +54,7 @@ from strawberry.http.typevars import Context, RootValue
60
54
  from strawberry.subscriptions import GRAPHQL_TRANSPORT_WS_PROTOCOL, GRAPHQL_WS_PROTOCOL
61
55
 
62
56
  if TYPE_CHECKING:
63
- from collections.abc import Mapping
57
+ from collections.abc import AsyncGenerator, AsyncIterator, Mapping
64
58
 
65
59
  from litestar.types import AnyCallable, Dependencies
66
60
  from strawberry.http import GraphQLHTTPResponse
@@ -97,7 +91,7 @@ class WebSocketContextDict(TypedDict):
97
91
 
98
92
 
99
93
  MergedContext = Union[
100
- BaseContext, WebSocketContextDict, HTTPContextDict, Dict[str, Any]
94
+ BaseContext, WebSocketContextDict, HTTPContextDict, dict[str, Any]
101
95
  ]
102
96
 
103
97
 
@@ -254,11 +248,11 @@ class GraphQLController(
254
248
  websocket_adapter_class = LitestarWebSocketAdapter
255
249
 
256
250
  allow_queries_via_get: bool = True
257
- graphiql_allowed_accept: FrozenSet[str] = frozenset({"text/html", "*/*"})
251
+ graphiql_allowed_accept: frozenset[str] = frozenset({"text/html", "*/*"})
258
252
  graphql_ide: Optional[GraphQL_IDE] = "graphiql"
259
253
  debug: bool = False
260
254
  connection_init_wait_timeout: timedelta = timedelta(minutes=1)
261
- protocols: Tuple[str, ...] = (
255
+ protocols: tuple[str, ...] = (
262
256
  GRAPHQL_TRANSPORT_WS_PROTOCOL,
263
257
  GRAPHQL_WS_PROTOCOL,
264
258
  )
@@ -329,7 +323,7 @@ class GraphQLController(
329
323
  request: Request,
330
324
  stream: Callable[[], AsyncIterator[str]],
331
325
  sub_response: Response,
332
- headers: Dict[str, str],
326
+ headers: dict[str, str],
333
327
  ) -> Response:
334
328
  return Stream(
335
329
  stream(),
@@ -416,13 +410,13 @@ def make_graphql_controller(
416
410
  root_value_getter: Optional[AnyCallable] = None,
417
411
  # TODO: context typevar
418
412
  context_getter: Optional[AnyCallable] = None,
419
- subscription_protocols: Tuple[str, ...] = (
413
+ subscription_protocols: tuple[str, ...] = (
420
414
  GRAPHQL_TRANSPORT_WS_PROTOCOL,
421
415
  GRAPHQL_WS_PROTOCOL,
422
416
  ),
423
417
  connection_init_wait_timeout: timedelta = timedelta(minutes=1),
424
418
  multipart_uploads_enabled: bool = False,
425
- ) -> Type[GraphQLController]: # sourcery skip: move-assign
419
+ ) -> type[GraphQLController]: # sourcery skip: move-assign
426
420
  if context_getter is None:
427
421
  custom_context_getter_ = _none_custom_context_getter
428
422
  else:
@@ -474,6 +468,6 @@ def make_graphql_controller(
474
468
 
475
469
 
476
470
  __all__ = [
477
- "make_graphql_controller",
478
471
  "GraphQLController",
472
+ "make_graphql_controller",
479
473
  ]
strawberry/parent.py CHANGED
@@ -1,5 +1,4 @@
1
- from typing import TypeVar
2
- from typing_extensions import Annotated
1
+ from typing import Annotated, TypeVar
3
2
 
4
3
 
5
4
  class StrawberryParent: ...
strawberry/permission.py CHANGED
@@ -7,11 +7,7 @@ from inspect import iscoroutinefunction
7
7
  from typing import (
8
8
  TYPE_CHECKING,
9
9
  Any,
10
- Awaitable,
11
- Dict,
12
- List,
13
10
  Optional,
14
- Type,
15
11
  Union,
16
12
  )
17
13
 
@@ -25,6 +21,8 @@ from strawberry.types.base import StrawberryList, StrawberryOptional
25
21
  from strawberry.utils.await_maybe import await_maybe
26
22
 
27
23
  if TYPE_CHECKING:
24
+ from collections.abc import Awaitable
25
+
28
26
  from graphql import GraphQLError, GraphQLErrorExtensions
29
27
 
30
28
  from strawberry.extensions.field_extension import (
@@ -56,7 +54,7 @@ class BasePermission(abc.ABC):
56
54
 
57
55
  error_extensions: Optional[GraphQLErrorExtensions] = None
58
56
 
59
- error_class: Type[GraphQLError] = StrawberryGraphQLError
57
+ error_class: type[GraphQLError] = StrawberryGraphQLError
60
58
 
61
59
  _schema_directive: Optional[object] = None
62
60
 
@@ -138,7 +136,7 @@ class PermissionExtension(FieldExtension):
138
136
 
139
137
  def __init__(
140
138
  self,
141
- permissions: List[BasePermission],
139
+ permissions: list[BasePermission],
142
140
  use_directives: bool = True,
143
141
  fail_silently: bool = False,
144
142
  ) -> None:
@@ -189,7 +187,7 @@ class PermissionExtension(FieldExtension):
189
187
  next_: SyncExtensionResolver,
190
188
  source: Any,
191
189
  info: Info,
192
- **kwargs: Dict[str, Any],
190
+ **kwargs: dict[str, Any],
193
191
  ) -> Any:
194
192
  """Checks if the permission should be accepted and raises an exception if not."""
195
193
  for permission in self.permissions:
@@ -202,7 +200,7 @@ class PermissionExtension(FieldExtension):
202
200
  next_: AsyncExtensionResolver,
203
201
  source: Any,
204
202
  info: Info,
205
- **kwargs: Dict[str, Any],
203
+ **kwargs: dict[str, Any],
206
204
  ) -> Any:
207
205
  for permission in self.permissions:
208
206
  has_permission = await await_maybe(
@@ -1,8 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import re
4
+ from collections.abc import Mapping
4
5
  from math import isfinite
5
- from typing import TYPE_CHECKING, Any, Mapping, Optional, cast
6
+ from typing import TYPE_CHECKING, Any, Optional, cast
6
7
 
7
8
  from graphql.language import (
8
9
  BooleanValueNode,
@@ -5,19 +5,14 @@ from itertools import chain
5
5
  from typing import (
6
6
  TYPE_CHECKING,
7
7
  Any,
8
- Dict,
9
- List,
10
8
  Optional,
11
- Set,
12
- Tuple,
13
- Type,
14
9
  TypeVar,
15
10
  Union,
16
11
  cast,
17
12
  overload,
18
13
  )
19
14
 
20
- from graphql import is_union_type
15
+ from graphql import GraphQLObjectType, GraphQLSchema, is_union_type
21
16
  from graphql.language.printer import print_ast
22
17
  from graphql.type import (
23
18
  is_enum_type,
@@ -38,7 +33,11 @@ from graphql.utilities.print_schema import (
38
33
  from graphql.utilities.print_schema import print_type as original_print_type
39
34
 
40
35
  from strawberry.schema_directive import Location, StrawberrySchemaDirective
41
- from strawberry.types.base import StrawberryContainer, has_object_definition
36
+ from strawberry.types.base import (
37
+ StrawberryContainer,
38
+ StrawberryObjectDefinition,
39
+ has_object_definition,
40
+ )
42
41
  from strawberry.types.enum import EnumDefinition
43
42
  from strawberry.types.scalar import ScalarWrapper
44
43
  from strawberry.types.unset import UNSET
@@ -64,18 +63,18 @@ _T = TypeVar("_T")
64
63
 
65
64
  @dataclasses.dataclass
66
65
  class PrintExtras:
67
- directives: Set[str] = dataclasses.field(default_factory=set)
68
- types: Set[type] = dataclasses.field(default_factory=set)
66
+ directives: set[str] = dataclasses.field(default_factory=set)
67
+ types: set[type] = dataclasses.field(default_factory=set)
69
68
 
70
69
 
71
70
  @overload
72
- def _serialize_dataclasses(value: Dict[_T, object]) -> Dict[_T, object]: ...
71
+ def _serialize_dataclasses(value: dict[_T, object]) -> dict[_T, object]: ...
73
72
 
74
73
 
75
74
  @overload
76
75
  def _serialize_dataclasses(
77
- value: Union[List[object], Tuple[object]],
78
- ) -> List[object]: ...
76
+ value: Union[list[object], tuple[object]],
77
+ ) -> list[object]: ...
79
78
 
80
79
 
81
80
  @overload
@@ -94,7 +93,7 @@ def _serialize_dataclasses(value):
94
93
 
95
94
 
96
95
  def print_schema_directive_params(
97
- directive: GraphQLDirective, values: Dict[str, Any]
96
+ directive: GraphQLDirective, values: dict[str, Any]
98
97
  ) -> str:
99
98
  params = []
100
99
  for name, arg in directive.args.items():
@@ -189,7 +188,7 @@ def print_argument_directives(
189
188
 
190
189
 
191
190
  def print_args(
192
- args: Dict[str, GraphQLArgument],
191
+ args: dict[str, GraphQLArgument],
193
192
  indentation: str = "",
194
193
  *,
195
194
  schema: BaseSchema,
@@ -225,7 +224,12 @@ def print_args(
225
224
  )
226
225
 
227
226
 
228
- def print_fields(type_: Type, schema: BaseSchema, *, extras: PrintExtras) -> str:
227
+ def print_fields(
228
+ type_: GraphQLObjectType,
229
+ schema: BaseSchema,
230
+ *,
231
+ extras: PrintExtras,
232
+ ) -> str:
229
233
  from strawberry.schema.schema_converter import GraphQLCoreConverter
230
234
 
231
235
  fields = []
@@ -320,11 +324,13 @@ def print_enum(
320
324
  )
321
325
 
322
326
 
323
- def print_extends(type_: Type, schema: BaseSchema) -> str:
327
+ def print_extends(type_: GraphQLObjectType, schema: BaseSchema) -> str:
324
328
  from strawberry.schema.schema_converter import GraphQLCoreConverter
325
329
 
326
- strawberry_type = type_.extensions and type_.extensions.get(
327
- GraphQLCoreConverter.DEFINITION_BACKREF
330
+ strawberry_type = cast(
331
+ Optional[StrawberryObjectDefinition],
332
+ type_.extensions
333
+ and type_.extensions.get(GraphQLCoreConverter.DEFINITION_BACKREF),
328
334
  )
329
335
 
330
336
  if strawberry_type and strawberry_type.extend:
@@ -334,12 +340,14 @@ def print_extends(type_: Type, schema: BaseSchema) -> str:
334
340
 
335
341
 
336
342
  def print_type_directives(
337
- type_: Type, schema: BaseSchema, *, extras: PrintExtras
343
+ type_: GraphQLObjectType, schema: BaseSchema, *, extras: PrintExtras
338
344
  ) -> str:
339
345
  from strawberry.schema.schema_converter import GraphQLCoreConverter
340
346
 
341
- strawberry_type = type_.extensions and type_.extensions.get(
342
- GraphQLCoreConverter.DEFINITION_BACKREF
347
+ strawberry_type = cast(
348
+ Optional[StrawberryObjectDefinition],
349
+ type_.extensions
350
+ and type_.extensions.get(GraphQLCoreConverter.DEFINITION_BACKREF),
343
351
  )
344
352
 
345
353
  if not strawberry_type:
@@ -354,7 +362,7 @@ def print_type_directives(
354
362
  for directive in strawberry_type.directives or []
355
363
  if any(
356
364
  location in allowed_locations
357
- for location in directive.__strawberry_directive__.locations
365
+ for location in directive.__strawberry_directive__.locations # type: ignore[attr-defined]
358
366
  )
359
367
  )
360
368
 
@@ -550,21 +558,33 @@ def is_builtin_directive(directive: GraphQLDirective) -> bool:
550
558
 
551
559
 
552
560
  def print_schema(schema: BaseSchema) -> str:
553
- graphql_core_schema = schema._schema # type: ignore
561
+ graphql_core_schema = cast(
562
+ GraphQLSchema,
563
+ schema._schema, # type: ignore
564
+ )
554
565
  extras = PrintExtras()
555
566
 
556
- directives = filter(
557
- lambda n: not is_builtin_directive(n), graphql_core_schema.directives
558
- )
567
+ filtered_directives = [
568
+ directive
569
+ for directive in graphql_core_schema.directives
570
+ if not is_builtin_directive(directive)
571
+ ]
572
+
559
573
  type_map = graphql_core_schema.type_map
560
- types = filter(is_defined_type, map(type_map.get, sorted(type_map)))
574
+ types = [
575
+ type_
576
+ for type_name in sorted(type_map)
577
+ if is_defined_type(type_ := type_map[type_name])
578
+ ]
561
579
 
562
580
  types_printed = [_print_type(type_, schema, extras=extras) for type_ in types]
563
581
  schema_definition = print_schema_definition(schema, extras=extras)
564
582
 
565
- directives = filter(
566
- None, [print_directive(directive, schema=schema) for directive in directives]
567
- )
583
+ directives = [
584
+ printed_directive
585
+ for directive in filtered_directives
586
+ if (printed_directive := print_directive(directive, schema=schema)) is not None
587
+ ]
568
588
 
569
589
  def _name_getter(type_: Any) -> str:
570
590
  if hasattr(type_, "name"):
strawberry/quart/views.py CHANGED
@@ -1,6 +1,6 @@
1
1
  import warnings
2
- from collections.abc import Mapping
3
- from typing import TYPE_CHECKING, AsyncGenerator, Callable, Dict, Optional, cast
2
+ from collections.abc import AsyncGenerator, Mapping
3
+ from typing import TYPE_CHECKING, Callable, Optional, cast
4
4
  from typing_extensions import TypeGuard
5
5
 
6
6
  from quart import Request, Response, request
@@ -111,7 +111,7 @@ class GraphQLView(
111
111
  request: Request,
112
112
  stream: Callable[[], AsyncGenerator[str, None]],
113
113
  sub_response: Response,
114
- headers: Dict[str, str],
114
+ headers: dict[str, str],
115
115
  ) -> Response:
116
116
  return (
117
117
  stream(),
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  from collections.abc import Callable
4
4
  from functools import cached_property
5
- from typing import TYPE_CHECKING, Optional, Type, cast
5
+ from typing import TYPE_CHECKING, Optional, cast
6
6
 
7
7
  from strawberry.exceptions.exception import StrawberryException
8
8
  from strawberry.exceptions.utils.source_finder import SourceFinder
@@ -13,7 +13,7 @@ if TYPE_CHECKING:
13
13
 
14
14
 
15
15
  class NodeIDAnnotationError(StrawberryException):
16
- def __init__(self, message: str, cls: Type) -> None:
16
+ def __init__(self, message: str, cls: type) -> None:
17
17
  self.cls = cls
18
18
 
19
19
  self.message = message
@@ -41,7 +41,7 @@ class NodeIDAnnotationError(StrawberryException):
41
41
 
42
42
 
43
43
  class RelayWrongAnnotationError(StrawberryException):
44
- def __init__(self, field_name: str, cls: Type) -> None:
44
+ def __init__(self, field_name: str, cls: type) -> None:
45
45
  self.cls = cls
46
46
  self.field_name = field_name
47
47
 
@@ -85,7 +85,7 @@ class RelayWrongResolverAnnotationError(StrawberryException):
85
85
  )
86
86
  self.suggestion = (
87
87
  "To fix this error you can annootate your resolver to return "
88
- "one of the following options: `List[<NodeType>]`, "
88
+ "one of the following options: `list[<NodeType>]`, "
89
89
  "`Iterator[<NodeType>]`, `Iterable[<NodeType>]`, "
90
90
  "`AsyncIterator[<NodeType>]`, `AsyncIterable[<NodeType>]`, "
91
91
  "`Generator[<NodeType>, Any, Any]` and "
@@ -4,29 +4,26 @@ import asyncio
4
4
  import dataclasses
5
5
  import inspect
6
6
  from collections import defaultdict
7
- from collections.abc import AsyncIterable
8
- from typing import (
9
- TYPE_CHECKING,
10
- Any,
7
+ from collections.abc import (
8
+ AsyncIterable,
11
9
  AsyncIterator,
12
10
  Awaitable,
13
- Callable,
14
- DefaultDict,
15
- Dict,
16
- ForwardRef,
17
11
  Iterable,
18
12
  Iterator,
19
- List,
20
13
  Mapping,
21
- Optional,
22
14
  Sequence,
23
- Tuple,
24
- Type,
15
+ )
16
+ from typing import (
17
+ TYPE_CHECKING,
18
+ Annotated,
19
+ Any,
20
+ Callable,
21
+ ForwardRef,
22
+ Optional,
25
23
  Union,
26
24
  cast,
27
- overload,
28
25
  )
29
- from typing_extensions import Annotated, get_origin
26
+ from typing_extensions import get_args, get_origin
30
27
 
31
28
  from strawberry.annotation import StrawberryAnnotation
32
29
  from strawberry.extensions.field_extension import (
@@ -44,9 +41,9 @@ from strawberry.types.field import _RESOLVER_TYPE, StrawberryField, field
44
41
  from strawberry.types.fields.resolver import StrawberryResolver
45
42
  from strawberry.types.lazy_type import LazyType
46
43
  from strawberry.utils.aio import asyncgen_to_list
47
- from strawberry.utils.typing import eval_type, is_generic_alias
44
+ from strawberry.utils.typing import eval_type, is_generic_alias, is_optional, is_union
48
45
 
49
- from .types import Connection, GlobalID, Node, NodeIterableType, NodeType
46
+ from .types import Connection, GlobalID, Node
50
47
 
51
48
  if TYPE_CHECKING:
52
49
  from typing_extensions import Literal
@@ -101,7 +98,7 @@ class NodeExtension(FieldExtension):
101
98
 
102
99
  def get_node_list_resolver(
103
100
  self, field: StrawberryField
104
- ) -> Callable[[Info, List[GlobalID]], Union[List[Node], Awaitable[List[Node]]]]:
101
+ ) -> Callable[[Info, list[GlobalID]], Union[list[Node], Awaitable[list[Node]]]]:
105
102
  type_ = field.type
106
103
  assert isinstance(type_, StrawberryList)
107
104
  is_optional = isinstance(type_.of_type, StrawberryOptional)
@@ -109,14 +106,14 @@ class NodeExtension(FieldExtension):
109
106
  def resolver(
110
107
  info: Info,
111
108
  ids: Annotated[
112
- List[GlobalID], argument(description="The IDs of the objects.")
109
+ list[GlobalID], argument(description="The IDs of the objects.")
113
110
  ],
114
- ) -> Union[List[Node], Awaitable[List[Node]]]:
115
- nodes_map: DefaultDict[Type[Node], List[str]] = defaultdict(list)
111
+ ) -> Union[list[Node], Awaitable[list[Node]]]:
112
+ nodes_map: defaultdict[type[Node], list[str]] = defaultdict(list)
116
113
  # Store the index of the node in the list of nodes of the same type
117
114
  # so that we can return them in the same order while also supporting
118
115
  # different types
119
- index_map: Dict[GlobalID, Tuple[Type[Node], int]] = {}
116
+ index_map: dict[GlobalID, tuple[type[Node], int]] = {}
120
117
  for gid in ids:
121
118
  node_t = gid.resolve_type(info)
122
119
  nodes_map[node_t].append(gid.node_id)
@@ -144,7 +141,7 @@ class NodeExtension(FieldExtension):
144
141
 
145
142
  if awaitable_nodes or asyncgen_nodes:
146
143
 
147
- async def resolve(resolved: Any = resolved_nodes) -> List[Node]:
144
+ async def resolve(resolved: Any = resolved_nodes) -> list[Node]:
148
145
  resolved.update(
149
146
  zip(
150
147
  [
@@ -183,7 +180,7 @@ class NodeExtension(FieldExtension):
183
180
 
184
181
 
185
182
  class ConnectionExtension(FieldExtension):
186
- connection_type: Type[Connection[Node]]
183
+ connection_type: type[Connection[Node]]
187
184
 
188
185
  def apply(self, field: StrawberryField) -> None:
189
186
  field.arguments = [
@@ -233,7 +230,11 @@ class ConnectionExtension(FieldExtension):
233
230
  f_type = f_type.resolve_type()
234
231
  field.type = f_type
235
232
 
233
+ if isinstance(f_type, StrawberryOptional):
234
+ f_type = f_type.of_type
235
+
236
236
  type_origin = get_origin(f_type) if is_generic_alias(f_type) else f_type
237
+
237
238
  if not isinstance(type_origin, type) or not issubclass(type_origin, Connection):
238
239
  raise RelayWrongAnnotationError(field.name, cast(type, field.origin))
239
240
 
@@ -253,13 +254,19 @@ class ConnectionExtension(FieldExtension):
253
254
  None,
254
255
  )
255
256
 
257
+ if is_union(resolver_type):
258
+ assert is_optional(resolver_type)
259
+
260
+ resolver_type = get_args(resolver_type)[0]
261
+
256
262
  origin = get_origin(resolver_type)
263
+
257
264
  if origin is None or not issubclass(
258
265
  origin, (Iterator, Iterable, AsyncIterator, AsyncIterable)
259
266
  ):
260
267
  raise RelayWrongResolverAnnotationError(field.name, field.base_resolver)
261
268
 
262
- self.connection_type = cast(Type[Connection[Node]], field.type)
269
+ self.connection_type = cast(type[Connection[Node]], f_type)
263
270
 
264
271
  def resolve(
265
272
  self,
@@ -327,59 +334,32 @@ else:
327
334
  return field(*args, **kwargs)
328
335
 
329
336
 
330
- @overload
331
- def connection(
332
- graphql_type: Optional[Type[Connection[NodeType]]] = None,
333
- *,
334
- resolver: Optional[_RESOLVER_TYPE[NodeIterableType[Any]]] = None,
335
- name: Optional[str] = None,
336
- is_subscription: bool = False,
337
- description: Optional[str] = None,
338
- init: Literal[True] = True,
339
- permission_classes: Optional[List[Type[BasePermission]]] = None,
340
- deprecation_reason: Optional[str] = None,
341
- default: Any = dataclasses.MISSING,
342
- default_factory: Union[Callable[..., object], object] = dataclasses.MISSING,
343
- metadata: Optional[Mapping[Any, Any]] = None,
344
- directives: Optional[Sequence[object]] = (),
345
- extensions: List[FieldExtension] = (), # type: ignore
346
- ) -> Any: ...
347
-
348
-
349
- @overload
350
- def connection(
351
- graphql_type: Optional[Type[Connection[NodeType]]] = None,
352
- *,
353
- name: Optional[str] = None,
354
- is_subscription: bool = False,
355
- description: Optional[str] = None,
356
- permission_classes: Optional[List[Type[BasePermission]]] = None,
357
- deprecation_reason: Optional[str] = None,
358
- default: Any = dataclasses.MISSING,
359
- default_factory: Union[Callable[..., object], object] = dataclasses.MISSING,
360
- metadata: Optional[Mapping[Any, Any]] = None,
361
- directives: Optional[Sequence[object]] = (),
362
- extensions: List[FieldExtension] = (), # type: ignore
363
- ) -> StrawberryField: ...
337
+ # we used to have `Type[Connection[NodeType]]` here, but that when we added
338
+ # support for making the Connection type optional, we had to change it to
339
+ # `Any` because otherwise it wouldn't be type check since `Optional[Connection[Something]]`
340
+ # is not a `Type`, but a special form, see https://discuss.python.org/t/is-annotated-compatible-with-type-t/43898/46
341
+ # for more information, and also https://peps.python.org/pep-0747/, which is currently
342
+ # in draft status (and no type checker supports it yet)
343
+ ConnectionGraphQLType = Any
364
344
 
365
345
 
366
346
  def connection(
367
- graphql_type: Optional[Type[Connection[NodeType]]] = None,
347
+ graphql_type: Optional[ConnectionGraphQLType] = None,
368
348
  *,
369
349
  resolver: Optional[_RESOLVER_TYPE[Any]] = None,
370
350
  name: Optional[str] = None,
371
351
  is_subscription: bool = False,
372
352
  description: Optional[str] = None,
373
- permission_classes: Optional[List[Type[BasePermission]]] = None,
353
+ permission_classes: Optional[list[type[BasePermission]]] = None,
374
354
  deprecation_reason: Optional[str] = None,
375
355
  default: Any = dataclasses.MISSING,
376
356
  default_factory: Union[Callable[..., object], object] = dataclasses.MISSING,
377
357
  metadata: Optional[Mapping[Any, Any]] = None,
378
358
  directives: Optional[Sequence[object]] = (),
379
- extensions: List[FieldExtension] = (), # type: ignore
359
+ extensions: list[FieldExtension] = (), # type: ignore
380
360
  # This init parameter is used by pyright to determine whether this field
381
361
  # is added in the constructor or not. It is not used to change
382
- # any behavior at the moment.
362
+ # any behaviour at the moment.
383
363
  init: Literal[True, False, None] = None,
384
364
  ) -> Any:
385
365
  """Annotate a property or a method to create a relay connection field.
@@ -478,4 +458,4 @@ def connection(
478
458
  return f
479
459
 
480
460
 
481
- __all__ = ["node", "connection"]
461
+ __all__ = ["connection", "node"]