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
@@ -0,0 +1,118 @@
1
+ from __future__ import annotations
2
+
3
+ import libcst as cst
4
+ import libcst.matchers as m
5
+ from libcst._nodes.expression import BaseExpression # noqa: TC002
6
+ from libcst.codemod import CodemodContext, VisitorBasedCodemodCommand
7
+ from libcst.codemod.visitors import AddImportsVisitor
8
+
9
+
10
+ class ConvertMaybeToOptional(VisitorBasedCodemodCommand):
11
+ DESCRIPTION: str = (
12
+ "Converts strawberry.Maybe[T] to strawberry.Maybe[T | None] "
13
+ "to match the new Maybe type definition"
14
+ )
15
+
16
+ def __init__(
17
+ self,
18
+ context: CodemodContext,
19
+ use_pipe_syntax: bool = True, # Default to pipe syntax for modern Python
20
+ ) -> None:
21
+ self.use_pipe_syntax = use_pipe_syntax
22
+ super().__init__(context)
23
+
24
+ @m.leave(
25
+ m.Subscript(
26
+ value=m.Attribute(value=m.Name("strawberry"), attr=m.Name("Maybe"))
27
+ | m.Name("Maybe")
28
+ )
29
+ )
30
+ def leave_maybe_subscript(
31
+ self, original_node: cst.Subscript, updated_node: cst.Subscript
32
+ ) -> BaseExpression:
33
+ # Skip if it's not a strawberry.Maybe or imported Maybe
34
+ if isinstance(original_node.value, cst.Name):
35
+ # Check if this is an imported Maybe from strawberry
36
+ # For now, we'll assume any standalone "Maybe" is from strawberry
37
+ # In a more robust implementation, we'd track imports
38
+ pass
39
+
40
+ # Get the inner type
41
+ if isinstance(original_node.slice, (list, tuple)):
42
+ if len(original_node.slice) != 1:
43
+ return original_node
44
+ slice_element = original_node.slice[0]
45
+ else:
46
+ slice_element = original_node.slice
47
+
48
+ if not isinstance(slice_element, cst.SubscriptElement):
49
+ return original_node
50
+
51
+ if not isinstance(slice_element.slice, cst.Index):
52
+ return original_node
53
+
54
+ inner_type = slice_element.slice.value
55
+
56
+ # Check if the inner type already includes None
57
+ if self._already_includes_none(inner_type):
58
+ return original_node
59
+
60
+ # Create the new union type with None
61
+ new_type: BaseExpression
62
+ if self.use_pipe_syntax:
63
+ new_type = cst.BinaryOperation(
64
+ left=inner_type,
65
+ operator=cst.BitOr(
66
+ whitespace_before=cst.SimpleWhitespace(" "),
67
+ whitespace_after=cst.SimpleWhitespace(" "),
68
+ ),
69
+ right=cst.Name("None"),
70
+ )
71
+ else:
72
+ # Use Union[T, None] syntax
73
+ AddImportsVisitor.add_needed_import(self.context, "typing", "Union")
74
+ new_type = cst.Subscript(
75
+ value=cst.Name("Union"),
76
+ slice=[
77
+ cst.SubscriptElement(slice=cst.Index(value=inner_type)),
78
+ cst.SubscriptElement(slice=cst.Index(value=cst.Name("None"))),
79
+ ],
80
+ )
81
+
82
+ # Return the updated Maybe[T | None]
83
+ return updated_node.with_changes(
84
+ slice=[cst.SubscriptElement(slice=cst.Index(value=new_type))]
85
+ )
86
+
87
+ def _already_includes_none(self, node: BaseExpression) -> bool:
88
+ """Check if the type already includes None (e.g., T | None or Union[T, None])."""
89
+ # Check for T | None pattern
90
+ if isinstance(node, cst.BinaryOperation) and isinstance(
91
+ node.operator, cst.BitOr
92
+ ):
93
+ if isinstance(node.right, cst.Name) and node.right.value == "None":
94
+ return True
95
+ # Recursively check left side for chained unions
96
+ if self._already_includes_none(node.left):
97
+ return True
98
+
99
+ # Check for Union[..., None] pattern
100
+ if (
101
+ isinstance(node, cst.Subscript)
102
+ and isinstance(node.value, cst.Name)
103
+ and node.value.value == "Union"
104
+ ):
105
+ # Handle both list and tuple slice formats
106
+ slice_elements = (
107
+ node.slice if isinstance(node.slice, (list, tuple)) else [node.slice]
108
+ )
109
+ for element in slice_elements:
110
+ if (
111
+ isinstance(element, cst.SubscriptElement)
112
+ and isinstance(element.slice, cst.Index)
113
+ and isinstance(element.slice.value, cst.Name)
114
+ and element.slice.value.value == "None"
115
+ ):
116
+ return True
117
+
118
+ return False
strawberry/dataloader.py CHANGED
@@ -8,11 +8,8 @@ from dataclasses import dataclass
8
8
  from typing import (
9
9
  TYPE_CHECKING,
10
10
  Any,
11
- Callable,
12
11
  Generic,
13
- Optional,
14
12
  TypeVar,
15
- Union,
16
13
  overload,
17
14
  )
18
15
 
@@ -20,7 +17,14 @@ from .exceptions import WrongNumberOfResultsReturned
20
17
 
21
18
  if TYPE_CHECKING:
22
19
  from asyncio.events import AbstractEventLoop
23
- from collections.abc import Awaitable, Hashable, Iterable, Mapping, Sequence
20
+ from collections.abc import (
21
+ Awaitable,
22
+ Callable,
23
+ Hashable,
24
+ Iterable,
25
+ Mapping,
26
+ Sequence,
27
+ )
24
28
 
25
29
 
26
30
  T = TypeVar("T")
@@ -46,9 +50,9 @@ class Batch(Generic[K, T]):
46
50
  return len(self.tasks)
47
51
 
48
52
 
49
- class AbstractCache(Generic[K, T], ABC):
53
+ class AbstractCache(ABC, Generic[K, T]):
50
54
  @abstractmethod
51
- def get(self, key: K) -> Union[Future[T], None]:
55
+ def get(self, key: K) -> Future[T] | None:
52
56
  pass
53
57
 
54
58
  @abstractmethod
@@ -65,13 +69,13 @@ class AbstractCache(Generic[K, T], ABC):
65
69
 
66
70
 
67
71
  class DefaultCache(AbstractCache[K, T]):
68
- def __init__(self, cache_key_fn: Optional[Callable[[K], Hashable]] = None) -> None:
72
+ def __init__(self, cache_key_fn: Callable[[K], Hashable] | None = None) -> None:
69
73
  self.cache_key_fn: Callable[[K], Hashable] = (
70
74
  cache_key_fn if cache_key_fn is not None else lambda x: x
71
75
  )
72
76
  self.cache_map: dict[Hashable, Future[T]] = {}
73
77
 
74
- def get(self, key: K) -> Union[Future[T], None]:
78
+ def get(self, key: K) -> Future[T] | None:
75
79
  return self.cache_map.get(self.cache_key_fn(key))
76
80
 
77
81
  def set(self, key: K, value: Future[T]) -> None:
@@ -85,7 +89,7 @@ class DefaultCache(AbstractCache[K, T]):
85
89
 
86
90
 
87
91
  class DataLoader(Generic[K, T]):
88
- batch: Optional[Batch[K, T]] = None
92
+ batch: Batch[K, T] | None = None
89
93
  cache: bool = False
90
94
  cache_map: AbstractCache[K, T]
91
95
 
@@ -93,12 +97,12 @@ class DataLoader(Generic[K, T]):
93
97
  def __init__(
94
98
  self,
95
99
  # any BaseException is rethrown in 'load', so should be excluded from the T type
96
- load_fn: Callable[[list[K]], Awaitable[Sequence[Union[T, BaseException]]]],
97
- max_batch_size: Optional[int] = None,
100
+ load_fn: Callable[[list[K]], Awaitable[Sequence[T | BaseException]]],
101
+ max_batch_size: int | None = None,
98
102
  cache: bool = True,
99
- loop: Optional[AbstractEventLoop] = None,
100
- cache_map: Optional[AbstractCache[K, T]] = None,
101
- cache_key_fn: Optional[Callable[[K], Hashable]] = None,
103
+ loop: AbstractEventLoop | None = None,
104
+ cache_map: AbstractCache[K, T] | None = None,
105
+ cache_key_fn: Callable[[K], Hashable] | None = None,
102
106
  ) -> None: ...
103
107
 
104
108
  # fallback if load_fn is untyped and there's no other info for inference
@@ -106,21 +110,21 @@ class DataLoader(Generic[K, T]):
106
110
  def __init__(
107
111
  self: DataLoader[K, Any],
108
112
  load_fn: Callable[[list[K]], Awaitable[list[Any]]],
109
- max_batch_size: Optional[int] = None,
113
+ max_batch_size: int | None = None,
110
114
  cache: bool = True,
111
- loop: Optional[AbstractEventLoop] = None,
112
- cache_map: Optional[AbstractCache[K, T]] = None,
113
- cache_key_fn: Optional[Callable[[K], Hashable]] = None,
115
+ loop: AbstractEventLoop | None = None,
116
+ cache_map: AbstractCache[K, T] | None = None,
117
+ cache_key_fn: Callable[[K], Hashable] | None = None,
114
118
  ) -> None: ...
115
119
 
116
120
  def __init__(
117
121
  self,
118
- load_fn: Callable[[list[K]], Awaitable[Sequence[Union[T, BaseException]]]],
119
- max_batch_size: Optional[int] = None,
122
+ load_fn: Callable[[list[K]], Awaitable[Sequence[T | BaseException]]],
123
+ max_batch_size: int | None = None,
120
124
  cache: bool = True,
121
- loop: Optional[AbstractEventLoop] = None,
122
- cache_map: Optional[AbstractCache[K, T]] = None,
123
- cache_key_fn: Optional[Callable[[K], Hashable]] = None,
125
+ loop: AbstractEventLoop | None = None,
126
+ cache_map: AbstractCache[K, T] | None = None,
127
+ cache_key_fn: Callable[[K], Hashable] | None = None,
124
128
  ):
125
129
  self.load_fn = load_fn
126
130
  self.max_batch_size = max_batch_size
@@ -244,7 +248,7 @@ async def dispatch_batch(loader: DataLoader, batch: Batch) -> None:
244
248
  expected=len(batch), received=len(values)
245
249
  )
246
250
 
247
- for task, value in zip(batch.tasks, values):
251
+ for task, value in zip(batch.tasks, values, strict=True):
248
252
  # Trying to set_result in a cancelled future would raise
249
253
  # asyncio.exceptions.InvalidStateError
250
254
  if task.future.cancelled():
strawberry/directive.py CHANGED
@@ -6,9 +6,7 @@ from typing import (
6
6
  TYPE_CHECKING,
7
7
  Annotated,
8
8
  Any,
9
- Callable,
10
9
  Generic,
11
- Optional,
12
10
  TypeVar,
13
11
  )
14
12
 
@@ -24,6 +22,7 @@ from strawberry.types.unset import UNSET
24
22
 
25
23
  if TYPE_CHECKING:
26
24
  import inspect
25
+ from collections.abc import Callable
27
26
 
28
27
  from strawberry.types.arguments import StrawberryArgument
29
28
 
@@ -82,17 +81,17 @@ class StrawberryDirectiveResolver(StrawberryResolver[T]):
82
81
  )
83
82
 
84
83
  @cached_property
85
- def value_parameter(self) -> Optional[inspect.Parameter]:
84
+ def value_parameter(self) -> inspect.Parameter | None:
86
85
  return self.reserved_parameters.get(VALUE_PARAMSPEC)
87
86
 
88
87
 
89
88
  @dataclasses.dataclass
90
89
  class StrawberryDirective(Generic[T]):
91
90
  python_name: str
92
- graphql_name: Optional[str]
91
+ graphql_name: str | None
93
92
  resolver: StrawberryDirectiveResolver[T]
94
93
  locations: list[DirectiveLocation]
95
- description: Optional[str] = None
94
+ description: str | None = None
96
95
 
97
96
  @cached_property
98
97
  def arguments(self) -> list[StrawberryArgument]:
@@ -102,8 +101,8 @@ class StrawberryDirective(Generic[T]):
102
101
  def directive(
103
102
  *,
104
103
  locations: list[DirectiveLocation],
105
- description: Optional[str] = None,
106
- name: Optional[str] = None,
104
+ description: str | None = None,
105
+ name: str | None = None,
107
106
  ) -> Callable[[Callable[..., T]], StrawberryDirective[T]]:
108
107
  """Decorator to create a GraphQL operation directive.
109
108
 
@@ -1,4 +1,4 @@
1
- from typing import Any, Optional
1
+ from typing import Any
2
2
 
3
3
  from strawberry.test import BaseGraphQLTestClient
4
4
 
@@ -7,8 +7,8 @@ class GraphQLTestClient(BaseGraphQLTestClient):
7
7
  def request(
8
8
  self,
9
9
  body: dict[str, object],
10
- headers: Optional[dict[str, object]] = None,
11
- files: Optional[dict[str, object]] = None,
10
+ headers: dict[str, object] | None = None,
11
+ files: dict[str, object] | None = None,
12
12
  ) -> Any:
13
13
  if files:
14
14
  return self._client.post(
@@ -5,12 +5,8 @@ import warnings
5
5
  from typing import (
6
6
  TYPE_CHECKING,
7
7
  Any,
8
- Callable,
9
- Optional,
10
- Union,
11
- cast,
8
+ TypeGuard,
12
9
  )
13
- from typing_extensions import TypeGuard
14
10
 
15
11
  from asgiref.sync import markcoroutinefunction
16
12
  from django.core.serializers.json import DjangoJSONEncoder
@@ -26,11 +22,10 @@ from django.template.exceptions import TemplateDoesNotExist
26
22
  from django.template.loader import render_to_string
27
23
  from django.utils.decorators import classonlymethod
28
24
  from django.views.generic import View
25
+ from lia import AsyncDjangoHTTPRequestAdapter, DjangoHTTPRequestAdapter, HTTPException
29
26
 
30
- from strawberry.http.async_base_view import AsyncBaseHTTPView, AsyncHTTPRequestAdapter
31
- from strawberry.http.exceptions import HTTPException
32
- from strawberry.http.sync_base_view import SyncBaseHTTPView, SyncHTTPRequestAdapter
33
- from strawberry.http.types import FormData, HTTPMethod, QueryParams
27
+ from strawberry.http.async_base_view import AsyncBaseHTTPView
28
+ from strawberry.http.sync_base_view import SyncBaseHTTPView
34
29
  from strawberry.http.typevars import (
35
30
  Context,
36
31
  RootValue,
@@ -39,7 +34,7 @@ from strawberry.http.typevars import (
39
34
  from .context import StrawberryDjangoContext
40
35
 
41
36
  if TYPE_CHECKING:
42
- from collections.abc import AsyncIterator, Mapping
37
+ from collections.abc import AsyncIterator, Callable
43
38
 
44
39
  from django.template.response import TemplateResponse
45
40
 
@@ -50,7 +45,7 @@ if TYPE_CHECKING:
50
45
 
51
46
  # TODO: remove this and unify temporal responses
52
47
  class TemporalHttpResponse(JsonResponse):
53
- status_code: Optional[int] = None # pyright: ignore
48
+ status_code: int | None = None # pyright: ignore
54
49
 
55
50
  def __init__(self) -> None:
56
51
  super().__init__({})
@@ -67,81 +62,14 @@ class TemporalHttpResponse(JsonResponse):
67
62
  )
68
63
 
69
64
 
70
- class DjangoHTTPRequestAdapter(SyncHTTPRequestAdapter):
71
- def __init__(self, request: HttpRequest) -> None:
72
- self.request = request
73
-
74
- @property
75
- def query_params(self) -> QueryParams:
76
- return self.request.GET.dict()
77
-
78
- @property
79
- def body(self) -> Union[str, bytes]:
80
- return self.request.body.decode()
81
-
82
- @property
83
- def method(self) -> HTTPMethod:
84
- assert self.request.method is not None
85
-
86
- return cast("HTTPMethod", self.request.method.upper())
87
-
88
- @property
89
- def headers(self) -> Mapping[str, str]:
90
- return self.request.headers
91
-
92
- @property
93
- def post_data(self) -> Mapping[str, Union[str, bytes]]:
94
- return self.request.POST
95
-
96
- @property
97
- def files(self) -> Mapping[str, Any]:
98
- return self.request.FILES
99
-
100
- @property
101
- def content_type(self) -> Optional[str]:
102
- return self.request.content_type
103
-
104
-
105
- class AsyncDjangoHTTPRequestAdapter(AsyncHTTPRequestAdapter):
106
- def __init__(self, request: HttpRequest) -> None:
107
- self.request = request
108
-
109
- @property
110
- def query_params(self) -> QueryParams:
111
- return self.request.GET.dict()
112
-
113
- @property
114
- def method(self) -> HTTPMethod:
115
- assert self.request.method is not None
116
-
117
- return cast("HTTPMethod", self.request.method.upper())
118
-
119
- @property
120
- def headers(self) -> Mapping[str, str]:
121
- return self.request.headers
122
-
123
- @property
124
- def content_type(self) -> Optional[str]:
125
- return self.headers.get("Content-type")
126
-
127
- async def get_body(self) -> str:
128
- return self.request.body.decode()
129
-
130
- async def get_form_data(self) -> FormData:
131
- return FormData(
132
- files=self.request.FILES,
133
- form=self.request.POST,
134
- )
135
-
136
-
137
65
  class BaseView:
138
66
  graphql_ide_html: str
139
67
 
140
68
  def __init__(
141
69
  self,
142
70
  schema: BaseSchema,
143
- graphiql: Optional[str] = None,
144
- graphql_ide: Optional[GraphQL_IDE] = "graphiql",
71
+ graphiql: str | None = None,
72
+ graphql_ide: GraphQL_IDE | None = "graphiql",
145
73
  allow_queries_via_get: bool = True,
146
74
  multipart_uploads_enabled: bool = False,
147
75
  **kwargs: Any,
@@ -163,7 +91,9 @@ class BaseView:
163
91
  super().__init__(**kwargs)
164
92
 
165
93
  def create_response(
166
- self, response_data: GraphQLHTTPResponse, sub_response: HttpResponse
94
+ self,
95
+ response_data: GraphQLHTTPResponse | list[GraphQLHTTPResponse],
96
+ sub_response: HttpResponse,
167
97
  ) -> HttpResponseBase:
168
98
  data = self.encode_json(response_data)
169
99
 
@@ -210,13 +140,13 @@ class GraphQLView(
210
140
  ],
211
141
  View,
212
142
  ):
213
- graphiql: Optional[bool] = None
214
- graphql_ide: Optional[GraphQL_IDE] = "graphiql"
143
+ graphiql: bool | None = None
144
+ graphql_ide: GraphQL_IDE | None = "graphiql"
215
145
  allow_queries_via_get = True
216
146
  schema: BaseSchema = None # type: ignore
217
147
  request_adapter_class = DjangoHTTPRequestAdapter
218
148
 
219
- def get_root_value(self, request: HttpRequest) -> Optional[RootValue]:
149
+ def get_root_value(self, request: HttpRequest) -> RootValue | None:
220
150
  return None
221
151
 
222
152
  def get_context(self, request: HttpRequest, response: HttpResponse) -> Context:
@@ -227,7 +157,7 @@ class GraphQLView(
227
157
 
228
158
  def dispatch(
229
159
  self, request: HttpRequest, *args: Any, **kwargs: Any
230
- ) -> Union[HttpResponseNotAllowed, TemplateResponse, HttpResponseBase]:
160
+ ) -> HttpResponseNotAllowed | TemplateResponse | HttpResponseBase:
231
161
  try:
232
162
  return self.run(request=request)
233
163
  except HTTPException as e:
@@ -258,8 +188,8 @@ class AsyncGraphQLView(
258
188
  ],
259
189
  View,
260
190
  ):
261
- graphiql: Optional[bool] = None
262
- graphql_ide: Optional[GraphQL_IDE] = "graphiql"
191
+ graphiql: bool | None = None
192
+ graphql_ide: GraphQL_IDE | None = "graphiql"
263
193
  allow_queries_via_get = True
264
194
  schema: BaseSchema = None # type: ignore
265
195
  request_adapter_class = AsyncDjangoHTTPRequestAdapter
@@ -274,7 +204,7 @@ class AsyncGraphQLView(
274
204
 
275
205
  return view
276
206
 
277
- async def get_root_value(self, request: HttpRequest) -> Optional[RootValue]:
207
+ async def get_root_value(self, request: HttpRequest) -> RootValue | None:
278
208
  return None
279
209
 
280
210
  async def get_context(
@@ -287,7 +217,7 @@ class AsyncGraphQLView(
287
217
 
288
218
  async def dispatch( # pyright: ignore
289
219
  self, request: HttpRequest, *args: Any, **kwargs: Any
290
- ) -> Union[HttpResponseNotAllowed, TemplateResponse, HttpResponseBase]:
220
+ ) -> HttpResponseNotAllowed | TemplateResponse | HttpResponseBase:
291
221
  try:
292
222
  return await self.run(request=request)
293
223
  except HTTPException as e:
@@ -307,11 +237,11 @@ class AsyncGraphQLView(
307
237
  def is_websocket_request(self, request: HttpRequest) -> TypeGuard[HttpRequest]:
308
238
  return False
309
239
 
310
- async def pick_websocket_subprotocol(self, request: HttpRequest) -> Optional[str]:
240
+ async def pick_websocket_subprotocol(self, request: HttpRequest) -> str | None:
311
241
  raise NotImplementedError
312
242
 
313
243
  async def create_websocket_response(
314
- self, request: HttpRequest, subprotocol: Optional[str]
244
+ self, request: HttpRequest, subprotocol: str | None
315
245
  ) -> TemporalHttpResponse:
316
246
  raise NotImplementedError
317
247
 
@@ -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, Union
4
+ from typing import TYPE_CHECKING
5
5
 
6
6
  from graphql import GraphQLError
7
7
 
@@ -72,20 +72,20 @@ class InvalidTypeInputForUnion(Exception):
72
72
  class MissingTypesForGenericError(Exception):
73
73
  """Raised when a generic types was used without passing any type."""
74
74
 
75
- def __init__(self, annotation: Union[StrawberryType, type]) -> None:
75
+ def __init__(self, annotation: StrawberryType | type) -> None:
76
76
  message = f'The type "{annotation!r}" is generic, but no type has been passed'
77
77
 
78
78
  super().__init__(message)
79
79
 
80
80
 
81
81
  class UnsupportedTypeError(StrawberryException):
82
- def __init__(self, annotation: Union[StrawberryType, type]) -> None:
82
+ def __init__(self, annotation: StrawberryType | type) -> None:
83
83
  message = f"{annotation} conversion is not supported"
84
84
 
85
85
  super().__init__(message)
86
86
 
87
87
  @cached_property
88
- def exception_source(self) -> Optional[ExceptionSource]:
88
+ def exception_source(self) -> ExceptionSource | None:
89
89
  return None
90
90
 
91
91
 
@@ -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
4
+ from typing import TYPE_CHECKING
5
5
 
6
6
  from .exception import StrawberryException
7
7
  from .utils.source_finder import SourceFinder
@@ -43,7 +43,7 @@ class ConflictingArgumentsError(StrawberryException):
43
43
  )
44
44
 
45
45
  @cached_property
46
- def exception_source(self) -> Optional[ExceptionSource]:
46
+ def exception_source(self) -> ExceptionSource | None:
47
47
  if self.function is None:
48
48
  return None # pragma: no cover
49
49
 
@@ -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
4
+ from typing import TYPE_CHECKING
5
5
 
6
6
  from .exception import StrawberryException
7
7
  from .utils.source_finder import SourceFinder
@@ -17,8 +17,8 @@ class DuplicatedTypeName(StrawberryException):
17
17
 
18
18
  def __init__(
19
19
  self,
20
- first_cls: Optional[type],
21
- second_cls: Optional[type],
20
+ first_cls: type | None,
21
+ second_cls: type | None,
22
22
  duplicated_type_name: str,
23
23
  ) -> None:
24
24
  self.first_cls = first_cls
@@ -66,7 +66,7 @@ class DuplicatedTypeName(StrawberryException):
66
66
  )
67
67
 
68
68
  @cached_property
69
- def exception_source(self) -> Optional[ExceptionSource]:
69
+ def exception_source(self) -> ExceptionSource | None:
70
70
  if self.first_cls is None:
71
71
  return None # pragma: no cover
72
72
 
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  from abc import ABC, abstractmethod
4
4
  from functools import cached_property
5
- from typing import TYPE_CHECKING, Optional
5
+ from typing import TYPE_CHECKING
6
6
 
7
7
  from strawberry.utils.str_converters import to_kebab_case
8
8
 
@@ -40,7 +40,7 @@ class StrawberryException(Exception, ABC):
40
40
 
41
41
  @cached_property
42
42
  @abstractmethod
43
- def exception_source(self) -> Optional[ExceptionSource]:
43
+ def exception_source(self) -> ExceptionSource | None:
44
44
  return None
45
45
 
46
46
  @property
@@ -61,7 +61,7 @@ class StrawberryException(Exception, ABC):
61
61
  f"[link={self.documentation_url}]{self.documentation_url}"
62
62
  ).strip()
63
63
 
64
- def __rich__(self) -> Optional[RenderableType]:
64
+ def __rich__(self) -> RenderableType | None:
65
65
  from rich.box import SIMPLE
66
66
  from rich.console import Group
67
67
  from rich.panel import Panel
@@ -1,8 +1,9 @@
1
1
  import os
2
2
  import sys
3
3
  import threading
4
+ from collections.abc import Callable
4
5
  from types import TracebackType
5
- from typing import Any, Callable, Optional, cast
6
+ from typing import Any, cast
6
7
 
7
8
  from .exception import StrawberryException, UnableToFindExceptionSource
8
9
 
@@ -10,7 +11,7 @@ original_threading_exception_hook = threading.excepthook
10
11
 
11
12
 
12
13
  ExceptionHandler = Callable[
13
- [type[BaseException], BaseException, Optional[TracebackType]], None
14
+ [type[BaseException], BaseException, TracebackType | None], None
14
15
  ]
15
16
 
16
17
 
@@ -31,7 +32,7 @@ def _get_handler(exception_type: type[BaseException]) -> ExceptionHandler:
31
32
  def _handler(
32
33
  exception_type: type[BaseException],
33
34
  exception: BaseException,
34
- traceback: Optional[TracebackType],
35
+ traceback: TracebackType | None,
35
36
  ) -> None:
36
37
  try:
37
38
  rich.print(exception)
@@ -49,7 +50,7 @@ def _get_handler(exception_type: type[BaseException]) -> ExceptionHandler:
49
50
  def strawberry_exception_handler(
50
51
  exception_type: type[BaseException],
51
52
  exception: BaseException,
52
- traceback: Optional[TracebackType],
53
+ traceback: TracebackType | None,
53
54
  ) -> None:
54
55
  _get_handler(exception_type)(exception_type, exception, traceback)
55
56
 
@@ -57,9 +58,9 @@ def strawberry_exception_handler(
57
58
  def strawberry_threading_exception_handler(
58
59
  args: tuple[
59
60
  type[BaseException],
60
- Optional[BaseException],
61
- Optional[TracebackType],
62
- Optional[threading.Thread],
61
+ BaseException | None,
62
+ TracebackType | None,
63
+ threading.Thread | None,
63
64
  ],
64
65
  ) -> None:
65
66
  (exception_type, exception, traceback, _) = args